Skip to content

Installing Agent Binaries

Daniel Ellison edited this page Sep 3, 2026 · 1 revision

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.

How Kai finds a binary

Resolution order at runtime:

  1. The backend registry (/etc/kai/backends.yaml). On any protected install this is mandatory and authoritative; the entries below never apply.
  2. Legacy *_BIN env vars (CLAUDE_BIN, CODEX_BIN, GOOSE_BIN, OPENCODE_BIN), single-user dev mode only. Pi has no such var by design.
  3. 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.

The registry file

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.5

command 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.

Where discovery looks

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.

Installation recipes

Goose

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

Codex CLI

# 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/codex

A 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.

Claude Code

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 first

OpenCode

Same 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.

Pi

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 install

Discovery re-runs, the registry regenerates, and the sudoers rules follow.

Multi-user reachability

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.

Fixing a moved or missing binary

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."

  1. Install (or symlink) the binary at a discoverable location from the table above.
  2. 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.

Related pages

Clone this wiki locally