Start a per-subsystem behavioral specification under docs/behavior/ that documents the current Python agent's observable behavior at reimplementation fidelity. It is the parity contract for the Go 2.0 rewrite (Active Migration Track), complementing the product-oriented docs/SPECIFICATION.md. Section 1 covers system-state capture, the daemon sweep loop, alert dedup, the detector registry gating, and the baseline lifecycle — including three behaviors the port must match exactly: baselines are rebuilt (not loaded) at daemon start, arming is time-based via baseline_grace, and first_seen's initialized flag persists across restarts while listener/SUID baselines re-learn every start. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D36ffwoga57opftxzJVHPW
2.8 KiB
Enodia Sentinel — Behavioral Specification
This directory documents the observable behavior of the current Python production agent at reimplementation fidelity: the inputs each subsystem reads, the algorithm it applies, the outputs it produces (SIDs, severities, dedup keys, JSON schemas), the files it writes, and its edge-case / fail-open rules.
Why this exists
Python is the production agent and the parity oracle for the Go rewrite
that will ship as Enodia Sentinel 2.0 (see docs/ROADMAP.md, Active
Migration Track). "Match the oracle" was previously tribal knowledge encoded
only in the parity fixture and the source itself. These documents make the
contract reviewable: a Go port of any subsystem is correct when it reproduces
the behavior described here.
This is deliberately distinct from docs/SPECIFICATION.md, which defines the
product (what Sentinel is, design principles, acceptance criteria). This set
defines mechanism (what the code does, precisely enough to re-derive).
Conventions
- Source anchors are written as
module.py:function— the authority is always the code, and these documents must be updated when the code changes. - SIDs / severities / keys quoted here are the stable contract; a port that emits a different value for the same condition is a parity break.
- "Fail-open" means a collection or read error degrades to an empty/absent value and never raises — detection continues on partial data.
- Defaults quoted are the
config.pydataclass defaults as of package version0.7.0; operators may override them.
Sections
Written in port-priority order. Status reflects which documents exist.
| # | File | Covers | Status |
|---|---|---|---|
| 1 | 01-sweep-and-state.md |
system.py capture, daemon.py sweep loop + baseline lifecycle, alert.py, detector registry, config.py timing knobs |
written |
| 2 | 02-poll-detectors.md |
the twelve detectors/* — per-detector input → match → output contract |
planned |
| 3 | 03-event-detection.md |
event.py, exec/syscall/host rule engines, ruleops.py, sids.py |
planned |
| 4 | 04-integrity.md |
fim.py, pkgdb.py, selfprotect.py, assurance.py |
planned |
| 5 | 05-rootcheck.md |
rootcheck.py anti-rootkit cross-view checks |
planned |
| 6 | 06-posture.md |
posture.py advisory host-hygiene checks |
planned |
| 7 | 07-investigation.md |
snapshot.py, incident.py, correlation.py, enrich.py, triage.py |
planned |
| 8 | 08-response.md |
respond.py plans, reconcile.py acknowledged-drift store |
planned |
| 9 | 09-interfaces.md |
cli.py, web.py, tui.py operator-visible contracts |
planned |
Shared helpers (netutil.py, provenance.py) are documented inline in whatever
section first depends on them.