HabrAugust 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

HabrPrivacy & Surveillance

Gesture Dynamics CAPTCHA Emerges as Privacy-Focused Drop-in Alternative to reCAPTCHA

A new open-source CAPTCHA system called Aptogon replaces traditional image-based challenges with analysis of hand gesture dynamics to verify human users. Instead of clicking on traffic lights or buses, visitors draw a free-form gesture for about ten seconds while the system measures velocity variance, pause entropy, rhythm irregularity, and micro-corrections that distinguish human motor patterns from bots. The solution addresses recent reCAPTCHA restrictions, including Google's reduction of free monthly verifications from one million to ten thousand and tightened GDPR data responsibility rules starting in April 2026. An iframe architecture loaded from the vendor origin eliminates cross-origin issues and CORS blocks while supporting public and secret key pairs for domain validation. Machine learning relies on a local gradient boosting model for confident decisions and an LLM only for borderline cases, with fail-closed behavior returning 503 errors when the classifier is unavailable. Coordinates never leave the browser; only derived statistics are sent, satisfying GDPR requirements without cookie banners or biometric templates. The project is released under AGPL-3.0 with a free tier of one thousand checks per month and integration examples for HTML, React, Node, Python, and PHP.

HabrPrivacy & Surveillance

Cat Tunnels Service Deploys Kotator-Rotator to Counter Mass Blocking of Relay Nodes in Russia

The operators of the decentralized Cat Tunnels service faced a sudden wave of blocks that disabled all several dozen of their tracker nodes inside Russia. Without these anchor relays, new user connections slowed dramatically and existing sessions degraded. The team responded by building Kotator-Rotator, an automated system that continuously evaluates node reachability from the client side and replaces failing relays with fresh instances. The decision engine relies on Grohotator, an aggregated availability metric derived from client technical logs that also triggers an audible alarm when thresholds are crossed. Analysis of the logs revealed that blocking activity follows a clear weekday pattern, pausing on Friday evenings and resuming Monday mornings. The experience demonstrated that server-side health checks alone are insufficient when censors interfere with paths between clients and relays.

AntiMalwarePrivacy & Surveillance

Google Chrome Tests Visible Global Privacy Control Toggle in Canary

Google is testing a new visible toggle for Global Privacy Control in Chrome Canary that lets users send a standardized request asking websites not to sell or share their personal data and not to use it for targeted advertising. When enabled, the browser adds the Sec-GPC: 1 header to web requests and exposes the setting via navigator.globalPrivacyControl. In regions with supporting laws such as California's CCPA, the signal can serve as a formal opt-out from data sales. The feature currently appears primarily on Android, with experimental flags available on Windows, macOS, Linux, and ChromeOS, though the desktop interface remains incomplete. Two separate flags are required—one to show the toggle and another to actually transmit the signal—because enabling only the UI does not send Sec-GPC: 1. The mechanism is not a guaranteed enforcement tool; websites decide how to respond, and effectiveness depends on legal frameworks and site compliance. The feature is absent from the stable Chrome release and may still change before wider rollout.

AntiMalwarePrivacy & Surveillance

OpenAI ChatGPT Computer History Feature on macOS Could Expose Detailed User Activity Logs to Infostealers

OpenAI has introduced the Computer History feature in its macOS ChatGPT app, which records application switches, clicks, keystrokes, and accessibility context to generate AI summaries and memories. The feature is disabled by default and requires explicit activation of Memories, with availability limited to Pro, Business, and Enterprise users outside the EEA, Switzerland, and the UK. While raw event files are deleted after 48 hours and not used for model training, the resulting Markdown memory files remain unencrypted on the local Mac. These files can be read by any process running under the same user account, creating a ready-made activity log for infostealers and other malware. OpenAI also warns about prompt injection risks where hidden instructions from websites or apps could influence ChatGPT or Codex behavior. Users retain controls to select participating apps, pause collection, or delete history, but the lack of encryption on stored memories raises significant privacy concerns.