@opengeni/runtime 2.5.2-canary.0 → 2.5.2

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.
@@ -24,15 +24,6 @@ export type {
24
24
 
25
25
  import type { Settings } from "@opengeni/config";
26
26
  import { collectSandboxEnvironment, parseExposedPorts } from "@opengeni/config";
27
- export type { WorkspaceArchiveSpool, VerifiedHostWorkspaceArchive } from "./archive-spool";
28
- import type { WorkspaceArchiveSpool } from "./archive-spool";
29
- import { restoreHostWorkspaceArchive } from "./host-archive-spool";
30
- export {
31
- captureWorkspaceArchiveForStorage,
32
- disposeWorkspaceArchive,
33
- type VerifiedWorkspaceArchivePayload,
34
- } from "./workspace-archive";
35
-
36
27
  import {
37
28
  BROWSER_CONTROL_PORT,
38
29
  DESKTOP_STREAM_PORT,
@@ -40,9 +31,6 @@ import {
40
31
  TERMINAL_STREAM_PORT,
41
32
  omitInlineWorkspaceArchiveWhenObjectRefPresent,
42
33
  parseWorkspaceArchiveObjectRef,
43
- parseWorkspaceArchiveDescriptor,
44
- type WorkspaceArchiveObjectRef,
45
- type TarWorkspaceArchiveDescriptor,
46
34
  type SandboxBackend,
47
35
  type SandboxProviderContinuityRecovery,
48
36
  } from "@opengeni/contracts";
@@ -1688,16 +1676,9 @@ async function terminateCreatedSandbox(
1688
1676
  }
1689
1677
 
1690
1678
  type ModalNativeWorkspacePersistence = "snapshot_filesystem" | "snapshot_directory";
1691
- type RestoreWorkspaceArchive =
1692
- | VerifiedWorkspaceArchive
1693
- | {
1694
- kind: "host_spool";
1695
- descriptor: TarWorkspaceArchiveDescriptor;
1696
- ref: WorkspaceArchiveObjectRef;
1697
- };
1698
1679
 
1699
1680
  function modalWorkspacePersistenceForRestore(
1700
- archive: RestoreWorkspaceArchive | null,
1681
+ archive: VerifiedWorkspaceArchive | null,
1701
1682
  ): ModalNativeWorkspacePersistence | null {
1702
1683
  if (archive?.kind !== "provider_snapshot") return null;
1703
1684
  const snapshot = archive.nativeSnapshot;
@@ -1726,7 +1707,7 @@ function modalWorkspacePersistenceForRestore(
1726
1707
  function settingsForWorkspaceArchiveRestore(
1727
1708
  backend: SandboxBackend,
1728
1709
  settings: Settings,
1729
- archive: RestoreWorkspaceArchive | null,
1710
+ archive: VerifiedWorkspaceArchive | null,
1730
1711
  ): Settings {
1731
1712
  if (backend !== "modal") return settings;
1732
1713
  const workspacePersistence = modalWorkspacePersistenceForRestore(archive);
@@ -1754,7 +1735,6 @@ export async function establishSandboxSessionFromEnvelope(
1754
1735
  opts: {
1755
1736
  sessionId: string;
1756
1737
  recovery: "create-or-restore" | "resume-only";
1757
- loadHostWorkspaceArchive?: (ref: WorkspaceArchiveObjectRef) => Promise<WorkspaceArchiveSpool>;
1758
1738
  backendOverride?: SandboxBackend;
1759
1739
  environment?: Record<string, string>;
1760
1740
  onSandboxCreated?: SandboxCreatedCallback;
@@ -1837,7 +1817,6 @@ export async function establishSandboxSessionFromEnvelope(
1837
1817
  envelopeSessionState && typeof envelopeSessionState === "object"
1838
1818
  ? (envelopeSessionState as {
1839
1819
  workspaceArchive?: unknown;
1840
- workspaceArchiveRef?: unknown;
1841
1820
  workspaceArchiveMeta?: unknown;
1842
1821
  })
1843
1822
  : undefined;
@@ -1859,217 +1838,220 @@ export async function establishSandboxSessionFromEnvelope(
1859
1838
  // Parse/verify lazily: a warm resume-by-id does not consume its retained
1860
1839
  // archive, so a legacy live box remains resumable. Creation/restoration is
1861
1840
  // the boundary that requires complete durable metadata.
1862
- let workspaceArchive: RestoreWorkspaceArchive | null = readVerifiedWorkspaceArchive(
1841
+ const workspaceArchive: VerifiedWorkspaceArchive | null = readVerifiedWorkspaceArchive(
1863
1842
  workspaceArchiveBase64,
1864
1843
  workspaceArchiveMetadata,
1865
1844
  );
1866
- const objectRef = parseWorkspaceArchiveObjectRef(archiveState?.workspaceArchiveRef);
1867
- const objectDescriptor = parseWorkspaceArchiveDescriptor(workspaceArchiveMetadata);
1868
- if (
1869
- process.platform === "linux" &&
1870
- objectRef &&
1871
- objectDescriptor?.version === 1 &&
1872
- objectDescriptor.workspace.projection === "sdk_local_archive_v1" &&
1873
- opts.loadHostWorkspaceArchive
1874
- ) {
1875
- if (
1876
- (backend !== "local" && backend !== "docker") ||
1877
- objectRef.sha256 !== objectDescriptor.archiveSha256 ||
1878
- objectRef.bytes !== objectDescriptor.archiveBytes
1879
- ) {
1880
- throw new WorkspaceArchiveIntegrityError(
1881
- "archive_metadata_invalid",
1882
- "Host archive object reference does not match its provider and descriptor",
1883
- );
1884
- }
1885
- workspaceArchive = { kind: "host_spool", descriptor: objectDescriptor, ref: objectRef };
1886
- }
1887
- if (objectRef && !workspaceArchive) {
1888
- throw new WorkspaceArchiveIntegrityError(
1889
- "archive_hydration_failed",
1890
- "Selected workspace archive object has no supported restore loader",
1845
+ // The selected native artifact is the durable authority for its restore
1846
+ // protocol. The process setting governs new sandboxes only: a mode rollout
1847
+ // must not make an older, verified Modal snapshot impossible to hydrate.
1848
+ const restoreSettings = settingsForWorkspaceArchiveRestore(backend, settings, workspaceArchive);
1849
+ const restoreClient =
1850
+ restoreSettings === settings
1851
+ ? client
1852
+ : ((opts.clientFactory
1853
+ ? opts.clientFactory(backend, restoreSettings, environment)
1854
+ : createSandboxClientForBackend(
1855
+ backend,
1856
+ restoreSettings,
1857
+ environment,
1858
+ opts.metrics,
1859
+ )) as ResumeCapableClient | undefined);
1860
+ if (!restoreClient?.create) {
1861
+ throw new SandboxConfigError(
1862
+ backend,
1863
+ `Sandbox backend "${backend}" does not support fresh archive restoration`,
1891
1864
  );
1892
1865
  }
1893
- let hostSpool: WorkspaceArchiveSpool | undefined;
1866
+ let createdClient = restoreClient;
1867
+ const createStarted = Date.now();
1868
+ let restored: Awaited<ReturnType<NonNullable<typeof restoreClient.create>>>;
1894
1869
  try {
1895
- // Preserve the pre-create integrity boundary used by inline archives.
1896
- // A missing/corrupt object must not create a provider just to delete it.
1897
- if (workspaceArchive?.kind === "host_spool") {
1898
- hostSpool = await opts.loadHostWorkspaceArchive!(workspaceArchive.ref);
1899
- if (
1900
- hostSpool.sha256 !== workspaceArchive.descriptor.archiveSha256 ||
1901
- hostSpool.byteSize !== workspaceArchive.descriptor.archiveBytes
1902
- ) {
1903
- throw new WorkspaceArchiveIntegrityError(
1904
- "archive_hash_mismatch",
1905
- "Downloaded workspace archive does not match its descriptor",
1906
- );
1907
- }
1870
+ restored = await restoreClient.create({ manifest: createManifest });
1871
+ recordSandboxCreateMetric(
1872
+ opts.metrics,
1873
+ restoreClient.backendId,
1874
+ createImageSource,
1875
+ "completed",
1876
+ createStarted,
1877
+ );
1878
+ } catch (error) {
1879
+ recordSandboxCreateMetric(
1880
+ opts.metrics,
1881
+ restoreClient.backendId,
1882
+ createImageSource,
1883
+ "failed",
1884
+ createStarted,
1885
+ );
1886
+ recordSandboxProviderApiThrottleMetric(
1887
+ opts.metrics,
1888
+ restoreClient.backendId,
1889
+ "create",
1890
+ error,
1891
+ );
1892
+ if (
1893
+ createImageSource !== "provider_immutable" ||
1894
+ backend !== "modal" ||
1895
+ !isProviderSandboxNotFoundError(restoreClient.backendId, error)
1896
+ ) {
1897
+ throw error;
1898
+ }
1899
+ const fallbackBaseSettings =
1900
+ opts.logicalFallbackSettings ??
1901
+ (settings.modalImageRef ? { ...settings, modalImageId: undefined } : null);
1902
+ if (!fallbackBaseSettings) {
1903
+ // An ID-only logical base is supported. Without the exact pre-selection
1904
+ // settings, clearing the optimized ID would silently boot Modal's
1905
+ // default image, so fail closed instead of changing the rig base.
1906
+ throw error;
1908
1907
  }
1909
- // The selected native artifact is the durable authority for its restore
1910
- // protocol. The process setting governs new sandboxes only: a mode rollout
1911
- // must not make an older, verified Modal snapshot impossible to hydrate.
1912
- const restoreSettings = settingsForWorkspaceArchiveRestore(
1908
+ const fallbackSettings = settingsForWorkspaceArchiveRestore(
1913
1909
  backend,
1914
- settings,
1910
+ fallbackBaseSettings,
1915
1911
  workspaceArchive,
1916
1912
  );
1917
- const restoreClient =
1918
- restoreSettings === settings
1919
- ? client
1920
- : ((opts.clientFactory
1921
- ? opts.clientFactory(backend, restoreSettings, environment)
1922
- : createSandboxClientForBackend(
1923
- backend,
1924
- restoreSettings,
1925
- environment,
1926
- opts.metrics,
1927
- )) as ResumeCapableClient | undefined);
1928
- if (!restoreClient?.create) {
1913
+ await ensureModalRegistryImage(fallbackSettings);
1914
+ const fallbackClient = (
1915
+ opts.clientFactory
1916
+ ? opts.clientFactory(backend, fallbackSettings, environment)
1917
+ : createSandboxClientForBackend(backend, fallbackSettings, environment, opts.metrics)
1918
+ ) as ResumeCapableClient | undefined;
1919
+ if (!fallbackClient?.create) {
1929
1920
  throw new SandboxConfigError(
1930
1921
  backend,
1931
- `Sandbox backend "${backend}" does not support fresh archive restoration`,
1922
+ "Modal logical-image fallback does not support fresh creation",
1932
1923
  );
1933
1924
  }
1934
- let createdClient = restoreClient;
1935
- const createStarted = Date.now();
1936
- let restored: Awaited<ReturnType<NonNullable<typeof restoreClient.create>>>;
1925
+ const fallbackStarted = Date.now();
1937
1926
  try {
1938
- restored = await restoreClient.create({ manifest: createManifest });
1927
+ restored = await fallbackClient.create({ manifest: createManifest });
1939
1928
  recordSandboxCreateMetric(
1940
1929
  opts.metrics,
1941
- restoreClient.backendId,
1942
- createImageSource,
1930
+ fallbackClient.backendId,
1931
+ "logical",
1943
1932
  "completed",
1944
- createStarted,
1933
+ fallbackStarted,
1945
1934
  );
1946
- } catch (error) {
1935
+ createdClient = fallbackClient;
1936
+ } catch (fallbackError) {
1947
1937
  recordSandboxCreateMetric(
1948
1938
  opts.metrics,
1949
- restoreClient.backendId,
1950
- createImageSource,
1939
+ fallbackClient.backendId,
1940
+ "logical",
1951
1941
  "failed",
1952
- createStarted,
1942
+ fallbackStarted,
1953
1943
  );
1954
1944
  recordSandboxProviderApiThrottleMetric(
1955
1945
  opts.metrics,
1956
- restoreClient.backendId,
1946
+ fallbackClient.backendId,
1957
1947
  "create",
1958
- error,
1959
- );
1960
- if (
1961
- createImageSource !== "provider_immutable" ||
1962
- backend !== "modal" ||
1963
- !isProviderSandboxNotFoundError(restoreClient.backendId, error)
1964
- ) {
1965
- throw error;
1966
- }
1967
- const fallbackBaseSettings =
1968
- opts.logicalFallbackSettings ??
1969
- (settings.modalImageRef ? { ...settings, modalImageId: undefined } : null);
1970
- if (!fallbackBaseSettings) {
1971
- // An ID-only logical base is supported. Without the exact pre-selection
1972
- // settings, clearing the optimized ID would silently boot Modal's
1973
- // default image, so fail closed instead of changing the rig base.
1974
- throw error;
1975
- }
1976
- const fallbackSettings = settingsForWorkspaceArchiveRestore(
1977
- backend,
1978
- fallbackBaseSettings,
1979
- workspaceArchive,
1948
+ fallbackError,
1980
1949
  );
1981
- await ensureModalRegistryImage(fallbackSettings);
1982
- const fallbackClient = (
1983
- opts.clientFactory
1984
- ? opts.clientFactory(backend, fallbackSettings, environment)
1985
- : createSandboxClientForBackend(backend, fallbackSettings, environment, opts.metrics)
1986
- ) as ResumeCapableClient | undefined;
1987
- if (!fallbackClient?.create) {
1988
- throw new SandboxConfigError(
1989
- backend,
1990
- "Modal logical-image fallback does not support fresh creation",
1991
- );
1992
- }
1993
- const fallbackStarted = Date.now();
1994
- try {
1995
- restored = await fallbackClient.create({ manifest: createManifest });
1996
- recordSandboxCreateMetric(
1997
- opts.metrics,
1998
- fallbackClient.backendId,
1999
- "logical",
2000
- "completed",
2001
- fallbackStarted,
2002
- );
2003
- createdClient = fallbackClient;
2004
- } catch (fallbackError) {
2005
- recordSandboxCreateMetric(
2006
- opts.metrics,
2007
- fallbackClient.backendId,
2008
- "logical",
2009
- "failed",
2010
- fallbackStarted,
2011
- );
2012
- recordSandboxProviderApiThrottleMetric(
2013
- opts.metrics,
2014
- fallbackClient.backendId,
2015
- "create",
2016
- fallbackError,
2017
- );
2018
- throw fallbackError;
2019
- }
1950
+ throw fallbackError;
2020
1951
  }
2021
- let restoredState = (restored as { state?: unknown }).state;
2022
- const restoredInstanceId = readInstanceId(backend, restored);
2023
- if (!restoredInstanceId) {
1952
+ }
1953
+ let restoredState = (restored as { state?: unknown }).state;
1954
+ const restoredInstanceId = readInstanceId(backend, restored);
1955
+ if (!restoredInstanceId) {
1956
+ await terminateCreatedSandbox(createdClient, restored, restoredState);
1957
+ throw new SandboxConfigError(
1958
+ backend,
1959
+ `Sandbox backend "${backend}" created a handle without its declared provider identity`,
1960
+ );
1961
+ }
1962
+ let established: EstablishedSandboxSession = {
1963
+ client: createdClient,
1964
+ session: restored,
1965
+ sessionState: restoredState ?? resumeFallbackState,
1966
+ instanceId: restoredInstanceId,
1967
+ backendId: createdClient.backendId,
1968
+ };
1969
+ if (opts.onSandboxCreated) {
1970
+ try {
1971
+ await opts.onSandboxCreated(established);
1972
+ } catch (createCallbackError) {
1973
+ await terminateCreatedSandbox(createdClient, restored, restoredState);
1974
+ throw createCallbackError;
1975
+ }
1976
+ }
1977
+ let hydrationApplied = false;
1978
+ if (workspaceArchive) {
1979
+ const hydrate = (restored as { hydrateWorkspace?: (data: Uint8Array) => Promise<void> })
1980
+ .hydrateWorkspace;
1981
+ if (typeof hydrate !== "function") {
2024
1982
  await terminateCreatedSandbox(createdClient, restored, restoredState);
1983
+ throw new WorkspaceArchiveIntegrityError(
1984
+ "archive_hydration_failed",
1985
+ `sandbox backend ${createdClient.backendId} cannot hydrate selected archive revision ${workspaceArchive.descriptor.revision}`,
1986
+ );
1987
+ }
1988
+ try {
1989
+ // hydrateWorkspace may internally replace the underlying box.
1990
+ await hydrate.call(restored, workspaceArchive.bytes);
1991
+ } catch (error) {
1992
+ await terminateCreatedSandbox(
1993
+ createdClient,
1994
+ restored,
1995
+ (restored as { state?: unknown }).state,
1996
+ );
1997
+ if (error instanceof WorkspaceArchiveIntegrityError) throw error;
1998
+ throw new WorkspaceArchiveIntegrityError(
1999
+ "archive_hydration_failed",
2000
+ `failed to hydrate selected workspace archive revision ${workspaceArchive.descriptor.revision}`,
2001
+ { retryable: true, cause: error },
2002
+ );
2003
+ }
2004
+ // hydrateWorkspace may replace the provider box (Modal's native snapshot
2005
+ // restore does this). Attribute the newly-active identity immediately,
2006
+ // before restore-state marking, fingerprint verification, or any caller
2007
+ // can publish the box warm. The callback is the durable lease/tagging
2008
+ // boundary; if it cannot persist the replacement, the caller fails closed
2009
+ // and this exact replacement is terminated below.
2010
+ const hydratedState = (restored as { state?: unknown }).state;
2011
+ const hydratedInstanceId = readInstanceId(backend, restored);
2012
+ if (!hydratedInstanceId) {
2013
+ await terminateCreatedSandbox(createdClient, restored, hydratedState);
2025
2014
  throw new SandboxConfigError(
2026
2015
  backend,
2027
- `Sandbox backend "${backend}" created a handle without its declared provider identity`,
2016
+ `Sandbox backend "${backend}" hydrated a handle without its declared provider identity`,
2028
2017
  );
2029
2018
  }
2030
- let established: EstablishedSandboxSession = {
2031
- client: createdClient,
2032
- session: restored,
2033
- sessionState: restoredState ?? resumeFallbackState,
2034
- instanceId: restoredInstanceId,
2035
- backendId: createdClient.backendId,
2036
- };
2037
- if (opts.onSandboxCreated) {
2038
- try {
2039
- await opts.onSandboxCreated(established);
2040
- } catch (createCallbackError) {
2041
- await terminateCreatedSandbox(createdClient, restored, restoredState);
2042
- throw createCallbackError;
2019
+ if (hydratedInstanceId !== established.instanceId) {
2020
+ established = {
2021
+ client: createdClient,
2022
+ session: restored,
2023
+ sessionState: hydratedState ?? resumeFallbackState,
2024
+ instanceId: hydratedInstanceId,
2025
+ backendId: createdClient.backendId,
2026
+ };
2027
+ if (opts.onSandboxCreated) {
2028
+ try {
2029
+ await opts.onSandboxCreated(established);
2030
+ } catch (createCallbackError) {
2031
+ await terminateCreatedSandbox(createdClient, restored, hydratedState);
2032
+ throw createCallbackError;
2033
+ }
2043
2034
  }
2044
2035
  }
2045
- let hydrationApplied = false;
2046
- if (workspaceArchive) {
2047
- const hydrate = (restored as { hydrateWorkspace?: (data: Uint8Array) => Promise<void> })
2048
- .hydrateWorkspace;
2049
- if (workspaceArchive.kind !== "host_spool" && typeof hydrate !== "function") {
2050
- await terminateCreatedSandbox(createdClient, restored, restoredState);
2051
- throw new WorkspaceArchiveIntegrityError(
2052
- "archive_hydration_failed",
2053
- `sandbox backend ${createdClient.backendId} cannot hydrate selected archive revision ${workspaceArchive.descriptor.revision}`,
2036
+ if (opts.onWorkspaceRestoreVerifying) {
2037
+ try {
2038
+ await opts.onWorkspaceRestoreVerifying(workspaceArchive.descriptor);
2039
+ } catch (error) {
2040
+ await terminateCreatedSandbox(
2041
+ createdClient,
2042
+ restored,
2043
+ (restored as { state?: unknown }).state,
2054
2044
  );
2045
+ throw error;
2055
2046
  }
2047
+ }
2048
+ // Native provider snapshots are restored by their exact opaque receipt.
2049
+ // OpenGeni verifies receipt identity/hash before hydration, but it must not
2050
+ // impose a tar/inode equivalence contract on the provider's filesystem
2051
+ // image. Tar archives remain content-verified after hydration.
2052
+ if (workspaceArchive.kind === "tar") {
2056
2053
  try {
2057
- // hydrateWorkspace may internally replace the underlying box.
2058
- if (workspaceArchive.kind === "host_spool") {
2059
- // This is the exact newly-created, unpublished destination. The lease
2060
- // restore fence must exclude admitted writers until verification ends;
2061
- // descriptor-relative filesystem access alone is not writer isolation.
2062
- const root = (restored as { state?: { workspaceRootPath?: unknown } }).state
2063
- ?.workspaceRootPath;
2064
- if (typeof root !== "string")
2065
- throw new WorkspaceArchiveIntegrityError(
2066
- "archive_hydration_failed",
2067
- "Host archive restore has no exact workspace root",
2068
- );
2069
- await restoreHostWorkspaceArchive(root, hostSpool!, {
2070
- archiveLimits: Reflect.get(restored as object, "archiveLimits"),
2071
- });
2072
- } else await hydrate!.call(restored, workspaceArchive.bytes);
2054
+ await verifyRestoredWorkspace(restored, workspaceArchive.descriptor);
2073
2055
  } catch (error) {
2074
2056
  await terminateCreatedSandbox(
2075
2057
  createdClient,
@@ -2078,106 +2060,35 @@ export async function establishSandboxSessionFromEnvelope(
2078
2060
  );
2079
2061
  if (error instanceof WorkspaceArchiveIntegrityError) throw error;
2080
2062
  throw new WorkspaceArchiveIntegrityError(
2081
- "archive_hydration_failed",
2082
- `failed to hydrate selected workspace archive revision ${workspaceArchive.descriptor.revision}`,
2083
- { retryable: true, cause: error },
2063
+ "workspace_fingerprint_unavailable",
2064
+ `failed to verify selected workspace archive revision ${workspaceArchive.descriptor.revision}`,
2065
+ { retryable: true },
2084
2066
  );
2085
2067
  }
2086
- // hydrateWorkspace may replace the provider box (Modal's native snapshot
2087
- // restore does this). Attribute the newly-active identity immediately,
2088
- // before restore-state marking, fingerprint verification, or any caller
2089
- // can publish the box warm. The callback is the durable lease/tagging
2090
- // boundary; if it cannot persist the replacement, the caller fails closed
2091
- // and this exact replacement is terminated below.
2092
- const hydratedState = (restored as { state?: unknown }).state;
2093
- const hydratedInstanceId = readInstanceId(backend, restored);
2094
- if (!hydratedInstanceId) {
2095
- await terminateCreatedSandbox(createdClient, restored, hydratedState);
2096
- throw new SandboxConfigError(
2097
- backend,
2098
- `Sandbox backend "${backend}" hydrated a handle without its declared provider identity`,
2099
- );
2100
- }
2101
- if (hydratedInstanceId !== established.instanceId) {
2102
- established = {
2103
- client: createdClient,
2104
- session: restored,
2105
- sessionState: hydratedState ?? resumeFallbackState,
2106
- instanceId: hydratedInstanceId,
2107
- backendId: createdClient.backendId,
2108
- };
2109
- if (opts.onSandboxCreated) {
2110
- try {
2111
- await opts.onSandboxCreated(established);
2112
- } catch (createCallbackError) {
2113
- await terminateCreatedSandbox(createdClient, restored, hydratedState);
2114
- throw createCallbackError;
2115
- }
2116
- }
2117
- }
2118
- if (opts.onWorkspaceRestoreVerifying) {
2119
- try {
2120
- await opts.onWorkspaceRestoreVerifying(workspaceArchive.descriptor);
2121
- } catch (error) {
2122
- await terminateCreatedSandbox(
2123
- createdClient,
2124
- restored,
2125
- (restored as { state?: unknown }).state,
2126
- );
2127
- throw error;
2128
- }
2129
- }
2130
- // Native provider snapshots are restored by their exact opaque receipt.
2131
- // OpenGeni verifies receipt identity/hash before hydration, but it must not
2132
- // impose a tar/inode equivalence contract on the provider's filesystem
2133
- // image. Tar archives remain content-verified after hydration.
2134
- if (workspaceArchive.kind === "tar" || workspaceArchive.kind === "host_spool") {
2135
- try {
2136
- await verifyRestoredWorkspace(restored, workspaceArchive.descriptor);
2137
- } catch (error) {
2138
- await terminateCreatedSandbox(
2139
- createdClient,
2140
- restored,
2141
- (restored as { state?: unknown }).state,
2142
- );
2143
- if (error instanceof WorkspaceArchiveIntegrityError) throw error;
2144
- throw new WorkspaceArchiveIntegrityError(
2145
- "workspace_fingerprint_unavailable",
2146
- `failed to verify selected workspace archive revision ${workspaceArchive.descriptor.revision}`,
2147
- { retryable: true },
2148
- );
2149
- }
2150
- }
2151
- hydrationApplied = true;
2152
- console.info(
2153
- `[sandbox] cold-restore applied ${workspaceArchive.kind === "provider_snapshot" ? "native snapshot receipt" : "verified tar archive"} revision ${workspaceArchive.descriptor.revision}`,
2154
- );
2155
2068
  }
2156
- restoredState = (restored as { state?: unknown }).state;
2157
- const finalInstanceId = readInstanceId(backend, restored);
2158
- if (!finalInstanceId) {
2159
- await terminateCreatedSandbox(createdClient, restored, restoredState);
2160
- throw new SandboxConfigError(
2161
- backend,
2162
- `Sandbox backend "${backend}" returned a handle without its declared provider identity`,
2163
- );
2164
- }
2165
- return {
2166
- client: createdClient,
2167
- session: restored,
2168
- sessionState: restoredState ?? resumeFallbackState,
2169
- instanceId: finalInstanceId,
2170
- backendId: createdClient.backendId,
2171
- origin: hydrationApplied ? ("restored" as const) : ("created" as const),
2172
- ...(workspaceArchive ? { restoredArchive: workspaceArchive.descriptor } : {}),
2173
- };
2174
- } finally {
2175
- await hostSpool?.dispose().catch(() => {
2176
- // Cleanup must not discard a successfully established provider handle
2177
- // or replace the authoritative restore failure with a filesystem error.
2178
- console.warn("workspace archive download spool cleanup failed");
2179
- });
2069
+ hydrationApplied = true;
2070
+ console.info(
2071
+ `[sandbox] cold-restore applied ${workspaceArchive.kind === "provider_snapshot" ? "native snapshot receipt" : "verified tar archive"} revision ${workspaceArchive.descriptor.revision}`,
2072
+ );
2180
2073
  }
2074
+ restoredState = (restored as { state?: unknown }).state;
2075
+ const finalInstanceId = readInstanceId(backend, restored);
2076
+ if (!finalInstanceId) {
2077
+ await terminateCreatedSandbox(createdClient, restored, restoredState);
2078
+ throw new SandboxConfigError(
2079
+ backend,
2080
+ `Sandbox backend "${backend}" returned a handle without its declared provider identity`,
2081
+ );
2082
+ }
2083
+ return {
2084
+ client: createdClient,
2085
+ session: restored,
2086
+ sessionState: restoredState ?? resumeFallbackState,
2087
+ instanceId: finalInstanceId,
2088
+ backendId: createdClient.backendId,
2089
+ origin: hydrationApplied ? ("restored" as const) : ("created" as const),
2090
+ ...(workspaceArchive ? { restoredArchive: workspaceArchive.descriptor } : {}),
2091
+ };
2181
2092
  };
2182
2093
 
2183
2094
  // Does the envelope carry a RESUMABLE box id (warm reattach), or only a
@@ -14,8 +14,6 @@ import {
14
14
  type WorkspaceTreeFingerprint,
15
15
  } from "@opengeni/contracts";
16
16
  import { withSandboxProviderCapture } from "./provider-operation-gate";
17
- import type { VerifiedHostWorkspaceArchive } from "./archive-spool";
18
- import { captureHostWorkspaceArchive, fingerprintHostWorkspace } from "./host-archive-spool";
19
17
 
20
18
  export {
21
19
  WORKSPACE_ARCHIVE_DESCRIPTOR_VERSION,
@@ -38,55 +36,6 @@ export type VerifiedWorkspaceArchive = {
38
36
  nativeSnapshot?: NativeSnapshotRef;
39
37
  };
40
38
 
41
- export type VerifiedWorkspaceArchivePayload =
42
- | VerifiedWorkspaceArchive
43
- | VerifiedHostWorkspaceArchive;
44
-
45
- /** Host-backed portable archives can go directly to object storage without an
46
- * intermediate whole-document string or buffer. Existing inline/provider APIs
47
- * retain their compatibility path when disk-backed publication is unavailable. */
48
- export async function captureWorkspaceArchiveForStorage(
49
- session: unknown,
50
- capturedAtMs: number,
51
- options: WorkspaceArchiveCaptureOptions,
52
- objectStorageAvailable: boolean,
53
- ): Promise<VerifiedWorkspaceArchivePayload> {
54
- const target = session as WorkspaceSession;
55
- const root = hostBackedWorkspaceRoot(target);
56
- if (
57
- process.platform !== "linux" ||
58
- !root ||
59
- !objectStorageAvailable ||
60
- options.strategy === "portable_tar"
61
- ) {
62
- return await captureVerifiedWorkspaceArchive(session, capturedAtMs, options);
63
- }
64
- return await withSandboxProviderCapture(session, async () => {
65
- const { spool, workspace } = await captureHostWorkspaceArchive(
66
- root,
67
- workspaceFingerprintExcludes(target),
68
- );
69
- return {
70
- kind: "host_spool" as const,
71
- spool,
72
- descriptor: {
73
- version: 1 as const,
74
- revision: `wa1:${String(capturedAtMs).padStart(13, "0")}:${spool.sha256}`,
75
- archiveSha256: spool.sha256,
76
- archiveBytes: spool.byteSize,
77
- capturedAt: new Date(capturedAtMs).toISOString(),
78
- workspace,
79
- },
80
- };
81
- });
82
- }
83
-
84
- export async function disposeWorkspaceArchive(
85
- archive: VerifiedWorkspaceArchivePayload | undefined,
86
- ): Promise<void> {
87
- if (archive?.kind === "host_spool") await archive.spool.dispose();
88
- }
89
-
90
39
  export type WorkspaceArchiveIntegrityCode =
91
40
  | "archive_metadata_missing"
92
41
  | "archive_metadata_invalid"
@@ -840,9 +789,7 @@ export async function verifyRestoredWorkspace(
840
789
  "selected host-backed workspace archive was restored by a non-host-backed provider",
841
790
  );
842
791
  }
843
- return process.platform === "linux"
844
- ? fingerprintHostWorkspace(root, workspaceFingerprintExcludes(target))
845
- : fingerprintHostBackedWorkspace(root, workspaceFingerprintExcludes(target));
792
+ return fingerprintHostBackedWorkspace(root, workspaceFingerprintExcludes(target));
846
793
  })()
847
794
  : await fingerprintRemoteWorkspace(target);
848
795
  if (!fingerprintsEqual(actual, descriptor.workspace)) {