mesh: a cut journal is caught by the receipts the steward handed out, and the receipt chain survives a restart #306

Merged
dcharlot merged 1 commit from feat/mesh-journal-witness into main 2026-10-18 00:42:33 -04:00
Owner

Stacked on #301 (itself on #294 and #300). Merge those first. This branch is #301's exact head plus one commit.

What was left open after #301

The journal re-established a grant's spend on restart, but nothing could tell whether records had been cut from its end. Removing a serve's records gave the grant its spend back and unspent its presentation, so a captured presentation could be replayed.

Separately, a restarted steward began a new receipt chain at sequence 0, so a restart looked the same as a log that had lost its history.

The fix

  • The journal is one chain: each record is {seq, prev, entry}, where prev is the BLAKE3 hash of the previous record's encoding. A record removed from the middle, edited or moved fails the replay. Only a cut from the end leaves a whole chain.
  • Every receipt is recorded before it is handed out: granted or not, each receipt gets a Sealed record holding its sequence and envelope id. ReceiptLog::seal_next / advance split sealing from advancing, so a receipt that cannot be recorded is not handed out and the chain does not pass it.
  • The receipt chain continues across a restart, from the last Sealed record (ReceiptLog::resume), instead of starting over at 0.
  • Mesh::witness(receipt, envelope): anyone holding a receipt this steward sealed can show it. If the journal does not hold it, records were cut after that receipt left, and the spend the steward replayed is less than it signed for. The steward returns JournalCut and refuses every grant from then on. Ungranted serves are unaffected. A witness that is not the steward's own unaltered receipt is WitnessInvalid.
  • SPEC §2.2, §6.3, §7 and §8 describe it, including what remains. A cut that removes only an in-flight serve, which produced no receipt, leaves no evidence outside the steward. A caller SHOULD show its latest receipt after the steward restarts.

Checked

On the build host:

  • mesh-rs: 77 tests pass, four of them new. They cover a receipt proving a cut, chain continuity across a restart, a record removed from the middle or edited, and a foreign or altered witness. The existing journal tests now count seal records.
  • Vectors: every mesh/1 vector is byte-identical.
  • Lockfiles and clippy: every Cargo.lock in the repo is current (--locked), and clippy is clean on mesh-core and mesh-receipt.
  • Mutations: each fails tests:
    • a witness that always passes
    • a detected cut that is ignored
    • seals not recorded
    • no chain check
    • a chain not resumed
  • Repository checks: spec references, tests-in-CI, vanity metrics and orphan sources all pass locally.

🤖 Generated with Claude Code

> **Stacked on #301** (itself on #294 and #300). Merge those first. This branch is #301's exact head plus one commit. ## What was left open after #301 The journal re-established a grant's spend on restart, but nothing could tell whether records had been **cut from its end**. Removing a serve's records gave the grant its spend back and unspent its presentation, so a captured presentation could be replayed. Separately, a restarted steward began a **new receipt chain at sequence 0**, so a restart looked the same as a log that had lost its history. ## The fix - **The journal is one chain:** each record is `{seq, prev, entry}`, where `prev` is the BLAKE3 hash of the previous record's encoding. A record removed from the middle, edited or moved fails the replay. Only a cut from the end leaves a whole chain. - **Every receipt is recorded before it is handed out:** granted or not, each receipt gets a `Sealed` record holding its sequence and envelope id. `ReceiptLog::seal_next` / `advance` split sealing from advancing, so a receipt that cannot be recorded is not handed out and the chain does not pass it. - **The receipt chain continues across a restart,** from the last `Sealed` record (`ReceiptLog::resume`), instead of starting over at 0. - **`Mesh::witness(receipt, envelope)`:** anyone holding a receipt this steward sealed can show it. If the journal does not hold it, records were cut after that receipt left, and the spend the steward replayed is less than it signed for. The steward returns `JournalCut` and refuses every grant from then on. Ungranted serves are unaffected. A witness that is not the steward's own unaltered receipt is `WitnessInvalid`. - **SPEC** §2.2, §6.3, §7 and §8 describe it, including what remains. A cut that removes only an in-flight serve, which produced no receipt, leaves no evidence outside the steward. A caller SHOULD show its latest receipt after the steward restarts. ## Checked On the build host: - **mesh-rs:** 77 tests pass, four of them new. They cover a receipt proving a cut, chain continuity across a restart, a record removed from the middle or edited, and a foreign or altered witness. The existing journal tests now count seal records. - **Vectors:** every `mesh/1` vector is byte-identical. - **Lockfiles and clippy:** every `Cargo.lock` in the repo is current (`--locked`), and clippy is clean on mesh-core and mesh-receipt. - **Mutations:** each fails tests: - a witness that always passes - a detected cut that is ignored - seals not recorded - no chain check - a chain not resumed - **Repository checks:** spec references, tests-in-CI, vanity metrics and orphan sources all pass locally. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
mesh: a grant is decided by JCP's runtime, so expiry, caveats, the model and spend bind
Some checks failed
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Failing after 1m55s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 13s
standards workspaces build + test / macOS energy backends (pull_request) Has been skipped
standards workspaces build + test / joulehook-rs (joulehook/joulehook-rs, joulehook-rs, linux) (pull_request) Has been cancelled
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) Has been cancelled
afde4f18ec
Mesh honoured a JCP grant by checking three things: the issuer signature,
that the capability permits `infer` on the request, and that the budget
covered the request's ceiling. Nothing else was checked:
- An expired grant was served.
- No caveat was evaluated, so `NotAfter`, `NotBefore`, `PayloadIs`,
  `MaxCostUj` and `ModelIn` were ignored.
