@aporthq/aport-agent-guardrails 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/LICENSE +217 -0
  2. package/README.md +481 -0
  3. package/bin/agent-guardrails +133 -0
  4. package/bin/aport-create-passport.sh +444 -0
  5. package/bin/aport-cursor-hook.sh +90 -0
  6. package/bin/aport-guardrail-api.sh +108 -0
  7. package/bin/aport-guardrail-bash.sh +394 -0
  8. package/bin/aport-guardrail-v2.sh +5 -0
  9. package/bin/aport-guardrail.sh +5 -0
  10. package/bin/aport-resolve-paths.sh +71 -0
  11. package/bin/aport-status.sh +276 -0
  12. package/bin/frameworks/crewai.sh +49 -0
  13. package/bin/frameworks/cursor.sh +95 -0
  14. package/bin/frameworks/langchain.sh +48 -0
  15. package/bin/frameworks/n8n.sh +36 -0
  16. package/bin/frameworks/openclaw.sh +19 -0
  17. package/bin/lib/allowlist.sh +18 -0
  18. package/bin/lib/common.sh +28 -0
  19. package/bin/lib/config.sh +46 -0
  20. package/bin/lib/constants.sh +232 -0
  21. package/bin/lib/detect.sh +65 -0
  22. package/bin/lib/error.sh +269 -0
  23. package/bin/lib/passport.sh +19 -0
  24. package/bin/lib/templates/.gitkeep +1 -0
  25. package/bin/lib/templates/config.yaml +6 -0
  26. package/bin/lib/validation.sh +206 -0
  27. package/bin/openclaw +660 -0
  28. package/docs/ADDING_A_FRAMEWORK.md +87 -0
  29. package/docs/AGENTS.md.example +40 -0
  30. package/docs/CODE_REVIEW.md +192 -0
  31. package/docs/DEPLOYMENT_READINESS.md +81 -0
  32. package/docs/FAQ_SECURITY_SCANNERS.md +373 -0
  33. package/docs/FRAMEWORK_ROADMAP.md +41 -0
  34. package/docs/HOSTED_PASSPORT_SETUP.md +362 -0
  35. package/docs/IMPLEMENTING_YOUR_OWN_EVALUATOR.md +433 -0
  36. package/docs/OPENCLAW_COMPATIBILITY.md +73 -0
  37. package/docs/OPENCLAW_LOCAL_INTEGRATION.md +596 -0
  38. package/docs/OPENCLAW_TOOLS_AND_POLICIES.md +54 -0
  39. package/docs/QUICKSTART.md +470 -0
  40. package/docs/QUICKSTART_OPENCLAW_PLUGIN.md +470 -0
  41. package/docs/README.md +28 -0
  42. package/docs/RELEASE.md +87 -0
  43. package/docs/REPO_LAYOUT.md +47 -0
  44. package/docs/SKILLS_ECOSYSTEM_ANALYSIS_FEB17.md +1260 -0
  45. package/docs/TOOL_POLICY_MAPPING.md +46 -0
  46. package/docs/UPGRADE.md +46 -0
  47. package/docs/VERIFICATION_METHODS.md +97 -0
  48. package/docs/assets/README.md +8 -0
  49. package/docs/assets/porter.svg +54 -0
  50. package/docs/development/ERROR_CODES.md +616 -0
  51. package/docs/frameworks/GITHUB_ISSUE_PROPOSALS.md +1105 -0
  52. package/docs/frameworks/crewai.md +114 -0
  53. package/docs/frameworks/cursor.md +159 -0
  54. package/docs/frameworks/langchain.md +72 -0
  55. package/docs/frameworks/n8n.md +40 -0
  56. package/docs/frameworks/openclaw.md +40 -0
  57. package/docs/launch/ADD_APORT_AWESOME_LISTS_INSTRUCTIONS.md +146 -0
  58. package/docs/launch/ANNOUNCEMENT_GUIDE.md +266 -0
  59. package/docs/launch/AWESOME_REPOS.md +53 -0
  60. package/docs/launch/CURSOR_VSCODE_HOOKS_RESEARCH.md +77 -0
  61. package/docs/launch/DEMO_TERMINAL_OUTPUT.txt +48 -0
  62. package/docs/launch/DRY_AND_PLAN_CHECKLIST.md +47 -0
  63. package/docs/launch/EVIDENCE_README.md +61 -0
  64. package/docs/launch/EVIDENCE_TERMINAL_CAPTURE.txt +10 -0
  65. package/docs/launch/FRAMEWORK_SUPPORT_PLAN.md +1640 -0
  66. package/docs/launch/LAUNCH_READINESS_CHECKLIST.md +237 -0
  67. package/docs/launch/LAUNCH_STRATEGY_SUMMARY.md +464 -0
  68. package/docs/launch/OPENCLAW_FEEDBACK_AND_FIXES.md +85 -0
  69. package/docs/launch/POST_1_VALENTINE_IMPROVED.md +233 -0
  70. package/docs/launch/POST_2_GUARDRAIL_IMPROVED.md +369 -0
  71. package/docs/launch/PRE_LAUNCH_FIXES.md +766 -0
  72. package/docs/launch/QUICK_LAUNCH_CHECKLIST.md +400 -0
  73. package/docs/launch/READINESS_SUMMARY.md +262 -0
  74. package/docs/launch/README.md +68 -0
  75. package/docs/launch/USER_STORIES.md +327 -0
  76. package/docs/launch/scripts/add-aport-awesome-pr.sh +69 -0
  77. package/docs/operations/MONITORING.md +588 -0
  78. package/docs/reviews/2026-02-18-staff-review.md +268 -0
  79. package/extensions/openclaw-aport/README.md +415 -0
  80. package/extensions/openclaw-aport/index.js +625 -0
  81. package/extensions/openclaw-aport/openclaw-aport.js +7 -0
  82. package/extensions/openclaw-aport/openclaw.plugin.json +46 -0
  83. package/extensions/openclaw-aport/package.json +36 -0
  84. package/extensions/openclaw-aport/test.js +307 -0
  85. package/external/aport-policies/README.md +363 -0
  86. package/external/aport-policies/agent.session.create.v1/README.md +345 -0
  87. package/external/aport-policies/agent.session.create.v1/policy.json +162 -0
  88. package/external/aport-policies/agent.tool.register.v1/README.md +361 -0
  89. package/external/aport-policies/agent.tool.register.v1/policy.json +172 -0
  90. package/external/aport-policies/code.release.publish.v1/README.md +51 -0
  91. package/external/aport-policies/code.release.publish.v1/policy.json +121 -0
  92. package/external/aport-policies/code.repository.merge.v1/README.md +287 -0
  93. package/external/aport-policies/code.repository.merge.v1/express.example.js +332 -0
  94. package/external/aport-policies/code.repository.merge.v1/fastapi.example.py +370 -0
  95. package/external/aport-policies/code.repository.merge.v1/policy.json +162 -0
  96. package/external/aport-policies/data.export.create.v1/README.md +226 -0
  97. package/external/aport-policies/data.export.create.v1/express.example.js +172 -0
  98. package/external/aport-policies/data.export.create.v1/fastapi.example.py +165 -0
  99. package/external/aport-policies/data.export.create.v1/policy.json +133 -0
  100. package/external/aport-policies/data.report.ingest.v1/README.md +134 -0
  101. package/external/aport-policies/data.report.ingest.v1/express.example.js +105 -0
  102. package/external/aport-policies/data.report.ingest.v1/minimal-example.js +68 -0
  103. package/external/aport-policies/data.report.ingest.v1/policy.json +174 -0
  104. package/external/aport-policies/finance.crypto.trade.v1/README.md +146 -0
  105. package/external/aport-policies/finance.crypto.trade.v1/express.example.js +109 -0
  106. package/external/aport-policies/finance.crypto.trade.v1/minimal-example.js +65 -0
  107. package/external/aport-policies/finance.crypto.trade.v1/policy.json +176 -0
  108. package/external/aport-policies/finance.payment.charge.v1/README.md +326 -0
  109. package/external/aport-policies/finance.payment.charge.v1/express.example.js +250 -0
  110. package/external/aport-policies/finance.payment.charge.v1/fastapi.example.py +227 -0
  111. package/external/aport-policies/finance.payment.charge.v1/minimal-example.js +64 -0
  112. package/external/aport-policies/finance.payment.charge.v1/policy.json +224 -0
  113. package/external/aport-policies/finance.payment.charge.v1/tests/contexts.jsonl +12 -0
  114. package/external/aport-policies/finance.payment.charge.v1/tests/expected.jsonl +12 -0
  115. package/external/aport-policies/finance.payment.charge.v1/tests/passport.instance.json +42 -0
  116. package/external/aport-policies/finance.payment.charge.v1/tests/passport.template.json +40 -0
  117. package/external/aport-policies/finance.payment.charge.v1/tests/payments-charge-policy.test.js +817 -0
  118. package/external/aport-policies/finance.payment.charge.v1/tests/test_payments_charge_policy.py +486 -0
  119. package/external/aport-policies/finance.payment.payout.v1/README.md +78 -0
  120. package/external/aport-policies/finance.payment.payout.v1/policy.json +181 -0
  121. package/external/aport-policies/finance.payment.refund.v1/README.md +275 -0
  122. package/external/aport-policies/finance.payment.refund.v1/express.example.js +167 -0
  123. package/external/aport-policies/finance.payment.refund.v1/fastapi.example.py +136 -0
  124. package/external/aport-policies/finance.payment.refund.v1/minimal-example.js +183 -0
  125. package/external/aport-policies/finance.payment.refund.v1/policy.json +216 -0
  126. package/external/aport-policies/finance.payment.refund.v1/tests/refunds-policy.test.js +924 -0
  127. package/external/aport-policies/finance.payment.refund.v1/tests/test_refunds_policy.py +778 -0
  128. package/external/aport-policies/finance.transaction.execute.v1/README.md +309 -0
  129. package/external/aport-policies/finance.transaction.execute.v1/express.example.js +261 -0
  130. package/external/aport-policies/finance.transaction.execute.v1/fastapi.example.py +231 -0
  131. package/external/aport-policies/finance.transaction.execute.v1/minimal-example.js +78 -0
  132. package/external/aport-policies/finance.transaction.execute.v1/policy.json +189 -0
  133. package/external/aport-policies/finance.transaction.execute.v1/tests/contexts.jsonl +12 -0
  134. package/external/aport-policies/finance.transaction.execute.v1/tests/expected.jsonl +12 -0
  135. package/external/aport-policies/finance.transaction.execute.v1/tests/passport.instance.json +42 -0
  136. package/external/aport-policies/finance.transaction.execute.v1/tests/passport.template.json +42 -0
  137. package/external/aport-policies/finance.transaction.execute.v1/tests/test_transactions_policy.py +214 -0
  138. package/external/aport-policies/finance.transaction.execute.v1/tests/transactions-policy.test.js +306 -0
  139. package/external/aport-policies/governance.data.access.v1/README.md +292 -0
  140. package/external/aport-policies/governance.data.access.v1/express.example.js +321 -0
  141. package/external/aport-policies/governance.data.access.v1/fastapi.example.py +279 -0
  142. package/external/aport-policies/governance.data.access.v1/minimal-example.js +65 -0
  143. package/external/aport-policies/governance.data.access.v1/policy.json +208 -0
  144. package/external/aport-policies/governance.data.access.v1/tests/contexts.jsonl +12 -0
  145. package/external/aport-policies/governance.data.access.v1/tests/data-access-policy.test.js +308 -0
  146. package/external/aport-policies/governance.data.access.v1/tests/expected.jsonl +12 -0
  147. package/external/aport-policies/governance.data.access.v1/tests/passport.instance.json +56 -0
  148. package/external/aport-policies/governance.data.access.v1/tests/passport.template.json +56 -0
  149. package/external/aport-policies/governance.data.access.v1/tests/test_data_access_policy.py +214 -0
  150. package/external/aport-policies/legal.contract.review.v1/README.md +109 -0
  151. package/external/aport-policies/legal.contract.review.v1/policy.json +378 -0
  152. package/external/aport-policies/legal.contract.review.v1/tests/legal-contract-review-policy.test.js +609 -0
  153. package/external/aport-policies/legal.contract.review.v1/tests/passport.template.json +49 -0
  154. package/external/aport-policies/mcp.tool.execute.v1/README.md +301 -0
  155. package/external/aport-policies/mcp.tool.execute.v1/policy.json +141 -0
  156. package/external/aport-policies/messaging.message.send.v1/README.md +230 -0
  157. package/external/aport-policies/messaging.message.send.v1/express.example.js +183 -0
  158. package/external/aport-policies/messaging.message.send.v1/fastapi.example.py +193 -0
  159. package/external/aport-policies/messaging.message.send.v1/policy.json +144 -0
  160. package/external/aport-policies/policy-template.json +107 -0
  161. package/external/aport-policies/system.command.execute.v1/README.md +275 -0
  162. package/external/aport-policies/system.command.execute.v1/policy.json +146 -0
  163. package/external/aport-spec/CONTRIBUTING.md +273 -0
  164. package/external/aport-spec/LICENSE +21 -0
  165. package/external/aport-spec/README.md +168 -0
  166. package/external/aport-spec/conformance/README.md +294 -0
  167. package/external/aport-spec/conformance/cases/data.export.v1/contexts/allow_users.json +6 -0
  168. package/external/aport-spec/conformance/cases/data.export.v1/contexts/deny_pii.json +6 -0
  169. package/external/aport-spec/conformance/cases/data.export.v1/expected/allow_users.decision.json +19 -0
  170. package/external/aport-spec/conformance/cases/data.export.v1/expected/deny_pii.decision.json +19 -0
  171. package/external/aport-spec/conformance/cases/data.export.v1/passports/template.json +29 -0
  172. package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/allow_50usd.json +9 -0
  173. package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/deny_150usd.json +9 -0
  174. package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/deny_currency.json +9 -0
  175. package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/allow_50usd.decision.json +19 -0
  176. package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/deny_150usd.decision.json +19 -0
  177. package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/deny_currency.decision.json +19 -0
  178. package/external/aport-spec/conformance/cases/payments.refunds.v1/passports/template.json +42 -0
  179. package/external/aport-spec/conformance/package.json +44 -0
  180. package/external/aport-spec/conformance/pnpm-lock.yaml +642 -0
  181. package/external/aport-spec/conformance/src/cases.ts +371 -0
  182. package/external/aport-spec/conformance/src/ed25519.ts +167 -0
  183. package/external/aport-spec/conformance/src/jcs.ts +85 -0
  184. package/external/aport-spec/conformance/src/runner.ts +533 -0
  185. package/external/aport-spec/conformance/src/validators.ts +185 -0
  186. package/external/aport-spec/conformance/test-runner.js +315 -0
  187. package/external/aport-spec/conformance/tsconfig.json +21 -0
  188. package/external/aport-spec/error-schema.json +192 -0
  189. package/external/aport-spec/index.json +12 -0
  190. package/external/aport-spec/integrations/clawmoat/README.md +12 -0
  191. package/external/aport-spec/integrations/shield/README.md +245 -0
  192. package/external/aport-spec/integrations/shield/adapters/index.js +116 -0
  193. package/external/aport-spec/integrations/shield/adapters/system-command-execute.js +133 -0
  194. package/external/aport-spec/integrations/shield/test/README.md +58 -0
  195. package/external/aport-spec/integrations/shield/test/shield.md +40 -0
  196. package/external/aport-spec/integrations/shield/test/test-shield-to-verify.js +274 -0
  197. package/external/aport-spec/metrics-schema.json +504 -0
  198. package/external/aport-spec/oap/CHANGELOG.md +54 -0
  199. package/external/aport-spec/oap/VERSION.md +40 -0
  200. package/external/aport-spec/oap/capability-registry.md +229 -0
  201. package/external/aport-spec/oap/conformance.md +257 -0
  202. package/external/aport-spec/oap/decision-schema.json +114 -0
  203. package/external/aport-spec/oap/examples/context.refund.usd.50.json +9 -0
  204. package/external/aport-spec/oap/examples/decision.allow.sample.json +20 -0
  205. package/external/aport-spec/oap/examples/decision.deny.sample.json +23 -0
  206. package/external/aport-spec/oap/examples/passport.instance.v1.json +50 -0
  207. package/external/aport-spec/oap/examples/passport.template.v1.json +71 -0
  208. package/external/aport-spec/oap/oap-spec.md +426 -0
  209. package/external/aport-spec/oap/passport-schema.json +396 -0
  210. package/external/aport-spec/oap/security.md +213 -0
  211. package/external/aport-spec/oap/vc/context-oap-v1.jsonld +137 -0
  212. package/external/aport-spec/oap/vc/examples/oap-decision-vc.json +37 -0
  213. package/external/aport-spec/oap/vc/examples/oap-passport-vc.json +68 -0
  214. package/external/aport-spec/oap/vc/tools/INTEGRATION.md +375 -0
  215. package/external/aport-spec/oap/vc/tools/README.md +278 -0
  216. package/external/aport-spec/oap/vc/tools/examples/decision-to-vc.js +66 -0
  217. package/external/aport-spec/oap/vc/tools/examples/passport-to-vc.js +83 -0
  218. package/external/aport-spec/oap/vc/tools/examples/vc-to-decision.js +77 -0
  219. package/external/aport-spec/oap/vc/tools/examples/vc-to-passport.js +94 -0
  220. package/external/aport-spec/oap/vc/tools/package.json +38 -0
  221. package/external/aport-spec/oap/vc/tools/pnpm-lock.yaml +472 -0
  222. package/external/aport-spec/oap/vc/tools/src/cli.ts +226 -0
  223. package/external/aport-spec/oap/vc/tools/src/crypto-utils.ts +427 -0
  224. package/external/aport-spec/oap/vc/tools/src/index.ts +653 -0
  225. package/external/aport-spec/oap/vc/tools/src/test.ts +148 -0
  226. package/external/aport-spec/oap/vc/tools/src/vp.ts +382 -0
  227. package/external/aport-spec/oap/vc/tools/test-simple.js +214 -0
  228. package/external/aport-spec/oap/vc/tools/tsconfig.json +19 -0
  229. package/external/aport-spec/oap/vc/vc-mapping.md +443 -0
  230. package/external/aport-spec/passport-schema.json +586 -0
  231. package/external/aport-spec/rate-limiting.md +136 -0
  232. package/external/aport-spec/transport-profile.md +325 -0
  233. package/external/aport-spec/webhook-spec.md +314 -0
  234. package/package.json +70 -0
  235. package/skills/aport-agent-guardrail/SKILL.md +314 -0
  236. package/src/evaluator.js +252 -0
  237. package/src/server/index.js +72 -0
@@ -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
+ [![Refunds-Ready](https://api.aport.io/badge/ap_a2d10232c6534523812423eec8a1425c45678.svg)](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
+ });