@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.
Files changed (512) hide show
  1. package/CONSTITUTION.md +144 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +6 -0
  4. package/README.md +31 -2
  5. package/agent-skills/discovery/SKILL.md +48 -0
  6. package/agent-skills/discovery/topics.json +139 -0
  7. package/agent-skills/generation-capabilities.json +760 -0
  8. package/agent-skills/service-lifecycle.json +21 -0
  9. package/agent-skills/specialists/architecture/SKILL.md +15 -0
  10. package/agent-skills/specialists/aws/SKILL.md +17 -0
  11. package/agent-skills/specialists/azure/SKILL.md +7 -0
  12. package/agent-skills/specialists/composition/SKILL.md +17 -0
  13. package/agent-skills/specialists/devops/SKILL.md +10 -0
  14. package/agent-skills/specialists/finops/SKILL.md +7 -0
  15. package/agent-skills/specialists/gcp/SKILL.md +7 -0
  16. package/agent-skills/specialists/ibm/SKILL.md +7 -0
  17. package/agent-skills/specialists/manifest.json +13 -0
  18. package/agent-skills/specialists/pulumi/SKILL.md +7 -0
  19. package/agent-skills/specialists/security/SKILL.md +7 -0
  20. package/agent-skills/specialists/terraform/SKILL.md +14 -0
  21. package/agent-skills/upstream/aws/LICENSE +175 -0
  22. package/agent-skills/upstream/aws/SKILL.md +125 -0
  23. package/agent-skills/upstream/azure/LICENSE +21 -0
  24. package/agent-skills/upstream/azure/SKILL.md +387 -0
  25. package/agent-skills/upstream/gcp/LICENSE +202 -0
  26. package/agent-skills/upstream/gcp/SKILL.md +159 -0
  27. package/agent-skills/upstream/manifest.json +57 -0
  28. package/agent-skills/upstream/pulumi/LICENSE +201 -0
  29. package/agent-skills/upstream/pulumi/SKILL.md +501 -0
  30. package/agent-skills/upstream/terraform/LICENSE +373 -0
  31. package/agent-skills/upstream/terraform/SKILL.md +316 -0
  32. package/agent-skills/workflows/workflows.json +601 -0
  33. package/cli/architect.mjs +3 -0
  34. package/cli/runtime.mjs +115 -0
  35. package/dist/assets/html2canvas.esm-QH1iLAAe.js +22 -0
  36. package/dist/assets/index-BTLHaa3J.css +1 -0
  37. package/dist/assets/index-CQ2C1gKF.js +21338 -0
  38. package/dist/assets/index.es-DTyqIdaO.js +18 -0
  39. package/dist/assets/purify.es-CAi2OJFm.js +3 -0
  40. package/dist/assets/visual-export-Ox0j5xj5.js +173 -0
  41. package/dist/favicon.svg +2 -0
  42. package/dist/fonts/DM-Sans-OFL.txt +93 -0
  43. package/dist/fonts/Manrope-OFL.txt +93 -0
  44. package/dist/fonts/NotoSans-Regular.ttf +0 -0
  45. package/dist/fonts/OFL.txt +94 -0
  46. package/dist/fonts/dm-sans-latin-wght-normal.woff2 +0 -0
  47. package/dist/fonts/manrope-latin-wght-normal.woff2 +0 -0
  48. package/dist/index.html +4 -0
  49. package/dist/licenses/atmem-APACHE-2.0.txt +202 -0
  50. package/dist/licenses/aws-architect-LICENSE.txt +17 -0
  51. package/dist/licenses/aws-mcp-LICENSE.txt +175 -0
  52. package/dist/licenses/aws-mcp-NOTICE.txt +1 -0
  53. package/dist/licenses/cloud-emulation-notices.txt +36619 -0
  54. package/dist/licenses/dependency-notices.txt +6379 -0
  55. package/dist/licenses/emulation-notices.txt +717 -0
  56. package/dist/licenses/github-copilot-sdk-LICENSE.txt +21 -0
  57. package/dist/licenses/memory-dependency-notices.txt +3319 -0
  58. package/dist/licenses/openarchflow-LICENSE.txt +21 -0
  59. package/dist/licenses/python-dependency-notices.txt +11951 -0
  60. package/docs/upstream-lock.json +26 -0
  61. package/knowledge/engineering-practices.json +536 -0
  62. package/package.json +30 -4
  63. package/packaging/uv-release-lock.json +54 -0
  64. package/public/licenses/atmem-APACHE-2.0.txt +202 -0
  65. package/public/licenses/aws-architect-LICENSE.txt +17 -0
  66. package/public/licenses/aws-mcp-LICENSE.txt +175 -0
  67. package/public/licenses/aws-mcp-NOTICE.txt +1 -0
  68. package/public/licenses/cloud-emulation-notices.txt +36619 -0
  69. package/public/licenses/dependency-notices.txt +6379 -0
  70. package/public/licenses/emulation-notices.txt +717 -0
  71. package/public/licenses/github-copilot-sdk-LICENSE.txt +21 -0
  72. package/public/licenses/memory-dependency-notices.txt +3319 -0
  73. package/public/licenses/openarchflow-LICENSE.txt +21 -0
  74. package/public/licenses/python-dependency-notices.txt +11951 -0
  75. package/scripts/terraform-render.mjs +8 -0
  76. package/server/__init__.py +0 -0
  77. package/server/abstraction.py +28 -0
  78. package/server/agent_runtime.py +153 -0
  79. package/server/app.py +257 -0
  80. package/server/architect.py +112 -0
  81. package/server/architecture_activity.py +67 -0
  82. package/server/attachments.py +101 -0
  83. package/server/cloud-emulation-requirements.txt +29 -0
  84. package/server/codex_app_server.py +219 -0
  85. package/server/codex_engineering_backend.py +266 -0
  86. package/server/codex_worker_policy.py +193 -0
  87. package/server/contracts.py +315 -0
  88. package/server/copilot_worker.py +16 -0
  89. package/server/dependency_mirror.py +54 -0
  90. package/server/design_patch.py +43 -0
  91. package/server/emulation-requirements.txt +8 -0
  92. package/server/emulation_adapters.py +20 -0
  93. package/server/emulation_routes.py +98 -0
  94. package/server/emulation_worker.py +351 -0
  95. package/server/engineering_artifacts.py +221 -0
  96. package/server/engineering_assets/application-runtime.json +4372 -0
  97. package/server/engineering_assets/architecture_work.schema.json +79 -0
  98. package/server/engineering_assets/materialize_runtime.py +62 -0
  99. package/server/engineering_assets/prepare_tools.py +96 -0
  100. package/server/engineering_assets/sql_validation.py +59 -0
  101. package/server/engineering_contexts.py +120 -0
  102. package/server/engineering_delivery.py +210 -0
  103. package/server/engineering_finalization.py +210 -0
  104. package/server/engineering_handoff.py +172 -0
  105. package/server/engineering_jobs.py +206 -0
  106. package/server/engineering_knowledge.py +75 -0
  107. package/server/engineering_reconciliation.py +159 -0
  108. package/server/engineering_routes.py +483 -0
  109. package/server/engineering_sessions.py +273 -0
  110. package/server/engineering_tools.py +166 -0
  111. package/server/generation_lint.py +18 -0
  112. package/server/local_providers.py +185 -0
  113. package/server/local_validation.py +236 -0
  114. package/server/memory-requirements.lock +181 -0
  115. package/server/memory-requirements.txt +6 -0
  116. package/server/memory_context.py +58 -0
  117. package/server/memory_routes.py +141 -0
  118. package/server/memory_service.py +306 -0
  119. package/server/memory_worker.py +129 -0
  120. package/server/openai_voice.py +80 -0
  121. package/server/orca_handoff.py +90 -0
  122. package/server/packaged_app.py +9 -0
  123. package/server/pricing.py +190 -0
  124. package/server/process_control_observation.py +31 -0
  125. package/server/prompts.py +136 -0
  126. package/server/public_faq_emulation_worker.py +44 -0
  127. package/server/requirements.lock +1618 -0
  128. package/server/requirements.txt +68 -0
  129. package/server/runtime_dependency_locks.py +27 -0
  130. package/server/runtime_evidence.py +93 -0
  131. package/server/settings.py +14 -0
  132. package/server/skills.py +103 -0
  133. package/server/storage_emulation_worker.py +160 -0
  134. package/server/structured_emulation_worker.py +48 -0
  135. package/server/terraform_routes.py +118 -0
  136. package/server/upstream/__init__.py +0 -0
  137. package/server/upstream/agent/__init__.py +1 -0
  138. package/server/upstream/agent/aws_architect.py +600 -0
  139. package/server/upstream/agent/config/__init__.py +1 -0
  140. package/server/upstream/agent/config/agent_config.py +82 -0
  141. package/server/upstream/agent/config/prompts.py +138 -0
  142. package/server/upstream/agent/rate_limiter.py +62 -0
  143. package/server/upstream/utils/__init__.py +1 -0
  144. package/server/upstream/utils/file_writer.py +43 -0
  145. package/server/validation_applicability.py +85 -0
  146. package/server/validation_jobs.py +252 -0
  147. package/server/validation_workers.py +48 -0
  148. package/server/voice_routes.py +91 -0
  149. package/server/voice_worker.py +26 -0
  150. package/server/windows_snapshot.py +119 -0
  151. package/specs/028-convergence-evaluation/evidence/20261110-ibm-invoice-native/registry-structured/qualification.json +72 -0
  152. package/specs/028-convergence-evaluation/evidence/20261110-invoice-runtime/durable-current/receipt.json +61 -0
  153. package/specs/029-general-purpose-engine/evidence/20261111-runtime-dependencies/sdk/receipt.json +22 -0
  154. package/src/ArchitectureActivity.jsx +54 -0
  155. package/src/ArchitectureWorkPanel.jsx +39 -0
  156. package/src/ArtifactChecks.jsx +49 -0
  157. package/src/BlockRequirements.jsx +13 -0
  158. package/src/BrainActivity.jsx +11 -0
  159. package/src/ChatMessage.jsx +29 -0
  160. package/src/ChatResizeHandle.jsx +6 -0
  161. package/src/CodePreview.jsx +8 -0
  162. package/src/ComponentReview.jsx +31 -0
  163. package/src/DeliveryViews.jsx +59 -0
  164. package/src/EmulationPanel.jsx +44 -0
  165. package/src/EngagementViews.jsx +387 -0
  166. package/src/EngineeringPanel.jsx +151 -0
  167. package/src/FileTree.jsx +7 -0
  168. package/src/FinalChecklist.jsx +15 -0
  169. package/src/HelpNote.jsx +3 -0
  170. package/src/ImplementationDelivery.jsx +91 -0
  171. package/src/JourneyViews.jsx +403 -0
  172. package/src/LocationChoices.jsx +12 -0
  173. package/src/MemoryPanel.jsx +58 -0
  174. package/src/OpenAIVoicePanel.jsx +66 -0
  175. package/src/OutputsView.jsx +71 -0
  176. package/src/ProjectChecks.jsx +44 -0
  177. package/src/TerraformView.jsx +57 -0
  178. package/src/WorkbenchDialogs.jsx +532 -0
  179. package/src/activity-presentation.mjs +28 -0
  180. package/src/ai-qualification-scenarios.mjs +31 -0
  181. package/src/approval-application-files.mjs +32 -0
  182. package/src/approval-applications.mjs +82 -0
  183. package/src/approval-spec-kit.mjs +30 -0
  184. package/src/architecture-activity.css +28 -0
  185. package/src/architecture-activity.mjs +63 -0
  186. package/src/architecture-work.mjs +176 -0
  187. package/src/artifact-work.mjs +103 -0
  188. package/src/async-documents.mjs +191 -0
  189. package/src/atflow.css +1140 -0
  190. package/src/atforma.css +748 -0
  191. package/src/atmem-tokens.css +79 -0
  192. package/src/aws-preflight.mjs +60 -0
  193. package/src/aws-public-faq.mjs +102 -0
  194. package/src/aws-validation-scenarios.mjs +10 -0
  195. package/src/aws-validation.mjs +48 -0
  196. package/src/azure-plan-mocks.mjs +25 -0
  197. package/src/azure-structured.mjs +133 -0
  198. package/src/bedrock-summary.mjs +152 -0
  199. package/src/bounded-rag.mjs +60 -0
  200. package/src/brand.css +9 -0
  201. package/src/canvas-view.mjs +28 -0
  202. package/src/capability-composition.mjs +44 -0
  203. package/src/chat-attachments.mjs +13 -0
  204. package/src/cloud-provider-locks.mjs +9 -0
  205. package/src/cloud-storage-scenarios.mjs +2 -0
  206. package/src/component-review.mjs +55 -0
  207. package/src/composition-spec-kit.mjs +30 -0
  208. package/src/consultation-context.mjs +15 -0
  209. package/src/consultation.mjs +52 -0
  210. package/src/container-contracts.mjs +5 -0
  211. package/src/container-inspection.mjs +75 -0
  212. package/src/container-runtime.mjs +30 -0
  213. package/src/container-security-inspection.mjs +49 -0
  214. package/src/container-terraform.mjs +109 -0
  215. package/src/conversation.mjs +18 -0
  216. package/src/core.mjs +863 -0
  217. package/src/cross-cloud-storage.mjs +100 -0
  218. package/src/delivery-capabilities.mjs +25 -0
  219. package/src/delivery.mjs +58 -0
  220. package/src/deployment-ownership.mjs +10 -0
  221. package/src/deployment-region.mjs +11 -0
  222. package/src/derive.mjs +31 -0
  223. package/src/design-build-flow.mjs +10 -0
  224. package/src/diagram-attention.mjs +24 -0
  225. package/src/discovery.mjs +359 -0
  226. package/src/document.mjs +170 -0
  227. package/src/emulation-feedback.mjs +61 -0
  228. package/src/emulation-guidance.mjs +37 -0
  229. package/src/engagement.mjs +274 -0
  230. package/src/engineering-client.mjs +73 -0
  231. package/src/engineering-defaults.mjs +17 -0
  232. package/src/engineering.css +31 -0
  233. package/src/enterprise-application.mjs +76 -0
  234. package/src/enterprise-aws-host-infrastructure.mjs +62 -0
  235. package/src/enterprise-aws-host-tests.mjs +15 -0
  236. package/src/enterprise-aws-infrastructure-tests.mjs +38 -0
  237. package/src/enterprise-aws-infrastructure.mjs +138 -0
  238. package/src/enterprise-google-infrastructure.mjs +84 -0
  239. package/src/enterprise-google-tests.mjs +25 -0
  240. package/src/estimate.mjs +190 -0
  241. package/src/existing-references.mjs +18 -0
  242. package/src/external-identities.mjs +17 -0
  243. package/src/faq-lambda-build.mjs +28 -0
  244. package/src/final-package.mjs +34 -0
  245. package/src/gcp-structured.mjs +104 -0
  246. package/src/generation-provider.mjs +7 -0
  247. package/src/generation-quality.mjs +69 -0
  248. package/src/handoff.mjs +141 -0
  249. package/src/ibm-registry.mjs +12 -0
  250. package/src/ibm-structured.mjs +103 -0
  251. package/src/identifiers.mjs +15 -0
  252. package/src/implementation-map.mjs +14 -0
  253. package/src/implementation-outcome.mjs +8 -0
  254. package/src/incomplete-terraform.mjs +9 -0
  255. package/src/integrations.ts +137 -0
  256. package/src/interface-contracts.mjs +30 -0
  257. package/src/invoice-application.mjs +66 -0
  258. package/src/invoice-aws-operations.mjs +136 -0
  259. package/src/invoice-aws.mjs +143 -0
  260. package/src/invoice-azure-operations.mjs +147 -0
  261. package/src/invoice-azure.mjs +134 -0
  262. package/src/invoice-builder.mjs +35 -0
  263. package/src/invoice-dependencies.mjs +43 -0
  264. package/src/invoice-gcp-operations.mjs +178 -0
  265. package/src/invoice-gcp.mjs +104 -0
  266. package/src/invoice-ibm-operations.mjs +278 -0
  267. package/src/invoice-ibm.mjs +142 -0
  268. package/src/invoice-operations.mjs +143 -0
  269. package/src/invoice-plan.mjs +45 -0
  270. package/src/invoice-region.mjs +6 -0
  271. package/src/invoice-spec-kit.mjs +61 -0
  272. package/src/invoice-state.mjs +7 -0
  273. package/src/invoice-terraform.mjs +29 -0
  274. package/src/invoice-tests.mjs +122 -0
  275. package/src/journey-workspace.css +106 -0
  276. package/src/journey.mjs +97 -0
  277. package/src/levels.mjs +145 -0
  278. package/src/local-agent.mjs +184 -0
  279. package/src/main.jsx +2481 -0
  280. package/src/model-policy-preflight.mjs +34 -0
  281. package/src/openai-voice.mjs +102 -0
  282. package/src/output-bundle.mjs +13 -0
  283. package/src/ownership-inspection.mjs +28 -0
  284. package/src/pack.mjs +201 -0
  285. package/src/pricing.mjs +85 -0
  286. package/src/production-state.mjs +8 -0
  287. package/src/project-copy.mjs +10 -0
  288. package/src/project-file.mjs +160 -0
  289. package/src/project-links.mjs +3 -0
  290. package/src/protected-state.mjs +43 -0
  291. package/src/public-faq-builder.mjs +19 -0
  292. package/src/public-faq-clouds.mjs +251 -0
  293. package/src/public-faq.mjs +122 -0
  294. package/src/pulumi.mjs +123 -0
  295. package/src/python-container.mjs +25 -0
  296. package/src/python-runtime.mjs +50 -0
  297. package/src/rag-handler.mjs +43 -0
  298. package/src/rag-web-assets.mjs +52 -0
  299. package/src/rag-web.mjs +68 -0
  300. package/src/records.mjs +177 -0
  301. package/src/resources.mjs +163 -0
  302. package/src/review.mjs +252 -0
  303. package/src/runtime/EnterpriseDocument.java +89 -0
  304. package/src/runtime/approved_http_destination.py +72 -0
  305. package/src/runtime/aws-lambda-qualification-lock.json +7 -0
  306. package/src/runtime/aws-python-lock.json +82 -0
  307. package/src/runtime/aws-web-python-lock.json +887 -0
  308. package/src/runtime/aws_json_model.py +42 -0
  309. package/src/runtime/azure-python-lock.json +863 -0
  310. package/src/runtime/azure-sources.json +8 -0
  311. package/src/runtime/azure-test-lock.json +23 -0
  312. package/src/runtime/azure_acceptance.py +192 -0
  313. package/src/runtime/azure_structured.py +153 -0
  314. package/src/runtime/bounded_rag.py +126 -0
  315. package/src/runtime/catalog_admin.py +70 -0
  316. package/src/runtime/cloud-sql-proxy-lock.json +8 -0
  317. package/src/runtime/cloud-sql-proxy-security-review.json +30 -0
  318. package/src/runtime/connector_cli.py +50 -0
  319. package/src/runtime/connector_sync.py +70 -0
  320. package/src/runtime/durable-python-lock.json +247 -0
  321. package/src/runtime/durable_workflow.py +325 -0
  322. package/src/runtime/enterprise-application-manifest.json +110 -0
  323. package/src/runtime/enterprise-application-sources.json +266 -0
  324. package/src/runtime/enterprise-auth-python-lock.json +592 -0
  325. package/src/runtime/enterprise-aws-host-bootstrap-source.json +1 -0
  326. package/src/runtime/enterprise-aws-host-probe-source.json +1 -0
  327. package/src/runtime/enterprise-aws-task-template.json +235 -0
  328. package/src/runtime/enterprise-document-lock.json +18 -0
  329. package/src/runtime/enterprise-document.Dockerfile +15 -0
  330. package/src/runtime/enterprise-runtime-aws-python-lock.json +1115 -0
  331. package/src/runtime/enterprise-runtime-azure-python-lock.json +1106 -0
  332. package/src/runtime/enterprise-runtime-gcp-python-lock.json +1060 -0
  333. package/src/runtime/enterprise-runtime-ibm-python-lock.json +1096 -0
  334. package/src/runtime/enterprise-runtime-python-lock.json +1058 -0
  335. package/src/runtime/enterprise-runtime.Dockerfile +15 -0
  336. package/src/runtime/enterprise-web-app.js +1 -0
  337. package/src/runtime/enterprise-web-entry.js +105 -0
  338. package/src/runtime/enterprise-web-lock.json +29 -0
  339. package/src/runtime/enterprise-web-notices.txt +229 -0
  340. package/src/runtime/enterprise-web.css +4 -0
  341. package/src/runtime/enterprise-web.html +8 -0
  342. package/src/runtime/enterprise_audit.py +91 -0
  343. package/src/runtime/enterprise_audit_schema.sql +25 -0
  344. package/src/runtime/enterprise_auth_schema.sql +67 -0
  345. package/src/runtime/enterprise_auth_store.py +241 -0
  346. package/src/runtime/enterprise_aws_host_bootstrap.py +120 -0
  347. package/src/runtime/enterprise_aws_host_probe.py +105 -0
  348. package/src/runtime/enterprise_aws_hosts.py +136 -0
  349. package/src/runtime/enterprise_aws_jobs.py +300 -0
  350. package/src/runtime/enterprise_aws_objects.py +93 -0
  351. package/src/runtime/enterprise_aws_transfer.py +83 -0
  352. package/src/runtime/enterprise_azure_batch_api.py +128 -0
  353. package/src/runtime/enterprise_azure_host.py +599 -0
  354. package/src/runtime/enterprise_azure_jobs.py +294 -0
  355. package/src/runtime/enterprise_azure_objects.py +234 -0
  356. package/src/runtime/enterprise_azure_pool.py +161 -0
  357. package/src/runtime/enterprise_azure_transfer.py +122 -0
  358. package/src/runtime/enterprise_configuration.py +136 -0
  359. package/src/runtime/enterprise_document.py +116 -0
  360. package/src/runtime/enterprise_document_cli.py +32 -0
  361. package/src/runtime/enterprise_document_contract.py +54 -0
  362. package/src/runtime/enterprise_document_job.py +109 -0
  363. package/src/runtime/enterprise_document_jobs.py +122 -0
  364. package/src/runtime/enterprise_document_jobs_schema.sql +23 -0
  365. package/src/runtime/enterprise_extraction_cache.py +186 -0
  366. package/src/runtime/enterprise_extraction_schema.sql +17 -0
  367. package/src/runtime/enterprise_google_jobs.py +275 -0
  368. package/src/runtime/enterprise_google_transfer.py +85 -0
  369. package/src/runtime/enterprise_http.py +212 -0
  370. package/src/runtime/enterprise_identity.py +52 -0
  371. package/src/runtime/enterprise_knowledge.py +152 -0
  372. package/src/runtime/enterprise_models.py +94 -0
  373. package/src/runtime/enterprise_native.py +181 -0
  374. package/src/runtime/enterprise_oauth.py +132 -0
  375. package/src/runtime/enterprise_operator.py +160 -0
  376. package/src/runtime/enterprise_parser_service.py +136 -0
  377. package/src/runtime/enterprise_parser_transport.py +68 -0
  378. package/src/runtime/enterprise_queries.py +255 -0
  379. package/src/runtime/enterprise_queries_schema.sql +27 -0
  380. package/src/runtime/enterprise_query_service.py +111 -0
  381. package/src/runtime/enterprise_secrets.py +110 -0
  382. package/src/runtime/enterprise_sessions.py +95 -0
  383. package/src/runtime/enterprise_sources.py +260 -0
  384. package/src/runtime/enterprise_web.py +39 -0
  385. package/src/runtime/enterprise_worker.py +68 -0
  386. package/src/runtime/export_contract_acceptance.py +18 -0
  387. package/src/runtime/faq-output-contract.json +40 -0
  388. package/src/runtime/faq-web-app.js +41 -0
  389. package/src/runtime/faq-web.css +1 -0
  390. package/src/runtime/faq-web.html +6 -0
  391. package/src/runtime/gcp-sources.json +9 -0
  392. package/src/runtime/gcp_acceptance.py +110 -0
  393. package/src/runtime/gcp_structured.py +150 -0
  394. package/src/runtime/http_acceptance.py +39 -0
  395. package/src/runtime/iap-web-app.js +36 -0
  396. package/src/runtime/iap-web-source.json +1 -0
  397. package/src/runtime/ibm-python-lock.json +856 -0
  398. package/src/runtime/ibm-sources.json +9 -0
  399. package/src/runtime/ibm_acceptance.py +133 -0
  400. package/src/runtime/ibm_structured.py +113 -0
  401. package/src/runtime/invoice-azapi-provider-lock.json +7 -0
  402. package/src/runtime/invoice-ibm-provider-lock.json +7 -0
  403. package/src/runtime/invoice-parser-lock.json +1199 -0
  404. package/src/runtime/invoice-parser-python-lock.json +56 -0
  405. package/src/runtime/invoice-parser.Dockerfile +190 -0
  406. package/src/runtime/invoice-sources.json +58 -0
  407. package/src/runtime/invoice-test-sources.json +24 -0
  408. package/src/runtime/invoice-web.css +2 -0
  409. package/src/runtime/invoice-web.html +12 -0
  410. package/src/runtime/invoice-web.js +112 -0
  411. package/src/runtime/invoice_configuration.py +17 -0
  412. package/src/runtime/invoice_document.py +95 -0
  413. package/src/runtime/invoice_extraction.py +96 -0
  414. package/src/runtime/invoice_http.py +129 -0
  415. package/src/runtime/invoice_identity.py +5 -0
  416. package/src/runtime/invoice_maintenance.py +40 -0
  417. package/src/runtime/invoice_native.py +77 -0
  418. package/src/runtime/invoice_object_store.py +153 -0
  419. package/src/runtime/invoice_operations.py +108 -0
  420. package/src/runtime/invoice_operator.py +79 -0
  421. package/src/runtime/invoice_operator_core.py +39 -0
  422. package/src/runtime/invoice_operator_job.py +147 -0
  423. package/src/runtime/invoice_parser_cli.py +17 -0
  424. package/src/runtime/invoice_parser_identity.py +39 -0
  425. package/src/runtime/invoice_parser_service.py +89 -0
  426. package/src/runtime/invoice_parser_transport.py +89 -0
  427. package/src/runtime/invoice_processing.py +24 -0
  428. package/src/runtime/invoice_retention.py +16 -0
  429. package/src/runtime/invoice_runtime_secrets.py +43 -0
  430. package/src/runtime/invoice_stage.py +80 -0
  431. package/src/runtime/invoice_web.py +39 -0
  432. package/src/runtime/invoice_worker.py +50 -0
  433. package/src/runtime/lambda_faq.py +5 -0
  434. package/src/runtime/parser-requirements.lock +4 -0
  435. package/src/runtime/policy_review.py +18 -0
  436. package/src/runtime/postgres-qualification-lock.json +21 -0
  437. package/src/runtime/public-faq-sources.json +22 -0
  438. package/src/runtime/public_catalog_admin.py +122 -0
  439. package/src/runtime/public_catalog_store.py +125 -0
  440. package/src/runtime/public_faq.py +144 -0
  441. package/src/runtime/public_faq_app.py +49 -0
  442. package/src/runtime/python-container-lock.json +18 -0
  443. package/src/runtime/rag-sources.json +10 -0
  444. package/src/runtime/rag_acceptance.py +60 -0
  445. package/src/runtime/rag_aws_adapter.py +95 -0
  446. package/src/runtime/rds-ca-source.json +9 -0
  447. package/src/runtime/rds-global-ca.pem +2888 -0
  448. package/src/runtime/state-contract-check-source.json +4 -0
  449. package/src/runtime/state_contract_check.py +338 -0
  450. package/src/runtime/structured-source.json +1 -0
  451. package/src/runtime/structured-web-app.js +71 -0
  452. package/src/runtime/structured-web-source.json +1 -0
  453. package/src/runtime/structured_http.py +105 -0
  454. package/src/runtime/structured_output.py +48 -0
  455. package/src/runtime/web-python-lock.json +802 -0
  456. package/src/runtime/web_user_admin.py +44 -0
  457. package/src/runtime/workflow_application.py +62 -0
  458. package/src/runtime/workflow_bindings.py +128 -0
  459. package/src/runtime/workflow_configuration.py +40 -0
  460. package/src/runtime/workflow_connection.py +105 -0
  461. package/src/runtime/workflow_contract.py +54 -0
  462. package/src/runtime/workflow_database.py +94 -0
  463. package/src/runtime/workflow_errors.py +6 -0
  464. package/src/runtime/workflow_http.py +178 -0
  465. package/src/runtime/workflow_identity.py +69 -0
  466. package/src/runtime/workflow_payload.py +118 -0
  467. package/src/runtime/workflow_process_stage.py +88 -0
  468. package/src/runtime/workflow_profile.py +91 -0
  469. package/src/runtime/workflow_review.py +106 -0
  470. package/src/runtime/workflow_role_bootstrap.py +104 -0
  471. package/src/runtime/workflow_schema.sql +72 -0
  472. package/src/runtime/workflow_worker.py +142 -0
  473. package/src/runtime-settings.mjs +15 -0
  474. package/src/s3-access.mjs +14 -0
  475. package/src/source-contracts.mjs +17 -0
  476. package/src/spec-kit.mjs +186 -0
  477. package/src/state-custody.mjs +32 -0
  478. package/src/state-inspection.mjs +45 -0
  479. package/src/state-spec-kit.mjs +13 -0
  480. package/src/storage-tests.mjs +25 -0
  481. package/src/structured-api.mjs +133 -0
  482. package/src/structured-contract.mjs +19 -0
  483. package/src/structured-operation-policy.mjs +2 -0
  484. package/src/structured-web.mjs +17 -0
  485. package/src/style.css +1 -0
  486. package/src/system-description.mjs +26 -0
  487. package/src/terraform-admission.mjs +66 -0
  488. package/src/terraform-composition.mjs +83 -0
  489. package/src/terraform-expressions.mjs +46 -0
  490. package/src/terraform-validation-coverage.mjs +11 -0
  491. package/src/terraform.mjs +128 -0
  492. package/src/text-statistics.mjs +51 -0
  493. package/src/upstream/openarchflow/format-payload.ts +26 -0
  494. package/src/upstream/openarchflow/import/mermaid.ts +205 -0
  495. package/src/upstream/openarchflow/import/sql-ddl.ts +109 -0
  496. package/src/upstream/openarchflow/layout.ts +54 -0
  497. package/src/upstream/openarchflow/providers/aws.ts +196 -0
  498. package/src/upstream/openarchflow/providers/azure.ts +126 -0
  499. package/src/upstream/openarchflow/providers/cloudnative.ts +71 -0
  500. package/src/upstream/openarchflow/providers/generic.ts +227 -0
  501. package/src/upstream/openarchflow/providers/integration.ts +22 -0
  502. package/src/upstream/openarchflow/providers/types.ts +2 -0
  503. package/src/upstream/openarchflow/templates.ts +937 -0
  504. package/src/upstream-catalog.mjs +10 -0
  505. package/src/useChatScroll.jsx +37 -0
  506. package/src/useEmulation.jsx +36 -0
  507. package/src/useEngineeringStatus.mjs +17 -0
  508. package/src/validation-receipt.mjs +10 -0
  509. package/src/visual-export.mjs +92 -0
  510. package/src/voice-conversation.mjs +117 -0
  511. package/src/work-intake.mjs +24 -0
  512. package/src/workflow-runtime-inventory.mjs +3 -0
