@mysten-incubation/devstack 0.1.0 → 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 (858) hide show
  1. package/README.md +7 -4
  2. package/dashboard-ui/assets/grpc-a4usE3Nk.js +3 -0
  3. package/dashboard-ui/assets/index-B82Bh84P.css +1 -0
  4. package/dashboard-ui/assets/index-CRYJ4pru.js +1277 -0
  5. package/dashboard-ui/index.html +2 -2
  6. package/dist/api/define-capabilities.d.mts +1 -20
  7. package/dist/api/define-capabilities.mjs +15 -22
  8. package/dist/api/define-capabilities.mjs.map +1 -1
  9. package/dist/api/define-devstack-with.d.mts +7 -1
  10. package/dist/api/define-devstack-with.mjs.map +1 -1
  11. package/dist/api/define-devstack.mjs.map +1 -1
  12. package/dist/api/define-plugin.d.mts +2 -1
  13. package/dist/api/inference-network.d.mts +31 -17
  14. package/dist/api/inference-network.mjs +40 -43
  15. package/dist/api/inference-network.mjs.map +1 -1
  16. package/dist/api/load-config.mjs +88 -0
  17. package/dist/api/load-config.mjs.map +1 -0
  18. package/dist/api/mode-narrowed-factory.d.mts +1 -1
  19. package/dist/api/mode-narrowed-factory.mjs.map +1 -1
  20. package/dist/api/run-stack-internal.mjs +202 -0
  21. package/dist/api/run-stack-internal.mjs.map +1 -0
  22. package/dist/api/run-stack.d.mts +82 -31
  23. package/dist/api/run-stack.mjs +22 -141
  24. package/dist/api/run-stack.mjs.map +1 -1
  25. package/dist/build-integrations/dapp-kit/index.d.mts +61 -0
  26. package/dist/build-integrations/dapp-kit/index.mjs +58 -0
  27. package/dist/build-integrations/dapp-kit/index.mjs.map +1 -0
  28. package/dist/build-integrations/playwright/config.d.mts +1 -30
  29. package/dist/build-integrations/playwright/config.mjs +1 -27
  30. package/dist/build-integrations/playwright/config.mjs.map +1 -1
  31. package/dist/build-integrations/playwright/errors.d.mts +4 -7
  32. package/dist/build-integrations/playwright/errors.mjs.map +1 -1
  33. package/dist/build-integrations/playwright/global-setup.d.mts +42 -71
  34. package/dist/build-integrations/playwright/global-setup.mjs +142 -218
  35. package/dist/build-integrations/playwright/global-setup.mjs.map +1 -1
  36. package/dist/build-integrations/playwright/index.d.mts +2 -2
  37. package/dist/build-integrations/playwright/index.mjs +2 -2
  38. package/dist/build-integrations/playwright/stack-context.d.mts +3 -1
  39. package/dist/build-integrations/playwright/stack-context.mjs +4 -2
  40. package/dist/build-integrations/playwright/stack-context.mjs.map +1 -1
  41. package/dist/build-integrations/playwright/wallet-context.mjs +44 -19
  42. package/dist/build-integrations/playwright/wallet-context.mjs.map +1 -1
  43. package/dist/build-integrations/runtime/cold-start-url.mjs.map +1 -1
  44. package/dist/build-integrations/runtime/conventional-routes.mjs.map +1 -1
  45. package/dist/build-integrations/runtime/dapp-kit-slot.mjs.map +1 -1
  46. package/dist/build-integrations/runtime/discover.d.mts +6 -4
  47. package/dist/build-integrations/runtime/discover.mjs +15 -4
  48. package/dist/build-integrations/runtime/discover.mjs.map +1 -1
  49. package/dist/build-integrations/runtime/endpoint-registry.mjs.map +1 -1
  50. package/dist/build-integrations/runtime/errors.mjs.map +1 -1
  51. package/dist/build-integrations/runtime/read-stack-context.mjs +3 -3
  52. package/dist/build-integrations/runtime/read-stack-context.mjs.map +1 -1
  53. package/dist/build-integrations/runtime/resolve-discovery-env.d.mts +23 -7
  54. package/dist/build-integrations/runtime/resolve-discovery-env.mjs +25 -12
  55. package/dist/build-integrations/runtime/resolve-discovery-env.mjs.map +1 -1
  56. package/dist/build-integrations/runtime/stack-context.d.mts +2 -2
  57. package/dist/build-integrations/vite/index.d.mts +84 -19
  58. package/dist/build-integrations/vite/index.mjs +203 -28
  59. package/dist/build-integrations/vite/index.mjs.map +1 -1
  60. package/dist/build-integrations/vitest/config.d.mts +20 -2
  61. package/dist/build-integrations/vitest/config.mjs +16 -4
  62. package/dist/build-integrations/vitest/config.mjs.map +1 -1
  63. package/dist/build-integrations/vitest/env.d.mts +18 -10
  64. package/dist/build-integrations/vitest/env.mjs +11 -10
  65. package/dist/build-integrations/vitest/env.mjs.map +1 -1
  66. package/dist/build-integrations/vitest/errors.d.mts +7 -11
  67. package/dist/build-integrations/vitest/errors.mjs +3 -4
  68. package/dist/build-integrations/vitest/errors.mjs.map +1 -1
  69. package/dist/build-integrations/vitest/global-setup.d.mts +35 -0
  70. package/dist/build-integrations/vitest/global-setup.mjs +156 -0
  71. package/dist/build-integrations/vitest/global-setup.mjs.map +1 -0
  72. package/dist/build-integrations/vitest/index.d.mts +3 -2
  73. package/dist/build-integrations/vitest/index.mjs +3 -2
  74. package/dist/build-integrations/vitest/setup.mjs +2 -2
  75. package/dist/build-integrations/vitest/setup.mjs.map +1 -1
  76. package/dist/build-integrations/vitest/stack-context.d.mts +5 -3
  77. package/dist/build-integrations/vitest/stack-context.mjs +3 -2
  78. package/dist/build-integrations/vitest/stack-context.mjs.map +1 -1
  79. package/dist/cli/bail.mjs.map +1 -1
  80. package/dist/cli/doctor-probes.mjs +1 -3
  81. package/dist/cli/doctor-probes.mjs.map +1 -1
  82. package/dist/cli/main.d.mts +23 -1
  83. package/dist/cli/main.mjs +63 -8
  84. package/dist/cli/main.mjs.map +1 -1
  85. package/dist/cli/prune-direct.mjs.map +1 -1
  86. package/dist/cli/snapshot-reader.mjs.map +1 -1
  87. package/dist/cli/up-lifecycle.mjs.map +1 -1
  88. package/dist/cli/wirings/apply.mjs +28 -21
  89. package/dist/cli/wirings/apply.mjs.map +1 -1
  90. package/dist/cli/wirings/codegen.mjs +108 -0
  91. package/dist/cli/wirings/codegen.mjs.map +1 -0
  92. package/dist/cli/wirings/config-loader.mjs +12 -62
  93. package/dist/cli/wirings/config-loader.mjs.map +1 -1
  94. package/dist/cli/wirings/dump-ids.mjs +86 -0
  95. package/dist/cli/wirings/dump-ids.mjs.map +1 -0
  96. package/dist/cli/wirings/engine-command.mjs +1 -1
  97. package/dist/cli/wirings/engine-command.mjs.map +1 -1
  98. package/dist/cli/wirings/identity.mjs +2 -2
  99. package/dist/cli/wirings/identity.mjs.map +1 -1
  100. package/dist/cli/wirings/provide-file-system.mjs.map +1 -1
  101. package/dist/cli/wirings/read-devstack-version.mjs +17 -0
  102. package/dist/cli/wirings/read-devstack-version.mjs.map +1 -0
  103. package/dist/cli/wirings/snapshot.mjs +27 -34
  104. package/dist/cli/wirings/snapshot.mjs.map +1 -1
  105. package/dist/cli/wirings/up-ipc.mjs +147 -0
  106. package/dist/cli/wirings/up-ipc.mjs.map +1 -0
  107. package/dist/cli/wirings/up.mjs +218 -198
  108. package/dist/cli/wirings/up.mjs.map +1 -1
  109. package/dist/cli/wirings/wipe.mjs +1 -1
  110. package/dist/cli/wirings/wipe.mjs.map +1 -1
  111. package/dist/contracts/chain-probe.d.mts +1 -1
  112. package/dist/contracts/chain-probe.mjs +1 -1
  113. package/dist/contracts/chain-probe.mjs.map +1 -1
  114. package/dist/contracts/codegenable.d.mts +60 -6
  115. package/dist/contracts/codegenable.mjs +28 -0
  116. package/dist/contracts/codegenable.mjs.map +1 -0
  117. package/dist/contracts/config-bindings.mjs +162 -0
  118. package/dist/contracts/config-bindings.mjs.map +1 -0
  119. package/dist/contracts/container-runtime.d.mts +23 -17
  120. package/dist/contracts/plugin-expander.mjs.map +1 -1
  121. package/dist/contracts/snapshotable.d.mts +5 -7
  122. package/dist/contracts/wallet-protocol.d.mts +2 -5
  123. package/dist/contracts/wallet-protocol.mjs +2 -5
  124. package/dist/contracts/wallet-protocol.mjs.map +1 -1
  125. package/dist/index.d.mts +21 -26
  126. package/dist/index.mjs +8 -9
  127. package/dist/orchestrators/boot.d.mts +28 -0
  128. package/dist/orchestrators/boot.mjs +440 -0
  129. package/dist/orchestrators/boot.mjs.map +1 -0
  130. package/dist/orchestrators/codegen/bindings.d.mts +13 -1
  131. package/dist/orchestrators/codegen/bindings.mjs +23 -9
  132. package/dist/orchestrators/codegen/bindings.mjs.map +1 -1
  133. package/dist/orchestrators/codegen/config-runtime.mjs +208 -0
  134. package/dist/orchestrators/codegen/config-runtime.mjs.map +1 -0
  135. package/dist/orchestrators/codegen/emit.mjs +1 -1
  136. package/dist/orchestrators/codegen/emit.mjs.map +1 -1
  137. package/dist/orchestrators/codegen/errors.mjs +21 -1
  138. package/dist/orchestrators/codegen/errors.mjs.map +1 -1
  139. package/dist/orchestrators/codegen/format.mjs +2 -0
  140. package/dist/orchestrators/codegen/format.mjs.map +1 -1
  141. package/dist/orchestrators/codegen/gitignore.mjs +30 -11
  142. package/dist/orchestrators/codegen/gitignore.mjs.map +1 -1
  143. package/dist/orchestrators/codegen/id-config.mjs +107 -0
  144. package/dist/orchestrators/codegen/id-config.mjs.map +1 -0
  145. package/dist/orchestrators/codegen/output-location.mjs +23 -31
  146. package/dist/orchestrators/codegen/output-location.mjs.map +1 -1
  147. package/dist/orchestrators/codegen/paths.mjs +8 -1
  148. package/dist/orchestrators/codegen/paths.mjs.map +1 -1
  149. package/dist/orchestrators/codegen/permissions.mjs.map +1 -1
  150. package/dist/orchestrators/codegen/service.mjs +286 -58
  151. package/dist/orchestrators/codegen/service.mjs.map +1 -1
  152. package/dist/orchestrators/layers.mjs +31 -0
  153. package/dist/orchestrators/layers.mjs.map +1 -0
  154. package/dist/orchestrators/lifecycle-prune/errors.mjs.map +1 -1
  155. package/dist/orchestrators/lifecycle-prune/index.mjs +8 -10
  156. package/dist/orchestrators/lifecycle-prune/index.mjs.map +1 -1
  157. package/dist/orchestrators/network-options.d.mts +52 -0
  158. package/dist/orchestrators/network-options.mjs +43 -0
  159. package/dist/orchestrators/network-options.mjs.map +1 -0
  160. package/dist/orchestrators/router/cleanup.mjs.map +1 -1
  161. package/dist/orchestrators/router/cors.mjs.map +1 -1
  162. package/dist/orchestrators/router/entrypoints.mjs.map +1 -1
  163. package/dist/orchestrators/router/errors.mjs.map +1 -1
  164. package/dist/orchestrators/router/file-provider.mjs.map +1 -1
  165. package/dist/orchestrators/router/hostname.mjs.map +1 -1
  166. package/dist/orchestrators/router/profile.mjs +1 -1
  167. package/dist/orchestrators/router/profile.mjs.map +1 -1
  168. package/dist/orchestrators/router/service.d.mts +2 -2
  169. package/dist/orchestrators/router/service.mjs +12 -27
  170. package/dist/orchestrators/router/service.mjs.map +1 -1
  171. package/dist/orchestrators/router/traefik-container.mjs +1 -1
  172. package/dist/orchestrators/router/traefik-container.mjs.map +1 -1
  173. package/dist/orchestrators/snapshot/capture.mjs +171 -195
  174. package/dist/orchestrators/snapshot/capture.mjs.map +1 -1
  175. package/dist/orchestrators/snapshot/descriptor.d.mts +1 -1
  176. package/dist/orchestrators/snapshot/descriptor.mjs +53 -11
  177. package/dist/orchestrators/snapshot/descriptor.mjs.map +1 -1
  178. package/dist/orchestrators/snapshot/identity-guard.mjs +3 -5
  179. package/dist/orchestrators/snapshot/identity-guard.mjs.map +1 -1
  180. package/dist/orchestrators/snapshot/image-bundle-tags.mjs +32 -101
  181. package/dist/orchestrators/snapshot/image-bundle-tags.mjs.map +1 -1
  182. package/dist/orchestrators/snapshot/index.mjs +1 -4
  183. package/dist/orchestrators/snapshot/integrity.mjs.map +1 -1
  184. package/dist/orchestrators/snapshot/interrupted-restore.mjs +116 -0
  185. package/dist/orchestrators/snapshot/interrupted-restore.mjs.map +1 -0
  186. package/dist/orchestrators/snapshot/phase-error.mjs.map +1 -1
  187. package/dist/orchestrators/snapshot/prune.mjs +40 -31
  188. package/dist/orchestrators/snapshot/prune.mjs.map +1 -1
  189. package/dist/orchestrators/snapshot/restore.mjs +221 -196
  190. package/dist/orchestrators/snapshot/restore.mjs.map +1 -1
  191. package/dist/orchestrators/snapshot/service.mjs +72 -62
  192. package/dist/orchestrators/snapshot/service.mjs.map +1 -1
  193. package/dist/orchestrators/snapshot/wipe.mjs +67 -56
  194. package/dist/orchestrators/snapshot/wipe.mjs.map +1 -1
  195. package/dist/plugins/account/codegen.mjs +3 -0
  196. package/dist/plugins/account/codegen.mjs.map +1 -1
  197. package/dist/plugins/account/errors.d.mts +2 -2
  198. package/dist/plugins/account/errors.mjs +1 -4
  199. package/dist/plugins/account/errors.mjs.map +1 -1
  200. package/dist/plugins/account/funding.mjs +3 -27
  201. package/dist/plugins/account/funding.mjs.map +1 -1
  202. package/dist/plugins/account/index.d.mts +3 -8
  203. package/dist/plugins/account/index.mjs +71 -35
  204. package/dist/plugins/account/index.mjs.map +1 -1
  205. package/dist/plugins/account/keypair.d.mts +8 -7
  206. package/dist/plugins/account/keypair.mjs +5 -18
  207. package/dist/plugins/account/keypair.mjs.map +1 -1
  208. package/dist/plugins/account/lease.mjs.map +1 -1
  209. package/dist/plugins/account/registry.d.mts +1 -46
  210. package/dist/plugins/account/registry.mjs.map +1 -1
  211. package/dist/plugins/account/service.d.mts +43 -34
  212. package/dist/plugins/account/service.mjs +9 -54
  213. package/dist/plugins/account/service.mjs.map +1 -1
  214. package/dist/plugins/account/snapshot.mjs.map +1 -1
  215. package/dist/plugins/account/variants/ephemeral.mjs.map +1 -1
  216. package/dist/plugins/account/variants/impersonate.mjs.map +1 -1
  217. package/dist/plugins/account/variants/signer.mjs.map +1 -1
  218. package/dist/plugins/action/discriminator.mjs.map +1 -1
  219. package/dist/plugins/action/errors.mjs +1 -4
  220. package/dist/plugins/action/errors.mjs.map +1 -1
  221. package/dist/plugins/action/execute.mjs +3 -4
  222. package/dist/plugins/action/execute.mjs.map +1 -1
  223. package/dist/plugins/action/index.d.mts +2 -2
  224. package/dist/plugins/action/index.mjs +7 -11
  225. package/dist/plugins/action/index.mjs.map +1 -1
  226. package/dist/plugins/action/service.d.mts +5 -7
  227. package/dist/plugins/action/service.mjs +2 -16
  228. package/dist/plugins/action/service.mjs.map +1 -1
  229. package/dist/plugins/coin/address-resolution.mjs.map +1 -1
  230. package/dist/plugins/coin/codegen.mjs +97 -16
  231. package/dist/plugins/coin/codegen.mjs.map +1 -1
  232. package/dist/plugins/coin/discovery.mjs.map +1 -1
  233. package/dist/plugins/coin/errors.mjs +1 -4
  234. package/dist/plugins/coin/errors.mjs.map +1 -1
  235. package/dist/plugins/coin/index.d.mts +4 -34
  236. package/dist/plugins/coin/index.mjs +47 -28
  237. package/dist/plugins/coin/index.mjs.map +1 -1
  238. package/dist/plugins/coin/metadata.d.mts +1 -0
  239. package/dist/plugins/coin/metadata.mjs +8 -9
  240. package/dist/plugins/coin/metadata.mjs.map +1 -1
  241. package/dist/plugins/coin/mint.mjs +7 -21
  242. package/dist/plugins/coin/mint.mjs.map +1 -1
  243. package/dist/plugins/coin/registry.mjs +33 -20
  244. package/dist/plugins/coin/registry.mjs.map +1 -1
  245. package/dist/plugins/coin/service.d.mts +1 -1
  246. package/dist/plugins/coin/service.mjs +1 -8
  247. package/dist/plugins/coin/service.mjs.map +1 -1
  248. package/dist/plugins/coin/snapshot.mjs.map +1 -1
  249. package/dist/plugins/coin/type-strings.mjs.map +1 -1
  250. package/dist/plugins/dashboard/domain.mjs +106 -167
  251. package/dist/plugins/dashboard/domain.mjs.map +1 -1
  252. package/dist/plugins/dashboard/index.mjs +13 -13
  253. package/dist/plugins/dashboard/index.mjs.map +1 -1
  254. package/dist/plugins/dashboard/origin-policy.mjs +3 -3
  255. package/dist/plugins/dashboard/origin-policy.mjs.map +1 -1
  256. package/dist/plugins/dashboard/routable.mjs.map +1 -1
  257. package/dist/plugins/dashboard/schema/builder.mjs.map +1 -1
  258. package/dist/plugins/dashboard/schema/enums.mjs.map +1 -1
  259. package/dist/plugins/dashboard/schema/root.mjs +23 -44
  260. package/dist/plugins/dashboard/schema/root.mjs.map +1 -1
  261. package/dist/plugins/dashboard/schema/types.mjs +37 -91
  262. package/dist/plugins/dashboard/schema/types.mjs.map +1 -1
  263. package/dist/plugins/dashboard/schema.mjs.map +1 -1
  264. package/dist/plugins/dashboard/server.mjs.map +1 -1
  265. package/dist/plugins/deepbook/codegen.d.mts +12 -11
  266. package/dist/plugins/deepbook/codegen.mjs +108 -11
  267. package/dist/plugins/deepbook/codegen.mjs.map +1 -1
  268. package/dist/plugins/deepbook/deploy.mjs +7 -14
  269. package/dist/plugins/deepbook/deploy.mjs.map +1 -1
  270. package/dist/plugins/deepbook/errors.mjs +4 -2
  271. package/dist/plugins/deepbook/errors.mjs.map +1 -1
  272. package/dist/plugins/deepbook/faucet-strategy.mjs +4 -10
  273. package/dist/plugins/deepbook/faucet-strategy.mjs.map +1 -1
  274. package/dist/plugins/deepbook/hash.mjs.map +1 -1
  275. package/dist/plugins/deepbook/index.d.mts +38 -34
  276. package/dist/plugins/deepbook/index.mjs +133 -64
  277. package/dist/plugins/deepbook/index.mjs.map +1 -1
  278. package/dist/plugins/deepbook/plugin-key.mjs.map +1 -1
  279. package/dist/plugins/deepbook/pyth/index.mjs +4 -8
  280. package/dist/plugins/deepbook/pyth/index.mjs.map +1 -1
  281. package/dist/plugins/deepbook/snapshot.mjs +0 -1
  282. package/dist/plugins/deepbook/snapshot.mjs.map +1 -1
  283. package/dist/plugins/deepbook/type-strings.mjs.map +1 -1
  284. package/dist/plugins/deepbook/types.d.mts +2 -1
  285. package/dist/plugins/deepbook/types.mjs.map +1 -1
  286. package/dist/plugins/faucet/dispatcher.d.mts +3 -3
  287. package/dist/plugins/faucet/dispatcher.mjs.map +1 -1
  288. package/dist/plugins/faucet/errors.mjs.map +1 -1
  289. package/dist/plugins/faucet/http.mjs +2 -10
  290. package/dist/plugins/faucet/http.mjs.map +1 -1
  291. package/dist/plugins/faucet/index.mjs +0 -1
  292. package/dist/plugins/faucet/index.mjs.map +1 -1
  293. package/dist/plugins/host-service/errors.d.mts +1 -3
  294. package/dist/plugins/host-service/errors.mjs +1 -2
  295. package/dist/plugins/host-service/errors.mjs.map +1 -1
  296. package/dist/plugins/host-service/index.d.mts +1 -2
  297. package/dist/plugins/host-service/index.mjs +11 -11
  298. package/dist/plugins/host-service/index.mjs.map +1 -1
  299. package/dist/plugins/host-service/routable.mjs.map +1 -1
  300. package/dist/plugins/host-service/service.mjs +13 -13
  301. package/dist/plugins/host-service/service.mjs.map +1 -1
  302. package/dist/plugins/internal/acquire-on-chain-artifact.mjs.map +1 -1
  303. package/dist/plugins/internal/codegen-helpers.mjs +2 -0
  304. package/dist/plugins/internal/codegen-helpers.mjs.map +1 -1
  305. package/dist/plugins/internal/funding-failure-error.mjs.map +1 -1
  306. package/dist/plugins/{postgres → internal/postgres-sidecar}/connection.mjs +1 -1
  307. package/dist/plugins/internal/postgres-sidecar/connection.mjs.map +1 -0
  308. package/dist/plugins/{postgres → internal/postgres-sidecar}/db-ensure.mjs +30 -16
  309. package/dist/plugins/internal/postgres-sidecar/db-ensure.mjs.map +1 -0
  310. package/dist/plugins/internal/postgres-sidecar/errors.mjs +21 -0
  311. package/dist/plugins/internal/postgres-sidecar/errors.mjs.map +1 -0
  312. package/dist/{substrate/runtime/scoped-multimap → plugins/internal/postgres-sidecar}/index.mjs +1 -0
  313. package/dist/plugins/internal/postgres-sidecar/service.mjs +121 -0
  314. package/dist/plugins/internal/postgres-sidecar/service.mjs.map +1 -0
  315. package/dist/plugins/package/build.mjs +2 -2
  316. package/dist/plugins/package/build.mjs.map +1 -1
  317. package/dist/plugins/package/codegen.d.mts +7 -14
  318. package/dist/plugins/package/codegen.mjs +187 -59
  319. package/dist/plugins/package/codegen.mjs.map +1 -1
  320. package/dist/plugins/package/dep-resolution.mjs +51 -3
  321. package/dist/plugins/package/dep-resolution.mjs.map +1 -1
  322. package/dist/plugins/package/errors.mjs +1 -4
  323. package/dist/plugins/package/errors.mjs.map +1 -1
  324. package/dist/plugins/package/git-source.d.mts +18 -0
  325. package/dist/plugins/package/git-source.mjs +119 -0
  326. package/dist/plugins/package/git-source.mjs.map +1 -0
  327. package/dist/plugins/package/index.d.mts +28 -19
  328. package/dist/plugins/package/index.mjs +107 -32
  329. package/dist/plugins/package/index.mjs.map +1 -1
  330. package/dist/plugins/package/mode-known.mjs +2 -2
  331. package/dist/plugins/package/mode-known.mjs.map +1 -1
  332. package/dist/plugins/package/mode-local.mjs +18 -35
  333. package/dist/plugins/package/mode-local.mjs.map +1 -1
  334. package/dist/plugins/package/publish-executor.mjs +13 -8
  335. package/dist/plugins/package/publish-executor.mjs.map +1 -1
  336. package/dist/plugins/package/publish-output.d.mts +1 -11
  337. package/dist/plugins/package/publish-output.mjs +1 -9
  338. package/dist/plugins/package/publish-output.mjs.map +1 -1
  339. package/dist/plugins/package/registry.d.mts +1 -1
  340. package/dist/plugins/package/registry.mjs +26 -14
  341. package/dist/plugins/package/registry.mjs.map +1 -1
  342. package/dist/plugins/package/service.mjs.map +1 -1
  343. package/dist/plugins/package/snapshot.mjs.map +1 -1
  344. package/dist/plugins/router-entrypoints.mjs +1 -3
  345. package/dist/plugins/router-entrypoints.mjs.map +1 -1
  346. package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs +1 -2
  347. package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs.map +1 -1
  348. package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs +1 -6
  349. package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs.map +1 -1
  350. package/dist/plugins/seal/codegen.d.mts +1 -5
  351. package/dist/plugins/seal/codegen.mjs +83 -12
  352. package/dist/plugins/seal/codegen.mjs.map +1 -1
  353. package/dist/plugins/seal/config-render.mjs +2 -6
  354. package/dist/plugins/seal/config-render.mjs.map +1 -1
  355. package/dist/plugins/seal/deploy.mjs +18 -29
  356. package/dist/plugins/seal/deploy.mjs.map +1 -1
  357. package/dist/plugins/seal/errors.d.mts +4 -5
  358. package/dist/plugins/seal/errors.mjs +1 -8
  359. package/dist/plugins/seal/errors.mjs.map +1 -1
  360. package/dist/plugins/seal/index.d.mts +7 -28
  361. package/dist/plugins/seal/index.mjs +69 -47
  362. package/dist/plugins/seal/index.mjs.map +1 -1
  363. package/dist/plugins/seal/key-manager.mjs.map +1 -1
  364. package/dist/plugins/seal/key-server.mjs +1 -6
  365. package/dist/plugins/seal/key-server.mjs.map +1 -1
  366. package/dist/plugins/seal/keygen.mjs +2 -3
  367. package/dist/plugins/seal/keygen.mjs.map +1 -1
  368. package/dist/plugins/seal/mode/fork-known.mjs.map +1 -1
  369. package/dist/plugins/seal/mode/live.mjs +1 -1
  370. package/dist/plugins/seal/mode/live.mjs.map +1 -1
  371. package/dist/plugins/seal/mode/local-keygen.mjs +4 -10
  372. package/dist/plugins/seal/mode/local-keygen.mjs.map +1 -1
  373. package/dist/plugins/seal/plugin-key.mjs.map +1 -1
  374. package/dist/plugins/seal/registry-publish.mjs.map +1 -1
  375. package/dist/plugins/seal/routable.mjs.map +1 -1
  376. package/dist/plugins/seal/service.mjs.map +1 -1
  377. package/dist/plugins/seal/snapshot.mjs +1 -2
  378. package/dist/plugins/seal/snapshot.mjs.map +1 -1
  379. package/dist/plugins/sui/auto-tick.mjs +5 -5
  380. package/dist/plugins/sui/auto-tick.mjs.map +1 -1
  381. package/dist/plugins/sui/chain-build-container.mjs +2 -2
  382. package/dist/plugins/sui/chain-build-container.mjs.map +1 -1
  383. package/dist/plugins/sui/chain-probe.mjs +4 -4
  384. package/dist/plugins/sui/chain-probe.mjs.map +1 -1
  385. package/dist/plugins/sui/codegen.d.mts +9 -7
  386. package/dist/plugins/sui/codegen.mjs +49 -46
  387. package/dist/plugins/sui/codegen.mjs.map +1 -1
  388. package/dist/plugins/sui/errors.d.mts +1 -1
  389. package/dist/plugins/sui/errors.mjs +1 -9
  390. package/dist/plugins/sui/errors.mjs.map +1 -1
  391. package/dist/{substrate/runtime/sui-execute → plugins/sui/exec}/index.d.mts +1 -1
  392. package/dist/{substrate/runtime/sui-execute → plugins/sui/exec}/index.mjs +3 -7
  393. package/dist/plugins/sui/exec/index.mjs.map +1 -0
  394. package/dist/{substrate/runtime/sui-execute → plugins/sui/exec}/sign-and-dispatch.mjs +1 -1
  395. package/dist/plugins/sui/exec/sign-and-dispatch.mjs.map +1 -0
  396. package/dist/plugins/sui/fork-faucet-strategy.mjs +2 -6
  397. package/dist/plugins/sui/fork-faucet-strategy.mjs.map +1 -1
  398. package/dist/plugins/sui/fork-orchestration.mjs +13 -23
  399. package/dist/plugins/sui/fork-orchestration.mjs.map +1 -1
  400. package/dist/plugins/sui/fork-transaction.mjs.map +1 -1
  401. package/dist/plugins/sui/index.d.mts +34 -83
  402. package/dist/plugins/sui/index.mjs +200 -68
  403. package/dist/plugins/sui/index.mjs.map +1 -1
  404. package/dist/{substrate/runtime/sui-ledger → plugins/sui/ledger}/object-ref.mjs +1 -1
  405. package/dist/plugins/sui/ledger/object-ref.mjs.map +1 -0
  406. package/dist/plugins/sui/local-faucet-strategy.mjs +1 -5
  407. package/dist/plugins/sui/local-faucet-strategy.mjs.map +1 -1
  408. package/dist/plugins/sui/log-attrs.mjs +11 -0
  409. package/dist/plugins/sui/log-attrs.mjs.map +1 -0
  410. package/dist/plugins/sui/mode/external.mjs +4 -12
  411. package/dist/plugins/sui/mode/external.mjs.map +1 -1
  412. package/dist/plugins/sui/mode/fork.mjs +19 -16
  413. package/dist/plugins/sui/mode/fork.mjs.map +1 -1
  414. package/dist/plugins/sui/mode/live.mjs +4 -17
  415. package/dist/plugins/sui/mode/live.mjs.map +1 -1
  416. package/dist/plugins/sui/mode/local.d.mts +1 -0
  417. package/dist/plugins/sui/mode/local.mjs +203 -61
  418. package/dist/plugins/sui/mode/local.mjs.map +1 -1
  419. package/dist/plugins/sui/mode/shared-boot.mjs +18 -23
  420. package/dist/plugins/sui/mode/shared-boot.mjs.map +1 -1
  421. package/dist/plugins/sui/mode/shared.d.mts +6 -7
  422. package/dist/plugins/sui/mode/shared.mjs.map +1 -1
  423. package/dist/plugins/sui/mode/spec.d.mts +23 -6
  424. package/dist/{substrate/runtime/sui-move-build → plugins/sui/move}/index.mjs +28 -30
  425. package/dist/plugins/sui/move/index.mjs.map +1 -0
  426. package/dist/plugins/sui/move-summary-runner.mjs +8 -4
  427. package/dist/plugins/sui/move-summary-runner.mjs.map +1 -1
  428. package/dist/{substrate/network.d.mts → plugins/sui/network-config.d.mts} +11 -10
  429. package/dist/plugins/sui/network-resolver.d.mts +6 -6
  430. package/dist/plugins/sui/routable.mjs.map +1 -1
  431. package/dist/plugins/sui/service.mjs +10 -6
  432. package/dist/plugins/sui/service.mjs.map +1 -1
  433. package/dist/plugins/sui/snapshot.mjs +10 -5
  434. package/dist/plugins/sui/snapshot.mjs.map +1 -1
  435. package/dist/plugins/wallet/codegen.d.mts +8 -6
  436. package/dist/plugins/wallet/codegen.mjs +4 -10
  437. package/dist/plugins/wallet/codegen.mjs.map +1 -1
  438. package/dist/plugins/wallet/errors.mjs +1 -4
  439. package/dist/plugins/wallet/errors.mjs.map +1 -1
  440. package/dist/plugins/wallet/index.d.mts +3 -6
  441. package/dist/plugins/wallet/index.mjs +16 -21
  442. package/dist/plugins/wallet/index.mjs.map +1 -1
  443. package/dist/plugins/wallet/origin-policy.mjs +3 -3
  444. package/dist/plugins/wallet/origin-policy.mjs.map +1 -1
  445. package/dist/plugins/wallet/pairing.mjs +6 -24
  446. package/dist/plugins/wallet/pairing.mjs.map +1 -1
  447. package/dist/plugins/wallet/protocol.mjs.map +1 -1
  448. package/dist/plugins/wallet/routable.mjs.map +1 -1
  449. package/dist/plugins/wallet/server.mjs +17 -24
  450. package/dist/plugins/wallet/server.mjs.map +1 -1
  451. package/dist/plugins/wallet/service.d.mts +2 -2
  452. package/dist/plugins/wallet/service.mjs +4 -15
  453. package/dist/plugins/wallet/service.mjs.map +1 -1
  454. package/dist/plugins/wallet/snapshot.mjs.map +1 -1
  455. package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs +1 -5
  456. package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs.map +1 -1
  457. package/dist/plugins/walrus/codegen.d.mts +2 -13
  458. package/dist/plugins/walrus/codegen.mjs +93 -28
  459. package/dist/plugins/walrus/codegen.mjs.map +1 -1
  460. package/dist/plugins/walrus/deploy-paths.mjs +2 -3
  461. package/dist/plugins/walrus/deploy-paths.mjs.map +1 -1
  462. package/dist/plugins/walrus/deploy.mjs +26 -19
  463. package/dist/plugins/walrus/deploy.mjs.map +1 -1
  464. package/dist/plugins/walrus/errors.mjs +1 -8
  465. package/dist/plugins/walrus/errors.mjs.map +1 -1
  466. package/dist/plugins/walrus/faucet-strategy.mjs +15 -1
  467. package/dist/plugins/walrus/faucet-strategy.mjs.map +1 -1
  468. package/dist/plugins/walrus/index.d.mts +13 -44
  469. package/dist/plugins/walrus/index.mjs +119 -122
  470. package/dist/plugins/walrus/index.mjs.map +1 -1
  471. package/dist/plugins/walrus/mode/known-deploy.mjs +6 -6
  472. package/dist/plugins/walrus/mode/known-deploy.mjs.map +1 -1
  473. package/dist/plugins/walrus/mode/local-cluster.d.mts +2 -2
  474. package/dist/plugins/walrus/mode/local-cluster.mjs +2 -10
  475. package/dist/plugins/walrus/mode/local-cluster.mjs.map +1 -1
  476. package/dist/plugins/walrus/plugin-key.mjs.map +1 -1
  477. package/dist/plugins/walrus/registry-publish.mjs.map +1 -1
  478. package/dist/plugins/walrus/routable.mjs.map +1 -1
  479. package/dist/plugins/walrus/service.mjs.map +1 -1
  480. package/dist/plugins/walrus/snapshot.mjs +3 -3
  481. package/dist/plugins/walrus/snapshot.mjs.map +1 -1
  482. package/dist/plugins/walrus/storage-nodes.mjs +53 -14
  483. package/dist/plugins/walrus/storage-nodes.mjs.map +1 -1
  484. package/dist/plugins/walrus/wal-swap.mjs +4 -8
  485. package/dist/plugins/walrus/wal-swap.mjs.map +1 -1
  486. package/dist/primitives/artifact-publisher.mjs.map +1 -1
  487. package/dist/primitives/cache.d.mts +1 -1
  488. package/dist/runtime/docker/client.mjs +47 -3
  489. package/dist/runtime/docker/client.mjs.map +1 -1
  490. package/dist/runtime/docker/container.mjs +37 -57
  491. package/dist/runtime/docker/container.mjs.map +1 -1
  492. package/dist/runtime/docker/errors.mjs.map +1 -1
  493. package/dist/runtime/docker/exec.mjs +2 -2
  494. package/dist/runtime/docker/exec.mjs.map +1 -1
  495. package/dist/runtime/docker/image.mjs +10 -11
  496. package/dist/runtime/docker/image.mjs.map +1 -1
  497. package/dist/runtime/docker/index.mjs +0 -1
  498. package/dist/runtime/docker/inspect-and-decode.mjs.map +1 -1
  499. package/dist/runtime/docker/inventory.mjs +9 -9
  500. package/dist/runtime/docker/inventory.mjs.map +1 -1
  501. package/dist/runtime/docker/labels.mjs +12 -1
  502. package/dist/runtime/docker/labels.mjs.map +1 -1
  503. package/dist/runtime/docker/network.mjs +6 -6
  504. package/dist/runtime/docker/network.mjs.map +1 -1
  505. package/dist/runtime/docker/render-run-args.mjs.map +1 -1
  506. package/dist/runtime/docker/service.mjs +31 -45
  507. package/dist/runtime/docker/service.mjs.map +1 -1
  508. package/dist/runtime/docker/sweep.mjs +14 -64
  509. package/dist/runtime/docker/sweep.mjs.map +1 -1
  510. package/dist/runtime/docker/volume.mjs +1 -1
  511. package/dist/runtime/docker/volume.mjs.map +1 -1
  512. package/dist/runtime/docker/wrap.mjs +14 -2
  513. package/dist/runtime/docker/wrap.mjs.map +1 -1
  514. package/dist/substrate/brand.d.mts +2 -5
  515. package/dist/substrate/brand.mjs +3 -2
  516. package/dist/substrate/brand.mjs.map +1 -1
  517. package/dist/substrate/cross-process.mjs +2 -16
  518. package/dist/substrate/cross-process.mjs.map +1 -1
  519. package/dist/substrate/event-time.mjs +0 -2
  520. package/dist/substrate/event-time.mjs.map +1 -1
  521. package/dist/substrate/events.d.mts +46 -16
  522. package/dist/substrate/identity.d.mts +11 -5
  523. package/dist/substrate/manifest.d.mts +33 -26
  524. package/dist/substrate/manifest.mjs +8 -3
  525. package/dist/substrate/manifest.mjs.map +1 -1
  526. package/dist/substrate/options.d.mts +33 -4
  527. package/dist/substrate/plugin-ctx.d.mts +70 -0
  528. package/dist/substrate/plugin-ctx.mjs +66 -0
  529. package/dist/substrate/plugin-ctx.mjs.map +1 -0
  530. package/dist/substrate/plugin.d.mts +46 -38
  531. package/dist/substrate/plugin.mjs +13 -9
  532. package/dist/substrate/plugin.mjs.map +1 -1
  533. package/dist/substrate/projection.d.mts +1 -1
  534. package/dist/substrate/runtime/atomic-write.mjs +3 -4
  535. package/dist/substrate/runtime/atomic-write.mjs.map +1 -1
  536. package/dist/substrate/runtime/cache/schema.mjs.map +1 -1
  537. package/dist/substrate/runtime/cache/service.mjs +50 -20
  538. package/dist/substrate/runtime/cache/service.mjs.map +1 -1
  539. package/dist/substrate/runtime/config-validation.mjs.map +1 -1
  540. package/dist/substrate/runtime/control-plane/domain.mjs +34 -12
  541. package/dist/substrate/runtime/control-plane/domain.mjs.map +1 -1
  542. package/dist/substrate/runtime/control-plane/service.mjs.map +1 -1
  543. package/dist/substrate/runtime/cross-process/command-channel/channel.mjs +14 -17
  544. package/dist/substrate/runtime/cross-process/command-channel/channel.mjs.map +1 -1
  545. package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs.map +1 -1
  546. package/dist/substrate/runtime/cross-process/command-channel/protocol.mjs.map +1 -1
  547. package/dist/substrate/runtime/cross-process/command-channel/runtime-control-lock.mjs.map +1 -1
  548. package/dist/substrate/runtime/cross-process/index.mjs +0 -2
  549. package/dist/substrate/runtime/cross-process/live-clock.mjs +1 -1
  550. package/dist/substrate/runtime/cross-process/live-clock.mjs.map +1 -1
  551. package/dist/substrate/runtime/cross-process/liveness.mjs +11 -18
  552. package/dist/substrate/runtime/cross-process/liveness.mjs.map +1 -1
  553. package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs +2 -2
  554. package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs.map +1 -1
  555. package/dist/substrate/runtime/cross-process/roster.mjs +17 -127
  556. package/dist/substrate/runtime/cross-process/roster.mjs.map +1 -1
  557. package/dist/substrate/runtime/cross-process/self-pid.mjs.map +1 -1
  558. package/dist/substrate/runtime/cross-process/stack-lock.mjs +29 -28
  559. package/dist/substrate/runtime/cross-process/stack-lock.mjs.map +1 -1
  560. package/dist/substrate/runtime/current-plugin.mjs.map +1 -1
  561. package/dist/substrate/runtime/errors.mjs +1 -13
  562. package/dist/substrate/runtime/errors.mjs.map +1 -1
  563. package/dist/substrate/runtime/format-unknown-error.mjs.map +1 -1
  564. package/dist/substrate/runtime/host-bind-mount-owner.mjs.map +1 -1
  565. package/dist/substrate/runtime/host-gateway.mjs.map +1 -1
  566. package/dist/substrate/runtime/host-tree-tar/index.mjs +15 -163
  567. package/dist/substrate/runtime/host-tree-tar/index.mjs.map +1 -1
  568. package/dist/substrate/runtime/http-probe.d.mts +1 -3
  569. package/dist/substrate/runtime/http-probe.mjs.map +1 -1
  570. package/dist/substrate/runtime/index.mjs +1 -6
  571. package/dist/substrate/runtime/lease-broker/service.mjs +5 -23
  572. package/dist/substrate/runtime/lease-broker/service.mjs.map +1 -1
  573. package/dist/substrate/runtime/lifecycle/dep-graph.mjs +3 -3
  574. package/dist/substrate/runtime/lifecycle/dep-graph.mjs.map +1 -1
  575. package/dist/substrate/runtime/lifecycle/graph-input-id.d.mts +1 -0
  576. package/dist/substrate/runtime/lifecycle/graph-input-id.mjs +133 -0
  577. package/dist/substrate/runtime/lifecycle/graph-input-id.mjs.map +1 -0
  578. package/dist/substrate/runtime/lifecycle/index.mjs +2 -2
  579. package/dist/substrate/runtime/lifecycle/plugin-registry.d.mts +1 -1
  580. package/dist/substrate/runtime/lifecycle/plugin-registry.mjs +22 -1
  581. package/dist/substrate/runtime/lifecycle/plugin-registry.mjs.map +1 -1
  582. package/dist/substrate/runtime/lifecycle/ready-gate.mjs +2 -15
  583. package/dist/substrate/runtime/lifecycle/ready-gate.mjs.map +1 -1
  584. package/dist/substrate/runtime/lifecycle/selective-restart.mjs +29 -25
  585. package/dist/substrate/runtime/lifecycle/selective-restart.mjs.map +1 -1
  586. package/dist/substrate/runtime/lifecycle/signals.mjs +1 -1
  587. package/dist/substrate/runtime/lifecycle/signals.mjs.map +1 -1
  588. package/dist/substrate/runtime/lifecycle/state-machine.mjs.map +1 -1
  589. package/dist/substrate/runtime/lifecycle/watch-attribution.d.mts +1 -1
  590. package/dist/substrate/runtime/lifecycle/watch-attribution.mjs +0 -1
  591. package/dist/substrate/runtime/lifecycle/watch-attribution.mjs.map +1 -1
  592. package/dist/substrate/runtime/managed-container.d.mts +5 -1
  593. package/dist/substrate/runtime/managed-container.mjs +8 -10
  594. package/dist/substrate/runtime/managed-container.mjs.map +1 -1
  595. package/dist/substrate/runtime/manifest/manifest.mjs +8 -30
  596. package/dist/substrate/runtime/manifest/manifest.mjs.map +1 -1
  597. package/dist/substrate/runtime/mode-errors.d.mts +1 -2
  598. package/dist/substrate/runtime/mode-errors.mjs.map +1 -1
  599. package/dist/substrate/runtime/observability/cascade-formatter.mjs +1 -11
  600. package/dist/substrate/runtime/observability/cascade-formatter.mjs.map +1 -1
  601. package/dist/substrate/runtime/observability/ignore-with-log.mjs.map +1 -1
  602. package/dist/substrate/runtime/observability/index.d.mts +1 -1
  603. package/dist/substrate/runtime/observability/index.mjs +4 -6
  604. package/dist/substrate/runtime/observability/log-attrs.mjs +46 -0
  605. package/dist/substrate/runtime/observability/log-attrs.mjs.map +1 -0
  606. package/dist/substrate/runtime/observability/log-store.mjs.map +1 -1
  607. package/dist/substrate/runtime/observability/logger.mjs +3 -3
  608. package/dist/substrate/runtime/observability/logger.mjs.map +1 -1
  609. package/dist/substrate/runtime/observability/output-truncate.mjs.map +1 -1
  610. package/dist/substrate/runtime/observability/pretty-error.mjs.map +1 -1
  611. package/dist/substrate/runtime/observability/process-lines.mjs.map +1 -1
  612. package/dist/substrate/runtime/observability/redaction.mjs.map +1 -1
  613. package/dist/substrate/runtime/observability/subprocess-capture.mjs +2 -2
  614. package/dist/substrate/runtime/observability/subprocess-capture.mjs.map +1 -1
  615. package/dist/substrate/runtime/passthrough-or-wrap.mjs.map +1 -1
  616. package/dist/substrate/runtime/paths.mjs +27 -9
  617. package/dist/substrate/runtime/paths.mjs.map +1 -1
  618. package/dist/substrate/runtime/phase-preserving-produce.mjs.map +1 -1
  619. package/dist/substrate/runtime/port-broker/service.mjs +10 -27
  620. package/dist/substrate/runtime/port-broker/service.mjs.map +1 -1
  621. package/dist/substrate/runtime/post-acquire-tasks.mjs.map +1 -1
  622. package/dist/substrate/runtime/probes.d.mts +2 -3
  623. package/dist/substrate/runtime/probes.mjs.map +1 -1
  624. package/dist/substrate/runtime/process-supervisor.mjs.map +1 -1
  625. package/dist/substrate/runtime/projection/operational-endpoints.mjs.map +1 -1
  626. package/dist/substrate/runtime/projection/state-ref.mjs.map +1 -1
  627. package/dist/substrate/runtime/projection/update.mjs +87 -11
  628. package/dist/substrate/runtime/projection/update.mjs.map +1 -1
  629. package/dist/substrate/runtime/random-suffix.mjs.map +1 -1
  630. package/dist/substrate/runtime/reconcile/fs-plan.mjs +146 -0
  631. package/dist/substrate/runtime/reconcile/fs-plan.mjs.map +1 -0
  632. package/dist/substrate/runtime/reconcile/graph.mjs +19 -0
  633. package/dist/substrate/runtime/reconcile/graph.mjs.map +1 -0
  634. package/dist/substrate/runtime/reconcile/index.mjs +5 -0
  635. package/dist/substrate/runtime/reconcile/label.mjs +41 -0
  636. package/dist/substrate/runtime/reconcile/label.mjs.map +1 -0
  637. package/dist/substrate/runtime/reconcile/spec.mjs +15 -0
  638. package/dist/substrate/runtime/reconcile/spec.mjs.map +1 -0
  639. package/dist/substrate/runtime/retry-policy.d.mts +2 -3
  640. package/dist/substrate/runtime/retry-policy.mjs.map +1 -1
  641. package/dist/substrate/runtime/routed-url.mjs.map +1 -1
  642. package/dist/substrate/runtime/runtime-decode.mjs.map +1 -1
  643. package/dist/substrate/runtime/scoped-http-server.mjs.map +1 -1
  644. package/dist/substrate/runtime/stage-and-swap/index.mjs +49 -12
  645. package/dist/substrate/runtime/stage-and-swap/index.mjs.map +1 -1
  646. package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.mjs.map +1 -1
  647. package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs.map +1 -1
  648. package/dist/substrate/runtime/strategy-registry/service.mjs +29 -17
  649. package/dist/substrate/runtime/strategy-registry/service.mjs.map +1 -1
  650. package/dist/substrate/runtime/subnet-broker.mjs.map +1 -1
  651. package/dist/substrate/runtime/supervisor/acquire-node.mjs +188 -56
  652. package/dist/substrate/runtime/supervisor/acquire-node.mjs.map +1 -1
  653. package/dist/substrate/runtime/supervisor/background-tasks.mjs +79 -65
  654. package/dist/substrate/runtime/supervisor/background-tasks.mjs.map +1 -1
  655. package/dist/substrate/runtime/supervisor/command-loop.mjs +82 -23
  656. package/dist/substrate/runtime/supervisor/command-loop.mjs.map +1 -1
  657. package/dist/substrate/runtime/supervisor/contribution-dispatcher.mjs +16 -0
  658. package/dist/substrate/runtime/supervisor/contribution-dispatcher.mjs.map +1 -0
  659. package/dist/substrate/runtime/supervisor/errors.mjs +11 -2
  660. package/dist/substrate/runtime/supervisor/errors.mjs.map +1 -1
  661. package/dist/substrate/runtime/supervisor/index.mjs +2 -0
  662. package/dist/substrate/runtime/supervisor/shutdown.mjs +6 -6
  663. package/dist/substrate/runtime/supervisor/shutdown.mjs.map +1 -1
  664. package/dist/substrate/runtime/supervisor/start-supervisor.mjs +60 -70
  665. package/dist/substrate/runtime/supervisor/start-supervisor.mjs.map +1 -1
  666. package/dist/substrate/runtime/supervisor/state.d.mts +1 -1
  667. package/dist/substrate/runtime/supervisor/state.mjs +1 -1
  668. package/dist/substrate/runtime/supervisor/state.mjs.map +1 -1
  669. package/dist/substrate/runtime/supervisor/teardown.mjs +10 -9
  670. package/dist/substrate/runtime/supervisor/teardown.mjs.map +1 -1
  671. package/dist/substrate/runtime/supervisor/wiring.mjs +6 -20
  672. package/dist/substrate/runtime/supervisor/wiring.mjs.map +1 -1
  673. package/dist/substrate/runtime/tar/reader.mjs +216 -0
  674. package/dist/substrate/runtime/tar/reader.mjs.map +1 -0
  675. package/dist/substrate/runtime/typed-env.mjs.map +1 -1
  676. package/dist/substrate/versioned-doc-schema.mjs.map +1 -1
  677. package/dist/substrate/versioned-doc-sync.mjs +5 -5
  678. package/dist/substrate/versioned-doc-sync.mjs.map +1 -1
  679. package/dist/surfaces/cli/command-tree.mjs +49 -7
  680. package/dist/surfaces/cli/command-tree.mjs.map +1 -1
  681. package/dist/surfaces/cli/commands/config.mjs +1 -1
  682. package/dist/surfaces/cli/commands/config.mjs.map +1 -1
  683. package/dist/surfaces/cli/commands/confirm-node.mjs.map +1 -1
  684. package/dist/surfaces/cli/commands/confirm.mjs.map +1 -1
  685. package/dist/surfaces/cli/commands/doctor.mjs +1 -1
  686. package/dist/surfaces/cli/commands/doctor.mjs.map +1 -1
  687. package/dist/surfaces/cli/commands/prune-picker-entry.mjs.map +1 -1
  688. package/dist/surfaces/cli/commands/prune-picker.mjs.map +1 -1
  689. package/dist/surfaces/cli/commands/prune.mjs +1 -1
  690. package/dist/surfaces/cli/commands/prune.mjs.map +1 -1
  691. package/dist/surfaces/cli/commands/snapshot.mjs +1 -1
  692. package/dist/surfaces/cli/commands/snapshot.mjs.map +1 -1
  693. package/dist/surfaces/cli/commands/status.mjs +1 -1
  694. package/dist/surfaces/cli/commands/status.mjs.map +1 -1
  695. package/dist/surfaces/cli/commands/supervisor-presence.mjs.map +1 -1
  696. package/dist/surfaces/cli/commands/wipe.mjs +3 -4
  697. package/dist/surfaces/cli/commands/wipe.mjs.map +1 -1
  698. package/dist/surfaces/cli/envelope.mjs.map +1 -1
  699. package/dist/surfaces/cli/errors.mjs.map +1 -1
  700. package/dist/surfaces/cli/flags.mjs.map +1 -1
  701. package/dist/surfaces/cli/index.mjs +45 -14
  702. package/dist/surfaces/cli/index.mjs.map +1 -1
  703. package/dist/surfaces/cli/output.mjs.map +1 -1
  704. package/dist/surfaces/cli/sysexits.mjs.map +1 -1
  705. package/dist/surfaces/tui/app.mjs +0 -21
  706. package/dist/surfaces/tui/app.mjs.map +1 -1
  707. package/dist/surfaces/tui/dashboard.mjs +0 -48
  708. package/dist/surfaces/tui/dashboard.mjs.map +1 -1
  709. package/dist/surfaces/tui/display-derivation.mjs +80 -16
  710. package/dist/surfaces/tui/display-derivation.mjs.map +1 -1
  711. package/dist/surfaces/tui/errors.mjs.map +1 -1
  712. package/dist/surfaces/tui/event-log.mjs +0 -9
  713. package/dist/surfaces/tui/event-log.mjs.map +1 -1
  714. package/dist/surfaces/tui/heartbeat.mjs.map +1 -1
  715. package/dist/surfaces/tui/index.mjs +2 -2
  716. package/dist/surfaces/tui/index.mjs.map +1 -1
  717. package/dist/surfaces/tui/input.mjs.map +1 -1
  718. package/dist/surfaces/tui/mode-detect.mjs.map +1 -1
  719. package/dist/surfaces/tui/mount-ink.mjs.map +1 -1
  720. package/dist/surfaces/tui/plain-renderer.mjs +26 -19
  721. package/dist/surfaces/tui/plain-renderer.mjs.map +1 -1
  722. package/dist/surfaces/tui/resource-table.mjs +8 -10
  723. package/dist/surfaces/tui/resource-table.mjs.map +1 -1
  724. package/images/postgres/Dockerfile +0 -1
  725. package/images/sui/Dockerfile +27 -54
  726. package/images/sui/entrypoint.sh +17 -179
  727. package/package.json +23 -15
  728. package/dashboard-ui/assets/index-Bmi1UtAg.js +0 -1356
  729. package/dashboard-ui/assets/index-D5EShVt4.js +0 -3
  730. package/dashboard-ui/assets/index-Deml9drg.css +0 -1
  731. package/dist/api/plugin-errors.d.mts +0 -7
  732. package/dist/api/plugin-errors.mjs +0 -10
  733. package/dist/api/plugin-errors.mjs.map +0 -1
  734. package/dist/cli/wirings/build-verb-layers.mjs +0 -42
  735. package/dist/cli/wirings/build-verb-layers.mjs.map +0 -1
  736. package/dist/contracts/capability-decl.d.mts +0 -40
  737. package/dist/orchestrators/built-in-plugin-layers.mjs +0 -54
  738. package/dist/orchestrators/built-in-plugin-layers.mjs.map +0 -1
  739. package/dist/orchestrators/run.mjs +0 -91
  740. package/dist/orchestrators/run.mjs.map +0 -1
  741. package/dist/orchestrators/runtime-composition.d.mts +0 -10
  742. package/dist/orchestrators/runtime-composition.mjs +0 -252
  743. package/dist/orchestrators/runtime-composition.mjs.map +0 -1
  744. package/dist/orchestrators/snapshot/capture-command.d.mts +0 -1
  745. package/dist/orchestrators/snapshot/capture-command.mjs +0 -25
  746. package/dist/orchestrators/snapshot/capture-command.mjs.map +0 -1
  747. package/dist/orchestrators/snapshot/pending-marker.mjs +0 -99
  748. package/dist/orchestrators/snapshot/pending-marker.mjs.map +0 -1
  749. package/dist/orchestrators/snapshot/recover-pending.mjs +0 -223
  750. package/dist/orchestrators/snapshot/recover-pending.mjs.map +0 -1
  751. package/dist/orchestrators/snapshot/state-document.d.mts +0 -1
  752. package/dist/orchestrators/snapshot/state-document.mjs +0 -58
  753. package/dist/orchestrators/snapshot/state-document.mjs.map +0 -1
  754. package/dist/plugins/account/spans.mjs +0 -16
  755. package/dist/plugins/account/spans.mjs.map +0 -1
  756. package/dist/plugins/account/variants/env.mjs +0 -24
  757. package/dist/plugins/account/variants/env.mjs.map +0 -1
  758. package/dist/plugins/account/variants/inline.mjs +0 -13
  759. package/dist/plugins/account/variants/inline.mjs.map +0 -1
  760. package/dist/plugins/account/variants/keystore.mjs +0 -91
  761. package/dist/plugins/account/variants/keystore.mjs.map +0 -1
  762. package/dist/plugins/action/spans.mjs +0 -11
  763. package/dist/plugins/action/spans.mjs.map +0 -1
  764. package/dist/plugins/coin/spans.mjs +0 -20
  765. package/dist/plugins/coin/spans.mjs.map +0 -1
  766. package/dist/plugins/deepbook/spans.mjs +0 -18
  767. package/dist/plugins/deepbook/spans.mjs.map +0 -1
  768. package/dist/plugins/faucet/spans.mjs +0 -12
  769. package/dist/plugins/faucet/spans.mjs.map +0 -1
  770. package/dist/plugins/package/spans.mjs +0 -14
  771. package/dist/plugins/package/spans.mjs.map +0 -1
  772. package/dist/plugins/postgres/codegen.mjs +0 -42
  773. package/dist/plugins/postgres/codegen.mjs.map +0 -1
  774. package/dist/plugins/postgres/connection.d.mts +0 -33
  775. package/dist/plugins/postgres/connection.mjs.map +0 -1
  776. package/dist/plugins/postgres/db-ensure.mjs.map +0 -1
  777. package/dist/plugins/postgres/errors.d.mts +0 -60
  778. package/dist/plugins/postgres/errors.mjs +0 -29
  779. package/dist/plugins/postgres/errors.mjs.map +0 -1
  780. package/dist/plugins/postgres/index.d.mts +0 -37
  781. package/dist/plugins/postgres/index.mjs +0 -68
  782. package/dist/plugins/postgres/index.mjs.map +0 -1
  783. package/dist/plugins/postgres/routable.mjs +0 -33
  784. package/dist/plugins/postgres/routable.mjs.map +0 -1
  785. package/dist/plugins/postgres/service.d.mts +0 -50
  786. package/dist/plugins/postgres/service.mjs +0 -179
  787. package/dist/plugins/postgres/service.mjs.map +0 -1
  788. package/dist/plugins/postgres/snapshot.mjs +0 -32
  789. package/dist/plugins/postgres/snapshot.mjs.map +0 -1
  790. package/dist/plugins/postgres/spans.mjs +0 -11
  791. package/dist/plugins/postgres/spans.mjs.map +0 -1
  792. package/dist/plugins/seal/spans.mjs +0 -18
  793. package/dist/plugins/seal/spans.mjs.map +0 -1
  794. package/dist/plugins/sui/spans.mjs +0 -17
  795. package/dist/plugins/sui/spans.mjs.map +0 -1
  796. package/dist/plugins/wallet/spans.mjs +0 -22
  797. package/dist/plugins/wallet/spans.mjs.map +0 -1
  798. package/dist/plugins/walrus/registry-publish.d.mts +0 -24
  799. package/dist/plugins/walrus/spans.mjs +0 -18
  800. package/dist/plugins/walrus/spans.mjs.map +0 -1
  801. package/dist/runtime/docker/logs.d.mts +0 -1
  802. package/dist/runtime/docker/logs.mjs +0 -34
  803. package/dist/runtime/docker/logs.mjs.map +0 -1
  804. package/dist/substrate/runtime/artifact-publisher/index.mjs +0 -86
  805. package/dist/substrate/runtime/artifact-publisher/index.mjs.map +0 -1
  806. package/dist/substrate/runtime/capability-sinks/index.d.mts +0 -1
  807. package/dist/substrate/runtime/capability-sinks/index.mjs +0 -3
  808. package/dist/substrate/runtime/capability-sinks/layer.d.mts +0 -1
  809. package/dist/substrate/runtime/capability-sinks/layer.mjs +0 -31
  810. package/dist/substrate/runtime/capability-sinks/layer.mjs.map +0 -1
  811. package/dist/substrate/runtime/capability-sinks/service.d.mts +0 -95
  812. package/dist/substrate/runtime/capability-sinks/service.mjs +0 -69
  813. package/dist/substrate/runtime/capability-sinks/service.mjs.map +0 -1
  814. package/dist/substrate/runtime/cross-process/lock.d.mts +0 -1
  815. package/dist/substrate/runtime/cross-process/lock.mjs +0 -23
  816. package/dist/substrate/runtime/cross-process/lock.mjs.map +0 -1
  817. package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs +0 -113
  818. package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs.map +0 -1
  819. package/dist/substrate/runtime/lifecycle/lifecycle-fact.mjs +0 -31
  820. package/dist/substrate/runtime/lifecycle/lifecycle-fact.mjs.map +0 -1
  821. package/dist/substrate/runtime/observability/formatter-registry.d.mts +0 -1
  822. package/dist/substrate/runtime/observability/formatter-registry.mjs +0 -48
  823. package/dist/substrate/runtime/observability/formatter-registry.mjs.map +0 -1
  824. package/dist/substrate/runtime/observability/span-store.d.mts +0 -1
  825. package/dist/substrate/runtime/observability/span-store.mjs +0 -110
  826. package/dist/substrate/runtime/observability/span-store.mjs.map +0 -1
  827. package/dist/substrate/runtime/observability/spans.d.mts +0 -1
  828. package/dist/substrate/runtime/observability/spans.mjs +0 -87
  829. package/dist/substrate/runtime/observability/spans.mjs.map +0 -1
  830. package/dist/substrate/runtime/projection/index.mjs +0 -4
  831. package/dist/substrate/runtime/projection/persisted.mjs +0 -213
  832. package/dist/substrate/runtime/projection/persisted.mjs.map +0 -1
  833. package/dist/substrate/runtime/scoped-multimap/service.mjs +0 -52
  834. package/dist/substrate/runtime/scoped-multimap/service.mjs.map +0 -1
  835. package/dist/substrate/runtime/scoped-ref-map/index.mjs +0 -2
  836. package/dist/substrate/runtime/scoped-ref-map/service.mjs +0 -83
  837. package/dist/substrate/runtime/scoped-ref-map/service.mjs.map +0 -1
  838. package/dist/substrate/runtime/state-store/index.mjs +0 -3
  839. package/dist/substrate/runtime/state-store/schema.d.mts +0 -1
  840. package/dist/substrate/runtime/state-store/schema.mjs +0 -41
  841. package/dist/substrate/runtime/state-store/schema.mjs.map +0 -1
  842. package/dist/substrate/runtime/state-store/service.d.mts +0 -1
  843. package/dist/substrate/runtime/state-store/service.mjs +0 -145
  844. package/dist/substrate/runtime/state-store/service.mjs.map +0 -1
  845. package/dist/substrate/runtime/sui-execute/index.mjs.map +0 -1
  846. package/dist/substrate/runtime/sui-execute/sign-and-dispatch.mjs.map +0 -1
  847. package/dist/substrate/runtime/sui-ledger/object-ref.mjs.map +0 -1
  848. package/dist/substrate/runtime/sui-move-build/index.mjs.map +0 -1
  849. package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs +0 -151
  850. package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs.map +0 -1
  851. package/dist/substrate/state-store.d.mts +0 -1
  852. /package/dist/orchestrators/{snapshot/pending-marker.d.mts → codegen/id-config.d.mts} +0 -0
  853. /package/dist/orchestrators/snapshot/{recover-pending.d.mts → interrupted-restore.d.mts} +0 -0
  854. /package/dist/{substrate/runtime/sui-execute → plugins/sui/exec}/sign-and-dispatch.d.mts +0 -0
  855. /package/dist/plugins/{postgres/snapshot.d.mts → sui/ledger/object-ref.d.mts} +0 -0
  856. /package/dist/{substrate/runtime/sui-move-build → plugins/sui/move}/index.d.mts +0 -0
  857. /package/dist/substrate/runtime/{state-store/index.d.mts → reconcile/graph.d.mts} +0 -0
  858. /package/dist/substrate/runtime/{cross-process/snapshot-reservation.d.mts → supervisor/contribution-dispatcher.d.mts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"capture.mjs","names":["streamHostTreeTar"],"sources":["../../../src/orchestrators/snapshot/capture.ts"],"sourcesContent":["// Capture pipeline.\n//\n// Architecture § Snapshot lifecycle (capture half):\n//\n// Walk Snapshotable registry; group by plugin.\n// Validate the full capture set.\n// Pause every running managed container (unless already stopped).\n// While paused:\n// docker commit + tag committed images\n// tar host-tree subtrees with mode round-trip\n// collect metadata slice\n// Unpause (always — success AND failure)\n// Stage everything in tempdir.\n// Atomic rename → snapshot catalog entry.\n//\n// The orchestrator is name-blind: it walks `Snapshotable` contributions\n// without referencing any service. Containers are enumerated via the\n// label tuple the participant declared; subtrees via the relative\n// paths the participant declared.\n\nimport { Effect, Exit, FileSystem, Schema, Stream } from 'effect';\n\nimport type {\n\tContainerRuntime,\n\tContainerHandle,\n\tTaggedImageRef,\n} from '../../contracts/container-runtime.ts';\nimport type { ContainerLabelTuple, SnapshotableDecl } from '../../contracts/snapshotable.ts';\nimport { tarHostTree as streamHostTreeTar } from '../../substrate/runtime/host-tree-tar/index.ts';\nimport {\n\tcontainerImagesBundlePath,\n\tcontributionPath,\n\tDEPLOY_CACHE_NAMESPACES,\n\tSnapshotLayout,\n\ttype CapturedContainer,\n\ttype CapturedSubtree,\n\ttype ContributionDoc,\n\ttype IdentitySlice,\n\ttype SnapshotMetadata,\n\ttype SnapshotId,\n\tSNAPSHOT_META_VERSION,\n\tisRestorableContainerImageName,\n\tisSafeSnapshotPathSegment,\n\tSNAPSHOT_CONTRIBUTION_VERSION,\n} from './descriptor.ts';\nimport { CACHE_DIR_NAME } from './wipe.ts';\nimport {\n\tImageBundleTagScanError,\n\treadImageBundleTags,\n\tverifyImageBundleTags,\n} from './image-bundle-tags.ts';\nimport { writeArtifactIntegrity } from './integrity.ts';\nimport { makePhaseFailer } from './phase-error.ts';\nimport {\n\tmergeContributions,\n\trequireIdentity,\n\ttype IdentityContribution,\n\ttype IdentityContributionConflictError,\n\ttype IdentityGuardError,\n} from './identity-guard.ts';\nimport { readSnapshotStateDocument, writeSnapshotStateDocument } from './state-document.ts';\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\n\n/** Tagged failure during one capture step. `phase` discriminates so\n * the user-facing message names the failing step. */\nexport class CapturePhaseError extends Schema.TaggedErrorClass<CapturePhaseError>()(\n\t'SnapshotCapturePhaseError',\n\t{\n\t\tphase: Schema.Literals([\n\t\t\t'enumerate-containers',\n\t\t\t'quiesce',\n\t\t\t'pause',\n\t\t\t'commit',\n\t\t\t'save-images',\n\t\t\t'tar-subtree',\n\t\t\t'tar-host-tree',\n\t\t\t'read-state',\n\t\t\t'write-state',\n\t\t\t'write-contribution',\n\t\t\t'write-meta',\n\t\t\t'write-integrity',\n\t\t\t'unpause',\n\t\t]),\n\t\tplugin: Schema.optional(Schema.String),\n\t\tdetail: Schema.String,\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n// -----------------------------------------------------------------------------\n// Participant — what the orchestrator receives from each plugin\n// -----------------------------------------------------------------------------\n\n/** Capture-time projection of one `SnapshotableDecl` plus the\n * contributing plugin's identity. The orchestrator does not see\n * `SnapshotableDecl` directly — it consumes this normalized shape so\n * the contribution is closed over the plugin's key (the contract\n * doesn't carry it; the substrate stamps it at registration time). */\nexport interface SnapshotParticipant {\n\treadonly plugin: string;\n\treadonly decl: SnapshotableDecl;\n\t/** Identity slice this participant contributes to the guard. The\n\t * orchestrator merges contributions across participants — see\n\t * `identity-guard.ts`. Returned by the participant's preRestore\n\t * hook on RESTORE; on CAPTURE the substrate reads the live value\n\t * via this `captureIdentity` helper (separately wired). */\n\treadonly captureIdentity: Effect.Effect<IdentitySlice>;\n\t/** Opaque JSON metadata the plugin wants snapshotted. The\n\t * contribution document validates only an envelope around this\n\t * payload; the orchestrator never relies on the payload shape. */\n\treadonly captureContribution: Effect.Effect<unknown>;\n}\n\nexport type SnapshotCaptureProgressPhase =\n\t| 'quiescing'\n\t| 'pausing'\n\t| 'paused'\n\t| 'capturing-containers'\n\t| 'saving-images'\n\t| 'capturing-host-tree'\n\t| 'saving-state'\n\t| 'saving-contributions'\n\t| 'writing-metadata'\n\t| 'resuming';\n\nexport interface SnapshotCaptureProgress {\n\treadonly phase: SnapshotCaptureProgressPhase;\n\treadonly detail?: string;\n\treadonly pausedContainers?: number;\n\treadonly totalContainers?: number;\n}\n\nexport type SnapshotProgressReporter = (\n\tprogress: SnapshotCaptureProgress,\n) => Effect.Effect<void, never>;\n\n// -----------------------------------------------------------------------------\n// Staging — populate a directory; the caller wraps in stage-and-swap.\n// -----------------------------------------------------------------------------\n\nconst failPhase = makePhaseFailer(CapturePhaseError);\n\nconst failImageBundleTagScan = (\n\tcause: ImageBundleTagScanError,\n): Effect.Effect<never, CapturePhaseError> =>\n\tEffect.fail(new CapturePhaseError({ phase: 'save-images', detail: cause.detail, cause }));\n\ninterface QuiescedContainer {\n\treadonly handle: ContainerHandle;\n\treadonly labels: ContainerLabelTuple;\n\treadonly unpauseAfterCapture: boolean;\n}\n\ninterface PlannedContainerCapture extends QuiescedContainer {\n\treadonly participant: SnapshotParticipant;\n}\n\ninterface ReadyContainerCapture extends PlannedContainerCapture {\n\treadonly commitHandle: ContainerHandle;\n}\n\ninterface CommittedContainerCapture {\n\treadonly captured: CapturedContainer;\n\treadonly imageRef: TaggedImageRef;\n}\n\nconst validateSnapshotPathSegment = (\n\tkind: string,\n\tvalue: string,\n\tphase: CapturePhaseError['phase'],\n\tplugin?: string,\n): Effect.Effect<void, CapturePhaseError> =>\n\tisSafeSnapshotPathSegment(value)\n\t\t? Effect.void\n\t\t: Effect.fail(\n\t\t\t\tnew CapturePhaseError({\n\t\t\t\t\tphase,\n\t\t\t\t\tplugin,\n\t\t\t\t\tdetail: `unsafe snapshot ${kind} path segment: ${value}`,\n\t\t\t\t}),\n\t\t\t);\n\nconst validateCapturedContainer = (\n\thandle: ContainerHandle,\n\tlabels: ContainerLabelTuple,\n\tparticipant: SnapshotParticipant,\n): Effect.Effect<void, CapturePhaseError> =>\n\tEffect.gen(function* () {\n\t\tyield* validateSnapshotPathSegment('plugin', labels.plugin, 'commit', participant.plugin);\n\t\tyield* validateSnapshotPathSegment('role', labels.role, 'commit', participant.plugin);\n\t\tif (!isRestorableContainerImageName(handle.imageName)) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew CapturePhaseError({\n\t\t\t\t\tphase: 'commit',\n\t\t\t\t\tplugin: participant.plugin,\n\t\t\t\t\tdetail: `container ${handle.name} imageName is not a restorable Docker tag destination: ${handle.imageName}`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t});\n\nconst detectContainerArtifactCollisions = (\n\tplanned: ReadonlyArray<PlannedContainerCapture>,\n): Effect.Effect<void, CapturePhaseError> =>\n\tEffect.gen(function* () {\n\t\tconst seen = new Map<string, PlannedContainerCapture>();\n\t\tfor (const candidate of planned) {\n\t\t\tconst key = `${candidate.labels.plugin}/${candidate.labels.role}`;\n\t\t\tconst previous = seen.get(key);\n\t\t\tif (previous !== undefined) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew CapturePhaseError({\n\t\t\t\t\t\tphase: 'commit',\n\t\t\t\t\t\tplugin: candidate.participant.plugin,\n\t\t\t\t\t\tdetail: `duplicate managed container snapshot identity ${key} for ${previous.handle.name} and ${candidate.handle.name}`,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\tseen.set(key, candidate);\n\t\t}\n\t});\n\n/**\n * Enumerate a participant's live managed containers, then run its\n * optional app-level quiesce hook. Already-paused, exited, and\n * created containers are committed as-is and are not unpaused by\n * capture finalization.\n *\n * Architecture § Invariants: \"A running container must be paused\n * around the writable-layer commit; the unpause must fire on both\n * success and failure paths (no orphaned paused containers). A\n * stopped container is already quiescent and must not be paused.\"\n *\n * The default quiescence is `pauseAndCommit`; participants that need\n * an application-level flush (postgres, RocksDB) declare a longer-grace\n * `quiesce` effect on their decl — the orchestrator runs it BEFORE\n * `pauseAndCommit`.\n */\nconst quiesceParticipant = (\n\tparticipant: SnapshotParticipant,\n\truntime: ContainerRuntime,\n): Effect.Effect<ReadonlyArray<QuiescedContainer>, CapturePhaseError> =>\n\tEffect.gen(function* () {\n\t\tconst labelTuples = participant.decl.managedContainers ?? [];\n\t\tconst containers: QuiescedContainer[] = [];\n\t\tfor (const tuple of labelTuples) {\n\t\t\tconst matched = yield* runtime\n\t\t\t\t.inspectByLabels(tuple)\n\t\t\t\t.pipe(\n\t\t\t\t\tEffect.catch(\n\t\t\t\t\t\tfailPhase(\n\t\t\t\t\t\t\t'enumerate-containers',\n\t\t\t\t\t\t\t`inspect by labels failed for ${tuple.plugin}/${tuple.role}`,\n\t\t\t\t\t\t\tparticipant.plugin,\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\tfor (const handle of matched) {\n\t\t\t\tif (handle.status === 'running') {\n\t\t\t\t\tcontainers.push({ handle, labels: tuple, unpauseAfterCapture: true });\n\t\t\t\t} else if (\n\t\t\t\t\thandle.status === 'paused' ||\n\t\t\t\t\thandle.status === 'exited' ||\n\t\t\t\t\thandle.status === 'created'\n\t\t\t\t) {\n\t\t\t\t\tcontainers.push({ handle, labels: tuple, unpauseAfterCapture: false });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Run the optional app-level flush hook first; default pause\n\t\t// follows. The hook itself only signals failure-or-success in\n\t\t// `Cause<never>` (per `SnapshotableDecl.quiesce`'s typed shape) —\n\t\t// the orchestrator does not surface flush errors as a separate\n\t\t// tag; defects propagate through the underlying Cause.\n\t\tif (participant.decl.quiesce) {\n\t\t\tyield* participant.decl.quiesce.pipe(Effect.scoped, Effect.ignore);\n\t\t}\n\t\treturn containers;\n\t}).pipe(Effect.withSpan('orchestrator.snapshot.capture.quiesce'));\n\n/** Commit one container's writable layer to a temporary snapshot image tag. */\nconst commitContainerToImage = (\n\thandle: ContainerHandle,\n\tlabels: ContainerLabelTuple,\n\truntime: ContainerRuntime,\n\tparticipant: SnapshotParticipant,\n\tregisterCommittedRef: (ref: TaggedImageRef) => Effect.Effect<void>,\n): Effect.Effect<CommittedContainerCapture, CapturePhaseError> =>\n\tEffect.gen(function* () {\n\t\t// pause+commit produces the image; the orchestrator does NOT\n\t\t// unpause here — `runCapture` owns the unpause-on-all-paths\n\t\t// finalizer.\n\t\tconst imageRef = yield* runtime\n\t\t\t.pauseAndCommit(handle)\n\t\t\t.pipe(\n\t\t\t\tEffect.catch(\n\t\t\t\t\tfailPhase('commit', `pauseAndCommit failed for ${handle.name}`, participant.plugin),\n\t\t\t\t),\n\t\t\t);\n\t\tconst snapshotTag = imageRef.tag;\n\t\tyield* registerCommittedRef(imageRef);\n\t\tif (!isRestorableContainerImageName(snapshotTag)) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew CapturePhaseError({\n\t\t\t\t\tphase: 'commit',\n\t\t\t\t\tplugin: participant.plugin,\n\t\t\t\t\tdetail: `committed image for ${handle.name} did not receive a restorable snapshot tag`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn {\n\t\t\tcaptured: {\n\t\t\t\tplugin: labels.plugin,\n\t\t\t\trole: labels.role,\n\t\t\t\timageName: handle.imageName,\n\t\t\t\tsnapshotTag,\n\t\t\t\ttarPath: containerImagesBundlePath(),\n\t\t\t},\n\t\t\timageRef,\n\t\t};\n\t}).pipe(Effect.withSpan('orchestrator.snapshot.capture.commit'));\n\nconst saveCommittedImages = (\n\tcommitted: ReadonlyArray<CommittedContainerCapture>,\n\tstagingDir: string,\n\truntime: ContainerRuntime,\n): Effect.Effect<void, CapturePhaseError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tif (committed.length === 0) return;\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst tarPath = containerImagesBundlePath();\n\t\tconst tarDest = `${stagingDir}/${tarPath}`;\n\t\tyield* fs\n\t\t\t.makeDirectory(`${stagingDir}/${SnapshotLayout.containersDir}`, { recursive: true })\n\t\t\t.pipe(Effect.catch(failPhase('save-images', `mkdir containers dir failed`)));\n\t\tyield* Stream.run(\n\t\t\truntime.saveImages(\n\t\t\t\tcommitted.map((entry) => entry.imageRef),\n\t\t\t\t{ removeAfterSave: true },\n\t\t\t),\n\t\t\tfs.sink(tarDest),\n\t\t).pipe(\n\t\t\tEffect.catch(\n\t\t\t\tfailPhase(\n\t\t\t\t\t'save-images',\n\t\t\t\t\t`save ${committed.length} committed container images to ${tarDest} failed`,\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\t\tconst savedTags = yield* readImageBundleTags(tarDest, tarPath).pipe(\n\t\t\tEffect.catch(failImageBundleTagScan),\n\t\t);\n\t\tyield* verifyImageBundleTags(\n\t\t\ttarPath,\n\t\t\tsavedTags,\n\t\t\tcommitted.map((entry) => entry.captured.snapshotTag),\n\t\t).pipe(Effect.catch(failImageBundleTagScan));\n\t}).pipe(Effect.withSpan('orchestrator.snapshot.capture.save-images'));\n\nconst cleanupCommittedRefs = (\n\truntime: ContainerRuntime,\n\trefs: ReadonlyArray<TaggedImageRef>,\n): Effect.Effect<void> =>\n\tEffect.forEach(\n\t\trefs,\n\t\t(ref) =>\n\t\t\truntime\n\t\t\t\t.removeImage(ref)\n\t\t\t\t.pipe(\n\t\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\t\tEffect.logWarning(\n\t\t\t\t\t\t\t`remove committed snapshot image ${ref.tag} failed during capture cleanup: ${String(\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t)}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t{ concurrency: 'unbounded' },\n\t).pipe(Effect.asVoid);\n\nconst isSafeSubtreePath = (relPath: string): boolean =>\n\trelPath !== '' &&\n\trelPath !== '.' &&\n\t!relPath.startsWith('/') &&\n\t!relPath.split(/[\\\\/]+/).includes('..');\n\n/** Resolve declared subtrees against the live stack root. Missing\n * `fine` subtrees are skipped; missing `fatal` subtrees fail before\n * tar starts so the error names the responsible plugin. */\nconst resolveCapturedSubtrees = (\n\tsubtrees: ReadonlyArray<CapturedSubtree>,\n\tstackRoot: string,\n): Effect.Effect<ReadonlyArray<CapturedSubtree>, CapturePhaseError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst captured: CapturedSubtree[] = [];\n\t\tfor (const subtree of subtrees) {\n\t\t\tif (!isSafeSubtreePath(subtree.relPath)) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew CapturePhaseError({\n\t\t\t\t\t\tphase: 'tar-subtree',\n\t\t\t\t\t\tplugin: subtree.plugin,\n\t\t\t\t\t\tdetail: `unsafe snapshot subtree path: ${subtree.relPath}`,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst exists = yield* fs\n\t\t\t\t.exists(`${stackRoot}/${subtree.relPath}`)\n\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(false)));\n\t\t\tif (!exists) {\n\t\t\t\tif (subtree.missingTolerance === 'fatal') {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\tnew CapturePhaseError({\n\t\t\t\t\t\t\tphase: 'tar-subtree',\n\t\t\t\t\t\t\tplugin: subtree.plugin,\n\t\t\t\t\t\t\tdetail: `required snapshot subtree missing: ${subtree.relPath}`,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcaptured.push(subtree);\n\t\t}\n\t\treturn captured;\n\t});\n\n/** Tar the union of declared live subtrees into one `host-tree.tar`. */\nconst writeHostTreeTar = (\n\tstackRoot: string,\n\tsubtrees: ReadonlyArray<CapturedSubtree>,\n\ttarDest: string,\n): Effect.Effect<void, CapturePhaseError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst archive = streamHostTreeTar({\n\t\t\tparentDir: stackRoot,\n\t\t\trelPaths: subtrees.map((s) => s.relPath),\n\t\t});\n\t\tyield* Stream.run(archive, fs.sink(tarDest)).pipe(\n\t\t\tEffect.catch(\n\t\t\t\tfailPhase(\n\t\t\t\t\t'tar-host-tree',\n\t\t\t\t\t`tar host-tree (${subtrees.length} subtrees) to ${tarDest} failed`,\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\t}).pipe(Effect.withSpan('orchestrator.snapshot.capture.tar-host-tree'));\n\n// -----------------------------------------------------------------------------\n// Top-level capture — populate a staging directory; caller wraps in swap.\n// -----------------------------------------------------------------------------\n\nexport interface CaptureInputs {\n\treadonly stagingDir: string;\n\treadonly snapshotId: SnapshotId;\n\treadonly label: string | null;\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly network: string;\n\treadonly runtimeStackRoot: string;\n\treadonly stateFilePath: string;\n\treadonly participants: ReadonlyArray<SnapshotParticipant>;\n\treadonly runtime: ContainerRuntime;\n\treadonly onProgress?: SnapshotProgressReporter;\n}\n\n/**\n * Populate `stagingDir` with a complete snapshot artifact.\n *\n * Discipline:\n * - Iterate participants — no service names appear.\n * - Pause-around-commit with always-unpause via `addFinalizer`.\n * - Mode bits preserved by the host-tree tar primitive.\n * - meta.json is written LAST so a crashed save leaves the artifact\n * invisible to the catalog (architecture § \"Partial saves are inert\").\n *\n * Caller wraps this in `stageAndSwap` + `acquireReservation` + the\n * stack lock.\n */\nexport const runCapture = (\n\tinputs: CaptureInputs,\n): Effect.Effect<\n\tSnapshotMetadata,\n\tCapturePhaseError | IdentityGuardError | IdentityContributionConflictError,\n\tFileSystem.FileSystem\n> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t'devstack.snapshot.phase': 'capture',\n\t\t\t'devstack.snapshot.id': inputs.snapshotId,\n\t\t});\n\n\t\tconst report = (progress: SnapshotCaptureProgress): Effect.Effect<void, never> =>\n\t\t\tinputs.onProgress?.(progress) ?? Effect.void;\n\n\t\treturn yield* Effect.scoped(\n\t\t\tEffect.gen(function* () {\n\t\t\t\t// 1. Quiesce every participant, validate the full capture\n\t\t\t\t// set, then pause all running managed containers before\n\t\t\t\t// any artifact is written. The pause finalizer covers the\n\t\t\t\t// entire capture pipeline so container commits, host-tree\n\t\t\t\t// tar, scalar state, and contribution metadata share one\n\t\t\t\t// consistent snapshot window.\n\t\t\t\tconst capturedContainers: CapturedContainer[] = [];\n\t\t\t\t// Two buckets:\n\t\t\t\t//\n\t\t\t\t// `pauseIntended` — every container the orchestrator\n\t\t\t\t// ASKED Docker to pause, recorded BEFORE the pause\n\t\t\t\t// syscall. The finalizer attempts `unpause` for every\n\t\t\t\t// one of these so a `docker pause` failure that left\n\t\t\t\t// the engine in a half-paused state (race: SIGSTOP\n\t\t\t\t// delivered, then `pause` returns non-zero on a\n\t\t\t\t// downstream check; ambiguous Docker daemon behavior —\n\t\t\t\t// `pause` is a multi-step engine op against cgroups +\n\t\t\t\t// the freezer) still gets `unpause`'d on the way out.\n\t\t\t\t//\n\t\t\t\t// `pauseConfirmed` — entries Docker confirmed paused\n\t\t\t\t// (i.e. pause returned success). Used only for progress\n\t\t\t\t// reporting so the operator sees the same count that\n\t\t\t\t// the engine acknowledged.\n\t\t\t\t//\n\t\t\t\t// The `unpause` finalizer treats this as best-effort:\n\t\t\t\t// `unpause` against a non-paused container is the typical\n\t\t\t\t// Docker \"container not paused\" error which we silently\n\t\t\t\t// swallow. Any other failure is logged but does not\n\t\t\t\t// interrupt the finalizer.\n\t\t\t\tconst pauseIntended: ContainerHandle[] = [];\n\t\t\t\tconst pauseConfirmed: ContainerHandle[] = [];\n\t\t\t\tyield* Effect.addFinalizer(() =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tif (pauseIntended.length > 0) {\n\t\t\t\t\t\t\tyield* report({\n\t\t\t\t\t\t\t\tphase: 'resuming',\n\t\t\t\t\t\t\t\tdetail: `unpausing ${pauseIntended.length} container${pauseIntended.length === 1 ? '' : 's'}`,\n\t\t\t\t\t\t\t\tpausedContainers: pauseConfirmed.length,\n\t\t\t\t\t\t\t\ttotalContainers: pauseIntended.length,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tyield* Effect.forEach(\n\t\t\t\t\t\t\tpauseIntended,\n\t\t\t\t\t\t\t(handle) =>\n\t\t\t\t\t\t\t\tinputs.runtime\n\t\t\t\t\t\t\t\t\t.unpause(handle)\n\t\t\t\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\t\t\t\t\t\t\tEffect.logWarning(\n\t\t\t\t\t\t\t\t\t\t\t\t`unpause(${handle.name}) failed during snapshot capture: ${String(cause)}`,\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t{ concurrency: 'unbounded' },\n\t\t\t\t\t\t);\n\t\t\t\t\t}).pipe(Effect.asVoid),\n\t\t\t\t);\n\t\t\t\tconst plannedContainers: PlannedContainerCapture[] = [];\n\t\t\t\tconst committedRefs: TaggedImageRef[] = [];\n\t\t\t\tyield* Effect.addFinalizer((exit) =>\n\t\t\t\t\tExit.isFailure(exit) ? cleanupCommittedRefs(inputs.runtime, committedRefs) : Effect.void,\n\t\t\t\t);\n\t\t\t\tyield* report({\n\t\t\t\t\tphase: 'quiescing',\n\t\t\t\t\tdetail: `checking ${inputs.participants.length} snapshot participant${\n\t\t\t\t\t\tinputs.participants.length === 1 ? '' : 's'\n\t\t\t\t\t}`,\n\t\t\t\t});\n\t\t\t\tfor (const participant of inputs.participants) {\n\t\t\t\t\tconst containers = yield* quiesceParticipant(participant, inputs.runtime);\n\t\t\t\t\tfor (const { handle, labels, unpauseAfterCapture } of containers) {\n\t\t\t\t\t\tyield* validateCapturedContainer(handle, labels, participant);\n\t\t\t\t\t\tplannedContainers.push({\n\t\t\t\t\t\t\thandle,\n\t\t\t\t\t\t\tlabels,\n\t\t\t\t\t\t\tunpauseAfterCapture,\n\t\t\t\t\t\t\tparticipant,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tyield* detectContainerArtifactCollisions(plannedContainers);\n\t\t\t\tconst runningContainers = plannedContainers.filter((entry) => entry.unpauseAfterCapture);\n\t\t\t\tif (runningContainers.length > 0) {\n\t\t\t\t\tyield* report({\n\t\t\t\t\t\tphase: 'pausing',\n\t\t\t\t\t\tdetail: `pausing ${runningContainers.length} running container${\n\t\t\t\t\t\t\trunningContainers.length === 1 ? '' : 's'\n\t\t\t\t\t\t}`,\n\t\t\t\t\t\tpausedContainers: 0,\n\t\t\t\t\t\ttotalContainers: runningContainers.length,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst readyContainers: ReadyContainerCapture[] = [];\n\t\t\t\tfor (const entry of plannedContainers) {\n\t\t\t\t\tconst { handle, unpauseAfterCapture } = entry;\n\t\t\t\t\tif (unpauseAfterCapture) {\n\t\t\t\t\t\t// Stamp INTENT before issuing the pause. If\n\t\t\t\t\t\t// `pause` fails partway through (Docker daemon\n\t\t\t\t\t\t// may have already moved the freezer into a\n\t\t\t\t\t\t// transitional state before returning non-zero),\n\t\t\t\t\t\t// the addFinalizer above will still attempt\n\t\t\t\t\t\t// `unpause(handle)` — `unpause` is a no-op for a\n\t\t\t\t\t\t// non-paused container so the worst case is a\n\t\t\t\t\t\t// swallowed \"container not paused\" log line.\n\t\t\t\t\t\t// Without this stamp, a half-paused container is\n\t\t\t\t\t\t// silently abandoned to its frozen state.\n\t\t\t\t\t\tpauseIntended.push(handle);\n\t\t\t\t\t\tyield* inputs.runtime\n\t\t\t\t\t\t\t.pause(handle)\n\t\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\t\tEffect.catch(\n\t\t\t\t\t\t\t\t\tfailPhase('pause', `pause failed for ${handle.name}`, entry.participant.plugin),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\tpauseConfirmed.push(handle);\n\t\t\t\t\t}\n\t\t\t\t\treadyContainers.push({\n\t\t\t\t\t\t...entry,\n\t\t\t\t\t\tcommitHandle: unpauseAfterCapture ? { ...handle, status: 'paused' as const } : handle,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (runningContainers.length > 0) {\n\t\t\t\t\tyield* report({\n\t\t\t\t\t\tphase: 'paused',\n\t\t\t\t\t\tdetail: `stack paused for snapshot (${pauseConfirmed.length}/${runningContainers.length} containers)`,\n\t\t\t\t\t\tpausedContainers: pauseConfirmed.length,\n\t\t\t\t\t\ttotalContainers: runningContainers.length,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// 2. Commit managed containers while the whole capture set\n\t\t\t\t// remains paused.\n\t\t\t\tconst committedContainers: CommittedContainerCapture[] = [];\n\t\t\t\tif (readyContainers.length > 0) {\n\t\t\t\t\tyield* report({\n\t\t\t\t\t\tphase: 'capturing-containers',\n\t\t\t\t\t\tdetail: `committing ${readyContainers.length} container${\n\t\t\t\t\t\t\treadyContainers.length === 1 ? '' : 's'\n\t\t\t\t\t\t}`,\n\t\t\t\t\t\tpausedContainers: pauseConfirmed.length,\n\t\t\t\t\t\ttotalContainers: runningContainers.length,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tfor (const { commitHandle, labels, participant } of readyContainers) {\n\t\t\t\t\tconst committed = yield* commitContainerToImage(\n\t\t\t\t\t\tcommitHandle,\n\t\t\t\t\t\tlabels,\n\t\t\t\t\t\tinputs.runtime,\n\t\t\t\t\t\tparticipant,\n\t\t\t\t\t\t(ref) =>\n\t\t\t\t\t\t\tEffect.sync(() => {\n\t\t\t\t\t\t\t\tcommittedRefs.push(ref);\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t\tcommittedContainers.push(committed);\n\t\t\t\t\tcapturedContainers.push(committed.captured);\n\t\t\t\t}\n\t\t\t\tif (committedContainers.length > 0) {\n\t\t\t\t\tyield* report({\n\t\t\t\t\t\tphase: 'saving-images',\n\t\t\t\t\t\tdetail: `saving ${committedContainers.length} committed image${\n\t\t\t\t\t\t\tcommittedContainers.length === 1 ? '' : 's'\n\t\t\t\t\t\t}`,\n\t\t\t\t\t\tpausedContainers: pauseConfirmed.length,\n\t\t\t\t\t\ttotalContainers: runningContainers.length,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tyield* saveCommittedImages(committedContainers, inputs.stagingDir, inputs.runtime);\n\n\t\t\t\t// 3. Tar the host-tree subtrees declared by participants.\n\t\t\t\tconst declaredSubtrees: CapturedSubtree[] = [\n\t\t\t\t\t...inputs.participants.flatMap((p) =>\n\t\t\t\t\t\tp.decl.subtrees.map((relPath) => ({\n\t\t\t\t\t\t\tplugin: p.plugin,\n\t\t\t\t\t\t\trelPath,\n\t\t\t\t\t\t\tmissingTolerance: p.decl.missingTolerance,\n\t\t\t\t\t\t\tsecretMaterial: p.decl.secretMaterial ?? false,\n\t\t\t\t\t\t})),\n\t\t\t\t\t),\n\t\t\t\t\t// Capture the deploy/mint artifact caches so the snapshot is\n\t\t\t\t\t// SELF-CONTAINED: the cached on-chain ids ride the tar and are\n\t\t\t\t\t// restored even when the live cache is gone (`snapshot → wipe →\n\t\t\t\t\t// fresh boot → restore`). `fine` tolerance — a stack with no\n\t\t\t\t\t// deploys yet simply has no `cache/<ns>` dir to capture. The\n\t\t\t\t\t// restore side ALSO preserves the live copy (the in-place fast\n\t\t\t\t\t// path) — see LIVE_RESTORE_PRESERVED_PATHS in restore.ts.\n\t\t\t\t\t...DEPLOY_CACHE_NAMESPACES.map(\n\t\t\t\t\t\t(ns): CapturedSubtree => ({\n\t\t\t\t\t\t\tplugin: 'snapshot/deploy-cache',\n\t\t\t\t\t\t\trelPath: `${CACHE_DIR_NAME}/${ns}`,\n\t\t\t\t\t\t\tmissingTolerance: 'fine',\n\t\t\t\t\t\t\tsecretMaterial: false,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t];\n\t\t\t\tconst subtrees = yield* resolveCapturedSubtrees(declaredSubtrees, inputs.runtimeStackRoot);\n\t\t\t\tconst hostTreeIncluded = subtrees.length > 0;\n\t\t\t\tif (hostTreeIncluded) {\n\t\t\t\t\tyield* report({\n\t\t\t\t\t\tphase: 'capturing-host-tree',\n\t\t\t\t\t\tdetail: `archiving ${subtrees.length} host subtree${subtrees.length === 1 ? '' : 's'}`,\n\t\t\t\t\t\tpausedContainers: pauseConfirmed.length,\n\t\t\t\t\t\ttotalContainers: runningContainers.length,\n\t\t\t\t\t});\n\t\t\t\t\tyield* writeHostTreeTar(\n\t\t\t\t\t\tinputs.runtimeStackRoot,\n\t\t\t\t\t\tsubtrees,\n\t\t\t\t\t\t`${inputs.stagingDir}/${SnapshotLayout.hostTreeTar}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// 4. Copy the scalar state file (best-effort missing-OK —\n\t\t\t\t// empty stack on first-boot has no state.json yet).\n\t\t\t\tconst stateExists = yield* fs\n\t\t\t\t\t.exists(inputs.stateFilePath)\n\t\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(false)));\n\t\t\t\tif (stateExists) {\n\t\t\t\t\tyield* report({\n\t\t\t\t\t\tphase: 'saving-state',\n\t\t\t\t\t\tdetail: 'copying runtime state',\n\t\t\t\t\t\tpausedContainers: pauseConfirmed.length,\n\t\t\t\t\t\ttotalContainers: runningContainers.length,\n\t\t\t\t\t});\n\t\t\t\t\tconst stateDoc = yield* readSnapshotStateDocument(inputs.stateFilePath).pipe(\n\t\t\t\t\t\tEffect.catch(failPhase('read-state', `state.json failed schema validation`)),\n\t\t\t\t\t);\n\t\t\t\t\tyield* writeSnapshotStateDocument(\n\t\t\t\t\t\t`${inputs.stagingDir}/${SnapshotLayout.stateFile}`,\n\t\t\t\t\t\tstateDoc,\n\t\t\t\t\t).pipe(Effect.catch(failPhase('write-state', `write state.json failed`)));\n\t\t\t\t}\n\n\t\t\t\t// 5. Write per-participant contribution docs + collect identity.\n\t\t\t\tyield* report({\n\t\t\t\t\tphase: 'saving-contributions',\n\t\t\t\t\tdetail: `writing ${inputs.participants.length} contribution document${\n\t\t\t\t\t\tinputs.participants.length === 1 ? '' : 's'\n\t\t\t\t\t}`,\n\t\t\t\t\tpausedContainers: pauseConfirmed.length,\n\t\t\t\t\ttotalContainers: runningContainers.length,\n\t\t\t\t});\n\t\t\t\tyield* fs\n\t\t\t\t\t.makeDirectory(`${inputs.stagingDir}/${SnapshotLayout.contributionsDir}`, {\n\t\t\t\t\t\trecursive: true,\n\t\t\t\t\t})\n\t\t\t\t\t.pipe(Effect.catch(failPhase('write-contribution', `mkdir contributions dir failed`)));\n\t\t\t\t// Collect per-participant identity slices first so the same\n\t\t\t\t// `mergeContributions` predicate the restore-side guard uses\n\t\t\t\t// rejects intra-capture conflicts here too. A capture that\n\t\t\t\t// silently last-write-wins would hide a conflict the\n\t\t\t\t// downstream restore would later reject as\n\t\t\t\t// `IdentityContributionConflictError` — surface it AT THE\n\t\t\t\t// CAPTURE SITE instead so the operator can fix the offending\n\t\t\t\t// plugins before the broken artifact is even written.\n\t\t\t\tconst identityContributions: IdentityContribution[] = [];\n\t\t\t\tconst stateByParticipant = new Map<string, unknown>();\n\t\t\t\tconst participantKeys: string[] = [];\n\t\t\t\tfor (const participant of inputs.participants) {\n\t\t\t\t\tconst identity = yield* participant.captureIdentity;\n\t\t\t\t\tidentityContributions.push({ plugin: participant.plugin, slice: identity });\n\t\t\t\t\tconst state = yield* participant.captureContribution;\n\t\t\t\t\tstateByParticipant.set(participant.plugin, state);\n\t\t\t\t}\n\t\t\t\tconst identityMerged = yield* mergeContributions(identityContributions);\n\t\t\t\tfor (const participant of inputs.participants) {\n\t\t\t\t\tconst identity =\n\t\t\t\t\t\tidentityContributions.find((c) => c.plugin === participant.plugin)?.slice ?? {};\n\t\t\t\t\tconst state = stateByParticipant.get(participant.plugin);\n\t\t\t\t\tconst doc: ContributionDoc = {\n\t\t\t\t\t\tversion: SNAPSHOT_CONTRIBUTION_VERSION,\n\t\t\t\t\t\tplugin: participant.plugin,\n\t\t\t\t\t\tidentity,\n\t\t\t\t\t\t...(state === undefined\n\t\t\t\t\t\t\t? {}\n\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\topaqueState: {\n\t\t\t\t\t\t\t\t\t\tencoding: 'json' as const,\n\t\t\t\t\t\t\t\t\t\tvalue: state,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t};\n\t\t\t\t\tyield* fs\n\t\t\t\t\t\t.writeFileString(\n\t\t\t\t\t\t\t`${inputs.stagingDir}/${contributionPath(participant.plugin)}`,\n\t\t\t\t\t\t\tJSON.stringify(doc, null, 2),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\tEffect.catch(\n\t\t\t\t\t\t\t\tfailPhase(\n\t\t\t\t\t\t\t\t\t'write-contribution',\n\t\t\t\t\t\t\t\t\t`write contribution doc failed`,\n\t\t\t\t\t\t\t\t\tparticipant.plugin,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\tparticipantKeys.push(participant.plugin);\n\t\t\t\t}\n\t\t\t\tyield* requireIdentity(identityMerged, 'snapshot');\n\n\t\t\t\t// 6. Write meta.json, then integrity over the full artifact.\n\t\t\t\t// The caller publishes via stage-and-swap, so catalog\n\t\t\t\t// readers still never observe a half-written artifact.\n\t\t\t\tyield* report({\n\t\t\t\t\tphase: 'writing-metadata',\n\t\t\t\t\tdetail: 'finalizing snapshot artifact',\n\t\t\t\t\tpausedContainers: pauseConfirmed.length,\n\t\t\t\t\ttotalContainers: runningContainers.length,\n\t\t\t\t});\n\t\t\t\tconst meta: SnapshotMetadata = {\n\t\t\t\t\tversion: SNAPSHOT_META_VERSION,\n\t\t\t\t\tid: inputs.snapshotId,\n\t\t\t\t\tlabel: inputs.label,\n\t\t\t\t\tcreatedAt: Date.now(),\n\t\t\t\t\tapp: inputs.app,\n\t\t\t\t\tstack: inputs.stack,\n\t\t\t\t\tnetwork: inputs.network,\n\t\t\t\t\thostTreeIncluded,\n\t\t\t\t\tsubtrees,\n\t\t\t\t\tcontainers: capturedContainers,\n\t\t\t\t\tidentity: identityMerged,\n\t\t\t\t\tparticipants: participantKeys,\n\t\t\t\t};\n\t\t\t\tyield* fs\n\t\t\t\t\t.writeFileString(\n\t\t\t\t\t\t`${inputs.stagingDir}/${SnapshotLayout.metaFile}`,\n\t\t\t\t\t\tJSON.stringify(meta, null, 2),\n\t\t\t\t\t)\n\t\t\t\t\t.pipe(Effect.catch(failPhase('write-meta', `write meta.json failed`)));\n\t\t\t\tyield* writeArtifactIntegrity(inputs.stagingDir).pipe(\n\t\t\t\t\tEffect.catch(failPhase('write-integrity', `write integrity.json failed`)),\n\t\t\t\t);\n\n\t\t\t\treturn meta;\n\t\t\t}),\n\t\t);\n\t}).pipe(Effect.withSpan('orchestrator.snapshot.capture'));\n"],"mappings":"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"}
