Skip to content

feat(mobile): deliver an encrypted test notification to one installation #199

Description

@ZingerLittleBee

Parent

#196

Architecture decision update (2026-10-03)

The current scope follows the approved public, stateless Cloudflare Workers Relay decision in #196 and #206. The earlier App Attest/challenge/device-grant requirements are superseded, without a replacement authorization service. Server login, installation ownership, key lifecycle, exact-target offline session revocation and account isolation remain required. Implementation and automated verification are recorded in #206 at ae5add35bb8b26c005b7a3d4c5dc173d22e949ee. Acceptance checkboxes remain unchanged; live-device acceptance remains separate and pending.

What to build

The user taps Test Notification in iOS, and the authenticated Server sends an encrypted message through the public stateless Worker Relay to that installation. The Notification Service Extension renders it, and tapping it safely opens the current account. This is the first complete delivery tracer bullet.

Acceptance criteria

  • The test operation derives the recipient from the authenticated mobile installation and cannot broadcast to other devices or accept a caller-selected victim.
  • Generate and register per-installation content keys through authenticated secure setup; keep the device copy in shared Keychain storage for the Notification Service Extension and never send the key to the Relay.
  • Rust encryption and Swift decryption share versioned AES-256-GCM interoperability vectors with authenticated identity metadata, fresh nonces, and bounded payloads.
  • The public Worker accepts the token/environment, event identity/expiry and unchanged encrypted envelope without a grant, and uses fixed APNs hosts/operator-controlled topic bindings, correct push type/priority/expiry, hard streaming and payload bounds, WebCrypto JWT reuse, Workers fetch, and provider-verdict handling. Bound rate-limit state and best-effort source/target/instance-wide protections; do not claim strict global quota enforcement.
  • The notification extension decrypts and localizes authentic business content or retains only fixed generic fallback text; no plaintext notification content or content key is forwarded through the Relay. Explicitly document that a public caller with a valid token can cause generic fallback notifications and that arbitrary/self-owned-token requests can consume quota; AEAD does not prevent those abuses.
  • Buffer early registration callbacks and cold-launch taps until authentication and stores are ready; reject old deployment/account targets, and show one foreground system banner.
  • Show provider acceptance separately from observed presentation; classify configuration/payload errors separately from invalid device conditions and prevent late errors from invalidating a replacement token.
  • Separate new Relay registrations from legacy direct-APNs fan-out so migration cannot notify the same installation through both paths; keep legitimate existing external channels working.
  • Verify the complete test-message path using real Server persistence, actual Relay/extension boundaries and shared vectors, with only Apple/network/system boundaries substituted. Include an isolated real-device smoke checklist without claiming unrun APNs delivery.

Blocked by

Implementation verification checkpoint (2026-10-03)

At ae5add35bb8b26c005b7a3d4c5dc173d22e949ee, Rust-to-Worker/iOS CI ran two real Rust trace producers and all three native ciphertext consumers. Actual Notification Service Extension rendering and authenticated cold-tap routing passed within the 423-test iOS suite, with zero failures and no XCTest skips. Worker CI passed 61 workerd tests plus one native HTTP/TCP-reset cancellation regression, along with types and the production bundle dry-run.

These automated boundaries do not establish live APNs provider acceptance, delivery, or physical-device presentation/taps. Those checks and Worker deployment remain NOT RUN. PR #206 records all 17 green checks; acceptance checkboxes remain unchanged.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-agentFully specified, ready for an AFK agent

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions