🔐 IMPLEMENTACIÓN TÉCNICA · OPEN SOURCE 🇨🇱 LEY 21.719 · PARTE 3/3

Ley 21.719: Cifrado, Consentimiento y Anonimización — Cómo implementarlo con stack open source

Guía técnica definitiva para pymes chilenas: arquitectura real, configuraciones funcionales y herramientas open source para cumplir con la nueva ley de protección de datos antes del 1 de diciembre 2026.

📅 2 Julio, 2026 ⏱ 18 min de lectura 📌 Saga: Ley 21.719
Ley 21.719: Cifrado, Consentimiento y Anonimización con stack open source
← Parte 1: DPO + AI Agent Parte 2: Marketing y PCC Parte 3: Implementación Técnica 👈

1. El problema: 5 meses para implementar lo que nadie sabe cómo hacer

En la Parte 1 de esta saga vimos que el DPO que las pymes chilenas necesitan no existe en el mercado laboral, y que un AI Agent especializado en compliance puede suplirlo por 40x menos costo.

En la Parte 2 recorrimos los 7 Puntos Críticos de Control (PCC) para campañas de marketing y prospección, con checklist de adecuación y tabla de bases legales.

Hoy cerramos la trilogía con lo que ninguna guía de compliance te dice: cómo implementar técnicamente cada exigencia de la Ley 21.719 usando herramientas open source que ya existen, que no cuestan licencias y que cualquier pyme con un VPS puede desplegar.

⚠️ El reloj corre

1 de diciembre de 2026. Faltan exactamente 5 meses para que la Fase 2 de la Ley 21.719 entre en vigencia plena: derechos ARCO+, consentimiento, DPO, evaluaciones de impacto. Las multas arrancan en 2027, pero las obligaciones empiezan a regir AHORA. No esperes a noviembre.

Lo que sigue no es teoría. Es código, configuraciones y arquitecturas que cualquier pyme puede desplegar hoy. Todo open source. Todo sin costos de licencia.

2. Sistema de consentimiento granular open source

La Ley 21.719 exige que el consentimiento sea libre, específico, informado e inequívoco (Art. 5 y siguientes). Esto implica:

Sistema de consentimiento con PostgreSQL + n8n

No necesitas un CRM enterprise ni un plataforma de consentimiento SaaS. Con PostgreSQL + n8n (del stack open source) puedes construir un sistema de consentimiento auditable en horas:

-- Esquema de consentimiento granular para Ley 21.719
CREATE TABLE consentimientos (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    titular_id UUID NOT NULL REFERENCES titulares(id),
    finalidad VARCHAR(100) NOT NULL,       -- 'marketing', 'analytics', 'prospeccion', 'compartir_terceros'
    base_legal VARCHAR(50) NOT NULL,       -- 'consentimiento', 'interes_legitimo', 'ejecucion_contractual'
    estado VARCHAR(20) NOT NULL DEFAULT 'activo',  -- 'activo', 'revocado', 'expirado'
    ip_origen INET,
    user_agent TEXT,
    medio VARCHAR(50),                     -- 'web_form', 'email', 'whatsapp', 'presencial'
    consentido_el TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    revocado_el TIMESTAMPTZ,
    expira_el TIMESTAMPTZ,
    evidencia_hash TEXT,                   -- hash del registro de consentimiento original
    CONSTRAINT consentimiento_unico UNIQUE (titular_id, finalidad)
);

-- Índices para consultas rápidas de la Agencia
CREATE INDEX idx_consentimientos_titular ON consentimientos(titular_id);
CREATE INDEX idx_consentimientos_estado ON consentimientos(estado);
CREATE INDEX idx_consentimientos_finalidad ON consentimientos(finalidad);

✅ Esto cumple con:

Art. 5 (Consentimiento), Art. 12 (Deber de información), Art. 14 (Derechos del titular). Cada consentimiento tiene trazabilidad completa: quién, cuándo, para qué, por qué medio, y si fue revocado.

API de consentimiento con FastAPI

Para que tus formularios web, APIs y sistemas puedan registrar consentimiento de forma estandarizada:

# microservicio-consentimiento/api.py
from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel
import hashlib, hmac
from datetime import datetime, timezone

app = FastAPI(title="API de Consentimiento - Ley 21.719")

class ConsentimientoRequest(BaseModel):
    titular_email: str
    finalidad: str
    base_legal: str = "consentimiento"
    medio: str
    acepta_terminos: bool

