-
Notifications
You must be signed in to change notification settings - Fork 18
Installing Agent Binaries
Kai works with five agent backends: Claude Code, Codex CLI, Goose, OpenCode, and Pi. Each one is a separate binary that Kai spawns as a subprocess. Your job as operator is simple: install the binary somewhere the right accounts can reach, then re-run sudo make install. The installer discovers each binary and records its absolute path in /etc/kai/backends.yaml, the command-path authority the runtime and the sudoers rules are both generated from, so the spawned binary and the sudo permission can never disagree.
Resolution order at runtime:
-
The backend registry (
/etc/kai/backends.yaml). On any protected install this is mandatory and authoritative; the entries below never apply. -
Legacy
*_BINenv vars (CLAUDE_BIN,CODEX_BIN,GOOSE_BIN,OPENCODE_BIN), single-user dev mode only. Pi has no such var by design. -
PATH (
shutil.which), then the bare command name as a last resort.
The registry validates every command path: it must be absolute, must exist as an executable file, and must not be group or other writable. A binary that fails validation is a startup error naming the offending path, which is the failure you will see if, say, a shared install directory is group-writable.
sudo make install writes /etc/kai/backends.yaml (root-owned) from what discovery found. Shape:
version: 1
default_backend: claude
backends:
claude:
command: /opt/homebrew/bin/claude
driver: claude
runtime: local_process
codex:
command: /usr/local/bin/codex
driver: codex
runtime: local_process
allowed_models:
- gpt-5.6-sol
- gpt-5.5command is the discovered absolute path. driver must match the backend id and runtime is always local_process; both exist for forward compatibility. allowed_models optionally restricts which model IDs the backend accepts. Do not hand-edit command paths; re-run sudo make install and let discovery regenerate them. Tests and unusual layouts can point the runtime at a different registry file with the KAI_BACKENDS_YAML env var.
Per backend, in order (the service user's home is checked where the upstream installer targets ~/.local/bin):
| Backend | Checked locations, in order |
|---|---|
| Claude Code | service user's ~/.local/bin/claude, /opt/homebrew/bin/claude, PATH |
| Codex CLI |
/usr/local/bin/codex, /opt/homebrew/bin/codex, PATH |
| Goose |
/opt/homebrew/bin/goose, PATH |
| OpenCode | service user's ~/.local/bin/opencode, PATH |
| Pi |
/opt/homebrew/bin/pi, /usr/local/bin/pi, PATH |
Note what the first column implies: for Claude Code and OpenCode, running the upstream per-user installer as the service user is a first-class supported layout, not an anti-pattern. Discovery prefers exactly that path.
Both installation methods land in a shared location; nothing else to do:
# macOS
brew install block-goose-cli # /opt/homebrew/bin/goose
# Linux
curl -fsSL https://block.github.io/goose/install.sh | bash # /usr/local/bin/goose# macOS
brew install codex # /opt/homebrew/bin/codex
# macOS and Linux via npm: verify the global prefix is a system path first
npm config get prefix # should be /usr/local or /opt/homebrew
npm install -g @openai/codexA per-user npm prefix (~/.npm-global) puts the binary where other accounts cannot reach it; point the prefix back at a system path or symlink the binary into /usr/local/bin.
Either install globally or run the upstream installer as the service user:
# Global (macOS)
brew install --cask claude-code # /opt/homebrew/bin/claude
# Or per service user: run the native installer while logged in as that
# account; discovery finds ~/.local/bin/claude firstSame choice as Claude Code: the upstream installer targets ~/.local/bin/opencode, which discovery finds when it belongs to the service user, or symlink to /usr/local/bin/opencode for a shared install.
Install to a system path (/opt/homebrew/bin/pi or /usr/local/bin/pi). After installing, authenticate inside Pi (/login) as each OS user who will run it; subscription providers only work through that flow.
Whatever you install or move, finish with:
sudo make installDiscovery re-runs, the registry regenerates, and the sudoers rules follow.
With per-user OS isolation, Kai spawns each person's agent as their os_user via sudo -H -u, and the generated sudoers file grants the service user exactly those spawns. The rules take this shape, one block per target user:
kai ALL=(alice) CWD=* SETENV: NOPASSWD: /opt/homebrew/bin/claude
kai ALL=(alice) CWD=* SETENV: NOPASSWD: /usr/local/bin/codex
kai ALL=(alice) CWD=* SETENV: NOPASSWD: /usr/local/bin/opencode
kai ALL=(alice) CWD=* SETENV: NOPASSWD: /opt/homebrew/bin/goose
kai ALL=(alice) CWD=* SETENV: NOPASSWD: /opt/homebrew/bin/pi
kai ALL=(alice) NOPASSWD: /bin/kill
All five backends get a rule regardless of which one is active, plus the kill rule for cross-user process cleanup. Because the rule pins one absolute path per binary, every target account must be able to traverse to and execute that path. System paths satisfy this automatically; a binary under the service user's home additionally needs execute (traversal) permission on the intervening directories, which the install documentation covers.
The symptom is a startup or spawn error naming the backend, or make install reporting that a configured backend was not found with a message like "Install claude globally and rerun make install so /etc/kai/backends.yaml is regenerated."
- Install (or symlink) the binary at a discoverable location from the table above.
- Re-run
sudo make install. Discovery records the new path; sudoers regenerates to match.
In single-user dev mode there is no registry; set the matching *_BIN env var or put the binary on PATH.
- Multiple Backends - what each backend is and how to configure it
- Multi-User Setup - per-user OS isolation
- Protected Installation - directory layout and permissions