Skip to content

fix(cli): gate vanity-subdomains and network-restrictions behind --experimental (CLI-1972) - #5945

Merged
Coly010 merged 3 commits into
developfrom
columferry/cli-1972-missing-experimental-gate-on-vanity-subdomains-and-network
Jul 27, 2026
Merged

fix(cli): gate vanity-subdomains and network-restrictions behind --experimental (CLI-1972)#5945
Coly010 merged 3 commits into
developfrom
columferry/cli-1972-missing-experimental-gate-on-vanity-subdomains-and-network

Conversation

@Coly010

@Coly010 Coly010 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Current Behavior

Go registers vanityCmd and restrictionsCmd (alongside bansCmd) as experimental (apps/cli-go/cmd/root.go:56-65) and refuses them in PersistentPreRunE with exit 1 and must set the --experimental flag to run this command — before login, ref resolution, telemetry, or any API call (root.go:91-96).

The TS legacy shell only reproduced this gate for network-bans. vanity-subdomains get|check-availability|activate|delete and network-restrictions get|update had zero gate calls: they ran to completion without --experimental — API call made, exit 0, cli_command_executed fired, linked-project.json written — where Go does none of that.

Expected Behavior

All six leaves now fail with Go's exact gate message (exit 1) unless --experimental or SUPABASE_EXPERIMENTAL is set, with no API call, no telemetry event, and no linked-project.json/telemetry.json write behind a closed gate.