@app.post("/v1/consentimiento")
async def registrar_consentimiento(data: ConsentimientoRequest, request: Request):
    if not data.acepta_terminos:
        raise HTTPException(400, "El consentimiento no fue otorgado")
    
    # Generar evidencia hash inmutable
    evidencia_raw = f"{data.titular_email}|{data.finalidad}|{datetime.now(timezone.utc).isoformat()}|{data.medio}|{request.client.host}"
    evidencia_hash = hashlib.sha256(evidencia_raw.encode()).hexdigest()
    
    # Registrar en PostgreSQL (via n8n o directo)
    # ... insert into consentimientos ...
    
    return {
        "status": "ok",
        "evidencia_hash": evidencia_hash,
        "mensaje": "Consentimiento registrado exitosamente"
    }

@app.post("/v1/consentimiento/{titular_email}/revocar")
async def revocar_consentimiento(titular_email: str, finalidad: str = None):
    # Revocar todos o por finalidad específica
    # ... update consentimientos set estado='revocado', revocado_el=now() ...
    return {"status": "ok", "mensaje": "Consentimiento revocado"}

@app.get("/v1/consentimiento/{titular_email}")
async def obtener_consentimientos(titular_email: str):
    # Retorna todos los consentimientos del titular con su estado
    # ... select * from consentimientos where titular_email = ...
    return {"consentimientos": [...]}

🔧 Despliegue con n8n

Puedes conectar formularios web (Typeform, Google Forms, o HTML propio) → n8n → PostgreSQL sin escribir una línea de código. n8n maneja el webhook, la validación y el registro. Tiempo de implementación: 2-4 horas.

3. Cifrado en reposo y en tránsito con herramientas open source

La Ley 21.719 establece un deber de seguridad (Art. 15) que exige "medidas técnicas y organizativas apropiadas para garantizar la seguridad de los datos personales". En la práctica, esto se traduce en:

Cifrado en reposo con SeaweedFS + LUKS

Si usas SeaweedFS (como en el stack de Wagner Solutions) para almacenamiento de archivos, puedes habilitar cifrado a nivel de volumen:

# Configuración SeaweedFS con cifrado en reposo
# weed master - Volume Server con cifrado

# 1. Crear volumen cifrado con LUKS
sudo cryptsetup luksFormat /dev/sdb
sudo cryptsetup open /dev/sdb seaweed_crypt
sudo mkfs.ext4 /dev/mapper/seaweed_crypt

# 2. Montar y configurar SeaweedFS volume server
# weed volume -dir=/mnt/seaweed_crypt -mserver=master:9333 -port=8080

# 3. Cifrado a nivel de aplicación con AES-256-GCM
# En cada upload, el archivo se cifra antes de almacenarse
openssl enc -aes-256-gcm -salt -in archivo.pdf -out archivo.pdf.enc -pass file:/etc/keys/datos.key

Cifrado en PostgreSQL a nivel de columna

Para datos sensibles como RUT, email, teléfono, salud financiera — PostgreSQL tiene extensiones nativas:

-- Cifrado a nivel de columna con pgcrypto
CREATE EXTENSION IF NOT EXISTS pgcrypto;

-- Crear clave maestra de cifrado (almacenada fuera de la DB)
-- La clave se obtiene de HashiCorp Vault o variable de entorno segura

