RFC-0001: v0.1 implementation plan¶
Status: Implemented Author: Alex Nodeland Created: 2026-10-12 Discussion: derived from lattice RFC-0001, accepted on 2026-10-12 with its decisions D1 to D9 Siblings:
- lattice RFC-0001 is the design this plan builds. Its decisions are grantr's ADR-0001 to ADR-0009.
- lattice RFC-0002 is portalr, whose admin UI manages access through grantr's API, and whose phase 2 draws grantr's mark.
Summary¶
grantr is identity and access for lattice, as lattice RFC-0001 designs it: tenants, members, groups, roles, service principals and grants, checked through the libraries' widened hooks, with an append-only audit log, in-memory and SQL adapters, and an admin API. This RFC is grantr's plan for v0.1: the design's seven phases, after a foundation, with the modules each phase adds, where its work lands, and what it waits on. The design itself is lattice RFC-0001's and is not repeated here.
Motivation¶
Lattice RFC-0001 is lattice's document, and its phases change artifactr, reflexr, relayr and stackr's template as well as grantr. grantr needs a plan in its own section that its pull requests reference and tick, and that says which work is grantr's.
Design¶
Modules¶
| Module | Holds | Phase | Extra |
|---|---|---|---|
grantr.core |
The model and its types (subjects, resources, roles, grants, memberships, visibility, the audit events), grantr's catalog, the built-in roles, the rules a check and a grant follow, and service principal keys, grantr_sk_<id>_<secret>, minted and verified by digest: no I/O |
2 | |
grantr.grants |
Grants, with check, hidden, member, memberships, resources and hooks, and the commands that change access, each audited; the storage port and its in-memory adapter, with one contract test for every adapter |
2 | |
grantr.sql |
The SQL adapter: SQLAlchemy, Alembic migrations and grantr_-prefixed tables in the application's schema |
3 | sql, sqlite, postgres |
grantr.fastapi |
grantr_router, with GET /grantr/v1/me and POST /grantr/v1/checks, and its OpenAPI document committed in schemas/ |
3 | fastapi |
grantr.telemetry |
Spans for checks over a threshold, counters of allowed and refused checks by action, and the audit events, through the OpenTelemetry API | 3 | |
grantr.testing |
matrix(catalogs, roles), every role against every action, which the template commits as a snapshot |
4 |
Phase 4 defers the on_tenant_created callback, which would create a tenant's gateway team and key: the template's gateway keys are settings, and stackr's make tenant creates the team (ADR-0021).
Lattice RFC-0001 names no phase for telemetry; it lands with the API in phase 3, since the refusals it counts are the router's as well as the hooks'. A module's name may change when it is built; grantr's architecture page, from phase 2, describes what exists.
Prerequisites¶
As of 2026-10-12:
| Prerequisite | Lands in | Needed by | State |
|---|---|---|---|
The widened hook: Access, ACTIONS, the hooks on Workspaces, on_behalf_of (lattice RFC-0001's phase 1) |
artifactr, reflexr | phases 4 and 5 | Done (#59, #61) |
reflexr's stored rules, whose allow phase 1 folds into the hook |
reflexr | phase 1 | Done |
| A tenant's workspaces listed (#5) | artifactr | none; whether its listing comes from grantr is an unresolved question | Open |
Phases 1 and 2 are independent, so grantr's core is built against the hook's shape in lattice RFC-0001, not its code: it reads an Access's fields by name, and takes actors as they come.
Phases¶
| Phase | Deliverable | Lands in | Waits on | Exit criteria |
|---|---|---|---|---|
| 0. Foundation | The package, its tooling and release configuration, its site section, ADRs for D1 to D9, and this plan | grantr | Nothing | CI green at 100% coverage, and the site builds in strict mode |
| 1. The hook | Access, the widened Authorize, OnOpen and ACTIONS, held by Workspaces; on_behalf_of; a check in every Workspace method; StoredRules.allow folded in; one author per turn; the matrix tests; the template's mirrors on on_open |
artifactr, reflexr and stackr's template | Nothing | Every library's matrix test passes, and a template application behaves as before with an allow-all hook |
| 2. grantr's core | grantr.core and grantr.grants: the model and types, built-in and custom roles, groups, service principals and keys, check, resources, hooks and SubjectOf, the audit log, and the in-memory adapter, with the contract test |
grantr | Nothing | In the contract test, a viewer is refused an edit, a key can't reach another tenant, and no one grants more than they hold |
| 3. Storage and API | grantr.sql and its migrations; grantr.fastapi and its committed OpenAPI document, with the list reads the router needs (a tenant's members, groups, grants and keys, and every tenant for operators); grantr.telemetry |
grantr | Phase 2 | The contract test passes on SQLite and PostgreSQL, and the drift test passes |
| 4. The template | The access question, tenancy from memberships, keys, make tenant, make operator, make migrate-tenants; grantr.testing and the matrix snapshot |
stackr's template and grantr | Phases 1 to 3 | A generated application passes its CI, and the smoke test signs a user in to one of two tenants and is refused the other |
| 5. Threads and artifacts | Visibility and Hidden in artifactr, every read path filtered, the restricted flag in grantr, and turns that act for their user |
artifactr and grantr | Phases 1 to 4 | A restricted thread is absent from every surface for a non-member, including the agent's tools in another user's turn |
| 6. Row-level security | The option in artifactr's, reflexr's, relayr's and grantr's SQL adapters, the second engine, and the template's non-owner role | artifactr, reflexr, relayr, grantr and stackr's template | Phase 3 | With the libraries' tenant scoping bypassed in a test, PostgreSQL returns no other tenant's rows, and reflexr's scheduler still runs every tenant's due runs |
| 7. Docs and ADRs | grantr's guides, reference and architecture, the ADRs for the amendments, and grantr's README | grantr, and the amended packages' sections | Phases 1 to 6 | grantr's section builds, with the architecture updated |
Each phase is a series of small pull requests to main, and a phase that lands in grantr brings its guides, reference pages, architecture and ADRs with it. Phase 2's hidden hides nothing: the restricted flag it reads comes in phase 5.
Drawbacks¶
- Half the plan lands elsewhere. Phases 1, 4, 5 and 6 change artifactr, reflexr, relayr and stackr's template, so this checklist tracks work its own pull requests don't contain.
- The core comes before the hook it answers. Phase 2 builds
hooksagainst the hook's shape in the design, and phase 1 may refine names that phase 4 then adapts to.
Alternatives¶
Lattice RFC-0001 weighs the engine, where membership lives and where access is checked, in its Alternatives and Decisions. For the plan itself, tracking grantr's work in lattice RFC-0001's checklist alone would keep one list, but grantr's own pull requests and issue couldn't then say which phases are grantr's.
Unresolved questions¶
Lattice RFC-0001's own open questions stand: a stored rule's grants at install, ending a live connection on revocation, and workspaces without a library behind them. One more is the libraries':
- How the libraries accept grantr's
Visibility. grantr imports neither library, so itshiddenreturns its ownVisibility, with the same fields as artifactr's. Phase 5 types artifactr'sHiddenso that grantr's answer satisfies it.
Tracking¶
The tracking issue is #47.
- Phase 0: foundation
- Phase 1: the hook (artifactr, reflexr and stackr's template)
- Phase 2: grantr's core (ADR-0010 to ADR-0015)
- Phase 3: storage and API (ADR-0016 to ADR-0018)
- Phase 4: the template (ADR-0019 to ADR-0022; stackr ADR-0019)
- Phase 5: threads and artifacts
- Phase 6: row-level security
- Phase 7: docs and ADRs (ADR-0001 to ADR-0009 recorded in phase 0)