The wiring mirrors the merged CLI-1854 precedent (#5766) and the in-repo network-bans reference exactly:

  • legacyValidateOutputFormat(LEGACY_RESOURCE_OUTPUT_FORMATS) runs first, so an invalid -o value still wins over a missing --experimental (cobra enum-validates root's persistent -o at parse time, before PersistentPreRunE).
  • legacyRequireExperimental runs next, before any instrumentation or layer work.
  • legacyManagementApiRuntimeLayer moves from Command.provide to an inline Effect.provide after the gate — Command.provide builds the layer (and eagerly resolves an access token) before the handler's first yield, which would let a missing-token error mask the gate error, exactly the failure mode CLI-1854 fixed for the other three families.

New gate integration suites for both families mirror network-bans.experimental-gate.integration.test.ts: a closed gate fails with LegacyExperimentalRequiredError and zero API requests; an open gate proceeds past the gate to the management-API auth step. The six SIDE_EFFECTS.md files document the gated file writes, the SUPABASE_EXPERIMENTAL env var, the new exit-code row, and the closed-gate telemetry behavior.

Deliberately-open judgement calls

  • vanity-subdomains activate|check-availability mark --desired-subdomain required. When both it and --experimental are omitted, the TS parser reports the missing-required-flag error where Go's gate error wins (cobra runs ValidateRequiredFlags only after PersistentPreRunE). This is a pre-existing framework-level parse-ordering divergence, not introduced here — exit code is 1 either way and each error names the exact flag to add. Documented in both commands' SIDE_EFFECTS.md; these are the first gated leaves with a required flag, so none of the merged gate PRs hit this.
  • The gate-wiring comment/boilerplate is intentionally duplicated per leaf to stay byte-consistent with the merged network-bans/postgres-config/ssl-enforcement precedents rather than hoisting a shared helper in this fix.
  • Release-note callout: anyone who scripted these six commands against the TS legacy shell during the ungated window will now get the hard gate error, matching Go. The message names the fix (--experimental), and SUPABASE_EXPERIMENTAL remains the env escape hatch.

Related Issue(s)

Fixes CLI-1972

https://linear.app/supabase/issue/CLI-1972/missing-experimental-gate-on-vanity-subdomains-and-network

@Coly010

Coly010 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6ddc87a121

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

… match Go CLI (review: codex)

Go marks --desired-subdomain required (cmd/vanitySubdomains.go:67,69) but
cobra validates required flags only after PersistentPreRunE
(cobra@v1.10.2/command.go:985,1005), so the gate, login check, and
project-ref resolution all win over the missing flag. Make the flag
optional at parse time and enforce it in the handler with cobra's exact
wording, inside the telemetry/linked-project ensuring wrappers so
PersistentPostRun side effects still fire on that failure, as in Go.
@Coly010

Coly010 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: 9c6d9c7629

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@Coly010 Coly010 self-assigned this Jul 27, 2026
@Coly010
Coly010 marked this pull request as ready for review July 27, 2026 11:58
@Coly010
Coly010 requested a review from a team as a code owner July 27, 2026 11:58
@github-actions

Copy link
Copy Markdown
Contributor

Supabase CLI preview

npx --yes https://pkg.pr.new/supabase/cli/supabase@9c6d9c7629efd5d2265fbc04d2b1401f2bf4b0aa

Preview package for commit 9c6d9c7.

@Coly010
Coly010 added this pull request to the merge queue Jul 27, 2026
Merged via the queue into develop with commit 78c8a7f Jul 27, 2026
35 checks passed
@Coly010
Coly010 deleted the columferry/cli-1972-missing-experimental-gate-on-vanity-subdomains-and-network branch July 27, 2026 20:03
pull Bot pushed a commit to chizee/cli that referenced this pull request Jul 28, 2026
Paid-feature denials now read the management API's
`entitlement_required` error envelope straight off the failed response
and print the upgrade hint with zero extra API calls. Previously the
suggest path made 2 extra round-trips (project lookup + entitlements)
per gated failure, and only 9 commands were wired at all.

**Changed:**
- **Wired commands stop making extra API calls on denial**: the shared
suggest functions (Go `plan_gate.go`, TS `legacy-upgrade-suggest.ts`)
parse `{ error: { code: "entitlement_required", feature, upgrade_url }
}` first; the entitlements round-trip stays as the fallback for gates
that do not emit the envelope yet (v1 SSO carries no server-side gate at
all).
- **`domains create/get/activate/reverify` and `vanity-subdomains get`
newly show the hint**: envelope-only, no fallback, because the
`custom_domain` entitlement key is plan-level while the gate is a
per-project add-on, so the old round-trip would tell Free orgs to
upgrade on unrelated 404s.
- **`cli_upgrade_suggested` reports the server-authoritative feature**:
`feature_key` comes from the envelope (fixes a `branching_persistent`
denial being logged as `branching_limit`); `org_slug` is parsed from
`upgrade_url`.
- **Go mirror kept 1:1** per the legacy-shell parity doctrine;
`AGENTS.md` telemetry table and the affected `SIDE_EFFECTS.md` files
updated to match.

**Note:** the envelope's `upgrade_url` is server-built and printed
without a host check. This trusts the management API host the same way
every authenticated call already does; a host allowlist would silently
suppress valid hints on profiles whose dashboard URL differs from the
CLI default.

## To test
Requires a Free-org project ref (to hit the gates).

Tested locally against prod (`bun src/legacy/main.ts`, Free org):
- [x] `domains get --project-ref <free-ref> --agent no` prints "Upgrade
your plan: .../org/<slug>/billing" on stderr; `--debug` shows exactly
one HTTP request, no project/entitlements lookups
- [x] `vanity-subdomains get` shows the same hint via the decorated
server gate (since the develop merge bringing in supabase#5945, this command
additionally needs `--experimental` or `SUPABASE_EXPERIMENTAL=1` to get
past the experimental gate)
- [x] Plain 4xx without the envelope on the newly wired commands
produces no hint (integration tests pin the false-positive guard)
- [x] Envelope-less denials on previously wired commands still run the
entitlements fallback unchanged

### Before / after

`domains get --project-ref <free-ref> --agent no` on the same Free-org
project, stderr verbatim (ref and org slug redacted). Before is the
merge-base with develop (`3ad3b875`), after is this branch.

Before, the envelope is already in the raw error JSON but nothing reads
it, so no hint:

```
unexpected get hostname status 400: {"message":"Custom domains require the Custom Domain add-on, available on the Pro plan and above.","error":{"code":"entitlement_required","feature":"custom_domain","upgrade_url":"https://supabase.com/dashboard/org/<slug>/billing"}}
Try rerunning the command with --debug to troubleshoot the error.
```

After, the hint prints first, still exactly one HTTP request (`--debug`
shows only the `/custom-hostname` GET):

```
Your organization does not have access to this feature. Upgrade your plan: https://supabase.com/dashboard/org/<slug>/billing
unexpected get hostname status 400: {"message":"Custom domains require the Custom Domain add-on, available on the Pro plan and above.","error":{"code":"entitlement_required","feature":"custom_domain","upgrade_url":"https://supabase.com/dashboard/org/<slug>/billing"}}
Try rerunning the command with --debug to troubleshoot the error.
```

## Linear
- fixes GROWTH-958
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants