open-standards/mesh/SPEC.md
Transaction Science 4a7ca45718
All checks were successful
wai-quantum WebGPU member / does this change touch it (pull_request) Successful in 6s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 14s
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (pull_request) Successful in 49s
standards workspaces build + test / mesh-rs (mesh/mesh-rs, -p mesh-engine-gguf -p mesh-openai --features mesh-engine-gguf/gguf,mesh-openai/server, mesh-rs, linux-arm64) (pull_request) Successful in 54s
standards workspaces build + test / joulehook-rs (joulehook/joulehook-rs, joulehook-rs, linux) (pull_request) Successful in 1m13s
wai-quantum WebGPU member / member · software Vulkan (linux) (pull_request) Successful in 1m49s
standards workspaces build + test / jouleclaw-rs (-nodebug, jouleclaw/jouleclaw-rs, -p jouleclaw-energy --features rapl,nvml, jouleclaw-rs, linux-arm64, -C target-feature=+fp16) (pull_request) Successful in 2m56s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 3m30s
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness (pull_request) Successful in 3m24s
energy meters on real hardware / sink decode table · per-task energy and the GPU rail on macOS (pull_request) Successful in 2m16s
wai-quantum WebGPU member / member · Metal (macos) (pull_request) Successful in 2m46s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 1m42s
wai-quantum WebGPU member / member · integrated GPU, Vulkan (framework-128gb) (pull_request) Successful in 7m42s
wai-quantum WebGPU member / member · browser WebGPU (macos) (pull_request) Successful in 3m52s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 6m56s
wai-quantum WebGPU member / member · DX12 and Vulkan (cbio-windows) (pull_request) Successful in 11m48s
energy meters on real hardware / RAPL and NVML on jetson-hub (pull_request) Successful in 35s
energy meters on real hardware / sink decode table · RAPL on jetson-hub (NVML surveyed) (pull_request) Successful in 2m23s
wai-quantum WebGPU member / member · GPU, Vulkan (thor) (pull_request) Successful in 24m54s
wai-quantum WebGPU member / member · GPU, Vulkan (jetson-hub) (pull_request) Successful in 17m32s
mesh-meter: a span shorter than its counter's minimum window is an estimate
A counter declares the smallest window over which its readings are not noise
(min_window_ns: about 10 ms for RAPL and IOReport, 100 ms for NVML; 2 s for a
Mac's SMC power integral). Across a shorter span its delta is whatever its
updates and quantization gave, often zero or a whole update period, yet mesh
graded it at the counter's grade: a few-millisecond request could be sealed
HwShunt at 0 µJ, or ModelBased at a power reading's extrapolation.

- SPEC 3.3: a span shorter than the minimum window of the counter that would
  grade it MUST NOT be graded HwShunt or ModelBased from that counter; it is
  an estimate of the request, and SHOULD be billed by an estimator that
  resolves the span's own length, so a short request is never billed zero.
- HwMeter opens its own window (Meter::open_span), which Session uses: it
  times the span, and one shorter than the backend's floor
  (HwMeter::min_window) is billed this crate's EstimatorMeter over the span's
  own length, graded Estimator with no measured_uj, as a span across a failed
  read is. Past the floor the counter's figure stands, graded as before.
  min_window is zero where the bridge already bills by its estimator.
- ProcessMeter: where ProcessSpanMeter falls back to the host meter, a span
  shorter than the host meter's floor is billed the same way. Spans its own
  CPU and GPU counters metered are not the host meter's and are unaffected.
- jouleclaw-energy: ProcessSpanMeter::fallback_min_window, the active fallback
  meter's min_window_ns, so a caller can apply the rule. Additive.

Each span is timed from after its opening read to before its closing one, a
little shorter than the counter's own window, so a span at the floor is judged
short rather than long.

Tests: under a 50 ms floor a 20 ms span is Estimator at the estimator's figure
for 20 ms, and a 60 ms span keeps the counter's figure and grade (HwShunt and
ModelBased); the bridge's minimum window is its counter's until a failed read,
then zero; a host-meter fallback span under the host floor is an estimate,
past it the host figure stands, and a span this process's own counters
metered keeps its grade. Linux x86_64: mesh-meter (default, hw-meter-rapl),
mesh-core (hw-meter-rapl) and mesh-conformance pass; clippy clean. macOS
(M3 Ultra): mesh-meter and mesh-core with hw-meter-apple pass, including a
real in-process serve sealed HwShunt; clippy clean for mesh-meter and
jouleclaw-energy.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-04 21:29:44 -05:00

44 KiB
Raw Permalink Blame History

Mesh — Energy-Accounted Inference Serving & Routing

Version: 0.1.0-draft.1 · Status: Draft · Profile: mesh/1 Specification text: CC-BY-4.0 · Reference code (mesh-rs/): Apache-2.0

