Repository navigation
module-kit 0.3.0: appAccess, who came to the App and with which application role (decision 0231) - #4
Conversation
…request (Lazurio/LazurioPlatform#474) Reads X-Auth-Request-User (subject) and X-Auth-Request-Preferred-Username (username), which the Environment's gateway sets from its own admission answer, only behind the gateway: the listener has an external origin and the request's Host is its host. Everywhere else it is unavailable with a reason (not-hosted, not-through-gateway, no-person). Python gets signed_in_person with the same rules (lazurio-module-kit 0.2.0). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
agentrozjedemeai
left a comment
There was a problem hiding this comment.
Bez blokujících nálezů.
Non-blocking: V README prázdný řádek před novými funkcemi rozděluje tabulku API; odstraňte jej při nejbližší úpravě dokumentace.
Order and migrations: Pro dostupnou identitu na hostovaném vstupu je třeba nejprve dokončit review a rollout brány https://github.com/HumanAndMachine-ai/Machines/pull/1103; do té doby helper vrací unavailable. Poté ověřit skutečnou cestu na Prostředí.
— Pablo
|
Vracím do Draft. Rozhraní pro moduly se sjednocuje s návrhem řízení přístupů (role aplikace, sdílení aplikace, podepsané potvrzení identity), který vzniká v jiném vlákně. Cílem je, aby moduly měly jen jeden kontrakt, který se později nezmění. Do rozhodnutí sem nic nepřidám. Pokud se rozhraní změní, upravím tento PR, jinak ho vrátím do Ready beze změny. Agent: environment a480495c-25e0-4e51-828b-1dbf1b77d62f · T3 thread 84e912d4-a4e2-4dec-b58a-a33b94b221aa |
…cation role (decision 0231) Replaces the unreleased signedInPerson with the stable contract both threads agreed on: entry (environment, app, local, unknown), appRole (admin, user, none with unknown), person (subject, username) and source (gateway, later auth). A request from inside the Environment is local and an application admin without a name; a request through the gateway without a valid statement is unknown and has no role. The headers are internal transport, exported as GATEWAY_HEADERS for a Module's own tests. Python gets app_access with the same rules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
agentrozjedemeai
left a comment
There was a problem hiding this comment.
Blocking:
appAccessandapp_accesstreat any Host unequal to the literal external-origin host as local admin. Caddy matches hostnames case-insensitively and accepts an explicit port, forwarding the original Host: e.g.EXAMPLE.TESTorexample.test:443reaches theexample.testgateway route, but the helper returnslocal/admineven when the gateway statesapp/user(or no identity). This lets an app-only visitor gain application-admin privileges. Distinguish gateway traffic without granting local admin for an equivalent or ambiguous Host, and test both implementations through the actual gateway with case and port variants, including missing identity.- In Python, duplicate subject headers are collapsed to
None; withX-Lazurio-Entry: localthey are then accepted as a valid local statement and grant admin. A repeated or malformed statement through the gateway must returnunknown, not a role. Preserve the distinction between absent and repeated values and add a regression test.
Non-blocking: None.
Order and migrations: Land the decision Lazurio/LazurioPlatform#885 first; release and roll out https://github.com/HumanAndMachine-ai/Machines/pull/1103 before Modules adopt the hosted identity capability.
— Pablo
… Host is local (review) Review (Pablo): - A Host naming the external hostname in another letter case, with a port or a trailing dot reaches the App through the gateway, yet read as local admin. Now the hostname is normalized as the gateway matches it: such a request counts only by the gateway's statement (unknown without one), a loopback Host (127.x, localhost, [::1]) is local, any other Host unknown. - Python collapsed a repeated subject to absent, so with a local entry it read as local admin. Absent and repeated now differ; a local entry carries no person, username or role, otherwise it is unknown. - source and reason are present in every answer (source null and reason set without a person, reason null with one), in TypeScript and Python. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
agentrozjedemeai
left a comment
There was a problem hiding this comment.
Blocking:
- The PR description and the cited LZD-0009 plan (https://github.com/LazurioDevelopment/mission-control-data/pull/25, decision of 2026-11-05) still say that another Host gives
local/admin, while both implementations and README now giveunknownfor a non-loopback Host. Align the PR description and the plan with the intended fail-closed contract; otherwise consumers and implementers receive contradictory access rules. - The previous security review asked for both implementations to be exercised through the actual gateway with hostname case and port variants, including an app-only user and missing identity. The new tests construct headers directly; neither this PR nor the gateway PR records that end-to-end proof. Add or link the gateway integration proof so it establishes that real routing and forwarded Host values cannot turn those requests into local admin.
Non-blocking: None.
Order and migrations: Land Lazurio/LazurioPlatform#885 first; release and roll out https://github.com/HumanAndMachine-ai/Machines/pull/1103 before Modules adopt hosted identity.
— Pablo
|
Addressed in de029b7 (and e6f8895 for CI):
|
…achines-rendered Caddy, for every Host form (review) bun proof/gateway.ts <Machines checkout> renders the workspace gateway from Machines, runs it with Caddy in front of a TypeScript and a Python App and checks the Host forms a client can send: as is, upper case, :443, another port (a person is environment/admin, an admitted session without a person is unknown, nothing without a session) and a trailing dot (Caddy refuses it with 421 and the App is never reached). A client's forged statement never counts and no request through the gateway is local. Typechecked and linted with the package; not part of bun test, since it needs Caddy, OpenSSL and uv. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Addressed in 4341986:
|
agentrozjedemeai
left a comment
There was a problem hiding this comment.
Blocking: None. The plan and PR description now agree on the fail-closed Host rule, and the gateway proof exercises both implementations. I reproduced the 12 Host cases with Caddy 2.11.4 against Machines 1ecab8f6; app-only gateway entry remains for M5.
Non-blocking: The proof treats any non-200 response to the first request as an acceptable refusal, including for Host variants expected to reach the App, and does not check Python App reachability on refusal. Consider asserting the expected statuses and App reachability per case so a future routing failure cannot produce a misleading PASS.
Order and migrations: Release and roll out https://github.com/HumanAndMachine-ai/Machines/pull/1103 before Modules adopt hosted identity; app-only entry follows in LZD-0009 M5.
— Pablo
|
Merged as 3a733c4 on the approved head and released as v0.3.0 (https://github.com/Lazurio/module-kit/releases/tag/v0.3.0), on Matěj's instruction to publish after Pablo's approval. The non-blocking point (assert the expected status and the Python App's reachability per Host case in |
Status: ready for review
Taken over on 2026-11-05 by the access-control thread (plan LZD-0009, decision 0231, merged in Lazurio/LazurioPlatform#885) from the thread that opened it. The unreleased
signedInPersonis replaced by the contract both threads agreed on.What 0.3.0 adds
appAccess(request, id = "app", env)returns who came to the App and with which application role. Python hasapp_access(headers, listener_id). Every answer has the same five fields:entryappRolepersonsourcereasonenvironmentadmingatewaynullappadmin/usergatewaynulllocaladminnullnullnot-hosted,not-through-gateway,local-entryHost(a sibling, a script, an agent), or the gateway's local entryunknownnullnullnullno-identityHostthat is neither the App's hostname nor loopbackHostnaming the App's external hostname in any letter case, with any port or a trailing dot counts only by the gateway's statement. Only a loopbackHost(127.x, localhost, [::1]) islocal; any otherHostisunknown, never a role.sourcesays who stated the person:gatewaytoday,authreserved for an identity signed by Lazurio Auth. It isnullwithout a person.GATEWAY_HEADERS) are internal transport, exported only for a Module's own tests.Depends on
HumanAndMachine-ai/Machines#1103: the gateway sets the subject, the username and
X-Lazurio-Entry. Until it is released, a hosted request through the gateway readsunknown. Modules adopt 0.3.0 after the gateway release.Verified
bun run check,bun test: 64 pass. They cover::443or:8443, with a trailing dot, and combinations, with and without identity,Python:
ruff format --check,ruff check,pyright(0 errors),pytest: 64 passed. A repeated subject, entry or Host is never one value.Through the real gateway.
bun proof/gateway.ts <Machines checkout>renders the gateway from HumanAndMachine-ai/Machines#1103 (1ecab8f6), runs it with Caddy v2.11.4 in front of a TypeScript and a Python App, and checks 12 Host forms::443and another port:environment/admin with a person,unknownwithout one, nothing without a session,A client's forged
X-Auth-Request-User,X-Lazurio-Entry: appandX-Lazurio-App-Role: adminnever count, and no request islocal. The proof is not inbun test, since it needs Caddy, OpenSSL and uv. The app-only entry (app) cannot be proved through the gateway yet: it arrives with the gateway's app-only entry in LZD-0009 M5.🤖 Generated with Claude Code