Skip to content

chore: production deploy - #7340

Open
supabase-cli-releaser[bot] wants to merge 174 commits into
mainfrom
develop
Open

supabase-cli-releaser[bot] wants to merge 174 commits into
mainfrom
develop

Conversation

@supabase-cli-releaser

Copy link
Copy Markdown
Contributor

Coly010 and others added 30 commits October 8, 2026 09:42
)

Removes the last of the Go CLI: the deprecated commands and flags the
TypeScript CLI still forwarded to a bundled `supabase-go` binary, and
the Go source tree, toolchain and packaging behind them. Targets `next`,
so it ships in the next major. The user-facing changes are in the
`BREAKING CHANGE` footer at the end, which is what the release notes
pick up.

## Notes for reviewers

- The removed commands and flags stay registered but hidden, so using
one exits 1 with its replacement rather than an unknown-command error.
Each records a `RemovedSurfaceError` telemetry fingerprint
(`removed_command` / `removed_flag`), so remaining usage can be tracked
before the stubs are deleted outright.
- `GoProxy` and its wiring in `cli/root.ts` and `shared/cli/run.ts` are
deleted.
- `apps/cli-go/` is deleted (its `LICENSE` moves to the repo root),
along with the Go CI workflows, toolchain entries and Go-specific lint,
format, knip, CODEOWNERS and dependabot config. Every platform package
ships one `supabase` binary, and the install script removes a leftover
`supabase-go`.
- The `pkg/v*` tags of `github.com/supabase/cli/pkg` are frozen and
protected by a ruleset. [ADR 0030](docs/adr/0030-go-cli-removal.md)
records the removal, supersedes ADR 0016, and lists the known external
importers of that module.

Part of CLI-2432

BREAKING CHANGE: Removed the deprecated commands and flags that still
ran through the bundled Go binary, and the `supabase-go` binary itself.
If your scripts or CI use any of these, switch to the replacement. Each
removed command or flag now exits with code 1 and prints its
replacement.

- `supabase db remote changes` → `supabase db diff --linked`
- `supabase gen keys` → `supabase projects api-keys --project-ref <ref>`
- `supabase db diff --use-pg-schema` → omit the flag to use the default
engine, or pass `--use-pg-delta`
- `supabase functions download --legacy-bundle` → `supabase functions
download --use-api`. If that also fails and the Function was deployed
with a CLI older than 1.120.0, redeploy it with the current CLI first.
- `supabase db branch create`, `delete`, `list` and `switch` → removed
with no local replacement. Hosted preview branches are managed with
`supabase branches`.
- `--create-ticket` (global flag) → removed. Report CLI problems with
`supabase issue bug`.
- `supabase-go` → no longer installed. Call `supabase` instead.

---------

Co-authored-by: Claude Sonnet 5 <[email protected]>
…7064)

## Summary

With the Go CLI removed on `next`, the codebase still described and
named much of its behaviour in Go terms. This PR removes that, including
user-facing error text that quoted Go library errors. Telemetry error
identity (error `_tag`s and actionability fingerprints) is unchanged;
the committed error-tag snapshot has no diff.

**Config loading**
- `@supabase/config`'s internal `goViperCompat` → `cliCompat` (still
internal-only via `@supabase/config/internal`).
- New `apps/cli/src/command-internal/cli-config-load.ts` wraps
`loadCliConfig`, `resolveCliConfigValue`, `resolveCliConfigSubtree`, and
the config-pull validation decode, always setting `cliCompat: true`. A
code-structure guard keeps other `apps/cli` files from bypassing it.
- Functions serve/deploy/download now require the CLI loader; the unused
strict fallback and its constant `config.go_compat` span attribute are
removed. Only `experimental compute` stays on the strict public loader.
Whether to drop the lenient behaviour itself is a separate discussion.

**Renames**
- Go/pflag/cobra/viper-named files and identifiers are renamed for what
they do, e.g. `go-json.ts` → `html-safe-json.ts`,
`go-output.encoders.ts` → `output.encoders.ts`, `pflag-reconcile.ts` →
`argv-flag-reconcile.ts`, `cobra-flag-groups.ts` → `flag-groups.ts`,
`viper-env.ts` → `supabase-env.ts`, `*.go-payload.ts` →
`*.response-shape.ts`, `FunctionsGoConfigCompat` →
`FunctionsLocalConfigLoader`.
- `ArgvWorkdirError`, `UrlParseError`, and `TomlEncodeError` keep their
previous tag/fingerprint/`name` strings so error tracking is unaffected.
- The `go-keyring-base64:` prefix on stored keyring tokens is unchanged
(persisted data).

**Docs, comments, test titles**
- `SIDE_EFFECTS.md` files, CLI docs, `AGENTS.md`, comments, and test
titles describe current behaviour instead of comparing against the Go
CLI. ADRs are left as historical records.
- Corrected two stale doc claims: `packages/stack` does not use the
strict config default, and `test db` honours the global `--network-id`.

## Linked issue

None; internal cleanup.

- [x] I'm a Supabase maintainer.

## Checklist

- [x] The PR title follows Conventional Commits.
- [x] Tests added or updated for the change.
- [x] `pnpm check:all` passes; relevant package tests pass for every
touched workspace.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

BREAKING CHANGE: `COBRA_COMPLETION_DESCRIPTIONS` is no longer read; set
`SUPABASE_COMPLETION_DESCRIPTIONS` to control shell completion
descriptions. Error messages that quoted Go library errors are reworded:
invalid boolean flag values (e.g. `sso add --skip-url-validation`) now
say `expected a boolean` instead of `strconv.ParseBool: …`; invalid
unsigned values for `storage cp --jobs` and `db reset --last` now say
`expected an unsigned integer` (or `value out of range`) instead of
`strconv.ParseUint: …`; oversized migration/seed statements report
`scanner: token too long` instead of `bufio.Scanner: token too long`;
`gen bearer-jwt` claim and JWK parse errors now read `invalid claims:
expected a JSON object, got <kind>`, `invalid claims: number <n> is out
of range`, `failed to parse JWK: expected a JSON object, got <kind>`,
and `invalid JWK field <field>: expected <type>, got <kind>` instead of
`json: cannot unmarshal … into Go value/struct field …`. Config type
errors name values by shape: `SUPABASE_PROFILE` files report `expected a
YAML mapping at the top level` and `array`/`object` type names, `[db]`
glob entries report TOML type names (`local date`, `offset date-time`,
`array`, `table`, …) instead of Go type names, and malformed `db
lint`/`db advisors` payloads report `invalid <field>: expected <shape>`
instead of naming Go types. `SUPABASE_PROFILE` errors are reworded
(`unknown keys: …`, `<key> is required`, `<key> must be an http(s) URL`,
`unsupported config file type …`, `no profile config file specified`)
and multi-line profile errors no longer pad lines with trailing spaces.
Config decode errors drop the `decoding failed due to the following
error(s):` header and render entries as `<key>: expected a string, got
<type>`, `<key>: unknown keys: …`, `<key>: must be one of …`. `failed to
unmarshal …` messages (postgres-config, bootstrap templates, functions
download) now read `failed to parse …`, `db lint`'s `failed to marshal
json:` reads `failed to parse lint result:`, and TOML output errors drop
the `toml:` prefix.

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
…7066)

## TL;DR

pg-delta becomes the schema diff engine for every project, not only ones
created by a recent `supabase init`. A `config.toml` with no
`[experimental.pgdelta]` section, or a section that omits `enabled`, now
resolves to pg-delta for `db diff`, `db pull`, and `db remote commit`.
Migra stays available through a one-line rollback.

```mermaid
flowchart LR
  subgraph Before
    A1["no [experimental.pgdelta]"] --> M1[migra]
    B1["enabled = true"] --> P1[pg-delta]
    C1["SUPABASE_EXPERIMENTAL_PG_DELTA=1"] --> P1
  end
  subgraph After
    A2["no [experimental.pgdelta]"] --> P2[pg-delta]
    B2["enabled = false"] --> M2[migra]
    C2["--use-migra / --diff-engine migra"] --> M2
  end
```

## Why

[CLI-1587](https://linear.app/supabase/issue/CLI-1587) opted new
projects into pg-delta, but existing projects still diffed with migra
unless they edited `config.toml`. This finishes the migration so every
project uses the same engine.

## What changed

- `[experimental.pgdelta].enabled` defaults to `true` when absent, in
the CLI's config reader and in `@supabase/config` (including the
published JSON schema). An explicit `enabled = false` still selects
migra.
- The historical `SUPABASE_EXPERIMENTAL_PG_DELTA` opt-in env var is no
longer read. With pg-delta on by default it added nothing, and a stale
value would have silently overridden `enabled = false`. `--use-pg-delta`
remains the per-run override.
- `db schema declarative generate` / `sync` no longer need
`--experimental`. They are closed only when `enabled = false` is set and
`--experimental` is omitted.
- A versionless `db reset --experimental` (and a fresh-volume `start
--experimental`) applies `[db.migrations].schema_paths` files only when
pg-delta is explicitly disabled. Otherwise it replays migrations and,
when `schema_paths` is set, prints a warning naming the `enabled =
false` rollback (before the remote reset's confirmation prompt).
- `db pull --declarative` writes `[db.migrations].schema_paths` into
`config.toml` only when pg-delta is explicitly disabled.
- `db diff -f` that finds declarative schema files it did not read
prints a note pointing to `supabase db schema declarative sync` (and to
`--use-migra` when migra would read those files), including when the
diff is empty. Local diffs without `-f` only report the JSON advisory.
- The `schema_paths` warning on `db diff` / `db pull` explains that
`declarative sync` reads `declarative_schema_path`, not `schema_paths`,
and names the `enabled = false` rollback. The declarative-command gate
suggests `--experimental` rather than undoing `enabled = false`.
- On the stack backend, `--use-migra=false` / `--use-pgadmin=false` are
accepted, since they keep pg-delta.
- `db diff --use-migra=false` keeps the pg-delta default instead of
selecting migra.
- Help text, command docs, and `SIDE_EFFECTS.md` contracts describe
pg-delta as the default.

## Linked issue

Closes [CLI-1588](https://linear.app/supabase/issue/CLI-1588)

## BREAKING CHANGES:

pg-delta is now the default schema diff engine for all projects.
Generated migration SQL from `db diff`, `db pull`, and `db remote
commit` changes for projects that had not opted in. Under pg-delta,
`[db.migrations].schema_paths` and declarative files in
`supabase/schemas` are no longer inputs to `db diff` or migration-style
`db pull`, so the "edit `supabase/schemas`, then `db diff -f`" workflow
no longer picks up those edits; use `supabase db schema declarative
sync` to generate migrations from declarative schema files.

To keep migra, set this in `supabase/config.toml`:

```toml
[experimental.pgdelta]
enabled = false
```

or pass `--use-migra` (`db diff`) / `--diff-engine migra` (`db pull`)
per run. `SUPABASE_EXPERIMENTAL_PG_DELTA` is ignored.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
# Conflicts:
#	.github/workflows/cli-go-api-sync.yml
#	.github/workflows/cli-go-mirror.yml
#	CONTRIBUTING.md
#	apps/cli-go/pkg/config/templates/Dockerfile
#	apps/cli/docs/release-process.md
#	apps/cli/src/cli/complete.ts
#	apps/cli/src/command-internal/db-bootstrap/local-container-inputs.ts
#	apps/cli/src/command-internal/db-bootstrap/shadow-cache.ts
#	apps/cli/src/command-internal/db-config.integration.test.ts
#	apps/cli/src/command-internal/db-config.parse.unit.test.ts
#	apps/cli/src/command-internal/db-config.toml-read.ts
#	apps/cli/src/command-internal/db-config.toml-read.unit.test.ts
#	apps/cli/src/command-internal/db-connection.sql-pg.integration.test.ts
#	apps/cli/src/command-internal/db-connection.sql-pg.unit.test.ts
#	apps/cli/src/command-internal/db-image.unit.test.ts
#	apps/cli/src/command-internal/debug-logger.layer.ts
#	apps/cli/src/command-internal/debug-logger.layer.unit.test.ts
#	apps/cli/src/command-internal/docker-ids.unit.test.ts
#	apps/cli/src/command-internal/experimental-gate.unit.test.ts
#	apps/cli/src/command-internal/global-flags.ts
#	apps/cli/src/command-internal/go-proxy.layer.ts
#	apps/cli/src/command-internal/go-proxy.layer.unit.test.ts
#	apps/cli/src/command-internal/local-project-context.ts
#	apps/cli/src/command-internal/local-project-context.unit.test.ts
#	apps/cli/src/command-internal/pg-dump.run.ts
#	apps/cli/src/command-internal/profile-load.ts
#	apps/cli/src/command-internal/profile-load.unit.test.ts
#	apps/cli/src/command-internal/project-environment.unit.test.ts
#	apps/cli/src/command-internal/stack-shadow-cache.ts
#	apps/cli/src/command-internal/string-slice-flag.ts
#	apps/cli/src/command-internal/supabase-env.ts
#	apps/cli/src/command-internal/viper-env.unit.test.ts
#	apps/cli/src/commands/db/branch/branch.command.ts
#	apps/cli/src/commands/db/branch/create/create.command.ts
#	apps/cli/src/commands/db/branch/delete/delete.command.ts
#	apps/cli/src/commands/db/branch/list/list.command.ts
#	apps/cli/src/commands/db/branch/switch/switch.command.ts
#	apps/cli/src/commands/db/diff/diff.command.ts
#	apps/cli/src/commands/db/dump/SIDE_EFFECTS.md
#	apps/cli/src/commands/db/dump/dump.handler.ts
#	apps/cli/src/commands/db/pull/SIDE_EFFECTS.md
#	apps/cli/src/commands/db/pull/pull.integration.test.ts
#	apps/cli/src/commands/db/remote/changes/changes.command.ts
#	apps/cli/src/commands/db/schema/declarative/generate/generate.integration.test.ts
#	apps/cli/src/commands/db/schema/declarative/sync/sync.integration.test.ts
#	apps/cli/src/commands/db/test/SIDE_EFFECTS.md
#	apps/cli/src/commands/functions/download/download.command.ts
#	apps/cli/src/commands/gen/keys/keys.command.ts
#	apps/cli/src/commands/gen/types/types.command.ts
#	apps/cli/src/commands/gen/types/types.handler.ts
#	apps/cli/src/commands/start/SIDE_EFFECTS.md
#	apps/cli/src/commands/start/start.handler.ts
#	apps/cli/src/commands/storage/cp/cp.command.ts
#	apps/cli/src/shared/cli/run.unit.test.ts
#	apps/cli/src/shared/functions/deploy.ts
#	apps/cli/src/shared/functions/download.ts
#	apps/cli/src/shared/functions/serve.ts
#	apps/cli/src/shared/init/project-init.templates.unit.test.ts
#	apps/cli/src/shared/telemetry/error-actionability.ts
#	apps/cli/src/telemetry/command-telemetry.unit.test.ts
#	docs/adr/README.md
#	knip.jsonc
#	mise.lock
#	mise.toml
…ults (#7074)

## Summary

Every config value now resolves in one order, in every command:

`explicit flag > shell env > project env files > config (config.json
over config.toml, a matched [remotes.*] block over the base) > default`

Project env files are `.env.<SUPABASE_ENV>.local`, `.env.local`,
`.env.<SUPABASE_ENV>` and `.env`. They are read from `supabase/` and
then the project root, and `SUPABASE_ENV` defaults to `development`.

Before this change, each command family overlaid flags, env and remotes
by hand, and the families disagreed:
- A matched `[remotes.*]` block beat an explicit `SUPABASE_*` variable.
- Some commands ignored variables that others honoured.
- `config diff` and `config push` could see different values.
- `--password` was silently dropped when combined with `--db-url`.

ADR 0001 already defined this order, so this is mostly making the code
actually follow it.

Closes CLI-1885, closes CLI-2214. The decisions are recorded in [ADR
0031](docs/adr/0031-config-value-precedence.md).

## How it works

Before, every command decided for itself where a setting came from, and
they didn't agree. Now one service answers "what's the value of X?" for
every command, and it always asks the same sources in the same order.

### The problem

A setting like "should `db push` seed?" can come from five places:

1. a flag you typed (`--include-seed`)
2. your shell (`export SUPABASE_DB_SEED_ENABLED=true`)
3. a project env file (`supabase/.env`, `.env.local`, ...)
4. `config.toml`, where a matching `[remotes.staging]` block beats the
base `[db.seed]`
5. a built-in default

Each command family used to walk that list by hand, in its own order,
with its own parser. So `db push` and `start` could look at the same
setting and get different answers, a remote block could beat your shell
env in one command but not another, and `config diff` could show
something different from what `config push` would send.

### The service: `CliConfigValues`

Think of it as a librarian. You don't go into the stacks yourself; you
ask the librarian. It checks the five shelves in one fixed order and
hands you the first answer it finds, with a note saying which shelf it
came from.

```
flag  >  shell env  >  project .env*  >  config.toml (remote over base)  >  default
```

A command uses it in three steps.

**1. Load the resolved config once per run.**

```ts
const configValues = yield* CliConfigValues
const resolvedConfig = yield* configValues.load({ workdir, projectRef })
```

This reads the flags, the env and the env files, parses `config.toml`,
picks the matching `[remotes.*]` block, applies the order above and
validates the whole config. If anything is invalid (for example
`SUPABASE_STUDIO_PORT=abc`), the command stops here, and the error names
where the bad value came from.

**2. Ask for a value by key.**

```ts
const enabled = yield* resolvedConfig.get(CliConfigKeys.db.seed.enabled)
// enabled.value  → true
// enabled.origin → { tier: "shell", envName: "SUPABASE_DB_SEED_ENABLED" }
```

You get the value plus its origin. That origin is what lets the CLI say
things like `SUPABASE_AUTH_SITE_URL (shell) overrides auth.site_url in
[remotes.staging]` or `(from SUPABASE_API_MAX_ROWS in supabase/.env)`.

**3. Bind flags to keys instead of reading them by hand.**

```ts
// push.command.ts
includeSeed: CliConfigKeys.db.seed.enabled.flag({
  name: "include-seed",
  description: "Include seed data from your config.",
}),
```

The flag isn't a separate boolean the handler has to remember to merge
in. It is declared as the flag tier of `db.seed.enabled`, so the
resolved config already knows about it.

### Where the keys come from

`CliConfigKeys` is generated from the config schema (`CliConfigSchema`).
Every leaf in `config.toml` becomes a key with:

- its path: `db.seed.enabled`
- its env name: `SUPABASE_` plus the path in upper snake case, so
`SUPABASE_DB_SEED_ENABLED`
- a parser chosen from its type: boolean, port, list, and so on

Nobody writes the 360 or so keys by hand. Add a field to the schema and
it gets an env override and a parser for free. The only hand-written
parts are exceptions, such as codec overrides, exclusions and section
gates like "SMTP env vars only apply if `[auth.email.smtp]` exists".

### A worked example

```toml
[db.seed]
enabled = true

