From cf5ba5cf2afbe6db2d45881483aafee8f54a7b03 Mon Sep 17 00:00:00 2001 From: anti Date: Tue, 21 Apr 2026 19:38:41 -0400 Subject: [PATCH] =?UTF-8?q?docs(debt):=20open=20DEBT-032=20=E2=80=94=20pro?= =?UTF-8?q?ber=20can't=20detect=20fingerprint=20rotation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mutation-event stream landed this session closes the "deckies are atomic nodes" gap for service-list changes, but substrate identity is really ``(service, implementation_fingerprint)``. A base-image rebuild that rotates OpenSSH 8.4 → 9.2 without changing the service list is invisible to the correlation graph today because the prober's dedup set is in-memory and per-run — no cross-run diff, no "fingerprint changed" event. DEBT-032 documents the required piece: a per-(decky, service, probe_type) persistence layer + diff-on-change emission, with the correlator's existing mutation-marker interleaving pattern as the model for fingerprint markers. A mutation-vs-fingerprint divergence detector then falls out of the data model for free — fingerprint drift without a preceding mutation ⇒ substrate_divergence finding. --- development/DEBT.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/development/DEBT.md b/development/DEBT.md index 38f5c09e..125e560a 100644 --- a/development/DEBT.md +++ b/development/DEBT.md @@ -1,6 +1,6 @@ # DECNET — Technical Debt Register -> Last updated: 2026-04-21 — All addressable debt cleared. +> Last updated: 2026-04-21 — DEBT-032 opened (fingerprint rotation detection). > Severity: 🔴 Critical · 🟠 High · 🟡 Medium · 🟢 Low --- @@ -218,6 +218,23 @@ DEBT-029 shipped the bus; DEBT-030 proved the pattern end-to-end through the mut - **Standalone `decnet correlate` worker** — the rollout plan presumed one; today the engine runs inside the profiler worker, which is the right shape for the current data flow. - **Bus-wake subscriptions** — publishes landed; subscribe-side (e.g. prober re-probe on `decky.*.state`) was not wired to avoid coupling the wake pattern to a subscriber we don't yet have. +### DEBT-032 — Prober can't detect fingerprint rotation without mutation +**Files:** `decnet/prober/worker.py` (~lines 235, 286, 334, 392), `decnet/web/db/models.py` (new `decky_service_fingerprints` table). + +Substrate identity is `(service_name, implementation_fingerprint)`, not service name alone. A base-image rebuild that rotates OpenSSH 8.4 → 9.2 — or any recompose that changes JARM / HASSH / TCP fingerprint without changing the service list — is a substrate transition from the attacker's recon POV, and today the correlation graph sees none of it. + +The prober already computes JARM (`worker.py:286`), HASSH (`worker.py:334`), and TCP fingerprint (`worker.py:392`), and emits each as RFC 5424 syslog + optional bus publish. What's missing is **per-(decky, service, probe_type) persistence** to diff against: the current dedup set `probed: dict[IP → {probe_type → set(ports)}]` (`worker.py:235`) is in-memory and scoped to one run, so any restart loses history and any same-IP probe on a changed substrate can't be detected as a change. + +**Design:** +1. New SQLModel table `decky_service_fingerprints` keyed by `(decky_name, service, probe_type)` with `last_hash, last_seen_at, sample_count`. One upsert per probe; bounded by fleet × probe families. +2. Prober reads `last_hash` before emitting; on diff, emits a new `substrate_fingerprint_changed` event (RFC 5424 syslog + `decky.{id}.fingerprint` bus topic) with `{decky, service, probe_type, old_hash, new_hash}`. On match, upsert the timestamp and skip the event. +3. Correlator consumes the new event kind into a parallel per-decky index (mirroring the mutation index landed in this session) and interleaves `🔍 decky-03 hassh drift` markers in `AttackerTraversal.fingerprints_during`. +4. Divergence detector: compare `substrate_state(t)` fold (mutations) vs `observed_identity(t)` fold (fingerprints) per decky. A fingerprint change without a preceding mutation ⇒ `substrate_divergence` finding — container drift, compromised base image, rootkit banner rewrite, or prober lag. Falls out of the data model for free once both streams exist. + +**Prerequisite satisfied:** mutation event stream + correlator mutation-kind parser landed alongside this DEBT entry (commits `f875350`, `fa0cdb3`, `bf5ed7a`, `d4d8a2a` on `dev`). The fingerprint stream plugs into the same substrate: same RFC 5424 emission pattern, sibling per-decky engine index, same timeline interleaving. + +**Status:** Open — deferred to its own commit sequence. The dedup state in `worker.py:235` is the only thing standing between "JARM hash computed" and "substrate rotation detected." + --- ## 🟢 Low @@ -275,6 +292,7 @@ DEBT-029 shipped the bus; DEBT-030 proved the pattern end-to-end through the mut | DEBT-029 | 🟡 Medium | Architecture / Bus | ✅ resolved | | DEBT-030 | 🟡 Medium | Web / Live mutations | ✅ resolved (Phase A) | | ~~DEBT-031~~ | ✅ | Workers / Bus integration | resolved | +| DEBT-032 | 🟡 Medium | Correlation / Prober | open | -**Remaining open:** DEBT-011 (Alembic), DEBT-023 (image pinning), DEBT-026 (modular mailboxes), DEBT-027 (Dynamic bait store), DEBT-028 (deploy endpoint tests) -**Estimated remaining effort:** ~12 hours. DEBT-030 Phase B (optimistic staged-buffer editor) is a follow-up, not debt. +**Remaining open:** DEBT-011 (Alembic), DEBT-023 (image pinning), DEBT-026 (modular mailboxes), DEBT-027 (Dynamic bait store), DEBT-028 (deploy endpoint tests), DEBT-032 (fingerprint rotation detection). +**Estimated remaining effort:** ~16 hours. DEBT-030 Phase B (optimistic staged-buffer editor) is a follow-up, not debt.