ADR-0008: grantr's own model, with OpenFGA-shaped ports¶
Status: Accepted Date: 2026-10-12 Deciders: Alex Nodeland
Records decision D8 of lattice RFC-0001, identity and access.
Context¶
Authorization engines exist, and each answers checks and some answer list queries. grantr's model has four resource levels (the deployment, a tenant, a workspace, and a thread, artifact or rule) and no relations deeper than that, and lattice keeps a core with no I/O behind ports, with in-memory and SQL adapters and one contract test (artifactr ADR-0034).
Decision¶
- grantr has its own model, in a core with no I/O, behind a storage port with an in-memory adapter and an SQL adapter, and one contract test run against both.
- Its reads are shaped like OpenFGA's:
checklikeCheck, andhiddenandresourceslikeListObjects, so an OpenFGA adapter could be added if the model outgrows fixed levels. None is planned. - Decisions aren't cached across requests. A check is one indexed query; a request may reuse its own answers.
Options considered¶
| Option | For | Against |
|---|---|---|
| grantr's own model, with OpenFGA-shaped ports (chosen) | No server to run, testable in memory, one store for the grants and the data they protect | grantr is the place a mistake becomes a breach |
| OpenFGA or SpiceDB | Strong list queries, maintained | A server to run, and every grant written twice |
Cedar, through cedarpy |
Embeddable | Takes the entities on every call, and can't list what an actor may see |
| Casbin | Embeddable | Untyped configuration files, and weak filtering |
| Oso or Topaz | Embeddable, or self-hosted | Oso's library is deprecated for its cloud, and Aserto, which made Topaz, has shut down |
Consequences¶
- Easier: grantr runs wherever the libraries do, in memory in tests and on the application's database in production.
- Harder: the model is lattice's to get right, and the contract test is its guard.