# 🔐 Pseudonymizer — Seudonimización de Payloads
## 🤔 ¿Qué hago? ¿Cómo lo hago? ¿Y para qué lo hago?
**¿Qué hago?** Seudonimizo automáticamente todos los payloads sensibles (messages, system, tool_results, etc.) **antes** de enviarlos a proveedores externos como Anthropic. Reemplazo valores confidenciales por UUIDs y almaceno el mapeo en Qdrant para auditoria y reversibilidad.
**¿Cómo lo hago?** Cada vez que Klaus-proxy-global recibe una petición `/v1/messages`:
1. Se procesa el payload según la lógica normal (caché, RAG, etc.)
2. **Justo antes de forwardear a Anthropic**, aplico seudonimización
3. Todos los campos sensibles se reemplazan con `[PSEUDONYM:uuid]`
4. El mapeo original → pseudonym se almacena en Qdrant (`klaude_pseudonyms`)
5. El payload seudonimizado se envía a Anthropic
6. Se registra un evento de log para que aparezca en el dashboard
**¿Para qué lo hago?** Proteger datos confidenciales del usuario cuando se trabaja con LLMs externos. Aunque confíes en Anthropic, la seudonimización añade una capa extra de aislamiento: Anthropic nunca ve los datos originales, solo los tokens seudonimizados. Cumple requisitos de:
- **GDPR**: Anonimización de datos personales en tránsito
- **Compliance**: Auditoría completa de qué datos se enviaron y cuándo
- **Control**: Toggle on/off en runtime, sin depender de configuración estática
---
## 🏗️ Arquitectura
```mermaid
flowchart LR
User["👤 Usuario<br/>Claude Code<br/>Continue.dev"]
Proxy["🔀 Klaus-proxy-global<br/>/v1/messages"]
Cache["💾 Cache<br/>RAG Injection"]
Pseudonym["🔐 Pseudonymizer<br/>messages, system<br/>tool_results"]
Anthropic["☁️ Anthropic API<br/>api.anthropic.com"]
Qdrant["🗄️ Qdrant<br/>klaude_pseudonyms"]
User -->|request| Proxy
Proxy -->|1. lookup| Cache
Cache -->|2. found? RAG| Proxy
Proxy -->|3. apply pseudonym| Pseudonym
Pseudonym -->|store mapping| Qdrant
Pseudonym -->|forward [PSEUDONYM:uuid]| Anthropic
Anthropic -->|response| User
style Pseudonym fill:#4c1d95,color:#e0e0e0,stroke:#a78bfa
style Qdrant fill:#1e293b,color:#64748b
```
---
## 📡 Endpoints
### `GET /pseudonymizer/status`
Consultar estado del seudonimizador y estadísticas.
**Sin autenticación.**
**Respuesta:**
```json
{
"enabled": true,
"collection": "klaude_pseudonyms",
"mappings_stored": 247,
"status": "Green"
}
```
| Campo | Tipo | Descripción |
| --- | --- | --- |
| `enabled` | bool | ¿Seudonimizador activo? |
| `collection` | string | Nombre de colección Qdrant |
| `mappings_stored` | int | Número de mappings almacenados (UUID → original) |
| `status` | string | Estado de colección Qdrant (`Green`, `Yellow`, `Red`, `unavailable`) |
---
### `POST /pseudonymizer/toggle`
Activar/desactivar el seudonimizador en runtime.
**Sin autenticación (pero considerar añadir x-api-key en futuro).**
**Body:**
```json
{ "enabled": true }
```
**Respuesta:**
```json
{
"enabled": true,
"message": "Pseudonymizer enabled"
}
```
| Parámetro | Tipo | Default | Descripción |
| --- | --- | --- | --- |
| `enabled` | bool | — | `true` para activar, `false` para desactivar. Si omitido, alterna el estado actual |
---
### `DELETE /pseudonymizer/mappings`
Limpiar todos los mappings almacenados en Qdrant. Útil después de testing o para reset.
**Headers:** `x-api-key: <cualquier valor no vacío>`
**Respuesta:**
```json
{
"flushed": true,
"deleted_points": 247
}
```
---
### `POST /pseudonymizer/config`
Configurar dinámicamente qué valores seudonimizar. **Klaus-code-cli llama esto al iniciar.**
**Sin autenticación.**
**Body:**
```json
{
"literals": ["path/to/project", "username", "custom-sensitive-value"]
}
```
**Respuesta:**
```json
{
"literals": 3,
"paths": 2
}
```
| Parámetro | Tipo | Default | Descripción |
| --- | --- | --- | --- |
| `literals` | list[str] | `[]` | Lista de valores específicos a seudonimizar. **Reemplaza** la lista anterior (no merge). Vacío = solo patrones regex + env |
**Nota:** Esta configuración es **por sesión**, se pierde al redeploy del container. Klaus-code-cli debe llamar esto cada vez que inicia.
---
### `GET /pseudonymizer/config`
Obtener literales actualmente configurados (para auditoría).
**Sin autenticación.**
**Respuesta:**
```json
{
"literals": ["path/to/project", "username"],
"paths": ["/home/user", "/root"]
}
```
| Campo | Tipo | Descripción |
| --- | --- | --- |
| `literals` | list[str] | Valores configurados vía `POST /config` |
| `paths` | list[str] | Prefijos de rutas (del entorno) |
---
## 🧩 Módulo: `proxy/pseudonymizer.py`
Módulo standalone que gestiona la lógica de seudonimización.
### Funciones públicas
| Función | Firma | Descripción |
| --- | --- | --- |
| `is_enabled()` | `() -> bool` | Consultar estado global |
| `set_enabled(enabled: bool)` | `(bool) -> None` | Cambiar estado |
| `apply(request_data)` | `(dict) -> (dict, list)` | Seudonimizar payload. Retorna (pseudonymized, events) |
| `get_stats()` | `() -> dict` | Estadísticas de mappings activos |
| `get_rules()` | `async () -> dict` | Obtener literales + rutas configuradas |
| `set_rules(literals)` | `async (list[str]) -> dict` | Actualizar literales dinámicamente (Klaus-code-cli) |
| `restore_response(text)` | `async (str) -> str` | Deseudonimizar respuesta de Anthropic |
| `flush()` | `() -> dict` | Limpiar todos los mappings |
### Campos seudonimizados
**Automáticamente**, se seudonimiza cualquier valor string en:
- `messages` — array de objetos con `content`
- `system` — string o list de system prompts
- `tool_results` — respuestas de herramientas (tool_use context)
- `instructions` — si está presente en el payload
- **Recursivamente**: funciona en nested structures (lists, dicts)
### Formato de pseudonym
```
[PSEUDONYM:550e8400]
```
Donde `550e8400` son los primeros 8 caracteres de un UUID v4. El UUID completo se usa como clave en Qdrant.
---
## 🗄️ Colección Qdrant: `klaude_pseudonyms`
| Campo payload | Tipo | Descripción |
| --- | --- | --- |
| `pseudonym_id` | string | UUID v4 completo |
| `original_value` | string | Valor original (seudonimizado) |
| `field_name` | string | Campo en el que estaba: `text`, `system`, etc. |
| `length` | int | Longitud del valor original |
| `created_at` | ISO8601 | Timestamp de seudonimización |
**ID determinista:** `hash(pseudonym_id) % (2**63)` — permite búsquedas eficientes por ID.
---
## 🎛️ Dashboard Integration
### Toggle en navbar
Botón `🔐 Seudonimizar` en la barra superior. Hacer clic activa/desactiva el seudonimizador.
- ✅ Verde cuando está **ON** (activo)
- ⚪ Gris cuando está **OFF** (inactivo)
### Panel de logs
Nueva sección **"Pseudonymizer"** con:
- **Estado actual**: `✅ Seudonimizador ACTIVO · 247 mappings almacenados`
- **Eventos recientes**: lista de últimos campos seudonimizados
- Formato: `🔐 system · 3 valores seudonimizados`
- Actualiza en tiempo real vía SSE
### Botones de control
- **🔐 Seudonimizar**: Abre el modal de control
- **🗑️ Limpiar mappings**: Borra todos los mappings (requiere confirmación)
- **Cerrar**: Cierra el modal
---
## ⏱️ Performance
| Escenario | Tiempo | Notas |
| --- | --- | --- |
| Seudonimización desactivada | ~0 ms | Solo check de flag (`if not enabled: return`) |
| Payload pequeño (1KB) | ~2-5 ms | Parsing + storage en Qdrant |
| Payload mediano (50KB) | ~20-50 ms | Más campos a procesar |
| Payload grande (500KB) | ~200-500 ms | Considera si activar selectivamente |
**Recomendación:** Mantener siempre activado en producción. El overhead es mínimo comparado con latencia de Anthropic (~1-2s).
---
## 🔒 Seguridad
- ✅ **Mappings en Qdrant**: Los valores originales se almacenan en BD, no en logs
- ✅ **No en stdout/stderr**: Los logs del proxy nunca contienen valores seudonimizados
- ✅ **UUID aleatorio**: Imposible invertir `[PSEUDONYM:uuid]` sin acceso a Qdrant
- ✅ **Audit trail**: Cada seudonimización queda registrada con timestamp
- ⚠️ **Requires Qdrant security**: Si Qdrant se compromete, los mappings están expuestos. Protégelo con firewall
---
## 🔗 Documentos relacionados
- [cache.md](cache.md) — Caché semántica (anterior en pipeline)
- [sources.md](sources.md) — Ingestión de fuentes externas
- [knowledge_base.md](knowledge_base.md) — Knowledge Base de proyecto
- [architecture.md](architecture.md) — Arquitectura general del proxy
---
## 💡 Ejemplos de uso
### Activar seudonimizador
```bash
curl -X POST http://localhost:8080/pseudonymizer/toggle \
-H "Content-Type: application/json" \
-d '{"enabled": true}'
```
**Response:**
```json
{"enabled": true, "message": "Pseudonymizer enabled"}
```
### Consultar estado
```bash
curl http://localhost:8080/pseudonymizer/status
```
**Response:**
```json
{
"enabled": true,
"collection": "klaude_pseudonyms",
"mappings_stored": 150,
"status": "Green"
}
```
### Limpiar mappings
```bash
curl -X DELETE http://localhost:8080/pseudonymizer/mappings \
-H "x-api-key: admin"
```
**Response:**
```json
{"flushed": true, "deleted_points": 150}
```
---
## 🚀 Roadmap futuro
- [ ] **Selective pseudonymization**: Solo ciertos campos (e.g., no seudonimizar `model`)
- [ ] **Encryption at rest**: Encriptar mappings en Qdrant con KMS
- [ ] **TTL per mapping**: Auto-delete después de X días
- [ ] **Audit export**: Endpoint para exportar audit trail completo
- [ ] **Rate limiting**: Limitar seudonimizaciones por usuario
- [ ] **Reversibility endpoint**: Endpoint privado para reversal (admin only)