feat(mllp)+fix(startup): expose the message-rate pacer (#1249); require public_origin for the 12.1.1 probe (#1026) - #392
Merged
Conversation
…LOG #1249) OWNER RULING 2026-08-14: expose the keys. The pacer was BUILT and CORRECT, and no documented configuration could turn it on -- `mllp.py:1378` and `:1382` read `max_messages_per_second` and `message_burst` from a settings dict nothing could populate. A ruled-off default and an unreachable setting are different things, and only the first was intended. Two keyword-only parameters on a factory that already had 26, plus the two keys in the settings dict it returns. That is the whole change. THE TOML SURFACE COMES FREE, AND I VERIFIED IT RATHER THAN ASSUMING IT. `connections_file.py` resolves a transport to its factory and calls `return factory(**settings)` -- "the factory is the schema, there is no second source of truth". So `connections.toml` can now express both keys with no second edit, and a test pins that so the claim is checkable instead of resting on a docstring. THE OFF DEFAULT IS PRESERVED AND TESTED. Exposing a knob must not turn it on: a rate on a clinical interface is only safe at a number from a real feed profile. `DEFAULT_MAX_MESSAGES_PER_SECOND` is still None, a default install still has no rate bound, and a test asserts that constant directly -- if it ever becomes non-None, this change would silently have paced everyone. WHY THIS WAS INVISIBLE FOR SO LONG, and it is the useful part. The pacer HAD tests. Every one of them built an MLLPSource from a raw settings dict, bypassing the factory -- so they proved the control worked while the only surface that could configure it was broken. The new tests go THROUGH the factory on purpose, and one of them is a positive control that MLLP() still rejects an unknown keyword: without it, every reachability assertion here would pass even if the parameters did not exist. docs/SECURITY.md's ingest row is updated in the same commit, because the guard added in e92b7ec reads reachability FROM THE SIGNATURE and requires the doc to agree with whatever it says. That guard failed the moment this code changed -- which is exactly what it was built to do -- and it named the correction it wanted. The row now states the shipped default rather than unreachability, and keeps the parts that were always true: the pause-don't-drop behaviour, the raw-TCP intake still having no pacer, and the resource bounds that do ship on.
… it (BACKLOG #1026)
OWNER RULING 2026-08-14: require it regardless of `serve_ui`.
THE DEFECT. The TLS-floor probe's gate has FOUR conditions; the comment above it named THREE and
asserted every other posture "never reaches here and is byte-identical". The undocumented fourth was
`public_origin` -- and the only thing that required `public_origin` was itself gated on `serve_ui`.
So with the console OFF, a PHI instance behind a declared terminator under `enforce` started with
the ASVS 12.1.1 control SILENTLY INERT, and nothing reported the skip. A reader of that comment
concluded the probe runs whenever a PHI instance sits behind a declared terminator under enforce.
THE SAME BLOCK ALREADY REFUSED THIS SHAPE TWICE, ONE LEVEL DOWN. It returns 2 when the probe's
MECHANISM is missing, because "a check that degrades to a no-op when its mechanism disappears
reports success forever afterwards", and it refuses on unreachable because "a gate that is trivially
defeated is not a gate". Leaving `public_origin` unset WAS trivially defeating this gate, and the
outcome WAS a check reporting success forever. The principle was stated twice and violated one level
up.
THE COMMENT IS NOW TRUE BY CONSTRUCTION, not by editing. The refusal guarantees `public_origin` for
exactly the posture the probe gate selects, so the fourth condition is no longer a hidden scope
narrowing -- it is a belt-and-braces and the type narrowing the call needs, and the comment says so.
SCOPE MATCHES THE PROBE'S SCOPE, INCLUDING LOOPBACK. The probe deliberately runs behind a declared
proxy on loopback ("a reachable front door that speaks TLS 1.0 is a fact, on loopback or not"), so
the precondition is required there too. A synthetic-data instance is untouched, and a test pins that
-- without it this would be a blanket refusal measuring something other than its own posture.
This REFUSES A POSTURE THAT STARTS TODAY. Per CLAUDE.md section 0 there are zero deployments, so it
breaks nothing and notifies nobody, and it will never be cheaper to add. That is why the cost is
acceptable -- not why the answer is (a). The reason for (a) is that it is the only end where the
control measures its own posture rather than measuring whether someone happened to configure an
unrelated console setting.
PROVED BY A NEGATIVE CONTROL, not by a green run: with the fix reverted, the console-OFF case FAILS
and the console-ON case still PASSES -- so the new test isolates exactly the defect, and the arm that
was already correct is shown to have been already correct.
Test fixtures pre-satisfy the new precondition the way they already pre-satisfy intra-service auth
and the KEX floor, so each case keeps testing its own subject. Two of them also stub the probe, for
the same reason `uvicorn.run` is stubbed: these are CONFIG-gate tests and the probe's network
behaviour is covered by tests/test_tls_floor_probe.py. NOTE FOR THE NEXT EDITOR: the authored key is
`[security].web_console_public_address`; `[api].public_origin` is the internal settings name and ADR
0118 retired the authored form, which caught these fixtures twice.
wshallwshall
enabled auto-merge (squash)
August 14, 2026 16:12
wshallwshall
disabled auto-merge
August 14, 2026 16:37
…g gate suites Repairs the 13 failures my own c162309 introduced. Same class as the four I fixed in test_api_tls.py and test_cli.py, in the two files I did not run: the #1026 start-time refusal blocks postures those suites expect to START, so each was failing on a precondition rather than on its own subject. THE SCOPING ERROR IS THE POINT, and it is worth more than the fix. The refusal is FLEET-WIDE across the startup ladder; my verification was FILE-LOCAL. "223 tests pass across every affected file" was a TRUE statement over a WRONG POPULATION -- affected meant "files I edited", and the change affects every suite that boots a declared-terminator PHI instance under enforce. Two shared seams per file, so the repair is four edits for thirteen tests: - the TOML builder declares `[security].web_console_public_address` - the serve harness stubs `probe_tls_floor`, for the same reason it already stubs `uvicorn.run`: these are CONFIG-GATE tests, and the probe's network behaviour is tests/test_tls_floor_probe.py. Stubbing it to FAIL would assert the probe rather than the ladder. In test_checks_gate_parity.py the constant follows the `_MEMORY_ENCRYPTION` precedent exactly, including its placement rule -- a [security] key must precede _PROXY or TOML files it under [api]. ONE CONSEQUENCE FLAGGED RATHER THAN ABSORBED: the loopback-behind-a-declared-proxy topology in OFF-LOOPBACK-DEPLOYMENT.md now needs a public address too. The 12.1.1 probe deliberately runs there ("a reachable front door that speaks TLS 1.0 is a fact, on loopback or not"), so the precondition applies -- but that changes the published runbook's minimum config, which is a doc question and is noted in the test rather than silently satisfied. THE NEGATIVE CONTROL STILL DISCRIMINATES, re-verified after these edits because pre-satisfying a precondition everywhere is exactly how you neuter your own proof: reverted to f8b9806 the console-OFF case FAILS while console-ON and non-PHI still pass. A first attempt at that check was INVALID and nearly cost the conclusion -- `git checkout --` restores from HEAD, and HEAD is the #1026 commit, so it reverted TO the fix and everything passed. Removing a committed change needs the parent, not the file.
wshallwshall
enabled auto-merge (squash)
August 14, 2026 19:35
THE PAIRED LEDGER COMMIT FOR THIS PR, supplied under the sanctioned interim (c):
a builder PR implementing a backlog item cannot make itself green, because the
required check `a PR that implements BACKLOG #N must update BACKLOG.md` reads the
PR's own three-dot diff, and ledger authoring is not the builder's lane.
WHY IT WAS THE PR BODY THAT TRIGGERED THE GATE, stated so nobody hunts a phantom:
the gate greps title and body for the literal token `BACKLOG #N`, and this PR's body
carries it because the Lander wrote it there. The claim is ACCURATE -- the PR does
implement both items -- so the honest satisfaction is to update the ledger, not to
reword the body until the gate stops looking. A gate you can silence by changing how
you describe the work is not a gate.
STATUS BANNERS ARE DELIBERATELY UNCHANGED, and this is the load-bearing choice.
`parse_items` before and after: 288 items, 208 open, #1249 and #1026 both still open.
Marking an item closed is a judgement about COMPLETENESS. Code landing is evidence
toward that judgement, never the judgement itself -- and the seat that supplied a
paired commit to unblock a gate is the worst-placed seat to make it. Left to the
ledger seats.
WHAT EACH AMENDMENT RECORDS:
#1249 the pacer settings the connector has read since it was built are now
reachable, from code-first wiring and from connections.toml alike
#1026 public_origin is required where the probe needs it, making a comment that
was true by convention true by construction; the negative control was
re-run and still discriminates
Pure addition: 4 insertions, 0 deletions, one file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two owner-ruled items, built by Builder 2. Opened and armed by the Lander at the author's explicit go.
f8b98061-- expose the message-rate pacer on theMLLP()factory (BACKLOG #1249)Exposes
max_messages_per_second/message_burstonMLLP(). The connector had read both sincethe pacer was built and nothing could populate them -- the settings were live in the code and
unreachable from any config surface.
TOML comes free:
connections_file.pydoesreturn factory(**settings), so the same change reachesboth the code-first and
connections.tomlpaths. Off by default, and that default is tested.SECURITY.md's ingest row moved in the same commit because thee92b7ecfguard reads reachabilityfrom the signature and flipped on its own -- the doc change is the guard's output, not an
editorial choice made alongside it.
c162309b-- requirepublic_originwhere the ASVS 12.1.1 probe needs it (BACKLOG #1026)Requires
public_originin the declared-terminator PHI enforce posture, which makes the falsecomment at
:2113-2114true by construction rather than by convention.Negative control run: reverted, console-OFF fails and console-ON still passes -- so the test
discriminates the thing it claims to.
Verification
Author-side, before handing over:
ruff+ruff format+mypy --strictclean on every changed file#391fixLander-side, independently:
The branch was not on origin when handed over -- both commits existed only as local refs
(
refs/archive/b2-1249-expose-pacer,refs/archive/b2-1026-require-public-origin). Pushed fordurability before anything else.
The rebase was then simulated read-only rather than attempted,
rcread before content:The author independently reproduced that same tree hash
53f9382bfrom their own checkout.Why this is armed rather than held
The author's local suite ran against the pre-rebase tree, and a pre-rebase run is not landing
evidence -- it is stale the moment the branch updates. At URGENT STOP neither of us should start a
fresh full suite. CI runs against the merged state, which is strictly better than any local run
either of us could produce, so arming is how the missing evidence gets generated rather than a way
of skipping it.
If CI goes red, it returns to the author -- not absorbed here.