diff --git a/CLAUDE.md b/CLAUDE.md index 31ec8c4..1d8da61 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -50,10 +50,11 @@ Implemented investigation/response state: - Incident tracking groups snapshots by process lineage and time window and exposes `incident list/show/export`. - The dashboard is HTTPS-only, token-protected off loopback, and read-only. It - shows status, incidents, timelines, alerts, posture findings, event-rule - metadata, event tail, dry-run response plans, and a local Settings menu with - persistent themes. Web tests cover the theme registry and core text/status - contrast checks. + shows status, incidents, timelines, alerts, posture findings, + integrity/watchdog state, event-rule metadata, event tail, dry-run response + plans, and a local Settings menu with persistent themes. Web tests cover the + theme registry, integrity console wiring, and core text/status contrast + checks. - `respond plan ` builds read-only containment/recovery plans, persists CLI-generated plans under `response-plans/`, and appends `response-audit.log`. Dashboard/API plan previews do not write artifacts. diff --git a/README.md b/README.md index 0f74f62..4a6965d 100644 --- a/README.md +++ b/README.md @@ -296,11 +296,11 @@ sudo systemctl enable --now enodia-sentinel-web saved on first run and printed in the startup line. Open `https://:8787/?token=…`. - **Read-only management**: incidents, timelines, alert inventory, posture - findings, event rule atlas, event tail, and dry-run response plans. No - commands are executed from the browser. JSON + findings, integrity/watchdog state, event rule atlas, event tail, and dry-run + response plans. No commands are executed from the browser. JSON API at `/api/status`, `/api/incidents`, `/api/respond/plan/`, - `/api/posture`, `/api/rules`, `/api/alerts`, `/api/alerts/`, - `/api/events`. + `/api/posture`, `/api/integrity`, `/api/rules`, `/api/alerts`, + `/api/alerts/`, `/api/events`. - **Operator settings**: the self-contained dashboard includes a Settings menu with persistent local themes: Console, Paper, Contrast, LGBTQ, Trans, Dracula, Solarized Dark, Solarized Light, and Twilight. Static dashboard tests guard diff --git a/docs/COMMAND_REFERENCE.md b/docs/COMMAND_REFERENCE.md index 83f44bf..f41c791 100644 --- a/docs/COMMAND_REFERENCE.md +++ b/docs/COMMAND_REFERENCE.md @@ -333,13 +333,13 @@ 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-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. +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`. diff --git a/docs/OPERATIONS.md b/docs/OPERATIONS.md index 0cf98eb..8ac834c 100644 --- a/docs/OPERATIONS.md +++ b/docs/OPERATIONS.md @@ -73,8 +73,9 @@ Expected healthy output: only ones you have consciously accepted (e.g. password auth on a host that needs it). -The HTTPS dashboard shows the same posture findings under the Posture tab for -quick remote review. +The HTTPS dashboard shows the same posture findings under the Posture tab and +summarizes FIM/package anchors plus heartbeat freshness under the Integrity tab +for quick remote review. ## Alert Workflow diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index b05a68a..143ffd9 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -95,7 +95,7 @@ Purpose: define a stable single-host product. - ✅ Add documentation versioning and manpage-style command reference. - ✅ Harden packaging for Arch first, then document Debian/RPM install paths. - ✅ Add signed release artifacts and checksums. -- Improve dashboard from alert browser to local console: +- ✅ Improve dashboard from alert browser to local console: incidents, posture, integrity state, response plans, and watchdog status. - ✅ Add a local dashboard Settings menu with persistent themes for dark console, light evidence review, high-contrast operation, LGBTQ, Trans, diff --git a/docs/SCHEMAS.md b/docs/SCHEMAS.md index 36c28ad..46d388c 100644 --- a/docs/SCHEMAS.md +++ b/docs/SCHEMAS.md @@ -90,6 +90,25 @@ Required fields: | `heartbeat_age` | number or null | Seconds since heartbeat, if present. | | `heartbeat_stale` | boolean | Whether heartbeat exceeds configured max age. | +## `enodia.integrity.v1` + +Integrity/watchdog object returned by `/api/integrity`. This is a read-only +summary of existing state; the dashboard endpoint does not run live FIM or +package verification work. + +Required fields: + +| Field | Type | Meaning | +|---|---|---| +| `schema` | string | `enodia.integrity.v1`. | +| `generated_at` | number | Unix timestamp when the report was built. | +| `status` | string | Overall state: `ok`, `review`, or `critical`. | +| `checks` | object | Compact per-check status map. | +| `watchdog` | object | Daemon running state, heartbeat age, stale flag, max age, and verdict message. | +| `anchors` | object | FIM baseline, package DB anchor, pacman keyring, and SigLevel summary. | +| `sentinel_footprint` | object | Configured Sentinel self-integrity paths and present/missing counts. | +| `read_only` | boolean | Always true for this dashboard API. | + ## `enodia.response.plan.v1` Dry-run response plan from `respond plan `. diff --git a/docs/SPECIFICATION.md b/docs/SPECIFICATION.md index 322f7e1..4050114 100644 --- a/docs/SPECIFICATION.md +++ b/docs/SPECIFICATION.md @@ -102,7 +102,7 @@ When a fresh alert survives cooldown, Sentinel writes: |---|---| | CLI | Run daemon, one-shot checks, baseline management, FIM checks, package DB checks, rootcheck, posture audit, incident review/export, triage, status/watchdog, and response planning. | | Logs | Durable local evidence under `/var/log/enodia-sentinel`. | -| Dashboard | HTTPS-only read-only web view for status, incidents, timelines, alert browsing, posture findings, event-rule metadata, event tail, and response-plan previews. | +| Dashboard | HTTPS-only read-only web view for status, incidents, timelines, alert browsing, posture findings, integrity/watchdog state, event-rule metadata, event tail, and response-plan previews. | | Push | ntfy, Pushover, and generic webhook notifications. | | Red-team harness | Safe drills for testing signatures and demos. | diff --git a/enodia_sentinel/schemas.py b/enodia_sentinel/schemas.py index 02fb508..0ac60c1 100644 --- a/enodia_sentinel/schemas.py +++ b/enodia_sentinel/schemas.py @@ -12,5 +12,6 @@ INCIDENT_V1 = "enodia.incident.v1" INCIDENT_VIEW_V1 = "enodia.incident.view.v1" INCIDENT_BUNDLE_V1 = "enodia.incident.bundle.v1" STATUS_V1 = "enodia.status.v1" +INTEGRITY_V1 = "enodia.integrity.v1" RESPONSE_PLAN_V1 = "enodia.response.plan.v1" RESPONSE_AUDIT_V1 = "enodia.response.audit.v1" diff --git a/enodia_sentinel/static/dashboard.html b/enodia_sentinel/static/dashboard.html index c636506..02c0ccd 100644 --- a/enodia_sentinel/static/dashboard.html +++ b/enodia_sentinel/static/dashboard.html @@ -186,12 +186,13 @@ .section-title{display:flex;align-items:center;justify-content:space-between; padding:13px 14px 8px;color:var(--muted);font-size:11px;letter-spacing:.12em; text-transform:uppercase} - .metrics{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-bottom:16px} + .metrics{display:grid;grid-template-columns:repeat(6,1fr);gap:10px;margin-bottom:16px} .metric{background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:12px} .metric b{display:block;font-size:24px;line-height:1;color:var(--white)} .metric span{display:block;color:var(--muted);font-size:11px;margin-top:8px;text-transform:uppercase} .metric.crit b{color:var(--red)} .metric.high b{color:var(--amber)} .metric.med b{color:var(--blue)} - .metric.posture b{color:var(--accent)} + .metric.posture b{color:var(--accent)} .metric.integrity b{color:var(--violet); + font-size:16px;text-transform:uppercase} .list{padding:0 8px 14px} .item{border:1px solid transparent;border-bottom-color:var(--line);padding:10px; cursor:pointer;border-radius:7px;margin-bottom:4px} @@ -235,6 +236,20 @@ .cond{display:grid;grid-template-columns:98px 1fr;gap:6px;border-top:1px solid var(--line);padding-top:7px} .cond dt{color:var(--muted);font-size:11px} .cond dd{margin:0;color:var(--soft);font-size:11px;word-break:break-word} + .integrity-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px} + .state-card{background:var(--panel);border:1px solid var(--line);border-radius:8px; + padding:13px;box-shadow:var(--shadow)} + .state-card.ok{border-color:var(--ok)} .state-card.review{border-color:var(--amber)} + .state-card.critical{border-color:var(--red)} + .state-card h3{margin-bottom:8px} + .state-line{display:grid;grid-template-columns:120px minmax(0,1fr);gap:7px; + border-top:1px solid var(--line);padding-top:7px;margin-top:7px} + .state-line span:first-child{color:var(--muted);font-size:11px;text-transform:uppercase} + .state-line span:last-child{word-break:break-word;color:var(--soft)} + .path-list{display:grid;gap:6px;margin-top:8px;max-height:230px;overflow:auto} + .path-row{display:grid;grid-template-columns:58px 1fr;gap:8px;font-size:11px} + .path-row b{color:var(--accent);font-weight:700} + .path-row.missing b{color:var(--muted)} .action{border:1px solid var(--line);border-radius:8px;background:var(--panel);padding:11px;margin-bottom:10px} .action .head{display:flex;gap:8px;align-items:center;justify-content:space-between} .risk{font-size:10px;text-transform:uppercase;border:1px solid var(--line);border-radius:999px;padding:2px 7px;color:var(--muted)} @@ -297,12 +312,14 @@ +
+ @@ -316,7 +333,7 @@