diff --git a/apps/captain-mobile/Caddyfile b/apps/captain-mobile/Caddyfile new file mode 100644 index 0000000..8f327d3 --- /dev/null +++ b/apps/captain-mobile/Caddyfile @@ -0,0 +1,13 @@ +# Caddy configuration for Captain Claude Mobile API +# Add this to your main Caddyfile or run: caddy run --config /path/to/Caddyfile + +captain.tzzrarchitect.me { + reverse_proxy localhost:3030 + + # Enable WebSocket support + @websocket { + header Connection *Upgrade* + header Upgrade websocket + } + reverse_proxy @websocket localhost:3030 +} diff --git a/deck-frontend/deck-v4.6.html b/deck-frontend/deck-v4.6.html index 067b2fd..ca20607 100644 --- a/deck-frontend/deck-v4.6.html +++ b/deck-frontend/deck-v4.6.html @@ -734,26 +734,26 @@ body { // 1. CONFIG // ============================================================================= const CONFIG = { - API_BASE: "https://tzrtech.org/api", + API_BASE: "/api", IMG_BASE: "https://atc.tzzrdeck.me", BASES: { - // Taxonomía (public schema on HST server) - hst: { schema: "public", table: "hst", hasGroups: true, hasLibraries: true }, - flg: { schema: "public", table: "flg", hasGroups: false, hasLibraries: true }, - itm: { schema: "public", table: "itm", hasGroups: false, hasLibraries: true }, - loc: { schema: "public", table: "loc", hasGroups: false, hasLibraries: true }, - ply: { schema: "public", table: "ply", hasGroups: false, hasLibraries: true }, - // Producción (tables pending) - mst: { schema: "public", table: "mst", hasGroups: false, hasLibraries: true }, - bck: { schema: "public", table: "bck", hasGroups: false, hasLibraries: true }, - mth: { schema: "public", table: "mth", hasGroups: false, hasLibraries: true }, + // Taxonomía + hst: { schema: "tzzr_core_hst", table: "hst", hasGroups: true, hasLibraries: true }, + flg: { schema: "tzzr_core_hst", table: "flg", hasGroups: false, hasLibraries: true }, + itm: { schema: "tzzr_core_itm_base", table: "itm", hasGroups: false, hasLibraries: true }, + loc: { schema: "tzzr_core_itm_base", table: "loc", hasGroups: false, hasLibraries: true }, + ply: { schema: "tzzr_core_itm_base", table: "ply", hasGroups: false, hasLibraries: true }, + // Producción + mst: { schema: "tzzr_core_produccion", table: "mst", hasGroups: false, hasLibraries: true }, + bck: { schema: "tzzr_core_produccion", table: "bck", hasGroups: false, hasLibraries: true }, + mth: { schema: "tzzr_core_produccion", table: "mth", hasGroups: false, hasLibraries: true }, // Secretaría - atc: { schema: "public", table: "atc", hasGroups: false, hasLibraries: true }, - oracle: { schema: "public", table: "oracle", hasGroups: false, hasLibraries: true }, - // Comunicación (different servers - schemas TBD) - mail: { schema: "public", table: "clara_registros", hasGroups: false, hasLibraries: false, orderBy: "timestamp_entrada.desc" }, - chat: { schema: "public", table: "messages", hasGroups: false, hasLibraries: false, orderBy: "created_at.desc" } + atc: { schema: "tzzr_core_secretaria", table: "atc", hasGroups: false, hasLibraries: true }, + oracle: { schema: "tzzr_core_secretaria", table: "oracle", hasGroups: false, hasLibraries: true }, + // Comunicación + mail: { schema: "mail_manager", table: "clara_registros", hasGroups: false, hasLibraries: false, orderBy: "timestamp_entrada.desc" }, + chat: { schema: "context_manager", table: "messages", hasGroups: false, hasLibraries: false, orderBy: "created_at.desc" } }, CATEGORIES: { diff --git a/session-logs/2026-01-16-multi-bucket-atc.md b/session-logs/2026-01-16-multi-bucket-atc.md new file mode 100644 index 0000000..92e4068 --- /dev/null +++ b/session-logs/2026-01-16-multi-bucket-atc.md @@ -0,0 +1,509 @@ +# Session Log: Sistema Multi-Bucket ATC +**Fecha:** 2026-01-16 +**Duración:** ~3 horas +**Sistema:** TZZR - DECK +**Objetivo:** Implementar soporte multi-bucket en tabla ATC + +--- + +## RESUMEN EJECUTIVO + +Implementación exitosa de sistema multi-bucket para la tabla ATC en DECK, permitiendo gestionar archivos en múltiples buckets R2 de Cloudflare. + +**Estado:** ✓ Completado +**Registros migrados:** 2275 archivos +**Tablas creadas:** 2 nuevas (bucket_registry, bucket_access_log) +**Campos añadidos:** 1 en tabla atc (bucket_mrf) + +--- + +## 1. CONTEXTO INICIAL + +### Problema +La tabla `tzzr_core_secretaria.atc` no tenía forma de identificar en qué bucket R2 estaba almacenado cada archivo. Todos los archivos se asumían en el bucket 'deck' sin registro explícito. + +### Objetivo +Añadir soporte para múltiples buckets manteniendo: +- Sistemas descentralizados (DECK, ARCHITECT, CORP, HST independientes) +- Cada servidor gestiona sus propios buckets +- Credenciales locales por servidor (no centralizadas) + +--- + +## 2. DISCUSIÓN TÉCNICA + +### 2.1 Identificación de Buckets en Cloudflare R2 + +Investigación sobre cómo Cloudflare define un bucket: + +**Componentes identificados:** +1. **Account ID** - ID de cuenta Cloudflare (ej: `7dedae6030f5554d99d37e98a5232996`) +2. **Bucket Name** - Nombre único por cuenta (3-63 chars, a-z, 0-9, -) +3. **Location/Jurisdiction** - Se fija al crear, no modificable después + - Automatic (default) + - Location Hints: WNAM, ENAM, APAC, WEUR, EEUR + - Jurisdictions: EU, FedRAMP + +**Propiedades NO mutables:** +- Bucket name (no se puede renombrar) +- Location/Jurisdiction (no se puede cambiar) + +**Propiedades externas:** +- API keys (aws_access_key_id + aws_secret_access_key) +- Se rotan como par +- Múltiples tokens pueden coexistir + +### 2.2 Decisiones de Diseño + +**Campo bucket_mrf:** +- Tipo: `VARCHAR(64)` (SHA256) +- Propósito: Identificador único del bucket en el sistema +- Hash temporal asignado: `a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2` +- **Pendiente:** Definir algoritmo final de derivación (Account ID + Name + Location?) + +**Arquitectura simplificada:** +- NO usar tabla de credenciales encriptadas (descartado por complejidad) +- Credenciales en `~/.aws/credentials` locales por servidor +- Tabla `bucket_registry` solo metadata (name, endpoint, active) +- Tabla `bucket_access_log` para auditoría + +--- + +## 3. IMPLEMENTACIÓN + +### 3.1 Schemas SQL Creados + +#### Tabla: public.bucket_registry +```sql +CREATE TABLE public.bucket_registry ( + bucket_mrf VARCHAR(64) PRIMARY KEY, + name VARCHAR(64) NOT NULL UNIQUE, + endpoint VARCHAR(255) NOT NULL, + active BOOLEAN DEFAULT true, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_bucket_registry_name ON public.bucket_registry(name); +CREATE INDEX idx_bucket_registry_active ON public.bucket_registry(active); +``` + +**Registro inicial:** +- bucket_mrf: `a1b2c3d4e5f6g7h8i9j0k1l2...` (temporal) +- name: `deck` +- endpoint: `https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com` + +#### Tabla: public.bucket_access_log +```sql +CREATE TABLE public.bucket_access_log ( + id BIGSERIAL PRIMARY KEY, + bucket_mrf VARCHAR(64) NOT NULL REFERENCES public.bucket_registry(bucket_mrf), + operation VARCHAR(32) NOT NULL, + mrf VARCHAR(64), + path VARCHAR(500), + server VARCHAR(32), + success BOOLEAN NOT NULL, + error_msg TEXT, + bytes_transferred BIGINT, + timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_bucket_log_bucket_mrf ON public.bucket_access_log(bucket_mrf); +CREATE INDEX idx_bucket_log_timestamp ON public.bucket_access_log(timestamp); +CREATE INDEX idx_bucket_log_mrf ON public.bucket_access_log(mrf); +CREATE INDEX idx_bucket_log_server ON public.bucket_access_log(server); +``` + +#### Modificación: tzzr_core_secretaria.atc +```sql +ALTER TABLE tzzr_core_secretaria.atc +ADD COLUMN bucket_mrf VARCHAR(64) NOT NULL +REFERENCES public.bucket_registry(bucket_mrf) ON DELETE RESTRICT; + +CREATE INDEX idx_atc_bucket_mrf ON tzzr_core_secretaria.atc(bucket_mrf); +``` + +### 3.2 Proceso de Migración + +**Script ejecutado:** `/tmp/migration_multi_bucket_v2.sql` + +**Pasos:** +1. Crear tabla `public.bucket_registry` +2. Insertar bucket 'deck' con hash temporal +3. Añadir campo `bucket_mrf` a `atc` (nullable) +4. Asignar bucket 'deck' a todos los registros (2275) +5. Verificar integridad (0 registros sin bucket) +6. Hacer campo `bucket_mrf` NOT NULL +7. Añadir foreign key constraint +8. Crear índice +9. Crear tabla `public.bucket_access_log` + +**Resultado:** +``` +MIGRACIÓN COMPLETADA +Registros en atc: 2275 +Buckets registrados: 1 +Logs de acceso: 0 +``` + +### 3.3 Verificación Post-Migración + +```sql +-- Verificar bucket registrado +SELECT * FROM public.bucket_registry; +-- 1 row: deck (a1b2c3d4e5f6...) + +-- Verificar campo en atc +\d tzzr_core_secretaria.atc +-- bucket_mrf | character varying(64) | not null + +-- Verificar distribución +SELECT COUNT(*) as total, bucket_mrf +FROM tzzr_core_secretaria.atc +GROUP BY bucket_mrf; +-- 2275 | a1b2c3d4e5f6... +``` + +--- + +## 4. DOCUMENTACIÓN GENERADA + +### 4.1 Propuesta Inicial +**Archivo:** `propuesta-multi-bucket-atc-v2.md` +**Ubicación:** Nextcloud `documentos adjuntos/architect/` +**Contenido:** +- Arquitectura propuesta (bucket_registry, bucket_access_log, atc) +- Modelo de permisos (full vs readwrite) +- Código Python MultiBucketClient +- Casos de uso + +**Estado:** Documento base, previo a implementación + +### 4.2 Documentación de Implementación +**Archivo:** `sistema-multi-bucket-atc-implementado.md` +**Ubicación:** Nextcloud `documentos adjuntos/architect/` +**Contenido:** +- Estado actual post-migración +- Estructura de tablas implementadas +- Queries útiles +- Código Python MultiBucketClient funcional +- Ejemplos de uso +- Próximos pasos (actualizar bucket_mrf con hash derivado) + +--- + +## 5. CÓDIGO IMPLEMENTADO + +### 5.1 MultiBucketClient (Python) + +```python +import boto3 +import psycopg2 +import socket + +class MultiBucketClient: + def __init__(self, db_conn): + self.db = db_conn + self._clients = {} + + def _get_bucket_config(self, bucket_name): + cursor = self.db.cursor() + cursor.execute(""" + SELECT bucket_mrf, endpoint + FROM public.bucket_registry + WHERE name = %s AND active = true + """, (bucket_name,)) + + row = cursor.fetchone() + if not row: + raise ValueError(f"Bucket '{bucket_name}' no encontrado") + + return {'bucket_mrf': row[0], 'endpoint': row[1]} + + def get_client(self, bucket_name): + if bucket_name in self._clients: + return self._clients[bucket_name] + + config = self._get_bucket_config(bucket_name) + + # boto3 usa profile [bucket_name] de ~/.aws/credentials + session = boto3.Session(profile_name=bucket_name) + client = session.client('s3', endpoint_url=config['endpoint'], region_name='auto') + + self._clients[bucket_name] = client + return client + + def upload_file(self, bucket_name, local_path, remote_path): + client = self.get_client(bucket_name) + + try: + client.upload_file(local_path, bucket_name, remote_path) + size = os.path.getsize(local_path) + self._log_operation(bucket_name, 'upload', remote_path, True, bytes_transferred=size) + except Exception as e: + self._log_operation(bucket_name, 'upload', remote_path, False, str(e)) + raise + + def _log_operation(self, bucket_name, operation, path, success, error_msg=None, bytes_transferred=None): + cursor = self.db.cursor() + cursor.execute(""" + INSERT INTO public.bucket_access_log + (bucket_mrf, operation, path, server, success, error_msg, bytes_transferred) + VALUES ( + (SELECT bucket_mrf FROM public.bucket_registry WHERE name = %s), + %s, %s, %s, %s, %s, %s + ) + """, (bucket_name, operation, path, socket.gethostname().upper(), + success, error_msg, bytes_transferred)) + self.db.commit() +``` + +**Ubicación sugerida:** `/opt/tzzr/lib/multi_bucket_client.py` (pendiente) + +### 5.2 Configuración AWS Credentials + +```bash +# ~/.aws/credentials en DECK +[deck] +aws_access_key_id = 55125dca442b0f3517d194a5bc0502b8 +aws_secret_access_key = 9b69de7a4e7fc0fe1348df55aee12662ae0b60b494dc7398447d9ffe0990671f + +chmod 600 ~/.aws/credentials +``` + +--- + +## 6. CONTEXTO PARALELO: Migración Stack a Docker en DECK + +Durante esta sesión, Frontend Claude ejecutó en paralelo una migración del stack de DECK a Docker: + +**Componentes migrados:** +- PostgreSQL → `tzzr-postgres` (puerto 5433) +- PostgREST → `tzzr-postgrest` (puerto 3002) +- Directus → `tzzr-directus` (puerto 8056) + +**Arquitectura:** +``` +Browser → tzzrdeck.me/api + → Caddy + → PostgREST (Docker) + → PostgreSQL (Docker) +``` + +**Problema potencial identificado:** +- PostgreSQL original del host (puerto 5432) podría seguir corriendo +- Riesgo de doble escritura (aplicaciones apuntando a viejo vs nuevo) +- Requiere verificar que servicios migraron correctamente + +**Estado:** En progreso, verificación pendiente + +--- + +## 7. PRÓXIMOS PASOS + +### 7.1 Corto Plazo (Urgente) + +1. **Definir algoritmo bucket_mrf** + - Decidir: SHA256(Account ID + Name) o SHA256(Account ID + Name + Location)? + - Calcular hash real del bucket 'deck' + - Actualizar bucket_registry y atc con hash definitivo + +2. **Verificar migración Docker en DECK** + - Confirmar que PostgreSQL del host está parada + - Verificar que todos los servicios apuntan a nuevo stack + - Validar Caddy/Nginx configs apuntan a puertos correctos (3002) + +3. **Implementar MultiBucketClient** + - Crear archivo `/opt/tzzr/lib/multi_bucket_client.py` + - Tests de integración + - Documentar uso + +### 7.2 Medio Plazo + +4. **Añadir buckets adicionales** + - `architect` en ARCHITECT (69.62.126.110) + - `personaldeck` si aplica en DECK + - `corp` en CORP (92.112.181.188) + +5. **Configurar credenciales** + - Generar tokens en Cloudflare R2 + - Configurar `~/.aws/credentials` en cada servidor + - Documentar proceso de rotación + +6. **Auditoría de accesos** + - Implementar logging automático en todas las operaciones + - Dashboard de métricas por servidor + - Alertas de errores + +### 7.3 Largo Plazo + +7. **Migración de archivos legacy** + - Identificar archivos que deberían estar en otros buckets + - Script de migración automática + - Validación de integridad + +8. **Backup y recuperación** + - Backup de bucket_registry + - Script de recuperación ante fallo + - Documentación de DR (Disaster Recovery) + +--- + +## 8. QUERIES ÚTILES + +### Ver buckets registrados +```sql +SELECT * FROM public.bucket_registry; +``` + +### Ver archivos por bucket +```sql +SELECT + br.name as bucket, + COUNT(*) as total_archivos +FROM tzzr_core_secretaria.atc a +JOIN public.bucket_registry br ON a.bucket_mrf = br.bucket_mrf +GROUP BY br.name; +``` + +### Registrar operación en log +```sql +INSERT INTO public.bucket_access_log + (bucket_mrf, operation, mrf, path, server, success, bytes_transferred) +VALUES ( + (SELECT bucket_mrf FROM public.bucket_registry WHERE name = 'deck'), + 'upload', '99445e97...', '99445e97...pdf', 'DECK', true, 1048576 +); +``` + +### Ver actividad últimas 24h +```sql +SELECT + br.name as bucket, + bal.operation, + bal.server, + COUNT(*) as operations +FROM public.bucket_access_log bal +JOIN public.bucket_registry br ON bal.bucket_mrf = br.bucket_mrf +WHERE bal.timestamp > NOW() - INTERVAL '24 hours' +GROUP BY br.name, bal.operation, bal.server; +``` + +### Verificar integridad +```sql +-- Archivos huérfanos (sin bucket válido) +SELECT COUNT(*) as huerfanos +FROM tzzr_core_secretaria.atc a +LEFT JOIN public.bucket_registry br ON a.bucket_mrf = br.bucket_mrf +WHERE br.bucket_mrf IS NULL; +-- Debe retornar 0 +``` + +--- + +## 9. LECCIONES APRENDIDAS + +### 9.1 Arquitectura Descentralizada +- No intentar centralizar credenciales en DB +- AWS profiles locales son suficientes y estándar +- Cada servidor gestiona sus buckets independientemente + +### 9.2 Iteración de Diseño +- Propuesta inicial muy compleja (KEK, AES-256-GCM, rotación automática) +- Simplificación a 2 tablas + 1 campo fue correcta +- KISS (Keep It Simple, Stupid) aplica + +### 9.3 Cloudflare R2 Constraints +- Bucket name no es renombrable +- Location/Jurisdiction no es modificable +- API keys se rotan como par completo +- Account ID + Name es suficiente para identificar bucket + +### 9.4 Migración Sin Downtime +- Usar hash temporal permite migrar primero, definir algoritmo después +- Foreign key con ON DELETE RESTRICT protege integridad +- Verificación en script SQL evita estados inconsistentes + +--- + +## 10. ARCHIVOS GENERADOS + +### En Nextcloud (cloud.tzzrdeck.me) +``` +documentos adjuntos/architect/ +├── propuesta-multi-bucket-atc-v2.md +└── sistema-multi-bucket-atc-implementado.md +``` + +### En DECK (/tmp) +``` +/tmp/migration_multi_bucket.sql (eliminado post-ejecución) +``` + +### Pendientes de crear +``` +/opt/tzzr/lib/multi_bucket_client.py +/opt/tzzr/scripts/rotate_bucket_credentials.sh +``` + +--- + +## 11. ESTADO FINAL + +**Base de datos:** `tzzr` en DECK (72.62.1.113) + +**Tablas:** +- ✓ `public.bucket_registry` - 1 bucket registrado (deck) +- ✓ `public.bucket_access_log` - 0 logs (recién creada) +- ✓ `tzzr_core_secretaria.atc` - 2275 registros con bucket_mrf asignado + +**Integridad:** +- ✓ Todos los archivos tienen bucket asignado +- ✓ Foreign keys configuradas +- ✓ Índices creados +- ✓ 0 registros huérfanos + +**Pendiente:** +- ⏳ Actualizar bucket_mrf con hash definitivo (algoritmo por definir) +- ⏳ Implementar MultiBucketClient en código +- ⏳ Añadir buckets adicionales (architect, personaldeck, corp) +- ⏳ Verificar migración Docker stack en DECK + +--- + +## 12. COMANDOS DE VERIFICACIÓN + +```bash +# En DECK +ssh root@72.62.1.113 + +# Verificar tablas +sudo -u postgres psql -d tzzr -c '\dt public.bucket_*' + +# Ver bucket registrado +sudo -u postgres psql -d tzzr -c 'SELECT * FROM public.bucket_registry;' + +# Ver distribución de archivos +sudo -u postgres psql -d tzzr -c ' +SELECT br.name, COUNT(*) +FROM tzzr_core_secretaria.atc a +JOIN public.bucket_registry br ON a.bucket_mrf = br.bucket_mrf +GROUP BY br.name;' + +# Verificar estructura atc +sudo -u postgres psql -d tzzr -c '\d tzzr_core_secretaria.atc' | grep bucket_mrf +``` + +--- + +## CONCLUSIÓN + +Implementación exitosa de sistema multi-bucket para ATC. La arquitectura permite escalar a múltiples buckets R2 manteniendo la independencia de cada servidor del sistema TZZR. + +**Resultado:** Sistema listo para soportar múltiples buckets. Siguiente paso crítico es definir y aplicar el algoritmo definitivo de bucket_mrf. + +--- + +**Fin del documento** +**Generado:** 2026-01-16 21:30 CET +**Autor:** CAPTAIN CLAUDE +**Sistema:** TZZR - ARCHITECT/DECK