

Canopy
Installation
Last updated
Requirements
canopy orchestrates AI agent tools — it doesn’t ship one. You need at
least one supported platform installed and authenticated first (a
terminal AI CLI like claude/codex/gemini, or an editor that ships
one, like Cursor/Antigravity/Qoder). See the
Requirements section in the README for the
full platform list and how to check a platform is usable.
Quick install
Linux / macOS:
curl -fsSL https://raw.githubusercontent.com/UniverLab/harness-canopy/main/scripts/install.sh | sh
Via cargo
cargo install harness-canopy
The crate is harness-canopy; the installed binary is canopy.
Available on crates.io.
From source
git clone https://github.com/UniverLab/harness-canopy.git
cd harness-canopy
cargo build --release
# Binary at target/release/canopy
First-time setup
canopy setup
The interactive wizard detects installed AI CLI platforms from a
GitHub-hosted registry, configures binary paths, model flags, headless
modes, environment variables and temperature units, and generates
~/.canopy/config.toml. Running canopy with no arguments triggers
setup automatically if it has never run.
If no supported platform is on PATH, setup is meant to say so, list
what it supports, and finish with zero platforms configured. It currently
doesn’t reach that message — see
Requirements: If no platform is installed
in the README for the verified failure and where it comes from.
The registry itself keeps changing after setup, and the TUI only refreshes it
in the background at startup, and only when the last refresh is more than 24
hours old. To apply the current registry right away, run
canopy registry refresh — see
CLI Reference: Platform registry.
Auto-update
The daemon checks GitHub releases for new stable versions every 24 hours, downloads the platform-specific binary (linux-musl, macos-darwin; x86_64/aarch64) and atomically replaces the running executable.
The check is tunable: update.check_interval_hours (default 24) sets the
interval, and update.check_enabled = false turns the check off so no
request is made. The manual canopy update command is not affected.
Running as a service
canopy daemon install-service # register with systemd/launchd
canopy daemon uninstall-service
canopy daemon install-service (and first-time canopy setup when it
installs the unit) copies BROWSER, DISPLAY and WAYLAND_DISPLAY —
only when set in the installing shell, verbatim, with whitespace quoted —
into the systemd unit. Re-running preserves previously stored values for
keys missing from the current environment. Hand drop-ins under
~/.config/systemd/user/canopy.service.d/ are never touched. canopy doctor warns when the unit lacks BROWSER.
The generated unit also sets OOMPolicy=continue and MemoryMax=80%, so a
process spawned by a graph node that exhausts memory is killed alone instead
of taking the daemon (and every other running graph) down with it. MemoryMax
is a percentage of the machine’s physical memory, so the unit stays portable.
canopy doctor warns when an installed unit still keeps OOMPolicy=stop
(the systemd default for a unit written before this), naming the reinstall
that fixes it.
canopy daemon start and canopy daemon restart compare the unit’s
ExecStart with the canopy binary running the command. When it names a
different or already-removed binary — a unit left behind by an older
cargo install, or one the installer moved — the unit is re-rendered for the
current binary, keeping the PATH and BROWSER/DISPLAY/WAYLAND_DISPLAY
values it already carries, followed by systemctl --user daemon-reload and
systemctl --user reset-failed. canopy daemon restart then restarts the
installed unit with a single systemctl --user restart.
When the daemon still never answers its port, the error names the unit’s state
rather than only pointing at the logs: an ExecMainStatus of 203 reports
ExecStart binary not executable or missing, and any other value reports
ActiveState, SubState, ExecMainStatus and Result verbatim.
Data directory
All state lives under ~/.canopy/:
| Data | Location | Format |
|---|---|---|
| Structured data | ~/.canopy/background_agents.db |
SQLite (WAL mode) |
| Database backup | ~/.canopy/background_agents.db.backup |
SQLite, written by the daily health routine |
| Vector embeddings | ~/.canopy/rag/vectors.lancedb |
LanceDB |
| Seed identities | ~/.canopy/seeds/<id>/identity.toml |
TOML |
| Terminal history | ~/.canopy/terminals/<name>/history.toml |
TOML |
| Configuration | ~/.canopy/config.toml |
TOML |
| Agent logs | ~/.canopy/logs/<id>.log |
Text (5 MB rotation) |
| Daemon log | ~/.canopy/daemon.log |
Text |
Each [[clis]] entry in ~/.canopy/config.toml may also carry the
infra-retry budget for every agent node/member dispatched on that
platform: infra_retry_limit (default 2), infra_crash_max_seconds
(default 60) and infra_backoff_seconds (default 30). More specific
ensemble/member/node overrides win; see docs/graphs.md.
An entry may also declare a binary identity check: identity_args (an
argv array, e.g. ["--version"]) plus identity_pattern (a regex the
combined stdout+stderr must match). When both are set, every surface that
resolves the binary — agent_probe, node dispatch, agent_add /
agent_watch validation and canopy doctor — runs
<binary> <identity_args> once per path+mtime (5 s timeout) and reports
the platform as not installed
(<platform>: '<path>' is not <name> (identity check failed: …)) on a
mismatch or timeout, so a wrong program answering to the name (e.g. the
Debian blackbox window manager) is never invoked as if it were the CLI.
A platform whose CLI has no headless/print mode (e.g. freebuff) declares
headless = false. Such a platform stays available for live interactive
sessions and appears in canopy doctor and the new-session picker marked
interactive only, but canopy refuses to use it anywhere a headless run
is required: graph_add_node / graph_update_node, ensemble members,
agent_add, and agent_probe (reported interactive_only, nothing
spawned) all reject it with
platform '<name>' is interactive-only (no headless mode), and
agent_models returns an empty list with that reason instead of
enumerating. graph_preflight flags every existing node that uses one.
Omit the field — the default is true — and the platform behaves exactly
as before.
Health check
canopy doctor
Checks the data directory, database, config, harnesses, RAG status, file watchers, daemon process, registry connectivity and auto-update health.
The database has a daily routine: a full integrity check and foreign-key
check, then one verified backup written with VACUUM INTO, replacing the
previous backup. It runs only while the daemon is idle (no graph running, no TUI
attached). A busy daemon defers it, and after 72 hours of deferral it runs
anyway. canopy doctor reports the routine’s last outcome, the backup and
its age, and how many times it has been deferred, and warns when it has never
completed. To run the check and backup now:
canopy daemon health-check
For platforms, it confirms each configured binary is present and
executable on PATH, and — when the entry declares identity_args /
identity_pattern — that the binary really is that program; a wrong
program is reported as not installed. It does not check that you’re
logged in. Check that per platform, e.g. claude auth status or
codex login status.