# DH-003: Persistenz, Wiederanlauf und Migration Status: implementierter lokaler v1-Speicher mit Prozess-Crash-Tests. Linux-Dateisysteme mit korrektem fsync/rename sind der primäre Zielpfad. **Kein** Nachweis für reale Stromausfälle, Windows-Powerloss, NFS oder fehlerhafte Hardware. Dieses Dokument beschreibt nur den lokalen Repo-Store; der getrennte Node-Event-/Config-/Outbox-Store nutzt inzwischen ebenfalls prozessgesperrte begrenzte atomare State-Dateien (siehe DDP-Spezifikation). ## Unveränderte Daten HEAD bleibt eine lowercase SHA-256-ID mit Newline. `.dh/objects/xx/...` enthält unveränderte Blob-/Manifest-Bytes; `.dh/snapshots/ID` bleibt die kompatible Manifest-Kopie. Neue lokale Commits verwenden weiter `dh-snapshot/1` und lesen alte Eltern unverändert. Bestehende Daten werden weder gescannt noch migriert beim Serverstart. Keine Hintergrundlöschung oder automatische Konvertierung. ## Commit-Protokoll 1. `.dh` ohne Symlink öffnen; `WRITELOCK` mit exklusivem OS-Lock belegen. Linux: flock; Windows: LockFileEx. Andere Plattformen lehnen Writes ab. Ein zweiter Writer bekommt einen Busy-Fehler. Lock-Datei niemals löschen: der Kernel gibt den Lock bei Close/Prozessende automatisch frei. 2. HEAD unter Lock lesen. `--expected-head ID` prüft ausdrücklich den erwarteten Basisstand; `--expected-head ''` erwartet ein leeres Repo. Stale Basis führt zu Konflikt, nicht zum Überschreiben einer abweichenden Historie. 3. Vorhandene erreichbare Historie prüfen; Dateien eingeschlossen lesen, Ignore-Regeln beachten. Jeder neue Blob höchstens 32 MiB, 10.000 Dateien, insgesamt höchstens 512 MiB pro neuem Snapshot, Manifest höchstens 4 MiB. Neue Pfade müssen portable sein; Groß-/Kleinschreibkollisionen werden abgelehnt. 4. Eindeutiges Tempfile im Zielverzeichnis, Write → File.Sync → Close. Hashadressiertes Objekt mit nichtüberschreibendem Hardlink installieren; Parent-Verzeichnis synchronisieren. Bestehende Objekte neu hashen und Datei/Verzeichnis synchronisieren; Korruption wird nicht überschrieben. 5. Manifest-Objekt und kompatible Snapshot-Kopie genauso dauerhaft installieren. 6. HEAD erneut mit erwarteter Basis vergleichen. Eindeutiges HEAD-Tempfile schreiben/synchronisieren, atomar umbenennen, `.dh` synchronisieren. Erst danach Erfolg melden. Es gibt keine gemeinsamen `HEAD.tmp`-Writer mehr. Ein Abbruch vor HEAD-Installation belässt alten HEAD und ggf. unreferenzierte Objekte. Danach ist der neue HEAD vollständig referenzierbar. Fehler nach Installation werden als **Ergebnis unklar** gemeldet; Log/Verify prüfen statt blind denselben Vorgang zu wiederholen. Windows synchronisiert Datei-Inhalte, aber die Standardbibliothek garantiert hier kein Directory-fsync: dessen Stromausfall-Dauerhaftigkeit bleibt ein explizites Plattform-Gate. Sperren gelten für kooperierende aktuelle Prozesse. Alte Binaries kennen den Lock nicht; alte/neue Writer niemals gleichzeitig auf dasselbe Repo loslassen. Dateisystem-Eigner können Dateien weiterhin manipulieren. Keine Aussage, dass dies einen bösartigen Prozess mit denselben OS-Rechten vollständig isoliert. ## Recovery `decentralhub recover --workdir DIR` nimmt den Writer-Lock, vervollständigt ggf. leere Init-Verzeichnisse, verifiziert den gesamten erreichbaren Graphen und entfernt nur `.tmp-*`/legacy `HEAD.tmp` innerhalb des Speichers. Symlinks oder Korruption führen zu Fehlern, nicht zu erratenem HEAD. Verwaiste unveränderliche Objekte bleiben erhalten; Recovery ist kein Garbage Collector und löscht keine gültige Historie. Der Browser ignoriert Tempfiles und liest nur HEAD-erreichbare Objekte. Auf einem defekten Dateisystem zuerst eine unveränderte Kopie sichern. ## Export und Backup Export validiert das ganze Manifest, portable/case-eindeutige Pfade, alle Objekthashes und Größen vor dem Schreiben. Bestehende Ziele werden nie übernommen. Auf **Linux amd64/arm64** entstehen Dateien zuerst in einem zufälligen privaten Geschwisterordner `.mf-export-`. Jede Datei und danach sämtliche enthaltenen Verzeichnisse werden von unten nach oben synchronisiert. Erst anschließend veröffentlicht fd-relatives `renameat2(RENAME_NOREPLACE)` den vollständigen Baum; auch ein gleichzeitig angelegtes leeres Zielverzeichnis wird nicht überschrieben. Danach wird das Parent-Verzeichnis synchronisiert. Fehlt diese Kernel-/Filesystem- Operation, bricht Export ohne unsicheren Rename-Fallback ab. Grundlage: [Linux rename(2)](https://man7.org/linux/man-pages/man2/rename.2.html). Der Parent muss der aktuellen UID oder root gehören; group/other-write ist nur mit Sticky-Schutz erlaubt. Die Staging-Inode wird zusätzlich vor Veröffentlichung und normalem Cleanup geprüft. Andere Prozesse derselben UID und privilegierte Administratoren bleiben Teil der vertrauenswürdigen lokalen Umgebung. Prozessabbruch vor Rename lässt das gewünschte Ziel fehlen; nach Rename ist der vollständige Baum sichtbar. Ein Fehler danach meldet `ErrExportUncertain`; das veröffentlichte Ziel wird **nicht** entfernt. Vor erneutem Export Inhalt prüfen und ein neues Ziel wählen. Ein Crash kann private `.mf-export-*`-Reste hinterlassen. Nach Beenden aller zugehörigen Exporte ausschließlich den eigenen bekannten Restordner prüfen/entfernen; kein automatischer Sweep beliebiger Geschwisterordner. Ein neues Exportziel kann unabhängig davon erneut erstellt werden. Auf **Windows und anderen Architekturen** bleibt exklusives Erstellen mit Cleanup bei normalen Fehlern erhalten; Prozessabbruch kann dort weiterhin ein unvollständiges Ziel hinterlassen. Keine plattformübergreifende Crash- oder Powerloss-Atomarität behaupten. Auch unter Linux sind die Tests Prozessabbrüche, keine echten Stromausfälle. Der öffentliche v1-Clone ist weiterhin keine atomare Export-plus-Metadaten-Transaktion. `backup --workdir DIR --dest NEW_BACKUP` sichert unter Writerlock die vollständigen v1-Snapshots und unveränderlichen Objekte einschließlich detached Historie und der optionalen `.dh/ignore` (max256KiB). Der kanonische Hashmanifest bindet alle gesicherten Bytes. Arbeitsdateien, Node-/Geräteschlüssel und Remotecredentials sind getrennte Sicherungsgüter; unbekannte Metadataerweiterungen werden mit Fehler abgelehnt und niemals still mitgenommen oder ausgelassen. `restore --source BACKUP --dest NEW_REPO --expected-head HASH --expected-manifest SHA256` verlangt einen unabhängig erhaltenen HEAD (bewusst leer bei leerem Repo), den separat vertrauenswürdig aufbewahrten Digest des exakten kanonischen Manifests und ein neues Ziel. Der Backupbefehl gibt beide Pins nach voller Prüfung aus. Der Restore leitet sie niemals als Vertrauen aus derselben untrusted Quelle ab. Er stellt Metadaten einschließlich Ignore-Overrides wieder her, verändert keinen bestehenden Arbeitsbaum und führt keinen Code aus. HEAD authentisiert seine Ancestry, nicht sämtliche detached/orphan Bytes; deshalb pinnt die CLI zusätzlich immer das vollständige Manifest, auch bei Backups ohne Ignore-Datei. Das schützt vor selbstkonsistent veränderter oder komplett entfernter Ausschlusspolitik bei unverändertem HEAD. Die ältere Bibliotheksoption ohne Manifestpin für Backups ohne Ignore-Datei ist ausschließlich strukturelle Prüfung eines anderweitig vertrauenswürdigen lokalen Backups; sie kann eine entfernte Ignore-Datei nicht erkennen. Der private Clone transportiert diese Metadaten verschlüsselt und materialisiert den geprüften aktuellen Snapshot atomar in ein neues Ziel. Backup/Restore sind zunächst auf Linux amd64/arm64 unterstützt; kein unsicherer Plattformfallback. Zusätzlich erforderliche Arbeitsdateien und Identitäten getrennt bei gestoppten Writern sichern. Restore zunächst in einem neuen Ordner mit `verify` prüfen, Browser/Export prüfen, erst dann den gewünschten Pfad umstellen. Rollback benötigt keine Datentransformation: v1-Bytes/IDs bleiben gleich. Ein älteres Binary verliert die neuen Sicherheits-/Durability-Eigenschaften; es darf nicht neben einem neuen Writer laufen. DOP/1 bleibt separater Codec und wird nicht in diesen Namespace geschrieben. ## Nachweise und Grenzen `TestCommitCrashRecovery` beendet einen separaten Prozess an neun Write-/Sync-/ Install-Grenzen. Danach gilt: alter oder neuer HEAD, vollständige Verify-Kette, Recovery erfolgreich und OS-Lock wieder verfügbar. Weitere Tests prüfen CAS, unklaren Commit-Ausgang, feste Alt-JSON-Bytes, manipulierte vorhandene Objekte, .dh-Symlinks, parallele Writer, fehlende Export-Objekte und Windows-Pfadfallen. Diese Tests simulieren Prozessende, nicht die Storage-Cache-Effekte eines echten Stromausfalls. Zwölf weitere Operationstests injizieren tatsächliche Teilwrites vor ENOSPC, Short-write ohne Fehler sowie File-/Directory-Sync-EIO für Blob, Snapshot und HEAD; nur nach HEAD-Installation ist der Commit-Ausgang unklar. Fünf Linux-Export-Crashpunkte, konkurrierende Zieltypen, Shared-Parent-Ablehnung, Export-I/O-Fehler und Flushreihenfolge sind getestet. Ein unabhängiger CLI-Test verursacht per Child-RLIMIT_FSIZE einen echten Kernel-EFBIG-Writefehler, prüft unverändertes HEAD und anschließenden Recover/CAS/Export. Das ist weder ein physisch volles Dateisystem noch ein echter fsync-/Powerloss-/Windows/NFS-Test.