Parte II — Tronco y raíces
04 · Las raíces: bajo tierra
Los baobabs de una misma región se tocan por las raíces. Una mucua no necesita internet para encontrar a otra: basta con que exista un camino — un cable, una antena, una red Tor, o alguien caminando con un pendrive.
Archivos para leer
infra/sankofa.py— la fachada;infra/sync/__init__.pyelige el backend.infra/sync/base.pyeinfra/sync/gitannex.py— los comandos git/annex de verdad (_execmata el árbol de procesos en el timeout).services/links.py— los remotes de cada galaxia, el reintento por Tor, el redescubrimiento del.onion, la autorización por clave SSH.services/sincronizacao.py—sincronizar_galaxia, el corazón del sync.infra/merge_bbx.py— el driver de merge semántico de los.bbx.services/pedidos.py— el store-and-forward (pedidos y rutas).infra/farol_lan.py,infra/tor_service.py,infra/ssh_keys.py.services/propagacao.py— la identidad local reflejada en toda galaxia.
Git para el texto, git-annex para el resto
saber.bbxy los textos pequeños van a git (historial completo, merge).- Fotos, videos, PDF, subtítulos
.vtt, textos de OCR van al annex: en git queda solo un enlace con la clave (SHA256E-s…--hash.ext). Cada mucua decide qué quiere tener (wanted) y puede pedir el resto después. El predeterminado (DEFAULT_WANTED) trae siempre los anexos de evento, mocambola y territorio, y los de artículo y colección hasta 20 MB — las imágenes del texto y las portadas; una galaxia con el predeterminado antiguo se actualiza sola al sincronizar (garantir_wanted). - La regla está en
GITATTRIBUTES(infra/sync/base.py):*.ocr.txt annex.largefiles=anythinggana sobre*.txt annex.largefiles=nothing. - Las mismas reglas van también en el
.git/info/attributesLOCAL (gana al versionado y se rehace en cada sync), con un límite: el texto solo va a git hasta 2 MB (LIMITE_TEXTO_GIT) — un.jsonde 300 MB va al annex. Sin git-annex en el PATH, el archivo pesado queda fuera del commit en lugar de entrar crudo en git. Lo que ya entró mal:bbx annex-reparar(o Cuidar › Archivos pesados en git), enservices/annex_reparo.py.
Un ciclo de sincronización
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: galaxia X
S->>S: TravaGalaxia (flock) — ¿ya en curso? sale
S->>L: garantir_annex_uuids (remote ↔ uuid de la mucua)
alt algún enlace es .onion
S->>S: _garantir_tor_local (levanta el Tor propio)
end
loop cada enlace habilitado
S->>G: git annex sync --remote=link (con reintento si es Tor)
G->>M: conflicto en *.bbx → merge campo a campo
alt falló y es .onion
S->>L: redescobrir_onion (pregunta la dirección nueva)
S->>G: lo intenta de nuevo
end
end
S->>G: resolver_conflitos (aborta un merge pendiente y lo informa)
S->>R: reindex(incremental=True)
opt data=true
S->>R: refrescar_presenca()
end
S->>S: atualizar_estado (limpia cachés)
El merge que no rompe
Un conflicto textual en un saber.bbx dejaría el YAML inválido, el saber desaparecería del índice y la galaxia dejaría de sincronizarse. El driver merge_bbx.py mezcla campo a campo: diccionarios por clave, listas de anexos por filename, escalares por el updated más reciente, y el cuerpo markdown con git merge-file. Es simétrico: ejecutado en la mucua A o en la B, genera los mismos bytes — eso es lo que hace converger a la red.
garantir_driver_merge graba la regla en .git/info/attributes (local, no versionado) antes de cada sync: se cura sola cuando cambia la ruta de Python.
Pedidos: el contenido camina de mano en mano
stateDiagram-v2
[*] --> ativo: criar_pedido (buscar | enviar)
ativo --> ativo: ciclo sin lograrlo (el TTL no cambia)
ativo --> ativo: obtuvo la copia y NO soy el destino → copy --to próximo salto
ativo --> [*]: soy el destino y la recibí → pedido ELIMINADO
ativo --> expirado: ttl ≤ 0 (saltos) o demasiado viejo
ativo --> cancelado: cancelar_pedido
expirado --> [*]
cancelado --> [*]
- El pedido es un saber (
nature: pedido) grabado en la rama de la mucua que lo hizo — viaja por git como cualquier otro. ttlcuenta saltos, no tiempo. La edad máxima (_velho_demais) solo vale para un pedido que nunca se movió.- El ciclo nunca borra una copia por su cuenta. El auto-drop ya causó pérdida de datos; liberar espacio es una acción explícita (
POST …/dropar). - Las rutas (
nature: rota) son listas ordenadas de mucuas — el camino de quien lleva el pendrive._proximo_saltodecide a quién empujar.
Caminos de red
| Camino | Dónde está | El detalle que importa |
|---|---|---|
| SSH por internet | uri_internet de la mucua | formato usuario@host[:puerto]; la ruta remota es siempre ~usuario/.baobaxia/galaxias/<carpeta> (util.montar_url_ssh_galaxia) |
| Tor | uri_tor + tor_service.py | Tor propio en el puerto SOCKS 19050; ProxyCommand con nc; diagnóstico en infra/sync/tor_diagnostico.py |
| Red local | farol_lan.py | anuncio UDP multicast; la dirección viene del origen del datagrama, no de lo que dice el paquete |
| A pie | pedidos + rutas | store-and-forward |
La confianza entre mucuas es la clave SSH: ssh_keys.py mantiene el authorized_keys del usuario baobaxia, cuyo shell es git-annex-shell — solo pasan comandos git/annex.
Identidad reflejada
La mucua local, su territorio y sus cuentas se replican con los mismos UUID en toda galaxia de esta instancia (propagacao.sincronizar_identidade). Así la misma persona existe en varios acervos.
Prácticas
- Corre
testes/test_merge_bbx.pyy leetest_as_duas_mucuas_convergem_para_os_mesmos_bytes. Arma a mano un conflicto (dos ediciones del mismo título) y mira quién gana. - En
test_pedidos_logica_pratica.py, busca el caso "pedido satisfecho es ELIMINADO". ¿Por qué eliminarlo, y no marcarlo como satisfecho? - Dibuja, para tu región, una
rotareal: qué mucuas, en qué orden, y quién lleva el pendrive. - En
test_farol_rede_local.py, busca el test de que la dirección viene del ORIGEN del datagrama, y no del contenido del paquete. ¿Qué ataque evita?
Para conversar
- ¿Qué es "estar sincronizado" en una red donde nunca todo el mundo está en línea al mismo tiempo?
- ¿Quién merece ser un salto en una ruta? ¿Qué confianza pide eso?