-- Datos sensibles cifrados con AES-256
CREATE TABLE datos_sensibles (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    titular_id UUID REFERENCES titulares(id),
    rut_cifrado BYTEA,          -- pgp_sym_encrypt('12345678-9', 'clave_maestra')
    email_cifrado BYTEA,        -- pgp_sym_encrypt('user@email.com', 'clave_maestra')
    telefono_cifrado BYTEA,
    datos_salud_cifrados BYTEA, -- cifrado con clave derivada
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Insertar con cifrado
INSERT INTO datos_sensibles (titular_id, rut_cifrado, email_cifrado)
VALUES (
    'uuid-del-titular',
    pgp_sym_encrypt('12.345.678-9', current_setting('app.encryption_key')),
    pgp_sym_encrypt('cliente@email.com', current_setting('app.encryption_key'))
);

-- Leer solo cuando sea necesario (y auditado)
-- SELECT pgp_sym_decrypt(rut_cifrado, current_setting('app.encryption_key')) FROM datos_sensibles WHERE id = ...;

⚠️ Importante: Gestión de claves

La clave de cifrado nunca debe estar en el mismo servidor que los datos cifrados. Usa HashiCorp Vault (open source) o un servicio de gestión de claves. Si pierdes la clave, pierdes los datos. La Ley 21.719 exige que demuestres que implementaste medidas técnicas — tener la clave separada de los datos es una de ellas.

Cifrado en tránsito: TLS 1.3 con Traefik

Traefik (el proxy inverso open source) maneja TLS 1.3 con Let's Encrypt automáticamente:

# docker-compose.yml - Traefik con TLS 1.3 forzado
services:
  traefik:
    image: traefik:v3.1
    command:
      - "--providers.docker=true"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.email=admin@tudominio.cl"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
      # Forzar TLS 1.3 exclusivamente
      - "--entrypoints.websecure.http.tls.options=modern@file"
    ports:
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./letsencrypt:/letsencrypt"
      - "./dynamic.yml:/dynamic.yml"  # Config TLS moderna

# dynamic.yml - Opciones TLS modernas (solo TLS 1.3)
tls:
  options:
    modern:
      minVersion: VersionTLS13
      sniStrict: true

🔐 Esto te cubre:

TLS 1.3 forzado en todas las comunicaciones → cifrado en tránsito. Certificados renovados automáticamente con Let's Encrypt. Sin costos. Sin mantenimiento manual. Listo para auditoría.

4. Anonimización y pseudonimización: la línea que separa dato personal de dato anónimo

La Ley 21.719 define dato personal como "cualquier información relativa a una persona natural identificada o identificable". Si un dato es anónimo (no permite identificar a la persona), no le aplica la ley. Esta es la clave estratégica: anonimizar bien es la mejor forma de cumplir.

Diferencia legal crítica

Técnica¿Reversible?¿Aplica Ley 21.719?Caso de uso
Anonimización🚫 Irreversible❌ No aplicaEstadísticas, BI, investigación
Pseudonimización🔑 Reversible con clave✅ Sí aplica (pero reduce riesgo)Producción, analytics con seudónimo
Cifrado🔐 Reversible con clave✅ Sí aplicaAlmacenamiento seguro de datos activos

Pipeline de anonimización con Python + PostgreSQL

Un pipeline automatizado que corre diariamente (vía cron o n8n) para anonimizar datos operacionales antes de pasarlos a BI:

# pipeline_anonimizacion.py
import hashlib, re, random, string
import psycopg2
from datetime import datetime

def anonimizar_rut(rut):
    """Reemplaza RUT real por RUT ficticio preservando formato"""
    return f"{random.randint(10,99)}.{random.randint(100,999)}.{random.randint(100,999)}-{random.choice('0123456789K')}"

def anonimizar_email(email):
    """Ofusca email: usuario***@dominio.cl"""
    local, dominio = email.split('@')
    return f"{local[:2]}{'*' * (len(local)-2)}@{dominio}"

def pseudonimizar_email(email, salt):
    """Pseudonimización determinística: mismo input → mismo seudónimo"""
    hash_obj = hashlib.sha256(f"{email}{salt}".encode())
    return f"user_{hash_obj.hexdigest()[:12]}@anon.local"

def anonimizar_telefono(telefono):
    """Mantiene prefijo, ofusca el resto: +56 9 **** 1234"""
    digito = re.sub(r'\D', '', telefono)
    return f"+56 {digito[2:3]} **** {digito[-4:]}" if len(digito) > 8 else "***"

# Pipeline diario: datos de producción → tabla anónima para BI
def ejecutar_pipeline_anonimizacion():
    conn = psycopg2.connect("dbname=produccion user=...")
    cur = conn.cursor()
    
    # 1. Pseudonimizar tabla de clientes para analytics
    cur.execute("""
        INSERT INTO clientes_anonimizados (id_seudonimo, edad, region, segmento)
        SELECT 
            encode(sha256((email || 'salt_anual_2026')::bytea), 'hex') as id_seudonimo,
            EXTRACT(YEAR FROM AGE(fecha_nacimiento))::int as edad,
            region,
            segmento
        FROM clientes
        WHERE updated_at > NOW() - INTERVAL '1 day'
    """)
    
    # 2. Anonimizar RAT histórico (irreversible)
    cur.execute("""
        UPDATE logs_tratamiento 
        SET rut_anon = '*********' || RIGHT(rut, 1),
            email_anon = SPLIT_PART(email, '@', 1) || '@dominio-anonimo.local'
        WHERE procesado_para_bi = false
    """)
    
    conn.commit()
    cur.close()
    conn.close()
    print(f"[{datetime.now()}] Pipeline de anonimización completado")

# Programar en cron: 0 3 * * * python3 pipeline_anonimizacion.py

💡 Estrategia recomendada

Pseudonimiza en producción (para conservar funcionalidad operativa) y anonimiza para BI/analytics (donde solo importan tendencias, no individuos). La Ley 21.719 permite el tratamiento de datos pseudonimizados, pero con obligaciones reducidas. Documenta el proceso para demostrarlo ante la Agencia.

5. RAT automatizado: El Registro de Actividades de Tratamiento que no te tomará meses

La Ley 21.719 exige que responsables y encargados mantengan un Registro de Actividades de Tratamiento (RAT) con: finalidad, base legal, categorías de titulares, destinatarios, plazos de conservación y medidas de seguridad (Art. 17).

Hacerlo en Excel es un desastre. Hacerlo con Metabase + PostgreSQL + n8n es tener un RAT vivo, auditable y actualizado automáticamente:

-- Esquema RAT para Ley 21.719
CREATE TABLE rat_registro (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    responsable VARCHAR(200) NOT NULL,          -- nombre de la organización responsable
    encargado VARCHAR(200),                     -- si aplica
    finalidad VARCHAR(500) NOT NULL,            -- para qué se tratan los datos
    base_legal VARCHAR(100) NOT NULL,           -- consentimiento, interés legítimo, etc.
    categorias_titulares TEXT[],                -- {'clientes', 'proveedores', 'empleados'}
    categorias_datos TEXT[],                    -- {'nombre', 'email', 'RUT', 'salud'}
    destinatarios TEXT[],                       -- {'ERP Odoo', 'Proveedor Cloud X'}
    plazos_conservacion VARCHAR(200),           -- '5 años desde última interacción'
    medidas_seguridad TEXT[],                   -- {'cifrado AES-256', 'TLS 1.3', '2FA'}
    transferencia_internacional BOOLEAN DEFAULT FALSE,
    pais_destino VARCHAR(100),
    activo BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Automatizar con n8n: cada nuevo proceso de datos → registro RAT automático
-- Trigger en PostgreSQL para mantener updated_at
CREATE OR REPLACE FUNCTION update_rat_timestamp()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_rat_updated
    BEFORE UPDATE ON rat_registro
    FOR EACH ROW EXECUTE FUNCTION update_rat_timestamp();

📊 Dashboard RAT en Metabase

Conecta Metabase a esta tabla y tendrás un dashboard de cumplimiento en vivo: qué datos tratas, con qué base legal, qué medidas de seguridad tienes, y cuándo se actualizó. Cualquier fiscalización se responde abriendo una URL. No más búsquedas en carpetas compartidas.

6. Notificación de brechas: el sistema que la ley exige y pocos tienen

La Ley 21.719 exige notificar a la Agencia de Protección de Datos dentro de 72 horas de conocida una brecha de seguridad (Art. 16). Y a los titulares afectados "sin dilación indebida". Sin un sistema automatizado, cumplir este plazo es imposible.

Sistema de detección y notificación con Wazuh + n8n

Wazuh (SIEM open source) monitorea logs del sistema en tiempo real. Cuando detecta un evento de seguridad, dispara un webhook a n8n, que ejecuta el protocolo de notificación:

# n8n workflow simplificado (representación)
# [Wazuh] → [Webhook] → [n8n Workflow]

# Paso 1: Recibir alerta de Wazuh
{
  "rule_id": "5712",
  "description": "Multiple failed login attempts - possible brute force",
  "agent": {"name": "servidor-produccion"},
  "data": {"src_ip": "185.220.101.x"},
  "timestamp": "2026-07-02T03:15:22Z",
  "risk_level": "critical"
}

# Paso 2: n8n evalúa si es brecha de datos personales
# Si el servidor contiene datos personales → activa protocolo

# Paso 3: Enviar notificación a Agencia (API o correo certificado)
# - Descripción de la brecha
# - Categorías de datos afectados
# - Número de titulares afectados
# - Medidas adoptadas
# - Contacto del DPO

# Paso 4: Notificar a titulares afectados
# - Email automático a cada titular
# - Qué datos fueron comprometidos
# - Qué medidas tomar
# - Canal de contacto

# Paso 5: Registrar en tabla de brechas (auditable)
CREATE TABLE brechas_seguridad (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    detectada_el TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    notificada_agencia BOOLEAN DEFAULT FALSE,
    notificada_agencia_el TIMESTAMPTZ,
    notificados_titulares INT DEFAULT 0,
    tipo_brecha VARCHAR(100),
    severidad VARCHAR(20),        -- 'baja', 'media', 'alta', 'crítica'
    datos_afectados TEXT[],
    num_titulares_afectados INT,
    medidas_adoptadas TEXT,
    estado VARCHAR(20) DEFAULT 'abierta',  -- 'abierta', 'cerrada', 'investigando'
    created_at TIMESTAMPTZ DEFAULT NOW()
);

⚡ Tiempo real de implementación

Wazuh + n8n + PostgreSQL = 2 días de configuración. Esto te da: detección automática de brechas, protocolo de notificación en 72 horas, registro auditable y dashboard en Metabase. Sin licencias. Sin costos operativos recurrentes.

7. Stack completo de compliance open source

Aquí está la arquitectura completa que cumple con los requisitos técnicos de la Ley 21.719 usando solo herramientas open source que puedes desplegar en un VPS de Hetzner o similar:

Requisito Ley 21.719Herramienta Open SourceFunciónCosto
Consentimiento granularPostgreSQL + n8n + FastAPIRegistro, evidencia, revocación$0
Cifrado en reposoLUKS + SeaweedFS + pgcryptoVolúmenes cifrados + cifrado columna$0
Cifrado en tránsitoTraefik + Let's EncryptTLS 1.3 forzado, certs automáticos$0
Gestión de clavesHashiCorp VaultClaves separadas de datos$0
AnonimizaciónPython pipeline + cron/n8nPipeline diario automatizado$0
RAT (Registro Actividades)PostgreSQL + MetabaseRegistro vivo + dashboard fiscalización$0
Notificación brechas 72hWazuh + n8nSIEM + workflow notificación$0
DPO / ComplianceAI Agent (DeepSeek V4)Respuesta ARCO+, EIPD, monitoreo~$18/mes
Gestión de titularesOdoo CRMPipeline consentimientos + derechos$0
Logs y auditoríaGrafana + Loki + PromtailTraza de accesos a datos personales$0
TOTALStack completoCumplimiento Ley 21.719~$18/mes

🏗️ Esto es exactamente el stack que puedes desplegar con Wagner Solutions

No es teoría. Es el mismo stack que usamos para operar nuestra infraestructura, nuestros clientes, y nuestros propios flujos de datos. Todo corre sobre VPS Hetzner, todo es open source, todo se automatiza con n8n. Si puedes ejecutar un docker-compose, puedes implementar compliance de nivel GDPR/Ley 21.719.

8. Conclusión: La ventana se cierra en 5 meses

La Ley 21.719 no es una opción. El 1 de diciembre de 2026, las obligaciones de la Fase 2 entran en vigencia. Las multas llegan hasta 20.000 UTM (~$1.300 millones CLP) para infracciones gravísimas.

Pero aquí está la buena noticia: no necesitas un presupuesto de compliance corporativo para cumplir.

Con $18/mes (lo que cuesta operar un AI Agent en DeepSeek V4 Flash) y las herramientas open source que recorrimos hoy, cualquier pyme chilena puede tener:

📅 Lo que deberías hacer esta semana

  1. Día 1-2: Desplegar el esquema de consentimiento en PostgreSQL + conectar con n8n
  2. Día 3-4: Configurar cifrado en reposo (LUKS + pgcrypto) y forzar TLS 1.3 en Traefik
  3. Día 5-6: Implementar pipeline de anonimización y dashboard RAT en Metabase
  4. Día 7: Configurar Wazuh + n8n para notificación de brechas

Una semana de trabajo. Eso es lo que separa a una empresa que cumple de una que enfrenta multas millonarias en 2027.

¿Necesitas implementar compliance Ley 21.719 en tu empresa?

En Wagner Solutions AI desplegamos el stack completo de protección de datos que viste en este artículo. Sin licencias enterprise. Sin consultorías interminables. Un AI Agent de compliance + infraestructura open source operativa en días.

🚀 Agenda un diagnóstico gratuito de 15 min

Primeros 5 diagnósticos de julio sin costo · No requerimos contratos anuales

📚 Saga completa: Ley 21.719 para pymes chilenas

Parte 1 → El DPO que no existe: AI Agent de compliance
Parte 2 → Meta Ads, Cold Outreach y los 7 PCC
Parte 3 → Implementación técnica: Cifrado, Consentimiento, Anonimización 👈 Estás aquí

Wagner Solutions AI · Transformación digital soberana para LATAM · Stack 100% open source

wagnersolutionsai.com · hub.wagnersolutionsai.com

Chile · Julio 2026 · Publicado bajo licencia Creative Commons BY-SA 4.0