keybound: auditoría de aislamiento de prompt cache en relays LLM multi-tenant
Una herramienta que veredicta si el defense contract de arXiv:2608.17485 (KeyPooling) se cumple: en un relay multi-tenant, nadie lee cache escrito por otro tenant.
El problema
En un relay LLM que sirve a varios tenants con una sola credencial upstream, el
dominio de cache puede colapsar: dos tenants distintos terminan compartiendo
el mismo namespace de cache. El resultado es una fuga de información — el tenant
B lee lo que el tenant A escribió, sin saberlo y sin permiso.
El paper KeyPooling (arXiv:2608.17485) define el defense contract:
«a namespace derived from authenticated identity must survive every final
cache lookup and write»
Es decir: la identidad autenticada del tenant debe derivar un namespace que
persista en cada lookup y escritura de cache. Si el relay no envía namespace
(lo colapsa), el contrato se rompe.
Qué hace keybound
Ejecuta las células formales E2 del paper contra un gateway mock y veredicta
el contrato:
- Modo
collapse: dominio único compartido → debe fallar (exit 1). - Modo
isolate: namespace por tenant derivado del bearer → debe pasar (exit 0).
El veredicto es por identidad real, no por la etiqueta de la célula. Un leak
es cualquier lectura con cached_tokens > 0 cuyo escritor (hit_writer) es un
tenant distinto del lector. Esto lo hace invariante al orden de ejecución,
incluida la permutación adversarial (la célula cross antes que las cold/owner
del otro tenant).
keybound audit --fixture collapse
# fixture: collapse | verdict: FAIL | defense_contract: not_satisfied
# cause: 1 dominio(s); cross_con_leak=['A1-cold-prime', 'A2-owner-hot']
El reporte nunca incluye claves de tenant ni el Authorization recibido —
solo identificadores (tenant A/B, prompt P/R) y los dominios efectivos.
Cómo se verificó (gobernanza de tres partes)
El desarrollo siguió el protocolo de la casa: implementación → auditoría
independiente en clon limpio → aprobación de merge. Tres bugs reales cazados y
corregidos en el camino:
- Matching de prefijo por caracteres (no por palabras) en el mock → falsos positivos de cache. Corregido con comparación de palabras completas.
-
cached_tokensnegativo no validado → podía dar PASS con valores absurdos. Corregido con validación de tipo/rango (CellError). -
Veredicto por
kind=="cross"→ falso negativo si la célulacrossse ejecutaba antes. Corregido por el camino de identidad real (hit_writer), descubierto por la auditoría, no por self-report.
Los tests corren contra un gateway mock sintético (sin red, determinista),
no contra LiteLLM real — el fast suite valida la lógica del auditor, no
reconfirma la vulnerabilidad en cada CI run. La vulnerabilidad real está
documentada en RESEARCH.md / KNOWN_ISSUES.md y se verifica con el adaptador
NewAPI (roadmap v0.3).
Resultados
-
46 tests pasan,
rufflimpio, cobertura 90% (núcleo de auditoría 97–100%). - Veredicto invariante a cualquier orden de células, incluido el adversarial.
Pruébalo
git clone https://github.com/amurlaniakea/keybound
cd keybound
python -m venv .venv && source .venv/bin/activate
pip install -e .
keybound audit --fixture collapse # FAIL esperado
keybound audit --fixture isolate # PASS esperado
Links
Licencia: AGPL-3.0-or-later — Copyright 2026 Pedro Sordo Martínez
Top comments (0)