@omnitron-dev/omnitron 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (374) hide show
  1. package/README.md +4 -0
  2. package/dist/BUILD.json +2 -2
  3. package/dist/cli/omnitron.js +26 -2
  4. package/dist/cli/omnitron.js.map +1 -1
  5. package/dist/cluster/mesh-link.js +1 -1
  6. package/dist/cluster/slave-connector.d.ts +2 -2
  7. package/dist/cluster/slave-connector.js +1 -1
  8. package/dist/commands/deploy.d.ts +4 -4
  9. package/dist/commands/deploy.js +4 -4
  10. package/dist/commands/discover.d.ts +2 -2
  11. package/dist/commands/discover.js +2 -2
  12. package/dist/commands/fleet-asking.d.ts +2 -2
  13. package/dist/commands/fleet-asking.js +2 -2
  14. package/dist/commands/fleet-recover.d.ts +2 -0
  15. package/dist/commands/fleet-recover.d.ts.map +1 -0
  16. package/dist/commands/fleet-recover.js +30 -0
  17. package/dist/commands/fleet-recover.js.map +1 -0
  18. package/dist/commands/host-ingress.d.ts +3 -0
  19. package/dist/commands/host-ingress.d.ts.map +1 -0
  20. package/dist/commands/host-ingress.js +31 -0
  21. package/dist/commands/host-ingress.js.map +1 -0
  22. package/dist/commands/infra.d.ts +2 -2
  23. package/dist/commands/infra.js +7 -7
  24. package/dist/commands/infra.js.map +1 -1
  25. package/dist/commands/logs.d.ts +1 -1
  26. package/dist/commands/logs.js +3 -3
  27. package/dist/commands/logs.js.map +1 -1
  28. package/dist/commands/node.d.ts +2 -14
  29. package/dist/commands/node.d.ts.map +1 -1
  30. package/dist/commands/node.js +33 -22
  31. package/dist/commands/node.js.map +1 -1
  32. package/dist/commands/pipeline.d.ts.map +1 -1
  33. package/dist/commands/pipeline.js +8 -1
  34. package/dist/commands/pipeline.js.map +1 -1
  35. package/dist/commands/project.d.ts +2 -2
  36. package/dist/commands/project.js +2 -2
  37. package/dist/commands/release.d.ts +1 -1
  38. package/dist/commands/release.js +4 -4
  39. package/dist/commands/release.js.map +1 -1
  40. package/dist/commands/remote.d.ts +3 -3
  41. package/dist/commands/remote.js +3 -3
  42. package/dist/commands/tor.d.ts +1 -1
  43. package/dist/commands/tor.js +2 -2
  44. package/dist/config/types.d.ts +9 -6
  45. package/dist/config/types.d.ts.map +1 -1
  46. package/dist/daemon/daemon-entry.js +1 -1
  47. package/dist/daemon/daemon-entry.js.map +1 -1
  48. package/dist/daemon/daemon.d.ts +5 -19
  49. package/dist/daemon/daemon.d.ts.map +1 -1
  50. package/dist/daemon/daemon.js +54 -51
  51. package/dist/daemon/daemon.js.map +1 -1
  52. package/dist/daemon/daemon.module.d.ts.map +1 -1
  53. package/dist/daemon/daemon.module.js +3 -5
  54. package/dist/daemon/daemon.module.js.map +1 -1
  55. package/dist/daemon/daemon.rpc-service.d.ts +2 -2
  56. package/dist/daemon/daemon.rpc-service.js +6 -6
  57. package/dist/daemon/daemon.rpc-service.js.map +1 -1
  58. package/dist/execution/data-channel.d.ts +1 -1
  59. package/dist/execution/data-channel.js +1 -1
  60. package/dist/execution/execution.service.d.ts +10 -3
  61. package/dist/execution/execution.service.d.ts.map +1 -1
  62. package/dist/execution/execution.service.js +101 -44
  63. package/dist/execution/execution.service.js.map +1 -1
  64. package/dist/infrastructure/bare-metal-plan.js +1 -1
  65. package/dist/infrastructure/bare-metal-plan.js.map +1 -1
  66. package/dist/infrastructure/config-payload.d.ts +4 -4
  67. package/dist/infrastructure/config-payload.js +4 -4
  68. package/dist/infrastructure/container-runtime.d.ts +2 -10
  69. package/dist/infrastructure/container-runtime.d.ts.map +1 -1
  70. package/dist/infrastructure/container-runtime.js +41 -20
  71. package/dist/infrastructure/container-runtime.js.map +1 -1
  72. package/dist/infrastructure/gateway-redis-db.d.ts +2 -2
  73. package/dist/infrastructure/gateway-redis-db.js +2 -2
  74. package/dist/infrastructure/host-ingress-config.d.ts +12 -0
  75. package/dist/infrastructure/host-ingress-config.d.ts.map +1 -0
  76. package/dist/infrastructure/host-ingress-config.js +197 -0
  77. package/dist/infrastructure/host-ingress-config.js.map +1 -0
  78. package/dist/infrastructure/host-ingress-lock.d.ts +4 -0
  79. package/dist/infrastructure/host-ingress-lock.d.ts.map +1 -0
  80. package/dist/infrastructure/host-ingress-lock.js +36 -0
  81. package/dist/infrastructure/host-ingress-lock.js.map +1 -0
  82. package/dist/infrastructure/host-ingress-runtime.d.ts +25 -0
  83. package/dist/infrastructure/host-ingress-runtime.d.ts.map +1 -0
  84. package/dist/infrastructure/host-ingress-runtime.js +114 -0
  85. package/dist/infrastructure/host-ingress-runtime.js.map +1 -0
  86. package/dist/infrastructure/host-ingress-takeover.d.ts +18 -0
  87. package/dist/infrastructure/host-ingress-takeover.d.ts.map +1 -0
  88. package/dist/infrastructure/host-ingress-takeover.js +142 -0
  89. package/dist/infrastructure/host-ingress-takeover.js.map +1 -0
  90. package/dist/infrastructure/host-ingress-types.d.ts +173 -0
  91. package/dist/infrastructure/host-ingress-types.d.ts.map +1 -0
  92. package/dist/infrastructure/host-ingress-types.js +2 -0
  93. package/dist/infrastructure/host-ingress-types.js.map +1 -0
  94. package/dist/infrastructure/host-ingress.service.d.ts +47 -0
  95. package/dist/infrastructure/host-ingress.service.d.ts.map +1 -0
  96. package/dist/infrastructure/host-ingress.service.js +514 -0
  97. package/dist/infrastructure/host-ingress.service.js.map +1 -0
  98. package/dist/infrastructure/infrastructure.service.d.ts +21 -5
  99. package/dist/infrastructure/infrastructure.service.d.ts.map +1 -1
  100. package/dist/infrastructure/infrastructure.service.js +91 -10
  101. package/dist/infrastructure/infrastructure.service.js.map +1 -1
  102. package/dist/infrastructure/orphan-containers.d.ts +7 -7
  103. package/dist/infrastructure/orphan-containers.js +6 -6
  104. package/dist/infrastructure/presets/minio.js +1 -1
  105. package/dist/infrastructure/presets/minio.js.map +1 -1
  106. package/dist/infrastructure/provisioning-outcome.d.ts +1 -1
  107. package/dist/infrastructure/provisioning-outcome.js +1 -1
  108. package/dist/infrastructure/service-binding.js +1 -1
  109. package/dist/infrastructure/service-resolver.d.ts +10 -4
  110. package/dist/infrastructure/service-resolver.d.ts.map +1 -1
  111. package/dist/infrastructure/service-resolver.js +88 -45
  112. package/dist/infrastructure/service-resolver.js.map +1 -1
  113. package/dist/infrastructure/shipped-config.js +4 -4
  114. package/dist/infrastructure/stack-infra-manager.d.ts +10 -1
  115. package/dist/infrastructure/stack-infra-manager.d.ts.map +1 -1
  116. package/dist/infrastructure/stack-infra-manager.js +53 -43
  117. package/dist/infrastructure/stack-infra-manager.js.map +1 -1
  118. package/dist/infrastructure/types.d.ts +8 -6
  119. package/dist/infrastructure/types.d.ts.map +1 -1
  120. package/dist/monitoring/log-line.d.ts +3 -3
  121. package/dist/monitoring/log-line.js +3 -3
  122. package/dist/monitoring/log-manager.d.ts +3 -3
  123. package/dist/monitoring/log-manager.js +3 -3
  124. package/dist/operations.d.ts +16 -0
  125. package/dist/operations.d.ts.map +1 -0
  126. package/dist/operations.js +13 -0
  127. package/dist/operations.js.map +1 -0
  128. package/dist/orchestrator/app-handle.d.ts +3 -3
  129. package/dist/orchestrator/app-handle.js +3 -3
  130. package/dist/orchestrator/bootstrap-loader.d.ts +2 -2
  131. package/dist/orchestrator/bootstrap-loader.js +3 -3
  132. package/dist/orchestrator/bootstrap-loader.js.map +1 -1
  133. package/dist/orchestrator/bootstrap-process.d.ts +1 -1
  134. package/dist/orchestrator/bootstrap-process.d.ts.map +1 -1
  135. package/dist/orchestrator/bootstrap-process.js +3 -2
  136. package/dist/orchestrator/bootstrap-process.js.map +1 -1
  137. package/dist/orchestrator/build-service.d.ts +6 -12
  138. package/dist/orchestrator/build-service.d.ts.map +1 -1
  139. package/dist/orchestrator/build-service.js +120 -83
  140. package/dist/orchestrator/build-service.js.map +1 -1
  141. package/dist/orchestrator/call-exposed-service.js +1 -1
  142. package/dist/orchestrator/classic-launcher.d.ts.map +1 -1
  143. package/dist/orchestrator/classic-launcher.js +6 -1
  144. package/dist/orchestrator/classic-launcher.js.map +1 -1
  145. package/dist/orchestrator/file-watcher.d.ts +1 -0
  146. package/dist/orchestrator/file-watcher.d.ts.map +1 -1
  147. package/dist/orchestrator/file-watcher.js +14 -3
  148. package/dist/orchestrator/file-watcher.js.map +1 -1
  149. package/dist/orchestrator/http-routes.d.ts +4 -0
  150. package/dist/orchestrator/http-routes.d.ts.map +1 -0
  151. package/dist/orchestrator/http-routes.js +19 -0
  152. package/dist/orchestrator/http-routes.js.map +1 -0
  153. package/dist/orchestrator/orchestrator.service.d.ts +18 -4
  154. package/dist/orchestrator/orchestrator.service.d.ts.map +1 -1
  155. package/dist/orchestrator/orchestrator.service.js +151 -110
  156. package/dist/orchestrator/orchestrator.service.js.map +1 -1
  157. package/dist/orchestrator/service-router.d.ts +1 -1
  158. package/dist/orchestrator/service-router.js +1 -1
  159. package/dist/orchestrator/topology-query.d.ts +1 -1
  160. package/dist/orchestrator/topology-query.js +1 -1
  161. package/dist/project/artifact-builder.js +2 -2
  162. package/dist/project/artifact-builder.js.map +1 -1
  163. package/dist/project/build-decision.d.ts +1 -1
  164. package/dist/project/build-decision.js +1 -1
  165. package/dist/project/config-resolver.js +1 -1
  166. package/dist/project/config-resolver.js.map +1 -1
  167. package/dist/project/declared-config.d.ts +2 -2
  168. package/dist/project/declared-config.js +2 -2
  169. package/dist/project/deploy-phases.d.ts +1 -1
  170. package/dist/project/deploy-phases.js +1 -1
  171. package/dist/project/exec-in-group.d.ts +1 -1
  172. package/dist/project/exec-in-group.js +1 -1
  173. package/dist/project/node-app-config.d.ts +2 -2
  174. package/dist/project/node-app-config.js +2 -2
  175. package/dist/project/node-app-config.js.map +1 -1
  176. package/dist/project/node-app-health.d.ts +1 -1
  177. package/dist/project/node-app-health.js +1 -1
  178. package/dist/project/operator-account.d.ts +9 -3
  179. package/dist/project/operator-account.d.ts.map +1 -1
  180. package/dist/project/operator-account.js +9 -9
  181. package/dist/project/operator-account.js.map +1 -1
  182. package/dist/project/registry.d.ts.map +1 -1
  183. package/dist/project/registry.js +4 -22
  184. package/dist/project/registry.js.map +1 -1
  185. package/dist/project/stack-env.d.ts +1 -1
  186. package/dist/project/stack-env.js +1 -1
  187. package/dist/project/stand-containers.d.ts +9 -2
  188. package/dist/project/stand-containers.d.ts.map +1 -1
  189. package/dist/project/stand-containers.js +17 -17
  190. package/dist/project/stand-containers.js.map +1 -1
  191. package/dist/release/artifact-store.d.ts +1 -1
  192. package/dist/release/artifact-store.d.ts.map +1 -1
  193. package/dist/release/artifact-store.js +1 -1
  194. package/dist/release/artifact-store.js.map +1 -1
  195. package/dist/release/attest-on-node.d.ts +3 -2
  196. package/dist/release/attest-on-node.d.ts.map +1 -1
  197. package/dist/release/attest-on-node.js +6 -6
  198. package/dist/release/attest-on-node.js.map +1 -1
  199. package/dist/release/attest.d.ts +3 -3
  200. package/dist/release/attest.js +1 -1
  201. package/dist/release/build-run.d.ts +2 -2
  202. package/dist/release/build-run.js +4 -4
  203. package/dist/release/build-run.js.map +1 -1
  204. package/dist/release/builder.d.ts +1 -1
  205. package/dist/release/builder.d.ts.map +1 -1
  206. package/dist/release/builder.js +1 -1
  207. package/dist/release/builder.js.map +1 -1
  208. package/dist/release/daemon-omni.d.ts +1 -1
  209. package/dist/release/daemon-omni.js +1 -1
  210. package/dist/release/definition-inputs.d.ts +1 -1
  211. package/dist/release/definition-inputs.js +1 -1
  212. package/dist/release/layout.d.ts +2 -2
  213. package/dist/release/layout.js +2 -2
  214. package/dist/release/manifest.d.ts +1 -1
  215. package/dist/release/manifest.js +1 -1
  216. package/dist/release/publish.d.ts +1 -1
  217. package/dist/release/publish.js +1 -1
  218. package/dist/release/store.d.ts +1 -1
  219. package/dist/release/store.d.ts.map +1 -1
  220. package/dist/release/store.js +3 -3
  221. package/dist/release/store.js.map +1 -1
  222. package/dist/services/alert.service.d.ts +2 -2
  223. package/dist/services/alert.service.js +2 -2
  224. package/dist/services/backup-pipeline.d.ts +1 -1
  225. package/dist/services/backup-pipeline.js +1 -1
  226. package/dist/services/backup.service.d.ts +3 -3
  227. package/dist/services/backup.service.js +3 -3
  228. package/dist/services/bundle-builder.d.ts +15 -12
  229. package/dist/services/bundle-builder.d.ts.map +1 -1
  230. package/dist/services/bundle-builder.js +124 -48
  231. package/dist/services/bundle-builder.js.map +1 -1
  232. package/dist/services/discovery.service.d.ts +1 -1
  233. package/dist/services/discovery.service.js +1 -1
  234. package/dist/services/host-operation-barrier.d.ts +8 -0
  235. package/dist/services/host-operation-barrier.d.ts.map +1 -0
  236. package/dist/services/host-operation-barrier.js +44 -0
  237. package/dist/services/host-operation-barrier.js.map +1 -0
  238. package/dist/services/host-upgrade.d.ts +129 -0
  239. package/dist/services/host-upgrade.d.ts.map +1 -0
  240. package/dist/services/host-upgrade.js +352 -0
  241. package/dist/services/host-upgrade.js.map +1 -0
  242. package/dist/services/infrastructure.rpc-service.d.ts +16 -1
  243. package/dist/services/infrastructure.rpc-service.d.ts.map +1 -1
  244. package/dist/services/infrastructure.rpc-service.js +71 -10
  245. package/dist/services/infrastructure.rpc-service.js.map +1 -1
  246. package/dist/services/local-bundle.d.ts +9 -0
  247. package/dist/services/local-bundle.d.ts.map +1 -1
  248. package/dist/services/local-bundle.js +11 -2
  249. package/dist/services/local-bundle.js.map +1 -1
  250. package/dist/services/log-collector.service.d.ts +1 -1
  251. package/dist/services/log-collector.service.js +1 -1
  252. package/dist/services/maintenance-lock.d.ts +2 -2
  253. package/dist/services/maintenance-lock.d.ts.map +1 -1
  254. package/dist/services/maintenance-lock.js +1 -1
  255. package/dist/services/migration-output.d.ts +2 -2
  256. package/dist/services/migration-output.js +2 -2
  257. package/dist/services/node-deploy-lease.d.ts +19 -2
  258. package/dist/services/node-deploy-lease.d.ts.map +1 -1
  259. package/dist/services/node-deploy-lease.js +84 -10
  260. package/dist/services/node-deploy-lease.js.map +1 -1
  261. package/dist/services/node-manager.rpc-service.d.ts +6 -1
  262. package/dist/services/node-manager.rpc-service.d.ts.map +1 -1
  263. package/dist/services/node-manager.rpc-service.js +25 -1
  264. package/dist/services/node-manager.rpc-service.js.map +1 -1
  265. package/dist/services/node-manager.service.d.ts +1 -1
  266. package/dist/services/node-manager.service.js +1 -1
  267. package/dist/services/node-upgrade.d.ts +1 -1
  268. package/dist/services/pipeline.rpc-service.d.ts +4 -0
  269. package/dist/services/pipeline.rpc-service.d.ts.map +1 -1
  270. package/dist/services/pipeline.rpc-service.js +9 -0
  271. package/dist/services/pipeline.rpc-service.js.map +1 -1
  272. package/dist/services/pipeline.service.d.ts +2 -0
  273. package/dist/services/pipeline.service.d.ts.map +1 -1
  274. package/dist/services/pipeline.service.js +12 -0
  275. package/dist/services/pipeline.service.js.map +1 -1
  276. package/dist/services/project.rpc-service.d.ts +7 -0
  277. package/dist/services/project.rpc-service.d.ts.map +1 -1
  278. package/dist/services/project.rpc-service.js +11 -0
  279. package/dist/services/project.rpc-service.js.map +1 -1
  280. package/dist/services/project.service.d.ts +21 -11
  281. package/dist/services/project.service.d.ts.map +1 -1
  282. package/dist/services/project.service.js +74 -59
  283. package/dist/services/project.service.js.map +1 -1
  284. package/dist/services/redeploy-decision.d.ts +2 -2
  285. package/dist/services/redeploy-decision.js +1 -1
  286. package/dist/services/release.rpc-service.d.ts +1 -1
  287. package/dist/services/release.rpc-service.js +1 -1
  288. package/dist/services/remote-deployer.service.d.ts +27 -16
  289. package/dist/services/remote-deployer.service.d.ts.map +1 -1
  290. package/dist/services/remote-deployer.service.js +269 -101
  291. package/dist/services/remote-deployer.service.js.map +1 -1
  292. package/dist/services/remote-provisioner.d.ts +2 -0
  293. package/dist/services/remote-provisioner.d.ts.map +1 -1
  294. package/dist/services/remote-provisioner.js +1 -1
  295. package/dist/services/remote-provisioner.js.map +1 -1
  296. package/dist/services/static-carry-forward.d.ts +26 -1
  297. package/dist/services/static-carry-forward.d.ts.map +1 -1
  298. package/dist/services/static-carry-forward.js +275 -1
  299. package/dist/services/static-carry-forward.js.map +1 -1
  300. package/dist/shared/dto/nodes.d.ts +2 -2
  301. package/dist/shared/dto/services.d.ts +27 -0
  302. package/dist/shared/dto/services.d.ts.map +1 -1
  303. package/dist/shared/local-infra-host.d.ts +2 -2
  304. package/dist/shared/local-infra-host.js +2 -2
  305. package/dist/shared/release-reading.d.ts +1 -1
  306. package/dist/shared/release-reading.js +1 -1
  307. package/package.json +16 -12
  308. package/webapp/dist/BUILD.json +2 -2
  309. package/webapp/dist/assets/FormControlLabel-BbSxpQwN.js +1 -0
  310. package/webapp/dist/assets/{Grid-BZ7xhoHQ.js → Grid-DizFZYue.js} +1 -1
  311. package/webapp/dist/assets/{Switch-DGSAxQPM.js → Switch-Bfiqzx8l.js} +1 -1
  312. package/webapp/dist/assets/alerts-DH_RVRS3.js +1 -0
  313. package/webapp/dist/assets/apps-B96355sz.js +1 -0
  314. package/webapp/dist/assets/audit-CYGtbi4k.js +1 -0
  315. package/webapp/dist/assets/client-BmqFExWT.js +1 -0
  316. package/webapp/dist/assets/containers-B_TpThln.js +2 -0
  317. package/webapp/dist/assets/dashboard-DHf2_65N.js +1 -0
  318. package/webapp/dist/assets/dashboard-builder-CWY2pjeG.js +1 -0
  319. package/webapp/dist/assets/{deploy-release-dialog-D6C-XcKY.js → deploy-release-dialog-Dp-zpSAd.js} +1 -1
  320. package/webapp/dist/assets/deployments-tJI6O3fK.js +1 -0
  321. package/webapp/dist/assets/detail-B2zP1Hto.js +8 -0
  322. package/webapp/dist/assets/detail-CoIrwmsy.js +1 -0
  323. package/webapp/dist/assets/detail-DWaGaOtL.js +2 -0
  324. package/webapp/dist/assets/index-Dvo0iprx.css +1 -0
  325. package/webapp/dist/assets/index-vMnY_4jR.js +412 -0
  326. package/webapp/dist/assets/logs-DTRjOfX8.js +12 -0
  327. package/webapp/dist/assets/maplibre-gl-DowHoZAx.js +814 -0
  328. package/webapp/dist/assets/{metrics-CQgjz2Lz.js → metrics-UCajtdox.js} +2 -2
  329. package/webapp/dist/assets/nodes-DuTVU0f6.js +7 -0
  330. package/webapp/dist/assets/pipelines-C8BeDcAl.js +4 -0
  331. package/webapp/dist/assets/projects-Dp_zFfLY.js +1 -0
  332. package/webapp/dist/assets/react-DlmbjZJV.js +1 -0
  333. package/webapp/dist/assets/react-apexcharts.esm-C8pn45ml.js +97 -0
  334. package/webapp/dist/assets/{release-bits-Dj4EOhhb.js → release-bits-BzTKz5fM.js} +1 -1
  335. package/webapp/dist/assets/release-build-panel-DcW9YcXQ.js +1 -0
  336. package/webapp/dist/assets/releases-B4hcr1bE.js +2 -0
  337. package/webapp/dist/assets/rolldown-runtime-XKyaHtl9.js +1 -0
  338. package/webapp/dist/assets/settings-DIteZTbe.js +1 -0
  339. package/webapp/dist/assets/sign-in-D4kMBOHp.js +1 -0
  340. package/webapp/dist/assets/stacks-Brnf-EB7.js +1 -0
  341. package/webapp/dist/assets/{system-info-BtE3dgfw.js → system-info-CFDNVKkU.js} +1 -1
  342. package/webapp/dist/assets/topology-7WeTUsDN.js +12 -0
  343. package/webapp/dist/assets/traces-9c3gwCN-.js +1 -0
  344. package/webapp/dist/assets/{use-stack-context-CZUgPq88.js → use-stack-context-CnA5FTHu.js} +1 -1
  345. package/webapp/dist/index.html +6 -6
  346. package/webapp/dist/assets/FormControlLabel-BrH2diyl.js +0 -1
  347. package/webapp/dist/assets/alerts-sXqHJbsa.js +0 -1
  348. package/webapp/dist/assets/apps-B_Ah8Flh.js +0 -1
  349. package/webapp/dist/assets/audit-YXihwyui.js +0 -1
  350. package/webapp/dist/assets/client-CGbBvy9k.js +0 -1
  351. package/webapp/dist/assets/containers-Di4NOTLf.js +0 -2
  352. package/webapp/dist/assets/dashboard-Cfuf95N3.js +0 -1
  353. package/webapp/dist/assets/dashboard-builder-CowaYb-M.js +0 -1
  354. package/webapp/dist/assets/deployments-ChTq5kCw.js +0 -1
  355. package/webapp/dist/assets/detail-BsGWv7Dj.js +0 -1
  356. package/webapp/dist/assets/detail-CyVf_cw5.js +0 -2
  357. package/webapp/dist/assets/detail-D027j6aO.js +0 -8
  358. package/webapp/dist/assets/index-B8AHIPc-.css +0 -1
  359. package/webapp/dist/assets/index-DiZbrwxT.js +0 -414
  360. package/webapp/dist/assets/logs-L2b8Oqmo.js +0 -12
  361. package/webapp/dist/assets/maplibre-gl-MlmG2kbn.js +0 -801
  362. package/webapp/dist/assets/nodes-Nwia2Ty6.js +0 -7
  363. package/webapp/dist/assets/pipelines-DCu_rXOr.js +0 -4
  364. package/webapp/dist/assets/projects-Bb4aV76h.js +0 -1
  365. package/webapp/dist/assets/react-CwJFpaho.js +0 -1
  366. package/webapp/dist/assets/react-apexcharts.esm-Dy0Fez42.js +0 -1956
  367. package/webapp/dist/assets/release-build-panel-D6OCVlhT.js +0 -1
  368. package/webapp/dist/assets/releases-CnAt3yO6.js +0 -2
  369. package/webapp/dist/assets/rolldown-runtime-hePW80VL.js +0 -1
  370. package/webapp/dist/assets/settings-DU-mjiwb.js +0 -1
  371. package/webapp/dist/assets/sign-in-Bcvjrr4C.js +0 -1
  372. package/webapp/dist/assets/stacks-B-e8p50I.js +0 -1
  373. package/webapp/dist/assets/topology-Cwch-uF1.js +0 -12
  374. package/webapp/dist/assets/traces-BbShR-4P.js +0 -1
@@ -49,18 +49,21 @@
49
49
  * to put them.
50
50
  */
51
51
  import { PLATFORM_PROBE, parsePlatformProbe, planProvisioning, describePlan, } from './remote-provisioner.js';
52
- import { installSteps, activateSteps, pruneSteps } from './bundle-builder.js';
52
+ import { installSteps, installPreflightCommand, versionDir, activateSteps, pruneSteps } from './bundle-builder.js';
53
53
  import { reachabilityRule, reachabilityCommand } from '../infrastructure/gateway-reachability.js';
54
54
  import { readNodeHealth, readNodeStatus } from '../project/node-app-health.js';
55
55
  import { decideRedeploy, decideNodeStackStart, decideSlaveDaemonRestart, artifactChanged, readChecksumCommand, parseRecordedChecksum, ARTIFACT_CHECKSUM_FILE, NODE_CONFIG_HASH_FILE, } from './redeploy-decision.js';
56
56
  import { deliveredProbeCommand, parseDeliveredProbe, checkDelivered, } from '../release/delivered.js';
57
57
  import crypto from 'node:crypto';
58
+ import { AsyncLocalStorage } from 'node:async_hooks';
59
+ import { assertControlledDaemonPort, assertServingBundle, bundleProbeCommand, contractPreflightCommand, executeHostUpgrade, hookCommand, inventoryCommand, planHostUpgrade, receiptCommand, verifyRestoredInventory, updateContractBindingsCommand, runtimeProbeCommand, runtimeStartCommand, runtimeStopCommand, appHealthCommand, pendingReceiptsCommand, recoverHostUpgrade, stoppedDaemonCommand, } from './host-upgrade.js';
58
60
  import { gunzipSync } from 'node:zlib';
