VeriFeed Working draft

Reference · Non-normative

Payload Profiles

What the entries mean, once the feed layer has stopped caring.

The feed inspects a payload only far enough to require a non-empty string type (§2). One wire format therefore carries any agent's stream, and an adopter starts from a blank page. Two profiles are worked out below. Neither is normative: nothing in the specification depends on them, and a conformant implementation need not support either.

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

TypeFieldsMeaning
agent/capability-announcedcapability, interface, facts_hashThe agent offers this capability
agent/capability-withdrawncapability, reasonIt 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

Not capability

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.

Not liveness

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.

Not discovery

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

TypeFieldsMeaning
registry/agent-registeredagent_id, facts_hash, endpointA new agent entered the registry
registry/agent-updatedagent_id, facts_hash, endpointAn existing agent's facts changed
registry/agent-deregisteredagent_id, reasonThe 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. An agent_id already present is a publisher error; a subscriber should not silently overwrite.
  • registry/agent-updated — replace the facts_hash and endpoint.
  • 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

Not truth

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.

Not currency

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.

Not erasure

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.