Part I — Preparing the ground
02 · Architecture: the whole tree
Principles
- The file system is the source of truth. Each saber is a
saber.bbxfile (YAML + markdown) versioned in git; binaries go through git-annex. The SQLite index is a cache: delete it and runbbx reindex. - Offline first. Each mucua works on its own. Network is a bonus.
- A federation of equals. There is no central mucua. Mucuas run the same code; only the configuration changes.
- Modular through environment variables.
BBX_AUTH_MODEturns on cryptography;BBX_FED_XMPP/BBX_FED_APturn on federation;BBX_ENRIQUECERturns on OCR and Whisper.
Backend layers
flowchart TB
subgraph Entradas["Entry points (branches)"]
API["api/v3/ — FastAPI<br/>~150 routes"]
CLI["cli.py — bbx"]
BOT["federacao/xmpp_bot.py"]
TIMERS["systemd timers<br/>bbx sincronizar · bbx enriquecer"]
end
subgraph Servicos["services/ — business rules"]
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/ — operations shared by the CLI and the bot"]
OPS["operacoes.py · processamento.py"]
end
subgraph Nucleo["core/ — the trunk"]
SAB["saber.py — model"]
REPO["repository.py — SaberRepository"]
IDX["index.py — GalaxyIndex (SQLite)"]
REG["registry.py · relation_graph.py"]
end
subgraph Infra["infra/ — the roots"]
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/ — text · OCR · Whisper"]
FS[("disk: ~/.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
The dependency rule: top to bottom. core/ does not import services/; infra/ knows nothing about FastAPI. When you see a late import (inside a function), it is almost always there to keep this rule without a cycle.
Deploying a mucua
flowchart LR
pessoa(["browser"]) -->|":80"| NGINX
subgraph Maquina["the mucua's machine (baobaxia.deb package)"]
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/>every 30 min"]
T2["baobaxia-enriquecer.timer<br/>20 min after each round"]
T3["baobaxia-healthcheck.timer<br/>every 2 min"]
TOR["own tor<br/>SOCKS 19050 + .onion service"]
SSHD["sshd<br/>user 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(["another mucua"]) -->|"git annex sync over SSH"| SSHD
SSHD --> DADOS
APIS -.->|"ssh / tor"| outra
Notice: three processes write to the same galaxy (the API, the sync timer, the enrichment timer). That is why services/trava_galaxia.py exists — a flock between processes — and why the index opens SQLite with a timeout.
One request, from click to disk
sequenceDiagram
autonumber
actor P as Person
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 /en-US/saber/ab12cd34
F->>A: GET /saberes/ab12… (Bearer from the 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) in each galaxy
R->>I: get(uuid) — finds rel_path
R-->>S: Saber (read from saber.bbx + presence on disk)
S->>S: permissions.can(actor, READ, _resource_of(saber))
S-->>A: to_public_dict(lang) + context
A-->>F: JSON
F-->>P: HTML
Practice
- Pick a route in
api/v3/saberes.pyand follow it down to the disk, noting each file and function. Compare with the sequence diagram. - Look in
services/for animportinside a function. Which cycle does it avoid? - In
api/v3/app.py, which middlewares exist? What doesinfra/vigia_api.pywatch, and why?
To talk about
- What are the advantages and the costs of the disk being the truth, rather than a database?
- If a mucua goes a year without a network, what happens when it comes back?
