1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
|
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Fase 4: Furando NATs — Tesseras</title>
<meta name="description" content="Os nos Tesseras agora podem descobrir seu tipo de NAT via STUN, coordenar UDP hole punching atraves de introdutores e usar relay transparente quando a conectividade direta falha.">
<!-- Open Graph -->
<meta property="og:type" content="article">
<meta property="og:title" content="Fase 4: Furando NATs">
<meta property="og:description" content="Os nos Tesseras agora podem descobrir seu tipo de NAT via STUN, coordenar UDP hole punching atraves de introdutores e usar relay transparente quando a conectividade direta falha.">
<meta property="og:image" content="https://tesseras.net/images/social.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:site_name" content="Tesseras">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Fase 4: Furando NATs">
<meta name="twitter:description" content="Os nos Tesseras agora podem descobrir seu tipo de NAT via STUN, coordenar UDP hole punching atraves de introdutores e usar relay transparente quando a conectividade direta falha.">
<meta name="twitter:image" content="https://tesseras.net/images/social.jpg">
<link rel="stylesheet" href="https://tesseras.net/style.css?h=21f0f32121928ee5c690">
<link rel="alternate" type="application/atom+xml" title="Tesseras" href="https://tesseras.net/atom.xml">
<link rel="icon" type="image/png" sizes="32x32" href="https://tesseras.net/images/favicon.png?h=be4e123a23393b1a027d">
</head>
<body>
<header>
<h1>
<a href="https://tesseras.net/pt-br/">
<img src="https://tesseras.net/images/logo-64.png?h=c1b8d0c4c5f93b49d40b" alt="Tesseras" width="40" height="40" class="logo">
Tesseras
</a>
</h1>
<nav>
<a href="https://tesseras.net/pt-br/about/">Sobre</a>
<a href="https://tesseras.net/pt-br/news/">Notícias</a>
<a href="https://tesseras.net/pt-br/releases/">Lançamentos</a>
<a href="https://tesseras.net/pt-br/faq/">FAQ</a>
<a href="https://tesseras.net/pt-br/subscriptions/">Inscrições</a>
<a href="https://tesseras.net/pt-br/contact/">Contato</a>
</nav>
<nav class="lang-switch">
<a href="https://tesseras.net/news/phase4-nat-traversal/">English</a> | <strong>Português</strong>
</nav>
</header>
<main>
<article>
<h2>Fase 4: Furando NATs</h2>
<p class="news-date">2026-02-15</p>
<p>A maioria dos dispositivos das pessoas ficam atras de um NAT — um tradutor de
enderecos de rede que permite acessar a internet mas impede conexoes de entrada.
Para uma rede P2P, isso e um problema existencial: se dois nos atras de NATs nao
conseguem se comunicar, a rede se fragmenta. A Fase 4 continua com uma pilha
completa de travessia de NAT: descoberta via STUN, hole punching coordenado e
fallback por relay.</p>
<p>A abordagem segue o mesmo padrao da maioria dos sistemas P2P consolidados
(WebRTC, BitTorrent, IPFS): tente a opcao mais barata primeiro, escale apenas
quando necessario. Conectividade direta nao custa nada. Hole punching custa
alguns pacotes coordenados. Relay custa largura de banda sustentada de um
terceiro. Tesseras tenta nessa ordem.</p>
<h2 id="o-que-foi-construido">O que foi construido</h2>
<p><strong>Classificacao NatType</strong> (<code>tesseras-core/src/network.rs</code>) — Um novo enum
<code>NatType</code> (Public, Cone, Symmetric, Unknown) adicionado a camada de dominio
core. Esse tipo e compartilhado por toda a pilha: o cliente STUN o escreve, o
DHT o divulga em mensagens Pong, e o coordenador de punch o le para decidir se
hole punching vale a pena tentar (Cone-para-Cone funciona ~80% das vezes;
Symmetric-para-Symmetric quase nunca funciona).</p>
<p><strong>Cliente STUN</strong> (<code>tesseras-net/src/stun.rs</code>) — Uma implementacao STUN minima
(RFC 5389 Binding Request/Response) que descobre o endereco externo de um no. O
codec codifica requisicoes de 20 bytes com um ID de transacao aleatorio e
decodifica respostas XOR-MAPPED-ADDRESS. A funcao <code>discover_nat()</code> consulta
multiplos servidores STUN em paralelo (Google, Cloudflare por padrao), compara
os enderecos mapeados e classifica o tipo de NAT:</p>
<ul>
<li>Mesmo IP e porta de todos os servidores → <strong>Public</strong> (sem NAT)</li>
<li>Mesmo endereco mapeado de todos os servidores → <strong>Cone</strong> (hole punching
funciona)</li>
<li>Enderecos mapeados diferentes → <strong>Symmetric</strong> (hole punching nao confiavel)</li>
<li>Sem respostas → <strong>Unknown</strong></li>
</ul>
<p>Retentativas com backoff exponencial e timeouts configuraveis. 12 testes
cobrindo roundtrips de codec, todos os caminhos de classificacao e consultas
async em loopback.</p>
<p><strong>Coordenacao de punch assinada</strong> (<code>tesseras-net/src/punch.rs</code>) — Assinatura e
verificacao Ed25519 para mensagens <code>PunchIntro</code>, <code>RelayRequest</code> e
<code>RelayMigrate</code>. Cada introducao e assinada pelo iniciador com uma janela de
timestamp de 30 segundos, prevenindo ataques de reflexao (onde um atacante
reproduz uma introducao antiga para redirecionar trafego). O formato do payload
e <code>target || external_addr || timestamp</code> — alterar qualquer campo invalida a
assinatura. 6 testes unitarios mais 3 testes baseados em propriedades com
proptest (IDs de no, portas e tokens de sessao arbitrarios).</p>
<p><strong>Gerenciador de sessoes de relay</strong> (<code>tesseras-net/src/relay.rs</code>) — Gerencia
sessoes de relay UDP transparente entre nos com NAT. Cada sessao tem um token
aleatorio de 16 bytes; os nos prefixam seus pacotes com o token, o relay remove
e encaminha. Funcionalidades:</p>
<ul>
<li>Encaminhamento bidirecional (A→R→B e B→R→A)</li>
<li>Limite de taxa: 256 KB/s para nos reciprocos, 64 KB/s para nao reciprocos</li>
<li>Duracao maxima de 10 minutos para sessoes bootstrap (nao reciprocas)</li>
<li>Migracao de endereco: quando o IP de um no muda (Wi-Fi para celular), um
<code>RelayMigrate</code> assinado atualiza a sessao sem derruba-la</li>
<li>Limpeza por inatividade com timeout configuravel</li>
<li>8 testes unitarios mais 2 testes baseados em propriedades</li>
</ul>
<p><strong>Extensoes de mensagens DHT</strong> (<code>tesseras-dht/src/message.rs</code>) — Sete novas
variantes de mensagem adicionadas ao protocolo DHT:</p>
<table><thead><tr><th>Mensagem</th><th>Proposito</th></tr></thead><tbody>
<tr><td><code>PunchIntro</code></td><td>"Quero conectar ao no X, aqui esta meu endereco externo assinado"</td></tr>
<tr><td><code>PunchRequest</code></td><td>O introdutor encaminha a requisicao ao destino</td></tr>
<tr><td><code>PunchReady</code></td><td>O destino confirma prontidao, envia seu endereco externo</td></tr>
<tr><td><code>RelayRequest</code></td><td>"Crie uma sessao de relay para o no X"</td></tr>
<tr><td><code>RelayOffer</code></td><td>O relay responde com seu endereco e token de sessao</td></tr>
<tr><td><code>RelayClose</code></td><td>Encerrar uma sessao de relay</td></tr>
<tr><td><code>RelayMigrate</code></td><td>Atualizar sessao apos mudanca de rede</td></tr>
</tbody></table>
<p>A mensagem <code>Pong</code> foi estendida com metadados NAT: <code>nat_type</code>,
<code>relay_slots_available</code> e <code>relay_bandwidth_used_kbps</code>. Todos os novos campos
usam <code>#[serde(default)]</code> para compatibilidade retroativa — nos antigos ignoram o
que nao reconhecem, nos novos usam defaults. 9 novos testes de roundtrip de
serializacao.</p>
<p><strong>Trait NatHandler e dispatch</strong> (<code>tesseras-dht/src/engine.rs</code>) — Uma nova trait
async <code>NatHandler</code> (5 metodos) injetada no engine DHT, seguindo o mesmo padrao
de injecao de dependencia do <code>ReplicationHandler</code> existente. O loop de dispatch
de mensagens do engine agora roteia todas as mensagens punch/relay para o
handler. Isso mantem o engine DHT agnóstico ao protocolo enquanto permite que a
logica de travessia de NAT viva em <code>tesseras-net</code>.</p>
<p><strong>Tipos de reconexao mobile</strong> (<code>tesseras-embedded/src/reconnect.rs</code>) — Uma
maquina de estados de reconexao em tres fases para dispositivos moveis:</p>
<ol>
<li><strong>QuicMigration</strong> (0-2s) — tenta migracao de conexao QUIC para todos os peers
ativos</li>
<li><strong>ReStun</strong> (2-5s) — redescobre endereco externo via STUN</li>
<li><strong>ReEstablish</strong> (5-10s) — reconecta peers que a migracao nao conseguiu salvar</li>
</ol>
<p>Peers sao reconectados em ordem de prioridade: nos bootstrap primeiro, depois
nos que guardam nossos fragmentos, depois nos cujos fragmentos guardamos, depois
vizinhos DHT gerais. Uma nova variante de evento <code>NetworkChanged</code> foi adicionada
ao stream de eventos FFI para que o app Flutter possa mostrar progresso de
reconexao.</p>
<p><strong>Configuracao NAT do daemon</strong> (<code>tesd/src/config.rs</code>) — Uma nova secao <code>[nat]</code>
na configuracao TOML com lista de servidores STUN, toggle de relay, maximo de
sessoes relay, limites de largura de banda (reciproco vs bootstrap) e timeout de
inatividade. Todos os campos tem defaults sensiveis; relay e desabilitado por
padrao.</p>
<p><strong>Metricas Prometheus</strong> (<code>tesseras-net/src/metrics.rs</code>) — 16 metricas em quatro
subsistemas:</p>
<ul>
<li><strong>STUN</strong>: requisicoes, falhas, histograma de latencia</li>
<li><strong>Punch</strong>: tentativas/sucessos/falhas (por par de tipo NAT), histograma de
latencia</li>
<li><strong>Relay</strong>: sessoes ativas, sessoes totais, bytes encaminhados, timeouts por
inatividade, hits de rate limit</li>
<li><strong>Reconexao</strong>: mudancas de rede, tentativas/sucessos por fase, histograma de
duracao</li>
</ul>
<p>6 testes verificando registro, incremento, cardinalidade de labels e deteccao de
registro duplo.</p>
<p><strong>Testes de integracao</strong> — Dois testes end-to-end usando <code>MemTransport</code> (rede
simulada em memoria):</p>
<ul>
<li><code>punch_integration.rs</code> — Fluxo completo de hole-punch com 3 nos: A envia
<code>PunchIntro</code> assinado ao introdutor I, I verifica e encaminha <code>PunchRequest</code> a
B, B verifica a assinatura original e envia <code>PunchReady</code> de volta, A e B
trocam mensagens diretamente. Tambem testa que uma assinatura invalida e
corretamente rejeitada.</li>
<li><code>relay_integration.rs</code> — Fluxo completo de relay com 3 nos: A solicita relay
de R, R cria sessao e envia <code>RelayOffer</code> a ambos os peers, A e B trocam
pacotes prefixados com token atraves de R, A migra para um novo endereco no
meio da sessao, A fecha a sessao, e o teste verifica que a sessao e encerrada
e encaminhamento posterior falha.</li>
</ul>
<p><strong>Testes de propriedade</strong> — 7 testes baseados em proptest cobrindo: roundtrips
de assinatura para todos os tres tipos de mensagem assinada (IDs de no, portas e
tokens arbitrarios), determinismo de classificacao NAT (mesmas entradas sempre
produzem mesma saida), validade de binding request STUN, unicidade de tokens de
sessao, e rejeicao de pacotes curtos pelo relay.</p>
<p><strong>Alvos Justfile</strong> — <code>just test-nat</code> executa todos os testes de travessia NAT em
<code>tesseras-net</code> e <code>tesseras-dht</code>. <code>just test-chaos</code> e um placeholder para futuros
testes de caos com Docker Compose e <code>tc netem</code>.</p>
<h2 id="decisoes-de-arquitetura">Decisoes de arquitetura</h2>
<ul>
<li><strong>STUN ao inves de TURN</strong>: implementamos STUN (descoberta) e relay customizado
ao inves de TURN completo. TURN requer alocacao autenticada e foi projetado
para relay de midia; nosso relay e mais simples — encaminhamento UDP com
prefixo de token e limites de taxa. Isso mantem o protocolo minimo e evita
depender de servidores TURN externos.</li>
<li><strong>Assinaturas em introducoes</strong>: cada <code>PunchIntro</code> e assinado pelo iniciador.
Sem isso, um atacante poderia enviar introducoes forjadas para redirecionar as
tentativas de hole-punch de um no para um endereco controlado pelo atacante
(ataque de reflexao). A janela de timestamp de 30 segundos limita replay.</li>
<li><strong>Tiers reciprocos de largura de banda</strong>: nos relay dao 4x mais largura de
banda (256 vs 64 KB/s) para peers com boas pontuacoes de reciprocidade. Isso
incentiva nos a armazenar fragmentos para outros — se voce contribui, recebe
melhor servico de relay quando precisa.</li>
<li><strong>Extensao Pong retrocompativel</strong>: novos campos NAT em <code>Pong</code> usam
<code>#[serde(default)]</code> e <code>Option<T></code>. Nos antigos que nao entendem esses campos
simplesmente os pulam durante deserializacao. Nenhum bump de versao de
protocolo necessario.</li>
<li><strong>NatHandler como trait async</strong>: a logica de travessia NAT e injetada no
engine DHT via trait, assim como <code>ReplicationHandler</code>. Isso mantem o engine
DHT focado em roteamento e gerenciamento de peers, e permite que a
implementacao NAT seja trocada ou desabilitada sem tocar no codigo core do
DHT.</li>
</ul>
<h2 id="o-que-vem-a-seguir">O que vem a seguir</h2>
<ul>
<li><strong>Fase 4 continuacao</strong> — tuning de performance (pooling de conexoes, cache de
fragmentos, SQLite WAL), auditorias de seguranca, onboarding de nos
institucionais, empacotamento para OS</li>
<li><strong>Fase 5: Exploracao e Cultura</strong> — navegador publico de tesseras por
era/localizacao/tema/idioma, curadoria institucional, integracao genealogica,
exportacao para midia fisica (M-DISC, microfilme, papel livre de acido com QR)</li>
</ul>
<p>Com travessia de NAT, Tesseras pode conectar nos independentemente de sua
topologia de rede. Nos publicos conversam diretamente. Nos com NAT Cone furam
com ajuda de um introdutor. Nos com NAT Symmetric ou firewalled usam relay
atraves de peers voluntarios. A rede se adapta ao mundo real, onde a maioria dos
dispositivos esta atras de um NAT e as condicoes de rede mudam constantemente.</p>
</article>
</main>
<footer>
<p>© 2026 Tesseras Project. <a href="/atom.xml">News Feed</a> · <a href="https://git.sr.ht/~ijanc/tesseras">Source</a></p>
</footer>
</body>
</html>
|