# Enodia Sentinel Command Reference Applies to package version: `0.7.0` Docs version: `0.7` Schema generation: `v1` 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 enodia-sentinel check --json ``` 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. `--json` emits the alerts as a JSON array of alert dicts (severity-ordered) instead of text, for scripts and the tray applet. 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. ### `baseline accept` / `revoke` / `list` ```bash enodia-sentinel baseline accept --reason "..." [--expires 7d] [--force] enodia-sentinel baseline revoke enodia-sentinel baseline list [--stale] [--json] ``` Baseline *reconciliation*: accept a specific, identified drift item with a mandatory reason instead of silently folding it into a baseline. `` is one of `fim`, `pkgfile`, `listener`, or `suid`; `` is a path (FIM/package/ SUID) or a `port/comm` key (listener). An acknowledgement suppresses that one alert **only while the live state still matches the accepted fingerprint**. `fim` and `pkgfile` acks are content-bound: when the file changes again the ack goes *stale* and the alert returns. `listener` and `suid` acks are identity-bound, so only `--expires` (TTL) or an explicit `revoke` retires them. Each record stores who accepted it, when, why, and the fingerprint, in `log_dir/reconciliation.json` (override with `reconcile_store`). - `accept` refuses to record drift it cannot observe live (e.g. a listener not currently bound); pass `--force` to accept anyway — required for accepting a *removed* file. Omitting `--reason` is a hard error. - `--expires` accepts `Nd` / `Nh` / `Nm` (days/hours/minutes). - `revoke` exits `1` if the target isn't present. - `list` shows every ack; `--stale` filters to stale/expired ones. It exits `1` whenever any acknowledgement is stale (a scriptable drift check) and re-derives FIM staleness against live files. Reconciled drift is dropped at the daemon detection chokepoint, hidden from `fim-check`, and summarised in the dashboard integrity view, so an acknowledged item stops alerting everywhere until its fingerprint changes or its TTL lapses. ## 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 enodia-sentinel rules docs ``` 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. `rules docs` emits Markdown rule documentation from the active rule metadata; the built-in snapshot lives in [RULES.md](RULES.md). 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`, ...). - Enabled systemd units with group/world-writable unit files, or whose `ExecStart` binary runs from a writable/unsafe path (`/tmp`, `/dev/shm`, `/var/tmp`, `/home`, `/run/user`) — a common persistence/hijack vector. Shells out to `systemctl` with timeouts and fails closed on non-systemd hosts; disable with `posture_systemd = false`. - 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. Recovery checks stay read-only too: plans can ask the operator to re-run `fim-check --packages`, verify package files with `pkgdb-verify`, re-run `rootcheck`, confirm persistence alerts are gone with `check --json`, and run `watchdog` from another host to prove the heartbeat/dashboard path is visible. 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. Stable JSON contracts are documented in [SCHEMAS.md](SCHEMAS.md). Current v1 IDs cover alert objects/snapshots, incident records/views/exports, status, response plans, and response audit JSONL records. 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, integrity/watchdog state, event-rule metadata, event tail, and dry-run response plans. It remains read-only; response commands are displayed for review but not executed from the browser. Dashboard settings are local to the browser and include persistent themes: Console, Paper, Contrast, LGBTQ, Trans, Dracula, Solarized Dark, Solarized Light, and Twilight. The theme registry and core text/status contrast pairs are covered by the web test suite. Expected use: `enodia-sentinel-web.service`. ### `enodia-sentinel-tray` ```bash enodia-sentinel-tray ``` Launches the optional desktop system-tray applet (no subcommands). It is a GUI frontend over the commands above: the menu opens the dashboard (starting `enodia-sentinel-web.service` if needed), starts/stops/restarts `enodia-sentinel.service` via `systemctl`, shows daemon status and alert counts from `status --json`, and runs `check --json` on demand with the result shown as a desktop notification. Requires the `[tray]` extra (`pystray`, `Pillow`); the core agent stays dependency-free without it. ### `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. Suggestions are TOML snippets and include the source alert `sid` and `classtype` in comments for review/audit context. ### `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.