Runtime safety for AI agents. Stop agents before they break things.
Project description
Edictum
Runtime contract enforcement for AI agent tool calls.
Prompts are suggestions. Contracts are enforcement. The LLM cannot talk its way past a contract.
Why This Exists
An agent says "I won't read sensitive files" -- then calls read_file(".env") and leaks your API keys.
A DevOps agent recognizes a jailbreak attempt, writes "I should NOT comply" in its reasoning -- then reads four production database credentials in the next tool call.
A medical assistant promises to respect patient privacy -- then dumps an entire clinical record into a chat response because nothing actually stops it.
Prompt engineering doesn't fix this. You need enforcement at the tool-call layer.
Without Edictum / With Edictum
Without Edictum -- the agent reads your secrets:
# Agent decides to read .env
result = await read_file(".env")
# => "OPENAI_API_KEY=sk-abc123..."
With Edictum -- the call is denied before it executes:
from edictum import Edictum, EdictumDenied
guard = Edictum.from_yaml("contracts.yaml")
try:
result = await guard.run("read_file", {"path": ".env"}, read_file)
except EdictumDenied as e:
print(e.reason)
# => "Sensitive file '.env' denied."
The contract that makes it happen -- contracts.yaml:
apiVersion: edictum/v1
kind: ContractBundle
metadata:
name: file-safety
defaults:
mode: enforce
contracts:
- id: block-sensitive-reads
type: pre
tool: read_file
when:
args.path:
contains_any: [".env", ".secret", "credentials", ".pem", "id_rsa"]
then:
effect: deny
message: "Sensitive file '{args.path}' denied."
Contracts are YAML. Enforcement is deterministic. The agent cannot bypass it.
How It Compares
| Approach | Scope | Runtime enforcement | Audit trail |
|---|---|---|---|
| Prompt/output guardrails | Input/output text | No -- advisory only | No |
| API gateways / MCP proxies | Network transport | Yes -- at the proxy | Partial |
| Security scanners | Post-hoc analysis | No -- detection only | Yes |
| Manual if-statements | Per-tool, ad hoc | Yes -- scattered logic | No |
| Edictum | Tool call contracts | Yes -- deterministic pipeline | Yes -- structured + redacted |
Quick Start
One line with a built-in template:
guard = Edictum.from_template("file-agent") # secret protection, destructive command denial
Or write your own contracts in YAML:
guard = Edictum.from_yaml("contracts.yaml")
See the quickstart guide to write your first contract and deny a dangerous call in five minutes.
What You Can Do
Write and enforce contracts
- Preconditions deny dangerous calls before execution
- Postconditions scan tool output -- warn, redact PII, or deny entirely
- Session contracts cap total calls, per-tool calls, and retry attempts
- Role-gate tools with
principalclaims andenv.*environment context on_deny/on_allowcallbacks for deny logging, alerting, or approval workflowsset_principal()for mid-session role changes (analyst -> operator escalation)
Test and validate
edictum validate-- catch schema errors at load time, not runtimeedictum test-- YAML test cases with expected verdictsguard.evaluate()-- dry-run contracts without executing the tool- CI/CD exit codes for pipeline gating
Ship safely
- Observe mode -- log what would be denied, tune contracts, then enforce
observe_alongside-- shadow-test new contracts next to productionedictum diff-- see what changed between contract bundle versionsedictum replay-- replay audit logs against updated contracts to predict drift- Multi-file composition with
from_yaml()and deterministic merge - Custom YAML operators and selectors for domain-specific contract logic
Automatic security (zero config)
- Secret values auto-redacted in audit events and denial messages
- Bash commands sanitized (passwords, tokens, connection strings)
- Regex inputs capped to prevent ReDoS
- Audit payloads capped at 32KB
- All contract errors fail closed -- never silently pass
Observe everything
- Structured audit events on every evaluation
- OpenTelemetry spans and metrics
- Policy version hash on every event
- File and stdout sinks included;
CompositeSinkfans out to multiple destinations --jsonCLI output for CI/CD pipeline integration
Works with 6 frameworks
| Framework | Integration | PII Redaction | Complexity |
|---|---|---|---|
| LangChain + LangGraph | as_tool_wrapper() |
Full interception | Low |
| OpenAI Agents SDK | as_guardrails() |
Logged only | Medium |
| Agno | as_tool_hook() |
Full interception | Low |
| Semantic Kernel | register() |
Full interception | Medium-High |
| CrewAI | register() |
Partial | High |
| Claude Agent SDK | to_hook_callables() |
Logged only | Low |
See Adapter Docs for setup, known limitations, and recommendations.
Use Cases
| Domain | What Edictum enforces |
|---|---|
| Coding agents | Secret protection, destructive command denial, write scope |
| Healthcare | Patient data access control, role-gated queries |
| Finance | PII redaction in query results, transaction limits |
| DevOps | Production deploy gates, ticket requirements, bash safety |
| Education | Student data protection, session limits per assignment |
| Legal | Privileged document access, audit trail for compliance |
See Use Cases for complete YAML bundles and wiring code.
Install
Requires Python 3.11+. Current version: v0.9.0.
pip install edictum # core (zero deps)
pip install edictum[yaml] # + YAML contract parsing
pip install edictum[otel] # + OpenTelemetry span emission
pip install edictum[cli] # + validate/check/diff/replay CLI
pip install edictum[all] # everything
Built-in Templates
guard = Edictum.from_template("file-agent") # secret file protection, destructive cmd denial
guard = Edictum.from_template("research-agent") # output PII detection, session limits
guard = Edictum.from_template("devops-agent") # role gates, ticket requirements, bash safety
Demos & Examples
- edictum-demo -- Full scenario demos, adversarial tests, benchmarks, and Grafana observability
- Contract Patterns -- Real-world contract recipes by concern
- Framework Adapters -- Integration guides for six frameworks
Research
Edictum was evaluated across six regulated domains in the GAP benchmark.
Links
- Documentation
- GitHub
- PyPI
- Changelog
- License (MIT)
Project details
Release history Release notifications | RSS feed
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 edictum-0.9.0.tar.gz.
File metadata
- Download URL: edictum-0.9.0.tar.gz
- Upload date:
- Size: 313.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
81a6d3fdb3ed13ef550db17b0d671c3e4ce50a8e4049d78a4f6899037c8dbf4d
|
|
| MD5 |
97a8b99d89d473d35f62ed29ee16ac72
|
|
| BLAKE2b-256 |
77ce4d721da959c2ec8fad64c420f476e425646231e78d9d077f58a22beae88e
|
File details
Details for the file edictum-0.9.0-py3-none-any.whl.
File metadata
- Download URL: edictum-0.9.0-py3-none-any.whl
- Upload date:
- Size: 77.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d129deef72d9873866963086396be16979dab6ba7bad6b075ddfe56ea1f2e03a
|
|
| MD5 |
601a4c6e8895e78c4fbf76edd71d6272
|
|
| BLAKE2b-256 |
058d81ab7cac97dcad67a69985ff93834b8093d86124bed958975f355a6abb91
|