Adapter admission receipts¶
Bernstein resolves an adapter by name: a key in the registry produces a live adapter that receives the task, the workspace, and the worker's credential scope. Historically that resolution asked nothing about whether the adapter had actually been verified against the binary installed on the host. An adapter whose conformance verdict was skip — because it shipped no contract, or because its binary was missing, or because the --help probe was inconclusive — spawned exactly like one that had passed every check.
A skip is not a pass. Admission receipts make that distinction load-bearing.
What it does¶
- Admission is proof-based, not name-based. The spawn path re-derives the adapter's evidence — the installed binary's version, the pinned contract's content hash, the capability profile's content hash, the golden-transcript replay outcome, and the nightly canary's attestation — and admits the adapter only when a sealed receipt pins that same evidence.
- The refusal is a first-class record. A skipped, stale, or failing adapter seals a refusal receipt naming the reason, the capabilities it is explicitly denied, and the remediation that would clear it. An absent admission can never be read as an implicit one.
- Both paths are anchored. Admissions and refusals alike are mirrored into the HMAC audit chain as
adapter.admission_receiptevents. Given a contiguous chain slice, an operator can prove offline which adapters held spawn authority during a window and, for each one that did not, why. - Drift is a named divergence. The replay fingerprint is a deterministic projection of
(contract bytes, binary version, golden-transcript replay output). Two operators on the same binary version derive byte-identical bytes, so a binary that moved under a still-valid receipt is caught as a fingerprint mismatch rather than riding a stale attestation. - Stripping the receipt withdraws authority. Deleting a sealed receipt makes the adapter un-spawnable under the enforce policy, not merely unlogged.
Receipt fields¶
| Field | Meaning |
|---|---|
adapter, binary, binary_path | Which adapter, and the binary the probe resolved. |
installed_version, probe_hash | The upstream version seen, and a content hash binding the probed binary identity. |
contract_hash, profile_hash | Content addresses of the pinned contract and the capability profile. |
replay_fingerprint | Deterministic projection of contract bytes, binary version, and transcript replay. |
conformance_run_id | Deterministic id of the conformance run behind the decision. |
conformance_verdict, canary_verdict | The in-process probe verdict and the nightly attestation state. |
verdict, reason | admit / refuse, and the refusal reason. |
allowed_capabilities, forbidden_capabilities | What the decision grants and what it explicitly withholds. On a refusal, spawn itself is forbidden. |
admission_ttl_seconds | Freshness window — one nightly-canary cycle. |
remediation | The operator-visible next action. |
Refusal reasons¶
| Reason | What happened |
|---|---|
no_contract | No contract YAML pins the adapter's invocation surface. |
no_transcript | No golden transcript replays the adapter's spawn path. |
conformance_skip | The --help probe was inconclusive — installed, missing, or redesigned. |
conformance_fail | The installed binary no longer advertises the required surface. |
replay_diverged | The golden transcript no longer replays clean. |
canary_red | The nightly attestation for the installed version is red. |
no_receipt | Live evidence is green but nothing is sealed on disk. |
receipt_stale | The sealed receipt is past its TTL. |
receipt_tampered | The receipt body no longer hashes to its recorded identity. |
fingerprint_mismatch | The binary, the contract, or the transcript changed after sealing. |
stored_refusal | The sealed receipt itself records a refusal. |
Policy¶
Warn-by-default: the gate records every decision from the first run so an operator sees exactly which adapters would be refused before it starts blocking. To make a refusal a hard stop:
=off disables the gate entirely. mock is the only exempt adapter — the test-only stub wraps no pinned upstream surface, so offline work against it is never blocked. generic was exempt until #4752 and is now gated like every other adapter; an operator running generic under enforce needs a sealed receipt for it, or =off.
Operator commands¶
Check an adapter and exit non-zero when it is refused:
Seal and anchor a fresh receipt from the current evidence:
Emit the raw receipt for a reviewer or a CI dashboard:
Sealed receipts live under .sdd/adapters/admission/ and are content-addressed, so a tampered body no longer hashes to its recorded identity and is rejected without any key material.
Receipt filenames embed a slug of the adapter name: a display name such as Qwen CLI is folded to qwen-cli for the filename, while the operator-visible name stays unmodified in the receipt body. This lets an adapter whose name is not already a filesystem-safe slug write a receipt at all. The slug fold also changes the stored filename for any adapter whose name was already slug-safe but under a different convention; after an upgrade the first run re-derives the receipt rather than reuses the previous file.
Where the evidence comes from¶
The verdict is derived from two trees that live under tests/ in a source checkout — the pinned contract YAMLs in tests/contract/contracts/ and the golden transcripts in tests/golden/. Both ship inside the wheel, at bernstein/_default_templates/adapter_contracts/ and bernstein/_default_templates/adapter_golden/, so a pip install derives the same replay fingerprint a checkout does and --seal goes green on the install channel too. A checkout, when present, is read in preference to the bundled copy.
To replay against a transcript tree of your own — a vendored fork, or an air-gapped mirror — point BERNSTEIN_ADAPTER_GOLDEN_DIR at it. It takes precedence over both layouts. Note that a different transcript tree changes the replay fingerprint, so receipts sealed against it are only comparable with other hosts using the same tree.
Relationship to the other adapter gates¶
The admission gate sits alongside two existing spawn-path gates and does not replace either:
- The security floor refuses a binary below its minimum-safe version. That is a supply-chain question about which build is installed.
- Capability-aware routing refuses an adapter whose profile cannot satisfy a task's declared requirements. That is a fit question about what the task needs.
- Admission refuses an adapter that has not been verified against the binary actually installed. That is a trust question about whether the surface was ever checked.
All three seal receipts into the same HMAC chain, so one slice answers all three questions.