Security
Security model
Tumaini’s safety properties are enforced by the backend and the database, not by the user interface.
Key properties
- No bypass from the client. Disabled buttons are cosmetic. The API refuses invalid transitions, and a PostgreSQL trigger re-validates every state change and the evidence for each required check.
- AI cannot authorise. Model output only adds caution. Uncertain or unavailable analysis never releases a hold.
- Pre-established channels. A challenge can only go to a trusted contact registered before the suspicious message was received (server-set timestamps).
- Scoped, single-use authorisations. Ed25519/JWS receipts bound to issuer, key, recipient, content hash, exact payment and expiry; single use is enforced by a database primary key.
- Tenant isolation. The application connects as a role without BYPASSRLS; every query runs with the tenant set for row-level security.
- Key custody. Issuer private keys are encrypted with AES-256-GCM (key id bound as associated data) and deleted when a key is revoked.
- Tamper-evident audit. Hash-chained audit events; the application role cannot update or delete them.
- Untrusted inputs. Size limits, magic-byte sniffing, ffmpeg decoding in a sandboxed temporary directory, no remote manifest fetching in C2PA validation.
- Web hardening. Strict CSP, HSTS, frame denial, SameSite cookies plus origin checks on state-changing requests, rate limits on all public and mutating endpoints.
Known gaps
- Demo institution access codes are public by design; production needs SSO and MFA.
- Keys are software keys, not HSM-backed.
- The simulated contact channel stores the one-time token so the demo phone can display it.
The full threat model and test evidence are in the repository (docs/THREAT_MODEL.md, docs/SECURITY_MODEL.md, docs/QA_REPORT.md).