PostgreSQL schema evolution with built-in multi-agent coordination 🍓
Project description
Confiture 🍓
PostgreSQL migrations, sweetly done.
Build from DDL. Adopt on day one against a database that already has migrations applied. Preflight every deploy against a parallel database with structural diff. Sync production data with PII anonymization.
In 30 seconds
# 1. You already have a database at migration 004 (applied by hand or by another tool).
# Tell Confiture about that history without re-running the SQL:
$ confiture migrate baseline --through 004 -c db/environments/production.yaml
✅ 001 create_users (marked as applied)
✅ 002 create_orders (marked as applied)
✅ 003 add_user_email (marked as applied)
✅ 004 add_user_preferences (marked as applied)
✅ Marked 4 migration(s) as applied, skipped 0 already applied
# 2. Machine-readable proof that the tracking is healthy:
$ confiture migrate status -c db/environments/production.yaml --format json | jq '.applied | length'
4
# 3. Preflight: replay pending migrations on a parallel DB, emit a structural diff vs. db/schema/.
$ confiture migrate preflight --against "$PREFLIGHT_URL" -c db/environments/production.yaml
▸ Replaying pending migrations on preflight DB …
✓ 20260520143015_add_user_bio applied in 24 ms
▸ Comparing resulting schema vs. db/schema/ …
✓ No drift — preflight matches db/schema/
✓ Preflight passed. Safe to deploy.
exit 0
That's the loop. Baseline once → status to confirm → preflight every deploy.
Already have migrations?
The single biggest reason migration tools fail adoption is the day-one cliff: existing tables already exist, so any tool that tries to apply migrations from scratch crashes on the first CREATE TABLE. Confiture's answer is migrate baseline:
confiture migrate baseline --through <last-applied-version>
The walkthrough — including failure modes, the integration test that backs the recipe, and what tb_confiture ends up looking like — is in docs/guides/legacy-bootstrap.md.
No db/schema/ directory? That works too.
confiture migrate up, down, down-to, status, current, baseline,
and preflight are the migration runner — they don't require a db/schema/
directory (migrate current prints the latest applied revision as a narrow
"what's deployed?" contract; migrate down --steps N rolls back relatively
while migrate down-to <revision> rolls back to a specific revision, refusing
atomically if any required .down.sql is missing). The
"Build from DDL" pitch above the fold sells one of confiture's four
strategies; the other three (incremental migrations, production sync,
schema-to-schema FDW migration) work against a project whose only source
of truth is the migration chain itself.
If you're evaluating confiture against Flyway / Alembic / dbmate / sqlx-cli
as a pure migration runner, skip confiture build and use everything else.
Walkthrough: docs/guides/02-incremental-migrations.md.
When to use Confiture?
| Capability | Confiture | Flyway | Alembic | dbmate | sqlx-cli | plain psql |
|---|---|---|---|---|---|---|
| Source of truth | DDL files or migration chain | migration chain | model classes | migration chain | migration chain | DDL files |
| Tracking table | yes | yes | yes | yes | yes | no |
Rollback (down.sql) |
yes | paid | yes | yes | yes | no |
| Preflight against a copy DB | yes (structural diff) | no | no | no | no | no |
| Build from scratch in <1s | yes | no | no | no | no | yes (manual) |
| Production sync + anonymization | yes | no | no | no | no | no |
| Zero-downtime via FDW | yes | no | no | no | no | no |
| Multi-agent coordination | yes | no | no | no | no | no |
| Ecosystem maturity / stars | early | very mature | mature | mature | mature | n/a |
Note on "source of truth": confiture can run as a pure migration tool against a project that has no
db/schema/directory — the DDL workflow is opt-in. See Nodb/schema/directory? above.
Confiture wins on build-from-DDL, structural-diff preflight, production sync, and multi-agent coordination. It loses on ecosystem age — Flyway and Alembic have a decade of community knowledge. Pick honestly.
Adoption checklist
| Situation | Recommended tool |
|---|---|
| 1 environment + 1 contributor, schema rarely changes | plain psql |
| 2+ environments, schema changes weekly | Confiture, Flyway, Alembic, or dbmate |
| Multi-agent / AI-driven development on shared schemas | Confiture |
You have a migration chain (no db/schema/) and want preflight + tracking |
Confiture (use everything except confiture build) |
You want db/schema/ to be source of truth, not a migration chain |
Confiture |
You need zero-downtime schema swaps with postgres_fdw |
Confiture (Medium 4) |
| You're committed to SQLAlchemy ORM | Alembic |
| You're committed to a JVM stack | Flyway |
CI integration
A migrate preflight gate on every PR, a migrate up step on deploy. Exit codes are semantic, so the CI configuration stays simple:
# .github/workflows/db.yml
name: DB
on:
pull_request:
paths:
- 'db/**'
push:
branches: [main]
jobs:
preflight:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env: { POSTGRES_PASSWORD: x }
ports: ['5432:5432']
options: >-
--health-cmd pg_isready --health-interval 10s
--health-timeout 5s --health-retries 5
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
- run: uv pip install --system "fraiseql-confiture[ast]"
- name: Restore production snapshot to preflight DB
run: ./scripts/restore-snapshot.sh # your own; pg_restore from S3/GCS
- name: Confiture preflight
env:
PREFLIGHT_URL: postgresql://postgres:x@localhost:5432/preflight
run: |
confiture migrate preflight \
--against "$PREFLIGHT_URL" \
-c db/environments/preflight.yaml \
--format json --output preflight.json
- uses: actions/upload-artifact@v4
with:
name: preflight-report
path: preflight.json
deploy:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
- run: uv pip install --system "fraiseql-confiture[ast]"
# No YAML needed in CI — the migrate family reads DATABASE_URL directly
# (or pass --database-url "$DSN"). See the connection-source docs below.
- run: confiture migrate up
env:
DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}
migrate up/down/status/verify/preflight accept --database-url <dsn> (or read CONFITURE_DATABASE_URL / DATABASE_URL) so runtime-resolved DSNs need no temp YAML — precedence and details in the CLI reference.
Exit codes are a documented stability contract — see the exit-code reference. The most operationally important: 2 tracking table absent, 3 DB connection failed, 5 config invalid, 6 lock contention. For migrate preflight's drift-gate codes specifically, see the dry-run guide.
Migrations that open their own SAVEPOINTs, use
psycopg'sconn.transaction(), or wrapDO $$ … EXCEPTION WHEN … $$blocks are supported under all three modes. The rules a migration body must follow for the SAVEPOINT-based rollback to stay clean are documented in the transaction & SAVEPOINT contract.
Python project snippet
Add Confiture as a dev dependency. The [ast] extra pulls in pglast for full PostgreSQL parsing — recommended for schemas with bulk seed data.
# pyproject.toml
[dependency-groups]
dev = [
"fraiseql-confiture[ast]>=0.9",
"pytest>=8",
]
# justfile
default:
just --list
db-build:
confiture build --env local
db-up:
confiture migrate up
db-status:
confiture migrate status
db-preflight:
confiture migrate preflight --against "$PREFLIGHT_URL"
Or as a Makefile:
db-build:
confiture build --env local
db-up:
confiture migrate up
db-status:
confiture migrate status
Library API
Confiture is a CLI first, but the migrator is fully usable from Python:
from confiture import Migrator
with Migrator.from_config("db/environments/prod.yaml") as m:
status = m.status()
if status.has_pending:
result = m.up()
print(f"Applied {len(result.applied)} migrations")
The Four Strategies
| Strategy | Use Case | Command |
|---|---|---|
| Build from DDL | Fresh databases, testing, CI | confiture build --env local |
| Incremental Migrations | Existing databases, production | confiture migrate up |
| Production Sync | Copy data with PII anonymization | confiture sync --from prod --anonymize users.email |
| Zero-Downtime | Complex migrations via FDW | confiture migrate schema-to-schema |
Documentation
Start here
- Getting started — first 5 minutes.
- Legacy bootstrap guide — adopting on an existing database.
- Prerequisites — PostgreSQL version, roles, secret stores.
Guides
- Build from DDL
- Incremental Migrations —
up,down, rollback. - Production Data Sync
- Zero-Downtime Migrations
- Dry-Run + Preflight
- Replica-safe migrations —
replica_001/PFLIGHT_REPLICA_*forward-compatibility lint under streaming replication. - Bootstrap —
confiture bootstrapfor one-shot env ownership setup. - Superuser Migrations —
requires_superuser = True+migrate apply-asrecovery workflow. - Function Uniqueness —
func_001catches duplicateCREATE FUNCTIONacross DDL files. - Named Schemas
- Hooks
- Multi-Agent Coordination
Reference
- Tracking table (
tb_confiture) - Transaction & SAVEPOINT contract — what migration bodies may and may not do under the wrapping transaction.
- Exit-code convention — the stable, wrapper-facing exit codes.
- CLI
- Configuration YAML
- Complete feature list
For agents and tooling
- Every machine-readable CLI output has a published JSON schema under
docs/reference/json-schemas/— seedocs/reference/json-schemas.md. - On an error path in
--format jsonmode, the migrate family emits a structured error envelope on stdout —{"ok": false, "error": {code, message, severity, actionable, details, migration, file, line}}— and exits with the exit code for that error. The full code list and the envelope schema are in the error-code codebook. confiture migrate validate --list-patterns --format jsonexposes the full idempotency-detection catalog (read-only, no DB / config / migrations directory needed).- Quiet-success ambiguities surface advisory hints in
payload["hints"](or on stderr in text mode) — exit codes are unaffected.
Contributing
git clone https://github.com/fraiseql/confiture.git
cd confiture
uv sync --all-extras
uv run pytest
See CONTRIBUTING.md and CLAUDE.md.
Author & License
Vibe-engineered by Lionel Hamayon 🍓
MIT License — Copyright (c) 2025 Lionel Hamayon
Making jam from strawberries, one migration at a time. 🍓→🍯
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fraiseql_confiture-0.23.0.tar.gz.
File metadata
- Download URL: fraiseql_confiture-0.23.0.tar.gz
- Upload date:
- Size: 1.9 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cae2d4714629139e7aa31703cc2d656926806f2b18798b8b400fd152b42a0dad
|
|
| MD5 |
5f599bab3f9454fd2ff123e8aa0b70ad
|
|
| BLAKE2b-256 |
797a92bfcd0d80cf348bb28f84b515e3aeae9a37402378b051200edb6d2ac3bf
|
File details
Details for the file fraiseql_confiture-0.23.0-cp314-cp314-win_amd64.whl.
File metadata
- Download URL: fraiseql_confiture-0.23.0-cp314-cp314-win_amd64.whl
- Upload date:
- Size: 932.1 kB
- Tags: CPython 3.14, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef9c2877bcf6f905abffea4a76829654860795a8a43b25bbc893a625c47f4e4b
|
|
| MD5 |
bd986f0f9cd2de136679584e1191b1d9
|
|
| BLAKE2b-256 |
284defae4e4aeb3fbc9bbc90d5e6d5c4b0a86f31337d76a8061d0a54f01d1df6
|
File details
Details for the file fraiseql_confiture-0.23.0-cp313-cp313-win_amd64.whl.
File metadata
- Download URL: fraiseql_confiture-0.23.0-cp313-cp313-win_amd64.whl
- Upload date:
- Size: 932.1 kB
- Tags: CPython 3.13, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b357a751f9d07738a6b477f7825705d5cee225fe4358088c01e7443aa55c4d23
|
|
| MD5 |
3b0a4d55440fa663bdff1ca29faf1238
|
|
| BLAKE2b-256 |
03c9486cc2044acd7e63016e7f2cc157738cda2e9955b5c3d05635ba2f287369
|
File details
Details for the file fraiseql_confiture-0.23.0-cp313-cp313-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: fraiseql_confiture-0.23.0-cp313-cp313-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 1.0 MB
- Tags: CPython 3.13, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1cae11a849408d53b7459d91d46f8f16b76546acb806c7f7ae7891a938f9b02c
|
|
| MD5 |
e0f422a02cd5bf72dbd5a9c903e7e851
|
|
| BLAKE2b-256 |
f7772dc5c681491fd2812f736b63dbe752f07a721e7e7ccfb741623ba3a276ee
|
File details
Details for the file fraiseql_confiture-0.23.0-cp313-cp313-macosx_11_0_arm64.whl.
File metadata
- Download URL: fraiseql_confiture-0.23.0-cp313-cp313-macosx_11_0_arm64.whl
- Upload date:
- Size: 978.6 kB
- Tags: CPython 3.13, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
15846d87bd68ebc69933c43f04135a1383789d51ce5f5e7623acffed1677f8ef
|
|
| MD5 |
4354b95c0199d11f518a1be35ff60caa
|
|
| BLAKE2b-256 |
7c22a3513839f54807e70dc81e8303d41bd97a8ed828d9308028d9f36bd1cf4c
|
File details
Details for the file fraiseql_confiture-0.23.0-cp312-cp312-win_amd64.whl.
File metadata
- Download URL: fraiseql_confiture-0.23.0-cp312-cp312-win_amd64.whl
- Upload date:
- Size: 932.1 kB
- Tags: CPython 3.12, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1b22a45a036988a65a2075088a45bfd849d37d6c38d680acbb0826c3ce0a6a71
|
|
| MD5 |
5d9cc5220bdea6a8b76e21778cf121d3
|
|
| BLAKE2b-256 |
d2016a86e344e76ea6b1d6b20cd662a0de77bba18b017508f51dabde249ebf56
|
File details
Details for the file fraiseql_confiture-0.23.0-cp312-cp312-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: fraiseql_confiture-0.23.0-cp312-cp312-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 1.0 MB
- Tags: CPython 3.12, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c2f59b6d356feeb0baea8338dc5d84e6691fcd6a840af64e1c1673fc4ba4c58b
|
|
| MD5 |
9657bf92cc80e0aaa0b5e396bb04006b
|
|
| BLAKE2b-256 |
2394d8a21f565e4877db6424bbae0d7e471d781a0b27f07db1586ae3d33eb450
|
File details
Details for the file fraiseql_confiture-0.23.0-cp312-cp312-macosx_11_0_arm64.whl.
File metadata
- Download URL: fraiseql_confiture-0.23.0-cp312-cp312-macosx_11_0_arm64.whl
- Upload date:
- Size: 978.6 kB
- Tags: CPython 3.12, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c3eb756ebe1bb4a4675acb1ef4a800c736f1921956017652dd4e248fe82cdde2
|
|
| MD5 |
c0ce22c6ce2836c35eb194096887a5b7
|
|
| BLAKE2b-256 |
51bf4f76c932818b3ba92fb8cd1ce9c80267505292429250103149eaf831a07c
|
File details
Details for the file fraiseql_confiture-0.23.0-cp311-cp311-win_amd64.whl.
File metadata
- Download URL: fraiseql_confiture-0.23.0-cp311-cp311-win_amd64.whl
- Upload date:
- Size: 931.8 kB
- Tags: CPython 3.11, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0de4d90574b942988439b05b4599a215a206562453491f80a69ccd14dd3cbee4
|
|
| MD5 |
339030992f68dfa5f019176d85e2c656
|
|
| BLAKE2b-256 |
7e1a13b6cbdfaea5dfcc2a0f2db818036198c9dd9c551d66ae82dfbc9ba48e9e
|
File details
Details for the file fraiseql_confiture-0.23.0-cp311-cp311-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: fraiseql_confiture-0.23.0-cp311-cp311-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 1.0 MB
- Tags: CPython 3.11, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
32badd5c0e818bf6d5c9f6d9b16ffe331b98b66b5c0ef3de4c7808f0e18dc7ee
|
|
| MD5 |
e343670e5a72dad7e19734ad4f3b4107
|
|
| BLAKE2b-256 |
8234413a0df0833fc335d5303578ec03b3133aa6176255821a31277951a40fdb
|
File details
Details for the file fraiseql_confiture-0.23.0-cp311-cp311-macosx_11_0_arm64.whl.
File metadata
- Download URL: fraiseql_confiture-0.23.0-cp311-cp311-macosx_11_0_arm64.whl
- Upload date:
- Size: 978.9 kB
- Tags: CPython 3.11, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8325df0f0f6f080f7614717da375122d87147451f7fc1c61dcdd28d71d628187
|
|
| MD5 |
29f5ab15d12e9021ae6a8d061843f94a
|
|
| BLAKE2b-256 |
72a1e6365e8158d03a9feb7439e3117226f2bd0eb4346c371c0b8f36018e7ba1
|