Your agent can act. Decide what it may do, before it does.
onedoor is a policy engine for agent actions. Every action an agent proposes is checked against a policy you wrote, before anything touches the world: permitted within limits, held for a person, or refused with a reason. Every decision on a well-formed request is written to an append-only ledger.
Free and open source (Apache-2.0) · runs on your machine · no account · pip install onedoor
The problem
Most guardrails govern what a model says. Agents do things.
An agent with tools can move money, change records, send messages. The question that matters before each one is plain: is this action allowed, within what limits, and who said so?
Usually Rules in prompts and code
- What the agent may do is scattered across prompts, tool code and good intentions.
- An action nobody thought about is allowed: nothing said no.
- Limits like "no more than €500 a day" are counted by hand, if at all.
- Afterwards, nobody can show which rule allowed what.
With onedoor One door, one policy
- Every action goes through one check, against a policy file you can read.
- An action no policy declares is refused, with a reason that says what would allow it.
- Caps are reserved race-free, so two requests cannot share the last euro.
- Every decision is written with its reason code, and never edited afterwards.
What you get
Positions most frameworks leave as wishes.
Default-deny
An unlisted action type is not an error and not a pass. It is refused as default_deny, and the refusal says how to allow it.
Bounds before a person
Parameters are checked against declared ranges first, so an approval screen only ever shows a request that is physically sane.
Caps, counted honestly
Per-action and per-effect budgets (€ per day, per month, calls per day), reserved inside the deciding transaction.
Undo before autonomy
An action runs without asking only if its policy names how to reverse it. The loader refuses to start otherwise.
A kill switch that wins
Read before any policy lookup, and it outranks approvals already granted. While it holds, nothing runs on its own and nothing new is proposed. Reads stay open.
An append-only ledger
Decisions, results, denials, dry-runs and kill-switch blocks, each with a typed reason code. Exportable, and checkable row by row.
Four tiers
How much an action may do on its own.
Each policy puts an action type in one tier. New action types can start in dry-run: decided and recorded as "would have executed", with nothing done and no budget spent.
| Tier | Meaning | From the shipped payments examples |
|---|---|---|
| 0 · observe | Recorded, never blocked by the kill switch. For reads. | ledger.read |
| 1 · auto | Runs without asking: reversible and within bounds. | payments.reverse, declared at tier 1; its money.egress effect lifts it to an effective tier 2 |
| 2 · auto, capped | Runs without asking, up to cumulative caps. | payments.transfer: €500/day, €5000/month, €0.01–€2000 each |
| 3 · confirm | Proposed, and waits for a person to approve before it runs. | payouts.schedule |
Product tour
The Policy Studio, on the shipped payments examples.
Real screensScreenshots of onedoor 0.8.1's own Studio and receipt page, run on this site's machine with the payments example policies and a handful of sample requests. The Studio binds to your own machine only: nothing it shows leaves it.
What is allowed, and under what limits
Every policy in force, with its tier, caps, bounds and effects. The page says plainly what it is not: a list of what is blocked. Nothing needs to be listed to be blocked.

Upload a file; see what the loader would refuse
A policy file becomes a draft, even one the engine would refuse at start-up. The draft shows each refusal from the engine's own loader, in its own words: here, two auto-running actions that name no way to undo them.

A guided form and the raw rule, never out of step
Both panes are drawn from one parsed rule, so they cannot disagree. Validation runs on the engine's own loader as you type. The rules in force are not touched by anything on this page. (Its note that a numeric bound refuses a decimal-string amount is out of date: 0.8.1's engine accepts them.)

What changes, in sentences, before you ratify
Each changed rule is described as it was and as it would become. Ratifying is a deliberate step with a name on it, and it seals a receipt for the new version. There is no un-ratify: going back is a new version, and nothing already decided is rewritten.

A new version in force, and its receipt
The version in force is a digest of the policy set. The receipt records what it replaced, when, under which name, and the kill switch's state at that moment. Each ratification can be checked again later on the Verify page.

The kill switch, every budget, every pending approval
How much of each action's cap is consumed, reserved and free; open reservations; approvals and when they expire. The Studio only shows the kill switch. Throwing it lives on the decision service, behind its own admin key.
One thing this screen gets wrong: the shipped policies cap the money.egress effect at €750 a day and €6000 a month, and 0.8.1's Studio shows that effect's limit as "none declared". The engine enforces the cap; the display is a known defect, reported to onedoor.

Every decision, by action, verdict and version
Filter the ledger by action, verdict, policy version, origin or date. The Studio's note on this page that no caller identity is recorded is out of date: 0.8.1's decision service writes a keyed fingerprint of the decide key to the rows it permits. Rows from in-process callers carry none.

One decision, and the checks that back it
A static, read-only page built from the ledger: the verdict, the frozen parameters, the budget at the moment of decision and the policy version. Every value on it is read from a verified artifact; if the evidence does not check out, it shows the failure and none of the values.

Optional, and better together
onedoor decides. onetrace records. Each works without the other.
onedoor is a separate product with its own package. Use it on its own to govern what agents may do. Add it beside onetrace and each gated step of a run cites the decision that allowed it, which anyone can check against onedoor's export.
onedoor alone
Policies, approvals, caps and the kill switch, with an append-only ledger and its own receipt page. No onetrace needed.
onetrace alone
A checkable record of each run and the comparison that finds the first difference. No policy engine needed.
Together
A step marked as gated must cite a onedoor decision. The verifier fails a step that acted on a deny, or cited nothing, and matches each reference to a row of onedoor's export.
From a real run of onetrace 0.6.0 against onedoor 0.8.1's decision service, checked with onetrace-verify --decisions:
[PASS ] decisions: gated steps cite a decision -- 1 gated step(s), each citing a decision [PASS ] decisions: references are well formed -- 1 reference(s), each a well-formed onedoor-decision-ref/1 [PASS ] decisions: a step that acted cites a permitting decision -- 1 acting step(s), each citing a permitting decision [NOT-RUN] decisions: the permit covers the recorded action -- no permit recorded [PASS ] decisions: the decision is in the export -- 1 reference(s), each matching a row in the export by digest
The NOT-RUN row is honest: onedoor 0.8.1's decision service does not hand a signed permit to the caller yet (see Coming next). The code for this run is on the Developers page.
Where it plugs in
In front of the tools you already have.
An HTTP decision service
Any enforcement point, in any language, asks POST /v1/decide, acts, then reports the outcome. Admin keys are separate from decide keys: the process that asks is not the process that grants.
A Python library
Decide-and-reserve, then report the result, around your own call. The same checks, in-process.
An MCP proxy
Point an MCP host at the proxy instead of the tool server. Every tools/call goes through the full check; unknown tools are denied by default.
LangChain middleware
Wraps tool calls in a LangChain or LangGraph agent, including a mode that pauses for a person's approval.
An AI gateway example
An experimental LiteLLM guardrail adapter in onedoor's repository (not in the package; the litellm extra installs what it needs): model allow-lists, daily caps, and the gateway's MCP tool calls.
Signals out
Approval requests to a webhook (Slack-compatible payload), and OpenTelemetry spans and decision counters with the otel extra.
Honest by design
What it doesn't do, stated up front.
onedoor's own documentation lists its known limits. These are the ones to read before you trust a boundary to it.
Parameter rules can be dodged
A command built indirectly (for example, decoded at run time) carries nothing a parameter rule can match. URL patterns match the string, not where it leads, unless you use the opt-in URL block. Put a fail-closed network control in front of anything that matters.
Not its own witness
A signature checked against a key found in the same store reads self_consistent, never verified. Independence needs a key or an anchor the store does not hold.
One process, one writer
The documented deployment is one process with one SQLite writer. Running several writers on one store without that serialization opens a window between check and act.
Operator keys guard the service
Admin keys separate approving from asking on the HTTP decision service. The library and the MCP proxy's operator paths run in your own process, so who may approve there depends on how you deploy them.
Coming next
From "allowed" to "done, and confirmed".
What 0.8.1 does not do yet. Built in the open, described here so you know where it is going; no dates.
- Signed permits on every surface. 0.8.1 can already issue a short-lived signed permit, bound to the exact action, the sender's key and the request body and usable once, and its library includes the receiving system's own check. The decision service does not hand that permit to its caller yet; next, every surface will.
- The recipient's signed confirmation. The system that acted signs what it did with which permit, so a record can show the action was authorized and its effect confirmed.
- Decision references on every surface. Today the decision service and the library return them, and the MCP proxy builds one without passing it on; next, the MCP proxy, the gateway adapter and the LangChain middleware return them too.
Go deeper
Pick your level of detail.
What onedoor is
What it governs, how it looks, and what it does not do.
You are here For developersAdd it to your agent
Four commands from PyPI, a policy, the decide-act-report loop, the Studio, and onetrace.
Developers → For researchers and reviewersThe mechanism
The check order, the two phases, ratification, receipts and what "verified" means.
How it works →