Mesh is the open standard for serving and routing inference as a metered transaction. A request declares a joule ceiling and a fidelity floor; the mesh routes it to the cheapest-sufficient model on the cheapest-sufficient backend, serves it under an energy meter, and seals a receipt whose headline figure is microjoules per accepted output token — measured at the meter, not estimated from a price list.

Mesh is one of the Transaction Science open standards. Transaction Science is the steward, not the proprietor: the wire format and the right to fork are public.


§1 Why Mesh exists

The serving shelf reports two numbers: dollars per token and tokens per second. Neither tells an operator, an auditor, or a regulator what an inference cost — in the one unit that is physical, comparable across hardware, and impossible to inflate: the joule. Mesh makes that number first-class. Every served inference produces a signed receipt carrying the route that was taken and why, the recipe that served it, the honest provenance of the energy figure, and the joules per accepted token.

Mesh does not replace the runtime that executes one inference, nor the substrate that supplies the compute. It is the front door in front of many models and many backends: the layer that decides where a request goes, meters what it spent, and proves it (§9).


§2 Roles and the serve lifecycle

A Mesh deployment has four roles:

  • Caller — issues a ServeRequest and, optionally, presents a JCP grant it issued to the steward, signed for that request (§2.1).
  • Router — selects a backend (§4).
  • Engine — a backend that serves the request (§5).
  • Steward — the endpoint that meters the span (§5/§6) and seals the receipt (§6). The steward identity is the COSE kid and signs the receipt log.

