TrustWarden
TrustWarden is a Python security library for AI agents and tool-calling applications. It tracks trust and sensitivity labels across wrapped Python tool calls, then evaluates policy before sensitive actions execute—so prompt injection, secret exfiltration, and untrusted mutations can be denied fail-closed before they run.
Features
Typed decorator and adapter API
Wrap any sync Python callable with pre-execution policy checks. No LangChain, OpenAI, or other agent framework is required.
Main actions: decorate with @warden.tool(...), adapt an existing function with warden.adapt(...), or wrap registries via wrap_callable, wrap_tool_map, and wrap_tool_specs / ToolSpec.
from trustwarden import Warden, mark_user_input, unwrap_result
warden = Warden()
@warden.tool(destination="local", state_changing=False)
def search(query: object) -> str:
return str(unwrap_result(query))
search(mark_user_input("roadmap"))
Trust-label propagation and marking
Values carry TrustLevel (unknown → system) and Sensitivity (unknown → secret) via Labelled. Mark data at boundaries with mark_user_input, mark_trusted, mark_system, mark_untrusted, mark_sensitive, and mark_secret. Tool outputs inherit the weakest trust and strongest sensitivity from their inputs through propagate_labels / the decorator (or use collect_labels / as_labelled / unwrap_result directly).
from trustwarden import mark_user_input, mark_secret, collect_labels, propagate_labels
trust, sensitivity = collect_labels(mark_user_input("q"), mark_secret("sk"))
out = propagate_labels({"hits": 1}, mark_user_input("q"))
Pre-execution policies (allow / block / escalate)
Versioned policy documents (POLICY_FORMAT_VERSION = 1.0) declare rules for data exposure, destinations, and state-changing actions. PolicyEngine evaluates a ToolContext and returns a DecisionAction. Load JSON with load_policy / load_policy_file, or use default_safe_policy(). Author rules with Policy, PolicyRule, and RuleKind.
from pathlib import Path
from trustwarden import Warden, load_policy_file, mark_secret
policy = load_policy_file(Path("policies/default.json"))
warden = Warden(policy)
warden.decide(
"http_post",
{"body": mark_secret("sk-demo")},
destination="external_http",
state_changing=True,
)
Optional LLM-assisted review
When a rule returns escalate, Warden can call a caller-supplied LLMReviewer (ReviewRequest → ReviewResult). TrustWarden never bundles an LLM client or API key. Without one, DeterministicReviewer resolves escalations offline (default: block). Use apply_review for manual review of a decision.
from trustwarden import Warden, DeterministicReviewer, DecisionAction, mark_user_input
warden = Warden(reviewer=DeterministicReviewer(default_action=DecisionAction.BLOCK))
warden.decide(
"send_email",
{"body": mark_user_input("hello")},
destination="email",
state_changing=True,
)
Safe-by-default / fail-closed behavior
Unknown labels and policy evaluation errors deny by default. Configure posture with FailClosedConfig (safe defaults or permissive) on Warden / PolicyEngine. Detect bare values with has_unknown_labels. Wrapped tools raise PolicyBlockedError when blocked; invalid policy documents raise PolicyError (both under TrustWardenError).
from trustwarden import Warden, FailClosedConfig, has_unknown_labels
assert has_unknown_labels("bare") is True
Warden(fail_closed=FailClosedConfig.safe_defaults())
Local attack simulator
AttackSimulator runs built-in malicious scenarios (builtin_scenarios: prompt-injection exfil, secret/webhook exfil, untrusted writes, unknown labels, and more) against a policy and reports pass/fail (AttackScenario, ScenarioResult). Use assert_all_passed() / summary() in CI, or the CLI entrypoint trustwarden-simulate.
from trustwarden import AttackSimulator
AttackSimulator().assert_all_passed()
uv run trustwarden-simulate
Structured, secret-redacted decision records and event hooks
Every evaluation produces a DecisionRecord (via build_decision) with redacted arguments (redact_text / redact_value / REDACTED). Subscribe to EventBus with callables or a DecisionSink: built-ins are CollectingSink, LoggingSink, and JsonLinesSink. Export flat dicts with decision_to_log_record.
from io import StringIO
from trustwarden import EventBus, JsonLinesSink, Warden, mark_user_input
bus = EventBus()
buf = StringIO()
bus.subscribe_sink(JsonLinesSink(buf))
warden = Warden(events=bus)
warden.decide("read", {"q": mark_user_input("x")}, destination="local")
Framework-neutral core, typed interfaces, versioned policies, examples
The core lives under src/trustwarden/ with py.typed. Compatibility adapters and typed interfaces (ToolSpec, ToolCallable, LLMReviewer, DecisionSink) sit beside the policy format (policies/default.json). Runnable examples: examples/basic_usage.py, examples/logging_hooks.py, examples/wrap_registry.py, examples/custom_policy.py. Brand assets (logo/favicon) are in frontend/public/ for packaging and docs; there is no web UI.
Pages
TrustWarden is a Python package, not a web application. There are no frontend routes and no interactive screens.
| Path | What it is |
|---|---|
| (none) | No app pages or SPA routes are served |
frontend/public/logo.svg |
Brand logo asset (primary #003366, accent #FFCC00) |
frontend/public/favicon.svg |
Brand favicon asset |
Use the library from your own agent or service process; there is nothing to open in a browser for TrustWarden itself.
API
There is no HTTP backend. The product surface is the importable Python API (package version 0.1.0) plus one console script. Grouped by feature:
Labels and marking
| Symbol | Purpose |
|---|---|
TrustLevel / Sensitivity |
Enumerations for provenance and sensitivity |
Labelled |
Value + label metadata (unwrap, with_labels) |
as_labelled / collect_labels / propagate_labels |
Coerce, aggregate, and attach labels |
mark_user_input / mark_trusted / mark_system / mark_untrusted |
Mark provenance |
mark_sensitive / mark_secret |
Mark sensitivity |
unwrap_result |
Extract raw value from a labelled result |
Guarding tools
| Symbol | Purpose |
|---|---|
Warden |
Policy guard: tool, adapt, decide, check |
ToolSpec |
Declarative tool description for registries |
wrap_callable / wrap_tool_map / wrap_tool_specs |
Framework-neutral adapters |
Policy
| Symbol | Purpose |
|---|---|
POLICY_FORMAT_VERSION |
Schema version (1.0) |
Policy / PolicyRule / RuleKind / ToolContext |
Versioned policy model |
PolicyEngine |
Deterministic evaluation |
default_safe_policy |
Built-in fail-closed policy |
load_policy / load_policy_file |
Parse JSON policies |
Decisions, redaction, events
| Symbol | Purpose |
|---|---|
DecisionAction / DecisionRecord / build_decision |
Structured decisions |
REDACTED / redact_text / redact_value |
Secret scrubbing |
EventBus / DecisionSink |
Export hooks |
CollectingSink / LoggingSink / JsonLinesSink |
Built-in sinks |
decision_to_log_record |
Flat mapping for loggers |
Review, safety, simulation, errors
| Symbol | Purpose |
|---|---|
LLMReviewer / ReviewRequest / ReviewResult / DeterministicReviewer / apply_review |
Escalation review |
FailClosedConfig / has_unknown_labels |
Fail-closed configuration |
AttackScenario / ScenarioResult / builtin_scenarios / AttackSimulator |
Attack simulation |
TrustWardenError / PolicyError / PolicyBlockedError |
Exception hierarchy |
CLI
| Entry | Purpose |
|---|---|
trustwarden-simulate |
Run built-in attack scenarios and print a JSON summary (trustwarden.simulator:main) |
Import everything from the package root, for example from trustwarden import Warden, mark_user_input.
Configuration
| Variable | Required | Purpose |
|---|---|---|
PYPI_PACKAGE_NAME |
Yes (for publish / simulator package field) | Distribution name of this package on PyPI; read at runtime via trustwarden.config (never hardcode). Listed in .env.example. |
Third-party API keys: none are owned or required by TrustWarden. Optional LLM review uses a caller-supplied LLMReviewer; pass any model credentials only inside that implementation (arguments or the caller’s own environment). The library works fully without an LLM via DeterministicReviewer.
Do not commit a .env file or put secret values in documentation.
Run it locally
Install dependencies and the editable package:
uv sync
Lint, typecheck, test, and build:
uv run ruff check .
uv run mypy src
uv run pytest
uv build
Try the library:
uv run python examples/basic_usage.py
uv run trustwarden-simulate
uv build writes a wheel and sdist under dist/ (for example trustwarden-0.1.0-py3-none-any.whl and trustwarden-0.1.0.tar.gz).
Architecture
Stack: frontend none · backend none · database none · vector database none. Language: Python 3.11+ · package manager: uv · build backend: hatchling · typed package with py.typed.
Repository layout:
| Path | Role |
|---|---|
src/trustwarden/ |
Library implementation (labels, wrap, policy, events, simulator, …) |
tests/ |
Unit tests and doctest runner |
examples/ |
Runnable usage samples |
policies/default.json |
Sample versioned policy document |
frontend/public/ |
Brand SVG assets only (no app) |
pyproject.toml |
Package metadata, ruff/mypy/pytest, trustwarden-simulate script |
.env.example |
Declares PYPI_PACKAGE_NAME (names only) |
How “frontend” and “backend” talk: they do not—there is no browser client and no HTTP API. Consumers import trustwarden in their own process (agent runtime, worker, or CLI). Policy evaluation happens in-process before the wrapped tool body runs; decisions can be exported to the host app’s logging via EventBus sinks.
Metadata
Release files for trustwarden 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 |
|---|---|---|---|---|
| trustwarden-0.1.0.tar.gz | 66.4 kB | Without provenance | Details for trustwarden-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| trustwarden-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for trustwarden-0.1.0-py3-none-any.whl |
Total release size: 100.2 kB
Release files / trustwarden-0.1.0.tar.gz
| Download URL | trustwarden-0.1.0.tar.gz |
|---|---|
| Size | 66.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
25c5a9e65912bd1c3b561060ec977df989135d2f18a461d63e0b85487add8a81
|
|
BLAKE2b-256 checksum How to use checksums |
560033394a571d08a1bfd52606bd68215976fcffabdb5a37d34e3a6e8b0a53cc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / trustwarden-0.1.0-py3-none-any.whl
| Download URL | trustwarden-0.1.0-py3-none-any.whl |
|---|---|
| Size | 33.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c521545829b2fc6b18f204b1211708ff699eba71f0b06d3275c67c25d25e8868
|
|
BLAKE2b-256 checksum How to use checksums |
770b1ec95d7f0d7c03a6fbb13656cbd7afa3c33252d6b66e5488d09a09623c10
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for trustwarden 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.