Skip to content

feat: add support for per-sandbox idle timeout option#6

Merged
mishamilovidov merged 11 commits into
mainfrom
RUNNER-9437-add-configurable-sbx-idle-to
Jun 10, 2026
Merged

feat: add support for per-sandbox idle timeout option#6
mishamilovidov merged 11 commits into
mainfrom
RUNNER-9437-add-configurable-sbx-idle-to

Conversation

@mishamilovidov

@mishamilovidov mishamilovidov commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Description

Add idleTimeout option to sandbox-creation payload and update README.md to document sandbox lifetime model.

Related Issue

Motivation and Context

The sandbox-creation API now support clients setting a per-sandbox idle timeout alongside the max lifetime option. This change exposes that capability to clients in the SDK.

How Has This Been Tested?

  • Add four new unit tests to cover the addition of the new option
  • Use local version of SDK via test_idle_timeout_sdk.js.zip to test usage of the new option (expand details below to see output)
Local shell session running test script...
node /Users/milovido/Downloads/test_idle_timeout_sdk.js --environment prd --auth $AIO_RUNTIME_AUTH_TMP
[2026-06-10T15:41:54.790Z] [setup] SDK idle-timeout smoke — prd
[2026-06-10T15:41:54.794Z] [setup]   APIHOST = https://next-adobeioruntime.net
[2026-06-10T15:41:54.794Z] [setup]   NS      = bladerunner-test
[2026-06-10T15:41:54.794Z] [test_create_echo] Sandbox.create({ idleTimeout: 600 }) — expect instance.idleTimeout === 600...
[2026-06-10T15:42:00.376Z] [test_create_echo]   instance.idleTimeout = 600 ✓
[2026-06-10T15:42:00.377Z] [test_create_default] Sandbox.create() with no idleTimeout — expect instance.idleTimeout === 900 (SDK default)...
[2026-06-10T15:42:05.618Z] [test_create_default]   instance.idleTimeout = 900 ✓
[2026-06-10T15:42:05.619Z] [test_get_echo] Sandbox.get(sb-9aeb6c65d3c5cfd7) — expect idleTimeout === 600 in get response...
[2026-06-10T15:42:05.991Z] [test_get_echo]   get response idleTimeout = 600 ✓
[2026-06-10T15:42:05.991Z] [test_idle_cap_rejection] Sandbox.create({ idleTimeout: 10801 }) — expect SandboxClientError (backend 400)...
[2026-06-10T15:42:06.142Z] [test_idle_cap_rejection]   rejected with SandboxClientError ✓ — 400 {"error":"idleTimeout must be 0 (use cluster default) or between 1 and 10800 seconds (3h); received 10801: idleTimeout out of range"}
[2026-06-10T15:42:06.142Z] [test_max_create_echo] Sandbox.create({ maxLifetime: 7200 }) — expect instance.maxLifetime === 7200...
[2026-06-10T15:42:11.573Z] [test_max_create_echo]   instance.maxLifetime = 7200 ✓
[2026-06-10T15:42:11.573Z] [test_max_create_default] Checking maxLifetime on test_create_default sandbox — expect 3600 (SDK default)...
[2026-06-10T15:42:11.573Z] [test_max_create_default]   instance.maxLifetime = 3600 ✓
[2026-06-10T15:42:11.573Z] [test_max_get_echo] Sandbox.get(sb-fc19e0999b6aa5ed) — expect maxLifetime === 7200 in get response...
[2026-06-10T15:42:11.951Z] [test_max_get_echo]   get response maxLifetime = 7200 ✓
[2026-06-10T15:42:11.951Z] [test_max_cap_rejection] Sandbox.create({ maxLifetime: 10801 }) — expect SandboxClientError (backend 400)...
[2026-06-10T15:42:12.153Z] [test_max_cap_rejection]   rejected with SandboxClientError ✓ — 400 {"error":"maxLifetime must be 0 (use cluster default) or between 1 and 10800 seconds (3h); received 10801: maxLifetime out of range"}
[2026-06-10T15:42:12.154Z] [test_idle_expiry] Creating sandbox with idleTimeout=30s, maxLifetime=300s...
[2026-06-10T15:42:17.676Z] [test_idle_expiry]   sandboxId = sb-d1af29f798b650de. WS closed, going dark for 30s...
[2026-06-10T15:42:47.677Z] [test_idle_expiry]   Polling for SandboxNotFoundError (up to 60s)...
[2026-06-10T15:42:48.088Z] [test_idle_expiry]   sandbox expired after 30s idle ✓
[2026-06-10T15:42:48.088Z] [test_activity_reset] Creating sandbox with idleTimeout=30s, maxLifetime=300s...
[2026-06-10T15:42:53.423Z] [test_activity_reset]   sandboxId = sb-478ddb730ff8378c. Waiting 25s (approaching idle deadline)...
[2026-06-10T15:43:18.425Z] [test_activity_reset]   Sending exec to reset idle timer...
[2026-06-10T15:43:18.593Z] [test_activity_reset]   exec complete. Waiting 25s (timer restarted at 30s, only 25s will pass)...
[2026-06-10T15:43:44.025Z] [test_activity_reset]   sandbox still alive (status=ready) — idle timer was reset by exec() ✓
[2026-06-10T15:43:44.025Z] [test_max_expiry] Creating sandbox with maxLifetime=30s, idleTimeout=300s...
[2026-06-10T15:43:49.568Z] [test_max_expiry]   sandboxId = sb-ef0fe851c2b0c58f. Sending exec keepalives every 5s to reset idle timer...
[2026-06-10T15:43:54.684Z] [test_max_expiry]   keepalive exec 1/6 — sandbox still alive at t+5s
[2026-06-10T15:43:59.788Z] [test_max_expiry]   keepalive exec 2/6 — sandbox still alive at t+10s
[2026-06-10T15:44:04.892Z] [test_max_expiry]   keepalive exec 3/6 — sandbox still alive at t+15s
[2026-06-10T15:44:10.002Z] [test_max_expiry]   keepalive exec 4/6 — sandbox still alive at t+20s
[2026-06-10T15:44:20.008Z] [test_max_expiry]   keepalive exec 5/6 failed — sandbox likely expired, stopping keepalives
[2026-06-10T15:44:20.008Z] [test_max_expiry]   WS closed. Polling for SandboxNotFoundError (up to 60s)...
[2026-06-10T15:44:20.380Z] [test_max_expiry]   sandbox expired after 30s maxLifetime despite active keepalives — hard deadline confirmed ✓
[2026-06-10T15:44:20.380Z] [cleanup] Destroying tracked sandboxes...