One serve proceeds in four phases, in order:

  1. Route. The router selects the cheapest admissible backend (§4). If none is admissible, the serve fails with the router's recorded reason. When the request rides on a grant whose chain carries a ModelIn caveat (JCP §4.9), a backend whose model the grant does not permit is inadmissible, and so is a backend that names no model.
  2. Authorise. If the request rides on a grant, the steward first verifies the presentation (§2.1), then decides the grant as a JCP enforcement point, and as the grant's holder, in JCP's two-phase metered path over its chain (JCP §4.2, §7, §4.9). The frame it decides over names the action infer on the request's content id, the request's prompt_hash as payload, and, as model, the content address of the weights the chosen backend runs, when the backend names one. Every check of that decision applies, on every link of the chain: signature, attenuation, expiry, revocation, and every cost-independent caveat. The steward MUST also refuse the serve when what the grant has left (the least any link has left, its budget less what earlier serves on it were debited) is below the request's joule ceiling. The decision is recorded in the steward's journal (§2.2) before the engine runs. A failure at any step aborts the serve before any work is done.
  3. Meter. The steward opens an energy window, calls the engine, and closes the window. Energy MUST be metered around the engine; an engine MUST NOT self-report energy. With a grant, the steward then debits the metered joules against it (JCP's second phase), where a cost-dependent caveat such as MaxCostUj binds. The work has run, so the joules are debited even when the debit breaches a caveat, and the breach is recorded on the receipt (grant_breach, §6.1). An engine that names its weights before serving MUST report the same weights after; the steward MUST NOT seal a serve whose engine reports others, since they are not the weights the grant was decided over.
  4. Seal. The steward builds the receipt (§6), stamps it into its hash-chained log, and seals it as a JCR-1 envelope.

§2.1 Presenting a grant

A grant is a signed public object, so holding one MUST NOT be enough to spend it. A caller presents a grant as:

Presentation {
  chain:     [signed-grant…]   ; root first, leaf last (JCP §4.2); a chain of one for a direct grant
  caller:    did:key
  nonce:     bytes(16)
  signature: bytes(64)         ; Ed25519 by `caller` over canonical_cbor({request_id, grant_id, caller, nonce})
}

where request_id is the request's content id and grant_id the leaf's. The steward MUST refuse a presentation whose caller is not the leaf's issuer, or whose signature does not verify for the request being served. The leaf's issuer is the party that authorised the steward to spend on its behalf, so it is the only party that may.

The leaf's subject is the steward: the steward decides the chain as its holder, under the identity of the key it seals receipts with, and MUST refuse a grant held by another steward. One grant therefore spends at one steward. A budget meant for several is sharded by its holder into sibling children, one held by each steward, whose budgets sum to no more than the parent's (JCP §4.5); each steward enforces only its own, and the total stays bounded without the stewards coordinating.

A presentation spends once at a steward. The steward MUST refuse one it has already spent, identified by the leaf and the nonce, so a captured presentation cannot be replayed there. A caller that wants the same request served again signs a new presentation with a new nonce, and that serve is debited too.

§2.2 The decision journal

A grant's spend at a steward is state, and a steward that restarts with its spend in memory would give every grant its budget back (JCP §4.11). A steward that can restart MUST therefore keep a durable journal of its grant decisions:

  • An Authorised entry, holding the presentation, the request, the model, the backend and the decision time, MUST be written before the engine runs. A serve whose authorisation cannot be recorded is refused.
  • A Debited entry, holding the metered energy, is written after the debit.
  • A Sealed entry, holding a receipt's sequence and its envelope's content id, MUST be written for every receipt, granted or not, before the receipt is handed out. A receipt that cannot be recorded is not handed out, and the receipt chain does not advance past it.

Each entry is a record of one chain: {seq, prev, entry}, where seq counts records from 0 and prev is the BLAKE3 hash of the previous record's encoding.

On restart the steward MUST re-establish the spend before serving by replaying the journal through the gate: each authorisation is verified and decided again, at its recorded time, and each debit applied to the serve it belongs to. Records that do not form one chain, a replay the gate refuses, a debit with no authorisation, a presentation spent twice, or a receipt out of sequence means the journal is not one the steward produced, and the steward MUST NOT serve from it. Replayed presentations count as spent, and the receipt chain continues from the last Sealed entry (§6.3).

One steward per journal. A steward MUST take exclusive access to the journal when it opens it, and MUST fail to start rather than serve where another holds it — or where the platform cannot provide exclusivity at all.

The chain above detects a second writer; it does not prevent one. Two stewards sharing a journal each replay it, each re-establish the same spend and each serve against it, so the grant is overspent while both are running. Their records then collide on seq, the chain no longer forms, and the rule above requires the steward not to serve from the journal at all — so the detection arrives after the overspend and costs the steward its ability to restart. An exclusive claim the operating system releases when the holding process dies is preferable to one the steward must clean up, because a crash then leaves nothing to recover.

An authorisation with no debit is a serve that was in flight when the steward stopped, and whether the engine finished is not knowable from the journal. The steward MUST NOT retry it or report it as completed or failed. It stays debited, at its request's joule ceiling, which is what its authorisation admitted, and is surfaced as unresolved, naming the caller and the backend that held it, for an operator to settle.

A cut journal. Because the records form one chain, a record removed from the middle, edited or moved fails the replay. Records cut from the end leave a chain that is whole, and nothing on the steward's own storage can show they existed; but every receipt the steward handed out was recorded first, so the receipts in callers' hands can. Any party may show the steward a receipt it sealed, with its envelope. If the steward's journal does not hold that receipt, records were cut after it left, the spend the steward replayed is less than it signed for, and the steward MUST refuse every grant from then on, until an operator restores the journal. A caller SHOULD show its latest receipt from a steward after the steward restarts. A cut that removes only a serve that produced no receipt, one that was in flight, leaves no evidence outside the steward.

The reference journal is an append-only file of one JSON record per line, each synced before the append returns. A final line with no newline is an append cut short and is dropped: an authorisation never recorded never started its serve, a debit never recorded leaves its serve unresolved, and a seal never recorded was never handed out.

§2.3 Grants over the OpenAI-compatible binding (informative)

The reference binding (mesh-openai) serves /v1/chat/completions and /v1/responses. A request rides on a grant by carrying its presentation (§2.1) in the body field jcp_presentation. The presentation is signed for a request id, so a caller must be able to compute the request a body is served as: a granted request is served as exactly what the binding derives from the body alone, with the prompt-token figure left as the body's estimate rather than replaced by the server's tokenizer count, and it MUST name its own metadata.joule_ceiling_uj, since a server default would otherwise enter the id unseen. The binding exports that derivation as granted_serve_request.

GET /v1/mesh/steward returns the steward's did:key, the subject a grant must name. POST /v1/mesh/witness, with a body of the receipt and envelope_hex a response's usage.energy carried, answers 200 when the journal holds that receipt and 409 when it does not, after which grants are refused (§2.2). A refused grant answers 401 when the caller is not its issuer, 403 when the steward refuses the grant, and 503 once the steward has stopped honouring grants.


§3 The request and the energy record

§3.1 ServeRequest

A request is content-addressed. Its identity request_id = ContentId.of(request) is the resource a JCP capability authorises and the request_id on the receipt.

Field Type Meaning
prompt_hash ContentId (BLAKE3) content address of the prompt bytes — never the prompt text
prompt_tokens u32 declared input token count (for the estimate)
max_tokens u32 output token ceiling
fidelity_floor Fidelity minimum acceptable fidelity (§3.2)
joule_ceiling_uj u64 hard energy budget, microjoules

A conforming request MUST carry both a fidelity_floor and a joule_ceiling_uj. The pair is the contract: give me an answer at least this good, for at most this much energy.

A request MAY additionally carry a decision (§3.6), which says it is asking for a bounded typed decision rather than generated text. Its absence is a generation, and is omitted from the canonical form — so the request_id of every request written before this field existed is unchanged.

§3.2 Fidelity

Fidelity is an ordered scale F0 … F4 — F0 the coarsest sufficient answer (cache / lawful / tiny model), F4 a frontier-grade response. Ordering is normative: a backend is admissible only when its level ≥ the request's floor (§4).

§3.2.1 Advertised and delivered

A Mesh request carries two bounds, and until this revision only one of them was held to a standard of honesty. Energy has a provenance model whose coupling MUST be enforced before sealing (§3.3) — the rule that keeps "measured" from meaning "estimated". Fidelity had none: a backend's level is an advertisement, read out of the route table before the engine runs, and fidelity_met was derived from it and never reconciled with what came back. A backend admitted at F3 that degraded at serve time still sealed fidelity_met: 1 against an F3 floor.

Fidelity therefore has two figures, and they are different claims:

figure who says it when
advertised — ServingEngine::fidelity the route table before the work; the basis the router admits on (§4)
delivered — ServeOutcome.delivered_fidelity the backend that served after the work

The coupling is normative:

  • Where the backend reports a delivered level, fidelity_met MUST be derived from it, not from the advertisement.
  • A backend MUST NOT report a delivered level above its advertised rating. The router admitted it on the rating; it cannot raise that basis after the fact. An implementation MUST refuse such a serve rather than clamp the figure — clamping hides a misconfigured or dishonest engine behind a receipt that looks ordinary.
  • Where the backend reports nothing, fidelity_met rests on the advertisement. This is the older and weaker basis and is retained, because an engine that cannot report what it served is still conformant. A consumer that requires the stronger basis MUST check for fidelity_delivered rather than reading fidelity_met alone.
  • The receipt MUST carry fidelity_advertised and fidelity_delivered together or carry neither. Their purpose is the comparison; the advertisement alone adds nothing a verifier does not already have from backend. Carrying neither is what a receipt sealed before these fields existed means, so such a receipt is unchanged and mesh/1 stays byte-stable.

What this does not claim. A delivered figure is the account of the party that did the work. It is better grounded than a table entry written before the work, and it is not an independent assessment of the output — Mesh does not score responses. What the pair buys is that a divergence between promise and delivery is visible in the receipt and attributable to a named backend, where before it was invisible.

§3.3 Energy and provenance

Energy is a two-part integer-microjoule record with an honest provenance tag:

Energy { measured_uj: Option<u64>, estimated_uj: u64, provenance: Provenance }
Provenance ∈ { HwShunt, ModelBased, Estimator }

The coupling is normative and MUST be enforced before a receipt is sealed:

  • HwShunt ⇒ measured_uj is present (a hardware reading exists).
  • Estimator ⇒ measured_uj is absent (no measurement is implied).
  • ModelBased ⇒ measured_uj MAY be absent.

A receipt that violates the coupling MUST NOT be sealable. This is the rule that keeps "measured" honest: a deployment with no hardware meter reports Estimator and says so, rather than dressing an estimate up as a measurement.

What the figure covers, and what it does not. The figure is the served span: the meter session opens immediately before the engine runs and closes when it returns. Routing (§2) and grant authorisation precede it and are not included, and a receipt therefore understates the energy of handling the request by whatever those steps cost.

An implementation MUST NOT present a mesh receipt's energy as the energy of the request. Where a router or an authorisation path itself runs a model — a classifier or an LLM deciding the route — that omission stops being negligible, and the shortfall lands in the direction this family treats as unsafe: under-reporting is the unsafe direction at a ceiling, because the grant is debited the served span only and admits further work on the strength of it (jouleclaw/SPEC.md §1.3; jcp/SPEC.md §5 — named by file, because mesh has a §5 of its own and no §1.3). An implementation that meters a model-running router SHOULD seal that span as its own field rather than folding it into this one, so the two remain separable by a verifier.

This boundary is an ordering invariant in the serving path, not an observable property of the receipt: the meter is snapshot-driven and routing takes no meter samples, so no test pins it today. Putting the router behind a trait, as the engine already is, is what would let one.

HwShunt covers a shunt and a vendor energy counter (RAPL, NVML, Apple IOReport), but a counter counts only while it is live:

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). 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.

