Reserved namespace
The feed/ type prefix is reserved for conventions the specification defines
(Appendix A). A profile MUST NOT mint its own feed/* types.
Use a prefix of your own: agent/, registry/.
ACapability announcement
An agent announcing what it can do is the first-person case. The agent is both subject and
issuer, so feed_id is the agent's own DID and every announcement is signed by the
party the announcement is about.
Why announcements need completeness
Announcing a capability is cheap and safe. Withdrawing one is where a subscription channel earns its place.
A peer that learns "agent A offers route-planning" and never receives the later
agent/capability-withdrawn keeps dispatching work to an agent that has retired it.
The peer cannot notice: it holds a signed, well-formed announcement, and the absence of a later
one is identical to the absence of any change. A hash-chained feed rules this out — a subscriber
walking a contiguous run from its anchor either has every announcement in between, or the chain
breaks.
Payload types
| Type | Fields | Meaning |
|---|---|---|
| agent/capability-announced | capability, interface, facts_hash | The agent offers this capability |
| agent/capability-withdrawn | capability, reason | It no longer does |
capability is a short identifier the agent chooses and keeps stable across
announcements; re-announcing under a new name reads as two capabilities, not one renamed.
interface names how to invoke it ("a2a", "mcp", an OpenAPI
URL) and carries no further meaning in this profile. facts_hash is
"sha256:<hex>" over the JCS-canonical AgentFacts document describing the
capability, so the entry carries a commitment, not the document, and a subscriber can fetch and
check the facts independently.
{
"type": "agent/capability-announced",
"capability": "route-planning",
"interface": "a2a",
"facts_hash": "sha256:9f2c…"
}
Re-announcing a capability already live replaces its interface and
facts_hash. That is how an agent moves a capability to a new interface without
opening a withdrawal gap.
Materializing state
Fold the verified run, in seq order, into a map keyed by capability:
agent/capability-announced— insert or replace.agent/capability-withdrawn— remove.
seq order is what resolves an announce / withdraw / re-announce sequence correctly.
Applied out of order, a withdrawal can erase a later re-announcement and leave the capability
missing. The chain forbids that reordering, not a convention about timestamps
(§5.2).
Compaction
An agent running for months should not make a new peer replay every announcement. It emits a
feed/state-checkpoint (§6) whose state_digest
is "sha256:<hex>" over the JCS-canonical live capability set. A late joiner
adopts it, inherits the agent's word for everything at or before it, and establishes completeness
only after.
Limits
It does not establish that the agent can do the thing
A verified agent/capability-announced establishes that the agent said it, in this order, completely. Whether the capability works, whether it is any good, and whether the agent is entitled to offer it are outside both this profile and the feed. A self-announced capability is a claim; a verified claim is still a claim.
It does not carry availability, and must not be made to
The temptation is an agent/availability-changed type — online, degraded, offline. Such a type cannot work in a feed. A feed establishes that you received a complete run between two anchors, never that you are current, and a frozen view of a liveness signal is indistinguishable from a live one that has not changed. Worse, an agent that has gone offline stops publishing, which is what withholding looks like. Liveness needs a channel where silence is itself evidence — a heartbeat with a deadline. Announce capabilities here; get availability from something that fails closed.
It does not tell a peer where to find the feed
Resolving an agent DID to a feed endpoint is a resolver's function and a registry's. This profile begins once the subscriber knows whose feed it is reading.
BRegistry change log
The same shape in the third person. A registry answers which agents exist and what their facts are, and subscribers — peer registries, resolvers, caches — need that answer to stay current, which usually means an incremental endpoint: give me what changed since cursor N.
An unsigned delta endpoint asks the puller to trust the server for completeness. A registry can drop one deregistration from one puller's response, and that puller keeps routing to an agent the registry has revoked. The failure is silent, invisible to the victim, and indistinguishable from nothing having happened.
Payload types
| Type | Fields | Meaning |
|---|---|---|
| registry/agent-registered | agent_id, facts_hash, endpoint | A new agent entered the registry |
| registry/agent-updated | agent_id, facts_hash, endpoint | An existing agent's facts changed |
| registry/agent-deregistered | agent_id, reason | The agent is no longer served |
agent_id is the agent's DID. facts_hash is
"sha256:<hex>" over the JCS-canonical AgentFacts document the registry served
at that moment. reason is a short free-text string to which this profile assigns no
semantics.
{
"type": "registry/agent-updated",
"agent_id": "did:key:z6MkfExample",
"facts_hash": "sha256:9f2c…",
"endpoint": "https://agent.example/a2a"
}
Materializing state
A subscriber folds the verified run into a map keyed by agent_id:
registry/agent-registered— insert. Anagent_idalready present is a publisher error; a subscriber should not silently overwrite.registry/agent-updated— replace thefacts_hashandendpoint.registry/agent-deregistered— remove.
Fold in seq order. A registered / deregistered pair applied out of order leaves the
agent present when it should be gone.
Compaction
A registry running for a year should not make a new subscriber replay every mutation. It emits a
feed/state-checkpoint whose state_digest is over the JCS-canonical
materialized map, serialized as a sorted-key object of agent_id →
{facts_hash, endpoint}. A late joiner adopts the checkpoint, fetches the
corresponding snapshot out of band, checks that hashing it reproduces state_digest,
and walks forward from seq + 1.
Limits
It does not make the registry's claims true
A verified registry/agent-registered establishes that the registry said it, in this order, completely. Whether the agent exists, whether the facts are accurate, and whether the registry was entitled to say so are outside both this profile and the feed. Establishing who controls a subject is a different primitive's function.
It does not make a subscriber up to date
Completeness is a property of the run between two anchors, never of being current. A registry can serve one subscriber a chain-correct view frozen months ago. A subscriber that needs freshness has to obtain it elsewhere.
It does not support deletion
A deregistration entry stays in the chain permanently; removing it would break the chain for everyone. A registry with an erasure obligation needs the entry to have carried a commitment, not the data, from the outset — hence facts_hash is a hash and not the document.
Composition
The two profiles compose. An agent publishes its own announcements; a registry that subscribes to many such feeds aggregates them into a change log of its own, which its peers subscribe to in turn. Each hop is independently verifiable, and no hop asks the one after it to trust the server for completeness.