Releases: pthm/melange
melange v0.8.2
v0.8.2
melange v0.8.1
Changelog
- c57bde2 chore(release): v0.8.1
melange v0.8.0
Adds modular model support (fga.mod / Schema 1.2) for splitting schemas across multiple files, configurable database schema for installing melange objects into a custom PostgreSQL schema, and safe identifier truncation to handle PostgreSQL's 63-byte name limit.
Warning
Generated SQL function names have been renamed with shorter suffixes (e.g., _no_wildcard to _nw). Running melange migrate will drop the old functions and install the new ones automatically. If you call specialized check or list functions directly (e.g., check_document_viewer_no_wildcard), you will need to update those references. If you only use the dispatcher functions (check_permission, list_accessible_objects, list_accessible_subjects), no changes are needed.
Highlights
Modular Models (fga.mod / Schema 1.2)
Schemas can now be split across multiple .fga files using OpenFGA's modular model format. Each module can define new types or extend existing ones. Melange merges them into a single schema at compile time.
# fga.mod
schema: '1.2'
contents:
- core.fga
- tracker/projects.fga
- wiki/spaces.fgaAll CLI commands accept fga.mod paths. Pass --schema fga.mod where you previously passed --schema schema.fga.
Configurable Database Schema
Melange objects can now be installed into a custom PostgreSQL schema instead of public, keeping authorization functions and tables separate from your application.
# melange.yaml
database:
url: postgres://localhost/mydb
schema: authzAlso configurable via MELANGE_DATABASE_SCHEMA env var or --db-schema CLI flag.
Safe Identifier Truncation
PostgreSQL silently truncates identifiers longer than 63 bytes, which can cause collisions or runtime failures with long type or relation names. Melange now handles this with shorter function suffixes and automatic truncation with an 8-character SHA-256 hash when names would exceed the limit.
| Old suffix | New suffix | Savings |
|---|---|---|
_no_wildcard |
_nw |
8 chars |
_subjects |
_sub |
5 chars |
_objects |
_obj |
4 chars |
Improvements
melange initnow asks whether to use the built-in migrator or versioned SQL file generation- Documentation site restructured with refreshed design and new homepage sections
- Schema qualification bug fixes for
DROP FUNCTIONstatements, identifier quoting, and per-schema validation - All Go dependencies updated to latest versions
Contributors
Thanks to @Desuuuu for implementing configurable database schema support.
Install / Upgrade
# CLI
brew install pthm/melange/melange
# Go runtime
go get github.com/pthm/melange/melange@v0.8.0
# TypeScript runtime
npm install @pthm/melange
# Apply migrations
melange migrateFull blog post: https://melange.pthm.dev/blog/v0.8.0/
melange v0.7.4
Adds the melange generate migration command for external migration framework integration, introduces two-phase skip detection to the built-in migrator, and includes new unit tests and documentation improvements.
Note
No breaking changes from v0.7.3. The built-in migrator automatically adds a function_checksums column to melange_migrations on the next melange migrate run.
Highlights
melange generate migration — External Migration Framework Support
Users with existing migration systems (golang-migrate, sqlx, Flyway, etc.) can now generate versioned UP/DOWN SQL files instead of running melange migrate directly. Inspired by River Queue's migrate-get command pattern.
# Full migration (first time or periodic snapshot)
melange generate migration \
--schema schemas/schema.fga \
--output db/migrations
# Diff migration against main branch (ideal for PRs)
melange generate migration \
--schema schemas/schema.fga \
--output db/migrations \
--git-ref mainThree comparison modes control which functions appear in the migration:
| Mode | Flag | Use Case |
|---|---|---|
| Full (default) | (none) | First migration or periodic full snapshot |
| Database | --db <url> |
Most precise — reads melange_migrations from a live database |
| Git | --git-ref <ref> |
No database needed — compares against a branch, tag, or commit |
| File | --previous-schema <path> |
Compares against a local backup of the last-deployed schema |
Function-level change detection uses SHA-256 checksums to include only changed functions in diff migrations. Dispatcher functions are always regenerated. Orphaned functions from removed relations receive DROP FUNCTION IF EXISTS statements.
Two-Phase Skip Detection in the Built-in Migrator
melange migrate now uses two-phase skip detection to avoid unnecessary database writes:
- Phase 1 (fast path): If both the schema checksum and melange version match the last migration, skip entirely — no SQL generation needed.
- Phase 2 (smart apply): If the schema or version changed, generate SQL and compare function checksums. If every function is identical and no orphans exist, skip re-applying and only record the new migration state.
This makes melange version upgrades efficient when the generated SQL hasn't actually changed.
Improvements
- Simplified config schema — redundant per-subcommand
schemafields removed in favour of a single top-levelschemafield - Added Migrations concept guide covering both the built-in migrator and external framework integration
- Documented the
melange generate migrationcommand in the CLI reference - Fixed all broken internal links across the documentation site
Testing & Maintenance
- Added unit tests for
pkg/migratorandpkg/clientgen - Added migration system integration tests covering two-phase skip detection
- Upgraded
google.golang.org/grpcfrom v1.78.0 to v1.79.3 - Enhanced README with Matrix chat link and contribution details
Install / Upgrade
# CLI
brew install pthm/melange/melange
# Go runtime
go get github.com/pthm/melange/melange@v0.7.4
# TypeScript runtime
npm install @pthm/melange
# Apply migrations
melange migrateFull blog post: https://melange.pthm.dev/blog/v0.7.4/
Melange v0.7.3
Adds melange init for interactive project scaffolding, fixes four list_objects/list_users bugs, and improves melange doctor with table index recommendations and orphaned tuple detection.
No breaking changes from v0.7.2. Upgrade and run melange migrate to pick up the list function fixes.
Highlights
New melange init command bootstraps a melange project with config, starter schema (four templates), and runtime dependency installation. Detects Go modules and Node.js packages automatically. Use -y to accept all defaults.
Bug Fixes
- list_objects returned results through non-recursive parent chains (#34) — Relations without from parent incorrectly propagated access to child objects. Thanks to @jtbeach for the fix.
- list_objects over-propagated when recursive and non-recursive relations share a permission — Non-recursive relations could still propagate through parent chains when combined with recursive ones in the same permission.
- list_objects and list_users ignored exclusions on userset lookups — Exclusions with userset patterns (e.g., member but not blocked via group#member) were not filtering out excluded subjects.
- list_users returned incomplete results for multi-path TTU relations — Only the first parent path was walked when a permission could be reached through multiple parent relations.
- melange doctor multi-table view parsing (#35) — Views joining multiple tables were incorrectly parsed, producing invalid CREATE INDEX suggestions. Thanks to @sgsfak for reporting.
Improvements
melange doctortable index recommendations — Checks for recommended indexes when melange_tuples is a table, with severity scaling by row count.melange doctororphaned tuple detection — Comprehensive validation of all tuples against the schema, replacing the previous sampling-based check. Detects unknown object types, relations, subject types, and invalid subject type assignments.
Testing & Maintenance
- Removed 1,000+ lines of dead code (#33)
- Added Codecov integration (#32)
- Added 22 new test cases covering real-world authorization patterns
Contributors
Melange v0.7.2
Adds performance linting to melange doctor and fixes two bugs reported by community contributors.
No breaking changes from v0.7.1. Upgrade and run melange migrate to pick up fixes.
Performance Linting in melange doctor
The doctor command now detects missing expression indexes for ::text casts in the melange_tuples view — the #1 production performance
issue, causing 26x slower queries at 1M tuples.
- Parses view definition via
pg_get_viewdef()to detectUNIONvsUNION ALLand enumerate source tables - Detects missing
::textexpression indexes per source table with severity based on row count and exactCREATE INDEXfix hints - Disable via
--skip-performanceflag ordoctor.skip_performanceconfig
Bug Fixes
-
Fix
list_objectsmissing results for closure userset patterns (#30) —list_*_objects
only expanded userset lookups for the first relation in a closure. When multiple relations shared the same userset pattern, only one got SQL
expansion. Thanks to @jtbeach for the fix. -
Fix uppercase relation names breaking re-migration (#26) — Schemas with uppercase relation
names caused functions to be dropped on re-migration due to a casing mismatch betweenCollectFunctionNamesand PostgreSQL's identifier
folding. Thanks to @Desuuuu for reporting and submitting an initial fix
(#27).
Contributors
Melange v0.7.1
Patch release with two bug fixes from community reports, security dependency upgrades, and testing improvements.
Bug Fixes
- Fix list_accessible_subjects returning empty results for TTU with implied parent relations (#22) — The parent closure optimization filtered tuples
by exact relation name, missing implied relations like can_read: member. Now expands the filter to include all satisfying relations. - Fix TypeScript client listObjects/listSubjects pagination (#21, #23) — 0 was sent as p_limit instead of NULL when no limit was specified, and
cursors read from a non-existent column name.
Security
- Upgraded TypeScript dev dependencies (vitest v2→v4, rollup override) to resolve 5 Dependabot-flagged vulnerabilities (no runtime impact).
Testing
- Derived list assertions automatically from check assertions for broader coverage
- Standardized hand-rolled SQL tests on OpenFGA YAML format
- Colocated parser unit tests with pkg/parser
Migration
No breaking changes. Run melange migrate and upgrade the TypeScript client if applicable.
Contributors
Thanks to @Desuuuu for reporting and fixing the TypeScript pagination issues and reporting the list_accessible_subjects bug.
Melange v0.7.0
Melange v0.7.0 introduces the Bulk Check API for batching multiple permission checks into a single SQL call, alongside multi-driver testing, module layout improvements, and a bug fix for intersection parsing.
Note
No breaking changes from v0.6.4. Upgrade and run melange migrate to install the new check_permission_bulk function.
Highlights
Bulk Permission Check API
The headline feature of v0.7.0 is check_permission_bulk (#11) — a new SQL function that evaluates multiple permission checks in a single database call. This is designed for the common pattern of checking many permissions at once, such as filtering a list of resources for display or rendering UI with permission-aware controls.
SELECT * FROM check_permission_bulk(
ARRAY['user', 'user'], -- subject types
ARRAY['alice', 'alice'], -- subject IDs
ARRAY['viewer', 'editor'], -- relations
ARRAY['document', 'document'], -- object types
ARRAY['doc-1', 'doc-2'] -- object IDs
);
-- Returns:
-- idx | allowed
-- -----+---------
-- 0 | 1
-- 1 | 0The generated SQL groups branches by object type with IF guards so that a batch checking only document permissions never touches folder branches, and simple direct-assignment relations are inlined as EXISTS subqueries to avoid function call overhead.
Go API
results, err := checker.NewBulkCheck(ctx).
Add(user, "viewer", doc1).
Add(user, "editor", doc2).
Execute()
if results.All() { /* all granted */ }
if results.Any() { /* at least one granted */ }TypeScript API
const results = await checker
.newBulkCheck()
.add(user, "viewer", doc1)
.add(user, "editor", doc2)
.execute();
results.all(); // true if all granted
results.allOrError(); // throws BulkCheckDeniedError if any deniedBoth clients include automatic deduplication, cache integration, decision overrides for testing, and a batch size guard (10,000 max).
Thanks to @sonalys for requesting this feature.
Bug Fix: Intersection with Tuple-to-Userset Unions
Fixed a bug (#13) where intersection rules containing unions of tuple-to-userset patterns were silently dropped during parsing — a permission bypass. The parser now correctly extracts both simple relations and tuple-to-userset patterns from unions and distributes them into intersection groups.
Thanks to @jake-wickstrom for reporting this.
Multi-Driver Integration Tests
Table-driven tests now run the same assertion suite against each supported driver:
- Go:
pgx/stdlibandlib/pq, plus transaction isolation - TypeScript:
pg(node-postgres) andpostgres.js
Module Layout and go install Fix
go install github.com/pthm/melange/cmd/melange@latest now works reliably. Fixed by removing the stale replace directive, renaming internal/ to lib/, and restoring cmd/melange as a proper sub-module.
What's Changed Since v0.6.4
- Bulk permission check —
check_permission_bulkSQL function, GoBulkCheckBuilder, TypeScriptBulkCheckBuilder - Bulk dispatcher optimization — branches grouped by object type with
IFguards, simple relations inlined asEXISTS - Intersection + TTU union bug fix — silently dropped tuple-to-userset unions in intersections (#13)
- Multi-driver tests — Go (
pgx,lib/pq) and TypeScript (pg,postgres.js) integration tests - Module restructuring —
internal/→lib/,cmd/melangesub-module restored,replacedirective removed go install @latestfixed — works reliably with the Go module proxy- CI improvements — race detection, TypeScript unit tests in CI, all-module linting, parallelism fix
- Documentation — TypeScript client examples, bulk check guide, SQL API reference
- Dependency update — OpenFGA v1.11.2 → v1.11.3
Install / Upgrade
# CLI
brew install pthm/tap/melange
# Go runtime
go get github.com/pthm/melange/melange@v0.7.0
# TypeScript runtime
npm install @pthm/melange@0.7.0
# Apply migrations
melange migrateFull blog post: https://melange.pthm.dev/blog/v0.7.0/
melange v0.6.7
Changelog
- 9125b72 chore(release): v0.6.7
melange v0.6.5
What's Changed
Security Fix
-
Fix intersection parsing for unions containing tuple-to-userset patterns (#14) - Fixes #13 reported by @jake-wickstrom
When parsing intersection rules like
viewer and (member from group or owner from group), the union containing TupleToUserset (TTU) patterns was silently dropped. This caused the generated SQL to only check theviewerrelation, ignoring the group membership requirement entirely.If you use intersection rules with
frompatterns in unions, upgrade immediately.
Other
- Fix release script to skip pushing root tag if goreleaser already pushed it
Full Changelog: v0.6.4...v0.6.5