Local development
Local development uses two independent processes in two terminals. You own the app's watchers; the CLI owns the Development session and the tunnel that reaches your machine. Sota CLI never starts, restarts, or kills your processes — including when the session stops.
# terminal 1 — your app's watchers
npm run dev
# terminal 2 — the Development session and tunnel
npm run dev:sota # equivalent to: sota devStart your own watchers
Run npm run dev. In a project scaffolded with both a backend and native UI this one script already runs both: concurrently starts the backend watcher and the UI watch build side by side, labelled backend and ui, with --kill-others so one crash stops the pair instead of leaving half a stack running.
Open Development
Run sota dev, select an organization and workspace, and keep the process running. The CLI prepares everything locally first — it compiles the manifest, verifies the built frontend output, and opens the tunnel transport — and only then publishes the session in one commit. A failure during preparation creates nothing on the server.
Iterate safely
The CLI watches every file the compiled manifest was built from — the manifest itself, JSON Schemas, locale files, native assets, and inlined skill bodies — and re-syncs your personal session on change. A compile error prints diagnostics and keeps the last known-good manifest instead of replacing it. A heartbeat every 15 seconds extends the session lease.
Stop cleanly
Run sota dev stop, or press Ctrl-C in the sota dev terminal. The Development app disappears and its Core-managed data cleanup is scheduled. Your own frontend and backend processes are left running — the CLI says so explicitly when it exits.
Running the UI and the backend separately#
npm run dev is a convenience wrapper over two scripts that also exist on their own. Run them individually whenever you want to restart one half without disturbing the other, attach a debugger to just one, or — most importantly — when your backend is not a Node.js process at all.
| Script | What it actually runs | Notes |
|---|---|---|
npm run dev | concurrently --kill-others --names backend,ui "tsx watch src/backend/server.ts" "sota contracts ensure && vite build --watch" | Both halves at once. In a backend-only or UI-only project it collapses to just that half. |
npm run dev:backend | tsx watch src/backend/server.ts | The app's HTTP service, restarted on source change. Listens on PORT, default 8787. |
npm run dev:ui | sota contracts ensure && vite build --watch | A watch build, not a dev server. It refreshes the App UI type contract, then rebuilds dist/ui/app.js and dist/ui/app.css on every change. |
npm run dev:sota | sota dev | The Development session, manifest sync, and tunnel. Independent of the two above. |
Native UI runs inside the SotaAgents host, not on localhost. dev:ui only has to keep the built module on disk current; the tunnel serves those exact bytes to the platform, which is why the same files work unchanged once they are packed into a deployed artifact.
Bringing your own backend runtime#
The scaffold is TypeScript and Express because that is widely understood, not because the platform requires it. The contract between SotaAgents and your backend is plain HTTP plus a verified token, so a Go, Python, Java, or Rust service is a first-class app backend. One of the SotaAgents system apps is a FastAPI service that verifies the very same invocation token in Python against the same Core JWKS endpoint.
In that setup you simply do not use the Node backend scripts. Start your service however your stack starts it, keep npm run dev:ui for the native UI if the app has one, and point the local overlay at whatever port your service listens on:
# terminal 1 — your backend, in your language
uvicorn app.main:app --reload --port 8787
# terminal 2 — native UI watch build (only if the app contributes UI)
npm run dev:ui
# terminal 3 — the Development session
sota devsota dev resolves the Local Backend Endpoint in this order: the --local-url flag, then environments.local.service.baseUrl from the manifest, then the endpoint you last used (remembered in .sota/dev.json). In an interactive terminal it prompts if none of those is set; otherwise it fails with LOCAL_BACKEND_UNAVAILABLE. The CLI then polls that endpoint about once a second and prints [backend] available or [backend] unavailable. That check is advisory only — it never gates publishing the session, so a backend that is temporarily down does not tear down your Development app.
It belongs to one developer and one selected workspace. Running sota dev again replaces only your own previous session; it is not a shared Staging environment. Staging and Production never depend on a tunnel — they call your hosted backend directly.