Daemon Lifecycle
Startup, background tasks, LISTEN/NOTIFY hot-reload and shutdown
Daemon Lifecycle
The daemon (sentry run, wiring in
daemon.rs)
assembles everything in dependency order: contracts first, shared state
next, sources and background tasks last. Optional infrastructure failures
(Postgres, geo, feeds, DNS) degrade with warn! instead of aborting;
configuration failures (invalid policy, malformed [real_ip], TLS without
the feature) abort with a readable error.
Startup sequence
- Config —
sentry.toml+SENTRY_*overlay;ActionKindandChallengeBackendare typed enums, so config typos fail at load. Protocol schemas are also compiled here (F9; a failed compile keeps the previous set). - Ruleset (
packs.rs) — default packs withshadow/enforcemodes (params flatten to<pack>__<param>),[[rules.custom]]rules, one synthetic rule per feed with anaction, and dataset rules (feed:<name>). - Reputation feeds (F3.7) — initial sync with a 120 s timeout, summary in the log, background refresh; a failing feed keeps its previous entries.
- Geo (
sentry-geo) — opens MaxMindDB; without the databases, enrichment stays off (no-op). - Postgres (
sentry-storage) — connect + migrations + three LISTEN tasks (rules, blocks, datasets) + DB dataset rules applied + block table pre-warm fromip_state.blocked()and offender tracker pre-warm from persisted strikes (a returning offender past the TTL re-blocks on the 1st event). Without a configured URL, the daemon runs without persistence. - Trackers and guards — scan, behavior, upload flood, posture,
correlation, offender (
pipeline.rs); theTrustSet(embedded Cloudflare ranges + daily refresh + trusted list presets); the bot rDNS-verification worker (F7.7) off the hot path. - Routes — validator merges config ∪ DB (
RouteValidator::merge); with no configured routes, every path generatesUnknownRoute(explicit warn). - Registry + sources —
build_registryinstantiates the actions ([[action]]) and the sources ([[source]]) are spawned, publishing into the fan-in. - Infrastructure reconcile — nginx deny includes against
ip_state(F7.8), kernel/firewall ban sync from the DB (60 s loop, F7.3), Cloudflare reconcile on startup (verify token, adopt live rules, delete expired, provision /64 IP Lists — F2.5/F2.14). - Inline edge (when
mode = "inline") —proxy::serveruns the mandatory upstream health check before accepting traffic (startup fails if the backend does not answer), then starts the port 80 listener, TLS 443 (featureedge-tls) and the optional edge-tcp. - Metrics server —
/metrics+/api/events(metrics.rs) when[metrics] enabled. - Ingest loop —
recv_many(64)on the fan-in, LRU dedupe,pipeline.process, inline forks, telemetry/action lanes.
Background tasks
| Task | Interval | Role |
|---|---|---|
| Tracker prunes (offender, scan, behavior, upload, correlation, posture, BlockTable, rate limiter) | 60 s | Expire per-IP windows and block entries |
prune_pending | 60 s | Drop expired response-phase signals (F3.9.1) |
| Pressure monitor | 5 s | Queue occupancy + INSERT latency (EMA) → flips OverloadState |
| Persist worker | continuous | Batches of up to 64 events per multi-INSERT |
| Action workers | continuous | Execute deferred (network) actions; 1 worker = strict ordering |
| Cloudflare reaper | 300 s | Deletes access rules/IP List entries whose sentry:<ts>:<ttl> TTL expired |
| Route learner | [route_learner] interval_secs | Infers route shapes and publishes via NOTIFY |
Cloudflare range refresh ([real_ip]) | daily | Updates the TrustSet |
| Protocol watcher (F9) | 500 ms debounce + 60 s safety poll | Recompiles schemas on fs events |
| Feed refresh | per feed | Re-fetches reputation/dataset feeds |
Hot-reload via LISTEN/NOTIFY
Shared state between nodes and between CLI/dashboard and daemon travels
over Postgres channels (PgPool):
| Channel | Listener | Effect |
|---|---|---|
sentry_rules_changed | rules_hot_reload | Reloads the RuleSet from the DB and hot-swaps it |
sentry_blocks_changed | blocks_hot_reload | Updates the BlockTable — a block on one node denies on every node's edge |
sentry_datasets_changed | datasets_hot_reload | Replaces dataset:<name> rules and dynamic prefilter literals (F7.7) |
sentry_routes_changed | routes_hot_reload | Merges config ∪ DB and swaps the validator (learner + sentry routes) |
sentry_model_changed | model reload loop | Swaps the ONNX model hot with the AiFork in Arc<RwLock<>> (F3.6) |
Emitters: CLI commands (sentry rules, sentry model reload,
sentry routes, sentry ip block/unblock), the dashboard (serve) and
the daemon itself (block mirror).
Shutdown
There is no explicit graceful drain: the ingest loop runs while producers
exist, and the process terminates on an OS signal (SIGINT/SIGTERM).
Durability comes from the in-memory-stateless / Postgres-stateful design:
blocks, strikes, incidents and events live in the database and are
re-warmed on the next startup (block table and offender tracker
pre-warm), so an abrupt death loses no security posture. Idempotent
background tasks (reaper, reconcile, learner) simply restart.