Provably Safe
AI Agents.
AI agents decide which tools to call: shell commands, APIs, databases. PetriFlow intercepts every call and enforces rules they cannot break.
require lint before test
require test before deploy
require human-approval before deploy
limit deploy to 2 per session
# that's it. each rule is mathematically verified. 4 rules, 1 domain. Every reachable state checked. Zero bypasses possible.
AI agents call tools.
Nothing controls the order.
An AI agent works by calling tools: functions that send messages, run shell commands, query databases, deploy code. The model picks which tools to call and in what order. There is no runtime layer between the model’s decision and the tool executing.
"Most failures are not fancy exploits. Someone messaged the bot, and the bot did what they asked." - OpenClaw safety documentation
Prompts don’t enforce
You can write “always back up before deleting” in your system prompt. The model reads it. The model might follow it. Instructions are suggestions, not constraints.
Deny-lists are never complete
You block rm -rf but miss find -delete. You block DROP TABLE but miss TRUNCATE. The agent finds the gap you didn’t think of.
IAM controls who, not when
Identity management controls which users can call a tool. It doesn’t control the order. “This agent can run bash” doesn’t prevent it deleting without backing up first.
More tools, more risk
Every tool you hand an agent is a tool it can misuse. You’re scaling capability and risk together, with nothing constraining the interaction between tools.
Your instructions are prompts.
Make them enforceable.
Tools make your agent powerful. Rules make it safe.
The model reads this. The model might follow it.
Nothing makes it.
Only mapped commands can reach the shell. unlink, shred, anything unmapped is blocked.
Allowlist, not deny-list.
A WhatsApp bot that
can't message the wrong person.
A real agent skill for sending WhatsApp messages via CLI. The skill tells the model how to send messages. Three rules make it safe.
BLOCKED no recipient lookup yet
OK resolves to +14155551212
BLOCKED human hasn't approved yet
Human approves → manual gate opens
ALLOWED lookup done, human approved, token consumed
BLOCKED budget consumed, must look up again
Policy checks vs safety rules.
Four scenarios. Same requirements. Two fundamentally different approaches.
| Scenario | Policy checks | PetriFlow rules |
|---|---|---|
| Tool approval | if (!approved) deny()Bypassable check can be skipped, token can expire between check and use | require human-approval before shellProven safe no code path reaches shell without approval token |
| Message gating | if (!allowlist.has(channel)) deny()Bypassable allowlist stale, new channel added without check | require read-channel before sendProven safe send is structurally unreachable without prior read |
| Privilege escalation | if (BLOCKED.includes(tool)) deny()Bypassable list incomplete, agent finds unlisted equivalent | block privilege-escalationProven safe transition provably dead in all reachable states |
| Sandbox escape | if (!inSandbox()) deny()Bypassable misconfigured mount, container escape, race condition | require human-approval before host-accessProven safe only a manual gate can unlock host access |
Simple by default.
Powerful when you need it.
Three layers. Most users never leave the first.
import { backupBeforeDelete,
createGateManager } from "@petriflow/rules"
const manager = createGateManager(
[backupBeforeDelete()],
{ mode: "enforce" }
) require read-channel before send-message require human-approval before deploy-prod limit discord.sendMessage to 5 per session block discord.timeout # provably dead
defineSkillNet({
places: ["ready", "backedUp"],
transitions: [
{ name: "backup",
inputs: ["ready"],
outputs: ["backedUp"],
tools: ["backup"],
deferred: true },
{ name: "destroy",
inputs: ["backedUp"],
outputs: ["ready"],
tools: ["destructive"] },
]
}) Build something real.
Learn by doing.
Five step-by-step tutorials. Each builds a working agent with increasing complexity.
File Safety Agent
Sequencing + permanent blocking. The agent must back up before deleting and can never use rm.
Deployment Agent
Chained dependencies + human approval + rate limits. A full CI/CD pipeline the agent cannot skip.
Discord Bot
Dot notation tool mapping + rate limits. One tool, multiple actions, each gated independently.
DevOps Assistant
Cross-domain composition. 13 tools, 5 domains, 10 rules that enforce independently without interference.
Workflow Safety
Beyond if/then rules. Resource tokens, mutual exclusion, and a formally verified deployment pipeline.
Rules you can build.
Guarantees you can prove.
Examples of safety patterns PetriFlow can enforce. Each is verified exhaustively before your agent starts.
Don't message blindly
Read messages before you can send. Each read earns one send. Cycling dependency prevents spam.
Don't ship broken code
Lint gates test. Test gates deploy. Deploy requires a human and is rate-limited.
Don't share fabrications
Each fetch earns one share. Each share consumes one. No sources fetched, no sharing possible.
Don't destroy without a safety net
Destructive commands blocked until a backup covers the target path. The backup must actually succeed.
One safety layer.
Any agent framework.
PetriFlow is a standalone gate that sits between your agent and its tools. In-memory state machine checks, no network calls, sub-millisecond overhead. Thin adaptors connect it to whatever framework you already use.
Claude Code
Hook into Claude Code's tool pipeline. Gate bash, file operations, and MCP tools.
import { configure } from "@petriflow/claude-code"
const config = configure(".")
// merge into .claude/settings.json
// hooks: SessionStart, PreToolUse,
// PostToolUse, PostToolUseFailure Vercel AI SDK
Wrap tool definitions with gate logic. Works with generateText, streamText, and any model provider.
import { createPetriflowGate } from "@petriflow/vercel-ai"
const gate = createPetriflowGate([safetyNet])
await generateText({
tools: gate.wrapTools({ bash, write }),
system: gate.systemPrompt(),
}) OpenClaw
Replace policy checks with structural rules. Drop-in safety upgrade for OpenClaw agents.
import { createPetriGatePlugin }
from "@petriflow/openclaw"
// drop-in plugin for OpenClaw agents
const plugin = createPetriGatePlugin(
[observeBeforeSend()],
{ mode: "enforce" }
) pi-mono
Native extension for pi-mono. Full access to skill nets and tool mapping.
import { composeGates }
from "@petriflow/pi-extension"
// native extension with full net access
const gates = composeGates(
[testBeforeDeploy()],
{ mode: "enforce" }
) A type system
for agent tools.
TypeScript catches undefined is not a function before your code runs.
PetriFlow catches "agent deleted without backing up" before your agent runs.
Same idea: compile-time proof, not runtime hope.
Every rule compiles to a state machine. Every reachable state is enumerated. If the verifier says "safe," it means safe in every possible execution, not just the ones you tested.
states: 196 · terminal: 4 · deadlocks: 0
invariant "no unguarded delete"
PASS holds in all reachable states
invariant "privilege escalation impossible"
PASS transition provably dead
invariant "human gate required for prod"
PASS no auto path exists
All invariants verified.
0 violations across 196 states.
Deep dives.
The theory, the benchmarks, and why this approach works.
You're already building Petri nets. You're just building them badly.
The 1962 formalism behind PetriFlow. 30 lines of TypeScript, three worked examples, and proofs you can run yourself.
Why AI agents need more than prompts
The problem with prompt-based safety, and how Petri nets solve it.
PetriFlow vs n8n vs ReAct
An LLM agent with 3 tools modeled three ways. Same call efficiency. Only one proves termination, human gates, join semantics, and bounded iterations.