Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introdução

Tesseras é uma rede peer-to-peer para preservar memórias humanas através dos milênios. Cada pessoa cria uma tessera — uma cápsula do tempo autocontida de memórias (fotos, áudio, vídeo, texto) que sobrevive independentemente de qualquer software, empresa ou infraestrutura.

O que é uma tessera?

A palavra tessera vem das pequenas peças usadas para fazer mosaicos no mundo antigo. No Tesseras, cada tessera é uma coleção de memórias empacotada em um formato projetado para ser compreendido mesmo daqui a milhares de anos, sem nenhum software especial.

Uma tessera contém:

  • Memórias — fotos (JPEG), gravações de áudio (WAV), vídeo (WebM) e texto (UTF-8 puro)
  • Metadados — quando e onde cada memória foi criada, quem está envolvido e o que significa
  • Identidade — assinaturas criptográficas provando quem criou
  • Instruções de decodificação — explicações em texto puro de cada formato utilizado, para que humanos do futuro possam ler o conteúdo

Filosofia central

  • Sem dependência de empresas — suas memórias são suas, armazenadas localmente e replicadas em uma rede peer-to-peer
  • Sem aprisionamento de formato — cada tessera inclui instruções para decodificar seu conteúdo
  • Disponibilidade acima de sigilo — memórias públicas não são criptografadas, porque acessibilidade a longo prazo importa mais do que esconder coisas
  • Criptografia mínima — apenas conteúdo privado e selado é criptografado; todo o resto é aberto
  • Resistente a computadores quânticos — assinaturas duplas (Ed25519 + ML-DSA) protegem a integridade mesmo contra futuros computadores quânticos

Status atual: Fase 4

Tesseras completou até a Fase 4 — criptografia e tesseras seladas. O projeto agora cobre gerenciamento local de tesseras, rede, replicação, app mobile e privacidade criptográfica.

O que está disponível hoje:

  • Geração de identidade (par de chaves Ed25519 com prova de trabalho)
  • Criação de tesseras a partir de arquivos locais
  • Armazenamento endereçado por conteúdo (hashing BLAKE3)
  • Verificação de integridade e exportação autocontida
  • Daemon de nó completo com transporte QUIC
  • Descoberta de pares via DHT Kademlia
  • Publicação e busca de ponteiros de tesseras pela rede
  • Codificação de apagamento Reed-Solomon com reparo automático de fragmentos
  • App mobile Flutter com nó Rust P2P embarcado
  • Tesseras privadas — conteúdo criptografado que apenas o dono pode acessar
  • Tesseras seladas — conteúdo com bloqueio temporal que abre após uma data específica
  • Criptografia híbrida pós-quântica — encapsulamento de chaves X25519 + ML-KEM-768
  • AES-256-GCM para criptografia de conteúdo com vinculação AAD

Conceitos-chave

ConceitoDescrição
TesseraUma cápsula do tempo autocontida de memórias
MemóriaUm item individual (foto, gravação, vídeo ou texto) dentro de uma tessera
Hash de conteúdoUm hash BLAKE3 que identifica unicamente uma tessera pelo seu conteúdo
VisibilidadeControla quem pode acessar uma tessera: pública, privada, selada ou círculo
Tessera seladaUma cápsula do tempo que só pode ser aberta após uma data específica
MANIFESTUm índice em texto puro listando cada arquivo na tessera com seu checksum
Tipo de memóriaCategoriza uma memória: momento, reflexão, cotidiano, relação ou objeto
Um dispositivo executando o daemon Tesseras, participando da rede P2P
DHTTabela hash distribuída — como os nós encontram ponteiros de tesseras sem um servidor central
BootstrapO processo de entrar na rede contactando nós semente conhecidos

Instalação

Tesseras está disponível atualmente através de compilação a partir do código-fonte.

Pré-requisitos

Rust

Tesseras requer Rust 1.85 ou superior. A maneira recomendada de instalar o Rust é via rustup:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Após a instalação, certifique-se de que ~/.cargo/bin está no seu PATH. O instalador normalmente adiciona isso automaticamente. Verifique com:

rustc --version
cargo --version

Se você já tem o Rust instalado, atualize para a versão mais recente:

rustup update stable

SQLite

Tesseras usa SQLite para armazenamento local. Você tem duas opções:

Opção 1: SQLite do sistema (recomendada)

Instale as bibliotecas de desenvolvimento do SQLite pelo gerenciador de pacotes do seu sistema:

DistribuiçãoComando
Arch Linuxsudo pacman -S sqlite
Debian / Ubuntusudo apt install libsqlite3-dev
Fedorasudo dnf install sqlite-devel
Alpineapk add sqlite-dev
macOS (Homebrew)brew install sqlite
FreeBSDpkg install sqlite3
OpenBSDJá incluído no sistema base

Opção 2: SQLite embutido

Se preferir não instalar o SQLite no sistema, use a feature flag bundled-sqlite durante a compilação. Isso compila o SQLite junto com o Tesseras:

cargo install --path crates/tesseras-cli --features bundled-sqlite
cargo install --path crates/tesseras-daemon --features bundled-sqlite

Ferramentas opcionais

FerramentaPara quêInstalação
justExecutar comandos de build do projetocargo install just
mdBookCompilar a documentaçãocargo install mdbook
DockerExecutar nós em contêineresVeja Docker
FlutterCompilar o app mobile/desktopVeja App Flutter

Compilar a partir do código-fonte

Clone o repositório e instale os binários:

git clone https://git.sr.ht/~ijanc/tesseras
cd tesseras
cargo install --path crates/tesseras-cli
cargo install --path crates/tesseras-daemon

Ou, se você tiver o just instalado:

just install

Isso instala dois binários em ~/.cargo/bin/ e configura auto-completions para o seu shell:

  • tes — ferramenta CLI para criar, verificar e exportar tesseras
  • tesseras-daemon — daemon de nó completo que participa da rede P2P

Verificar a instalação

tes --help

Você deverá ver:

Create and preserve human memories

Usage: tes [OPTIONS] <COMMAND>

Commands:
  init    Initialize identity and local database
  create  Create a tessera from a directory of files
  verify  Verify integrity of a stored tessera
  export  Export tessera to a self-contained directory
  list    List local tesseras
  help    Print this message or the help of the given subcommand(s)

Options:
      --data-dir <DATA_DIR>  Base directory for data storage [default: ~/.tesseras]
  -h, --help                 Print help

Auto-completions do shell

O comando just install configura completions automaticamente. Se você instalou manualmente, gere as completions para o seu shell:

# Fish
tes completions fish > ~/.config/fish/completions/tes.fish

# Zsh
tes completions zsh > "${XDG_DATA_HOME:-$HOME/.local/share}/zsh/site-functions/_tes"

# Bash
tes completions bash > "${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion/completions/tes"

App Flutter

Para compilar o app mobile ou desktop, você precisa de dependências adicionais:

Pré-requisitos do Flutter

  1. Flutter SDK — instale seguindo o guia oficial
  2. Rust — já instalado conforme acima
  3. Dependências de plataforma:
PlataformaDependências
AndroidAndroid SDK, Android NDK, Java 17+
iOSXcode, CocoaPods
Linux desktopGTK 3.0+, pkg-config (sudo apt install libgtk-3-dev pkg-config no Debian/Ubuntu)
macOS desktopXcode Command Line Tools

