Skip to content
View as .md

Local development

apps/www is the only worker. For normal app development, run Vite and Caddy in two terminals, then browse through Caddy:

Terminal window
bun run --filter '@arbe/www' dev # terminal 1: vite, HMR, http://localhost:8888
bun run --filter '@arbe/www' dev:proxy # terminal 2: Caddy h2 → https://localhost:8443

Open https://localhost:8443 for any house/thread page. Caddy is only a reverse proxy; it does not replace the Vite dev server, and it does not provide live reload/HMR itself. If the proxied page does not reload after edits, refresh manually while keeping the Vite dev server running. Use Wrangler instead when you need full Cloudflare bindings:

Terminal window
bunx wrangler dev -c apps/www/wrangler.jsonc # full bindings, no HMR

The Caddy proxy isn’t optional once you touch house/thread pages. Each Electric shape holds one long-poll open against the dev origin, and a typical house view mounts ~6 shapes (members, house, agents, threads, environments, configs). Browsers cap HTTP/1.1 at 6 connections per host, so all sockets pin. SvelteKit’s __data.json for client-side nav, Vite’s HMR ping, and any fetch then queue until a long-poll releases one. Production is fine: Cloudflare serves over h2/h3 and multiplexes everything onto one socket. The Electric team calls this out as the #1 dev gotcha.

One-time setup: brew install caddy (or caddyserver.com/docs/install); then run caddy trust if your browser does not already trust Caddy’s local CA. Verify in DevTools — the Protocol column should show h2 for /api/shapes/*. If you see http/1.1, you’re on :8888, not :8443. Shape URLs are origin-relative, so they route through Caddy automatically with no code changes.

With Vite dev, Cloudflare-specific bindings (rate limiter) aren’t available — most routes don’t need them; the ones that do require wrangler dev. Bot replies run in-process via packages/core/dispatch/ when POST /api/threads/:id/entries writes. Secrets live in apps/www/.env.local (SUPABASE_JWT_SECRET, DURABLE_STREAMS_SECRET, ELECTRIC_*, OPENROUTER_API_KEY; ANTHROPIC_API_KEY is optional for explicit direct Anthropic refs); missing or empty values fail fast with a loud error naming the keys.

Terminal window
bun run check # format + lint + typecheck across packages (subsumes lint)
bun run test # all packages
# scope: bun run --filter '@arbe/cli' check

check subsumes lint — never run both. bun run lint still exists for read-only lint without formatting or typechecking. Biome handles lint + format for most packages (biome.jsonc at repo root); apps/www uses eslint + prettier because of the svelte plugin.

Code: apps/www/Caddyfile.dev, apps/www/.env.example.
See system/environment-variables, system/deployment, debugging.