mesh/1 + AR-1: receipts commit to the output; deterministic serves replay #378

Merged
dcharlot merged 1 commit from feat/receipts-bind-the-output into main 2026-10-21 04:50:48 -04:00
Owner

Sweep 2026-10-19 item #6. A mesh receipt bound what was asked (request_id) and what it cost, and nothing about what came back, so no one could later show which answer a receipt was for. AR-1 had the same gap beside input_hash.

mesh/1 (SPEC §6.4)

  • output_commitment. A salted BLAKE3 commitment over:

    • the request id;
    • the digest of the prompt payload the engine was handed;
    • the output tokens.

    It is a commitment rather than a hash, because a short output (a one-token answer) can be reversed from its hash.

    • The opening is derived per request from a key drawn from the steward's signing key, so a restarted steward can still disclose it.
    • It is absent for a typed decision, whose answer the engine interface does not carry.
    • It is omitted when absent, so older receipts keep their bytes.
  • The payload is bound. prompt_hash addresses the prompt's bytes, while an engine is handed token ids, so request_id alone cannot confirm what a replay runs on. Without this binding, an honest disclosed output replayed on the wrong tokens would read as the steward's divergence.

  • ServingEngine::deterministic. The default is false; the reference engine returns true.

  • mesh_core::replay. It re-executes a sealed serve and returns one of three verdicts:

    • Verified: the re-run opens the commitment.
    • Diverged { first_difference }: returned only when a disclosed output opens the commitment on the same payload and opening. That confirms all three, so the mismatch is the serve's.
    • NotReplayable(why): the engine samples; the backend, recipe or weights are not the ones the receipt names; the request is not the receipt's; the receipt commits to no output; or no disclosed output confirms the inputs, so nothing attributes the mismatch.
  • Conformance pack regenerated. The reference receipt gains the field, and the envelope grows from 663 to 747 bytes.

AR-1

  • output_hash. An optional field after input_hash, sealed only when bound (§2.3).
    • The spec recommends a commitment over a plain hash, and names mesh's output_commitment as one.
    • The published AR-1 vectors regenerate byte-identical.
  • ar-verify lint. len() % 2 == 0 becomes is_multiple_of(2). This clippy error predates this change; fixing it makes the crate clippy-clean.

Verification (build host)

  • mesh-rs: 94 tests plus 19 behind features. Clippy (-D warnings) and rustdoc are clean on the touched crates.
  • jcp-rs: 565 tests and the jcp-conformance binary.
  • Mutation testing: 20 mutants, all killed. They cover:
    • each input of the commitment and of the payload digest, and the domains;
    • every replay gate;
    • the attribution rule;
    • the decision → no-commitment branch;
    • the first-difference position;
    • AR-1 emission.
  • Repo checks: tests-in-ci, spec-refs, orphan sources, vanity metrics, comparative prose.

Left for later

From the same sweep item, as follow-ups:

  • a disclosure bundle beside cra.rs;
  • a signed record of a weights-digest change.

🤖 Generated with Claude Code

