Skip to content

feat(sdk): filesystem-only snapshots via mode on createSnapshot - #1925

Merged
bchalios merged 4 commits into
mainfrom
feature/fs-only-snapshot
Oct 8, 2026
Merged

bchalios merged 4 commits into
mainfrom
feature/fs-only-snapshot

Conversation

@bchalios

@bchalios bchalios commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds mode: 'full' | 'filesystem' (exported as SnapshotMode) to createSnapshot / create_snapshot. With 'filesystem', the SDK sends memory: false on the snapshot request and the API takes a filesystem-only snapshot: only the filesystem is persisted, so the snapshot is smaller and faster to take, and sandboxes created from it cold-boot instead of restoring memory. The source sandbox keeps running either way. When omitted, nothing is sent and the API default, a full memory snapshot, applies.

The same mode is accepted by pause() and by the lifecycle.onTimeout / lifecycle["on_timeout"] pause object form. Their existing keepMemory / keep_memory option is deprecated in favor of it (keepMemory: false is mode: 'filesystem') and keeps working. One resolver maps mode (or the deprecated flag) onto the wire memory / autoPauseMemory field for all three call sites; it rejects an unknown mode and passing both options with InvalidArgumentError / InvalidArgumentException before any request is sent.

Spec copy updated from the API, both generated clients regenerated, docstrings and a changeset (minor for both SDKs) included.

Usage

import { Sandbox } from 'e2b'

const sandbox = await Sandbox.create()

// Filesystem-only snapshot: sandboxes created from it start fresh from disk
const snapshot = await sandbox.createSnapshot({ mode: 'filesystem' })

// Filesystem-only pause: resuming cold-boots the sandbox
await sandbox.pause({ mode: 'filesystem' })

// Filesystem-only auto-pause on timeout
await Sandbox.create({
  lifecycle: { onTimeout: { action: 'pause', mode: 'filesystem' } },
})

// Deprecated, still works: equivalent to mode: 'filesystem'
await sandbox.pause({ keepMemory: false })
from e2b import Sandbox

sandbox = Sandbox.create()

snapshot = sandbox.create_snapshot(mode="filesystem")
sandbox.pause(mode="filesystem")
Sandbox.create(lifecycle={"on_timeout": {"action": "pause", "mode": "filesystem"}})

# Deprecated, still works
sandbox.pause(keep_memory=False)

Server side

The API support merged in e2b-dev/belt#4073 on top of the orchestrator support in e2b-dev/belt#4049. The API refuses memory: false with 400 snapshot_filesystem_only_disabled while the feature is not enabled for the team, and with 409 snapshot_filesystem_only_unsupported_node when the sandbox's node runs an orchestrator that predates the field. A request is never downgraded to a memory snapshot by a server that understands the field.

Why this is a draft

An API deployment from before e2b-dev/belt#4073 accepts the request body, ignores memory, and takes a memory snapshot while answering 201. Releasing this before production runs the new API would let callers ask for a filesystem-only snapshot and silently get a full one. Keep this in draft until production has the API build, then release.

The SDK tests for the new option call the live API with mode: 'filesystem'. Where the feature is off for the test team the API answers 400 and the tests skip with that reason; where it is on, they prove the snapshot kind: the source keeps its kernel boot id across the snapshot and the sandbox created from it reports a different one, which a memory snapshot cannot produce. Against an API that predates the field those assertions fail, by design.

Verification

  • Rebased on main; Python modules compile; the JS SDK typechecks.
  • spec/runtime-ref is bumped to the e2b-dev/runtime commit that carries the API change and both clients are regenerated with make codegen, so the Generated files check passes. The bump also pulls in the upstream spec changes since the previous pin (team id description, cached node admission fields, the secret_limit_reached error code, envd oom_kills replacing the memory counters, is_symlink on the filesystem proto); none of them is read by SDK code outside the generated files.
  • mode is keyword-only in Python (pause keeps keep_memory positional for compatibility). Both SDKs document the 400 and 409 refusals on createSnapshot / create_snapshot.
  • Unit tests cover the mode → memory / autoPauseMemory mapping, the deprecated flag, and the rejected combinations; the filesystem-mode integration tests (snapshot, pause, timeout auto-pause) pass against production in JS and Python sync/async.
  • The three filesystem-only snapshot tests (JS, Python sync, Python async) assert the cold boot through the kernel boot id, read with a command rather than files.read because envd serves procfs files as an empty 200, and skip on the 400 snapshot_filesystem_only_disabled refusal.
  • End-to-end behaviour of the filesystem-only snapshot (no memfile written, source keeps the same Firecracker process, cold-boot from the template, 400 and 409 refusals) was verified against a dev cluster from the API side.

🤖 Generated with Claude Code

@cla-bot cla-bot Bot added the cla-signed label Oct 1, 2026
@changeset-bot

