@atmem/architect 0.0.0-stage → 0.2.0-alpha.4.1
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/README.md +33 -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-B97MkVMs.css +1 -0
- package/dist/assets/index-qtCM2D7O.js +21271 -0
- package/dist/assets/index.es-C6C0sneB.js +18 -0
- package/dist/assets/purify.es-CAi2OJFm.js +3 -0
- package/dist/assets/visual-export-DjbjgQbA.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/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/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 +170 -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 +215 -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 +163 -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 +469 -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 +181 -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_routes.py +82 -0
- package/server/memory_service.py +273 -0
- package/server/memory_worker.py +66 -0
- package/server/openai_voice.py +80 -0
- package/server/packaged_app.py +9 -0
- package/server/pricing.py +84 -0
- package/server/process_control_observation.py +31 -0
- package/server/prompts.py +123 -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/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 +46 -0
- package/src/ArtifactChecks.jsx +49 -0
- package/src/BlockRequirements.jsx +13 -0
- package/src/BrainActivity.jsx +11 -0
- package/src/ChatMessage.jsx +19 -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 +385 -0
- package/src/EngineeringPanel.jsx +151 -0
- package/src/ImplementationDelivery.jsx +55 -0
- package/src/JourneyViews.jsx +400 -0
- package/src/MemoryPanel.jsx +46 -0
- package/src/OpenAIVoicePanel.jsx +65 -0
- package/src/OutputsView.jsx +65 -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 +17 -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 +188 -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/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 +72 -0
- package/src/journey.mjs +87 -0
- package/src/levels.mjs +145 -0
- package/src/local-agent.mjs +184 -0
- package/src/main.jsx +2458 -0
- package/src/model-policy-preflight.mjs +34 -0
- package/src/openai-voice.mjs +102 -0
- package/src/output-bundle.mjs +11 -0
- package/src/ownership-inspection.mjs +28 -0
- package/src/pack.mjs +201 -0
- package/src/pricing.mjs +31 -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 +36 -0
- package/src/useEmulation.jsx +36 -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,178 @@
|
|
|
1
|
+
"""Typed approval-workflow HTTP boundary with operator-owned tenant and policy.
|
|
2
|
+
|
|
3
|
+
No domain inference, identity provider, role administration or automatic execution
|
|
4
|
+
is provided here. Trusted bootstrap supplies the verifier, typed store and origin.
|
|
5
|
+
Acceptance means durable enqueue, never completed processing or delivery.
|
|
6
|
+
"""
|
|
7
|
+
import asyncio
|
|
8
|
+
import json
|
|
9
|
+
import re
|
|
10
|
+
import threading
|
|
11
|
+
from fastapi import FastAPI, Request
|
|
12
|
+
from fastapi.responses import JSONResponse
|
|
13
|
+
from starlette.concurrency import run_in_threadpool
|
|
14
|
+
from durable_workflow import Principal, Denied, Missing, Conflict, identifier
|
|
15
|
+
from workflow_payload import PayloadContract
|
|
16
|
+
from workflow_configuration import owned_origin
|
|
17
|
+
|
|
18
|
+
MAX_BODY = 66000 # Includes the envelope; typed data itself remains <=64000.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BodyTooLarge(ValueError): pass
|
|
22
|
+
class UnsupportedBody(ValueError): pass
|
|
23
|
+
class Busy(RuntimeError): pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def public_operation(row, *, include_source=False):
|
|
27
|
+
result = {'id':str(row['id']), 'state':row['state'], 'revision':row['result_revision'],
|
|
28
|
+
'result':row['result'], 'receipt':row['receipt'],
|
|
29
|
+
'createdAt':row['created_at'].isoformat(), 'updatedAt':row['updated_at'].isoformat()}
|
|
30
|
+
if include_source: result['source'] = row['source_ref']
|
|
31
|
+
return result
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async def body(request, fields):
|
|
35
|
+
types = request.headers.getlist('content-type')
|
|
36
|
+
if len(types) != 1 or types[0].split(';')[0].lower() != 'application/json':
|
|
37
|
+
raise UnsupportedBody('Use application/json')
|
|
38
|
+
lengths = request.headers.getlist('content-length')
|
|
39
|
+
if len(lengths) > 1 or lengths and not re.fullmatch(r'0|[1-9][0-9]{0,8}',lengths[0]):
|
|
40
|
+
raise ValueError('Use an exact body length')
|
|
41
|
+
expected = int(lengths[0]) if lengths else None
|
|
42
|
+
if expected is not None and expected > MAX_BODY: raise BodyTooLarge()
|
|
43
|
+
raw = bytearray()
|
|
44
|
+
async with asyncio.timeout(5):
|
|
45
|
+
async for chunk in request.stream():
|
|
46
|
+
if len(raw) + len(chunk) > MAX_BODY: raise BodyTooLarge()
|
|
47
|
+
raw.extend(chunk)
|
|
48
|
+
if expected is not None and len(raw) != expected: raise ValueError('Body length differs')
|
|
49
|
+
def unique(pairs):
|
|
50
|
+
result = {}
|
|
51
|
+
for key, value in pairs:
|
|
52
|
+
if key in result: raise ValueError('Duplicate application field')
|
|
53
|
+
result[key] = value
|
|
54
|
+
return result
|
|
55
|
+
def constant(_): raise ValueError('Nonfinite JSON is unsupported')
|
|
56
|
+
value = json.loads(raw.decode('utf-8'),object_pairs_hook=unique,parse_constant=constant)
|
|
57
|
+
if type(value) is not dict or set(value) != set(fields): raise ValueError('Use the exact operation envelope')
|
|
58
|
+
return value
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def create_app(verify, store, *, account_id, public_origin, operation_kind='draft', token_header=None):
|
|
62
|
+
identifier(account_id); origin = owned_origin(public_origin)
|
|
63
|
+
if not callable(verify) or type(getattr(store,'payload_contract',None)) is not PayloadContract:
|
|
64
|
+
raise ValueError('Supply an owned verifier and typed application store')
|
|
65
|
+
if type(operation_kind) is not str or operation_kind not in {'document','draft','account_change'}: raise ValueError('Use an owned operation policy')
|
|
66
|
+
if getattr(store,'operation_kind',None) not in {None,operation_kind}: raise ValueError('HTTP and stored operation policies differ')
|
|
67
|
+
if token_header is not None and (type(token_header) is not str or token_header != 'x-goog-iap-jwt-assertion'): raise ValueError('Unsupported native identity transport')
|
|
68
|
+
app = FastAPI(docs_url=None,redoc_url=None,openapi_url=None)
|
|
69
|
+
policy = getattr(store, 'review_policy', None)
|
|
70
|
+
def representation(row): return public_operation(row, include_source=policy is not None)
|
|
71
|
+
slots = threading.BoundedSemaphore(20)
|
|
72
|
+
def error(code,status): return JSONResponse({'error':{'code':code}},status_code=status)
|
|
73
|
+
async def actor(request):
|
|
74
|
+
if token_header:
|
|
75
|
+
headers = request.headers.getlist(token_header)
|
|
76
|
+
token = headers[0] if len(headers) == 1 else ''
|
|
77
|
+
else:
|
|
78
|
+
headers = request.headers.getlist('authorization')
|
|
79
|
+
supplied = headers[0] if len(headers) == 1 else ''
|
|
80
|
+
token = supplied[7:] if supplied.startswith('Bearer ') else ''
|
|
81
|
+
if not token or len(token) > 16384: raise Denied('Verified identity required')
|
|
82
|
+
try: subject = await run_in_threadpool(verify,token)
|
|
83
|
+
except Exception: raise Denied('Verified identity required') from None
|
|
84
|
+
try: principal = Principal(account_id,subject)
|
|
85
|
+
except ValueError: raise Denied('Verified identity required') from None
|
|
86
|
+
await run_in_threadpool(store.authorize,principal)
|
|
87
|
+
return principal
|
|
88
|
+
async def mutation(request,fields):
|
|
89
|
+
user = await actor(request)
|
|
90
|
+
if request.query_params: raise ValueError('Mutations do not accept query configuration')
|
|
91
|
+
value = await body(request,fields)
|
|
92
|
+
if await actor(request) != user: raise Denied('Identity changed while reading input')
|
|
93
|
+
return user,value
|
|
94
|
+
@app.middleware('http')
|
|
95
|
+
async def boundary(request,next_handler):
|
|
96
|
+
acquired = False
|
|
97
|
+
try:
|
|
98
|
+
origins = request.headers.getlist('origin')
|
|
99
|
+
if len(origins) > 1 or origins and origins[0] != origin: raise Denied('Wrong application origin')
|
|
100
|
+
if request.method not in {'GET','HEAD','OPTIONS'}:
|
|
101
|
+
if origins != [origin]: raise Denied('Exact application origin required')
|
|
102
|
+
acquired = slots.acquire(blocking=False)
|
|
103
|
+
if not acquired: raise Busy()
|
|
104
|
+
response = await next_handler(request)
|
|
105
|
+
except Denied: response = error('access_denied',403)
|
|
106
|
+
except Missing: response = error('not_found',404)
|
|
107
|
+
except Conflict: response = error('conflict',409)
|
|
108
|
+
except BodyTooLarge: response = error('body_too_large',413)
|
|
109
|
+
except UnsupportedBody: response = error('unsupported_content_type',415)
|
|
110
|
+
except Busy: response = error('busy',429)
|
|
111
|
+
except TimeoutError: response = error('body_timeout',408)
|
|
112
|
+
except (ValueError,UnicodeError,RecursionError): response = error('invalid_request',400)
|
|
113
|
+
except Exception: response = error('unavailable',503)
|
|
114
|
+
finally:
|
|
115
|
+
if acquired: slots.release()
|
|
116
|
+
response.headers.update({'Cache-Control':'no-store','X-Content-Type-Options':'nosniff',
|
|
117
|
+
'Referrer-Policy':'no-referrer','Strict-Transport-Security':'max-age=31536000',
|
|
118
|
+
'Content-Security-Policy':"default-src 'none'; frame-ancestors 'none'; base-uri 'none'"})
|
|
119
|
+
return response
|
|
120
|
+
@app.get('/healthz')
|
|
121
|
+
def health(): return {'status':'running'}
|
|
122
|
+
@app.get('/api/session')
|
|
123
|
+
async def session(request:Request):
|
|
124
|
+
user = await actor(request)
|
|
125
|
+
if request.query_params: raise ValueError('No session overrides are accepted')
|
|
126
|
+
return {'roles':await run_in_threadpool(store.authorize,user),'accountMode':'operator-owned'}
|
|
127
|
+
@app.post('/api/operations',status_code=202)
|
|
128
|
+
async def submit(request:Request):
|
|
129
|
+
user,value = await mutation(request,['source'])
|
|
130
|
+
keys = request.headers.getlist('idempotency-key')
|
|
131
|
+
if len(keys) != 1 or not re.fullmatch(r'[A-Za-z0-9][A-Za-z0-9_.:-]{0,199}',keys[0]):
|
|
132
|
+
raise ValueError('Use a stable bounded operation key')
|
|
133
|
+
operation = await run_in_threadpool(store.submit,user,operation_kind,keys[0],value['source'])
|
|
134
|
+
return {'operation':representation(await run_in_threadpool(store.get,user,operation))}
|
|
135
|
+
@app.get('/api/operations')
|
|
136
|
+
async def listing(request:Request):
|
|
137
|
+
user = await actor(request)
|
|
138
|
+
if set(request.query_params)-{'before'} or len(request.query_params.getlist('before')) > 1:
|
|
139
|
+
raise ValueError('Use the bounded operation cursor')
|
|
140
|
+
rows = await run_in_threadpool(store.list,user,before=request.query_params.get('before'),limit=25)
|
|
141
|
+
return {'operations':[representation(row) for row in rows], 'next':str(rows[-1]['id']) if len(rows)==25 else None}
|
|
142
|
+
@app.get('/api/operations/{operation_id}')
|
|
143
|
+
async def detail(operation_id:str,request:Request):
|
|
144
|
+
user = await actor(request)
|
|
145
|
+
if request.query_params: raise ValueError('No operation overrides are accepted')
|
|
146
|
+
return {'operation':representation(await run_in_threadpool(store.get,user,operation_id))}
|
|
147
|
+
@app.post('/api/operations/{operation_id}/review')
|
|
148
|
+
async def review(operation_id:str,request:Request):
|
|
149
|
+
user,value = await mutation(request,['revision','result'])
|
|
150
|
+
if type(value['revision']) is not int: raise ValueError('Use an exact revision')
|
|
151
|
+
row = await run_in_threadpool(store.edit_result,user,operation_id,value['revision'],value['result'])
|
|
152
|
+
return {'operation':representation(row)}
|
|
153
|
+
if policy is not None:
|
|
154
|
+
@app.post('/api/operations/{operation_id}/inspect')
|
|
155
|
+
async def inspect(operation_id:str,request:Request):
|
|
156
|
+
user,_ = await mutation(request,[])
|
|
157
|
+
row = await run_in_threadpool(store.inspect,user,operation_id)
|
|
158
|
+
return {'operation':representation(row),'payloadSha256':store._fingerprint(row),
|
|
159
|
+
'destination':row['destination_contract']}
|
|
160
|
+
@app.get('/api/operations/{operation_id}/history')
|
|
161
|
+
async def history(operation_id:str,request:Request):
|
|
162
|
+
user = await actor(request)
|
|
163
|
+
if set(request.query_params)-{'afterRevision'}: raise ValueError('Invalid history cursor')
|
|
164
|
+
cursors = request.query_params.getlist('afterRevision')
|
|
165
|
+
if len(cursors)>1 or cursors and not re.fullmatch(r'0|[1-9][0-9]{0,9}',cursors[0]): raise ValueError('Invalid history cursor')
|
|
166
|
+
rows = await run_in_threadpool(store.result_history,user,operation_id,after_revision=int(cursors[0]) if cursors else 0)
|
|
167
|
+
return {'history':[{'revision':r['revision'],'result':r['result'],'actor':r['actor'],
|
|
168
|
+
'createdAt':r['created_at'].isoformat()} for r in rows]}
|
|
169
|
+
@app.post('/api/operations/{operation_id}/approve')
|
|
170
|
+
async def approve(operation_id:str,request:Request):
|
|
171
|
+
user,value = await mutation(request,['revision','payloadSha256'] if policy is not None else ['revision'])
|
|
172
|
+
if type(value['revision']) is not int: raise ValueError('Use an exact revision')
|
|
173
|
+
if policy is not None:
|
|
174
|
+
row = await run_in_threadpool(store.approve,user,operation_id,value['revision'],value['payloadSha256'])
|
|
175
|
+
else:
|
|
176
|
+
row = await run_in_threadpool(store.approve,user,operation_id,value['revision'])
|
|
177
|
+
return {'operation':representation(row)}
|
|
178
|
+
return app
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Native staff login bindings. Database membership remains authoritative for roles.
|
|
2
|
+
|
|
3
|
+
Configuration is compiler/operator-owned. Never derive a signing-key URL, tenant,
|
|
4
|
+
issuer, OAuth audience or processing role from caller claims or document content.
|
|
5
|
+
JWT validity is bounded by expiry, not a claim of continuous IdP revocation.
|
|
6
|
+
"""
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
import re
|
|
9
|
+
import uuid
|
|
10
|
+
import jwt
|
|
11
|
+
from durable_workflow import Denied
|
|
12
|
+
|
|
13
|
+
UUID=r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
|
|
14
|
+
|
|
15
|
+
class CognitoStaffVerifier:
|
|
16
|
+
def __init__(self,region,pool_id,client_id,scope,*,jwks=None):
|
|
17
|
+
if(not isinstance(region,str)or not re.fullmatch(r'[a-z]{2}-[a-z]+-[0-9]',region)
|
|
18
|
+
or not isinstance(pool_id,str)or not re.fullmatch(re.escape(region)+r'_[A-Za-z0-9]{1,55}',pool_id)
|
|
19
|
+
or not isinstance(client_id,str)or not re.fullmatch(r'[a-z0-9]{1,128}',client_id)
|
|
20
|
+
or not isinstance(scope,str)or not re.fullmatch(r'[A-Za-z0-9_.-]{1,80}/process',scope)):raise ValueError('Use the exact owned Cognito pool/client/scope')
|
|
21
|
+
self.issuer='https://cognito-idp.'+region+'.amazonaws.com/'+pool_id;self.client_id=client_id;self.scope=scope
|
|
22
|
+
self.jwks=jwks or jwt.PyJWKClient(self.issuer+'/.well-known/jwks.json',timeout=2,lifespan=300)
|
|
23
|
+
def __call__(self,token):
|
|
24
|
+
try:
|
|
25
|
+
if not isinstance(token,str)or not 1<=len(token)<=16384:raise Denied('Verified staff identity required')
|
|
26
|
+
key=self.jwks.get_signing_key_from_jwt(token).key
|
|
27
|
+
# Cognito ACCESS tokens bind the app client in client_id, rather than
|
|
28
|
+
# the ID token's aud. Never accept an ID token as an API credential.
|
|
29
|
+
claims=jwt.decode(token,key,algorithms=['RS256'],issuer=self.issuer,options={'verify_aud':False,'require':['iss','sub','exp','iat','client_id','token_use','scope','username']})
|
|
30
|
+
if(claims['token_use']!='access'or claims['client_id']!=self.client_id
|
|
31
|
+
or not isinstance(claims['scope'],str)or self.scope not in claims['scope'].split()
|
|
32
|
+
or not isinstance(claims['sub'],str)or str(uuid.UUID(claims['sub']))!=claims['sub']
|
|
33
|
+
or not isinstance(claims['username'],str)or not claims['username']
|
|
34
|
+
or any(type(claims[name])is not int for name in ['iat','exp'])or not 0<claims['exp']-claims['iat']<=3600):raise Denied('Verified staff identity required')
|
|
35
|
+
return claims['sub']
|
|
36
|
+
except Exception:raise Denied('Verified staff identity required')from None
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class StaffIdentity:
|
|
40
|
+
verify:object
|
|
41
|
+
public_configuration:dict
|
|
42
|
+
token_header:str|None=None
|
|
43
|
+
|
|
44
|
+
def native_staff_identity(provider,settings,*,jwks=None):
|
|
45
|
+
if not isinstance(settings,dict)or any(not isinstance(value,str)for value in settings.values()):raise ValueError('Use owned native staff identity settings')
|
|
46
|
+
patterns={
|
|
47
|
+
'aws':{'REGION':r'[a-z]{2}-[a-z]+-[0-9]','POOL_ID':r'[a-z]{2}-[a-z]+-[0-9]_[A-Za-z0-9]{1,55}','CLIENT_ID':r'[a-z0-9]{1,128}','AUTH_ORIGIN':r'https://[a-z0-9][a-z0-9-]{0,61}[a-z0-9]\.auth\.[a-z]{2}-[a-z]+-[0-9]\.amazoncognito\.com','AUTH_SCOPE':r'[A-Za-z0-9_.-]{1,80}/process'},
|
|
48
|
+
'azure':{'TENANT_ID':UUID,'API_CLIENT_ID':UUID,'SPA_CLIENT_ID':UUID},
|
|
49
|
+
'gcp':{'PROJECT_NUMBER':r'[0-9]{6,20}','REGION':r'[a-z]+-[a-z]+[0-9]','SERVICE_NAME':r'[a-z][a-z0-9-]{4,47}[a-z0-9]'},
|
|
50
|
+
'ibm':{'APPID_TENANT_ID':UUID,'APPID_CLIENT_ID':UUID,'APPID_ISSUER':r'https://[a-z]{2}-[a-z]+\.appid\.cloud\.ibm\.com/oauth/v4/'+UUID,'AUTH_SCOPE':r'[A-Za-z0-9_.-]{1,80}/process'}}
|
|
51
|
+
if provider not in patterns or set(settings)!=set(patterns[provider])or any(not re.fullmatch(patterns[provider][name],value)for name,value in settings.items()):raise ValueError('Use exact reviewed native staff identity settings')
|
|
52
|
+
c=dict(settings)
|
|
53
|
+
if provider=='aws':
|
|
54
|
+
if not c['AUTH_ORIGIN'].endswith('.auth.'+c['REGION']+'.amazoncognito.com'):raise ValueError('Cognito authorization region differs from the owned pool')
|
|
55
|
+
verify=CognitoStaffVerifier(c['REGION'],c['POOL_ID'],c['CLIENT_ID'],c['AUTH_SCOPE'],jwks=jwks)
|
|
56
|
+
public={'mode':'oauth-pkce','authOrigin':c['AUTH_ORIGIN'],'authorizationEndpoint':c['AUTH_ORIGIN']+'/oauth2/authorize','tokenEndpoint':c['AUTH_ORIGIN']+'/oauth2/token','clientId':c['CLIENT_ID'],'scopes':'openid '+c['AUTH_SCOPE']}
|
|
57
|
+
elif provider=='azure':
|
|
58
|
+
from azure_structured import EntraVerifier
|
|
59
|
+
verify=EntraVerifier(c,jwks);authority='https://login.microsoftonline.com/'+c['TENANT_ID']+'/oauth2/v2.0'
|
|
60
|
+
public={'mode':'oauth-pkce','authOrigin':'https://login.microsoftonline.com','authorizationEndpoint':authority+'/authorize','tokenEndpoint':authority+'/token','clientId':c['SPA_CLIENT_ID'],'scopes':'openid api://'+c['API_CLIENT_ID']+'/access_as_user'}
|
|
61
|
+
elif provider=='gcp':
|
|
62
|
+
from gcp_structured import IapVerifier
|
|
63
|
+
return StaffIdentity(IapVerifier(c,jwks),{'mode':'proxy','loginPath':'/','logoutPath':None},'x-goog-iap-jwt-assertion')
|
|
64
|
+
else:
|
|
65
|
+
from ibm_structured import AppIdVerifier
|
|
66
|
+
if not c['APPID_ISSUER'].endswith('/oauth/v4/'+c['APPID_TENANT_ID']):raise ValueError('App ID issuer tenant differs from the owned tenant')
|
|
67
|
+
verify=AppIdVerifier(c,jwks);issuer=c['APPID_ISSUER'];origin=issuer.split('/oauth/v4/')[0]
|
|
68
|
+
public={'mode':'oauth-pkce','authOrigin':origin,'authorizationEndpoint':issuer+'/authorization','tokenEndpoint':issuer+'/token','clientId':c['APPID_CLIENT_ID'],'scopes':'openid '+c['AUTH_SCOPE']}
|
|
69
|
+
return StaffIdentity(verify,public)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Versioned bounded application data contracts, independent of domain/provider.
|
|
2
|
+
|
|
3
|
+
This is a small closed contract language, not a JSON Schema implementation.
|
|
4
|
+
Operator-selected contracts describe data, never roles, executable validation,
|
|
5
|
+
destinations or transitions. Domain semantic checks remain the adapter's duty.
|
|
6
|
+
"""
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
import hashlib
|
|
9
|
+
import json
|
|
10
|
+
import re
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def encoded(value):
|
|
14
|
+
def exact(item, depth=0):
|
|
15
|
+
if depth > 32: raise ValueError('Application JSON exceeds its nesting boundary')
|
|
16
|
+
if type(item) is dict:
|
|
17
|
+
if any(type(k) is not str for k in item): raise ValueError('Application object keys must be strings')
|
|
18
|
+
for child in item.values(): exact(child, depth + 1)
|
|
19
|
+
elif type(item) is list:
|
|
20
|
+
for child in item: exact(child, depth + 1)
|
|
21
|
+
elif type(item) not in {str, int, bool, type(None)}:
|
|
22
|
+
raise ValueError('Use exact application JSON types; decimals require an explicit string contract')
|
|
23
|
+
exact(value)
|
|
24
|
+
try:
|
|
25
|
+
raw = json.dumps(value, sort_keys=True, separators=(',', ':'), ensure_ascii=False, allow_nan=False).encode('utf-8')
|
|
26
|
+
except (TypeError, ValueError, UnicodeError, RecursionError):
|
|
27
|
+
raise ValueError('Invalid bounded application JSON') from None
|
|
28
|
+
if len(raw) > 64000:
|
|
29
|
+
raise ValueError('Application JSON exceeds its byte boundary')
|
|
30
|
+
return raw
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def schema(value, depth=0, budget=None):
|
|
34
|
+
if budget is None: budget = [256]
|
|
35
|
+
budget[0] -= 1
|
|
36
|
+
if depth > 8 or budget[0] < 0 or type(value) is not dict:
|
|
37
|
+
raise ValueError('Application contract exceeds its structural boundary')
|
|
38
|
+
kind = value.get('type')
|
|
39
|
+
shapes = {'object': {'type', 'properties', 'required'}, 'array': {'type', 'items', 'maxItems'},
|
|
40
|
+
'string': {'type', 'minBytes', 'maxBytes'}, 'integer': {'type', 'minimum', 'maximum'},
|
|
41
|
+
'boolean': {'type'}, 'enum': {'type', 'values'}}
|
|
42
|
+
if type(kind) is not str or kind not in shapes or set(value) != shapes[kind]:
|
|
43
|
+
raise ValueError('Use the closed application contract language')
|
|
44
|
+
if kind == 'object':
|
|
45
|
+
properties, required = value['properties'], value['required']
|
|
46
|
+
if (type(properties) is not dict or not 1 <= len(properties) <= 32 or
|
|
47
|
+
any(type(k) is not str or not re.fullmatch(r'[A-Za-z][A-Za-z0-9_]{0,79}', k) for k in properties) or
|
|
48
|
+
type(required) is not list or any(type(k) is not str for k in required) or
|
|
49
|
+
len(required) != len(set(required)) or not set(required) <= set(properties)):
|
|
50
|
+
raise ValueError('Use bounded exact object fields')
|
|
51
|
+
for item in properties.values(): schema(item, depth + 1, budget)
|
|
52
|
+
elif kind == 'array':
|
|
53
|
+
if type(value['maxItems']) is not int or not 1 <= value['maxItems'] <= 64:
|
|
54
|
+
raise ValueError('Use a bounded array')
|
|
55
|
+
schema(value['items'], depth + 1, budget)
|
|
56
|
+
elif kind == 'string':
|
|
57
|
+
if any(type(value[k]) is not int for k in ['minBytes', 'maxBytes']) or not 0 <= value['minBytes'] <= value['maxBytes'] <= 64000:
|
|
58
|
+
raise ValueError('Use exact UTF-8 byte limits')
|
|
59
|
+
elif kind == 'integer':
|
|
60
|
+
if any(type(value[k]) is not int for k in ['minimum', 'maximum']) or not -(2**53 - 1) <= value['minimum'] <= value['maximum'] <= 2**53 - 1:
|
|
61
|
+
raise ValueError('Use exact portable integer limits')
|
|
62
|
+
elif kind == 'enum':
|
|
63
|
+
items = value['values']
|
|
64
|
+
if (type(items) is not list or not 1 <= len(items) <= 64 or
|
|
65
|
+
any(type(item) is not str or not 1 <= len(item.encode('utf-8')) <= 256 for item in items) or len(items) != len(set(items))):
|
|
66
|
+
raise ValueError('Use a bounded distinct string enumeration')
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def conforms(rule, value):
|
|
70
|
+
kind = rule['type']
|
|
71
|
+
valid = False
|
|
72
|
+
if kind == 'object':
|
|
73
|
+
valid = (type(value) is dict and set(rule['required']) <= set(value) <= set(rule['properties']))
|
|
74
|
+
if valid:
|
|
75
|
+
for key, item in value.items(): conforms(rule['properties'][key], item)
|
|
76
|
+
elif kind == 'array':
|
|
77
|
+
valid = type(value) is list and len(value) <= rule['maxItems']
|
|
78
|
+
if valid:
|
|
79
|
+
for item in value: conforms(rule['items'], item)
|
|
80
|
+
elif kind == 'string':
|
|
81
|
+
valid = type(value) is str and rule['minBytes'] <= len(value.encode('utf-8')) <= rule['maxBytes']
|
|
82
|
+
elif kind == 'integer': valid = type(value) is int and rule['minimum'] <= value <= rule['maximum']
|
|
83
|
+
elif kind == 'boolean': valid = type(value) is bool
|
|
84
|
+
elif kind == 'enum': valid = type(value) is str and value in rule['values']
|
|
85
|
+
if not valid: raise ValueError('Application payload does not match its owned contract')
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@dataclass(frozen=True, init=False)
|
|
89
|
+
class PayloadContract:
|
|
90
|
+
_encoded: bytes
|
|
91
|
+
|
|
92
|
+
def __init__(self, value):
|
|
93
|
+
raw = encoded(value)
|
|
94
|
+
copied = json.loads(raw)
|
|
95
|
+
if (type(copied) is not dict or set(copied) != {'version', 'id', 'source', 'result'} or
|
|
96
|
+
type(copied['version']) is not int or copied['version'] != 1 or
|
|
97
|
+
type(copied['id']) is not str or not re.fullmatch(r'[a-z][a-z0-9-]{0,79}', copied['id'])):
|
|
98
|
+
raise ValueError('Use a versioned owned application payload contract')
|
|
99
|
+
budget = [256]
|
|
100
|
+
for side in ['source', 'result']:
|
|
101
|
+
schema(copied[side], budget=budget)
|
|
102
|
+
if copied[side]['type'] != 'object': raise ValueError('Application boundaries require object payloads')
|
|
103
|
+
object.__setattr__(self, '_encoded', raw)
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def sha256(self): return hashlib.sha256(self._encoded).hexdigest()
|
|
107
|
+
|
|
108
|
+
def fields(self, side):
|
|
109
|
+
if side not in {'source', 'result'}: raise ValueError('Use an application input or result boundary')
|
|
110
|
+
return tuple(json.loads(self._encoded)[side]['properties'])
|
|
111
|
+
|
|
112
|
+
def validate(self, side, value):
|
|
113
|
+
if side not in {'source', 'result'}: raise ValueError('Use an application input or result boundary')
|
|
114
|
+
# Round trip before validation: no caller mutation can change the bytes
|
|
115
|
+
# validated for persistence. No clipping, coercion or default insertion.
|
|
116
|
+
copied = json.loads(encoded(value))
|
|
117
|
+
conforms(json.loads(self._encoded)[side], copied)
|
|
118
|
+
return copied
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Hard deadlines for trusted processor/destination child processes on Linux.
|
|
2
|
+
|
|
3
|
+
The compiler/operator supplies absolute commands and a minimal environment. No
|
|
4
|
+
shell, model-selected program, inherited account credentials or stderr payloads.
|
|
5
|
+
Input/output are bounded JSON. This is a deadline boundary, not an OS sandbox;
|
|
6
|
+
untrusted PDF parsers still require a separate hardened container boundary.
|
|
7
|
+
"""
|
|
8
|
+
import json
|
|
9
|
+
import math
|
|
10
|
+
import os
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
import selectors
|
|
13
|
+
import signal
|
|
14
|
+
import subprocess
|
|
15
|
+
import tempfile
|
|
16
|
+
import time
|
|
17
|
+
from durable_workflow import canonical
|
|
18
|
+
from workflow_errors import TemporaryWork,PermanentWork,ExternalOutcomeUnknown,DestinationNotAttempted,InvalidDispatch
|
|
19
|
+
|
|
20
|
+
class ProcessStage:
|
|
21
|
+
def __init__(self,command,*,kind,timeout_seconds=60,environment=None,cwd=None,idempotency_verified=False):
|
|
22
|
+
if (not isinstance(command,(tuple,list))or not command or any(not isinstance(v,str)or not v or '\0'in v for v in command)
|
|
23
|
+
or not Path(command[0]).is_absolute()or not Path(command[0]).is_file()):raise ValueError('A trusted absolute executable is required')
|
|
24
|
+
if kind not in {'extract','dispatch'}or type(timeout_seconds)not in {int,float}or not math.isfinite(timeout_seconds)or not 0<timeout_seconds<=300:raise ValueError('Invalid bounded stage configuration')
|
|
25
|
+
if type(idempotency_verified)is not bool or(kind=='extract'and idempotency_verified):raise ValueError('Invalid stage replay configuration')
|
|
26
|
+
environment={}if environment is None else environment
|
|
27
|
+
if not isinstance(environment,dict)or any(not isinstance(k,str)or not isinstance(v,str)or not k or '='in k or '\0'in k+v for k,v in environment.items()):raise ValueError('Use an explicit trusted child environment')
|
|
28
|
+
directory=Path(cwd).resolve()if cwd else Path(command[0]).resolve().parent
|
|
29
|
+
if not directory.is_dir():raise ValueError('Use an existing owned working directory')
|
|
30
|
+
self.command=tuple(command);self.kind=kind;self.timeout_seconds=timeout_seconds;self.environment=dict(environment);self.cwd=directory;self.idempotency_verified=idempotency_verified
|
|
31
|
+
|
|
32
|
+
def __call__(self,row,**identity):
|
|
33
|
+
# Deliberately exclude database DSNs, lease tokens and application logins.
|
|
34
|
+
selected={key:row[key]for key in ['kind','source_ref','destination_contract','result','result_revision','approved_by','approved_hash','state']if key in row}
|
|
35
|
+
selected.update({key:str(row[key])for key in ['id','account_id']if key in row})
|
|
36
|
+
if 'created_at'in row:selected['created_at']=row['created_at'].isoformat()if hasattr(row['created_at'],'isoformat')else row['created_at']
|
|
37
|
+
payload=canonical({'operation':selected,'identity':identity},140000)
|
|
38
|
+
failure=TemporaryWork if self.kind=='extract'else ExternalOutcomeUnknown
|
|
39
|
+
process=None
|
|
40
|
+
try:
|
|
41
|
+
# A bounded private temporary input avoids a pipe-write deadlock when
|
|
42
|
+
# a child never reads stdin; no sensitive input appears in argv/env.
|
|
43
|
+
with tempfile.TemporaryFile(mode='w+b')as source,selectors.DefaultSelector()as selector:
|
|
44
|
+
source.write(payload);source.seek(0)
|
|
45
|
+
started=time.monotonic()
|
|
46
|
+
process=subprocess.Popen(self.command,stdin=source,stdout=subprocess.PIPE,stderr=subprocess.DEVNULL,cwd=self.cwd,env=self.environment,start_new_session=True)
|
|
47
|
+
os.set_blocking(process.stdout.fileno(),False);selector.register(process.stdout,selectors.EVENT_READ)
|
|
48
|
+
output=bytearray()
|
|
49
|
+
while selector.get_map():
|
|
50
|
+
remaining=self.timeout_seconds-(time.monotonic()-started)
|
|
51
|
+
if remaining<=0:raise failure('Owned stage exceeded its deadline')
|
|
52
|
+
for key,_ in selector.select(min(remaining,.1)):
|
|
53
|
+
chunk=os.read(key.fd,8192)
|
|
54
|
+
if not chunk:selector.unregister(key.fileobj);continue
|
|
55
|
+
output.extend(chunk)
|
|
56
|
+
if len(output)>70000:raise failure('Owned stage exceeded its output boundary')
|
|
57
|
+
remaining=self.timeout_seconds-(time.monotonic()-started)
|
|
58
|
+
if remaining<=0:raise failure('Owned stage exceeded its deadline')
|
|
59
|
+
try:code=process.wait(timeout=remaining)
|
|
60
|
+
except subprocess.TimeoutExpired:raise failure('Owned stage exceeded its deadline')from None
|
|
61
|
+
if code!=0:raise failure('Owned stage did not confirm completion')
|
|
62
|
+
def unique(pairs):
|
|
63
|
+
value={}
|
|
64
|
+
for key,item in pairs:
|
|
65
|
+
if key in value:raise ValueError('Duplicate stage field')
|
|
66
|
+
value[key]=item
|
|
67
|
+
return value
|
|
68
|
+
try:
|
|
69
|
+
response=json.loads(output,object_pairs_hook=unique,parse_constant=lambda _:(_ for _ in ()).throw(ValueError('Nonfinite stage result')))
|
|
70
|
+
if not isinstance(response,dict)or set(response)!={'status','result'}or not isinstance(response['result'],dict):raise ValueError('Invalid stage result')
|
|
71
|
+
canonical(response['result'],64000)
|
|
72
|
+
except (ValueError,TypeError,UnicodeError,RecursionError):raise failure('Owned stage returned an invalid response')from None
|
|
73
|
+
status=response['status']
|
|
74
|
+
if status=='ok':return response['result']
|
|
75
|
+
if self.kind=='extract'and status=='temporary':raise TemporaryWork('Processing temporarily unavailable')
|
|
76
|
+
if self.kind=='extract'and status=='permanent':raise PermanentWork('Processing requires review')
|
|
77
|
+
if self.kind=='dispatch'and status=='not_attempted':raise DestinationNotAttempted('Destination not attempted')
|
|
78
|
+
if self.kind=='dispatch'and status=='invalid':raise InvalidDispatch('Approved dispatch integrity requires review')
|
|
79
|
+
raise failure('Owned stage did not confirm completion')
|
|
80
|
+
except (TemporaryWork,PermanentWork,ExternalOutcomeUnknown):raise
|
|
81
|
+
except Exception:raise failure('Owned stage did not confirm completion')from None
|
|
82
|
+
finally:
|
|
83
|
+
if process is not None:
|
|
84
|
+
# Kill descendants even if the direct child exited before them.
|
|
85
|
+
try:os.killpg(process.pid,signal.SIGKILL)
|
|
86
|
+
except ProcessLookupError:pass
|
|
87
|
+
process.wait()
|
|
88
|
+
if process.stdout:process.stdout.close()
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Pure versioned application declaration, shared by engine and delivered runtime."""
|
|
2
|
+
import base64
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
import hashlib
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import uuid
|
|
8
|
+
from urllib.parse import urlsplit
|
|
9
|
+
if __package__:
|
|
10
|
+
from .workflow_payload import PayloadContract,encoded
|
|
11
|
+
from .workflow_contract import bind_payload_contract,bind_application_contract
|
|
12
|
+
else:
|
|
13
|
+
from workflow_payload import PayloadContract,encoded
|
|
14
|
+
from workflow_contract import bind_payload_contract,bind_application_contract
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def identifier(value):
|
|
18
|
+
if type(value)is not str or str(uuid.UUID(value))!=value:raise ValueError('Use a canonical account UUID')
|
|
19
|
+
return value
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def owned_origin(value):
|
|
23
|
+
try:
|
|
24
|
+
parts = urlsplit(value)
|
|
25
|
+
port = parts.port
|
|
26
|
+
except (TypeError, ValueError): raise ValueError('Use the exact owned application origin') from None
|
|
27
|
+
if (type(value) is not str or not parts.hostname or parts.username or parts.password or
|
|
28
|
+
parts.query or parts.fragment or parts.path or
|
|
29
|
+
(port is not None and not 1 <= port <= 65535) or
|
|
30
|
+
any(ord(c) < 33 or ord(c) > 126 for c in value) or
|
|
31
|
+
(parts.scheme != 'https' and not (parts.scheme == 'http' and parts.hostname in {'localhost','127.0.0.1','::1'}))):
|
|
32
|
+
raise ValueError('Use the exact HTTPS origin or explicit loopback test origin')
|
|
33
|
+
return value
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
IDENTITY_KEYS={
|
|
37
|
+
'aws':('REGION','POOL_ID','CLIENT_ID','AUTH_ORIGIN','AUTH_SCOPE'),
|
|
38
|
+
'azure':('TENANT_ID','API_CLIENT_ID','SPA_CLIENT_ID'),
|
|
39
|
+
'gcp':('PROJECT_NUMBER','REGION','SERVICE_NAME'),
|
|
40
|
+
'ibm':('APPID_TENANT_ID','APPID_CLIENT_ID','APPID_ISSUER','AUTH_SCOPE')}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(frozen=True,init=False)
|
|
44
|
+
class ApplicationProfile:
|
|
45
|
+
_encoded:bytes
|
|
46
|
+
|
|
47
|
+
def __init__(self,value):
|
|
48
|
+
raw=encoded(value);data=json.loads(raw)
|
|
49
|
+
keys={'format','provider','componentId','environment','accountId','publicOrigin','operationKind','identitySettings','payloadContract','destinationContract'}
|
|
50
|
+
if type(data)is not dict or set(data)!=keys or data['format']!='approval-application-v1':raise ValueError('Use the exact versioned application profile')
|
|
51
|
+
if type(data['provider'])is not str or data['provider']not in IDENTITY_KEYS:raise ValueError('Use the selected application provider')
|
|
52
|
+
if type(data['componentId'])is not str or not 1<=len(data['componentId'].encode('utf-8'))<=512 or any(ord(c)<32 for c in data['componentId']):raise ValueError('Use the complete bounded component identity')
|
|
53
|
+
if type(data['environment'])is not str or data['environment']not in {'staging','production','qualification'}:raise ValueError('Use the reviewed deployment environment')
|
|
54
|
+
identifier(data['accountId']);owned_origin(data['publicOrigin'])
|
|
55
|
+
if type(data['operationKind'])is not str or data['operationKind']not in {'document','draft','account_change'}:raise ValueError('Use an explicit owned operation policy')
|
|
56
|
+
identity=data['identitySettings']
|
|
57
|
+
if type(identity)is not dict or set(identity)!=set(IDENTITY_KEYS[data['provider']])or any(type(v)is not str or not 1<=len(v.encode('utf-8'))<=2000 for v in identity.values()):raise ValueError('Use the complete nonsecret native identity settings')
|
|
58
|
+
if type(data['destinationContract'])is not dict or 'applicationContractSha256'in data['destinationContract']:raise ValueError('Use the reviewed base destination; application binding is generated')
|
|
59
|
+
bind_payload_contract(data['destinationContract'],PayloadContract(data['payloadContract']))
|
|
60
|
+
object.__setattr__(self,'_encoded',raw)
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def data(self):return json.loads(self._encoded)
|
|
64
|
+
@property
|
|
65
|
+
def payload(self):return PayloadContract(self.data['payloadContract'])
|
|
66
|
+
@property
|
|
67
|
+
def sha256(self):return hashlib.sha256(self._encoded).hexdigest()
|
|
68
|
+
@property
|
|
69
|
+
def destination(self):return bind_application_contract(bind_payload_contract(self.data['destinationContract'],self.payload),self.sha256)
|
|
70
|
+
@property
|
|
71
|
+
def binding_sha256(self):
|
|
72
|
+
value={key:self.data[key]for key in ['provider','componentId','environment','accountId']}
|
|
73
|
+
return hashlib.sha256(encoded(value)).hexdigest()
|
|
74
|
+
@property
|
|
75
|
+
def namespace(self):
|
|
76
|
+
# Explicit native alias, not truncation of the saved component identity.
|
|
77
|
+
# 88-bit digest encoding fits the existing task/notification constraints.
|
|
78
|
+
return 'w'+base64.b32encode(bytes.fromhex(self.binding_sha256)[:11]).decode().lower().rstrip('=')
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def load_profile(path):
|
|
82
|
+
if not isinstance(path,(str,Path))or not Path(path).is_absolute():raise ValueError('Use an owned absolute application profile path')
|
|
83
|
+
with Path(path).open('rb')as stream:raw=stream.read(64001)
|
|
84
|
+
if len(raw)>64000:raise ValueError('Application profile exceeds its byte boundary')
|
|
85
|
+
def unique(pairs):
|
|
86
|
+
result={}
|
|
87
|
+
for key,value in pairs:
|
|
88
|
+
if key in result:raise ValueError('Duplicate application profile field')
|
|
89
|
+
result[key]=value
|
|
90
|
+
return result
|
|
91
|
+
return ApplicationProfile(json.loads(raw.decode('utf-8'),object_pairs_hook=unique))
|