@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,105 @@
1
+ """Fixed SSM host inspection. Standard library only; no document or secret input."""
2
+ import base64
3
+ import json
4
+ import ipaddress
5
+ from pathlib import Path
6
+ import re
7
+ import subprocess
8
+ import shlex
9
+ import sys
10
+
11
+
12
+ def run(argv):
13
+ if argv and argv[0]=='/usr/bin/docker':
14
+ # An operator's saved Docker context must never redirect this native
15
+ # host inspection to another daemon, even with a scrubbed environment.
16
+ argv=[argv[0],'--host','unix:///var/run/docker.sock',*argv[1:]]
17
+ value=subprocess.run(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.DEVNULL,
18
+ timeout=4,check=True,env={'PATH':'/usr/sbin:/usr/bin:/sbin:/bin','LANG':'C'})
19
+ if len(value.stdout)>32768:raise ValueError('Invalid host observation')
20
+ return value.stdout.decode().strip()
21
+
22
+
23
+ def expected_policy(expected):
24
+ if (not isinstance(expected,dict)or set(expected)!={'format','clusterArn','policySha256'}or
25
+ expected['format']!='enterprise-aws-host-policy-v1'or
26
+ not isinstance(expected['policySha256'],str)or not re.fullmatch('[0-9a-f]{64}',expected['policySha256'])or
27
+ not isinstance(expected['clusterArn'],str)or not re.fullmatch(r'arn:aws:ecs:[a-z]{2}-[a-z]+-[1-9]:[0-9]{12}:cluster/[A-Za-z0-9_-]{1,255}',expected['clusterArn'])):
28
+ raise ValueError('Use the exact owned host policy')
29
+ return dict(expected)
30
+
31
+
32
+ def first_rule_matches(lines,chain,pairs):
33
+ active=[value for value in lines.splitlines()if value.startswith('-A ')]
34
+ if not active:return False
35
+ fields=shlex.split(active[0])
36
+ if fields[:2]!=['-A',chain]or len(fields)!=2+2*len(pairs):return False
37
+ actual={}
38
+ for index in range(2,len(fields),2):
39
+ key,value=fields[index:index+2]
40
+ if key in actual:return False
41
+ if key=='-d':
42
+ try:value=str(ipaddress.ip_network(value,strict=True))
43
+ except ValueError:return False
44
+ actual[key]=value
45
+ return actual==pairs
46
+
47
+
48
+ def inspect_host(expected,*,read=lambda name:Path(name).read_text(),command=run):
49
+ expected=expected_policy(expected)
50
+ # Parse values as inert text. Never source a host config into a shell.
51
+ values={}
52
+ for line in read('/etc/ecs/ecs.config').splitlines():
53
+ if not line.strip()or line.lstrip().startswith('#'):continue
54
+ key,separator,value=line.partition('=')
55
+ if not separator or key in values:raise ValueError('Ambiguous ECS configuration')
56
+ values[key]=value
57
+ if (values.get('ECS_CLUSTER')!=expected['clusterArn']or
58
+ values.get('ECS_ENABLE_TASK_IAM_ROLE')!='true'or
59
+ values.get('ECS_ENGINE_TASK_CLEANUP_WAIT_DURATION')!='1s'or
60
+ values.get('ECS_ENABLE_TASK_IAM_ROLE_NETWORK_HOST')!='false'):
61
+ raise ValueError('ECS host policy changed')
62
+ attributes=json.loads(values.get('ECS_INSTANCE_ATTRIBUTES','{}'))
63
+ if attributes!={'atmem-document-policy':expected['policySha256']}:raise ValueError('Host policy selection changed')
64
+ daemon=json.loads(read('/etc/docker/daemon.json'))
65
+ if (daemon.get('log-driver')!='none'or daemon.get('live-restore',False)is not False or daemon.get('bridge','docker0')!='docker0'):
66
+ raise ValueError('Unsafe daemon logging, network or task persistence')
67
+ if json.loads(command(['/usr/bin/docker','info','--format','{{json .LoggingDriver}}']))!='none':raise ValueError('Actual daemon logging changed')
68
+ if json.loads(command(['/usr/bin/docker','info','--format','{{json .LiveRestoreEnabled}}']))is not False:raise ValueError('Native live restore is active')
69
+ options=json.loads(command(['/usr/bin/docker','info','--format','{{json .SecurityOptions}}']))
70
+ if not isinstance(options,list)or 'name=seccomp,profile=builtin'not in options:
71
+ raise ValueError('Native seccomp is not active')
72
+ bridge=json.loads(command(['/usr/bin/docker','network','inspect','bridge','--format','{{json .Options}}']))
73
+ if bridge.get('com.docker.network.bridge.name')!='docker0':raise ValueError('Actual default bridge changed')
74
+ native_env=json.loads(command(['/usr/bin/docker','inspect','ecs-agent','--format','{{json .Config.Env}}']))
75
+ if not isinstance(native_env,list)or any(not isinstance(v,str)for v in native_env):raise ValueError('Native ECS agent configuration unavailable')
76
+ native={}
77
+ for entry in native_env:
78
+ key,separator,value=entry.partition('=')
79
+ if not separator or key in native:raise ValueError('Ambiguous native ECS environment')
80
+ native[key]=value
81
+ for key in ['ECS_CLUSTER','ECS_ENABLE_TASK_IAM_ROLE','ECS_ENGINE_TASK_CLEANUP_WAIT_DURATION','ECS_ENABLE_TASK_IAM_ROLE_NETWORK_HOST','ECS_INSTANCE_ATTRIBUTES']:
82
+ if native.get(key)!=values[key]:raise ValueError('Running ECS agent differs from host policy')
83
+ # Both paths are fixed, read-only probes. A plain TCP bridge must not expose
84
+ # instance-profile credentials to the transfer containers.
85
+ for destination in ['169.254.169.254/32','fd00:ec2::254/128']:
86
+ tool='/usr/sbin/iptables'if ':'not in destination else'/usr/sbin/ip6tables'
87
+ rules=command([tool,'-w','2','-S','DOCKER-USER'])
88
+ if not first_rule_matches(rules,'DOCKER-USER',{'-i':'docker0','-d':destination,'-j':'DROP'}):raise ValueError('IMDS rule is missing or shadowed')
89
+ forward=command([tool,'-w','2','-S','FORWARD'])
90
+ if not first_rule_matches(forward,'FORWARD',{'-j':'DOCKER-USER'}):raise ValueError('IMDS chain is not the first forwarding hook')
91
+ boot=read('/proc/sys/kernel/random/boot_id').strip()
92
+ if not re.fullmatch('[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}',boot):raise ValueError('Invalid native boot identity')
93
+ return {'format':'enterprise-aws-host-observation-v1','policySha256':expected['policySha256'],
94
+ 'clusterArn':expected['clusterArn'],'bootId':boot}
95
+
96
+
97
+ def main():
98
+ try:
99
+ if len(sys.argv)!=2 or len(sys.argv[1])>4096:raise ValueError()
100
+ expected=json.loads(base64.b64decode(sys.argv[1],validate=True))
101
+ print(json.dumps(inspect_host(expected),separators=(',',':')))
102
+ except Exception:raise SystemExit(2)from None
103
+
104
+
105
+ if __name__=='__main__':main()
@@ -0,0 +1,136 @@
1
+ """Point-in-time host inspection through one fixed, hash/version-bound SSM document."""
2
+ from dataclasses import dataclass
3
+ import base64
4
+ import json
5
+ import math
6
+ from pathlib import Path
7
+ import re
8
+ import shlex
9
+ import threading
10
+ import time
11
+ from types import MappingProxyType
12
+ from enterprise_sources import SourceUnavailable
13
+
14
+
15
+ def unique(pairs):
16
+ value={}
17
+ for key,item in pairs:
18
+ if key in value:raise ValueError('Ambiguous host-inspection content')
19
+ value[key]=item
20
+ return value
21
+
22
+
23
+ def command_document(config):
24
+ source=Path(__file__).with_name('enterprise_aws_host_probe.py').read_bytes()
25
+ expected={'format':'enterprise-aws-host-policy-v1','clusterArn':config['clusterArn'],'policySha256':config['hostPolicySha256']}
26
+ launcher='import base64;exec(compile(base64.b64decode('+repr(base64.b64encode(source).decode())+'),"owned-host-probe","exec"))'
27
+ command='exec /usr/bin/python3 -I -c '+shlex.quote(launcher)+' '+shlex.quote(base64.b64encode(json.dumps(expected,sort_keys=True,separators=(',',':')).encode()).decode())
28
+ return {'schemaVersion':'2.2','description':'Fixed read-only parser host inspection; no caller command or secret parameters.',
29
+ 'parameters':{},'mainSteps':[{'action':'aws:runShellScript','name':'verifyHost',
30
+ 'precondition':{'StringEquals':['platformType','Linux']},'inputs':{'timeoutSeconds':'30','runCommand':[command]}}]}
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class VerifiedHosts:
35
+ policy_sha256:str
36
+ members:tuple
37
+ expires_at:float
38
+
39
+ def placement(self,config):
40
+ if (self.policy_sha256!=config['hostPolicySha256']or type(self.expires_at)not in {int,float}or not math.isfinite(self.expires_at)or
41
+ not 0<self.expires_at-time.monotonic()<=121 or type(self.members)is not tuple or not 1<=len(self.members)<=10):
42
+ raise SourceUnavailable('document_host_verification_changed')
43
+ prefix='arn:aws:ecs:'+config['region']+':'+config['accountId']+':container-instance/'+config['clusterArn'].rsplit('/',1)[1]+'/'
44
+ instances=[];arns=[]
45
+ for member in self.members:
46
+ if (type(member)is not tuple or len(member)!=2 or not isinstance(member[0],str)or
47
+ not re.fullmatch(re.escape(prefix)+'[0-9a-f]{32}',member[0])or not isinstance(member[1],str)or not re.fullmatch('i-[0-9a-f]{17}',member[1])):
48
+ raise SourceUnavailable('document_host_verification_changed')
49
+ arns.append(member[0]);instances.append(member[1])
50
+ if len(set(instances))!=len(instances)or len(set(arns))!=len(arns):raise SourceUnavailable('document_host_verification_changed')
51
+ return [{'type':'memberOf','expression':'ec2InstanceId in ['+', '.join("'"+value+"'"for value in sorted(instances))+']'}]
52
+
53
+
54
+ class HostApi:
55
+ def __init__(self,region,*,ecs=None,ssm=None):
56
+ if not isinstance(region,str)or not re.fullmatch('[a-z]{2}-[a-z]+-[1-9]',region):raise ValueError('Use the owned AWS region')
57
+ self.region=region;self.clients={'ecs':ecs,'ssm':ssm};self._lock=threading.Lock()
58
+ def call(self,service,operation,**values):
59
+ allowed={'ecs':{'list_container_instances','describe_container_instances'},'ssm':{'get_document','send_command','get_command_invocation'}}
60
+ if service not in allowed or operation not in allowed[service]:raise ValueError('Use the fixed host-inspection contract')
61
+ try:
62
+ if self.clients[service]is None:
63
+ with self._lock:
64
+ if self.clients[service]is None:
65
+ import boto3
66
+ from botocore.config import Config
67
+ self.clients[service]=boto3.Session().client(service,region_name=self.region,
68
+ endpoint_url='https://'+service+'.'+self.region+'.amazonaws.com',
69
+ config=Config(proxies={},connect_timeout=2,read_timeout=5,retries={'total_max_attempts':1}))
70
+ value=getattr(self.clients[service],operation)(**values)
71
+ if not isinstance(value,dict):raise ValueError()
72
+ return value
73
+ except Exception as error:
74
+ if service=='ssm'and operation=='get_command_invocation'and getattr(error,'response',{}).get('Error',{}).get('Code')=='InvocationDoesNotExist':
75
+ return {'Status':'Pending'}
76
+ raise SourceUnavailable('document_host_verification_unavailable')from None
77
+
78
+
79
+ class EcsHostPolicy:
80
+ def __init__(self,config,*,api=None,poll_seconds=1):
81
+ from enterprise_aws_jobs import configuration
82
+ self.config=MappingProxyType(configuration(config));self.api=api or HostApi(self.config['region'])
83
+ if type(poll_seconds)not in {int,float}or not .01<=poll_seconds<=2:raise ValueError('Use a bounded host-inspection poll interval')
84
+ self.poll_seconds=poll_seconds
85
+ def __call__(self,config):
86
+ if config!=self.config:raise ValueError('Use the exact owned host policy destination')
87
+ try:return self.verify()
88
+ except Exception:raise SourceUnavailable('document_host_verification_unavailable')from None
89
+ def verify(self):
90
+ c=self.config;started=time.monotonic();cluster=c['clusterArn']
91
+ def call(service,operation,**values):
92
+ if time.monotonic()-started>60:raise ValueError('Host inspection deadline')
93
+ return self.api.call(service,operation,**values)
94
+ listing=call('ecs','list_container_instances',cluster=cluster,status='ACTIVE',maxResults=10,
95
+ filter='attribute:atmem-document-policy == '+c['hostPolicySha256'])
96
+ arns=listing.get('containerInstanceArns')
97
+ if not isinstance(arns,list)or not 1<=len(arns)<=10 or len(set(arns))!=len(arns)or listing.get('nextToken'):raise ValueError('Owned host pool unavailable')
98
+ observed=call('ecs','describe_container_instances',cluster=cluster,containerInstances=arns)
99
+ instances=observed.get('containerInstances')
100
+ if observed.get('failures')or not isinstance(instances,list)or len(instances)!=len(arns):raise ValueError('Owned host pool changed')
101
+ members=[]
102
+ for value in instances:
103
+ attributes=value.get('attributes',[])
104
+ if (value.get('containerInstanceArn')not in arns or value.get('agentConnected')is not True or value.get('status')!='ACTIVE'or
105
+ not isinstance(attributes,list)or sum(v.get('name')=='atmem-document-policy'and v.get('value')==c['hostPolicySha256']for v in attributes)!=1):
106
+ raise ValueError('Native host selection changed')
107
+ members.append((value['containerInstanceArn'],value.get('ec2InstanceId')))
108
+ verified=VerifiedHosts(c['hostPolicySha256'],tuple(members),time.monotonic()+120);verified.placement(c)
109
+ version=c['hostDocumentVersion'];document=call('ssm','get_document',Name=c['hostDocumentArn'],DocumentVersion=version,DocumentFormat='JSON')
110
+ content=document.get('Content')
111
+ if not isinstance(content,str)or len(content.encode())>65536 or document.get('DocumentVersion')!=version or json.loads(content,object_pairs_hook=unique)!=command_document(c):
112
+ raise ValueError('Owned command document content changed')
113
+ sent=call('ssm','send_command',InstanceIds=[instance for _,instance in members],DocumentName=c['hostDocumentArn'],
114
+ DocumentVersion=version,DocumentHash=c['hostDocumentHash'],DocumentHashType='Sha256',TimeoutSeconds=30,
115
+ CloudWatchOutputConfig={'CloudWatchOutputEnabled':False})
116
+ command=sent.get('Command',{}).get('CommandId')
117
+ if not isinstance(command,str)or not re.fullmatch('[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}',command):raise ValueError('Invalid native inspection identity')
118
+ for _,instance in members:
119
+ while True:
120
+ value=call('ssm','get_command_invocation',CommandId=command,InstanceId=instance,PluginName='verifyHost')
121
+ status=value.get('Status')
122
+ if status=='Success':break
123
+ if status not in {'Pending','InProgress','Delayed'}:raise ValueError('Host inspection failed')
124
+ time.sleep(self.poll_seconds)
125
+ if (value.get('CommandId')!=command or value.get('InstanceId')!=instance or value.get('DocumentVersion')!=version or
126
+ value.get('DocumentName')not in {c['hostDocumentArn'],c['hostDocumentArn'].rsplit('/',1)[1]}or
127
+ type(value.get('ResponseCode'))is not int or value['ResponseCode']!=0 or value.get('StandardErrorContent')or
128
+ value.get('StandardOutputUrl')or value.get('StandardErrorUrl')):raise ValueError('Host inspection identity changed')
129
+ encoded=value.get('StandardOutputContent')
130
+ if not isinstance(encoded,str)or not 0<len(encoded.encode())<=4096:raise ValueError('Invalid host observation')
131
+ output=json.loads(encoded,object_pairs_hook=unique)
132
+ if (not isinstance(output,dict)or set(output)!={'format','policySha256','clusterArn','bootId'}or
133
+ output['format']!='enterprise-aws-host-observation-v1'or output['policySha256']!=c['hostPolicySha256']or output['clusterArn']!=cluster or
134
+ not isinstance(output['bootId'],str)or not re.fullmatch('[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}',output['bootId'])):
135
+ raise ValueError('Host observation changed')
136
+ verified.placement(c);return verified
@@ -0,0 +1,300 @@
1
+ """Owned EC2/ECS per-file controller, not a complete production composition.
2
+
3
+ The parser requires per-container network disabling, unavailable on Fargate.
4
+ Host-policy verification/provisioning and complete export integration are required;
5
+ this module does not register a capability or change existing cloud resources.
6
+ """
7
+ import json
8
+ import copy
9
+ import re
10
+ import hashlib
11
+ import threading
12
+ import time
13
+ import uuid
14
+ from pathlib import Path
15
+ from types import MappingProxyType
16
+ from enterprise_aws_objects import S3DocumentObjects
17
+ from enterprise_document_job import request,result
18
+ from enterprise_document_jobs import DocumentJobLedger
19
+ from enterprise_sources import SourceUnavailable
20
+ from enterprise_aws_hosts import EcsHostPolicy,VerifiedHosts
21
+
22
+
23
+ def configuration(value):
24
+ fields={'format','accountId','region','bucket','clusterArn','hostPolicySha256','hostDocumentArn','hostDocumentHash','hostDocumentVersion','workerRoleArn',
25
+ 'transferRoleArn','executionRoleArn','parserImage','transferImage','taskDefinitionArn'}
26
+ if not isinstance(value,dict)or set(value)!=fields or value['format']!='enterprise-aws-document-job-v2':
27
+ raise ValueError('Use the exact owned AWS document-job configuration')
28
+ patterns={'accountId':r'[0-9]{12}','region':r'(?:af|ap|ca|eu|il|me|mx|sa|us)-(?:east|west|south|north|central|southeast|southwest|northeast|northwest)-[1-9]',
29
+ 'bucket':r'[a-z0-9][a-z0-9-]{1,61}[a-z0-9]'}
30
+ for key,pattern in patterns.items():
31
+ if not isinstance(value[key],str)or not re.fullmatch(pattern,value[key]):raise ValueError('Invalid owned AWS destination')
32
+ if not isinstance(value['hostPolicySha256'],str)or not re.fullmatch('[0-9a-f]{64}',value['hostPolicySha256']):
33
+ raise ValueError('Use the reviewed dedicated-host policy fingerprint')
34
+ account,region=value['accountId'],value['region']
35
+ prefix='arn:aws:'
36
+ if (not isinstance(value['hostDocumentArn'],str)or not re.fullmatch(re.escape(prefix+'ssm:'+region+':'+account+':document/')+r'[A-Za-z0-9_.-]{3,128}',value['hostDocumentArn'])or
37
+ not isinstance(value['hostDocumentHash'],str)or not re.fullmatch('[0-9a-f]{64}',value['hostDocumentHash'])or
38
+ not isinstance(value['hostDocumentVersion'],str)or not re.fullmatch('[1-9][0-9]{0,8}',value['hostDocumentVersion'])):
39
+ raise ValueError('Use the exact owned version/hash-bound host document')
40
+ for key in ['workerRoleArn','transferRoleArn','executionRoleArn']:
41
+ if not isinstance(value[key],str)or not re.fullmatch(re.escape(prefix+'iam::'+account+':role/')+r'[A-Za-z0-9_+=,.@/-]{1,128}',value[key]):
42
+ raise ValueError('Use explicit same-account owned roles')
43
+ if len({value[key]for key in ['workerRoleArn','transferRoleArn','executionRoleArn']})!=3:
44
+ raise ValueError('Worker, storage transfer and image execution authority must be distinct')
45
+ if not isinstance(value['clusterArn'],str)or not re.fullmatch(re.escape(prefix+'ecs:'+region+':'+account+':cluster/')+r'[A-Za-z0-9_-]{1,255}',value['clusterArn']):
46
+ raise ValueError('Use the owned regional ECS cluster')
47
+ if (not isinstance(value['taskDefinitionArn'],str)or not re.fullmatch(re.escape(prefix+'ecs:'+region+':'+account+':task-definition/document-')+r'[A-Za-z0-9_-]{1,200}:[1-9][0-9]{0,8}',value['taskDefinitionArn'])):
48
+ raise ValueError('Use the exact operator-owned regional task-definition revision')
49
+ for key in ['parserImage','transferImage']:
50
+ if not isinstance(value[key],str)or not re.fullmatch(re.escape(account+'.dkr.ecr.'+region+'.amazonaws.com/')+r'[a-z0-9]+(?:[._/-][a-z0-9]+)*@sha256:[0-9a-f]{64}',value[key]):
51
+ raise ValueError('Use immutable owned regional ECR images')
52
+ if value['parserImage']==value['transferImage']:raise ValueError('Keep the parser and trusted transfer artifacts separate')
53
+ return dict(value)
54
+
55
+
56
+ def transfer_metadata(config,metadata,etag):
57
+ # Preserve the shared byte contract's exact fields and canonical media type.
58
+ if not isinstance(metadata,dict)or set(metadata)!={'format','requestId','sha256','mediaType'}:
59
+ raise ValueError('Use the exact document request')
60
+ if (metadata['format']!='enterprise-document-job-v1' or
61
+ not isinstance(metadata['requestId'],str)or not re.fullmatch('[0-9a-f]{64}',metadata['requestId'])or
62
+ not isinstance(metadata['sha256'],str)or not re.fullmatch('[0-9a-f]{64}',metadata['sha256'])):
63
+ raise ValueError('Use the owned byte-bound request')
64
+ from enterprise_document_contract import media_type
65
+ if media_type(metadata['mediaType'])!=metadata['mediaType']:raise ValueError('Use canonical document MIME')
66
+ # Opaque ETag is a conditional read token, never treated as an input digest.
67
+ if not isinstance(etag,str)or not re.fullmatch(r'"[0-9a-f]{32}(?:-[1-9][0-9]{0,4})?"',etag):
68
+ raise ValueError('Use the acknowledged native input ETag')
69
+ return {'format':'enterprise-aws-document-transfer-v1','accountId':config['accountId'],
70
+ 'region':config['region'],'bucket':config['bucket'],'request':dict(metadata),'inputETag':etag}
71
+
72
+
73
+ def task_family(config):
74
+ return config['taskDefinitionArn'].rsplit('/',1)[1].rsplit(':',1)[0]
75
+
76
+
77
+ def task_definition(config):
78
+ """Immutable operator-owned shape, shared with the Terraform compiler.
79
+
80
+ No file identity, bytes, ETag, user prompt or model authority belongs here.
81
+ """
82
+ owned=configuration(config)
83
+ replacements={'__FAMILY__':task_family(owned),'__POLICY_SHA256__':owned['hostPolicySha256'],
84
+ '__TRANSFER_ROLE_ARN__':owned['transferRoleArn'],'__EXECUTION_ROLE_ARN__':owned['executionRoleArn'],
85
+ '__TRANSFER_IMAGE__':owned['transferImage'],'__PARSER_IMAGE__':owned['parserImage']}
86
+ def resolve(value):
87
+ if isinstance(value,dict):return {key:resolve(item)for key,item in value.items()}
88
+ if isinstance(value,list):return [resolve(item)for item in value]
89
+ if isinstance(value,str):
90
+ for key,replacement in replacements.items():value=value.replace(key,replacement)
91
+ return value
92
+ return resolve(json.loads(Path(__file__).with_name('enterprise-aws-task-template.json').read_text()))
93
+
94
+
95
+ def task_overrides(config,metadata,etag):
96
+ encoded=json.dumps(transfer_metadata(configuration(config),metadata,etag),separators=(',',':'))
97
+ return {'containerOverrides':[{'name':name,'environment':[{'name':'DOCUMENT_JOB','value':encoded}]}for name in ['download','upload']]}
98
+
99
+
100
+ def verify_definition(config,reply):
101
+ """Reject drift before storing bytes; tolerate only inert native defaults.
102
+
103
+ Native describe metadata is not a runtime override or deployment proof.
104
+ Unknown nonempty configuration fails closed rather than being projected away.
105
+ """
106
+ desired=task_definition(config);desired.pop('tags')
107
+ if not isinstance(reply,dict)or not isinstance(reply.get('taskDefinition'),dict):raise SourceUnavailable('document_definition_unavailable')
108
+ actual=copy.deepcopy(reply['taskDefinition'])
109
+ if (actual.pop('taskDefinitionArn',None)!=config['taskDefinitionArn']or
110
+ type(actual.get('revision'))is not int or actual.pop('revision')!=int(config['taskDefinitionArn'].rsplit(':',1)[1])or
111
+ actual.pop('status',None)!='ACTIVE'):
112
+ raise SourceUnavailable('document_definition_unavailable')
113
+ for field in ['registeredAt','registeredBy','compatibilities','requiresAttributes']:
114
+ actual.pop(field,None)
115
+ if actual.get('enableFaultInjection')is False:actual.pop('enableFaultInjection')
116
+ if not isinstance(actual.get('containerDefinitions'),list):raise SourceUnavailable('document_definition_unavailable')
117
+ for container in actual['containerDefinitions']:
118
+ if not isinstance(container,dict):raise SourceUnavailable('document_definition_unavailable')
119
+ if container.get('disableNetworking')is False:container.pop('disableNetworking')
120
+ if container.get('versionConsistency')=='enabled':container.pop('versionConsistency')
121
+ def canonical(value):
122
+ if isinstance(value,dict):return {key:canonical(item)for key,item in value.items()if item!=[]and item!={}}
123
+ if isinstance(value,list):return [canonical(item)for item in value]
124
+ return value
125
+ if json.dumps(canonical(actual),sort_keys=True)!=json.dumps(canonical(desired),sort_keys=True):raise SourceUnavailable('document_definition_unavailable')
126
+ return config['taskDefinitionArn']
127
+
128
+
129
+ class EcsDocumentApi:
130
+ def __init__(self,region,*,client=None):
131
+ if not isinstance(region,str)or not re.fullmatch(r'[a-z]{2}-[a-z]+-[1-9]',region):raise ValueError('Use a reviewed commercial AWS region')
132
+ self.region,self.client=region,client;self._lock=threading.Lock()
133
+
134
+ def call(self,operation,**values):
135
+ allowed={'describe_task_definition','run_task','describe_tasks','list_tasks','stop_task'}
136
+ if operation not in allowed:raise ValueError('Use the owned document lifecycle')
137
+ try:
138
+ if self.client is None:
139
+ with self._lock:
140
+ if self.client is None:
141
+ import boto3
142
+ from botocore.config import Config
143
+ self.client=boto3.Session().client('ecs',region_name=self.region,
144
+ endpoint_url='https://ecs.'+self.region+'.amazonaws.com',
145
+ config=Config(proxies={},connect_timeout=2,read_timeout=8,retries={'total_max_attempts':1}))
146
+ value=getattr(self.client,operation)(**values)
147
+ if not isinstance(value,dict):raise ValueError()
148
+ return value
149
+ except Exception:raise SourceUnavailable('document_job_unavailable')from None
150
+
151
+
152
+ class EcsDocuments:
153
+ def __init__(self,config,*,host_check=None,api=None,objects=None,ledger=None,deadline=140,poll_seconds=1):
154
+ self.config=MappingProxyType(configuration(config))
155
+ if host_check is None:host_check=EcsHostPolicy(dict(self.config))
156
+ if not callable(host_check):raise ValueError('Supply the owned dedicated-host verifier')
157
+ if type(deadline)is not int or not 1<=deadline<=140 or type(poll_seconds)not in {int,float}or not .01<=poll_seconds<=2:
158
+ raise ValueError('Use bounded document deadlines')
159
+ self.host_check=host_check;self.api=api or EcsDocumentApi(self.config['region'])
160
+ self.objects=objects or S3DocumentObjects(self.config['bucket'],self.config['region'],self.config['accountId'])
161
+ self.ledger=ledger;self.deadline,self.poll_seconds=deadline,poll_seconds
162
+ self.cluster=self.config['clusterArn'];self.prefix='arn:aws:ecs:'+self.config['region']+':'+self.config['accountId']+':'
163
+ self.target=hashlib.sha256(json.dumps({**{key:self.config[key]for key in ['accountId','region','bucket','clusterArn']},'taskFamily':task_family(self.config)},sort_keys=True,separators=(',',':')).encode()).hexdigest()
164
+
165
+ def __call__(self,raw,mime):raise SourceUnavailable('source_validation_required')
166
+
167
+ def bind_runtime(self,provider,environment):
168
+ if (provider!='aws'or environment.get('REGION')!=self.config['region']or
169
+ environment.get('AWS_ACCOUNT_ID')!=self.config['accountId']or environment.get('WORKER_ROLE_ARN')!=self.config['workerRoleArn']):
170
+ raise ValueError('Document execution must match the selected AWS destination and worker identity')
171
+
172
+ def attach_runtime(self,auth,account_id):
173
+ if self.ledger is None:self.ledger=DocumentJobLedger(auth,account_id,self.target)
174
+ elif (not isinstance(self.ledger,DocumentJobLedger)or self.ledger.auth is not auth or self.ledger.account_id!=account_id or self.ledger.target!=self.target):
175
+ raise ValueError('Use the owned persistent cleanup ledger')
176
+
177
+ def definition(self,arn):
178
+ if not isinstance(arn,str)or not re.fullmatch(re.escape(self.prefix+'task-definition/'+task_family(self.config)+':')+'[1-9][0-9]{0,8}',arn):
179
+ raise SourceUnavailable('document_job_unavailable')
180
+ return arn
181
+
182
+ def task(self,identifier,value,definition=None):
183
+ arn=value.get('taskArn');cluster_name=self.cluster.rsplit('/',1)[1]
184
+ if (not isinstance(arn,str)or not re.fullmatch(re.escape(self.prefix+'task/'+cluster_name+'/')+'[0-9a-f]{32}',arn)or
185
+ value.get('clusterArn')!=self.cluster or value.get('startedBy')!=identifier):raise SourceUnavailable('document_job_unavailable')
186
+ owned=self.definition(value.get('taskDefinitionArn'))
187
+ if definition is not None and owned!=definition:raise SourceUnavailable('document_job_unavailable')
188
+ return arn
189
+
190
+ def from_source(self,raw,mime,guard):
191
+ if not callable(guard):raise ValueError('Supply the live source/company/job check')
192
+ identifier=uuid.uuid4().hex+uuid.uuid4().hex;metadata=request(raw,mime,identifier)
193
+ guard()
194
+ if self.ledger is None:raise SourceUnavailable('document_job_recovery_required')
195
+ hosts=self.host_check(dict(self.config));guard()
196
+ if not isinstance(hosts,VerifiedHosts):raise SourceUnavailable('document_host_verification_required')
197
+ hosts.placement(dict(self.config))
198
+ definition=verify_definition(dict(self.config),self.api.call('describe_task_definition',taskDefinition=self.config['taskDefinitionArn']))
199
+ guard()
200
+ lease=self.ledger.allocate(identifier);known=None;started=time.monotonic()
201
+ def check():self.ledger.guard(lease);guard();self.ledger.guard(lease)
202
+ try:
203
+ check();condition=self.objects.put(identifier,'input',raw)
204
+ self.ledger.advance(lease,'job_pending');check()
205
+ launched=self.api.call('run_task',cluster=self.cluster,taskDefinition=definition,launchType='EC2',count=1,
206
+ clientToken=identifier,startedBy=identifier,enableExecuteCommand=False,enableECSManagedTags=False,
207
+ overrides=task_overrides(dict(self.config),metadata,condition),
208
+ placementConstraints=hosts.placement(dict(self.config)))
209
+ if launched.get('failures')or len(launched.get('tasks',[]))!=1:raise SourceUnavailable('document_job_unavailable')
210
+ known=self.task(identifier,launched['tasks'][0],definition);self.ledger.advance(lease,'job_running')
211
+ while True:
212
+ check()
213
+ if time.monotonic()-started>self.deadline:raise SourceUnavailable('document_job_unavailable')
214
+ observed=self.api.call('describe_tasks',cluster=self.cluster,tasks=[known])
215
+ if observed.get('failures')or len(observed.get('tasks',[]))!=1:raise SourceUnavailable('document_job_unavailable')
216
+ task=observed['tasks'][0]
217
+ if self.task(identifier,task,definition)!=known:raise SourceUnavailable('document_job_unavailable')
218
+ if task.get('lastStatus')=='STOPPED':
219
+ if task.get('containerInstanceArn')not in {arn for arn,_ in hosts.members}:raise SourceUnavailable('document_host_verification_changed')
220
+ containers=task.get('containers',[])
221
+ if (len(containers)!=3 or {v.get('name')for v in containers}!={'download','parser','upload'}or
222
+ any(v.get('lastStatus')!='STOPPED'or type(v.get('exitCode'))is not int or v['exitCode']!=0 for v in containers)):
223
+ raise SourceUnavailable('document_job_unavailable')
224
+ break
225
+ if task.get('lastStatus')not in {'PROVISIONING','PENDING','ACTIVATING','RUNNING','DEACTIVATING','STOPPING','DEPROVISIONING'}:
226
+ raise SourceUnavailable('document_job_unavailable')
227
+ time.sleep(self.poll_seconds)
228
+ self.ledger.advance(lease,'succeeded');check()
229
+ value=result(raw,mime,identifier,self.objects.load(identifier,'result'));check();return value
230
+ finally:
231
+ cleanup=self.ledger.claim_owner(lease)
232
+ try:self.cleanup(identifier,known=known)
233
+ except BaseException:self.ledger.failed(cleanup);raise
234
+ else:self.ledger.completed(cleanup)
235
+
236
+ def listed(self,operation,key,*,_deadline=None,**values):
237
+ found=[];token=None;seen=set()
238
+ for _ in range(5):
239
+ if _deadline is not None and time.monotonic()>_deadline:raise SourceUnavailable('document_cleanup_unavailable')
240
+ reply=self.api.call(operation,**values,**({'nextToken':token}if token else{}))
241
+ items=reply.get(key)
242
+ if not isinstance(items,list)or any(not isinstance(v,str)for v in items):raise SourceUnavailable('document_cleanup_unavailable')
243
+ found.extend(items);token=reply.get('nextToken')
244
+ if token is None:return list(dict.fromkeys(found))
245
+ if not isinstance(token,str)or not 0<len(token)<=4096 or token in seen:break
246
+ seen.add(token)
247
+ raise SourceUnavailable('document_cleanup_unavailable')
248
+
249
+ def cleanup(self,identifier,*,known=None):
250
+ if not isinstance(identifier,str)or not re.fullmatch('[0-9a-f]{64}',identifier):raise ValueError('Use the owned cleanup identity')
251
+ tasks=[];failure=False;deadline=time.monotonic()+70
252
+ def call(operation,**values):
253
+ if time.monotonic()>deadline:raise SourceUnavailable('document_cleanup_unavailable')
254
+ return self.api.call(operation,**values)
255
+ try:
256
+ # AWS startedBy is the only filter permitted alongside cluster.
257
+ # Default RUNNING desired status also includes pending launches.
258
+ # Unknown terminal tasks are left to native task-volume cleanup;
259
+ # uncertain creation keeps its durable settlement/retry window.
260
+ tasks+=self.listed('list_tasks','taskArns',_deadline=deadline,cluster=self.cluster,startedBy=identifier,maxResults=100)
261
+ if known is not None:tasks.append(known)
262
+ tasks=list(dict.fromkeys(tasks))
263
+ if len(tasks)>10:raise SourceUnavailable('document_cleanup_unavailable')
264
+ for arn in tasks:
265
+ observed=call('describe_tasks',cluster=self.cluster,tasks=[arn])
266
+ if observed.get('failures')or len(observed.get('tasks',[]))!=1:raise SourceUnavailable('document_cleanup_unavailable')
267
+ value=observed['tasks'][0]
268
+ if self.task(identifier,value)!=arn:raise SourceUnavailable('document_cleanup_unavailable')
269
+ if value.get('lastStatus')!='STOPPED':
270
+ call('stop_task',cluster=self.cluster,task=arn,reason='Owned document cleanup')
271
+ started=time.monotonic()
272
+ while True:
273
+ reply=call('describe_tasks',cluster=self.cluster,tasks=[arn])
274
+ if reply.get('failures')or len(reply.get('tasks',[]))!=1:raise SourceUnavailable('document_cleanup_unavailable')
275
+ current=reply['tasks'][0]
276
+ if self.task(identifier,current)!=arn:raise SourceUnavailable('document_cleanup_unavailable')
277
+ if current.get('lastStatus')=='STOPPED':break
278
+ if time.monotonic()-started>20:raise SourceUnavailable('document_cleanup_unavailable')
279
+ time.sleep(self.poll_seconds)
280
+ except Exception:failure=True
281
+ # Even a native task cleanup failure still attempts both owned
282
+ # objects. Return no document text until the entire cleanup succeeds.
283
+ for side in ['input','result']:
284
+ try:self.objects.delete(identifier,side)
285
+ except Exception:failure=True
286
+ if failure:raise SourceUnavailable('document_cleanup_unavailable')
287
+
288
+ def reconcile(self,*,limit=5):
289
+ if self.ledger is None:raise SourceUnavailable('document_job_recovery_required')
290
+ if type(limit)is not int or not 1<=limit<=20:raise ValueError('Use bounded cleanup batches')
291
+ counts={'claimed':0,'retired':0,'settling':0,'failed':0}
292
+ for _ in range(limit):
293
+ lease=self.ledger.claim_due()
294
+ if lease is None:break
295
+ counts['claimed']+=1
296
+ try:self.cleanup(lease.request_id)
297
+ except Exception:self.ledger.failed(lease);counts['failed']+=1
298
+ else:
299
+ retired=self.ledger.completed(lease);counts['retired'if retired else'settling']+=1
300
+ return counts
@@ -0,0 +1,93 @@
1
+ """Bounded, owner-checked temporary S3 document bytes, not a source connector."""
2
+ import base64
3
+ import hashlib
4
+ import re
5
+ import threading
6
+ from enterprise_document_contract import MAX_INPUT
7
+ from enterprise_document_job import MAX_ENVELOPE
8
+ from enterprise_sources import SourceUnavailable
9
+
10
+
11
+ def object_key(identifier,side):
12
+ if not isinstance(identifier,str)or not re.fullmatch('[0-9a-f]{64}',identifier)or side not in {'input','result'}:
13
+ raise ValueError('Use the owned per-file object identity')
14
+ return 'document-jobs/'+identifier+'/'+side
15
+
16
+
17
+ def etag(value):
18
+ if not isinstance(value,str)or not re.fullmatch(r'"[0-9a-f]{32}(?:-[1-9][0-9]{0,4})?"',value):
19
+ raise SourceUnavailable('document_object_unavailable')
20
+ return value
21
+
22
+
23
+ class S3DocumentObjects:
24
+ def __init__(self,bucket,region,account,*,client=None):
25
+ if (not isinstance(bucket,str)or not re.fullmatch('[a-z0-9][a-z0-9-]{1,61}[a-z0-9]',bucket)or
26
+ not isinstance(region,str)or not re.fullmatch(r'(?:af|ap|ca|eu|il|me|mx|sa|us)-(?:east|west|south|north|central|southeast|southwest|northeast|northwest)-[1-9]',region)or
27
+ not isinstance(account,str)or not re.fullmatch('[0-9]{12}',account)):
28
+ raise ValueError('Use an owned regional temporary bucket')
29
+ self.bucket,self.region,self.account=bucket,region,account
30
+ self.client=client;self._lock=threading.Lock()
31
+
32
+ def _client(self):
33
+ if self.client is None:
34
+ with self._lock:
35
+ if self.client is None:
36
+ import boto3
37
+ from botocore.config import Config
38
+ self.client=boto3.Session().client('s3',region_name=self.region,
39
+ endpoint_url='https://s3.'+self.region+'.amazonaws.com',
40
+ config=Config(proxies={},connect_timeout=2,read_timeout=8,retries={'total_max_attempts':1},
41
+ s3={'addressing_style':'virtual'}))
42
+ return self.client
43
+
44
+ def owner(self):return {'Bucket':self.bucket,'ExpectedBucketOwner':self.account}
45
+
46
+ def preflight(self):
47
+ """No payload mutation if native placement or no-history policy changed."""
48
+ try:
49
+ client=self._client();where=client.get_bucket_location(**self.owner()).get('LocationConstraint')
50
+ region='us-east-1'if where is None else'eu-west-1'if where=='EU'else where
51
+ history=client.get_bucket_versioning(**self.owner())
52
+ if region!=self.region or history.get('Status')is not None or history.get('MFADelete')is not None:raise ValueError()
53
+ except Exception:raise SourceUnavailable('document_object_policy_unavailable')from None
54
+
55
+ def put(self,identifier,side,raw):
56
+ key=object_key(identifier,side);maximum=MAX_INPUT if side=='input'else MAX_ENVELOPE
57
+ if not isinstance(raw,bytes)or not 0<len(raw)<=maximum:raise ValueError('Use bounded owned document bytes')
58
+ self.preflight()
59
+ try:
60
+ value=self._client().put_object(**self.owner(),Key=key,Body=raw,IfNoneMatch='*',
61
+ ContentType='application/octet-stream',ServerSideEncryption='AES256',
62
+ ChecksumSHA256=base64.b64encode(hashlib.sha256(raw).digest()).decode())
63
+ if value.get('VersionId')not in {None,'null'}or value.get('ServerSideEncryption')!='AES256':raise ValueError()
64
+ return etag(value.get('ETag'))
65
+ except Exception:raise SourceUnavailable('document_object_unavailable')from None
66
+
67
+ def load(self,identifier,side,condition=None):
68
+ key=object_key(identifier,side);maximum=MAX_INPUT if side=='input'else MAX_ENVELOPE
69
+ extra={}if condition is None else {'IfMatch':etag(condition)}
70
+ self.preflight()
71
+ stream=None
72
+ try:
73
+ value=self._client().get_object(**self.owner(),Key=key,**extra);stream=value['Body']
74
+ if (value.get('VersionId')not in {None,'null'}or value.get('ServerSideEncryption')!='AES256'or
75
+ type(value.get('ContentLength'))is not int or not 0<value['ContentLength']<=maximum or
76
+ (condition is not None and value.get('ETag')!=condition)):raise ValueError()
77
+ raw=stream.read(maximum+1)
78
+ if not isinstance(raw,bytes)or len(raw)!=value['ContentLength']or not 0<len(raw)<=maximum:raise ValueError()
79
+ return raw
80
+ except Exception:raise SourceUnavailable('document_object_unavailable')from None
81
+ finally:
82
+ if stream is not None:
83
+ try:stream.close()
84
+ except Exception:raise SourceUnavailable('document_object_unavailable')from None
85
+
86
+ def delete(self,identifier,side):
87
+ key=object_key(identifier,side)
88
+ # Never treat a delete marker as erasure of retained object versions.
89
+ self.preflight()
90
+ try:
91
+ value=self._client().delete_object(**self.owner(),Key=key)
92
+ if value.get('DeleteMarker')or value.get('VersionId')not in {None,'null'}:raise ValueError()
93
+ except Exception:raise SourceUnavailable('document_cleanup_unavailable')from None