truceptor 0.3.2__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 (112) hide show
  1. truceptor-0.3.2/LICENSE +21 -0
  2. truceptor-0.3.2/MANIFEST.in +1 -0
  3. truceptor-0.3.2/PKG-INFO +401 -0
  4. truceptor-0.3.2/README.md +375 -0
  5. truceptor-0.3.2/acp/__init__.py +16 -0
  6. truceptor-0.3.2/acp/bundle/dashboard/app.js +1564 -0
  7. truceptor-0.3.2/acp/bundle/dashboard/index.html +123 -0
  8. truceptor-0.3.2/acp/bundle/dashboard/styles.css +1114 -0
  9. truceptor-0.3.2/acp/bundle/docker-compose.yml +82 -0
  10. truceptor-0.3.2/acp/bundle/interceptor/Dockerfile +16 -0
  11. truceptor-0.3.2/acp/bundle/interceptor/README.md +105 -0
  12. truceptor-0.3.2/acp/bundle/interceptor/api/openapi.yaml +220 -0
  13. truceptor-0.3.2/acp/bundle/interceptor/cmd/server/main.go +41 -0
  14. truceptor-0.3.2/acp/bundle/interceptor/go.mod +47 -0
  15. truceptor-0.3.2/acp/bundle/interceptor/go.sum +111 -0
  16. truceptor-0.3.2/acp/bundle/interceptor/internal/approval/errors.go +10 -0
  17. truceptor-0.3.2/acp/bundle/interceptor/internal/approval/repository.go +12 -0
  18. truceptor-0.3.2/acp/bundle/interceptor/internal/approval/store.go +221 -0
  19. truceptor-0.3.2/acp/bundle/interceptor/internal/approval/store_test.go +47 -0
  20. truceptor-0.3.2/acp/bundle/interceptor/internal/approval/sync.go +48 -0
  21. truceptor-0.3.2/acp/bundle/interceptor/internal/approval/sync_test.go +53 -0
  22. truceptor-0.3.2/acp/bundle/interceptor/internal/audit/events.go +6 -0
  23. truceptor-0.3.2/acp/bundle/interceptor/internal/audit/logger.go +53 -0
  24. truceptor-0.3.2/acp/bundle/interceptor/internal/audit/store.go +116 -0
  25. truceptor-0.3.2/acp/bundle/interceptor/internal/audit/store_iface.go +9 -0
  26. truceptor-0.3.2/acp/bundle/interceptor/internal/auth/context.go +41 -0
  27. truceptor-0.3.2/acp/bundle/interceptor/internal/auth/load.go +42 -0
  28. truceptor-0.3.2/acp/bundle/interceptor/internal/auth/principal.go +9 -0
  29. truceptor-0.3.2/acp/bundle/interceptor/internal/auth/validator.go +168 -0
  30. truceptor-0.3.2/acp/bundle/interceptor/internal/auth/validator_test.go +61 -0
  31. truceptor-0.3.2/acp/bundle/interceptor/internal/config/config.go +24 -0
  32. truceptor-0.3.2/acp/bundle/interceptor/internal/config/env.go +83 -0
  33. truceptor-0.3.2/acp/bundle/interceptor/internal/gateway/bootstrap.go +51 -0
  34. truceptor-0.3.2/acp/bundle/interceptor/internal/gateway/router.go +36 -0
  35. truceptor-0.3.2/acp/bundle/interceptor/internal/gateway/routes.go +42 -0
  36. truceptor-0.3.2/acp/bundle/interceptor/internal/gateway/server.go +90 -0
  37. truceptor-0.3.2/acp/bundle/interceptor/internal/handlers/agent_handler.go +94 -0
  38. truceptor-0.3.2/acp/bundle/interceptor/internal/handlers/approval_handler.go +122 -0
  39. truceptor-0.3.2/acp/bundle/interceptor/internal/handlers/insights_handler.go +37 -0
  40. truceptor-0.3.2/acp/bundle/interceptor/internal/handlers/policy_catalog_handler.go +35 -0
  41. truceptor-0.3.2/acp/bundle/interceptor/internal/handlers/replay_handler.go +61 -0
  42. truceptor-0.3.2/acp/bundle/interceptor/internal/handlers/tool_handler.go +51 -0
  43. truceptor-0.3.2/acp/bundle/interceptor/internal/middleware/auth.go +29 -0
  44. truceptor-0.3.2/acp/bundle/interceptor/internal/middleware/cors.go +60 -0
  45. truceptor-0.3.2/acp/bundle/interceptor/internal/middleware/jwt.go +47 -0
  46. truceptor-0.3.2/acp/bundle/interceptor/internal/middleware/logging.go +35 -0
  47. truceptor-0.3.2/acp/bundle/interceptor/internal/middleware/recovery.go +27 -0
  48. truceptor-0.3.2/acp/bundle/interceptor/internal/middleware/tracing.go +15 -0
  49. truceptor-0.3.2/acp/bundle/interceptor/internal/models/policy.go +38 -0
  50. truceptor-0.3.2/acp/bundle/interceptor/internal/models/request.go +11 -0
  51. truceptor-0.3.2/acp/bundle/interceptor/internal/models/response.go +8 -0
  52. truceptor-0.3.2/acp/bundle/interceptor/internal/policy/evaluator.go +64 -0
  53. truceptor-0.3.2/acp/bundle/interceptor/internal/policy/mapper.go +33 -0
  54. truceptor-0.3.2/acp/bundle/interceptor/internal/policy/opa_client.go +83 -0
  55. truceptor-0.3.2/acp/bundle/interceptor/internal/policycatalog/rules.go +242 -0
  56. truceptor-0.3.2/acp/bundle/interceptor/internal/policycatalog/rules_test.go +80 -0
  57. truceptor-0.3.2/acp/bundle/interceptor/internal/policycatalog/scanner.go +126 -0
  58. truceptor-0.3.2/acp/bundle/interceptor/internal/policycatalog/scanner_test.go +54 -0
  59. truceptor-0.3.2/acp/bundle/interceptor/internal/registry/agent.go +53 -0
  60. truceptor-0.3.2/acp/bundle/interceptor/internal/registry/memory.go +154 -0
  61. truceptor-0.3.2/acp/bundle/interceptor/internal/replay/service.go +187 -0
  62. truceptor-0.3.2/acp/bundle/interceptor/internal/replay/service_test.go +76 -0
  63. truceptor-0.3.2/acp/bundle/interceptor/internal/services/audit_service.go +18 -0
  64. truceptor-0.3.2/acp/bundle/interceptor/internal/services/interceptor_service.go +183 -0
  65. truceptor-0.3.2/acp/bundle/interceptor/internal/services/policy_service.go +23 -0
  66. truceptor-0.3.2/acp/bundle/interceptor/internal/storage/postgres/agents.go +153 -0
  67. truceptor-0.3.2/acp/bundle/interceptor/internal/storage/postgres/approval.go +171 -0
  68. truceptor-0.3.2/acp/bundle/interceptor/internal/storage/postgres/audit.go +156 -0
  69. truceptor-0.3.2/acp/bundle/interceptor/internal/storage/postgres/db.go +62 -0
  70. truceptor-0.3.2/acp/bundle/interceptor/internal/storage/postgres/migrations/001_schema.sql +55 -0
  71. truceptor-0.3.2/acp/bundle/interceptor/internal/storage/postgres/migrations/002_seed_agents.sql +7 -0
  72. truceptor-0.3.2/acp/bundle/interceptor/internal/tracing/context.go +24 -0
  73. truceptor-0.3.2/acp/bundle/interceptor/internal/tracing/trace.go +34 -0
  74. truceptor-0.3.2/acp/bundle/interceptor/internal/utils/errors.go +13 -0
  75. truceptor-0.3.2/acp/bundle/interceptor/internal/utils/response.go +28 -0
  76. truceptor-0.3.2/acp/bundle/interceptor/internal/utils/validation.go +21 -0
  77. truceptor-0.3.2/acp/bundle/interceptor/policies/README.md +102 -0
  78. truceptor-0.3.2/acp/bundle/interceptor/policies/mortgage_application.rego +25 -0
  79. truceptor-0.3.2/acp/bundle/interceptor/policies/mortgage_underwriting.rego +34 -0
  80. truceptor-0.3.2/acp/bundle/interceptor/policies/rbac.rego +50 -0
  81. truceptor-0.3.2/acp/bundle/interceptor/policies/router.rego +30 -0
  82. truceptor-0.3.2/acp/bundle/interceptor/scripts/generate-jwt-keys.sh +35 -0
  83. truceptor-0.3.2/acp/bundle/interceptor/scripts/run-local.sh +24 -0
  84. truceptor-0.3.2/acp/bundle/jwt/public.pem +9 -0
  85. truceptor-0.3.2/acp/bundle/nginx-dashboard.conf +26 -0
  86. truceptor-0.3.2/acp/bundle/nginx-gateway.conf +36 -0
  87. truceptor-0.3.2/acp/bundle/policies/README.md +102 -0
  88. truceptor-0.3.2/acp/bundle/policies/mortgage_application.rego +25 -0
  89. truceptor-0.3.2/acp/bundle/policies/mortgage_underwriting.rego +34 -0
  90. truceptor-0.3.2/acp/bundle/policies/rbac.rego +50 -0
  91. truceptor-0.3.2/acp/bundle/policies/router.rego +30 -0
  92. truceptor-0.3.2/acp/bundle/public/index.html +12 -0
  93. truceptor-0.3.2/acp/cli.py +177 -0
  94. truceptor-0.3.2/acp/client.py +215 -0
  95. truceptor-0.3.2/acp/config.py +19 -0
  96. truceptor-0.3.2/acp/decorators.py +61 -0
  97. truceptor-0.3.2/acp/exceptions.py +30 -0
  98. truceptor-0.3.2/acp/policies.py +113 -0
  99. truceptor-0.3.2/acp/policy-starter/mortgage_application.rego +25 -0
  100. truceptor-0.3.2/acp/policy-starter/mortgage_underwriting.rego +34 -0
  101. truceptor-0.3.2/acp/policy-starter/rbac.rego +50 -0
  102. truceptor-0.3.2/acp/policy-starter/router.rego +30 -0
  103. truceptor-0.3.2/acp/runtime.py +93 -0
  104. truceptor-0.3.2/pyproject.toml +56 -0
  105. truceptor-0.3.2/setup.cfg +4 -0
  106. truceptor-0.3.2/tests/test_decorator.py +41 -0
  107. truceptor-0.3.2/truceptor.egg-info/PKG-INFO +401 -0
  108. truceptor-0.3.2/truceptor.egg-info/SOURCES.txt +110 -0
  109. truceptor-0.3.2/truceptor.egg-info/dependency_links.txt +1 -0
  110. truceptor-0.3.2/truceptor.egg-info/entry_points.txt +2 -0
  111. truceptor-0.3.2/truceptor.egg-info/requires.txt +8 -0
  112. truceptor-0.3.2/truceptor.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Raja Datascientist
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ recursive-include acp/bundle *
@@ -0,0 +1,401 @@
1
+ Metadata-Version: 2.4
2
+ Name: truceptor
3
+ Version: 0.3.2
4
+ Summary: Truceptor — trusted intercept for agent tool calls: allow/deny/escalate (does not execute tools).
5
+ Author: SV
6
+ License-Expression: MIT
7
+ Keywords: ai,agents,governance,policy,opa,control-plane,mcp,audit
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: requests>=2.33.0
19
+ Provides-Extra: dev
20
+ Requires-Dist: pytest>=7.0; extra == "dev"
21
+ Requires-Dist: build>=1.0; extra == "dev"
22
+ Requires-Dist: twine>=5.0; extra == "dev"
23
+ Requires-Dist: pip-audit>=2.7; extra == "dev"
24
+ Requires-Dist: bandit>=1.7; extra == "dev"
25
+ Dynamic: license-file
26
+
27
+ # Truceptor (Python SDK)
28
+
29
+ **Truceptor** — trusted intercept for agent tool calls: preflight allow/deny/escalate; your runtime executes tools after allow.
30
+
31
+ > CLI stays **`acp`** (`acp up`, `acp init`). Install: **`pip install truceptor`**.
32
+
33
+ > **Note:** “ACP” means many things in other industries (medical, political, nonprofit, and more). In this project it always means **ACP — AI Control Plane**, not a generic acronym.
34
+
35
+ **Add governance, approvals, policy enforcement, and execution visibility to AI agents in minutes.**
36
+
37
+ Works with CrewAI, LangGraph, Strands, Google ADK, MCP tools, and custom Python workflows.
38
+
39
+ **Requires:** [Docker Desktop](https://docs.docker.com/get-docker/) (Compose v2).
40
+
41
+ ---
42
+
43
+ ## Quick start
44
+
45
+ Make sure Docker Desktop is already running before you start ACP.
46
+
47
+ ```bash
48
+ pip install truceptor
49
+ acp init # creates starter policies in ~/.acp/policies
50
+ acp up
51
+ acp dashboard # open governance UI
52
+ ```
53
+
54
+ | Step | Command |
55
+ |------|---------|
56
+ | Install | `pip install truceptor` |
57
+ | Initialize policies | `acp init` |
58
+ | Start stack | `acp up` |
59
+ | Open UI | `acp dashboard` → http://localhost:3090/dashboard/ |
60
+ | Stop | `acp down` |
61
+
62
+ For a real local integration, the usual sequence is:
63
+
64
+ 1. `acp init`
65
+ 2. start Docker Desktop
66
+ 3. `acp up`
67
+ 4. `export ACP_INTERCEPTOR_URL=http://localhost:8080`
68
+ 5. `export ACP_BEARER_TOKEN=...`
69
+ 6. run your governed agent
70
+
71
+ If Docker is not already running, `acp up` will fail with Docker daemon / compose connection errors.
72
+
73
+ ---
74
+
75
+ ## Why ACP — AI Control Plane?
76
+
77
+ Most agents call tools, APIs, and other agents directly. Teams then scatter rules across Python, workflows, and frameworks:
78
+
79
+ ```python
80
+ if supplier_risk_score > threshold:
81
+ require_human_approval()
82
+ ```
83
+
84
+ That becomes inconsistent, hard to audit, easy to bypass, and duplicated everywhere.
85
+
86
+ **ACP — AI Control Plane** centralizes governance **outside** agent code:
87
+
88
+ | Capability | What you get |
89
+ |------------|----------------|
90
+ | **Centralized governance** | One place for rules, not copy-paste per team |
91
+ | **Policy enforcement** | OPA/Rego evaluates every governed call |
92
+ | **Approvals** | Escalate high-risk actions to humans |
93
+ | **A2A governance** | Governed agent-to-agent calls |
94
+ | **A2T governance** | Governed agent-to-tool calls |
95
+ | **Audit & visibility** | Decisions, traces, registry in one dashboard |
96
+
97
+ ---
98
+
99
+ ## Architecture
100
+
101
+ ![ACP — AI Control Plane architecture](https://raw.githubusercontent.com/raja-datascientist/acp-docs/refs/heads/main/images/acp-architecture.svg)
102
+
103
+ ```text
104
+ Agent runtime -> ACP SDK -> Interceptor / Gateway -> OPA / Rego -> allow | deny | escalate -> governed execution
105
+ ```
106
+
107
+ ACP sits at the execution boundary:
108
+
109
+ - agent frameworks keep reasoning and orchestration
110
+ - the interceptor validates identity, routes policy, and records audit traces
111
+ - OPA / Rego returns deterministic allow, deny, or escalate decisions
112
+ - only allowed or approved requests reach tools, APIs, MCP servers, or other agents
113
+
114
+ ## Request sequence
115
+
116
+ ![ACP — AI Control Plane governed request sequence](https://raw.githubusercontent.com/raja-datascientist/acp-docs/refs/heads/main/images/acp-sequence.svg)
117
+
118
+ Typical governed flow:
119
+
120
+ 1. the agent calls a governed function through the ACP SDK
121
+ 2. the interceptor validates JWT identity and normalizes the request
122
+ 3. OPA / Rego evaluates policy for the tool, action, and claims
123
+ 4. ACP records the decision and exposes it in the dashboard
124
+ 5. the SDK executes only on `allow`, waits for review on `escalate`, and blocks on `deny`
125
+
126
+ ---
127
+
128
+ ## Dashboard
129
+
130
+ The **ACP — AI Control Plane** dashboard is a core differentiator: live allow/deny/escalate decisions, approvals, agent registry, and policy catalog. Open **http://localhost:3090/dashboard/** after `acp up`.
131
+
132
+ ### Overview & activity
133
+
134
+ ![Overview — governance posture and live activity](https://raw.githubusercontent.com/raja-datascientist/acp-docs/refs/heads/main/images/dashboard-overview.png)
135
+
136
+ ![Live activity — real-time enforcement stream](https://raw.githubusercontent.com/raja-datascientist/acp-docs/refs/heads/main/images/dashboard-live-activity.png)
137
+
138
+ ### Decisions & approvals
139
+
140
+ ![Governance decisions — allow / deny / escalate analytics](https://raw.githubusercontent.com/raja-datascientist/acp-docs/refs/heads/main/images/dashboard-governance-decisions.png)
141
+
142
+ ![Approvals — human-in-the-loop escalation queue](https://raw.githubusercontent.com/raja-datascientist/acp-docs/refs/heads/main/images/dashboard-approvals.png)
143
+
144
+ ### Registry & policies
145
+
146
+ ![Agent registry — identity catalog for policy enforcement](https://raw.githubusercontent.com/raja-datascientist/acp-docs/refs/heads/main/images/dashboard-agent-registry.png)
147
+
148
+ ![Policies — Rego catalog from the interceptor](https://raw.githubusercontent.com/raja-datascientist/acp-docs/refs/heads/main/images/dashboard-policies.png)
149
+
150
+ ![Policy detail — mortgage underwriting rules](https://raw.githubusercontent.com/raja-datascientist/acp-docs/refs/heads/main/images/dashboard-policy-underwriting.png)
151
+
152
+ ### Forensics
153
+
154
+ ![Traces — governance trace investigation](https://raw.githubusercontent.com/raja-datascientist/acp-docs/refs/heads/main/images/dashboard-traces.png)
155
+
156
+ ---
157
+
158
+ ## Deployment modes
159
+
160
+ | Mode | How | Best for |
161
+ |------|-----|----------|
162
+ | **Local** | `acp up` via pip + Docker | Demos, dev, quickstart |
163
+ | **SDK** | `@governed_tool` in your agent code | CrewAI, LangGraph, Strands, custom Python |
164
+ | **Gateway** | Single origin on `:3090` (dashboard + API proxy) | Local unified URL; pattern for prod ingress |
165
+ | **Cloud / self-hosted** | Docker Compose, Kubernetes, ECS/EKS on AWS/Azure/GCP | Team or enterprise rollout |
166
+
167
+ **Local endpoints**
168
+
169
+ | URL | Purpose |
170
+ |-----|---------|
171
+ | http://localhost:3090/dashboard/ | Governance dashboard |
172
+ | http://localhost:8080 | Interceptor API (`/tool-call`, `/api/v1/*`) |
173
+
174
+ **Self-hosted (example)**
175
+
176
+ ```text
177
+ https://acp.your-company.example
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Example: governed tool
183
+
184
+ Set `ACP_INTERCEPTOR_URL` and `ACP_BEARER_TOKEN` **before** defining a `@governed_tool`.
185
+
186
+ The decorator constructs its ACP client at decoration time, so setting those env vars later inside `main()` is too late.
187
+
188
+ ```python
189
+ import os
190
+ from acp import governed_tool
191
+
192
+ # Set env vars before decorator evaluation.
193
+ os.environ.setdefault("ACP_INTERCEPTOR_URL", "http://localhost:8080")
194
+ os.environ.setdefault("ACP_BEARER_TOKEN", "<dev-jwt>")
195
+
196
+ @governed_tool(
197
+ agent_id="texas-weather-agent",
198
+ tool="weather_api",
199
+ action="read_weather",
200
+ )
201
+ def get_texas_weather(city: str):
202
+ return {"city": city, "status": "pending_review"}
203
+ ```
204
+
205
+ The **AI Control Plane** intercepts the call, evaluates policy, then allows, denies, or escalates.
206
+
207
+ `tool="weather_api"` is the governed ACP tool id. It does **not** have to match the Python function name (`get_texas_weather`). ACP policy keys on `tool`, not the Python symbol name.
208
+
209
+ The JWT `agent_id` claim must match `agent_id="texas-weather-agent"` in `@governed_tool(...)`.
210
+
211
+ ---
212
+
213
+ ## Example: policy (Rego)
214
+
215
+ ```rego
216
+ package acp.policy
217
+
218
+ allow {
219
+ input.identity.role == "supply-chain-manager"
220
+ input.action.tool == "supplier_approval"
221
+ input.risk_score < 70
222
+ }
223
+ ```
224
+
225
+ Edit policies in `~/.acp/policies/` after `acp init`.
226
+
227
+ ---
228
+
229
+ ## Adding a new governed tool
230
+
231
+ `acp init` creates starter policy files in `~/.acp/policies/`, but you still need to wire new tools into policy yourself.
232
+
233
+ For a brand-new governed tool, update all of these:
234
+
235
+ 1. choose an ACP tool id, for example `weather_api`
236
+ 2. add tool-to-role mapping in `~/.acp/policies/rbac.rego`
237
+ 3. route the tool in `~/.acp/policies/router.rego`
238
+ 4. add or reuse a domain policy file such as `weather.rego`
239
+ 5. mint and export `ACP_BEARER_TOKEN`
240
+ 6. make sure the JWT `agent_id` claim matches `@governed_tool(agent_id=...)`
241
+ 7. run the agent
242
+
243
+ Two valid patterns:
244
+
245
+ ### Pattern 1: reuse an ACP tool bucket
246
+
247
+ Use one ACP tool id for multiple Python functions in the same policy domain.
248
+
249
+ ```python
250
+ @governed_tool(
251
+ agent_id="texas-weather-agent",
252
+ tool="weather_api",
253
+ action="read_weather",
254
+ )
255
+ def get_texas_weather(city: str):
256
+ ...
257
+ ```
258
+
259
+ `~/.acp/policies/rbac.rego`
260
+
261
+ ```rego
262
+ "weather_api": ["weather"],
263
+ ```
264
+
265
+ `~/.acp/policies/router.rego`
266
+
267
+ ```rego
268
+ decision = weather.decision {
269
+ input.tool == "weather_api"
270
+ rbac.decision.decision == "allow"
271
+ }
272
+ ```
273
+
274
+ `~/.acp/policies/weather.rego`
275
+
276
+ ```rego
277
+ package acp.weather
278
+
279
+ default decision = {
280
+ "decision": "allow",
281
+ "reason": "weather_action_allowed",
282
+ "policy": "acp/weather",
283
+ }
284
+
285
+ decision = {
286
+ "decision": "deny",
287
+ "reason": "weather_action_not_permitted",
288
+ "policy": "acp/weather",
289
+ } {
290
+ not input.action == "read_weather"
291
+ not input.action == "read_weather_batch"
292
+ }
293
+ ```
294
+
295
+ ### Pattern 2: add a brand-new ACP tool id
296
+
297
+ If you want policy to key directly on a new id such as `get_texas_weather`, add that id to both `rbac.rego` and `router.rego`, then route it to the right domain policy file.
298
+
299
+ If your starter `router.rego` or `rbac.rego` references a tool id, make sure the corresponding domain policy file also exists.
300
+
301
+ ---
302
+
303
+ ## Local JWT dev example
304
+
305
+ Local governed `/tool-call` requests require a bearer token when JWT auth is enabled. `ACP_INTERCEPTOR_URL` alone is not enough.
306
+
307
+ Set:
308
+
309
+ - `ACP_INTERCEPTOR_URL=http://localhost:8080`
310
+ - `ACP_BEARER_TOKEN=<signed-jwt>`
311
+
312
+ If `ACP_BEARER_TOKEN` is missing, governed calls may fail with `401 Unauthorized` / `missing bearer token`.
313
+
314
+ One dev-friendly way to mint a token is:
315
+
316
+ ```bash
317
+ python -m pip install pyjwt cryptography
318
+
319
+ export ACP_BEARER_TOKEN="$(
320
+ python - <<'PY'
321
+ from pathlib import Path
322
+ import time
323
+ import jwt
324
+
325
+ private_key = (Path.home() / ".acp" / "jwt" / "private.pem").read_text()
326
+ token = jwt.encode(
327
+ {
328
+ "sub": "agent:texas-weather-agent",
329
+ "agent_id": "texas-weather-agent",
330
+ "roles": ["weather"],
331
+ "iss": "acp-dev",
332
+ "aud": "acp-interceptor",
333
+ "exp": int(time.time()) + 3600,
334
+ },
335
+ private_key,
336
+ algorithm="RS256",
337
+ )
338
+ print(token)
339
+ PY
340
+ )"
341
+ ```
342
+
343
+ Use these claims for local development:
344
+
345
+ - `sub=agent:texas-weather-agent`
346
+ - `agent_id=texas-weather-agent`
347
+ - `roles=["weather"]`
348
+ - `iss=acp-dev`
349
+ - `aud=acp-interceptor`
350
+
351
+ `agent_id` in the JWT must match the `agent_id` passed to `@governed_tool(...)`.
352
+
353
+ ---
354
+
355
+ ## Example: governance flow
356
+
357
+ ```text
358
+ Supply Chain Agent
359
+ → ACP — AI Control Plane validates identity (JWT)
360
+ → OPA evaluates policy
361
+ → Decision: ESCALATE
362
+ → Human approves in dashboard
363
+ → Execution resumes
364
+ ```
365
+
366
+ ---
367
+
368
+ ## What the AI Control Plane provides
369
+
370
+ - **Policy enforcement** — OPA/Rego (Cedar on roadmap)
371
+ - **Identity** — JWT from Okta, Auth0, Keycloak, or your IdP
372
+ - **Approvals** — human-in-the-loop for risky actions
373
+ - **Observability** — dashboard for decisions, traces, agents, tools
374
+ - **Agent registry** — lightweight catalog of agents and capabilities
375
+ - **Framework-friendly** — keep CrewAI / LangGraph / Strands for reasoning; govern execution here
376
+
377
+ ---
378
+
379
+ ## Philosophy
380
+
381
+ Orchestration frameworks handle **reasoning, planning, and workflows**.
382
+
383
+ **ACP — AI Control Plane** handles **governance, trust, approvals, policy, and auditability**.
384
+
385
+ Reasoning stays autonomous. Execution stays governed.
386
+
387
+ ---
388
+
389
+ ## Roadmap
390
+
391
+ - MCP/A2A governance intercept (decision-only, no execution)
392
+ - MCP-native governance
393
+ - Policy studio and replay
394
+ - Enterprise topology views
395
+ - Multi-cloud deployment templates
396
+
397
+ ---
398
+
399
+ ## License
400
+
401
+ MIT License