Propagare
Propagare ist ein ausführbares Grundprojekt für einen anonymitätsorientierten,
dezentralen Messenger. Das zugrunde liegende Wire-Protokoll heißt ENIG
(spezifiziert in docs/PROTOCOL.md); alle Adressen sind
selbstzertifizierende ENIG…-IDs, und die Metadaten-/Cover-Schicht ist
ENIG-Mix v2. Das Projekt trennt den plattformneutralen Client-Core strikt von
Frontends und Node-Servern. Android-, iOS-, Desktop-, CLI- und Web-Oberflächen
können später dieselbe Core-API verwenden.
Das Repository ist ein Security-Prototyp und kein produktionsfertiger
Messenger. Die bereits implementierten Bausteine sind real und getestet; die
noch fehlenden Sicherheitsgrenzen sind in SECURITY.md und docs/ARCHITECTURE.md
explizit aufgeführt. Die verbindliche Release-Entscheidung und alle externen
Gates stehen in docs/RELEASE-READINESS.md.
Bereits implementiert
- Client-Core als öffentliches Go-Paket ohne Benutzeroberfläche
- Node-Server mit persistenter, automatisch ablaufender Speicherung
- persistente Lösch-Tombstones, signierte Bindung von Store und Node-Identität
sowie ein exklusiver Schlüssel-Lease über die gesamte Prozesslaufzeit
- festes Speicherfenster: jedes Item läuft exakt 60 Tage nach Erstellung ab;
ein anderes Ablaufdatum wird abgelehnt, früheres Entfernen geht nur über die
geheime Lösch-Capability
- Rechenporto (Proof of Work), das bei hoher Speicherauslastung steigt
- hybride ML-KEM-768/X25519-HPKE-Verschlüsselung für versiegelte Nachrichten
- hybride Ed25519- und ML-DSA-65-Signaturen für Node-Belege und Administration
- ein Schlüssel oder eine Signatur gilt nur, wenn beide Verfahren gültig sind
- gepolsterte Textnachrichten und konstant große verschlüsselte Dateiblöcke
- konfigurierbare Dateigrößenbegrenzung
- Lösch-Capabilities statt Benutzeridentitäten auf Nodes
- Löschen von Dateien erst nach vollständigem Download und Authentifizierung
- Replikationsquorum, signierte Speicherbelege und automatische Ersatz-Nodes
- zufällige Speicherprüfungen und lokaler Ausschluss unzuverlässiger Nodes
- authentisiert verschlüsselter lokaler Client-Store mit standardmäßig und
maximal 10 GiB, oldest-first Cache-Pruning und Schutz nicht abgelaufener
Sicherheitszustände sowie exklusivem Prozess-Lock
- signierter und replaygeschützter verschlüsselter Sync zu allen durch das
aktuelle Konto-Profil autorisierten eigenen Geräten
- selbstzertifizierende, typisierte
ENIGC1…-Konten, ENIGD1…-Geräte und
ENIGG1…-Gruppen; Directory-Antworten werden lokal neu gebunden und signiert
- Registrierung hinter einer verpflichtenden OS-Keychain-/Hardware-Vault-Grenze
- hybrid gerätesignierte Nachrichten und gerätesignierte Client-Zustellbelege
- strikt geschlossener Nachrichtenpfad, der ohne auditierten PQ-Ratchet,
persistenten Replay-Speicher und Mix-/Cover-Transport nicht startet
ENIG-Mix v2: moderater, größenbegrenzter Command-Layer und konstant
getakteter Real-/Poll-/Cover-Scheduler mit festen Paketen und hartem
Fail-Closed-Verhalten
- verifizierte zufällige Full-Node-Routenzuweisung mit drei Mix-Hops, einem
Courier und drei Replikaten ohne Identitäts- oder grobe IP-Präfix-Wiederholung
- IP-basiertes Node-Verzeichnis mit ausgelieferter Seed-Pin-Liste, hybrid
signierten Kurzzeit-Leases, Rückruf-Challenge, Seed-Quorum, Ablaufbereinigung
und signierten vollständigen Snapshots für Nodes und Clients
- CA-PKI-freie Client↔Node- und Node↔Node-Kanäle: automatisch aus der Node-
Identität erzeugter Zertifikatscontainer, exaktes Schlüssel-Pinning und
erzwungener TLS-1.3-Schlüsselaustausch mit
X25519MLKEM768
- Gruppenrollen, Ban, Admin-Delegation und Eigentümerübertragung
- Provider-Schnittstelle für RFC-9420-MLS/TreeKEM
- direkte 1:1-Audio-/Videoanrufe über authentisiertes WebRTC DTLS-SRTP
- pro Call ephemerer DTLS-Fingerprint, hybrid signiertes SDP, Replay- und
Ressourcenlimits sowie klassische Forward Secrecy auf Sitzungsebene
Bewusst noch nicht als „gelöst“ bezeichnet
- Der Referenztransport ist direktes, identitätsgepinntes HTTPS ohne öffentliche
PKI (Plain HTTP nur explizit für lokale Entwicklung). Er verbirgt IP-Adressen und
Zeitkorrelationen nicht. Produktiv braucht er ein geprüftes Onion-/Mixnet-
Transportmodul, Cover Traffic, Batching und TLS/QUIC innerhalb der Hops.
- Der ENIG-Mix-Scheduler und seine PQ-/Audit-Provider-Grenzen sind implementiert;
ein konkreter auditierter PQ-hybrider Sphinx-/Mixnet-Provider, Courier und
reale Relay-Infrastruktur sind noch nicht enthalten.
- Alle Nodes sollen dasselbe Full-Node-Binary mit Mix-, Courier-, Speicher- und
Directory-Fähigkeiten ausführen. Die Aufgabenzuweisung ist implementiert; die
gemeinsame Relay-/Courier-Laufzeit und ihre Konformitätsprüfung fehlen noch.
- Die versiegelte HPKE-Nachricht ist ein sicherer PQ-hybrider Baustein, aber noch
kein vollständiges PQXDH-/Double-Ratchet-Protokoll mit Forward Secrecy und
Post-Compromise Security.
message.StrictPipeline definiert und erzwingt die
Provider-Grenze; ein konkreter auditierter Provider ist noch nicht enthalten.
- Die Secret-Vault-Schnittstelle ist implementiert, nicht jedoch die
plattformspezifischen Keychain-/Secure-Enclave-Adapter.
- ENIG-IDs verhindern unbemerkten Schlüssel-Austausch. Ein Verzeichnis kann
Profile weiterhin vorenthalten und bei einer frischen Installation einen alten,
noch gültig signierten Stand wiedergeben; dafür fehlen Key Transparency und
ein gesicherter lokaler Revisions-Pin.
- Direkte 1:1-Calls sind E2EE und klassisch forward-secret. Der DTLS-ECDHE-
Schlüsselaustausch ist noch nicht post-quantenresistent und besitzt während
eines laufenden Calls keine Post-Compromise-Recovery. SFU-/Gruppencalls
benötigen eine auditierte SFrame+MLS-Integration und sind nicht aktiviert.
- Gruppen-Administration ist implementiert; die eigentliche Gruppenverschlüsselung
muss über einen auditierten MLS-Provider erfolgen. Die hybriden PQ-MLS-
Ciphersuites sind im Juli 2026 noch IETF-Entwürfe.
- Das signierte Node-Verzeichnis verhindert ungeprüfte Einzelanmeldungen, ist
aber noch keine permissionless Sybil-Lösung: Seed-Quorum, Betreiberdiversität,
Probezeit und Ressourcennachweis bleiben eine Produktionsanforderung.
- Ein lokaler Client kann böse Nodes ausschließen. Netzwerkweit beweisbarer
Ausschluss benötigt Merkle-Speichernachweise und mehrere unabhängige Auditoren.
Projektstruktur
account/ Konten, Geräte-Zertifikate und verschlüsselter Geräte-Sync
identity/ selbstzertifizierende ENIG-Konto-, Geräte- und Gruppen-IDs
message/ signierte Nachrichten/Client-Belege und strikte Provider-Grenzen
mixtransport/ ENIG-Mix-v2-Commands und moderater Real/Poll/Cover-Scheduler
nodedir/ signierte IP-Leases, Seed-Quorum und Snapshot-Reconciliation
client/ UI-unabhängiger Client-Core, Replikation, Audit und Failover
cmd/propagare-node/ ausführbarer Referenz-Node
group/ Admin-/Ban-Zustandsmaschine und MLS-Provider-Grenze
call/ direkte, hybrid authentisierte WebRTC-DTLS-SRTP-Calls
media/ gepolsterte, verschlüsselte Datei- und Bildblöcke
node/ Node-HTTP-API und persistenter Speicher
pqcrypto/ Hybrid-HPKE, Hybrid-Signaturen und Padding
transportauth/ CA-PKI-freies Node-Key-Pinning und hybrides TLS-1.3-Profil
protocol/ versionsgebundene Transportobjekte und Proof of Work
docs/ Architektur, Protokoll und Codex-Weiterbauanleitung
Voraussetzungen
- Go 1.26.5 oder neuer;
go.mod pinnt den minimal gehärteten Toolchain
- Linux, macOS oder Windows für die persistente Preview; weitere per Build-Tag
unterstützte Unix-Ziele benötigen einen eigenen Plattform-CI-Lauf, alle
anderen Dateisystemplattformen scheitern an der Lock-/Rechte-/Durability-
Grenze geschlossen
Go 1.26 wird wegen dessen standardisierter ML-KEM- und hybrider
HPKE-Unterstützung benötigt. Patchlevel 1.26.5 ist das Minimum, weil ältere
1.26-Patchstände erreichbare Standardbibliotheks-Advisories enthalten.
Testen
go mod download
go mod verify
test -z "$(gofmt -l $(git ls-files '*.go'))"
go mod tidy -diff
go vet ./...
go test -count=1 ./...
go test -race -count=1 ./...
go run golang.org/x/vuln/cmd/govulncheck@v1.1.4 -show verbose ./...
Die Integrationstests starten lokale kurzlebige Nodes und prüfen Verschlüsselung,
Replikation, Ausfallersatz, Speicher-Audit und Löschen.
Entwicklungs-Node starten
go run ./cmd/propagare-node \
-listen 127.0.0.1:8787 \
-data ./local/node-1-data \
-key ./local/node-1-key.json \
-difficulty 16
Für ein Quorum sollten mindestens fünf unabhängige Nodes laufen. Der
Referenzserver sollte nicht direkt ins öffentliche Internet gestellt werden.
Der Standardschlüssel liegt getrennt vom Datenspeicher unter
./propagare-secrets/node-key.json. Ein gebundener Store startet bei fehlendem
Schlüssel immer geschlossen und erzeugt keine neue Identität. Soll ein bereits
vorhandener Schlüssel absichtlich an ein neues leeres Datenverzeichnis gebunden
werden, ist beim ersten Start zusätzlich -initialize-empty-store erforderlich.
Alte nichtleere Stores ohne signierte Identitätsbindung werden nicht automatisch
migriert; sie müssen mit einem kontrollierten, offline geprüften Migrationsplan
überführt werden.
Der optionale Verzeichnisbetrieb verlangt eine mit Client-Releases gemeinsam
ausgelieferte propagare-seeds.json. Sie pinnt vollständige hybride
Seed-Identitäten und literale IP-Endpunkte. Eine öffentliche Node wird zum
Beispiel mit -advertise-ip, -advertise-port, -node-seeds und
-node-quorum gestartet. Der Server erzeugt seinen Zertifikatscontainer
automatisch aus dem persistenten Node-Schlüssel; -tls-cert, -tls-key, eine
CA und IP-SANs sind nicht erforderlich. Reale Seed-IP-Adressen und -Schlüssel
sind absichtlich nicht erfunden oder im Prototyp vorgegeben. Der
Zertifikatscontainer wird vor Ablauf automatisch erneuert, ohne den gepinnten
Node-Schlüssel zu ändern.
Direkttransport verwenden
nodeA, _ := client.ConnectPinnedHTTPNode(ctx, "https://127.0.0.1:8787", pinnedIdentityA, nil)
nodeB, _ := client.ConnectPinnedHTTPNode(ctx, "https://127.0.0.1:8788", pinnedIdentityB, nil)
nodeC, _ := client.ConnectPinnedHTTPNode(ctx, "https://127.0.0.1:8789", pinnedIdentityC, nil)
// storeKey muss produktiv aus OS-Keychain/Secure Enclave kommen.
store, _ := client.NewEncryptedDiskStore(client.DiskClientStoreConfig{
Directory: "./local/client-state",
Key: storeKey,
// 0 verwendet das sichere Standard- und Maximallimit von 10 GiB.
MaxBytes: 0,
}, time.Now())
core, _ := client.New(client.Config{
Nodes: []*client.HTTPNode{nodeA, nodeB, nodeC},
Replicas: 3,
WriteQuorum: 2,
Store: store,
})
recipient, _ := pqcrypto.GenerateHybridKEMKeyPair()
routeTag, _ := client.RandomCapability()
// Jedes Item wird exakt für das feste 60-Tage-Protokollfenster gespeichert.
delivery, err := core.SendDirect(
ctx,
recipient.PublicKey,
routeTag,
[]byte("Hallo"),
)
Die pinnedIdentity*-Werte müssen aus der ausgelieferten Seed-Liste oder einem
vollständig verifizierten Directory-Record stammen; sie dürfen nicht vom gerade
kontaktierten Netzpfad übernommen werden. Der ausführbare Node lauscht immer mit
diesem CA-PKI-freien verschlüsselten Transport. Plain HTTP bleibt ausschließlich
für eingebettete private Entwicklungstests über
DiscoverHTTPNodeForDevelopment verfügbar.
Core.SendDirect ist nur der direkte Entwicklungs-/Bootstrap-Pfad und erfüllt
weder Forward Secrecy noch Metadatenanonymität. Eine produktive App registriert
zuerst mit account.Register(ctx, secretVault), teilt die resultierende
ENIGC1…-ID und verwendet ausschließlich message.NewStrictPipeline mit
auditiertem Ratchet-, Replay- und Mixnet-Adapter.
Frontends sollen weder eigene Kryptografie implementieren noch direkt mit Nodes
sprechen. Sie rufen ausschließlich den Client-Core auf und erhalten daraus
Ereignisse, Nachrichtenmodelle und Zustandsänderungen.
Die geplante stabile Oberfläche für Apps und alternative Frontends steht in
docs/FRONTEND-API.md.
Das eigene Metadatenprotokoll ist in
docs/ENIG-MIX-V2.md spezifiziert. Das Node-Verzeichnis ist
in docs/NODE-DIRECTORY-V1.md beschrieben.
Direkten 1:1-Call aushandeln
caller, _ := call.NewEndpoint(call.Config{Signer: callerDeviceSigner})
callee, _ := call.NewEndpoint(call.Config{Signer: calleeDeviceSigner})
outgoing, offer, _ := caller.Start(ctx, callee.Identity(), call.Media{Audio: true})
// offer als Inhalt einer bestehenden E2E-verschlüsselten Nachricht senden.
incoming, answer, _ := callee.Accept(ctx, caller.Identity(), offer)
// answer auf demselben E2E-Kanal zurücksenden.
_ = outgoing.ApplyAnswer(answer)
// Plattformadapter hängen danach Pion-Audio-/Videotracks an die Sessions.
defer outgoing.Close()
defer incoming.Close()
Das vollständige SDP darf nicht durch ein Frontend oder einen Signaling-Server
verändert werden. Direkte Kandidaten legen dem Gesprächspartner IP-Adressen
offen; ICETransportPolicyRelay erzwingt TURN, schützt aber nicht vor Timing-
und Volumenmetadaten beim Relay.
Kryptografische Grundlagen
Vor einem realen Einsatz sind unabhängige Protokoll-, Kryptografie-,
Metadaten- und Implementierungs-Audits zwingend.
Lizenz
Dieses Projekt steht unter der GNU Affero General Public License v3.0
(AGPL-3.0). Wer Propagare verändert und als Netzwerkdienst betreibt, muss den
Quelltext der veränderten Version den Nutzern dieses Dienstes zugänglich machen.