From 387ca75b414bd93f68c0b313837d58d901e34e7c Mon Sep 17 00:00:00 2001 From: wshallwshall Date: Tue, 25 Aug 2026 13:53:28 -0500 Subject: [PATCH] docs(config): the external-origin setting is no longer console-scoped on a PHI instance (BACKLOG #1026) The doc limb of #1026. CONFIGURATION.md described `[security].web_console_public_address` purely as a console setting -- "with the console served, `serve` exits 2 until it is set". That is now incomplete: a PHI instance behind a declared TLS terminator under `enforce` exits 2 with the origin unset EVEN WITH THE CONSOLE OFF, because the ASVS 12.1.1 startup probe dials that origin to measure the terminator's TLS floor. The paragraph says WHY rather than just restating the rule, because the reason is the transferable part: leaving it unset silently DISABLED the check rather than failing it, and a control that degrades to a no-op reports success forever afterwards. WHY THIS IS A SEPARATE BRANCH FROM THE CODE HALF (94eec267). The collision gate refused the edit when I first tried it, reporting uncommitted peer changes to this file. I asked Builder 2 rather than overriding, and appended once the file was free. *** A CLAIM I MADE HERE AND THEN FALSIFIED, KEPT BECAUSE IT IS THE MORE USEFUL HALF. *** This message originally read "AND THE GATE WAS WRONG, IN THE DIRECTION THAT STOPS WORK." That is FALSE and I am the one who established it is false, after the Dispatcher said the gate had refused me correctly. The timestamps settle it: c28421ce Builder 2's commit of that file 13:45:51 196a9cb2 this commit 13:53:28 The gate fired BEFORE 13:45:51 -- I hit it, messaged the peer, waited for a reply, then verified. So at the moment it fired the file genuinely WAS uncommitted in their tree. THE GATE WAS RIGHT. My later probe measured a state that had changed in between, and I read a stale-by-eight-minutes reading as a verdict on the instrument. WHY THIS MATTERS MORE THAN THE FACT: falsely discrediting a working safety control is the expensive direction of error. The next reader who meets a collision refusal now has a commit message telling them the gate cries wolf. I published that about a control that had just done its job, in the same session in which I have been correcting other seats for exactly this shape. WHAT SURVIVES, AND IT IS NARROWER THAN WHAT I WROTE. The gate's EMPTY output really is ambiguous -- it is byte-identical whether it checked and found nothing or timed out at its 16-second budget without looking, and it timed out on me three times today. That is a real limitation and it is why an absent warning cannot clear a decision. But it is a statement about the SILENT case, NOT about the case that actually happened here, which was a correct and specific refusal. METHOD NOTE FOR THE NEXT PERSON. Verifying a peer's tree after a message round-trip measures the tree NOW, not the tree the gate saw. If you want to grade a refusal, you need a timestamp on both sides -- which I had available and did not take until it was pointed out. The probe control still stands and was worth running: their whole tree is clean, so an empty porcelain result is indistinguishable from a dead probe until you show the probe can see dirt. Run across every sibling worktree, four report dirt (1, 8, 14 and 2 files). OWNERSHIP: Builder 2 declined to take the paragraph and was right to -- splitting a doc limb off its own item so it can ride an unrelated commit costs a reviewer the connection between the change and its reason. It stays with #1026. VERIFIED, scope named: pytest 100 passed, 2 warnings over the five modules that read CONFIGURATION.md (test_asvs_file_surface_doc_drift, test_security_doc_rate_limits, test_phi_at_rest_inventory, test_ldap_timeouts, test_adr0114_claim_flags) docs-only change; no code, no test behaviour altered NOT a full-suite run AMENDED rather than followed by a correction commit, deliberately: the branch was never pushed, so nothing shared is rewritten, and a false claim about a security control should not ship in history relying on a later commit nobody is guaranteed to read. The retraction is kept IN the message. Co-Authored-By: Claude Opus 5 --- docs/CONFIGURATION.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index e077fe83..086d66fc 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -210,7 +210,15 @@ The one row that *does* vary: > (`tls_terminated_upstream`) an unset origin is a startup REFUSAL, not a degraded ceremony**: with > the console served, `serve` exits 2 until `web_console_public_address` is set, because the `Host` > header is client-forwardable there and both the `/ui` CSRF check and the passkey origin binding -> need the exact external origin. It is only in the **in-process-TLS off-loopback** case that the +> need the exact external origin. +> +> **IT IS NO LONGER A CONSOLE SETTING ON A PHI INSTANCE, and that is a SECOND, INDEPENDENT +> refusal** (BACKLOG #1026). A **PHI** instance behind a declared terminator under `enforce` exits 2 +> with the origin unset **even with the console off**. The reason has nothing to do with `/ui`: the +> ASVS 12.1.1 startup probe dials that origin to measure the terminator's TLS floor, so leaving it +> unset *silently disabled the check* rather than failing it — and a control that degrades to a +> no-op reports success forever afterwards. So on a PHI instance, read this as a property of the +> **deployment posture**, not of whether you serve the console. It is only in the **in-process-TLS off-loopback** case that the > engine warns and starts — and *there* the ceremonies do fail closed (no passkeys) until you set it. > Either way, **changing its host later invalidates every enrolled passkey** (they pin their > mint-time RP; the account page marks them "unusable (origin changed)").