Sweep 2026-10-19 item #6. A mesh receipt bound what was asked (`request_id`) and what it cost, and nothing about what came back, so no one could later show which answer a receipt was for. AR-1 had the same gap beside `input_hash`. ## mesh/1 (SPEC §6.4) - **`output_commitment`.** A salted BLAKE3 commitment over: - the request id; - the digest of the prompt payload the engine was handed; - the output tokens. It is a commitment rather than a hash, because a short output (a one-token answer) can be reversed from its hash. - The opening is derived per request from a key drawn from the steward's signing key, so a restarted steward can still disclose it. - It is absent for a typed decision, whose answer the engine interface does not carry. - It is omitted when absent, so older receipts keep their bytes. - **The payload is bound.** `prompt_hash` addresses the prompt's bytes, while an engine is handed token ids, so `request_id` alone cannot confirm what a replay runs on. Without this binding, an honest disclosed output replayed on the wrong tokens would read as the steward's divergence. - **`ServingEngine::deterministic`.** The default is `false`; the reference engine returns `true`. - **`mesh_core::replay`.** It re-executes a sealed serve and returns one of three verdicts: - `Verified`: the re-run opens the commitment. - `Diverged { first_difference }`: returned only when a disclosed output opens the commitment on the same payload and opening. That confirms all three, so the mismatch is the serve's. - `NotReplayable(why)`: the engine samples; the backend, recipe or weights are not the ones the receipt names; the request is not the receipt's; the receipt commits to no output; or no disclosed output confirms the inputs, so nothing attributes the mismatch. - **Conformance pack regenerated.** The reference receipt gains the field, and the envelope grows from 663 to 747 bytes. ## AR-1 - **`output_hash`.** An optional field after `input_hash`, sealed only when bound (§2.3). - The spec recommends a commitment over a plain hash, and names mesh's `output_commitment` as one. - The published AR-1 vectors regenerate byte-identical. - **`ar-verify` lint.** `len() % 2 == 0` becomes `is_multiple_of(2)`. This clippy error predates this change; fixing it makes the crate clippy-clean. ## Verification (build host) - **mesh-rs:** 94 tests plus 19 behind features. Clippy (`-D warnings`) and rustdoc are clean on the touched crates. - **jcp-rs:** 565 tests and the `jcp-conformance` binary. - **Mutation testing:** 20 mutants, all killed. They cover: - each input of the commitment and of the payload digest, and the domains; - every replay gate; - the attribution rule; - the decision → no-commitment branch; - the first-difference position; - AR-1 emission. - **Repo checks:** tests-in-ci, spec-refs, orphan sources, vanity metrics, comparative prose. ## Left for later From the same sweep item, as follow-ups: - a disclosure bundle beside `cra.rs`; - a signed record of a weights-digest change. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
mesh/1 + AR-1: receipts commit to the output; deterministic serves replay
All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 6s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 44s
standards workspaces build + test / joule-ui-rs (joule-ui/joule-ui-rs, joule-ui-rs, linux) (pull_request) Successful in 1m22s
standards workspaces build + test / jcp-rs (jcp-conformance, jcp/jcp-rs, -p jcp-energy --features nvml, jcp-rs, linux) (pull_request) Successful in 1m41s
standards workspaces build + test / mesh-rs (mesh/mesh-rs, -p mesh-engine-gguf -p mesh-openai --features mesh-engine-gguf/gguf,mesh-openai/server, mesh-rs, linux-arm64) (pull_request) Successful in 1m23s
standards workspaces build + test / joulehook-rs (joulehook/joulehook-rs, joulehook-rs, linux) (pull_request) Successful in 1m11s
standards workspaces build + test / jouleclaw-rs (-nodebug, jouleclaw/jouleclaw-rs, -p jouleclaw-energy --features rapl,nvml, jouleclaw-rs, linux-arm64, -C target-feature=+fp16) (pull_request) Successful in 3m7s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 3m54s
standards workspaces build + test / sandbox-rs (sandbox-conformance, sandbox/sandbox-rs, sandbox-rs, linux) (pull_request) Successful in 2m16s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 7m37s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 2m42s
62a81b3472
A mesh receipt bound what was asked (request_id) and what it cost, and
nothing about what came back, so no one could later show which answer a
receipt was for. AR-1 had the same gap beside input_hash.

mesh/1 (SPEC §6.4):
- output_commitment: a salted BLAKE3 commitment over the request id, the
  digest of the prompt payload the engine was handed, and the output tokens.
  It is a commitment, not a hash, because a short output is reversible from
  its hash. The opening is derived per request from a key drawn from the
  steward's signing key, so a restarted steward can still disclose it.
  The field is absent for a typed decision, whose answer the engine
  interface does not carry, and omitted when absent, so older receipts keep
  their bytes.
- The payload is bound because prompt_hash addresses the prompt's bytes
  while an engine is handed token ids, so request_id alone cannot confirm
  what a replay runs on.
- ServingEngine::deterministic (default false; the reference engine true).
- mesh_core::replay re-executes a sealed serve. Its verdicts:
  - Verified: the re-run opens the commitment.
  - Diverged { first_difference }: only when a disclosed output opens the
    commitment on the same payload and opening, so the mismatch is the
    serve's and not the replayer's input.
  - NotReplayable(why): the engine samples; another backend, recipe or
    weights; another request; no commitment; or a mismatch nothing
    attributes.
- The conformance pack is regenerated (the reference receipt gains the
  field; the envelope grows from 663 to 747 bytes).

AR-1: output_hash, an optional field after input_hash, sealed only when
bound (§2.3). It recommends a commitment over a plain hash and names
mesh's output_commitment as one. The published AR-1 vectors regenerate
byte-identical.

Also: ar-verify's `len() % 2 == 0` becomes `is_multiple_of(2)`, a clippy
error that predates this change, so the crate is clippy-clean.

Tests:
- mesh: the commitment binds request, opening, payload and tokens; a
  generation commits and a decision does not; deterministic replay
  verifies and locates a drift; replay refuses what it cannot settle,
  including a wrong payload with the honest output disclosed; a restarted
  steward reopens.
- AR-1: output_hash sealed when bound and absent otherwise.
- Mutation testing: 20 mutants, all killed (13, then 7 more after
  binding the payload; one of the 13 targets code since replaced).

Verified on the build host:
- mesh-rs: 94 tests plus 19 behind features; clippy and rustdoc clean.
- jcp-rs: 565 tests and the conformance binary; jcp-action-receipt
  clippy-clean.
- Repo checks pass.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!378
No description provided.