Skip to content

feat(sdk): filesystem-only snapshots (pause memory:false)#1465

Merged
bchalios merged 5 commits into
mainfrom
feat/pause-filesystem-only
Jun 25, 2026
Merged

feat(sdk): filesystem-only snapshots (pause memory:false)#1465
bchalios merged 5 commits into
mainfrom
feat/pause-filesystem-only

Conversation

@bchalios

Copy link
Copy Markdown
Contributor

Summary

Adds an optional memory flag to pause in both the JS and Python SDKs. When memory is false, the pause captures only the filesystem (no memory snapshot); resuming such a snapshot cold-boots (reboots) the sandbox from disk — losing in-memory state, running processes, and open connections. Defaults to true (full memory snapshot), so existing callers are unaffected.

This is the SDK surface for the filesystem-only snapshot feature on the infra side.

Usage

// JS / TS
const sbx = await Sandbox.create()
await sbx.pause({ memory: false })   // filesystem-only snapshot
const resumed = await sbx.connect()  // resumes by cold-booting from disk
# Python (sync)
sbx = Sandbox()
sbx.pause(memory=False)              # filesystem-only snapshot
resumed = sbx.connect()              # resumes by cold-booting from disk

# Python (async)
sbx = await AsyncSandbox.create()
await sbx.pause(memory=False)
resumed = await sbx.connect()

memory defaults to truepause() / pause({}) behave exactly as before.

What changed

  • spec: optional memory: boolean (default true) on POST /sandboxes/{sandboxID}/pause (SandboxPauseRequest); both API clients regenerated via make codegen.
  • JS: Sandbox.pause / betaPause accept { memory }SandboxApi.pause sends the request body.
  • Python: pause(memory=...) / beta_pause_cls_pause (sync + async) sends SandboxPauseRequest(memory=...).
  • Tests: filesystem-only pause+resume reboots the guest while the filesystem survives — JS (tests/sandbox/snapshot.test.ts) and Python sync + async. All pass against a local stack; format / lint / typecheck clean.
  • Changeset: minor for e2b and @e2b/python-sdk.

Note (related infra observation, not addressed here)

While testing, a filesystem-only resume cold-boots into a different default exec context (root / /root) than a memory resume (user / /home/user). The filesystem itself is fully intact; tests use absolute paths to be robust to this. Worth confirming on the infra reboot path whether the template's default user should be restored after a cold boot.

🤖 Generated with Claude Code

@changeset-bot

changeset-bot Bot commented Jun 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f259b02

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 Jun 19, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Pause/resume semantics change when callers opt out of memory snapshots; wrong docs would send users to a non-existent memory SDK option.

Overview
The PR description and title show pause({ memory: false }) and pause(memory=False), but the JS and Python SDKs in this diff expose keepMemory and keep_memory, mapped to the API body field memory.

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

@github-actions

github-actions Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Package Artifacts

Built from 0177a12. Download artifacts from this workflow run.

JS SDK (e2b@2.30.7-feat-pause-filesystem-only.0):

npm install ./e2b-2.30.7-feat-pause-filesystem-only.0.tgz

CLI (@e2b/cli@2.13.1-feat-pause-filesystem-only.0):

npm install ./e2b-cli-2.13.1-feat-pause-filesystem-only.0.tgz

Python SDK (e2b==2.29.6+feat-pause-filesystem-only):

pip install ./e2b-2.29.6+feat.pause.filesystem.only-py3-none-any.whl

@bchalios bchalios force-pushed the feat/pause-filesystem-only branch from 4b7cf94 to d434332 Compare June 19, 2026 14:30

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4b7cf94857

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/js-sdk/src/sandbox/index.ts Outdated
@bchalios bchalios force-pushed the feat/pause-filesystem-only branch from d434332 to d54797f Compare June 19, 2026 14:35

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d54797f. Configure here.

Comment thread packages/js-sdk/src/sandbox/index.ts Outdated
@bchalios bchalios marked this pull request as draft June 19, 2026 14:43
@bchalios bchalios force-pushed the feat/pause-filesystem-only branch from d54797f to 57674a0 Compare June 19, 2026 14:52

@mishushakov mishushakov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

everything looks good - only thing is that i believe the envd token should be persistent across pause/resume cycle.
please use SandboxPauseOpts consistently.

