The wrapper backend package

The backend workspace that turns one Express process into a control plane for many local Supabase stacks.

What it is

packages/supabase-wrapper is the backend workspace, published internally as @supabase-manager/backend. It is a TypeScript Express service. It owns the management API, the WebSocket, the proxy and front-door services, project lifecycle, port allocation, Postgres introspection, the manager database and the vault. The frontend calls it; it calls the Supabase CLI and each project's services.

The package is the product's control plane in code. Understanding its responsibilities explains why the manager can be a single process without hiding a second service.

Why it is a separate workspace

The repository is an npm-workspaces monorepo with two workspaces: the web application and this backend. The split keeps the browser bundle free of server dependencies and lets the backend be built, tested and packaged on its own. The release build compiles the backend to dist and copies its database migrations alongside it, so a packaged install has everything it needs.

Inside the wrapper package

Subsystem component map The browser app talks to one Express backend. The backend owns the manager API, the front door, the subdomain proxy, the shared pooler and the SQLite manager database, and drives the Supabase CLI for each project stack. Browser React SPA (apps/web) manager UI + Studio links Express backend packages/supabase-wrapper manager API + WebSocket front door :6000 / proxy :9000 shared PgBouncer :6432 Manager SQLite (sql.js) projects / users / settings port allocations / vault metadata Supabase CLI start / stop / db driven per project no Docker API client Docker stack Kong / Postgres / Auth Storage / Realtime Functions / Studio one per project /api /ws state spawn containers Responsibilities Web app: pages, forms, live streams. Backend: auth, tenancy, lifecycle, proxying, introspection. Manager database: durable product state. CLI: the stack itself. Docker: containers and volumes. The front door and subdomain proxy share one access evaluator; neither forwards before a decision. backend state

Diagram: the wrapper package contains the routes, proxy services, lifecycle manager, port allocator, manager database, vault and introspection adapters; arrows point to the Supabase CLI, Docker and the project services.

Wrapper moduleStateExternal dependency

What it owns

The HTTP surface is split into route modules for projects, admin, auth, settings, pools, notifications and dashboard health, plus system routes. The proxy services resolve a project by path or host, evaluate access, forward the request and rewrite the response. The lifecycle manager spawns the CLI and tracks status. The allocator reserves port blocks. The database layer loads SQLite with sql.js and applies numbered migrations in a transaction with a backup on failure. The vault encrypts secrets under the master key. Introspection adapters talk to postgres-meta and the project's own services.

A management API call

Request flow sequence A browser calls the manager API with a session cookie and CSRF header. Middleware verifies the session and CSRF token, the route checks project ownership, the backend queries the manager database or project Postgres, and the response returns as JSON to the browser. A manager API request, end to end BrowserExpressMiddlewareRouteStore GET /api/projects/:id/status session cookie verify + CSRF + ownership route handler manager DB / Postgres rows / status 200 JSON Failure paths Missing or invalid session gives 401. CSRF mismatch gives 403. A non-owner gets a non-disclosing 404. An upstream failure goes through central error redaction; no stack trace reaches the browser.

Diagram: a management API call enters the Express routes, passes authentication and CSRF checks, reaches a service module, and reads or writes the manager database or a project service before returning.

Request pathGuardProject service

Public surface and boundaries

The backend exposes a cookie-authenticated API and a WebSocket. It does not expose a general-purpose database proxy: every project operation goes through a route that checks ownership first. The front door and the subdomain proxy are the only paths that forward arbitrary project traffic, and they share one access evaluator.

Shell execution is restricted to the Supabase CLI and a small number of operations, without a shell. Error responses pass through a central redaction path so internal details are not reflected to clients. The manager's own token is stripped before a proxied request leaves the process.

Failure and recovery

The package refuses to boot without a valid JWT_SECRET and VAULT_MASTER_KEY. Database migrations run in a single transaction with a pre-migration backup; a failed migration is restored rather than left half-applied. The launcher frees orphaned processes on the manager and proxy ports before a start. A failed project operation returns an error for that operation, not a process exit; the manager stays up so the other projects remain manageable.

Configuration

<code>PORT</code>
The bind port for the API and WebSocket.
<code>JWT_SECRET</code>
Signs manager sessions; required and at least 32 characters.
<code>VAULT_MASTER_KEY</code>
Encrypts secrets; required and at least 32 characters.
<code>SUPABASE_MANAGER_ENV_FILE</code>
Overrides the environment file path.
The backend's own tests are the best description of its contracts. The quality page lists the suites and the commands that run them.

How modules communicate

Routes validate input, authenticate the request and delegate to a service module. Service modules own the behaviour and the database writes; they do not know about the HTTP response shape. The proxy layer sits between the routes and the project, and the lifecycle manager is the only module that spawns the CLI. Keeping those seams narrow is what makes the backend testable without a browser.

The manager database and the vault are shared infrastructure rather than per-route state. A route that needs a secret asks the vault; it does not read the environment directly. A route that needs a project asks the project store; it does not hold its own copy.