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
Renew the registrar surface's certificates, with a reload contract and a lapse surface
Summary
Minting the registrar surface's two leaves once is not enough. Both expire; a lapsed leaf stops every enrollment in the deployment, and the registrar cannot re-mint itself. This group keeps both leaves valid over time and makes a failing renewal visible before it becomes an outage. It is a grouping issue and carries no work of its own.
The seam between the children is doing versus telling, and it is drawn where the wire is. The first child renews: it adds both leaves to the daemon's existing certificate-renewal mechanism, preserves the name invariants a fresh key pair leaves behind, and delivers both halves of the reload contract — the server-side certificate swap and the caller-side torn-pair-safe per-dial load. It puts nothing on the wire and touches no serialization fixture. The second child reports and classifies: it populates the endpoint health container's certificates member, fixes a precedence between simultaneous conditions, turns an actual lapse into a typed non-retryable error on both leaves, classifies every post-connect failure by layer, and documents the remediation. Every wire-visible consequence in this group is the second child's, and that line is checkable in review rather than a matter of taste.
Only one of those two reporting shapes is on the wire, and conflating them would send this group looking for an error identifier it must not add. The health member rides ordinary responses, so it is serialized and fixture-pinned. The lapse error is not: a lapsed client leaf is caught before dialing and a lapsed server leaf fails the handshake, and neither produces a response to carry an identifier — so it is a local typed error in this repository's client, asserted by variant, and the translation into the ecosystem's permanent unavailability reason happens in the co-located registrar's repository out of what it observes. Neither child adds a wire identifier or a reason.
They are strictly ordered — reporting depends on renewal — and the whole interface between them is one in-process accessor, described below.
Children
Renew both registrar leaves on the daemon's existing loop and honour the reload contract — Surface
Report the registrar certificates' lifetime and turn a lapse into a typed non-retryable error — Surface
Shared background
The accessor is the entire interface, and it exists so there is exactly one source of truth about a certificate's state. The renewal child records, per leaf, the observed notAfter, the outcome of the last renewal attempt and when that attempt ran, and exposes them through one in-process accessor written on the daemon's own tick. Those three values are exactly what the reporting child's member publishes, so the accessor's shape and the member's fields move together; a value the member reports that the accessor does not carry would have to be re-derived on the request path, which is the thing this seam exists to forbid. The reporting child reads that accessor on the same tick and never re-derives either value. The failure this seam exists to prevent is concrete: a health member computed on the request path stats and parses a certificate per request, which puts filesystem work behind an endpoint a caller can drive. Neither child may add a second place where a certificate's lifetime is discovered.
"A renewal is failing" and "a leaf has lapsed" are different states, and the split follows them rather than cutting across them. The renewal child's job ends at recording a failed attempt while the leaf is still valid — which is the entire warning window, and the only interval in which anything can be repaired quietly. The reporting child is what makes that window reach an operator, and only the already-expired state becomes the typed non-retryable error. An implementation that reports lapses alone deletes the warning window and turns a recoverable condition into the outage this group exists to prevent.
Neither child restructures the listener, the wire protocol or the caller. Renewal drives seams other issues expose — the mTLS issue's certificate swap, the client issue's per-dial load — and reporting populates a member inside a container the protocol issue shapes and places. A renewal or a report that cannot be implemented without reordering one of those is a finding to raise against that issue, not a change to make here.
Observation never repairs. Neither child may trigger issuance from the request path or from the act of noticing a lapse. Making an observation trigger work would hand a caller a way to drive issuance, on the one host that must not be disturbed. Recovery lives in two places instead, and neither is reachable from a request: a leaf that became unusable while the daemon was down — expired, or left signed by a trust generation a rotation retired — is repaired by the issuance issue at the next daemon start, before the endpoint's loader runs, and a leaf that needs replacing while it runs is renewed by the first child, on its lead time or on the chain-drift trigger that covers a rotation performed under a running daemon. So an operator's remedy is to start the daemon, or to let a running one reach its tick — never to re-provision the host, and never something a caller's retry can trigger.
Two arrival paths into a lapse are reachable, and one is not — the distinction is per leaf, and every case in this group must use a reachable one. The issuance issue repairs an already-expired leaf at daemon start, before the endpoint's loader runs — a deliberate refinement it records against the RFC — so an endpoint never comes up serving or holding an expired leaf. What that removes is the server-side path only: "the daemon was down through the deadline" is no longer a way to observe a server-leaf lapse, and no case in this group stands an endpoint up on expired material. Two paths remain reachable. The first is the running-daemon timeline: renewals that keep failing from the lead-time point through not_after, which is exactly what the first child's failed-attempt recording makes visible and the second child's warning window covers. The second is the caller's own client leaf while the daemon is still down: the caller reads its configured client material directly and refuses before dialing, so a client leaf that expired while the daemon was down is observable until someone starts the daemon — which is why the second child gives that condition the highest precedence and drives it by handing the client an expired pair. Start-time repair changes the sequence an operator sees on that path, not its existence.
Both children drive the schedule deterministically, and neither proves survival by waiting. The wall-clock proof that a renewed leaf outlives its original notAfter belongs to the extended-tier acceptance scenario. A criterion in either child that sleeps past a real notAfter is in the wrong issue.
Execution order
Issues in the same wave have no unmet dependencies among these children and can run in parallel.
graph TD
issue768["#768 Renew both registrar leaves on the daemon's existing loop and honour the reload contract [phase: Surface]"]
issue769["#769 Report the registrar certificates' lifetime and turn a lapse into a typed non-retryable error [phase: Surface]"]
issue767["#767 Self-issue the registrar surface's two certificates from the daemon [phase: Surface]"]
issue767 -.-> issue768
issue764["#764 Terminate mTLS on the registrar endpoint and scope it to the registrar identity [phase: Surface]"]
issue764 -.-> issue768
issue765["#765 Add the in-repo client for the registrar endpoint [phase: Surface]"]
issue765 -.-> issue768
issue768 --> issue769
issue762["#762 Implement the registrar endpoint's versioned wire protocol and its codec [phase: Surface]"]
issue762 -.-> issue769
issue765 -.-> issue769
Renew the registrar surface's certificates, with a reload contract and a lapse surface
Summary
Minting the registrar surface's two leaves once is not enough. Both expire; a lapsed leaf stops every enrollment in the deployment, and the registrar cannot re-mint itself. This group keeps both leaves valid over time and makes a failing renewal visible before it becomes an outage. It is a grouping issue and carries no work of its own.
The seam between the children is doing versus telling, and it is drawn where the wire is. The first child renews: it adds both leaves to the daemon's existing certificate-renewal mechanism, preserves the name invariants a fresh key pair leaves behind, and delivers both halves of the reload contract — the server-side certificate swap and the caller-side torn-pair-safe per-dial load. It puts nothing on the wire and touches no serialization fixture. The second child reports and classifies: it populates the endpoint health container's
certificatesmember, fixes a precedence between simultaneous conditions, turns an actual lapse into a typed non-retryable error on both leaves, classifies every post-connect failure by layer, and documents the remediation. Every wire-visible consequence in this group is the second child's, and that line is checkable in review rather than a matter of taste.Only one of those two reporting shapes is on the wire, and conflating them would send this group looking for an error identifier it must not add. The health member rides ordinary responses, so it is serialized and fixture-pinned. The lapse error is not: a lapsed client leaf is caught before dialing and a lapsed server leaf fails the handshake, and neither produces a response to carry an identifier — so it is a local typed error in this repository's client, asserted by variant, and the translation into the ecosystem's permanent unavailability reason happens in the co-located registrar's repository out of what it observes. Neither child adds a wire identifier or a reason.
They are strictly ordered — reporting depends on renewal — and the whole interface between them is one in-process accessor, described below.
Children
Shared background
The accessor is the entire interface, and it exists so there is exactly one source of truth about a certificate's state. The renewal child records, per leaf, the observed
notAfter, the outcome of the last renewal attempt and when that attempt ran, and exposes them through one in-process accessor written on the daemon's own tick. Those three values are exactly what the reporting child's member publishes, so the accessor's shape and the member's fields move together; a value the member reports that the accessor does not carry would have to be re-derived on the request path, which is the thing this seam exists to forbid. The reporting child reads that accessor on the same tick and never re-derives either value. The failure this seam exists to prevent is concrete: a health member computed on the request path stats and parses a certificate per request, which puts filesystem work behind an endpoint a caller can drive. Neither child may add a second place where a certificate's lifetime is discovered."A renewal is failing" and "a leaf has lapsed" are different states, and the split follows them rather than cutting across them. The renewal child's job ends at recording a failed attempt while the leaf is still valid — which is the entire warning window, and the only interval in which anything can be repaired quietly. The reporting child is what makes that window reach an operator, and only the already-expired state becomes the typed non-retryable error. An implementation that reports lapses alone deletes the warning window and turns a recoverable condition into the outage this group exists to prevent.
Neither child restructures the listener, the wire protocol or the caller. Renewal drives seams other issues expose — the mTLS issue's certificate swap, the client issue's per-dial load — and reporting populates a member inside a container the protocol issue shapes and places. A renewal or a report that cannot be implemented without reordering one of those is a finding to raise against that issue, not a change to make here.
Observation never repairs. Neither child may trigger issuance from the request path or from the act of noticing a lapse. Making an observation trigger work would hand a caller a way to drive issuance, on the one host that must not be disturbed. Recovery lives in two places instead, and neither is reachable from a request: a leaf that became unusable while the daemon was down — expired, or left signed by a trust generation a rotation retired — is repaired by the issuance issue at the next daemon start, before the endpoint's loader runs, and a leaf that needs replacing while it runs is renewed by the first child, on its lead time or on the chain-drift trigger that covers a rotation performed under a running daemon. So an operator's remedy is to start the daemon, or to let a running one reach its tick — never to re-provision the host, and never something a caller's retry can trigger.
Two arrival paths into a lapse are reachable, and one is not — the distinction is per leaf, and every case in this group must use a reachable one. The issuance issue repairs an already-expired leaf at daemon start, before the endpoint's loader runs — a deliberate refinement it records against the RFC — so an endpoint never comes up serving or holding an expired leaf. What that removes is the server-side path only: "the daemon was down through the deadline" is no longer a way to observe a server-leaf lapse, and no case in this group stands an endpoint up on expired material. Two paths remain reachable. The first is the running-daemon timeline: renewals that keep failing from the lead-time point through
not_after, which is exactly what the first child's failed-attempt recording makes visible and the second child's warning window covers. The second is the caller's own client leaf while the daemon is still down: the caller reads its configured client material directly and refuses before dialing, so a client leaf that expired while the daemon was down is observable until someone starts the daemon — which is why the second child gives that condition the highest precedence and drives it by handing the client an expired pair. Start-time repair changes the sequence an operator sees on that path, not its existence.Both children drive the schedule deterministically, and neither proves survival by waiting. The wall-clock proof that a renewed leaf outlives its original
notAfterbelongs to the extended-tier acceptance scenario. A criterion in either child that sleeps past a realnotAfteris in the wrong issue.Execution order
Issues in the same wave have no unmet dependencies among these children and can run in parallel.
External dependencies: