open-standards/joule-code/CHANGELOG.md
Transaction Science 4a8a6e340b
Some checks failed
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 2m11s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 18s
joule-code conformance gate / joule-moq-relay, every target (pull_request) Successful in 3m31s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 3m57s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 5m37s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Failing after 2m37s
joule-code CHANGELOG: joule-moq-relay 0.1.0
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 00:42:30 -04:00

15 KiB
Raw Permalink Blame History

Changelog

All notable changes to the Joule Code standard are recorded here. The format follows the spirit of Keep a Changelog; the standard versions independently of its siblings (tags joule-code-vX.Y.Z).

[Unreleased]

Added

  • §5.1 Grant-gated consultation (OPTIONAL) — the JCP binding: tier as granted action (l0…l4), energy class as chained budget debit, denial as fallthrough, every consultation sealed as a chained JCR-1 receipt. Reference binding: the joule-code-jcp crate (JcpGate::wrap over any Stage); no l4 in the chain → no remote frontier agent, structurally. Conformance gains class C5 with a published vector file (gated_cascade_vectors.json, fixed fixture identities, deterministic end to end) run by the harness against the real jcp runtime alongside C1–C4.

  • joule-code-cascade ships a reference L0 (Cache) stage: a content-addressed cache of resolved coding operations keyed by (prior, intent). A hit closes the request at L0 (picojoule class) without consulting any higher tier — the first concrete cascade stage, making stop-at-first-close demonstrable in the reference library. A third party may use it or substitute its own.

  • joule-code-cascade ships a generic FnStage: any tier (L1 Lawful, L2 Embed, L3 Model) can be expressed by supplying a resolver closure, so a consumer binds the JouleClaw agent schema — or any other runtime/model — to the cascade without this crate depending on it. (A test wires an L3 model stage behind an empty L0 and confirms it closes at L3.)

  • examples/l3-jouleclaw/ — a consumer binding showing JouleClaw's L3 (Model) tier wired into the cascade: a jouleclaw_llm_cheap::LlmBackend behind a FnStage(L3) that generates a CodingOp + joules-class receipt. Three backends share the slot: EchoBackend (model-free, for the unit tests); JouleclawRuntimeBackend, which runs a GGUF model in-process on JouleClaw's own from-scratch tensor runtime (jouleclaw-runtime over jouleclaw-loader-gguf and the reference/Apple kernel backends — no llama.cpp, no FFI); and LlamaCompletionBackend, which shells out to llama.cpp. The run binary generates with a local model (in-process runtime by default; JOULE_CODE_L3_ENGINE=llama for the shell-out), content-addresses the result as a CodingOp, and meters host energy over the inference for the receipt (honest provenance: HwShunt on RAPL/NVML, Estimator on Apple). Verified live: the llama.cpp path against a local 350M GGUF, and the in-process from-scratch runtime against ternary-bonsai-1.7b-q2_0 (qwen3, Q2_0) — real text, a real op_id, the cascade closing at L3 (tests/runtime_l3.rs, opt-in via --ignored). Standalone workspace, so the standard's lib stays free of a JouleClaw dependency; jouleclaw_dispatch::ModelBank selects the cheapest capable backend in production.

  • joule-code-collab (Research) lands the federated-collaboration interface (Collab trait: submit / committed / membership), a single-node reference cluster (LocalCluster), and the BFT consensus core (LockstepCluster): a multi-node lockstep deterministic simulation with BFT supermajority commit — an op commits only when ≥2f+1 of n=3f+1 honest nodes independently validate it, and committed ops are op_id-ordered so every honest node derives a byte-identical log. Tested: commits with honest agreement, tolerates f Byzantine nodes (safety), loses liveness only beyond f, rejects non-members despite quorum, deterministic round ordering. It also adds protected — FROST threshold authorization of protected operations: a t-of-n FROST (Ed25519) aggregate signature over an op's op_id, verifiable against the cluster's single group key (no member holds the whole key; fewer than t cannot authorize). Tested: a 2-of-3 threshold authorizes and binds exactly one op_id; below threshold cannot. And MLS group keying (RFC 9420, via openmls, tests/mls_keying.rs): the cluster members form an MLS group and derive a shared group key with no central server — verified that members agree on the exported secret, that adding a member advances the epoch, and that a member an epoch behind derives a different (forward-secret) key. (SPEC §7, spec/collab.md.)

  • examples/transport-moqt/ — the CommunicationOS (MoQT) transport binding, the last of the four federation layers. A consumer binding that carries a collaboration's committed (consensus-ordered) coding operations over inv-moq — CommunicationOS's MoQT (Media over QUIC Transport) protocol with its JWP receipt extension. The end-to-end test wires the whole stack in-process: LockstepCluster commits ops → each is published as a MoQT object → every object carries a real Ed25519/BLAKE3 signed receipt → a subscriber receives all ops, verifies the receipts against the publisher key, and recovers the original SignedOp intact; a second test shows a receipt-requiring subscriber rejecting an untrusted publisher's group (ownerless authenticity). Driven through inv-moq's MockFabric (real signatures + BLAKE3 merkle receipts, no QUIC; the quic feature is the real-wire path behind the same trait surface). Standalone workspace, so the reference library stays free of a joulesperbit dependency. The four federation layers — consensus, threshold authorization, group keying, transport — are now all implemented and tested. (SPEC §7, spec/collab.md.)

