Developers
How NALA is built: six artifacts, the transports between them, and where to start reading.
NALA is not one program. Understanding which of six separately-built artifacts owns a behaviour answers most "where does this live" questions before you open a file.
The six artifacts
| # | Artifact | Built by | Entry | Owns |
|---|---|---|---|---|
| 1 | Electron main | npm run build:main | src/main/index.ts | Window and tray lifecycle, PTY ownership, privileged services |
| 2 | Renderer | Vite | src/renderer | The visible desktop UI |
| 3 | Preload bridge | Vite | src/preload/preload.ts | Namespaced IPC surfaces exposed to the renderer |
| 4 | Daemon | npm run build:daemon | src/daemon/index.ts | Sessions, orchestration store, tasks, artifacts, A2A, channels. Survives app close |
| 5 | CLI | npm run build:cli | src/cli/index.ts | nala / wmux binaries; also hosts the TUI |
| 6 | MCP server | npm run build:mcp | src/mcp/index.ts | Tools published to MCP hosts |
There is a seventh optional runtime: the Pi runtime
(npm run build:pi), which is not in git.
WARNING
An empty vendor/ directory will silently ship a build without the Pi
runtime. Always build with NALA_REQUIRE_PI_RUNTIME=1 so a missing runtime
fails loudly instead of producing a quietly incomplete package.
Transports
| Path | Mechanism |
|---|---|
| Renderer ↔ main | Electron IPC, schema-validated |
| Main ↔ daemon | Named pipe / unix socket |
| CLI / MCP / external ↔ main | Pipe server with an RPC router |
| TUI ↔ daemon | Direct daemon RPC client |
| Peer machines | LanLink |
IPC is validated rather than trusted — schemas, sender validation, and handler wrapping sit between the renderer and anything privileged.
Instance isolation
The entire runtime is namespaced by a data-suffix environment variable
(WMUX_DATA_SUFFIX / NALA_DATA_SUFFIX): data directory, socket, auth token,
and pipes. Development builds set -dev.
This is why a development build and a packaged build coexist without
interfering, and why a nala CLI invocation reaches the daemon matching its own
suffix. It is also the first thing to check when the CLI "cannot find" a running
daemon.
Building
npm run make
Runs the daemon, MCP, CLI, and main builds, then packages. npm run make:full
and npm run package:full are the complete variants.
For iteration:
npm run start:fast
Testing
| Command | Scope |
|---|---|
npm test | Parallel suite plus runtime suite |
npm run test:parallel | Everything except runtime-tagged tests |
npm run test:runtime | Runtime-tagged tests |
npm run test:harness | Harness suite |
npm run test:rig | Simulation rig |
npm run test:packaged-self-test | Verifies a packaged build |
Vitest throughout, with separate configs per surface.
Where to start reading
| If you are changing… | Start at |
|---|---|
| Anything durable (tasks, sessions, A2A) | src/daemon/ |
| Provider launch or PTYs | src/daemon/sessionHost/, src/main/pty/ |
| The desktop UI | src/renderer/ |
| The terminal agent | src/tui/ |
A nala subcommand | src/cli/commands/nala/ |
| Tools exposed to other agents | src/mcp/ |
| Shared contracts and types | src/shared/ |
IMPORTANT
The daemon is the single durable writer. If you find yourself adding state to the renderer that must survive a restart, it belongs in the daemon instead.