open-standards/jouleclaw/jouleclaw-rs/crates/jouleclaw-jcr1/docs/JCR-1-spec.md
Transaction Science 84768074ca
Some checks failed
energy meters on real hardware / RAPL and NVML on jetson-hub (push) Has been cancelled
energy meters on real hardware / sink decode table · RAPL on jetson-hub (NVML surveyed) (push) Has been cancelled
energy meters on real hardware / sink decode table · per-task energy and the GPU rail on macOS (push) Has been cancelled
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Has been cancelled
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (push) Has been cancelled
joule-code conformance gate / joule-moq-relay, its own energy on a counter (macos) (push) Successful in 2m15s
wai-app and Create / wai-app · does this change touch it (push) Successful in 1m45s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (push) Has been cancelled
wai-app and Create / wai-app · Create, browser end-to-end (macos) (push) Has been cancelled
joule-code conformance gate / Build + round-trip C1-C4 vectors (push) Successful in 5m43s
joule-code conformance gate / joule-moq-ingest, every target (push) Has been cancelled
joule-code conformance gate / joule-moq-relay, every target (push) Has been cancelled
Every text that states the IOReport liveness rule states it as the meters apply it
The meters count an IOReport interval only within three of the probe's ticks, and
the texts that state the rule said nothing of it, so an implementation following
any of them would call a lump-refreshed rail live once load stretched its intervals.
Each now says what an interval is (from one sample in hand to the next), that only
one no longer than three of the probe's ticks (75 ms at its 25 ms tick) counts, that
a longer one counts as nothing (not among the intervals, not as an advance, not
towards the 300 ms), and that the 300 ms is the summed length of the intervals that
count.

The normative clauses in arl/ARL-S.md, jcp/SPEC.md, joule-code/spec/receipt.md,
joule-ui/SPEC.md, jouleclaw/SPEC.md, JCR-1-spec.md, map/spec/receipt.md,
mesh/SPEC.md (a block quote), proof/SPEC.md and sense/SPEC.md; proof-schema's
quotation of proof/SPEC.md's; jouleclaw-energy's conformance README; and the proof
and sandbox READMEs, which say the cap is IOReport's alone. Map's and Joule Code's
unreleased changelog entries, which record the rule as it was added, are amended,
since neither release has gone out. No wire or conformance-vector change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-07 08:20:02 -05:00

