Part II — Trunk and roots
04 · The roots: underground
Baobabs in the same region touch through their roots. A mucua doesn't need the internet to find another: it is enough that some path exists — a cable, an antenna, a Tor network, or someone walking with a USB stick.
Files to read
infra/sankofa.py— the facade;infra/sync/__init__.pypicks the backend.infra/sync/base.pyandinfra/sync/gitannex.py— the actual git/annex commands (_execkills the whole process tree on timeout).services/links.py— each galaxy's remotes, the retry over Tor, the rediscovery of the.onion, authorization by SSH key.services/sincronizacao.py—sincronizar_galaxia, the heart of sync.infra/merge_bbx.py— the semantic merge driver for.bbxfiles.services/pedidos.py— store-and-forward (requests and routes).infra/farol_lan.py,infra/tor_service.py,infra/ssh_keys.py.services/propagacao.py— the local identity mirrored into every galaxy.
Git for text, git-annex for the rest
saber.bbxand small texts go to git (full history, merge).- Photos, videos, PDFs,
.vttsubtitles, OCR texts go to the annex: git keeps only a link with the key (SHA256E-s…--hash.ext). Each mucua decides what it wants to have (wanted) and can request the rest later. The default (DEFAULT_WANTED) always brings event, mocambola and territory attachments, and article and collection ones up to 20 MB — the images in the text and the covers; a galaxy with the old default is updated by itself on sync (garantir_wanted). - The rule lives in
GITATTRIBUTES(infra/sync/base.py):*.ocr.txt annex.largefiles=anythingwins over*.txt annex.largefiles=nothing. - The same rules also go in the LOCAL
.git/info/attributes(it beats the versioned one and is rewritten on every sync), with a limit: text only goes to git up to 2 MB (LIMITE_TEXTO_GIT) — a 300 MB.jsongoes to the annex. Without git-annex on the PATH, the heavy file stays out of the commit instead of going raw into git. What already went in wrong:bbx annex-reparar(or Care › Heavy files in git), inservices/annex_reparo.py.
One sync cycle
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: galaxy X
S->>S: TravaGalaxia (flock) — already running? leave
S->>L: garantir_annex_uuids (remote ↔ mucua uuid)
alt some link is .onion
S->>S: _garantir_tor_local (starts our own Tor)
end
loop each enabled link
S->>G: git annex sync --remote=link (with retry if Tor)
G->>M: conflict in *.bbx → field-by-field merge
alt failed and it is .onion
S->>L: redescobrir_onion (asks for the new address)
S->>G: tries again
end
end
S->>G: resolver_conflitos (aborts a pending merge and reports)
S->>R: reindex(incremental=True)
opt data=true
S->>R: refrescar_presenca()
end
S->>S: atualizar_estado (clears caches)
The merge that doesn't break
A textual conflict in a saber.bbx would leave invalid YAML, the saber would vanish from the index and the galaxy would stop synchronizing. The merge_bbx.py driver merges field by field: dictionaries by key, attachment lists by filename, scalars by the most recent updated, and the markdown body with git merge-file. It is symmetric: run on mucua A or on B, it produces the same bytes — that is what makes the network converge.
garantir_driver_merge writes the rule into .git/info/attributes (local, not versioned) before every sync: it heals itself when the Python path changes.
Requests: content travels from hand to hand
stateDiagram-v2
[*] --> ativo: criar_pedido (fetch | send)
ativo --> ativo: cycle without success (TTL unchanged)
ativo --> ativo: got the copy and I am NOT the target → copy --to next hop
ativo --> [*]: I am the target and received it → request REMOVED
ativo --> expirado: ttl ≤ 0 (hops) or too old
ativo --> cancelado: cancelar_pedido
expirado --> [*]
cancelado --> [*]
- The request is a saber (
nature: pedido) written in the branch of the mucua that asked — it travels through git like any other. ttlcounts hops, not time. The maximum age (_velho_demais) only applies to a request that never moved.- The cycle never deletes a copy on its own. Auto-drop once caused data loss; freeing space is an explicit action (
POST …/dropar). - Routes (
nature: rota) are ordered lists of mucuas — the path of whoever carries the USB stick._proximo_saltodecides whom to push to.
Network paths
| Path | Where it is | The detail that matters |
|---|---|---|
| SSH over the internet | the mucua's uri_internet | format user@host[:port]; the remote path is always ~user/.baobaxia/galaxias/<folder> (util.montar_url_ssh_galaxia) |
| Tor | uri_tor + tor_service.py | own Tor on SOCKS port 19050; ProxyCommand with nc; diagnostics in infra/sync/tor_diagnostico.py |
| Local network | farol_lan.py | UDP multicast announcement; the address comes from the datagram's source, not from what the packet says |
| On foot | requests + routes | store-and-forward |
Trust between mucuas is the SSH key: ssh_keys.py keeps the authorized_keys of the baobaxia user, whose shell is git-annex-shell — only git/annex commands get through.
Mirrored identity
The local mucua, its territory and its accounts are replicated with the same UUIDs in every galaxy of this instance (propagacao.sincronizar_identidade). That is how the same person exists in several collections.
Practice
- Run
testes/test_merge_bbx.pyand readtest_as_duas_mucuas_convergem_para_os_mesmos_bytes. Build a conflict by hand (two edits of the same title) and see who wins. - In
test_pedidos_logica_pratica.py, find the case "a satisfied request is REMOVED". Why remove it, rather than mark it as satisfied? - Draw a real
rotafor your region: which mucuas, in what order, and who carries the USB stick. - In
test_farol_rede_local.py, find the test showing that the address comes from the datagram's SOURCE, not from the packet content. Which attack does that prevent?
To talk about
- What does "being in sync" mean in a network where never everyone is online at the same time?
- Who deserves to be a hop on a route? What trust does that require?
