Repository navigation
chore: production deploy - #7452
Conversation
`branches create`, `vanity-subdomains activate`, and `vanity-subdomains check-availability` ran an entitlements lookup (get project, then get organization entitlements) after any 4xx without the `entitlement_required` envelope. The Management API already returns that envelope on all three gates, so the lookup only ran for errors unrelated to the plan: two extra requests, and on an org without the feature, an upgrade hint and a `plan_limit` classification for an unrelated error such as a validation failure. I removed the lookup and its permission declarations at those three commands. The CLI still prints the upgrade hint from the envelope on gated denials and fires `cli_upgrade_suggested` for `branches create` and `vanity-subdomains activate` (`check-availability` still suppresses the event), unchanged since #7271. **Note:** I left `branches update` and the `sso` commands on the lookup for now: the SAML routes return no envelope yet, and I haven't confirmed that the persistent-branch gate on `branches update` returns one. ## Linear - part of GROWTH-1050
## TL;DR fixes `supabase start` sitting about 10s on docker's first health check when the services are already up. ## whats improved/fixed? docker runs a container's first health check one full interval after it starts, and our interval is 10s. so `supabase start` kept waiting on that check even when everything was ready sooner. a restart with the issue's excludes went from about 12.8s to 5.2s. a full stack restart went from about 14s to 8s `db start` and `db reset` get the same. ## how? containers with a docker healthcheck now get `--health-start-interval 1s` and a 10s start period when `docker version` shows API 1.44 or newer for the client binary, the client in use, and the daemon, so the first check runs about 1s in and the interval stays 10s after. kong's check lived only in its image, so its spec now sets the same `kong health` check and gets those flags too. > [!NOTE] > on engines without the flag (docker below API 1.44, podman) containers keep the 10s first check. ## ref: - closes: #7427
) Follow-up to #7412. Block-scoped workloads started one stack per block of five launches, so a ten-run workload gave each build only two stack sessions. Launches in one block share a stack session, so session-to-session spread could push the branch median outside the base range, and an A/A run (same CLI code as base and branch) was flagged as slower or faster. - Blocks are now two launches, so each build gets five stack sessions at ten runs, and each measured launch records its block index in the report. - With at least three paired blocks, the block is the unit of evidence. The median of the per-block deltas must clear the existing 5 percent and 5 ms floor, a strict majority of blocks must move the same way past that floor, and the median delta must exceed 1.5 times the larger interquartile range of the two builds' block medians. One odd block cannot create or hide a verdict, and mixed-sign blocks read as within noise. - Run-scoped workloads keep the existing rule (the branch median outside the base min-max range) and also need the median delta to exceed 1.5 times the larger interquartile range of the two builds' launches, so a wide spread reads as noise. - `estimatedSeconds` for block-scoped workloads is rescaled for the extra stack sessions, and `tracing-monitoring.md` describes the new rule. False positives may not be entirely eliminated. A follow-up could allow re-running one or more chosen workloads, so a reported regression can be confirmed without re-running the whole benchmark. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
) The local container inputs precedence goldens recorded the default Postgres image tag, so every Postgres bump PR (e.g. #7408) failed integration tests until the goldens were regenerated by hand. The test now redacts the current default database image as `<DEFAULT_POSTGRES_IMAGE>`, keeping the goldens focused on config precedence. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…7443) The Management API spec now describes the ESZIP update-function body through a shared component that carries a `description` and `contentEncoding`. The generator only rewrote a bare `{ format: "binary" }` annotation to `BinaryInput`, so this body regenerated as `Schema.String` and rejected `Uint8Array`/`ArrayBuffer`/`Blob` uploads (breaking `functions deploy`). The sanitizer now reduces binary string schemas to their type and format, so they keep generating as `BinaryInput`. Generated output on `develop` is unchanged; this unblocks the API sync in #7418, which still needs its v2 PostgREST config fixture updated for the new `db_pool_max_lifetime`/`db_pool_max_idletime` fields. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This bumps postgres from 17.11.0.011-r0 to 17.11.0.016-r0. https://github.com/supabase/slim-services/releases/tag/postgres-17.11.0.016-r0 Planned after supabase/slim-services published 17.11.0.015-orioledb-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> Co-authored-by: Andrew Valleteau <avallete@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> 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 postgres from 17.11.0.016-r0 to 17.11.0.017-r0. https://github.com/supabase/slim-services/releases/tag/postgres-17.11.0.017-r0 Planned after supabase/slim-services published 17.11.0.017-orioledb-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.016-r0 to 15.19.0.017-r0. https://github.com/supabase/slim-services/releases/tag/postgres-15.19.0.017-r0 Planned after supabase/slim-services published 17.11.0.017-orioledb-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>
## TL;DR The live branch helper now re-creates a branch once when the platform drops TLS while setting the branch password, the same way it handles a 57P01 database restart. - `apps/cli/src/commands/branches/get/get.live.test.ts › gets a preview branch by name`: `branches create` failed with `400: Failed to update password: Client network socket disconnected before secure TLS connection was established`. This is a transient platform rejection during create, like the 57P01 restart from #7256, but the helper only re-issued the create for 57P01. The helper's transient-rejection matcher now also covers this documented TLS drop, so it runs the same single clear-and-recreate. A second rejection still fails. `create.live` uses the same helper and gets the fix too. ## ref spotted in: - https://github.com/supabase/cli/actions/runs/40170147136/job/121099157809 (develop) - https://github.com/supabase/cli/actions/runs/40170152974/job/121099176450 (main) --- _Generated by [Claude Code](https://claude.ai/code/session_014y76TaNM2eKGdpmiMQXWqu)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
## TL;DR The Realtime, pg-meta and Studio catalog test hung on Podman without saying where. It now fails before the test timeout, naming the service and phase that stalled and that service's output. - `Realtime.integration.test.ts › serves Realtime, pg-meta, and Studio against the owned database`: on Podman, the test hit the 120s test timeout with only the generator start frame as a clue. The test runs four services in sequence (database, Realtime, pg-meta, Studio), each with a recipe, start, readiness and HTTP step, so a stall anywhere looked the same. The cause is still unknown, so this change is a diagnostic. The scoped body now runs under a 110s guard. Each step records its phase, and each recipe's launch output is collected. A stall fails with `CatalogStallError` naming the service and phase in progress (including stop and root cleanup) and the last part of that service's output. ## ref spotted in: - https://github.com/supabase/cli/actions/runs/40530528881/job/122281757547 --- _Generated by [Claude Code](https://claude.ai/code/session_01MBEk1PCdaXDCUPR16M9Ria)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
## TL;DR The native owner-logs test no longer pays a private cold artifact fetch inside its timeout, and a stall now names the step that hung. - `Owner.logs.integration.test.ts › persists native output, serves history and follow, and deletes it on destroy`: the test hit the 30s test timeout while starting native Mailpit. The vitest frame pointed at the `openOwner` helper, not the stalled step. This file kept native artifacts in an OS temp cache (`$TMPDIR/supabase-stack-artifacts`) that no other suite shares, so on a runner without a warm temp dir its first native test fetched Mailpit inside the test timeout (the next native test in the file then took ~12s instead of under 1s). The file now uses the shared test artifact cache (`testArtifactCacheRoot`) like the other stack integration suites. The create, start (artifact preparation), readiness, first-follow and history steps also run under a 25s guard that fails with `NativeMailStallError`, naming the step, the instance id and Mailpit's persisted output, so a remaining stall points at its phase. ## ref spotted in: https://github.com/supabase/cli/actions/runs/41383389524/job/125078843228 (green on the same SHA: https://github.com/supabase/cli/actions/runs/41383048926/job/125077793411) --- _Generated by [Claude Code](https://claude.ai/code/session_01MBEk1LVZyNoX4Zg9Nd5ZMM)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
This bumps storage from v1.88.0-r0 to v1.89.1-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.89.1-r0 Planned after supabase/slim-services published v1.89.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>
## TL;DR re-issues the live `db reset` cleanup once when the platform restarts the database, so one dropped connection can't leave a migration behind for the next file - `db reset` live failed in cleanup with `FATAL: terminating connection due to administrator command (SQLSTATE 57P01)` from the pooler on connect. the cleanup reset never ran, so the test's migration stayed in the shared project's history and the following `db push` live file failed with `Remote migration versions not found`. the `db reset` and `db push` cleanups now go through `resetLiveDatabase`, which re-issues the reset once on a platform database restart (the same rule `branches create` already uses) and names the re-issue when it fails again ## ref - spotted in: [failing job](https://github.com/supabase/cli/actions/runs/41176803525/job/124465943942) [green same SHA](https://github.com/supabase/cli/actions/runs/41015125746) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01MBEk1LFBnaQgPd2VvJLo4X Co-authored-by: Claude <noreply@anthropic.com>
…7288) ## TL;DR Two stack integration tests failed because of runner state, not because of the code under test. - `Network.integration.test.ts › a pinned shared listener keeps its reservation until its last route closes`: on Windows, the bind failed as a foreign port conflict on 1801. The random pinned port landed on a port that a Windows runner service holds, and the static held-port list cannot anticipate every such port. `pinnedPort` now draws again until no listener answers the drawn loopback port and the bind's own vacancy guard (`Ports.probeVacant`) accepts it. This replaces the static list. - `DockerDatabaseStorage.integration.test.ts › round-trips PostgreSQL 15 data with its catalog image`: the pull failed with `toomanyrequests` after all pull retries. GHCR rate-limited the runner for longer than the pull backoff, and the tests had no image mirror, unlike the stack's production wiring. Three changes give the tests the same ECR fallback the stack uses: - The tests' container runtimes and the spawned helper-owner fixture now use `slimImageMirrors`. - The direct engine runs use the reference the runtime resolved. - The test that checks a custom mirror keeps its explicit mirror. ## ref spotted in: - https://github.com/supabase/cli/actions/runs/39757673300/job/119753742485 - https://github.com/supabase/cli/actions/runs/39812209412/job/119941209064 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01XrrxbNdkjLwsEeJJiJvGs8 --------- Co-authored-by: Claude <noreply@anthropic.com>
## TL;DR The Mailpit launch test hung on Podman without saying where. It now fails within its timeout, naming the phase that stalled and Mailpit's output. The detached stall guard it uses is now one shared test helper, `failOnStall`, which the Realtime and native owner-logs tests also use. - `Mail.integration.test.ts › launches the real Mailpit recipe and serves its HTTP endpoint`: on Podman, the test hit the 30s test timeout with no diagnostic. The error frame pointed at `sendTestEmail`, but this test never sends mail, so that frame is misleading. The test waits on start, readiness and a `/readyz` request. Container readiness may wait up to 60s, longer than the test timeout, and the `/readyz` request has no bound. So a stall in any of the three looked the same. The whole test scope now runs under one 25s guard inside the 30s test timeout, and a `Ref` tracks the current phase. The test also collects the recipe's log stream, so a stall fails with `MailStallError` naming the phase and showing Mailpit's output so far. - `packages/stack/tests/stall-guard.ts` adds `failOnStall(duration, onStall)`. It joins the guarded effect from a detached fiber, so the stall error surfaces at the deadline instead of waiting for teardown, and it interrupts the stalled fiber in the background so scoped cleanup still runs. `Mail`, `Realtime` (#7359) and `Owner.logs` (#7434) integration tests now share it instead of carrying three copies; each keeps its own stall message. ## ref spotted in: - https://github.com/supabase/cli/actions/runs/39920135558/job/120303098411 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude <noreply@anthropic.com>
This adds postgres 17.11.0.017-orioledb-r0 as a new release line. https://github.com/supabase/slim-services/releases/tag/postgres-17.11.0.017-orioledb-r0 Planned after supabase/slim-services published 17.11.0.017-orioledb-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.89.1-r0 to v1.89.2-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.89.2-r0 Planned after supabase/slim-services published v1.89.2-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.151.3-r0 to v2.151.4-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.151.4-r0 Planned after supabase/slim-services published v2.151.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>
## TL;DR folds the effect lint into `pnpm lint:check`, so one `.oxlintrc.json` drives editors, agents and CI ## whats introduced? - `.oxlintrc.json` extends the `@effect/tsgo` recommended preset, and `.oxlintrc.effect.json` and the `lint:effect:*` scripts go away - `.github/scripts`, the npm launcher shim, compute stack templates and the generated API contracts move to `ignorePatterns`, so they leave the repo lint entirely - `lint:fix` applies suggestions, and `fix:all` runs knip, lint and format in order so fixes never race ## ref - closes: CLI-2899 & CLI-2407
… (#7394) ## TL;DR adds `SUPABASE_API_MAX_RETRIES` so automation can cap management api retries, where `0` sends each request once. ## whats introduced? the new env var sets the retry count on every management api client the cli builds login and `services` included. `functions deploy` caps its own retry loops at that value and keeps honouring rate limit headers, and a duplicated create still becomes an update even at `0`. an invalid value stops the command before any request, and leaving it unset keeps today's behavior... ```mermaid flowchart TD env["SUPABASE_API_MAX_RETRIES"] --> parse{"CommandSettings.load"} parse -- "unset or empty" --> none["none: today's behaviour"] parse -- "plain digits, e.g. 0 or 2" --> some["some(n)"] parse -- "anything else" --> err["ApiMaxRetriesError, exit 1, 0 requests"] none --> defaults["client retries 5 times, deploy keeps its own loops"] some --> clients["Management API clients: retry.maxRetries = n"] clients --> platform["platform api: projects, secrets, link, ..."] clients --> login["login profile lookup"] clients --> services["services version lookup"] some --> deploy["functions deploy: apiMaxRetries"] deploy --> single["command loops capped, client retries off"] single --> dup["duplicated create still becomes one update"] ``` ## why? the management api client retries retryable failures up to 5 times with no way to change that, so eg: a failing `projects list` sends 6 requests, automation that has to stop on the first failure had no supported way to send each request once.... ## ref - closes: #6958
This bumps realtime from v2.151.4-r0 to v2.151.5-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.151.5-r0 Planned after supabase/slim-services published v2.151.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.151.5-r0 to v2.151.6-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.151.6-r0 Planned after supabase/slim-services published v2.151.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>
This bumps postgres from 17.11.0.017-r0 to 17.11.0.018-r0. https://github.com/supabase/slim-services/releases/tag/postgres-17.11.0.018-r0 Planned after supabase/slim-services published 17.11.0.018-orioledb-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 adds postgres 17.11.0.018-orioledb-r0 as a new release line. https://github.com/supabase/slim-services/releases/tag/postgres-17.11.0.018-orioledb-r0 Planned after supabase/slim-services published 17.11.0.018-orioledb-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.017-r0 to 15.19.0.018-r0. https://github.com/supabase/slim-services/releases/tag/postgres-15.19.0.018-r0 Planned after supabase/slim-services published 17.11.0.018-orioledb-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 Adds `supabase inspect db toast-sizes` — a new `inspect db` subcommand that lists user tables with TOAST storage, ordered by TOAST size (largest first). ## What changed - `apps/cli/src/commands/inspect/db/toast-sizes/` — 3-file subcommand (query, handler, command) - `apps/cli/docs/supabase/inspect/db-toast-sizes.md` — docs overlay with description and sample output - `apps/cli/src/commands/inspect/db/db.command.ts` — registers the new subcommand - `apps/cli/src/commands/inspect/db/SIDE_EFFECTS.md` — updates active count (13 → 14) and query table - `apps/cli/src/commands/inspect/db/inspect-specs.integration.test.ts` — adds a test case for `toastSizesSpec` ## Why it matters TOAST (The Oversized-Attribute Storage Technique) is PostgreSQL's overflow storage for large values in `TEXT`, `JSONB`, and `bytea` columns. Autovacuum runs on TOAST tables **independently** from the main heap, so dead TOAST chunks accumulate silently. A high TOAST dead-tuple ratio increases I/O and inflates effective database size — but it doesn't appear in the existing `bloat` or `vacuum-stats` commands, which only cover the main heap. This command surfaces: - Heap vs. TOAST size split per table - Live/dead TOAST chunk counts and dead-chunk percentage - Last autovacuum/vacuum timestamps on the TOAST table It's a common source of invisible bloat, especially in tables with large JSONB columns. ## Testing - New integration test case in `inspect-specs.integration.test.ts` (count updated 13 → 14) - Query filters out internal Supabase schemas via the shared `$1` LIKE-escape parameter (consistent with `bloat`, `vacuum-stats`, `index-stats`, `table-stats`, `db-stats`) - Docs overlay at `apps/cli/docs/supabase/inspect/db-toast-sizes.md` follows the same format as existing overlays ## Docs A follow-up PR to `supabase/supabase` will add this command to the [Inspect guide](https://supabase.com/docs/guides/observability/inspect) under the **Disk Storage Analysis** section after this merges. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: avallete <andrew.valleteau@supabase.io>
…LI-2860, CLI-2861) (#7488) ## Summary The CLI now reads and writes `supabase/config.jsonc`, and treats `config.json` the same way, so comments and trailing commas work and survive every write the CLI makes to the config. Reading: - The loader looks for `config.json`, `config.jsonc` and `config.toml` in `supabase/`. `config.jsonc` reports `format: "json"`, so everything that already handles a JSON config treats it the same way - Both JSON files go through `jsonc-parser`. A syntax error now names its position, e.g. `Comma expected (line 4, column 3)`, instead of the runtime's `JSON.parse` message - The CLI warns once per command when a JSON config shadows `config.toml`: `Ignoring supabase/config.toml because supabase/config.json takes precedence. Delete the file you don't use.` - The two comment strippers the CLI had (VS Code settings in `init`, Deno import maps in `functions deploy`) use the same parser now, and the experimental-feature reader and the upgrade notice's project walk-up know about `config.jsonc` Writing: - `config pull` edits a JSON config in place through `jsonc-parser`'s `modify` instead of re-serialising the whole file, so comments, trailing commas and untouched values stay as they are. A new key goes after the previous property's whole line, so a trailing comment stays with its own key. ADR 0023 has an addendum for the new JSON strategy - `saveCliConfig` applies the same minimal edits when it saves over an existing `config.json`/`config.jsonc`, and removes a dropped key by its whole line. Removing the last key of an object also drops the comma before it, so a `config.json` stays strict JSON for older CLIs. If the edited text doesn't re-parse to exactly the saved config, it falls back to writing the regenerated file. A TOML save keeps the file's `#:schema` line - `saveCliConfig` writes back to `config.jsonc` when that's the project's JSON file, and still removes the other config files after the write - `compute new` (experimental) records the new compute in a JSON config through the same editor, instead of refusing - `functions new` and the declarative `db pull` write their `functions.<slug>` and `db.migrations.schema_paths` entries into a JSON config through the same editor. Before, both appended TOML to a new `config.toml` that the JSON file shadowed, so the setting never applied - `init` treats an existing `config.json`, `config.jsonc` or `config.toml` as an existing config and names that file when it refuses. `init --force` and a blank `bootstrap` remove `config.json`/`config.jsonc` when they write the fresh `config.toml`, so the new template is the file that gets read Messages: - Errors, hints and suggestions that named `supabase/config.toml` or gave TOML syntax now name the project's own config file and write the key in its syntax. A TOML project still sees `[db] major_version = 17`, and a JSON project sees `` `db.major_version` to `17` in supabase/config.jsonc ``. Where the command has no config loaded, the text says "your config file" or lists all three file names - Command help says "the project config file" and uses dotted keys such as `db.seed.sql_paths` - `db-config.toml-read.ts` is renamed to `db-config.read.ts`, since it reads every format `jsonc-parser` is a new runtime dependency of `@supabase/config` (MIT, no dependencies of its own). Its `parse()` turns a `__proto__` key into the object's prototype, so `parseJsonc` builds values through its `visit` API with own-property assignment instead. ## Behaviour changes for existing projects - A `config.jsonc` next to `config.toml` now takes over from it. Before, `config.jsonc` was never read. The new warning names `config.toml` as ignored, so the switch shouldn't go unnoticed - `config.json` and `config.jsonc` in the same `supabase/` directory is now an error: `Found both supabase/config.json and supabase/config.jsonc. Keep one and delete the other.` Before, `config.jsonc` was ignored - Projects that already had `config.json` next to `config.toml` now get the warning above. `config.json` already won there, so which file is read doesn't change - `config pull` and `saveCliConfig` on a `config.json` now only change what they write. Before, both rewrote the whole file - The stack's advice to pin a service version now reads ``pin `local.services.<service>.version` to "<version>" in your config file`` for every project, including TOML ones - Errors for a missing project config name `supabase/config.toml, config.json or config.jsonc` instead of only `config.toml` - `init` in a project with only a `config.json`/`config.jsonc` now reports the config as already there. Before, it wrote a `config.toml` that the JSON file then shadowed. `init --force` in such a project now removes the JSON file ## Linked issue Closes CLI-2859 Closes CLI-2860 Closes CLI-2861 🤖 Generated with [Claude Code](https://claude.com/claude-code)
#7510) ## TL;DR Two `functions deploy` failures were reported to telemetry as internal bugs even though both are typed failures with a known cause. A multi-function API deploy now fails with `FunctionsDeployFailedError`, classified by the failure it wraps. A malformed `deno.json` or import map now counts as invalid config. ## What changed - `deployViaApi` collects each function's upload failure, plus the bulk update failure, into a `FunctionsDeployFailedError` instead of a native `AggregateError`. - It extends `AggregateError`, so `errors`, the joined message and `instanceof AggregateError` are unchanged. - Its classification comes from its most severe cause, ranked internal bug, then unclassified, then external service, then user-actionable, so a user error never masks a CLI or upstream failure. For example, a deploy whose uploads all fail with 401 becomes `error:FunctionsDeployFailedError:auth` (`user_actionable`). With no causes it falls back to `unknown`, not `internal_bug`. - `FunctionImportMapSyntaxError` gets the `invalidConfig` classification and its own fingerprint, `error:FunctionImportMapSyntaxError`. It still reports `name: "SyntaxError"`. ## Behaviour change Telemetry only. These failures stop counting as internal bugs and are bucketed by their real cause. Messages, exit codes and JSON `error.code` (`UnknownError`) are unchanged. ## Linked issue None (Linear CLI-2910)
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> Co-authored-by: Julien Goux <hi@jgoux.dev>
This bumps realtime from v2.153.3-r0 to v2.153.4-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.153.4-r0 Planned after supabase/slim-services published v2.153.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>
…7515) ## Problem PostgREST derives a view's relationships by following views in the exposed schemas **and** in `db-extra-search-path`. `gen types` only followed the generated schemas. When a view chain passed through an extra search path schema, its relationships were lost, so the types rejected embeds that work at runtime. ```sql create schema api; create schema extra; -- [api].extra_search_path = ["extra"] create table api.accounts (id int primary key); create table private.invoices ( id int primary key, account_id int not null references api.accounts (id) ); create view extra.invoices as select * from private.invoices; create view api.invoices as select * from extra.invoices; ``` ```mermaid flowchart LR V["api.invoices<br/>(generated)"] --> E["extra.invoices<br/>(extra search path)"] --> T["private.invoices<br/>(FK → api.accounts)"] ``` `supabase gen types --schema api`, `Views.invoices.Relationships`: ```ts // before Relationships: [] // after Relationships: [ { foreignKeyName: "invoices_account_id_fkey" columns: ["account_id"] referencedRelation: "accounts" referencedColumns: ["id"] }, ] ``` ## Change - Bump `@supabase/typegen` to 0.3.2 (postgrest-typegen 0.7.0, supabase/sdk#329), which takes an `extraSearchPath` option. - Every path that introspects the database passes `[api].extra_search_path`: `--local`, `--remote=<db-url>`, and remote projects or branches with a non-TypeScript `--lang`. For a project ref, the matching `[remotes]` block applies, like it does for `[api].schemas`. - The setting is read even with `--schema`, because it describes the target's PostgREST, not which schemas to generate. Without a `config.toml`, the default `public, extensions` applies. - Nothing in the extra search path schemas is generated unless `--schema` or `[api].schemas` names it. - Linked TypeScript generation runs on the Management API and is unchanged.
## TL;DR
stops realtime, analytics and the pooler idling at over 2 GB each when
containerd's `LimitNOFILE=infinity` reaches the containers
## what's hurting the user(s)?
on Linux hosts where containerd runs with `LimitNOFILE=infinity` and a
high `fs.nr_open`, containers inherit an open file limit of about 1B
unless a ulimit is set
the erlang vm in those services sizes its port table from that limit
together they take about 6 GB more at idle on a 10 GB machine, and one
run there hit 24 OOM kills in about 6 minutes
## now fixed by?
the legacy backend and the experimental stack now set
`ERL_MAX_PORTS=65536`, the erlang default, on those containers without
touching their ulimit
on those hosts they now idle at about 170 to 550 MB each with no OOM
kills
```mermaid
flowchart LR
subgraph before["before"]
B1["nofile ~1B<br/>port table ~134M<br/>about 2.3 to 2.7 GB"]:::t
B2["nofile ~1M<br/>port table ~1M<br/>about 180 to 630 MB"]:::t
B3["nofile 1024<br/>port table 64K<br/>about 170 to 610 MB"]:::t
end
F{{"fix:<br/>ERL_MAX_PORTS=65536"}}:::t
subgraph now["now"]
A1["nofile ~1B<br/>port table 64K<br/>about 170 to 550 MB"]:::t
A2["nofile ~1M<br/>port table 64K<br/>about 170 to 560 MB"]:::t
A3["nofile 1024<br/>port table 64K<br/>about 170 to 550 MB"]:::t
end
B1 -.-> F
B2 --> F
B3 --> F
F --> A1
F --> A2
F --> A3
classDef t fill:transparent
style before fill:transparent
style now fill:transparent
```
## ref:
- closes: #7503
…7513) ## TL;DR adds a consumer contract suite that runs the same `supabase-js` cases against legacy, `stack/container` and `stack/native` ## whats introduced? `tests/contract/cases.ts` holds cases for PostgREST, Auth with Mailpit confirmation, Storage, Realtime and Functions with `verify_jwt` each case runs unchanged against one real stack per setup in the normal integration job using the keys and urls `start` reports a new golden path in `start.e2e.test.ts` edits `auth.site_url` then stops and starts and checks Auth picks up the new value ## why? nothing proved the stack and legacy behave the same for a real client so parity fixes had no shared place to add the case they make pass ## ref: - closes: CLI-2574
) The tokenless `gen types` e2e asserts the output never mentions the offline profile's `http://127.0.0.1:1` API URL with a plain substring check. The CLI prints `Target: local stack (127.0.0.1:<dbPort>)`, so whenever the randomly allocated DB port starts with `1` (e.g. `1191`) the assertion fails even though no remote call happened. Seen on #7523. The assertion now matches `127.0.0.1:1` only when no further port digit follows.
## Summary An unset `sslmode` (libpq's default, `prefer`) and an explicit `sslmode=prefer` were TLS-only. That broke any remote Postgres or Supavisor without TLS, such as a self-hosted stack, even though `psql` connects to the same URL fine: ``` $ supabase db push --db-url "postgres://postgres:…@db.internal:5432/postgres" failed to connect to postgres: failed to connect to `host=db.internal user=postgres database=postgres`: tls error (The server does not support SSL connections) ``` `prefer` and unset now follow libpq: try TLS first, then retry the same host in plaintext. | `sslmode` | before | after | | --------------------------------- | --------------- | --------------- | | `disable` | plaintext | plaintext | | `allow` | plaintext → TLS | plaintext → TLS | | `prefer` / unset | TLS only | TLS → plaintext | | `require`, `verify-ca`, `verify-full` | TLS only | TLS only | Local targets (the running project stack) still connect in plaintext without a TLS attempt. ### When the plaintext retry runs The retry is limited to failures where the server could not do TLS. Unreachable hosts and auth errors are not dialed again in plaintext. ```mermaid flowchart TD T[TLS attempt] -->|connected| OK[use connection] T -->|"server answered N to SSLRequest,<br/>or the TLS handshake failed"| P[plaintext attempt, same host] T -->|"28000: pg_hba has no TLS entry<br/>(hostnossl only)"| P T -->|"28P01 / 3D000 / 42501<br/>(wrong password, missing database, …)"| F[fail] T -->|"anything else<br/>(unreachable, timeout, …)"| N[next host, if any] P -->|connected| OK P -->|"failed (not an auth error)"| N ``` For a 28000 rejection the retry matches libpq, which reconnects without TLS when the server rejects a `prefer` TLS session. `require` and stricter modes have no plaintext attempt, so they still fail on these errors, with the existing `sslmode=disable` hint. Fixes #4142
This bumps storage from v1.89.12-r0 to v1.89.13-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.89.13-r0 Planned after supabase/slim-services published v1.89.13-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> Co-authored-by: Vaibhav <117663341+7ttp@users.noreply.github.com> Co-authored-by: Julien Goux <hi@jgoux.dev>
…API fallback (#7520) ## Summary `supabase gen types --lang typescript` for a remote project takes its types from `GET /v1/projects/{ref}/types/typescript`. That endpoint ignores `[api].extra_search_path` from `config.toml` and runs the server's generator, so remote TypeScript differs from local output and from every other language. TypeScript for the linked project now generates from the database like the other languages, with `[api].extra_search_path` applied, when it can match the API's output without side effects: - the ref is the linked project, and `link` recorded its PostgREST version in `supabase/.temp/rest-version`. The version goes to typegen as `postgrest-version`, so the output keeps `__InternalSupabase.PostgrestVersion`, which supabase-js needs for `.maxAffected()` and to-many spreads; - a database password resolves (`SUPABASE_DB_PASSWORD`). Without one the CLI would mint `cli_login_postgres`, which resets that role's password under any concurrent command. Otherwise the command uses the types endpoint exactly as before: no warning, no extra calls. When an eligible direct run fails before introspection starts, the command falls back to the types endpoint. That covers config loading, the project or branch lookup (it needs `projects:read`, which the types endpoint doesn't), the libpq `PG*` environment, connection and pooler resolution, and dial, TLS or authentication failures. The fallback prints a yellow `Warning:` on stderr, so stdout stays a clean types file. The warning names the failure, says the API types don't apply `[api].extra_search_path`, adds the error's own suggestion when it has one, and says the fallback goes away in the next major. Generator failures, where the database answered, still fail. Other languages are unchanged. **Release note:** direct output is the CLI generator's, the same as `--local` and `--db-url`. It is not run through a formatter, so its layout differs from the Management API output. ## Verification - `bun --bun vitest run --project integration src/commands/gen/types/types.integration.test.ts`: 165 passed - `bun --bun vitest run src/tests/gen.e2e.test.ts -t "gen:types"` (replay): 6 passed, 6 skipped - `pnpm run types:check`, oxlint, oxfmt.
…ds (#7525) ## TL;DR Two Podman stack integration tests failed when GitHub Releases was slow or returned 5xx. - `Database.integration.test.ts › restarts initialized data on a new release revision of its build and refuses another build`: failed with `SchemaError: Expected a valid JSON string`. The test fetched the published manifest from GitHub and decoded the body without checking the status. It now rejects non-2xx responses, which names the URL and status. It also falls back to the S3 mirror the stack itself uses and checks the body against the published manifest pin. - `Functions.integration.test.ts › service catalog › destroy survives a symlink planted where its runtime directory used to be`: timed out at 30s. A cold edge-runtime download kept getting `HTTP 504` from GitHub before it fell back to S3. The download now runs in a `beforeAll` with its own 120s guard, so the test only measures `prepare` and `removeData` against a warm cache. ## ref spotted in: - https://github.com/supabase/cli/actions/runs/42045749730/job/127308085819 - https://github.com/supabase/cli/actions/runs/42056253334/job/127345322962 --- _Generated by [Claude Code](https://claude.ai/code/session_01MBEk1KNTZbCp5HjsBksymT)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
) ## Summary Only `gen types` checked hosted database servers against the bundled Supabase root CA. Every other command that connects to a linked project used `sslmode` unset, which means no certificate verification. That covers `db push/pull/dump/diff/reset/lint/advisors/query`, `migration *` and `inspect *`. The shared linked resolver now pins the CA. It uses `sslmode=require` plus the inline bundle, which the driver verifies as `verify-ca`. **Where the pin applies:** - the direct `db.<ref>` host; - the pooler URL (saved or from the Management API); - the temp-role probe, which previously connected before any pin; - the container pooler fallback; - the `bootstrap` push when it falls back to the direct host. **When it applies:** - **Profiles:** only `supabase`, `supabase-staging` and `snap`, and only on that profile's own direct-host or pooler domains. `supabase-local` and YAML profiles are never pinned. - **Explicit settings win:** an explicit `sslmode` from any source (DSN, `pg_service.conf`, `PGSSLMODE`) is used as is, including `disable` and `verify-full`. A root CA set without `sslmode` (`PGSSLROOTCERT`, DSN or service file) replaces the bundled CA and keeps `require`, so the chain is verified against it. `gen types --db-url` honors the DSN as is. - **Not covered:** tools the CLI spawns that build their own connection, such as `pg_prove` in `test db` and the pgAdmin and migra fallbacks in `db diff`. **Behavior changes:** - Linked commands on the built-in profiles now verify the server certificate chain. They already used TLS but accepted any certificate, so a man-in-the-middle could read the database password; `gen types` already verified it. A certificate that does not chain to the Supabase CA, for example from a TLS-terminating proxy in front of Postgres, now fails. Set `PGSSLROOTCERT` to a CA bundle that trusts it, or `PGSSLMODE=require` to skip verification as before. - The temp-role probe stops at once on a rejected certificate (suggesting the settings above instead of `SUPABASE_DB_PASSWORD`) or when network restrictions reject the caller's IP (`Address not in tenant allow_list`, with a link to the dashboard's database settings), instead of retrying for about 2.5 minutes. Auth and dial failures keep retrying, since a network ban shows up as a dial failure. - `gen types` on a linked project or preview branch no longer overrides an explicit `PGSSLMODE`/`PGSSLROOTCERT`. - `db dump` passes `PGSSLMODE=require` to `pg_dump`, without the inline CA, so the dump requires TLS but does not verify the chain. This is documented in its `SIDE_EFFECTS.md`. **Cleanup:** the CA templates move to `command-internal/`, and migra's duplicate `PG_DELTA_CA_BUNDLE` now uses the same bundle. The pooler certificates on `aws-0-us-east-1.pooler.supabase.com`, `aws-1-eu-west-1.pooler.supabase.com` and `aws-0-us-east-1.pooler.snapcloud.co` chain to `Supabase Root 2021 CA` in the bundle (`openssl s_client -starttls postgres`). For direct hosts, `gen types` already pinned them unconditionally. ## Verification - `bun --bun vitest run --project integration` on `db-config.integration.test.ts`, `gen/types/types.integration.test.ts` and `db/diff`: 417 passed. New tests cover: - the direct host on each pinned profile; - the pooler; - the temp-role probe; - explicit `verify-full`, `disable`, `PGSSLROOTCERT`, the service file and the pooler URL `sslmode`; - `supabase-local`, a YAML profile and a foreign host; - the temp-role probe failing fast on a rejected certificate while auth failures and bans still retry; - host matching across case, a trailing dot and lookalike domains. - After the review fixes: the same integration files plus `bootstrap`: 471 passed; `supabase-tls` and `connect-errors` unit tests: 69 passed. The `bootstrap` direct-host fallback test fails without the pin. - `pnpm run types:check`, `oxfmt` and `oxlint` are clean.
## TL;DR adds `start` and `db reset` ordering cases to the contract suite for migrations, seeds, config buckets and service schemas the audit found no ordering difference between backends, only the already documented stack `db reset` warning on a rejected bucket upload ## ref: - closes: CLI-2581
| * which on a hosted project is the bundled Supabase CA unless the user chose a trust setting. | ||
| */ | ||
| export const SUGGEST_CA_VERIFICATION = | ||
| "The server certificate did not verify against the trusted CA (the bundled Supabase CA on a hosted project). Behind a TLS-inspecting proxy, set PGSSLROOTCERT to a CA bundle that trusts it, or set PGSSLMODE=require to skip certificate verification."; |
There was a problem hiding this comment.
⚪ Severity: LOW
When a TLS certificate fails verification, this new remediation tells users to set PGSSLMODE=require “to skip certificate verification.” A user following it makes later remote connections accept any certificate; an on-path attacker can then impersonate the database and capture credentials or alter SQL, defeating the CLI’s server-identity guarantee.
Helpful? Add 👍 / 👎
💡 Fix Suggestion
Suggestion: Remove the advice to set PGSSLMODE=require, because it disables certificate verification. Tell users to trust the proxy's CA with PGSSLROOTCERT, or to fix the certificate chain. Do not suggest any option that skips server identity verification.
⚠️ Experimental Feature: This code suggestion is automatically generated. Please review carefully.
| "The server certificate did not verify against the trusted CA (the bundled Supabase CA on a hosted project). Behind a TLS-inspecting proxy, set PGSSLROOTCERT to a CA bundle that trusts it, or set PGSSLMODE=require to skip certificate verification."; | |
| "The server certificate did not verify against the trusted CA (the bundled Supabase CA on a hosted project). Behind a TLS-inspecting proxy, set PGSSLROOTCERT to a CA bundle that includes the proxy's CA so the certificate can be verified. Otherwise, check that the connection is not being intercepted."; |
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> Co-authored-by: Julien Goux <hi@jgoux.dev>
## Problem Compute and local stacks have separate log commands. Hosted logs have no general SQL command. Users need one way to find, filter, and inspect logs across products. ## Solution Add `supabase logs` in CLI v3: an interactive log explorer for people and structured output for scripts and agents. `L load more` fetches logs up to now in bounded batches. Custom SQL queries hosted logs. `logs sources` lists available services and filters. Keep `compute logs` and `stack logs` working with their existing flags, defaults, and output. Both warn on stderr that they will be removed in v4 and show the replacement command. Removal is tracked in [CLI-2921](https://linear.app/supabase/issue/CLI-2921). This is an additive 3.x change; older CLI versions are out of scope. The old commands keep their defaults: `compute logs` reads up to 100 records from 24 hours; `stack logs` reads up to 200 retained lines with no default time filter. Neither follows unless `--follow` is passed. Existing structured output formats remain supported. ### `supabase logs` Read the latest 100 matching logs from the last hour. In a terminal, open the viewer; when piped or using structured output, read once and exit. | Option | Behavior | | --- | --- | | No target flag | Use local logs. | | `--remote` | Use the linked project. | | `--remote=<ref>` / `--remote=<branch>` | Use another project or a branch of the linked project. | | `--stack <name>` / `--stack-id <id-or-prefix>` | Select an existing local stack; rejected remotely. | | `--profile <name>`, `--workdir <path>` | Use the usual CLI login profile and working-directory rules. | | `--service <name>` | Repeat for multiple services; omit for all available services. | | `--resource <id-or-name>` | Remote: Compute name or function ID, with one matching service. Local: instance ID; cannot combine with `--service`. | | `--kind app`, `requests`, or `builds` | Requires `--service compute`. | | `--level <level>` | Repeatable: `error`, `warn`/`warning`, `info`, `debug`, `log`, `notice`. Without `--service`, searches services with reliable severity; Compute requires an explicit service and kind. | | `--search <text>` | Match literal text in remote messages, ignoring case. | | `--since <duration-or-UTC-timestamp>` | Default: last hour. Local also accepts `start` for each service's latest launch. | | `--from <UTC-timestamp>`, `--to <UTC-timestamp>` | Explicit bounds ending in `Z`; cannot combine with `--since`. `--to` alone selects the preceding hour. | | `--limit <count>` | Initial filtered history count: 1–1000, default 100. Remote load more reads up to five pages per press and resumes remaining pages on the next press. | | `--sql <query>`, `--file <path>`, or `--file -` | Run custom hosted-log SQL; requires `--remote`. SQL controls its own ordering and limit. | | `--output-format text` | Explicit text reads once and exits, including in a terminal. Omit the flag for the viewer. | | `--output-format json` | One indented array of complete records. | | `--output-format stream-json` | One JSON event per line: `log-entry`/`log-marker` for filtered logs, `result` for SQL/sources. Complete log records are included. | `--local` remains the shared hidden alias. Use `--output-format`; `-o/--output` is rejected, though shared help still lists it for other commands. The new command has no `query` or `tail` subcommand, `--tail`, or `--no-follow` option. ```sh supabase logs --service postgres supabase logs --remote --service postgres --level error supabase logs --remote --service auth --search "invalid token" --since 30m supabase logs --remote --service compute --resource api --kind requests supabase logs --remote --service functions --resource <function-id> --output-format json ``` ### Interactive viewer The header shows the project name and ref (or local stack), active filters, and requested window. Project-name lookup falls back to the ref after two seconds or an error. The list shows UTC time, service, and message. Timestamps are dimmed; the selected row has a full-width background. `NO_COLOR` keeps a plain selection marker. The fixed `Window:` header shows the requested bounds, even if only part of the matching history is loaded. - ↑/↓ selects a row. The list scrolls only when selection crosses an edge. - Enter opens full details; ↑/↓ scrolls them; Esc returns to the list. - `C` copies the full record as formatted JSON, including nested metadata. - `L load more` appends filtered logs without moving the selection or viewport. Remote loads read up to five pages at a time. When more pages remain, the header says `more pending`; the next press resumes the same requested window before a later press starts a new one. There is no automatic polling, and repeated presses during a load start no extra request. - With custom SQL, `L` reruns the same query through the current time and replaces its results. SQL can return aggregates or choose its own ordering, so its results cannot be appended safely. - `q` quits; Ctrl-C exits with code 130. Both cancel an active load and restore the terminal. Successful loads report `X new logs loaded.`, with `1 new log loaded.` for one record and `0 new logs loaded.` for an empty load. The count is for that load, including records discarded by the display limit. SQL refreshes show `Query reloaded.` instead. A failed load opens a full, scrollable error panel and keeps the current records and window. ↑/↓ scrolls the error, Esc returns to the previous view, and `L` retries. Copy is disabled while the error panel is open. Empty results stay open so later logs can be loaded. Copy feedback also appears in the footer; the history-limit warning is removed. Filtered history is limited to 1000 displayed rows and approximately 8 MiB, with a visible discarded-row count. A single oversized record stays complete. Selection is preserved while its row remains retained; open details remain pinned. SQL keeps all returned rows. Select `timestamp` or `ts_ms` to show time; missing or unsupported timestamps show `—`. ### Output for scripts and agents Explicit `--output-format text`, `json`, or `stream-json` reads once and exits. Without the flag, the viewer opens only with terminal stdin/stdout and interaction enabled. Pipes, CI, and `TERM=dumb` use finite output. No `--no-interactive` flag is needed. ```sh supabase logs --remote --service postgres --output-format json supabase logs --remote --sql "select timestamp, source, event_message from logs order by timestamp desc limit 10" --output-format json supabase logs --remote --output-format text ``` Piped filtered text keeps one event per line without wrapping; embedded newlines are escaped. Progress and retry messages stay on stderr. Help includes copyable examples and explains how to skip the viewer. The same output rules apply to SQL from `--file <path>` or `--file -`. `stream-json` describes the output format; it does not follow logs or poll for updates. ### Custom SQL SQL queries hosted logs through `GET /v1/projects/{ref}/analytics/endpoints/logs`. It does not query the project's Postgres database. Supply one nonempty input: `--sql`, `--file <path>`, or `--file -` for stdin. `--sql` and `--file` cannot be combined. Other invocations leave stdin untouched. The new command has no `-f` alias; the legacy commands retain `-f` for follow. SQL cannot be combined with service, resource, kind, level, search, or stack filters. Known invalid combinations and hosted time ranges fail before reading SQL input or resolving credentials. ```sh supabase logs --remote --sql "select timestamp, source, id, event_message, log_attributes from logs limit 20" supabase logs --remote --file errors.sql --since 30m --output-format json cat errors.sql | supabase logs --remote --file - --output-format stream-json ``` ### `logs sources` Show a compact service list. `--service <name>` shows SQL, supported filters, levels, and log kinds as readable fields. JSON and stream-json always include all details. Remote results describe support, not whether logs exist. Local results list saved service instances and gateway availability. ```sh supabase logs sources supabase logs sources --remote supabase logs sources --remote --service postgres --output-format json ``` Remote services: `edge`, `postgres`, `postgrest`, `auth`, `auth-audit`, `storage`, `realtime`, `functions`, `function-edge`, `supavisor`, `pgbouncer`, `pg-upgrade`, and `compute` when enabled. Local `postgres` selects `database`. ### Limits - Local logs require `SUPABASE_EXPERIMENTAL_STACK=1` or `experimental.stack = true` in config. Use `--remote` for hosted logs. Reading local logs never starts services and works while stopped. Local SQL, level, search, and kind filters are rejected. - Compute must be enabled. Its app logs have no reliable level; level filters require request or build logs. - Local time ranges have no 24-hour ceiling. Hosted requests are limited to 24-hour windows. Refresh errors if its window exceeds that limit; restart with a narrower range. Existing retention and access rules apply. - Remote refresh shows page progress and retries a rate-limited page once, displaying the wait duration and UTC retry time. It waits at least 60 seconds or the server's longer `Retry-After`. Quitting cancels the wait. Refresh rereads a short overlap and removes duplicate IDs. Logs arriving outside that overlap can be missed. An excessive overlap stops with a retryable error rather than silently dropping rows. - Long SQL files can exceed the API's GET URL limit. ## Earlier QA These results describe the earlier query/tail revision, before the single-command viewer and manual refresh. Tested the compiled CLI with its saved staging login against `supabase.green`: | Test | Result | | --- | --- | | SQL inputs and output | Argument, file, and stdin worked in text, JSON, and stream-json. Sources worked in all three formats. | | Filters and errors | Postgres search returned only the generated marker. Earlier runs covered repeated services, levels, and function IDs. Invalid SQL and credentials returned errors and nonzero exits. | | Sustained follow | All 600 generated Postgres records arrived once, with no duplicate IDs or malformed JSON events. Ctrl-C returned 130. | | Late arrivals | The same fixed time window returned 568, then 600 records. Tail received all 32 late arrivals. This does not prove delivery outside the reread window. | | Overflow | SQL confirmed all 1100 generated records. Tail stopped with a nonzero exit and the explicit 1000-row error. | | Compute | App, request, and build results matched SQL record IDs and resource/kind filters. A GET to the staging example app returned HTTP 200 and produced the request log. | | Existing commands | Old and new Compute commands returned matching app, request, and build logs on staging. Each old invocation warned once on stderr and kept stdout valid JSON. Both old commands appear in help and completion. | <details> <summary>Automated tests and fixes found during QA</summary> Compatibility tests cover the original defaults, stopped-stack history, and clean JSON/YAML/TOML output with one stderr warning. Deliberately moving the warning to stdout or changing the default count made the tests fail. Coverage includes SQL inputs and time bounds; local/remote selection; Compute settings; rejected filter combinations; JSON and streaming output; correlation fields; saved launch, line, and lost-output records; resume positions; stopped stacks; duplicate removal; retries, permanent errors, and cancellation. Tests passed repeated runs with shuffled order and retries disabled. Temporarily breaking Compute discovery, the JSON-follow guard, correlation data, and local stream data made tests fail. The last check exposed a weak assertion, which was strengthened. The native-stack end-to-end test checks history and live follow across restart, stopped-stack history, refusal to follow a stopped stack, database preservation, and cleanup. It found missing backend selection for local logs. The fix selects the configured backend for local tail/sources while leaving query remote-only; regression tests failed before the fix and passed after it. Two tests timed out during an earlier broad local run under concurrent load; both passed repeated focused runs. The existing stack tests also cover the new command, keeping their original behavior checks. </details> ## Run locally <details> <summary>Build the PR and test hosted, Compute, and local logs</summary> For macOS/Linux. Requires GitHub CLI and [mise](https://mise.jdx.dev/) installed and activated. **1. Check out and build the PR.** ```sh gh repo clone supabase/cli cli-logs-test cd cli-logs-test gh pr checkout 7437 mise trust mise install pnpm install pnpm exec turbo run supabase#build export CLI_BIN="$PWD/apps/cli/dist/supabase" ``` Use `"$CLI_BIN"` below to run this PR's build. **2. Log in to staging.** Replace the project ref with a project you can access on `supabase.green`. Approve macOS Keychain access if prompted. ```sh export SUPABASE_PROFILE=supabase-staging export PROJECT_REF="your-staging-project-ref" "$CLI_BIN" login ``` **3. Browse logs.** Press `L` to load more, Enter for details, `C` to copy, and `q` to quit. ```sh "$CLI_BIN" logs --help "$CLI_BIN" logs sources --help "$CLI_BIN" logs sources --remote="$PROJECT_REF" "$CLI_BIN" logs --remote="$PROJECT_REF" --service postgres --since 30m ``` **Custom SQL and a finite JSON export:** ```sh "$CLI_BIN" logs --remote="$PROJECT_REF" \ --sql "select timestamp, id, event_message, log_attributes from logs limit 10" "$CLI_BIN" logs --remote="$PROJECT_REF" --service postgres --output-format json ``` With SQL, `L` reruns the query and replaces the results. JSON and stream-json output read once and exit. Empty interactive results remain open so `L` can fetch later logs. **Compute:** enable the feature and replace `api` with an existing deployment name. ```sh export SUPABASE_EXPERIMENTAL_COMPUTE=true "$CLI_BIN" logs --remote="$PROJECT_REF" --service compute --resource api --kind requests "$CLI_BIN" compute logs api --remote="$PROJECT_REF" --kind requests -o json ``` Both should work. The old command prints one v4 deprecation warning to stderr and keeps stdout valid JSON. Its existing `--follow` mode remains available in v3. **Local logs:** use an existing test project with the stack backend. ```sh cd /path/to/test-project export SUPABASE_EXPERIMENTAL_STACK=1 "$CLI_BIN" logs sources "$CLI_BIN" logs --service postgres "$CLI_BIN" stack logs --service database ``` Local logs work while the stack is stopped. The old `stack logs` command still works and shows the v4 warning. When reporting a problem, include the command, expected result, actual output, and whether you tested local or remote. Remove credentials and private log content. </details> ## Screenshots Historical captures from the earlier query/tail revision. They predate the single-command viewer, manual refresh, and output readability fixes; their command syntax is no longer current. Captions remain outside the images. <details> <summary>supabase logs --help</summary>  </details> <details> <summary>supabase logs query --help</summary>  </details> <details> <summary>supabase logs tail --help</summary>  </details> <details> <summary>supabase logs sources --help</summary>  </details> ### CLI results Staging query and tail results, using the saved profile. The fixed SQL value shows the output formats without exposing project data. <details> <summary>Hosted SQL query, text output</summary> `supabase logs query "select 731 as qa_value" --remote --output-format text`  </details> <details> <summary>Hosted SQL query, JSON output</summary> `supabase logs query "select 731 as qa_value" --remote --output-format json`  </details> <details> <summary>Hosted SQL query, stream-json output</summary> `supabase logs query "select 731 as qa_value" --remote --output-format stream-json`  </details> <details> <summary>Filtered Postgres tail</summary> `supabase logs tail --remote --service postgres --search "$QA_MARKER" --since 23h59m --no-follow` returns the generated QA marker.  </details> <details> <summary>Full remote source catalogue</summary> `supabase logs sources --remote --output-format json`. JSON spacing is adjusted for readability; values are unchanged. This lists supported sources, not log volume.  </details> ## Documentation follow-up Tracks [DEBUG-261](https://linear.app/supabase/issue/DEBUG-261/unify-cli-logs-command-spec-and-delivery). In the Correlated Logs CLI milestone: [DEBUG-267](https://linear.app/supabase/issue/DEBUG-267/publish-v3-unified-logs-cli-reference) for the v3 reference and [DEBUG-268](https://linear.app/supabase/issue/DEBUG-268/add-v3-unified-logs-troubleshooting-guide) for troubleshooting. Coordinate ID examples with [DEBUG-244](https://linear.app/supabase/issue/DEBUG-244/update-docs-to-reflect-examples-of-querying-logs-unique-identifiers).
This bumps storage from v1.89.13-r0 to v1.90.0-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.90.0-r0 Planned after supabase/slim-services published v1.90.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 edge-runtime from v1.78.0-r0 to v1.78.1-r0. https://github.com/supabase/slim-services/releases/tag/edge-runtime-v1.78.1-r0 Planned after supabase/slim-services published v1.78.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 pooler from v2.9.13-r1 to v2.10.0-r0. https://github.com/supabase/slim-services/releases/tag/pooler-v2.10.0-r0 Planned after supabase/slim-services published v2.10.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 storage from v1.90.0-r0 to v1.90.1-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.90.1-r0 Planned after supabase/slim-services published v1.90.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>
## Summary A config now declares its schema version with `$schema` in `config.json`/`config.jsonc`, or a leading `#:schema` directive in `config.toml` (a top-level `"$schema"` key also works in TOML, and the directive wins). No declaration means v1. This check has to ship before any v2 schema exists: only CLIs that already have it can give a clean error on a v2 file. - Only `https://supabase.com/schemas/config/v<N>.json` declares a version. A file declaring a version newer than v1 fails every command that loads or saves config with `CliConfigSchemaVersionError`: `supabase/config.toml declares config schema v2, but this version of the Supabase CLI only reads up to v1. Upgrade the Supabase CLI to use this config.` TOML is checked before parsing, so a v2 file that v1 can't parse still gets this message. Saving over a v2 file fails as well, so an older CLI never rewrites it. - `https://supabase.com/docs/cli/config.schema.json` (the old schema `$id`) and paths ending in `node_modules/@supabase/config/schema.json` or `.../dist/schema.json` read as v1 silently. Any other ref reads as v1 with a warning. - The CLI warns once per run about keys `CliConfigSchema` doesn't accept. The list comes from the schema itself (`findUnknownCliConfigKeys` on `@supabase/config/internal`), minus the CLI's document-only keys (`db.password`, `db.root_key`, `auth.external_url`, `[auth.passkey]`, `[auth.webauthn]`). Both warnings are `WARN:` lines on stderr, so `-o env|json` output stays clean. `CliConfigSchemaVersionError` is the only new public export. `packages/config/docs/cli-config-loading.md` documents the rules. ## Behaviour changes for existing projects - A config with a key the schema doesn't recognise now prints `WARN: supabase/<file> has keys this version of the Supabase CLI does not recognize and ignores: <keys>. Check them for typos, remove keys older CLI versions used, or upgrade the CLI if they come from a newer version.` on stderr. Previously these keys were ignored silently. The config still loads. - A config whose `$schema`/`#:schema` points somewhere other than a Supabase config schema URL now prints a `WARN:` line on stderr pointing at `https://supabase.com/schemas/config/v1.json`, and loads as before. ## Linked issue Closes CLI-2862
This bumps studio from 2026.11.02-sha-a05e7ec-r0 to 2026.11.09-sha-1567384-r0. https://github.com/supabase/slim-services/releases/tag/studio-2026.11.09-sha-1567384-r0 Planned after supabase/slim-services published 2026.11.09-sha-1567384-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> Co-authored-by: Julien Goux <hi@jgoux.dev>
) (#7530) ## TL;DR hides Studio's Logs pages when `supabase start` runs without Logflare, instead of showing pages that fail with a 500 ## whats biting the user? with `[analytics] enabled = false` Studio still shows Logs and every log query fails with `fetch failed` the CLI sends Studio `NEXT_PUBLIC_ENABLE_LOGS`, which Studio no longer reads, so its `logs:all` feature stays on ## fixed by: sending `ENABLED_FEATURES_LOGS_ALL`, the env Studio reads for `logs:all`, set from whether Logflare starts in that run so excluding Logflare with `-x` hides Logs too, and the experimental stack turns it on only when Studio gets both the analytics url and key ## ref - closes: #7529
…CLI-2746) (#7527) ## TL;DR `stack start` now serves auth email templates from `content_path` instead of refusing them ## whats introduced? the api listener serves each configured template at `/email/<id>?token=<uuid>`, and auth fetches it from the runtime url it already calls the file is read on every fetch, so edits and atomic replacements show up without a restart reads stay inside the project root like legacy, and the open file is checked against its resolved path, so a swapped link or folder is refused ## why/how? the stack refused every `content_path` template, so projects with a custom invite or magic link email could not start on it ```mermaid flowchart LR A[auth] -- GET /email/invite with token --> L{{api listener}} L -- regular file inside the project --> T[supabase/templates/invite.html] L -. anything else .-> X[404] ``` </details> ## ref: - closes: CLI-2583 & CLI-2746
This bumps realtime from v2.153.4-r0 to v2.154.0-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.154.0-r0 Planned after supabase/slim-services published v2.154.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>
SUPABASE_API_MAX_RETRIESto cap api retries (CLI-2607) (feat(cli): addSUPABASE_API_MAX_RETRIESto cap api retries (CLI-2607) #7394)