// docs

sealed projects & CK epochs

“Sealed” is not a metaphor in CozyLabs. A project’s contents are ciphertext everywhere except in the memory of the members working it.

what’s inside the seal

Everything domain-shaped crosses the wire as a signed, encrypted event: task lifecycle (task.createdtask.completed), messages, project events, artifact references — even the collaboration’s name. The relay sees the event’s routing metadata and a blob. Artifacts go further: bytes are encrypted under the CK before upload and travel directly to your object store, never through the relay at all.

epochs: the key has a version

Each member’s wrapped CK carries a ckEpoch — a monotonic version stamp. It exists for one reason: membership is not forever.

When a human with management capabilities revokes a member:

  1. the target’s membership goes revoked — their token still authenticates, but every collaboration surface returns 401/403;
  2. the revoker’s client generates fresh wrapped CKs for every remaining active member;
  3. everyone’s ckEpoch bumps; clients notice and refresh their key;
  4. all new content seals under the new epoch.

The rotation is all-or-nothing: unless a complete new set of wrapped keys is written, the revoke doesn’t happen. There is no state where someone is half-removed.

And it is client-driven — the relay can’t rotate the CK because the relay has never had the CK. The decision that the server cannot read your content implies the server cannot re-key your content; the cost is that a member’s machine does the wrapping work, and that’s the right trade.

what an old key still opens

Honesty requirement: cryptography cannot retroactively revoke plaintext someone already decrypted. A revoked member keeps what they already read. What the rotation guarantees is the future — content sealed after the revoke is under a key the revoked member has never been given, and a stale wrapped CK from epoch 3 opens nothing from epoch 4 (SO-7).

Old-epoch keys are memory-only on each client and are dropped after rotation. Content sealed under an epoch a client no longer holds renders as a sealed card — visibly encrypted, never silently wrong.