diff options
Diffstat (limited to 'book/pt-br/print.html')
| -rw-r--r-- | book/pt-br/print.html | 1532 |
1 files changed, 1532 insertions, 0 deletions
diff --git a/book/pt-br/print.html b/book/pt-br/print.html new file mode 100644 index 0000000..c8e30ce --- /dev/null +++ b/book/pt-br/print.html @@ -0,0 +1,1532 @@ +<!DOCTYPE HTML> +<html lang="pt-BR" class="light sidebar-visible" dir="ltr"> + <head> + <!-- Book generated using mdBook --> + <meta charset="UTF-8"> + <title>Guia do Usuário Tesseras</title> + <meta name="robots" content="noindex"> + + + <!-- Custom HTML head --> + + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="theme-color" content="#ffffff"> + + <link rel="shortcut icon" href="favicon-bfbdfe47.png"> + <link rel="stylesheet" href="css/variables-8adf115d.css"> + <link rel="stylesheet" href="css/general-2459343d.css"> + <link rel="stylesheet" href="css/chrome-ae938929.css"> + <link rel="stylesheet" href="css/print-9e4910d8.css" media="print"> + + <!-- Fonts --> + <link rel="stylesheet" href="fonts/fonts-9644e21d.css"> + + <!-- Highlight.js Stylesheets --> + <link rel="stylesheet" id="mdbook-highlight-css" href="highlight-493f70e1.css"> + <link rel="stylesheet" id="mdbook-tomorrow-night-css" href="tomorrow-night-4c0ae647.css"> + <link rel="stylesheet" id="mdbook-ayu-highlight-css" href="ayu-highlight-3fdfc3ac.css"> + + <!-- Custom theme stylesheets --> + <link rel="stylesheet" href="theme/custom-eff59930.css"> + + + <!-- Provide site root and default themes to javascript --> + <script> + const path_to_root = ""; + const default_light_theme = "light"; + const default_dark_theme = "navy"; + window.path_to_searchindex_js = "searchindex-3eaa3696.js"; + </script> + <!-- Start loading toc.js asap --> + <script src="toc-25102ebc.js"></script> + </head> + <body> + <div id="mdbook-help-container"> + <div id="mdbook-help-popup"> + <h2 class="mdbook-help-title">Keyboard shortcuts</h2> + <div> + <p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p> + <p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p> + <p>Press <kbd>?</kbd> to show this help</p> + <p>Press <kbd>Esc</kbd> to hide this help</p> + </div> + </div> + </div> + <div id="mdbook-body-container"> + <!-- Work around some values being stored in localStorage wrapped in quotes --> + <script> + try { + let theme = localStorage.getItem('mdbook-theme'); + let sidebar = localStorage.getItem('mdbook-sidebar'); + + if (theme.startsWith('"') && theme.endsWith('"')) { + localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1)); + } + + if (sidebar.startsWith('"') && sidebar.endsWith('"')) { + localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1)); + } + } catch (e) { } + </script> + + <!-- Set the theme before any content is loaded, prevents flash --> + <script> + const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme; + let theme; + try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { } + if (theme === null || theme === undefined) { theme = default_theme; } + const html = document.documentElement; + html.classList.remove('light') + html.classList.add(theme); + html.classList.add("js"); + </script> + + <input type="checkbox" id="mdbook-sidebar-toggle-anchor" class="hidden"> + + <!-- Hide / unhide sidebar before it is displayed --> + <script> + let sidebar = null; + const sidebar_toggle = document.getElementById("mdbook-sidebar-toggle-anchor"); + if (document.body.clientWidth >= 1080) { + try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { } + sidebar = sidebar || 'visible'; + } else { + sidebar = 'hidden'; + sidebar_toggle.checked = false; + } + if (sidebar === 'visible') { + sidebar_toggle.checked = true; + } else { + html.classList.remove('sidebar-visible'); + } + </script> + + <nav id="mdbook-sidebar" class="sidebar" aria-label="Table of contents"> + <!-- populated by js --> + <mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox> + <noscript> + <iframe class="sidebar-iframe-outer" src="toc.html"></iframe> + </noscript> + <div id="mdbook-sidebar-resize-handle" class="sidebar-resize-handle"> + <div class="sidebar-resize-indicator"></div> + </div> + </nav> + + <div id="mdbook-page-wrapper" class="page-wrapper"> + + <div class="page"> + <div id="mdbook-menu-bar-hover-placeholder"></div> + <div id="mdbook-menu-bar" class="menu-bar sticky"> + <div class="left-buttons"> + <label id="mdbook-sidebar-toggle" class="icon-button" for="mdbook-sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="mdbook-sidebar"> + <span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg></span> + </label> + <button id="mdbook-theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="mdbook-theme-list"> + <span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M371.3 367.1c27.3-3.9 51.9-19.4 67.2-42.9L600.2 74.1c12.6-19.5 9.4-45.3-7.6-61.2S549.7-4.4 531.1 9.6L294.4 187.2c-24 18-38.2 46.1-38.4 76.1L371.3 367.1zm-19.6 25.4l-116-104.4C175.9 290.3 128 339.6 128 400c0 3.9 .2 7.8 .6 11.6c1.8 17.5-10.2 36.4-27.8 36.4H96c-17.7 0-32 14.3-32 32s14.3 32 32 32H240c61.9 0 112-50.1 112-112c0-2.5-.1-5-.2-7.5z"/></svg></span> + </button> + <ul id="mdbook-theme-list" class="theme-popup" aria-label="Themes" role="menu"> + <li role="none"><button role="menuitem" class="theme" id="mdbook-theme-default_theme">Auto</button></li> + <li role="none"><button role="menuitem" class="theme" id="mdbook-theme-light">Light</button></li> + <li role="none"><button role="menuitem" class="theme" id="mdbook-theme-rust">Rust</button></li> + <li role="none"><button role="menuitem" class="theme" id="mdbook-theme-coal">Coal</button></li> + <li role="none"><button role="menuitem" class="theme" id="mdbook-theme-navy">Navy</button></li> + <li role="none"><button role="menuitem" class="theme" id="mdbook-theme-ayu">Ayu</button></li> + </ul> + <button id="mdbook-search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="mdbook-searchbar"> + <span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352c79.5 0 144-64.5 144-144s-64.5-144-144-144S64 128.5 64 208s64.5 144 144 144z"/></svg></span> + </button> + </div> + + <h1 class="menu-title">Guia do Usuário Tesseras</h1> + + <div class="right-buttons"> + <a href="print.html" title="Print this book" aria-label="Print this book"> + <span class=fa-svg id="print-button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M128 0C92.7 0 64 28.7 64 64v96h64V64H354.7L384 93.3V160h64V93.3c0-17-6.7-33.3-18.7-45.3L400 18.7C388 6.7 371.7 0 354.7 0H128zM384 352v32 64H128V384 368 352H384zm64 32h32c17.7 0 32-14.3 32-32V256c0-35.3-28.7-64-64-64H64c-35.3 0-64 28.7-64 64v96c0 17.7 14.3 32 32 32H64v64c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V384zm-16-88c-13.3 0-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24s-10.7 24-24 24z"/></svg></span> + </a> + <a href="https://git.sr.ht/~ijanc/tesseras" title="Git repository" aria-label="Git repository"> + <span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg></span> + </a> + + </div> + </div> + + <div id="mdbook-search-wrapper" class="hidden"> + <form id="mdbook-searchbar-outer" class="searchbar-outer"> + <div class="search-wrapper"> + <input type="search" id="mdbook-searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="mdbook-searchresults-outer" aria-describedby="searchresults-header"> + <div class="spinner-wrapper"> + <span class=fa-svg id="fa-spin"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M304 48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zm0 416c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM48 304c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm464-48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM142.9 437c18.7-18.7 18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zm0-294.2c18.7-18.7 18.7-49.1 0-67.9S93.7 56.2 75 75s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zM369.1 437c18.7 18.7 49.1 18.7 67.9 0s18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9z"/></svg></span> + </div> + </div> + </form> + <div id="mdbook-searchresults-outer" class="searchresults-outer hidden"> + <div id="mdbook-searchresults-header" class="searchresults-header"></div> + <ul id="mdbook-searchresults"> + </ul> + </div> + </div> + + <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM --> + <script> + document.getElementById('mdbook-sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible'); + document.getElementById('mdbook-sidebar').setAttribute('aria-hidden', sidebar !== 'visible'); + Array.from(document.querySelectorAll('#mdbook-sidebar a')).forEach(function(link) { + link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1); + }); + </script> + + <div id="mdbook-content" class="content"> + <main> + <h1 id="introdução"><a class="header" href="#introdução">Introdução</a></h1> +<p>Tesseras é uma rede peer-to-peer para preservar memórias humanas através dos milênios. Cada pessoa cria uma <strong>tessera</strong> — uma cápsula do tempo autocontida de memórias (fotos, áudio, vídeo, texto) que sobrevive independentemente de qualquer software, empresa ou infraestrutura.</p> +<h2 id="o-que-é-uma-tessera"><a class="header" href="#o-que-é-uma-tessera">O que é uma tessera?</a></h2> +<p>A palavra <em>tessera</em> 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.</p> +<p>Uma tessera contém:</p> +<ul> +<li><strong>Memórias</strong> — fotos (JPEG), gravações de áudio (WAV), vídeo (WebM) e texto (UTF-8 puro)</li> +<li><strong>Metadados</strong> — quando e onde cada memória foi criada, quem está envolvido e o que significa</li> +<li><strong>Identidade</strong> — assinaturas criptográficas provando quem criou</li> +<li><strong>Instruções de decodificação</strong> — explicações em texto puro de cada formato utilizado, para que humanos do futuro possam ler o conteúdo</li> +</ul> +<h2 id="filosofia-central"><a class="header" href="#filosofia-central">Filosofia central</a></h2> +<ul> +<li><strong>Sem dependência de empresas</strong> — suas memórias são suas, armazenadas localmente e replicadas em uma rede peer-to-peer</li> +<li><strong>Sem aprisionamento de formato</strong> — cada tessera inclui instruções para decodificar seu conteúdo</li> +<li><strong>Disponibilidade acima de sigilo</strong> — memórias públicas não são criptografadas, porque acessibilidade a longo prazo importa mais do que esconder coisas</li> +<li><strong>Criptografia mínima</strong> — apenas conteúdo privado e selado é criptografado; todo o resto é aberto</li> +<li><strong>Resistente a computadores quânticos</strong> — assinaturas duplas (Ed25519 + ML-DSA) protegem a integridade mesmo contra futuros computadores quânticos</li> +</ul> +<h2 id="status-atual-fase-4"><a class="header" href="#status-atual-fase-4">Status atual: Fase 4</a></h2> +<p>Tesseras completou até a <strong>Fase 4</strong> — criptografia e tesseras seladas. O projeto agora cobre gerenciamento local de tesseras, rede, replicação, app mobile e privacidade criptográfica.</p> +<p>O que está disponível hoje:</p> +<ul> +<li>Geração de identidade (par de chaves Ed25519 com prova de trabalho)</li> +<li>Criação de tesseras a partir de arquivos locais</li> +<li>Armazenamento endereçado por conteúdo (hashing BLAKE3)</li> +<li>Verificação de integridade e exportação autocontida</li> +<li>Daemon de nó completo com transporte QUIC</li> +<li>Descoberta de pares via DHT Kademlia</li> +<li>Publicação e busca de ponteiros de tesseras pela rede</li> +<li>Codificação de apagamento Reed-Solomon com reparo automático de fragmentos</li> +<li>App mobile Flutter com nó Rust P2P embarcado</li> +<li><strong>Tesseras privadas</strong> — conteúdo criptografado que apenas o dono pode acessar</li> +<li><strong>Tesseras seladas</strong> — conteúdo com bloqueio temporal que abre após uma data específica</li> +<li><strong>Criptografia híbrida pós-quântica</strong> — encapsulamento de chaves X25519 + ML-KEM-768</li> +<li><strong>AES-256-GCM</strong> para criptografia de conteúdo com vinculação AAD</li> +</ul> +<h2 id="conceitos-chave"><a class="header" href="#conceitos-chave">Conceitos-chave</a></h2> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Conceito</th><th>Descrição</th></tr> +</thead> +<tbody> +<tr><td><strong>Tessera</strong></td><td>Uma cápsula do tempo autocontida de memórias</td></tr> +<tr><td><strong>Memória</strong></td><td>Um item individual (foto, gravação, vídeo ou texto) dentro de uma tessera</td></tr> +<tr><td><strong>Hash de conteúdo</strong></td><td>Um hash BLAKE3 que identifica unicamente uma tessera pelo seu conteúdo</td></tr> +<tr><td><strong>Visibilidade</strong></td><td>Controla quem pode acessar uma tessera: pública, privada, selada ou círculo</td></tr> +<tr><td><strong>Tessera selada</strong></td><td>Uma cápsula do tempo que só pode ser aberta após uma data específica</td></tr> +<tr><td><strong>MANIFEST</strong></td><td>Um índice em texto puro listando cada arquivo na tessera com seu checksum</td></tr> +<tr><td><strong>Tipo de memória</strong></td><td>Categoriza uma memória: momento, reflexão, cotidiano, relação ou objeto</td></tr> +<tr><td><strong>Nó</strong></td><td>Um dispositivo executando o daemon Tesseras, participando da rede P2P</td></tr> +<tr><td><strong>DHT</strong></td><td>Tabela hash distribuída — como os nós encontram ponteiros de tesseras sem um servidor central</td></tr> +<tr><td><strong>Bootstrap</strong></td><td>O processo de entrar na rede contactando nós semente conhecidos</td></tr> +</tbody> +</table> +</div> +<div style="break-before: page; page-break-before: always;"></div> +<h1 id="instalação"><a class="header" href="#instalação">Instalação</a></h1> +<p>Tesseras está disponível atualmente através de compilação a partir do código-fonte.</p> +<h2 id="pré-requisitos"><a class="header" href="#pré-requisitos">Pré-requisitos</a></h2> +<h3 id="rust"><a class="header" href="#rust">Rust</a></h3> +<p>Tesseras requer <strong>Rust 1.85 ou superior</strong>. A maneira recomendada de instalar o Rust é via <a href="https://rustup.rs/">rustup</a>:</p> +<pre><code class="language-bash">curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +</code></pre> +<p>Após a instalação, certifique-se de que <code>~/.cargo/bin</code> está no seu <code>PATH</code>. O instalador normalmente adiciona isso automaticamente. Verifique com:</p> +<pre><code class="language-bash">rustc --version +cargo --version +</code></pre> +<p>Se você já tem o Rust instalado, atualize para a versão mais recente:</p> +<pre><code class="language-bash">rustup update stable +</code></pre> +<h3 id="sqlite"><a class="header" href="#sqlite">SQLite</a></h3> +<p>Tesseras usa SQLite para armazenamento local. Você tem duas opções:</p> +<p><strong>Opção 1: SQLite do sistema (recomendada)</strong></p> +<p>Instale as bibliotecas de desenvolvimento do SQLite pelo gerenciador de pacotes do seu sistema:</p> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Distribuição</th><th>Comando</th></tr> +</thead> +<tbody> +<tr><td>Arch Linux</td><td><code>sudo pacman -S sqlite</code></td></tr> +<tr><td>Debian / Ubuntu</td><td><code>sudo apt install libsqlite3-dev</code></td></tr> +<tr><td>Fedora</td><td><code>sudo dnf install sqlite-devel</code></td></tr> +<tr><td>Alpine</td><td><code>apk add sqlite-dev</code></td></tr> +<tr><td>macOS (Homebrew)</td><td><code>brew install sqlite</code></td></tr> +<tr><td>FreeBSD</td><td><code>pkg install sqlite3</code></td></tr> +<tr><td>OpenBSD</td><td>Já incluído no sistema base</td></tr> +</tbody> +</table> +</div> +<p><strong>Opção 2: SQLite embutido</strong></p> +<p>Se preferir não instalar o SQLite no sistema, use a feature flag <code>bundled-sqlite</code> durante a compilação. Isso compila o SQLite junto com o Tesseras:</p> +<pre><code class="language-bash">cargo install --path crates/tesseras-cli --features bundled-sqlite +cargo install --path crates/tesseras-daemon --features bundled-sqlite +</code></pre> +<h3 id="ferramentas-opcionais"><a class="header" href="#ferramentas-opcionais">Ferramentas opcionais</a></h3> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Ferramenta</th><th>Para quê</th><th>Instalação</th></tr> +</thead> +<tbody> +<tr><td><a href="https://github.com/casey/just">just</a></td><td>Executar comandos de build do projeto</td><td><code>cargo install just</code></td></tr> +<tr><td><a href="https://rust-lang.github.io/mdBook/">mdBook</a></td><td>Compilar a documentação</td><td><code>cargo install mdbook</code></td></tr> +<tr><td><a href="https://docs.docker.com/get-docker/">Docker</a></td><td>Executar nós em contêineres</td><td>Veja <a href="#docker">Docker</a></td></tr> +<tr><td><a href="https://flutter.dev/docs/get-started/install">Flutter</a></td><td>Compilar o app mobile/desktop</td><td>Veja <a href="#app-flutter">App Flutter</a></td></tr> +</tbody> +</table> +</div> +<h2 id="compilar-a-partir-do-código-fonte"><a class="header" href="#compilar-a-partir-do-código-fonte">Compilar a partir do código-fonte</a></h2> +<p>Clone o repositório e instale os binários:</p> +<pre><code class="language-bash">git clone https://git.sr.ht/~ijanc/tesseras +cd tesseras +cargo install --path crates/tesseras-cli +cargo install --path crates/tesseras-daemon +</code></pre> +<p>Ou, se você tiver o <code>just</code> instalado:</p> +<pre><code class="language-bash">just install +</code></pre> +<p>Isso instala dois binários em <code>~/.cargo/bin/</code> e configura auto-completions para o seu shell:</p> +<ul> +<li><code>tes</code> — ferramenta CLI para criar, verificar e exportar tesseras</li> +<li><code>tesseras-daemon</code> — daemon de nó completo que participa da rede P2P</li> +</ul> +<h2 id="verificar-a-instalação"><a class="header" href="#verificar-a-instalação">Verificar a instalação</a></h2> +<pre><code class="language-bash">tes --help +</code></pre> +<p>Você deverá ver:</p> +<pre><code>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 +</code></pre> +<h2 id="auto-completions-do-shell"><a class="header" href="#auto-completions-do-shell">Auto-completions do shell</a></h2> +<p>O comando <code>just install</code> configura completions automaticamente. Se você instalou manualmente, gere as completions para o seu shell:</p> +<pre><code class="language-bash"># 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" +</code></pre> +<h2 id="app-flutter"><a class="header" href="#app-flutter">App Flutter</a></h2> +<p>Para compilar o app mobile ou desktop, você precisa de dependências adicionais:</p> +<h3 id="pré-requisitos-do-flutter"><a class="header" href="#pré-requisitos-do-flutter">Pré-requisitos do Flutter</a></h3> +<ol> +<li><strong>Flutter SDK</strong> — instale seguindo o <a href="https://flutter.dev/docs/get-started/install">guia oficial</a></li> +<li><strong>Rust</strong> — já instalado conforme acima</li> +<li><strong>Dependências de plataforma:</strong></li> +</ol> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Plataforma</th><th>Dependências</th></tr> +</thead> +<tbody> +<tr><td>Android</td><td>Android SDK, Android NDK, Java 17+</td></tr> +<tr><td>iOS</td><td>Xcode, CocoaPods</td></tr> +<tr><td>Linux desktop</td><td>GTK 3.0+, pkg-config (<code>sudo apt install libgtk-3-dev pkg-config</code> no Debian/Ubuntu)</td></tr> +<tr><td>macOS desktop</td><td>Xcode Command Line Tools</td></tr> +</tbody> +</table> +</div> +<h3 id="compilar-o-app"><a class="header" href="#compilar-o-app">Compilar o app</a></h3> +<pre><code class="language-bash">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 +</code></pre> +<p>Ou usando <code>just</code> a partir da raiz do repositório:</p> +<pre><code class="language-bash">just build-linux # Linux desktop +just build-android # Android APK +just test-flutter # Testes +</code></pre> +<h2 id="portas-de-rede"><a class="header" href="#portas-de-rede">Portas de rede</a></h2> +<p>O daemon Tesseras usa QUIC (protocolo sobre UDP). Se você estiver atrás de um firewall, permita tráfego na porta:</p> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Protocolo</th><th>Porta</th><th>Direção</th></tr> +</thead> +<tbody> +<tr><td>UDP</td><td>4433</td><td>Entrada e saída</td></tr> +</tbody> +</table> +</div> +<h2 id="próximos-passos"><a class="header" href="#próximos-passos">Próximos passos</a></h2> +<ul> +<li><a href="#início-rápido">Início Rápido</a> — crie sua primeira tessera</li> +<li><a href="#executando-um-no">Executando um Nó</a> — configure e execute o daemon</li> +<li><a href="#configuracao">Configuração</a> — opções de configuração</li> +<li><a href="#docker">Docker</a> — execute em contêineres</li> +</ul> +<div style="break-before: page; page-break-before: always;"></div> +<h1 id="início-rápido"><a class="header" href="#início-rápido">Início Rápido</a></h1> +<p>Este tutorial guia você por um fluxo completo: criar uma identidade, construir uma tessera a partir de arquivos, verificá-la e exportá-la.</p> +<h2 id="1-inicializar-sua-identidade"><a class="header" href="#1-inicializar-sua-identidade">1. Inicializar sua identidade</a></h2> +<p>Primeiro, configure sua identidade local e banco de dados:</p> +<pre><code class="language-bash">tes init +</code></pre> +<pre><code>Generated Ed25519 identity +Database initialized +Config written to /home/user/.tesseras/config.toml +Tesseras initialized at /home/user/.tesseras +</code></pre> +<p>Isso cria:</p> +<ul> +<li><code>~/.tesseras/identity/</code> — seu par de chaves Ed25519</li> +<li><code>~/.tesseras/db/</code> — banco de dados SQLite para indexação</li> +<li><code>~/.tesseras/blobs/</code> — armazenamento para arquivos de memória</li> +<li><code>~/.tesseras/config.toml</code> — arquivo de configuração</li> +</ul> +<h2 id="2-preparar-seus-arquivos"><a class="header" href="#2-preparar-seus-arquivos">2. Preparar seus arquivos</a></h2> +<p>Crie um diretório com as memórias que deseja preservar:</p> +<pre><code class="language-bash">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 +</code></pre> +<p>Formatos suportados: <code>.jpg</code>, <code>.jpeg</code>, <code>.png</code> (imagens), <code>.wav</code> (áudio), <code>.webm</code> (vídeo), <code>.txt</code> (texto).</p> +<h2 id="3-pré-visualizar-com-dry-run"><a class="header" href="#3-pré-visualizar-com-dry-run">3. Pré-visualizar com dry run</a></h2> +<p>Veja o que seria incluído sem criar nada:</p> +<pre><code class="language-bash">tes create minhas-memorias --dry-run +</code></pre> +<h2 id="4-criar-uma-tessera"><a class="header" href="#4-criar-uma-tessera">4. Criar uma tessera</a></h2> +<pre><code class="language-bash">tes create minhas-memorias --tags "familia,domingo" --location "Casa" +</code></pre> +<p>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.</p> +<h2 id="5-listar-suas-tesseras"><a class="header" href="#5-listar-suas-tesseras">5. Listar suas tesseras</a></h2> +<pre><code class="language-bash">tes list +</code></pre> +<pre><code>Hash Created Memories Size Visibility +9f2c4a1b3e7d8f0c 2026-02-14 3 284 KB public +</code></pre> +<h2 id="6-verificar-integridade"><a class="header" href="#6-verificar-integridade">6. Verificar integridade</a></h2> +<p>Use o hash de conteúdo para verificar que todos os arquivos estão intactos e a assinatura é válida:</p> +<pre><code class="language-bash">tes verify 9f2c4a1b3e7d8f0c... +</code></pre> +<pre><code>Tessera: 9f2c4a1b3e7d8f0c... +Signature: VALID + [OK] memories/a1b2c3/media.jpg + [OK] memories/d4e5f6/media.jpg + [OK] memories/g7h8i9/media.txt +Verification: PASSED +</code></pre> +<h2 id="7-exportar-uma-cópia-autocontida"><a class="header" href="#7-exportar-uma-cópia-autocontida">7. Exportar uma cópia autocontida</a></h2> +<p>Exporte a tessera para um diretório que pode ser lido sem o Tesseras:</p> +<pre><code class="language-bash">tes export 9f2c4a1b3e7d8f0c... ./backup +</code></pre> +<pre><code>Exported to ./backup/tessera-9f2c4a1b3e7d8f0c... +</code></pre> +<h2 id="8-inspecionar-a-exportação"><a class="header" href="#8-inspecionar-a-exportação">8. Inspecionar a exportação</a></h2> +<p>O diretório exportado é totalmente autocontido:</p> +<pre><code>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 +</code></pre> +<p>Tudo que um leitor futuro precisa para entender o conteúdo está incluído no próprio diretório — nenhum software Tesseras é necessário.</p> +<div style="break-before: page; page-break-before: always;"></div> +<h1 id="tes-init"><a class="header" href="#tes-init">tes init</a></h1> +<p>Inicializar identidade e banco de dados local.</p> +<h2 id="uso"><a class="header" href="#uso">Uso</a></h2> +<pre><code class="language-bash">tes init +</code></pre> +<h2 id="descrição"><a class="header" href="#descrição">Descrição</a></h2> +<p>Configura seu ambiente Tesseras local. Este é o primeiro comando que você deve executar após instalar o Tesseras.</p> +<p>O comando cria:</p> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Caminho</th><th>Conteúdo</th></tr> +</thead> +<tbody> +<tr><td><code>~/.tesseras/identity/</code></td><td>Par de chaves Ed25519 para assinar tesseras</td></tr> +<tr><td><code>~/.tesseras/db/</code></td><td>Banco de dados SQLite para indexação</td></tr> +<tr><td><code>~/.tesseras/blobs/</code></td><td>Armazenamento de blobs para arquivos de memória</td></tr> +<tr><td><code>~/.tesseras/config.toml</code></td><td>Arquivo de configuração</td></tr> +</tbody> +</table> +</div> +<h2 id="opções"><a class="header" href="#opções">Opções</a></h2> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Opção</th><th>Descrição</th></tr> +</thead> +<tbody> +<tr><td><code>--data-dir <CAMINHO></code></td><td>Diretório base para armazenamento de dados (padrão: <code>~/.tesseras</code>)</td></tr> +</tbody> +</table> +</div> +<h2 id="idempotente"><a class="header" href="#idempotente">Idempotente</a></h2> +<p>Executar <code>init</code> novamente é seguro. Se uma identidade já existe, ela é preservada:</p> +<pre><code class="language-bash">tes init +</code></pre> +<pre><code>Ed25519 identity already exists +Database initialized +Tesseras initialized at /home/user/.tesseras +</code></pre> +<h2 id="diretório-de-dados-personalizado"><a class="header" href="#diretório-de-dados-personalizado">Diretório de dados personalizado</a></h2> +<pre><code class="language-bash">tes --data-dir /mnt/usb/tesseras init +</code></pre> +<p>Isso cria toda a estrutura de diretórios em <code>/mnt/usb/tesseras/</code> ao invés do local padrão.</p> +<h2 id="o-que-acontece-internamente"><a class="header" href="#o-que-acontece-internamente">O que acontece internamente</a></h2> +<ol> +<li>Cria a estrutura de diretórios (<code>identity/</code>, <code>db/</code>, <code>blobs/</code>)</li> +<li>Gera um par de chaves Ed25519 (a chave privada permanece local, a chave pública identifica você)</li> +<li>Executa as migrações SQLite para configurar o esquema do banco de dados</li> +<li>Escreve um <code>config.toml</code> padrão</li> +</ol> +<div style="break-before: page; page-break-before: always;"></div> +<h1 id="tes-create"><a class="header" href="#tes-create">tes create</a></h1> +<p>Criar uma tessera a partir de um diretório de arquivos.</p> +<h2 id="uso-1"><a class="header" href="#uso-1">Uso</a></h2> +<pre><code class="language-bash">tes create <CAMINHO> [OPÇÕES] +</code></pre> +<h2 id="argumentos"><a class="header" href="#argumentos">Argumentos</a></h2> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Argumento</th><th>Descrição</th></tr> +</thead> +<tbody> +<tr><td><code><CAMINHO></code></td><td>Diretório contendo os arquivos a incluir</td></tr> +</tbody> +</table> +</div> +<h2 id="opções-1"><a class="header" href="#opções-1">Opções</a></h2> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Opção</th><th>Descrição</th><th>Padrão</th></tr> +</thead> +<tbody> +<tr><td><code>-n, --non-interactive</code></td><td>Pular prompts</td><td>desativado</td></tr> +<tr><td><code>--dry-run</code></td><td>Pré-visualizar o que seria incluído</td><td>desativado</td></tr> +<tr><td><code>--visibility <VALOR></code></td><td>Nível de visibilidade: <code>public</code>, <code>private</code>, <code>circle</code></td><td><code>public</code></td></tr> +<tr><td><code>--language <CÓDIGO></code></td><td>Código de idioma (ex.: <code>en</code>, <code>pt-BR</code>)</td><td><code>en</code></td></tr> +<tr><td><code>--tags <LISTA></code></td><td>Tags separadas por vírgula</td><td>nenhuma</td></tr> +<tr><td><code>--location <DESC></code></td><td>Descrição do local</td><td>nenhuma</td></tr> +<tr><td><code>--data-dir <CAMINHO></code></td><td>Diretório base para armazenamento de dados</td><td><code>~/.tesseras</code></td></tr> +</tbody> +</table> +</div> +<h2 id="formatos-de-arquivo-suportados"><a class="header" href="#formatos-de-arquivo-suportados">Formatos de arquivo suportados</a></h2> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Extensão</th><th>Tipo</th><th>Tipo de memória</th></tr> +</thead> +<tbody> +<tr><td><code>.jpg</code>, <code>.jpeg</code></td><td>Imagem (JPEG)</td><td>Momento</td></tr> +<tr><td><code>.png</code></td><td>Imagem (PNG)</td><td>Momento</td></tr> +<tr><td><code>.wav</code></td><td>Áudio (WAV PCM)</td><td>Momento</td></tr> +<tr><td><code>.webm</code></td><td>Vídeo (WebM)</td><td>Momento</td></tr> +<tr><td><code>.txt</code></td><td>Texto puro (UTF-8)</td><td>Reflexão</td></tr> +</tbody> +</table> +</div> +<p>Arquivos com outras extensões são ignorados.</p> +<h2 id="inferência-de-tipo-de-memória"><a class="header" href="#inferência-de-tipo-de-memória">Inferência de tipo de memória</a></h2> +<p>O comando atribui automaticamente um tipo de memória baseado no formato do arquivo:</p> +<ul> +<li><strong>Arquivos de texto</strong> (<code>.txt</code>) são classificados como <strong>Reflexão</strong> — pensamentos, crenças ou opiniões</li> +<li><strong>Todos os outros formatos</strong> são classificados como <strong>Momento</strong> — uma foto, gravação ou vídeo de algo acontecendo</li> +</ul> +<h2 id="exemplos"><a class="header" href="#exemplos">Exemplos</a></h2> +<h3 id="pré-visualizar-antes-de-criar"><a class="header" href="#pré-visualizar-antes-de-criar">Pré-visualizar antes de criar</a></h3> +<pre><code class="language-bash">tes create ./minhas-fotos --dry-run +</code></pre> +<h3 id="criar-com-metadados"><a class="header" href="#criar-com-metadados">Criar com metadados</a></h3> +<pre><code class="language-bash">tes create ./ferias-2026 \ + --tags "ferias,verao,praia" \ + --location "Florianópolis, Brasil" \ + --language pt-BR \ + --visibility public +</code></pre> +<h3 id="modo-não-interativo"><a class="header" href="#modo-não-interativo">Modo não-interativo</a></h3> +<pre><code class="language-bash">tes create ./diario --non-interactive --tags "cotidiano" +</code></pre> +<h2 id="níveis-de-visibilidade"><a class="header" href="#níveis-de-visibilidade">Níveis de visibilidade</a></h2> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Nível</th><th>Quem pode acessar</th></tr> +</thead> +<tbody> +<tr><td><code>public</code></td><td>Qualquer pessoa (padrão)</td></tr> +<tr><td><code>private</code></td><td>Apenas você (e herdeiros designados)</td></tr> +<tr><td><code>circle</code></td><td>Pessoas explicitamente escolhidas</td></tr> +</tbody> +</table> +</div> +<h2 id="o-que-acontece-internamente-1"><a class="header" href="#o-que-acontece-internamente-1">O que acontece internamente</a></h2> +<ol> +<li>Varre o diretório em busca de arquivos suportados</li> +<li>Calcula um hash BLAKE3 para cada arquivo</li> +<li>Atribui um tipo de memória baseado na extensão do arquivo</li> +<li>Gera um MANIFEST listando todos os arquivos com seus checksums</li> +<li>Assina o MANIFEST com sua chave privada Ed25519</li> +<li>Armazena os arquivos e metadados no banco de dados local</li> +<li>Exibe o hash de conteúdo que identifica unicamente esta tessera</li> +</ol> +<div style="break-before: page; page-break-before: always;"></div> +<h1 id="tes-verify"><a class="header" href="#tes-verify">tes verify</a></h1> +<p>Verificar integridade de uma tessera armazenada.</p> +<h2 id="uso-2"><a class="header" href="#uso-2">Uso</a></h2> +<pre><code class="language-bash">tes verify <HASH> +</code></pre> +<h2 id="argumentos-1"><a class="header" href="#argumentos-1">Argumentos</a></h2> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Argumento</th><th>Descrição</th></tr> +</thead> +<tbody> +<tr><td><code><HASH></code></td><td>Hash de conteúdo da tessera (64 caracteres hexadecimais)</td></tr> +</tbody> +</table> +</div> +<h2 id="opções-2"><a class="header" href="#opções-2">Opções</a></h2> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Opção</th><th>Descrição</th></tr> +</thead> +<tbody> +<tr><td><code>--data-dir <CAMINHO></code></td><td>Diretório base para armazenamento de dados (padrão: <code>~/.tesseras</code>)</td></tr> +</tbody> +</table> +</div> +<h2 id="o-que-é-verificado"><a class="header" href="#o-que-é-verificado">O que é verificado</a></h2> +<ol> +<li><strong>Validade da assinatura</strong> — verifica a assinatura Ed25519 sobre o MANIFEST</li> +<li><strong>Integridade dos arquivos</strong> — recalcula o hash BLAKE3 de cada arquivo e compara com o MANIFEST</li> +</ol> +<h2 id="códigos-de-saída"><a class="header" href="#códigos-de-saída">Códigos de saída</a></h2> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Código</th><th>Significado</th></tr> +</thead> +<tbody> +<tr><td><code>0</code></td><td>Verificação passou — todos os arquivos intactos, assinatura válida</td></tr> +<tr><td><code>1</code></td><td>Verificação falhou — arquivos corrompidos ou assinatura inválida</td></tr> +</tbody> +</table> +</div> +<h2 id="exemplos-1"><a class="header" href="#exemplos-1">Exemplos</a></h2> +<h3 id="verificação-bem-sucedida"><a class="header" href="#verificação-bem-sucedida">Verificação bem-sucedida</a></h3> +<pre><code class="language-bash">tes verify 9f2c4a1b3e7d8f0cabc123def456789012345678abcdef0123456789abcdef01 +</code></pre> +<pre><code>Tessera: 9f2c4a1b3e7d8f0cabc123def456789012345678abcdef0123456789abcdef01 +Signature: VALID + [OK] memories/a1b2c3d4/media.jpg + [OK] memories/e5f6a7b8/media.txt + [OK] memories/c9d0e1f2/media.wav +Verification: PASSED +</code></pre> +<h3 id="verificação-com-falha"><a class="header" href="#verificação-com-falha">Verificação com falha</a></h3> +<p>Se um arquivo foi modificado ou corrompido:</p> +<pre><code>Tessera: 9f2c4a1b3e7d8f0cabc123def456789012345678abcdef0123456789abcdef01 +Signature: VALID + [OK] memories/a1b2c3d4/media.jpg + [FAILED] memories/e5f6a7b8/media.txt + [OK] memories/c9d0e1f2/media.wav +Verification: FAILED +</code></pre> +<h2 id="casos-de-uso"><a class="header" href="#casos-de-uso">Casos de uso</a></h2> +<ul> +<li><strong>Verificações rotineiras de integridade</strong> — verifique periodicamente que suas tesseras armazenadas não foram corrompidas</li> +<li><strong>Após transferência</strong> — verifique após copiar tesseras para um novo dispositivo ou meio de armazenamento</li> +<li><strong>Verificação de confiança</strong> — confirme que uma tessera recebida de outra pessoa não foi adulterada</li> +</ul> +<div style="break-before: page; page-break-before: always;"></div> +<h1 id="tes-export"><a class="header" href="#tes-export">tes export</a></h1> +<p>Exportar uma tessera como um diretório autocontido.</p> +<h2 id="uso-3"><a class="header" href="#uso-3">Uso</a></h2> +<pre><code class="language-bash">tes export <HASH> <DESTINO> +</code></pre> +<h2 id="argumentos-2"><a class="header" href="#argumentos-2">Argumentos</a></h2> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Argumento</th><th>Descrição</th></tr> +</thead> +<tbody> +<tr><td><code><HASH></code></td><td>Hash de conteúdo da tessera (64 caracteres hexadecimais)</td></tr> +<tr><td><code><DESTINO></code></td><td>Diretório de destino</td></tr> +</tbody> +</table> +</div> +<h2 id="opções-3"><a class="header" href="#opções-3">Opções</a></h2> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Opção</th><th>Descrição</th></tr> +</thead> +<tbody> +<tr><td><code>--data-dir <CAMINHO></code></td><td>Diretório base para armazenamento de dados (padrão: <code>~/.tesseras</code>)</td></tr> +</tbody> +</table> +</div> +<h2 id="estrutura-de-saída"><a class="header" href="#estrutura-de-saída">Estrutura de saída</a></h2> +<p>A exportação cria um diretório chamado <code>tessera-<hash></code> dentro do destino:</p> +<pre><code>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 +</code></pre> +<h2 id="exemplo"><a class="header" href="#exemplo">Exemplo</a></h2> +<pre><code class="language-bash">tes export 9f2c4a1b3e7d8f0cabc123def4567890... ./backup +</code></pre> +<pre><code>Exported to ./backup/tessera-9f2c4a1b3e7d8f0cabc123def4567890... +</code></pre> +<h2 id="característica-principal-autocontido"><a class="header" href="#característica-principal-autocontido">Característica principal: autocontido</a></h2> +<p>O diretório exportado é projetado para ser legível <strong>sem o software Tesseras</strong>. Ele inclui:</p> +<ul> +<li><strong>MANIFEST</strong> — um arquivo em texto puro listando cada arquivo com seu checksum BLAKE3, legível por qualquer editor de texto</li> +<li><strong>README.decode</strong> — instruções legíveis por humanos para compreender o conteúdo</li> +<li><strong>decode/</strong> — explicações detalhadas de cada formato de arquivo usado (JPEG, WAV, JSON, UTF-8)</li> +</ul> +<p>Isso significa que alguém daqui a milhares de anos, sem conhecimento algum sobre o Tesseras, ainda pode entender e acessar as memórias.</p> +<h2 id="casos-de-uso-1"><a class="header" href="#casos-de-uso-1">Casos de uso</a></h2> +<ul> +<li><strong>Backup</strong> — exporte para um disco externo, pendrive ou armazenamento em nuvem</li> +<li><strong>Compartilhamento</strong> — entregue a alguém uma cópia completa de uma tessera</li> +<li><strong>Arquivamento</strong> — armazene em mídia de escrita única (DVD, Blu-ray, fita)</li> +<li><strong>Migração</strong> — mova tesseras entre máquinas sem precisar do banco de dados</li> +</ul> +<div style="break-before: page; page-break-before: always;"></div> +<h1 id="tes-list"><a class="header" href="#tes-list">tes list</a></h1> +<p>Listar todas as tesseras locais.</p> +<h2 id="uso-4"><a class="header" href="#uso-4">Uso</a></h2> +<pre><code class="language-bash">tes list +</code></pre> +<h2 id="opções-4"><a class="header" href="#opções-4">Opções</a></h2> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Opção</th><th>Descrição</th></tr> +</thead> +<tbody> +<tr><td><code>--data-dir <CAMINHO></code></td><td>Diretório base para armazenamento de dados (padrão: <code>~/.tesseras</code>)</td></tr> +</tbody> +</table> +</div> +<h2 id="saída"><a class="header" href="#saída">Saída</a></h2> +<p>Exibe uma tabela com as seguintes colunas:</p> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Coluna</th><th>Descrição</th></tr> +</thead> +<tbody> +<tr><td><strong>Hash</strong></td><td>Primeiros 16 caracteres do hash de conteúdo</td></tr> +<tr><td><strong>Created</strong></td><td>Data de criação (AAAA-MM-DD)</td></tr> +<tr><td><strong>Memories</strong></td><td>Número de memórias na tessera</td></tr> +<tr><td><strong>Size</strong></td><td>Tamanho total (B, KB, MB ou GB)</td></tr> +<tr><td><strong>Visibility</strong></td><td>Nível de visibilidade (public, private ou circle)</td></tr> +</tbody> +</table> +</div> +<h2 id="exemplo-1"><a class="header" href="#exemplo-1">Exemplo</a></h2> +<pre><code class="language-bash">tes list +</code></pre> +<pre><code>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 +</code></pre> +<h2 id="banco-de-dados-vazio"><a class="header" href="#banco-de-dados-vazio">Banco de dados vazio</a></h2> +<p>Se nenhuma tessera foi criada ainda:</p> +<pre><code class="language-bash">tes list +</code></pre> +<pre><code>No tesseras found. +</code></pre> +<div style="break-before: page; page-break-before: always;"></div> +<h1 id="executando-um-no"><a class="header" href="#executando-um-no">Executando um No</a></h1> +<p>O binario <code>tesseras-daemon</code> 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.</p> +<h2 id="iniciando-o-daemon"><a class="header" href="#iniciando-o-daemon">Iniciando o daemon</a></h2> +<pre><code class="language-bash">tesseras-daemon +</code></pre> +<p>Na primeira execucao, o daemon:</p> +<ol> +<li>Cria o diretorio de dados (<code>~/.local/share/tesseras</code> no Linux, <code>~/Library/Application Support/tesseras</code> no macOS)</li> +<li>Gera uma identidade de no com prova de trabalho (leva cerca de 1 segundo)</li> +<li>Abre um listener QUIC em <code>0.0.0.0:4433</code></li> +<li>Faz bootstrap na rede contactando nos semente</li> +<li>Imprime <code>daemon ready</code> quando totalmente operacional</li> +</ol> +<h2 id="opcoes-de-linha-de-comando"><a class="header" href="#opcoes-de-linha-de-comando">Opcoes de linha de comando</a></h2> +<pre><code>tesseras-daemon [OPTIONS] +</code></pre> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Opcao</th><th>Descricao</th><th>Padrao</th></tr> +</thead> +<tbody> +<tr><td><code>-c, --config <PATH></code></td><td>Caminho para um arquivo de configuracao TOML</td><td>Nenhum (usa padroes internos)</td></tr> +<tr><td><code>-l, --listen <ADDR></code></td><td>Endereco e porta para escutar</td><td><code>0.0.0.0:4433</code></td></tr> +<tr><td><code>-b, --bootstrap <ADDRS></code></td><td>Enderecos de bootstrap separados por virgula</td><td><code>boot1.tesseras.net:4433,boot2.tesseras.net:4433</code></td></tr> +<tr><td><code>-d, --data-dir <PATH></code></td><td>Diretorio de dados</td><td>Especifico da plataforma (veja acima)</td></tr> +</tbody> +</table> +</div> +<p>Opcoes CLI sobrescrevem valores do arquivo de configuracao.</p> +<h2 id="exemplos-2"><a class="header" href="#exemplos-2">Exemplos</a></h2> +<p>Executar com padroes (entrar na rede publica):</p> +<pre><code class="language-bash">tesseras-daemon +</code></pre> +<p>Executar como no semente (sem bootstrap, outros nos conectam a voce):</p> +<pre><code class="language-bash">tesseras-daemon --bootstrap "" +</code></pre> +<p>Executar em uma porta personalizada com um diretorio de dados especifico:</p> +<pre><code class="language-bash">tesseras-daemon --listen 0.0.0.0:5000 --data-dir /var/lib/tesseras +</code></pre> +<p>Fazer bootstrap a partir de um no especifico:</p> +<pre><code class="language-bash">tesseras-daemon --bootstrap "192.168.1.50:4433" +</code></pre> +<p>Entrar em uma rede local com multiplos nos:</p> +<pre><code class="language-bash">tesseras-daemon --bootstrap "192.168.1.10:4433,192.168.1.11:4433" +</code></pre> +<h2 id="identidade-do-no"><a class="header" href="#identidade-do-no">Identidade do no</a></h2> +<p>Cada no tem uma identidade unica armazenada em <code><data-dir>/identity.key</code>. Este arquivo contem uma chave publica de 32 bytes e um nonce de prova de trabalho de 8 bytes.</p> +<p>O ID do no e derivado da chave publica: <code>BLAKE3(pubkey || nonce)</code> 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.</p> +<p>A identidade e gerada automaticamente na primeira execucao e reutilizada nas execucoes seguintes. Se voce apagar <code>identity.key</code>, uma nova identidade sera gerada.</p> +<h2 id="logging"><a class="header" href="#logging">Logging</a></h2> +<p>O daemon usa logging estruturado via <code>tracing</code>. Controle o nivel de log com a variavel de ambiente <code>RUST_LOG</code>:</p> +<pre><code class="language-bash"># 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 +</code></pre> +<h2 id="desligamento"><a class="header" href="#desligamento">Desligamento</a></h2> +<p>Pressione <strong>Ctrl+C</strong> para iniciar o desligamento gracioso. O daemon ira:</p> +<ol> +<li>Parar de aceitar novas conexoes</li> +<li>Finalizar operacoes em andamento (ate 5 segundos)</li> +<li>Fechar todas as conexoes QUIC</li> +<li>Sair de forma limpa</li> +</ol> +<h2 id="firewall"><a class="header" href="#firewall">Firewall</a></h2> +<p>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.</p> +<pre><code class="language-bash"># Exemplo: Linux com ufw +sudo ufw allow 4433/udp +</code></pre> +<div style="break-before: page; page-break-before: always;"></div> +<h1 id="configuracao"><a class="header" href="#configuracao">Configuracao</a></h1> +<p>O daemon pode ser configurado via um arquivo TOML. Passe o caminho com <code>--config</code>:</p> +<pre><code class="language-bash">tesseras-daemon --config /etc/tesseras/config.toml +</code></pre> +<p>Se nenhum arquivo de configuracao for fornecido, o daemon usa padroes sensiveis. Opcoes CLI (<code>--listen</code>, <code>--bootstrap</code>, <code>--data-dir</code>) sobrescrevem os valores correspondentes da configuracao.</p> +<h2 id="exemplo-completo"><a class="header" href="#exemplo-completo">Exemplo completo</a></h2> +<pre><code class="language-toml">[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" +</code></pre> +<h2 id="secoes"><a class="header" href="#secoes">Secoes</a></h2> +<h3 id="node"><a class="header" href="#node"><code>[node]</code></a></h3> +<p>Configuracoes basicas do no.</p> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Chave</th><th>Tipo</th><th>Padrao</th><th>Descricao</th></tr> +</thead> +<tbody> +<tr><td><code>data_dir</code></td><td>caminho</td><td>Especifico da plataforma</td><td>Onde armazenar identidade, banco de dados e blobs</td></tr> +<tr><td><code>listen_addr</code></td><td>endereco</td><td><code>0.0.0.0:4433</code></td><td>Endereco do listener QUIC</td></tr> +</tbody> +</table> +</div> +<p>O <code>data_dir</code> padrao e <code>~/.local/share/tesseras</code> no Linux e <code>~/Library/Application Support/tesseras</code> no macOS.</p> +<h3 id="dht"><a class="header" href="#dht"><code>[dht]</code></a></h3> +<p>Parametros de ajuste da DHT Kademlia. Os padroes funcionam bem para a maioria das implantacoes.</p> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Chave</th><th>Tipo</th><th>Padrao</th><th>Descricao</th></tr> +</thead> +<tbody> +<tr><td><code>k</code></td><td>inteiro</td><td><code>20</code></td><td>Maximo de entradas por bucket da tabela de roteamento</td></tr> +<tr><td><code>alpha</code></td><td>inteiro</td><td><code>3</code></td><td>Paralelismo para buscas iterativas</td></tr> +<tr><td><code>bucket_refresh_interval_secs</code></td><td>inteiro</td><td><code>3600</code></td><td>Com que frequencia atualizar buckets da tabela de roteamento (segundos)</td></tr> +<tr><td><code>republish_interval_secs</code></td><td>inteiro</td><td><code>3600</code></td><td>Com que frequencia republicar ponteiros armazenados (segundos)</td></tr> +<tr><td><code>pointer_ttl_secs</code></td><td>inteiro</td><td><code>86400</code></td><td>Quanto tempo manter um ponteiro antes de expirar (segundos)</td></tr> +<tr><td><code>max_stored_pointers</code></td><td>inteiro</td><td><code>100000</code></td><td>Numero maximo de ponteiros armazenados localmente</td></tr> +<tr><td><code>ping_failure_threshold</code></td><td>inteiro</td><td><code>3</code></td><td>Quantas falhas consecutivas de ping antes de remover um par</td></tr> +</tbody> +</table> +</div> +<h3 id="bootstrap"><a class="header" href="#bootstrap"><code>[bootstrap]</code></a></h3> +<p>Como o no descobre seus primeiros pares ao entrar na rede.</p> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Chave</th><th>Tipo</th><th>Padrao</th><th>Descricao</th></tr> +</thead> +<tbody> +<tr><td><code>dns_domain</code></td><td>string</td><td><code>_tesseras._udp.tesseras.net</code></td><td>Dominio DNS para descoberta de pares via registros TXT</td></tr> +<tr><td><code>hardcoded</code></td><td>lista de strings</td><td><code>["boot1.tesseras.net:4433", "boot2.tesseras.net:4433"]</code></td><td>Enderecos de bootstrap de fallback</td></tr> +</tbody> +</table> +</div> +<h3 id="network"><a class="header" href="#network"><code>[network]</code></a></h3> +<p>Funcionalidades de nivel de rede.</p> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Chave</th><th>Tipo</th><th>Padrao</th><th>Descricao</th></tr> +</thead> +<tbody> +<tr><td><code>enable_mdns</code></td><td>booleano</td><td><code>true</code></td><td>Habilitar descoberta na rede local via mDNS</td></tr> +</tbody> +</table> +</div> +<h3 id="observability"><a class="header" href="#observability"><code>[observability]</code></a></h3> +<p>Monitoramento e logging.</p> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Chave</th><th>Tipo</th><th>Padrao</th><th>Descricao</th></tr> +</thead> +<tbody> +<tr><td><code>metrics_addr</code></td><td>endereco</td><td><code>127.0.0.1:9190</code></td><td>Endereco para o endpoint de metricas Prometheus</td></tr> +<tr><td><code>log_format</code></td><td>string</td><td><code>json</code></td><td>Formato de saida de log (<code>json</code> ou <code>text</code>)</td></tr> +</tbody> +</table> +</div> +<h2 id="suporte-a-ipv6"><a class="header" href="#suporte-a-ipv6">Suporte a IPv6</a></h2> +<p>Tesseras suporta IPv6 nativamente. Os campos <code>listen_addr</code> e <code>listen_addrs</code> aceitam tanto enderecos IPv4 quanto IPv6.</p> +<h3 id="escutando-em-ipv6"><a class="header" href="#escutando-em-ipv6">Escutando em IPv6</a></h3> +<p>Para escutar em todas as interfaces IPv6:</p> +<pre><code class="language-toml">[node] +listen_addr = "[::]:4433" +</code></pre> +<p>No Linux e na maioria dos BSDs, vincular a <code>[::]</code> tambem aceita conexoes IPv4 (dual-stack) por padrao. Em alguns sistemas (notavelmente OpenBSD), <code>[::]</code> e somente IPv6 porque <code>IPV6_V6ONLY</code> e habilitado por padrao. Para garantir tanto IPv4 quanto IPv6 em todas as plataformas, use <code>listen_addrs</code> com enderecos explicitos:</p> +<pre><code class="language-toml">[node] +listen_addrs = ["0.0.0.0:4433", "[::]:4433"] +</code></pre> +<p>Para loopback IPv6 apenas (testes):</p> +<pre><code class="language-toml">[node] +listen_addr = "[::1]:4433" +</code></pre> +<h3 id="bootstrap-com-ipv6"><a class="header" href="#bootstrap-com-ipv6">Bootstrap com IPv6</a></h3> +<p>Enderecos de bootstrap podem ser IPv6:</p> +<pre><code class="language-toml">[bootstrap] +hardcoded = [ + "boot1.tesseras.net:4433", + "[2001:db8::1]:4433", +] +</code></pre> +<p>Hostnames DNS com registros A e AAAA sao resolvidos para todos os enderecos, entao o daemon se conectara pelo protocolo que estiver acessivel.</p> +<h3 id="comportamento-de-ipv6_v6only-por-so"><a class="header" href="#comportamento-de-ipv6_v6only-por-so">Comportamento de <code>IPV6_V6ONLY</code> por SO</a></h3> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>SO</th><th><code>[::]</code> aceita IPv4?</th><th>Notas</th></tr> +</thead> +<tbody> +<tr><td>Linux</td><td>Sim (dual-stack)</td><td><code>IPV6_V6ONLY</code> padrao 0</td></tr> +<tr><td>macOS</td><td>Sim (dual-stack)</td><td><code>IPV6_V6ONLY</code> padrao 0</td></tr> +<tr><td>FreeBSD</td><td>Sim (dual-stack)</td><td><code>IPV6_V6ONLY</code> padrao 0</td></tr> +<tr><td>OpenBSD</td><td>Nao (somente IPv6)</td><td><code>IPV6_V6ONLY</code> sempre 1</td></tr> +<tr><td>Windows</td><td>Sim (dual-stack)</td><td><code>IPV6_V6ONLY</code> padrao 0</td></tr> +</tbody> +</table> +</div> +<p>Se precisar de controle explicito, use <code>listen_addrs</code> com um endereco IPv4 e um IPv6.</p> +<h2 id="configuracao-minima"><a class="header" href="#configuracao-minima">Configuracao minima</a></h2> +<p>A maioria dos usuarios nao precisa de um arquivo de configuracao. Se precisar, uma configuracao minima sobrescrevendo apenas o necessario e suficiente:</p> +<pre><code class="language-toml">[node] +listen_addr = "0.0.0.0:5000" + +[bootstrap] +hardcoded = ["192.168.1.10:4433"] +</code></pre> +<p>Todos os outros valores usam seus padroes.</p> +<div style="break-before: page; page-break-before: always;"></div> +<h1 id="conceitos-de-rede"><a class="header" href="#conceitos-de-rede">Conceitos de Rede</a></h1> +<p>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.</p> +<h2 id="como-os-nos-se-encontram"><a class="header" href="#como-os-nos-se-encontram">Como os nos se encontram</a></h2> +<p>Tesseras usa uma <strong>tabela hash distribuida (DHT) Kademlia</strong> — 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.</p> +<p>Quando seu no inicia, ele contacta um ou mais <strong>nos de bootstrap</strong> (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.</p> +<h2 id="o-que-a-dht-armazena"><a class="header" href="#o-que-a-dht-armazena">O que a DHT armazena</a></h2> +<p>A DHT armazena <strong>ponteiros</strong>, 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.</p> +<p>Isso significa que a DHT permanece pequena e rapida — ela rastreia apenas quem tem o que, nao o conteudo em si.</p> +<h2 id="identidade-do-no-e-prova-de-trabalho"><a class="header" href="#identidade-do-no-e-prova-de-trabalho">Identidade do no e prova de trabalho</a></h2> +<p>Cada no tem um <strong>ID de no</strong> de 160 bits derivado de sua chave publica. Para evitar que um atacante crie milhares de nos falsos de forma barata (um <strong>ataque Sybil</strong>), gerar um ID de no requer uma pequena prova de trabalho: o no deve encontrar um nonce tal que <code>BLAKE3(chave_publica || nonce)</code> comece com 8 bits zero.</p> +<p>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.</p> +<h2 id="distancia-xor"><a class="header" href="#distancia-xor">Distancia XOR</a></h2> +<p>Kademlia define “proximidade” entre nos usando a <strong>metrica XOR</strong>: 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.</p> +<p>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 <strong>busca iterativa</strong> tipicamente alcanca qualquer no na rede em poucos saltos.</p> +<h2 id="transporte-quic"><a class="header" href="#transporte-quic">Transporte: QUIC</a></h2> +<p>Toda comunicacao entre nos usa <strong>QUIC</strong>, um protocolo de transporte moderno construido sobre UDP. O QUIC oferece:</p> +<ul> +<li><strong>Criptografia integrada</strong> — cada conexao usa TLS 1.3</li> +<li><strong>Amigavel a NAT</strong> — funciona atraves da maioria dos tradutores de endereco de rede por ser baseado em UDP</li> +<li><strong>Multiplexacao</strong> — multiplas operacoes independentes sobre uma conexao sem bloqueio head-of-line</li> +<li><strong>Migracao de conexao</strong> — sobrevive a mudancas de rede (ex: trocar de Wi-Fi para dados moveis)</li> +</ul> +<p>O daemon escuta na porta UDP <strong>4433</strong> por padrao.</p> +<h2 id="processo-de-bootstrap"><a class="header" href="#processo-de-bootstrap">Processo de bootstrap</a></h2> +<p>Quando um no inicia, ele segue esta sequencia:</p> +<ol> +<li><strong>Contactar nos semente</strong> — conectar a um ou mais enderecos de bootstrap conhecidos</li> +<li><strong>Trocar pings</strong> — verificar que o semente esta vivo e trocar identidades de no</li> +<li><strong>Auto-busca</strong> — perguntar ao semente por nos proximos ao seu proprio ID, para popular sua tabela de roteamento</li> +<li><strong>Descoberta iterativa</strong> — contactar os nos recem-descobertos, que apontam para ainda mais pares</li> +</ol> +<p>Apos o bootstrap, o no mantem sua tabela de roteamento automaticamente: ele atualiza buckets periodicamente e substitui pares nao responsivos por novos.</p> +<h2 id="tipos-de-no"><a class="header" href="#tipos-de-no">Tipos de no</a></h2> +<p>Nem todo dispositivo participa da rede da mesma forma:</p> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Tipo</th><th>Descricao</th><th>Sempre ligado?</th></tr> +</thead> +<tbody> +<tr><td><strong>No completo</strong></td><td>Desktop, servidor ou Raspberry Pi executando <code>tesseras-daemon</code>. Participa plenamente da DHT e armazena dados de outros nos.</td><td>Sim</td></tr> +<tr><td><strong>No movel</strong></td><td>Celular ou tablet executando o app Tesseras. Participa da DHT quando o app esta ativo.</td><td>Nao</td></tr> +<tr><td><strong>No navegador</strong></td><td>Navegador web executando o cliente WASM. Conecta via um no relay. Somente leitura.</td><td>Nao</td></tr> +<tr><td><strong>No IoT</strong></td><td>ESP32 ou dispositivo similar na rede local. Armazena fragmentos passivamente, nao participa da DHT.</td><td>Sim</td></tr> +</tbody> +</table> +</div> +<p>O daemon de no completo e a espinha dorsal da rede. Quanto mais nos completos em execucao, mais resiliente a rede se torna.</p> +<div style="break-before: page; page-break-before: always;"></div> +<h1 id="replicação-e-reparo"><a class="header" href="#replicação-e-reparo">Replicação e Reparo</a></h1> +<p>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.</p> +<h2 id="por-que-a-replicação-importa"><a class="header" href="#por-que-a-replicação-importa">Por que a replicação importa</a></h2> +<p>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.</p> +<h2 id="codificação-de-apagamento"><a class="header" href="#codificação-de-apagamento">Codificação de apagamento</a></h2> +<p>O Tesseras usa <strong>codificação de apagamento Reed-Solomon</strong> 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.</p> +<p>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.</p> +<h2 id="camadas-de-fragmentação"><a class="header" href="#camadas-de-fragmentação">Camadas de fragmentação</a></h2> +<p>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:</p> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Camada</th><th>Tamanho</th><th>Estratégia</th><th>Fragmentos</th></tr> +</thead> +<tbody> +<tr><td><strong>Small</strong></td><td>< 4 MB</td><td>Replicação do arquivo inteiro</td><td>7 cópias do arquivo completo</td></tr> +<tr><td><strong>Medium</strong></td><td>4–256 MB</td><td>Reed-Solomon 16+8</td><td>16 dados + 8 paridade = 24 fragmentos</td></tr> +<tr><td><strong>Large</strong></td><td>≥ 256 MB</td><td>Reed-Solomon 48+24</td><td>48 dados + 24 paridade = 72 fragmentos</td></tr> +</tbody> +</table> +</div> +<p>Todas as camadas visam um <strong>fator de replicação de 7</strong> — significando que os fragmentos são distribuídos para 7 pares diferentes.</p> +<h2 id="como-a-distribuição-funciona"><a class="header" href="#como-a-distribuição-funciona">Como a distribuição funciona</a></h2> +<p>Quando você cria uma tessera e o daemon a replica, isto é o que acontece:</p> +<ol> +<li><strong>Codificar</strong> — os dados da tessera são divididos em fragmentos de acordo com sua camada de tamanho</li> +<li><strong>Encontrar pares</strong> — o daemon consulta a DHT pelos nós mais próximos ao hash da tessera</li> +<li><strong>Diversidade de sub-rede</strong> — os pares são filtrados para que poucos venham da mesma sub-rede (para evitar falhas correlacionadas se um datacenter cair)</li> +<li><strong>Distribuir</strong> — os fragmentos são enviados aos pares selecionados em ordem round-robin</li> +<li><strong>Confirmar</strong> — cada par valida o checksum do fragmento e confirma o recebimento</li> +</ol> +<p>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.</p> +<h2 id="verificação-de-fragmentos"><a class="header" href="#verificação-de-fragmentos">Verificação de fragmentos</a></h2> +<p>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.</p> +<p>Os fragmentos sao armazenados em um <strong>armazenamento enderecavel por conteudo (CAS)</strong> 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.</p> +<h2 id="loop-de-reparo"><a class="header" href="#loop-de-reparo">Loop de reparo</a></h2> +<p>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:</p> +<ol> +<li><strong>Solicita atestações</strong> dos detentores conhecidos — cada detentor prova que ainda possui os fragmentos reportando seus checksums</li> +<li><strong>Recorre ao ping</strong> se a atestação falhar — para distinguir entre “nó está offline” e “nó perdeu os dados”</li> +<li><strong>Verifica fragmentos locais</strong> — verifica a integridade de quaisquer fragmentos armazenados localmente recalculando checksums BLAKE3</li> +<li><strong>Decide a ação</strong>: +<ul> +<li><strong>Healthy</strong> — todos os detentores responderam, todos os checksums válidos, nada a fazer</li> +<li><strong>Needs replication</strong> — alguns detentores sumiram, encontrar novos pares e redistribuir fragmentos ausentes</li> +<li><strong>Corrupt local</strong> — um fragmento local tem dados corrompidos, buscar uma substituição na rede</li> +</ul> +</li> +</ol> +<h2 id="reciprocidade"><a class="header" href="#reciprocidade">Reciprocidade</a></h2> +<p>O Tesseras usa um <strong>livro-razão de reciprocidade bilateral</strong> 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:</p> +<pre><code>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) +</code></pre> +<p>As regras são simples:</p> +<ul> +<li>Armazene 1 GB na rede → você deveria armazenar aproximadamente 1 GB para outros</li> +<li>Nós com saldo positivo (eles armazenam mais para você) recebem prioridade quando você precisa distribuir novos fragmentos</li> +<li>Free riders perdem redundância gradualmente — seus fragmentos são despriorizados para reparo, mas nunca deletados</li> +<li>Ao receber um fragmento, um nó verifica o déficit do remetente. Se o remetente deve muito armazenamento, o fragmento é rejeitado</li> +<li>Nós institucionais (universidades, arquivos) podem operar altruisticamente com proporções desequilibradas</li> +</ul> +<h2 id="tamanho-máximo-de-tessera"><a class="header" href="#tamanho-máximo-de-tessera">Tamanho máximo de tessera</a></h2> +<p>O tamanho máximo de uma tessera é <strong>1 GB</strong>. 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.</p> +<h2 id="configuração"><a class="header" href="#configuração">Configuração</a></h2> +<p>O comportamento de replicação do daemon pode ser ajustado através da configuração:</p> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Parâmetro</th><th>Padrão</th><th>Descrição</th></tr> +</thead> +<tbody> +<tr><td>Intervalo de reparo</td><td>24 horas</td><td>Com que frequência o loop de reparo roda</td></tr> +<tr><td>Jitter de reparo</td><td>2 horas</td><td>Atraso aleatório adicionado para evitar tempestades na rede</td></tr> +<tr><td>Transferências simultâneas</td><td>4</td><td>Máximo de transferências paralelas de fragmentos</td></tr> +<tr><td>Espaço livre mínimo</td><td>1 GB</td><td>Parar de aceitar fragmentos abaixo deste limite</td></tr> +<tr><td>Tolerância de déficit</td><td>256 MB</td><td>Déficit máximo de armazenamento antes de rejeitar fragmentos de um par</td></tr> +<tr><td>Limite por par</td><td>1 GB</td><td>Armazenamento total máximo para qualquer par individual</td></tr> +</tbody> +</table> +</div> +<div style="break-before: page; page-break-before: always;"></div> +<h1 id="criptografia-e-tesseras-seladas"><a class="header" href="#criptografia-e-tesseras-seladas">Criptografia e Tesseras Seladas</a></h1> +<p>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:</p> +<ul> +<li><strong>Privada</strong> — apenas o criador (e seus herdeiros) podem acessar o conteúdo</li> +<li><strong>Selada</strong> — o conteúdo é bloqueado por tempo e se torna acessível após uma data específica</li> +</ul> +<p>Tesseras públicas nunca são criptografadas. Disponibilidade é mais importante que sigilo para preservação.</p> +<h2 id="como-a-criptografia-funciona"><a class="header" href="#como-a-criptografia-funciona">Como a criptografia funciona</a></h2> +<p>Quando você cria uma tessera privada ou selada, o seguinte acontece:</p> +<ol> +<li>Uma <strong>chave de conteúdo</strong> aleatória (256 bits) é gerada</li> +<li>Cada arquivo de memória é criptografado com <strong>AES-256-GCM</strong> usando essa chave de conteúdo</li> +<li>A chave de conteúdo é envolvida em um <strong>envelope de chave selada</strong> usando sua chave pública de criptografia</li> +<li>A chave envolvida é armazenada junto ao conteúdo criptografado</li> +</ol> +<p>Apenas o detentor da chave privada correspondente pode desembrulhar a chave de conteúdo e decriptar o conteúdo.</p> +<h2 id="encapsulamento-de-chave-híbrido-pós-quântico"><a class="header" href="#encapsulamento-de-chave-híbrido-pós-quântico">Encapsulamento de chave híbrido pós-quântico</a></h2> +<p>O envelope de chave selada usa um <strong>Mecanismo de Encapsulamento de Chave (KEM) híbrido</strong> combinando dois algoritmos:</p> +<ul> +<li><strong>X25519</strong> — uma troca de chaves clássica bem testada baseada em curva elíptica</li> +<li><strong>ML-KEM-768</strong> — um KEM pós-quântico baseado em reticulados padronizado pelo NIST (anteriormente Kyber)</li> +</ul> +<p>Ambos os algoritmos produzem segredos compartilhados que são combinados usando derivação de chaves BLAKE3. Um atacante precisa quebrar <strong>ambos</strong> 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.</p> +<h2 id="dados-autenticados-associados-aad"><a class="header" href="#dados-autenticados-associados-aad">Dados autenticados associados (AAD)</a></h2> +<p>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:</p> +<ul> +<li>O <strong>hash do conteúdo</strong> da tessera (sempre)</li> +<li>O <strong>timestamp open_after</strong> (apenas para tesseras seladas)</li> +</ul> +<p>Isso previne <strong>ataques de troca de texto cifrado</strong>: 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.</p> +<h2 id="tesseras-seladas-cápsulas-do-tempo"><a class="header" href="#tesseras-seladas-cápsulas-do-tempo">Tesseras seladas: cápsulas do tempo</a></h2> +<p>Uma tessera selada é uma verdadeira cápsula do tempo. Quando você cria uma, você especifica uma data <code>open_after</code>. O conteúdo é criptografado e a chave é selada em um envelope que apenas você pode abrir.</p> +<p>Quando a data <code>open_after</code> passa, o dono publica a chave de conteúdo como uma <strong>Publicação de Chave</strong> 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.</p> +<p>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.</p> +<h2 id="e-as-chaves"><a class="header" href="#e-as-chaves">E as chaves?</a></h2> +<p>Cada identidade agora inclui um <strong>par de chaves de criptografia</strong> junto ao par de chaves de assinatura:</p> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Tipo de chave</th><th>Algoritmo</th><th>Finalidade</th></tr> +</thead> +<tbody> +<tr><td>Ed25519</td><td>Clássico</td><td>Assinatura de manifestos e publicações de chave</td></tr> +<tr><td>ML-DSA</td><td>Pós-quântico</td><td>Assinatura (quando habilitado)</td></tr> +<tr><td>X25519</td><td>Clássico</td><td>Encapsulamento de chave (criptografia)</td></tr> +<tr><td>ML-KEM-768</td><td>Pós-quântico</td><td>Encapsulamento de chave (criptografia)</td></tr> +</tbody> +</table> +</div> +<p>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.</p> +<h2 id="princípios-de-design"><a class="header" href="#princípios-de-design">Princípios de design</a></h2> +<ul> +<li><strong>Criptografar o mínimo possível</strong> — apenas conteúdo privado e selado é criptografado. Memórias públicas permanecem abertas para acessibilidade a longo prazo.</li> +<li><strong>Algoritmos duplos desde o início</strong> — criptografia clássica e pós-quântica, para que o conteúdo esteja protegido mesmo que um algoritmo seja quebrado.</li> +<li><strong>Manifestos imutáveis</strong> — chaves são publicadas separadamente, nunca modificando dados existentes.</li> +<li><strong>Falhar fechado</strong> — o sistema rejeita tentativas de criar tesseras privadas ou seladas sem chaves de criptografia.</li> +</ul> +<div style="break-before: page; page-break-before: always;"></div> +<h1 id="recuperação-de-chaves-por-herdeiros"><a class="header" href="#recuperação-de-chaves-por-herdeiros">Recuperação de Chaves por Herdeiros</a></h1> +<p>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 <strong>Shamir’s Secret Sharing</strong> para permitir que você distribua sua identidade criptográfica para herdeiros de confiança.</p> +<h2 id="como-funciona"><a class="header" href="#como-funciona">Como funciona</a></h2> +<p>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 <strong>nada</strong> — isso é informação-teoricamente seguro, não apenas computacionalmente difícil de quebrar.</p> +<p>Por exemplo, com limiar 2 e 3 fragmentos totais:</p> +<ul> +<li>Dê o fragmento 1 ao seu cônjuge</li> +<li>Dê o fragmento 2 ao seu irmão</li> +<li>Dê o fragmento 3 ao seu advogado</li> +</ul> +<p>Quaisquer dois deles podem recuperar sua identidade. Um único fragmento sozinho é inútil.</p> +<h2 id="criando-fragmentos-de-herdeiros"><a class="header" href="#criando-fragmentos-de-herdeiros">Criando fragmentos de herdeiros</a></h2> +<pre><code class="language-bash">tes heir create --threshold 2 --shares 3 +</code></pre> +<p>Isso divide sua chave de identidade Ed25519 em 3 fragmentos (necessitando 2 para reconstruir) e os salva em <code>./heir-shares/</code>:</p> +<pre><code>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 +</code></pre> +<p>Cada fragmento é gerado em dois formatos:</p> +<ul> +<li><strong>Binário</strong> (<code>.bin</code>) — MessagePack compacto, adequado para pendrives ou armazenamento digital</li> +<li><strong>Texto</strong> (<code>.txt</code>) — base64 com cabeçalho legível, adequado para impressão em papel</li> +</ul> +<p>O formato texto se parece com isso:</p> +<pre><code>--- 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 --- +</code></pre> +<h2 id="reconstruindo-a-partir-de-fragmentos"><a class="header" href="#reconstruindo-a-partir-de-fragmentos">Reconstruindo a partir de fragmentos</a></h2> +<p>Quando os herdeiros precisam recuperar a identidade:</p> +<pre><code class="language-bash">tes heir reconstruct heir_share_1.txt heir_share_2.bin --output-dir ./recovered-keys +</code></pre> +<p>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.</p> +<p>Para instalar as chaves recuperadas como identidade ativa:</p> +<pre><code class="language-bash">tes heir reconstruct share1.txt share2.txt --output-dir ./recovered --install +</code></pre> +<p>Isso faz backup da identidade atual antes de substituí-la.</p> +<h2 id="inspecionando-um-fragmento"><a class="header" href="#inspecionando-um-fragmento">Inspecionando um fragmento</a></h2> +<p>Para ver metadados sobre um fragmento sem expor dados secretos:</p> +<pre><code class="language-bash">tes heir info heir_share_1.txt +</code></pre> +<p>Saída:</p> +<pre><code>Heir Share Information: + Format version: 1 + Share: 1 of 3 (threshold: 2) + Session: 9f8e7d6c5b4a3210 + Owner fingerprint: a1b2c3d4e5f6a7b8 + Share data size: 34 bytes + Checksum: valid +</code></pre> +<h2 id="considerações-de-segurança"><a class="header" href="#considerações-de-segurança">Considerações de segurança</a></h2> +<ul> +<li><strong>Escolha do limiar</strong>: 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.</li> +<li><strong>Armazenamento físico</strong>: imprima os arquivos <code>.txt</code> em papel livre de ácido e armazene em locais físicos separados (cofres bancários, casas diferentes). Papel sobrevive décadas sem degradação.</li> +<li><strong>Nunca armazene fragmentos juntos</strong>: todo o propósito da divisão é a distribuição. Manter todos os fragmentos em um lugar anula o objetivo.</li> +<li><strong>Isolamento de sessão</strong>: cada chamada <code>heir create</code> 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.</li> +<li><strong>Verificação de checksum</strong>: 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.</li> +<li><strong>Re-dividir após mudanças de chave</strong>: se você regenerar sua identidade, crie novos fragmentos de herdeiros e destrua com segurança os antigos.</li> +</ul> +<h2 id="princípios-de-design-1"><a class="header" href="#princípios-de-design-1">Princípios de design</a></h2> +<ul> +<li><strong>Segurança informação-teórica</strong> — T-1 fragmentos revelam exatamente zero informação sobre o segredo. Isso não é uma suposição computacional; é matematicamente provado.</li> +<li><strong>Detecção de corrupção</strong> — checksums BLAKE3 detectam degradação de bits, erros de OCR e truncamento antes de qualquer tentativa de reconstrução.</li> +<li><strong>Resiliência de formato</strong> — saída dupla (binário + texto) garante que fragmentos sobrevivam a diferentes modos de falha de mídia de armazenamento.</li> +<li><strong>Compatibilidade retroativa</strong> — o blob do segredo é versionado, para que versões futuras possam incluir material de chave adicional sem quebrar fragmentos existentes.</li> +</ul> +<div style="break-before: page; page-break-before: always;"></div> +<h1 id="travessia-de-nat"><a class="header" href="#travessia-de-nat">Travessia de NAT</a></h1> +<p>A maioria dos dispositivos na internet ficam atras de um <strong>NAT</strong> (Network Address Translator). Seu roteador atribui ao seu dispositivo um endereco privado (como <code>192.168.1.100</code>) 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.</p> +<p>Tesseras resolve isso com uma abordagem em tres camadas, tentando a opcao mais barata primeiro:</p> +<ol> +<li><strong>Conexao direta</strong> — se ambos os nos tem IPs publicos, eles conectam diretamente</li> +<li><strong>UDP hole punching</strong> — um terceiro no apresenta os dois peers para que eles possam furar seus NATs</li> +<li><strong>Relay</strong> — um no com IP publico encaminha pacotes entre os dois peers</li> +</ol> +<h2 id="descoberta-do-tipo-de-nat"><a class="header" href="#descoberta-do-tipo-de-nat">Descoberta do tipo de NAT</a></h2> +<p>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:</p> +<div class="table-wrapper"> +<table> +<thead> +<tr><th>Tipo de NAT</th><th>O que significa</th><th>Hole punching?</th></tr> +</thead> +<tbody> +<tr><td><strong>Public</strong></td><td>Sem NAT — seu dispositivo tem IP publico</td><td>Nao necessario</td></tr> +<tr><td><strong>Cone</strong></td><td>NAT mapeia a mesma porta interna para a mesma porta externa independente do destino</td><td>Funciona bem (~80%)</td></tr> +<tr><td><strong>Symmetric</strong></td><td>NAT atribui uma porta externa diferente para cada destino</td><td>Nao confiavel</td></tr> +<tr><td><strong>Unknown</strong></td><td>Nao conseguiu alcancar servidores STUN</td><td>Relay necessario</td></tr> +</tbody> +</table> +</div> +<p>Seu no divulga seu tipo de NAT em mensagens Pong do DHT, para que outros nos saibam se hole punching vale a pena tentar.</p> +<h2 id="hole-punching"><a class="header" href="#hole-punching">Hole punching</a></h2> +<p>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:</p> +<ol> +<li> +<p>A envia uma mensagem <strong>PunchIntro</strong> 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.</p> +</li> +<li> +<p>I verifica a assinatura e encaminha um <strong>PunchRequest</strong> a B, incluindo o endereco de A e a assinatura original.</p> +</li> +<li> +<p>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 <strong>PunchReady</strong> de volta a A com o endereco externo de B.</p> +</li> +<li> +<p>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.</p> +</li> +</ol> +<p>O processo inteiro leva 2-5 segundos. As assinaturas Ed25519 previnem <strong>ataques de reflexao</strong>, onde um atacante reproduz uma introducao antiga para redirecionar trafego.</p> +<h2 id="fallback-por-relay"><a class="header" href="#fallback-por-relay">Fallback por relay</a></h2> +<p>Quando hole punching falha (NAT Symmetric, firewalls estritos ou redes corporativas), nos usam relay atraves de um no com IP publico:</p> +<ol> +<li>A envia um <strong>RelayRequest</strong> ao no R (um no com IP publico com relay habilitado).</li> +<li>R cria uma sessao e envia um <strong>RelayOffer</strong> a ambos A e B, contendo o endereco do relay e um token de sessao.</li> +<li>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.</li> +</ol> +<p>Sessoes de relay tem limites de largura de banda:</p> +<ul> +<li><strong>256 KB/s</strong> para peers com boa reciprocidade (eles armazenam fragmentos para outros)</li> +<li><strong>64 KB/s</strong> para peers sem reciprocidade</li> +<li>Sessoes nao reciprocas sao limitadas a 10 minutos</li> +</ul> +<p>Isso incentiva nos a contribuir armazenamento — bons cidadaos da rede recebem melhor servico de relay.</p> +<h2 id="migracao-de-endereco"><a class="header" href="#migracao-de-endereco">Migracao de endereco</a></h2> +<p>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 <strong>RelayMigrate</strong> assinada para atualizar seu endereco na sessao existente. Isso evita reestabelecer conexoes do zero.</p> +<h2 id="configuracao-1"><a class="header" href="#configuracao-1">Configuracao</a></h2> +<p>A secao <code>[nat]</code> na configuracao do daemon controla a travessia de NAT:</p> +<pre><code class="language-toml">[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 +</code></pre> +<p>Para executar um no relay, defina <code>relay_enabled = true</code>. Seu no deve ter um IP publico (ou roteador com port forwarding) para servir como relay.</p> +<h2 id="reconexao-mobile"><a class="header" href="#reconexao-mobile">Reconexao mobile</a></h2> +<p>Quando o app Tesseras detecta uma mudanca de rede em um dispositivo movel, ele executa uma sequencia de reconexao em tres fases:</p> +<ol> +<li><strong>Migracao QUIC</strong> (0-2s) — QUIC suporta migracao de conexao nativamente. O app tenta migrar todas as conexoes ativas para o novo endereco.</li> +<li><strong>Re-STUN</strong> (2-5s) — descobre o novo endereco externo e re-anuncia ao DHT.</li> +<li><strong>Reestabelecimento</strong> (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.</li> +</ol> +<p>O app mostra progresso de reconexao atraves do stream de eventos <code>NetworkChanged</code>.</p> +<h2 id="monitoramento"><a class="header" href="#monitoramento">Monitoramento</a></h2> +<p>A travessia de NAT expoe metricas Prometheus em <code>/metrics</code>:</p> +<ul> +<li><code>tesseras_nat_type</code> — tipo de NAT detectado atualmente</li> +<li><code>tesseras_stun_requests_total</code> / <code>tesseras_stun_failures_total</code> — confiabilidade STUN</li> +<li><code>tesseras_punch_attempts_total{initiator_nat, target_nat}</code> — taxa de sucesso de punch por par de NAT</li> +<li><code>tesseras_relay_sessions_active</code> — carga atual de relay</li> +<li><code>tesseras_relay_bytes_forwarded</code> — largura de banda total de relay</li> +<li><code>tesseras_network_change_total</code> — frequencia de mudanca de rede no mobile</li> +</ul> +<div style="break-before: page; page-break-before: always;"></div> +<h1 id="docker"><a class="header" href="#docker">Docker</a></h1> +<p>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.</p> +<h2 id="construindo-a-imagem"><a class="header" href="#construindo-a-imagem">Construindo a imagem</a></h2> +<p>A partir da raiz do repositorio:</p> +<pre><code class="language-bash">docker build -t tesseras-daemon . +</code></pre> +<p>O Dockerfile multi-estagio usa <code>rust:1.85</code> para compilar e <code>debian:bookworm-slim</code> como base de execucao. A imagem resultante e pequena e contem apenas o binario do daemon e certificados CA.</p> +<h2 id="executando-um-unico-no"><a class="header" href="#executando-um-unico-no">Executando um unico no</a></h2> +<pre><code class="language-bash">docker run -d \ + --name tesseras \ + -p 4433:4433/udp \ + tesseras-daemon +</code></pre> +<p>Isso inicia um no que:</p> +<ul> +<li>Escuta na porta UDP 4433</li> +<li>Faz bootstrap a partir dos nos semente padrao</li> +<li>Armazena dados dentro do conteiner (efemero)</li> +</ul> +<p>Para persistir dados entre reinicializacoes do conteiner, monte um volume:</p> +<pre><code class="language-bash">docker run -d \ + --name tesseras \ + -p 4433:4433/udp \ + -v tesseras-data:/root/.local/share/tesseras \ + tesseras-daemon +</code></pre> +<h2 id="executando-como-no-semente"><a class="header" href="#executando-como-no-semente">Executando como no semente</a></h2> +<p>Para executar um no semente que nao faz bootstrap de ninguem:</p> +<pre><code class="language-bash">docker run -d \ + --name tesseras-seed \ + -p 4433:4433/udp \ + tesseras-daemon --listen 0.0.0.0:4433 --bootstrap "" +</code></pre> +<h2 id="rede-multi-no-com-docker-compose"><a class="header" href="#rede-multi-no-com-docker-compose">Rede multi-no com Docker Compose</a></h2> +<p>O repositorio inclui um arquivo Docker Compose para testar uma rede de 3 nos:</p> +<pre><code class="language-yaml">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] +</code></pre> +<p>Iniciar a rede:</p> +<pre><code class="language-bash">cd tests/smoke +docker compose up --build -d +</code></pre> +<p>Verificar que todos os nos estao executando:</p> +<pre><code class="language-bash">docker compose logs --tail=5 +</code></pre> +<p>Voce devera ver <code>daemon ready</code> nos logs de cada no, e <code>bootstrap successful</code> para <code>boot2</code> e <code>client</code>.</p> +<p>Parar a rede:</p> +<pre><code class="language-bash">docker compose down +</code></pre> +<h2 id="configuracao-personalizada"><a class="header" href="#configuracao-personalizada">Configuracao personalizada</a></h2> +<p>Para usar um arquivo de configuracao com Docker, monte-o no conteiner:</p> +<pre><code class="language-bash">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 +</code></pre> +<p>Veja o capitulo <a href="#configuracao">Configuracao</a> para todas as opcoes disponiveis.</p> + + </main> + + <nav class="nav-wrapper" aria-label="Page navigation"> + <!-- Mobile navigation buttons --> + + + <div style="clear: both"></div> + </nav> + </div> + </div> + + <nav class="nav-wide-wrapper" aria-label="Page navigation"> + + </nav> + + </div> + + <template id=fa-eye><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"/></svg></span></template> + <template id=fa-eye-slash><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z"/></svg></span></template> + <template id=fa-copy><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M502.6 70.63l-61.25-61.25C435.4 3.371 427.2 0 418.7 0H255.1c-35.35 0-64 28.66-64 64l.0195 256C192 355.4 220.7 384 256 384h192c35.2 0 64-28.8 64-64V93.25C512 84.77 508.6 76.63 502.6 70.63zM464 320c0 8.836-7.164 16-16 16H255.1c-8.838 0-16-7.164-16-16L239.1 64.13c0-8.836 7.164-16 16-16h128L384 96c0 17.67 14.33 32 32 32h47.1V320zM272 448c0 8.836-7.164 16-16 16H63.1c-8.838 0-16-7.164-16-16L47.98 192.1c0-8.836 7.164-16 16-16H160V128H63.99c-35.35 0-64 28.65-64 64l.0098 256C.002 483.3 28.66 512 64 512h192c35.2 0 64-28.8 64-64v-32h-47.1L272 448z"/></svg></span></template> + <template id=fa-play><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg></span></template> + <template id=fa-clock-rotate-left><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M75 75L41 41C25.9 25.9 0 36.6 0 57.9V168c0 13.3 10.7 24 24 24H134.1c21.4 0 32.1-25.9 17-41l-30.8-30.8C155 85.5 203 64 256 64c106 0 192 86 192 192s-86 192-192 192c-40.8 0-78.6-12.7-109.7-34.4c-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6C151.2 495 201.7 512 256 512c141.4 0 256-114.6 256-256S397.4 0 256 0C185.3 0 121.3 28.7 75 75zm181 53c-13.3 0-24 10.7-24 24V256c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65V152c0-13.3-10.7-24-24-24z"/></svg></span></template> + + + + <script> + window.playground_copyable = true; + </script> + + + <script src="elasticlunr-ef4e11c1.min.js"></script> + <script src="mark-09e88c2c.min.js"></script> + <script src="searcher-c2a407aa.js"></script> + + <script src="clipboard-1626706a.min.js"></script> + <script src="highlight-abc7f01d.js"></script> + <script src="book-a0b12cfe.js"></script> + + <!-- Custom JS scripts --> + + <script> + window.addEventListener('load', function() { + window.setTimeout(window.print, 100); + }); + </script> + + + </div> + </body> +</html> |