trustwarden 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. trustwarden-0.1.0/.cursor/mcp.json +19 -0
  2. trustwarden-0.1.0/.cursor/rules/efficiency.mdc +20 -0
  3. trustwarden-0.1.0/.cursor/rules/engineering.mdc +30 -0
  4. trustwarden-0.1.0/.env.example +2 -0
  5. trustwarden-0.1.0/.gitignore +38 -0
  6. trustwarden-0.1.0/.python-version +1 -0
  7. trustwarden-0.1.0/AGENTS.md +50 -0
  8. trustwarden-0.1.0/PKG-INFO +256 -0
  9. trustwarden-0.1.0/README.md +238 -0
  10. trustwarden-0.1.0/examples/basic_usage.py +37 -0
  11. trustwarden-0.1.0/examples/custom_policy.py +39 -0
  12. trustwarden-0.1.0/examples/logging_hooks.py +52 -0
  13. trustwarden-0.1.0/examples/wrap_registry.py +46 -0
  14. trustwarden-0.1.0/frontend/public/favicon.svg +6 -0
  15. trustwarden-0.1.0/frontend/public/logo.svg +6 -0
  16. trustwarden-0.1.0/kaisen-manifest.json +106 -0
  17. trustwarden-0.1.0/policies/default.json +68 -0
  18. trustwarden-0.1.0/pyproject.toml +64 -0
  19. trustwarden-0.1.0/src/trustwarden/__init__.py +138 -0
  20. trustwarden-0.1.0/src/trustwarden/adapters.py +186 -0
  21. trustwarden-0.1.0/src/trustwarden/config.py +32 -0
  22. trustwarden-0.1.0/src/trustwarden/decisions.py +164 -0
  23. trustwarden-0.1.0/src/trustwarden/events.py +283 -0
  24. trustwarden-0.1.0/src/trustwarden/exceptions.py +83 -0
  25. trustwarden-0.1.0/src/trustwarden/labels.py +275 -0
  26. trustwarden-0.1.0/src/trustwarden/llm_review.py +187 -0
  27. trustwarden-0.1.0/src/trustwarden/marking.py +159 -0
  28. trustwarden-0.1.0/src/trustwarden/policy.py +513 -0
  29. trustwarden-0.1.0/src/trustwarden/py.typed +0 -0
  30. trustwarden-0.1.0/src/trustwarden/redaction.py +96 -0
  31. trustwarden-0.1.0/src/trustwarden/safety.py +189 -0
  32. trustwarden-0.1.0/src/trustwarden/simulator.py +346 -0
  33. trustwarden-0.1.0/src/trustwarden/wrap.py +337 -0
  34. trustwarden-0.1.0/tests/test_adapters_and_policy_format.py +95 -0
  35. trustwarden-0.1.0/tests/test_config_and_redaction.py +40 -0
  36. trustwarden-0.1.0/tests/test_doctests.py +39 -0
  37. trustwarden-0.1.0/tests/test_events.py +121 -0
  38. trustwarden-0.1.0/tests/test_labels.py +82 -0
  39. trustwarden-0.1.0/tests/test_llm_review.py +163 -0
  40. trustwarden-0.1.0/tests/test_policy.py +216 -0
  41. trustwarden-0.1.0/tests/test_safety.py +213 -0
  42. trustwarden-0.1.0/tests/test_simulator.py +86 -0
  43. trustwarden-0.1.0/tests/test_wrap.py +144 -0
  44. trustwarden-0.1.0/uv.lock +381 -0
