fix: Enforce instance autonomy principle #2

Merged
admin merged 2 commits from system-v5 into main 2025-12-25 11:17:35 +00:00
4 changed files with 443 additions and 302 deletions

View File

@@ -101,5 +101,18 @@ VALORES → OBJETIVOS → IMÁGENES IA → CURACIÓN HUMANA → LO QUE SOBREVIVE
Cada instancia:
- Tiene su propio bucket de almacenamiento
- Puede renombrar sus agentes
- Opera de forma descentralizada
- Se conecta a servicios compartidos (GRACE, THE FACTORY, CIRCLE)
- **Opera de forma autónoma** (no depende de ARCHITECT en runtime)
- Tiene su propio gestor de secretos (Vaultwarden)
- Hace sus propios backups a R2
### Servicios Compartidos (Opcionales)
Las instancias **pueden** conectarse a servicios GPU compartidos:
| Servicio | Función | Requerido |
|----------|---------|-----------|
| GRACE | Extracción IA | Opcional |
| THE FACTORY | Generación | Opcional |
| CIRCLE | Colaboración | Opcional |
> **Nota:** Si los servicios compartidos no están disponibles, la instancia sigue operando. Solo las funciones de IA estarán limitadas.

View File

@@ -5,24 +5,37 @@
---
## Principio Fundamental
> **ARCHITECT es el constructor. DECK y CORP son instancias autónomas.**
- **ARCHITECT**: Construye, despliega, coordina. NO es dependencia runtime.
- **DECK/CORP**: Operan independientemente. Funcionan si ARCHITECT está caído.
---
## Diagrama General
```
┌─────────────────────────────────────────────────────────────────────────┐
CAPA DE COORDINACIÓN
│ CAPA DE CONSTRUCCIÓN (solo deploy/dev)
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ARCHITECT (69.62.126.110) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌────────────┐ ┌───────────────┐ │ │
│ │ │PostgreSQL│ │ Gitea │ │Orchestrator│ │ Infisical │ │ │
│ │ │ central │ │ 25 repos │ │ v5 │ │ Secrets │ │ │
│ │ │ contexto │ │ 25 repos │ │ v5 │ │ (master) │ │ │
│ │ └──────────┘ └──────────┘ └────────────┘ └───────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Rol: Construcción, deployment, gestión central de secretos │
│ NO es dependencia runtime de las instancias │
└─────────────────────────────────────────────────────────────────────────┘
│ │ │
deploy │ │ │ deploy
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────┐
CAPA DE SERVIDORES
│ CAPA DE INSTANCIAS (autónomas)
│ │
│ ┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐ │
│ │ DECK │ │ CORP │ │ HST │ │

View File

@@ -5,205 +5,266 @@
---
## Estado Actual: Triple Gestión
## Arquitectura de Secretos
**PROBLEMA CRÍTICO:** Actualmente existen tres fuentes de secretos sin sincronización.
### Principio Fundamental
> **DECK y CORP son instancias autónomas. Deben operar independientemente de ARCHITECT.**
ARCHITECT es el constructor/coordinador. DECK y CORP son instancias diseñadas para trabajar solas.
### Modelo de Gestión
```
┌─────────────────────────────────────────────────────────────────┐
FUENTES DE SECRETOS
ARCHITECT
│ (Constructor) │
│ │
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
Infisical creds_* .env │
│ (central) (PostgreSQL)│ │ (archivos) │
│ └─────────────┘ └─────────────┘ └─────────────┘
│ ▼ ▼ ▼ │
┌─────────────────────────────────────────────────────────┐
❌ SIN SINCRONIZACIÓN - Riesgo de inconsistencia │ │
└─────────────────────────────────────────────────────────┘
─────────────────────────────────────────────────────────────────┘
┌──────────────┐
│ Infisical │ ← Gestión centralizada
:8082 │ Single source of truth
└─────────────┘
│ │
└───────────────────────────┼─────────────────────────────────────┘
sync/push (en deploy o periódico)
────────────────────────────────
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ DECK │ │ CORP │
│ (Instancia) │ │ (Instancia) │
│ │ │ │
│ ┌──────────────┐ │ │ ┌──────────────┐ │
│ │ Vaultwarden │ │ │ │ Vaultwarden │ │
│ │ :8085 │ │ │ │ :8081 │ │
│ └──────┬───────┘ │ │ └──────┬───────┘ │
│ │ │ │ │ │
│ ▼ │ │ ▼ │
│ CLARA, ALFRED │ │ MARGARET, JARED │
│ leen de aquí │ │ MASON, FELDMAN │
│ │ │ leen de aquí │
└──────────────────────┘ └──────────────────────┘
```
### Gestores por Servidor
| Servidor | Gestor | Puerto | Rol |
|----------|--------|--------|-----|
| ARCHITECT | Infisical | 8082 | Gestión central, source of truth |
| DECK | Vaultwarden | 8085 | Operación autónoma DECK |
| CORP | Vaultwarden | 8081 | Operación autónoma CORP |
---
## Flujo de Secretos
### Administración (en ARCHITECT)
```
Administrador
┌─────────────┐
│ Infisical │ ← Crear/modificar/rotar secretos aquí
│ (central) │
└─────────────┘
```
### Sincronización (ARCHITECT → Instancias)
```
Infisical (ARCHITECT)
│ Script de sync / deploy
├────────────────┬────────────────┐
▼ ▼ ▼
Vaultwarden Vaultwarden (otras instancias
DECK CORP futuras)
```
### Operación (en cada instancia)
```
┌──────────────────────────────────────┐
│ DECK │
│ │
│ CLARA ──────► Vaultwarden ◄─────── ALFRED
│ :8085 │
│ │
│ ✅ No depende de ARCHITECT │
│ ✅ Opera si ARCHITECT está caído │
└──────────────────────────────────────┘
```
---
## Fuente 1: Infisical
## Estado Actual: Problema
**URL:** http://localhost:8082 (ARCHITECT)
**Estado:** Operativo
**Triple gestión sin sincronización:**
### Configuración
| Fuente | Ubicación | Estado |
|--------|-----------|--------|
| Infisical | ARCHITECT :8082 | Parcialmente usado |
| creds_* | PostgreSQL ARCHITECT | Activo |
| .env | /opt/*/.env en cada servidor | Activo, **permisos 644** |
```bash
# Acceso a Infisical
infisical login
infisical secrets --env=prod
```
### Ventajas
- Centralizado
- Versionado
- Auditable
- SDK para múltiples lenguajes
### Uso Actual
- Parcial - no todos los servicios lo usan
**Problemas:**
- Secretos duplicados en múltiples lugares
- Sin sincronización entre fuentes
- .env legibles por todos (644)
- Vaultwarden de DECK/CORP no integrados
---
## Fuente 2: Tablas creds_* (PostgreSQL)
**Base de datos:** architect
**Estado:** Activo
### Tablas
| Tabla | Contenido |
|-------|-----------|
| creds_ia | APIs de IA (OpenAI, Anthropic, etc.) |
| creds_runpod | API keys de RunPod |
| creds_r2 | Credenciales Cloudflare R2 |
| creds_gitea | Tokens Gitea |
| creds_mail | Credenciales SMTP |
| creds_apis | APIs externas varias |
### Schema Típico
```sql
CREATE TABLE creds_ia (
id SERIAL PRIMARY KEY,
servicio VARCHAR(50) NOT NULL,
api_key TEXT NOT NULL,
descripcion TEXT,
activo BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
```
### Acceso
```sql
-- Consultar credenciales
sudo -u postgres psql -d architect -c "SELECT servicio, descripcion FROM creds_ia;"
```
---
## Fuente 3: Archivos .env
**Ubicaciones:**
- /opt/clara/.env
- /opt/alfred/.env
- /opt/margaret/.env
- /opt/mason/.env
- /opt/feldman/.env
### ALERTA CRÍTICA
```
Permisos actuales: 644 (legibles por todos)
Permisos correctos: 600 (solo propietario)
```
### Contenido Típico
```bash
# Ejemplo .env
DB_HOST=localhost
DB_PORT=5432
DB_NAME=corp
DB_USER=margaret
DB_PASSWORD=secreto_aqui # ⚠️ Expuesto si 644
R2_ACCESS_KEY=access_key_aqui
R2_SECRET_KEY=secret_key_aqui # ⚠️ Expuesto si 644
```
---
## Decisión D-001: Migración a Infisical
## Decisión D-001: Arquitectura de Secretos
### Objetivo
Infisical como **única fuente de verdad** para todos los secretos.
### Plan de Migración
1. **Infisical** (ARCHITECT): Única fuente de verdad para administración
2. **Vaultwarden** (DECK/CORP): Gestores locales para operación autónoma
3. **Sync**: Mecanismo de propagación Infisical → Vaultwarden
### División de Responsabilidades
| Tipo de Secreto | Gestión en | Operación en |
|-----------------|------------|--------------|
| API keys externas (OpenAI, etc.) | Infisical | Vaultwarden local |
| R2 credentials | Infisical | Vaultwarden local |
| Gitea tokens | Infisical | Vaultwarden local |
| DB password DECK | Infisical | Vaultwarden DECK |
| DB password CORP | Infisical | Vaultwarden CORP |
| Tokens internos DECK | Infisical | Vaultwarden DECK |
| Tokens internos CORP | Infisical | Vaultwarden CORP |
### Flujo de Trabajo
```
Fase 1: Inventario
├── Catalogar todos los secretos en creds_*
├── Catalogar todos los secretos en .env
└── Identificar duplicados/inconsistencias
Fase 2: Centralización
├── Migrar todos los secretos a Infisical
├── Organizar por proyecto/ambiente
└── Establecer políticas de acceso
Fase 3: Actualización de Servicios
├── CLARA: Usar Infisical SDK
├── MARGARET: Usar Infisical SDK
├── MASON: Usar Infisical SDK
├── FELDMAN: Usar Infisical SDK
└── ALFRED/JARED: Usar Infisical SDK
Fase 4: Deprecación
├── Eliminar archivos .env
├── Marcar creds_* como "solo referencia"
└── Documentar proceso de rotación
```
### Implementación con SDK
```python
from infisical_client import InfisicalClient
client = InfisicalClient(
host="http://localhost:8082",
token=INFISICAL_TOKEN
)
# Obtener secreto
db_password = client.get_secret(
secret_name="DB_PASSWORD",
project_id="tzzr",
environment="production"
)
1. Admin modifica secreto en Infisical (ARCHITECT)
2. Script de sync detecta cambio
3. Propaga a Vaultwarden de DECK y/o CORP
4. Servicios locales usan Vaultwarden local
5. ARCHITECT puede caer → DECK/CORP siguen operando
```
---
## Rotación de Secretos
## Plan de Migración
### Política Propuesta
| Tipo | Frecuencia | Responsable |
|------|------------|-------------|
| API Keys externas | 90 días | Orchestrator |
| Contraseñas DB | 180 días | DBA |
| SSH Keys | 365 días | SysAdmin |
| Tokens Gitea | 180 días | DevOps |
### Proceso de Rotación
### Fase 1: Inventario
```
1. Generar nuevo secreto
2. Actualizar en Infisical
3. Desplegar servicios afectados
4. Verificar funcionamiento
5. Revocar secreto anterior
6. Documentar en changelog
├── Catalogar todos los secretos en creds_*
├── Catalogar todos los secretos en .env
├── Identificar duplicados/inconsistencias
└── Mapear qué secretos necesita cada instancia
```
### Fase 2: Centralización en Infisical
```
├── Migrar todos los secretos a Infisical
├── Organizar por instancia (DECK, CORP)
├── Establecer políticas de acceso
└── Documentar cada secreto
```
### Fase 3: Configurar Vaultwarden en Instancias
```
├── DECK: Configurar Vaultwarden :8085
│ ├── Crear organización "DECK"
│ ├── Importar secretos de DECK desde Infisical
│ └── Configurar acceso para servicios
└── CORP: Configurar Vaultwarden :8081
├── Crear organización "CORP"
├── Importar secretos de CORP desde Infisical
└── Configurar acceso para servicios
```
### Fase 4: Implementar Sync
```
├── Crear script de sincronización
│ └── infisical export → vaultwarden import
├── Configurar trigger (manual, cron, o webhook)
└── Probar sync en ambas direcciones
```
### Fase 5: Actualizar Servicios
```
├── CLARA: Leer de Vaultwarden DECK
├── ALFRED: Leer de Vaultwarden DECK
├── MARGARET: Leer de Vaultwarden CORP
├── JARED: Leer de Vaultwarden CORP
├── MASON: Leer de Vaultwarden CORP
└── FELDMAN: Leer de Vaultwarden CORP
```
### Fase 6: Deprecación
```
├── Eliminar archivos .env
├── Marcar creds_* como "solo referencia histórica"
└── Documentar proceso completo
```
---
## Implementación
### Lectura desde Vaultwarden (Python)
```python
import requests
VAULTWARDEN_URL = "http://localhost:8085" # DECK
# VAULTWARDEN_URL = "http://localhost:8081" # CORP
def get_secret(name: str) -> str:
"""Obtiene secreto de Vaultwarden local."""
# Implementar según API de Vaultwarden/Bitwarden
pass
```
### Script de Sync (Infisical → Vaultwarden)
```bash
#!/bin/bash
# /opt/scripts/sync_secrets.sh
# Exportar desde Infisical
infisical export --env=deck --format=json > /tmp/deck_secrets.json
infisical export --env=corp --format=json > /tmp/corp_secrets.json
# Importar a Vaultwarden DECK
ssh deck 'bw import --format json /tmp/deck_secrets.json'
# Importar a Vaultwarden CORP
ssh corp 'bw import --format json /tmp/corp_secrets.json'
# Cleanup
rm /tmp/*_secrets.json
```
---
## Mitigación Inmediata
### Paso 1: Permisos .env
### Paso 1: Permisos .env (URGENTE)
```bash
# Ejecutar en todos los servidores
# DECK
ssh -i ~/.ssh/tzzr root@72.62.1.113 'chmod 600 /opt/clara/.env /opt/alfred/.env'
@@ -211,82 +272,73 @@ ssh -i ~/.ssh/tzzr root@72.62.1.113 'chmod 600 /opt/clara/.env /opt/alfred/.env'
ssh -i ~/.ssh/tzzr root@92.112.181.188 'chmod 600 /opt/margaret/.env /opt/mason/.env /opt/feldman/.env'
```
### Paso 2: Auditoría
### Paso 2: Verificar Vaultwarden
```bash
# Verificar permisos
ls -la /opt/*/.env
# DECK
curl -s http://72.62.1.113:8085/api/health
# Buscar secretos expuestos
grep -r "password\|secret\|key" /opt/*/ 2>/dev/null
# CORP
curl -s http://92.112.181.188:8081/api/health
```
---
## Inventario de Secretos
## Rotación de Secretos
### APIs de IA
### Política
| Servicio | Ubicación Actual | Notas |
|----------|------------------|-------|
| OpenAI | creds_ia | GPT-4 |
| Anthropic | creds_ia | Claude |
| Replicate | creds_ia | Modelos varios |
| RunPod | creds_runpod | GRACE, PENNY, FACTORY |
| Tipo | Frecuencia | Responsable |
|------|------------|-------------|
| API Keys externas | 90 días | Admin vía Infisical |
| Contraseñas DB | 180 días | Admin vía Infisical |
| SSH Keys | 365 días | Admin vía Infisical |
| Tokens Gitea | 180 días | Admin vía Infisical |
### Infraestructura
### Proceso
| Servicio | Ubicación Actual | Notas |
|----------|------------------|-------|
| PostgreSQL | .env + creds_* | Múltiples usuarios |
| R2/Cloudflare | .env + creds_r2 | Access + Secret key |
| Gitea | .env + creds_gitea | Read + Write tokens |
| SMTP | creds_mail | Mailcow |
### Servicios Externos
| Servicio | Ubicación Actual | Notas |
|----------|------------------|-------|
| Stripe | creds_apis | (si aplica) |
| Twilio | creds_apis | (si aplica) |
| AWS | creds_apis | (si aplica) |
---
## Verificación de Seguridad
### Checklist Diario
- [ ] Permisos .env son 600
- [ ] No hay secretos en logs
- [ ] No hay secretos en commits git
- [ ] Infisical accesible
### Comandos de Verificación
```bash
# Verificar permisos
find /opt -name ".env" -exec ls -la {} \;
# Buscar secretos en git history
git log -p | grep -i "password\|secret\|key\|token"
# Verificar Infisical
curl -s http://localhost:8082/api/v1/health
```
1. Rotar en Infisical (ARCHITECT)
2. Ejecutar sync a Vaultwarden (DECK/CORP)
3. Verificar servicios operando
4. Documentar rotación
```
---
## Buenas Prácticas
## Verificación
### DO (Hacer)
- Usar Infisical para todos los secretos nuevos
- Rotar secretos según política
- Auditar accesos regularmente
- Cifrar secretos en reposo
### Checklist
### DON'T (No Hacer)
- Hardcodear secretos en código
- Commitear .env a repositorios
- Compartir secretos por chat/email
- Usar el mismo secreto en múltiples servicios
- [ ] Infisical operativo en ARCHITECT
- [ ] Vaultwarden operativo en DECK (:8085)
- [ ] Vaultwarden operativo en CORP (:8081)
- [ ] Sync configurado y probado
- [ ] Servicios leen de Vaultwarden local
- [ ] .env eliminados o con permisos 600
- [ ] DECK opera si ARCHITECT cae
- [ ] CORP opera si ARCHITECT cae
### Test de Autonomía
```bash
# 1. Detener ARCHITECT (simulado)
# 2. Verificar DECK sigue operando
curl http://72.62.1.113:5051/health # CLARA
# 3. Verificar CORP sigue operando
curl http://92.112.181.188:5051/health # MARGARET
```
---
## Resumen
| Componente | Rol |
|------------|-----|
| **Infisical (ARCHITECT)** | Gestión centralizada, source of truth |
| **Vaultwarden (DECK)** | Operación autónoma instancia personal |
| **Vaultwarden (CORP)** | Operación autónoma instancia empresarial |
| **Sync** | Propagación de cambios |
**Principio clave:** Las instancias (DECK, CORP) nunca dependen de ARCHITECT en runtime.

View File

@@ -5,6 +5,15 @@
---
## Principio Fundamental
> **Cada instancia es responsable de su propio backup.**
DECK y CORP son instancias autónomas. No dependen de ARCHITECT para hacer backups.
Cada servidor ejecuta su script de backup localmente y sube directamente a R2.
---
## Estado Actual
### Backups Existentes
@@ -20,94 +29,144 @@
---
## Plan de Backup Propuesto
## Arquitectura de Backups
### PostgreSQL - Backup Diario
```bash
#!/bin/bash
# /opt/scripts/backup_postgres.sh
set -e
DATE=$(date +%F)
BACKUP_DIR="/tmp/pg_backup"
# Cargar credenciales R2
source /home/orchestrator/orchestrator/.env
export AWS_ACCESS_KEY_ID="$R2_ACCESS_KEY"
export AWS_SECRET_ACCESS_KEY="$R2_SECRET_KEY"
R2_ENDPOINT="https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com"
mkdir -p $BACKUP_DIR
# Backup ARCHITECT
echo "Backing up ARCHITECT..."
sudo -u postgres pg_dump architect | gzip > $BACKUP_DIR/architect_$DATE.sql.gz
aws s3 cp $BACKUP_DIR/architect_$DATE.sql.gz s3://architect/backups/postgres/ \
--endpoint-url $R2_ENDPOINT
# Cleanup local
rm -rf $BACKUP_DIR
echo "Backup completado: $DATE"
```
### Cron Configuration
```bash
# /etc/cron.d/tzzr-backup
# Backup diario a las 3:00 AM
0 3 * * * orchestrator /opt/scripts/backup_postgres.sh >> /var/log/tzzr-backup.log 2>&1
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ ARCHITECT │ │ DECK │ │ CORP │
│ │ │ │ │ │
│ backup.sh ───┼────►│ backup.sh ───┼────►│ backup.sh ───┼────► R2
│ (local) │ │ (local) │ │ (local) │
└──────────────┘ └──────────────┘ └──────────────┘
│ │
▼ ▼
Sin dependencia Sin dependencia
de ARCHITECT de ARCHITECT
```
---
## Backup por Servidor
## Backup por Servidor (LOCAL)
### ARCHITECT (69.62.126.110)
**Ubicación script:** `/opt/scripts/backup_postgres.sh`
```bash
# Base de datos: architect
sudo -u postgres pg_dump architect | gzip > architect_$(date +%F).sql.gz
#!/bin/bash
# Ejecutar EN ARCHITECT - backup local
set -e
DATE=$(date +%F)
# Credenciales R2 (desde Vaultwarden local o .env)
source /opt/architect/.env
export AWS_ACCESS_KEY_ID="$R2_ACCESS_KEY"
export AWS_SECRET_ACCESS_KEY="$R2_SECRET_KEY"
R2_ENDPOINT="https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com"
# Backup local
sudo -u postgres pg_dump architect | gzip > /tmp/architect_$DATE.sql.gz
# Subir a R2
aws s3 cp architect_$(date +%F).sql.gz s3://architect/backups/postgres/ \
aws s3 cp /tmp/architect_$DATE.sql.gz s3://architect/backups/postgres/ \
--endpoint-url $R2_ENDPOINT
rm /tmp/architect_$DATE.sql.gz
echo "ARCHITECT backup completado: $DATE"
```
### DECK (72.62.1.113)
```bash
# Base de datos: tzzr
ssh deck 'sudo -u postgres pg_dump tzzr | gzip' > deck_tzzr_$(date +%F).sql.gz
**Ubicación script:** `/opt/scripts/backup_postgres.sh`
# Subir a R2
aws s3 cp deck_tzzr_$(date +%F).sql.gz s3://architect/backups/deck/ \
```bash
#!/bin/bash
# Ejecutar EN DECK - backup local (NO depende de ARCHITECT)
set -e
DATE=$(date +%F)
# Credenciales R2 (desde Vaultwarden DECK)
source /opt/deck/.env
export AWS_ACCESS_KEY_ID="$R2_ACCESS_KEY"
export AWS_SECRET_ACCESS_KEY="$R2_SECRET_KEY"
R2_ENDPOINT="https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com"
# Backup local
sudo -u postgres pg_dump tzzr | gzip > /tmp/deck_tzzr_$DATE.sql.gz
# Subir a R2 (bucket propio de DECK)
aws s3 cp /tmp/deck_tzzr_$DATE.sql.gz s3://deck/backups/postgres/ \
--endpoint-url $R2_ENDPOINT
rm /tmp/deck_tzzr_$DATE.sql.gz
echo "DECK backup completado: $DATE"
```
### CORP (92.112.181.188)
```bash
# Base de datos: corp
ssh corp 'sudo -u postgres pg_dump corp | gzip' > corp_$(date +%F).sql.gz
**Ubicación script:** `/opt/scripts/backup_postgres.sh`
# Subir a R2
aws s3 cp corp_$(date +%F).sql.gz s3://architect/backups/corp/ \
```bash
#!/bin/bash
# Ejecutar EN CORP - backup local (NO depende de ARCHITECT)
set -e
DATE=$(date +%F)
# Credenciales R2 (desde Vaultwarden CORP)
source /opt/corp/.env
export AWS_ACCESS_KEY_ID="$R2_ACCESS_KEY"
export AWS_SECRET_ACCESS_KEY="$R2_SECRET_KEY"
R2_ENDPOINT="https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com"
# Backup local
sudo -u postgres pg_dump corp | gzip > /tmp/corp_$DATE.sql.gz
# Subir a R2 (bucket propio de CORP)
aws s3 cp /tmp/corp_$DATE.sql.gz s3://corp/backups/postgres/ \
--endpoint-url $R2_ENDPOINT
rm /tmp/corp_$DATE.sql.gz
echo "CORP backup completado: $DATE"
```
### HST (72.62.2.84)
```bash
# Base de datos: hst_images
ssh hst 'sudo -u postgres pg_dump hst_images | gzip' > hst_$(date +%F).sql.gz
**Ubicación script:** `/opt/scripts/backup_postgres.sh`
# Subir a R2
aws s3 cp hst_$(date +%F).sql.gz s3://architect/backups/hst/ \
```bash
#!/bin/bash
# Ejecutar EN HST - backup local
set -e
DATE=$(date +%F)
source /opt/hst/.env
export AWS_ACCESS_KEY_ID="$R2_ACCESS_KEY"
export AWS_SECRET_ACCESS_KEY="$R2_SECRET_KEY"
R2_ENDPOINT="https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com"
sudo -u postgres pg_dump hst_images | gzip > /tmp/hst_$DATE.sql.gz
aws s3 cp /tmp/hst_$DATE.sql.gz s3://hst/backups/postgres/ \
--endpoint-url $R2_ENDPOINT
rm /tmp/hst_$DATE.sql.gz
echo "HST backup completado: $DATE"
```
---
## Cron en Cada Servidor
Cada instancia configura su propio cron:
```bash
# /etc/cron.d/tzzr-backup (en cada servidor)
# Backup diario a las 3:00 AM
0 3 * * * root /opt/scripts/backup_postgres.sh >> /var/log/backup.log 2>&1
```
---
@@ -157,26 +216,30 @@ docker exec gitea rm /tmp/gitea-dump-$DATE.zip
## Estructura de Backups en R2
Cada instancia usa su propio bucket:
```
s3://architect/backups/
├── postgres/
── architect_2024-12-24.sql.gz
│ ├── architect_2024-12-23.sql.gz
│ └── ...
├── deck/
│ ├── deck_tzzr_2024-12-24.sql.gz
│ └── ...
├── corp/
│ ├── corp_2024-12-24.sql.gz
│ └── ...
├── hst/
│ ├── hst_2024-12-24.sql.gz
│ └── ...
── architect_2024-12-24.sql.gz
└── gitea/
── gitea-dump-2024-12-24_0300.zip
└── ...
── gitea-dump-2024-12-24_0300.zip
s3://deck/backups/
└── postgres/
└── deck_tzzr_2024-12-24.sql.gz
s3://corp/backups/
└── postgres/
└── corp_2024-12-24.sql.gz
s3://hst/backups/
└── postgres/
└── hst_2024-12-24.sql.gz
```
> **Nota:** Cada instancia es dueña de sus backups. No hay dependencia cruzada.
---
## Retención de Backups