CLI
One binary that installs the plugin, provisions an agent machine and runs the daemon.
kairoku is a single binary for mac and linux. On a laptop it installs the Claude Code plugin. On an agent machine it provisions everything the runner needs and runs the orchestration daemon as a service. On either it verifies the machine and updates itself.
Install
curl -fsSL https://gitlab.com/owds-inc/releases/kairoku/distribution/-/raw/main/install.sh | sh # mac or linuxThe script reads the stable channel pointer (channels/cli-stable in the public distribution project), picks the binary for your OS and CPU from that GitLab release, verifies its SHA-256 against the release's checksums.txt, and installs it as kairoku in /usr/local/bin when that is writable, otherwise ~/.local/bin — no sudo needed for the latter. KAIROKU_INSTALL_DIR overrides the directory. The four release assets are kairoku-darwin-arm64, kairoku-darwin-x64, kairoku-linux-x64 and kairoku-linux-arm64.
Homebrew is not a supported install path yet: the formula is promoted in the distribution project separately from the stable channel, and it has not been. kairoku update already defers to brew upgrade kairoku for a binary that lives in a Homebrew Cellar.
Then:
kairoku setupCommands
| Command | What it does |
|---|---|
kairoku setup | A wizard: install the plugin? set up the daemon on this machine? --plugin, --daemon and --all select without asking; --yes skips the prompts (alone it means --all); --repo <url> names the application repository the daemon clones as its worktree base — asked once otherwise, then remembered. --app-url and --app-token pass the daemon's API URL (default https://api.kairoku.io) and the token from Settings → Developers → Machines without prompting. |
kairoku plugin install | Registers the kairoku-marketplace marketplace from the GitLab distribution project and installs kairoku@kairoku-marketplace through the claude CLI, non-interactively. Idempotent: an existing registration or install is left alone. |
kairoku plugin update | Refreshes the marketplace and updates the plugin. |
kairoku plugin status | Installed version and whether it is enabled. |
kairoku login | Signs this machine in for MCP access: one browser hop mints an owner-scoped token named cli:<hostname>, verified with one MCP round trip before anything is written. --paste prints a link and reads the code back, for an SSH shell. |
kairoku logout | Removes the local MCP token. It does not revoke it — do that in Settings → Developers → MCP tokens. |
kairoku mcp setup | Registers kairoku mcp-bridge as the kairoku stdio MCP server in Codex and/or Claude Code (--agent codex|claude|all), so an agent uses the kairoku login token instead of its own OAuth. |
kairoku doctor | Verifies the machine and changes nothing. PASS, WARN or FAIL per check, nonzero exit on any FAIL. The plugin checks run where claude is on PATH; without it, FAIL claude installed is reported and the rest of the plugin checks are skipped. The daemon checks run where a daemon is configured, so a laptop with only the plugin can pass. |
kairoku daemon | The orchestration daemon in the foreground, until SIGTERM. |
kairoku daemon install | Linux: writes the kairoku-daemon systemd unit and starts it. The file is rewritten only when its content changes, and an unchanged running daemon is left alone. On a mac it refuses — the io.kairoku.daemon LaunchAgent belongs to kairokud, which installs its own service. |
kairoku daemon start, stop, status | Drive that service. status is nonzero when it is not running. |
kairoku daemon drain | Stops this machine claiming new work; heartbeats, event flushes and cancels carry on. |
kairoku daemon prune | Removes stale run worktrees left by a dead daemon, after listing them and asking. It never deletes a branch. |
kairoku env set|import|list|rm | The values this machine holds for a repository's runs — see kairoku.json. list prints names, never values. |
kairoku update | Replaces the binary with the release the stable channel names, after verifying its checksum. --check only reports. A Homebrew install is told to run brew upgrade kairoku instead. |
kairoku version | The version. |
kairoku enroll, kairoku daemon update, kairoku daemon migrate and setup --link act on a Rust kairokud installation on the same machine; they do nothing useful without one.
Every command answers --help.
kairoku daemon is the orchestration runner — it claims Floor runs from the web app. It is not kairokud, the daemon the desktop app carries for Teammates, Slack, the integration catalog and automations. The runner's files are listed below; the desktop daemon keeps its own data under ~/.kairoku/daemon/. A kairokud can also be linked to the app as a runner in its own right — kairoku setup --daemon --link opens the app's /link page and writes its credential — and on a mac, where this CLI installs no service, the io.kairoku.daemon LaunchAgent is kairokud's. See Glossary — Two daemons.
kairoku doctor reports where kairoku-marketplace points under kairoku marketplace source: PASS for the GitLab distribution project (https://gitlab.com/owds-inc/releases/kairoku/distribution.git), WARN for the old GitHub source owds-inc/kairoku — the marketplace moved — and FAIL for a local directory or any other source. For either of the last two it prints the repair: claude plugin marketplace remove kairoku-marketplace then kairoku plugin install. It reports and never repairs; kairoku plugin install deliberately leaves an existing registration alone whatever its source, and kairoku plugin update refreshes that same registration. An absent registration is a FAIL, with kairoku plugin install as the fix. If the marketplace listing command fails, its output is malformed or source fields are missing, the check reports WARN rather than guessing.
kairoku doctor also runs a slack connection row, but only when the runner's own /status carries a slack section — which it never does in this release, so in practice this row is not seen. A group of rows runs on every machine instead, asking kairokud — the desktop app's daemon, a different program from this CLI's own kairoku daemon — over its Unix socket with newline-delimited JSON-RPC 2.0 (system.status, integration.slack.status, link.status): desktop daemon (kairokud) is PASS with its version, uptime and socket path; desktop daemon slack is PASS "connected — <team>" when Slack is connected, WARN "installed, no token — reconnect Slack in desktop Connections" when it is installed but disconnected, or WARN "not installed — connect Slack in desktop Connections" when it is not set up at all; cloud link is PASS "live to <app>" when that kairokud is linked to the app as a runner and heartbeating, and WARN otherwise — not enrolled (kairoku setup --daemon --link links it), credential refused, or enrolled but not connected. The socket is KAIROKUD_SOCKET, else $KAIROKUD_DATA_DIR/kairokud.sock, else ~/.kairoku/daemon/kairokud.sock. No socket or a stale one collapses the group to a single WARN row, and a kairokud that answers only some of the methods is WARN too, never FAIL — the desktop app is optional on a runner, so the group can't fail an otherwise healthy machine, and kairoku doctor still exits 0. See Slack.
Configuration
Everything the daemon owns lives under ~/.kairoku/:
| Path | Holds |
|---|---|
~/.kairoku/config.json | The daemon's configuration — see below. |
~/.kairoku/token.env | KAIROKU_DAEMON_TOKEN=…, mode 600 — the credential the daemon presents to the app, taken from the snippet in Settings → Developers → Machines. Written by setup --daemon, never printed. The file is a list of KEY=value lines; the daemon reads KAIROKU_DAEMON_TOKEN for its app credential, and other keys legitimately live beside it — KAIROKU_MCP_TOKEN from kairoku login, and the link credential setup --link writes for kairokud. Setup rewrites only its own key and preserves other KEY=value lines — leave entries you did not add. |
~/.kairoku/runs/<id>/ | Each run's events.jsonl and stdout.log. |
~/.kairoku/worktrees/<id>/ | Each run's git worktree while it runs. |
~/.kairoku/env/<owner>/<repo>/<profile>.env | The values kairoku env holds for a repository's runs, mode 600. |
config.json — every key optional:
{
"appUrl": "https://api.kairoku.io",
"defaultBranch": "main",
"listen": { "host": "127.0.0.1", "port": 7801 },
"maxConcurrent": 2,
"repoPath": "/home/<user>/work/kairoku",
"repoUrl": "https://github.com/acme/app.git",
"worktreesDir": "/home/<user>/.kairoku/worktrees",
"runsDir": "/home/<user>/.kairoku/runs",
"envDir": "/home/<user>/.kairoku/env",
"ports": "20000-29999",
"keepWorktreeOnFailure": false,
"defaultTimeoutSec": 3600,
"killGraceMs": 5000,
"pluginPath": "/home/<user>/.claude/plugins/cache/kairoku-marketplace/kairoku/<version>"
}appUrl is the Kairoku API the daemon dials out to; defaultBranch is the branch runs are cut from (origin/<defaultBranch>); ports is the range per-run service ports come from; pluginPath is written by setup and only worth setting by hand when the plugin lives somewhere unusual; listen is the loopback surface kairoku doctor reads (/capacity and /status, no credential — nothing arrives from off the machine any more), and a wildcard bind is refused outright. The token is never in this file: KAIROKU_DAEMON_TOKEN in the environment wins, then token.env beside the config. KAIROKU_DAEMON_CONFIG points the daemon at a different config file (its token.env is looked for next to it).
The daemon as a service
kairoku daemon install writes a service that runs kairoku daemon with an explicit PATH (the binary's directory, bun, node) and nothing secret — the daemon reads its token from token.env itself.
- linux —
/etc/systemd/system/kairoku-daemon.servicewhen passwordlesssudois available, enabled and started. Without it, a user unit at~/.config/systemd/user/kairoku-daemon.service, which runs only while you are logged in unless you enable lingering:loginctl enable-linger <user>. Logs:journalctl -u kairoku-daemon. - mac — not installed by this CLI. The
io.kairoku.daemonLaunchAgent label belongs to the Rustkairokud, which installs and supervises its own service;kairoku daemon installon a mac stops with that message, andstart,stopandstatusdrive whateverkairokudinstalled.
setup --daemon ends by asking the service it just started for /status on the loopback listener, and then prints only the manual steps still owed.
Updating
kairoku update # fetch the release the stable channel names, verify, replace the binary
kairoku plugin update # the Claude Code pluginAfter a CLI update on an agent machine, kairoku daemon install again: the service file is rewritten only if its content changed, and the running daemon is restarted only then.