@aporthq/aport-agent-guardrails 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +217 -0
- package/README.md +481 -0
- package/bin/agent-guardrails +133 -0
- package/bin/aport-create-passport.sh +444 -0
- package/bin/aport-cursor-hook.sh +90 -0
- package/bin/aport-guardrail-api.sh +108 -0
- package/bin/aport-guardrail-bash.sh +394 -0
- package/bin/aport-guardrail-v2.sh +5 -0
- package/bin/aport-guardrail.sh +5 -0
- package/bin/aport-resolve-paths.sh +71 -0
- package/bin/aport-status.sh +276 -0
- package/bin/frameworks/crewai.sh +49 -0
- package/bin/frameworks/cursor.sh +95 -0
- package/bin/frameworks/langchain.sh +48 -0
- package/bin/frameworks/n8n.sh +36 -0
- package/bin/frameworks/openclaw.sh +19 -0
- package/bin/lib/allowlist.sh +18 -0
- package/bin/lib/common.sh +28 -0
- package/bin/lib/config.sh +46 -0
- package/bin/lib/constants.sh +232 -0
- package/bin/lib/detect.sh +65 -0
- package/bin/lib/error.sh +269 -0
- package/bin/lib/passport.sh +19 -0
- package/bin/lib/templates/.gitkeep +1 -0
- package/bin/lib/templates/config.yaml +6 -0
- package/bin/lib/validation.sh +206 -0
- package/bin/openclaw +660 -0
- package/docs/ADDING_A_FRAMEWORK.md +87 -0
- package/docs/AGENTS.md.example +40 -0
- package/docs/CODE_REVIEW.md +192 -0
- package/docs/DEPLOYMENT_READINESS.md +81 -0
- package/docs/FAQ_SECURITY_SCANNERS.md +373 -0
- package/docs/FRAMEWORK_ROADMAP.md +41 -0
- package/docs/HOSTED_PASSPORT_SETUP.md +362 -0
- package/docs/IMPLEMENTING_YOUR_OWN_EVALUATOR.md +433 -0
- package/docs/OPENCLAW_COMPATIBILITY.md +73 -0
- package/docs/OPENCLAW_LOCAL_INTEGRATION.md +596 -0
- package/docs/OPENCLAW_TOOLS_AND_POLICIES.md +54 -0
- package/docs/QUICKSTART.md +470 -0
- package/docs/QUICKSTART_OPENCLAW_PLUGIN.md +470 -0
- package/docs/README.md +28 -0
- package/docs/RELEASE.md +87 -0
- package/docs/REPO_LAYOUT.md +47 -0
- package/docs/SKILLS_ECOSYSTEM_ANALYSIS_FEB17.md +1260 -0
- package/docs/TOOL_POLICY_MAPPING.md +46 -0
- package/docs/UPGRADE.md +46 -0
- package/docs/VERIFICATION_METHODS.md +97 -0
- package/docs/assets/README.md +8 -0
- package/docs/assets/porter.svg +54 -0
- package/docs/development/ERROR_CODES.md +616 -0
- package/docs/frameworks/GITHUB_ISSUE_PROPOSALS.md +1105 -0
- package/docs/frameworks/crewai.md +114 -0
- package/docs/frameworks/cursor.md +159 -0
- package/docs/frameworks/langchain.md +72 -0
- package/docs/frameworks/n8n.md +40 -0
- package/docs/frameworks/openclaw.md +40 -0
- package/docs/launch/ADD_APORT_AWESOME_LISTS_INSTRUCTIONS.md +146 -0
- package/docs/launch/ANNOUNCEMENT_GUIDE.md +266 -0
- package/docs/launch/AWESOME_REPOS.md +53 -0
- package/docs/launch/CURSOR_VSCODE_HOOKS_RESEARCH.md +77 -0
- package/docs/launch/DEMO_TERMINAL_OUTPUT.txt +48 -0
- package/docs/launch/DRY_AND_PLAN_CHECKLIST.md +47 -0
- package/docs/launch/EVIDENCE_README.md +61 -0
- package/docs/launch/EVIDENCE_TERMINAL_CAPTURE.txt +10 -0
- package/docs/launch/FRAMEWORK_SUPPORT_PLAN.md +1640 -0
- package/docs/launch/LAUNCH_READINESS_CHECKLIST.md +237 -0
- package/docs/launch/LAUNCH_STRATEGY_SUMMARY.md +464 -0
- package/docs/launch/OPENCLAW_FEEDBACK_AND_FIXES.md +85 -0
- package/docs/launch/POST_1_VALENTINE_IMPROVED.md +233 -0
- package/docs/launch/POST_2_GUARDRAIL_IMPROVED.md +369 -0
- package/docs/launch/PRE_LAUNCH_FIXES.md +766 -0
- package/docs/launch/QUICK_LAUNCH_CHECKLIST.md +400 -0
- package/docs/launch/READINESS_SUMMARY.md +262 -0
- package/docs/launch/README.md +68 -0
- package/docs/launch/USER_STORIES.md +327 -0
- package/docs/launch/scripts/add-aport-awesome-pr.sh +69 -0
- package/docs/operations/MONITORING.md +588 -0
- package/docs/reviews/2026-02-18-staff-review.md +268 -0
- package/extensions/openclaw-aport/README.md +415 -0
- package/extensions/openclaw-aport/index.js +625 -0
- package/extensions/openclaw-aport/openclaw-aport.js +7 -0
- package/extensions/openclaw-aport/openclaw.plugin.json +46 -0
- package/extensions/openclaw-aport/package.json +36 -0
- package/extensions/openclaw-aport/test.js +307 -0
- package/external/aport-policies/README.md +363 -0
- package/external/aport-policies/agent.session.create.v1/README.md +345 -0
- package/external/aport-policies/agent.session.create.v1/policy.json +162 -0
- package/external/aport-policies/agent.tool.register.v1/README.md +361 -0
- package/external/aport-policies/agent.tool.register.v1/policy.json +172 -0
- package/external/aport-policies/code.release.publish.v1/README.md +51 -0
- package/external/aport-policies/code.release.publish.v1/policy.json +121 -0
- package/external/aport-policies/code.repository.merge.v1/README.md +287 -0
- package/external/aport-policies/code.repository.merge.v1/express.example.js +332 -0
- package/external/aport-policies/code.repository.merge.v1/fastapi.example.py +370 -0
- package/external/aport-policies/code.repository.merge.v1/policy.json +162 -0
- package/external/aport-policies/data.export.create.v1/README.md +226 -0
- package/external/aport-policies/data.export.create.v1/express.example.js +172 -0
- package/external/aport-policies/data.export.create.v1/fastapi.example.py +165 -0
- package/external/aport-policies/data.export.create.v1/policy.json +133 -0
- package/external/aport-policies/data.report.ingest.v1/README.md +134 -0
- package/external/aport-policies/data.report.ingest.v1/express.example.js +105 -0
- package/external/aport-policies/data.report.ingest.v1/minimal-example.js +68 -0
- package/external/aport-policies/data.report.ingest.v1/policy.json +174 -0
- package/external/aport-policies/finance.crypto.trade.v1/README.md +146 -0
- package/external/aport-policies/finance.crypto.trade.v1/express.example.js +109 -0
- package/external/aport-policies/finance.crypto.trade.v1/minimal-example.js +65 -0
- package/external/aport-policies/finance.crypto.trade.v1/policy.json +176 -0
- package/external/aport-policies/finance.payment.charge.v1/README.md +326 -0
- package/external/aport-policies/finance.payment.charge.v1/express.example.js +250 -0
- package/external/aport-policies/finance.payment.charge.v1/fastapi.example.py +227 -0
- package/external/aport-policies/finance.payment.charge.v1/minimal-example.js +64 -0
- package/external/aport-policies/finance.payment.charge.v1/policy.json +224 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/contexts.jsonl +12 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/expected.jsonl +12 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/passport.instance.json +42 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/passport.template.json +40 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/payments-charge-policy.test.js +817 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/test_payments_charge_policy.py +486 -0
- package/external/aport-policies/finance.payment.payout.v1/README.md +78 -0
- package/external/aport-policies/finance.payment.payout.v1/policy.json +181 -0
- package/external/aport-policies/finance.payment.refund.v1/README.md +275 -0
- package/external/aport-policies/finance.payment.refund.v1/express.example.js +167 -0
- package/external/aport-policies/finance.payment.refund.v1/fastapi.example.py +136 -0
- package/external/aport-policies/finance.payment.refund.v1/minimal-example.js +183 -0
- package/external/aport-policies/finance.payment.refund.v1/policy.json +216 -0
- package/external/aport-policies/finance.payment.refund.v1/tests/refunds-policy.test.js +924 -0
- package/external/aport-policies/finance.payment.refund.v1/tests/test_refunds_policy.py +778 -0
- package/external/aport-policies/finance.transaction.execute.v1/README.md +309 -0
- package/external/aport-policies/finance.transaction.execute.v1/express.example.js +261 -0
- package/external/aport-policies/finance.transaction.execute.v1/fastapi.example.py +231 -0
- package/external/aport-policies/finance.transaction.execute.v1/minimal-example.js +78 -0
- package/external/aport-policies/finance.transaction.execute.v1/policy.json +189 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/contexts.jsonl +12 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/expected.jsonl +12 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/passport.instance.json +42 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/passport.template.json +42 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/test_transactions_policy.py +214 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/transactions-policy.test.js +306 -0
- package/external/aport-policies/governance.data.access.v1/README.md +292 -0
- package/external/aport-policies/governance.data.access.v1/express.example.js +321 -0
- package/external/aport-policies/governance.data.access.v1/fastapi.example.py +279 -0
- package/external/aport-policies/governance.data.access.v1/minimal-example.js +65 -0
- package/external/aport-policies/governance.data.access.v1/policy.json +208 -0
- package/external/aport-policies/governance.data.access.v1/tests/contexts.jsonl +12 -0
- package/external/aport-policies/governance.data.access.v1/tests/data-access-policy.test.js +308 -0
- package/external/aport-policies/governance.data.access.v1/tests/expected.jsonl +12 -0
- package/external/aport-policies/governance.data.access.v1/tests/passport.instance.json +56 -0
- package/external/aport-policies/governance.data.access.v1/tests/passport.template.json +56 -0
- package/external/aport-policies/governance.data.access.v1/tests/test_data_access_policy.py +214 -0
- package/external/aport-policies/legal.contract.review.v1/README.md +109 -0
- package/external/aport-policies/legal.contract.review.v1/policy.json +378 -0
- package/external/aport-policies/legal.contract.review.v1/tests/legal-contract-review-policy.test.js +609 -0
- package/external/aport-policies/legal.contract.review.v1/tests/passport.template.json +49 -0
- package/external/aport-policies/mcp.tool.execute.v1/README.md +301 -0
- package/external/aport-policies/mcp.tool.execute.v1/policy.json +141 -0
- package/external/aport-policies/messaging.message.send.v1/README.md +230 -0
- package/external/aport-policies/messaging.message.send.v1/express.example.js +183 -0
- package/external/aport-policies/messaging.message.send.v1/fastapi.example.py +193 -0
- package/external/aport-policies/messaging.message.send.v1/policy.json +144 -0
- package/external/aport-policies/policy-template.json +107 -0
- package/external/aport-policies/system.command.execute.v1/README.md +275 -0
- package/external/aport-policies/system.command.execute.v1/policy.json +146 -0
- package/external/aport-spec/CONTRIBUTING.md +273 -0
- package/external/aport-spec/LICENSE +21 -0
- package/external/aport-spec/README.md +168 -0
- package/external/aport-spec/conformance/README.md +294 -0
- package/external/aport-spec/conformance/cases/data.export.v1/contexts/allow_users.json +6 -0
- package/external/aport-spec/conformance/cases/data.export.v1/contexts/deny_pii.json +6 -0
- package/external/aport-spec/conformance/cases/data.export.v1/expected/allow_users.decision.json +19 -0
- package/external/aport-spec/conformance/cases/data.export.v1/expected/deny_pii.decision.json +19 -0
- package/external/aport-spec/conformance/cases/data.export.v1/passports/template.json +29 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/allow_50usd.json +9 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/deny_150usd.json +9 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/deny_currency.json +9 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/allow_50usd.decision.json +19 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/deny_150usd.decision.json +19 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/deny_currency.decision.json +19 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/passports/template.json +42 -0
- package/external/aport-spec/conformance/package.json +44 -0
- package/external/aport-spec/conformance/pnpm-lock.yaml +642 -0
- package/external/aport-spec/conformance/src/cases.ts +371 -0
- package/external/aport-spec/conformance/src/ed25519.ts +167 -0
- package/external/aport-spec/conformance/src/jcs.ts +85 -0
- package/external/aport-spec/conformance/src/runner.ts +533 -0
- package/external/aport-spec/conformance/src/validators.ts +185 -0
- package/external/aport-spec/conformance/test-runner.js +315 -0
- package/external/aport-spec/conformance/tsconfig.json +21 -0
- package/external/aport-spec/error-schema.json +192 -0
- package/external/aport-spec/index.json +12 -0
- package/external/aport-spec/integrations/clawmoat/README.md +12 -0
- package/external/aport-spec/integrations/shield/README.md +245 -0
- package/external/aport-spec/integrations/shield/adapters/index.js +116 -0
- package/external/aport-spec/integrations/shield/adapters/system-command-execute.js +133 -0
- package/external/aport-spec/integrations/shield/test/README.md +58 -0
- package/external/aport-spec/integrations/shield/test/shield.md +40 -0
- package/external/aport-spec/integrations/shield/test/test-shield-to-verify.js +274 -0
- package/external/aport-spec/metrics-schema.json +504 -0
- package/external/aport-spec/oap/CHANGELOG.md +54 -0
- package/external/aport-spec/oap/VERSION.md +40 -0
- package/external/aport-spec/oap/capability-registry.md +229 -0
- package/external/aport-spec/oap/conformance.md +257 -0
- package/external/aport-spec/oap/decision-schema.json +114 -0
- package/external/aport-spec/oap/examples/context.refund.usd.50.json +9 -0
- package/external/aport-spec/oap/examples/decision.allow.sample.json +20 -0
- package/external/aport-spec/oap/examples/decision.deny.sample.json +23 -0
- package/external/aport-spec/oap/examples/passport.instance.v1.json +50 -0
- package/external/aport-spec/oap/examples/passport.template.v1.json +71 -0
- package/external/aport-spec/oap/oap-spec.md +426 -0
- package/external/aport-spec/oap/passport-schema.json +396 -0
- package/external/aport-spec/oap/security.md +213 -0
- package/external/aport-spec/oap/vc/context-oap-v1.jsonld +137 -0
- package/external/aport-spec/oap/vc/examples/oap-decision-vc.json +37 -0
- package/external/aport-spec/oap/vc/examples/oap-passport-vc.json +68 -0
- package/external/aport-spec/oap/vc/tools/INTEGRATION.md +375 -0
- package/external/aport-spec/oap/vc/tools/README.md +278 -0
- package/external/aport-spec/oap/vc/tools/examples/decision-to-vc.js +66 -0
- package/external/aport-spec/oap/vc/tools/examples/passport-to-vc.js +83 -0
- package/external/aport-spec/oap/vc/tools/examples/vc-to-decision.js +77 -0
- package/external/aport-spec/oap/vc/tools/examples/vc-to-passport.js +94 -0
- package/external/aport-spec/oap/vc/tools/package.json +38 -0
- package/external/aport-spec/oap/vc/tools/pnpm-lock.yaml +472 -0
- package/external/aport-spec/oap/vc/tools/src/cli.ts +226 -0
- package/external/aport-spec/oap/vc/tools/src/crypto-utils.ts +427 -0
- package/external/aport-spec/oap/vc/tools/src/index.ts +653 -0
- package/external/aport-spec/oap/vc/tools/src/test.ts +148 -0
- package/external/aport-spec/oap/vc/tools/src/vp.ts +382 -0
- package/external/aport-spec/oap/vc/tools/test-simple.js +214 -0
- package/external/aport-spec/oap/vc/tools/tsconfig.json +19 -0
- package/external/aport-spec/oap/vc/vc-mapping.md +443 -0
- package/external/aport-spec/passport-schema.json +586 -0
- package/external/aport-spec/rate-limiting.md +136 -0
- package/external/aport-spec/transport-profile.md +325 -0
- package/external/aport-spec/webhook-spec.md +314 -0
- package/package.json +70 -0
- package/skills/aport-agent-guardrail/SKILL.md +314 -0
- package/src/evaluator.js +252 -0
- package/src/server/index.js +72 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "data.report.ingest.v1",
|
|
3
|
+
"name": "Report Data Ingestion Policy",
|
|
4
|
+
"description": "Pre-action governance for data being ingested by an AI agent for reporting purposes (e.g., ESG, financial disclosures). Enforces rules on data source credibility and freshness.",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"status": "active",
|
|
7
|
+
"requires_capabilities": ["data.report.ingest"],
|
|
8
|
+
"min_assurance": "L2",
|
|
9
|
+
"limits_required": [
|
|
10
|
+
"approved_sources",
|
|
11
|
+
"max_data_age_seconds",
|
|
12
|
+
"max_data_size_mb",
|
|
13
|
+
"max_ingest_frequency_per_hour",
|
|
14
|
+
"data_quality_threshold",
|
|
15
|
+
"required_validation_checks"
|
|
16
|
+
],
|
|
17
|
+
"required_fields": ["report_type", "data_source_id", "data_timestamp"],
|
|
18
|
+
"optional_fields": [
|
|
19
|
+
"metric_type",
|
|
20
|
+
"data_size_mb",
|
|
21
|
+
"validation_checks",
|
|
22
|
+
"data_quality_score",
|
|
23
|
+
"ingest_reason",
|
|
24
|
+
"idempotency_key"
|
|
25
|
+
],
|
|
26
|
+
"enforcement": {
|
|
27
|
+
"source_approved": true,
|
|
28
|
+
"data_freshness": true,
|
|
29
|
+
"data_size_limit": true,
|
|
30
|
+
"ingest_frequency_limit": true,
|
|
31
|
+
"data_quality_enforced": true,
|
|
32
|
+
"validation_required": true,
|
|
33
|
+
"idempotency_required": true
|
|
34
|
+
},
|
|
35
|
+
"mcp": {
|
|
36
|
+
"require_allowlisted_if_present": true
|
|
37
|
+
},
|
|
38
|
+
"advice": [
|
|
39
|
+
"Cache /verify with ETag; 60s TTL",
|
|
40
|
+
"Subscribe to status webhooks for instant suspend",
|
|
41
|
+
"Log all data ingestion attempts for Verifiable Attestation",
|
|
42
|
+
"Implement data quality scoring before ingestion",
|
|
43
|
+
"Use data lineage tracking for audit compliance",
|
|
44
|
+
"Implement progressive data validation checks",
|
|
45
|
+
"Monitor for data anomalies and unusual patterns",
|
|
46
|
+
"Use data encryption for sensitive report data",
|
|
47
|
+
"Implement data retention policies based on report type",
|
|
48
|
+
"Maintain data source reputation scoring",
|
|
49
|
+
"Use idempotency keys to prevent duplicate ingestion",
|
|
50
|
+
"Implement data freshness monitoring and alerts"
|
|
51
|
+
],
|
|
52
|
+
"cache": {
|
|
53
|
+
"default_ttl_seconds": 300,
|
|
54
|
+
"suspend_invalidate_seconds": 60
|
|
55
|
+
},
|
|
56
|
+
"required_context": {
|
|
57
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
58
|
+
"type": "object",
|
|
59
|
+
"required": ["report_type", "data_source_id", "data_timestamp"],
|
|
60
|
+
"properties": {
|
|
61
|
+
"report_type": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"description": "The type of report being generated (e.g., 'ESG', 'QuarterlyFinancials')."
|
|
64
|
+
},
|
|
65
|
+
"data_source_id": {
|
|
66
|
+
"type": "string",
|
|
67
|
+
"description": "A unique identifier for the source of the data being ingested (e.g., 'api.climate-data.com', 'internal-hr-db')."
|
|
68
|
+
},
|
|
69
|
+
"data_timestamp": {
|
|
70
|
+
"type": "string",
|
|
71
|
+
"format": "date-time",
|
|
72
|
+
"description": "The ISO 8601 timestamp of when the data was generated."
|
|
73
|
+
},
|
|
74
|
+
"metric_type": {
|
|
75
|
+
"type": "string",
|
|
76
|
+
"description": "The specific metric this data point relates to (e.g., 'carbon_emissions', 'employee_diversity')."
|
|
77
|
+
},
|
|
78
|
+
"mcp_servers": {
|
|
79
|
+
"type": "array",
|
|
80
|
+
"items": { "type": "string" },
|
|
81
|
+
"description": "MCP servers being used in this request (e.g., [\"https://mcp.climate-data.com\"])"
|
|
82
|
+
},
|
|
83
|
+
"mcp_tools": {
|
|
84
|
+
"type": "array",
|
|
85
|
+
"items": { "type": "string" },
|
|
86
|
+
"description": "MCP tools being used in this request (e.g., [\"climate_data.reports.ingest\"])"
|
|
87
|
+
},
|
|
88
|
+
"mcp_server": {
|
|
89
|
+
"type": "string",
|
|
90
|
+
"description": "Single MCP server being used (backward compatibility - use mcp_servers array for multiple)"
|
|
91
|
+
},
|
|
92
|
+
"mcp_tool": {
|
|
93
|
+
"type": "string",
|
|
94
|
+
"description": "Single MCP tool being used (backward compatibility - use mcp_tools array for multiple)"
|
|
95
|
+
},
|
|
96
|
+
"mcp_session": {
|
|
97
|
+
"type": "string",
|
|
98
|
+
"description": "MCP session identifier for audit trail (optional)"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"evaluation_rules": [
|
|
103
|
+
{
|
|
104
|
+
"name": "passport_status_active",
|
|
105
|
+
"condition": "passport.status == 'active'",
|
|
106
|
+
"deny_code": "oap.passport_suspended"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"name": "source_is_approved",
|
|
110
|
+
"condition": "context.data_source_id in passport.limits.data.report.ingest[context.report_type].approved_sources",
|
|
111
|
+
"deny_code": "oap.source_unapproved"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"name": "data_is_fresh",
|
|
115
|
+
"condition": "(now() - to_timestamp(context.data_timestamp)) <= passport.limits.data.report.ingest[context.report_type].max_data_age_seconds",
|
|
116
|
+
"deny_code": "oap.data_stale",
|
|
117
|
+
"description": "Data must not be older than maximum allowed age for the report type."
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"name": "assurance_minimum",
|
|
121
|
+
"condition": "passport.assurance_level >= policy.min_assurance",
|
|
122
|
+
"deny_code": "oap.assurance_insufficient",
|
|
123
|
+
"description": "Assurance level must meet minimum requirement for data ingestion."
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"name": "data_size_limit_check",
|
|
127
|
+
"condition": "not context.data_size_mb or context.data_size_mb <= passport.limits.data.report.ingest[context.report_type].max_data_size_mb",
|
|
128
|
+
"deny_code": "oap.data_size_exceeded",
|
|
129
|
+
"description": "Data size must not exceed maximum allowed for the report type."
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"name": "ingest_frequency_check",
|
|
133
|
+
"condition": "ingest_count_per_hour[context.data_source_id] < passport.limits.data.report.ingest[context.report_type].max_ingest_frequency_per_hour",
|
|
134
|
+
"deny_code": "oap.ingest_frequency_exceeded",
|
|
135
|
+
"description": "Data source ingestion frequency must not exceed hourly limit."
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"name": "data_quality_check",
|
|
139
|
+
"condition": "not context.data_quality_score or context.data_quality_score >= passport.limits.data.report.ingest[context.report_type].data_quality_threshold",
|
|
140
|
+
"deny_code": "oap.data_quality_insufficient",
|
|
141
|
+
"description": "Data quality score must meet minimum threshold."
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"name": "validation_checks_required",
|
|
145
|
+
"condition": "context.validation_checks and all(check in passport.limits.data.report.ingest[context.report_type].required_validation_checks for check in context.validation_checks)",
|
|
146
|
+
"deny_code": "oap.validation_checks_missing",
|
|
147
|
+
"description": "All required validation checks must be performed."
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"name": "idempotency_check",
|
|
151
|
+
"condition": "context.idempotency_key not in recent_keys",
|
|
152
|
+
"deny_code": "oap.idempotency_conflict",
|
|
153
|
+
"description": "Idempotency key must be unique to prevent duplicate ingestion."
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"name": "report_type_allowed",
|
|
157
|
+
"condition": "context.report_type in passport.limits.data.report.ingest.allowed_report_types",
|
|
158
|
+
"deny_code": "oap.report_type_forbidden",
|
|
159
|
+
"description": "Report type must be in the allowed list."
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"name": "data_source_reputation_check",
|
|
163
|
+
"condition": "data_source_reputation[context.data_source_id] >= passport.limits.data.report.ingest.min_source_reputation_score",
|
|
164
|
+
"deny_code": "oap.source_reputation_insufficient",
|
|
165
|
+
"description": "Data source must meet minimum reputation score."
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"name": "metric_type_validation",
|
|
169
|
+
"condition": "not context.metric_type or context.metric_type in passport.limits.data.report.ingest[context.report_type].allowed_metric_types",
|
|
170
|
+
"deny_code": "oap.metric_type_forbidden",
|
|
171
|
+
"description": "Metric type must be allowed for the report type."
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Crypto Asset Trading Policy Pack v1
|
|
2
|
+
|
|
3
|
+
Protect your crypto trading endpoints with APort's standardized policy pack. This pack ensures only verified agents with proper capabilities, assurance levels, and trading limits can execute crypto asset trades with appropriate security controls.
|
|
4
|
+
|
|
5
|
+
## What This Pack Protects
|
|
6
|
+
|
|
7
|
+
- **Route**: `/finance/crypto/trade/*` (POST)
|
|
8
|
+
- **Risk**: Crypto trading, market manipulation, security breaches, regulatory compliance
|
|
9
|
+
- **Impact**: Financial loss, regulatory violations, security incidents, market disruption
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
| Requirement | Value | Description |
|
|
14
|
+
|-------------|-------|-------------|
|
|
15
|
+
| **Capability** | `finance.crypto.trade` | Agent must have crypto trading capability |
|
|
16
|
+
| **Assurance** | `L3` or higher | Enhanced verification minimum |
|
|
17
|
+
| **Limits** | `allowed_exchanges[]` | Allowed crypto exchanges (coinbase, binance, etc.) |
|
|
18
|
+
| **Limits** | `allowed_tokens[]` | Allowed cryptocurrency tokens |
|
|
19
|
+
| **Limits** | `max_trade_size_usd` | Maximum trade size in USD |
|
|
20
|
+
| **Limits** | `max_hot_wallet_trade_usd` | Maximum hot wallet trade size |
|
|
21
|
+
| **Limits** | `max_daily_trade_volume_usd` | Maximum daily trading volume |
|
|
22
|
+
| **Limits** | `max_trades_per_day` | Maximum number of trades per day |
|
|
23
|
+
| **Regions** | Must match | Agent must be authorized in caller's region |
|
|
24
|
+
| **Idempotency** | Required | Prevents duplicate trades |
|
|
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.post('/finance/crypto/trade',
|
|
35
|
+
requirePolicy('finance.crypto.trade.v1', 'ap_a2d10232c6534523812423eec8a1425c45678'),
|
|
36
|
+
async (req, res) => {
|
|
37
|
+
// Your crypto trading logic here
|
|
38
|
+
// req.policyResult contains the verified passport
|
|
39
|
+
const { exchange_id, pair, side, amount_usd } = req.body;
|
|
40
|
+
const passport = req.policyResult.passport;
|
|
41
|
+
|
|
42
|
+
// Process crypto trade...
|
|
43
|
+
res.json({ success: true, trade_id: generateId() });
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Client Request Example:**
|
|
49
|
+
```javascript
|
|
50
|
+
fetch('/finance/crypto/trade', {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
headers: {
|
|
53
|
+
'Content-Type': 'application/json',
|
|
54
|
+
'X-Agent-Passport-Id': 'ap_a2d10232c6534523812423eec8a1425c45678'
|
|
55
|
+
},
|
|
56
|
+
body: JSON.stringify({
|
|
57
|
+
exchange_id: "coinbase",
|
|
58
|
+
pair: "BTC-USD",
|
|
59
|
+
side: "buy",
|
|
60
|
+
amount_usd: 1000,
|
|
61
|
+
source_wallet_type: "hot",
|
|
62
|
+
idempotency_key: "crypto-trade-123"
|
|
63
|
+
})
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Best Practices
|
|
68
|
+
|
|
69
|
+
1. **Cache Verification**: Cache `/verify` responses with ETag for 30 seconds
|
|
70
|
+
2. **Webhook Integration**: Subscribe to `status.changed` webhooks for instant suspension
|
|
71
|
+
3. **Verifiable Attestation**: Log all crypto trade attempts for compliance
|
|
72
|
+
4. **Real-time Price Validation**: Implement real-time price validation before trade execution
|
|
73
|
+
5. **Wallet Security**: Use cold storage for large amounts and hot wallets for small trades
|
|
74
|
+
6. **Exchange Monitoring**: Monitor exchange connectivity and implement circuit breakers
|
|
75
|
+
7. **Slippage Protection**: Implement slippage protection for volatile markets
|
|
76
|
+
8. **Idempotency**: Always use unique idempotency keys to prevent duplicate trades
|
|
77
|
+
9. **Exchange Allowlists**: Maintain exchange allowlists for trusted platforms only
|
|
78
|
+
10. **Progressive Limits**: Implement progressive limits for new trading pairs
|
|
79
|
+
11. **Pattern Monitoring**: Monitor for unusual trading patterns and potential manipulation
|
|
80
|
+
12. **Multi-signature Wallets**: Use multi-signature wallets for high-value transactions
|
|
81
|
+
|
|
82
|
+
## Support
|
|
83
|
+
|
|
84
|
+
- [Documentation](https://aport.io/docs/policies/finance.crypto.trade.v1)
|
|
85
|
+
- [Community](https://github.com/aporthq/community)
|
|
86
|
+
- [Support](https://aport.io/support)
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
**Last Updated**: 2025-01-30 00:00:00 UTC
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
## Required Context
|
|
93
|
+
|
|
94
|
+
This policy requires the following context (JSON Schema):
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
99
|
+
"type": "object",
|
|
100
|
+
"required": [
|
|
101
|
+
"exchange_id",
|
|
102
|
+
"pair",
|
|
103
|
+
"side",
|
|
104
|
+
"amount_usd"
|
|
105
|
+
],
|
|
106
|
+
"properties": {
|
|
107
|
+
"exchange_id": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"description": "The exchange where the trade is being executed (e.g., 'coinbase', 'binance')."
|
|
110
|
+
},
|
|
111
|
+
"pair": {
|
|
112
|
+
"type": "string",
|
|
113
|
+
"pattern": "^[A-Z]+-[A-Z]+$",
|
|
114
|
+
"description": "The trading pair (e.g., 'BTC-USD')."
|
|
115
|
+
},
|
|
116
|
+
"side": {
|
|
117
|
+
"type": "string",
|
|
118
|
+
"enum": [
|
|
119
|
+
"buy",
|
|
120
|
+
"sell"
|
|
121
|
+
],
|
|
122
|
+
"description": "The side of the trade."
|
|
123
|
+
},
|
|
124
|
+
"amount_usd": {
|
|
125
|
+
"type": "integer",
|
|
126
|
+
"description": "The total value of the trade in USD minor units (cents)."
|
|
127
|
+
},
|
|
128
|
+
"source_wallet_type": {
|
|
129
|
+
"type": "string",
|
|
130
|
+
"enum": [
|
|
131
|
+
"hot",
|
|
132
|
+
"cold",
|
|
133
|
+
"custodial"
|
|
134
|
+
],
|
|
135
|
+
"description": "The type of wallet initiating the trade."
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
You can also fetch this live via the discovery endpoint:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
curl -s "https://aport.io/api/policies/finance.crypto.trade.v1?format=schema"
|
|
145
|
+
```
|
|
146
|
+
|
|
@@ -0,0 +1,109 @@
|
|
|
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 finance.crypto.trade policy to all crypto trading routes
|
|
8
|
+
app.post(
|
|
9
|
+
"/finance/crypto/trade",
|
|
10
|
+
requirePolicy("finance.crypto.trade.v1"),
|
|
11
|
+
async (req, res) => {
|
|
12
|
+
try {
|
|
13
|
+
const {
|
|
14
|
+
exchange_id,
|
|
15
|
+
pair,
|
|
16
|
+
side,
|
|
17
|
+
amount_usd,
|
|
18
|
+
source_wallet_type,
|
|
19
|
+
idempotency_key,
|
|
20
|
+
trade_reason,
|
|
21
|
+
risk_score,
|
|
22
|
+
} = req.body;
|
|
23
|
+
|
|
24
|
+
const passport = req.policyResult.passport;
|
|
25
|
+
|
|
26
|
+
// Additional business logic validation
|
|
27
|
+
if (amount_usd <= 0) {
|
|
28
|
+
return res.status(400).json({ error: "Invalid trade amount" });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Check if required fields are provided
|
|
32
|
+
if (!exchange_id || !pair || !side) {
|
|
33
|
+
return res
|
|
34
|
+
.status(400)
|
|
35
|
+
.json({ error: "Exchange, pair, and side are required" });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Process crypto trade using your trading system
|
|
39
|
+
const trade_id = await processCryptoTrade({
|
|
40
|
+
exchange_id,
|
|
41
|
+
pair,
|
|
42
|
+
side,
|
|
43
|
+
amount_usd,
|
|
44
|
+
source_wallet_type,
|
|
45
|
+
idempotency_key,
|
|
46
|
+
trade_reason,
|
|
47
|
+
risk_score,
|
|
48
|
+
agent_id: passport.passport_id,
|
|
49
|
+
agent_name: passport.metadata?.template_name || "Unknown Agent",
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Log the trade
|
|
53
|
+
console.log(
|
|
54
|
+
`Crypto trade processed: ${trade_id} for ${amount_usd} USD by agent ${passport.passport_id}`
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
res.json({
|
|
58
|
+
success: true,
|
|
59
|
+
trade_id,
|
|
60
|
+
exchange_id,
|
|
61
|
+
pair,
|
|
62
|
+
side,
|
|
63
|
+
amount_usd,
|
|
64
|
+
status: "processed",
|
|
65
|
+
decision_id: req.policyResult.decision_id,
|
|
66
|
+
});
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error("Crypto trade processing error:", error);
|
|
69
|
+
res.status(500).json({ error: "Internal server error" });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// Mock crypto trade processing function
|
|
75
|
+
async function processCryptoTrade({
|
|
76
|
+
exchange_id,
|
|
77
|
+
pair,
|
|
78
|
+
side,
|
|
79
|
+
amount_usd,
|
|
80
|
+
source_wallet_type,
|
|
81
|
+
idempotency_key,
|
|
82
|
+
trade_reason,
|
|
83
|
+
risk_score,
|
|
84
|
+
agent_id,
|
|
85
|
+
}) {
|
|
86
|
+
// Simulate trading system call
|
|
87
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
88
|
+
|
|
89
|
+
// Log trade details for audit
|
|
90
|
+
console.log(`Processing crypto trade:`, {
|
|
91
|
+
exchange_id,
|
|
92
|
+
pair,
|
|
93
|
+
side,
|
|
94
|
+
amount_usd,
|
|
95
|
+
source_wallet_type,
|
|
96
|
+
idempotency_key,
|
|
97
|
+
trade_reason,
|
|
98
|
+
risk_score,
|
|
99
|
+
agent_id,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return `trade_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const PORT = process.env.PORT || 3000;
|
|
106
|
+
app.listen(PORT, () => {
|
|
107
|
+
console.log(`Crypto trading service running on port ${PORT}`);
|
|
108
|
+
console.log("Protected by APort finance.crypto.trade.v1 policy pack");
|
|
109
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Example: finance.crypto.trade.v1 Policy
|
|
3
|
+
*
|
|
4
|
+
* This is a quick-start example showing the basic usage of the finance.crypto.trade.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 crypto trade endpoint with policy protection
|
|
15
|
+
app.post(
|
|
16
|
+
"/crypto/trade",
|
|
17
|
+
requirePolicy("finance.crypto.trade.v1"),
|
|
18
|
+
async (req, res) => {
|
|
19
|
+
try {
|
|
20
|
+
const { exchange_id, pair, side, amount_usd } = req.body;
|
|
21
|
+
const passport = req.policyResult.passport;
|
|
22
|
+
|
|
23
|
+
// Process the crypto trade (your business logic here)
|
|
24
|
+
const trade_id = `trade_${Date.now()}`;
|
|
25
|
+
|
|
26
|
+
console.log(`Crypto trade processed: ${trade_id} for ${amount_usd} USD`);
|
|
27
|
+
|
|
28
|
+
res.json({
|
|
29
|
+
success: true,
|
|
30
|
+
trade_id,
|
|
31
|
+
exchange_id,
|
|
32
|
+
pair,
|
|
33
|
+
side,
|
|
34
|
+
amount_usd,
|
|
35
|
+
decision_id: req.policyResult.decision_id,
|
|
36
|
+
});
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error("Crypto trade error:", error);
|
|
39
|
+
res.status(500).json({ error: "Internal server error" });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// Example client request
|
|
45
|
+
const exampleRequest = {
|
|
46
|
+
exchange_id: "coinbase",
|
|
47
|
+
pair: "BTC-USD",
|
|
48
|
+
side: "buy",
|
|
49
|
+
amount_usd: 1000,
|
|
50
|
+
source_wallet_type: "hot",
|
|
51
|
+
idempotency_key: "crypto-trade-123",
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
console.log("Example request:", JSON.stringify(exampleRequest, null, 2));
|
|
55
|
+
|
|
56
|
+
const PORT = process.env.PORT || 3000;
|
|
57
|
+
app.listen(PORT, () => {
|
|
58
|
+
console.log(`Minimal crypto trading service running on port ${PORT}`);
|
|
59
|
+
console.log("Protected by APort finance.crypto.trade.v1 policy");
|
|
60
|
+
console.log(
|
|
61
|
+
`Try: curl -X POST http://localhost:${PORT}/crypto/trade -H "Content-Type: application/json" -d '${JSON.stringify(
|
|
62
|
+
exampleRequest
|
|
63
|
+
)}'`
|
|
64
|
+
);
|
|
65
|
+
});
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "finance.crypto.trade.v1",
|
|
3
|
+
"name": "Crypto Asset Trading Policy",
|
|
4
|
+
"description": "Pre-action governance for agent-initiated crypto asset trades. Enforces rules on specific tokens, exchanges, wallet types, and transaction value.",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"status": "active",
|
|
7
|
+
"requires_capabilities": ["finance.crypto.trade"],
|
|
8
|
+
"min_assurance": "L3",
|
|
9
|
+
"limits_required": [
|
|
10
|
+
"allowed_exchanges",
|
|
11
|
+
"allowed_tokens",
|
|
12
|
+
"max_trade_size_usd",
|
|
13
|
+
"max_hot_wallet_trade_usd",
|
|
14
|
+
"max_daily_trade_volume_usd",
|
|
15
|
+
"max_trades_per_day"
|
|
16
|
+
],
|
|
17
|
+
"required_fields": ["exchange_id", "pair", "side", "amount_usd"],
|
|
18
|
+
"optional_fields": [
|
|
19
|
+
"source_wallet_type",
|
|
20
|
+
"idempotency_key",
|
|
21
|
+
"trade_reason",
|
|
22
|
+
"risk_score"
|
|
23
|
+
],
|
|
24
|
+
"enforcement": {
|
|
25
|
+
"exchange_allowed": true,
|
|
26
|
+
"token_allowed": true,
|
|
27
|
+
"trade_size_limit": true,
|
|
28
|
+
"hot_wallet_limit": true,
|
|
29
|
+
"daily_volume_limit": true,
|
|
30
|
+
"trade_frequency_limit": true,
|
|
31
|
+
"idempotency_required": true
|
|
32
|
+
},
|
|
33
|
+
"mcp": {
|
|
34
|
+
"require_allowlisted_if_present": true
|
|
35
|
+
},
|
|
36
|
+
"advice": [
|
|
37
|
+
"Cache /verify with ETag; 60s TTL",
|
|
38
|
+
"Subscribe to status webhooks for instant suspend",
|
|
39
|
+
"Log all crypto trade attempts for Verifiable Attestation",
|
|
40
|
+
"Implement real-time price validation before trade execution",
|
|
41
|
+
"Use cold storage for large amounts and hot wallets for small trades",
|
|
42
|
+
"Monitor exchange connectivity and implement circuit breakers",
|
|
43
|
+
"Implement slippage protection for volatile markets",
|
|
44
|
+
"Use unique idempotency keys to prevent duplicate trades",
|
|
45
|
+
"Maintain exchange allowlists for trusted platforms only",
|
|
46
|
+
"Implement progressive limits for new trading pairs",
|
|
47
|
+
"Monitor for unusual trading patterns and potential manipulation",
|
|
48
|
+
"Use multi-signature wallets for high-value transactions"
|
|
49
|
+
],
|
|
50
|
+
"cache": {
|
|
51
|
+
"default_ttl_seconds": 30,
|
|
52
|
+
"suspend_invalidate_seconds": 15
|
|
53
|
+
},
|
|
54
|
+
"required_context": {
|
|
55
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
56
|
+
"type": "object",
|
|
57
|
+
"required": ["exchange_id", "pair", "side", "amount_usd"],
|
|
58
|
+
"properties": {
|
|
59
|
+
"exchange_id": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"description": "The exchange where the trade is being executed (e.g., 'coinbase', 'binance')."
|
|
62
|
+
},
|
|
63
|
+
"pair": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"pattern": "^[A-Z]+-[A-Z]+$",
|
|
66
|
+
"description": "The trading pair (e.g., 'BTC-USD')."
|
|
67
|
+
},
|
|
68
|
+
"side": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"enum": ["buy", "sell"],
|
|
71
|
+
"description": "The side of the trade."
|
|
72
|
+
},
|
|
73
|
+
"amount_usd": {
|
|
74
|
+
"type": "integer",
|
|
75
|
+
"description": "The total value of the trade in USD minor units (cents)."
|
|
76
|
+
},
|
|
77
|
+
"source_wallet_type": {
|
|
78
|
+
"type": "string",
|
|
79
|
+
"enum": ["hot", "cold", "custodial"],
|
|
80
|
+
"description": "The type of wallet initiating the trade."
|
|
81
|
+
},
|
|
82
|
+
"mcp_servers": {
|
|
83
|
+
"type": "array",
|
|
84
|
+
"items": { "type": "string" },
|
|
85
|
+
"description": "MCP servers being used in this request (e.g., [\"https://mcp.coinbase.com\"])"
|
|
86
|
+
},
|
|
87
|
+
"mcp_tools": {
|
|
88
|
+
"type": "array",
|
|
89
|
+
"items": { "type": "string" },
|
|
90
|
+
"description": "MCP tools being used in this request (e.g., [\"coinbase.trades.execute\"])"
|
|
91
|
+
},
|
|
92
|
+
"mcp_server": {
|
|
93
|
+
"type": "string",
|
|
94
|
+
"description": "Single MCP server being used (backward compatibility - use mcp_servers array for multiple)"
|
|
95
|
+
},
|
|
96
|
+
"mcp_tool": {
|
|
97
|
+
"type": "string",
|
|
98
|
+
"description": "Single MCP tool being used (backward compatibility - use mcp_tools array for multiple)"
|
|
99
|
+
},
|
|
100
|
+
"mcp_session": {
|
|
101
|
+
"type": "string",
|
|
102
|
+
"description": "MCP session identifier for audit trail (optional)"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"evaluation_rules": [
|
|
107
|
+
{
|
|
108
|
+
"name": "passport_status_active",
|
|
109
|
+
"condition": "passport.status == 'active'",
|
|
110
|
+
"deny_code": "oap.passport_suspended"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"name": "exchange_is_allowed",
|
|
114
|
+
"condition": "context.exchange_id in passport.limits.finance.crypto.trade.allowed_exchanges",
|
|
115
|
+
"deny_code": "oap.exchange_forbidden"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"name": "token_is_allowed",
|
|
119
|
+
"condition": "all(token in passport.limits.finance.crypto.trade.allowed_tokens for token in context.pair.split('-'))",
|
|
120
|
+
"deny_code": "oap.token_forbidden"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"name": "trade_size_limit_check",
|
|
124
|
+
"condition": "context.amount_usd <= passport.limits.finance.crypto.trade.max_trade_size_usd",
|
|
125
|
+
"deny_code": "oap.limit_exceeded"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"name": "hot_wallet_limit_check",
|
|
129
|
+
"condition": "not (context.source_wallet_type == 'hot' and context.amount_usd > passport.limits.finance.crypto.trade.max_hot_wallet_trade_usd)",
|
|
130
|
+
"deny_code": "oap.wallet_limit_exceeded",
|
|
131
|
+
"description": "Hot wallet trades must not exceed maximum allowed amount."
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"name": "assurance_minimum",
|
|
135
|
+
"condition": "passport.assurance_level >= policy.min_assurance",
|
|
136
|
+
"deny_code": "oap.assurance_insufficient",
|
|
137
|
+
"description": "Assurance level must meet minimum requirement for crypto trading."
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"name": "daily_volume_limit_check",
|
|
141
|
+
"condition": "daily_volume_total + context.amount_usd <= passport.limits.finance.crypto.trade.max_daily_trade_volume_usd",
|
|
142
|
+
"deny_code": "oap.daily_volume_exceeded",
|
|
143
|
+
"description": "Daily trading volume must not exceed limit."
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"name": "trade_frequency_check",
|
|
147
|
+
"condition": "trades_today_count < passport.limits.finance.crypto.trade.max_trades_per_day",
|
|
148
|
+
"deny_code": "oap.trade_frequency_exceeded",
|
|
149
|
+
"description": "Number of trades per day must not exceed limit."
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"name": "idempotency_check",
|
|
153
|
+
"condition": "context.idempotency_key not in recent_keys",
|
|
154
|
+
"deny_code": "oap.idempotency_conflict",
|
|
155
|
+
"description": "Idempotency key must be unique to prevent duplicate trades."
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"name": "pair_volatility_check",
|
|
159
|
+
"condition": "context.risk_score <= passport.limits.finance.crypto.trade.max_risk_score OR not context.risk_score",
|
|
160
|
+
"deny_code": "oap.risk_score_exceeded",
|
|
161
|
+
"description": "Trade risk score must not exceed maximum allowed."
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"name": "exchange_connectivity_check",
|
|
165
|
+
"condition": "exchange_status[context.exchange_id] == 'online'",
|
|
166
|
+
"deny_code": "oap.exchange_offline",
|
|
167
|
+
"description": "Exchange must be online and accessible."
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"name": "market_hours_check",
|
|
171
|
+
"condition": "is_market_open(context.exchange_id) OR context.side == 'sell'",
|
|
172
|
+
"deny_code": "oap.market_closed",
|
|
173
|
+
"description": "Trades must be during market hours unless selling."
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
}
|