Files
system-plan/PHASES/FASE_1_PIPELINE_MINIMO.md
ARCHITECT 73ae91d337 Auditoria completa y plan de implementacion TZZR
- ARCHITECTURE.md: Estado real de 23 repos
- IMPLEMENTATION_PLAN.md: 7 fases de implementacion
- PHASES/: Scripts detallados para cada fase

Resultado de auditoria:
- 5 repos implementados
- 4 repos parciales
- 14 repos solo documentacion
2025-12-24 08:59:14 +00:00

8.4 KiB

FASE 1: PIPELINE MÍNIMO VIABLE

Complejidad: Media Duración estimada: 2-3 días Prioridad: CRÍTICA


OBJETIVO

Establecer el flujo: PACKET → CLARA → PostgreSQL + R2

Esto permite que la app móvil envíe contenido y se almacene correctamente.


PREREQUISITOS

  • FASE 0 completada
  • SSH acceso a DECK (72.62.1.113)
  • R2 bucket 'deck' accesible
  • PostgreSQL en DECK funcionando
  • HST funcionando (tags)

PASO 1.1: Crear tablas de CLARA en DECK

Conectar a DECK

ssh -i /home/orchestrator/.ssh/tzzr root@72.62.1.113

Ejecutar SQL

sudo -u postgres psql -d tzzr << 'EOF'

-- Tabla principal de log
CREATE TABLE IF NOT EXISTS clara_log (
    id BIGSERIAL PRIMARY KEY,
    h_instancia VARCHAR(64) NOT NULL,
    h_entrada VARCHAR(64) NOT NULL,
    contenedor JSONB NOT NULL,
    r2_paths JSONB DEFAULT '{}',
    estado VARCHAR(20) DEFAULT 'recibido',
    procesado_at TIMESTAMP,
    created_at TIMESTAMP DEFAULT NOW(),
    CONSTRAINT clara_log_h_entrada_unique UNIQUE (h_entrada)
);