mesh/1 has no coverage field. So a figure from a counter that does not cover the whole request, such as a GPU counter on a host whose CPU has none, MUST NOT be graded HwShunt.

A span shorter than its counter's minimum window. A counter declares the smallest window over which its readings are not noise (JouleClaw's min_window_ns: about 10 ms for RAPL and IOReport, 100 ms for NVML). Across a shorter span its delta is whatever the counter's update and quantization gave — often zero, or a whole update period — not the span's energy. A span shorter than the minimum window of the counter that would grade it MUST NOT be graded HwShunt or ModelBased from that counter: it is an estimate of the request, Estimator, and SHOULD be billed by an estimator that resolves the span's own length, so that a short request is never billed zero.

§3.6 Typed decisions — the other unit of work

A decision model takes state plus typed questions and returns a calibrated probability per allowed answer, generating no text. The category converged through 2026 on three primitives — a proposition (noul), a choice over 1–255 named options, and a score over an ordered rubric — and backends score the schema non-autoregressively rather than emitting tokens.

Everything else in this spec measures work in tokens, and a decision's output token count is zero. Measured on a 421M decision model: output_tokens: 0, with 132 input tokens of which 121 were the question schema and 11 the state under judgement. Three consequences follow, and a conforming implementation MUST handle each.

A request asking for a decision carries:

