MyFinAI · el sistema operativo financiero con IA← Documentación
Documentación técnica · v1.0.0 · actualizado 2026-06-09 · ESENPT · ⬇ Descargar Markdown

Arquitectura

MyFinAI es un monolito FastAPI autocontenido sobre PostgreSQL que *extrae* patrones probados de los demás sistemas Lopettia en vez de acoplarse a ellos: el mayor de doble entrada y la auditoría encadenada de AICOS, el RLS multi-tenant de Kairos, el BYOK Fernet+HKDF de Servitium, los agentes LAF de AIaaSAgents. Un solo servicio, una sola base de datos, sin dependencias entre productos.

El plano

Caddy (TLS, reverse proxy)
  ├── /            → sitio de marketing estático (/srv/myfinai)
  ├── /app, /app/* → la consola SPA (contenedor)
  └── /api/*       → la API FastAPI (contenedor)

Contenedor myfinai-api  ──►  PostgreSQL compartido
  (red lopettia_net)          rol myfinai_app (NOSUPERUSER, NOBYPASSRLS)
                              base de datos myfinai

El contenedor expone el puerto 8000 dentro de lopettia_net; Caddy enruta por ruta. El sitio de marketing vive como estático en el host (/opt/www/myfinai, que Caddy monta como /srv).

Una identidad, varios espacios

El modelo de datos separa quién eres de dónde trabajas:

household o platform.

Una persona puede pertenecer a varios espacios (Felipe es owner del café y homeowner del hogar). Al cambiar de espacio (/api/auth/switch) el sistema valida la membresía y reemite el token con el nuevo tenant_id y rol activos. El libro del negocio y el del hogar nunca se mezclan.

RLS: el aislamiento vive en la base de datos

Cada petición autenticada corre dentro de una transacción que fija dos variables de Postgres que las políticas RLS leen:

SELECT set_config('app.current_tenant_id', :tenant, true);
SELECT set_config('app.is_platform',       :flag,   true);

Como se usan con is_local = true (equivalente a SET LOCAL), se reinician al terminar la transacción y no se filtran entre conexiones del pool. Todas las tablas con datos de inquilino llevan FORCE ROW LEVEL SECURITY, y la aplicación se conecta con un rol sin BYPASSRLS: ni un error de la aplicación puede ver otro inquilino. La capa de identidad (login, switch) usa una sesión raw_db sin contexto de inquilino, y nunca consulta datos de negocio.

El libro de doble entrada

Toda operación financiera se registra como un asiento (journal_entries) con sus líneas (journal_lines), cada una débito o crédito. El sistema rechaza un asiento que no cuadre, así que la balanza de comprobación (∑DR = ∑CR) está cuadrada por construcción, no por revisión posterior. Los tableros (caja, margen, resultado) se calculan desde el libro en cada carga; no hay cifras paralelas.

Capas extraídas

CapaArchivoOrigen del patrón
Configuraciónapp/config.pyEducatio
Sesiones RLSapp/db.pyKairos
Identidad / JWTapp/security.pyServitium
BYOK (cifrado de llaves)app/crypto.pyServitium
Auth + workspacesapp/routers/auth.pynuevo (memberships)
Dashboards por personaapp/routers/dash.pynuevo
Superficies de datosapp/routers/data.pynuevo
Agente Norteapp/routers/agent.pyAIaaSAgents (LAF)

El esquema y las políticas viven en api/migrations/V001…V005. Para desplegar se usa el patrón operador-laptop de Lopettia (api/infra/scripts/deploy.sh), nunca runners de CI.