Skip to main content

govern

Non-cooperative policy enforcement for AI agents: govern wraps the actual client or tool call an agent makes, so there's no audit() hook for the agent to just skip.

Per-call checks only ever see one agent's one action. govern also watches for coordinated behavior across agents — the starter policy ships a commented-out example that alerts when more than 3 distinct agents read *customer_pii* within an hour, a pattern none of them would trip individually.

pip install govern-agent
govern init            # write a starter policy (rules + an aggregate_rules example)
govern demo            # watch a scripted agent get blocked, live
govern dashboard       # pip install "govern-agent[dashboard]" first — see it happen

Why it's built this way: ## Design decisions below. Full command set: ## CLI Reference.

Install

pip install govern-agent
# or from source:
pip install -e .

Try it

govern demo                  # scripted agent hitting a production policy
govern init                  # write govern.yaml into the current repo
govern policy                # show the active policy
govern check s3:DeleteBucket prod-billing --env production
govern log --blocked-only

Use it

import boto3, govern

govern.init(agent_id="cost-optimizer", environment="production")

s3 = govern.guard(boto3.client("s3"), service="s3")
s3.list_buckets()                          # allowed
s3.delete_bucket(Bucket="prod-billing")    # raises PolicyViolation

Three interception points:

# 1. wrap a client — method calls become "<service>:<Operation>"
client = govern.guard(some_sdk_client, service="k8s")

# 2. decorate a tool the agent can call
@govern.guarded("db:DropTable", resource=lambda a, k: k["table"])
def drop_table(table): ...

# 3. replace subprocess.run
govern.run("terraform apply -auto-approve")

Design decisions

Most agent-guardrail prototypes expose an audit() function the agent is supposed to call before doing something dangerous. That only works if the agent cooperates — which is exactly the assumption you cannot make. It's a logging convention, not a control.

govern wraps the client, the tool function, or the subprocess call instead. The agent doesn't get a choice about whether policy runs, because govern owns the only path to the resource.

Policy itself is data — a YAML file a customer can edit, review, and commit — not if statements in Python.

The cross-agent detection mentioned at the top isn't a separate bolt-on: aggregate_rules are validated by the same policy loader as rules, evaluated by the same detector in live enforcement, policy simulate, and the dashboard — see ### aggregate_rules under ## Policy below for the schema.

Policy

version: 1
default_effect: deny

rules:
  - id: deny-destructive-prod
    effect: deny
    severity: critical
    description: Never let an agent destroy production resources.
    actions: ["*:Delete*", "*:Destroy*", "*:Terminate*"]
    resources: ["*"]
    environments: ["production", "prod"]

Matching is glob-based across three fields (actions, resources, environments). Precedence is deny > require_approval > allow; anything matching no rule falls through to default_effect. Policy is discovered like .git — GOVERN_POLICY env var, then govern.yaml walking up from cwd, then the bundled default.

aggregate_rules

Regular rules only ever see one call at a time. aggregate_rules watch a rolling window across all agents for a count that crosses a threshold — distinct agents touching a resource, or total calls against it — and either log it or block the call that crosses the line:

aggregate_rules:
  - id: too-many-readers-of-customer-pii
    effect: alert   # or "deny" to block the call that crosses the threshold
    description: More than 3 distinct agents read customer_pii within an hour.
    actions: ["*:Get*", "*:List*", "*:Read*"]
    resources: ["*customer_pii*"]
    environments: ["production"]
    window: "1h"
    threshold:
      type: distinct_agents   # or "total_calls"
      max: 3

Same glob matching as rules. Empty by default (aggregate_rules: []) — enabling one is a deliberate choice. effect: alert lets the action through and writes a separate {"alert_type": "collusion"} line to the audit log; effect: deny overrides the per-call decision and records both the original and final outcome. govern policy simulate and the dashboard's /api/cross-agent both replay history through the same detector, so you can see what an aggregate rule would have caught before turning it on.

Modes

  • enforce (default) — denied actions raise PolicyViolation
  • monitor — nothing is blocked, but everything that would have been is flagged in the audit log and session summary

Monitor mode is how you get a new customer to say yes: run it for a week, show them what their agents actually tried to do, then turn on enforce.

Approvals

govern.init("migration-bot", "production", approval=govern.prompt_approval)

