From 7cd67a996f680c6825305b1a0df9197956f377f8 Mon Sep 17 00:00:00 2001 From: David Charlot Date: Wed, 14 Oct 2026 01:30:11 -0400 Subject: [PATCH 1/2] =?UTF-8?q?mesh=20=C2=A73.6:=20the=20receipt=20sealed?= =?UTF-8?q?=20the=20model=20by=20name,=20so=20the=20name=20was=20not=20a?= =?UTF-8?q?=20binding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SPEC §3.6 asks for this in as many words: "A receipt for a decision SHOULD therefore bind the weight digest and the precision ... a receipt that omits precision does not reproduce." The `mesh/1` wire had no field for either, so no conforming implementation could satisfy it. What the receipt sealed was obj.insert("recipe".into(), json!(self.recipe)); a caller-supplied `String`. Re-point that name at different weights, a different quantization or a different revision and every receipt sealed under it still verifies, while describing a decode some other model produced. The reproducibility §3.6 claims for a decision — one forward pass, no sampling, therefore checkable by re-execution — rested on nothing. `ModelRecipe` and `RecipeComponent` were already in mesh-schema for this, with components, references and merge weights. Three references in the whole tree, all three their own definitions: dead since they were written. So `weights_digest` and `precision` are sealed into the signed core, both OPTIONAL, both skipped when absent. Absence is the tag: it says the receipt binds the model by name alone, which is the weaker basis, and lets a verifier tell the two apart instead of assuming. `canonical_cbor` orders keys itself, so a receipt that reports neither seals to the bytes it sealed to before these fields existed — the published pack's vectors verify unchanged, which the pack test confirms. `weights_digest` is a `ContentId`, not a hex `String`. The field's presence is a claim to re-executability, so a 32-byte address makes that claim structural: a digest that is not one cannot be reported, and the validator I first wrote for it was deleted as unreachable. `precision` is a `String` because the vocabulary is open and this standard does not own it — an enum would refuse a precision that really ran, and a catch-all variant records less than the name. `ServeOutcome` loses `Copy`, which `precision` made impossible. Measured rather than assumed: removing the derive produced no error anywhere in the workspace beyond the new fields themselves. The real cost appeared only once the receipt read from the outcome — three reads hoisted above the move, and one clone where the receipt and the response both want it. The reference engine reports neither and says why: it answers from a table, so it has no weights to address. `reporting_model_identity` makes the fields exercisable end to end without changing the vectors, mirroring `reporting_delivered_fidelity`. The gguf engine also reports neither, with the honest reason — its weights are a real file, so a digest is obtainable once at load, and until it does that absent beats something cheaper that would look like a binding. The test asserts the third property, which is the one that matters: a receipt differing only in its identity seals to different bytes. Without that, fields could reach the wire and never reach what the signature covers — present to a reader, absent from the seal, which is the exact shape of a binding that is not one. A different digest gives a different core again. Co-Authored-By: Claude Opus 5 --- mesh/SPEC.md | 12 ++- mesh/mesh-rs/crates/mesh-core/src/lib.rs | 23 ++++- .../crates/mesh-engine-gguf/src/lib.rs | 14 +++ mesh/mesh-rs/crates/mesh-engine/src/lib.rs | 33 ++++++- mesh/mesh-rs/crates/mesh-receipt/src/lib.rs | 86 +++++++++++++++++++ mesh/mesh-rs/crates/mesh-schema/src/lib.rs | 41 ++++++++- 6 files changed, 203 insertions(+), 6 deletions(-) diff --git a/mesh/SPEC.md b/mesh/SPEC.md index 338b7e1c4..399354aac 100644 --- a/mesh/SPEC.md +++ b/mesh/SPEC.md @@ -225,6 +225,14 @@ decision from a warm one. > probabilities of a published decision model differ between `fp16` and `fp32` > even where the selected answer agrees, so a receipt that omits precision does > not reproduce. +> +> The fields are `weights_digest` and `precision` (§6.1), both OPTIONAL. A +> backend that can address its weights **SHOULD** report both; one that cannot +> omits them, and the receipt then binds the model by `recipe` alone — a name, +> which an operator can re-point at other weights while every receipt sealed +> under it goes on verifying. That is the weaker basis, and the absence of the +> fields is how a verifier can tell it apart from the stronger one, rather than +> having to assume. --- @@ -309,7 +317,9 @@ and nulls; booleans are encoded as `0|1` uints and absent options dropped. | `mesh_receipt` | `"mesh/1"` | | `request_id` | hex content id of the request | | `grant_id` | hex JCP grant id this inference debited (omitted if ungranted) | -| `backend`, `recipe` | the backend and composed model that served it | +| `backend`, `recipe` | the backend and composed model that served it — `recipe` is a name, read it with the two fields below | +| `weights_digest` | hex content address of the weights that answered (OPTIONAL — §3.6; absent means the receipt binds the model by name alone) | +| `precision` | the numeric precision the forward pass ran at, as the backend names it — `fp32`, `fp16`, `bf16`, `int8` (OPTIONAL — §3.6; paired with `weights_digest`, since neither reproduces alone) | | `route` | the router's one-line justification | | `tokens_in/out/accepted`, `cache_hit_tokens`, `spec_proposed/accepted` | the outcome counters | | `fidelity_floor` | the floor the request demanded (uint) | diff --git a/mesh/mesh-rs/crates/mesh-core/src/lib.rs b/mesh/mesh-rs/crates/mesh-core/src/lib.rs index 97b225f0b..bd51ed769 100644 --- a/mesh/mesh-rs/crates/mesh-core/src/lib.rs +++ b/mesh/mesh-rs/crates/mesh-core/src/lib.rs @@ -213,22 +213,37 @@ impl<'m> Mesh<'m> { let fidelity_met = basis >= req.fidelity_floor; // 4. Build, chain, and seal the receipt. + // + // Read off the outcome before it is moved into the receipt. `ServeOutcome` + // stopped being `Copy` when `precision` made it hold a `String`, so the + // fields the receipt lifts out of it are taken first. let nonce = nonce_from(&req.id()); + let weights_digest = served.outcome.weights_digest; + let precision = served.outcome.precision.clone(); + let delivered = served.outcome.delivered_fidelity; let receipt = MeshReceipt { request_id: req.id(), grant_id, backend: backend.clone(), recipe, + // Carried through from the party that ran the weights, absent where + // it did not say. No check is needed on the way past: `ContentId` is + // 32 bytes, so a digest that is not one cannot have been reported, + // and the field's presence therefore always carries its substance. + weights_digest, + precision, route_justification: route.justification.clone(), - outcome: served.outcome, + // Cloned, not moved: the response carries the same outcome, and + // `ServeOutcome` is six counters plus two options and a short name. + outcome: served.outcome.clone(), fidelity_floor: req.fidelity_floor, // The pair travels together: their purpose is to make a divergence // between promise and delivery checkable, and the advertisement // alone adds nothing a verifier does not already have from // `backend`. Both absent is the historical shape, which seals to // the bytes the published pack shipped with. - fidelity_advertised: served.outcome.delivered_fidelity.map(|_| advertised), - fidelity_delivered: served.outcome.delivered_fidelity, + fidelity_advertised: delivered.map(|_| advertised), + fidelity_delivered: delivered, fidelity_met, energy, sequence: 0, // stamped by the log @@ -382,6 +397,8 @@ mod tests { spec_accepted: 0, decision: None, delivered_fidelity: self.serves, + weights_digest: None, + precision: None, }, } } diff --git a/mesh/mesh-rs/crates/mesh-engine-gguf/src/lib.rs b/mesh/mesh-rs/crates/mesh-engine-gguf/src/lib.rs index 48f5bfac8..63d499e66 100644 --- a/mesh/mesh-rs/crates/mesh-engine-gguf/src/lib.rs +++ b/mesh/mesh-rs/crates/mesh-engine-gguf/src/lib.rs @@ -123,6 +123,13 @@ mod engine { // makes no claim: the gate falls back to the advertised // rating, which is where these engines already stood. delivered_fidelity: None, + // The weights are a real file, so a digest is obtainable — + // but once, at load, never per request. Until this engine does + // that it reports absent rather than something cheaper that + // would look like a binding: the field's presence is a claim to + // re-executability, and a partial digest is worse than none. + weights_digest: None, + precision: None, }; Served { tokens, outcome } } @@ -261,6 +268,13 @@ mod falcon { // makes no claim: the gate falls back to the advertised // rating, which is where these engines already stood. delivered_fidelity: None, + // The weights are a real file, so a digest is obtainable — + // but once, at load, never per request. Until this engine does + // that it reports absent rather than something cheaper that + // would look like a binding: the field's presence is a claim to + // re-executability, and a partial digest is worse than none. + weights_digest: None, + precision: None, }; Served { tokens, outcome } } diff --git a/mesh/mesh-rs/crates/mesh-engine/src/lib.rs b/mesh/mesh-rs/crates/mesh-engine/src/lib.rs index 994445816..a22caf540 100644 --- a/mesh/mesh-rs/crates/mesh-engine/src/lib.rs +++ b/mesh/mesh-rs/crates/mesh-engine/src/lib.rs @@ -13,7 +13,7 @@ #![forbid(unsafe_code)] -use mesh_schema::{BackendId, Fidelity, ServeOutcome, ServeRequest}; +use mesh_schema::{BackendId, ContentId, Fidelity, ServeOutcome, ServeRequest}; /// What an engine returns from one `serve`: the generated token ids and the /// serving counters. @@ -69,6 +69,14 @@ pub struct ReferenceEngine { /// it unconditionally would change every published vector and break every /// verifier that byte-compares them. report_delivered: bool, + /// What to report as the weights' address and the arithmetic's precision. + /// Both `None` by default, for the same reason as `report_delivered`: the + /// published `mesh/1` vectors are generated from this engine, and a receipt + /// that carries either field seals to different bytes. This engine answers + /// from a table, so it has no weights of its own to address — the switch + /// exists to make the fields exercisable end to end. + weights_digest: Option, + precision: Option, /// Seeds served before — a repeat counts as a cache hit. seen: std::collections::HashSet, } @@ -95,6 +103,8 @@ impl ReferenceEngine { work_per_token, spec_accept_permille: 0, report_delivered: false, + weights_digest: None, + precision: None, seen: std::collections::HashSet::new(), } } @@ -113,6 +123,21 @@ impl ReferenceEngine { self } + /// Report a weights address and a precision, as a backend serving real + /// weights would (SPEC §3.6). + /// + /// Off by default for the same reason as `reporting_delivered_fidelity`: + /// the published vectors are built from this engine, and either field + /// changes the sealed bytes. With it, a vector exercises the binding; a + /// vector built without it keeps the bytes the pack shipped with. A real + /// backend needs no switch — it digests its weights once at load and + /// reports what it ran. + pub fn reporting_model_identity(mut self, weights_digest: ContentId, precision: &str) -> Self { + self.weights_digest = Some(weights_digest); + self.precision = Some(precision.to_string()); + self + } + pub fn with_speculation(mut self, accept_permille: u32) -> Self { self.spec_accept_permille = accept_permille.min(1000); self @@ -201,6 +226,12 @@ impl ServingEngine for ReferenceEngine { spec_accepted, decision: None, delivered_fidelity: self.report_delivered.then_some(self.fidelity), + // This engine answers from a table: there are no weights to + // address and no arithmetic whose precision could be reported. + // A backend serving real weights digests them once at load and + // reports it here; absent is the honest answer for this one. + weights_digest: self.weights_digest, + precision: self.precision.clone(), }; Served { tokens, outcome } } diff --git a/mesh/mesh-rs/crates/mesh-receipt/src/lib.rs b/mesh/mesh-rs/crates/mesh-receipt/src/lib.rs index be07de0b2..c5a567d19 100644 --- a/mesh/mesh-rs/crates/mesh-receipt/src/lib.rs +++ b/mesh/mesh-rs/crates/mesh-receipt/src/lib.rs @@ -55,7 +55,26 @@ pub struct MeshReceipt { /// The backend that served the request. pub backend: BackendId, /// The recipe (composed model) that served it. + /// + /// A name. Read it with `weights_digest` and `precision`: alone it says + /// which label the operator gave the model, not which weights ran. pub recipe: String, + /// The content address of the weights `recipe` named, when the backend + /// reported one (SPEC §3.6). + /// + /// This is what makes `recipe` binding rather than advisory. Sealed into the + /// signed core, so re-pointing a name at different weights no longer leaves + /// past receipts verifying against a model that did not answer. `None` is + /// the historical shape, skipped on the wire — the receipt then binds the + /// model by name alone, and says so by the field's absence. + pub weights_digest: Option, + /// The numeric precision the forward pass ran at (SPEC §3.6), when the + /// backend reported one. + /// + /// Travels with `weights_digest` because neither reproduces alone: the same + /// weights at `fp16` and `fp32` give different probabilities even where the + /// selected answer agrees. `None` is the historical shape, skipped. + pub precision: Option, /// One-line, auditable reason the router chose this route. pub route_justification: String, /// Serving counters (the inputs to the J/accepted-token figure). @@ -140,6 +159,17 @@ impl MeshReceipt { } obj.insert("backend".into(), json!(self.backend.0)); obj.insert("recipe".into(), json!(self.recipe)); + // Emitted only when the backend reported them, so a receipt that binds + // the model by name alone seals to the bytes it sealed to before these + // fields existed — the published pack's vectors included. `canonical_cbor` + // orders keys itself (by length, then lexicographically), so where these + // are inserted here never reaches the wire. + if let Some(d) = self.weights_digest { + obj.insert("weights_digest".into(), json!(d.to_hex())); + } + if let Some(p) = &self.precision { + obj.insert("precision".into(), json!(p)); + } obj.insert("route".into(), json!(self.route_justification)); obj.insert("tokens_in".into(), json!(self.outcome.tokens_in)); obj.insert("tokens_out".into(), json!(self.outcome.tokens_out)); @@ -306,6 +336,8 @@ mod tests { grant_id: None, backend: BackendId::new("ref-f1"), recipe: "tiny-greedy".into(), + weights_digest: None, + precision: None, route_justification: "cheapest-sufficient".into(), outcome: outcome(accepted), fidelity_floor: Fidelity::F1, @@ -319,6 +351,60 @@ mod tests { } } + /// `recipe` is a name; `weights_digest` and `precision` are what make it + /// binding (SPEC §3.6). + /// + /// Three properties, and the third is the one that matters. Without it the + /// other two would pass on fields that reached the struct and never reached + /// the signed core — present on the wire, absent from what the signature + /// covers, which is exactly the shape of a binding that is not one. + #[test] + fn the_model_identity_is_sealed_not_merely_carried() { + let steward = "did:example:mesh-endpoint"; + let sk = SigningKey::from_bytes(&[9u8; 32]); + let digest = ContentId::of_bytes(b"the weights that answered"); + + // 1. Absent is the historical shape: it seals to the bytes it sealed to + // before these fields existed, which is what keeps the published + // pack's vectors verifying. + let bare = receipt(Energy::measured(1_000), 4); + assert!(bare.weights_digest.is_none() && bare.precision.is_none()); + let bare_wire = bare.to_jcr1_wire(steward); + assert!( + bare_wire.get("weights_digest").is_none() && bare_wire.get("precision").is_none(), + "an unreported identity must be absent from the wire, not null or empty" + ); + let bare_core = bare.signed_core(steward).unwrap(); + + // 2. Present, it reaches the wire as hex and as the backend's own name, + // and the receipt still seals and verifies. + let mut bound = receipt(Energy::measured(1_000), 4); + bound.weights_digest = Some(digest); + bound.precision = Some("fp16".into()); + let bound_wire = bound.to_jcr1_wire(steward); + assert_eq!(bound_wire["weights_digest"], json!(digest.to_hex())); + assert_eq!(bound_wire["precision"], json!("fp16")); + let env = bound.seal(&sk, steward).unwrap(); + assert!(bound + .verify_envelope(&env, steward, &sk.verifying_key().to_bytes()) + .is_ok()); + + // 3. The binding is in the signed core. Same receipt but for the + // identity ⇒ different sealed bytes, so a signature over one cannot + // be read as a signature over the other. + let bound_core = bound.signed_core(steward).unwrap(); + assert_ne!( + bare_core, bound_core, + "the identity must change the bytes the signature covers, or it binds nothing" + ); + + // And the digest is what distinguishes them, not just its presence: + // a different digest is a different core again. + let mut other = bound.clone(); + other.weights_digest = Some(ContentId::of_bytes(b"different weights")); + assert_ne!(bound_core, other.signed_core(steward).unwrap()); + } + /// A typed decision accepts no output tokens, so the token metric is /// undefined and the decision metric is the one that exists. /// diff --git a/mesh/mesh-rs/crates/mesh-schema/src/lib.rs b/mesh/mesh-rs/crates/mesh-schema/src/lib.rs index c1d73c50f..5f78ea802 100644 --- a/mesh/mesh-rs/crates/mesh-schema/src/lib.rs +++ b/mesh/mesh-rs/crates/mesh-schema/src/lib.rs @@ -272,7 +272,10 @@ pub struct RouteDecision { } /// Per-request serving counters — the inputs to the J/accepted-token metric. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +// `Copy` was dropped when `precision` arrived: an open-vocabulary name is a +// `String`, and nothing in the workspace relied on copying this — removing the +// derive produced no error anywhere outside the new fields themselves. +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct ServeOutcome { pub tokens_in: u32, pub tokens_out: u32, @@ -314,6 +317,42 @@ pub struct ServeOutcome { /// basis. #[serde(default, skip_serializing_if = "Option::is_none")] pub delivered_fidelity: Option, + /// The content address of the weights that answered, as the party that ran + /// them reports it (SPEC §3.6). + /// + /// `recipe` is a name, and a name is not a binding: re-point it at + /// different weights, a different quantization or a different revision and + /// every receipt sealed under it still verifies while describing a decode + /// some other model produced. A decision is one forward pass with no + /// sampling, so given the weights and the precision it is reproducible — + /// which is what makes a decision receipt checkable by re-execution rather + /// than merely signed. + /// + /// A [`ContentId`], not a string, so a digest that is not one cannot be + /// reported at all. The field's presence is the claim that the model is + /// bound by content; a 32-byte address keeps that claim structural instead + /// of something a validator has to catch later. + /// + /// `None` is the historical shape: skipped on the wire, and the receipt then + /// binds the model by name alone, which is the older and weaker basis. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub weights_digest: Option, + /// The numeric precision the forward pass ran at, named as the backend names + /// it (`fp32`, `fp16`, `bf16`, `int8`). + /// + /// A `String`, not an enum, because the vocabulary is open and this standard + /// does not own it. An enum would either refuse a precision that really ran + /// or need a catch-all variant, and a catch-all that discards the name + /// records less than the string does. + /// + /// Load-bearing for reproduction rather than descriptive: a published + /// decision model's probabilities differ between `fp16` and `fp32` even + /// where the selected answer agrees, so a receipt that omits the precision + /// does not reproduce (SPEC §3.6). Stored-weight quantization is the + /// digest's business; this is what the arithmetic ran in. `None` is the + /// historical shape. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub precision: Option, } /// Per-request decision counters — the inputs to the µJ/decision metric -- 2.54.0 (Apple Git-157) From ab5f508023b96dcb6a066d42fc6ab41338813406 Mon Sep 17 00:00:00 2001 From: David Charlot Date: Wed, 14 Oct 2026 01:33:23 -0400 Subject: [PATCH 2/2] mesh: the identity rides the whole path, and the keys it adds are exactly two MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The receipt-level test pins that the identity changes the sealed bytes. This one pins the path: two meshes differing only in whether the engine reports what ran, same recipe name on both, and the envelopes must differ. If the identity were carried but not sealed they would match, and a verifier comparing signatures could not tell a receipt bound to these weights from one bound to any others. It also asserts the added keys are *exactly* `precision` and `weights_digest`, which catches an extra key as well as a missing one — the same assertion shape the delivered-fidelity test uses, and the reason that test is worth copying. Falsified three ways, each caught at a different assertion: - projection removed → the wire assertion fires - digest projected as a constant → the hex assertion fires - wire keeps it, `signed_core_bytes` drops it → the signed-core assertion fires, which is the "carried but not sealed" case the test is named for and the third is the one a weaker test would have missed. No conformance vector is added, deliberately: #183 took the same decision for `delivered_fidelity`. The published pack's value is that its bytes do not move, and `reporting_model_identity` exists so the fields are exercisable without touching it. Co-Authored-By: Claude Opus 5 --- mesh/mesh-rs/crates/mesh-core/src/lib.rs | 49 ++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/mesh/mesh-rs/crates/mesh-core/src/lib.rs b/mesh/mesh-rs/crates/mesh-core/src/lib.rs index bd51ed769..22517d79d 100644 --- a/mesh/mesh-rs/crates/mesh-core/src/lib.rs +++ b/mesh/mesh-rs/crates/mesh-core/src/lib.rs @@ -534,4 +534,53 @@ mod tests { added.sort(); assert_eq!(added, vec!["fidelity_advertised", "fidelity_delivered"]); } + + /// The model identity rides the whole path — engine, receipt, envelope — + /// and is signed rather than advisory (SPEC §3.6). + /// + /// Same shape as the fidelity test above, for the same reason: two meshes + /// differing only in whether the engine reports what ran. If the identity + /// were carried but not sealed the envelopes would match, and a verifier + /// comparing signatures could not tell a receipt bound to these weights + /// from one bound to any others. + #[test] + fn the_model_identity_rides_the_path_and_is_signed() { + let meter = SteppingMeter::new("test-rapl", Provenance::HwShunt, 1_000_000); + let (sk, steward) = steward_key(); + let digest = ContentId::of_bytes(b"ssm-1b weights, revision 3"); + + let mut by_name = Mesh::new(&meter, sk.clone(), steward.clone()); + by_name.register( + BackendSpec::new("tiny", "ssm-1b", Fidelity::F1, 50_000), + Box::new(ReferenceEngine::new("tiny", "ssm-1b", Fidelity::F1, 50_000, 256)), + ); + let mut bound = Mesh::new(&meter, sk, steward); + bound.register( + BackendSpec::new("tiny", "ssm-1b", Fidelity::F1, 50_000), + Box::new( + ReferenceEngine::new("tiny", "ssm-1b", Fidelity::F1, 50_000, 256) + .reporting_model_identity(digest, "fp16"), + ), + ); + + let req = request(8, Fidelity::F1, 10_000_000); + let a = by_name.serve(&req, &[], None).expect("serve"); + let b = bound.serve(&req, &[], None).expect("serve"); + + // Both name the same recipe — which is the point: the name alone does + // not distinguish them, and before these fields nothing else did. + assert_eq!(a.receipt.recipe, b.receipt.recipe); + assert!(a.receipt.weights_digest.is_none()); + assert_eq!(b.receipt.weights_digest, Some(digest)); + assert_eq!(b.receipt.precision.as_deref(), Some("fp16")); + + assert_ne!(a.envelope, b.envelope, "the identity must be signed, not advisory"); + + let (wa, wb) = (a.receipt.to_jcr1_wire("s"), b.receipt.to_jcr1_wire("s")); + assert_eq!(wb["weights_digest"], serde_json::json!(digest.to_hex())); + let (oa, ob) = (wa.as_object().unwrap(), wb.as_object().unwrap()); + let mut added: Vec<&String> = ob.keys().filter(|k| !oa.contains_key(*k)).collect(); + added.sort(); + assert_eq!(added, vec!["precision", "weights_digest"]); + } } -- 2.54.0 (Apple Git-157)