313 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# JouleClaw Receipt Specification (JCR-1) — Draft
**Status:** Draft · **Version:** 1 (`jc_receipt: "1"`) · **License:** CC-BY-4.0 (text), Apache-2.0 (vectors)
An open, implementation-independent format for a **signed, per-resolution
thermodynamic receipt**: proof that a given output cost a given number of
joules, on hardware of a stated measurement honesty, verifiable by a third party
who never ran the cascade.
This document formalizes the `jouleclaw-prov` receipt shape and **closes the gap
the reference crate leaves open**: it defines (a) a canonical serialization, (b) a
dependency-free signed envelope, and (c) a verification flow — so conformance does
not depend on any JouleClaw code.
Key words MUST / SHOULD / MAY per RFC 2119.
---
## 1. Design invariants
1. **Honesty over precision.** A receipt MUST NOT claim accuracy finer than the
worst energy counter in its span can deliver.
2. **Determinism.** The signed bytes MUST be reproducible bit-for-bit from the
receipt fields. No field that cannot be canonically encoded may enter the
signed core.
3. **Producer/verifier separation.** A producer mints and signs; a verifier needs
only this spec, the public key, and (for joule re-validation) the conformance
vectors. Verification MUST NOT require re-running proprietary code.
4. **Additive evolution.** Unknown fields MUST be ignored, never rejected. v1 only
adds optional fields.
---
## 2. The Receipt object
Units are fixed: **energy is microjoules as an unsigned 64-bit integer
(`joules_uj`)**; floating point is forbidden in the signed core. Timestamps are
RFC 3339 UTC. Hashes are lowercase hex BLAKE3-256. Tier tags are `L0`–`L4`.
### 2.1 Top-level fields
| Field | Type | Req | Meaning |
|---|---|---|---|
| `jc_receipt` | string | MUST | Schema version. `"1"` for this spec. |
| `id` | string | MUST | Stable receipt id, monotonic per steward. |
| `closed_at` | string (RFC3339) | MUST | When the cascade walk closed. |
| `input_hash` | string (blake3-hex) | MUST | BLAKE3 of the normalized input that started the walk. |
| `tier` | enum `L0..L4` | MUST | Tier that **closed** the query. |
| `joules_uj` | u64 | MUST | Total microjoules across all tiers walked. |
| `energy_provenance` | enum | MUST | Honesty tier of the **worst** counter in the span (§2.3). |
| `steward` | string | MUST¹ | Signing identity (KERI AID or `did:key`). Binds receipt to issuer. |
| `tools_touched` | array⟨ToolTouch⟩ | MAY | Per-tool energy, in call order. |
| `claims` | array⟨ClaimProvenance⟩ | MAY | Sources that grounded the resolution. Empty for L0/L1. |
| `tool_receipts` | array⟨ToolReceipt⟩ | MAY | Gateway-HMAC'd attestations (§2.6). |
| `eoc_stage` | string | MAY | EOC cascade stage label, if driven by one. |
| `citations` | array⟨Citation⟩ | MAY | Span→source grounding (advisory). |
| `token_attributions` | array⟨TokenAttribution⟩ | MAY | Per-token saliency (advisory, **not in signed core** — §3.3). |
¹ `steward` is added by this spec; the reference crate carried the issuer in the
outer Smart Byte envelope. Carrying it inline makes the receipt self-describing.
### 2.2 CascadeTier
`L0` Cache (picojoules) · `L1` Lawful/deterministic (nanojoules) · `L2` Embed
(sub-mJ) · `L3` Model (joules) · `L4` Wire/remote frontier (tens of J+). Wire tags
`L0`–`L4` are stable across versions.
### 2.3 Provenance (the honesty tag) — normative
Every energy reading carries one of:
- `HwShunt` — hardware reading: a shunt (Jetson INA3221), or a vendor energy
counter (x86 RAPL MSR, NVIDIA cumulative-energy counter, Apple IOReport) while
it is live for the domain it claims (below). Honest to the counter's
resolution.
- `ModelBased` — vendor model estimate from freq/voltage/utilization (NVML
`power.draw`, ROCm SMI). Millijoule-quantized at best.
- `Estimator` — JouleClaw static cost model. Wide tolerance by construction.
A vendor energy counter's reading is a real reading; the trust gap is
provenance (which counter, which domain, whether it was live), not accuracy.
Hence a live vendor counter is `HwShunt`, under this condition:
**Liveness (MUST):** A reading from a vendor energy counter (RAPL, NVML, Apple
IOReport, …) MAY be graded as a hardware reading only while the counter is
demonstrably live for the domain the figure claims: the implementation MUST have
observed the counter covering that domain advance under load (for Apple IOReport:
the CPU energy rail itself advancing in at least 5 sample intervals, in at least
4 of every 5, over at least 300 ms while the probing thread burns CPU; an interval
runs from when one sample is in hand to when the next is, and only one no longer
than three of the probe's ticks, 75 ms at its 25 ms tick, counts: a longer one
counts as nothing, neither among the intervals, nor as an advance, nor towards
the 300 ms, which is the summed length of the intervals that count). A counter
that is present but not live — frozen, or republished in lumps — MUST NOT produce
a hardware-graded figure; the implementation falls back to its estimator or
reports the work unmetered, and SHOULD record why. A figure MUST NOT claim
coverage of a domain its counter does not read; where the format has a coverage
field, such a figure is Partial. (JCR-1 v1 has no coverage field: a figure for a
domain its counter does not read is not a `HwShunt` figure for that work at all.)
Apple IOReport's GPU rail is machine-wide (display compositing moves it with
nothing of the metered work running), so it is not a partial reading of CPU work:
where only the GPU rail is live, the reference implementation declines the
counter and the CPU work is graded `Estimator`.
**Worst-counter rule (MUST):** `Receipt.energy_provenance` = the least-honest
provenance among all readings contributing to `joules_uj`, ordered
`HwShunt > ModelBased > Estimator`. A receipt whose span mixed a HwShunt GPU read
and an Estimator NPU read MUST report `Estimator`.
**Resolution floor (MUST):** `joules_uj` MUST NOT be reported at finer resolution
than the worst counter's `resolution_uj`. For `ModelBased`/`Estimator` the
realistic floor is ~1000 µJ (1 mJ); values SHOULD be rounded to that quantum and
the receipt MUST NOT imply sub-mJ precision it cannot support.
### 2.4 ClaimProvenance
`source` (string: URL/DOI/did) · `content_hash` (blake3-hex of claim bytes at
fetch) · `fetched_at` (RFC3339) · `trust_tier` (u8, higher = more trusted).
PROV-O mapping: each claim is a `prov:Entity`; `source` is the IRI, `content_hash`
the `wasGeneratedBy` fingerprint.
### 2.5 ToolTouch
`tool_id` (string, e.g. `mcp:filesystem/read`, `iac:fs.read`,
`model:gemma4-9b-q5_k_m`) · `joules_uj` (u64) · `energy_provenance` (enum).
### 2.6 ToolReceipt (gateway-attested)
Proves a tool was invoked **through the gateway**, not asserted by the model.
`tool_id` · `joules_uj` · `energy_provenance` · `minted_at` (RFC3339) ·
`gateway_id` (string) · `hmac_sha256_hex` (64 hex chars).
The HMAC (§5) is keyed by a gateway secret the model never holds.
### 2.7 Citation / TokenAttribution
`Citation`: `text_span {start:u32, end:u32}` (half-open byte range over the
output the consumer holds) · `source_chunk_id` · `exact_quote` (verbatim,
findable in the chunk).
`TokenAttribution`: `token_idx:u32` · `chunk_scores: map<string,float>`
(provider-defined, **ordinal not absolute**) · optional `method`. Advisory only.
---
## 3. Canonicalization (the signed core)
### 3.1 Signed core vs advisory
The **signed core** is every MUST/MAY field in §2.1 **except** `token_attributions`
(provider-defined floats, ordinal, not reproducible across methods). `citations`
are in the core (strings/ints, deterministic, and grounding-relevant).
`token_attributions` are carried alongside, outside the signature.
### 3.2 Canonical encoding
The signed core MUST be encoded as **deterministic CBOR** per RFC 8949 §4.2:
- Map keys are the field names as text strings, sorted by byte-wise
lexicographic order of the UTF-8 key.
- Integers use the shortest form; `joules_uj`, `start`, `end`, `token_idx`,
`trust_tier` are unsigned.
- Absent optional fields are **omitted**, not encoded as null.
- Arrays preserve producer order (`tools_touched`, `claims`, `tool_receipts`,
`citations` are order-significant).
- No floats appear in the signed core (see §3.1).
A JSON projection (RFC 8785 JCS) MAY accompany the CBOR for debugging, but the
signature is ALWAYS over the deterministic CBOR bytes.
### 3.3 The bytes to sign
`CORE = det_cbor(signed_core_fields)`. The envelope signature (§4) is computed
over `CORE`. Recomputing `CORE` from the parsed fields MUST yield identical bytes;
a verifier that cannot reproduce `CORE` MUST treat the receipt as invalid.
---
## 4. The signed envelope
Receipts are sealed in a **COSE_Sign1** structure (RFC 9052) so they interoperate
with the RATS/CoRIM attestation tooling JouleClaw already uses.
```
COSE_Sign1 = [
protected: bstr .cbor { 1: alg, 4: kid }, ; alg = -8 (EdDSA) or -7 (ES256)
unprotected: { },
payload: bstr, ; = CORE (det-CBOR signed core)
signature: bstr ; over Sig_structure per RFC 9052
]
```
- **alg (MUST):** EdDSA/Ed25519 (`-8`) is the default; ES256 (`-7`) permitted.
- **kid (MUST):** the `steward` identity — a KERI AID (recommended; supports key
rotation/witnessing) or a `did:key`. The verifier resolves `kid` → public key
out of band.
- **payload:** the deterministic-CBOR signed core (§3.3).
Key management is explicitly out of scope. KERI AID rotation is the RECOMMENDED
substrate; any resolvable `kid → public key` mapping conforms.
---
## 5. Tool-receipt HMAC (sub-signatures)
Each `ToolReceipt.hmac_sha256_hex` is `HMAC-SHA-256(gateway_key, payload)`,
lowercase hex, where `payload` is the NUL-delimited canonical byte string:
```
tool_id ‖ 0x00 ‖ ascii(joules_uj) ‖ 0x00 ‖ prov_tag ‖ 0x00 ‖ minted_at ‖ 0x00 ‖ gateway_id
```
`prov_tag` ∈ {`HwShunt`,`ModelBased`,`Estimator`}; `joules_uj` as decimal ASCII.
Verifiers look up the gateway key by `gateway_id` and recompute. Comparison MUST
be constant-time. This sub-signature is independent of the envelope: it survives
replay into a different context, attesting the gateway — not the model — observed
the cost.
---
## 6. Verification flow
A verifier processes a received envelope in this order; failure at any step makes
the receipt **invalid** (steps 1–4) or **unverified-but-structurally-valid**
(step 5, when vectors are unavailable):
1. **Structural.** Parse COSE_Sign1; parse payload as det-CBOR; reject unknown
`jc_receipt` major versions; ignore unknown fields.
2. **Canonical re-encode.** Recompute `CORE` from parsed fields; it MUST equal the
payload bytes byte-for-byte.
3. **Envelope signature.** Resolve `kid → public key`; verify the COSE_Sign1
signature over `CORE`.
4. **Tool receipts.** For each `tool_receipts[i]`, look up `gateway_id`'s key and
verify the HMAC (§5). A receipt asserting gateway-metered tools whose HMAC
fails is invalid.
5. **Honesty consistency.** Check `energy_provenance` equals the worst provenance
across `tools_touched[*].energy_provenance` and `tool_receipts[*]`; check
`joules_uj ≥ Σ tools_touched.joules_uj`; check the resolution floor (§2.3).
6. **(Optional) joule re-validation.** If the verifier holds the conformance
vector for this `input_hash` class, re-execute the cascade and confirm
`joules_uj` falls within the **drift band** for the receipt's provenance tier
(§7). Absent vectors, the receipt is signature-valid but joule-unverified.
---
## 7. Conformance
### 7.1 Drift bands (joule re-validation tolerance)
Re-execution will not reproduce joules exactly; the permitted band scales with
honesty:
| Provenance | Drift band on `joules_uj` |
|---|---|
| `HwShunt` | ±10% |
| `ModelBased` | ±35% |
| `Estimator` | ±100% (order-of-magnitude only) |
(Bands are the v1 defaults and are themselves part of the conformance vector;
publish measured bands as data accrues rather than asserting these forever.)
### 7.2 Conformance levels
- **Producer-conformant:** emits receipts whose signed core re-encodes
identically (§3) and whose `energy_provenance`/resolution obey §2.3.
- **Verifier-conformant:** implements steps 1–5 and reproduces published vectors.
- **Vector-conformant standard:** an *independent* implementation round-trips all
10 published conformance vectors (verify + re-encode), proving the spec — not
the reference code — is the source of truth.
### 7.3 Vector slots (to populate + sign)
1 L0 cache hit · 2 L1 deterministic (no model) · 3 L1 IaC/tool-only ·
4 L2 retrieval-grounded · 5 L3 local model · 6 L3 with citations · 7 L4 wire
with claims · 8 mixed-span worst-counter (HwShunt+Estimator → Estimator) ·
9 gateway tool-receipt HMAC · 10 promoted-skill (L3-derived → L1 cost).
*(Reference repo currently ships 3 of 10 — the gating work for "standard" status.)*
---
## 8. Versioning & extensibility
`jc_receipt` is the schema major. Within `"1"`, only optional fields are added,
guarded so older verifiers ignore them. A breaking change increments to `"2"`;
verifiers reject unknown majors at step 1. The `L0`–`L4` tags and field semantics
in §2 are frozen for v1.
## 9. Security & honesty considerations
- A receipt proves **cost + provenance**, not output correctness. Pair with the
calibrated escalation gate so the *answer* is trustworthy, not just the bill.
- The signed core deliberately excludes advisory floats; do not infer that
`token_attributions` are attested.
- The worst-counter rule is the spec's spine: a system that quietly upgrades an
`Estimator` span to `HwShunt` to look precise is non-conformant and defeats the
entire purpose. Honest meters are the whole product.
- HMAC keys are per-gateway secrets; envelope keys are per-steward. Compromise of
one does not forge the other.
---
## 10. Worked example (JSON projection; signature over det-CBOR of the core)
```json
{
"jc_receipt": "1",
"id": "rcpt-000128",
"closed_at": "2026-05-31T18:04:22Z",
"input_hash": "b3:9f2c...e1",
"tier": "L1",
"joules_uj": 4200,
"energy_provenance": "HwShunt",
"steward": "did:keri:EJ9...",
"tools_touched": [
{ "tool_id": "iac:aws.ec2.describe", "joules_uj": 4200, "energy_provenance": "HwShunt" }
],
"claims": [],
"tool_receipts": [
{
"tool_id": "iac:aws.ec2.describe", "joules_uj": 4200,
"energy_provenance": "HwShunt", "minted_at": "2026-05-31T18:04:22Z",
"gateway_id": "gw-edge-7", "hmac_sha256_hex": "3f1a...c0"
}
]
}
```
A deterministic motif (an IaC describe call) closed the query at L1 for **4.2 mJ**,
HwShunt-honest, with a gateway HMAC proving the gateway — not a model — ran it.
No tokens burned; the receipt proves it.
```
```