Local development
How stackbone dev boots the local emulator, injects env vars and tunnels to cloud Studio.
stackbone devis the local control-plane emulator. It boots the same services your agent will see in production (Postgres, object storage, queues, secrets, config) and serves the Studio API athttp://127.0.0.1:4242with the identical wire shapeapps/apiserves in cloud — see Stackbone Agent Protocol v1.The headline goal: edit code → see it run in Studio in <2 seconds. No deploy, no cloud, no API key fiddling.
What it does
When you run stackbone dev the orchestrator wires up four long-lived
processes and tears them down on Ctrl-C:
- Docker Compose stack — Postgres (with
pgvectorandpgmq) on:5433, MinIO on:9004/:9005. Project name isstackbone-dev-<agent-slug>so multiple projects can run in parallel. - Database migrations — applies
stackbone_platform.*schemas via@stackbone/platform-schemaso the emulator's tables match cloud. - Agent process — runs your
package.jsondevscript under a free port, with all the@stackbone/sdkenv vars injected. - Studio API — the emulator HTTP server on
127.0.0.1:4242(or--port) speaking the same protocol asapi.stackbone.com. - Cloudflare quick tunnel (default on) — exposes
127.0.0.1:4242over HTTPS so the cloud-hosted Studio (https://app.stackbone.com/studio) can reach the emulator without tripping mixed-content / local-network-access rules in Safari, Brave or Chrome.
Prerequisites
- Docker. Docker Desktop on macOS/Windows; Docker Engine on Linux.
- A linked project. The cwd needs
.stackbone/project.json. Runstackbone initto scaffold orstackbone linkto connect an existing directory. cloudflared(optional). The CLI auto-fetches a per-platform binary into~/.cache/stackbone/bin/on first run if missing. Skip with--no-auto-cloudflaredwhen your environment forbids untrusted binaries; in that case installcloudflaredsystem-wide or pass--no-tunnelto skip the public tunnel altogether.
Synopsis
stackbone dev [--port 4242] [--no-tunnel] [--listen] [--no-auto-cloudflared]| Flag | Default | Why you'd flip it |
|---|---|---|
--port |
4242 |
Pick another Studio API port (the agent process always picks a free port automatically). |
--no-tunnel |
off | Skip the Cloudflare tunnel. Use only when you'll open Studio at http://127.0.0.1:4242 from the same machine. |
--no-auto-cloudflared |
off | Refuse to download cloudflared. Requires a system install on PATH or STACKBONE_CLOUDFLARED_BIN. |
--listen |
off | Bind the Studio server to 0.0.0.0 so other machines on the LAN can reach it. The CLI prints a visible warning. |
Override the binary location with the env var:
export STACKBONE_CLOUDFLARED_BIN=/opt/homebrew/bin/cloudflaredThe boot banner
Once the stack is up the CLI prints a banner with both URLs:
┌─ stackbone dev ───────────────────────────────────────────────┐
│ Studio (cloud, recommended): https://app.stackbone.com/studio?baseUrl=https://<random>.trycloudflare.com
│ Studio (local fallback): http://127.0.0.1:4242
└────────────────────────────────────────────────────────────┘The "cloud" deeplink is the recommended path — it gives you the production Studio UI pointed at your local emulator. The "local fallback" is for offline work or when the tunnel is disabled.
CORS allowlist
The emulator enforces an explicit CORS allowlist (no *). Default origins:
https://app.stackbone.comhttp://localhost:*(any port; coversnx serve webon:4200)
Add origins per-repo via stackbone.config.json at the project root
(committable, unlike .stackbone/project.json):
{
"schemaVersion": 1,
"studio": {
"corsOrigins": [
"https://app.stackbone.com",
"https://staging.stackbone.com",
"http://localhost:*",
],
},
}Or override per run with the env var (CSV):
STACKBONE_CORS_ALLOW_ORIGINS="https://staging.stackbone.com,http://localhost:*" stackbone devPrecedence: env > file > default. Each entry is either an exact
origin or a wildcard with * matching a single path segment (no dot
crossing): https://*.stackbone.com works; localhost:* matches ports
but not subdomains.
Environment injected into the agent
The orchestrator injects these env vars into the agent process so
createClient() zero-args from @stackbone/sdk works the same locally as
in production:
| Variable | Points at |
|---|---|
PORT |
The auto-picked agent port. |
DATABASE_URL |
Local Postgres on :5433 with stackbone_dev DB. |
S3_ENDPOINT |
MinIO on :9004. |
S3_BUCKET |
stackbone-dev. |
AWS_ACCESS_KEY_ID |
stackbone. |
AWS_SECRET_ACCESS_KEY |
stackbone-secret. |
QSTASH_* |
TODO — local QStash mock not yet wired (Epic V1). |
OPENROUTER_API_KEY |
Read from your shell. Bring your own key in dev — the emulator does not provide credits. |
See Configuration → Environment variables for the full list.
Tearing down
Ctrl-C stops the agent process, the HTTP server, the tunnel and the
Compose stack in reverse order. The stackbone-dev-<slug> Compose project
is left in place between runs so Postgres data persists across
stackbone dev restarts. Wipe it with:
docker compose -p stackbone-dev-<slug> down -vTunneling without the auto-fetcher
When the auto-fetcher is disabled or your platform isn't supported:
brew install cloudflared # macOS
choco install cloudflared # Windows
# Linux: see https://github.com/cloudflare/cloudflared/releases
stackbone dev --no-auto-cloudflaredOr skip the tunnel entirely:
stackbone dev --no-tunnel
# Studio at http://127.0.0.1:4242 onlyStudio handshake
The cloud-hosted Studio at app.stackbone.com/studio performs a handshake
against the emulator's GET /api/contract to detect the protocol
version. If your CLI is older than minSupported the UI shows a
version drift banner — upgrade with pnpm add -g @stackbone/cli@latest
(or update the project-local install) and rerun stackbone dev. See
Stackbone Agent Protocol v1 §3
for the full handshake spec.
Troubleshooting
TODO — common failure modes (Docker not running, port collisions, tunnel rate-limited by Cloudflare, mismatched protocol version). Tracked alongside Troubleshooting.