---
title: "Local development"
description: "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. Sot…"
url: "https://sotaagents.ai/manual/developer-guide/local-development"
generated_by: "sotaagents-ldp"
docs_index: "https://sotaagents.ai/manual/llms.txt"
locale: "en"
---

# 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

```
# terminal 1 — your app's watchers
npm run dev

# terminal 2 — the Development session and tunnel
npm run dev:sota   # equivalent to: sota dev
```

1. #### Start 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.

2. #### 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.

3. #### 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.

4. #### 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. |

> [!NOTE]
> There is no local UI server, and that is deliberate.
>
> 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

```
# 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 dev
```

`sota 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.

> [!WARNING]
> A live session is personal.
>
> 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.
