Agent Action Capsule · explain
10 / 11

Mesh-LLM capsules plugin

The open-source capsules plugin for Mesh-LLM (release v0.1.4) seals a signed record of every exchange a node serves or asks for, lets two nodes swap those records, and, for a pair of requests a client marked as twins, can ask a third node to referee answers that differ.

Records, record push and a refereed twin pair across Mesh-LLM nodes Four Mesh-LLM nodes, each running the capsules plugin: R the requesting node on the left, A and B the two serving nodes in the middle, C the referee on the right, and a client at far left. Stage 1: each node shows its plugin's own key; dashed lines show mesh-llm's host gossip of plugin keys bound by node keys, labelled as not used by capsules v0.1.4 for its own key, which does not announce it; a note says peers' keys come from CAPSULES_PEER_KEYS, or from the host's list when a key is announced. Stage 2: the client sends two requests to R, both with the header x-mesh-twin-bracket: bracket-1. Stage 3: R forwards one request to A and one to B with the header stripped, so neither is told it is a twin. Stage 4: A and B each seal a record holding the request and response digests, the model and the key id, signed with COSE_Sign1 by the plugin key; R seals its own requester halves, carrying twin_bracket_id. The key file keys/node-key.pub.pem is the plugin's key, not mesh-llm's node key. Stage 5: A and B push their records to R over record-push/1; R checks that the sender's key, from the host's list or CAPSULES_PEER_KEYS, is the record's key_id, then the signature, the claims and the inclusion proof, then holds the record or sends a signed refusal. Stage 6: the client supplies the request and both answers to R, which keeps them only if their digests match its own records; the twins answered the same request at temperature 0 on the same model and weights with different answers, so R asks referee C to re-answer the request and then sends it both halves. Stage 7: C returns a signed verdict from its re-answer, here contradicted naming node B; the referee signs only contradicted, inconclusive or not_comparable; a pair whose answers agree is never sent to a referee, and a pair without a verdict reads Not adjudicated with its reason. The final picture shows all four nodes, their records, the record pushes, the referee call and the verdict. Client Node R requesting node capsules plugin Node A capsules plugin Node B capsules plugin Node C eligible referee capsules plugin own plugin key own plugin key own plugin key own plugin key peers' keys in v0.1.4: CAPSULES_PEER_KEYS (own key in host gossip: not in v0.1.4) host gossip: PeerAnnouncement.plugin_keys mesh-llm binds and gossips; capsules' own key: not in v0.1.4 x-mesh-twin-bracket: bracket-1 same value on both requests serves; header stripped serves; header stripped A's record (served half) request · response digests model · chain · key_id COSE_Sign1 by A's plugin key B's record (served half) request · response digests model · chain · key_id COSE_Sign1 by B's plugin key R's records (requester) twin_bracket_id · digests signed by R's plugin key record-push/1 record-push/1 R checks: key (host list or CAPSULES_PEER_KEYS) = key_id, signature, claims, proof → held, or a signed refusal re-answer request, then both halves (x-mesh-target) client supplies the 3 texts; kept if digests match signed verdict: contradicted:<B> else: “Not adjudicated” + reason

1What it is

capsules is a Mesh-LLM plugin that keeps, on each node's own disk, a signed and hash-chained record of every exchange that node serves or asks for, in the Agent Action Capsule format.

When the operators turn it on, the two sides of an exchange send each other their own sealed records, and each side checks the other's against the peer's key: one its host announced, bound to its node key, or one the operator configured.

When a client marks two requests as one pair and the two answers differ, a node can ask a third node to re-answer the request and sign a verdict on the pair.

2Design choices

Chosen: a pair exists only when a client sends the same x-mesh-twin-bracket value on two requests.
Rejected: the host sending a second request, or pairs guessed from timing or content: the host never does either. (mesh-llm docs/plugins/openai-exchange-lifecycle.md, “Client-marked pairs”; capsules docs/TWIN-REFEREE-SELECTION.md §4)
Chosen: the header is stripped before a request is forwarded to the serving node.
Rejected: passing it on, which would let a serving node know its answer is half of a pair. (mesh-llm PR 2300; openai-exchange-lifecycle.md)
Chosen: the host signs a binding of the plugin's key with its node key and carries it in gossip; a peer keeps it only if the binding verifies, and never relays it.
Rejected: operators copying each peer's key by hand, which does not scale and breaks for client nodes whose id changes on every start; and the node key signing plugin data itself. (mesh-llm PR 2299)
Chosen: when the host's announced key and the operator's CAPSULES_PEER_KEYS disagree for a peer, nothing from that peer verifies.
Rejected: picking one of the two keys. (capsules crates/evidence-plugin/src/peer_keys.rs)
Chosen: referee eligibility is a set of yes/no rules; the pick is random within a tier, and the tier is sealed in the verdict.
Rejected: ordering nodes by merit, weighting, or combining anything into a number. (capsules docs/TWIN-REFEREE-SELECTION.md §2, §3, §5)
Chosen: any difference between the two answers counts, and at most one referee call is made per pair.
Rejected: a similarity threshold, and retrying another referee after one did not answer. (capsules docs/TWIN-REFEREE-SELECTION.md §1)
Chosen: by default nothing leaves the node on its own: record push, verdict delivery and the referee are each off until the operator turns them on.
Rejected: sharing on by default. Asking a referee sends it the twins' prompt, so it needs an explicit switch. (capsules docs/SHARING-POLICY.md; CHANGELOG.md 0.1.3)

3What it shows / what it does not

Shows

  • That the holder of a given plugin key signed a record of one exchange, and that the record's bytes are unchanged (its capsule_id recomputes).
  • Where a host lists a peer's plugin key (mesh-llm PR 2299): that the peer's node key vouched for that key. capsules v0.1.4 reads such keys; it does not yet announce its own.
  • With record push on at both ends: each side holds the other's signed record of the same exchange.
  • For a pushed bundle: inclusion of the record under a checkpoint the sender signed.
  • For a refereed pair: a signed ruling on one pair of answers, which twin the referee's re-answer agrees with, and the word position where they start to differ (divergence_index).

Does not show

  • Who holds a key in the real world. The node key's binding says nothing about whether the node served honestly (PR 2299).
  • That a model claim is true. model_name_digest is a hash of the model's name, not of its weights.
  • Anything about a pair no client marked: those read “Not adjudicated” and nothing is called.
  • A node's future answers. A verdict is a ruling on one pair; nothing counts against a twin without one.
  • That the referee's prompt is the twins' prompt: the verdict records it as the requester's word (referee_prompt: requester_attested).
  • In v0.1.4, the plugin announcing its own key: it reads peers' announced keys; announcing its own “follows once a mesh-llm plugin release carries the request” (INSTALL.md).
  • A live run: host-announced keys were exercised only against a test build of the host, and a referee verdict on live hosts is covered by tests only (CHANGELOG 0.1.4).

4Example

A verdict signed by a reference referee, kept as a test fixture; the plugin's test checks that its signature verifies under referee_key_id. It rules on a twin pair in which node aaaa… served half A and node bbbb… served half B.

{
  …
  "referee_key_id": "6a9a93a3af20ca17d85764e3206fd1d0417ea943f53f07157792299ba9b6f033",
  "verdict_capsule": {
    …
    "capsule_id": "1bd4fa92a5ff02c278b3c79e4e0398ede6c62212483c5a9466176bfc45408f62",
    "action_id": "adjudicate/1bb92507-473a-49b7-96a0-a6752fb42308",
    "action_type": "decide",
    …
    "provenance": "referee",
    "model_attestation": {
      "compute_attestation": {
        "epistemic_type": "adjudication",
        "adjudication": {
          "schema": "capsule-emit-mesh/adjudication/v1",
          "source": "twin_comparison",
          "capture_method": "deterministic_replay",
          "verdict": "contradicted:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
          "status": "CONTRADICTED",
          "divergence_index": 3,
          "margin": "0",
          "margin_tau": "0.9",
          "prefix_digest": "0ec7db901e57f96f8d195aa69bbdbd858eb13d125021a24c90972a5ce35d587b",
          "twin_owner_distinct": true,
          "weights_digest": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
          "half_a_capsule_id": "7dc9427b6df5ff222617fb721f25d3c54aebe32682329bb7075902336437fb06",
          "half_b_capsule_id": "b80db10999779aa17c8fcf7006a57aeaecbc81f5e9de3ecea33e9365c2bfd4d9",
          "referee_capsule_id": "5d6b949bc8a8f20cd0dc4ba25642b12d4906eeccf5763adbe27f00841356cd44",
          …
          "referee_node_id": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
          "half_a_node_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "half_b_node_id": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
          "twins_request_digest": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
          "referee_prompt": "requester_attested",
          "referee_answer_digest": "0242bc9526adbb014565b8175ff134e2ba8731f4a13cab2d1b4d8aff8727586e",
          "twin_bracket_id": "bracket-1"
        }
      }
    },
    "assurance": {
      "attestation_mode": "self_attested",
      …
    },
    …
    "chain": {
      "parent_capsule_id": "7dc9427b6df5ff222617fb721f25d3c54aebe32682329bb7075902336437fb06",
      "relation": "adjudicates"
    },
    …
    "key_id": "6a9a93a3af20ca17d85764e3206fd1d0417ea943f53f07157792299ba9b6f033"
  }
}
FieldTypeMeaning
referee_key_idhexThe referee's Ed25519 public key; the fixture's signature verifies under it.
capsule_idhexSHA-256 of the record's canonical (JCS) bytes; changing any committed field changes it.
action_typestringdecide: this record is a ruling, not a served exchange.
provenancestringreferee: the node that wrote it was asked as referee.
source / capture_methodstringA comparison of twins, decided by the referee re-answering the request.
verdictstringA referee signs contradicted: + the node id its re-answer did not agree with, inconclusive, or not_comparable. (corroborated applies only when the answers agree, and then no referee is called.)
statusstringPaired with the verdict: SATISFIED, CONTRADICTED or UNKNOWN.
divergence_indexintegerThe word position at which the two answers start to differ.
prefix_digesthexDigest of the words the two answers share before they differ.
margin / margin_taustringRead and shown by the v0.1.4 web UI; written by no v0.1.4 Rust code. unverified what they mean.
twin_owner_distinctboolThe twins have different owners; a same-owner pair gets no verdict.
weights_digesthexThe weights both halves name; set only when both name the same.
half_a/b_capsule_idhexThe two twins' served records (A's and B's), pushed to the requesting node and held there.
referee_capsule_idhexThe referee's own record of its re-answer.
half_a/b_node_id, referee_node_idhexThe node ids of the two twins and the referee.
twins_request_digesthexDigest of the request both twins answered.
referee_promptstringrequester_attested: the prompt is the requester's word, not checked by the referee. The requesting node checks a supplied request against both records' request_digest.
referee_answer_digesthexDigest of the referee's own answer.
twin_bracket_idstringThe value the client sent in x-mesh-twin-bracket.
attestation_modestringself_attested: the signer's own key, with no outside vouching.
chainobjectLinks the verdict to half A's record with the relation adjudicates.
key_idhexThe plugin's own key that signed this record (here referee_key_id); on disk keys/node-key.pub.pem, not mesh-llm's node key.

This fixture's spec_version (trimmed above) names an earlier revision of the capsule Internet-Draft; the v0.1.4 producer seals draft-mih-scitt-agent-action-capsule-05.

Source: Mesh-LLM/capsules:crates/evidence-plugin/tests/fixtures/referee-verdict.json @ b4e24453f572b2d9fe8f2cefbd0f35a5eb54b0ad (tag v0.1.4)

5Specs