1
+ {"version":3,"file":"capture.mjs","names":["streamHostTreeTar"],"sources":["../../../src/orchestrators/snapshot/capture.ts"],"sourcesContent":["// Capture — the unified lifecycle bounce (capture half).\n//\n// Capture is a parameterization of the lifecycle bounce, NOT a separate\n// subsystem:\n//\n// gather (BEFORE stop, plugins live)\n// → graceful-stop all managed containers (FLUSHES RocksDB/WAL → the\n// committed image is faithful — the walrus capture-survival fix; a\n// `docker pause` does NOT flush, which is the original regression)\n// → docker commit each STOPPED container + saveImages + tar host-tree\n// + write contributions + meta.json LAST → publish via stageAndSwap\n// → retag each committed image to the container's ORIGINAL image name +\n// HARD-rm the stopped containers\n// → resume = recreate-from-image + wait-write-ready\n//\n// The resume is recreate-from-image (NEVER `docker start` — walrus storage\n// nodes EXIT on `docker start` after a graceful stop) and waits for\n// write-readiness (a recreated node re-syncs its committee/epoch and is not\n// write-ready immediately). Both of those live in the LIFECYCLE: the\n// orchestrator cannot recreate a container (it has no plugin spec — env,\n// mounts, networks), so resume is an INJECTED effect that re-runs the\n// plugin acquire (the supervisor's restart / the converge), which goes\n// through `startStorageNodes`' strengthened write-ready ready-gate. So\n// capture inherits write-readiness exactly like up / restart / restore.\n//\n// Because capture commits the live state and resumes the same state, it is\n// NON-DESTRUCTIVE: the retag aliases each container's original image name\n// onto its just-committed writable layer (the flushed RocksDB), so the\n// resume's recreate-from-fresh boots the node on its own committed data —\n// the same mechanism restore uses with its loaded image bundle.\n//\n// The orchestrator is name-blind: it walks `Snapshotable` contributions\n// without referencing any service. Containers are enumerated via the label\n// tuple the participant declared; subtrees via the relative paths declared.\n// Identity / contributions are gathered BEFORE the stop because the plugin's\n// resolved state is read by live Effects that are gone once scopes close.\n\nimport { Duration, Effect, Exit, FileSystem, Schema, Stream } from 'effect';\n\nimport type {\n\tContainerRuntime,\n\tContainerHandle,\n\tTaggedImageRef,\n} from '../../contracts/container-runtime.ts';\nimport type { ContainerLabelTuple, SnapshotableDecl } from '../../contracts/snapshotable.ts';\nimport { tarHostTree as streamHostTreeTar } from '../../substrate/runtime/host-tree-tar/index.ts';\nimport {\n\tcontainerImagesBundlePath,\n\tcontributionPath,\n\tdeployCacheSubtreeRelPaths,\n\tSnapshotLayout,\n\ttype CapturedContainer,\n\ttype CapturedSubtree,\n\ttype ContributionDoc,\n\ttype IdentitySlice,\n\ttype SnapshotGraphInputIdentity,\n\ttype SnapshotMetadata,\n\ttype SnapshotId,\n\tSNAPSHOT_META_VERSION,\n\tisRestorableContainerImageName,\n\tisSafeSnapshotPathSegment,\n\tSNAPSHOT_CONTRIBUTION_VERSION,\n} from './descriptor.ts';\nimport { CACHE_DIR_NAME } from './wipe.ts';\nimport {\n\tImageBundleTagScanError,\n\treadImageBundleTags,\n\tverifyImageBundleTags,\n} from './image-bundle-tags.ts';\nimport { writeArtifactIntegrity } from './integrity.ts';\nimport { makePhaseFailer } from './phase-error.ts';\nimport {\n\tmergeContributions,\n\trequireIdentity,\n\ttype IdentityContribution,\n\ttype IdentityContributionConflictError,\n\ttype IdentityGuardError,\n} from './identity-guard.ts';\n\n/** Synthetic \"plugin\" key for the orchestrator-owned deploy-cache subtrees.\n * The cache is per-stack runtime state (not plugin-declared), so the capture\n * orchestrator injects `cache/<ns>` subtrees under this key rather than\n * attributing them to any one plugin. Distinct from any real plugin name (the\n * `__…__` sentinel shape), so it cannot collide with a participant. */\nconst DEPLOY_CACHE_SUBTREE_PLUGIN = '__deploy-cache__';\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\n\n/** Tagged failure during one capture step. `phase` discriminates so\n * the user-facing message names the failing step. */\nexport class CapturePhaseError extends Schema.TaggedErrorClass<CapturePhaseError>()(\n\t'SnapshotCapturePhaseError',\n\t{\n\t\tphase: Schema.Literals([\n\t\t\t'enumerate-containers',\n\t\t\t'stop',\n\t\t\t'commit',\n\t\t\t'save-images',\n\t\t\t'tar-subtree',\n\t\t\t'tar-host-tree',\n\t\t\t'write-contribution',\n\t\t\t'write-meta',\n\t\t\t'write-integrity',\n\t\t\t'retag-image',\n\t\t\t'remove-container',\n\t\t\t'resume',\n\t\t]),\n\t\tplugin: Schema.optional(Schema.String),\n\t\tdetail: Schema.String,\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n// -----------------------------------------------------------------------------\n// Participant — what the orchestrator receives from each plugin\n// -----------------------------------------------------------------------------\n\n/** Capture-time projection of one `SnapshotableDecl` plus the\n * contributing plugin's identity. The orchestrator does not see\n * `SnapshotableDecl` directly — it consumes this normalized shape so\n * the contribution is closed over the plugin's key (the contract\n * doesn't carry it; the substrate stamps it at registration time). */\nexport interface SnapshotParticipant {\n\treadonly plugin: string;\n\treadonly decl: SnapshotableDecl;\n\t/** Identity slice this participant contributes to the guard. Read by a\n\t * LIVE Effect (the plugin's resolved state), so it MUST run during the\n\t * pre-stop gather — it is gone once the bounce stops/recreates. */\n\treadonly captureIdentity: Effect.Effect<IdentitySlice>;\n\t/** Opaque JSON metadata the plugin wants snapshotted. The\n\t * contribution document validates only an envelope around this\n\t * payload; the orchestrator never relies on the payload shape. */\n\treadonly captureContribution: Effect.Effect<unknown>;\n}\n\n// -----------------------------------------------------------------------------\n// Staging — populate a directory; the caller wraps in stage-and-swap.\n// -----------------------------------------------------------------------------\n\nconst failPhase = makePhaseFailer(CapturePhaseError);\n\nconst failImageBundleTagScan = (\n\tcause: ImageBundleTagScanError,\n): Effect.Effect<never, CapturePhaseError> =>\n\tEffect.fail(new CapturePhaseError({ phase: 'save-images', detail: cause.detail, cause }));\n\ninterface EnumeratedContainer {\n\treadonly handle: ContainerHandle;\n\treadonly labels: ContainerLabelTuple;\n}\n\ninterface PlannedContainerCapture extends EnumeratedContainer {\n\treadonly plugin: string;\n}\n\ninterface CommittedContainerCapture {\n\treadonly captured: CapturedContainer;\n\treadonly imageRef: TaggedImageRef;\n}\n\nconst validateSnapshotPathSegment = (\n\tkind: string,\n\tvalue: string,\n\tphase: CapturePhaseError['phase'],\n\tplugin?: string,\n): Effect.Effect<void, CapturePhaseError> =>\n\tisSafeSnapshotPathSegment(value)\n\t\t? Effect.void\n\t\t: Effect.fail(\n\t\t\t\tnew CapturePhaseError({\n\t\t\t\t\tphase,\n\t\t\t\t\tplugin,\n\t\t\t\t\tdetail: `unsafe snapshot ${kind} path segment: ${value}`,\n\t\t\t\t}),\n\t\t\t);\n\nconst validateCapturedContainer = (\n\thandle: ContainerHandle,\n\tlabels: ContainerLabelTuple,\n\tplugin: string,\n): Effect.Effect<void, CapturePhaseError> =>\n\tEffect.gen(function* () {\n\t\tyield* validateSnapshotPathSegment('plugin', labels.plugin, 'commit', plugin);\n\t\tyield* validateSnapshotPathSegment('role', labels.role, 'commit', plugin);\n\t\tif (!isRestorableContainerImageName(handle.imageName)) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew CapturePhaseError({\n\t\t\t\t\tphase: 'commit',\n\t\t\t\t\tplugin,\n\t\t\t\t\tdetail: `container ${handle.name} imageName is not a restorable Docker tag destination: ${handle.imageName}`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t});\n\nconst detectContainerArtifactCollisions = (\n\tplanned: ReadonlyArray<PlannedContainerCapture>,\n): Effect.Effect<void, CapturePhaseError> =>\n\tEffect.gen(function* () {\n\t\tconst seen = new Map<string, PlannedContainerCapture>();\n\t\tfor (const candidate of planned) {\n\t\t\tconst key = `${candidate.labels.plugin}/${candidate.labels.role}`;\n\t\t\tconst previous = seen.get(key);\n\t\t\tif (previous !== undefined) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew CapturePhaseError({\n\t\t\t\t\t\tphase: 'commit',\n\t\t\t\t\t\tplugin: candidate.plugin,\n\t\t\t\t\t\tdetail: `duplicate managed container snapshot identity ${key} for ${previous.handle.name} and ${candidate.handle.name}`,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\tseen.set(key, candidate);\n\t\t}\n\t});\n\n/** Enumerate a participant's live managed containers by its declared\n * label tuples. `inspectByLabels` matches running AND stopped/created\n * containers (`docker ps -a`). */\nconst enumerateParticipantContainers = (\n\tplugin: string,\n\tlabelTuples: ReadonlyArray<ContainerLabelTuple>,\n\truntime: ContainerRuntime,\n): Effect.Effect<ReadonlyArray<EnumeratedContainer>, CapturePhaseError> =>\n\tEffect.gen(function* () {\n\t\tconst containers: EnumeratedContainer[] = [];\n\t\tfor (const tuple of labelTuples) {\n\t\t\tconst matched = yield* runtime\n\t\t\t\t.inspectByLabels(tuple)\n\t\t\t\t.pipe(\n\t\t\t\t\tEffect.catch(\n\t\t\t\t\t\tfailPhase(\n\t\t\t\t\t\t\t'enumerate-containers',\n\t\t\t\t\t\t\t`inspect by labels failed for ${tuple.plugin}/${tuple.role}`,\n\t\t\t\t\t\t\tplugin,\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\tfor (const handle of matched) {\n\t\t\t\tcontainers.push({ handle, labels: tuple });\n\t\t\t}\n\t\t}\n\t\treturn containers;\n\t});\n\n/** Commit one STOPPED container's writable layer to a temporary snapshot\n * image tag. The container has already been gracefully stopped (RocksDB\n * flushed), so this commits the faithful flushed layer. */\nconst commitStoppedContainer = (\n\thandle: ContainerHandle,\n\tlabels: ContainerLabelTuple,\n\truntime: ContainerRuntime,\n\tplugin: string,\n\tregisterCommittedRef: (ref: TaggedImageRef) => Effect.Effect<void>,\n): Effect.Effect<CommittedContainerCapture, CapturePhaseError> =>\n\tEffect.gen(function* () {\n\t\t// The container is stopped (`exited`), so `pauseAndCommit` skips its\n\t\t// pause branch and commits the already-quiescent, already-flushed\n\t\t// writable layer as-is.\n\t\tconst stoppedHandle: ContainerHandle = { ...handle, status: 'exited' };\n\t\tconst imageRef = yield* runtime\n\t\t\t.pauseAndCommit(stoppedHandle)\n\t\t\t.pipe(Effect.catch(failPhase('commit', `commit failed for ${handle.name}`, plugin)));\n\t\tconst snapshotTag = imageRef.tag;\n\t\tyield* registerCommittedRef(imageRef);\n\t\tif (!isRestorableContainerImageName(snapshotTag)) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew CapturePhaseError({\n\t\t\t\t\tphase: 'commit',\n\t\t\t\t\tplugin,\n\t\t\t\t\tdetail: `committed image for ${handle.name} did not receive a restorable snapshot tag`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn {\n\t\t\tcaptured: {\n\t\t\t\tplugin: labels.plugin,\n\t\t\t\trole: labels.role,\n\t\t\t\timageName: handle.imageName,\n\t\t\t\tsnapshotTag,\n\t\t\t\ttarPath: containerImagesBundlePath(),\n\t\t\t},\n\t\t\timageRef,\n\t\t};\n\t});\n\nconst saveCommittedImages = (\n\tcommitted: ReadonlyArray<CommittedContainerCapture>,\n\tstagingDir: string,\n\truntime: ContainerRuntime,\n): Effect.Effect<void, CapturePhaseError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tif (committed.length === 0) return;\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst tarPath = containerImagesBundlePath();\n\t\tconst tarDest = `${stagingDir}/${tarPath}`;\n\t\tyield* fs\n\t\t\t.makeDirectory(`${stagingDir}/${SnapshotLayout.containersDir}`, { recursive: true })\n\t\t\t.pipe(Effect.catch(failPhase('save-images', `mkdir containers dir failed`)));\n\t\t// `removeAfterSave: false` — the committed temp tags are retained so\n\t\t// the post-publish step can retag each onto its original image name\n\t\t// (the capture-resume alias). A capture-failure finalizer cleans them.\n\t\tyield* Stream.run(\n\t\t\truntime.saveImages(\n\t\t\t\tcommitted.map((entry) => entry.imageRef),\n\t\t\t\t{ removeAfterSave: false },\n\t\t\t),\n\t\t\tfs.sink(tarDest),\n\t\t).pipe(\n\t\t\tEffect.catch(\n\t\t\t\tfailPhase(\n\t\t\t\t\t'save-images',\n\t\t\t\t\t`save ${committed.length} committed container images to ${tarDest} failed`,\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\t\tconst savedTags = yield* readImageBundleTags(tarDest, tarPath).pipe(\n\t\t\tEffect.catch(failImageBundleTagScan),\n\t\t);\n\t\tyield* verifyImageBundleTags(\n\t\t\ttarPath,\n\t\t\tsavedTags,\n\t\t\tcommitted.map((entry) => entry.captured.snapshotTag),\n\t\t).pipe(Effect.catch(failImageBundleTagScan));\n\t});\n\nconst cleanupCommittedRefs = (\n\truntime: ContainerRuntime,\n\trefs: ReadonlyArray<TaggedImageRef>,\n): Effect.Effect<void> =>\n\tEffect.forEach(\n\t\trefs,\n\t\t(ref) =>\n\t\t\truntime\n\t\t\t\t.removeImage(ref)\n\t\t\t\t.pipe(\n\t\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\t\tEffect.logWarning(\n\t\t\t\t\t\t\t`remove committed snapshot image ${ref.tag} failed during capture cleanup: ${String(\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t)}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t{ concurrency: 'unbounded' },\n\t).pipe(Effect.asVoid);\n\nconst isSafeSubtreePath = (relPath: string): boolean =>\n\trelPath !== '' &&\n\trelPath !== '.' &&\n\t!relPath.startsWith('/') &&\n\t!relPath.split(/[\\\\/]+/).includes('..');\n\n/** Resolve declared subtrees against the live stack root. Missing\n * `fine` subtrees are skipped; missing `fatal` subtrees fail before\n * tar starts so the error names the responsible plugin. */\nconst resolveCapturedSubtrees = (\n\tsubtrees: ReadonlyArray<CapturedSubtree>,\n\tstackRoot: string,\n): Effect.Effect<ReadonlyArray<CapturedSubtree>, CapturePhaseError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst captured: CapturedSubtree[] = [];\n\t\tfor (const subtree of subtrees) {\n\t\t\tif (!isSafeSubtreePath(subtree.relPath)) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew CapturePhaseError({\n\t\t\t\t\t\tphase: 'tar-subtree',\n\t\t\t\t\t\tplugin: subtree.plugin,\n\t\t\t\t\t\tdetail: `unsafe snapshot subtree path: ${subtree.relPath}`,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst exists = yield* fs\n\t\t\t\t.exists(`${stackRoot}/${subtree.relPath}`)\n\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(false)));\n\t\t\tif (!exists) {\n\t\t\t\tif (subtree.missingTolerance === 'fatal') {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\tnew CapturePhaseError({\n\t\t\t\t\t\t\tphase: 'tar-subtree',\n\t\t\t\t\t\t\tplugin: subtree.plugin,\n\t\t\t\t\t\t\tdetail: `required snapshot subtree missing: ${subtree.relPath}`,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcaptured.push(subtree);\n\t\t}\n\t\treturn captured;\n\t});\n\n/** Tar the union of declared live subtrees into one `host-tree.tar`. */\nconst writeHostTreeTar = (\n\tstackRoot: string,\n\tsubtrees: ReadonlyArray<CapturedSubtree>,\n\ttarDest: string,\n): Effect.Effect<void, CapturePhaseError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst archive = streamHostTreeTar({\n\t\t\tparentDir: stackRoot,\n\t\t\trelPaths: subtrees.map((s) => s.relPath),\n\t\t});\n\t\tyield* Stream.run(archive, fs.sink(tarDest)).pipe(\n\t\t\tEffect.catch(\n\t\t\t\tfailPhase(\n\t\t\t\t\t'tar-host-tree',\n\t\t\t\t\t`tar host-tree (${subtrees.length} subtrees) to ${tarDest} failed`,\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\t});\n\n// -----------------------------------------------------------------------------\n// Pre-stop gather — runs while plugins are LIVE (the gather-before-drain\n// lesson: identity/contribution Effects read resolved plugin state that is\n// gone once the bounce stops/recreates).\n// -----------------------------------------------------------------------------\n\n/** One participant projected to CONCRETE pre-stop values. No unresolved\n * Effect, no live handle — every field is a plain value read while the\n * plugin was still acquired, so it survives the stop + recreate. */\ninterface GatheredParticipant {\n\treadonly plugin: string;\n\treadonly labelTuples: ReadonlyArray<ContainerLabelTuple>;\n\treadonly identitySlice: IdentitySlice;\n\treadonly opaqueState: unknown;\n}\n\n/** Plain pre-stop capture data threaded across the bounce. */\nexport interface GatheredCapture {\n\treadonly participants: ReadonlyArray<GatheredParticipant>;\n\t/** Merged + fail-closed-checked identity. `requireIdentity` already\n\t * passed during gather, so this is non-empty by construction. */\n\treadonly identityMerged: IdentitySlice;\n\treadonly declaredSubtrees: ReadonlyArray<CapturedSubtree>;\n\treadonly participantKeys: ReadonlyArray<string>;\n}\n\n/**\n * PRE-STOP gather. Runs while every plugin is still acquired/live:\n *\n * - Run each participant's `captureIdentity` / `captureContribution`\n * Effects to concrete values (they read live resolved plugin state).\n * - Snapshot each participant's `managedContainers` label tuples and\n * declared `subtrees` as plain data.\n * - Merge the identity slices with the SAME `mergeContributions`\n * predicate the restore-side guard uses (so intra-capture conflicts\n * fail HERE, at the capture site, before any stop).\n * - `requireIdentity` (fail-closed) HERE so an empty/conflicting\n * identity short-circuits BEFORE the bounce stops anything.\n *\n * The returned `GatheredCapture` holds no live registry handles; it\n * survives the stop + recreate.\n */\nexport const gatherCaptureParticipants = (\n\tparticipants: ReadonlyArray<SnapshotParticipant>,\n): Effect.Effect<GatheredCapture, IdentityGuardError | IdentityContributionConflictError, never> =>\n\tEffect.gen(function* () {\n\t\tconst identityContributions: IdentityContribution[] = [];\n\t\tconst gathered: GatheredParticipant[] = [];\n\t\tconst participantKeys: string[] = [];\n\t\tfor (const participant of participants) {\n\t\t\tconst identitySlice = yield* participant.captureIdentity;\n\t\t\tconst opaqueState = yield* participant.captureContribution;\n\t\t\tidentityContributions.push({ plugin: participant.plugin, slice: identitySlice });\n\t\t\tgathered.push({\n\t\t\t\tplugin: participant.plugin,\n\t\t\t\tlabelTuples: participant.decl.managedContainers ?? [],\n\t\t\t\tidentitySlice,\n\t\t\t\topaqueState,\n\t\t\t});\n\t\t\tparticipantKeys.push(participant.plugin);\n\t\t}\n\t\tconst identityMerged = yield* mergeContributions(identityContributions);\n\t\t// Fail-closed BEFORE any stop: an empty contributed identity refuses\n\t\t// here (pre-stop) so the bounce never tears the stack down for a\n\t\t// capture that would have been rejected anyway. Preserves the\n\t\t// `SnapshotIdentityEmpty` fail-closed guard — only EARLIER.\n\t\tyield* requireIdentity(identityMerged, 'snapshot');\n\n\t\t// Declared host-tree subtrees, captured as concrete descriptors —\n\t\t// real host-tree data (walrus blobs, the seal vault, keystores).\n\t\tconst pluginSubtrees: CapturedSubtree[] = participants.flatMap((p) =>\n\t\t\tp.decl.subtrees.map((relPath) => ({\n\t\t\t\tplugin: p.plugin,\n\t\t\t\trelPath,\n\t\t\t\tmissingTolerance: p.decl.missingTolerance,\n\t\t\t\tsecretMaterial: p.decl.secretMaterial ?? false,\n\t\t\t})),\n\t\t);\n\n\t\t// Deploy/mint artifact caches (`cache/<ns>`) ride the host-tree tar too,\n\t\t// so the snapshot is SELF-CONTAINED: a fresh runner with an empty live\n\t\t// cache recovers the deploy ids from the snapshot itself (cross-machine\n\t\t// restore). Restore untars them and the post-restore boot REUSES the\n\t\t// deploy rather than re-running it with fresh ids. `missingTolerance:\n\t\t// 'fine'` — a namespace whose plugin is disabled (e.g. `cache/deepbook`\n\t\t// in a deepbook-less stack) simply isn't on disk and is skipped; only the\n\t\t// namespaces that exist are tarred (and recorded in `meta.subtrees`). Not\n\t\t// secret material — these are public on-chain deploy/mint ids. See\n\t\t// LIVE_RESTORE_PRESERVED_PATHS in restore.ts.\n\t\tconst deployCacheSubtrees: CapturedSubtree[] = deployCacheSubtreeRelPaths(CACHE_DIR_NAME).map(\n\t\t\t(relPath) => ({\n\t\t\t\tplugin: DEPLOY_CACHE_SUBTREE_PLUGIN,\n\t\t\t\trelPath,\n\t\t\t\tmissingTolerance: 'fine' as const,\n\t\t\t\tsecretMaterial: false,\n\t\t\t}),\n\t\t);\n\n\t\t// Deploy cache FIRST so it lands at the FRONT of the host-tree tar — the\n\t\t// restore preflight scans for the cache namespaces and short-circuits the\n\t\t// (potentially huge — walrus blobs) tar read once they're all found.\n\t\tconst declaredSubtrees: CapturedSubtree[] = [...deployCacheSubtrees, ...pluginSubtrees];\n\n\t\treturn {\n\t\t\tparticipants: gathered,\n\t\t\tidentityMerged,\n\t\t\tdeclaredSubtrees,\n\t\t\tparticipantKeys,\n\t\t} satisfies GatheredCapture;\n\t});\n\n// -----------------------------------------------------------------------------\n// Top-level capture — the bounce.\n// -----------------------------------------------------------------------------\n\nexport interface CaptureInputs {\n\treadonly stagingDir: string;\n\treadonly snapshotId: SnapshotId;\n\treadonly label: string | null;\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly network: string;\n\treadonly graphInput: SnapshotGraphInputIdentity;\n\treadonly runtimeStackRoot: string;\n\treadonly participants: ReadonlyArray<SnapshotParticipant>;\n\treadonly runtime: ContainerRuntime;\n\t/** Per-container graceful-stop grace. Storage nodes need >10s to flush\n\t * + checkpoint RocksDB on `docker stop`. Defaults to 20s. */\n\treadonly stopGraceSeconds?: number;\n\t/** RESUME — re-converge the stack to write-ready AFTER the host-tree +\n\t * images are published and the original containers are removed. The\n\t * orchestrator cannot recreate a container itself (no plugin spec), so\n\t * resume is injected: the supervisor wires it to a stack restart (drain\n\t * ∘ converge), which re-runs each plugin's acquire — including walrus's\n\t * strengthened write-ready ready-gate. Omitted in unit tests / the\n\t * offline first-boot capture (where the next boot is the resume). */\n\treadonly resume?: Effect.Effect<void>;\n}\n\n/** Default graceful-stop grace — mirrors the walrus storage-node grace so\n * RocksDB flushes before the commit captures the writable layer. */\nconst DEFAULT_CAPTURE_STOP_GRACE_SECONDS = 20;\n\n/**\n * Populate `stagingDir` with a complete snapshot artifact via the bounce:\n * gather (live) → graceful-stop (flush) → commit + tar + meta → retag\n * committed images onto original names + hard-rm stopped containers →\n * resume (recreate + wait-write-ready).\n *\n * Discipline:\n * - Iterate participants — no service names appear.\n * - Identity gathered + fail-closed BEFORE any stop.\n * - Graceful stop (NOT pause) so RocksDB/WAL flush — the committed image\n * is faithful.\n * - meta.json is written LAST so a crashed save leaves the artifact\n * invisible to the catalog.\n * - Resume is recreate-from-image (injected), NEVER `docker start`.\n *\n * Caller wraps the staging build in `stageAndSwap` (the publish) — but the\n * retag/hard-rm/resume tail runs AFTER the swap so a publish failure leaves\n * the live stack untouched (only stopped, recoverable by the next boot).\n */\nexport const runCapture = (\n\tinputs: CaptureInputs,\n): Effect.Effect<\n\tSnapshotMetadata,\n\tCapturePhaseError | IdentityGuardError | IdentityContributionConflictError,\n\tFileSystem.FileSystem\n> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst grace = Duration.seconds(inputs.stopGraceSeconds ?? DEFAULT_CAPTURE_STOP_GRACE_SECONDS);\n\n\t\t// 0. GATHER (plugins live) — identity fail-closed BEFORE any stop.\n\t\tconst gathered = yield* gatherCaptureParticipants(inputs.participants);\n\n\t\treturn yield* Effect.scoped(\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst capturedContainers: CapturedContainer[] = [];\n\t\t\t\tconst committedRefs: TaggedImageRef[] = [];\n\t\t\t\t// On capture failure, drop any committed temp tags so a failed\n\t\t\t\t// capture leaves no orphaned snapshot images (prune also reaps\n\t\t\t\t// these via the SNAPSHOT_IMAGE_ROLE label).\n\t\t\t\tyield* Effect.addFinalizer((exit) =>\n\t\t\t\t\tExit.isFailure(exit) ? cleanupCommittedRefs(inputs.runtime, committedRefs) : Effect.void,\n\t\t\t\t);\n\n\t\t\t\t// 1. ENUMERATE + validate the full capture set.\n\t\t\t\tconst plannedContainers: PlannedContainerCapture[] = [];\n\t\t\t\tfor (const g of gathered.participants) {\n\t\t\t\t\tconst containers = yield* enumerateParticipantContainers(\n\t\t\t\t\t\tg.plugin,\n\t\t\t\t\t\tg.labelTuples,\n\t\t\t\t\t\tinputs.runtime,\n\t\t\t\t\t);\n\t\t\t\t\tfor (const { handle, labels } of containers) {\n\t\t\t\t\t\tyield* validateCapturedContainer(handle, labels, g.plugin);\n\t\t\t\t\t\tplannedContainers.push({ handle, labels, plugin: g.plugin });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tyield* detectContainerArtifactCollisions(plannedContainers);\n\n\t\t\t\t// 2. GRACEFUL-STOP every managed container — FLUSHES RocksDB/WAL\n\t\t\t\t// so the committed writable layer is faithful (the walrus\n\t\t\t\t// capture-survival fix). Stopped (not removed) so a publish\n\t\t\t\t// failure leaves them recoverable by the next boot.\n\t\t\t\tyield* Effect.forEach(\n\t\t\t\t\tplannedContainers,\n\t\t\t\t\t(entry) =>\n\t\t\t\t\t\tinputs.runtime\n\t\t\t\t\t\t\t.stop(entry.handle, grace)\n\t\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\t\tEffect.catch(\n\t\t\t\t\t\t\t\t\tfailPhase('stop', `graceful stop failed for ${entry.handle.name}`, entry.plugin),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t{ concurrency: 'unbounded', discard: true },\n\t\t\t\t);\n\n\t\t\t\t// 3. COMMIT each stopped container's flushed layer.\n\t\t\t\tconst committedContainers: CommittedContainerCapture[] = [];\n\t\t\t\tfor (const { handle, labels, plugin } of plannedContainers) {\n\t\t\t\t\tconst committed = yield* commitStoppedContainer(\n\t\t\t\t\t\thandle,\n\t\t\t\t\t\tlabels,\n\t\t\t\t\t\tinputs.runtime,\n\t\t\t\t\t\tplugin,\n\t\t\t\t\t\t(ref) =>\n\t\t\t\t\t\t\tEffect.sync(() => {\n\t\t\t\t\t\t\t\tcommittedRefs.push(ref);\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t\tcommittedContainers.push(committed);\n\t\t\t\t\tcapturedContainers.push(committed.captured);\n\t\t\t\t}\n\t\t\t\tyield* saveCommittedImages(committedContainers, inputs.stagingDir, inputs.runtime);\n\n\t\t\t\t// 4. TAR the host-tree subtrees declared by participants.\n\t\t\t\tconst subtrees = yield* resolveCapturedSubtrees(\n\t\t\t\t\tgathered.declaredSubtrees,\n\t\t\t\t\tinputs.runtimeStackRoot,\n\t\t\t\t);\n\t\t\t\tconst hostTreeIncluded = subtrees.length > 0;\n\t\t\t\tif (hostTreeIncluded) {\n\t\t\t\t\tyield* writeHostTreeTar(\n\t\t\t\t\t\tinputs.runtimeStackRoot,\n\t\t\t\t\t\tsubtrees,\n\t\t\t\t\t\t`${inputs.stagingDir}/${SnapshotLayout.hostTreeTar}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// 5. WRITE per-participant contribution docs from gathered state.\n\t\t\t\tyield* fs\n\t\t\t\t\t.makeDirectory(`${inputs.stagingDir}/${SnapshotLayout.contributionsDir}`, {\n\t\t\t\t\t\trecursive: true,\n\t\t\t\t\t})\n\t\t\t\t\t.pipe(Effect.catch(failPhase('write-contribution', `mkdir contributions dir failed`)));\n\t\t\t\tfor (const g of gathered.participants) {\n\t\t\t\t\tconst doc: ContributionDoc = {\n\t\t\t\t\t\tversion: SNAPSHOT_CONTRIBUTION_VERSION,\n\t\t\t\t\t\tplugin: g.plugin,\n\t\t\t\t\t\tidentity: g.identitySlice,\n\t\t\t\t\t\t...(g.opaqueState === undefined\n\t\t\t\t\t\t\t? {}\n\t\t\t\t\t\t\t: { opaqueState: { encoding: 'json' as const, value: g.opaqueState } }),\n\t\t\t\t\t};\n\t\t\t\t\tyield* fs\n\t\t\t\t\t\t.writeFileString(\n\t\t\t\t\t\t\t`${inputs.stagingDir}/${contributionPath(g.plugin)}`,\n\t\t\t\t\t\t\tJSON.stringify(doc, null, 2),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\tEffect.catch(\n\t\t\t\t\t\t\t\tfailPhase('write-contribution', `write contribution doc failed`, g.plugin),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// 6. WRITE meta.json, then integrity over the full artifact.\n\t\t\t\t// The caller publishes via stage-and-swap (atomic rename),\n\t\t\t\t// so catalog readers never observe a half-written artifact.\n\t\t\t\tconst meta: SnapshotMetadata = {\n\t\t\t\t\tversion: SNAPSHOT_META_VERSION,\n\t\t\t\t\tid: inputs.snapshotId,\n\t\t\t\t\tlabel: inputs.label,\n\t\t\t\t\tcreatedAt: Date.now(),\n\t\t\t\t\tapp: inputs.app,\n\t\t\t\t\tstack: inputs.stack,\n\t\t\t\t\tnetwork: inputs.network,\n\t\t\t\t\tgraphInput: inputs.graphInput,\n\t\t\t\t\thostTreeIncluded,\n\t\t\t\t\tsubtrees,\n\t\t\t\t\tcontainers: capturedContainers,\n\t\t\t\t\tidentity: gathered.identityMerged,\n\t\t\t\t\tparticipants: [...gathered.participantKeys],\n\t\t\t\t};\n\t\t\t\tyield* fs\n\t\t\t\t\t.writeFileString(\n\t\t\t\t\t\t`${inputs.stagingDir}/${SnapshotLayout.metaFile}`,\n\t\t\t\t\t\tJSON.stringify(meta, null, 2),\n\t\t\t\t\t)\n\t\t\t\t\t.pipe(Effect.catch(failPhase('write-meta', `write meta.json failed`)));\n\t\t\t\tyield* writeArtifactIntegrity(inputs.stagingDir).pipe(\n\t\t\t\t\tEffect.catch(failPhase('write-integrity', `write integrity.json failed`)),\n\t\t\t\t);\n\n\t\t\t\treturn meta;\n\t\t\t}),\n\t\t);\n\t});\n\n/**\n * The post-publish bounce tail: retag each committed image onto its\n * container's ORIGINAL image name (so the resume's recreate-from-image\n * boots the node on its just-committed, flushed layer), hard-rm the stopped\n * containers, then run the injected resume (recreate + wait-write-ready).\n *\n * Runs AFTER `stageAndSwap` publishes the artifact, so a publish failure\n * never reaches here — the live stack is only stopped (recoverable). The\n * retag aliases the original image name onto the committed layer; restore\n * uses the SAME retag-to-original-name mechanism with its loaded bundle, so\n * capture and restore resume identically (recreate-from-fresh off an image\n * whose name now resolves to the snapshot layer).\n *\n * This is NEVER expressed as `docker start` — walrus storage nodes EXIT on\n * `docker start` after a graceful stop; the resume's recreate is a fresh\n * `docker run` that re-syncs and the write-ready ready-gate blocks on.\n */\nexport const resumeAfterCapture = (\n\tmeta: SnapshotMetadata,\n\tinputs: Pick<CaptureInputs, 'runtime' | 'app' | 'stack' | 'resume'>,\n): Effect.Effect<void, CapturePhaseError> =>\n\tEffect.gen(function* () {\n\t\t// Retag each committed image onto its original name. Re-find the\n\t\t// committed ref by its snapshot tag; tagImage aliases the original\n\t\t// imageName onto it (removeSourceAfterTag drops the temp snapshot\n\t\t// tag — its label-owned image is now reachable by the original name).\n\t\tfor (const captured of meta.containers) {\n\t\t\t// Image GC: the retag below moves `imageName` from the layer it\n\t\t\t// currently resolves to (the now-superseded previous capture) onto\n\t\t\t// the freshly-committed snapshot layer. The previous layer is then\n\t\t\t// orphaned — no tag references it — and would dangle forever. Read\n\t\t\t// the old layer's digest BEFORE the retag so we can drop it AFTER.\n\t\t\t// A null here (first capture: nothing tagged that name yet) means\n\t\t\t// there is no orphan to collect.\n\t\t\tconst oldDigest = yield* inputs.runtime\n\t\t\t\t.inspectImageDigest(captured.imageName)\n\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(null)));\n\t\t\t// The contract's `tagImage` resolves the source by `tag ?? digest`\n\t\t\t// (runtime/docker/service.ts), so the snapshot tag is the operative\n\t\t\t// source; `digest` mirrors it to satisfy the `ImageRef` shape.\n\t\t\tyield* inputs.runtime\n\t\t\t\t.tagImage({ digest: captured.snapshotTag, tag: captured.snapshotTag }, captured.imageName, {\n\t\t\t\t\tremoveSourceAfterTag: true,\n\t\t\t\t})\n\t\t\t\t.pipe(\n\t\t\t\t\tEffect.catch(\n\t\t\t\t\t\tfailPhase(\n\t\t\t\t\t\t\t'retag-image',\n\t\t\t\t\t\t\t`tag committed image ${captured.snapshotTag} as ${captured.imageName} failed`,\n\t\t\t\t\t\t\tcaptured.plugin,\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t// ORDERING INVARIANT: GC runs strictly AFTER the retag succeeds.\n\t\t\t// Removing the old layer before the retag would destroy the live\n\t\t\t// image if the retag then failed. Now `imageName` resolves to the\n\t\t\t// new committed layer; read that digest to confirm the orphan is\n\t\t\t// genuinely distinct.\n\t\t\tconst newDigest = yield* inputs.runtime\n\t\t\t\t.inspectImageDigest(captured.imageName)\n\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(null)));\n\t\t\t// Two guards before removal:\n\t\t\t// - null guard: no previous layer to collect (first capture).\n\t\t\t// - equal-digest guard: the commit produced an identical layer\n\t\t\t// id, so `imageName` still resolves to it — removing `oldDigest`\n\t\t\t// would delete the LIVE layer. Skip the no-op case.\n\t\t\tif (oldDigest !== null && oldDigest !== newDigest) {\n\t\t\t\t// Digest-only target: `removeImage` resolves `tag ?? digest`, so\n\t\t\t\t// a digest-only ref drops the orphaned layer by id, NOT the live\n\t\t\t\t// `imageName` tag. Best-effort — any failure (image-in-use,\n\t\t\t\t// multiple tags, ImageRemoveFailed) is logged and swallowed; it\n\t\t\t\t// must NEVER fail the capture. Mirrors `cleanupCommittedRefs`.\n\t\t\t\tyield* inputs.runtime\n\t\t\t\t\t.removeImage({ digest: oldDigest })\n\t\t\t\t\t.pipe(\n\t\t\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\t\t\tEffect.logWarning(\n\t\t\t\t\t\t\t\t`GC of superseded image layer ${oldDigest} for ${captured.imageName} failed (best-effort): ${String(\n\t\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t\t)}`,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\t// HARD-rm the stopped containers (claim-bypassing) so the resume's\n\t\t// recreate sees facts:null → fresh → `docker run` the original name,\n\t\t// which now resolves to the committed layer.\n\t\tfor (const captured of meta.containers) {\n\t\t\tyield* inputs.runtime\n\t\t\t\t.removeManagedContainers({\n\t\t\t\t\tapp: inputs.app,\n\t\t\t\t\tstack: inputs.stack,\n\t\t\t\t\tplugin: captured.plugin,\n\t\t\t\t\trole: captured.role,\n\t\t\t\t})\n\t\t\t\t.pipe(\n\t\t\t\t\tEffect.catch(\n\t\t\t\t\t\tfailPhase(\n\t\t\t\t\t\t\t'remove-container',\n\t\t\t\t\t\t\t`remove managed containers for ${captured.plugin}/${captured.role} failed`,\n\t\t\t\t\t\t\tcaptured.plugin,\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t}\n\t\t// RESUME = recreate-from-image + wait-write-ready. Injected (the\n\t\t// orchestrator can't recreate without the plugin spec); the supervisor\n\t\t// wires it to a stack restart whose converge re-runs each plugin's\n\t\t// acquire — including walrus's write-ready ready-gate.\n\t\tif (inputs.resume !== undefined) {\n\t\t\tyield* inputs.resume.pipe(\n\t\t\t\tEffect.catch(failPhase('resume', `stack resume after capture failed`)),\n\t\t\t);\n\t\t}\n\t});\n"],"mappings":"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"}
@@ -1 +1 @@
1
- import { Schema } from "effect";
1
+ import { Effect, Schema } from "effect";
@@ -1,10 +1,10 @@
1
1
  import { versionedDocSchema } from "../../substrate/versioned-doc-schema.mjs";