changeset-bot Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 920a48b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
e2b Minor
@e2b/python-sdk Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cursor

cursor Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

PR Summary

Medium Risk
Snapshot and pause behavior changes how sandboxes resume and what the API receives; incorrect deployment pairing could mismatch caller intent with server behavior until the new API is live.

Overview
Adds exported SnapshotMode ('full' | 'filesystem') on createSnapshot, pause, and timeout auto-pause via lifecycle.onTimeout.mode, mapping to API memory / autoPauseMemory with keepMemory / keep_memory deprecated (mutually exclusive with mode). OpenAPI and generated clients gain snapshot memory and related spec updates (secret_limit_reached, team ID wording, envd oom_kills, filesystem is_symlink on EntryInfo). JS and Python map is_symlink for directory listings; tests cover wire mapping, validation, and filesystem-only snapshot cold-boot behavior.

Reviewed by Cursor Bugbot for commit 920a48b. Bugbot is set up for automated code reviews on this repo. Configure here.

devin-ai-integration[bot]

This comment was marked as resolved.

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Package Artifacts

Built from a052157. Download artifacts from this workflow run.

JS SDK (e2b@2.54.1-feature-fs-only-snapshot.0, with @e2b/dockerfile-utils@0.1.1-feature-fs-only-snapshot.0):

npm install ./e2b-dockerfile-utils-0.1.1-feature-fs-only-snapshot.0.tgz ./e2b-2.54.1-feature-fs-only-snapshot.0.tgz

CLI (@e2b/cli@2.21.2-feature-fs-only-snapshot.0):

npm install ./e2b-cli-2.21.2-feature-fs-only-snapshot.0.tgz

Code Interpreter JS SDK (@e2b/code-interpreter@2.8.3-feature-fs-only-snapshot.0):

npm install ./e2b-code-interpreter-2.8.3-feature-fs-only-snapshot.0.tgz

Desktop JS SDK (@e2b/desktop@2.4.1-feature-fs-only-snapshot.0):

npm install ./e2b-desktop-2.4.1-feature-fs-only-snapshot.0.tgz

Python SDK (e2b==2.54.0+feature.fs.only.snapshot, with e2b-dockerfile-utils==0.1.0+feature.fs.only.snapshot):

pip install ./e2b_dockerfile_utils-0.1.0+feature.fs.only.snapshot-py3-none-any.whl ./e2b-2.54.0+feature.fs.only.snapshot-py3-none-any.whl

Code Interpreter Python SDK (e2b-code-interpreter==2.10.3+feature.fs.only.snapshot):

pip install ./e2b_code_interpreter-2.10.3+feature.fs.only.snapshot-py3-none-any.whl

Desktop Python SDK (e2b-desktop==2.6.1+feature.fs.only.snapshot):

pip install ./e2b_desktop-2.6.1+feature.fs.only.snapshot-py3-none-any.whl

@bchalios
bchalios force-pushed the feature/fs-only-snapshot branch 4 times, most recently from 5920e38 to 7ca14e0 Compare October 8, 2026 08:21
@bchalios
bchalios marked this pull request as ready for review October 8, 2026 11:11
@bchalios
bchalios requested a review from mishushakov as a code owner October 8, 2026 11:11
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

devin-ai-integration[bot]

This comment was marked as resolved.

@bchalios
bchalios force-pushed the feature/fs-only-snapshot branch 2 times, most recently from f8d85fb to 9b64cdf Compare October 8, 2026 11:17

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed this PR and didn't find any bugs. Because it adds a new cross-SDK API option (keepMemory/keep_memory) touching sandbox snapshot lifecycle, a server-side feature flag with 400/409 refusal semantics, and a spec pin bump across 23 generated and hand-written files in both SDKs, a human look would still be worthwhile.

What was reviewed: the memory/keepMemory wiring from spec (openapi.yml, envd.yaml, filesystem.proto, runtime-ref bump) through generated clients into js-sdk's sandboxApi.ts/index.ts and both Python sync/async main.py/sandbox_api.py; confirmed keep_memory is keyword-only in both Python variants and mirrors the existing keepMemory pattern already used for pause; checked that an omitted option serializes to nothing on the wire (undefined dropped from the JS request body, UNSET in the Python model) matching the documented "API default applies" behavior; and reviewed the new JS/Python tests, which correctly skip on the 400 snapshot_filesystem_only_disabled case and otherwise assert cold-boot via a changed kernel boot id.

Extended reasoning...

The diff adds a filesystem-only snapshot option end-to-end: spec/runtime-ref pin bump plus regenerated clients, and hand-written additions to js-sdk sandboxApi.ts/index.ts and python-sdk sandbox_sync/sandbox_async main.py/sandbox_api.py, with new tests and a changeset. No injection/auth/crypto surface is touched; the sensitive aspect is correctly wiring a new persistence-affecting option across two SDK languages and keeping it consistent with the spec-sync convention. The PR author's own description flags this as a draft pending a production API rollout (to avoid silently downgrading filesystem-only requests to full snapshots against an older API), and an earlier automated review (Devin) left six inline comments on these same files that have no visible resolution signal in the metadata available to me — both are reasons a human should still weigh in even though no bugs were found in this pass.