Compilar o app

cd apps/flutter
flutter pub get

# Linux desktop
flutter build linux --debug

# Android
flutter build apk --debug

# iOS
flutter build ios --debug

# Testes
flutter test

Ou usando just a partir da raiz do repositório:

just build-linux    # Linux desktop
just build-android  # Android APK
just test-flutter   # Testes

Portas de rede

O daemon Tesseras usa QUIC (protocolo sobre UDP). Se você estiver atrás de um firewall, permita tráfego na porta:

ProtocoloPortaDireção
UDP4433Entrada e saída

Próximos passos

Início Rápido

Este tutorial guia você por um fluxo completo: criar uma identidade, construir uma tessera a partir de arquivos, verificá-la e exportá-la.

1. Inicializar sua identidade

Primeiro, configure sua identidade local e banco de dados:

tes init
Generated Ed25519 identity
Database initialized
Config written to /home/user/.tesseras/config.toml
Tesseras initialized at /home/user/.tesseras

Isso cria:

  • ~/.tesseras/identity/ — seu par de chaves Ed25519
  • ~/.tesseras/db/ — banco de dados SQLite para indexação
  • ~/.tesseras/blobs/ — armazenamento para arquivos de memória
  • ~/.tesseras/config.toml — arquivo de configuração

2. Preparar seus arquivos

Crie um diretório com as memórias que deseja preservar:

mkdir minhas-memorias
cp ~/fotos/jantar-familia.jpg minhas-memorias/
cp ~/fotos/jardim.jpg minhas-memorias/
echo "Uma tarde quente de domingo com a família." > minhas-memorias/reflexao.txt

Formatos suportados: .jpg, .jpeg, .png (imagens), .wav (áudio), .webm (vídeo), .txt (texto).

3. Pré-visualizar com dry run

Veja o que seria incluído sem criar nada:

tes create minhas-memorias --dry-run

4. Criar uma tessera

tes create minhas-memorias --tags "familia,domingo" --location "Casa"

A saída inclui o hash de conteúdo — uma string hexadecimal de 64 caracteres que identifica unicamente sua tessera. Copie-o para os próximos passos.

5. Listar suas tesseras

tes list
Hash             Created     Memories  Size    Visibility
9f2c4a1b3e7d8f0c 2026-02-14         3  284 KB  public

6. Verificar integridade

Use o hash de conteúdo para verificar que todos os arquivos estão intactos e a assinatura é válida:

tes verify 9f2c4a1b3e7d8f0c...
Tessera: 9f2c4a1b3e7d8f0c...
Signature: VALID
  [OK] memories/a1b2c3/media.jpg
  [OK] memories/d4e5f6/media.jpg
  [OK] memories/g7h8i9/media.txt
Verification: PASSED

7. Exportar uma cópia autocontida

Exporte a tessera para um diretório que pode ser lido sem o Tesseras:

tes export 9f2c4a1b3e7d8f0c... ./backup
Exported to ./backup/tessera-9f2c4a1b3e7d8f0c...

8. Inspecionar a exportação

O diretório exportado é totalmente autocontido:

tessera-9f2c4a1b3e7d8f0c.../
├── MANIFEST                    # Índice em texto puro com checksums
├── README.decode               # Como ler esta tessera sem software
├── identity/
│   ├── creator.pub.ed25519     # Sua chave pública
│   └── signature.ed25519.sig   # Assinatura do MANIFEST
├── memories/
│   ├── <hash>/
│   │   ├── media.jpg           # A foto
│   │   ├── context.txt         # Descrição em texto puro
│   │   └── meta.json           # Metadados estruturados
│   └── .../
└── decode/
    ├── formats.txt             # Explicação de todos os formatos usados
    ├── jpeg.txt                # Como decodificar JPEG
    └── json.txt                # Como decodificar JSON

Tudo que um leitor futuro precisa para entender o conteúdo está incluído no próprio diretório — nenhum software Tesseras é necessário.

tes init

Inicializar identidade e banco de dados local.

Uso

tes init

Descrição

Configura seu ambiente Tesseras local. Este é o primeiro comando que você deve executar após instalar o Tesseras.

O comando cria:

CaminhoConteúdo
~/.tesseras/identity/Par de chaves Ed25519 para assinar tesseras
~/.tesseras/db/Banco de dados SQLite para indexação
~/.tesseras/blobs/Armazenamento de blobs para arquivos de memória
~/.tesseras/config.tomlArquivo de configuração

Opções

OpçãoDescrição
--data-dir <CAMINHO>Diretório base para armazenamento de dados (padrão: ~/.tesseras)

Idempotente

Executar init novamente é seguro. Se uma identidade já existe, ela é preservada:

tes init
Ed25519 identity already exists
Database initialized
Tesseras initialized at /home/user/.tesseras

Diretório de dados personalizado

tes --data-dir /mnt/usb/tesseras init

Isso cria toda a estrutura de diretórios em /mnt/usb/tesseras/ ao invés do local padrão.

O que acontece internamente

  1. Cria a estrutura de diretórios (identity/, db/, blobs/)
  2. Gera um par de chaves Ed25519 (a chave privada permanece local, a chave pública identifica você)
  3. Executa as migrações SQLite para configurar o esquema do banco de dados
  4. Escreve um config.toml padrão

tes create

Criar uma tessera a partir de um diretório de arquivos.

Uso

tes create <CAMINHO> [OPÇÕES]

Argumentos

ArgumentoDescrição
<CAMINHO>Diretório contendo os arquivos a incluir

Opções

OpçãoDescriçãoPadrão
-n, --non-interactivePular promptsdesativado
--dry-runPré-visualizar o que seria incluídodesativado
--visibility <VALOR>Nível de visibilidade: public, private, circlepublic
--language <CÓDIGO>Código de idioma (ex.: en, pt-BR)en
--tags <LISTA>Tags separadas por vírgulanenhuma
--location <DESC>Descrição do localnenhuma
--data-dir <CAMINHO>Diretório base para armazenamento de dados~/.tesseras

Formatos de arquivo suportados

ExtensãoTipoTipo de memória
.jpg, .jpegImagem (JPEG)Momento
.pngImagem (PNG)Momento
.wavÁudio (WAV PCM)Momento
.webmVídeo (WebM)Momento
.txtTexto puro (UTF-8)Reflexão

Arquivos com outras extensões são ignorados.

Inferência de tipo de memória

O comando atribui automaticamente um tipo de memória baseado no formato do arquivo:

  • Arquivos de texto (.txt) são classificados como Reflexão — pensamentos, crenças ou opiniões
  • Todos os outros formatos são classificados como Momento — uma foto, gravação ou vídeo de algo acontecendo

Exemplos

Pré-visualizar antes de criar

tes create ./minhas-fotos --dry-run

Criar com metadados

tes create ./ferias-2026 \
    --tags "ferias,verao,praia" \
    --location "Florianópolis, Brasil" \
    --language pt-BR \
    --visibility public

Modo não-interativo

tes create ./diario --non-interactive --tags "cotidiano"

Níveis de visibilidade