59
61
  import { shellEscape } from '../shared/shell-escape.js';
60
62
  import { fromDataChannel, throughDataChannel } from '../execution/data-channel.js';
61
63
  /** A node that could not be asked at all — the SSH call itself failed. */
62
64
  const UNREADABLE = -3;
63
65
  import { withNodeLeases } from './node-deploy-lease.js';
66
+ import { hostOperationBarrierCommand } from './host-operation-barrier.js';
64
67
  import { migrationOutput } from './migration-output.js';
65
68
  /**
66
69
  * A name that is safe to place in a remote filesystem path.
@@ -231,6 +234,7 @@ export function wholeTarGz(archive) {
231
234
  export class RemoteDeployer {
232
235
  logger;
233
236
  execution;
237
+ deploymentLeases = new AsyncLocalStorage();
234
238
  /** Active deployment progress handlers */
235
239
  progressHandlers = [];
236
240
  /**
@@ -417,7 +421,7 @@ export class RemoteDeployer {
417
421
  // Titan package identity mismatch detected between omnitron daemon
418
422
  // and app 'geo'. Daemon resolves @omnitron-dev/titan from
419
423
  // /opt/omnitron/versions/…; App resolves … from /opt/omnitron/
420
- // artifacts/daos/geo/0.0.1/node_modules/@omnitron-dev/titan
424
+ // artifacts/sample/geo/0.0.1/node_modules/@omnitron-dev/titan
421
425
  //
422
426
  // That is the correct refusal, and the answer is not to install less:
423
427
  // the vendored copies are what let `npm install` resolve a coherent
@@ -449,7 +453,7 @@ export class RemoteDeployer {
449
453
  // puts the link INSIDE it, leaving a directory with no package.json
450
454
  // that node reports as a missing `index.js` — a message about a file
451
455
  // that was never the problem.
452
- const daemonRoot = '/opt/omnitron/current';
456
+ const daemonRoot = (await this.inspectInstalledBundle(target)).root;
453
457
  await this.sshExec(target, `set -e; APP=${shellEscape(`${remotePath}/node_modules/@omnitron-dev`)}; ` +
454
458
  `mkdir -p "$APP"; ` +
455
459
  `for p in ${shellEscape(daemonRoot)}/node_modules/@omnitron-dev/*; do ` +
@@ -480,7 +484,7 @@ export class RemoteDeployer {
480
484
  // 5b. No record yet. The next deployment reads `.artifact-sha256` as
481
485
  // «the app runs this», and at this point it does not: the files are
482
486
  // unpacked, the process still runs what it started with. The record
483
- // used to be written here, and on daos/test (2026-09-24) a deployment
487
+ // used to be written here, and on sample/test (2026-09-24) a deployment
484
488
  // that stalled after installing three artifacts left them recorded;
485
489
  // the retry read «installed, running» and left all three on the code
486
490
  // they had started with an hour earlier. It is written once the app
@@ -746,7 +750,7 @@ export class RemoteDeployer {
746
750
  *
747
751
  * Measured on the test node after six apps installed and five started:
748
752
  * every one of the six databases existed and held ZERO tables. Five apps
749
- * came up anyway, because they touch no table at boot; `paysys` does, and
753
+ * came up anyway, because they touch no table at boot; `payments` does, and
750
754
  * said so —
751
755
  *
752
756
  * Failed to call @PostConstruct 'bootstrap' on 'PlatformRevenueService':
@@ -757,7 +761,7 @@ export class RemoteDeployer {
757
761
  * refuses to start.
758
762
  *
759
763
  * The artifact already carries what is needed: `dist/database/migrate.js`
760
- * beside `dist/database/migrations/` — 160 compiled files for paysys. The
764
+ * beside `dist/database/migrations/` — 160 compiled files for payments. The
761
765
  * same build the app itself runs from, so there is no second copy of the
762
766
  * schema to drift.
763
767
  *
@@ -780,7 +784,7 @@ export class RemoteDeployer {
780
784
  * running the batch again after the cause is fixed applies exactly what
781
785
  * is missing. That holds because `HardenedMigrationRunner` defaults
782
786
  * `useTransactions` to true (`titan-database/src/migration/
783
- * hardened-runner.ts:171`) and no daos `migrate.ts` overrides it — it is
787
+ * hardened-runner.ts:171`) and no sample `migrate.ts` overrides it — it is
784
788
  * conditional in the runner (`:502`), so a migrator that turned it off
785
789
  * would make a refused deployment leave a half-applied migration behind.
786
790
  */
