npm run dev — what's happening?
Four processes, three ports, one Turbo pipeline.
npm run devThe root dev script is a one-line alias for turbo run dev. Turbo finds every workspace that defines a dev task and runs them in parallel. Here's what comes up:
| Workspace | Port | Command | What it is |
|---|---|---|---|
@orbit/api | 4002 | tsx watch src/index.ts | Hono REST + WebSocket server. |
@orbit/web-tanstack | 4001 | vite --port 4001 | Authenticated app (TanStack Start + React 19). |
@orbit/web-next | 4003 | next dev --turbopack --port 4003 | Authenticated app (Next 16 + React 19). |
@orbit/www | 4000 | vite dev --port 4000 | Marketing site — this page. |
@orbit/webhook-tunnel | — | Node forwarder | Only runs if SMEE_URL is set. Forwards smee.io POSTs to the local API. |
You almost never need all four apps running at once. Use the per-workspace shortcuts below to trim what comes up.
Running a subset
The root package.json ships these filtered shortcuts:
npm run dev:api— API only (port 4002).npm run dev:web— whichever web shell the CLI kept (the create-orb scaffold repoints this to the surviving app).npm run dev:web-tanstack— the TanStack shell.npm run dev:web-next— the Next shell.npm run dev:www— the marketing site only.
What starts in what order
Turbo runs every dev task in parallel — there's no declared dependency between them. In practice the API is usually ready a second or two after the web shells, which is fine: React Query retries failed requests, and the TanStack shell waits on the first session fetch before rendering.
If you want a hard boot order (e.g. in CI or a container health check), use the per-workspace scripts and chain them yourself.
What you're actually getting
The API (:4002)
- Hono HTTP server under
tsx watch, hot-reloading on every TS save. - WebSocket server on
/v1/ws— see Realtime & presence under Concepts. - If
JOBS_PROVIDER=graphile, a co-located graphile-worker loop starts alongside the HTTP server. - If billing is on, Stripe / Polar / Dodo webhook routes wait at
/v1/billing/webhooks/<provider>.
The web shells (:4001 or :4003)
You'll have exactly one of these after scaffolding — the CLI strips whichever app you didn't pick. Both shells point at the API via VITE_API_URL (or its Next-equivalent) at build time. To run both against one API, set ADDITIONAL_WEB_ORIGINS=http://localhost:4003 in apps/api/.env so the CORS allowlist and better-auth cookie policy both trust them.
The marketing site (:4000)
TanStack Start too, but unauthenticated and short. "Get access" links over to the web shell's request-access page using VITE_WEB_URL.
The webhook tunnel
Only runs when SMEE_URL is set. Covered in detail in apps/webhook-tunnel/README.md. Short version:
- Visit
https://smee.io/new, copy the URL. - Paste it as
SMEE_URLinapps/api/.env. - Register that same smee URL as your provider's webhook endpoint.
npm run dev— the tunnel relays every POST tohttp://localhost:4002${SMEE_TARGET_PATH}.
Logs and noise
Turbo prefixes every line with the workspace name in a unique color, so interleaved logs stay readable:
@orbit/api:dev: listening on http://localhost:4002@orbit/web-tanstack:dev: VITE ready in 312 ms@orbit/www:dev: ➜ Local: http://localhost:4000/Stopping cleanly
A single ^C in the terminal where npm run dev is running sends SIGINT to every child process — Turbo propagates it, tsx cleans up watchers, Vite shuts its HMR socket. If ports stay bound after a crash, it's usually a stray tsx or node — kill by port and retry.