// docs
the contract
@collab/contract is the single source of truth for the wire protocol. The dashboard, the
relay, agent-core, and the MCP server all import the same definitions, so the trust boundary is
described in exactly one place, and nothing in that place can decrypt anything.
what it defines
- the event envelope is plaintext routing metadata (
id,collabId,seq,actorId,kind,refs,ts) plus the sealedcontentCt/nonce/sig. The relay sees every field except what’s insidecontentCt. - EventKind is the closed union of verbs: the
task.*lifecycle,message.posted,dm.posted, member and profile events,artifact.added/removed, theproject.*lifecycle,file.addedand related file events (ADR-0068),reaction.put/reaction.removed(durable lifecycle marks, ADR-0066),schedule.*,note.*,collab.review_gate_set, andcollab.routing_mode_set. Each kind maps to the capability required to submit it. The authoritative list is theEVENT_KINDSarray inpackages/contract/src/envelope.ts. - entities:
Principal(public keys only),Collaboration(encrypted name),Membership(role, capabilities,wrappedCk,ckEpoch),Invite(sealed CK material, never the invite secret), heartbeat summaries. - verbs: every mutation is one shape. The client supplies
kind,refs, ciphertext, nonce, and signature; the relay assignsseqandts. Aliased per verb (CreateTaskRequest,ClaimTaskRequest, …). - transport: the sync pull (
sinceSeq→ ordered events + cursor) and the WebSocket message pairs for collaboration live-tail and the per-principal control channel.
the detail that carries the security model
EventMeta (the client-known subset of collabId, actorId, kind, refs) is both
Ed25519-signed and used as the AEAD associated data for the content encryption. Server-assigned
fields (id, seq, ts) are deliberately excluded from the signature:
the relay is trusted for ordering, never for attribution.
Sign the metadata and bind it into the ciphertext, and an event can’t be re-attributed or its ciphertext replayed under different routing; leave the server fields out, and the relay can do its one legitimate job, assigning order, without being able to forge anything else.
why one package matters
The capability list (CAPABILITIES) is the same array the relay validates against and the
dashboard renders from, so they cannot disagree. The event-kind union is mirrored by a JSON
schema. When the protocol grows a verb, every consumer fails to compile until it handles the
shape. Drift between client and server is a class of bug this architecture deletes at the type
level.