Field Type Meaning
questions u32 typed questions in the request
options_scored u32 options scored across all of them
schema_tokens u32 how many of prompt_tokens are schema, not state
  1. It MUST NOT be priced for tokens it cannot emit. The estimate for a decision MUST exclude max_tokens. Including it refuses work at a ceiling that would in fact have covered it.
  2. Cost MUST be allowed to follow the schema. A profile MAY declare a cost per scored option; one question over 20 options is more work than one over 2 at identical token counts, and no token count expresses the difference. Declaring nothing reproduces the token-only model exactly, so an unmeasured profile claims nothing.
  3. The efficiency figure MUST be defined. uj_per_accepted_token (§6) is undefined when no tokens were accepted, which is every request in this category. A decision receipt MUST instead carry uj_per_decision, and SHOULD carry uj_per_option_scored. The unit of work is the answered question.

schema_tokens is reported because a fixed schema is a cacheable input prefix — distinct from cache_hit_tokens, which counts output tokens served from cache, of which a decision has none. In the measured request 92% of the input was schema, so a reader that cannot see the split cannot tell a cold decision from a warm one.

Why a decision receipt is worth more than a generation receipt. A decision is a single forward pass with no sampling. Given the weight digest, revision and numeric precision, it is reproducible — so a receipt over one is verifiable by re-execution, which a receipt over sampled text can never be. A receipt for a decision SHOULD therefore bind the weight digest and the precision; the 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.

A backend that loads its weights from files addresses them as BLAKE3 over the canonical encoding of the list of files its forward pass reads, in path order, each as its path relative to the model directory and the BLAKE3 of its bytes. Every byte is hashed, once, when the backend loads: a digest over a sample, a header or a size would look like a binding while leaving the weights free to change beneath it, and is worse than none. The file names are part of the address because they are part of what loads, and files that do not reach the forward pass, such as a tokenizer, are not. The reference engines address config.json with model.safetensors, or with model.safetensors.index.json and every shard it names; Gemma reports fp32, the arithmetic its loader widens to, and Falcon-H1 bf16. A backend that names its weights before serving MUST report the same digest after (§2).

§3.7 Where a decision model came from

§3.6 makes a decision receipt verifiable by re-execution: given the weight digest and the precision, a verifier runs the forward pass again and gets the same probabilities. That settles one question — did this answer come from these weights — and is silent on a different one. A decision model is ordinarily fitted to a corpus of earlier decisions, and that corpus is its disposition: it decides which answer the probabilities favour before any request arrives. A receipt binding the weights and not the corpus can prove the answer reproduces while leaving unanswerable what taught it to answer that way.

Two OPTIONAL fields, paired as weights_digest and precision are, because neither carries the claim alone:

Field Type Meaning
training_digest bstr content address of the corpus the weights were fitted to
training_authority tstr what a verifier can do with that corpus — signed, logged or opaque

training_digest is BLAKE3 over the canonical encoding of the corpus records in the order the fit consumed them, each record as its content id. The order is part of the address because it is part of what the fit consumed; a digest over an unordered set would look like a binding while leaving the curriculum free to change beneath it.

training_authority states the strength of that binding, and only three values say anything a verifier can act on:

  • signed — every record carries a signature over its own content by the principal that produced it, so a third party can check the corpus without trusting whoever trained on it. A corpus drawn from the accepted transitions of a signed op-log is this case.
  • logged — the records sit in an append-only log whose head is attested, so removal and reordering are detectable, but individual records are not attributable to a principal.
  • opaque — bytes with a digest and nothing more. The digest still detects substitution of the whole corpus; it establishes nothing about the records in it.

A backend that cannot address its corpus omits both fields, and the receipt then binds the disposition to nothing. As with weights_digest, the absence is the signal: a verifier distinguishes an unattributable model from an attributable one instead of assuming, which is the only reason these are optional rather than required.

What this does not license. A decision model is usually introduced because it is cheaper than the generation it displaces, and a receipt MUST NOT state that saving. The joules that generation would have cost were never metered, because it was never run. uj_per_decision (§6.2) is a measured figure; a difference against an unrun alternative is not, and a receipt that carried one would be asserting an estimate in a field a reader is entitled to read as measurement. An operator who wants the comparison runs both and keeps both receipts, and the difference then lives where it belongs — between two sealed figures, rather than inside one of them as a claim about the other.


§4 Routing — cheapest-sufficient

