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/