Changed

  • §6 / spec/receipt.md §2 — a vendor counter is a hardware reading only while it is demonstrably live. A reading from a vendor energy counter (RAPL, NVML, Apple SMC/IOReport) MAY be tagged HwShunt, and supply measured_uj, only while the counter has been observed advancing under load for the domain the figure claims (for IOReport: the CPU energy rail itself, in at least 5 sample intervals, at least 4 of every 5, over at least 300 ms of CPU burn). A counter that is present but not live — frozen, or republished in lumps — MUST NOT produce either; the reading falls back to the estimator. A figure MUST NOT claim coverage of a domain its counter does not read. No wire or conformance-vector change.

Changed — joule-moq 0.3.0: draft-ietf-moq-transport-22

joule-moq speaks draft-ietf-moq-transport-22 (1 October 2026), negotiated by the ALPN moqt-22, in place of its draft-08-style codec. No byte of the old wire form survives: -22 changed the varint, the control streams, every message and the object framing. The crate's version moves to 0.3.0 because its public API changes:

  • draft22: the codec of -22, a module of its own selected through draft::Draft, so another draft can be added beside it. Every control message and parameter, Key-Value-Pairs, Properties, Setup Options, the Authorization Token, Request IDs and request-stream rules, subgroup streams, datagrams, fetch streams and padding, and the status and error codes. Every refusal is a typed WireError carrying the draft's consequence; MoqError::Wire wraps it.
  • MOQT_DRAFT_VERSION is 0xff000016. transport_quic::ALPN_MOQT is moqt-22.
  • messages re-exports -22's control messages. SetupMessage, AnnounceMessage, SubscribeMessage, SubscribeFilter and the draft-08 ControlMessage are gone. SUBSCRIBE's max_mrl_layer is an OBJECT_PROPERTY_FILTER on the MRL_LAYER property (messages::mrl_cap_filter); require_receipts is the subscriber's own policy, met by subscribing to a track's receipts track.
  • carriage (new): the crate's objects ride -22 subgroup objects, with the object receipt, the WAI_RECEIPT property of WAI extensions/moq-streaming-format.md §5 and the layer fields as properties in an application range; a group's receipts ride its receipts track (§4.1). ReceiptBundle moved here from mock, which re-exports it.
  • session: the draft by ALPN (MoqSession::select_alpn); SETUP carries options and no version or role, and build_setup takes no label; Request IDs by end.
  • transport_quic: a pair of unidirectional control streams opened by SETUP, a request stream per request, subgroup, fetch and padding streams, and datagrams. ControlStream, write_object_to_uni, read_object_from_uni, write_group_receipt_to_uni and read_group_receipt_from_uni are replaced by QuicSession::{open_control, accept_control, open_request, accept_request, open_subgroup, open_fetch, accept_data}, send_object, send_receipt_bundle and DataReceiver.
  • wire::encode_varint and decode_varint are -22's integer; every u64 encodes.
  • mock: objects, receipt bundles and requests cross the -22 codec; the relay reads an object's delivery timeout from its OBJECT_DELIVERY_TIMEOUT property.
  • tests/vectors/draft-22/: a corpus of -22 vectors, good and negative, generated by tests/draft22_vectors.rs, and an independent decoder that checks it in CI.

Added — joule-moq-relay 0.1.0: a draft-ietf-moq-transport-22 relay

A new crate, library and binary, on joule-moq's codec. Publishers send it tracks, subscribers ask it for them, and it fans each object out to every subscription in the order -22's priorities give (§5.1.2), dropping what passes a subscriber's delivery timeout at this hop (§5.2). Whatever it drops, the object's receipts still reach the subscriber: a track's receipts track is never given a delivery timeout.

  • Three transports, one draft: native QUIC (ALPN moqt-22) and WebTransport over HTTP/3 (WT-Available-Protocols / WT-Protocol, draft-ietf-webtrans-http3-16) on one UDP endpoint, and a WebSocket fallback that carries draft-ietf-webtrans-http2-15 capsules, one per binary message, under the subprotocol moqt-22. The fallback is a non-normative binding, written out in the crate's README with example bytes.
  • The relay protocol: session setup; namespace publish and subscribe routing; PUBLISH; SUBSCRIBE served from an upstream the relay holds or opens; FETCH and fills from its cache or forwarded upstream; TRACK_STATUS; REQUEST_UPDATE; GOAWAY on shutdown; Request-ID parity and duplicate checks; the authorization-token alias cache; several publishers of one track, deduplicated by (group, subgroup).
  • What the cache covers is worked out from the groups it holds whole, each time it is asked, so a group whose streams finish after a later group's is still served from the cache once it is whole; a group never seen is a gap it does not answer across.
  • A signed session summary per session. The relay's own energy is the unmetered marker, since no meter backs it.
  • Not yet: SUBSCRIBE_TRACKS and Range Filters, WebTransport over HTTP/2, 0-RTT, the relay moving its own upstream subscriptions, and finding a receipts track from the catalog rather than by its name; the README lists each.