For each registered backend the router computes an estimate est_total_uj = uj_per_token × (prompt_tokens + max_tokens), or for a typed decision (§3.6) uj_per_token × prompt_tokens + uj_per_option_scored × options_scored, and marks it admissible iff:

backend.fidelity ≥ request.fidelity_floor   AND   est_total_uj ≤ request.joule_ceiling_uj

The router MUST choose the admissible backend with the lowest est_total_uj. Ties are broken deterministically (registration order). When no backend is admissible the serve fails, and the decision records why.

The router MUST record, for every backend it weighed, a RouteCandidate with its fidelity, estimate, admissibility, and — when inadmissible — a human-readable reason (below floor / over ceiling). The chosen route MUST carry a one-line justification. This is what makes a route auditable from the receipt alone: the cheapest sufficient answer is preferred and a frontier model is the expensive last resort, and the comparison is denominated in joules.

A model is addressed as a recipe — a base plus optional merges/adapters — not a fixed blob, so the receipt can record which composed model answered.


§5 The serving engine

A backend implements one interface: advertise a fidelity level and a modeled per-token energy cost, and serve a request producing tokens and a ServeOutcome:

ServeOutcome {
  tokens_in, tokens_out, tokens_accepted,
  cache_hit_tokens, spec_proposed, spec_accepted,
  delivered_fidelity: Option<Fidelity>          // §3.2.1
}

The advertised level is a promise made before the work; delivered_fidelity is what the backend says it actually served. An engine that cannot degrade may report its rating; an engine that can — a fallback, a truncation, a timeout that drops to a cheaper path — reports what it served. Omitting it is conformant and means fidelity_met rests on the advertisement (§3.2.1).

The input token payload travels with the serve call, alongside the request — it is never part of the request's sealed identity and never enters the receipt in the clear. The receipt records the content-addressed prompt_hash (§3.1), and the payload's digest only inside the output commitment, under its opening (§6.4). A receipt is therefore safe to publish without disclosing the prompt.

tokens_accepted is the denominator of the wedge metric. For greedy decode it equals tokens_out; under speculative decoding it is the number of proposed tokens the target model accepted. cache_hit_tokens records output served from a prefix/result cache (energy saved). An engine MUST do real work proportional to the tokens it serves so the surrounding meter reads a real span; it MUST NOT self-report energy.

An engine declares whether it is deterministic: whether serving the same request and prompt payload again yields the same tokens. Only a deterministic engine's serve can be re-executed against its receipt (§6.4). An engine that samples MUST NOT declare it, and the default is not to.

The reference engine (mesh-engine::ReferenceEngine) is deterministic and weights-free, for conformance. Production backends — a quantized device-resident decoder, a high-throughput batching server — implement the same interface and drop in unchanged; mesh-engine-gguf::GgufEngine (behind the gguf feature) is one such backend over a pure-Rust GGUF/Metal decoder, so the mesh routes and meters real model tokens.


§6 The receipt — mesh/1

A MeshReceipt is sealed with JCR-1: canonical det-CBOR (RFC 8949 §4.2.1) inside a COSE_Sign1 envelope (RFC 9052, EdDSA/Ed25519, alg = -8), byte-compatible with the rest of the family's receipts. The signed core forbids floats, booleans, and nulls; booleans are encoded as 0|1 uints and absent options dropped.

§6.1 Wire fields (mesh/1)

Key Meaning
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 — 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)
training_digest hex content address of the corpus the weights were fitted to (OPTIONAL — §3.7; absent means the receipt binds the model's disposition to nothing)
training_authority what a verifier can do with that corpus — signed, logged, opaque (OPTIONAL — §3.7; paired with training_digest, since a digest alone says which corpus, not whether anyone can check it)
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)
fidelity_advertised the level the backend is rated for (uint, OPTIONAL — paired with the next)
fidelity_delivered the level the backend says it served (uint, OPTIONAL — §3.2.1)
fidelity_met `0
estimated_uj always present
measured_uj present iff provenance is HwShunt
uj_per_accepted_token the wedge metric for a generation — energy.uj() / tokens_accepted; omitted when no tokens were accepted
questions_answered, options_scored, schema_tokens present iff the request was a typed decision (§3.6)
uj_per_decision the wedge metric for a decision — energy.uj() / questions_answered
uj_per_option_scored the finer decision figure — energy.uj() / options_scored
energy_provenance `HwShunt
grant_breach the JCP denial label when debiting the metered joules breached the grant, e.g. caveat-unsatisfied for a per-call cost caveat (OPTIONAL — §2; absent means the debit was within the grant, or the serve rode on none)
output_commitment hex commitment to the output tokens of a generation and the prompt payload they were served on (§6.4; absent for a typed decision, whose answer the engine interface does not carry, and in receipts sealed before the field existed)
sequence, prev hash-chain position and prior envelope's content id
nonce, steward per-receipt nonce and the sealing identity (COSE kid)

§6.2 The wedge metric

uj_per_accepted_token = energy.uj() / tokens_accepted, where energy.uj() is the measured figure if present, else the estimate. It is null/absent when no tokens were accepted. This is the number Mesh exists to make legible, and it is sealed into every receipt.

§6.3 Hash chain

A steward keeps a receipt log. Appending a receipt stamps its sequence (from 0) and prev (the content id of the previous sealed envelope; absent at genesis), seals it, and advances the head. The log is tamper-evident end to end: altering any envelope breaks verification of that receipt and the chain link from the next. A steward that restarts continues its chain from its journal (§2.2): the next receipt takes the next sequence and chains to the last envelope sealed, rather than starting a new chain at 0, so a restart is not mistaken for a log that lost its history.

§6.4 The output commitment, and replay

A receipt that binds what was asked (request_id, which content-addresses the prompt) and what it cost, but nothing about what came back, cannot show which answer it was for. A generation's receipt therefore carries output_commitment:

payload           = BLAKE3("mesh/1 prompt payload\0" ‖ prompt)
output_commitment = BLAKE3("mesh/1 output commitment\0" ‖ opening ‖ request_id ‖ payload ‖ tokens)

Here prompt is the input token payload the engine was handed (§5) and tokens are the output token ids, each as little-endian u32s in order; request_id and payload are their 32 bytes; and opening is a 32-byte value the steward derives per request from a key it holds. In the reference steward, the key is derived from its signing key, so a steward that restarts can disclose any receipt's opening without having stored it.

  • It is a commitment, not a hash. A plain hash of a short output, such as a one-token answer, can be reversed by trying every output, and a receipt is shown to parties who were not owed the answer. Without its opening, the commitment reveals nothing about the tokens.
  • Disclosure. A steward that discloses an output for one receipt discloses that receipt's opening and payload with it. Anyone can then check that this is the output the receipt sealed. The opening binds request_id, so it opens no other receipt.
  • The payload is bound. prompt_hash addresses the prompt's bytes, and an engine is handed token ids. Nothing ties the two together without the tokenizer, so request_id alone cannot confirm the payload a replay runs on. The commitment binds it instead.
  • Replay. Where the engine is deterministic (§5: its tokens are a function of the request and the prompt payload alone), a party holding the prompt payload and the opening can re-execute the serve and compare.
    • Re-execution yields one of three verdicts: verified (the re-run opens the commitment), diverged, or not replayable.
    • It is diverged only when a disclosed output opens the commitment on the payload the replay ran. That confirms the opening, the payload and the output together, so the mismatch is the serve's. The verdict names the first token at which the two outputs part.
    • A replay MUST NOT report a divergence it cannot attribute to the serve. It is not replayable when:
      • the engine samples;
      • it is not the backend and recipe the receipt names, or not the weights, where the receipt sealed weights_digest;
      • the request is not the receipt's;
      • the receipt commits to no output;
      • the re-run does not open the commitment, and no disclosed output opens it on the replay's payload and opening. The mismatch might then be the replayer's own input.
    • A sampled generation is not expected to repeat, and re-running it settles nothing.

These hold a serve to its record. Whether such a record carries any weight in a given dispute is not this specification's to say. It is evidence anyone can produce and check without the steward's help, opening apart.

§6.5 The weights change record

A receipt's weights_digest (§3.6) names the weights that answered. Without a further record, nothing shows when a backend's weights changed, from what, or whether the change was one its steward had planned for. A backend's change record is that record. It is a hash-chained sequence of entries, each sealed as a JCR-1 envelope under the steward key that seals its receipts, with the profile tag mesh_change: "mesh/1".

Every entry carries backend, recipe, seq (its position, from 0), prev (the content id of the envelope before it, absent for the first), made_at (Unix seconds) and kind. Optional fields are omitted when absent. There are two kinds:

kind Fields Records
plan precisions, fidelity_floor, validation, foresees, risk_assessment? the changes the steward foresees for the backend, before them
weights from?, to, precision?, fidelity, plan?, validation_result?, effective_from, reason one change of the backend's weights

The digests are hex content addresses. For a weights entry:

  • from and to are the weights before and after the change; from is absent for the backend's first entry of this kind.
  • plan is the content id of the plan entry the change falls under, and is absent for a change no plan foresaw.
  • effective_from is the first sequence of the steward's receipt log (§6.3) served on the new weights.

An entry MUST be refused when:

  • it is for another backend, or does not chain: its seq is not the next, or its prev is not the content id of the envelope before it;
  • its made_at is earlier than the entry before it;
  • it is a weights entry and:
    • its from is not the to of the last weights entry before it;
    • its to equals its from;
    • its effective_from is earlier than the last change's;
  • it names a plan, and that is not a plan entry earlier in the same record, or the change is outside it. A change is outside its plan when it has:
    • another recipe;
    • a precision the plan's non-empty precisions does not list, or no precision at all;
    • a fidelity below the plan's fidelity_floor;
    • no validation_result.

