@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,231 @@
1
+ from fastapi import FastAPI, HTTPException, Request
2
+ from pydantic import BaseModel
3
+ from aport.middleware import require_policy
4
+ import asyncio
5
+ from typing import List, Optional
6
+ from datetime import datetime
7
+
8
+ app = FastAPI(title="Financial Transaction Service", version="1.0.0")
9
+
10
+ class TransactionRequest(BaseModel):
11
+ transaction_type: str
12
+ amount: int
13
+ currency: str
14
+ asset_class: str
15
+ source_account_id: str
16
+ destination_account_id: str
17
+ source_account_type: Optional[str] = None
18
+ destination_account_type: Optional[str] = None
19
+ counterparty_id: Optional[str] = None
20
+ idempotency_key: str
21
+
22
+ class BatchTransactionRequest(BaseModel):
23
+ transactions: List[TransactionRequest]
24
+
25
+ class CancelRequest(BaseModel):
26
+ reason: str
27
+
28
+ class TransactionResponse(BaseModel):
29
+ success: bool
30
+ transaction_id: str
31
+ transaction_type: str
32
+ amount: int
33
+ currency: str
34
+ asset_class: str
35
+ status: str
36
+ decision_id: str
37
+
38
+ class TransactionStatus(BaseModel):
39
+ transaction_id: str
40
+ status: str
41
+ created_at: str
42
+ transaction_type: str
43
+ amount: int
44
+ currency: str
45
+ asset_class: str
46
+ source_account_id: str
47
+ destination_account_id: str
48
+
49
+ @app.post("/finance/transaction")
50
+ @require_policy("finance.transaction.execute.v1")
51
+ async def execute_transaction(request: Request, transaction_data: TransactionRequest):
52
+ try:
53
+ passport = request.state.policy_result.passport
54
+
55
+ # Additional business logic validation
56
+ if transaction_data.amount <= 0:
57
+ raise HTTPException(status_code=400, detail="Invalid transaction amount")
58
+
59
+ # Check if required fields are provided
60
+ if not transaction_data.source_account_id or not transaction_data.destination_account_id:
61
+ raise HTTPException(status_code=400, detail="Source and destination accounts are required")
62
+
63
+ # Process transaction using your financial system
64
+ transaction_id = await process_transaction({
65
+ "transaction_type": transaction_data.transaction_type,
66
+ "amount": transaction_data.amount,
67
+ "currency": transaction_data.currency,
68
+ "asset_class": transaction_data.asset_class,
69
+ "source_account_id": transaction_data.source_account_id,
70
+ "destination_account_id": transaction_data.destination_account_id,
71
+ "source_account_type": transaction_data.source_account_type,
72
+ "destination_account_type": transaction_data.destination_account_type,
73
+ "counterparty_id": transaction_data.counterparty_id,
74
+ "idempotency_key": transaction_data.idempotency_key,
75
+ "agent_id": passport.passport_id,
76
+ "agent_name": passport.metadata.get("template_name", "Unknown Agent") if passport.metadata else "Unknown Agent"
77
+ })
78
+
79
+ # Log the transaction
80
+ print(f"Transaction processed: {transaction_id} for {transaction_data.amount} {transaction_data.currency} by agent {passport.passport_id}")
81
+
82
+ return TransactionResponse(
83
+ success=True,
84
+ transaction_id=transaction_id,
85
+ transaction_type=transaction_data.transaction_type,
86
+ amount=transaction_data.amount,
87
+ currency=transaction_data.currency,
88
+ asset_class=transaction_data.asset_class,
89
+ status="processed",
90
+ decision_id=request.state.policy_result.decision_id
91
+ )
92
+
93
+ except Exception as e:
94
+ print(f"Transaction processing error: {e}")
95
+ raise HTTPException(status_code=500, detail="Internal server error")
96
+
97
+ @app.post("/finance/transaction/batch")
98
+ @require_policy("finance.transaction.execute.v1")
99
+ async def execute_batch_transactions(request: Request, batch_data: BatchTransactionRequest):
100
+ try:
101
+ passport = request.state.policy_result.passport
102
+
103
+ # Group transactions by counterparty for exposure checking
104
+ counterparty_totals = {}
105
+ for transaction in batch_data.transactions:
106
+ counterparty = transaction.counterparty_id or "default"
107
+ counterparty_totals[counterparty] = counterparty_totals.get(counterparty, 0) + transaction.amount
108
+
109
+ # Check counterparty exposure limits
110
+ for counterparty, total_amount in counterparty_totals.items():
111
+ max_exposure = passport.limits.get("finance", {}).get("transaction", {}).get("max_exposure_per_counterparty_usd")
112
+ if max_exposure and total_amount > max_exposure:
113
+ raise HTTPException(
114
+ status_code=403,
115
+ detail={
116
+ "error": "Batch total exceeds counterparty exposure limit",
117
+ "counterparty": counterparty,
118
+ "total": total_amount,
119
+ "limit": max_exposure
120
+ }
121
+ )
122
+
123
+ # Process batch transactions
124
+ results = await asyncio.gather(*[
125
+ process_transaction({
126
+ "transaction_type": transaction.transaction_type,
127
+ "amount": transaction.amount,
128
+ "currency": transaction.currency,
129
+ "asset_class": transaction.asset_class,
130
+ "source_account_id": transaction.source_account_id,
131
+ "destination_account_id": transaction.destination_account_id,
132
+ "source_account_type": transaction.source_account_type,
133
+ "destination_account_type": transaction.destination_account_type,
134
+ "counterparty_id": transaction.counterparty_id,
135
+ "idempotency_key": transaction.idempotency_key,
136
+ "agent_id": passport.passport_id
137
+ })
138
+ for transaction in batch_data.transactions
139
+ ])
140
+
141
+ return {
142
+ "success": True,
143
+ "processed": len(results),
144
+ "counterparty_totals": counterparty_totals,
145
+ "decision_id": request.state.policy_result.decision_id
146
+ }
147
+
148
+ except Exception as e:
149
+ print(f"Batch transaction error: {e}")
150
+ raise HTTPException(status_code=500, detail="Internal server error")
151
+
152
+ @app.get("/finance/transaction/{transaction_id}")
153
+ @require_policy("finance.transaction.execute.v1")
154
+ async def get_transaction_status(request: Request, transaction_id: str):
155
+ try:
156
+ passport = request.state.policy_result.passport
157
+
158
+ transaction_info = await get_transaction_status(transaction_id, passport.passport_id)
159
+
160
+ if not transaction_info:
161
+ raise HTTPException(status_code=404, detail="Transaction not found")
162
+
163
+ return transaction_info
164
+
165
+ except Exception as e:
166
+ print(f"Transaction status error: {e}")
167
+ raise HTTPException(status_code=500, detail="Internal server error")
168
+
169
+ @app.post("/finance/transaction/{transaction_id}/cancel")
170
+ @require_policy("finance.transaction.execute.v1")
171
+ async def cancel_transaction(request: Request, transaction_id: str, cancel_data: CancelRequest):
172
+ try:
173
+ passport = request.state.policy_result.passport
174
+
175
+ cancel_id = await cancel_transaction_payment({
176
+ "transaction_id": transaction_id,
177
+ "reason": cancel_data.reason,
178
+ "agent_id": passport.passport_id
179
+ })
180
+
181
+ return {
182
+ "success": True,
183
+ "cancel_id": cancel_id,
184
+ "transaction_id": transaction_id,
185
+ "status": "cancelled",
186
+ "decision_id": request.state.policy_result.decision_id
187
+ }
188
+
189
+ except Exception as e:
190
+ print(f"Transaction cancellation error: {e}")
191
+ raise HTTPException(status_code=500, detail="Internal server error")
192
+
193
+ async def process_transaction(transaction_data: dict) -> str:
194
+ """Mock transaction processing function"""
195
+ # Simulate financial system call
196
+ await asyncio.sleep(0.15)
197
+
198
+ # Log transaction details for audit
199
+ print(f"Processing transaction: {transaction_data}")
200
+
201
+ return f"txn_{asyncio.get_event_loop().time()}_{hash(str(transaction_data)) % 1000000}"
202
+
203
+ async def get_transaction_status(transaction_id: str, agent_id: str) -> Optional[TransactionStatus]:
204
+ """Mock transaction status lookup"""
205
+ await asyncio.sleep(0.05)
206
+ return TransactionStatus(
207
+ transaction_id=transaction_id,
208
+ status="completed",
209
+ created_at=datetime.now().isoformat(),
210
+ transaction_type="buy",
211
+ amount=10000,
212
+ currency="USD",
213
+ asset_class="equity",
214
+ source_account_id="acc_client_123",
215
+ destination_account_id="acc_trading_456"
216
+ )
217
+
218
+ async def cancel_transaction_payment(cancel_data: dict) -> str:
219
+ """Mock transaction cancellation function"""
220
+ # Simulate transaction cancellation
221
+ await asyncio.sleep(0.1)
222
+
223
+ print(f"Cancelling transaction: {cancel_data}")
224
+
225
+ return f"cancel_{asyncio.get_event_loop().time()}_{hash(str(cancel_data)) % 1000000}"
226
+
227
+ if __name__ == "__main__":
228
+ import uvicorn
229
+ print("Financial transaction service starting...")
230
+ print("Protected by APort finance.transaction.execute.v1 policy pack")
231
+ uvicorn.run(app, host="0.0.0.0", port=8000)
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Minimal Example: finance.transaction.execute.v1 Policy
3
+ *
4
+ * This is a quick-start example showing the basic usage of the finance.transaction.execute.v1 policy.
5
+ * For production use, see the full express.example.js file.
6
+ */
7
+
8
+ const express = require("express");
9
+ const { requirePolicy } = require("@aporthq/middleware-express");
10
+
11
+ const app = express();
12
+ app.use(express.json());
13
+
14
+ // Minimal transaction endpoint with policy protection
15
+ app.post(
16
+ "/transaction",
17
+ requirePolicy("finance.transaction.execute.v1"),
18
+ async (req, res) => {
19
+ try {
20
+ const {
21
+ transaction_type,
22
+ amount,
23
+ currency,
24
+ asset_class,
25
+ source_account_id,
26
+ destination_account_id,
27
+ } = req.body;
28
+ const passport = req.policyResult.passport;
29
+
30
+ // Process the transaction (your business logic here)
31
+ const transaction_id = `txn_${Date.now()}`;
32
+
33
+ console.log(
34
+ `Transaction processed: ${transaction_id} for ${amount} ${currency}`
35
+ );
36
+
37
+ res.json({
38
+ success: true,
39
+ transaction_id,
40
+ transaction_type,
41
+ amount,
42
+ currency,
43
+ asset_class,
44
+ decision_id: req.policyResult.decision_id,
45
+ });
46
+ } catch (error) {
47
+ console.error("Transaction error:", error);
48
+ res.status(500).json({ error: "Internal server error" });
49
+ }
50
+ }
51
+ );
52
+
53
+ // Example client request
54
+ const exampleRequest = {
55
+ transaction_type: "buy",
56
+ amount: 10000, // $100.00 in cents
57
+ currency: "USD",
58
+ asset_class: "equity",
59
+ source_account_id: "acc_client_123",
60
+ destination_account_id: "acc_trading_456",
61
+ source_account_type: "client_funds",
62
+ destination_account_type: "trading",
63
+ counterparty_id: "cpty_broker_789",
64
+ idempotency_key: "txn-buy-123",
65
+ };
66
+
67
+ console.log("Example request:", JSON.stringify(exampleRequest, null, 2));
68
+
69
+ const PORT = process.env.PORT || 3000;
70
+ app.listen(PORT, () => {
71
+ console.log(`Minimal transaction service running on port ${PORT}`);
72
+ console.log("Protected by APort finance.transaction.execute.v1 policy");
73
+ console.log(
74
+ `Try: curl -X POST http://localhost:${PORT}/transaction -H "Content-Type: application/json" -d '${JSON.stringify(
75
+ exampleRequest
76
+ )}'`
77
+ );
78
+ });
@@ -0,0 +1,189 @@
1
+ {
2
+ "id": "finance.transaction.execute.v1",
3
+ "name": "Financial Transaction Execution Policy",
4
+ "description": "Pre-action governance for agent-initiated financial transactions like trades or transfers. Enforces rules on asset classes, exposure limits, and account types.",
5
+ "version": "1.0.0",
6
+ "status": "active",
7
+ "requires_capabilities": ["finance.transaction"],
8
+ "min_assurance": "L3",
9
+ "limits_required": [
10
+ "allowed_transaction_types",
11
+ "allowed_asset_classes",
12
+ "max_exposure_per_tx_usd",
13
+ "allowed_source_account_types",
14
+ "restricted_source_account_types",
15
+ "max_exposure_per_counterparty_usd",
16
+ "approval_required"
17
+ ],
18
+ "required_fields": [
19
+ "transaction_type",
20
+ "amount",
21
+ "currency",
22
+ "asset_class",
23
+ "source_account_id",
24
+ "destination_account_id"
25
+ ],
26
+ "optional_fields": [
27
+ "source_account_type",
28
+ "idempotency_key",
29
+ "destination_account_type",
30
+ "counterparty_id"
31
+ ],
32
+ "enforcement": {
33
+ "transaction_type_allowed": true,
34
+ "asset_class_allowed": true,
35
+ "exposure_limit_enforced": true,
36
+ "account_segregation_enforced": true,
37
+ "segregation_of_funds_enforced": true,
38
+ "counterparty_exposure_enforced": true,
39
+ "idempotency_required": true
40
+ },
41
+ "mcp": {
42
+ "require_allowlisted_if_present": true
43
+ },
44
+ "advice": [
45
+ "Cache /verify with ETag; 60s TTL",
46
+ "Subscribe to status webhooks for instant suspend",
47
+ "Log all transaction attempts for Verifiable Attestation",
48
+ "Implement daily exposure tracking per counterparty to prevent concentration risk",
49
+ "Always use unique idempotency keys to prevent duplicate transactions",
50
+ "Maintain strict segregation between client and proprietary funds",
51
+ "Monitor counterparty exposure limits to prevent over-concentration",
52
+ "Implement real-time balance checks before transaction execution",
53
+ "Use progressive limits for new counterparties",
54
+ "Maintain audit trails for all financial transactions"
55
+ ],
56
+ "cache": {
57
+ "default_ttl_seconds": 60,
58
+ "suspend_invalidate_seconds": 30
59
+ },
60
+ "required_context": {
61
+ "$schema": "http://json-schema.org/draft-07/schema#",
62
+ "type": "object",
63
+ "required": [
64
+ "transaction_type",
65
+ "amount",
66
+ "currency",
67
+ "asset_class",
68
+ "source_account_id",
69
+ "destination_account_id"
70
+ ],
71
+ "properties": {
72
+ "transaction_type": {
73
+ "type": "string",
74
+ "enum": ["buy", "sell", "transfer", "short_sell"],
75
+ "description": "The type of financial transaction being executed."
76
+ },
77
+ "amount": {
78
+ "type": "integer",
79
+ "description": "Transaction amount in minor units (e.g., cents)."
80
+ },
81
+ "currency": {
82
+ "type": "string",
83
+ "pattern": "^[A-Z]{3}$",
84
+ "description": "ISO 4217 currency code."
85
+ },
86
+ "asset_class": {
87
+ "type": "string",
88
+ "description": "The class of the asset being transacted (e.g., 'equity', 'bond', 'crypto', 'cash')."
89
+ },
90
+ "source_account_id": {
91
+ "type": "string",
92
+ "description": "The ID of the account from which funds/assets are being moved."
93
+ },
94
+ "source_account_type": {
95
+ "type": "string",
96
+ "description": "The type of the source account (e.g., 'client_funds', 'trust_funds', 'proprietary')."
97
+ },
98
+ "destination_account_id": {
99
+ "type": "string",
100
+ "description": "The ID of the destination account."
101
+ },
102
+ "idempotency_key": {
103
+ "type": "string",
104
+ "description": "Idempotency key to prevent duplicate transactions."
105
+ },
106
+ "destination_account_type": {
107
+ "type": "string",
108
+ "description": "The type of the destination account (e.g., 'client_funds', 'proprietary'). Solves for Segregation of Funds."
109
+ },
110
+ "counterparty_id": {
111
+ "type": "string",
112
+ "description": "A unique identifier for the counterparty in a trade. Solves for Counterparty Exposure."
113
+ },
114
+ "mcp_servers": {
115
+ "type": "array",
116
+ "items": { "type": "string" },
117
+ "description": "MCP servers being used in this request (e.g., [\"https://mcp.plaid.com\"])"
118
+ },
119
+ "mcp_tools": {
120
+ "type": "array",
121
+ "items": { "type": "string" },
122
+ "description": "MCP tools being used in this request (e.g., [\"plaid.transactions.execute\"])"
123
+ },
124
+ "mcp_server": {
125
+ "type": "string",
126
+ "description": "Single MCP server being used (backward compatibility - use mcp_servers array for multiple)"
127
+ },
128
+ "mcp_tool": {
129
+ "type": "string",
130
+ "description": "Single MCP tool being used (backward compatibility - use mcp_tools array for multiple)"
131
+ },
132
+ "mcp_session": {
133
+ "type": "string",
134
+ "description": "MCP session identifier for audit trail (optional)"
135
+ }
136
+ }
137
+ },
138
+ "evaluation_rules": [
139
+ {
140
+ "name": "passport_status_active",
141
+ "condition": "passport.status == 'active'",
142
+ "deny_code": "oap.passport_suspended"
143
+ },
144
+ {
145
+ "name": "transaction_type_allowed",
146
+ "condition": "context.transaction_type in passport.limits.finance.transaction.allowed_transaction_types",
147
+ "deny_code": "oap.action_forbidden"
148
+ },
149
+ {
150
+ "name": "asset_class_allowed",
151
+ "condition": "context.asset_class in passport.limits.finance.transaction.allowed_asset_classes",
152
+ "deny_code": "oap.asset_class_forbidden"
153
+ },
154
+ {
155
+ "name": "exposure_limit_check",
156
+ "condition": "context.amount <= passport.limits.finance.transaction.max_exposure_per_tx_usd",
157
+ "deny_code": "oap.limit_exceeded"
158
+ },
159
+ {
160
+ "name": "account_segregation_check",
161
+ "condition": "context.source_account_type not in passport.limits.finance.transaction.restricted_source_account_types",
162
+ "deny_code": "oap.account_type_restricted"
163
+ },
164
+ {
165
+ "name": "account_type_check",
166
+ "condition": "context.source_account_type in passport.limits.finance.transaction.allowed_source_account_types",
167
+ "deny_code": "oap.account_type_restricted",
168
+ "description": "Restricts agent access to specific account classes (e.g., trust, retirement)."
169
+ },
170
+ {
171
+ "name": "segregation_of_funds_check",
172
+ "condition": "not (context.source_account_type == 'client_funds' and context.destination_account_type == 'proprietary')",
173
+ "deny_code": "oap.commingling_of_funds_forbidden",
174
+ "description": "Enforces policies preventing the commingling of client and corporate funds."
175
+ },
176
+ {
177
+ "name": "counterparty_exposure_check",
178
+ "condition": "daily_counterparty_total[context.counterparty_id] + context.amount <= passport.limits.finance.transaction.max_exposure_per_counterparty_usd",
179
+ "deny_code": "oap.counterparty_limit_exceeded",
180
+ "description": "Limits total daily exposure to any single counterparty."
181
+ },
182
+ {
183
+ "name": "idempotency_check",
184
+ "condition": "context.idempotency_key not in recent_keys",
185
+ "deny_code": "oap.idempotency_conflict",
186
+ "description": "Idempotency key must be unique to prevent duplicate transactions."
187
+ }
188
+ ]
189
+ }
@@ -0,0 +1,12 @@
1
+ {"name":"allow_buy_equity_10k","context":{"transaction_type":"buy","amount":10000,"currency":"USD","asset_class":"equity","source_account_id":"acc_client_123","destination_account_id":"acc_trading_456","source_account_type":"client_funds","destination_account_type":"trading","counterparty_id":"cpty_broker_789","idempotency_key":"txn-buy-1001"}}
2
+ {"name":"deny_transaction_type_forbidden","context":{"transaction_type":"short_sell","amount":5000,"currency":"USD","asset_class":"equity","source_account_id":"acc_client_123","destination_account_id":"acc_trading_456","idempotency_key":"txn-short-1002"}}
3
+ {"name":"deny_asset_class_forbidden","context":{"transaction_type":"buy","amount":5000,"currency":"USD","asset_class":"commodity","source_account_id":"acc_client_123","destination_account_id":"acc_trading_456","idempotency_key":"txn-commodity-1003"}}
4
+ {"name":"deny_over_exposure_limit","context":{"transaction_type":"buy","amount":60000,"currency":"USD","asset_class":"equity","source_account_id":"acc_client_123","destination_account_id":"acc_trading_456","idempotency_key":"txn-over-limit-1004"}}
5
+ {"name":"deny_restricted_account_type","context":{"transaction_type":"buy","amount":5000,"currency":"USD","asset_class":"equity","source_account_id":"acc_trust_123","destination_account_id":"acc_trading_456","source_account_type":"trust_funds","idempotency_key":"txn-trust-1005"}}
6
+ {"name":"deny_account_type_not_allowed","context":{"transaction_type":"buy","amount":5000,"currency":"USD","asset_class":"equity","source_account_id":"acc_retirement_123","destination_account_id":"acc_trading_456","source_account_type":"retirement","idempotency_key":"txn-retirement-1006"}}
7
+ {"name":"deny_commingling_funds","context":{"transaction_type":"transfer","amount":5000,"currency":"USD","asset_class":"cash","source_account_id":"acc_client_123","destination_account_id":"acc_proprietary_456","source_account_type":"client_funds","destination_account_type":"proprietary","idempotency_key":"txn-commingling-1007"}}
8
+ {"name":"allow_sell_bond_5k","context":{"transaction_type":"sell","amount":5000,"currency":"USD","asset_class":"bond","source_account_id":"acc_trading_123","destination_account_id":"acc_client_456","source_account_type":"trading","destination_account_type":"client_funds","counterparty_id":"cpty_broker_789","idempotency_key":"txn-sell-1008"}}
9
+ {"name":"allow_transfer_crypto_2k","context":{"transaction_type":"transfer","amount":2000,"currency":"USD","asset_class":"crypto","source_account_id":"acc_trading_123","destination_account_id":"acc_cold_storage_456","source_account_type":"trading","destination_account_type":"custodial","counterparty_id":"cpty_exchange_789","idempotency_key":"txn-transfer-1009"}}
10
+ {"name":"deny_counterparty_exposure_exceeded","context":{"transaction_type":"buy","amount":250000,"currency":"USD","asset_class":"equity","source_account_id":"acc_client_123","destination_account_id":"acc_trading_456","counterparty_id":"cpty_broker_789","idempotency_key":"txn-counterparty-1010"}}
11
+ {"name":"deny_missing_required_fields","context":{"transaction_type":"buy","amount":5000,"currency":"USD","asset_class":"equity","idempotency_key":"txn-missing-1011"}}
12
+ {"name":"allow_cash_transfer_1k","context":{"transaction_type":"transfer","amount":1000,"currency":"USD","asset_class":"cash","source_account_id":"acc_client_123","destination_account_id":"acc_savings_456","source_account_type":"client_funds","destination_account_type":"client_funds","idempotency_key":"txn-cash-1012"}}
@@ -0,0 +1,12 @@
1
+ {"name":"allow_buy_equity_10k","expected":{"allow":true,"reasons":[{"code":"oap.allowed","message":"Transaction within limits and policy requirements","severity":"info"}]}}
2
+ {"name":"deny_transaction_type_forbidden","expected":{"allow":false,"reasons":[{"code":"oap.action_forbidden","message":"Transaction type short_sell is not allowed","severity":"error"}]}}
3
+ {"name":"deny_asset_class_forbidden","expected":{"allow":false,"reasons":[{"code":"oap.asset_class_forbidden","message":"Asset class commodity is not allowed","severity":"error"}]}}
4
+ {"name":"deny_over_exposure_limit","expected":{"allow":false,"reasons":[{"code":"oap.limit_exceeded","message":"Amount 60000 exceeds maximum exposure limit 50000","severity":"error"}]}}
5
+ {"name":"deny_restricted_account_type","expected":{"allow":false,"reasons":[{"code":"oap.account_type_restricted","message":"Source account type trust_funds is restricted","severity":"error"}]}}
6
+ {"name":"deny_account_type_not_allowed","expected":{"allow":false,"reasons":[{"code":"oap.account_type_restricted","message":"Source account type retirement is not allowed","severity":"error"}]}}
7
+ {"name":"deny_commingling_funds","expected":{"allow":false,"reasons":[{"code":"oap.commingling_of_funds_forbidden","message":"Cannot transfer from client funds to proprietary accounts","severity":"error"}]}}
8
+ {"name":"allow_sell_bond_5k","expected":{"allow":true,"reasons":[{"code":"oap.allowed","message":"Transaction within limits and policy requirements","severity":"info"}]}}
9
+ {"name":"allow_transfer_crypto_2k","expected":{"allow":true,"reasons":[{"code":"oap.allowed","message":"Transaction within limits and policy requirements","severity":"info"}]}}
10
+ {"name":"deny_counterparty_exposure_exceeded","expected":{"allow":false,"reasons":[{"code":"oap.counterparty_limit_exceeded","message":"Amount 250000 exceeds counterparty exposure limit 200000","severity":"error"}]}}
11
+ {"name":"deny_missing_required_fields","expected":{"allow":false,"reasons":[{"code":"oap.invalid_context","message":"Missing required fields: source_account_id, destination_account_id","severity":"error"}]}}
12
+ {"name":"allow_cash_transfer_1k","expected":{"allow":true,"reasons":[{"code":"oap.allowed","message":"Transaction within limits and policy requirements","severity":"info"}]}}
@@ -0,0 +1,42 @@
1
+ {
2
+ "passport_id": "ap_a2d10232c6534523812423eec8a1425c45678",
3
+ "kind": "instance",
4
+ "spec_version": "oap/1.0",
5
+ "owner_id": "org_demo_co",
6
+ "owner_type": "org",
7
+ "assurance_level": "L3",
8
+ "status": "active",
9
+ "capabilities": [
10
+ {
11
+ "id": "finance.transaction",
12
+ "params": {
13
+ "max_amount": 100000,
14
+ "currency": "USD"
15
+ }
16
+ }
17
+ ],
18
+ "limits": {
19
+ "finance.transaction": {
20
+ "allowed_transaction_types": ["buy", "sell", "transfer"],
21
+ "allowed_asset_classes": ["equity", "bond", "crypto", "cash"],
22
+ "max_exposure_per_tx_usd": 50000,
23
+ "allowed_source_account_types": [
24
+ "client_funds",
25
+ "trading",
26
+ "proprietary"
27
+ ],
28
+ "restricted_source_account_types": ["trust_funds", "retirement"],
29
+ "max_exposure_per_counterparty_usd": 200000,
30
+ "require_assurance_at_least": "L3",
31
+ "idempotency_required": true
32
+ }
33
+ },
34
+ "regions": ["US", "CA", "EU"],
35
+ "metadata": {
36
+ "template_name": "Demo Transaction Agent",
37
+ "description": "Instance for financial transaction operations"
38
+ },
39
+ "created_at": "2025-01-30T00:00:00Z",
40
+ "updated_at": "2025-01-30T00:00:00Z",
41
+ "version": "1.0.0"
42
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "passport_id": "550e8400-e29b-41d4-a716-446655440001",
3
+ "kind": "template",
4
+ "spec_version": "oap/1.0",
5
+ "owner_id": "org_demo_co",
6
+ "owner_type": "org",
7
+ "assurance_level": "L3",
8
+ "status": "active",
9
+ "capabilities": [
10
+ {
11
+ "id": "finance.transaction",
12
+ "params": {
13
+ "max_amount": 100000,
14
+ "currency": "USD"
15
+ }
16
+ }
17
+ ],
18
+ "limits": {
19
+ "finance.transaction": {
20
+ "allowed_transaction_types": ["buy", "sell", "transfer"],
21
+ "allowed_asset_classes": ["equity", "bond", "crypto", "cash"],
22
+ "max_exposure_per_tx_usd": 50000,
23
+ "allowed_source_account_types": [
24
+ "client_funds",
25
+ "trading",
26
+ "proprietary"
27
+ ],
28
+ "restricted_source_account_types": ["trust_funds", "retirement"],
29
+ "max_exposure_per_counterparty_usd": 200000,
30
+ "require_assurance_at_least": "L3",
31
+ "idempotency_required": true
32
+ }
33
+ },
34
+ "regions": ["US", "CA", "EU"],
35
+ "metadata": {
36
+ "template_name": "Demo Transaction Template",
37
+ "description": "Template for financial transaction operations"
38
+ },
39
+ "created_at": "2025-01-30T00:00:00Z",
40
+ "updated_at": "2025-01-30T00:00:00Z",
41
+ "version": "1.0.0"
42
+ }