Cliente: Centrica Soluciones
Proyecto: Nebula ERP — Componente Nebula Vault (DataVault)
Version: 1.0
Fecha: 16 de Abril, 2026
Autor: Carlos Alberto Torres Camargo — Arquitecto de Software
Clasificacion: Uso Interno — Equipo de Desarrollo
Estado: RESUELTO
El backend de DataVault presentaba fallos criticos de conectividad con el servidor OAIS tras la migracion a contenedores Docker. Se identificaron tres problemas independientes que, combinados, impedian el funcionamiento de multiples endpoints.
| Problema | Severidad | Estado |
|---|---|---|
| Conectividad SSH/SFTP rota (contenedores sin servidor SSH) | CRITICA | Resuelto |
| Modelo ORM Tenant con 15 relationships huerfanas | CRITICA | Resuelto |
| Codigo muerto referenciando funciones eliminadas | CRITICA | Resuelto |
DataVault fue disenado como un sistema de dos servidores fisicos:
+---------------------------+ SSH/SFTP +---------------------------+
| Servidor Backend | ----------------------> | Servidor OAIS |
| (192.168.0.16) | Paramiko (Python) | (192.168.0.33) |
| | | |
| FastAPI + PostgreSQL | | Watcher + Repositorio |
| Puerto 8000 | | /home/egs/datavault/ |
+---------------------------+ +---------------------------+
El backend se conectaba al servidor OAIS mediante:
tenants (campos server_url, server_config)Ambos servicios fueron containerizados y desplegados en nodo-01 dentro de la misma red Docker (dev-network):
+-------------------------------------------------------+
| nodo-01 (10.120.0.2) |
| |
| +-------------------+ +------------------------+ |
| | datavault-backend | | oais-watcher | |
| | (FastAPI) | | (Python daemon) | |
| | Puerto 8547:8000 | | | |
| +-------------------+ +------------------------+ |
| |
| Volumen: oais-repositorio |
| /home/egs/datavault/Repositorio (compartido) |
+-------------------------------------------------------+
Problema: Los contenedores Docker no ejecutan un servidor SSH interno. El backend seguia intentando conectar por SSH al contenedor OAIS, lo cual fallaba sistematicamente.
Sintoma: Cualquier operacion que involucrara el repositorio OAIS fallaba con error de conexion SSH.
Causa raiz: SFTPClient usaba paramiko.Transport(host, port) para conectar por SSH al servidor OAIS. En Docker, los contenedores:
Archivos afectados:
app/clients/sftp_client.py — constructor conecta a host:port via SSHapp/api/ingest.py — 6 puntos de instanciacion directa de SFTPClientapp/api/preservation.py — lectura de preservation_status.json via SFTPapp/api/cloud.py — ejecucion de comandos SSH para gestion de daemonsapp/services/oais_integration_service.py — operaciones sftp.stat(), sftp.open(), sftp.chmod()Alcance: 130+ lineas de codigo Paramiko distribuidas en 6 archivos.
Sintoma: Todos los endpoints autenticados retornaban HTTP 500 con error:
sqlalchemy.exc.InvalidRequestError: Tenant.user_tenants -
no foreign keys linking these tables via column "tenants.id"
Causa raiz: La migracion Alembic fada7e94b5eb (database-per-tenant) removio correctamente la columna tenant_id de 15 tablas. Sin embargo, el modelo ORM Tenant conservaba 15 declaraciones relationship() que referenciaban esas columnas FK ya inexistentes:
# app/models/tenant.py (ANTES - ROTO)
class Tenant(Base):
__tablename__ = "tenants"
# ... columnas ...
# 15 relationships huerfanas:
user_tenants = relationship("UserTenant", back_populates="tenant")
employees = relationship("Employee", back_populates="tenant")
contracts = relationship("Contract", back_populates="tenant")
# ... 12 mas ...
SQLAlchemy intentaba inicializar el mapper de Tenant al arrancar y fallaba porque las tablas referenciadas ya no tenian la columna tenant_id como FK.
Tablas afectadas (sin FK a tenants):
| # | Tabla | Migracion que removio FK |
|---|---|---|
| 1 | user_tenants |
fada7e94b5eb |
| 2 | employees |
fada7e94b5eb |
| 3 | contracts |
fada7e94b5eb |
| 4 | certifications |
fada7e94b5eb |
| 5 | trainings |
fada7e94b5eb |
| 6 | absences |
fada7e94b5eb |
| 7 | hr_documents |
fada7e94b5eb |
| 8 | notifications |
fada7e94b5eb |
| 9 | preservation_messages |
fada7e94b5eb |
| 10 | audit_log |
fada7e94b5eb |
| 11 | file_assignments |
fada7e94b5eb |
| 12 | module_permissions |
fada7e94b5eb |
| 13 | legal_holds |
fada7e94b5eb |
| 14 | retention_policies |
fada7e94b5eb |
| 15 | document_retentions |
fada7e94b5eb |
Sintoma: Endpoints de OAIS y preservacion crasheaban con NameError.
Causa raiz: Durante la limpieza de codigo se eliminaron funciones helper y bloques select(Tenant), pero quedaron llamadas huerfanas:
| Archivo | Lineas | Referencia rota |
|---|---|---|
oais_integration.py |
87, 124, 166 | _get_current_tenant_record(db) — funcion eliminada |
preservation.py |
204, 243, 804 | tenant — variable no definida (bloque select(Tenant) eliminado) |
Sintoma: Los endpoints de control de daemons (start/stop/status) fallaban.
Causa raiz: cloud.py usaba paramiko.SSHClient() + exec_command() para ejecutar comandos remotos como systemctl status datavault-watcher. En Docker:
systemd dentro de los contenedores| Modulo | Endpoints Afectados | Causa |
|---|---|---|
| Ingest | POST /projects, POST /upload, POST /process, GET /projects, POST /subfolders, POST /assign-file |
SFTP roto |
| Preservation | GET /status, GET /file, POST /diagnose, POST /repair, POST /mark-optimal, POST /verify |
SFTP + ORM + codigo muerto |
| OAIS Integration | GET /inspect, POST /apply-legal-hold, POST /remove-legal-hold, POST /apply-retention |
SFTP + ORM + codigo muerto |
| Cloud/Daemons | GET /daemon/status, POST /daemon/start, POST /daemon/stop, GET /daemon/logs, GET /daemon/stats |
SSH exec_command |
| Document Retention | POST /apply-physical, POST /dispose |
SFTP + ORM |
| Legal Hold | POST /apply-physical, POST /remove-physical |
SFTP + ORM |
Total: 23+ endpoints no funcionales de ~60 endpoints totales del backend.
| Modulo | Razon |
|---|---|
| Autenticacion | No usa SFTP ni Tenant model |
| Usuarios y Roles | Solo usa User/UserTenant (sin FK a Tenant) |
| Notificaciones | No usa SFTP |
| Health Check | Independiente |
| Opcion | Descripcion | Descartada por |
|---|---|---|
| A. Instalar SSH en contenedores OAIS | Agregar openssh-server al Dockerfile del watcher |
Anti-patron Docker. Agrega complejidad, superficie de ataque y overhead |
| B. Usar Docker network DNS | Conectar por SSH a oais-watcher:22 dentro de la red Docker |
Requiere SSH en contenedor. Mismos problemas que opcion A |
| C. Volumenes compartidos + Docker API | Acceso local al filesystem via volumen compartido. Docker API para gestion de contenedores | SELECCIONADA |
Los tres problemas identificados (SFTP roto, ORM corrupto, codigo muerto) fueron causados por la transicion de arquitectura de dos servidores fisicos a contenedores Docker, combinada con la migracion database-per-tenant. La solucion adoptada — patron Facade + Docker API — alinea la arquitectura del backend con la infraestructura real de despliegue.
Departamento de Arquitectura — Centrica Soluciones