diff --git a/deploy/backup/RESTORE.md b/deploy/backup/RESTORE.md new file mode 100644 index 0000000..141c313 --- /dev/null +++ b/deploy/backup/RESTORE.md @@ -0,0 +1,145 @@ +# RESTORE.md — Restauración de CayceOps desde backups cifrados + +**Modelo:** la VPS genera backups cifrados (age) en `/var/backups/cayce/YYYY-MM-DD/`; +tu Mac los descarga con el usuario `backup-pull` (solo lectura). La clave +**privada** de age vive SOLO en tu Mac. Este runbook asume una VPS nueva +(Ubuntu + IP apuntada por DNS) y tus backups descifrados a mano. + +--- + +## 0. Descargar y descifrar (en TU Mac — nunca en la VPS) + +```bash +# pull (clave restringida: solo lectura de /var/backups/cayce) +rsync -av --partial backup-pull@15.235.3.239:/ ~/backups/cayce/ + +# integridad (sin clave privada; manifest lista los artefactos CIFRADOS) +cd ~/backups/cayce/2026-XX-XX && shasum -a 256 -c manifest.sha256 +# (Linux: sha256sum -c manifest.sha256) + +# descifrar (clave privada age del operador — la que NO está en la VPS) +age -d -i ~/keys/cayce-backup.txt -o cayce-ops-db.dump cayce-ops-db.dump.age +age -d -i ~/keys/cayce-backup.txt -o gitea-dump.zip gitea-dump.zip.age +age -d -i ~/keys/cayce-backup.txt -o config.tar.gz config.tar.gz.age +``` + +Artefactos: +| Archivo | Contenido | Formato | +|---|---|---| +| `cayce-ops-db.dump` | BD de CayceOps | `pg_dump` custom (restaurar con `pg_restore`) | +| `gitea-dump.zip` | repos + sqlite + adjuntos + config Gitea | zip de `gitea dump` | +| `config.tar.gz` | `caddy/` (Caddyfile+Caddyfile.d), `gitea/` (app.ini + internal_token), `systemd/` (unidades cayce-*), `cayce-ops/dot-env` | tar.gz | + +--- + +## 1. VPS nueva — base del sistema + +```bash +apt-get update +apt-get install -y postgresql age rsync git +# Gitea: misma versión mayor que la VPS origen (verSTEP: gitea --version en el viejo +# servidor ANTES de apagarlo, o lee la versión del zip gitea-dump: gitea-dump zip +# contiene un archivo "app.ini" con la versión en comentarios... anota la versión +# manualmente; el binario se baja de https://dl.gitea.com/gitea/) +adduser --system --group --home /var/lib/gitea git +mkdir -p /etc/gitea /var/lib/gitea /etc/cayce-backup +curl -L "https://dl.gitea.com/gitea//gitea--linux-amd64" -o /usr/local/bin/gitea +chmod +x /usr/local/bin/gitea +``` + +## 2. Postgres + +```bash +systemctl enable --now postgresql +sudo -u postgres psql <'; +CREATE DATABASE cayce_ops OWNER cayce; +SQL + +# restaurar el dump (los ficheros descifrados son legibles solo por ti; +# cópiales permisos de lectura para postgres o pásalos por stdin) +sudo -u postgres pg_restore -d cayce_ops --no-owner --no-privileges \ + /ruta/cayce-ops-db.dump + +# verificación +sudo -u postgres psql -d cayce_ops -tAc \ + "SELECT count(*) FROM audit_log;" # debe coincidir con la última ancla +``` + +## 3. Gitea + +```bash +# config restaurada desde config.tar.gz +tar -xzf config.tar.gz -C /tmp/restored +cp /tmp/restored/gitea/app.ini /etc/gitea/app.ini +chown root:git /etc/gitea/app.ini && chmod 660 /etc/gitea/app.ini +cp /tmp/restored/gitea/internal_token /etc/gitea/ && chown git:git /etc/gitea/internal_token + +# NOTA sobre ROOT_URL: app.ini restaurado apunta a https://git.cayce-co.xyz/ — +# el DNS de ese subdominio debe existir ANTES de iniciar Gitea o las URLs +# absolutas (webhooks, notificaciones) quedarán mal. + +# dump de Gitea: contiene data/, repos/ y custom/. Restaurar SOBRE el dump +# de Gitea exige la misma versión del binario; descomprimir dentro de WORK_PATH +systemctl stop gitea 2>/dev/null || true +unzip -o gitea-dump.zip -d /tmp/gitea-restore +rsync -a /tmp/gitea-restore/data/ /var/lib/gitea/data/ +rsync -a /tmp/gitea-restore/repos/ /var/lib/gitea/data/gitea-repositories/ 2>/dev/null || true +rsync -a /tmp/gitea-restore/custom/ /var/lib/gitea/custom/ 2>/dev/null || true +chown -R git:git /var/lib/gitea +sudo -u git /usr/local/bin/gitea migrate -c /etc/gitea/app.ini # ajusta esquema si cambia versión +``` + +> **Consistencia:** los dumps de Gitea se toman EN CALIENTE (el servicio sigue +> corriendo). Riesgo teórico de inconsistencia sqlite/repos si un push ocurre +> en el mismo instante. Para un backup garantizado-consistente: `systemctl stop +> gitea` antes de `gitea dump` y volver a levantar después (no automatizado a +> propósito: no queremos ventanas de downtime diarias). + +## 4. Config del sistema (Caddy, unidades systemd, .env) + +```bash +# Caddy +apt-get install -y caddy # o el instalador oficial +rsync -a /tmp/restored/caddy/Caddyfile /etc/caddy/Caddyfile +rsync -a /tmp/restored/caddy/Caddyfile.d/ /etc/caddy/Caddyfile.d/ +systemctl enable --now caddy # emitirá certificados si el DNS ya apunta aquí + +# unidades systemd de cayce (DESHABILITADAS por diseño — ver riesgos) +rsync -a /tmp/restored/systemd/ /etc/systemd/system/ +systemctl daemon-reload +# NO habilitar cayce-ops@*.timer sin el operador + +# .env de cayce-ops +install -d -m 755 /home/ubuntu +rsync -a /tmp/restored/cayce-ops/dot-env /home/ubuntu/cayce-ops/.env +chown ubuntu:ubuntu /home/ubuntu/cayce-ops/.env && chmod 660 /home/ubuntu/cayce-ops/.env +``` + +## 5. Código del repo + +```bash +sudo -u ubuntu git clone http://127.0.0.1:3100/cayce/cayce-ops.git /home/ubuntu/cayce-ops \ + || git clone https://git.cayce-co.xyz/cayce/cayce-ops.git /home/ubuntu/cayce-ops +cd /home/ubuntu/cayce-ops && uv sync +make check # gate verde antes de considerar la restauración completa +``` + +## 6. Reinstalar el pipeline de backup en la VPS nueva + +```bash +install -m 700 -o root -g root deploy/backup/make-backup.sh /usr/local/bin/cayce-make-backup +install -m 644 deploy/systemd/cayce-backup.{service,timer} /etc/systemd/system/ +systemctl daemon-reload && systemctl enable --now cayce-backup.timer +# la MISMA clave pública age → tu Mac sigue pudiendo descifrar: +echo 'age1' > /etc/cayce-backup/age-recipient.pub +``` + +## 7. Checklist de verificación post-restauración + +- [ ] `sha256sum -c manifest.sha256` OK en tu Mac (antes de descifrar) +- [ ] `pg_restore --list` del dump sin errores +- [ ] `verify_audit_chain(sess)` → `(True, None)` contra la BD restaurada +- [ ] Gitea responde por TLS en `git.cayce-co.xyz` y `git rev-parse main` da el commit esperado +- [ ] `make check` verde en el repo restaurado +- [ ] Primer backup nuevo generado y verificado desde la Mac