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
| Conceito | Descrição |
|---|---|
| Tessera | Uma cápsula do tempo autocontida de memórias |
| Memória | Um item individual (foto, gravação, vídeo ou texto) dentro de uma tessera |
| Hash de conteúdo | Um hash BLAKE3 que identifica unicamente uma tessera pelo seu conteúdo |
| Visibilidade | Controla quem pode acessar uma tessera: pública, privada, selada ou círculo |
| Tessera selada | Uma cápsula do tempo que só pode ser aberta após uma data específica |
| MANIFEST | Um índice em texto puro listando cada arquivo na tessera com seu checksum |
| Tipo de memória | Categoriza uma memória: momento, reflexão, cotidiano, relação ou objeto |
| Nó | Um dispositivo executando o daemon Tesseras, participando da rede P2P |
| DHT | Tabela hash distribuída — como os nós encontram ponteiros de tesseras sem um servidor central |
| Bootstrap | O 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ção | Comando |
|---|---|
| Arch Linux | sudo pacman -S sqlite |
| Debian / Ubuntu | sudo apt install libsqlite3-dev |
| Fedora | sudo dnf install sqlite-devel |
| Alpine | apk add sqlite-dev |
| macOS (Homebrew) | brew install sqlite |
| FreeBSD | pkg install sqlite3 |
| OpenBSD | Já 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
| Ferramenta | Para quê | Instalação |
|---|---|---|
| just | Executar comandos de build do projeto | cargo install just |
| mdBook | Compilar a documentação | cargo install mdbook |
| Docker | Executar nós em contêineres | Veja Docker |
| Flutter | Compilar o app mobile/desktop | Veja 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 tesserastesseras-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
- Flutter SDK — instale seguindo o guia oficial
- Rust — já instalado conforme acima
- Dependências de plataforma:
| Plataforma | Dependências |
|---|---|
| Android | Android SDK, Android NDK, Java 17+ |
| iOS | Xcode, CocoaPods |
| Linux desktop | GTK 3.0+, pkg-config (sudo apt install libgtk-3-dev pkg-config no Debian/Ubuntu) |
| macOS desktop | Xcode 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:
| Protocolo | Porta | Direção |
|---|---|---|
| UDP | 4433 | Entrada e saída |
Próximos passos
- Início Rápido — crie sua primeira tessera
- Executando um Nó — configure e execute o daemon
- Configuração — opções de configuração
- Docker — execute em contêineres
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:
| Caminho | Conteú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.toml | Arquivo de configuração |
Opções
| Opção | Descriçã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
- Cria a estrutura de diretórios (
identity/,db/,blobs/) - Gera um par de chaves Ed25519 (a chave privada permanece local, a chave pública identifica você)
- Executa as migrações SQLite para configurar o esquema do banco de dados
- Escreve um
config.tomlpadrão
tes create
Criar uma tessera a partir de um diretório de arquivos.
Uso
tes create <CAMINHO> [OPÇÕES]
Argumentos
| Argumento | Descrição |
|---|---|
<CAMINHO> | Diretório contendo os arquivos a incluir |
Opções
| Opção | Descrição | Padrão |
|---|---|---|
-n, --non-interactive | Pular prompts | desativado |
--dry-run | Pré-visualizar o que seria incluído | desativado |
--visibility <VALOR> | Nível de visibilidade: public, private, circle | public |
--language <CÓDIGO> | Código de idioma (ex.: en, pt-BR) | en |
--tags <LISTA> | Tags separadas por vírgula | nenhuma |
--location <DESC> | Descrição do local | nenhuma |
--data-dir <CAMINHO> | Diretório base para armazenamento de dados | ~/.tesseras |
Formatos de arquivo suportados
| Extensão | Tipo | Tipo de memória |
|---|---|---|
.jpg, .jpeg | Imagem (JPEG) | Momento |
.png | Imagem (PNG) | Momento |
.wav | Áudio (WAV PCM) | Momento |
.webm | Vídeo (WebM) | Momento |
.txt | Texto 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ível | Quem pode acessar |
|---|---|
public | Qualquer pessoa (padrão) |
private | Apenas você (e herdeiros designados) |
circle | Pessoas explicitamente escolhidas |
O que acontece internamente
- Varre o diretório em busca de arquivos suportados
- Calcula um hash BLAKE3 para cada arquivo
- Atribui um tipo de memória baseado na extensão do arquivo
- Gera um MANIFEST listando todos os arquivos com seus checksums
- Assina o MANIFEST com sua chave privada Ed25519
- Armazena os arquivos e metadados no banco de dados local
- Exibe o hash de conteúdo que identifica unicamente esta tessera
tes verify
Verificar integridade de uma tessera armazenada.
Uso
tes verify <HASH>
Argumentos
| Argumento | Descrição |
|---|---|
<HASH> | Hash de conteúdo da tessera (64 caracteres hexadecimais) |
Opções
| Opção | Descrição |
|---|---|
--data-dir <CAMINHO> | Diretório base para armazenamento de dados (padrão: ~/.tesseras) |
O que é verificado
- Validade da assinatura — verifica a assinatura Ed25519 sobre o MANIFEST
- Integridade dos arquivos — recalcula o hash BLAKE3 de cada arquivo e compara com o MANIFEST
Códigos de saída
| Código | Significado |
|---|---|
0 | Verificação passou — todos os arquivos intactos, assinatura válida |
1 | Verificaçã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
| Argumento | Descrição |
|---|---|
<HASH> | Hash de conteúdo da tessera (64 caracteres hexadecimais) |
<DESTINO> | Diretório de destino |
Opções
| Opção | Descriçã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ção | Descrição |
|---|---|
--data-dir <CAMINHO> | Diretório base para armazenamento de dados (padrão: ~/.tesseras) |
Saída
Exibe uma tabela com as seguintes colunas:
| Coluna | Descrição |
|---|---|
| Hash | Primeiros 16 caracteres do hash de conteúdo |
| Created | Data de criação (AAAA-MM-DD) |
| Memories | Número de memórias na tessera |
| Size | Tamanho total (B, KB, MB ou GB) |
| Visibility | Ní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:
- Cria o diretorio de dados (
~/.local/share/tesserasno Linux,~/Library/Application Support/tesserasno macOS) - Gera uma identidade de no com prova de trabalho (leva cerca de 1 segundo)
- Abre um listener QUIC em
0.0.0.0:4433 - Faz bootstrap na rede contactando nos semente
- Imprime
daemon readyquando totalmente operacional
Opcoes de linha de comando
tesseras-daemon [OPTIONS]
| Opcao | Descricao | Padrao |
|---|---|---|
-c, --config <PATH> | Caminho para um arquivo de configuracao TOML | Nenhum (usa padroes internos) |
-l, --listen <ADDR> | Endereco e porta para escutar | 0.0.0.0:4433 |
-b, --bootstrap <ADDRS> | Enderecos de bootstrap separados por virgula | boot1.tesseras.net:4433,boot2.tesseras.net:4433 |
-d, --data-dir <PATH> | Diretorio de dados | Especifico 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:
- Parar de aceitar novas conexoes
- Finalizar operacoes em andamento (ate 5 segundos)
- Fechar todas as conexoes QUIC
- 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.
| Chave | Tipo | Padrao | Descricao |
|---|---|---|---|
data_dir | caminho | Especifico da plataforma | Onde armazenar identidade, banco de dados e blobs |
listen_addr | endereco | 0.0.0.0:4433 | Endereco 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.
| Chave | Tipo | Padrao | Descricao |
|---|---|---|---|
k | inteiro | 20 | Maximo de entradas por bucket da tabela de roteamento |
alpha | inteiro | 3 | Paralelismo para buscas iterativas |
bucket_refresh_interval_secs | inteiro | 3600 | Com que frequencia atualizar buckets da tabela de roteamento (segundos) |
republish_interval_secs | inteiro | 3600 | Com que frequencia republicar ponteiros armazenados (segundos) |
pointer_ttl_secs | inteiro | 86400 | Quanto tempo manter um ponteiro antes de expirar (segundos) |
max_stored_pointers | inteiro | 100000 | Numero maximo de ponteiros armazenados localmente |
ping_failure_threshold | inteiro | 3 | Quantas falhas consecutivas de ping antes de remover um par |
[bootstrap]
Como o no descobre seus primeiros pares ao entrar na rede.
| Chave | Tipo | Padrao | Descricao |
|---|---|---|---|
dns_domain | string | _tesseras._udp.tesseras.net | Dominio DNS para descoberta de pares via registros TXT |
hardcoded | lista de strings | ["boot1.tesseras.net:4433", "boot2.tesseras.net:4433"] | Enderecos de bootstrap de fallback |
[network]
Funcionalidades de nivel de rede.
| Chave | Tipo | Padrao | Descricao |
|---|---|---|---|
enable_mdns | booleano | true | Habilitar descoberta na rede local via mDNS |
[observability]
Monitoramento e logging.
| Chave | Tipo | Padrao | Descricao |
|---|---|---|---|
metrics_addr | endereco | 127.0.0.1:9190 | Endereco para o endpoint de metricas Prometheus |
log_format | string | json | Formato 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 |
|---|---|---|
| Linux | Sim (dual-stack) | IPV6_V6ONLY padrao 0 |
| macOS | Sim (dual-stack) | IPV6_V6ONLY padrao 0 |
| FreeBSD | Sim (dual-stack) | IPV6_V6ONLY padrao 0 |
| OpenBSD | Nao (somente IPv6) | IPV6_V6ONLY sempre 1 |
| Windows | Sim (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:
- Contactar nos semente — conectar a um ou mais enderecos de bootstrap conhecidos
- Trocar pings — verificar que o semente esta vivo e trocar identidades de no
- Auto-busca — perguntar ao semente por nos proximos ao seu proprio ID, para popular sua tabela de roteamento
- 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:
| Tipo | Descricao | Sempre ligado? |
|---|---|---|
| No completo | Desktop, servidor ou Raspberry Pi executando tesseras-daemon. Participa plenamente da DHT e armazena dados de outros nos. | Sim |
| No movel | Celular ou tablet executando o app Tesseras. Participa da DHT quando o app esta ativo. | Nao |
| No navegador | Navegador web executando o cliente WASM. Conecta via um no relay. Somente leitura. | Nao |
| No IoT | ESP32 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:
| Camada | Tamanho | Estratégia | Fragmentos |
|---|---|---|---|
| Small | < 4 MB | Replicação do arquivo inteiro | 7 cópias do arquivo completo |
| Medium | 4–256 MB | Reed-Solomon 16+8 | 16 dados + 8 paridade = 24 fragmentos |
| Large | ≥ 256 MB | Reed-Solomon 48+24 | 48 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:
- Codificar — os dados da tessera são divididos em fragmentos de acordo com sua camada de tamanho
- Encontrar pares — o daemon consulta a DHT pelos nós mais próximos ao hash da tessera
- 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)
- Distribuir — os fragmentos são enviados aos pares selecionados em ordem round-robin
- 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:
- Solicita atestações dos detentores conhecidos — cada detentor prova que ainda possui os fragmentos reportando seus checksums
- Recorre ao ping se a atestação falhar — para distinguir entre “nó está offline” e “nó perdeu os dados”
- Verifica fragmentos locais — verifica a integridade de quaisquer fragmentos armazenados localmente recalculando checksums BLAKE3
- 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âmetro | Padrão | Descrição |
|---|---|---|
| Intervalo de reparo | 24 horas | Com que frequência o loop de reparo roda |
| Jitter de reparo | 2 horas | Atraso aleatório adicionado para evitar tempestades na rede |
| Transferências simultâneas | 4 | Máximo de transferências paralelas de fragmentos |
| Espaço livre mínimo | 1 GB | Parar de aceitar fragmentos abaixo deste limite |
| Tolerância de déficit | 256 MB | Déficit máximo de armazenamento antes de rejeitar fragmentos de um par |
| Limite por par | 1 GB | Armazenamento 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:
- Uma chave de conteúdo aleatória (256 bits) é gerada
- Cada arquivo de memória é criptografado com AES-256-GCM usando essa chave de conteúdo
- A chave de conteúdo é envolvida em um envelope de chave selada usando sua chave pública de criptografia
- 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 chave | Algoritmo | Finalidade |
|---|---|---|
| Ed25519 | Clássico | Assinatura de manifestos e publicações de chave |
| ML-DSA | Pós-quântico | Assinatura (quando habilitado) |
| X25519 | Clássico | Encapsulamento de chave (criptografia) |
| ML-KEM-768 | Pós-quântico | Encapsulamento 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
.txtem 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 creategera 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:
- Conexao direta — se ambos os nos tem IPs publicos, eles conectam diretamente
- UDP hole punching — um terceiro no apresenta os dois peers para que eles possam furar seus NATs
- 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 NAT | O que significa | Hole punching? |
|---|---|---|
| Public | Sem NAT — seu dispositivo tem IP publico | Nao necessario |
| Cone | NAT mapeia a mesma porta interna para a mesma porta externa independente do destino | Funciona bem (~80%) |
| Symmetric | NAT atribui uma porta externa diferente para cada destino | Nao confiavel |
| Unknown | Nao conseguiu alcancar servidores STUN | Relay 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:
-
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.
-
I verifica a assinatura e encaminha um PunchRequest a B, incluindo o endereco de A e a assinatura original.
-
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.
-
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:
- A envia um RelayRequest ao no R (um no com IP publico com relay habilitado).
- R cria uma sessao e envia um RelayOffer a ambos A e B, contendo o endereco do relay e um token de sessao.
- 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:
- Migracao QUIC (0-2s) — QUIC suporta migracao de conexao nativamente. O app tenta migrar todas as conexoes ativas para o novo endereco.
- Re-STUN (2-5s) — descobre o novo endereco externo e re-anuncia ao DHT.
- 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 atualmentetesseras_stun_requests_total/tesseras_stun_failures_total— confiabilidade STUNtesseras_punch_attempts_total{initiator_nat, target_nat}— taxa de sucesso de punch por par de NATtesseras_relay_sessions_active— carga atual de relaytesseras_relay_bytes_forwarded— largura de banda total de relaytesseras_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.