# Experimenteller privater nativer Workflow Ein ausdrücklich separat angelegter privater Store speichert nur verschlüsselte Pakete. Er hat weder öffentliche Repositorylisten noch unauthentisierte Objekt-/Ref-Lesezugriffe. Private Kennungen, Dateien und Mitgliedschaften werden nicht in Discovery, öffentliche Feeds oder MCP aufgenommen. Das bestehende Public-Protokoll bleibt getrennt und unverändert. **Aktueller Plattformumfang:** Private Writer mit dauerhaftem Nonce-Budget und atomarer nativer Clone sind zunächst für Linux amd64/arm64 implementiert. Andere Plattformen verweigern diese Teile ausdrücklich. Windows-Crossbuilds sind keine Windows-Laufzeit- oder ACL-Abnahme. Dieser begrenzte Workflow ist keine Produktionsfreigabe oder vollständige Gruppen-Key-Recovery. ## Vertrauen und Vorbereitung Owner und Geräte erzeugen getrennte Identitäten mit `init --data DIR`. `identity-public` zeigt ausschließlich den öffentlichen Schlüssel. Der Owner bleibt für Enrollment offline; der Node erhält niemals seinen privaten Schlüssel. Eine zufällige öffentliche Repo-ID liefert `private-package new-id`. ```sh mf delegate --data /offline/owner --repo PRIVATE_REPO_ID --device-key DEVICE_PUBLIC --permissions read,write --ttl 24h --dest /handoff/device.json mf private-writer-init --writer /protected/new-writer --repo PRIVATE_REPO_ID --epoch 1 --device-key DEVICE_PUBLIC ``` Für ein nur lesendes Gerät `--permissions read` verwenden. Die Ausgabe von `private-writer-init` ist eine **öffentliche vollständige Writerbindung**; sie separat als JSON behalten. Der geheime Datenschlüssel verbleibt in `private.key`. Jeder Writer hat einen eigenen zufälligen Schlüssel, eine eigene DataKeyID und InstallationID. Fehlende Counterdateien dürfen nicht neu initialisiert werden. Jeder Verschlüsselungsversuch wird vor AES-GCM dauerhaft reserviert; höchstens 2^20 Versuche je Writer. Crashs verbrauchen Reservierungen. Read-Key-Import oder Clone erzeugt niemals einen Writer aus alten Schlüsseln. Ein Membership-Body folgt exakt dem typisierten Schema in `internal/privatesync/types.go`: Schema/Repo/Owner, nächste Sequenz und Epoche, Vorgänger-ID, Head-Cutover, Ausgabezeit, sortierte Mitglieder. Jedes Mitglied enthält das Owner-signierte Delegationszertifikat; Writer zusätzlich ihre neue DataKeyID, reine Leser eine leere DataKeyID. Initial Sequenz=Epoche=1, Vorgänger und Head leer, Headrevision=0. ```sh mf private-sign --kind membership --file /handoff/membership-body.json --data /offline/owner mf private-id --kind membership --file /handoff/membership-body.json mf private-store-init --dest /node/new-private-store --owner-key OWNER_PUBLIC --origin https://trusted.example --repo PRIVATE_REPO_ID --file /handoff/signed-membership.json ``` Signierung und IDs akzeptieren feste typisierte Feldreihenfolge, Einrückung ist erlaubt. Signierte Ausgabe enthält keine privaten Schlüssel. Owner, Repo und Origin kommen aus einem unabhängig authentisierten Kanal, nie aus der Antwort eines unbekannten Peers. ## Node und Client explizit verbinden ```sh meshforge serve --data /node/discovery --addr 127.0.0.1:8787 --private-sync /node/new-private-store --private-owner OWNER_PUBLIC --private-repo PRIVATE_REPO_ID --private-origin http://127.0.0.1:8787 ``` Ohne diese vier privaten Flags wird der Endpunkt nicht angehängt. Der Node-Key für Discovery ist keine private Repository-Owneridentität. Kein Serverlog nennt private Repo-ID, Dateinamen oder Inhalte. HTTPS ist außerhalb Loopback Pflicht; LAN-Adressen benötigen einen eigenen späteren expliziten Netzwerkvertrag. Die lokale Browseroberfläche wird dadurch nicht zur entfernten Benutzeranmeldung. Clients benötigen ein unabhängig übermitteltes Onboarding-JSON mit den beiden Feldern `memberships` und `heads`: vollständige Owner-signierte Membershipkette und Device-signierte Headhistorie, initial `[signed_initial_membership]` und `[]`. Ein älterer Head-/Membershipprefix kann nie einen bereits bekannten neueren Checkpoint ersetzen. Gültiger eigener Widerruf wird ebenfalls dauerhaft gepinnt. ```sh mf private-client-init --state /protected/client --data /protected/device --delegation /handoff/device.json --repo PRIVATE_REPO_ID --owner-key OWNER_PUBLIC --origin https://trusted.example --onboarding /handoff/reference.json mf private-reference --state /protected/client --data /protected/device --delegation /handoff/device.json --repo PRIVATE_REPO_ID --owner-key OWNER_PUBLIC --origin https://trusted.example ``` Alle Netzwerkbefehle verwenden dieselben expliziten Clientparameter. Status- und Referenzausgaben sind private Metadaten für den lokalen Bediener, keine öffentlichen Discoverydokumente. Signier-, Daten- und Recoverykeys erscheinen niemals in Befehlszeilenargumenten oder stdout. ## Daten übertragen und lesen Ein Owner stellt Leseschlüssel separat über einen authentisierten vertraulichen Offlinekanal bereit. Die Exportdatei muss neu sein und `reader.keys` heißen: ```sh mf private-writer-share --writer /protected/new-writer --binding /handoff/writer-binding.json --dest /separate/new-share/reader.keys mf private-push CLIENT_FLAGS --writer /protected/new-writer --binding /handoff/writer-binding.json --workdir /projects/source --outbox /protected/new-transfer --read-keys /separate/old-share/reader.keys mf private-clone CLIENT_FLAGS --read-keys /separate/new-share/reader.keys --dest /projects/new-clone ``` `CLIENT_FLAGS` steht hier für die vollständigen obigen Parameter. Historische Read-Key-Dateien können wiederholt über `--read-keys` angegeben werden. Sie sind Secrets und benötigen unter Linux Eigentümer-Leserechte (0400 oder 0600). Es gibt keinen Passwortmodus und keinen privaten Schlüsseltransport zum Node. Ein neuer Push sichert den vollständigen nativen `.dh`-Bestand unter Writerlock, prüft dessen innere Vorgängerkette gegen **alle** zuvor akzeptierten privaten Heads und verschlüsselt Manifest und gebundene Chunks. Ein Offlinefork schlägt vor Verschlüsselung oder Upload als Konflikt fehl; beide lokalen Historien bleiben erhalten. Ein bestehender vollständiger Outboxpfad sendet ausschließlich seine alten unveränderlichen Ciphertextbytes erneut. Geänderte lokale Commits werden nicht still in diesen Wiederholungsversuch aufgenommen. Eine unvollständige Outbox ist ein Fehler, kein Anlass zum Reset eines Counters. Clone entschlüsselt sämtliche gebundenen Chunks, prüft Dateiinventar, Hashes, Offsets, historische native Abstammung und vollständige `.dh`-Integrität. Erst danach erscheinen Historie und Arbeitsdateien zusammen in einem **neuen** Ziel. Vorhandene Dateien/Projekte werden nie überschrieben. Fetch/Pull in bestehende Arbeitsbäume und automatisches Merge sind gesonderte noch offene Integrationen. ## Widerruf und Grenzen Jeder Mitgliederwechsel ist Owner-signiert und erhöht Sequenz/Epoche jeweils um eins. Der Owner bindet den aktuellen Head samt Revision als Cutover. Alle Writer erhalten neue zufällige Schlüssel und neue DataKeyIDs. Entfernte Zertifikate können nicht reaktiviert werden. `private-membership-apply` überträgt das exakt signierte CAS-Dokument; `private-client-onboard` pinnt die unabhängig bestätigte neue vollständige Referenz. Ohne aktuellen Mitgliedschaftspin scheitern Reads und Writes. `private-writer-retire` sperrt weitere Verschlüsselung dauerhaft. Bereits bekannte historische Klartexte und alte Leseschlüssel sind nicht rückrufbar. Der erste Store ist absichtlich begrenzt: max. 64 Ciphertextobjekte, je 256 KiB, zusammen 8 MiB, je 256 Membership-/Headeinträge, finales Statefile 16 MiB. Die native Bridge erlaubt höchstens 4 MiB Backupdaten, 4096 Dateien und 128-KiB- Chunks; vorhandene Historie belastet die Gesamtquota weiter. Kein Historypruning oder stilles Abschneiden. Auf Rate-Limits reagiert der Client begrenzt mit Retry-After und **frischer** Challenge bei unveränderten Operationsbytes. `--timeout` umfasst diese Wartezeiten. Übernahme unbekannter Peers, Proxies, Redirects, öffentliche Fallbacks und automatische Veröffentlichung fehlen bewusst. Schlüsselprovisionierung ist offline/manuell. OS-KeyStore, vollständige Owner-Recovery, echte Windows-Backends und externe Abnahme sind weiterhin offen. Lokaler Admin/Dateisystem/Uhr bleiben Vertrauensgrenzen. Das schützt nicht vor vollständigem lokalem Counter-/Checkpointrollback oder einem kompromittierten berechtigten Client. Die vollständige v1-Historyübertragung erhält auch die optionale `.dh/ignore` (max256KiB) bytegenau und verschlüsselt. Der Clone verwendet dieselben lokalen Ausschlussregeln für spätere Commits. Der Restore prüft ausdrücklich den Manifestdigest aus dem AEAD-authentisierten Transfermanifest; ausgeschlossene Arbeitsdateien werden nicht als Backupinhalt eingesammelt. Node-/Writer-/Readerkeys sind weiterhin separate Geheimnisse. Unbekannte Metadataerweiterungen werden abgelehnt, nicht still übertragen oder gelöscht. ## Expliziter Store v2 und Transfer v2 (Runde 3, noch kein Releasecheckpoint) Storeformat und Transferformat sind getrennte Entscheidungen. Standard bleibt jeweils v1. Ein v2-Store wird mit `private-store-init --backend v2` erstellt und mit `serve --private-backend v2` geöffnet; alle bisherigen unabhängigen Pins bleiben Pflicht. Ein bestehender v1-Store wird niemals automatisch migriert. Bei einer Formatverwechslung schlägt Open fehl. Store v2 ist Linux-spezifisch; andere Plattformen lehnen ihn ab. V2 speichert unveränderliche Ciphertexte als Dateien, das Indexfile enthält nur Metadaten. Gesamtlimit 2 GiB einschließlich 64 MiB Kontrollreserve, höchstens 16384 belastete Dateislots einschließlich Reservierungen, Crashreste und Checkpointreserve, unverändert 256 KiB je Envelope und 256 Historieneinträge. Bei einem abgebrochenen Upload bleiben Reservierungen/Dateien belastet und inaktiv. Eine frische authentisierte Wiederholung darf ausschließlich exakte Bytes abschließen. Es gibt kein automatisches GC oder Quotareset. Rein lokale Diagnose unter denselben unabhängigen Storepins: ```sh mf private-store-inspect --backend v2 --store /node/private-v2 --owner-key OWNER_PUBLIC --origin https://trusted.example --repo PRIVATE_REPO_ID ``` Die Ausgabe enthält nur aggregierte Counts/Bytekosten/Grenzen. Sie verändert weder Indexuhr noch Reservierungsstatus und ist nicht per HTTP verfügbar. `reserved_bytes` ist die Summe der Reservierungsgrößen, kein zusätzlicher Posten zum bereits konservativ berechneten `charged_bytes`. Transfer v2 wird unabhängig am Client gewählt: ```sh mf private-push CLIENT_FLAGS --transfer v2 --writer /protected/new-writer --binding /handoff/writer-binding.json --workdir /projects/source --outbox /protected/new-v2-transfer --read-keys /separate/old-share/reader.keys mf private-clone CLIENT_FLAGS --transfer v2 --read-keys /separate/new-share/reader.keys --dest /projects/new-clone ``` V2 bindet bis 64 MiB vollständige Backupbytes/4096 Dateien/512 Chunks über verschlüsselte Manifestseiten. Der Payloadpfad puffert einzelne 128-KiB-Chunks, nicht die gesamte Sicherung. Der existierende Backup-/Restorepfad hat eigene Datei-/Speicher-/Scratchgrenzen; die 64-MiB-Transfergrenze ist keine Zusage für den gesamten Prozessspeicher oder temporären Plattenbedarf. Ein vollständiges Backup wird vor dieser Transfergrenzenprüfung auf dem Datenträger erstellt. Eine vollständige Outbox wird vor dem ersten Upload vollständig geprüft. Teilvorbereitung wird als Fehler erhalten, nicht neu verschlüsselt. Wiederholung verwendet dieselben Ciphertexte und denselben signierten Head. Clone v2 liest explizit versionierte v1- und v2-Transfers einschließlich gemischter Historie; Clone v1 lehnt einen aktuellen v2-Transfer vor einer größeren Allokation ab. Beschädigtes v2 wird niemals als v1 neu interpretiert. [Storevertrag](PRIVATE_STORE_V2_DESIGN.md) und [Transfervertrag](PRIVATE_BRIDGE_V2_DESIGN.md) nennen sämtliche Grenzen. Der Store scannt bei Operationen das gesamte Inventar und schreibt den Index neu; dies verursacht bei vielen Chunks insgesamt quadratische Arbeit. Ungültige Zertifikate werden jetzt vor diesem Scan abgewiesen; gültige alte Credentials, autorisierte Last und Internet-DoS benötigen weitere Betriebsbelege. Keine Produktionsdurchsatz- oder SLO-Zusage folgt daraus.