Skip to content

feat(cli): port supabase start command to native TypeScript#5847

Draft
Coly010 wants to merge 50 commits into
developfrom
columferry/cli-1323-port-supabase-start
Draft

feat(cli): port supabase start command to native TypeScript#5847
Coly010 wants to merge 50 commits into
developfrom
columferry/cli-1323-port-supabase-start

Conversation

@Coly010

@Coly010 Coly010 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

What changed

Ports supabase start from a Go-binary proxy to native TypeScript in the legacy CLI shell (CLI-1323), continuing the local-dev-stack shell migration alongside the already-ported stop/status/db push/db reset/db start.

Talks directly to Docker/Podman via subprocess, mirroring Go's sequential per-container DockerStart — no Docker Compose, and no @supabase/stack/effect orchestration (that runtime targets a deliberately different local-dev product and would silently manage the wrong set of containers). Brings up all 14 containers in Go's real start order, with per-service config-boolean + --exclude gating, Go-byte-exact env/image resolution, a bulk health-check phase (Docker healthcheck for most services, an HTTP-HEAD-through-Kong bypass for PostgREST/Edge Runtime), and full rollback on any bring-up failure.

Also natively implements the fresh-volume DB schema/migration/seed pipeline, Edge Runtime container bring-up, and fresh-volume storage bucket seeding — previously tracked as out-of-scope follow-ups for this port, now closed. Only the linked-project version-check suggestion (a best-effort "update available" hint with zero Management API dependency otherwise) remains out of scope by design.

Why

Continues the legacy-shell migration from Go-binary-proxy commands to native TypeScript, closing CLI-1323.

Reviewer-relevant context

  • legacyParseGoDuration (the config.toml duration-string parser feeding Go-parity env vars like GOTRUE_SESSIONS_TIMEBOX) silently accepted a malformed duration with no digits (a bare unit like "s", or a lone "." with no digits on either side) and returned 0 instead of erroring like Go's real time.ParseDuration — both cases now throw Go's exact time: invalid duration "..." message.
  • start.services.ts's descriptive enabledGate metadata for Mailpit referenced the deprecated inbucket config section instead of its local_smtp rename. Fixed, and added a mechanical cross-check test that evaluates every service's enabledGate string against start.gates.ts's real computed gate across a battery of synthetic configs, so a future drift between the two fails loudly instead of silently.
  • Confirmed and closed the PostgREST health-check's TLS/CA trust gap for [api.tls] enabled = true local stacks (self-signed Kong cert) — the local-Kong-CA-trust mechanism already used by seed buckets/storage/db reset is now wired into start's health-check HTTP client too.

Note

This branch is currently 9 commits behind develop (unrelated deps/docker bump commits) — opening as draft to get CI/review visibility; happy to rebase before this comes out of draft.

Ports `supabase start` from a Go-binary proxy to native TypeScript in the
legacy CLI shell (CLI-1323), continuing the local-dev-stack shell migration
alongside the already-ported `stop`/`status`/`db push`/`db reset`/`db start`.

## What changed

Talks directly to Docker/Podman via subprocess, mirroring Go's sequential
per-container `DockerStart` — no Docker Compose, and no
`@supabase/stack/effect` orchestration (that runtime targets a deliberately
different local-dev product and would silently manage the wrong set of
containers). Brings up all 14 containers in Go's real start order, with
per-service config-boolean + `--exclude` gating, Go-byte-exact env/image
resolution, a bulk health-check phase (Docker healthcheck for most services,
an HTTP-HEAD-through-Kong bypass for PostgREST/Edge Runtime), and full
rollback on any bring-up failure.

Also natively implements the fresh-volume DB schema/migration/seed pipeline,
Edge Runtime container bring-up, and fresh-volume storage bucket seeding —
previously tracked as out-of-scope follow-ups for this port, now closed.
Only the linked-project version-check suggestion (a best-effort "update
available" hint with zero Management API dependency otherwise) remains out
of scope by design.

## Notable review-driven fixes

- `legacyParseGoDuration` (the `config.toml` duration-string parser feeding
  Go-parity env vars like `GOTRUE_SESSIONS_TIMEBOX`) silently accepted a
  malformed duration with no digits (a bare unit like `"s"`, or a lone `"."`
  with no digits on either side) and returned `0` instead of erroring like
  Go's real `time.ParseDuration` — both cases now throw Go's exact
  `time: invalid duration "..."` message.
- `start.services.ts`'s descriptive `enabledGate` metadata for Mailpit
  referenced the deprecated `inbucket` config section instead of its
  `local_smtp` rename. Fixed, and added a mechanical cross-check test that
  evaluates every service's `enabledGate` string against `start.gates.ts`'s
  real computed gate across a battery of synthetic configs, so a future
  drift between the two fails loudly instead of silently.
- Confirmed and closed the PostgREST health-check's TLS/CA trust gap for
  `[api.tls] enabled = true` local stacks (self-signed Kong cert) — the
  local-Kong-CA-trust mechanism already used by `seed buckets`/`storage`/
  `db reset` is now wired into `start`'s health-check HTTP client too.

Closes CLI-1323
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

👋 Thanks for the contribution! This pull request isn't linked to a tracked issue, so it's being closed automatically.

Please open an issue first, wait for a maintainer to add the open-for-contribution label, then open a pull request that links the issue with a closing keyword (e.g. Closes #123).
See CONTRIBUTING.md for the full workflow. Once a maintainer adds the open-for-contribution label to a linked open issue, reopen or open a new pull request and it will be accepted.

@github-actions github-actions Bot closed this Jul 9, 2026
@Coly010 Coly010 reopened this Jul 9, 2026
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

👋 Thanks for the contribution! This pull request isn't linked to a tracked issue, so it's being closed automatically.

Please open an issue first, wait for a maintainer to add the open-for-contribution label, then open a pull request that links the issue with a closing keyword (e.g. Closes #123).
See CONTRIBUTING.md for the full workflow. Once a maintainer adds the open-for-contribution label to a linked open issue, reopen or open a new pull request and it will be accepted.

@github-actions github-actions Bot closed this Jul 9, 2026
@Coly010 Coly010 self-assigned this Jul 9, 2026
@Coly010 Coly010 reopened this Jul 9, 2026
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

👋 Thanks for the contribution! This pull request isn't linked to a tracked issue, so it's being closed automatically.

Please open an issue first, wait for a maintainer to add the open-for-contribution label, then open a pull request that links the issue with a closing keyword (e.g. Closes #123).
See CONTRIBUTING.md for the full workflow. Once a maintainer adds the open-for-contribution label to a linked open issue, reopen or open a new pull request and it will be accepted.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

👋 Thanks for the contribution! This pull request isn't linked to a tracked issue, so it's being closed automatically.

Please open an issue first, wait for a maintainer to add the open-for-contribution label, then open a pull request that links the issue with a closing keyword (e.g. Closes #123).
See CONTRIBUTING.md for the full workflow. Once a maintainer adds the open-for-contribution label to a linked open issue, reopen or open a new pull request and it will be accepted.

@github-actions github-actions Bot closed this Jul 9, 2026
@avallete avallete reopened this Jul 9, 2026
pull Bot pushed a commit to chizee/cli that referenced this pull request Jul 9, 2026
)

The contribution gate identified internal maintainers solely from the
PR's `author_association`, which GitHub only reports as `MEMBER` when a
user's organization membership is public. A private org member (e.g. a
`supabase/cli` team member who keeps membership private) is reported as
`CONTRIBUTOR`/`NONE`, so the gate wrongly closed their PRs as
"no-linked-issue" (see supabase#5847).

Resolve maintainer status from the author's effective repository
permission (`admin`/`write`), which reflects team/org-granted access
that `author_association` does not surface, falling back to it only when
the cheap signals (bot, public internal association) are inconclusive.
The permission endpoint needs just `Metadata: read`, already covered by
the workflow's `contents: read`.


Claude-Session: https://claude.ai/code/session_01D41gYiFBSU7adE4ppnUUKg

---------

Co-authored-by: Claude <noreply@anthropic.com>
@Coly010

Coly010 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 623dd8b18e

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread apps/cli/src/legacy/commands/start/start.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/start/start.gates.ts Outdated
Comment thread apps/cli/src/legacy/commands/start/start.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/start/start.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/start/start.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/start/services/postgres.service.ts
@Coly010

Coly010 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 623dd8b18e

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread apps/cli/src/legacy/commands/start/start.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/start/start.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/start/lib/container-lifecycle.ts Outdated
Comment thread apps/cli/src/legacy/commands/start/lib/db-setup.ts
Comment thread apps/cli/src/legacy/commands/start/start.gates.ts Outdated
Comment thread apps/cli/src/legacy/commands/start/start.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/start/start.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/start/start.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/start/services/gotrue.service.ts Outdated
Comment thread apps/cli/src/legacy/commands/start/start.handler.ts
supabase start's TS port had five spots where Go's real behaviour was
silently dropped:

- supabase/.temp/storage-migration (the linked project's Storage
  migration pin) was never read, so DB_MIGRATIONS_FREEZE_AT was always
  empty on a fresh DB setup and the Storage container itself.
- supabase/.temp/{gotrue,rest,storage,realtime,studio,pgmeta,logflare,
  pooler}-version pins were never applied to the images start
  pulls/creates, unlike Go's Config.Load rewrite. Hoisted the existing
  services command's reader into shared/legacy-service-version-overrides.ts
  and reused services.shared.ts's tag-rewrite helpers instead of a third
  copy.
- --network-id was read by several other native ports but never by
  start itself, so the override never reached any container or the
  Docker network start creates.
- SUPABASE_API_PORT's env override was computed for status URLs but
  never exposed for Kong/Edge Runtime's own container specs, so they
  kept publishing/binding the un-overridden config.api.port.
- Studio's function bind mounts were hardcoded to [], unlike Go's
  unconditional (Edge-Runtime-independent) PopulatePerFunctionConfigs
  call — extracted the existing per-function bind computation in
  shared/functions/serve.ts into a reusable resolveFunctionBindMounts
  so both callers share one calculation.
@Coly010

Coly010 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

…l in start

Three more Go-parity gaps in supabase start found by review:

- db.root_key (including encrypted: values) was never resolved or
  passed to the Postgres container, so every project booted with the
  hard-coded default pgsodium key instead of a customized one, breaking
  decryption of existing encrypted data. Resolved in
  legacyResolveLocalConfigValues off the raw config document (unmodeled
  in @supabase/config's schema), reusing the existing decrypt helpers.
- DockerStart's Linux-only host.docker.internal:host-gateway mapping
  was already correctly ported for the one-shot migrate jobs and Edge
  Runtime bring-up, but never made it into the common
  legacyStartContainer path the other 13 services go through.
- auth.external_url (also unmodeled in the schema, with its own Go
  regression test) was never read, so GoTrue's API_EXTERNAL_URL/JWT
  issuer default/mailer verify URL/OAuth redirect fallbacks always
  derived from apiUrl even when a project intentionally exposed auth at
  a different host.
@Coly010

Coly010 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

…es tag ordering

Three more Go-parity gaps found by review:

- [auth.email.smtp] present without an explicit enabled key should
  default to enabled (Go sets this at load time), but the schema always
  decodes enabled: false when the key is absent, so GoTrue silently
  fell back to Mailpit. Reuses the existing correct resolution
  already implemented for config validation, exposed as
  legacyResolveAuthEmailSmtp.
- auth.external providers outside the schema's fixed ~19-provider set
  (e.g. a custom [auth.external.my_oidc] block) never reached GoTrue's
  env, even though Go's Auth.External is a genuine map iterated
  unconditionally and this port's own config validation already
  accepts arbitrary provider names. Reuses the same raw-document
  iteration validateAuthExternalProviders already established.
- A registry override with a port (SUPABASE_INTERNAL_IMAGE_REGISTRY=
  localhost:5000) broke the Postgres version-tag comparison, since Go
  compares the pre-registry-rewrite image while this port was comparing
  the already-rewritten one. Threaded the original image through as a
  separate configImage field used only for that comparison.
@Coly010

Coly010 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@Coly010

Coly010 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

…in-container behavior (review: #5847)

Go writes Kong's kong.yml and Postgres's pgsodium_root.key inside the
container via root-authored heredocs, landing at world-readable 0644.
This port instead stages them on the host at 0600 and bind-mounts them
read-only; on real Linux/Podman bind mounts that host uid/mode is
preserved verbatim, so Kong (uid 100) and Postgres's post-privilege-drop
postgres user (uid 100) get EACCES reading them. The enclosing 0700
per-container directory is unchanged and remains the actual host-side
protection via ancestor-directory execute/search checks, so this does
not reopen host-level access.
@Coly010

Coly010 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

…5847)

Go's DockerImagePullWithRetry(ctx, image, 2) retries any non-canceled
pull failure unconditionally, twice, with escalating 4s/8s backoff.
This port allowed only one retry at a flat 500ms and gated it on the
failure text matching a local regex allowlist, so transient errors
worded differently than expected (or a second consecutive failure)
would give up where Go would retry and often recover. Removed the
message-pattern gating and matched Go's retry count/backoff exactly,
with a new unit test covering a non-matching error message. Also bumped
one existing integration test's timeout (10s -> 45s, matching this
file's own convention for multi-candidate scenarios) since exhausting
all 3 registry candidates now takes ~36s of real backoff, same as Go.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f53bf09a5b

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread apps/cli/src/legacy/commands/start/start.handler.ts
Comment thread apps/cli/src/legacy/commands/start/start.handler.ts
Comment thread apps/cli/src/legacy/commands/start/start.handler.ts
Coly010 added 2 commits July 20, 2026 11:56
…review: #5847)

Go's DockerStart sets both the CLI project label and the compose
project label on config.Labels before creating anything, then passes
that same map into DockerNetworkCreateIfNotExists, so Go's network
carries both labels just like its containers and volumes. This port's
containers/volumes already merge both labels correctly, but the
network-creation call site only passed the CLI label, leaving fresh
start networks outside compose-project-label-based grouping/tooling.
…th minutes/hours (review: #5847)

Go's time.Duration.String() computes the fractional-seconds remainder
from the whole nanosecond count before peeling off minutes/hours, so
it always survives in the output (e.g. 1h0.5s -> 1h0m0.5s). This port
built the seconds component as a bare integer whenever an hours/minutes
prefix was present, silently truncating any sub-second remainder.
auth.sessions.timebox, auth.sms.max_frequency, and similar config
duration fields are formatted through this function before being
passed as GoTrue env vars, so a configured value like "1h0.5s" was
regressing to a shorter effective timeout/rate-limit than Go CLI sets.
@Coly010

Coly010 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

Coly010 added 2 commits July 20, 2026 12:30
…eview: #5847)

Go's ServeFunctions (the actual Edge Runtime bring-up) never reloads
Kong; only restartEdgeRuntime does, after ServeFunctions succeeds, and
that wrapper is used exclusively by functions serve's own restart loop.
start.go calls serve.ServeFunctions directly, bypassing
restartEdgeRuntime entirely, so Go's start never reloads Kong.

This port had the reload baked into startEdgeRuntimeContainer (the
ServeFunctions port), so start's Edge Runtime bring-up inherited a
Kong reload Go's start never performs -- which can race Kong's own
startup on a cold start and emit a misleading warning. Moved the
reload into startEdgeRuntime (the restartEdgeRuntime port), so only
functions serve's restart path triggers it, matching Go exactly.
…r, gated on confirmed removal (review: #5847)

Two related bugs in stop's staged-secret cleanup (Kong/Postgres/
Supavisor plaintext files under <workdir>/supabase/.temp/start-secrets):
Go has no equivalent here (it never stages secrets on host disk), so
neither is a parity question, but both are genuine TS-side correctness
bugs.

1. Cleanup always used the invoking cwd's workdir, never the actual
   container's. Tearing down another project's containers via --all
   or --project-id orphaned that project's plaintext secret files on
   disk forever, since the removed containers can no longer be
   rediscovered via docker ps. Fixed by stamping each container with
   its own workdir label at creation time and reading it back through
   the existing single docker ps listing (no extra Engine API call),
   falling back to the caller's workdir only when the label is absent.

2. Cleanup ran via Effect.ensuring keyed off the pre-teardown listing,
   so it fired even when the stop stage itself failed and container
   prune never ran -- deleting secrets for containers that were never
   actually removed and might still be live. Fixed by moving the
   capture point to fire only once container prune has confirmed
   removal, while still firing on a later volume/network prune failure
   (preserving the existing 983eab9 fix for that case).

Applied the same fix to start's own rollback path (start.rollback.ts),
which had the identical pattern.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

yield* reloadKong(projectId);

P2 Badge Keep Kong reload out of start

Checked the Go paths: supabase start calls ServeFunctions directly (apps/cli-go/internal/start/start.go:1101-1108), while only standalone functions serve's restartEdgeRuntime reloads Kong afterward (apps/cli-go/internal/functions/serve/serve.go:124-130). Because this shared helper is also used by legacyStartEdgeRuntimeContainer, supabase start --exclude kong still attempts docker exec <kong> kong reload and emits a warning for an intentionally excluded gateway; move the reload back to the standalone serve wrapper or gate it away for start.

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread apps/cli/src/legacy/commands/start/start.handler.ts
Comment thread apps/cli/src/legacy/commands/start/start.handler.ts
Comment thread apps/cli/src/legacy/commands/start/start.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/start/start.handler.ts
@Coly010

Coly010 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

#5847)

Go's Config.Load decodes and validates every Viper-bound field --
including realtime.ip_version, realtime.max_header_length,
api.max_rows, api.enabled, api.tls.enabled, storage.vector.enabled,
and the Mailpit/analytics ports -- in one atomic pass before any
Docker work runs (pkg/config/config.go:749-756, called before
ensureImagesCached at start.go:289). This port's pooler/Edge Runtime
overrides already matched that by wrapping in Effect.try +
LegacyStartInvalidConfigError, but these 10 sibling fields threw raw
exceptions instead -- becoming untyped Effect defects that bypass
withJsonErrorHandling's Effect.catch (breaking --output-format json/
stream-json error formatting) and wasting image pre-pull work on a
config Go would have already rejected.

Extracted the existing inline Effect.try pattern into a shared
wrapConfigOverride helper, applied it to all 10 previously-unwrapped
call sites plus the 4 existing pooler ones (removing the duplicated
boilerplate), and added regression tests for the malformed-value path
on 4 representative fields.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 99f0bedc22

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread apps/cli/src/legacy/commands/start/start.handler.ts Outdated
Comment thread apps/cli/src/shared/functions/serve.ts
Comment thread apps/cli/src/legacy/shared/legacy-docker-image-resolve.ts Outdated
Comment thread apps/cli/src/legacy/shared/legacy-docker-remove-all.ts
Coly010 added 5 commits July 20, 2026 14:02
Go's Config.Validate gates the Kong TLS disk-read on len(CertPath) > 0
/ len(KeyPath) > 0 (pkg/config/config.go:1006-1027), treating an empty
string the same as absent and falling back to Kong's embedded default
cert/key. This port's guard only checked !== undefined, so an explicit
cert_path = ""/key_path = "" passed through and tried to read
<workdir>/supabase/ as a file, aborting start. The correct check
already existed in legacy-local-config-values.ts's readApiTlsFiles
(used by status/stop) -- this just brings start's guard in line with
it.
Go's DockerNetworkCreateIfNotExists checks isUserDefined(mode) first
and skips creation entirely for default/bridge/host/none network
modes (docker.go:63-77), since those already exist and can't be
created -- verified live that docker network create host errors with
"operation is not permitted on predefined host network". This port's
legacyEnsureStartNetwork had no equivalent guard, so --network-id host
(or bridge/none/default) failed the whole start run instead of reusing
the existing network. Reused the existing isUserDefinedDockerNetwork
check from shared/functions/deploy.ts (exported it) rather than
reimplementing it.
… (review: #5847)

Go's Run() checks ignoreHealthCheck && IsUnhealthyError(err) against
whatever run() returns as a whole (start.go:73-81), and run()
propagates StartDatabase's health-wait failure immediately
(start.go:294-296), before any other service starts -- there is no
Postgres-specific carve-out in Go's classification (db/start/
start.go:227-231 is service-agnostic). With --ignore-health-check set
and Postgres itself timing out, Go skips rollback, prints a warning,
and still reaches the same "Started..."/status tail every other path
reaches.

This port's Postgres wait sat inside bringUp's unconditional-rollback
tapError, so a Postgres timeout always hard-failed regardless of the
flag -- the existing ignore-health-check downgrade mechanism was only
wired up for the later bulk (non-Postgres) health check. Routed
Postgres's wait through the same classification/downgrade branch,
short-circuiting the rest of bring-up (no other services, no seeding,
no cli_stack_started) on the ignored path while still reaching the
final tail, matching Go exactly.
…ck (review: #5847)

hasLocalImage's docker image inspect call only awaited the exit code
but left stdout/stderr at the default "pipe", and docker image inspect
writes the full image JSON to stdout on a cache hit -- large enough to
exceed the OS pipe buffer for images with a non-trivial layer history,
deadlocking the child against an unread pipe. Every other exit-code-
only containerCliExitCode call site in this codebase already passes
stdin/stdout/stderr: "ignore" for exactly this reason
(legacy-docker-remove-all.ts, legacy-pgdelta.seam.layer.ts); this call
site was the one outlier still at the unsafe default.
…(review: #5847)

An earlier fix (99f0bed) stamped every legacyStartContainer-created
container with a com.supabase.cli.workdir label so a later stop from
a different cwd could reclaim that container's own staged-secret
directory instead of falling back to the caller's cwd. Edge Runtime's
container is created via a separate docker run in shared/functions/
serve.ts rather than legacyStartContainer, so it never got the label
-- even though it stages the same kind of secret-like artifacts
(JWT/env/multiline-env/serve-main template) under the same
<workdir>/supabase/.temp/start-secrets/<containerId> convention and
carries the project label that puts it in scope for stop's cleanup
scan. This extends the same label to Edge Runtime's docker run call.
@Coly010

Coly010 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

Coly010 added 3 commits July 20, 2026 14:51
…5847)

apps/cli/AGENTS.md requires every exported token from legacy/ to carry
the Legacy/legacy prefix at the START of the name. buildLegacyGotrueEnv,
buildLegacyGotrueContainerSpec, buildLegacyPgMetaContainerSpec,
buildLegacyStudioEnv, buildLegacyStudioContainerSpec, and
buildLegacyStartContainerCreateArgs had it embedded mid-name instead,
unlike the rest of start/services/*.ts in this same PR (legacyBuild*).
Pure mechanical rename, no behavior change.

buildLegacyDockerArgs in legacy-docker-run.args.ts has the same
inversion but predates this PR and is shared by other already-merged
commands, so it's left for a separate cleanup.
…x (review: #5847)

Go's Config.Load passes .temp/*-version pin file contents straight
into replaceImageTag, which only trims whitespace and never adds a v
prefix (pkg/config/utils.go:81-84). legacyResolvePinnedImage instead
routed the same raw pin through tagForServiceVersion, which does
prepend v for several services -- so a storage-version pin of 1.2.3
produced :v1.2.3 instead of Go's :1.2.3. The services command already
gets this right via normalizeVersionTags: false; start.gates.ts
reinvented the same case and got it wrong. Also fixes the existing
integration test that had locked in the wrong :v1.2.3 assertion.
…isabled (review: #5847)

Go's Config.Validate only runs Auth.ThirdParty.validate() (the
required-field/multi-provider checks) inside "if c.Auth.Enabled"
(config.go:1087,1151-1153), and functions serve's own JWKS resolution
discards ResolveJWKS's error unconditionally regardless of auth.enabled
(serve.go:141). This port's resolveAuthArtifacts called the strict
resolveThirdPartyIssuerUrl unconditionally, so a malformed or
multi-enabled third-party provider config aborted functions serve even
with auth disabled, where Go tolerates it.

start's own JWKS resolution already had the correct pattern
(legacyResolveLocalJwks in legacy-local-config-values.ts, gating on
auth.enabled and falling back to the unchecked builder) -- this
carries the same fix to standalone functions serve's resolveAuthArtifacts.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

yield* output.raw("Setting up Edge Functions runtime...\n", "stderr");

P2 Badge Keep the Edge Runtime setup banner out of start

startEdgeRuntimeContainer is now also called by supabase start, but this banner belongs to the functions serve restart wrapper in Go: start.go calls serve.ServeFunctions directly, while only restartEdgeRuntime prints Setting up Edge Functions runtime.... With edge_runtime.enabled, supabase start now emits an extra stderr line (including non-text output modes), breaking the Go-compatible command transcript; move the print back to the functions serve wrapper.

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread apps/cli/src/legacy/commands/start/services/gotrue.service.ts Outdated
Comment thread apps/cli/src/legacy/shared/legacy-local-config-values.ts Outdated
Comment thread apps/cli/src/legacy/commands/start/start.handler.ts Outdated
Comment thread apps/cli/src/shared/functions/serve.ts Outdated
Coly010 added 3 commits July 20, 2026 15:02
…re needed (review: #5847)

writeDockerMultilineEnvScript's env.length === 0 early return ran
before the self-healing rm(dir) cleanup, so a run with no multiline
secrets never reclaimed a stale multiline-env directory a previous
run (or a crash) left behind -- leaving plaintext JWT/custom secret
material on host disk until the next stop/failed-start rollback.
Moved the cleanup to run unconditionally before the early return.
…iew: #5847)

db.root_key is unmodeled by @supabase/config's schema, so a bare
root_key = 12345 in config.toml parses to a JS number. The as string
cast here let that number flow unguarded into legacyIsEncryptedSecret,
which calls .startsWith(...) and throws an uncaught TypeError instead
of a config-load failure. Go's DecryptSecretHookFunc passes non-string
kinds through untouched and mapstructure then rejects the scalar with
a clean "failed to parse config" error (verified via a live repro).
Replaced the cast with a runtime type guard that throws
LegacyConfigValidateError, matching this file's existing pattern for
the same class of unmodeled-field decode failure, and removes a
production `as` cast this repo's own conventions ban.
…ount path (review: #5847)

Go's PopulatePerFunctionConfigs unconditionally prints "Skipped
serving Function: <slug>" to stderr for every disabled function,
called from two independent sites -- ServeFunctions (gated on Edge
Runtime enabled) and, separately, Studio's bind-mount setup in
start.go:1149-1159, which is not gated on Edge Runtime at all. So
when Edge Runtime is disabled/excluded but Studio is enabled, Go
still emits the message via the Studio path alone.

resolveFunctionBindMounts (the shared helper backing Studio's bind
mounts) silently dropped disabled functions with no output. Added the
matching stderr warning, matching Go's message exactly, including the
double-print quirk when both Edge Runtime and Studio are enabled
(both call sites fire in Go, so this port reproduces that rather than
deduping it).
@Coly010

Coly010 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

…review: #5847)

legacyStart resolved local config values once early on to build every
container spec, but the success-path status print called
legacyResolveStatusLocalState -> legacyResolveLocalConfigValues again
after bring-up/health checks completed. For auth.signing_keys_path
configured with unset anon/service-role keys, legacyGenerateAsymmetricGoJwt
embeds a time-dependent exp claim, so the second call mints a
different signed token than the one already baked into the running
Kong/GoTrue containers -- and since Kong does literal string matching
on these key values (including Realtime's apikey query param), a
printed key that doesn't byte-match the running one can genuinely
fail client requests.

Go generates these keys exactly once per process (Config.Validate())
and reuses the same in-memory value for both container config and the
final print. Threaded the already-resolved values through as an
optional precomputedLocal param on legacyResolveStatusLocalState,
reused only by the success path -- the already-running branch
correctly still recomputes, since Go's own status.Run() genuinely
calls LoadConfig a second time in that branch too.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 38c452030d

ℹ️ About Codex in GitHub

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

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

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

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

Command.withShortDescription("Start local Supabase stack"),
Command.withHandler((flags) => legacyStart(flags)),
Command.withHandler((flags) =>
legacyStart(flags).pipe(withLegacyCommandInstrumentation({ flags }), withJsonErrorHandling),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject unsupported --output for start

When users pass -o/--output to supabase start, this wrapper uses withLegacyCommandInstrumentation's default allowed output formats, so the global LegacyOutputFlag is accepted and then ignored by the handler. The Go command registers only exclude, ignore-health-check, and hidden preview in apps/cli-go/cmd/start.go:55-60, so supabase start -o json should fail during flag parsing; after this port it proceeds to Docker startup instead of rejecting the unsupported flag.

Useful? React with 👍 / 👎.

Comment on lines +1013 to +1015
const configFunctions = yield* inferFunctionsManifest({
cwd: cliConfig.workdir,
config: { ...config, functions: configDeclaredFunctions },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep function discovery within the selected workdir

When --workdir points at a directory without its own supabase/config.toml under another Supabase project, this call lets inferFunctionsManifest climb ancestors via findProjectPaths(options.cwd) and use the ancestor project as the functions discovery root. The rest of start deliberately loads config with search:false for exact-workdir parity, so matching function slugs in the requested workdir can inherit ancestor filesystem defaults such as deno.json/import-map settings and then fail or mount the wrong paths during Studio/Edge Runtime setup.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants