Crumbforest Outbox & Inbox — Format v0 (SOT)
Jeder Krümel hat eine Inbox (hoch, gehasht, bewusst antwortlos) und eine
Outbox (runter, read-only). Der Zero greift ins Nullfeld hinein — das
Nullfeld greift nie zum Zero. Das Ergebnis kommt am LED-Streifen an,
nicht am Bildschirm. Kein Kind muss Tastatur oder Screen anfassen, um sich
zu freuen.
Status: DESIGN / v0. Legt nur das Format fest. Kein Code, keine Wirkung.
Konvergiert mit dem Receipt-Idiom (crumb-missions-core/src/receipt.rs):
append-only JSONL, echtes sha256, Einmal-Hash, bezeugen nicht speichern,
Identität declared/derived — nie über Netz geprobt. „jeder krümel zählt."
0. Topologie (festgelegt)
| Ort | Rolle |
|---|---|
| OZM — Container am Museumsparkplatz | erster Crumbforest, läuft #lokal |
| Strato — Crumbforest Root Server | Range, in der die Kids sitzen (lokaler Hotspot oder VPN) |
| OZMAI | baut es anders herum — für den Raum und die lokale Verteilung |
Beide entwickeln dasselbe, nur anders. Diese Datei beschreibt die Crumbforest-Seite:
die per-Krümel Inbox/Outbox. OZMAIs raumseitige Verteilung ist ein eigener Pfad.
1. Zwei Ebenen, die sich nie berühren
| MQTT-Ebene (Raum) | Inbox/Outbox-Ebene (Kind) | |
|---|---|---|
| Hinter dem Signal steht | kein Kind | ein Kind |
| Transport | MQTT publish/subscribe, per Raum | Pull/Push über Rustcore, Zero = Client |
| Muster | Broadcast, lokal verarbeitet | Einbahn, adressiert an eine NetBox-ID |
Diese Datei beschreibt nur die Kind-Ebene. Umgebungssensorik bleibt MQTT.
2. Die drei Richtungen des Krümels
Krümel-Zero Nullfeld / Strato-Range
┌───────────────────┐ ┌──────────────────────────┐
① │ Mission fertig │──push (hoch)─▶│ inbox/<netbox_id>.jsonl │
│ JSON, gehasht │ bewusst │ (gehashte Antworten, │
│ "keine Antwort" │ antwortlos │ Weiterverarbeitung) │
├───────────────────┤ ├──────────────────────────┤
② │ cron: poll online │◀──GET (runter)│ outbox/<netbox_id>.jsonl │
│ msg_id neu? → LED │ read-only │ (Ergebnis, zertifiziert) │
├───────────────────┤ └──────────────────────────┘
③ │ crumbtype Echtzeit │ rein lokal, kein Round-Trip
│ Tastendruck → LED │ (Abschnitt 6)
└───────────────────┘
- ① Inbox (hoch): Krümel macht z. B. bashpanda-Gürtelprüfung, sendet die
Antwort als JSON. Bewusst kommt jetzt keine Antwort — fire-and-forget.
Alle Missions-Antworten landen gehasht in der Inbox zur Weiterverarbeitung.
Der Zero schreibt hoch; das Nullfeld liest den Zero nicht — es nimmt nur an. - ② Outbox (runter): Später/morgen liegt das Ergebnis bereit. cron-Poll (nur
online), neuemsg_id→ LED. „Wenn morgen dein Streifen grün leuchtet, hast
du bestanden, kleiner Freund." Kind freut sich ohne Screen, ohne Tastatur. - ③ crumbtype (lokal, Echtzeit): kein Nullfeld, kein Round-Trip. Siehe §6.
Was fließt hoch: nur die Missions-Antwort, gehasht — Inhalt aus der
Mission, adressiert an die eigene NetBox-ID. Kein Klarname, kein Kind-Attribut.
Was fließt runter: nur ein an eine NetBox-ID adressiertes, zertifiziertes
Ergebnis-JSON.
3. Adressierung — „nur für den einen Krümel"
Identität kommt aus der NetBox, nicht aus Chat/Klartextname. Ein Krümel-Raspi
trägt eine pseudonyme NetBox-Device-Identität; ihr Handle ist das to/from.
- Eine Datei pro Krümel je Richtung:
inbox/<id>.jsonl,outbox/<id>.jsonl. - Keine Klarnamen, keine Kind-Attribute im JSON. Der Handle ist die
ganze Identität (crumbmatrix: Zugehörigkeit ohne Identifizierung).
4. Die Nachrichten — je eine JSONL-Zeile
4a. Inbox (hoch) — Missions-Antwort, gehasht
{
"v": 0,
"ts": 1758132000,
"from": "krumel-7f3a",
"mission": "bashpanda/guertelpruefung",
"msg_id": "a1b2c3…64hex",
"answer_hash": "e3b0c4…64hex",
"answers": [{"q": 1, "hash": "…"}, {"q": 2, "hash": "…"}],
"witness": {"netbox_check": "mac", "nic_mac_hash": "…64hex", "class": "device-registered"},
"sig": "sha256:9f8e…64hex"
}
answer_hash bezeugt die Antwort per Hash (bezeugen statt speichern). answers
optional je Frage, für die granulare LED-Auswertung (§5). Bewusst keine
synchrone Antwort — der Push ist vollständig, sobald die Zeile liegt.
witness — die Evidenzklasse steht IM Event (die Grenze in der Architektur):
Der Zero legt offen, wie stark seine Identität belegt ist, statt sie zu
behaupten. Die MAC geht nur gehasht raus (roher MAC bleibt off-wire), ist
Teil des signierten sig-Kerns (Klasse nicht fälschbar ohne Bruch).
class |
Bedeutung | Grenze (ehrlich) |
|---|---|---|
device-registered |
MAC lesbar → gegen NetBox der Range kreuzprüfbar | MAC ist software-setzbar, keine Hardware-Attestierung |
declared |
keine MAC lesbar, nur from-Handle |
reine Behauptung, keine Gerätebindung |
hw-attested |
(Zukunft) Secure Element / TPM / DICE | erst wenn real gebaut — bis dahin nie setzen |
Kreuzprüfung grader-seitig: sha256 der in NetBox hinterlegten MAC für den
Handle vergleichen. Gotcha: MAC liegt in NetBox 4.x nur am
dcim/mac-addresses-Endpoint (nicht am Interface-Objekt). „device-registered"
sagt „diese NIC kennt die Range als diesen einen" — nicht „ein Kind hat
gedrückt" (Geräte-Identität ≠ Handlungs-Witness, NEXUS §IV).
4b. Outbox (runter) — Ergebnis
{
"v": 0,
"ts": 1758218400,
"from": "capacitoby",
"to": "krumel-7f3a",
"msg_id": "d4e5f6…64hex",
"kind": "result",
"ref": "a1b2c3…64hex",
"score": {"total": 10, "passed": 9, "per_q": [1,1,1,1,1,0,1,1,1,1]},
"body": "Fast alles grün! Frage 6 nochmal, dann sitzt der Gürtel.",
"body_hash": "…",
"sig": "sha256:…"
}
| Feld | Bedeutung |
|---|---|
kind |
result | unlock | badge | letter — steuert LED/Ton-Geste |
ref |
msg_id der Inbox-Zeile, auf die geantwortet wird (Genealogie) |
score.per_q |
Pro Frage 1=grün/0=rot → mappt direkt auf die LED-Kette (§5) |
body |
lesbarer Inhalt für das Kind, nie über das Kind |
5. Die LED als Ergebnis-Oberfläche (kein Bildschirm)
Das Ergebnis wird gesehen, nicht gelesen. Zwei Auflösungen:
- Binär: morgen grün = bestanden. Ein Puls, eine Farbe.
- Granular: 10 LEDs = 10 Fragen. Alle grün = 100 %, eine rot = 90 %.
score.per_qbildet 1:1 auf den Streifen ab.
Farb-/Ton-Vokabular kommt aus crew_signals.json (CapaciToby: 🐿️ A3/220 Hz,
Bernstein). kind und score wählen die Geste. Read-Once wie in §7: die LED
feuert genau einmal pro msg_id.
6. crumbtype — Echtzeit, rein lokal (Ebene ③)
Getrennte Ebene, kein Inbox/Outbox-Round-Trip. Kinder schauen beim Tippen
auf die Hände, nicht auf den Bildschirm. Liegt der LED-Streifen vor der
Tastatur:
- Tastendruck falsch → LED wechselt sofort die Farbe (rot). Richtig → ruhig/grün.
- Tippfehler wird in Echtzeit am Ort des Blicks sichtbar, ohne Screen.
Das läuft komplett auf dem Zero (bzw. der lokalen crumbtype-App), ohne Netz,
ohne Feststellung im Nullfeld. Dieselbe LED-Hardware, andere Quelle.
7. Einmal-Hash & Read-Once
msg_id ist zur Nachricht, was exec_id zum Receipt ist: verhindert zweite
Wirkung. Der Zero führt lokal seen.jsonl (nur msg_id, append-only). Neue
msg_id → prüfen → LED → in seen.jsonl schreiben. Bekannte msg_id → still.
(Client-Spiegel von receipt::already_witnessed.)
8. Pull-/Push-Kante
Invariant, egal welcher Transport (Filesystem → MariaDB/DB):
- Outbox read-only & Client-initiiert. Nullfeld antwortet nur, ruft nie.
- Inbox append-only. Nullfeld nimmt an, liest den Zero nicht aktiv.
- Empfänger-gescopet. Ein Zero berührt nur
*/<eigene-id>.jsonl.
Oberfläche: Rustcore-Endpoint (empfohlen, wg0-gescopet, NetBox-Token-Gate) —
Zero ist Client. MQTT scheidet für diese Ebene aus (kein Broadcast fürs Kind).
Später MariaDB inbox/outbox(id, msg_id, ts, kind, hash, sig) über denselben
Endpoint — das Format ändert sich nicht.
9. Warum das ein NEXUS-Knoten ist (Seam zu OZMAI)
| Crumbforest | NEXUS-Satz |
|---|---|
msg_id Einmal-Hash |
§7 stabile Identität, rückwirkend unverändert |
| Inbox/Outbox/seen append-only | §2/§8 ergänzen statt umschreiben |
answer_hash/body_hash/sig |
§12 Evidenz, §13 austauschbarer Nachweis (v0 sha256 → v1 echte Signatur) |
to/from = NetBox-Handle |
§11 Berechtigungswurzel endet sichtbar in der NetBox |
ref (Outbox→Inbox) |
§9 Genealogie: „beruht auf", nicht physische Kausalität |
Die zwei Sicherheits-Achsen (§12-Trennung):
- „kein Kind baut eine Armbrust" = Berechtigung/Wirkung — der Raum begrenzt,
was buildbar ist (Vor-Gate, No-Double-Effect vor dem Routing).
- „der Agent lügt & verändert nix" (#hash #vertrag) = Evidenz/Verwahrung —
append-only, bezeugen, nie überschreiben (Nach-Gate, Receipt nach Wirkung).
Föderation (§17): jede IP-Range im RZ ist ein lokaler NEXUS. nullfeld und
backbone „dürfen mitspielen" = tauschen Belege, nicht Urteile. NetBox ist
der lokale Genesis-Anker pro Range, kein globaler Root (§19).
10. Was v0 bewusst NICHT festlegt
- Echte Signatur statt
sha256-Siegel (sigv1). - MariaDB-Schema im Detail (erst wenn die Datei zu klein wird).
- Weiterverarbeitungs-Logik der gehashten Inbox (Grading-Pipeline).
- LED-/Ton-Gesten je
kind/scoreim Detail (mappt aufcrew_signals.json). - crumbtype-Tastatur-Mapping (Ebene ③, eigener Schnitt).