Project Setup
This page covers the melange init wizard in detail: what each prompt does, how the starter templates differ, and how to customize the setup.
For the shortest path to a working setup, see Quick Start.
The Init Wizard
melange initThe wizard detects your project type and prompts for configuration values. If it finds a go.mod, it defaults to Go; if it finds a package.json (and no go.mod), it defaults to TypeScript. When both exist, Go takes precedence.
Wizard Prompts
The wizard collects these settings in order:
| Prompt | Default | Description |
|---|---|---|
| Schema path | melange/schema.fga | Where to put your authorization model |
| Starter model | Organization RBAC | Pre-built schema template |
| Database URL | postgres://localhost:5432/mydb | PostgreSQL connection string |
| Migration strategy | Built-in | How schema changes are applied |
| Generate client code? | Yes (if project detected) | Whether to set up type-safe code generation |
If you choose versioned migration strategy, you’ll also be asked:
| Prompt | Default | Description |
|---|---|---|
| Migration output directory | migrations/ | Where to write versioned SQL files |
| File format | Split | Separate up/down files, or combined |
| Migration name suffix | melange | Suffix for generated filenames |
If you enable client code generation, you’ll also be asked:
| Prompt | Default | Description |
|---|---|---|
| Runtime | Auto-detected | go or typescript |
| Output directory | internal/authz (Go) or src/authz (TypeScript) | Where to write generated code |
| Package name | authz | Go package name (Go only) |
| ID type | string | Type for object/subject IDs: string, int64, uuid.UUID |
Starter Templates
Four starter templates are available:
The default template. Models organizations with a role hierarchy 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: ownerCovers role hierarchies (owner > admin > member) and parent inheritance (member from org). A good starting point for SaaS applications with organizations, teams, and resources.
The Authorization Modelling guide covers additional relation patterns including exclusions, intersections, and wildcards.
Migration Strategies
The wizard asks you to choose how schema changes are applied to your database:
| Strategy | Command | Description |
|---|---|---|
| Built-in | melange migrate | Connects directly and applies SQL. Suited to solo projects, prototyping, and simple deployments. |
| Versioned | melange generate migration | Produces .sql files for your existing migration framework. Suited to teams, PR reviews, and CI/CD pipelines. |
Built-in connects directly to PostgreSQL and applies SQL functions. It tracks changes via a melange_migrations table and skips unchanged schemas automatically.
Versioned produces timestamped .sql files (e.g., 20240315_melange.up.sql) that you apply with your existing migration tool (golang-migrate, Atlas, Flyway, etc.). This means schema changes go through code review in PRs like any other migration.
You can change strategies later by updating your config file. See Running Migrations for details on comparison modes and CI workflows.
Generated Config File
After running melange init, the generated config file looks like this:
database:
url: postgres://localhost:5432/mydb
generate:
client:
id_type: string
output: internal/authz
package: authz
runtime: go
schema: melange/schema.fgaWith versioned migrations enabled:
database:
url: postgres://localhost:5432/mydb
generate:
client:
id_type: string
output: internal/authz
package: authz
runtime: go
migration:
format: split
name: melange
output: migrations/
schema: melange/schema.fgaConfig File Placement
The config file location depends on your schema path:
- Schema under
melange/(e.g.,melange/schema.fga): config written tomelange/config.yaml - Otherwise: config written to
melange.yamlat the project root
Melange discovers config files by searching up from the current directory. See Configuration Reference for the full discovery order and all options.
Non-Interactive Mode
Use -y to accept all defaults without prompting:
melange init -yOverride individual values with flags:
melange init -y \
--schema melange/auth.fga \
--db postgres://prod:5432/app \
--template doc-sharing \
--migration-strategy versionedSkip runtime dependency installation:
melange init -y --no-installAll Init Flags
| Flag | Description | Default |
|---|---|---|
-y, --yes | Accept all defaults without prompting | |
--no-install | Skip installing runtime dependencies | |
--schema | Schema file path | melange/schema.fga |
--db | Database URL | postgres://localhost:5432/mydb |
--template | Starter model: org-rbac, doc-sharing, minimal, none | org-rbac |
--runtime | Client runtime: go, typescript | Auto-detected |
--output | Client output directory | internal/authz (Go), src/authz (TS) |
--package | Client package name (Go only) | authz |
--id-type | Client ID type: string, int64, uuid.UUID | string |
--migration-strategy | Migration strategy: builtin, versioned | builtin |
--migration-output | Versioned migration output directory | migrations/ |
--migration-format | Versioned migration format: split, single | split |
--migration-name | Versioned migration name suffix | melange |
Client Code Generation
When code generation is enabled, melange init installs the runtime dependency automatically:
- Go:
go get github.com/pthm/melange/melange - TypeScript: Detected package manager (
bun>pnpm>yarn>npm) runsadd @pthm/melange
To generate client code after setup:
melange generate clientThis produces type-safe constants and constructors from your schema. For example, with Go:
import "myapp/internal/authz"
// Generated constants and constructors
allowed, err := checker.Check(ctx,
authz.User("alice"),
authz.RelCanRead,
authz.Repository("42"),
)The --filter option (configurable in generate.client.filter) lets you limit which relations are generated. For example, --filter can_ generates only relations starting with can_.
Next Steps
- Creating Your Tuples View: mapping your domain tables to authorization tuples
- Your First Migration: applying your schema and verifying the setup