@aporthq/aport-agent-guardrails 1.0.8

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 (237) hide show
  1. package/LICENSE +217 -0
  2. package/README.md +481 -0
  3. package/bin/agent-guardrails +133 -0
  4. package/bin/aport-create-passport.sh +444 -0
  5. package/bin/aport-cursor-hook.sh +90 -0
  6. package/bin/aport-guardrail-api.sh +108 -0
  7. package/bin/aport-guardrail-bash.sh +394 -0
  8. package/bin/aport-guardrail-v2.sh +5 -0
  9. package/bin/aport-guardrail.sh +5 -0
  10. package/bin/aport-resolve-paths.sh +71 -0
  11. package/bin/aport-status.sh +276 -0
  12. package/bin/frameworks/crewai.sh +49 -0
  13. package/bin/frameworks/cursor.sh +95 -0
  14. package/bin/frameworks/langchain.sh +48 -0
  15. package/bin/frameworks/n8n.sh +36 -0
  16. package/bin/frameworks/openclaw.sh +19 -0
  17. package/bin/lib/allowlist.sh +18 -0
  18. package/bin/lib/common.sh +28 -0
  19. package/bin/lib/config.sh +46 -0
  20. package/bin/lib/constants.sh +232 -0
  21. package/bin/lib/detect.sh +65 -0
  22. package/bin/lib/error.sh +269 -0
  23. package/bin/lib/passport.sh +19 -0
  24. package/bin/lib/templates/.gitkeep +1 -0
  25. package/bin/lib/templates/config.yaml +6 -0
  26. package/bin/lib/validation.sh +206 -0
  27. package/bin/openclaw +660 -0
  28. package/docs/ADDING_A_FRAMEWORK.md +87 -0
  29. package/docs/AGENTS.md.example +40 -0
  30. package/docs/CODE_REVIEW.md +192 -0
  31. package/docs/DEPLOYMENT_READINESS.md +81 -0
  32. package/docs/FAQ_SECURITY_SCANNERS.md +373 -0
  33. package/docs/FRAMEWORK_ROADMAP.md +41 -0
  34. package/docs/HOSTED_PASSPORT_SETUP.md +362 -0
  35. package/docs/IMPLEMENTING_YOUR_OWN_EVALUATOR.md +433 -0
  36. package/docs/OPENCLAW_COMPATIBILITY.md +73 -0
  37. package/docs/OPENCLAW_LOCAL_INTEGRATION.md +596 -0
  38. package/docs/OPENCLAW_TOOLS_AND_POLICIES.md +54 -0
  39. package/docs/QUICKSTART.md +470 -0
  40. package/docs/QUICKSTART_OPENCLAW_PLUGIN.md +470 -0
  41. package/docs/README.md +28 -0
  42. package/docs/RELEASE.md +87 -0
  43. package/docs/REPO_LAYOUT.md +47 -0
  44. package/docs/SKILLS_ECOSYSTEM_ANALYSIS_FEB17.md +1260 -0
  45. package/docs/TOOL_POLICY_MAPPING.md +46 -0
  46. package/docs/UPGRADE.md +46 -0
  47. package/docs/VERIFICATION_METHODS.md +97 -0
  48. package/docs/assets/README.md +8 -0
  49. package/docs/assets/porter.svg +54 -0
  50. package/docs/development/ERROR_CODES.md +616 -0
  51. package/docs/frameworks/GITHUB_ISSUE_PROPOSALS.md +1105 -0
  52. package/docs/frameworks/crewai.md +114 -0
  53. package/docs/frameworks/cursor.md +159 -0
  54. package/docs/frameworks/langchain.md +72 -0
  55. package/docs/frameworks/n8n.md +40 -0
  56. package/docs/frameworks/openclaw.md +40 -0
  57. package/docs/launch/ADD_APORT_AWESOME_LISTS_INSTRUCTIONS.md +146 -0
  58. package/docs/launch/ANNOUNCEMENT_GUIDE.md +266 -0
  59. package/docs/launch/AWESOME_REPOS.md +53 -0
  60. package/docs/launch/CURSOR_VSCODE_HOOKS_RESEARCH.md +77 -0
  61. package/docs/launch/DEMO_TERMINAL_OUTPUT.txt +48 -0
  62. package/docs/launch/DRY_AND_PLAN_CHECKLIST.md +47 -0
  63. package/docs/launch/EVIDENCE_README.md +61 -0
  64. package/docs/launch/EVIDENCE_TERMINAL_CAPTURE.txt +10 -0
  65. package/docs/launch/FRAMEWORK_SUPPORT_PLAN.md +1640 -0
  66. package/docs/launch/LAUNCH_READINESS_CHECKLIST.md +237 -0
  67. package/docs/launch/LAUNCH_STRATEGY_SUMMARY.md +464 -0
  68. package/docs/launch/OPENCLAW_FEEDBACK_AND_FIXES.md +85 -0
  69. package/docs/launch/POST_1_VALENTINE_IMPROVED.md +233 -0
  70. package/docs/launch/POST_2_GUARDRAIL_IMPROVED.md +369 -0
  71. package/docs/launch/PRE_LAUNCH_FIXES.md +766 -0
  72. package/docs/launch/QUICK_LAUNCH_CHECKLIST.md +400 -0
  73. package/docs/launch/READINESS_SUMMARY.md +262 -0
  74. package/docs/launch/README.md +68 -0
  75. package/docs/launch/USER_STORIES.md +327 -0
  76. package/docs/launch/scripts/add-aport-awesome-pr.sh +69 -0
  77. package/docs/operations/MONITORING.md +588 -0
  78. package/docs/reviews/2026-02-18-staff-review.md +268 -0
  79. package/extensions/openclaw-aport/README.md +415 -0
  80. package/extensions/openclaw-aport/index.js +625 -0
  81. package/extensions/openclaw-aport/openclaw-aport.js +7 -0
  82. package/extensions/openclaw-aport/openclaw.plugin.json +46 -0
  83. package/extensions/openclaw-aport/package.json +36 -0
  84. package/extensions/openclaw-aport/test.js +307 -0
  85. package/external/aport-policies/README.md +363 -0
  86. package/external/aport-policies/agent.session.create.v1/README.md +345 -0
  87. package/external/aport-policies/agent.session.create.v1/policy.json +162 -0
  88. package/external/aport-policies/agent.tool.register.v1/README.md +361 -0
  89. package/external/aport-policies/agent.tool.register.v1/policy.json +172 -0
  90. package/external/aport-policies/code.release.publish.v1/README.md +51 -0
  91. package/external/aport-policies/code.release.publish.v1/policy.json +121 -0
  92. package/external/aport-policies/code.repository.merge.v1/README.md +287 -0
  93. package/external/aport-policies/code.repository.merge.v1/express.example.js +332 -0
  94. package/external/aport-policies/code.repository.merge.v1/fastapi.example.py +370 -0
  95. package/external/aport-policies/code.repository.merge.v1/policy.json +162 -0
  96. package/external/aport-policies/data.export.create.v1/README.md +226 -0
  97. package/external/aport-policies/data.export.create.v1/express.example.js +172 -0
  98. package/external/aport-policies/data.export.create.v1/fastapi.example.py +165 -0
  99. package/external/aport-policies/data.export.create.v1/policy.json +133 -0
  100. package/external/aport-policies/data.report.ingest.v1/README.md +134 -0
  101. package/external/aport-policies/data.report.ingest.v1/express.example.js +105 -0
  102. package/external/aport-policies/data.report.ingest.v1/minimal-example.js +68 -0
  103. package/external/aport-policies/data.report.ingest.v1/policy.json +174 -0
  104. package/external/aport-policies/finance.crypto.trade.v1/README.md +146 -0
  105. package/external/aport-policies/finance.crypto.trade.v1/express.example.js +109 -0
  106. package/external/aport-policies/finance.crypto.trade.v1/minimal-example.js +65 -0
  107. package/external/aport-policies/finance.crypto.trade.v1/policy.json +176 -0
  108. package/external/aport-policies/finance.payment.charge.v1/README.md +326 -0
  109. package/external/aport-policies/finance.payment.charge.v1/express.example.js +250 -0
  110. package/external/aport-policies/finance.payment.charge.v1/fastapi.example.py +227 -0
  111. package/external/aport-policies/finance.payment.charge.v1/minimal-example.js +64 -0
  112. package/external/aport-policies/finance.payment.charge.v1/policy.json +224 -0
  113. package/external/aport-policies/finance.payment.charge.v1/tests/contexts.jsonl +12 -0
  114. package/external/aport-policies/finance.payment.charge.v1/tests/expected.jsonl +12 -0
  115. package/external/aport-policies/finance.payment.charge.v1/tests/passport.instance.json +42 -0
  116. package/external/aport-policies/finance.payment.charge.v1/tests/passport.template.json +40 -0
  117. package/external/aport-policies/finance.payment.charge.v1/tests/payments-charge-policy.test.js +817 -0
  118. package/external/aport-policies/finance.payment.charge.v1/tests/test_payments_charge_policy.py +486 -0
  119. package/external/aport-policies/finance.payment.payout.v1/README.md +78 -0
  120. package/external/aport-policies/finance.payment.payout.v1/policy.json +181 -0
  121. package/external/aport-policies/finance.payment.refund.v1/README.md +275 -0
  122. package/external/aport-policies/finance.payment.refund.v1/express.example.js +167 -0
  123. package/external/aport-policies/finance.payment.refund.v1/fastapi.example.py +136 -0
  124. package/external/aport-policies/finance.payment.refund.v1/minimal-example.js +183 -0
  125. package/external/aport-policies/finance.payment.refund.v1/policy.json +216 -0
  126. package/external/aport-policies/finance.payment.refund.v1/tests/refunds-policy.test.js +924 -0
  127. package/external/aport-policies/finance.payment.refund.v1/tests/test_refunds_policy.py +778 -0
  128. package/external/aport-policies/finance.transaction.execute.v1/README.md +309 -0
  129. package/external/aport-policies/finance.transaction.execute.v1/express.example.js +261 -0
  130. package/external/aport-policies/finance.transaction.execute.v1/fastapi.example.py +231 -0
  131. package/external/aport-policies/finance.transaction.execute.v1/minimal-example.js +78 -0
  132. package/external/aport-policies/finance.transaction.execute.v1/policy.json +189 -0
  133. package/external/aport-policies/finance.transaction.execute.v1/tests/contexts.jsonl +12 -0
  134. package/external/aport-policies/finance.transaction.execute.v1/tests/expected.jsonl +12 -0
  135. package/external/aport-policies/finance.transaction.execute.v1/tests/passport.instance.json +42 -0
  136. package/external/aport-policies/finance.transaction.execute.v1/tests/passport.template.json +42 -0
  137. package/external/aport-policies/finance.transaction.execute.v1/tests/test_transactions_policy.py +214 -0
  138. package/external/aport-policies/finance.transaction.execute.v1/tests/transactions-policy.test.js +306 -0
  139. package/external/aport-policies/governance.data.access.v1/README.md +292 -0
  140. package/external/aport-policies/governance.data.access.v1/express.example.js +321 -0
  141. package/external/aport-policies/governance.data.access.v1/fastapi.example.py +279 -0
  142. package/external/aport-policies/governance.data.access.v1/minimal-example.js +65 -0
  143. package/external/aport-policies/governance.data.access.v1/policy.json +208 -0
  144. package/external/aport-policies/governance.data.access.v1/tests/contexts.jsonl +12 -0
  145. package/external/aport-policies/governance.data.access.v1/tests/data-access-policy.test.js +308 -0
  146. package/external/aport-policies/governance.data.access.v1/tests/expected.jsonl +12 -0
  147. package/external/aport-policies/governance.data.access.v1/tests/passport.instance.json +56 -0
  148. package/external/aport-policies/governance.data.access.v1/tests/passport.template.json +56 -0
  149. package/external/aport-policies/governance.data.access.v1/tests/test_data_access_policy.py +214 -0
  150. package/external/aport-policies/legal.contract.review.v1/README.md +109 -0
  151. package/external/aport-policies/legal.contract.review.v1/policy.json +378 -0
  152. package/external/aport-policies/legal.contract.review.v1/tests/legal-contract-review-policy.test.js +609 -0
  153. package/external/aport-policies/legal.contract.review.v1/tests/passport.template.json +49 -0
  154. package/external/aport-policies/mcp.tool.execute.v1/README.md +301 -0
  155. package/external/aport-policies/mcp.tool.execute.v1/policy.json +141 -0
  156. package/external/aport-policies/messaging.message.send.v1/README.md +230 -0
  157. package/external/aport-policies/messaging.message.send.v1/express.example.js +183 -0
  158. package/external/aport-policies/messaging.message.send.v1/fastapi.example.py +193 -0
  159. package/external/aport-policies/messaging.message.send.v1/policy.json +144 -0
  160. package/external/aport-policies/policy-template.json +107 -0
  161. package/external/aport-policies/system.command.execute.v1/README.md +275 -0
  162. package/external/aport-policies/system.command.execute.v1/policy.json +146 -0
  163. package/external/aport-spec/CONTRIBUTING.md +273 -0
  164. package/external/aport-spec/LICENSE +21 -0
  165. package/external/aport-spec/README.md +168 -0
  166. package/external/aport-spec/conformance/README.md +294 -0
  167. package/external/aport-spec/conformance/cases/data.export.v1/contexts/allow_users.json +6 -0
  168. package/external/aport-spec/conformance/cases/data.export.v1/contexts/deny_pii.json +6 -0
  169. package/external/aport-spec/conformance/cases/data.export.v1/expected/allow_users.decision.json +19 -0
  170. package/external/aport-spec/conformance/cases/data.export.v1/expected/deny_pii.decision.json +19 -0
  171. package/external/aport-spec/conformance/cases/data.export.v1/passports/template.json +29 -0
  172. package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/allow_50usd.json +9 -0
  173. package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/deny_150usd.json +9 -0
  174. package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/deny_currency.json +9 -0
  175. package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/allow_50usd.decision.json +19 -0
  176. package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/deny_150usd.decision.json +19 -0
  177. package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/deny_currency.decision.json +19 -0
  178. package/external/aport-spec/conformance/cases/payments.refunds.v1/passports/template.json +42 -0
  179. package/external/aport-spec/conformance/package.json +44 -0
  180. package/external/aport-spec/conformance/pnpm-lock.yaml +642 -0
  181. package/external/aport-spec/conformance/src/cases.ts +371 -0
  182. package/external/aport-spec/conformance/src/ed25519.ts +167 -0
  183. package/external/aport-spec/conformance/src/jcs.ts +85 -0
  184. package/external/aport-spec/conformance/src/runner.ts +533 -0
  185. package/external/aport-spec/conformance/src/validators.ts +185 -0
  186. package/external/aport-spec/conformance/test-runner.js +315 -0
  187. package/external/aport-spec/conformance/tsconfig.json +21 -0
  188. package/external/aport-spec/error-schema.json +192 -0
  189. package/external/aport-spec/index.json +12 -0
  190. package/external/aport-spec/integrations/clawmoat/README.md +12 -0
  191. package/external/aport-spec/integrations/shield/README.md +245 -0
  192. package/external/aport-spec/integrations/shield/adapters/index.js +116 -0
  193. package/external/aport-spec/integrations/shield/adapters/system-command-execute.js +133 -0
  194. package/external/aport-spec/integrations/shield/test/README.md +58 -0
  195. package/external/aport-spec/integrations/shield/test/shield.md +40 -0
  196. package/external/aport-spec/integrations/shield/test/test-shield-to-verify.js +274 -0
  197. package/external/aport-spec/metrics-schema.json +504 -0
  198. package/external/aport-spec/oap/CHANGELOG.md +54 -0
  199. package/external/aport-spec/oap/VERSION.md +40 -0
  200. package/external/aport-spec/oap/capability-registry.md +229 -0
  201. package/external/aport-spec/oap/conformance.md +257 -0
  202. package/external/aport-spec/oap/decision-schema.json +114 -0
  203. package/external/aport-spec/oap/examples/context.refund.usd.50.json +9 -0
  204. package/external/aport-spec/oap/examples/decision.allow.sample.json +20 -0
  205. package/external/aport-spec/oap/examples/decision.deny.sample.json +23 -0
  206. package/external/aport-spec/oap/examples/passport.instance.v1.json +50 -0
  207. package/external/aport-spec/oap/examples/passport.template.v1.json +71 -0
  208. package/external/aport-spec/oap/oap-spec.md +426 -0
  209. package/external/aport-spec/oap/passport-schema.json +396 -0
  210. package/external/aport-spec/oap/security.md +213 -0
  211. package/external/aport-spec/oap/vc/context-oap-v1.jsonld +137 -0
  212. package/external/aport-spec/oap/vc/examples/oap-decision-vc.json +37 -0
  213. package/external/aport-spec/oap/vc/examples/oap-passport-vc.json +68 -0
  214. package/external/aport-spec/oap/vc/tools/INTEGRATION.md +375 -0
  215. package/external/aport-spec/oap/vc/tools/README.md +278 -0
  216. package/external/aport-spec/oap/vc/tools/examples/decision-to-vc.js +66 -0
  217. package/external/aport-spec/oap/vc/tools/examples/passport-to-vc.js +83 -0
  218. package/external/aport-spec/oap/vc/tools/examples/vc-to-decision.js +77 -0
  219. package/external/aport-spec/oap/vc/tools/examples/vc-to-passport.js +94 -0
  220. package/external/aport-spec/oap/vc/tools/package.json +38 -0
  221. package/external/aport-spec/oap/vc/tools/pnpm-lock.yaml +472 -0
  222. package/external/aport-spec/oap/vc/tools/src/cli.ts +226 -0
  223. package/external/aport-spec/oap/vc/tools/src/crypto-utils.ts +427 -0
  224. package/external/aport-spec/oap/vc/tools/src/index.ts +653 -0
  225. package/external/aport-spec/oap/vc/tools/src/test.ts +148 -0
  226. package/external/aport-spec/oap/vc/tools/src/vp.ts +382 -0
  227. package/external/aport-spec/oap/vc/tools/test-simple.js +214 -0
  228. package/external/aport-spec/oap/vc/tools/tsconfig.json +19 -0
  229. package/external/aport-spec/oap/vc/vc-mapping.md +443 -0
  230. package/external/aport-spec/passport-schema.json +586 -0
  231. package/external/aport-spec/rate-limiting.md +136 -0
  232. package/external/aport-spec/transport-profile.md +325 -0
  233. package/external/aport-spec/webhook-spec.md +314 -0
  234. package/package.json +70 -0
  235. package/skills/aport-agent-guardrail/SKILL.md +314 -0
  236. package/src/evaluator.js +252 -0
  237. package/src/server/index.js +72 -0
