Skip to content

M1-43 Trusted proxy header login

No Date Ruling Reason
M1-43/1 2026-10-14 Base int/m1-wave13 at 82693e50, branch feat/M1-43-identity-trusted-header in its own worktree; not merged locally and not pushed, the PR lane ports it. Text this ticket adds is English, also in German chapters. Instructions of the launcher, which override the brief's merge step and language (as M1-37/1).
M1-43/2 2026-10-14 W4 is resolved as PLAN section 7 and 16 section 4.3 state it: a trusted_header provider without a loopback public listener does not stop the start. The driver stays off (loginWithTrustedHeader answers 404), the readiness check trusted_header_requires_loopback fails while such a provider is enabled, and identity_provider.manage refuses to enable it with 409 conflict, reason: trusted_header_requires_loopback and a hint naming server.listen_addr. platform/config has no start-time cross-check of the driver, so nothing was removed there. The driver is a runtime setting of the owner; a mistake in it must fail closed without preventing the start (08 section 2.4 rule 1, ID-T08, SEC-T01).
M1-43/3 2026-10-14 The loopback flag is not a field of app.Config but state of app.TrustedHeaderDriver: internal/wire sets it from trustedheader.IsLoopbackListener of the bound public listener after the listen and registers the readiness check then; tests set it with Identity.SetPublicListenerLoopback. It starts as false (fail closed); the role worker opens no public listener and never registers the check. config.Bind refuses an exported field without a key, and the bound address exists only after the listen, which runs after the identity step.
M1-43/4 2026-10-14 kernel/reqmeta.Transport (TCP peer from RemoteAddr, the request header, the route's action) is set by the authenticate stage of platform/httpserver; the header login handler and the per-request binding read it. Two files outside the brief's scope change for this: kernel/reqmeta (new file) and platform/httpserver/stages.go (three lines). The generated strict handlers and the actor.Authenticator receive no *http.Request; the brief requires the raw peer and the configured headers in the use case and the binding check in the shared Authenticator.
M1-43/5 2026-10-14 The binding check also runs in refreshToken (inside its unit of work, after the family key matched; the revocation commits like a reuse and the answer is 401 session_revoked). The brief says the check covers refreshToken "because the authenticator is shared", but refreshToken is anonymous and never reaches the Authenticator; rule 5 of 08 section 2.4 binds every later request. A proof of another key still never revokes (ID-T17).
M1-43/6 2026-10-14 The binding reads the identity and the provider of a trusted_header session with two queries after the session lookup instead of joining them into GetAuthState. Only sessions of this driver with the switch on pay the reads; the shared lookup and its sqlc code stay unchanged.
M1-43/7 2026-10-14 The refusal of enabling the driver is audited as a deny of identity_provider.manage with the new reason trusted_header_requires_loopback, added to the closed list of 07 section 4.1 (row "no authorize result", like malware_detected) and to the reason lists of kernel/auditing and authz/domain. The brief asks for an audit deny; a deny needs a reason of the closed list, and none of the existing ones describes the refusal.
M1-43/8 2026-10-14 The handle of a provisioned person follows the rule of brief M1-41 (part of the login before @, lower case, only a-z0-9._-, starting with a letter or digit, cut to principal.handle_max_chars, user when nothing remains; a taken handle gets - and four random Crockford Base32 characters). M1-41 is not merged, so the rule lives in domain/trustedheader (HandleFromLogin, HandleWithSuffix) until both drivers share one helper. Brief M1-43 Design notes, step 5.
M1-43/9 2026-10-14 CompleteLogin gains AllowPending (a pending person logs in, 08 section 3.1 item 3 and R-ID-14) and ProviderID (recorded as provider_id in the allow entry). A known identity of a pending person logs in with the header too; disabled and deleted people never do. Several field lines of the login header are absent; a comma inside one value is part of the value. Brief M1-43 step 6; 08 section 2.4 rule 6 speaks of several values, which HTTP represents as several field lines.
M1-43/10 2026-10-14 When the binding fails at the Authenticator, the deny entry names the route's action from reqmeta.Transport; a caller without a route (the gateway before M1-64) records auth.login.complete. The audit row needs an action code; M1-64 owns the gateway binding and its action.
M1-43/11 2026-10-14 The integration tests of the driver run in five worlds: one each for ID-T08, SEC-T01 and the binding switch off, TestTrustedHeaderLogin (binding on, no provisioning) with the cases ID-T09, ID-T10, the missing header, a disabled person, several values, no mapping and the enrollment code as ordered subtests, and TestTrustedHeaderProvisioning with provisioning and TestTrustedHeaderNeverLogged. The test-first red runs were taken by mutation of the finished code (binding always bound, peer not checked, loopback ignored, missing header accepted), each turning its test red. With one world per case the package identity/adapters/httpapi passed the default timeout of go test in make test on the loaded host (as M1-44/13); the code and the tests were written in one step, so the mutations show the tests catch the behaviour.
M1-43/12 2026-10-15 Port onto main: code and tests are taken byte for byte from the integration squash, with LoginCompletion.AllowPending, loginAttrs, ServiceDeps.Random and the refresh order as INT-W14/5 states them (AllowPending itself came with M1-41). Code comments and the chapter key of auth.trusted_header_bind_requests name 08-identity.md; the regenerated config-schema.json carries the key. The language gate (tools/docs/language_check.py) allows the non-ASCII test values Röbin and ÄÖÜ of this ticket and Ünal of M1-41 as words of tools/docs/language-allow.txt. The gate reads every tracked file and also decodes escaped umlauts, so these RFC 2047 and handle test vectors failed make docs; one allow list entry per value keeps the vectors unchanged and fixes the class in one place.