@@ -0,0 +1,19 @@
1
+ {
2
+ "mcpServers": {
3
+ "context7": {
4
+ "command": "npx",
5
+ "args": [
6
+ "-y",
7
+ "@upstash/context7-mcp"
8
+ ]
9
+ },
10
+ "shadcn": {
11
+ "command": "npx",
12
+ "args": [
13
+ "-y",
14
+ "shadcn@latest",
15
+ "mcp"
16
+ ]
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,20 @@
1
+ ---
2
+ description: Work efficiently with few tokens
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Efficiency
7
+
8
+ - Read only the files you need; search before opening; never print whole large files.
9
+ - Make focused edits; do not rewrite files that only need a small change.
10
+ - Prefer established libraries and generators over writing boilerplate by hand.
11
+ - Look up current library APIs with the Context7 MCP server instead of guessing.
12
+ - Verify by running the commands, not by reasoning about them.
13
+ - Never run anything that does not exit on its own in the foreground (`next start`,
14
+ `npm run dev`, `uvicorn`, `vite`, watch modes): it blocks you until your time runs out.
15
+ To try a server, start it, check it and stop it in ONE command, with a time limit:
16
+ `timeout 120 npx next start -p 3999 > /tmp/server.log 2>&1 & pid=$!; for i in $(seq 40);
17
+ do curl -sf -m 3 http://127.0.0.1:3999/api/health && break; sleep 1; done; <your curl
18
+ checks>; kill $pid`. Use port 3999, never 8765 (the Factory's own check uses it). Run
19
+ tests once, never in watch mode (`vitest run`, `CI=1`).
20
+ - Keep your final summary short: what changed and what was verified.
@@ -0,0 +1,30 @@
1
+ ---
2
+ description: Kaisen Factory engineering standards
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Engineering standards
7
+
8
+ - Production quality, not a prototype: every feature works end to end (UI, API, database),
9
+ with validation, error handling, loading and empty states.
10
+ - Layout: `frontend/` (the web app) and the backend API served under `/api` (see AGENTS.md).
11
+ Keep modules small and named by what they do. Shared types between layers where the stack
12
+ allows it.
13
+ - Data: model the domain properly (relations, indexes, timestamps, ownership per user/team).
14
+ Seed realistic demo data so the product is meaningful on first login.
15
+ - Serverless startup must be fast (the backend runs as a function that cold-starts): never
16
+ run a chain of database calls before serving. Create indexes concurrently (e.g.
17
+ `asyncio.gather` / `Promise.all`) and at most once per process, and keep `/api/health`
18
+ independent of index creation. No work at import time beyond building the app.
19
+ - Authentication is mandatory: every application has accounts with JWT authentication
20
+ (sign-up, sign-in, sign-out, the current user; tokens signed with `JWT_SECRET`, with an
21
+ expiry), and every non-public endpoint and page requires a valid token.
22
+ - Security: hash passwords (bcrypt/argon2), sign tokens with `JWT_SECRET`, check authorisation
23
+ on every endpoint (users only see their own or their team's data), validate all input,
24
+ never log or return secrets, never commit `.env` (only `.env.example` with names).
25
+ - Configuration comes only from environment variables (names in AGENTS.md); never hardcode
26
+ their values.
27
+ - Tests: unit tests for logic and API tests for every endpoint, including auth failures.
28
+ Never delete or weaken tests to make them pass.
29
+ - Before finishing a task, run the install, lint, typecheck, test and build commands from
30
+ AGENTS.md and fix what fails.
@@ -0,0 +1,2 @@
1
+ # PyPI: published package name for this project (set by the Factory; do not commit real .env)
2
+ PYPI_PACKAGE_NAME
@@ -0,0 +1,38 @@
1
+ # Environment and secrets
2
+ .env
3
+ .env.*
4
+ !.env.example
5
+
6
+ # Python
7
+ __pycache__/
8
+ *.py[cod]
9
+ *$py.class
10
+ *.so
11
+ .Python
12
+ build/
13
+ dist/
14
+ wheels/
15
+ *.egg-info/
16
+ .eggs/
17
+ *.egg
18
+
19
+ # Virtual environments
20
+ .venv/
21
+ venv/
22
+ ENV/
23
+
24
+ # Tooling
25
+ .mypy_cache/
26
+ .ruff_cache/
27
+ .pytest_cache/
28
+ .coverage
29
+ htmlcov/
30
+ .tox/
31
+ .nox/
32
+ .cache/
33
+
34
+ # Editors / OS
35
+ .idea/
36
+ .vscode/
37
+ *.swp
38
+ .DS_Store
@@ -0,0 +1 @@
1
+ 3.11
@@ -0,0 +1,50 @@
1
+ # TrustWarden — Guarding your tools with certainty.
2
+
3
+ Built autonomously by Kaisen Factory. This file and `.cursor/rules` are generated by the Factory; keep them. `README.md` is the application's documentation, written in the plan's documentation task and kept current by every change; the Factory maintains the part between `<!-- kaisen-factory:live -->` markers (live URLs, owner account).
4
+
5
+ ## Product
6
+ A production-ready Python security library that propagates trust labels from tool outputs into subsequent calls, evaluates declared policies before sensitive tools run, and provides a runnable attack simulator to test enforcement.
7
+
8
+ - Domain: AI agent security and developer tools
9
+ - Core: Track trust and sensitivity labels across wrapped Python tool calls, then evaluate policy before sensitive actions execute.
10
+ - Features (all of them must be fully implemented):
11
+ - Typed decorator and adapter API for wrapping Python tools without requiring a specific agent framework.
12
+ - Trust-label propagation for tool outputs, with explicit APIs for marking user input, trusted instructions, and sensitive values.
13
+ - Pre-execution policies for data exposure, destinations, and state-changing actions, with configurable allow, block, or escalate decisions.
14
+ - Optional LLM-assisted review for policy-defined ambiguous cases; deterministic rules remain available without an LLM.
15
+ - Safe-by-default handling of unknown labels and policy errors, with explicit configuration for fail-closed behavior.
16
+ - Local attack simulator with realistic malicious tool outputs, expected outcomes, and policy regression tests.
17
+ - Structured, secret-redacted decision records and hooks for applications to export events to their own logging systems.
18
+ - Framework-neutral core, compatibility adapters, typed interfaces, versioned policy formats, and comprehensive documentation and examples.
19
+
20
+ ## Brand
21
+ - Name: TrustWarden
22
+ - Tagline: Guarding your tools with certainty.
23
+ - Colours: primary `#003366`, accent `#FFCC00`
24
+ - Logo: `frontend/public/logo.svg` (also `frontend/public/favicon.svg`)
25
+
26
+ ## Stack
27
+ - Frontend: -; backend: -; database: -; vector database: -
28
+ - Install: `uv sync`
29
+ - Lint: `uv run ruff check .`
30
+ - Typecheck: `uv run mypy src`
31
+ - Test: `uv run pytest`
32
+ - Build: `uv build`
33
+
34
+ ## Third-party services: each user brings their own keys
35
+ Add an external service only when a feature truly needs it. The application is multi-tenant: every user (or workspace) connects **their own** account or API key for it (Slack, Stripe, Notion, an email provider, …), never a shared key of the owner:
36
+ - A Connections/Integrations page in the user's settings: paste a key, token or webhook URL; test it with one harmless call before saving; show it masked (last 4 characters); allow replace and disconnect.
37
+ - Store each credential encrypted at rest (AES-GCM, e.g. Fernet) in the user's or workspace's own record, with a key derived from `JWT_SECRET` via HKDF (info `tenant-credentials`); never return it to the browser, log it or send it to another tenant; every call uses the current tenant's credential.
38
+ - A feature whose service the user has not connected shows a clear "connect <service> in Settings" state; nothing crashes.
39
+ - Prefer services that work with a pasted key or webhook URL. Only when a service requires an OAuth app (a client ID and secret registered once for the whole application), treat that app credential as an owner key (below); each user's OAuth tokens are still stored per tenant as above.
40
+ - A Python package takes credentials from its caller (arguments or the caller's own environment variables), never bundles one.
41
+
42
+ ## Keys the owner provides (rare: application-wide credentials only)
43
+ For an application-wide credential that cannot be per user (an OAuth app's client ID and secret):
44
+ - Read its key from an environment variable and declare it in `.env.example` of the part that uses it (a key the browser needs: the frontend's, prefixed `VITE_`), with a comment line right above: `# <Feature>: <what the key is and where to get it>`.
45
+ - The application must work, deploy and pass every check **without** the key: the feature shows a clear "not configured yet" state and nothing crashes.
46
+ - The Factory asks the owner for declared keys and redeploys when one is provided; never hardcode, commit or log a key.
47
+
48
+ ## Environment (already set; read at runtime, never hardcode)
49
+ `PYPI_PACKAGE_NAME`
50
+
@@ -0,0 +1,256 @@
1
+ Metadata-Version: 2.5
2
+ Name: trustwarden
3
+ Version: 0.1.0
4
+ Summary: Guarding your tools with certainty. Trust labels and policy enforcement for AI agent tools.
5
+ License-Expression: MIT
6
+ Keywords: ai-agents,llm,policy,security,tool-calling,trust
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Topic :: Security
15
+ Classifier: Typing :: Typed
16
+ Requires-Python: >=3.11
17
+ Description-Content-Type: text/markdown
18
+
19
+ # TrustWarden
20
+
21
+ 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.
22
+
23
+ ## Features
24
+
25
+ ### Typed decorator and adapter API
26
+
27
+ Wrap any sync Python callable with pre-execution policy checks. No LangChain, OpenAI, or other agent framework is required.
28
+
29
+ **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`.
30
+
31
+ ```python
32
+ from trustwarden import Warden, mark_user_input, unwrap_result
33
+
34
+ warden = Warden()
35
+
36
+ @warden.tool(destination="local", state_changing=False)
37
+ def search(query: object) -> str:
38
+ return str(unwrap_result(query))
39
+
40
+ search(mark_user_input("roadmap"))
41
+ ```
42
+
43
+ ### Trust-label propagation and marking
44
+
45
+ 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).
46
+
47
+ ```python
48
+ from trustwarden import mark_user_input, mark_secret, collect_labels, propagate_labels
49
+
50
+ trust, sensitivity = collect_labels(mark_user_input("q"), mark_secret("sk"))
51
+ out = propagate_labels({"hits": 1}, mark_user_input("q"))
52
+ ```
53
+
54
+ ### Pre-execution policies (allow / block / escalate)
55
+
56
+ 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`.
57
+
58
+ ```python
59
+ from pathlib import Path
60
+ from trustwarden import Warden, load_policy_file, mark_secret
61
+
62
+ policy = load_policy_file(Path("policies/default.json"))
63
+ warden = Warden(policy)
64
+ warden.decide(
65
+ "http_post",
66
+ {"body": mark_secret("sk-demo")},
67
+ destination="external_http",
68
+ state_changing=True,
69
+ )
70
+ ```
71
+
72
+ ### Optional LLM-assisted review
73
+
74
+ 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.
75
+
76
+ ```python
77
+ from trustwarden import Warden, DeterministicReviewer, DecisionAction, mark_user_input
78
+
79
+ warden = Warden(reviewer=DeterministicReviewer(default_action=DecisionAction.BLOCK))
80
+ warden.decide(
81
+ "send_email",
82
+ {"body": mark_user_input("hello")},
83
+ destination="email",
84
+ state_changing=True,
85
+ )
86
+ ```
87
+
88
+ ### Safe-by-default / fail-closed behavior
89
+
90
+ 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`).
91
+
92
+ ```python
93
+ from trustwarden import Warden, FailClosedConfig, has_unknown_labels
94
+
95
+ assert has_unknown_labels("bare") is True
96
+ Warden(fail_closed=FailClosedConfig.safe_defaults())
97
+ ```
98
+
99
+ ### Local attack simulator
100
+
101
+ `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`.
102
+
103
+ ```python
104
+ from trustwarden import AttackSimulator
105
+
106
+ AttackSimulator().assert_all_passed()
107
+ ```
108
+
109
+ ```bash
110
+ uv run trustwarden-simulate
111
+ ```
112
+
113
+ ### Structured, secret-redacted decision records and event hooks
114
+
115
+ 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`.
116
+
117
+ ```python
118
+ from io import StringIO
119
+ from trustwarden import EventBus, JsonLinesSink, Warden, mark_user_input
120
+
121
+ bus = EventBus()
122
+ buf = StringIO()
123
+ bus.subscribe_sink(JsonLinesSink(buf))
124
+ warden = Warden(events=bus)
125
+ warden.decide("read", {"q": mark_user_input("x")}, destination="local")
126
+ ```
127
+
128
+ ### Framework-neutral core, typed interfaces, versioned policies, examples
129
+
130
+ 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.
131
+
132
+ ## Pages
133
+
134
+ TrustWarden is a **Python package**, not a web application. There are **no frontend routes** and no interactive screens.
135
+
136
+ | Path | What it is |
137
+ | --- | --- |
138
+ | _(none)_ | No app pages or SPA routes are served |
139
+ | `frontend/public/logo.svg` | Brand logo asset (primary `#003366`, accent `#FFCC00`) |
140
+ | `frontend/public/favicon.svg` | Brand favicon asset |
141
+
142
+ Use the library from your own agent or service process; there is nothing to open in a browser for TrustWarden itself.
143
+
144
+ ## API
145
+
146
+ 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:
147
+
148
+ ### Labels and marking
149
+
150
+ | Symbol | Purpose |
151
+ | --- | --- |
152
+ | `TrustLevel` / `Sensitivity` | Enumerations for provenance and sensitivity |
153
+ | `Labelled` | Value + label metadata (`unwrap`, `with_labels`) |
154
+ | `as_labelled` / `collect_labels` / `propagate_labels` | Coerce, aggregate, and attach labels |
155
+ | `mark_user_input` / `mark_trusted` / `mark_system` / `mark_untrusted` | Mark provenance |
156
+ | `mark_sensitive` / `mark_secret` | Mark sensitivity |
157
+ | `unwrap_result` | Extract raw value from a labelled result |
158
+
159
+ ### Guarding tools
160
+
161
+ | Symbol | Purpose |
162
+ | --- | --- |
163
+ | `Warden` | Policy guard: `tool`, `adapt`, `decide`, `check` |
164
+ | `ToolSpec` | Declarative tool description for registries |
165
+ | `wrap_callable` / `wrap_tool_map` / `wrap_tool_specs` | Framework-neutral adapters |
166
+
167
+ ### Policy
168
+
169
+ | Symbol | Purpose |
170
+ | --- | --- |
171
+ | `POLICY_FORMAT_VERSION` | Schema version (`1.0`) |
172
+ | `Policy` / `PolicyRule` / `RuleKind` / `ToolContext` | Versioned policy model |
173
+ | `PolicyEngine` | Deterministic evaluation |
174
+ | `default_safe_policy` | Built-in fail-closed policy |
175
+ | `load_policy` / `load_policy_file` | Parse JSON policies |
176
+
177
+ ### Decisions, redaction, events
178
+
179
+ | Symbol | Purpose |
180
+ | --- | --- |
181
+ | `DecisionAction` / `DecisionRecord` / `build_decision` | Structured decisions |
182
+ | `REDACTED` / `redact_text` / `redact_value` | Secret scrubbing |
183
+ | `EventBus` / `DecisionSink` | Export hooks |
184
+ | `CollectingSink` / `LoggingSink` / `JsonLinesSink` | Built-in sinks |
185
+ | `decision_to_log_record` | Flat mapping for loggers |
186
+
187
+ ### Review, safety, simulation, errors
188
+
189
+ | Symbol | Purpose |
190
+ | --- | --- |
191
+ | `LLMReviewer` / `ReviewRequest` / `ReviewResult` / `DeterministicReviewer` / `apply_review` | Escalation review |
192
+ | `FailClosedConfig` / `has_unknown_labels` | Fail-closed configuration |
193
+ | `AttackScenario` / `ScenarioResult` / `builtin_scenarios` / `AttackSimulator` | Attack simulation |
194
+ | `TrustWardenError` / `PolicyError` / `PolicyBlockedError` | Exception hierarchy |
195
+
196
+ ### CLI
197
+
198
+ | Entry | Purpose |
199
+ | --- | --- |
200
+ | `trustwarden-simulate` | Run built-in attack scenarios and print a JSON summary (`trustwarden.simulator:main`) |
201
+
202
+ Import everything from the package root, for example `from trustwarden import Warden, mark_user_input`.
203
+
204
+ ## Configuration
205
+
206
+ | Variable | Required | Purpose |
207
+ | --- | --- | --- |
208
+ | `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`. |
209
+
210
+ **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`.
211
+
212
+ Do not commit a `.env` file or put secret values in documentation.
213
+
214
+ ## Run it locally
215
+
216
+ Install dependencies and the editable package:
217
+
218
+ ```bash
219
+ uv sync
220
+ ```
221
+
222
+ Lint, typecheck, test, and build:
223
+
224
+ ```bash
225
+ uv run ruff check .
226
+ uv run mypy src
227
+ uv run pytest
228
+ uv build
229
+ ```
230
+
231
+ Try the library:
232
+
233
+ ```bash
234
+ uv run python examples/basic_usage.py
235
+ uv run trustwarden-simulate
236
+ ```
237
+
238
+ `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`).
239
+
240
+ ## Architecture
241
+
242
+ **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`.
243
+
244
+ **Repository layout:**
245
+
246
+ | Path | Role |
247
+ | --- | --- |
248
+ | `src/trustwarden/` | Library implementation (labels, wrap, policy, events, simulator, …) |
249
+ | `tests/` | Unit tests and doctest runner |
250
+ | `examples/` | Runnable usage samples |
251
+ | `policies/default.json` | Sample versioned policy document |
252
+ | `frontend/public/` | Brand SVG assets only (no app) |
253
+ | `pyproject.toml` | Package metadata, ruff/mypy/pytest, `trustwarden-simulate` script |
254
+ | `.env.example` | Declares `PYPI_PACKAGE_NAME` (names only) |
255
+
256
+ **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.
@@ -0,0 +1,238 @@
1
+ # TrustWarden
2
+
3
+ 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.
4
+
5
+ ## Features
6
+
7
+ ### Typed decorator and adapter API
8
+
9
+ Wrap any sync Python callable with pre-execution policy checks. No LangChain, OpenAI, or other agent framework is required.
10
+
11
+ **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`.
12
+
13
+ ```python
14
+ from trustwarden import Warden, mark_user_input, unwrap_result
15
+
16
+ warden = Warden()
17
+
18
+ @warden.tool(destination="local", state_changing=False)
19
+ def search(query: object) -> str:
20
+ return str(unwrap_result(query))
21
+
22
+ search(mark_user_input("roadmap"))
23
+ ```
24
+
25
+ ### Trust-label propagation and marking
26
+
27
+ 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).
28
+
29
+ ```python
30
+ from trustwarden import mark_user_input, mark_secret, collect_labels, propagate_labels
31
+
32
+ trust, sensitivity = collect_labels(mark_user_input("q"), mark_secret("sk"))
33
+ out = propagate_labels({"hits": 1}, mark_user_input("q"))
34
+ ```
35
+
36
+ ### Pre-execution policies (allow / block / escalate)
37
+
38
+ 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`.
39
+
40
+ ```python
41
+ from pathlib import Path
42
+ from trustwarden import Warden, load_policy_file, mark_secret
43
+
44
+ policy = load_policy_file(Path("policies/default.json"))
45
+ warden = Warden(policy)
46
+ warden.decide(
47
+ "http_post",
48
+ {"body": mark_secret("sk-demo")},
49
+ destination="external_http",
50
+ state_changing=True,
51
+ )
52
+ ```
53
+
54
+ ### Optional LLM-assisted review
55
+
56
+ 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.
57
+
58
+ ```python
59
+ from trustwarden import Warden, DeterministicReviewer, DecisionAction, mark_user_input
60
+
61
+ warden = Warden(reviewer=DeterministicReviewer(default_action=DecisionAction.BLOCK))
62
+ warden.decide(
63
+ "send_email",
64
+ {"body": mark_user_input("hello")},
65
+ destination="email",
66
+ state_changing=True,
67
+ )
68
+ ```
69
+
70
+ ### Safe-by-default / fail-closed behavior
71
+
72
+ 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`).
73
+
74
+ ```python
75
+ from trustwarden import Warden, FailClosedConfig, has_unknown_labels
76
+
77
+ assert has_unknown_labels("bare") is True
78
+ Warden(fail_closed=FailClosedConfig.safe_defaults())
79
+ ```
80
+
81
+ ### Local attack simulator
82
+
83
+ `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`.
84
+
85
+ ```python
86
+ from trustwarden import AttackSimulator
87
+
88
+ AttackSimulator().assert_all_passed()
89
+ ```
90
+
91
+ ```bash
92
+ uv run trustwarden-simulate
93
+ ```
94
+
95
+ ### Structured, secret-redacted decision records and event hooks
96
+
97
+ 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`.
98
+
99
+ ```python
100
+ from io import StringIO
101
+ from trustwarden import EventBus, JsonLinesSink, Warden, mark_user_input
102
+
103
+ bus = EventBus()
104
+ buf = StringIO()
105
+ bus.subscribe_sink(JsonLinesSink(buf))
106
+ warden = Warden(events=bus)
107
+ warden.decide("read", {"q": mark_user_input("x")}, destination="local")
108
+ ```
109
+
110
+ ### Framework-neutral core, typed interfaces, versioned policies, examples
111
+
112
+ 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.
113
+
114
+ ## Pages
115
+
116
+ TrustWarden is a **Python package**, not a web application. There are **no frontend routes** and no interactive screens.
117
+
118
+ | Path | What it is |
119
+ | --- | --- |
120
+ | _(none)_ | No app pages or SPA routes are served |
121
+ | `frontend/public/logo.svg` | Brand logo asset (primary `#003366`, accent `#FFCC00`) |
122
+ | `frontend/public/favicon.svg` | Brand favicon asset |
123
+
124
+ Use the library from your own agent or service process; there is nothing to open in a browser for TrustWarden itself.
125
+
126
+ ## API
127
+
128
+ 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:
129
+
130
+ ### Labels and marking
131
+
132
+ | Symbol | Purpose |
133
+ | --- | --- |
134
+ | `TrustLevel` / `Sensitivity` | Enumerations for provenance and sensitivity |
135
+ | `Labelled` | Value + label metadata (`unwrap`, `with_labels`) |
136
+ | `as_labelled` / `collect_labels` / `propagate_labels` | Coerce, aggregate, and attach labels |
137
+ | `mark_user_input` / `mark_trusted` / `mark_system` / `mark_untrusted` | Mark provenance |
138
+ | `mark_sensitive` / `mark_secret` | Mark sensitivity |
139
+ | `unwrap_result` | Extract raw value from a labelled result |
140
+
141
+ ### Guarding tools
142
+
143
+ | Symbol | Purpose |
144
+ | --- | --- |
145
+ | `Warden` | Policy guard: `tool`, `adapt`, `decide`, `check` |
146
+ | `ToolSpec` | Declarative tool description for registries |
147
+ | `wrap_callable` / `wrap_tool_map` / `wrap_tool_specs` | Framework-neutral adapters |
148
+
149
+ ### Policy
150
+
151
+ | Symbol | Purpose |
152
+ | --- | --- |
153
+ | `POLICY_FORMAT_VERSION` | Schema version (`1.0`) |
154
+ | `Policy` / `PolicyRule` / `RuleKind` / `ToolContext` | Versioned policy model |
155
+ | `PolicyEngine` | Deterministic evaluation |
156
+ | `default_safe_policy` | Built-in fail-closed policy |
157
+ | `load_policy` / `load_policy_file` | Parse JSON policies |
158
+
159
+ ### Decisions, redaction, events
160
+
161
+ | Symbol | Purpose |
162
+ | --- | --- |
163
+ | `DecisionAction` / `DecisionRecord` / `build_decision` | Structured decisions |
164
+ | `REDACTED` / `redact_text` / `redact_value` | Secret scrubbing |
165
+ | `EventBus` / `DecisionSink` | Export hooks |
166
+ | `CollectingSink` / `LoggingSink` / `JsonLinesSink` | Built-in sinks |
167
+ | `decision_to_log_record` | Flat mapping for loggers |
168
+
169
+ ### Review, safety, simulation, errors
170
+
171
+ | Symbol | Purpose |
172
+ | --- | --- |
173
+ | `LLMReviewer` / `ReviewRequest` / `ReviewResult` / `DeterministicReviewer` / `apply_review` | Escalation review |
174
+ | `FailClosedConfig` / `has_unknown_labels` | Fail-closed configuration |
175
+ | `AttackScenario` / `ScenarioResult` / `builtin_scenarios` / `AttackSimulator` | Attack simulation |
176
+ | `TrustWardenError` / `PolicyError` / `PolicyBlockedError` | Exception hierarchy |
177
+
178
+ ### CLI
179
+
180
+ | Entry | Purpose |
181
+ | --- | --- |
182
+ | `trustwarden-simulate` | Run built-in attack scenarios and print a JSON summary (`trustwarden.simulator:main`) |
183
+
184
+ Import everything from the package root, for example `from trustwarden import Warden, mark_user_input`.
185
+
186
+ ## Configuration
187
+
188
+ | Variable | Required | Purpose |
189
+ | --- | --- | --- |
190
+ | `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`. |
191
+
192
+ **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`.
193
+
194
+ Do not commit a `.env` file or put secret values in documentation.
195
+
196
+ ## Run it locally
197
+
198
+ Install dependencies and the editable package:
199
+
200
+ ```bash
201
+ uv sync
202
+ ```
203
+
204
+ Lint, typecheck, test, and build:
205
+
206
+ ```bash
207
+ uv run ruff check .
208
+ uv run mypy src
209
+ uv run pytest
210
+ uv build
211
+ ```
212
+
213
+ Try the library:
214
+
215
+ ```bash
216
+ uv run python examples/basic_usage.py
217
+ uv run trustwarden-simulate
218
+ ```
219
+
220
+ `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`).
221
+
222
+ ## Architecture
223
+
224
+ **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`.
225
+
226
+ **Repository layout:**
227
+
228
+ | Path | Role |
229
+ | --- | --- |
230
+ | `src/trustwarden/` | Library implementation (labels, wrap, policy, events, simulator, …) |
231
+ | `tests/` | Unit tests and doctest runner |
232
+ | `examples/` | Runnable usage samples |
233
+ | `policies/default.json` | Sample versioned policy document |
234
+ | `frontend/public/` | Brand SVG assets only (no app) |
235
+ | `pyproject.toml` | Package metadata, ruff/mypy/pytest, `trustwarden-simulate` script |
236
+ | `.env.example` | Declares `PYPI_PACKAGE_NAME` (names only) |
237
+
238
+ **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.