Metadata-Version: 2.5
Name: trustwarden
Version: 0.1.0
Summary: Guarding your tools with certainty. Trust labels and policy enforcement for AI agent tools.
License-Expression: MIT
Keywords: ai-agents,llm,policy,security,tool-calling,trust
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown

# 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`.

```python
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).

```python
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`.

```python
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.

```python
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`).

```python
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`.

```python
from trustwarden import AttackSimulator

AttackSimulator().assert_all_passed()
```

```bash
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`.

```python
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:

```bash
uv sync
```

Lint, typecheck, test, and build:

```bash
uv run ruff check .
uv run mypy src
uv run pytest
uv build
```

Try the library:

```bash
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.
