From aff80d1d7a1d4823707b73921c4c9d26a300c3a8 Mon Sep 17 00:00:00 2001 From: Keith Jackson Date: Sun, 13 Sep 2026 16:26:54 -0500 Subject: [PATCH] =?UTF-8?q?docs(pm):=20R-TLS-02=20needs=20the=20owner=20?= =?UTF-8?q?=E2=80=94=20the=20drafted=20spec=20amendment,=20recovered=20and?= =?UTF-8?q?=20restated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The R-TLS-02 analysis and its drafted spec amendment were written on feat/coverage-debt-paydown (b37defe, 2026-09-07) and lived ONLY in that branch's copy of DECISIONS-LOG.md. That branch's tree is ~107 commits behind main and must never be merged, so the amendment went out of view with it — the second time this decision has been raised and lost. Recovered, re-checked against today's main, and written where it cannot vanish with a branch. THE SUBSTANCE IS UNCHANGED. R-TLS-02 says certificates MUST be automatically "issued, renewed, and revoked without manual steps". Issuance and renewal hold. Revocation is not implemented and is not implementable with the stack ADR-0006 selects: cert-manager has no revoke-on-delete, no Certificate field requesting revocation, and no controller that calls ACME revokeCert, and neither ADR-0006 nor docs/08 specifies a mechanism. enableCertificateOwnerRef is NOT the remedy — it garbage-collects the Secret, which is cleanup, not revocation — and it is a global controller flag that would drop the Gateway's wildcard-home-tls Secret on any future Certificate rename/prune, so it needs its own risk assessment rather than a side effect of a test task. Still not set. WHAT CHANGED SINCE THE FIRST ESCALATION, and why it now matters more: R-TLS-02 is no longer a COVERAGE gap (l0-static/tls-lifecycle-automated + l2-behavioral/ cert-lifecycle-automated both carry the label and main counts it covered), and the L0 check is honest about the boundary in its own verdict line ("'revoked' is NOT proven at any layer"). But the L2 test deletes a Certificate and requires its Secret to disappear, and its failure message says "set enableCertificateOwnerRef: true ... (owner decision — WS-TEST does not weaken the assertion instead)". That assertion is written to FAIL on a live run until the owner decides. The decision is now arriving as a red test rather than as a question, which is exactly how it gets weakened by whoever meets it next. Two options are recorded verbatim for the architect, in the drafted preference order: amend the requirement so revocation is an explicitly manual, documented break-glass action (which also makes enableCertificateOwnerRef a CONSEQUENCE of the requirement), or keep the text and record a reviewed DOCUMENTED_GAPS entry naming the clause, the reason and the removal condition. NOT APPLIED, and not appliable by an agent: the spec repo is read-only under R-TEST-06. This is a request for a human decision, stated plainly as one. Co-Authored-By: Claude Opus 5 --- DECISIONS-LOG.md | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) 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.