Repository navigation
chore(repo): promote next into develop for the v3 beta - #7402
Conversation
) 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 <noreply@anthropic.com>
…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 <noreply@anthropic.com>
…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 <noreply@anthropic.com>
# 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 <noreply@anthropic.com>
# 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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
# 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
# Conflicts: # apps/cli-go/pkg/config/templates/Dockerfile
# Conflicts: # apps/cli-go/pkg/config/templates/Dockerfile
# Conflicts: # apps/cli-go/pkg/config/templates/Dockerfile
…utation consent rule (#7188) ## Summary Commands that can run against either the local stack or a hosted project didn't agree on what they target by default, and each took a different mix of `--local`, `--linked`, `--project-ref` and `--db-url`. Hosted mutations also each had their own idea of when to ask. This makes both consistent: every command that can run locally runs locally unless you pass `--remote`, and every hosted mutation follows one consent rule. Design: [CLI-2695: one `--remote` flag](https://linear.app/supabase/document/cli-2695-one-remote-flag-6110a06b977e). Decisions and enforcement: ADR 0033. **Targets** - One flag, `--remote`, on every command that can reach a hosted project: | Value | Means | |---|---| | none (`--remote`) | The linked project (`SUPABASE_PROJECT_ID`, then the link file) | | `true`, `yes`, `on`, `1`, `y` | The linked project, as bare `--remote` | | `false`, `no`, `off`, `0`, `n` | Rejected: leave `--remote` out instead | | 20 lowercase letters | That project ref | | `postgres://…` / `postgresql://…` | That database (database commands only) | | anything else | A branch name or branch UUID of the linked project, resolved with `supabase link`'s rule (a branch UUID also works without a link) | - `--remote=<value>` always works. `--remote <value>` is rewritten to the `=` form before parsing wherever it's unambiguous. When a bare `--remote` is followed by a word the command takes as an operand and that word is, or nearly is, a branch name, or is shaped like a project ref, the command stops before doing anything and shows both readings. `--remote` given twice with different values fails before anything runs. - Database commands (db, migration, inspect, storage, seed buckets, test db, gen types, db schema declarative generate) default to the local stack. In a linked checkout, a bare run says `default; pass --remote for <project>`. - Hosted-only commands (functions, secrets, config, branches, sso, …) keep the linked project as their default and take `--remote=<ref|branch>`. `branches *` takes `--remote=<ref>` for the parent project. - `--linked`, `--project-ref`, `--db-url` and `gen types --project-id` still work, routed through `--remote`, and print a deprecation warning on stderr naming the `--remote` replacement and v4 as the removal release. `--local` is accepted, does nothing, and warns. They're hidden from help. - `db diff --from/--to` take the same values plus `local` and `migrations`; `remote` is the linked project and `linked` is deprecated. The deprecated flags still work alongside them and warn: `--project-ref` points the `remote` side at that project, and the others change nothing. - A hosted database whose direct host is unreachable falls back to its connection pooler: first the pooler URL `supabase link` saved, then the project's pooler config from the Management API. The linked project used to try only the saved URL. - `--remote=<default branch>` is reported as production, not as a branch. A branch UUID used without a link is reported by its project ref alone, since the CLI can't tell whether it's the default branch. - Every command declares its target group with `withTargetPolicy` (local-only, account, hosted, selectable); a tree test fails if a leaf doesn't. Help shows a `TARGET` section and labels subcommands local only / remote only / local or remote. - Commands that resolve a target print one `Target: …` line on stderr (`db diff --from/--to` prints one per database, in order), and JSON results carry a `target` object. **Hosted consent** - One rule for every hosted mutation: ask in an interactive terminal, otherwise require `--yes` or a shell-exported `SUPABASE_YES`. A `SUPABASE_YES` from `supabase/.env` or a working-directory `.env` doesn't count, stdin is never read as an answer, and agents never get a prompt. - Replacing live state defaults to No. Only create-only operations (projects, branches, orgs and domains create, and bootstrap) default to Yes. - Commands that used to ask several questions (db push, db reset with seeding, config push, functions deploy --prune) ask one, after showing everything they'll change. - Network bans are only removed automatically under `--yes`, and a failed removal is reported as that failure. - `pull` asks once, up front, on every run except `--dry-run`. `db pull` asks when it would update the remote migration history. Declining either aborts before any file is written. **New** - `db query --read-only` accepts only read statements (`SELECT`, `WITH`, `VALUES`, `TABLE`, `SHOW`, `EXPLAIN` or a parenthesised query), runs them in a read-only transaction with a short lock timeout (or the Management API's `read_only` mode on a hosted target), and skips consent. Functions with side effects called from a `SELECT` still run, and the docs say so. The companion PR to `develop`, #7186, warns on bare runs of every command whose default flips here. ## Linked issue Closes CLI-2695 🤖 Generated with [Claude Code](https://claude.com/claude-code) BREAKING CHANGE: Commands that can run locally now default to the local stack, `--remote` replaces the target flags, and every hosted mutation follows one consent rule. - `db push`, `db pull`, `migration fetch`, `db dump`, `migration list`, `migration repair`, `inspect db *`, `inspect report`, `gen types` and `storage ls/cp/mv/rm` now target the local stack when run without a target flag. Add `--remote` to reach the linked project (`supabase db push --remote`), `--remote=<ref>` for another project, `--remote=<branch>` for a branch of the linked project, or `--remote=<postgres-url>` for a database URL. `SUPABASE_PROJECT_ID` on its own no longer selects a hosted project for these commands; it only says which project bare `--remote` means. - `gen types` with no target generates from the local database instead of opening a project picker or failing. In a linked checkout with no local stack running, it suggests `--remote`. `db schema declarative generate` no longer asks which target to use. `db schema declarative sync` with no declarative files generates them from the local database and no longer offers the linked project as the source; run `supabase db schema declarative generate --remote` first to start from the linked project. - `--linked`, `--project-ref`, `--db-url` and `gen types --project-id` are deprecated: they still work and print a warning on stderr naming the `--remote` form. `--local` does nothing and warns. v4 removes all of them, along with `db diff`'s `linked` (use `remote`). They no longer appear in help. - `--remote=<value>` can't be combined with any of the deprecated flags. `--project-ref` on a database command no longer needs `--linked`. An empty `--project-ref=` is now an error instead of being ignored. `--password` only applies to a hosted target. - `db diff` rejects `--remote` together with `--from`/`--to`; use `--from=remote` or `--to=remote`. - `db query` rejects SQL passed both as an argument and with `--file`. - Bare `--remote` (and the deprecated `--linked`) opens the project picker in an interactive terminal when the checkout isn't linked. Database and storage commands used to fail. - `--remote <word>` after a command that takes an operand fails with a "did you mean" error when `<word>` is, or is close to, a branch name of the linked project, when it's shaped like a project ref, or when the branch list can't be fetched. Put the operand before `--remote` (`supabase db pull add_profiles --remote`) or use `--remote=<branch>`. - Every hosted mutation now asks for confirmation, and fails with `HostedMutationConsentRequiredError` in non-interactive, JSON, stream-json, agent or `-o json|yaml|toml|env` runs unless `--yes` (or `SUPABASE_YES` exported in the shell) is set. Commands that newly need this for unattended runs: - db push; db pull when it updates the remote migration history; pull, except with `--dry-run` - migration up/down/squash/repair against a hosted target - db reset and test db against a hosted target - db query against a hosted target (or pass `--read-only` for reads) - seed buckets and storage cp/mv/rm against a hosted target - functions deploy/delete; secrets set/unset; config push - branches create/delete/disable/pause/unpause/update; backups restore - encryption update-root-key; network-bans remove; network-restrictions update; ssl-enforcement update - postgres-config update/delete; sso add/update/remove - vanity-subdomains activate/delete; domains create/activate/delete - notebooks push; projects create/delete; orgs create; bootstrap; feedback delete - compute push/delete - A `y` piped on stdin no longer confirms a hosted mutation. - The default answer is No for every hosted mutation except projects, branches, orgs and domains create, and bootstrap. Pressing Enter now declines db push, deploys, secrets changes and migration history updates. - `SUPABASE_YES` in `supabase/.env`, or in a `.env`, `.env.local` or `.env.<NODE_ENV>[.local]` file in the working directory, no longer confirms hosted mutations. When a working-directory file defines it at all, a shell-exported `SUPABASE_YES` doesn't count either, since the CLI can't tell the two apart. Use `--yes`. - db push, db reset against a hosted target (including seeding), config push and functions deploy --prune each ask one combined question instead of several. Deploy and prune are confirmed before anything is deployed, and `--prune` now runs even when every function is already up to date. `secrets unset --all` lists the secrets before asking. - Declining a hosted mutation fails with `HostedMutationDeclinedError` ("context canceled"). The command-specific cancelled error codes are removed. Declining `db pull` or `pull` aborts before any file is written; there's no longer a files-only path. - Removing network bans automatically, while connecting to a hosted database, now needs `--yes`. Otherwise the command fails and points to `supabase network-bans remove`. - `migration down` and `migration repair --all` on the local database no longer prompt. - Commands that resolve a target print `Target: …` once on stderr, including read-only commands against a hosted project. `config push` no longer prints its own "Pushing config to" line. JSON object results and error envelopes gain a `target` object; `db reset`'s `target` changes from a string to that object, and `config push` JSON no longer reports `declined_addons` or a skipped status. Arrays and generated artifacts are unchanged. - `migration up` against a hosted target reports "Remote database is up to date." instead of "Local database is up to date.", and `migration list` labels the history column "Local DB" when the target is local. - Agents never get a confirmation prompt, even with a terminal attached; they need `--yes` for hosted mutations.
Keeps next's target selection (ProjectRefResolver, target/*, --remote) and develop's on-disk link state (link snapshot over .temp or .link.json), now under shared/config. Ports develop's stack-mode version locking, PGSSL/TLS handling, config parse errors and token-permission help onto next's APIs, and drops develop's v3 local-default warning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The merged resolver reads the pooler URL from the target, not the TOML reader. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # apps/cli-go/pkg/config/templates/Dockerfile # apps/cli/docs/supabase/db/pull.md # apps/cli/src/commands/branches/create/create.live.test.ts # apps/cli/tests/helpers/branches-live.ts
# Conflicts: # apps/cli-go/pkg/config/templates/Dockerfile
# Conflicts: # .github/workflows/cli-go-ci.yml # .github/workflows/cli-go-tag-pkg.yml # apps/cli/docs/release-process.md # mise.toml
#7297) ## TL;DR parallel `db advisors --remote` runs no longer break each other with `28P01` ## whats improved/fixed? before calling the advisors API it set up a database login it never used every setup resets the shared `cli_login_postgres` password, so two runs at once (or one next to another remote db command) knocked each other out, and on a network without IPv6 one failed after about 150 s ## how? the remote path now skips the database and goes straight to the advisors API this brings it in line with `db query --remote` since #7188, so both remote read commands work the same way ```mermaid flowchart LR run["db advisors --remote"] run -->|before, no DB password| mint["new temp login role<br/>resets the shared password"] mint -- IPv6 --> api1["advisors API"] mint -- no IPv6 --> wait["pooler retries the login"] wait -- another run reset it --> fail["fails with 28P01"] wait -- no clash --> api1 run -- now --> ref["resolve project ref<br/>or branch"] ref --> token["check access token"] token -- missing --> login["asks to run supabase login"] token -- ok --> api2["advisors API<br/>security and/or performance"] api2 --> out["filter by --level<br/>print, exit by --fail-on"] ``` ## ref: - related: #7281 - missed in: #7188 BREAKING CHANGE: `db advisors --remote` no longer connects to the database first (matches `db query --remote`), so it no longer resolves database settings, creates a temporary login role or removes network bans under `--yes`
…-2782, CLI-2783) (#7312) ## Summary Adds a `[db.migrations.checks]` section to `config.toml`. Each of the nine migration security rules gets a severity (`error`, `warn` or `off`), and `[db.migrations.checks.exclude]` lists `"schema.name"` objects to skip per rule. Nothing reads the policy yet; the migration apply path consumes it in a follow-up. - Unknown rule names, invalid severities and malformed exclusion entries fail config loading with path-scoped messages, including a "did you mean" suggestion for misspelled rules. This section is stricter than the rest of the config so an older CLI never ignores a rule a teammate set. - An absent section leaves every rule unset; the CLI resolver treats unset rules as `warn`. - The section is published in the CLI config JSON Schema. `CliConfig` gains `db.migrations.checks`; the new constants are exported from `@supabase/config/internal` only. ## Linked issue Closes CLI-2782 Closes CLI-2783 Part of CLI-2786 Part of CLI-2787
next removed the Go CLI, so cli-go-ci.yml and cli-go-tag-pkg.yml go too, and sync-repair.yml no longer listens for the deleted CI workflow.
# 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
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.
CLI benchmark: no changes foundCommit
API calls: unchanged for all 7 commands. Binary size: 134.0 MB, up 172.0 KB from 133.8 MB. Details: timing ranges, API calls per command, slowest phasesTiming ranges
API calls per command
Slowest phasesNo command moved beyond noise, so there are no phase deltas. Comment |
There was a problem hiding this comment.
🤖 AI Review
Verified all six findings against the checked-out code. Five are confirmed: a reproduced hosted-consent bypass, two repeated --remote validation gaps, double config interpolation, and an obsolete environment assignment. The stale Turbo exclusion is verified, but its alleged runtime failure remains uncertain. No findings overlap.
Findings
| Severity | Location | Category | Sources | Claim |
|---|---|---|---|---|
| 🔴 CRITICAL | apps/cli/src/command-internal/hosted-consent/hosted-consent.yes.ts:49 |
security |
codex | A dotenv parser mismatch allows file-sourced SUPABASE_YES to bypass hosted-write consent. |
| 🟠 MAJOR | apps/cli/src/shared/cli/remote-argv.ts:149 |
target-safety |
codex | Repeated bare --remote flags validate only the first candidate operand, allowing later ambiguous target words to escape validation. |
| 🟠 MAJOR | apps/cli/src/shared/cli/remote-argv.ts:128 |
argument-validation |
codex | Invalid later --remote values can be ignored instead of preventing hosted mutations. |
| 🟡 MINOR | apps/cli/src/shared/functions/serve.ts:717 |
correctness |
codex | functions serve interpolates already-resolved auth environment references a second time. |
| 🟡 MINOR | .github/scripts/flaky-check/plan.ts:222 |
ci |
claude | The flaky-check unit suite retains an exclusion for the deleted @supabase/cli-go package, potentially causing Turbo to reject the invocation. |
| ⚪ NIT | .github/scripts/flaky-check/cli.ts:116 |
dead-code |
claude | The flaky-check runner still assigns SUPABASE_GO_BINARY to a deleted binary path, although no remaining code consumes the variable. |
Findings outside the diff
- 🟡 MINOR
.github/scripts/flaky-check/plan.ts:222— The flaky-check unit suite retains an exclusion for the deleted @supabase/cli-go package, potentially causing Turbo to reject the invocation. - ⚪ NIT
.github/scripts/flaky-check/cli.ts:116— The flaky-check runner still assigns SUPABASE_GO_BINARY to a deleted binary path, although no remaining code consumes the variable.
Stats
Claude findings: 2 · Codex findings: 4 · Confirmed: 5 · Refuted: 0 · Uncertain: 1
Models: claude-opus-5-5 + gpt-6.1-sol · Trigger: auto · Workflow run
This review runs once per PR. A maintainer can request another with a /ai-review comment.
Summary
Promotes
nextintodevelopsodevelopstarts publishing3.0.0-beta.N. This is the major cut from the release process.This one lands by approval, not the merge button. Approving fast-forwards
developto this PR's head once the required checks are green on it. Ifdevelophas moved in the meantime the fast-forward job comments and fails - the develop → next sync brings the new commits in, then it needs a re-approve.It'd help to hold merges into
developwhile this is open so it doesn't move under the cut. Once it lands:developpublishes3.0.0-beta.1on thebetatagnextstay frozen for the beta soakmainuntil the deploy PR carriesrelease-major🤖 Generated with Claude Code