2
- import { Schema } from "effect";
2
+ import { computeGraphInputIdentity, computeStackGraphInputIdentity } from "../../substrate/runtime/lifecycle/graph-input-id.mjs";
3
+ import { Effect, Schema } from "effect";
3
4
  //#region src/orchestrators/snapshot/descriptor.ts
4
5
  /** Canonical file / directory names inside one snapshot artifact. */
5
6
  const SnapshotLayout = {
6
7
  metaFile: "meta.json",
7
- stateFile: "state.json",
8
8
  hostTreeTar: "host-tree.tar",
9
9
  containersDir: "containers",
10
10
  contributionsDir: "contributions",
@@ -69,13 +69,19 @@ const CapturedSubtreeSchema = Schema.Struct({
69
69
  * objects, seal key-server object, deepbook pool ids, coin treasury. These
70
70
  * must come back after a restore so the post-restore boot REUSES the deploy
71
71
  * instead of re-running it with fresh ids (which orphans every pre-snapshot
72
- * object). Honored by BOTH halves so the contract holds across lifecycles:
73
- * the capture TARS `cache/<ns>` into the snapshot (self-contained — survives a
74
- * `wipe` that drops the live cache), and the restore also PRESERVES the live
75
- * `cache/<ns>` (the in-place `snapshot → restore` fast path, no tar). A
76
- * slash-prefixed plugin namespace (`seal/package`, `deepbook/pools`) nests
77
- * under its root, so the root entry covers all of that plugin's namespaces.
78
- * The generic per-call `cache/entry` is NOT here and stays dropped on restore. */
72
+ * object).
73
+ *
74
+ * The contract is SELF-CONTAINED: capture TARs `cache/<ns>` into the
75
+ * snapshot's host-tree (see `deployCacheSubtreeRelPaths`), and restore
76
+ * untars that copy and DROPS the deploy-cache from the stage-and-swap
77
+ * preserve list the snapshot's cache is the SOLE source on restore
78
+ * (no preserve-from-live, so no double-store drift). This is what makes a
79
+ * CROSS-MACHINE restore work: a fresh runner with an empty live cache
80
+ * recovers the ids from the snapshot itself. (Earlier the live cache was
81
+ * the sole source, which only works same-machine.) A slash-prefixed plugin
82
+ * namespace (`seal/package`, `deepbook/pools`) nests under its root, so the
83
+ * root entry covers all of that plugin's namespaces. The generic per-call
84
+ * `cache/entry` is NOT here and stays dropped on restore. */
79
85
  const DEPLOY_CACHE_NAMESPACES = [
80
86
  "walrus-deploy",
81
87
  "package",
@@ -84,6 +90,12 @@ const DEPLOY_CACHE_NAMESPACES = [
84
90
  "coin-mint",
85
91
  "action"
86
92
  ];
93
+ /** Host-tree subtree relPaths (relative to the stack root) for the deploy-cache
94
+ * namespaces. Capture tars these into `host-tree.tar` so the snapshot is
95
+ * self-contained; restore untars them into the swapped tree. The on-disk cache
96
+ * lives at `<stackRoot>/cache/<ns>/<chain>/<contentHash>.json`, so the root
97
+ * `cache/<ns>` entry covers every nested namespace + chain under it. */
98
+ const deployCacheSubtreeRelPaths = (cacheDirName) => DEPLOY_CACHE_NAMESPACES.map((namespace) => `${cacheDirName}/${namespace}`);
87
99
  /** Identity slice that fires the cross-chain refusal guard. The
88
100
  * orchestrator threads contributions from participating plugins
89
101
  * through this map — `chain` is the canonical case but other plugins
@@ -104,9 +116,35 @@ const ContributionDocSchema = versionedDocSchema(1, {
104
116
  identity: Schema.optional(IdentitySliceSchema),
105
117
  opaqueState: Schema.optional(OpaqueContributionStateSchema)
106
118
  });
119
+ const SnapshotNodeInputIdentitySchema = Schema.Struct({
120
+ key: Schema.String,
121
+ inputId: Schema.String,
122
+ upstreamInputIds: Schema.Array(Schema.Struct({
123
+ key: Schema.String,
124
+ inputId: Schema.String
125
+ }))
126
+ });
127
+ const SnapshotGraphInputIdentitySchema = versionedDocSchema(1, {
128
+ graphInputId: Schema.String,
129
+ nodes: Schema.Array(SnapshotNodeInputIdentitySchema)
130
+ });
131
+ const snapshotGraphInputFromIdentity = (identity) => ({
132
+ version: 1,
133
+ graphInputId: identity.graphInputId,
134
+ nodes: identity.nodes.map((node) => ({
135
+ key: node.key,
136
+ inputId: node.inputId,
137
+ upstreamInputIds: node.upstreamInputIds.map((upstream) => ({
138
+ key: upstream.key,
139
+ inputId: upstream.inputId
140
+ }))
141
+ }))
142
+ });
143
+ const computeSnapshotGraphInputFromGraph = (args) => computeGraphInputIdentity(args).pipe(Effect.map(snapshotGraphInputFromIdentity));
144
+ const computeSnapshotGraphInputFromStack = (args) => computeStackGraphInputIdentity(args).pipe(Effect.map(snapshotGraphInputFromIdentity));
107
145
  /** Top-level metadata record. Architecture § Snapshot — single canonical
108
146
  * metadata; "metadata absent = do not trust this directory". */
109
- const SnapshotMetadataSchema = versionedDocSchema(3, {
147
+ const SnapshotMetadataSchema = versionedDocSchema(4, {
110
148
  /** Stable snapshot id (caller-supplied or substrate-minted). */
111
149
  id: Schema.String,
112
150
  /** User-facing name. It is never used as filesystem authority. */
@@ -115,6 +153,10 @@ const SnapshotMetadataSchema = versionedDocSchema(3, {
115
153
  app: Schema.String,
116
154
  stack: Schema.String,
117
155
  network: Schema.String,
156
+ /** Desired-input identity of the resolved stack graph at capture time.
157
+ * Restore/startup policy compares this with the current graph before
158
+ * deciding whether a snapshot is valid for the current inputs. */
159
+ graphInput: SnapshotGraphInputIdentitySchema,
118
160
  /** Whether `host-tree.tar` is present in the artifact (false for
119
161
  * the first-boot / empty-stack capture). */
120
162
  hostTreeIncluded: Schema.Boolean,
@@ -129,6 +171,6 @@ const IntegrityFileSchema = versionedDocSchema(1, {
129
171
  /** Relative-path → hex-encoded SHA-256 of the file's bytes. */
130
172
  hashes: Schema.Record(Schema.String, Schema.String) });
131
173
  //#endregion
132
- export { ContributionDocSchema, DEPLOY_CACHE_NAMESPACES, IdentitySliceSchema, IntegrityFileSchema, OpaqueContributionStateSchema, SNAPSHOT_ID_RULE, SnapshotDescriptorError, SnapshotLayout, SnapshotMetadataSchema, containerImagesBundlePath, contributionPath, isRestorableContainerImageName, isSafeSnapshotPathSegment, isSafeSnapshotRelativePath, isValidSnapshotId, parseSnapshotId };
174
+ export { ContributionDocSchema, DEPLOY_CACHE_NAMESPACES, IntegrityFileSchema, SNAPSHOT_ID_RULE, SnapshotDescriptorError, SnapshotGraphInputIdentitySchema, SnapshotLayout, SnapshotMetadataSchema, computeSnapshotGraphInputFromGraph, computeSnapshotGraphInputFromStack, containerImagesBundlePath, contributionPath, deployCacheSubtreeRelPaths, isRestorableContainerImageName, isSafeSnapshotPathSegment, isSafeSnapshotRelativePath, parseSnapshotId, snapshotGraphInputFromIdentity };
133
175
 
134
176
  //# sourceMappingURL=descriptor.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"descriptor.mjs","names":[],"sources":["../../../src/orchestrators/snapshot/descriptor.ts"],"sourcesContent":["// Snapshot artifact descriptor + metadata schema.\n//\n// Architecture § Snapshot:\n// \"A tar of declared filesystem subtrees, committed container images\n// (one per managed container identified by label tuples), a typed\n// metadata slice (identity-guard contributions + plugin's structured\n// state).\"\n//\n// On-disk layout of one snapshot artifact (under\n// `<stackRoot>/snapshots/<snapshotId>/`):\n//\n// meta.json — versioned metadata record, schema-decoded\n// state.json — copy of the stack's scalar state-store\n// host-tree.tar — tar of subtrees declared by participants\n// containers/images.tar — deduplicated Docker save bundle for all\n// committed managed-container images\n// contributions/<encoded-plugin>.json\n// — one typed metadata slice per participant\n// integrity.json — hashes over the above for resume / probe\n//\n// Layout discipline:\n// - `meta.json` is authoritative. Architecture § Snapshotable\n// \"Metadata is authoritative\": if it's absent or unparseable,\n// restore refuses (no silent partial-restore downgrade).\n// - Anything else missing surfaces as a phase-tagged\n// `SnapshotPartialError` and aborts before any destructive mutation.\n// - The artifact directory itself becomes \"complete\" only via the\n// stage-and-swap atomic rename — readers never observe a\n// half-written tree.\n\nimport { Schema } from 'effect';\n\nimport type { Brand } from '../../substrate/brand.ts';\nimport { versionedDocSchema } from '../../substrate/versioned-doc-schema.ts';\n\n// -----------------------------------------------------------------------------\n// File layout — string constants the capture / restore code reaches for\n// -----------------------------------------------------------------------------\n\n/** Canonical file / directory names inside one snapshot artifact. */\nexport const SnapshotLayout = {\n\tmetaFile: 'meta.json',\n\tstateFile: 'state.json',\n\thostTreeTar: 'host-tree.tar',\n\tcontainersDir: 'containers',\n\tcontributionsDir: 'contributions',\n\tintegrityFile: 'integrity.json',\n} as const;\n\nexport type SnapshotId = Brand<string, 'SnapshotId'>;\n\nconst SNAPSHOT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;\n\nexport const SNAPSHOT_ID_RULE =\n\t'snapshot ids must be 1-128 characters matching [A-Za-z0-9][A-Za-z0-9_-]*';\n\n/** Tagged failure for descriptor-layer validation. Carries a discriminator\n * so downstream phase classifiers can `catchTag` and dispatch on `kind`\n * without sniffing the message. STYLE_GUIDE §2 rule 5. */\nexport class SnapshotDescriptorError extends Schema.TaggedErrorClass<SnapshotDescriptorError>()(\n\t'SnapshotDescriptorError',\n\t{\n\t\tkind: Schema.Literals(['invalid-id', 'invalid-segment']),\n\t\tdetail: Schema.String,\n\t\tvalue: Schema.String,\n\t},\n) {}\n\nexport const isValidSnapshotId = (value: string): boolean =>\n\tSNAPSHOT_ID_PATTERN.test(value) && isSafeSnapshotPathSegment(value);\n\nexport const parseSnapshotId = (value: string): SnapshotId | null =>\n\tisValidSnapshotId(value) ? (value as SnapshotId) : null;\n\n/** Brand-cast helper for known-valid id literals (primarily test\n * fixtures). Throws a typed `SnapshotDescriptorError` instance on\n * violation so any defect surfacing carries the canonical `_tag`\n * instead of a bare `Error` string-match. */\nexport const snapshotIdFromString = (value: string): SnapshotId => {\n\tconst parsed = parseSnapshotId(value);\n\tif (parsed === null) {\n\t\tthrow new SnapshotDescriptorError({\n\t\t\tkind: 'invalid-id',\n\t\t\tdetail: SNAPSHOT_ID_RULE,\n\t\t\tvalue,\n\t\t});\n\t}\n\treturn parsed;\n};\n\nexport const isSafeSnapshotPathSegment = (segment: string): boolean =>\n\tsegment.length > 0 &&\n\tsegment !== '.' &&\n\tsegment !== '..' &&\n\t!segment.includes('/') &&\n\t!segment.includes('\\\\') &&\n\t!segment.includes('\\0');\n\nexport const isSafeSnapshotRelativePath = (relPath: string): boolean =>\n\trelPath !== '' &&\n\trelPath !== '.' &&\n\t!relPath.startsWith('/') &&\n\t!relPath.includes('\\\\') &&\n\t!relPath.includes('\\0') &&\n\t!relPath.split(/[\\\\/]+/).includes('..');\n\nexport const isRestorableContainerImageName = (imageName: string): boolean =>\n\timageName.length > 0 &&\n\timageName.trim() === imageName &&\n\t!/\\s/.test(imageName) &&\n\t!imageName.includes('@') &&\n\t!imageName.startsWith('sha256:');\n\nconst snapshotPathSegment = (kind: string, value: string): string => {\n\tif (!isSafeSnapshotPathSegment(value)) {\n\t\tthrow new SnapshotDescriptorError({\n\t\t\tkind: 'invalid-segment',\n\t\t\tdetail: `unsafe snapshot ${kind} path segment`,\n\t\t\tvalue,\n\t\t});\n\t}\n\treturn value;\n};\n\nconst encodedSnapshotPathSegment = (value: string): string => {\n\tlet hex = '';\n\tfor (const byte of new TextEncoder().encode(value)) {\n\t\thex += byte.toString(16).padStart(2, '0');\n\t}\n\treturn `p-${hex}`;\n};\n\n/** Canonical sub-path for one captured container image. */\nexport const containerImagePath = (plugin: string, role: string): string =>\n\t`${SnapshotLayout.containersDir}/${snapshotPathSegment(\n\t\t'plugin',\n\t\tplugin,\n\t)}/${snapshotPathSegment('role', role)}.tar`;\n\n/** Canonical sub-path for the deduplicated managed-container image bundle. */\nexport const containerImagesBundlePath = (): string => `${SnapshotLayout.containersDir}/images.tar`;\n\n/** Canonical sub-path for one participant's typed metadata slice. */\nexport const contributionPath = (plugin: string): string =>\n\t`${SnapshotLayout.contributionsDir}/${encodedSnapshotPathSegment(plugin)}.json`;\n\n// -----------------------------------------------------------------------------\n// Metadata schemas — versioned record\n// -----------------------------------------------------------------------------\n\n/** A label tuple recorded for re-tag-on-restore. Mirrors\n * `ContainerLabelTuple` from `contracts/snapshotable.ts` so the\n * metadata file holds the same shape the runtime adapter filters on. */\nexport const CapturedContainerSchema = Schema.Struct({\n\tplugin: Schema.String,\n\trole: Schema.String,\n\t/** Original image name the supervisor used when last creating this\n\t * container — must be re-tagged on restore so the supervisor's\n\t * reuse-if-name-and-image-match probe adopts the restored image. */\n\timageName: Schema.String,\n\t/** Temporary tag assigned to this committed container image inside\n\t * the Docker save bundle. Restore loads the bundle once, then\n\t * re-tags this source to `imageName`. */\n\tsnapshotTag: Schema.String,\n\t/** Sub-path of the committed image bundle inside the artifact. */\n\ttarPath: Schema.String,\n});\nexport type CapturedContainer = Schema.Schema.Type<typeof CapturedContainerSchema>;\n\n/** Captured host-tree subtree entry — preserved for missing-tolerance\n * classification on restore. */\nexport const CapturedSubtreeSchema = Schema.Struct({\n\tplugin: Schema.String,\n\t/** Subtree path relative to the runtime root (i.e. `<plugin>/...`). */\n\trelPath: Schema.String,\n\t/** `fatal` = restore must refuse if absent on disk at restore time;\n\t * `fine` = silently skipped. Mirrors `SnapshotableDecl.missingTolerance`. */\n\tmissingTolerance: Schema.Literals(['fatal', 'fine']),\n\t/** Did the subtree carry secret material at capture time? Drives\n\t * the 0o600/0o700 mode round-trip discipline. */\n\tsecretMaterial: Schema.Boolean,\n});\nexport type CapturedSubtree = Schema.Schema.Type<typeof CapturedSubtreeSchema>;\n\n/** Artifact-cache namespaces (`<stackRoot>/cache/<ns>/`) whose cached payload\n * is an on-chain deploy/mint identity — package id, walrus system/staking\n * objects, seal key-server object, deepbook pool ids, coin treasury. These\n * must come back after a restore so the post-restore boot REUSES the deploy\n * instead of re-running it with fresh ids (which orphans every pre-snapshot\n * object). Honored by BOTH halves so the contract holds across lifecycles:\n * the capture TARS `cache/<ns>` into the snapshot (self-contained — survives a\n * `wipe` that drops the live cache), and the restore also PRESERVES the live\n * `cache/<ns>` (the in-place `snapshot → restore` fast path, no tar). A\n * slash-prefixed plugin namespace (`seal/package`, `deepbook/pools`) nests\n * under its root, so the root entry covers all of that plugin's namespaces.\n * The generic per-call `cache/entry` is NOT here and stays dropped on restore. */\nexport const DEPLOY_CACHE_NAMESPACES: ReadonlyArray<string> = [\n\t'walrus-deploy',\n\t'package',\n\t'seal',\n\t'deepbook',\n\t'coin-mint',\n\t'action',\n];\n\n/** Identity slice that fires the cross-chain refusal guard. The\n * orchestrator threads contributions from participating plugins\n * through this map — `chain` is the canonical case but other plugins\n * may contribute (e.g. `postgres.majorVersion`). */\nexport const IdentitySliceSchema = Schema.Record(Schema.String, Schema.String);\nexport type IdentitySlice = Schema.Schema.Type<typeof IdentitySliceSchema>;\n\nexport const SNAPSHOT_CONTRIBUTION_VERSION = 1 as const;\n\n/** Plugin-owned JSON payload. The orchestrator validates only this\n * envelope and never interprets `value`; plugins that need stronger\n * guarantees own their schema at the plugin boundary. */\nexport const OpaqueContributionStateSchema = Schema.Struct({\n\tencoding: Schema.Literal('json'),\n\tvalue: Schema.Unknown,\n});\nexport type OpaqueContributionState = Schema.Schema.Type<typeof OpaqueContributionStateSchema>;\n\n/** Per-participant metadata envelope. The plugin payload is explicitly\n * opaque so a successful decode cannot be mistaken for validation of\n * plugin-owned state. */\nexport const ContributionDocSchema = versionedDocSchema(SNAPSHOT_CONTRIBUTION_VERSION, {\n\tplugin: Schema.String,\n\tidentity: Schema.optional(IdentitySliceSchema),\n\topaqueState: Schema.optional(OpaqueContributionStateSchema),\n});\nexport type ContributionDoc = Schema.Schema.Type<typeof ContributionDocSchema>;\n\n/** Schema version of the metadata record. Bumped when the on-disk\n * shape changes in a way that earlier readers cannot ignore. */\nexport const SNAPSHOT_META_VERSION = 3 as const;\n\n/** Top-level metadata record. Architecture § Snapshot — single canonical\n * metadata; \"metadata absent = do not trust this directory\". */\nexport const SnapshotMetadataSchema = versionedDocSchema(SNAPSHOT_META_VERSION, {\n\t/** Stable snapshot id (caller-supplied or substrate-minted). */\n\tid: Schema.String,\n\t/** User-facing name. It is never used as filesystem authority. */\n\tlabel: Schema.NullOr(Schema.String),\n\tcreatedAt: Schema.Number,\n\tapp: Schema.String,\n\tstack: Schema.String,\n\tnetwork: Schema.String,\n\t/** Whether `host-tree.tar` is present in the artifact (false for\n\t * the first-boot / empty-stack capture). */\n\thostTreeIncluded: Schema.Boolean,\n\tsubtrees: Schema.Array(CapturedSubtreeSchema),\n\tcontainers: Schema.Array(CapturedContainerSchema),\n\tidentity: IdentitySliceSchema,\n\t/** Plugin keys that contributed a contribution doc. Mirror is on\n\t * disk under `contributions/<encoded-plugin>.json`. */\n\tparticipants: Schema.Array(Schema.String),\n});\nexport type SnapshotMetadata = Schema.Schema.Type<typeof SnapshotMetadataSchema>;\n\n/** Integrity record — per-file SHA-256 over the artifact's contents.\n * Separate file so restore can re-verify after an atomic swap landed\n * without re-reading the (potentially large) tar. */\nexport const SNAPSHOT_INTEGRITY_VERSION = 1 as const;\n\nexport const IntegrityFileSchema = versionedDocSchema(SNAPSHOT_INTEGRITY_VERSION, {\n\t/** Relative-path → hex-encoded SHA-256 of the file's bytes. */\n\thashes: Schema.Record(Schema.String, Schema.String),\n});\nexport type IntegrityFile = Schema.Schema.Type<typeof IntegrityFileSchema>;\n\n// -----------------------------------------------------------------------------\n// Catalog-listing shape — projected by service.ts; partial / corrupt\n// entries collapse to `null` so the listing tolerates damage.\n// -----------------------------------------------------------------------------\n\nexport interface SnapshotCatalogEntry {\n\treadonly id: string;\n\treadonly directory: string;\n\treadonly metadata: SnapshotMetadata | null;\n}\n"],"mappings":";;;;AAwCA,MAAa,iBAAiB;CAC7B,UAAU;CACV,WAAW;CACX,aAAa;CACb,eAAe;CACf,kBAAkB;CAClB,eAAe;CACf;AAID,MAAM,sBAAsB;AAE5B,MAAa,mBACZ;;;;AAKD,IAAa,0BAAb,cAA6C,OAAO,kBAA2C,CAC9F,2BACA;CACC,MAAM,OAAO,SAAS,CAAC,cAAc,kBAAkB,CAAC;CACxD,QAAQ,OAAO;CACf,OAAO,OAAO;CACd,CACD,CAAC;AAEF,MAAa,qBAAqB,UACjC,oBAAoB,KAAK,MAAM,IAAI,0BAA0B,MAAM;AAEpE,MAAa,mBAAmB,UAC/B,kBAAkB,MAAM,GAAI,QAAuB;AAkBpD,MAAa,6BAA6B,YACzC,QAAQ,SAAS,KACjB,YAAY,OACZ,YAAY,QACZ,CAAC,QAAQ,SAAS,IAAI,IACtB,CAAC,QAAQ,SAAS,KAAK,IACvB,CAAC,QAAQ,SAAS,KAAK;AAExB,MAAa,8BAA8B,YAC1C,YAAY,MACZ,YAAY,OACZ,CAAC,QAAQ,WAAW,IAAI,IACxB,CAAC,QAAQ,SAAS,KAAK,IACvB,CAAC,QAAQ,SAAS,KAAK,IACvB,CAAC,QAAQ,MAAM,SAAS,CAAC,SAAS,KAAK;AAExC,MAAa,kCAAkC,cAC9C,UAAU,SAAS,KACnB,UAAU,MAAM,KAAK,aACrB,CAAC,KAAK,KAAK,UAAU,IACrB,CAAC,UAAU,SAAS,IAAI,IACxB,CAAC,UAAU,WAAW,UAAU;AAajC,MAAM,8BAA8B,UAA0B;CAC7D,IAAI,MAAM;AACV,MAAK,MAAM,QAAQ,IAAI,aAAa,CAAC,OAAO,MAAM,CACjD,QAAO,KAAK,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI;AAE1C,QAAO,KAAK;;;AAWb,MAAa,kCAA0C,GAAG,eAAe,cAAc;;AAGvF,MAAa,oBAAoB,WAChC,GAAG,eAAe,iBAAiB,GAAG,2BAA2B,OAAO,CAAC;;;;AAS1E,MAAa,0BAA0B,OAAO,OAAO;CACpD,QAAQ,OAAO;CACf,MAAM,OAAO;;;;CAIb,WAAW,OAAO;;;;CAIlB,aAAa,OAAO;;CAEpB,SAAS,OAAO;CAChB,CAAC;;;AAKF,MAAa,wBAAwB,OAAO,OAAO;CAClD,QAAQ,OAAO;;CAEf,SAAS,OAAO;;;CAGhB,kBAAkB,OAAO,SAAS,CAAC,SAAS,OAAO,CAAC;;;CAGpD,gBAAgB,OAAO;CACvB,CAAC;;;;;;;;;;;;;AAeF,MAAa,0BAAiD;CAC7D;CACA;CACA;CACA;CACA;CACA;CACA;;;;;AAMD,MAAa,sBAAsB,OAAO,OAAO,OAAO,QAAQ,OAAO,OAAO;;;;AAQ9E,MAAa,gCAAgC,OAAO,OAAO;CAC1D,UAAU,OAAO,QAAQ,OAAO;CAChC,OAAO,OAAO;CACd,CAAC;;;;AAMF,MAAa,wBAAwB,mBAAA,GAAkD;CACtF,QAAQ,OAAO;CACf,UAAU,OAAO,SAAS,oBAAoB;CAC9C,aAAa,OAAO,SAAS,8BAA8B;CAC3D,CAAC;;;AASF,MAAa,yBAAyB,mBAAA,GAA0C;;CAE/E,IAAI,OAAO;;CAEX,OAAO,OAAO,OAAO,OAAO,OAAO;CACnC,WAAW,OAAO;CAClB,KAAK,OAAO;CACZ,OAAO,OAAO;CACd,SAAS,OAAO;;;CAGhB,kBAAkB,OAAO;CACzB,UAAU,OAAO,MAAM,sBAAsB;CAC7C,YAAY,OAAO,MAAM,wBAAwB;CACjD,UAAU;;;CAGV,cAAc,OAAO,MAAM,OAAO,OAAO;CACzC,CAAC;AAQF,MAAa,sBAAsB,mBAAA,GAA+C;;AAEjF,QAAQ,OAAO,OAAO,OAAO,QAAQ,OAAO,OAAO,EACnD,CAAC"}
1
+ {"version":3,"file":"descriptor.mjs","names":[],"sources":["../../../src/orchestrators/snapshot/descriptor.ts"],"sourcesContent":["// Snapshot artifact descriptor + metadata schema.\n//\n// Architecture § Snapshot:\n// \"A tar of declared filesystem subtrees, committed container images\n// (one per managed container identified by label tuples), a typed\n// metadata slice (identity-guard contributions + plugin's structured\n// state).\"\n//\n// On-disk layout of one snapshot artifact (under\n// `<stackRoot>/snapshots/<snapshotId>/`):\n//\n// meta.json — versioned metadata record, schema-decoded\n// host-tree.tar — tar of subtrees declared by participants\n// containers/images.tar — deduplicated Docker save bundle for all\n// committed managed-container images\n// contributions/<encoded-plugin>.json\n// — one typed metadata slice per participant\n// integrity.json — hashes over the above for resume / probe\n//\n// Layout discipline:\n// - `meta.json` is authoritative. Architecture § Snapshotable\n// \"Metadata is authoritative\": if it's absent or unparseable,\n// restore refuses (no silent partial-restore downgrade).\n// - Anything else missing surfaces as a phase-tagged\n// `SnapshotPartialError` and aborts before any destructive mutation.\n// - The artifact directory itself becomes \"complete\" only via the\n// stage-and-swap atomic rename — readers never observe a\n// half-written tree.\n\nimport { Effect, Schema } from 'effect';\n\nimport type { Brand } from '../../substrate/brand.ts';\nimport {\n\tcomputeGraphInputIdentity,\n\tcomputeStackGraphInputIdentity,\n\ttype GraphInputIdentity,\n\ttype StackGraphInputIdentityError,\n\ttype StackGraphInputSource,\n} from '../../substrate/runtime/lifecycle/graph-input-id.ts';\nimport type { DevstackOptions } from '../../substrate/options.ts';\nimport type { ResolvedGraph } from '../../substrate/runtime/lifecycle/dep-graph.ts';\nimport { versionedDocSchema } from '../../substrate/versioned-doc-schema.ts';\n\n// -----------------------------------------------------------------------------\n// File layout — string constants the capture / restore code reaches for\n// -----------------------------------------------------------------------------\n\n/** Canonical file / directory names inside one snapshot artifact. */\nexport const SnapshotLayout = {\n\tmetaFile: 'meta.json',\n\thostTreeTar: 'host-tree.tar',\n\tcontainersDir: 'containers',\n\tcontributionsDir: 'contributions',\n\tintegrityFile: 'integrity.json',\n} as const;\n\nexport type SnapshotId = Brand<string, 'SnapshotId'>;\n\nconst SNAPSHOT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;\n\nexport const SNAPSHOT_ID_RULE =\n\t'snapshot ids must be 1-128 characters matching [A-Za-z0-9][A-Za-z0-9_-]*';\n\n/** Tagged failure for descriptor-layer validation. Carries a discriminator\n * so downstream phase classifiers can `catchTag` and dispatch on `kind`\n * without sniffing the message. STYLE_GUIDE §2 rule 5. */\nexport class SnapshotDescriptorError extends Schema.TaggedErrorClass<SnapshotDescriptorError>()(\n\t'SnapshotDescriptorError',\n\t{\n\t\tkind: Schema.Literals(['invalid-id', 'invalid-segment']),\n\t\tdetail: Schema.String,\n\t\tvalue: Schema.String,\n\t},\n) {}\n\nexport const isValidSnapshotId = (value: string): boolean =>\n\tSNAPSHOT_ID_PATTERN.test(value) && isSafeSnapshotPathSegment(value);\n\nexport const parseSnapshotId = (value: string): SnapshotId | null =>\n\tisValidSnapshotId(value) ? (value as SnapshotId) : null;\n\n/** Brand-cast helper for known-valid id literals (primarily test\n * fixtures). Throws a typed `SnapshotDescriptorError` instance on\n * violation so any defect surfacing carries the canonical `_tag`\n * instead of a bare `Error` string-match. */\nexport const snapshotIdFromString = (value: string): SnapshotId => {\n\tconst parsed = parseSnapshotId(value);\n\tif (parsed === null) {\n\t\tthrow new SnapshotDescriptorError({\n\t\t\tkind: 'invalid-id',\n\t\t\tdetail: SNAPSHOT_ID_RULE,\n\t\t\tvalue,\n\t\t});\n\t}\n\treturn parsed;\n};\n\nexport const isSafeSnapshotPathSegment = (segment: string): boolean =>\n\tsegment.length > 0 &&\n\tsegment !== '.' &&\n\tsegment !== '..' &&\n\t!segment.includes('/') &&\n\t!segment.includes('\\\\') &&\n\t!segment.includes('\\0');\n\nexport const isSafeSnapshotRelativePath = (relPath: string): boolean =>\n\trelPath !== '' &&\n\trelPath !== '.' &&\n\t!relPath.startsWith('/') &&\n\t!relPath.includes('\\\\') &&\n\t!relPath.includes('\\0') &&\n\t!relPath.split(/[\\\\/]+/).includes('..');\n\nexport const isRestorableContainerImageName = (imageName: string): boolean =>\n\timageName.length > 0 &&\n\timageName.trim() === imageName &&\n\t!/\\s/.test(imageName) &&\n\t!imageName.includes('@') &&\n\t!imageName.startsWith('sha256:');\n\nconst encodedSnapshotPathSegment = (value: string): string => {\n\tlet hex = '';\n\tfor (const byte of new TextEncoder().encode(value)) {\n\t\thex += byte.toString(16).padStart(2, '0');\n\t}\n\treturn `p-${hex}`;\n};\n\n/** Canonical sub-path for the deduplicated managed-container image bundle. */\nexport const containerImagesBundlePath = (): string => `${SnapshotLayout.containersDir}/images.tar`;\n\n/** Canonical sub-path for one participant's typed metadata slice. */\nexport const contributionPath = (plugin: string): string =>\n\t`${SnapshotLayout.contributionsDir}/${encodedSnapshotPathSegment(plugin)}.json`;\n\n// -----------------------------------------------------------------------------\n// Metadata schemas — versioned record\n// -----------------------------------------------------------------------------\n\n/** A label tuple recorded for re-tag-on-restore. Mirrors\n * `ContainerLabelTuple` from `contracts/snapshotable.ts` so the\n * metadata file holds the same shape the runtime adapter filters on. */\nexport const CapturedContainerSchema = Schema.Struct({\n\tplugin: Schema.String,\n\trole: Schema.String,\n\t/** Original image name the supervisor used when last creating this\n\t * container — must be re-tagged on restore so the supervisor's\n\t * reuse-if-name-and-image-match probe adopts the restored image. */\n\timageName: Schema.String,\n\t/** Temporary tag assigned to this committed container image inside\n\t * the Docker save bundle. Restore loads the bundle once, then\n\t * re-tags this source to `imageName`. */\n\tsnapshotTag: Schema.String,\n\t/** Sub-path of the committed image bundle inside the artifact. */\n\ttarPath: Schema.String,\n});\nexport type CapturedContainer = Schema.Schema.Type<typeof CapturedContainerSchema>;\n\n/** Captured host-tree subtree entry — preserved for missing-tolerance\n * classification on restore. */\nexport const CapturedSubtreeSchema = Schema.Struct({\n\tplugin: Schema.String,\n\t/** Subtree path relative to the runtime root (i.e. `<plugin>/...`). */\n\trelPath: Schema.String,\n\t/** `fatal` = restore must refuse if absent on disk at restore time;\n\t * `fine` = silently skipped. Mirrors `SnapshotableDecl.missingTolerance`. */\n\tmissingTolerance: Schema.Literals(['fatal', 'fine']),\n\t/** Did the subtree carry secret material at capture time? Drives\n\t * the 0o600/0o700 mode round-trip discipline. */\n\tsecretMaterial: Schema.Boolean,\n});\nexport type CapturedSubtree = Schema.Schema.Type<typeof CapturedSubtreeSchema>;\n\n/** Artifact-cache namespaces (`<stackRoot>/cache/<ns>/`) whose cached payload\n * is an on-chain deploy/mint identity — package id, walrus system/staking\n * objects, seal key-server object, deepbook pool ids, coin treasury. These\n * must come back after a restore so the post-restore boot REUSES the deploy\n * instead of re-running it with fresh ids (which orphans every pre-snapshot\n * object).\n *\n * The contract is SELF-CONTAINED: capture TARs `cache/<ns>` into the\n * snapshot's host-tree (see `deployCacheSubtreeRelPaths`), and restore\n * untars that copy and DROPS the deploy-cache from the stage-and-swap\n * preserve list — the snapshot's cache is the SOLE source on restore\n * (no preserve-from-live, so no double-store drift). This is what makes a\n * CROSS-MACHINE restore work: a fresh runner with an empty live cache\n * recovers the ids from the snapshot itself. (Earlier the live cache was\n * the sole source, which only works same-machine.) A slash-prefixed plugin\n * namespace (`seal/package`, `deepbook/pools`) nests under its root, so the\n * root entry covers all of that plugin's namespaces. The generic per-call\n * `cache/entry` is NOT here and stays dropped on restore. */\nexport const DEPLOY_CACHE_NAMESPACES: ReadonlyArray<string> = [\n\t'walrus-deploy',\n\t'package',\n\t'seal',\n\t'deepbook',\n\t'coin-mint',\n\t'action',\n];\n\n/** Host-tree subtree relPaths (relative to the stack root) for the deploy-cache\n * namespaces. Capture tars these into `host-tree.tar` so the snapshot is\n * self-contained; restore untars them into the swapped tree. The on-disk cache\n * lives at `<stackRoot>/cache/<ns>/<chain>/<contentHash>.json`, so the root\n * `cache/<ns>` entry covers every nested namespace + chain under it. */\nexport const deployCacheSubtreeRelPaths = (cacheDirName: string): ReadonlyArray<string> =>\n\tDEPLOY_CACHE_NAMESPACES.map((namespace) => `${cacheDirName}/${namespace}`);\n\n/** Identity slice that fires the cross-chain refusal guard. The\n * orchestrator threads contributions from participating plugins\n * through this map — `chain` is the canonical case but other plugins\n * may contribute (e.g. `postgres.majorVersion`). */\nexport const IdentitySliceSchema = Schema.Record(Schema.String, Schema.String);\nexport type IdentitySlice = Schema.Schema.Type<typeof IdentitySliceSchema>;\n\nexport const SNAPSHOT_CONTRIBUTION_VERSION = 1 as const;\n\n/** Plugin-owned JSON payload. The orchestrator validates only this\n * envelope and never interprets `value`; plugins that need stronger\n * guarantees own their schema at the plugin boundary. */\nexport const OpaqueContributionStateSchema = Schema.Struct({\n\tencoding: Schema.Literal('json'),\n\tvalue: Schema.Unknown,\n});\n\n/** Per-participant metadata envelope. The plugin payload is explicitly\n * opaque so a successful decode cannot be mistaken for validation of\n * plugin-owned state. */\nexport const ContributionDocSchema = versionedDocSchema(SNAPSHOT_CONTRIBUTION_VERSION, {\n\tplugin: Schema.String,\n\tidentity: Schema.optional(IdentitySliceSchema),\n\topaqueState: Schema.optional(OpaqueContributionStateSchema),\n});\nexport type ContributionDoc = Schema.Schema.Type<typeof ContributionDocSchema>;\n\n/** Schema version of the metadata record. Bumped when the on-disk\n * shape changes in a way that earlier readers cannot ignore. */\nexport const SNAPSHOT_GRAPH_INPUT_VERSION = 1 as const;\n\nexport const SnapshotNodeInputIdentitySchema = Schema.Struct({\n\tkey: Schema.String,\n\tinputId: Schema.String,\n\tupstreamInputIds: Schema.Array(\n\t\tSchema.Struct({\n\t\t\tkey: Schema.String,\n\t\t\tinputId: Schema.String,\n\t\t}),\n\t),\n});\nexport type SnapshotNodeInputIdentity = Schema.Schema.Type<typeof SnapshotNodeInputIdentitySchema>;\n\nexport const SnapshotGraphInputIdentitySchema = versionedDocSchema(SNAPSHOT_GRAPH_INPUT_VERSION, {\n\tgraphInputId: Schema.String,\n\tnodes: Schema.Array(SnapshotNodeInputIdentitySchema),\n});\nexport type SnapshotGraphInputIdentity = Schema.Schema.Type<\n\ttypeof SnapshotGraphInputIdentitySchema\n>;\n\nexport const snapshotGraphInputFromIdentity = (\n\tidentity: GraphInputIdentity,\n): SnapshotGraphInputIdentity => ({\n\tversion: SNAPSHOT_GRAPH_INPUT_VERSION,\n\tgraphInputId: identity.graphInputId,\n\tnodes: identity.nodes.map((node) => ({\n\t\tkey: node.key,\n\t\tinputId: node.inputId,\n\t\tupstreamInputIds: node.upstreamInputIds.map((upstream) => ({\n\t\t\tkey: upstream.key,\n\t\t\tinputId: upstream.inputId,\n\t\t})),\n\t})),\n});\n\nexport const computeSnapshotGraphInputFromGraph = (args: {\n\treadonly graph: ResolvedGraph;\n\treadonly options: DevstackOptions;\n\treadonly devstackVersion: string;\n}): Effect.Effect<SnapshotGraphInputIdentity, StackGraphInputIdentityError> =>\n\tcomputeGraphInputIdentity(args).pipe(Effect.map(snapshotGraphInputFromIdentity));\n\nexport const computeSnapshotGraphInputFromStack = (args: {\n\treadonly stack: StackGraphInputSource;\n\treadonly devstackVersion: string;\n}): Effect.Effect<SnapshotGraphInputIdentity, StackGraphInputIdentityError> =>\n\tcomputeStackGraphInputIdentity(args).pipe(Effect.map(snapshotGraphInputFromIdentity));\n\n/** Schema version of the metadata record. Bumped when the on-disk\n * shape changes in a way that earlier readers cannot ignore. */\nexport const SNAPSHOT_META_VERSION = 4 as const;\n\n/** Top-level metadata record. Architecture § Snapshot — single canonical\n * metadata; \"metadata absent = do not trust this directory\". */\nexport const SnapshotMetadataSchema = versionedDocSchema(SNAPSHOT_META_VERSION, {\n\t/** Stable snapshot id (caller-supplied or substrate-minted). */\n\tid: Schema.String,\n\t/** User-facing name. It is never used as filesystem authority. */\n\tlabel: Schema.NullOr(Schema.String),\n\tcreatedAt: Schema.Number,\n\tapp: Schema.String,\n\tstack: Schema.String,\n\tnetwork: Schema.String,\n\t/** Desired-input identity of the resolved stack graph at capture time.\n\t * Restore/startup policy compares this with the current graph before\n\t * deciding whether a snapshot is valid for the current inputs. */\n\tgraphInput: SnapshotGraphInputIdentitySchema,\n\t/** Whether `host-tree.tar` is present in the artifact (false for\n\t * the first-boot / empty-stack capture). */\n\thostTreeIncluded: Schema.Boolean,\n\tsubtrees: Schema.Array(CapturedSubtreeSchema),\n\tcontainers: Schema.Array(CapturedContainerSchema),\n\tidentity: IdentitySliceSchema,\n\t/** Plugin keys that contributed a contribution doc. Mirror is on\n\t * disk under `contributions/<encoded-plugin>.json`. */\n\tparticipants: Schema.Array(Schema.String),\n});\nexport type SnapshotMetadata = Schema.Schema.Type<typeof SnapshotMetadataSchema>;\n\n/** Integrity record — per-file SHA-256 over the artifact's contents.\n * Separate file so restore can re-verify after an atomic swap landed\n * without re-reading the (potentially large) tar. */\nexport const SNAPSHOT_INTEGRITY_VERSION = 1 as const;\n\nexport const IntegrityFileSchema = versionedDocSchema(SNAPSHOT_INTEGRITY_VERSION, {\n\t/** Relative-path → hex-encoded SHA-256 of the file's bytes. */\n\thashes: Schema.Record(Schema.String, Schema.String),\n});\nexport type IntegrityFile = Schema.Schema.Type<typeof IntegrityFileSchema>;\n\n// -----------------------------------------------------------------------------\n// Catalog-listing shape — projected by service.ts; partial / corrupt\n// entries collapse to `null` so the listing tolerates damage.\n// -----------------------------------------------------------------------------\n\nexport interface SnapshotCatalogEntry {\n\treadonly id: string;\n\treadonly directory: string;\n\treadonly metadata: SnapshotMetadata | null;\n}\n"],"mappings":"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"}
@@ -90,7 +90,6 @@ const requireIdentity = (identity, source) => Object.keys(identity).length === 0
90
90
  * return there is no I/O effect to roll back.
91
91
  */
92
92
  const runIdentityGuard = (snapshotIdentity, liveIdentity) => Effect.gen(function* () {
93
- yield* Effect.annotateCurrentSpan({ "devstack.snapshot.phase": "identity-guard" });
94
93
  const snapshotKeys = Object.keys(snapshotIdentity);
95
94
  const liveKeys = Object.keys(liveIdentity);
96
95
  yield* requireIdentity(snapshotIdentity, "snapshot");
@@ -112,9 +111,8 @@ const runIdentityGuard = (snapshotIdentity, liveIdentity) => Effect.gen(function
112
111
  key,
113
112
  liveValue: liveIdentity[key]
114
113
  }));
115
- }).pipe(Effect.withSpan("orchestrator.snapshot.identity-guard"));
114
+ });
116
115
  const runRuntimeIdentityGuard = (snapshotIdentity, liveIdentity) => Effect.gen(function* () {
117
- yield* Effect.annotateCurrentSpan({ "devstack.snapshot.phase": "identity-guard" });
118
116
  for (const key of [
119
117
  "app",
120
118
  "stack",
@@ -125,8 +123,8 @@ const runRuntimeIdentityGuard = (snapshotIdentity, liveIdentity) => Effect.gen(f
125
123
  snapshotValue: snapshotIdentity[key],
126
124
  liveValue: liveIdentity[key]
127
125
  }));
128
- }).pipe(Effect.withSpan("orchestrator.snapshot.runtime-identity-guard"));
126
+ });
129
127
  //#endregion
130
- export { IdentityContributionConflictError, IdentityEmptyError, IdentityMismatchError, IdentityMissingLiveError, IdentityMissingSnapshotError, mergeContributions, requireIdentity, runIdentityGuard, runRuntimeIdentityGuard };
128
+ export { IdentityContributionConflictError, IdentityEmptyError, IdentityMismatchError, mergeContributions, requireIdentity, runIdentityGuard, runRuntimeIdentityGuard };
131
129
 
132
130
  //# sourceMappingURL=identity-guard.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity-guard.mjs","names":[],"sources":["../../../src/orchestrators/snapshot/identity-guard.ts"],"sourcesContent":["// Identity guard — fail-closed cross-chain refusal.\n//\n// Architecture § Snapshot:\n// \"Identity guard. `(app, stack, network)` plus chain identity plus\n// optional plugin contributions. Fires before any destructive\n// mutation.\"\n//\n// Architecture § Invariants:\n// \"Cross-chain refusal. When both the snapshot and the live stack\n// carry a known chain identity and they disagree, restore must\n// refuse before any destructive mutation.\"\n//\n// Composition shape: the orchestrator collects an `IdentitySlice`\n// from EACH participating plugin's `preRestore` contribution. The\n// final guard compares the snapshot's stored slice against the live\n// slice key-by-key. A disagreement on ANY key refuses. A key set on\n// one side but not the other is decided by the fail-closed policy:\n// the orchestrator's policy is fail-closed-when-snapshot-set (open\n// question §5 resolution): if the snapshot recorded `chain=X` and the\n// caller did not pass `chain`, the guard refuses with `missing-live`.\n\nimport { Effect, Schema } from 'effect';\n\nimport type { IdentitySlice } from './descriptor.ts';\n\nexport interface SnapshotRuntimeIdentity {\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly network: string;\n}\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\n\n/** Tagged failure: chain identity (or another contributed key) disagrees\n * between the snapshot and the live stack. No mutation occurred. */\nexport class IdentityMismatchError extends Schema.TaggedErrorClass<IdentityMismatchError>()(\n\t'SnapshotIdentityMismatch',\n\t{\n\t\tphase: Schema.Literal('identity-guard'),\n\t\tkey: Schema.String,\n\t\tsnapshotValue: Schema.String,\n\t\tliveValue: Schema.String,\n\t},\n) {}\n\n/** Tagged failure: snapshot recorded a key that the live stack did\n * not contribute. Fail-closed (architecture open question §5\n * resolution); the alternative is silently restoring across an\n * unverified seam. */\nexport class IdentityMissingLiveError extends Schema.TaggedErrorClass<IdentityMissingLiveError>()(\n\t'SnapshotIdentityMissingLive',\n\t{\n\t\tphase: Schema.Literal('identity-guard'),\n\t\tkey: Schema.String,\n\t\tsnapshotValue: Schema.String,\n\t},\n) {}\n\n/** Tagged failure: live stack contributed a key the snapshot did NOT\n * record. Fail-closed for the same reason as above — restoring an\n * older snapshot that pre-dates the contribution is a code-version\n * mismatch, not a happy path. */\nexport class IdentityMissingSnapshotError extends Schema.TaggedErrorClass<IdentityMissingSnapshotError>()(\n\t'SnapshotIdentityMissingSnapshot',\n\t{\n\t\tphase: Schema.Literal('identity-guard'),\n\t\tkey: Schema.String,\n\t\tliveValue: Schema.String,\n\t},\n) {}\n\nexport class IdentityEmptyError extends Schema.TaggedErrorClass<IdentityEmptyError>()(\n\t'SnapshotIdentityEmpty',\n\t{\n\t\tphase: Schema.Literal('identity-guard'),\n\t\tsource: Schema.Literals(['snapshot', 'live']),\n\t},\n) {}\n\nexport type IdentityGuardError =\n\t| IdentityMismatchError\n\t| IdentityMissingLiveError\n\t| IdentityMissingSnapshotError\n\t| IdentityEmptyError;\n\n// -----------------------------------------------------------------------------\n// Composition — many plugins, one merged slice\n// -----------------------------------------------------------------------------\n\n/** One participant's contribution to the identity slice — produced by\n * the plugin's `preRestore` hook, schema-decoded by the orchestrator. */\nexport interface IdentityContribution {\n\treadonly plugin: string;\n\treadonly slice: IdentitySlice;\n}\n\n/** Tagged failure: two plugins contributed the same key with different\n * values. The orchestrator surfaces this BEFORE running the guard;\n * same-key contributions from independent plugins should agree (the\n * same chain identity contributed by sui and a probe-renderer, say). */\nexport class IdentityContributionConflictError extends Schema.TaggedErrorClass<IdentityContributionConflictError>()(\n\t'SnapshotIdentityContributionConflict',\n\t{\n\t\tphase: Schema.Literal('identity-guard'),\n\t\tkey: Schema.String,\n\t\tconflictingPlugins: Schema.Array(Schema.String),\n\t\tvalues: Schema.Array(Schema.String),\n\t},\n) {}\n\n/**\n * Merge a list of plugin contributions into one slice. Conflict\n * (same key, different values across plugins) surfaces as a tagged\n * error — the orchestrator refuses to proceed rather than picking a\n * \"winner\" silently.\n *\n * Architecture § Snapshotable: identity contributions stack — chain\n * is the canonical case but plugins are free to contribute their own\n * (e.g. postgres major version, sui fork checkpoint).\n */\nexport const mergeContributions = (\n\tcontributions: ReadonlyArray<IdentityContribution>,\n): Effect.Effect<IdentitySlice, IdentityContributionConflictError> =>\n\tEffect.gen(function* () {\n\t\t// Group by key so the conflict surface lists every offender.\n\t\tconst seen: Record<string, ReadonlyArray<{ plugin: string; value: string }>> = {};\n\t\tfor (const { plugin, slice } of contributions) {\n\t\t\tfor (const [key, value] of Object.entries(slice)) {\n\t\t\t\tconst prior = seen[key] ?? [];\n\t\t\t\tseen[key] = [...prior, { plugin, value }];\n\t\t\t}\n\t\t}\n\t\tconst merged: Record<string, string> = {};\n\t\tfor (const [key, entries] of Object.entries(seen)) {\n\t\t\tconst distinct = new Set(entries.map((e) => e.value));\n\t\t\tif (distinct.size > 1) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew IdentityContributionConflictError({\n\t\t\t\t\t\tphase: 'identity-guard',\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tconflictingPlugins: entries.map((e) => e.plugin),\n\t\t\t\t\t\tvalues: [...distinct],\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\t// `distinct.size === 1` guaranteed (every entry contributes one value).\n\t\t\tmerged[key] = entries[0]!.value;\n\t\t}\n\t\treturn merged;\n\t});\n\n// -----------------------------------------------------------------------------\n// The guard itself\n// -----------------------------------------------------------------------------\n\nexport const requireIdentity = (\n\tidentity: IdentitySlice,\n\tsource: IdentityEmptyError['source'],\n): Effect.Effect<void, IdentityEmptyError> =>\n\tObject.keys(identity).length === 0\n\t\t? Effect.fail(new IdentityEmptyError({ phase: 'identity-guard', source }))\n\t\t: Effect.void;\n\n/**\n * Compare the snapshot's recorded identity against the live stack's\n * contributed identity. Fail-closed on every shape:\n *\n * - snapshot identity is empty → IdentityEmptyError\n * - keys present on both sides, values differ → IdentityMismatchError\n * - keys present only in the snapshot → IdentityMissingLiveError\n * - keys present only in the live stack → IdentityMissingSnapshotError\n *\n * The architecture's open-question §5 (\"require both sides of the\n * comparison rather than fail-open on either-undefined\") resolves to\n * fail-closed; this is the implementation.\n *\n * The guard runs BEFORE any destructive mutation. On a successful\n * return there is no I/O effect to roll back.\n */\nexport const runIdentityGuard = (\n\tsnapshotIdentity: IdentitySlice,\n\tliveIdentity: IdentitySlice,\n): Effect.Effect<void, IdentityGuardError> =>\n\tEffect.gen(function* () {\n\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t'devstack.snapshot.phase': 'identity-guard',\n\t\t});\n\t\tconst snapshotKeys = Object.keys(snapshotIdentity);\n\t\tconst liveKeys = Object.keys(liveIdentity);\n\t\tyield* requireIdentity(snapshotIdentity, 'snapshot');\n\t\t// 1. Keys present on both sides — disagreement is the canonical refuse.\n\t\tfor (const key of snapshotKeys) {\n\t\t\tif (key in liveIdentity) {\n\t\t\t\tif (snapshotIdentity[key] !== liveIdentity[key]) {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\tnew IdentityMismatchError({\n\t\t\t\t\t\t\tphase: 'identity-guard',\n\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\tsnapshotValue: snapshotIdentity[key]!,\n\t\t\t\t\t\t\tliveValue: liveIdentity[key]!,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// 2. Snapshot recorded a key live did not contribute. Fail-closed.\n\t\tfor (const key of snapshotKeys) {\n\t\t\tif (!(key in liveIdentity)) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew IdentityMissingLiveError({\n\t\t\t\t\t\tphase: 'identity-guard',\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tsnapshotValue: snapshotIdentity[key]!,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\t// 3. Live contributed a key the snapshot did not record. Fail-closed.\n\t\tfor (const key of liveKeys) {\n\t\t\tif (!(key in snapshotIdentity)) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew IdentityMissingSnapshotError({\n\t\t\t\t\t\tphase: 'identity-guard',\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tliveValue: liveIdentity[key]!,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}).pipe(Effect.withSpan('orchestrator.snapshot.identity-guard'));\n\nexport const runRuntimeIdentityGuard = (\n\tsnapshotIdentity: SnapshotRuntimeIdentity,\n\tliveIdentity: SnapshotRuntimeIdentity,\n): Effect.Effect<void, IdentityMismatchError> =>\n\tEffect.gen(function* () {\n\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t'devstack.snapshot.phase': 'identity-guard',\n\t\t});\n\t\tconst keys: ReadonlyArray<keyof SnapshotRuntimeIdentity> = ['app', 'stack', 'network'];\n\t\tfor (const key of keys) {\n\t\t\tif (snapshotIdentity[key] !== liveIdentity[key]) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew IdentityMismatchError({\n\t\t\t\t\t\tphase: 'identity-guard',\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tsnapshotValue: snapshotIdentity[key],\n\t\t\t\t\t\tliveValue: liveIdentity[key],\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}).pipe(Effect.withSpan('orchestrator.snapshot.runtime-identity-guard'));\n"],"mappings":"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"}
1
+ {"version":3,"file":"identity-guard.mjs","names":[],"sources":["../../../src/orchestrators/snapshot/identity-guard.ts"],"sourcesContent":["// Identity guard — fail-closed cross-chain refusal.\n//\n// Architecture § Snapshot:\n// \"Identity guard. `(app, stack, network)` plus chain identity plus\n// optional plugin contributions. Fires before any destructive\n// mutation.\"\n//\n// Architecture § Invariants:\n// \"Cross-chain refusal. When both the snapshot and the live stack\n// carry a known chain identity and they disagree, restore must\n// refuse before any destructive mutation.\"\n//\n// Composition shape: the orchestrator collects an `IdentitySlice`\n// from EACH participating plugin's `preRestore` contribution. The\n// final guard compares the snapshot's stored slice against the live\n// slice key-by-key. A disagreement on ANY key refuses. A key set on\n// one side but not the other is decided by the fail-closed policy:\n// the orchestrator's policy is fail-closed-when-snapshot-set (open\n// question §5 resolution): if the snapshot recorded `chain=X` and the\n// caller did not pass `chain`, the guard refuses with `missing-live`.\n\nimport { Effect, Schema } from 'effect';\n\nimport type { IdentitySlice } from './descriptor.ts';\n\nexport interface SnapshotRuntimeIdentity {\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly network: string;\n}\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\n\n/** Tagged failure: chain identity (or another contributed key) disagrees\n * between the snapshot and the live stack. No mutation occurred. */\nexport class IdentityMismatchError extends Schema.TaggedErrorClass<IdentityMismatchError>()(\n\t'SnapshotIdentityMismatch',\n\t{\n\t\tphase: Schema.Literal('identity-guard'),\n\t\tkey: Schema.String,\n\t\tsnapshotValue: Schema.String,\n\t\tliveValue: Schema.String,\n\t},\n) {}\n\n/** Tagged failure: snapshot recorded a key that the live stack did\n * not contribute. Fail-closed (architecture open question §5\n * resolution); the alternative is silently restoring across an\n * unverified seam. */\nexport class IdentityMissingLiveError extends Schema.TaggedErrorClass<IdentityMissingLiveError>()(\n\t'SnapshotIdentityMissingLive',\n\t{\n\t\tphase: Schema.Literal('identity-guard'),\n\t\tkey: Schema.String,\n\t\tsnapshotValue: Schema.String,\n\t},\n) {}\n\n/** Tagged failure: live stack contributed a key the snapshot did NOT\n * record. Fail-closed for the same reason as above — restoring an\n * older snapshot that pre-dates the contribution is a code-version\n * mismatch, not a happy path. */\nexport class IdentityMissingSnapshotError extends Schema.TaggedErrorClass<IdentityMissingSnapshotError>()(\n\t'SnapshotIdentityMissingSnapshot',\n\t{\n\t\tphase: Schema.Literal('identity-guard'),\n\t\tkey: Schema.String,\n\t\tliveValue: Schema.String,\n\t},\n) {}\n\nexport class IdentityEmptyError extends Schema.TaggedErrorClass<IdentityEmptyError>()(\n\t'SnapshotIdentityEmpty',\n\t{\n\t\tphase: Schema.Literal('identity-guard'),\n\t\tsource: Schema.Literals(['snapshot', 'live']),\n\t},\n) {}\n\nexport type IdentityGuardError =\n\t| IdentityMismatchError\n\t| IdentityMissingLiveError\n\t| IdentityMissingSnapshotError\n\t| IdentityEmptyError;\n\n// -----------------------------------------------------------------------------\n// Composition — many plugins, one merged slice\n// -----------------------------------------------------------------------------\n\n/** One participant's contribution to the identity slice — produced by\n * the plugin's `preRestore` hook, schema-decoded by the orchestrator. */\nexport interface IdentityContribution {\n\treadonly plugin: string;\n\treadonly slice: IdentitySlice;\n}\n\n/** Tagged failure: two plugins contributed the same key with different\n * values. The orchestrator surfaces this BEFORE running the guard;\n * same-key contributions from independent plugins should agree (the\n * same chain identity contributed by sui and a probe-renderer, say). */\nexport class IdentityContributionConflictError extends Schema.TaggedErrorClass<IdentityContributionConflictError>()(\n\t'SnapshotIdentityContributionConflict',\n\t{\n\t\tphase: Schema.Literal('identity-guard'),\n\t\tkey: Schema.String,\n\t\tconflictingPlugins: Schema.Array(Schema.String),\n\t\tvalues: Schema.Array(Schema.String),\n\t},\n) {}\n\n/**\n * Merge a list of plugin contributions into one slice. Conflict\n * (same key, different values across plugins) surfaces as a tagged\n * error — the orchestrator refuses to proceed rather than picking a\n * \"winner\" silently.\n *\n * Architecture § Snapshotable: identity contributions stack — chain\n * is the canonical case but plugins are free to contribute their own\n * (e.g. postgres major version, sui fork checkpoint).\n */\nexport const mergeContributions = (\n\tcontributions: ReadonlyArray<IdentityContribution>,\n): Effect.Effect<IdentitySlice, IdentityContributionConflictError> =>\n\tEffect.gen(function* () {\n\t\t// Group by key so the conflict surface lists every offender.\n\t\tconst seen: Record<string, ReadonlyArray<{ plugin: string; value: string }>> = {};\n\t\tfor (const { plugin, slice } of contributions) {\n\t\t\tfor (const [key, value] of Object.entries(slice)) {\n\t\t\t\tconst prior = seen[key] ?? [];\n\t\t\t\tseen[key] = [...prior, { plugin, value }];\n\t\t\t}\n\t\t}\n\t\tconst merged: Record<string, string> = {};\n\t\tfor (const [key, entries] of Object.entries(seen)) {\n\t\t\tconst distinct = new Set(entries.map((e) => e.value));\n\t\t\tif (distinct.size > 1) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew IdentityContributionConflictError({\n\t\t\t\t\t\tphase: 'identity-guard',\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tconflictingPlugins: entries.map((e) => e.plugin),\n\t\t\t\t\t\tvalues: [...distinct],\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\t// `distinct.size === 1` guaranteed (every entry contributes one value).\n\t\t\tmerged[key] = entries[0]!.value;\n\t\t}\n\t\treturn merged;\n\t});\n\n// -----------------------------------------------------------------------------\n// The guard itself\n// -----------------------------------------------------------------------------\n\nexport const requireIdentity = (\n\tidentity: IdentitySlice,\n\tsource: IdentityEmptyError['source'],\n): Effect.Effect<void, IdentityEmptyError> =>\n\tObject.keys(identity).length === 0\n\t\t? Effect.fail(new IdentityEmptyError({ phase: 'identity-guard', source }))\n\t\t: Effect.void;\n\n/**\n * Compare the snapshot's recorded identity against the live stack's\n * contributed identity. Fail-closed on every shape:\n *\n * - snapshot identity is empty → IdentityEmptyError\n * - keys present on both sides, values differ → IdentityMismatchError\n * - keys present only in the snapshot → IdentityMissingLiveError\n * - keys present only in the live stack → IdentityMissingSnapshotError\n *\n * The architecture's open-question §5 (\"require both sides of the\n * comparison rather than fail-open on either-undefined\") resolves to\n * fail-closed; this is the implementation.\n *\n * The guard runs BEFORE any destructive mutation. On a successful\n * return there is no I/O effect to roll back.\n */\nexport const runIdentityGuard = (\n\tsnapshotIdentity: IdentitySlice,\n\tliveIdentity: IdentitySlice,\n): Effect.Effect<void, IdentityGuardError> =>\n\tEffect.gen(function* () {\n\t\tconst snapshotKeys = Object.keys(snapshotIdentity);\n\t\tconst liveKeys = Object.keys(liveIdentity);\n\t\tyield* requireIdentity(snapshotIdentity, 'snapshot');\n\t\t// 1. Keys present on both sides — disagreement is the canonical refuse.\n\t\tfor (const key of snapshotKeys) {\n\t\t\tif (key in liveIdentity) {\n\t\t\t\tif (snapshotIdentity[key] !== liveIdentity[key]) {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\tnew IdentityMismatchError({\n\t\t\t\t\t\t\tphase: 'identity-guard',\n\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\tsnapshotValue: snapshotIdentity[key]!,\n\t\t\t\t\t\t\tliveValue: liveIdentity[key]!,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// 2. Snapshot recorded a key live did not contribute. Fail-closed.\n\t\tfor (const key of snapshotKeys) {\n\t\t\tif (!(key in liveIdentity)) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew IdentityMissingLiveError({\n\t\t\t\t\t\tphase: 'identity-guard',\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tsnapshotValue: snapshotIdentity[key]!,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\t// 3. Live contributed a key the snapshot did not record. Fail-closed.\n\t\tfor (const key of liveKeys) {\n\t\t\tif (!(key in snapshotIdentity)) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew IdentityMissingSnapshotError({\n\t\t\t\t\t\tphase: 'identity-guard',\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tliveValue: liveIdentity[key]!,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t});\n\nexport const runRuntimeIdentityGuard = (\n\tsnapshotIdentity: SnapshotRuntimeIdentity,\n\tliveIdentity: SnapshotRuntimeIdentity,\n): Effect.Effect<void, IdentityMismatchError> =>\n\tEffect.gen(function* () {\n\t\tconst keys: ReadonlyArray<keyof SnapshotRuntimeIdentity> = ['app', 'stack', 'network'];\n\t\tfor (const key of keys) {\n\t\t\tif (snapshotIdentity[key] !== liveIdentity[key]) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew IdentityMismatchError({\n\t\t\t\t\t\tphase: 'identity-guard',\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tsnapshotValue: snapshotIdentity[key],\n\t\t\t\t\t\tliveValue: liveIdentity[key],\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t});\n"],"mappings":"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"}