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 agent@supapool.io 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.