Changed — joule-moq 0.2.0: WAI's labelled JWP forms, and a strict verifier

joule-moq reads and writes the forms of WAI's extensions/jwp-receipts.md that sign an object's energy figure, its class and its origin, and a group total's class. Every receipt in the legacy forms keeps its bytes and verifies as before; a verifier that predates the labelled forms refuses them. The crate's version moves to 0.2.0 because its public API changes:

  • ObjectReceipt gains energy_class: Option<Vec<u8>> (the class in WAI energy-measurement §2.7's signed encoding). Present, the object's leaf is BLAKE3("moq-jwp:merkle-leaf\x02" ‖ content_hash ‖ u64_be(joules_micro) ‖ class ‖ u32_be(len(origin)) ‖ origin); absent, the legacy leaf. New: ObjectReceipt::leaf, ObjectReceipt::check_label, ReceiptBuilder::add_labelled_object, ReceiptBuilder::class_for.
  • GroupReceipt gains energy_class, energy_coverage and parent_receipt_hash. With a class, the group is signed under moq-jwp:group-sign\x02, which covers the class, the coverage and the parent link. energy_coverage is written only when it is non-zero, and a legacy group carrying a non-zero coverage is refused: its signature does not cover one. New: ReceiptBuilder::finalize_labelled, GroupReceipt::link_hash and compose_group_class, the rule a labelled group's class is composed by.
  • ReceiptVerifier::verify_group returns a VerifiedGroup (the root with the group's leaf count and coordinates) instead of the bare root, and verify_object and verify_payload take &VerifiedGroup. An object is accepted only at the position its proof walks: its coordinates are the group's, object_id is its leaf index, the proof has the group's depth and its direction bits are the index's.
  • MerkleProof::verify is replaced by MerkleProof::verify_leaf, which takes the leaf in its form and the group's leaf count, and MerkleProof::walks_its_position.
  • verify_group holds the key and the signature to WAI jwp-receipts §3.1 step 3's rule: the publisher key must be the canonical encoding of a point not of small order (a y coordinate ≥ p is refused), S must be below L (checked by the crate as well as by verify_strict), R must not be of small order, and the cofactorless equation must hold.
  • JWP_MOQ_EXTENSION_VERSION is 2.
  • tests/wai_jwp_corpus.rs runs every leaf, group and object vector of wai/jwp-conformance/vectors.json through the crate.

External JWP implementations need the same update to verify labelled leaves and groups, and to refuse what this verifier refuses.

[0.2.0] — draft

Changed

  • Receipts now seal as JCR-1 envelopes. The receipt's signed form is the family-shared JCR-1 wire format — canonical det-CBOR (RFC 8949 §4.2.1, length-first key order) inside a COSE_Sign1 envelope (RFC 9052, EdDSA alg = -8), as the joule-code/1 profile. This replaces v0.1's bespoke estimated_uj ‖ sequence ‖ nonce preimage. A Joule Code receipt now verifies under the family's existing JCR-1 tooling and its cross-language (Python/Go) reference verifiers. (SPEC §6.1.)
  • joule-code-receipt gains a jcr1 module (canonical CBOR + COSE_Sign1, vendored from the JCR-1 reference, no JouleClaw dependency) and Receipt::to_jcr1_wire / seal / verify_envelope; the bespoke sign/verify are removed.
  • Conformance C3 vectors regenerated as JCR-1 COSE_Sign1 envelopes; the harness verifies the envelope and that its payload is the receipt's canonical core. A byte-equality test pins the canonical encoder against a published JCR-1 vector.

[0.1.0] — draft

Initial draft standard.

Added

  • SPEC.md — normative specification: the cascade requirement (L0–L4, stop-at-first-close), the receipt requirement (measured + estimated, Provenance), and the no-capture requirement.
  • The coding-operation cargo: content-addressed (BLAKE3-256), signed (Ed25519, prior-state-bound) transitions over a source tree; deterministic CBOR canonical form.
  • The receipt schema in Joule Wire Protocol form, energy-signed.
  • The cascade interface and the agent-schema binding to JouleClaw; the L4 binding to remote frontier agents over ACP.
  • The federated-collaboration binding, specified at interface level and classified Research.
  • joule-code-rs/ reference library: joule-code-schema, joule-code-cargo, joule-code-receipt, joule-code-cascade, joule-code-conformance.
  • conformance/v1/ reference vector pack (C1–C4), internally consistent pending regeneration against the reference library.