Melange
OpenFGA-to-PostgreSQL authorization compiler. Generates specialized SQL functions from .fga schemas that query your existing tables. Zero sync, always consistent.
Install / Use
/learn @pthm/MelangeREADME
Melange
<img align="right" width="300" src="assets/mascot.png">An OpenFGA-to-PostgreSQL authorization compiler.
Melange compiles OpenFGA authorization schemas into specialized PL/pgSQL functions that run directly in your PostgreSQL database. Like Protocol Buffers compiles .proto files into serialization code, Melange compiles .fga files into optimized SQL functions for Zanzibar-style relationship-based access control.
The generated functions query a melange_tuples view you define over your existing domain tables—no separate tuple storage or synchronization required.
Why Melange?
Traditional authorization systems require syncing your application data to a separate service. Melange takes a different approach: it's a compiler, not a runtime service.
How it works
Compile time — When you run melange migrate, the compiler:
- Parses your OpenFGA schema
- Analyzes relation patterns (direct, union, exclusion, etc.)
- Computes transitive closures for role hierarchies
- Generates specialized SQL functions for each relation
- Installs the functions into PostgreSQL
Runtime — Permission checks are simple SQL function calls:
check_permission()executes the generated functions- Functions query a
melange_tuplesview you define over your domain tables - PostgreSQL's query planner optimizes the specialized functions
This compilation model gives you:
- Always in sync — Permissions query your tables directly, no replication lag
- Transaction-aware — Permission checks see uncommitted changes in the same transaction
- Language-agnostic — Use from any language that can call SQL (Go, TypeScript, Python, Ruby, etc.)
- Optional runtime libraries — Convenience clients for Go and TypeScript, or use raw SQL
- Single query — Role hierarchies resolved at compile time, no recursive lookups at runtime
Inspired by OpenFGA and built on ideas from pgFGA.
[!NOTE] 📚 Full Documentation Visit melange.sh for comprehensive guides, API reference, and examples.
Installation
CLI
Homebrew (macOS and Linux):
brew install pthm/tap/melange
Go install:
go install github.com/pthm/melange/cmd/melange@latest
Pre-built binaries: Download from GitHub Releases (macOS binaries are code-signed)
Updating:
# Homebrew
brew upgrade melange
# Go install
go install github.com/pthm/melange/cmd/melange@latest
Melange automatically checks for updates and notifies you when a new version is available. Use --no-update-check to disable.
Optional: Go Runtime Library
If you want to use the Go convenience library instead of raw SQL:
go get github.com/pthm/melange/melange
The runtime module has zero external dependencies (Go stdlib only).
Quick Start
1. Define Your Schema
Create a schema file (schema.fga) using the OpenFGA DSL:
model
schema 1.1
type user
type repository
relations
define owner: [user]
define reader: [user] or owner
define can_read: reader
2. Compile Schema into SQL Functions
Run the migration to generate specialized PL/pgSQL functions:
melange migrate --db postgres://localhost/mydb --schemas-dir ./schemas/
This generates optimized SQL functions like check_permission(), list_objects(), and specialized check functions for each relation.
3. Define Your Tuples View
Create a melange_tuples view that exposes your authorization data:
CREATE VIEW melange_tuples AS
SELECT
'user' AS subject_type,
user_id::text AS subject_id,
'owner' AS relation,
'repository' AS object_type,
repo_id::text AS object_id
FROM repository_owners
UNION ALL
SELECT 'user', user_id::text, 'reader', 'repository', repo_id::text
FROM repository_readers;
4. Check Permissions
With Go runtime (optional):
import "github.com/pthm/melange/melange"
checker := melange.NewChecker(db)
decision, err := checker.Check(ctx,
melange.Object{Type: "user", ID: "alice"},
"can_read",
melange.Object{Type: "repository", ID: "my-repo"},
)
if !decision.Allowed {
return ErrForbidden
}
Or use raw SQL from any language:
SELECT check_permission(
'user', 'alice',
'can_read',
'repository', 'my-repo'
);
-- Returns: true/false
5. (Optional) Generate Type-Safe Client Code
For better type safety, generate constants and constructors:
melange generate client --runtime go --schema schema.fga --output ./authz/
import "yourapp/authz"
checker := melange.NewChecker(db)
decision, err := checker.Check(ctx,
authz.User("alice"),
authz.RelCanRead,
authz.Repository("my-repo"),
)
CLI Reference
melange - PostgreSQL Fine-Grained Authorization
Commands:
generate client Generate type-safe client code from schema
migrate Apply schema to database
validate Validate schema syntax
status Show current schema status
doctor Run health checks on authorization infrastructure
Generate Client Code
# Generate Go code
melange generate client --runtime go --schema schema.fga --output ./authz/
# With custom package name
melange generate client --runtime go --schema schema.fga --output ./authz/ --package myauthz
# With int64 IDs instead of strings
melange generate client --runtime go --schema schema.fga --output ./authz/ --id-type int64
# Only generate permission relations (can_*)
melange generate client --runtime go --schema schema.fga --output ./authz/ --filter can_
Supported runtimes: go (TypeScript coming soon)
Apply Schema to Database
# Apply schema
melange migrate --db postgres://localhost/mydb --schemas-dir ./schemas/
# Dry run (show SQL without applying)
melange migrate --db postgres://localhost/mydb --schemas-dir ./schemas/ --dry-run
# Force re-apply even if unchanged
melange migrate --db postgres://localhost/mydb --schemas-dir ./schemas/ --force
Validate Schema
melange validate --schema schema.fga
Check Status
melange status --db postgres://localhost/mydb
Health Check
melange doctor --db postgres://localhost/mydb --verbose
Using from Any Language
Melange generates standard PostgreSQL functions, so you can use it from any language that can execute SQL:
# Python
cursor.execute(
"SELECT check_permission(%s, %s, %s, %s, %s)",
('user', 'alice', 'can_read', 'repository', 'my-repo')
)
# Ruby
DB.fetch(
"SELECT check_permission(?, ?, ?, ?, ?)",
'user', 'alice', 'can_read', 'repository', 'my-repo'
).first
// TypeScript (with pg or any SQL client)
const result = await db.query("SELECT check_permission($1, $2, $3, $4, $5)", [
"user",
"alice",
"can_read",
"repository",
"my-repo",
]);
Optional Runtime Libraries
For convenience, Melange provides type-safe runtime clients:
| Language | Runtime Package | CLI Flag | Status |
| ---------- | --------------------------------- | ---------------------- | ----------- |
| Go | github.com/pthm/melange/melange | --runtime go | Implemented |
| TypeScript | @pthm/melange | --runtime typescript | Planned |
These libraries provide a nicer API but are completely optional. See clients/ for language-specific implementations.
Contributing
Contributions are welcome! Melange is still evolving and feedback, bug reports, and improvements are greatly appreciated.
Chat
If you have questions, want to discuss ideas, or need help getting started, join the Matrix room:
Matrix is the best place for:
- general discussion about the project
- design ideas or architecture questions
- quick help getting set up
Issues
For bug reports, feature requests, or design discussions, please open an issue:
https://github.com/pthm/melange/issues
When reporting a bug, please include:
- steps to reproduce
- expected vs actual behavior
- relevant logs or error messages
- environment details if applicable
Pull Requests
Code contributions are very welcome.
- Fork the repository and clone locally
- Create a branch for your changes
- Run tests with
just test - Submit a pull request with a clear description
Please ensure your code:
- Passes all existing tests
- Includes tests for new functionality
- Follows the existing code style
For larger changes or new features, it's often helpful to open an issue or start a discussion on Matrix first so we can align on the approach.
Resources
- Documentation — Guides, API reference, and examples
- OpenFGA — The authorization model Melange implements
- Zanzibar Paper — Google's original authorization system
- pgFGA — PostgreSQL FGA implementation that inspired th