This review covers commit 7ca14e0, which is no longer the latest commit on this pull request; later commits are not covered by it.

@bchalios
bchalios force-pushed the feature/fs-only-snapshot branch from 9b64cdf to bc31550 Compare October 8, 2026 11:36
devin-ai-integration[bot]

This comment was marked as resolved.

`createSnapshot({ keepMemory: false })` (JS) and
`create_snapshot(keep_memory=False)` (Python) send `memory: false` on the
snapshot request, mirroring the pause option: only the filesystem is
persisted, so the snapshot is smaller and faster to take, and sandboxes
created from it cold-boot instead of restoring memory. The source sandbox
keeps running either way. Omitted, nothing is sent and the API default (a
full memory snapshot) applies.

The runtime spec pin (spec/runtime-ref) moves to the e2b-dev/runtime
commit that carries the API change, and both clients are regenerated
from it with the repo's codegen. The pin bump also brings the spec
changes landed upstream since the previous pin (18 September): the
team_id description, cached node admission fields, the
secret_limit_reached error code and 409 body, the envd metrics change
from mem_total_mib/mem_used_mib to oom_kills, and is_symlink on the
filesystem proto. The last one is honoured: newer envd reports a
symlink's target type in `type` and marks the link with is_symlink, so
both filesystem mappers now report FileType.SYMLINK when the flag is
set and keep the old enum mapping otherwise, with unit tests for links
to a file and to a directory. Nothing else in the regenerated files is
read by SDK code.

keep_memory is keyword-only in Python. Both SDKs document the two
refusals: status 400 (snapshot_filesystem_only_disabled) while the
feature is off for the team, and 409
(snapshot_filesystem_only_unsupported_node) when the sandbox's node
runs an orchestrator that predates the option.

The SDK tests prove the snapshot kind rather than only the file copy:
the source keeps its kernel boot id across the snapshot and the sandbox
created from it reports a different one, which a memory snapshot
cannot produce. The boot id is read with a command, not files.read,
because envd serves procfs files as an empty 200. Where the feature is
off for the team the API answers 400 and the tests skip with that
reason.

Signed-off-by: Babis Chalios <babis.chalios@e2b.dev>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@bchalios
bchalios force-pushed the feature/fs-only-snapshot branch from bc31550 to 917f733 Compare October 8, 2026 11:54
createSnapshot / create_snapshot take `mode` instead of the unreleased
`keepMemory` / `keep_memory`. pause() and the lifecycle onTimeout pause
object form accept the same `mode`; their `keepMemory` / `keep_memory`
is deprecated in favor of it and keeps working. The new `SnapshotMode`
type is exported from both SDKs.

One resolver maps `mode` (or the deprecated flag) onto the API's
`memory` / `autoPauseMemory` field so the three call sites can't drift.
It rejects an unknown mode and passing both options with
InvalidArgumentError / InvalidArgumentException before any request is
sent. An unset mode still omits the field, leaving the API default
(a full snapshot) in effect.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mishushakov mishushakov changed the title feat(sdk): filesystem-only snapshots via keepMemory on createSnapshot feat(sdk): filesystem-only snapshots via mode on createSnapshot Oct 8, 2026
devin-ai-integration[bot]

This comment was marked as resolved.

@linear-code

linear-code Bot commented Oct 8, 2026

Copy link
Copy Markdown

EN-3233

cursor[bot]

This comment was marked as resolved.

`createSnapshot({ kind: 'filesystem' })` (JS) and
`create_snapshot(kind="filesystem")` (Python) replace the unreleased
`mode` option on snapshot creation. `pause()` and the lifecycle
onTimeout pause object form keep `mode`; both still take the shared
`SnapshotMode` values `'full' | 'filesystem'`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mishushakov mishushakov changed the title feat(sdk): filesystem-only snapshots via mode on createSnapshot feat(sdk): filesystem-only snapshots via kind on createSnapshot Oct 8, 2026
This reverts commit b43363a. createSnapshot goes back to `mode`, so
createSnapshot, pause() and the lifecycle onTimeout pause object form
share one option name for the same `SnapshotMode` values instead of
splitting them between `kind` and `mode`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mishushakov mishushakov changed the title feat(sdk): filesystem-only snapshots via kind on createSnapshot feat(sdk): filesystem-only snapshots via mode on createSnapshot Oct 8, 2026
@bchalios
bchalios merged commit b52cd30 into main Oct 8, 2026
50 checks passed
@bchalios
bchalios deleted the feature/fs-only-snapshot branch October 8, 2026 13:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants