Agent Action Capsule · explain
12 / 16 · From the September deck

Who can check what: three vantage points

The same records, checked from three places: your own node, the other party to the exchange, and anyone holding the evidence file. Each place adds a party who can check without trusting the one before, and each check is reported on its own, with what it does not show.

One record checked from three rings: local, bilateral, relying party Left: three concentric rings. The inner ring is your own node, the middle ring is the other party to the exchange, the outer ring is a relying party, such as an auditor or anyone with the evidence file. A record starts in the inner ring and moves outward one ring at a time. Right: three groups of checks, one per ring, that light as the record reaches that ring. Step 1, local: the capsule_id recomputes and the signature verifies under the node key. Step 2, local: inclusion under a signed checkpoint and consistency with the previous checkpoint; summary, my log is intact, in order, unchanged. Step 3, bilateral: the record arrives with its inclusion proof and checkpoint; the sender's announced key is the record's key and the signature verifies; the proof reaches the sender's signed root. Step 4, bilateral: a server a served half names is its sender, a model it names does not contradict the receiver's own record of that request fingerprint if one is held, and its checkpoint gives no other root for a size the receiver already holds; summary, with both halves held, a difference shows. Step 5, relying party: signatures, checkpoint, inclusion and disclosed text checked offline from an evidence bundle with no account, not yet joined for a mesh node's records, and a witness receipt if the operator named a witness. Step 6, relying party: a referee's verdict checks under the referee's key, and a refusal, when given, is signed; summary, what was recorded and when, unchanged. Step 7: a banner across the bottom says no ring shows whether the answer was right; that is judgment, layered on top. 1 · local your own node 2 · bilateral the other party 3 · relying party auditor, anyone with the file record checkpoint record + proof + ckpt file offline 1 · Local: holds its own log and signed checkpoints ✓ capsule_id recomputes from the record (integrity) ✓ signature verifies under this node's key ✓ inclusion under a signed checkpoint · consistency with the last My log is intact, in order, unchanged. 2 · Bilateral: receives the sender's half, proof, checkpoint ✓ announced key = record's key_id; signature verifies ✓ inclusion proof reaches the sender's own signed root ✓ server named = sender; model fits my record, if held Both halves held: a difference between them shows. 3 · Relying party: gets a file, needs no account ✓ signatures, checkpoint, inclusion (mesh: not joined) ✓ witness receipt, if the operator named a witness ✓ verdict under referee's key · refusals, when given, signed Recorded, unchanged; when: a witness shows it. Not in any ring: whether the answer was right. That is judgment, layered on top, named as a gap.

1What it is

A vantage point is a place a record is checked from: the node that sealed it, the other party to the same exchange, or a relying party who was in neither seat.

