Appearance
Install
Burrowee ships four installable components, each with its own one-line installer on the public release channel at release.burrowee.com. Every download is verified end-to-end before a single byte runs: minisign signature → SHA-256 checksum → unzip → a verified inner installer.
| Component | One-liner | Binaries installed |
|---|---|---|
| CLI — the client you connect from | curl -fsSL --proto '=https' --tlsv1.2 https://release.burrowee.com/cli/install.sh | sh | burrowee, burrowee-cli, burrowee-cli-updater |
| Gateway — the machine you connect to | curl -fsSL --proto '=https' --tlsv1.2 https://release.burrowee.com/gateway/install.sh | sh | burrowee, burrowee-gateway, burrowee-gateway-cli, burrowee-gateway-console, burrowee-register, burrowee-gateway-updater |
| Edge — your own self-hosted relay | curl -fsSL --proto '=https' --tlsv1.2 https://release.burrowee.com/edge/install.sh | sh | burrowee, burrowee-edge, burrowee-edge-cli, burrowee-edge-updater |
| Agent — the AI-agent identity client | curl -fsSL --proto '=https' --tlsv1.2 https://release.burrowee.com/agent/install.sh | sh | burrowee, burrowee-agent |
Each component bundles the burrowee dispatcher (see below), so after any one install the bare burrowee command works. Per-component walkthroughs: CLI · Gateway · Edge.
Burrowee also has a relay component — the shared relay fleet Burrowee operates for you — but it has no public installer; you never install it yourself. See What is Burrowee for what it does.
What the installer does
The install.sh you pipe to sh is the trust anchor. It never runs an unverified byte:
- Detects your platform — macOS (darwin) or Linux, arm64 or amd64. Anything else aborts.
- Preflight — installs any missing OS dependencies (
minisign,unzip,curl,ca-certificates; plusnginxand its stream module for edge) via your package manager, using root if available. Non-fatal on its own — the verification steps below are the real backstop. Skip it withBURROWEE_SKIP_PREFLIGHT=1(edge:BURROWEE_SKIP_NGINX=1to skip only the nginx group). - Resolves the release — the latest published GitHub release for that component, unless you pin a version.
- Downloads the release zip plus
SHA256SUMS.txtandSHA256SUMS.txt.minisig, logging the exact URL of every attempt. See If GitHub is unreachable for what happens when the primary download fails. - Verifies the minisign signature over the sums file, against a public key baked into the installer itself — no key is fetched over the network.
- Verifies the zip's SHA-256 against the now-trusted sums file.
- Unzips and runs the inner installer, which installs the binaries. The gateway and edge install to the root-owned exec root
/usr/local/burrowee/bin— one machine-owned tree beside its config and data roots, and not on yourPATH(see Put the exec root onPATH) — and lay down system-level service units (com.burrowee.<component>+com.burrowee.<component>.updaterin/Library/LaunchDaemonson macOS; system units in/etc/systemd/systemon Linux), so the daemons start at boot with no login. The CLI and agent install to$PREFIX/bin— by default$HOME/.local/bin.
Any failure at any step aborts before anything is installed.
Installs and updates also run the component's migration ladder: a pre-0.2.0 per-user tree is adopted into the machine-owned roots (sourcing only from the running user's home), and stale copies of the binaries that would shadow the fresh ones are swept — pre-0.2.0 per-user copies under ~/.local/bin (the sweep asks per file), and the 0.2-era binaries and symlinks an earlier installer left in /usr/local/bin. After removing a shadowing copy it reminds you to clear your shell's stale command cache (hash -r). The installer never writes units or swaps binaries unless the migration can complete.
An install whose bin directory is not on your PATH ends with a Next steps block: the line that adds it, in the syntax of your login shell, and the file that makes it permanent. It is printed, never applied — no installer edits your shell's startup files, and the old BURROWEE_NO_PATH_EDIT switch is gone with the edit it used to suppress. The CLI and agent installers check PATH for ~/.local/bin and print the block only when it is missing; the gateway and edge installers run under sudo, cannot see your PATH, and always print it for the exec root — see Put the exec root on PATH. On a fresh interactive install the gateway installer also offers to set the component up immediately — see the per-component pages for that first-run prompt.
Put the exec root on PATH
The gateway and edge binaries live in /usr/local/burrowee/bin, and no OS puts that directory on a shell's PATH by default. Until you add it, burrowee is not a command you can type. (The install still works: the service units name the binaries by absolute path, and so does the dispatcher when it resolves a system component.) Two ways through:
- Type the full path —
/usr/local/burrowee/bin/burrowee gateway statusworks from any shell, with nothing added anywhere. - Run the lines the installer printed. They are the last thing a successful install prints, rendered for the login shell of the user who ran it (zsh, bash and fish get their own syntax; any other shell gets the POSIX line and no file). If the terminal has scrolled away, this is the block (the installer prints your actual home directory where this shows
$HOME):
==> Next steps
burrowee's commands are in /usr/local/burrowee/bin, which is not on your PATH.
Add it to this shell now:
export PATH="/usr/local/burrowee/bin:$PATH"
Make it permanent:
echo 'export PATH="/usr/local/burrowee/bin:$PATH"' >> "$HOME/.zprofile"
Then: burrowee helpThe first line covers the shell you are in; the second covers every login shell from now on. Which file, and which syntax, depends on the shell and on the component:
| Shell | Add it to this shell now | Make it permanent (gateway: your own profile) |
|---|---|---|
| zsh | export PATH="/usr/local/burrowee/bin:$PATH" | echo 'export PATH="/usr/local/burrowee/bin:$PATH"' >> "$HOME/.zprofile" |
| bash | export PATH="/usr/local/burrowee/bin:$PATH" | the same echo … >> into ~/.bash_profile on macOS, ~/.profile on Linux |
| fish | set -gx PATH /usr/local/burrowee/bin $PATH | mkdir -p ~/.config/fish && echo 'fish_add_path --global /usr/local/burrowee/bin' >> ~/.config/fish/config.fish |
The edge installer runs as root for a component every account on the host shares, so its permanent step goes in the system-wide profile instead of yours: echo '/usr/local/burrowee/bin' | sudo tee /etc/paths.d/burrowee on macOS, echo 'export PATH="/usr/local/burrowee/bin:$PATH"' | sudo tee /etc/profile.d/burrowee.sh on Linux (on Linux, plus a /etc/fish/conf.d/burrowee.fish line if your own shell is fish, which reads neither; on macOS fish picks up /etc/paths.d itself). The "add it to this shell now" line is the same as the gateway's.
Two things to know about the permanent step: the per-user >> form appends, so run it once — a second run writes the entry twice (the edge's sudo tee form overwrites, and is safe to repeat); and a profile is read by the next login shell, so an already-open terminal keeps its old PATH until you run the first line in it (or open a new one). You never need hash -r for this — the directory was never on PATH, so no shell has a stale entry for it.
Upgrading — the same one-liner family
Every public component also serves a hosted upgrade one-liner:
sh
curl -fsSL https://release.burrowee.com/<component>/upgrade.sh | sh [-s -- <floor>]It exists for cli, gateway, edge, and agent, and is the same trust anchor as install.sh under a mode switch — same baked-in key, same minisign + SHA-256 verify path, same version floor. It does everything a plain install does (resolve → verify → place binaries), then force-runs the component's migration pass from the same verified kit. Day to day you won't need it: burrowee <component> update goes through the component's own updater (see CLI updates).
What the forced migration pass runs
The routine installer walks the migration ladder gated: it compares the version this host last recorded against each rung and skips what looks already done. That gate compares only MAJOR.MINOR.PATCH, and it trusts the recorded version — so there are states it cannot see:
- the build changed without a version bump (same semver, different
.date.shabuild stamp), so the host looks already migrated; - the host's recorded version anchor is missing or wrong;
- the host already reports a newer version than a rung it never actually ran — e.g. a machine that got 0.2.1 binaries onto disk without going through the installer, so the 0.1.x → 0.2.0 state migration (system-root adoption) never happened. The gate sees "0.2.1 ≥ 0.2.0, nothing to do" and skips it forever.
upgrade.sh is the one-liner for all of these. Its migration pass ignores the recorded version entirely and forces every rung at or above the floor — by default the newest target in the installed release's own ladder (today that is every migration the release carries). For the third case, name the floor of the work that was skipped: sh -s -- 0.2.0 forces the 0.2.0-and-newer rungs even though the host already reports 0.2.1 — the recorded version cannot argue back. The list of rungs about to be re-run is printed before any of them runs. Rungs are idempotent, with one deliberate exception: under the forced pass the adoption rung may overwrite this component's identity/config from the running user's pre-0.2.0 tree (after snapshotting both destination roots to named siblings) — that is the repair for a tree adopted from the wrong source, not something to run casually.
The floor argument
The optional argument (e.g. 0.2.0) is the inclusive migration floor — "assume this host is below it". Rungs targeting that version or newer are forced; rungs targeting strictly older versions are treated as genuinely done and skipped. It never changes which release installs: that is always the newest the channel serves (pin an exact release with the BURROWEE_<COMP>_VERSION environment variable instead). Absent, the floor defaults to the newest target in the installed release's own ladder — the whole shipped ladder. A floor above the ladder's newest target is refused by the release itself (exit 64, naming both values): that release carries no such migration.
sh
# repair a host that skipped older migrations — installs the newest, forces the whole shipped ladder:
curl -fsSL https://release.burrowee.com/gateway/upgrade.sh | sh
# force only the 0.2.0-and-newer migrations (older rungs stay done):
curl -fsSL https://release.burrowee.com/gateway/upgrade.sh | sh -s -- 0.2.0Exit codes: 0 installed, and the migration pass either had nothing to apply or its rungs ran clean · 1 installed, but the pass refused or a rung failed · 3 installed and ran, but a receipt was lost (safe to re-run) · 64 the command line was wrong.
Prerequisites
The installer needs curl, unzip, a SHA-256 tool (shasum on macOS or sha256sum on Linux — it detects either), and minisign. The preflight step above tries to install these for you automatically; the warning below is what you'll see if that isn't possible (no supported package manager, no root, offline, …).
minisign is required — and never auto-fetched
minisign is the trust root: it must already be on PATH from a source you trust (your package manager). The installer refuses to download a verifier over the network itself, because an unverified verifier would defeat the whole signature chain. If it is missing, install it and re-run:
sh
brew install minisign # macOS — installs Homebrew first if needed:
# /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
apt-get install minisign # Debian/Ubuntu (or your distro's package manager)Upstream: https://github.com/jedisct1/minisign
Verify by hand
The signing public key is mirrored at https://release.burrowee.com/burrowee-release.pub. To verify a release download yourself:
sh
minisign -V -P "$(cat burrowee-release.pub)" \
-m SHA256SUMS.txt -x SHA256SUMS.txt.minisig
shasum -a 256 -c --ignore-missing SHA256SUMS.txt # or sha256sum on LinuxA failed signature check means the bytes are untrusted — do not install them.
If GitHub is unreachable
Every download is logged — the exact URL of each attempt is printed as it happens — so a stalled or blocked network is easy to diagnose. The primary GitHub download gets a tight 30-second cap; if it fails, the installer retries the same download through a rotating set of GitHub HTTP mirrors (gh-proxy.org, cdn.gh-proxy.org, v6.gh-proxy.org, gh-proxy.com by default) before giving up. Set BURROWEE_GH_PROXY="<mirror> <mirror> …" to use your own list, or BURROWEE_GH_PROXY= (empty) to disable mirrors entirely. Mirrored bytes still go through the same minisign + SHA-256 verification, so a compromised mirror can't slip in tampered bytes undetected.
If GitHub and every mirror are unreachable, and this machine already has an authorized burrowee (see burrowee login), the installer falls back to a signed download URL from the console instead. Without an authorized burrowee on the machine, that fallback isn't available and the installer fails with a clear message — retry once GitHub or a mirror is reachable.
Supported platforms
| OS | arm64 | amd64 |
|---|---|---|
| macOS (darwin) | ✓ | ✓ |
| Linux | ✓ | ✓ |
Windows is not supported.
The burrowee dispatcher
Every component zip ships burrowee — the universal entry command. It is a near-zero-logic dispatcher: burrowee <component> … finds the installed binary for that component and replaces itself with it. The root-owned system components — gateway, edge, relay, register — are resolved at the exec root /usr/local/burrowee/bin by absolute path, never on PATH and never in /usr/local/bin; the per-user ones (cli, agent, console) are found on PATH only. So burrowee gateway … works whether or not you have put the exec root on PATH — the only command that needs it is the bare burrowee itself. Anything that isn't a component word falls through to the CLI untouched, so burrowee connect … runs burrowee-cli connect ….
| Word | Runs | What it is |
|---|---|---|
cli | burrowee-cli | client tunnels (connect, ssh, daemon, relays) — also the fallthrough default |
gateway | burrowee-gateway | the home gateway daemon + local console |
relay | burrowee-relay | the system relay server |
edge | burrowee-edge | self-hosted edge relay |
console | burrowee-console | the cloud control plane server |
register | burrowee-register | register a local service with the gateway |
agent | burrowee-agent | the AI-agent identity client (burrowee agent <verb> ⇔ burrowee-agent <verb>) |
Gateway and edge each split into a serving binary (the daemon — just run/version) and a -cli companion that handles every other verb, including bootstrap, status, doctor, …. The dispatcher routes each verb to the right binary automatically, so burrowee gateway bootstrap … and burrowee gateway status both just work — burrowee gateway update / burrowee edge update route through the companion to that component's updater; burrowee <component> cli … also reaches the companion explicitly if you ever need to (e.g. burrowee edge cli doctor, which is the same as burrowee-edge-cli doctor).
burrowee --help prints this component table; burrowee <component> --help shows that component's commands. Help forwards, too: burrowee help <verb> (and burrowee --help <verb> / burrowee -h <verb>) runs <verb> --help, so the binary that owns the verb prints its own scoped page — and the same rewrite works one level down, burrowee <component> help <verb>. Asking for a component that isn't installed on the machine exits with code 127 and prints which components are installed there, plus where to install the missing one. (Exit 126 means the binary was found but could not be executed.)
burrowee version (or --version) prints just the dispatcher's own version. burrowee <component> version prints that component's. burrowee cli version goes further and prints a small versions table — the dispatcher plus the cli's installed-vs-currently-running state, flagging drift — handy right after an install or update to confirm everything landed.
Pin a version
Each installer reads a version-pin env var; the value is the release tag (<comp>/<stamp>):
| Component | Env var |
|---|---|
cli | BURROWEE_CLI_VERSION |
gateway | BURROWEE_GATEWAY_VERSION |
edge | BURROWEE_EDGE_VERSION |
agent | BURROWEE_AGENT_VERSION |
sh
BURROWEE_CLI_VERSION=cli/v0.1.0.2026.06.08.7dbdd72 \
curl -fsSL https://release.burrowee.com/cli/install.sh | shUnset → the installer resolves the newest release for that component.
Choose the install location
For the CLI and agent, set PREFIX to change the install root (binaries go to PREFIX/bin; the default is $HOME/.local, i.e. binaries in ~/.local/bin):
sh
PREFIX=/usr/local curl -fsSL --proto '=https' --tlsv1.2 https://release.burrowee.com/cli/install.sh | shThe gateway and edge have exactly one install location — the root-owned exec root /usr/local/burrowee/bin, beside their config root /usr/local/burrowee/etc/<component> and data root /usr/local/burrowee/var/<component> — because root-run services must never exec a binary an unprivileged user can rewrite, and one machine-owned tree gives every root-run path a single ancestor chain to verify. Nothing is written to /usr/local/bin: the directory does not exist on a clean Apple-silicon Mac and belongs to Homebrew on an Intel one, which is why the exec root has to be added to PATH by hand. Their installers refuse a set PREFIX (loudly, before anything is written) rather than honoring or silently overriding it.
To uninstall a component's binaries, re-run its installer with BURROWEE_UNINSTALL=1.
macOS notes
Gatekeeper & code signing
Every release is verified before anything runs — the installer checks a minisign signature over SHA256SUMS.txt and the SHA-256 of the downloaded zip (see Verify by hand). The macOS binaries themselves are ad-hoc signed, not Developer ID signed or notarized — but since they arrive via curl | sh rather than a browser download, macOS never attaches the com.apple.quarantine attribute in the first place, so Gatekeeper has nothing to block. The installer also strips the attribute defensively, in case a binary reaches you by another route (a manual download, a build you made yourself). If you ever move a binary onto PATH by hand and macOS refuses to run it, remove the attribute yourself:
sh
xattr -d com.apple.quarantine ~/.local/bin/burrowee-cli