The chokepoint position the AI-gateway field occupies (LiteLLM, Bifrost, Portkey), rebuilt on the two axes nobody there holds: the unit of account is the joule, and the audit is a hash-chained cryptographic receipt log rather than a dollar figure and an OpenTelemetry span. jcp-runtime gains two-phase metered enforcement. A metered gateway cannot know a call's cost until it runs, so `decide` (check + debit a known cost) splits: `authorize` runs every validity check and gates on "any budget remains" without debiting; `charge` debits the MEASURED cost afterward, saturating at the ceiling so the final call's overrun is recorded (budget_remaining 0, cost > debited) rather than hidden. jcp-receipt gains ReceiptLog: an append-only, hash-chained log where each entry's signed core commits to the prior entry's envelope content id (genesis links to an all-zero sentinel). verify_log walks the chain end to end; excising, reordering, or splicing a receipt breaks it at that index — the integrity layer OTel lacks and the property the EU AI Act logging obligations and the notarized-agents research both reach for. Allows and denies are both chained. jcp-gateway: the gateway crate + stdio binary. relay() classifies each client message, passes through non-tools/call traffic, and turns every tools/call into an enforcement point: authorize → meter the upstream call on real hardware (sandbox-meter: IOReport/RAPL/NVML) → debit measured joules → seal into the chain. A denied call never reaches the server. The Upstream trait keeps policy testable without a subprocess. Live, in front of a subprocess MCP server: read_file calls allowed and debited (827,000 and 834,825 µJ measured on Apple Silicon), a shell/`rm -rf /` call DENIED before reaching the server, three sealed receipts (allow/deny/allow) in a chain that verifies. 69 jcp tests (gateway: allow/deny/budget-exhaustion+chain-verify; receipt: tamper-evidence via excise+reorder), clippy zero. Co-Authored-By: Claude Fable 5 <[email protected]>
90 lines
10 KiB
Markdown
90 lines
10 KiB
Markdown
# JCP — The Joule Context Protocol
|
||
|
||
A unified, energy-native, capability-secured protocol for agentic systems. Where
|
||
today's stack splits across MCP (tools), A2A/ACP (agent-to-agent), an identity
|
||
layer, and an authorization layer — and standardizes energy nowhere — JCP is one
|
||
spec, built on one idea:
|
||
|
||
> **A capability and an energy budget are the same object: a signed, bounded,
|
||
> debited grant. A capability you can't pay for is denied; an expenditure you're
|
||
> not capable of is denied. One check, made outside the model, sealed in a
|
||
> receipt.**
|
||
|
||
The design answers the documented failure modes of the current agentic
|
||
ecosystem, not hypotheticals. Tool descriptions are untrusted input → JCP refuses
|
||
to take control from a tool's *output*. Prompts can't enforce access control (and
|
||
a more capable model only follows injected instructions *better*) → JCP decides
|
||
every action in the runtime, never in the LLM. Agent loops have no cost ceiling →
|
||
every grant carries a joule budget that's debited and enforced before the side
|
||
effect. Audit is a mutable telemetry span → JCP seals every decision, allow and
|
||
deny, as a non-repudiable signed receipt.
|
||
|
||
- **Licence:** Apache-2.0 for reference code (see [`LICENSE`](LICENSE)); CC-BY-4.0 for specification text (see [`LICENSE-SPEC`](LICENSE-SPEC)).
|
||
- **Status:** v0.3.0 draft. Specification core published ([`SPEC.md`](SPEC.md) + [`conformance/v1/`](conformance/v1/)); reference workspace `jcp-rs/`.
|
||
|
||
## What's here
|
||
|
||
```
|
||
jcp/
|
||
├── SPEC.md — the normative standard (§1–§12)
|
||
├── conformance/v1/ — published vectors (a signed grant + allow/deny receipts)
|
||
└── jcp-rs/ — the Apache-2.0 reference implementation
|
||
```
|
||
|
||
### The reference implementation (`jcp-rs/`)
|
||
|
||
- **`jcp-schema`** — the wire objects: the `did:key` actor, the **Grant** (the metered capability — capability ⊗ joule budget, content-addressed and signed), the **Frame** (every message, with its `control`/`data` trust tag), the family `Energy`/`Provenance` model, canonical det-CBOR content addressing.
|
||
- **`jcp-grant`** — issuance, signature verification, **attenuation** (object-capability delegation where a child grant can only narrow — capability ⊆, budget ≤, expiry no later, delegation depth − 1), **chain verification** (root-first, hop-by-hop: parent links, holder-issued hops, narrowing, signatures, depth bounds), **revocation** (issuer-only, signed, admitted into a `RevocationSet` only after authority checks), **sharding** (`shard()` splits a grant into siblings whose budgets must sum within the parent — distributed enforcement with no consensus, SPEC §4.5), and **identity continuity** (`KeyLog`: KERI-style pre-committed rotation, SPEC §2.1 — a thief with the current key can't rotate, an owner who lost it still can).
|
||
- **`jcp-runtime`** — the deterministic enforcer. One ordered check fuses authorization and energy, decided **outside any model**: untrusted-control, revocation, **configured trust roots** (an enforcement point honors only the root-issuer keys it was configured with — `authorized_keys` shape, no CA), audience binding, signature, expiry, capability, and budget — then debit. The first failure names the reason. `decide_chained` enforces full delegation chains: a revoked ancestor kills the subtree, and the cost must fit — and is debited from — **every** link, so sibling delegations share their root's budget and delegation can never mint joules.
|
||
- **`jcp-receipt`** — the `jcp/1` JCR-1 receipt: canonical det-CBOR + COSE_Sign1/EdDSA, byte-compatible with the family verifiers. Allow and deny both seal. The receipt stream is the write-ahead log (SPEC §8.1): a chained receipt records `chain_ids` — every link the decision debited — so `replay_spend` rebuilds an enforcement point's budget state, **ancestors included**, from its own verified output with no external index; `aggregate` keys a team's receipts by `(grant, subject)` for per-worker cost attribution computed from evidence (SPEC §8.2).
|
||
- **`jcp-bridge`** — a reference gateway that wraps **existing MCP tool calls and A2A tasks** in Grants and Frames without rewriting the endpoint. It maps each outbound call to a Frame (the tool/method is the action; the `(server, tool)` identity is the content-addressed resource), decides it against the caller's grant *before* the call reaches the endpoint, and seals a receipt — so an existing server keeps its transport while every call becomes capability-scoped, joule-budgeted, decided outside the model, and sealed. Inbound results are framed as untrusted `data`.
|
||
- **`jcp-energy`** — the metering ladder (SPEC §5.1): RAPL package counters (Linux powercap, wraparound-corrected, `HwShunt`), NVML GPU energy behind the `nvml` feature (`HwShunt`), a time×calibrated-power model (`ModelBased`, always available), and a constant per-op estimator (`Estimator`). Measured when the platform exposes it, estimated when it doesn't — and the provenance always says which; a hardware backend that fails mid-reading degrades honestly to the model.
|
||
- **`jcp-gateway`** — the **energy-native MCP gateway**: a JCP enforcement point that sits in front of an MCP server so an existing client/server pair keeps its transport while every `tools/call` becomes capability-scoped, joule-budgeted, and sealed. The novel mechanic is two-phase metered enforcement: the call is *authorized* before it runs (no debit, because the cost isn't yet known), the upstream call is **metered on real hardware** (the sibling Sandbox standard's energy probe — Apple Silicon IOReport, RAPL, NVML), and the **measured** joules are debited from the grant — so the budget that stops a runaway agent is denominated in energy actually spent, not a token estimate. Every decision, allow and deny, seals into a **hash-chained receipt log**: excise, reorder, or splice a receipt and the chain breaks at that point. A denied call never reaches the server (the live 2025–26 MCP exploits land on tool *output*, which the gateway frames as untrusted `data` that can never exercise a capability), and the `jcp-gateway` binary runs it over stdio in front of any MCP-server subprocess. This is the chokepoint the AI-gateway field occupies, on the two axes nobody there holds: the unit of account is the joule, and the audit is cryptographic and tamper-evident rather than a dollar figure and a span.
|
||
- **`jcp-wire`** — the `jcp/1` wire binding (SPEC §11): 4-byte length prefix + canonical det-CBOR over any byte stream, with TCP and Unix-socket endpoints and a client. The decoder rejects non-canonical bodies, so a message has exactly one wire form. Requests carry the full delegation chain and are **caller-signed** — the endpoint authenticates from the request itself and denies an impersonation `bad-signature`, sealed like any other decision. Responses carry the deterministic decision and the sealed receipt.
|
||
- **`jcp-threshold`** — threshold actors (SPEC §2.2): a `did:key` whose private key is split k-of-n with FROST (RFC 9591); the group key is an ordinary Ed25519 key, so a 2-of-3 team issues, holds, and signs wire requests with nothing else in JCP changing. Shares come from a trusted dealer or **distributed key generation** — with DKG the group's private key never exists anywhere, not even momentarily. The two-agent rule for irreversible actions, refused by arithmetic rather than policy.
|
||
- **`jcp-channel`** — receipt-settled budget channels (SPEC §4.5, the OPTIONAL profile): N enforcement points spend optimistically against one grant and settle by exchanging signed statements of cumulative spend — forged, stale, and regressing statements refused; between settlements the exposure is bounded by the window, and after settlement any overdraft is provable from the signed statements. Where bounded overdraft is unacceptable, `shard()` remains the baseline.
|
||
- **`jcp-conformance`** — C1–C18 + a negative pack, all checks real (C8 decides over a live socket; C13 drives a FROST 2-of-3 caller; C16 replays ancestor debits from a chained receipt; C17 settles a budget channel by signed statement; C18 mints a dealer-free group via DKG), published vectors in `conformance/v1/`.
|
||
- **`jcp-cli`** — the `jcp` demo: one metered grant driven through an agent loop — legitimate calls allowed and debited, a poisoned tool-result refused structurally, out-of-scope actions and resources denied, the budget stopping a runaway loop, a sub-agent getting a strictly-weaker delegated grant, a signed receipt verified, the detected energy meter reporting its honest provenance, and the issuer revoking the grant — after which the runtime goes dark.
|
||
|
||
`cargo test --workspace` green; `cargo clippy --workspace --all-targets`
|
||
warning-free.
|
||
|
||
## The demo, abbreviated
|
||
|
||
```
|
||
frame verdict reason rem µJ
|
||
read the db (legit) allow −20000 µJ 80000
|
||
tool-result says 'delete db' (poison) DENY untrusted-control 80000
|
||
try to delete (not granted) DENY action-not-granted 80000
|
||
read prod-secrets (wrong audience) DENY wrong-resource 80000
|
||
read again — budget runs out DENY budget-exceeded 30000
|
||
|
||
amplification refused: capability exceeds parent
|
||
receipt allow · 50000 µJ · verified true
|
||
```
|
||
|
||
The model never gates itself; the runtime does, and the receipt proves it.
|
||
|
||
## Consumers in this repository
|
||
|
||
JCP is not only specified — it is consumed. Three sibling standards gate their
|
||
real dispatch through it, each with its own adapter crate and tests:
|
||
|
||
- **JouleClaw** (`jouleclaw-rs/crates/jouleclaw-jcp`) — every MCP tool dispatch
|
||
is decided against a Grant before a byte reaches the tool; an ungranted tool
|
||
named by an injected instruction is refused and the refusal sealed.
|
||
- **Sandbox** (`sandbox-rs/crates/sandbox-jcp`) — a unit's execution is
|
||
authorized against a Grant bound to its image **by content address**; one
|
||
flipped image byte is a different audience and is denied before the executor
|
||
is ever invoked. Two receipts per run: the JCP authorization and the
|
||
sandbox's metered, attested execution.
|
||
- **Joule Code** (`joule-code-rs/crates/joule-code-jcp`) — every cascade-stage
|
||
consultation is decided pre-dispatch; the L4 escape hatch to a remote
|
||
frontier agent exists only when a signed grant names it and can pay for it.
|
||
|
||
## Relationship to the family
|
||
|
||
JCP composes the primitives the Transaction Science family already proved:
|
||
`did:key`/Ed25519 identity, capability-gated execution (`sandbox`), the honest
|
||
`Energy`/`Provenance` model (`proof`), and JCR-1 signed receipts (family-wide).
|
||
It is where they stop being per-tool adapters and become the protocol.
|