feat(arstotzka): v3 statement-based grants, package split, doorman cutover #2

Merged
arrdem merged 1 commit from arrdem/arstotzka-v3-cutover into trunk 2026-04-23 08:23:58 +00:00
Contributor

Summary

Arstotzka moves to v3 statement-based grants per SPEC.md — AWS-IAM-style (actions, resources) per audience, replacing the v2 {role: "<name>"} claim lists. The package is split into three sub-projects under projects/arstotzka/ so downstream services pull in only what they need. Doorman is cut over as the one real consumer.

What changed

  • projects/arstotzka/statements/ — new standalone package. Pure allows(statements, action, resource) and resource_access_allows(ra, aud, action, resource), zero runtime deps. Import as from arstotzka.statements import .... Build target //projects/arstotzka/statements:statements.
  • projects/arstotzka/server/ — what was projects/arstotzka/src/arstotzka/ lives here now. Package path arstotzka.server.*. v3 config loader rejects v2 at startup; validate_token emits a full resource_access slice scoped to the forwarded host. Token narrowing is authctl-enforced as a subset of the user's grants, so the server emits token.grants directly with no runtime intersection. /assets/logo.svg + favicon/banner on the login page.
  • projects/arstotzka/cli/ — authctl learns v2→v3 migration (synthesizes per-(role, host) role templates), new role define/list/show/remove group, user grant/revoke, token grant/revoke-grant. token add --grant replaces --service host:role. token grant rejects roles the user doesn't have so the subset invariant holds.
  • projects/doorman/_extract_priority now probes the v3 resource_access slice at ollama.tirefireind.us for (queue, priority:<level>) in descending order, highest wins. Tests updated. Depends on //projects/arstotzka/statements.

Changes are visible to end-users: no

Infrastructure change. No API surface changes for human users until arstotzka is redeployed.

  • Searched for relevant documentation and updated as needed: yes (SPEC.md already describes v3)
  • Breaking change (forces users to change their own code or config): yes — v2 configs are rejected at server startup; RPs that read X-Auth-Context.roles break
  • Suggested release notes appear below: no

Cutover plan