NívelQuem pode acessar
publicQualquer pessoa (padrão)
privateApenas você (e herdeiros designados)
circlePessoas explicitamente escolhidas

O que acontece internamente

  1. Varre o diretório em busca de arquivos suportados
  2. Calcula um hash BLAKE3 para cada arquivo
  3. Atribui um tipo de memória baseado na extensão do arquivo
  4. Gera um MANIFEST listando todos os arquivos com seus checksums
  5. Assina o MANIFEST com sua chave privada Ed25519
  6. Armazena os arquivos e metadados no banco de dados local
  7. Exibe o hash de conteúdo que identifica unicamente esta tessera

tes verify

Verificar integridade de uma tessera armazenada.

Uso

tes verify <HASH>

Argumentos

ArgumentoDescrição
<HASH>Hash de conteúdo da tessera (64 caracteres hexadecimais)

Opções

OpçãoDescrição
--data-dir <CAMINHO>Diretório base para armazenamento de dados (padrão: ~/.tesseras)

O que é verificado

  1. Validade da assinatura — verifica a assinatura Ed25519 sobre o MANIFEST
  2. Integridade dos arquivos — recalcula o hash BLAKE3 de cada arquivo e compara com o MANIFEST

Códigos de saída

CódigoSignificado
0Verificação passou — todos os arquivos intactos, assinatura válida
1Verificação falhou — arquivos corrompidos ou assinatura inválida

Exemplos

Verificação bem-sucedida

tes verify 9f2c4a1b3e7d8f0cabc123def456789012345678abcdef0123456789abcdef01
Tessera: 9f2c4a1b3e7d8f0cabc123def456789012345678abcdef0123456789abcdef01
Signature: VALID
  [OK] memories/a1b2c3d4/media.jpg
  [OK] memories/e5f6a7b8/media.txt
  [OK] memories/c9d0e1f2/media.wav
Verification: PASSED

Verificação com falha

Se um arquivo foi modificado ou corrompido:

Tessera: 9f2c4a1b3e7d8f0cabc123def456789012345678abcdef0123456789abcdef01
Signature: VALID
  [OK] memories/a1b2c3d4/media.jpg
  [FAILED] memories/e5f6a7b8/media.txt
  [OK] memories/c9d0e1f2/media.wav
Verification: FAILED

Casos de uso

  • Verificações rotineiras de integridade — verifique periodicamente que suas tesseras armazenadas não foram corrompidas
  • Após transferência — verifique após copiar tesseras para um novo dispositivo ou meio de armazenamento
  • Verificação de confiança — confirme que uma tessera recebida de outra pessoa não foi adulterada

tes export

Exportar uma tessera como um diretório autocontido.

Uso

tes export <HASH> <DESTINO>

Argumentos

ArgumentoDescrição
<HASH>Hash de conteúdo da tessera (64 caracteres hexadecimais)
<DESTINO>Diretório de destino

Opções

OpçãoDescrição
--data-dir <CAMINHO>Diretório base para armazenamento de dados (padrão: ~/.tesseras)

Estrutura de saída

A exportação cria um diretório chamado tessera-<hash> dentro do destino:

tessera-9f2c4a1b.../
├── MANIFEST                    # Índice em texto puro com checksums
├── README.decode               # Instruções de decodificação legíveis por humanos
├── identity/
│   ├── creator.pub.ed25519     # Chave pública do criador
│   └── signature.ed25519.sig   # Assinatura do MANIFEST
├── memories/
│   ├── <hash-conteudo>/
│   │   ├── media.jpg           # Arquivo de mídia principal
│   │   ├── context.txt         # Contexto humano em UTF-8 puro
│   │   └── meta.json           # Metadados estruturados
│   └── .../
├── schema/
│   └── v1.json                 # Esquema JSON para validação de metadados
└── decode/
    ├── formats.txt             # Explicação de todos os formatos usados
    ├── jpeg.txt                # Como decodificar JPEG
    ├── wav.txt                 # Como decodificar WAV
    └── json.txt                # Como decodificar JSON

Exemplo

tes export 9f2c4a1b3e7d8f0cabc123def4567890... ./backup
Exported to ./backup/tessera-9f2c4a1b3e7d8f0cabc123def4567890...

Característica principal: autocontido

O diretório exportado é projetado para ser legível sem o software Tesseras. Ele inclui:

  • MANIFEST — um arquivo em texto puro listando cada arquivo com seu checksum BLAKE3, legível por qualquer editor de texto
  • README.decode — instruções legíveis por humanos para compreender o conteúdo
  • decode/ — explicações detalhadas de cada formato de arquivo usado (JPEG, WAV, JSON, UTF-8)

Isso significa que alguém daqui a milhares de anos, sem conhecimento algum sobre o Tesseras, ainda pode entender e acessar as memórias.

Casos de uso

  • Backup — exporte para um disco externo, pendrive ou armazenamento em nuvem
  • Compartilhamento — entregue a alguém uma cópia completa de uma tessera
  • Arquivamento — armazene em mídia de escrita única (DVD, Blu-ray, fita)
  • Migração — mova tesseras entre máquinas sem precisar do banco de dados

tes list

Listar todas as tesseras locais.

Uso

tes list

Opções

OpçãoDescrição
--data-dir <CAMINHO>Diretório base para armazenamento de dados (padrão: ~/.tesseras)

Saída

Exibe uma tabela com as seguintes colunas:

ColunaDescrição
HashPrimeiros 16 caracteres do hash de conteúdo
CreatedData de criação (AAAA-MM-DD)
MemoriesNúmero de memórias na tessera
SizeTamanho total (B, KB, MB ou GB)
VisibilityNível de visibilidade (public, private ou circle)

Exemplo

tes list
Hash             Created     Memories  Size    Visibility
9f2c4a1b3e7d8f0c 2026-02-14         3  284 KB  public
a3b7c2d9e4f01823 2026-02-10         1   12 KB  private
f8e7d6c5b4a39201 2026-01-28        12    4 MB  public

Banco de dados vazio

Se nenhuma tessera foi criada ainda:

tes list
No tesseras found.

Executando um No

O binario tesseras-daemon executa um no completo do Tesseras que participa da rede peer-to-peer. Ele escuta conexoes sobre QUIC, entra na tabela hash distribuida (DHT) e permite que outros nos descubram e encontrem ponteiros de tesseras.

Iniciando o daemon

tesseras-daemon

Na primeira execucao, o daemon:

  1. Cria o diretorio de dados (~/.local/share/tesseras no Linux, ~/Library/Application Support/tesseras no macOS)
  2. Gera uma identidade de no com prova de trabalho (leva cerca de 1 segundo)
  3. Abre um listener QUIC em 0.0.0.0:4433
  4. Faz bootstrap na rede contactando nos semente
  5. Imprime daemon ready quando totalmente operacional

Opcoes de linha de comando

tesseras-daemon [OPTIONS]
OpcaoDescricaoPadrao
-c, --config <PATH>Caminho para um arquivo de configuracao TOMLNenhum (usa padroes internos)
-l, --listen <ADDR>Endereco e porta para escutar0.0.0.0:4433
-b, --bootstrap <ADDRS>Enderecos de bootstrap separados por virgulaboot1.tesseras.net:4433,boot2.tesseras.net:4433
-d, --data-dir <PATH>Diretorio de dadosEspecifico da plataforma (veja acima)