══════════════════════════════════
  SDK idle-timeout smoke: prd
══════════════════════════════════
  test_create_echo         ✓  PASS
  test_create_default      ✓  PASS
  test_get_echo            ✓  PASS
  test_idle_cap_rejection  ✓  PASS
  test_max_create_echo     ✓  PASS
  test_max_create_default  ✓  PASS
  test_max_get_echo        ✓  PASS
  test_max_cap_rejection   ✓  PASS
  test_idle_expiry         ✓  PASS
  test_activity_reset      ✓  PASS
  test_max_expiry          ✓  PASS
──────────────────────────────────
  Result: PASS
══════════════════════════════════

Screenshots (if appropriate):

N/A

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • I have signed the Adobe Open Source CLA.
  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

Adds an optional idleTimeout param to Sandbox.create() that is forwarded to the backend only when set, letting the backend apply the cluster default when omitted. The resolved value is stored on the instance from the create/get response alongside maxLifetime. JSDoc documents the "whichever fires first" deletion model, the cluster-default inheritance, and the 10800 s cap.

RUNNER-9437
Adds idleTimeout to the create() code example with inline comments explaining defaults and the 10800 s cap. Adds a new "Sandbox lifetime model" section describing the "whichever fires first" deletion semantics for maxLifetime and idleTimeout so callers understand how to keep a sandbox alive.

RUNNER-9437
Aligns idleTimeout with maxLifetime by applying a client-side default of 900 s in the request body rather than omitting the field when unset. This makes the two parameters consistent and ensures callers always see explicit values echoed back. Updates JSDoc and README to reflect the concrete default instead of "inherits cluster value".

RUNNER-9437
Reorders idleTimeout before maxLifetime consistently across the constructor, JSDoc params, request body, and create/get response objects so the new field appears first everywhere it was introduced.

RUNNER-9437
Covers four cases: default values (900/3600) sent in the request body when neither is specified, explicit idleTimeout forwarded in the body, and idleTimeout stored on the instance from both the create and get API responses. All 88 tests pass.

RUNNER-9437
@codecov

codecov Bot commented Jun 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

FaaSt's SandboxHostname middleware rejects status/management requests
that arrive at the generic apiHost instead of the per-sandbox
HMAC-validated managementEndpoint. Added options.managementEndpoint to
Sandbox.get() so callers can supply the value returned by create();
falls back to apiHost for dev environments without this routing.

Also forward managementEndpoint from the GET response payload back onto
the returned instance so subsequent destroy() calls use the correct host.

@MichaelGoberling MichaelGoberling left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Awesome to see this contribution 🙌

Comments for some user facing stuff

Comment thread src/Sandbox.js
Comment thread src/Sandbox.js Outdated
Comment thread src/Sandbox.js
Comment thread README.md
Comment thread README.md Outdated
@mishamilovidov mishamilovidov marked this pull request as ready for review June 10, 2026 16:00
@mishamilovidov mishamilovidov merged commit 47da060 into main Jun 10, 2026
8 checks passed
@mishamilovidov mishamilovidov deleted the RUNNER-9437-add-configurable-sbx-idle-to branch June 10, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants