@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.
- package/LICENSE +217 -0
- package/README.md +481 -0
- package/bin/agent-guardrails +133 -0
- package/bin/aport-create-passport.sh +444 -0
- package/bin/aport-cursor-hook.sh +90 -0
- package/bin/aport-guardrail-api.sh +108 -0
- package/bin/aport-guardrail-bash.sh +394 -0
- package/bin/aport-guardrail-v2.sh +5 -0
- package/bin/aport-guardrail.sh +5 -0
- package/bin/aport-resolve-paths.sh +71 -0
- package/bin/aport-status.sh +276 -0
- package/bin/frameworks/crewai.sh +49 -0
- package/bin/frameworks/cursor.sh +95 -0
- package/bin/frameworks/langchain.sh +48 -0
- package/bin/frameworks/n8n.sh +36 -0
- package/bin/frameworks/openclaw.sh +19 -0
- package/bin/lib/allowlist.sh +18 -0
- package/bin/lib/common.sh +28 -0
- package/bin/lib/config.sh +46 -0
- package/bin/lib/constants.sh +232 -0
- package/bin/lib/detect.sh +65 -0
- package/bin/lib/error.sh +269 -0
- package/bin/lib/passport.sh +19 -0
- package/bin/lib/templates/.gitkeep +1 -0
- package/bin/lib/templates/config.yaml +6 -0
- package/bin/lib/validation.sh +206 -0
- package/bin/openclaw +660 -0
- package/docs/ADDING_A_FRAMEWORK.md +87 -0
- package/docs/AGENTS.md.example +40 -0
- package/docs/CODE_REVIEW.md +192 -0
- package/docs/DEPLOYMENT_READINESS.md +81 -0
- package/docs/FAQ_SECURITY_SCANNERS.md +373 -0
- package/docs/FRAMEWORK_ROADMAP.md +41 -0
- package/docs/HOSTED_PASSPORT_SETUP.md +362 -0
- package/docs/IMPLEMENTING_YOUR_OWN_EVALUATOR.md +433 -0
- package/docs/OPENCLAW_COMPATIBILITY.md +73 -0
- package/docs/OPENCLAW_LOCAL_INTEGRATION.md +596 -0
- package/docs/OPENCLAW_TOOLS_AND_POLICIES.md +54 -0
- package/docs/QUICKSTART.md +470 -0
- package/docs/QUICKSTART_OPENCLAW_PLUGIN.md +470 -0
- package/docs/README.md +28 -0
- package/docs/RELEASE.md +87 -0
- package/docs/REPO_LAYOUT.md +47 -0
- package/docs/SKILLS_ECOSYSTEM_ANALYSIS_FEB17.md +1260 -0
- package/docs/TOOL_POLICY_MAPPING.md +46 -0
- package/docs/UPGRADE.md +46 -0
- package/docs/VERIFICATION_METHODS.md +97 -0
- package/docs/assets/README.md +8 -0
- package/docs/assets/porter.svg +54 -0
- package/docs/development/ERROR_CODES.md +616 -0
- package/docs/frameworks/GITHUB_ISSUE_PROPOSALS.md +1105 -0
- package/docs/frameworks/crewai.md +114 -0
- package/docs/frameworks/cursor.md +159 -0
- package/docs/frameworks/langchain.md +72 -0
- package/docs/frameworks/n8n.md +40 -0
- package/docs/frameworks/openclaw.md +40 -0
- package/docs/launch/ADD_APORT_AWESOME_LISTS_INSTRUCTIONS.md +146 -0
- package/docs/launch/ANNOUNCEMENT_GUIDE.md +266 -0
- package/docs/launch/AWESOME_REPOS.md +53 -0
- package/docs/launch/CURSOR_VSCODE_HOOKS_RESEARCH.md +77 -0
- package/docs/launch/DEMO_TERMINAL_OUTPUT.txt +48 -0
- package/docs/launch/DRY_AND_PLAN_CHECKLIST.md +47 -0
- package/docs/launch/EVIDENCE_README.md +61 -0
- package/docs/launch/EVIDENCE_TERMINAL_CAPTURE.txt +10 -0
- package/docs/launch/FRAMEWORK_SUPPORT_PLAN.md +1640 -0
- package/docs/launch/LAUNCH_READINESS_CHECKLIST.md +237 -0
- package/docs/launch/LAUNCH_STRATEGY_SUMMARY.md +464 -0
- package/docs/launch/OPENCLAW_FEEDBACK_AND_FIXES.md +85 -0
- package/docs/launch/POST_1_VALENTINE_IMPROVED.md +233 -0
- package/docs/launch/POST_2_GUARDRAIL_IMPROVED.md +369 -0
- package/docs/launch/PRE_LAUNCH_FIXES.md +766 -0
- package/docs/launch/QUICK_LAUNCH_CHECKLIST.md +400 -0
- package/docs/launch/READINESS_SUMMARY.md +262 -0
- package/docs/launch/README.md +68 -0
- package/docs/launch/USER_STORIES.md +327 -0
- package/docs/launch/scripts/add-aport-awesome-pr.sh +69 -0
- package/docs/operations/MONITORING.md +588 -0
- package/docs/reviews/2026-02-18-staff-review.md +268 -0
- package/extensions/openclaw-aport/README.md +415 -0
- package/extensions/openclaw-aport/index.js +625 -0
- package/extensions/openclaw-aport/openclaw-aport.js +7 -0
- package/extensions/openclaw-aport/openclaw.plugin.json +46 -0
- package/extensions/openclaw-aport/package.json +36 -0
- package/extensions/openclaw-aport/test.js +307 -0
- package/external/aport-policies/README.md +363 -0
- package/external/aport-policies/agent.session.create.v1/README.md +345 -0
- package/external/aport-policies/agent.session.create.v1/policy.json +162 -0
- package/external/aport-policies/agent.tool.register.v1/README.md +361 -0
- package/external/aport-policies/agent.tool.register.v1/policy.json +172 -0
- package/external/aport-policies/code.release.publish.v1/README.md +51 -0
- package/external/aport-policies/code.release.publish.v1/policy.json +121 -0
- package/external/aport-policies/code.repository.merge.v1/README.md +287 -0
- package/external/aport-policies/code.repository.merge.v1/express.example.js +332 -0
- package/external/aport-policies/code.repository.merge.v1/fastapi.example.py +370 -0
- package/external/aport-policies/code.repository.merge.v1/policy.json +162 -0
- package/external/aport-policies/data.export.create.v1/README.md +226 -0
- package/external/aport-policies/data.export.create.v1/express.example.js +172 -0
- package/external/aport-policies/data.export.create.v1/fastapi.example.py +165 -0
- package/external/aport-policies/data.export.create.v1/policy.json +133 -0
- package/external/aport-policies/data.report.ingest.v1/README.md +134 -0
- package/external/aport-policies/data.report.ingest.v1/express.example.js +105 -0
- package/external/aport-policies/data.report.ingest.v1/minimal-example.js +68 -0
- package/external/aport-policies/data.report.ingest.v1/policy.json +174 -0
- package/external/aport-policies/finance.crypto.trade.v1/README.md +146 -0
- package/external/aport-policies/finance.crypto.trade.v1/express.example.js +109 -0
- package/external/aport-policies/finance.crypto.trade.v1/minimal-example.js +65 -0
- package/external/aport-policies/finance.crypto.trade.v1/policy.json +176 -0
- package/external/aport-policies/finance.payment.charge.v1/README.md +326 -0
- package/external/aport-policies/finance.payment.charge.v1/express.example.js +250 -0
- package/external/aport-policies/finance.payment.charge.v1/fastapi.example.py +227 -0
- package/external/aport-policies/finance.payment.charge.v1/minimal-example.js +64 -0
- package/external/aport-policies/finance.payment.charge.v1/policy.json +224 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/contexts.jsonl +12 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/expected.jsonl +12 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/passport.instance.json +42 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/passport.template.json +40 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/payments-charge-policy.test.js +817 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/test_payments_charge_policy.py +486 -0
- package/external/aport-policies/finance.payment.payout.v1/README.md +78 -0
- package/external/aport-policies/finance.payment.payout.v1/policy.json +181 -0
- package/external/aport-policies/finance.payment.refund.v1/README.md +275 -0
- package/external/aport-policies/finance.payment.refund.v1/express.example.js +167 -0
- package/external/aport-policies/finance.payment.refund.v1/fastapi.example.py +136 -0
- package/external/aport-policies/finance.payment.refund.v1/minimal-example.js +183 -0
- package/external/aport-policies/finance.payment.refund.v1/policy.json +216 -0
- package/external/aport-policies/finance.payment.refund.v1/tests/refunds-policy.test.js +924 -0
- package/external/aport-policies/finance.payment.refund.v1/tests/test_refunds_policy.py +778 -0
- package/external/aport-policies/finance.transaction.execute.v1/README.md +309 -0
- package/external/aport-policies/finance.transaction.execute.v1/express.example.js +261 -0
- package/external/aport-policies/finance.transaction.execute.v1/fastapi.example.py +231 -0
- package/external/aport-policies/finance.transaction.execute.v1/minimal-example.js +78 -0
- package/external/aport-policies/finance.transaction.execute.v1/policy.json +189 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/contexts.jsonl +12 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/expected.jsonl +12 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/passport.instance.json +42 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/passport.template.json +42 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/test_transactions_policy.py +214 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/transactions-policy.test.js +306 -0
- package/external/aport-policies/governance.data.access.v1/README.md +292 -0
- package/external/aport-policies/governance.data.access.v1/express.example.js +321 -0
- package/external/aport-policies/governance.data.access.v1/fastapi.example.py +279 -0
- package/external/aport-policies/governance.data.access.v1/minimal-example.js +65 -0
- package/external/aport-policies/governance.data.access.v1/policy.json +208 -0
- package/external/aport-policies/governance.data.access.v1/tests/contexts.jsonl +12 -0
- package/external/aport-policies/governance.data.access.v1/tests/data-access-policy.test.js +308 -0
- package/external/aport-policies/governance.data.access.v1/tests/expected.jsonl +12 -0
- package/external/aport-policies/governance.data.access.v1/tests/passport.instance.json +56 -0
- package/external/aport-policies/governance.data.access.v1/tests/passport.template.json +56 -0
- package/external/aport-policies/governance.data.access.v1/tests/test_data_access_policy.py +214 -0
- package/external/aport-policies/legal.contract.review.v1/README.md +109 -0
- package/external/aport-policies/legal.contract.review.v1/policy.json +378 -0
- package/external/aport-policies/legal.contract.review.v1/tests/legal-contract-review-policy.test.js +609 -0
- package/external/aport-policies/legal.contract.review.v1/tests/passport.template.json +49 -0
- package/external/aport-policies/mcp.tool.execute.v1/README.md +301 -0
- package/external/aport-policies/mcp.tool.execute.v1/policy.json +141 -0
- package/external/aport-policies/messaging.message.send.v1/README.md +230 -0
- package/external/aport-policies/messaging.message.send.v1/express.example.js +183 -0
- package/external/aport-policies/messaging.message.send.v1/fastapi.example.py +193 -0
- package/external/aport-policies/messaging.message.send.v1/policy.json +144 -0
- package/external/aport-policies/policy-template.json +107 -0
- package/external/aport-policies/system.command.execute.v1/README.md +275 -0
- package/external/aport-policies/system.command.execute.v1/policy.json +146 -0
- package/external/aport-spec/CONTRIBUTING.md +273 -0
- package/external/aport-spec/LICENSE +21 -0
- package/external/aport-spec/README.md +168 -0
- package/external/aport-spec/conformance/README.md +294 -0
- package/external/aport-spec/conformance/cases/data.export.v1/contexts/allow_users.json +6 -0
- package/external/aport-spec/conformance/cases/data.export.v1/contexts/deny_pii.json +6 -0
- package/external/aport-spec/conformance/cases/data.export.v1/expected/allow_users.decision.json +19 -0
- package/external/aport-spec/conformance/cases/data.export.v1/expected/deny_pii.decision.json +19 -0
- package/external/aport-spec/conformance/cases/data.export.v1/passports/template.json +29 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/allow_50usd.json +9 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/deny_150usd.json +9 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/deny_currency.json +9 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/allow_50usd.decision.json +19 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/deny_150usd.decision.json +19 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/deny_currency.decision.json +19 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/passports/template.json +42 -0
- package/external/aport-spec/conformance/package.json +44 -0
- package/external/aport-spec/conformance/pnpm-lock.yaml +642 -0
- package/external/aport-spec/conformance/src/cases.ts +371 -0
- package/external/aport-spec/conformance/src/ed25519.ts +167 -0
- package/external/aport-spec/conformance/src/jcs.ts +85 -0
- package/external/aport-spec/conformance/src/runner.ts +533 -0
- package/external/aport-spec/conformance/src/validators.ts +185 -0
- package/external/aport-spec/conformance/test-runner.js +315 -0
- package/external/aport-spec/conformance/tsconfig.json +21 -0
- package/external/aport-spec/error-schema.json +192 -0
- package/external/aport-spec/index.json +12 -0
- package/external/aport-spec/integrations/clawmoat/README.md +12 -0
- package/external/aport-spec/integrations/shield/README.md +245 -0
- package/external/aport-spec/integrations/shield/adapters/index.js +116 -0
- package/external/aport-spec/integrations/shield/adapters/system-command-execute.js +133 -0
- package/external/aport-spec/integrations/shield/test/README.md +58 -0
- package/external/aport-spec/integrations/shield/test/shield.md +40 -0
- package/external/aport-spec/integrations/shield/test/test-shield-to-verify.js +274 -0
- package/external/aport-spec/metrics-schema.json +504 -0
- package/external/aport-spec/oap/CHANGELOG.md +54 -0
- package/external/aport-spec/oap/VERSION.md +40 -0
- package/external/aport-spec/oap/capability-registry.md +229 -0
- package/external/aport-spec/oap/conformance.md +257 -0
- package/external/aport-spec/oap/decision-schema.json +114 -0
- package/external/aport-spec/oap/examples/context.refund.usd.50.json +9 -0
- package/external/aport-spec/oap/examples/decision.allow.sample.json +20 -0
- package/external/aport-spec/oap/examples/decision.deny.sample.json +23 -0
- package/external/aport-spec/oap/examples/passport.instance.v1.json +50 -0
- package/external/aport-spec/oap/examples/passport.template.v1.json +71 -0
- package/external/aport-spec/oap/oap-spec.md +426 -0
- package/external/aport-spec/oap/passport-schema.json +396 -0
- package/external/aport-spec/oap/security.md +213 -0
- package/external/aport-spec/oap/vc/context-oap-v1.jsonld +137 -0
- package/external/aport-spec/oap/vc/examples/oap-decision-vc.json +37 -0
- package/external/aport-spec/oap/vc/examples/oap-passport-vc.json +68 -0
- package/external/aport-spec/oap/vc/tools/INTEGRATION.md +375 -0
- package/external/aport-spec/oap/vc/tools/README.md +278 -0
- package/external/aport-spec/oap/vc/tools/examples/decision-to-vc.js +66 -0
- package/external/aport-spec/oap/vc/tools/examples/passport-to-vc.js +83 -0
- package/external/aport-spec/oap/vc/tools/examples/vc-to-decision.js +77 -0
- package/external/aport-spec/oap/vc/tools/examples/vc-to-passport.js +94 -0
- package/external/aport-spec/oap/vc/tools/package.json +38 -0
- package/external/aport-spec/oap/vc/tools/pnpm-lock.yaml +472 -0
- package/external/aport-spec/oap/vc/tools/src/cli.ts +226 -0
- package/external/aport-spec/oap/vc/tools/src/crypto-utils.ts +427 -0
- package/external/aport-spec/oap/vc/tools/src/index.ts +653 -0
- package/external/aport-spec/oap/vc/tools/src/test.ts +148 -0
- package/external/aport-spec/oap/vc/tools/src/vp.ts +382 -0
- package/external/aport-spec/oap/vc/tools/test-simple.js +214 -0
- package/external/aport-spec/oap/vc/tools/tsconfig.json +19 -0
- package/external/aport-spec/oap/vc/vc-mapping.md +443 -0
- package/external/aport-spec/passport-schema.json +586 -0
- package/external/aport-spec/rate-limiting.md +136 -0
- package/external/aport-spec/transport-profile.md +325 -0
- package/external/aport-spec/webhook-spec.md +314 -0
- package/package.json +70 -0
- package/skills/aport-agent-guardrail/SKILL.md +314 -0
- package/src/evaluator.js +252 -0
- 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
|
+
[](https://www.npmjs.com/package/@aporthq/aport-agent-guardrails)
|
|
6
|
+
[](https://pypi.org/project/aport-agent-guardrails/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](tests/)
|
|
9
|
+
[](package.json)
|
|
10
|
+
[](python/aport_guardrails/pyproject.toml)
|
|
11
|
+
[](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 <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 (<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 <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
|