Repository navigation
chore: production deploy - #7253
Open
supabase-cli-releaser[bot] wants to merge 80 commits into
Open
supabase-cli-releaser[bot] wants to merge 80 commits into
supabase-cli-releaser[bot] wants to merge 80 commits into
Conversation
Maps `migration list`, `fetch`, `repair`, `up`, `down`, `squash` and `new`, plus `test db` and `test new`, to the Management API operations they call, so a scoped access token can be granted per command. The six database commands declare `dbConfig` and the linked-project cache under the linked target. `list`, `fetch` and `repair` default to linked; `up`, `down` and `squash` default to local, where no request is made. `--password` (list, repair, squash) removes the login-role call, and `--project-ref` adds the pooler config fetch. `migration new` and `test new` make no calls. `test db` declares the same database config as its hidden alias `db test`. Each command's integration tests run once through the real database config resolver and linked-project cache and check the recorded requests against the declarations. That covers the linked run (pooler config, login role, project lookup for the cache), the local run (no request; for `up`, `down`, `squash` and `test db` a login-role request is also rejected under the default flags), `migration up --linked=false`, and `--password` on `list`, `repair` and `squash`. Also corrects the stale Management API routes in `migration squash`'s `SIDE_EFFECTS.md`. No operations without FGA permissions and no shared block changes. `--linked=false` is exercised through the real layers only on `migration up`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Maps the Management API permissions for the `inspect` commands: the 25 `inspect db` subcommands and `inspect report`. All 25 `inspect db` subcommands run one shared query runner, so they share one declaration: the database-config block (`dbConfig`) with `linked` as the default database target. `inspect report` declares the same block plus `--output-dir`, which makes no API call. On `--local` and `--db-url` neither command calls the Management API. On the linked project they fetch the pooler config (with `--project-ref`) and create a temporary login role unless a database password is already available. Neither command fills the linked-project cache or prompts for a project. Tests run the real database-config resolver, record the requests it makes and check them against the declarations. One linked `db-stats` run is checked against all 25 `inspect db` declarations, taken from the real command tree; `--linked=false`, an empty `--project-ref` and the `--local`/`--db-url` no-request cases are covered separately, as is `inspect report`. Shared pieces: no block or model change, no `oneOf`/`is` conditions of its own, no new operations, no change to `OPERATIONS_WITHOUT_FGA`. The 26 commands leave the pending list. Docs: `inspect db`'s `SIDE_EFFECTS.md` had the wrong login-role and network-ban routes and no concrete pooler route; `inspect report`'s claimed a linked-project cache write that nothing performs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…ase-oss (#7124) ## Summary Claude-resolved `sync/develop-into-next` PRs now get approved without a maintainer when the merge is clean in practice: - every check except `Require fast-forward` is green on the head, - the AI review posted a review on that exact head, - Claude fixed or declined every finding (declining the ones the merge did not introduce), - no `.github/` workflow suggestions are waiting. `sync-repair` plans an `approve` action in that state, and a new `approve` job submits an approval for that commit as `supabase-oss` through the `auto-approve` environment. The approval runs `fast-forward.yml` as usual, which re-checks the approver, the head and the checks before moving `next`. A head moved past the reviewed commit, by a repair commit or a maintainer push, is never re-reviewed, so it still waits for a person. The job skips if the head moved or `supabase-oss` already approved that commit. This reverses the documented rule that the bot never approves sync heads; `release-process.md` records the new accepted decision. Reviewers should confirm the `auto-approve` environment's branch rule lets `sync-repair` runs on `develop` deploy. ## Linked issue - [x] I'm a Supabase maintainer. ## Checklist - [x] The PR title follows Conventional Commits. - [x] Tests added or updated for the change. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
## TL;DR patches a few flakes: - **native launcher pin** failed when a cache sweep briefly locked the artifact pin. it now waits up to 2s like the owner's own pin, and a failed launch carries the launcher's stderr. - **native Realtime** could be marked ready by another process answering its port. it now waits for its own bind line like Pooler, so a lost port relaunches. - **HostProcess argv** blocked the event loop on a slow Windows CIM query. i've made the read async with a named guard. - **Owner.logs resume** could read history before the first record landed. it now waits for output first. - **pinned ports** in the Network tests could land on RDP 3389 on Windows. they now stay under 3000. - **Owner.analytics** now says whether the events table or the row was missing. ## ref: - spotted in: [launcher pin](https://github.com/supabase/cli/actions/runs/38494662586/job/115627588416) [native Realtime](https://github.com/supabase/cli/actions/runs/38415198781/job/115357678754) [HostProcess argv](https://github.com/supabase/cli/actions/runs/38399326861/job/115302578574) [Owner.logs](https://github.com/supabase/cli/actions/runs/38373564250/job/115216209193) [pinned ports](https://github.com/supabase/cli/actions/runs/38536558032/job/115761323171) [Owner.analytics](https://github.com/supabase/cli/actions/runs/38232117245/job/114753854349) --------- Co-authored-by: Andrew Valleteau <avallete@users.noreply.github.com>
This bumps storage from v1.82.1-r0 to v1.83.0-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.83.0-r0 Planned after supabase/slim-services published v1.83.0-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…7123) ## Summary Every Management API call in `apps/cli` now goes through the typed `@supabase/api` client. Each call gets an `api.operation` span named after its OpenAPI operation ID, so telemetry can tell which operations a command really calls. It complements the permission mapping added in #7105, which declares the operations each module calls. Before this PR, a dozen call sites built URLs by hand. Those calls skipped the span, response decoding, and the client's retry and error handling. Some of them existed because the generated client couldn't express what they needed, so this PR fixes the generator and client first, then moves the call sites, then adds a lint rule so new raw URLs fail CI. ```mermaid flowchart LR subgraph before["Before"] direction TB H1["command handler"] -->|"typed call"| C1["@supabase/api"] H2["sso / snippets / advisors /<br/>postgres-config / db query / login …"] -->|"hand-built /v1/... URL"| X1["HttpClient"] C1 --> X1 X1 --> API1[("Management API")] end subgraph after["After"] direction TB H3["every command handler<br/>and shared module"] -->|"api.v1.op(input, options?)"| C2["@supabase/api<br/>span · decode · retry"] C2 --> X2["HttpClient"] X2 --> API2[("Management API")] L["lint rule<br/>supabase/no-raw-management-api-url"] -. "blocks new raw URLs" .-> H3 end ``` ## What changes ### 1. Generator and client (`packages/api`) Four gaps forced callers back to raw HTTP: | Gap | Fix | | --- | --- | | Response enums were closed, so a new server value broke decoding | Multi-value response enums are open: `"ACTIVE" \| "PAUSED" \| (string & {})`. Single-value enums stay closed. | | Unknown request keys were dropped without an error | Request input decodes with `onExcessProperty: "error"`, so a typo fails as `SupabaseApiInputError` before any request is sent | | `const` schemas weren't generated | `const` maps to a literal | | One client meant one host and one token | Every wrapper takes an optional second argument: `{ baseUrl?, accessToken?, headers? }` | The last row is what let login and SSO profile reconciliation drop raw HTTP: ```ts // before: login built its own request because the client had no token yet const response = yield* httpClient.execute( HttpClientRequest.get(`${apiHost}/v1/profile`).pipe( HttpClientRequest.setHeader("Authorization", `Bearer ${token}`), ), ); // after const api = yield* makeApiClient({ baseUrl: apiHost, accessToken: token, userAgent }); const profile = yield* api.v1.getProfile(); ``` ```ts // per-request override on an existing client (sso update against a reconciled profile) yield* api.v1.updateASsoProvider(input, { baseUrl: profile.apiUrl, accessToken: profile.token }); ``` **Retries now back off.** The client used to retry immediately. It now uses exponential backoff with full jitter: ```mermaid xychart-beta title "Maximum delay before each retry (ms, full jitter picks 0 to max)" x-axis ["retry 1", "retry 2", "retry 3", "retry 4", "retry 5"] y-axis "ms" 0 --> 4500 bar [250, 500, 1000, 2000, 4000] ``` | Response | Retried for | Delay | | --- | --- | --- | | `429` | every method | `Retry-After` if present, otherwise backoff | | `5xx` / transport error | `GET` `PUT` `DELETE` `HEAD` only | `Retry-After` if present, otherwise backoff | | other `4xx` | never | — | `POST` and `PATCH` aren't retried on 5xx because they aren't idempotent. A `Retry-After` longer than 10 s isn't waited out: the response goes straight back to the caller. Each retry records `http.request.resend_count` on the span. A single request can override the policy with `retry: { maxRetries, requestTimeoutMs }`: ```ts // functions deploy runs its own rate-limit loop, so the client stays out of it api.executeRaw(operationDefinitions.v1DeployAFunction, input, { retry: { maxRetries: 0 } }); ``` | Caller | Override | Why | | --- | --- | --- | | `functions deploy` upload and bulk update | `maxRetries: 0` | the command's own `429` loop is the only retry layer | | `postgres-config update/delete` PUT | `maxRetries: 0` | a resent write with `restart_database` restarts the database twice | | linked-project cache fill at exit | `maxRetries: 0` | exit isn't held up when the API is failing | | `db query --linked` | `requestTimeoutMs: Infinity` | long queries keep running with no client timeout, as before | | `sso add` / `sso update` | `requestTimeoutMs: Infinity` | the server may fetch `--metadata-url` during the request; no client timeout, as before | ### 2. Self-validating OpenAPI overrides `openapi-overrides.json` patches upstream spec bugs. Patches used to fail silently once upstream fixed the bug, so stale patches piled up. The loader now rejects: - a `replace` without a `test` on the same path directly before it, so the patch states what it expects upstream to say; - a `replace` whose value already matches upstream, because the fix has landed upstream; - a `remove` whose target doesn't exist; - any entry without a `$comment` explaining the upstream bug. ```jsonc { "$comment": "Hostnames stored before the upstream fix can lack `status`; the fix covers only new writes.", "op": "test", "path": "/components/schemas/.../required", "value": ["status", "..."] }, { "op": "replace", "path": "/components/schemas/.../required", "value": ["..."] } ``` With these checks, the overrides go from 37 entries to 25. The removed patches were ones upstream has since fixed, plus a lenient removal of the v2 webhook paths, which now generate normally. ### 3. Call sites moved to the typed client (`apps/cli`) | Area | Before | After | | --- | --- | --- | | `sso add` / `sso update` | string URLs, hand-written response views | `createASsoProvider`, `getASsoProvider`, `updateASsoProvider`, per-request host and token for reconciled profiles. Structured output still prints the server body unchanged. | | `db query` | raw `POST /database/query` | `v1RunAQuery` | | `postgres-config get/update/delete` | raw GET and PUT | GET keeps the raw record (stored keys may predate the schema). PUT validates the keys the user passed against the same strict schema the platform enforces. Stored overrides are resent unvalidated through `executeRaw`'s `extraBody`, so one this CLI version doesn't know is kept. The returned record prints unchanged. | | `snippets list/download` | raw | typed operation. `--output json/yaml/toml` still prints the server body unchanged. | | `db advisors` (linked) | raw | `getSecurityAdvisors`, `getPerformanceAdvisors` | | `config push` cost check | raw | `listProjectAddons` | | login profile, upgrade suggestion, linked-project cache | raw | typed calls. Each builds its own client for its host and token. | | `projects list` / `projects create` | typed client's `executeRaw` | unchanged: `executeRaw` keeps fields the schema doesn't model | The shared `raw-http.ts` helper is deleted. One raw call remains, the device-login poll under `/platform/cli/login`. That route isn't in the Management API spec, so no typed operation exists. It now URL-encodes its parameters and has a commented lint exception. ### 4. Lint rule: `supabase/no-raw-management-api-url` A local oxlint JS plugin (`tools/oxlint/supabase-plugin.js`) reports any string or template literal in `apps/cli/src` (tests excluded) that starts a `/v1/…`, `/v2/…` or `/platform/…` path, including path constants that are joined to a base URL later. Service-scoped paths such as `/auth/v1/` and `/storage/v1/` don't match. ```ts const url = `${apiHost}/v1/projects/${ref}/config/database/postgres`; // ^ supabase/no-raw-management-api-url: Build Management API requests with the // typed `@supabase/api` client (`api.v1.*`, `execute`/`executeRaw`) so the call // emits the `api.operation` span; do not assemble `/v1/`, `/v2/` or `/platform/` URLs by hand. ``` ### 5. cli-e2e placeholder ref Recorded fixtures used `__PROJECT_REF__` as the project ref, which the typed client rejects as a path parameter. That kept login, upgrade suggestion and the linked-project cache on raw HTTP. Fixtures now use `replayfixtureprojref`, a 20-character ref that passes the check, and the replay server no longer rewrites placeholders back and forth. This explains the large fixture rename in the diff. Injected `429`/`5xx` replay responses send `Retry-After: 0`, so the new backoff doesn't slow down the suite. ## User-visible behavior changes Most of these come from validating input locally and decoding responses strictly: - `postgres-config update` and `config push` reject keys and values the platform would refuse before sending a request. An unknown `--config` key reports `unknown Postgres config parameter(s): …` and suggests upgrading the CLI if the parameter is new. Stored overrides are never checked locally. An invalid value reports `invalid Postgres config value for <key>: …`, and a bare number for a size or duration suggests adding a unit (`work_mem=4096kB`). - The SSO attribute-mapping file is decoded strictly, so unknown keys are an error. The `sso` table shows only modelled fields. - `notebooks` rejects an unknown `view`. - `functions deploy` rejects an unknown function status. Unknown custom-domain statuses are shown as returned. - `snippets` output follows the spec's nullability (`description: null` instead of a missing field). - `db query` error output caps the response body and strips control characters. - Idempotent requests now retry on `5xx` and transport errors with backoff. `429` is retried for every method, except where a command opts out (see the overrides table above).
This bumps storage from v1.83.0-r0 to v1.83.1-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.83.1-r0 Planned after supabase/slim-services published v1.83.1-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps analytics from v1.53.0-r0 to v1.53.1-r0. https://github.com/supabase/slim-services/releases/tag/analytics-v1.53.1-r0 Planned after supabase/slim-services published v1.53.1-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This PR was automatically created to sync the generated `@supabase/api` package with the latest Management API OpenAPI document. Changes were detected in the upstream OpenAPI documents exposed by `https://api.supabase.com/api/v1-json` and `https://api.supabase.com/api/v2-json`. Co-authored-by: jgoux <1443499+jgoux@users.noreply.github.com>
) Maps `gen types`, `gen signing-key` and `gen bearer-jwt` to the Management API operations they call, so a scoped access token can be granted per command. `gen keys` stays pending. `gen types` picks its target from flag values, so it declares flag conditions instead of a database target: `--local`, `--db-url` and `--postgrest-v9-compat` turn every call off. TypeScript calls the types endpoint. Other languages look the project up, fall back to the preview branch config on a 404, and reach the database through the database config resolver (login role, pooler config) and the pooler retry. The project picker and the linked-project cache fill are declared too. `signing-key` and `bearer-jwt` make no calls. Shared block change: a target-free `dbConfigPoolerFallbackLinked` for commands that choose their target from flag values (`dbConfigPoolerFallback` is built from the same entries), and the first fallback context is reworded to fit every command. Dump, pull and the db-config tests are unchanged. `gen types` runs through the real database config resolver and linked-project cache with the Management API recorded, for the TypeScript, pooler, IPv6 retry, preview branch, picker, `--local=false` and `--db-url` paths. On this command the pooler, login-role and network-ban calls are each declared by more than one source, so removing one source alone is not caught by a test; the grants are per operation either way. No operations without FGA permissions. Only `typescript` and `go` run through the recording mocks. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> Co-authored-by: Andrew Valleteau <avallete@users.noreply.github.com>
Maps `branches create|delete|disable|get|list|pause|unpause|update` and `backups list|restore` to the Management API operations they can call, so scoped PATs can be granted per command. Drift tests keep each declaration in step with what the command does. **Shared changes.** - New `branchPrompt` block (`v1-list-all-branches`, required) for the branch listing shown when no branch is given on a TTY. - The context text of `projectRefPicker`, `linkedProjectCache` and `branchRef` is reworded so it fits every command that includes it. No condition or label changed. **Blocks used.** `projectRefPicker` and `linkedProjectCache` (all ten), `branchPrompt` and `branchRef` (delete, pause, unpause, update; `get` uses `branchPrompt` and declares its name lookup itself), `upgradeSuggestion` (create, update). **Conditions.** `branches get` names the global output flags: the branch's API keys and pooler config are read for `-o` other than `pretty`, and with no `-o` when the output format is json or stream-json (`--output-format`, `--agent yes`, or a detected coding agent). This is the first declaration to name global flags in `when`. **Findings.** - `pause` and `unpause` call the project-level pause and restore operations, so they need `project_admin_write`, not a branching permission. - `disable` needs only `branching_production_delete`. - A 4xx on `create` or `update` triggers the upgrade-suggestion lookups (best-effort). **Tests.** Each command's integration test runs the project picker, the branch picker where it applies, and the real linked-project cache, and checks the exact request sequence. `branches get` covers each output mode that changes its calls; the agent-detected case is simulated through the output format. **Not covered.** Context entries (`branchPrompt`, `branchRef`, the cache, the agent-detected entry) are asserted by request sequence only. `--persistent=false` is not run on every command. **Docs.** The `branches` `SIDE_EFFECTS.md` files now give the cache path as `<workdir>/supabase/.temp/linked-project.json`, and `get`'s doc states when the API keys and pooler config are read. All ten docs now list the project picker, branch picker and linked-project cache routes, and say when the cache file is written. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> Co-authored-by: Andrew Valleteau <avallete@users.noreply.github.com>
Maps the Management API operations behind `config diff`, `config pull` and `config push`, so scoped access tokens can be given matching permissions. This changes no command behaviour. **Commands:** `config diff`, `config pull`, `config push` (removed from the pending list). **New shared blocks** - `configTarget` (`project-target.ts`): the branch lookup for a non-ref `--project-ref` (`v1-get-a-branch-config` for a UUID, `v1-get-a-branch` for a name), else the project picker. Reuses `branchRef` and `projectRefPicker`. - `configPullRun` (`config-pull-run.ts`): the remote config read `v2-get-project-config`, shared with top-level `pull`. **Declarations** - `diff`: `configTarget`, the config read, linked-project cache. - `pull`: `configTarget`, `configPullRun`, linked-project cache. - `push`: `configTarget`; the target probe (best-effort); the add-ons list and config read (required); one required write per resource (api, db settings, network restrictions, SSL enforcement, auth, storage, webhooks), each with a context naming what triggers it; the branch listing (best-effort); linked-project cache. **Conditions:** `--project-ref` present gates the branch lookups. Write triggers, the probe and the branch listing are contexts, since they depend on `config.toml` contents and `.temp` state rather than flags. **Worth knowing** - `config push` always needs the add-ons read, even when nothing is billable. - Which writes happen depends on which sections the token can read. - Not covered by a drift check: `push`'s cache inclusion (its project probe shares an operation), and the write contexts, which are asserted by request. **Docs:** three wrong rows in `config push/SIDE_EFFECTS.md` corrected (branch lookup parent, cost matrix order, webhooks key). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Record the Management API operations behind 20 project-data commands, so
scoped access tokens can be built from what each command actually calls:
`sso add|info|list|remove|show|update`, `domains
activate|create|delete|get|reverify`, `secrets set|unset`, `snippets
download`, `notebooks pull|push` and `storage cp|ls|mv|rm`.
Each command declares its own operations and composes the existing
`projectRefPicker`, `linkedProjectCache`, `upgradeSuggestion` (sso
commands that call the upgrade gate) and `storageCredentials` blocks. No
user-facing behaviour changes.
New shared pieces:
- `notebooksList`, `notebookRead` and `notebookCreate` blocks in
`notebooks.shared.ts`, for the calls both notebooks commands make; each
command adds its own context.
- `STORAGE_PERMISSIONS` in `storage.flags.ts`. Storage routes on the
parsed `--local` value, so its credentials and cache entries use `{
flag: "local", is: false }` rather than a database target;
`--local=false` targets the linked project.
- A `recordManagementApi` option in the storage test setup and a
`realLayers` option in the notebooks test setup, so those tests run the
real picker, cache and credentials lookup and check the recorded
requests against the declarations.
Conditions: `secrets unset` lists secrets only when no names are given,
and the notebooks entries carry contexts for the id argument, the
reconcile answers and which files exist. `exact` drift checks skip
those, so they are asserted by request sequence.
Things worth knowing:
- `sso info` makes no call of its own, but needs a token and fills the
project cache; its doc said otherwise.
- `snippets download` resolves a project ref it never sends, so it fails
unlinked even though the snippet call needs no ref.
- `storage cp` resolves credentials before its source/destination
checks, and `rm` before its confirmation.
- The `--linked`/`--local` mutual-exclusion error isn't modelled, so
storage over-declares when both are set to false.
- The experimental gate on storage isn't modelled; declarations describe
a run with the gate open.
- Dropping `linkedProjectCache` from `sso add|list|remove|update` fails
no test, because `upgradeSuggestion` also declares the project lookup.
- The `SIDE_EFFECTS.md` files for these commands had the wrong
linked-project cache path and conditions, and were missing the picker
and cache-fill routes; corrected in the last commit.
No `OPERATIONS_WITHOUT_FGA` changes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
#7170) Maps the Management API operations behind the projects, organizations and linking commands, so scoped access tokens can be given matching permissions. This changes no command behaviour. **Commands:** `projects api-keys|create|delete|list`, `orgs create|list`, `link`, `unlink`, `bootstrap`, `services` (removed from the pending list). **New shared block:** `organizationPicker` (`projects.prompt.ts`): the organization listing made when no organization ID is given. **Declarations** - `projects api-keys`: the keys read, with `reveal=true` when `--reveal` is set; the project picker and the cache fill. - `projects create`: the create call, the organization listing (via the picker, when `--interactive` allows a prompt), the cache fill. - `projects delete`: the delete call and a project listing for its own picker (it ignores the env and `.temp` ref, so it doesn't reuse the shared picker), the cache fill. - `projects list`, `orgs create|list`: their single call; `list` also fills the cache for a linked ref. - `link`: project, API keys (`reveal=true`), storage and pooler reads, the branch listings, the picker, the cache fill. `unlink` makes no API calls. - `bootstrap`: organization listing (always, since it passes no org ID), project create, API keys, health, storage and pooler reads, cache fill. The pooler read is required because the migration push depends on the pooler URL it saves on IPv4-only networks. No login call is declared: with no token the command fails before the login runs. - `services`: the linked project's API keys and project reads (both best-effort), cache fill. **Conditions:** `--reveal` is tested by value. `--interactive` is optional, so it is declared as two entries (absent, and present with "is true" in the context). Interactive-terminal, config and argument-shape prerequisites are contexts. **Worth knowing** - `bootstrap`'s browser login is unreachable when no token resolves; the platform-API layer fails first. - `link --password` and `SUPABASE_DB_PASSWORD` have no effect. - Not covered by a drift check: `link`'s cache inclusion and its second branch-listing entry (a required entry for the same operation shadows them), and `bootstrap`'s pooler entry (asserted from the handler). **Docs:** wrong rows corrected in the projects, bootstrap and link `SIDE_EFFECTS.md` files (environment variable name, create body, org listing, bootstrap request body and cache-fill attribution, the `--password` row). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Follow-up to #7129. CLI e2e jobs now set the Test workflow's wall time. - Split the `@supabase/cli-e2e` suite into two shards; it was the single longest job. - The e2e sequencer packs shards by estimated wall time: concurrent files share half the `e2e` project's workers, because they contend for one Docker daemon, and exclusive files add their full duration. - Drop the `actions/cache` + `go build` steps in e2e jobs; `supabase#build` already builds or restores `@supabase/cli-go` through turbo. - The `gen types` e2e readiness probe connects over TCP. The Postgres entrypoint's init server listens only on the Unix socket, so a socket probe could pass before the final server started and the seed then failed mid-restart. - macOS port tests run on Blacksmith macOS runners; GitHub-hosted macOS runs took up to several times as long. Measured over repeated CI runs against develop after #7129: | | develop after #7129 | This PR | Change | | --- | --- | --- | --- | | Test workflow wall time (median) | 3.9 min | 2.75 min | about 30% faster | | Slowest CLI e2e job | 3.3 min | about 2.5 min | about 25% faster | | macOS port tests job | 2.1–8.5 min | 0.7–0.9 min | 3–10x faster | | Files on the Docker-heavy CLI shard | 32 | 10 | | Required check names are unchanged. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…7139) ## Summary `supabase db diff --from linked --to linked` failed with `password authentication failed for user "cli_login_postgres"` when no database password was available (no `--password`, `SUPABASE_DB_PASSWORD`, or `supabase/.env`). Each linked side called the resolver separately, minting two temporary login roles; the second mint invalidated the first side's credentials. The explicit-mode `linked` endpoint is now resolved once and reused for both sides, so a run issues a single `POST /v1/projects/{ref}/cli/login-role`. Runs with a password configured are unchanged. Also corrects routes in the `SIDE_EFFECTS.md` files for `db diff`, `db pull`, `db dump`, `migration squash`, `inspect db`, and `test db`: temp login role is `POST /cli/login-role`, pooler fallback is `GET /config/database/pooler`, and ban listing is `POST /network-bans/retrieve`. ## Linked issue Supabase maintainer change; no linked issue. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Declares the Management API permissions for the project network commands: `vanity-subdomains` (get, check-availability, activate, delete), `ssl-enforcement` (get, update), `encryption` (get-root-key, update-root-key), `network-bans` (get, remove), `network-restrictions` (get, update) and `postgres-config` (get, update, delete). Each leaf composes the project-ref picker and the linked-project cache. **Shared change:** `postgresConfigRead` and `postgresConfigWrite` are new blocks in `postgres-config.shared.ts`, next to the helpers that issue the calls (`postgres-config` reaches the API through `executeRaw` on the typed client, so the drift matcher resolves them). `get` uses the read block, `update` the read block (only when `--replace-existing-overrides` is false) plus the write block, and `delete` both. **Conditions declared** - `network-restrictions update`: `is` on `--append` selects the POST `/apply` or the PATCH. - `ssl-enforcement update`: the PUT is declared once per switch (`--enable-db-ssl-enforcement`, `--disable-db-ssl-enforcement`), each with `is: true`. - `vanity-subdomains activate` and `check-availability`: the call is declared for `--desired-subdomain` present and for `oneOf: [""]`, because the handlers send an empty value to the API. Both also compose `upgradeSuggestion`. **Findings** - `v1-get-pgsodium-config` is a GET that needs `project_admin_write`, and `check-availability` is a POST that needs `vanity_subdomain_write`. - `postgres-config` makes no restart call and has no delete endpoint: `delete` is a GET followed by a PUT, and `--no-restart` only shapes the PUT body. - `network-bans remove` fills the linked-project cache even when an IP is invalid, while `network-restrictions update` and `ssl-enforcement update` validate before resolving the ref and fill nothing. `postgres-config update` only checks that each `--config` value is a `key=value` pair before resolving; an unknown key is rejected after the ref resolves and the GET, so the cache is still filled. `encryption` resolves the ref outside the cache-fill scope. - The SIDE_EFFECTS docs pointed the cache at `~/.supabase/<workdir-hash>/`, described a `.supabase/config.json` that nothing reads, and omitted the picker and cache-fill routes. They now match the code. **Not covered** - The experimental gate is not modelled; declarations describe a run whose gate is open. - `ssl-enforcement update` over-declares when both switches are true, a run that fails before any call. - Picker, cache and upgrade-lookup entries carry a context, so they are checked by request sequence rather than by the exact drift check. Dropping `linkedProjectCache` from vanity `activate` or `check-availability` fails no test, because `upgradeSuggestion` also declares `v1-get-project`. - `--switch=false` and `--desired-subdomain=` are exercised with parsed values, not through the flag parser. - No new entries in `OPERATIONS_WITHOUT_FGA`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Maps which Management API operations the `functions` and `compute` commands call, and under which flags, so scoped tokens can be granted the right permissions. The mapping is recorded in each command's declaration, and integration tests check it against the requests the handlers actually make. There is no user-facing change. **Commands (12):** `functions` list, delete, download, deploy, new, serve; `compute` list, status, delete, logs, push, new. **Blocks used:** `projectRefPicker` and `linkedProjectCache` on every command that calls the API. **New shared blocks:** - `functionsDownload` in `src/shared/functions/download.ts`, next to `downloadFunctions`, so `pull` can compose it later. - `computeRead` in `src/shared/compute/compute-api.ts`, used by status, delete, logs and push. **`is` conditions declared:** - `functions deploy`: `use-api`, `use-docker`, `no-verify-jwt` and `prune` select the API path, the Docker path, the JWT list lookup and prune. - `compute logs`: `follow`. - `compute push`: `no-wait`. **Notes** - `functions download` lists `--use-api`, `--use-docker` and `--legacy-bundle` as flags that don't change the call set. Under `--legacy-bundle` the Go binary makes its own list, get and body calls, which aren't recorded. - `compute delete` marks its lookup best-effort: it carries on to the DELETE after a 403 on the GET. - `compute logs` needs the `analytics_logs_read` permission, unlike the other compute commands. - `functions new`, `functions serve` and `compute new` make no API calls and declare none. Their layers provide no API client, so a call fails type-checking. - The context-only entries (deploy create, update, bulk update, prune delete and the API-path list; download's get and list; logs' history and compute lookup) carry a `context` and are asserted through explicit request sequences, not `exact` drift checks. - `--no-verify-jwt=false` on deploy over-declares the API-path list call, and `compute logs --tail 0` can't be expressed as a condition. - The registry unit test's "undeclared command in a gated tree" example moves from `compute list` to `stack list`. - `OPERATIONS_WITHOUT_FGA`: no change. The `SIDE_EFFECTS.md` commit corrects the cache-fill wording and adds the missing picker and cache rows for the functions and compute commands. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
… --all (#7177) `supabase stop --no-backup` no longer exits 1 when a Docker CLI older than 23.0 talks to a newer daemon. `docker volume prune --all` is a client-side flag. Docker CLI 20.10 doesn't have it, and newer CLIs reject it when the negotiated API version is below 1.42. The gate only read the daemon's API version, so a 20.10 client against a 25.0 daemon was handed `--all`: the containers were already gone, the volume prune failed, and `stop` reported `StopVolumePruneError`. The gate now reads `{{.Client.APIVersion}} {{.Server.APIVersion}}` and passes `--all` only when both are 1.42 or later. A request negotiated below 1.42 already prunes named volumes without the flag, so nothing is left behind. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This bumps realtime from v2.143.4-r0 to v2.144.0-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.144.0-r0 Planned after supabase/slim-services published v2.144.0-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Maps the permissions of `login`, `whoami` and the local-only commands
(`logout`, `feedback add|delete`, `issue bug|docs|feature`, `completion
bash|fish|powershell|zsh`, `telemetry enable|disable|status`), so scoped
tokens can tell which of them need Management API access.
- `login` composes the existing best-effort `loginProfile` block: the
profile lookup only stitches the telemetry identity, and login succeeds
when it fails.
- `whoami` declares `v1-get-profile` as required, since the profile is
its whole output.
- The other 13 leaves can't reach the Management API, so they declare no
operations and classify their flags as having no API effect. They get no
drift checks, which would be vacuous.
- `v1-get-profile` has no FGA permission in the spec, so it becomes the
first `OPERATIONS_WITHOUT_FGA` entry (a shared test change).
- Tests: `login` now runs the real login API layer against a recording
HTTP mock (`--token` path, a 403 on the profile lookup, and the browser
flow); `whoami` gains drift checks on its happy and 401 paths.
Notes:
- `login`'s device-flow poll (`/platform/cli/login/{sessionId}`) isn't
in the spec, so it can't be declared.
- `bootstrap` can reach the same profile call when no token resolves;
that path isn't changed here.
- `whoami -o …` fails before its required call, so the declaration
over-declares that error run.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Maps the top-level `pull` command to the Management API operations it calls. `pull` composes the config-pull, linked-project cache, database config and function-download blocks directly, because its sub-commands' declarations describe different flags and gating. The database and function steps are gated on `--dry-run` being false, since a dry run stops after the target and config reads. `pull` also declares its own pooler-config entry: its database steps always get the resolved ref, so the pooler fetch happens without `--project-ref`. Findings: a bootstrap run resolves the database twice, so it fetches the pooler config and mints a login role twice; step failures are isolated but `pull` still fails, so every step call is required. Untested conditions: a declined confirmation (it over-declares, like `--dry-run`), a failing step under the real layers, and a branch-name target under the real layers. `dbConfigLinked`, `dbConfigPoolerFallbackLinked` and the own pooler entry overlap, so dropping one alone fails no test. No blocks without a real-layer test, no best-effort label turned required, no `OPERATIONS_WITHOUT_FGA` entries. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…ns (#7179) Records which Management API operations each of these commands calls, as `withPermissions` declarations checked by the existing completeness and drift tests. - `init`, `stack destroy|list|logs|prepare|restart|start|status|stop` and legacy `start` and `stop` make no Management API calls; each declares no operations and classifies every own flag, hidden ones included. The aliased `start|status|stop` in the stack tree are covered by the same declarations. - Legacy `status` declares one best-effort `v1-list-all-branches` through the existing `branchTarget` block, with a context naming the linked-ref, cached-parent and token prerequisites. - Legacy `start` gets a drift check on the requests its bucket-seeding test records, and legacy `status` gets HTTP-level 200 and 403 branch-lookup tests checked against the declaration. - No new blocks, conditions or `OPERATIONS_WITHOUT_FGA` entries. Untested: the stack `start` drift check and the `SUPABASE_PROJECT_ID=''` case for `status`. - The `declaredPermissions` test now asserts `Declared` for `stack list` instead of `Undeclared`, because that leaf is no longer undeclared. A second test asserts that every pending command still in the tree is reported as `Undeclared`, and fails once the pending list is empty, at which point it can be deleted. - Reworded the `status` runtime-layer comment, which claimed `status` makes no Management API calls. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
## Summary **TL;DR:** A new `Flaky check` workflow runs one commit on many fresh runners with retries off and reports every test that both passed and failed. It runs nightly on `develop`, weekly for e2e, and on demand through the `run-flaky-check` PR label, so PRs that fix flaky tests can show the fix holds. ```mermaid flowchart LR T["nightly develop / weekly e2e<br/>run-flaky-check label / dispatch"] --> P["plan: commit, suites, N runs"] P --> M["N fresh runners per suite<br/>retries off, JUnit output"] M --> R["report: flaky / always failing<br/>runs without results"] R --> O["artifact + job summary<br/>PR comment"] ``` ### Why Flaky tests surface one at a time in unrelated PRs and get retried away. There is no way to ask "is this commit stable?" or to show that a flaky-test fix actually holds. `stack-stress.yml` covers two stack scenarios only. ### What changed - `flaky-check.yml` runs: - nightly: unit + integration ×10 on `develop` - weekly: CLI e2e (4 shards + `cli-e2e`) and stack whole-stack e2e ×3 - `run-flaky-check` label: unit + integration ×10 on the PR merge commit, plus a `focused` suite that repeats the tests the PR affects (`vitest --changed` against the merge base, `--repeats=4`, ×10 runs) - manual dispatch: any ref, suites, run count, repeats, and file filter - The label is removed when the run starts, so re-adding it starts another run. - A report job classifies each test as flaky (mixed results) or always failing. It also lists runs that produced no usable results: timeouts, crashes, missing JUnit reports, or missing uploads. Each failed `--repeats` execution counts separately. - The report goes to the job summary, a `flaky-check-report` artifact (`report.md`, `report.json`), and one sticky PR comment. The report job fails when it finds anything. - `.github/MAINTAINERS.md` documents the triggers and the `focused` caveats. ### Linked issue None; maintainer change. The `run-flaky-check` label already exists. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Local Studio logs pages fail with "Backend error" on the default Postgres analytics backend (#7155). Logflare 1.52.0 and later cast endpoint query parameters such as `@project` to `jsonb` when translating to Postgres, so every logs query fails with `invalid input syntax for type json` (Logflare/logflare#4158). - Pins `analytics` back to Logflare 1.50.15 (slim `v1.50.15-r1`), the last release before the regression, in the stack catalog and both service Dockerfiles. - Adds an `UPGRADE_HOLDS` entry for `analytics` to the slim release planner so the bump bot can't upgrade it again. Hotfix revisions of 1.50.15 still go through. Remove the entry once Logflare ships a fix. The per-minute log chart still fails on the Postgres backend because `IFNULL` is never translated (Logflare/logflare#4159); that has been the case in every Logflare version. A local database first created by Logflare 1.53.x (CLI 2.121.0) keeps its Oban tables only in `_analytics`, which 1.50.15 doesn't read. On those databases the analytics alerting and event-touch workers restart in a loop and log `relation "public.oban_jobs" does not exist`. Log ingestion and queries are unaffected, and `supabase stop --no-backup` clears it. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This bumps storage from v1.83.1-r0 to v1.83.3-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.83.3-r0 Planned after supabase/slim-services published v1.83.3-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Closes the command permission mapping: every command leaf now declares a permission status, so the pending list is gone. The six commands that still hand off to the bundled Go binary (`db branch create|delete|list|switch`, `db remote changes`, `gen keys`) declare `unmapped` with reason `go-delegated`. The completeness test now requires a declaration on every leaf, and a second check fails if a path on the go-delegation allowlist stops being a real leaf declared `unmapped`. The pending list and the registry test that depended on it are removed; the `Undeclared` lookup result stays for command groups. Findings: `unmapped` records how these commands are implemented, not what they need. Reading the Go source, the four `db branch` leaves make no Management API calls, `gen keys` makes one (`V1GetPostgrestServiceConfig`), and `db remote changes` runs the same diff as `db diff` against the linked database. All six could be mapped later, which would let the `unmapped` status and its allowlist go. Untested conditions: the six `unmapped` commands make no claim, so nothing checks them. No blocks without a real-layer test, no best-effort label turned required, no `OPERATIONS_WITHOUT_FGA` entries. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…d stops (#7184) Two native stacks started at the same time could end up running their Logflare backends on the same private port. Each native backend port was held only by a placeholder listener, and that listener closes right before the backend is spawned. Logflare takes a few seconds to boot and then binds with `SO_REUSEPORT`, so another stack's scan could pick the same port in that window, and both Logflares bound it. The kernel then split connections between the two stacks, so a request carrying one stack's API key could reach the other stack's Logflare and get a 401. This was the intermittent `401` from the analytics marker in the native whole-stack lifecycle e2e. - Native backend ports (10000–19999) are now claimed in the per-user port registry (`ports.sqlite`). The claim is taken before the placeholder binds and released only after the backend process stops, so other stacks' scans skip the port for the whole handoff. - A claim whose stack has no running owner, or whose registration is gone, is taken over. A same-stack claim not held by the current owner process is treated as left by a dead owner. - A registry error fails the port scan straight away instead of being retried as a bad port. - `CatalogOptions.claimNativePort` is required, and the owner wires it to the registry. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This bumps realtime from v2.144.0-r0 to v2.144.2-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.144.2-r0 Planned after supabase/slim-services published v2.144.1-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This PR was automatically created to sync API types from the infrastructure repository. Changes were detected after refreshing `apps/cli-go/api/v1-openapi.yaml` from the latest spec from infrastructure, and `apps/cli-go/pkg/api` is regenerated from it. The snapshot diff is the upstream API change and is worth reading: it is marked `linguist-generated`, so GitHub collapses it behind a "Load diff" click rather than hiding it. Co-authored-by: supabase-cli-releaser[bot] <246109035+supabase-cli-releaser[bot]@users.noreply.github.com>
…7217) ## TL;DR brings the `src/cli` area under the effect lint, tests included ## whats introduced? - `src/cli/**` joins the allow list, and `docs`, `output` and the top level files follow in the next PR of this stack. - shell completion runs as one effect with a named span, taking the clock and the run id from `Clock` and `Crypto`, so its output, exit codes and telemetry event stay the same and the capture still lands before exit - `isValidGoRfc3339` checks dates through `DateTime`, with a few new test cases for `gen bearer-jwt --exp` - the cli tests move to `it.effect`, `it.live` and a new `withTempCliProject` helper ## ref - related: CLI-2742
…LI-2742) (#7218) ## TL;DR brings the `docs` and `output` areas under the effect lint, with the top level `main.ts` and `globals.d.ts` ## whats introduced? - `docs/**`, `output/**`, `main.ts` and `globals.d.ts` join the allow list, and `output` and the top level files needed no changes - `readDocsContent` walks the docs through the `FileSystem` and `Path` services with a named span, keeps a leading BOM, and wraps YAML and shape failures in a typed `DocsExamplesError` that keeps the original error - the generator script rethrows the original fs, YAML or shape error, so the generated spec, error messages and exit codes stay the same, but an fs failure such as a missing `docs/supabase` or `templates/examples.yaml` no longer prints a source code frame or stack, since async `FileSystem` errors carry none - the walk matches develop on any tree that is not changing during the walk, except a directory `stat` cannot reach, such as one inside a readable but not searchable directory or past the path length limit, which is now skipped, or fails on its read when named `.md`, since the `FileSystem` service has no dirent types - the docs tests move to a lazy loader, scoped temp dirs and `Effect.flip` checks on the same messages ## ref - closes: CLI-2742
This adds postgres 17.11.0.006-orioledb-r0 as a new release line. https://github.com/supabase/slim-services/releases/tag/postgres-17.11.0.006-orioledb-r0 Planned after supabase/slim-services published 15.19.0.006-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps postgres from 15.19.0.005-r0 to 15.19.0.006-r0. https://github.com/supabase/slim-services/releases/tag/postgres-15.19.0.006-r0 Planned after supabase/slim-services published 15.19.0.006-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps postgres from 17.11.0.005-r0 to 17.11.0.006-r0. https://github.com/supabase/slim-services/releases/tag/postgres-17.11.0.006-r0 Planned after supabase/slim-services published 15.19.0.006-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…2749) (#7216) ## TL;DR stack `rest` now comes up even when a `db_pre_request` hook rejects requests without an app header. ## whats missing? the stack never started PostgREST's admin server, so it checked readiness with a plain `GET /`. that request goes through the project's `db_pre_request` hook, which answers 403 to anything without the header, so rest never looked ready. eager start timed out, and lazy start gave the first API request a 502 after a min ## now fixed by: rest now runs the admin server and checks its `/ready` same as legacy start, so the hook never sees the probe. recipes pick the probed port with a new optional `healthPort`, which defaults to `http` <details> <summary>visual rep:</summary> ```mermaid flowchart TB subgraph before direction LR A1["readiness probe"] -->|"GET /"| B1["data port"] B1 --> C1["db_pre_request hook"] C1 -->|"403"| D1["never ready (eager timeout, lazy 502)"] end subgraph after direction LR A2["readiness probe"] -->|"GET /ready"| B2["admin server"] B2 -->|"200 once the schema cache loads"| D2["ready"] end before ~~~ after ``` </details> ## ref: - closes: #7215
This PR was automatically created to sync API types from the infrastructure repository. Changes were detected after refreshing `apps/cli-go/api/v1-openapi.yaml` from the latest spec from infrastructure, and `apps/cli-go/pkg/api` is regenerated from it. The snapshot diff is the upstream API change and is worth reading: it is marked `linguist-generated`, so GitHub collapses it behind a "Load diff" click rather than hiding it. Co-authored-by: supabase-cli-releaser[bot] <246109035+supabase-cli-releaser[bot]@users.noreply.github.com>
This bumps realtime from v2.145.3-r0 to v2.145.4-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.145.4-r0 Planned after supabase/slim-services published v2.145.4-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps edge-runtime from v1.77.4-r0 to v1.77.5-r0. https://github.com/supabase/slim-services/releases/tag/edge-runtime-v1.77.5-r0 Planned after supabase/slim-services published v1.77.5-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps realtime from v2.145.4-r0 to v2.145.5-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.145.5-r0 Planned after supabase/slim-services published v2.145.5-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
supabase-cli-releaser
Bot
requested review from
a team and
supabase-oss
as code owners
October 20, 2026 02:22
This bumps postgrest from v16.4-r0 to v16.5-r0. https://github.com/supabase/slim-services/releases/tag/postgrest-v16.5-r0 Planned after supabase/slim-services published v16.5-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps edge-runtime from v1.77.5-r0 to v1.77.6-r0. https://github.com/supabase/slim-services/releases/tag/edge-runtime-v1.77.6-r0 Planned after supabase/slim-services published v1.77.6-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Summary
Stack mode can now:
- run any service version that slim-services has published;
- match the hosted project's versions after `supabase link`;
- reproduce the whole local stack from one committed file,
`supabase/supabase.lock`.
Before this change, every service version was compiled into the CLI. You
couldn't try a hotfix, match production, or keep a team on the same
builds without everyone running the same CLI release.
Legacy (docker-compose) mode does not change, except for one fix: a
`SUPABASE_PROJECT_ID` naming another project than the linked one now
gets its pooler URL from the Management API, as `--project-ref` already
did, instead of reusing the linked project's saved
`supabase/.temp/pooler-url`. Everything below applies only to stack mode
(`SUPABASE_EXPERIMENTAL_STACK=1` or `[experimental] stack = true`).
> Version numbers and project refs in the examples are illustrative.
---
## The mental model: it works like a package manager
If you know `package.json` + `pnpm-lock.yaml`, or `Cargo.toml` +
`Cargo.lock`, you already know this.
| Package manager | Supabase stack mode | Committed? | Purpose |
| --- | --- | --- | --- |
| `package.json` | `[local.services]` in `supabase/config.toml` | ✅ |
What you **ask for**: `linked`, `bundled`, `v2.197.0`, `v2.197.0-r1` |
| `pnpm-lock.yaml` | `supabase/supabase.lock` | ✅ | What you **got**:
the exact build of every service, pinned by digest and checksum |
| — | `supabase/.link.json` | ❌ (gitignored) | Which hosted project or
branch *this checkout* targets |
| `pnpm install` | `supabase start` / `supabase stack install` | |
Resolves what changed, then runs or downloads the locked builds |
| `pnpm update` | `supabase stack update [svc…]` | | Re-resolves on
purpose and rewrites the lock |
| `pnpm outdated` | `supabase stack outdated` | | Read-only report of
what an update would change |
| `pnpm install --frozen-lockfile` | `supabase start --frozen` /
`supabase stack install --frozen` (implied when `CI` is set and a lock
exists) | | Fails instead of changing the lock |
```mermaid
flowchart LR
subgraph git["Committed to git"]
CFG["supabase/config.toml<br/>[local.services]<br/><i>what you ask for</i>"]
LOCK["supabase/supabase.lock<br/><i>what you got</i>"]
end
subgraph checkout["This checkout only"]
LINK["supabase/.link.json<br/><i>target hosted project</i>"]
end
HOSTED[("Hosted project")]
RELEASES[("slim-services releases")]
CFG -->|specifiers| START["supabase start<br/>stack install / stack update"]
RELEASES -->|published builds + checksums| START
START -->|writes when stale| LOCK
HOSTED -->|versions it runs| LINKCMD["supabase link"]
LINKCMD --> LINK
LINKCMD -->|records observations,<br/>resolves 'linked'| LOCK
LOCK -->|exact pins| STACK[("Local stack")]
```
---
## How a version is asked for
```toml
[local.services]
default = { version = "linked" } # the default: follow the linked hosted project
[local.services.auth]
version = "v2.197.0" # an upstream version: its highest published revision
[local.services.realtime]
version = "v2.144.2-r1" # exactly revision 1 of that upstream version
```
`bundled` means "the version this CLI ships with". `[db]
orioledb_version` is shorthand for the database specifier, and a
database version must match `db.major_version`.
```mermaid
flowchart TD
SPEC["specifier of a service"] --> KIND{"which kind?"}
KIND -->|"linked (default)"| LINKED{"is the project linked,<br/>and does it report a version?"}
LINKED -->|never linked| B1["bundled pin<br/>unlinked"]
LINKED -->|service not reported| B2["bundled pin<br/>unobserved"]
LINKED -->|reported| PUB{"published by<br/>slim-services?"}
PUB -->|no| B3["bundled pin<br/>unpublished"]
PUB -->|yes| R1["the hosted release<br/>resolved"]
KIND -->|bundled| R2["this CLI's bundled pin"]
KIND -->|"v2.197.0"| R3["highest published revision<br/>of v2.197.0"]
KIND -->|"v2.197.0-r1"| R4["exactly v2.197.0-r1"]
```
The fallback reasons (`unlinked`, `unobserved`, `unpublished`) are
recorded in the lock, so nobody has to guess why a service isn't running
the hosted version.
## What the lock contains
One entry per service (database, rest, auth, realtime, storage,
imgproxy, functions, studio, pgmeta, mail, analytics, pooler). Keys are
sorted and the document is written atomically, so diffs and merge
conflicts stay readable.
```jsonc
{
"$schema": "https://supabase.com/docs/cli/supabase-lock/v1.schema.json",
"lockfileVersion": 1,
"linked": { // the remote the observations came from
"project": "abcdefghijklmnopqrst",
"branch": { "ref": "abcdefghijklmnopqrst", "isDefault": true },
"observedAt": "2026-10-16T09:12:44.000Z"
},
"services": {
"auth": {
"specifier": "linked", // what config.toml asked for
"linked": { "raw": "v2.196.1", "version": "v2.196.1" }, // what the hosted project reported
"resolution": "resolved", // or unlinked / unobserved / unpublished
"resolved": { // the exact build that runs
"upstreamVersion": "v2.196.1",
"revision": 0,
"upstreamImage": "supabase/gotrue:v2.196.1",
"image": "ghcr.io/supabase/cli/gotrue:v2.196.1-r0@sha256:…",
"natives": { "darwin-arm64": { "archive": "…", "manifest": "…" }, "…": {} }
}
},
"database": { "majorVersion": 17, "line": "stock", "…": "…" }
// …one entry per service
}
}
```
---
## What `supabase start` does with the lock
```mermaid
flowchart TD
S["supabase start"] --> FRESH{"lock exists, and every entry<br/>still matches config.toml?"}
FRESH -->|yes| RUN["run the locked builds<br/>(no network, no Management API)"]
FRESH -->|"no (missing or stale)"| FROZEN{"frozen?<br/>--frozen, or CI with a lock"}
FROZEN -->|yes| FAIL["fail: lock missing or stale<br/>→ run supabase start locally and commit it"]
FROZEN -->|no| LEASE["take the project lease<br/>(serializes concurrent starts)"]
LEASE --> RESOLVE["re-resolve only the services<br/>whose specifier changed"]
RESOLVE --> WRITE["write supabase.lock atomically"]
WRITE --> RUN
```
A start never calls the Management API and needs no login. It only warns
when:
- `.link.json` targets another project or branch than the lock observed
(run `supabase stack update`);
- this CLI bundles a newer revision of a locked version.
---
## Typical workflows
### 1. New project, never linked
```console
$ supabase start
Resolved service versions:
database 17.11.0.004-r0 (specifier linked; unlinked)
auth v2.197.0-r0 (specifier linked; unlinked)
realtime v2.144.2-r0 (specifier linked; unlinked)
…
Created supabase/supabase.lock.
```
Nothing is linked yet, so `linked` falls back to the bundled builds and
says so. Commit `supabase/supabase.lock`, and every teammate and CI job
now runs exactly these builds, whatever CLI version they have.
### 2. Match production: `supabase link`
```console
$ supabase link --project-ref abcdefghijklmnopqrst
Hosted versions:
service hosted locked resolution
database 17.6.1.011 17.6.1.011-r0 resolved
auth v2.196.1 v2.196.1-r0 resolved
realtime v2.143.0 v2.143.0-r2 resolved
studio - 2026.10.12-… unobserved
…
Updated supabase/supabase.lock: now observing abcdefghijklmnopqrst (5 version changes). Review and commit it.
Finished supabase link.
```
`link` probes the hosted project, or the branch, and records what it
runs. Every `linked` service moves to that release. `link` writes
`.link.json` and the lock together, under the same project lease as
`start`. The next `supabase start` runs the hosted versions.
Linking a preview branch works the same way. The lock records which
project and branch it observed, so `start` warns if the checkout's link
later points elsewhere.
### 3. Try one specific version, or a hotfix revision
```toml
[local.services.auth]
version = "v2.198.0"
```
```console
$ supabase start
Resolved service versions:
auth v2.196.1-r0 → v2.198.0-r0 (specifier v2.198.0 ≠ locked linked)
Updated supabase/supabase.lock.
```
Only the changed entry is re-resolved, and every other pin stays where
it was. Remove the override to go back to `linked`. Pinning
`v2.198.0-r1` selects exactly that revision.
### 4. A teammate clones the repo, and CI
```console
$ git clone … && cd my-app
$ supabase start # the lock is fresh: runs the committed builds, no resolution
```
In CI (`CI` set and a lock present), `start`, `stack install` and `link`
are frozen: a stale or missing lock fails instead of being rewritten.
```console
$ CI=1 supabase start
supabase/supabase.lock is stale for: auth (specifier v2.198.0 ≠ locked linked); run supabase start locally and commit the updated lock
```
`supabase stack install --frozen --runtime native` downloads everything
the lock pins without creating a stack, which makes it handy as a cached
CI warm-up step.
### 5. Upgrading the CLI
Upgrading never changes locked versions. If the new CLI bundles a newer
revision of a version you locked, `start` mentions it:
```console
$ supabase start
This CLI bundles newer revisions of locked versions: realtime v2.144.2-r0 → v2.144.2-r1. Run supabase stack outdated to review, supabase stack update to adopt them.
```
```console
$ supabase stack outdated
SERVICE | SPECIFIER | HOSTED | LOCKED | UPDATE | BUNDLED | STATUS
----------|-----------|----------|-------------|-------------|-------------|------------------
auth | linked | v2.196.1 | v2.196.1-r0 | - | v2.197.0-r0 | up to date
realtime | bundled | - | v2.144.2-r0 | v2.144.2-r1 | v2.144.2-r1 | update available
…
Run supabase stack update to adopt the updates.
$ supabase stack update realtime # or no argument for every service
```
`stack outdated` is read-only. `stack update` re-resolves the named
services even when the lock is fresh:
- an upstream version moves to its newest revision;
- `bundled` moves to the version this CLI bundles;
- `linked` probes the hosted project again;
- an exact `-rN` pin stays.
### 6. Unlinking
`supabase unlink` removes `.link.json` and the stored credential under
the project lease. The lock is not touched: it stays a valid, reviewed
lock, and its `linked` entry still shows where the observations came
from.
---
## Your local data is protected when versions change
Changing a version must never silently corrupt a local database.
| Change | What happens |
| --- | --- |
| A new revision of the same database build | Accepted |
| Any other database build (another upstream version or Postgres major,
or switching between stock and OrioleDB) with initialized data |
Refused. The advice offers pinning the recorded build under
`[local.services.database]` to keep the data (setting `[db]
major_version` back or removing `[db] orioledb_version` where they would
conflict with that pin), or `supabase stack destroy --stack-id …` (this
deletes the local data) |
| The same change on a database that never initialized | Adopted |
| Moving auth, storage or realtime **back** to a version older than one
that already migrated this database | Refused: a downgrade doesn't
restore the old schema |
`link` and `stack update` still move `linked` pins, but they warn when a
saved stack's data would refuse the new pins.
The database records the build that initialized it. Each schema-owning
service records the newest version that migrated it. Snapshots, restores
and `db reset` carry and respect both records.
---
## Stack mode no longer uses `supabase/.temp`
| Before (`supabase/.temp`) | Now in stack mode |
| --- | --- |
| Project ref, pooler URL, branch | `supabase/.link.json` (gitignored) |
| Postgres and edge-runtime versions | `supabase/supabase.lock` |
| Storage uploads | `$SUPABASE_HOME/stacks/<id>/uploads`, removed by
`stack destroy` |
| Scratch files (pgdelta debug bundles, Functions bundling) |
`$SUPABASE_HOME/tmp` |
| Release-check cache | `$SUPABASE_HOME/cli-latest` |
Delegated Go commands skip `.temp` too. Remote commands resolve the
hosted target once per command, in this order:
1. the `--project-ref` flag;
2. `SUPABASE_PROJECT_ID`;
3. the link;
4. the interactive picker.
Three commands change with this beyond the `.temp` move:
- `gen keys` resolves its project this way, picker included, and passes
it to the delegated command;
- the upgrade notice picks its release-check cache by the project's
mode, read from the working directory it was handed;
- `db pull` builds its shadow database from the locked pins, falling
back to the bundled database pin of the major a `[remotes]` block
selects.
`supabase stack install` replaces the experimental `supabase stack
prepare`.
---
## Reading this PR
There is one commit per chunk, in dependency order; each builds on the
previous one.
| Commit | What it adds |
| --- | --- |
| `docs(stack)` | ADRs 0026 and 0017: the lock-based version model |
| `feat(config)` | `[local.services]` specifiers in the config schema |
| `feat(stack)` resolve pins | Resolving slim-services releases with
checksum, manifest and provenance verification |
| `refactor(cli)` `.temp` layout | One owner for every legacy `.temp`
path |
| `feat(stack)` explicit pin | Every service creation carries the exact
pin it runs |
| `feat(cli)` `.link.json` | The hosted link file in stack mode |
| `feat(stack)` lock format | The `supabase.lock` schema, deterministic
write, typed read errors |
| `feat(stack)` test stacks | `@supabase/stack` test stacks run the
project lock |
| `feat(stack)` database build guard | Initialized data is tied to its
build |
| `feat(cli)` hosted target | One hosted-target resolution per command |
| `feat(stack)` schema writers | Refuses older schema writers on
migrated databases |
| `feat(cli)` lock at start | Resolve and lock at `start`; `--frozen`
and CI behavior |
| `feat(cli)` install | `stack install`, `stack prepare` removed |
| `feat(cli)` update/outdated | `stack update` and `stack outdated` |
| `feat(cli)` linked versions | `link` observes hosted versions and
resolves `linked` |
| `feat(cli)` no `.temp` | Stack mode leaves `supabase/.temp` alone |
| `feat(cli)` Go off `.temp` | Delegated Go commands do the same |
| `test(cli)` | Explicit timeouts on owner-spawning destroy tests |
Review fixes and merges from `develop` follow these commits.
User-facing reference:
[`apps/cli/docs/stack-commands.md`](https://github.com/supabase/cli/blob/juliengoux/cli-2688-stack-mode-lockable-service-versions-with-hosted-parity-via/apps/cli/docs/stack-commands.md#service-versions)
and the [`start` side
effects](https://github.com/supabase/cli/blob/juliengoux/cli-2688-stack-mode-lockable-service-versions-with-hosted-parity-via/apps/cli/src/commands/experimental/stack/start/SIDE_EFFECTS.md#service-versions-and-the-lock).
---------
Co-authored-by: Colum Ferry <cferry09@gmail.com>
## Summary
The `link` and `bootstrap` integration tests picked out Management API
requests with `url.startsWith("https://api.supabase.com")`, which CodeQL
reports as incomplete URL substring sanitization (code scanning alerts
#59 and #60). They now parse each request URL and compare its origin.
The alerts also fail the CodeQL check on unrelated PRs whenever a diff
shifts those lines.
## Summary v3 (CLI-2695, landing on `next`) makes the local stack the default target for every command that can run against either the local stack or a hosted project, with one `--remote` flag to reach a project. Today nothing tells you that's coming. This adds a warning on develop, without changing any default: ``` Warning: supabase db push will target the local database by default in the next major version. Pass --linked to keep targeting the linked project. ``` - Printed on stderr, once, when one of these commands runs with no target selector and goes to the linked project: `db push`, `db pull`, `db dump`, `migration fetch`, `migration list`, `migration repair`, `inspect db *` (including the deprecated aliases, named as typed), `inspect report`, `gen types`, `storage ls/cp/mv/rm`. Storage says "the local Storage API". - Any of `--linked`, `--local`, `--db-url`, `--project-ref` (or `gen types --project-id`) silences it, in any form including `--linked=false`. `SUPABASE_PROJECT_ID` doesn't, since it won't select the project in v3. - `gen types` warns only once a project ref has resolved, so an unlinked run still prints just the existing "Must specify one of" error. - stdout, exit codes and the target itself are unchanged in every output format. The opt-in `target` config key and `SUPABASE_TARGET` this PR previously added are gone; the v3 design drops them in favour of `--remote`. ## Linked issue Part of CLI-2695 (the non-breaking half for the current major). ## Checklist - [x] The PR title follows Conventional Commits. - [x] Tests added or updated for the change. - [x] From the repository root, `pnpm check:all` passes. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…nts (#7211) ## TL;DR Two `stack start` failure fingerprints each covered many unrelated causes, so telemetry could not say what failed. Each start configuration check now has its own fingerprint, and an unclassified stack failure names the stack operation that failed. ## What changes - `tag:ExperimentalStackStartError:invalid_config` splits by check: `project_config`, `service_config`, `functions_env`, `saved_stack_changes`, `credentials_conflict`, `missing_database`, `missing_credentials`. - Failing to create the Storage uploads, Functions, or Studio snippets directory is classified by its file-system cause (`filesystem` permission, else `filesystem_io`) instead of as a user config error. - A stack failure with no known kind or reason reports `unclassified_<operation>` for the operations `stack start` runs (for example `unclassified_start_composition`, `unclassified_start_service`, `unclassified_ready_service`), covering both the client and the stack owner side. Other operations keep `unclassified`. This applies to every stack command wrapper and `functions serve`. Error messages and output are unchanged. A wrapper that keeps its own fallback for an unclassified stack failure still does. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Stacked on #7211. ## TL;DR Every experimental stack command reported every target-resolution failure under one `invalid_config` fingerprint. That covers reading saved stack state, decoding a saved stack, and resolving the project root, so telemetry could not tell these causes apart. Each failed step now has its own fingerprint in every stack command. ## Before ```mermaid flowchart LR A["stack discovery fails"] --> X["<command>:invalid_config"] B["stack lookup fails"] --> X C["prefix match unreadable"] --> X D["project root unresolvable"] --> X ``` ## After ```mermaid flowchart LR A["stack discovery fails"] --> A2["<command>:stack_discovery"] B["stack lookup fails"] --> B2["<command>:stack_lookup"] C["prefix match unreadable"] --> C2["<command>:unreadable_stack"] D["project root unresolvable"] --> D2["<command>:project_root"] ``` ## Why Review on #7211 pointed out that the only remaining `invalid_config` path in `stack start` is target resolution. That path is shared by all stack commands, so it was left for a follow-up. ## What changed - `StackTargetError` carries a closed `targetIssue` that the resolver sets where each failure occurs. - `mapTargetError` carries it into the start, stop, restart, status, destroy, prepare, and logs errors. They report `stack_discovery`, `stack_lookup`, `unreadable_stack`, or `project_root` instead of `invalid_config`. In `stack start`, a configuration check's own fingerprint still takes priority. - prepare, restart, logs, and stop now use the shared `mapTargetError` instead of local copies that would drop the new field. - An invalid-config error without a `targetIssue` still reports `invalid_config`. Error tags, messages, and output are unchanged. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
## TL;DR `supabase db push --linked` stops silently dropping `PGSSLMODE` and `PGSSLROOTCERT` when the direct host answers ## whats introduced? `--linked` commands that connect from the CLI itself (`db push`, `migration` commands, `inspect db`, … now read `PGSSLMODE`, `PGSSLROOTCERT` and `PGSSLCERT`/`PGSSLKEY` from the shell on the direct host, the way the pooler fallback already did. ps: the `pg_dump` steps in `db dump`, `db pull` and `migration squash` still don't forward them. `PGSERVICE` and `PGCONNECT_TIMEOUT` apply too, and a missing `PGSSLROOTCERT` file now fails instead of being ignored. host, role and database stay pinned. an invalid mode fails naming the vars on either route, and `sslmode=disable` now dials plaintext without reading cert files. <details> <summary>visual rep</summary> | `supabase db push --linked` | before | now | | ------------------------------------ | -------------------- | ---------------------------- | | direct host | `PGSSL*` ignored | read from the shell | | pooler fallback | read from the shell | unchanged | | invalid `PGSSLMODE` | ignored / IPv6 error | fails naming the vars | </details> ## why this? `--db-url` and the pooler fallback already read these vars. the linked direct host was skipped by mistake during the port, so it now reads them too ## ref: - closes: CLI-2753 - broken/missed in: #5715
This bumps edge-runtime from v1.77.6-r0 to v1.77.8-r0. https://github.com/supabase/slim-services/releases/tag/edge-runtime-v1.77.8-r0 Planned after supabase/slim-services published v1.77.8-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…7262) ## TL;DR brings `io.ts` and its tests under the effect lint ## whats introduced? - `packages/config/src/io.*` joins the allow list - `writeFileAtomic` reads the temp suffix time from the effect `Clock`, the same value `Date.now()` gave - `io.unit.test.ts` moves to effect: `FileSystem` and `Path`, scoped temp dirs, schema json codecs, and `vi.stubEnv` inside a scoped acquire and release instead of writing `process.env` ## ref - related: CLI-2756
…I-2756) (#7263) ## TL;DR brings the rest of `packages/config/src` under the effect lint ## whats introduced? - `packages/config/src/**` joins the allow list, and the `schema-number` modules stay out until the schema fix on top - `project.ts` yields its parse error directly instead of through `Effect.fail` - the `./io` methods are plain promise returning functions behind `Effect.suspend` instead of `async`, so they resolve and reject exactly as before and never throw synchronously, only `isAsyncFunction` flips - the src tests move to effect, and the entrypoint purity and import contract walkers run on `FileSystem`, skipping entries that vanish mid scan ## ref - related: CLI-2756
#7265) ## TL;DR brings the `packages/config` build and release scripts under the effect lint ## whats introduced? - the allow list widens to `packages/config/**` - `build.ts`, `release-plan.ts` and the semantic release path filter run as effect programs with the same output, exit codes, release outputs and temp dir cleanup - a malformed `package.json` now reports a schema decode message instead of the native json parse one, same exit code ## ref - closes: CLI-2757
#7267) ## TL;DR a broken config now says what is wrong and where, and `inf` or `nan` numbers no longer load ## whats hurting the user? `supabase start`, `status`, `stop` and `functions serve` print a bare `CliConfigParseError` for any broken config. `config push`, `gen types`, `seed buckets` and the rest print the raw parser output, which quotes nearby lines of the file, secrets included. `max_rows = inf` or a `nan` rate limit loads quietly and reaches the containers as `Infinity` ## now fixed by? every command prints the field path or the line and column, never the file contents. number fields only take finite values, so the json schema post processing that hid the `inf` and `nan` variants goes away. > [!NOTE] > a config with `inf` or `nan` in a number field used to load and now fails, so set a real number instead. ## ref: - closes: CLI-2756 - closes: CLI-2755
Adds a PERMISSIONS section to `--help` for every command, listing the
personal access token permissions it needs.
Every command already declares the Management API operations it may
call. The help section resolves those declarations against the
permissions the API spec lists for each operation, so users can see
which token to create before running a command.
- Permissions come from a generated `operationId` to `x-fga-permissions`
/ `x-oauth-scope` table in `@supabase/api/permissions`.
- Conditions are worded against the command's own flags: a flag the
command lacks is never named, equivalent cases merge, and a permission
already shown under a broader condition is not repeated. Requirements
that depend on something other than a flag show as "May be required",
without a reason.
- `--output-format json` help carries the same section under a top-level
`permissions` field, with each group's flag conditions as data. The raw
permission declaration is no longer serialized into `doc.annotations`.
- Each permission scheme is resolved and rendered on its own, so OAuth
scopes can be added later by registering one more scheme.
Output sample:
```
$ supabase projects api-keys --help
DESCRIPTION
List all API keys for a Supabase project.
... rest of the content ...
PERMISSIONS
Personal access token (FGA):
Required with --reveal:
api_gateway_keys_read + api_gateway_keys_secret_read
Required without --reveal:
api_gateway_keys_read
May be required (without --project-ref):
projects_read
Optional (command still runs without these):
project_admin_read
```
```
$ supabase projects api-keys --help --output-format json
...
"permissions": {
"status": "mapped",
"schemes": [
{
"scheme": "fga",
"title": "Personal access token (FGA)",
"groups": [
{
"heading": "Required with --reveal",
"need": "required",
"when": [
{
"all": [
{
"flag": "reveal",
"values": [
"true"
]
}
]
}
],
"rows": [
{
"anyOf": [
[
"api_gateway_keys_read",
"api_gateway_keys_secret_read"
]
]
}
]
},
{
"heading": "Required without --reveal",
"need": "required",
"when": [
{
"all": [
{
"flag": "reveal",
"values": [
"false"
]
}
]
}
],
"rows": [
{
"anyOf": [
[
"api_gateway_keys_read"
]
]
}
]
},
{
"heading": "May be required (without --project-ref)",
"need": "maybe",
"when": [
{
"all": [
{
"flag": "project-ref",
"values": [
"unset",
"empty"
]
}
]
}
],
"rows": [
{
"anyOf": [
[
"projects_read"
]
]
}
]
},
{
"heading": "Optional (command still runs without these)",
"need": "optional",
"rows": [
{
"anyOf": [
[
"project_admin_read"
]
]
}
]
}
]
}
]
}
...
```
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
This branch was successfully 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.
/sharedunder the effect skill (chore(cli): bring/sharedunder the effect skill #7164)upgrade,flagsandfilesystemmodules with effect lint (CLI-2648) (refactor(cli): cover theupgrade,flagsandfilesystemmodules with effect lint (CLI-2648) #7144)upgrade-noticeeffect-native and lint (CLI-2648) (feat(cli): makeupgrade-noticeeffect-native and lint (CLI-2648) #7150)db-bootstrap,stackandtest-dbmodules with effect lint (CLI-2655) (refactor(cli): cover thedb-bootstrap,stackandtest-dbmodules with effect lint (CLI-2655) #7167)go-jwtandconfig-pull-runwith effect lint (CLI-2651) (refactor(cli): covergo-jwtandconfig-pull-runwith effect lint (CLI-2651) #7174)telemetry-stateandlinked-project-cachewith effect lint (CLI-2741) (refactor(cli): covertelemetry-stateandlinked-project-cachewith effect lint (CLI-2741) #7206)telemetryarea with effect lint (CLI-2741) (refactor(cli): cover the rest of thetelemetryarea with effect lint (CLI-2741) #7207)authandconfigareas with effect lint (CLI-2740) (refactor(cli): cover theauthandconfigareas with effect lint (CLI-2740) #7212)src/cliarea with effect lint (CLI-2742) (refactor(cli): cover thesrc/cliarea with effect lint (CLI-2742) #7217)docsandoutputareas with effect lint (CLI-2742) (refactor(cli): cover thedocsandoutputareas with effect lint (CLI-2742) #7218)db_pre_requesthooks blocking rest readiness (CLI-2749) (fix(stack): stopdb_pre_requesthooks blocking rest readiness (CLI-2749) #7216)