Architecture
Big picture
OpenFGA is a single Go binary that exposes a gRPC API and an HTTP gateway. A request lands on a transport handler, passes through middleware (validation, authentication, API authorization), reaches a server handler, which delegates to a transport-independent command, which drives the graph resolution engine, which reads relationship tuples from a pluggable datastore. The CLI in cmd/ wires this together and starts the server.
Components
CLI and server bootstrap (cmd/)
cmd/openfga/main.go:14-26 builds a Cobra root command and attaches the run, migrate, validate-models, and version subcommands. The server itself starts in cmd/run/run.go, which loads configuration, opens the datastore, and handles graceful shutdown. migrate applies database schema migrations.
Transport handlers (pkg/server/)
pkg/server/ holds the gRPC/HTTP handlers: one file per API surface (check.go, batch_check.go, list_objects.go, list_users.go, expand.go, write.go, read.go). An AuthZEN-compatible endpoint lives in pkg/server/authzen.go. Handlers do request validation and API authorization, then hand off to a command.
Commands (pkg/server/commands/)
pkg/server/commands/ is the business-logic layer decoupled from transport. CheckQuery.Execute (check_command.go:102) builds the internal resolve request and invokes the resolver.
Graph engine (internal/graph/)
internal/graph/ is the core. LocalChecker resolves a check by walking the model's rewrite rules and reading tuples. Resolvers are composed into a chain (caching, dispatch throttling, shadow evaluation) built in builder.go.
Type system (pkg/typesystem/)
pkg/typesystem/ parses and validates authorization models and builds a weighted graph of the model for query-path optimization. The TypeSystem carries an authzWeightedGraph field (typesystem.go:184).
Storage (pkg/storage/)
pkg/storage/ defines the OpenFGADatastore and RelationshipTupleReader interfaces (storage.go:152) with implementations for in-memory, PostgreSQL, MySQL, and SQLite.
How a request flows
Tracing a Check call end to end:
pkg/server/check.go:37(*Server).Checkstarts a trace span, validates the request, and runs API authorization viacheckAuthz(check.go:63).- If the
ExperimentalWeightedGraphCheckflag is set for the store, it tries the v2 paths.v2Checkand falls back to v1 on non-timeout errors (check.go:69-152). - The v1 path builds the resolver chain with
getCheckResolverBuilder(storeID).Build()(check.go:156), resolves the model (check.go:162), constructsNewCheckCommand(check.go:168), and callscheckQuery.Execute(check.go:182). (*CheckQuery).Execute(check_command.go:102) builds aResolveCheckRequest, wraps the datastore with a per-request tuple cache, and callsc.checkResolver.ResolveCheck(check_command.go:150).(*LocalChecker).ResolveCheck(internal/graph/check.go:395) checks resolution depth, detects cycles, short-circuits self-defining tuples, prunes unreachable paths withPathExists, then evaluates the relation's rewrite rule viaCheckRewrite(check.go:465).
The internals page walks these branches in detail.
Key design decisions
- Stateless engine, pluggable storage. All authorization state lives behind the
RelationshipTupleReaderinterface (storage.go:152), so instances scale horizontally and storage is swappable. - Resolver chain as a circular linked list. Resolvers are ordered least-to-most expensive, and the last delegates back to the first (
builder.go:66-104). TheCheckResolvercontract requires thatDelegate.ResolveChecknot cause infinite recursion (interface.go:13-40). - Consistency is tunable per request. Unless a request asks for
HIGHER_CONSISTENCY, the engine consults a cache-invalidation timestamp and may serve from cache (check_command.go:110-112). Strong consistency is opt-in. - Online strategy selection. Rather than a fixed rule for which resolution strategy to use, the planner in
internal/planner/learns per query path with Thompson Sampling.
Extension points
- Datastore drivers implement
OpenFGADatastore/RelationshipTupleReader(pkg/storage/storage.go:152). The SQLite and MySQL adapters were community contributions. - Authentication supports none, pre-shared key, and OIDC (
internal/authn/). - AuthZEN endpoint (
pkg/server/authzen.go) exposes an OpenID AuthZEN-compatible API on top ofCheck.