# 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