Write a policy
A good policy lets agents do the everyday work without prompts, asks about the few actions that can hurt, and blocks what should never happen. Here is how to get there in an afternoon.
On this page
Start by watching#
Put a pilot machine in monitor mode for a few days. Every rule still matches and every alert still fires, but nothing is blocked or held, so developers are not interrupted while you learn.
vardhold mode monitorThen use the dashboard's Timeline, filtered by agent and verdict, to answer three questions: what do the agents do all day, which of those actions would the default policy have asked about, and which would it have blocked?
Read a rule#
A policy is a list of rules, read from top to bottom. The first rule that matches decides, and anything no rule matches is logged.
version: 1
mode: enforce
rules:
- id: confirm-recursive-delete # unique name, shown to people and in the audit log
description: Recursive deletes need approval.
match: # every key given must match
action: shell.exec
command_regex: 'rm\s+-rf|Remove-Item\b.*-Recurse'
verdict: ask # allow | log | ask | block
severity: medium
timeout_seconds: 60 # how long to wait for a person
on_timeout: block # what happens if nobody answersBecause the first match wins, order matters: put protections that must always hold (Vardhold's own files, credentials) at the top, specific exceptions next, and broad rules last.
Allow the everyday work#
Agents spend most of their time reading and editing files in the project they were opened in. Logging that, rather than asking, keeps prompts for the moments that matter:
- id: project-work
description: Reading and editing files inside the current project.
match:
action: [file.read, file.write]
outside_cwd: false
verdict: logUse allow for actions you do not need in the audit trail at all, and log (the usual choice) to record them without interrupting anyone.
Ask about what can hurt#
Use ask for actions that are sometimes legitimate but costly if wrong: rewriting git history, deleting folders, publishing packages, deploying.
- id: ask-before-publish
description: Publishing packages needs a person.
match:
action: shell.exec
command_regex: '\b(npm|pnpm|yarn)\s+publish\b|\bdotnet\s+nuget\s+push\b|\btwine\s+upload\b'
verdict: ask
severity: medium
timeout_seconds: 120
on_timeout: blockBlock what must never happen#
Use block for data agents should never touch and actions with no legitimate agent use:
- id: protect-customer-exports
description: Customer data exports stay out of reach of agents.
match:
action: [file.read, file.write, file.delete]
path: ["D:/exports/**", "~/OneDrive - Contoso/Customers/**"]
verdict: block
severity: highPaths accept ~ and %VAR% and are compared the way Windows resolves them, so .., links, short names and administrative shares lead to the same rule. Shell commands are also checked as the files they name, so type or Get-Content on a blocked file meets the same rule as reading it directly.
Make it stricter or looser where it should be#
One rule can say how it changes in strict mode and for agents you have marked trusted. Nothing else changes for them, so the policy itself states everything strict mode and trust do.
- id: first-contact-egress
match:
action: net.connect
host_not_in: allowlists/known-hosts.txt
first_contact: true
verdict: log # normally: record it and raise an alert
verdict_strict: ask # in strict mode: ask first
verdict_trusted: log # trusted agents: never more than log
alert: true
severity: lowCheck, dry-run and apply#
In the dashboard, open Policy, edit the YAML and choose Review & apply. Before anything is applied you see:
- Validation with line numbers for any mistake.
- The rules added, removed and changed, and a line-by-line diff.
- A dry run that replays recent agent activity through the new policy and lists every action that would now be decided differently.
Applying needs administrator confirmation. Every applied policy becomes a new version in the policy history, sealed in the audit log, and any earlier version can be restored from the dashboard.
From the command line, the same checks are:
vardhold policy validate .\policy.yaml
vardhold policy apply .\policy.yamlWhen the dry run looks right, switch back to enforcement:
vardhold mode enforceEvery key, action and pattern is listed in the Policy reference.