Part II — Trunk and roots

05 · The circle: identity and permissions

Ubuntu: "I am because we are". In Baobáxia, who you are counts across the whole network; what you may do counts inside the circle that recognizes you.

The circle of roles: USER, PAJÉ, OXALÁ

Files to read

  1. services/auth.py — passwords (PBKDF2), JWT tokens, verifying a token from another mucua, the login-attempt brake.
  2. api/v3/auth.py and api/v3/deps.py — login, and how the token becomes the moc every route receives.
  3. infra/identity.py — Ed25519 (signing) and X25519 (encryption) keys.
  4. services/permissions.py — can(): every access decision goes through here.
  5. services/acesso_galaxia.py, services/grupos.py, services/acl.py.
  6. infra/envelope.py — end-to-end encryption.
  7. services/challenge.py, services/recovery.py, infra/seed_phrase.py.

Two identities, one person

  • Password (password_hash, PBKDF2-SHA256, 200 thousand rounds) in the mocambola's saber.
  • Keys: an Ed25519 pair (signs authorship and tokens) + an X25519 pair (opens encrypted saberes). The private key stays in ~/.baobaxia/keys/<uuid>.json, encrypted with the password (scrypt + AES-GCM), mode 600 — it never travels. The public one goes in saber.bbx.
  • BBX_AUTH_MODE = senha (password) | chave (key) | ambos (both, the default).

Login and token

sequenceDiagram
    autonumber
    actor P as Person
    participant A as POST /auth/login
    participant F as brake (services/auth.py)
    participant S as SaberService
    participant K as identity
    P->>A: username (or name@uuid8 of the mucua) + password
    A->>F: tentativas_bloqueadas?
    alt too many failures in the window
        A-->>P: 429
    end
    A->>S: find_mocambola (prefers the one from the local mucua)
    A->>A: verify_password (PBKDF2, compare_digest)
    A->>K: cache_session_key — unlocks the private keys in memory (8 h)
    A->>A: _issue_for: effective role + administered galaxies
    A->>K: signs an EdDSA JWT with the MUCUA's key (or HS256)
    A-->>P: access_token (7 days) + profile without password_hash

With login by key: POST /auth/desafio returns a single-use nonce; POST /auth/login-chave sends the signature (and, for a secondary key, the delegation certificate signed by the main key).

A token signed by another mucua

flowchart TD
    T[EdDSA token, iss = bbx:mucua:X] --> L{is X the local mucua?}
    L -- yes --> K[verifies with the key that is ON DISK<br/>never with the pubkey from a saber]
    L -- no --> P[verifies with the pubkey of saber mucua X]
    P --> Q{is the sub mocambola known here?}
    Q -- "yes, and from another mucua" --> N[refuses: X only vouches<br/>for its own mocambolas]
    Q -- "yes, and from X" --> R[role, galaxies and mucua<br/>recomputed from OUR data]
    Q -- no --> U[comes in as USER, with no scope]

The signature proves who someone is; never what they may do here. (This design is from version 3.3 — see module 11.)

can(): the decision

flowchart TD
    A[can mocambola, action, resource] --> S{system actor?}
    S -- yes --> OK[allow]
    S -- no --> R{action = READ?}
    R -- yes --> RA{draft?}
    RA -- yes --> RB[only author, the mucua's PAJÉ,<br/>the galaxy's OXALÁ]
    RA -- no --> G{restricted galaxy<br/>and not on the list?}
    G -- yes --> NO[deny]
    G -- no --> PR{protecao acl/cifrado?}
    PR -- yes --> PL[readers, author;<br/>acl: + the mucua's PAJÉ]
    PR -- no --> V[visibility: public → everyone<br/>mucua → same mucua or OXALÁ<br/>private → owner or PAJÉ]
    R -- no --> W{anonymous?}
    W -- yes --> NO
    W -- no --> RO{read-only galaxy?}
    RO -- yes --> NO
    RO -- no --> E[saber editors UPDATE → local manager →<br/>owner → the mucua's PAJÉ → the galaxy's OXALÁ]

The resource descriptor is built in a single place (SaberService._descritor / _resource_of). When each route built its own, some forgot protecao and leaked — read the docstring of testes/test_vazamentos_de_leitura.py.

End-to-end encryption

The sealed gourd

With protecao: cifrado, each saber gets a content key (ChaCha20-Poly1305). description, body, location and the attachments (.bbxenc) are encrypted; title and tags stay in clear text for search. The saber's key is wrapped for each reader (X25519 ECDH + HKDF + AES-GCM). A role does not open the gourd: only someone holding a wrapping with their own name does.

Recovery

Three independent paths (services/recovery.py): a guardian authorizes rotating the identity (never seeing a password or any content), a seed phrase built from personal answers + entropy, or the key kept on the home mucua.

Practice

  1. In test_token_federado_recalcula_papel.py, read mucua_de_fora_nao_responde_por_mocambola_daqui. Explain the attack in your own words.
  2. Create a saber with protecao: acl and leitores: [someone]. Try to read it as anonymous through the listing, the detail, the collection, the attachment download. (test_vazamentos_de_leitura.py does exactly that.)
  3. In permissions.py, what is the difference between acl and cifrado for a PAJÉ? Why is that difference honest?

To talk about

  • The comment on GalaxiaData.acesso (natures/schemas.py) calls the closed galaxy "software courtesy". What does that mean, and when is it enough?
  • Who should be the guardian of someone's identity in your community?

ver este capítulo como saber