- Nothing was ever debited, so one grant paid for any number of serves.
mesh-core had no unit test of the grant path.

The steward now holds a jcp-runtime `Runtime` and decides every grant in
JCP's two-phase metered path:
- **Route.** With a grant, a backend whose model the grant's `ModelIn`
  does not permit is inadmissible, and so is one that names no model. The
  route is the cheapest permitted backend, and the candidates record why
  the others were refused (`Router::route_where`).
- **Authorise,** before any work. The frame names `infer` on the request,
  the prompt hash as payload, and the chosen backend's weights as `model`.
  Signature, expiry, revocation and every cost-independent caveat apply,
  and what the grant has left must cover the request's ceiling.
- **Debit,** after metering. The metered joules are charged whatever
  happens next. A per-call cost caveat binds here, and a breach is sealed on
  the receipt as `grant_breach`.
- **Model check.** An engine that runs other weights than the ones it named
  before serving is refused a receipt.

Engines name their weights before serving through a new
`ServingEngine::weights_digest()`, which defaults to `None`.

The refusals that existed keep their errors (`GrantInvalid`,
`Unauthorized`, `BudgetTooLow`). `BudgetTooLow` now reports what the grant
has left. SPEC §2, §6.1, §7 and §8 and the README describe the new order.
§8 states that Mesh does not authenticate the caller, honouring a grant
as a bearer token of its subject, and that spend is per steward.

Checked on the build host:
- mesh-rs: 65 tests pass, six of them new, and every regenerated `mesh/1`
  vector is byte-identical.
- The gguf and openai server features build.
- clippy is clean on the changed crates.
- Mutations: no debit, a router that ignores the filter, ignoring other
  denials, and skipping the model check each fail tests.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
jcp: a chained decision enforces caveats, and a chain cannot drop one
All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 9s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 1m21s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 5m57s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 1m21s
standards workspaces build + test / jcp-rs (jcp-conformance, jcp/jcp-rs, -p jcp-energy --features nvml, jcp-rs, linux) (pull_request) Successful in 1m34s
standards workspaces build + test / joule-ui-rs (joule-ui/joule-ui-rs, joule-ui-rs, linux) (pull_request) Successful in 1m9s
standards workspaces build + test / joulehook-rs (joulehook/joulehook-rs, joulehook-rs, linux) (pull_request) Successful in 1m11s
standards workspaces build + test / sandbox-rs (sandbox-conformance, sandbox/sandbox-rs, sandbox-rs, linux) (pull_request) Successful in 2m36s
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 1m9s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 2m1s
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 3m13s
f0952309c1
`decide_chained` evaluated no caveat at all: not the leaf's, not any
ancestor's. It is the decision path behind jcp-wire's endpoint (§11),
jcp-channel, jouleclaw-jcp and joule-code-jcp. Those always present a
chain, even a one-link chain for an undelegated grant. So `PayloadIs`,
`NotBefore`, `NotAfter`, `CallerIs`, `ActionIn`, `MaxCostUj` and
`ModelIn` were enforced only on the single-grant `decide`/`authorize`
path, which the conformance suite exercises.

Separately, `verify_chain` checked capability, budget, expiry and depth
at each hop, but not that a child carries its parent's caveats or no
wider money ceiling. `attenuate` refuses both when it mints. A chain
minted by hand verified anyway and conveyed the authority the caveat
withheld.

- `verify_chain` refuses a hop that drops a parent caveat
  (`caveats_narrow`) or widens or drops its money ceiling
  (`funds_narrows`). Both are denied `attenuation-violation`.
- The chained decision's checks move into `check_chained`, which now
  evaluates every link's caveats. Cost-dependent ones are evaluated when
  the cost is known.
- `authorize_chained` / `charge_chained` add the two-phase metered path
  for a chain. Authorize debits nothing and reports the least any link
  has left. Charge debits every link, so siblings share their parent as
  in `decide_chained`, and a per-call breach is reported with the joules
  still debited.
- SPEC §4.2 states both rules and the chained metered path. C7's
  conformance check covers an inherited caveat and a dropped one.

