CLI Reference
Two command surfaces: the sentinel binary on each Wings node, and the panel-side artisan command.
The sentinel binary
Installed to /usr/local/bin/sentinel by install.sh. Single static binary, CGO-free. All subcommands accept --config PATH (default /etc/sentinel/config.yaml).
usage: sentinel [daemon|scan|status|version] [--config PATH]sentinel daemon
Runs the agent. This is what sentinel-node.service starts; you rarely invoke it by hand.
sentinel daemon --config /etc/sentinel/config.yamlOn start it loads the local config, reads the pairing token from panel.token_file, and exits with an error if the token is unreadable or panel.url is unset. Then it registers with the panel (retrying with backoff), applies the pushed config, and enters the tick loop: shared system snapshot every scan_interval_seconds, detectors, cooldown dedup, rule matching, action execution, event spool and batch flush, heartbeat every 30 s.
sentinel daemon --help prints usage; sentinel daemon --version prints the version without starting.
Log lines worth knowing:
sentinel 1.0.0 starting (config version 5)
registered as node 3The installer waits specifically for the registered as node line after a restart — a stale line from a previous run does not count.
sentinel scan
One-off detection pass. Prints findings as JSON to stdout and a count to stderr. No actions are taken and nothing is shipped to the panel — safe to run any time, useful for verifying detectors against a node you suspect is dirty.
sentinel scan[
{
"uuid": "3f6c1a2e-…",
"occurred_at": "2026-07-31T12:00:00Z",
"category": "miner",
"detector": "miner",
"severity": "critical",
"title": "cryptocurrency miner active",
"server_uuid": "d8321c4e-…",
"container_id": "a1b2c3…",
"process": "/usr/bin/xmrig -o pool.minexmr.com:4444 --donate-level 1",
"pid": 23841,
"path": "",
"evidence": { "cpu_percent": "387.2" },
"actions_taken": [],
"dry_run": false
}
]1 finding(s)Bounded by a 5-minute internal timeout. Uses the same engine and config as the daemon, so what scan finds is what the daemon would flag.
sentinel status
Config and connectivity summary — the first thing to run when a node misbehaves.
sentinel statussentinel 1.0.0
config file: /etc/sentinel/config.yaml
state dir: /var/lib/sentinel
panel url: https://panel.example.com
api listen: 0.0.0.0:8481
config version: 5 (applied from panel push)
dry run: false
docker: ok, 12 running container(s)The docker line reports UNREACHABLE (...) when the socket cannot be dialed — almost always a docker outage or a non-standard docker_socket path.
sentinel version
sentinel versionsentinel 1.0.0Also available as sentinel --version / sentinel -v. The agent reports this version at registration and in every heartbeat; the Nodes tab shows it per node.
Installer scripts
install.sh
Interactive node pairing installer, run as root on the Wings node (prompts read from /dev/tty, so curl | bash works). Prompts for panel URL, 64-hex token (regex-validated, lowercased), listen address (default 0.0.0.0) and port (default 8481); writes /etc/sentinel/config.yaml and /etc/sentinel/token (both 0600); builds from source if Go is present, otherwise installs the prebuilt binary next to the script; installs and starts sentinel-node.service; then waits up to 90 s for registration and prints the result.
Idempotent: re-running rewrites config/token and restarts the service — this is the supported re-pairing path after a token reset. Full walkthrough: Installation.
uninstall.sh
sudo bash uninstall.sh [--purge] [--keep-config]Stops and disables the systemd unit and removes the binary and configuration. State under /var/lib/sentinel (spool, quarantine, intel, persisted panel config) is kept unless --purge is given; --keep-config keeps /etc/sentinel for a later reinstall.
systemd
The unit is sentinel-node.service (Type=simple, runs as root, Restart=always, RestartSec=10, MemoryMax=512M, CPUQuota=100%, LimitNOFILE=65536, logs to the journal as sentinel). It requires docker.service and starts after network + docker.
systemctl status sentinel-node # state + recent log lines
systemctl restart sentinel-node # apply a hand-edit to /etc/sentinel/config.yaml
systemctl stop sentinel-node # pause the agent (panel marks node offline after ~120s)
journalctl -u sentinel-node.service -f # follow live logs
journalctl -u sentinel-node.service --since today | grep "registered as node"The service auto-restarts on crash (max 5 starts per 60 s). If it enters a failed restart loop, read the journal — the common causes are an unreadable token file or an empty panel.url.
Panel artisan commands
Run from the panel directory (/var/www/pterodactyl).
sentinel:housekeeping
php artisan sentinel:housekeepingTwo jobs in one pass:
- Offline sweep — flips
is_onlineto false for any node whoselast_seen_atis older thansentinel.offline_after_seconds(default 120 s). This is what keeps the Nodes tab honest between heartbeats. - Event pruning — deletes detection events older than
sentinel.event_retention_days(default 30 days).
Output (only when something changed):
Marked 1 node(s) offline, pruned 418 event(s).Register it with the panel's scheduler (the same crontab entry that runs schedule:run every minute) so both jobs run continuously; it is safe and cheap to run manually at any time.
