# Supabase dev image case

Verified October 3, 2026. The project has the shape described in
[ariga/atlas#3807](https://github.com/ariga/atlas/issues/3807):
- an `atlas.hcl` `docker` block that builds the dev database from
  `supabase/postgres`, with an empty `baseline`;
- a `composite_schema` desired state made of an HCL part and a SQL part;
- `schema.mode.permissions` turned on.

## Versions

- Ptah Compat edge, built with Go 1.27.1 for linux/amd64 from master commit
  `e9d996d560bf90cfcd035d808e3515e798509a8a`, with `buildinfo.Version=edge`.
  The binary hash is in `verified.json`. The capabilities this case uses are not
  in a release yet.
- `supabase/postgres:17.6.1.011`, resolved to
  `supabase/postgres@sha256:6291866b0f14119ba3c2e60231b9d893705b1d23b18607fd0738287cfeec8b39`.
  It reports PostgreSQL `17.6`.
- Docker Engine 29.7.2 on the Linux host that ran every command.

The article calls the binary `atlas`, the name a drop-in installation gives it.
The measured runs invoked a symbolic link named `atlas` that points at the
`ptah-compat` executable. `measured/commands.json` records each invocation, its
working directory, exit code and timestamp, and the SHA-256 of its raw output.

## Setup

`project/` is the working directory:
- `Dockerfile` builds the dev image from `supabase/postgres:17.6.1.011` and
  adds `init/90-tenant-helper.sql`, a helper function with its own grants;
- `schema/tables.hcl` and `schema/security.sql` are the two parts of the
  desired state;
- `migrations/` holds what the first `migrate diff` generated.

The target stands in for a local Supabase database. It is a container built
from the same `Dockerfile`:

```console
docker build -q -t supabase-local:target .
docker run -d --name ptah-blog-3807-target -e POSTGRES_PASSWORD=postgres \
  -p 127.0.0.1:55440:5432 supabase-local:target
export DATABASE_URL='postgres://postgres:<password>@127.0.0.1:55440/postgres?sslmode=disable'
```

The `docker` block's dev database is started by Ptah on the same daemon. The
block's own image tag, `supabase-dev:local`, is built and removed by every
command that uses it.

## Sequence

Each command ran from `project/` in this order; the file it wrote is in
`measured/`:

| Command | Output |
| --- | --- |
| `atlas version` | `version.txt` |
| `atlas migrate diff init --env local` | `diff-init.txt` |
| `atlas migrate diff again --env local` | `diff-again.txt` |
| `psql -f catalog.sql` on the target | `target-before.txt` |
| `atlas migrate apply --env local --allow-dirty` | `apply.txt` |
| `atlas migrate diff settled --env local` | `diff-settled.txt` |
| `psql -f catalog.sql` on the target | `target-after.txt` |
| `psql -f serial.sql` on the target | `serial.txt` |
| `atlas migrate diff probe --env local --dev-url "docker+postgres://_/supabase-local:target/postgres"` | `plain-url.txt` |
| `ls migrations` | `migrations.txt` |
| `atlas migrate diff pinned --env local --dev-url "docker+postgres://_/supabase-local:target/postgres?search_path=public"` | `pinned-url.txt` |

The PostgreSQL version came from `SHOW server_version` on the target, recorded in
`postgres-version.txt`.

`target-before.txt` and `target-after.txt` differ in two added rows, the ones
for `public.todos` and `public.todos_id_seq`. Every row for an object the image
created, and every default privilege, reads the same.

## Output normalization

Standard output and standard error are combined. ANSI escapes and trailing
whitespace are removed, the working directory is written as `/work`, and the
target's password is replaced with `<password>`. The raw output hashes are in
`measured/commands.json`.
