ADR-032
WasmPluginError OutOfFuel + Trap + PluginCallContext reservation
accepted · 2026-04-17 · L0.5 · cites 7
0ADR-032: WasmPluginError OutOfFuel + Trap + PluginCallContext reservation #
Status flipped proposed → accepted 2026-07-12 under the merged-code
rule (the #474 ADR ruling): all three reservations ship in
crates/nika-kernel-plugin/src/wasm.rs (the kernel's plugin member —
the ADR predates the kernel descent; nika-kernel re-exports). The
stub's two open follow-ups were RESOLVED in the shipped code; the
prose records those resolutions.
Context #
Wave 4A R4 (commit 368820e42, 2026-04-17) extended the WASM plugin
stubs shipped under ADR-020 with three additive reservations, so the
v0.100 wasmtime integration lands without breaking every v0.8x plugin
consumer: a fuel-cancellation error, a structured trap surface, and a
per-call context that carries trust + budgets.
Decision #
All three shapes are #[non_exhaustive], shipped in
nika-kernel-plugin/src/wasm.rs:
- `WasmPluginError::OutOfFuel { consumed, budget }` — fuel-based
cancellation beside the wall-clock
Timeout. The seed sketched a singlefuel_consumed; the shipped shape carries BOTH sides of the ledger — an error that says how much was spent but not what was allowed teaches nothing. - `WasmPluginError::Trap { kind: TrapKind }` — guest-level aborts
(unreachable · divide-by-zero · stack overflow · …) distinct from
host errors. Follow-up resolved:
TrapKindis#[non_exhaustive], mirroring wasmtime'sTrapCodetaxonomy at the nika layer — engine-specific variants land without a major AND without a direct wasmtime dep in L0.5. - `PluginCallContext { trust, input_trust, cancel, fuel_budget,
wall_timeout_ms }` — the per-call DTO replacing the bare-cancel
argument. Follow-up resolved: the seed's
caller_trust/plugin_trustpair becameinput_trust(propagated from the verb that constructed the call) andtrust(granted to the plugin's OUTPUT — lower thaninput_trustby host policy: plugin output is always untrusted data regardless of what it was given, the ADR-030 sticky-taint model applied at the plugin boundary). Both budget knobs ride the context (None= unmetered), so the dual-timeout policy (fuel + wall) is per-call, not global.
Alternatives considered #
- Single timeout (wall-clock only) — rejected: wall time cannot distinguish a slow host from a hot guest loop; fuel is deterministic per-op and survives host load. Both ship.
- Closed `TrapKind` enum — rejected (the stub's open question): wasmtime/wasmer taxonomies evolve; a closed enum turns every new trap class into a major.
- Trust pair as caller/plugin (the seed's sketch) — rejected at hardening: what gates downstream consumption is the trust of the OUTPUT, not the identity of the caller; input/output naming states the dataflow directly.
Empirical validation (what makes this "merged code") #
nika-kernel-plugin/src/wasm.rs—OutOfFuel(line ~146),Trap+TrapKind(lines ~162-192, withDisplay),PluginCallContext(line ~217) with the untrusted-default constructor.- The reservations are exercised by the plugin trait's mock host in
nika-kernel-mock(the ADR-020 test seam).
See also #
- ADR-020 — WASM plugin boundary + Sandbox (amended by this ADR).
- ADR-028 — Forward-compat reservation policy (this ADR is an instance).
- ADR-030 — sticky-taint trust model (applied here at the plugin edge).
- ADR-033 —
TrustLevellattice used byPluginCallContext. - FCI-035 addendum — Wave 4A/4B reservation catalog.
- NIKA-700..749 — WASM plugin error code range (reserved v0.80).
🦋
read at v0.107.0 · the decision record ships with the engine