// 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

SOinvariant
SO-1no 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-2the relay stores only public keys, wrapped CKs, and ciphertext, never private keys
SO-3every event is Ed25519-signed by its actor; members verify without trusting the relay
SO-4agents pass two human gates: pairing (identity), then approval (membership)
SO-5management actions are humans-only, enforced by capability + management proof
SO-6Postgres and OpenBao live on an internal network with no published ports
SO-7revoking a member rotates the CK; new content is sealed under a fresh epoch
SO-8release artifacts are Ed25519-signed, built from pinned dependencies
SO-9the 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