Enterprise · Control Plane

Authoring & Customizing a Policy

A policy is a signed YAML overlay an org admin manages in the dashboard. Enrolled devices pull it on top of the bundled default policy and apply it to every AI agent action. This guide covers how a rule works, when to observe versus enforce, how to add or disable detection patterns, the security floor that keeps you safe from mistakes, and how a published policy reaches your devices.

Overview

Every Warden device ships with a bundled default policy of 61 rules. A policy profile is a signed YAML overlay you author in the dashboard under Policy & Exemptions → Policy profiles. When you publish a profile, enrolled devices pull it (roughly every 30 seconds) and apply it on top of the defaults — you only describe what changes, not the whole rule set.

The overlay does three things: it sets a baseline mode, it can flip individual rules between observe and enforce, and — the focus of this page — it can customize the detection patterns inside a rule by adding your own and disabling defaults you don't want.

The overlay is signed. Devices verify the signature before applying it, so a tampered or unsigned profile is rejected and the device keeps running the last good policy.

How a Rule Works

A rule is a named bundle of regex patterns OR'd together. Each pattern is checked against the candidate action (for example, a shell command about to run). If any pattern matches, the rule fires — you do not need every pattern to match, just one.

A rule = many regexes, ANY match fires
rule: curl-pipe-shell
  pattern A:  curl ... | sh        ──┐
  pattern B:  curl ... | bash        ├─ OR  ──▶  ANY match → rule fires
  pattern C:  wget ... | sh        ──┘

A single command only has to match ONE of these to trigger the rule.

Customizing a rule therefore means adjusting that OR-set of patterns: making it broader by adding patterns, or narrower by disabling defaults. You never rewrite the whole list.

Observe vs Enforce

When a rule fires, the resulting finding is handled in one of two modes:

observe

Detect and log the finding. The action still runs. This is the default for everything — you start by watching, not blocking.

enforce

Block the action on the pre-action tool call. The agent is denied before the command runs. Flip a rule here once you trust its precision.

settings.default_mode sets the baseline for the whole policy, and a per-rule mode overrides it for that one rule. The recommended path is to leave the default at observe, watch the findings roll in, then flip individual rules to enforce once you're confident they won't false-positive on your team.

Flip one rule to enforce, keep the rest observing
settings:
  default_mode: observe        # everything detects + logs

rules:
  destructive-command:
    mode: enforce              # this one rule blocks

Adding Custom Patterns

Use add_patterns to strengthen a rule with regexes specific to your environment — an internal hostname, a deploy script you never want piped to a shell, a credential format unique to your org.

  • Patterns use the Python re flavor and are compiled with IGNORECASE + DOTALL.
  • Quote each pattern in YAML single-quotes and escape accordingly — this keeps backslashes literal so your regex survives the YAML parser intact.
  • Each pattern compiles in isolation. If one is invalid, the device drops that pattern with a warning and keeps the rest of the rule working — a bad custom pattern never breaks the rule.
add_patterns
rules:
  curl-pipe-shell:
    add_patterns:
      - 'wget\s+\S+\s*\|\s*(ba)?sh'     # extend the rule's OR-set
      - 'deploy\.internal\.acme\.io'       # an internal host we want flagged

Disabling a Default Pattern

Use disable_patterns to turn off a specific default pattern that's too noisy for your workflow. You reference a default by its exact regex string — copy it verbatim from the rule's pattern list in the editor.

A string in disable_patterns that doesn't match any current default is a harmless no-op. This is deliberate: if a default pattern is renamed or removed upstream, your stale disable entry simply stops doing anything rather than silently weakening a rule. Drift always fails toward more detection, never less.

disable_patterns
rules:
  curl-pipe-shell:
    disable_patterns:
      # paste the default's regex EXACTLY as it appears in the editor
      - 'curl\s+\S+\s*\|\s*python'

The effective pattern set for a rule is defaults − disabled + custom, de-duplicated and order-stable. You never write a patterns: key to customize a default rule — only add_patterns and disable_patterns.

The Security Floor

Customization is powerful, so two guardrails make it impossible to accidentally disarm your own protection.

Core rules are add-only

Five rules form the non-negotiable core. On these, disable_patterns is rejected and their default patterns can never be replaced. You may still add_patterns to make them stronger — you just can't weaken them.

Core ruleCovers
destructive-commandrm -rf, disk wipes, irreversible deletes
secret-exfiltrationpiping credentials to remote hosts
rce-canaryremote code execution probes
privilege-escalationsudo abuse, setuid, capability grabs
dos-resource-exhaustionfork bombs, fill-the-disk, runaway loops

A rule can never reach zero patterns

If your edits would leave a rule with no active patterns at all, the console blocks the save. And as a runtime backstop, a device that somehow receives a rule resolving to zero patterns falls back to the full defaults for that rule rather than running blind.

Both guardrails fail toward more protection. The worst outcome of a bad edit is that a rule keeps running its bundled defaults — never that it silently stops detecting.

