@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,279 @@
1
+ from fastapi import FastAPI, HTTPException, Request, Query
2
+ from pydantic import BaseModel
3
+ from aport.middleware import require_policy
4
+ import asyncio
5
+ from typing import List, Optional, Dict, Any
6
+ from datetime import datetime
7
+
8
+ app = FastAPI(title="Data Access Governance Service", version="1.0.0")
9
+
10
+ class DataExportRequest(BaseModel):
11
+ data_classification: str
12
+ accessing_entity_id: str
13
+ accessing_entity_type: str
14
+ resource_id: str
15
+ action_type: Optional[str] = "export"
16
+ jurisdiction: Optional[str] = None
17
+ row_count: int
18
+ destination_jurisdiction: Optional[str] = None
19
+ export_format: str = "json"
20
+ filters: Optional[Dict[str, Any]] = None
21
+
22
+ class DataAccessResponse(BaseModel):
23
+ success: bool
24
+ access_id: str
25
+ data_classification: str
26
+ resource_id: str
27
+ action_type: str
28
+ status: str
29
+ decision_id: str
30
+
31
+ class DataExportResponse(BaseModel):
32
+ success: bool
33
+ export_id: str
34
+ data_classification: str
35
+ row_count: int
36
+ export_format: str
37
+ status: str
38
+ decision_id: str
39
+
40
+ class BalanceInfo(BaseModel):
41
+ account_id: str
42
+ balance_usd: int
43
+ currency: str
44
+ last_updated: str
45
+
46
+ class AuditLog(BaseModel):
47
+ access_id: str
48
+ data_classification: str
49
+ resource_id: str
50
+ action_type: str
51
+ timestamp: str
52
+ agent_id: str
53
+
54
+ @app.get("/data/access")
55
+ @require_policy("governance.data.access.v1")
56
+ async def access_data(
57
+ request: Request,
58
+ data_classification: str = Query(...),
59
+ accessing_entity_id: str = Query(...),
60
+ accessing_entity_type: str = Query(...),
61
+ resource_id: str = Query(...),
62
+ action_type: str = Query("read"),
63
+ jurisdiction: Optional[str] = Query(None),
64
+ row_count: Optional[int] = Query(None),
65
+ destination_jurisdiction: Optional[str] = Query(None),
66
+ resource_attributes: Optional[str] = Query(None)
67
+ ):
68
+ try:
69
+ passport = request.state.policy_result.passport
70
+
71
+ # Additional business logic validation
72
+ if not data_classification or not accessing_entity_id or not resource_id:
73
+ raise HTTPException(status_code=400, detail="Missing required parameters")
74
+
75
+ # Process data access using your data system
76
+ access_id = await process_data_access({
77
+ "data_classification": data_classification,
78
+ "accessing_entity_id": accessing_entity_id,
79
+ "accessing_entity_type": accessing_entity_type,
80
+ "resource_id": resource_id,
81
+ "action_type": action_type,
82
+ "jurisdiction": jurisdiction,
83
+ "row_count": row_count,
84
+ "destination_jurisdiction": destination_jurisdiction,
85
+ "resource_attributes": json.loads(resource_attributes) if resource_attributes else None,
86
+ "agent_id": passport.passport_id,
87
+ "agent_name": passport.metadata.get("template_name", "Unknown Agent") if passport.metadata else "Unknown Agent"
88
+ })
89
+
90
+ # Log the data access
91
+ print(f"Data access processed: {access_id} for {data_classification} data by agent {passport.passport_id}")
92
+
93
+ return DataAccessResponse(
94
+ success=True,
95
+ access_id=access_id,
96
+ data_classification=data_classification,
97
+ resource_id=resource_id,
98
+ action_type=action_type,
99
+ status="processed",
100
+ decision_id=request.state.policy_result.decision_id
101
+ )
102
+
103
+ except Exception as e:
104
+ print(f"Data access processing error: {e}")
105
+ raise HTTPException(status_code=500, detail="Internal server error")
106
+
107
+ @app.post("/data/export")
108
+ @require_policy("governance.data.access.v1")
109
+ async def export_data(request: Request, export_data: DataExportRequest):
110
+ try:
111
+ passport = request.state.policy_result.passport
112
+
113
+ # Check row count limits
114
+ max_rows_per_export = passport.limits.get("data", {}).get("access", {}).get("max_rows_per_export")
115
+ if max_rows_per_export and export_data.row_count > max_rows_per_export:
116
+ raise HTTPException(
117
+ status_code=403,
118
+ detail={
119
+ "error": "Export row count exceeds limit",
120
+ "row_count": export_data.row_count,
121
+ "limit": max_rows_per_export
122
+ }
123
+ )
124
+
125
+ # Process data export
126
+ export_id = await process_data_export({
127
+ "data_classification": export_data.data_classification,
128
+ "accessing_entity_id": export_data.accessing_entity_id,
129
+ "accessing_entity_type": export_data.accessing_entity_type,
130
+ "resource_id": export_data.resource_id,
131
+ "action_type": export_data.action_type,
132
+ "jurisdiction": export_data.jurisdiction,
133
+ "row_count": export_data.row_count,
134
+ "destination_jurisdiction": export_data.destination_jurisdiction,
135
+ "export_format": export_data.export_format,
136
+ "filters": export_data.filters,
137
+ "agent_id": passport.passport_id
138
+ })
139
+
140
+ return DataExportResponse(
141
+ success=True,
142
+ export_id=export_id,
143
+ data_classification=export_data.data_classification,
144
+ row_count=export_data.row_count,
145
+ export_format=export_data.export_format,
146
+ status="exported",
147
+ decision_id=request.state.policy_result.decision_id
148
+ )
149
+
150
+ except Exception as e:
151
+ print(f"Data export error: {e}")
152
+ raise HTTPException(status_code=500, detail="Internal server error")
153
+
154
+ @app.get("/data/balance/{account_id}")
155
+ @require_policy("governance.data.access.v1")
156
+ async def get_balance(
157
+ request: Request,
158
+ account_id: str,
159
+ accessing_entity_id: str = Query(...),
160
+ accessing_entity_type: str = Query(...)
161
+ ):
162
+ try:
163
+ passport = request.state.policy_result.passport
164
+
165
+ # Get account balance
166
+ balance_info = await get_account_balance(account_id, accessing_entity_id, passport.passport_id)
167
+
168
+ if not balance_info:
169
+ raise HTTPException(status_code=404, detail="Account not found")
170
+
171
+ # Check balance inquiry limits
172
+ balance_inquiry_cap = passport.limits.get("data", {}).get("access", {}).get("balance_inquiry_cap_usd")
173
+ if balance_inquiry_cap and balance_info["balance_usd"] >= balance_inquiry_cap:
174
+ raise HTTPException(
175
+ status_code=403,
176
+ detail={
177
+ "error": "Account balance exceeds inquiry cap",
178
+ "balance": balance_info["balance_usd"],
179
+ "cap": balance_inquiry_cap
180
+ }
181
+ )
182
+
183
+ return BalanceInfo(
184
+ account_id=account_id,
185
+ balance_usd=balance_info["balance_usd"],
186
+ currency=balance_info["currency"],
187
+ last_updated=balance_info["last_updated"]
188
+ )
189
+
190
+ except Exception as e:
191
+ print(f"Balance inquiry error: {e}")
192
+ raise HTTPException(status_code=500, detail="Internal server error")
193
+
194
+ @app.get("/data/access/audit")
195
+ @require_policy("governance.data.access.v1")
196
+ async def get_audit_logs(
197
+ request: Request,
198
+ start_date: Optional[str] = Query(None),
199
+ end_date: Optional[str] = Query(None),
200
+ data_classification: Optional[str] = Query(None)
201
+ ):
202
+ try:
203
+ passport = request.state.policy_result.passport
204
+
205
+ audit_logs = await get_data_access_audit_logs({
206
+ "start_date": start_date,
207
+ "end_date": end_date,
208
+ "data_classification": data_classification,
209
+ "agent_id": passport.passport_id
210
+ })
211
+
212
+ return {
213
+ "success": True,
214
+ "audit_logs": audit_logs,
215
+ "total_entries": len(audit_logs),
216
+ "decision_id": request.state.policy_result.decision_id
217
+ }
218
+
219
+ except Exception as e:
220
+ print(f"Audit log error: {e}")
221
+ raise HTTPException(status_code=500, detail="Internal server error")
222
+
223
+ async def process_data_access(data_access_data: dict) -> str:
224
+ """Mock data access processing function"""
225
+ # Simulate data system call
226
+ await asyncio.sleep(0.1)
227
+
228
+ # Log data access details for audit
229
+ print(f"Processing data access: {data_access_data}")
230
+
231
+ return f"access_{asyncio.get_event_loop().time()}_{hash(str(data_access_data)) % 1000000}"
232
+
233
+ async def process_data_export(export_data: dict) -> str:
234
+ """Mock data export processing function"""
235
+ # Simulate data export processing
236
+ await asyncio.sleep(0.2)
237
+
238
+ print(f"Processing data export: {export_data}")
239
+
240
+ return f"export_{asyncio.get_event_loop().time()}_{hash(str(export_data)) % 1000000}"
241
+
242
+ async def get_account_balance(account_id: str, accessing_entity_id: str, agent_id: str) -> Optional[Dict[str, Any]]:
243
+ """Mock account balance lookup"""
244
+ await asyncio.sleep(0.05)
245
+ return {
246
+ "account_id": account_id,
247
+ "balance_usd": 50000, # $500.00 in cents
248
+ "currency": "USD",
249
+ "last_updated": datetime.now().isoformat()
250
+ }
251
+
252
+ async def get_data_access_audit_logs(params: dict) -> List[Dict[str, Any]]:
253
+ """Mock audit log lookup"""
254
+ await asyncio.sleep(0.1)
255
+ return [
256
+ {
257
+ "access_id": "access_123",
258
+ "data_classification": "PII",
259
+ "resource_id": "user_456",
260
+ "action_type": "read",
261
+ "timestamp": datetime.now().isoformat(),
262
+ "agent_id": params["agent_id"]
263
+ },
264
+ {
265
+ "access_id": "access_124",
266
+ "data_classification": "Financial",
267
+ "resource_id": "account_789",
268
+ "action_type": "export",
269
+ "timestamp": datetime.now().isoformat(),
270
+ "agent_id": params["agent_id"]
271
+ }
272
+ ]
273
+
274
+ if __name__ == "__main__":
275
+ import uvicorn
276
+ import json
277
+ print("Data access governance service starting...")
278
+ print("Protected by APort governance.data.access.v1 policy pack")
279
+ uvicorn.run(app, host="0.0.0.0", port=8000)
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Minimal Example: governance.data.access.v1 Policy
3
+ *
4
+ * This is a quick-start example showing the basic usage of the governance.data.access.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 data access endpoint with policy protection
15
+ app.get(
16
+ "/data/access",
17
+ requirePolicy("governance.data.access.v1"),
18
+ async (req, res) => {
19
+ try {
20
+ const { data_classification, accessing_entity_id, resource_id } =
21
+ req.query;
22
+ const passport = req.policyResult.passport;
23
+
24
+ // Process the data access (your business logic here)
25
+ const access_id = `access_${Date.now()}`;
26
+
27
+ console.log(
28
+ `Data access processed: ${access_id} for ${data_classification} data`
29
+ );
30
+
31
+ res.json({
32
+ success: true,
33
+ access_id,
34
+ data_classification,
35
+ resource_id,
36
+ decision_id: req.policyResult.decision_id,
37
+ });
38
+ } catch (error) {
39
+ console.error("Data access error:", error);
40
+ res.status(500).json({ error: "Internal server error" });
41
+ }
42
+ }
43
+ );
44
+
45
+ // Example client request
46
+ const exampleRequest = {
47
+ data_classification: "PII",
48
+ accessing_entity_id: "emp_123",
49
+ accessing_entity_type: "employee",
50
+ resource_id: "user_456",
51
+ action_type: "read",
52
+ jurisdiction: "US",
53
+ row_count: 100,
54
+ };
55
+
56
+ console.log("Example request:", JSON.stringify(exampleRequest, null, 2));
57
+
58
+ const PORT = process.env.PORT || 3000;
59
+ app.listen(PORT, () => {
60
+ console.log(`Minimal data access service running on port ${PORT}`);
61
+ console.log("Protected by APort governance.data.access.v1 policy");
62
+ console.log(
63
+ `Try: curl -X GET "http://localhost:${PORT}/data/access?data_classification=PII&accessing_entity_id=emp_123&resource_id=user_456"`
64
+ );
65
+ });
@@ -0,0 +1,208 @@
1
+ {
2
+ "id": "governance.data.access.v1",
3
+ "name": "Data Access Governance Policy",
4
+ "description": "Pre-action governance for agent-initiated data access. Enforces controls based on data classification, sensitivity, entity access rights, and jurisdictional boundaries.",
5
+ "version": "1.0.0",
6
+ "status": "active",
7
+ "requires_capabilities": ["data.access"],
8
+ "min_assurance": "L3",
9
+ "limits_required": [
10
+ "allowed_classifications",
11
+ "permissions",
12
+ "allowed_jurisdictions",
13
+ "max_rows_per_export",
14
+ "allowed_destination_jurisdictions",
15
+ "balance_inquiry_cap_usd"
16
+ ],
17
+ "required_fields": [
18
+ "data_classification",
19
+ "accessing_entity_id",
20
+ "accessing_entity_type",
21
+ "resource_id"
22
+ ],
23
+ "optional_fields": [
24
+ "action_type",
25
+ "jurisdiction",
26
+ "row_count",
27
+ "destination_jurisdiction",
28
+ "resource_attributes"
29
+ ],
30
+ "enforcement": {
31
+ "classification_allowed": true,
32
+ "entity_type_allowed": true,
33
+ "jurisdiction_check": true,
34
+ "row_limit_enforced": true,
35
+ "data_locality_enforced": true,
36
+ "balance_inquiry_limited": true
37
+ },
38
+ "mcp": {
39
+ "require_allowlisted_if_present": true
40
+ },
41
+ "advice": [
42
+ "Cache /verify with ETag; 60s TTL",
43
+ "Subscribe to status webhooks for instant suspend",
44
+ "Log all data access attempts for Verifiable Attestation",
45
+ "Implement data classification tagging for all resources",
46
+ "Maintain entity access matrices for different data types",
47
+ "Use jurisdiction-aware data routing for compliance",
48
+ "Implement progressive disclosure for sensitive data access",
49
+ "Monitor access patterns for unusual behavior",
50
+ "Maintain audit trails for all data access operations",
51
+ "Use encryption for data in transit and at rest",
52
+ "Implement data retention policies based on classification"
53
+ ],
54
+ "cache": {
55
+ "default_ttl_seconds": 60,
56
+ "suspend_invalidate_seconds": 30
57
+ },
58
+ "required_context": {
59
+ "$schema": "http://json-schema.org/draft-07/schema#",
60
+ "type": "object",
61
+ "required": [
62
+ "data_classification",
63
+ "accessing_entity_id",
64
+ "accessing_entity_type",
65
+ "resource_id"
66
+ ],
67
+ "properties": {
68
+ "data_classification": {
69
+ "type": "string",
70
+ "description": "The classification of the data being accessed (e.g., 'PII', 'Financial', 'HR', 'ClientTier1')."
71
+ },
72
+ "accessing_entity_id": {
73
+ "type": "string",
74
+ "description": "The unique ID of the entity (user, agent, employee) attempting the access."
75
+ },
76
+ "accessing_entity_type": {
77
+ "type": "string",
78
+ "enum": ["employee", "client", "system_agent"],
79
+ "description": "The type of entity attempting the access."
80
+ },
81
+ "resource_id": {
82
+ "type": "string",
83
+ "description": "The unique ID of the data resource being accessed (e.g., account number, user profile ID)."
84
+ },
85
+ "action_type": {
86
+ "type": "string",
87
+ "enum": ["read", "export", "delete", "update"],
88
+ "default": "read",
89
+ "description": "The type of data access action being performed."
90
+ },
91
+ "jurisdiction": {
92
+ "type": "string",
93
+ "pattern": "^[A-Z]{2}$",
94
+ "description": "The ISO 3166-1 alpha-2 country code relevant to the data's jurisdiction."
95
+ },
96
+ "row_count": {
97
+ "type": "integer",
98
+ "description": "The number of rows/records being accessed or exported. Solves for Row Limits."
99
+ },
100
+ "destination_jurisdiction": {
101
+ "type": "string",
102
+ "pattern": "^[A-Z]{2}$",
103
+ "description": "The destination country code for data transfers. Solves for Data Locality."
104
+ },
105
+ "resource_attributes": {
106
+ "type": "object",
107
+ "description": "A flexible object for passing specific attributes of the resource being accessed. Solves for Balance Inquiry.",
108
+ "properties": {
109
+ "account_balance_usd": {
110
+ "type": "integer",
111
+ "description": "The balance of the account in USD minor units, used for balance-based access rules."
112
+ }
113
+ }
114
+ },
115
+ "mcp_servers": {
116
+ "type": "array",
117
+ "items": { "type": "string" },
118
+ "description": "MCP servers being used in this request (e.g., [\"https://mcp.snowflake.com\"])"
119
+ },
120
+ "mcp_tools": {
121
+ "type": "array",
122
+ "items": { "type": "string" },
123
+ "description": "MCP tools being used in this request (e.g., [\"snowflake.query.execute\"])"
124
+ },
125
+ "mcp_server": {
126
+ "type": "string",
127
+ "description": "Single MCP server being used (backward compatibility - use mcp_servers array for multiple)"
128
+ },
129
+ "mcp_tool": {
130
+ "type": "string",
131
+ "description": "Single MCP tool being used (backward compatibility - use mcp_tools array for multiple)"
132
+ },
133
+ "mcp_session": {
134
+ "type": "string",
135
+ "description": "MCP session identifier for audit trail (optional)"
136
+ }
137
+ }
138
+ },
139
+ "evaluation_rules": [
140
+ {
141
+ "name": "passport_status_active",
142
+ "condition": "passport.status == 'active'",
143
+ "deny_code": "oap.passport_suspended"
144
+ },
145
+ {
146
+ "name": "assurance_minimum",
147
+ "condition": "passport.assurance_level >= policy.min_assurance",
148
+ "deny_code": "oap.assurance_insufficient"
149
+ },
150
+ {
151
+ "name": "classification_allowed",
152
+ "condition": "context.data_classification in passport.limits.data.access.allowed_classifications",
153
+ "deny_code": "oap.classification_forbidden"
154
+ },
155
+ {
156
+ "name": "entity_type_allowed_for_action",
157
+ "condition": "context.accessing_entity_type in passport.limits.data.access.permissions[context.data_classification].allowed_entity_types",
158
+ "deny_code": "oap.entity_type_forbidden"
159
+ },
160
+ {
161
+ "name": "jurisdiction_check",
162
+ "condition": "context.jurisdiction in passport.limits.data.access.allowed_jurisdictions",
163
+ "deny_code": "oap.jurisdiction_blocked"
164
+ },
165
+ {
166
+ "name": "row_limit_check",
167
+ "condition": "context.row_count <= passport.limits.data.access.max_rows_per_export",
168
+ "deny_code": "oap.row_limit_exceeded",
169
+ "description": "Ensures the number of records in an export does not exceed the defined cap."
170
+ },
171
+ {
172
+ "name": "data_locality_check",
173
+ "condition": "context.destination_jurisdiction in passport.limits.data.access.allowed_destination_jurisdictions",
174
+ "deny_code": "oap.jurisdiction_blocked",
175
+ "description": "Restricts international transfers of sensitive data to approved jurisdictions."
176
+ },
177
+ {
178
+ "name": "balance_inquiry_limit",
179
+ "condition": "not context.resource_attributes.account_balance_usd or context.resource_attributes.account_balance_usd < passport.limits.data.access.balance_inquiry_cap_usd",
180
+ "deny_code": "oap.balance_inquiry_forbidden",
181
+ "description": "Limits agent access to view high-net-worth client balances."
182
+ },
183
+ {
184
+ "name": "assurance_minimum",
185
+ "condition": "passport.assurance_level >= policy.min_assurance",
186
+ "deny_code": "oap.assurance_insufficient",
187
+ "description": "Assurance level must meet minimum requirement for data access."
188
+ },
189
+ {
190
+ "name": "action_type_allowed",
191
+ "condition": "context.action_type in passport.limits.data.access.permissions[context.data_classification].allowed_actions",
192
+ "deny_code": "oap.action_forbidden",
193
+ "description": "Action type must be allowed for the data classification."
194
+ },
195
+ {
196
+ "name": "entity_access_frequency_check",
197
+ "condition": "entity_access_count[context.accessing_entity_id] < passport.limits.data.access.max_access_attempts_per_hour",
198
+ "deny_code": "oap.access_frequency_exceeded",
199
+ "description": "Entity access frequency must not exceed hourly limits."
200
+ },
201
+ {
202
+ "name": "data_retention_check",
203
+ "condition": "context.data_timestamp and (now() - to_timestamp(context.data_timestamp)) <= passport.limits.data.access.max_data_age_seconds",
204
+ "deny_code": "oap.data_expired",
205
+ "description": "Data must not be older than retention policy allows."
206
+ }
207
+ ]
208
+ }
@@ -0,0 +1,12 @@
1
+ {"name":"allow_pii_read_employee","context":{"data_classification":"PII","accessing_entity_id":"emp_123","accessing_entity_type":"employee","resource_id":"user_456","action_type":"read","jurisdiction":"US"}}
2
+ {"name":"deny_classification_forbidden","context":{"data_classification":"Sensitive","accessing_entity_id":"emp_123","accessing_entity_type":"employee","resource_id":"user_456","action_type":"read","jurisdiction":"US"}}
3
+ {"name":"deny_entity_type_forbidden","context":{"data_classification":"HR","accessing_entity_id":"client_123","accessing_entity_type":"client","resource_id":"emp_456","action_type":"read","jurisdiction":"US"}}
4
+ {"name":"deny_jurisdiction_blocked","context":{"data_classification":"PII","accessing_entity_id":"emp_123","accessing_entity_type":"employee","resource_id":"user_456","action_type":"read","jurisdiction":"CN"}}
5
+ {"name":"deny_row_limit_exceeded","context":{"data_classification":"PII","accessing_entity_id":"emp_123","accessing_entity_type":"employee","resource_id":"user_456","action_type":"export","jurisdiction":"US","row_count":15000}}
6
+ {"name":"deny_destination_jurisdiction_blocked","context":{"data_classification":"PII","accessing_entity_id":"emp_123","accessing_entity_type":"employee","resource_id":"user_456","action_type":"export","jurisdiction":"US","destination_jurisdiction":"CN"}}
7
+ {"name":"deny_balance_inquiry_forbidden","context":{"data_classification":"Financial","accessing_entity_id":"emp_123","accessing_entity_type":"employee","resource_id":"account_456","action_type":"read","jurisdiction":"US","resource_attributes":{"account_balance_usd":150000}}}
8
+ {"name":"allow_financial_export_system_agent","context":{"data_classification":"Financial","accessing_entity_id":"agent_123","accessing_entity_type":"system_agent","resource_id":"account_456","action_type":"export","jurisdiction":"US","row_count":5000}}
9
+ {"name":"allow_client_tier1_read","context":{"data_classification":"ClientTier1","accessing_entity_id":"client_123","accessing_entity_type":"client","resource_id":"client_data_456","action_type":"read","jurisdiction":"US"}}
10
+ {"name":"deny_action_forbidden","context":{"data_classification":"HR","accessing_entity_id":"emp_123","accessing_entity_type":"employee","resource_id":"emp_456","action_type":"delete","jurisdiction":"US"}}
11
+ {"name":"allow_hr_read_employee","context":{"data_classification":"HR","accessing_entity_id":"emp_123","accessing_entity_type":"employee","resource_id":"emp_456","action_type":"read","jurisdiction":"US"}}
12
+ {"name":"deny_missing_required_fields","context":{"data_classification":"PII","accessing_entity_id":"emp_123","action_type":"read","jurisdiction":"US"}}