Guardrails, connection scopes, spend ceilings, and approval thresholds are security controls. They determine what agents can do and what content can pass. In most deployments they are edited through an admin UI, take effect immediately, and leave a row in a table with a timestamp.

Compare that to how the same organization handles a change to an authorization check in application code: branch, review, CI, staged deploy, revertible, with a full history. The control that matters more has the weaker process.

Policy as code closes that gap — with an important qualification about what "as code" should mean.

Policy Is Data, Not Application Code

The instinct is to put policy in the application repository and deploy it with releases. That gets you review and history, and it introduces a problem: enforcement changes now require a deployment.

That is wrong for two reasons. During an incident you need to tighten a scope or disable a connection in seconds, not through a release pipeline. And policy legitimately changes on a different cadence than code — new tenants, new rules, threshold adjustments — so coupling them means either slow policy or reckless deploys.

The workable model treats policy as versioned data with code-like discipline:

  • Stored in the platform, not in the application binary.
  • Every change produces a new immutable version with a stable identifier.
  • Changes are reviewable, with a diff, before taking effect.
  • The active version is a pointer, so activation and rollback are atomic.
  • Full history is retained, including versions that were never activated.
  • Applying a version takes effect in seconds, without a deployment.

Whether the authoring interface is a UI, an API, or a file synced from a repository is a workflow preference. What matters is that every version is immutable, identified, and attributable.

Every Record Names Its Version

This is the requirement that makes everything else useful, and it is the one most often missing.

When an enforcement decision is recorded, it must include the identifier of the policy version that produced it. Without that, an investigation reads the current policy and reasons about a decision made under a different one — and reaches wrong conclusions confidently.

The record should carry: the policy version, the specific rule that matched, the disposition, and the evaluated inputs. Then the question "was this agent restricted at the time?" has a definitive answer from the record rather than an inference from configuration history.

This also makes compliance evidence tractable. Demonstrating that a control was in force over a period means showing the version active during that period and the records referencing it. See audit trails that hold up and post-incident forensics for AI agents.

Asymmetric Review

Uniform review friction is wrong, because the risk of a policy change depends entirely on its direction.

Tightening — narrowing a scope, lowering a ceiling, adding a guardrail rule, disabling a connection — reduces what agents can do. Worst case it breaks a workflow, which is visible and quickly reversible. This should be fast: one person, applied immediately, recorded.

Loosening — widening a scope, raising a ceiling, disabling a guardrail, adding a connection — increases what agents can do. Worst case is a security incident. This should require a second approver.

The asymmetry matters operationally. If loosening and tightening carry the same friction, people batch changes and push them together, which means a tightening you wanted urgently waits behind a loosening under review. And an emergency tightening path must exist that no approval can block, because during an incident the containment action cannot wait for a reviewer. See maker-checker for platform administration.

Emergency loosening is the awkward case — sometimes a legitimate workflow is blocked and someone needs it working now. Handle it as break-glass: allowed with a single approver or self-approval, time-limited with automatic reversion, and flagged for retrospective review. See break-glass and emergency access.

Test Against Recorded Traffic

A policy change's real effect is invisible from its diff. A regex tightened to catch one pattern may match a thousand legitimate requests. A scope narrowing may break a workflow nobody documented.

The mechanism that resolves this is evaluating a candidate version against recorded traffic before activating it:

  1. Take a window of recorded requests — a day, a week.
  2. Evaluate the candidate policy against them without enforcing.
  3. Report what would have changed: newly blocked, newly allowed, newly redacted.
  4. Review the deltas. Sample the newly-blocked and confirm they are what you intended.

This turns policy review from reading a diff into examining consequences. For guardrail rule changes specifically it is close to essential, because false-positive rates on content rules are impossible to estimate by inspection.

It requires that you retain enough request content to replay evaluation against, which intersects retention and privacy decisions. A short window with redacted content is usually enough, and it is worth designing for. See staged guardrail rollout and shadow mode for the production-side version of the same idea.

Rollback

Rollback must be atomic and fast: change the active-version pointer, and every enforcement point picks up the previous version within seconds. Not a re-edit that reconstructs the old configuration by hand, which is slow and error-prone under pressure.

Requirements worth being explicit about:

Rollback is itself a recorded event, with who, when, and from which version to which. A rollback that leaves no trace is indistinguishable from a policy change.

Rollback must not silently discard a subsequent change. If someone else edited the policy after the version you are rolling back from, reverting the pointer discards their change too. Warn on it.

Cached evaluations must invalidate. Enforcement points that cache the compiled policy need a propagation mechanism with a bounded staleness window, and you should know what that window is.

Rollback across coupled configuration. A prompt version and a policy version may need to move together. Where changes span both, they should be grouped so rollback restores a consistent state. See versioning and rollback for AI agents.

What Belongs Under Version Control

The scope is broader than guardrail rules:

  • Guardrail rule sets — patterns, classifiers, thresholds, dispositions.
  • Connection scopes — enumerated tools, verbs, destinations, volume caps.
  • Spend ceilings and rate limits per agent, connection, team, and organization.
  • Approval thresholds — which actions require a human and at what values.
  • Trust-level requirements on connections.
  • System prompts and tool definitions, since these determine behavior and are security-relevant. Tool descriptions in particular enter the model's context.
  • Model routing rules, which determine which provider and jurisdiction handles data.
  • Retention and redaction settings — with the additional constraint that reducing audit retention should sit in a higher privilege tier than other policy changes.

Anything that changes what an agent can do or what evidence you retain belongs in the versioned set.

Common questions

Should policy live in Git or in the platform?

Either can work, and the properties matter more than the location. Git gives you familiar review tooling and a natural home for policy that is genuinely static; a platform store gives you fast activation, per-tenant policy, and no deployment coupling. The common hybrid is authoring in Git for the stable baseline, synced into the platform, with per-tenant and emergency changes made directly in the platform — with both paths producing immutable identified versions.

How do I handle per-tenant policy variation?

As layered versions: a baseline policy plus tenant overrides, each independently versioned, with the effective policy resolved and recorded per decision. Recording the effective resolution rather than just the layers is what keeps this debuggable — otherwise reconstructing what applied to a specific request means re-running the resolution logic against historical layer versions. See per-org feature overrides and canary.

Does this apply to guardrail model updates too?

Yes, and it is easy to miss. If a guardrail uses a classifier model, that model's version is part of the policy. A classifier update changes enforcement behavior without any policy edit, and if the version is not recorded on decisions, a behavior change becomes unexplainable. Pin the classifier version and treat updating it as a policy change requiring the same review and shadow evaluation.

Who should be able to change policy?

Separate the roles: building agents, granting their authority, and configuring guardrails and audit settings should not all be the same permission. The people who build agents will always want more scope; the approval should sit elsewhere. And the ability to weaken a control or reduce retention deserves the narrowest grant of all. See RBAC and custom roles for AI operations and insider threat in the age of AI agents.

How Praesidia approaches policy versioning

Praesidia treats guardrails, connection scopes, budget policies, and approval thresholds as versioned platform configuration rather than deployed code, so changes take effect in seconds without a release and every version is retained immutably. Every enforcement record names the policy version and the specific rule that produced the decision, which is what makes "what was in force at the time" answerable from the audit trail rather than inferred.

Sensitive changes — widening scope, weakening a guardrail, raising a ceiling — can require a second approver while tightening stays fast, and guardrail and audit configuration sit in a distinct privilege tier from agent configuration. Every change and rollback is written to the tamper-evident audit trail with the actor and the before-and-after state. See guardrails versus policies and the Praesidia documentation.