jcp: hand the caller its receipt, in MCP's namespaced _meta slot #69

Merged
dcharlot merged 1 commit from jcp/mcp-meta-receipt into main 2026-09-03 15:12:27 -04:00
Owner

Ranked #1 by the 2026-08-29 sweep.

The gateway authorized every tools/call, metered it on real hardware, debited the grant, and sealed allow and deny into a JCR-1 chain — and the client never saw any of it. The receipt lived in gateway.receipts(): the enforcing party keeping an artifact about itself, which the subject of the decision could not present, dispute, or forward. That is most of what a receipt is for.

MCP's 2026-07-28 revision supplies the place to put it. _meta is now a first-class namespaced extension point with a 12-month deprecation policy behind it — so a policy object carried there has a defined home rather than an invented one, and the home stops moving.

What changes

  • An allowed call returns its sealed receipt in result._meta under science.transaction/jcp, beside the upstream's own untouched payload, with the measured joules and remaining budget.
  • A denial returns its sealed refusal too, in error.data._meta — JSON-RPC reserves data for exactly this. A refusal the caller cannot see is an enforcement nobody can audit, and a receipt's value does not depend on the verdict going the caller's way.
  • The witness migrates from the hand-rolled flat params._jcp_witness into the namespaced slot, and the flat key keeps working. A silent behaviour change at an enforcement point is worse than a legacy key.

The returned bytes are the same sealed envelope the gateway logged, not a summary — asserted directly rather than described. Attaching is defensive: a response that is not a JSON-RPC result object comes back untouched, because this must never mangle an upstream payload it doesn't understand.

Base64 is hand-rolled (no new dependency for one encode) and pinned against the RFC 4648 §10 vectors — a receipt that doesn't decode is not a receipt.

From the same sweep

Its repo-check phase refuted two items I had queued as gaps: the IETF agent-audit-trail draft and a hardware-rooted attestation paper are both already shipped — the draft being a strictly weaker subset, with an optional signature and no energy field at all.

Not in this change, and worth its own: MCP's new CacheableResult/ttlMs makes a cache hit into joules avoided, and nothing in the family binds a TTL'd result to an energy receipt.

Verification

245 jcp-rs tests pass (5 new) · conformance passes all classes · published vectors unchanged on disk · no new clippy warnings (the two reported are pre-existing, in flow.rs and store.rs).

Ranked **#1** by the 2026-08-29 sweep. The gateway authorized every `tools/call`, metered it on real hardware, debited the grant, and sealed allow *and* deny into a JCR-1 chain — and **the client never saw any of it**. The receipt lived in `gateway.receipts()`: the enforcing party keeping an artifact about itself, which the subject of the decision could not present, dispute, or forward. That is most of what a receipt is for. MCP's 2026-07-28 revision supplies the place to put it. `_meta` is now a first-class **namespaced** extension point with a **12-month deprecation policy** behind it — so a policy object carried there has a defined home rather than an invented one, and the home stops moving. ## What changes - **An allowed call returns its sealed receipt** in `result._meta` under `science.transaction/jcp`, beside the upstream's own untouched payload, with the measured joules and remaining budget. - **A denial returns its sealed refusal too**, in `error.data._meta` — JSON-RPC reserves `data` for exactly this. A refusal the caller cannot see is an enforcement nobody can audit, and *a receipt's value does not depend on the verdict going the caller's way*. - **The witness migrates** from the hand-rolled flat `params._jcp_witness` into the namespaced slot, and the flat key keeps working. A silent behaviour change at an enforcement point is worse than a legacy key. The returned bytes are **the same sealed envelope the gateway logged**, not a summary — asserted directly rather than described. Attaching is defensive: a response that is not a JSON-RPC result object comes back untouched, because this must never mangle an upstream payload it doesn't understand. Base64 is hand-rolled (no new dependency for one encode) and pinned against the **RFC 4648 §10 vectors** — a receipt that doesn't decode is not a receipt. ## From the same sweep Its repo-check phase **refuted two items I had queued as gaps**: the IETF agent-audit-trail draft and a hardware-rooted attestation paper are both already shipped — the draft being a strictly *weaker* subset, with an optional signature and no energy field at all. **Not in this change, and worth its own:** MCP's new `CacheableResult`/`ttlMs` makes a cache hit into *joules avoided*, and nothing in the family binds a TTL'd result to an energy receipt. ## Verification 245 jcp-rs tests pass (5 new) · conformance passes all classes · published vectors unchanged on disk · no new clippy warnings (the two reported are pre-existing, in `flow.rs` and `store.rs`).
The gateway authorized every `tools/call`, metered it on real hardware, debited
the grant and sealed allow and deny alike into a JCR-1 chain — and the client
never saw any of it. The receipt lived in `gateway.receipts()`: the enforcing
party keeping an artifact about itself, which the subject of the decision could
not present, dispute, or forward. That is most of what a receipt is for.

MCP's 2026-07-28 revision supplies the place to put it. `_meta` is now a
first-class namespaced extension point with a 12-month deprecation policy behind
it, so a policy object carried there has a defined home rather than an invented
one, and the home stops moving.

- An allowed call returns its sealed receipt in `result._meta` under
  `science.transaction/jcp`, beside the upstream's own untouched payload, with
  the measured joules and the remaining budget.
- A denial returns its sealed refusal too, in `error.data._meta` — JSON-RPC
  reserves `data` for exactly this. A refusal the caller cannot see is an
  enforcement nobody can audit, and a receipt's value does not depend on the
  verdict going the caller's way.
- The witness moves from the hand-rolled flat `params._jcp_witness` into the
  namespaced slot, and the flat key keeps working. A silent behaviour change at
  an enforcement point is worse than a legacy key, so the fallback stays until
  something forces it out.

The returned bytes are the same sealed envelope the gateway logged, not a
summary of it — asserted directly rather than described. Attaching is defensive:
a response that is not a JSON-RPC result object comes back untouched, because
this must never mangle an upstream payload it does not understand.

Base64 is hand-rolled (no new dependency for one encode) and pinned against the
RFC 4648 §10 vectors, since a receipt that does not decode is not a receipt.

Found by the 2026-08-29 sweep, whose repo-check phase also refuted two things I
had queued as gaps — the IETF agent-audit-trail draft and a hardware-rooted
attestation paper are both already shipped, the draft being a strictly weaker
subset with an optional signature and no energy field.

Not in this change, and worth its own: MCP's new `CacheableResult`/`ttlMs` makes
a cache hit into joules avoided, and nothing in the family binds a TTL'd result
to an energy receipt.

Verified: 245 jcp-rs tests pass (5 new), conformance passes all classes,
published vectors unchanged on disk, no new clippy warnings.
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!69
No description provided.