Watchlight — Developer Edition
Govern an AI agent in five minutes. One install, zero infrastructure, same API as production.
Watchlight puts a policy decision point in front of every action your agents take. It authorizes each tool call and records a value-free audit trail.
The Developer Edition runs the real authorization engine in-process,
inside your own program. You do not need a server, a database or an account. You
get a governed ALLOW / DENY on your laptop, from the same code you ship to
production.
pip install watchlight # Python 3.9+, prebuilt wheels
npm install @watchlight/sdk # Node 18+, the compiled engine comes with it
Quickstart
Python — save as agent.py, run python agent.py:
from watchlight import govern, configure_default, Denied
configure_default(agent="research-agent") # the name on every record
# Permit ONLY "research". Fail-closed: everything else 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}"
@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 — denied first
print(web_search("watchlight docs")) # ALLOW → the body runs
try:
transfer_funds("mallory", 1000) # DENY → refused before the body runs
except Denied as e:
print(e)
TypeScript / Node — the same program ("type": "module" or a .mjs file):
import { govern, configureDefault, Denied } from "@watchlight/sdk";
configureDefault({ agent: "research-agent" });
govern.allow('permit(principal, action == Action::"research", resource);');
async function webSearch(query: string) { return `results for: ${query}`; }
async function transferFunds(to: string, amount: number) { return `sent $${amount} to ${to}`; }
const search = govern.tool(webSearch, { intent: "research" });
const transfer = govern.tool(transferFunds, { intent: "transfer" }); // nothing permits it
console.log(await search("watchlight docs")); // ALLOW → the body runs
try {
await transfer("mallory", 1000); // DENY → refused before the body runs
} catch (e) {
if (e instanceof Denied) console.log(e.message);
}
Both lanes print the same decisions:
watchlight: governing 'research-agent' (dev mode, in-process engine)
watchlight: ALLOW research tool/web_search
results for: watchlight docs
watchlight: DENY transfer tool/transfer_funds not authorized
watchlight denied intent 'transfer' on tool/transfer_funds: not authorized
Node names the same tools tool/webSearch and tool/transferFunds. Both also
print a one-time note that no audit sink is configured.
That DENY is the product. The transfer_funds body never ran, and the
decision is already on disk in .watchlight/audit.jsonl.
What you can govern
The table below shows what to install, and which call to use, for each kind of thing you might want to govern, first for Python and then for Node.
Python — pip install … |
Node — npm install … |
|
|---|---|---|
| A tool you wrote | watchlight → @govern.tool(intent=…) |
@watchlight/sdk → govern.tool(fn, { intent }) |
| A framework agent | watchlight[langgraph] · [pydantic-ai] · [claude-agent] · [deepagents] |
governedHooks() for the Claude Agent SDK, governTool() for LangChain / LangGraph.js |
| An MCP server | watchlight-mcp — a policy enforcement point in front of any MCP server |
point any MCP client at that same PEP |
| Your own app | govern.authorize(…); watchlight-agent-sdk adds sessions, preflight and local lineage (it imports as watchlight_core) |
await govern.authorize({…}) |
pip install "watchlight[all]" is one install for the whole Python lane, and
every Python example in the documentation runs after it. On the Node lane
@watchlight/sdk is the only install you need.
Both lanes write .watchlight/audit.jsonl, and watchlight dev tails it:
watchlight dev # → http://localhost:7000
Who is acting, and on whose behalf
A governed call keeps two identities apart: the runtime that is acting (the
agent), and the person or system it is acting for (the subject). A policy can
look at either one, or at both. In the example below, the flight-booker runtime
may book on behalf of any user:
// this runtime may book for any user — whoever it acts for
permit(principal is User, action == Action::"book", resource)
when { context.actor == "flight-booker" };
The table shows where each part of that answer lives in the request that a policy sees.
| Question | Where it goes |
|---|---|
| On whose behalf does this run? | principal — the subject, e.g. User::"db:4412" |
| Which runtime is acting? | context.actor, set by the SDK from the agent name |
| Through whose delegation? | context.actor_chain |
| Under what narrowed authority? | govern.scope(tools=[...]) |
That is what separates a policy from an if: it names whichever runtime is
acting and whoever it acts for. The SDK sets context.actor itself and
refuses a caller-supplied value that disagrees, so a policy can trust it.
Where to go next
→ The documentation index — every page, and when to read it.
Runnable programs live in examples/ — start with
governed_research_agent.py or
ts/examples/agent.mjs. Copy-paste policy recipes for
the high-stakes decisions — spending money, deleting things, messaging the
outside world, stopping a runaway agent — are in
examples/patterns/, each one run through the real engine.
A note on identity
The Developer Edition authorizes the principal you assert — the agent you
construct the governor with, or the Watchlight-Agent-Id on a governed MCP
request. It does not cryptographically prove the caller. On your own machine,
running both sides, that is the right trade.
Bind any non-loopback listener behind something that authenticates the caller — a reverse proxy doing mTLS or OIDC, or the Enterprise plane.
Identity hardens as you grow, without changing your policies:
- Developer Edition — the principal is asserted (cooperative, local dev).
- Next — an optional signed session token binds the principal to a key your process holds, so a prompt-injected sub-agent cannot rewrite a header to escalate. It still needs no external infrastructure.
- Enterprise — identity is attested: federated (OIDC) and workload (mTLS), verified across the fleet.
Only how strongly the principal is proven changes. The policies do not.
Developer Edition vs Enterprise
Enterprise points the same code at a running control plane, so there is nothing to rewrite. What changes is what happens around the decision. The first table lists what the Developer Edition already does and Enterprise keeps; the second lists what only Enterprise does.
What the Developer Edition does, and Enterprise keeps
| Capability | Developer Edition | Enterprise |
|---|---|---|
| Allow / require approval / deny, on real Cedar | ✅ in-process engine, policies from a local file | ✅ a running, scaled decision service |
| Strict-subset sub-agent attenuation, bounded by a delegation-depth limit | ✅ engine-side; the limit is configurable — max_delegation_depth per governor (default 8) |
✅ server-side; the limit is configurable per tenant (default 8) and per agent |
| Human-in-the-loop approvals | ✅ single-use tokens | ✅ across the fleet, with an operator queue |
| Content screening | ✅ rule-based and in-process: govern.sanitize, govern.screen |
✅ the same class of check as a managed service — centrally authored policies, applied to traffic your code never touches |
| Framework plugins | ✅ LangGraph, Pydantic AI, Claude Agent SDK, DeepAgents, LangChain.js, MCP | ✅ those plus Claude Code, Google ADK, AWS Bedrock, Microsoft Agent Framework, OpenClaw |
| Scopes across a process boundary | ✅ HMAC scope token — integrity within one trust domain | ✅ independently attestable scopes |
| Dashboard | ✅ watchlight dev → localhost:7000 |
✅ the operator console |
What only Enterprise does
| Capability | Developer Edition | Enterprise |
|---|---|---|
| Real-time enforcement effects — quarantine an agent, terminate a run, sever a delegation subtree, revoke authority | ❌ the policy loads and the call is denied; the containment action never fires | ✅ a contained agent's live connections are force-closed mid-run, server-side, with no cooperation from the agent; a sever durably collapses the delegation tree |
| Drift detection | ❌ | ✅ an agent leaving its declared plan is quarantined automatically, and its live connections drop |
| Runtime enforcement proxy | ❌ governs in-process, plus a PEP in front of one MCP server | ✅ agent egress is mediated on the wire as well as in-process, so a call has to clear both |
| Discovery and registry | ❌ | ✅ continuously scans your environments for agents and MCP servers, and tracks trust state |
| Signed execution lineage | ❌ local JSONL, unsigned | ✅ each step is signed, persisted and verified fail-closed, over a hash-chained audit trail anyone can re-verify |
| Fleet-wide revocation | ❌ one process | ✅ revocation is durable and enforced at the credential broker, across tenants |
| Attested identity | ❌ the principal is asserted | ✅ OIDC federation and mTLS workload identity |
| Air-gapped fleet deployment | ❌ the library itself runs offline, but there is no plane to deploy | ✅ on-premises, with local policy evaluation |
Everything the Developer Edition leaves out needs state outside your process: a fleet to revoke across, a plane to quarantine into, or a key to sign lineage with. It keeps every guarantee that fits in one process, and those work identically in both editions: fail-closed semantics, strict-subset attenuation bounded by a configurable delegation-depth limit, explicit scopes, and a value-free audit trail.
→ The platform · email sales@watchlight.ai
Govern locally. Manage centrally.
Watchlight Cloud — early access
One control centre for every Developer Edition deployment you run.
You already have the decision. Cloud gives you everything around it — central policy, decision history that outlives the container, an inventory of every agent, environments, and a team that can see all of it.
| Central policy | Author, lint and version Cedar bundles, then distribute them to every environment. |
| Decision history | Every Allow, Deny and NeedsApproval, retained past the life of the process that made it. |
| Agent inventory | What is running, under which identity, in which environment. |
| Team access | Invite the people who need to see the trail, without handing out a server. |
The authorization decision stays in your process — same engine, same
latency, and your agents keep deciding if Cloud is unreachable. It attaches to
the options you already have: audit_sink, counter_source, approval_store.
Nothing to rewrite, and no upgrade needed.
Open source, and the license
Everything you write against is open and Apache-2.0 — the govern decorator
and scope attenuation, the CLI and the watchlight dev dashboard, the framework
plugins, the MCP PEP's transport layer, and every example here.
You do not have to trust a black box to trust the decisions. Policies are
standard Cedar — open, formally specified, and
deterministic. The integration layer is readable, so you can see exactly what
the engine is asked and what it returns. And every ALLOW / DENY lands
value-free in a file you can grep.
The decision engine ships as a compiled wheel — watchlight-engine and the
watchlight-mcp runtime — under the Watchlight Developer Edition license. Both
are free to use, including in production and commercially. A commercial
license is needed only to re-offer the engine itself as a hosted authorization
service.
Want the engine source, an air-gapped build, or to govern a fleet in production? email sales@watchlight.ai.
Metadata
Release files for watchlight 0.13.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| watchlight-0.13.2.tar.gz | 260.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| watchlight-0.13.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 426.9 kB
Release files / watchlight-0.13.2.tar.gz
| Download URL | watchlight-0.13.2.tar.gz |
|---|---|
| Size | 260.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
60c27dc879ff9d18f25c76cdb5d7f505f521c278e7f209b02e99163e6e579588
|
|
BLAKE2b-256 checksum How to use checksums |
16b28e8e354b6ce0a1abaa4932daac21e26269ad34476a70633c1dde5a396076
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.
Transparency logRelease files / watchlight-0.13.2-py3-none-any.whl
| Download URL | watchlight-0.13.2-py3-none-any.whl |
|---|---|
| Size | 166.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2c5db233652be21638925a971f098141dda08dec7974e6321e4540e794a6e06f
|
|
BLAKE2b-256 checksum How to use checksums |
f02e8d0d5608b91e00f57718472a02897a34dc4a889aa438f73204287be7a211
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.
Transparency log