[remotes.staging]
project_id = "abc123"

[remotes.staging.db.seed]
enabled = false
```

```bash
SUPABASE_DB_SEED_ENABLED=true supabase db push --project-ref abc123 --include-seed
```

| Shelf | Says | Used? |
|---|---|---|
| flag `--include-seed` | true | ✅ wins |
| shell `SUPABASE_DB_SEED_ENABLED` | true | |
| project `.env*` | unset | |
| `[remotes.staging.db.seed]` | false | |
| `[db.seed]` (base) | true | |
| default | false | |

Seeding is on, from the flag. Without the flag, the shell value wins
(still on, origin shell). Before this PR, the remote block's `false`
would have beaten the shell variable, which is the headline breaking
change. And because seeding is on for a project that matched a remote
block that doesn't itself turn seeding on, the CLI asks for confirmation
before seeding.

### How it relates to `config.toml`

`config.toml` isn't replaced. It's shelf 4. The `@supabase/config`
package still parses it, merges the matching remote and decodes it. The
service calls those package stages and layers flags and env on top, and
commands can no longer call the package loaders directly.

The resolved config gives two views of the whole config:

- `resolvedConfig.loaded`: what you declared (file plus flags and env),
with no defaults. `config push` and `config diff` use this, so push
never sends a default you didn't write.
- `resolvedConfig.materialized`: the same with defaults applied. Used by
things that need a complete config to run, like `start`, local Docker
and stack.

### What stops this drifting back

- A guard test fails if any file outside the service mentions a
`SUPABASE_*` config env name or imports the package config loaders.
- `oxlint` bans `process.env` and `Bun.env` across the CLI.
- A command that reads config without wiring the service in fails `tsc`.
- A contract test checks every key in the registry against the order.

## Design

It's a big diff, sorry - but most of it is commands moving onto the new
service. The interesting parts are
`apps/cli/src/config/cli-config-*.ts`, the guard tests in
`code-structure.unit.test.ts` and ADR 0031. If you disagree with any of
the behaviour calls in the table below, that's totally fine, I'd rather
we settle them here than after `next` ships.

- One service. `CliConfigValues` is the only service that resolves
values.
- `load({ workdir, projectRef })` returns a memoised
`ResolvedCliConfig`.
- `resolvedConfig.get(key)` returns the value and the tier it came from.
- `resolvedConfig.loaded` is the declared config: file, flags and env,
with no defaults. `config push` and `config diff` use it.
- `resolvedConfig.materialized` is the declared config with defaults
applied.
- The whole config is decoded on load, so an invalid value fails the
command before it does anything.
- Generated key registry. The registry is generated from
`CliConfigSchema`. Each leaf gets the env name `SUPABASE_` + its
upper-snake path, and a codec derived from its type. Section gates,
codec overrides and exclusions are hand-written annotations.
- Flags. A flag binds to a key with `key.flag(...)`, so "explicitly
passed" is part of the type rather than a convention. If two flags set
the same key to different values, the first load fails with
`CliConfigFlagConflictError`.
- Package stages. `@supabase/config/internal` exposes the pipeline
stages: parse, merge a selected remote, decode. Only the foundation
calls them.
- Guardrails:
  - A code-structure test fails on:
- registry env names written as string literals outside the foundation
files
- imports of the package loaders (`loadCliConfig`,
`resolveCliConfigSubtree`, `loadCliProjectEnvironment`) outside the
foundation
    - the old overlay helpers
    - registry-backed flags declared without `key.flag`
- `oxlint` bans `process.env`, `Bun.env`, `globalThis.process` and `env`
imported from `node:process` across `apps/cli/src`.
- A command that reads config values without `withCliConfigFlags` fails
`tsc`.
- Visibility:
- When a shell or project env value beats a value that the matched
`[remotes.*]` block sets, the CLI prints one WARN per key.
  - `--debug` lists the origin of every non-default value.
- `config push` labels env-sourced rows and prints a one-line summary of
them, even with `--yes`.
- Exceptions kept on purpose:
  - `CommandSettings` reads `SUPABASE_PROJECT_ID` from the shell only.
- `experimental-feature.ts` ignores remotes and env files, because it
runs before the project is known.
  - `db.password` has no env tier.
- `secrets set` tolerates an invalid unrelated value, so a broken key
doesn't block rotating a secret.
- The service-role key isn't scoped to the linked project; only the
database password is.

## Behaviour changes

| Change | Before | Now |
| --- | --- | --- |
| Env vs matched remote | A `[remotes.*]` block beat `SUPABASE_*`
variables for the keys it set | Flags and env beat the block, with a
WARN for each key they override |
| `SUPABASE_PROJECT_ID` and a matched remote | The remote's `project_id`
named local Docker resources | `SUPABASE_PROJECT_ID` wins |
| `--include-seed` and `enabled = false` | A remote's or the base's
`[db.seed] enabled = false` won | `--include-seed`, `--sql-paths` or
`SUPABASE_DB_SEED_ENABLED=true` override it. `db push` still seeds only
with `--include-seed` |
| Seed confirmation | None | `db push` and `db reset --linked` ask
before seeding a project that matched a `[remotes.*]` block, unless that
block itself sets `db.seed.enabled = true`. `--yes` or `SUPABASE_YES`
confirms. A non-interactive run without it exits 1 with
`SeedConsentRequiredError` before applying anything |
| `--password` with `--db-url` or `--local` | Silently ignored by `db
push`, `db pull`, `db dump` and `db schema declarative generate` | Exits
1 with a message saying where the password goes instead. `migration
list` and `migration repair` now also reject `--local`. `migration
squash` targets the local database unless you pass `--linked`, so
`squash -p` needs `--linked`. An empty `-p ""` counts as not passed |
| `SUPABASE_DB_PASSWORD` for another project | Sent to whichever project
was targeted | Withheld when the target differs from the linked project.
A notice names both refs and says a temporary login role is used
instead. Unlinked directories and same-project targets keep using the
variable |
| `db diff --use-pg-delta=false` | No effect against env or config |
Turns pg-delta off over env and config. The stack backend always uses
pg-delta |
| `db pull --use-pg-delta` (hidden, deprecated) | Selected the
declarative export only | Also turns pg-delta on for the run, so the
export leaves `[db.migrations].schema_paths` alone |
| `SUPABASE_EXPERIMENTAL_PGDELTA_ENABLED` | Not read | Overrides
`[experimental.pgdelta].enabled` like any other key, so `false` selects
migra for the run. `SUPABASE_EXPERIMENTAL_PG_DELTA` stays ignored |
| Boolean variables | Each reader had its own parser
(`SUPABASE_EXPERIMENTAL_STACK` took `0`/`1` only) | One grammar
everywhere: `1`, `t`, `T`, `true`, `True`, `TRUE` or `0`, `f`, `F`,
`false`, `False`, `FALSE`. Anything else, including mixed case like
`tRue`, fails the command |
| Invalid config value | Failed only the commands that read it, with a
command-specific error | Fails every command that loads the config
(including `stop` and `services`) with `CliConfigValueError`, naming the
variable or file it came from. `functions new` also stops before writing
anything instead of falling back to defaults. `secrets set` is the one
exception |
| Env applies wherever a key is read | Some readers ignored variables
others honoured, e.g. storage and realtime `enabled`,
`SUPABASE_API_PORT` in `functions serve`, `SUPABASE_API_SCHEMAS` in `gen
types --linked` | One behaviour per key, and validation runs on the
effective values. Env for a key inside an optional section (webhooks,
SMS providers, captcha, SMTP, ...) still applies only when that section
exists |
| Ports | `/^\d+$/` in one reader, octal-aware in another | One parser:
decimal, `0x`, `0o`, `0b` and `_` separators, max 65535. A leading zero
(`054322`) is rejected |
| `SUPABASE_REMOTES_<NAME>_PROJECT_ID` | Worked for `db` commands only |
Selects the remote in every command, including `secrets set`, `config
diff`, `config push` and storage |
| Local Docker project id | `start` and `functions` fell back to the
project ref; the db readers used the folder name | Always the sanitised
folder name, never the ref |
| `gen types --local` password | Read `SUPABASE_DB_PASSWORD` | Uses
`[db].password`, like every other `--local` command |
| `services`, `functions deploy`, `functions download`, `functions
serve` | Read `config.toml` only, in places | Read `config.json` first,
like every other command |
| `config push` and `config diff` | Push sent the declared document;
diff read the file on its own | Both apply `SUPABASE_*` and flag
overrides and show the same values. Env-sourced rows are labelled, and
JSON output carries `origin` |
| Default workdir | The walk up from the current directory looked for
`supabase/config.toml` only; some commands then climbed again for
`config.json` | One walk for every command, stopping at the nearest
`supabase/config.toml` or `supabase/config.json`. A nested `config.json`
project now wins over an outer `config.toml` one |
| `SUPABASE_SERVICES_HOSTNAME` | Read from the project `.env` by `gen
types` and seed buckets only, ahead of the shell | Read by every local
command, shell first, then project `.env*`. The Docker daemon still
comes from the shell environment only |
| `[experimental.inspect.rules]` | `inspect report` accepted a single
table and non-string rule fields | Rules must be
`[[experimental.inspect.rules]]` with string fields, as every other
command already required |
| `link --password` | Listed in help | Hidden, deprecated and ignored
with a warning. It never connected to the database |

### Newly env-overridable keys

Four config keys had no `SUPABASE_*` override before and now have one:

- `auth.sms.otp_expiry` (`SUPABASE_AUTH_SMS_OTP_EXPIRY`)
- `auth.sms.otp_length` (`SUPABASE_AUTH_SMS_OTP_LENGTH`)
- `db.network_restrictions.allowed_cidrs`
(`SUPABASE_DB_NETWORK_RESTRICTIONS_ALLOWED_CIDRS`)
- `db.network_restrictions.allowed_cidrs_v6`
(`SUPABASE_DB_NETWORK_RESTRICTIONS_ALLOWED_CIDRS_V6`)

### Error codes

- `MigrationPasswordFlagsError` is now `DbPasswordFlagsError`.
- Invalid values report `CliConfigValueError` instead of
`DbConfigLoadError`, `SeedConfigLoadError`, `StatusConfigLoadError` or
`StopConfigLoadError`.
- Seeding without consent reports the new `SeedConsentRequiredError`.

BREAKING CHANGE: Flags and `SUPABASE_*` environment variables now
override config, including a matched `[remotes.<name>]` block. Every
value resolves in one order: flag > shell environment > project env
files > `config.json`/`config.toml` (a matched remote block over the
base) > default. Project env files are `.env.<SUPABASE_ENV>.local`,
`.env.local`, `.env.<SUPABASE_ENV>` and `.env`, in `supabase/` and then
the project root; `SUPABASE_ENV` defaults to `development`.

- If you deploy to a project with a `[remotes.*]` block, any
`SUPABASE_*` variable in the job or in those files now beats the block.
The CLI warns for each value it overrides. Check `env | grep ^SUPABASE_`
in CI and `grep -h ^SUPABASE_ supabase/.env* .env*` in the repo, and set
`SUPABASE_ENV` in CI so `.env.development` doesn't apply.
- `supabase config push` now pushes those overrides; for example, a
`SUPABASE_AUTH_SITE_URL` in `.env.local` replaces the value in
`[remotes.production]`. Push labels each env-sourced value, and
`supabase config diff` shows the same values.
- Seeding a project that matches a `[remotes.*]` block now asks first,
unless the block itself sets `db.seed.enabled = true`. `db push
--include-seed` and `db reset --linked` exit 1 in CI unless you pass
`--yes`. `--include-seed` and `SUPABASE_DB_SEED_ENABLED=true` now
override `enabled = false` in the base `[db.seed]` too. `db push` still
seeds only with `--include-seed`.
- `--password` with `--local` or `--db-url` now exits 1 on `db push`,
`db pull`, `db dump`, `db schema declarative generate`, `migration list`
and `migration repair`. `migration squash` targets the local database by
default, so `migration squash -p` needs `--linked`. Put the password in
the URL, or set `[db].password` for the local database.
- In a directory linked to project A, `SUPABASE_DB_PASSWORD` is no
longer sent to another project (`--project-ref B`). Pass `--password`,
or provide an access token so the CLI can create a temporary login role.
- An invalid value in config or in a `SUPABASE_*` variable now fails
every command that loads project config, including `db push`, `stop` and
`services` (for example `SUPABASE_STUDIO_PORT=abc`). The error names the
variable or file and reports `CliConfigValueError`.
- `SUPABASE_EXPERIMENTAL_PGDELTA_ENABLED` overrides
`[experimental.pgdelta].enabled`, so `false` selects migra for the run.
Boolean variables, including `SUPABASE_EXPERIMENTAL_STACK`, accept `1`,
`t`, `T`, `true`, `True`, `TRUE` or `0`, `f`, `F`, `false`, `False`,
`FALSE` and fail on anything else.
- `supabase db diff --use-pg-delta=false` now turns pg-delta off even
when config or env turns it on.
- `supabase gen types --local` uses `[db].password`, not
`SUPABASE_DB_PASSWORD`.
- `SUPABASE_PROJECT_ID` now names local containers even when a remote
block matches. Without a `project_id` in config.toml, local containers
and volumes are named after the project folder, not the project ref. Run
`supabase stop` before upgrading, or set `project_id`.
- Ports with a leading zero (`054322`) are rejected.
- `services` and the `functions` commands read `config.json` before
`config.toml`.
- Without `--workdir`, the CLI uses the nearest directory with
`supabase/config.toml` or `supabase/config.json`, so a nested
`config.json` project now wins over an outer `config.toml` one.
- `inspect report` needs `[[experimental.inspect.rules]]` with string
fields; a single `[experimental.inspect.rules]` table now fails.
- `supabase link --password` is deprecated and ignored.
- Error codes: `MigrationPasswordFlagsError` is now
`DbPasswordFlagsError`; invalid values report `CliConfigValueError`;
seeding without consent reports `SeedConsentRequiredError`.
#7109)

## Summary

This removes four commands that have been deprecated for at least one
stable cycle and have a direct replacement. They stay registered but are
hidden from help. Running any of them now exits 1 and names the
replacement. This uses the same `removedCommand()` tombstone as `db
remote changes` and `gen keys`.

| Command | Replacement |
| -- | -- |
| `db remote commit` | `db pull` |
| `inspect db total-index-size` | `inspect db index-stats` |
| `inspect db total-table-sizes` | `inspect db table-stats` |
| `inspect db table-index-sizes` | `inspect db table-stats` |

- The tombstones still accept their old flags, so existing invocations
reach the removal error instead of a parse error. The inspect tombstones
open no DB connection.
- Each tombstone records a `RemovedSurfaceError` telemetry fingerprint
(`removed_command`), so remaining usage stays visible.
- Their handlers, handler tests and per-command docs pages are deleted.
SIDE_EFFECTS.md and the `DOCS_EXCLUDED` table are updated.
- The pg-delta config description no longer mentions `db remote commit`,
and `config.schema.json` is regenerated.
- The higher-use `inspect db` aliases (`table-sizes`, `index-sizes`,
`cache-hit`, …) are unchanged.

The matching "removed in v3" warning text for stable is in #7108
(develop).

## Linked issue

Part of CLI-2595

## Checklist

- [x] The PR title follows Conventional Commits.
- [x] Tests added or updated: `hidden-flag.unit.test.ts` runs all four
tombstones and asserts the removal message and suggestion, and checks
they're absent from `inspect db --help`.
- [x] From the repository root, `pnpm check:all` passes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

BREAKING CHANGE: `supabase db remote commit` is removed; use `supabase
db pull`. `supabase inspect db total-index-size` is removed; use
`supabase inspect db index-stats`. `supabase inspect db
total-table-sizes` and `supabase inspect db table-index-sizes` are
removed; use `supabase inspect db table-stats`. Each removed command
exits 1 and prints its replacement.

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
# Conflicts:
#	.oxlintrc.effect.json
#	apps/cli-go/pkg/config/templates/Dockerfile
#	apps/cli/src/command-internal/db-bootstrap/local-db-running.ts
#	apps/cli/src/command-internal/db-pull-run.ts
#	apps/cli/src/command-internal/docker-ids.ts
#	apps/cli/src/command-internal/docker-ids.unit.test.ts
#	apps/cli/src/command-internal/local-project-context.ts
#	apps/cli/src/command-internal/pgdelta.ts
#	apps/cli/src/command-internal/seed-buckets.ts
#	apps/cli/src/command-internal/storage-content-type.unit.test.ts
#	apps/cli/src/command-internal/storage-credentials.ts
#	apps/cli/src/command-internal/storage-url.ts
#	apps/cli/src/commands/backups/list/list.integration.test.ts
#	apps/cli/src/commands/backups/restore/restore.integration.test.ts
#	apps/cli/src/commands/branches/create/create.integration.test.ts
#	apps/cli/src/commands/branches/delete/delete.integration.test.ts
#	apps/cli/src/commands/branches/get/get.integration.test.ts
#	apps/cli/src/commands/branches/list/list.integration.test.ts
#	apps/cli/src/commands/branches/pause/pause.integration.test.ts
#	apps/cli/src/commands/branches/update/update.handler.ts
#	apps/cli/src/commands/completion/bash/bash.integration.test.ts
#	apps/cli/src/commands/completion/completion-scripts.unit.test.ts
#	apps/cli/src/commands/completion/fish/fish.integration.test.ts
#	apps/cli/src/commands/completion/powershell/powershell.integration.test.ts
#	apps/cli/src/commands/completion/zsh/zsh.integration.test.ts
#	apps/cli/src/commands/config/config.load.ts
#	apps/cli/src/commands/config/diff/diff.integration.test.ts
#	apps/cli/src/commands/config/pull/pull.integration.test.ts
#	apps/cli/src/commands/config/push/push.handler.ts
#	apps/cli/src/commands/config/push/push.integration.test.ts
#	apps/cli/src/commands/db/diff/diff.handler.ts
#	apps/cli/src/commands/db/pull/pull.integration.test.ts
#	apps/cli/src/commands/db/remote/commit/SIDE_EFFECTS.md
#	apps/cli/src/commands/db/remote/commit/commit.command.ts
#	apps/cli/src/commands/db/remote/commit/commit.handler.ts
#	apps/cli/src/commands/db/schema/declarative/generate/generate.handler.ts
#	apps/cli/src/commands/db/schema/declarative/sync/sync.handler.ts
#	apps/cli/src/commands/db/shared/pgdelta.seam.layer.ts
#	apps/cli/src/commands/domains/activate/activate.integration.test.ts
#	apps/cli/src/commands/domains/create/create.integration.test.ts
#	apps/cli/src/commands/domains/delete/delete.integration.test.ts
#	apps/cli/src/commands/domains/get/get.integration.test.ts
#	apps/cli/src/commands/domains/reverify/reverify.integration.test.ts
#	apps/cli/src/commands/experimental/compute/compute.shared.ts
#	apps/cli/src/commands/experimental/compute/list/list.integration.test.ts
#	apps/cli/src/commands/feedback/add/add.integration.test.ts
#	apps/cli/src/commands/feedback/delete/delete.integration.test.ts
#	apps/cli/src/commands/functions/delete/delete.integration.test.ts
#	apps/cli/src/commands/functions/deploy/deploy.integration.test.ts
#	apps/cli/src/commands/functions/download/download.handler.ts
#	apps/cli/src/commands/functions/download/download.integration.test.ts
#	apps/cli/src/commands/functions/list/list.encoders.ts
#	apps/cli/src/commands/functions/list/list.integration.test.ts
#	apps/cli/src/commands/functions/new/new.integration.test.ts
#	apps/cli/src/commands/functions/serve/serve.integration.test.ts
#	apps/cli/src/commands/gen/bearer-jwt/bearer-jwt.claims.ts
#	apps/cli/src/commands/gen/bearer-jwt/bearer-jwt.integration.test.ts
#	apps/cli/src/commands/gen/gen.signing-keys-config.ts
#	apps/cli/src/commands/gen/signing-key/signing-key.integration.test.ts
#	apps/cli/src/commands/gen/types/types.integration.test.ts
#	apps/cli/src/commands/inspect/db/inspect-deprecated.integration.test.ts
#	apps/cli/src/commands/inspect/db/table-index-sizes/table-index-sizes.command.ts
#	apps/cli/src/commands/inspect/db/table-index-sizes/table-index-sizes.handler.ts
#	apps/cli/src/commands/inspect/db/total-index-size/total-index-size.command.ts
#	apps/cli/src/commands/inspect/db/total-index-size/total-index-size.handler.ts
#	apps/cli/src/commands/inspect/db/total-table-sizes/total-table-sizes.command.ts
#	apps/cli/src/commands/inspect/db/total-table-sizes/total-table-sizes.handler.ts
#	apps/cli/src/commands/logout/logout.integration.test.ts
#	apps/cli/src/commands/network-bans/get/get.integration.test.ts
#	apps/cli/src/commands/network-bans/network-bans.experimental-gate.integration.test.ts
#	apps/cli/src/commands/network-bans/remove/remove.integration.test.ts
#	apps/cli/src/commands/network-restrictions/get/get.integration.test.ts
#	apps/cli/src/commands/network-restrictions/network-restrictions.cidr.unit.test.ts
#	apps/cli/src/commands/network-restrictions/network-restrictions.experimental-gate.integration.test.ts
#	apps/cli/src/commands/network-restrictions/update/update.integration.test.ts
#	apps/cli/src/commands/notebooks/notebooks.integration.test.ts
#	apps/cli/src/commands/orgs/create/create.integration.test.ts
#	apps/cli/src/commands/orgs/list/list.integration.test.ts
#	apps/cli/src/commands/postgres-config/delete/delete.command.unit.test.ts
#	apps/cli/src/commands/postgres-config/postgres-config.experimental-gate.integration.test.ts
#	apps/cli/src/commands/postgres-config/postgres-config.integration.test.ts
#	apps/cli/src/commands/postgres-config/update/update.command.unit.test.ts
#	apps/cli/src/commands/projects/api-keys/api-keys.integration.test.ts
#	apps/cli/src/commands/projects/create/create.integration.test.ts
#	apps/cli/src/commands/projects/delete/delete.integration.test.ts
#	apps/cli/src/commands/projects/list/list.integration.test.ts
#	apps/cli/src/commands/pull/pull.integration.test.ts
#	apps/cli/src/commands/secrets/list/list.integration.test.ts
#	apps/cli/src/commands/secrets/set/set.integration.test.ts
#	apps/cli/src/commands/secrets/unset/unset.integration.test.ts
#	apps/cli/src/commands/snippets/download/download.handler.ts
#	apps/cli/src/commands/snippets/download/download.integration.test.ts
#	apps/cli/src/commands/snippets/list/list.integration.test.ts
#	apps/cli/src/commands/ssl-enforcement/get/get.integration.test.ts
#	apps/cli/src/commands/ssl-enforcement/update/update.integration.test.ts
#	apps/cli/src/commands/sso/add/add.command.unit.test.ts
#	apps/cli/src/commands/sso/add/add.handler.ts
#	apps/cli/src/commands/sso/add/add.integration.test.ts
#	apps/cli/src/commands/sso/info/info.integration.test.ts
#	apps/cli/src/commands/sso/list/list.integration.test.ts
#	apps/cli/src/commands/sso/remove/remove.integration.test.ts
#	apps/cli/src/commands/sso/show/show.integration.test.ts
#	apps/cli/src/commands/sso/sso.string-slice-flags.integration.test.ts
#	apps/cli/src/commands/sso/update/update.command.unit.test.ts
#	apps/cli/src/commands/sso/update/update.handler.ts
#	apps/cli/src/commands/sso/update/update.integration.test.ts
#	apps/cli/src/commands/storage/cp/cp.command.integration.test.ts
#	apps/cli/src/commands/storage/ls/ls.integration.test.ts
#	apps/cli/src/commands/storage/rm/rm.integration.test.ts
#	apps/cli/src/commands/storage/storage.frame.ts
#	apps/cli/src/commands/vanity-subdomains/vanity-subdomains.integration.test.ts
#	apps/cli/src/commands/whoami/whoami.integration.test.ts
# Conflicts:
#	apps/cli/src/command-internal/db-pull-run.ts
#	apps/cli/src/commands/db/pull/SIDE_EFFECTS.md
#	apps/cli/src/commands/db/pull/pull.integration.test.ts
#	apps/cli/src/commands/domains/SIDE_EFFECTS.md
#	apps/cli/src/commands/domains/activate/activate.integration.test.ts
#	apps/cli/src/commands/domains/create/create.integration.test.ts
#	apps/cli/src/commands/domains/delete/delete.integration.test.ts
#	apps/cli/src/commands/domains/get/get.integration.test.ts
#	apps/cli/src/commands/domains/reverify/reverify.integration.test.ts
#	apps/cli/src/telemetry/linked-project-cache.integration.test.ts
# Conflicts:
#	apps/cli/src/command-internal/db-pull-run.ts
#	apps/cli/src/command-internal/migrate-and-seed.unit.test.ts
#	apps/cli/src/command-internal/migration-apply.ts
#	apps/cli/src/command-internal/migration-apply.unit.test.ts
#	apps/cli/src/command-internal/migration-history.unit.test.ts
#	apps/cli/src/command-internal/pgdelta.ts
#	apps/cli/src/command-internal/pgdelta.unit.test.ts
#	apps/cli/src/command-internal/seed-ops.unit.test.ts
#	apps/cli/src/command-internal/seed.unit.test.ts
#	apps/cli/src/commands/db/diff/diff.integration.test.ts
#	apps/cli/src/commands/start/start.lifecycle.e2e.test.ts
# Conflicts:
#	apps/cli-go/pkg/config/templates/Dockerfile
#	apps/cli/src/commands/bootstrap/bootstrap.handler.ts
#	apps/cli/src/commands/bootstrap/bootstrap.templates.integration.test.ts
#	apps/cli/src/commands/db/advisors/advisors.format.ts
#	apps/cli/src/commands/db/advisors/advisors.integration.test.ts
#	apps/cli/src/commands/db/diff/diff.integration.test.ts
#	apps/cli/src/commands/db/dump/dump.handler.ts
#	apps/cli/src/commands/db/dump/dump.integration.test.ts
#	apps/cli/src/commands/db/lint/lint.format.ts
#	apps/cli/src/commands/db/lint/lint.integration.test.ts
#	apps/cli/src/commands/db/pull/pull.integration.test.ts
#	apps/cli/src/commands/db/push/push.integration.test.ts
#	apps/cli/src/commands/db/query/query.format.ts
#	apps/cli/src/commands/db/query/query.integration.test.ts
#	apps/cli/src/commands/db/reset/reset.integration.test.ts
#	apps/cli/src/commands/db/schema/declarative/generate/generate.integration.test.ts
#	apps/cli/src/commands/db/schema/declarative/sync/sync.integration.test.ts
#	apps/cli/src/commands/db/shared/pgdelta.seam.integration.test.ts
#	apps/cli/src/commands/db/start/start.integration.test.ts
#	apps/cli/src/commands/db/test/test.integration.test.ts
#	apps/cli/src/commands/experimental/stack/stack-forwarding.integration.test.ts
#	apps/cli/src/commands/init/init.integration.test.ts
#	apps/cli/src/commands/inspect/report/report.config.ts
#	apps/cli/src/commands/link/link.integration.test.ts
#	apps/cli/src/commands/migration/down/down.integration.test.ts
#	apps/cli/src/commands/migration/fetch/fetch.integration.test.ts
#	apps/cli/src/commands/migration/list/list.integration.test.ts
#	apps/cli/src/commands/migration/migration.integration.test.ts
#	apps/cli/src/commands/migration/new/new.integration.test.ts
#	apps/cli/src/commands/migration/repair/repair.integration.test.ts
#	apps/cli/src/commands/migration/squash/squash.diff.unit.test.ts
#	apps/cli/src/commands/migration/squash/squash.integration.test.ts
#	apps/cli/src/commands/seed/buckets/buckets.flags.unit.test.ts
#	apps/cli/src/commands/seed/buckets/buckets.integration.test.ts
#	apps/cli/src/commands/services/services.integration.test.ts
#	apps/cli/src/commands/start/start.gates.ts
#	apps/cli/src/commands/start/start.handler.ts
#	apps/cli/src/commands/start/start.integration.test.ts
#	apps/cli/src/commands/start/start.string-slice-flags.integration.test.ts
#	apps/cli/src/commands/status/status.command.unit.test.ts
#	apps/cli/src/commands/status/status.integration.test.ts
#	apps/cli/src/commands/status/status.string-slice-flags.integration.test.ts
#	apps/cli/src/commands/stop/stop.integration.test.ts
#	apps/cli/src/commands/test/new/new.integration.test.ts
# Conflicts:
#	apps/cli-go/pkg/config/templates/Dockerfile
#	apps/cli/src/commands/db/advisors/advisors.integration.test.ts
#	apps/cli/src/commands/db/diff/diff.command.ts
#	apps/cli/src/commands/db/diff/diff.integration.test.ts
#	apps/cli/src/commands/db/dump/dump.command.ts
#	apps/cli/src/commands/db/pull/pull.command.ts
#	apps/cli/src/commands/db/pull/pull.integration.test.ts
#	apps/cli/src/commands/db/push/push.command.ts
#	apps/cli/src/commands/db/push/push.integration.test.ts
#	apps/cli/src/commands/db/remote/commit/commit.command.ts
#	apps/cli/src/commands/db/reset/reset.command.ts
#	apps/cli/src/commands/db/reset/reset.integration.test.ts
#	apps/cli/src/commands/db/schema/declarative/generate/generate.command.ts
#	apps/cli/src/commands/db/schema/declarative/generate/generate.integration.test.ts
#	apps/cli/src/commands/db/schema/declarative/sync/sync.command.ts
#	apps/cli/src/commands/db/start/start.command.ts
#	apps/cli/src/commands/db/start/start.integration.test.ts
#	apps/cli/src/commands/db/test/test.integration.test.ts
#	apps/cli/src/commands/seed/buckets/buckets.integration.test.ts
# Conflicts:
#	.github/actions/setup/action.yml
#	.github/workflows/test.yml
#	apps/cli-go/pkg/config/templates/Dockerfile
#	apps/cli/docs/supabase/db/diff.md
#	apps/cli/docs/supabase/db/pull.md
#	apps/cli/src/command-internal/db-bootstrap/bootstrap-config.ts
#	apps/cli/src/command-internal/db-config.toml-read.ts
#	apps/cli/src/command-internal/db-pull-run.ts
#	apps/cli/src/command-internal/local-config-values.ts
#	apps/cli/src/command-internal/local-project-context.ts
#	apps/cli/src/command-internal/stack-config.ts
#	apps/cli/src/commands/bootstrap/SIDE_EFFECTS.md
#	apps/cli/src/commands/bootstrap/bootstrap.handler.ts
#	apps/cli/src/commands/db/diff/SIDE_EFFECTS.md
#	apps/cli/src/commands/db/diff/diff.handler.ts
#	apps/cli/src/commands/db/diff/diff.integration.test.ts
#	apps/cli/src/commands/db/dump/SIDE_EFFECTS.md
#	apps/cli/src/commands/db/dump/dump.handler.ts
#	apps/cli/src/commands/db/push/SIDE_EFFECTS.md
#	apps/cli/src/commands/db/push/push.handler.ts
#	apps/cli/src/commands/db/reset/reset.handler.ts
#	apps/cli/src/commands/db/schema/declarative/generate/generate.handler.ts
#	apps/cli/src/commands/experimental/stack/start/start.handler.ts
#	apps/cli/src/commands/gen/types/SIDE_EFFECTS.md
#	apps/cli/src/commands/gen/types/types.handler.ts
#	apps/cli/src/commands/migration/down/down.handler.ts
#	apps/cli/src/commands/services/SIDE_EFFECTS.md
#	apps/cli/src/commands/services/services-local-stack.ts
#	apps/cli/src/commands/services/services.handler.ts
#	apps/cli/src/commands/status/status.handler.ts
#	apps/cli/src/commands/status/status.integration.test.ts
#	packages/config/src/io.ts
next removed apps/cli-go, so the Go build, cache, and keyring steps synced from develop fail.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
next already removed the deprecated config, env vars and import maps that #7154
warns about, so its warnings, env alias and tests do not apply here.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Remove the pre-renumber Go CLI removal and config precedence ADRs and point
config precedence references at ADR 0032.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
# Conflicts:
#	apps/cli/src/command-internal/test-db.integration.test.ts
#	apps/cli/src/commands/inspect/db/table-index-sizes/table-index-sizes.command.ts
#	apps/cli/src/commands/inspect/db/total-index-size/total-index-size.command.ts
#	apps/cli/src/commands/inspect/db/total-table-sizes/total-table-sizes.command.ts
#	apps/cli/src/commands/inspect/report/report.integration.test.ts
#	apps/cli/src/commands/migration/list/list.command.ts
#	apps/cli/src/commands/migration/repair/repair.command.ts
#	apps/cli/src/commands/migration/squash/squash.command.ts
supabase-cli-releaser Bot and others added 10 commits October 29, 2026 13:50
This adds postgres 17.11.0.011-orioledb-r0 as a new release line.


https://github.com/supabase/slim-services/releases/tag/postgres-17.11.0.011-orioledb-r0

Planned after supabase/slim-services published 15.19.0.011-r0. This
branch is rewritten from develop whenever a newer relevant release
arrives.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…#7379)

## TL;DR

`supabase db advisors --local` / `--db-url` now lints the schemas that
`[api]` in `config.toml` exposes, instead of whatever `pgrst.db_schemas`
value the connection happens to carry.

## What's fixed?

Splinter decides which schemas to lint by reading `pgrst.db_schemas`.
The CLI never set it, so:

- When it was unset, splinter fell back to `public`, and tables without
RLS in other exposed schemas were never reported.
- After any transaction on a backend had set it locally, the backend
read `''` from then on. A pooled connection could then make `db
advisors` report "No issues found" even with unprotected `public`
tables.

## How?

- `readDbToml` resolves `exposedSchemas` from `[api].schemas` and
`[api].enabled` (empty when the Data API is off), with the
`SUPABASE_API_SCHEMAS` / `SUPABASE_API_ENABLED` overrides and
`[remotes.*]` precedence that `supabase start` uses. `DbConfigResolver`
passes it on the `--local` and `--db-url` paths.
- `db advisors` runs `select set_config('pgrst.db_schemas', $1, true)`
inside its existing rollback transaction, before the lint setup. A Data
API that is off sets `''`, so nothing is reported as exposed.
- The `db query` agent-mode RLS advisory binds the same schema list as
`$1::text[]` instead of reading the setting, and is skipped when the
Data API is off.
- `--linked` is unchanged; the platform sets the value for hosted
projects.

Behavior change: a malformed `SUPABASE_API_ENABLED` now fails config
loading for commands that resolve the local database config, matching
how `SUPABASE_AUTH_ENABLED` and `supabase start` already treat it.

## ref:

- CLI-2824
This PR was automatically created to sync API types from the
infrastructure repository.

Changes were detected after refreshing `apps/cli-go/api/v1-openapi.yaml`
from the latest spec from infrastructure, and `apps/cli-go/pkg/api` is
regenerated from it. The snapshot diff is the upstream API change and is
worth reading: it is marked `linguist-generated`, so GitHub collapses it
behind a "Load diff" click rather than hiding it.

Co-authored-by: supabase-cli-releaser[bot] <246109035+supabase-cli-releaser[bot]@users.noreply.github.com>
# Conflicts:
#	apps/cli-go/api/v1-openapi.yaml
#	apps/cli-go/pkg/api/types.gen.go
#	apps/cli-go/pkg/config/templates/Dockerfile
#	apps/cli/src/command-internal/db-bootstrap/local-db-running.ts
#	apps/cli/src/command-internal/db-bootstrap/start-local-database.ts
#	apps/cli/src/command-internal/db-config.layer.ts
#	apps/cli/src/command-internal/db-config.toml-read.ts
#	apps/cli/src/commands/config/push/push.handler.ts
#	apps/cli/src/commands/db/advisors/SIDE_EFFECTS.md
#	apps/cli/src/commands/db/advisors/advisors.integration.test.ts
#	apps/cli/src/commands/db/query/SIDE_EFFECTS.md
#	apps/cli/src/commands/db/query/query.handler.ts
#	apps/cli/src/commands/db/query/query.integration.test.ts
#	apps/cli/src/commands/db/schema/declarative/generate/SIDE_EFFECTS.md
#	apps/cli/src/commands/db/schema/declarative/sync/SIDE_EFFECTS.md
#	apps/cli/src/commands/db/shared/pgdelta.seam.integration.test.ts
#	apps/cli/src/commands/db/start/SIDE_EFFECTS.md
#	apps/cli/src/commands/link/SIDE_EFFECTS.md
#	apps/cli/src/commands/start/SIDE_EFFECTS.md
#	apps/cli/tests/helpers/command-mocks.ts
#	packages/config/AGENTS.md
#	packages/config/README.md
## TL;DR

stops `supabase db reset` randomly failing at `Initialising schema...`

## whats hurting the user(s)?

`supabase db reset` fails at random with `error running container: exit
1`, mostly in CI

it recreated the db while realtime kept running, so docker restarted
realtime mid reset and its migrations collided with the reset's own on
`schema_migrations_pkey`

## now fixed by:

storage, auth, realtime and the pooler are stopped before the db is
removed and started once at the end,
even when the reset fails...

```mermaid
sequenceDiagram
    participant R as db reset
    participant S as storage, auth, realtime, pooler
    participant D as docker
    participant DB as db
    participant J as init jobs

    Note over R,J: before
    R->>DB: remove and recreate
    DB--xS: connection lost
    D->>S: restart (unless-stopped)
    par
        S->>DB: realtime migrations
    and
        J->>DB: realtime migrations
    end
    DB-->>J: schema_migrations_pkey
    J-->>R: exit 1, reset fails

    Note over R,J: now
    R->>S: stop
    R->>DB: remove and recreate
    J->>DB: init migrations
    R->>DB: your migrations + seed
    R->>S: start once, reload kong
    Note over R,S: same restart on failure or ctrl-c
