Design: Silence-Eskalation & Outbox-Verwahrung

Version: v0.1
Stand: 22. September 2026
Status: WORKING DESIGN — Architektur-Kante, keine Runtime-Invariante
Ort: Ansible-Crumbforest / docs
Anlass: Brand-/Ausfallfall im Container & Stockwerk — „wer ist bei den Warnings, wenn es brennt?"
Bezug: COUNTABILITY_AT_PASSKANTEN_v0.2, PRUEFLISTE-SENSOR-NODE-PERSISTENZ-AUDIT-2026-09-21,
Memory: was-nicht-antwortet-ist-nicht-da, brueckenmitte-outbox-durability, omc-rz-topologie-und-grenze


TEIL 1 — Silence-Eskalation

1.1 Die Frage, die das Design trÀgt

„Wenn das Stockwerk oder der Container brennt — wer ist bei den Warnings oder Alarmen?"

Antwort: niemand Lokales. Genau deshalb darf die Sicherheits-Eigenschaft nicht auf dem
Node leben, der brennt oder die Verbindung verliert.

1.2 Zwei Pfade — presence-driven und absence-driven

Normalpfad (presence-driven). Der Sensor erkennt etwas und funkt aktiv:

Sensor erkennt → warning an lokale Crew (#mqtt #mesh #sms #signal) → Resonanz-Ticket
→ nicht bestĂ€tigt in 1/2/3 min → Eskalation in die Queue OZM↔crumbforest → CRITICAL

Das ist richtig und bleibt. Vorab-Ereignisse (Rauchmelder, panische Bewegung, realer Crew-Alarm
im Raum) werden hier als warning #notfall interpretiert.

Grenzfall (absence-driven). Der Brandfall invertiert den Normalpfad: der Node, der „ich brenne"
funken soll, ist derselbe, der abbrennt oder abreißt. Auf ihn ist im schlimmsten Moment kein Verlass.
Darum die tragende Regel (Memory: was-nicht-antwortet-ist-nicht-da):

Die Stille ist das Signal. Ein erwarteter Heartbeat eines Life-Safety-Nodes, der ausbleibt
UND nicht erreichbar ist, ist selbst eine Eskalation — nicht eine stille LĂŒcke.

remote check → container/stockwerk → UNKNOWN → keine Verbindung darf nicht in Ruhe zerfallen.

1.3 UNKNOWN muss zum Alarm hin fehlschlagen, nicht zur Ruhe hin

UNKNOWN ist ein gĂŒltiger Zustand (OZMAI-Manifest). Aber UNKNOWN ĂŒber einen Node, an dem Menschen
sind
, ist fail-toward-alarm, nicht fail-toward-silence. Das ist der Dead-man's-switch:

erwarteter Heartbeat (Life-Safety-Node)
  fehlt fĂŒr > T_miss  UND  Node nicht erreichbar
    → UNKNOWN_LIFE_SAFETY
    → behandelt wie eine unbestĂ€tigte warning: eskaliert nach 1/2/3 min → CRITICAL

Ein Node, der „ich brenne" senden kann, ist nice-to-have. Ein Node, dessen Schweigen anderswo
Alarm auslöst, ist die eigentliche Sicherheits-Eigenschaft.

1.4 Warum zwei MQTTs eine Sicherheits-Eigenschaft sind, keine Bequemlichkeit

Brennt der Node, verbrennt sein lokaler Broker mit. BestÀtigung und Eskalation können deshalb
nicht dort liegen. Sie mĂŒssen auf dem Broker des anderen Cores leben.

Core A (crumbforest)          OZM↔crumbforest-Queue          Core B (onezeromore)
  eigene NetBox                 (die eine Eskalations-Kante)     eigene NetBox
  eigener MQTT  ───── heartbeat / warning / critical ─────  eigener MQTT
  eigene Pipeline                                              eigene Pipeline
  • Zwei Cores, jeder eigenstĂ€ndig fail-closed. FĂ€llt einer, fĂ€llt der andere nicht (kein shared fate).
  • Jeder Core watcht den Heartbeat des anderen. Geht Core A dunkel, eskaliert Core B das Schweigen — und umgekehrt.
  • Getrennte Trust-Domains (eigenes OZM-WG-VPN nach den getesteten Basis-Regeln). Der Eskalations-Kanal ĂŒberquert genau eine Kante: die OZM↔crumbforest-Queue.

Der Monitoring-Kanal trĂ€gt Liveness, nicht Payload — „Baum steht / Baum gefallen", nie
„was der Baum gesehen hat". Sonst wird aus gegenseitigem Wachen ein zweiter Verwahrungspfad
ĂŒber die VPN-Kante (verletzt PRUEFLISTE §6).

1.5 Die Grenze, die nicht verschoben wird

Der Mesh-Layer ist ein zusÀtzlicher Interpretations- und Eskalations-Layer. Er ersetzt nicht
die zertifizierte Brandmeldeanlage / den Rauchmelder-Pfad (Memory: omc-rz — Wald liest & rendert
Atmo, aktuiert KEINE Safety).

Seine Aufgabe ist „einem Menschen sagen" und „Schweigen bemerken" — nie „ich regel das selbst".
Er scheitert immer Richtung Mensch.

1.6 Verschraubbare Parameter (Sollstand, kein Runtime-Zwang)

T_hb        Heartbeat-Intervall je Life-Safety-Node           (z. B. 30 s)
T_miss      fehlende Heartbeats bis UNKNOWN_LIFE_SAFETY        (z. B. 3 × T_hb)
T_esc       Eskalationsstufen unbestĂ€tigt → CRITICAL           1 / 2 / 3 min
watch_pairs welcher Core welchen Node/Core ĂŒberwacht           (analog wg_route_pairs)

NetBox-Sichtbarkeit: life_safety: true und heartbeat_watched_by: <core> an der Rolle —
damit die Silence-Eskalation eine belegte Eigenschaft ist, keine stille Annahme.


TEIL 2 — Borgmatic als Outbox-Verwahrung

2.1 Borgmatic spaltet sich an der Verwahrungsgrenze

Backup ist strukturell eine Outbox: einseitig, runter, durable, weitergereicht. FĂŒr körpernahe
Sensorik spaltet es sich in zwei völlig verschiedene Aussagen:

  • Als Klasse-A-Pfad: verboten. Ein Sensor-Node im Backup-Scope spiegelt Roh-Frames /
    Keypoints off-site (PRUEFLISTE §5). backup_scope: excluded bleibt positiv gesetzt.
  • Als Klasse-B-Outbox: legitim — durable Verwahrung dessen, was einen eigenen Vertrag hat
    (Count, Alert, msg_id). Aber nur mit Vertrag: was / wozu / wie lange / IdentitÀt ja-nein /
    Leser / Löschung (COUNTABILITY v0.2 §3).

2.2 Die Durability-Kante: genau eine Wirkung, auch nach Restore

Eine Outbox mit Read-once/Wirkung braucht DURABLE_LOCAL-Dedup, sonst erzeugt ein Restore
eine zweite Zustellung (Memory: brueckenmitte-outbox-durability).

Borgmatic ist von Natur aus append-only und chunk-dedupliziert — das ist die halbe Miete gegen
Speicher-Redundanz, aber nicht dasselbe wie Zustell-Idempotenz. Die offene Frage:

Wenn ein Alert ĂŒber die Outbox getragen und dabei ge-backupt wird — ĂŒberquert er die Kante
genau einmal, oder erzeugt der Restore eine zweite Wirkung?

Die Antwort gehört in den Vertrag der Klasse-B-Zeile, nicht in borgmatic:

delivery_id     stabiler, deduplizierbarer Identifier je Outbox-Ereignis (msg_id-Idiom)
effect_once     EmpfÀnger verwirft delivery_id, den er schon gesehen hat (DURABLE_LOCAL)
restore_is_read Restore eines Outbox-Records ist LESEN, nie erneutes ZUSTELLEN

2.3 PrĂŒfpunkte (ErgĂ€nzung zur PRUEFLISTE §5/§8)

# EnthÀlt der Backup-Scope irgendeinen Klasse-A-Pfad?  (muss leer sein)
grep -rn "<sensor-node>\|<pipeline-workdir>" /etc/borgmatic* 2>/dev/null
# Hat jeder ge-backupte Klasse-B-Store einen Vertrag mit delivery_id / effect_once?
#   -> PrĂŒfung gegen die Vertragsliste, nicht gegen borgmatic

PASS: kein Klasse-A im Backup; jeder ge-backupte Klasse-B-Store trÀgt einen Vertrag mit
delivery_id + effect_once; ein Restore ist als LESEN definiert, nicht als erneute Zustellung.

NetBox: je Klasse-B-Outbox-Store ein Eintrag mit Zweck, Retention und effect_once: true.


Kurzform

Sensor misst, Raum deutet, Policy begrenzt, Mensch entscheidet.
Warning geht raus; unbestÀtigt eskaliert sie.
Und die Stille eskaliert auch — der ĂŒberlebende Baum bezeugt das Schweigen des toten.
Zwei Cores, zwei NetBoxen, zwei Broker: der Survivor trĂ€gt den Alarm ĂŒber den Toten.
Der Mesh scheitert immer Richtung Mensch, nie Richtung Handlung.
Backup ist Outbox: Klasse A nie, Klasse B nur mit Vertrag — und genau eine Wirkung, auch nach Restore.

niefertig

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com