Skip to content
Melange v0.10.2

Melange v0.10.2

November 5, 2026·pthm
pthm

Melange v0.10.2 fixes list_accessible_objects and list_accessible_subjects in two families of models. Where a parent relation allows more than one type, such as parent: [folder, project], list_accessible_objects could miss every object under the second type. Where a tuple-to-userset links an object to its own type, such as parent: [folder] on folder, the list functions could miss objects or return access that check_permission denies. check_permission was correct throughout. Code generation is also faster on large models, and the CLI and Go libraries now build with Go 1.26.

Upgrading

v0.10.2 changes generated SQL for affected models. Run melange migrate after upgrading. The codegen version changes with every release, so the functions are regenerated even if your schema is unchanged.

You are affected if you call the list functions and your model has a tuple-to-userset (x from parent) where either:

  • the linking relation allows more than one type and the lookup sits in a union with other access, as in viewer: [user] or viewer from parent with parent: [folder, project], or
  • the linking relation can point at the object’s own type.

check_permission and the Go and TypeScript Check APIs were not affected.

If you use the list functions to make access decisions, for example in row-level security policies or to filter query results, upgrade promptly: three of the fixed shapes returned access that check denies.

Parents with more than one type

Since v0.6.7, a tuple-to-userset in a union only found objects whose parent was the first type its linking relation listed:

type document
  relations
    define parent: [folder, project]
    define viewer: [user] or viewer from parent

list_accessible_objects returned documents in folders the user can view, but none in projects, because a quoting bug dropped every type after the first from the generated query. A lookup on its own (viewer: viewer from parent) was not affected, and neither was list_accessible_subjects.

Same-type parents

The reported case is a one-hop lookup on the parent of the same type:

type organization
  relations
    define parent: [organization]
    define documents_user: [user]
    define program_documents_user: [user]
    define document_editor: documents_user or program_documents_user from parent

program_documents_user from parent reads the relation on the direct parent only: a grandchild gets nothing from its grandparent. The list functions treated it as recursion because the parent has the same type as the object. With a chain P → C → G and program_documents_user granted on P, list_accessible_objects returned nothing instead of C, and list_accessible_subjects on G returned the grantee from P.

Looking for the rest of this class turned up more cases. Each shape below now matches check_permission:

ShapeWas
x: [user] or y from parent, with y not recursive (the reported case)objects missed; subjects over-reported
r: v but not blocked, with v: [user] or v from parent and blocked held on an ancestorobjects missed below the ancestor
r: ([user] or r from parent or both) but not c, with both: c and aobjects over-reported below an excluded object
Two linking relations, such as v from parent and a from ownersubjects over-reported through mixed chains; objects missed through the second linking relation
r: y from parent, with y only an alias (y: b)both lists returned nothing
r: y from parent and (r from parent or y from parent), with y an aliassubjects returned nothing

r: v but not blocked is the common “viewer unless blocked” pattern. It was only wrong when blocked was granted on an ancestor rather than on the object itself.

Testing

Melange’s generated differential tests compare Check, ListObjects and ListUsers against a real OpenFGA server, but they only ever used parents of a different type. A new generator builds models with a same-type parent, plus a variant with a second linking relation, and found five of the same-type cases above. Each fix also adds a regression test, validated against OpenFGA.

Faster code generation

Generating SQL for large models grew faster than the model did. The relation graph is now built once per run rather than walked again for every safety check, and each function filters only the closure rows for its own type.

ModelBeforeAfter
200 types644 ms231 ms (−64%), half the memory
50 types93 ms59 ms (−37%)
Kitchen-sink schema12.9 ms11.9 ms (−8%)

These are compile-time numbers for melange migrate and melange generate. The generated SQL was byte-identical across 250 models before and after this change. The SQL changes in this release come from the list fixes above.

Go 1.26

The CLI and the Go library packages (pkg/migrator, pkg/compiler and the rest of the root module) now declare go 1.26.0, down from 1.27.3. That is the lowest version their dependencies allow. Each go.mod also names a toolchain, so melange itself is still built and tested with the latest Go.

  • Importing melange’s packages needs Go 1.26 or later.
  • go install github.com/pthm/melange@v0.10.2 works from Go 1.21 or later: Go downloads the newer toolchain for the build. With GOTOOLCHAIN=local you need Go 1.26.
  • The runtime module, github.com/pthm/melange/melange, still supports Go 1.24.

CI now builds each published module with the oldest Go its go.mod names, so a newer standard library call can’t raise the floor unnoticed.

Release builds

Release binaries are built with -trimpath, so they no longer embed paths from the build machine and can be reproduced from the tagged source.

Documentation

  • Row-Level Security: enforce Melange permissions inside PostgreSQL with RLS policies built on check_permission and list_accessible_objects.
  • The homepage has an install section that picks the right command for your operating system.

Try it out

# Upgrade the CLI
brew upgrade pthm/tap/melange          # Homebrew
scoop update melange                   # Scoop
go install github.com/pthm/melange@v0.10.2

# Or pull the container image
docker pull ghcr.io/pthm/melange:v0.10.2

# Then regenerate your functions
melange migrate

See the installation guide for every channel.

Feedback

Open an issue for bugs or feature requests, and report security issues through private vulnerability reporting.