Opcoes CLI sobrescrevem valores do arquivo de configuracao.

Exemplos

Executar com padroes (entrar na rede publica):

tesseras-daemon

Executar como no semente (sem bootstrap, outros nos conectam a voce):

tesseras-daemon --bootstrap ""

Executar em uma porta personalizada com um diretorio de dados especifico:

tesseras-daemon --listen 0.0.0.0:5000 --data-dir /var/lib/tesseras

Fazer bootstrap a partir de um no especifico:

tesseras-daemon --bootstrap "192.168.1.50:4433"

Entrar em uma rede local com multiplos nos:

tesseras-daemon --bootstrap "192.168.1.10:4433,192.168.1.11:4433"

Identidade do no

Cada no tem uma identidade unica armazenada em <data-dir>/identity.key. Este arquivo contem uma chave publica de 32 bytes e um nonce de prova de trabalho de 8 bytes.

O ID do no e derivado da chave publica: BLAKE3(pubkey || nonce) truncado para 20 bytes. O nonce deve produzir um hash com 8 bits zero iniciais, o que leva cerca de 256 tentativas de hash. Esta prova de trabalho leve torna caro criar milhares de identidades falsas enquanto custa menos de um segundo para usuarios legitimos.

A identidade e gerada automaticamente na primeira execucao e reutilizada nas execucoes seguintes. Se voce apagar identity.key, uma nova identidade sera gerada.

Logging

O daemon usa logging estruturado via tracing. Controle o nivel de log com a variavel de ambiente RUST_LOG:

# Padrao (nivel info)
tesseras-daemon

# Logging de debug
RUST_LOG=debug tesseras-daemon

# Mostrar apenas avisos e erros
RUST_LOG=warn tesseras-daemon

# Debug para DHT, info para o resto
RUST_LOG=info,tesseras_dht=debug tesseras-daemon

Desligamento

Pressione Ctrl+C para iniciar o desligamento gracioso. O daemon ira:

  1. Parar de aceitar novas conexoes
  2. Finalizar operacoes em andamento (ate 5 segundos)
  3. Fechar todas as conexoes QUIC
  4. Sair de forma limpa

Firewall

O daemon se comunica pela porta UDP 4433 (QUIC). Se voce esta atras de um firewall, certifique-se de que esta porta esta aberta para trafego UDP de entrada e saida.

# Exemplo: Linux com ufw
sudo ufw allow 4433/udp

Configuracao

O daemon pode ser configurado via um arquivo TOML. Passe o caminho com --config:

tesseras-daemon --config /etc/tesseras/config.toml

Se nenhum arquivo de configuracao for fornecido, o daemon usa padroes sensiveis. Opcoes CLI (--listen, --bootstrap, --data-dir) sobrescrevem os valores correspondentes da configuracao.

Exemplo completo

[node]
data_dir = "~/.local/share/tesseras"
listen_addr = "0.0.0.0:4433"

[dht]
k = 20
alpha = 3
bucket_refresh_interval_secs = 3600
republish_interval_secs = 3600
pointer_ttl_secs = 86400
max_stored_pointers = 100000
ping_failure_threshold = 3

[bootstrap]
dns_domain = "_tesseras._udp.tesseras.net"
hardcoded = [
    "boot1.tesseras.net:4433",
    "boot2.tesseras.net:4433",
]

[network]
enable_mdns = true

[observability]
metrics_addr = "127.0.0.1:9190"
log_format = "json"

Secoes

[node]

Configuracoes basicas do no.

ChaveTipoPadraoDescricao
data_dircaminhoEspecifico da plataformaOnde armazenar identidade, banco de dados e blobs
listen_addrendereco0.0.0.0:4433Endereco do listener QUIC

O data_dir padrao e ~/.local/share/tesseras no Linux e ~/Library/Application Support/tesseras no macOS.

[dht]

Parametros de ajuste da DHT Kademlia. Os padroes funcionam bem para a maioria das implantacoes.

ChaveTipoPadraoDescricao
kinteiro20Maximo de entradas por bucket da tabela de roteamento
alphainteiro3Paralelismo para buscas iterativas
bucket_refresh_interval_secsinteiro3600Com que frequencia atualizar buckets da tabela de roteamento (segundos)
republish_interval_secsinteiro3600Com que frequencia republicar ponteiros armazenados (segundos)
pointer_ttl_secsinteiro86400Quanto tempo manter um ponteiro antes de expirar (segundos)
max_stored_pointersinteiro100000Numero maximo de ponteiros armazenados localmente
ping_failure_thresholdinteiro3Quantas falhas consecutivas de ping antes de remover um par

[bootstrap]

Como o no descobre seus primeiros pares ao entrar na rede.

ChaveTipoPadraoDescricao
dns_domainstring_tesseras._udp.tesseras.netDominio DNS para descoberta de pares via registros TXT
hardcodedlista de strings["boot1.tesseras.net:4433", "boot2.tesseras.net:4433"]Enderecos de bootstrap de fallback

[network]

Funcionalidades de nivel de rede.

ChaveTipoPadraoDescricao
enable_mdnsbooleanotrueHabilitar descoberta na rede local via mDNS

[observability]

Monitoramento e logging.

ChaveTipoPadraoDescricao
metrics_addrendereco127.0.0.1:9190Endereco para o endpoint de metricas Prometheus
log_formatstringjsonFormato de saida de log (json ou text)

Suporte a IPv6

Tesseras suporta IPv6 nativamente. Os campos listen_addr e listen_addrs aceitam tanto enderecos IPv4 quanto IPv6.

Escutando em IPv6

Para escutar em todas as interfaces IPv6:

[node]
listen_addr = "[::]:4433"

No Linux e na maioria dos BSDs, vincular a [::] tambem aceita conexoes IPv4 (dual-stack) por padrao. Em alguns sistemas (notavelmente OpenBSD), [::] e somente IPv6 porque IPV6_V6ONLY e habilitado por padrao. Para garantir tanto IPv4 quanto IPv6 em todas as plataformas, use listen_addrs com enderecos explicitos:

[node]
listen_addrs = ["0.0.0.0:4433", "[::]:4433"]

Para loopback IPv6 apenas (testes):

[node]
listen_addr = "[::1]:4433"

Bootstrap com IPv6

Enderecos de bootstrap podem ser IPv6:

[bootstrap]
hardcoded = [
    "boot1.tesseras.net:4433",
    "[2001:db8::1]:4433",
]

Hostnames DNS com registros A e AAAA sao resolvidos para todos os enderecos, entao o daemon se conectara pelo protocolo que estiver acessivel.

Comportamento de IPV6_V6ONLY por SO

SO[::] aceita IPv4?Notas
LinuxSim (dual-stack)IPV6_V6ONLY padrao 0
macOSSim (dual-stack)IPV6_V6ONLY padrao 0
FreeBSDSim (dual-stack)IPV6_V6ONLY padrao 0
OpenBSDNao (somente IPv6)IPV6_V6ONLY sempre 1
WindowsSim (dual-stack)IPV6_V6ONLY padrao 0

