# 1. Oasis a nivel de red (Secure Scuttlebutt) Oasis es un cliente/servidor de **Secure Scuttlebutt (SSB)**. Toda la "red" de Oasis se construye sobre la pila clásica del ecosistema SSB: `secret-stack` + `ssb-db` + un montón de plugins. Hay una separación clara en **dos procesos**: - **Backend SSB (sbot)** — `src/server/SSB_server.js`: el nodo de red propiamente dicho. Es quien habla con otros peers. - **Frontend web (GUI Koa)** — `src/backend/backend.js`: un servidor HTTP que actúa como **cliente muxrpc** del sbot, conectándose por un socket Unix local. ``` RED SSB (otros peers Oasis, misma SHS cap) │ net:IP:8008~shs: · ssb-lan (UDP) · ssb-onion (Tor) │ ┌─────────────────────────────────────────────┐ │ SBOT (src/server/SSB_server.js) │ │ peer <-> MULTISERVER <-> SECRET-STACK (SHS) │ │ <-> MUXRPC <-> PLUGINS │ └─────────────────────────┬───────────────────┘ │ unix socket ~noauth (muxrpc local, sin SHS) ▼ ┌─────────────────────────────────────────────┐ │ FRONTEND (src/backend/backend.js, Koa) │ │ cooler = src/client/gui.js → ssb-client │ │ app.listen({host, port:3000}) │ └─────────────────────────┬───────────────────┘ ▼ Navegador http://localhost:3000 ``` --- ## 1.1 Arranque de red ### Scripts de arranque - **`install.sh`**: instala Node 22 y hace `npm install` en `src/server` (línea 23). Opcionalmente descarga modelos de IA. No toca la red. - **`oasis.sh`** es el lanzador. Modos relevantes: - `server` / `pub` (líneas 61-69): ejecuta `node SSB_server.js start` — **solo el sbot headless**, sin interfaz web. - `gui` o por defecto (líneas 73-81): ejecuta `node backend.js` — el frontend. - comandos pub `whoami|invite|name|announce|follow|status|gossip` (líneas 70-72) → delegan en `scripts/oasis-pub.js`. - **`src/server/package.json`**, script `start`: `npm run start:ssb && sleep 10 && npm run start:backend`. Arranca el sbot en segundo plano, **espera 10 s** (para que el socket Unix exista) y luego arranca el frontend. ### Qué hace `SSB_server.js` paso a paso (`src/server/SSB_server.js`) 1. **Líneas 7-11**: carga `secret-stack`, `ssb-caps` (la capability SHS), `ssb-db`, la config (`./ssb_config`) y utilidades de metadatos. 2. **Líneas 13-52** (IIFE): *monkey-patch* de `console.error`/`console.warn` para silenciar ruido de red — rechazos de handshake por SHS cap incorrecta, excepciones EBT, etc. Un rechazo se convierte en un log limpio: `[ts] REJECTED (wrong SHS cap)`. 3. **Líneas 56-81**: construye el servidor con `SecretStack({ caps }).use(plugin).use(plugin)...` encadenando todos los plugins (ver tabla abajo). 4. **Líneas 83-86**: si **no** es pub, carga `ssb-lan` (descubrimiento LAN por UDP) y el router LAN local `./lanRouter`. 5. **Líneas 88-98**: lógica de `autofollow`; carga `ssb-autofollow` si hay feeds. 6. **Líneas 123-196** (`argv[0] === 'start'`): instancia el servidor, escribe `manifest.json` (el catálogo de métodos muxrpc remotos), monta una barra de progreso de replicación, imprime metadatos y **escucha eventos del hub de conexiones** (`server.conn.hub().listen()`) para loguear `CONNECTED` / `DISCONNECTED`. 7. **Líneas 198-202**: exporta `{ config, server, open() }` — esto permite a la GUI reusar el servidor **in-process** si corre en el mismo proceso. ### Plugins secret-stack cargados (líneas 56-98) | Plugin | Función de red | |---|---| | `ssb-db` | base de datos *append-only* de feeds | | `ssb-master` | identidad maestra / auth local | | `ssb-gossip` | gossip clásico (legacy) de peers | | `ssb-ebt` | **Epidemic Broadcast Trees** — replicación gossip eficiente | | `ssb-friends` | grafo social: hops, follows/blocks | | `ssb-blobs` | transferencia de ficheros binarios | | `ssb-meme` | blobs de imágenes/memes | | `ssb-conn` | gestor de conexiones moderno (hub, db, staging) | | `ssb-box` | cifrado de mensajes privados (box1) | | `ssb-search`, `ssb-private`, `ssb-friend-pub` | búsqueda, PM, pub de amigos | | `ssb-invite` / `ssb-invite-client` | invites (servidor si es pub, cliente si no) | | `ssb-replication-scheduler`, `ssb-partial-replication` | planificación de replicación | | `ssb-onion` | transporte Tor | | `ssb-unix-socket` | **socket Unix local** para clientes (la GUI) | | `ssb-no-auth` | conexiones locales sin handshake SHS | | `ssb-about`, `ssb-backlinks`, `ssb-links`, `ssb-tangle`, `ssb-query` | índices de vistas | | `ssb-lan` + `lanRouter` | descubrimiento LAN (solo si no es pub) | > Nota: la replicación NO usa `ssb-replicate` directamente; la orquestan > `ssb-ebt` + `ssb-replication-scheduler` + `ssb-friends`. --- ## 1.2 El protocolo SSB en términos del proyecto - **secret-stack + handshake SHS (capability).** `SecretStack({ caps })` crea el nodo. La `caps.shs` está **fijada en `src/configs/server-config.json`** (líneas 5-7): `"H5EC+V5BU9s0lWxCkt4z8a095Sj8a6TgiLKPYi1JD7s="`. Es la **clave de capacidad de red** (Secret Handshake): dos peers solo completan el handshake si comparten *exactamente* esta cap. Actúa como "contraseña de red": **Oasis forma su propia red SSB privada**, distinta de la red SSB pública. - **multiserver (transportes).** Capa de transporte abstracta. La config `connections` (`server-config.json`, líneas 29-60) define: - **incoming.net**: `transform: "shs"`, `port: 8008` → TCP cifrado con SHS. - **incoming.unix**: `transform: "noauth"` → socket Unix local sin handshake, para que el frontend se conecte como cliente privilegiado. - **outgoing.net**: `transform: "shs"`. - Una dirección multiserver típica: `net:IP:8008~shs:`. - **muxrpc (RPC sobre el stream).** Una vez establecido el stream SHS, `muxrpc` multiplexa llamadas RPC asíncronas y *pull-streams* sobre la misma conexión. El `manifest.json` describe qué métodos remotos existen (`sync`/`async`/ `source`). Es como el frontend invoca `ssb.publish(...)`, `ssb.blobs.get(...)`, etc. - **EBT (`epidemic-broadcast-trees` + `ssb-ebt`).** El algoritmo de replicación gossip. Cada peer anuncia el `seq` (reloj vectorial) de los feeds que tiene; el otro envía solo los mensajes que faltan, formando árboles de difusión epidémica que evitan duplicados. - **Identidad de los peers.** Cada peer es un **feed id ed25519**: `@.ed25519`. En las direcciones multiserver, el sufijo `~shs:` identifica criptográficamente al peer durante el handshake. --- ## 1.3 Federación / P2P - **Descubrimiento LAN** (`src/server/lanRouter.js`): plugin local custom. `startRouter` (líneas 55-64) arranca `ssb.lan.start()` (broadcast UDP) y consume `ssb.lan.discoveredPeers()`. Cada peer descubierto pasa por `handleDiscovery` → `stagePeer` (líneas 15-45): lo *stagea* en `ssb.conn.stage(...)` (vía moderna) con fallback a `ssb.gossip.add(...)` (legacy); si `eagerReplicate`, fuerza `ebt.request`. Respeta el flag `lanBroadcasting` de `oasis-config.json`. - **Pubs / invites.** En modo pub se carga `ssb-invite` (servidor); en cliente, `ssb-invite-client`. Los comandos `oasis.sh invite|announce|follow` (→ `scripts/oasis-pub.js`) permiten a un pub publicar su dirección y generar códigos de invitación. Un invite contiene `host:port:pubkey~seed`; al canjearlo, el peer se conecta y el pub le sigue de vuelta (`ssb-friend-pub`). - **Replicación por hops.** `ssb-friends` define el grafo social. `server-config.json` fija `friends.hops: 2` → el nodo replica los feeds de la gente que sigues y de la gente que ellos siguen (amigos de amigos). El `ssb-replication-scheduler` decide qué feeds pedir por EBT según ese grafo. - **"Distributed not decentralized".** No hay servidor central ni DHT global. Cada nodo Oasis tiene su **propia copia local** de la base de datos (logs *append-only* firmados con ed25519) y solo replica los feeds dentro de su radio social (hops) más los descubiertos por LAN. La red se federa por gossip entre peers que comparten la misma SHS cap. Es **distribuida** (datos replicados en muchos nodos autónomos, funciona offline) pero **no descentralizada** como una blockchain (no hay consenso global ni vista única; cada quien ve solo su subgrafo). El flag `wish` de `oasis-config.json` (`whole` / `mutuals` / `only-lan`) controla el alcance de replicación deseado. --- ## 1.4 Puertos y endpoints | Qué | Dónde | Puerto / dirección | |---|---|---| | muxrpc SSB (sbot) — otros peers se conectan aquí | `server-config.json` incoming.net | **8008/TCP** transform `shs` | | Socket Unix local — para el frontend | incoming.unix | `unix:/socket~noauth:` | | Frontend web (Koa) — tu navegador | `oasis_client.js` / `middleware.js:180` | **3000** host `localhost` (configurable con `--host`/`--port`) | **Cómo se conecta el frontend al sbot** (el "cooler", `src/client/gui.js`): 1. Intenta `require('../server/SSB_server')` y reusar el `server` **in-process** si existe (sin socket, uso directo del objeto). 2. Si no, construye `remote = unix:/socket~noauth:` y se conecta con **`ssb-client`** (muxrpc por el socket Unix `noauth`). 3. `attemptConnectionWithBackoff`: reintentos con backoff exponencial (de ahí el `sleep 10` del arranque). 4. Expone `cooler.open()` / `cooler.close()`. **Todos los modelos** del backend reciben este `cooler` y hacen `await cooler.open()` para obtener el cliente muxrpc. > Para exponer la GUI en un VPS se usa `--host=0.0.0.0`. --- ## 1.5 Blobs (ficheros / imágenes) - Plugin `ssb-blobs` (+ `ssb-meme`). Tamaño máximo en `ssb_config.js`: 50 MB. - Un blob se referencia por hash: `&.sha256`. **Los mensajes del log solo llevan el hash**, no el binario. - Flujo en `src/backend/blobHandler.js`: - **Subida**: `ssbClient.blobs.add(...)` recibe el contenido por pull-stream y devuelve el ref `&...sha256`. - **Descarga por red**: `blobs.has(id)` comprueba si ya está local; si no, `blobs.want(id)` **lo pide a los peers conectados** (gossip de "want/has" sobre muxrpc), y `blobs.get(id)` lo lee como stream. Los blobs viajan sobre la misma conexión SHS/muxrpc, fuera del log append-only. --- ## 1.6 Caps / cifrado - **`ssb-caps`** define las capabilities de protocolo (shs, sign, invite). La **shs cap real** está fijada en `server-config.json` → red privada de Oasis. - **Handshake SHS**: autenticación mutua por ed25519 + confidencialidad del stream de transporte. - **`ssb-box`**: cifrado de **mensajes privados box1** (hasta ~7 destinatarios, cifrado por clave pública de cada feed). `ssb-private` expone publish/read. - **Grupos privados ("tribes").** No se usa `box2`/`ssb-tribes`. En su lugar Oasis implementa cifrado **propio a nivel de aplicación** en `src/models/crypto.js`, instanciado por dominio en `backend.js` (`tribeCrypto`, `chatCrypto`, `padCrypto`, `mapCrypto`, `calendarCrypto`, `eventCrypto`, `forumCrypto`). Los grupos se cifran con esas claves derivadas y se almacenan/replican como mensajes normales del feed. --- ## 1.7 Archivos clave de la capa de red | Archivo | Rol | |---|---| | `src/server/SSB_server.js` | Construye el sbot y carga los plugins (56-98) | | `src/server/ssb_config.js` | Fusiona `server-config.json`; fija `blobs.max=50MB` | | `src/server/ssb_metadata.js` | Deriva e imprime puerto SSB, hops, LAN, feed ID | | `src/server/lanRouter.js` | Descubrimiento/staging de peers LAN | | `src/configs/server-config.json` | `caps.shs`, `pub`, `friends.hops`, `connections` (puertos) | | `src/configs/oasis-config.json` | `lanBroadcasting`, `wish`, `pmVisibility`, módulos | | `src/client/gui.js` | El "cooler": cliente muxrpc por socket Unix noauth | | `src/backend/blobHandler.js` | add/has/want/get de blobs | | `scripts/oasis-pub.js` | Comandos pub (invite/announce/follow) | Siguiente: **[02-arquitectura-software.md](02-arquitectura-software.md)**.