Skip to content

chore: production deploy - #7452

Merged
supabase-cli-releaser[bot] merged 101 commits into
mainfrom
develop
Nov 9, 2026
Merged

supabase-cli-releaser[bot] merged 101 commits into
mainfrom
develop

Conversation

@supabase-cli-releaser

Copy link
Copy Markdown
Contributor

pamelachia and others added 22 commits November 2, 2026 12:08
`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>
@supabase-cli-releaser supabase-cli-releaser Bot added the do not merge Approve to apply; do not merge. label Nov 3, 2026
@supabase-cli-releaser
supabase-cli-releaser Bot requested review from a team and supabase-oss as code owners November 3, 2026 02:15
supabase-cli-releaser Bot and others added 5 commits November 3, 2026 02:41
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)
Coly010 and others added 13 commits November 5, 2026 15:19
#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.";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚪ 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.

Suggested change
"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.";

supabase-cli-releaser Bot and others added 8 commits November 6, 2026 10:57
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>

![logs
help](https://github.com/user-attachments/assets/bbbcb62f-b0ae-4795-ab57-539dc597a368)

</details>

<details>
<summary>supabase logs query --help</summary>

![query
help](https://github.com/user-attachments/assets/1d27f8c8-f03e-43d4-9809-e3c5dfa1bd01)

</details>

<details>
<summary>supabase logs tail --help</summary>

![tail
help](https://github.com/user-attachments/assets/d6848817-cddc-4526-9a1f-1f75c0c1b477)

</details>

<details>
<summary>supabase logs sources --help</summary>

![sources
help](https://github.com/user-attachments/assets/378c2b6a-4768-420c-a3b1-0a801a40c45a)

</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`

![Hosted SQL query, text
output](https://github.com/user-attachments/assets/4bd7bc78-c4fe-4d01-867d-82eddaf90bb9)

</details>

<details>
<summary>Hosted SQL query, JSON output</summary>

`supabase logs query "select 731 as qa_value" --remote --output-format
json`

![Hosted SQL query, JSON
output](https://github.com/user-attachments/assets/4a6b9ae9-ff8c-4ad1-b58c-2817ae7fdf2d)

</details>

<details>
<summary>Hosted SQL query, stream-json output</summary>

`supabase logs query "select 731 as qa_value" --remote --output-format
stream-json`

![Hosted SQL query, stream-json
output](https://github.com/user-attachments/assets/d8a6450b-5882-4672-96d8-74e355aaea87)

</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.

![Filtered Postgres
tail](https://github.com/user-attachments/assets/44c84119-bab9-4d97-981d-ac88c3824338)

</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.

![Full remote source
catalogue](https://github.com/user-attachments/assets/8af46572-e146-498c-85b8-b6faffc86614)

</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>
7ttp and others added 3 commits November 9, 2026 11:14
) (#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-cli-releaser
supabase-cli-releaser Bot merged commit d4fc0e5 into main Nov 9, 2026
145 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do not merge Approve to apply; do not merge.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants