
CTRLRun Operator
io.github.CTRLRun由 GitHub 帳號 CTRLRun 發布v0.12.2更新於 Oct 24, 2026
Answer CTRLRun approvals from an MCP client: list what is held, inspect it, grant or deny.
概覽
CTRLRun Operator 讓助理列出、檢視並回覆 CTRLRun 中等待人工核准的受限動作請求。
- 可以做什麼
- 列出被擱置等待人工核准的動作,並檢視其詳細內容,包括授權所對應的確切動作。在 MCP 用戶端中核准或拒絕擱置的請求。回覆會進入 CTRLRun 的原則檢查,由其決定放行、要求核准或阻擋退款、部署、刪除、權限變更等動作。核准綁定到確切動作且僅能使用一次,每次決定都會留下收據。
- 適用情境
- 當代理透過 CTRLRun 執行,而你希望在助理中而非命令列回覆核准請求時使用。僅當 CTRLRun 已在管控傳送、付款、刪除、部署或權限變更等動作時才有意義;唯讀代理不需要它。
- 執行需求
- 本機 Python 環境並安裝 ctrlrun 套件,以及已設定的 CTRLRun 原則與狀態存放區(預設 SQLite,也可用 Postgres)。以本機 stdio 程序執行,僅限桌面端。
安裝
在 SourceWeft 中
- 開啟 儀表板中的 CTRLRun Operator,將其新增到工作區。
- 為需要使用其工具的對話啟用該服務。
Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。
其他 MCP 客戶端
參照 儲存庫 中的啟動說明。
README
ctrlrun stops AI agents from taking wrong, restricted, or malicious actions in your workflows.
Every action is checked against your rules before it runs. Allowed actions go through.
Sensitive ones wait for a person. Forbidden ones are blocked.
Execution safety for AI agents. A Python library that sits between the decision to act and the call that acts.
A consequential action happens at most once, exactly as approved, and leaves a receipt.
When the outcome is unknown, ctrlrun says so instead of guessing.
Runs in production on a single file, or on Postgres across hosts. Apache-2.0.
[Clones] [PyPI] [Downloads] [Docs] [CI] [CodeQL] [Fuzz] [Tests] [ctrlrun verified] [OpenSSF Scorecard] [OpenSSF Best Practices] [License]
What it does
The model guesses. ctrlrun does not. The ticket says refund €500. The agent asks for €5,000, one extra zero. The tool is in its list, the arguments are well formed, and the model is completely confident. Nothing above the call disagrees, because nothing above the call is a check: a tool being callable is not permission to call it with those arguments.
ctrlrun is that check. It reads the arguments about to leave your process and answers what may happen to them: let it run, ask a human, or stop it cold. Four rules do the work, and each one is a test in this repository before it is a sentence here.
The third one is the half people forget. A correct €500 refund commits at the provider and the
reply is lost coming back, so the agent retries. Retry libraries, agent frameworks and tool
loops collapse this failed into I do not know what happened. ctrlrun keeps them apart: a
lost reply is AMBIGUOUS, never FAILED, and a retry against an AMBIGUOUS effect is refused
until a human, or a reconcile hook, says what happened.
What ctrlrun demo shows: five failures and five refusals, byte for byte
Approval and delegation ids are generated per run; everything else is exactly what the demo
prints, and a test fails if the two drift apart. No network, no external service, under a
second. pip install ctrlrun && ctrlrun demo runs it locally in about the same time.
Where it stops. It does not detect prompt injection: it contains the consequence rather
than reading the cause. It cannot promise exactly-once against a remote it does not control, it
refuses to knowingly act twice, and it rolls nothing back. Receipts are chained, so an alteration
is detected; a truncation at the end and a forged append are not, because the head that would catch
them is a row in the same database, and closing that is what ctrlrun anchor is for. They are not
signed: alteration is not authorship. The badge above means the
declared guarantees pass in the setup they ran against, and it does not mean secure, safe,
compliant, certified or audited:
what the badge means
· OWASP-AGENTIC-TOP10.md
names the four entries this does not address.
If an agent only reads and answers, you do not need ctrlrun. The moment it can send, pay, refund, delete, deploy, grant, revoke, approve, submit, purchase or cancel, you do.
Use it in three steps
The animation above is this section, recorded against the real library: one policy file, two short programs, four commands, nothing staged.
1. Install it.
2. Write down what the agent may do. One file, ctrlrun.yaml. Amounts are integer minor
units, so 50000 is €500. Both ends of every band are bound, because an upper bound alone lets
a negative amount through, and a refund of a negative amount is a charge. Anything not listed is
denied; there is no default-allow.
3. Wrap the call that has the consequence. The decorator names the action, the effect key
names the consequence it has in the world, and the context names who is acting. stripe here
is a stand-in that records calls instead of making them.
The €500 refund runs on its own. The €5,000 one stops and names the request a human answers:
A human answers from the shell. The grant names the hash of the exact action the human read, and when it lapses. Ids, hashes and dates are generated per run; yours differ.
The agent presents it, then tries to spend it on something else. The first call is exactly what the human approved, and it runs. The second is the same approval with one digit changed, and it matches nothing:
Every attempt, refusals included, left a receipt, and ctrlrun receipts lists them. That is the
whole integration: a policy file, a decorator, a context, and with_approval to present a grant.
Money is the example, not the scope. A condition is <argument>_<op>, so the same policy
language reads role_in: [reader, viewer] or replicas_lte: 10 as easily as amount_lte, and
nine domains below have one policy each.
Protect your first action walks the same path
with every output explained ·
Policy YAML reference ·
Cookbook: refunds, deploys, IAM, deletions, email, MCP.
Three ways to use it
You probably do not need an adapter. @protect covers anything running in this process: a
raw model call, a LangChain tool, a hand-rolled loop, a cron job. The gateway covers anything
that reaches its tools over MCP, in any language.
It works with agents you can and can't modify. WhatsApp, Slack and Teams bots, ChatGPT, Cursor, Codex, OpenAI Agents: any AI agent you have. ctrlrun checks the action, not the agent, so if the agent acts through a tool server or an API you run, the action is checked, and the agent is not rebuilt, redeployed or told. Agents you can't modify says where the boundary goes for each kind.
An adapter exists for one reason: to route an approve decision through the framework's own
interrupt, so a human answers where they already answer. There is never a second place to say
yes. ctrlrun-langgraph
gives prevention, because the resumption carries the arguments and core re-checks them
against the hash.
ctrlrun-openai-agents
gives attribution, because that SDK records that a call was approved and not what its
arguments were. None of the three is only for agents: a worker, a webhook handler and a
scheduled job cannot tell a first attempt from a retry either.
It is an official LangChain middleware integration.
ctrlrun-langchain
is listed in LangChain's middleware integrations.
Its CTRLRunMiddleware goes in create_agent(middleware=[...]) and gates every tool call
through LangChain's own wrap_tool_call, tools you did not write included: a refused call never
reaches the tool, and the model is told which rule refused it.
Use the LangChain middleware has the setup.
How it works
Every protected call, whichever way it arrives, goes through the same seven steps. Only then does it reach your systems.
- Normalize: one action, one id. The call becomes an
Action: a name, canonical arguments (sorted keys, no floats), a resource, the principal. Its SHA-256 is the action hash. - Decide: allow, ask or block. Authority first (may this principal propose this at all,
and within what bounds?), then policy (how much autonomy does this action get?). Unknown
action, missing policy or missing principal is
deny. Silence is never permission. - Approve: bound to this action. A human answers against the action hash. The approval is
single-use, expires, and matches nothing but that exact action, so arguments changed after
the answer void it and a person answers again. Name a
preconditions=provider and the approval is also bound to the resource state it was granted against, rechecked strictly before the reservation: that narrows the window between the answer and the execution, from minutes of deliberation to milliseconds. It does not close it, because the recheck is a network call and cannot run inside the atomic write. - Reserve: claimed once. The effect key,
refund:txn_1ornamespace:prod-eu:checkout, is taken in one atomic write. A second caller, in another process or on another host, is refused. - Execute: your code runs. Only
NotExecuted, raised by you, meansFAILED; every other exception and every timeout meansAMBIGUOUS. Deciding which one you are looking at is the hard part, soctrlrun.transportdoes it for you:urlopen,HTTPConnectionandHTTPSConnectionfrom stdliburllibandhttp.client, which raiseNotExecutedonly where the connection they opened was handed no request byte. After one byte, every failure stays the exception it was, and the outcome isAMBIGUOUS. No setting widens that. - Resolve: unknown is not failed. An
AMBIGUOUSeffect keeps its key and refuses a retry untilctrlrun resolve, or areconcilehook that asked the remote, says what happened. Nothing runs twice on a guess. - Record: a receipt either way. A portable JSON receipt: who, what, decision, approval, effect key, outcome, and the hash of the policy that decided it, chained to the receipt before it. Refusals get one too.
Who may ask, and how much. The policy decides the action and cannot see who is asking. Who
may ask at all is a second axis, authority: every principal needs a grant, a delegation cannot
widen one, and an action needs both axes, the stricter of the pair. Since 0.9 a grant can also
carry a budget, a metric with a limit over a rolling window, consumed on reserve inside the
same write, so a thousand refunds that each pass amount_lte cannot add up to more than the
grant allows. An AMBIGUOUS effect holds its budget until it is resolved, because otherwise an
agent that can manufacture ambiguity could manufacture authority. A budget bounds what the next
reservation may do; it cannot recall an action already in flight.
Authority has the whole model.
State lives in SQLite by default, a file with no server and no ops, and the reservation holds
across processes rather than merely across threads. Point it at Postgres when more than one
host writes: pip install "ctrlrun[postgres]", one URL, the same guarantees graded by the same
suite. Prove it in your own setup with ctrlrun verify, which runs the kernel's own failure
scenarios against your policy in a scratch store. It reaches no network: the only sockets it
opens are to the store you named and to loopback listeners it bound itself, which is how it
grades the transport classifier.
The same shape in nine domains
Nothing in ctrlrun knows what a refund is. An action is a name, canonical arguments, an effect key and a resource, and the three questions asked of it are the same whichever domain it came from: how much autonomy does this action get, did a human approve this exact action, and has this effect already happened. Two things carry your domain, and you write both.
- The effect key is the only domain knowledge in the system. It is the string that says two
calls are the same real-world consequence:
refund:{payment_id},namespace:{cluster}:{name},grant:{user_id}:{role},prescription:{patient_id}:{drug}. Name it well and a retry cannot act twice; leave it out and there is nothing for at most once to be about. - Conditions are arguments, not amounts. The language is
<argument>_<op>, so the same operators readreplicas_lte: 10,role_in: [reader, viewer]andto_domain_eq: acme.comas easily asamount_lte. A band is available to a domain that has never issued an invoice.
Read any row left to right and it is one rule wearing different nouns. The security row is the one to read twice: adding a deny rule to a firewall is autonomous and adding an allow rule is not, which no amount threshold would have told you. The policy is where your judgement about your domain gets written down; ctrlrun is what makes it hold.
Documentation
docs.ctrlrun.dev is the documentation: concepts, guides, a cookbook, the full reference.
Contributing
Issues and pull requests are welcome:
CONTRIBUTING.md and
CODE_OF_CONDUCT.md have the
working agreement, and
SECURITY.md is how to report a
vulnerability. Every claim in this file has a test behind it, so a change to the prose usually
means a change to the suite.
CHANGELOG.md and
https://docs.ctrlrun.dev/ROADMAP say
where it is going. Releases carry PyPI provenance attestations from GitHub Actions.
License
Apache-2.0. The enforcement kernel is and will remain fully open source.
來源:README.md,提交 1c02d1e
工具
0版本歷史
1- v0.12.2最新Sep 16, 2026
評分
已滿足適用檢查項的 50%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
- 近 30 天內可連通25 分
請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。
- 已宣告認證方式10 分
請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。
- 已列出工具15 分
請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。
- 描述必需15 分
- 圖示10 分
- 文件15 分
- 授權條款10 分