Allowlist vs Blocklist (por qué gana default-deny) + Código

Las blocklists se pudren. Las allowlists escalan. Un modelo de policy de herramientas que no autoriza por accidente el próximo tool peligroso.
En esta página
  1. El problema (en producción)
  2. Por qué esto se rompe en producción
  3. 1) No puedes enumerar “todas las herramientas peligrosas” por adelantado
  4. 2) Las blocklists fallan por naming e indirección
  5. 3) Default-allow hace crecer la blast radius en silencio
  6. 4) Las allowlists fuerzan una decisión explícita
  7. Ejemplo de implementación (código real)
  8. Incidente real (con números)
  9. Trade-offs
  10. Cuándo NO usarlo
  11. Checklist (copiar/pegar)
  12. Config segura por defecto (JSON/YAML)
  13. FAQ (3–5)
  14. Páginas relacionadas (3–6 links)
Flujo interactivo
Escenario:
Paso 1/3: Execution

Action is proposed as structured data (tool + args).

El problema (en producción)

Empiezas con una blocklist porque es rápido: “bloqueamos lo peligroso”.

Luego agregas un tool nuevo. Te olvidas de actualizar la blocklist. El agente lo descubre.

Y ahora estás on-call porque “bloqueamos lo peligroso” era más una historia que un control.

Las blocklists se sienten como control. En sistemas de agentes, casi siempre son una trampa. Y se pudren rápido: dos semanas después nadie recuerda qué estaba “protegiendo” la lista.

Por qué esto se rompe en producción

1) No puedes enumerar “todas las herramientas peligrosas” por adelantado

Hoy es db.delete_user. Mañana es crm.merge_accounts. La semana que viene es tickets.close_all.

El tool que olvides bloquear es el que te muerde.

2) Las blocklists fallan por naming e indirección

Bloqueas db.write, alguien shippea db.patch. Bloqueas email.send, alguien shippea email.send_bulk.

Peor: wrappers. El agente llama workflow.run("close_ticket") y tu “blocklist” ni ve el side effect real.

3) Default-allow hace crecer la blast radius en silencio

Si la policy dice “todo permitido excepto…”, cada tool nuevo es expansión de permisos por defecto. Eso es un incidente esperando turno.

4) Las allowlists fuerzan una decisión explícita

Las allowlists molestan. Bien. Te obligan a decir: “sí, el agente puede llamar este tool bajo estas condiciones”.

Ejemplo de implementación (código real)

Un evaluador de policy pequeño:

  • default-deny
  • deny list opcional para modo incidente (freno de emergencia)
  • “write tools requieren approval”
PYTHON
from dataclasses import dataclass


WRITE_TOOLS = {"email.send", "db.write", "ticket.close"}


@dataclass(frozen=True)
class Policy:
  allow: set[str]
  deny: set[str] = None  # for incident mode
  require_approval_for_writes: bool = True


class Denied(RuntimeError):
  pass


def evaluate(policy: Policy, tool: str) -> str:
  deny = policy.deny or set()
  if tool in deny:
      raise Denied(f"denied: {tool} (incident mode)")
  if tool not in policy.allow:
      raise Denied(f"not allowed: {tool}")
  if policy.require_approval_for_writes and tool in WRITE_TOOLS:
      return "approve"
  return "allow"
JAVASCRIPT
const WRITE_TOOLS = new Set(["email.send", "db.write", "ticket.close"]);

export class Denied extends Error {}

export function evaluate(policy, tool) {
const deny = new Set(policy.deny || []);
if (deny.has(tool)) throw new Denied("denied: " + tool + " (incident mode)");
if (!policy.allow.includes(tool)) throw new Denied("not allowed: " + tool);
if (policy.requireApprovalForWrites && WRITE_TOOLS.has(tool)) return "approve";
return "allow";
}

Incidente real (con números)

Vimos a un equipo shippear un agente con: “bloquea herramientas peligrosas”.

Luego agregaron un tool: ticket.close_bulk. No estaba en la deny list. El agente lo usó porque era el camino más corto a “resolver”.

Impacto:

  • ~200 tickets cerrados incorrectamente
  • ~5 horas ingeniero para reabrir, explicar y parchear
  • el agente quedó apagado una semana: nadie confiaba en él

