Three reference estimators priced work by a coefficient no host calibrated and graded the figure ModelBased: - sandbox-exec-microvm: with no live CPU counter, vCPU-seconds × the reference 12,000 µJ/vCPU-second (a nominal work-unit cost), and the accelerator's "coarse proxy" (that cost × 10); - sandbox-exec-wasm: fuel × the reference 20,000 µJ per million fuel; - sandbox-meter StorageActivity (from_samples, from_volume): ops and bytes × nominal NVMe-class pJ coefficients documented as "calibrated". By the family's ladder (JCP SPEC §5.1) a model earns ModelBased by a calibration on the host; a nominal coefficient is the whole of such a model, so the figure is an estimate. - MicroVmConfig.calibrated and WasmConfig.fuel_cost_calibrated (both false by default) keep a ModelBased path for a cost the operator calibrated on the host, as spec/receipts.md's "calibrated table" describes; the accelerator proxy is Estimator either way. Neither config is constructed outside its crate. - Tests that pinned the over-claim now pin Estimator: cpu_only_run_meters_modelbased (renamed ..._an_estimate), a_cpu_counter_that_held_across_a_run_is_not_a_measured_zero, power.rs's two RAPL fallback tests, runs_a_unit_and_meters_cpu_energy, storage_globs_enforced_through_execution, contention_is_loaded_minus_ baseline. New tests: a fallback is a model only at a host-calibrated cost (CPU) and never for the proxy (accelerator); the same for the fuel cost; both storage constructors are estimates. - boot_microvm example's 15 W/vCPU fallback is Estimator too. - SPEC §6.3, spec/receipts.md, SECURITY.md, README and sandbox-meter's Provenance docs say a nominal or assumed coefficient is Estimator. No vector changes: sandbox-conformance passes every class, file-backed vectors included, and conformance/v1 is untouched (its builders use fixed constructors, none of these estimators). Co-Authored-By: Claude Opus 5.5 <[email protected]>
78 lines
6.2 KiB
Markdown
78 lines
6.2 KiB
Markdown
# The energy receipt (per-device, JCR-1)
|
|
|
|
Normative companion to [`SPEC.md`](../SPEC.md) §6. RFC 2119 keywords apply.
|
|
|
|
A **receipt** accounts for the energy of one run, attributed to each device class the unit touched, two-part (`measured + estimated`) in integer microjoules, every reading honestly tagged, sealed in the family's JCR-1 envelope.
|
|
|
|
## Core
|
|
|
|
```
|
|
Receipt {
|
|
unit_id : ContentId, // SPEC §3
|
|
tier : Tier, // S0..S7
|
|
devices : [DeviceEnergy], // one per used class; non-empty
|
|
total : { measured_uj : Option<u64>, estimated_uj : u64 },
|
|
sequence : u64,
|
|
nonce : [u8; 8],
|
|
}
|
|
|
|
DeviceEnergy {
|
|
class : DeviceClass, // Cpu | Simd | Gpu | Npu | Tpu | Lpu | Photonic | Quantum
|
|
measured_uj : Option<u64>,
|
|
estimated_uj : u64,
|
|
provenance : Provenance, // HwShunt | ModelBased | Estimator
|
|
}
|
|
```
|
|
|
|
## Per-device attribution (REQUIRED)
|
|
|
|
1. `devices` **MUST** contain exactly one record per device class the unit **actually used** during the run — not per granted class. A granted-but-unused class produces no record.
|
|
2. A host that ran accelerated work **MUST** emit a separate record for each accelerator class used. It **MUST NOT** fold accelerator energy into the `Cpu` record, and **MUST NOT** omit it.
|
|
3. `total.estimated_uj` **MUST** equal the sum of `estimated_uj` over `devices`. `total.measured_uj` is `Some(sum)` **only if** every record in `devices` has a `measured_uj`; otherwise it **MUST** be absent.
|
|
|
|
This is the clause that distinguishes a Sandbox receipt from a process-sandbox cost line: the joules are resolved to the silicon that spent them.
|
|
|
|
## Provenance discipline
|
|
|
|
| Tag | Meaning | `measured_uj` |
|
|
|---|---|---|
|
|
| `HwShunt` | read from a hardware energy interface for **this class** | **MUST** be present |
|
|
| `ModelBased` | from a per-operation model for this class calibrated on the host, or a reading apportioned by a model | **MAY** be absent |
|
|
| `Estimator` | a figure nothing calibrated: a coarse constant, or a model at a nominal or assumed coefficient | **MUST** be absent |
|
|
|
|
Per-class hardware interfaces the reference recognizes: **CPU** — Linux powercap/RAPL, AMD energy, Apple SMC; **GPU** — NVIDIA NVML `nvmlDeviceGetTotalEnergyConsumption`, AMD ROCm SMI, Intel Level Zero; **NPU/TPU/LPU/Photonic/Quantum** — the device's own energy or power-integration interface where one exists.
|
|
|
|
Rules:
|
|
|
|
- A host **MUST NOT** report a `measured_uj` it did not obtain from a hardware interface **for that specific class**. A GPU `measured` figure may never be inferred from a CPU shunt.
|
|
- The `(provenance, measured_uj)` coupling is enforced: `HwShunt` ⇒ present; `Estimator` ⇒ absent. A receipt violating the coupling is invalid (vector class **C3**).
|
|
- **At tiers S5 and above**, where a hardware interface for a used class exists on the host, the host **MUST** use it (`HwShunt`) rather than estimate. At lower tiers, where dedicated attribution may be unavailable, `ModelBased`/`Estimator` are permitted and the tag makes the limitation explicit.
|
|
|
|
## Reference estimators (non-normative)
|
|
|
|
When no hardware interface is available, a host estimates. The reference coefficients (microjoules) are tunable; the **standard is that the figure is reported and honestly tagged, not the constant**:
|
|
|
|
- CPU: per vCPU-millisecond cost from a table calibrated on the host, tagged `ModelBased`.
|
|
- Accelerator: per device-operation (e.g. per TFLOP-second, per inference forward pass) from a table calibrated on the host, tagged `ModelBased`.
|
|
- Otherwise — a reference or nominal coefficient no host calibrated (the reference executors' defaults: a per-vCPU-second cost, a cost per million WASM fuel units, an accelerator proxy scaled from the CPU figure), or a coarse constant per class — tagged `Estimator`. The coefficient is the whole of such a model, so an uncalibrated one makes the figure an estimate.
|
|
|
|
## Storage activity — measurement from contention (non-normative)
|
|
|
|
Storage is a resource the platform usually cannot meter: there is no per-tenant SSD energy interface the way there is RAPL for the CPU. The standard turns the **measurement technique behind a known side channel** to honest use. The contention a unit creates — random-read latency rising as a shared device saturates (`SECURITY.md` describes the adversarial version) — is, **measured cooperatively by the host and disclosed**, a real signal: a probe times page-cache-busting reads against an idle baseline and reports the device's observed activity (`iops`, `bytes`, `contention_ns`) and an estimated energy figure.
|
|
|
|
Two disciplines keep it honest:
|
|
|
|
1. **It is a distinct axis, not a device class.** Storage activity is its own line (`sandbox-meter::StorageActivity`); it does **not** enter the per-device-class energy sum of §6.2, and reporting it never perturbs the sealed receipt format or its vectors. Storage is not folded into a compute class it did not consume.
|
|
2. **It is never `HwShunt`.** A timing-derived figure is `Estimator` at the reference's nominal per-op and per-byte coefficients (`ModelBased` only with coefficients calibrated on the host's device); a timing channel is not a hardware shunt, and the provenance coupling (§6.3) is enforced for it exactly as for a device record. The same physics that leaks information when undisclosed becomes auditable telemetry when the host runs it on itself and tags it truthfully.
|
|
|
|
## Signed form (JCR-1)
|
|
|
|
The receipt seals as a **JCR-1 envelope** — the signed-receipt format shared across the family:
|
|
|
|
- a canonical **det-CBOR** signed core (RFC 8949 §4.2.1: length-first key order, no floats, no bools, no nulls — absent fields are dropped, not encoded as null),
|
|
- inside a **COSE_Sign1** envelope (RFC 9052), **EdDSA/Ed25519**, `alg = -8`,
|
|
- profile tag `"sandbox/1"` in the core, the steward identity in the COSE `kid`.
|
|
|
|
The `sandbox-meter` crate vendors the JCR-1 reference primitives so the library is self-contained, and is byte-compatible with the family's cross-language JCR-1 verifiers. A Sandbox receipt therefore verifies under existing JCR-1 tooling alongside Joule Code and JouleClaw receipts.
|
|
|
|
The receipt references, and is referenced by, the run's attestation: the attestation carries `receipt_id = content_address(receipt core)` (SPEC §7.1), binding the two without duplicating the energy figures.
|