@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,292 @@
1
+ # Data Access Governance Policy Pack v1
2
+
3
+ Protect your data access endpoints with APort's standardized policy pack. This pack ensures only verified agents with proper capabilities, assurance levels, and data access permissions can access sensitive data based on classification, entity types, and jurisdictional boundaries.
4
+
5
+ ## What This Pack Protects
6
+
7
+ - **Route**: `/data/access/*` (GET, POST)
8
+ - **Risk**: Data breaches, privacy violations, regulatory compliance, unauthorized access
9
+ - **Impact**: Data exposure, GDPR violations, audit findings, reputational damage
10
+
11
+ ## Requirements
12
+
13
+ | Requirement | Value | Description |
14
+ |-------------|-------|-------------|
15
+ | **Capability** | `data.access` | Agent must have data access capability |
16
+ | **Assurance** | `L3` or higher | Enhanced verification minimum |
17
+ | **Limits** | `allowed_classifications[]` | Allowed data classifications (PII, Financial, HR, ClientTier1) |
18
+ | **Limits** | `permissions.{classification}.allowed_entity_types[]` | Entity types allowed per classification |
19
+ | **Limits** | `permissions.{classification}.allowed_actions[]` | Actions allowed per classification |
20
+ | **Limits** | `allowed_jurisdictions[]` | Allowed data jurisdictions |
21
+ | **Limits** | `max_rows_per_export` | Maximum rows per data export |
22
+ | **Limits** | `allowed_destination_jurisdictions[]` | Allowed destination jurisdictions for data transfer |
23
+ | **Limits** | `balance_inquiry_cap_usd` | Maximum account balance for inquiry access |
24
+ | **Regions** | Must match | Agent must be authorized in caller's region |
25
+
26
+ ## Implementation
27
+
28
+ ### Express.js
29
+
30
+ ```javascript
31
+ const { requirePolicy } = require('@aporthq/middleware-express');
32
+
33
+ // Option 1: Explicit agent ID (preferred)
34
+ app.get('/data/access',
35
+ requirePolicy('governance.data.access.v1', 'ap_a2d10232c6534523812423eec8a1425c45678'),
36
+ async (req, res) => {
37
+ // Your data access logic here
38
+ // req.policyResult contains the verified passport
39
+ const { data_classification, accessing_entity_id, resource_id } = req.query;
40
+ const passport = req.policyResult.passport;
41
+
42
+ // Process data access...
43
+ res.json({ success: true, data: retrievedData });
44
+ }
45
+ );
46
+
47
+ // Option 2: Header fallback (backward compatible)
48
+ app.get('/data/access',
49
+ requirePolicy('governance.data.access.v1'),
50
+ async (req, res) => {
51
+ // Your data access logic here
52
+ // req.policyResult contains the verified passport
53
+ const { data_classification, accessing_entity_id, resource_id } = req.query;
54
+ const passport = req.policyResult.passport;
55
+
56
+ // Process data access...
57
+ res.json({ success: true, data: retrievedData });
58
+ }
59
+ );
60
+ ```
61
+
62
+ **Client Request Example:**
63
+ ```javascript
64
+ // The client must include the agent ID in the header (for Option 2)
65
+ fetch('/data/access?data_classification=PII&accessing_entity_id=emp_123&resource_id=user_456', {
66
+ method: 'GET',
67
+ headers: {
68
+ 'Content-Type': 'application/json',
69
+ 'X-Agent-Passport-Id': 'ap_a2d10232c6534523812423eec8a1425c45678' // ← Agent ID passed here
70
+ }
71
+ });
72
+ ```
73
+
74
+ ### FastAPI
75
+
76
+ ```python
77
+ from aport.middleware import require_policy
78
+
79
+ @app.get("/data/access")
80
+ @require_policy("governance.data.access.v1")
81
+ async def access_data(request: Request, data_classification: str, accessing_entity_id: str, resource_id: str):
82
+ # Your data access logic here
83
+ # request.state.policy_result contains the verified passport
84
+ passport = request.state.policy_result.passport
85
+
86
+ # Process data access...
87
+ return {"success": True, "data": retrieved_data}
88
+ ```
89
+
90
+ **Client Request Example:**
91
+ ```python
92
+ import requests
93
+
94
+ # The client must include the agent ID in the header
95
+ response = requests.get('/data/access',
96
+ headers={
97
+ 'Content-Type': 'application/json',
98
+ 'X-Agent-Passport-Id': 'ap_a2d10232c6534523812423eec8a1425c45678' # ← Agent ID passed here
99
+ },
100
+ params={
101
+ 'data_classification': 'PII',
102
+ 'accessing_entity_id': 'emp_123',
103
+ 'resource_id': 'user_456'
104
+ }
105
+ )
106
+ ```
107
+
108
+ ## How It Works
109
+
110
+ The `requirePolicy('governance.data.access.v1', agentId?)` middleware implements a flexible approach:
111
+
112
+ 1. **Agent ID Resolution** (in order of preference):
113
+ - **Function Parameter**: Uses explicit `agentId` if provided
114
+ - **Header Fallback**: Reads `X-Agent-Passport-Id` header from request
115
+ - **Validation**: Ensures agent ID format is valid (`ap_xxxxxxxxxxxxx`)
116
+ - **Failure**: Returns 400 error if neither provided
117
+
118
+ 2. **Policy Validation**:
119
+ - **Format Check**: Validates policy ID format (`governance.data.access.v1`)
120
+ - **API Call**: Calls `/api/verify/policy/governance.data.access.v1` with agent ID and context
121
+ - **Requirements Check**: Validates agent meets governance.data.access.v1 requirements:
122
+ - Has `data.access` capability
123
+ - Meets minimum assurance level (L3+)
124
+ - Data classification is allowed
125
+ - Entity type is allowed for the classification
126
+ - Action type is allowed for the classification
127
+ - Jurisdiction is allowed
128
+ - Row count within limits (for exports)
129
+ - Destination jurisdiction allowed (for transfers)
130
+ - Balance inquiry within limits
131
+ - Access frequency within limits
132
+ - Data retention policy compliance
133
+
134
+ 3. **Result Handling**:
135
+ - **Success**: Adds `req.policyResult` with verified data and continues
136
+ - **Failure**: Returns 403 with detailed error information
137
+ - **Logging**: Logs violations for monitoring and debugging
138
+
139
+ ## Error Responses
140
+
141
+ When policy checks fail, you'll receive a `403 Forbidden` with detailed error information:
142
+
143
+ ```json
144
+ {
145
+ "error": "policy_violation",
146
+ "code": "oap.classification_forbidden",
147
+ "message": "Data classification 'Sensitive' is not allowed",
148
+ "policy_id": "governance.data.access.v1",
149
+ "agent_id": "ap_a2d10232c6534523812423eec8a1425c45678",
150
+ "upgrade_instructions": "Request access to this data classification in your passport"
151
+ }
152
+ ```
153
+
154
+ ## Test Payloads
155
+
156
+ ### Valid Request
157
+ ```json
158
+ {
159
+ "data_classification": "PII",
160
+ "accessing_entity_id": "emp_123",
161
+ "accessing_entity_type": "employee",
162
+ "resource_id": "user_456",
163
+ "action_type": "read",
164
+ "jurisdiction": "US",
165
+ "row_count": 100
166
+ }
167
+ ```
168
+
169
+ ### Invalid Request (classification forbidden)
170
+ ```json
171
+ {
172
+ "data_classification": "Sensitive",
173
+ "accessing_entity_id": "emp_123",
174
+ "accessing_entity_type": "employee",
175
+ "resource_id": "user_456"
176
+ }
177
+ ```
178
+ *Returns 403: "Data classification 'Sensitive' is not allowed"*
179
+
180
+ ## Best Practices
181
+
182
+ 1. **Cache Verification**: Cache `/verify` responses with ETag for 60 seconds
183
+ 2. **Webhook Integration**: Subscribe to `status.changed` webhooks for instant suspension
184
+ 3. **Verifiable Attestation**: Log all data access attempts for compliance
185
+ 4. **Data Classification**: Implement data classification tagging for all resources
186
+ 5. **Entity Access Matrices**: Maintain entity access matrices for different data types
187
+ 6. **Jurisdiction Awareness**: Use jurisdiction-aware data routing for compliance
188
+ 7. **Progressive Disclosure**: Implement progressive disclosure for sensitive data access
189
+ 8. **Access Monitoring**: Monitor access patterns for unusual behavior
190
+ 9. **Audit Trails**: Maintain audit trails for all data access operations
191
+ 10. **Encryption**: Use encryption for data in transit and at rest
192
+ 11. **Data Retention**: Implement data retention policies based on classification
193
+
194
+ ## Compliance Badge
195
+
196
+ Agents that meet this policy's requirements can display the "Data-Access-Ready" badge:
197
+
198
+ ```markdown
199
+ [![Data-Access-Ready](https://api.aport.io/badge/ap_a2d10232c6534523812423eec8a1425c45678.svg)](https://aport.io/agents/ap_a2d10232c6534523812423eec8a1425c45678)
200
+ ```
201
+
202
+ ## Support
203
+
204
+ - [Documentation](https://aport.io/docs/policies/governance.data.access.v1)
205
+ - [Community](https://github.com/aporthq/community)
206
+ - [Support](https://aport.io/support)
207
+
208
+ ---
209
+ **Last Updated**: 2025-01-30 00:00:00 UTC
210
+
211
+
212
+ ## Required Context
213
+
214
+ This policy requires the following context (JSON Schema):
215
+
216
+ ```json
217
+ {
218
+ "$schema": "http://json-schema.org/draft-07/schema#",
219
+ "type": "object",
220
+ "required": [
221
+ "data_classification",
222
+ "accessing_entity_id",
223
+ "accessing_entity_type",
224
+ "resource_id"
225
+ ],
226
+ "properties": {
227
+ "data_classification": {
228
+ "type": "string",
229
+ "description": "The classification of the data being accessed (e.g., 'PII', 'Financial', 'HR', 'ClientTier1')."
230
+ },
231
+ "accessing_entity_id": {
232
+ "type": "string",
233
+ "description": "The unique ID of the entity (user, agent, employee) attempting the access."
234
+ },
235
+ "accessing_entity_type": {
236
+ "type": "string",
237
+ "enum": [
238
+ "employee",
239
+ "client",
240
+ "system_agent"
241
+ ],
242
+ "description": "The type of entity attempting the access."
243
+ },
244
+ "resource_id": {
245
+ "type": "string",
246
+ "description": "The unique ID of the data resource being accessed (e.g., account number, user profile ID)."
247
+ },
248
+ "action_type": {
249
+ "type": "string",
250
+ "enum": [
251
+ "read",
252
+ "export",
253
+ "delete",
254
+ "update"
255
+ ],
256
+ "default": "read",
257
+ "description": "The type of data access action being performed."
258
+ },
259
+ "jurisdiction": {
260
+ "type": "string",
261
+ "pattern": "^[A-Z]{2}$",
262
+ "description": "The ISO 3166-1 alpha-2 country code relevant to the data's jurisdiction."
263
+ },
264
+ "row_count": {
265
+ "type": "integer",
266
+ "description": "The number of rows/records being accessed or exported. Solves for Row Limits."
267
+ },
268
+ "destination_jurisdiction": {
269
+ "type": "string",
270
+ "pattern": "^[A-Z]{2}$",
271
+ "description": "The destination country code for data transfers. Solves for Data Locality."
272
+ },
273
+ "resource_attributes": {
274
+ "type": "object",
275
+ "description": "A flexible object for passing specific attributes of the resource being accessed. Solves for Balance Inquiry.",
276
+ "properties": {
277
+ "account_balance_usd": {
278
+ "type": "integer",
279
+ "description": "The balance of the account in USD minor units, used for balance-based access rules."
280
+ }
281
+ }
282
+ }
283
+ }
284
+ }
285
+ ```
286
+
287
+ You can also fetch this live via the discovery endpoint:
288
+
289
+ ```bash
290
+ curl -s "https://aport.io/api/policies/governance.data.access.v1?format=schema"
291
+ ```
292
+
@@ -0,0 +1,321 @@
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 governance.data.access policy to all data access routes
8
+ app.get(
9
+ "/data/access",
10
+ requirePolicy("governance.data.access.v1"),
11
+ async (req, res) => {
12
+ try {
13
+ const {
14
+ data_classification,
15
+ accessing_entity_id,
16
+ accessing_entity_type,
17
+ resource_id,
18
+ action_type,
19
+ jurisdiction,
20
+ row_count,
21
+ destination_jurisdiction,
22
+ resource_attributes,
23
+ } = req.query;
24
+
25
+ const passport = req.policyResult.passport;
26
+
27
+ // Additional business logic validation
28
+ if (!data_classification || !accessing_entity_id || !resource_id) {
29
+ return res.status(400).json({ error: "Missing required parameters" });
30
+ }
31
+
32
+ // Process data access using your data system
33
+ const access_id = await processDataAccess({
34
+ data_classification,
35
+ accessing_entity_id,
36
+ accessing_entity_type,
37
+ resource_id,
38
+ action_type: action_type || "read",
39
+ jurisdiction,
40
+ row_count: row_count ? parseInt(row_count) : undefined,
41
+ destination_jurisdiction,
42
+ resource_attributes: resource_attributes
43
+ ? JSON.parse(resource_attributes)
44
+ : undefined,
45
+ agent_id: passport.passport_id,
46
+ agent_name: passport.metadata?.template_name || "Unknown Agent",
47
+ });
48
+
49
+ // Log the data access
50
+ console.log(
51
+ `Data access processed: ${access_id} for ${data_classification} data by agent ${passport.passport_id}`
52
+ );
53
+
54
+ res.json({
55
+ success: true,
56
+ access_id,
57
+ data_classification,
58
+ resource_id,
59
+ action_type: action_type || "read",
60
+ status: "processed",
61
+ decision_id: req.policyResult.decision_id,
62
+ });
63
+ } catch (error) {
64
+ console.error("Data access processing error:", error);
65
+ res.status(500).json({ error: "Internal server error" });
66
+ }
67
+ }
68
+ );
69
+
70
+ // Data export endpoint
71
+ app.post(
72
+ "/data/export",
73
+ requirePolicy("governance.data.access.v1"),
74
+ async (req, res) => {
75
+ try {
76
+ const {
77
+ data_classification,
78
+ accessing_entity_id,
79
+ accessing_entity_type,
80
+ resource_id,
81
+ action_type,
82
+ jurisdiction,
83
+ row_count,
84
+ destination_jurisdiction,
85
+ export_format,
86
+ filters,
87
+ } = req.body;
88
+
89
+ const passport = req.policyResult.passport;
90
+
91
+ // Check row count limits
92
+ const maxRowsPerExport =
93
+ passport.limits?.data?.access?.max_rows_per_export;
94
+ if (maxRowsPerExport && row_count > maxRowsPerExport) {
95
+ return res.status(403).json({
96
+ error: "Export row count exceeds limit",
97
+ row_count,
98
+ limit: maxRowsPerExport,
99
+ });
100
+ }
101
+
102
+ // Process data export
103
+ const export_id = await processDataExport({
104
+ data_classification,
105
+ accessing_entity_id,
106
+ accessing_entity_type,
107
+ resource_id,
108
+ action_type: action_type || "export",
109
+ jurisdiction,
110
+ row_count,
111
+ destination_jurisdiction,
112
+ export_format,
113
+ filters,
114
+ agent_id: passport.passport_id,
115
+ });
116
+
117
+ res.json({
118
+ success: true,
119
+ export_id,
120
+ data_classification,
121
+ row_count,
122
+ export_format,
123
+ status: "exported",
124
+ decision_id: req.policyResult.decision_id,
125
+ });
126
+ } catch (error) {
127
+ console.error("Data export error:", error);
128
+ res.status(500).json({ error: "Internal server error" });
129
+ }
130
+ }
131
+ );
132
+
133
+ // Balance inquiry endpoint
134
+ app.get(
135
+ "/data/balance/:account_id",
136
+ requirePolicy("governance.data.access.v1"),
137
+ async (req, res) => {
138
+ try {
139
+ const { account_id } = req.params;
140
+ const { accessing_entity_id, accessing_entity_type } = req.query;
141
+ const passport = req.policyResult.passport;
142
+
143
+ // Get account balance
144
+ const balance_info = await getAccountBalance(
145
+ account_id,
146
+ accessing_entity_id,
147
+ passport.passport_id
148
+ );
149
+
150
+ if (!balance_info) {
151
+ return res.status(404).json({ error: "Account not found" });
152
+ }
153
+
154
+ // Check balance inquiry limits
155
+ const balanceInquiryCap =
156
+ passport.limits?.data?.access?.balance_inquiry_cap_usd;
157
+ if (balanceInquiryCap && balance_info.balance_usd >= balanceInquiryCap) {
158
+ return res.status(403).json({
159
+ error: "Account balance exceeds inquiry cap",
160
+ balance: balance_info.balance_usd,
161
+ cap: balanceInquiryCap,
162
+ });
163
+ }
164
+
165
+ res.json({
166
+ success: true,
167
+ account_id,
168
+ balance_usd: balance_info.balance_usd,
169
+ currency: balance_info.currency,
170
+ last_updated: balance_info.last_updated,
171
+ decision_id: req.policyResult.decision_id,
172
+ });
173
+ } catch (error) {
174
+ console.error("Balance inquiry error:", error);
175
+ res.status(500).json({ error: "Internal server error" });
176
+ }
177
+ }
178
+ );
179
+
180
+ // Data access audit endpoint
181
+ app.get(
182
+ "/data/access/audit",
183
+ requirePolicy("governance.data.access.v1"),
184
+ async (req, res) => {
185
+ try {
186
+ const { start_date, end_date, data_classification } = req.query;
187
+ const passport = req.policyResult.passport;
188
+
189
+ const audit_logs = await getDataAccessAuditLogs({
190
+ start_date,
191
+ end_date,
192
+ data_classification,
193
+ agent_id: passport.passport_id,
194
+ });
195
+
196
+ res.json({
197
+ success: true,
198
+ audit_logs,
199
+ total_entries: audit_logs.length,
200
+ decision_id: req.policyResult.decision_id,
201
+ });
202
+ } catch (error) {
203
+ console.error("Audit log error:", error);
204
+ res.status(500).json({ error: "Internal server error" });
205
+ }
206
+ }
207
+ );
208
+
209
+ // Mock data access processing function
210
+ async function processDataAccess({
211
+ data_classification,
212
+ accessing_entity_id,
213
+ accessing_entity_type,
214
+ resource_id,
215
+ action_type,
216
+ jurisdiction,
217
+ row_count,
218
+ destination_jurisdiction,
219
+ resource_attributes,
220
+ agent_id,
221
+ }) {
222
+ // Simulate data system call
223
+ await new Promise((resolve) => setTimeout(resolve, 100));
224
+
225
+ // Log data access details for audit
226
+ console.log(`Processing data access:`, {
227
+ data_classification,
228
+ accessing_entity_id,
229
+ accessing_entity_type,
230
+ resource_id,
231
+ action_type,
232
+ jurisdiction,
233
+ row_count,
234
+ destination_jurisdiction,
235
+ resource_attributes,
236
+ agent_id,
237
+ });
238
+
239
+ return `access_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
240
+ }
241
+
242
+ // Mock data export processing function
243
+ async function processDataExport({
244
+ data_classification,
245
+ accessing_entity_id,
246
+ accessing_entity_type,
247
+ resource_id,
248
+ action_type,
249
+ jurisdiction,
250
+ row_count,
251
+ destination_jurisdiction,
252
+ export_format,
253
+ filters,
254
+ agent_id,
255
+ }) {
256
+ // Simulate data export processing
257
+ await new Promise((resolve) => setTimeout(resolve, 200));
258
+
259
+ console.log(`Processing data export:`, {
260
+ data_classification,
261
+ accessing_entity_id,
262
+ accessing_entity_type,
263
+ resource_id,
264
+ action_type,
265
+ jurisdiction,
266
+ row_count,
267
+ destination_jurisdiction,
268
+ export_format,
269
+ filters,
270
+ agent_id,
271
+ });
272
+
273
+ return `export_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
274
+ }
275
+
276
+ // Mock account balance lookup
277
+ async function getAccountBalance(account_id, accessing_entity_id, agent_id) {
278
+ // Simulate account balance lookup
279
+ await new Promise((resolve) => setTimeout(resolve, 50));
280
+ return {
281
+ account_id,
282
+ balance_usd: 50000, // $500.00 in cents
283
+ currency: "USD",
284
+ last_updated: new Date().toISOString(),
285
+ };
286
+ }
287
+
288
+ // Mock audit log lookup
289
+ async function getDataAccessAuditLogs({
290
+ start_date,
291
+ end_date,
292
+ data_classification,
293
+ agent_id,
294
+ }) {
295
+ // Simulate audit log lookup
296
+ await new Promise((resolve) => setTimeout(resolve, 100));
297
+ return [
298
+ {
299
+ access_id: "access_123",
300
+ data_classification: "PII",
301
+ resource_id: "user_456",
302
+ action_type: "read",
303
+ timestamp: new Date().toISOString(),
304
+ agent_id,
305
+ },
306
+ {
307
+ access_id: "access_124",
308
+ data_classification: "Financial",
309
+ resource_id: "account_789",
310
+ action_type: "export",
311
+ timestamp: new Date().toISOString(),
312
+ agent_id,
313
+ },
314
+ ];
315
+ }
316
+
317
+ const PORT = process.env.PORT || 3000;
318
+ app.listen(PORT, () => {
319
+ console.log(`Data access governance service running on port ${PORT}`);
320
+ console.log("Protected by APort governance.data.access.v1 policy pack");
321
+ });