@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.
Files changed (498) hide show
  1. package/CONSTITUTION.md +144 -0
  2. package/README.md +33 -2
  3. package/agent-skills/discovery/SKILL.md +48 -0
  4. package/agent-skills/discovery/topics.json +139 -0
  5. package/agent-skills/generation-capabilities.json +760 -0
  6. package/agent-skills/service-lifecycle.json +21 -0
  7. package/agent-skills/specialists/architecture/SKILL.md +15 -0
  8. package/agent-skills/specialists/aws/SKILL.md +17 -0
  9. package/agent-skills/specialists/azure/SKILL.md +7 -0
  10. package/agent-skills/specialists/composition/SKILL.md +17 -0
  11. package/agent-skills/specialists/devops/SKILL.md +10 -0
  12. package/agent-skills/specialists/finops/SKILL.md +7 -0
  13. package/agent-skills/specialists/gcp/SKILL.md +7 -0
  14. package/agent-skills/specialists/ibm/SKILL.md +7 -0
  15. package/agent-skills/specialists/manifest.json +13 -0
  16. package/agent-skills/specialists/pulumi/SKILL.md +7 -0
  17. package/agent-skills/specialists/security/SKILL.md +7 -0
  18. package/agent-skills/specialists/terraform/SKILL.md +14 -0
  19. package/agent-skills/upstream/aws/LICENSE +175 -0
  20. package/agent-skills/upstream/aws/SKILL.md +125 -0
  21. package/agent-skills/upstream/azure/LICENSE +21 -0
  22. package/agent-skills/upstream/azure/SKILL.md +387 -0
  23. package/agent-skills/upstream/gcp/LICENSE +202 -0
  24. package/agent-skills/upstream/gcp/SKILL.md +159 -0
  25. package/agent-skills/upstream/manifest.json +57 -0
  26. package/agent-skills/upstream/pulumi/LICENSE +201 -0
  27. package/agent-skills/upstream/pulumi/SKILL.md +501 -0
  28. package/agent-skills/upstream/terraform/LICENSE +373 -0
  29. package/agent-skills/upstream/terraform/SKILL.md +316 -0
  30. package/agent-skills/workflows/workflows.json +601 -0
  31. package/cli/architect.mjs +3 -0
  32. package/cli/runtime.mjs +115 -0
  33. package/dist/assets/html2canvas.esm-QH1iLAAe.js +22 -0
  34. package/dist/assets/index-B97MkVMs.css +1 -0
  35. package/dist/assets/index-qtCM2D7O.js +21271 -0
  36. package/dist/assets/index.es-C6C0sneB.js +18 -0
  37. package/dist/assets/purify.es-CAi2OJFm.js +3 -0
  38. package/dist/assets/visual-export-DjbjgQbA.js +173 -0
  39. package/dist/favicon.svg +2 -0
  40. package/dist/fonts/DM-Sans-OFL.txt +93 -0
  41. package/dist/fonts/Manrope-OFL.txt +93 -0
  42. package/dist/fonts/NotoSans-Regular.ttf +0 -0
  43. package/dist/fonts/OFL.txt +94 -0
  44. package/dist/fonts/dm-sans-latin-wght-normal.woff2 +0 -0
  45. package/dist/fonts/manrope-latin-wght-normal.woff2 +0 -0
  46. package/dist/index.html +4 -0
  47. package/dist/licenses/aws-architect-LICENSE.txt +17 -0
  48. package/dist/licenses/aws-mcp-LICENSE.txt +175 -0
  49. package/dist/licenses/aws-mcp-NOTICE.txt +1 -0
  50. package/dist/licenses/cloud-emulation-notices.txt +36619 -0
  51. package/dist/licenses/dependency-notices.txt +6379 -0
  52. package/dist/licenses/emulation-notices.txt +717 -0
  53. package/dist/licenses/github-copilot-sdk-LICENSE.txt +21 -0
  54. package/dist/licenses/memory-dependency-notices.txt +3319 -0
  55. package/dist/licenses/openarchflow-LICENSE.txt +21 -0
  56. package/dist/licenses/python-dependency-notices.txt +11951 -0
  57. package/docs/upstream-lock.json +26 -0
  58. package/knowledge/engineering-practices.json +536 -0
  59. package/package.json +30 -4
  60. package/packaging/uv-release-lock.json +54 -0
  61. package/public/licenses/aws-architect-LICENSE.txt +17 -0
  62. package/public/licenses/aws-mcp-LICENSE.txt +175 -0
  63. package/public/licenses/aws-mcp-NOTICE.txt +1 -0
  64. package/public/licenses/cloud-emulation-notices.txt +36619 -0
  65. package/public/licenses/dependency-notices.txt +6379 -0
  66. package/public/licenses/emulation-notices.txt +717 -0
  67. package/public/licenses/github-copilot-sdk-LICENSE.txt +21 -0
  68. package/public/licenses/memory-dependency-notices.txt +3319 -0
  69. package/public/licenses/openarchflow-LICENSE.txt +21 -0
  70. package/public/licenses/python-dependency-notices.txt +11951 -0
  71. package/scripts/terraform-render.mjs +8 -0
  72. package/server/__init__.py +0 -0
  73. package/server/abstraction.py +28 -0
  74. package/server/agent_runtime.py +153 -0
  75. package/server/app.py +257 -0
  76. package/server/architect.py +112 -0
  77. package/server/architecture_activity.py +67 -0
  78. package/server/attachments.py +101 -0
  79. package/server/cloud-emulation-requirements.txt +29 -0
  80. package/server/codex_app_server.py +219 -0
  81. package/server/codex_engineering_backend.py +266 -0
  82. package/server/codex_worker_policy.py +170 -0
  83. package/server/contracts.py +315 -0
  84. package/server/copilot_worker.py +16 -0
  85. package/server/dependency_mirror.py +54 -0
  86. package/server/design_patch.py +43 -0
  87. package/server/emulation-requirements.txt +8 -0
  88. package/server/emulation_adapters.py +20 -0
  89. package/server/emulation_routes.py +98 -0
  90. package/server/emulation_worker.py +351 -0
  91. package/server/engineering_artifacts.py +215 -0
  92. package/server/engineering_assets/application-runtime.json +4372 -0
  93. package/server/engineering_assets/architecture_work.schema.json +79 -0
  94. package/server/engineering_assets/materialize_runtime.py +62 -0
  95. package/server/engineering_assets/prepare_tools.py +96 -0
  96. package/server/engineering_assets/sql_validation.py +59 -0
  97. package/server/engineering_contexts.py +120 -0
  98. package/server/engineering_delivery.py +210 -0
  99. package/server/engineering_finalization.py +210 -0
  100. package/server/engineering_handoff.py +163 -0
  101. package/server/engineering_jobs.py +206 -0
  102. package/server/engineering_knowledge.py +75 -0
  103. package/server/engineering_reconciliation.py +159 -0
  104. package/server/engineering_routes.py +469 -0
  105. package/server/engineering_sessions.py +273 -0
  106. package/server/engineering_tools.py +166 -0
  107. package/server/generation_lint.py +18 -0
  108. package/server/local_providers.py +181 -0
  109. package/server/local_validation.py +236 -0
  110. package/server/memory-requirements.lock +181 -0
  111. package/server/memory-requirements.txt +6 -0
  112. package/server/memory_routes.py +82 -0
  113. package/server/memory_service.py +273 -0
  114. package/server/memory_worker.py +66 -0
  115. package/server/openai_voice.py +80 -0
  116. package/server/packaged_app.py +9 -0
  117. package/server/pricing.py +84 -0
  118. package/server/process_control_observation.py +31 -0
  119. package/server/prompts.py +123 -0
  120. package/server/public_faq_emulation_worker.py +44 -0
  121. package/server/requirements.lock +1618 -0
  122. package/server/requirements.txt +68 -0
  123. package/server/runtime_dependency_locks.py +27 -0
  124. package/server/runtime_evidence.py +93 -0
  125. package/server/settings.py +14 -0
  126. package/server/skills.py +103 -0
  127. package/server/storage_emulation_worker.py +160 -0
  128. package/server/structured_emulation_worker.py +48 -0
  129. package/server/terraform_routes.py +118 -0
  130. package/server/upstream/__init__.py +0 -0
  131. package/server/upstream/agent/__init__.py +1 -0
  132. package/server/upstream/agent/aws_architect.py +600 -0
  133. package/server/upstream/agent/config/__init__.py +1 -0
  134. package/server/upstream/agent/config/agent_config.py +82 -0
  135. package/server/upstream/agent/config/prompts.py +138 -0
  136. package/server/upstream/agent/rate_limiter.py +62 -0
  137. package/server/upstream/utils/__init__.py +1 -0
  138. package/server/upstream/utils/file_writer.py +43 -0
  139. package/server/validation_applicability.py +85 -0
  140. package/server/validation_jobs.py +252 -0
  141. package/server/validation_workers.py +48 -0
  142. package/server/voice_routes.py +91 -0
  143. package/server/voice_worker.py +26 -0
  144. package/specs/028-convergence-evaluation/evidence/20261110-ibm-invoice-native/registry-structured/qualification.json +72 -0
  145. package/specs/028-convergence-evaluation/evidence/20261110-invoice-runtime/durable-current/receipt.json +61 -0
  146. package/specs/029-general-purpose-engine/evidence/20261111-runtime-dependencies/sdk/receipt.json +22 -0
  147. package/src/ArchitectureActivity.jsx +54 -0
  148. package/src/ArchitectureWorkPanel.jsx +46 -0
  149. package/src/ArtifactChecks.jsx +49 -0
  150. package/src/BlockRequirements.jsx +13 -0
  151. package/src/BrainActivity.jsx +11 -0
  152. package/src/ChatMessage.jsx +19 -0
  153. package/src/ChatResizeHandle.jsx +6 -0
  154. package/src/CodePreview.jsx +8 -0
  155. package/src/ComponentReview.jsx +31 -0
  156. package/src/DeliveryViews.jsx +59 -0
  157. package/src/EmulationPanel.jsx +44 -0
  158. package/src/EngagementViews.jsx +385 -0
  159. package/src/EngineeringPanel.jsx +151 -0
  160. package/src/ImplementationDelivery.jsx +55 -0
  161. package/src/JourneyViews.jsx +400 -0
  162. package/src/MemoryPanel.jsx +46 -0
  163. package/src/OpenAIVoicePanel.jsx +65 -0
  164. package/src/OutputsView.jsx +65 -0
  165. package/src/TerraformView.jsx +57 -0
  166. package/src/WorkbenchDialogs.jsx +532 -0
  167. package/src/activity-presentation.mjs +28 -0
  168. package/src/ai-qualification-scenarios.mjs +31 -0
  169. package/src/approval-application-files.mjs +32 -0
  170. package/src/approval-applications.mjs +82 -0
  171. package/src/approval-spec-kit.mjs +30 -0
  172. package/src/architecture-activity.css +28 -0
  173. package/src/architecture-activity.mjs +63 -0
  174. package/src/architecture-work.mjs +176 -0
  175. package/src/artifact-work.mjs +103 -0
  176. package/src/async-documents.mjs +191 -0
  177. package/src/atflow.css +1140 -0
  178. package/src/atforma.css +748 -0
  179. package/src/atmem-tokens.css +79 -0
  180. package/src/aws-preflight.mjs +60 -0
  181. package/src/aws-public-faq.mjs +102 -0
  182. package/src/aws-validation-scenarios.mjs +10 -0
  183. package/src/aws-validation.mjs +48 -0
  184. package/src/azure-plan-mocks.mjs +25 -0
  185. package/src/azure-structured.mjs +133 -0
  186. package/src/bedrock-summary.mjs +152 -0
  187. package/src/bounded-rag.mjs +60 -0
  188. package/src/brand.css +9 -0
  189. package/src/canvas-view.mjs +28 -0
  190. package/src/capability-composition.mjs +44 -0
  191. package/src/chat-attachments.mjs +13 -0
  192. package/src/cloud-provider-locks.mjs +9 -0
  193. package/src/cloud-storage-scenarios.mjs +2 -0
  194. package/src/component-review.mjs +55 -0
  195. package/src/composition-spec-kit.mjs +30 -0
  196. package/src/consultation-context.mjs +15 -0
  197. package/src/consultation.mjs +52 -0
  198. package/src/container-contracts.mjs +5 -0
  199. package/src/container-inspection.mjs +75 -0
  200. package/src/container-runtime.mjs +30 -0
  201. package/src/container-security-inspection.mjs +49 -0
  202. package/src/container-terraform.mjs +109 -0
  203. package/src/conversation.mjs +17 -0
  204. package/src/core.mjs +863 -0
  205. package/src/cross-cloud-storage.mjs +100 -0
  206. package/src/delivery-capabilities.mjs +25 -0
  207. package/src/delivery.mjs +58 -0
  208. package/src/deployment-ownership.mjs +10 -0
  209. package/src/deployment-region.mjs +11 -0
  210. package/src/derive.mjs +31 -0
  211. package/src/design-build-flow.mjs +10 -0
  212. package/src/diagram-attention.mjs +24 -0
  213. package/src/discovery.mjs +359 -0
  214. package/src/document.mjs +170 -0
  215. package/src/emulation-feedback.mjs +61 -0
  216. package/src/emulation-guidance.mjs +37 -0
  217. package/src/engagement.mjs +274 -0
  218. package/src/engineering-client.mjs +73 -0
  219. package/src/engineering-defaults.mjs +17 -0
  220. package/src/engineering.css +31 -0
  221. package/src/enterprise-application.mjs +76 -0
  222. package/src/enterprise-aws-host-infrastructure.mjs +62 -0
  223. package/src/enterprise-aws-host-tests.mjs +15 -0
  224. package/src/enterprise-aws-infrastructure-tests.mjs +38 -0
  225. package/src/enterprise-aws-infrastructure.mjs +138 -0
  226. package/src/enterprise-google-infrastructure.mjs +84 -0
  227. package/src/enterprise-google-tests.mjs +25 -0
  228. package/src/estimate.mjs +188 -0
  229. package/src/existing-references.mjs +18 -0
  230. package/src/external-identities.mjs +17 -0
  231. package/src/faq-lambda-build.mjs +28 -0
  232. package/src/gcp-structured.mjs +104 -0
  233. package/src/generation-provider.mjs +7 -0
  234. package/src/generation-quality.mjs +69 -0
  235. package/src/handoff.mjs +141 -0
  236. package/src/ibm-registry.mjs +12 -0
  237. package/src/ibm-structured.mjs +103 -0
  238. package/src/identifiers.mjs +15 -0
  239. package/src/implementation-map.mjs +14 -0
  240. package/src/implementation-outcome.mjs +8 -0
  241. package/src/incomplete-terraform.mjs +9 -0
  242. package/src/integrations.ts +137 -0
  243. package/src/interface-contracts.mjs +30 -0
  244. package/src/invoice-application.mjs +66 -0
  245. package/src/invoice-aws-operations.mjs +136 -0
  246. package/src/invoice-aws.mjs +143 -0
  247. package/src/invoice-azure-operations.mjs +147 -0
  248. package/src/invoice-azure.mjs +134 -0
  249. package/src/invoice-builder.mjs +35 -0
  250. package/src/invoice-dependencies.mjs +43 -0
  251. package/src/invoice-gcp-operations.mjs +178 -0
  252. package/src/invoice-gcp.mjs +104 -0
  253. package/src/invoice-ibm-operations.mjs +278 -0
  254. package/src/invoice-ibm.mjs +142 -0
  255. package/src/invoice-operations.mjs +143 -0
  256. package/src/invoice-plan.mjs +45 -0
  257. package/src/invoice-region.mjs +6 -0
  258. package/src/invoice-spec-kit.mjs +61 -0
  259. package/src/invoice-state.mjs +7 -0
  260. package/src/invoice-terraform.mjs +29 -0
  261. package/src/invoice-tests.mjs +122 -0
  262. package/src/journey-workspace.css +72 -0
  263. package/src/journey.mjs +87 -0
  264. package/src/levels.mjs +145 -0
  265. package/src/local-agent.mjs +184 -0
  266. package/src/main.jsx +2458 -0
  267. package/src/model-policy-preflight.mjs +34 -0
  268. package/src/openai-voice.mjs +102 -0
  269. package/src/output-bundle.mjs +11 -0
  270. package/src/ownership-inspection.mjs +28 -0
  271. package/src/pack.mjs +201 -0
  272. package/src/pricing.mjs +31 -0
  273. package/src/production-state.mjs +8 -0
  274. package/src/project-copy.mjs +10 -0
  275. package/src/project-file.mjs +160 -0
  276. package/src/project-links.mjs +3 -0
  277. package/src/protected-state.mjs +43 -0
  278. package/src/public-faq-builder.mjs +19 -0
  279. package/src/public-faq-clouds.mjs +251 -0
  280. package/src/public-faq.mjs +122 -0
  281. package/src/pulumi.mjs +123 -0
  282. package/src/python-container.mjs +25 -0
  283. package/src/python-runtime.mjs +50 -0
  284. package/src/rag-handler.mjs +43 -0
  285. package/src/rag-web-assets.mjs +52 -0
  286. package/src/rag-web.mjs +68 -0
  287. package/src/records.mjs +177 -0
  288. package/src/resources.mjs +163 -0
  289. package/src/review.mjs +252 -0
  290. package/src/runtime/EnterpriseDocument.java +89 -0
  291. package/src/runtime/approved_http_destination.py +72 -0
  292. package/src/runtime/aws-lambda-qualification-lock.json +7 -0
  293. package/src/runtime/aws-python-lock.json +82 -0
  294. package/src/runtime/aws-web-python-lock.json +887 -0
  295. package/src/runtime/aws_json_model.py +42 -0
  296. package/src/runtime/azure-python-lock.json +863 -0
  297. package/src/runtime/azure-sources.json +8 -0
  298. package/src/runtime/azure-test-lock.json +23 -0
  299. package/src/runtime/azure_acceptance.py +192 -0
  300. package/src/runtime/azure_structured.py +153 -0
  301. package/src/runtime/bounded_rag.py +126 -0
  302. package/src/runtime/catalog_admin.py +70 -0
  303. package/src/runtime/cloud-sql-proxy-lock.json +8 -0
  304. package/src/runtime/cloud-sql-proxy-security-review.json +30 -0
  305. package/src/runtime/connector_cli.py +50 -0
  306. package/src/runtime/connector_sync.py +70 -0
  307. package/src/runtime/durable-python-lock.json +247 -0
  308. package/src/runtime/durable_workflow.py +325 -0
  309. package/src/runtime/enterprise-application-manifest.json +110 -0
  310. package/src/runtime/enterprise-application-sources.json +266 -0
  311. package/src/runtime/enterprise-auth-python-lock.json +592 -0
  312. package/src/runtime/enterprise-aws-host-bootstrap-source.json +1 -0
  313. package/src/runtime/enterprise-aws-host-probe-source.json +1 -0
  314. package/src/runtime/enterprise-aws-task-template.json +235 -0
  315. package/src/runtime/enterprise-document-lock.json +18 -0
  316. package/src/runtime/enterprise-document.Dockerfile +15 -0
  317. package/src/runtime/enterprise-runtime-aws-python-lock.json +1115 -0
  318. package/src/runtime/enterprise-runtime-azure-python-lock.json +1106 -0
  319. package/src/runtime/enterprise-runtime-gcp-python-lock.json +1060 -0
  320. package/src/runtime/enterprise-runtime-ibm-python-lock.json +1096 -0
  321. package/src/runtime/enterprise-runtime-python-lock.json +1058 -0
  322. package/src/runtime/enterprise-runtime.Dockerfile +15 -0
  323. package/src/runtime/enterprise-web-app.js +1 -0
  324. package/src/runtime/enterprise-web-entry.js +105 -0
  325. package/src/runtime/enterprise-web-lock.json +29 -0
  326. package/src/runtime/enterprise-web-notices.txt +229 -0
  327. package/src/runtime/enterprise-web.css +4 -0
  328. package/src/runtime/enterprise-web.html +8 -0
  329. package/src/runtime/enterprise_audit.py +91 -0
  330. package/src/runtime/enterprise_audit_schema.sql +25 -0
  331. package/src/runtime/enterprise_auth_schema.sql +67 -0
  332. package/src/runtime/enterprise_auth_store.py +241 -0
  333. package/src/runtime/enterprise_aws_host_bootstrap.py +120 -0
  334. package/src/runtime/enterprise_aws_host_probe.py +105 -0
  335. package/src/runtime/enterprise_aws_hosts.py +136 -0
  336. package/src/runtime/enterprise_aws_jobs.py +300 -0
  337. package/src/runtime/enterprise_aws_objects.py +93 -0
  338. package/src/runtime/enterprise_aws_transfer.py +83 -0
  339. package/src/runtime/enterprise_azure_batch_api.py +128 -0
  340. package/src/runtime/enterprise_azure_host.py +599 -0
  341. package/src/runtime/enterprise_azure_jobs.py +294 -0
  342. package/src/runtime/enterprise_azure_objects.py +234 -0
  343. package/src/runtime/enterprise_azure_pool.py +161 -0
  344. package/src/runtime/enterprise_azure_transfer.py +122 -0
  345. package/src/runtime/enterprise_configuration.py +136 -0
  346. package/src/runtime/enterprise_document.py +116 -0
  347. package/src/runtime/enterprise_document_cli.py +32 -0
  348. package/src/runtime/enterprise_document_contract.py +54 -0
  349. package/src/runtime/enterprise_document_job.py +109 -0
  350. package/src/runtime/enterprise_document_jobs.py +122 -0
  351. package/src/runtime/enterprise_document_jobs_schema.sql +23 -0
  352. package/src/runtime/enterprise_extraction_cache.py +186 -0
  353. package/src/runtime/enterprise_extraction_schema.sql +17 -0
  354. package/src/runtime/enterprise_google_jobs.py +275 -0
  355. package/src/runtime/enterprise_google_transfer.py +85 -0
  356. package/src/runtime/enterprise_http.py +212 -0
  357. package/src/runtime/enterprise_identity.py +52 -0
  358. package/src/runtime/enterprise_knowledge.py +152 -0
  359. package/src/runtime/enterprise_models.py +94 -0
  360. package/src/runtime/enterprise_native.py +181 -0
  361. package/src/runtime/enterprise_oauth.py +132 -0
  362. package/src/runtime/enterprise_operator.py +160 -0
  363. package/src/runtime/enterprise_parser_service.py +136 -0
  364. package/src/runtime/enterprise_parser_transport.py +68 -0
  365. package/src/runtime/enterprise_queries.py +255 -0
  366. package/src/runtime/enterprise_queries_schema.sql +27 -0
  367. package/src/runtime/enterprise_query_service.py +111 -0
  368. package/src/runtime/enterprise_secrets.py +110 -0
  369. package/src/runtime/enterprise_sessions.py +95 -0
  370. package/src/runtime/enterprise_sources.py +260 -0
  371. package/src/runtime/enterprise_web.py +39 -0
  372. package/src/runtime/enterprise_worker.py +68 -0
  373. package/src/runtime/export_contract_acceptance.py +18 -0
  374. package/src/runtime/faq-output-contract.json +40 -0
  375. package/src/runtime/faq-web-app.js +41 -0
  376. package/src/runtime/faq-web.css +1 -0
  377. package/src/runtime/faq-web.html +6 -0
  378. package/src/runtime/gcp-sources.json +9 -0
  379. package/src/runtime/gcp_acceptance.py +110 -0
  380. package/src/runtime/gcp_structured.py +150 -0
  381. package/src/runtime/http_acceptance.py +39 -0
  382. package/src/runtime/iap-web-app.js +36 -0
  383. package/src/runtime/iap-web-source.json +1 -0
  384. package/src/runtime/ibm-python-lock.json +856 -0
  385. package/src/runtime/ibm-sources.json +9 -0
  386. package/src/runtime/ibm_acceptance.py +133 -0
  387. package/src/runtime/ibm_structured.py +113 -0
  388. package/src/runtime/invoice-azapi-provider-lock.json +7 -0
  389. package/src/runtime/invoice-ibm-provider-lock.json +7 -0
  390. package/src/runtime/invoice-parser-lock.json +1199 -0
  391. package/src/runtime/invoice-parser-python-lock.json +56 -0
  392. package/src/runtime/invoice-parser.Dockerfile +190 -0
  393. package/src/runtime/invoice-sources.json +58 -0
  394. package/src/runtime/invoice-test-sources.json +24 -0
  395. package/src/runtime/invoice-web.css +2 -0
  396. package/src/runtime/invoice-web.html +12 -0
  397. package/src/runtime/invoice-web.js +112 -0
  398. package/src/runtime/invoice_configuration.py +17 -0
  399. package/src/runtime/invoice_document.py +95 -0
  400. package/src/runtime/invoice_extraction.py +96 -0
  401. package/src/runtime/invoice_http.py +129 -0
  402. package/src/runtime/invoice_identity.py +5 -0
  403. package/src/runtime/invoice_maintenance.py +40 -0
  404. package/src/runtime/invoice_native.py +77 -0
  405. package/src/runtime/invoice_object_store.py +153 -0
  406. package/src/runtime/invoice_operations.py +108 -0
  407. package/src/runtime/invoice_operator.py +79 -0
  408. package/src/runtime/invoice_operator_core.py +39 -0
  409. package/src/runtime/invoice_operator_job.py +147 -0
  410. package/src/runtime/invoice_parser_cli.py +17 -0
  411. package/src/runtime/invoice_parser_identity.py +39 -0
  412. package/src/runtime/invoice_parser_service.py +89 -0
  413. package/src/runtime/invoice_parser_transport.py +89 -0
  414. package/src/runtime/invoice_processing.py +24 -0
  415. package/src/runtime/invoice_retention.py +16 -0
  416. package/src/runtime/invoice_runtime_secrets.py +43 -0
  417. package/src/runtime/invoice_stage.py +80 -0
  418. package/src/runtime/invoice_web.py +39 -0
  419. package/src/runtime/invoice_worker.py +50 -0
  420. package/src/runtime/lambda_faq.py +5 -0
  421. package/src/runtime/parser-requirements.lock +4 -0
  422. package/src/runtime/policy_review.py +18 -0
  423. package/src/runtime/postgres-qualification-lock.json +21 -0
  424. package/src/runtime/public-faq-sources.json +22 -0
  425. package/src/runtime/public_catalog_admin.py +122 -0
  426. package/src/runtime/public_catalog_store.py +125 -0
  427. package/src/runtime/public_faq.py +144 -0
  428. package/src/runtime/public_faq_app.py +49 -0
  429. package/src/runtime/python-container-lock.json +18 -0
  430. package/src/runtime/rag-sources.json +10 -0
  431. package/src/runtime/rag_acceptance.py +60 -0
  432. package/src/runtime/rag_aws_adapter.py +95 -0
  433. package/src/runtime/rds-ca-source.json +9 -0
  434. package/src/runtime/rds-global-ca.pem +2888 -0
  435. package/src/runtime/state-contract-check-source.json +4 -0
  436. package/src/runtime/state_contract_check.py +338 -0
  437. package/src/runtime/structured-source.json +1 -0
  438. package/src/runtime/structured-web-app.js +71 -0
  439. package/src/runtime/structured-web-source.json +1 -0
  440. package/src/runtime/structured_http.py +105 -0
  441. package/src/runtime/structured_output.py +48 -0
  442. package/src/runtime/web-python-lock.json +802 -0
  443. package/src/runtime/web_user_admin.py +44 -0
  444. package/src/runtime/workflow_application.py +62 -0
  445. package/src/runtime/workflow_bindings.py +128 -0
  446. package/src/runtime/workflow_configuration.py +40 -0
  447. package/src/runtime/workflow_connection.py +105 -0
  448. package/src/runtime/workflow_contract.py +54 -0
  449. package/src/runtime/workflow_database.py +94 -0
  450. package/src/runtime/workflow_errors.py +6 -0
  451. package/src/runtime/workflow_http.py +178 -0
  452. package/src/runtime/workflow_identity.py +69 -0
  453. package/src/runtime/workflow_payload.py +118 -0
  454. package/src/runtime/workflow_process_stage.py +88 -0
  455. package/src/runtime/workflow_profile.py +91 -0
  456. package/src/runtime/workflow_review.py +106 -0
  457. package/src/runtime/workflow_role_bootstrap.py +104 -0
  458. package/src/runtime/workflow_schema.sql +72 -0
  459. package/src/runtime/workflow_worker.py +142 -0
  460. package/src/runtime-settings.mjs +15 -0
  461. package/src/s3-access.mjs +14 -0
  462. package/src/source-contracts.mjs +17 -0
  463. package/src/spec-kit.mjs +186 -0
  464. package/src/state-custody.mjs +32 -0
  465. package/src/state-inspection.mjs +45 -0
  466. package/src/state-spec-kit.mjs +13 -0
  467. package/src/storage-tests.mjs +25 -0
  468. package/src/structured-api.mjs +133 -0
  469. package/src/structured-contract.mjs +19 -0
  470. package/src/structured-operation-policy.mjs +2 -0
  471. package/src/structured-web.mjs +17 -0
  472. package/src/style.css +1 -0
  473. package/src/system-description.mjs +26 -0
  474. package/src/terraform-admission.mjs +66 -0
  475. package/src/terraform-composition.mjs +83 -0
  476. package/src/terraform-expressions.mjs +46 -0
  477. package/src/terraform-validation-coverage.mjs +11 -0
  478. package/src/terraform.mjs +128 -0
  479. package/src/text-statistics.mjs +51 -0
  480. package/src/upstream/openarchflow/format-payload.ts +26 -0
  481. package/src/upstream/openarchflow/import/mermaid.ts +205 -0
  482. package/src/upstream/openarchflow/import/sql-ddl.ts +109 -0
  483. package/src/upstream/openarchflow/layout.ts +54 -0
  484. package/src/upstream/openarchflow/providers/aws.ts +196 -0
  485. package/src/upstream/openarchflow/providers/azure.ts +126 -0
  486. package/src/upstream/openarchflow/providers/cloudnative.ts +71 -0
  487. package/src/upstream/openarchflow/providers/generic.ts +227 -0
  488. package/src/upstream/openarchflow/providers/integration.ts +22 -0
  489. package/src/upstream/openarchflow/providers/types.ts +2 -0
  490. package/src/upstream/openarchflow/templates.ts +937 -0
  491. package/src/upstream-catalog.mjs +10 -0
  492. package/src/useChatScroll.jsx +36 -0
  493. package/src/useEmulation.jsx +36 -0
  494. package/src/validation-receipt.mjs +10 -0
  495. package/src/visual-export.mjs +92 -0
  496. package/src/voice-conversation.mjs +117 -0
  497. package/src/work-intake.mjs +24 -0
  498. package/src/workflow-runtime-inventory.mjs +3 -0
@@ -0,0 +1,4 @@
1
+ {
2
+ "source": "\"\"\"Local synthetic HTTP-state protocol check; never opens a project backend.\n\nThis fixture is not a production state service. All state, credentials, databases\nand native workspaces are created in one fresh directory. No apply, migration,\nprovider, cloud credential or caller-supplied endpoint is used.\n\"\"\"\nimport argparse\nimport base64\nimport contextlib\nimport hashlib\nimport http.server\nimport json\nimport os\nfrom pathlib import Path\nimport secrets\nimport shutil\nimport sqlite3\nimport subprocess\nimport threading\nimport urllib.error\nimport urllib.parse\nimport urllib.request\nimport uuid\n\n\ndef encoded(value):\n return json.dumps(value, sort_keys=True, separators=(',', ':')).encode()\n\n\ndef digest(value):\n return hashlib.sha256(value).hexdigest()\n\n\nclass FixtureStore:\n def __init__(self, path, initial):\n self.path = path\n with self.connection() as db:\n db.executescript('''CREATE TABLE IF NOT EXISTS versions(\n id INTEGER PRIMARY KEY, payload BLOB NOT NULL, sha TEXT NOT NULL);\n CREATE TABLE IF NOT EXISTS head(id INTEGER PRIMARY KEY CHECK(id=1),version INTEGER NOT NULL);\n CREATE TABLE IF NOT EXISTS lease(id INTEGER PRIMARY KEY CHECK(id=1),actor TEXT,info TEXT);''')\n if not db.execute('SELECT 1 FROM head').fetchone():\n db.execute('INSERT INTO versions VALUES(1,?,?)', (initial, digest(initial)))\n db.execute('INSERT INTO head VALUES(1,1)')\n\n @contextlib.contextmanager\n def connection(self):\n db = sqlite3.connect(self.path, timeout=5)\n try:\n db.execute('PRAGMA journal_mode=WAL')\n db.execute('PRAGMA synchronous=FULL')\n db.execute('BEGIN IMMEDIATE')\n yield db\n db.commit()\n except BaseException:\n db.rollback()\n raise\n finally:\n db.close()\n\n def read(self):\n with self.connection() as db:\n row = db.execute('SELECT payload,sha FROM versions JOIN head ON versions.id=head.version').fetchone()\n if digest(row[0]) != row[1]:\n raise ValueError('Fixture state digest mismatch')\n return row[0]\n\n def lock(self):\n with self.connection() as db:\n row = db.execute('SELECT actor,info FROM lease').fetchone()\n return (row[0], json.loads(row[1])) if row else None\n\n def acquire(self, actor, info):\n if not isinstance(info.get('ID'), str) or not info['ID'] or len(info['ID']) > 100:\n return 400, {}\n with self.connection() as db:\n row = db.execute('SELECT actor,info FROM lease').fetchone()\n if row:\n return 409, json.loads(row[1])\n db.execute('INSERT INTO lease VALUES(1,?,?)', (actor, json.dumps(info)))\n return 200, {}\n\n def unlock(self, actor, info):\n with self.connection() as db:\n row = db.execute('SELECT actor,info FROM lease').fetchone()\n if not row:\n return 409, {}\n current = json.loads(row[1])\n if actor != row[0] or info.get('ID') != current['ID']:\n return 409, current\n db.execute('DELETE FROM lease')\n return 200, {}\n\n def denied_write(self, actor, lock_id):\n with self.connection() as db:\n row = db.execute('SELECT actor,info FROM lease').fetchone()\n # Fixture intentionally has no HTTP write implementation. Plans and\n # state pull must not write; only the test controller adds versions.\n if not row or row[0] != actor or json.loads(row[1])['ID'] != lock_id:\n return 409\n return 405\n\n def add_fixture_version(self, payload):\n with self.connection() as db:\n if db.execute('SELECT 1 FROM lease').fetchone():\n raise ValueError('An active writer forbids fixture version changes')\n cursor = db.execute('INSERT INTO versions(payload,sha) VALUES(?,?)', (payload, digest(payload)))\n db.execute('UPDATE head SET version=?', (cursor.lastrowid,))\n return cursor.lastrowid\n\n def restore_fixture_version(self, version):\n with self.connection() as db:\n if db.execute('SELECT 1 FROM lease').fetchone():\n raise ValueError('An active writer forbids fixture recovery')\n row = db.execute('SELECT payload,sha FROM versions WHERE id=?', (version,)).fetchone()\n if not row or digest(row[0]) != row[1]:\n raise ValueError('Unverified fixture backup')\n json.loads(row[0])\n db.execute('UPDATE head SET version=?', (version,))\n\n\nclass Control:\n def __init__(self):\n self.pause = False\n self.read_held = threading.Event()\n self.release = threading.Event()\n self.events = []\n\n\ndef fixture_server(store, credentials, control):\n class Handler(http.server.BaseHTTPRequestHandler):\n def log_message(self, *_):\n pass\n\n def respond(self, status, value=None):\n body = value if isinstance(value, bytes) else encoded(value or {})\n self.send_response(status)\n self.send_header('Content-Type', 'application/json')\n self.send_header('Content-Length', str(len(body)))\n self.end_headers()\n try:\n self.wfile.write(body)\n except (BrokenPipeError, ConnectionResetError):\n pass\n\n def actor(self):\n header = self.headers.get('Authorization', '')\n for actor, password in credentials.items():\n expected = 'Basic ' + base64.b64encode((actor + ':' + password).encode()).decode()\n if secrets.compare_digest(header, expected):\n return actor\n return None\n\n def dispatch(self):\n actor = self.actor()\n route = urllib.parse.urlsplit(self.path)\n if not actor:\n self.respond(401)\n return\n control.events.append({'method': self.command, 'path': route.path, 'actor': actor})\n if self.command == 'GET' and route.path == '/state':\n lease = store.lock()\n if control.pause and lease and lease[0] == actor:\n control.read_held.set()\n if not control.release.wait(25):\n self.respond(503)\n return\n self.respond(200, store.read())\n return\n length = int(self.headers.get('Content-Length', '0'))\n if length < 0 or length > 65536:\n self.respond(413)\n return\n try:\n value = json.loads(self.rfile.read(length)) if length else {}\n except ValueError:\n self.respond(400)\n return\n if not isinstance(value, dict):\n self.respond(400)\n return\n if route.path == '/lock' and self.command in {'POST', 'DELETE'}:\n status, response = (store.acquire if self.command == 'POST' else store.unlock)(actor, value)\n self.respond(status, response)\n elif route.path == '/state' and self.command == 'POST':\n lock_id = urllib.parse.parse_qs(route.query).get('ID', [None])[0]\n self.respond(store.denied_write(actor, lock_id))\n else:\n self.respond(405)\n\n do_GET = dispatch\n do_POST = dispatch\n do_DELETE = dispatch\n\n server = http.server.ThreadingHTTPServer(('127.0.0.1', 0), Handler)\n server.daemon_threads = True\n return server\n\n\ndef run_probe(terraform, folder):\n folder.mkdir(parents=True, exist_ok=False)\n private = folder / 'private'\n private.mkdir(mode=0o700)\n os.chmod(folder, 0o700)\n credentials = {actor: secrets.token_urlsafe(32) for actor in ['writer-a', 'writer-b']}\n base_env = {'PATH': str(terraform.parent), 'HOME': str(private), 'TMPDIR': str(private),\n 'TF_IN_AUTOMATION': '1', 'CHECKPOINT_DISABLE': '1', 'TF_INPUT': '0',\n 'TF_HTTP_RETRY_MAX': '0', 'AWS_EC2_METADATA_DISABLED': 'true'}\n cli = private / 'empty.tfrc'\n cli.write_text('provider_installation { filesystem_mirror { path = ' + json.dumps(str(private)) + ' } }\\n')\n base_env['TF_CLI_CONFIG_FILE'] = str(cli)\n version = json.loads(subprocess.check_output([str(terraform), 'version', '-json'], env=base_env, timeout=15))\n initial = encoded({'version': 4, 'terraform_version': version['terraform_version'], 'serial': 1,\n 'lineage': str(uuid.uuid4()), 'outputs': {'fixture': {'value': 'synthetic-state', 'type': 'string'}}, 'resources': []})\n store = FixtureStore(private / 'fixture.sqlite', initial)\n control = Control()\n server = fixture_server(store, credentials, control)\n base = 'http://127.0.0.1:' + str(server.server_port)\n thread = threading.Thread(target=server.serve_forever, daemon=True)\n thread.start()\n checks, commands = [], []\n active = None\n\n def check(name, condition):\n checks.append({'name': name, 'passed': bool(condition)})\n if not condition:\n raise AssertionError(name)\n\n def environment(actor):\n return {**base_env, 'TF_HTTP_USERNAME': actor, 'TF_HTTP_PASSWORD': credentials[actor]}\n\n def native(actor, arguments, expected_exit=0):\n result = subprocess.run([str(terraform), *arguments], cwd=private / actor,\n env=environment(actor), capture_output=True, text=True, timeout=20)\n commands.append({'actor': actor, 'arguments': arguments, 'exitCode': result.returncode, 'expectedExitCode': expected_exit})\n (private / ('command-' + str(len(commands)) + '.log')).write_text(result.stdout + '\\n' + result.stderr)\n return result\n\n def request(actor, method, path, value=None, password=None):\n header = base64.b64encode((actor + ':' + (password or credentials[actor])).encode()).decode()\n req = urllib.request.Request(base + path, data=encoded(value) if value is not None else None,\n method=method, headers={'Authorization': 'Basic ' + header})\n opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))\n try:\n with opener.open(req, timeout=5) as response:\n return response.status\n except urllib.error.HTTPError as error:\n return error.code\n\n try:\n for actor in credentials:\n workspace = private / actor\n workspace.mkdir()\n (workspace / 'main.tf.json').write_text(json.dumps({'terraform': {'required_version': '>= 1.10.0, < 2.0.0', 'backend': {'http': {}}}, 'output': {'fixture': {'value': 'synthetic-state'}}}))\n (workspace / 'backend.hcl').write_text('address = ' + json.dumps(base + '/state') + '\\nlock_address = ' + json.dumps(base + '/lock') + '\\nunlock_address = ' + json.dumps(base + '/lock') + '\\nlock_method = \"POST\"\\nunlock_method = \"DELETE\"\\n')\n check('native init ' + actor, native(actor, ['init', '-input=false', '-no-color', '-backend-config=backend.hcl']).returncode == 0)\n pulled = native('writer-a', ['state', 'pull'])\n check('native reads synthetic lineage and payload', pulled.returncode == 0 and all(json.loads(pulled.stdout).get(k) == json.loads(initial)[k] for k in ['version', 'serial', 'lineage', 'outputs', 'resources']))\n check('unknown credential denied', request('writer-a', 'GET', '/state', password='invalid-fixture-password') == 401)\n control.pause = True\n active = subprocess.Popen([str(terraform), 'plan', '-input=false', '-no-color', '-lock-timeout=0s'], cwd=private / 'writer-a', env=environment('writer-a'), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)\n check('real native plan holds lock while reading', control.read_held.wait(12) and active.poll() is None and store.lock() is not None)\n current = store.lock()[1]\n competing = native('writer-b', ['plan', '-input=false', '-no-color', '-lock-timeout=0s'], expected_exit=1)\n check('second native writer excluded', competing.returncode == 1 and 'Error acquiring the state lock' in competing.stderr + competing.stdout)\n check('foreign actor cannot unlock owner', request('writer-b', 'DELETE', '/lock', {'ID': current['ID']}) == 409 and store.lock()[1]['ID'] == current['ID'])\n check('incorrect lock ID cannot unlock owner', request('writer-a', 'DELETE', '/lock', {'ID': 'wrong-fixture-lock'}) == 409 and store.lock()[1]['ID'] == current['ID'])\n check('foreign writer cannot replace state', request('writer-b', 'POST', '/state?ID=' + current['ID'], {}) == 409 and store.read() == initial)\n try:\n store.restore_fixture_version(1)\n refused = False\n except ValueError:\n refused = True\n check('recovery refuses an active writer', refused)\n control.pause = False\n control.release.set()\n output, _ = active.communicate(timeout=15)\n (private / 'writer-a-plan.log').write_text(output)\n commands.append({'actor': 'writer-a', 'arguments': ['plan', '-input=false', '-no-color', '-lock-timeout=0s'], 'exitCode': active.returncode, 'expectedExitCode': 0})\n check('native plan succeeds and releases owned lock', active.returncode == 0 and store.lock() is None)\n check('plan-only clients never write state', not any(e['method'] == 'POST' and e['path'] == '/state' and e['actor'] == 'writer-a' for e in control.events) and store.read() == initial)\n changed = json.loads(initial)\n changed['serial'] = 2\n changed['outputs']['fixture']['value'] = 'synthetic-new-version'\n changed_id = store.add_fixture_version(encoded(changed))\n reopened = FixtureStore(private / 'fixture.sqlite', b'not-a-new-seed')\n check('local fixture versions survive reopen', json.loads(reopened.read())['serial'] == 2)\n reopened.restore_fixture_version(1)\n check('verified fixture recovery restores exact bytes', reopened.read() == initial)\n restored = native('writer-b', ['state', 'pull'])\n check('native observes restored lineage and payload', restored.returncode == 0 and json.loads(restored.stdout) == json.loads(pulled.stdout))\n with reopened.connection() as db:\n db.execute('UPDATE versions SET payload=? WHERE id=?', (b'corrupted-synthetic-backup', changed_id))\n try:\n reopened.restore_fixture_version(changed_id)\n refused = False\n except ValueError:\n refused = True\n check('corrupt fixture backup cannot replace good state', refused and reopened.read() == initial)\n abandoned = {'ID': str(uuid.uuid4()), 'Operation': 'synthetic-abandoned-writer'}\n reopened.acquire('writer-a', abandoned)\n restarted = FixtureStore(private / 'fixture.sqlite', b'not-a-new-seed')\n check('uncertain abandoned lock persists across reopen', restarted.lock()[1]['ID'] == abandoned['ID'])\n check('abandoned lock still needs matching owner and ID', restarted.unlock('writer-b', abandoned)[0] == 409 and restarted.unlock('writer-a', abandoned)[0] == 200)\n finally:\n control.pause = False\n control.release.set()\n if active is not None and active.poll() is None:\n active.terminate()\n try:\n active.communicate(timeout=5)\n except subprocess.TimeoutExpired:\n active.kill()\n active.communicate(timeout=5)\n server.shutdown()\n server.server_close()\n thread.join(timeout=5)\n report = {'passed': all(c['passed'] for c in checks), 'scope': 'isolated-loopback-http-state-protocol',\n 'sourceSha256': digest(Path(__file__).read_bytes()), 'terraformVersion': version['terraform_version'],\n 'terraformSha256': digest(terraform.read_bytes()), 'checks': checks, 'commands': commands,\n 'initialSyntheticStateSha256': digest(initial), 'selectedBackendVerified': False,\n 'migrationAuthorized': False, 'boundary': 'Actual Terraform init/plan/state pull against a synthetic loopback HTTP fixture and local SQLite version/lock store. No apply, project backend, TLS, cloud authentication, S3/Azure/GCS behavior, migration or application-system qualification.'}\n serialized = json.dumps(report, indent=2) + '\\n'\n if any(secret in serialized for secret in credentials.values()):\n raise ValueError('Credential appeared in public fixture evidence')\n (folder / 'report.json').write_text(serialized)\n return report\n\n\nif __name__ == '__main__':\n parser = argparse.ArgumentParser(description=__doc__)\n parser.add_argument('--terraform', type=Path, default=shutil.which('terraform'))\n parser.add_argument('--work-dir', type=Path, required=True)\n args = parser.parse_args()\n if not args.terraform:\n parser.error('Install a local Terraform executable or provide --terraform.')\n result = run_probe(Path(args.terraform).resolve(strict=True), args.work_dir.resolve())\n print(json.dumps({'passed': result['passed'], 'checks': len(result['checks']), 'report': str(args.work_dir / 'report.json'), 'selectedBackendVerified': False}))\n",
3
+ "sha256": "c82017b8ef1cf30113d0e0bd0c28268b975cf54ab2208261b7c5610b57a96b28"
4
+ }
@@ -0,0 +1,338 @@
1
+ """Local synthetic HTTP-state protocol check; never opens a project backend.
2
+
3
+ This fixture is not a production state service. All state, credentials, databases
4
+ and native workspaces are created in one fresh directory. No apply, migration,
5
+ provider, cloud credential or caller-supplied endpoint is used.
6
+ """
7
+ import argparse
8
+ import base64
9
+ import contextlib
10
+ import hashlib
11
+ import http.server
12
+ import json
13
+ import os
14
+ from pathlib import Path
15
+ import secrets
16
+ import shutil
17
+ import sqlite3
18
+ import subprocess
19
+ import threading
20
+ import urllib.error
21
+ import urllib.parse
22
+ import urllib.request
23
+ import uuid
24
+
25
+
26
+ def encoded(value):
27
+ return json.dumps(value, sort_keys=True, separators=(',', ':')).encode()
28
+
29
+
30
+ def digest(value):
31
+ return hashlib.sha256(value).hexdigest()
32
+
33
+
34
+ class FixtureStore:
35
+ def __init__(self, path, initial):
36
+ self.path = path
37
+ with self.connection() as db:
38
+ db.executescript('''CREATE TABLE IF NOT EXISTS versions(
39
+ id INTEGER PRIMARY KEY, payload BLOB NOT NULL, sha TEXT NOT NULL);
40
+ CREATE TABLE IF NOT EXISTS head(id INTEGER PRIMARY KEY CHECK(id=1),version INTEGER NOT NULL);
41
+ CREATE TABLE IF NOT EXISTS lease(id INTEGER PRIMARY KEY CHECK(id=1),actor TEXT,info TEXT);''')
42
+ if not db.execute('SELECT 1 FROM head').fetchone():
43
+ db.execute('INSERT INTO versions VALUES(1,?,?)', (initial, digest(initial)))
44
+ db.execute('INSERT INTO head VALUES(1,1)')
45
+
46
+ @contextlib.contextmanager
47
+ def connection(self):
48
+ db = sqlite3.connect(self.path, timeout=5)
49
+ try:
50
+ db.execute('PRAGMA journal_mode=WAL')
51
+ db.execute('PRAGMA synchronous=FULL')
52
+ db.execute('BEGIN IMMEDIATE')
53
+ yield db
54
+ db.commit()
55
+ except BaseException:
56
+ db.rollback()
57
+ raise
58
+ finally:
59
+ db.close()
60
+
61
+ def read(self):
62
+ with self.connection() as db:
63
+ row = db.execute('SELECT payload,sha FROM versions JOIN head ON versions.id=head.version').fetchone()
64
+ if digest(row[0]) != row[1]:
65
+ raise ValueError('Fixture state digest mismatch')
66
+ return row[0]
67
+
68
+ def lock(self):
69
+ with self.connection() as db:
70
+ row = db.execute('SELECT actor,info FROM lease').fetchone()
71
+ return (row[0], json.loads(row[1])) if row else None
72
+
73
+ def acquire(self, actor, info):
74
+ if not isinstance(info.get('ID'), str) or not info['ID'] or len(info['ID']) > 100:
75
+ return 400, {}
76
+ with self.connection() as db:
77
+ row = db.execute('SELECT actor,info FROM lease').fetchone()
78
+ if row:
79
+ return 409, json.loads(row[1])
80
+ db.execute('INSERT INTO lease VALUES(1,?,?)', (actor, json.dumps(info)))
81
+ return 200, {}
82
+
83
+ def unlock(self, actor, info):
84
+ with self.connection() as db:
85
+ row = db.execute('SELECT actor,info FROM lease').fetchone()
86
+ if not row:
87
+ return 409, {}
88
+ current = json.loads(row[1])
89
+ if actor != row[0] or info.get('ID') != current['ID']:
90
+ return 409, current
91
+ db.execute('DELETE FROM lease')
92
+ return 200, {}
93
+
94
+ def denied_write(self, actor, lock_id):
95
+ with self.connection() as db:
96
+ row = db.execute('SELECT actor,info FROM lease').fetchone()
97
+ # Fixture intentionally has no HTTP write implementation. Plans and
98
+ # state pull must not write; only the test controller adds versions.
99
+ if not row or row[0] != actor or json.loads(row[1])['ID'] != lock_id:
100
+ return 409
101
+ return 405
102
+
103
+ def add_fixture_version(self, payload):
104
+ with self.connection() as db:
105
+ if db.execute('SELECT 1 FROM lease').fetchone():
106
+ raise ValueError('An active writer forbids fixture version changes')
107
+ cursor = db.execute('INSERT INTO versions(payload,sha) VALUES(?,?)', (payload, digest(payload)))
108
+ db.execute('UPDATE head SET version=?', (cursor.lastrowid,))
109
+ return cursor.lastrowid
110
+
111
+ def restore_fixture_version(self, version):
112
+ with self.connection() as db:
113
+ if db.execute('SELECT 1 FROM lease').fetchone():
114
+ raise ValueError('An active writer forbids fixture recovery')
115
+ row = db.execute('SELECT payload,sha FROM versions WHERE id=?', (version,)).fetchone()
116
+ if not row or digest(row[0]) != row[1]:
117
+ raise ValueError('Unverified fixture backup')
118
+ json.loads(row[0])
119
+ db.execute('UPDATE head SET version=?', (version,))
120
+
121
+
122
+ class Control:
123
+ def __init__(self):
124
+ self.pause = False
125
+ self.read_held = threading.Event()
126
+ self.release = threading.Event()
127
+ self.events = []
128
+
129
+
130
+ def fixture_server(store, credentials, control):
131
+ class Handler(http.server.BaseHTTPRequestHandler):
132
+ def log_message(self, *_):
133
+ pass
134
+
135
+ def respond(self, status, value=None):
136
+ body = value if isinstance(value, bytes) else encoded(value or {})
137
+ self.send_response(status)
138
+ self.send_header('Content-Type', 'application/json')
139
+ self.send_header('Content-Length', str(len(body)))
140
+ self.end_headers()
141
+ try:
142
+ self.wfile.write(body)
143
+ except (BrokenPipeError, ConnectionResetError):
144
+ pass
145
+
146
+ def actor(self):
147
+ header = self.headers.get('Authorization', '')
148
+ for actor, password in credentials.items():
149
+ expected = 'Basic ' + base64.b64encode((actor + ':' + password).encode()).decode()
150
+ if secrets.compare_digest(header, expected):
151
+ return actor
152
+ return None
153
+
154
+ def dispatch(self):
155
+ actor = self.actor()
156
+ route = urllib.parse.urlsplit(self.path)
157
+ if not actor:
158
+ self.respond(401)
159
+ return
160
+ control.events.append({'method': self.command, 'path': route.path, 'actor': actor})
161
+ if self.command == 'GET' and route.path == '/state':
162
+ lease = store.lock()
163
+ if control.pause and lease and lease[0] == actor:
164
+ control.read_held.set()
165
+ if not control.release.wait(25):
166
+ self.respond(503)
167
+ return
168
+ self.respond(200, store.read())
169
+ return
170
+ length = int(self.headers.get('Content-Length', '0'))
171
+ if length < 0 or length > 65536:
172
+ self.respond(413)
173
+ return
174
+ try:
175
+ value = json.loads(self.rfile.read(length)) if length else {}
176
+ except ValueError:
177
+ self.respond(400)
178
+ return
179
+ if not isinstance(value, dict):
180
+ self.respond(400)
181
+ return
182
+ if route.path == '/lock' and self.command in {'POST', 'DELETE'}:
183
+ status, response = (store.acquire if self.command == 'POST' else store.unlock)(actor, value)
184
+ self.respond(status, response)
185
+ elif route.path == '/state' and self.command == 'POST':
186
+ lock_id = urllib.parse.parse_qs(route.query).get('ID', [None])[0]
187
+ self.respond(store.denied_write(actor, lock_id))
188
+ else:
189
+ self.respond(405)
190
+
191
+ do_GET = dispatch
192
+ do_POST = dispatch
193
+ do_DELETE = dispatch
194
+
195
+ server = http.server.ThreadingHTTPServer(('127.0.0.1', 0), Handler)
196
+ server.daemon_threads = True
197
+ return server
198
+
199
+
200
+ def run_probe(terraform, folder):
201
+ folder.mkdir(parents=True, exist_ok=False)
202
+ private = folder / 'private'
203
+ private.mkdir(mode=0o700)
204
+ os.chmod(folder, 0o700)
205
+ credentials = {actor: secrets.token_urlsafe(32) for actor in ['writer-a', 'writer-b']}
206
+ base_env = {'PATH': str(terraform.parent), 'HOME': str(private), 'TMPDIR': str(private),
207
+ 'TF_IN_AUTOMATION': '1', 'CHECKPOINT_DISABLE': '1', 'TF_INPUT': '0',
208
+ 'TF_HTTP_RETRY_MAX': '0', 'AWS_EC2_METADATA_DISABLED': 'true'}
209
+ cli = private / 'empty.tfrc'
210
+ cli.write_text('provider_installation { filesystem_mirror { path = ' + json.dumps(str(private)) + ' } }\n')
211
+ base_env['TF_CLI_CONFIG_FILE'] = str(cli)
212
+ version = json.loads(subprocess.check_output([str(terraform), 'version', '-json'], env=base_env, timeout=15))
213
+ initial = encoded({'version': 4, 'terraform_version': version['terraform_version'], 'serial': 1,
214
+ 'lineage': str(uuid.uuid4()), 'outputs': {'fixture': {'value': 'synthetic-state', 'type': 'string'}}, 'resources': []})
215
+ store = FixtureStore(private / 'fixture.sqlite', initial)
216
+ control = Control()
217
+ server = fixture_server(store, credentials, control)
218
+ base = 'http://127.0.0.1:' + str(server.server_port)
219
+ thread = threading.Thread(target=server.serve_forever, daemon=True)
220
+ thread.start()
221
+ checks, commands = [], []
222
+ active = None
223
+
224
+ def check(name, condition):
225
+ checks.append({'name': name, 'passed': bool(condition)})
226
+ if not condition:
227
+ raise AssertionError(name)
228
+
229
+ def environment(actor):
230
+ return {**base_env, 'TF_HTTP_USERNAME': actor, 'TF_HTTP_PASSWORD': credentials[actor]}
231
+
232
+ def native(actor, arguments, expected_exit=0):
233
+ result = subprocess.run([str(terraform), *arguments], cwd=private / actor,
234
+ env=environment(actor), capture_output=True, text=True, timeout=20)
235
+ commands.append({'actor': actor, 'arguments': arguments, 'exitCode': result.returncode, 'expectedExitCode': expected_exit})
236
+ (private / ('command-' + str(len(commands)) + '.log')).write_text(result.stdout + '\n' + result.stderr)
237
+ return result
238
+
239
+ def request(actor, method, path, value=None, password=None):
240
+ header = base64.b64encode((actor + ':' + (password or credentials[actor])).encode()).decode()
241
+ req = urllib.request.Request(base + path, data=encoded(value) if value is not None else None,
242
+ method=method, headers={'Authorization': 'Basic ' + header})
243
+ opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))
244
+ try:
245
+ with opener.open(req, timeout=5) as response:
246
+ return response.status
247
+ except urllib.error.HTTPError as error:
248
+ return error.code
249
+
250
+ try:
251
+ for actor in credentials:
252
+ workspace = private / actor
253
+ workspace.mkdir()
254
+ (workspace / 'main.tf.json').write_text(json.dumps({'terraform': {'required_version': '>= 1.10.0, < 2.0.0', 'backend': {'http': {}}}, 'output': {'fixture': {'value': 'synthetic-state'}}}))
255
+ (workspace / 'backend.hcl').write_text('address = ' + json.dumps(base + '/state') + '\nlock_address = ' + json.dumps(base + '/lock') + '\nunlock_address = ' + json.dumps(base + '/lock') + '\nlock_method = "POST"\nunlock_method = "DELETE"\n')
256
+ check('native init ' + actor, native(actor, ['init', '-input=false', '-no-color', '-backend-config=backend.hcl']).returncode == 0)
257
+ pulled = native('writer-a', ['state', 'pull'])
258
+ check('native reads synthetic lineage and payload', pulled.returncode == 0 and all(json.loads(pulled.stdout).get(k) == json.loads(initial)[k] for k in ['version', 'serial', 'lineage', 'outputs', 'resources']))
259
+ check('unknown credential denied', request('writer-a', 'GET', '/state', password='invalid-fixture-password') == 401)
260
+ control.pause = True
261
+ active = subprocess.Popen([str(terraform), 'plan', '-input=false', '-no-color', '-lock-timeout=0s'], cwd=private / 'writer-a', env=environment('writer-a'), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
262
+ check('real native plan holds lock while reading', control.read_held.wait(12) and active.poll() is None and store.lock() is not None)
263
+ current = store.lock()[1]
264
+ competing = native('writer-b', ['plan', '-input=false', '-no-color', '-lock-timeout=0s'], expected_exit=1)
265
+ check('second native writer excluded', competing.returncode == 1 and 'Error acquiring the state lock' in competing.stderr + competing.stdout)
266
+ check('foreign actor cannot unlock owner', request('writer-b', 'DELETE', '/lock', {'ID': current['ID']}) == 409 and store.lock()[1]['ID'] == current['ID'])
267
+ check('incorrect lock ID cannot unlock owner', request('writer-a', 'DELETE', '/lock', {'ID': 'wrong-fixture-lock'}) == 409 and store.lock()[1]['ID'] == current['ID'])
268
+ check('foreign writer cannot replace state', request('writer-b', 'POST', '/state?ID=' + current['ID'], {}) == 409 and store.read() == initial)
269
+ try:
270
+ store.restore_fixture_version(1)
271
+ refused = False
272
+ except ValueError:
273
+ refused = True
274
+ check('recovery refuses an active writer', refused)
275
+ control.pause = False
276
+ control.release.set()
277
+ output, _ = active.communicate(timeout=15)
278
+ (private / 'writer-a-plan.log').write_text(output)
279
+ commands.append({'actor': 'writer-a', 'arguments': ['plan', '-input=false', '-no-color', '-lock-timeout=0s'], 'exitCode': active.returncode, 'expectedExitCode': 0})
280
+ check('native plan succeeds and releases owned lock', active.returncode == 0 and store.lock() is None)
281
+ check('plan-only clients never write state', not any(e['method'] == 'POST' and e['path'] == '/state' and e['actor'] == 'writer-a' for e in control.events) and store.read() == initial)
282
+ changed = json.loads(initial)
283
+ changed['serial'] = 2
284
+ changed['outputs']['fixture']['value'] = 'synthetic-new-version'
285
+ changed_id = store.add_fixture_version(encoded(changed))
286
+ reopened = FixtureStore(private / 'fixture.sqlite', b'not-a-new-seed')
287
+ check('local fixture versions survive reopen', json.loads(reopened.read())['serial'] == 2)
288
+ reopened.restore_fixture_version(1)
289
+ check('verified fixture recovery restores exact bytes', reopened.read() == initial)
290
+ restored = native('writer-b', ['state', 'pull'])
291
+ check('native observes restored lineage and payload', restored.returncode == 0 and json.loads(restored.stdout) == json.loads(pulled.stdout))
292
+ with reopened.connection() as db:
293
+ db.execute('UPDATE versions SET payload=? WHERE id=?', (b'corrupted-synthetic-backup', changed_id))
294
+ try:
295
+ reopened.restore_fixture_version(changed_id)
296
+ refused = False
297
+ except ValueError:
298
+ refused = True
299
+ check('corrupt fixture backup cannot replace good state', refused and reopened.read() == initial)
300
+ abandoned = {'ID': str(uuid.uuid4()), 'Operation': 'synthetic-abandoned-writer'}
301
+ reopened.acquire('writer-a', abandoned)
302
+ restarted = FixtureStore(private / 'fixture.sqlite', b'not-a-new-seed')
303
+ check('uncertain abandoned lock persists across reopen', restarted.lock()[1]['ID'] == abandoned['ID'])
304
+ check('abandoned lock still needs matching owner and ID', restarted.unlock('writer-b', abandoned)[0] == 409 and restarted.unlock('writer-a', abandoned)[0] == 200)
305
+ finally:
306
+ control.pause = False
307
+ control.release.set()
308
+ if active is not None and active.poll() is None:
309
+ active.terminate()
310
+ try:
311
+ active.communicate(timeout=5)
312
+ except subprocess.TimeoutExpired:
313
+ active.kill()
314
+ active.communicate(timeout=5)
315
+ server.shutdown()
316
+ server.server_close()
317
+ thread.join(timeout=5)
318
+ report = {'passed': all(c['passed'] for c in checks), 'scope': 'isolated-loopback-http-state-protocol',
319
+ 'sourceSha256': digest(Path(__file__).read_bytes()), 'terraformVersion': version['terraform_version'],
320
+ 'terraformSha256': digest(terraform.read_bytes()), 'checks': checks, 'commands': commands,
321
+ 'initialSyntheticStateSha256': digest(initial), 'selectedBackendVerified': False,
322
+ 'migrationAuthorized': False, 'boundary': 'Actual Terraform init/plan/state pull against a synthetic loopback HTTP fixture and local SQLite version/lock store. No apply, project backend, TLS, cloud authentication, S3/Azure/GCS behavior, migration or application-system qualification.'}
323
+ serialized = json.dumps(report, indent=2) + '\n'
324
+ if any(secret in serialized for secret in credentials.values()):
325
+ raise ValueError('Credential appeared in public fixture evidence')
326
+ (folder / 'report.json').write_text(serialized)
327
+ return report
328
+
329
+
330
+ if __name__ == '__main__':
331
+ parser = argparse.ArgumentParser(description=__doc__)
332
+ parser.add_argument('--terraform', type=Path, default=shutil.which('terraform'))
333
+ parser.add_argument('--work-dir', type=Path, required=True)
334
+ args = parser.parse_args()
335
+ if not args.terraform:
336
+ parser.error('Install a local Terraform executable or provide --terraform.')
337
+ result = run_probe(Path(args.terraform).resolve(strict=True), args.work_dir.resolve())
338
+ print(json.dumps({'passed': result['passed'], 'checks': len(result['checks']), 'report': str(args.work_dir / 'report.json'), 'selectedBackendVerified': False}))
@@ -0,0 +1 @@
1
+ "\"\"\"Provider-neutral flat output boundary. Never executes model-supplied actions.\"\"\"\nimport json\nimport math\n\nclass InvalidModelOutput(ValueError):\n pass\n\ndef _object(pairs):\n result = {}\n for key, value in pairs:\n if key in result:\n raise InvalidModelOutput('Duplicate field')\n result[key] = value\n return result\n\ndef decode_output(text, fields):\n if not isinstance(text, str):\n raise InvalidModelOutput('Expected JSON text')\n try:\n if len(text.encode('utf-8')) > 131072:\n raise InvalidModelOutput('Oversized output')\n value = json.loads(text, object_pairs_hook=_object,\n parse_constant=lambda _: (_ for _ in ()).throw(InvalidModelOutput('Non-finite number')))\n except (ValueError, UnicodeError, RecursionError) as error:\n raise InvalidModelOutput('Invalid JSON output') from None\n if not isinstance(value, dict) or set(value) != {f['name'] for f in fields}:\n raise InvalidModelOutput('Output fields do not match contract')\n for field in fields:\n item = value[field['name']]\n kind = field['type']\n if kind == 'string':\n if not isinstance(item, str) or len(item) > field.get('maxLength', 4096):\n raise InvalidModelOutput('Invalid string output')\n try:\n item.encode('utf-8')\n except UnicodeError:\n raise InvalidModelOutput('Invalid Unicode output') from None\n if 'values' in field and item not in field['values']:\n raise InvalidModelOutput('Value outside enumeration')\n elif kind == 'boolean':\n if type(item) is not bool:\n raise InvalidModelOutput('Invalid boolean output')\n elif kind == 'number':\n if type(item) not in (int, float) or abs(item) > 1e308 or not math.isfinite(item):\n raise InvalidModelOutput('Invalid numeric output')\n else:\n raise InvalidModelOutput('Unsupported output contract')\n return value\n"
@@ -0,0 +1,71 @@
1
+ 'use strict';
2
+ const byId=id=>document.getElementById(id);
3
+ let config,accessToken=null,expiresAt=0,pending=null,sessionEpoch=0;
4
+ const status=text=>{byId('status').textContent=text;};
5
+ const signedIn=()=>!!accessToken&&Date.now()<expiresAt;
6
+ function ready(){
7
+ const enabled=signedIn();byId('question').disabled=!enabled;byId('submit').disabled=!enabled||!!pending;
8
+ byId('sign-in').hidden=enabled;byId('sign-out').hidden=!enabled;
9
+ }
10
+ function clearPrivateView(){byId('question').value='';byId('answer').replaceChildren();byId('citations').replaceChildren();}
11
+ function endSession(){sessionEpoch++;accessToken=null;expiresAt=0;if(pending)pending.abort();pending=null;sessionStorage.removeItem('atmem-oauth');clearPrivateView();ready();}
12
+ const random=()=>Array.from(crypto.getRandomValues(new Uint8Array(32)),b=>b.toString(16).padStart(2,'0')).join('');
13
+ function endpoint(value,fallback){const result=new URL(value||fallback,config.authOrigin);if(result.protocol!=='https:')throw Error('Invalid sign-in endpoint');return result;}
14
+ async function signIn(){
15
+ const verifier=random(),state=random();sessionStorage.setItem('atmem-oauth',JSON.stringify({verifier,state,created:Date.now()}));
16
+ const digest=new Uint8Array(await crypto.subtle.digest('SHA-256',new TextEncoder().encode(verifier)));
17
+ const challenge=btoa(String.fromCharCode(...digest)).replaceAll('+','-').replaceAll('/','_').replaceAll('=','');
18
+ const url=endpoint(config.authorizationEndpoint,'/oauth2/authorize');
19
+ url.search=new URLSearchParams({client_id:config.clientId,response_type:'code',scope:config.scopes||'openid atmem/ask',redirect_uri:location.origin+'/',state,code_challenge:challenge,code_challenge_method:'S256'});location.assign(url);
20
+ }
21
+ async function initialize(){
22
+ const response=await fetch('/config.json',{cache:'no-store'});if(!response.ok)throw Error('Configuration unavailable');config=await response.json();
23
+ if(new URL(config.authOrigin).protocol!=='https:'||typeof config.clientId!=='string'||!Number.isInteger(config.maxInputBytes)||config.maxInputBytes<1||config.maxInputBytes>1000000)throw Error('Invalid configuration');
24
+ config.fields=JSON.parse(new TextDecoder().decode(Uint8Array.from(atob(config.fieldsBase64),c=>c.charCodeAt(0))));
25
+ if(!Array.isArray(config.fields)||!config.fields.length||config.fields.length>24||config.fields.some(f=>!f||typeof f.name!=='string'||typeof f.description!=='string'||!['string','boolean','number'].includes(f.type)))throw Error('Invalid result configuration');
26
+ byId('question').maxLength=config.maxInputBytes;
27
+ const query=new URLSearchParams(location.search),code=query.get('code');
28
+ if(query.has('error')){history.replaceState(null,'','/');sessionStorage.removeItem('atmem-oauth');throw Error('Sign-in was not completed');}
29
+ if(code){
30
+ const saved=JSON.parse(sessionStorage.getItem('atmem-oauth')||'null');sessionStorage.removeItem('atmem-oauth');history.replaceState(null,'','/');
31
+ if(!saved||saved.state!==query.get('state')||!Number.isFinite(saved.created)||Date.now()-saved.created>600000||saved.created>Date.now()+5000)throw Error('Sign-in state expired or mismatched');
32
+ const tokenResponse=await fetch(endpoint(config.tokenEndpoint,'/oauth2/token'),{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:new URLSearchParams({grant_type:'authorization_code',client_id:config.clientId,code,redirect_uri:location.origin+'/',code_verifier:saved.verifier})});
33
+ if(!tokenResponse.ok)throw Error('Sign-in could not be completed');const tokens=await tokenResponse.json();
34
+ if(typeof tokens.access_token!=='string'||!tokens.access_token||!Number.isFinite(tokens.expires_in)||tokens.expires_in<=0)throw Error('Invalid sign-in response');
35
+ accessToken=tokens.access_token;expiresAt=Date.now()+tokens.expires_in*1000;sessionEpoch++;ready();status('Signed in. Submit input for a result you can review.');
36
+ }else{ready();status('Sign in to use this workspace.');}
37
+ }
38
+ function renderResult(result){
39
+ if(!result.result||typeof result.result!=='object'||Array.isArray(result.result)||result.requiresHumanReview!==true)throw Error('The result could not be verified.');
40
+ const valid=Object.keys(result.result).length===config.fields.length&&config.fields.every(field=>{
41
+ const value=result.result[field.name];return typeof value===field.type&&(field.type!=='number'||Number.isFinite(value))&&(field.type!=='string'||Array.from(value).length<=(field.maxLength||4096))&&(!field.values||field.values.includes(value));
42
+ });
43
+ if(!valid)throw Error('The result could not be verified.');
44
+ const fragment=document.createDocumentFragment();
45
+ for(const field of config.fields){const value=result.result[field.name],label=document.createElement('dt'),item=document.createElement('dd');label.textContent=field.description;item.textContent=typeof value==='boolean'?(value?'Yes':'No'):String(value);fragment.append(label,item);}
46
+ byId('answer').replaceChildren(fragment);status('Result prepared. Review it before taking action.');
47
+ }
48
+ byId('sign-in').addEventListener('click',()=>signIn().catch(()=>status('Sign-in could not be started. Please retry.')));
49
+ byId('sign-out').addEventListener('click',()=>{
50
+ endSession();
51
+ if(config.localLogout===true){status('Workspace cleared. Your identity-provider session may still be active. Close this browser on a shared device.');return;}
52
+ const url=endpoint(config.logoutEndpoint,'/logout');url.search=new URLSearchParams({client_id:config.clientId,[config.logoutParameter||'logout_uri']:location.origin+'/'});location.assign(url);
53
+ });
54
+ byId('ask').addEventListener('submit',async event=>{
55
+ event.preventDefault();if(pending)return;
56
+ if(!signedIn()){endSession();status('Your session expired. Sign in again.');return;}
57
+ const text=byId('question').value.trim();if(!text||new TextEncoder().encode(text).length>config.maxInputBytes){status('Use input of at most '+config.maxInputBytes.toLocaleString()+' UTF-8 bytes.');return;}
58
+ const epoch=sessionEpoch,token=accessToken,controller=new AbortController();pending=controller;ready();byId('answer').replaceChildren();status('Preparing a result…');
59
+ const timer=setTimeout(()=>controller.abort(),config.requestDeadlineMs||28000);
60
+ try{
61
+ const response=await fetch('/api/ask',{method:'POST',headers:{'Content-Type':'application/json',Authorization:'Bearer '+token},body:JSON.stringify({text}),signal:controller.signal,cache:'no-store'});
62
+ if(epoch!==sessionEpoch||accessToken!==token)return;
63
+ if(response.status===401){endSession();status('Your session expired. Sign in again.');return;}
64
+ if(response.status===403){endSession();status('Access is unavailable or permissions changed. Sign in again or contact the app owner.');return;}
65
+ const result=await response.json();if(epoch!==sessionEpoch||accessToken!==token)return;
66
+ if(!response.ok){if(result.error==='data_retention_unverified')throw Error('This workspace needs its data-handling setup checked by the app owner. Your input was not sent to the AI.');throw Error('The AI result is unavailable or could not be verified. Please try again later.');}
67
+ renderResult(result);
68
+ }catch(error){if(epoch===sessionEpoch&&accessToken===token)status(error.name==='AbortError'?'The request timed out. Please try again later.':error.message);}
69
+ finally{clearTimeout(timer);if(pending===controller)pending=null;ready();}
70
+ });
71
+ initialize().catch(()=>{endSession();status('Sign-in or configuration could not be verified. Please retry sign-in.');});
@@ -0,0 +1 @@
1
+ "'use strict';\nconst byId=id=>document.getElementById(id);\nlet config,accessToken=null,expiresAt=0,pending=null,sessionEpoch=0;\nconst status=text=>{byId('status').textContent=text;};\nconst signedIn=()=>!!accessToken&&Date.now()<expiresAt;\nfunction ready(){\n const enabled=signedIn();byId('question').disabled=!enabled;byId('submit').disabled=!enabled||!!pending;\n byId('sign-in').hidden=enabled;byId('sign-out').hidden=!enabled;\n}\nfunction clearPrivateView(){byId('question').value='';byId('answer').replaceChildren();byId('citations').replaceChildren();}\nfunction endSession(){sessionEpoch++;accessToken=null;expiresAt=0;if(pending)pending.abort();pending=null;sessionStorage.removeItem('atmem-oauth');clearPrivateView();ready();}\nconst random=()=>Array.from(crypto.getRandomValues(new Uint8Array(32)),b=>b.toString(16).padStart(2,'0')).join('');\nfunction endpoint(value,fallback){const result=new URL(value||fallback,config.authOrigin);if(result.protocol!=='https:')throw Error('Invalid sign-in endpoint');return result;}\nasync function signIn(){\n const verifier=random(),state=random();sessionStorage.setItem('atmem-oauth',JSON.stringify({verifier,state,created:Date.now()}));\n const digest=new Uint8Array(await crypto.subtle.digest('SHA-256',new TextEncoder().encode(verifier)));\n const challenge=btoa(String.fromCharCode(...digest)).replaceAll('+','-').replaceAll('/','_').replaceAll('=','');\n const url=endpoint(config.authorizationEndpoint,'/oauth2/authorize');\n url.search=new URLSearchParams({client_id:config.clientId,response_type:'code',scope:config.scopes||'openid atmem/ask',redirect_uri:location.origin+'/',state,code_challenge:challenge,code_challenge_method:'S256'});location.assign(url);\n}\nasync function initialize(){\n const response=await fetch('/config.json',{cache:'no-store'});if(!response.ok)throw Error('Configuration unavailable');config=await response.json();\n if(new URL(config.authOrigin).protocol!=='https:'||typeof config.clientId!=='string'||!Number.isInteger(config.maxInputBytes)||config.maxInputBytes<1||config.maxInputBytes>1000000)throw Error('Invalid configuration');\n config.fields=JSON.parse(new TextDecoder().decode(Uint8Array.from(atob(config.fieldsBase64),c=>c.charCodeAt(0))));\n if(!Array.isArray(config.fields)||!config.fields.length||config.fields.length>24||config.fields.some(f=>!f||typeof f.name!=='string'||typeof f.description!=='string'||!['string','boolean','number'].includes(f.type)))throw Error('Invalid result configuration');\n byId('question').maxLength=config.maxInputBytes;\n const query=new URLSearchParams(location.search),code=query.get('code');\n if(query.has('error')){history.replaceState(null,'','/');sessionStorage.removeItem('atmem-oauth');throw Error('Sign-in was not completed');}\n if(code){\n const saved=JSON.parse(sessionStorage.getItem('atmem-oauth')||'null');sessionStorage.removeItem('atmem-oauth');history.replaceState(null,'','/');\n if(!saved||saved.state!==query.get('state')||!Number.isFinite(saved.created)||Date.now()-saved.created>600000||saved.created>Date.now()+5000)throw Error('Sign-in state expired or mismatched');\n const tokenResponse=await fetch(endpoint(config.tokenEndpoint,'/oauth2/token'),{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:new URLSearchParams({grant_type:'authorization_code',client_id:config.clientId,code,redirect_uri:location.origin+'/',code_verifier:saved.verifier})});\n if(!tokenResponse.ok)throw Error('Sign-in could not be completed');const tokens=await tokenResponse.json();\n if(typeof tokens.access_token!=='string'||!tokens.access_token||!Number.isFinite(tokens.expires_in)||tokens.expires_in<=0)throw Error('Invalid sign-in response');\n accessToken=tokens.access_token;expiresAt=Date.now()+tokens.expires_in*1000;sessionEpoch++;ready();status('Signed in. Submit input for a result you can review.');\n }else{ready();status('Sign in to use this workspace.');}\n}\nfunction renderResult(result){\n if(!result.result||typeof result.result!=='object'||Array.isArray(result.result)||result.requiresHumanReview!==true)throw Error('The result could not be verified.');\n const valid=Object.keys(result.result).length===config.fields.length&&config.fields.every(field=>{\n const value=result.result[field.name];return typeof value===field.type&&(field.type!=='number'||Number.isFinite(value))&&(field.type!=='string'||Array.from(value).length<=(field.maxLength||4096))&&(!field.values||field.values.includes(value));\n });\n if(!valid)throw Error('The result could not be verified.');\n const fragment=document.createDocumentFragment();\n for(const field of config.fields){const value=result.result[field.name],label=document.createElement('dt'),item=document.createElement('dd');label.textContent=field.description;item.textContent=typeof value==='boolean'?(value?'Yes':'No'):String(value);fragment.append(label,item);}\n byId('answer').replaceChildren(fragment);status('Result prepared. Review it before taking action.');\n}\nbyId('sign-in').addEventListener('click',()=>signIn().catch(()=>status('Sign-in could not be started. Please retry.')));\nbyId('sign-out').addEventListener('click',()=>{\n endSession();\n if(config.localLogout===true){status('Workspace cleared. Your identity-provider session may still be active. Close this browser on a shared device.');return;}\n const url=endpoint(config.logoutEndpoint,'/logout');url.search=new URLSearchParams({client_id:config.clientId,[config.logoutParameter||'logout_uri']:location.origin+'/'});location.assign(url);\n});\nbyId('ask').addEventListener('submit',async event=>{\n event.preventDefault();if(pending)return;\n if(!signedIn()){endSession();status('Your session expired. Sign in again.');return;}\n const text=byId('question').value.trim();if(!text||new TextEncoder().encode(text).length>config.maxInputBytes){status('Use input of at most '+config.maxInputBytes.toLocaleString()+' UTF-8 bytes.');return;}\n const epoch=sessionEpoch,token=accessToken,controller=new AbortController();pending=controller;ready();byId('answer').replaceChildren();status('Preparing a result…');\n const timer=setTimeout(()=>controller.abort(),config.requestDeadlineMs||28000);\n try{\n const response=await fetch('/api/ask',{method:'POST',headers:{'Content-Type':'application/json',Authorization:'Bearer '+token},body:JSON.stringify({text}),signal:controller.signal,cache:'no-store'});\n if(epoch!==sessionEpoch||accessToken!==token)return;\n if(response.status===401){endSession();status('Your session expired. Sign in again.');return;}\n if(response.status===403){endSession();status('Access is unavailable or permissions changed. Sign in again or contact the app owner.');return;}\n const result=await response.json();if(epoch!==sessionEpoch||accessToken!==token)return;\n if(!response.ok){if(result.error==='data_retention_unverified')throw Error('This workspace needs its data-handling setup checked by the app owner. Your input was not sent to the AI.');throw Error('The AI result is unavailable or could not be verified. Please try again later.');}\n renderResult(result);\n }catch(error){if(epoch===sessionEpoch&&accessToken===token)status(error.name==='AbortError'?'The request timed out. Please try again later.':error.message);}\n finally{clearTimeout(timer);if(pending===controller)pending=null;ready();}\n});\ninitialize().catch(()=>{endSession();status('Sign-in or configuration could not be verified. Please retry sign-in.');});\n"
@@ -0,0 +1,105 @@
1
+ """Provider-neutral HTTP boundary; identity and model transports are explicit adapters."""
2
+ import asyncio
3
+ import json
4
+ import threading
5
+ from pathlib import Path
6
+ from fastapi import FastAPI, Request
7
+ from fastapi.responses import FileResponse, JSONResponse
8
+ from starlette.concurrency import run_in_threadpool
9
+ from structured_output import _object
10
+
11
+ class PolicyUnverified(Exception):
12
+ pass
13
+
14
+ class AccessDenied(Exception):
15
+ pass
16
+
17
+ def create_app(config, verify, infer, public_config, static_root=None, token_header=None, protect_assets=False, require_origin=False, origin_validator=None):
18
+ root = Path(static_root or Path(__file__).parent / "web")
19
+ app = FastAPI(docs_url=None, redoc_url=None, openapi_url=None)
20
+ # Small internal apps admit a burst of five model requests per process.
21
+ # Capacity is bounded; excess callers receive a retryable 429, never a hidden
22
+ # unbounded queue. Destination quota/latency still needs real qualification.
23
+ slots = threading.BoundedSemaphore(5)
24
+
25
+ @app.middleware("http")
26
+ async def headers(request, next_handler):
27
+ denied = False
28
+ if protect_assets and request.url.path != "/healthz":
29
+ try:
30
+ await run_in_threadpool(verify, request.headers.get(token_header, ""))
31
+ except Exception:
32
+ denied = True
33
+ response = JSONResponse({"error": "access_denied"}, status_code=401) if denied else await next_handler(request)
34
+ response.headers.update({"Cache-Control": "no-store", "X-Content-Type-Options": "nosniff", "Referrer-Policy": "no-referrer",
35
+ "Content-Security-Policy": config.get("csp", "default-src 'self'; script-src 'self'; style-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'"), "Strict-Transport-Security": "max-age=31536000"})
36
+ return response
37
+
38
+ @app.get("/healthz")
39
+ def health():
40
+ # Startup liveness, not proof that identity/model/account policy is ready.
41
+ return {"status": "running"}
42
+
43
+ @app.get("/config.json")
44
+ def get_public_config():
45
+ return public_config()
46
+
47
+
48
+ @app.get("/")
49
+ def index():
50
+ return FileResponse(root / "index.html")
51
+
52
+ @app.get("/app.js")
53
+ def script():
54
+ return FileResponse(root / "app.js", media_type="text/javascript")
55
+
56
+ @app.get("/app.css")
57
+ def style():
58
+ return FileResponse(root / "app.css", media_type="text/css")
59
+
60
+ @app.post("/api/ask")
61
+ async def ask(request: Request):
62
+ origin = request.headers.get("origin")
63
+ origin_ok=origin_validator(origin,request.headers.get('host','')) if origin_validator else not ((require_origin and not origin) or (origin and origin != config["PUBLIC_ORIGIN"]))
64
+ if not origin_ok:
65
+ return JSONResponse({"error": "access_denied"}, status_code=403)
66
+ auth = request.headers.get("authorization", "")
67
+ token = request.headers.get(token_header, "") if token_header else auth[7:] if auth.startswith("Bearer ") else ""
68
+ try:
69
+ await run_in_threadpool(verify, token)
70
+ except Exception:
71
+ return JSONResponse({"error": "access_denied"}, status_code=401)
72
+ if request.headers.get("content-type", "").split(";")[0].lower() != "application/json":
73
+ return JSONResponse({"error": "invalid_request"}, status_code=400)
74
+ if not slots.acquire(blocking=False):
75
+ return JSONResponse({"error": "busy"}, status_code=429)
76
+ try:
77
+ raw = bytearray()
78
+ async with asyncio.timeout(5):
79
+ async for chunk in request.stream():
80
+ raw.extend(chunk)
81
+ if len(raw) > config["max_input"] * 6 + 100:
82
+ return JSONResponse({"error": "input_too_large"}, status_code=413)
83
+ body = json.loads(raw.decode("utf-8"), object_pairs_hook=_object)
84
+ text = body.get("text") if isinstance(body, dict) and set(body) == {"text"} else None
85
+ if not isinstance(text, str) or not text.strip() or len(text.encode("utf-8")) > config["max_input"]:
86
+ return JSONResponse({"error": "invalid_request"}, status_code=400)
87
+ try:
88
+ result = await run_in_threadpool(infer, text)
89
+ except (ValueError, UnicodeError, RecursionError):
90
+ return JSONResponse({"error": "invalid_model_output"}, status_code=503)
91
+ # Never return an inference result after expiry, or if injected verification fails.
92
+ await run_in_threadpool(verify, token)
93
+ return {"result": result, "requiresHumanReview": True}
94
+ except PolicyUnverified:
95
+ return JSONResponse({"error": "data_retention_unverified"}, status_code=503)
96
+ except AccessDenied:
97
+ return JSONResponse({"error": "access_denied"}, status_code=403)
98
+ except (ValueError, UnicodeError, RecursionError):
99
+ return JSONResponse({"error": "invalid_request_or_output"}, status_code=400)
100
+ except Exception:
101
+ return JSONResponse({"error": "temporarily_unavailable"}, status_code=503)
102
+ finally:
103
+ slots.release()
104
+ return app
105
+