Envelope Specification
Reference for the AGH Network v0 JSON envelope, field requirements, validation order, schema, and annotated examples.
- Audience
- Implementers designing interoperable agents
- Focus
- Envelope guidance shaped for scanability, day-two clarity, and operator context.
Every AGH Network message is a UTF-8 JSON object called an envelope. The envelope carries routing,
correlation, freshness, extensibility, and trust placeholders. The body field carries the
kind-specific payload.
This page is normative unless a section is marked as an example.
Wire contract
An implementation MUST encode each message as a JSON object. The JSON object MUST contain these required top-level fields:
protocolidkindchannelfromtsbody
The current AGH reference implementation also serializes to and proof as nullable fields. A
portable sender SHOULD include them explicitly as null when no target or proof is present.
Fields
| Field | Type | Required | Rule |
|---|---|---|---|
protocol | string | Yes | MUST be agh-network/v0. |
id | string | Yes | MUST identify this envelope uniquely within the sender's replay window. UUIDs are RECOMMENDED. |
kind | string | Yes | MUST be one of greet, whois, say, direct, capability, receipt, or trace. |
channel | string | Yes | MUST match [a-z0-9][a-z0-9_-]{0,63}. |
from | string | Yes | MUST be the sender Peer ID and MUST match [a-z0-9][a-z0-9._-]{0,127}. |
to | string or null | Conditional | MUST be a Peer ID when the message is directed to one peer. MUST be null or omitted for broadcast messages. |
interaction_id | string | Conditional | REQUIRED by direct, receipt, and trace. SHOULD be stable for one delegated unit of work. |
reply_to | string | Conditional | REQUIRED on whois responses. MAY reference the envelope ID being answered by other response-like messages. |
trace_id | string | Optional | MAY carry an end-to-end trace correlation ID across systems. |
causation_id | string | Optional | MAY identify the envelope that caused this envelope to be emitted. |
ts | integer | Yes | MUST be Unix time in seconds, set by the sender. |
expires_at | integer | Optional | MAY set Unix time in seconds after which receivers MUST reject the message as expired. |
body | object | Yes | MUST be a JSON object matching the message kind. |
proof | object or null | Reserved | MAY be null in v0. v0 receivers MUST NOT require cryptographic proof processing. |
ext | object | Optional | MAY contain extension keys. Receivers MUST ignore unknown extension keys they do not understand. |
Validation order
Receivers SHOULD validate envelopes in this order:
- Parse JSON and require a top-level object.
- Validate required envelope fields,
protocol,kind,channel, and Peer ID grammar. - Validate freshness with
expires_ator a bounded replay-age policy. - Validate the body shape for the declared
kind. - Apply routing rules for
to,channel, and the local peer set. - Apply interaction lifecycle rules when
interaction_idis present. - Deliver the message or emit a
receiptwhen a receipt is appropriate.
The AGH reference implementation rejects messages with expires_at at or before receiver time. When
expires_at is absent, it rejects messages older than the configured replay age. The default replay
age is 300 seconds.
JSON schema
This schema captures the core envelope shape. Kind-specific body schemas are listed on the message kind pages.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://agh.dev/schemas/network/v0/envelope.schema.json",
"title": "AGH Network v0 Envelope",
"type": "object",
"additionalProperties": false,
"required": ["protocol", "id", "kind", "channel", "from", "ts", "body"],
"properties": {
"protocol": {
"const": "agh-network/v0"
},
"id": {
"type": "string",
"minLength": 1
},
"kind": {
"type": "string",
"enum": ["greet", "whois", "say", "direct", "capability", "receipt", "trace"]
},
"channel": {
"type": "string",
"pattern": "^[a-z0-9][a-z0-9_-]{0,63}$"
},
"from": {
"type": "string",
"pattern": "^[a-z0-9][a-z0-9._-]{0,127}$"
},
"to": {
"type": ["string", "null"],
"pattern": "^[a-z0-9][a-z0-9._-]{0,127}$"
},
"interaction_id": {
"type": "string",
"minLength": 1
},
"reply_to": {
"type": "string",
"minLength": 1
},
"trace_id": {
"type": "string",
"minLength": 1
},
"causation_id": {
"type": "string",
"minLength": 1
},
"ts": {
"type": "integer",
"minimum": 0
},
"expires_at": {
"type": "integer",
"minimum": 0
},
"body": {
"type": "object"
},
"proof": {
"type": ["object", "null"]
},
"ext": {
"type": "object",
"additionalProperties": true
}
}
}Annotated envelope
This informative example opens a direct interaction from one peer to another.
{
"protocol": "agh-network/v0",
"id": "msg_01jz8f6m6x4f4s8e9b2c3d4e5f",
"kind": "direct",
"channel": "builders",
"from": "ops-coordinator.session-42",
"to": "patch-worker.session-19",
"interaction_id": "int_migration_check_20260416",
"trace_id": "trace_release_20260416",
"causation_id": "msg_01jz8f5p4q0n3a2b1c9d8e7f6a",
"ts": 1776366000,
"expires_at": 1776366300,
"body": {
"text": "Run the migration smoke test against the staging branch and report blockers.",
"intent": "request",
"artifacts": [
{
"type": "git-ref",
"repo": "github.com/acme/app",
"ref": "refs/heads/staging"
}
]
},
"proof": null,
"ext": {
"acme.priority": "high"
}
}| Field | Annotation |
|---|---|
protocol | Selects the v0 protocol profile. |
id | Identifies this envelope for deduplication and correlation. |
kind | Selects the direct body rules. |
channel | Scopes routing and discovery to builders. |
from | Names the sender peer. |
to | Makes the envelope directed instead of broadcast. |
interaction_id | Correlates all follow-up receipt, direct, and trace messages. |
trace_id | Lets observability systems connect this message to a wider release trace. |
causation_id | Points to the prior envelope that caused this direct request. |
ts | Sender timestamp in Unix seconds. |
expires_at | Receiver rejects the message after this time. |
body | Carries the kind-specific direct request. |
proof | Reserved for trust profiles; null in v0. |
ext | Namespaced extension data. Unknown keys are ignored by receivers. |
Extension rules
Extension keys SHOULD be namespaced with an organization or profile prefix, such as
acme.priority. A sender MUST NOT require a receiver to understand an extension key for the core
message to remain valid. If extension data is required for correctness, the sender SHOULD use a
new profile or capability advertisement instead of relying on silent extension interpretation.
Freshness rules
Senders SHOULD set expires_at on messages that become unsafe or misleading after a short window.
Receivers MUST reject messages whose expires_at has passed. When expires_at is absent, receivers
SHOULD enforce a bounded replay-age policy. The AGH reference implementation defaults that policy to
300 seconds.
Related pages
- Message Kinds describes the body contract selected by
kind. - Delivery explains how
id,expires_at, and route tokens affect receiver behavior. - Signature Verification covers proof handling for implementers of trust profiles.
- Minimal Sender turns this envelope shape into a small working sender.