Editing in the Web Console

Most editing happens visually in the policy editor. Expand a rule to see all of its default patterns:

  • Each default pattern has a Disable / Enable toggle. For the five core rules the toggle is locked — you can't turn a core default off.
  • An "add custom pattern" box lets you type a new regex with live validation. An invalid regex turns red and the Add button stays disabled until it's valid.
  • The YAML below the toggles is generated from your choices and is the source of truth. It's validated as you go — green when it parses, red when it doesn't.

Toggling a default off writes its exact regex into disable_patterns for you, and adding a custom pattern appends it to add_patterns — so the visual editor and the YAML stay in sync automatically.

Editing the YAML Directly

You can also edit the YAML by hand. The keys you'll use:

KeyValuePurpose
settings.default_modeobserve | enforceBaseline mode applied to every rule unless a rule overrides it. Ships as observe.
rules.<name>.modeobserve | enforcePer-rule override. Flip a single rule to enforce while the rest stay observe.
rules.<name>.add_patterns[regex, ...]Extra custom regexes added to the rule. Python re flavor, IGNORECASE + DOTALL.
rules.<name>.disable_patterns[regex, ...]Default patterns to turn off, referenced by their exact regex string. Rejected on core rules.

A complete example: an observe baseline, one non-core rule that adds custom patterns and disables a default, and one core rule that adds patterns only.

policy.yaml — full example
# Org policy overlay — applied on top of the bundled default policy.
# You never write a 'patterns:' key here. To customize a default rule,
# use add_patterns (strengthen) and/or disable_patterns (turn off).

settings:
  default_mode: observe        # everything detects + logs unless flipped to enforce

rules:

  # --- A non-core rule: strengthen it and turn off one noisy default ---
  curl-pipe-shell:
    mode: enforce              # block instead of just logging
    add_patterns:
      - 'wget\s+\S+\s*\|\s*(ba)?sh'        # wget ... | sh
      - 'curl\s+-fsSL\s+\S+\s*\|\s*(ba)?sh'
    disable_patterns:
      # Copy these strings VERBATIM from the rule's default pattern list.
      - 'curl\s+\S+\s*\|\s*python'         # we never run pipe-to-python; drop the noise

  # --- A core rule: add-only. disable_patterns here is REJECTED. ---
  destructive-command:
    add_patterns:
      - 'shred\s+-[a-z]*u'                     # our extra: catch shred -u
      - 'dd\s+if=/dev/zero\s+of=/dev/sd[a-z]'

Layering & Precedence

Policy is resolved from three layers, lowest to highest precedence:

LayerPrecedenceSource
projectlowestPer-project .prismor config committed to the repo
remote (org)middleThe signed org policy profile managed in the dashboard
repo-exemptionhighestAdmin-granted, repo-scoped exemptions layered on top

For pattern customization, the key rule is that add_patterns and disable_patterns are unioned across all layers. A later layer can add patterns and disable defaults, but it cannot wipe an earlier layer's additions — once a custom pattern is added at any layer, it stays in the effective set.

Because adds are unioned and never wiped, layering is purely additive for detection. The most a higher layer can do is loosen mode (via an exemption) or disable a non-core default — it can never remove protection another layer contributed.

Validation & Troubleshooting

JavaScript validation vs Python execution

The console validates your custom regex with JavaScript's RegExp, but the device compiles it with Python's re. The two flavors overlap heavily, but they aren't identical. If a pattern is valid in the browser but invalid in Python, the device drops just that one pattern (with a warning) and keeps the rest of the rule running. So a pattern that looked green in the editor can still be skipped on-device — check the device warnings if a custom pattern never seems to fire.

A disable that does nothing

If a disable_patterns entry doesn't turn anything off, the string didn't match a current default exactly. Re-copy the regex verbatim from the expanded rule in the editor — a single different character (or an extra escape) means no match, and the entry is treated as a harmless no-op.

Save blocked: zero patterns

If the console won't let you save, you've likely disabled every default in a rule without adding a replacement. Either re-enable at least one default or add a custom pattern so the rule has something to match. Remember core rules can't be emptied at all — their disable toggles are locked.

How It Reaches Devices

Once you publish a profile, delivery is automatic:

Publish → pull → apply
1. Admin saves & publishes the profile in Policy & Exemptions.
   The control plane signs the YAML overlay.

2. Each enrolled device polls (~30s) and pulls the signed overlay.

3. The device verifies the signature, then layers the overlay on top
   of the bundled defaults:
      effective patterns = defaults − disabled + custom   (per rule)

4. If a rule would resolve to zero patterns, the device falls back to
   that rule's full defaults instead of running blind.

5. The new policy takes effect on the next agent action — no restart.

Because the pull interval is short, an edit you publish is live across your fleet within roughly half a minute — you can flip a rule to enforce, watch the telemetry, and roll it back just as quickly if needed.