ImagID — Modelo de Datos
Base de Datos
- Nombre:
imagy_subject - Usuario:
imagy_subject_app(NOBYPASSRLS) - RLS: Habilitado en todas las tablas excepto
subject_profiles
Diagrama ER
Tablas
subject_profiles (SIN RLS - Global)
Perfil unico y global de un sujeto. Una persona con un numero de documento tiene exactamente un registro aqui, sin importar cuantos tenants interactuen con ella.
| Columna | Tipo | Descripcion |
|---|---|---|
id | UUID PK | Identificador unico del sujeto |
identifier_type | VARCHAR(50) | Tipo de documento: cedula, pasaporte, nit, rut |
identifier_value | VARCHAR(100) | Numero de documento (unico por tipo) |
full_name | VARCHAR(500) | Nombre completo consolidado |
email | VARCHAR(255) | Email mas reciente conocido (nullable) |
phone | VARCHAR(50) | Telefono mas reciente conocido (nullable) |
identity_data | JSONB | Datos adicionales de identidad (fecha nacimiento, genero, etc.) |
first_seen_at | TIMESTAMPTZ | Primera vez que aparecio en la plataforma |
last_seen_at | TIMESTAMPTZ | Ultima interaccion en cualquier tenant |
row_version | INT | Concurrencia optimista |
created_at | TIMESTAMPTZ | Fecha de creacion |
updated_at | TIMESTAMPTZ | Ultima modificacion |
Constraint unico: UNIQUE(identifier_type, identifier_value)
Importante: Esta tabla NO tiene tenant_id y NO tiene RLS. Es el unico punto global del sistema. Los datos aqui son minimos y no sensibles desde perspectiva de negocio.
subject_tenant_views (RLS por tenant_id)
Vista materializada de un sujeto dentro de un tenant especifico. Contiene metricas agregadas y el trust score calculado para ese tenant.
| Columna | Tipo | Descripcion |
|---|---|---|
id | UUID PK | Identificador unico |
tenant_id | UUID | Tenant propietario (RLS) |
subject_id | UUID FK | Referencia al perfil global |
status | VARCHAR(30) | active, inactive, flagged, blacklisted |
trust_score | DECIMAL(5,2) | Puntaje de confianza 0.00 a 100.00 |
total_interactions | INT | Total de interacciones en este tenant |
total_approvals | INT | Flujos aprobados |
total_rejections | INT | Flujos rechazados |
total_defaults | INT | Creditos en default |
metrics_summary | JSONB | Metricas adicionales agregadas |
first_interaction_at | TIMESTAMPTZ | Primera interaccion en este tenant |
last_interaction_at | TIMESTAMPTZ | Ultima interaccion en este tenant |
row_version | INT | Concurrencia optimista |
created_at | TIMESTAMPTZ | Fecha de creacion |
updated_at | TIMESTAMPTZ | Ultima modificacion |
Constraint unico: UNIQUE(tenant_id, subject_id)
subject_events (RLS por tenant_id)
Timeline completo de eventos del sujeto dentro de un tenant. Cada evento consumido de otros dominios genera un registro aqui.
| Columna | Tipo | Descripcion |
|---|---|---|
id | UUID PK | Identificador unico |
tenant_id | UUID | Tenant (RLS) |
subject_id | UUID FK | Referencia al perfil global |
event_type | VARCHAR(100) | Tipo de evento original (ej: flow.execution.completed) |
event_source | VARCHAR(100) | Dominio origen (ej: imagy-flow-engine) |
source_event_id | UUID | ID del evento original (idempotencia) |
event_data | JSONB | Payload relevante del evento |
device_info | JSONB | Info de dispositivo si aplica (nullable) |
event_timestamp | TIMESTAMPTZ | Timestamp original del evento |
created_at | TIMESTAMPTZ | Cuando se registro en ImagID |
Constraint unico: UNIQUE(source_event_id) — garantiza idempotencia.
subject_devices (RLS por tenant_id)
Dispositivos desde los cuales un sujeto ha interactuado dentro de un tenant. Incluye dispositivos certificados (con keypair registrada para firma y autenticacion).
| Columna | Tipo | Descripcion |
|---|---|---|
id | UUID PK | Identificador unico |
tenant_id | UUID | Tenant (RLS) |
subject_id | UUID FK | Referencia al perfil global |
device_fingerprint | VARCHAR(255) | Fingerprint del dispositivo |
ip_address | INET | Direccion IP |
user_agent | TEXT | User agent del navegador |
geo_country | VARCHAR(100) | Pais de geolocalizacion |
geo_city | VARCHAR(200) | Ciudad de geolocalizacion |
geo_latitude | DECIMAL(10,7) | Latitud |
geo_longitude | DECIMAL(10,7) | Longitud |
access_count | INT | Numero de accesos desde este dispositivo |
is_certified | BOOLEAN | Si el dispositivo fue validado biometricamente |
public_key | TEXT | Clave publica del dispositivo (nullable, solo si certified) |
key_algorithm | VARCHAR(50) | Algoritmo de la clave (RSA-2048, EC-P256) |
certified_at | TIMESTAMPTZ | Cuando se certifico (nullable) |
certification_flow_id | UUID | Flujo de ImagFlow que valido el dispositivo (nullable) |
key_expires_at | TIMESTAMPTZ | Expiracion de la clave (nullable, renovacion periodica) |
is_revoked | BOOLEAN | Si la clave fue revocada |
revoked_at | TIMESTAMPTZ | Cuando se revoco (nullable) |
first_seen_at | TIMESTAMPTZ | Primera vez visto |
last_seen_at | TIMESTAMPTZ | Ultima vez visto |
created_at | TIMESTAMPTZ | Fecha de creacion |
Constraint unico: UNIQUE(tenant_id, subject_id, device_fingerprint)
Dispositivos certificados:
- Un dispositivo certificado tiene
is_certified = trueypublic_keyno null - La certificacion requiere validacion biometrica previa via ImagFlow
- La clave publica se usa para verificar firmas (ImagSign) y autenticacion (ImagAuth)
- Las claves pueden expirar y requieren renovacion con nueva validacion
- Un dispositivo revocado no puede usarse para firma ni autenticacion
subject_list_entries (RLS por tenant_id)
Entradas en listas de control (blacklist, whitelist, watchlist) por tenant.
| Columna | Tipo | Descripcion |
|---|---|---|
id | UUID PK | Identificador unico |
tenant_id | UUID | Tenant (RLS) |
subject_id | UUID FK | Referencia al perfil global |
list_type | VARCHAR(30) | blacklist, whitelist, watchlist |
reason | TEXT | Razon de inclusion |
added_by | UUID | Actor que agrego (usuario o sistema) |
added_by_type | VARCHAR(20) | user, system, rule |
metadata | JSONB | Datos adicionales (regla que lo agrego, evento trigger) |
added_at | TIMESTAMPTZ | Cuando se agrego |
expires_at | TIMESTAMPTZ | Expiracion automatica (nullable = permanente) |
removed_at | TIMESTAMPTZ | Cuando se removio (nullable = activo) |
removed_by | UUID | Quien lo removio (nullable) |
Nota: Un sujeto puede estar en multiples listas simultaneamente. La entrada activa es aquella donde removed_at IS NULL y (expires_at IS NULL o expires_at > NOW()).
subject_alerts (RLS por tenant_id)
Alertas generadas por deteccion de patrones sospechosos.
| Columna | Tipo | Descripcion |
|---|---|---|
id | UUID PK | Identificador unico |
tenant_id | UUID | Tenant (RLS) |
subject_id | UUID FK | Referencia al perfil global |
alert_type | VARCHAR(50) | multiple_rejections, new_device_high_risk, velocity_anomaly, geo_anomaly, default_pattern |
severity | VARCHAR(20) | low, medium, high, critical |
status | VARCHAR(20) | open, acknowledged, resolved, dismissed |
title | VARCHAR(255) | Titulo descriptivo de la alerta |
description | TEXT | Descripcion detallada |
alert_data | JSONB | Datos que dispararon la alerta |
resolved_by | UUID | Quien resolvio (nullable) |
resolution_notes | TEXT | Notas de resolucion (nullable) |
triggered_at | TIMESTAMPTZ | Cuando se detecto el patron |
resolved_at | TIMESTAMPTZ | Cuando se resolvio (nullable) |
created_at | TIMESTAMPTZ | Fecha de creacion |
Tablas de Soporte
processed_events
CREATE TABLE processed_events (
event_id UUID PRIMARY KEY,
event_type VARCHAR(100) NOT NULL,
processed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
source VARCHAR(100) NOT NULL
);RLS Policies
-- subject_profiles: SIN RLS (tabla global)
-- Razon: un sujeto es unico en toda la plataforma.
-- El acceso se controla a nivel de aplicacion, no de base de datos.
-- Todas las demas tablas: RLS habilitado
ALTER TABLE subject_tenant_views ENABLE ROW LEVEL SECURITY;
ALTER TABLE subject_events ENABLE ROW LEVEL SECURITY;
ALTER TABLE subject_devices ENABLE ROW LEVEL SECURITY;
ALTER TABLE subject_list_entries ENABLE ROW LEVEL SECURITY;
ALTER TABLE subject_alerts ENABLE ROW LEVEL SECURITY;
-- Politica estandar de aislamiento por tenant
CREATE POLICY tenant_isolation ON subject_tenant_views
FOR ALL USING (tenant_id = get_current_tenant_id());
CREATE POLICY tenant_isolation ON subject_events
FOR ALL USING (tenant_id = get_current_tenant_id());
CREATE POLICY tenant_isolation ON subject_devices
FOR ALL USING (tenant_id = get_current_tenant_id());
CREATE POLICY tenant_isolation ON subject_list_entries
FOR ALL USING (tenant_id = get_current_tenant_id());
CREATE POLICY tenant_isolation ON subject_alerts
FOR ALL USING (tenant_id = get_current_tenant_id());
-- Acceso global para el servicio interno (event ingestion)
-- El servicio usa un rol especial para escribir en subject_profiles
CREATE POLICY global_write ON subject_profiles
FOR ALL USING (true);
-- Politica para consultas cross-tenant (solo servicio interno)
CREATE POLICY service_cross_tenant ON subject_tenant_views
FOR SELECT USING (
current_setting('app.service_role', true) = 'imagy_subject_service'
);Indices
-- Busqueda de perfil por documento (consulta mas frecuente)
CREATE UNIQUE INDEX idx_subject_profiles_identifier
ON subject_profiles(identifier_type, identifier_value);
-- Busqueda de vista por tenant y sujeto
CREATE UNIQUE INDEX idx_tenant_views_tenant_subject
ON subject_tenant_views(tenant_id, subject_id);
-- Busqueda de vistas por estado (para dashboards)
CREATE INDEX idx_tenant_views_status
ON subject_tenant_views(tenant_id, status);
-- Timeline de eventos (paginacion por fecha)
CREATE INDEX idx_subject_events_timeline
ON subject_events(tenant_id, subject_id, event_timestamp DESC);
-- Idempotencia de eventos
CREATE UNIQUE INDEX idx_subject_events_source_id
ON subject_events(source_event_id);
-- Busqueda de dispositivos por fingerprint
CREATE UNIQUE INDEX idx_subject_devices_fingerprint
ON subject_devices(tenant_id, subject_id, device_fingerprint);
-- Dispositivos por IP (deteccion de patrones)
CREATE INDEX idx_subject_devices_ip
ON subject_devices(tenant_id, ip_address);
-- Listas activas (consulta critica para ImagLend e ImagFlow)
CREATE INDEX idx_list_entries_active
ON subject_list_entries(tenant_id, subject_id, list_type)
WHERE removed_at IS NULL;
-- Listas por tipo (para gestion administrativa)
CREATE INDEX idx_list_entries_type
ON subject_list_entries(tenant_id, list_type)
WHERE removed_at IS NULL;
-- Alertas abiertas (dashboard de operadores)
CREATE INDEX idx_alerts_open
ON subject_alerts(tenant_id, status, severity)
WHERE status IN ('open', 'acknowledged');
-- Alertas por sujeto
CREATE INDEX idx_alerts_subject
ON subject_alerts(tenant_id, subject_id, triggered_at DESC);
-- Limpieza de eventos procesados
CREATE INDEX idx_processed_events_date
ON processed_events(processed_at);Estrategia de Trust Score
El trust score es un valor entre 0.00 y 100.00 que representa la confianza en un sujeto dentro de un tenant especifico. Se recalcula de forma incremental con cada evento relevante.
Formula Base
trust_score = base_score + sum(factor_weights * factor_values)Factores de Calculo
| Factor | Peso | Efecto | Evento trigger |
|---|---|---|---|
| Flujo aprobado | +5.0 | Positivo | flow.execution.completed con result=approved |
| Flujo rechazado | -10.0 | Negativo | flow.execution.failed |
| Credito desembolsado | +3.0 | Positivo | lending.credit.disbursed |
| Pago recibido | +2.0 | Positivo | lending.payment.received |
| Credito en default | -25.0 | Negativo fuerte | lending.credit.defaulted |
| Dispositivo nuevo | -2.0 | Leve negativo | Primer acceso desde dispositivo desconocido |
| Multiples rechazos consecutivos | -15.0 | Negativo | 3+ rechazos sin aprobacion intermedia |
| Antiguedad (meses activo) | +1.0/mes | Positivo gradual | Calculado periodicamente |
Reglas de Calculo
- Score inicial: 50.00 (neutro)
- Minimo: 0.00 (no puede ser negativo)
- Maximo: 100.00
- Se almacena en
subject_tenant_views.trust_score - Se cachea en Valkey con TTL de 5 minutos para consultas frecuentes
- Key en Valkey:
tenant:{tenant_id}:subject:{subject_id}:trust_score
Umbrales de Accion
| Rango | Clasificacion | Accion automatica |
|---|---|---|
| 80 - 100 | Alta confianza | Whitelist automatica sugerida |
| 50 - 79 | Normal | Sin accion |
| 25 - 49 | Bajo | Watchlist automatica, alerta generada |
| 0 - 24 | Critico | Blacklist automatica, alerta critica |
Cache en Valkey
Datos frecuentemente consultados se cachean en Valkey:
{
"key": "tenant:{tenant_id}:subject:{subject_id}:trust_score",
"ttl": 300,
"value": {
"score": 72.50,
"status": "active",
"last_calculated_at": "2026-05-18T10:30:00Z"
}
}{
"key": "tenant:{tenant_id}:subject:{subject_id}:lists",
"ttl": 60,
"value": {
"blacklisted": false,
"whitelisted": false,
"watchlisted": true,
"checked_at": "2026-05-18T10:30:00Z"
}
}