```

## ref:

- closes: #6915
- closes: #6445
Hosted mutations now refuse in a non-TTY run without --yes, so the live
suite's remaining unconfirmed setup and cleanup calls failed with
"Confirmation required". Pass --yes to db query (write statements),
migration repair and postgres-config delete in the live tests and helpers.
## Summary

- `publish` lists `live-e2e-gate` in `needs`, and that gate only runs
for `stable`/`maintenance`, so it is skipped on `beta` and `next`.
`publish` still runs (`!cancelled()`), but `backfill-release-notes`,
`publish-homebrew` and `publish-scoop` have no status-check function in
their `if:`, so they get an implicit `success()` that is false when an
ancestor was skipped. Result: beta/next GitHub Releases have empty
bodies and Homebrew/Scoop `supabase-beta` stop updating. These three
conditions now start with `!cancelled()`, and brew/scoop also require
`needs.publish.result == 'success'`.
- `backfill-release-notes.yml` reads `inputs.non_blocking`, which is
only declared under `workflow_call`, so a manual `workflow_dispatch`
fails at startup. It now uses `${{ inputs.non_blocking || false }}`,
matching `propose-release-notes.yml`.

## Maintenance branches

This release-infrastructure change needs a cherry-pick to `v2.x` per
`apps/cli/docs/release-process.md`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
# A `/ai-review` comment on a pull request starts a review. The resolve job checks who
# commented, and the one job that can write to the pull request runs code from the default
# branch only, never from the pull request.
# jactionlint ignore=dangerous-triggers

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ Severity: LOW

Any GitHub user can comment exactly /ai-review; the new issue-comment trigger puts that run in the same workflow-level concurrency group as an authorized manual review. With cancel-in-progress, an outsider's run can cancel the active review before resolve checks permissions, suppressing the requested review without starting a replacement.
Helpful? Add 👍 / 👎

💡 Fix Suggestion

Suggestion: Stop untrusted /ai-review comments from sharing the review concurrency group with authorized runs. The fix belongs in the concurrency: block (around lines 42-48), not at line 22.

Option 1 (preferred): in the group expression, give any issue_comment run a unique group (github.run_id) unless the commenter is trusted. Check github.event.comment.author_association against OWNER, MEMBER and COLLABORATOR. Untrusted or non-matching comments then land in their own group and cannot cancel anything. For example, change the condition to:
(github.event_name == 'issue_comment' && (github.event.comment.body != '/ai-review' || !contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association))) && github.run_id || (github.event_name == 'pull_request' && 'auto' || 'review')

Option 2: only cancel in-progress runs when the trigger is trusted, for example cancel-in-progress: ${{ github.event_name != 'issue_comment' }}. This also stops an outsider's comment from cancelling an active review.

Keep the resolve job's permission check as the authoritative authorization gate.

supabase-cli-releaser Bot and others added 17 commits October 29, 2026 23:31
This bumps realtime from v2.149.2-r0 to v2.150.0-r0.


https://github.com/supabase/slim-services/releases/tag/realtime-v2.150.0-r0

Planned after supabase/slim-services published v2.150.0-r0. This branch
is rewritten from develop whenever a newer relevant release arrives.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps realtime from v2.150.0-r0 to v2.150.1-r0.


https://github.com/supabase/slim-services/releases/tag/realtime-v2.150.1-r0

Planned after supabase/slim-services published v2.150.1-r0. This branch
is rewritten from develop whenever a newer relevant release arrives.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Addresses #7339

## Problem

Between `start` creating a project's Docker network and attaching its
first container, the network has no endpoints. Any
project-label-filtered `docker network prune` that lands in that window
removes it, and `start` then fails with `network supabase_network_<id>
not found`. Within one invocation every prune runs before bring-up
(stopped-stack recovery) or after it stops (rollback), so the prune in
the report most likely came from an overlapping lifecycle command on the
same project: an earlier `start` still rolling back, a `stop`, `stop
--all`, or a `db start` rollback.

## Change

- `start`, `db start` (including the declarative auto-start), and `stop`
hold a cross-process lifecycle lock under `$SUPABASE_HOME/locks`:
- a per-project lock keyed by the project's Docker label, so checkouts
sharing a `project_id` serialize and different projects still run in
parallel;
- a gate every per-project command holds shared and `stop --all` holds
exclusively.
- The lock is taken before the first Docker probe and released after any
rollback, outside the rollback region, so a waiting command never rolls
back another command's resources.
- A blocked command prints one waiting line to stderr and fails with an
actionable error after 10 minutes; waiting is interruptible.
- The lock replaces the per-project start lock from #7369, which it
covers.
- `@supabase/stack` gains `holdSharedLock` / `holdExclusiveLock` next to
`holdLock`.
- The `ensureNetwork` doc comment no longer claims the network cannot be
removed mid-run.

## Limitations

Older CLI binaries, raw `docker` commands, a different `SUPABASE_HOME`,
and projects sharing a custom `--network-id` do not participate, and
`stop --all` is not queued ahead of a stream of single-project commands
(documented in `start`'s `SIDE_EFFECTS.md`). `db reset` and the shadow
database still create their network without the lock; that is a separate
follow-up. When `$SUPABASE_HOME/locks` cannot be created or a lock file
there cannot be opened, these commands continue without the lock and log
a debug line.

---------

Co-authored-by: 7ttp <[email protected]>
This bumps postgres from 15.19.0.011-r0 to 15.19.0.013-r0.


https://github.com/supabase/slim-services/releases/tag/postgres-15.19.0.013-r0

Planned after supabase/slim-services published 15.19.0.013-r0. This
branch is rewritten from develop whenever a newer relevant release
arrives.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This adds postgres 17.11.0.013-orioledb-r0 as a new release line.


https://github.com/supabase/slim-services/releases/tag/postgres-17.11.0.013-orioledb-r0

Planned after supabase/slim-services published 15.19.0.013-r0. This
branch is rewritten from develop whenever a newer relevant release
arrives.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Summary

The post-release smoke test installs the just-published CLI through
`supabase/setup-cli` on ubuntu, macos, windows and Alpine, but only with
setup-cli v1 and v2. setup-cli's current major is v3, and its README
tells users to use `supabase/setup-cli@v3`, so the major we point people
at had no coverage.

This adds a `v3` leg to both matrices (the runner matrix and the Alpine
job), pinned to `v3.0.1` (`45a513f8…`) the same way v1 and v2 are
pinned. Job names keep the `setup-cli vN (runner)` shape.

v3 still takes the same `version` input and strips a leading `v`, so the
step is identical to the others. The difference is that v3 installs the
`supabase` npm package instead of downloading GitHub Release assets, so
the smoke test now also covers the npm install path. The `publish` job
pushes to npm before the GitHub Release, so the version is already on
npm when `setup-cli-smoke` runs. I updated the `version` input
description and the two lines in `release-process.md` that describe this
job to match. Release logic isn't touched.

A dispatch of this workflow on this branch against `3.0.0-beta.14` is
green for v1, v2 and v3 on every runner:
https://github.com/supabase/cli/actions/runs/40960508203

## Maintenance branch

`v2.x` exists, and `setup-cli-smoke-test.yml` is on the
release-infrastructure list in the [release
process](https://github.com/supabase/cli/blob/develop/apps/cli/docs/release-process.md),
so this needs a cherry-pick to `v2.x` through a `backport/*` PR.
This bumps storage from v1.84.36-r0 to v1.87.0-r0.


https://github.com/supabase/slim-services/releases/tag/storage-v1.87.0-r0

Planned after supabase/slim-services published v1.87.0-r0. This branch
is rewritten from develop whenever a newer relevant release arrives.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…able_signup (#7398)

## Summary

`enable_signup` under `[auth.email]` and `[auth.sms]` doesn't control
signups. `config push` sends it as `external_email_enabled` /
`external_phone_enabled`, which turns the whole provider on or off. So
setting `[auth.email] enable_signup = false` to stop new signups also
stops every existing user signing in with email (password, magic link or
OTP), which is what #7055 ran into. The key that blocks new signups is
`[auth] enable_signup`.

This adds `enabled` as the new name for both keys and deprecates
`enable_signup` in those two sections. The old key goes away in v4.

- `[auth.email] enabled` and `[auth.sms] enabled` are new, including
under `[remotes.<name>.auth.email]` and `[remotes.<name>.auth.sms]`. If
a section sets both, `enabled` wins.
- While a config still uses the old key, loading it prints a warning on
stderr, e.g. `WARN: auth.email.enable_signup is deprecated and will be
removed in v4, use auth.email.enabled instead.`
- `config push` and `config diff` print a warning when a change turns
the email or phone provider off, and point at `[auth] enable_signup` for
blocking new signups only. Push prints it before its confirmation
question.
- `config pull` writes whichever key the file already uses, and
`enabled` when it adds the key.
- `supabase init` writes `enabled`.
- The schema descriptions for these keys and for `[auth] enable_signup`
now say what each one does.

`enable_signup` stays the name in the decoded config, `ProjectConfig`,
the API mapping and the `config diff` / `config pull` JSON `path` until
v4, so nothing changes for `@supabase/config` consumers or JSON output.
`config pull`'s `document_path` names the key it actually wrote.

### Not covered

- `config diff` and `config push` still label the row
`auth.email.enable_signup` even when the file uses `enabled`. Renaming
the canonical path belongs with removing the old key (CLI-2892).

## Linked issue

Closes #7055 (CLI-2658)
…LI-2864) (#7397)

## Summary

Since #7366 the config, project-config and `supabase.lock` schemas have
`$id`s under `https://supabase.com/schemas/`, but nothing serves them
yet. supabase/supabase#52509 sets up `apps/www/public/schemas` in
supabase/supabase, and this publishes to it from every stable release.

- New `scripts/publish-schemas.ts` copies the newest version of each
schema in `apps/docs/public/schemas` to `apps/www/public/schemas` on a
`cli/schemas` branch in supabase/supabase. It only opens a PR (or reuses
the open one) when a file changed, and arms auto-merge on it
- Before pushing, it checks that each schema is a JSON object, declares
draft-07, and has an `$id` matching the URL it'll be served from. Lower
versions are never written, and it refuses to publish if supabase.com
already serves a newer version or the `cli/schemas` branch changes
anything else, so a published version can't be modified
- `--dry-run` validates and prints what it would publish without
touching supabase/supabase. Release dry runs pass it
- The clone, branch, commit and PR steps move into
`scripts/publish-pull-request.ts`, shared with `publish-docs-spec.ts`.
The CLI reference publisher behaves the same as before
- The release `docs` job is now "Publish to supabase.com" and runs both
publishers, each without stopping the other. A failed publish adds an
error annotation and a step summary line, turns the job red and posts to
Slack (new `publish-failure` message). The release itself still succeeds

Auto-merge still waits on a code-owner approval, since supabase/supabase
`master` requires one and the releaser App can't approve its own PR. The
schemas path is owned by `@supabase/cli`, so one of us approves and it
merges itself. Making it fully hands-off would need the App added as a
bypass actor on that ruleset.

There's no `v*.x` branch at the minute, so no cherry-pick is needed.

## Linked issue

Closes CLI-2864
…7396)

## Summary

Adds two properties to every CLI analytics event, `cli_command_executed`
included:

- `is_eval` is `true` when `SUPABASE_EVAL` is `1` or `true`
(case-insensitive), otherwise `false`. supabase/evals#416 already sets
`SUPABASE_EVAL=1` on every CLI run in the evals harness, and this is the
CLI side that reports it, so eval runs can be kept out of agent usage
numbers.
- `agent_name` is the detected agent (for example `claude`, `codex` or
`cursor_cli`), sent only when `is_agent` is true. It reuses the `AiTool`
detection that already drives `is_agent` and the `feedback add`
metadata, so usage can be split by harness without decoding
`env_signals`.

Events from earlier CLI versions don't carry `is_eval`, so queries
should treat a missing value as `false`. Both properties are documented
in `apps/cli/docs/analytics.md`.

## Linked issue

Closes CLI-2716
## TL;DR

brings the `apps/cli-e2e` harness under the effect lint

## whats introduced?

- `env.ts` reads the environment through `Config` and passes the record
mode and staging url to the global setup instead of rewriting
`process.env`
- the global setup, staging project helpers, fixture loader, pg mock and
vitest config run as effect programs
- the fixture loader decodes fixtures through a schema, so a hand
written fixture missing a field fails at load with its file name, and
every committed fixture decodes unchanged
- the replay server stays excluded until the last page of this stack

## ref

- related: CLI-2878
## TL;DR

brings the `apps/cli-e2e` test files and their fixtures under the effect
lint

## whats introduced?

- `run` and `runNoProjectId` return effects, every test body runs
through one `runE2e` edge, and control calls go through one
`postControl` helper instead of `fetch`
- a test that asks for a runner but passes without running a CLI command
now fails, since vitest would otherwise pass a body that returned an
unrun effect
- the same tests run with the same results

## ref

- related: CLI-2879
…7387)

## TL;DR

brings the `apps/cli-e2e` replay server under the effect lint, so the
allow list covers the whole

## whats introduced?

- the server runs on `FileSystem`, `HttpClient` and typed errors with
`Bun.serve` as its only edge, and the global setup owns it through a
scope
- the docker relay uses the `node:http` client from
`@effect/platform-node`, because bun's `fetch` sorts response headers
and recorded fixtures keep them in arrival order
- replay responses and recorded fixture files stay byte for byte the
same

ps: in record mode the docker relay can send a response chunked where it
used to send `content-length`, with the same body bytes, and a failed
relay logs its own error instead of the raw socket error

## ref

- closes: CLI-2880
Two things have been turning develop's checks red.

1. The hourly API Package Sync has failed every run since 20 Oct. The
Management API spec now sets `contentEncoding: "binary"` on the ESZIP
upload body, and Effect's OpenAPI 3.0 converter rejects
`contentEncoding` and `contentMediaType` because they'd become active
keywords in Draft 2020-12. Both are only annotations and `format:
binary` already says the same thing, so `sanitizeOpenApiSchema` now
drops them. Once this lands, the next sync run should open the backlog
PR for `@supabase/api`.
2. CodeQL flags `js/incomplete-sanitization` in
`apps/cli/scripts/publish.ts`, because `name.replace("/", "%2F")` only
replaces the first slash. It shows up on every develop commit through
the production deploy PR. It's `replaceAll` now - no package name has
more than one slash, so there's no behaviour change.

`v2.x` has the same `publish.ts` line. With no behaviour change I don't
think it needs a backport, unless we want the alert cleared there too.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
This bumps realtime from v2.150.1-r0 to v2.150.2-r0.


https://github.com/supabase/slim-services/releases/tag/realtime-v2.150.2-r0

Planned after supabase/slim-services published v2.150.2-r0. This branch
is rewritten from develop whenever a newer relevant release arrives.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…7421)

## TL;DR

stops the `unit` and `focused` flaky checks failing before any test
runs, by dropping the filter on the removed `cli-go` package

## ref

- spotted while working on: #7414
…#7414)

## TL;DR

brings the last excluded test files under the effect lint

## whats introduced?

- the seven test files from the `--remote` and config values work and
the `local-db-running` integration test drop their `node:` imports and
`process.env` writes for `FileSystem`, `Path`, `Config` and `vi.stubEnv`
- the target selection and hosted consent helpers type their error
channels as `Error` instead of `unknown`
- `goldenJson` becomes an effect, so its four callers yield it
- the same tests run with the same results

## ref

- related: CLI-2898

This branch was successfully deployed

No deployments
config-release — 465b1d86 Deployed Oct 29, 2026 by supabase-cli-releaser[bot] via Publish #47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do not merge Approve to apply; do not merge.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants