Skip to content

Quick Start

This is the shortest path to a working permission check. It uses the default settings; see Project Setup for customization.

1. Initialize Your Project

melange init -y

This creates a melange/ directory with a config file and starter schema:

myproject/
├── melange/
│   ├── config.yaml
│   └── schema.fga
└── ...

The default schema defines organizations with role hierarchies and repositories with inherited permissions:

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
See Project Setup for details on starter templates, migration strategies, and customization options.

2. Create the melange_tuples View

Melange reads authorization data from a SQL view called melange_tuples. This view maps your existing tables into tuples. Create it in your database:

CREATE OR REPLACE VIEW melange_tuples AS
-- Organization memberships
SELECT
    'user' AS subject_type,
    user_id::text AS subject_id,
    role AS relation,                -- 'owner', 'admin', or 'member'
    'organization' AS object_type,
    organization_id::text AS object_id
FROM organization_members

UNION ALL

-- Repository -> Organization relationship
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

UNION ALL

-- Direct repository owners
SELECT
    'user' AS subject_type,
    user_id::text AS subject_id,
    'owner' AS relation,
    'repository' AS object_type,
    repository_id::text AS object_id
FROM repository_owners;
This view queries your existing tables directly, so there is no separate tuple store to keep in sync. See Creating Your Tuples View for a detailed walkthrough.

3. Apply Migrations

melange migrate

This compiles your schema into PostgreSQL functions and installs them.

To preview the generated SQL without applying:

melange migrate --dry-run

4. Check Permissions

The migration installed a check_permission function. Try it in SQL:

-- Returns 1 (allowed) or 0 (denied)
SELECT check_permission('user', 'alice', 'can_read', 'repository', '42');

Or from application code:

import (
    "context"
    "database/sql"
    "github.com/pthm/melange/melange"
)

db, _ := sql.Open("postgres", "postgres://localhost/mydb")

checker := melange.NewChecker(db)
user := melange.Object{Type: "user", ID: "alice"}
repo := melange.Object{Type: "repository", ID: "42"}

allowed, err := checker.Check(context.Background(), user, "can_read", repo)

Install the runtime library (stdlib-only, no external dependencies):

go get github.com/pthm/melange/melange

What melange migrate Did

  1. Parsed your OpenFGA schema
  2. Analyzed each relation to detect patterns (direct assignment, role hierarchy, parent inheritance)
  3. Generated a SQL function for each type+relation pair (e.g., check_repository_can_read)
  4. Installed a dispatcher function (check_permission) that routes to the correct per-relation function

Permission checks are SQL function calls. There is no external service, no network round-trip, and no tuple synchronization.

Next Steps