Parte II — Tronco e raízes

04 · As raízes: debaixo da terra

Este saber ainda não tem versão em SITEMAP.XML — mostrando em Português.

Os baobás de uma mesma região se tocam pelas raízes. Uma mucua não precisa de internet para encontrar outra: basta que exista um caminho — um cabo, uma antena, uma rede Tor, ou alguém andando com um pendrive.

Raízes que se tocam: SSH, Tor, rede local e sneakernet

Arquivos para ler

  1. infra/sankofa.py — a fachada; infra/sync/__init__.py escolhe o backend.
  2. infra/sync/base.py e infra/sync/gitannex.py — os comandos git/annex de verdade (_exec mata a árvore de processos no timeout).
  3. services/links.py — os remotes de cada galáxia, o retry pelo Tor, a redescoberta do .onion, a autorização por chave SSH.
  4. services/sincronizacao.py — sincronizar_galaxia, o coração do sync.
  5. infra/merge_bbx.py — o driver de merge semântico dos .bbx.
  6. services/pedidos.py — o store-and-forward (pedidos e rotas).
  7. infra/farol_lan.py, infra/tor_service.py, infra/ssh_keys.py.
  8. services/propagacao.py — a identidade local espelhada em toda galáxia.

Git para o texto, git-annex para o resto

  • saber.bbx e textos pequenos vão para o git (história completa, merge).
  • Fotos, vídeos, PDFs, legendas .vtt, textos de OCR vão para o annex: no git fica só um link com a chave (SHA256E-s…--hash.ext). Cada mucua decide o que quer ter (wanted) e pode pedir o resto depois. O padrão (DEFAULT_WANTED) traz sempre os anexos de evento, mocambola e território, e os de artigo e coleção até 20 MB — as imagens do texto e as capas; uma galáxia com o padrão antigo é atualizada sozinha na sincronização (garantir_wanted).
  • A regra fica em GITATTRIBUTES (infra/sync/base.py): *.ocr.txt annex.largefiles=anything vence *.txt annex.largefiles=nothing.
  • As mesmas regras vão também no .git/info/attributes LOCAL (vence o versionado e se refaz a cada sync), com um limite: texto só vai para o git até 2 MB (LIMITE_TEXTO_GIT) — um .json de 300 MB vai para o annex. Sem git-annex no PATH, o arquivo pesado fica fora do commit em vez de entrar cru no git. O que já entrou errado: bbx annex-reparar (ou Cuidar › Arquivos pesados no git), em services/annex_reparo.py.

Um ciclo de sincronização

sequenceDiagram
    autonumber
    participant T as timer / POST /sync
    participant S as sincronizar_galaxia
    participant L as links
    participant G as Sankofa (git-annex)
    participant M as merge_bbx (driver)
    participant R as SaberRepository
    T->>S: galáxia X
    S->>S: TravaGalaxia (flock) — já em andamento? sai
    S->>L: garantir_annex_uuids (remote ↔ uuid da mucua)
    alt algum link é .onion
        S->>S: _garantir_tor_local (sobe o Tor próprio)
    end
    loop cada link habilitado
        S->>G: git annex sync --remote=link (com retry se Tor)
        G->>M: conflito em *.bbx → merge campo a campo
        alt falhou e é .onion
            S->>L: redescobrir_onion (pergunta o endereço novo)
            S->>G: tenta de novo
        end
    end
    S->>G: resolver_conflitos (aborta merge pendente e reporta)
    S->>R: reindex(incremental=True)
    opt data=true
        S->>R: refrescar_presenca()
    end
    S->>S: atualizar_estado (limpa caches)

O merge que não quebra

Um conflito textual num saber.bbx deixaria o YAML inválido, o saber sumiria do índice e a galáxia pararia de sincronizar. O driver merge_bbx.py mescla campo a campo: dicionários por chave, listas de anexos por filename, escalares pelo updated mais recente, e o corpo markdown com git merge-file. Ele é simétrico: rodado na mucua A ou na B, gera os mesmos bytes — é isso que faz a rede convergir.

garantir_driver_merge grava a regra em .git/info/attributes (local, não versionada) antes de cada sync: auto-cura quando o caminho do Python muda.

Pedidos: o conteúdo caminha de mão em mão

stateDiagram-v2
    [*] --> ativo: criar_pedido (buscar | enviar)
    ativo --> ativo: ciclo sem conseguir (TTL não muda)
    ativo --> ativo: obteve a cópia e NÃO sou o alvo → copy --to próximo salto
    ativo --> [*]: sou o alvo e recebi → pedido REMOVIDO
    ativo --> expirado: ttl ≤ 0 (saltos) ou velho demais
    ativo --> cancelado: cancelar_pedido
    expirado --> [*]
    cancelado --> [*]
  • O pedido é um saber (nature: pedido) gravado no galho da mucua que pediu — ele viaja pelo git como qualquer outro.
  • ttl conta saltos, não tempo. A idade máxima (_velho_demais) só vale para pedido que nunca andou.
  • O ciclo nunca apaga cópia sozinho. Auto-drop já causou perda de dados; liberar espaço é ação explícita (POST …/dropar).
  • Rotas (nature: rota) são listas ordenadas de mucuas — o caminho de quem leva o pendrive. _proximo_salto decide para quem empurrar.

Caminhos de rede

CaminhoOnde estáDetalhe que importa
SSH pela interneturi_internet da mucuaformato usuario@host[:porta]; o caminho remoto é sempre ~usuario/.baobaxia/galaxias/<pasta> (util.montar_url_ssh_galaxia)
Toruri_tor + tor_service.pyTor próprio na porta SOCKS 19050; ProxyCommand com nc; diagnóstico em infra/sync/tor_diagnostico.py
Rede localfarol_lan.pyanúncio UDP multicast; o endereço vem da origem do datagrama, não do que o pacote diz
A pépedidos + rotasstore-and-forward

A confiança entre mucuas é a chave SSH: ssh_keys.py mantém o authorized_keys do usuário baobaxia, cujo shell é git-annex-shell — só comandos git/annex passam.

Identidade espelhada

A mucua local, o território dela e as contas dela são replicados com os mesmos UUIDs em toda galáxia desta instância (propagacao.sincronizar_identidade). É assim que a mesma pessoa existe em vários acervos.

Práticas

  1. Rode testes/test_merge_bbx.py e leia test_as_duas_mucuas_convergem_para_os_mesmos_bytes. Monte à mão um conflito (duas edições do mesmo título) e veja quem vence.
  2. Em test_pedidos_logica_pratica.py, ache o caso "pedido satisfeito é REMOVIDO". Por que remover, e não marcar como satisfeito?
  3. Desenhe, para a sua região, uma rota real: quais mucuas, em que ordem, e quem carrega o pendrive.
  4. Em test_farol_rede_local.py, ache o teste de que o endereço vem da ORIGEM do datagrama, e não do conteúdo do pacote. Que ataque isso evita?

Para conversar

  • O que é "estar sincronizado" numa rede em que nunca todo mundo está online ao mesmo tempo?
  • Quem merece ser um salto numa rota? Que confiança isso pede?

ver este capítulo como saber