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:

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:

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:

4. Canonicalization and Signatures

DARTC signatures cover the entire envelope except the signature field.

Implementations MUST:

  1. Remove signature.
  2. Canonicalize JSON by sorting object keys recursively.
  3. UTF-8 encode the canonical JSON string.
  4. Sign or verify those bytes with Ed25519.

GemmaPod implementation detail:

5. Topics

Reserved DARTC topics:

TopicPurpose
dartc.helloSession negotiation and topic advertisement
dartc.ackPositive acknowledgement for requires_ack messages
dartc.errorProtocol or application error
dartc.pingHeartbeat
dartc.closeGraceful shutdown

GemmaPod topics:

TopicPurpose
gemmapod.chat.requestChat completion request from widget to origin
gemmapod.chat.deltaStreamed text/reasoning delta from origin
gemmapod.chat.doneEnd of chat stream
gemmapod.tool.callOptional future direct tool-call event
gemmapod.tool.resultOptional future direct tool result

A2A topics:

TopicPurpose
a2a.discoveryExchange Agent Cards
a2a.messageSend or stream A2A Message objects
a2a.taskTask state, subscription, cancellation, and artifacts
a2a.capabilityCapability 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:

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:

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

  1. Shared @gemmapod/dartc package: envelope types, canonicalization, signing adapters, topic helpers, ack/error helpers, tests.
  2. Generic byte signing/verification exports in gemmapod-core.
  3. Browser shim sends dartc.hello and gemmapod.chat.request over the existing WebRTC DataChannel.
  4. Origin daemon accepts both legacy chat frames and DARTC envelopes, verifies DARTC signatures, and streams signed DARTC deltas back.
  5. Add A2A Agent Card generation from the signed pod manifest.
  6. Add WebSocket relay fallback using the same DARTC envelope.

12. Open Questions