Comment thread packages/js-sdk/src/sandbox/index.ts Outdated
* ```
*/
async pause(opts?: ConnectionOpts): Promise<boolean> {
async pause(opts?: ConnectionOpts & { memory?: boolean }): Promise<boolean> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we're not using the SandboxPauseOpts here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This was anchored to the pre-amend commit — it's addressed in the latest commit (57674a071): both pause and betaPause now take the exported SandboxPauseOpts (and the static SandboxApi.pause/betaPause too), matching how createSnapshot uses CreateSnapshotOpts. resolveApiOpts spreads the opts through, so memory reaches SandboxApi.pause.

/**
* @deprecated Use {@link Sandbox.pause} instead.
*/
async betaPause(opts?: ConnectionOpts): Promise<boolean> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we're not using SandboxPauseOpts here

@bchalios bchalios force-pushed the feat/pause-filesystem-only branch from 57674a0 to 59a8378 Compare June 19, 2026 16:34
@bchalios

Copy link
Copy Markdown
Contributor Author

Per offline feedback from @mishushakov, renamed the SDK-facing parameter to make the drop-on-false semantics explicit:

  • JS/TS: pause({ keepMemory: false })
  • Python: pause(keep_memory=False)

The wire/spec field stays memory (the contract with the API in the infra PR is unchanged) — the SDK simply maps keepMemory/keep_memory onto the memory body field with identical semantics (true = keep the full memory snapshot). No generated files changed. Typecheck/lint/format pass for both SDKs; changeset and docstrings updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Babis Chalios <babis.chalios@e2b.dev>
@bchalios bchalios marked this pull request as ready for review June 25, 2026 07:31
@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.

Comment thread packages/js-sdk/src/sandbox/sandboxApi.ts
Comment thread packages/js-sdk/tests/sandbox/snapshot.test.ts
Comment thread packages/js-sdk/src/sandbox/index.ts Outdated
- Re-export SandboxPauseOpts from the package root so callers can
  `import { SandboxPauseOpts } from 'e2b'` (the point of the named type).
- Fix Sandbox.pause @returns JSDoc: it returns a boolean (paused/already
  paused), not a sandbox ID.
- Correct the resume test comments: connect() returns the same handle and its
  credentials stay valid across a filesystem-only resume (no stale handle).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@mishushakov mishushakov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

all looks good - in the tests you can use sandbox.files.read instead of cat, but i am fine with either

@bchalios bchalios force-pushed the feat/pause-filesystem-only branch from 4a0e4c2 to 1bed5d8 Compare June 25, 2026 10:30
@bchalios

Copy link
Copy Markdown
Contributor Author

Done in 1bed5d8 — switched the reads to files.read (and the marker write to files.write) in both Python tests and the JS test. I checked your instinct on cat for the boot id: files.read("/proc/sys/kernel/random/boot_id") returns the full UUID against a live stack (envd streams proc files fine), so no cat fallback is needed there either. Both Python tests pass live; JS typecheck/lint clean.

Per review (mishushakov): dogfood the SDK's filesystem API in its own tests.
The rootfs marker and the kernel boot id are now written/read via
files.write/files.read instead of echo/cat, in both Python tests and the JS
test. files.read returns the full content for the /proc boot_id pseudo-file
(verified against a live stack), so no cat fallback is needed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bchalios bchalios force-pushed the feat/pause-filesystem-only branch from 1bed5d8 to f259b02 Compare June 25, 2026 10:33
@bchalios bchalios enabled auto-merge (squash) June 25, 2026 10:33
@bchalios bchalios merged commit cb5a387 into main Jun 25, 2026
28 of 29 checks passed
@bchalios bchalios deleted the feat/pause-filesystem-only branch June 25, 2026 10:42
bchalios added a commit that referenced this pull request Jun 25, 2026
…form (#1471)

## Filesystem-only auto-pause (`onTimeout` object form)

Adds an object form to the sandbox **lifecycle** `onTimeout`
(`on_timeout` in Python) that controls the snapshot kind taken when a
sandbox auto-pauses on timeout, via `keepMemory` (`keep_memory`).

`onTimeout` now accepts either the existing bare action (`'pause'` /
`'kill'`) or the object form `{ action, keepMemory }`. When `keepMemory`
is `false` (with `action: 'pause'`), a timeout auto-pause takes a
**filesystem-only** snapshot (no memory) instead of a full memory one,
so the sandbox cold-boots (reboots) from disk on resume — losing running
processes and open connections. Defaults to `true` (full memory
snapshot), so existing callers are unaffected. **The bare string form is
unchanged.**

It's the create-time / auto-pause counterpart to the explicit
`pause(keepMemory=false)` from #1465: same `keepMemory` naming, mapped
onto the `autoPauseMemory` create field.

### Type safety
The object form is a **discriminated union** on `action`: `keepMemory`
is only valid with `action: 'pause'`. Pairing it with `action: 'kill'`
is a **compile-time type error** (TS) / static error (`ty`), and is
additionally rejected at runtime (`InvalidArgumentError` /
`InvalidArgumentException`) for untyped callers.

### Behavior & validation
- `keepMemory` only applies to a `pause` action.
- **Incompatible with auto-resume** — auto-resume wakes a paused sandbox
on inbound traffic by restoring its memory snapshot in place; a
filesystem-only snapshot has no memory to restore (resuming cold-boots
it), so it must be resumed explicitly via `connect()`. Combining
`keepMemory: false` with `autoResume` is rejected client-side.

### Usage
```ts
// JS/TS — filesystem-only auto-pause on timeout
const sbx = await Sandbox.create({
  lifecycle: { onTimeout: { action: 'pause', keepMemory: false } },
})

// bare string form still works (full memory snapshot)
const sbx2 = await Sandbox.create({ lifecycle: { onTimeout: 'pause' } })
```
```python
# Python
sbx = Sandbox.create(
    lifecycle={"on_timeout": {"action": "pause", "keep_memory": False}}
)
```

### Changes
- `spec/openapi.yml`: `autoPauseMemory` on the create body (+
regenerated JS/Python clients).
- JS `SandboxOnTimeout` discriminated union (`'pause' | 'kill' | {
action: 'pause'; keepMemory? } | { action: 'kill' }`) and the Python
`SandboxOnTimeoutPause` / `SandboxOnTimeoutKill` TypedDicts, wired
through `createSandbox` / `_create_sandbox` (sync + async) to
`autoPauseMemory`, with the client-side guards.
- Tests: payload serialization + validation (offline, incl. the `action:
'kill'` type/runtime guard) and live cold-boot e2e in both SDKs;
changeset (`e2b` + `@e2b/python-sdk`, minor).

### Backend dependency
The live e2e tests exercise the real auto-pause→cold-boot path and
require the infra-side `autoPauseMemory` support (e2b-dev/infra#3055),
now merged and deployed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Babis Chalios <babis.chalios@e2b.dev>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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