Option A — install script (downloads the latest release). It detects the machine's OS and architecture, picks the matching release asset, and installs to /usr/local/bin:
Control server to enrol with. Omit to install the binary only.
--authkey <key>
SPECTRE_AUTHKEY
—
Auth key from the UI. Omit and the agent prints a code to approve.
--tag <vX.Y.Z>
TAG
latest release
Which release to install.
--bin-dir <dir>
BIN_DIR
/usr/local/bin
Where to put the binary.
Prefer SPECTRE_AUTHKEY over --authkey: a flag is visible in ps to every user on the machine for as long as the command runs. The script passes the key to the agent through the environment either way, so the agent's own process never exposes it.
Supported assets are linux/amd64, linux/arm64, darwin/amd64, and darwin/arm64. A 32-bit Pi (armv7l) has no published build — use Option B.
Option B — build it here and copy it to a machine on your network. This is the usual path for a home lab or LAN box: cross-compile on your dev machine, scp the binary over, and enrol it against your local server. No published release needed.
1. Find the target's OS and architecture. Go cross-compiles to a specific GOOS/GOARCH pair, so you need to know what the target machine is. If you can reach it, ask it:
cd agentGOOS=linux GOARCH=arm64 go build -o spectre-agent . # e.g. a 64-bit Raspberry Pi
CGO_ENABLED=0 is the default here, so the binary is static and has no libc dependency — it runs on any machine of that arch, including minimal or musl-based distros.
3. Copy it to the target and install it.scp cannot write to /usr/local/bin directly (it's root-owned), so land it in a writable spot first, then move it into place:
4. Enrol it against your server. On the target, point --host at your control server. On a LAN without TLS that's plaintext ws:// to the server's IP and the published proxy port; the agent appends /api/agents/register itself:
# Interactive — prints a code to approve in the web UI:sudo spectre-agent up --host ws://<server-lan-ip>:3000# Or with an auth key from the UI:sudo spectre-agent up --host ws://<server-lan-ip>:3000 --authkey sk_...
ws:// vs wss://: a bare host or wss:// uses TLS. Plaintext ws:// to anything other than localhost logs a loud warning, because terminal I/O and the device key travel unencrypted. On a trusted home LAN that may be an acceptable trade-off; over the internet, always put the server behind a TLS proxy and use wss://.
To rebuild after pulling changes, repeat steps 2–3 — the installed binary is replaced in place and the stored device key in ~/.spectre-agent (or /var/lib/spectre-agent) is reused, so there's no need to re-enrol.
# With an auth key from the UI (non-interactive):sudo spectre-agent up --host wss://spectre.example.com --authkey sk_...# Or interactively — prints a code to approve in the UI:sudo spectre-agent up --host wss://spectre.example.com
up enrols the machine, stores the device key, installs a service, and starts it. The enrollment credential is never written into the service file: the agent enrols once, up front, and the service runs with only --host.
The key is written to the service's own state directory (/var/lib/spectre-agent, or SPECTRE_AGENT_HOME if you set it) and handed to the account the service runs as — the same place the service reads it from. A machine you had already enrolled by hand keeps its device key: up carries it over rather than enrolling the machine a second time.
TLS: a bare host (--host spectre.example.com) defaults to wss://. Plaintext ws:// to anything other than localhost logs a loud warning — terminal I/O and the device key would be exposed to the network.
spectre-agent status # running state, pid, device id, service statussudo spectre-agent up --host ... # enrol, install as a service, and startspectre-agent update # upgrade to the latest release, in placesudo spectre-agent down # stop and remove the servicesudo spectre-agent down --purge # also delete the device keyspectre-agent run --host ... # run in the foreground (Ctrl+C to stop)
Two ways: from the dashboard, or on the machine itself.
From the dashboard. The control server checks GitHub hourly for the newest
agent release. Any connected machine running something else shows an
Update to vX.Y.Z button in the machine list. Clicking it sends the request
down that machine's existing socket; the agent downloads the release, swaps its
binary, and exits — systemd's Restart=always then starts it again on the new
build. The button reads Updating… until the machine reconnects reporting the
new version, which is the real confirmation it worked.
The agent exits rather than calling systemctl restart on itself: a service
restarting its own unit needs privileges the service account does not have, and
the exit achieves the same thing for free.
Where the binary lives. Replacing a running binary means rename(2) —
writing in place fails with ETXTBSY — and rename checks write permission on
the directory, not the file. A sudo install puts the binary in root-owned
/usr/local/bin but runs the service as the invoking user, which is exactly the
combination that cannot replace itself. So up moves the binary to
/var/lib/spectre-agent/bin/spectre-agent, hands that directory to the service
account, and leaves a symlink at the original path:
One binary, still on PATH under its usual name, and the CLI and the service
can never drift onto different versions. Nothing else about the host changes —
no extra units, no sudoers entries. A service already running as root, or one
whose binary sits somewhere it can already write, is left where it is.
down --purge deletes that state directory, so it copies the binary back to
its original path first rather than leaving a dangling symlink.
The button only appears on connected machines — the request travels over the
live socket, so an offline machine has nowhere to receive it. If the server
cannot reach GitHub, no button appears at all rather than a guess. A failed
update is reported back and shown on that machine's row, so it does not sit
spinning.
On the machine.
spectre-agent update asks GitHub for the newest release, downloads the build
for the machine's OS and architecture, replaces the binary in place, and
hands the running agent over to it — no sudo required.
spectre-agent update --check # is there a newer release? changes nothingspectre-agent update # install the latestspectre-agent update --tag v1.2.3 # pin a version, or roll backspectre-agent update --force # reinstall the version already running
It does not re-enrol. The device key lives in the agent's state directory,
which the update never touches, so the machine keeps its identity and needs no
new auth key — it reconnects as the same device it already was.
Notes:
Neither kind of update needs root.up has already put the binary
somewhere the service account owns (see Where the binary lives above), and
the restart is a signal, not a systemctl call: the CLI sends SIGTERM to
the running agent, which shuts down cleanly, and Restart=always starts the
new binary. Both the CLI and the control server take that route.
If the binary is somewhere you cannot write — a stock install where the
service runs as root — the command stops before downloading anything and
tells you to re-run with sudo.
The downloaded binary is run once before it is installed. A truncated
download or a wrong-architecture asset fails there, leaving the working
binary in place.
The swap is a rename within one directory, so it is atomic — an interrupted
update never leaves a half-written agent behind. Replacing the file of a
running process is safe on Linux and macOS; the old process keeps running
until the service restarts.
Unauthenticated GitHub API calls are rate-limited per IP (60/hour). If you
hit that, pass --tag to skip the lookup.
Flag
Description
--host
Control server URL. Required. wss://host (or a bare host, which defaults to TLS)
--authkey
Auth key from the UI. Omit to approve the machine interactively
When tmux is installed, the agent wraps each terminal in a tmux session named spectre. Close the tab and reopen it and you're back where you left off; multiple tabs share the session. This survives browser disconnects, WebSocket drops, and server restarts. Without tmux, sessions are ephemeral.