Saltar a contenido

ADR-028 · Modelo de identidad CIS — Personas/Empresas/Vínculos · bridge Authentik ↔ cis_admin

Fecha: 2026-04-29 Source: /srv/projects/cis/cis-plan/DECISIONS.md (do not edit here — re-split desde la fuente)


Contexto

Hallazgo del 2026-04-29: el modelo legal Personas/Empresas/Vínculos NO necesita ADR de diseño. Ya existe construido en cis_admin Postgres, hereado del cds-admin original migrado a CIS. El plan inicialmente lo trataba como gap a diseñar; el audit lo encontró ya implementado en 14 tablas con relaciones bien definidas.

Este ADR no diseñadocumenta el schema existente, formaliza el bridge con Authentik, declara las reglas anti-alucinación de RUT, y especifica el flow de onboarding de empleados con FES.

La distinción es importante: cualquier cambio al schema requiere migration Alembic + ADR nuevo. El schema actual es canon hasta que un ADR explícito lo modifique.

Alternativas consideradas

  1. (a) Diseñar modelo nuevo cleanroom
  2. Pro: schema teóricamente óptimo.
  3. Contra: ignora 2 años de iteración en cis_admin con datos reales (estatutos firmados, actas, contratos). Migration costosa. Riesgo de re-introducir bugs ya resueltos.

  4. (b) Documentar schema existente + bridge Authentik explícitopropuesta

  5. Pro: cero migration de datos. Captura conocimiento operativo en ADR. Permite a agentes nuevos razonar sobre el modelo sin abrir psql.
  6. Contra: el schema heredado tiene decisiones legacy (column names en castellano + JSONB metadata) que no son las que se elegirían cleanroom. Aceptable como costo.

  7. (c) Ignorar el modelo y hacer todo en JSON metadata

  8. Pro: flexibilidad.
  9. Contra: pierde queryability. Constraints SQL no aplican. Reportes contables imposibles.

Decisión

Schema canónico (existente en cis_admin DB, 14 tablas verificadas 2026-04-29):

empresas                  ← persona jurídica · RUT canon SII
  - id, rut (UNIQUE), razon_social, nombre_fantasia, tipo, giro,
    direccion, comuna, fecha_constitucion, regimen_tributario,
    estado, parent_id (FK self · grupo holding),
    capital_pagado, capital_suscrito, total_acciones,
    valor_nominal_accion, mutual_code, tasa_mutual, zona_extrema,
    actividad_economica, metadata (JSONB)

accionistas               ← FK empresa_id · persona × % participación
actas                     ← FK empresa_id · sesiones de directorio
contratos                 ← FK empresa_id · contratos generales
contratos_comerciales     ← FK empresa_id · acuerdos cliente/proveedor
cuentas_bancarias         ← FK empresa_id · banco + N° cuenta
declaraciones             ← FK empresa_id · F29, F22 SII
documentos                ← FK empresa_id · estatutos, poderes
firmas                    ← receipts FES de firmasydocumentos
obligaciones_tributarias  ← deadlines + status SII por empresa
sesiones_directorio       ← actas formales registradas
usuarios                  ← persona física (interno cis-admin)
usuario_empresa_acceso    ← M:N user × empresa con permisos
asientos                  ← entradas contables doble partida (FK empresa_id)

Hechos consumados:

  • empresas.parent_id (self-FK) ya soporta multi-tenant: parent = CIS, children = clientes externos cuando se active marketplace whitelabel (LH long-horizon).
  • CIS SpA es empresas.id = 968, RUT 78.384.591-1.
  • Sopapo tiene Authentik attribute empresas_acceso=[968] mapeando a CIS.

Bridge Authentik ↔ cis_admin.usuarios (formalización ADR-028)

  • Authentik User UUID = canónico (heredado de ADR-014). Es la sub del JWT.
  • cis_admin.usuarios.authentik_uuid UUID UNIQUE NULL (NULL durante transición; backfill flujo F.0).
  • Authentik User attributes mirror en cis_admin.usuarios:
  • rut TEXT (validado contra SII, ver reglas abajo)
  • cargo TEXT (Gerente General, Director, Empleado, Contador, etc.)
  • telefono TEXT
  • domicilio TEXT
  • profesion TEXT
  • rol_empresa TEXT (mapa al catalog §7.2 de CONSTITUTION)
  • email_corporativo TEXT (puede diferir de Authentik primary email)
  • usuario_empresa_acceso mapea: User × Empresa →
    rol TEXT,
    permisos TEXT[],
    fecha_inicio DATE,
    fecha_fin DATE NULL,  -- expira con contrato proyecto
    documento_id UUID FK documentos
    

Reglas anti-alucinación de RUT (canónicas)

