Zanzibar-Style Authorization for PostgreSQL
Fine-grained, relationship-based access control without an external service. Melange compiles OpenFGA schemas into SQL functions that run inside your existing database.
- MIT licensed
- PostgreSQL 14+
- Go and TypeScript clients
- SQL from any language
schema.fga
type repository
relations
define org: [organization]
define admin: [user]
define can_read: member from org or admin
Your application
SELECT check_permission(
'user', 'alice', 'can_read', 'repository', '123'
);
-- 1: alice is a member of the repository's orgFeatures
Pure PostgreSQL
Zero external dependencies. No sidecars, no services. Everything runs as SQL functions inside your database.
OpenFGA Compatible
Every Schema 1.1 feature except conditions. Use the same DSL and .fga files, validated against the official OpenFGA test suite.
Optimized for Speed
Measured checks took 0.4–0.7 ms end to end on same-zone managed Postgres, across fixtures from thousands to two million tuples.
No Tuple Sync
Permissions derived from views over your domain tables. Always in sync, transaction-aware, zero data duplication.
Transaction Aware
Permission checks see uncommitted changes within the same transaction. No eventual consistency, no stale authorization data.
Language Agnostic
Go and TypeScript client libraries included, or call SQL functions directly from any language. Works in triggers, RLS policies, or application code.
Latest release
Melange v0.11.0
Melange v0.11.0 lets you choose which part of the generated SQL API a database gets, and adds per-relation accessible_* functions for filtering your own queries by access. Paged lists now do the work of one page, generated functions are planned once per session, and depth limits and cycles follow OpenFGA. It also fixes two ways a check or a migration could grant more than the model says.
- Choose which relations and function families the SQL API installs, and filter your own queries with inlinable
accessible_*functions - Paged lists read only the page: a first page of 50 contacts on the SaaS benchmark went from 3.5 s to 6 ms
- Depth limits match OpenFGA, lists raise
M2002instead of truncating, and userset checks stop over-granting
How It Works
Melange is an authorization compiler. Like Protocol Buffers or GraphQL Code Generator, you define a schema and Melange generates optimized code tailored to your exact model. Instead of a generic runtime that interprets your model at query time, Melange generates purpose-built SQL functions for each relation in your schema. Role hierarchies are resolved at compile time and inlined into SQL, so runtime checks only recurse where your data does, such as nested parent folders.
Install
Install the CLI
The melange CLI is a single binary with no runtime dependencies, published for macOS, Linux, and Windows on amd64 and arm64. You only need PostgreSQL 14 or later when you run melange migrate.
brew install pthm/tap/melangeStep 1
Define Your Schema
Write your authorization model using the OpenFGA DSL. The same .fga files work with both Melange and OpenFGA, so there’s no vendor lock-in.
Melange supports direct assignments, computed usersets, unions, intersections, exclusions, tuple-to-userset, wildcards, userset references, and contextual tuples.
model
schema 1.1
type user
type organization
relations
define owner: [user]
define admin: [user] or owner
define member: [user] or admin
type repository
relations
define org: [organization]
define owner: [user]
define admin: [user] or owner
define can_read: member from org or admin
define can_write: admin
define can_delete: owner
Step 2
Query Your Existing Tables
Unlike traditional FGA systems, Melange doesn’t need a separate tuple store. Create a SQL view that maps your existing domain tables into tuples, and Melange queries them directly.
No data duplication. No sync jobs. Permissions are always consistent with your domain data, down to the current transaction.
CREATE VIEW melange_tuples AS
-- Organization memberships
SELECT
'user' AS subject_type,
user_id::text AS subject_id,
role AS relation,
'organization' AS object_type,
organization_id::text AS object_id
FROM organization_members
UNION ALL
-- Repository ownership
SELECT
'organization' AS subject_type,
organization_id::text AS subject_id,
'org' AS relation,
'repository' AS object_type,
id::text AS object_id
FROM repositories;Step 3
Compile to SQL Functions
melange migrate compiles your schema into a PostgreSQL function per relation and installs them, along with check_permission and the list functions that dispatch to them.
Role hierarchies are flattened at compile time. can_read inherits from admin, which inherits from owner, so the generated function matches all three in a single lookup instead of walking the graph at query time. Run it in CI or a deploy step, or use melange generate migration to emit SQL files for your existing migration tool.
# Compile and install the functions
melange migrate
# Or emit up/down SQL for your migration tool
melange generate migration --output db/migrationsStep 4
Check Permissions From Any Language
Once compiled, permission checks are simple SQL function calls. Use the Go or TypeScript client libraries for convenience, or call the generated functions directly from any language that can talk to PostgreSQL.
checker := melange.NewChecker(db)
allowed, err := checker.Check(ctx,
authz.User("alice"),
authz.RelCanRead,
authz.Repository("123"),
)Studio
Inspect Everything in Studio
melange studio opens a local, read-only console on any melange database. See who can access what, explain any decision step by step, try a grant before you write it, and read the SQL generated for every relation. It needs only a database connection.





Is Melange a Fit?
Good fit
An application backed by PostgreSQL that needs roles, sharing, teams, or folder-style hierarchies, and would rather not run and sync a separate authorization service.
Look elsewhere
Authorization shared across many services and databases, or attribute-based rules that need conditions.
Outgrow it? Move to OpenFGA
Your .fga files work unchanged in OpenFGA. Export your melange_tuples view as tuples and point your application at an OpenFGA server.
Coming from OpenFGA?
Keep your model, and replace the tuple store with a view over your tables.
Get started
Get Started in Minutes
Install the CLI, point it at your database, and check your first permission. The quick start takes you from an empty schema to a working check.
Terminal
# Initialize your project
melange init
# Apply schema and generate SQL functions
melange migrate
# Generate type-safe client code
melange generate client
# Inspect the database in a local web console
melange studio