Checked on the build host:
- jcp-rs: 501 tests pass, and the conformance suite passes all checks.
- Every vector gen-vectors writes is byte-identical.
- jouleclaw-jcp, joule-code-jcp and sandbox-jcp pass their tests.
- mesh, joule-ui, joulehook and jouledesk compile.
- Mutations each fail tests: chains ignoring caveats, `verify_chain`
  allowing a dropped caveat, and a charge debiting only the leaf.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
joulehook-rs: lock mesh-core's new dependency on jcp-runtime
All checks were successful
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 57s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 6s
standards workspaces build + test / macOS energy backends (pull_request) Has been skipped
standards workspaces build + test / joulehook-rs (joulehook/joulehook-rs, joulehook-rs, linux) (pull_request) Successful in 1m24s
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 2m49s
3e2e3f85f3
Co-Authored-By: Claude Opus 5.5 <[email protected]>
dcharlot force-pushed feat/mesh-journal-witness from 9eb217be9f
Some checks failed
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 20s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 2m11s
standards workspaces build + test / jcp-rs (jcp-conformance, jcp/jcp-rs, -p jcp-energy --features nvml, jcp-rs, linux) (pull_request) Successful in 1m37s
standards workspaces build + test / joule-ui-rs (joule-ui/joule-ui-rs, joule-ui-rs, linux) (pull_request) Successful in 1m12s
standards workspaces build + test / joulehook-rs (joulehook/joulehook-rs, joulehook-rs, linux) (pull_request) Successful in 1m14s
standards workspaces build + test / sandbox-rs (sandbox-conformance, sandbox/sandbox-rs, sandbox-rs, linux) (pull_request) Successful in 2m28s
standards workspaces build + test / macOS energy backends (pull_request) Failing after 7m35s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Failing after 3s
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 2m37s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 4m2s
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) Has been cancelled
to 29ee437557
All checks were successful
wai-quantum tensor-engine member / does this change touch it (pull_request) Successful in 4s
wai-quantum tensor-engine member / member · tensor engine, the kernel toolchain's simulator (linux) (pull_request) Has been skipped
wai-quantum tensor-engine member / member · tensor engine absent (linux-arm64) (pull_request) Has been skipped
wai-quantum portable tensor program / does this change touch it (pull_request) Successful in 4s
wai-quantum portable tensor program / tensor program · CPU, mutants, GPU builds (pull_request) Has been skipped
wai-quantum portable tensor program / tensor program · GPUs (jetson-hub) (pull_request) Has been skipped
wai-quantum portable tensor program / tensor program · integrated GPU (framework-128gb) (pull_request) Has been skipped
wai-quantum portable tensor program / tensor program · Metal and the arm64 CPU (macos) (pull_request) Has been skipped
wai-quantum CPU vector units / does this change touch it (pull_request) Successful in 4s
wai-quantum CPU vector units / member · vector unit, rvv 1024-bit (riscv64, emulated) (pull_request) Has been skipped
wai-quantum CPU vector units / member · vector unit, sve 128-bit (aarch64, emulated) (pull_request) Has been skipped
wai-quantum CPU vector units / member · vector unit, rvv 128-bit (riscv64, emulated) (pull_request) Has been skipped
wai-quantum CPU vector units / member · vector unit, sve 2048-bit (aarch64, emulated) (pull_request) Has been skipped
wai-quantum CPU vector units / member · vector unit, avx2 (x86_64 silicon) (pull_request) Has been skipped
wai-quantum CPU vector units / member · vector unit, sve 512-bit (aarch64, emulated) (pull_request) Has been skipped
wai-quantum CPU vector units / member · vector units, avx2 and avx512 (framework-128gb silicon) (pull_request) Has been skipped
wai-quantum CPU vector units / member · vector unit absent (macos) (pull_request) Has been skipped
wai-quantum WebGPU member / does this change touch it (pull_request) Successful in 4s
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness · MSRV (pull_request) Has been skipped
wai-quantum WebGPU member / member · software Vulkan (linux) (pull_request) Has been skipped
wai-quantum WebGPU member / member · Metal (macos) (pull_request) Has been skipped
wai-quantum WebGPU member / member · discrete GPU, Vulkan (jetson-hub) (pull_request) Has been skipped
wai-quantum WebGPU member / member · integrated GPU, Vulkan (framework-128gb) (pull_request) Has been skipped
wai-quantum WebGPU member / member · browser WebGPU (macos) (pull_request) Has been skipped
WAI mbt2018_mean integer decode (regenerated vectors) / does this change touch it (pull_request) Successful in 4s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Has been skipped
wai-web wasm32 / does this change touch it (pull_request) Successful in 4s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Has been skipped
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 1m1s
standards workspaces build + test / joulehook-rs (joulehook/joulehook-rs, joulehook-rs, linux) (pull_request) Successful in 1m14s
2026-10-17 17:14:50 -04:00
Compare
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!306
No description provided.