Skip to main content

Developers

PreviewAvailable on: WindowsShips in the preview channel only. Not a stable release.

How Harmony is built: six artifacts, the transports between them, and where to start reading.

Harmony 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

#ArtifactBuilt byEntryOwns
1Electron mainnpm run build:mainsrc/main/index.tsWindow and tray lifecycle, PTY ownership, privileged services
2RendererVitesrc/rendererThe visible desktop UI
3Preload bridgeVitesrc/preload/preload.tsNamespaced IPC surfaces exposed to the renderer
4Daemonnpm run build:daemonsrc/daemon/index.tsSessions, orchestration store, tasks, artifacts, A2A, channels. Survives app close
5CLInpm run build:clisrc/cli/index.tsnala / wmux binaries; also hosts the TUI
6MCP servernpm run build:mcpsrc/mcp/index.tsTools 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

PathMechanism
Renderer ↔ mainElectron IPC, schema-validated
Main ↔ daemonNamed pipe / unix socket
CLI / MCP / external ↔ mainPipe server with an RPC router
TUI ↔ daemonDirect daemon RPC client
Peer machinesLanLink

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

CommandScope
npm testParallel suite plus runtime suite
npm run test:parallelEverything except runtime-tagged tests
npm run test:runtimeRuntime-tagged tests
npm run test:harnessHarness suite
npm run test:rigSimulation rig
npm run test:packaged-self-testVerifies 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 PTYssrc/daemon/sessionHost/, src/main/pty/
The desktop UIsrc/renderer/
The terminal agentsrc/tui/
A nala subcommandsrc/cli/commands/nala/
Tools exposed to other agentssrc/mcp/
Shared contracts and typessrc/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.