Status: Draft (proposed for CCCC v0.4.x ecosystem)
This document defines CCCS v1, a small, transport-agnostic standard for multi-agent collaboration built around an append-only event ledger. It is designed to be stable, extensible, and implementable by:
- CCCC itself (daemon + web UI + MCP/IM bridges)
- Client SDKs (TypeScript/Python/Go/etc.)
- External tools and integrations (CI, IM bots, IDE plugins, automation)
CCCS v1 deliberately does not standardize workflows, model providers, or prompting. It standardizes the collaboration substrate: event envelopes, routing semantics, attention/ack, system notifications, and cross-group provenance.
The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY in this document are to be interpreted as described in RFC 2119.
CCCS v1 MUST enable:
- Tool/code ⇄ agent collaboration: tools can send, observe, and act on the same collaboration stream as agents.
- Append‑only truth: collaboration history is represented as immutable events appended to a ledger.
- Provenance: relayed/forwarded messages can be traced back to an original event (cross-group).
- Attention loops: important messages have an explicit acknowledgement mechanism independent of “read”.
- Forward compatibility: unknown event kinds and unknown fields do not break clients.
CCCS v1 does NOT standardize:
- Any specific workflow engine, DAG, or no-code builder.
- Any model/provider API (OpenAI/Claude/Gemini/etc.) or prompt format.
- Any single transport (Unix socket, HTTP, SSE, WS, gRPC). CCCS v1 is transport-agnostic.
- Multi-tenant auth schemes (but it reserves fields and rules for provenance/permissions).
- Group: A collaboration namespace (working group).
- Scope: A project root URL attached to a group; each event is attributed to a
scope_key. - Actor: A named agent identity within a group (e.g.,
foreman,peer-1). - Principal: Any entity that can write events (
user, anactor_id,system, orsvc:<name>).- Service principals SHOULD use a stable namespace (RECOMMENDED:
svc:com.example.mybotwhen disambiguation is needed). - The
svc:cccc.prefix is RESERVED for CCCC ecosystem services.
- Service principals SHOULD use a stable namespace (RECOMMENDED:
- Ledger: An append-only sequence of events for a group.
- Client: Any process/UI/bot that reads or writes events via a daemon.
- Daemon: A single-writer authority that appends events and enforces permissions.
Each event belongs to exactly one group, identified by group_id (string).
Each group MAY have one or more scopes. Each event MUST include a scope_key:
scope_keyMAY be""(unknown / global / not tied to a scope).scope_keyis a stable identifier for a scope assigned by the daemon.- Clients MAY use
scope_keyfor equality and filtering, but MUST treat it as an opaque string and MUST NOT parse or interpret its value.
Actors are identities within a group. CCCS v1 standardizes only:
actor_id: stable string identifierrole:"foreman"or"peer"(optional; the collaboration semantics do not require a role)
All events MUST use the envelope below. Field semantics are fixed.
interface CCCSEventV1 {
v: 1
id: string // MUST be unique within the group ledger; SHOULD be globally unique (ULID/UUIDv7/UUID4)
ts: string // RFC3339 UTC timestamp assigned by the daemon at append time
seq?: number // OPTIONAL: monotonic sequence number assigned by the daemon (useful for streaming/cursors)
kind: string // e.g. "chat.message"
group_id: string
scope_key: string // "" allowed
by: string // principal id ("user", "system", actor_id, or "svc:<name>") set by the daemon
data: Record<string, unknown>
}- Clients MUST ignore unknown
kindvalues (but MAY display them as raw/unknown events). - Clients MUST ignore unknown fields inside
data. - Clients MUST preserve the event envelope when relaying/forwarding (see §9).
vis the envelope version. CCCS v1 requiresv: 1.- Implementations MAY add a
data.vfield for kind-specific versioning, but MUST NOT change envelope semantics without bumpingv.
- The authoritative ordering of events is the ledger append order.
tsMUST be assigned by the daemon at append time. Clients MUST NOT rely on client-local timestamps for ordering.- Implementations MAY record a client-provided timestamp (RECOMMENDED:
data.client_ts) for diagnostics or UI display, but it MUST NOT affect ordering.
Standard kinds use the chat.*, system.*, group.*, actor.*, context.*, and assistant.* namespaces.
Extensions SHOULD use one of:
x.<vendor>.*(recommended for private/vendor-specific kinds)vendor.<name>.*(alternative vendor namespace)
Clients MUST treat unknown kinds as opaque and ignore them unless explicitly supported.
Chat message routing uses to: string[] with these token types:
Actor IDs
- Example:
"peer-1","claude-1"
Selectors (MUST start with @)
@all: all actors in the group@peers: all peer actors@foreman: foreman actor(s)@user: the human user (UI recipient)
Compatibility
- Implementations MAY accept the literal token
"user"as equivalent to@user.
Multi-user note
- CCCS v1 assumes a single human principal per group, identified as
user. - Multi-user semantics (multiple distinct human principals) are out of scope for v1. Implementations MAY extend this outside of CCCS v1 (e.g.,
usr:<id>principals and selectors), but clients MUST remain forward-compatible.
If to is absent or an empty list, the message is a broadcast.
For compatibility with CCCC v0.4.x semantics, broadcast SHOULD be treated as equivalent to @all.
CCCS does not mandate a single permission model, but a conforming daemon MUST ensure:
- The daemon MUST set
event.byto the principal identity it ascribes to the event.- If the transport provides authentication,
event.byMUST be derived from the authenticated principal and clients MUST NOT be able to chooseevent.byarbitrarily. - If the transport does not provide authentication (local-trust IPC), a daemon MAY accept a client-provided principal hint (e.g., an RPC arg like
by) as the effective principal. Such deployments MUST document thatbyis not a security boundary.
- If the transport provides authentication,
- A principal cannot acknowledge (
chat.ack) on behalf of another recipient.
chat.message represents an IM-style message.
data: {
text: string
format?: "plain" | "markdown" // default "plain"
priority?: "normal" | "attention" // default "normal"
to?: string[] // recipient tokens (see §5)
reply_to?: string | null // replied-to event_id
quote_text?: string | null // display hint
// Cross-group provenance (relay/forward)
src_group_id?: string | null
src_event_id?: string | null
// Cross-group destination metadata (optional send record)
dst_group_id?: string | null
dst_to?: string[] | null
// Attachments and references (see §8)
attachments?: AttachmentRefV1[]
refs?: ReferenceV1[]
// Reserved for future threading
thread?: string
// Optional idempotency key (client-generated)
client_id?: string | null
}Rules
textMUST be present (it may be empty if and only if attachments convey the message).priority="attention"MUST trigger the attention/ack rules in §6.2.- If either
src_group_idorsrc_event_idis present, both MUST be present. - The
threadfield is RESERVED in v1; its semantics are undefined. Implementations MUST NOT rely onthreadfor v1 behavior. Clients MUST ignore it. - If
client_idis present, a daemon SHOULD provide best-effort idempotency for(group_id, by, client_id)within a bounded time window (RECOMMENDED: 5 minutes).- Duplicate submissions SHOULD return success with the original event reference, not a hard error.
chat.ack is the only completion signal for attention messages.
data: {
actor_id: string // the acknowledging recipient ("user" or an actor_id)
event_id: string // the acknowledged chat.message event_id
}Rules
- A daemon MUST enforce self-only ACK:
event.byMUST equaldata.actor_id. chat.ackMUST be idempotent per(group_id, actor_id, event_id); repeated ACK MUST NOT create repeated side effects.chat.ackMUST reference a validchat.messagewhosepriorityis"attention".- A daemon MUST reject ACK attempts for non-attention messages.
- The error
codeSHOULD beinvalid_request(or an implementation-specific equivalent such asnot_an_attention_message).
- The error
- A recipient MUST NOT be required to ACK a message that was not addressed to them.
chat.ackMUST be independent from read cursors. Marking read MUST NOT automatically clear the need for ACK.- Implementations MAY provide a convenience gesture where a recipient’s explicit “mark read” action on an attention message results in emitting
chat.ack, but thechat.ackevent MUST still exist as a distinct record.
- Implementations MAY provide a convenience gesture where a recipient’s explicit “mark read” action on an attention message results in emitting
chat.read records a recipient’s read watermark up to a given event.
data: {
actor_id: string // the reader/recipient ("user" or an actor_id)
event_id: string // the last read event_id (inclusive)
}Rules
- Read is a cursor, not an acknowledgement.
event_idMUST reference an event that exists in the group ledger.- For the Core Collaboration Profile,
event_idSHOULD reference an addressable event (RECOMMENDED:chat.messageorsystem.notify) and the daemon SHOULD reject watermarks for events that are not addressed toactor_id. - A daemon MUST enforce authorization: only the recipient (
event.by == data.actor_id) or an authorized privileged principal (e.g.,user) MAY emitchat.readfordata.actor_id. - “Inclusive” means the referenced
event_iditself is considered read. - If a client cannot efficiently determine ordering, it SHOULD treat
event_idas an opaque watermark maintained by the daemon.
data: {
event_id: string
actor_id: string
emoji: string
}System notifications are separated from chat to avoid polluting conversations.
data: {
kind: "nudge" | "keepalive" | "help_nudge" | "actor_idle" | "silence_check" | "automation" | "status_change" | "error" | "info" | string
priority?: "low" | "normal" | "high" | "urgent" // default "normal"
title?: string
message?: string
target_actor_id?: string | null // null = broadcast
context?: Record<string, unknown> // implementation-defined
requires_ack?: boolean // default false
related_event_id?: string | null // optional correlation
}Rules
- Clients MUST ignore unknown
data.kindvalues withinsystem.notify(open enum). - Implementations MAY enforce an allowlist of
data.kindvalues, but should not assume clients understand new kinds.
data: {
notify_event_id: string
actor_id: string
}Rules
system.notify_ackMUST be self-only:event.byMUST equaldata.actor_id.- A daemon MUST NOT allow a principal to ack on behalf of another recipient.
type AttachmentRefV1 = {
kind?: "text" | "image" | "file" // default "file"
path: string // group-scoped path (implementation-defined)
title?: string
mime_type?: string
bytes?: number
sha256?: string
// extra fields MAY exist; clients MUST ignore unknown fields
}type ReferenceV1 =
| {
kind?: "file" | "url" | "commit" | "text" // default "url"
url?: string
path?: string
title?: string
sha?: string
bytes?: number
// extra fields MAY exist; clients MUST ignore unknown fields
}
| {
kind: "presentation_ref"
v?: 1
slot_id: string
label?: string
locator_label?: string
title?: string
card_type?: string
status?: "open" | "needs_user" | "resolved"
href?: string
excerpt?: string
locator?: Record<string, unknown>
snapshot?: {
path: string
mime_type?: string
bytes?: number
sha256?: string
width?: number
height?: number
captured_at?: string
source?: "browser_surface" | "pdf_viewer" | "viewer_dom" | string
}
// extra fields MAY exist; clients MUST ignore unknown fields
}
| {
kind: "task_ref"
task_id: string
title?: string
status?: "planned" | "active" | "done" | "archived" | string
// extra fields MAY exist; clients MUST ignore unknown fields
}Rules
- Attachments SHOULD include content hashes where possible (
sha256) to enable reproducibility/auditing. pathMUST be stable and retrievable within the group’s storage scope.kind="presentation_ref"is a structured evidence anchor into a group Presentation slot.slot_idMUST identify the target slot within the group presentation surface.locatorSHOULD carry best-effort position hints for the current view (for example page, heading, row, or scroll state).snapshot, when present, SHOULD be treated as a best-effort visual anchor for the quoted view.snapshot.pathMUST be a stable group-scoped blob path.- Implementations SHOULD use
locatorfor precise recovery when possible, andsnapshotas a visual fallback when precise recovery is unavailable.
status, when present, is advisory metadata for the referenced discussion state; ledger-derived obligation status remains authoritative.
kind="task_ref"links a chat message to a durable shared task.task_idMUST identify a task in the same working group unless explicitly documented otherwise by a relay/bridge.titleandstatus, when present, are UI hints; the task store remains authoritative.
CCCS v1 does not mandate a transport, but implementations SHOULD provide a way to resolve AttachmentRefV1.path to bytes, for example:
- An HTTP endpoint (e.g.,
GET /groups/{group_id}/blobs/{path}), or - An RPC/IPC operation that returns attachment metadata and streams bytes.
CCCS v1 standardizes cross-group provenance via src_group_id/src_event_id on the destination message.
To relay a message from group A into group B:
- In group B, append a
chat.messagewhosedata.src_group_idanddata.src_event_idreference the original event in group A. - The relayed message MUST either:
- (a) include the original text verbatim, or
- (b) clearly indicate truncation/summarization in the message (e.g., prefix with
[Summarized]) and includesrc_group_id/src_event_idfor full content retrieval.
Rules
- If either
src_group_idorsrc_event_idis set, both MUST be set. - UIs SHOULD provide “Open source message” (jump-to) affordances.
- If the source is unavailable due to permissions or retention, clients MUST show a clear “source unavailable” state (not silent failure).
Implementations MAY also append an “outbound send record” in the source group (for auditability) by writing a local chat.message with:
dst_group_iddst_to
This record is OPTIONAL and MUST NOT be required for the destination’s provenance correctness.
CCCS v1 defines an abstract “event stream” interface:
- Input:
(group_id, since_cursor?, filters?, follow?) - Output: an ordered stream of
CCCSEventV1
CCCS does not mandate a single cursor type. A daemon SHOULD support at least one:
since_ts(timestamp cursor), orsince_event_id(event-id cursor), orsince_seq(monotonic sequence number)
Clients MUST treat cursors as opaque and MUST NOT infer ordering from id.
If since_seq is supported, it SHOULD correspond to the daemon-assigned event.seq field (when present).
Implementations SHOULD support filtering by:
kinds[](e.g., onlychat.message,system.notify)limit(for bounded replays)
Different implementations may support different cursor types and stream filters.
Implementations SHOULD expose capability discovery via their chosen transport (e.g., an info/capabilities endpoint or an IPC operation) so SDKs can adapt automatically.
To make SDKs interoperable, daemons SHOULD expose errors as:
{
code: string
message: string
details?: Record<string, unknown>
}Recommended stable code values:
invalid_requestpermission_deniedgroup_not_foundactor_not_foundevent_not_foundunknown_opdaemon_unavailable
A conforming daemon MUST:
- Enforce single-writer semantics for the ledger.
- Set
event.byto a principal identity consistent with the deployment’s trust model (authenticated vs. local-trust IPC) and document the security properties. - Enforce self-only ack rules for
chat.ackandsystem.notify_ack.
To reduce implementation burden, CCCS v1 MAY be implemented in profiles:
chat.message,chat.ack,chat.readsystem.notify,system.notify_ack(optional ack)- Recipient token semantics (§5)
group.*,actor.*
context.sync(implementation-defined ops)
The following examples use placeholder IDs for brevity. Conformance test vectors with complete values may be provided separately.
{
"v": 1,
"id": "01HZY2... (opaque)",
"ts": "2026-01-13T10:00:00Z",
"kind": "chat.message",
"group_id": "g_123",
"scope_key": "s_abc",
"by": "user",
"data": {
"text": "Please review the release checklist today.",
"format": "plain",
"priority": "attention",
"to": ["@foreman"]
}
}Ack:
{
"v": 1,
"id": "01HZY3... (opaque)",
"ts": "2026-01-13T10:01:00Z",
"kind": "chat.ack",
"group_id": "g_123",
"scope_key": "",
"by": "foreman",
"data": {
"actor_id": "foreman",
"event_id": "01HZY2... (opaque)"
}
}Destination group message:
{
"v": 1,
"id": "01HZY4... (opaque)",
"ts": "2026-01-13T10:02:00Z",
"kind": "chat.message",
"group_id": "g_dst",
"scope_key": "",
"by": "svc:relay",
"data": {
"text": "Relayed: please review the release checklist today.",
"priority": "attention",
"to": ["@all"],
"src_group_id": "g_src",
"src_event_id": "01HZY2... (opaque)"
}
}