diff --git a/DECISIONS-LOG.md b/DECISIONS-LOG.md index cdf348b..42956e5 100644 --- a/DECISIONS-LOG.md +++ b/DECISIONS-LOG.md @@ -3042,3 +3042,85 @@ shared etcd/Longhorn device plus the I/O-pressure alert that missed the prod-1 o Branch pruning was deliberately NOT done: four agent branches read as "merged, no unique commits" only because their agents had not committed yet, and deleting those would have destroyed live work. + +## R-TLS-02 — the "revoked" clause still needs the OWNER's decision (recovered 2026-09-13) + +**This is a decision only the repo owner/architect can make. It has now been escalated twice and +lost twice, which is why it is written into this log rather than into another branch's commit +message.** The analysis was first drafted on `feat/coverage-debt-paydown` (`b37defe`, 2026-09-07), +a branch whose tree is ~107 commits behind main and which must never be merged; the drafted +amendment lived only in that branch's copy of this file, so it disappeared from view the moment the +branch went stale. Recovered here, re-checked against today's main, and restated unchanged in +substance. + +**R-TLS-02, verbatim (spec `docs/00-vision-and-scope.md`):** *"Certificates MUST be automatically +**issued, renewed, and revoked** without manual steps."* + +Three verbs. Two hold; the third does not, and cannot with the stack ADR-0006 selects: + +- **Issued — satisfied.** Both ClusterIssuers are declarative (ACME DNS-01, Cloudflare token + referenced by name from SOPS), the wildcard Certificate plus its apex SAN make onboarding + certificate-free, and the active issuer is chosen per environment by `${ACME_CLUSTER_ISSUER}`. +- **Renewed — satisfied, and unhobbled.** cert-manager renews at ~2/3 of lifetime with no operator + action; nothing in the repo sets a `duration`/`renewBefore` that would defeat it, and + `CertificateExpiringSoon` / `CertificateExpiryImminent` / `CertificateNotReady` fire if renewal + silently stops. +- **Revoked — NOT implemented, and not implementable here.** cert-manager has no automatic ACME + revocation: no revoke-on-delete, no `Certificate` field that requests revocation, no controller + that calls ACME `revokeCert`. Deleting a `Certificate` stops renewal; it does not revoke, and the + issued certificate stays valid at the CA until it expires. Neither ADR-0006 nor `docs/08` + specifies a mechanism — `docs/08`'s own "Renewal & revocation" section describes replacement + issuance, which is rotation. + +**`enableCertificateOwnerRef` is NOT the remedy, and is still not set.** That cert-manager +controller flag makes a `Certificate` own its `Secret`, so deleting the Certificate +garbage-collects the key material. That is *cleanup*, not revocation. It does remove one real +manual step (today a deleted Certificate leaves an orphaned TLS Secret and private key to be +deleted by hand), so it is defensible on its own merits — but it is a GLOBAL controller flag, and +any future rename or prune that deletes and recreates a `Certificate` would take the Gateway's +`wildcard-home-tls` Secret with it, dropping TLS on every service until reissuance, against Let's +Encrypt rate limits, on two clusters. It deserves its own risk assessment, not a side effect of a +coverage or test task. + +**WHAT HAS CHANGED SINCE THE FIRST ESCALATION.** R-TLS-02 is no longer a COVERAGE gap — main +carries `l0-static/tls-lifecycle-automated` and `l2-behavioral/cert-lifecycle-automated`, and the +coverage report counts it covered. The honesty was kept where it matters: the L0 check's own verdict +line ends *"R-TLS-02 'without manual steps' clause only — issued/renewed/revoked are RUNTIME verbs +(see l2-behavioral/cert-lifecycle-automated; 'revoked' is NOT proven at any layer)"*. But the +decision is now also LOAD-BEARING on the gate in a second way: the L2 test deletes a Certificate and +requires its Secret to disappear, and on failure prints *"set enableCertificateOwnerRef: true in +platform/cert-manager values (owner decision — WS-TEST does not weaken the assertion instead)"*. So +that test is written to FAIL on any live cluster run until the owner decides. Nobody should weaken +it to make a run green; that is the decision below, arriving as a red test instead of as a question. + +**THE TWO OPTIONS, in the drafted preference order. The spec repo is READ-ONLY to every agent +(R-TEST-06), so neither has been applied — this is a request for a human decision.** + +1. **PREFERRED — amend R-TLS-02 to the property that is achievable and actually protective**, and + move revocation to an explicitly manual, documented break-glass procedure: + + > **R-TLS-02** — Certificates MUST be automatically **issued and renewed** without manual steps, + > and decommissioning a service MUST NOT leave its key material behind. Certificate + > **revocation** is an exceptional, operator-initiated action (suspected key compromise); it is + > NOT automated, and MUST have a documented procedure. Rationale: the ACME / Let's Encrypt model + > relies on SHORT certificate lifetimes plus automated rotation rather than on revocation — Let's + > Encrypt retired OCSP in 2025 and cert-manager implements no automatic revocation — so an + > "auto-revoked" requirement cannot be met by the stack ADR-0006 selects, and could only be met + > by bespoke revocation automation the threat model does not justify. + + Under this wording R-TLS-02 becomes fully testable: issuance and renewal are already asserted + statically, and "no key material left behind" is exactly what `enableCertificateOwnerRef` + implements — which makes that flag a *consequence of the requirement*, decided on its own risk + assessment, rather than a fudge to turn a gate green. + +2. **Keep R-TLS-02 as written and accept it as permanently partially satisfied**, with a reviewed + `DOCUMENTED_GAPS` entry in `conformance/run.sh` naming the revocation clause, the reason (no ACME + revocation exists in cert-manager) and the removal condition (cert-manager ships revocation, or + the requirement is amended). Cheaper, and records a requirement the system does not meet. + +**Until the owner picks one, the state is: R-TLS-02 counted as covered, its third clause +unimplemented and labelled as such in the test's own output, and the L2 withdrawal assertion +standing as a deliberate red.** Writing a test tagged R-TLS-02 that silently verifies only issuance +and renewal would report the requirement as met while a third of its normative text is +unimplemented — which is the one thing this harness exists to prevent. The choice is the +architect's, not an agent's.