Habr•August 1, 2026•🇷🇺Translated from Russian

UnifiedPush and Public ntfy.sh: Why Push Notifications Fail on Android Without Google Services

Developers of a messenger operating in a region where Google services are unavailable faced the familiar problem of replacing FCM. The standard open-source answer is UnifiedPush, which splits delivery among the app, the application server, and a distributor that maintains a connection to a push server. The team implemented this approach in June using the popular ntfy distributor paired with the public ntfy.sh instance.

One and a half months later, user complaints about missing notifications prompted an examination of production logs. Over a 36-hour window the server recorded 100 successful deliveries against 507 responses with code 507 (Insufficient Storage), 351 with code 429 (Too Many Requests), and 69 with code 400 (Bad Request). Failures had been present since the feature launch; they simply went unnoticed on always-connected test devices.

The 507 errors stem from a deliberate design choice in ntfy: when visitor-subscriber rate limiting is enabled, a UnifiedPush topic must have an active subscriber before any message can be published. The official ntfy client from Google Play uses Firebase for instant delivery and only maintains a persistent WebSocket when the F-Droid build is used. In regions without Google services the client never registers a subscriber, so every server POST receives an immediate 507 and the message is discarded rather than queued.

Code 429 responses arise because rate-limit buckets are attached to the subscriber’s IP address, not the sender. Mobile carriers place hundreds of users behind a single NAT address; a single group message can exhaust the shared 60-request bucket and cause subsequent deliveries to fail for unrelated users sharing the same IP. Many endpoints alternated between 429 and 507 within a single day, producing the intermittent behavior reported by users.

Endpoints pointing at updates.push.services.mozilla.com returned 400 because the server implements the WebPush specification (RFC 8030), which requires a TTL header. The original implementation omitted the header, assuming ntfy behavior would apply to every endpoint. The team now sends a minimal WebPush-compliant request containing Content-Type, TTL, and Urgency headers for all destinations.

Purchasing a paid ntfy.sh plan does not resolve the problem. Both the subscriber-existence check and the rate-limit logic operate on the recipient side; elevating the sender’s quota has no effect. The developers therefore deployed a self-hosted ntfy instance with visitor-subscriber-rate-limiting disabled, a generous burst limit of 200 requests, and an exempt host list that includes the backend address.

Even a private push server leaves users with the burden of installing and configuring a separate distributor application. To remove this friction the team implemented an embedded distributor directly inside the messenger. The component consists of a BroadcastReceiver that answers REGISTER requests and a foreground service maintaining a WebSocket to the private server. The WebSocket reconnect logic includes a “since” parameter so cached messages are delivered after network interruptions.

Two device-only bugs surfaced during live testing. Re-registration generates a fresh topic, yet the service continued listening on the old topic until an explicit comparison was added. Closing an obsolete socket triggered an onClosed handler that scheduled a duplicate reconnect, resulting in duplicate notifications; a generation counter now prevents the second connection from processing events.

Finally, the push WebSocket must traverse the same anti-censorship tunnel used by the rest of the application. Because OkHttp caches the route at client creation time, the service recreates the HTTP client whenever the tunnel state changes, ensuring notifications remain available even when the primary domain is blocked.

Related articles

AntiMalware•Privacy & Surveillance

WhatsApp Help Pages Hid Secret Instructions Directing AI Bot to Avoid GDPR References

Researchers at Redact discovered hidden instructions embedded in WhatsApp's public help documentation that instructed the company's AI support chatbot to avoid recommending users contact data protection regulators. The instructions also prohibited the bot from referencing GDPR, LGPD, or CCPA when users mentioned legal proceedings or lawyers. The findings emerged after Redact analyzed 545 help pages and identified 102 concealed blocks across 80 of them. One directive explicitly told the bot not to suggest contacting supervisory authorities even when users had lost access to their phone number and could not delete their account, despite WhatsApp's own European privacy policy stating that such complaints are permitted. WhatsApp confirmed to WIRED that the blocks were intended solely for its internal chatbot and attributed their public exposure to an error. The company stated the restrictions aimed to prevent the bot from offering legal advice. All hidden blocks were removed by October 18.

Habr•Privacy & Surveillance

WhatsApp Secretly Instructed AI Support Bot to Suppress References to Data Regulators and Privacy Laws

Investigators discovered hidden instructions embedded in WhatsApp's public help pages that directed the company's AI chatbot to avoid recommending users contact data protection authorities. The instructions also prohibited the AI from citing privacy regulations such as GDPR, LGPD, or CCPA, even when users raised these topics themselves. The directives appeared on pages about account deletion and complaints, remaining invisible to human visitors but delivered to crawlers and the AI model. WhatsApp later confirmed the text was intended for its internal support chatbot and attributed the exposure to an error that has since been corrected. The findings highlight a gap between the company's publicly stated privacy policy and the operational guidance given to its AI systems.

Habr•Privacy & Surveillance

Developer Builds Password Manager with Plausible Deniability Using Argon2id and XChaCha20-Poly1305

A Java backend developer rewrote a personal password manager originally built with Electron and React, migrating it to Tauri and Rust while redesigning its storage to support plausible deniability. The new implementation derives keys with Argon2id using a single per-install salt and encrypts files with XChaCha20-Poly1305, storing only random 32-character hex filenames without any headers or magic bytes. Multiple decoy vaults of identical size are created automatically for every user, and all files in the folder receive synchronized timestamps to eliminate metadata-based identification of the active vault. History and sync data are stored as additional encrypted files of the same format, making it impossible to distinguish real from decoy vaults from the filesystem alone. The approach mirrors VeraCrypt hidden volumes but operates on ordinary files and folders, with the explicit goal of providing deniability when a user is compelled to reveal a password.

Habr•Privacy & Surveillance

LxBox Evolves from VPN Client to Comprehensive Network Traffic Management Ecosystem

LxBox has grown from a personal Android client for sing-box into a full ecosystem spanning mobile, desktop, and router platforms. The project now supports a wide range of VPN and proxy protocols within a single routing framework, allowing users to manage corporate SSL VPNs, home WireGuard connections, and proxy servers together. Key security features include default ingress blocking to prevent unauthorized reverse connections through VPN tunnels and protections against traffic leaks via excluded applications. The developer introduced progressive configuration levels, from one-click WARP setup to advanced JSON editing and AI-friendly documentation. The core sing-box-lx fork ranks among the top sing-box derivatives and powers third-party tools such as the upcoming NekoBox 6. LxBox is available on Google Play, F-Droid, and GitHub, with desktop launcher support for Windows 7 and later, macOS, and Linux.