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") # uses RUNEWARD_API_TOKEN when set
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, "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 |
whoami() / readiness(profile) / simulate_policy(...) |
Identity, setup, and dry-run policy APIs |
list_runs() / get_run(id) |
Durable provider-neutral Run lineage |
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 |
export_evidence(sandbox) |
Portable resolved Charter + signed Chronicle evidence |
create_cohort / list_cohorts / add_task / claim_task |
Cohort lifecycle and leased work queue |
heartbeat_task / complete_task / fail_task |
Signed-lease task transitions |
create_snapshot / list_snapshots / restore_snapshot |
Tenant-scoped recovery |
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 use the same stable concept names as MCP, including
runeward_create_citadel, runeward_kill_citadel, and
runeward_list_conclave. Governance verdicts are returned as descriptive
strings so the agent can reason about a denial or an approval gate.
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.
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.3.0.tar.gz.
File metadata
- Download URL: runeward-0.3.0.tar.gz
- Upload date:
- Size: 18.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f616cf0172a273f9fe6480fa72d3777d6c4507982807caa216ccf77ffd3addde
|
|
| MD5 |
d667a3022fba58916ffa6b7a443c63f6
|
|
| BLAKE2b-256 |
8f982ffb76c33b294d53bfb45b4a0ecefe7f26e5a917e20e3215f2c5b0194b74
|
Provenance
The following attestation bundles were made for runeward-0.3.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.3.0.tar.gz -
Subject digest:
f616cf0172a273f9fe6480fa72d3777d6c4507982807caa216ccf77ffd3addde - Sigstore transparency entry: 2377432806
- Sigstore integration time:
-
Permalink:
Runewardd/runeward@2ba5eb89ee9822994b4e9cd9b1b1548d06e61cb0 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/Runewardd
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-sdks.yml@2ba5eb89ee9822994b4e9cd9b1b1548d06e61cb0 -
Trigger Event:
push
-
Statement type:
File details
Details for the file runeward-0.3.0-py3-none-any.whl.
File metadata
- Download URL: runeward-0.3.0-py3-none-any.whl
- Upload date:
- Size: 23.4 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 |
83cd590f5d71ef7291364919261b929049c991e3126b3f70f4bc5f32e31f611f
|
|
| MD5 |
f6b8dce0671ff005d24dcbb711123653
|
|
| BLAKE2b-256 |
23d74b29e9d4c81134940bb00b34de29769628bc025b262b1236bca71be352b2
|
Provenance
The following attestation bundles were made for runeward-0.3.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.3.0-py3-none-any.whl -
Subject digest:
83cd590f5d71ef7291364919261b929049c991e3126b3f70f4bc5f32e31f611f - Sigstore transparency entry: 2377432915
- Sigstore integration time:
-
Permalink:
Runewardd/runeward@2ba5eb89ee9822994b4e9cd9b1b1548d06e61cb0 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/Runewardd
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-sdks.yml@2ba5eb89ee9822994b4e9cd9b1b1548d06e61cb0 -
Trigger Event:
push
-
Statement type: