Parte II — Tronco e radici

04 · Le radici: sottoterra

I baobab di una stessa regione si toccano con le radici. Una mucua non ha bisogno di internet per trovarne un'altra: basta che esista un cammino — un cavo, un'antenna, una rete Tor, o qualcuno che cammina con una chiavetta USB.

Radici che si toccano: SSH, Tor, rete locale e sneakernet

File da leggere

  1. infra/sankofa.py — la facciata; infra/sync/__init__.py sceglie il backend.
  2. infra/sync/base.py e infra/sync/gitannex.py — i comandi git/annex veri (_exec uccide l'albero dei processi allo scadere del timeout).
  3. services/links.py — i remote di ogni galassia, i tentativi via Tor, la riscoperta dell'.onion, l'autorizzazione tramite chiave SSH.
  4. services/sincronizacao.py — sincronizar_galaxia, il cuore del sync.
  5. infra/merge_bbx.py — il driver di merge semantico dei .bbx.
  6. services/pedidos.py — lo store-and-forward (richieste e rotte).
  7. infra/farol_lan.py, infra/tor_service.py, infra/ssh_keys.py.
  8. services/propagacao.py — l'identità locale rispecchiata in ogni galassia.

Git per il testo, git-annex per il resto

  • saber.bbx e i testi piccoli vanno in git (cronologia completa, merge).
  • Foto, video, PDF, sottotitoli .vtt, testi OCR vanno nell'annex: in git resta solo un link con la chiave (SHA256E-s…--hash.ext). Ogni mucua decide cosa vuole avere (wanted) e può chiedere il resto più tardi. Il predefinito (DEFAULT_WANTED) porta sempre gli allegati di evento, mocambola e territorio, e quelli di articolo e collezione fino a 20 MB — le immagini del testo e le copertine; una galassia con il vecchio predefinito si aggiorna da sola alla sincronizzazione (garantir_wanted).
  • La regola sta in GITATTRIBUTES (infra/sync/base.py): *.ocr.txt annex.largefiles=anything vince su *.txt annex.largefiles=nothing.
  • Le stesse regole vanno anche nel .git/info/attributes LOCALE (vince su quello versionato e si rifà a ogni sync), con un limite: il testo va in git solo fino a 2 MB (LIMITE_TEXTO_GIT) — un .json da 300 MB va nell'annex. Senza git-annex nel PATH, il file pesante resta fuori dal commit invece di entrare crudo in git. Ciò che è già entrato male: bbx annex-reparar (o Curare › File pesanti in git), in services/annex_reparo.py.

Un ciclo di sincronizzazione

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: galassia X
    S->>S: TravaGalaxia (flock) — già in corso? esce
    S->>L: garantir_annex_uuids (remote ↔ uuid della mucua)
    alt qualche link è .onion
        S->>S: _garantir_tor_local (avvia il Tor proprio)
    end
    loop ogni link abilitato
        S->>G: git annex sync --remote=link (con nuovi tentativi se Tor)
        G->>M: conflitto in *.bbx → merge campo per campo
        alt fallito ed è .onion
            S->>L: redescobrir_onion (chiede il nuovo indirizzo)
            S->>G: riprova
        end
    end
    S->>G: resolver_conflitos (interrompe un merge pendente e lo segnala)
    S->>R: reindex(incremental=True)
    opt data=true
        S->>R: refrescar_presenca()
    end
    S->>S: atualizar_estado (svuota le cache)

Il merge che non si rompe

Un conflitto testuale in un saber.bbx lascerebbe lo YAML non valido, il saber sparirebbe dall'indice e la galassia smetterebbe di sincronizzarsi. Il driver merge_bbx.py unisce campo per campo: dizionari per chiave, liste di allegati per filename, scalari per l'updated più recente, e il corpo markdown con git merge-file. È simmetrico: eseguito sulla mucua A o sulla B, produce gli stessi byte — è questo che fa convergere la rete.

garantir_driver_merge scrive la regola in .git/info/attributes (locale, non versionato) prima di ogni sync: si ripara da sé quando cambia il percorso di Python.

Richieste: il contenuto viaggia di mano in mano

stateDiagram-v2
    [*] --> ativo: criar_pedido (cercare | inviare)
    ativo --> ativo: giro senza successo (il TTL non cambia)
    ativo --> ativo: ha ottenuto la copia e NON sono la destinazione → copy --to salto successivo
    ativo --> [*]: sono la destinazione e l'ho ricevuta → richiesta RIMOSSA
    ativo --> expirado: ttl ≤ 0 (salti) o troppo vecchia
    ativo --> cancelado: cancelar_pedido
    expirado --> [*]
    cancelado --> [*]
  • La richiesta è un saber (nature: pedido) scritto nel ramo della mucua che l'ha fatta — viaggia in git come qualsiasi altro.
  • ttl conta salti, non tempo. L'età massima (_velho_demais) vale solo per una richiesta che non si è mai mossa.
  • Il ciclo non cancella mai una copia da solo. L'auto-drop ha già causato perdita di dati; liberare spazio è un'azione esplicita (POST …/dropar).
  • Le rotte (nature: rota) sono liste ordinate di mucua — il percorso di chi porta la chiavetta. _proximo_salto decide a chi spingere.

Cammini di rete

CamminoDove staIl dettaglio che conta
SSH via interneturi_internet della mucuaformato utente@host[:porta]; il percorso remoto è sempre ~utente/.baobaxia/galaxias/<cartella> (util.montar_url_ssh_galaxia)
Toruri_tor + tor_service.pyTor proprio sulla porta SOCKS 19050; ProxyCommand con nc; diagnostica in infra/sync/tor_diagnostico.py
Rete localefarol_lan.pyannuncio UDP multicast; l'indirizzo viene dall'origine del datagramma, non da ciò che dice il pacchetto
A piedirichieste + rottestore-and-forward

La fiducia tra mucua è la chiave SSH: ssh_keys.py mantiene l'authorized_keys dell'utente baobaxia, la cui shell è git-annex-shell — passano solo comandi git/annex.

Identità rispecchiata

La mucua locale, il suo territorio e i suoi account vengono replicati con gli stessi UUID in ogni galassia di questa istanza (propagacao.sincronizar_identidade). È così che la stessa persona esiste in più archivi.

Esercizi

  1. Esegui testes/test_merge_bbx.py e leggi test_as_duas_mucuas_convergem_para_os_mesmos_bytes. Costruisci a mano un conflitto (due modifiche dello stesso titolo) e guarda chi vince.
  2. In test_pedidos_logica_pratica.py, trova il caso "una richiesta soddisfatta viene RIMOSSA". Perché rimuoverla, e non segnarla come soddisfatta?
  3. Disegna, per la tua regione, una rota reale: quali mucua, in che ordine, e chi porta la chiavetta.
  4. In test_farol_rede_local.py, trova il test che verifica che l'indirizzo viene dall'ORIGINE del datagramma, e non dal contenuto del pacchetto. Quale attacco evita?

Da discutere

  • Cosa significa "essere sincronizzati" in una rete in cui non sono mai tutti online nello stesso momento?
  • Chi merita di essere un salto in una rotta? Che fiducia richiede?

vedi questo capitolo come sapere