# OpenFGA integration — authorization layer over the auth substrate

w4 m1–m3 shipped **authentication**: every caller is a known identity (Kratos) or API key (Hydra client), resolved into the request context via `api.IdentityFrom` (`operator/internal/api/auth.go`). But **authorization** is still all-or-nothing — any authenticated identity can do anything (list/mint/revoke every key, touch every App/Database).

Idea: add OpenFGA (Zanzibar-style ReBAC, CNCF sandbox, Apache-2.0 — passes the same governance test as CNPG/Ory) as the authorization layer:

- **Deploy**: GitOps base component like Kratos/Hydra — pinned `openfga/openfga` Helm chart, its own CNPG database (mirror `charts/auth-dbs/` shape, e.g. add `openfga-db` there), cluster-internal only (no public ingress — bex-api is the only caller).
- **Model**: tenant → member/admin relations; App/Database/api-key objects owned by tenants; `can_view / can_manage / can_mint_keys` permissions. Model file checked into git, applied idempotently (same out-of-band pattern as `scripts/auth-secrets.sh`).
- **bex-api**: a `Checker` seam on Core (like `APIKeyStore`/`PodLogSource` — nil ⇒ allow-all, preserving today's behavior) consulted after the auth gate using `IdentityFrom`'s subject; fail closed when wired but unreachable.
- **Seed**: bex-bootstrap and existing identities get an initial tenant/admin tuple at deploy time (extend the deploy.yml auth steps).

Prereq/sequencing: pairs naturally with the control-plane tenants/accounts tables (w1/m2 t004) — OpenFGA stores *relations*, but what a "tenant" is comes from the control plane. Could ship model + checker seam + single-default-tenant first, real multi-tenant tuples when w1/m2 lands.

Definitely milestone-sized (deploy + model + seam + seeding + E2E) — promote via `/pm promote w4/001` when ready to schedule.

---

Promoted to milestone **w4/m4** (2026-07-06) — see `.pm/w4/m4/`.
