01 · Problem
Wofür braucht man das?
Peers müssen sich finden, eine stabile Identität aushandeln, Nachrichten begrenzen und Vertraulichkeit sowie Authentizität über eine Session sicherstellen.
02 · Denkmodell
Das mentale Modell
Discovery verrät nur eine Adresse. Ein Handshake bestätigt Identität und Fähigkeiten; danach transportieren längengerahmte, sequenzierte Nachrichten über eine etablierte sichere Session.
Startet TCP-Peers explizit und überträgt größenbegrenzte Frames mit Längenpräfix. Lokale Discovery ist der Standard; NetworkMDNS lässt sich mit einem veröffentlichten Netzwerk-Hostnamen injizieren. TCP-Daten sind unverschlüsselt; Vertrauensaufbau, Authentifizierung und Replay-Schutz benötigen ein vollständiges Protokoll.
03 · Kontrollfluss
Was passiert in welcher Reihenfolge?
- Per mDNS nur nodeId, Host, Port und öffentlichen Fingerprint ankündigen.
- Transport verbinden und Peer über TLS oder ein geprüftes Handshake-Protokoll authentifizieren.
- Stabile Node-IDs und Protokollversionen austauschen; Self- und Duplicate-Verbindungen ablehnen.
- Nachrichten mit Länge, Typ und Sequenz framen und harte Maximalgrößen erzwingen.
- Backpressure, Heartbeat, Reconnect und vollständigen Shutdown implementieren.
04 · Bauteile
Die entscheidenden Verträge
P2P.start / discoverPeers / destroyVerwaltet TCP-Server und Discovery-Lebenszyklus.Peer.connect / send / disconnectÜberträgt begrenzte Klartext-Frames über TCP.KeyPairEncryptionDemonstriert RSA-Verschlüsselung und Signieren als separate Primitive.05 · Selbst bauen
Selbst implementieren
Baue kein eigenes Kryptoprotokoll. Beginne mit klarem Framing und verwende eine etablierte sichere Transportschicht.
- Trenne Discovery, Transport, Session und Peer-Registry in eigene Ports.
- Nutze TLS oder Noise und binde den erwarteten Fingerprint an einen Trust Store.
- Erzwinge Frame-Limits, Sequenzen, Timeouts und idempotenten Lifecycle.
type Frame = { type: number; sequence: number; payload: Uint8Array };
function encodeFrame(frame: Frame) {
const body = encodeBody(frame);
if (body.length > 1_048_576) throw new RangeError("Frame too large");
const output = new Uint8Array(4 + body.length);
new DataView(output.buffer).setUint32(0, body.length);
output.set(body, 4);
return output;
}
function acceptSequence(last: number, next: number) {
if (next !== last + 1) throw new Error("Invalid sequence");
}06 · Verifizieren
Was solltest du testen?
- TCP-Chunks mit halbem, genau einem und mehreren Frames werden korrekt reassembliert.
- Zu große Frames, Replay-Sequenzen und falsche Peer-Fingerprints werden vor Payload-Verarbeitung abgelehnt.
- Disconnect, Timeout und destroy räumen Server, Sockets, Timer und Discovery vollständig auf.
Selbst ausprobieren
Formuliere die erste Prüfaussage als fehlschlagenden Test, implementiere die kleinste Korrektur und ergänze einen Grenzfall.
TCP-Chunks mit halbem, genau einem und mehreren Frames werden korrekt reassembliert.
Ansatz vergleichen
Dein Test sollte beim gezielten Verletzen der Invariante fehlschlagen und nach der Korrektur wieder bestehen. Halte den Aufbau deterministisch und prüfe ein beobachtbares Ergebnis.
07 · Grenzen
Kompromisse und Stolperfallen
- mDNS entdeckt nur Peers im lokalen Netz; Internet-P2P braucht Rendezvous, NAT-Traversal oder Relay.
- Direct RSA encryption eignet sich nicht für beliebige Payloads; Sessions verwenden symmetrische AEAD-Keys.
- Discovery-Metadaten sind ohne nachfolgenden Trust-Check nicht vertrauenswürdig.
Lokale Discovery ist der Standard; NetworkMDNS lässt sich mit einem veröffentlichten Netzwerk-Hostnamen injizieren. TCP-Daten sind unverschlüsselt; Vertrauensaufbau, Authentifizierung und Replay-Schutz benötigen ein vollständiges Protokoll.
08 · Weiterdenken
Quellcode und verwandte Ideen
yarn tsx packages/p2p/examples/basic.ts