package/README.md ADDED
@@ -0,0 +1,481 @@
1
+ <div align="center">
2
+
3
+ # ๐Ÿ›ก๏ธ APort Agent Guardrails
4
+
5
+ [![npm](https://img.shields.io/npm/v/@aporthq/aport-agent-guardrails.svg)](https://www.npmjs.com/package/@aporthq/aport-agent-guardrails)
6
+ [![PyPI](https://img.shields.io/pypi/v/aport-agent-guardrails.svg)](https://pypi.org/project/aport-agent-guardrails/)
7
+ [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
8
+ [![Tests](https://img.shields.io/badge/tests-passing-brightgreen.svg)](tests/)
9
+ [![Node](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg)](package.json)
10
+ [![Python](https://img.shields.io/badge/python-3.10%2B-brightgreen.svg)](python/aport_guardrails/pyproject.toml)
11
+ [![OpenClaw](https://img.shields.io/badge/OpenClaw-%3E%3D2026.2.0-blue.svg)](extensions/openclaw-aport/package.json)
12
+
13
+ <p>
14
+ <a href="https://www.npmjs.com/package/@aporthq/aport-agent-guardrails">npm</a> โ€ข
15
+ <a href="https://pypi.org/project/aport-agent-guardrails/">PyPI</a> โ€ข
16
+ <a href="https://aport.io">Website</a> โ€ข
17
+ <a href="https://aport.io/docs">Docs</a> โ€ข
18
+ <a href="https://aport.io/brand-mascot-agent/">Meet Porter</a> โ€ข
19
+ <a href="#-quick-start">Quick Start</a> โ€ข
20
+ <a href="SECURITY.md">Security</a>
21
+ </p>
22
+
23
+ </div>
24
+
25
+ ---
26
+
27
+ ## The problem: OpenClaw skills can exfiltrate data without you knowing
28
+
29
+ [Cisco's AI security team](https://blogs.cisco.com/ai/personal-ai-agents-like-openclaw-are-a-security-nightmare) documented that **OpenClaw skills can perform silent data exfiltration and prompt-injection attacks**โ€”third-party skills run with the same trust as the agent, so a malicious or compromised skill can read files, run commands, or call external APIs without user awareness. That finding has been amplified by [FourWeekMBA](https://fourweekmba.com/openclaws-security-nightmare-the-risk-openai-just-inherited/), [AuthMind](https://www.authmind.com/post/openclaw-malicious-skills-agentic-ai-supply-chain), [Bitsight](https://www.bitsight.com/blog), and others. Additional vulnerabilities (e.g. [CVE-2026-25253](https://www.securityweek.com/vulnerability-allows-hackers-to-hijack-openclaw-ai-assistant/) token exfiltration leading to gateway compromise) keep surfacing.
30
+
31
+ **APort Agent Guardrails is the pre-action authorization layer that blocks this before it executes.** Every tool call is checked against a **passport** (identity + capabilities + limits) in the platform's `before_tool_call` hook. The model cannot skip it; malicious or injected commands are denied before they run. See [SECURITY.md](SECURITY.md) for how we address the Cisco findings, prompt injection, and related attack vectors.
32
+
33
+ ---
34
+
35
+ ## Why pre-action authorization?
36
+
37
+ Your agent should **only do what you explicitly allow**. APort runs in the hookโ€”not in the promptโ€”so enforcement is deterministic and cannot be bypassed by prompt injection. No โ€œtrust the promptโ€. The guardrail runs in the hook; the model cannot skip it.
38
+
39
+
40
+ | | Without APort | With APort (plugin) |
41
+ |---|----------------|---------------------|
42
+ | **Enforcement** | Best-effort (prompts) | Deterministic (platform hook) |
43
+ | **Bypass risk** | High (prompt injection) | None |
44
+ | **Command control** | Agent can run anything | Allowlist + blocked patterns |
45
+ | **Audit** | Optional / ad hoc | Every decision logged |
46
+
47
+ ---
48
+
49
+ ## โœจ Features
50
+
51
+ ๐Ÿ›ก๏ธ **Pre-action checks** โ€” Policy runs *before* the tool executes; deny = tool never runs
52
+ ๐Ÿ“‹ **Passport-driven** โ€” OAP v1.0 passports define capabilities and limits (allowed commands, message caps, etc.)
53
+ ๐Ÿ”Œ **OpenClaw plugin** โ€” `before_tool_call` hook; 5-minute setup, no code changes
54
+ ๐ŸŒ **API (default) or local** โ€” APort API (recommended, full OAP) or bash evaluator (offline / no network). Both modes now have identical behavior (exec mapping fixed); messaging runs at assurance L0 by default.
55
+ ๐Ÿ”ด **Kill switch** โ€” Suspend the agent so all tool execution is denied: local file (local mode) or **Global Suspend** (hosted passport + API; see below).
56
+ โšก **Sub-100ms (API)** โ€” Python API verification is typically **~60โ€“65 ms** (mean); local evaluation sub-300ms. [Benchmarks](tests/performance/README.md)
57
+ ๐Ÿ”„ **Framework-agnostic** โ€” OpenClaw, IronClaw, PicoClaw, or any runtime that can call a script or API
58
+
59
+ **Whatโ€™s protected (out of the box):**
60
+
61
+ | Policy | What it guards |
62
+ |--------|----------------|
63
+ | **system.command.execute.v1** | Shell commands โ€” allowlist, 40+ blocked patterns (`rm -rf`, `sudo`, injection) |
64
+ | **mcp.tool.execute.v1** | MCP tool calls โ€” server allowlist, rate limits |
65
+ | **messaging.message.send.v1** | Message sends โ€” rate caps, capability checks |
66
+ | **agent.session.create.v1** / **agent.tool.register.v1** | Sessions and tool registration |
67
+
68
+ ---
69
+
70
+ ## ๐Ÿ”ด Kill switch (suspend agent) โ€” core feature
71
+
72
+ The **kill switch suspends the agent**: once active, every tool call is denied until the switch is cleared. No tool runs until you re-enable the agent. **Same standard across all frameworks** (OpenClaw, Cursor, LangChain, CrewAI, n8n): the **passport is the source of truth**; we do **not** create or read any separate file. Local = passport `status`; remote = status in registry.
73
+
74
+ | Mode | How it works | Scope |
75
+ |------|----------------|--------|
76
+ | **Local** | The **passport is the source of truth**. Set passport `status` to `suspended` (e.g. edit `passport.json` or `jq '.status = "suspended"' passport.json`). The guardrail checks passport status first and denies with `oap.passport_suspended`. Set back to `active` to resume. No separate file. | Single machine / config dir only. |
77
+ | **Global Suspend** | Use a **hosted passport** (agent_id) with API mode. **Log in** to [APort](https://aport.io) and suspend the passport. Per the [Open Agent Passport (OAP) spec](external/aport-spec/oap/oap-spec.md): when a passport is suspended or revoked, validators MUST treat cached decisions as invalid within **โ‰ค30 seconds** globally. | **All systems** using that passport deny within &lt;200ms. |
78
+
79
+ **Remote passports and Global Suspend (pro):** When the same passport is used **across multiple systems** (e.g. team agents, many machines), a **hosted passport** plus API mode lets you **log in once** and suspend that passport in the registry. Every agent using that passport then stops authorizing tool calls within 200ms, per OAP suspend/revoke semantics. That's **Global Suspend** โ€” a major advantage when one passport is shared. See [OAP spec: Suspend/Revoke Semantics](external/aport-spec/oap/oap-spec.md) and [Hosted passport setup](docs/HOSTED_PASSPORT_SETUP.md).
80
+
81
+ ---
82
+
83
+ ## ๐Ÿ”Œ Supported frameworks
84
+
85
+ **APort Agent Guardrail** adapters are available per framework; the same passport and policies apply. **Node users:** `npx @aporthq/aport-agent-guardrails` (then choose framework) or `npx @aporthq/aport-agent-guardrails <framework>`. **Python users (LangChain/CrewAI):** run the same CLI for the wizard and config, then `pip install aport-agent-guardrails-langchain` or `aport-agent-guardrails-crewai` and `aport-langchain setup` / `aport-crewai setup`. **n8n:** coming soon.
86
+
87
+ **Two ways to use APort:** (1) **Guardrails (CLI/setup)** โ€” run the installer to create your passport and config; (2) **Core (library)** โ€” use the evaluator or framework callback in your app so each tool call is verified. Each framework doc ([LangChain](docs/frameworks/langchain.md), [CrewAI](docs/frameworks/crewai.md), [Cursor](docs/frameworks/cursor.md), [OpenClaw](docs/frameworks/openclaw.md)) describes both and how to use them for that framework (Python and Node where applicable).
88
+
89
+ **Production-ready today:** OpenClaw (plugin + full installer), Cursor (hooks), **Python** LangChain/CrewAI (`pip install aport-agent-guardrails-langchain` / `aport-agent-guardrails-crewai`), and **Node** (CLI + `@aporthq/aport-agent-guardrails-core`, `-langchain`, `-crewai`, `-cursor` published via CI). See [Deployment readiness](docs/DEPLOYMENT_READINESS.md).
90
+
91
+ | Framework | Doc | Integration | Install |
92
+ |-----------|-----|--------------|--------|
93
+ | **OpenClaw** | [docs/frameworks/openclaw.md](docs/frameworks/openclaw.md) | `before_tool_call` plugin | `npx @aporthq/aport-agent-guardrails openclaw` |
94
+ | **Cursor** | [docs/frameworks/cursor.md](docs/frameworks/cursor.md) | `beforeShellExecution` / `preToolUse` hooks โ†’ writes `~/.cursor/hooks.json`. **Runtime enforcement is the bash hook;** the Node package `@aporthq/aport-agent-guardrails-cursor` is a helper only (Evaluator, `getHookPath()`). | `npx @aporthq/aport-agent-guardrails cursor` |
95
+ | **LangChain / LangGraph** | [docs/frameworks/langchain.md](docs/frameworks/langchain.md) | **Python:** `APortCallback` (`on_tool_start`) | `npx @aporthq/aport-agent-guardrails langchain` then `pip install aport-agent-guardrails-langchain` + `aport-langchain setup` |
96
+ | **CrewAI** | [docs/frameworks/crewai.md](docs/frameworks/crewai.md) | **Python:** `@before_tool_call` hook, `register_aport_guardrail` | `npx @aporthq/aport-agent-guardrails crewai` then `pip install aport-agent-guardrails-crewai` + `aport-crewai setup` |
97
+ | **n8n** | [docs/frameworks/n8n.md](docs/frameworks/n8n.md) | *Coming soon* โ€” custom node and runtime in progress | โ€” |
98
+
99
+ Install via `npx @aporthq/aport-agent-guardrails <framework>` (or choose when prompted). OpenClaw can also use the full installer flow. **For LangChain and CrewAI, the Node CLI only runs the wizard and writes config; you must then run the printed `pip install` and setup commands to install the runtime adapter.** **Python** adapters on PyPI; **Node** adapters on npm (same version as the CLI).
100
+
101
+ **Passport path:** Each framework has its own **default** passport path (where that framework stores data): e.g. Cursor โ†’ `~/.cursor/aport/passport.json`, OpenClaw โ†’ `~/.openclaw/aport/passport.json`, LangChain โ†’ `~/.aport/langchain/aport/passport.json`. The passport wizardโ€™s **first question** is โ€œPassport file path [default]:โ€ โ€” press Enter for the framework default or type a different path. In non-interactive mode (e.g. CI) use **`--output /path/to/passport.json`** to choose the path. Roadmap: [docs/FRAMEWORK_ROADMAP.md](docs/FRAMEWORK_ROADMAP.md).
102
+
103
+ **Using SDKs or middleware directly:** If you prefer to integrate with the APort API from your own app (no CLI/framework installer), use the official SDKs and middleware: **Node** โ€” [@aporthq/sdk-node](https://www.npmjs.com/package/@aporthq/sdk-node), [@aporthq/middleware-express](https://www.npmjs.com/package/@aporthq/middleware-express); **Python** โ€” [aporthq-sdk-python](https://pypi.org/project/aporthq-sdk-python/), [aporthq-middleware-fastapi](https://pypi.org/project/aporthq-middleware-fastapi/). See [User Stories (Story F)](docs/launch/USER_STORIES.md#story-f) for details.
104
+
105
+ ---
106
+
107
+ ## ๐Ÿš€ Quick Start
108
+
109
+ **Prerequisites:** For the setup wizard you need **Node 18+** (or use the Python CLI below). `jq` is needed for local/bash guardrail. No clone required.
110
+
111
+ **1. Run the setup** โ€” Choose your framework when prompted (or pass it). Same wizard for everyone.
112
+
113
+ **Node (Cursor, OpenClaw, or to create config for any framework):**
114
+ ```bash
115
+ npx @aporthq/aport-agent-guardrails
116
+ # or: npx @aporthq/aport-agent-guardrails cursor | openclaw | langchain | crewai
117
+ ```
118
+
119
+ **Python (LangChain or CrewAI only):** Run the wizard via the Node command above, then install the Python adapter and run the framework setup (see the printed next steps). Or use the Python CLI to see the exact commands:
120
+ ```bash
121
+ pip install aport-agent-guardrails
122
+ aport setup --framework=langchain # prints: npx ... langchain, then pip install aport-agent-guardrails-langchain, aport-langchain setup
123
+ # or --framework=crewai for CrewAI
124
+ ```
125
+ Then run the printed `npx` command to create passport and config, and the printed `pip` / `aport-<framework> setup` to install the adapter.
126
+
127
+ This runs the **passport wizard** and writes config for your framework. Follow the **next steps** printed at the end (e.g. restart Cursor; or for LangChain/CrewAI: `pip install aport-agent-guardrails-langchain` + `aport-langchain setup`).
128
+
129
+ **2. Hosted passport (optional)** โ€” If you already have an agent_id from [aport.io](https://aport.io), use it to skip the wizard: `npx @aporthq/aport-agent-guardrails openclaw <agent_id>`. See [Hosted passport setup](docs/HOSTED_PASSPORT_SETUP.md).
130
+
131
+ **3. Test that policy runs** โ€” After setup, the guardrail runs automatically when your agent uses tools (Cursor hook, LangChain callback, OpenClaw plugin, etc.). To try allow/deny from the command line (any framework), use the installed `aport-guardrail` command (Node) or call the evaluator from Python; both use your existing passport from the framework config dir (e.g. `~/.cursor/aport/`, `~/.aport/langchain/aport/`).
132
+
133
+ **Node:**
134
+ ```bash
135
+ aport-guardrail system.command.execute '{"command":"ls"}' # ALLOW (safe)
136
+ aport-guardrail system.command.execute '{"command":"rm -rf /"}' # DENY (blocked pattern)
137
+ # Exit: 0 = ALLOW, 1 = DENY
138
+ ```
139
+ *(If you use `npx` without `-g`, run `npx aport-guardrail ...`.)*
140
+
141
+ **Python:** Use the guardrail in your app (e.g. add `APortCallback()` to your LangChain agent, or `register_aport_guardrail()` for CrewAI). The guardrail runs on every tool call. To test allow/deny from the shell without Node, use `npx aport-guardrail ...` as above, or see your framework doc for in-app testing.
142
+
143
+ **Check passport status and audit:**
144
+
145
+ | What | Where |
146
+ |------|--------|
147
+ | **Passport & audit** | Stored in your **framework config dir** (e.g. `~/.cursor/aport/`, `~/.openclaw/aport/`, `~/.aport/langchain/aport/`). Same for all frameworks. |
148
+ | **Audit log** | `config_dir/aport/audit.log` โ€” one line per decision (timestamp, tool, allow/deny, policy, context). |
149
+ | **Last decision** | `config_dir/aport/decision.json` (OAP v1.0 format). |
150
+
151
+ Your framework doc (Cursor, OpenClaw, LangChain, CrewAI) describes where the config dir is and any framework-specific status commands.
152
+
153
+ ๐Ÿ“– **Per-framework:** [OpenClaw](docs/frameworks/openclaw.md) ยท [Cursor](docs/frameworks/cursor.md) ยท [LangChain](docs/frameworks/langchain.md) ยท [CrewAI](docs/frameworks/crewai.md)
154
+ ๐ŸŒ **Hosted passport:** [Use agent_id from aport.io](docs/HOSTED_PASSPORT_SETUP.md)
155
+
156
+ ---
157
+
158
+ ## ๐Ÿ”’ Enforcement Options
159
+
160
+ | | OpenClaw Plugin โœ… | AGENTS.md only โš ๏ธ |
161
+ |---|-------------------|-------------------|
162
+ | **Deterministic** | Yes | No |
163
+ | **Bypass risk** | None | High |
164
+ | **Recommended** | **Yes** | Only if plugin unavailable |
165
+
166
+ **Plugin (recommended):** Platform runs the guardrail before every tool; the model cannot skip it. This repo implements the **plugin (before_tool_call)** integrationโ€”Option 2 in the [APort ร— OpenClaw integration proposal](https://github.com/aporthq/agent-passport/tree/main/_plan/execution/openclaw).
167
+ **AGENTS.md:** Agent is *instructed* to call the guardrail; best-effort only.
168
+
169
+ ---
170
+
171
+ ## ๐Ÿ”Œ Verification methods (local vs API)
172
+
173
+ **Default and recommended:** **API mode** โ€” full OAP policy evaluation (JSON Schema, assurance, regions, evaluation rules from policy JSON, signed decisions). The setup wizard defaults to API when you choose a mode.
174
+
175
+ **Fail-closed by default:** If the evaluator cannot find a passport or guardrail script (e.g. first run, wrong config dir), it **denies** the tool call (`oap.misconfigured`). For legacy allow-when-missing behavior, set `fail_open_when_missing_config: true` in your config or `APORT_FAIL_OPEN_WHEN_MISSING_CONFIG=1` in the environment.
176
+
177
+ | Mode | Best for | Full OAP? | Network |
178
+ |------|----------|-----------|---------|
179
+ | **API (default)** | Production, full policy parity, new policy packs without code changes | โœ… | Yes (api.aport.io or self-hosted) |
180
+ | **Local (bash)** | Privacy, offline, air-gapped | Subset only (hand-coded limits for exec, messaging, repo) | No |
181
+
182
+ **API mode** can use either a **local passport file** (sent in the request body; not stored) or **agent_id only**: set `APORT_AGENT_ID` to your hosted passportโ€™s agent ID and the API fetches the passport from the registry โ€” no passport JSON file needed. See [Hosted passport setup](docs/HOSTED_PASSPORT_SETUP.md).
183
+
184
+ Deep dive (what each supports, comparison table): [Verification methods](docs/VERIFICATION_METHODS.md).
185
+
186
+ ---
187
+
188
+ ## โšก Performance
189
+
190
+ Guardrail verification latency (real API + local). Python is typically fastest for API mode. Below: **Python only** (n=30, warmup=10, real APort API).
191
+
192
+ | Method | Identity | Policy | Mean (ms) | p50 | p95 | p99 | N |
193
+ |--------|----------|--------|-----------|-----|-----|-----|---|
194
+ | API | agent_id (cloud) | pack in path | 63.42 | 63.16 | 70.31 | 70.65 | 30 |
195
+ | API | agent_id (cloud) | policy in body | 62.62 | 62.37 | 69.58 | 72.22 | 30 |
196
+ | API | passport in body | pack in path | 63.03 | 63.12 | 67.48 | 71.45 | 30 |
197
+ | API | passport in body | policy in body | 61.60 | 62.11 | 66.90 | 70.39 | 30 |
198
+ | Local | passport file | pack in path | 120.27 | 117.85 | 131.62 | 145.38 | 30ยฒ |
199
+
200
+ ยฒ Policy in body not yet for local evaluation.
201
+
202
+ ---
203
+
204
+ ## ๐Ÿ“ How It Works
205
+
206
+ <div align="center">
207
+
208
+ ```mermaid
209
+ %%{init: {'theme':'base', 'themeVariables': {'primaryColor':'#f0f9ff','primaryTextColor':'#0c4a6e','primaryBorderColor':'#0284c7','lineColor':'#0369a1','secondaryColor':'#e0f2fe','tertiaryColor':'#bae6fd'}}}%%
210
+ sequenceDiagram
211
+ autonumber
212
+ participant User as ๐Ÿ‘ค User
213
+ participant OC as ๐Ÿฆ€ OpenClaw
214
+ participant Hook as ๐Ÿ”’ before_tool_call
215
+ participant Plugin as ๐Ÿ›ก๏ธ APort Plugin
216
+ participant Guard as ๐Ÿ“‹ Guardrail
217
+
218
+ User->>OC: "Run: rm -rf /tmp"
219
+ activate OC
220
+ OC->>Hook: tool call (exec.run, params)
221
+ activate Hook
222
+ Hook->>Plugin: before_tool_call(exec.run, params)
223
+ activate Plugin
224
+ Note over Plugin: Map tool โ†’ policy<br/>exec.run โ†’ system.command.execute.v1
225
+ Plugin->>Guard: evaluate(passport, policy, context)
226
+ activate Guard
227
+ Note over Guard: API or local script<br/>passport + limits
228
+ Guard-->>Plugin: DENY (blocked pattern)
229
+ deactivate Guard
230
+ Plugin-->>Hook: block: true, blockReason
231
+ deactivate Plugin
232
+ Hook-->>OC: Tool blocked
233
+ deactivate Hook
234
+ OC-->>User: โŒ Action blocked by policy
235
+ deactivate OC
236
+ ```
237
+
238
+ **Flow (high level):**
239
+
240
+ ```mermaid
241
+ %%{init: {'theme':'base', 'themeVariables': {'primaryColor':'#f0f9ff','primaryTextColor':'#0c4a6e','primaryBorderColor':'#0284c7','lineColor':'#0369a1'}}}%%
242
+ flowchart TB
243
+ subgraph User["๐Ÿ‘ค User"]
244
+ A[User request]
245
+ end
246
+ B[๐Ÿฆ€ OpenClaw: tool call]
247
+ C[๐Ÿ”’ before_tool_call hook]
248
+ D[๐Ÿ›ก๏ธ APort plugin]
249
+ E[๐Ÿ“‹ Guardrail: passport + policy]
250
+ F{Decision}
251
+ G[โœ… ALLOW โ€” tool runs]
252
+ H[โŒ DENY โ€” tool blocked]
253
+ A --> B --> C --> D --> E --> F
254
+ F --> G
255
+ F --> H
256
+ style A fill:#0277bd,stroke:#01579b,stroke-width:2px,color:#fff
257
+ style B fill:#1565c0,stroke:#0d47a1,stroke-width:2px,color:#fff
258
+ style C fill:#0288d1,stroke:#01579b,stroke-width:2px,color:#fff
259
+ style D fill:#ff6f00,stroke:#bf360c,stroke-width:3px,color:#fff
260
+ style E fill:#ff6f00,stroke:#bf360c,stroke-width:2px,color:#fff
261
+ style F fill:#7b1fa2,stroke:#4a148c,stroke-width:2px,color:#fff
262
+ style G fill:#388e3c,stroke:#1b5e20,stroke-width:2px,color:#fff
263
+ style H fill:#c62828,stroke:#b71c1c,stroke-width:2px,color:#fff
264
+ ```
265
+
266
+ </div>
267
+
268
+ ```
269
+ User โ†’ "Delete all log files"
270
+ โ†“
271
+ OpenClaw: tool "exec.run"
272
+ โ†“
273
+ ๐Ÿ”’ before_tool_call hook
274
+ โ†“
275
+ ๐Ÿ›ก๏ธ APort plugin โ†’ guardrail (passport + policy)
276
+ โ†“
277
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
278
+ โœ… ALLOW โŒ DENY
279
+ Tool runs Tool blocked
280
+ ```
281
+
282
+ **Key:** The platform enforces policy. The AI cannot skip this check.
283
+
284
+ ---
285
+
286
+ ## ๐Ÿ›๏ธ Security model (three layers)
287
+
288
+ APort enforces **identity โ†’ authorization โ†’ audit** before any tool runs. This repo implements the **plugin (Option 2)** integration: OpenClaw calls the APort extension in `before_tool_call`; the extension uses either local script or API to evaluate policy.
289
+
290
+ <div align="center">
291
+
292
+ ```mermaid
293
+ %%{init: {'theme':'base', 'themeVariables': {'primaryColor':'#f0f9ff','primaryTextColor':'#0c4a6e','primaryBorderColor':'#0284c7','lineColor':'#0369a1'}}}%%
294
+ graph TB
295
+ subgraph L1["Layer 1: Identity (Who)"]
296
+ A[Agent Passport<br/>OAP v1.0 / W3C DID]
297
+ B[Owner, contact, org]
298
+ C[Assurance level L0โ€“L3]
299
+ end
300
+
301
+ subgraph L2["Layer 2: Authorization (What)"]
302
+ D[Policy packs<br/>code.*, data.*, messaging.*]
303
+ E[Graduated controls<br/>Max amounts, daily caps]
304
+ F[Context-aware rules<br/>Branch allowlist, PII filters]
305
+ end
306
+
307
+ subgraph L3["Layer 3: Audit (Proof)"]
308
+ G[Decision receipts<br/>Ed25519 in API mode]
309
+ H[Audit trail<br/>Allow/deny logged]
310
+ I[Kill switch<br/>Local file or global via API]
311
+ end
312
+
313
+ A --> D
314
+ B --> D
315
+ C --> D
316
+ D --> G
317
+ E --> G
318
+ F --> G
319
+ G --> H
320
+ H --> I
321
+
322
+ style A fill:#0277bd,stroke:#01579b,stroke-width:2px,color:#fff
323
+ style D fill:#ff6f00,stroke:#bf360c,stroke-width:2px,color:#fff
324
+ style G fill:#6a1b9a,stroke:#4a148c,stroke-width:2px,color:#fff
325
+ ```
326
+
327
+ </div>
328
+
329
+ - **Local-first:** Passport and policy live on your machine (or in repo); no cloud required for basic enforcement.
330
+ - **Fail-closed:** Missing or invalid passport โ†’ deny.
331
+ - **Opt-in cloud:** Use API mode for global kill switch, signed receipts, and team sync.
332
+
333
+ ---
334
+
335
+ ## ๐ŸŒ When to use API vs local
336
+
337
+ | Use **local** when | Use **API** (default) when |
338
+ |--------------------|----------------------------|
339
+ | Single developer, no cloud | Team; same policies across machines |
340
+ | Offline or air-gapped | You want global kill switch (&lt;15s) |
341
+ | Plain audit logs are enough | You need signed receipts (e.g. SOC 2, compliance) |
342
+ | No API key / self-host not ready | Registry checks, analytics, or policy marketplace |
343
+
344
+ See [Verification methods](docs/VERIFICATION_METHODS.md) for a detailed comparison.
345
+
346
+ ---
347
+
348
+ ## ๐Ÿ“– Example outcomes
349
+
350
+ | Scenario | Without APort | With APort |
351
+ |----------|----------------|---------------------------|
352
+ | **Oversized PR** | Agent creates 1200-file PR | Denied: โ€œPR size exceeds limit of 500 filesโ€ |
353
+ | **PII export** | Agent exports SSN/driverโ€™s license | Denied: โ€œPII export not allowedโ€ (data.export policy) |
354
+ | **Kill switch** | Manually edit config on every machine | API: suspend passport once โ†’ all agents deny in &lt;15s |
355
+
356
+ ---
357
+
358
+ ## ๐Ÿ“œ Commands (after install)
359
+
360
+ **Node:** When you install via `npm install @aporthq/aport-agent-guardrails` (or use `npx`), these commands are available:
361
+
362
+ | Command | Purpose |
363
+ |--------|---------|
364
+ | `agent-guardrails` | Main entry โ€” prompt for framework or pass one: `agent-guardrails cursor \| openclaw \| langchain \| crewai`. Args after the framework are passed through (e.g. `agent-guardrails openclaw <agent_id>`). |
365
+ | `aport` | OpenClaw one-command setup (passport + plugin + wrappers). Optional: `aport <agent_id>` for hosted passport. |
366
+ | `aport-guardrail` | Run guardrail check from the CLI (e.g. `aport-guardrail system.command.execute '{"command":"ls"}'`). Uses passport from your framework config dir. |
367
+
368
+ **Python:** After `pip install aport-agent-guardrails` you get `aport` (setup helper). For LangChain or CrewAI, install the adapter and framework setup:
369
+
370
+ | Command | Purpose |
371
+ |--------|---------|
372
+ | `aport setup --framework=langchain` | Print next-step commands (npx wizard, then `pip install aport-agent-guardrails-langchain`, `aport-langchain setup`). |
373
+ | `aport setup --framework=crewai` | Same for CrewAI: npx wizard, then `pip install aport-agent-guardrails-crewai`, `aport-crewai setup`. |
374
+ | `aport-langchain setup` | LangChain config and wizard (after installing `aport-agent-guardrails-langchain`). |
375
+ | `aport-crewai setup` | CrewAI config and wizard (after installing `aport-agent-guardrails-crewai`). |
376
+
377
+ Use the framework-specific doc for where config and passport live and for any extra steps (e.g. Cursor: restart IDE; LangChain/CrewAI: add callback/hook in code).
378
+
379
+ *Contributors: repo layout and dev scripts (build, test, release) are in [docs/REPO_LAYOUT.md](docs/REPO_LAYOUT.md) and [CONTRIBUTING.md](CONTRIBUTING.md).*
380
+
381
+ ---
382
+
383
+ ## ๐Ÿ“š Documentation
384
+
385
+ | Doc | Description |
386
+ |-----|-------------|
387
+ | [QuickStart: OpenClaw Plugin](docs/QUICKSTART_OPENCLAW_PLUGIN.md) | 5-minute OpenClaw setup |
388
+ | [Hosted passport setup](docs/HOSTED_PASSPORT_SETUP.md) | Use passport from aport.io โ€” `npx ... <agent_id>` or choose hosted in wizard |
389
+ | [Verification methods (local vs API)](docs/VERIFICATION_METHODS.md) | Deep dive: bash vs API evaluator |
390
+ | [Quick Start Guide](docs/QUICKSTART.md) | Passport wizard, copy-paste option |
391
+ | [OpenClaw Local Integration](docs/OPENCLAW_LOCAL_INTEGRATION.md) | API, Python example |
392
+ | [Tool / Policy Mapping](docs/TOOL_POLICY_MAPPING.md) | Tool names โ†’ policy packs |
393
+ | [Repo Layout](docs/REPO_LAYOUT.md) | For contributors: package layout (`bin/`, `src/`, `extensions/`) |
394
+ | [Upgrade Guide](docs/UPGRADE.md) | Migrating between versions (e.g. 0.1.0 โ†’ 1.0.0) |
395
+ | **Frameworks** | Per-framework setup and how guardrails run |
396
+ | โ†’ [OpenClaw](docs/frameworks/openclaw.md) | `before_tool_call` plugin |
397
+ | โ†’ [Cursor](docs/frameworks/cursor.md) | beforeShellExecution / preToolUse hooks, `~/.cursor/hooks.json` |
398
+ | โ†’ [LangChain / LangGraph](docs/frameworks/langchain.md) | `APortCallback` handler |
399
+ | โ†’ [CrewAI](docs/frameworks/crewai.md) | `@before_tool_call` hook, `register_aport_guardrail` |
400
+ | โ†’ [n8n](docs/frameworks/n8n.md) | Custom node, branch on allow/deny |
401
+ | [Framework support plan](docs/launch/FRAMEWORK_SUPPORT_PLAN.md) | Strategy, rankings, roadmap |
402
+
403
+ ---
404
+
405
+ ## ๐Ÿ—๏ธ Architecture
406
+
407
+ <div align="center">
408
+
409
+ ```mermaid
410
+ %%{init: {'theme':'base', 'themeVariables': {'primaryColor':'#f0f9ff','primaryTextColor':'#0c4a6e','primaryBorderColor':'#0284c7','lineColor':'#0369a1','secondaryColor':'#e0f2fe','tertiaryColor':'#bae6fd'}}}%%
411
+ flowchart LR
412
+ subgraph Runtime["Runtime"]
413
+ OC[๐Ÿฆ€ OpenClaw / IronClaw]
414
+ S[Sandbox, channels, tools]
415
+ OC --> S
416
+ end
417
+ subgraph Policy["Pre-action policy"]
418
+ AP[๐Ÿ›ก๏ธ APort Guardrails]
419
+ P[Passport, limits, audit]
420
+ AP --> P
421
+ end
422
+ Runtime <-->|before every tool| Policy
423
+ style OC fill:#1565c0,stroke:#0d47a1,stroke-width:2px,color:#fff
424
+ style S fill:#6a1b9a,stroke:#4a148c,stroke-width:1px,color:#fff
425
+ style AP fill:#ff6f00,stroke:#bf360c,stroke-width:3px,color:#fff
426
+ style P fill:#ff6f00,stroke:#bf360c,stroke-width:1px,color:#fff
427
+ ```
428
+
429
+ **Where verification runs (this repo):**
430
+
431
+ ```mermaid
432
+ %%{init: {'theme':'base', 'themeVariables': {'primaryColor':'#f0f9ff','primaryTextColor':'#0c4a6e','primaryBorderColor':'#0284c7','lineColor':'#0369a1'}}}%%
433
+ flowchart TB
434
+ subgraph Machine["Your machine"]
435
+ OC[๐Ÿฆ€ OpenClaw]
436
+ Plug[๐Ÿ›ก๏ธ APort plugin<br/>before_tool_call]
437
+ Guard[๐Ÿ“‹ Guardrail]
438
+ OC --> Plug
439
+ Plug --> Guard
440
+ end
441
+ Guard -->|API mode| API[๐Ÿ“ก api.aport.io<br/>or self-hosted]
442
+ Guard -->|Local mode| Bash[๐Ÿ“œ aport-guardrail-bash.sh]
443
+ style OC fill:#1565c0,stroke:#0d47a1,stroke-width:2px,color:#fff
444
+ style Plug fill:#ff6f00,stroke:#bf360c,stroke-width:3px,color:#fff
445
+ style Guard fill:#ff6f00,stroke:#bf360c,stroke-width:2px,color:#fff
446
+ style API fill:#2e7d32,stroke:#1b5e20,stroke-width:1px,color:#fff
447
+ style Bash fill:#6b7280,stroke:#374151,stroke-width:1px,color:#fff
448
+ ```
449
+
450
+ </div>
451
+
452
+ - **OpenClaw** = Runtime (sandbox, channels, tools).
453
+ - **APort plugin** = Pre-action hook; calls guardrail (API or local script).
454
+ - **Guardrail** = Passport + policy evaluation; allow/deny before the tool runs.
455
+
456
+ Defense in depth: policy *before* execution, runtime safety *during* execution.
457
+
458
+ ---
459
+
460
+ ## ๐Ÿค Contributing
461
+
462
+ Contributions welcome: policy packs, framework adapters, docs. See [CONTRIBUTING.md](CONTRIBUTING.md).
463
+
464
+ ---
465
+
466
+ ## ๐Ÿ“„ License
467
+
468
+ Apache 2.0 โ€” see [LICENSE](LICENSE).
469
+
470
+ **Open-core:** Local evaluation and CLI in this repo are open source (Apache 2.0). [api.aport.io](https://api.aport.io) is a separate product for cloud features (signed receipts, global kill switch, team sync). See [APort ร— OpenClaw proposal](https://github.com/aporthq/agent-passport/tree/main/_plan/execution/openclaw) for free vs. paid tiers.
471
+
472
+ ---
473
+
474
+ ## ๐Ÿ”— Links
475
+
476
+ - [npm package](https://www.npmjs.com/package/@aporthq/aport-agent-guardrails) ยท [APort](https://aport.io) ยท [Docs](https://aport.io/docs)
477
+ - [GitHub Issues](https://github.com/aporthq/aport-agent-guardrails/issues) ยท [Discussions](https://github.com/aporthq/aport-agent-guardrails/discussions)
478
+
479
+ ---
480
+
481
+ <p align="center">Made with โค๏ธ by [Uchi](https://github.com/uchibeke/) </p>
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env bash
2
+ # Main dispatcher: detect or prompt for framework, then run framework setup.
3
+ # Usage:
4
+ # agent-guardrails # detect from cwd or prompt
5
+ # agent-guardrails openclaw # explicit framework
6
+ # agent-guardrails openclaw ap_xxx # openclaw with agent_id (pass-through)
7
+ # agent-guardrails --framework=langchain
8
+ # agent-guardrails -f crewai
9
+ # Supported: openclaw | langchain | crewai | cursor (n8n coming soon)
10
+ #
11
+ # Backward compatibility: All arguments after the framework are passed through to
12
+ # the framework script. For OpenClaw, bin/openclaw receives them (e.g. agent_id).
13
+ # The "aport" bin still points at bin/openclaw directly for existing scripts.
14
+
15
+ set -euo pipefail
16
+
17
+ # Resolve script path so npx (node_modules/.bin/agent-guardrails -> package/bin/agent-guardrails) works
18
+ SCRIPT_PATH="${BASH_SOURCE[0]:-.}"
19
+ while [[ -L "$SCRIPT_PATH" ]]; do
20
+ TARGET="$(readlink "$SCRIPT_PATH")"
21
+ if [[ "$TARGET" == /* ]]; then
22
+ SCRIPT_PATH="$TARGET"
23
+ else
24
+ SCRIPT_PATH="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)/$TARGET"
25
+ fi
26
+ done
27
+ SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)"
28
+ ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
29
+ FRAMEWORKS_DIR="$SCRIPT_DIR/frameworks"
30
+ LIB_DIR="$SCRIPT_DIR/lib"
31
+
32
+ # Parse --framework= and -f (skip detection when set)
33
+ framework=""
34
+ REST=()
35
+ while [[ $# -gt 0 ]]; do
36
+ case "$1" in
37
+ --framework=*)
38
+ framework="${1#--framework=}"
39
+ shift
40
+ ;;
41
+ -f)
42
+ if [[ $# -gt 1 ]]; then
43
+ framework="$2"
44
+ shift 2
45
+ else
46
+ echo "[aport] ERROR: -f requires a value (e.g. -f openclaw)" >&2
47
+ exit 1
48
+ fi
49
+ ;;
50
+ *)
51
+ REST+=("$1")
52
+ shift
53
+ ;;
54
+ esac
55
+ done
56
+
57
+ # If no framework from args, try APORT_FRAMEWORK (non-interactive) or detection
58
+ if [[ -z "$framework" ]]; then
59
+ if [[ -n "${APORT_FRAMEWORK:-}" ]]; then
60
+ framework="$APORT_FRAMEWORK"
61
+ else
62
+ project_dir="${APORT_PROJECT_DIR:-$PWD}"
63
+ if [[ -f "$LIB_DIR/detect.sh" ]]; then
64
+ # shellcheck source=./lib/detect.sh
65
+ source "$LIB_DIR/detect.sh"
66
+ detected_list="$(detect_frameworks_list "$project_dir")"
67
+ framework="$(detect_framework "$project_dir")"
68
+ # Multiple detected: show conflict and all options
69
+ if [[ -n "$detected_list" ]]; then
70
+ count=$(echo "$detected_list" | wc -w)
71
+ if [[ "$count" -gt 1 ]]; then
72
+ noninteractive="${APORT_NONINTERACTIVE:-${CI:-}}"
73
+ if [[ -n "$noninteractive" ]]; then
74
+ echo "[aport] ERROR: Multiple frameworks detected: $detected_list" >&2
75
+ echo " Set APORT_FRAMEWORK or use --framework=openclaw | langchain | crewai | cursor" >&2
76
+ exit 1
77
+ fi
78
+ echo ""
79
+ echo " APort Agent Guardrails โ€” Framework setup"
80
+ echo " โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€"
81
+ echo " Multiple frameworks detected: $detected_list"
82
+ echo " Choose one: openclaw | langchain | crewai | cursor"
83
+ echo " Example: npx @aporthq/agent-guardrails --framework=openclaw"
84
+ echo ""
85
+ read -p " Framework [${framework:-openclaw}]: " choice
86
+ framework="${choice:-${framework:-openclaw}}"
87
+ fi
88
+ fi
89
+ fi
90
+ fi
91
+ fi
92
+
93
+ # If still empty, prompt or exit (non-interactive)
94
+ if [[ -z "$framework" ]]; then
95
+ noninteractive="${APORT_NONINTERACTIVE:-${CI:-}}"
96
+ if [[ -n "$noninteractive" ]]; then
97
+ echo "[aport] ERROR: No framework detected in current directory." >&2
98
+ echo " Set APORT_FRAMEWORK or use --framework=openclaw | langchain | crewai | cursor" >&2
99
+ exit 1
100
+ fi
101
+ echo ""
102
+ echo " APort Agent Guardrails โ€” Framework setup"
103
+ echo " โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€"
104
+ echo " No framework detected in current directory."
105
+ echo " Choose: openclaw | langchain | crewai | cursor"
106
+ echo " Example: npx @aporthq/agent-guardrails openclaw"
107
+ echo " npx @aporthq/agent-guardrails --framework=langchain"
108
+ echo ""
109
+ read -p " Framework [openclaw]: " framework
110
+ framework="${framework:-openclaw}"
111
+ fi
112
+
113
+ framework="$(echo "$framework" | tr '[:upper:]' '[:lower:]')"
114
+ script="$FRAMEWORKS_DIR/${framework}.sh"
115
+
116
+ # n8n: custom node not yet available; warn before running wizard-only script
117
+ if [[ "$framework" == "n8n" ]]; then
118
+ echo "[aport] Note: n8n custom node is not yet available. This setup only runs the passport wizard and writes config." >&2
119
+ fi
120
+
121
+ # Run framework script if it exists and is executable (safe with empty REST)
122
+ if [[ -x "$script" ]]; then
123
+ exec "$script" ${REST+"${REST[@]}"}
124
+ fi
125
+
126
+ # Fallback: OpenClaw uses the full installer (wizard + config + plugin + next steps)
127
+ if [[ "$framework" == "openclaw" ]]; then
128
+ exec "$SCRIPT_DIR/openclaw" ${REST+"${REST[@]}"}
129
+ fi
130
+
131
+ echo "[aport] ERROR: Unknown or unsupported framework: $framework" >&2
132
+ echo " Supported: openclaw, langchain, crewai, cursor (n8n coming soon)" >&2
133
+ exit 1