Repository navigation
Conversation
Every leaf command carries a withTargetPolicy annotation with its target group and hosted-mutation consent default. A tree guard checks the group against each leaf's flags, database-target default and consent.
…LI-2695) Add resolveTarget for sync and selectable commands and DbConfigResolver.resolveConnection. It takes an already-resolved target, rejects --password unless the target is linked, announces the local and --db-url targets, and only removes network bans when the caller holds an approval. The existing resolve method keeps its behaviour.
requireHostedMutationConsent grants on --yes or a shell SUPABASE_YES, asks only an interactive text-mode terminal, and otherwise fails with HostedMutationConsentRequiredError; stdin is never read. A local target needs no consent, and hostedMutationPreapproval lets reads clear a network ban only under --yes.
planDbSeed returns the seed input and a detail line for the single hosted consent. The existing resolveDbSeedInput and confirmSeedIntoMatchedRemote stay until the integration shard removes them.
describeHostedConsent runs the six consent cases against a leaf: JSON and piped text without --yes, a TTY decline, --yes, a shell SUPABASE_YES, and a project .env that must not count. A synthetic leaf proves the helper.
…ith hosted consent
CLI-2695) Encryption, network bans/restrictions, SSL enforcement, Postgres config, SSO and notebooks push ask for consent (default No) before their first API write, or accept --yes. Reads never prompt.
withCommandTelemetry logs --remote, --linked, --local and a ref-shaped --project-ref/--project-id for every hosted or selectable command, so the per-leaf projectRefSafeFlags list is gone.
Hand-written selector interfaces dropped the --db-url acceptance brand, so a database-URL leaf could lose --db-url, or a no-URL leaf pass one, without a compile error.
…branch resolution tree-wide The pooler entry applies to a ref or branch selector only. A guard test over the command tree requires every --remote leaf to compose branchResolution. seed buckets reuses STORAGE_PERMISSIONS and reads all four selector flags from the seed group's scoped globals.
gen types resolves the target first, so a bare --remote followed by a branch name reaches the ambiguity check instead of being read as the positional language, and --remote <ref> works on the rewritten command line. The remote-selector mutex groups collapse into one list of flags a hosted project excludes.
Completion asks the same rewrite the runtime uses whether the word after a bare --remote is its value, and offers --remote= (with no trailing space) instead of a bare switch.
…mote from the argv scan
Moves the declaration into command-internal so seed buckets no longer imports another command. The drift helper test now also covers --project-ref given as a boolean.
…f sides - repair, down, squash and the removed `db remote commit` hint name the target's flags so the suggested command reaches the same database - `db diff` rejects a target selector combined with --from/--to and documents that `remote` there always means the linked project - `db diff --from/--to=<ref|branch>` declares the linked-database permissions (login role, pooler config, network bans) - the IPv6 error after an ad-hoc --remote=<ref|branch> points at the pooler instead of suggesting `supabase link` - the `migration fetch` overwrite prompt names the target
- deprecation warnings show the command path, its operand placeholders and the `--remote` replacement last, and never echo operands, other flags, the `--` tail or URL userinfo - `--remote` values are trimmed and matched case-insensitively against `postgres(ql)://`; other URLs are rejected, and a project URL suggests its ref - keyword `--db-url` and boolean-literal `--project-ref` warnings say how to reach the same target through `--remote` - an empty or false `--remote` fails without a help dump and names the values that command accepts; `--remote` on `link` and on local-only commands says so - the default branch renders as the production project and a branch banner names its parent project - the bare `--remote` ambiguity check reports a match, a near miss or a failed listing with both readings, and keeps the cause and a login suggestion - branch lookups tell an expired token to log in and suggest the closest name - the linked-project hint reads `SUPABASE_PROJECT_ID` before the link file - `--remote` help rows, descriptions and JSON help follow each command's variant - conflict errors list only the selectors the command has and suggest `--remote`
… command-target - empty and false `--remote` pins use the new messages and the command's value hint - deprecation warning examples show the operand placeholders - ADR 0033 and command-target describe the SUPABASE_PROJECT_ID hint, the JSON help fields and the `link` and local-only `--remote` errors
…li-2695-bc-consistent-local-vs-remote-target-defaults-and-one
…erinfo at the last @ A word with whitespace or '=' is never read as a branch name, so a NAME=value pair or a keyword DSN no longer reaches the bare-remote ambiguity check or a branch lookup. --remote=<keyword dsn> is rejected with a pointer to --remote=<url>, and stripUserinfo drops everything up to the last '@' so a password containing '@' cannot leak.
A usage error no longer costs a branch listing or prints a Target banner. A positional language that is also the word after a bare --remote still lets the branch check rule first.
…nch UUID as unverified
The bare --remote ambiguity message no longer echoes the word, names a missing
or invalid access token, and the branch lookups declare required permissions
that match when they run (a new uuid selector shape covers GET /v1/branches/{id}).
A branch UUID resolved without a linked project is announced as the bare
project, environment unverified, since the lookup carries no default-branch flag.
A positional declared with Param.withHidden still parses but counts for no operand capacity and no warning placeholder. gen types --remote <word> now joins to --remote=<word> and its deprecation warnings read 'supabase gen types --remote=<ref>'; a language written before --remote keeps working. The gen types positional-language guard no longer needs to wait for the bare-remote ambiguity check.
…or --to classifyExplicitRef no longer turns it into a branch name, so the value is never echoed in a branch-not-found or not-linked message. The error points at --from=<url> or --to=<url>.
classifyExplicitRef returns the rejection --remote gives a value instead of reading it as a branch name, so a URL with credentials is never echoed. A project's own https URL names --from=<ref> or --to=<ref>.
…c-consistent-local-vs-remote-target-defaults-and-one # Conflicts: # apps/cli/src/commands/gen/types/SIDE_EFFECTS.md # apps/cli/src/shared/output/output.layer.ts # apps/cli/src/shared/output/output.layer.unit.test.ts
| expect(text).toContain(`TARGET\n ${target}\n`); | ||
| expect(row).toContain(`--remote[=${hint}]`); | ||
| expect(row).toContain(`--remote=${hint}`); | ||
| expect(row).not.toMatch(new RegExp(`\\]\\s+${hint.replace(/[|[\]]/g, "\\$&")}\\s`)); |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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-refand--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
--remoteflag. Decisions and enforcement: ADR 0033.Targets
One flag,
--remote, on every command that can reach a hosted project:--remote)SUPABASE_PROJECT_ID, then the link file)postgres://…/postgresql://…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--remoteis followed by a word the command takes as an operand and that word is, or nearly is, a branch name, the command stops before doing anything and shows both readings.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-urlandgen types --project-idstill work, routed through--remote, and print a deprecation warning on stderr naming the--remotereplacement and v4 as the removal release.--localis accepted, does nothing, and warns. They're hidden from help.db diff --from/--totake the same values pluslocalandmigrations;remoteis the linked project andlinkedis deprecated.--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 aTARGETsection and labels subcommands local only / remote only / local or remote.Commands that resolve a target print one
Target: …line on stderr, and JSON results carry atargetobject.Hosted consent
--yesor a shell-exportedSUPABASE_YES. ASUPABASE_YESfromsupabase/.envor a working-directory.envdoesn't count, stdin is never read as an answer, and agents never get a prompt.--yes.db pullorpullwhen it would update the remote migration history aborts before any file is written.New
db query --read-onlyaccepts only read statements (SELECT,WITH,VALUES,TABLE,SHOW,EXPLAINor a parenthesised query), runs them in a read-only transaction with a short lock timeout (or the Management API'sread_onlymode on a hosted target), and skips consent. Functions with side effects called from aSELECTstill 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
BREAKING CHANGE: Commands that can run locally now default to the local stack,
--remotereplaces 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 typesandstorage ls/cp/mv/rmnow target the local stack when run without a target flag. Add--remoteto 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_IDon its own no longer selects a hosted project for these commands; it only says which project bare--remotemeans.gen typeswith no target generates from the local database instead of opening a project picker or failing.db schema declarative generateno longer asks which target to use.--linked,--project-ref,--db-urlandgen types --project-idare deprecated: they still work and print a warning on stderr naming the--remoteform.--localdoes nothing and warns. v4 removes all of them, along withdb diff'slinked(useremote). They no longer appear in help.--remote=<value>can't be combined with any of the deprecated flags.--project-refon a database command no longer needs--linked. An empty--project-ref=is now an error instead of being ignored.--passwordonly applies to a hosted target.db diffrejects--remotetogether with--from/--to; use--from=remoteor--to=remote.--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, or when the branch list can't be fetched. Put the operand before--remote(supabase db pull add_profiles --remote) or use--remote=<branch>.HostedMutationConsentRequiredErrorin non-interactive, JSON, stream-json, agent or-o json|yaml|toml|envruns unless--yes(orSUPABASE_YESexported in the shell) is set. Commands that newly need this for unattended runs:--read-onlyfor reads)ypiped on stdin no longer confirms a hosted mutation.SUPABASE_YESinsupabase/.env, or in a.env,.env.localor.env.<NODE_ENV>[.local]file in the working directory, no longer confirms hosted mutations. When one of those files sets it, a shell export of the same value doesn't count either. Use--yes.--prunenow runs even when every function is already up to date.secrets unset --alllists the secrets before asking.HostedMutationDeclinedError("context canceled"). The command-specific cancelled error codes are removed. Decliningdb pullorpullaborts before any file is written; there's no longer a files-only path.--yes. Otherwise the command fails and points tosupabase network-bans remove.migration downandmigration repair --allon the local database no longer prompt.Target: …once on stderr, including read-only commands against a hosted project.config pushno longer prints its own "Pushing config to" line. JSON object results and error envelopes gain atargetobject;db reset'stargetchanges from a string to that object, andconfig pushJSON no longer reportsdeclined_addonsor a skipped status. Arrays and generated artifacts are unchanged.migration upagainst a hosted target reports "Remote database is up to date." instead of "Local database is up to date.", andmigration listlabels the history column "Local DB" when the target is local.--yesfor hosted mutations.