Se precisar de controle explicito, use listen_addrs com um endereco IPv4 e um IPv6.

Configuracao minima

A maioria dos usuarios nao precisa de um arquivo de configuracao. Se precisar, uma configuracao minima sobrescrevendo apenas o necessario e suficiente:

[node]
listen_addr = "0.0.0.0:5000"

[bootstrap]
hardcoded = ["192.168.1.10:4433"]

Todos os outros valores usam seus padroes.

Conceitos de Rede

Este capitulo explica como os nos do Tesseras se encontram e localizam ponteiros de tesseras na rede. Voce nao precisa entender esses detalhes para usar o Tesseras, mas eles ajudam a explicar o que o daemon esta fazendo em segundo plano.

Como os nos se encontram

Tesseras usa uma tabela hash distribuida (DHT) Kademlia — um algoritmo comprovado usado pelo BitTorrent e outros sistemas P2P por mais de 20 anos. Nao ha servidor central. Cada no mantem uma tabela de roteamento dos pares que conhece, e os nos cooperam para direcionar consultas ao lugar certo.

Quando seu no inicia, ele contacta um ou mais nos de bootstrap (nos semente com enderecos conhecidos). Atraves dessas conexoes iniciais, seu no descobre outros pares e constroi sua tabela de roteamento. Com o tempo, seu no naturalmente aprende sobre mais pares conforme participa da rede.

O que a DHT armazena

A DHT armazena ponteiros, nao dados. Um ponteiro e um registro leve que diz “a tessera X esta com os nos Y e Z.” Quando alguem quer recuperar uma tessera, primeiro busca seu ponteiro na DHT para descobrir quais nos a possuem, depois conecta diretamente a esses nos para baixar os dados reais.

Isso significa que a DHT permanece pequena e rapida — ela rastreia apenas quem tem o que, nao o conteudo em si.

Identidade do no e prova de trabalho

Cada no tem um ID de no de 160 bits derivado de sua chave publica. Para evitar que um atacante crie milhares de nos falsos de forma barata (um ataque Sybil), gerar um ID de no requer uma pequena prova de trabalho: o no deve encontrar um nonce tal que BLAKE3(chave_publica || nonce) comece com 8 bits zero.

Isso leva cerca de 256 tentativas de hash — menos de um segundo em qualquer dispositivo, incluindo um Raspberry Pi. Mas um atacante tentando criar 10.000 identidades falsas precisaria de milhoes de tentativas, tornando o ataque impraticavel.

Distancia XOR

Kademlia define “proximidade” entre nos usando a metrica XOR: a distancia entre dois IDs de no e seu XOR bit a bit. Os nos sao responsaveis por armazenar ponteiros cujas chaves estao proximas de seu proprio ID (em distancia XOR). Isso distribui dados uniformemente pela rede sem nenhuma coordenacao.

Ao buscar um ponteiro de tessera, seu no pergunta aos pares que conhece que estao mais proximos da chave alvo. Esses pares apontam para outros ainda mais proximos, e assim por diante, ate que o ponteiro seja encontrado. Essa busca iterativa tipicamente alcanca qualquer no na rede em poucos saltos.

Transporte: QUIC

Toda comunicacao entre nos usa QUIC, um protocolo de transporte moderno construido sobre UDP. O QUIC oferece:

  • Criptografia integrada — cada conexao usa TLS 1.3
  • Amigavel a NAT — funciona atraves da maioria dos tradutores de endereco de rede por ser baseado em UDP
  • Multiplexacao — multiplas operacoes independentes sobre uma conexao sem bloqueio head-of-line
  • Migracao de conexao — sobrevive a mudancas de rede (ex: trocar de Wi-Fi para dados moveis)

O daemon escuta na porta UDP 4433 por padrao.

Processo de bootstrap

Quando um no inicia, ele segue esta sequencia:

  1. Contactar nos semente — conectar a um ou mais enderecos de bootstrap conhecidos
  2. Trocar pings — verificar que o semente esta vivo e trocar identidades de no
  3. Auto-busca — perguntar ao semente por nos proximos ao seu proprio ID, para popular sua tabela de roteamento
  4. Descoberta iterativa — contactar os nos recem-descobertos, que apontam para ainda mais pares

Apos o bootstrap, o no mantem sua tabela de roteamento automaticamente: ele atualiza buckets periodicamente e substitui pares nao responsivos por novos.

Tipos de no

Nem todo dispositivo participa da rede da mesma forma:

TipoDescricaoSempre ligado?
No completoDesktop, servidor ou Raspberry Pi executando tesseras-daemon. Participa plenamente da DHT e armazena dados de outros nos.Sim
No movelCelular ou tablet executando o app Tesseras. Participa da DHT quando o app esta ativo.Nao
No navegadorNavegador web executando o cliente WASM. Conecta via um no relay. Somente leitura.Nao
No IoTESP32 ou dispositivo similar na rede local. Armazena fragmentos passivamente, nao participa da DHT.Sim

O daemon de no completo e a espinha dorsal da rede. Quanto mais nos completos em execucao, mais resiliente a rede se torna.

Replicação e Reparo

Este capítulo explica como o Tesseras mantém suas memórias seguras mesmo quando nós individuais ficam offline ou sofrem falhas de hardware. Você não precisa entender esses detalhes para usar o Tesseras — o daemon cuida de tudo automaticamente.

Por que a replicação importa

Uma tessera armazenada em uma única máquina morre quando essa máquina morre. O Tesseras resolve isso dividindo os dados em fragmentos, espalhando-os entre múltiplos pares e verificando continuamente que cópias suficientes existem. Se alguns fragmentos desaparecem, a rede se repara automaticamente.

Codificação de apagamento

O Tesseras usa codificação de apagamento Reed-Solomon para criar fragmentos redundantes. A ideia é simples: a partir de N fragmentos de dados, gerar M fragmentos extras de paridade. Quaisquer N dos N+M fragmentos totais podem reconstruir os dados originais.

Isso é muito mais eficiente em armazenamento do que replicação simples. Armazenar 3 cópias completas de um arquivo de 100 MB custa 300 MB. Com 16 dados + 8 fragmentos de paridade, você obtém proteção mais forte (pode perder até 8 de 24 fragmentos — 33%) por apenas 150 MB no total.

Camadas de fragmentação

Nem toda tessera é tratada da mesma forma. Arquivos pequenos não se beneficiam do overhead da codificação de apagamento, então o Tesseras usa três camadas:

CamadaTamanhoEstratégiaFragmentos
Small< 4 MBReplicação do arquivo inteiro7 cópias do arquivo completo
Medium4–256 MBReed-Solomon 16+816 dados + 8 paridade = 24 fragmentos
Large≥ 256 MBReed-Solomon 48+2448 dados + 24 paridade = 72 fragmentos

Todas as camadas visam um fator de replicação de 7 — significando que os fragmentos são distribuídos para 7 pares diferentes.

Como a distribuição funciona

Quando você cria uma tessera e o daemon a replica, isto é o que acontece:

  1. Codificar — os dados da tessera são divididos em fragmentos de acordo com sua camada de tamanho
  2. Encontrar pares — o daemon consulta a DHT pelos nós mais próximos ao hash da tessera
  3. Diversidade de sub-rede — os pares são filtrados para que poucos venham da mesma sub-rede (para evitar falhas correlacionadas se um datacenter cair)
  4. Distribuir — os fragmentos são enviados aos pares selecionados em ordem round-robin
  5. Confirmar — cada par valida o checksum do fragmento e confirma o recebimento