Any rule with effect: require_approval calls the handler. The default handler denies, since there's usually no human attached to an agent run.

Audit log

Every decision is appended as JSONL to .govern/audit.jsonl (override with GOVERN_AUDIT_LOG). An unwritable log never takes the agent down.

Dashboard

pip install "govern-agent[dashboard]"
govern dashboard --port 8000

A local, read-only view over .govern/audit.jsonl — a live activity feed, a blocked-vs-allowed timeline, and a per-agent summary table. It only ever tails the log; it never writes to it. fastapi/uvicorn are an optional extra, not part of the core install.

govern --audit-log path/to/audit.jsonl dashboard --port 9000

CLI Reference

Commands follow a Docker/kubectl-style noun-verb pattern. Every existing invocation below the line still works exactly as it always has — the noun-verb forms are additions, not replacements.

Command What it does
govern init write a starter policy file
govern check <action> [resource] evaluate one action against the policy
govern log read the audit log
govern demo run a scripted agent against the policy
govern dashboard launch the read-only monitoring dashboard
govern policy list show the active policy's rules
govern policy validate <file> schema-check a policy file, no side effects
govern policy diff <a> <b> structural diff between two policy files
govern policy apply <file> replace the active policy file (validated first, backed up)
govern policy simulate <file> replay audit history through a candidate policy
govern agent list every agent govern has seen
govern agent inspect <agent_id> one agent's full event history
govern completion bash|zsh print a shell completion script
(legacy) govern policy / govern policy --validate unchanged — same output as always
(legacy) govern agents [--agent name] unchanged — same output as always

Read-only commands (policy list, agent list, agent inspect, log) take -o table\|json. -o json always wraps the payload in an envelope with a top-level apiVersion: "govern/v1", so a future schema change won't silently break a script parsing it:

govern agent list -o json | jq '.agents[] | select(.blocked > 0)'

Simulating a policy change before shipping it

policy simulate never touches the real audit log, the active policy, or a live approval handler — it replays recorded history through a candidate policy file using the exact same rule-precedence logic (deny > require_approval > allow) that live enforcement uses, so what it predicts can't drift from what enforcement would actually do.

govern init tightened.yaml            # start from the current policy
$EDITOR tightened.yaml                # add a rule, e.g. deny k8s:Scale* everywhere

govern policy simulate tightened.yaml --since 7d
#   CHANGED OUTCOMES (2)
#     ALLOW -> DENY   k8s:ScaleDeployment -> checkout   agent=k8s-autoscaler   2026-09-01 09:14:02
#         new rule: deny-k8s-scale [critical]
#   6 unchanged event(s) (same outcome as recorded)
#   new denials introduced: 1  (see --fail-on-new-blocks)

# gate a CI job on it: fails the build if the new policy would introduce a new denial
govern policy simulate tightened.yaml --since 7d --fail-on-new-blocks

# happy with it? replace the active policy (validated + backed up first)
govern policy apply tightened.yaml

--against <path> points at a different audit log than the configured one; --since 24h|7d|30m windows the replay; --diff prints only the changed events, skipping the unchanged-count summary, for piping into other tools.

Status

v0.1.0 prototype. The API is intentionally small so the core idea can move without breaking the surface.

Metadata

Release files for govern-agent 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for govern-agent 0.1.1
File Size Uploaded
govern_agent-0.1.1.tar.gz 58.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for govern-agent 0.1.1
File Interpreter ABI Platform
govern_agent-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 112.7 kB

Release files / govern_agent-0.1.1.tar.gz

Download URL govern_agent-0.1.1.tar.gz
Size 58.8 kB
Tags Source
SHA-256 checksum
How to use checksums
9aa6338e8ad750ee093e02d35ffd090d21970e285138f99196160fc039096555
BLAKE2b-256 checksum
How to use checksums
1401daed77e576b5594b01d1c6c20e011b1bcef7203825d2b3ad24ddc5b53198
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release files / govern_agent-0.1.1-py3-none-any.whl

Download URL govern_agent-0.1.1-py3-none-any.whl
Size 53.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d5f97e90b96e3ec4f66f91c5a2319624e66822614b8e93e093b28aa65584a954
BLAKE2b-256 checksum
How to use checksums
f7bdf3a995bbf65cca6180a3cb652a7656bfb15b9b9884805ca684884d88af40
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page