@@ -0,0 +1,153 @@
1
+ """Private immutable invoice objects with native conditional writes/version reads.
2
+
3
+ Account identity comes from the verified application membership, never an object
4
+ URL or a model. Adapters use operator-owned destinations/workload credentials.
5
+ Cloud IAM, versioning, retention and residency require matching infrastructure.
6
+ """
7
+ import base64
8
+ from email.utils import formatdate
9
+ import hashlib
10
+ import json
11
+ import re
12
+ import time
13
+ import threading
14
+ import uuid
15
+ from urllib.parse import quote
16
+ import requests
17
+
18
+ LIMIT=10000000
19
+ FIELDS={'provider','region','bucket','key','version','sha256'}
20
+ class ObjectUnavailable(RuntimeError):pass
21
+
22
+ def digest_pdf(raw):
23
+ if not isinstance(raw,bytes)or not raw.startswith(b'%PDF-')or len(raw)>LIMIT:raise ValueError('Supply a PDF no larger than 10 MB')
24
+ return hashlib.sha256(raw).hexdigest()
25
+
26
+ def object_key(account_id,digest):
27
+ if not isinstance(account_id,str)or str(uuid.UUID(account_id))!=account_id:raise ValueError('Use the verified canonical account identity')
28
+ if not isinstance(digest,str)or not re.fullmatch(r'[0-9a-f]{64}',digest):raise ValueError('Invalid invoice content digest')
29
+ return'accounts/'+account_id+'/invoices/'+digest+'.pdf'
30
+
31
+ class InvoiceObjects:
32
+ def _reference(self,account_id,digest,version):
33
+ if not isinstance(version,str)or not 1<=len(version)<=1024 or version=='null'or any(ord(c)<33 or ord(c)>126 for c in version):raise ObjectUnavailable('A native immutable object version is required')
34
+ if self.provider=='gcp'and not re.fullmatch(r'[1-9][0-9]{0,29}',version):raise ObjectUnavailable('Invalid native object generation')
35
+ return {'provider':self.provider,'region':self.region,'bucket':self.bucket,'key':object_key(account_id,digest),'version':version,'sha256':digest}
36
+ def _check(self,account_id,reference):
37
+ if not isinstance(reference,dict)or set(reference)!=FIELDS:raise ValueError('Use the exact owned object reference')
38
+ expected=self._reference(account_id,reference['sha256'],reference['version'])
39
+ if reference!=expected:raise PermissionError('Object reference is outside the account/destination boundary')
40
+ return expected
41
+ def _content(self,raw,reference):
42
+ if digest_pdf(raw)!=reference['sha256']:raise ObjectUnavailable('Original invoice digest changed')
43
+ return raw
44
+
45
+ class HttpInvoiceObjects(InvoiceObjects):
46
+ def __init__(self,provider,region,bucket,authorization,*,account=None,session=None):
47
+ if not isinstance(bucket,str)or not re.fullmatch(r'[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]',bucket)or'..'in bucket:raise ValueError('Invalid private document destination')
48
+ if not callable(authorization):raise ValueError('Explicit workload identity is required')
49
+ if not isinstance(region,str)or not re.fullmatch(r'[a-z][a-z0-9-]{1,40}',region):raise ValueError('Invalid document region')
50
+ self.provider=provider;self.region=region;self.bucket=bucket;self.authorization=authorization;self.session=session or requests.Session();self.session.trust_env=False
51
+ if provider=='azure':
52
+ if not isinstance(account,str)or not re.fullmatch(r'[a-z0-9]{3,24}',account)or'.'in bucket:raise ValueError('Invalid private Azure storage destination')
53
+ self.origin='https://'+account+'.blob.core.windows.net/'+bucket+'/'
54
+ elif provider=='ibm':
55
+ if not re.fullmatch(r'[a-z]{2}-[a-z]+',region):raise ValueError('Use an explicit regional IBM object destination')
56
+ self.origin='https://s3.'+region+'.cloud-object-storage.appdomain.cloud/'+bucket+'/'
57
+ elif provider=='gcp':
58
+ self.origin='https://storage.googleapis.com/storage/v1/b/'+bucket+'/o/'
59
+ self.upload='https://storage.googleapis.com/upload/storage/v1/b/'+bucket+'/o'
60
+ else:raise ValueError('Unsupported native private object adapter')
61
+ def _headers(self):
62
+ supplied=self.authorization();auth=supplied.get('Authorization')if isinstance(supplied,dict)else None
63
+ if not isinstance(auth,str)or not auth.startswith('Bearer ')or not 8<=len(auth)<=20000 or any(ord(c)<33 or ord(c)>126 for c in auth[7:]):raise PermissionError('Private object workload identity unavailable')
64
+ headers={'Authorization':auth,'Content-Type':'application/pdf'}
65
+ if self.provider=='azure':headers.update({'x-ms-version':'2023-11-03','x-ms-date':formatdate(usegmt=True)})
66
+ return headers
67
+ def _send(self,method,url,*,headers=None,params=None,data=None,limit=LIMIT):
68
+ actual={**self._headers(),**(headers or {})};deadline=time.monotonic()+15
69
+ try:
70
+ with self.session.request(method,url,headers=actual,params=params,data=data,timeout=(2,8),allow_redirects=False,stream=True)as response:
71
+ code=response.status_code
72
+ if code in {401,403}:raise PermissionError('Private object access denied')
73
+ if code in {409,412}:return code,b'',{}
74
+ if code not in {200,201}:raise ObjectUnavailable('Private object operation unavailable')
75
+ raw=bytearray()
76
+ for chunk in response.iter_content(16384):
77
+ raw.extend(chunk)
78
+ if len(raw)>limit or time.monotonic()>deadline:raise ObjectUnavailable('Private object response exceeded its boundary')
79
+ return code,bytes(raw),{key.lower():value for key,value in response.headers.items()}
80
+ except(PermissionError,ObjectUnavailable):raise
81
+ except Exception:raise ObjectUnavailable('Private object operation unavailable')from None
82
+ def _version(self,raw,headers):
83
+ if self.provider=='gcp':
84
+ try:
85
+ def unique(pairs):
86
+ result={}
87
+ for key,value in pairs:
88
+ if key in result:raise ValueError('Duplicate metadata field')
89
+ result[key]=value
90
+ return result
91
+ return json.loads(raw,object_pairs_hook=unique)['generation']
92
+ except(ValueError,TypeError,KeyError):raise ObjectUnavailable('Native object generation unavailable')from None
93
+ return headers.get('x-ms-version-id'if self.provider=='azure'else'x-amz-version-id')
94
+ def put(self,account_id,raw):
95
+ digest=digest_pdf(raw);key=object_key(account_id,digest)
96
+ if self.provider=='gcp':
97
+ code,body,headers=self._send('POST',self.upload,params={'uploadType':'media','name':key,'ifGenerationMatch':'0','fields':'generation'},data=raw,limit=8192)
98
+ else:
99
+ extra={'If-None-Match':'*','Content-MD5':base64.b64encode(hashlib.md5(raw,usedforsecurity=False).digest()).decode()}
100
+ if self.provider=='azure':extra['x-ms-blob-type']='BlockBlob'
101
+ code,body,headers=self._send('PUT',self.origin+quote(key,safe='/'),headers=extra,data=raw,limit=8192)
102
+ if code in {409,412}:
103
+ # A repeated delivery or lost upload acknowledgement must resolve the
104
+ # exact existing content/version, never overwrite it or guess a token.
105
+ if self.provider=='gcp':_,body,headers=self._send('GET',self.origin+quote(key,safe=''),params={'fields':'generation'},limit=8192)
106
+ else:_,body,headers=self._send('GET',self.origin+quote(key,safe='/'))
107
+ reference=self._reference(account_id,digest,self._version(body,headers))
108
+ # Re-read the pinned native version, including after a conflict. This
109
+ # also verifies missing versioning and mismatched object bytes fail closed.
110
+ self.load(account_id,reference)
111
+ return reference
112
+ def load(self,account_id,reference):
113
+ ref=self._check(account_id,reference);key=ref['key'];version=ref['version']
114
+ if self.provider=='gcp':url=self.origin+quote(key,safe='');params={'alt':'media','generation':version}
115
+ else:url=self.origin+quote(key,safe='/');params={'versionid'if self.provider=='azure'else'versionId':version}
116
+ code,raw,headers=self._send('GET',url,params=params)
117
+ if code!=200:raise ObjectUnavailable('Pinned original invoice unavailable')
118
+ version_header='x-goog-generation'if self.provider=='gcp'else'x-ms-version-id'if self.provider=='azure'else'x-amz-version-id'
119
+ if headers.get(version_header)!=version:raise ObjectUnavailable('Native original version mismatch')
120
+ return self._content(raw,ref)
121
+
122
+ class S3InvoiceObjects(InvoiceObjects):
123
+ def __init__(self,bucket,region,owner_account_id,*,client=None):
124
+ if not isinstance(bucket,str)or not re.fullmatch(r'[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]',bucket)or'..'in bucket or not isinstance(region,str)or not re.fullmatch(r'[a-z]{2}-[a-z]+-[0-9]',region)or not isinstance(owner_account_id,str)or not re.fullmatch(r'\d{12}',owner_account_id):raise ValueError('Invalid owned regional S3 invoice destination')
125
+ self.provider='aws';self.bucket=bucket;self.region=region;self.owner_account_id=owner_account_id;self.client=client;self._client_lock=threading.Lock()
126
+ def _client(self):
127
+ if self.client is None:
128
+ with self._client_lock:
129
+ if self.client is None:
130
+ import boto3
131
+ from botocore.config import Config
132
+ # A Session/client must not be constructed through boto3's
133
+ # shared default Session concurrently by first uploads.
134
+ self.client=boto3.Session().client('s3',region_name=self.region,endpoint_url='https://s3.'+self.region+'.amazonaws.com',config=Config(proxies={},connect_timeout=2,read_timeout=8,retries={'total_max_attempts':1}))
135
+ return self.client
136
+ def put(self,account_id,raw):
137
+ digest=digest_pdf(raw);key=object_key(account_id,digest);client=self._client()
138
+ try:reply=client.put_object(Bucket=self.bucket,Key=key,Body=raw,ContentType='application/pdf',ChecksumSHA256=base64.b64encode(hashlib.sha256(raw).digest()).decode(),ExpectedBucketOwner=self.owner_account_id,IfNoneMatch='*')
139
+ except Exception as error:
140
+ code=getattr(error,'response',{}).get('Error',{}).get('Code')
141
+ if code not in {'PreconditionFailed','ConditionalRequestConflict','412','409'}:raise ObjectUnavailable('Private object upload unavailable')from None
142
+ try:reply=client.head_object(Bucket=self.bucket,Key=key,ExpectedBucketOwner=self.owner_account_id)
143
+ except Exception:raise ObjectUnavailable('Existing original invoice unavailable')from None
144
+ reference=self._reference(account_id,digest,reply.get('VersionId'));self.load(account_id,reference);return reference
145
+ def load(self,account_id,reference):
146
+ ref=self._check(account_id,reference)
147
+ try:
148
+ reply=self._client().get_object(Bucket=self.bucket,Key=ref['key'],VersionId=ref['version'],ExpectedBucketOwner=self.owner_account_id);stream=reply['Body']
149
+ try:raw=stream.read(LIMIT+1)
150
+ finally:stream.close()
151
+ except Exception:raise ObjectUnavailable('Pinned original invoice unavailable')from None
152
+ if reply.get('VersionId')!=ref['version']:raise ObjectUnavailable('Native original version mismatch')
153
+ return self._content(raw,ref)
@@ -0,0 +1,108 @@
1
+ """Separate operator authority for retention, recovery and staff membership.
2
+
3
+ Never import this module into an HTTP handler. Runtime SQL logins cannot perform
4
+ these operations. Restore/reconciliation require paused, isolated workloads.
5
+ Expired-only cleanup serializes table mutation and refuses live jobs/leases;
6
+ ordinary active and younger operations are preserved during online maintenance.
7
+ All calls require an explicit transaction; receipts contain counts, not documents.
8
+ """
9
+ import hashlib
10
+ from pathlib import Path
11
+ import re
12
+ import uuid
13
+ from psycopg.rows import tuple_row
14
+ from psycopg.types.json import Jsonb
15
+ from durable_workflow import canonical
16
+ from workflow_database import SCHEMA_VERSION
17
+
18
+ def owned_scope(conn,account_id,namespace):
19
+ if conn.autocommit:raise ValueError('Use one operator transaction')
20
+ if not isinstance(account_id,str)or str(uuid.UUID(account_id))!=account_id:raise ValueError('Use the owned canonical account UUID')
21
+ if not isinstance(namespace,str)or not re.fullmatch(r'[a-zA-Z0-9-]{1,20}',namespace):raise ValueError('Use the owned workflow namespace')
22
+ with conn.cursor(row_factory=tuple_row)as cur:
23
+ authority=cur.execute("SELECT pg_has_role(current_user,nspowner,'USAGE') FROM pg_namespace WHERE nspname='workflow'").fetchone()
24
+ version=cur.execute('SELECT version,source_sha256,queue_version FROM workflow.schema_version').fetchone()
25
+ expected=hashlib.sha256((Path(__file__).parent/'workflow_schema.sql').read_bytes()).hexdigest()
26
+ if not authority or authority[0]is not True:raise PermissionError('Use the separate reviewed schema-owner/operator connection')
27
+ if version!=(SCHEMA_VERSION,expected,'3.10.0'):raise ValueError('Review the schema migration before operating this database')
28
+ conn.execute('SET LOCAL search_path=pg_catalog,workflow,pg_temp')
29
+ conn.execute("SET LOCAL lock_timeout='3s'");conn.execute("SET LOCAL statement_timeout='30s'")
30
+ conn.execute('SELECT pg_advisory_xact_lock(hashtextextended(%s,0))',('atmem-invoice-operations:'+account_id+':'+namespace,))
31
+ return namespace+'.extract',namespace+'.dispatch'
32
+
33
+ def quiescent_rows(conn,account_id,namespace,*,expired=False,restoring=False,limit=500):
34
+ extract,dispatch=owned_scope(conn,account_id,namespace)
35
+ if type(limit)is not int or not 1<=limit<=500:raise ValueError('Use a bounded maintenance batch')
36
+ # One table-lock order for every operator action. A running task retains a
37
+ # doing queue row even while its external network request is outside SQL.
38
+ conn.execute('LOCK TABLE workflow.operations IN SHARE ROW EXCLUSIVE MODE')
39
+ conn.execute('LOCK TABLE workflow.procrastinate_jobs IN SHARE ROW EXCLUSIVE MODE')
40
+ with conn.cursor(row_factory=tuple_row)as cur:
41
+ restoration_filter=" AND(state IN('approved','dispatching','processing')OR EXISTS(SELECT 1 FROM workflow.procrastinate_jobs j WHERE j.task_name=ANY(%s)AND j.args->>'operation_id'=workflow.operations.id::text))"if restoring else''
42
+ rows=cur.execute("SELECT id FROM workflow.operations WHERE account_id=%s AND workflow_key=%s"+(" AND created_at <= clock_timestamp()-interval '90 days'"if expired else'')+restoration_filter+' ORDER BY created_at,id LIMIT %s FOR UPDATE',(account_id,extract,*([[extract,dispatch]]if restoring else[]),limit)).fetchall()
43
+ ids=[str(row[0])for row in rows]
44
+ if ids:
45
+ live=cur.execute("SELECT EXISTS(SELECT 1 FROM workflow.procrastinate_jobs WHERE task_name=ANY(%s)AND args->>'operation_id'=ANY(%s)AND status='doing')OR EXISTS(SELECT 1 FROM workflow.operations WHERE id::text=ANY(%s)AND lease_until>clock_timestamp())",([extract,dispatch],ids,ids)).fetchone()[0]
46
+ if live and not restoring:raise ValueError('Settle live execution jobs/leases before expired-record maintenance')
47
+ return ids,extract,dispatch
48
+
49
+ def purge_expired(conn,*,account_id,namespace='invoice',limit=500):
50
+ ids,extract,dispatch=quiescent_rows(conn,account_id,namespace,expired=True,limit=limit)
51
+ if ids:
52
+ # Only owned task identities and exact operation IDs. Queue event rows
53
+ # cascade through the native library schema; no global queue truncation.
54
+ conn.execute("DELETE FROM workflow.procrastinate_jobs WHERE task_name=ANY(%s)AND args->>'operation_id'=ANY(%s)",([extract,dispatch],ids))
55
+ for table in ['events','results_history','operations']:
56
+ column='id'if table=='operations'else'operation_id'
57
+ conn.execute('DELETE FROM workflow.'+table+' WHERE account_id=%s AND '+column+'::text=ANY(%s)',(account_id,ids))
58
+ return{'purgedOperations':len(ids),'retentionDays':90,'batchLimit':limit,'moreMayRemain':len(ids)==limit,'originalObjects':'Native private-store lifecycle; provider deletion is asynchronous. No object-read or deletion authority is conferred on this SQL tool.'}
59
+
60
+ def quarantine_restored_deliveries(conn,*,account_id,workloads_stopped,namespace='invoice',limit=500):
61
+ # This explicit assertion is operator authorization, not a software proof
62
+ # that a cloud workload is stopped. Run only against the isolated restored
63
+ # database before changing application endpoints or starting any worker.
64
+ if workloads_stopped is not True:raise ValueError('Confirm workloads are stopped and the restored database is isolated')
65
+ ids,extract,dispatch=quiescent_rows(conn,account_id,namespace,restoring=True,limit=limit)
66
+ if not ids:return{'quarantinedDeliveries':0,'resetExtractions':0,'batchLimit':limit,'moreMayRemain':False}
67
+ # Keep payload and approval hashes. A restored database cannot know what the
68
+ # destination accepted after its recovery point; never infer no side effect.
69
+ with conn.cursor(row_factory=tuple_row)as cur:
70
+ rows=cur.execute("UPDATE workflow.operations SET state='unknown',lease_token=NULL,lease_until=NULL,execution_epoch=execution_epoch+1,updated_at=clock_timestamp() WHERE account_id=%s AND id::text=ANY(%s)AND state IN('approved','dispatching') RETURNING id,approved_hash",(account_id,ids)).fetchall()
71
+ for operation,digest in rows:
72
+ conn.execute("INSERT INTO workflow.events(operation_id,account_id,event,actor,detail)VALUES(%s,%s,'outcome_unknown','operator:restore',%s)",(operation,account_id,Jsonb({'reason':'database_restored','payloadSha256':digest})))
73
+ with conn.cursor(row_factory=tuple_row)as cur:
74
+ processing=cur.execute("UPDATE workflow.operations SET state='pending',lease_token=NULL,lease_until=NULL,execution_epoch=execution_epoch+1,updated_at=clock_timestamp() WHERE account_id=%s AND id::text=ANY(%s)AND state='processing' RETURNING id",(account_id,ids)).fetchall()
75
+ for operation, in processing:
76
+ conn.execute("INSERT INTO workflow.events(operation_id,account_id,event,actor,detail)VALUES(%s,%s,'recovered','operator:restore',%s)",(operation,account_id,Jsonb({'reason':'restored_extraction_fenced'})))
77
+ # All restored notifications may carry stale native doing/worker state.
78
+ # Pending extraction intent is reconstructed by reconcile_notifications after
79
+ # expiry cleanup. Unknown delivery intent is never automatically requeued.
80
+ conn.execute("DELETE FROM workflow.procrastinate_jobs WHERE task_name=ANY(%s)AND args->>'operation_id'=ANY(%s)",([extract,dispatch],ids))
81
+ return{'quarantinedDeliveries':len(rows),'resetExtractions':len(processing),'batchLimit':limit,'moreMayRemain':len(ids)==limit}
82
+
83
+ def reconcile_completed(conn,*,account_id,operation_id,payload_sha256,external_id,operator,namespace='invoice'):
84
+ extract,dispatch=owned_scope(conn,account_id,namespace)
85
+ if not isinstance(operation_id,str)or str(uuid.UUID(operation_id))!=operation_id:raise ValueError('Use the exact operation identity')
86
+ if not isinstance(payload_sha256,str)or not re.fullmatch(r'[0-9a-f]{64}',payload_sha256):raise ValueError('Use the independently checked approval digest')
87
+ if not isinstance(external_id,str)or not re.fullmatch(r'[A-Za-z0-9_.:/-]{1,200}',external_id):raise ValueError('Use the independently verified accounting receipt identity')
88
+ if not isinstance(operator,str)or not re.fullmatch(r'[A-Za-z0-9_.@:/-]{1,200}',operator):raise ValueError('Record the accountable operator')
89
+ conn.execute('LOCK TABLE workflow.operations IN SHARE ROW EXCLUSIVE MODE');conn.execute('LOCK TABLE workflow.procrastinate_jobs IN SHARE ROW EXCLUSIVE MODE')
90
+ with conn.cursor(row_factory=tuple_row)as cur:
91
+ row=cur.execute('SELECT state,approved_hash,kind,source_ref,destination_contract,result,result_revision,lease_until FROM workflow.operations WHERE account_id=%s AND id=%s AND workflow_key=%s FOR UPDATE',(account_id,operation_id,extract)).fetchone()
92
+ active=cur.execute("SELECT EXISTS(SELECT 1 FROM workflow.procrastinate_jobs WHERE task_name=%s AND args->>'operation_id'=%s AND status='doing')",(dispatch,operation_id)).fetchone()[0]
93
+ if not row or row[0]not in {'unknown','dead'}or row[1]!=payload_sha256 or active or row[7]is not None:raise ValueError('Reconcile only an inactive uncertain delivery with its exact approval digest')
94
+ snapshot={'kind':row[2],'source':row[3],'destination':row[4],'result':row[5],'revision':row[6]}
95
+ if hashlib.sha256(canonical(snapshot,140000)).hexdigest()!=payload_sha256:raise ValueError('Approved snapshot changed; do not invent completion')
96
+ receipt={'externalId':external_id,'operationId':operation_id,'payloadSha256':payload_sha256,'reconciledBy':operator}
97
+ conn.execute("UPDATE workflow.operations SET state='sent',receipt=%s,updated_at=clock_timestamp() WHERE id=%s",(Jsonb(receipt),operation_id))
98
+ conn.execute("INSERT INTO workflow.events(operation_id,account_id,event,actor,detail)VALUES(%s,%s,'sent',%s,%s)",(operation_id,account_id,'operator:'+operator,Jsonb({'payloadSha256':payload_sha256,'reconciled':True})))
99
+ conn.execute("DELETE FROM workflow.procrastinate_jobs WHERE task_name=%s AND args->>'operation_id'=%s",(dispatch,operation_id))
100
+ return{'state':'sent','receipt':receipt}
101
+
102
+ def set_membership(conn,*,account_id,subject,roles,active=True,namespace='invoice'):
103
+ owned_scope(conn,account_id,namespace)
104
+ if not isinstance(subject,str)or not 1<=len(subject.encode())<=500 or any(ord(c)<32 for c in subject):raise ValueError('Use the verified native login subject, never a display-name guess')
105
+ if type(active)is not bool or not isinstance(roles,list)or not roles or len(set(roles))!=len(roles)or not set(roles)<={'user','reviewer','supervisor','owner'}:raise ValueError('Use an explicit reviewed role set')
106
+ conn.execute('INSERT INTO workflow.accounts(id)VALUES(%s)ON CONFLICT DO NOTHING',(account_id,))
107
+ conn.execute('INSERT INTO workflow.memberships(account_id,subject,roles,active)VALUES(%s,%s,%s,%s)ON CONFLICT(account_id,subject)DO UPDATE SET roles=excluded.roles,active=excluded.active',(account_id,subject,roles,active))
108
+ return{'active':active,'roles':roles}
@@ -0,0 +1,79 @@
1
+ """Explicit operator CLI; never run with the API or worker SQL identity.
2
+
3
+ Connection/credential files are private local inputs, not export payloads. This
4
+ tool performs destination writes only after --execute; obtain authorization first.
5
+ It never contacts a model or the accounting service, and never claims that a human
6
+ supplied receipt has been independently verified by this tool.
7
+ """
8
+ import argparse
9
+ import json
10
+ import os
11
+ from pathlib import Path
12
+ import stat
13
+ import sys
14
+ sys.path.insert(0,str(Path(__file__).resolve().parent))
15
+ import psycopg
16
+ from invoice_operator_core import validate_deployment,initialize_runtime
17
+ from invoice_operations import purge_expired,quarantine_restored_deliveries,reconcile_completed,set_membership
18
+ from structured_output import _object
19
+
20
+ def private_json(path):
21
+ file=Path(path)
22
+ metadata=file.stat()
23
+ if not stat.S_ISREG(metadata.st_mode)or metadata.st_uid!=os.geteuid()or metadata.st_mode&0o077 or metadata.st_size>20000:raise ValueError('Use an owned mode-0600 bounded JSON input file')
24
+ value=json.loads(file.read_text(),object_pairs_hook=_object)
25
+ if not isinstance(value,dict):raise ValueError('Use a JSON object')
26
+ return value
27
+
28
+ def connection_settings(path):
29
+ value=private_json(path)
30
+ if set(value)!={'host','port','dbname','user','passwordFile','caFile'}:raise ValueError('Use the documented operator connection fields')
31
+ secret=Path(value['passwordFile']);metadata=secret.stat()
32
+ if not secret.is_absolute()or metadata.st_uid!=os.geteuid()or metadata.st_mode&0o077 or not stat.S_ISREG(metadata.st_mode)or not 1<=metadata.st_size<=20000:raise ValueError('Use a private separately supplied database password/token file')
33
+ password=secret.read_text()
34
+ ca=Path(value['caFile'])
35
+ if not ca.is_absolute()or not ca.is_file()or b'-----BEGIN CERTIFICATE-----'not in ca.read_bytes():raise ValueError('Use the reviewed native CA bundle')
36
+ if any(not isinstance(value[key],str)or not value[key]or '\0'in value[key]for key in ['host','dbname','user'])or type(value['port'])is not int or not 1<=value['port']<=65535:raise ValueError('Use the owned native database connection')
37
+ if not password or '\0'in password:raise ValueError('Invalid private operator credential')
38
+ return dict(host=value['host'],port=value['port'],dbname=value['dbname'],user=value['user'],password=password,sslmode='verify-full',sslrootcert=str(ca),ssl_min_protocol_version='TLSv1.2',connect_timeout=5,options='-c search_path=pg_catalog,workflow,pg_temp -c statement_timeout=30000 -c lock_timeout=3000')
39
+
40
+ def main(argv=None):
41
+ parser=argparse.ArgumentParser(description=__doc__)
42
+ parser.add_argument('action',choices=['initialize','membership','purge-expired','quarantine-restore','reconcile-completed'])
43
+ parser.add_argument('--connection-file',required=True);parser.add_argument('--deployment-file',required=True);parser.add_argument('--input-file');parser.add_argument('--execute',action='store_true');parser.add_argument('--confirm-workloads-stopped',action='store_true')
44
+ args=parser.parse_args(argv)
45
+ deployment=private_json(args.deployment_file)
46
+ scope=validate_deployment(deployment)
47
+ payload=private_json(args.input_file)if args.input_file else{}
48
+ if not args.execute:
49
+ print(json.dumps({'action':args.action,'willWrite':False,'next':'Review the destination, private inputs and authorization, then pass --execute. No database connection was made.'}));return
50
+ settings=connection_settings(args.connection_file)
51
+ if args.action=='initialize':
52
+ def passwords(purpose,principal):
53
+ if purpose not in deployment['bindings']or principal!=deployment['bindings'][purpose]['principal']:raise ValueError('Use the exact owned native password binding')
54
+ field=payload.get(purpose+'PasswordFile')
55
+ if not isinstance(field,str):raise ValueError('Supply a private separate IBM role password file')
56
+ path=Path(field);metadata=path.stat()
57
+ if not path.is_absolute()or metadata.st_uid!=os.geteuid()or metadata.st_mode&0o077 or not stat.S_ISREG(metadata.st_mode)or not 24<=metadata.st_size<=500:raise ValueError('Use an owned private strong runtime password file')
58
+ return path.read_text()
59
+ def connect(database=None):return psycopg.connect(**{**settings,**({'dbname':database}if database else{})})
60
+ receipt=initialize_runtime(connect,deployment,passwords=passwords if deployment['provider']=='ibm'else None)
61
+ else:
62
+ if args.action in {'purge-expired','quarantine-restore','reconcile-completed'}and not args.confirm_workloads_stopped:raise ValueError('Pause workloads and explicitly confirm the maintenance boundary')
63
+ with psycopg.connect(**settings)as conn:
64
+ if args.action=='membership':
65
+ if set(payload)!={'subject','roles','active'}:raise ValueError('Supply exact membership fields')
66
+ receipt=set_membership(conn,**scope,**payload)
67
+ elif args.action=='purge-expired':receipt=purge_expired(conn,**scope)
68
+ elif args.action=='quarantine-restore':receipt=quarantine_restored_deliveries(conn,**scope,workloads_stopped=True)
69
+ else:
70
+ if set(payload)!={'operation_id','payload_sha256','external_id','operator'}:raise ValueError('Supply the independently verified accounting completion receipt fields')
71
+ receipt=reconcile_completed(conn,**scope,**payload)
72
+ print(json.dumps({'action':args.action,'committed':True,'receipt':receipt}))
73
+
74
+ if __name__=='__main__':
75
+ try:main()
76
+ except Exception:
77
+ # Driver exceptions can include connection fields or SQL parameter data.
78
+ # Keep private input details out of terminals and automated logs.
79
+ print('Operator action failed. No success is claimed. Check the private inputs, destination permissions, current schema and workload state.',file=sys.stderr);sys.exit(1)
@@ -0,0 +1,39 @@
1
+ """Shared operator initialization for CLI and fixed native jobs, never HTTP.
2
+
3
+ Validate the whole deployment before opening a connection. Authentication and
4
+ transport belong to the caller's reviewed native credential factory; this module
5
+ neither reads credentials nor chooses endpoints from model/request input.
6
+ """
7
+ import re
8
+ from workflow_role_bootstrap import canonical_uuid,validate_runtime_bindings,bootstrap_runtime_roles
9
+ from workflow_database import initialize_database,grant_runtime_roles
10
+ from invoice_operations import owned_scope
11
+
12
+ def validate_deployment(deployment):
13
+ if not isinstance(deployment,dict)or set(deployment)!={'provider','deploymentId','accountId','namespace','bindings','tenantId'}:raise ValueError('Use the exact documented deployment identity')
14
+ canonical_uuid(deployment['accountId'])
15
+ if not isinstance(deployment['namespace'],str)or not re.fullmatch(r'[a-zA-Z0-9-]{1,20}',deployment['namespace']):raise ValueError('Use the owned workflow namespace')
16
+ if deployment['provider']!='azure'and deployment['tenantId']is not None:raise ValueError('Only the Azure profile has a native tenant mapping')
17
+ validate_runtime_bindings(provider=deployment['provider'],deployment_id=deployment['deploymentId'],bindings=deployment['bindings'],tenant_id=deployment['tenantId'])
18
+ return {'account_id':deployment['accountId'],'namespace':deployment['namespace']}
19
+
20
+ def initialize_runtime(connect,deployment,*,passwords=None):
21
+ scope=validate_deployment(deployment)
22
+ if deployment['provider']=='ibm'and not callable(passwords):raise ValueError('Use the separate owned secret-store password reader')
23
+ # Native identity mapping and application schema are separate committed
24
+ # phases. A retry admits only exact ownership markers, never rotates existing
25
+ # SQL passwords, and then completes the application phase idempotently.
26
+ with connect('postgres'if deployment['provider']=='azure'else None)as conn:
27
+ if conn.autocommit:raise ValueError('Use an explicit operator transaction')
28
+ if conn.info.user in {item['name']for item in deployment['bindings'].values()}:raise PermissionError('Use the separate operator identity')
29
+ bootstrap_runtime_roles(conn,provider=deployment['provider'],deployment_id=deployment['deploymentId'],bindings=deployment['bindings'],tenant_id=deployment['tenantId'],passwords=passwords)
30
+ with connect(None)as conn:
31
+ if conn.autocommit:raise ValueError('Use an explicit operator transaction')
32
+ # Database-local and shared across deployments: serialize the check and
33
+ # CREATE, rather than allowing competing fresh-schema initializations.
34
+ conn.execute("SELECT pg_advisory_xact_lock(hashtextextended('atmem-workflow-schema-bootstrap-v1',0))")
35
+ if conn.execute("SELECT to_regnamespace('workflow')").fetchone()[0]is None:initialize_database(conn)
36
+ owned_scope(conn,**scope)
37
+ grant_runtime_roles(conn,deployment['bindings']['api']['name'],deployment['bindings']['worker']['name'],native_provider=deployment['provider'])
38
+ conn.execute('INSERT INTO workflow.accounts(id)VALUES(%s)ON CONFLICT DO NOTHING',(scope['account_id'],))
39
+ return {'initialized':True,'schemaVersion':1,'nativeIdentity':'Requires destination-native authentication checks; local success alone is insufficient.'}
@@ -0,0 +1,147 @@
1
+ """Fixed native operator job entrypoint. Not an HTTP server or model tool.
2
+
3
+ The emitted task supplies owned nonsecret deployment metadata and a separate
4
+ operator identity. Credentials are platform secret references, never Terraform
5
+ values. Execution is explicit; preview makes no SDK, secret-file or SQL calls.
6
+ """
7
+ import argparse
8
+ import base64
9
+ from contextlib import contextmanager
10
+ import http.client
11
+ import json
12
+ import os
13
+ from pathlib import Path
14
+ import re
15
+ import shutil
16
+ import signal
17
+ import sys
18
+ import tempfile
19
+ import time
20
+ sys.path.insert(0,str(Path(__file__).resolve().parent))
21
+ import psycopg
22
+ from invoice_operator_core import validate_deployment,initialize_runtime
23
+ from invoice_maintenance import maintain,monitor
24
+ from invoice_configuration import owned_deployment,native_database,mounted_secret
25
+ from structured_output import _object
26
+ from workflow_connection import DatabaseConnection
27
+
28
+ def deployment_input(environment,provider):
29
+ encoded=environment.get('OPERATOR_DEPLOYMENT_BASE64')
30
+ if not isinstance(encoded,str)or not 1<=len(encoded)<=16000:raise ValueError('Use the emitted bounded operator deployment')
31
+ deployment=json.loads(base64.b64decode(encoded,validate=True),object_pairs_hook=_object)
32
+ validate_deployment(deployment)
33
+ if deployment['provider']!=provider:raise ValueError('Operator provider differs from the bundled deployment')
34
+ return deployment
35
+
36
+ @contextmanager
37
+ def private_environment(provider,environment):
38
+ env=dict(environment)
39
+ if any(key.startswith(('MODEL_','DESTINATION_','PARSER_','DOCUMENT_'))for key in env):raise ValueError('Operator must not receive application/model/action authority')
40
+ if provider not in {'aws','azure','gcp','ibm'}:raise ValueError('Use the owned operator provider')
41
+ if provider not in {'aws','ibm'}and any(key in env for key in ['DATABASE_PASSWORD','DATABASE_PASSWORD_FILE']):raise ValueError('Use native workload database identity')
42
+ if provider!='ibm'and any(key in env for key in ['API_DATABASE_PASSWORD','WORKER_DATABASE_PASSWORD','API_DATABASE_PASSWORD_FILE','WORKER_DATABASE_PASSWORD_FILE']):raise ValueError('Runtime passwords are IBM-only bindings')
43
+ directory=Path(tempfile.mkdtemp(prefix='atmem-private-operator-'));directory.chmod(0o700)
44
+ try:
45
+ for key,maximum in [('DATABASE_PASSWORD',500),('API_DATABASE_PASSWORD',500),('WORKER_DATABASE_PASSWORD',500),('DATABASE_CA_PEM',256000)]:
46
+ if key not in env:continue
47
+ pointer='DATABASE_CA_FILE'if key=='DATABASE_CA_PEM'else key+'_FILE';value=env[key]
48
+ if pointer in env or not isinstance(value,str)or not value or len(value.encode())>maximum or'\0'in value:raise ValueError('Invalid or conflicting native operator secret injection')
49
+ if key=='DATABASE_CA_PEM'and'-----BEGIN CERTIFICATE-----'not in value:raise ValueError('Use the native database CA certificate')
50
+ path=directory/key.lower();descriptor=os.open(path,os.O_CREAT|os.O_EXCL|os.O_WRONLY,0o600)
51
+ with os.fdopen(descriptor,'wb')as stream:stream.write(value.encode())
52
+ env[pointer]=str(path);env.pop(key)
53
+ if environment is os.environ:os.environ.pop(key,None)
54
+ yield env
55
+ finally:shutil.rmtree(directory,ignore_errors=True)
56
+
57
+ def native_connect(provider,env,deployment):
58
+ def connect(database=None):
59
+ settings={**env,**({'DATABASE_NAME':database}if database else{})}
60
+ if provider=='aws':
61
+ host=settings['DATABASE_HOST'];region=settings['REGION']
62
+ if not re.fullmatch(r'[a-z]{2}-[a-z]+-[0-9]',region)or not host.endswith('.'+region+'.rds.amazonaws.com'):raise ValueError('Use the owned regional RDS endpoint')
63
+ factory=DatabaseConnection('aws',host,5432,settings['DATABASE_NAME'],settings['DATABASE_USER'],mounted_secret(settings['DATABASE_PASSWORD_FILE']),ca_file=settings['DATABASE_CA_FILE'],role='migration')
64
+ else:factory=native_database(provider,settings,'migration')
65
+ if factory.parameters['user']in {row['name']for row in deployment['bindings'].values()}:raise PermissionError('Do not run an operator job as API or worker')
66
+ for attempt in range(3):
67
+ try:return psycopg.connect(factory())
68
+ except psycopg.OperationalError as error:
69
+ # Only retry connection establishment before any transaction.
70
+ # Cold proxy certificate/network setup can be transient; SQL
71
+ # permission/authentication/schema failures are never repaired
72
+ # by bypassing TLS, changing identities or replaying statements.
73
+ state=error.sqlstate
74
+ if provider!='gcp'or attempt==2 or(state is not None and not(state.startswith('08')or state=='57P03')):raise
75
+ time.sleep(attempt+1)
76
+ return connect
77
+
78
+ def runtime_passwords(environment,deployment):
79
+ def password(purpose,principal):
80
+ if purpose not in deployment['bindings']or deployment['bindings'][purpose]['principal']!=principal:raise ValueError('Use the exact owned native password binding')
81
+ path=Path(environment[purpose.upper()+'_DATABASE_PASSWORD_FILE']);metadata=path.stat()
82
+ import stat
83
+ if not path.is_absolute()or not stat.S_ISREG(metadata.st_mode)or metadata.st_uid!=os.geteuid()or metadata.st_mode&0o077 or not 24<=metadata.st_size<=500:raise ValueError('Use a private separately supplied native runtime password')
84
+ return path.read_text()
85
+ return password
86
+
87
+ def run_action(action,connect,deployment,*,passwords=None):
88
+ validate_deployment(deployment)
89
+ if action=='initialize':return {'status':'ok',**initialize_runtime(connect,deployment,passwords=passwords)}
90
+ if action=='maintain':return maintain(connect,deployment)
91
+ if action=='monitor':return monitor(connect,deployment)
92
+ raise ValueError('Unsupported operator job action')
93
+
94
+ @contextmanager
95
+ def private_proxy_job(provider,environment):
96
+ mode=environment.get('OPERATOR_PROXY_SHUTDOWN')
97
+ if mode is None:
98
+ yield;return
99
+ if provider!='gcp'or mode!='cloudsql-private-proxy'or environment.get('DATABASE_TRANSPORT')!='cloudsql-private-proxy':raise ValueError('Use the emitted Google job proxy lifecycle')
100
+ try:
101
+ # Stable GA job containers can start concurrently. Wait for the pinned
102
+ # proxy's own startup endpoint rather than depending on beta ordering.
103
+ deadline=time.monotonic()+30
104
+ while time.monotonic()<deadline:
105
+ startup=http.client.HTTPConnection('127.0.0.1',9090,timeout=1)
106
+ try:
107
+ startup.request('GET','/startup');response=startup.getresponse()
108
+ if response.status==200 and len(response.read(2049))<=2048:break
109
+ except (OSError,http.client.HTTPException):pass
110
+ finally:startup.close()
111
+ time.sleep(0.2)
112
+ else:raise TimeoutError('Private SQL proxy did not finish startup')
113
+ yield
114
+ finally:
115
+ # Fixed same-instance loopback endpoint; no configurable URL, redirects,
116
+ # ambient HTTP proxy or outbound credentials. A failed shutdown must not
117
+ # produce an ok receipt while the sidecar keeps the job running.
118
+ connection=http.client.HTTPConnection('127.0.0.1',9091,timeout=2)
119
+ try:
120
+ connection.request('POST','/quitquitquit',body=b'')
121
+ response=connection.getresponse()
122
+ if response.status!=200 or len(response.read(2049))>2048:raise ValueError('Private SQL proxy did not confirm shutdown')
123
+ finally:connection.close()
124
+
125
+ def main(argv=None):
126
+ parser=argparse.ArgumentParser(description=__doc__)
127
+ parser.add_argument('action',choices=['initialize','maintain','monitor']);parser.add_argument('--execute',action='store_true');args=parser.parse_args(argv)
128
+ provider=owned_deployment()['provider']
129
+ if not args.execute:
130
+ deployment_input(os.environ,provider)
131
+ print(json.dumps({'format':'invoice-operator-v1','action':args.action,'status':'preview','willWrite':False}));return 0
132
+ def deadline(*_):raise TimeoutError('Operator execution deadline')
133
+ with private_proxy_job(provider,os.environ):
134
+ deployment=deployment_input(os.environ,provider)
135
+ previous=signal.signal(signal.SIGALRM,deadline);signal.alarm(900)
136
+ try:
137
+ with private_environment(provider,os.environ)as env:
138
+ result=run_action(args.action,native_connect(provider,env,deployment),deployment,passwords=runtime_passwords(env,deployment)if provider=='ibm'else None)
139
+ finally:signal.alarm(0);signal.signal(signal.SIGALRM,previous)
140
+ print(json.dumps({'format':'invoice-operator-v1','action':args.action,**result}))
141
+ return 0 if result['status']=='ok'else 2
142
+
143
+ if __name__=='__main__':
144
+ try:sys.exit(main())
145
+ except Exception:
146
+ # Driver/SDK exceptions can contain sensitive endpoints, SQL or values.
147
+ print(json.dumps({'format':'invoice-operator-v1','status':'failed','code':'operator_job_failed'}));sys.exit(1)
@@ -0,0 +1,17 @@
1
+ """Credentialless one-shot parser protocol; no URLs, cloud calls or model work."""
2
+ import hashlib
3
+ import json
4
+ import sys
5
+ from pathlib import Path
6
+ sys.path.insert(0,str(Path(__file__).resolve().parent))
7
+ from invoice_document import PdfDocument,DocumentError
8
+
9
+ if __name__=='__main__':
10
+ try:
11
+ raw=sys.stdin.buffer.read(10000001)
12
+ document=PdfDocument().extract(raw,hashlib.sha256(raw).hexdigest())
13
+ sys.stdout.write(json.dumps({'status':'ok','document':document},ensure_ascii=False))
14
+ except DocumentError as error:
15
+ sys.stdout.write(json.dumps({'status':'rejected','reason':error.code}));sys.exit(2)
16
+ except Exception:
17
+ sys.stdout.write(json.dumps({'status':'rejected','reason':'extraction_unavailable'}));sys.exit(2)
@@ -0,0 +1,39 @@
1
+ """Parser-only Entra workload authorization; no application/cloud credentials.
2
+
3
+ The issuer/key endpoint, audience and exact worker identities are compiler-owned.
4
+ Staff/delegated tokens and another application in the same tenant grant no access.
5
+ This module does not claim that a valid token proves a native deployment exists.
6
+ """
7
+ import re
8
+ import hmac
9
+ import jwt
10
+
11
+ UUID=r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
12
+
13
+ class EntraParserVerifier:
14
+ def __init__(self,tenant,audience,worker_client,worker_principal,*,jwks=None):
15
+ if any(not isinstance(value,str)or not re.fullmatch(UUID,value)for value in [tenant,audience,worker_client,worker_principal]):raise ValueError('Use exact owned Entra parser and worker identities')
16
+ self.tenant=tenant;self.audience=audience;self.worker_client=worker_client;self.worker_principal=worker_principal
17
+ self.issuer='https://login.microsoftonline.com/'+tenant+'/v2.0'
18
+ self.jwks=jwks or jwt.PyJWKClient('https://login.microsoftonline.com/'+tenant+'/discovery/v2.0/keys',timeout=2,lifespan=300)
19
+ def __call__(self,token):
20
+ try:
21
+ if not isinstance(token,str)or not 1<=len(token)<=16384:raise ValueError('Invalid workload token')
22
+ header=jwt.get_unverified_header(token)
23
+ if header.get('alg')!='RS256'or not isinstance(header.get('kid'),str)or not 1<=len(header['kid'])<=200:raise ValueError('Invalid workload signing key')
24
+ key=self.jwks.get_signing_key_from_jwt(token).key
25
+ claims=jwt.decode(token,key,algorithms=['RS256'],issuer=self.issuer,audience=self.audience,leeway=30,options={'require':['iss','aud','iat','nbf','exp','tid','oid','azp','ver','roles']})
26
+ if(claims['aud']!=self.audience or claims['tid']!=self.tenant or claims['oid']!=self.worker_principal or claims['azp']!=self.worker_client or claims['ver']!='2.0'
27
+ or'scp'in claims or claims.get('idtyp','app')!='app'or not isinstance(claims['roles'],list)or not 1<=len(claims['roles'])<=100 or any(not isinstance(role,str)for role in claims['roles'])or'Parser.Invoke'not in claims['roles']
28
+ or any(type(claims[name])is not int for name in ['iat','nbf','exp'])or not 0<claims['exp']-claims['iat']<=86700):raise ValueError('Caller is not the owned parser worker')
29
+ return self.worker_principal
30
+ except Exception:raise PermissionError('Authorized parser worker required')from None
31
+
32
+ class SharedParserVerifier:
33
+ """Private TLS RPC admission only; this token confers no cloud/data authority."""
34
+ def __init__(self,token):
35
+ if not isinstance(token,str)or not re.fullmatch(r'[0-9a-f]{64}',token):raise ValueError('Use a separately provisioned 256-bit parser token')
36
+ self._token=token
37
+ def __call__(self,token):
38
+ if not isinstance(token,str)or not re.fullmatch(r'[0-9a-f]{64}',token)or not hmac.compare_digest(self._token,token):raise PermissionError('Authorized parser worker required')
39
+ return'owned-worker'