threat model
this is the security north star. every change must preserve the invariants below. if a feature would violate them, the feature changes — not the invariants.
full version lives at docs/threat-model.md in the source repo; abridged here.
what we protect against
-
server compromise (RCE / supply chain)all user content is client-side encrypted with keys the server never possesses.
-
legal process against the companywe cannot decrypt what we serve.
-
hosting-provider compromise / hypervisor accesssame defence as #1; disk encryption is belt + suspenders.
-
malicious insider / rogue adminprivilege separation; no plaintext access endpoints anywhere.
-
passive network surveillanceTLS everywhere; minimised metadata; no IP retention.
what we don't pretend to defend against
- endpoint compromise — if your device is owned, we can't help
- traffic-analysis correlation across services — not running a mixnet
- quantum-future decryption of recorded traffic — revisit when PQ-TLS is standard
- coercion of the user — rubber-hose cryptanalysis always wins
- outbound /email sending — composing to an external inbox is plaintext by nature (they hold no key). admin-only, relayed not stored. receiving stays sealed.
invariants
these are the contracts. any PR that violates one is rejected.
- the server never sees user plaintext for notes, file contents, or mask mappings.
- the server never logs PII — no IPs, no emails, no user IDs in long-lived logs.
- no third-party scripts, fonts, or analytics reach the browser. strict CSP, self-hosted everything.
- no third-party CDNs for plaintext — ciphertext-only on any external edge.
- the admin backend has no endpoint that decrypts user content.
- backups encrypted with a separate key from runtime data.
- cryptographic parameters are versioned so rotation is possible without re-encrypting everything.
- anonymous features stay anonymous — never linked to an account even if you're logged in.