Build from source
Build Legato from source to work on it, or to run a version that is not released yet. One command builds the legato binary; a second builds the web UI into it first.
Prerequisites
Section titled “Prerequisites”- Go 1.25 or later, the version in
go.mod. - Task, from taskfile.dev. Every command on this page is a Task target in
Taskfile.yml. - Node.js and pnpm, for the web UI and this documentation site. The CI uses Node 24 and pnpm 11.
- tmux, to start agents from the board. You can build and run Legato without it.
- golangci-lint, for
task lintandtask check.task install:toolsinstalls it.
You do not need a C compiler. Legato builds with CGO_ENABLED=0 into one static binary, and cross-compiles with GOOS and GOARCH alone. A test fails when a dependency starts to need C. The race detector is the exception: task test:race needs C.
Clone the repository and build:
git clone https://github.com/cpave3/legato.gitcd legatotask buildtask build writes ./legato with the TUI and the API, but no web UI. Without the web UI, legato serve answers API requests only.
To include the web UI, build it first, then the binary:
task build:fulltask build:full runs task web:build, which installs nothing: run pnpm install in web/ once before your first build. The web UI is built into internal/server/static/dist/, and the Go build embeds that folder in the binary.
Install the binary
Section titled “Install the binary”task installinstalls a copy oflegatointo$GOPATH/bin. It embeds the web UI only if you built it before.task linkbuilds./legatoand links it into$GOBIN, or$GOPATH/binwhenGOBINis not set. Each latertask buildupdates the linked binary in place.
Version and updates
Section titled “Version and updates”A build from source reports its version as dev:
./legato versionA dev build never updates itself, so a checkout and an installed release can run side by side. See Updates.
Run with hot reload
Section titled “Run with hot reload”Hot reload restarts Legato each time you rebuild its binary. Build once, then start the TUI or the web server under the supervisor:
task buildtask run:hot # the TUI, with its web server if configuredtask server:hot # the web server alonetask server:hot listens on port 3080, or on LEGATO_SERVER_PORT. These run ./legato --hot-reload and ./legato --hot-reload serve.
- Change the code.
- Run
task build, ortask build:fullwhen you changed the web UI. - The supervisor stops the old process, waits for it to shut down, and starts the new binary with the same arguments.
A build that fails leaves the running process alone. The TUI loses its place on each restart. After task build:full, open web pages see the new web UI and reload once.
Under hot reload, the web UI’s Settings shows Rebuild and hot reload, which runs task build:full in the checkout. Use it to rebuild from a phone. Its output goes to .legato/run/hot-reload-build.log.
Hot reload is for development in the foreground. It is not a way to run Legato as a service.
task test # all Go teststask test:race # all Go tests with the race detectortask test:cover # all Go tests, with coveragetask vet # go vettask lint # golangci-linttask check # build, test, vet and linttask web:test # the web UI's testsRun task test:race when you change the event bus or other code that runs at the same time.
Go tests use real SQLite databases in temporary folders, not mocks. Tests that need tmux skip when it is not installed.
Work on the web UI
Section titled “Work on the web UI”The web UI is a React and Vite app in web/. Run a Legato web server on port 3080, then start the Vite dev server:
task web:devThe dev server reloads as you edit, and sends API and WebSocket requests to localhost:3080.
In web/, check types with pnpm exec tsc -b, the same check the build runs.
Work on these docs
Section titled “Work on these docs”The documentation site is a Starlight site in site/. Both tasks install its dependencies first when they changed:
task docs:dev # serve the site with live reload at http://localhost:4321/legato/task docs:build # build it into site/dist; fails on a broken linksite/README.md is the style guide and the map of pages.
Two pages come from the code. The keyboard reference is written from the TUI’s key bindings by go test ./internal/tui/hotkey -update. go test ./config fails when a setting is missing from the configuration reference.
Other parts of the repository
Section titled “Other parts of the repository”Android app
Section titled “Android app”A native Kotlin app in mobile/. It needs a JDK from 17 to 24 and the Android SDK, but no Node.
task mobile:test # unit teststask mobile:apk # release APKtask mobile:install # build, install on the connected device, and startSee mobile/README.md for signing.
Desktop app
Section titled “Desktop app”A Tauri app in desktop/: Rust, with a React shell. It needs Rust, Node with pnpm, and on Linux the WebKitGTK 4.1, GTK 3, librsvg and libudev development packages.
task desktop:test # Rust unit tests and the TypeScript checktask desktop:dev # run it, the shell reloading as you edittask desktop:build # an AppImageSee The desktop app and desktop/README.md.
Stream Deck plugin
Section titled “Stream Deck plugin”A Go plugin for OpenDeck in streamdeck/.
task streamdeck:build # build the plugin binarytask streamdeck:package # build and assemble the .sdPlugin foldertask streamdeck:dev # build, deploy to OpenDeck and restart the pluginSee Stream Deck.
Browser extension
Section titled “Browser extension”A Chrome extension in browser-extension/. Run pnpm install there once.
task extension:build # build the unpacked extension into browser-extension/disttask extension:testtask extension:lintSee Browser extension.
Release builds
Section titled “Release builds”Releases are built by a GitHub workflow, not by hand. task release:dist VERSION=vX.Y.Z builds the web UI once, then a binary stamped with that version for Linux on amd64 and arm64 and for macOS on arm64, with a SHA256SUMS file. docs/ai/updates.md describes the release workflow.
