Writing policy

Part of the Paladin docs.

Policy is YAML. Rules are evaluated top to bottom and the first match wins. If nothing matches, the policy default applies. With no default set, Paladin denies.

version: "1.0"
id: "pii-egress-block"
name: "Block PII egress to external tools; mask PII to the model"
default_decision: allow

rules:
  # PII must never leave through an external tool.
  - id: "block-pii-external-data"
    description: "Deny external-data calls whose payload contains PII."
    when:
      action_types: [tool_call, mcp_call]
      targets: ["external-data"]
      data_classes_any: ["pii.ssn", "pii.email", "pii.credit_card", "pii.phone"]
    decision: deny
    reason: "PII detected in a call to an external data tool; egress denied."
    owasp: ["AAI-sensitive-information-disclosure", "AAI-excessive-agency"]

  # Mask the most sensitive fields before the model ever sees them.
  - id: "redact-pii-to-model"
    description: "Redact SSNs and card numbers in prompts sent to the model."
    when:
      action_types: [model_call]
      data_classes_any: ["pii.ssn", "pii.credit_card"]
    decision: redact
    reason: "High-sensitivity PII masked before reaching the model."
    redact_classes: ["pii.ssn", "pii.credit_card"]
    owasp: ["AAI-sensitive-information-disclosure"]

  # Explicit catch-all, so the intent is auditable rather than implied.
  - id: "allow-everything-else"
    description: "Allow all other observed actions."
    when: {}
    decision: allow
    reason: "No restriction matched."

A rule matches on the kind of action, the target it names, the classes of data found in it, and the state of the session it belongs to. Every rule carries a reason, which is the text a person sees when the action is stopped, and an optional OWASP tag that carries into reporting.

Apply a policy and read it back:

paladin policy apply my-policy.yaml
paladin policy show
paladin policy history

Policy changes take effect without a restart. Every version is kept.

What the default baseline covers

paladin up seeds a baseline called Recommended. It blocks personal information and credentials from leaving through a tool, asks for approval before data goes to an outside service, holds irreversible operations for a human, blocks content carrying a prompt injection, and tracks the combination of private data plus untrusted content plus an unapproved destination across a session.

What it does not cover, stated plainly. The baseline matches on tool names, targets, and the data found inside arguments. It does not match on arbitrary substrings of an argument. So a rule like "hold any shell command containing rm -rf" is not expressible today. For a coding agent, where every shell command arrives under a single tool name, the available lever is coarse: allow shell, or hold every shell command for approval. Argument-level matching and file-path awareness are the next things being built, and until they land this is a real limit rather than a configuration mistake.

Measured results, including the false-positive rate and the attacks that still get through, are on the benchmarks page.

Tuning, and what there is to tune

A safeguard built on a model has a dial. Turn it up and it catches more real violations, and more harmless actions with them. Turn it down and both fall. The labs ship classifiers with the most resources behind them, and their own documentation still says the result reduces prompts and does not guarantee safety.

Paladin's decision has no dial. A rule matches or it does not. That leaves two kinds of mistake, and both are visible in the policy file. A rule written wider than you meant denies an action you wanted: narrow the rule. An action the policy never named gets the policy default: name it. Neither is tuned. Both are edited, versioned, reviewed, and applied like any other change, without a restart.

The detectors are the one place a threshold lives. A rule that matches on data classes or on injected content relies on a detector to say what a payload contains, and a detector can be wrong. The false-positive rate on the benchmarks page is theirs. When one fires wrongly you see the rule and the reason in the log, and you can narrow the rule to the targets that matter instead of retuning a model you cannot see.

Where that leaves you depends on what a mistake costs. Under a deny default, an agent doing new work will hit unnamed actions, and each one is stopped. Under an escalate default, each one is held for a person. Teams that would rather see a stop or a hold than a wrong action, which is what regulated teams usually want, get exactly that. Teams that need the agent to keep moving run an allow default with deny rules on the actions that must never happen. Either one is a single line in the policy.

The audit log

Every decision is appended to a hash-chained log, so a record cannot be altered or removed without breaking the chain. Checkpoints over the chain are signed.

paladin audit verify
chain intact: every record connects to the one before it
OK: chain intact, all checkpoints verified

Previous: Put it in front of an agent. Next: When the agent drives a browser.

Cortexa Labs, Inc.