Skip to main content
A policy is a YAML document with rules that match a tool call and assign an effect (allow or deny). Policies are authored in the dashboard or pushed via the API, versioned on each save, and published as a signed bundle that the SDK pulls every 30 seconds.

Anatomy

apiVersion: agent-governance.io/v1
kind: Policy
metadata:
  name: deny-destructive-fs
  description: Block any agent from removing files.
  labels:
    owner: platform
spec:
  defaultEffect: allow
  rules:
    - id: no-delete
      description: Deny delete_file calls
      effect: deny
      conditions:
        - field: tool_name
          operator: eq
          value: delete_file
    - id: no-rm-shell
      effect: deny
      conditions:
        - field: tool_name
          operator: eq
          value: shell.exec
        - field: input.command
          operator: contains
          value: "rm -rf"

Fields

FieldRequiredNotes
apiVersionYesAlways agent-governance.io/v1.
kindYesAlways Policy.
metadata.nameYesHuman label. Doesn’t have to be unique.
metadata.descriptionNoSurfaces in the dashboard.
metadata.labelsNoRecord<string, string> for ownership tagging.
spec.defaultEffectYesallow or deny. Applied if no rule matches.
spec.rulesYes (≥1)Evaluated top-to-bottom; first match wins.

Rule conditions

A rule matches when every condition in its list matches (logical AND). Condition shape:
- field: <dotted.path.into.request>
  operator: <eq | neq | in | nin | contains | starts_with | ends_with | gt | gte | lt | lte | regex>
  value: <string | number | bool | string[] | number[]>

Common fields

FieldTypeSource
tool_namestringFirst positional arg to evaluate().
agent_idstringBound to the JWT’s sub claim.
input.<key>anyFrom metadata.input passed to evaluate(). Supports nested paths.
args.<index>anyPositional args (LangChain adapter).
kwargs.<key>anyKeyword args (LangChain adapter).
dlp_detectedboolSet by the inline DLP scanner if enabled.
dlp_severitylow|medium|highHighest severity detected.
dlp_typesstring[]Types that fired (EMAIL, SSN, CREDIT_CARD, …).

Versioning

Every save creates a new version. Versions can be:
  • draft — saved but not published. Visible in the editor, not in the bundle.
  • active — currently published. There is at most one active version per policy.
  • archived — was active, has been replaced. Kept for audit.
When you click Publish in the dashboard, the new version becomes active and the bundle version increments. SDKs pulling the bundle see the new policies on their next poll (~30s).

Dry-run

Before publishing, click Dry-run in the editor. Rubric replays the last 1000 audit events through the candidate policy and shows you:
  • How many events would have been allowed vs denied.
  • Which agents are most affected.
  • A diff against the currently-published version.
Always dry-run a deny rule before shipping it. The audit replay catches over-broad matchers cheaply.

Scope: which agents does this policy enforce against?

Scope is prescriptive and operator-controlled. Every policy carries an explicit list of agents it enforces against, stored separately from the YAML. The list is editable on the policy detail page and required at publish time.
StateMeaningSDK behavior
Empty scopePolicy applies to no agents.Bundle build excludes the policy from every per-agent slice. The policy is inert.
≥ 1 agentPolicy applies only to the listed agents.Each scoped agent’s bundle includes the policy. Unscoped agents don’t see it.
This is explicit opt-in by design. A newly-enrolled agent is unpoliced until an operator scopes a policy onto it — the cost of accidentally leaving a policy off one agent was judged lower than the cost of an over-broad policy quietly enforcing somewhere unintended.

How it ships

  • Scope is not part of the policy YAML — it’s a separate per-policy agent list. Forking a template never carries scope across orgs; the fork prompts the operator for fresh agent selection.
  • Each agent gets its own bundle slice. Editing scope on a published policy fans out fresh bundles to the affected agents only — no version bump, no SDK restart.
  • Every scope add/remove is recorded in the audit ledger right alongside tool-call decisions, on the same tamper-evident chain. Config tampering is as detectable as decision tampering.

Required at publish

The dashboard disables the publish button until you pick at least one agent. Drafts are allowed to have empty scope — they’re not in the bundle anyway.

Filtering rule logic by agent metadata

Conditions like agent_environment eq prod still work for narrowing which calls inside a scoped agent match a rule. They’re orthogonal to scope: scope picks which agents see the policy; conditions pick which calls within those agents trigger which effect.

Default-allow vs default-deny

The default spec.defaultEffect: allow is the right starting point — it lets you ship an integration with zero policies and add denies incrementally. For high-trust environments, flip to defaultEffect: deny and add explicit allow rules. The SDK supports both; nothing in the framework prefers one model.