Protocolo de publicaciones
Una publicación en Hashiverse es HTML — escrita en el navegador con un editor de texto enriquecido, saneada con DOMPurify antes de mostrarse. Entre la autoría y la visualización hay un pipeline de compresión, cifrado, firma, envío en dos fases, enrutamiento DHT y agregación en bundles. Esta página sigue una publicación a lo largo de ese pipeline.
La publicación codificada
EncodedPostV1 es el formato de cable de una publicación individual.
Contiene:
- header: bytes de la clave de verificación, bytes del compromiso post-cuántico, timestamp, longitud de la publicación y
linked_base_ids— referencias a publicaciones anteriores a las que esta responde o sobre las que se apoya. - signature: firma Ed25519 (o PQ) sobre los hashes del header y de la publicación.
- post: el contenido HTML, comprimido con Brotli y cifrado.
- post_id: hash Blake3 de la firma — una dirección de contenido estable y verificable.
El cifrado usa varias passphrases — una por cada contexto en el que aparece la
publicación — mediante un esquema personalizado de varias claves inspirado en el
formato de cifrado age. Una publicación que está a la vez en la
cronología de un usuario y bajo un hashtag se cifra con el identificador público del
usuario y la cadena del hashtag como passphrases. Cualquiera de las claves la
descifra. Los servidores que guardan los bytes cifrados no pueden leer ninguna de
ellas.
Envío: Claim y luego Commit
El envío de publicaciones es un protocolo en dos fases:
- SubmitClaim: el cliente envía una solicitud de claim con PoW. El servidor valida la PoW y, si el ID de publicación no está ya presente, emite un token que concede permiso para hacer commit.
- SubmitCommit: el cliente envía los bytes reales de la publicación con el token. El servidor almacena la publicación y devuelve una firma de confirmación.
Separar claim de commit evita ataques de inundación de payload: un servidor puede rechazar claims maliciosos a bajo coste (basta verificar la PoW) antes de aceptar cualquier payload grande. La firma de confirmación del servidor es la prueba para el cliente de que la publicación fue aceptada.
Fuente: hashiverse-server/src/server/handlers/
Bundles y buckets
Las publicaciones no se almacenan ni recuperan individualmente — se agrupan en
objetos EncodedPostBundleV1, un bundle por bucket por servidor. Un
bundle contiene unas 20 publicaciones de un ID de ubicación concreto (una ventana
temporal específica para un usuario, hashtag o contexto de respuesta determinado),
pero ese número puede ser mayor por la reparación y los retrasos de consistencia
eventual. Los bundles los firma el par que sirve.
Recuperar la cronología de un usuario significa recorrer la jerarquía de buckets de
forma recursiva: empezar por el bucket mensual, profundizar en el semanal, diario,
horario y buckets más finos donde existen publicaciones. El
RecursiveBucketVisitor gestiona esto con un callback que decide en cada
nivel si descender a granularidad más fina o saltar, permitiendo paginación
eficiente incluso en cronologías dispersas.
Fuente: encoded_post_bundle.rs,
hashiverse-lib/src/client/timeline/
Tipos de bucket
Cuatro (al momento de escribir) tipos de bucket determinan cómo se indexan y descubren las publicaciones:
- User (0): la cronología personal del autor
- Hashtag (1): publicaciones que contienen un hashtag dado
- Mention (2): publicaciones que mencionan a un usuario concreto
- ReplyToPost (3): publicaciones que responden a una publicación concreta
Una misma publicación puede aparecer en varios buckets simultáneamente, cada uno cifrado con la passphrase apropiada para ese bucket.
Fuente: buckets.rs
Almacenamiento
En el servidor, los bundles de publicaciones se persisten en disco, mientras que sus metadatos se persisten en fjall — un almacén clave-valor embebido, en Rust puro, con compactación automática y buen rendimiento de escritura. Se eligió fjall frente a redb y sled por su trayectoria de mantenimiento y simplicidad operativa. Las publicaciones se almacenan como bytes comprimidos con Brotli y cifrados.
En el cliente del navegador, las publicaciones se cachean en IndexedDB
a través de indexed_db_futures, con un stub en memoria para pruebas.
Cada categoría de datos del lado del cliente se cifra en reposo con una clave
distinta, elegida para encajar con su modelo de acceso.
Fuente: hashiverse-server/src/environment/,
wasm_client_storage.rs