A plan therefore covers only the changes recorded after it. A change recorded as planned was foreseen before it was made, by the record's own order. A plan recorded later does not reach back over it. A change no plan covers is recorded too, without a plan. The record shows what changed, and makes no judgement of it.

The gate. Once a backend has a change record, the steward MUST serve on it only the weights its last weights entry has in effect. It MUST refuse a serve before any grant is decided or work runs where the engine names other weights, or none. It MUST refuse to seal a serve whose engine reports other weights, or none. A weights change takes effect only by being recorded, so every receipt from effective_from on was served on the weights the change put in effect. A steward that restarts holds a backend to the record it persisted, once every entry verifies under its key, the record holds, and its weights in effect are the ones the engine runs.

Coverage. A party holding a receipt and the record for its backend reads the weights entry in effect at the receipt's sequence: the last one whose effective_from is at or before it. The receipt is covered when that entry is for its backend and recipe, its to is the receipt's weights_digest, and, where both name one, its precision is the receipt's. Otherwise it is not covered. The entry then says which change put the weights in effect, when the steward made it, under which plan, and with which validation result. A position in the receipt log orders a change against receipts without trusting a clock; made_at is the steward's statement of the time.


§7 Conformance

A conforming implementation MUST:

  1. Decide a serve's grant as a JCP enforcement point and as its holder before any work (§2): reject a presentation not signed for the request by the grant's issuer, or already spent here (§2.1); reject a chain that fails verification, is held by another steward, has expired or been revoked, does not permit infer on the request, carries a cost-independent caveat that does not hold, or has less left than the request's joule ceiling; debit the metered joules against every link after the work, recording a breach on the receipt.
  2. Where the steward can restart, journal each decision before its serve runs and each receipt before it is handed out, re-establish spend and the receipt chain from the journal before serving again, and refuse grants once shown a receipt the journal does not hold (§2.2).
  3. Choose the cheapest admissible backend and record every candidate weighed (§4).
  4. Meter energy around the engine and tag provenance honestly, enforcing the Energy coupling before sealing (§3.3).
  5. Seal the receipt as a JCR-1 mesh/1 envelope and compute uj_per_accepted_token (§6).
  6. Maintain a tamper-evident hash chain (§6.3).
  7. Where a backend has a change record, refuse its entries and its serves as §6.5 sets out, so that its weights change only by being recorded.

The published vectors in ../conformance/v1/ pin a deterministic reference serve. reference/signed_core.hex MUST canonical-CBOR encode identically across implementations, and reference/envelope.hex MUST be reproducible from it with the steward key in meta.json. Regenerate with:

cargo run -p mesh-conformance --bin gen-vectors -- ../conformance/v1

§8 Security considerations

Mesh composes proven primitives: Ed25519 signatures, BLAKE3 content addressing, RFC 9052 COSE_Sign1, and the JCP capability model. It introduces no new cryptography. These properties are correctness, not style:

  • Authorisation happens outside the model. The grant check (§2) is enforced by the steward before any inference runs; a prompt cannot grant itself budget or capability.
  • A grant spends only for its issuer, once per presentation, at one steward. A presentation is signed by the grant's issuer for one request (§2.1), so a party that obtains a grant, or a captured presentation, cannot spend it; and the steward is the grant's holder, so a grant cannot be spent at a steward it was not issued to. Spreading a budget over stewards is sharding.
  • Spend survives the process. The journal is written before the work and replayed through the gate on restart (§2.2), so a restart gives no budget back and a crash mid-serve leaves the serve debited and unresolved rather than free. A journal is only as durable as the storage under it, and a steward with an in-memory journal has no such guarantee. Records cut from its end are invisible to the steward, but not to whoever holds a receipt it sealed after the cut point: shown one, the steward stops honouring grants.
  • Honesty is enforced at seal time. A receipt cannot claim a measurement it does not have (§3.3). An operator without a hardware meter produces honest Estimator receipts.

Energy figures are only as honest as the worst meter in the span. A receipt's provenance tag states which meter produced its figure; consumers SHOULD treat Estimator figures as bounds, not measurements.


§9 Relationship to the family

Mesh is the serving + routing fabric. It is expressed in primitives the family already publishes, and it composes with the neighbouring standards rather than duplicating them:

  • It rides on JCP: a serve request is authorised by a JCP Grant (capability ⊗ joule-budget), and the receipt seals in JCR-1 — the same envelope every family receipt uses.
  • It dispatches to runtimes such as JouleClaw: a backend that executes one inference cheaply is one engine behind the mesh.
  • It routes across compute described by EOC and isolated by Sandbox: a backend may be a metered, attested execution target.

The boundary is deliberate: a runtime executes one inference; Mesh routes across many models and many backends and proves what each one cost.