Repository navigation
chore: production deploy - #7104
Conversation
…7017) ## TL;DR reaps the containers a stack leaves behind once its directory is deleted ## whats introduced? the orphan sweep now removes containers labelled with its state root whose stack is no longer registered `stack destroy --stack-id <full id>` removes them on demand, also for a deleted `SUPABASE_HOME` when run with that home the shared database volume is left as is ## ref - closes: #6963
## TL;DR fixes the live e2e ssl reboot flake and stops develop pushes from cancelling a running live suite - `ssl-enforcement update` reboots the database and `get` can report it applied before the reboot ends, so the reboot landed in the next file and killed `migration up` with `57P01`. the test now waits for a new postmaster after the toggle and the restore, and `queryLiveDb` no longer crashes when a restart drops an idle client - merge queue bursts land develop pushes closer together than one live run, so each push cancelled the running suite and turned its commit red. pushes now share a lane that never cancels a running suite ## ref - spotted in: [migration up](https://github.com/supabase/cli/actions/runs/37284888772/job/111681321268) [cancelled run](https://github.com/supabase/cli/actions/runs/37296027587)
This bumps realtime from v2.140.10-r0 to v2.140.11-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.140.11-r0 Planned after supabase/slim-services published v2.140.11-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 storage from v1.79.36-r0 to v1.79.37-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.79.37-r0 Planned after supabase/slim-services published v1.79.37-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.140.11-r0 to v2.141.0-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.141.0-r0 Planned after supabase/slim-services published v2.141.0-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps realtime from v2.141.0-r0 to v2.142.0-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.142.0-r0 Planned after supabase/slim-services published v2.142.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>
…) (#7028) ## TL;DR fixes local `functions serve --env-file` and `--no-verify-jwt` failing after a normal stack `supabase start` ## what's hurting? studio gets its Functions URL through a binding that also makes it a lifecycle dependent of Functions that means Studio blocks the restart needed to apply a temporary override, even when Studio is idle. the same guard blocks restoring the saved configuration on exit.. ## now fixed by: keeping Studio’s Functions URL binding without making Functions a startup prerequisite for studio functions can use the existing restart and cleanup paths to apply the override and restore its saved configuration studio stays in its current state ## ref: - closes: #7009
This bumps storage from v1.79.37-r0 to v1.79.38-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.79.38-r0 Planned after supabase/slim-services published v1.79.38-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>
…) (#7027) ## TL;DR fixes local `functions serve` failing before startup in coding-agent environments when no output format is passed ## prob/sol agent detection defaults the CLI to `json`, but stack `functions serve` only accepts `text` or `stream-json`. so the command fails with `Functions serve requires text or stream-json output` unless the caller passes an output flag now fixed by: defaulting stack `functions serve` to `stream-json` for agents when no output format is chosen explicit output choices still win... ## ref: - closes: #7010
…#7029) ## TL;DR fixes migrations with database webhook triggers failing on the stack backend with `schema "supabase_functions" does not exist` ## whats broken? the regular docker `supabase start` creates `supabase_functions` in its postgres entrypoint, which neither stack runtime runs, so webhook triggers failed on both `--runtime native` and `--runtime docker` ## now fixed by: the stack catalog creates the schema, its tables, `http_request()` and `supabase_functions_admin` before migrations, only when missing and under an advisory lock, and leaves the image's own `pg_net` policy alone existing stack databases get it on their next stop and start, and the shadow cache key moves so `db diff` rebuilds its baseline. > [!NOTE] > a migration that recreates `supabase_functions`, like the workaround in the issue now fails on the stack as it does on the regular docker start, so delete that migration file ## ref: - closes: #7007
## TL;DR fixes https requests from `http` and `pg_net` failing certificate verification on the native runtime on macOS arm64. ## whats broken? the OpenSSL bundled with native postgres looks for its CA bundle under `/nix`, which a macOS host does not have, so `http_get` to an https url failed with `unable to get local issuer certificate`. the native launcher also kept a host `SSL_CERT_FILE` away from postgres, so exporting one did not help. ## now fixed by: the native postgres launch now forwards an exported `SSL_CERT_FILE` and `SSL_CERT_DIR`, and when `SSL_CERT_FILE` is not exported it uses the first CA bundle on the host, `/etc/ssl/cert.pem` on macOS. ## ref: - closes: #7008
## Summary
The `fast-forward` job in `release.yml` fast-forwards `main` to
`develop` whenever an approving review is submitted on the develop →
main deploy PR, but it never checks who approved. On a public repo any
GitHub user can leave an approving review, so a drive-by approval on the
weekly "chore: production deploy" PR would fast-forward `main` and cut a
stable release.
The job now looks up the reviewer's repo permission first (`GET
/repos/{owner}/{repo}/collaborators/{username}/permission`, checking
both `permission` and `role_name`) and only mints the App token, checks
out and fast-forwards when it's `admin`, `maintain` or `write`. Anyone
else gets a `::notice::` and a green skip rather than a red run.
Maintainer approvals behave exactly as before.
While there, the head SHA moved out of the inline `run:` script into
`env:`.
Split out of CLI-2630 so it doesn't wait on the larger branching/release
PR, which also generalises this job.
## Linked issue
Linear: CLI-2630
- [x] The linked issue is **open** and carries the
`open-for-contribution` label (or I'm a Supabase maintainer).
## Checklist
- [x] The PR title follows [Conventional
Commits](https://www.conventionalcommits.org/) (e.g. `fix(cli): …`).
- [ ] Tests added or updated for the change. (Workflow-only change;
validated with `actionlint`. The permission lookup gets its first live
run on the next deploy PR approval, and fails closed if the lookup
errors.)
- [x] From the repository root, `pnpm check:all` passes; relevant
package tests pass for every touched workspace, and `pnpm types:check`
passes for each touched TypeScript workspace (or workspace declaring
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.79.38-r0 to v1.80.0-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.80.0-r0 Planned after supabase/slim-services published v1.80.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>
The AI Review "Post review" job fails on large PRs because it fetches the PR diff from the pulls API, which returns 406 once a diff exceeds 20,000 lines or 300 files. See the failure on #7036: https://github.com/supabase/cli/actions/runs/37609410161/job/112759286813 The review jobs already build the diff locally with `generate-pr-diff.ts`, which has no size cap. The `adjudicate` job now uploads that `pr.diff` as an artifact, and `post-review` anchors inline comments against it instead of calling the API. Inline anchors now match the exact diff the reviewers saw; if the PR head moves during the run, the existing 422 fallback still folds unanchorable comments into the review body.
## Summary **TL;DR:** Two `LogForwarder` integration tests occasionally hit the 30 s timeout in CI because each assumed an ordering the forwarder doesn't guarantee. Both tests now cover every ordering without sleeps. The forwarder itself is unchanged. **Why:** CI runs on #6942 timed out in: - `pauses without advancing when Analytics refuses its credentials` - `ships a record persisted while Analytics slept once it wakes on a new port, …` **What changed** - **Credentials refusal:** the test armed its cursor-write gate only after `logflare.next` returned. When the forwarder handled the 401 and removed its cursor first, the gate never fired, and the paused forwarder never slept `pollMillis`, so both race branches hung. The test now holds the 401 answer with `logflare.hold` until the gate is armed. Delaying the test fiber after `logflare.next` reproduces the hang reliably on the old test. - **Retarget to a new port:** when the held post is released, the retired launch-1 session can reach the asleep record before the race interrupts it. It then saves that record as pending to launch 1 and fails `requireCurrent` without posting. The new session waits until the saved post deadline plus the flush window before posting, but the test expected the post without advancing the manual clock. The test now takes that post with `nextPost`, which steps through stored-id polls until a post arrives, so both orderings pass. Delaying the retarget side of the race reproduces the hang reliably on the old test. In production this ordering only delays shipping until that deadline plus the flush window (about 10 s), so the forwarder is left as is. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…7039) ## TL;DR fixes every pg_cron job run failing with `connection failed` on the native runtime ## whats broken? native postgres listens only on its socket, but pg_cron by default opens a libpq connection to `localhost:5432` for each job. nothing listens there, so every run failed with `connection failed`, or `job startup timeout` on macOS. ## now fixed by: native postgres now runs pg_cron jobs in background workers and defaults `max_worker_processes` to 17 below your own config, `ALTER SYSTEM` and `[db.settings]`. supautils stays preloaded so jobs keep their docker role policies shadow databases keep pg_cron's default mode ## ref: - closes: #6977
## Summary **TL;DR:** Database helpers now stay attached to the owning process. If that process exits or is killed, the Docker client's stdin closes, the helper exits, and Docker removes the container. A normal `docker stop` also ends the helper. ### Before Both shared-volume and per-instance helpers used detached `docker run -d` containers with a shell that ignored TERM. They could remain after the owner disappeared, and `docker stop` could not shut them down. ### After Helpers start with `docker run --rm -i` and wait for a readiness signal before commands use them. Their shell exits on stdin EOF or TERM. Existing named helpers are replaced instead of being adopted without an owner connection. The shared database volume is left intact when a helper exits. ### What changed - Give each helper a lifetime scope so its attached Docker client remains open for as long as the helper is in use, including cleanup during owner shutdown. - Preserve startup cleanup when helper creation is interrupted, and report Docker's startup error when available. - Cover attached startup, helper scope behavior, and owner SIGKILL cleanup in the storage and registry tests. ## Linked issue Closes #6962 --------- Co-authored-by: avallete <andrew.valleteau@supabase.io> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Andrew Valleteau <avallete@users.noreply.github.com>
…ns (CLI-2630) (#7038) ## Summary Sets up the branching and release machinery from [CLI-2630](https://linear.app/supabase/issue/CLI-2630/gh-branch-and-release-process) so we can start merging breaking-change work for v3. The design (and the review rounds behind it) lives in the Linear issue; the canonical runbook is now in `apps/cli/docs/release-process.md`, with ADR 0028 superseding ADR 0011's dist-tag section. ### What changes **New `next` channel** - `next` is a long-lived branch for breaking work. Breaking PRs target it with a `type(scope)!:` title. - semantic-release treats it as a `next` prerelease branch, so it publishes `X.Y.Z-next.N`. It goes out to npm `next` and as a GitHub prerelease, with no brew/scoop. - In `release.yml`, a push to `next` runs `test.yml` first, and only plans and releases once that passes. - `-next.N` builds identify as "Supabase CLI (next channel)." **Syncing (`sync-branches.yml`, replaces `sync-main-to-develop.yml`)** - Two pairs only: main → develop after a stable release, and develop → next on every develop push. - A clean merge is pushed directly. - On a conflict it opens a `sync/<source>-into-<target>` PR at the source tip and pauses that pair until the PR closes. Someone resolves it by merging the target in. **Approve-to-fast-forward (`fast-forward.yml`, moved out of `release.yml`)** - Covers the deploy PR (develop → main), the major cut (next → develop), and the two bot-authored sync PRs. - Before pushing it checks: - the approver has write access - the approved commit is still the PR head - the 4 required checks are green, read from this PR's own check rollup so another PR's runs on the same commit don't count - `--ff-only` (sync PRs re-sync and ask for re-approval if the target moved) - Anything that lands on `main` also refuses a major bump without the `release-major` label. - `test.yml` now runs on PRs into `main`, so the deploy PR head carries the required checks. Hotfix PRs into `main` finally get CI too. **Maintenance lines (`v<N>.x`)** - Released by running `release.yml` on the branch itself (`gh workflow run release.yml --ref v2.x -f channel=maintenance`). Its own frozen workflow, scripts and live e2e tests are used. - A wildcard semantic-release entry (`v+([0-9]).x`) maps `v2.x` to range `2.x`, channel `v2.stable`. - Publishes to npm `v<N>.stable`, Homebrew `supabase@<N>` (with `conflicts_with "supabase"`) and Scoop `supabase-v<N>-stable`. - Releases are published with `--latest=false`. - The live e2e gate takes a branch input. **Guards** - Lint Pull Request hard-fails a breaking `!` title unless the base is `next`. It runs the guard from the default branch, and the cut exemption only applies to same-repo branches. - `branch-policy.yml` adds two checks: - `Require fast-forward`, which keeps the merge button off sync and cut PRs - `Check maintenance source`, which only allows same-repo `hotfix/*` or `backport/*` into `v*.x` - `bot-pr-auto-merge.yml` skips the two sync PRs. - Manual `release.yml` runs: - refuse `stable`/`beta` from `next` or `v*.x` - only run a non-dry-run `stable` from `main` or `hotfix/*` - validate the version string **Also** - The AI review adds a non-blocking "Next impact (advisory)" section on PRs into develop that conflict with work on `next`. - Release scripts accept the `next` and `v<N>.stable` tags. - Backfill/apply release notes understand `-next` and maintenance tags. - Nightly live e2e also covers `main` and `next`. - Root `AGENTS.md`, `MAINTAINERS.md` and `CONTRIBUTING.md` cover branch targeting and the release-infra cherry-pick rule. ### Deliberately not here - No `next` dist-tag move on stable releases, because publishing stays OIDC-only (`npm dist-tag` would need a long-lived token). - Brew/scoop `supabase-next`, a Linear `next` pipeline and an `@supabase/config` next channel are deferred until someone asks. - One writer may resolve *and* approve a sync conflict PR. That's an accepted decision, documented in the runbook. ### After merge (manual, in this order) 1. Create `next` from the `develop` tip. 2. Ruleset "Default (next)": copy of develop's (squash merge queue, the 4 required checks, code-owner review), with the release App as bypass actor. 3. Add `Require fast-forward` as a required check on `develop` and `next`. 4. Ruleset for `refs/heads/v*.x` with `Check maintenance source` required. 5. Create the `release-major` label. 6. Confirm the App is a bypass actor on `develop`, and that npm trusted publishing isn't restricted by branch. Details are in `.github/MAINTAINERS.md` → "Branches and releases". ### Heads-up - #7035 (merged) added an approver check to the old fast-forward job in `release.yml`. This PR removes that job, and `fast-forward.yml` carries the same check (`.permission` exactly `admin | write`); the merge conflict is already resolved on this branch. - `lint-pull-request.yml` is `pull_request_target`, so the new title guard only starts running once this is on `develop`. - The fast-forward and sync paths can't be fully exercised before merge. The first deploy PR approval and first develop push after `next` exists are the real tests, so it's worth watching those runs. ## Linked issue Linear: [CLI-2630](https://linear.app/supabase/issue/CLI-2630/gh-branch-and-release-process) - [x] The linked issue is **open** and carries the `open-for-contribution` label (or I'm a Supabase maintainer). ## Checklist - [x] The PR title follows [Conventional Commits](https://www.conventionalcommits.org/) (e.g. `fix(cli): …`). - [x] Tests added or updated for the change. (`.github/scripts`: 372 bun tests incl. real-git temp repos for sync/fast-forward; `apps/cli` unit tests for release channels, title analysis and the build channel.) - [x] From the repository root, `pnpm check:all` passes; relevant package tests pass for every touched workspace, and `pnpm types:check` passes for each touched TypeScript workspace (or workspace declaring it). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
## TL;DR Studio's Logs → API Gateway page was always empty on the experimental stack, because the stack's API proxy logged nothing per request. The proxy now writes one access line per request, persists it like service logs, and ships it to Analytics in the shape Studio reads, so the page lists every request with its status, method and path. `supabase stack logs` shows the same lines. Stacked on #6893. ## Before ```mermaid flowchart LR C[Client] --> P["Stack API proxy"] P --> S["REST / Auth / Storage / Functions / Realtime"] S --> L["Service logs"] L --> A[Analytics] P -. "no access log" .-> X["Studio API Gateway: empty"] ``` ## After ```mermaid flowchart LR C[Client] --> P["Stack API proxy"] P --> S["REST / Auth / Storage / Functions / Realtime"] P --> G["gateway log stream<br/>one line per request"] G --> F["logs/gateway files"] F --> CLI["supabase stack logs --service gateway"] F --> A["Analytics cloudflare.logs.prod"] A --> U["Studio API Gateway"] ``` ## Why The legacy `supabase start` fills the API Gateway page from Kong's access log (source `cloudflare.logs.prod`). The experimental stack has no Kong: its shared API port is served by the owner's own HTTP proxy, which had no access log, so the page showed "No data" however much traffic the stack served. ## What changed - The shared API proxy records one access line per request and per websocket upgrade, in nginx combined format plus the duration. The status is the one actually sent, including proxy-generated 404/502/503 and requests that wait for a lazy service to wake; a client that leaves before the response is recorded as 499, and body bytes appear only for responses that finished. Logging never delays a response. - `apikey`, `access_token` and `token` query values are replaced by `redacted` before the line is written; request headers such as `Authorization` are not logged. - The lines are kept as a `gateway` log stream under the stack's `logs/gateway/` directory with the same rotation and retention as service logs, survive owner restarts, and are removed with the stack. - While Analytics is running and healthy, gateway lines ship to `cloudflare.logs.prod` with the Kong request/response metadata Studio's API Gateway list, detail panel and status filter read (the path without its query, the query as `search`). - `supabase stack logs` includes gateway lines by default when the stack serves the shared API port, and `--service gateway` selects them; reading works while the stack is stopped. - Studio's per-function logs page stays empty: it filters on a function id that local Studio regenerates on every call, and the local edge runtime output does not carry the function name. The legacy start has the same gap. ## Terminal captures Recorded from this branch's source on macOS (Docker runtime): terminal with vhs, Studio in headless Chrome, at the same time against the same stack. Top: `stack logs -f --service gateway` (the `apikey` query value is redacted); bottom: requests to REST, Auth, Storage and Functions; right: Studio's API Gateway list filling in, then the error and warning status filter. Before this change the page showed "No data".  🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ls (#7041) ## TL;DR retries the network restrictions live update once when the platform stalls it, and makes the analytics poll say what stalled - `network-restrictions update` failed with `expected 1 to be +0` and no stderr. the platform's `apply` stalled until the harness killed the cli at the 60s exit guard. the update is now re-issued once when it was killed before printing its result, the same way the restore already retries, and a second stall fails naming the kill - the Logflare stored id poll in `Owner.analytics` ran out with a bare `NoSuchElementError` after Analytics left healthy mid test. it now fails with `NotStoredError` naming the first or sentinel event, its id and the poll count ## ref - spotted in: [network restrictions](https://github.com/supabase/cli/actions/runs/37436825182/job/112180576049) [analytics](https://github.com/supabase/cli/actions/runs/37451747630/job/112229699817)
#7036) ## Why `@supabase/stack` runs a local Supabase anywhere: in containers (Docker or Podman) or as native processes on macOS and Linux, with several stacks running side by side. This PR is an architecture and hardening pass over that runtime and the experimental `supabase stack` commands (still behind `SUPABASE_EXPERIMENTAL_STACK=1`). The aim is a design that is robust because it is simple: - one owner per fact; - derive state instead of recording it; - prevent conflicts by construction; - rely on guarantees the OS and the engine already give: kernel locks, atomic rename, pipe EOF and container labels. Several mechanisms were deleted rather than patched. The legacy `supabase start` path is untouched. ## What users stop hitting | Situation | Before | After | |---|---|---| | `supabase stack stop` | Listeners closed first, so Vector couldn't flush logs through the proxy and was force-killed after its grace period. Every stop paid that delay, plus an analytics drain and a 10 s hold for idle pooled connections. | Services stop in reverse dependency order with their listeners still open. Each listener closes when its own service has stopped. | | A service crashes | The proxy could keep routing to the dead process while cleanup ran, and a retried cleanup could lose the crash outcome. | The crash is reported first, so routing stops at once. The service ends `Failed` even across retried cleanup. It wakes again on demand, behind a crash-loop breaker. | | Restarting a stack | Public ports could change between `stop` and `start`, breaking URLs embedded in apps. | Automatic ports are reserved per OS user and kept across stop and restart and across state roots until destroy. Configured ports are held only while the stack runs, so a stopped project never blocks another. A conflict names the port, the holding project and the setting to change. | | Two native stacks on macOS | Wildcard binds let another stack's same-port loopback bind shadow a service, so requests hit the wrong stack ("Invalid JWT"). | Native services bind loopback only, and backend ports come from a dedicated range below the OS ephemeral range. | | Proxy retry after a backend restart | The retry reused the address captured before the first attempt and could reach a foreign process that took the port. | The address lives in the lifecycle's `Running` state and is re-resolved on retry. A dead workload has no routable address. | | Deleting a stack's state directory | The owner kept running and its containers leaked. | The owner notices that it lost ownership, stops only its own services without touching data or ports, and exits. Leftover labelled containers are swept by the next container stack under that state root, and `destroy --stack-id` reaches them on demand. | | `destroy` | Helper containers and files under the data root could survive, and open leases made destroy fail. | Everything labelled with the stack is removed, owned data is removed by location, and destroy succeeds despite open leases. | | Engine not running | Startup and destroy failed in opaque ways. | Startup and `destroy` fail with `runtime-unavailable`. `destroy` leaves data and ports untouched and prints the exact command to retry once the engine is back. | | Podman | Not supported. | Podman is a first-class engine. The CLI picks Docker, then Podman, then native. Rootless launches keep file ownership with `--userns=keep-id`. | | Changing stack credentials | A stopped consumer kept stale keys baked into its saved config. | Credential changes refresh the stopped consumers in the same locked save, keeping values set explicitly. | | Concurrent native preparers | Two processes preparing the same binaries could lose files. macOS temp-dir aging could empty a cached generation. | Artifacts are immutable, content-addressed generations published by rename into the user cache directory. | | Config change on a saved stack | Rejected with an opaque error. | The error names the changed settings and the exact recreate command. | ## How it works now ### One lifecycle owner per service Start, stop, sleep, restart, crash handling and destroy used to have separate writers. A single pure reducer now decides every transition under one gate. The runtime only executes the step it is given, and late outcomes are fenced by generation. ```mermaid stateDiagram-v2 [*] --> Stopped Stopped --> Starting: demand or explicit start Starting --> Running: session available Starting --> Failed: launch failed, cleanup confirmed Running --> Stopping: stop, restart, idle sleep or crash Stopping --> Stopped: stop / restart / sleep, cleanup confirmed Stopping --> Failed: crash, cleanup confirmed Stopping --> Stopping: cleanup failed, retried on request Failed --> Starting: demand, behind the breaker Failed --> Stopped: explicit stop ``` - **Routing.** An address exists only in `Running`, so traffic can't reach a stopping or crashed workload. - **Breaker.** It opens for 30 s after 3 failures, each within 5 minutes of the previous one. - **Wake waiters.** Requests that wake a sleeping service wait up to 120 s. - **Dependencies.** Prerequisites stay awake while a dependent runs. ### Ordered stop instead of a listener drain ```mermaid sequenceDiagram participant P as Stack proxy participant F as Functions participant A as Auth participant V as Vector participant DB as Postgres Note over P: Draining (one-way): wake-up traffic refused,<br/>traffic to running services still admitted P->>F: stop (dependents first) F-->>P: stopped, listener closes P->>A: stop A-->>P: stopped, listener closes V->>P: final log flush still reaches analytics P->>V: stop V-->>P: stopped, listener closes P->>DB: stop (prerequisites last) DB-->>P: stopped, listener closes ``` Draining is one-way. If stop or destroy fails, the stack stays registered and the next command reclaims it under the lease. There are no resume or recovery paths. ### Who owns what ```mermaid flowchart TB subgraph user["Per OS user"] ports[("~/.supabase/ports.sqlite<br/>public port reservations")] cache["~/.cache artifact generations<br/>immutable, content-addressed"] end subgraph root["State root (listing is per root)"] lock["<id>/owner.lock<br/>SQLite lease = stack is live"] state["<id>/state.json<br/>registration and definitions"] data["<id>/data/<instance><br/>owned by location"] end engine["Engine containers<br/>com.supabase.stack + com.supabase.stack-root labels"] owner(("Stack owner process")) owner -- holds --> lock owner -- polls --> state owner -- sweeps by label --> engine owner -- reserves --> ports owner -- removes on destroy --> data ``` - **Liveness.** A stack is live exactly while its lease is held, and the OS releases the lease when the process exits for any reason. - **Containers.** They are tracked only by two labels set atomically at creation. The claims journal is gone. - **Data.** Ownership is by location: everything under an instance or data root is owned. Borrowed caller paths may not resolve inside it. Destroy checks containment before removing. - **Ports.** Reservations live only in the per-user registry; `state.json` stores none. Automatic ports stay reserved until destroy; configured ports are held only while their listener is open, and a crashed holder's reservation is taken over. - **Engines.** Each container stack pins one engine target (Docker context or host, Podman connection or URL), and every engine call uses it. ### Removed or intentionally unsupported | Item | What changed | |---|---| | Resource-claims journal, listener-drain deadline, abandon mode and its retry loop, in-process artifact lock registry | Replaced by labels, ordered stop, an owner that exits when it loses ownership, and SQLite file locks. | | Stacks saved by earlier experimental releases | No migration. A saved stack that can't be read fails with an error naming its directory to remove. | | Filesystems without hard links | Unsupported; the non-atomic fallback was removed. | | Native mode on Windows | Out of scope; Windows runs the stack in containers. | | Container sweeps in native mode | Native owners skip them; native mode has no container concerns. | | Port reservations across OS users | Not provided; kernel binds still keep different users' running stacks apart. | | Proxy completion window during stop | None; in-flight requests survive as far as each service's own graceful stop carries them. | ## Size 240 files changed: +23,442 / −11,645 (**+11,797 net**). About two thirds of the growth is tests. | Area | Kind | Net lines | |---|---|---:| | `packages/stack` | Production code | +3,277 | | | Integration tests | +3,960 | | | Unit tests | +1,605 | | | Test helpers | +1,288 | | | E2E tests | +897 | | | Docs | +64 | | `apps/cli` | Production code | −90 | | | Tests | +123 | | | Docs | +44 | | Repo root | Namespace import-boundary lint rules | +380 | | | CI (Podman on rootless Linux, stress workflow) | +188 | | | ADRs | +58 | | **Total** | Production code | **+3,187** | | | Tests | **+7,873** | The production growth is in the stack runtime only. The CLI's production code is slightly smaller.
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>
) ## TL;DR lets `stack destroy` take `--stack-id` more than once, destroying every listed stack after a single confirmation ## whats introduced? every id resolves before the prompt, so a missing one destroys nothing and a prefix plus the full id of one stack count once. a stack that fails to destroy doesn't stop the rest, and the command still exits 1 with one error naming the failed ids and a retry command example: ``` $ supabase stack destroy --stack-id <a> --stack-id <b> --yes Permanently destroying stack <a> at /work/app and its owned data; stack <b> at /work/app and its owned data. Storage upload files will be preserved. Stack <a> destroyed. Stack <b> destroyed. ``` > [!NOTE] > a single id behaves exactly as before. with more than one `--stack-id` json returns `stacks` and a partly failed batch emits one error envelope carrying `destroyed_stacks` ## ref - closes: #6964 --------- Co-authored-by: 7ttp <117663341+7ttp@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>
**TL;DR**: develop's `supabase` type check fails since the Management API sync in #7054 because the hand-kept project-config mirror lacks four new `postgres_settings` keys. This adds them, and fixes the turbo cache key that let the sync PR (and develop's merge-queue run) replay a cached pass instead of type-checking. **Before** ```mermaid flowchart LR A["Sync PR changes packages/api"] --> B["supabase#types:check hash unchanged"] B --> C["Cache hit: replayed pass"] C --> D["Auto-merge; develop broken"] ``` **After** ```mermaid flowchart LR A["Sync PR changes packages/api"] --> B["@supabase/api#transit hash changes"] B --> C["supabase#types:check re-runs"] C --> D["Drift guard fails the sync PR"] ``` ## Why On develop at 2286e8c, an uncached `pnpm run types:check` in `apps/cli` fails: ``` src/shared/config/project-config-api-drift.unit.test.ts(45,3): error TS2344: Type '"autovacuum_max_workers" | "autovacuum_work_mem" | "pg_net_batch_size" | "pg_stat_statements_max"' does not satisfy the constraint 'never'. ``` Develop's CI showed green only because turbo replayed a cached `supabase:types:check` ("cache hit, replaying logs") on the #7054 merge-queue run. `types:check` had no `dependsOn`, so its hash covered only the package's own files: a change confined to `packages/api` (or `packages/config`) never invalidated a dependent's cached type check. Any PR that touches `apps/cli` misses the cache and fails "Check code quality" (e.g. #6934). Sync PRs are auto-merged, so the drift guard has to fail on the sync PR itself. ## What changed - `packages/config` mirror (`ProjectConfigApiAttributes`): add `autovacuum_max_workers`, `autovacuum_work_mem`, `pg_net_batch_size` and `pg_stat_statements_max` as unmapped `Schema.Unknown` fields. They follow their API-only siblings (`log_*`, `checkpoint_timeout`, `hot_standby_feedback`, `cron_log_statement`): no `[db.settings]` key, no Go CLI counterpart, still reachable through `_apiResponse`. Making them configurable in `config.toml` would be a separate published-schema change. - `turbo.json`: add a `transit` task (`dependsOn: ["^transit"]`) and make `types:check` (including `@supabase/docs#types:check`) depend on it. Each workspace dependency's files now feed the dependent's cache key without serialising the type checks. - `packages/api/scripts/dependent-types-check-cache.unit.test.ts`: asks turbo's dry-run graph whether every workspace that depends on `@supabase/api` reaches `@supabase/api#transit` from its `types:check`. It fails on the previous `turbo.json`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This bumps storage from v1.80.0-r0 to v1.80.2-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.80.2-r0 Planned after supabase/slim-services published v1.80.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>
…nt (CLI-2653) (#7045) ## TL;DR brings the `db` connection string parser, `pgpass` and `pgservicefile` under the effect lint ## whats introduced? - `db-config.parse`, `pgpass` and `pgservicefile` join the allow list file by file, so the rest of the `db` module waits for the later PRs in this stack - the parser reads `pgpass` and the service file through the `FileSystem` and `Path` services, and its libpq env reads go through `Config` once up front - `parseConnectionString` and `poolerConfigFromConnectionString` become effects, and every caller across `db`, `stack`, `test-db`, `migration squash`, `gen types` and the pg-delta engine yields them - an empty shell var still wins over the project `.env`, a missing `pgpass` or service file still reads as absent, and every parse error keeps its text - unit tests run on temp dirs and a pinned `Config` env instead of `process.env` writes, with a new case for the empty shell var ## ref - related: CLI-2653
) (#7046) ## TL;DR brings the `db` pg connection layer and few resolvers under the effect lint ## whats introduced? - `db-connection` and `db-dns` join the allow list - the `sslrootcert`, `sslcert` and `sslkey` reads go through `FileSystem`, keeping the BOM and the errno messages, and `dbConnectionLayer` captures it at build so `DbConnection.connect` keeps its type - a cert path with a NUL byte, only reachable as `%00` in a DSN, now reads `FileSystem.readFile: ...` instead of `TypeError [ERR_INVALID_ARG_VALUE]: ...`, since the platform keeps only the message for that throw - `--dns-resolver https` sends the same request through `HttpClient` with automatic HTTP tracing disabled, so no trace headers reach Cloudflare, and keeps every failure text and the 10s timeout; a `Db.resolveHostsOverHttps` span records the response status and answer count - tests pin the full cert read errors and cover the DoH resolver: non-200, rejected request, no valid IP, invalid and empty JSON and the timeout ## ref - related: CLI-2653
…I-2653) (#7048) ## TL;DR brings the `db` config toml reader under the effect lint ## whats introduced? - `db-config.toml-read` joins the allow list - shell env reads go through `Config` once, for the override names and every `env(...)` ref in the toml, and the sync `envLookup` reads from that snapshot - `config push` drops its copy of the env ref helpers and uses the reader's - the toml parse runs before the env snapshot with the same failure order and messages - invalid signing keys files retain native JSON parser details in both `config.toml` loading and `local-config-values`, with the same narrow lint exception used by the SSO readers - unit tests pin env through `Config` and temp dirs instead of `process.env` writes, and the push, reset and start integration tests pin the three values the reader now takes from `Config` ## ref - related: CLI-2653
…I-2653) (#7049) ## TL;DR brings the rest of the `db` module under the effect lint ## whats introduced? - the per file `db-config.parse`, `db-config.toml-read`, `db-connection` and `db-dns` entries collapse into `db-*.ts`, and `branch-target`, `connect-errors`, `pooler-fallback` and `schema-flags` join, which covers the whole `db` module - `db-config.layer`, `db-pull-run` and `db-push-core` yield their errors directly with the same classes and messages, and `branch-target` recovers through `Effect.option` and `orElseSucceed` - `db-config` integration tests feed env through `Config` instead of `process.env` hooks, and `db-image` and `db-target-flags` move to `it.effect` ## ref - closes: CLI-2653
## Summary Scoped personal access tokens let a user pick exactly which permissions a token gets, so the CLI needs to say which permissions each command requires. This continues the mapping work from the command permission model; see [CLI-2474](https://linear.app/supabase/issue/CLI-2474/proposal-derive-and-document-the-management-api-scopes-each-cli). ## What's in this PR - Reusable permission building blocks for the Management API calls made by shared modules rather than command handlers: the DB config resolver, project ref picker, linked-project cache, link services, storage credentials, project API keys, upgrade suggestion, branch ref and target lookups, the login profile lookup, and linked service versions. Many commands reach these modules, so each is mapped once and included by the commands that use it instead of being repeated in every declaration. Each block lives next to the module whose calls it describes. - An integration test per block that runs the real module and checks the requests it makes against the block, so the mapping can't drift from the code. Command tests replace these modules with fakes, so they wouldn't catch it. Each best-effort call also has a 403 case showing the module's result survives, and the drift check verifies `request.query` qualifiers against the recorded requests. - Model additions the blocks needed: - `{ flag, is }` tests a switch by its effective value, so `--skip-pooler=false` is distinguished from omitting it. A flag given an empty value counts as absent. - `{ dbTarget }` conditions with `withDbTarget` select on the database a run targets: `--db-url`, then `--local`, then `--linked`, then the command's default. The DB config block applies only on the linked target. - `withKind` lets a command override a block's required or best-effort label. - `request.query` records request details such as `reveal=true` as data. The blocks live in shipped modules and run `block()` when those modules load, which adds a small amount of declaration data to the bundle and changes nothing else at runtime. No command includes them yet; later PRs map the commands that do. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…og tail (#7120) ## Summary The sync repair agent only received the last 150 lines of each failed CI job. Turbo interleaves package output, so on #7112 the failing CLI integration test landed mid-log, behind the `@supabase/stack` output, and the agent reported it could not see the failure. `failureExcerpt` now keeps 40 lines after every `##[error]` annotation and vitest `FAIL` header, plus the existing tail, with ANSI codes and timestamps stripped. Gaps are marked with `[...]`. ## 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>
## Summary Approving a sync PR required its branch to contain the latest source tip (`develop` for `sync/develop-into-next`). A sync PR stays open while `develop` keeps moving, so any new conflict sent it back to a human, as on #7112 (comment). - When only the source moved, the fast-forward job now lands the approved head that CI tested. - When the target moved, it merges only the latest target into the approved head. A conflict there still asks for re-approval. - Newer source commits arrive with the next `Sync branches` run, on the next push to the source. The release-process doc is updated to match. ### Before ```mermaid flowchart LR A[sync PR approved] --> T{next moved?} T -- no --> S{develop moved?} S -- no --> F[fast-forward next] S -- yes --> M T -- yes --> M{merge latest next + develop cleanly?} M -- yes --> L[land merged head] M -- no --> H[human resolves conflict, re-approves] ``` ### After ```mermaid flowchart LR A[sync PR approved] --> T{next moved?} T -- no --> F[fast-forward next to approved head] T -- yes --> M{merge latest next cleanly?} M -- yes --> L[land merged head] M -- no --> H[human resolves conflict, re-approves] F --> D[next develop push: Sync branches brings the rest] L --> D ``` ## 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>
The macOS native-scenarios job is the longest job in the Test workflow, about twice any other. Most of it is `native: parallel stack isolation`, which starts two full native stacks at once and is starved on the standard `macos-latest` runner: it takes roughly 2x longer there than on the 8 vCPU Linux native job running the same test. Sharding the scenario files did not help (#7121), since Vitest already ran both files concurrently. This moves the job to the 6 vCPU Blacksmith macOS runner (`blacksmith-6vcpu-macos-latest`) so the two stacks get enough CPU and memory, keeping full macOS coverage. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
| async (run) => | ||
| `## ${run.name} (${run.conclusion})\n\n\`\`\`\`text\n${await jobFailureExcerpt(token, repository, run.id)}\n\`\`\`\`\n`, |
There was a problem hiding this comment.
⚪ Severity: LOW
An untrusted test can print attacker-controlled instructions into this fixed four-backtick block; failureExcerpt preserves fence delimiters, and this file is loaded into Claude's editable context. The injected text can steer the repair agent to commit malicious source changes as a bot, which may reach next after maintainer approval.
Helpful? Add 👍 / 👎
💡 Fix Suggestion
Suggestion: Don't wrap the CI failure excerpt in a hardcoded four-backtick fence. Use the existing codeFence() helper, which picks a fence longer than any backtick run in the text, so untrusted test output can't close the block and inject instructions. Compute the excerpt once, then build the section with codeFence(excerpt, "text").
⚠️ Experimental Feature: This code suggestion is automatically generated. Please review carefully.
| async (run) => | |
| `## ${run.name} (${run.conclusion})\n\n\`\`\`\`text\n${await jobFailureExcerpt(token, repository, run.id)}\n\`\`\`\`\n`, | |
| async (run) => { | |
| const excerpt = await jobFailureExcerpt(token, repository, run.id); | |
| return `## ${run.name} (${run.conclusion})\n\n${codeFence(excerpt, "text").join("\n")}\n`; | |
| }, |
…LI-2599) (#7115) ## Summary Stable now warns on stderr for every flag that CLI-2596 removes in v3. Each warning names the replacement and says the flag goes away in v3. | Flag | Warning | | -- | -- | | `domains create\|get\|reverify\|activate\|delete --include-raw-output` | `Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.` (new, previously help text only) | | `db pull --use-pg-delta` | `Flag --use-pg-delta is deprecated and will be removed in v3, use --declarative with [experimental.pgdelta] enabled = true in your config.toml instead.` (reworded) | | `db pull --experimental` (structured dump) | ``The --experimental structured-dump mode for `db pull` is deprecated and will be removed in v3. Use --declarative instead to pull the remote schema as per-object files.`` (reworded) | - The domains warning fires only when the flag is set. It goes to stderr, so `-o json` / stream-json stdout is unchanged. - There are no other behaviour changes. `--include-raw-output` still forces JSON output, and it is still inert on `delete`. - Flag-level usage over the last 30 days is low: `--include-raw-output` 2 runs from 1 person, `--use-pg-delta` 47/30, `--experimental` 17/9. The numbers are on CLI-2596. The removal itself is in a separate PR against `next`. ## Linked issue Closes CLI-2599 ## Checklist - [x] The PR title follows Conventional Commits. - [x] Tests added or updated: each domains command's integration test covers the warning with and without the flag, and the db pull tests assert the new wording. - [x] `pnpm check:all` passes. The apps/cli unit and integration suites pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…7106) ## Summary Scoped personal access tokens let a user pick exactly which permissions a token gets, so the CLI needs to say which permissions each command requires. This maps the first commands onto the permission model; see [CLI-2474](https://linear.app/supabase/issue/CLI-2474/proposal-derive-and-document-the-management-api-scopes-each-cli). ## What's in this PR - Permission declarations for `secrets list`, `snippets list` and `db query`, built from the shared building blocks (project ref picker, linked-project cache, DB config). `db query` selects its Management API call by database target, defaulting to local. These three work as the worked example for mapping the remaining commands. - Each command's integration test checks the requests it records against its declaration, so the mapping can't drift from the code. The tests also run the commands through the real linked-project cache layer, and `secrets list` through the project picker, so a block dropped from a declaration fails a test. - `assertBlockDrift`, a drift check for building blocks that takes its label from the block instead of a made-up command name. The existing block tests use it, and `permission-drift.ts` has callers now, so its `knip` entry is gone. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
`Run integration tests` ran every package's integration suite in one job, with the stack suite as the long pole. This turns it into a matrix: the stack suite in three Vitest shards, the CLI suite in two, and the API suite plus the stack namespace e2e tests in their own entry. The `Run unit and integration tests` summary gate still aggregates the matrix result. CLI e2e files run serially, so a shard can be no shorter than its longest file, and `start.lifecycle.e2e.test.ts` alone set that floor. Its two health-check scenarios move to `start.health.e2e.test.ts`, the e2e timings are refreshed from a current run, and CLI e2e runs in five shards instead of four. ## Effect on the Test workflow Compared with the same workflow on `develop`, excluding time spent waiting for a runner: - The slowest integration job is about 55% shorter, so integration tests are no longer on the critical path. - The slowest CLI e2e shard spends about 15% less time in tests. One shard still runs about 25% over the others, so the timing-based split is not yet even. - The workflow as a whole finishes about 20% sooner. It is now bounded by the CLI and stack e2e jobs, which end within a few seconds of each other. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
| readFileSync(join(PROMPTS_DIR, "resolve-prompt.md"), "utf8"), | ||
| "## Conflicted files", | ||
| "", | ||
| ...conflicts.map(({ path, kind }) => `- \`${path}\` (${kind})`), |
There was a problem hiding this comment.
⚪ Severity: LOW
A contributor-controlled filename from a conflicting PR is interpolated into Claude's resolution prompt; a newline or escaped backtick can break out of the file-list entry and inject instructions. The edit-capable agent may then commit malicious source changes to the sync branch, which can be promoted after approval.
Helpful? Add 👍 / 👎
💡 Fix Suggestion
Suggestion: Don't interpolate the raw filename into the prompt. Serialize it with JSON.stringify so newlines, control characters and quotes are escaped, and drop the backtick code-span wrapper that a crafted filename could break out of. Optionally, add a line to resolve-prompt.md saying that the file names listed are untrusted data and not instructions.
⚠️ Experimental Feature: This code suggestion is automatically generated. Please review carefully.
| ...conflicts.map(({ path, kind }) => `- \`${path}\` (${kind})`), | |
| ...conflicts.map(({ path, kind }) => `- ${JSON.stringify(path)} (${kind})`), |
… (#7117) ## TL;DR brings the `migration` module under the effect lint ## whats introduced? - the `migration*` and `migrate-and-seed` files join the allow list, with the sql helpers they run through - (`postgres-url`, `sql-split`, `param-introspection`, `pg-net-guidance`, `drop-objects`, `pg-dump` and `bundled-postgres-client`), and `postgres-client.run` widens to its tests - `checkScannerBufferSize` reads `SUPABASE_SCANNER_BUFFER_SIZE` through `Config`, still ahead of the project env - migration timestamps go through `DateTime`, and `migration fetch` yields its errors directly with the same messages - the migration and seed tests swap `node:fs` and `process.env` for scoped temp dirs and `withConfigEnv`, and the `pg-dump` and some `migration-history` tests move to `it.effect` ## ref - related: CLI-2654
CLI-2654) (#7118) ## TL;DR brings the `seed` and `pgdelta` modules under the effect lint ## whats introduced? - the `seed*`, `pgdelta*` and `diff-engine` files join the allow list - `isPgDeltaDebugEnabled` reads `PGDELTA_DEBUG` through `Config`, so `db diff`, `db pull` and the declarative `generate` and `sync` yield it - `seed-ops` fails the scanner check with `MigrationSeedError` instead of a bare `Error`, with the same message - `seed-buckets` yields its errors directly and recovers with `orElseSucceed` - the pgdelta tests pin `PGDELTA_DEBUG` through `withConfigEnv`, and the ssl probe test owns its server through `acquireRelease` ## ref - closes: CLI-2654
This bumps storage from v1.81.3-r0 to v1.81.4-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.81.4-r0 Planned after supabase/slim-services published v1.81.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 realtime from v2.143.3-r0 to v2.143.4-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.143.4-r0 Planned after supabase/slim-services published v2.143.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 storage from v1.81.4-r0 to v1.81.5-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.81.5-r0 Planned after supabase/slim-services published v1.81.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>
…2693) (#7122) ## TL;DR moves the remaining commands that predate the effect skill onto it, with no change in behaviour ## whats introduced? <details> <summary>commands covered</summary> `start` `stop` `status` `services` `init` `link` `unlink` `bootstrap` `db` `migration` `inspect` `seed` `test` & `stack` </details> - error recovery narrows to `catchTag`, `catchTags` and `catchReason` instead of broad catches and `_tag` checks - casts and non null assertions give way to `Predicate` narrowing, exhaustive `Match` and schema decoding - mutable state read by finalizers moves into `Ref`, and the shadow port retry goes through `Schedule` - the pg-delta services use `Effect.fn` under their existing span names, adding `PgAdmin.diff` and two `stack start` spans - tests run on `it.effect` and wait on `Deferred` instead of polling, staying on `it.live` only where a real clock, process, server or backoff is involved ## notes - no user facing behaviour change.. - output, exit codes, errors and telemetry match develop, and the new spans only appear in trace output and the `SUPABASE_DEBUG=1` span console - the shadow port probe now also closes its server when interrupted instead of leaking it ## ref - closes: CLI-2693 - extends: #7092
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>
…7135) Maps `db start`, `db test`, `db lint`, `db advisors`, `db reset`, `db push` and `seed buckets` 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 model change.** `withCondition` now accepts a database-target condition as well as a flag condition, so a command can gate a block on the target it selects (`--local`, `--db-url`, linked). `FlagCondition` is no longer exported. **Blocks used.** `dbConfig` (lint, advisors, reset, push, `db test`), `linkedProjectCache` (all but `db start` and `db test`), `storageCredentials` (`seed buckets`). `db advisors` adds its own security and performance entries, keyed on `--type`; because `--type` is optional, each operation has an entry for the listed values and one for the flag being absent. **Defaults.** `db lint`, `db advisors`, `db reset`, `db test` and `seed buckets` default to the local target; `db push` defaults to linked. Each handler reads its target from the command line, not from the parsed flag values. **Tests.** Lint, advisors, reset, push and `db test` run the real database-config resolver and the real linked-project cache and check the recorded requests; `--local` runs assert no Management API request. `seed buckets` records the api-keys request (`reveal=true`) and the cache fill. Also covered: `--password` skipping the login role, an advisors 403 stopping before the performance call, and `--linked=false` in lint and seed. The `node:net` mock the real-resolver tests need now lives in one helper, `tests/helpers/refuse-invalid-db-hosts.ts`, and replaces the copies in `db query` and the resolver tests. **Not covered.** - `--linked=false` on push, reset, advisors and `db test`. - A default target declared linked when it is really local would not fail a drift test: a local run records no requests. Also corrects the API routes listed in the `db pull` and `db diff` side-effects docs (`/cli/login-role`, `/config/database/pooler`, `POST /network-bans/retrieve`); they listed paths the code does not call. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
This bumps storage from v1.81.5-r0 to v1.82.0-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.82.0-r0 Planned after supabase/slim-services published v1.82.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>
…ns (#7136) Maps `db dump`, `db pull`, `db remote commit`, `db diff`, `db schema declarative generate` and `db schema declarative sync` 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. Stacked on the previous db PR. **Shared changes.** - `dbConfigPoolerFallback` (new block): the pooler retry after a linked dump, declarative export or pull diff fails with an IPv6 error against the direct host. `dbConfig` is now built from a shared entries list with the linked target prepended; its declaration is unchanged. - `dbConfigLinked` (new block): the same calls without the target condition, for commands that reach the linked path some other way. - `withContext` (new builder): records a prompt choice or other non-flag prerequisite on every entry of a fragment. **Defaults.** `db dump`, `db pull` and `db remote commit` default to the linked project; `db diff` defaults to local. `db dump`, `db pull` and `db remote commit` declare `dbConfig`, the fallback block and the linked-project cache. `db diff` declares `dbConfig` and the cache for `--linked`, plus the linked calls for a `--from` or `--to` side set to `linked`, which resolves the project without `--linked`. `generate` declares them for `--linked` and for the target prompt (no target flag, migrations present). `sync` has no target flags, so it declares them for the bootstrap prompt only. **Tests.** Each command runs the real database-config resolver and the real linked-project cache and checks the recorded requests, including `--password` skipping the login role and `--local` making no request. Dump and pull also run the container IPv6 retry (login role, pooler config, login role). `db diff` covers `--from linked`, `--to linked` and both. The `node:net` mock gains a scoped `withReachableDirectHost` helper for the direct-host path. **Not covered.** - `--use-pg-schema` on `db diff` hands off to Go before any TypeScript call; it is declared as having no API effect here. - The generate and sync prompt-path entries and the fallback entries carry a prerequisite, so the exact check skips them; tests assert their request sequences directly. - `dbConfig` and `dbConfigPoolerFallback` cover the same operations under the linked target in dump, pull and remote commit, so a drift test cannot tell a missing `dbConfig` from a present one there. Both are tested on their own. - `db remote commit` has no retry test; it shares pull's code. - `--linked=false` and `--local=false` on these commands rely on the existing handler tests. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
) ## Summary **TL;DR** When a command dies on an unclassified Effect `PlatformError` (a filesystem or stdio failure whose reason is not `NotFound`, `PermissionDenied`, or `BadArgument`), telemetry now names the reason and the Effect operation that raised it. Today every such failure collapses into one `error:Defect:PlatformError` fingerprint, which recent prereleases report on `status` / `stack status` for the new local stack without any way to tell which call fails. ### Before ```mermaid flowchart LR A["FileSystem / Stdio call fails"] --> B["PlatformError<br/>(Busy, Unknown, ...)"] B -->|orDie| C["error:Defect:PlatformError"] ``` ### After ```mermaid flowchart LR A["FileSystem / Stdio call fails"] --> B["PlatformError<br/>(Busy, Unknown, ...)"] B -->|orDie| C["error:Defect:PlatformError:<br/>Busy:FileSystem.readFile"] ``` ### Why The failures cluster right after `start` while other commands run in parallel, and a retry succeeds, which points at a race on shared state or on stdout. Several call sites turn a `PlatformError` into a defect, so the fingerprint has to carry the operation before the cause can be fixed. ### What changed - A `PlatformError` defect fingerprints as `error:Defect:PlatformError:<reason>:<Module>.<method>`, for example `…:Busy:FileSystem.readFile`. - Only the closed reason tag and Effect's module and method names are read, as data descriptors; `pathOrDescriptor` and `description` never reach telemetry. If any part is not a plain identifier, the fingerprint stays `error:Defect:PlatformError`. - `PlatformError` failures that are already classified (`not_found`, `filesystem`, `bad_argument`) are unchanged. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…rlays (#7130) ## Summary The published CLI reference describes pg-delta, but its source in this repo still describes migra. The next release would regenerate the reference and revert the September fixes made directly in supabase/supabase. This PR moves those fixes into the overlays so the generator produces the corrected text. Three files under `apps/cli/docs/` change. No code changes. - `db diff`: projects with `[experimental.pgdelta] enabled = true` use pg-delta. Other projects run migra. Storage buckets are data, so no engine diffs them. The known-miss list (publications, `security_invoker` views) now applies to migra only, since pg-delta tracks both. - `db pull`: initial-pull behavior is stated per engine. pg-delta diffs against an empty shadow database. migra seeds the migration with `pg_dump`. `--diff-engine pg-delta` is the single-run opt-in. Added the managed-schemas paragraph (triggers on `auth.users`, RLS on `storage.*`). Added a caveat for `--declarative` on projects without pg-delta: it also sets `[db.migrations].schema_paths` and breaks `db diff`. - `db pull` examples: the CLI no longer prints "auth and storage schemas are excluded" or "Try rerunning with --debug". The examples show current output. The custom-schemas example uses `public,extensions` instead of `auth,storage`. Verification: ran `bun scripts/generate-docs-spec.ts` and diffed the `db diff` and `db pull` sections against the yaml on supabase/supabase `master`. No stale sentence remains. The only other differences are flag descriptions, which come from the command code. ## Linked issue Closes # - [x] The linked issue is **open** and carries the `open-for-contribution` label (or I'm a Supabase maintainer). ## Checklist - [x] The PR title follows [Conventional Commits](https://www.conventionalcommits.org/) (e.g. `fix(cli): …`). - [ ] Tests added or updated for the change. - [ ] From the repository root, `pnpm check:all` passes; relevant package tests pass for every touched workspace, and `pnpm types:check` passes for each touched TypeScript workspace (or workspace declaring it). --------- Co-authored-by: avallete <andrew.valleteau@supabase.io> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Andrew Valleteau <avallete@users.noreply.github.com>
This bumps storage from v1.82.0-r0 to v1.82.1-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.82.1-r0 Planned after supabase/slim-services published v1.82.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>
`supabase stop` no longer fails when another stack on the same Docker daemon is stopping at the same moment. Docker runs one prune at a time per daemon and rejects a concurrent container, volume, or network prune with `a prune operation is already running`, even when the label filters differ. `stop` now runs that prune again while Docker reports the conflict, with a short jittered wait and a bounded number of attempts; any other prune failure still fails immediately. The container CLI helper now also collects stderr so the conflict can be recognised. Split out of #7129, which runs CLI e2e files concurrently and depends on this. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ows and macOS jobs (#7129) Cuts CI time and cost: dependency and Docker caches come from Blacksmith sticky disks that only protected branches write, more end-to-end work runs in parallel, and PRs into `develop` run Windows and macOS jobs only when they touch platform-sensitive code. ## Gains ### Whole run Measured from every successful `test.yml` run between Oct 10 and Oct 14. Run time counts from run creation to the last job finishing, so it includes queueing. Values are median [range]; every run below ran the Windows and macOS jobs. | | Other branches, macOS native on `macos-latest` (Oct 10–13, 132 runs) | Other branches, macOS native on Blacksmith macOS since #7126 (Oct 13–14, 52 runs) | This PR, warm disks (7 runs) | This PR, empty disks (2 runs) | | --- | --- | --- | --- | --- | | Run time | 8.5 min [4.5–22.6] | 5.0 min [3.9–9.4] | 3.3 min [2.7–4.8] | 3.4–4.1 min | | Slowest job | 8.1 min [4.4–11.1] | 4.6 min [3.6–7.9] | 2.8 min [2.5–3.4] | 3.1–3.3 min | | macOS native scenarios | 8.1 min [4.4–11.1], one job | 3.4 min [2.9–7.9], one job | 2.1–3.4 min, slowest of up to four jobs | 1.7–1.9 min, slowest of four jobs | | Total job time | 62 min, 20 jobs | 65 min, 26 jobs | 44–56 min, 29–33 jobs | 58–62 min, 33 jobs | The macOS runner change in #7126 brought most of the drop between the two baselines. On top of it, this PR brings a run to about 3 minutes once disks are warm: the slowest Linux job goes from 4.3 min [3.6–5.0] on other branches since Oct 14 to 2.7 min [2.5–3.3], and Linux is now what bounds the run. One warm run took 15.9 min while its slowest job took 2.8 min; the rest was runner queueing, so it is left out of the range above. About half of PRs into `develop` will also skip the Windows and macOS jobs. ### Warm vs empty disks Measured on this PR by running the same commit twice: once with empty sticky disks, then again after the fill workflow populated them. | Linux jobs (Blacksmith) | Warm vs empty disks | | --- | --- | | Setup | ~80% faster | | Code quality | ~40% faster | | Unit tests | ~40% faster | | Integration shards (average) | ~30% faster | | CLI e2e shards (average) | ~25% faster | | Stack e2e, native (average) | ~25% faster | | Stack e2e, Docker (average) | ~20% faster | | Stack e2e, Podman (average) | ~15% faster | Compared with the base branch, Windows port tests setup is ~60% faster and macOS port tests setup ~40% faster. Splitting the declarative sync e2e into two files and letting its commands reuse the shadow baseline cache makes the slowest of those files ~65% faster than the original single file. Splitting the macOS native scenarios into four jobs and caching their native artifacts makes the slowest of them ~35% faster than the single job it replaces; with a warm cache the isolation job is ~45% faster than without. On warm disks every job finished in under three minutes; remaining time is test work rather than setup. ## Changes **Sticky disks.** On Blacksmith Linux runners, `test.yml` jobs mount the pnpm store, Go caches, and (for container jobs) Docker's image store from Blacksmith sticky disks instead of downloading them through `actions/cache`. Test jobs always mount them read-only; only the fill workflow commits, after pruning unused pnpm packages, containers, and volumes. Native stack jobs also mount the native artifact cache. Jobs that mount the Docker image disk stop Docker in a final step so the disk unmounts cleanly; the fill workflow also prunes containers and volumes first, even after a failure. Other workflows, including release builds, keep `actions/cache`. **Disk refill.** `fill-sticky-disks.yml` runs on pushes to protected branches, daily, and on demand. It installs dependencies, downloads the stack's native service artifacts, pulls both the `supabase start` images and the stack package's catalog images, so every disk stays warm. Default-branch fills also drop artifacts and images of older versions. It is the only writer, so Blacksmith's sticky-disk branch protection can be enabled. The disk keys are new, so the first fill after merge seeds every disk from protected-branch code. **Parallel CLI e2e.** CLI e2e files run four at a time in CI (`SUPABASE_E2E_WORKERS`); local runs stay serial. Each worker takes free ports only from its own slot, so a port one worker released is never handed to another. Files that cannot share the daemon run one at a time afterwards in an `e2e-exclusive` project: `start.health`, which tags a broken Mailpit image, and the files that provision pg-delta shadow databases on ports the CLI picks itself. Concurrent `supabase stop` teardowns rely on the prune retry from #7143. **Lighter Windows and macOS setup.** Port tests install only node, bun, and pnpm through mise instead of the full toolchain. Windows also skips the pnpm store archive because downloading packages is faster there than unpacking it. **Split slow e2e work.** The declarative sync e2e runs as two files that shard independently, and their commands share one home with the shipped shadow baseline cache on, so only the first sync provisions a cold shadow. macOS native scenarios run as separate lifecycle, confinement, idle, and isolation jobs, each restoring its native artifacts through `actions/cache`. **Windows and macOS on demand.** On PRs into `develop` or `next`, the Windows and macOS port tests and macOS native scenarios run only when the PR touches platform-sensitive paths (stack host, namespace, port, process, internal and artifact code, the CLI commands the Windows job covers, the lockfile, root config, and the test workflow). They always run in the merge queue, on PRs into `main` or `v*.x`, on `next` release runs, and when the `run-ci` label is added, which now also applies to ready PRs into `develop` and `next`. Replaying the last 50 merged PRs, about half would skip them. The summary jobs fail if change detection fails, so a skipped platform job never hides an error. **Concurrent whole-stack scenarios.** Stack e2e jobs run their scenarios concurrently. The write-confinement scenario watches the whole temp tree, so it runs in its own `confinement` job per runtime. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…havior (#51751) ## Problem The two `supabase stack` guides describe behavior the CLI changed in v2.120.0 (Oct 6) and v2.121.0 (deploy PR supabase/cli#7104 open). A reader following them today hits different output than the pages promise. | Guide says | CLI does | Since | | --- | --- | --- | | `stack logs` streams live lines only, no history | Keeps history, adds `--tail`, `--since`, `--service gateway`, reads while stopped (supabase/cli#6893) | v2.120.0 | | Rejected config change fails with `cannot change on the saved stack` | Fails with `The saved stack cannot adopt these changes` (supabase/cli#6944) | v2.120.0 | | Port changes always need a new local project | A stopped project re-plans ports on next start (supabase/cli#6950) | v2.121.0 | | No bulk destroy | `destroy` accepts repeated `--stack-id` (supabase/cli#7000) | v2.121.0 | | OrioleDB unsupported | `db.orioledb_version` runs in both runtimes (supabase/cli#6934) | v2.121.0 | | Nothing about native `pg_cron` | Jobs run in background workers; earlier versions fail every job (supabase/cli#7039) | v2.121.0 | The CLI getting-started guide (#50736) also says a container runtime is required and never mentions the native runtime. ## Solution Updated both stack guides and added one sentence to the getting-started prerequisites. - v2.121.0-only behavior is marked "In CLI v2.121.0 or later", with the earlier behavior stated beside it. The guides' baseline stays at v2.119.0. - Every claim was checked against supabase/cli `develop` source and the commits above. - The `supabase stack` commands stay out of the generated CLI reference by design, so these guides remain the only place the flags are documented. Check all before review: - [x] I have read [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) - [x] If I wrote a new docs topic or edited an existing topic, I used the `/write-the-docs` or `/edit-the-docs` skill, which applies the docs [style guide](https://github.com/supabase/supabase/tree/master/apps/docs/style-guide)
…emoved in v3 (#7154) ## TL;DR Users still relying on config keys, env vars or file conventions that v3 removes now get a warning on stable that says so. `SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION` was silently ignored after the rename to `SUPABASE_DB_ORIOLEDB_VERSION`; it now works as a deprecated alias with a warning. ## Before ```mermaid flowchart LR A["SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION set"] --> B[Ignored silently] --> C[Plain Postgres image] ``` ## After ```mermaid flowchart LR A["SUPABASE_DB_ORIOLEDB_VERSION set?"] -- yes --> D[OrioleDB image] A -- no --> E["SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION set?"] E -- yes --> F["Warn: removed in v3"] --> D E -- no --> G[config.toml value] ``` ## Why The v3 removals in CLI-2597 need a stable release that warns first. Several existing warnings did not mention v3, `INBUCKET_URL` in `status` had no warning, and a project using the old OrioleDB env var started plain Postgres with no message. ## What changed - `SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION` is used when `SUPABASE_DB_ORIOLEDB_VERSION` is unset and prints a deprecation warning; the new variable wins when both are set. Resolution lives in one helper used by every reader. - Warnings for `[inbucket]` / `[remotes.*.inbucket]`, `experimental.orioledb_version`, the `linkedin` / `slack` auth providers and `import_map.json` in `functions deploy` now say they will be removed in v3 and name the replacement. - `supabase status` warns that `INBUCKET_URL` is deprecated in favour of `MAILPIT_URL` for output formats that include it. - All warnings go to stderr; machine-readable stdout is unchanged. - Also touches `packages/config` warning wording, so this cuts an `@supabase/config` release. ## Terminal captures Recorded with vhs from source (`bun apps/cli/src/main.ts`) against a fresh `supabase init` project whose `config.toml` sets `[inbucket]`, `experimental.orioledb_version` and `[auth.external.slack]`. Each GIF runs `supabase stop` twice (it loads the config without a running stack): first with `SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION` unset, then with it set inline on the second run only. Before (merge-base with develop):  After:  Closes CLI-2601 Closes CLI-2569 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
supabase_functionson the stack backend (CLI-2626) (fix(cli): createsupabase_functionson the stack backend (CLI-2626) #7029)stack destroyshould accept a list of stack ids #6964) (feat(stack): allow repeatable --stack-id in stack destroy (#6964) #7000)dbconnection string parser with effect lint (CLI-2653) (refactor(cli): cover thedbconnection string parser with effect lint (CLI-2653) #7045)dbconnections and dns with effect lint (CLI-2653) (refactor(cli): coverdbconnections and dns with effect lint (CLI-2653) #7046)dbconfig toml reader with effect lint (CLI-2653) (refactor(cli): cover thedbconfig toml reader with effect lint (CLI-2653) #7048)dbmodule with effect lint (CLI-2653) (refactor(cli): cover the rest of thedbmodule with effect lint (CLI-2653) #7049)startfailing on podman without snippets (CLI-2690) (fix(cli): stopstartfailing on podman without snippets (CLI-2690) #7089)api,httpandauthmodules with effect lint (CLI-2649) (refactor(cli): cover theapi,httpandauthmodules with effect lint (CLI-2649) #7084)project-environmentandlocal-project-contextwith effect lint (CLI-2650) (refactor(cli): coverproject-environmentandlocal-project-contextwith effect lint (CLI-2650) #7077)viper-envwith effect lint (CLI-2650) (refactor(cli): coverviper-envwith effect lint (CLI-2650) #7078)