✓ Writ

For AI agents

Your agent is about to
do something.

— make sure it should.

Writ scans your Python, TypeScript, or JavaScript agent, finds the writes it can make — refunds, permission changes, emails, deletes — and puts a gate in front of each one. ALLOW or DENY, before it runs. Every decision goes to your tamper-evident audit log.

Gate your first write in five minutes.

Wrap this repo in Writ: install pywrit and run writ scan. Show me every write it finds, then gate them — ask for my approval before applying.
$ pip install pywrit && writ scan .
$ npm install -g pywrit && writ scan .
$ curl -fsSL https://withwrit.com/writ -o writ && chmod +x writ && writ scan .

10,000 receipts/month free, no credit card. Then $1 per 1,000 receipts

Writ scan
terminal
$ writ scan .
 
payments.refund payments/refunds.py:42 HIGH RISK ✗ unprotected
email.send notify/email.py:23 MEDIUM ✗ unprotected
 
2 writes unprotected. Run --apply to gate them.
$ writ check --key $KEY --verb payments.refund …
{ "decision": "DENY", "reason": "no live grant" }
$ writ sandbox …
$ writ check --key $KEY --verb payments.refund …
{ "decision": "ALLOW", "authToken": "writ_at_…" }
Audit record
{
"verb": "payments.refund",
"decision": "ALLOW",
"sponsorId": "you",
"agentId": "billing-agent",
"receiptId": "rcpt_01j…",
"chainHash": "a3f9…c21e",
"prevHash": "77b1…9aa0",
"createdAt": "2026-09-30T14:22:11Z"
}

How it works

Add a gate. Keep a record.

One call before the write. Policy decides. Every decision — allowed or denied — lands in your audit log.

01

Scan

Run writ scan . on your codebase. The scanner finds likely write sites in Python, TypeScript, and JavaScript — Stripe calls, database mutations, file writes — and maps them to verbs. On Python it also inserts one writ_check() per function. Nothing changes until you run --apply.

$ writ scan . --apply
02

Gate

Before the write runs, the agent calls POST /v1/check with five fields: sponsor, agent, verb, target, purpose. Policy returns ALLOW, DENY, or STEP_UP. The grant is spent on use — it cannot be reused for a different write.

$ writ check --verb payments.refund …
03

Verify

Every decision writes to your audit log — whether it ran or not. HMAC-SHA256 chained. Run writ verify-chain to prove no record was altered. Stream live to any SIEM with writ stream.

$ writ verify-chain --key $WRIT_API_KEY
Tamper-evident

HMAC-SHA256 chained. Alter any stored record and writ verify-chain catches it.

NIST AU-9

Supports NIST SP 800-53 AU-9. Hash-chained audit records. Your assessor determines compliance.

Fail closed

No key, no network? No ALLOW. An ALLOW only exists after a live policy evaluation — the gate can't fail open.

Instant revoke

One call cuts off any agent, everywhere, immediately. No redeploy.

Integration

One check. Before the write runs.

The Python client wraps the gate. DENY raises PermissionError and stops the write. ALLOW returns a 90-second token bound to exactly what you're about to do.

Python
from pywrit.client import WritClient
 
client = WritClient(api_key="writ_...", base_url="https://api.withwrit.com")
 
@app.post("/refunds")
def refund(order_id: str):
result = client.check(
sponsor_id="you",
agent_id="api",
verb="payments.refund",
target=order_id,
purpose="refund order",
)
if result.decision != "ALLOW":
raise HTTPException(403, "Denied by Writ")
# proceed with refund

Coverage: Python — scan and auto-instrumentation. TypeScript and JavaScript — scan-only (via the polyglot extra locally: pip install "pywrit[polyglot]"; bundled by the GitHub App). Go: not yet covered.

MCP

Already using MCP? Drop Writ into Claude, Cursor, or any compatible agent — 17 tools, no code changes.

MCP setup guide →
Enforcement

Checks can be skipped. Execution cannot. In enforcement mode Writ holds the credential in its vault and executes the write itself — Stripe, GitHub, Salesforce — so a wrapped write cannot complete without an ALLOW, and the agent never holds the raw credential.

Enforcement docs →
GitHub App

Install the Writ GitHub App on selected repositories and every push and pull request is scanned for ungated write-sites, with results as a check run and in the portal.

GitHub App docs →

Audit log

Every decision on record. Permanently.

ALLOW or DENY, STEP_UP or REVOKE — all of them land in your audit log the moment they happen. Hash-chained. No gaps. Verify anytime.

Even DENY writes a record. The refusal is as auditable as the allow.
Each entry is chained to the previous one. Alter anything and writ verify-chain catches it.
Stream live to Splunk, Datadog, or any SIEM with writ stream.
$ writ verify-chain --key $WRIT_API_KEY
{"ok": true, "checked": 1204, "tip": "a3f9…c21e"}
{
"verb": "payments.refund",
"decision": "ALLOW",
"sponsorId": "you",
"agentId": "billing-agent",
"target": "ord_8812",
"purpose": "refund order",
"policyVersion": 7,
"receiptId": "rcpt_01j…",
"chainHash": "a3f9…c21e",
"prevHash": "77b1…9aa0",
"createdAt": "2026-09-30T14:22:11Z"
}
Kill switch

Something goes wrong. One call. Every later check for that agent is DENY — everywhere, immediately, no redeploy.

$ writ revoke --sponsor you --agent billing-agent --reason "unauthorized activity" --sponsor-token $WRIT_SPONSOR_TOKEN
{"principal": "you#billing-agent", "revokedAt": 1759000000}

Pricing

Free until you're sure.

10,000 receipts/month free, no credit card. Then $1 per 1,000 receipts. The meter is your own audit log, so you can verify your bill.

Free
$0

forever, for the first 10,000 audit log entries/month

  • 10,000 audit log entries/month
  • Full CLI and API access
  • Tamper-evident audit log
  • No credit card
Get started
Enterprise
Let's talk

Annual commit + volume, sized to your usage

  • Production verbs on customer systems
  • Custom policy
  • Priority support
hello@withwrit.com