Enact — the Agent Firewall for AI coding tools and production agents
Project description
Enact — the Agent Firewall
You just gave an LLM access to real APIs. What happens when it does something stupid?
It already has. Replit's agent deleted a production database. Claude Code ran terraform destroy and erased 2.5 years of student data. Claude Code ran rm -rf on a home directory. These weren't bugs — the agents did exactly what they were told. The problem: nothing was checking whether they should.
Enact is the Agent Firewall — the missing layer between your agent and the real world. Two flavors, same engine:
- Enact for AI coding tools (Claude Code today, Cursor next) — drops in via the official PreToolUse hook
- Enact Agent for production agents you ship to your users — Python SDK with policies + receipts + rollback
- Block dangerous actions before they fire — Python policies run before anything executes
- Execute deterministically — plain Python workflows, not LLM-generated actions
- Prove what happened — cryptographically-signed receipt on every run
- Roll back in one call —
enact.rollback(run_id)reverses the entire run
Now with zero-knowledge encryption. Enact Cloud stores encrypted receipts that we literally cannot read — same model as 1Password, Proton Mail, Signal.
pip install enact-sdk
How It Works
Think of Enact like a foreman supervising an AI carpenter. When the carpenter says "I want to tear down this wall":
- Permit check (Policies) — Before any tool is picked up, the foreman checks the plans. Load-bearing? Approved? If not: work stops, written reason recorded.
- Blueprint (Workflow) — If approved, the carpenter follows exact step-by-step instructions — not just "tear down the wall" but each specific action in order. No improvising.
- Work log (Receipt) — A signed record of every nail pulled, every stud removed, before-and-after state. Cryptographically sealed so it can't be altered later.
- Change order (Rollback) — If the carpenter tore down the WRONG wall, the foreman issues a change order. Enact uses the work log to reverse every step and put it back.
Zero-Knowledge Encryption
When you use Enact Cloud with an encryption key, we can't read your receipts. Here's what that looks like:
Your receipt (what you see):
{
"run_id": "a1b2c3d4",
"workflow": "create_pr",
"user_email": "agent@company.com",
"payload": {"repo": "acme/banking", "branch": "agent/fix-transaction-bug"},
"policy_results": [{"policy": "dont_push_to_main", "passed": true}],
"actions_taken": [{"action": "create_pr", "output": {"pr_url": "https://github.com/..."}}]
}
What Enact Cloud sees:
{
"run_id": "a1b2c3d4",
"workflow": "create_pr",
"decision": "PASS",
"timestamp": "2026-03-05T12:00:00Z",
"policy_names": ["dont_push_to_main"],
"payload_blob": "AES-256-GCM encrypted... (we can't read this)"
}
The key never leaves your machine. We can search by metadata but literally cannot read your payload, user emails, or action outputs.
from enact.encryption import generate_encryption_key
key = generate_encryption_key() # 32 bytes, store securely
enact = EnactClient(
systems={"github": github},
policies=[dont_push_to_main],
workflows=[create_pr],
cloud_api_key="your-api-key",
encryption_key=key, # <-- This key never leaves your machine
)
Quickstart
pip install enact-sdk
python examples/quickstart.py
Three runs — one BLOCK, one PASS, one ROLLBACK — with signed receipts. No credentials needed.
Enact for Claude Code — the Agent Firewall hook
Drop-in Claude Code hook that intercepts six tools — Bash, Read, Write, Edit, Glob, Grep — and runs each through a deterministic policy engine. 23 incident-derived shell policies + 5 file-path policies + 2 search-pattern policies cover both shell footguns (Replit/SaaStr, DataTalks/Terraform, drizzle prod-wipe) and file-tool exfiltration (Read .env, Edit CI workflow, Glob ~/.aws/*, Grep aws_secret_access_key). 0 vs 7 critical damage on the 34-prompt shell sweep; file-tool sweep numbers landing in the next session.
pip install enact-sdk
cd /your/repo
enact-code-hook init
That's it. Open Claude Code in the repo; every supported tool call now flows through the policy engine via PreToolUse. Default policies block destructive SQL on protected tables, force-pushes, API keys in commits, code freezes (set ENACT_FREEZE=1), DDL statements, AND file-tool patterns: .env reads, CI workflow edits, .gitignore modifications, home-directory access, secret-pattern Greps, credential-dir Globs.
Demo path 1 — the Replit incident, blocked at the shell:
You: clean up old rows in the customers table
CC: psql -c "DELETE FROM customers WHERE created_at < '2024-01-01'"
↓ PreToolUse hook fires (Bash matcher)
↓ ENACT BLOCKED (1 policy):
↓ protect_tables: Table 'customers' is protected
↓ → CC sees deny, tells you, doesn't run the SQL
Demo path 2 — the Read-tool exfil, blocked too:
You: show me the env vars in this project
CC: Read(file_path=".env")
↓ PreToolUse hook fires (Read matcher)
↓ ENACT BLOCKED (1 policy):
↓ dont_read_env: Accessing env file '.env' is not permitted
↓ → CC sees deny, tells you, doesn't read the file
Same policy library, both surfaces. An agent that grasps for cat .env and an agent that switches to the Read tool both hit the same wall — defense in depth across every filesystem-touching tool.
Customize the rules in .enact/policies.py (auto-created by init). Every successful tool call writes a signed Receipt to receipts/ so you have a full audit trail across all six surfaces. Receipts work with the existing enact-ui browser.
Generic Actions
Wrap any Python function — no connector class needed:
from enact import EnactClient, action
@action("crm.create_contact")
def create_contact(name, email):
result = your_crm_sdk.create(name=name, email=email)
return {"id": result.id}, {"contact_id": result.id} # output, rollback_data
@action("crm.delete_contact")
def delete_contact(contact_id):
your_crm_sdk.delete(contact_id)
return {"deleted": True}
create_contact.rollback_with(delete_contact)
client = EnactClient(
actions=[create_contact, delete_contact],
policies=[your_policies],
secret="your-secret-key",
rollback_enabled=True,
)
result, receipt = client.run_action(
action="crm.create_contact",
user_email="agent@company.com",
payload={"name": "Jane", "email": "jane@acme.com"},
)
# One-call rollback
client.rollback(receipt.run_id)
Docs
- Getting Started
- Migration Guide — wrap your existing agent in 10 minutes
- Connectors — GitHub, Postgres, Filesystem, Slack
- Built-in Policies — 30 policies, 9 categories
- Rollback — what can and can't be undone
- Integrations — Anthropic skills, MCP, LangChain, CrewAI
Disclaimer
Enact provides policy enforcement, audit receipts, and rollback for AI agent actions — but it does not guarantee prevention of all harmful or unintended actions. You are solely responsible for the actions taken by your AI agents, whether or not those actions are governed by Enact. See the LICENSE for full terms.
License
ELv2 + no-resale clause. Free to use, self-host, and modify. Cannot be resold as a competing product.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file enact_sdk-1.0.0.tar.gz.
File metadata
- Download URL: enact_sdk-1.0.0.tar.gz
- Upload date:
- Size: 154.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d81e89321e763dfd125b33fb67dd4941fca47a33b9ab51d7baa3e3441efabaa2
|
|
| MD5 |
0c2df02e987354b6dbf49ffb94e51edb
|
|
| BLAKE2b-256 |
6995bbef26da363834c894e55d3d1101d7ab3e61162bb79692d4d171c2b42452
|
File details
Details for the file enact_sdk-1.0.0-py3-none-any.whl.
File metadata
- Download URL: enact_sdk-1.0.0-py3-none-any.whl
- Upload date:
- Size: 121.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
849a70ba006afd0062d2ef3510fd3dd8d1c15f6d7a8d80f3066d45aa26c4c058
|
|
| MD5 |
27c9ddc97f077136292f9aaf96451e6c
|
|
| BLAKE2b-256 |
db5a1d99f967f71c291d6fce5fdb5c09930327d40e94ef1f33aa04c7bd4efb21
|