cloudg 0.5.2__tar.gz → 0.6.0__tar.gz
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.
- {cloudg-0.5.2 → cloudg-0.6.0}/PKG-INFO +315 -20
- {cloudg-0.5.2 → cloudg-0.6.0}/README.md +37 -8
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/__init__.py +2 -2
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/api.py +86 -307
- cloudg-0.6.0/cloudg/api_scanners.py +255 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/cli.py +97 -63
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/cli_commands.py +31 -30
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/cli_helpers.py +2 -2
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/cli_inventory.py +2 -2
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/cli_run_helpers.py +64 -34
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/collectors/__init__.py +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/collectors/aws.py +137 -66
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/collectors/aws_services.py +3 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/collectors/aws_services_extended.py +5 -3
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/collectors/azure.py +49 -22
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/collectors/gcp.py +62 -53
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/collectors/multi.py +92 -41
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/config.py +216 -16
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/coverage.py +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/credentials.py +83 -35
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/graph/builder.py +128 -13
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/graph/ontology.py +27 -17
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/graph/ontology_rules.py +255 -77
- cloudg-0.6.0/cloudg/graph/ports.py +174 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/graph/rag_export.py +135 -80
- cloudg-0.6.0/cloudg/graph/reachability.py +527 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/ingest.py +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/__init__.py +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_deep.py +3 -3
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/_common.py +40 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/alarms.py +2 -2
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_deep.py +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/__init__.py +2 -2
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/query.py +50 -8
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/dependencies.py +205 -18
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_deep.py +4 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/context.py +2 -2
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/iam.py +2 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/names.py +21 -4
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/kubernetes.py +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/linker.py +9 -185
- cloudg-0.6.0/cloudg/inventory/linker_rules.py +252 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/mapper.py +104 -41
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/mapper_result.py +33 -20
- cloudg-0.6.0/cloudg/mcp/__init__.py +138 -0
- cloudg-0.6.0/cloudg/mcp/__main__.py +10 -0
- cloudg-0.6.0/cloudg/mcp/adapters/__init__.py +232 -0
- cloudg-0.6.0/cloudg/mcp/adapters/_common.py +276 -0
- cloudg-0.6.0/cloudg/mcp/adapters/export.py +259 -0
- cloudg-0.6.0/cloudg/mcp/adapters/fastmcp.py +583 -0
- cloudg-0.6.0/cloudg/mcp/adapters/lowlevel.py +593 -0
- cloudg-0.6.0/cloudg/mcp/adapters/mcp_sdk.py +108 -0
- cloudg-0.6.0/cloudg/mcp/catalog/__init__.py +89 -0
- cloudg-0.6.0/cloudg/mcp/catalog/_common.py +545 -0
- cloudg-0.6.0/cloudg/mcp/catalog/_compliance.py +330 -0
- cloudg-0.6.0/cloudg/mcp/catalog/_findings.py +236 -0
- cloudg-0.6.0/cloudg/mcp/catalog/_graph.py +480 -0
- cloudg-0.6.0/cloudg/mcp/catalog/_graph_impact.py +400 -0
- cloudg-0.6.0/cloudg/mcp/catalog/_inventory.py +292 -0
- cloudg-0.6.0/cloudg/mcp/catalog/_live.py +445 -0
- cloudg-0.6.0/cloudg/mcp/catalog/_resources.py +161 -0
- cloudg-0.6.0/cloudg/mcp/catalog/_sparql.py +247 -0
- cloudg-0.6.0/cloudg/mcp/catalog/compliance.py +212 -0
- cloudg-0.6.0/cloudg/mcp/catalog/export.py +180 -0
- cloudg-0.6.0/cloudg/mcp/catalog/findings.py +499 -0
- cloudg-0.6.0/cloudg/mcp/catalog/graph.py +384 -0
- cloudg-0.6.0/cloudg/mcp/catalog/inventory.py +387 -0
- cloudg-0.6.0/cloudg/mcp/catalog/live.py +449 -0
- cloudg-0.6.0/cloudg/mcp/catalog/meta.py +293 -0
- cloudg-0.6.0/cloudg/mcp/catalog/ontology.py +352 -0
- cloudg-0.6.0/cloudg/mcp/catalog/privacy.py +424 -0
- cloudg-0.6.0/cloudg/mcp/catalog/prompts.py +587 -0
- cloudg-0.6.0/cloudg/mcp/catalog/resources.py +602 -0
- cloudg-0.6.0/cloudg/mcp/catalog/workspace.py +240 -0
- cloudg-0.6.0/cloudg/mcp/cli.py +471 -0
- cloudg-0.6.0/cloudg/mcp/cli_common.py +238 -0
- cloudg-0.6.0/cloudg/mcp/cli_config.py +148 -0
- cloudg-0.6.0/cloudg/mcp/content.py +275 -0
- cloudg-0.6.0/cloudg/mcp/context.py +162 -0
- cloudg-0.6.0/cloudg/mcp/core.py +643 -0
- cloudg-0.6.0/cloudg/mcp/layer.py +653 -0
- cloudg-0.6.0/cloudg/mcp/layer_options.py +96 -0
- cloudg-0.6.0/cloudg/mcp/layer_output.py +452 -0
- cloudg-0.6.0/cloudg/mcp/middleware.py +619 -0
- cloudg-0.6.0/cloudg/mcp/native/__init__.py +61 -0
- cloudg-0.6.0/cloudg/mcp/native/_http_io.py +404 -0
- cloudg-0.6.0/cloudg/mcp/native/_http_sessions.py +262 -0
- cloudg-0.6.0/cloudg/mcp/native/_methods.py +238 -0
- cloudg-0.6.0/cloudg/mcp/native/_shared.py +178 -0
- cloudg-0.6.0/cloudg/mcp/native/auth.py +229 -0
- cloudg-0.6.0/cloudg/mcp/native/http.py +597 -0
- cloudg-0.6.0/cloudg/mcp/native/jsonrpc.py +152 -0
- cloudg-0.6.0/cloudg/mcp/native/protocol.py +608 -0
- cloudg-0.6.0/cloudg/mcp/native/stdio.py +215 -0
- cloudg-0.6.0/cloudg/mcp/policies/airgapped.yaml +8 -0
- cloudg-0.6.0/cloudg/mcp/policies/audit.yaml +15 -0
- cloudg-0.6.0/cloudg/mcp/policies/open.yaml +11 -0
- cloudg-0.6.0/cloudg/mcp/policies/read_only.yaml +8 -0
- cloudg-0.6.0/cloudg/mcp/policies/soc-analyst.yaml +64 -0
- cloudg-0.6.0/cloudg/mcp/policies/standard.yaml +56 -0
- cloudg-0.6.0/cloudg/mcp/policies/strict.yaml +62 -0
- cloudg-0.6.0/cloudg/mcp/policy/__init__.py +133 -0
- cloudg-0.6.0/cloudg/mcp/policy/access.py +260 -0
- cloudg-0.6.0/cloudg/mcp/policy/audit.py +184 -0
- cloudg-0.6.0/cloudg/mcp/policy/config.py +229 -0
- cloudg-0.6.0/cloudg/mcp/policy/describe.py +98 -0
- cloudg-0.6.0/cloudg/mcp/policy/engine.py +346 -0
- cloudg-0.6.0/cloudg/mcp/policy/pipelines.py +282 -0
- cloudg-0.6.0/cloudg/mcp/policy/ratelimit.py +95 -0
- cloudg-0.6.0/cloudg/mcp/policy/vaults.py +48 -0
- cloudg-0.6.0/cloudg/mcp/schema.py +198 -0
- cloudg-0.6.0/cloudg/mcp/server.py +744 -0
- cloudg-0.6.0/cloudg/mcp/state/__init__.py +72 -0
- cloudg-0.6.0/cloudg/mcp/state/base.py +83 -0
- cloudg-0.6.0/cloudg/mcp/state/dataset.py +592 -0
- cloudg-0.6.0/cloudg/mcp/state/diff.py +140 -0
- cloudg-0.6.0/cloudg/mcp/state/loading.py +358 -0
- cloudg-0.6.0/cloudg/mcp/state/workspace.py +347 -0
- cloudg-0.6.0/cloudg/mcp/transforms/__init__.py +230 -0
- cloudg-0.6.0/cloudg/mcp/transforms/annotation.py +518 -0
- cloudg-0.6.0/cloudg/mcp/transforms/base.py +83 -0
- cloudg-0.6.0/cloudg/mcp/transforms/builtin_detectors.py +399 -0
- cloudg-0.6.0/cloudg/mcp/transforms/builtin_rules.py +221 -0
- cloudg-0.6.0/cloudg/mcp/transforms/detectors.py +370 -0
- cloudg-0.6.0/cloudg/mcp/transforms/entities.py +366 -0
- cloudg-0.6.0/cloudg/mcp/transforms/projection.py +403 -0
- cloudg-0.6.0/cloudg/mcp/transforms/redaction.py +475 -0
- cloudg-0.6.0/cloudg/mcp/transforms/redaction_run.py +612 -0
- cloudg-0.6.0/cloudg/mcp/transforms/strategies.py +222 -0
- cloudg-0.6.0/cloudg/mcp/transforms/substitution.py +578 -0
- cloudg-0.6.0/cloudg/mcp/transforms/textscan.py +95 -0
- cloudg-0.6.0/cloudg/mcp/transforms/vault.py +531 -0
- cloudg-0.6.0/cloudg/mcp/transforms/vault_formats.py +561 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/normaliser.py +9 -9
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/policies/custodian-aws-security.yml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/policies/custodian-azure.yml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/policies/custodian-gcp.yml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/registry.py +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/renderers/html_report.py +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/renderers/svg.py +3 -3
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/renderers/svg_layout.py +46 -27
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/renderers/terraform_export.py +6 -6
- cloudg-0.6.0/cloudg/resilience/__init__.py +127 -0
- cloudg-0.6.0/cloudg/resilience/aws.py +395 -0
- cloudg-0.6.0/cloudg/resilience/azure.py +344 -0
- cloudg-0.6.0/cloudg/resilience/breaker.py +239 -0
- cloudg-0.6.0/cloudg/resilience/errors.py +490 -0
- cloudg-0.6.0/cloudg/resilience/gcp.py +169 -0
- cloudg-0.6.0/cloudg/resilience/governor.py +441 -0
- cloudg-0.6.0/cloudg/resilience/guard.py +494 -0
- cloudg-0.6.0/cloudg/resilience/limiter.py +758 -0
- cloudg-0.6.0/cloudg/resilience/retry.py +290 -0
- cloudg-0.6.0/cloudg/resilience/stats.py +354 -0
- cloudg-0.6.0/cloudg/retry.py +180 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/check_equivalence.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/cis_aws_v3.yaml +2 -2
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/cis_azure_v2.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/cis_gcp_v3.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/aws_foundational_security_best_practices_aws.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/cis_5.0_aws.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/cis_5.0_azure.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/cis_5.0_gcp.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/gdpr_aws.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/hipaa_aws.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/hipaa_azure.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/hipaa_gcp.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/iso27001_2022_aws.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/iso27001_2022_azure.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/iso27001_2022_gcp.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/mitre_attack_aws.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/mitre_attack_azure.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/mitre_attack_gcp.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/nist_800_53_revision_5_aws.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/nist_csf_2.0_aws.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/pci_4.0_aws.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/pci_4.0_azure.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/pci_4.0_gcp.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/soc2_aws.yaml +3 -3
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/soc2_azure.yaml +3 -3
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/frameworks/soc2_gcp.yaml +4 -4
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/gdpr.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/hipaa_security_rule.yaml +6 -6
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/iso_27001_2022.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/nist_800_53.yaml +8 -8
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/pci_dss_v4.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/rules/soc2_tsc.yaml +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/scanners/checkov.py +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/scanners/prowler.py +3 -3
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/scanners/scoutsuite.py +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/templates/report.html.j2 +1 -1
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/ui.py +8 -8
- {cloudg-0.5.2 → cloudg-0.6.0}/docs/DOCUMENTATION.md +309 -18
- cloudg-0.6.0/docs/INVENTORY_CATALOG.md +2895 -0
- cloudg-0.6.0/docs/INVENTORY_INTERNALS.md +1380 -0
- cloudg-0.6.0/docs/INVENTORY_REFERENCE.md +1518 -0
- cloudg-0.6.0/docs/MCP.md +2902 -0
- cloudg-0.6.0/docs/MCP_INTERNALS.md +1553 -0
- cloudg-0.6.0/docs/MCP_PRIVACY.md +1913 -0
- cloudg-0.6.0/docs/MCP_TOOLS.md +11755 -0
- cloudg-0.6.0/docs/RESILIENCE.md +733 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/pyproject.toml +16 -5
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/conftest.py +39 -0
- cloudg-0.6.0/tests/mcp/__init__.py +0 -0
- cloudg-0.6.0/tests/mcp/_adapter_testkit.py +157 -0
- cloudg-0.6.0/tests/mcp/conftest.py +91 -0
- cloudg-0.6.0/tests/mcp/fake_secrets.py +39 -0
- cloudg-0.6.0/tests/mcp/fixtures/__init__.py +1 -0
- cloudg-0.6.0/tests/mcp/fixtures/sample_estate.py +905 -0
- cloudg-0.6.0/tests/mcp/test_adapters_args_model.py +117 -0
- cloudg-0.6.0/tests/mcp/test_adapters_export.py +111 -0
- cloudg-0.6.0/tests/mcp/test_adapters_fastmcp.py +206 -0
- cloudg-0.6.0/tests/mcp/test_adapters_sdk.py +341 -0
- cloudg-0.6.0/tests/mcp/test_adapters_serve.py +414 -0
- cloudg-0.6.0/tests/mcp/test_catalog_audit_fixes.py +451 -0
- cloudg-0.6.0/tests/mcp/test_catalog_compliance.py +91 -0
- cloudg-0.6.0/tests/mcp/test_catalog_export_live_meta.py +596 -0
- cloudg-0.6.0/tests/mcp/test_catalog_findings.py +222 -0
- cloudg-0.6.0/tests/mcp/test_catalog_graph.py +222 -0
- cloudg-0.6.0/tests/mcp/test_catalog_inventory.py +204 -0
- cloudg-0.6.0/tests/mcp/test_catalog_ontology.py +134 -0
- cloudg-0.6.0/tests/mcp/test_catalog_prompts.py +162 -0
- cloudg-0.6.0/tests/mcp/test_catalog_registry.py +237 -0
- cloudg-0.6.0/tests/mcp/test_catalog_regressions.py +374 -0
- cloudg-0.6.0/tests/mcp/test_catalog_resources.py +259 -0
- cloudg-0.6.0/tests/mcp/test_catalog_workspace.py +108 -0
- cloudg-0.6.0/tests/mcp/test_cli_mcp.py +248 -0
- cloudg-0.6.0/tests/mcp/test_middleware.py +318 -0
- cloudg-0.6.0/tests/mcp/test_native_auth.py +75 -0
- cloudg-0.6.0/tests/mcp/test_native_http.py +650 -0
- cloudg-0.6.0/tests/mcp/test_native_privacy.py +200 -0
- cloudg-0.6.0/tests/mcp/test_native_protocol.py +580 -0
- cloudg-0.6.0/tests/mcp/test_native_stdio.py +247 -0
- cloudg-0.6.0/tests/mcp/test_policy.py +753 -0
- cloudg-0.6.0/tests/mcp/test_policy_regressions.py +697 -0
- cloudg-0.6.0/tests/mcp/test_privacy_tools.py +528 -0
- cloudg-0.6.0/tests/mcp/test_state.py +437 -0
- cloudg-0.6.0/tests/mcp/test_transforms.py +1401 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_aws_application.py +21 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_aws_network_ext.py +2 -2
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_collectors.py +57 -0
- cloudg-0.6.0/tests/test_graph_builder.py +253 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_inventory_deep.py +33 -1
- cloudg-0.6.0/tests/test_ontology.py +1091 -0
- cloudg-0.6.0/tests/test_ports.py +392 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_rag_export.py +131 -0
- cloudg-0.6.0/tests/test_reachability.py +513 -0
- cloudg-0.6.0/tests/test_resilience.py +1884 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_scanner_orchestration.py +1 -1
- cloudg-0.5.2/cloudg/graph/reachability.py +0 -261
- cloudg-0.5.2/cloudg/retry.py +0 -106
- cloudg-0.5.2/tests/test_ontology.py +0 -389
- {cloudg-0.5.2 → cloudg-0.6.0}/.gitignore +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/LICENSE +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/collectors/base.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/collectors/gcp_assets.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/graph/__init__.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/_util.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_deep_tasks.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/__init__.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/_base.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/_policy_grants.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/__init__.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/backup.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/batch.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/build_deploy.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/cicd.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/compute.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/containers_ext.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/firehose.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/integration.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/logs.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/operations.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/application/serverless_ext.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/cloudcontrol.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/containers/__init__.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/containers/ecr.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/containers/ecs.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/containers/eks.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/__init__.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/_common.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/analytics.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/bedrock.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/databases.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/glue.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/glue_etl.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/lakeformation.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/messaging.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/registry.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/sagemaker.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/serverless_data.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/data_ml/transfer.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/__init__.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/_common.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/cognito.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/data_protection.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/iam_extras.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/identity_center.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/ram.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/s3_access_points.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/service_catalog.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/sharing.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/governance/stack_sets.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/identity.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/__init__.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/_common.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/apigateway.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/cloudfront.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/direct_connect.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/elb_rules.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/global_accelerator.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/lattice.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/network_manager.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/privatelink.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/resolver.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/tgw.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/network_ext/vpn.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/platform/__init__.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/platform/compute_fabric.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/platform/data.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/platform/dns_iac_ops.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/platform/load_balancing.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/platform/storage.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/security/__init__.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/security/_common.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/security/detection.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/security/network_protection.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/security/posture.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/serverless/__init__.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/serverless/apigateway.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/serverless/functions.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/aws_services/serverless/messaging.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/arm_types.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/builder.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/compute.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/data_security.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/edge.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/helpers.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/network.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/registry.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_graph/web_containers.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/azure_hierarchy.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/catalogs/__init__.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/catalogs/aws_arn_types.yaml +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/catalogs/aws_cloudcontrol.yaml +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/catalogs/azure_arm_types.yaml +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/catalogs/gcp_asset_types.yaml +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_hierarchy.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/__init__.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/compute.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/data_logging.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/gke.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/load_balancing.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/messaging.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/network.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/pipelines.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/resource_manager.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/gcp_relations/serverless.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/linker_index.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/organization.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/inventory/organization_assets.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/policies/custodian.yml +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/region_discovery.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/renderers/__init__.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/renderers/json_export.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/scanners/__init__.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/scanners/iam_linter.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/scanners/trivy.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/schema/__init__.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/cloudg/schema/models.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/__init__.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_api.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_aws_data_ml.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_aws_governance.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_azure_inventory.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_catalogs_cloudcontrol.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_gcp_inventory.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_ingest.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_inventory.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_normaliser.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_region_discovery.py +0 -0
- {cloudg-0.5.2 → cloudg-0.6.0}/tests/test_terraform_export.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cloudg
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: cloudg (cloud graphing) - multi-cloud infrastructure mapping, knowledge graphs and security intelligence for AWS, Azure and GCP
|
|
5
5
|
Project-URL: Homepage, https://github.com/morpheuslord/cloudg
|
|
6
6
|
Project-URL: Repository, https://github.com/morpheuslord/cloudg
|
|
@@ -9,7 +9,7 @@ Project-URL: Changelog, https://github.com/morpheuslord/cloudg/blob/main/CHANGEL
|
|
|
9
9
|
Author-email: morpheuslord <chiranjeevi.naidu@proton.me>
|
|
10
10
|
License: MIT
|
|
11
11
|
License-File: LICENSE
|
|
12
|
-
Keywords: audit,aws,azure,cloud,gcp,graph,ontology,security,terraform
|
|
12
|
+
Keywords: audit,aws,azure,cloud,gcp,graph,mcp,model-context-protocol,ontology,security,terraform
|
|
13
13
|
Classifier: Development Status :: 4 - Beta
|
|
14
14
|
Classifier: Environment :: Console
|
|
15
15
|
Classifier: Intended Audience :: Information Technology
|
|
@@ -44,6 +44,7 @@ Requires-Dist: azure-mgmt-storage>=21.0; extra == 'all'
|
|
|
44
44
|
Requires-Dist: boto3>=1.28; extra == 'all'
|
|
45
45
|
Requires-Dist: google-auth>=2.22; extra == 'all'
|
|
46
46
|
Requires-Dist: google-cloud-asset>=3.0; extra == 'all'
|
|
47
|
+
Requires-Dist: mcp<3,>=1.30; extra == 'all'
|
|
47
48
|
Provides-Extra: aws
|
|
48
49
|
Requires-Dist: aioboto3>=12.0; extra == 'aws'
|
|
49
50
|
Requires-Dist: boto3>=1.28; extra == 'aws'
|
|
@@ -59,6 +60,7 @@ Requires-Dist: azure-mgmt-storage>=21.0; extra == 'azure'
|
|
|
59
60
|
Provides-Extra: dev
|
|
60
61
|
Requires-Dist: aioboto3>=12.0; extra == 'dev'
|
|
61
62
|
Requires-Dist: boto3>=1.28; extra == 'dev'
|
|
63
|
+
Requires-Dist: mcp<3,>=1.30; extra == 'dev'
|
|
62
64
|
Requires-Dist: moto[all]>=5.0; extra == 'dev'
|
|
63
65
|
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
64
66
|
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
@@ -70,6 +72,8 @@ Requires-Dist: weasyprint>=60.0; extra == 'full'
|
|
|
70
72
|
Provides-Extra: gcp
|
|
71
73
|
Requires-Dist: google-auth>=2.22; extra == 'gcp'
|
|
72
74
|
Requires-Dist: google-cloud-asset>=3.0; extra == 'gcp'
|
|
75
|
+
Provides-Extra: mcp
|
|
76
|
+
Requires-Dist: mcp<3,>=1.30; extra == 'mcp'
|
|
73
77
|
Description-Content-Type: text/markdown
|
|
74
78
|
|
|
75
79
|
<p align="center">
|
|
@@ -84,9 +88,17 @@ This is the complete reference: every CLI command, the exact input and output fo
|
|
|
84
88
|
|
|
85
89
|
The inventory mapper (`cloudg map`, `cloudg deps`, `cloudg.inventory`) has three deeper companion documents:
|
|
86
90
|
|
|
87
|
-
- [Inventory reference](INVENTORY_REFERENCE.md): the structure of every return value and exported file, field by field, with annotated examples (`CloudAsset`, `NetworkEdge`, relation objects, `InventoryResult`, `summary`, coverage records, dependency trees, blast radius, security coverage, `inventory-map.json`, `inventory-graph.json`, GraphML, `inventory-organization.json`, `asset-map.json`, `compliance-map.json`, `cloudg deps --json`).
|
|
88
|
-
- [Inventory catalog](INVENTORY_CATALOG.md): all 156 asset types with the native resource types mapped to each, the metadata keys and relations each one carries, and the full relationship matrix.
|
|
89
|
-
- [Inventory internals](INVENTORY_INTERNALS.md): how a mapping run works, every module, the 133 AWS collector tasks, the linker's resolution rules, the Azure and GCP extractor frameworks, dependency semantics, catalogs, and recipes for adding collectors, extractors and asset types.
|
|
91
|
+
- [Inventory reference](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_REFERENCE.md): the structure of every return value and exported file, field by field, with annotated examples (`CloudAsset`, `NetworkEdge`, relation objects, `InventoryResult`, `summary`, coverage records, dependency trees, blast radius, security coverage, `inventory-map.json`, `inventory-graph.json`, GraphML, `inventory-organization.json`, `asset-map.json`, `compliance-map.json`, `cloudg deps --json`).
|
|
92
|
+
- [Inventory catalog](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_CATALOG.md): all 156 asset types with the native resource types mapped to each, the metadata keys and relations each one carries, and the full relationship matrix.
|
|
93
|
+
- [Inventory internals](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_INTERNALS.md): how a mapping run works, every module, the 133 AWS collector tasks, the linker's resolution rules, the Azure and GCP extractor frameworks, dependency semantics, catalogs, and recipes for adding collectors, extractors and asset types.
|
|
94
|
+
|
|
95
|
+
Since 0.6.0 cloudg also runs as an MCP server for AI agents, and paces every cloud API call it makes. Both have their own documents on GitHub:
|
|
96
|
+
|
|
97
|
+
- [MCP guide](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP.md): installing and serving the MCP layer, client configuration, transports and protocol versions, mounting it into an existing MCP server, in-process use, middleware, the live tools.
|
|
98
|
+
- [MCP tool reference](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP_TOOLS.md): every tool, resource, resource template and prompt, with arguments and result shapes.
|
|
99
|
+
- [MCP privacy](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP_PRIVACY.md): policies, profiles, roles, transforms, detectors and the pseudonym vault.
|
|
100
|
+
- [MCP internals](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP_INTERNALS.md): how the layer is built, for contributors.
|
|
101
|
+
- [Rate limits and throttling](https://github.com/morpheuslord/cloudg/blob/main/docs/RESILIENCE.md): the shared rate limiter, circuit breakers and retries, per-provider wiring, every `ratelimit` key, and the guard on live collections.
|
|
90
102
|
|
|
91
103
|
Contents:
|
|
92
104
|
|
|
@@ -99,6 +111,8 @@ Contents:
|
|
|
99
111
|
- [Configuration](#configuration)
|
|
100
112
|
- [Python API](#python-api)
|
|
101
113
|
- [Integration recipes](#integration-recipes)
|
|
114
|
+
- [MCP server](#mcp-server)
|
|
115
|
+
- [Rate limits and throttling](#rate-limits-and-throttling)
|
|
102
116
|
- [Plugin system](#plugin-system)
|
|
103
117
|
- [Authentication](#authentication)
|
|
104
118
|
- [Docker](#docker)
|
|
@@ -112,7 +126,8 @@ pip install cloudg # core
|
|
|
112
126
|
pip install cloudg[aws] # + boto3, aioboto3
|
|
113
127
|
pip install cloudg[azure] # + Azure management SDKs
|
|
114
128
|
pip install cloudg[gcp] # + google-cloud-asset, google-auth
|
|
115
|
-
pip install cloudg[
|
|
129
|
+
pip install cloudg[mcp] # + the official MCP SDK (mcp>=1.30,<3), see MCP server
|
|
130
|
+
pip install cloudg[all] # every provider SDK and the MCP SDK
|
|
116
131
|
```
|
|
117
132
|
|
|
118
133
|
The cloud SDKs are optional extras, pulled in only when you collect from that provider. The core package alone ships the graph engine, the ontology builder, the findings normaliser, the report renderers and `cloudg ingest`. That is enough to aggregate existing scan results on a machine with no cloud access at all.
|
|
@@ -287,7 +302,7 @@ cloudg deps --map ./reports # overview
|
|
|
287
302
|
cloudg deps my-function --direction up --json # machine-readable
|
|
288
303
|
```
|
|
289
304
|
|
|
290
|
-
Each edge is read as "dependent needs dependency": a function needs its role, image, key and the queue that triggers it; an ALB needs the WAF protecting it; a resource needs the stack managing it. `--direction up` shows what an asset needs, `down` what needs it (what breaks or changes with it), `both` the two trees. Without an asset, `deps` prints the most shared dependencies (one KMS key behind forty resources), the largest blast radius, and every cross-account edge. Assets are matched by ARN or resource ID, internal ID, or a unique
|
|
305
|
+
Each edge is read as "dependent needs dependency": a function needs its role, image, key and the queue that triggers it; an ALB needs the WAF protecting it; a resource needs the stack managing it. `--direction up` shows what an asset needs, `down` what needs it (what breaks or changes with it), `both` the two trees. Without an asset, `deps` prints the most shared dependencies (one KMS key behind forty resources), the largest blast radius, and every cross-account edge. Assets are matched by ARN or resource ID, internal ID, a unique name, or a unique ARN tail (the part after the last `/`).
|
|
291
306
|
|
|
292
307
|
### cloudg scan
|
|
293
308
|
|
|
@@ -400,7 +415,7 @@ All commands write into the output directory (`./reports` by default).
|
|
|
400
415
|
| `findings.json` | the machine-readable result, structure below |
|
|
401
416
|
| `raw-findings.json` | pre-normalisation findings (from `scan` and `ingest`) |
|
|
402
417
|
| `topology.svg`, `topology.graphml`, `topology-cytoscape.json` | the graph in three formats |
|
|
403
|
-
| `ontology.ttl`, `ontology.jsonld` | RDF ontology,
|
|
418
|
+
| `ontology.ttl`, `ontology.jsonld` | RDF ontology, 64 inferred relation types, SPARQL-queryable |
|
|
404
419
|
| `rag_chunks.jsonl`, `rag_metadata_index.json` | retrieval-ready chunks, one JSON object per line |
|
|
405
420
|
| `terraform/*.tf.json`, `terraform/import.sh` | Terraform recreation of live infrastructure, 25+ asset types |
|
|
406
421
|
| `inventory-map.json`, `inventory-map.graphml`, `inventory-graph.json` | scanner-independent inventory map: assets, interconnections, summary (`cloudg map`) |
|
|
@@ -408,7 +423,7 @@ All commands write into the output directory (`./reports` by default).
|
|
|
408
423
|
| `inventory-organization.json` | AWS Organization / Control Tower topology (`cloudg map --org`) |
|
|
409
424
|
| `asset-map.json`, `compliance-map.json` | inventory overlaid with scanner findings (`cloudg map --findings`) |
|
|
410
425
|
|
|
411
|
-
The inventory files are specified field by field in the [inventory reference](INVENTORY_REFERENCE.md).
|
|
426
|
+
The inventory files are specified field by field in the [inventory reference](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_REFERENCE.md).
|
|
412
427
|
|
|
413
428
|
`findings.json` has this shape:
|
|
414
429
|
|
|
@@ -448,7 +463,7 @@ The unit every scanner and parser produces.
|
|
|
448
463
|
|
|
449
464
|
| Field | Type | Notes |
|
|
450
465
|
|---|---|---|
|
|
451
|
-
| `id` | `str` | auto-generated UUID |
|
|
466
|
+
| `id` | `str` | auto-generated UUID; reachability findings get a deterministic UUID5 of the rule and the asset's stable key (its ARN, else its asset ID), so the same finding keeps its id from scan to scan |
|
|
452
467
|
| `resource_id` | `str` | internal asset ID or native identifier |
|
|
453
468
|
| `resource_arn` | `str \| None` | cloud-native identifier when known |
|
|
454
469
|
| `severity` | `Severity` | `CRITICAL`, `HIGH`, `MEDIUM`, `LOW`, `INFO` |
|
|
@@ -471,14 +486,14 @@ The unit every scanner and parser produces.
|
|
|
471
486
|
| `id` | `str` | auto UUID |
|
|
472
487
|
| `arn` | `str \| None` | native identifier |
|
|
473
488
|
| `name` | `str` | |
|
|
474
|
-
| `asset_type` | `AssetType` | 156-value taxonomy covering compute, networking, storage, databases, IAM and identity federation, keys and secrets, logging, containers and Kubernetes, integration, data and ML platforms, DNS and deployment, security services and scanners, organization and governance, hybrid networking, `OTHER`; the full list with what maps to each is in the [inventory catalog](INVENTORY_CATALOG.md#asset-types-by-category) |
|
|
489
|
+
| `asset_type` | `AssetType` | 156-value taxonomy covering compute, networking, storage, databases, IAM and identity federation, keys and secrets, logging, containers and Kubernetes, integration, data and ML platforms, DNS and deployment, security services and scanners, organization and governance, hybrid networking, `OTHER`; the full list with what maps to each is in the [inventory catalog](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_CATALOG.md#asset-types-by-category) |
|
|
475
490
|
| `provider` | `CloudProvider` | `AWS`, `AZURE`, `GCP` |
|
|
476
491
|
| `region` | `str` | `"global"` for regionless resources |
|
|
477
492
|
| `account_id` | `str \| None` | |
|
|
478
493
|
| `tags` | `dict[str, str]` | |
|
|
479
494
|
| `metadata` | `dict[str, Any]` | normalised extra attributes; inventory assets also carry declared `relations` and identifier `aliases` |
|
|
480
495
|
| `collected_at` | `datetime` | when the asset was built |
|
|
481
|
-
| `is_internet_exposed` | `bool` | set by the collector
|
|
496
|
+
| `is_internet_exposed` | `bool` | set by the collector; the reachability analysis marks the graph nodes it reaches and reports them as findings, but does not change the asset |
|
|
482
497
|
| `raw_data` | `dict[str, Any]` | raw API payload, never serialised |
|
|
483
498
|
| `display_id` | computed `str` | `arn` or `id` |
|
|
484
499
|
|
|
@@ -491,7 +506,7 @@ Directed edge between two asset IDs (`source_id`, `target_id`); direction always
|
|
|
491
506
|
| Field | Type | Notes |
|
|
492
507
|
|---|---|---|
|
|
493
508
|
| `id` | `str` | auto UUID |
|
|
494
|
-
| `source_id`, `target_id` | `str` | asset IDs; network rule
|
|
509
|
+
| `source_id`, `target_id` | `str` | asset IDs; the far side of a network rule edge can be a CIDR (or an Azure service tag) instead of an asset ID |
|
|
495
510
|
| `edge_type` | `EdgeType` | coarse class, below |
|
|
496
511
|
| `relationship` | `str \| None` | fine-grained ontology relation (`TRIGGERED_BY`, `RUNS_ON`, `CROSS_ACCOUNT_TRUST`, `ENCRYPTED_BY_KMS`, ...) |
|
|
497
512
|
| `properties` | `dict[str, Any]` | relation detail: trust conditions, granted actions, EKS access policies, notification events, `cross_account`, `external_reference`, `hierarchy` |
|
|
@@ -508,7 +523,7 @@ Directed edge between two asset IDs (`source_id`, `target_id`); direction always
|
|
|
508
523
|
| Workload | `INVOKES`, `USES_IMAGE`, `LOGS_TO` |
|
|
509
524
|
| Security and governance | `PROTECTS`, `MONITORS`, `MANAGES`, `GOVERNS` |
|
|
510
525
|
|
|
511
|
-
What each one means, its typical endpoints and how it counts for dependency analysis is in the [inventory reference](INVENTORY_REFERENCE.md#6-edge-types-and-direction); every observed source type → relationship → target type combination is in the [relationship matrix](INVENTORY_CATALOG.md#relationship-matrix).
|
|
526
|
+
What each one means, its typical endpoints and how it counts for dependency analysis is in the [inventory reference](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_REFERENCE.md#6-edge-types-and-direction); every observed source type → relationship → target type combination is in the [relationship matrix](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_CATALOG.md#relationship-matrix).
|
|
512
527
|
|
|
513
528
|
### ComplianceResult
|
|
514
529
|
|
|
@@ -620,9 +635,38 @@ report:
|
|
|
620
635
|
rulesets:
|
|
621
636
|
rules_dir: null # defaults to the packaged cloudg/rules/
|
|
622
637
|
|
|
638
|
+
ratelimit: # cloud API throttling resilience (0.6.0)
|
|
639
|
+
enabled: true
|
|
640
|
+
aws: # azure and gcp take the same keys
|
|
641
|
+
max_rps: null # null = built-in (AWS 20, Azure 15, GCP 10 per service)
|
|
642
|
+
burst: null
|
|
643
|
+
account_max_rps: null # per account / subscription / project (Azure 20)
|
|
644
|
+
account_burst: null # (Azure 200)
|
|
645
|
+
global_max_rps: null # everything this process sends to the provider
|
|
646
|
+
global_burst: null
|
|
647
|
+
max_concurrency: null # requests in flight per account (AWS 128, Azure 16, GCP 16)
|
|
648
|
+
adaptive: true # halve on throttling, recover additively
|
|
649
|
+
min_rps: null
|
|
650
|
+
breaker_threshold: 5
|
|
651
|
+
breaker_cooldown_seconds: 60
|
|
652
|
+
max_retries: 8 # Azure SDK retries, GCP retries per page (AWS: aws.max_retries)
|
|
653
|
+
max_backoff_seconds: 60
|
|
654
|
+
deadline_seconds: 900
|
|
655
|
+
retry_budget: 500 # retries per provider, all SDKs, refilled by successes
|
|
656
|
+
services: {} # {ec2: {max_rps: 10, burst: 50}, "ec2.DescribeImages": {...}}
|
|
657
|
+
live_cooldown_seconds: null
|
|
658
|
+
live_cooldown_seconds: 120 # guard on live collections triggered through MCP
|
|
659
|
+
live_max_concurrent: 1
|
|
660
|
+
live_max_concurrent_total: 2
|
|
661
|
+
live_caller_max_operations: 0
|
|
662
|
+
live_caller_window_seconds: 3600
|
|
663
|
+
live_operation_timeout_seconds: 3600 # cancel a live collection after this (null: no limit)
|
|
664
|
+
|
|
623
665
|
concurrency_limit: 5
|
|
624
666
|
```
|
|
625
667
|
|
|
668
|
+
`aws.max_retries` and `aws.retry_mode` configure botocore's own retries. They were accepted but ignored before 0.6.0; now they are applied to every AWS client. botocore counts `max_retries` after the first attempt, so the default of 10 allows up to 11 attempts per call. The `ratelimit` section is explained under [Rate limits and throttling](#rate-limits-and-throttling).
|
|
669
|
+
|
|
626
670
|
In code, the same structure is `CloudGConfig`, a Pydantic model:
|
|
627
671
|
|
|
628
672
|
```python
|
|
@@ -699,7 +743,7 @@ print(inventory.summary) # services, types, regions, exposur
|
|
|
699
743
|
inventory.export("./reports") # inventory-map.json / .graphml / graph
|
|
700
744
|
```
|
|
701
745
|
|
|
702
|
-
`InventoryResult` carries `assets`, `edges`, `coverage`, `providers`, `regions`, `organization` (the discovered topology, when mapped) and `
|
|
746
|
+
`InventoryResult` carries `assets`, `edges`, `coverage`, `providers`, `regions`, `organization` (the discovered topology, when mapped), `unresolved_references` and `throttling` (the run's throttling telemetry, or `None` when no API pushed back), plus a computed `summary` (totals, per-service/type/region/account breakdowns, edge types and relationships, cross-account edges, external accounts, security service gaps, internet-exposed and unlinked counts). `export(dir)` writes `inventory-map.json`, `inventory-map.graphml`, `inventory-graph.json`, `inventory-dependencies.json` and, for an organization, `inventory-organization.json`; `InventoryResult.load(dir_or_file)` reads a map back.
|
|
703
747
|
|
|
704
748
|
Interdependency questions go through `DependencyGraph`:
|
|
705
749
|
|
|
@@ -750,7 +794,7 @@ Your own collectors can declare relationships the same way the built-in ones do,
|
|
|
750
794
|
|
|
751
795
|
The deep collectors are public too, when you want single-region, single-account control: `AWSDeepInventoryCollector(session, region="us-east-1", account_id=None, tagging_sweep=True, **options)` (keyword options `is_primary_region`, `services`, `exclude_services`, `kubernetes`, `kubernetes_timeout`, `iam_resource_edges`, `max_images_per_repository`, `stack_resources`, `cloud_control`, `cloud_control_types`, `cloud_control_exclude`, `cloud_control_concurrency`; an unknown option raises `TypeError`), `AzureDeepInventoryCollector(credential, subscription_id, graph_client_factory=None, use_resource_graph=True)` and `GCPDeepInventoryCollector(project_id, credentials=None, **options)` (keyword options `organization_id`, `scope`, `project_filter`, `skip_asset_types`, `page_size`, `include_iam`, `client`, `coverage`, `timeout`, `link_locally`) all implement the standard `collect()` / `collect_edges()` / `run()` collector interface. Their assets carry declared relations; pass them through `RelationshipLinker` for the typed edges.
|
|
752
796
|
|
|
753
|
-
Every structure above, from `summary` to the dependency tree, is specified field by field in the [inventory reference](INVENTORY_REFERENCE.md).
|
|
797
|
+
Every structure above, from `summary` to the dependency tree, is specified field by field in the [inventory reference](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_REFERENCE.md).
|
|
754
798
|
|
|
755
799
|
### The deeper toolkit
|
|
756
800
|
|
|
@@ -758,9 +802,10 @@ Modules the pipeline uses internally that are equally useful standalone:
|
|
|
758
802
|
|
|
759
803
|
| Module | Class | What it does from code |
|
|
760
804
|
|---|---|---|
|
|
761
|
-
| `cloudg.graph.builder` | `GraphBuilder` | `build(assets, edges)` → NetworkX DiGraph; `find_attack_paths(src, dst)`, `find_lateral_movement_paths()`, `compute_centrality()` for blast-radius scoring; `to_d3_json()`, `to_cytoscape_json()`, `save_graphml()` / `load_graphml()` |
|
|
762
|
-
| `cloudg.graph.reachability` | `ReachabilityAnalyzer` | BFS from the internet
|
|
763
|
-
| `cloudg.graph.
|
|
805
|
+
| `cloudg.graph.builder` | `GraphBuilder` | `build(assets, edges)` → NetworkX DiGraph, with parallel security group, NACL and internet-exposure rules between the same two nodes merged into one edge (`port_range` and `protocol` become comma lists); `find_attack_paths(src, dst)`, `find_lateral_movement_paths()`, `compute_centrality()` for blast-radius scoring; `to_d3_json()`, `to_cytoscape_json()`, `save_graphml()` / `load_graphml()` |
|
|
806
|
+
| `cloudg.graph.reachability` | `ReachabilityAnalyzer`, `network_flow_graph`, `finding_id`, `asset_key` | BFS from the internet over network-flow edges only. Entry points are `0.0.0.0/0`, `::/0` and the Azure `Internet`, `Any` and `*` sources. The walk follows `INTERNET_EXPOSED`, ingress `SECURITY_GROUP_RULE` and `NACL_RULE` edges, `ROUTE`, `PEERING` and `LOAD_BALANCER_TARGET` source to target; `ATTACHED_TO` backwards into a security group, NSG or NACL and forwards from a network interface or Elastic IP; `CONTAINS` only from a VPC, VNet or subnet. It never follows IAM edges, and not `INVOKES` either, so a function behind a public API gateway is not flagged. Security groups, NSGs, NACLs and target groups are hops: they are marked exposed but get no exposure finding of their own. `generate_findings()` returns exposure and sensitive-port findings with deterministic ids; `flow_hops()`, `flow_successors()`, `internet_entry_points()` and `network_flow_graph()` expose the same walk; `compute_blast_radius(node)` follows every edge type |
|
|
807
|
+
| `cloudg.graph.ports` | `parse_port_ranges`, `edge_port_ranges`, `port_in_ranges`, `is_internet_source` | the port and edge rules the builder, the reachability analysis and the ontology share: `port_range` strings parsed into numeric ranges, filter rule and egress checks, internet sources |
|
|
808
|
+
| `cloudg.graph.ontology` | `CloudOntology` | `build(assets, edges, findings)` infers 64 typed RDF relations; `save(path, fmt)` writes Turtle/JSON-LD/XML; query the graph with SPARQL via rdflib |
|
|
764
809
|
| `cloudg.graph.rag_export` | `RAGExporter` | `export_all(...)` chunks the infrastructure three ways (entity, community, relation group) into JSONL for retrieval pipelines |
|
|
765
810
|
| `cloudg.renderers.terraform_export` | `TerraformExporter` | `export(assets, edges)` recreates live infrastructure as `.tf.json` plus an `import.sh`; `preview(assets)` reports mappable coverage first |
|
|
766
811
|
| `cloudg.normaliser` | `FindingsNormaliser` | the full dedupe / cross-scanner merge / CVSS rescore / compliance mapping pass, on any `list[Finding]` |
|
|
@@ -773,6 +818,65 @@ Modules the pipeline uses internally that are equally useful standalone:
|
|
|
773
818
|
| `cloudg.inventory.catalogs` | `load_catalog`, `asset_type_map` | the YAML reference catalogs with `$CLOUDG_CATALOG_DIR` overlays |
|
|
774
819
|
| `cloudg.registry` | `PluginRegistry` | entry-point discovery of collector and scanner plugins |
|
|
775
820
|
|
|
821
|
+
### MCP layer API
|
|
822
|
+
|
|
823
|
+
`cloudg.mcp` exposes the inventory, graph, findings, compliance and ontology to AI agents over the Model Context Protocol; the [MCP server](#mcp-server) chapter shows it in use. Importing it needs nothing beyond cloudg's own dependencies. Its exports:
|
|
824
|
+
|
|
825
|
+
| Name | What it is |
|
|
826
|
+
|---|---|
|
|
827
|
+
| `CloudGMCPLayer` | The layer: registry, workspace, policy and middleware. `call_tool`, `read_resource`, `get_prompt`, `complete`, `list_*` and `*_wire` methods, `register_into(server)`, `serve(transport)` |
|
|
828
|
+
| `default_registry()`, `Registry` | The built-in catalog, and the container for your own tools (`@registry.tool`, `@registry.resource`, `@registry.resource_template`, `@registry.prompt`) |
|
|
829
|
+
| `Workspace`, `Dataset` | Loaded datasets, the active one, the allowed directories |
|
|
830
|
+
| `Policy`, `available_profiles()` | Access rules, rate limits and transform pipelines; the seven built-in profile names |
|
|
831
|
+
| `Principal`, `ToolContext`, `CallInfo` | Who is calling, what a tool receives, what middleware sees |
|
|
832
|
+
| `register_into(layer, server)`, `build_server(layer, flavor)`, `export_definitions(layer)` | Mount into an MCP SDK or fastmcp server, build a standalone server object, or get framework-free wire definitions plus async handlers |
|
|
833
|
+
| `Pipeline`, `TokenVault`, `TransformContext`, `build_transform()` | The transform machinery and the pseudonym vault |
|
|
834
|
+
| `ToolSpec`, `ResourceSpec`, `ResourceTemplateSpec`, `PromptSpec`, `PromptArgument`, `ToolAnnotations`, `Capability`, `Sensitivity`, `Icon` | Primitive specifications |
|
|
835
|
+
| `ToolResult`, `TextContent`, `ImageContent`, `EmbeddedResource`, `ResourceLink`, `TextResourceContents`, `ContentAnnotations`, `PromptMessage`, `PromptResult` | Results and content blocks |
|
|
836
|
+
| `MCPLayerError`, `AccessDeniedError`, `InvalidArgumentsError`, `NotFoundError`, `RateLimitedError` | Errors, each with its JSON-RPC code |
|
|
837
|
+
|
|
838
|
+
`CloudGMCPLayer` also takes all of its keyword arguments as one `LayerOptions` object (`from cloudg.mcp.layer import LayerOptions`; `CloudGMCPLayer(options=LayerOptions(policy="strict", prefix="cloudg_"))`), and an unknown keyword raises `TypeError`. A tool handler declares its arguments as parameters after `ctx`, or, when there are many, as one pydantic model in a parameter named `args` (`def my_tool(ctx, args: MyArgs)`); both forms produce the same flat input schema.
|
|
839
|
+
|
|
840
|
+
The server side lives in `cloudg.mcp.server` (`serve`, `serve_async`, `ServeOptions`, `create_layer_from_options`; `serve_async(layer, transport, options=ServeOptions(...))` takes the serve settings as one object), the CLI in `cloudg.mcp.cli` (`mcp_group`, `main`, `client_config`), the middleware in `cloudg.mcp.middleware` (`AuditLogMiddleware`, `MetricsMiddleware`, `CachingMiddleware`, `ConcurrencyLimitMiddleware`, `RetryMiddleware`). Every signature is in the [MCP guide](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP.md).
|
|
841
|
+
|
|
842
|
+
### Resilience API
|
|
843
|
+
|
|
844
|
+
`cloudg.resilience` is the throttling layer every collector goes through, and you can put your own cloud calls through it too ([Rate limits and throttling](#rate-limits-and-throttling)):
|
|
845
|
+
|
|
846
|
+
| Name | What it does |
|
|
847
|
+
|---|---|
|
|
848
|
+
| `call_with_resilience(fn, *args, scope=..., policy=None, **kwargs)` | Async. Breaker, deadline, shared rate limit, bulkhead, then the call; retries throttling and transient errors with decorrelated jitter, never sooner than Retry-After. Sync functions run in a worker thread |
|
|
849
|
+
| `call_with_resilience_sync(...)` | The blocking variant, for worker threads |
|
|
850
|
+
| `@resilient(scope, policy=None)` | Decorator form; `scope` may be a callable that receives the function's arguments |
|
|
851
|
+
| `Scope(provider, account, region, service, operation)`, `RetryPolicy(...)` | Where a call goes; per-call retry bounds |
|
|
852
|
+
| `classify(exc)`, `ErrorKind`, `retry_after(exc)`, `describe_error(exc)`, `is_throttle(exc)` | Provider-aware error classification (THROTTLED, TRANSIENT, FATAL) and the server's requested delay |
|
|
853
|
+
| `classify_strict(exc)`, `is_provider_answer(exc)` | Classification from error codes, exception classes and HTTP statuses only, never message text (`None` when they say nothing); whether the error is an answer from the provider rather than a local failure |
|
|
854
|
+
| `get_governor()`, `configure(config)`, `reset_governor()` | The process-wide limiter, breakers, budgets and stats |
|
|
855
|
+
| `stats_scope()`, `current_stats()`, `ResilienceStats` | Per-run throttling telemetry; `get_governor().summary(stats)` is what `InventoryResult.throttling` holds |
|
|
856
|
+
| `LiveOperationGuard`, `GuardSettings`, `operation_key()`, `LiveOperationRejected` (`CooldownActive`, `LiveOperationBusy`, `CallerQuotaExceeded`), `LiveOperationTimeout` | Single-flight, concurrency caps, cooldowns, caller quotas and an operation timeout for on-demand collections |
|
|
857
|
+
| `RateLimiter`, `TokenBucket`, `LimitSpec`, `ProviderLimits`, `Bulkhead`, `CircuitBreaker`, `BreakerRegistry`, `DEFAULT_LIMITS` | The building blocks |
|
|
858
|
+
| `CircuitOpenError`, `DeadlineExceededError`, `RetryBudgetExhaustedError` | cloudg's own errors, carrying `retry_after` |
|
|
859
|
+
|
|
860
|
+
```python
|
|
861
|
+
import asyncio
|
|
862
|
+
|
|
863
|
+
import boto3
|
|
864
|
+
from cloudg.resilience import Scope, call_with_resilience, stats_scope
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
async def main():
|
|
868
|
+
ec2 = boto3.client("ec2", region_name="eu-west-1")
|
|
869
|
+
scope = Scope("aws", "123456789012", "eu-west-1", "ec2", "DescribeVpcs")
|
|
870
|
+
with stats_scope() as stats:
|
|
871
|
+
vpcs = await call_with_resilience(ec2.describe_vpcs, scope=scope)
|
|
872
|
+
print(len(vpcs["Vpcs"]), stats.messages())
|
|
873
|
+
|
|
874
|
+
|
|
875
|
+
asyncio.run(main())
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
`cloudg.retry.with_retry` keeps its signature and retries what it always did. It now also retries provider throttling that `classify_strict` recognises from an error code, exception class or HTTP status (Azure 429s, GCP `ResourceExhausted`), and never sleeps less than the server's Retry-After, capped at its `max_delay`. It does not retry cloudg's own resilience errors, a plain HTTP 500, or an error whose message merely mentions rate limiting.
|
|
879
|
+
|
|
776
880
|
### PipelineResult
|
|
777
881
|
|
|
778
882
|
| Field | Type |
|
|
@@ -945,6 +1049,197 @@ if critical:
|
|
|
945
1049
|
sys.exit(1)
|
|
946
1050
|
```
|
|
947
1051
|
|
|
1052
|
+
## MCP server
|
|
1053
|
+
|
|
1054
|
+
cloudg 0.6.0 ships a [Model Context Protocol](https://modelcontextprotocol.io) layer, `cloudg.mcp`. An AI agent (Claude Desktop, Claude Code, Cursor, VS Code, or your own) can load an inventory map or a findings report, search and inspect assets, walk the relationship graph, ask for attack paths and blast radius, review findings and compliance gaps, query the ontology, and, when the policy allows it, start live collections. Every request goes through one policy that decides who may call what and transforms the data on its way out: secrets redacted, attacker-controllable text fenced, identifiers pseudonymised under the stricter profiles.
|
|
1055
|
+
|
|
1056
|
+
The full guide is [MCP.md](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP.md); this chapter is the summary.
|
|
1057
|
+
|
|
1058
|
+
### Installing
|
|
1059
|
+
|
|
1060
|
+
```bash
|
|
1061
|
+
pip install cloudg # the layer, the CLI and the dependency-free native server
|
|
1062
|
+
pip install "cloudg[mcp]" # adds the official mcp SDK: the sdk flavor and register_into(...)
|
|
1063
|
+
pip install fastmcp # only to serve with fastmcp or mount into a fastmcp server
|
|
1064
|
+
```
|
|
1065
|
+
|
|
1066
|
+
The `mcp` extra pins `mcp>=1.30,<3`; this release was verified with mcp 1.30.0 and 2.3.0, and with fastmcp 2.14.7, 3.4.8 and 4.0.11. Mounting into an SDK `MCPServer` (mcp 2.x) or `FastMCP` (mcp 1.x) goes through private SDK attributes that a future release can rename, so applications that use that path should pin the minor version they tested (for example `mcp>=2.3,<2.4`). Mounting into the SDK's low-level `Server` and the native server do not depend on those attributes. `cloudg[all]` includes the `mcp` extra.
|
|
1067
|
+
|
|
1068
|
+
### Serving it
|
|
1069
|
+
|
|
1070
|
+
```bash
|
|
1071
|
+
# stdio, for a desktop client that launches the server itself
|
|
1072
|
+
cloudg mcp serve --dataset prod=./reports/inventory-map.json
|
|
1073
|
+
|
|
1074
|
+
# print the client configuration to paste (claude-desktop, claude-code, cursor, vscode)
|
|
1075
|
+
cloudg mcp config --client claude-desktop --dataset prod=./reports/inventory-map.json
|
|
1076
|
+
|
|
1077
|
+
# streamable HTTP on 127.0.0.1:8765/mcp, with a bearer token mapped to a role
|
|
1078
|
+
cloudg mcp serve --transport http --auth-token env:CLOUDG_MCP_TOKEN:analyst
|
|
1079
|
+
```
|
|
1080
|
+
|
|
1081
|
+
The same commands are available as `cloudg-mcp ...` and `python -m cloudg.mcp ...`. The server speaks both MCP protocol eras: the handshake versions 2024-11-05, 2025-03-26, 2025-06-18 and 2025-11-25, and the stateless 2026-07-28. `--flavor` picks the implementation: `native` (standard library only), `sdk` (the official SDK, needs `cloudg[mcp]`), `fastmcp`, or `auto`, which uses the SDK when it is installed and the native server otherwise. HTTP binds to loopback and checks `Origin` and `Host` by default; when binding anything else, pass `--auth-token`, `--allowed-host` and `--allowed-origin`.
|
|
1082
|
+
|
|
1083
|
+
### Embedding it
|
|
1084
|
+
|
|
1085
|
+
Mount every tool, resource and prompt into an MCP server you already run. Here an mcp 2.x `MCPServer` with one tool of its own; an mcp 1.x `FastMCP`, a low-level `Server` and a `fastmcp.FastMCP` work the same way:
|
|
1086
|
+
|
|
1087
|
+
```python
|
|
1088
|
+
import asyncio
|
|
1089
|
+
|
|
1090
|
+
from mcp import Client
|
|
1091
|
+
from mcp.server.mcpserver import MCPServer
|
|
1092
|
+
|
|
1093
|
+
from cloudg.mcp import CloudGMCPLayer
|
|
1094
|
+
|
|
1095
|
+
server = MCPServer("platform-tools")
|
|
1096
|
+
|
|
1097
|
+
|
|
1098
|
+
@server.tool()
|
|
1099
|
+
def ticket_count(team: str) -> int:
|
|
1100
|
+
"""The host's own tool."""
|
|
1101
|
+
return 7
|
|
1102
|
+
|
|
1103
|
+
|
|
1104
|
+
layer = CloudGMCPLayer(policy="standard")
|
|
1105
|
+
layer.workspace.load("inventory/inventory-map.json", "prod")
|
|
1106
|
+
layer.register_into(server, prefix="cloudg_")
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
async def main() -> None:
|
|
1110
|
+
async with Client(server) as client:
|
|
1111
|
+
tools = (await client.list_tools()).tools
|
|
1112
|
+
print(len(tools), [t.name for t in tools][:3])
|
|
1113
|
+
res = await client.call_tool("cloudg_count_assets", {"group_by": "provider"})
|
|
1114
|
+
print(res.structured_content["groups"])
|
|
1115
|
+
|
|
1116
|
+
|
|
1117
|
+
asyncio.run(main())
|
|
1118
|
+
```
|
|
1119
|
+
|
|
1120
|
+
```text
|
|
1121
|
+
74 ['ticket_count', 'cloudg_workspace_status', 'cloudg_list_datasets']
|
|
1122
|
+
{'AWS': 36, 'AZURE': 5, 'GCP': 3}
|
|
1123
|
+
```
|
|
1124
|
+
|
|
1125
|
+
Mount after the host has registered its own handlers: the host's tools stay, and names cloudg does not own still reach the host. Or skip MCP on the wire and call the layer in-process:
|
|
1126
|
+
|
|
1127
|
+
```python
|
|
1128
|
+
import asyncio
|
|
1129
|
+
from pathlib import Path
|
|
1130
|
+
|
|
1131
|
+
from cloudg.mcp import CloudGMCPLayer, Workspace
|
|
1132
|
+
|
|
1133
|
+
|
|
1134
|
+
async def main() -> None:
|
|
1135
|
+
layer = CloudGMCPLayer(policy="strict", workspace=Workspace(allowed_roots=[Path(".")]))
|
|
1136
|
+
await layer.call_tool("load_dataset", {"path": "inventory/inventory-map.json", "name": "prod"})
|
|
1137
|
+
res = await layer.call_tool("find_assets", {"internet_exposed": True, "limit": 2,
|
|
1138
|
+
"fields": ["name", "type", "arn"]})
|
|
1139
|
+
print(res.is_error, res.structured["total"], res.structured["items"])
|
|
1140
|
+
|
|
1141
|
+
|
|
1142
|
+
asyncio.run(main())
|
|
1143
|
+
```
|
|
1144
|
+
|
|
1145
|
+
```text
|
|
1146
|
+
False 6 [{'id': 'res-04ddd3e8be', 'name': 'res-04ddd3e8be', 'type': 'EC2', 'arn': 'arn:aws:ec2:us-east-1:934619913841:instance/res-c6e5dce4cb'}, ...]
|
|
1147
|
+
```
|
|
1148
|
+
|
|
1149
|
+
Under `strict` the names, account ids and ARNs come back pseudonymised; passing a pseudonym back as an argument works, because the input pipeline reverses it. Set `CLOUDG_MCP_VAULT_KEY` to keep pseudonyms stable across restarts (without it they change with every process, as in this run). A policy with `vault.path` also saves the vault when the server shuts down, so issued pseudonyms survive a restart. Both examples ran against the synthetic estate in `tests/mcp/fixtures/sample_estate.py`.
|
|
1150
|
+
|
|
1151
|
+
### CLI
|
|
1152
|
+
|
|
1153
|
+
| Command | What it does |
|
|
1154
|
+
|---|---|
|
|
1155
|
+
| `cloudg mcp serve` | Run the server: `--transport stdio/http/sse`, `--host`, `--port`, `--path`, `--flavor`, `--auth-token`, `--allowed-origin`, `--allowed-host`, `--cache-ttl`, `--max-concurrency`, `--log-level` |
|
|
1156
|
+
| `cloudg mcp tools` / `resources` / `prompts` | List what a principal sees under a policy (`--role`, `--principal-id`, `--json` for the exact wire definitions) |
|
|
1157
|
+
| `cloudg mcp call TOOL --args JSON` | Call a tool in-process through the full policy; exit status 1 on a tool error |
|
|
1158
|
+
| `cloudg mcp read URI` | Read a resource in-process |
|
|
1159
|
+
| `cloudg mcp config` | Print a client configuration snippet |
|
|
1160
|
+
|
|
1161
|
+
All of them except `config` accept `--policy`, `--dataset [NAME=]PATH` (repeatable), `--prefix`, `--include-category` / `--exclude-category`, `--include-tool` / `--exclude-tool`, `--read-only` (drops tools that touch the cloud, run processes or write files), `--audit-log FILE`, `--timeout` and `--registry MODULE:ATTR`.
|
|
1162
|
+
|
|
1163
|
+
### Catalog
|
|
1164
|
+
|
|
1165
|
+
The built-in catalog has 74 tools, 13 resources, 11 resource templates and 11 prompts. The CLI adds `cloudg://metrics`. Tools by category:
|
|
1166
|
+
|
|
1167
|
+
| Category | Tools | What they do |
|
|
1168
|
+
|---|---|---|
|
|
1169
|
+
| `graph` | 17 | neighbours, paths, exposure, attack and lateral paths, dependencies, blast radius, centrality, sub-graphs |
|
|
1170
|
+
| `inventory` | 11 | search, inspect and aggregate assets, accounts, regions, tags, coverage, organization |
|
|
1171
|
+
| `findings` | 10 | browse, summarise, prioritise, suppress and ingest security findings |
|
|
1172
|
+
| `workspace` | 8 | load, select, snapshot, diff and unload datasets |
|
|
1173
|
+
| `ontology` | 6 | RDF ontology, read-only SPARQL, semantic neighbourhoods, RAG chunks |
|
|
1174
|
+
| `compliance` | 5 | framework posture, controls and gaps |
|
|
1175
|
+
| `live` | 5 | the four live tools (`map_inventory`, `collect_assets`, `run_scanners`, `run_pipeline`) and `rate_limit_status` |
|
|
1176
|
+
| `privacy` | 5 | policy, pseudonym vault and data-handling controls |
|
|
1177
|
+
| `meta` | 4 | server capabilities and cloudg's vocabulary |
|
|
1178
|
+
| `export` | 3 | Terraform recreation, reports, ontology files |
|
|
1179
|
+
|
|
1180
|
+
The 11 prompts are packaged workflows such as `security_posture_review`, `attack_surface_report`, `blast_radius_assessment`, `compliance_gap_analysis` and `incident_triage`. Every primitive is documented in [MCP_TOOLS.md](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP_TOOLS.md).
|
|
1181
|
+
|
|
1182
|
+
The `cloudg://docs` resources let an agent read this reference and the inventory reference. The wheel ships both files inside the package (`cloudg/_docs/`), so they work from a plain `pip install`; a source checkout reads them from `docs/`, and `CLOUDG_DOCS_DIR` points elsewhere. The sdist includes every `docs/*.md`.
|
|
1183
|
+
|
|
1184
|
+
### Policies and privacy
|
|
1185
|
+
|
|
1186
|
+
`--policy` takes a built-in profile, a YAML or JSON file, or inline JSON; the default is `standard`.
|
|
1187
|
+
|
|
1188
|
+
| Profile | In short | Tools listed for the local user |
|
|
1189
|
+
|---|---|---|
|
|
1190
|
+
| `open` | no restrictions, no transforms; for trusted local use | 74 |
|
|
1191
|
+
| `standard` | redacts secrets, drops private keys, masks credential ids, fences attacker-controllable text, refuses secrets in arguments; `reveal_token` only for `admin` and `privacy-admin` | 73 |
|
|
1192
|
+
| `strict` | `standard` plus pseudonymised identifiers, IPs, e-mails and tag values; hides the live, export and RESTRICTED tools (among them `sparql_query`, `subgraph_export` and the text graph and ontology exports); rate limits on every tool | 61 |
|
|
1193
|
+
| `read_only` | `standard` without cloud access, processes or file writes | 66 |
|
|
1194
|
+
| `airgapped` | `standard` without cloud access or processes | 69 |
|
|
1195
|
+
| `audit` | `read_only` with every call logged and risk annotations inline | 66 |
|
|
1196
|
+
| `soc-analyst` | a role-based example: analysts get pseudonymised data, a `lead` role sees it in clear | 62; `analyst` 60, `lead` 70 |
|
|
1197
|
+
|
|
1198
|
+
Policies can extend each other, carry per-role rules, sensitivity ceilings and per-principal rate limits, and compose transforms (`redact`, `sanitize`, `project`, `annotate`, `substitute`, `alias`, `regex_replace`, `rename_keys`, `template`, `guard_secrets`, `depseudonymize`). The pseudonym vault (`TokenVault`) derives format-preserving pseudonyms with HMAC-SHA256. Every profile, key and transform is in [MCP_PRIVACY.md](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP_PRIVACY.md). `reveal_token` (for the roles allowed to reveal) turns a pseudonym, or an alias a `substitute` or `alias` transform introduced, back into the real value.
|
|
1199
|
+
|
|
1200
|
+
### Live tools
|
|
1201
|
+
|
|
1202
|
+
`map_inventory`, `collect_assets`, `run_scanners` and `run_pipeline` call cloud APIs with the server's own credentials, whoever the caller is, so restrict them to trusted roles. The `strict`, `read_only`, `airgapped`, `audit` and `soc-analyst` profiles hide them, and `--read-only` drops them. Before any cloud call, a credential preflight (`preflight=true` by default, at most 25 seconds) checks that the configured AWS, Azure and GCP credentials work, so a missing profile fails in seconds instead of after every collector has retried.
|
|
1203
|
+
|
|
1204
|
+
Then the call goes through the live operation guard. Identical concurrent calls share one run; a scope collected in the last two minutes is refused with `retry_after_seconds` and a pointer to a loaded live dataset that covers the same providers; at most one live operation runs per scope and two overall. A refusal is a tool error with code -31029 and `reason` `cooldown`, `busy` or `quota`. Principals with the `admin` or `operator` role can pass `force=true` to skip the cooldown. An operation still running after `ratelimit.live_operation_timeout_seconds` (default 3600) is cancelled and frees its scope. The answer of every live tool includes a `throttling` block when a cloud API pushed back during the run. `rate_limit_status` and the resource `cloudg://ratelimit` show the cooldowns, the operations in flight and the throttling state without touching any cloud API. The guard's settings are the `ratelimit.live_*` keys described in the next chapter.
|
|
1205
|
+
|
|
1206
|
+
## Rate limits and throttling
|
|
1207
|
+
|
|
1208
|
+
Since 0.6.0 every cloud API call cloudg makes goes through `cloudg.resilience`, one process-wide governor shared by all clients:
|
|
1209
|
+
|
|
1210
|
+
- Token buckets per account x region x service (per API action for EC2; per subscription and resource provider on Azure; per project or organization and RPC on GCP), with defaults at or below each provider's published limits: AWS 20 rps per service and 20 per EC2 action, Azure 20 rps per subscription and 3 per user for Resource Graph, GCP 1.5 rps for Cloud Asset `ListAssets`.
|
|
1211
|
+
- Adaptive rates: a throttled scope's rate is halved for every client using it, paused for the server's Retry-After, and recovered by 5% of the ceiling every 5 seconds without throttling.
|
|
1212
|
+
- Concurrency caps per account (`max_concurrency`: 128 requests in flight on AWS, 16 on Azure and GCP).
|
|
1213
|
+
- Circuit breakers per service, or per API action for EC2: after 5 consecutive calls that still failed after retries, calls are skipped for 60 seconds, then one probe goes through. A throttled `DescribeSubnets` does not block `DescribeVpcs`.
|
|
1214
|
+
- Retries with decorrelated jitter that never undercut Retry-After, bounded by attempts, a per-call deadline and a per-provider retry budget that every SDK draws from. botocore keeps doing the retrying on AWS (`aws.retry_mode`, `aws.max_retries`), azure-core on Azure (an explicit `RetryPolicy` with cloudg's retries, backoff cap and timeout), google-api-core on GCP (a `Retry` capped per page); all of them report back to the governor. The first two retries of every call are free; after that each retry draws on the budget, and when it is spent the call gives up.
|
|
1215
|
+
|
|
1216
|
+
Throttling never fails a map. A service that stayed throttled is recorded in coverage as FAILED or PARTIAL with an error starting `throttled:`, the rest of the map continues, and the run's numbers are attached to the result:
|
|
1217
|
+
|
|
1218
|
+
```python
|
|
1219
|
+
result = InventoryMapper(config).map_inventory_sync()
|
|
1220
|
+
result.throttling # None, or totals / messages / skipped / scopes / breakers / slowed_buckets
|
|
1221
|
+
result.summary["throttling"] # totals, messages, skipped
|
|
1222
|
+
```
|
|
1223
|
+
|
|
1224
|
+
`inventory-map.json` carries the full block under `throttling`, and `cloudg map` logs each message, for example `Throttling: aws/123456789012/us-east-1/ec2: throttled 11x, slowed to 5.0 rps, 10 retries, 1 calls gave up`.
|
|
1225
|
+
|
|
1226
|
+
Everything is configured under `ratelimit` (structure under [Configuration](#configuration)). Unset limits fall back to the built-ins, and service overrides merge into them:
|
|
1227
|
+
|
|
1228
|
+
```yaml
|
|
1229
|
+
ratelimit:
|
|
1230
|
+
aws:
|
|
1231
|
+
account_max_rps: 40 # all services of one account together
|
|
1232
|
+
services:
|
|
1233
|
+
resourcegroupstaggingapi: {max_rps: 1, burst: 2}
|
|
1234
|
+
ec2.DescribeImageAttribute: {max_rps: 5, burst: 10}
|
|
1235
|
+
azure:
|
|
1236
|
+
services:
|
|
1237
|
+
resourcegraph: {max_rps: 1, burst: 5}
|
|
1238
|
+
live_cooldown_seconds: 600 # MCP live collections, per scope
|
|
1239
|
+
```
|
|
1240
|
+
|
|
1241
|
+
A misspelled key anywhere under `ratelimit` is logged as `Unknown config key ratelimit.aws.max_rpss is ignored` and the rest still loads. `ratelimit.enabled: false` turns it all off (botocore's own retries remain), and `ratelimit.<provider>.enabled: false` does it for one provider. The [rate limits and throttling guide](https://github.com/morpheuslord/cloudg/blob/main/docs/RESILIENCE.md) has the architecture, the per-provider wiring, the built-in limits with their sources, every key with its default and where it takes effect, example output of a throttled run, the Python API and tuning advice.
|
|
1242
|
+
|
|
948
1243
|
## Plugin system
|
|
949
1244
|
|
|
950
1245
|
Custom collectors and scanners register through setuptools entry points; no core changes needed. `PluginRegistry` (in `cloudg.registry`) discovers them at startup and falls back to the built-ins for any name not overridden.
|
|
@@ -982,7 +1277,7 @@ myscanner = "my_package.scanner:MyScanner"
|
|
|
982
1277
|
mycloud = "my_package.collector:MyCollector"
|
|
983
1278
|
```
|
|
984
1279
|
|
|
985
|
-
After `pip install`, the name is available in `scanners.enabled` and `--scanners`. Collectors follow the base interface in `cloudg/collectors/base.py`. A collector plugin's assets join the inventory map like the built-in ones: declare what each asset talks to in `metadata["relations"]` and extra identifiers in `metadata["aliases"]`, and the relationship linker resolves them (see [Inventory internals](INVENTORY_INTERNALS.md#156-declare-relations-from-your-own-collector-or-plugin)). If your scanner emits a stable check ID in `source_finding_id`, you can add it to `rules/check_equivalence.yaml` so its findings merge with equivalent checks from other tools.
|
|
1280
|
+
After `pip install`, the name is available in `scanners.enabled` and `--scanners`. Collectors follow the base interface in `cloudg/collectors/base.py`. A collector plugin's assets join the inventory map like the built-in ones: declare what each asset talks to in `metadata["relations"]` and extra identifiers in `metadata["aliases"]`, and the relationship linker resolves them (see [Inventory internals](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_INTERNALS.md#156-declare-relations-from-your-own-collector-or-plugin)). If your scanner emits a stable check ID in `source_finding_id`, you can add it to `cloudg/rules/check_equivalence.yaml` so its findings merge with equivalent checks from other tools.
|
|
986
1281
|
|
|
987
1282
|
## Authentication
|
|
988
1283
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<p align="center">
|
|
6
6
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License: MIT"></a>
|
|
7
7
|
<img src="https://img.shields.io/badge/python-3.11%2B-blue.svg" alt="Python 3.11+">
|
|
8
|
-
<img src="https://img.shields.io/badge/version-0.
|
|
8
|
+
<img src="https://img.shields.io/badge/version-0.6.0-4c1.svg" alt="Version 0.6.0">
|
|
9
9
|
<a href="https://github.com/astral-sh/uv"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json" alt="uv"></a>
|
|
10
10
|
</p>
|
|
11
11
|
|
|
@@ -20,7 +20,9 @@ One command collects assets from every configured provider in parallel, feeds th
|
|
|
20
20
|
|
|
21
21
|
Need the map without the security tooling? `cloudg map` is a scanner-independent inventory mapper: it deep-collects everything deployed (or default) in an account, down to the network fabric, sweeps every service for the rest, and links it all into one asset map you can overlay with scanner findings later. See [Inventory mapping](#inventory-mapping).
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Want an AI agent to work with all of it? `cloudg mcp serve` turns cloudg into an MCP server for Claude Desktop, Claude Code, Cursor or anything else that speaks the Model Context Protocol. See [MCP server](#mcp-server).
|
|
24
|
+
|
|
25
|
+
**Full documentation:** the rendered handbook lives at [morpheuslord.github.io/cloudg](https://morpheuslord.github.io/cloudg/), with the same content as markdown in the [feature reference](https://github.com/morpheuslord/cloudg/blob/main/docs/DOCUMENTATION.md) and release notes in the [changelog](https://github.com/morpheuslord/cloudg/blob/main/CHANGELOG.md). The inventory mapper has its own deep-dive documents: the [inventory reference](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_REFERENCE.md) (every return structure and output file, field by field), the [inventory catalog](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_CATALOG.md) (all 156 asset types, their metadata and relationships) and the [inventory internals](https://github.com/morpheuslord/cloudg/blob/main/docs/INVENTORY_INTERNALS.md) (how it works and how to extend it). The MCP server is covered by the [MCP guide](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP.md), the [tool reference](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP_TOOLS.md) and the [privacy reference](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP_PRIVACY.md), and throttling by [rate limits and throttling](https://github.com/morpheuslord/cloudg/blob/main/docs/RESILIENCE.md).
|
|
24
26
|
|
|
25
27
|
---
|
|
26
28
|
|
|
@@ -47,7 +49,7 @@ cloudg run -p aws --regions us-east-1 --terraform
|
|
|
47
49
|
|
|
48
50
|
Reports land in `./reports`. Open `report.html` first.
|
|
49
51
|
|
|
50
|
-
Already ran the scanners yourself? Feed cloudg their native output files instead
|
|
52
|
+
Already ran the scanners yourself? Feed cloudg their native output files instead. No cloud credentials, no scanner binaries:
|
|
51
53
|
|
|
52
54
|
```bash
|
|
53
55
|
cloudg ingest --prowler ./prowler-output/ --checkov ./results_json.json --trivy ./trivy.json
|
|
@@ -115,6 +117,31 @@ print(inventory.summary["assets_by_service"])
|
|
|
115
117
|
|
|
116
118
|
---
|
|
117
119
|
|
|
120
|
+
## MCP server
|
|
121
|
+
|
|
122
|
+
cloudg can hand its inventory, graph, findings, compliance mappings and ontology to AI agents over the Model Context Protocol: 74 tools, 13 resources, 11 resource templates and 11 prompts, from `find_assets` and `attack_paths` to `blast_radius`, `compliance_gaps` and a `security_posture_review` prompt.
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pip install "cloudg[mcp]" # the native server needs no extra; this adds the official SDK (mcp>=1.30,<3)
|
|
126
|
+
|
|
127
|
+
# stdio for a desktop client, with a map you already made
|
|
128
|
+
cloudg mcp serve --dataset prod=./reports/inventory-map.json
|
|
129
|
+
cloudg mcp config --client claude-desktop --dataset prod=./reports/inventory-map.json
|
|
130
|
+
|
|
131
|
+
# streamable HTTP on 127.0.0.1:8765/mcp with a bearer token
|
|
132
|
+
cloudg mcp serve --transport http --auth-token env:CLOUDG_MCP_TOKEN:analyst
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Already running an MCP server? Mount cloudg into it (official `mcp` SDK 1.x or 2.x, or fastmcp) with `CloudGMCPLayer(...).register_into(server)`, or skip the wire and `await layer.call_tool(...)` from Python. Every request, whichever way it arrives, goes through one policy. The default `standard` profile redacts secrets and fences attacker-controllable text; `strict` also pseudonymises account ids, ARNs, IPs and names (pseudonyms passed back as arguments are resolved) and hides the tools that touch the cloud. The live tools (`map_inventory`, `collect_assets`, `run_scanners`, `run_pipeline`) check credentials before the first API call and refuse to re-collect a scope that was collected a couple of minutes ago.
|
|
136
|
+
|
|
137
|
+
The [MCP guide](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP.md) covers serving, client setup, transports, mounting and in-process use; the [tool reference](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP_TOOLS.md) lists every primitive, and the [privacy reference](https://github.com/morpheuslord/cloudg/blob/main/docs/MCP_PRIVACY.md) every profile and transform.
|
|
138
|
+
|
|
139
|
+
### Rate limits
|
|
140
|
+
|
|
141
|
+
Mapping a whole AWS Organization means thousands of API calls per account, so every call cloudg makes goes through a shared rate limiter with per-service limits at or below what each provider publishes. A throttled API gets slowed down for every client at once, Retry-After is honoured, requests in flight are capped per account, retries beyond the first two of each call draw from one budget per provider, and an API that stays throttled is skipped for a while by a circuit breaker. The map does not fail over it: coverage says `throttled: ...` and the result carries a `throttling` summary. Limits are tunable under `ratelimit` in `config.yaml`; see [rate limits and throttling](https://github.com/morpheuslord/cloudg/blob/main/docs/RESILIENCE.md).
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
118
145
|
## Authentication
|
|
119
146
|
|
|
120
147
|
Every provider supports several auth methods, resolved in a fixed priority order. The same config works on a laptop, in CI, and on cloud compute. The full set of fields lives in `config.yaml` with comments for each method.
|
|
@@ -172,11 +199,11 @@ graph LR
|
|
|
172
199
|
E --> F
|
|
173
200
|
```
|
|
174
201
|
|
|
175
|
-
Collection runs all providers concurrently with asyncio, iterating accounts and regions per provider (regions are auto-discovered when you pass `--regions all`). Assets and network edges go into a directed graph, where BFS from the internet node finds exposed resources and blast radius scoring estimates what an attacker could reach from each node.
|
|
202
|
+
Collection runs all providers concurrently with asyncio, iterating accounts and regions per provider (regions are auto-discovered when you pass `--regions all`). Assets and network edges go into a directed graph, where BFS from the internet node over network-flow edges finds exposed resources and blast radius scoring estimates what an attacker could reach from each node.
|
|
176
203
|
|
|
177
|
-
The same inventory feeds three other exports. The ontology module infers
|
|
204
|
+
The same inventory feeds three other exports. The ontology module infers 64 typed relations (`INTERNET_REACHABLE`, `ROLE_ASSUMES_ROLE`, `ENCRYPTED_BY_KMS`, `VPC_CONTAINS_SUBNET` and so on) and writes RDF you can query with SPARQL. The RAG exporter chunks the graph three ways (per asset, per Louvain community, per relation domain) into JSONL for retrieval pipelines. The Terraform exporter maps 25+ asset types to `.tf.json` resources with an `import.sh` to adopt them into state.
|
|
178
205
|
|
|
179
|
-
Scanner findings are deduplicated in two passes
|
|
206
|
+
Scanner findings are deduplicated in two passes (within a scanner by scanner, check ID and resource; across scanners only when both the normalised title and the underlying check semantics in `cloudg/rules/check_equivalence.yaml` match), then rescored against CVSS and mapped to compliance controls.
|
|
180
207
|
|
|
181
208
|
---
|
|
182
209
|
|
|
@@ -313,11 +340,13 @@ Much more of cloudg is public, importable API than the CLI suggests. The [Python
|
|
|
313
340
|
|---|---|
|
|
314
341
|
| `cloudg.inventory.InventoryMapper` / `RelationshipLinker` | scanner-free inventory maps and metadata-derived relationship edges |
|
|
315
342
|
| `cloudg.graph.builder.GraphBuilder` | NetworkX graph, attack paths, centrality/blast-radius metrics, D3/Cytoscape/GraphML export |
|
|
316
|
-
| `cloudg.graph.ontology.CloudOntology` | RDF ontology (
|
|
343
|
+
| `cloudg.graph.ontology.CloudOntology` | RDF ontology (64 typed relations), SPARQL-queryable, Turtle/JSON-LD |
|
|
317
344
|
| `cloudg.graph.rag_export.RAGExporter` | retrieval-ready JSONL chunks of the infrastructure for LLM pipelines |
|
|
318
345
|
| `cloudg.renderers.terraform_export.TerraformExporter` | `.tf.json` recreation of live infrastructure plus `import.sh` |
|
|
319
346
|
| `cloudg.ingest.parse_report` and the scanner classes | every scanner's parser, usable standalone |
|
|
320
347
|
| `cloudg.normaliser.FindingsNormaliser` | cross-scanner dedupe, CVSS rescoring, compliance mapping |
|
|
348
|
+
| `cloudg.mcp.CloudGMCPLayer` | the MCP layer, in-process or mounted into your own MCP server |
|
|
349
|
+
| `cloudg.resilience` | shared rate limits, circuit breakers and retries (`call_with_resilience`, `@resilient`) for your own cloud calls |
|
|
321
350
|
|
|
322
351
|
Custom collectors and scanners register through entry points, no core changes needed:
|
|
323
352
|
|
|
@@ -332,7 +361,7 @@ mycloud = "my_package.collector:MyCollector"
|
|
|
332
361
|
|
|
333
362
|
```bash
|
|
334
363
|
uv pip install -e ".[all,dev]"
|
|
335
|
-
pytest #
|
|
364
|
+
pytest # about 1,650 tests, moto-mocked AWS included
|
|
336
365
|
ruff check cloudg/ tests/
|
|
337
366
|
uv build # wheel + sdist for PyPI
|
|
338
367
|
```
|