@mysten-incubation/devstack 0.0.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.
- package/LICENSE +189 -0
- package/README.md +94 -0
- package/dist/_virtual/_rolldown/runtime.mjs +13 -0
- package/dist/api/define-capabilities.d.mts +25 -0
- package/dist/api/define-capabilities.mjs +19 -0
- package/dist/api/define-capabilities.mjs.map +1 -0
- package/dist/api/define-devstack-with.d.mts +41 -0
- package/dist/api/define-devstack-with.mjs +24 -0
- package/dist/api/define-devstack-with.mjs.map +1 -0
- package/dist/api/define-devstack.d.mts +62 -0
- package/dist/api/define-devstack.mjs +108 -0
- package/dist/api/define-devstack.mjs.map +1 -0
- package/dist/api/define-plugin.d.mts +1 -0
- package/dist/api/inference-network.mjs +122 -0
- package/dist/api/inference-network.mjs.map +1 -0
- package/dist/api/mode-narrowed-factory.d.mts +26 -0
- package/dist/api/mode-narrowed-factory.mjs +8 -0
- package/dist/api/mode-narrowed-factory.mjs.map +1 -0
- package/dist/api/plugin-errors.mjs +10 -0
- package/dist/api/plugin-errors.mjs.map +1 -0
- package/dist/api/run-stack.d.mts +82 -0
- package/dist/api/run-stack.mjs +127 -0
- package/dist/api/run-stack.mjs.map +1 -0
- package/dist/build-integrations/playwright/config.d.mts +92 -0
- package/dist/build-integrations/playwright/config.mjs +71 -0
- package/dist/build-integrations/playwright/config.mjs.map +1 -0
- package/dist/build-integrations/playwright/errors.d.mts +82 -0
- package/dist/build-integrations/playwright/errors.mjs +43 -0
- package/dist/build-integrations/playwright/errors.mjs.map +1 -0
- package/dist/build-integrations/playwright/global-setup.d.mts +64 -0
- package/dist/build-integrations/playwright/global-setup.mjs +44 -0
- package/dist/build-integrations/playwright/global-setup.mjs.map +1 -0
- package/dist/build-integrations/playwright/index.d.mts +6 -0
- package/dist/build-integrations/playwright/index.mjs +6 -0
- package/dist/build-integrations/playwright/stack-context.d.mts +95 -0
- package/dist/build-integrations/playwright/stack-context.mjs +277 -0
- package/dist/build-integrations/playwright/stack-context.mjs.map +1 -0
- package/dist/build-integrations/playwright/wallet-context.d.mts +87 -0
- package/dist/build-integrations/playwright/wallet-context.mjs +148 -0
- package/dist/build-integrations/playwright/wallet-context.mjs.map +1 -0
- package/dist/build-integrations/runtime/cold-start-url.d.mts +67 -0
- package/dist/build-integrations/runtime/cold-start-url.mjs +56 -0
- package/dist/build-integrations/runtime/cold-start-url.mjs.map +1 -0
- package/dist/build-integrations/runtime/dapp-kit-slot.mjs +10 -0
- package/dist/build-integrations/runtime/dapp-kit-slot.mjs.map +1 -0
- package/dist/build-integrations/runtime/discover.d.mts +78 -0
- package/dist/build-integrations/runtime/discover.mjs +124 -0
- package/dist/build-integrations/runtime/discover.mjs.map +1 -0
- package/dist/build-integrations/runtime/endpoint-registry.d.mts +26 -0
- package/dist/build-integrations/runtime/endpoint-registry.mjs +41 -0
- package/dist/build-integrations/runtime/endpoint-registry.mjs.map +1 -0
- package/dist/build-integrations/runtime/errors.d.mts +64 -0
- package/dist/build-integrations/runtime/errors.mjs +51 -0
- package/dist/build-integrations/runtime/errors.mjs.map +1 -0
- package/dist/build-integrations/runtime/index.d.mts +7 -0
- package/dist/build-integrations/runtime/index.mjs +6 -0
- package/dist/build-integrations/runtime/read-stack-context.d.mts +38 -0
- package/dist/build-integrations/runtime/read-stack-context.mjs +130 -0
- package/dist/build-integrations/runtime/read-stack-context.mjs.map +1 -0
- package/dist/build-integrations/runtime/stack-context.d.mts +53 -0
- package/dist/build-integrations/vitest/config.d.mts +62 -0
- package/dist/build-integrations/vitest/config.mjs +80 -0
- package/dist/build-integrations/vitest/config.mjs.map +1 -0
- package/dist/build-integrations/vitest/env.d.mts +48 -0
- package/dist/build-integrations/vitest/env.mjs +50 -0
- package/dist/build-integrations/vitest/env.mjs.map +1 -0
- package/dist/build-integrations/vitest/errors.d.mts +44 -0
- package/dist/build-integrations/vitest/errors.mjs +18 -0
- package/dist/build-integrations/vitest/errors.mjs.map +1 -0
- package/dist/build-integrations/vitest/index.d.mts +6 -0
- package/dist/build-integrations/vitest/index.mjs +6 -0
- package/dist/build-integrations/vitest/setup.d.mts +53 -0
- package/dist/build-integrations/vitest/setup.mjs +56 -0
- package/dist/build-integrations/vitest/setup.mjs.map +1 -0
- package/dist/build-integrations/vitest/stack-context.d.mts +58 -0
- package/dist/build-integrations/vitest/stack-context.mjs +67 -0
- package/dist/build-integrations/vitest/stack-context.mjs.map +1 -0
- package/dist/cli/main.d.mts +5 -0
- package/dist/cli/main.mjs +586 -0
- package/dist/cli/main.mjs.map +1 -0
- package/dist/cli/prune-direct.mjs +216 -0
- package/dist/cli/prune-direct.mjs.map +1 -0
- package/dist/cli/snapshot-reader.mjs +73 -0
- package/dist/cli/snapshot-reader.mjs.map +1 -0
- package/dist/cli/up-lifecycle.mjs +28 -0
- package/dist/cli/up-lifecycle.mjs.map +1 -0
- package/dist/contracts/capability-decl.d.mts +42 -0
- package/dist/contracts/chain-probe.d.mts +26 -0
- package/dist/contracts/chain-probe.mjs +8 -0
- package/dist/contracts/chain-probe.mjs.map +1 -0
- package/dist/contracts/codegenable.d.mts +43 -0
- package/dist/contracts/container-runtime.d.mts +245 -0
- package/dist/contracts/liveness-classifier.d.mts +19 -0
- package/dist/contracts/network-resolver.d.mts +15 -0
- package/dist/contracts/network-resolver.mjs +7 -0
- package/dist/contracts/network-resolver.mjs.map +1 -0
- package/dist/contracts/projection.d.mts +15 -0
- package/dist/contracts/routable.d.mts +53 -0
- package/dist/contracts/snapshotable.d.mts +39 -0
- package/dist/contracts/strategy-contributor.d.mts +26 -0
- package/dist/index.d.mts +89 -0
- package/dist/index.mjs +37 -0
- package/dist/orchestrators/codegen/bindings.d.mts +2 -0
- package/dist/orchestrators/codegen/bindings.mjs +340 -0
- package/dist/orchestrators/codegen/bindings.mjs.map +1 -0
- package/dist/orchestrators/codegen/emit.mjs +56 -0
- package/dist/orchestrators/codegen/emit.mjs.map +1 -0
- package/dist/orchestrators/codegen/errors.d.mts +1 -0
- package/dist/orchestrators/codegen/errors.mjs +66 -0
- package/dist/orchestrators/codegen/errors.mjs.map +1 -0
- package/dist/orchestrators/codegen/extras.mjs +16 -0
- package/dist/orchestrators/codegen/extras.mjs.map +1 -0
- package/dist/orchestrators/codegen/format.mjs +109 -0
- package/dist/orchestrators/codegen/format.mjs.map +1 -0
- package/dist/orchestrators/codegen/gitignore.mjs +67 -0
- package/dist/orchestrators/codegen/gitignore.mjs.map +1 -0
- package/dist/orchestrators/codegen/paths.d.mts +1 -0
- package/dist/orchestrators/codegen/paths.mjs +33 -0
- package/dist/orchestrators/codegen/paths.mjs.map +1 -0
- package/dist/orchestrators/codegen/permissions.mjs +6 -0
- package/dist/orchestrators/codegen/permissions.mjs.map +1 -0
- package/dist/orchestrators/codegen/service.d.mts +1 -0
- package/dist/orchestrators/codegen/service.mjs +276 -0
- package/dist/orchestrators/codegen/service.mjs.map +1 -0
- package/dist/orchestrators/router/cleanup.mjs +58 -0
- package/dist/orchestrators/router/cleanup.mjs.map +1 -0
- package/dist/orchestrators/router/cors.mjs +31 -0
- package/dist/orchestrators/router/cors.mjs.map +1 -0
- package/dist/orchestrators/router/entrypoints.d.mts +1 -0
- package/dist/orchestrators/router/entrypoints.mjs +80 -0
- package/dist/orchestrators/router/entrypoints.mjs.map +1 -0
- package/dist/orchestrators/router/errors.d.mts +1 -0
- package/dist/orchestrators/router/errors.mjs +103 -0
- package/dist/orchestrators/router/errors.mjs.map +1 -0
- package/dist/orchestrators/router/file-provider.d.mts +1 -0
- package/dist/orchestrators/router/file-provider.mjs +320 -0
- package/dist/orchestrators/router/file-provider.mjs.map +1 -0
- package/dist/orchestrators/router/hostname.d.mts +1 -0
- package/dist/orchestrators/router/hostname.mjs +107 -0
- package/dist/orchestrators/router/hostname.mjs.map +1 -0
- package/dist/orchestrators/router/index.d.mts +1 -0
- package/dist/orchestrators/router/profile.d.mts +1 -0
- package/dist/orchestrators/router/profile.mjs +109 -0
- package/dist/orchestrators/router/profile.mjs.map +1 -0
- package/dist/orchestrators/router/service.d.mts +3 -0
- package/dist/orchestrators/router/service.mjs +421 -0
- package/dist/orchestrators/router/service.mjs.map +1 -0
- package/dist/orchestrators/router/traefik-container.d.mts +1 -0
- package/dist/orchestrators/router/traefik-container.mjs +229 -0
- package/dist/orchestrators/router/traefik-container.mjs.map +1 -0
- package/dist/orchestrators/runtime-composition.d.mts +10 -0
- package/dist/orchestrators/runtime-composition.mjs +168 -0
- package/dist/orchestrators/runtime-composition.mjs.map +1 -0
- package/dist/orchestrators/snapshot/capture.d.mts +1 -0
- package/dist/orchestrators/snapshot/capture.mjs +350 -0
- package/dist/orchestrators/snapshot/capture.mjs.map +1 -0
- package/dist/orchestrators/snapshot/descriptor.d.mts +1 -0
- package/dist/orchestrators/snapshot/descriptor.mjs +109 -0
- package/dist/orchestrators/snapshot/descriptor.mjs.map +1 -0
- package/dist/orchestrators/snapshot/identity-guard.d.mts +1 -0
- package/dist/orchestrators/snapshot/identity-guard.mjs +132 -0
- package/dist/orchestrators/snapshot/identity-guard.mjs.map +1 -0
- package/dist/orchestrators/snapshot/image-bundle-tags.mjs +207 -0
- package/dist/orchestrators/snapshot/image-bundle-tags.mjs.map +1 -0
- package/dist/orchestrators/snapshot/index.d.mts +1 -0
- package/dist/orchestrators/snapshot/integrity.d.mts +1 -0
- package/dist/orchestrators/snapshot/integrity.mjs +74 -0
- package/dist/orchestrators/snapshot/integrity.mjs.map +1 -0
- package/dist/orchestrators/snapshot/prune.d.mts +1 -0
- package/dist/orchestrators/snapshot/prune.mjs +110 -0
- package/dist/orchestrators/snapshot/prune.mjs.map +1 -0
- package/dist/orchestrators/snapshot/restore.d.mts +1 -0
- package/dist/orchestrators/snapshot/restore.mjs +404 -0
- package/dist/orchestrators/snapshot/restore.mjs.map +1 -0
- package/dist/orchestrators/snapshot/service.d.mts +1 -0
- package/dist/orchestrators/snapshot/service.mjs +296 -0
- package/dist/orchestrators/snapshot/service.mjs.map +1 -0
- package/dist/orchestrators/snapshot/state-document.mjs +18 -0
- package/dist/orchestrators/snapshot/state-document.mjs.map +1 -0
- package/dist/orchestrators/snapshot/wipe.d.mts +1 -0
- package/dist/orchestrators/snapshot/wipe.mjs +59 -0
- package/dist/orchestrators/snapshot/wipe.mjs.map +1 -0
- package/dist/plugins/account/codegen.d.mts +12 -0
- package/dist/plugins/account/codegen.mjs +22 -0
- package/dist/plugins/account/codegen.mjs.map +1 -0
- package/dist/plugins/account/errors.d.mts +44 -0
- package/dist/plugins/account/errors.mjs +16 -0
- package/dist/plugins/account/errors.mjs.map +1 -0
- package/dist/plugins/account/funding.d.mts +94 -0
- package/dist/plugins/account/funding.mjs +199 -0
- package/dist/plugins/account/funding.mjs.map +1 -0
- package/dist/plugins/account/index.d.mts +54 -0
- package/dist/plugins/account/index.mjs +151 -0
- package/dist/plugins/account/index.mjs.map +1 -0
- package/dist/plugins/account/keypair.d.mts +32 -0
- package/dist/plugins/account/keypair.mjs +99 -0
- package/dist/plugins/account/keypair.mjs.map +1 -0
- package/dist/plugins/account/lease.mjs +21 -0
- package/dist/plugins/account/lease.mjs.map +1 -0
- package/dist/plugins/account/registry.d.mts +33 -0
- package/dist/plugins/account/registry.mjs +36 -0
- package/dist/plugins/account/registry.mjs.map +1 -0
- package/dist/plugins/account/service.d.mts +104 -0
- package/dist/plugins/account/service.mjs +405 -0
- package/dist/plugins/account/service.mjs.map +1 -0
- package/dist/plugins/account/snapshot.mjs +33 -0
- package/dist/plugins/account/snapshot.mjs.map +1 -0
- package/dist/plugins/account/variants/env.mjs +24 -0
- package/dist/plugins/account/variants/env.mjs.map +1 -0
- package/dist/plugins/account/variants/ephemeral.mjs +72 -0
- package/dist/plugins/account/variants/ephemeral.mjs.map +1 -0
- package/dist/plugins/account/variants/impersonate.d.mts +16 -0
- package/dist/plugins/account/variants/impersonate.mjs +48 -0
- package/dist/plugins/account/variants/impersonate.mjs.map +1 -0
- package/dist/plugins/account/variants/inline.mjs +13 -0
- package/dist/plugins/account/variants/inline.mjs.map +1 -0
- package/dist/plugins/account/variants/keystore.mjs +93 -0
- package/dist/plugins/account/variants/keystore.mjs.map +1 -0
- package/dist/plugins/account/variants/signer.mjs +31 -0
- package/dist/plugins/account/variants/signer.mjs.map +1 -0
- package/dist/plugins/action/build-context.d.mts +41 -0
- package/dist/plugins/action/discriminator.d.mts +20 -0
- package/dist/plugins/action/discriminator.mjs +13 -0
- package/dist/plugins/action/discriminator.mjs.map +1 -0
- package/dist/plugins/action/errors.d.mts +31 -0
- package/dist/plugins/action/errors.mjs +13 -0
- package/dist/plugins/action/errors.mjs.map +1 -0
- package/dist/plugins/action/execute.d.mts +2 -0
- package/dist/plugins/action/execute.mjs +145 -0
- package/dist/plugins/action/execute.mjs.map +1 -0
- package/dist/plugins/action/index.d.mts +56 -0
- package/dist/plugins/action/index.mjs +82 -0
- package/dist/plugins/action/index.mjs.map +1 -0
- package/dist/plugins/action/service.d.mts +26 -0
- package/dist/plugins/action/service.mjs +84 -0
- package/dist/plugins/action/service.mjs.map +1 -0
- package/dist/plugins/coin/address-resolution.d.mts +34 -0
- package/dist/plugins/coin/address-resolution.mjs +80 -0
- package/dist/plugins/coin/address-resolution.mjs.map +1 -0
- package/dist/plugins/coin/codegen.d.mts +16 -0
- package/dist/plugins/coin/codegen.mjs +21 -0
- package/dist/plugins/coin/codegen.mjs.map +1 -0
- package/dist/plugins/coin/discovery.d.mts +26 -0
- package/dist/plugins/coin/discovery.mjs +112 -0
- package/dist/plugins/coin/discovery.mjs.map +1 -0
- package/dist/plugins/coin/errors.d.mts +48 -0
- package/dist/plugins/coin/errors.mjs +13 -0
- package/dist/plugins/coin/errors.mjs.map +1 -0
- package/dist/plugins/coin/index.d.mts +95 -0
- package/dist/plugins/coin/index.mjs +172 -0
- package/dist/plugins/coin/index.mjs.map +1 -0
- package/dist/plugins/coin/metadata.d.mts +15 -0
- package/dist/plugins/coin/metadata.mjs +99 -0
- package/dist/plugins/coin/metadata.mjs.map +1 -0
- package/dist/plugins/coin/mint.d.mts +34 -0
- package/dist/plugins/coin/mint.mjs +164 -0
- package/dist/plugins/coin/mint.mjs.map +1 -0
- package/dist/plugins/coin/registry.d.mts +1 -0
- package/dist/plugins/coin/registry.mjs +37 -0
- package/dist/plugins/coin/registry.mjs.map +1 -0
- package/dist/plugins/coin/service.d.mts +47 -0
- package/dist/plugins/coin/service.mjs +51 -0
- package/dist/plugins/coin/service.mjs.map +1 -0
- package/dist/plugins/coin/snapshot.mjs +19 -0
- package/dist/plugins/coin/snapshot.mjs.map +1 -0
- package/dist/plugins/coin/type-strings.mjs +49 -0
- package/dist/plugins/coin/type-strings.mjs.map +1 -0
- package/dist/plugins/deepbook/codegen.d.mts +39 -0
- package/dist/plugins/deepbook/codegen.mjs +15 -0
- package/dist/plugins/deepbook/codegen.mjs.map +1 -0
- package/dist/plugins/deepbook/deploy.mjs +437 -0
- package/dist/plugins/deepbook/deploy.mjs.map +1 -0
- package/dist/plugins/deepbook/errors.d.mts +29 -0
- package/dist/plugins/deepbook/errors.mjs +26 -0
- package/dist/plugins/deepbook/errors.mjs.map +1 -0
- package/dist/plugins/deepbook/faucet-strategy.d.mts +8 -0
- package/dist/plugins/deepbook/faucet-strategy.mjs +81 -0
- package/dist/plugins/deepbook/faucet-strategy.mjs.map +1 -0
- package/dist/plugins/deepbook/index.d.mts +171 -0
- package/dist/plugins/deepbook/index.mjs +359 -0
- package/dist/plugins/deepbook/index.mjs.map +1 -0
- package/dist/plugins/deepbook/plugin-key.mjs +7 -0
- package/dist/plugins/deepbook/plugin-key.mjs.map +1 -0
- package/dist/plugins/deepbook/pyth/index.mjs +226 -0
- package/dist/plugins/deepbook/pyth/index.mjs.map +1 -0
- package/dist/plugins/deepbook/routable.mjs +21 -0
- package/dist/plugins/deepbook/routable.mjs.map +1 -0
- package/dist/plugins/deepbook/snapshot.mjs +49 -0
- package/dist/plugins/deepbook/snapshot.mjs.map +1 -0
- package/dist/plugins/deepbook/types.d.mts +125 -0
- package/dist/plugins/deepbook/types.mjs +12 -0
- package/dist/plugins/deepbook/types.mjs.map +1 -0
- package/dist/plugins/faucet/dispatcher.d.mts +9 -0
- package/dist/plugins/faucet/dispatcher.mjs +13 -0
- package/dist/plugins/faucet/dispatcher.mjs.map +1 -0
- package/dist/plugins/faucet/errors.d.mts +86 -0
- package/dist/plugins/faucet/errors.mjs +19 -0
- package/dist/plugins/faucet/errors.mjs.map +1 -0
- package/dist/plugins/faucet/http.mjs +127 -0
- package/dist/plugins/faucet/http.mjs.map +1 -0
- package/dist/plugins/faucet/index.d.mts +43 -0
- package/dist/plugins/faucet/index.mjs +42 -0
- package/dist/plugins/faucet/index.mjs.map +1 -0
- package/dist/plugins/faucet/service.d.mts +20 -0
- package/dist/plugins/faucet/strategies/sui-local.d.mts +23 -0
- package/dist/plugins/faucet/strategies/sui-local.mjs +29 -0
- package/dist/plugins/faucet/strategies/sui-local.mjs.map +1 -0
- package/dist/plugins/host-service/errors.d.mts +27 -0
- package/dist/plugins/host-service/errors.mjs +14 -0
- package/dist/plugins/host-service/errors.mjs.map +1 -0
- package/dist/plugins/host-service/index.d.mts +10 -0
- package/dist/plugins/host-service/index.mjs +48 -0
- package/dist/plugins/host-service/index.mjs.map +1 -0
- package/dist/plugins/host-service/routable.mjs +24 -0
- package/dist/plugins/host-service/routable.mjs.map +1 -0
- package/dist/plugins/host-service/service.d.mts +49 -0
- package/dist/plugins/host-service/service.mjs +375 -0
- package/dist/plugins/host-service/service.mjs.map +1 -0
- package/dist/plugins/package/build.d.mts +1 -0
- package/dist/plugins/package/build.mjs +24 -0
- package/dist/plugins/package/build.mjs.map +1 -0
- package/dist/plugins/package/codegen.d.mts +19 -0
- package/dist/plugins/package/codegen.mjs +41 -0
- package/dist/plugins/package/codegen.mjs.map +1 -0
- package/dist/plugins/package/dep-resolution.mjs +10 -0
- package/dist/plugins/package/dep-resolution.mjs.map +1 -0
- package/dist/plugins/package/errors.d.mts +34 -0
- package/dist/plugins/package/errors.mjs +13 -0
- package/dist/plugins/package/errors.mjs.map +1 -0
- package/dist/plugins/package/index.d.mts +109 -0
- package/dist/plugins/package/index.mjs +207 -0
- package/dist/plugins/package/index.mjs.map +1 -0
- package/dist/plugins/package/mode-known.mjs +41 -0
- package/dist/plugins/package/mode-known.mjs.map +1 -0
- package/dist/plugins/package/mode-local.d.mts +1 -0
- package/dist/plugins/package/mode-local.mjs +183 -0
- package/dist/plugins/package/mode-local.mjs.map +1 -0
- package/dist/plugins/package/publish-executor.mjs +202 -0
- package/dist/plugins/package/publish-executor.mjs.map +1 -0
- package/dist/plugins/package/publish-output.d.mts +28 -0
- package/dist/plugins/package/publish-output.mjs +45 -0
- package/dist/plugins/package/publish-output.mjs.map +1 -0
- package/dist/plugins/package/registry.d.mts +25 -0
- package/dist/plugins/package/registry.mjs +30 -0
- package/dist/plugins/package/registry.mjs.map +1 -0
- package/dist/plugins/package/service.mjs +20 -0
- package/dist/plugins/package/service.mjs.map +1 -0
- package/dist/plugins/package/snapshot.mjs +25 -0
- package/dist/plugins/package/snapshot.mjs.map +1 -0
- package/dist/plugins/postgres/codegen.mjs +42 -0
- package/dist/plugins/postgres/codegen.mjs.map +1 -0
- package/dist/plugins/postgres/connection.d.mts +33 -0
- package/dist/plugins/postgres/connection.mjs +19 -0
- package/dist/plugins/postgres/connection.mjs.map +1 -0
- package/dist/plugins/postgres/db-ensure.mjs +100 -0
- package/dist/plugins/postgres/db-ensure.mjs.map +1 -0
- package/dist/plugins/postgres/errors.d.mts +55 -0
- package/dist/plugins/postgres/errors.mjs +29 -0
- package/dist/plugins/postgres/errors.mjs.map +1 -0
- package/dist/plugins/postgres/index.d.mts +37 -0
- package/dist/plugins/postgres/index.mjs +75 -0
- package/dist/plugins/postgres/index.mjs.map +1 -0
- package/dist/plugins/postgres/routable.mjs +33 -0
- package/dist/plugins/postgres/routable.mjs.map +1 -0
- package/dist/plugins/postgres/service.d.mts +50 -0
- package/dist/plugins/postgres/service.mjs +145 -0
- package/dist/plugins/postgres/service.mjs.map +1 -0
- package/dist/plugins/postgres/snapshot.d.mts +1 -0
- package/dist/plugins/postgres/snapshot.mjs +32 -0
- package/dist/plugins/postgres/snapshot.mjs.map +1 -0
- package/dist/plugins/router-entrypoints.mjs +21 -0
- package/dist/plugins/router-entrypoints.mjs.map +1 -0
- package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs +59 -0
- package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs.map +1 -0
- package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs +132 -0
- package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs.map +1 -0
- package/dist/plugins/seal/codegen.d.mts +19 -0
- package/dist/plugins/seal/codegen.mjs +16 -0
- package/dist/plugins/seal/codegen.mjs.map +1 -0
- package/dist/plugins/seal/config-render.mjs +82 -0
- package/dist/plugins/seal/config-render.mjs.map +1 -0
- package/dist/plugins/seal/deploy.mjs +198 -0
- package/dist/plugins/seal/deploy.mjs.map +1 -0
- package/dist/plugins/seal/errors.d.mts +52 -0
- package/dist/plugins/seal/errors.mjs +19 -0
- package/dist/plugins/seal/errors.mjs.map +1 -0
- package/dist/plugins/seal/index.d.mts +161 -0
- package/dist/plugins/seal/index.mjs +235 -0
- package/dist/plugins/seal/index.mjs.map +1 -0
- package/dist/plugins/seal/key-manager.d.mts +22 -0
- package/dist/plugins/seal/key-manager.mjs +22 -0
- package/dist/plugins/seal/key-manager.mjs.map +1 -0
- package/dist/plugins/seal/key-server.mjs +175 -0
- package/dist/plugins/seal/key-server.mjs.map +1 -0
- package/dist/plugins/seal/keygen.mjs +96 -0
- package/dist/plugins/seal/keygen.mjs.map +1 -0
- package/dist/plugins/seal/mode/fork-known.d.mts +8 -0
- package/dist/plugins/seal/mode/fork-known.mjs +23 -0
- package/dist/plugins/seal/mode/fork-known.mjs.map +1 -0
- package/dist/plugins/seal/mode/live.d.mts +18 -0
- package/dist/plugins/seal/mode/live.mjs +64 -0
- package/dist/plugins/seal/mode/live.mjs.map +1 -0
- package/dist/plugins/seal/mode/local-keygen.mjs +180 -0
- package/dist/plugins/seal/mode/local-keygen.mjs.map +1 -0
- package/dist/plugins/seal/plugin-key.mjs +7 -0
- package/dist/plugins/seal/plugin-key.mjs.map +1 -0
- package/dist/plugins/seal/registry-publish.d.mts +43 -0
- package/dist/plugins/seal/registry-publish.mjs +9 -0
- package/dist/plugins/seal/registry-publish.mjs.map +1 -0
- package/dist/plugins/seal/routable.mjs +57 -0
- package/dist/plugins/seal/routable.mjs.map +1 -0
- package/dist/plugins/seal/service.mjs +17 -0
- package/dist/plugins/seal/service.mjs.map +1 -0
- package/dist/plugins/seal/snapshot.mjs +51 -0
- package/dist/plugins/seal/snapshot.mjs.map +1 -0
- package/dist/plugins/sui/auto-tick.mjs +45 -0
- package/dist/plugins/sui/auto-tick.mjs.map +1 -0
- package/dist/plugins/sui/chain-build-container.d.mts +1 -0
- package/dist/plugins/sui/chain-probe.d.mts +85 -0
- package/dist/plugins/sui/chain-probe.mjs +104 -0
- package/dist/plugins/sui/chain-probe.mjs.map +1 -0
- package/dist/plugins/sui/codegen.d.mts +14 -0
- package/dist/plugins/sui/codegen.mjs +26 -0
- package/dist/plugins/sui/codegen.mjs.map +1 -0
- package/dist/plugins/sui/errors.d.mts +74 -0
- package/dist/plugins/sui/errors.mjs +28 -0
- package/dist/plugins/sui/errors.mjs.map +1 -0
- package/dist/plugins/sui/fork-orchestration.mjs +24 -0
- package/dist/plugins/sui/fork-orchestration.mjs.map +1 -0
- package/dist/plugins/sui/fork-transaction.mjs +96 -0
- package/dist/plugins/sui/fork-transaction.mjs.map +1 -0
- package/dist/plugins/sui/index.d.mts +318 -0
- package/dist/plugins/sui/index.mjs +182 -0
- package/dist/plugins/sui/index.mjs.map +1 -0
- package/dist/plugins/sui/mode/external.mjs +60 -0
- package/dist/plugins/sui/mode/external.mjs.map +1 -0
- package/dist/plugins/sui/mode/fork.mjs +283 -0
- package/dist/plugins/sui/mode/fork.mjs.map +1 -0
- package/dist/plugins/sui/mode/live.mjs +103 -0
- package/dist/plugins/sui/mode/live.mjs.map +1 -0
- package/dist/plugins/sui/mode/local.mjs +293 -0
- package/dist/plugins/sui/mode/local.mjs.map +1 -0
- package/dist/plugins/sui/mode/shared-boot.mjs +254 -0
- package/dist/plugins/sui/mode/shared-boot.mjs.map +1 -0
- package/dist/plugins/sui/mode/shared.d.mts +103 -0
- package/dist/plugins/sui/mode/shared.mjs +12 -0
- package/dist/plugins/sui/mode/shared.mjs.map +1 -0
- package/dist/plugins/sui/mode/spec.d.mts +94 -0
- package/dist/plugins/sui/network-resolver.d.mts +23 -0
- package/dist/plugins/sui/routable.mjs +88 -0
- package/dist/plugins/sui/routable.mjs.map +1 -0
- package/dist/plugins/sui/seed-objects.d.mts +18 -0
- package/dist/plugins/sui/seed-objects.mjs +25 -0
- package/dist/plugins/sui/seed-objects.mjs.map +1 -0
- package/dist/plugins/sui/service.mjs +38 -0
- package/dist/plugins/sui/service.mjs.map +1 -0
- package/dist/plugins/sui/snapshot.mjs +53 -0
- package/dist/plugins/sui/snapshot.mjs.map +1 -0
- package/dist/plugins/wallet/codegen.d.mts +33 -0
- package/dist/plugins/wallet/codegen.mjs +34 -0
- package/dist/plugins/wallet/codegen.mjs.map +1 -0
- package/dist/plugins/wallet/errors.d.mts +66 -0
- package/dist/plugins/wallet/errors.mjs +16 -0
- package/dist/plugins/wallet/errors.mjs.map +1 -0
- package/dist/plugins/wallet/index.d.mts +63 -0
- package/dist/plugins/wallet/index.mjs +126 -0
- package/dist/plugins/wallet/index.mjs.map +1 -0
- package/dist/plugins/wallet/origin-policy.d.mts +1 -0
- package/dist/plugins/wallet/origin-policy.mjs +65 -0
- package/dist/plugins/wallet/origin-policy.mjs.map +1 -0
- package/dist/plugins/wallet/pairing.d.mts +11 -0
- package/dist/plugins/wallet/pairing.mjs +125 -0
- package/dist/plugins/wallet/pairing.mjs.map +1 -0
- package/dist/plugins/wallet/protocol.d.mts +1 -0
- package/dist/plugins/wallet/protocol.mjs +96 -0
- package/dist/plugins/wallet/protocol.mjs.map +1 -0
- package/dist/plugins/wallet/routable.mjs +32 -0
- package/dist/plugins/wallet/routable.mjs.map +1 -0
- package/dist/plugins/wallet/server.d.mts +12 -0
- package/dist/plugins/wallet/server.mjs +317 -0
- package/dist/plugins/wallet/server.mjs.map +1 -0
- package/dist/plugins/wallet/service.d.mts +68 -0
- package/dist/plugins/wallet/service.mjs +97 -0
- package/dist/plugins/wallet/service.mjs.map +1 -0
- package/dist/plugins/wallet/snapshot.d.mts +1 -0
- package/dist/plugins/wallet/snapshot.mjs +14 -0
- package/dist/plugins/wallet/snapshot.mjs.map +1 -0
- package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs +42 -0
- package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs.map +1 -0
- package/dist/plugins/walrus/codegen.d.mts +38 -0
- package/dist/plugins/walrus/codegen.mjs +34 -0
- package/dist/plugins/walrus/codegen.mjs.map +1 -0
- package/dist/plugins/walrus/deploy-paths.mjs +14 -0
- package/dist/plugins/walrus/deploy-paths.mjs.map +1 -0
- package/dist/plugins/walrus/deploy.d.mts +1 -0
- package/dist/plugins/walrus/deploy.mjs +224 -0
- package/dist/plugins/walrus/deploy.mjs.map +1 -0
- package/dist/plugins/walrus/errors.d.mts +38 -0
- package/dist/plugins/walrus/errors.mjs +26 -0
- package/dist/plugins/walrus/errors.mjs.map +1 -0
- package/dist/plugins/walrus/faucet-strategy.d.mts +12 -0
- package/dist/plugins/walrus/faucet-strategy.mjs +62 -0
- package/dist/plugins/walrus/faucet-strategy.mjs.map +1 -0
- package/dist/plugins/walrus/index.d.mts +177 -0
- package/dist/plugins/walrus/index.mjs +284 -0
- package/dist/plugins/walrus/index.mjs.map +1 -0
- package/dist/plugins/walrus/mode/known-deploy.d.mts +25 -0
- package/dist/plugins/walrus/mode/known-deploy.mjs +71 -0
- package/dist/plugins/walrus/mode/known-deploy.mjs.map +1 -0
- package/dist/plugins/walrus/mode/local-cluster.d.mts +34 -0
- package/dist/plugins/walrus/mode/local-cluster.mjs +153 -0
- package/dist/plugins/walrus/mode/local-cluster.mjs.map +1 -0
- package/dist/plugins/walrus/plugin-key.mjs +7 -0
- package/dist/plugins/walrus/plugin-key.mjs.map +1 -0
- package/dist/plugins/walrus/registry-publish.d.mts +24 -0
- package/dist/plugins/walrus/registry-publish.mjs +7 -0
- package/dist/plugins/walrus/registry-publish.mjs.map +1 -0
- package/dist/plugins/walrus/routable.mjs +99 -0
- package/dist/plugins/walrus/routable.mjs.map +1 -0
- package/dist/plugins/walrus/service.mjs +20 -0
- package/dist/plugins/walrus/service.mjs.map +1 -0
- package/dist/plugins/walrus/snapshot.mjs +53 -0
- package/dist/plugins/walrus/snapshot.mjs.map +1 -0
- package/dist/plugins/walrus/storage-nodes.d.mts +15 -0
- package/dist/plugins/walrus/storage-nodes.mjs +154 -0
- package/dist/plugins/walrus/storage-nodes.mjs.map +1 -0
- package/dist/plugins/walrus/wal-swap.d.mts +2 -0
- package/dist/plugins/walrus/wal-swap.mjs +58 -0
- package/dist/plugins/walrus/wal-swap.mjs.map +1 -0
- package/dist/primitives/artifact-publisher.d.mts +12 -0
- package/dist/primitives/cache.d.mts +1 -0
- package/dist/runtime/built-in-plugin-layers.mjs +49 -0
- package/dist/runtime/built-in-plugin-layers.mjs.map +1 -0
- package/dist/runtime/docker/client.d.mts +2 -0
- package/dist/runtime/docker/client.mjs +54 -0
- package/dist/runtime/docker/client.mjs.map +1 -0
- package/dist/runtime/docker/container.d.mts +1 -0
- package/dist/runtime/docker/container.mjs +562 -0
- package/dist/runtime/docker/container.mjs.map +1 -0
- package/dist/runtime/docker/errors.d.mts +1 -0
- package/dist/runtime/docker/errors.mjs +187 -0
- package/dist/runtime/docker/errors.mjs.map +1 -0
- package/dist/runtime/docker/exec.d.mts +1 -0
- package/dist/runtime/docker/exec.mjs +63 -0
- package/dist/runtime/docker/exec.mjs.map +1 -0
- package/dist/runtime/docker/image.d.mts +1 -0
- package/dist/runtime/docker/image.mjs +278 -0
- package/dist/runtime/docker/image.mjs.map +1 -0
- package/dist/runtime/docker/index.d.mts +1 -0
- package/dist/runtime/docker/inventory.d.mts +1 -0
- package/dist/runtime/docker/inventory.mjs +167 -0
- package/dist/runtime/docker/inventory.mjs.map +1 -0
- package/dist/runtime/docker/labels.d.mts +1 -0
- package/dist/runtime/docker/labels.mjs +97 -0
- package/dist/runtime/docker/labels.mjs.map +1 -0
- package/dist/runtime/docker/logs.d.mts +1 -0
- package/dist/runtime/docker/logs.mjs +34 -0
- package/dist/runtime/docker/logs.mjs.map +1 -0
- package/dist/runtime/docker/network.d.mts +1 -0
- package/dist/runtime/docker/network.mjs +168 -0
- package/dist/runtime/docker/network.mjs.map +1 -0
- package/dist/runtime/docker/service.d.mts +9 -0
- package/dist/runtime/docker/service.mjs +266 -0
- package/dist/runtime/docker/service.mjs.map +1 -0
- package/dist/runtime/docker/sweep.d.mts +1 -0
- package/dist/runtime/docker/sweep.mjs +220 -0
- package/dist/runtime/docker/sweep.mjs.map +1 -0
- package/dist/runtime/docker/volume.d.mts +1 -0
- package/dist/runtime/docker/volume.mjs +24 -0
- package/dist/runtime/docker/volume.mjs.map +1 -0
- package/dist/runtime/docker/wrap.d.mts +1 -0
- package/dist/runtime/docker/wrap.mjs +131 -0
- package/dist/runtime/docker/wrap.mjs.map +1 -0
- package/dist/substrate/brand.d.mts +24 -0
- package/dist/substrate/brand.mjs +14 -0
- package/dist/substrate/brand.mjs.map +1 -0
- package/dist/substrate/cross-process.d.mts +1 -0
- package/dist/substrate/cross-process.mjs +28 -0
- package/dist/substrate/cross-process.mjs.map +1 -0
- package/dist/substrate/events.d.mts +117 -0
- package/dist/substrate/identity.d.mts +13 -0
- package/dist/substrate/lifecycle.d.mts +12 -0
- package/dist/substrate/manifest.d.mts +39 -0
- package/dist/substrate/manifest.mjs +33 -0
- package/dist/substrate/manifest.mjs.map +1 -0
- package/dist/substrate/network.d.mts +34 -0
- package/dist/substrate/options.d.mts +23 -0
- package/dist/substrate/plugin.d.mts +101 -0
- package/dist/substrate/plugin.mjs +61 -0
- package/dist/substrate/plugin.mjs.map +1 -0
- package/dist/substrate/projection.d.mts +100 -0
- package/dist/substrate/runtime/artifact-publisher/index.mjs +76 -0
- package/dist/substrate/runtime/artifact-publisher/index.mjs.map +1 -0
- package/dist/substrate/runtime/atomic-write.mjs +106 -0
- package/dist/substrate/runtime/atomic-write.mjs.map +1 -0
- package/dist/substrate/runtime/cache/index.d.mts +1 -0
- package/dist/substrate/runtime/cache/schema.d.mts +1 -0
- package/dist/substrate/runtime/cache/schema.mjs +22 -0
- package/dist/substrate/runtime/cache/schema.mjs.map +1 -0
- package/dist/substrate/runtime/cache/service.d.mts +1 -0
- package/dist/substrate/runtime/cache/service.mjs +89 -0
- package/dist/substrate/runtime/cache/service.mjs.map +1 -0
- package/dist/substrate/runtime/capability-sinks/index.d.mts +1 -0
- package/dist/substrate/runtime/capability-sinks/layer.d.mts +1 -0
- package/dist/substrate/runtime/capability-sinks/layer.mjs +31 -0
- package/dist/substrate/runtime/capability-sinks/layer.mjs.map +1 -0
- package/dist/substrate/runtime/capability-sinks/service.d.mts +36 -0
- package/dist/substrate/runtime/capability-sinks/service.mjs +74 -0
- package/dist/substrate/runtime/capability-sinks/service.mjs.map +1 -0
- package/dist/substrate/runtime/config-validation.d.mts +38 -0
- package/dist/substrate/runtime/config-validation.mjs +137 -0
- package/dist/substrate/runtime/config-validation.mjs.map +1 -0
- package/dist/substrate/runtime/context-helpers.mjs +32 -0
- package/dist/substrate/runtime/context-helpers.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/command-channel/channel.mjs +128 -0
- package/dist/substrate/runtime/cross-process/command-channel/channel.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs +136 -0
- package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/command-channel/protocol.mjs +49 -0
- package/dist/substrate/runtime/cross-process/command-channel/protocol.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/command-channel/runtime-control-lock.mjs +9 -0
- package/dist/substrate/runtime/cross-process/command-channel/runtime-control-lock.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/liveness.mjs +119 -0
- package/dist/substrate/runtime/cross-process/liveness.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/roster.mjs +275 -0
- package/dist/substrate/runtime/cross-process/roster.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/snapshot-reservation.d.mts +1 -0
- package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs +117 -0
- package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/stack-lock.d.mts +1 -0
- package/dist/substrate/runtime/cross-process/stack-lock.mjs +142 -0
- package/dist/substrate/runtime/cross-process/stack-lock.mjs.map +1 -0
- package/dist/substrate/runtime/current-plugin.mjs +15 -0
- package/dist/substrate/runtime/current-plugin.mjs.map +1 -0
- package/dist/substrate/runtime/errors.d.mts +1 -0
- package/dist/substrate/runtime/errors.mjs +76 -0
- package/dist/substrate/runtime/errors.mjs.map +1 -0
- package/dist/substrate/runtime/host-tree-tar/index.mjs +364 -0
- package/dist/substrate/runtime/host-tree-tar/index.mjs.map +1 -0
- package/dist/substrate/runtime/http-probe.d.mts +35 -0
- package/dist/substrate/runtime/http-probe.mjs +65 -0
- package/dist/substrate/runtime/http-probe.mjs.map +1 -0
- package/dist/substrate/runtime/lease-broker/index.d.mts +1 -0
- package/dist/substrate/runtime/lease-broker/service.d.mts +64 -0
- package/dist/substrate/runtime/lease-broker/service.mjs +155 -0
- package/dist/substrate/runtime/lease-broker/service.mjs.map +1 -0
- package/dist/substrate/runtime/lifecycle/dep-graph.d.mts +1 -0
- package/dist/substrate/runtime/lifecycle/dep-graph.mjs +138 -0
- package/dist/substrate/runtime/lifecycle/dep-graph.mjs.map +1 -0
- package/dist/substrate/runtime/lifecycle/index.d.mts +1 -0
- package/dist/substrate/runtime/lifecycle/lifecycle-fact.mjs +31 -0
- package/dist/substrate/runtime/lifecycle/lifecycle-fact.mjs.map +1 -0
- package/dist/substrate/runtime/lifecycle/plugin-registry.d.mts +1 -0
- package/dist/substrate/runtime/lifecycle/plugin-registry.mjs +133 -0
- package/dist/substrate/runtime/lifecycle/plugin-registry.mjs.map +1 -0
- package/dist/substrate/runtime/lifecycle/ready-gate.d.mts +1 -0
- package/dist/substrate/runtime/lifecycle/ready-gate.mjs +34 -0
- package/dist/substrate/runtime/lifecycle/ready-gate.mjs.map +1 -0
- package/dist/substrate/runtime/lifecycle/selective-restart.d.mts +1 -0
- package/dist/substrate/runtime/lifecycle/selective-restart.mjs +43 -0
- package/dist/substrate/runtime/lifecycle/selective-restart.mjs.map +1 -0
- package/dist/substrate/runtime/lifecycle/signals.d.mts +1 -0
- package/dist/substrate/runtime/lifecycle/signals.mjs +64 -0
- package/dist/substrate/runtime/lifecycle/signals.mjs.map +1 -0
- package/dist/substrate/runtime/lifecycle/state-machine.d.mts +1 -0
- package/dist/substrate/runtime/lifecycle/state-machine.mjs +38 -0
- package/dist/substrate/runtime/lifecycle/state-machine.mjs.map +1 -0
- package/dist/substrate/runtime/lifecycle/watch-attribution.d.mts +1 -0
- package/dist/substrate/runtime/lifecycle/watch-attribution.mjs +27 -0
- package/dist/substrate/runtime/lifecycle/watch-attribution.mjs.map +1 -0
- package/dist/substrate/runtime/managed-container.d.mts +30 -0
- package/dist/substrate/runtime/managed-container.mjs +32 -0
- package/dist/substrate/runtime/managed-container.mjs.map +1 -0
- package/dist/substrate/runtime/manifest/manifest.mjs +73 -0
- package/dist/substrate/runtime/manifest/manifest.mjs.map +1 -0
- package/dist/substrate/runtime/mode-errors.d.mts +36 -0
- package/dist/substrate/runtime/observability/cascade-formatter.d.mts +1 -0
- package/dist/substrate/runtime/observability/cascade-formatter.mjs +135 -0
- package/dist/substrate/runtime/observability/cascade-formatter.mjs.map +1 -0
- package/dist/substrate/runtime/observability/formatter-registry.d.mts +1 -0
- package/dist/substrate/runtime/observability/formatter-registry.mjs +65 -0
- package/dist/substrate/runtime/observability/formatter-registry.mjs.map +1 -0
- package/dist/substrate/runtime/observability/index.d.mts +3 -0
- package/dist/substrate/runtime/observability/logger.d.mts +58 -0
- package/dist/substrate/runtime/observability/logger.mjs +89 -0
- package/dist/substrate/runtime/observability/logger.mjs.map +1 -0
- package/dist/substrate/runtime/observability/pretty-error.d.mts +1 -0
- package/dist/substrate/runtime/observability/pretty-error.mjs +119 -0
- package/dist/substrate/runtime/observability/pretty-error.mjs.map +1 -0
- package/dist/substrate/runtime/observability/process-lines.d.mts +31 -0
- package/dist/substrate/runtime/observability/process-lines.mjs +40 -0
- package/dist/substrate/runtime/observability/process-lines.mjs.map +1 -0
- package/dist/substrate/runtime/observability/redaction.d.mts +28 -0
- package/dist/substrate/runtime/observability/redaction.mjs +45 -0
- package/dist/substrate/runtime/observability/redaction.mjs.map +1 -0
- package/dist/substrate/runtime/observability/spans.d.mts +1 -0
- package/dist/substrate/runtime/observability/spans.mjs +87 -0
- package/dist/substrate/runtime/observability/spans.mjs.map +1 -0
- package/dist/substrate/runtime/observability/subprocess-capture.d.mts +2 -0
- package/dist/substrate/runtime/observability/subprocess-capture.mjs +82 -0
- package/dist/substrate/runtime/observability/subprocess-capture.mjs.map +1 -0
- package/dist/substrate/runtime/paths.d.mts +17 -0
- package/dist/substrate/runtime/paths.mjs +61 -0
- package/dist/substrate/runtime/paths.mjs.map +1 -0
- package/dist/substrate/runtime/port-broker/index.d.mts +1 -0
- package/dist/substrate/runtime/port-broker/service.d.mts +1 -0
- package/dist/substrate/runtime/port-broker/service.mjs +319 -0
- package/dist/substrate/runtime/port-broker/service.mjs.map +1 -0
- package/dist/substrate/runtime/post-acquire-tasks.d.mts +1 -0
- package/dist/substrate/runtime/post-acquire-tasks.mjs +34 -0
- package/dist/substrate/runtime/post-acquire-tasks.mjs.map +1 -0
- package/dist/substrate/runtime/probes.d.mts +51 -0
- package/dist/substrate/runtime/probes.mjs +84 -0
- package/dist/substrate/runtime/probes.mjs.map +1 -0
- package/dist/substrate/runtime/process-supervisor.d.mts +57 -0
- package/dist/substrate/runtime/process-supervisor.mjs +73 -0
- package/dist/substrate/runtime/process-supervisor.mjs.map +1 -0
- package/dist/substrate/runtime/projection/operational-endpoints.mjs +44 -0
- package/dist/substrate/runtime/projection/operational-endpoints.mjs.map +1 -0
- package/dist/substrate/runtime/projection/persisted.mjs +195 -0
- package/dist/substrate/runtime/projection/persisted.mjs.map +1 -0
- package/dist/substrate/runtime/projection/state-ref.mjs +48 -0
- package/dist/substrate/runtime/projection/state-ref.mjs.map +1 -0
- package/dist/substrate/runtime/projection/update.mjs +200 -0
- package/dist/substrate/runtime/projection/update.mjs.map +1 -0
- package/dist/substrate/runtime/retry-policy.d.mts +18 -0
- package/dist/substrate/runtime/retry-policy.mjs +16 -0
- package/dist/substrate/runtime/retry-policy.mjs.map +1 -0
- package/dist/substrate/runtime/run.mjs +86 -0
- package/dist/substrate/runtime/run.mjs.map +1 -0
- package/dist/substrate/runtime/runtime-decode.d.mts +32 -0
- package/dist/substrate/runtime/runtime-decode.mjs +53 -0
- package/dist/substrate/runtime/runtime-decode.mjs.map +1 -0
- package/dist/substrate/runtime/scoped-http-server.mjs +36 -0
- package/dist/substrate/runtime/scoped-http-server.mjs.map +1 -0
- package/dist/substrate/runtime/scoped-ref-map/service.mjs +83 -0
- package/dist/substrate/runtime/scoped-ref-map/service.mjs.map +1 -0
- package/dist/substrate/runtime/stage-and-swap/index.d.mts +1 -0
- package/dist/substrate/runtime/stage-and-swap/index.mjs +131 -0
- package/dist/substrate/runtime/stage-and-swap/index.mjs.map +1 -0
- package/dist/substrate/runtime/state-store/schema.mjs +36 -0
- package/dist/substrate/runtime/state-store/schema.mjs.map +1 -0
- package/dist/substrate/runtime/strategy-registry/chain-probe-for.d.mts +1 -0
- package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs +22 -0
- package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs.map +1 -0
- package/dist/substrate/runtime/strategy-registry/faucet-capability-for.d.mts +1 -0
- package/dist/substrate/runtime/strategy-registry/faucet-capability-for.mjs +30 -0
- package/dist/substrate/runtime/strategy-registry/faucet-capability-for.mjs.map +1 -0
- package/dist/substrate/runtime/strategy-registry/index.d.mts +1 -0
- package/dist/substrate/runtime/strategy-registry/service.d.mts +1 -0
- package/dist/substrate/runtime/strategy-registry/service.mjs +67 -0
- package/dist/substrate/runtime/strategy-registry/service.mjs.map +1 -0
- package/dist/substrate/runtime/sui-execute/index.d.mts +11 -0
- package/dist/substrate/runtime/sui-execute/index.mjs +144 -0
- package/dist/substrate/runtime/sui-execute/index.mjs.map +1 -0
- package/dist/substrate/runtime/sui-move-build/index.d.mts +1 -0
- package/dist/substrate/runtime/sui-move-build/index.mjs +283 -0
- package/dist/substrate/runtime/sui-move-build/index.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor.d.mts +1 -0
- package/dist/substrate/runtime/supervisor.mjs +831 -0
- package/dist/substrate/runtime/supervisor.mjs.map +1 -0
- package/dist/surfaces/cli/command-tree.mjs +276 -0
- package/dist/surfaces/cli/command-tree.mjs.map +1 -0
- package/dist/surfaces/cli/commands/config.mjs +31 -0
- package/dist/surfaces/cli/commands/config.mjs.map +1 -0
- package/dist/surfaces/cli/commands/confirm-node.mjs +26 -0
- package/dist/surfaces/cli/commands/confirm-node.mjs.map +1 -0
- package/dist/surfaces/cli/commands/confirm.mjs +26 -0
- package/dist/surfaces/cli/commands/confirm.mjs.map +1 -0
- package/dist/surfaces/cli/commands/doctor-probes.mjs +452 -0
- package/dist/surfaces/cli/commands/doctor-probes.mjs.map +1 -0
- package/dist/surfaces/cli/commands/doctor.mjs +38 -0
- package/dist/surfaces/cli/commands/doctor.mjs.map +1 -0
- package/dist/surfaces/cli/commands/prune-picker.mjs +347 -0
- package/dist/surfaces/cli/commands/prune-picker.mjs.map +1 -0
- package/dist/surfaces/cli/commands/prune.mjs +178 -0
- package/dist/surfaces/cli/commands/prune.mjs.map +1 -0
- package/dist/surfaces/cli/commands/snapshot.mjs +141 -0
- package/dist/surfaces/cli/commands/snapshot.mjs.map +1 -0
- package/dist/surfaces/cli/commands/status.mjs +95 -0
- package/dist/surfaces/cli/commands/status.mjs.map +1 -0
- package/dist/surfaces/cli/commands/supervisor-presence.mjs +33 -0
- package/dist/surfaces/cli/commands/supervisor-presence.mjs.map +1 -0
- package/dist/surfaces/cli/commands/wipe.mjs +28 -0
- package/dist/surfaces/cli/commands/wipe.mjs.map +1 -0
- package/dist/surfaces/cli/envelope.mjs +43 -0
- package/dist/surfaces/cli/envelope.mjs.map +1 -0
- package/dist/surfaces/cli/errors.mjs +110 -0
- package/dist/surfaces/cli/errors.mjs.map +1 -0
- package/dist/surfaces/cli/flags.mjs +56 -0
- package/dist/surfaces/cli/flags.mjs.map +1 -0
- package/dist/surfaces/cli/index.mjs +418 -0
- package/dist/surfaces/cli/index.mjs.map +1 -0
- package/dist/surfaces/cli/output.mjs +115 -0
- package/dist/surfaces/cli/output.mjs.map +1 -0
- package/dist/surfaces/cli/sysexits.mjs +139 -0
- package/dist/surfaces/cli/sysexits.mjs.map +1 -0
- package/dist/surfaces/tui/app.mjs +104 -0
- package/dist/surfaces/tui/app.mjs.map +1 -0
- package/dist/surfaces/tui/dashboard.mjs +261 -0
- package/dist/surfaces/tui/dashboard.mjs.map +1 -0
- package/dist/surfaces/tui/display-derivation.mjs +395 -0
- package/dist/surfaces/tui/display-derivation.mjs.map +1 -0
- package/dist/surfaces/tui/errors.mjs +11 -0
- package/dist/surfaces/tui/errors.mjs.map +1 -0
- package/dist/surfaces/tui/event-log.mjs +155 -0
- package/dist/surfaces/tui/event-log.mjs.map +1 -0
- package/dist/surfaces/tui/heartbeat.mjs +33 -0
- package/dist/surfaces/tui/heartbeat.mjs.map +1 -0
- package/dist/surfaces/tui/index.mjs +50 -0
- package/dist/surfaces/tui/index.mjs.map +1 -0
- package/dist/surfaces/tui/input.mjs +104 -0
- package/dist/surfaces/tui/input.mjs.map +1 -0
- package/dist/surfaces/tui/mode-detect.mjs +32 -0
- package/dist/surfaces/tui/mode-detect.mjs.map +1 -0
- package/dist/surfaces/tui/mount-ink.mjs +31 -0
- package/dist/surfaces/tui/mount-ink.mjs.map +1 -0
- package/dist/surfaces/tui/plain-renderer.mjs +170 -0
- package/dist/surfaces/tui/plain-renderer.mjs.map +1 -0
- package/dist/surfaces/tui/resource-table.mjs +507 -0
- package/dist/surfaces/tui/resource-table.mjs.map +1 -0
- package/images/_shared/signal-forward.sh +77 -0
- package/images/postgres/Dockerfile +32 -0
- package/images/seal/Dockerfile +68 -0
- package/images/seal/entrypoint.sh +65 -0
- package/images/sui/Dockerfile +73 -0
- package/images/sui/entrypoint.sh +244 -0
- package/images/sui-fork/Dockerfile +53 -0
- package/images/sui-fork/entrypoint.sh +51 -0
- package/images/walrus/Dockerfile +122 -0
- package/images/walrus/deploy-walrus.sh +305 -0
- package/images/walrus/run-walrus.sh +53 -0
- package/package.json +94 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
//#region src/orchestrators/snapshot/identity-guard.ts
|
|
3
|
+
/** Tagged failure: chain identity (or another contributed key) disagrees
|
|
4
|
+
* between the snapshot and the live stack. No mutation occurred. */
|
|
5
|
+
var IdentityMismatchError = class extends Schema.TaggedErrorClass()("SnapshotIdentityMismatch", {
|
|
6
|
+
phase: Schema.Literal("identity-guard"),
|
|
7
|
+
key: Schema.String,
|
|
8
|
+
snapshotValue: Schema.String,
|
|
9
|
+
liveValue: Schema.String
|
|
10
|
+
}) {};
|
|
11
|
+
/** Tagged failure: snapshot recorded a key that the live stack did
|
|
12
|
+
* not contribute. Fail-closed (architecture open question §5
|
|
13
|
+
* resolution); the alternative is silently restoring across an
|
|
14
|
+
* unverified seam. */
|
|
15
|
+
var IdentityMissingLiveError = class extends Schema.TaggedErrorClass()("SnapshotIdentityMissingLive", {
|
|
16
|
+
phase: Schema.Literal("identity-guard"),
|
|
17
|
+
key: Schema.String,
|
|
18
|
+
snapshotValue: Schema.String
|
|
19
|
+
}) {};
|
|
20
|
+
/** Tagged failure: live stack contributed a key the snapshot did NOT
|
|
21
|
+
* record. Fail-closed for the same reason as above — restoring an
|
|
22
|
+
* older snapshot that pre-dates the contribution is a code-version
|
|
23
|
+
* mismatch, not a happy path. */
|
|
24
|
+
var IdentityMissingSnapshotError = class extends Schema.TaggedErrorClass()("SnapshotIdentityMissingSnapshot", {
|
|
25
|
+
phase: Schema.Literal("identity-guard"),
|
|
26
|
+
key: Schema.String,
|
|
27
|
+
liveValue: Schema.String
|
|
28
|
+
}) {};
|
|
29
|
+
var IdentityEmptyError = class extends Schema.TaggedErrorClass()("SnapshotIdentityEmpty", {
|
|
30
|
+
phase: Schema.Literal("identity-guard"),
|
|
31
|
+
source: Schema.Literals(["snapshot", "live"])
|
|
32
|
+
}) {};
|
|
33
|
+
/** Tagged failure: two plugins contributed the same key with different
|
|
34
|
+
* values. The orchestrator surfaces this BEFORE running the guard;
|
|
35
|
+
* same-key contributions from independent plugins should agree (the
|
|
36
|
+
* same chain identity contributed by sui and a probe-renderer, say). */
|
|
37
|
+
var IdentityContributionConflictError = class extends Schema.TaggedErrorClass()("SnapshotIdentityContributionConflict", {
|
|
38
|
+
phase: Schema.Literal("identity-guard"),
|
|
39
|
+
key: Schema.String,
|
|
40
|
+
conflictingPlugins: Schema.Array(Schema.String),
|
|
41
|
+
values: Schema.Array(Schema.String)
|
|
42
|
+
}) {};
|
|
43
|
+
/**
|
|
44
|
+
* Merge a list of plugin contributions into one slice. Conflict
|
|
45
|
+
* (same key, different values across plugins) surfaces as a tagged
|
|
46
|
+
* error — the orchestrator refuses to proceed rather than picking a
|
|
47
|
+
* "winner" silently.
|
|
48
|
+
*
|
|
49
|
+
* Architecture § Snapshotable: identity contributions stack — chain
|
|
50
|
+
* is the canonical case but plugins are free to contribute their own
|
|
51
|
+
* (e.g. postgres major version, sui fork checkpoint).
|
|
52
|
+
*/
|
|
53
|
+
const mergeContributions = (contributions) => Effect.gen(function* () {
|
|
54
|
+
const seen = {};
|
|
55
|
+
for (const { plugin, slice } of contributions) for (const [key, value] of Object.entries(slice)) seen[key] = [...seen[key] ?? [], {
|
|
56
|
+
plugin,
|
|
57
|
+
value
|
|
58
|
+
}];
|
|
59
|
+
const merged = {};
|
|
60
|
+
for (const [key, entries] of Object.entries(seen)) {
|
|
61
|
+
const distinct = new Set(entries.map((e) => e.value));
|
|
62
|
+
if (distinct.size > 1) return yield* Effect.fail(new IdentityContributionConflictError({
|
|
63
|
+
phase: "identity-guard",
|
|
64
|
+
key,
|
|
65
|
+
conflictingPlugins: entries.map((e) => e.plugin),
|
|
66
|
+
values: [...distinct]
|
|
67
|
+
}));
|
|
68
|
+
merged[key] = entries[0].value;
|
|
69
|
+
}
|
|
70
|
+
return merged;
|
|
71
|
+
});
|
|
72
|
+
const requireIdentity = (identity, source) => Object.keys(identity).length === 0 ? Effect.fail(new IdentityEmptyError({
|
|
73
|
+
phase: "identity-guard",
|
|
74
|
+
source
|
|
75
|
+
})) : Effect.void;
|
|
76
|
+
/**
|
|
77
|
+
* Compare the snapshot's recorded identity against the live stack's
|
|
78
|
+
* contributed identity. Fail-closed on every shape:
|
|
79
|
+
*
|
|
80
|
+
* - snapshot identity is empty → IdentityEmptyError
|
|
81
|
+
* - keys present on both sides, values differ → IdentityMismatchError
|
|
82
|
+
* - keys present only in the snapshot → IdentityMissingLiveError
|
|
83
|
+
* - keys present only in the live stack → IdentityMissingSnapshotError
|
|
84
|
+
*
|
|
85
|
+
* The architecture's open-question §5 ("require both sides of the
|
|
86
|
+
* comparison rather than fail-open on either-undefined") resolves to
|
|
87
|
+
* fail-closed; this is the implementation.
|
|
88
|
+
*
|
|
89
|
+
* The guard runs BEFORE any destructive mutation. On a successful
|
|
90
|
+
* return there is no I/O effect to roll back.
|
|
91
|
+
*/
|
|
92
|
+
const runIdentityGuard = (snapshotIdentity, liveIdentity) => Effect.gen(function* () {
|
|
93
|
+
yield* Effect.annotateCurrentSpan({ "devstack.snapshot.phase": "identity-guard" });
|
|
94
|
+
const snapshotKeys = Object.keys(snapshotIdentity);
|
|
95
|
+
const liveKeys = Object.keys(liveIdentity);
|
|
96
|
+
yield* requireIdentity(snapshotIdentity, "snapshot");
|
|
97
|
+
for (const key of snapshotKeys) if (key in liveIdentity) {
|
|
98
|
+
if (snapshotIdentity[key] !== liveIdentity[key]) return yield* Effect.fail(new IdentityMismatchError({
|
|
99
|
+
phase: "identity-guard",
|
|
100
|
+
key,
|
|
101
|
+
snapshotValue: snapshotIdentity[key],
|
|
102
|
+
liveValue: liveIdentity[key]
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
for (const key of snapshotKeys) if (!(key in liveIdentity)) return yield* Effect.fail(new IdentityMissingLiveError({
|
|
106
|
+
phase: "identity-guard",
|
|
107
|
+
key,
|
|
108
|
+
snapshotValue: snapshotIdentity[key]
|
|
109
|
+
}));
|
|
110
|
+
for (const key of liveKeys) if (!(key in snapshotIdentity)) return yield* Effect.fail(new IdentityMissingSnapshotError({
|
|
111
|
+
phase: "identity-guard",
|
|
112
|
+
key,
|
|
113
|
+
liveValue: liveIdentity[key]
|
|
114
|
+
}));
|
|
115
|
+
}).pipe(Effect.withSpan("orchestrator.snapshot.identity-guard"));
|
|
116
|
+
const runRuntimeIdentityGuard = (snapshotIdentity, liveIdentity) => Effect.gen(function* () {
|
|
117
|
+
yield* Effect.annotateCurrentSpan({ "devstack.snapshot.phase": "identity-guard" });
|
|
118
|
+
for (const key of [
|
|
119
|
+
"app",
|
|
120
|
+
"stack",
|
|
121
|
+
"network"
|
|
122
|
+
]) if (snapshotIdentity[key] !== liveIdentity[key]) return yield* Effect.fail(new IdentityMismatchError({
|
|
123
|
+
phase: "identity-guard",
|
|
124
|
+
key,
|
|
125
|
+
snapshotValue: snapshotIdentity[key],
|
|
126
|
+
liveValue: liveIdentity[key]
|
|
127
|
+
}));
|
|
128
|
+
}).pipe(Effect.withSpan("orchestrator.snapshot.runtime-identity-guard"));
|
|
129
|
+
//#endregion
|
|
130
|
+
export { mergeContributions, requireIdentity, runIdentityGuard, runRuntimeIdentityGuard };
|
|
131
|
+
|
|
132
|
+
//# sourceMappingURL=identity-guard.mjs.map
|
|
@@ -0,0 +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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"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { isRestorableContainerImageName } from "./descriptor.mjs";
|
|
2
|
+
import { Effect, FileSystem, Schema, Stream } from "effect";
|
|
3
|
+
//#region src/orchestrators/snapshot/image-bundle-tags.ts
|
|
4
|
+
var ImageBundleTagScanError = class extends Schema.TaggedErrorClass()("SnapshotImageBundleTagScanError", {
|
|
5
|
+
detail: Schema.String,
|
|
6
|
+
cause: Schema.optional(Schema.Defect)
|
|
7
|
+
}) {};
|
|
8
|
+
const failScan = (detail, cause) => new ImageBundleTagScanError({
|
|
9
|
+
detail,
|
|
10
|
+
...cause === void 0 ? {} : { cause }
|
|
11
|
+
});
|
|
12
|
+
const TAR_BLOCK_SIZE = 512;
|
|
13
|
+
const MAX_DOCKER_SAVE_MANIFEST_BYTES = 1024 * 1024;
|
|
14
|
+
const bytesToString = (bytes) => {
|
|
15
|
+
const nul = bytes.indexOf(0);
|
|
16
|
+
const end = nul === -1 ? bytes.length : nul;
|
|
17
|
+
return Buffer.from(bytes.subarray(0, end)).toString("utf8");
|
|
18
|
+
};
|
|
19
|
+
const concatBytes = (a, b) => {
|
|
20
|
+
if (a.length === 0) return b;
|
|
21
|
+
if (b.length === 0) return a;
|
|
22
|
+
const out = new Uint8Array(a.length + b.length);
|
|
23
|
+
out.set(a, 0);
|
|
24
|
+
out.set(b, a.length);
|
|
25
|
+
return out;
|
|
26
|
+
};
|
|
27
|
+
const consumeBytes = (buffer, count) => buffer.subarray(count);
|
|
28
|
+
const isZeroBlock = (block) => block.every((byte) => byte === 0);
|
|
29
|
+
const parseTarSize = (header) => {
|
|
30
|
+
const raw = bytesToString(header.subarray(124, 136)).trim();
|
|
31
|
+
if (raw === "") return 0;
|
|
32
|
+
if (!/^[0-7]+$/.test(raw)) return null;
|
|
33
|
+
const parsed = Number.parseInt(raw, 8);
|
|
34
|
+
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : null;
|
|
35
|
+
};
|
|
36
|
+
const tarPathFromHeader = (header) => {
|
|
37
|
+
const name = bytesToString(header.subarray(0, 100));
|
|
38
|
+
const prefix = bytesToString(header.subarray(345, 500));
|
|
39
|
+
return prefix === "" ? name : `${prefix}/${name}`;
|
|
40
|
+
};
|
|
41
|
+
const normalizedTarEntryPath = (path) => {
|
|
42
|
+
let normalized = path;
|
|
43
|
+
while (normalized.startsWith("./")) normalized = normalized.slice(2);
|
|
44
|
+
return normalized;
|
|
45
|
+
};
|
|
46
|
+
const processDockerSaveMetadataChunk = (state, chunk) => {
|
|
47
|
+
state.buffer = concatBytes(state.buffer, chunk);
|
|
48
|
+
while (state.buffer.length > 0) {
|
|
49
|
+
if (state.content !== null) {
|
|
50
|
+
const content = state.content;
|
|
51
|
+
const remaining = content.paddedSize - content.totalBytesRead;
|
|
52
|
+
const take = Math.min(remaining, state.buffer.length);
|
|
53
|
+
const contentTake = Math.max(0, Math.min(take, content.size - content.contentBytesRead));
|
|
54
|
+
if (contentTake > 0) content.chunks.push(state.buffer.subarray(0, contentTake));
|
|
55
|
+
state.content = {
|
|
56
|
+
...content,
|
|
57
|
+
contentBytesRead: content.contentBytesRead + contentTake,
|
|
58
|
+
totalBytesRead: content.totalBytesRead + take
|
|
59
|
+
};
|
|
60
|
+
state.buffer = consumeBytes(state.buffer, take);
|
|
61
|
+
if (state.content.totalBytesRead === state.content.paddedSize) {
|
|
62
|
+
const completed = state.content;
|
|
63
|
+
state.content = null;
|
|
64
|
+
const entryBytes = completed.chunks.length === 1 ? completed.chunks[0] : Buffer.concat(completed.chunks.map((entry) => Buffer.from(entry)));
|
|
65
|
+
if (completed.entry === "manifest.json") {
|
|
66
|
+
state.manifestBytes = entryBytes;
|
|
67
|
+
return { done: true };
|
|
68
|
+
}
|
|
69
|
+
state.indexBytes = entryBytes;
|
|
70
|
+
}
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (state.skipRemaining > 0) {
|
|
74
|
+
const take = Math.min(state.skipRemaining, state.buffer.length);
|
|
75
|
+
state.skipRemaining -= take;
|
|
76
|
+
state.buffer = consumeBytes(state.buffer, take);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (state.buffer.length < TAR_BLOCK_SIZE) return { done: false };
|
|
80
|
+
const header = state.buffer.subarray(0, TAR_BLOCK_SIZE);
|
|
81
|
+
state.buffer = consumeBytes(state.buffer, TAR_BLOCK_SIZE);
|
|
82
|
+
if (isZeroBlock(header)) continue;
|
|
83
|
+
const size = parseTarSize(header);
|
|
84
|
+
if (size === null) return {
|
|
85
|
+
done: false,
|
|
86
|
+
error: failScan("docker save bundle has invalid tar size")
|
|
87
|
+
};
|
|
88
|
+
const paddedSize = Math.ceil(size / TAR_BLOCK_SIZE) * TAR_BLOCK_SIZE;
|
|
89
|
+
const entryPath = normalizedTarEntryPath(tarPathFromHeader(header));
|
|
90
|
+
if (entryPath === "manifest.json" || entryPath === "index.json") {
|
|
91
|
+
if (size > MAX_DOCKER_SAVE_MANIFEST_BYTES) return {
|
|
92
|
+
done: false,
|
|
93
|
+
error: failScan(`docker save ${entryPath} is too large`)
|
|
94
|
+
};
|
|
95
|
+
if (size === 0) {
|
|
96
|
+
if (entryPath === "manifest.json") {
|
|
97
|
+
state.manifestBytes = new Uint8Array(0);
|
|
98
|
+
return { done: true };
|
|
99
|
+
}
|
|
100
|
+
state.indexBytes = new Uint8Array(0);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
state.content = {
|
|
104
|
+
entry: entryPath,
|
|
105
|
+
size,
|
|
106
|
+
paddedSize,
|
|
107
|
+
chunks: [],
|
|
108
|
+
contentBytesRead: 0,
|
|
109
|
+
totalBytesRead: 0
|
|
110
|
+
};
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
state.skipRemaining = paddedSize;
|
|
114
|
+
}
|
|
115
|
+
return { done: false };
|
|
116
|
+
};
|
|
117
|
+
const parseDockerSaveManifestTags = (bytes, tarPath) => Effect.gen(function* () {
|
|
118
|
+
const raw = yield* Effect.try({
|
|
119
|
+
try: () => JSON.parse(Buffer.from(bytes).toString("utf8")),
|
|
120
|
+
catch: (cause) => failScan(`docker save manifest.json in ${tarPath} is not valid JSON`, cause)
|
|
121
|
+
});
|
|
122
|
+
if (!Array.isArray(raw)) return yield* Effect.fail(failScan(`docker save manifest.json in ${tarPath} is not an array`));
|
|
123
|
+
const tags = /* @__PURE__ */ new Set();
|
|
124
|
+
for (const [index, entry] of raw.entries()) {
|
|
125
|
+
if (typeof entry !== "object" || entry === null) return yield* Effect.fail(failScan(`docker save manifest entry ${index} in ${tarPath} is not an object`));
|
|
126
|
+
const repoTags = entry.RepoTags;
|
|
127
|
+
if (repoTags === void 0 || repoTags === null) continue;
|
|
128
|
+
if (!Array.isArray(repoTags)) return yield* Effect.fail(failScan(`docker save manifest entry ${index} in ${tarPath} has non-array RepoTags`));
|
|
129
|
+
for (const tag of repoTags) {
|
|
130
|
+
if (typeof tag !== "string" || !isRestorableContainerImageName(tag)) return yield* Effect.fail(failScan(`docker save manifest entry ${index} in ${tarPath} has invalid RepoTag ${String(tag)}`));
|
|
131
|
+
if (tags.has(tag)) return yield* Effect.fail(failScan(`docker save manifest in ${tarPath} repeats RepoTag ${tag}`));
|
|
132
|
+
tags.add(tag);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return tags;
|
|
136
|
+
});
|
|
137
|
+
const normalizeOciImageName = (value) => {
|
|
138
|
+
if (value.startsWith("docker.io/library/")) {
|
|
139
|
+
const localName = value.slice(18);
|
|
140
|
+
if (localName.startsWith("devstack-snapshot:") && isRestorableContainerImageName(localName)) return localName;
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
if (value.startsWith("devstack-snapshot:") && isRestorableContainerImageName(value)) return value;
|
|
144
|
+
return null;
|
|
145
|
+
};
|
|
146
|
+
const parseOciImageLayoutIndexTags = (bytes, tarPath) => Effect.gen(function* () {
|
|
147
|
+
const raw = yield* Effect.try({
|
|
148
|
+
try: () => JSON.parse(Buffer.from(bytes).toString("utf8")),
|
|
149
|
+
catch: (cause) => failScan(`docker save index.json in ${tarPath} is not valid JSON`, cause)
|
|
150
|
+
});
|
|
151
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return yield* Effect.fail(failScan(`docker save index.json in ${tarPath} is not an object`));
|
|
152
|
+
const manifests = raw.manifests;
|
|
153
|
+
if (!Array.isArray(manifests)) return yield* Effect.fail(failScan(`docker save index.json in ${tarPath} has non-array manifests`));
|
|
154
|
+
const tags = /* @__PURE__ */ new Set();
|
|
155
|
+
for (const [index, entry] of manifests.entries()) {
|
|
156
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return yield* Effect.fail(failScan(`docker save index manifest ${index} in ${tarPath} is not an object`));
|
|
157
|
+
const annotations = entry.annotations;
|
|
158
|
+
if (annotations === void 0 || annotations === null) continue;
|
|
159
|
+
if (typeof annotations !== "object" || Array.isArray(annotations)) return yield* Effect.fail(failScan(`docker save index manifest ${index} in ${tarPath} has invalid annotations`));
|
|
160
|
+
const imageName = annotations["io.containerd.image.name"];
|
|
161
|
+
const refName = annotations["org.opencontainers.image.ref.name"];
|
|
162
|
+
for (const value of [imageName, refName]) {
|
|
163
|
+
if (typeof value !== "string") continue;
|
|
164
|
+
const tag = normalizeOciImageName(value);
|
|
165
|
+
if (tag === null) continue;
|
|
166
|
+
if (tags.has(tag)) return yield* Effect.fail(failScan(`docker save index in ${tarPath} repeats RepoTag ${tag}`));
|
|
167
|
+
tags.add(tag);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return tags;
|
|
171
|
+
});
|
|
172
|
+
const readImageBundleTags = (fullTarPath, tarPath) => Effect.gen(function* () {
|
|
173
|
+
const fs = yield* FileSystem.FileSystem;
|
|
174
|
+
const state = {
|
|
175
|
+
buffer: new Uint8Array(0),
|
|
176
|
+
skipRemaining: 0,
|
|
177
|
+
manifestBytes: null,
|
|
178
|
+
indexBytes: null,
|
|
179
|
+
content: null
|
|
180
|
+
};
|
|
181
|
+
yield* fs.stream(fullTarPath).pipe(Stream.takeUntilEffect((chunk) => {
|
|
182
|
+
const result = processDockerSaveMetadataChunk(state, chunk);
|
|
183
|
+
if (result.error !== void 0) return Effect.fail(result.error);
|
|
184
|
+
return Effect.succeed(result.done);
|
|
185
|
+
}), Stream.runDrain, Effect.catch((cause) => cause instanceof ImageBundleTagScanError ? Effect.fail(cause) : Effect.fail(failScan(`read docker save metadata from ${fullTarPath} failed`, cause))));
|
|
186
|
+
if (state.manifestBytes !== null) return yield* parseDockerSaveManifestTags(state.manifestBytes, tarPath);
|
|
187
|
+
if (state.indexBytes !== null) return yield* parseOciImageLayoutIndexTags(state.indexBytes, tarPath);
|
|
188
|
+
return yield* Effect.fail(failScan(`docker save bundle ${tarPath} does not contain manifest.json or index.json`));
|
|
189
|
+
});
|
|
190
|
+
const verifyImageBundleTags = (tarPath, actualTags, expectedSnapshotTags) => {
|
|
191
|
+
const expected = /* @__PURE__ */ new Set();
|
|
192
|
+
const repeatedExpected = [];
|
|
193
|
+
for (const tag of expectedSnapshotTags) if (expected.has(tag)) repeatedExpected.push(tag);
|
|
194
|
+
else expected.add(tag);
|
|
195
|
+
if (repeatedExpected.length > 0) return Effect.fail(failScan(`docker save bundle ${tarPath} expected duplicate snapshot tags: ${repeatedExpected.join(", ")}`));
|
|
196
|
+
const missing = [...expected].filter((tag) => !actualTags.has(tag));
|
|
197
|
+
const unexpected = [...actualTags].filter((tag) => !expected.has(tag));
|
|
198
|
+
if (missing.length === 0 && unexpected.length === 0) return Effect.void;
|
|
199
|
+
const parts = [];
|
|
200
|
+
if (missing.length > 0) parts.push(`missing: ${missing.join(", ")}`);
|
|
201
|
+
if (unexpected.length > 0) parts.push(`unexpected: ${unexpected.join(", ")}`);
|
|
202
|
+
return Effect.fail(failScan(`docker save bundle ${tarPath} tags do not match snapshot metadata (${parts.join("; ")})`));
|
|
203
|
+
};
|
|
204
|
+
//#endregion
|
|
205
|
+
export { readImageBundleTags, verifyImageBundleTags };
|
|
206
|
+
|
|
207
|
+
//# sourceMappingURL=image-bundle-tags.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-bundle-tags.mjs","names":[],"sources":["../../../src/orchestrators/snapshot/image-bundle-tags.ts"],"sourcesContent":["import { Effect, FileSystem, Schema, Stream } from 'effect';\n\nimport { isRestorableContainerImageName } from './descriptor.ts';\n\nexport class ImageBundleTagScanError extends Schema.TaggedErrorClass<ImageBundleTagScanError>()(\n\t'SnapshotImageBundleTagScanError',\n\t{\n\t\tdetail: Schema.String,\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\nconst failScan = (detail: string, cause?: unknown): ImageBundleTagScanError =>\n\tnew ImageBundleTagScanError({\n\t\tdetail,\n\t\t...(cause === undefined ? {} : { cause }),\n\t});\n\nconst TAR_BLOCK_SIZE = 512;\nconst MAX_DOCKER_SAVE_MANIFEST_BYTES = 1024 * 1024;\n\nconst bytesToString = (bytes: Uint8Array): string => {\n\tconst nul = bytes.indexOf(0);\n\tconst end = nul === -1 ? bytes.length : nul;\n\treturn Buffer.from(bytes.subarray(0, end)).toString('utf8');\n};\n\nconst concatBytes = (a: Uint8Array, b: Uint8Array): Uint8Array => {\n\tif (a.length === 0) return b;\n\tif (b.length === 0) return a;\n\tconst out = new Uint8Array(a.length + b.length);\n\tout.set(a, 0);\n\tout.set(b, a.length);\n\treturn out;\n};\n\nconst consumeBytes = (buffer: Uint8Array, count: number): Uint8Array => buffer.subarray(count);\n\nconst isZeroBlock = (block: Uint8Array): boolean => block.every((byte) => byte === 0);\n\nconst parseTarSize = (header: Uint8Array): number | null => {\n\tconst raw = bytesToString(header.subarray(124, 136)).trim();\n\tif (raw === '') return 0;\n\tif (!/^[0-7]+$/.test(raw)) return null;\n\tconst parsed = Number.parseInt(raw, 8);\n\treturn Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : null;\n};\n\nconst tarPathFromHeader = (header: Uint8Array): string => {\n\tconst name = bytesToString(header.subarray(0, 100));\n\tconst prefix = bytesToString(header.subarray(345, 500));\n\treturn prefix === '' ? name : `${prefix}/${name}`;\n};\n\nconst normalizedTarEntryPath = (path: string): string => {\n\tlet normalized = path;\n\twhile (normalized.startsWith('./')) normalized = normalized.slice(2);\n\treturn normalized;\n};\n\ntype ImageBundleMetadataEntry = 'manifest.json' | 'index.json';\n\ninterface DockerSaveMetadataTarState {\n\tbuffer: Uint8Array;\n\tskipRemaining: number;\n\tmanifestBytes: Uint8Array | null;\n\tindexBytes: Uint8Array | null;\n\tcontent: {\n\t\treadonly entry: ImageBundleMetadataEntry;\n\t\treadonly size: number;\n\t\treadonly paddedSize: number;\n\t\treadonly chunks: Array<Uint8Array>;\n\t\treadonly contentBytesRead: number;\n\t\treadonly totalBytesRead: number;\n\t} | null;\n}\n\nconst processDockerSaveMetadataChunk = (\n\tstate: DockerSaveMetadataTarState,\n\tchunk: Uint8Array,\n): { readonly done: boolean; readonly error?: ImageBundleTagScanError } => {\n\tstate.buffer = concatBytes(state.buffer, chunk);\n\twhile (state.buffer.length > 0) {\n\t\tif (state.content !== null) {\n\t\t\tconst content = state.content;\n\t\t\tconst remaining = content.paddedSize - content.totalBytesRead;\n\t\t\tconst take = Math.min(remaining, state.buffer.length);\n\t\t\tconst contentTake = Math.max(0, Math.min(take, content.size - content.contentBytesRead));\n\t\t\tif (contentTake > 0) {\n\t\t\t\tcontent.chunks.push(state.buffer.subarray(0, contentTake));\n\t\t\t}\n\t\t\tstate.content = {\n\t\t\t\t...content,\n\t\t\t\tcontentBytesRead: content.contentBytesRead + contentTake,\n\t\t\t\ttotalBytesRead: content.totalBytesRead + take,\n\t\t\t};\n\t\t\tstate.buffer = consumeBytes(state.buffer, take);\n\t\t\tif (state.content.totalBytesRead === state.content.paddedSize) {\n\t\t\t\tconst completed = state.content;\n\t\t\t\tstate.content = null;\n\t\t\t\tconst entryBytes =\n\t\t\t\t\tcompleted.chunks.length === 1\n\t\t\t\t\t\t? completed.chunks[0]!\n\t\t\t\t\t\t: Buffer.concat(completed.chunks.map((entry) => Buffer.from(entry)));\n\t\t\t\tif (completed.entry === 'manifest.json') {\n\t\t\t\t\tstate.manifestBytes = entryBytes;\n\t\t\t\t\treturn { done: true };\n\t\t\t\t}\n\t\t\t\tstate.indexBytes = entryBytes;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (state.skipRemaining > 0) {\n\t\t\tconst take = Math.min(state.skipRemaining, state.buffer.length);\n\t\t\tstate.skipRemaining -= take;\n\t\t\tstate.buffer = consumeBytes(state.buffer, take);\n\t\t\tcontinue;\n\t\t}\n\t\tif (state.buffer.length < TAR_BLOCK_SIZE) return { done: false };\n\t\tconst header = state.buffer.subarray(0, TAR_BLOCK_SIZE);\n\t\tstate.buffer = consumeBytes(state.buffer, TAR_BLOCK_SIZE);\n\t\tif (isZeroBlock(header)) continue;\n\t\tconst size = parseTarSize(header);\n\t\tif (size === null) {\n\t\t\treturn { done: false, error: failScan('docker save bundle has invalid tar size') };\n\t\t}\n\t\tconst paddedSize = Math.ceil(size / TAR_BLOCK_SIZE) * TAR_BLOCK_SIZE;\n\t\tconst entryPath = normalizedTarEntryPath(tarPathFromHeader(header));\n\t\tif (entryPath === 'manifest.json' || entryPath === 'index.json') {\n\t\t\tif (size > MAX_DOCKER_SAVE_MANIFEST_BYTES) {\n\t\t\t\treturn {\n\t\t\t\t\tdone: false,\n\t\t\t\t\terror: failScan(`docker save ${entryPath} is too large`),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (size === 0) {\n\t\t\t\tif (entryPath === 'manifest.json') {\n\t\t\t\t\tstate.manifestBytes = new Uint8Array(0);\n\t\t\t\t\treturn { done: true };\n\t\t\t\t}\n\t\t\t\tstate.indexBytes = new Uint8Array(0);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tstate.content = {\n\t\t\t\tentry: entryPath,\n\t\t\t\tsize,\n\t\t\t\tpaddedSize,\n\t\t\t\tchunks: [],\n\t\t\t\tcontentBytesRead: 0,\n\t\t\t\ttotalBytesRead: 0,\n\t\t\t};\n\t\t\tcontinue;\n\t\t}\n\t\tstate.skipRemaining = paddedSize;\n\t}\n\treturn { done: false };\n};\n\nconst parseDockerSaveManifestTags = (\n\tbytes: Uint8Array,\n\ttarPath: string,\n): Effect.Effect<ReadonlySet<string>, ImageBundleTagScanError> =>\n\tEffect.gen(function* () {\n\t\tconst raw = yield* Effect.try({\n\t\t\ttry: () => JSON.parse(Buffer.from(bytes).toString('utf8')) as unknown,\n\t\t\tcatch: (cause) =>\n\t\t\t\tfailScan(`docker save manifest.json in ${tarPath} is not valid JSON`, cause),\n\t\t});\n\t\tif (!Array.isArray(raw)) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tfailScan(`docker save manifest.json in ${tarPath} is not an array`),\n\t\t\t);\n\t\t}\n\t\tconst tags = new Set<string>();\n\t\tfor (const [index, entry] of raw.entries()) {\n\t\t\tif (typeof entry !== 'object' || entry === null) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tfailScan(`docker save manifest entry ${index} in ${tarPath} is not an object`),\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst repoTags = (entry as { readonly RepoTags?: unknown }).RepoTags;\n\t\t\tif (repoTags === undefined || repoTags === null) continue;\n\t\t\tif (!Array.isArray(repoTags)) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tfailScan(`docker save manifest entry ${index} in ${tarPath} has non-array RepoTags`),\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (const tag of repoTags) {\n\t\t\t\tif (typeof tag !== 'string' || !isRestorableContainerImageName(tag)) {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\tfailScan(\n\t\t\t\t\t\t\t`docker save manifest entry ${index} in ${tarPath} has invalid RepoTag ${String(\n\t\t\t\t\t\t\t\ttag,\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\t\tif (tags.has(tag)) {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\tfailScan(`docker save manifest in ${tarPath} repeats RepoTag ${tag}`),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\ttags.add(tag);\n\t\t\t}\n\t\t}\n\t\treturn tags;\n\t});\n\nconst normalizeOciImageName = (value: string): string | null => {\n\tconst dockerLibraryPrefix = 'docker.io/library/';\n\tif (value.startsWith(dockerLibraryPrefix)) {\n\t\tconst localName = value.slice(dockerLibraryPrefix.length);\n\t\tif (localName.startsWith('devstack-snapshot:') && isRestorableContainerImageName(localName)) {\n\t\t\treturn localName;\n\t\t}\n\t\treturn null;\n\t}\n\tif (value.startsWith('devstack-snapshot:') && isRestorableContainerImageName(value)) {\n\t\treturn value;\n\t}\n\treturn null;\n};\n\nconst parseOciImageLayoutIndexTags = (\n\tbytes: Uint8Array,\n\ttarPath: string,\n): Effect.Effect<ReadonlySet<string>, ImageBundleTagScanError> =>\n\tEffect.gen(function* () {\n\t\tconst raw = yield* Effect.try({\n\t\t\ttry: () => JSON.parse(Buffer.from(bytes).toString('utf8')) as unknown,\n\t\t\tcatch: (cause) => failScan(`docker save index.json in ${tarPath} is not valid JSON`, cause),\n\t\t});\n\t\tif (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n\t\t\treturn yield* Effect.fail(failScan(`docker save index.json in ${tarPath} is not an object`));\n\t\t}\n\t\tconst manifests = (raw as { readonly manifests?: unknown }).manifests;\n\t\tif (!Array.isArray(manifests)) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tfailScan(`docker save index.json in ${tarPath} has non-array manifests`),\n\t\t\t);\n\t\t}\n\t\tconst tags = new Set<string>();\n\t\tfor (const [index, entry] of manifests.entries()) {\n\t\t\tif (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tfailScan(`docker save index manifest ${index} in ${tarPath} is not an object`),\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst annotations = (entry as { readonly annotations?: unknown }).annotations;\n\t\t\tif (annotations === undefined || annotations === null) continue;\n\t\t\tif (typeof annotations !== 'object' || Array.isArray(annotations)) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tfailScan(`docker save index manifest ${index} in ${tarPath} has invalid annotations`),\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst imageName = (annotations as { readonly ['io.containerd.image.name']?: unknown })[\n\t\t\t\t'io.containerd.image.name'\n\t\t\t];\n\t\t\tconst refName = (annotations as { readonly ['org.opencontainers.image.ref.name']?: unknown })[\n\t\t\t\t'org.opencontainers.image.ref.name'\n\t\t\t];\n\t\t\tfor (const value of [imageName, refName]) {\n\t\t\t\tif (typeof value !== 'string') continue;\n\t\t\t\tconst tag = normalizeOciImageName(value);\n\t\t\t\tif (tag === null) continue;\n\t\t\t\tif (tags.has(tag)) {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\tfailScan(`docker save index in ${tarPath} repeats RepoTag ${tag}`),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\ttags.add(tag);\n\t\t\t}\n\t\t}\n\t\treturn tags;\n\t});\n\nexport const readImageBundleTags = (\n\tfullTarPath: string,\n\ttarPath: string,\n): Effect.Effect<ReadonlySet<string>, ImageBundleTagScanError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst state: DockerSaveMetadataTarState = {\n\t\t\tbuffer: new Uint8Array(0),\n\t\t\tskipRemaining: 0,\n\t\t\tmanifestBytes: null,\n\t\t\tindexBytes: null,\n\t\t\tcontent: null,\n\t\t};\n\t\tyield* fs.stream(fullTarPath).pipe(\n\t\t\tStream.takeUntilEffect((chunk) => {\n\t\t\t\tconst result = processDockerSaveMetadataChunk(state, chunk);\n\t\t\t\tif (result.error !== undefined) return Effect.fail(result.error);\n\t\t\t\treturn Effect.succeed(result.done);\n\t\t\t}),\n\t\t\tStream.runDrain,\n\t\t\tEffect.catch((cause) =>\n\t\t\t\tcause instanceof ImageBundleTagScanError\n\t\t\t\t\t? Effect.fail(cause)\n\t\t\t\t\t: Effect.fail(failScan(`read docker save metadata from ${fullTarPath} failed`, cause)),\n\t\t\t),\n\t\t);\n\t\tif (state.manifestBytes !== null) {\n\t\t\treturn yield* parseDockerSaveManifestTags(state.manifestBytes, tarPath);\n\t\t}\n\t\tif (state.indexBytes !== null) {\n\t\t\treturn yield* parseOciImageLayoutIndexTags(state.indexBytes, tarPath);\n\t\t}\n\t\treturn yield* Effect.fail(\n\t\t\tfailScan(`docker save bundle ${tarPath} does not contain manifest.json or index.json`),\n\t\t);\n\t});\n\nexport const verifyImageBundleTags = (\n\ttarPath: string,\n\tactualTags: ReadonlySet<string>,\n\texpectedSnapshotTags: ReadonlyArray<string>,\n): Effect.Effect<void, ImageBundleTagScanError> => {\n\tconst expected = new Set<string>();\n\tconst repeatedExpected: string[] = [];\n\tfor (const tag of expectedSnapshotTags) {\n\t\tif (expected.has(tag)) {\n\t\t\trepeatedExpected.push(tag);\n\t\t} else {\n\t\t\texpected.add(tag);\n\t\t}\n\t}\n\tif (repeatedExpected.length > 0) {\n\t\treturn Effect.fail(\n\t\t\tfailScan(\n\t\t\t\t`docker save bundle ${tarPath} expected duplicate snapshot tags: ${repeatedExpected.join(\n\t\t\t\t\t', ',\n\t\t\t\t)}`,\n\t\t\t),\n\t\t);\n\t}\n\tconst missing = [...expected].filter((tag) => !actualTags.has(tag));\n\tconst unexpected = [...actualTags].filter((tag) => !expected.has(tag));\n\tif (missing.length === 0 && unexpected.length === 0) return Effect.void;\n\tconst parts: string[] = [];\n\tif (missing.length > 0) parts.push(`missing: ${missing.join(', ')}`);\n\tif (unexpected.length > 0) parts.push(`unexpected: ${unexpected.join(', ')}`);\n\treturn Effect.fail(\n\t\tfailScan(\n\t\t\t`docker save bundle ${tarPath} tags do not match snapshot metadata (${parts.join('; ')})`,\n\t\t),\n\t);\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { Effect, FileSystem } from "effect";
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { IntegrityFileSchema, SnapshotLayout, isSafeSnapshotRelativePath } from "./descriptor.mjs";
|
|
2
|
+
import { Effect, FileSystem, Schema, Stream } from "effect";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
//#region src/orchestrators/snapshot/integrity.ts
|
|
5
|
+
const SHA256_HEX = /^[a-f0-9]{64}$/;
|
|
6
|
+
const fail = (detail) => Effect.fail(new Error(detail));
|
|
7
|
+
const hashFile = (path) => Effect.gen(function* () {
|
|
8
|
+
const fs = yield* FileSystem.FileSystem;
|
|
9
|
+
const hash = createHash("sha256");
|
|
10
|
+
yield* Stream.runForEach(fs.stream(path), (chunk) => Effect.sync(() => {
|
|
11
|
+
hash.update(chunk);
|
|
12
|
+
})).pipe(Effect.catch((cause) => fail(`hash ${path} failed: ${String(cause)}`)));
|
|
13
|
+
return hash.digest("hex");
|
|
14
|
+
});
|
|
15
|
+
const collectArtifactFiles = (root) => Effect.gen(function* () {
|
|
16
|
+
const fs = yield* FileSystem.FileSystem;
|
|
17
|
+
const files = [];
|
|
18
|
+
const walk = (dir, prefix) => Effect.gen(function* () {
|
|
19
|
+
const names = yield* fs.readDirectory(dir).pipe(Effect.catch((cause) => fail(`readDirectory(${dir}) failed: ${String(cause)}`)));
|
|
20
|
+
for (const name of names) {
|
|
21
|
+
const relPath = prefix === "" ? name : `${prefix}/${name}`;
|
|
22
|
+
if (!isSafeSnapshotRelativePath(relPath)) return yield* fail(`unsafe artifact relative path: ${relPath}`);
|
|
23
|
+
const absPath = `${dir}/${name}`;
|
|
24
|
+
const stat = yield* fs.stat(absPath).pipe(Effect.catch((cause) => fail(`stat(${absPath}) failed: ${String(cause)}`)));
|
|
25
|
+
if (stat.type === "Directory") yield* walk(absPath, relPath);
|
|
26
|
+
else if (stat.type === "File") {
|
|
27
|
+
if (relPath !== SnapshotLayout.integrityFile) files.push(relPath);
|
|
28
|
+
} else return yield* fail(`snapshot artifact path is not a regular file: ${relPath}`);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
yield* walk(root, "");
|
|
32
|
+
return files.sort((a, b) => a.localeCompare(b));
|
|
33
|
+
});
|
|
34
|
+
const computeArtifactIntegrity = (root) => Effect.gen(function* () {
|
|
35
|
+
const hashes = {};
|
|
36
|
+
for (const relPath of yield* collectArtifactFiles(root)) hashes[relPath] = yield* hashFile(`${root}/${relPath}`);
|
|
37
|
+
return {
|
|
38
|
+
version: 1,
|
|
39
|
+
hashes
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
const writeArtifactIntegrity = (root) => Effect.gen(function* () {
|
|
43
|
+
const fs = yield* FileSystem.FileSystem;
|
|
44
|
+
const integrity = yield* computeArtifactIntegrity(root);
|
|
45
|
+
yield* fs.writeFileString(`${root}/${SnapshotLayout.integrityFile}`, JSON.stringify(integrity, null, 2)).pipe(Effect.catch((cause) => fail(`write ${SnapshotLayout.integrityFile} failed: ${String(cause)}`)));
|
|
46
|
+
return integrity;
|
|
47
|
+
});
|
|
48
|
+
const readIntegrity = (root) => Effect.gen(function* () {
|
|
49
|
+
const fs = yield* FileSystem.FileSystem;
|
|
50
|
+
const path = `${root}/${SnapshotLayout.integrityFile}`;
|
|
51
|
+
const text = yield* fs.readFileString(path).pipe(Effect.catch((cause) => fail(`read ${path} failed: ${String(cause)}`)));
|
|
52
|
+
const raw = yield* Effect.try({
|
|
53
|
+
try: () => JSON.parse(text),
|
|
54
|
+
catch: (cause) => /* @__PURE__ */ new Error(`${path} is not valid JSON: ${String(cause)}`)
|
|
55
|
+
});
|
|
56
|
+
const decoded = yield* Schema.decodeUnknownEffect(IntegrityFileSchema)(raw).pipe(Effect.catch((cause) => fail(`${path} failed schema decode: ${String(cause)}`)));
|
|
57
|
+
for (const [relPath, digest] of Object.entries(decoded.hashes)) {
|
|
58
|
+
if (!isSafeSnapshotRelativePath(relPath) || relPath === SnapshotLayout.integrityFile) return yield* fail(`unsafe integrity path: ${relPath}`);
|
|
59
|
+
if (!SHA256_HEX.test(digest)) return yield* fail(`invalid sha256 digest for ${relPath}`);
|
|
60
|
+
}
|
|
61
|
+
return decoded;
|
|
62
|
+
});
|
|
63
|
+
const verifyArtifactIntegrity = (root) => Effect.gen(function* () {
|
|
64
|
+
const expected = yield* readIntegrity(root);
|
|
65
|
+
const actual = yield* computeArtifactIntegrity(root);
|
|
66
|
+
const expectedPaths = Object.keys(expected.hashes).sort((a, b) => a.localeCompare(b));
|
|
67
|
+
const actualPaths = Object.keys(actual.hashes).sort((a, b) => a.localeCompare(b));
|
|
68
|
+
if (expectedPaths.join("\n") !== actualPaths.join("\n")) return yield* fail("snapshot integrity file list does not match artifact contents");
|
|
69
|
+
for (const relPath of expectedPaths) if (expected.hashes[relPath] !== actual.hashes[relPath]) return yield* fail(`snapshot integrity mismatch for ${relPath}`);
|
|
70
|
+
});
|
|
71
|
+
//#endregion
|
|
72
|
+
export { verifyArtifactIntegrity, writeArtifactIntegrity };
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=integrity.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrity.mjs","names":[],"sources":["../../../src/orchestrators/snapshot/integrity.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\n\nimport { Effect, FileSystem, Schema, Stream } from 'effect';\n\nimport {\n\tIntegrityFileSchema,\n\tSNAPSHOT_INTEGRITY_VERSION,\n\tSnapshotLayout,\n\tisSafeSnapshotRelativePath,\n\ttype IntegrityFile,\n} from './descriptor.ts';\n\nconst SHA256_HEX = /^[a-f0-9]{64}$/;\n\nconst fail = (detail: string): Effect.Effect<never, Error> => Effect.fail(new Error(detail));\n\nconst hashFile = (path: string): Effect.Effect<string, Error, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst hash = createHash('sha256');\n\t\tyield* Stream.runForEach(fs.stream(path), (chunk) =>\n\t\t\tEffect.sync(() => {\n\t\t\t\thash.update(chunk);\n\t\t\t}),\n\t\t).pipe(Effect.catch((cause) => fail(`hash ${path} failed: ${String(cause)}`)));\n\t\treturn hash.digest('hex');\n\t});\n\nconst collectArtifactFiles = (\n\troot: string,\n): Effect.Effect<ReadonlyArray<string>, Error, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst files: string[] = [];\n\t\tconst walk = (dir: string, prefix: string): Effect.Effect<void, Error> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst names = yield* fs\n\t\t\t\t\t.readDirectory(dir)\n\t\t\t\t\t.pipe(Effect.catch((cause) => fail(`readDirectory(${dir}) failed: ${String(cause)}`)));\n\t\t\t\tfor (const name of names) {\n\t\t\t\t\tconst relPath = prefix === '' ? name : `${prefix}/${name}`;\n\t\t\t\t\tif (!isSafeSnapshotRelativePath(relPath)) {\n\t\t\t\t\t\treturn yield* fail(`unsafe artifact relative path: ${relPath}`);\n\t\t\t\t\t}\n\t\t\t\t\tconst absPath = `${dir}/${name}`;\n\t\t\t\t\tconst stat = yield* fs\n\t\t\t\t\t\t.stat(absPath)\n\t\t\t\t\t\t.pipe(Effect.catch((cause) => fail(`stat(${absPath}) failed: ${String(cause)}`)));\n\t\t\t\t\tif (stat.type === 'Directory') {\n\t\t\t\t\t\tyield* walk(absPath, relPath);\n\t\t\t\t\t} else if (stat.type === 'File') {\n\t\t\t\t\t\tif (relPath !== SnapshotLayout.integrityFile) {\n\t\t\t\t\t\t\tfiles.push(relPath);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn yield* fail(`snapshot artifact path is not a regular file: ${relPath}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\tyield* walk(root, '');\n\t\treturn files.sort((a, b) => a.localeCompare(b));\n\t});\n\nexport const computeArtifactIntegrity = (\n\troot: string,\n): Effect.Effect<IntegrityFile, Error, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst hashes: Record<string, string> = {};\n\t\tfor (const relPath of yield* collectArtifactFiles(root)) {\n\t\t\thashes[relPath] = yield* hashFile(`${root}/${relPath}`);\n\t\t}\n\t\treturn { version: SNAPSHOT_INTEGRITY_VERSION, hashes };\n\t});\n\nexport const writeArtifactIntegrity = (\n\troot: string,\n): Effect.Effect<IntegrityFile, Error, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst integrity = yield* computeArtifactIntegrity(root);\n\t\tyield* fs\n\t\t\t.writeFileString(\n\t\t\t\t`${root}/${SnapshotLayout.integrityFile}`,\n\t\t\t\tJSON.stringify(integrity, null, 2),\n\t\t\t)\n\t\t\t.pipe(\n\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\tfail(`write ${SnapshotLayout.integrityFile} failed: ${String(cause)}`),\n\t\t\t\t),\n\t\t\t);\n\t\treturn integrity;\n\t});\n\nconst readIntegrity = (root: string): Effect.Effect<IntegrityFile, Error, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst path = `${root}/${SnapshotLayout.integrityFile}`;\n\t\tconst text = yield* fs\n\t\t\t.readFileString(path)\n\t\t\t.pipe(Effect.catch((cause) => fail(`read ${path} failed: ${String(cause)}`)));\n\t\tconst raw = yield* Effect.try({\n\t\t\ttry: () => JSON.parse(text) as unknown,\n\t\t\tcatch: (cause) => new Error(`${path} is not valid JSON: ${String(cause)}`),\n\t\t});\n\t\tconst decoded = yield* Schema.decodeUnknownEffect(IntegrityFileSchema)(raw).pipe(\n\t\t\tEffect.catch((cause) => fail(`${path} failed schema decode: ${String(cause)}`)),\n\t\t);\n\t\tfor (const [relPath, digest] of Object.entries(decoded.hashes)) {\n\t\t\tif (!isSafeSnapshotRelativePath(relPath) || relPath === SnapshotLayout.integrityFile) {\n\t\t\t\treturn yield* fail(`unsafe integrity path: ${relPath}`);\n\t\t\t}\n\t\t\tif (!SHA256_HEX.test(digest)) {\n\t\t\t\treturn yield* fail(`invalid sha256 digest for ${relPath}`);\n\t\t\t}\n\t\t}\n\t\treturn decoded;\n\t});\n\nexport const verifyArtifactIntegrity = (\n\troot: string,\n): Effect.Effect<void, Error, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst expected = yield* readIntegrity(root);\n\t\tconst actual = yield* computeArtifactIntegrity(root);\n\t\tconst expectedPaths = Object.keys(expected.hashes).sort((a, b) => a.localeCompare(b));\n\t\tconst actualPaths = Object.keys(actual.hashes).sort((a, b) => a.localeCompare(b));\n\t\tif (expectedPaths.join('\\n') !== actualPaths.join('\\n')) {\n\t\t\treturn yield* fail('snapshot integrity file list does not match artifact contents');\n\t\t}\n\t\tfor (const relPath of expectedPaths) {\n\t\t\tif (expected.hashes[relPath] !== actual.hashes[relPath]) {\n\t\t\t\treturn yield* fail(`snapshot integrity mismatch for ${relPath}`);\n\t\t\t}\n\t\t}\n\t});\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { Effect, FileSystem, Schema } from "effect";
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { SnapshotMetadataSchema } from "./descriptor.mjs";
|
|
2
|
+
import { SNAPSHOTS_DIR_NAME } from "./wipe.mjs";
|
|
3
|
+
import { Effect, FileSystem, Schema } from "effect";
|
|
4
|
+
//#region src/orchestrators/snapshot/prune.ts
|
|
5
|
+
var PrunePhaseError = class extends Schema.TaggedErrorClass()("SnapshotPrunePhaseError", {
|
|
6
|
+
phase: Schema.Literals([
|
|
7
|
+
"enumerate-catalog",
|
|
8
|
+
"read-meta",
|
|
9
|
+
"classify",
|
|
10
|
+
"sweep-images",
|
|
11
|
+
"sweep-directories"
|
|
12
|
+
]),
|
|
13
|
+
detail: Schema.String,
|
|
14
|
+
cause: Schema.optional(Schema.Defect)
|
|
15
|
+
}) {};
|
|
16
|
+
const failPhase = (phase, detail) => (cause) => Effect.fail(new PrunePhaseError({
|
|
17
|
+
phase,
|
|
18
|
+
detail,
|
|
19
|
+
cause
|
|
20
|
+
}));
|
|
21
|
+
const readMetaOpt = (dir) => Effect.gen(function* () {
|
|
22
|
+
const fs = yield* FileSystem.FileSystem;
|
|
23
|
+
const path = `${dir}/meta.json`;
|
|
24
|
+
if (!(yield* fs.exists(path).pipe(Effect.catch(() => Effect.succeed(false))))) return null;
|
|
25
|
+
const text = yield* fs.readFileString(path).pipe(Effect.catch(failPhase("read-meta", `read ${path} failed`)));
|
|
26
|
+
const raw = yield* Effect.try({
|
|
27
|
+
try: () => JSON.parse(text),
|
|
28
|
+
catch: () => null
|
|
29
|
+
}).pipe(Effect.catch(() => Effect.succeed(null)));
|
|
30
|
+
if (raw === null) return null;
|
|
31
|
+
return yield* Schema.decodeUnknownEffect(SnapshotMetadataSchema)(raw).pipe(Effect.catch(() => Effect.succeed(null)));
|
|
32
|
+
});
|
|
33
|
+
/** Build the hints document a classifier consumes. Snapshot prune is
|
|
34
|
+
* age- + identity-based by default; plugin-specific hints flow
|
|
35
|
+
* through `pluginHints`. */
|
|
36
|
+
const hintsFor = (meta) => ({
|
|
37
|
+
heartbeatAt: meta.createdAt,
|
|
38
|
+
claimPid: null,
|
|
39
|
+
claimStartTime: null,
|
|
40
|
+
pluginHints: {
|
|
41
|
+
snapshotId: meta.id,
|
|
42
|
+
identity: meta.identity,
|
|
43
|
+
participants: meta.participants
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
/**
|
|
47
|
+
* Walk the snapshot catalog, dispatch each artifact's hints through
|
|
48
|
+
* the contributed classifiers, and reap anything classified
|
|
49
|
+
* `abandoned`. Concurrent sweeps over the same catalog are not
|
|
50
|
+
* supported (caller holds `snapshot.reservation` or `stack.lock`).
|
|
51
|
+
*
|
|
52
|
+
* Also removes managed images via the runtime adapter's label-filtered
|
|
53
|
+
* image cleanup.
|
|
54
|
+
*/
|
|
55
|
+
const runPrune = (inputs) => Effect.gen(function* () {
|
|
56
|
+
const fs = yield* FileSystem.FileSystem;
|
|
57
|
+
yield* Effect.annotateCurrentSpan({ "devstack.snapshot.phase": "prune" });
|
|
58
|
+
const catalogDir = `${inputs.stackRoot}/${SNAPSHOTS_DIR_NAME}`;
|
|
59
|
+
if (!(yield* fs.exists(catalogDir).pipe(Effect.catch(() => Effect.succeed(false))))) return {
|
|
60
|
+
inspected: 0,
|
|
61
|
+
reaped: [],
|
|
62
|
+
imagesSwept: 0
|
|
63
|
+
};
|
|
64
|
+
const ids = yield* fs.readDirectory(catalogDir).pipe(Effect.catch(failPhase("enumerate-catalog", `readdir ${catalogDir} failed`)));
|
|
65
|
+
const reaped = [];
|
|
66
|
+
for (const id of ids) {
|
|
67
|
+
const dir = `${catalogDir}/${id}`;
|
|
68
|
+
const meta = yield* readMetaOpt(dir);
|
|
69
|
+
if (meta === null) {
|
|
70
|
+
yield* fs.remove(dir, {
|
|
71
|
+
recursive: true,
|
|
72
|
+
force: true
|
|
73
|
+
}).pipe(Effect.catch(failPhase("sweep-directories", `remove ${dir} failed`)));
|
|
74
|
+
reaped.push({
|
|
75
|
+
id,
|
|
76
|
+
classification: "abandoned"
|
|
77
|
+
});
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const hints = hintsFor(meta);
|
|
81
|
+
let classification = "alive";
|
|
82
|
+
for (const { decl } of inputs.classifiers) {
|
|
83
|
+
const c = yield* decl.classify(hints).pipe(Effect.catch(() => Effect.succeed("alive")));
|
|
84
|
+
if (c !== "alive") {
|
|
85
|
+
classification = c;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (classification === "abandoned") {
|
|
90
|
+
yield* fs.remove(dir, {
|
|
91
|
+
recursive: true,
|
|
92
|
+
force: true
|
|
93
|
+
}).pipe(Effect.catch(failPhase("sweep-directories", `remove ${dir} failed`)));
|
|
94
|
+
reaped.push({
|
|
95
|
+
id,
|
|
96
|
+
classification
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const imagesSwept = yield* inputs.runtime.removeManagedImages(inputs.imageLabelFilter).pipe(Effect.catch(failPhase("sweep-images", `image sweep failed`)));
|
|
101
|
+
return {
|
|
102
|
+
inspected: ids.length,
|
|
103
|
+
reaped,
|
|
104
|
+
imagesSwept
|
|
105
|
+
};
|
|
106
|
+
}).pipe(Effect.withSpan("orchestrator.snapshot.prune"));
|
|
107
|
+
//#endregion
|
|
108
|
+
export { runPrune };
|
|
109
|
+
|
|
110
|
+
//# sourceMappingURL=prune.mjs.map
|