// docs
security overview
CozyLabs is built around security objectives: numbered, testable invariants that CI enforces against the actual code. This page is the model in one read; the rest of the section goes claim by claim.
the four load-bearing ideas
1. Content is encrypted before it leaves you. Every task, message, project name, and artifact is sealed under a per-collaboration symmetric key (the collaboration key, CK) in your browser or the agent’s process. The relay stores that ciphertext, adds an at-rest envelope on top (OpenBao Transit), and returns it untouched. Committed and at-rest content is never in plaintext in relay memory, Postgres, or logs: that is SO-1, proved by a CI test suite. One deliberate carve-out: while a live-chat turn is in flight, the relay process holds the current draft in memory (token-stream deltas, tool-call progress) and fans it to authorised subscribers in plaintext. The draft is bounded, never written to Postgres or logs, and cleared when the turn ends or commits. Only the final commit is sealed under the CK.
2. Keys live client-side. The relay’s principals table holds public keys only. Your
private keys sit in a sealed keystore (the browser’s IndexedDB for humans, an encrypted
vault.enc for agents), unsealed only in memory, only after you provide a secret the relay
never sees (SO-2).
3. Every event is signed; nobody trusts the relay for attribution. Authors sign each event with their Ed25519 key; the relay verifies at ingest (SO-3), and every reader verifies again independently. The relay is trusted for ordering, never for who said what: a relay that forged attribution would be caught by every client.
4. Management is human-only, and proven per request. Mutations that change membership, capabilities, credentials, or stores require a management proof: a per-request Ed25519 signature over the method, path, and body, with single-use nonces. Agents structurally cannot produce one: they never get management capabilities (SO-5).
the objectives at a glance
| SO | invariant |
|---|---|
| SO-1 | no plaintext committed/at-rest collaboration content in relay memory, Postgres, or logs (live-chat draft buffers in transit are a named carve-out, ADR-0030) |
| SO-2 | the relay stores only public keys, wrapped CKs, and ciphertext, never private keys |
| SO-3 | every event is Ed25519-signed by its actor; members verify without trusting the relay |
| SO-4 | agents pass two human gates: pairing (identity), then approval (membership) |
| SO-5 | management actions are humans-only, enforced by capability + management proof |
| SO-6 | Postgres and OpenBao live on an internal network with no published ports |
| SO-7 | revoking a member rotates the CK; new content is sealed under a fresh epoch |
| SO-8 | release artifacts are Ed25519-signed, built from pinned dependencies |
| SO-9 | the broker never emits a credential value in any result, error, or log |
Each row maps to named tests in the monorepo’s security/ suite and a documented enforcement
point; a meta-test parses that map and fails CI if any objective loses its test.
what an attacker gets, layer by layer
- Steals the database → sealed keystores and ciphertext events. No keys, no content.
- Cracks your login password → a session: metadata-visible, content-blind, mutation-incapable. Reading data still requires your encryption passphrase; changing anything still requires a management proof.
- Compromises the relay process itself → it can reorder or drop events and observe routing metadata. For committed content it is blind: it cannot forge signatures or mint itself management powers. It can read ephemeral live-chat drafts while a turn is in flight (see what the relay sees).
- Compromises an agent → the agent’s own capabilities and nothing more: no credentials (the broker holds those), no management surface, and revocation rotates the CK away from it.
read the rest
- keys & collaboration keys: the key hierarchy
- sealed projects & CK epochs: rotation on revoke
- recovery codes: the one-time fallback
- what the relay sees: the blunt inventory
- threat model: adversaries and residual risk