O dono da tessera envia os fragmentos aos pares. Os pares não puxam — isso mantém o protocolo simples e garante distribuição imediata.

Verificação de fragmentos

Cada fragmento carrega um checksum BLAKE3. Quando um nó recebe um fragmento, ele recalcula o hash e compara com o checksum esperado. Se não coincidem, o fragmento é rejeitado. Isso detecta tanto erros de transmissão quanto adulteração deliberada.

Os fragmentos sao armazenados em um armazenamento enderecavel por conteudo (CAS) onde cada dado unico existe exatamente uma vez em disco, indexado pelo seu hash BLAKE3. Uma tabela de referencias no SQLite mapeia identificadores logicos de fragmentos para hashes CAS, habilitando deduplicacao automatica — se duas tesseras compartilham dados de fragmento identicos, apenas uma copia e armazenada. Contagem de referencias garante que os dados sejam limpos apenas quando nenhuma tessera os referencia.

Loop de reparo

O daemon executa um loop de reparo em segundo plano a cada 24 horas (com jitter aleatório para evitar tempestades em toda a rede). Para cada tessera sob sua responsabilidade, o loop de reparo:

  1. Solicita atestações dos detentores conhecidos — cada detentor prova que ainda possui os fragmentos reportando seus checksums
  2. Recorre ao ping se a atestação falhar — para distinguir entre “nó está offline” e “nó perdeu os dados”
  3. Verifica fragmentos locais — verifica a integridade de quaisquer fragmentos armazenados localmente recalculando checksums BLAKE3
  4. Decide a ação:
    • Healthy — todos os detentores responderam, todos os checksums válidos, nada a fazer
    • Needs replication — alguns detentores sumiram, encontrar novos pares e redistribuir fragmentos ausentes
    • Corrupt local — um fragmento local tem dados corrompidos, buscar uma substituição na rede

Reciprocidade

O Tesseras usa um livro-razão de reciprocidade bilateral para garantir troca justa de armazenamento. Não há criptomoeda, não há blockchain, não há consenso global — cada nó simplesmente rastreia seu saldo com cada par localmente:

par_a: +500 MB  (eles armazenam 500 MB meus)
par_b: -200 MB  (eu armazeno 200 MB a mais deles do que eles armazenam meu)
par_c:    0 MB  (equilibrado)

As regras são simples:

  • Armazene 1 GB na rede → você deveria armazenar aproximadamente 1 GB para outros
  • Nós com saldo positivo (eles armazenam mais para você) recebem prioridade quando você precisa distribuir novos fragmentos
  • Free riders perdem redundância gradualmente — seus fragmentos são despriorizados para reparo, mas nunca deletados
  • Ao receber um fragmento, um nó verifica o déficit do remetente. Se o remetente deve muito armazenamento, o fragmento é rejeitado
  • Nós institucionais (universidades, arquivos) podem operar altruisticamente com proporções desequilibradas

Tamanho máximo de tessera

O tamanho máximo de uma tessera é 1 GB. Este é um limite prático que mantém os tamanhos de fragmentos gerenciáveis e a replicação rápida. Para coleções maiores de memórias, crie múltiplas tesseras.

Configuração

O comportamento de replicação do daemon pode ser ajustado através da configuração:

ParâmetroPadrãoDescrição
Intervalo de reparo24 horasCom que frequência o loop de reparo roda
Jitter de reparo2 horasAtraso aleatório adicionado para evitar tempestades na rede
Transferências simultâneas4Máximo de transferências paralelas de fragmentos
Espaço livre mínimo1 GBParar de aceitar fragmentos abaixo deste limite
Tolerância de déficit256 MBDéficit máximo de armazenamento antes de rejeitar fragmentos de um par
Limite por par1 GBArmazenamento total máximo para qualquer par individual

Criptografia e Tesseras Seladas

A maioria das tesseras são públicas — projetadas para serem acessíveis a qualquer pessoa, para sempre. Mas algumas memórias precisam de privacidade. Tesseras suporta dois modos de visibilidade criptografada:

  • Privada — apenas o criador (e seus herdeiros) podem acessar o conteúdo
  • Selada — o conteúdo é bloqueado por tempo e se torna acessível após uma data específica

Tesseras públicas nunca são criptografadas. Disponibilidade é mais importante que sigilo para preservação.

Como a criptografia funciona

Quando você cria uma tessera privada ou selada, o seguinte acontece:

  1. Uma chave de conteúdo aleatória (256 bits) é gerada
  2. Cada arquivo de memória é criptografado com AES-256-GCM usando essa chave de conteúdo
  3. A chave de conteúdo é envolvida em um envelope de chave selada usando sua chave pública de criptografia
  4. A chave envolvida é armazenada junto ao conteúdo criptografado

Apenas o detentor da chave privada correspondente pode desembrulhar a chave de conteúdo e decriptar o conteúdo.

Encapsulamento de chave híbrido pós-quântico

O envelope de chave selada usa um Mecanismo de Encapsulamento de Chave (KEM) híbrido combinando dois algoritmos:

  • X25519 — uma troca de chaves clássica bem testada baseada em curva elíptica
  • ML-KEM-768 — um KEM pós-quântico baseado em reticulados padronizado pelo NIST (anteriormente Kyber)

Ambos os algoritmos produzem segredos compartilhados que são combinados usando derivação de chaves BLAKE3. Um atacante precisa quebrar ambos os algoritmos para recuperar a chave de conteúdo. Isso segue o mesmo princípio das assinaturas duplas do Tesseras (Ed25519 + ML-DSA): não sabemos quais suposições criptográficas se manterão ao longo dos séculos, então apostamos nos dois.

Dados autenticados associados (AAD)

AES-256-GCM suporta dados autenticados associados — informações extras que são verificadas durante a decriptação mas não são criptografadas. Tesseras vincula as seguintes informações no AAD:

  • O hash do conteúdo da tessera (sempre)
  • O timestamp open_after (apenas para tesseras seladas)

Isso previne ataques de troca de texto cifrado: um atacante não pode copiar conteúdo criptografado de uma tessera para outra, porque o AAD não vai corresponder e a decriptação vai falhar. Para tesseras seladas, isso também significa que você não pode alterar a data do selo — o timestamp está criptograficamente vinculado ao texto cifrado.

Tesseras seladas: cápsulas do tempo

Uma tessera selada é uma verdadeira cápsula do tempo. Quando você cria uma, você especifica uma data open_after. O conteúdo é criptografado e a chave é selada em um envelope que apenas você pode abrir.

Quando a data open_after passa, o dono publica a chave de conteúdo como uma Publicação de Chave assinada — um artefato independente contendo a chave, o hash da tessera e a assinatura do dono. Outros nós podem verificar a assinatura e usar a chave publicada para decriptar o conteúdo.

O manifesto da tessera nunca é modificado. A Publicação de Chave é um documento separado, preservando a natureza imutável e endereçada por conteúdo das tesseras.

E as chaves?