@@ -815,9 +819,9 @@ export class RemoteDeployer {
815
819
  this.emitProgress(`${target.host}:${target.daemonPort ?? 9700}`, entry.app, 'installing', 75, 'Applying database migrations...');
816
820
  // Both spellings, because the migrators read the second one.
817
821
  //
818
- // `apps/paysys/src/database/migrate.ts` declares
819
- // `envPrefix: 'PAYSYS__DATABASE'` and builds its connection from
820
- // `PAYSYS__DATABASE__HOST` / `__PORT` / `__USER` / `__PASSWORD` /
822
+ // `apps/payments/src/database/migrate.ts` declares
823
+ // `envPrefix: 'PAYMENTS__DATABASE'` and builds its connection from
824
+ // `PAYMENTS__DATABASE__HOST` / `__PORT` / `__USER` / `__PASSWORD` /
821
825
  // `__DATABASE`. Given only `DATABASE_URL` it falls back to its own
822
826
  // defaults and reports `password authentication failed for user
823
827
  // "postgres"` — against a password that is correct and that it never
@@ -1194,17 +1198,20 @@ export class RemoteDeployer {
1194
1198
  async installBundle(target, archivePath, version, prefix = '/opt/omnitron') {
1195
1199
  const nodeKey = `${target.host}:${target.daemonPort ?? 9700}`;
1196
1200
  const layout = { prefix, version };
1197
- // Under the prefix, not `/tmp`: a bundle is tens of megabytes and `/tmp`
1198
- // is a tmpfs on many hosts, where a large transfer competes with memory.
1199
- const remoteArchive = `${prefix}/releases/${version}.tar.gz`;
1200
1201
  try {
1202
+ // Reject path traversal before any connection, upload or filesystem mutation.
1203
+ versionDir(layout);
1204
+ const fs = await import('node:fs/promises');
1205
+ const archiveSha256 = crypto.createHash('sha256').update(await fs.readFile(archivePath)).digest('hex');
1206
+ // Content-addressed transfer cannot replace an earlier archive with different bytes.
1207
+ const remoteArchive = `${prefix}/releases/${version}.${archiveSha256}.tar.gz`;
1201
1208
  this.emitProgress(nodeKey, '*', 'pending', 0, 'Connecting via SSH...');
1202
1209
  await this.verifySSH(target);
1203
1210
  this.emitProgress(nodeKey, '*', 'transferring', 10, `Transferring ${version}...`);
1204
- await this.sshExec(target, `mkdir -p ${shellEscape(`${prefix}/releases`)}`);
1211
+ await this.sshExec(target, installPreflightCommand(layout));
1205
1212
  await this.scpTransfer(target, archivePath, remoteArchive);
1206
1213
  let progress = 30;
1207
- for (const step of installSteps(layout, remoteArchive)) {
1214
+ for (const step of installSteps(layout, remoteArchive, archiveSha256)) {
1208
1215
  this.emitProgress(nodeKey, '*', 'extracting', progress, step.what);
1209
1216
  const output = await this.sshExec(target, step.command, step.timeoutMs);
1210
1217
  if (step.what.includes('runs')) {
@@ -1238,83 +1245,171 @@ export class RemoteDeployer {
1238
1245
  * left to expire by itself. A node with no gateway activates as before.
1239
1246
  */
1240
1247
  async activateBundleHeld(target, version, prefix = '/opt/omnitron', keepVersions = 3) {
1241
- const { runningGatewaySites, MaintenanceLock, DEFAULT_MAINTENANCE_TIMING } = await import('./maintenance-lock.js');
1242
- const run = this.leaseRunner(target);
1243
- const locks = [];
1244
- for (const site of await runningGatewaySites(run, `omnitron ${version}`)) {
1245
- const lock = await MaintenanceLock.open(site, this.logger, { ...DEFAULT_MAINTENANCE_TIMING, probeWithinMs: 180_000 });
1246
- if (lock)
1247
- locks.push(lock);
1248
- }
1249
- try {
1250
- const activated = await this.activateBundle(target, version, prefix, keepVersions);
1251
- while (locks.length > 0) {
1252
- const lifted = await locks.shift().liftWhenAnswering(['/', '/api/main/health']);
1253
- if (!lifted.lifted) {
1254
- this.logger.error({ host: target.host, version, failed: lifted.failed }, 'After the upgrade the platform does not answer through its gateway — maintenance expires by itself');
1248
+ assertControlledDaemonPort(target.daemonPort);
1249
+ return this.underLease(target, `held host upgrade to ${version}`, async () => {
1250
+ await this.assertNoPendingHostOperations(target, prefix);
1251
+ const { runningGatewaySites, MaintenanceLock, DEFAULT_MAINTENANCE_TIMING } = await import('./maintenance-lock.js');
1252
+ const run = (script) => this.sshExecData(target, script, 30_000);
1253
+ const locks = [];
1254
+ for (const site of await runningGatewaySites(run, `omnitron ${version}`)) {
1255
+ const lock = await MaintenanceLock.open(site, this.logger, { ...DEFAULT_MAINTENANCE_TIMING, probeWithinMs: 180_000 });
1256
+ if (lock)
1257
+ locks.push(lock);
1258
+ }
1259
+ try {
1260
+ let activated = await this.activateBundle(target, version, prefix, keepVersions, async () => {
1261
+ for (const lock of locks) {
1262
+ const failed = await lock.probe(['/', '/api/main/health']);
1263
+ if (failed.length)
1264
+ throw new Error(`Gateway verification failed: ${failed.join(', ')}`);
1265
+ }
1266
+ });
1267
+ while (locks.length > 0) {
1268
+ const lifted = await locks.shift().liftWhenAnswering(['/', '/api/main/health']);
1269
+ if (!lifted.lifted) {
1270
+ activated = false;
1271
+ this.logger.error({ host: target.host, version, failed: lifted.failed }, 'After the upgrade the platform does not answer through its gateway — maintenance expires by itself');
1272
+ }
1255
1273
  }
1274
+ return activated;
1256
1275
  }
1257
- return activated;
1258
- }
1259
- finally {
1260
- for (const lock of locks)
1261
- await lock.lift('failed').catch(() => undefined);
1262
- }
1276
+ finally {
1277
+ for (const lock of locks)
1278
+ await lock.lift('failed').catch(() => undefined);
1279
+ }
1280
+ });
1263
1281
  }
1264
- /**
1265
- * Make an installed version the one the node runs, and restart into it.
1266
- *
1267
- * Separate from `installBundle` for the reason the layout exists: this is
1268
- * the only step that changes what runs.
1269
- */
1270
- async activateBundle(target, version, prefix = '/opt/omnitron', keepVersions = 3) {
1271
- const nodeKey = `${target.host}:${target.daemonPort ?? 9700}`;
1272
- const layout = { prefix, version };
1273
- try {
1274
- for (const step of activateSteps(layout)) {
1275
- this.emitProgress(nodeKey, '*', 'restarting', 40, step.what);
1276
- await this.sshExec(target, step.command, step.timeoutMs);
1282
+ /** Check all durable operator workflows; a recovery can allow only its exact active journal. */
1283
+ async assertNoPendingHostOperations(target, prefix = '/opt/omnitron', allowance) {
1284
+ await this.sshExecData(target, hostOperationBarrierCommand(prefix, allowance));
1285
+ }
1286
+ /** Require a settled host and the actual serving immutable daemon build. */
1287
+ async inspectInstalledBundle(target, prefix = '/opt/omnitron', allowance) {
1288
+ assertControlledDaemonPort(target.daemonPort);
1289
+ await this.assertNoPendingHostOperations(target, prefix, allowance);
1290
+ const pending = JSON.parse(await this.sshExecData(target, pendingReceiptsCommand(prefix)));
1291
+ if (pending.length)
1292
+ throw new Error(`Unfinished host upgrade ${pending.map(r => r.id).join(', ')}; recover it before deploying an app`);
1293
+ const bundle = JSON.parse(await this.sshExecData(target, bundleProbeCommand(prefix)));
1294
+ const status = JSON.parse(await this.sshExecData(target, inventoryCommand(bundle, false)));
1295
+ assertServingBundle(bundle, status);
1296
+ await this.sshExecData(target, runtimeProbeCommand(bundle, status.pid));
1297
+ return bundle;
1298
+ }
1299
+ async activateBundle(target, version, prefix = '/opt/omnitron', keepVersions = 3, verifyPlatform, allowance) {
1300
+ assertControlledDaemonPort(target.daemonPort);
1301
+ if (!/^[A-Za-z0-9][A-Za-z0-9._+-]*$/.test(version) || version.includes('..'))
1302
+ throw new Error('Invalid installed bundle version');
1303
+ if (!Number.isSafeInteger(keepVersions) || keepVersions < 1)
1304
+ throw new Error('keepVersions must be a positive integer');
1305
+ return this.underLease(target, `host upgrade to ${version}`, async () => {
1306
+ const nodeKey = `${target.host}:${target.daemonPort ?? 9700}`;
1307
+ try {
1308
+ await this.assertNoPendingHostOperations(target, prefix, allowance);
1309
+ const pending = JSON.parse(await this.sshExecData(target, pendingReceiptsCommand(prefix)));
1310
+ if (pending.length) {
1311
+ if (pending.length !== 1)
1312
+ throw new Error('Multiple unfinished host upgrades; recover an explicit receipt ID first');
1313
+ const recovered = await this.recoverBundleUpgrade(target, pending[0].id, prefix, verifyPlatform, allowance);
1314
+ this.logger.warn({ receipt: pending[0].id, recovered }, 'Recovered the unfinished host upgrade; retry the requested upgrade separately');
1315
+ return false;
1316
+ }
1317
+ const previous = await this.inspectInstalledBundle(target, prefix, allowance);
1318
+ const candidate = JSON.parse(await this.sshExecData(target, bundleProbeCommand(prefix, version)));
1319
+ await this.sshExec(target, `${shellEscape(`${candidate.root}/dist/cli/omnitron.js`)} --version`, 120_000);
1320
+ const inventory = JSON.parse(await this.sshExecData(target, inventoryCommand(previous, true)));
1321
+ inventory.runtime = JSON.parse(await this.sshExecData(target, runtimeProbeCommand(previous, inventory.status.pid)));
1322
+ const plan = planHostUpgrade(previous, candidate, inventory);
1323
+ await this.sshExec(target, contractPreflightCommand(plan.apps));
1324
+ for (const app of plan.apps) {
1325
+ await this.sshExec(target, hookCommand(app, previous, 'verify'), 330_000);
1326
+ if (plan.online.includes(app.name))
1327
+ await this.sshExec(target, appHealthCommand(app, previous), 330_000);
1328
+ }
1329
+ const id = `${new Date().toISOString().replace(/[:.]/g, '-')}-${crypto.randomUUID()}`;
1330
+ const receiptPath = `${prefix}/upgrade-receipts/${id}.json`;
1331
+ const receipt = await executeHostUpgrade(plan, id, this.hostUpgradeActions(target, prefix, keepVersions, plan, verifyPlatform));
1332
+ const ok = receipt.outcome === 'upgraded';
1333
+ this.emitProgress(nodeKey, '*', ok ? 'success' : 'failed', 100, `${receipt.outcome}; recovery receipt: ${receiptPath}`);
1334
+ this.logger[ok ? 'info' : 'error']({ host: target.host, version, receipt: receiptPath, outcome: receipt.outcome }, 'Controlled host upgrade finished');
1335
+ return ok;
1277
1336
  }
1278
- this.emitProgress(nodeKey, '*', 'restarting', 60, 'Restarting the daemon into the new version');
1279
- // By absolute path into `current`, not by name. A bare `omnitron` is
1280
- // whatever the node's PATH resolves — which, until the step above ran,
1281
- // was a different installation entirely. Naming the copy we just
1282
- // activated is the difference between restarting the new version and
1283
- // restarting whatever was there.
1284
- const cli = shellEscape(`${prefix}/current/dist/cli/omnitron.js`);
1285
- await this.sshExec(target, `${cli} down 2>/dev/null; ${cli} up --no-infra`, 180_000).catch((err) => {
1286
- this.logger.warn({ host: target.host, error: err.message }, 'Restart command did not return cleanly — verifying anyway');
1287
- });
1288
- this.emitProgress(nodeKey, '*', 'verifying', 80, 'Verifying the daemon answers');
1289
- const started = await this.awaitDaemon(target, 120_000);
1290
- if (!started.ok) {
1291
- // Reported, and NOT rolled back automatically. A daemon that will not
1292
- // start is a decision for whoever is watching: the previous version
1293
- // is still on disk and one `ln -sfn` away, and guessing that a
1294
- // rollback is wanted can be as wrong as guessing it is not.
1295
- this.logger.error({ host: target.host, version, detail: started.detail }, 'The new version did not answer — the previous one is still installed');
1296
- this.emitProgress(nodeKey, '*', 'failed', 80, `${version} did not answer: ${started.detail}. Roll back with ` +
1297
- `\`ln -sfn ${prefix}/versions/<previous> ${prefix}/current\` and restart.`);
1337
+ catch (error) {
1338
+ const message = error.message;
1339
+ this.emitProgress(nodeKey, '*', 'failed', 0, message);
1340
+ this.logger.error({ host: target.host, version, error: message }, 'Host upgrade refused or preparation failed');
1298
1341
  return false;
1299
1342
  }
1300
- // Only once the new version is serving. Pruning before this could
1301
- // remove the version a rollback needs.
1302
- for (const step of pruneSteps(layout, keepVersions)) {
1303
- await this.sshExec(target, step.command, step.timeoutMs).catch((err) => {
1304
- // Retention failing is not the upgrade failing.
1305
- this.logger.warn({ host: target.host, error: err.message }, 'Version retention failed');
1306
- });
1307
- }
1308
- this.emitProgress(nodeKey, '*', 'success', 100, `Running ${version} — ${started.detail}`);
1309
- this.logger.info({ host: target.host, version, daemon: started.detail }, 'Node upgraded');
1310
- return true;
1311
- }
1312
- catch (err) {
1313
- const message = err.message;
1314
- this.emitProgress(nodeKey, '*', 'failed', 0, message);
1315
- this.logger.error({ host: target.host, version, error: message }, 'Activation failed');
1316
- return false;
1317
- }
1343
+ });
1344
+ }
1345
+ /** Recover a recorded interrupted upgrade under the same node lease; never prunes or changes app release versions. */
1346
+ async recoverBundleUpgrade(target, id, prefix = '/opt/omnitron', verifyPlatform, allowance) {
1347
+ assertControlledDaemonPort(target.daemonPort);
1348
+ return this.underLease(target, `recover host upgrade ${id}`, async () => {
1349
+ await this.assertNoPendingHostOperations(target, prefix, allowance);
1350
+ const records = JSON.parse(await this.sshExecData(target, pendingReceiptsCommand(prefix, id)));
1351
+ if (records.length !== 1 || records[0].id !== id)
1352
+ throw new Error('No unfinished host upgrade with that receipt ID');
1353
+ const receipt = records[0];
1354
+ const plan = planHostUpgrade(receipt.plan.previous, receipt.plan.candidate, receipt.plan.inventory);
1355
+ const installed = JSON.parse(await this.sshExecData(target, bundleProbeCommand(prefix, plan.previous.version)));
1356
+ if (JSON.stringify(installed) !== JSON.stringify(plan.previous))
1357
+ throw new Error('Recorded previous bundle changed; recovery refused');
1358
+ await this.sshExec(target, contractPreflightCommand(plan.apps));
1359
+ const result = await recoverHostUpgrade(receipt, this.hostUpgradeActions(target, prefix, 3, plan, verifyPlatform));
1360
+ this.logger[result.outcome === 'rolled-back' ? 'info' : 'error']({ receipt: `${prefix}/upgrade-receipts/${id}.json`, outcome: result.outcome }, 'Host upgrade recovery finished');
1361
+ return result.outcome === 'rolled-back';
1362
+ });
1363
+ }
1364
+ hostUpgradeActions(target, prefix, keepVersions, plan, verifyPlatform) {
1365
+ return {
1366
+ save: async (receipt) => { await this.sshExec(target, receiptCommand(prefix, receipt)); },
1367
+ stop: async (bundle) => {
1368
+ await this.sshExec(target, runtimeStopCommand(plan.inventory.runtime, bundle), 180_000);
1369
+ await this.sshExec(target, stoppedDaemonCommand(bundle), 30_000);
1370
+ },
1371
+ activate: async (bundle) => {
1372
+ for (const step of activateSteps({ prefix, version: bundle.version }))
1373
+ await this.sshExec(target, step.command, step.timeoutMs);
1374
+ },
1375
+ rebind: async (app, bundle) => { await this.sshExec(target, hookCommand(app, bundle, 'rebind'), 330_000); },
1376
+ start: async (bundle) => {
1377
+ await this.sshExec(target, runtimeStartCommand(plan.inventory.runtime, plan.previous, bundle, [plan.previous.root, plan.candidate.root]), 180_000);
1378
+ },
1379
+ verify: async (bundle) => {
1380
+ const deadline = Date.now() + 180_000;
1381
+ let lastError;
1382
+ do {
1383
+ try {
1384
+ const status = JSON.parse(await this.sshExecData(target, inventoryCommand(bundle, false), 30_000));
1385
+ verifyRestoredInventory(plan, bundle, status);
1386
+ const runtime = JSON.parse(await this.sshExecData(target, runtimeProbeCommand(bundle, status.pid)));
1387
+ if (runtime.kind !== plan.inventory.runtime.kind || (runtime.kind === 'systemd' && plan.inventory.runtime.kind === 'systemd' &&
1388
+ (runtime.scope !== plan.inventory.runtime.scope || runtime.enabled !== plan.inventory.runtime.enabled)))
1389
+ throw new Error('Supervisor state was not restored');
1390
+ for (const app of plan.apps) {
1391
+ await this.sshExec(target, hookCommand(app, bundle, 'verify'), 330_000);
1392
+ if (plan.online.includes(app.name))
1393
+ await this.sshExec(target, appHealthCommand(app, bundle), 330_000);
1394
+ }
1395
+ if (verifyPlatform)
1396
+ await verifyPlatform();
1397
+ await this.sshExec(target, updateContractBindingsCommand(plan, bundle));
1398
+ return;
1399
+ }
1400
+ catch (error) {
1401
+ lastError = error;
1402
+ }
1403
+ if (Date.now() < deadline)
1404
+ await new Promise(resolve => setTimeout(resolve, 2_000));
1405
+ } while (Date.now() < deadline);
1406
+ throw new Error(`Host and application verification failed: ${String(lastError)}`);
1407
+ },
1408
+ prune: async (protectedRoots) => {
1409
+ for (const step of pruneSteps({ prefix, version: plan.candidate.version }, keepVersions, protectedRoots))
1410
+ await this.sshExec(target, step.command, step.timeoutMs);
1411
+ },
1412
+ };
1318
1413
  }
1319
1414
  async verifySSH(target) {
1320
1415
  await this.sshExec(target, 'echo ok', 10_000);
@@ -1338,7 +1433,14 @@ export class RemoteDeployer {
1338
1433
  // Through the data channel: a lease answer carries the holder's record,
1339
1434
  // `{"token":…}`, which the masker rewrote into `"token": [REDACTED]` on
1340
1435
  // every read — a refusal could never say who held the node.
1341
- return (script) => this.sshExecData(target, script, 30_000);
1436
+ return async (script) => {
1437
+ // Confirmation itself must use the raw channel, never recursively ask
1438
+ // for another confirmation of the lease being acquired or renewed.
1439
+ const result = await this.readFromNodeRaw(target, script, 30_000);
1440
+ if (result.code !== 0)
1441
+ throw new Error(`Lease transport failed: ${result.stderr || result.stdout}`);
1442
+ return result.stdout.trim();
1443
+ };
1342
1444
  }
1343
1445
  /**
1344
1446
  * Run `work` holding the machine's deploy lease — for what changes a node
@@ -1348,15 +1450,32 @@ export class RemoteDeployer {
1348
1450
  */
1349
1451
  async underLease(target, purpose, work) {
1350
1452
  const machine = `${target.host}:${target.sshPort ?? 22}`;
1453
+ const held = this.deploymentLeases.getStore();
1454
+ if (held?.has(machine))
1455
+ return work();
1351
1456
  return withNodeLeases([{ node: machine, machine, run: this.leaseRunner(target) }], purpose, this.logger, async (leases) => {
1352
1457
  if (!leases.has(machine)) {
1353
1458
  throw new Error(`Could not reach ${machine} to lease it for ${purpose}: ${leases.unreachable.get(machine) ?? 'no answer'}`);
1354
1459
  }
1355
- return work();
1460
+ const context = new Map(held);
1461
+ context.set(machine, { confirm: step => leases.confirm(machine, step), guard: (command, timeoutMs) => leases.guard(machine, command, timeoutMs) });
1462
+ return this.deploymentLeases.run(context, work);
1356
1463
  });
1357
1464
  }
1465
+ /** Confirm a held lease before the next node operation; outside a lease this is a no-op. */
1466
+ async confirmLease(target, step) {
1467
+ const machine = `${target.host}:${target.sshPort ?? 22}`;
1468
+ await this.deploymentLeases.getStore()?.get(machine)?.confirm(step);
1469
+ }
1470
+ /** The node holds its execution fence for the command lifetime, beyond a lost controller. */
1471
+ guardedCommand(target, command, timeout) {
1472
+ const lease = this.deploymentLeases.getStore()?.get(`${target.host}:${target.sshPort ?? 22}`);
1473
+ return lease ? { command: lease.guard(command, timeout), timeout: timeout + 45_000 } : { command, timeout };
1474
+ }
1358
1475
  async sshExec(target, command, timeout = 60_000) {
1359
- const result = await this.execution.ssh(sshTargetOf(target), command, { timeout });
1476
+ await this.confirmLease(target, 'remote command');
1477
+ const guarded = this.guardedCommand(target, command, timeout);
1478
+ const result = await this.execution.ssh(sshTargetOf(target), guarded.command, { timeout: guarded.timeout });
1360
1479
  if (result.exitCode !== 0) {
1361
1480
  const detail = result.stderr || result.stdout || `exit ${result.exitCode}`;
1362
1481
  throw new Error(`ssh ${target.username ?? 'root'}@${target.host}: ${detail}`);
@@ -1381,7 +1500,9 @@ export class RemoteDeployer {
1381
1500
  * keep), and a throw would flatten the two into one.
1382
1501
  */
1383
1502
  async runOnNode(target, command, timeoutMs = 600_000) {
1384
- const result = await this.execution.ssh(sshTargetOf(target), command, { timeout: timeoutMs });
1503
+ await this.confirmLease(target, 'remote command');
1504
+ const guarded = this.guardedCommand(target, command, timeoutMs);
1505
+ const result = await this.execution.ssh(sshTargetOf(target), guarded.command, { timeout: guarded.timeout });
1385
1506
  return { stdout: result.stdout, stderr: result.stderr, code: result.exitCode ?? -1 };
1386
1507
  }
1387
1508
  /**
@@ -1396,6 +1517,11 @@ export class RemoteDeployer {
1396
1517
  * failure with its own code, -2, and its words on stderr — never data.
1397
1518
  */
1398
1519
  async readFromNode(target, command, timeoutMs = 600_000) {
1520
+ await this.confirmLease(target, 'remote data command');
1521
+ const guarded = this.guardedCommand(target, command, timeoutMs);
1522
+ return this.readFromNodeRaw(target, guarded.command, guarded.timeout);
1523
+ }
1524
+ async readFromNodeRaw(target, command, timeoutMs) {
1399
1525
  const result = await this.execution.ssh(sshTargetOf(target), throughDataChannel(command), { timeout: timeoutMs });
1400
1526
  const data = fromDataChannel(result.stdout);
1401
1527
  if (!data.ok) {
@@ -1403,8 +1529,23 @@ export class RemoteDeployer {
1403
1529
  }
1404
1530
  return { stdout: data.text, stderr: result.stderr, code: result.exitCode ?? -1 };
1405
1531
  }
1532
+ /** Use this inside underLease so uploads share the command confirmation boundary. */
1533
+ async uploadToNode(target, localPath, remotePath) {
1534
+ await this.confirmLease(target, 'file upload');
1535
+ const lease = this.deploymentLeases.getStore()?.get(`${target.host}:${target.sshPort ?? 22}`);
1536
+ if (!lease) {
1537
+ await this.execution.uploadFile(sshTargetOf(target), localPath, remotePath);
1538
+ return;
1539
+ }
1540
+ await this.execution.uploadFile(sshTargetOf(target), localPath, remotePath, {
1541
+ runControl: async (command, timeout) => {
1542
+ const result = await this.runOnNode(target, command, timeout);
1543
+ return { stdout: result.stdout, stderr: result.stderr, exitCode: result.code };
1544
+ },
1545
+ });
1546
+ }
1406
1547
  async scpTransfer(target, localPath, remotePath) {
1407
- await this.execution.uploadFile(sshTargetOf(target), localPath, remotePath);
1548
+ await this.uploadToNode(target, localPath, remotePath);
1408
1549
  }
1409
1550
  /**
1410
1551
  * Ask the node's daemon to run the app whose artifact just landed.
@@ -1499,7 +1640,7 @@ export class RemoteDeployer {
1499
1640
  // this pipeline's status is gzip's alone, so a `tar` that stopped
1500
1641
  // halfway left a short archive that was then summed, sent, checked
1501
1642
  // against its own short sum — and failed only on the node: «gzip:
1502
- // stdin: unexpected end of file» (daos-202609221949-23dce2f6 to test,
1643
+ // stdin: unexpected end of file» (sample-202609221949-23dce2f6 to test,
1503
1644
  // 2026-09-22). The sum proves the file ARRIVED as it left; nothing
1504
1645
  // proved it left whole.
1505
1646
  const content = await fsp.readFile(archive).catch(() => null);
@@ -1560,7 +1701,7 @@ export class RemoteDeployer {
1560
1701
  if (!alreadyThere) {
1561
1702
  await this.sshExec(target, `rm -rf ${shellEscape(remoteDir)} ${shellEscape(marker)}`).catch(() => undefined);
1562
1703
  await this.sshExec(target, `mkdir -p ${shellEscape(remoteDir)}`);
1563
- await this.execution.uploadFile(sshTargetOf(target), archive, remoteFile);
1704
+ await this.uploadToNode(target, archive, remoteFile);
1564
1705
  // `uploadFile` already refuses a file that landed SHORT. That catches
1565
1706
  // the likely failure and not the other one: a transfer that completes
1566
1707
  // and corrupts in the middle has the right length and the wrong
@@ -1625,14 +1766,41 @@ export class RemoteDeployer {
1625
1766
  await this.sshExec(target, `ln -s ${shellEscape(digest)} ${shellEscape(tmp)} && mv -T ${shellEscape(tmp)} ${shellEscape(`${root}/${link}`)}`);
1626
1767
  this.logger.info({ host: target.host, link, digest }, 'The gateway now serves this build through its link');
1627
1768
  }
1628
- /**
1629
- * Hard-link the assets of the build the running gateways serve into the
1630
- * one just unpacked, and remove builds nobody serves
1631
- * (`static-carry-forward.ts`). Never fails the delivery: without it an open
1632
- * tab reloads onto the new build instead of continuing, which the portal
1633
- * handles; with a failed delivery nobody gets a portal at all.
1634
- */
1635
- async carryPreviousBuildForward(target, root, fresh) {
1769
+ async carryPreviousBuildForward(target, input, fresh) {
1770
+ if (typeof input !== 'string') {
1771
+ const { immutableAssetsRoot, immutableAssetsScript } = await import('./static-carry-forward.js');
1772
+ const expectedRoot = immutableAssetsRoot(input.owner);
1773
+ // The native execution fence serializes every publisher, including reentrant calls.
1774
+ return this.underLease(target, 'retain immutable public assets', async () => {
1775
+ await this.assertNoPendingHostOperations(target);
1776
+ const fs = await import('node:fs/promises');
1777
+ const { tmpdir } = await import('node:os');
1778
+ const { join } = await import('node:path');
1779
+ const { randomUUID } = await import('node:crypto');
1780
+ const temporary = await fs.mkdtemp(join(tmpdir(), 'omnitron-static-assets-'));
1781
+ const local = join(temporary, 'request.json');
1782
+ const remote = '/tmp/omnitron-static-assets-' + randomUUID() + '.json';
1783
+ try {
1784
+ await fs.writeFile(local, JSON.stringify(input), { mode: 0o600, flag: 'wx' });
1785
+ await this.uploadToNode(target, local, remote);
1786
+ const output = await this.sshExec(target, immutableAssetsScript(remote), 300_000);
1787
+ const receipt = JSON.parse(output);
1788
+ if (receipt.assetsRoot !== expectedRoot || !/^[a-f0-9]{64}$/.test(receipt.manifestSha256) ||
1789
+ ![receipt.files, receipt.added, receipt.reused, receipt.bytes].every((n) => Number.isSafeInteger(n) && n >= 0)) {
1790
+ throw new Error('Invalid immutable asset publication receipt');
1791
+ }
1792
+ this.logger.info({ host: target.host, owner: input.owner, ...receipt }, 'Immutable public asset bytes retained');
1793
+ return receipt;
1794
+ }
1795
+ finally {
1796
+ await fs.rm(temporary, { recursive: true, force: true });
1797
+ await this.sshExec(target, 'rm -f -- ' + shellEscape(remote)).catch(() => undefined);
1798
+ }
1799
+ });
1800
+ }
1801
+ const root = input;
1802
+ if (!fresh)
1803
+ throw new Error('Missing legacy static build');
1636
1804
  try {
1637
1805
  const { GATEWAY_MOUNTS_COMMAND, readGatewayMounts, carryForwardScript } = await import('./static-carry-forward.js');
1638
1806
  const mounts = readGatewayMounts(await this.sshExec(target, GATEWAY_MOUNTS_COMMAND).catch(() => ''));