Rama de desarrollo experimental sobre el Oasis de epsylon, sin afiliacion con el proyecto original. Base: 0.9.5. Trae el modulo Karvan —salas efimeras en memoria con autodestruccion por TTL, chat que funciona sin JavaScript y llamadas de audio y video sobre WebRTC— con el enlace en el menu lateral por el mismo patron que el resto de modulos: renderKarvanLink calcado de renderPollsLink, entrada en modules_view y en la lista de /modules. Los estilos del modulo van en su propia hoja, styles/karvan.css, siguiendo el patron de highlight.css: asi se ve igual con cualquier tema, sin depender del tema movil. Sin servidores ICE de terceros: no hay STUN de fabrica, porque un STUN aprende la IP publica y el momento de cada llamada. Solo candidatos host salvo que se configure un TURN propio en oasis-config.json, con credenciales efimeras por el mecanismo REST de coturn. El codigo propio vive separado —views/fork/, backend/fork_routes.js, models/karvan_model.js y translations/fork/— de modo que sobre los ficheros de upstream solo hay enganches de una linea y cada version nueva se integra sin arrastrar nada. Respecto al arbol de Android: fuera el wrapper y main.js, que es su arranque; el tema vuelve a Dark-SNH y se restauran los modulos que en movil se recortan por peso. Verificado arrancando sin OASIS_MOBILE: nueve rutas OK, menu lateral de upstream con 27 grupos y el enlace de Karvan, cero rastro de la interfaz movil (ni hexagonos, ni topbar, ni barra inferior), y una sala creada y servida.
243 lines
12 KiB
Markdown
243 lines
12 KiB
Markdown
# 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:<pubkey> · 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 <ip:port> (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:<pubkey-base64>`.
|
|
|
|
- **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**:
|
|
`@<base64>.ed25519`. En las direcciones multiserver, el sufijo
|
|
`~shs:<pubkey>` 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:<ssb.path>/socket~noauth:<pubkey>` |
|
|
| 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:<path>/socket~noauth:<pubkey>` 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-base64>.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)**.
|