Cada identidade agora inclui um par de chaves de criptografia junto ao par de chaves de assinatura:

Tipo de chaveAlgoritmoFinalidade
Ed25519ClássicoAssinatura de manifestos e publicações de chave
ML-DSAPós-quânticoAssinatura (quando habilitado)
X25519ClássicoEncapsulamento de chave (criptografia)
ML-KEM-768Pós-quânticoEncapsulamento de chave (criptografia)

O par de chaves de criptografia é gerado quando a identidade é criada. A metade pública é armazenada no diretório de identidade da tessera; a metade privada fica no dispositivo do dono.

Princípios de design

  • Criptografar o mínimo possível — apenas conteúdo privado e selado é criptografado. Memórias públicas permanecem abertas para acessibilidade a longo prazo.
  • Algoritmos duplos desde o início — criptografia clássica e pós-quântica, para que o conteúdo esteja protegido mesmo que um algoritmo seja quebrado.
  • Manifestos imutáveis — chaves são publicadas separadamente, nunca modificando dados existentes.
  • Falhar fechado — o sistema rejeita tentativas de criar tesseras privadas ou seladas sem chaves de criptografia.

Recuperação de Chaves por Herdeiros

Suas tesseras podem sobreviver a falhas de infraestrutura, computadores quânticos e séculos de tempo. Mas o que acontece quando você não consegue mais acessar suas próprias chaves? Tesseras usa Shamir’s Secret Sharing para permitir que você distribua sua identidade criptográfica para herdeiros de confiança.

Como funciona

Shamir’s Secret Sharing divide um segredo em N fragmentos com um limiar T. Qualquer T fragmentos podem reconstruir o segredo original. Menos que T fragmentos não revelam nada — isso é informação-teoricamente seguro, não apenas computacionalmente difícil de quebrar.

Por exemplo, com limiar 2 e 3 fragmentos totais:

  • Dê o fragmento 1 ao seu cônjuge
  • Dê o fragmento 2 ao seu irmão
  • Dê o fragmento 3 ao seu advogado

Quaisquer dois deles podem recuperar sua identidade. Um único fragmento sozinho é inútil.

Criando fragmentos de herdeiros

tes heir create --threshold 2 --shares 3

Isso divide sua chave de identidade Ed25519 em 3 fragmentos (necessitando 2 para reconstruir) e os salva em ./heir-shares/:

heir-shares/
├── heir_share_1.bin   # Binário MessagePack
├── heir_share_1.txt   # Texto base64 legível por humanos
├── heir_share_2.bin
├── heir_share_2.txt
├── heir_share_3.bin
└── heir_share_3.txt

Cada fragmento é gerado em dois formatos:

  • Binário (.bin) — MessagePack compacto, adequado para pendrives ou armazenamento digital
  • Texto (.txt) — base64 com cabeçalho legível, adequado para impressão em papel

O formato texto se parece com isso:

--- TESSERAS HEIR SHARE ---
Format: v1
Owner: a1b2c3d4e5f6a7b8 (fingerprint)
Share: 1 of 3 (threshold: 2)
Session: 9f8e7d6c5b4a3210
Created: 2026-02-15

<dados codificados em base64>
--- END HEIR SHARE ---

Reconstruindo a partir de fragmentos

Quando os herdeiros precisam recuperar a identidade:

tes heir reconstruct heir_share_1.txt heir_share_2.bin --output-dir ./recovered-keys

O comando detecta automaticamente se cada arquivo é formato binário ou texto. Ele valida que todos os fragmentos pertencem à mesma sessão e dono, verifica checksums e reconstrói o par de chaves Ed25519.

Para instalar as chaves recuperadas como identidade ativa:

tes heir reconstruct share1.txt share2.txt --output-dir ./recovered --install

Isso faz backup da identidade atual antes de substituí-la.

Inspecionando um fragmento

Para ver metadados sobre um fragmento sem expor dados secretos:

tes heir info heir_share_1.txt

Saída:

Heir Share Information:
  Format version: 1
  Share: 1 of 3 (threshold: 2)
  Session: 9f8e7d6c5b4a3210
  Owner fingerprint: a1b2c3d4e5f6a7b8
  Share data size: 34 bytes
  Checksum: valid

Considerações de segurança

  • Escolha do limiar: um limiar de 2-de-3 ou 3-de-5 é recomendado para a maioria das pessoas. Limiares mais altos são mais seguros mas requerem mais herdeiros para cooperar.
  • Armazenamento físico: imprima os arquivos .txt em papel livre de ácido e armazene em locais físicos separados (cofres bancários, casas diferentes). Papel sobrevive décadas sem degradação.
  • Nunca armazene fragmentos juntos: todo o propósito da divisão é a distribuição. Manter todos os fragmentos em um lugar anula o objetivo.
  • Isolamento de sessão: cada chamada heir create gera um novo ID de sessão. Fragmentos de sessões diferentes não podem ser misturados — isso previne confusão após rotações de chave.
  • Verificação de checksum: cada fragmento inclui um checksum BLAKE3. Fragmentos corrompidos (erros de OCR, degradação de bits) são detectados antes de qualquer tentativa de reconstrução.
  • Re-dividir após mudanças de chave: se você regenerar sua identidade, crie novos fragmentos de herdeiros e destrua com segurança os antigos.

Princípios de design

  • Segurança informação-teórica — T-1 fragmentos revelam exatamente zero informação sobre o segredo. Isso não é uma suposição computacional; é matematicamente provado.
  • Detecção de corrupção — checksums BLAKE3 detectam degradação de bits, erros de OCR e truncamento antes de qualquer tentativa de reconstrução.
  • Resiliência de formato — saída dupla (binário + texto) garante que fragmentos sobrevivam a diferentes modos de falha de mídia de armazenamento.
  • Compatibilidade retroativa — o blob do segredo é versionado, para que versões futuras possam incluir material de chave adicional sem quebrar fragmentos existentes.

Travessia de NAT

A maioria dos dispositivos na internet ficam atras de um NAT (Network Address Translator). Seu roteador atribui ao seu dispositivo um endereco privado (como 192.168.1.100) e o traduz para um endereco publico quando voce conecta para fora. Isso funciona bem para navegar na web, mas cria um problema para redes P2P: dois dispositivos atras de NATs diferentes nao conseguem se conectar diretamente sem ajuda.

Tesseras resolve isso com uma abordagem em tres camadas, tentando a opcao mais barata primeiro:

  1. Conexao direta — se ambos os nos tem IPs publicos, eles conectam diretamente
  2. UDP hole punching — um terceiro no apresenta os dois peers para que eles possam furar seus NATs
  3. Relay — um no com IP publico encaminha pacotes entre os dois peers

Descoberta do tipo de NAT

Quando um no inicia, ele envia requisicoes STUN (Session Traversal Utilities for NAT) para multiplos servidores publicos. Comparando os enderecos externos que esses servidores reportam, o no classifica seu NAT:

Tipo de NATO que significaHole punching?
PublicSem NAT — seu dispositivo tem IP publicoNao necessario
ConeNAT mapeia a mesma porta interna para a mesma porta externa independente do destinoFunciona bem (~80%)
SymmetricNAT atribui uma porta externa diferente para cada destinoNao confiavel
UnknownNao conseguiu alcancar servidores STUNRelay necessario

Seu no divulga seu tipo de NAT em mensagens Pong do DHT, para que outros nos saibam se hole punching vale a pena tentar.

Hole punching

Quando o no A (atras de um NAT Cone) quer conectar ao no B (tambem atras de um NAT Cone), nenhum consegue alcancar o outro diretamente. A solucao:

  1. A envia uma mensagem PunchIntro ao no I (um introdutor — qualquer no com IP publico que ambos conhecam). A mensagem inclui o endereco externo de A (do STUN) e uma assinatura Ed25519 provando a identidade de A.

  2. I verifica a assinatura e encaminha um PunchRequest a B, incluindo o endereco de A e a assinatura original.

  3. B verifica a assinatura (provando que a requisicao realmente veio de A, nao de uma fonte falsificada). B entao envia um pacote UDP para o endereco externo de A — isso abre um pinhole no NAT de B. B tambem envia uma mensagem PunchReady de volta a A com o endereco externo de B.

  4. A envia um pacote UDP para o endereco externo de B. Ambos os NATs agora tem pinholes, e os dois nos podem se comunicar diretamente.

O processo inteiro leva 2-5 segundos. As assinaturas Ed25519 previnem ataques de reflexao, onde um atacante reproduz uma introducao antiga para redirecionar trafego.

Fallback por relay

Quando hole punching falha (NAT Symmetric, firewalls estritos ou redes corporativas), nos usam relay atraves de um no com IP publico:

  1. A envia um RelayRequest ao no R (um no com IP publico com relay habilitado).
  2. R cria uma sessao e envia um RelayOffer a ambos A e B, contendo o endereco do relay e um token de sessao.
  3. A e B enviam seus pacotes a R, prefixados com o token de sessao. R remove o token e encaminha o payload ao outro peer.

Sessoes de relay tem limites de largura de banda:

  • 256 KB/s para peers com boa reciprocidade (eles armazenam fragmentos para outros)
  • 64 KB/s para peers sem reciprocidade
  • Sessoes nao reciprocas sao limitadas a 10 minutos

Isso incentiva nos a contribuir armazenamento — bons cidadaos da rede recebem melhor servico de relay.

Migracao de endereco

Quando um dispositivo movel troca de rede (Wi-Fi para celular), seu endereco IP muda. Ao inves de encerrar e reconstruir sessoes de relay, o no envia uma mensagem RelayMigrate assinada para atualizar seu endereco na sessao existente. Isso evita reestabelecer conexoes do zero.

Configuracao

A secao [nat] na configuracao do daemon controla a travessia de NAT:

[nat]
# Servidores STUN para deteccao de tipo de NAT
stun_servers = ["stun.l.google.com:19302", "stun.cloudflare.com:3478"]

# Habilitar relay (encaminhar trafego para outros nos com NAT)
relay_enabled = false

# Maximo de sessoes de relay simultaneas
relay_max_sessions = 50

# Limite de largura de banda para peers reciprocos (KB/s)
relay_reciprocal_kbps = 256

# Limite de largura de banda para peers nao reciprocos (KB/s)
relay_bootstrap_kbps = 64

# Timeout de inatividade de sessao relay (segundos)
relay_idle_timeout_secs = 60

Para executar um no relay, defina relay_enabled = true. Seu no deve ter um IP publico (ou roteador com port forwarding) para servir como relay.

Reconexao mobile

Quando o app Tesseras detecta uma mudanca de rede em um dispositivo movel, ele executa uma sequencia de reconexao em tres fases:

  1. Migracao QUIC (0-2s) — QUIC suporta migracao de conexao nativamente. O app tenta migrar todas as conexoes ativas para o novo endereco.
  2. Re-STUN (2-5s) — descobre o novo endereco externo e re-anuncia ao DHT.
  3. Reestabelecimento (5-10s) — reconecta peers que a migracao nao conseguiu salvar, em ordem de prioridade: nos bootstrap primeiro, depois nos que guardam seus fragmentos, depois nos cujos fragmentos voce guarda.

O app mostra progresso de reconexao atraves do stream de eventos NetworkChanged.

Monitoramento

A travessia de NAT expoe metricas Prometheus em /metrics:

  • tesseras_nat_type — tipo de NAT detectado atualmente
  • tesseras_stun_requests_total / tesseras_stun_failures_total — confiabilidade STUN
  • tesseras_punch_attempts_total{initiator_nat, target_nat} — taxa de sucesso de punch por par de NAT
  • tesseras_relay_sessions_active — carga atual de relay
  • tesseras_relay_bytes_forwarded — largura de banda total de relay
  • tesseras_network_change_total — frequencia de mudanca de rede no mobile

Docker

Tesseras fornece uma imagem Docker para executar o daemon em conteineres. Isso e util para servidores, testar redes com multiplos nos e ambientes de CI.

Construindo a imagem

A partir da raiz do repositorio:

docker build -t tesseras-daemon .

O Dockerfile multi-estagio usa rust:1.85 para compilar e debian:bookworm-slim como base de execucao. A imagem resultante e pequena e contem apenas o binario do daemon e certificados CA.

Executando um unico no

docker run -d \
  --name tesseras \
  -p 4433:4433/udp \
  tesseras-daemon

Isso inicia um no que:

  • Escuta na porta UDP 4433
  • Faz bootstrap a partir dos nos semente padrao
  • Armazena dados dentro do conteiner (efemero)

Para persistir dados entre reinicializacoes do conteiner, monte um volume:

docker run -d \
  --name tesseras \
  -p 4433:4433/udp \
  -v tesseras-data:/root/.local/share/tesseras \
  tesseras-daemon

Executando como no semente

Para executar um no semente que nao faz bootstrap de ninguem:

docker run -d \
  --name tesseras-seed \
  -p 4433:4433/udp \
  tesseras-daemon --listen 0.0.0.0:4433 --bootstrap ""

Rede multi-no com Docker Compose

O repositorio inclui um arquivo Docker Compose para testar uma rede de 3 nos:

services:
  boot1:
    build: ../..
    command: ["--listen", "0.0.0.0:4433", "--bootstrap", ""]
    ports: ["4433:4433/udp"]

  boot2:
    build: ../..
    command: ["--listen", "0.0.0.0:4433", "--bootstrap", "boot1:4433"]
    depends_on: [boot1]

  client:
    build: ../..
    command: ["--listen", "0.0.0.0:4433", "--bootstrap", "boot2:4433"]
    depends_on: [boot2]

Iniciar a rede:

cd tests/smoke
docker compose up --build -d

Verificar que todos os nos estao executando:

docker compose logs --tail=5

Voce devera ver daemon ready nos logs de cada no, e bootstrap successful para boot2 e client.

Parar a rede:

docker compose down

Configuracao personalizada

Para usar um arquivo de configuracao com Docker, monte-o no conteiner:

docker run -d \
  --name tesseras \
  -p 4433:4433/udp \
  -v ./config.toml:/etc/tesseras/config.toml:ro \
  -v tesseras-data:/root/.local/share/tesseras \
  tesseras-daemon --config /etc/tesseras/config.toml

Veja o capitulo Configuracao para todas as opcoes disponiveis.