-- Índices para búsqueda eficiente
CREATE INDEX IF NOT EXISTS idx_clara_h_instancia ON clara_log(h_instancia);
CREATE INDEX IF NOT EXISTS idx_clara_estado ON clara_log(estado);
CREATE INDEX IF NOT EXISTS idx_clara_created ON clara_log(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_clara_contenedor ON clara_log USING gin(contenedor);

-- Comentarios
COMMENT ON TABLE clara_log IS 'Log inmutable de entrada - recibe contenedores de PACKET';
COMMENT ON COLUMN clara_log.h_instancia IS 'Hash SHA-256 que identifica la instancia DECK';
COMMENT ON COLUMN clara_log.h_entrada IS 'Hash SHA-256 único del contenedor';
COMMENT ON COLUMN clara_log.contenedor IS 'Contenedor completo en formato JSON (S-CONTRACT)';
COMMENT ON COLUMN clara_log.r2_paths IS 'Rutas de archivos subidos a R2';
COMMENT ON COLUMN clara_log.estado IS 'Estado: recibido, en_mason, en_feldman, consolidado';

-- Verificar
SELECT 'Tabla clara_log creada correctamente' AS resultado;
\dt clara_log
\d clara_log

EOF

Verificación

sudo -u postgres psql -d tzzr -c "SELECT COUNT(*) FROM clara_log;"
# Debe retornar 0 (tabla vacía)

Rollback

DROP TABLE IF EXISTS clara_log CASCADE;

PASO 1.2: Generar h_instancia para DECK

Concepto

h_instancia es un hash SHA-256 único que identifica esta instancia de DECK.

Generar

# En DECK
SEED="deck-personal-$(hostname)-$(date +%s)"
H_INSTANCIA=$(echo -n "$SEED" | sha256sum | cut -d' ' -f1)
echo "H_INSTANCIA=$H_INSTANCIA"

# Guardar en archivo de configuración
echo "H_INSTANCIA=$H_INSTANCIA" >> /opt/clara/.env

Verificación

El hash debe tener 64 caracteres hexadecimales.


PASO 1.3: Desplegar CLARA como Docker

Crear estructura en DECK

ssh -i /home/orchestrator/.ssh/tzzr root@72.62.1.113 << 'REMOTE'

mkdir -p /opt/clara
cd /opt/clara

# Clonar repo
git clone http://localhost:3000/tzzr/clara.git .

# Crear .env
cat > .env << 'EOF'
# Generado automáticamente
H_INSTANCIA=<PEGAR_HASH_GENERADO>

# PostgreSQL
DB_HOST=172.17.0.1
DB_PORT=5432
DB_NAME=tzzr
DB_USER=deck
DB_PASSWORD=<PASSWORD_DECK>

# Cloudflare R2
R2_ENDPOINT=https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com
R2_ACCESS_KEY=ecddc771824c3cb3417d9451780db3d2
R2_SECRET_KEY=c8138e2597100ffb7dd1477ad722c0214f86097cd968752aea3cfcea5d54dbac
R2_BUCKET=deck

# Puerto
PORT=5051
EOF

# Construir y levantar
docker-compose up -d --build

# Verificar
docker-compose logs --tail=20

REMOTE

docker-compose.yml actualizado

version: '3.8'

services:
  clara:
    build: .
    container_name: clara
    ports:
      - "5051:5051"
    env_file:
      - .env
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5051/health"]
      interval: 30s
      timeout: 10s
      retries: 3
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

networks:
  default:
    external:
      name: deck_network

Dockerfile

FROM python:3.11-slim

WORKDIR /app

# Dependencias del sistema
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    && rm -rf /var/lib/apt/lists/*

# Dependencias Python
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Código
COPY app.py .

# Puerto
EXPOSE 5051

# Ejecutar
CMD ["python", "app.py"]

Verificación

# Desde DECK
curl http://localhost:5051/health
# Debe retornar: {"service": "clara", "status": "ok", ...}

# Desde ARCHITECT
curl http://72.62.1.113:5051/health

PASO 1.4: Configurar Caddy para /ingest

Editar Caddyfile en DECK

ssh -i /home/orchestrator/.ssh/tzzr root@72.62.1.113 << 'REMOTE'

# Añadir a Caddyfile existente
cat >> /etc/caddy/Caddyfile << 'EOF'

# CLARA - API de ingesta
clara.tzzrdeck.me {
    reverse_proxy localhost:5051
}

# También en el dominio principal
tzzrdeck.me {
    handle /ingest* {
        reverse_proxy localhost:5051
    }
    # ... resto de configuración
}
EOF

# Recargar Caddy
systemctl reload caddy

REMOTE

Verificación

# HTTPS (si DNS configurado)
curl https://clara.tzzrdeck.me/health

# O directamente
curl http://72.62.1.113:5051/health

PASO 1.5: Probar ingesta completa

Test desde curl

# Definir variables
H_INSTANCIA="<hash_de_paso_1.2>"
TEST_HASH=$(echo -n "test-$(date +%s)" | sha256sum | cut -d' ' -f1)

# Enviar contenedor de prueba
curl -X POST http://72.62.1.113:5051/ingest \
  -H "Content-Type: application/json" \
  -H "X-Auth-Key: $H_INSTANCIA" \
  -d '{
    "id": "'$TEST_HASH'",
    "archivo_hash": "'$TEST_HASH'",
    "origen": {
      "app": "curl-test",
      "version": "1.0",
      "timestamp_captura": "'$(date -Iseconds)'"
    },
    "contenido": {
      "titulo": "Test de ingesta",
      "descripcion": "Contenedor de prueba desde curl"
    },
    "etiquetas": []
  }'

Respuestas esperadas

// Éxito
{"ok": true, "id": 1, "h_entrada": "abc123..."}

// Duplicado
{"error": "hash_exists"}

// Sin auth
{"error": "unauthorized"}

Verificar en PostgreSQL

ssh -i /home/orchestrator/.ssh/tzzr root@72.62.1.113 \
  "sudo -u postgres psql -d tzzr -c 'SELECT id, h_entrada, estado, created_at FROM clara_log ORDER BY id DESC LIMIT 5;'"

PASO 1.6: Probar subida a R2

Test con archivo

# Crear archivo de prueba
echo "Contenido de prueba" > /tmp/test.txt
TEST_DATA=$(base64 /tmp/test.txt)
TEST_HASH=$(echo -n "test-file-$(date +%s)" | sha256sum | cut -d' ' -f1)

curl -X POST http://72.62.1.113:5051/ingest \
  -H "Content-Type: application/json" \
  -H "X-Auth-Key: $H_INSTANCIA" \
  -d '{
    "id": "'$TEST_HASH'",
    "archivo_hash": "'$TEST_HASH'",
    "origen": {
      "app": "curl-test",
      "version": "1.0"
    },
    "archivos": [{
      "nombre": "test.txt",
      "tipo": "text/plain",
      "data": "'$TEST_DATA'"
    }]
  }'

Verificar en R2

# Usando AWS CLI configurado para R2
aws s3 ls s3://deck/ --endpoint-url https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com

CHECKLIST FINAL FASE 1

  • 1.1 - Tabla clara_log creada en DECK PostgreSQL
  • 1.2 - h_instancia generado y guardado
  • 1.3 - CLARA corriendo en Docker
  • 1.4 - Caddy configurado (opcional)
  • 1.5 - Test de ingesta exitoso
  • 1.6 - Archivo subido a R2
  • Health check responde correctamente
  • Logs sin errores

MÉTRICAS DE ÉXITO

Métrica Valor Esperado
/health responde < 100ms
/ingest (sin archivo) < 500ms
/ingest (con archivo) < 2s
Registros en clara_log > 0
Archivos en R2/deck > 0

TROUBLESHOOTING

CLARA no arranca

docker logs clara
# Verificar variables de entorno
docker exec clara env | grep -E "DB_|R2_"

No conecta a PostgreSQL

# Verificar red
docker exec clara ping -c1 172.17.0.1
# Verificar puerto
docker exec clara nc -zv 172.17.0.1 5432

Error R2

# Verificar credenciales
docker exec clara python -c "
import boto3
s3 = boto3.client('s3',
    endpoint_url='https://7dedae6030f5554d99d37e98a5232996.r2.cloudflarestorage.com',
    aws_access_key_id='ecddc771824c3cb3417d9451780db3d2',
    aws_secret_access_key='c8138e2597100ffb7dd1477ad722c0214f86097cd968752aea3cfcea5d54dbac'
)
print(s3.list_buckets())
"

SIGUIENTE FASE

Continuar con FASE_2_PROCESAMIENTO_IA.md