Add a coherent set of long-running-agent capabilities, each as a spec section + reference implementation + conformance class, every one metered, attested, and receipt-chained. Sandbox: - §13 Durable run records (C6): the receipt chain IS the resumable run; execute/suspend/resume segments hash-chained; idle energy metered across parks. (sandbox-attest::runrecord) - §14 Environment manifest (C7): devcontainer.json declarative-subset ingestion -> canonical, content-addressed, signed manifest; command/widening and unknown fields refused. (sandbox-attest::manifest) - §5.5 Content-addressed process control (C8): exec allow/deny by BLAKE3 of the binary; denylist absolute, survives rename/move/wrapper. - §15 Warm pools (C9): a claim attributes accrued idle energy to the run (a fast start has a visible joule price); evictions record overhead, not zero. (sandbox-attest::warmpool) - §16 Fleet records (C10): merkle aggregate receipt over per-item runs; log-size membership proofs. (sandbox-attest::fleet) - durable.json cross-impl wire vectors + file-backed verify. JCP: - §4.6 Standing grants (C20): a signed mint policy turns a held grant into bounded, rate-limited, event-triggered children via the attenuation path. (jcp-grant::standing) - §4.7 Checkpoints + amendments (C21): mid-run steering under separation of duties — the agent checkpoints its own state, only the issuer (a distinct principal) may amend, and only to tighten. (jcp-grant::checkpoint) - standing.json cross-impl vectors. Both workspaces green; conformance harnesses pass with no skips; clippy clean. Co-Authored-By: Claude Opus 4.8 <[email protected]>
25 KiB
JCP v0.3 — The Joule Context Protocol
Status: v0.3.0 draft. Licence: specification text CC-BY-4.0; reference code Apache-2.0.
A unified, energy-native, capability-secured protocol for agentic systems. JCP subsumes in one spec what today's stack splits across MCP (tool/context connection), A2A/ACP (agent-to-agent), an identity layer, an authorization layer, and — missing everywhere — an energy layer. Its thesis is one object:
A capability and an energy budget are the same thing — a signed, bounded, debited grant. A capability you cannot pay for is denied; an expenditure you are not capable of is denied. One check, made deterministically outside the model, sealed in a receipt.
1. Scope and doctrine
- Enforcement is outside the model. Authorization and metering are decided by the runtime, never by the LLM's instruction-following. The published inverse-scaling-law result — more capable models are more exploitable because they follow injected instructions better — makes model-side control unsound by construction.
- Authority is a capability, not ambient. No actor holds standing privilege; every action requires an unforgeable Grant. There are no long-lived ambient secrets in env vars.
- Energy is first-class. Every grant carries a joule budget that is debited per action and enforced before the side effect — not merely observed.
- Data is never control. Tool and agent outputs are untrusted data and can never be executed as instructions or used to exercise a capability. This closes the asymmetric-trust gap that tool poisoning exploits.
- Every decision is non-repudiable. Allow and deny alike seal as a signed receipt. Forging the audit means forging a signature.
- One substrate. The same identity, grant, frame, and receipt span the tool layer and the agent layer; there is no second protocol to secure.
2. Actors and identity (REQUIRED)
Every actor — model, agent, tool, or server — is an Ed25519 key, expressed as a
W3C did:key (multibase base58btc of the 0xed01 multicodec ‖ the public key).
There are no anonymous actors. An actor proves itself by signing.
2.1 Identity continuity: pre-committed rotation (OPTIONAL)
For long-lived actors, an identity is its inception DID (seq 0), forever; the key that controls it rotates through a verified event log:
KeyEvent {
identity: did:key ; the seq-0 (inception) DID, stable for life
seq: uint ; 0 = inception, N = the Nth rotation
current: did:key ; the controlling key from this event on
next_hash: content-id ; blake3 of the NEXT key's 32 public bytes
parent: content-id? ; the previous event's id; absent at inception
nonce: bytes(16)
}
Inception is signed by the identity key (current MUST equal identity).
Rotation event N is signed by its new current key, which MUST hash to
event N−1's next_hash — possession of the pre-committed key is the proof of
control. The consequences are exactly the two that matter: a thief who steals
the current key still cannot rotate the identity away (they lack the
pre-committed next key), and an owner who loses the current key still can
(they hold it). A verifier MUST reject sequence gaps, parent-link mismatches,
hash mismatches, and bad signatures.
Rotation machinery is the long-lived-identity case. The RECOMMENDED norm is cheaper: short-lived grants re-issued on a heartbeat, with revocation (§4.3) covering the emergency. Rotation pressure scales with credential lifetime.
2.2 Threshold actors: the two-agent rule (OPTIONAL)
A threshold actor is a did:key whose private key never exists in one
place: it is split k-of-n (FROST, RFC 9591, Ed25519 ciphersuite), and a
signature exists only when at least k shareholders cooperate. Because the group
key is an ordinary Ed25519 verifying key, nothing else in JCP changes — a
threshold actor issues, holds, delegates, revokes, and signs wire requests, and
no verifier can tell or needs to.
Use it where one gated model is still too few: make the subject of an irreversible capability a k-of-n team, and no single agent — however capable, however poisoned — can exercise it alone. The refusal is arithmetic, not policy.
Shares MAY be minted by a trusted dealer (one party creates a team identity and hands out shares) or by distributed key generation, in which the group's private key never exists anywhere — not even momentarily at a dealer. The group key is the same ordinary Ed25519 key either way.
3. Canonical serialization and content addressing (REQUIRED)
Content addresses and the signed receipt core use canonical det-CBOR
(RFC 8949 §4.2.1): length-first then bytewise key order, shortest integer form,
no floats, bools, or nulls in a signed core. A content id is
blake3(canonical_cbor(value)), lowercase-hex on the wire — the same encoding
the JCR-1 receipt signs over (§8), so an id and a receipt agree on bytes.
4. The Grant (REQUIRED)
The metered capability is JCP's central object:
Grant {
issuer: did:key ; who granted this authority
subject: did:key ; who holds it
capability: { resource: content-id, actions: [string] }
budget_uj: uint ; the joule ceiling this grant may ever spend
expiry: uint ; unix seconds; 0 = no expiry
parent: content-id? ; the grant this was attenuated from
delegate_depth: uint? ; further hops permitted; absent = unlimited, 0 = leaf-only (§4.4)
nonce: bytes(16)
}
A grant is identified by blake3(canonical_cbor(grant)) and signed by the
issuer's key (Ed25519 over the canonical bytes). The resource is addressed
by content, not by URL — audience binding an attacker cannot spoof by name
(a content-addressed strengthening of RFC 8707).
4.1 Attenuation (REQUIRED)
Delegation is object-capability attenuation. The holder of a grant (its
subject) MAY mint a child, but the child MUST be strictly weaker:
child.capability⊆parent.capability(same resource; actions a subset),child.budget_uj≤parent.budget_uj,- the child's expiry MUST NOT outlive the parent's (
0= never expires), child.parent= the parent's content id (forming the chain).
A conforming implementation MUST refuse any amplification and MUST refuse delegation by anyone other than the parent's subject. A chain therefore can never convey more authority — or more energy — than its root.
4.2 Chain verification (REQUIRED for multi-hop)
When a frame is decided against a delegated grant, the enforcement point MUST be
presented the full chain, root first, leaf last, and MUST verify it hop by
hop: the root has no parent; every later grant's parent equals the id of the
grant before it; each hop is issued by the previous grant's subject; each hop
narrows per §4.1; and every signature in the chain verifies. A structurally
invalid chain is denied chain-broken; an amplifying one
attenuation-violation.
Energy conservation across siblings. The cost of an allowed action MUST fit the remaining budget of every grant in the chain and MUST be debited from all of them. Two children attenuated from one root therefore share the root's budget — delegation can never mint joules.
4.3 Revocation (REQUIRED)
Revocation {
grant_id: content-id ; the grant being revoked
revoked_by: did:key ; MUST equal that grant's issuer
nonce: bytes(16)
}
A revocation is signed by revoked_by over its canonical bytes. An enforcement
point MUST verify authority before honoring one — the ids match, the revoker is
the grant's issuer, the signature verifies — and MUST then deny (revoked) any
decision against the revoked grant or any chain containing it: a delegation
subtree dies with any of its ancestors. Both the direct and the chained decision
path MUST enforce this.
4.4 Delegation control (REQUIRED)
A grant MAY carry delegate_depth: how many further attenuation hops it
permits. Absent = unlimited; 0 = leaf-only — the holder may act but never
re-delegate. Like every attenuation axis, depth only narrows: a child minted
from a depth-d parent carries at most d−1, and chain verification (§4.2) MUST
refuse a hop under a leaf-only parent (delegation-forbidden semantics map to
chain-broken on the wire) and any child whose allowance exceeds its parent's
bound (an amplification). In group topologies this is the primitive that makes
a worker a worker: an orchestrator delegates depth-0 grants and the team
cannot deepen itself.
4.5 Sharded budgets: distributed enforcement without consensus (REQUIRED baseline)
When one grant must be enforceable at N enforcement points, the holder MUST shard it: mint N sibling children whose budgets sum to no more than the parent's, one per enforcement point. Each point then enforces only its own shard, locally — no coordination, no consensus — and the root stays conserved by mint-time arithmetic. Rebalancing is revocation + re-issuance.
A conforming sharding operation MUST refuse slices that sum past the parent. Fragmentation (an unused slice in one shard unavailable to another) is the deliberate price of coordination-freedom — the same trade cash makes.
Receipt-settled budget channels (OPTIONAL profile). Where shards are too rigid, N enforcement points MAY spend optimistically against one grant and settle by exchanging signed statements of cumulative local spend:
Statement {
grant_id: content-id
point: did:key ; the enforcement point making the claim
spent_uj: uint ; cumulative µJ allowed at this point since open
seq: uint ; strictly increasing per point
}
A statement is signed by point over its canonical bytes. A receiving point
MUST verify the signature, refuse stale (seq not increasing) and regressing
(spent_uj decreasing) statements, and thereafter count the peer's settled
spend against the shared budget. Between settlements the points can
collectively overspend by at most what the unsettled window allows — a bounded,
tunable exposure, and after settlement the overdraft is provable from the
signed statements rather than suspected. Where even bounded overdraft is
unacceptable, use shards; that is why the baseline is the baseline.
4.6 Standing grants: bounded authority for event-triggered work (OPTIONAL)
An autonomous agent often starts work on its own — on a webhook, a schedule, a ticket — with no human present at the firing instant. A long-lived credential that any trigger may spend is unbounded authority in an automation costume. A standing grant keeps the object-capability discipline instead: an ordinary grant (§4) the issuer holds, plus a signed mint policy that says how a trigger may turn it into work. The policy is bound to one grant by id, signed by that grant's issuer, and MUST NOT amplify it. It fixes three bounds at once: the event class permitted to fire it (content-addressed, so a webhook for one source cannot spend a grant meant for another), the per-firing budget (a slice no larger than the grant's budget), and the maximum number of firings.
Each firing mints one fresh child through the ordinary attenuation path (§4.1):
the child is strictly weaker, its parent is the standing grant, and it
chain-verifies (§4.2) like any delegation — so its receipt traces back to the
standing grant. A small mint ledger (firing count and cumulative spend)
carried by the enforcement point holds the rate limit and the parent's budget
conservation across firings by arithmetic alone, no coordination — the same
local-enforcement property as sharded budgets (§4.5). A firing is refused, and
the ledger left unchanged, when the event is of the wrong class, the firing
count is exhausted, or the next firing would push cumulative spend past the
grant. Conformance class C20.
4.7 Checkpoints and amendments: steering a long run (OPTIONAL)
A multi-hour run is not a black box that returns only at the end. At defined
boundaries the running agent commits to its state — a checkpoint — and a
distinct principal may steer it before it continues. The two roles are
structurally separate (the separation-of-duties rule): the checkpoint's
author is the agent (the grant's subject), which commits to its own
state and cannot steer itself; the amender is the grant's issuer — the
steward who delegated the authority and is therefore already a different
principal. Steering is an issuer power, like revocation (§4.3): an
amendment is a soft revoke that re-bounds the run rather than killing it.
A checkpoint cites the active grant and a content-addressed state commitment, signed by the agent. An amendment cites a checkpoint, is signed by the grant's issuer, MUST differ from the agent, and MUST only tighten — the continuation's budget is at most the checkpoint grant's, so steering can never escalate a running agent's authority; it MAY also hand the agent a new instruction-set commitment. The run resumes under a continuation grant the issuer re-issues to the same agent with the amended budget, linked back to the steered grant. This continuation is an issuer re-binding, not a holder delegation — an auditor verifies it alongside the signed checkpoint+amendment pair, which together record who intervened, when, and how. Conformance class C21.
5. Energy (REQUIRED)
Every metered cost is integer microjoules with a provenance tag: HwShunt
(hardware reading; a measured figure REQUIRED), ModelBased (calibrated model;
measured MAY be absent), or Estimator (coarse constant; measured MUST be
absent). The runtime debits cost.uj() from a grant's remaining budget on every
allow; the coupling MUST be enforced. The energy axis is what no existing
agentic protocol standardizes — JCP makes it a precondition, not a report.
5.1 The metering ladder (REQUIRED behavior, RECOMMENDED backends)
The rule: measured when the platform exposes it, estimated when it doesn't —
and the provenance always says which. There is always an estimate: an
environment with no energy interface still yields a usable figure, it just
carries ModelBased or Estimator provenance instead of HwShunt. A figure
MUST NOT claim better provenance than how it was obtained — including when a
hardware backend fails mid-reading and the figure degrades to a model.
Recommended ladder, best first:
- RAPL (Linux
powercap) — package energy counters, wraparound-corrected →HwShunt. - NVML — the GPU total-energy counter →
HwShunt. - Time × calibrated power — wall-clock duration times a host-calibrated
active power →
ModelBased. Always available. - Constant per-operation estimate →
Estimator.
6. The Frame (REQUIRED)
Every message is a typed Frame — one envelope across the tool layer (MCP's job) and the agent layer (A2A's job):
Frame {
kind: tool-call | tool-result | agent-task | agent-reply
trust: control | data
grant_id: content-id ; the grant this frame exercises
action: string
resource: content-id
payload_hash: content-id
}
trust = data marks untrusted output. A conforming runtime MUST refuse to
exercise any capability from a data frame (§7), regardless of its content. Tool
and agent outputs MUST be tagged data.
7. The decision (REQUIRED)
Given a Frame, its SignedGrant, the caller's DID, a
metered cost, and the current time, a conforming runtime decides Allow or
Deny(reason) by these checks, in order, the first failure naming the reason:
data-tagged frame → Deny(untrusted-control).- grant revoked (§4.3) → Deny(revoked).
- the issuer (for a chain: the root issuer) is not a configured trust root for the frame's resource → Deny(untrusted-root). An unconfigured resource is open; once any root is configured, only listed keys anchor authority.
- frame's
grant_id≠ the grant's id → Deny(wrong-resource). - caller ≠ grant's
subject→ Deny(wrong-subject). - issuer signature invalid → Deny(bad-signature).
- expired → Deny(expired).
- frame's
resource≠ capability's resource → Deny(wrong-resource); action ∉ capability → Deny(action-not-granted). - prior spend +
cost>budget_uj→ Deny(budget-exceeded).
On Allow, the runtime debits cost from the grant's remaining budget. The
decision is pure and deterministic; no model participates.
For a delegated grant the decision takes the full chain (§4.2): structure and
signatures first (chain-broken / attenuation-violation / bad-signature),
then revocation against every link (revoked), expiry of every link, the
leaf-level checks above, and budget against every link, debiting all on allow.
Trust roots. An enforcement point is configured with the root-issuer
keys it honors for each resource it guards — the same local, pairwise trust
shape as an authorized_keys file. did:key is self-certifying, so there is no
certificate authority and no hierarchy; cross-organization federation is a
directory convenience, never a trust dependency. A chain whose root issuer is
not configured for the frame's resource MUST be denied.
8. The receipt (REQUIRED)
Every decision — allow and deny — seals as a JCR-1 receipt: canonical
det-CBOR inside a COSE_Sign1 envelope (RFC 9052, EdDSA/Ed25519, alg = -8). The
jcp/1 profile signs a flat uint/text core:
jcp_receipt = "jcp/1"
grant_id = hex
issuer, subject = did:key
resource, action = hex, string
frame_kind, trust = "tool-call"…, "control" | "data"
verdict = "allow" | "deny"
deny_reason = string ; omitted on allow
cost_uj = uint
energy_provenance = "HwShunt" | "ModelBased" | "Estimator"
budget_remaining_uj, budget_uj = uint
steward = the signer's id (the COSE kid)
Receipts are byte-compatible with the family's JCR-1 verifiers. A deny receipt is as load-bearing as an allow receipt — the record of a refused attack is evidence.
8.1 The receipt stream is the write-ahead log (REQUIRED)
A receipt carries everything enforcement needs to remember: the grant, the
verdict, the debit. A receipt sealed for a multi-hop chain MUST additionally
record chain_ids — every grant id the decision debited, root first — so the
stream is self-contained. An enforcement point that crashes MUST be able to
rebuild its budget state by replaying its own verified receipt stream:
allow-receipts re-apply their debits to every grant they name, ancestors
included, with no external index. Durability and audit are the same artifact;
there is no second database to drift from the evidence.
8.2 Group accounting (RECOMMENDED profile)
When a team works under sub-grants of one root, aggregation of the verified
receipt stream keyed by (grant_id, subject) yields per-worker attribution —
actions allowed, actions refused, joules spent — computed from evidence rather
than self-reporting. An aggregator MUST count (not silently skip) receipts that
fail verification: a ledger that drops entries is not a ledger.
9. Conformance
A conforming implementation passes, with real checks:
- C1 — signed grants + audience binding: a grant verifies only for its issuer's key and authorizes only its exact content-addressed resource.
- C2 — attenuation monotonicity: a child grant can only narrow; any amplification or non-holder delegation is refused.
- C3 — budget enforcement: spend beyond the ceiling is denied deterministically and the denied call is never debited.
- C4 — data/control separation: a
dataframe can never exercise a capability. - C5 — the receipt: every decision seals to JCR-1 and verifies; a wrong steward fails.
- C6 — revocation: only the issuer revokes; a revoked ancestor kills the whole delegation subtree; a forged revocation is never admitted.
- C7 — chain conservation: a multi-hop chain verifies hop by hop; a spliced chain is refused; sibling delegations share their root's budget.
- C8 — wire binding: a request round-trips the
jcp/1framing canonically and an enforcement endpoint decides + seals over a live socket. - C9 — energy honesty: the metering ladder always yields a figure and its provenance is internally consistent.
- C10 — delegation control: depth narrows per hop; a leaf-only grant cannot be delegated, at mint or in chain verification.
- C11 — sharded budgets: shard slices that sum past the parent are refused at mint.
- C12 — rotation: an un-pre-committed key cannot rotate an identity, the pre-committed key can, and the identity DID is stable across rotations.
- C13 — caller-signed wire + threshold: an impersonated caller is denied
bad-signature; one share of a 2-of-3 actor cannot sign; two can, and the endpoint treats the aggregate as any caller signature. - C14 — replay + group accounting: a fresh runtime restores spend from the verified receipt stream; per-worker attribution matches the original ledger; a tampered receipt contributes nothing.
- C15 — trust roots: an unconfigured resource is open; once configured, a
validly-signed grant from an unlisted root is denied
untrusted-rooton both decision paths. - C16 — chained replay: a chained receipt names every debited link; a fresh runtime replays leaf AND ancestor debits exactly and keeps conserving.
- C17 — budget channels: optimistic spends settle by signed statement; forged, stale, and regressing statements are refused; post-settlement the merged ledger binds and any overdraft is provable.
- C18 — DKG: a dealer-free k-of-n actor signs a standard Ed25519 signature; one share cannot.
- C-neg: expired, forged, and wrong-caller frames are denied with the precise reason; canonical CBOR rejects floats/bools.
Published vectors live in conformance/v1/ (cargo run -p jcp-conformance --bin gen-vectors).
10. What JCP closes (mapping to the documented failures)
| Documented failure | JCP mechanism |
|---|---|
| Prompts-as-security; inverse scaling law | §1.1 enforcement outside the model |
| Tool poisoning / asymmetric trust | §6 data≠control |
| Ambient authority, static env secrets | §4 unforgeable grants |
| Confused deputy, token passthrough | §4 content-addressed audience binding |
| Delegated authority | §4.1–§4.2 attenuation chains, verified hop by hop |
| A leaked grant lives forever | §4.3 issuer revocation, subtree-transitive |
| Runaway loops, no energy accounting | §5 enforced joule budgets, conserved across delegation |
| Mutable / optional audit | §8 signed JCR-1 receipts |
| Fragmentation across protocols | §6 one frame for tools and agents |
11. Wire binding: jcp/1 over a byte stream (OPTIONAL)
JCP is transport-agnostic; this section defines the reference binding for any ordered byte stream (TCP, Unix socket, pipe, TLS session).
Framing. Each message is a 4-byte big-endian length followed by the message's canonical det-CBOR bytes (§3). A receiver MUST reject a message longer than its declared cap and MUST reject a non-canonical body — a message has exactly one wire form, so content addressing stays injective across the wire.
Messages. One request/response pair per action:
WireRequest { frame, chain: [signed-grant…], caller: did:key, cost: energy,
now: uint, signature: bytes(64) }
WireResponse { decision, receipt: bytes }
chain is the full delegation chain, root first (a single un-delegated grant is
a one-link chain). now makes decision time an explicit input — a decision is
replayable, not ambient. The response carries the deterministic decision and the
sealed JCR-1 receipt, deny and allow alike.
Caller authentication. signature is Ed25519 by caller over the
canonical bytes of every other field. The endpoint MUST verify it before
deciding; a request that fails is denied bad-signature and sealed like any
other decision — the refused impersonation is evidence too. A threshold actor
(§2.2) signs the same field with a k-of-n aggregate, indistinguishable on the
wire.
The wire never carries a secret: grants are signed public objects, and a captured request replays into the same deterministic decision and the same budget accounting it produced the first time. The endpoint behind the enforcement point is invoked only on Allow.
12. Non-goals
JCP does not define a model or a tool registry. It does not detect misbehavior with probabilistic "trust scores" — bounded capability and enforced budget make the dangerous action unreachable rather than observed. Behavioral scoring MAY be layered above as a secondary signal; it is never the gate.
- C20 — standing grants: an event-triggered firing mints a strictly-weaker child that traces to the standing grant; the wrong event class, an exhausted firing count, and a firing past the cumulative budget are each refused without advancing the ledger; a policy that would amplify its grant is refused at author time.
- C21 — checkpoints + amendments: an agent checkpoints its own state; only the grant's issuer — a principal distinct from the agent — may amend, only to tighten; an agent steering itself, a non-issuer amending, a widening amendment, and an amendment bound to a different checkpoint are each refused.