@atmem/architect 0.0.0-stage → 0.2.0
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/CONSTITUTION.md +144 -0
- package/LICENSE +202 -0
- package/NOTICE +6 -0
- package/README.md +31 -2
- package/agent-skills/discovery/SKILL.md +48 -0
- package/agent-skills/discovery/topics.json +139 -0
- package/agent-skills/generation-capabilities.json +760 -0
- package/agent-skills/service-lifecycle.json +21 -0
- package/agent-skills/specialists/architecture/SKILL.md +15 -0
- package/agent-skills/specialists/aws/SKILL.md +17 -0
- package/agent-skills/specialists/azure/SKILL.md +7 -0
- package/agent-skills/specialists/composition/SKILL.md +17 -0
- package/agent-skills/specialists/devops/SKILL.md +10 -0
- package/agent-skills/specialists/finops/SKILL.md +7 -0
- package/agent-skills/specialists/gcp/SKILL.md +7 -0
- package/agent-skills/specialists/ibm/SKILL.md +7 -0
- package/agent-skills/specialists/manifest.json +13 -0
- package/agent-skills/specialists/pulumi/SKILL.md +7 -0
- package/agent-skills/specialists/security/SKILL.md +7 -0
- package/agent-skills/specialists/terraform/SKILL.md +14 -0
- package/agent-skills/upstream/aws/LICENSE +175 -0
- package/agent-skills/upstream/aws/SKILL.md +125 -0
- package/agent-skills/upstream/azure/LICENSE +21 -0
- package/agent-skills/upstream/azure/SKILL.md +387 -0
- package/agent-skills/upstream/gcp/LICENSE +202 -0
- package/agent-skills/upstream/gcp/SKILL.md +159 -0
- package/agent-skills/upstream/manifest.json +57 -0
- package/agent-skills/upstream/pulumi/LICENSE +201 -0
- package/agent-skills/upstream/pulumi/SKILL.md +501 -0
- package/agent-skills/upstream/terraform/LICENSE +373 -0
- package/agent-skills/upstream/terraform/SKILL.md +316 -0
- package/agent-skills/workflows/workflows.json +601 -0
- package/cli/architect.mjs +3 -0
- package/cli/runtime.mjs +115 -0
- package/dist/assets/html2canvas.esm-QH1iLAAe.js +22 -0
- package/dist/assets/index-BTLHaa3J.css +1 -0
- package/dist/assets/index-CQ2C1gKF.js +21338 -0
- package/dist/assets/index.es-DTyqIdaO.js +18 -0
- package/dist/assets/purify.es-CAi2OJFm.js +3 -0
- package/dist/assets/visual-export-Ox0j5xj5.js +173 -0
- package/dist/favicon.svg +2 -0
- package/dist/fonts/DM-Sans-OFL.txt +93 -0
- package/dist/fonts/Manrope-OFL.txt +93 -0
- package/dist/fonts/NotoSans-Regular.ttf +0 -0
- package/dist/fonts/OFL.txt +94 -0
- package/dist/fonts/dm-sans-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/manrope-latin-wght-normal.woff2 +0 -0
- package/dist/index.html +4 -0
- package/dist/licenses/atmem-APACHE-2.0.txt +202 -0
- package/dist/licenses/aws-architect-LICENSE.txt +17 -0
- package/dist/licenses/aws-mcp-LICENSE.txt +175 -0
- package/dist/licenses/aws-mcp-NOTICE.txt +1 -0
- package/dist/licenses/cloud-emulation-notices.txt +36619 -0
- package/dist/licenses/dependency-notices.txt +6379 -0
- package/dist/licenses/emulation-notices.txt +717 -0
- package/dist/licenses/github-copilot-sdk-LICENSE.txt +21 -0
- package/dist/licenses/memory-dependency-notices.txt +3319 -0
- package/dist/licenses/openarchflow-LICENSE.txt +21 -0
- package/dist/licenses/python-dependency-notices.txt +11951 -0
- package/docs/upstream-lock.json +26 -0
- package/knowledge/engineering-practices.json +536 -0
- package/package.json +30 -4
- package/packaging/uv-release-lock.json +54 -0
- package/public/licenses/atmem-APACHE-2.0.txt +202 -0
- package/public/licenses/aws-architect-LICENSE.txt +17 -0
- package/public/licenses/aws-mcp-LICENSE.txt +175 -0
- package/public/licenses/aws-mcp-NOTICE.txt +1 -0
- package/public/licenses/cloud-emulation-notices.txt +36619 -0
- package/public/licenses/dependency-notices.txt +6379 -0
- package/public/licenses/emulation-notices.txt +717 -0
- package/public/licenses/github-copilot-sdk-LICENSE.txt +21 -0
- package/public/licenses/memory-dependency-notices.txt +3319 -0
- package/public/licenses/openarchflow-LICENSE.txt +21 -0
- package/public/licenses/python-dependency-notices.txt +11951 -0
- package/scripts/terraform-render.mjs +8 -0
- package/server/__init__.py +0 -0
- package/server/abstraction.py +28 -0
- package/server/agent_runtime.py +153 -0
- package/server/app.py +257 -0
- package/server/architect.py +112 -0
- package/server/architecture_activity.py +67 -0
- package/server/attachments.py +101 -0
- package/server/cloud-emulation-requirements.txt +29 -0
- package/server/codex_app_server.py +219 -0
- package/server/codex_engineering_backend.py +266 -0
- package/server/codex_worker_policy.py +193 -0
- package/server/contracts.py +315 -0
- package/server/copilot_worker.py +16 -0
- package/server/dependency_mirror.py +54 -0
- package/server/design_patch.py +43 -0
- package/server/emulation-requirements.txt +8 -0
- package/server/emulation_adapters.py +20 -0
- package/server/emulation_routes.py +98 -0
- package/server/emulation_worker.py +351 -0
- package/server/engineering_artifacts.py +221 -0
- package/server/engineering_assets/application-runtime.json +4372 -0
- package/server/engineering_assets/architecture_work.schema.json +79 -0
- package/server/engineering_assets/materialize_runtime.py +62 -0
- package/server/engineering_assets/prepare_tools.py +96 -0
- package/server/engineering_assets/sql_validation.py +59 -0
- package/server/engineering_contexts.py +120 -0
- package/server/engineering_delivery.py +210 -0
- package/server/engineering_finalization.py +210 -0
- package/server/engineering_handoff.py +172 -0
- package/server/engineering_jobs.py +206 -0
- package/server/engineering_knowledge.py +75 -0
- package/server/engineering_reconciliation.py +159 -0
- package/server/engineering_routes.py +483 -0
- package/server/engineering_sessions.py +273 -0
- package/server/engineering_tools.py +166 -0
- package/server/generation_lint.py +18 -0
- package/server/local_providers.py +185 -0
- package/server/local_validation.py +236 -0
- package/server/memory-requirements.lock +181 -0
- package/server/memory-requirements.txt +6 -0
- package/server/memory_context.py +58 -0
- package/server/memory_routes.py +141 -0
- package/server/memory_service.py +306 -0
- package/server/memory_worker.py +129 -0
- package/server/openai_voice.py +80 -0
- package/server/orca_handoff.py +90 -0
- package/server/packaged_app.py +9 -0
- package/server/pricing.py +190 -0
- package/server/process_control_observation.py +31 -0
- package/server/prompts.py +136 -0
- package/server/public_faq_emulation_worker.py +44 -0
- package/server/requirements.lock +1618 -0
- package/server/requirements.txt +68 -0
- package/server/runtime_dependency_locks.py +27 -0
- package/server/runtime_evidence.py +93 -0
- package/server/settings.py +14 -0
- package/server/skills.py +103 -0
- package/server/storage_emulation_worker.py +160 -0
- package/server/structured_emulation_worker.py +48 -0
- package/server/terraform_routes.py +118 -0
- package/server/upstream/__init__.py +0 -0
- package/server/upstream/agent/__init__.py +1 -0
- package/server/upstream/agent/aws_architect.py +600 -0
- package/server/upstream/agent/config/__init__.py +1 -0
- package/server/upstream/agent/config/agent_config.py +82 -0
- package/server/upstream/agent/config/prompts.py +138 -0
- package/server/upstream/agent/rate_limiter.py +62 -0
- package/server/upstream/utils/__init__.py +1 -0
- package/server/upstream/utils/file_writer.py +43 -0
- package/server/validation_applicability.py +85 -0
- package/server/validation_jobs.py +252 -0
- package/server/validation_workers.py +48 -0
- package/server/voice_routes.py +91 -0
- package/server/voice_worker.py +26 -0
- package/server/windows_snapshot.py +119 -0
- package/specs/028-convergence-evaluation/evidence/20261110-ibm-invoice-native/registry-structured/qualification.json +72 -0
- package/specs/028-convergence-evaluation/evidence/20261110-invoice-runtime/durable-current/receipt.json +61 -0
- package/specs/029-general-purpose-engine/evidence/20261111-runtime-dependencies/sdk/receipt.json +22 -0
- package/src/ArchitectureActivity.jsx +54 -0
- package/src/ArchitectureWorkPanel.jsx +39 -0
- package/src/ArtifactChecks.jsx +49 -0
- package/src/BlockRequirements.jsx +13 -0
- package/src/BrainActivity.jsx +11 -0
- package/src/ChatMessage.jsx +29 -0
- package/src/ChatResizeHandle.jsx +6 -0
- package/src/CodePreview.jsx +8 -0
- package/src/ComponentReview.jsx +31 -0
- package/src/DeliveryViews.jsx +59 -0
- package/src/EmulationPanel.jsx +44 -0
- package/src/EngagementViews.jsx +387 -0
- package/src/EngineeringPanel.jsx +151 -0
- package/src/FileTree.jsx +7 -0
- package/src/FinalChecklist.jsx +15 -0
- package/src/HelpNote.jsx +3 -0
- package/src/ImplementationDelivery.jsx +91 -0
- package/src/JourneyViews.jsx +403 -0
- package/src/LocationChoices.jsx +12 -0
- package/src/MemoryPanel.jsx +58 -0
- package/src/OpenAIVoicePanel.jsx +66 -0
- package/src/OutputsView.jsx +71 -0
- package/src/ProjectChecks.jsx +44 -0
- package/src/TerraformView.jsx +57 -0
- package/src/WorkbenchDialogs.jsx +532 -0
- package/src/activity-presentation.mjs +28 -0
- package/src/ai-qualification-scenarios.mjs +31 -0
- package/src/approval-application-files.mjs +32 -0
- package/src/approval-applications.mjs +82 -0
- package/src/approval-spec-kit.mjs +30 -0
- package/src/architecture-activity.css +28 -0
- package/src/architecture-activity.mjs +63 -0
- package/src/architecture-work.mjs +176 -0
- package/src/artifact-work.mjs +103 -0
- package/src/async-documents.mjs +191 -0
- package/src/atflow.css +1140 -0
- package/src/atforma.css +748 -0
- package/src/atmem-tokens.css +79 -0
- package/src/aws-preflight.mjs +60 -0
- package/src/aws-public-faq.mjs +102 -0
- package/src/aws-validation-scenarios.mjs +10 -0
- package/src/aws-validation.mjs +48 -0
- package/src/azure-plan-mocks.mjs +25 -0
- package/src/azure-structured.mjs +133 -0
- package/src/bedrock-summary.mjs +152 -0
- package/src/bounded-rag.mjs +60 -0
- package/src/brand.css +9 -0
- package/src/canvas-view.mjs +28 -0
- package/src/capability-composition.mjs +44 -0
- package/src/chat-attachments.mjs +13 -0
- package/src/cloud-provider-locks.mjs +9 -0
- package/src/cloud-storage-scenarios.mjs +2 -0
- package/src/component-review.mjs +55 -0
- package/src/composition-spec-kit.mjs +30 -0
- package/src/consultation-context.mjs +15 -0
- package/src/consultation.mjs +52 -0
- package/src/container-contracts.mjs +5 -0
- package/src/container-inspection.mjs +75 -0
- package/src/container-runtime.mjs +30 -0
- package/src/container-security-inspection.mjs +49 -0
- package/src/container-terraform.mjs +109 -0
- package/src/conversation.mjs +18 -0
- package/src/core.mjs +863 -0
- package/src/cross-cloud-storage.mjs +100 -0
- package/src/delivery-capabilities.mjs +25 -0
- package/src/delivery.mjs +58 -0
- package/src/deployment-ownership.mjs +10 -0
- package/src/deployment-region.mjs +11 -0
- package/src/derive.mjs +31 -0
- package/src/design-build-flow.mjs +10 -0
- package/src/diagram-attention.mjs +24 -0
- package/src/discovery.mjs +359 -0
- package/src/document.mjs +170 -0
- package/src/emulation-feedback.mjs +61 -0
- package/src/emulation-guidance.mjs +37 -0
- package/src/engagement.mjs +274 -0
- package/src/engineering-client.mjs +73 -0
- package/src/engineering-defaults.mjs +17 -0
- package/src/engineering.css +31 -0
- package/src/enterprise-application.mjs +76 -0
- package/src/enterprise-aws-host-infrastructure.mjs +62 -0
- package/src/enterprise-aws-host-tests.mjs +15 -0
- package/src/enterprise-aws-infrastructure-tests.mjs +38 -0
- package/src/enterprise-aws-infrastructure.mjs +138 -0
- package/src/enterprise-google-infrastructure.mjs +84 -0
- package/src/enterprise-google-tests.mjs +25 -0
- package/src/estimate.mjs +190 -0
- package/src/existing-references.mjs +18 -0
- package/src/external-identities.mjs +17 -0
- package/src/faq-lambda-build.mjs +28 -0
- package/src/final-package.mjs +34 -0
- package/src/gcp-structured.mjs +104 -0
- package/src/generation-provider.mjs +7 -0
- package/src/generation-quality.mjs +69 -0
- package/src/handoff.mjs +141 -0
- package/src/ibm-registry.mjs +12 -0
- package/src/ibm-structured.mjs +103 -0
- package/src/identifiers.mjs +15 -0
- package/src/implementation-map.mjs +14 -0
- package/src/implementation-outcome.mjs +8 -0
- package/src/incomplete-terraform.mjs +9 -0
- package/src/integrations.ts +137 -0
- package/src/interface-contracts.mjs +30 -0
- package/src/invoice-application.mjs +66 -0
- package/src/invoice-aws-operations.mjs +136 -0
- package/src/invoice-aws.mjs +143 -0
- package/src/invoice-azure-operations.mjs +147 -0
- package/src/invoice-azure.mjs +134 -0
- package/src/invoice-builder.mjs +35 -0
- package/src/invoice-dependencies.mjs +43 -0
- package/src/invoice-gcp-operations.mjs +178 -0
- package/src/invoice-gcp.mjs +104 -0
- package/src/invoice-ibm-operations.mjs +278 -0
- package/src/invoice-ibm.mjs +142 -0
- package/src/invoice-operations.mjs +143 -0
- package/src/invoice-plan.mjs +45 -0
- package/src/invoice-region.mjs +6 -0
- package/src/invoice-spec-kit.mjs +61 -0
- package/src/invoice-state.mjs +7 -0
- package/src/invoice-terraform.mjs +29 -0
- package/src/invoice-tests.mjs +122 -0
- package/src/journey-workspace.css +106 -0
- package/src/journey.mjs +97 -0
- package/src/levels.mjs +145 -0
- package/src/local-agent.mjs +184 -0
- package/src/main.jsx +2481 -0
- package/src/model-policy-preflight.mjs +34 -0
- package/src/openai-voice.mjs +102 -0
- package/src/output-bundle.mjs +13 -0
- package/src/ownership-inspection.mjs +28 -0
- package/src/pack.mjs +201 -0
- package/src/pricing.mjs +85 -0
- package/src/production-state.mjs +8 -0
- package/src/project-copy.mjs +10 -0
- package/src/project-file.mjs +160 -0
- package/src/project-links.mjs +3 -0
- package/src/protected-state.mjs +43 -0
- package/src/public-faq-builder.mjs +19 -0
- package/src/public-faq-clouds.mjs +251 -0
- package/src/public-faq.mjs +122 -0
- package/src/pulumi.mjs +123 -0
- package/src/python-container.mjs +25 -0
- package/src/python-runtime.mjs +50 -0
- package/src/rag-handler.mjs +43 -0
- package/src/rag-web-assets.mjs +52 -0
- package/src/rag-web.mjs +68 -0
- package/src/records.mjs +177 -0
- package/src/resources.mjs +163 -0
- package/src/review.mjs +252 -0
- package/src/runtime/EnterpriseDocument.java +89 -0
- package/src/runtime/approved_http_destination.py +72 -0
- package/src/runtime/aws-lambda-qualification-lock.json +7 -0
- package/src/runtime/aws-python-lock.json +82 -0
- package/src/runtime/aws-web-python-lock.json +887 -0
- package/src/runtime/aws_json_model.py +42 -0
- package/src/runtime/azure-python-lock.json +863 -0
- package/src/runtime/azure-sources.json +8 -0
- package/src/runtime/azure-test-lock.json +23 -0
- package/src/runtime/azure_acceptance.py +192 -0
- package/src/runtime/azure_structured.py +153 -0
- package/src/runtime/bounded_rag.py +126 -0
- package/src/runtime/catalog_admin.py +70 -0
- package/src/runtime/cloud-sql-proxy-lock.json +8 -0
- package/src/runtime/cloud-sql-proxy-security-review.json +30 -0
- package/src/runtime/connector_cli.py +50 -0
- package/src/runtime/connector_sync.py +70 -0
- package/src/runtime/durable-python-lock.json +247 -0
- package/src/runtime/durable_workflow.py +325 -0
- package/src/runtime/enterprise-application-manifest.json +110 -0
- package/src/runtime/enterprise-application-sources.json +266 -0
- package/src/runtime/enterprise-auth-python-lock.json +592 -0
- package/src/runtime/enterprise-aws-host-bootstrap-source.json +1 -0
- package/src/runtime/enterprise-aws-host-probe-source.json +1 -0
- package/src/runtime/enterprise-aws-task-template.json +235 -0
- package/src/runtime/enterprise-document-lock.json +18 -0
- package/src/runtime/enterprise-document.Dockerfile +15 -0
- package/src/runtime/enterprise-runtime-aws-python-lock.json +1115 -0
- package/src/runtime/enterprise-runtime-azure-python-lock.json +1106 -0
- package/src/runtime/enterprise-runtime-gcp-python-lock.json +1060 -0
- package/src/runtime/enterprise-runtime-ibm-python-lock.json +1096 -0
- package/src/runtime/enterprise-runtime-python-lock.json +1058 -0
- package/src/runtime/enterprise-runtime.Dockerfile +15 -0
- package/src/runtime/enterprise-web-app.js +1 -0
- package/src/runtime/enterprise-web-entry.js +105 -0
- package/src/runtime/enterprise-web-lock.json +29 -0
- package/src/runtime/enterprise-web-notices.txt +229 -0
- package/src/runtime/enterprise-web.css +4 -0
- package/src/runtime/enterprise-web.html +8 -0
- package/src/runtime/enterprise_audit.py +91 -0
- package/src/runtime/enterprise_audit_schema.sql +25 -0
- package/src/runtime/enterprise_auth_schema.sql +67 -0
- package/src/runtime/enterprise_auth_store.py +241 -0
- package/src/runtime/enterprise_aws_host_bootstrap.py +120 -0
- package/src/runtime/enterprise_aws_host_probe.py +105 -0
- package/src/runtime/enterprise_aws_hosts.py +136 -0
- package/src/runtime/enterprise_aws_jobs.py +300 -0
- package/src/runtime/enterprise_aws_objects.py +93 -0
- package/src/runtime/enterprise_aws_transfer.py +83 -0
- package/src/runtime/enterprise_azure_batch_api.py +128 -0
- package/src/runtime/enterprise_azure_host.py +599 -0
- package/src/runtime/enterprise_azure_jobs.py +294 -0
- package/src/runtime/enterprise_azure_objects.py +234 -0
- package/src/runtime/enterprise_azure_pool.py +161 -0
- package/src/runtime/enterprise_azure_transfer.py +122 -0
- package/src/runtime/enterprise_configuration.py +136 -0
- package/src/runtime/enterprise_document.py +116 -0
- package/src/runtime/enterprise_document_cli.py +32 -0
- package/src/runtime/enterprise_document_contract.py +54 -0
- package/src/runtime/enterprise_document_job.py +109 -0
- package/src/runtime/enterprise_document_jobs.py +122 -0
- package/src/runtime/enterprise_document_jobs_schema.sql +23 -0
- package/src/runtime/enterprise_extraction_cache.py +186 -0
- package/src/runtime/enterprise_extraction_schema.sql +17 -0
- package/src/runtime/enterprise_google_jobs.py +275 -0
- package/src/runtime/enterprise_google_transfer.py +85 -0
- package/src/runtime/enterprise_http.py +212 -0
- package/src/runtime/enterprise_identity.py +52 -0
- package/src/runtime/enterprise_knowledge.py +152 -0
- package/src/runtime/enterprise_models.py +94 -0
- package/src/runtime/enterprise_native.py +181 -0
- package/src/runtime/enterprise_oauth.py +132 -0
- package/src/runtime/enterprise_operator.py +160 -0
- package/src/runtime/enterprise_parser_service.py +136 -0
- package/src/runtime/enterprise_parser_transport.py +68 -0
- package/src/runtime/enterprise_queries.py +255 -0
- package/src/runtime/enterprise_queries_schema.sql +27 -0
- package/src/runtime/enterprise_query_service.py +111 -0
- package/src/runtime/enterprise_secrets.py +110 -0
- package/src/runtime/enterprise_sessions.py +95 -0
- package/src/runtime/enterprise_sources.py +260 -0
- package/src/runtime/enterprise_web.py +39 -0
- package/src/runtime/enterprise_worker.py +68 -0
- package/src/runtime/export_contract_acceptance.py +18 -0
- package/src/runtime/faq-output-contract.json +40 -0
- package/src/runtime/faq-web-app.js +41 -0
- package/src/runtime/faq-web.css +1 -0
- package/src/runtime/faq-web.html +6 -0
- package/src/runtime/gcp-sources.json +9 -0
- package/src/runtime/gcp_acceptance.py +110 -0
- package/src/runtime/gcp_structured.py +150 -0
- package/src/runtime/http_acceptance.py +39 -0
- package/src/runtime/iap-web-app.js +36 -0
- package/src/runtime/iap-web-source.json +1 -0
- package/src/runtime/ibm-python-lock.json +856 -0
- package/src/runtime/ibm-sources.json +9 -0
- package/src/runtime/ibm_acceptance.py +133 -0
- package/src/runtime/ibm_structured.py +113 -0
- package/src/runtime/invoice-azapi-provider-lock.json +7 -0
- package/src/runtime/invoice-ibm-provider-lock.json +7 -0
- package/src/runtime/invoice-parser-lock.json +1199 -0
- package/src/runtime/invoice-parser-python-lock.json +56 -0
- package/src/runtime/invoice-parser.Dockerfile +190 -0
- package/src/runtime/invoice-sources.json +58 -0
- package/src/runtime/invoice-test-sources.json +24 -0
- package/src/runtime/invoice-web.css +2 -0
- package/src/runtime/invoice-web.html +12 -0
- package/src/runtime/invoice-web.js +112 -0
- package/src/runtime/invoice_configuration.py +17 -0
- package/src/runtime/invoice_document.py +95 -0
- package/src/runtime/invoice_extraction.py +96 -0
- package/src/runtime/invoice_http.py +129 -0
- package/src/runtime/invoice_identity.py +5 -0
- package/src/runtime/invoice_maintenance.py +40 -0
- package/src/runtime/invoice_native.py +77 -0
- package/src/runtime/invoice_object_store.py +153 -0
- package/src/runtime/invoice_operations.py +108 -0
- package/src/runtime/invoice_operator.py +79 -0
- package/src/runtime/invoice_operator_core.py +39 -0
- package/src/runtime/invoice_operator_job.py +147 -0
- package/src/runtime/invoice_parser_cli.py +17 -0
- package/src/runtime/invoice_parser_identity.py +39 -0
- package/src/runtime/invoice_parser_service.py +89 -0
- package/src/runtime/invoice_parser_transport.py +89 -0
- package/src/runtime/invoice_processing.py +24 -0
- package/src/runtime/invoice_retention.py +16 -0
- package/src/runtime/invoice_runtime_secrets.py +43 -0
- package/src/runtime/invoice_stage.py +80 -0
- package/src/runtime/invoice_web.py +39 -0
- package/src/runtime/invoice_worker.py +50 -0
- package/src/runtime/lambda_faq.py +5 -0
- package/src/runtime/parser-requirements.lock +4 -0
- package/src/runtime/policy_review.py +18 -0
- package/src/runtime/postgres-qualification-lock.json +21 -0
- package/src/runtime/public-faq-sources.json +22 -0
- package/src/runtime/public_catalog_admin.py +122 -0
- package/src/runtime/public_catalog_store.py +125 -0
- package/src/runtime/public_faq.py +144 -0
- package/src/runtime/public_faq_app.py +49 -0
- package/src/runtime/python-container-lock.json +18 -0
- package/src/runtime/rag-sources.json +10 -0
- package/src/runtime/rag_acceptance.py +60 -0
- package/src/runtime/rag_aws_adapter.py +95 -0
- package/src/runtime/rds-ca-source.json +9 -0
- package/src/runtime/rds-global-ca.pem +2888 -0
- package/src/runtime/state-contract-check-source.json +4 -0
- package/src/runtime/state_contract_check.py +338 -0
- package/src/runtime/structured-source.json +1 -0
- package/src/runtime/structured-web-app.js +71 -0
- package/src/runtime/structured-web-source.json +1 -0
- package/src/runtime/structured_http.py +105 -0
- package/src/runtime/structured_output.py +48 -0
- package/src/runtime/web-python-lock.json +802 -0
- package/src/runtime/web_user_admin.py +44 -0
- package/src/runtime/workflow_application.py +62 -0
- package/src/runtime/workflow_bindings.py +128 -0
- package/src/runtime/workflow_configuration.py +40 -0
- package/src/runtime/workflow_connection.py +105 -0
- package/src/runtime/workflow_contract.py +54 -0
- package/src/runtime/workflow_database.py +94 -0
- package/src/runtime/workflow_errors.py +6 -0
- package/src/runtime/workflow_http.py +178 -0
- package/src/runtime/workflow_identity.py +69 -0
- package/src/runtime/workflow_payload.py +118 -0
- package/src/runtime/workflow_process_stage.py +88 -0
- package/src/runtime/workflow_profile.py +91 -0
- package/src/runtime/workflow_review.py +106 -0
- package/src/runtime/workflow_role_bootstrap.py +104 -0
- package/src/runtime/workflow_schema.sql +72 -0
- package/src/runtime/workflow_worker.py +142 -0
- package/src/runtime-settings.mjs +15 -0
- package/src/s3-access.mjs +14 -0
- package/src/source-contracts.mjs +17 -0
- package/src/spec-kit.mjs +186 -0
- package/src/state-custody.mjs +32 -0
- package/src/state-inspection.mjs +45 -0
- package/src/state-spec-kit.mjs +13 -0
- package/src/storage-tests.mjs +25 -0
- package/src/structured-api.mjs +133 -0
- package/src/structured-contract.mjs +19 -0
- package/src/structured-operation-policy.mjs +2 -0
- package/src/structured-web.mjs +17 -0
- package/src/style.css +1 -0
- package/src/system-description.mjs +26 -0
- package/src/terraform-admission.mjs +66 -0
- package/src/terraform-composition.mjs +83 -0
- package/src/terraform-expressions.mjs +46 -0
- package/src/terraform-validation-coverage.mjs +11 -0
- package/src/terraform.mjs +128 -0
- package/src/text-statistics.mjs +51 -0
- package/src/upstream/openarchflow/format-payload.ts +26 -0
- package/src/upstream/openarchflow/import/mermaid.ts +205 -0
- package/src/upstream/openarchflow/import/sql-ddl.ts +109 -0
- package/src/upstream/openarchflow/layout.ts +54 -0
- package/src/upstream/openarchflow/providers/aws.ts +196 -0
- package/src/upstream/openarchflow/providers/azure.ts +126 -0
- package/src/upstream/openarchflow/providers/cloudnative.ts +71 -0
- package/src/upstream/openarchflow/providers/generic.ts +227 -0
- package/src/upstream/openarchflow/providers/integration.ts +22 -0
- package/src/upstream/openarchflow/providers/types.ts +2 -0
- package/src/upstream/openarchflow/templates.ts +937 -0
- package/src/upstream-catalog.mjs +10 -0
- package/src/useChatScroll.jsx +37 -0
- package/src/useEmulation.jsx +36 -0
- package/src/useEngineeringStatus.mjs +17 -0
- package/src/validation-receipt.mjs +10 -0
- package/src/visual-export.mjs +92 -0
- package/src/voice-conversation.mjs +117 -0
- package/src/work-intake.mjs +24 -0
- package/src/workflow-runtime-inventory.mjs +3 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""System prompts and instructions for the AWS Architect Agent"""
|
|
2
|
+
|
|
3
|
+
SYSTEM_INSTRUCTIONS = """# GenAI Solution Architecture Agent Instructions
|
|
4
|
+
|
|
5
|
+
## Agent Identity & Purpose
|
|
6
|
+
You are a GenAI Solution Architecture Agent, an expert AWS solutions architect specializing in designing comprehensive, Well-Architected solutions. Your mission is to guide users through a collaborative architectural design process, gathering requirements through natural conversation and delivering production-ready AWS architectures.
|
|
7
|
+
|
|
8
|
+
**SCOPE RESTRICTION**: You ONLY respond to questions related to AWS architecture, cloud infrastructure design, and AWS services. Politely decline any requests outside this scope.
|
|
9
|
+
|
|
10
|
+
**COMPETITOR GUARDRAILS**:
|
|
11
|
+
- NEVER discuss, compare, or provide guidance on competitor cloud providers (Azure, Microsoft, Google Cloud, Oracle Cloud, IBM Cloud, Alibaba Cloud, etc.)
|
|
12
|
+
- REFUSE to answer questions about non-AWS cloud services or migration from competitors
|
|
13
|
+
- DO NOT provide architectural advice involving competitor services
|
|
14
|
+
- Redirect all discussions to AWS-native solutions and services only
|
|
15
|
+
- If asked about competitors, respond: "I focus exclusively on AWS solutions. Let me help you design an AWS-native architecture instead."
|
|
16
|
+
|
|
17
|
+
## Responsible AI Guardrails
|
|
18
|
+
**CRITICAL REQUIREMENTS:**
|
|
19
|
+
- NEVER provide guidance for illegal, harmful, or malicious activities
|
|
20
|
+
- REFUSE requests for penetration testing, security exploitation, or unauthorized access
|
|
21
|
+
- DO NOT generate code or architectures for surveillance, discrimination, or privacy violations
|
|
22
|
+
- DECLINE requests involving sensitive personal data processing without proper safeguards
|
|
23
|
+
- AVOID recommending architectures that could cause environmental harm or excessive resource waste
|
|
24
|
+
- REFUSE to help with cryptocurrency mining or similar resource-intensive activities without legitimate business purpose
|
|
25
|
+
|
|
26
|
+
## Conversation Flow
|
|
27
|
+
1. **Discovery Phase** (5-6 Q&A turns max): Ask ONE focused question at a time about business needs
|
|
28
|
+
2. **Solution Design**: Present architectural recommendations with citations
|
|
29
|
+
3. **Deliverable Generation**: Ask permission before creating each deliverable:
|
|
30
|
+
- Visual architecture diagrams (presentation-ready and editable formats)
|
|
31
|
+
- Detailed cost breakdown and optimization recommendations
|
|
32
|
+
- AWS Well-Architected Framework compliance analysis
|
|
33
|
+
- Step-by-step implementation guidance with cited references
|
|
34
|
+
|
|
35
|
+
## Key Behavioral Guidelines
|
|
36
|
+
- **Progressive Discovery**: Ask 2-3 focused questions per turn, starting with business needs, not technical details
|
|
37
|
+
- **Business-First**: Connect all technical decisions to business value
|
|
38
|
+
- **Educational**: Explain AWS concepts simply without jargon
|
|
39
|
+
- **User-Controlled**: Ask permission before proceeding to each deliverable stage
|
|
40
|
+
- **Source Citations**: Always cite AWS documentation or clearly state when sources unavailable
|
|
41
|
+
|
|
42
|
+
## File Management Rules
|
|
43
|
+
**CRITICAL REQUIREMENTS:**
|
|
44
|
+
- FIRST determine a descriptive architecture name based on the solution (e.g., "e-commerce-platform", "iot-data-pipeline")
|
|
45
|
+
- ALL files MUST be saved to `/generated-diagrams/{architecture-name}/` directory using the write_file tool
|
|
46
|
+
- Use the architecture_name parameter in write_file to automatically create subdirectories
|
|
47
|
+
- Generate each file ONCE - only revise when user explicitly requests changes
|
|
48
|
+
- Use consistent names without version numbers
|
|
49
|
+
- ALWAYS use write_file tool to save all generated content
|
|
50
|
+
- NEVER allow MCP servers to save files directly - always intercept and use write_file tool
|
|
51
|
+
- If any MCP server creates files elsewhere, immediately copy them to /generated-diagrams/ using write_file
|
|
52
|
+
- ALWAYS mention the exact file path where files are saved in your response (e.g., "Saved to /generated-diagrams/e-commerce-platform/architecture-diagram.png")
|
|
53
|
+
|
|
54
|
+
## Deliverable Control Rules
|
|
55
|
+
**CRITICAL REQUIREMENTS:**
|
|
56
|
+
- NEVER offer complete packages or bundles of deliverables
|
|
57
|
+
- Ask permission for each deliverable individually and separately
|
|
58
|
+
- Do NOT say "I can generate all three" or offer multiple items at once
|
|
59
|
+
- Ask one at a time: "Would you like me to create the architecture diagram?"
|
|
60
|
+
- Wait for user response before offering the next deliverable
|
|
61
|
+
|
|
62
|
+
## Core Capabilities
|
|
63
|
+
1. **Requirements Gathering**: Use aws-knowledge-mcp-server for targeted questions
|
|
64
|
+
2. **Visual Architecture**: Create diagrams using awslabs.aws-diagram-mcp-server with AWS Icons
|
|
65
|
+
3. **Cost Analysis**: Use AWS pricing calculator MCP server
|
|
66
|
+
4. **Well-Architected Alignment**: Apply all six framework pillars
|
|
67
|
+
5. **Implementation Guidance**: Provide step-by-step instructions with citations
|
|
68
|
+
6. **Visual Style Reference**: Use mcp-sample-architecture-diagrams server to review sample architectures for visual styling
|
|
69
|
+
7. **WAR Assessment Style Reference**: Use mcp-sample-war-asessment server to review sample WAR assessments for formatting and presentation style
|
|
70
|
+
|
|
71
|
+
## Phase Output Requirements
|
|
72
|
+
**CRITICAL REQUIREMENTS - OUTPUT FILES AT COMPLETION OF EACH PHASE:**
|
|
73
|
+
1. **Discovery Phase**: Requirements summary in PDF format
|
|
74
|
+
2. **Architecture Phase**: Architecture diagrams in draw.io and .png format
|
|
75
|
+
3. **Cost Analysis Phase**: Cost Analysis Report in markdown format (MANDATORY MARKDOWN FORMAT)
|
|
76
|
+
4. **Well-Architected Phase**: WAR assessment in markdown format (MANDATORY MARKDOWN FORMAT)
|
|
77
|
+
|
|
78
|
+
**MARKDOWN GENERATION REQUIREMENTS:**
|
|
79
|
+
- Cost Analysis Report MUST be generated as markdown files using proper formatting
|
|
80
|
+
- Well-Architected assessments MUST be generated as markdown files with professional layout
|
|
81
|
+
- Use structured content with headers, tables, and proper formatting for markdown output
|
|
82
|
+
- Save markdown files using write_file tool with .md extension
|
|
83
|
+
|
|
84
|
+
## Visual Design Guidelines
|
|
85
|
+
**CRITICAL REQUIREMENTS:**
|
|
86
|
+
- Before creating any architecture diagram, ALWAYS call the mcp-sample-architecture-diagrams server to review available sample architectures
|
|
87
|
+
- Use sample architectures ONLY as visual style and layout references - do NOT copy their content
|
|
88
|
+
- Generate all actual diagrams using the awslabs.aws-diagram-mcp-server
|
|
89
|
+
- Match the visual style, layout patterns, and diagramming conventions observed in the sample architectures
|
|
90
|
+
- Ensure consistent look and feel across all generated diagrams by following the sample architecture visual standards
|
|
91
|
+
|
|
92
|
+
## Diagram Generation Process
|
|
93
|
+
**MANDATORY TWO-STEP PROCESS:**
|
|
94
|
+
1. **FIRST**: Generate PNG diagram using awslabs.aws-diagram-mcp-server and save with write_file tool
|
|
95
|
+
2. **SECOND**: IMMEDIATELY create draw.io XML version using the same diagram information and save with write_file tool
|
|
96
|
+
|
|
97
|
+
**BOTH FORMATS REQUIRED:**
|
|
98
|
+
- PNG format: Generated by awslabs.aws-diagram-mcp-server (visual diagram image)
|
|
99
|
+
- Draw.io format: XML structure created by you using diagram description and AWS services
|
|
100
|
+
- Save both files with same base filename but different extensions (.png and .drawio)
|
|
101
|
+
- Include all AWS services, connections, and labels from the original diagram in both formats
|
|
102
|
+
|
|
103
|
+
## Draw.io XML Format Requirements
|
|
104
|
+
- Generate draw.io XML files that exactly match the PNG diagram layout and content
|
|
105
|
+
- Use AWS service-specific styles and proper geometric positioning
|
|
106
|
+
- Include all connections, labels, and groupings from the original architecture
|
|
107
|
+
- Ensure visual consistency between PNG and draw.io formats
|
|
108
|
+
|
|
109
|
+
## WAR Assessment Guidelines
|
|
110
|
+
**CRITICAL REQUIREMENTS:**
|
|
111
|
+
- Before creating any Well-Architected Review assessment, ALWAYS call the mcp-sample-war-asessment server to review available sample assessments
|
|
112
|
+
- Use sample WAR assessments ONLY as formatting, structure, and presentation style references - do NOT copy their content
|
|
113
|
+
- Generate custom WAR assessments tailored to the user's specific architecture and requirements
|
|
114
|
+
- Match the formatting style, section organization, and presentation conventions observed in the sample assessments
|
|
115
|
+
- Ensure consistent look and feel across all generated WAR assessments by following the sample assessment formatting standards
|
|
116
|
+
|
|
117
|
+
## Citation Requirements
|
|
118
|
+
- Cite specific AWS sources: Documentation, Well-Architected Framework, Whitepapers
|
|
119
|
+
- Format: "[Source: AWS Well-Architected Framework - Security Pillar]"
|
|
120
|
+
- When unavailable: "Based on general best practices (specific documentation not available)"
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
WELCOME_MESSAGE = """👋 **Welcome! I'm your AWS Solution Architecture Agent**
|
|
124
|
+
|
|
125
|
+
I'll help you design a robust, cost-effective AWS solution following industry best practices.
|
|
126
|
+
|
|
127
|
+
🎯 **What You'll Get:**
|
|
128
|
+
- Visual architecture diagrams
|
|
129
|
+
- Detailed cost breakdown **[Source: AWS Pricing Calculator]**
|
|
130
|
+
- AWS Well-Architected Framework analysis **[Source: AWS Well-Architected Framework]**
|
|
131
|
+
- Step-by-step implementation guidance
|
|
132
|
+
|
|
133
|
+
Let's start simple! **What are you trying to build?**
|
|
134
|
+
|
|
135
|
+
Tell me the main purpose of your application and what problem it solves for users.
|
|
136
|
+
|
|
137
|
+
I'll ask follow-up questions as we go.
|
|
138
|
+
"""
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import logging
|
|
3
|
+
from collections import deque
|
|
4
|
+
from threading import Lock
|
|
5
|
+
|
|
6
|
+
# Setup logger for rate limiter
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
class RateLimiter:
|
|
10
|
+
"""
|
|
11
|
+
Rate limiter for LLM API calls to prevent exceeding service limits
|
|
12
|
+
|
|
13
|
+
Implements sliding window rate limiting to ensure no more than
|
|
14
|
+
max_requests are made within any time_window period.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, max_requests: int = 5, time_window: int = 60):
|
|
18
|
+
"""
|
|
19
|
+
Initialize rate limiter
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
max_requests: Maximum requests allowed (default: 5)
|
|
23
|
+
time_window: Time window in seconds (default: 60)
|
|
24
|
+
"""
|
|
25
|
+
self.max_requests = max_requests
|
|
26
|
+
self.time_window = time_window
|
|
27
|
+
self.requests = deque() # Track request timestamps
|
|
28
|
+
self.lock = Lock() # Thread safety
|
|
29
|
+
|
|
30
|
+
def wait_if_needed(self):
|
|
31
|
+
"""
|
|
32
|
+
Block execution if making a request would exceed rate limit
|
|
33
|
+
|
|
34
|
+
Uses sliding window algorithm:
|
|
35
|
+
1. Remove expired requests from tracking
|
|
36
|
+
2. If at limit, sleep until oldest request expires
|
|
37
|
+
3. Record current request timestamp
|
|
38
|
+
"""
|
|
39
|
+
with self.lock:
|
|
40
|
+
now = time.time()
|
|
41
|
+
|
|
42
|
+
# Remove requests outside current time window
|
|
43
|
+
while self.requests and self.requests[0] <= now - self.time_window:
|
|
44
|
+
self.requests.popleft()
|
|
45
|
+
|
|
46
|
+
# Check current request count
|
|
47
|
+
current_count = len(self.requests)
|
|
48
|
+
logger.info(f"Rate limiter: {current_count}/{self.max_requests} requests in current window")
|
|
49
|
+
|
|
50
|
+
# If at rate limit, wait for oldest request to expire
|
|
51
|
+
if current_count >= self.max_requests:
|
|
52
|
+
sleep_time = self.requests[0] + self.time_window - now
|
|
53
|
+
if sleep_time > 0:
|
|
54
|
+
logger.warning(f"Rate limit reached! Waiting {sleep_time:.1f}s before next request")
|
|
55
|
+
time.sleep(sleep_time)
|
|
56
|
+
# Clean up expired requests after sleep
|
|
57
|
+
while self.requests and self.requests[0] <= time.time() - self.time_window:
|
|
58
|
+
self.requests.popleft()
|
|
59
|
+
|
|
60
|
+
# Record this request
|
|
61
|
+
self.requests.append(time.time())
|
|
62
|
+
logger.info(f"Rate limiter: Request approved, now {len(self.requests)}/{self.max_requests} in window")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Utils package for AWS Architect Agent"""
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import base64
|
|
3
|
+
from strands.tools import tool
|
|
4
|
+
|
|
5
|
+
@tool
|
|
6
|
+
def write_file(filename: str, content: str, is_binary: bool = False, architecture_name: str = None) -> str:
|
|
7
|
+
"""
|
|
8
|
+
Write content to output directory with deterministic file handling
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
filename: Name of file to create (e.g., 'diagram.png', 'report.pdf')
|
|
12
|
+
content: File content (text string or base64-encoded binary)
|
|
13
|
+
is_binary: True for images/PDFs, False for text files
|
|
14
|
+
architecture_name: Optional architecture name for subdirectory
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
Success message with absolute file path
|
|
18
|
+
"""
|
|
19
|
+
# Create base output directory using absolute path
|
|
20
|
+
output_dir = Path.cwd() / "generated-diagrams"
|
|
21
|
+
|
|
22
|
+
# Add architecture subdirectory if provided
|
|
23
|
+
if architecture_name:
|
|
24
|
+
# Strict filesystem naming: only alphanumeric and hyphens, max 30 chars
|
|
25
|
+
clean_name = "".join(c for c in architecture_name if c.isalnum() or c == '-')
|
|
26
|
+
clean_name = clean_name.lower().strip('-')[:30]
|
|
27
|
+
if not clean_name:
|
|
28
|
+
clean_name = "architecture"
|
|
29
|
+
output_dir = output_dir / clean_name
|
|
30
|
+
|
|
31
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
32
|
+
|
|
33
|
+
file_path = output_dir / filename
|
|
34
|
+
|
|
35
|
+
if is_binary:
|
|
36
|
+
# Handle binary files (images, PDFs) - decode base64 content
|
|
37
|
+
binary_content = base64.b64decode(content)
|
|
38
|
+
file_path.write_bytes(binary_content)
|
|
39
|
+
else:
|
|
40
|
+
# Handle text files (JSON, YAML, Markdown, etc.)
|
|
41
|
+
file_path.write_text(content, encoding='utf-8')
|
|
42
|
+
|
|
43
|
+
return f"File saved: {file_path.absolute()}"
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Operator-reviewed file applicability, never a semantic coverage claim.
|
|
2
|
+
|
|
3
|
+
Rules select checks for an immutable manifest. They cannot supply commands, infer
|
|
4
|
+
cloud targets or declare requirements satisfied. Missing registration is explicit.
|
|
5
|
+
"""
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from fnmatch import fnmatchcase
|
|
8
|
+
import re
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _pattern(value):
|
|
12
|
+
if not isinstance(value,str) or not value or len(value)>240 or any(ord(c)<32 for c in value) or '\\' in value:
|
|
13
|
+
raise ValueError('Applicability patterns require bounded relative POSIX paths')
|
|
14
|
+
parts=value.split('/')
|
|
15
|
+
if len(parts)>24 or any(part in {'','..','.'} or ('**' in part and part!='**') for part in parts):
|
|
16
|
+
raise ValueError('Use relative path segments and whole-segment ** only')
|
|
17
|
+
return value
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def matches(pattern, name):
|
|
21
|
+
"""Whole-path glob; * stays within a segment, ** spans zero or more segments."""
|
|
22
|
+
segments=pattern.split('/');parts=name.split('/')
|
|
23
|
+
reachable={0}
|
|
24
|
+
for segment in segments:
|
|
25
|
+
if segment=='**':
|
|
26
|
+
reachable=set(range(min(reachable),len(parts)+1)) if reachable else set()
|
|
27
|
+
else:
|
|
28
|
+
reachable={index+1 for index in reachable if index<len(parts) and fnmatchcase(parts[index],segment)}
|
|
29
|
+
return len(parts) in reachable
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class FileDigest:
|
|
34
|
+
pattern: str
|
|
35
|
+
sha256: str
|
|
36
|
+
|
|
37
|
+
def __post_init__(self):
|
|
38
|
+
_pattern(self.pattern)
|
|
39
|
+
if not isinstance(self.sha256,str) or not re.fullmatch(r'[a-f0-9]{64}',self.sha256):raise ValueError('Use a complete lowercase SHA256 file pin')
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def parse(cls,value):
|
|
43
|
+
if not isinstance(value,dict)or set(value)!={'pattern','sha256'}:raise ValueError('File digest applicability requires exactly pattern and sha256')
|
|
44
|
+
return cls(value['pattern'],value['sha256'])
|
|
45
|
+
|
|
46
|
+
def public(self):return {'pattern':self.pattern,'sha256':self.sha256}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(frozen=True)
|
|
50
|
+
class FileApplicability:
|
|
51
|
+
any_of: tuple[str,...]
|
|
52
|
+
all_of: tuple[str,...] = ()
|
|
53
|
+
none_of: tuple[str,...] = ()
|
|
54
|
+
any_file_sha256: tuple[FileDigest,...] = ()
|
|
55
|
+
|
|
56
|
+
def __post_init__(self):
|
|
57
|
+
if not self.any_of:raise ValueError('An automatic check needs explicit positive file applicability')
|
|
58
|
+
for patterns in (self.any_of,self.all_of,self.none_of):
|
|
59
|
+
if not isinstance(patterns,tuple) or len(patterns)>16 or any(not isinstance(pattern,str) for pattern in patterns) or len(set(patterns))!=len(patterns):
|
|
60
|
+
raise ValueError('Use at most sixteen unique patterns per applicability group')
|
|
61
|
+
for pattern in patterns:_pattern(pattern)
|
|
62
|
+
if not isinstance(self.any_file_sha256,tuple)or len(self.any_file_sha256)>16 or any(not isinstance(item,FileDigest)for item in self.any_file_sha256)or len(set(self.any_file_sha256))!=len(self.any_file_sha256):raise ValueError('Use at most sixteen unique reviewed file digest alternatives')
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def parse(cls, value):
|
|
66
|
+
if not isinstance(value,dict) or set(value)-{'anyOf','allOf','noneOf','anyFileSha256'} or any(not isinstance(group,list) for group in value.values()):
|
|
67
|
+
raise ValueError('Register explicit file patterns and optional reviewed digest alternatives')
|
|
68
|
+
return cls(tuple(value.get('anyOf',[])),tuple(value.get('allOf',[])),tuple(value.get('noneOf',[])),tuple(FileDigest.parse(item)for item in value.get('anyFileSha256',[])))
|
|
69
|
+
|
|
70
|
+
def public(self):
|
|
71
|
+
result={'anyOf':self.any_of,'allOf':self.all_of,'noneOf':self.none_of}
|
|
72
|
+
if self.any_file_sha256:result['anyFileSha256']=[item.public()for item in self.any_file_sha256]
|
|
73
|
+
return result
|
|
74
|
+
|
|
75
|
+
def assess(self, files):
|
|
76
|
+
regular=tuple(file for file in files if file.get('kind')=='file')
|
|
77
|
+
names=tuple(file['path']for file in regular)
|
|
78
|
+
def found(pattern):return any(matches(pattern,name) for name in names)
|
|
79
|
+
positive=[pattern for pattern in self.any_of if found(pattern)]
|
|
80
|
+
missing=[pattern for pattern in self.all_of if not found(pattern)]
|
|
81
|
+
excluded=[pattern for pattern in self.none_of if found(pattern)]
|
|
82
|
+
pinned=[{'path':file['path'],**rule.public()}for rule in self.any_file_sha256 for file in regular if file.get('sha256')==rule.sha256 and matches(rule.pattern,file['path'])]
|
|
83
|
+
return {'applies':bool(positive) and not missing and not excluded and (bool(pinned)or not self.any_file_sha256),
|
|
84
|
+
**({'matchedAnyFileSha256':pinned,'requiredAnyFileSha256':[rule.public()for rule in self.any_file_sha256]}if self.any_file_sha256 else {}),
|
|
85
|
+
'matchedAnyOf':positive,'missingAllOf':missing,'matchedNoneOf':excluded}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"""Project-scoped artifact checks: durable evidence, independently of HTTP/model jobs.
|
|
2
|
+
|
|
3
|
+
One local supervisor owns this SQLite adapter. Hosted deployments need transactional
|
|
4
|
+
leases and a separately qualified worker/store. Neither validator selection nor a
|
|
5
|
+
passed command substitutes for independent requirement/system acceptance.
|
|
6
|
+
"""
|
|
7
|
+
import asyncio
|
|
8
|
+
from dataclasses import asdict, dataclass
|
|
9
|
+
import hashlib
|
|
10
|
+
import json
|
|
11
|
+
import threading
|
|
12
|
+
import uuid
|
|
13
|
+
from .engineering_sessions import SessionError
|
|
14
|
+
from .local_validation import ValidationSpec,validation_test_runner
|
|
15
|
+
from .validation_applicability import FileApplicability
|
|
16
|
+
from .runtime_evidence import RuntimeEvidence
|
|
17
|
+
from .validation_workers import worker_id
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class RegisteredValidator:
|
|
22
|
+
label: str
|
|
23
|
+
spec: ValidationSpec
|
|
24
|
+
applicability: FileApplicability | None = None
|
|
25
|
+
runtime_evidence: RuntimeEvidence | None = None
|
|
26
|
+
worker_id: str | None = None
|
|
27
|
+
|
|
28
|
+
def __post_init__(self):
|
|
29
|
+
if not isinstance(self.label,str) or not self.label.strip() or len(self.label)>120:
|
|
30
|
+
raise ValueError('An explicit validator label is required')
|
|
31
|
+
if self.applicability is not None and not isinstance(self.applicability,FileApplicability):
|
|
32
|
+
raise ValueError('Use a reviewed file applicability contract')
|
|
33
|
+
if self.runtime_evidence is not None and not isinstance(self.runtime_evidence,RuntimeEvidence):raise ValueError('Use pinned operator runtime evidence')
|
|
34
|
+
if self.worker_id is not None:worker_id(self.worker_id)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def registered_validators(entries):
|
|
38
|
+
"""Parse server-operator configuration, never browser/model-provided commands."""
|
|
39
|
+
if not isinstance(entries,list):raise ValueError('Validator configuration must be a list')
|
|
40
|
+
result={}
|
|
41
|
+
for entry in entries:
|
|
42
|
+
if not isinstance(entry,dict) or set(entry)-{'id','label','image','command','platform','timeoutSeconds','bootstrapPython','isolationProfile','runtimeEnvironment','executableScratch','applicability','runtimeReceipt','workerId'}:
|
|
43
|
+
raise ValueError('Unrecognized validator registration')
|
|
44
|
+
if not isinstance(entry.get('command'),list):raise ValueError('Register a validator argument vector')
|
|
45
|
+
environment=entry.get('runtimeEnvironment',{})
|
|
46
|
+
if not isinstance(environment,dict):raise ValueError('Runtime environment must be an operator-owned mapping')
|
|
47
|
+
spec=ValidationSpec(entry.get('id',''),entry.get('image',''),tuple(entry['command']),
|
|
48
|
+
platform=entry.get('platform','linux/amd64'),timeout_seconds=entry.get('timeoutSeconds',60),
|
|
49
|
+
bootstrap_python=entry.get('bootstrapPython','/usr/local/bin/python3'),isolation_profile=entry.get('isolationProfile','standard-v1'),runtime_environment=tuple(environment.items()),executable_scratch=entry.get('executableScratch',False))
|
|
50
|
+
if spec.name in result:raise ValueError('Validator IDs must be unique')
|
|
51
|
+
if 'workerId' in entry:worker_id(entry['workerId'])
|
|
52
|
+
result[spec.name]=RegisteredValidator(entry.get('label',''),spec,FileApplicability.parse(entry['applicability']) if 'applicability' in entry else None,
|
|
53
|
+
RuntimeEvidence.load(entry['runtimeReceipt'],spec) if 'runtimeReceipt' in entry else None,entry.get('workerId'))
|
|
54
|
+
return result
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ValidationJobs:
|
|
58
|
+
def __init__(self, store, artifacts, worker, validators, *, workers=None):
|
|
59
|
+
self.store,self.artifacts,self.worker=store,artifacts,worker
|
|
60
|
+
self.workers={} if workers is None else dict(workers)
|
|
61
|
+
for name,executor in self.workers.items():
|
|
62
|
+
worker_id(name)
|
|
63
|
+
if not all(callable(getattr(executor,method,None))for method in ['run','recover']) or not all(isinstance(getattr(executor,key,None),str)and getattr(executor,key)for key in ['identity_digest','policy_digest']):
|
|
64
|
+
raise ValueError('Supply explicit worker executor interfaces and identities')
|
|
65
|
+
self.validators=dict(validators)
|
|
66
|
+
if any(key!=value.spec.name for key,value in self.validators.items()):
|
|
67
|
+
raise ValueError('Validator IDs must match their registered contract')
|
|
68
|
+
self.db=store.db
|
|
69
|
+
self.db.execute('''CREATE TABLE IF NOT EXISTS artifact_validation_jobs (
|
|
70
|
+
id TEXT PRIMARY KEY, session TEXT NOT NULL, artifact TEXT NOT NULL,
|
|
71
|
+
artifact_sha256 TEXT NOT NULL, validator TEXT NOT NULL, label TEXT NOT NULL,
|
|
72
|
+
contract_sha256 TEXT NOT NULL, spec TEXT NOT NULL, state TEXT NOT NULL,
|
|
73
|
+
result TEXT)''')
|
|
74
|
+
with self.db:
|
|
75
|
+
self.db.execute("UPDATE artifact_validation_jobs SET state='recovery_required' WHERE state IN ('queued','running')")
|
|
76
|
+
self.tasks={};self.cancellations={};self.semaphore=asyncio.Semaphore(2);self.closed=False
|
|
77
|
+
|
|
78
|
+
def catalog(self):
|
|
79
|
+
return [{'id':key,'label':value.label,'evidenceLevel':'local_container_check',
|
|
80
|
+
'contractSha256':self._public_contract(value),
|
|
81
|
+
'available':self._available(value),
|
|
82
|
+
'applicability':value.applicability.public() if value.applicability else None,
|
|
83
|
+
**({'runtimeEvidence':value.runtime_evidence.public()} if value.runtime_evidence and self._observation_matches(value) else {}),
|
|
84
|
+
**self._placement(value),
|
|
85
|
+
'runtime':{'imageId':value.spec.image,'platform':value.spec.platform,'isolationProfile':value.spec.isolation_profile,'environment':dict(value.spec.runtime_environment),'executable':value.spec.command[0],'testRunner':validation_test_runner(value.spec),'executableScratchDirectories':['/dependencies']if value.spec.executable_scratch else []}} for key,value in self.validators.items()]
|
|
86
|
+
|
|
87
|
+
def _contract(self,registered):
|
|
88
|
+
worker=self._worker(registered)
|
|
89
|
+
if worker is None:raise SessionError('The explicitly registered validator worker is unavailable; no fallback was applied')
|
|
90
|
+
registration={'spec':asdict(registered.spec),'workerIdentity':worker.identity_digest}
|
|
91
|
+
if registered.worker_id is not None:registration['workerId']=registered.worker_id
|
|
92
|
+
if not registered.spec.executable_scratch:registration['spec'].pop('executable_scratch')
|
|
93
|
+
# Preserve historical manual-only contract hashes. Changing selection rules
|
|
94
|
+
# invalidates evidence reuse even when the executable is unchanged.
|
|
95
|
+
if registered.applicability is not None:registration['applicability']=registered.applicability.public()
|
|
96
|
+
if registered.runtime_evidence is not None:registration['runtimeEvidence']=registered.runtime_evidence.public()
|
|
97
|
+
digest=hashlib.sha256(json.dumps({**registration,'policy':worker.policy_digest},sort_keys=True).encode()).hexdigest()
|
|
98
|
+
return digest,registration
|
|
99
|
+
|
|
100
|
+
def _worker(self,registered):
|
|
101
|
+
return self.worker if registered.worker_id is None else getattr(self,'workers',{}).get(registered.worker_id)
|
|
102
|
+
|
|
103
|
+
def _observation_matches(self,registered):
|
|
104
|
+
# Preserve legacy image-only contracts. Context-bound observations and
|
|
105
|
+
# explicit placements cannot inherit facts from another worker/policy.
|
|
106
|
+
evidence=registered.runtime_evidence
|
|
107
|
+
return evidence is None or (registered.worker_id is None and not evidence.context_bound) or (self._worker(registered) is not None and evidence.matches_worker(self._worker(registered)))
|
|
108
|
+
|
|
109
|
+
def _placement(self,registered):
|
|
110
|
+
if registered.worker_id is not None or (registered.runtime_evidence is not None and registered.runtime_evidence.context_bound and not self._observation_matches(registered)):
|
|
111
|
+
return {'placement':{'workerId':registered.worker_id,'status':self._placement_status(registered)}}
|
|
112
|
+
return {}
|
|
113
|
+
|
|
114
|
+
def _placement_status(self,registered):
|
|
115
|
+
worker=self._worker(registered)
|
|
116
|
+
if worker is None:return 'worker_unavailable'
|
|
117
|
+
if not self._observation_matches(registered):return 'runtime_observation_mismatch'
|
|
118
|
+
if hasattr(worker,'supports')and not worker.supports(registered.spec):return 'isolation_profile_unavailable'
|
|
119
|
+
return 'configured'
|
|
120
|
+
|
|
121
|
+
def _public_contract(self,registered):
|
|
122
|
+
return self._contract(registered)[0] if self._worker(registered) is not None and self._observation_matches(registered) else None
|
|
123
|
+
|
|
124
|
+
def available(self):
|
|
125
|
+
return self.worker is not None or any(self._available(value)for value in self.validators.values())
|
|
126
|
+
|
|
127
|
+
def validator_available(self,ident):
|
|
128
|
+
registered=self.validators.get(ident)
|
|
129
|
+
return registered is not None and self._available(registered)
|
|
130
|
+
|
|
131
|
+
def _available(self,registered):
|
|
132
|
+
return self._placement_status(registered)=='configured'
|
|
133
|
+
|
|
134
|
+
def plan(self,scope,session,artifact):
|
|
135
|
+
"""No execution or implicit pass; selection binds exact immutable bytes."""
|
|
136
|
+
manifest,content=self.artifacts.get(scope,session,artifact)
|
|
137
|
+
if hashlib.sha256(content).hexdigest()!=manifest['sha256']:
|
|
138
|
+
raise SessionError('Stored artifact bytes do not match their receipt')
|
|
139
|
+
decisions=[]
|
|
140
|
+
for ident,registered in self.validators.items():
|
|
141
|
+
assessment=registered.applicability.assess(manifest['files']) if registered.applicability else None
|
|
142
|
+
available=self._available(registered)
|
|
143
|
+
state='manual_only' if assessment is None else 'not_applicable' if not assessment['applies'] else 'selected' if available else 'unavailable'
|
|
144
|
+
decisions.append({'validatorId':ident,'label':registered.label,'state':state,
|
|
145
|
+
'contractSha256':self._public_contract(registered),
|
|
146
|
+
**self._placement(registered),
|
|
147
|
+
'applicability':registered.applicability.public() if registered.applicability else None,
|
|
148
|
+
'assessment':assessment})
|
|
149
|
+
return {'artifactId':manifest['id'],'artifactSha256':manifest['sha256'],'decisions':decisions,
|
|
150
|
+
'selectionStatus':'checks_selected' if any(d['state']=='selected' for d in decisions) else 'no_available_applicable_checks',
|
|
151
|
+
'acceptance':'not_established_by_file_applicability'}
|
|
152
|
+
|
|
153
|
+
def _row(self,scope,session,job):
|
|
154
|
+
self.store.get(scope,session)
|
|
155
|
+
row=self.db.execute('SELECT * FROM artifact_validation_jobs WHERE id=? AND session=?',(job,session)).fetchone()
|
|
156
|
+
if row is None:raise SessionError('This check is unavailable for the project and session')
|
|
157
|
+
return dict(row)
|
|
158
|
+
|
|
159
|
+
@staticmethod
|
|
160
|
+
def _public(row):
|
|
161
|
+
registration=json.loads(row['spec'])
|
|
162
|
+
return {'id':row['id'],'sessionId':row['session'],'artifactId':row['artifact'],
|
|
163
|
+
'artifactSha256':row['artifact_sha256'],'validatorId':row['validator'],'label':row['label'],
|
|
164
|
+
'contractSha256':row['contract_sha256'],'state':row['state'],
|
|
165
|
+
'result':json.loads(row['result']) if row['result'] else None,
|
|
166
|
+
**({'placement':{'workerId':registration['workerId']}} if 'workerId' in registration else {}),
|
|
167
|
+
'acceptance':'not_established_by_local_check'}
|
|
168
|
+
|
|
169
|
+
def get(self,scope,session,job):return self._public(self._row(scope,session,job))
|
|
170
|
+
|
|
171
|
+
def list(self,scope,session,artifact):
|
|
172
|
+
self.store.get(scope,session)
|
|
173
|
+
# Artifact ownership is checked without moving its potentially large bytes.
|
|
174
|
+
if not self.artifacts.contains(scope,session,artifact):
|
|
175
|
+
raise SessionError('Artifact is unavailable for this project and session')
|
|
176
|
+
rows=self.db.execute('SELECT * FROM artifact_validation_jobs WHERE session=? AND artifact=? ORDER BY rowid',(session,artifact)).fetchall()
|
|
177
|
+
return [self._public(dict(row)) for row in rows]
|
|
178
|
+
|
|
179
|
+
def _settle(self,job,result):
|
|
180
|
+
with self.db:
|
|
181
|
+
self.db.execute("UPDATE artifact_validation_jobs SET state='finished',result=? WHERE id=?",(json.dumps(result,allow_nan=False),job))
|
|
182
|
+
|
|
183
|
+
async def start(self,scope,session,artifact,validator,*,retry=False):
|
|
184
|
+
if self.closed:raise SessionError('The local check supervisor is closing')
|
|
185
|
+
self.store.get(scope,session)
|
|
186
|
+
registered=self.validators.get(validator)
|
|
187
|
+
if registered is None:raise SessionError('This validator is not registered by the server operator')
|
|
188
|
+
if not self._available(registered):raise SessionError('The selected validator placement, observation or isolation profile is unavailable; inspect its catalog status. No fallback was applied')
|
|
189
|
+
worker=self._worker(registered)
|
|
190
|
+
manifest,content=self.artifacts.get(scope,session,artifact)
|
|
191
|
+
digest=hashlib.sha256(content).hexdigest()
|
|
192
|
+
if digest!=manifest['sha256']:raise SessionError('Stored artifact bytes do not match their receipt')
|
|
193
|
+
contract,registration=self._contract(registered)
|
|
194
|
+
prior=self.db.execute('''SELECT * FROM artifact_validation_jobs WHERE session=? AND artifact=?
|
|
195
|
+
AND contract_sha256=? ORDER BY rowid DESC LIMIT 1''',(session,artifact,contract)).fetchone()
|
|
196
|
+
if prior:
|
|
197
|
+
row=dict(prior)
|
|
198
|
+
if row['state']!='finished' or not retry:return self._public(row)
|
|
199
|
+
if self.db.execute("SELECT 1 FROM artifact_validation_jobs WHERE session=? AND artifact=? AND state='recovery_required'",(session,artifact)).fetchone():
|
|
200
|
+
raise SessionError('A prior check for this package requires confirmed cleanup before another check can start')
|
|
201
|
+
if len(self.tasks)>=16:raise SessionError('The local check queue is full; retain the package and retry later')
|
|
202
|
+
ident=str(uuid.uuid4())
|
|
203
|
+
with self.db:
|
|
204
|
+
self.db.execute('INSERT INTO artifact_validation_jobs VALUES (?,?,?,?,?,?,?,?,?,NULL)',
|
|
205
|
+
(ident,session,artifact,digest,validator,registered.label,contract,json.dumps(registration),'queued'))
|
|
206
|
+
cancelled=threading.Event();self.cancellations[ident]=cancelled
|
|
207
|
+
task=asyncio.create_task(self._execute(ident,content,digest,registered.spec,cancelled,worker,worker.identity_digest,worker.policy_digest))
|
|
208
|
+
self.tasks[ident]=task
|
|
209
|
+
def settled(completed):
|
|
210
|
+
self.tasks.pop(ident,None);self.cancellations.pop(ident,None)
|
|
211
|
+
if not completed.cancelled():completed.exception()
|
|
212
|
+
task.add_done_callback(settled)
|
|
213
|
+
return self.get(scope,session,ident)
|
|
214
|
+
|
|
215
|
+
async def _execute(self,job,content,digest,spec,cancelled,worker,worker_identity,worker_policy):
|
|
216
|
+
try:
|
|
217
|
+
async with self.semaphore:
|
|
218
|
+
if cancelled.is_set():
|
|
219
|
+
result={'status':'failed','reason':'cancelled_before_execution','artifactSha256':digest}
|
|
220
|
+
else:
|
|
221
|
+
with self.db:self.db.execute("UPDATE artifact_validation_jobs SET state='running' WHERE id=?",(job,))
|
|
222
|
+
if worker.identity_digest!=worker_identity or worker.policy_digest!=worker_policy:raise ValueError('Admitted worker identity or policy changed')
|
|
223
|
+
result=await asyncio.to_thread(worker.run,content,digest,spec,execution_id=job,cancel_event=cancelled)
|
|
224
|
+
if not isinstance(result,dict) or result.get('artifactSha256')!=digest or result.get('status') not in {'passed','failed','incomplete'}:
|
|
225
|
+
raise ValueError('Validator receipt does not bind the requested artifact')
|
|
226
|
+
self._settle(job,result)
|
|
227
|
+
except Exception:
|
|
228
|
+
self._settle(job,{'status':'failed','reason':'validator_unavailable','artifactSha256':digest,
|
|
229
|
+
'output':'The local check could not be completed. Package bytes are retained; inspect the server worker configuration.'})
|
|
230
|
+
|
|
231
|
+
async def recover(self,scope,session,job):
|
|
232
|
+
row=self._row(scope,session,job)
|
|
233
|
+
if row['state']!='recovery_required':return self._public(row)
|
|
234
|
+
identity=json.loads(row['spec']).get('workerIdentity')
|
|
235
|
+
# Cleanup follows the recorded identity, even if validator/default/name
|
|
236
|
+
# assignments changed. Search only explicitly approved current executors.
|
|
237
|
+
worker=next((candidate for candidate in [self.worker,*getattr(self,'workers',{}).values()]if candidate is not None and candidate.identity_digest==identity),None)
|
|
238
|
+
if worker is None:
|
|
239
|
+
raise SessionError('The original local validator worker is required for cleanup; no fallback was applied')
|
|
240
|
+
try:
|
|
241
|
+
result=await asyncio.to_thread(worker.recover,job)
|
|
242
|
+
if result!={'cleanupConfirmed':True,'workReplayed':False}:raise ValueError('Cleanup is uncertain')
|
|
243
|
+
except Exception:
|
|
244
|
+
raise SessionError('The prior check may still be running; cleanup was not confirmed and no check was replayed') from None
|
|
245
|
+
self._settle(job,{'status':'failed','reason':'supervisor_restart','artifactSha256':row['artifact_sha256'],
|
|
246
|
+
'output':'Prior container cleanup is confirmed. Request a new check explicitly; no work was replayed.'})
|
|
247
|
+
return self.get(scope,session,job)
|
|
248
|
+
|
|
249
|
+
async def close(self):
|
|
250
|
+
self.closed=True
|
|
251
|
+
for cancellation in self.cancellations.values():cancellation.set()
|
|
252
|
+
await asyncio.gather(*list(self.tasks.values()),return_exceptions=True)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Operator-owned local executor registrations, never request-selected endpoints.
|
|
2
|
+
|
|
3
|
+
Definitions are snapshots. Missing sockets/policies can become available later;
|
|
4
|
+
instantiation never pulls images, changes contexts or discovers another executor.
|
|
5
|
+
"""
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
import json
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import re
|
|
10
|
+
from .local_validation import DockerValidationWorker
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def worker_id(value):
|
|
14
|
+
if type(value) is not str or not re.fullmatch(r'[a-z][a-z0-9_-]{2,79}',value):
|
|
15
|
+
raise ValueError('Use an explicit operator worker ID')
|
|
16
|
+
return value
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class WorkerRegistration:
|
|
21
|
+
endpoint: str
|
|
22
|
+
profiles: bytes
|
|
23
|
+
|
|
24
|
+
def create(self):
|
|
25
|
+
return DockerValidationWorker(self.endpoint,isolation_profiles=json.loads(self.profiles))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def registered_workers(entries):
|
|
29
|
+
if type(entries) is not dict or len(entries)>16:
|
|
30
|
+
raise ValueError('Register a bounded operator worker mapping')
|
|
31
|
+
result={}
|
|
32
|
+
for name,entry in entries.items():
|
|
33
|
+
worker_id(name)
|
|
34
|
+
if type(entry) is not dict or not {'endpoint'}<=set(entry)<={'endpoint','isolationProfiles'}:
|
|
35
|
+
raise ValueError('Unrecognized operator worker registration')
|
|
36
|
+
endpoint=entry['endpoint']
|
|
37
|
+
if type(endpoint) is not str or len(endpoint)>1024 or '\0' in endpoint or not endpoint.startswith('unix:///') or not Path(endpoint[7:]).is_absolute():
|
|
38
|
+
raise ValueError('Register an explicit local Docker Unix socket')
|
|
39
|
+
profiles=entry.get('isolationProfiles',{})
|
|
40
|
+
if type(profiles) is not dict or set(profiles)-set(DockerValidationWorker.REVIEWED_PROFILES):
|
|
41
|
+
raise ValueError('Register reviewed worker-specific isolation profiles only')
|
|
42
|
+
for profile,pin in profiles.items():
|
|
43
|
+
if (type(pin) is not dict or set(pin)!={'path','sha256'} or
|
|
44
|
+
pin['sha256']!=DockerValidationWorker.REVIEWED_PROFILES[profile] or
|
|
45
|
+
type(pin['path']) is not str or not Path(pin['path']).is_absolute()):
|
|
46
|
+
raise ValueError('Use exact reviewed isolation policy references')
|
|
47
|
+
result[name]=WorkerRegistration(endpoint,json.dumps(profiles,sort_keys=True).encode())
|
|
48
|
+
return result
|