DARTC Protocol Specification v0.2
Distributed Agent Real-Time Communication
Status: Implementation Draft Date: May 13, 2026 Maintainer: GemmaPod Project License: MIT
1. Summary
DARTC is the real-time transport layer for portable AI agents. It defines a signed, topic-multiplexed message envelope over WebRTC DataChannels, with a WebSocket relay fallback when peer-to-peer connectivity fails.
DARTC is designed to carry A2A messages without replacing A2A:
- A2A is the semantic layer: Agent Cards, messages, tasks, artifacts, and agent capability discovery.
- DARTC is the real-time binding: low-latency peer-to-peer delivery, streaming, topic multiplexing, acknowledgements, and session policy.
- MCP remains the tool/data access layer behind an agent runtime.
In GemmaPod, the signed pod manifest is the initial trust anchor. DARTC messages then provide session-level integrity, topic routing, replay defense, and a clean path to A2A-compatible agent-to-agent interaction.
2. Current A2A Alignment
DARTC v0.2 targets the current Agent2Agent model:
- Agent discovery uses an
AgentCard. - Interaction payloads use
Message,Task,Artifact, andPartobjects. - Streaming should be modeled as task/message updates and stream closure, not as a custom final task status.
- A2A protocol bindings may include HTTP+JSON, JSON-RPC, gRPC, and this DARTC binding.
DARTC topics beginning with a2a. MUST carry A2A-shaped objects in the a2a
field. DARTC-specific streaming metadata belongs in the dartc field.
3. Envelope
Every DARTC frame is one JSON object.
type DartcEnvelope<TPayload = unknown, TA2A = unknown> = {
version: "0.2";
msg_id: string;
from: string;
to: string;
topic: string;
timestamp: number;
signature: string;
a2a?: TA2A;
dartc?: {
stream?: boolean;
chunk_id?: number;
is_final?: boolean;
priority?: "low" | "normal" | "high";
requires_ack?: boolean;
ack_for?: string;
};
payload?: TPayload;
};Rules:
versionis the DARTC envelope version.msg_idSHOULD be UUIDv7. Implementations MAY accept UUIDv4 during early development.fromandtoare agent/session identifiers.to = "*"means broadcast within the current connection.topiccontrols routing and policy.timestampis Unix epoch milliseconds.signatureis base64 Ed25519 over the canonical JSON representation of the envelope withsignatureomitted.payloadis for non-A2A topics or DARTC binding-specific data.
4. Canonicalization and Signatures
DARTC signatures cover the entire envelope except the signature field.
Implementations MUST:
- Remove
signature. - Canonicalize JSON by sorting object keys recursively.
- UTF-8 encode the canonical JSON string.
- Sign or verify those bytes with Ed25519.
GemmaPod implementation detail:
- Packed pods do not contain the owner private key.
- Visitor/browser sessions generate an ephemeral session key.
- The origin daemon may sign responses with an origin session key or an owner key if configured.
- The signed pod manifest remains the authority for pod identity, system prompt, model preference, transport policy, and tool allow-list.
5. Topics
Reserved DARTC topics:
| Topic | Purpose |
|---|---|
dartc.hello | Session negotiation and topic advertisement |
dartc.ack | Positive acknowledgement for requires_ack messages |
dartc.error | Protocol or application error |
dartc.ping | Heartbeat |
dartc.close | Graceful shutdown |
GemmaPod topics:
| Topic | Purpose |
|---|---|
gemmapod.chat.request | Chat completion request from widget to origin |
gemmapod.chat.delta | Streamed text/reasoning delta from origin |
gemmapod.chat.done | End of chat stream |
gemmapod.tool.call | Optional future direct tool-call event |
gemmapod.tool.result | Optional future direct tool result |
A2A topics:
| Topic | Purpose |
|---|---|
a2a.discovery | Exchange Agent Cards |
a2a.message | Send or stream A2A Message objects |
a2a.task | Task state, subscription, cancellation, and artifacts |
a2a.capability | Capability or extension advertisement |
Application topics may use any non-reserved prefix, such as orders,
negotiate, calendar, or support.
6. Session Handshake
After the WebRTC DataChannel opens, both peers SHOULD exchange dartc.hello.
{
"version": "0.2",
"msg_id": "018f2f42-...",
"from": "visitor:session-pubkey",
"to": "pod:raj-card:origin",
"topic": "dartc.hello",
"timestamp": 1747070000000,
"dartc": { "requires_ack": true },
"payload": {
"role": "visitor",
"pod_id": "raj-card",
"agent_id": "visitor:session-pubkey",
"protocol_versions": { "dartc": "0.2", "a2a": "0.2.2" },
"supported_topics": ["gemmapod.chat.*", "a2a.discovery", "dartc.*"],
"signedManifestB64": "..."
},
"signature": "..."
}The origin daemon MUST verify:
- The DARTC envelope signature.
timestampis within the accepted clock skew window.msg_idhas not been seen before on this session.- The signed manifest is valid.
- The manifest pod id or WebRTC pod id matches the registered origin pod id.
- The owner pubkey matches
OWNER_PUBKEYwhen configured. - The requested topics are allowed by local origin policy.
7. GemmaPod Chat Binding
gemmapod.chat.request payload:
type GemmaPodChatRequest = {
request_id: string;
model?: string;
messages: Array<{ role: "system" | "user" | "assistant"; content: string }>;
signedManifestB64?: string;
};gemmapod.chat.delta payload:
type GemmaPodChatDelta = {
request_id: string;
delta: string;
};gemmapod.chat.done payload:
type GemmaPodChatDone = {
request_id: string;
};dartc.error payload:
type DartcErrorPayload = {
code: string;
message: string;
request_id?: string;
fatal?: boolean;
};8. A2A Binding
For topics beginning with a2a., the a2a field carries the A2A object or
operation payload. The DARTC envelope provides delivery metadata only.
Example Agent Card exchange:
{
"version": "0.2",
"topic": "a2a.discovery",
"from": "pod:raj-card:origin",
"to": "visitor:session-pubkey",
"a2a": {
"kind": "AgentCard",
"card": {
"name": "Raj Card",
"description": "A portable AI business card.",
"capabilities": { "streaming": true },
"skills": []
}
},
"dartc": { "stream": false },
"payload": {
"binding": "dartc",
"topics": ["a2a.discovery", "a2a.message", "a2a.task"]
},
"signature": "..."
}9. Backpressure and Chunking
v0.2 uses ordered reliable WebRTC DataChannels by default.
Implementations SHOULD:
- Keep individual JSON frames below 64 KiB.
- Use
chunk_idfor ordered stream chunks. - Set
is_finalonly for DARTC stream completion metadata. A2A task completion still follows A2A task state semantics. - Use
requires_ackfor high-value control messages, not for every text delta.
10. Relay Fallback
DARTC is transport-neutral above the envelope. When WebRTC cannot connect, GemmaPod Cloud MAY relay the same signed envelopes over WebSocket.
Relay servers MUST NOT need to decrypt or mutate DARTC payloads. They MAY route
by to, topic, and pod/session metadata.
11. Initial Implementation Milestones
- Shared
@gemmapod/dartcpackage: envelope types, canonicalization, signing adapters, topic helpers, ack/error helpers, tests. - Generic byte signing/verification exports in
gemmapod-core. - Browser shim sends
dartc.helloandgemmapod.chat.requestover the existing WebRTC DataChannel. - Origin daemon accepts both legacy chat frames and DARTC envelopes, verifies DARTC signatures, and streams signed DARTC deltas back.
- Add A2A Agent Card generation from the signed pod manifest.
- Add WebSocket relay fallback using the same DARTC envelope.
12. Open Questions
- Should DARTC standardize CBOR framing after JSON v0.2 is stable?
- Should topic-level encryption use sender keys, shared session keys, or an MLS style group protocol?
- Should Agent Cards declare DARTC as an A2A extension or a formal protocol binding?
- What should the minimum replay cache window be for browser-origin sessions?