Skip to content

Doc status: Latest (rolling). See Versions.

High level

  • Mobile App (Expo / React Native)
  • Admin Web (Next.js)
  • API (Cloudflare Workers)
  • Data (Firestore)
  • Push (FCM → APNs/Android)
mermaid
flowchart LR
  Mobile[Mobile App] -->|Firebase ID Token| API[Cloudflare Worker API]
  Admin[Admin Web] -->|Firebase ID Token| API
  API -->|Firestore REST| DB[(Firestore)]
  API -->|FCM HTTP v1| FCM[FCM]
  FCM --> APNs[APNs / iOS]
  FCM --> Droid[Android]

Request / auth flow

  1. Client signs in via Firebase Auth.
  2. Client calls the Worker API with Authorization: Bearer <FirebaseIdToken>.
  3. Worker verifies the Firebase ID token server-side.
  4. Worker enforces authorization (role + membership + status) server-side.
  5. Worker reads/writes Firestore via REST using a service account.
  6. Worker triggers push fan-out using FCM HTTP v1.

Authorization model

  • Members can:

    • register device
    • join group via invite
    • trigger incident (only for groups they are an active member of)
  • Managers can (scoped to group):

    • create invite codes
    • broadcast
    • pause/ban a member
  • Server-side checks are mandatory for all privileged actions.

Data model (logical)

This is the conceptual structure (implemented in Firestore collections/subcollections):

  • users/{uid}

    • profile: email, displayName, role, status
    • devices/{deviceId}: platform, token, createdAt, lastSeenAt
  • groups/{groupId}

    • metadata: name, zoneId, createdByUid, createdAt
    • members/{uid}: role (member/manager), status (active/paused/banned)
    • incidents/{incidentId}: incident records
  • invites/{inviteCode}

    • groupId, expiresAt, maxUses, uses, revoked

Security model

  • All privileged actions are checked server-side:
    • Authentication: Firebase ID token verification
    • Authorization: group manager role checks in Firestore
  • Avoid storing exact addresses; prefer coarse zones.

Address handling (on-device + encrypted)

NeighbourhoodWatch does not store a user’s exact home address centrally.

  • The address is stored only on the user’s phone and can be edited at any time.
  • The address is sent only when an alert is triggered, as an encrypted payload.
  • The system should persist no plaintext address server-side (logs or database). If stored at all, store only an opaque encrypted blob.

Encrypted incident payload (PQC-ready, implementable)

Important detail: FALCON and Dilithium are signature algorithms (authenticity), not encryption. For key transport we need a KEM (recommended: ML-KEM/Kyber). Payload encryption remains AES-256-GCM.

Keys

  • Each device generates and stores private keys locally.
  • Public keys are safe to store centrally.

Recommended per-user keys:

  • Signature keypair: FALCON-512 (or ML-DSA-44 / Dilithium2) for signing.
  • KEM keypair: ML-KEM-768 (Kyber) for encrypting a per-incident symmetric key.

Store public keys in Firestore:

  • users/{uid}/crypto: sigPublicKey, kemPublicKey, kid, createdAt
  1. Triggering user’s app creates an AddressPackage (e.g., addressText, optional instructions).
  2. App generates a random contentKey and encrypts the package using AES-256-GCMciphertext.
  3. Worker determines the recipients (active members for the group / zone).
  4. Worker wraps contentKey for each recipient using ML-KEM-768 to their kemPublicKey (producing a compact per-recipient “key envelope”).
  5. Worker sends push with:
  • incident metadata (non-sensitive)
  • ciphertext (AES-GCM)
  • key envelopes (recipient uid → wrapped key)
  • optional signature (FALCON/Dilithium) over the message for integrity
  1. Recipient devices decrypt the key envelope locally, recover contentKey, then decrypt the address package.

Fallback flow (simpler but not true end-to-end)

If required initially for implementation constraints:

  • Triggering app encrypts the address package to a server-held key.
  • The Worker decrypts server-side and re-encrypts for recipients.

This is not end-to-end (server sees plaintext). If used, treat it as a transitional mode and enforce:

  • no plaintext persistence
  • strict no-logging of payloads
  • short retention / TTL on any incident blobs

See also: Security.

Static “waterfall” delivery plan

The requirements are intentionally clear and narrow; this supports a straightforward waterfall delivery sequence:

  1. Requirements sign-off (roles, invite join, alerting, admin controls, data minimisation)
  2. System design sign-off (API contracts + data model + security invariants)
  3. Implementation
  • Worker API + Firestore schema
  • Mobile minimal UX
  • Admin minimal UX
  1. Verification
  • Contract tests (local/staging)
  • Staging E2E tests
  1. Deployment
  • Worker deploy
  • Mobile release pipeline
  • Admin deploy
  1. Operational hardening
  • Monitoring + alerting
  • Incident review process

Content & diagrams

  • Put images in docs/public/images/ and reference as /images/....
  • Use Mermaid blocks (like above) for flow diagrams.