How it works
npx @supapool/cli run -- npm run dev
Wrap any command. Supapool leases a clean instance, injects its credentials, keeps it alive, and releases it when the command exits.
Why
- Real Supabase. Postgres, Auth, and S3-compatible Storage.
- Parallel agents need this. Agents never wipe each other's databases mid-run.
- No mocks. Test migrations and database operations against a real, isolated Supabase stack without touching production.
- Branching is slow and expensive. Branches can take minutes to create and bill as if they were production instances.
- Colocated. Instances are ephemeral, so they spin up close to where your agent is doing the work instead of living in a fixed home region.
- CLI-only. There is no dashboard. Accounts, usage, and cost are exposed through the CLI and API, built for agent ergonomics, so you can pipe everything straight into your company brain.
- Free during beta. Email [email protected] for an immediate automated response with a fix or a request for the information needed to solve it.
Docs
Commands
The CLI has two commands.
npx @supapool/cli login
npx @supapool/cli run -- <command>
login opens GitHub sign-in and saves an API key to ~/.config/supapool/config.json. run does the same sign-in on first use, so most people never run login directly.
run acquires a clean instance, applies every .sql file in supabase/migrations in filename order, starts your command with the instance credentials in its environment, renews the lease while the command is alive, and releases the instance when it exits. Your repository's environment files are never modified.
What gets injected
The wrapped command receives standard Supabase variables: SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY, and DATABASE_URL. The same values are mirrored to the current publishable and secret key names, the public prefixes used by Next.js, Vite, Astro, Svelte, Expo, Create React App, Gatsby, and Nuxt, Prisma and Postgres URL aliases, and the standard PG* connection variables. Secret keys are never assigned to browser-public variables. SUPAPOOL_INSTANCE_ID identifies the lease.
Leases
Every instance is a lease with a TTL, 30 minutes by default. The CLI renews it every 5 minutes while your command runs. When the command exits, or if the process dies and renewals stop, the lease expires and the slot is wiped and returned to the pool. Nothing you store in an instance survives release, so treat every run as disposable.
CI
CI jobs skip the browser login. Sign in once on a laptop, copy the key from ~/.config/supapool/config.json into a CI secret, and set it as SUPAPOOL_API_KEY:
SUPAPOOL_API_KEY=sp_live_... npx @supapool/cli run -- pnpm test
Use it from code
The npm package is also a library. withInstance acquires a lease, renews it while your callback runs, and always releases it, even when the callback throws:
import { withInstance } from '@supapool/cli'
await withInstance(async (instance) => {
const { SUPABASE_URL, SUPABASE_ANON_KEY, DATABASE_URL } = instance.env
// run tests, seed data, exercise the real stack
})
The lifecycle primitives acquire, renew, release, and startRenewer are exported for code that manages its own lease boundaries. See the package README for the full API.