# The external witness: a second receipt from a log nobody here operates Status: **implemented on the three keepers behind a flag since 2026-09-13** (seller `WITNESS_EXTERNAL_URL`, buyer `AGENT_BUYER_EXTERNAL_WITNESS_URL`, authority `AGENT_AUTHORITY_EXTERNAL_WITNESS_URL`), verified by agent-verifier the same day. The emulator the verifier operates (`checkpoint-receipt.schema.json`) keeps sealing; every checkpoint carries two receipts when the flag is on. Whether the flag is on for a given demonstration is the operator's decision, not the contract's. ## Why a second receipt A checkpoint (`checkpoint.schema.json`) is the chain's head at a height, signed by the instance. Sealed at the emulator, it defeats a rewrite of the chain as long as the reader trusts the party that runs the emulator, which is the verifier, the party that also scores the run. The limitation is declared on every scorecard. A receipt from a log that none of the four parties operates removes it: the keeper has committed the head to a tree it cannot edit, and anyone can check the commitment against the log's own signed tree head. ## The log `https://witness.agentactioncapsule.org`, a public SCITT transparency service operated by a third party (Apache-2.0, `github.com/action-state-group/capsule-anchor`). No authentication. What this contract uses of it: | Call | Used for | |---|---| | `POST /register {"capsule_id": "<64 hex>"}` | The keeper registers the checkpoint's **head** as a record. Answer `{receipt_b64, entry_hash, entry_hash_scheme, leaf_index, tree_size}` | | `GET /.well-known/did.json` | The log's Ed25519 key (`publicKeyJwk`, OKP, `x` base64url); `key_id` = first 16 hex of SHA-256 over the raw key | | `GET /anchor/sth` | The current signed tree head `{tree_size, root_hash, timestamp, signature}` | | `GET /anchor/consistency-proof?old_size&new_size` | RFC 6962 consistency proof between two tree sizes | | `GET /v1/inclusion/{capsule_id}` | Whether a head is in the log at all (404 when it is not), with the proof the log recorded at registration (under the tree of that moment, not the current one, as observed on 2026-09-13) | Limits the log states: 300 registrations a minute across all callers (429 beyond), no retention promise, no service level. The log's own checkpoint route (`POST /checkpoints`) takes its checkpoint format, not ours; nothing here converts one into the other. The head is registered as a bare digest and that is all the log learns. ## What the keeper stores Per checkpoint, the object `external` of `checkpoints-document.schema.json`: the log, the time of the registration, and the answer **as returned**. The keeper checks one thing before storing a receipt, that `entry_hash` equals the SHA-256 over the 32 bytes of the head; it does not decode the COSE receipt (the seller runs with no CBOR code, and the rule is the same for the three keepers). A registration that fails leaves `receipt_b64: null` and `error: ": "`, and never fails the transaction that triggered the seal, the emulator's rule. A checkpoint left without a receipt from a log that is configured is offered again at the next seal of the same head (the timer), on the same record. ## The receipt, as observed on 2026-09-13 `receipt_b64` is standard base64 of a COSE_Sign1 (CBOR tag 18): ``` [protected_bstr, unprotected, nil, signature] protected = {1: -8, 395: 1, 15: {6: }} alg EdDSA; verifiable data structure RFC9162_SHA256; CWT iat unprotected = {396: {-1: [cbor([tree_size, leaf_index, [<32-byte sibling>...]])]}} signature = Ed25519 over cbor(["Signature1", protected_bstr, b"", ]) ``` The payload is detached: it is the root of the tree of `tree_size` leaves, which the verifier rebuilds. `vectors/external-receipt.json` is one registration as the log answered it, with the values a verifier recomputes. ## What the verifier checks 1. `entry_hash` = SHA-256(head bytes); `leaf_hash` = SHA-256(0x00 || entry_hash) (RFC 6962). 2. From `leaf_hash` at `leaf_index` in a tree of `tree_size`, the root along the audit path (RFC 6962 §2.1.1). The path must be consumed exactly. 3. The COSE signature under the log's key from `did.json`, EdDSA, over the Sig_structure with the rebuilt root as the detached payload; `key_id` derived from the key must match what the log publishes. 4. The current tree head, its signature verified under the same key over the compact, key-sorted JSON of `{tree_size, root_hash, timestamp}` (the timestamp signed as `+00:00`, which is not the `Z` the endpoint prints), and the consistency proof from the receipt's tree to it (RFC 6962 §2.1.2). The head is read at the start of a run, and a receipt over a larger tree is a registration made during the run: the head is read again (at most five times a run) and held from then on when the log's consistency proof says it extends the first. Only a head signed after the receipt over a smaller tree, or a new head that does not extend the first, is the log contradicting itself (since 2026-09-25, when a checkpoint sealed during a run was named an equivocation). This is the only online step; when the log cannot be asked the receipt is still verified offline and the scorecard says the inclusion was not confirmed this run. 5. For the latest head that carries a second receipt, `GET /v1/inclusion/{head}`: the log resolves the head by name, on its own word rather than the keeper's stored receipt, and the proof it answers verifies to the root it names (`in_log_now` on the scorecard). Any of 1 to 3 failing is a problem named by code on the checkpoints check (`receipt_invalid`, `signature_invalid`, `inclusion_invalid`); 4 failing is `equivocation` on the log's side, said as such. ## What it proves, and what it does not Proves: at the log's signed time, the keeper had published this head, and the log has not dropped it as long as its tree stays consistent. With the checkpoint's own signature, that the instance stood behind that head at that height. Two receipts for one height with two heads (R-28) are two commitments the keeper cannot deny. Does not prove: that the head is the head of *this* chain (the checkpoints check does that against the chain fetched); anything about the entries under it (the ledgers do that); retention (if the log disappears, the receipt remains verifiable offline by its signature and the inclusion does not).