Protocolo de Publicações

Uma publicação no Hashiverse é HTML — escrita no browser com um editor de rich-text, sanitizada com DOMPurify antes de ser apresentada. Entre a autoria e a apresentação está um pipeline de compressão, cifragem, assinatura, submissão em duas fases, encaminhamento DHT e agregação em bundles. Esta página segue uma publicação por esse pipeline.

A publicação codificada

EncodedPostV1 é o formato de fio para uma única publicação. Contém:

A cifragem usa várias passphrases — uma por contexto em que a publicação aparece — através de um esquema personalizado de várias chaves inspirado no formato de cifragem age. Uma publicação que esteja simultaneamente na cronologia de um utilizador e sob um hashtag é cifrada tendo como passphrases o ID público do utilizador e a string do hashtag. Qualquer uma das chaves a decifra. Os servidores que detêm os bytes cifrados não conseguem ler nem uma nem outra.

Submissão: Claim seguido de Commit

A submissão de uma publicação é um protocolo em duas fases:

  1. SubmitClaim: O cliente envia um pedido de claim com PoW. O servidor valida a PoW e, se o ID da publicação ainda não estiver presente, emite um token que concede permissão para o commit.
  2. SubmitCommit: O cliente envia os bytes reais da publicação com o token. O servidor guarda a publicação e devolve uma assinatura de confirmação.

Separar o claim do commit evita ataques de inundação por payload: um servidor pode rejeitar de forma barata claims de má-fé (basta verificar a PoW) antes de aceitar qualquer payload grande. A assinatura de confirmação do servidor é a prova do cliente de que a publicação foi aceite.

Origem: hashiverse-server/src/server/handlers/

Bundles e baldes

As publicações não são guardadas nem obtidas individualmente — são agrupadas em objetos EncodedPostBundleV1, um bundle por balde por servidor. Um bundle contém cerca de 20 publicações de um determinado location ID (uma janela temporal específica para um utilizador, hashtag ou contexto de resposta específico), mas este número pode ser maior com auto-recuperação e atrasos de consistência eventual. Os bundles são assinados pelo par que serve.

Obter a cronologia de um utilizador significa atravessar a hierarquia de baldes recursivamente: começar pelo balde mensal, descer ao semanal, diário, horário e baldes mais finos onde existam publicações. O RecursiveBucketVisitor trata disto com um callback que decide em cada nível se recurse para granularidade mais fina ou se salta, permitindo paginação eficiente mesmo em cronologias esparsas.

Origem: encoded_post_bundle.rs, hashiverse-lib/src/client/timeline/

Tipos de balde

Quatro (à data de escrita) tipos de balde determinam como as publicações são indexadas e descobertas:

Uma única publicação pode aparecer em vários baldes em simultâneo, cifrada em cada um com a passphrase apropriada a esse balde.

Origem: buckets.rs

Armazenamento

No servidor, os bundles de publicações são persistidos em disco, enquanto os seus metadados são persistidos em fjall — um key-value store embutido em puro Rust com compaction automática e bom débito de escrita. O fjall foi escolhido em detrimento do redb e do sled pelo seu histórico de manutenção e simplicidade operacional. As publicações são guardadas como bytes cifrados e comprimidos com Brotli.

No cliente do browser, as publicações são colocadas em cache em IndexedDB via indexed_db_futures, com um stub em memória para testes. Cada categoria de dados do lado do cliente é cifrada em repouso com uma chave diferente, escolhida para corresponder ao seu modelo de acesso.

Origem: hashiverse-server/src/environment/, wasm_client_storage.rs