This document explains how the Supabase CLI is packaged and distributed, covering the two-binary model used by the legacy shell.
The CLI is distributed as a set of platform-specific npm packages. Each platform package contains two binaries:
@supabase/cli-darwin-arm64/
└── bin/
├── supabase ← TypeScript CLI (Bun single-file executable)
└── supabase-go ← Go CLI binary (for Phase 0 proxy commands)
The base supabase package routes to the correct platform package via src/shared/cli/bin.ts, which resolves and execFileSyncs the platform-specific bin/supabase binary.
The legacy shell is a gradual TypeScript port of the Go CLI. Commands are ported in phases:
- Phase 0 — The command is defined in the TS CLI tree but proxied to the Go binary at runtime via
LegacyGoProxy. - Phase 1+ — The command is implemented natively in TypeScript.
During Phase 0, the TS binary (supabase) needs the Go binary (supabase-go) available on the same system. Once all commands are ported to TypeScript, the Go binary will no longer be needed.
packages/
cli-darwin-arm64/bin/ supabase + supabase-go
cli-darwin-x64/bin/ supabase + supabase-go
cli-linux-arm64/bin/ supabase + supabase-go
cli-linux-x64/bin/ supabase + supabase-go
cli-linux-arm64-musl/bin/ supabase (musl TS binary only)
cli-linux-x64-musl/bin/ supabase (musl TS binary only)
cli-windows-arm64/bin/ supabase.exe + supabase-go.exe
cli-windows-x64/bin/ supabase.exe + supabase-go.exe
The musl packages only carry the Bun TS binary (compiled for musl). The Go binary is statically linked (CGO_ENABLED=0), so the glibc Linux binary works on musl as well — it is installed alongside the musl TS binary by the Linux package managers (deb/rpm/apk) from the glibc build.
When a Phase 0 command runs, go-proxy.layer.ts resolves the Go binary in this order:
SUPABASE_GO_BINARYenv var — explicit override, takes priority.- Co-located
supabase-go— looks next toprocess.execPath. Works in compiled SFE mode because the base shim usesexecFileSync, making the TS SFE the main process withprocess.execPathpointing to itself. - npm package resolution — resolves
@supabase/cli-{platform}/bin/supabase-go. Works when running from source with the platform packages installed. supabaseon PATH — final fallback, useful for local development.
The Go CLI source lives in apps/cli-go/ and is managed via:
pnpm repos:installThis must be run after a fresh clone before building a legacy release.
No build step is required to run the legacy CLI from source. The PATH fallback handles Go binary resolution automatically.
-
Install the Go CLI on your PATH (via npm, brew, or building from
apps/cli-go/). -
Create a shell alias to run the legacy CLI from source. For example in
.zshrc:alias supabase-dev="bun /path/to/dx-lab/apps/cli/src/legacy/main.ts"
-
Run commands:
supabase-dev orgs list # Phase 0: proxied to Go binary on PATH supabase-dev login # Phase 1+: native TypeScript implementation
Alternatively, set SUPABASE_GO_BINARY to point to a specific binary:
export SUPABASE_GO_BINARY=/path/to/supabaseThe scripts/build.ts script compiles both binaries for all target platforms when --shell legacy is passed:
bun scripts/build.ts --shell legacy --version X.Y.ZThis:
- Compiles the TS CLI to a Bun SFE for each platform →
packages/cli-{platform}/bin/supabase - Cross-compiles the Go CLI (
CGO_ENABLED=0) for each platform →packages/cli-{platform}/bin/supabase-go - Signs the macOS binaries (both
supabaseandsupabase-go) before archiving, so every channel ships the signed bytes — see release-process.md § Code signing (macOS) and ADR 0014 - Bundles both binaries into the platform archives (
.tar.gz/.zip) - Includes both binaries in the Linux package manager packages (deb/rpm/apk)
- ADR 0011 — the release & distribution strategy decision (binary packaging choice, per-channel publish mechanisms, CI pipeline design, open blockers).
release-process.md— operational playbook for local, PoC, and production releases.