Skip to content

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:

scp install-web-01.sh you@web-01:/tmp/

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:

  1. Extracts the embedded binary to <install-dir>/scout (mode 0755).
  2. Creates the data dir <install-dir>/data with mode 0700.
  3. Writes <install-dir>/scout.env (mode 0600) holding the Hub endpoints (HUB_REST, HUB_NATS) — the single place they live (see Re-pointing at a different Hub).
  4. Enrolls: runs the agent in the foreground until agent.crt appears (≤ 30s), then stops it. Enrollment is a POST /enroll over 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 to ca.crt comes from that verified handshake, not the reply.)
  5. Writes /etc/systemd/system/cyphers-scout.service (reads endpoints from scout.env via EnvironmentFile=). The unit provisions /var/log/cyphers-scout/ (LogsDirectory=cyphers-scout) for the agent's log file — see Logging.
  6. 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 status cyphers-scout
systemctl restart cyphers-scout
journalctl -u cyphers-scout -f

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:

vi /opt/cyphers/scout.env          # change HUB_REST / HUB_NATS
systemctl restart cyphers-scout

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.