Skip to main content

Developer Edition Quickstart

Govern an AI agent's tools and print a real, fail-closed ALLOW / DENY — with zero infrastructure. About five minutes.

1. Install

python -m venv .venv && source .venv/bin/activate
pip install watchlight

watchlight is the open Developer-Edition package; it pulls in the compiled authorization engine (watchlight-engine) automatically. Prebuilt wheels cover Linux (x86_64, aarch64) and macOS (Apple Silicon, Intel) for CPython 3.9+ — no Rust toolchain required.

2. Govern a tool

Decorate a tool with the intent it serves, load a Cedar policy, and the engine authorizes every call — allowing what a policy permits and refusing everything else before the function body runs.

from watchlight import govern, Denied

# Only the "research" intent is permitted. (Fail-closed: without a policy,
# every governed call is denied.)
govern.allow('permit(principal, action == Action::"research", resource);')

@govern.tool(intent="research")
def web_search(query: str) -> str:
return f"results for {query!r}"

@govern.tool(intent="transfer") # governed, but no policy permits it
def transfer_funds(to: str, amount: int) -> str:
return f"sent {amount} to {to}" # never runs in this example

print(web_search("watchlight")) # research → ALLOW, runs

try:
transfer_funds("attacker", 1000) # transfer → DENY, body never runs
except Denied as denied:
print("blocked before execution:", denied)
watchlight: governing 'my-agent' (dev mode, in-process engine)
watchlight: ALLOW research tool/web_search
results for 'watchlight'
watchlight: DENY transfer tool/transfer_funds Policy evaluation completed
blocked before execution: watchlight denied intent 'transfer' on tool/transfer_funds: …

That DENY — the tool refused before the side effect, by the same engine that governs production — is the whole point. Every decision is also written to a value-free audit trail at .watchlight/audit.jsonl.

3. Watch every decision live

watchlight dev            # → http://127.0.0.1:7000

A zero-dependency local dashboard that tails the audit trail and shows every ALLOW and DENY as it happens. Run your agent in one terminal and watchlight dev in another.

4. Govern what you already have

The same package governs your existing agents and servers:

Runnable, self-contained examples for all of these are in the examples/ directory.

Under the hood: the engine directly

The govern decorator is a thin layer over the engine. For fine-grained control you can call it directly — load policies, then authorize (principal, action, resource, context) and read back the decision:

import json
import watchlight_engine

engine = watchlight_engine.PolicyEngine()
engine.add_policy(json.dumps({
"name": "admin-allow-all",
"code": 'permit(principal == User::"admin", action, resource);',
}))

def authorize(principal, action, resource):
resp = json.loads(engine.authorize(json.dumps({
"principal": principal, "action": action,
"resource": resource, "context": {},
})))
return resp["decision"]

print(authorize("admin", "delete", "record")) # Allow
print(authorize("alice", "delete", "record")) # Deny (fail-closed)

This is the same call the framework plugins make; see Examples for more policy patterns.

Where to go next

Same code, one step away

Everything you write here works unchanged against the full Watchlight platform. Graduating to Enterprise points the same policies and calls at the governed control plane — a configuration change, never a rewrite.