Beitragsprotokoll
Ein Beitrag in Hashiverse ist HTML — im Browser mit einem Rich-Text-Editor verfasst, mit DOMPurify vor der Anzeige bereinigt. Zwischen Verfassen und Anzeige liegt eine Pipeline aus Kompression, Verschlüsselung, Signatur, Zwei-Phasen-Einreichung, DHT-Routing und Bundle-Aggregation. Diese Seite verfolgt einen Beitrag durch diese Pipeline.
Der kodierte Beitrag
EncodedPostV1 ist das Drahtformat für einen einzelnen Beitrag. Er enthält:
- header: Bytes des Verifikationsschlüssels, Bytes des Post-Quanten-Commitments, Timestamp, Beitragslänge und
linked_base_ids— Verweise auf vorherige Beiträge, auf die dieser Beitrag antwortet oder aufbaut. - signature: Ed25519- (oder PQ-)Signatur über Header und Beitragshashes.
- post: der HTML-Inhalt, Brotli-komprimiert und verschlüsselt.
- post_id: Blake3-Hash der Signatur — eine stabile, verifizierbare Inhaltsadresse.
Die Verschlüsselung verwendet mehrere Passphrasen — eine pro Kontext, in dem der
Beitrag erscheint — über ein eigenes Mehrschlüssel-Schema, inspiriert vom
age-Verschlüsselungsformat. Ein Beitrag, der sowohl in der Timeline eines
Nutzers als auch unter einem Hashtag erscheint, wird mit der öffentlichen ID des
Nutzers und dem Hashtag-String als Passphrasen verschlüsselt. Jede der Schlüssel
entschlüsselt ihn. Server, die die verschlüsselten Bytes halten, können keine davon
lesen.
Einreichung: Claim, dann Commit
Die Beitragseinreichung ist ein Zwei-Phasen-Protokoll:
- SubmitClaim: Der Client sendet eine Claim-Anfrage mit PoW. Der Server validiert die PoW und gibt, wenn die Beitrags-ID nicht bereits existiert, ein Token aus, das die Erlaubnis zum Commit gewährt.
- SubmitCommit: Der Client sendet die eigentlichen Beitrags-Bytes mit dem Token. Der Server speichert den Beitrag und gibt eine Bestätigungssignatur zurück.
Die Trennung von Claim und Commit verhindert Nutzlast-Flutangriffe: Ein Server kann böswillige Claims günstig ablehnen (nur die PoW prüfen), bevor er eine große Nutzlast annimmt. Die Bestätigungssignatur des Servers ist der Beweis des Clients, dass der Beitrag akzeptiert wurde.
Quelle: hashiverse-server/src/server/handlers/
Bundles und Buckets
Beiträge werden weder einzeln gespeichert noch einzeln abgerufen — sie werden in
EncodedPostBundleV1-Objekte gruppiert, ein Bundle pro Bucket pro Server.
Ein Bundle enthält etwa 20 Beiträge aus einer bestimmten Standort-ID (ein konkretes
Zeitfenster für einen konkreten Nutzer, Hashtag oder Antwortkontext), aber diese Zahl
kann mit Heilung und eventuell konsistenten Verzögerungen größer werden. Bundles
werden vom liefernden Peer signiert.
Die Timeline eines Nutzers abzurufen bedeutet, die Bucket-Hierarchie rekursiv zu
durchlaufen: mit dem Monats-Bucket beginnen, in das wöchentliche, tägliche, stündliche
und feinere Buckets absteigen, wo Beiträge existieren. Der
RecursiveBucketVisitor handhabt das mit einem Callback, der auf jeder
Ebene entscheidet, ob in feinere Granularität abzusteigen ist oder ob übersprungen
wird, was effiziente Paginierung selbst über spärliche Timelines hinweg erlaubt.
Quelle: encoded_post_bundle.rs,
hashiverse-lib/src/client/timeline/
Bucket-Typen
Vier (zum Zeitpunkt des Schreibens) Bucket-Typen bestimmen, wie Beiträge indiziert und entdeckt werden:
- User (0): die persönliche Timeline des Autors
- Hashtag (1): Beiträge, die einen bestimmten Hashtag enthalten
- Mention (2): Beiträge, die einen bestimmten Nutzer erwähnen
- ReplyToPost (3): Beiträge, die auf einen bestimmten Beitrag antworten
Ein einzelner Beitrag kann gleichzeitig in mehreren Buckets erscheinen, jeweils mit der für diesen Bucket passenden Passphrase verschlüsselt.
Quelle: buckets.rs
Speicherung
Auf dem Server werden Beitrags-Bundles auf die Festplatte persistiert, während ihre Metadaten in fjall persistiert werden — einem in reinem Rust geschriebenen, eingebetteten Schlüssel-Wert-Speicher mit automatischer Verdichtung und gutem Schreibdurchsatz. fjall wurde gegenüber redb und sled wegen seiner Wartungsbilanz und betrieblichen Einfachheit gewählt. Beiträge werden als Brotli-komprimierte, verschlüsselte Bytes gespeichert.
Im Browser-Client werden Beiträge in IndexedDB über
indexed_db_futures zwischengespeichert, mit einem In-Memory-Stub für
Tests. Jede Kategorie clientseitiger Daten wird im Ruhezustand mit einem anderen
Schlüssel verschlüsselt, gewählt passend zu ihrem Zugriffsmodell.
Quelle: hashiverse-server/src/environment/,
wasm_client_storage.rs