Identificadores legales no se pueden alucinar. La constitución impone:

  1. RUT empresa → validado contra cis-sii-gateway /sii/contribuyente/datos. Lo que SII reconoce es canon. Si SII responde con razón social diferente a la que el user ingresó, prevalece SII y se actualiza cis_admin.empresas.razon_social.

  2. RUT persona → validado contra el mismo endpoint. Cualquier RUT chileno (números 1.000.000-K hasta 99.999.999-9 con dígito verificador correcto). Si el RUT no calcula DV correcto → rechazo en validación frontend.

  3. Sin coincidencia SII → el RUT queda como unverified. El vínculo en usuario_empresa_acceso no activa permisos privilegiados hasta que un FES vía firmasydocumentos.cl lo confirme. Estado intermedio Authentik attribute: rut: "PENDING_FES_VERIFICATION".

  4. RUT inventado por agente Claudeprohibido. Si Claudia genera un RUT en una conversación o doc, debe ser explícitamente prefijado RUT-PROPUESTO-NO-VERIFICADO y validado por humano antes de persistirse en DB. La DB tiene check constraint que rechaza inserts con RUTs no validados sin flag _unverified=true explícito.

Flow de onboarding agente empleado (canónico)

  1. Datos básicos: email + nombre + RUT.
  2. RUT validado contra cis-sii-gateway /sii/contribuyente/datos (paso obligatorio antes de continuar).
  3. Email único en Authentik (validation OIDC).
  4. Rol asignado según catalog §7.2 CONSTITUTION (Socio | Director | Representante legal | Empleado interno | Contador externo | Abogado externo | Periodista | Colaborador externo | Cliente | Proveedor).

  5. Documento generado: cis-admin/documentos/contratos/<año>-<id>.pdf con plantilla apropiada al rol (contrato laboral, mandato profesional, contrato proyecto, términos servicio).

  6. Firma FES: empleado firma vía firmasydocumentos.cl con factor segundo (OTP / SMS / TOTP / WebAuthn). Receipt (JWT firmado RS256) queda en tabla firmas con FK a documentos.id.

  7. Activación:

  8. Backend valida receipt (signature, expiry, audience).
  9. Crea Authentik User si no existe.
  10. Asigna group correspondiente al rol (cis-staff, cis-readonly-contable, cis-collab, etc.).
  11. Inserta row en cis_admin.usuarios con authentik_uuid set.
  12. Inserta row en usuario_empresa_acceso con {rol, permisos[], fecha_inicio, fecha_fin?, documento_id}.

  13. Notificación: cis-mailer envía recovery link al empleado con flow cis-primary-auth (Google primary + email OTP fallback, ADR-014).

  14. Audit log:

  15. Entry en cis_claudia.chat_tool_audits con tool=user_onboard, params={user, rol, empresa_id, documento_id}.
  16. Log en CHANNEL.md con tag [onboarding] <persona> · rol <rol> · receipt <fes_id>.

  17. Primera operación: la persona entra al panel correspondiente (admin.innovacionsantiago.cl para staff; indieweb.cl/billing para cliente/proveedor) con SSO transparente.

Permisos privilegiados activos solo post-FES: ningún row en usuario_empresa_acceso con permisos privilegiados (sudo, vault_set, db_write en cis-admin) se considera activo si no hay receipt FES asociado vía documento_id → firmas.documento_id. Verificación on-the-fly en cada autorización.

Consecuencias

  • Positivo: agentes nuevos entienden el modelo legal sin abrir psql. El flow de onboarding queda documentado y reproducible. Reglas anti-alucinación protegen contra agentes que inventan RUTs en docs. Receipt FES = evidencia legal de activación de cualquier rol privilegiado.
  • Negativo: el schema heredado tiene castellano + JSONB metadata. Queries cross-DB requieren conocer column names en castellano. Aceptable como costo histórico.
  • Riesgo: bug en cis-sii-gateway que produce false positive de validación RUT (acepta RUT inválido) propaga al modelo. Mitigación: el gateway está en T2 con monitoring; cualquier divergencia respecto a SII oficial dispara alerta a cis-inbox (ya implementado).
  • Migración:
  • Backfill cis_admin.usuarios.authentik_uuid para usuarios pre-Authentik vía email lower (flujo F.0).
  • Usuarios actuales (martin, sopapo) ya tienen authentik_uuid desde ADR-014. Sopapo ya tiene empresas_acceso=[968] (CIS).
  • Empresas actuales (CIS, CDS, LIBRO si aplica) presentes con RUT validado.
  • Compatibilidad: cualquier endpoint actual que consume cis_admin.usuarios sigue funcionando. Lo nuevo es la disciplina formal del flow + el bridge authentik_uuid.
  • Dependencia de ADR-008 + ADR-026: el flow requires firmasydocumentos.cl con sign endpoint productivo (T1 roadmap). Mientras esté en construcción, onboarding manual para superadmin (caso actual martin + sopapo) sigue válido como excepción documentada.
  • Dependencia de ADR-020: este ADR provee el modelo legal; ADR-020 provee el bridge técnico cross-service (JIT provisioning + cross-DB GRANT). Son complementarios.
  • Schema evolution: cualquier cambio a las 14 tablas (rename column, add constraint, drop column) requires migration Alembic en cis-admin/alembic/versions/ + ADR explícito que cite este ADR-028 como base.
  • Multi-tenant futuro: cuando active marketplace whitelabel (LH), parent_id = CIS, children = clientes permite isolation por scope. RBAC sobre usuario_empresa_acceso filtra por empresa.