The v2 → v3 switch is atomic at the wire format, so any Arstotzka-fronted RP that parses X-Auth-Context must land its v3 reader before (or with) this:

  1. Merge this PR.
  2. Run authctl migrate against the 1P config, review the 7 auto-synthesized roles, rename/tighten as desired, push.
  3. Define explicit ollama quota roles (ollama-quota-{max,high,normal,low}) with statements {actions:["queue"], resources:["priority:<level>"]} on ollama.tirefireind.us, grant them to the appropriate users, revoke the auto-synthesized ollama-tirefireind-us-quota-* stand-in. The wildcard-allow template lets doorman match priority:max by accident today, so migrated configs aren't broken, just imprecise.
  4. Deploy arstotzka (bazel run //projects/arstotzka:deploy).
  5. Deploy doorman (bazel run //projects/doorman:deploy).
  6. Flowmetal's frontend reader update ships separately in the flowmetal repo — crates/frontend/src/auth.rs switches from role-string matching to (action, resource) probing. Roll that out right after arstotzka.

Test plan

  • Covered by existing test cases (61 pytest cases total across statements/server/cli/doorman)
  • New test cases added (evaluator, migration, doorman v3 shape)
  • Manual testing: authctl migrate smoke-tested against a snapshot of the production config — 19 users, 19 tokens, 7 roles synthesized, deep-equal to the hand-constructed expected output.
## Summary Arstotzka moves to v3 statement-based grants per SPEC.md — AWS-IAM-style `(actions, resources)` per audience, replacing the v2 `{role: "<name>"}` claim lists. The package is split into three sub-projects under `projects/arstotzka/` so downstream services pull in only what they need. Doorman is cut over as the one real consumer. ### What changed - **`projects/arstotzka/statements/`** — new standalone package. Pure `allows(statements, action, resource)` and `resource_access_allows(ra, aud, action, resource)`, zero runtime deps. Import as `from arstotzka.statements import ...`. Build target `//projects/arstotzka/statements:statements`. - **`projects/arstotzka/server/`** — what was `projects/arstotzka/src/arstotzka/` lives here now. Package path `arstotzka.server.*`. v3 config loader rejects v2 at startup; `validate_token` emits a full `resource_access` slice scoped to the forwarded host. Token narrowing is authctl-enforced as a subset of the user's grants, so the server emits `token.grants` directly with no runtime intersection. `/assets/logo.svg` + favicon/banner on the login page. - **`projects/arstotzka/cli/`** — authctl learns v2→v3 migration (synthesizes per-`(role, host)` role templates), new `role define/list/show/remove` group, `user grant/revoke`, `token grant/revoke-grant`. `token add --grant` replaces `--service host:role`. `token grant` rejects roles the user doesn't have so the subset invariant holds. - **`projects/doorman/`** — `_extract_priority` now probes the v3 `resource_access` slice at `ollama.tirefireind.us` for `(queue, priority:<level>)` in descending order, highest wins. Tests updated. Depends on `//projects/arstotzka/statements`. ### Changes are visible to end-users: no Infrastructure change. No API surface changes for human users until arstotzka is redeployed. - Searched for relevant documentation and updated as needed: yes (SPEC.md already describes v3) - Breaking change (forces users to change their own code or config): yes — v2 configs are rejected at server startup; RPs that read `X-Auth-Context.roles` break - Suggested release notes appear below: no ### Cutover plan The v2 → v3 switch is atomic at the wire format, so any Arstotzka-fronted RP that parses `X-Auth-Context` must land its v3 reader before (or with) this: 1. Merge this PR. 2. Run `authctl migrate` against the 1P config, review the 7 auto-synthesized roles, rename/tighten as desired, push. 3. Define explicit ollama quota roles (`ollama-quota-{max,high,normal,low}`) with statements `{actions:["queue"], resources:["priority:<level>"]}` on `ollama.tirefireind.us`, grant them to the appropriate users, revoke the auto-synthesized `ollama-tirefireind-us-quota-*` stand-in. The wildcard-allow template lets doorman match `priority:max` by accident today, so migrated configs aren't *broken*, just imprecise. 4. Deploy arstotzka (`bazel run //projects/arstotzka:deploy`). 5. Deploy doorman (`bazel run //projects/doorman:deploy`). 6. Flowmetal's frontend reader update ships separately in the `flowmetal` repo — `crates/frontend/src/auth.rs` switches from role-string matching to `(action, resource)` probing. Roll that out right after arstotzka. ### Test plan - Covered by existing test cases (61 pytest cases total across statements/server/cli/doorman) - New test cases added (evaluator, migration, doorman v3 shape) - Manual testing: `authctl migrate` smoke-tested against a snapshot of the production config — 19 users, 19 tokens, 7 roles synthesized, deep-equal to the hand-constructed expected output.
Arstotzka moves from per-role claim lists to AWS-IAM-style (actions,
resources) statements per the SPEC. Config gets a v3 layout with
top-level roles, user-level grants, optional token narrowing, and the
v2->v3 migration synthesizes per-(role, host) role templates that
operators should review and tighten before rollout.

The package is split into three workspace members so downstream
services can pull in only what they need:

  projects/arstotzka/server     - HTTP validator (arstotzka.server.*)
  projects/arstotzka/statements - pure evaluator (arstotzka.statements.*)
  projects/arstotzka/cli        - authctl (unchanged module name)

`from arstotzka.statements import allows, resource_access_allows` is
the reference API and has zero runtime dependencies.

Server changes:
- v3 config loader rejects v2 at startup; `authctl migrate` chains any
  older version up to v3.
- validate_token emits the full Keycloak-shaped resource_access slice
  scoped to the forwarded host.
- Token narrowing is an authctl-enforced subset of the user's grants,
  so the server can emit token.grants directly with no runtime
  intersection.
- /assets/logo.svg mount + favicon + banner on the login page.

authctl:
- `role define/list/show/remove` group (remove refuses while still
  referenced).
- `user grant/revoke`, `token grant/revoke-grant`. `token add --grant`
  replaces `--service host:role`.
- `token grant` enforces token.grants ⊆ user.grants.

doorman:
- Migrated to arstotzka.statements. _extract_priority probes the
  resource_access slice at ollama.tirefireind.us for (queue,
  priority:<level>) in descending order, highest wins. Old v2
  level-field parsing is gone.
arrdem force-pushed arrdem/arstotzka-v3-cutover from 04e758c93d to 454b1956a2 2026-04-23 08:22:52 +00:00 Compare
arrdem merged commit 454b1956a2 into trunk 2026-04-23 08:23:58 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
arrdem/source!2
No description provided.