GLIM clean-room Phase3d r3 machine identity and custodian handoff¶
This document records an opt-in, non-promoting handoff boundary for the Phase3d r3 OpenPGP executor. It does not change the active r2 profile, create a trust key, capture this host, run GnuPG, or make the candidate benchmark-ready.
Machine identity¶
apt_machine_identity.py emits glim_clean_room_r3_machine_identity_v1 and a
read-only .sha256.json sidecar. The capture boundary reads only stable private
machine inputs (machine-id, DMI UUID, and board serial); their raw bytes never
leave the capture function. At least two distinct, meaningful stable
identifiers are required: common DMI placeholder values (for example “to be
filled by o.e.m.”, “default string”, and “system serial number”) and duplicate
raw values do not satisfy that minimum. The artifact contains only
domain-separated SHA-256 projections and public architecture/CPU/memory/kernel/
toolchain fields. Hostname, username, and MAC addresses are not collected
because they are volatile and unnecessary.
The artifact binds campaign ID, producer module/source SHA/version, canonical
identity SHA, capture time, file bytes, and file SHA. It rejects symlinks,
hardlinks, parent symlinks, unknown fields, source drift, canonical drift, and
TOCTOU changes. The two output files are sealed as a fresh pair. Offline reopen
returns HOST_MATCH_NOT_CHECKED (live_host_match: false); only an explicit
live validation performs a current-host projection comparison. LIVE_CAPTURED
is required before an authorization policy can use the artifact; synthetic test
artifacts cannot enter a READY policy.
No repository machine artifact is checked in. A real capture remains a future custodian operation and is not represented by the checked-in NOT_READY policy.
DMI-less keyed fallback (additive, opt-in authorization method)¶
When a host exposes only one meaningful stable identifier, the v1 two-ID
contract still fails closed. The additive keyed path in
apt_machine_identity.py does not weaken that rule. An operator supplies an
external, read-only Ed25519 public-key descriptor with a nonempty provisioning
receipt identity. prepare-key-challenge then binds the campaign, keyed
domain, current machine-id hash, public host projection, key identity, source
identity, fixed candidate-manifest identity, intended output filename, and a
fresh random challenge nonce. A separate external signer
returns only a detached proof document; finalize-keyed verifies it and emits
the final artifact. No command in this repository reads, creates, stores, or
logs private-key material.
The challenge fixes the intended final output filename before signing; finalization requires that fresh path. It is therefore bound to the signed campaign/candidate/output-name/nonce tuple, but a copied immutable challenge directory is not, by itself, a universal replay-prevention mechanism. The authorization executor's separately signed nonce claim is the one-shot execution boundary. A changed challenge, proof, key descriptor, campaign, machine-id projection, or public projection fails closed.
The key descriptor, challenge, proof, final artifact, and their sidecars are
bounded regular single-link files. All source paths are same-directory safe
names, and every source is reopened by bytes, canonical hash, sidecar hash,
campaign, key, challenge, and signature identity. Finalization re-reads the
current machine-id and public projection, so a copied artifact cannot claim a
different host in live mode. Offline validation reports
HOST_MATCH_NOT_CHECKED; it is not a host-match claim. The external
provisioning receipt is an input binding, not an independent trust decision:
custodian policy must still review the key before any READY authorization.
The keyed method is an additive tagged-union branch in the authorization policy,
authorization document, executor receipt reopen path, and custodian handoff.
A future READY policy must select exactly one method (legacy v1 or keyed
PROVISIONED_ED25519) and bind its exact schema, file/canonical/identity
projections, public-key descriptor, challenge, and proof. Initial authorization
verification invokes keyed live validation; receipt reopen invokes keyed offline
validation and preserves the sealed live_host_match: true evidence rather than
claiming that the current host was checked. Handoff accepts a keyed
LIVE_CAPTURED artifact but records OFFLINE_ARTIFACT_ONLY.
The checked-in authorization policy remains empty NOT_READY; no authorized
key or keyed machine artifact is checked in. provisioning_receipt_sha256 is
only a precommitted external receipt identity and is not cryptographically
verified by this repository. Custodian review and a separately signed
authorization remain required before any real runner can be enabled.
Custodian request¶
apt_release_signature_handoff.py and
apt_release_signature_handoff.schema.json define an unsigned
UNSIGNED_REVIEW_REQUIRED request. prepare-request accepts only sealed JSON
files for the plan, repository, and deb-binding ledger; it never accepts an
inline trust root/key or generates a key/signature. The request reopens and
binds:
- fixed candidate and NOT_READY policy identities;
- executor, authorization verifier, cryptography backend/version/source hash;
- a LIVE_CAPTURED machine artifact, campaign identity, and offline-only validation status;
- a fully validated plan, trust root, Release/Packages/deb binding, and source bytes; and
- exact root/scope/safety values, validity window, nonce, and empty custodian key/signature fields.
The output and JSON sidecar are fresh, single-link, read-only, and reopened before a successful result is returned. Tampering, replay across campaigns, source or plan drift, symlink/hardlink replacement, scope drift, arbitrary custodian key material, and an offline-as-live claim fail closed. A separate external custodian must review this request and return policy/authorization material; that process is outside this repository and remains NOT_READY.
Custodian response review (implemented, non-promoting)¶
apt_release_signature_handoff_response.py and its response/sidecar schemas
validate a returned READY_AUTHORIZATION_RESPONSE as an offline review
artifact. The verifier reopens the immutable request and its request sidecar,
recomputes the complete request binding, then reopens the candidate, READY
policy, and signed authorization bytes. It checks the candidate and policy
file/canonical identities against the exact request template, the policy's
candidate and machine/backend projections, the authorization body against the
template (with only custodian validity and detached signature fields filled),
and the existing Ed25519 verifier against the reopened Release/Packages/deb
context. The verifier implementation, schemas, backend version, authorization
payload identity, and live_host_match=false offline result are also sealed in
the response binding. The immutable binding records authorization_issued_at,
not a verifier-selected review time. Operational validate-response obtains
its own integer wall clock and passes that review time to the existing
authorization verifier, requiring issued_at <= review_time < expires_at; the
response author cannot choose it. The returned review_time/current-status
projection is an ephemeral, non-promoting review result, not a sealed claim.
Historical structural reopening must not be described as current
authorization validity. Response and sidecar files must be fresh, immutable,
single-link regular files; path replacement, sidecar drift, mixed method
branches, request/policy/candidate swaps, expanded validity/scope, and
self-rehashed metadata fail closed.
The checked-in request producer intentionally pins the checked-in candidate and
NOT_READY policy. Therefore the current repository cannot accept a READY
response merely by placing a different policy beside the request: the exact
candidate/policy bytes must first be reviewed and resealed by the external
custodian workflow, followed by a new request bound to those bytes. Synthetic
tests exercise that future response path only; they do not add a key, generate
a signature, enable the executor, or promote the candidate. Offline response
review never claims that the response machine is the current host.
Evidence status¶
| Boundary | Status | Promotion |
|---|---|---|
| machine identity schema/module | IMPLEMENTED, synthetic-tested | not captured on this host |
| live machine comparison | IMPLEMENTED, test-only provider seam | not run |
| custodian handoff request | IMPLEMENTED, synthetic-tested | UNSIGNED_REVIEW_REQUIRED |
| custodian READY response review | IMPLEMENTED, synthetic-tested | non-promoting, requires external reseal |
| GPG/network/apt/Docker execution | NOT_RUN | forbidden in this task |
| active r2 / README / production policy | unchanged | no claim |
The checked-in policy keeps authorized_keys: [] and an empty NOT_READY machine
identity projection. This candidate therefore cannot authorize a real runner.