Each one holds different material (its own log; the other side's half with its proof; a file with no account behind it), so each can run a different set of checks.

Every check says what it shows and what it does not, and none of them, at any distance, says whether an answer was right.

2Design choices

Chosen: each check is a separate, independent link; a verifier stops at the level it needs, and each link states what it does not cover.
Rejected: one combined trust value; it would hide which check failed and round a gap away. (capsules docs/VERIFICATION-CHAIN.md: "Each link is independent" under "The whole chain, as one offline check"; "History is evidence, not a score" in the closing list of what the chain leaves open)
Chosen: the receiver looks up its own record of a pushed half by the half's request fingerprint. A fingerprint is not unique (one prompt sent to several peers shares it), and a push can arrive before the receiver seals its own record, so finding no own record is never a refusal. (record_push_receive.rs step 7)
Rejected: joining by exchange_id; each node mints its own, so the two would never meet. (capsules docs/VERIFICATION-CHAIN.md, opening section, which joins the two halves for display by the shared client nonce; the request fingerprint is the receiver's lookup for its own record)
Chosen: cheap refusals before any work: a push from a sender with no announced key is refused before its record is checked, and a received half is held apart, never written into the receiver's own chain.
Rejected: storing what arrives and checking later; anyone's flood would cost the receiver work and disk, and another node's record would sit in this node's history. (record_push_receive.rs, steps 1–9 and "What is kept")
Chosen: no default witness. The operator names one or more; a receipt counts only once it checks offline under the witness's key.
Rejected: a built-in witness; one log is only as neutral as its operator, and the structural answer is more than one witness holding the same checkpoint. (witness_status.rs; VERIFICATION-CHAIN Link 7)
Chosen: a refusal is an answer: signed by the responder, binding the digest of the request and the time it was issued.
Rejected: declining by silence, which leaves nothing a requester can show later; an unanswered request is a recorded absence, kept distinct from a refusal. (draft-mih-agent-evidence-request-00, "The Signed Refusal" and "The unaccountable refusal")

3What it shows / what it does not

Shows

  • Local: this exact record has not changed since it was sealed; this node's key signed it; it is in the log under a signed checkpoint; each checkpoint this node holds extends the one before it.
  • Bilateral: the other side's half is signed under the key it announced and is in its own signed log; a server a served half names is its sender, and a model it names does not contradict my own record of the same request fingerprint, when I hold one.
  • Bilateral: a sender whose checkpoint gives another root for a size that a checkpoint I already hold signed is caught (checkpoint_equivocation).
  • Relying party: from an evidence bundle, offline: record and checkpoint signatures, coverage, each record's place in the log, disclosed text against its digest; with a witness named, that the checkpoint existed outside the node by a time.
  • Relying party: which key signed a verdict, and which key signed a refusal, when one was given.

Does not show

  • Local: that the content is true, or that the node keeps only this one history; that needs someone outside it.
  • Bilateral: which side is right when the two differ, or that the two sides did not collude.
  • Bilateral: who a key belongs to; the key is node-generated, so it shows continuity, not identity.
  • Relying party: that a key is one they already trust: the signatures verify under keys the file itself carries.
  • Relying party: that every event was logged, or that the witness is honest on its own; one witness is only as neutral as its operator.
  • Relying party: the receiver-side checks of the bilateral ring, which are not in the bundle; and, for a mesh node's records, the bundle verifier is not joined yet.
  • Any ring: who withheld a record by silence; that is a recorded absence, not a refusal.
  • Any ring: whether the answer was right for you. That is judgment, and is named as a gap, never rounded away.

4Example

What the bilateral ring receives: one node's sealed half of a served exchange, pushed as a bundle with its inclusion proof and the signed checkpoint it sits under. The plugin's own generator test writes this vector; trimmed here.

{
  "record_push_bundle": 1,
  "capsule": {
    "spec_version": "draft-mih-scitt-agent-action-capsule-05",
    "format_version": "4",
    "capsule_id": "ddd8685c1b578b39c9163a5c748ac7148108663719227f7636b2a113a1dd34dc",
    …
    "effect": {
      "type": "inference_completion",
      "request_digest": "6d139f1403cd84518768857efed6c375c0e3fe1af3f250cd3a5c59ea87a0323c",
      "response_digest": "a8461110d26a1ceb91765128a3f8a648e86238810f4bdf27a330deb8681f9d87",
      …
    },
    "chain": {
      "parent_capsule_id": "6f1165b4e8f487815ea9bae5674c61ebe50862efa903ba8b0733c227f5303a1e",
      "relation": "follows"
    },
    "signature": "d284584ca3012703…",
    "key_id": "9e4480104975505a70ec6fc913efff2884a93af380a657adf39babf91fbe1fe1"
  },
  "inclusion": {
    "leaf_index": 1,
    "proof": { "v": 1, "kind": "inclusion", "size": 3, "leaf_index": 1,
      "witness": ["8ec22eba289ccb94b3a7f9504ae4e648f91b4518bee10bb9b0da1e9a38d2af84"],
      "peaks_left": [], "peaks_right": [] }
  },
  "checkpoint": {
    "v": 1, "kind": "mmr_checkpoint", "log_id": "rust-node",
    "mmr_size": 3,
    "root": "f85629f081a79ba7b416e4ea5f1dec66c73bed90fb19e4c2b2af5e4df432f524",
    "prev_size": 0, "prev_root": "",
    "key_id": "9e4480104975505a70ec6fc913efff2884a93af380a657adf39babf91fbe1fe1",
    "timestamp": "2026-09-30T03:38:00.000Z",
    "signature": "802a4a60a953709b…"
  }
}
FieldTypeMeaning
record_push_bundleintegerMarks a bundle (version 1), not a bare half. Plugin-internal, not a standardized wire.
capsule.capsule_idhexSHA-256 of the record's JCS form without capsule_id, signature, key_id. The local integrity check.
capsule.effect.request_digesthexThe request fingerprint the receiver looks its own record up by.
capsule.chainobjectThe previous record in the sender's own log; committed in format 4, so a forged parent changes the id.
capsule.key_idhexThe sender's key. Must be the key the sender announced, and the signature must verify under it.
inclusion.proofobjectSibling hashes from this leaf up to its peak, in an MMR of size 3 nodes (two records).
checkpoint.roothexThe log's root at mmr_size 3 nodes, signed under the same key. The proof must reach it.
checkpoint.prev_size / prev_rootint / hexThe checkpoint this one extends; 0 and empty here, as this is the log's opening checkpoint.

Recomputed for this page: the record's sorted, compact JSON without those three members hashes to ddd8685c…, its stated capsule_id. The leaf hash is SHA-256(0x00 ‖ capsule_id); SHA-256 of the node position (2, 0-based, written as 3 in eight bytes), then the witness hash on the left and the leaf on the right, gives f85629f0…, the checkpoint's signed root. That is the bilateral ring's inclusion check (step 9 of record_push_receive.rs), by hand.

Source: Mesh-LLM/capsules:vectors/record_push_bundle_rust.json @ efcada74026a2e18b9e62d77432888a31bbe9de4 (written by writes_the_cross_language_bundle_fixture, vectors/SOURCES.md); hashes per action-state-group/checkpointed-local-log:rust/checkpointed-local-log/src/mmr.rs @ 40dabd74508d0f8649c8754873e316badef1057d

5Status

Checked today against the plugin's release tag v0.1.4 (b4e24453f572b2d9fe8f2cefbd0f35a5eb54b0ad) and its main (efcada74026a2e18b9e62d77432888a31bbe9de4, eleven commits later); each plugin row's claim holds at both, though the code under some rows changed in between. The verifier rows are checked against their own repositories. live is in a release and on by default; built is in the code and tested, but off by default, pre-release only, or not yet joined end to end; designed is specified, with no checking code on this path.

CheckStatusEvidence
Local: integrity, signature, checkpoints, inclusion and consistencyliveEach node seals its own record of every exchange; the log's checkpoints and integrity checks work. README.md "What works today"; docs/VERIFICATION-CHAIN.md Links 1 and 3–6 (Link 2, disclosed text, is the designed row below).
Bilateral: receive the other side's half and check key, signature, claims, proof, checkpointbuiltOff by default; needs mesh-llm 0.78 or later (which names the peer that asked and the peer that served, mesh-llm#2138) and record push turned on at both ends. README.md; record_push_receive.rs steps 1–9.
Bilateral: shared text hashes to the sealed digestdesignedBlocked on the host: mesh-llm sends no exchange bodies today, so the plugin's opt-in text keeping (off by default) has nothing to keep, and there is no digest-match check on this path. exchange_text.rs. The disclosure format is draft-mih-zhang-agent-disclosure-bundle-00.
Relying party: verify a bundle offline, no accountbuiltPre-release CLI. capsulectl verify --bundle checks checkpoint signature, interval coverage, each record's place, citation closure, record signatures and disclosed payloads. action-state-group/capsule-cli:README.md @ 551b5c616641d15089eb0aac747f5c90aa29bf69.
Relying party: the same, on a mesh node's recordsbuiltNot joined yet: a mesh-plugin profile asks a node's book over its evidence-request tool, but its bundles are carried, not verified. Same README, "A mesh-plugin profile".
Witness receipt (RFC 9942)builtNo default witness, and none contacted until the operator names one; a receipt counts only once it checks offline under the witness's key, configured by the operator or, when none is, pinned from the witness itself. witness_status.rs; scitt-cose-receipt pinned at 8833281c11d33e3c1e289cf74f11d25ee7f3c6dc in Cargo.toml.
Several witnesses: a split history is detectabledesignedVERIFICATION-CHAIN Link 7. What is built is one receiver's check: a checkpoint contradicting one already held is refused checkpoint_equivocation (record_push_receive.rs step 9).
Verdict under the referee's keybuiltOff by default; the verdict must verify under the referee's announced key before a node holds it. README.md; referee/hold.rs check 1.
Signed refusalliveRefusals of an evidence request and of a pushed record are signed and bind the digest of the request bytes as received (evidence_answer.rs, record_push_receive.rs). Two replies are not signed: the record-push bridge's own citing_record_seal_failed reply (record_push_bridge.rs) and refusals on the record-lookup channel (ledger_fetch_bridge.rs). capsulectl book accepts a refusal only if its signature verifies offline; that shows which key refused, not who holds it (capsule-cli README).

Sources: Mesh-LLM/capsules @ efcada74026a2e18b9e62d77432888a31bbe9de4 and tag v0.1.4 @ b4e24453f572b2d9fe8f2cefbd0f35a5eb54b0ad (paths under crates/evidence-plugin/src/ unless shown); action-state-group/capsule-cli @ 551b5c616641d15089eb0aac747f5c90aa29bf69

6Specs