Appearance
Encryption and group access
NWatch encrypts alert details on the sender's phone, separately for each recipient. Active group members fetch their own encrypted copy and decrypt it on their phone. A separate service-recovery copy supports manager reveal and incident-key recovery—not account recovery. Manager reveals are authorised and audit logged.
What is encrypted
| Information | Handling |
|---|---|
| Group-specific alert text, including an address or instructions entered by the sender | Encrypted inside each recipient's alert envelope |
| Current GPS coordinates, when the sender includes them | Encrypted with the alert text |
| Saved alert text before sending | Local app storage on the phone |
| Incident ID, group ID, sender identity, time, status and delivery diagnostics | Operational records, outside the encrypted content |
| Mobile alert notification | Generic alert wording and routing metadata; no private alert text or precise GPS |
| Manager-written resolution notes, broadcasts and operational messages | Outside the encrypted alert-details envelope; these can appear in notifications |
The encrypted content is the alert's message and optional location—not all account, membership or billing data. HTTPS protects app and portal traffic in addition to the envelope encryption. Received details can be displayed and cached locally on the recipient's phone.
A snapshot when you send
The portal and service cannot inspect unsent alert text saved on the phone or request a live view of its GPS. At the send gesture, the app saves the selected destination groups and their saved alert text in an encrypted, account-bound local queue. If the phone is offline, it shows that it is still trying. It retries while the app is open; after closing or restarting, review the saved alert to resume or discard it.
For a waiting group, the app first contacts the API for recipient keys. If GPS was selected and remains enabled, it then requests a fresh location fix, rather than using the original offline gesture's location. The encrypted message includes the fix time. If a current fix is unavailable, the alert proceeds with a location-unavailable note instead of waiting indefinitely.
The resulting message is encrypted on the phone before upload. The private message and precise coordinates leave the sender only as ciphertext. Once a group's encrypted request is prepared, retries reuse that exact request and incident ID—even if a response was lost. They do not capture new text or GPS or create a second incident for an already accepted destination. NWatch does not stream location updates.
How an alert is encrypted
- The signed-in sender chooses the destination groups. For each group, the app obtains its active members' registered public keys from the API, plus the configured service-recovery public key.
- The app creates a new incident ID for that group and combines its saved alert text with any chosen GPS coordinates.
- For each recipient independently, ML-KEM-768 encapsulation produces a fresh shared secret and a KEM ciphertext. The 32-byte shared secret is used directly as the AES-256-GCM key.
- AES-256-GCM encrypts the message with a fresh 12-byte random nonce and authenticated context. Its 16-byte authentication tag is appended to the ciphertext.
- The app uploads the successfully sealed envelopes. The API checks the sender and recipient membership, stores the incident and submits notifications for eligible group devices. The sender's own phone is excluded from the notification fan-out.
- A recipient signs in, fetches only their own envelope and uses their local private key to recover its shared secret. AES-GCM verifies the tag and context before returning the message.
Each envelope contains uid, kid, kemCiphertext, ciphertext, nonce and aadVersion. Binary values use base64url encoding. Every recipient has a separate ciphertext, nonce and encapsulated secret; there is no single shared group password or group decryption key.
Binding the message to its context
Current clients use additional authenticated data (AAD), encoded as the UTF-8 bytes of this JSON array:
json
["nwatch-envelope","v1","groupId","incidentId","recipientUid","recipientKeyId"]The placeholders are replaced with the actual identifiers. This binds the encrypted message to its group, incident, recipient and key. Moving an envelope to a different context causes authentication to fail. kid selects the recipient key; it is not a password or signature.
Readers also support older envelopes without aadVersion, using their original empty AAD. Member-envelope recovery preserves the source envelope's AAD regime. The current suite uses ML-KEM-768 and AES-256-GCM, with no separate HKDF step or ML-DSA message signature. Sender identity is established by API authentication and authorisation.
Group membership and multiple groups
The API uses server-side membership records, not a recipient list trusted from the phone. New member-recipient envelopes and push targets are restricted to active members of the destination group. Fetching a personal envelope also requires active membership in that group and an envelope addressed to the signed-in user.
Membership and role apply separately to each group. A person in family and work groups can select either or both for outgoing alerts. Each selected group gets its own incident, message and independently sealed envelopes. Joining another group does not silently add it to the outgoing selection; receiving remains enabled for active memberships.
Pausing, banning or removing a member stops their eligibility for new group deliveries and subsequent personal-envelope retrieval. It does not erase details or encrypted copies already downloaded to their device.
Manager access in the portal
In the manager portal, select the correct group, open Incidents, then choose Reveal alert details on an incident. Hide alert details clears that displayed reveal. Opening the incident list alone does not decrypt every incident. A reveal opens the snapshot already sent, not the sender's current settings or location.
The API verifies that the requester is an active manager of that specific group. It opens the incident's service-recovery envelope in the Worker and returns the details over HTTPS. The reveal is audit logged. The portal receives the requested plaintext, not the recovery private key or any member's private key.
Administrative reveal is separately controlled: even a super administrator must have direct active membership in the target group. An invite code, a licence, or manager status in another group does not grant access to these details.
Keys and recovery
Member keys
The app generates and self-tests an ML-KEM-768 keypair. Public keys are registered with the service; member private keys stay in OS-protected storage through expo-secure-store—iOS Keychain or Android Keystore-backed storage. Saved message text and displayed alert details use local app storage, separately from private keys.
iOS tries the native CryptoKit module and falls back to JavaScript when needed. Android uses the JavaScript mlkem and @noble/ciphers implementations. Both use the same envelope format.
One active device/key identity per account is supported. Key rotation registers a new public key for future alerts. Previous private keys are retained locally by kid to open older envelopes while that secure storage remains available. Account deletion's local cleanup removes current and archived private-key material.
Server recovery key and Cloudflare secrets
The production recovery private key is configured as a Cloudflare Worker secret, not a plaintext configuration variable or a key delivered to the mobile app or portal. Only its public key is distributed for encryption.
Cloudflare stores secret values encrypted and does not display their saved values in the dashboard or Wrangler. The authorised Worker receives the secret at runtime and uses it for recovery operations. This is protected, server-side key storage; the runtime can read the key. See Cloudflare's secret-storage documentation.
The service is therefore inside the encryption trust boundary: member copies are decrypted on member devices, and the recovery copy can be decrypted by the service through its authorised workflows. Plaintext is processed transiently for reveal or recovery rather than saved back into the incident database.
Incident-key recovery re-encrypts a stored alert for an eligible member's current public key. It does not recover a sign-in account, restore their private key or retrieve unsent phone data. Recovery requires usable keys and a service-recovery envelope. Automatic recovery is best-effort; an incident without a recoverable copy remains unavailable.
Registering a changed key automatically attempts recovery of open incidents in the member's current active groups. Controlled administrative tools also support incident re-wrapping. Access is based on current membership, not a joining-date cutoff: recovered open incidents can predate a member's join, and active managers can reveal historical incidents in their group. Joining alone does not create personal copies of every past alert.
Delivery and decryption results
A successful encryption operation, a stored incident and push-service acceptance are separate results. If some recipients cannot be sealed, the app reports incomplete coverage; if none can be sealed, it does not submit that group's incident. Receiving an alert notification is not proof that its details decrypted.
The app distinguishes missing envelopes, missing local or historical keys, authentication failures and unavailable cryptography, and reports decryption diagnostics for investigation. These results do not include the private alert message.
The sender sees a separate count for each group, such as Delivered 1/5. It counts original recipient members whose apps have reported receipt, once per member—not devices, push-service acceptance or later additions to the group. A positive complete count, such as Delivered 5/5, is green. Receipt does not establish that someone read, heard or can respond to the alert. Older incidents without a saved audience show an unavailable count rather than an invented total.
Incident cards and the manager portal identify the sender using that group's member name or their profile name. Managers can correct a member's group name under Members. This is authenticated account metadata, separate from the encrypted message, and is not a verified legal identity.
Algorithm references
See also Security, Architecture and the Privacy Policy.