You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Record and surface every registrar verb invocation
Summary
The restricted registrar surface bounds what a compromised caller can mint, but not how often or for whom. Repeated mints, and a teardown driven against a live instance, are therefore detected, not prevented — and that detection only means anything if it lives on the bootroot side, where the registrar cannot reach it. The registrar holds no OpenBao credential at all, and the verb-level record is not an OpenBao write: it is an append to a root-owned file made by the daemon process itself. What makes it one the registrar can neither forge nor erase is therefore process and filesystem ownership — the daemon's own identity and a root-owned store the caller cannot reach — not a credential, since no credential is involved in appending to a local file. "Under the bootroot-internal credential" describes the OpenBao writes a mint performs, which are recorded separately by the device below; the two artifacts carry the same property by different mechanisms, and the children state each one where it applies.
This is a grouping issue and carries no work of its own. It exists because its three children share one on-disk contract and one security argument, and reviewing any of them against a restatement of that contract rather than against the contract itself is how they drift apart.
The split is the write / read / hold seam. The first child — itself a grouping issue — defines the store and puts records into it: the JSON Lines record schema, the two-phase intent-before-any-OpenBao-write / outcome-after ordering, the bounds on attacker-influenced fields, rotation, the startup ownership and symlink checks, and the verb return semantics that make unrecordable implies refused actually hold. The second child consumes that contract without redefining any of it, and turns the store into a signal — the intent-without-outcome scan, the malformed-line count, the retention shortfall, and the bootroot status section that reports them. The third child — also a grouping issue — gives both this store and the mandatory OpenBao audit device a bounded, measured home on the bootroot host, and relays the reader's signals off it onto the endpoint. The first two are strictly ordered: the reader depends on the writer. The third depends on both.
Children
Registrar audit records: the store and its two-phase verb integration — Detection (grouping issue)
Scan the audit store for anomalies and report them on bootroot status — Detection
Complete the audit scanner contract and coverage — Detection (verify follow-up)
Shared background
Two thirds of the substrate already exists, and this group deliberately does not replace it. bootroot requires a file-based OpenBao audit device and fails init when one is absent (verify_audit_file, src/openbao.rs:560, propagated at src/commands/init/steps/openbao_setup.rs:162); the audit stanza survives the TLS rewrite; the device has its own volume. Every OpenBao write a mint performs is already recorded there under the bootroot-internal credential. The verb-level record sits on top of that device rather than replacing it, and the init check keeps holding.
Why a second artifact is needed at all. The existing device has two gaps that no configuration of it can close. A refused invocation performs no OpenBao write, so it leaves no trace there — and refusals are exactly what a caller probing the verbs generates. And the device records paths, not the request: host, instance and the caller's identity never appear in an OpenBao write, so it cannot answer "who asked for what", which is the question a mint-storm signal is made of.
The verb-layer shape both children hang on is defined by the verbs issue and consumed here, never redefined: a pre-derivation arm under a single process-wide lock, where a refusal has a per-invocation request id and no registration_id yet; a post-derivation arm under the per-registration_id mutex; an outcome classification per invocation; and an opaque caller-identity value the verb layer threads through but never interprets. That identity is coarse until mTLS lands and sharpens afterwards without changing the record's shape — which is why neither child depends on the mTLS issue.
The surface is conditional, and so is the store.bootroot-agent is a fleet-wide binary; the registrar surface is off by default and exists only where bootroot itself runs. The store is therefore constructed with the verb layer rather than unconditionally at daemon start — a host that serves no verbs creates nothing and checks nothing, which is why "no store" is the ordinary reading on most hosts rather than a fault. It also bounds what a failed startup check may take down: it fails the registrar surface closed, never the daemon, whose fast-poll and ACME renewal duties every host in the deployment depends on.
Boundary against the rate limiter, which is a sibling and not a child. Rate limiting, coalescing of repeated refusals, the RegistrarBusy wire error and the limiter's health member belong to a sibling issue under the umbrella, which depends on this group's writer and reader and is depended on by neither. (The reserved and quota'd filesystem, its low-water alarm and the endpoint health relay are not in that sibling — they are this group's third child.) Whichever of the limiter and this group lands second is additive, and the audit-side contract with it is deliberately narrow — two clauses and no third. The limiter adds one record variant to this group's encoding — a third phase value inside the same serde types and the same record_version, which is the extension point this group ships for exactly that purpose — and at each of its check points it suppresses both audit phases together, later accounting for the suppressed traffic with that counted record, which is also where the unpaired-intent definition is amended for it. What a throttled caller is told is not this group's to state. That question — whether a suppressed invocation returns a permanent refusal or a retryable throttle, and how the limiter's buckets are keyed — is the limiter subtree's, and it turns on a recorded departure from the accepted RFC that a dedicated decision issue settles. Restating an answer here would pin a caller-facing contract this group neither owns nor tests, and would go stale the moment that decision lands differently. The reserved store sets this group's audit_record_dir to a new value and calls its reader. Neither reshapes the format: the limiter extends it along the axis this group designed for extension, adding a variant rather than a field, a file, an encoding or a version — and because phase is an enum, that addition surfaces in the reader as a compile error rather than as a silently miscounted line.
The claim each child must not overstate. The record bounds a credential, not a root-compromised host. The daemon runs as root on the bootroot host; root there can write or suppress anything on it. What is in scope is a credential leaked or copied off the host, independence from the calling repository's code being correct, and a trail the registrar itself cannot touch.
Execution order
Issues in the same wave have no unmet dependencies among these children and can run in parallel.
graph TD
issue775["#775 Reserved audit store: layout, enforced reserve, and capacity reporting [phase: Detection]"]
issue778["#778 Registrar audit records: the store and its two-phase verb integration [phase: Detection]"]
issue779["#779 Scan the audit store for anomalies and report them on `bootroot status` [phase: Detection]"]
issue761["#761 Serve the registrar verbs on a root-owned, socket-activated unix socket [phase: Surface]"]
issue761 -.-> issue775
issue762["#762 Implement the registrar endpoint's versioned wire protocol and its codec [phase: Surface]"]
issue762 -.-> issue775
issue776["#776 Add the append-only record store and its on-disk format for registrar audit records [phase: Detection]"]
issue776 -.-> issue775
issue779 --> issue775
issue758["#758 Implement the restricted registrar mint and deregister verbs [phase: Verb building blocks]"]
issue758 -.-> issue778
issue776 -.-> issue779
Record and surface every registrar verb invocation
Summary
The restricted registrar surface bounds what a compromised caller can mint, but not how often or for whom. Repeated mints, and a teardown driven against a live instance, are therefore detected, not prevented — and that detection only means anything if it lives on the bootroot side, where the registrar cannot reach it. The registrar holds no OpenBao credential at all, and the verb-level record is not an OpenBao write: it is an append to a root-owned file made by the daemon process itself. What makes it one the registrar can neither forge nor erase is therefore process and filesystem ownership — the daemon's own identity and a root-owned store the caller cannot reach — not a credential, since no credential is involved in appending to a local file. "Under the bootroot-internal credential" describes the OpenBao writes a mint performs, which are recorded separately by the device below; the two artifacts carry the same property by different mechanisms, and the children state each one where it applies.
This is a grouping issue and carries no work of its own. It exists because its three children share one on-disk contract and one security argument, and reviewing any of them against a restatement of that contract rather than against the contract itself is how they drift apart.
The split is the write / read / hold seam. The first child — itself a grouping issue — defines the store and puts records into it: the JSON Lines record schema, the two-phase intent-before-any-OpenBao-write / outcome-after ordering, the bounds on attacker-influenced fields, rotation, the startup ownership and symlink checks, and the verb return semantics that make unrecordable implies refused actually hold. The second child consumes that contract without redefining any of it, and turns the store into a signal — the intent-without-outcome scan, the malformed-line count, the retention shortfall, and the
bootroot statussection that reports them. The third child — also a grouping issue — gives both this store and the mandatory OpenBao audit device a bounded, measured home on the bootroot host, and relays the reader's signals off it onto the endpoint. The first two are strictly ordered: the reader depends on the writer. The third depends on both.Children
bootroot status— DetectionShared background
Two thirds of the substrate already exists, and this group deliberately does not replace it. bootroot requires a file-based OpenBao audit device and fails init when one is absent (
verify_audit_file,src/openbao.rs:560, propagated atsrc/commands/init/steps/openbao_setup.rs:162); the audit stanza survives the TLS rewrite; the device has its own volume. Every OpenBao write a mint performs is already recorded there under the bootroot-internal credential. The verb-level record sits on top of that device rather than replacing it, and the init check keeps holding.Why a second artifact is needed at all. The existing device has two gaps that no configuration of it can close. A refused invocation performs no OpenBao write, so it leaves no trace there — and refusals are exactly what a caller probing the verbs generates. And the device records paths, not the request:
host,instanceand the caller's identity never appear in an OpenBao write, so it cannot answer "who asked for what", which is the question a mint-storm signal is made of.The verb-layer shape both children hang on is defined by the verbs issue and consumed here, never redefined: a pre-derivation arm under a single process-wide lock, where a refusal has a per-invocation request id and no
registration_idyet; a post-derivation arm under the per-registration_idmutex; an outcome classification per invocation; and an opaque caller-identity value the verb layer threads through but never interprets. That identity is coarse until mTLS lands and sharpens afterwards without changing the record's shape — which is why neither child depends on the mTLS issue.The surface is conditional, and so is the store.
bootroot-agentis a fleet-wide binary; the registrar surface is off by default and exists only where bootroot itself runs. The store is therefore constructed with the verb layer rather than unconditionally at daemon start — a host that serves no verbs creates nothing and checks nothing, which is why "no store" is the ordinary reading on most hosts rather than a fault. It also bounds what a failed startup check may take down: it fails the registrar surface closed, never the daemon, whose fast-poll and ACME renewal duties every host in the deployment depends on.Boundary against the rate limiter, which is a sibling and not a child. Rate limiting, coalescing of repeated refusals, the
RegistrarBusywire error and the limiter's health member belong to a sibling issue under the umbrella, which depends on this group's writer and reader and is depended on by neither. (The reserved and quota'd filesystem, its low-water alarm and the endpoint health relay are not in that sibling — they are this group's third child.) Whichever of the limiter and this group lands second is additive, and the audit-side contract with it is deliberately narrow — two clauses and no third. The limiter adds one record variant to this group's encoding — a thirdphasevalue inside the sameserdetypes and the samerecord_version, which is the extension point this group ships for exactly that purpose — and at each of its check points it suppresses both audit phases together, later accounting for the suppressed traffic with that counted record, which is also where the unpaired-intent definition is amended for it. What a throttled caller is told is not this group's to state. That question — whether a suppressed invocation returns a permanent refusal or a retryable throttle, and how the limiter's buckets are keyed — is the limiter subtree's, and it turns on a recorded departure from the accepted RFC that a dedicated decision issue settles. Restating an answer here would pin a caller-facing contract this group neither owns nor tests, and would go stale the moment that decision lands differently. The reserved store sets this group'saudit_record_dirto a new value and calls its reader. Neither reshapes the format: the limiter extends it along the axis this group designed for extension, adding a variant rather than a field, a file, an encoding or a version — and becausephaseis an enum, that addition surfaces in the reader as a compile error rather than as a silently miscounted line.The claim each child must not overstate. The record bounds a credential, not a root-compromised host. The daemon runs as root on the bootroot host; root there can write or suppress anything on it. What is in scope is a credential leaked or copied off the host, independence from the calling repository's code being correct, and a trail the registrar itself cannot touch.
Execution order
Issues in the same wave have no unmet dependencies among these children and can run in parallel.
bootroot status#779External dependencies:
bootroot status#779 also depends on issue(s) outside this umbrella: Add the append-only record store and its on-disk format for registrar audit records #776