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)
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
- Client signs in via Firebase Auth.
- Client calls the Worker API with
Authorization: Bearer <FirebaseIdToken>. - Worker verifies the Firebase ID token server-side.
- Worker enforces authorization (role + membership + status) server-side.
- Worker reads/writes Firestore via REST using a service account.
- 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
- profile:
groups/{groupId}- metadata:
name,zoneId,createdByUid,createdAt members/{uid}:role(member/manager),status(active/paused/banned)incidents/{incidentId}: incident records
- metadata:
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
Payload flow (recommended: server never decrypts address)
- Triggering user’s app creates an
AddressPackage(e.g.,addressText, optionalinstructions). - App generates a random
contentKeyand encrypts the package using AES-256-GCM →ciphertext. - Worker determines the recipients (active members for the group / zone).
- Worker wraps
contentKeyfor each recipient using ML-KEM-768 to theirkemPublicKey(producing a compact per-recipient “key envelope”). - 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
- 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:
- Requirements sign-off (roles, invite join, alerting, admin controls, data minimisation)
- System design sign-off (API contracts + data model + security invariants)
- Implementation
- Worker API + Firestore schema
- Mobile minimal UX
- Admin minimal UX
- Verification
- Contract tests (local/staging)
- Staging E2E tests
- Deployment
- Worker deploy
- Mobile release pipeline
- Admin deploy
- 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.