REF · STACKBONE / WIKI · v0.9.4 stackbone

Local development

How stackbone dev boots the local emulator, injects env vars and tunnels to cloud Studio.

stackbone dev is 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 at http://127.0.0.1:4242 with the identical wire shape apps/api serves 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:

  1. Docker Compose stack — Postgres (with pgvector and pgmq) on :5433, MinIO on :9004/:9005. Project name is stackbone-dev-<agent-slug> so multiple projects can run in parallel.
  2. Database migrations — applies stackbone_platform.* schemas via @stackbone/platform-schema so the emulator's tables match cloud.
  3. Agent process — runs your package.json dev script under a free port, with all the @stackbone/sdk env vars injected.
  4. Studio API — the emulator HTTP server on 127.0.0.1:4242 (or --port) speaking the same protocol as api.stackbone.com.
  5. Cloudflare quick tunnel (default on) — exposes 127.0.0.1:4242 over 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. Run stackbone init to scaffold or stackbone link to 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-cloudflared when your environment forbids untrusted binaries; in that case install cloudflared system-wide or pass --no-tunnel to 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/cloudflared

The 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.com
  • http://localhost:* (any port; covers nx serve web on :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 dev

Precedence: 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 -v

Tunneling 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-cloudflared

Or skip the tunnel entirely:

stackbone dev --no-tunnel
# Studio at http://127.0.0.1:4242 only

Studio 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.