@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,160 @@
|
|
|
1
|
+
import {approvalProfiles} from './approval-applications.mjs';
|
|
2
|
+
import {validateArchitectureWork} from './architecture-work.mjs';
|
|
3
|
+
import {validateStateCustody} from './state-custody.mjs';
|
|
4
|
+
import {validateExternalReferences} from './external-identities.mjs';
|
|
5
|
+
// The durable project file: atforma.project 2.0.0. See specs/002-project-model.
|
|
6
|
+
import { validateImport } from "./core.mjs";
|
|
7
|
+
|
|
8
|
+
export const FORMAT = "atmem.ai-architect.project";
|
|
9
|
+
export const FORMAT_VERSION = "2.3.0";
|
|
10
|
+
export const APPROVAL_FORMAT_VERSION = "3.0.0";
|
|
11
|
+
const approvalFormat=nodes=>Array.isArray(nodes)&&nodes.some(n=>n.data?.implementation==='approval-workflow'||n.data?.approvalProfile!=null);
|
|
12
|
+
export const MAX_FILE_BYTES = 2_000_000;
|
|
13
|
+
const GENERATOR = "atmem-ai-architect 0.2.0";
|
|
14
|
+
|
|
15
|
+
// Section keys the 2.0 reader understands; anything else is kept in extensions.
|
|
16
|
+
const KNOWN_TOP = ["format", "formatVersion", "meta", "design", "discovery", "requirements", "assumptions", "openQuestions", "decisions", "options", "estimate", "review", "extensions"];
|
|
17
|
+
const KNOWN = {
|
|
18
|
+
meta: ["id", "name", "createdAt", "updatedAt", "revision", "generator", "domain"],
|
|
19
|
+
design: ["provider", "nodes", "edges"],
|
|
20
|
+
discovery: ["problem", "brief", "useCases", "gate", "facts", "workflow", "asked", "lastAsked", "sketchOffered"],
|
|
21
|
+
review: ["accepted"],
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const SECRET_VALUE = [
|
|
25
|
+
/AKIA[0-9A-Z]{16}/,
|
|
26
|
+
/sk-ant-[A-Za-z0-9_-]{20,}/,
|
|
27
|
+
/sk-[A-Za-z0-9_-]{20,}/,
|
|
28
|
+
/AIza[0-9A-Za-z_-]{35}/,
|
|
29
|
+
/ghp_[A-Za-z0-9]{36}/,
|
|
30
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----/,
|
|
31
|
+
];
|
|
32
|
+
const SECRET_KEY = /^(password|secret|apiKey|api_key|accessKey|access_key|token|clientSecret|client_secret)$/i;
|
|
33
|
+
|
|
34
|
+
// Paths of values that look like credentials.
|
|
35
|
+
export function findCredentials(value, path = "") {
|
|
36
|
+
if (typeof value === "string") return SECRET_VALUE.some((re) => re.test(value)) ? [path || "(root)"] : [];
|
|
37
|
+
if (Array.isArray(value)) return value.flatMap((v, i) => findCredentials(v, `${path}[${i}]`));
|
|
38
|
+
if (value && typeof value === "object")
|
|
39
|
+
return Object.entries(value).flatMap(([k, v]) => {
|
|
40
|
+
const at = path ? `${path}.${k}` : k;
|
|
41
|
+
if (SECRET_KEY.test(k) && typeof v === "string" && v.trim()) return [at];
|
|
42
|
+
return findCredentials(v, at);
|
|
43
|
+
});
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function refuseCredentials(value, verb) {
|
|
48
|
+
const found = findCredentials(value);
|
|
49
|
+
if (found.length)
|
|
50
|
+
throw Error(`${verb} refused: ${found.slice(0, 3).join(", ")}${found.length > 3 ? "…" : ""} looks like a credential. Remove it and try again.`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function unknown(section, allowed) {
|
|
54
|
+
return Object.fromEntries(Object.entries(section || {}).filter(([k]) => !allowed.includes(k)));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function toProjectFile(project, now = new Date()) {
|
|
58
|
+
validateStateCustody(project.extensions?.terraformState);
|
|
59
|
+
validateExternalReferences(project);
|
|
60
|
+
approvalProfiles(project);
|
|
61
|
+
const {
|
|
62
|
+
version, revision, name, provider, domain, requirements, nodes, edges,
|
|
63
|
+
brief, useCases, gate, needs, assumptions, openQuestions, decisions, assessment,
|
|
64
|
+
facts, workflow, asked, lastAsked, sketchOffered, estimate, reviewAccepted,
|
|
65
|
+
id, createdAt, extensions, importReport, ...rest
|
|
66
|
+
} = project;
|
|
67
|
+
const file = {
|
|
68
|
+
format: FORMAT,
|
|
69
|
+
formatVersion: approvalFormat(nodes)?APPROVAL_FORMAT_VERSION:FORMAT_VERSION,
|
|
70
|
+
meta: {
|
|
71
|
+
id: id || crypto.randomUUID(),
|
|
72
|
+
name,
|
|
73
|
+
createdAt: createdAt || now.toISOString(),
|
|
74
|
+
updatedAt: now.toISOString(),
|
|
75
|
+
revision,
|
|
76
|
+
generator: GENERATOR,
|
|
77
|
+
...(domain ? { domain } : {}),
|
|
78
|
+
},
|
|
79
|
+
design: { provider: provider || "tools", nodes, edges },
|
|
80
|
+
discovery: {
|
|
81
|
+
problem: requirements || "",
|
|
82
|
+
...(brief ? { brief } : {}),
|
|
83
|
+
...(useCases ? { useCases } : {}),
|
|
84
|
+
...(gate ? { gate } : {}),
|
|
85
|
+
...(facts ? { facts } : {}),
|
|
86
|
+
...(workflow ? { workflow } : {}),
|
|
87
|
+
...(asked ? { asked } : {}),
|
|
88
|
+
...(lastAsked ? { lastAsked } : {}),
|
|
89
|
+
...(sketchOffered ? { sketchOffered } : {}),
|
|
90
|
+
},
|
|
91
|
+
requirements: needs || [],
|
|
92
|
+
assumptions: assumptions || [],
|
|
93
|
+
openQuestions: openQuestions || [],
|
|
94
|
+
decisions: decisions || [],
|
|
95
|
+
...(assessment ? { options: assessment } : {}),
|
|
96
|
+
...(estimate ? { estimate } : {}),
|
|
97
|
+
...(reviewAccepted ? { review: { accepted: reviewAccepted } } : {}),
|
|
98
|
+
// Browser-only fields the file format doesn't name yet are preserved, not dropped.
|
|
99
|
+
extensions: { ...(extensions || {}), ...rest },
|
|
100
|
+
};
|
|
101
|
+
if(file.extensions.architectureWork!==undefined)validateArchitectureWork(file.extensions.architectureWork);
|
|
102
|
+
refuseCredentials(file, "Export");
|
|
103
|
+
if(new TextEncoder().encode(JSON.stringify(file)).length>MAX_FILE_BYTES)throw Error("Project files must be 2 MB or smaller; no content was truncated.");
|
|
104
|
+
return file;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Returns the browser project plus what happened, so the UI can say so.
|
|
108
|
+
export function fromProjectFile(raw) {
|
|
109
|
+
if (!raw || typeof raw !== "object") throw Error("Not a project file.");
|
|
110
|
+
refuseCredentials(raw, "Import");
|
|
111
|
+
if (raw.format === undefined && raw.version === 1)
|
|
112
|
+
return { project: validateImport(raw), migrated: true, from: "workbench v1" };
|
|
113
|
+
if (![FORMAT,"atforma.project"].includes(raw.format)) throw Error("Not an AtMem AI Architect project file.");
|
|
114
|
+
const [major] = String(raw.formatVersion || "").split(".").map(Number);
|
|
115
|
+
if (major !== 2 && !(raw.formatVersion===APPROVAL_FORMAT_VERSION&&approvalFormat(raw.design?.nodes)))
|
|
116
|
+
throw Error(`This file is format ${raw.formatVersion}; this version of AtMem AI Architect reads ${FORMAT_VERSION}. Update AtMem AI Architect to open it.`);
|
|
117
|
+
const meta = raw.meta || {},
|
|
118
|
+
design = raw.design || {},
|
|
119
|
+
discovery = raw.discovery || {};
|
|
120
|
+
const extensions = {
|
|
121
|
+
...(raw.extensions || {}),
|
|
122
|
+
...unknown(raw, KNOWN_TOP),
|
|
123
|
+
...Object.fromEntries(
|
|
124
|
+
Object.entries(KNOWN)
|
|
125
|
+
.map(([section, allowed]) => [section, unknown(raw[section], allowed)])
|
|
126
|
+
.filter(([, extra]) => Object.keys(extra).length)
|
|
127
|
+
.map(([section, extra]) => [`${section}.extra`, extra]),
|
|
128
|
+
),
|
|
129
|
+
};
|
|
130
|
+
const project = validateImport({
|
|
131
|
+
version: 1,
|
|
132
|
+
revision: meta.revision,
|
|
133
|
+
name: meta.name,
|
|
134
|
+
provider: design.provider,
|
|
135
|
+
...(meta.domain ? { domain: meta.domain } : {}),
|
|
136
|
+
id: meta.id,
|
|
137
|
+
createdAt: meta.createdAt,
|
|
138
|
+
requirements: discovery.problem || "",
|
|
139
|
+
nodes: design.nodes,
|
|
140
|
+
edges: design.edges,
|
|
141
|
+
...(discovery.brief ? { brief: discovery.brief } : {}),
|
|
142
|
+
...(discovery.useCases ? { useCases: discovery.useCases } : {}),
|
|
143
|
+
...(discovery.gate ? { gate: discovery.gate } : {}),
|
|
144
|
+
...Object.fromEntries(["facts", "workflow", "asked", "lastAsked", "sketchOffered"].filter((k) => discovery[k] !== undefined).map((k) => [k, discovery[k]])),
|
|
145
|
+
...(raw.estimate ? { estimate: raw.estimate } : {}),
|
|
146
|
+
...(raw.review?.accepted ? { reviewAccepted: raw.review.accepted } : {}),
|
|
147
|
+
needs: raw.requirements || [],
|
|
148
|
+
assumptions: raw.assumptions || [],
|
|
149
|
+
openQuestions: raw.openQuestions || [],
|
|
150
|
+
decisions: raw.decisions || [],
|
|
151
|
+
...(raw.options ? { assessment: raw.options } : {}),
|
|
152
|
+
...(Object.keys(extensions).length ? { extensions } : {}),
|
|
153
|
+
});
|
|
154
|
+
return { project, migrated: raw.format !== FORMAT || raw.formatVersion !== (approvalFormat(project.nodes)?APPROVAL_FORMAT_VERSION:FORMAT_VERSION), from: raw.formatVersion };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function parseProjectFile(textContent) {
|
|
158
|
+
if (new TextEncoder().encode(textContent).length > MAX_FILE_BYTES) throw Error("Project files must be 2 MB or smaller.");
|
|
159
|
+
return fromProjectFile(JSON.parse(textContent));
|
|
160
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const definitions={
|
|
2
|
+
s3:{type:'s3',settings:{encrypt:true,use_lockfile:true},example:'bucket = "REVIEWED_PROTECTED_STATE_BUCKET"\nkey = "REVIEWED_PROJECT/production.tfstate"\nregion = "REVIEWED_STATE_REGION"\n',guide:'Use a separate private, encrypted, versioned S3 state bucket under the accepted custody/residency boundary. Terraform 1.17 uses native S3 lock files (use_lockfile=true), not an invented DynamoDB dependency. Restrict ListBucket to the project prefix and GetObject/PutObject to its state key; the corresponding .tflock also needs DeleteObject. Do not grant state DeleteObject merely for locking. Use the standard authorized AWS credential chain, never backend access_key/secret_key values.',source:'https://developer.hashicorp.com/terraform/language/backend/s3'},
|
|
3
|
+
azurerm:{type:'azurerm',settings:{use_azuread_auth:true},example:'storage_account_name = "REVIEWED_PRIVATE_STATE_ACCOUNT"\ncontainer_name = "REVIEWED_STATE_CONTAINER"\nkey = "REVIEWED_PROJECT/production.tfstate"\ntenant_id = "REVIEWED_DIRECTORY_TENANT_UUID"\nsubscription_id = "REVIEWED_STATE_SUBSCRIPTION_UUID"\n',guide:'Use a separate reviewed private Azure Blob state container with versioning/recovery and the accepted residency. Native Blob leases implement state locking. use_azuread_auth=true uses Entra data-plane authentication; grant only reviewed container-scoped state access. Configure the approved Azure CLI, managed identity or workload federation through its documented environment chain; do not export storage account keys, SAS tokens or client secrets into backend.hcl. Verify the exact state subscription/tenant and effective inherited rights independently.',source:'https://developer.hashicorp.com/terraform/language/backend/azurerm'},
|
|
4
|
+
gcs:{type:'gcs',settings:{},example:'bucket = "REVIEWED_PRIVATE_VERSIONED_STATE_BUCKET"\nprefix = "REVIEWED_PROJECT/production"\n',guide:'Use a separately bootstrapped private, versioned GCS state bucket under the reviewed project/custody/residency boundary. Terraform supports native state locking. Give the authorized state operator scoped object access, preserving unrelated bucket IAM; use the reviewed Google credential/workload-identity chain, never an embedded service-account key. Review encryption/key ownership, inherited grants, version recovery and the exact project/bucket/prefix. The state bucket is not the invoice originals bucket.',source:'https://developer.hashicorp.com/terraform/language/backend/gcs'},
|
|
5
|
+
http:{type:'http',settings:{},example:'address = "https://REVIEWED_PRIVATE_STATE_SERVICE/EXACT_PROJECT_PRODUCTION_STATE"\nlock_address = "https://REVIEWED_PRIVATE_STATE_SERVICE/EXACT_PROJECT_PRODUCTION_STATE/lock"\nunlock_address = "https://REVIEWED_PRIVATE_STATE_SERVICE/EXACT_PROJECT_PRODUCTION_STATE/lock"\nlock_method = "POST"\nunlock_method = "DELETE"\nretry_wait_min = 5\n',guide:'Use the existing reviewed private platform state service implementing Terraform HTTP state and lock/unlock semantics, not a generic object URL or an unverified S3-compatible IBM COS lock assumption. The example selects POST/DELETE locking; verify the actual platform protocol and owner-safe lock behavior before use. Keep the service in the accepted custody/residency boundary. Configure TF_HTTP_USERNAME/TF_HTTP_PASSWORD or reviewed mutual-TLS environment credentials, never credentials in backend.hcl. No state platform is deployed by this module.',source:'https://developer.hashicorp.com/terraform/language/backend/http'}
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const PROTECTED_BACKENDS=Object.freeze(Object.keys(definitions));
|
|
9
|
+
export function protectedState(backendType,{title='Terraform state and environment ownership',environment='production'}={}){
|
|
10
|
+
const definition=definitions[backendType];if(!definition)throw Error('Choose an explicit supported state backend; no target-provider fallback.');
|
|
11
|
+
if(!['staging','production','qualification'].includes(environment))throw Error('Use an explicit supported state environment.');
|
|
12
|
+
return {backend:{[definition.type]:structuredClone(definition.settings)},files:{'backend.hcl.example':definition.example.replaceAll('production',environment).replaceAll('PRODUCTION',environment.toUpperCase()),'STATE.md':'# '+title+'\n\nSelected backend contract: '+definition.type+'.\n\n'+definition.guide.replace('invoice originals bucket','application data bucket')+'\n\n'+COMMON+'\nReference: '+definition.source+'\n'}};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const COMMON=`Staging/qualification default to local state only for isolated local validation.
|
|
16
|
+
Production declares the provider-specific protected backend with no account names,
|
|
17
|
+
endpoints or credentials embedded. The existing state store is a separate platform
|
|
18
|
+
prerequisite, not an application resource. If the destination repository
|
|
19
|
+
already has another reviewed backend, preserve it through an explicit state/owner
|
|
20
|
+
decision and validate compatibility before any authorized migration.
|
|
21
|
+
|
|
22
|
+
Keep backend.hcl private and excluded from source. Do not put authentication values
|
|
23
|
+
in configuration or command arguments: Terraform can retain backend arguments in
|
|
24
|
+
its local metadata and saved plans. State can contain provider-computed identity
|
|
25
|
+
credentials even when runtime SQL/accounting secret payloads are supplied outside
|
|
26
|
+
Terraform. Restrict readers, protect backups/plans, and review retention/key policy.
|
|
27
|
+
Never disable TLS verification or use -lock=false to bypass an unavailable lock.
|
|
28
|
+
|
|
29
|
+
Local-only checks: terraform init -backend=false, terraform validate, terraform test.
|
|
30
|
+
Before authorized production planning, verify the existing protected state store
|
|
31
|
+
on an isolated approved state: concurrent clients must conflict on one lock, an
|
|
32
|
+
unrelated identity must not unlock it, and version recovery must preserve the
|
|
33
|
+
expected digest. Record actual service version, access, custody and recovery proof.
|
|
34
|
+
Then run terraform init -backend-config=backend.hcl through the approved credential
|
|
35
|
+
chain. Platform/account access, store creation and migrations require authorization.
|
|
36
|
+
|
|
37
|
+
Each deployment environment has a distinct physical resource-name seed. Use separate
|
|
38
|
+
staging/production state paths and reviewed immutable image/account/policy inputs;
|
|
39
|
+
never point production at staging state. Keep a verified private state backup before
|
|
40
|
+
an authorized migration and inspect exact lineage. Do not -reconfigure/-migrate-state
|
|
41
|
+
over a mismatched lineage. No external accounting service or actor is imported or
|
|
42
|
+
recreated. Resource deletion protections do not prove backup, restore or availability.
|
|
43
|
+
`;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {publicFaqFiles} from './public-faq.mjs';
|
|
2
|
+
import {existingReferenceFiles} from './existing-references.mjs';
|
|
3
|
+
export const v=n=>'${var.'+n+'}',ref=(type,n,key)=>'${'+type+'.'+n+'.'+key+'}';
|
|
4
|
+
export const UUID='^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$';
|
|
5
|
+
export function builder(project,plan,environment,provider){
|
|
6
|
+
if(!plan.publicFaqPlan?.entries.length||plan.publicFaqPlan.entries.some(e=>e.provider!==provider))throw Error('A public FAQ adapter must match every proposed application provider; no cross-cloud substitution.');
|
|
7
|
+
const variable={review_approved:{type:'bool',default:false}},examples={review_approved:false},resource={},output={},files={},data={},assertions=[];
|
|
8
|
+
const gaps=[...plan.gapDetails.map(g=>g.id),...(environment==='production'?['production-state-backup-and-operating-review-required']:[])];
|
|
9
|
+
resource.terraform_data={review_gate:{input:{design_revision:project.revision,environment},lifecycle:{precondition:[{condition:'${var.review_approved && length(local.unfinished_work) == 0}',error_message:'Review the source, public-only publication boundary, destination, native roles, model policy, costs and unresolved scope before deployment.'}]}}};
|
|
10
|
+
const input=(name,description,example,pattern)=>{variable[name]={type:'string',nullable:false,description,validation:{condition:pattern?'${can(regex('+JSON.stringify(pattern)+', var.'+name+'))}':'${length(trimspace(var.'+name+')) > 0}',error_message:'Supply the reviewed '+name+' value.'}};examples[name]=example;return v(name);};
|
|
11
|
+
const add=(type,name,body)=>((resource[type]||={})[name]={...body,depends_on:['terraform_data.review_gate',...(body.depends_on||[])]});
|
|
12
|
+
const source=row=>Object.assign(files,publicFaqFiles(provider,row.address));
|
|
13
|
+
function finish(config,lock,mock,guide){
|
|
14
|
+
const values=Object.entries({...examples,review_approved:true}).map(([name,value])=>name+' = '+JSON.stringify(value)).join('\n');
|
|
15
|
+
return {...files,...existingReferenceFiles(project),'.terraform.lock.hcl':lock,'main.tf.json':JSON.stringify({...config,variable,data,locals:{unfinished_work:gaps},resource,output},null,2),'resource-schedule.json':JSON.stringify(plan,null,2),'deployment.tfvars.json.example':JSON.stringify(examples,null,2),'.gitignore':'.terraform/\n*.tfstate*\n*.tfplan\n*.tfvars\n*.tfvars.json\n!deployment.tfvars.json.example\n.venv/\nbuild/\n*.zip\n__pycache__/\n','tests/public-faq.tftest.hcl':mock+'\nvariables {\n'+values+'\n}\n'+assertions.join('\n')+'\nrun "unapproved_blocked" {\n command = plan\n variables { review_approved = false }\n expect_failures = [terraform_data.review_gate]\n}\n','README.md':guide};
|
|
16
|
+
}
|
|
17
|
+
return {variable,examples,resource,output,files,data,assertions,input,add,source,finish};
|
|
18
|
+
}
|
|
19
|
+
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import {ibmRegistryHostExpression,ibmRegistryRegionValidation}from'./ibm-registry.mjs';
|
|
2
|
+
import {azurePlanMocks} from './azure-plan-mocks.mjs';
|
|
3
|
+
import {CLOUD_PROVIDER_LOCKS} from './cloud-provider-locks.mjs';
|
|
4
|
+
export {awsPublicFaqTerraform} from './aws-public-faq.mjs';
|
|
5
|
+
import {builder,v,ref,UUID} from './public-faq-builder.mjs';
|
|
6
|
+
|
|
7
|
+
export function azurePublicFaqTerraform(project,plan,environment='staging'){
|
|
8
|
+
const b=builder(project,plan,environment,'azure'),{input,add}=b;
|
|
9
|
+
const subscription=input('subscription_id','Expected destination Azure subscription; operator credential chain only.','11111111-1111-1111-1111-111111111111',UUID);
|
|
10
|
+
const tenant=input('tenant_id','Expected destination tenant. Existing editor identity is preserved.','22222222-2222-2222-2222-222222222222',UUID);
|
|
11
|
+
const rg=input('resource_group_name','Existing reviewed resource group; not recreated or imported.','reviewed-applications','^[A-Za-z0-9_.()-]{1,90}$');
|
|
12
|
+
const registry=input('registry_name','Existing RBAC-mode ACR that accepts the ARM audience for managed identity.','reviewedregistry','^[a-z0-9]{5,50}$');
|
|
13
|
+
const registryRg=input('registry_resource_group_name','Existing registry resource group in this subscription.','reviewed-registry-rg','^[A-Za-z0-9_.()-]{1,90}$');
|
|
14
|
+
b.data.azurerm_client_config={destination:{}};
|
|
15
|
+
b.resource.terraform_data.review_gate.lifecycle.precondition.push({condition:'${data.azurerm_client_config.destination.subscription_id == var.subscription_id && data.azurerm_client_config.destination.tenant_id == var.tenant_id}',error_message:'Operator identity must match the reviewed tenant and subscription.'});
|
|
16
|
+
const registryId='/subscriptions/'+subscription+'/resourceGroups/'+registryRg+'/providers/Microsoft.ContainerRegistry/registries/'+registry;
|
|
17
|
+
for(const {row}of plan.publicFaqPlan.entries){
|
|
18
|
+
const n=row.address,name=input(n+'_name','Unique public application name; internal managed services belong to this composition.','reviewed-public-help','^[a-z][a-z0-9-]{3,18}[a-z0-9]$');
|
|
19
|
+
const region=input(n+'_region','Reviewed application, storage and Regional Standard model location. Verify actual model/version/quotas and processing geography.','australiaeast','^[a-z][a-z0-9]{2,39}$');
|
|
20
|
+
if(/^[a-z][a-z0-9]{2,39}$/.test(row.region)){b.variable[n+'_region'].default=row.region;b.examples[n+'_region']=row.region;}
|
|
21
|
+
const repository=input(n+'_repository','Build/publish path for this emitted application in the existing registry.','architect/public-help','^[a-z0-9]+(?:[._/-][a-z0-9]+)*$');
|
|
22
|
+
const digest=input(n+'_image_digest','Exact built, scanned, tested and authorized-published image digest.','sha256:'+'0'.repeat(64),'^sha256:[0-9a-f]{64}$');
|
|
23
|
+
const model=input(n+'_model_name','Reviewed regional model supporting strict JSON Schema output; no availability or price is invented.','REVIEW_MODEL');
|
|
24
|
+
const version=input(n+'_model_version','Exact supported model version; verify lifecycle and Standard regional availability.','REVIEW_VERSION');
|
|
25
|
+
const editor=input(n+'_editor_group_object_id','Existing Entra editor security-group object ID; no group/users are created. It receives only public-container data access.','33333333-3333-3333-3333-333333333333',UUID);
|
|
26
|
+
const tags={ManagedBy:'AtMem AI Architect',Environment:environment,Component:n};
|
|
27
|
+
add('azurerm_user_assigned_identity',n,{name:name+'-runtime',location:region,resource_group_name:rg,tags});
|
|
28
|
+
add('azurerm_service_plan',n,{name:name+'-plan',location:region,resource_group_name:rg,os_type:'Linux',sku_name:'B1',worker_count:1,tags});
|
|
29
|
+
add('azurerm_cognitive_account',n,{name:name+'-ai',location:region,resource_group_name:rg,kind:'OpenAI',sku_name:'S0',custom_subdomain_name:name+'-ai',local_auth_enabled:false,public_network_access_enabled:true,tags,lifecycle:{prevent_destroy:true}});
|
|
30
|
+
add('azurerm_cognitive_deployment',n,{name:'public-faq',cognitive_account_id:ref('azurerm_cognitive_account',n,'id'),version_upgrade_option:'NoAutoUpgrade',model:{format:'OpenAI',name:model,version},sku:{name:'Standard',capacity:1}});
|
|
31
|
+
const storage='${format("faq%s", substr(sha256(format("%s:%s", var.subscription_id, var.'+n+'_name)), 0, 20))}';
|
|
32
|
+
add('azurerm_storage_account',n,{name:storage,location:region,resource_group_name:rg,account_tier:'Standard',account_replication_type:'LRS',https_traffic_only_enabled:true,min_tls_version:'TLS1_2',allow_nested_items_to_be_public:false,shared_access_key_enabled:false,default_to_oauth_authentication:true,public_network_access_enabled:true,blob_properties:{versioning_enabled:true,delete_retention_policy:{days:7},container_delete_retention_policy:{days:7}},tags,lifecycle:{prevent_destroy:true}});
|
|
33
|
+
add('azurerm_storage_container',n,{name:'public-catalog',storage_account_id:ref('azurerm_storage_account',n,'id'),container_access_type:'private',lifecycle:{prevent_destroy:true}});
|
|
34
|
+
const principal=ref('azurerm_user_assigned_identity',n,'principal_id');
|
|
35
|
+
for(const [suffix,scope,role]of [['',ref('azurerm_cognitive_account',n,'id'),'Cognitive Services OpenAI User'],['_policy',ref('azurerm_cognitive_account',n,'id'),'Reader'],['_pull',registryId,'AcrPull'],['_catalog',ref('azurerm_storage_container',n,'id'),'Storage Blob Data Reader']])add('azurerm_role_assignment',n+suffix,{scope,role_definition_name:role,principal_id:principal,principal_type:'ServicePrincipal',skip_service_principal_aad_check:true});
|
|
36
|
+
add('azurerm_role_assignment',n+'_editor',{scope:ref('azurerm_storage_container',n,'id'),role_definition_name:'Storage Blob Data Contributor',principal_id:editor,principal_type:'Group'});
|
|
37
|
+
add('azurerm_linux_web_app',n,{name,location:region,resource_group_name:rg,service_plan_id:ref('azurerm_service_plan',n,'id'),https_only:true,ftp_publish_basic_authentication_enabled:false,webdeploy_publish_basic_authentication_enabled:false,identity:{type:'UserAssigned',identity_ids:[ref('azurerm_user_assigned_identity',n,'id')]},site_config:{always_on:true,minimum_tls_version:'1.2',scm_minimum_tls_version:'1.2',ftps_state:'Disabled',http2_enabled:true,remote_debugging_enabled:false,health_check_path:'/healthz',health_check_eviction_time_in_min:2,container_registry_use_managed_identity:true,container_registry_managed_identity_client_id:ref('azurerm_user_assigned_identity',n,'client_id'),application_stack:{docker_registry_url:'https://'+registry+'.azurecr.io',docker_image_name:repository+'@'+digest}},app_settings:{WEBSITES_PORT:'8080',MANAGED_IDENTITY_CLIENT_ID:ref('azurerm_user_assigned_identity',n,'client_id'),MODEL_RESOURCE_ID:ref('azurerm_cognitive_account',n,'id'),MODEL_ENDPOINT:ref('azurerm_cognitive_account',n,'endpoint'),MODEL_DEPLOYMENT:ref('azurerm_cognitive_deployment',n,'name'),MAX_OUTPUT_TOKENS:'1024',CATALOG_ACCOUNT:ref('azurerm_storage_account',n,'name'),CATALOG_CONTAINER:ref('azurerm_storage_container',n,'name')},tags,depends_on:['azurerm_role_assignment.'+n,'azurerm_role_assignment.'+n+'_policy','azurerm_role_assignment.'+n+'_pull','azurerm_role_assignment.'+n+'_catalog']});
|
|
38
|
+
b.output[n+'_url']={value:'https://'+ref('azurerm_linux_web_app',n,'default_hostname')+'/'};
|
|
39
|
+
b.output[n+'_catalog']={value:{storage_account:ref('azurerm_storage_account',n,'name'),container:ref('azurerm_storage_container',n,'name'),region}};
|
|
40
|
+
b.source(row);
|
|
41
|
+
b.assertions.push(`run "${n}_public_boundary" {\n command = plan\n assert {\n condition = azurerm_storage_container.${n}.container_access_type == "private" && !azurerm_storage_account.${n}.shared_access_key_enabled && !azurerm_storage_account.${n}.allow_nested_items_to_be_public\n error_message = "Public answers must not make the object store public or enable storage keys."\n }\n assert {\n condition = azurerm_role_assignment.${n}_catalog.role_definition_name == "Storage Blob Data Reader" && azurerm_role_assignment.${n}_editor.role_definition_name == "Storage Blob Data Contributor" && azurerm_role_assignment.${n}_catalog.principal_id != azurerm_role_assignment.${n}_editor.principal_id\n error_message = "Runtime read and authorized editor write identities must remain separate."\n }\n assert {\n condition = azurerm_linux_web_app.${n}.https_only && !azurerm_cognitive_account.${n}.local_auth_enabled\n error_message = "TLS and workload identity are required."\n }\n}\nrun "${n}_mutable_image_blocked" {\n command = plan\n variables { ${n}_image_digest = "latest" }\n expect_failures = [var.${n}_image_digest]\n}`);
|
|
42
|
+
}
|
|
43
|
+
const config={terraform:{required_version:'>= 1.17.1, < 2.0.0',required_providers:{azurerm:{source:'hashicorp/azurerm',version:'4.81.0'}}},provider:{azurerm:{subscription_id:subscription,tenant_id:tenant,features:{},storage_use_azuread:true}}};
|
|
44
|
+
return b.finish(config,CLOUD_PROVIDER_LOCKS.azure,azurePlanMocks({subscription:b.examples.subscription_id,tenant:b.examples.tenant_id,resourceGroup:b.examples.resource_group_name,addresses:plan.publicFaqPlan.entries.map(e=>e.row.address)}),AZURE_PUBLIC_FAQ_GUIDE);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const AZURE_PUBLIC_FAQ_GUIDE=`# Azure public-only FAQ composition
|
|
48
|
+
|
|
49
|
+
The generated Python browser/API is intentionally anonymous. Its dedicated managed
|
|
50
|
+
identity can read only this private public-article container and invoke/read policy
|
|
51
|
+
on its generated regional model account. A separate existing Entra editor group
|
|
52
|
+
receives container-scoped data contributor access. No application HTTP endpoint
|
|
53
|
+
edits articles. No users/groups, existing resource group or ACR are recreated.
|
|
54
|
+
This is not a private enterprise knowledge assistant or existing-SSO integration.
|
|
55
|
+
|
|
56
|
+
Shared storage keys and anonymous blob access are disabled; the Azure provider
|
|
57
|
+
uses Entra for storage access. App Service basic publishing/debug/FTP are disabled;
|
|
58
|
+
the application uses managed identity for image pulls. Verify the existing registry
|
|
59
|
+
uses standard RBAC and accepts the ARM audience. No registry credential or model
|
|
60
|
+
key is exported. Public TLS endpoints use IAM/RBAC; private networking is not
|
|
61
|
+
implemented. Review inherited permissions and current tenant/subscription context.
|
|
62
|
+
|
|
63
|
+
See applications/*/BUILD.md for exact source/test/build and conditional editor CLI
|
|
64
|
+
steps. Protect Terraform state and variables. Seed only genuinely public articles
|
|
65
|
+
with the authorized editor before admitting traffic; a missing/inaccessible source
|
|
66
|
+
does not become an empty catalog or a cached answer. Blob versions and seven-day
|
|
67
|
+
soft deletion are recovery features, not an immediate erasure guarantee. Review
|
|
68
|
+
recovery and costs. Anonymous access plus concurrency/instance/output limits is
|
|
69
|
+
not an invoice cap; approve and test destination abuse controls and budget policy.
|
|
70
|
+
|
|
71
|
+
Choose a compatible regional Standard structured-output model/version and verify
|
|
72
|
+
actual quotas, prices and geography. Runtime requires approved ContentLogging=false
|
|
73
|
+
before inference and never writes that account-wide setting. Provider/platform
|
|
74
|
+
logging and identity/control-plane processing require separate review. No app
|
|
75
|
+
question/answer persistence or content-bearing application logs are implemented.
|
|
76
|
+
Real model answer/citation/abstention and latency tests remain required.
|
|
77
|
+
|
|
78
|
+
Use Terraform 1.17.1+, terraform init -backend=false, terraform validate and
|
|
79
|
+
terraform test. These mock checks do not prove real Azure IAM, managed identity,
|
|
80
|
+
storage conditionals or model quality. Obtain explicit authorization for registry
|
|
81
|
+
publication, cloud access, a real account plan and deployment. Production state,
|
|
82
|
+
backup and operating review remain gated; no export proves deployment.
|
|
83
|
+
`;
|
|
84
|
+
|
|
85
|
+
export function gcpPublicFaqTerraform(project,plan,environment='staging'){
|
|
86
|
+
const b=builder(project,plan,environment,'gcp'),{input,add}=b;
|
|
87
|
+
const projectId=input('project_id','Existing reviewed Google project with billing; no project is recreated.','reviewed-project','^[a-z][a-z0-9-]{4,28}[a-z0-9]$');
|
|
88
|
+
const registryLocation=input('registry_location','Location of the existing Artifact Registry Docker repository.','australia-southeast1','^[a-z]+(?:-[a-z]+[0-9])?$');
|
|
89
|
+
const registry=input('registry_repository','Existing Docker repository in this project; preserved.','reviewed-images','^[a-z][a-z0-9-]{1,61}[a-z0-9]$');
|
|
90
|
+
b.data.google_project={destination:{project_id:projectId}};
|
|
91
|
+
for(const [name,service]of Object.entries({run:'run.googleapis.com',model:'aiplatform.googleapis.com',storage:'storage.googleapis.com',registry:'artifactregistry.googleapis.com'}))add('google_project_service','required_'+name,{project:projectId,service,disable_on_destroy:false});
|
|
92
|
+
add('google_project_service_identity','run',{provider:'google-beta',project:projectId,service:'run.googleapis.com',depends_on:['google_project_service.required_run']});
|
|
93
|
+
add('google_artifact_registry_repository_iam_member','image_pull',{project:projectId,location:registryLocation,repository:registry,role:'roles/artifactregistry.reader',member:ref('google_project_service_identity','run','member'),depends_on:['google_project_service.required_registry']});
|
|
94
|
+
const number='${data.google_project.destination.number}';
|
|
95
|
+
for(const {row}of plan.publicFaqPlan.entries){
|
|
96
|
+
const n=row.address,name=input(n+'_name','Unique short Cloud Run/runtime name. The generated deterministic URL is used for this public browser.','reviewed-public-help','^[a-z][a-z0-9-]{3,18}[a-z0-9]$');
|
|
97
|
+
const region=input(n+'_region','Reviewed single-region application, catalog and model location; no global model fallback.','australia-southeast1','^[a-z]+-[a-z]+[0-9]$');
|
|
98
|
+
if(/^[a-z]+-[a-z]+[0-9]$/.test(row.region)){b.variable[n+'_region'].default=row.region;b.examples[n+'_region']=row.region;}
|
|
99
|
+
const repository=input(n+'_image_repository','Path for the emitted image in the existing registry.','architect/public-help','^[a-z0-9]+(?:[._/-][a-z0-9]+)*$');
|
|
100
|
+
const digest=input(n+'_image_digest','Built, scanned, tested and authorized-published emitted image digest.','sha256:'+'0'.repeat(64),'^sha256:[0-9a-f]{64}$');
|
|
101
|
+
const model=input(n+'_model_id','Compatible reviewed regional Gemini model/version with structured JSON output; verify lifecycle, quota, context size and prices.','gemini-reviewed-version','^gemini-[a-z0-9.-]{1,80}$');
|
|
102
|
+
const editor=input(n+'_editor_group','Existing authorized Google editor group email. No users/groups are created.','help-editors@example.test','^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$');
|
|
103
|
+
const review=n+'_policy_review';b.variable[review]={type:'object({ projectId = string, region = string, model = string, abuseLogging = string, approvedBy = string, evidenceSha256 = string, expiresAt = string })',nullable:false,description:'Scoped expiring destination-owner review of actual abuse-monitoring terms/exemption. Operator attestation, not a fabricated approval API; runtime separately reads caching/model logging/sharing settings.'};b.examples[review]={projectId:'reviewed-project',region:b.examples[n+'_region'],model:'gemini-reviewed-version',abuseLogging:'REVIEW_REQUIRED',approvedBy:'REVIEW_REQUIRED',evidenceSha256:'0'.repeat(64),expiresAt:'REPLACE_AFTER_REVIEW'};
|
|
104
|
+
add('google_service_account',n,{project:projectId,account_id:name,display_name:'Public FAQ runtime; no catalog edit permission'});
|
|
105
|
+
const member='serviceAccount:'+ref('google_service_account',n,'email');
|
|
106
|
+
add('google_project_iam_custom_role',n,{project:projectId,role_id:'${replace(var.'+n+'_name, "-", "_")}_model',title:'FAQ model invocation and read-only policy',permissions:['aiplatform.endpoints.predict','aiplatform.cacheConfigs.get','aiplatform.endpoints.fetchPublisherModelConfig'],stage:'GA'});
|
|
107
|
+
add('google_project_iam_member',n,{project:projectId,role:ref('google_project_iam_custom_role',n,'name'),member,depends_on:['google_project_service.required_model']});
|
|
108
|
+
add('google_storage_bucket',n,{project:projectId,name:projectId+'-'+name+'-public',location:region,storage_class:'STANDARD',uniform_bucket_level_access:true,public_access_prevention:'enforced',force_destroy:false,versioning:{enabled:true},soft_delete_policy:{retention_duration_seconds:604800},lifecycle_rule:{action:{type:'Delete'},condition:{days_since_noncurrent_time:7}},lifecycle:{prevent_destroy:true},depends_on:['google_project_service.required_storage']});
|
|
109
|
+
for(const [suffix,permissions]of [['_catalog',['storage.objects.get']],['_editor',['storage.objects.get','storage.objects.create','storage.objects.delete']]])add('google_project_iam_custom_role',n+suffix,{project:projectId,role_id:'${replace(var.'+n+'_name, "-", "_")}'+suffix,title:suffix==='_catalog'?'FAQ current catalog reader':'FAQ conditional catalog editor',permissions,stage:'GA'});
|
|
110
|
+
add('google_storage_bucket_iam_member',n,{bucket:ref('google_storage_bucket',n,'name'),role:ref('google_project_iam_custom_role',n+'_catalog','name'),member});
|
|
111
|
+
add('google_storage_bucket_iam_member',n+'_editor',{bucket:ref('google_storage_bucket',n,'name'),role:ref('google_project_iam_custom_role',n+'_editor','name'),member:'group:'+editor});
|
|
112
|
+
const origin='https://'+name+'-'+number+'.'+region+'.run.app',settings={PROJECT_ID:projectId,REGION:region,MODEL_ID:model,PUBLIC_ORIGIN:origin,MAX_OUTPUT_TOKENS:'1024',CATALOG_BUCKET:ref('google_storage_bucket',n,'name'),POLICY_REVIEW_BASE64:'${base64encode(jsonencode(var.'+review+'))}'};
|
|
113
|
+
add('google_cloud_run_v2_service',n,{project:projectId,name,location:region,deletion_protection:true,ingress:'INGRESS_TRAFFIC_ALL',invoker_iam_disabled:false,iap_enabled:false,labels:{managed_by:'atmem-ai-architect',environment},template:{service_account:ref('google_service_account',n,'email'),timeout:'30s',max_instance_request_concurrency:5,scaling:{min_instance_count:0,max_instance_count:2},containers:{name:'application',image:registryLocation+'-docker.pkg.dev/'+projectId+'/'+registry+'/'+repository+'@'+digest,ports:{container_port:8080},resources:{limits:{cpu:'1',memory:'512Mi'},cpu_idle:true,startup_cpu_boost:true},env:Object.entries(settings).map(([name,value])=>({name,value})),startup_probe:{http_get:{path:'/healthz',port:8080},initial_delay_seconds:0,period_seconds:5,timeout_seconds:2,failure_threshold:12}}},depends_on:['google_project_service.required_run','google_project_service_identity.run','google_artifact_registry_repository_iam_member.image_pull','google_project_iam_member.'+n,'google_storage_bucket_iam_member.'+n]});
|
|
114
|
+
// Anonymous invocation is intentional for this public-only application, never
|
|
115
|
+
// applied to a private module or the bucket itself. Org policy must allow it.
|
|
116
|
+
add('google_cloud_run_v2_service_iam_member',n,{project:projectId,location:region,name:ref('google_cloud_run_v2_service',n,'name'),role:'roles/run.invoker',member:'allUsers'});
|
|
117
|
+
b.output[n+'_url']={value:origin};b.output[n+'_catalog']={value:{bucket:ref('google_storage_bucket',n,'name'),region}};b.source(row);
|
|
118
|
+
b.assertions.push(`run "${n}_public_boundary" {\n command = plan\n assert {\n condition = google_storage_bucket.${n}.public_access_prevention == "enforced" && google_storage_bucket.${n}.uniform_bucket_level_access && !google_storage_bucket.${n}.force_destroy\n error_message = "Only the app is public; the catalog bucket stays private and protected."\n }\n assert {\n condition = google_cloud_run_v2_service_iam_member.${n}.member == "allUsers" && google_cloud_run_v2_service_iam_member.${n}.role == "roles/run.invoker" && !google_cloud_run_v2_service.${n}.iap_enabled\n error_message = "This explicit public-only application has anonymous invocation, not an invented end-user identity."\n }\n assert {\n condition = toset(google_project_iam_custom_role.${n}_catalog.permissions) == toset(["storage.objects.get"]) && google_storage_bucket_iam_member.${n}_editor.member == "group:\${var.${n}_editor_group}"\n error_message = "Runtime read and native editor write authority must remain separate."\n }\n}\nrun "${n}_mutable_image_blocked" {\n command = plan\n variables { ${n}_image_digest = "latest" }\n expect_failures = [var.${n}_image_digest]\n}`);
|
|
119
|
+
}
|
|
120
|
+
const config={terraform:{required_version:'>= 1.17.1, < 2.0.0',required_providers:{google:{source:'hashicorp/google',version:'7.46.2'},'google-beta':{source:'hashicorp/google-beta',version:'7.46.2'}}},provider:{google:{project:projectId},'google-beta':{project:projectId}}};
|
|
121
|
+
return b.finish(config,CLOUD_PROVIDER_LOCKS.gcp+'\n'+CLOUD_PROVIDER_LOCKS.gcpBeta,'mock_provider "google" {\n override_during = plan\n mock_data "google_project" { defaults = { number = "123456789012" } }\n mock_resource "google_service_account" { defaults = { email = "runtime@reviewed-project.iam.gserviceaccount.com" } }\n}\nmock_provider "google-beta" {\n override_during = plan\n mock_resource "google_project_service_identity" { defaults = { email = "service-123456789012@serverless-robot-prod.iam.gserviceaccount.com", member = "serviceAccount:service-123456789012@serverless-robot-prod.iam.gserviceaccount.com" } }\n}\n',GCP_PUBLIC_FAQ_GUIDE);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export const GCP_PUBLIC_FAQ_GUIDE=`# Google public-only FAQ composition
|
|
125
|
+
|
|
126
|
+
Only this explicitly selected public Cloud Run browser/API receives anonymous
|
|
127
|
+
run.invoker access. Its catalog bucket remains private with public access
|
|
128
|
+
prevention and uniform IAM. The dedicated runtime has a bucket-scoped custom
|
|
129
|
+
object-reader role; a separate existing company editor group has get/create/delete
|
|
130
|
+
only on that public catalog bucket. There is no HTTP editing route. No private
|
|
131
|
+
source, arbitrary URL fetch, user identity or enterprise authorization is implied.
|
|
132
|
+
|
|
133
|
+
Existing project, registry and editor group are preserved. Required APIs are never
|
|
134
|
+
disabled on destroy. The Cloud Run platform agent, not the application, receives
|
|
135
|
+
reader access on the exact existing image repository. The runtime has narrow
|
|
136
|
+
project-level prediction and read-only cache/model-policy access; no policy writes
|
|
137
|
+
or user-managed key is emitted. Google-beta is used only for the documented service
|
|
138
|
+
identity resource. Reconcile inherited IAM and organization restrictions on public
|
|
139
|
+
invocation; do not disable organization policy to bypass a failure.
|
|
140
|
+
|
|
141
|
+
See applications/*/BUILD.md. Seed genuinely public articles using the authorized
|
|
142
|
+
editor and native generation preconditions; updates/deletes never fall back to
|
|
143
|
+
stale or inaccessible evidence. Versioning, noncurrent-version expiry and seven-day
|
|
144
|
+
soft deletion support recovery and are not an immediate erasure guarantee. Runtime
|
|
145
|
+
never retrieves a historical generation selected by a visitor. No question/answer
|
|
146
|
+
persistence or content-bearing app logs are implemented; review platform logging.
|
|
147
|
+
|
|
148
|
+
The owner policy review is scoped/expiring operator-attested evidence, not an IBM,
|
|
149
|
+
Google or other fabricated provider approval API. Runtime separately requires
|
|
150
|
+
disabled project caching and disabled model request/response logging/sharing.
|
|
151
|
+
The documented global cache-control read sends no question text; review that
|
|
152
|
+
control-plane processing and actual model geography/terms against residency needs.
|
|
153
|
+
Model availability/context/quotas, prices, anonymous abuse controls, budgets and
|
|
154
|
+
real answer/citation/abstention/latency tests remain destination checks. Concurrency,
|
|
155
|
+
token and instance bounds are not an invoice cap or SLA.
|
|
156
|
+
|
|
157
|
+
Use Terraform 1.17.1+; run init -backend=false, validate and test. These native schema
|
|
158
|
+
and mock plans do not establish actual IAM, model or deployed behavior. Build, scan
|
|
159
|
+
and test the exact source/image; obtain authorization before publishing, cloud
|
|
160
|
+
access, real account plan or deployment. Production state/operations remain gated.
|
|
161
|
+
`;
|
|
162
|
+
|
|
163
|
+
export function ibmPublicFaqTerraform(project,plan,environment='staging'){
|
|
164
|
+
const b=builder(project,plan,environment,'ibm'),{input,add}=b;
|
|
165
|
+
const account=input('account_id','Existing destination IBM account; no account is created.','a'.repeat(32),'^[0-9a-f]{32}$');
|
|
166
|
+
const region=input('region','Reviewed regional Code Engine, COS and watsonx model destination.','au-syd','^[a-z]{2}-[a-z]+$');
|
|
167
|
+
const entries=plan.publicFaqPlan.entries;
|
|
168
|
+
if(entries.every(e=>e.row.region===entries[0].row.region)&&/^[a-z]{2}-[a-z]+$/.test(entries[0].row.region)){b.variable.region.default=entries[0].row.region;b.examples.region=entries[0].row.region;}
|
|
169
|
+
b.variable.region.validation=ibmRegistryRegionValidation();
|
|
170
|
+
const ceProject=input('code_engine_project_id','Existing reviewed Code Engine project; never recreate or import it.','11111111-1111-1111-1111-111111111111',UUID);
|
|
171
|
+
const registry=input('registry_namespace','Existing regional IBM Container Registry namespace.','reviewed-namespace','^[a-z0-9]+(?:[-_][a-z0-9]+)*$');
|
|
172
|
+
const pullSecret=input('registry_pull_secret','Existing registry-pull secret NAME in that project; no credential value is exported.','reviewed-registry-pull','^[a-z][a-z0-9-]{1,61}[a-z0-9]$');
|
|
173
|
+
b.data.ibm_code_engine_project={destination:{project_id:ceProject}};
|
|
174
|
+
b.resource.terraform_data.review_gate.lifecycle.precondition.push({condition:'${data.ibm_code_engine_project.destination.account_id == var.account_id && data.ibm_code_engine_project.destination.region == var.region}',error_message:'Existing Code Engine project must match the reviewed account and region.'});
|
|
175
|
+
for(const {row}of entries){
|
|
176
|
+
const n=row.address,name=input(n+'_name','Unique application and trusted-profile name.','reviewed-public-help','^[a-z][a-z0-9-]{2,38}[a-z0-9]$');
|
|
177
|
+
const repository=input(n+'_image_repository','Path for this emitted image in the existing namespace.','architect/public-help','^[a-z0-9]+(?:[._/-][a-z0-9]+)*$');
|
|
178
|
+
const digest=input(n+'_image_digest','Built, scanned, tested and authorized-published image digest.','sha256:'+'0'.repeat(64),'^sha256:[0-9a-f]{64}$');
|
|
179
|
+
const wxProject=input(n+'_watsonx_project_id','Existing watsonx data-platform project. Add the trusted profile as an authorized collaborator and associate the runtime instance.','22222222-2222-2222-2222-222222222222',UUID);
|
|
180
|
+
const instance=input(n+'_runtime_instance_id','Existing associated watsonx Runtime instance GUID.','33333333-3333-3333-3333-333333333333',UUID);
|
|
181
|
+
const model=input(n+'_model_id','Reviewed regional Text Chat JSON-object model; verify lifecycle, quotas and geography.','ibm/reviewed-model','^[a-z0-9][a-z0-9_.-]{0,79}/[a-z0-9][a-z0-9_.-]{0,99}$');
|
|
182
|
+
const editor=input(n+'_editor_access_group_id','Existing authorized company editor access-group ID. No group or user is created.','AccessGroup-44444444-4444-4444-4444-444444444444','^AccessGroup-[0-9a-fA-F-]{36}$');
|
|
183
|
+
const bucket=input(n+'_catalog_bucket','Globally unique regional bucket dedicated to genuinely public article content; the bucket itself remains private.','reviewed-public-catalog-unique','^[a-z][a-z0-9-]{1,61}[a-z0-9]$');
|
|
184
|
+
const review=n+'_policy_review';b.variable[review]={type:'object({ accountId = string, projectId = string, runtimeInstanceId = string, region = string, model = string, retentionReview = string, approvedBy = string, evidenceSha256 = string, expiresAt = string })',nullable:false,description:'Scoped expiring destination-owner review of actual model/provider retention and processing terms. Operator-attested evidence, not a provider approval API.'};b.examples[review]={accountId:b.examples.account_id,projectId:b.examples[n+'_watsonx_project_id'],runtimeInstanceId:b.examples[n+'_runtime_instance_id'],region:b.examples.region,model:b.examples[n+'_model_id'],retentionReview:'REVIEW_REQUIRED',approvedBy:'REVIEW_REQUIRED',evidenceSha256:'0'.repeat(64),expiresAt:'REPLACE_AFTER_REVIEW'};
|
|
185
|
+
add('ibm_resource_instance',n+'_catalog',{name:name+'-public-catalog',service:'cloud-object-storage',plan:'standard',location:'global',resource_group_id:'${data.ibm_code_engine_project.destination.resource_group_id}',lifecycle:{prevent_destroy:true,postcondition:[{condition:'${self.account_id == var.account_id}',error_message:'Catalog instance must belong to the reviewed account.'}]}});
|
|
186
|
+
add('ibm_cos_bucket',n,{bucket_name:bucket,resource_instance_id:ref('ibm_resource_instance',n+'_catalog','id'),region_location:region,storage_class:'standard',endpoint_type:'public',force_delete:false,object_versioning:{enable:true},noncurrent_version_expiration:{enable:true,noncurrent_days:7,prefix:'',rule_id:'expire-old-catalog-versions'},lifecycle:{prevent_destroy:true}});
|
|
187
|
+
add('ibm_iam_trusted_profile',n,{name,description:'Public FAQ compute identity; no catalog write or end-user authority'});
|
|
188
|
+
add('ibm_iam_trusted_profile_link',n,{profile_id:ref('ibm_iam_trusted_profile',n,'profile_id'),cr_type:'CE',link:{crn:'${data.ibm_code_engine_project.destination.crn}',component_name:ref('ibm_code_engine_app',n,'name'),component_type:'app'}});
|
|
189
|
+
add('ibm_iam_trusted_profile_policy',n,{iam_id:ref('ibm_iam_trusted_profile',n,'iam_id'),roles:['Writer'],resources:{service:'pm-20',resource_instance_id:instance,region},description:'Existing watsonx runtime-instance scope. Writer is broader than prediction-only; project membership remains separate.'});
|
|
190
|
+
const catalogScope={service:'cloud-object-storage',resource_instance_id:ref('ibm_resource_instance',n+'_catalog','guid'),resource_type:'bucket',resource:ref('ibm_cos_bucket',n,'bucket_name')};
|
|
191
|
+
add('ibm_iam_trusted_profile_policy',n+'_catalog',{iam_id:ref('ibm_iam_trusted_profile',n,'iam_id'),roles:['Object Reader'],resources:catalogScope});
|
|
192
|
+
// Article deletion rewrites catalog.json; object DELETE is intentionally absent.
|
|
193
|
+
// Object Writer includes object metadata/retention writes; it is not a custom
|
|
194
|
+
// put-only role. It cannot administer this bucket or grant public access.
|
|
195
|
+
add('ibm_iam_access_group_policy',n+'_editor',{access_group_id:editor,roles:['Object Reader','Object Writer'],resources:catalogScope});
|
|
196
|
+
const settings={ACCOUNT_ID:account,WATSONX_PROJECT_ID:wxProject,RUNTIME_INSTANCE_ID:instance,MODEL_REGION:region,MODEL_ID:model,IAM_PROFILE_ID:ref('ibm_iam_trusted_profile',n,'profile_id'),MAX_OUTPUT_TOKENS:'1024',CATALOG_BUCKET:ref('ibm_cos_bucket',n,'bucket_name'),POLICY_REVIEW_BASE64:'${base64encode(jsonencode(var.'+review+'))}'};
|
|
197
|
+
add('ibm_code_engine_app',n,{project_id:ceProject,name,image_reference:ibmRegistryHostExpression()+'/'+registry+'/'+repository+'@'+digest,image_secret:pullSecret,image_port:8080,managed_domain_mappings:'local_public',run_as_user:10001,run_compute_resource_token_enabled:true,scale_cpu_limit:'0.5',scale_memory_limit:'1G',scale_min_instances:0,scale_max_instances:2,scale_concurrency:5,scale_concurrency_target:5,scale_request_timeout:30,run_env_variables:Object.entries(settings).map(([name,value])=>({name,value,type:'literal'})),probe_readiness:{type:'http',path:'/healthz',port:8080,initial_delay:2,interval:5,timeout:2,failure_threshold:12},depends_on:['ibm_iam_trusted_profile_policy.'+n,'ibm_iam_trusted_profile_policy.'+n+'_catalog']});
|
|
198
|
+
b.assertions.push(`run "${n}_sydney_registry" {\n command = plan\n assert {\n condition = var.region != "au-syd" || startswith(ibm_code_engine_app.${n}.image_reference,"au.icr.io/")\n error_message = "Sydney uses au.icr.io; region concatenation cannot select a valid registry."\n }\n}`);
|
|
199
|
+
b.output[n+'_url']={value:ref('ibm_code_engine_app',n,'endpoint')};b.output[n+'_catalog']={value:{bucket,region}};b.output[n+'_trusted_profile_iam_id']={value:ref('ibm_iam_trusted_profile',n,'iam_id')};b.source(row);
|
|
200
|
+
b.assertions.push(`run "${n}_public_boundary" {\n command = plan\n assert {\n condition = ibm_iam_trusted_profile_policy.${n}_catalog.roles == tolist(["Object Reader"]) && ibm_iam_trusted_profile_policy.${n}_catalog.resources[0].resource_type == "bucket" && ibm_iam_trusted_profile_policy.${n}_catalog.resources[0].resource == var.${n}_catalog_bucket\n error_message = "Runtime may read only the dedicated catalog bucket; no write or public-access grant."\n }\n assert {\n condition = toset(ibm_iam_access_group_policy.${n}_editor.roles) == toset(["Object Reader", "Object Writer"]) && ibm_iam_access_group_policy.${n}_editor.resources[0].resource_type == "bucket" && ibm_iam_access_group_policy.${n}_editor.access_group_id == var.${n}_editor_access_group_id\n error_message = "Separate existing editors have bucket-scoped object access, never Manager or bucket Writer."\n }\n assert {\n condition = !ibm_cos_bucket.${n}.force_delete && ibm_cos_bucket.${n}.object_versioning[0].enable && ibm_code_engine_app.${n}.run_compute_resource_token_enabled && ibm_code_engine_app.${n}.run_as_user == 10001\n error_message = "Protect catalog recovery and use nonroot keyless compute identity."\n }\n}\nrun "${n}_mutable_image_blocked" {\n command = plan\n variables { ${n}_image_digest = "latest" }\n expect_failures = [var.${n}_image_digest]\n}`);
|
|
201
|
+
}
|
|
202
|
+
const config={terraform:{required_version:'>= 1.17.1, < 2.0.0',required_providers:{ibm:{source:'IBM-Cloud/ibm',version:'1.89.0'}}},provider:{ibm:{region}}};
|
|
203
|
+
const mocks=`mock_provider "ibm" {\n override_during = plan\n mock_data "ibm_code_engine_project" { defaults = { account_id = "${b.examples.account_id}", region = "${b.examples.region}", resource_group_id = "${'b'.repeat(32)}", crn = "crn:v1:bluemix:public:codeengine:${b.examples.region}:a/${b.examples.account_id}:11111111-1111-1111-1111-111111111111::" } }\n mock_resource "ibm_resource_instance" { defaults = { account_id = "${b.examples.account_id}", id = "crn:v1:bluemix:public:cloud-object-storage:global:a/${b.examples.account_id}:55555555-5555-5555-5555-555555555555::", guid = "55555555-5555-5555-5555-555555555555" } }\n}\n`;
|
|
204
|
+
b.assertions.push(`run "wrong_account_blocked" {\n command = plan\n variables { account_id = "${'c'.repeat(32)}" }\n expect_failures = [terraform_data.review_gate]\n}`);
|
|
205
|
+
return b.finish(config,CLOUD_PROVIDER_LOCKS.ibm,mocks,IBM_PUBLIC_FAQ_GUIDE);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export const IBM_PUBLIC_FAQ_GUIDE=`# IBM public-only FAQ composition
|
|
209
|
+
|
|
210
|
+
The anonymous browser/API uses a dedicated Code Engine trusted profile linked only
|
|
211
|
+
to this existing project and this new app. The runtime receives Object Reader on
|
|
212
|
+
the exact private COS bucket; a separate existing company access group receives
|
|
213
|
+
Object Reader and Object Writer on that bucket. Object Writer includes object
|
|
214
|
+
metadata and retention operations, not merely PUT; it does not administer buckets
|
|
215
|
+
or make them public. Article deletion conditionally replaces catalog.json; no
|
|
216
|
+
object DELETE grant or application HTTP editing route is needed. No public-access
|
|
217
|
+
group policy is emitted. Reconcile inherited account/group policies, which can
|
|
218
|
+
override narrower grants. Public endpoint routing does not mean anonymous storage.
|
|
219
|
+
|
|
220
|
+
COS uses a global service instance with a single regional data bucket, encryption
|
|
221
|
+
at rest managed by IBM and TLS. Control-plane geography requires separate review.
|
|
222
|
+
Versioning and seven-day noncurrent expiry aid recovery, not immediate erasure.
|
|
223
|
+
The runtime reads only the current catalog, checks it again before returning an
|
|
224
|
+
answer and withholds stale/unsupported evidence. Questions/answers are not persisted
|
|
225
|
+
or written to application logs. Private networking/customer-managed keys are not
|
|
226
|
+
implemented. This public-only application is not a private knowledge assistant.
|
|
227
|
+
|
|
228
|
+
Existing Code Engine project, namespace/pull-secret, watsonx project and associated
|
|
229
|
+
runtime instance remain intact. The profile gets Writer on that exact pm-20
|
|
230
|
+
instance; this is broader than prediction-only. Add the generated profile as a
|
|
231
|
+
minimum supported project collaborator and verify the association/model separately.
|
|
232
|
+
App creation precedes its compute link to avoid a Terraform cycle; health is
|
|
233
|
+
liveness, not model readiness. No app API key or end-user identity is invented.
|
|
234
|
+
|
|
235
|
+
See applications/*/BUILD.md for editor/build/testing commands. Supply a scoped,
|
|
236
|
+
expiring destination-owner policy review and test real IAM, compute token exchange,
|
|
237
|
+
conditional storage edits, source updates/deletions, model citations/abstention and
|
|
238
|
+
latency. The policy review is operator evidence, not a fabricated provider API.
|
|
239
|
+
Review current model availability, quotas, processing terms, prices, anonymous
|
|
240
|
+
abuse controls and budget policy. Instance/concurrency/token limits are not an
|
|
241
|
+
invoice cap. Protect state/variables. Native schema/mocks and local runtime tests
|
|
242
|
+
do not establish account-plan or deployed behavior. Obtain authorization before
|
|
243
|
+
image publication, cloud access, real plans or deployment; production operating
|
|
244
|
+
and state/backup review remain gated.
|
|
245
|
+
|
|
246
|
+
References:
|
|
247
|
+
- https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-iam-bucket-permissions
|
|
248
|
+
- https://cloud.ibm.com/docs/iam?topic=iam-iam-service-roles-actions
|
|
249
|
+
- https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-upload
|
|
250
|
+
- https://cloud.ibm.com/docs/codeengine?topic=codeengine-trusted-profiles-authentication-file
|
|
251
|
+
`;
|