Protocolo de recuperación de Oasis (fork dockerizado)
Checklist operativo reutilizable para recuperar un despliegue de Oasis (cliente o pub) tras corrupción de disco, pérdida del log SSB o daño en el repositorio git. Complementa a UPGRADE-PROTOCOL.md. Deriva de una recuperación real completa (repo + imagen + identidad).
Modelo mental. Solo hay UNA cosa irremplazable: el
secret(identidad SSB) y las claves (GPG, SSH). Todo lo demás es derivable: el log se re-replica desde el pub, los índices (flume) se reconstruyen del log, los blobs son content-addressed y se re-descargan, el código vive en GitHub y la imagen Docker se reconstruye del árbol. El pub es la fuente de verdad de los feeds: lo que el pub no tenga de tu feed, no existe.
0. Triaje — verificar integridad ANTES de usar nada
Nunca confiar en un fichero de un disco sospechoso sin verificarlo. El daño típico: fichero con tamaño intacto pero contenido a ceros (total o por tramos alineados a 4KB).
# Texto: buscar bytes NUL (un fichero de texto sano tiene CERO)
rg -la '\x00' <dir> # lista los dañados
tr -dc '\000' < fichero | wc -c # debe dar 0
# Binarios: NUL no discrimina (un PNG sano tiene NULs) → comprobar magic bytes
head -c 8 fichero | od -An -tx1
# PNG=89504e47 JPG=ffd8ff GGUF=47475546 gzip=1f8b PACK(git)=5041434bTrampas conocidas de las herramientas:
grep -qP '\x00'NO detecta NUL de forma fiable → falsos negativos silenciosos. Usarrg.grep -cen ficheros binarios cuenta líneas, no ocurrencias → usargrep -ao ... | wc -l.- Los greps son case-sensitive: buscar nombres propios con
-i. - Un fichero "legible" que muestra solo espacios en el editor puede ser 100% NUL (el render los pinta como blancos). Verificar con
od.
1. Prioridades (en este orden, siempre)
- Blindar lo insustituible: copiar
secret/claves a otro medio físico y VERIFICAR la copia (tamaño, sin NUL, estructura). Un backup no verificado no es un backup. - Blindar el working tree:
robocopy /E /XD node_modules volumes-deva una carpeta de recovery ANTES de tocar git. Los uncommitted viven en disco, no en.git. - Sacar el código de la máquina: commit + push a GitHub en cuanto haya algo verificado.
- Solo después: reconstruir imagen, contenedor y log.
2. Recuperar el repositorio git
Si el object store está corrupto, NO reparar in-place: dejar el .git dañado intacto como forense y reconstruir aparte.
# 1) Clon limpio en carpeta de recovery
git clone <origin-url> recovery-clean
# 2) Apuntar el .git sano al working tree real (sin copiar nada)
git --git-dir=recovery-clean/.git --work-tree=<working-tree> status --short
# 3) Elegir la base correcta: la rama remota MÁS CERCANA al working tree
# (comparar diff --name-status contra cada candidata; menor huella gana)
# 4) Commit por plumbing (no toca el working tree):
git ... add -A && WTREE=$(git ... write-tree)
git ... commit-tree $WTREE -p <base> -m "recover: ..." && git ... update-ref ...Del store corrupto aún se puede salvar:
- El reflog (
logs/HEAD) es texto plano: da los SHAs de los commits locales perdidos. - Commits/árboles individuales pueden ser legibles aunque la punta esté corrupta:
git cat-file -p <sha>uno a uno, del más nuevo al más viejo. - Truco blob-id: el hash de un blob está en el árbol (legible) aunque el blob esté corrupto. Si el blob-id local == blob-id en una rama remota, la versión de GitHub es byte-exacta a la local → recuperación exacta garantizada sin leer el blob.
Antes del push, SIEMPRE:
- Dry-run de secretos:
git add -An+ grep de.env|secret|.key|.gpg|.ssh|id_ed25519por nombre Y por contenido (BEGIN.*PRIVATE KEY). - Verificación NUL/magic de todo lo que entra (§0) — commitear un árbol dañado obliga a un commit correctivo después.
- Si hay daño mezclado: separar por procedencia (¿este diff es trabajo real o es daño?); los binarios que el trabajo no tocó se restauran wholesale de la rama remota.
3. Reconstruir la imagen y el contenedor cliente
# SIEMPRE desde un árbol verificado (el working tree dañado hornea el daño en la imagen)
docker compose build oasis-client
docker rm <contenedor-viejo> # los datos viven en los bind mounts, no se pierden
docker compose up -d oasis-client- Un
docker-compose.override.yml(no versionado) permite apuntar los bind-volumes alvolumes-devoriginal desde otra carpeta de código. - El modelo AI se re-descarga solo si falta (
download_ai_modeldel entrypoint); opt-out conOASIS_SKIP_AI_MODEL=true. Apartar el dañado: el entrypoint decide por presencia del fichero. - Síntoma "package.json parse error at position 4096" dentro del contenedor = imagen construida con árbol dañado → rebuild desde árbol limpio.
4. Restaurar un log SSB perdido — ⚠️ LA PARTE DELICADA
REGLA DE ORO: nunca arrancar la GUI de Oasis con un log vacío. backend.js auto-publica oasisVersion al arrancar; sobre log vacío eso crea un seq-1 nuevo → FORK del feed frente al historial que guarda el pub → la replicación del propio feed queda bloqueada para siempre (EBT no puede reconciliar) y la identidad puede quedar marcada como bifurcada en la red.
Actualización 1.1.2 (2026-09-17). El
oasisVersiondel arranque ya está guardado en upstream (if (!latestSeq) return), pero la regla sigue en pie porque el PM de bienvenida de la GUI (backend.js, a los 3 s) no lo está: se publica si no existe~/.ssb/oasis-first-contacto si el flag es de otra identidad (lo borra y publica). Mitigación: pre-crear el flag con el feed ywelcome=done. Todo el flujo está empaquetado enclient/scripts/import-identity.sh+client/scripts/sync-only.sh(../CLIENT-PROTOCOL.md§2-§3); lo de abajo es el mecanismo.
Secuencia correcta:
# 1) Preservar: secret, config, gossip.json, blobs/. Apartar: flume/, ebt/ (derivados),
# y borrar socket y manifest.json residuales.
# 2) Arrancar sbot PURO (sin GUI → nada puede publicar). Hoy: bash client/scripts/sync-only.sh start
# (= modo `server` del entrypoint: chown, parches de runtime y `exec node SSB_server.js start`; nunca backend.js)
docker compose run -d --rm --no-deps --name oasis-sync-only -e OASIS_SKIP_AI_MODEL=true oasis-client server
# 3) Vigilar la re-replicación (termómetro = log.offset creciendo):
watch stat -c%s volumes-dev/ssb-data/flume/log.offset
# 4) Preguntar al pub qué tiene de tu feed (respuesta definitiva, no adivinar):
bash devops/scripts/pub-feed-seq.sh '<tu-feed>' # seq_pub · pub_follows_feed (pub/tools/ssb-probe.js por stdin)
bash client/scripts/sync-only.sh status --pub # compara fichero / sbot / pub → SYNC-OK | AHEAD | BEHIND | PUB-UNKNOWN
# Cuando el log local alcance ese seq (y esté estable): sincronización COMPLETA.
# 5) Solo entonces: parar el sbot puro y arrancar la GUI normal.
# Su auto-publish caerá en seq N+1 = continuación legítima, sin fork.
docker stop oasis-sync-only && docker start oasis-clientTips de diagnóstico SSB:
- "Another Oasis instance is already running" también salta con un
OpenErrorde LevelDB (flume corrupto), no solo con un lock real (SSB_server.js,isLockError). conn.jsoncorrupto bloquea el marcado de conexiones y NO siempre se auto-sana → borrarlo; se regenera desdegossip.json.- Conexión al pub verificable sin herramientas:
cat /proc/net/tcpdentro del contenedor y buscar la IP del pub en hex little-endian, estado01= ESTABLISHED. - Si EBT se estanca con la conexión caída y el scheduler no rediala:
docker restartdel contenedor fuerza el redial y EBT continúa desde el último seq. - El
/dev/tcp/...de bash no existe en elsh(dash) del contenedor → falso "no conecta". Probar concurl telnet://host:puerto(exit 0/52/56 = conectó). - El perfil (nombre/avatar) son mensajes
aboutDEL log: si la GUI muestra la clave truncada, no es pérdida de identidad — es que el log aún no tiene los abouts.
5. Healthcheck final
- Contenedor
healthy; la web muestra la versión esperada. Oasis IDdel boot = feed id de siempre.- Log local en el mismo seq que el pub (paso 4.4) y mensajes nuevos apendizando en seq siguientes (sin errores de fork en logs).
- Perfil con nombre y avatar visibles.
- Anotar en el journal (
devops/logs/deploy-history.jsonl) qué se restauró, desde qué fuente y hasta qué seq.
6. Después: reducir la superficie de la próxima
- Backup periódico VERIFICADO de
secret+ claves en medio externo (y probar la restauración). - El pub guarda tu feed: sincronizar el cliente con el pub a menudo ES el backup del log.
- No acumular trabajo sin push: el working tree es un único punto de fallo.
- Mantener
.gitignoreanidados en las carpetas de identidad (patrón*+ whitelist de README/scripts) para que ungit add -Ade emergencia nunca pueda filtrar claves.