Planned Loam has no authentication or authorization on any listener yet; one plan will add both. This post covers the authorization design and the project it builds on.
OpenFGA is an authorization service inspired by Google's Zanzibar paper. It answers one question very fast and at scale: does this user have this relation to this object? Loam plans to use it as its fine-grained authorizer, shared with Lakekeeper, and as the authority behind every token in the function runtime.
| Repository | openfga/openfga |
| Docs | openfga.dev |
| License | Apache-2.0 |
| Version checked | v1.21.0 |
| Foundation | CNCF, incubating since October 2025 |
| Rust client | openfga-client 0.6, Apache-2.0, maintained by Vakamo (Lakekeeper's vendor) |
| In Loam | Planned |
How OpenFGA works
Authorization data is a set of relationship tuples: (user, relation, object), such as (user:ana, member, org:acme) or (org:acme#member, reader, namespace:acme/docs). An authorization model says what the relations mean, in a small DSL:
model
schema 1.1
type user
type org
relations
define member: [user]
define admin: [user]
type namespace
relations
define org: [org]
define reader: [user, org#member] or writer
define writer: [user] or admin from orgRelations compose. reader above is anyone granted directly, any member of an org granted as a group, or anyone who is a writer. admin from org follows the namespace's org relation and takes that org's admins (a tuple-to-userset rewrite, in Zanzibar's terms). A Check walks those rules over the stored tuples to answer yes or no. ListObjects answers "which namespaces can Ana read?", ListUsers answers "who can read this namespace?", and BatchCheck answers many checks in one call. Tuples live in Postgres, MySQL or SQLite. Reads can ask for lower latency or for higher consistency right after a write.
The appeal is that authorization becomes data plus a model instead of code scattered across services: sharing, groups, inheritance and delegation are tuples, and one query answers them the same way everywhere.
Why Loam plans to use it
Loam's tenancy has organisations, members, teams, projects, environments (namespaces), agents and service accounts, and it has to support delegated grants in the managed cloud and in customers' own clusters. Role-based access control covers simple installs, and Loam will ship built-in roles. Multi-org tenancy, "share this collection with that team", and agents that may touch only what their owner allows want relationships.
OpenFGA is Apache-2.0, CNCF-hosted, runs as a separate service, and has a proven model to start from: Lakekeeper, the Iceberg catalog Loam will use for tables, already authorizes with OpenFGA. Sharing one OpenFGA store with Lakekeeper means one grant covers a namespace's collections and its Iceberg tables.
How Loam would use it
An Authorizer trait in the engine, in the style of Lakekeeper's, that every surface calls: check(principal, action, resource), batch_check, filter_visible(list), lifecycle hooks when orgs, namespaces, collections and API keys are created or deleted, and bootstrap. Three implementations:
| Implementation | For |
|---|---|
AllowAll | Development and single-user installs |
Rbac | Built in: roles grant read, write or admin over namespaces and collections |
OpenFga | Fine-grained, delegated grants for multi-org tenancy and BYOC; the default in the managed cloud |
The model. Loam adapts Lakekeeper's modular model (its server, project, warehouse and table become Loam's platform, org, namespace and collection), copied with attribution under Apache-2.0 and with its notice kept. Loam adds a tenant fence Lakekeeper's model lacks: every grant is effective only when the subject is also a member of the owning tenant, so no tuple, however it was written, can make a subject from another org effective.
Tuple writes through an outbox. Lakekeeper writes tuples before its database commit and deletes them after it, which can orphan tuples when a commit fails. Loam writes each tuple change as a row in the same metastore or control-store transaction as the resource change, and a worker drains the outbox with idempotent writes. A reconcile job repairs any drift. So a resource and its permissions never disagree for longer than the drain.
Reads go through a decision cache in the gateway with a short TTL (at most five seconds), with higher-consistency reads right after a principal's own writes.
Beyond the engine. In the function runtime, per-invocation Biscuit tokens carry only narrowing facts (tenant, function, allowed operations) and OpenFGA remains the authority for every decision on data. In the Loam Commons showcase, one OpenFGA model spans every app (a project fans out to a Loam namespace, repositories, chat channels and error projects), and each app's own permissions are a projection reconciled from it. In the Neon spike, OpenFGA's migrations ran unchanged on Neon, and a check allowed the member and denied the non-member.
Limits
- A network hop per decision, which the cache and batch checks mitigate. Search results filtered per user (
filter_visibleover thousands of candidates) are the expensive case, to be measured before it ships. - One more service with its own database. OpenFGA needs Postgres or MySQL in production. Whether Loam's and Lakekeeper's model migration managers can share one store cleanly is an open question.
- Tuples are only as correct as their writes. The outbox and the reconcile job exist because dual writes drift.
- Consistency is a choice per read. Default reads favour latency; a grant followed immediately by a check needs the higher-consistency read.
The next post covers the engines Loam plans to run beside it rather than inside it: Sail, RisingWave, Neon and WeSQL.