← Back to blog
2026-01-22 · dcode · security, policy, yaml

Deny Everything: Writing Security Policies for AI Agents in YAML

A complete walkthrough of klawty-policy.yaml — deny-by-default security for AI agents covering network, filesystem, exec, and PII detection.

The default should be no

Most agent frameworks ship permissive by default. Your agent can hit any URL, read any file, execute any shell command. The assumption is that you'll add restrictions later. You won't. And when your agent tries to curl an internal endpoint or reads ~/.ssh/id_rsa, you'll wish you had.

Klawty inverts this. Everything is denied by default. You explicitly allow what the agent needs. Nothing more.

klawty-policy.yaml

This is the complete policy file. Every agent in the system is bound by it. No exceptions, no overrides at runtime.

<h1 class="font-extrabold text-2xl mt-10 mb-4">klawty-policy.yaml — deny-by-default agent security</h1>
version: 1
mode: enforce  # enforce | audit | disabled

network: default: deny allow: - "api.openrouter.ai" # LLM provider - "api.anthropic.com" # Direct Anthropic - "api.openai.com" # OpenAI embeddings - "discord.com" # Channel - "gateway.discord.gg" # WebSocket - "api.telegram.org" # Channel - "smtp.gmail.com" # Outbound email - "imap.gmail.com" # Inbound email block: - "169.254.169.254" # Cloud metadata endpoint - "localhost" # No loopback - "*.internal" # No internal services

filesystem: default: deny allow: read: - "/workspace/**" # Agent workspace - "/tmp/klawty-*" # Temp files (prefixed) write: - "/workspace/data/**" # Database and state - "/workspace/agents/*/MEMORY.md" # Agent memory - "/tmp/klawty-*" block: - "~/.ssh/" - "~/.env" - "/etc/shadow" - "/.git/config" - "**/credentials*" - "**/secrets*"

exec: default: deny allow: - "node" - "sqlite3" - "git status" - "git log" - "git diff" block: - "rm -rf *" - "curl *" # Use network allowlist instead - "wget *" - "sudo *" - "chmod 777 *" - "eval *" - "bash -c *"

resources: max_memory_mb: 512 max_cpu_seconds: 30 max_file_size_mb: 10 max_open_files: 50

pii: detect: - email # RFC 5322 pattern - phone # International formats - credit_card # Luhn-valid 13-19 digit - iban # 2-letter country + check digits action: route_local # route_local | redact | block local_model: "ollama/llama3"

What changes with a policy

Without policy: Your agent can curl any URL, read your SSH keys, execute arbitrary shell commands, and send PII to a cloud LLM.

With deny-by-default: The agent can only reach the 8 domains you listed. It can only read files under /workspace/. Shell commands are limited to node, sqlite3, and read-only git. Any content containing an email address or IBAN gets routed to a local Ollama model instead of the cloud.

Audit mode

Not ready to enforce? Set mode: audit. The policy engine logs every violation without blocking it. Run in audit mode for a week, review the logs, then switch to enforce once you're confident the allowlist is complete.

klawty logs --filter policy-violation
<h1 class="font-extrabold text-2xl mt-10 mb-4">2026-01-22T14:30:12Z AUDIT agent=raph action=network url=registry.npmjs.org result=would_block</h1>

That log tells you Raph needs registry.npmjs.org in the network allowlist. Add it, and you're one step closer to a locked-down production deployment.

The policy file is version-controlled alongside your agent configuration. Every change is in git history. Every restriction is auditable. Every permission is intentional.