Scout — install procedure¶
Scout ships as a self-extracting per-host installer you download from the Hub, copy to the target server, and run once as root. It enrolls, installs a systemd service, and starts scanning. Three steps: download → copy → run.
Requirements¶
Supported platforms & dependencies¶
| Requirement | Specification |
|---|---|
| OS | Linux — the only target the Hub generates installers for. |
| Architecture | x86-64 (amd64) — the one prebuilt binary the Hub bundles (scout-linux-amd64). |
| Kernel | ≥ 3.2 (the Go 1.25 minimum for Linux). Any systemd-capable distro clears this. |
| libc | None. The Scout binary is statically linked (pure Go, CGO off) — it runs identically on glibc distros (Debian, Ubuntu, RHEL) and musl distros (Alpine). |
| Shared libraries | None. TLS and crypto are Go's standard library — no OpenSSL needed on the target. (This is also what lets Scout still negotiate TLS 1.0/1.1 for scanning, which a modern system OpenSSL cannot — see Scanning.) |
| Init system | systemd — the installer writes /etc/systemd/system/cyphers-scout.service and drives systemctl. The unit's hardening stanza uses directives up to systemd 245 (ProtectClock); an older systemd logs an "unknown key" warning for the newer directives and skips them — the service still runs. |
| Tools for the installer script | bash, awk, and coreutils (base64, tail, id, chmod, …) — present on any standard systemd distro. The installer fetches nothing from the network; everything it installs is embedded in the script. |
| Disk | ~29 MB for the installer file in transit; ~22 MB installed binary. The data directory grows by kilobytes (keys, certs, state). |
| Privileges | root — the installer refuses to run otherwise. It writes to the install root and /etc/systemd/system, and the service runs as root (see The service). |
One platform
Scout is built and supported for Linux x86-64 only.
Network — target host → Hub¶
| Port (default) | Direction | Purpose |
|---|---|---|
| 7443 (TLS) | target → Hub | one-time enrollment (POST /enroll) |
| 4222 NATS (mTLS) | target ↔ Hub | all commands, reports, acks, CSRs after enrollment |
| 8443 (HTTP) | target → Hub | public PKI reads: CA chain/root, CRL, OCSP |
These are defaults, not fixed ports — the actual ports are whatever the Hub
was configured with, and they reach the target baked into the installer via the
--hub / --hub-nats URLs (step 1).
The installer artifact¶
The downloaded install-<name>.sh (step 1 below) is the only file you need on
the target — it carries a live one-time enrollment token (see the warning in
step 1).
1. Download the installer (Hub UI)¶
From the Hub UI: Scouts → + Enroll New Scout → name the Scout →
Download Installer. The browser downloads a single self-contained
install-<name>.sh with everything baked in: the Scout binary, the Hub
endpoints (enrollment + NATS), the CA pin, the acceptance-policy signer pin,
and a one-time enrollment token.
The downloaded file embeds a live enrollment token
The token is single-use and expires in 1 hour. Treat
install-<name>.sh as a secret in transit, and delete it after a
successful install.
The dialog's Generate Token alternative shows raw scout agent commands
instead, for hosts where the binary is already in place.
Generating from the CLI instead¶
The button runs installer/build.sh server-side; you can run the same
generator yourself — it ships in the installer/ directory of every Hub
deployment (release package, image, repo checkout) — with a token from the
Hub UI:
./build.sh --hub https://hub.corp:7443 --hub-nats nats://hub.corp:4222 \
--name web-01 --token <token-from-the-hub-ui> \
--policy-signer-fp sha256:<hex-from-the-hub-ui>
| Flag | Meaning | Example / format |
|---|---|---|
--hub |
The Hub's enrollment URL (HTTPS, on the Hub's dedicated TLS port — default 7443). A custom port goes right in the URL. | https://hub.corp:7443 |
--hub-nats |
The Hub's NATS URL (mTLS) — Scout refuses to start without it. Default NATS port 4222. | nats://hub.corp:4222 |
--name |
Display name for this Scout in the Hub UI. | web-01 |
--token |
The one-time enrollment token (issued by the Hub). | opaque string |
--ca-fingerprint |
The Hub CA pin (see Security). Optional for build.sh: when omitted it derives the pin from the Hub's CA chain, so the emitted installer is always pinned — enrollment refuses to run without one. |
sha256:ab12… |
--policy-signer-fp |
Required. The Hub's acceptance-policy signer fingerprint (see Security) — shown next to the CA fingerprint in the Hub UI's generated command. build.sh refuses to build without it: there is no auto-derive fallback, unlike --ca-fingerprint. Scout pins this fingerprint at enrollment, fetches the signer public key in the enroll reply, verifies it against the pin, and persists it as its local verifier key. |
sha256:ef01… |
--cert-dir |
A directory of certs to discover (repeatable; default /etc/letsencrypt/live) |
/etc/nginx/ssl |
--target |
An explicit host[:port] to scan (repeatable) |
example.com:443 |
--trust-store-path |
An extra Linux trust-store CA bundle file or anchor directory to collect, on top of the built-in defaults (repeatable; absolute path — a relative path refuses to start) | /usr/local/share/my-ca.crt |
--interval |
Scan interval (default 6h) |
1h |
--segment, --location |
Deployment metadata | production / us-east-1 |
Acceptance policy is mandatory
A Scout will not connect to NATS without a verified, signed acceptance
policy — there is no optional or observe-only mode. Enrollment always
delivers a signed policy plus the Hub's signer public key in the same
reply; Scout verifies the key's fingerprint against --policy-signer-fp
before trusting anything it signs. If a Scout's local policy cache is
ever wiped, recovery is automatic re-enrollment: with a still-valid
re-enrollment token (persisted in state.json) it re-enrolls and gets a
fresh policy on its own; otherwise re-run with a fresh --token (and
--policy-signer-fp) from the Hub UI.
2. Copy it to the target¶
install-<name>.sh is a single, self-contained script — the Scout binary is
embedded inside it, so there's no separate tarball and nothing to unpack by
hand. Move that one file to the target host however you normally would:
3. Run it (as root)¶
Run it from anywhere on the target (e.g. /tmp) — the paths it writes are
absolute, independent of where you launch it.
Install locations are defaults, not rules. The install root defaults to
/opt/cyphers and the data directory to <install-dir>/data; both are
overridable per run with environment variables:
sudo /tmp/install-web-01.sh # → /opt/cyphers
sudo CYPHERS_INSTALL_DIR=/srv/cyphers /tmp/install-web-01.sh # custom install root
sudo CYPHERS_DATA_DIR=/srv/scout-data /tmp/install-web-01.sh # custom data dir only
An overridden data dir propagates everywhere it matters — the installer passes
--data-dir explicitly to both the enrollment run and the systemd unit, so the
service uses the same location.
The script then:
- Extracts the embedded binary to
<install-dir>/scout(mode0755). - Creates the data dir
<install-dir>/datawith mode0700. - Writes
<install-dir>/scout.env(mode0600) holding the Hub endpoints (HUB_REST,HUB_NATS) — the single place they live (see Re-pointing at a different Hub). - Enrolls: runs the agent in the foreground until
agent.crtappears (≤ 30s), then stops it. Enrollment is aPOST /enrollover TLS to the Hub's dedicated enrollment listener (default port 7443): Scout generates its key locally, pin-verifies and captures the Hub CA from the TLS handshake, then sends a CSR + the token and receives its signed client certificate. (The CA it writes toca.crtcomes from that verified handshake, not the reply.) - Writes
/etc/systemd/system/cyphers-scout.service(reads endpoints fromscout.envviaEnvironmentFile=). The unit provisions/var/log/cyphers-scout/(LogsDirectory=cyphers-scout) for the agent's log file — see Logging. systemctl daemon-reload && systemctl enable --now cyphers-scout.
What it installs (filesystem layout)¶
Paths below use <install-dir> — default /opt/cyphers (see
Run it for the override).
| Path | Mode | Contents |
|---|---|---|
<install-dir>/scout |
0755 |
agent binary (statically linked, single file) |
<install-dir>/scout.env |
0600 |
Hub endpoints (HUB_REST, HUB_NATS) — edit + restart to re-point |
<install-dir>/cert-dirs.conf |
0644 |
extra certificate directories (one absolute path per line) — edit + systemctl reload cyphers-scout to apply; kept across installer re-runs |
<install-dir>/data/ |
0700 |
data directory (holds creds, state.json, and Scout-issued/delivered certs under certs/) |
…/data/agent.crt |
0644 |
client certificate (public) |
…/data/agent.key |
0600 |
private key |
…/data/ca.crt |
0644 |
Hub CA trust root (captured from the pin-verified enroll handshake) |
…/data/state.json |
0600 |
agent id, re-enrollment token, SPIFFE URI, pinned CA fingerprint, segment/location/labels |
/var/log/cyphers-scout/ |
0700 |
JSON log file scout.log + rotated backups (0600) — see Logging |
Two layers of data-dir default
The Scout binary's built-in --data-dir default is /opt/cyphers/data
— running it by hand with no flags uses that. The installer always
passes --data-dir explicitly (derived from the install root or
CYPHERS_DATA_DIR), so what the service actually uses is whatever the
install chose.
The service¶
cyphers-scout.service is Type=simple, Restart=on-failure, RestartSec=30,
logging to the journal. It runs as root (no User=/Group=) with a
conservative sandbox stanza (PrivateTmp, ProtectKernelTunables/Modules/
ControlGroups/Clock, RestrictRealtime, LockPersonality).
First start right after enrollment
The Hub provisions this agent's NATS ACL moments after enrollment. A Scout
dialing inside that gap is rejected with an authorization violation; it
retries the initial connection for up to 90 seconds (logging
the Hub may still be provisioning this agent's ACL) before giving up to
systemd's restart loop. A rejection that persists past that window means
the agent genuinely isn't authorized — check its status on the Hub.
Operate it the normal systemd way:
systemctl reload cyphers-scout is also available: it only re-reads
cert-dirs.conf (see Changing scan configuration after
install) — nothing else about the
running Scout changes.
Re-pointing at a different Hub¶
The Hub endpoints are not baked into the unit file — the unit reads
HUB_REST / HUB_NATS from <install-dir>/scout.env at start. To move a
Scout to a different Hub address, edit that file and restart; no reinstall:
Changing scan configuration after install¶
No reinstall — and no new enrollment token — is needed to change what an installed Scout scans or watches.
Certificate directories are the common change, and they have a dedicated
file: add one absolute path per line to <install-dir>/cert-dirs.conf and run
systemctl reload cyphers-scout — the Scout re-reads the file and starts a
scan cycle immediately (no restart, no NATS reconnect; a reload landing while
a cycle is already running joins that cycle, and the new directories show up
in the next one). A file that fails to
parse is rejected: the running Scout keeps its last-good list and logs the
error; at startup a bad file refuses to start.
Everything else (--target, --interval, …) is still an ExecStart= edit:
| What | Where | Apply with |
|---|---|---|
Hub endpoints (HUB_REST, HUB_NATS) |
<install-dir>/scout.env |
systemctl restart cyphers-scout |
Everything else (--target, --interval, …) |
the ExecStart= line in /etc/systemd/system/cyphers-scout.service |
systemctl daemon-reload && systemctl restart cyphers-scout |
Example — add a scan target to an existing install:
sed -i 's|^ExecStart=.*|& --target www.example.com:443|' \
/etc/systemd/system/cyphers-scout.service
systemctl daemon-reload
systemctl restart cyphers-scout
The enrollment credentials in the data directory are untouched by any of this — the Scout reconnects with its existing identity.
Upgrade & uninstall¶
Currently manual: rebuild the binary and re-run the installer to upgrade;
there is no in-place updater or uninstall script yet. To remove a Scout, stop +
disable the service, delete /etc/systemd/system/cyphers-scout.service and
the install dir (default /opt/cyphers/), and revoke/retire the agent from
the Hub.
Check the data directory before deleting it
{data-dir}/certs/ is the Scout's certificate delivery directory —
when a renewal delivers a certificate, this is where it lands, and web
servers are commonly pointed (often via symlinks) straight at it. Deleting
the install dir can therefore remove the certificate and private key a
running web server is serving: it keeps working from memory until its next
reload, restart, or reboot — then fails. Before removing anything:
ls -laR /opt/cyphers/data/certs/ 2>/dev/null # anything delivered?
grep -rl /opt/cyphers /etc/nginx /etc/apache2 /etc/haproxy 2>/dev/null # anything pointing in?
If there are consumers, repoint them (or complete a renewal to a fresh install first) before deleting the directory. A deleted private key is not recoverable.