OpenScript
Irreversible actions wait for a person, even when the model has been fooled.
OpenScript sits between an AI agent and the tools it calls. It refuses calls outside rules you write, holds the ones that cannot be undone until a person approves that exact call, masks secrets and PII, and records every decision. None of it depends on recognising an attack, so it still holds when the model is fooled, wrong, or swapped for another.
It is for anyone building an agent whose tools have real effects, such as payments, email or a shell, in Python with any framework. It is version 0.1.0 and alpha: the API may still change.
Try the live demo → The package runs in your browser tab, compiled to WebAssembly, with no backend.
What it is not
- Not an injection detector to rely on. It has pattern detectors, and they catch 2 to 7% of attacks they were not written for. The boundary is the rules and the approvals, and the rules are only as tight as you write them.
- Not a compliance certification. Its compliance presets assist a program and certify nothing.
What it has been shown to do
Measured on AgentDojo's banking and Slack suites, with Qwen2.5-7B-Instruct, a model that follows prompt injections. The protocol was frozen before the run.
| The run | |
|---|---|
| Hardware | 1 NVIDIA RTX 4090 on RunPod, Secure Cloud, EU-RO-1 |
| Serving | vLLM 0.10.2, bf16, temperature 0 |
| Size | 1,430 cases, 5,382 requests, 9.8M input and 0.63M output tokens |
| Errors | 0 |
| Date | 2026-11-04 |
| Configuration | Attacks that succeeded | Attacker's irreversible actions that took effect |
|---|---|---|
| No protection | 58 of 249 | 108 |
| Session rule: hold irreversible calls after an untrusted read | 6 of 249 | 10 |
| Ask a person on every irreversible call | 0 of 249 | 0 |
| No policy at all, effect side enforcing | 0 of 249 | 0 |
- The effect side is the boundary. A ledger in front of each irreversible tool applies a call only with a token minted when a person approves it. A call that goes round the wrapper changes nothing.
- Safety has a price, and it is published. In the run above, asking on every irreversible call meant a person on 23 of the 37 ordinary tasks. In the no-model replay of all four suites it is 59 of 97. The session rule asks less and misses the attacks that arrive through Slack's channel listing.
- It was attacked before it was trusted. A red team searched for bypasses and published 7 findings, each committed as a failing test before its fix.
Every number, with its date, command and caveats, is in
docs/metrics.md. The claim and what it does not cover
are in the threat model.
How it works
agent ── calls a tool ──► SecureTool ── before: tool rules · session rule ──► refuse / hold for a person
│
▼ the call that was checked, and nothing else
effect side ── a person's token? ──► apply, and write it in the ledger
│
▼
after: mask secrets and PII · record
- Tool rules limit which tools, arguments, recipients and amounts are allowed, in YAML.
- The session rule remembers when a session has read something a stranger wrote, and then holds the calls you list as irreversible.
- An approval is single-use, expires in an hour, and is bound to the tool, its exact arguments and the session.
- The effect side (
evals/ledger.py) is a reference implementation, standard library only.
Quick start
pip install openscript-sdk # Python 3.11+; pulls pydantic, structlog and PyYAML
The distribution is openscript-sdk and the package imports as
openscript_sdk. The extras are [server] (the FastAPI server, whose
database migrations are in this repository's alembic/), [redis],
[metrics] and [otel]. To work on it, clone the repository and run
make install.
A tool that the rules hold waits for a person, and the retry with the approval runs that exact call once:
from openscript_sdk import ActionBlockedError, SecureTool, ToolFirewallPolicy
async def transfer_funds(to: str, amount: int) -> str: ...
transfer = SecureTool(transfer_funds, policies=[ToolFirewallPolicy(rules_path="tools.yaml")])
call = {"to": "acct-22", "amount": 500}
try:
await transfer.call(call) # held: the tool does not run
except ActionBlockedError as held:
approval_id = held.approval_id # a person approves it ...
await transfer.call(call, approval_id=approval_id) # ... and the same call runs, once
# tools.yaml
default_deny: true
rules:
transfer_funds:
requires_approval: true
args:
amount: {min: 0, max: 1000}
Also in the package: redaction of secrets and PII, input screening, an audit trail and an event server. The guide covers each.
Where it runs
| Surface | Start here |
|---|---|
Python agents and tools (LangChain, LangGraph, anything with invoke) |
the guide |
| Claude Code, as a hook, with a strict profile that holds Bash after a web read | the hook |
| Java agents, through the server | clients/java |
| A browser, and a Chrome extension | the demo |
Further reading
docs/guide.md: every feature, its limits and its configuration, including environment variables and options.docs/metrics.md: every measurement.docs/threat-model.mdanddocs/red-team.md: the claim and the attempts to break it.docs/submission.md: the AI-safety challenge write-up.
Contributing
make install && pytest && ruff check . && black --check . && mypy --strict openscript_sdk/ --exclude openscript_sdk/server/
CONTRIBUTING.md has the full checklist, including the
Pyodide and Java checks. Changes to approvals, the session rule or the hook
also run pytest tests/bypass.
License
Apache License 2.0. See LICENSE.
Metadata
Release files for openscript-sdk 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| openscript_sdk-0.1.0.tar.gz | 216.4 kB | Without provenance | Details for openscript_sdk-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| openscript_sdk-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for openscript_sdk-0.1.0-py3-none-any.whl |
Total release size: 364.4 kB
Release files / openscript_sdk-0.1.0.tar.gz
| Download URL | openscript_sdk-0.1.0.tar.gz |
|---|---|
| Size | 216.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4d41b13c0674728d80989f12a2268f92b9e03aad15389a29c26bc34b0405ebb1
|
|
BLAKE2b-256 checksum How to use checksums |
b39a690bd377c14abdc6eafc71eee50e6c9ba9e365b79cf06f41c67c649aa621
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / openscript_sdk-0.1.0-py3-none-any.whl
| Download URL | openscript_sdk-0.1.0-py3-none-any.whl |
|---|---|
| Size | 148.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
95bc64ad15b8e1499c88dc2f3a76f4404956a7507b42968aa6985d924e29862f
|
|
BLAKE2b-256 checksum How to use checksums |
ad5c68a018ea1071e07aa9d4e2f493a2924349db51d522d8eeb0e26deae145d5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Security for openscript-sdk 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.