ImagSign — Modelo de Datos
Base de Datos
- Nombre:
imagy_sign - Usuario:
imagy_sign_app(NOBYPASSRLS) - RLS: Habilitado en todas las tablas con
tenant_id
Diagrama ER
Tablas
signature_requests
Solicitud de firma sobre uno o mas documentos. Representa la unidad principal de trabajo.
| Columna | Tipo | Descripcion |
|---|---|---|
id | UUID PK | Identificador unico |
tenant_id | UUID | Tenant propietario (RLS) |
organization_id | UUID | Organizacion (nullable) |
status | VARCHAR(30) | created, pending_signature, signing_in_progress, signed, rejected, expired, revoked |
signature_type | VARCHAR(30) | digital_certificate, electronic_otp, electronic_biometric, electronic_acceptance |
provider_code | VARCHAR(100) | Codigo del proveedor a usar |
signing_token | VARCHAR(255) UNIQUE | Token para acceso publico del firmante |
signing_url | TEXT | URL completa para el firmante |
signer_name | VARCHAR(255) | Nombre del firmante |
signer_email | VARCHAR(255) | Email del firmante (nullable) |
signer_phone | VARCHAR(50) | Telefono del firmante (nullable) |
signer_identifier | VARCHAR(100) | Documento de identidad del firmante |
callback_url | TEXT | URL para notificar resultado (nullable, usado por ImagFlow/ImagLend) |
origin_service | VARCHAR(100) | Servicio que origino la solicitud: standalone, imagflow, imaglend |
origin_request_id | UUID | ID de la solicitud en el servicio origen (nullable) |
metadata | JSONB | Metadata adicional (canal, contexto) |
row_version | INT | Concurrencia optimista |
created_at | TIMESTAMPTZ | Fecha de creacion |
expires_at | TIMESTAMPTZ | Fecha de expiracion |
completed_at | TIMESTAMPTZ | Fecha de completado (nullable) |
created_by | UUID | Actor que creo la solicitud |
documents
Documentos asociados a una solicitud de firma. Se almacenan como referencias a S3, no inline.
| Columna | Tipo | Descripcion |
|---|---|---|
id | UUID PK | Identificador unico |
signature_request_id | UUID FK | Solicitud de firma padre |
status | VARCHAR(20) | uploaded, pending_signature, signed, failed |
file_name | VARCHAR(500) | Nombre original del archivo |
content_type | VARCHAR(100) | MIME type (ej: application/pdf) |
s3_bucket | VARCHAR(255) | Bucket de S3 |
s3_key_original | VARCHAR(1000) | Key del documento original en S3 |
s3_key_signed | VARCHAR(1000) | Key del documento firmado en S3 (nullable, se llena al firmar) |
file_size_bytes | BIGINT | Tamano del archivo en bytes |
checksum_sha256 | VARCHAR(64) | Hash SHA-256 del documento original |
document_order | INT | Orden del documento en la solicitud |
created_at | TIMESTAMPTZ | Fecha de subida |
signed_at | TIMESTAMPTZ | Fecha en que se firmo (nullable) |
Estrategia de almacenamiento:
- Los documentos se almacenan en S3 con server-side encryption (SSE-S3).
- La base de datos solo guarda referencias (bucket + key), nunca el contenido binario.
- El key sigue el patron:
tenants/{tenant_id}/signatures/{request_id}/original/{document_id}.pdf - El documento firmado se almacena en:
tenants/{tenant_id}/signatures/{request_id}/signed/{document_id}.pdf - Se genera una URL pre-firmada (presigned URL) con TTL de 15 minutos para descarga.
signature_providers
Configuracion de proveedores de firma disponibles.
| Columna | Tipo | Descripcion |
|---|---|---|
id | UUID PK | Identificador unico |
provider_code | VARCHAR(100) UNIQUE | Codigo unico del proveedor |
provider_name | VARCHAR(255) | Nombre legible |
signature_type | VARCHAR(30) | digital_certificate, electronic_otp, electronic_biometric, electronic_acceptance |
country | VARCHAR(5) | Codigo ISO del pais (ej: EC, CO) |
connection_config | JSONB | Configuracion de conexion (encrypted via KMS) |
is_active | BOOLEAN | Si esta activo |
row_version | INT | Concurrencia optimista |
created_at | TIMESTAMPTZ | Fecha de creacion |
Nota: connection_config contiene API keys, endpoints y secrets del proveedor. Se almacena cifrado con KMS y solo el Signing Engine puede descifrarlo.
signature_certificates
Informacion del certificado digital usado en firmas de tipo digital_certificate.
| Columna | Tipo | Descripcion |
|---|---|---|
id | UUID PK | Identificador unico |
signature_request_id | UUID FK | Solicitud de firma asociada |
provider_code | VARCHAR(100) | Proveedor que emitio/uso el certificado |
certificate_serial | VARCHAR(255) | Numero serial del certificado |
certificate_issuer | VARCHAR(500) | Autoridad certificadora emisora |
certificate_subject | VARCHAR(500) | Sujeto del certificado (nombre del firmante) |
certificate_valid_from | TIMESTAMPTZ | Inicio de validez del certificado |
certificate_valid_to | TIMESTAMPTZ | Fin de validez del certificado |
signature_algorithm | VARCHAR(100) | Algoritmo usado (ej: RSA-SHA256) |
signed_hash | TEXT | Hash firmado del documento |
provider_metadata | JSONB | Metadata adicional del proveedor |
created_at | TIMESTAMPTZ | Fecha de registro |
signature_audit
Registro inmutable de todas las acciones sobre una solicitud de firma.
| Columna | Tipo | Descripcion |
|---|---|---|
id | UUID PK | Identificador unico |
signature_request_id | UUID FK | Solicitud de firma asociada |
action | VARCHAR(50) | request_created, document_uploaded, signing_started, otp_sent, otp_verified, signature_executed, signature_completed, signature_rejected, request_expired, request_revoked |
actor_type | VARCHAR(20) | operator, signer, system, provider |
actor_id | VARCHAR(255) | Identificador del actor |
ip_address | VARCHAR(45) | IP del actor (nullable) |
user_agent | VARCHAR(500) | User-Agent del navegador (nullable) |
details | JSONB | Detalles adicionales de la accion |
created_at | TIMESTAMPTZ | Timestamp de la accion |
Tablas de Soporte
idempotency_keys
sql
CREATE TABLE idempotency_keys (
key VARCHAR(255) NOT NULL,
tenant_id UUID NOT NULL,
response_status INT NOT NULL,
response_body JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + INTERVAL '24 hours',
PRIMARY KEY (key, tenant_id)
);processed_events
sql
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
sql
-- Todas las tablas principales tienen RLS por tenant_id
ALTER TABLE signature_requests ENABLE ROW LEVEL SECURITY;
ALTER TABLE documents ENABLE ROW LEVEL SECURITY;
ALTER TABLE signature_certificates ENABLE ROW LEVEL SECURITY;
ALTER TABLE signature_audit ENABLE ROW LEVEL SECURITY;
-- Politica de aislamiento por tenant
CREATE POLICY tenant_isolation ON signature_requests
FOR ALL USING (tenant_id = get_current_tenant_id());
CREATE POLICY tenant_isolation ON documents
FOR ALL USING (
signature_request_id IN (
SELECT id FROM signature_requests
WHERE tenant_id = get_current_tenant_id()
)
);
CREATE POLICY tenant_isolation ON signature_certificates
FOR ALL USING (
signature_request_id IN (
SELECT id FROM signature_requests
WHERE tenant_id = get_current_tenant_id()
)
);
CREATE POLICY tenant_isolation ON signature_audit
FOR ALL USING (
signature_request_id IN (
SELECT id FROM signature_requests
WHERE tenant_id = get_current_tenant_id()
)
);
-- Tabla global (sin RLS): signature_providers
-- Razon: configurada por platform admin, no pertenece a un tenant
-- Control plane (platform admin sin tenant context)
CREATE POLICY control_plane ON signature_requests
FOR ALL USING (
current_setting('app.current_tenant_id', true) IS NULL
OR current_setting('app.current_tenant_id', true) = ''
);Indices
sql
-- Busqueda de solicitudes
CREATE INDEX idx_signature_requests_tenant_status ON signature_requests(tenant_id, status);
CREATE INDEX idx_signature_requests_signing_token ON signature_requests(signing_token);
CREATE INDEX idx_signature_requests_origin ON signature_requests(origin_service, origin_request_id);
CREATE INDEX idx_signature_requests_expires ON signature_requests(expires_at)
WHERE status IN ('created', 'pending_signature', 'signing_in_progress');
CREATE INDEX idx_signature_requests_signer ON signature_requests(tenant_id, signer_identifier);
-- Busqueda de documentos
CREATE INDEX idx_documents_request ON documents(signature_request_id);
CREATE INDEX idx_documents_status ON documents(status);
-- Busqueda de certificados
CREATE INDEX idx_certificates_request ON signature_certificates(signature_request_id);
-- Auditoria
CREATE INDEX idx_audit_request ON signature_audit(signature_request_id);
CREATE INDEX idx_audit_created ON signature_audit(created_at);
-- Proveedores
CREATE UNIQUE INDEX idx_providers_code ON signature_providers(provider_code);
-- Limpieza
CREATE INDEX idx_idempotency_expires ON idempotency_keys(expires_at);
CREATE INDEX idx_processed_events_date ON processed_events(processed_at);Estrategia de Almacenamiento de Documentos (S3)
Estructura de Keys
tenants/
{tenant_id}/
signatures/
{request_id}/
original/
{document_id}.pdf
signed/
{document_id}.pdfPoliticas de S3
| Politica | Valor |
|---|---|
| Encryption | SSE-S3 (AES-256) |
| Versionamiento | Habilitado |
| Lifecycle | Mover a Glacier despues de 365 dias |
| Access | Solo via presigned URLs (15 min TTL) |
| CORS | Restringido a dominios de la plataforma |
| Bucket policy | Deny public access |
Flujo de Upload
- El cliente solicita una URL de upload (presigned PUT)
- ImagSign genera la URL con TTL de 10 minutos
- El cliente sube el archivo directamente a S3
- ImagSign recibe notificacion via S3 Event o el cliente confirma
- ImagSign registra el documento en la tabla
documentscon el checksum
Flujo de Download
- El cliente solicita descarga del documento (original o firmado)
- ImagSign valida permisos (RLS + rol)
- ImagSign genera presigned GET URL con TTL de 15 minutos
- El cliente descarga directamente de S3