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:
- Framework agents —
watchlight[langgraph]/[pydantic-ai]/[claude-agent]: the same plugin you ship to production, wired to the in-process engine. - MCP servers —
watchlight-mcp: a policy enforcement point in front of any MCP server; a deniedtools/callnever reaches it.
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
- Cedar policy syntax and policy examples — write the policies your agents are governed by.
- Govern a framework agent and Govern an MCP server.
- Developer Edition vs Enterprise — when to graduate to the governed control plane (signed lineage, fleet governance, the operator console).
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.