Verdict engines and the certified registry
The process rule that decides pass or fail - a threshold hold, a pressure band, a cumulative-lethality integral - is not welded into the session loop. It plugs in through a narrow trait, and only a fixed, firmware-compiled list of engines may ever be named by a signed profile.
The problem a profile alone could not solve
A device profile can describe new hardware, but until recently it could not describe a new process. The verdict logic was invoked directly by the session loop, and profile validation matched exactly one hard-coded engine id. A product whose hardware arrived with its own compliance rule had nowhere to attach it - signing a profile was not enough, because there was no seam to sign into.
A narrow port, owned by the consumer
The fix applies dependency inversion at the measurement engine, not at the certified oracle:
pub trait VerdictEngine {
fn push(&mut self, reading: &[f64]) -> StepVerdict;
fn steps(&self) -> u32;
}VerdictEngine lives in edge-measurement-engine - the orchestrator that needs it - not in a frozen, byte-parity engine crate. That placement is deliberate: a local trait can be implemented for a foreign type, so a frozen, byte-parity product engine sitting in its own crate gets a pure forwarding impl VerdictEngine, and that frozen crate is untouched by the change. Putting the trait the other way around - inside the frozen crate, with the orchestrator depending on it - was considered and rejected: it would grow a zero-dependency, byte-parity crate an abstraction shaped by a future consumer's needs, and it would couple every future engine to that crate.
The engine is handed in as a factory, not a shared instance - it carries per-session state (step and hold counters), and each session gets a fresh one so no run's progress leaks into the next.
Everything above the rule is unchanged and inherited by any engine that plugs into this seam: acquisition cadence, stop polling, fail-safe fault abort, the journal sink, and bounded step counts - and, from the worker, the frozen Idle -> Armed -> Heating safety typestate, the per-step guard, the quarantine cross-check, and every de-energize path. None of the safety logic moved; only where the rule comes from did.
The fail-closed certified registry
Signing a profile is not enough to introduce a new rule. Profile validation checks the named engine against a table compiled into the binary:
pub const CERTIFIED_ENGINES: &[EngineSpec] = &[
EngineSpec {
id: "diyar:engine:threshold-hold;1",
exact_channels: None, // a generic engine binds its inputs by name
// ...
},
// more certified engines follow, each with a stable id
];A signed profile naming an id this build does not recognize is rejected at load, and where an engine declares a required input width (exact_channels), that too is checked along with its id. Onboarding a new process rule is therefore three separate, explicit acts:
- an entry in
CERTIFIED_ENGINES, - a
VerdictEngineimplementation, - that product's own certification evidence - its own parity oracle.
A compromised or over-eager signer cannot put an uncertified verdict rule into production by signing a profile alone; the registry is compiled into the binary, and adding to it is a firmware release.
Product engines: a frozen rule in its own crate
Some CERTIFIED_ENGINES entries are product engines. Each wraps one industry's own rule - often a frozen regulatory oracle it must reproduce byte for byte - and lives in its own crate, separate from the generic library below, because it exists to preserve one specific certified behavior rather than to be reused. A product engine's verdict trace, canonical evidence bytes, and chain head are held stable, and socketing it in behind the trait must not perturb them: a test asserts the certified path yields identical outcomes through both the old direct-construction entry point and the new socketed one. Diyar's first product engine is the ISPM-15 phytosanitary rule, worked through end to end in its own dedicated unit rather than here.
The generic engines: a product with no new code
Alongside any product engines, the registry carries a small library of generic engines - certified code, compiled into the same protected crate, parameterized entirely by typed profile data:
| Engine | What it does |
|---|---|
diyar:engine:telemetry-only;1 | records every step; has no pass condition, passed is always false |
diyar:engine:threshold-hold;1 | passes after N consecutive in-band samples on a bound signal |
diyar:engine:cumulative-dwell-lethality;1 | accumulates a lethal-rate integral over a run (for example steam F0, or a plain hold-above-threshold) |
Because these are already certified and already compiled in, a genuinely different product can be onboarded as pure signed data - a device profile and its app package - with no new crate and no new code. The platform's own stated, falsifiable bar for this claim: if roughly 95 of 100 product types cannot be onboarded this way, the design has failed.
NOTE
pressure-hold (a pressure band held over Modbus TCP), coldroom-hold (a temperature band with no heat actuator at all), and steam-sterilizer-f0 (an F0 lethality target) are the repository's internal proof fixtures for this claim - each a signed profile and app package, no product crate, run through a real session loop into a real evidence store. They are test/example fixtures that exercise the generic-engine pattern, not customer case studies or field-proven deployments.
A parameterized engine is still not an escape hatch for an arbitrary rule: everything a profile hands an engine is a closed, tagged enum of typed scalars - there is no expression language, no formula string, no operator, and no field that references another field. The only way to extend what a generic engine can express is a new enum variant, which is a firmware event like any other CERTIFIED_ENGINES addition. Every params block is validated twice - once when the profile loads, once again in the engine's own constructor - so a caller that bypasses profile validation still cannot build a malformed engine.
The reserved-id rule against laundering
Because a generic engine serves many products, the evidence identity it stamps into its own records (solution_id) has to come from profile data, not a compiled constant. That opens a path a fixed-id engine never had to worry about: a signed profile could name a generic engine and stamp a reserved product id into its own evidence, producing records a downstream verifier might mistake for a certified product engine's output.
The registry closes this in both directions: every fixed id in CERTIFIED_ENGINES - a product engine's own id - is reserved, and a generic profile that claims one is refused at load. Symmetrically, a product engine owns its id and no profile may rename it. A regulator reading a solution id in the evidence chain can trust it names what it says it names.