Skip to content
Melange v0.10.1

Melange v0.10.1

October 21, 2026·pthm
pthm

Melange v0.10.1 makes melange migrate safe to run on every application startup: concurrent runs are serialized, and an older melange no longer overwrites functions generated by a newer one. The CLI now warns when it, or the functions deployed in your database, are covered by a published security advisory. Windows users can install with Scoop, and each release is now checked by installing it through every channel.

Upgrading

v0.10.1 changes no generated SQL and no Go or TypeScript runtime APIs. Running melange migrate is optional.

The downgrade guard described below only runs in melange v0.10.1 and later. During the rolling deploy that brings in v0.10.1, replicas still on v0.10.0 can still re-apply their functions, as before. From the next upgrade on, every replica has the guard.

If a startup script runs melange migrate && ./app, read exit code 5 before your next upgrade.

Safe concurrent migrations

The docs recommend running melange migrate when your application starts, but concurrent runs were unprotected:

  • On a fresh database, replicas starting together could fail with a unique-violation (23505) while racing to create melange_migrations.
  • During a rolling deploy, a replica still on the old version saw a newer codegen version, re-applied its older generated SQL over the upgraded functions, and undid codegen fixes. Old and new replicas then kept overwriting each other as they restarted.

Every migration now takes a transaction-scoped advisory lock on the target schema before touching the tracking table. Under the lock it re-reads the latest migration record, so the first replica applies and records the migration and the rest see that record and skip. The lock uses PostgreSQL’s two-key form, so it cannot collide with your application’s own single-key advisory locks.

Refusing downgrades

migrate now refuses to replace functions generated by a newer melange:

Error: migration refused: database functions were generated by melange 0.11.0, newer than this melange (0.10.1); nothing was applied (upgrade melange, or force the migration to downgrade deliberately)

The refusal exits with code 5. The newer functions keep the same SQL API, so an older replica can keep serving, and a startup script can treat code 5 as success:

melange migrate || [ $? -eq 5 ]

Code 5 is non-zero on purpose: melange migrate && ./app stops an old replica instead of silently skipping. Pass --force to downgrade deliberately. Builds whose versions cannot be ordered are never refused: dev builds and local git describe builds such as v0.10.0-3-gabc1234.

Go users of pkg/migrator get the same lock and guard. A refusal returns *migrator.DatabaseAheadError.

The guard compares codegen versions only. An old replica with an older schema but the same melange version still re-applies its schema; --if-deployed-checksum covers that case. See Concurrent migrations and downgrades.

Security advisory warnings

Dependabot and govulncheck only see Go modules listed in a go.mod. They miss a CLI installed with Homebrew, Scoop, a package or go install. They also miss a database whose generated functions predate a fix: upgrading the CLI clears the alert, but the functions stay vulnerable until melange migrate regenerates them.

The background update check now also fetches the repository’s published security advisories and caches them alongside the version check:

  • CLI: when the running version is covered, a warning listing each advisory appears above the update notice.
  • melange doctor: warns under Deployed Model when the melange version recorded with the deployed functions is covered, and tells you to upgrade and run melange migrate.
  • melange status: adds a Security line, and an advisories field to --format json.

Advisories follow the update check’s rules: off in CI and with --no-update-check, and cached for 24 hours. A slow or failed advisory fetch delays the update notice by at most half a second. doctor and status wait up to three seconds for it, then skip the check.

Installation and updates

  • Scoop on Windows:

    scoop bucket add pthm https://github.com/pthm/scoop-bucket
    scoop install pthm/melange
  • The right update command. The update notice now prints the command for how you installed melange: brew upgrade pthm/tap/melange, scoop update melange, go install github.com/pthm/melange@latest, or a link to the update instructions. It used to suggest Homebrew or go install to everyone.

  • Floating Docker tags. Images are also tagged vX.Y, which follows patch releases: ghcr.io/pthm/melange:v0.10. A vX tag will start at 1.0.

  • Build provenance. Release archives, packages, checksums.txt and the container image carry GitHub attestations. Check them with gh attestation verify <file> --repo pthm/melange.

  • Tested installs. After each release, CI installs the new version with Scoop on Windows, Homebrew on macOS, and the .deb and container image on Linux, then checks that melange version reports it.

  • Update-check cache location. The cache now lives in the operating system’s cache directory: %LocalAppData% on Windows and ~/Library/Caches on macOS, rather than ~/.cache everywhere. XDG_CACHE_HOME still overrides it. The old ~/.cache/melange directory on those systems can be deleted.

The installation guide is reorganised by platform, covers every channel, and has a section on the Go and TypeScript client libraries.

Fixes

  • No stray characters on your prompt. Every command used to query the terminal’s background colour, and a reply that arrived after a short command exited was echoed onto the shell prompt. Only the interactive melange init wizard queries it now.
  • Consistent colour. doctor and status are now coloured like explain and expand, and every command follows the same rules: plain when output is piped, and NO_COLOR, CLICOLOR_FORCE and TERM=dumb are respected.
  • melange init on Windows wrote the config path into AGENTS.md with a backslash (melange\config.yaml). Paths in the agent instructions now always use forward slashes, since the file is committed and read on every OS.

Documentation

  • Replacing Conditions: Melange rejects OpenFGA conditions, but most can be expressed another way. Conditions on stored data (expiry, status) become a WHERE clause in melange_tuples. Request-only data becomes contextual tuples. Request data compared with stored data goes through a transaction-local setting read by the view.
  • The Performance reference is rebuilt on measurements from Cloud SQL, with check latency split into database and network time, and list costs by query shape. Earlier figures were local, and mostly measured Docker networking.
  • Docs pages and release posts have a copy menu that copies a page’s Markdown or opens it in Claude or ChatGPT.

Try it out

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

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

See the installation guide for every channel.

Feedback

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