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.
Arquivos para ler
infra/sankofa.py— a fachada;infra/sync/__init__.pyescolhe o backend.infra/sync/base.pyeinfra/sync/gitannex.py— os comandos git/annex de verdade (_execmata a árvore de processos no timeout).services/links.py— os remotes de cada galáxia, o retry pelo Tor, a redescoberta do.onion, a autorização por chave SSH.services/sincronizacao.py—sincronizar_galaxia, o coração do sync.infra/merge_bbx.py— o driver de merge semântico dos.bbx.services/pedidos.py— o store-and-forward (pedidos e rotas).infra/farol_lan.py,infra/tor_service.py,infra/ssh_keys.py.services/propagacao.py— a identidade local espelhada em toda galáxia.
Git para o texto, git-annex para o resto
saber.bbxe 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=anythingvence*.txt annex.largefiles=nothing. - As mesmas regras vão também no
.git/info/attributesLOCAL (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.jsonde 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), emservices/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. ttlconta 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_saltodecide para quem empurrar.
Caminhos de rede
| Caminho | Onde está | Detalhe que importa |
|---|---|---|
| SSH pela internet | uri_internet da mucua | formato usuario@host[:porta]; o caminho remoto é sempre ~usuario/.baobaxia/galaxias/<pasta> (util.montar_url_ssh_galaxia) |
| Tor | uri_tor + tor_service.py | Tor próprio na porta SOCKS 19050; ProxyCommand com nc; diagnóstico em infra/sync/tor_diagnostico.py |
| Rede local | farol_lan.py | anúncio UDP multicast; o endereço vem da origem do datagrama, não do que o pacote diz |
| A pé | pedidos + rotas | store-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
- Rode
testes/test_merge_bbx.pye leiatest_as_duas_mucuas_convergem_para_os_mesmos_bytes. Monte à mão um conflito (duas edições do mesmo título) e veja quem vence. - Em
test_pedidos_logica_pratica.py, ache o caso "pedido satisfeito é REMOVIDO". Por que remover, e não marcar como satisfeito? - Desenhe, para a sua região, uma
rotareal: quais mucuas, em que ordem, e quem carrega o pendrive. - 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?
