Parte I — Preparar el suelo
02 · Arquitectura: el árbol entero
Principios
- El sistema de archivos es la fuente de verdad. Cada saber es un archivo
saber.bbx(YAML + markdown) versionado en git; los binarios van por git-annex. El índice SQLite es una caché: bórralo y correbbx reindex. - Primero sin conexión. Cada mucua funciona sola. La red es un extra.
- Federación de iguales. No hay mucua central. Las mucuas corren el mismo código; solo cambia la configuración.
- Modular por variable de entorno.
BBX_AUTH_MODEactiva la criptografía;BBX_FED_XMPP/BBX_FED_APactivan la federación;BBX_ENRIQUECERactiva OCR y Whisper.
Capas del backend
flowchart TB
subgraph Entradas["Entradas (ramas)"]
API["api/v3/ — FastAPI<br/>~150 rutas"]
CLI["cli.py — bbx"]
BOT["federacao/xmpp_bot.py"]
TIMERS["timers systemd<br/>bbx sincronizar · bbx enriquecer"]
end
subgraph Servicos["services/ — reglas del negocio"]
SS["SaberService<br/>(saber_service.py)"]
PERM["permissions · acl · grupos<br/>acesso_galaxia"]
SYNC["sincronizacao · links<br/>pedidos · propagacao"]
OUT["copias · restauro · historico<br/>recovery · challenge · jobs"]
end
subgraph Pub["publica/ — operaciones comunes de la CLI y el bot"]
OPS["operacoes.py · processamento.py"]
end
subgraph Nucleo["core/ — el tronco"]
SAB["saber.py — modelo"]
REPO["repository.py — SaberRepository"]
IDX["index.py — GalaxyIndex (SQLite)"]
REG["registry.py · relation_graph.py"]
end
subgraph Infra["infra/ — las raíces"]
STO["storage.py — .bbx"]
SANK["sankofa.py + sync/ — git-annex"]
IDT["identity · envelope · jwt"]
NET["tor_service · farol_lan · ssh_keys"]
end
FED["federacao/ — XMPP · ActivityPub"]
ENR["enriquecimento/ — texto · OCR · Whisper"]
FS[("disco: ~/.baobaxia/galaxias/*<br/>git + git-annex")]
API --> SS
CLI --> OPS --> SS
BOT --> OPS
TIMERS --> SYNC
TIMERS --> ENR
SS --> PERM
SS --> SYNC
SS --> OUT
SS --> REPO
FED --> SS
ENR --> REPO
REPO --> SAB
REPO --> IDX
REPO --> STO
REPO --> SANK
SYNC --> SANK
SYNC --> NET
PERM --> IDT
STO --> FS
SANK --> FS
La regla de dependencia: de arriba hacia abajo. core/ no importa services/; infra/ no conoce FastAPI. Cuando veas un import tardío (dentro de una función), casi siempre es para mantener esa regla sin ciclos.
Despliegue de una mucua
flowchart LR
pessoa(["navegador"]) -->|":80"| NGINX
subgraph Maquina["máquina de la mucua (paquete baobaxia.deb)"]
NGINX["nginx<br/>sites-available/baobaxia"]
FRONT["baobaxia-front<br/>node build/index.js :3000"]
APIS["baobaxia-api<br/>bbx api :8000"]
T1["baobaxia-sincronizar.timer<br/>cada 30 min"]
T2["baobaxia-enriquecer.timer<br/>20 min después de cada ronda"]
T3["baobaxia-healthcheck.timer<br/>cada 2 min"]
TOR["tor propio<br/>SOCKS 19050 + servicio .onion"]
SSHD["sshd<br/>usuario baobaxia → git-annex-shell"]
DADOS[("/var/lib/baobaxia/.baobaxia")]
end
NGINX -->|"/"| FRONT
NGINX -->|"/api/"| APIS
FRONT -->|"SSR: 127.0.0.1:8000"| APIS
APIS --> DADOS
T1 --> DADOS
T2 --> DADOS
TOR -->|".onion → :8000"| APIS
outra(["otra mucua"]) -->|"git annex sync por SSH"| SSHD
SSHD --> DADOS
APIS -.->|"ssh / tor"| outra
Fíjate: tres procesos escriben en la misma galaxia (la API, el timer de sync, el timer de enriquecimiento). Por eso existe services/trava_galaxia.py — un flock entre procesos — y el índice abre SQLite con timeout.
Una petición, del clic al disco
sequenceDiagram
autonumber
actor P as Persona
participant F as Front (SSR)
participant A as API /api/v3
participant D as deps.py
participant S as SaberService
participant R as SaberRepository
participant I as GalaxyIndex
P->>F: GET /es-ES/saber/ab12cd34
F->>A: GET /saberes/ab12… (Bearer de la cookie)
A->>D: current_mocambola(token)
D-->>A: {uuid, role, galaxias, mucua, grupos}
A->>S: get_saber(uuid, actor)
S->>R: _find_saber → repo.get(uuid) en cada galaxia
R->>I: get(uuid) — encuentra rel_path
R-->>S: Saber (leído de saber.bbx + presencia en disco)
S->>S: permissions.can(actor, READ, _resource_of(saber))
S-->>A: to_public_dict(lang) + contexto
A-->>F: JSON
F-->>P: HTML
Prácticas
- Elige una ruta de
api/v3/saberes.pyy síguela hasta el disco, anotando cada archivo y función. Compara con el diagrama de secuencia. - Busca en
services/unimportdentro de una función. ¿Qué ciclo evita? - En
api/v3/app.py, ¿qué middlewares hay? ¿Qué vigilainfra/vigia_api.pyy por qué?
Para conversar
- ¿Cuáles son las ventajas y los costos de que el disco sea la verdad, y no la base de datos?
- Si una mucua pasa un año sin red, ¿qué ocurre cuando vuelve?
