fix(identity): actually wire the built-in verifier — a config key registers nothing - #235
Merged
Conversation
…isters nothing #234 shipped the Ed25519 verifier and #232 the seam, and between them the feature was unusable: nothing ever called register_verifier. Setting auth_verifier="ed25519" made every claim fail closed — correct behaviour, and a silently dead feature. Found by standing a real govd up from merged main with auth_verifier="ed25519" and watching a valid assertion resolve to nobody. No unit test could have caught it: both halves were individually correct. serve() now calls install_builtin_verifier(cfg) before the first claim. Only a name we SHIP is installed; an unknown name installs nothing and therefore resolves nobody, which is the intended fail-closed behaviour for a typo — it must never fall back to the bearer-secret path. Verifier CODE stays in the image by design: a verifier loaded from the mounted config would be ungoverned code executing inside the syscall boundary, which is the one thing this system exists to prevent. The mount configures WHICH verifier, never its body. Verified against a real govd over real HTTP (local, merged main, ed25519 scheme): alice's assertion -> 200 allow, and the signed chain records principal=alice undeclared key -> 401 bearer secret -> 401 no credential -> 401 That `principal=alice` is the point of the whole exercise: a named identity in the audit trail, which a shared per-node token can never provide. TESTS. Two unit tests pin the helper, and — because mutation showed deleting the serve() call left every unit test GREEN — an integration test starts a real govd on a free port and authenticates over HTTP, so the CALL SITE is covered, not just the function. Re-mutated after adding it: the suite now goes red. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI's enforcement-surface mutation ratchet caught this and it was right to: [FAIL] infra/govern/govd.py: score=0.95 floor=1.0 survivors=['==->!=@9986'] MUTATION REGRESSION below floor: [('infra/govern/govd.py', 0.95, 1.0)] govd.py was at a perfect 1.0 — every mutant killed — and the new `if name == "ed25519"` branch dropped it to 0.95. The branch was NOT untested: tests/test_ed25519_auth.py kills that mutant. But the ratchet drives each enforcement-surface module against a DESIGNATED slice (infra/govern/selfmonitor_policy.json: govd.py -> tests/test_govd.py), so a proof living anywhere else is invisible to it. Coverage for code in govd.py belongs in govd.py's slice; that policy is the point, not an obstacle to route around — the alternative (widening the slice) would dilute the ratchet for every module. Verified by re-running the exact CI survivor locally: mutating `==` to `!=` now fails tests/test_govd.py::test_install_builtin_verifier_registers_only_a_name_we_ship, and the suite is green restored. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
#234 shipped the Ed25519 verifier and #232 the seam, and between them the feature was unusable: nothing ever called
register_verifier. Settingauth_verifier="ed25519"made every claim fail closed — correct behaviour, and a silently dead feature.Found by standing a real govd up from merged main and watching a valid assertion resolve to nobody. No unit test could have caught it — both halves were individually correct.
serve()now callsinstall_builtin_verifier(cfg)before the first claim. Only a name we ship is installed; an unknown name installs nothing and resolves nobody, which is the intended fail-closed behaviour for a typo — it must never fall back to the bearer-secret path.Verifier code stays in the image by design: a verifier loaded from the mounted config would be ungoverned code executing inside the syscall boundary. The mount configures which verifier, never its body.
Verified against a real govd over real HTTP
That
principal=aliceis the point of the whole exercise — a named identity in the audit trail, which a shared per-node token can never provide.Tests
Two unit tests pin the helper. Because mutation showed that deleting the
serve()call left every unit test green, there's also an integration test that starts a real govd on a free port and authenticates over HTTP — so the call site is covered, not just the function. Re-mutated after adding it: the suite now goes red.🤖 Generated with Claude Code