# Enodia Sentinel Command Reference This is the operator-facing command contract for the current local agent. Commands are intentionally plain: they either inspect state, manage baselines, run the daemon, or expose existing evidence. Global options: ```bash enodia-sentinel --version enodia-sentinel -c /path/to/config.toml ``` If no command is provided, `run` is used. ## Service Commands ### `run` ```bash enodia-sentinel run ``` Starts the daemon loop. This is the command used by the systemd service. It builds or loads baselines, starts optional exec/syscall event monitoring, writes heartbeats, runs detector sweeps, captures snapshots, and sends notifications. Expected use: systemd, not an interactive shell. ### `check` ```bash enodia-sentinel check ``` Runs the enabled detector set once and prints alerts. This forces the SUID scan and arms baseline-gated detectors immediately, so it is useful for health checks and debugging. Exit code: - `0`: no alerts. - `0`: alerts may still print today; this command is currently human-oriented, not a strict CI gate. ### `baseline` ```bash enodia-sentinel baseline ``` Rebuilds listener and SUID baselines under `log_dir`. Use after installing on a known-good host or after intentionally adding persistent services or privileged helpers. Do not run this to silence unexplained findings. ## Detection and Integrity Commands ### `list-detectors` ```bash enodia-sentinel list-detectors ``` Prints the poll detectors and whether each is enabled by config. ### `rules` ```bash enodia-sentinel rules list enodia-sentinel rules list --json enodia-sentinel rules show enodia-sentinel rules show --json enodia-sentinel rules test enodia-sentinel rules test --json ``` Inspects and tests event-driven detection rules without reading source code. `rules list` includes built-in exec/syscall rules plus configured exec rules from `exec_rules_file`. `rules show ` prints the rule metadata and match conditions. `rules test ` loads an exec or syscall event JSON file, runs the matching rule engine, and prints any alerts that would fire. Use `-` instead of a path to read the event JSON from stdin. Exec event JSON shape: ```json { "pid": 42, "ppid": 1, "uid": 1000, "parent_comm": "nginx", "filename": "/bin/sh", "argv": ["-c", "id"] } ``` Syscall event JSON shape: ```json { "pid": 42, "ppid": 1, "uid": 1000, "comm": "payload", "syscall": "mprotect", "args": [0, 4096, 6, 0, 0, 0] } ``` Exit code: - `0`: list/show succeeded, or `rules test` matched at least one rule. - `1`: unknown sid for `show`, or no rule matched for `test`. - `2`: malformed sid, missing target, unreadable JSON, or unsupported event shape. ### `rootcheck` ```bash enodia-sentinel rootcheck ``` Runs anti-rootkit cross-view checks once: - PIDs alive via `kill(pid, 0)` but missing from `/proc`. - PIDs visible in `/proc` but missing from `ps`. - Live modules in `/sys/module` but missing from `/proc/modules`. - Listening TCP ports in `/proc/net/tcp*` but missing from `ss`. - UDP ports in `/proc/net/udp*` but missing from `ss -u`. - Raw and special-protocol sockets in `/proc/net` but missing from `ss`. - Network interfaces in promiscuous mode. - Known rootkit module names. - Tainted loaded modules (`/sys/module/*/taint`) and global kernel taint (`/proc/sys/kernel/tainted`). Exit code: - `0`: no rootcheck findings. - `1`: one or more rootcheck alerts. ### `fim-baseline` ```bash enodia-sentinel fim-baseline ``` Builds the SHA-256 file integrity baseline for configured critical paths plus Sentinel's own footprint. Use on a known-good host. The package hook also calls `fim-update` after normal package transactions. ### `fim-update` ```bash enodia-sentinel fim-update ``` Refreshes the FIM baseline. This exists primarily for the package manager hook. Manual use should be treated as an acknowledgement that the current file state is legitimate. ### `fim-check` ```bash enodia-sentinel fim-check enodia-sentinel fim-check --packages ``` Diffs current file state against the FIM baseline. With `--packages`, also runs package-owned file verification through the package manager (`pacman -Qkk` on Arch). Exit code: - `0`: no baseline changes. - `1`: one or more added, removed, or modified files. ### `pkgdb-check` ```bash enodia-sentinel pkgdb-check ``` Checks whether the local package database fingerprint changed outside a logged package transaction. This catches an attacker rewriting stored package checksums to hide a modified binary. Exit code: - `0`: package DB matches the anchor or was legitimately re-anchored. - `1`: out-of-band DB tampering suspected. ### `pkgdb-verify` ```bash enodia-sentinel pkgdb-verify enodia-sentinel pkgdb-verify --sample 200 ``` Compares on-disk package-owned files to SHA-256 hashes from `.MTREE` manifests inside cached signed packages. This is independent of the mutable local package database. The command also flags insecure global `SigLevel` settings and warns if the pacman keyring is missing. Exit code: - `0`: sampled files match and signature policy is not downgraded. - `1`: a signed-package mismatch or insecure signature setting was found. ### `incident` ```bash enodia-sentinel incident list [--json] enodia-sentinel incident show [--json] enodia-sentinel incident export ``` Groups related alerts into incidents so one intrusion reads as one story instead of several disconnected alerts. Grouping is **process-lineage first** (alerts sharing a PID or a common ancestor — the web server or SSH session they descend from), with a **time-window fallback** for alerts that carry no live PID (FIM drift, package tamper, hidden modules). Each captured snapshot records an `incident_id`; the index lives in `incidents.json` under `log_dir`. - `list` — incidents newest-first: id, last seen, severity, alert count, and the signatures involved. - `show ` — incident summary plus a time-ordered timeline of its member snapshots (time, severity, signatures, PIDs, and the snapshot file to open). - `export ` — a single JSON bundle of the incident record with every member snapshot report inlined, suitable for handoff or offline analysis. Tuning lives in config: `incident_window` (how long an incident stays open for new alerts) and `incident_lineage_depth` (ancestors walked when correlating). Exit code: `0` on success; `1` if the incident id is unknown; `2` if `show`/ `export` is called without an id. ### `posture check` ```bash enodia-sentinel posture check enodia-sentinel posture check --json ``` Audits host configuration hygiene — the conditions that make a host *easy* to attack, as opposed to an attack in progress: - SSH: root login, password authentication, and empty-password logins. - sudo: passwordless (`NOPASSWD`) rules, disabled authentication, and group/world-writable sudoers files. - World-writable or non-root-owned `PATH` directories (binary-hijack vectors). - Loose permissions on sensitive files (`/etc/shadow`, `/etc/passwd`, ...). - Downgraded package-signature policy (`SigLevel`). Findings are advisory and reuse the standard alert JSON shape (`--json`). This command does not run inside the daemon and never blocks startup. Exit code: - `0`: no posture findings. - `1`: one or more findings. ## Evidence and Operator Commands ### `respond` ```bash enodia-sentinel respond plan enodia-sentinel respond plan --json ``` Builds a dry-run response plan from an incident's retained JSON snapshots. The plan may include evidence preservation, process freeze/terminate candidates, outbound IP blocks, systemd unit disablement, suspicious-file quarantine, package-restore lookup, and follow-up verification checks. This command is intentionally read-only: it prints commands for operator review and never executes them. Each CLI-generated plan is also saved under `/response-plans/`, and a compact JSONL audit record is appended to `/response-audit.log` with the plan id, incident id, action count, and saved path. The JSON schema is `enodia.response.plan.v1` and marks `apply_supported: false` until an audited apply workflow exists. Dashboard/API plan previews remain read-only and do not create new artifacts. Exit code: - `0`: plan generated. - `1`: unknown incident id. - `2`: missing incident id. ### `status` ```bash enodia-sentinel status enodia-sentinel status --json ``` Prints a local health and alert summary: daemon running state, heartbeat age and staleness, total alerts with per-severity counts, last alert time, and eBPF monitor state. `--json` emits the same data as the dashboard `/api/status` endpoint, for cron/monitoring. Exit code: - `0`: daemon is running and the heartbeat is fresh. - `1`: daemon is down or the heartbeat is stale. ### `web` ```bash enodia-sentinel web ``` Starts the read-only dashboard. By default it binds to the Tailscale interface when available and uses bearer-token authentication for non-loopback binds. HTTPS is mandatory. If `web_tls_cert` / `web_tls_key` are not configured, Sentinel creates a self-signed pair under `log_dir`; add a browser exception for that certificate or replace it with a private/local CA certificate. The console exposes incidents, alert snapshots, posture findings, event tail, and dry-run response plans. It remains read-only; response commands are displayed for review but not executed from the browser. Expected use: `enodia-sentinel-web.service`. ### `triage` ```bash enodia-sentinel triage ``` Reads captured alert JSON snapshots and classifies distinct findings as likely false positive or review-needed. It suggests config snippets for known benign noise but never edits config automatically. ### `watchdog` ```bash enodia-sentinel watchdog --url https://100.x.y.z:8787 --token --max-age 120 enodia-sentinel watchdog --url https://100.x.y.z:8787 --token --insecure-tls ``` Polls a remote dashboard and alerts if the sensor is down, unreachable, or has a stale heartbeat. Run this from a separate host. Use `--insecure-tls` only for the built-in self-signed certificate while you have no local/private CA trust path. A watchdog on the same machine does not prove the protected host is alive. Exit code: - `0`: remote sensor is reachable and fresh. - `1`: remote sensor is stale, down, or unreachable. ## Development Shortcuts The Makefile wraps common local tasks: ```bash make test make check make baseline make web make drill ``` `make install` installs a plain package directory plus a launcher wrapper. It does not require pip or a virtualenv. ## Future Command Contracts The roadmap reserves these command shapes: ```bash enodia-sentinel respond apply ``` State-changing response commands should be introduced with stable JSON schemas, audit logs, and tests before they are documented as supported.