Python SDK for governing AI agent execution with policy, approvals, isolation, budgets, and signed evidence.
Project description
runeward (Python)
A dependency-light Python client and agent-framework adapters for the Runeward agent governance harness. Put policy, human approvals, isolated Citadels, Rationing, and signed Chronicles around an existing agent without replacing its model or framework.
The core client uses only the Python standard library (urllib). The
LangChain, CrewAI, LlamaIndex, OpenAI Agents SDK, and Strands helpers are
optional extras and are imported lazily, so the base client works with nothing
else installed.
Install
pip install runeward # core client only (no third-party deps)
pip install "runeward[langchain]" # + LangChain tools
pip install "runeward[crewai]" # + CrewAI tools
pip install "runeward[llamaindex]" # + LlamaIndex tools
pip install "runeward[openai-agents]" # + OpenAI Agents SDK tools
pip install "runeward[strands]" # + Strands Agents SDK tools
During local development from this directory:
pip install -e .
Quick start
Start the control plane first (runeward serve, default
http://localhost:8080), then:
from runeward import RunewardClient, RunewardDenied, RunewardApprovalRequired
rw = RunewardClient("http://localhost:8080")
sbx = rw.create_sandbox("dev") # -> {"id": "sbx_...", "backend": "docker", ...}
sid = sbx["id"]
result = rw.shell(sid, ["python3", "--version"])
print(result["stdout"]) # "Python 3.11.2\n"
rw.write_file(sid, "/workspace/main.py", "print(2 + 2)")
print(rw.python(sid, "exec(open('/workspace/main.py').read())")["stdout"]) # "4\n"
rw.kill_sandbox(sid) # always tear down when done
Use allow_insecure=True (or RUNEWARD_ALLOW_INSECURE_HTTP=1) only when you must call a non-loopback http:// control-plane endpoint.
Handling governance verdicts
The two governance outcomes are raised as typed exceptions. Handle them explicitly — a denial must not be blindly retried, and an approval gate must pause for a human:
try:
rw.shell(sid, ["rm", "-rf", "/"])
except RunewardDenied as e:
print("blocked by policy:", e.reason) # do NOT retry the same action
try:
rw.write_file(sid, "/etc/hosts", "127.0.0.1 example")
except RunewardApprovalRequired as e:
print("needs a human:", e.approval_id) # pause; ask an operator to approve/deny
Approvals inbox
for a in rw.list_approvals():
print(a["id"], a["tool"], a["action"], a["reason"])
rw.approve("apr_31c") # or rw.deny("apr_31c")
Chronicle (audit ledger)
events = rw.audit(sid) # this Citadel's Chronicle events
assert rw.verify_audit() # verify the tamper-evident hash chain
Client method surface
| Method | REST endpoint |
|---|---|
healthz() |
GET /healthz |
list_profiles() |
GET /v1/charters |
create_sandbox(profile) |
POST /v1/citadels |
list_sandboxes() / get_sandbox(id) / kill_sandbox(id) |
GET/GET/DELETE /v1/citadels[/{id}] |
shell(sandbox, command, workdir="") |
POST .../shell/exec |
python(sandbox, code) / node(sandbox, code) |
POST .../code/{python,node} |
read_file / write_file / list_files / search_files |
POST .../file/{read,write,list,search} |
audit(sandbox) / verify_audit() |
GET .../chronicle, GET /v1/chronicle/verify |
list_approvals() / approve(id) / deny(id) |
GET /v1/conclave, POST /v1/conclave/{id}/{approve,deny} |
LangChain
from runeward import RunewardClient
from runeward.langchain_tools import make_runeward_tools
tools = make_runeward_tools(RunewardClient("http://localhost:8080"))
# Pass `tools` to any LangChain agent / AgentExecutor.
The Python framework tools are named runeward_create_sandbox,
runeward_shell, …. Governance verdicts are returned as descriptive strings so
the agent can reason about a denial or an approval gate. (Note: the standalone
runeward mcp server now exposes the renamed runeward_create_citadel /
runeward_kill_citadel / runeward_list_conclave tools; the Python framework
tool names have not yet been updated to match.)
CrewAI
from runeward import RunewardClient
from runeward.crewai_tools import make_runeward_tools
tools = make_runeward_tools(RunewardClient("http://localhost:8080"))
# Attach `tools` to a crewai.Agent(tools=tools, ...).
LlamaIndex
from runeward import RunewardClient
from runeward.llamaindex_tools import make_runeward_tools
tools = make_runeward_tools(RunewardClient("http://localhost:8080"))
# Pass `tools` to a FunctionAgent / ReActAgent / AgentRunner.
Returns llama_index.core.tools.FunctionTool instances; the tool schema is
derived from each function's type hints and docstring.
OpenAI Agents SDK
from agents import Agent, Runner
from runeward import RunewardClient
from runeward.openai_agents_tools import make_runeward_tools
tools = make_runeward_tools(RunewardClient("http://localhost:8080"))
agent = Agent(name="builder", instructions="Use the sandbox tools.", tools=tools)
result = Runner.run_sync(agent, "Create a dev sandbox, run `node --version`, then tear it down.")
Returns @function_tool-built tools; the SDK derives each schema from the
function's type hints and docstring.
Strands Agents SDK
from strands import Agent
from runeward import RunewardClient
from runeward.strands_tools import make_runeward_tools
tools = make_runeward_tools(RunewardClient("http://localhost:8080"))
agent = Agent(tools=tools)
agent("Create a dev sandbox, run `node --version`, then tear it down.")
Returns @tool-decorated functions; Strands derives each schema from the
function's type hints and docstring.
Notes
denyis a policy decision, not a transient error. Don't retry the same action; pick a different, allowed approach.require-approvalis a hard pause. Surface theapproval_idto a human and wait for the outcome.- Prefer the tightest profile that lets the task succeed.
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 runeward-0.2.0.tar.gz.
File metadata
- Download URL: runeward-0.2.0.tar.gz
- Upload date:
- Size: 16.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8b8cfb00fbc5ff7108b58004a94684e5c7361e8be967dd30dde324515ee8f896
|
|
| MD5 |
fca6d77c1518b8aade0f4a202df4c288
|
|
| BLAKE2b-256 |
c41f1d7cfccdcd9b2a7de58bd6408a11cc93b97b666c61a6fb61e60cf307e89b
|
Provenance
The following attestation bundles were made for runeward-0.2.0.tar.gz:
Publisher:
publish-sdks.yml on Runewardd/runeward
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
runeward-0.2.0.tar.gz -
Subject digest:
8b8cfb00fbc5ff7108b58004a94684e5c7361e8be967dd30dde324515ee8f896 - Sigstore transparency entry: 2309263653
- Sigstore integration time:
-
Permalink:
Runewardd/runeward@6bf0b783491473bd789b1892ed9df6eea1addfaa -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Runewardd
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-sdks.yml@6bf0b783491473bd789b1892ed9df6eea1addfaa -
Trigger Event:
push
-
Statement type:
File details
Details for the file runeward-0.2.0-py3-none-any.whl.
File metadata
- Download URL: runeward-0.2.0-py3-none-any.whl
- Upload date:
- Size: 22.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5c6e4e3104c2b182bb1f66debe20ddef684c870dcb3d8ae3ab0aeb6a33659be8
|
|
| MD5 |
521ad64784c6eefce290c06152e852c6
|
|
| BLAKE2b-256 |
70f03aa696dc0feaa05212bc1e864ed2078ac2de250c580b768289f81bece7c9
|
Provenance
The following attestation bundles were made for runeward-0.2.0-py3-none-any.whl:
Publisher:
publish-sdks.yml on Runewardd/runeward
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
runeward-0.2.0-py3-none-any.whl -
Subject digest:
5c6e4e3104c2b182bb1f66debe20ddef684c870dcb3d8ae3ab0aeb6a33659be8 - Sigstore transparency entry: 2309263772
- Sigstore integration time:
-
Permalink:
Runewardd/runeward@6bf0b783491473bd789b1892ed9df6eea1addfaa -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Runewardd
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-sdks.yml@6bf0b783491473bd789b1892ed9df6eea1addfaa -
Trigger Event:
push
-
Statement type: