AGDX
Understand AGDX records, delivery rules, trust boundaries, capabilities, and protocol mappings
AGDX (Agent Data Exchange Protocol) defines records and behavior for streaming, managed data, and agent coordination. Laser SDK implements it on Apache Iggy's durable partitioned log. Iggy supplies transport, retention, and consumer groups. The SDK records model provenance, which model produced an output, but does not call models.
This page explains architecture and application rules. The specification and docs/agdx.md define exact fields, limits, codes, and byte layout.
One contract, five layers
| Layer | Owns | Can be used alone |
|---|---|---|
| Substrate | Durable partitions, offsets, retention, consumer groups, pull-based reads | Laser SDK ships Apache Iggy |
| Wire | Portable types, named-field CBOR, dictionaries, limits, capability shapes, fixtures | Yes, by an independent port |
| Platform | Publish and consume, projections and query, key-value, forks, graph | Yes, with no agent concepts |
| Fabric | Agent envelopes, reliable consumers, coordination, context, memory, governance | Yes, with no public edge protocol |
| Edges | A2A, MCP, and AG-UI mappings | Only when an external client needs that contract |
The log remains the source of truth. Projections, indexes, state, graph data, run status, and agent registries derive from its records. Applications do not synchronize independent stores for these features.
Envelope anatomy
Each typed agent record contains a named-field CBOR AgentEnvelope, the structured agent message. The Iggy binding keeps routing information outside it when that information is needed before decoding.
Each field has one authoritative location. Agent records put agdx.ct, agdx.av, gen_ai.conversation.id, and optional agdx.to outside the envelope.
The envelope contains source, cause, correlation, deadlines, idempotency, operation details, and body. Generic records can use the broader provenance headers.
command {
kind: command
record: 01J...
conversation: 01J... # partition key and trace identity
source: planner # a claim unless verified
target: summarizer
correlation: 01J... # request and reply pairing
operation: summarize
body: <bytes> # decoded using agdx.ct
signature: <optional>
}| Field family | Fields | Purpose |
|---|---|---|
| Identity and routing | kind, record, conversation, source, target | What this is, who claims to have sent it, where it belongs |
| Causality | cause, cause_at, correlation | Parent record, optional native log position, request and reply pairing |
| Chunk lifecycle | channel, sequence, last, finish_reason | Ordered streaming and deterministic reassembly |
| Execution | operation, tool, task_state, deadline_micros, idempotency_key | What is happening and which safety constraints apply |
| Content | body, content type header | Opaque payload bytes and their codec |
| Accounting and policy | usage, metadata | Advisory token usage and pinned policy or routing context |
| Evolution and integrity | must_understand, signature | Strict feature handling and optional verified authorship |
Machine IDs are 128-bit values, displayed as 26-character Crockford base32 strings. CBOR encodes them as fixed 16-byte big-endian byte strings. cause identifies the parent record portably. Optional cause_at gives its Iggy log position for direct local lookup.
Legal message shapes
Message kind determines required and prohibited fields. The wire decoder, SDK constructors, and receivers enforce the same rules:
| Kind | Required core | Meaning |
|---|---|---|
command | record, conversation, source, correlation, body | Requests a reply or an effect. A fire-and-forget command is invalid |
response | record, conversation, source, correlation, body | Answers the command carrying the same correlation |
event | record, conversation, source, body | Announces something and expects no reply |
chunk | conversation, source, correlation, channel, sequence, body | Carries one ordered part of chat, reasoning, or tool_args |
status | record, conversation, source, operation | Carries task, card, progress, quarantine, or unquarantine state |
error | record, conversation, source, correlation, body | Terminates a request, and optionally a chunk channel, with a typed error |
A status record with operation task also requires correlation and task state. A chunk requires purpose at sequence 0 and forbids it on later chunks. Only a terminal chunk carries usage. Use typed SDK constructors instead of raw maps.
Command and stream lifecycle
Chunks share a channel and use sequence order within a conversation partition. Reassembly starts at zero, discards duplicate sequences, and stops at a gap. Only the first terminal record is accepted.
Offsets allow a reader to resume. Local gap and abandoned outcomes are not written back to the raw log.
Produce the same command in every SDK
const conversation = ConversationId.new()
const correlation = CorrelationId.parse(
conversation.toString()
)
const record = await laser
.agdx(
AgentTopic.Commands,
AgentId.new("planner"),
conversation
)
.command(correlation, utf8("summarize incident 42"))
.withOperation("summarize")
.send()let conversation = ConversationId::new();
let correlation =
CorrelationId::from_u128(conversation.as_u128());
let record = laser
.agdx(
AgentTopic::Commands,
"planner".parse()?,
WireConversationId::from(conversation),
)
.command(
correlation,
b"summarize incident 42".to_vec(),
)
.with_operation("summarize")
.send()
.await?;conversation = ls.new_conversation_id()
correlation = ls.new_correlation_id()
record = await laser.agdx(
ls.Topics.COMMANDS,
"planner",
conversation,
).command(
correlation,
b"summarize incident 42",
operation="summarize",
)Each client publishes the same logical command envelope and returns its generated record ID. The worker calls respond with the same correlation or opens a chunk stream for incremental output.
Delivery, ordering, and replay
Applications must account for these delivery rules:
- Delivery is at least once. Commit the consumer offset after successful handling. A crash before commit causes replay.
- Agent records use the conversation ID as their partition key. Each conversation is ordered, while separate conversations can run in parallel.
- Exactly-once effects require an application idempotency key and durable processed-key storage. They are not a wire delivery mode.
- Acknowledgement commits an offset. AGDX adds no ack, nack, visibility timeout, priority, or broker retry protocol.
- Dead-letter records include the original encoded envelope, source position, reason, attempt count, and optional detail.
- Large bodies use
BodyRefto identify external bytes, their size, and SHA-256 digest. Consumers must make sure that retrieved bytes match.
Trust boundary
Agent-written fields are claims until authenticated. Routing information does not grant access.
| Signal | Safe interpretation |
|---|---|
source, target, usage, cost, policy metadata | Advisory on a shared unsigned topic |
| Verified envelope signature | Proves the enrolled principal signed the canonical envelope |
| Signature context | Also binds the out-of-band content type and agent version |
| Write-exclusive Iggy topic with ACLs | Establishes authorship through topology |
| Server-stamped user on managed commands | Trusted input to capability RBAC |
| Fence token checked by the state store | Rejects a stale lease holder before an effect |
target restricts routing without granting permission. Usage and cost fields are advisory. Privileged control records, such as quarantine, require a valid operator signature.
Delegated work stores on_behalf_of in signed envelope metadata. An action must satisfy both the agent's grants and the user's grants.
More than agent messages
These data interfaces use standard authenticated Iggy transport. Filtered group readers also open dedicated coordinator and partition connections:
| Surface | Operations | Source of truth |
|---|---|---|
| Streaming | publish, consume, typed envelopes, replay, batching | Apache Iggy log |
| Consumer filters | group-aware reads, fenced acknowledgments, sample tests, previews, group filter policies and their revisions | Apache Iggy log, with the policy catalog in laser-plane |
| Materialized views | projections, schemas, query, change feed, graph traversal | Deterministic views derived from log records |
| Working state | key-value, compare-and-swap, fenced writes, leases, copy-on-write forks | Ordered mutations recorded through the platform |
Memory combines these interfaces rather than adding a wire command family. remember publishes a typed record. Recall reads an available view, graph operations manage relationships, and context supplies conversation scope.
Capability negotiation
The SDK probes hello during connection. The response reports the managed plane, operation versions, feature bits, backends, and resolved AGDX topic topology.
Backend descriptors report resource identity, generation, readiness, and supported operations. A backend that is replaying does not enable managed operations. After startup or restart, refresh capabilities or wait for readiness with a deadline. See Managed Data.
Capabilities control these behaviors:
- An unavailable interface returns typed
Unsupported. - A mismatched operation version fails locally before transmission.
- Optional guarantees, such as stronger query consistency, require explicit advertised support.
- Subfeatures default to off. A server must not advertise a guarantee that it cannot provide.
- Consumer filters report native evaluation, group-aware reads and the policy catalog separately. The LaserData Iggy fork evaluates filters and serves group reads without
laser-plane. Configuring a group's policy needs it, and without it every group reads unfiltered. - Standalone Iggy supports streaming and agent services backed by the log. Managed operations require advertised capabilities, supplied by
laser-planein Laser Stack.
Use laser.capabilities() to select supported operations. Applications do not need to infer support from failed requests or supply a manual capability list.
Interop is an edge mapping
Bridges translate public protocols into AGDX at the system boundary. Internal agents keep reading and appending durable records. A request passes through the external adapter, AGDX command, internal agent, AGDX reply or stream, and response adapter.
| External contract | AGDX mapping |
|---|---|
| A2A message and task lifecycle | Command on a fresh task conversation, then response, error, and task-status records |
MCP tools/call | Command carrying the tool name and correlated response or error |
| AG-UI chat, reasoning, and tool calls | Chunk streams rendered as frontend events |
| AG-UI shared state | state_snapshot and RFC 6902 state_delta events |
| Human approval | Ordinary command and response through request_input and respond_input |
Map shared meaning into envelope fields. Keep protocol-specific body bytes unchanged. Each bridge appends its ID to bridge_hops and rejects a record that already contains it. This prevents translation loops. See Interop.
Encoding and conformance
Every client must preserve these rules:
- Encode wire payloads through one named-field CBOR encoder.
- Encode exactly one CBOR item. Reject trailing bytes, corrupt known fields, and incorrect known types.
- Ignore unknown fields for additive changes. Preserve unknown dictionary codes without rejecting the whole record.
- Omit absent optional fields instead of inserting empty placeholders.
- Encode envelope IDs as 16-byte big-endian values. The duplicate Iggy
Uint128routing header uses its little-endian representation. - Apply the message-kind rules before publication and after decoding.
- Preserve accepted bytes and rejected shapes from the fixtures. Decoding, checking validity, and encoding again must produce identical bytes.
The laser-wire crate defines the contract and golden fixtures. It contains no transport, cryptography, clock, or ID-generation implementation. All clients use the same envelope rules and scenarios.