@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
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "finance.payment.payout.v1",
|
|
3
|
+
"name": "Payment Payout Policy",
|
|
4
|
+
"description": "Pre-action governance for payment payout operations. Enforces per-currency caps, destination restrictions, and compliance requirements.",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"status": "active",
|
|
7
|
+
"requires_capabilities": ["payments.payout"],
|
|
8
|
+
"min_assurance": "L3",
|
|
9
|
+
"limits_required": [
|
|
10
|
+
"supported_currencies",
|
|
11
|
+
"currency_limits",
|
|
12
|
+
"allowed_destination_types",
|
|
13
|
+
"allowed_recipients",
|
|
14
|
+
"max_payouts_per_day",
|
|
15
|
+
"compliance_checks_required",
|
|
16
|
+
"approval_required"
|
|
17
|
+
],
|
|
18
|
+
"required_fields": [
|
|
19
|
+
"amount",
|
|
20
|
+
"currency",
|
|
21
|
+
"destination_type",
|
|
22
|
+
"destination_id",
|
|
23
|
+
"payout_method",
|
|
24
|
+
"idempotency_key"
|
|
25
|
+
],
|
|
26
|
+
"optional_fields": ["description", "compliance_notes", "approval_required"],
|
|
27
|
+
"enforcement": {
|
|
28
|
+
"currency_supported": true,
|
|
29
|
+
"destination_type_allowed": true,
|
|
30
|
+
"daily_cap_check": true,
|
|
31
|
+
"compliance_required": true,
|
|
32
|
+
"idempotency_required": true
|
|
33
|
+
},
|
|
34
|
+
"mcp": {
|
|
35
|
+
"require_allowlisted_if_present": true
|
|
36
|
+
},
|
|
37
|
+
"advice": [
|
|
38
|
+
"Cache /verify with ETag; 60s TTL",
|
|
39
|
+
"Subscribe to status webhooks for instant suspend",
|
|
40
|
+
"Log all payout attempts for Verifiable Attestation",
|
|
41
|
+
"Implement daily payout tracking per currency to prevent abuse",
|
|
42
|
+
"Always use unique idempotency keys to prevent duplicate payouts",
|
|
43
|
+
"Verify destination accounts before processing payouts",
|
|
44
|
+
"Implement compliance checks for high-value payouts",
|
|
45
|
+
"Maintain audit trails for all payout operations"
|
|
46
|
+
],
|
|
47
|
+
"required_context": {
|
|
48
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
49
|
+
"type": "object",
|
|
50
|
+
"required": [
|
|
51
|
+
"amount",
|
|
52
|
+
"currency",
|
|
53
|
+
"destination_type",
|
|
54
|
+
"destination_id",
|
|
55
|
+
"payout_method",
|
|
56
|
+
"idempotency_key"
|
|
57
|
+
],
|
|
58
|
+
"properties": {
|
|
59
|
+
"amount": {
|
|
60
|
+
"type": "integer",
|
|
61
|
+
"minimum": 1,
|
|
62
|
+
"description": "Payout amount in minor units (e.g., cents)"
|
|
63
|
+
},
|
|
64
|
+
"currency": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"pattern": "^[A-Z]{3}$",
|
|
67
|
+
"description": "ISO 4217 currency code"
|
|
68
|
+
},
|
|
69
|
+
"destination_type": {
|
|
70
|
+
"type": "string",
|
|
71
|
+
"enum": ["bank_account", "digital_wallet", "crypto_address"],
|
|
72
|
+
"description": "Type of destination account"
|
|
73
|
+
},
|
|
74
|
+
"destination_id": {
|
|
75
|
+
"type": "string",
|
|
76
|
+
"description": "Destination account identifier"
|
|
77
|
+
},
|
|
78
|
+
"payout_method": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"enum": ["wire_transfer", "ach", "crypto_transfer", "digital_wallet"],
|
|
81
|
+
"description": "Method of payout"
|
|
82
|
+
},
|
|
83
|
+
"idempotency_key": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"minLength": 8,
|
|
86
|
+
"description": "Idempotency key for duplicate prevention"
|
|
87
|
+
},
|
|
88
|
+
"description": {
|
|
89
|
+
"type": "string",
|
|
90
|
+
"description": "Optional payout description"
|
|
91
|
+
},
|
|
92
|
+
"compliance_notes": {
|
|
93
|
+
"type": "string",
|
|
94
|
+
"description": "Compliance verification notes"
|
|
95
|
+
},
|
|
96
|
+
"approval_required": {
|
|
97
|
+
"type": "boolean",
|
|
98
|
+
"description": "Whether payout requires manual approval"
|
|
99
|
+
},
|
|
100
|
+
"mcp_servers": {
|
|
101
|
+
"type": "array",
|
|
102
|
+
"items": { "type": "string" },
|
|
103
|
+
"description": "MCP servers being used in this request (e.g., [\"https://mcp.stripe.com\"])"
|
|
104
|
+
},
|
|
105
|
+
"mcp_tools": {
|
|
106
|
+
"type": "array",
|
|
107
|
+
"items": { "type": "string" },
|
|
108
|
+
"description": "MCP tools being used in this request (e.g., [\"stripe.payouts.create\"])"
|
|
109
|
+
},
|
|
110
|
+
"mcp_server": {
|
|
111
|
+
"type": "string",
|
|
112
|
+
"description": "Single MCP server being used (backward compatibility - use mcp_servers array for multiple)"
|
|
113
|
+
},
|
|
114
|
+
"mcp_tool": {
|
|
115
|
+
"type": "string",
|
|
116
|
+
"description": "Single MCP tool being used (backward compatibility - use mcp_tools array for multiple)"
|
|
117
|
+
},
|
|
118
|
+
"mcp_session": {
|
|
119
|
+
"type": "string",
|
|
120
|
+
"description": "MCP session identifier for audit trail (optional)"
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"evaluation_rules": [
|
|
125
|
+
{
|
|
126
|
+
"name": "passport_status_active",
|
|
127
|
+
"condition": "passport.status == 'active'",
|
|
128
|
+
"deny_code": "oap.passport_suspended",
|
|
129
|
+
"description": "Passport must be active"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"name": "assurance_minimum",
|
|
133
|
+
"condition": "passport.assurance_level >= 'L3'",
|
|
134
|
+
"deny_code": "oap.assurance_insufficient",
|
|
135
|
+
"description": "Assurance level must be L3 or higher for payouts"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"name": "currency_supported",
|
|
139
|
+
"condition": "currency in limits.supported_currencies",
|
|
140
|
+
"deny_code": "oap.currency_unsupported",
|
|
141
|
+
"description": "Currency must be supported"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"name": "per_tx_amount_cap",
|
|
145
|
+
"condition": "amount <= limits.currency_limits[currency].max_per_tx",
|
|
146
|
+
"deny_code": "oap.limit_exceeded",
|
|
147
|
+
"description": "Amount must not exceed per-transaction limit"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"name": "destination_type_allowed",
|
|
151
|
+
"condition": "destination_type in limits.allowed_destination_types",
|
|
152
|
+
"deny_code": "oap.destination_type_forbidden",
|
|
153
|
+
"description": "Destination type must be allowed"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"name": "daily_cap_check",
|
|
157
|
+
"condition": "daily_payout_count < limits.max_payouts_per_day",
|
|
158
|
+
"deny_code": "oap.limit_exceeded",
|
|
159
|
+
"description": "Daily payout limit must not be exceeded"
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"name": "compliance_check",
|
|
163
|
+
"condition": "limits.compliance_checks_required == false OR compliance_notes is not empty",
|
|
164
|
+
"deny_code": "oap.compliance_check_required",
|
|
165
|
+
"description": "Compliance checks must be completed for high-value payouts"
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"name": "idempotency_check",
|
|
169
|
+
"condition": "idempotency_key not in recent_keys",
|
|
170
|
+
"deny_code": "oap.idempotency_conflict",
|
|
171
|
+
"description": "Idempotency key must be unique"
|
|
172
|
+
}
|
|
173
|
+
],
|
|
174
|
+
"cache": {
|
|
175
|
+
"default_ttl_seconds": 60,
|
|
176
|
+
"suspend_invalidate_seconds": 30
|
|
177
|
+
},
|
|
178
|
+
"deprecation": null,
|
|
179
|
+
"created_at": "2025-01-30T00:00:00Z",
|
|
180
|
+
"updated_at": "2025-01-30T00:00:00Z"
|
|
181
|
+
}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# Refunds Protection Policy Pack v1
|
|
2
|
+
|
|
3
|
+
Protect your refund endpoints with APort's standardized policy pack. This pack ensures only verified agents with proper capabilities, assurance levels, and transaction limits can process refunds.
|
|
4
|
+
|
|
5
|
+
## What This Pack Protects
|
|
6
|
+
|
|
7
|
+
- **Route**: `/refunds/*` (POST, PUT, PATCH)
|
|
8
|
+
- **Risk**: Financial transactions, potential fraud, regulatory compliance
|
|
9
|
+
- **Impact**: Direct monetary loss, customer disputes, audit findings
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
| Requirement | Value | Description |
|
|
14
|
+
|-------------|-------|-------------|
|
|
15
|
+
| **Capability** | `finance.payment.refund` | Agent must have refund capability |
|
|
16
|
+
| **Assurance** | `L2` or higher | Email + GitHub verification minimum |
|
|
17
|
+
| **Limits** | `refund_amount_max_per_tx` | Maximum per-transaction refund amount |
|
|
18
|
+
| **Limits** | `refund_amount_daily_cap` | Maximum daily refund total |
|
|
19
|
+
| **Regions** | Must match | Agent must be authorized in caller's region |
|
|
20
|
+
|
|
21
|
+
## Implementation
|
|
22
|
+
|
|
23
|
+
### Express.js
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
const { requirePolicy } = require('@aporthq/middleware-express');
|
|
27
|
+
|
|
28
|
+
// Option 1: Explicit agent ID (preferred)
|
|
29
|
+
app.post('/refunds',
|
|
30
|
+
requirePolicy('finance.payment.refund.v1', 'ap_a2d10232c6534523812423eec8a1425c45678'),
|
|
31
|
+
async (req, res) => {
|
|
32
|
+
// Your refund logic here
|
|
33
|
+
// req.policyResult contains the verified passport
|
|
34
|
+
const { amount, reason } = req.body;
|
|
35
|
+
const passport = req.policyResult.passport;
|
|
36
|
+
|
|
37
|
+
// Process refund...
|
|
38
|
+
res.json({ success: true, refund_id: generateId() });
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// Option 2: Header fallback (backward compatible)
|
|
43
|
+
app.post('/refunds',
|
|
44
|
+
requirePolicy('finance.payment.refund.v1'),
|
|
45
|
+
async (req, res) => {
|
|
46
|
+
// Your refund logic here
|
|
47
|
+
// req.policyResult contains the verified passport
|
|
48
|
+
const { amount, reason } = req.body;
|
|
49
|
+
const passport = req.policyResult.passport;
|
|
50
|
+
|
|
51
|
+
// Process refund...
|
|
52
|
+
res.json({ success: true, refund_id: generateId() });
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Client Request Example:**
|
|
58
|
+
```javascript
|
|
59
|
+
// The client must include the agent ID in the header (for Option 2)
|
|
60
|
+
fetch('/refunds', {
|
|
61
|
+
method: 'POST',
|
|
62
|
+
headers: {
|
|
63
|
+
'Content-Type': 'application/json',
|
|
64
|
+
'X-Agent-Passport-Id': 'ap_a2d10232c6534523812423eec8a1425c45678' // ← Agent ID passed here
|
|
65
|
+
},
|
|
66
|
+
body: JSON.stringify({
|
|
67
|
+
amount: 25.00,
|
|
68
|
+
reason: 'Customer requested refund'
|
|
69
|
+
})
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### FastAPI
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from aport.middleware import require_policy
|
|
77
|
+
|
|
78
|
+
@app.post("/refunds")
|
|
79
|
+
@require_policy("finance.payment.refund.v1")
|
|
80
|
+
async def process_refund(request: Request, refund_data: RefundRequest):
|
|
81
|
+
# Your refund logic here
|
|
82
|
+
# request.state.policy_result contains the verified passport
|
|
83
|
+
passport = request.state.policy_result.passport
|
|
84
|
+
|
|
85
|
+
# Process refund...
|
|
86
|
+
return {"success": True, "refund_id": generate_id()}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Client Request Example:**
|
|
90
|
+
```python
|
|
91
|
+
import requests
|
|
92
|
+
|
|
93
|
+
# The client must include the agent ID in the header
|
|
94
|
+
response = requests.post('/refunds',
|
|
95
|
+
headers={
|
|
96
|
+
'Content-Type': 'application/json',
|
|
97
|
+
'X-Agent-Passport-Id': 'ap_a2d10232c6534523812423eec8a1425c45678' # ← Agent ID passed here
|
|
98
|
+
},
|
|
99
|
+
json={
|
|
100
|
+
'amount': 25.00,
|
|
101
|
+
'reason': 'Customer requested refund'
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## How It Works
|
|
107
|
+
|
|
108
|
+
The `requirePolicy('finance.payment.refund.v1', agentId?)` middleware implements a flexible approach:
|
|
109
|
+
|
|
110
|
+
1. **Agent ID Resolution** (in order of preference):
|
|
111
|
+
- **Function Parameter**: Uses explicit `agentId` if provided
|
|
112
|
+
- **Header Fallback**: Reads `X-Agent-Passport-Id` header from request
|
|
113
|
+
- **Validation**: Ensures agent ID format is valid (`ap_xxxxxxxxxxxxx`)
|
|
114
|
+
- **Failure**: Returns 400 error if neither provided
|
|
115
|
+
|
|
116
|
+
2. **Policy Validation**:
|
|
117
|
+
- **Format Check**: Validates policy ID format (`finance.payment.refund.v1`)
|
|
118
|
+
- **API Call**: Calls `/api/verify/policy/finance.payment.refund.v1` with agent ID and context
|
|
119
|
+
- **Requirements Check**: Validates agent meets finance.payment.refund.v1 requirements:
|
|
120
|
+
- Has `finance.payment.refund` capability
|
|
121
|
+
- Meets minimum assurance level (L2+)
|
|
122
|
+
- Within transaction and daily limits
|
|
123
|
+
- Authorized in the request region
|
|
124
|
+
|
|
125
|
+
3. **Result Handling**:
|
|
126
|
+
- **Success**: Adds `req.policyResult` with verified data and continues
|
|
127
|
+
- **Failure**: Returns 403 with detailed error information
|
|
128
|
+
- **Logging**: Logs violations for monitoring and debugging
|
|
129
|
+
|
|
130
|
+
## Error Responses
|
|
131
|
+
|
|
132
|
+
When policy checks fail, you'll receive a `403 Forbidden` with detailed error information:
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"error": "policy_violation",
|
|
137
|
+
"code": "MISSING_CAPABILITY",
|
|
138
|
+
"message": "Agent missing required capability: finance.payment.refund",
|
|
139
|
+
"policy_id": "finance.payment.refund.v1",
|
|
140
|
+
"agent_id": "ap_a2d10232c6534523812423eec8a1425c45678",
|
|
141
|
+
"upgrade_instructions": "Add 'finance.payment.refund' capability to your passport"
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Test Payloads
|
|
146
|
+
|
|
147
|
+
### Valid Request
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"amount": 25.00,
|
|
151
|
+
"reason": "Customer requested refund",
|
|
152
|
+
"order_id": "ORD-12345"
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Invalid Request (exceeds limit)
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"amount": 1000.00,
|
|
160
|
+
"reason": "Customer requested refund",
|
|
161
|
+
"order_id": "ORD-12345"
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
*Returns 403: "Amount $1000.00 exceeds limit $500.00"*
|
|
165
|
+
|
|
166
|
+
## Best Practices
|
|
167
|
+
|
|
168
|
+
1. **Cache Verification**: Cache `/verify` responses with ETag for 60 seconds
|
|
169
|
+
2. **Webhook Integration**: Subscribe to `status.changed` webhooks for instant suspension
|
|
170
|
+
3. **Verifiable Attestation**: Log all refund attempts for compliance
|
|
171
|
+
4. **Daily Tracking**: Implement daily spend tracking to prevent abuse
|
|
172
|
+
5. **Error Handling**: Provide clear error messages to help agents self-remediate
|
|
173
|
+
|
|
174
|
+
## Compliance Badge
|
|
175
|
+
|
|
176
|
+
Agents that meet this policy's requirements can display the "Refunds-Ready" badge:
|
|
177
|
+
|
|
178
|
+
```markdown
|
|
179
|
+
[](https://aport.io/agents/ap_a2d10232c6534523812423eec8a1425c45678)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Support
|
|
183
|
+
|
|
184
|
+
- [Documentation](https://aport.io/docs/policies/finance.payment.refund.v1)
|
|
185
|
+
- [Community](https://github.com/aporthq/community)
|
|
186
|
+
- [Support](https://aport.io/support)
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
**Last Updated**: 2025-09-24 23:15:00 UTC
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
## Required Context
|
|
193
|
+
|
|
194
|
+
This policy requires the following context (JSON Schema):
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
199
|
+
"type": "object",
|
|
200
|
+
"required": [
|
|
201
|
+
"order_id",
|
|
202
|
+
"customer_id",
|
|
203
|
+
"amount",
|
|
204
|
+
"currency",
|
|
205
|
+
"region",
|
|
206
|
+
"reason_code",
|
|
207
|
+
"idempotency_key"
|
|
208
|
+
],
|
|
209
|
+
"properties": {
|
|
210
|
+
"order_id": {
|
|
211
|
+
"type": "string",
|
|
212
|
+
"minLength": 1,
|
|
213
|
+
"description": "Original order identifier"
|
|
214
|
+
},
|
|
215
|
+
"customer_id": {
|
|
216
|
+
"type": "string",
|
|
217
|
+
"minLength": 1,
|
|
218
|
+
"description": "Customer identifier"
|
|
219
|
+
},
|
|
220
|
+
"amount": {
|
|
221
|
+
"type": "integer",
|
|
222
|
+
"minimum": 1,
|
|
223
|
+
"description": "Refund amount in minor units (e.g., cents)"
|
|
224
|
+
},
|
|
225
|
+
"currency": {
|
|
226
|
+
"type": "string",
|
|
227
|
+
"pattern": "^[A-Z]{3}$",
|
|
228
|
+
"description": "ISO 4217 currency code"
|
|
229
|
+
},
|
|
230
|
+
"region": {
|
|
231
|
+
"type": "string",
|
|
232
|
+
"description": "Geographic region"
|
|
233
|
+
},
|
|
234
|
+
"reason_code": {
|
|
235
|
+
"type": "string",
|
|
236
|
+
"description": "Refund reason code"
|
|
237
|
+
},
|
|
238
|
+
"idempotency_key": {
|
|
239
|
+
"type": "string",
|
|
240
|
+
"minLength": 8,
|
|
241
|
+
"description": "Idempotency key for duplicate prevention"
|
|
242
|
+
},
|
|
243
|
+
"note": {
|
|
244
|
+
"type": "string",
|
|
245
|
+
"description": "Optional refund note"
|
|
246
|
+
},
|
|
247
|
+
"merchant_case_id": {
|
|
248
|
+
"type": "string",
|
|
249
|
+
"description": "Merchant's internal case ID"
|
|
250
|
+
},
|
|
251
|
+
"order_currency": {
|
|
252
|
+
"type": "string",
|
|
253
|
+
"pattern": "^[A-Z]{3}$",
|
|
254
|
+
"description": "Original order currency"
|
|
255
|
+
},
|
|
256
|
+
"order_total_minor": {
|
|
257
|
+
"type": "integer",
|
|
258
|
+
"minimum": 0,
|
|
259
|
+
"description": "Original order total in minor units"
|
|
260
|
+
},
|
|
261
|
+
"already_refunded_minor": {
|
|
262
|
+
"type": "integer",
|
|
263
|
+
"minimum": 0,
|
|
264
|
+
"description": "Amount already refunded in minor units"
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
You can also fetch this live via the discovery endpoint:
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
curl -s "https://aport.io/api/policies/finance.payment.refund.v1?format=schema"
|
|
274
|
+
```
|
|
275
|
+
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
const express = require("express");
|
|
2
|
+
const { requirePolicy } = require("@aporthq/middleware-express");
|
|
3
|
+
|
|
4
|
+
const app = express();
|
|
5
|
+
app.use(express.json());
|
|
6
|
+
|
|
7
|
+
// Apply refunds policy to all refund routes
|
|
8
|
+
app.post(
|
|
9
|
+
"/refunds",
|
|
10
|
+
requirePolicy("finance.payment.refund.v1"),
|
|
11
|
+
async (req, res) => {
|
|
12
|
+
try {
|
|
13
|
+
const {
|
|
14
|
+
amount_minor,
|
|
15
|
+
currency,
|
|
16
|
+
order_id,
|
|
17
|
+
customer_id,
|
|
18
|
+
reason_code,
|
|
19
|
+
region,
|
|
20
|
+
idempotency_key,
|
|
21
|
+
order_currency,
|
|
22
|
+
order_total_minor,
|
|
23
|
+
already_refunded_minor,
|
|
24
|
+
note,
|
|
25
|
+
merchant_case_id,
|
|
26
|
+
} = req.body;
|
|
27
|
+
|
|
28
|
+
const passport = req.policyResult.passport;
|
|
29
|
+
|
|
30
|
+
// Additional business logic validation
|
|
31
|
+
if (amount_minor <= 0) {
|
|
32
|
+
return res.status(400).json({ error: "Invalid refund amount" });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Process refund using your payment processor
|
|
36
|
+
const refund_id = await processRefund({
|
|
37
|
+
amount_minor,
|
|
38
|
+
currency,
|
|
39
|
+
order_id,
|
|
40
|
+
customer_id,
|
|
41
|
+
reason_code,
|
|
42
|
+
region,
|
|
43
|
+
idempotency_key,
|
|
44
|
+
order_currency,
|
|
45
|
+
order_total_minor,
|
|
46
|
+
already_refunded_minor,
|
|
47
|
+
note,
|
|
48
|
+
merchant_case_id,
|
|
49
|
+
agent_id: passport.agent_id,
|
|
50
|
+
agent_name: passport.name,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Log the transaction
|
|
54
|
+
console.log(
|
|
55
|
+
`Refund processed: ${refund_id} for ${amount_minor} ${currency} by agent ${passport.agent_id}`
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
res.json({
|
|
59
|
+
success: true,
|
|
60
|
+
refund_id,
|
|
61
|
+
amount_minor,
|
|
62
|
+
currency,
|
|
63
|
+
status: "processed",
|
|
64
|
+
decision_id: req.policyResult.decision_id,
|
|
65
|
+
});
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error("Refund processing error:", error);
|
|
68
|
+
res.status(500).json({ error: "Internal server error" });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// Batch refunds endpoint
|
|
74
|
+
app.post(
|
|
75
|
+
"/refunds/batch",
|
|
76
|
+
requirePolicy("finance.payment.refund.v1"),
|
|
77
|
+
async (req, res) => {
|
|
78
|
+
try {
|
|
79
|
+
const { refunds } = req.body;
|
|
80
|
+
const passport = req.policyResult.passport;
|
|
81
|
+
|
|
82
|
+
// Group refunds by currency for daily cap checking
|
|
83
|
+
const currencyTotals = {};
|
|
84
|
+
for (const refund of refunds) {
|
|
85
|
+
const currency = refund.currency || "USD";
|
|
86
|
+
currencyTotals[currency] =
|
|
87
|
+
(currencyTotals[currency] || 0) + (refund.amount_minor || 0);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Check daily caps per currency
|
|
91
|
+
for (const [currency, totalAmount] of Object.entries(currencyTotals)) {
|
|
92
|
+
const currencyLimits = passport.limits?.currency_limits?.[currency];
|
|
93
|
+
if (
|
|
94
|
+
currencyLimits?.daily_cap &&
|
|
95
|
+
totalAmount > currencyLimits.daily_cap
|
|
96
|
+
) {
|
|
97
|
+
return res.status(403).json({
|
|
98
|
+
error: "Batch total exceeds daily cap",
|
|
99
|
+
currency,
|
|
100
|
+
total: totalAmount,
|
|
101
|
+
limit: currencyLimits.daily_cap,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Process batch refunds
|
|
107
|
+
const results = await Promise.all(
|
|
108
|
+
refunds.map((refund) =>
|
|
109
|
+
processRefund({
|
|
110
|
+
...refund,
|
|
111
|
+
agent_id: passport.agent_id,
|
|
112
|
+
})
|
|
113
|
+
)
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
res.json({
|
|
117
|
+
success: true,
|
|
118
|
+
processed: results.length,
|
|
119
|
+
currency_totals: currencyTotals,
|
|
120
|
+
decision_id: req.policyResult.decision_id,
|
|
121
|
+
});
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error("Batch refund error:", error);
|
|
124
|
+
res.status(500).json({ error: "Internal server error" });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
// Mock refund processing function
|
|
130
|
+
async function processRefund({
|
|
131
|
+
amount_minor,
|
|
132
|
+
currency,
|
|
133
|
+
order_id,
|
|
134
|
+
customer_id,
|
|
135
|
+
reason_code,
|
|
136
|
+
region,
|
|
137
|
+
idempotency_key,
|
|
138
|
+
order_currency,
|
|
139
|
+
order_total_minor,
|
|
140
|
+
already_refunded_minor,
|
|
141
|
+
note,
|
|
142
|
+
merchant_case_id,
|
|
143
|
+
agent_id,
|
|
144
|
+
}) {
|
|
145
|
+
// Simulate payment processor call
|
|
146
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
147
|
+
|
|
148
|
+
// Log refund details for audit
|
|
149
|
+
console.log(`Processing refund:`, {
|
|
150
|
+
amount_minor,
|
|
151
|
+
currency,
|
|
152
|
+
order_id,
|
|
153
|
+
customer_id,
|
|
154
|
+
reason_code,
|
|
155
|
+
region,
|
|
156
|
+
idempotency_key,
|
|
157
|
+
agent_id,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
return `ref_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const PORT = process.env.PORT || 3000;
|
|
164
|
+
app.listen(PORT, () => {
|
|
165
|
+
console.log(`Refunds service running on port ${PORT}`);
|
|
166
|
+
console.log("Protected by APort finance.payment.refund.v1 policy pack");
|
|
167
|
+
});
|