# CLARA y MARGARET - Agentes de Ingesta **Versión:** 5.0 **Fecha:** 2024-12-24 --- ## Resumen | Aspecto | CLARA | MARGARET | |---------|-------|----------| | Servidor | DECK (72.62.1.113) | CORP (92.112.181.188) | | Puerto | 5051 | 5051 | | Dominio | Personal | Empresarial | | Base de datos | tzzr | corp | | Tabla log | clara_log | margaret_log | | Bucket R2 | deck | corp | | Estado | Operativo | Operativo | --- ## Arquitectura ``` PACKET (App móvil) │ │ POST /ingest │ Content-Type: multipart/form-data │ X-Auth-Key: {h_instancia} │ ▼ ┌─────────────────────────────────────────┐ │ CLARA (Personal) / MARGARET (Corp) │ │ │ │ 1. Validar h_instancia │ │ 2. Calcular h_entrada (SHA-256) │ │ 3. Subir archivos a R2 │ │ 4. Insertar en log (inmutable) │ │ 5. Estado: 'recibido' │ │ 6. Responder con h_entrada │ └─────────────────────────────────────────┘ │ ▼ ALFRED / JARED (siguiente paso) ``` --- ## Endpoint de Ingesta ### Request ```http POST /ingest HTTP/1.1 Host: deck.example.com:5051 Content-Type: multipart/form-data X-Auth-Key: {h_instancia} --boundary Content-Disposition: form-data; name="tipo" documento --boundary Content-Disposition: form-data; name="archivo"; filename="doc.pdf" Content-Type: application/pdf {binary data} --boundary-- ``` ### Response ```json { "success": true, "h_entrada": "a1b2c3d4e5f6...", "estado": "recibido", "timestamp": "2024-12-24T12:00:00Z" } ``` --- ## Cálculo de h_entrada ```python import hashlib import json def calcular_h_entrada(h_instancia, tipo, contenido_bytes, timestamp): """ Calcula el hash único de entrada. h_entrada = SHA-256(h_instancia:tipo:sha256(contenido):timestamp) """ hash_contenido = hashlib.sha256(contenido_bytes).hexdigest() data = f"{h_instancia}:{tipo}:{hash_contenido}:{timestamp}" return hashlib.sha256(data.encode()).hexdigest() ``` --- ## Schema: clara_log / margaret_log ```sql CREATE TABLE clara_log ( id BIGSERIAL PRIMARY KEY, h_entrada VARCHAR(64) UNIQUE NOT NULL, h_instancia VARCHAR(64) NOT NULL, tipo VARCHAR(50) NOT NULL, contenido_url TEXT, -- locker://deck/{h_entrada}/archivo contenido_hash VARCHAR(64), -- SHA-256 del contenido metadata JSONB, estado VARCHAR(20) DEFAULT 'recibido', created_at TIMESTAMPTZ DEFAULT NOW(), processed_at TIMESTAMPTZ, processed_by VARCHAR(50) -- 'alfred' o 'manual' ); -- margaret_log tiene schema idéntico CREATE TABLE margaret_log ( id BIGSERIAL PRIMARY KEY, h_entrada VARCHAR(64) UNIQUE NOT NULL, h_instancia VARCHAR(64) NOT NULL, tipo VARCHAR(50) NOT NULL, contenido_url TEXT, contenido_hash VARCHAR(64), metadata JSONB, estado VARCHAR(20) DEFAULT 'recibido', created_at TIMESTAMPTZ DEFAULT NOW(), processed_at TIMESTAMPTZ, processed_by VARCHAR(50) ); ``` --- ## Estados del Log | Estado | Descripción | |--------|-------------| | recibido | Ingesta completada, pendiente procesamiento | | procesando | ALFRED/JARED está procesando | | enviado_mason | Enviado a MASON para enriquecimiento | | consolidado | Procesado por FELDMAN | | error | Error en procesamiento | --- ## Almacenamiento en R2 ### Estructura de URLs ``` locker://deck/{h_entrada}/archivo.ext locker://corp/{h_entrada}/archivo.ext ``` ### Implementación ```python import boto3 def subir_a_r2(bucket, h_entrada, archivo_bytes, filename): """Sube archivo a Cloudflare R2.""" s3 = boto3.client( 's3', endpoint_url='https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com', aws_access_key_id=R2_ACCESS_KEY, aws_secret_access_key=R2_SECRET_KEY ) key = f"{h_entrada}/{filename}" s3.put_object( Bucket=bucket, Key=key, Body=archivo_bytes ) return f"locker://{bucket}/{key}" ``` --- ## Validación de h_instancia ```python def validar_instancia(h_instancia): """ Valida que h_instancia exista y esté activa. TODO: Implementar tabla de instancias activas. Actualmente: Validación básica de formato. """ if not h_instancia: return False if len(h_instancia) != 64: return False # TODO: Consultar tabla de instancias return True ``` --- ## Tipos de Ingesta Soportados | Tipo | Descripción | Extensiones | |------|-------------|-------------| | documento | Documentos | pdf, doc, docx | | imagen | Imágenes | jpg, png, gif | | audio | Audio | mp3, wav, m4a | | video | Video | mp4, mov | | texto | Texto plano | txt, md | | json | Datos estructurados | json | --- ## Configuración ### Variables de Entorno ```bash # CLARA (.env en /opt/clara/) DB_HOST=localhost DB_PORT=5432 DB_NAME=tzzr DB_USER=clara DB_PASSWORD=**** R2_ENDPOINT=https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com R2_ACCESS_KEY=**** R2_SECRET_KEY=**** R2_BUCKET=deck # MARGARET (.env en /opt/margaret/) DB_HOST=localhost DB_PORT=5432 DB_NAME=corp DB_USER=margaret DB_PASSWORD=**** R2_BUCKET=corp ``` ### Docker Compose ```yaml # CLARA version: '3.8' services: clara: container_name: clara-clara image: tzzr/clara:latest ports: - "5051:5051" env_file: - .env restart: unless-stopped ``` --- ## Diferencias CLARA vs MARGARET | Aspecto | CLARA | MARGARET | |---------|-------|----------| | Uso | Personal | Empresarial | | Volumen | Bajo | Alto | | Privacidad | Máxima | Compartida (empresa) | | Integración | ALFRED | JARED → MASON → FELDMAN | | Backup | deck bucket | corp bucket | --- ## Seguridad ### Actual - Autenticación por h_instancia - Comunicación HTTP (sin TLS interno) - .env con permisos 644 (**CRÍTICO: cambiar a 600**) ### Recomendado - TLS interno con certificados - Rate limiting - Validación de tipos MIME - Escaneo antivirus (ClamAV) --- ## Monitoreo ### Logs ```bash # CLARA docker logs clara-clara -f # MARGARET docker logs margaret-margaret -f ``` ### Métricas Sugeridas - Ingestas por minuto - Tamaño promedio de archivos - Tiempo de procesamiento - Errores por tipo