La seguridad de MyFinAI descansa en tres muros que no dependen de la disciplina de nadie: aislamiento por inquilino (RLS), aislamiento por rol, y trazas inviolables (auditoría encadenada + BYOK). Cada uno se verifica de forma continua y re-ejecutable.
Cada tabla con datos de inquilino lleva una política FORCE ROW LEVEL SECURITY sobre app.current_tenant_id, y la aplicación se conecta con el rol myfinai_app (NOSUPERUSER, NOBYPASSRLS). El libro de un negocio y el de un hogar son físicamente invisibles entre sí.
Probado en vivo: dos espacios —Café La Aurora y Hogar Marrero— con fuga cero en ambos sentidos. Sus conjuntos de transacciones son disjuntos, y un mismo login que cambia de espacio obtiene un libro distinto: café DR $91,216 ≠ hogar DR $6,360.
El RLS aísla *inquilinos*; los roles aíslan *personas dentro de un inquilino*. Los libros del negocio (movimientos, nómina, mayor, impuestos, auditoría) están detrás del nivel books (dueño · tenedor de libros · contador · jefe de hogar). El empleado y el miembro de familia quedan fuera: solo ven su propio recibo o su propia mesada.
Esto se impide en el servidor, no en la interfaz. Aunque un empleado pidiera
GET /api/data/payrolldirectamente, la respuesta es403. Lo mismo para Norte:
nadie saca los libros a través del asistente.
No puedes obtener un token para un espacio al que no perteneces. /api/auth/switch valida la membresía en el servidor: un usuario del café recibe 403 al intentar cambiar al hogar, y viceversa. La pertenencia se comprueba, no se asume.
Si un inquilino trae su propia llave de IA (BYOK), se guarda cifrada con Fernet + HKDF (llave maestra del entorno + sal por inquilino, info="myfinai-byok-v1"). Solo se guarda el texto cifrado; la maestra falla en cerrado y el texto plano nunca se registra ni se devuelve. Sin llave, los agentes deterministas siguen funcionando: la IA pule la respuesta, no es un requisito para que el sistema opere.
Cada acción relevante —apertura de período, decisión, aprovisionamiento, y lo que hace cada agente— se escribe en audit_log con el hash del evento anterior (prev_hash → hash). Alterar un registro pasado rompe la cadena y se nota. La auditoría cubre operaciones, no solo dinero.
Todo lo anterior se prueba con api/infra/audit_flows.py: 77 comprobaciones, 0 fallos contra el sistema en producción, cubriendo los siete roles, el cruce de inquilinos en ambos sentidos, y la reconciliación de ambos libros. El registro de hallazgos, correcciones y pendientes vive en api/AUDIT.md.
python api/infra/audit_flows.py # contra producción
BASE=http://localhost:8090 python … # contra una instancia local
La idea: la confianza no se pide, se demuestra. Cualquiera puede re-correr la
prueba y ver, en su pantalla, que los muros siguen en pie.