Fix:

  1. allowlist default-deny
  2. writes detrás de approvals
  3. deny list solo para modo incidente (temporal)

Las blocklists sirven como freno. Como volante, son malas.

Trade-offs

  • Las allowlists ralentizan el “just ship it”. En prod, es un feature.
  • Necesitas tooling para gestionarlas (no hardcodees en 12 lugares).
  • La gente intentará saltárselas con wrappers. No lo permitas.

Cuándo NO usarlo

  • En prototipos locales puedes ser más laxo — pero no lleves eso a prod.
  • Incluso con tools read-only, una allowlist pequeña evita exposiciones accidentales.
  • Si tu “tool” es un RPC genérico que puede hacer cualquier cosa, arréglalo primero: divide por capacidades.

Checklist (copiar/pegar)

  • [ ] Allowlist default-deny (nombres explícitos)
  • [ ] Separar tools por capacidad (read vs write)
  • [ ] Approvals para writes irreversibles
  • [ ] Deny list solo para modo incidente (temporal)
  • [ ] Log de denies (señal)
  • [ ] No exponer tools genéricos “do anything”

Config segura por defecto (JSON/YAML)

YAML
policy:
  default: "deny"
  allow: ["search.read", "kb.read", "http.get"]
  require_approval_for_writes: true
incident_mode:
  deny: ["browser.run"] # freno temporal
logging:
  log_denies: true

FAQ (3–5)

¿Las blocklists sirven para algo?
Sí: modo incidente y deshabilitaciones temporales. Son freno, no modelo de permisos.
¿Puedo usar wildcards en allowlists?
Con cuidado. Los wildcards se vuelven default-allow con el tiempo. Si los usas, que sean estrechos y revisados.
¿Qué pasa con tools tipo ‘workflow.run’?
Ocultan side effects. Prefiere tools explícitos para acciones explícitas, para que la policy pueda razonar.
¿Cuál es la policy mínima segura?
Default-deny allowlist + tools read-only. Agrega writes después, con approvals.

P: ¿Las blocklists sirven para algo?
R: Sí: modo incidente y deshabilitaciones temporales. Son freno, no modelo de permisos.

P: ¿Puedo usar wildcards en allowlists?
R: Con cuidado. Los wildcards se vuelven default-allow con el tiempo. Si los usas, que sean estrechos y revisados.

P: ¿Qué pasa con tools tipo ‘workflow.run’?
R: Ocultan side effects. Prefiere tools explícitos para acciones explícitas, para que la policy pueda razonar.

P: ¿Cuál es la policy mínima segura?
R: Default-deny allowlist + tools read-only. Agrega writes después, con approvals.

No sabes si este es tu caso?

Disena tu agente ->
⏱️ 5 min de lecturaActualizado Mar, 2026Dificultad: ★★★
Implementar en OnceOnly
Budgets + permissions you can enforce at the boundary.
Usar en OnceOnly
# onceonly guardrails (concept)
version: 1
budgets:
  max_steps: 25
  max_tool_calls: 12
  max_seconds: 60
  max_usd: 1.00
policy:
  tool_allowlist:
    - search.read
    - http.get
writes:
  require_approval: true
  idempotency: true
controls:
  kill_switch: { enabled: true }
Integrado: control en producciónOnceOnly
Guardrails para agentes con tool-calling
Lleva este patrón a producción con gobernanza:
  • Presupuestos (pasos / topes de gasto)
  • Permisos de herramientas (allowlist / blocklist)
  • Kill switch y parada por incidente
  • Idempotencia y dedupe
  • Audit logs y trazabilidad
Mención integrada: OnceOnly es una capa de control para sistemas de agentes en producción.
Autor

Esta documentación está curada y mantenida por ingenieros que despliegan agentes de IA en producción.

El contenido es asistido por IA, con responsabilidad editorial humana sobre la exactitud, la claridad y la relevancia en producción.

Los patrones y las recomendaciones se basan en post-mortems, modos de fallo e incidentes operativos en sistemas desplegados, incluido durante el desarrollo y la operación de infraestructura de gobernanza para agentes en OnceOnly.