Skip to content

Changelog

26 October 202624 changes

SDKs and CLI 1 · Platform 1 · Sandboxes 13 · Agents 3 · Apps 2 · CI/CD 3 · Jobs 1

11 min read

NewerOlderFollow by RSS

The CLI says when a newer version exists after it rejects an option

An installed withruntime stays at its version until you update it, and npx withruntime runs an installed copy before it downloads one, so an older CLI could reject an option the docs list, such as secrets scope --only-label, with no sign that it was out of date. Now, when the CLI rejects an option or a command, it checks npm for the latest version, for at most 1.5 seconds, and prints the installed and latest versions with npm install -g withruntime@latest. It skips the check in CI and when RUNTIME_NO_UPDATE_CHECK=1 is set (the CLI).

Raising a job's timeout no longer requires changing its compute

Previously, PATCH /v1/jobs/{id} with only a larger timeoutSeconds returned 400 with "Invalid integer" when the new timeout exceeded the job's compute.durationSeconds. Now the update extends compute.durationSeconds to the timeout plus 60 seconds of start time, up to 3,600, as a create does. A timeout that already fits leaves it as it is. Refusals of timeoutSeconds and compute.durationSeconds now name the field and the allowed range.

Previously each server signed with its own certificate authority, so a process started before a wake, a move or a fork onto another server (such as Node with NODE_EXTRA_CA_CERTS) failed every request to an egress secret's host with SELF_SIGNED_CERT_IN_CHAIN until it restarted. Now each organization has one certificate authority, the same on every server, and running processes keep working. Each sandbox switches to its organization's certificate authority at its next start or wake; a process started before that switch must be restarted once. See Security.

Sandboxes created from older images hold as many outbound connections as their limits allow

Previously a sandbox created from an image or snapshot made before 15 October 2026 kept that image's web proxy relay, which held 64 connections and closed the 65th without a response, even when GET /v1/sandboxes/{id}/network reported a higher limits.connections. Now every sandbox boots with the current relay, so it holds its full limit and a connection past it returns 503 Service Unavailable with the limit in X-Runtime-Egress. A sandbox that resumes saved memory (a wake, or a copy of a memory snapshot) takes the current relay immediately before its next pause, so it holds its full limit from the wake after that. See The network.

Agents that start from a repository start their sessions

An agent whose definition sets from: {"repo": "owner/name"} starts each session from that repository's CI/CD warm snapshot. Previously every session of such an agent ended immediately with sandbox_refused (403 forbidden, "a create naming another snapshot"), because the session's key was allowed to create its sandbox only from a snapshot the definition names by id. A session's key may now create its sandbox from any ready warm snapshot of the definition's repository, and from no other snapshot.

A secret can now be given to the machine as its real value

Some secrets are not HTTPS headers: a database password, an SSH key, a signing key or a service account's JSON file. Store one with --exposed (or exposed: true in the API, the MCP tool and the SDKs) and the machines it reaches get the value itself in its variable. It may be any text up to 64 KiB, several lines included. The value is still sealed, and opened only on the server that runs the machine. Code in the machine can read it, so the placeholder kind stays the default and the one to use for HTTPS APIs. For the same reason, it goes only to the sandboxes and Apps you name by id (--only-sandbox, --only-app), never to every sandbox or by label, and only a key that may run commands in each of them can store it. Which machines get it changes only when you store it again with its value. Every secret now has a version too, one more for each change to it: its value, who gets it, its hosts or its variable. Pass it as expectedVersion (--expected-version) and a write happens only if no one has changed the secret since; otherwise the answer is 409 version_conflict. GET /v1/egress-secrets/{name} and runtime secrets get show one secret's kind and version, never its value. The website's Secrets page offers both kinds and shows each secret's kind and version. See a secret the machine needs itself.

A failed agent session shows why in the console

When a session fails because its sandbox could not be created (sandbox_refused), the session's page in the console now shows the error the API returned, which previously appeared only in the session's events.

App deploys report live only when the address serves them

A deploy reaches live only after every router serves the new version's route, and an App's state is waiting until its first version is served, so waitFor=live, runtime apps deploy and the SDKs return when the address answers from the new version.

CI/CD: built-in and light-sandbox steps record their result immediately

An undoes or hot-spots step, a gate, or a machine: light step now shows its result in the console as soon as it finishes, instead of when the check's other jobs end. Previously such a step stayed "running" until then, and a check whose other jobs waited more than 40 minutes for capacity failed with "undoes sent nothing for 40 minutes" although the step had passed.

GET http://runtime.internal/v1/identity/token returns 401 for a request token that is not the sandbox's current one

It previously returned 403. Read the current token from http://runtime.internal/v1/environment and retry. The JavaScript and Python SDKs already do this for both statuses.

Browsers start with Chromium already running

A headless browser at the default window size (1280 by 800) is now created from a snapshot of Chromium that is already running, instead of starting Chromium in the new sandbox, which took 1 to 3 seconds. Each browser gets its own CDP secret, its own random state, and an empty profile. A server builds the snapshot the first time a browser of that size is created on it, so that first browser starts as before. Other settings start Chromium as before.

An outbound connection that cannot reach Runtime's web proxy returns 503 Service Unavailable with the reason

Previously the sandbox's relay closed such a connection without a response, which clients reported as Connection reset by peer. Now the response carries X-Runtime-Egress: proxy unreachable (<reason>) and Retry-After: 1. A sandbox holds about 1,000 outbound connections at the same time, fewer than limits.connections for a sandbox with more than one vCPU, until a later release raises it; past that, new connections return this response.

A sandbox with no time limit can be funded up to 12 hours ahead

Set leaseSeconds (from its first lease, 1,800 seconds, to 43,200) on create or :update (--lease in the CLI, lease_seconds= in Python, .leaseSeconds() in Java, LeaseSeconds in Go), and each renewal funds it that far ahead instead of 1,800 seconds. It is renewed when half is left, so it keeps running for at least half of leaseSeconds if Runtime's control plane cannot renew it. Each renewal holds that much time's credit at the sandbox's rates; what it does not use is released. A sandbox on the free usage renews by 1,800 seconds until the account has credit. Only an owner's, an admin's or an account-wide key may set it, on a sandbox one of those made. If that key or member loses the right, a sandbox funded past its first lease is paused or stopped at once, as its lease's end would. A sandbox's details include its leaseSeconds.

CI/CD: a cancelled check names the key that cancelled it, and your production branch's check gets machines sooner

A check cancelled with POST /v1/ci/checks/{id}:cancel, runtime ci cancel or the MCP tool now shows the reason asked through Runtime's API with the key "<label>" in the console, in the API's reason field and on GitHub, where <label> is the calling key's label. The audit log entry also includes the key's id. When your account's machine limit is reached, your production branch's check now takes machines from your pull requests' checks as their running tasks end. Previously it waited for those checks to finish when the limit, rather than the servers, held it back.

Slack triggers start agent sessions from a channel

Add Runtime's Slack app to a workspace with runtime agents slack install (or POST /v1/agents/slack/installs; the link works only for a signed-in member of the account), invite it to a channel, and add a slack trigger with the workspace's team and the channel's id. Each new message in the channel starts a session; a reply in its thread joins that session. The session's replies and result are posted in the thread, its status is one message edited in place, and each ask is posted with a button for each option. GET /v1/agents/slack/teams lists the added workspaces and DELETE /v1/agents/slack/teams/{team} removes one. Available in the JavaScript, Python, Go, Java and Ruby SDKs (agents.slack).

SSH to Runtime's own servers counts toward its own limit in the probing check

SSH connections from a sandbox to the public addresses of Runtime's servers are still refused. They now count toward a separate limit of 10 different servers in ten minutes, instead of the limit of 5 internal addresses. Previously, a test suite that tried SSH on five of them within ten minutes could have its sandbox held. Connections to Runtime's servers on other ports still count toward the internal-address limit.

A paid account's sandbox is reviewed by a person before it is halted

When the abuse checks flag a sandbox of an account that has paid, it is held as described above and keeps running, with its processes and files untouched. A person then lifts the hold, which turns the network back on, or confirms it, which pauses the sandbox. Previously, the sandbox was paused immediately.

A sandbox held by the abuse checks keeps its files readable

You can read and download a held sandbox's files with GET /v1/sandboxes/{id}/files/content or GET /v1/sandboxes/{id}/files/archive. A read wakes the sandbox if it is paused, and it can also be woken or started. Previously, every wake and start of a held sandbox returned sandbox_halted (403), so its files could not be read. While the hold is in place, the sandbox's network is off for outbound and inbound traffic (previews, custom domains, TCP ports and private networks), and commands, terminals, file writes, snapshots and forks return sandbox_halted (403). See Network access.

A lease that ends without you never deletes a sandbox's disk

Only a request from you deletes data. Previously a sandbox created with onTimeout: "delete" had its disk deleted when its lease ended before its time limit (a renewal that did not arrive, or a pause that failed at the lease's end), and a sandbox created with pausable: false, one on a server that stopped responding, or one stopped while moving between servers had its disk deleted when its lease ended or it crashed. Each is now kept: a pausable sandbox as a stopped sandbox you can restart, billed as paused storage; the others at no charge, until you delete them. onTimeout: "delete" still deletes the disk when the time limit you set ends.

CI/CD: built-in history checks finish immediately on production pushes

On a push to the production branch, an undoes or hot-spots step now passes as soon as the plan is made, because the pull request was already checked. It no longer waits for a sandbox. On a pull request, these steps still run on the check's light sandbox. While that sandbox, or the one that runs gates, waits for capacity, the console shows "Waiting for room" on each of its jobs, with the elapsed minutes updated every 5 minutes. Previously these jobs showed no update while they waited, and a check whose jobs waited more than 40 minutes failed with "sent nothing for 40 minutes".

Commands in sandboxes and App copies can open 65,536 files

The open-file limit is 65,536, previously 4,096, and a process can raise it to 1,048,576 without root (ulimit -n 1048576), so one server holds tens of thousands of connections. See Limits of one sandbox or one feature.

Move scheduled jobs from Trigger.dev, Inngest, Modal, Google Cloud Run and AWS Batch

Change one import, or run one command, and the jobs run on Runtime unchanged, with their schedules, sizes, timeouts and retries. withruntime/trigger runs Trigger.dev schedules.task and task definitions, and withruntime/inngest runs Inngest functions with cron or event triggers; runtime jobs deploy builds the project and creates one job per task. python -m withruntime.modal deploy app.py creates a job for each Modal Function with a schedule (modal.Cron or modal.Period). runtime jobs import reads a Cloud Run job's YAML with its Cloud Scheduler job, or an AWS Batch job definition (API JSON, describe-job-definitions output or a CloudFormation template) with its EventBridge schedule or rule, and creates the jobs. Settings with no equivalent, such as a GPU, parallel tasks or a mounted bucket, are listed with the reason. The plan is also available as POST /v1/job-imports, jobs.importPlan in every SDK and the runtime_jobs_import MCP tool. Requires SDK 0.36.0 (Python 0.34.0). See Move jobs from another platform.

OpenTelemetry exports can send traces

Add "traces" to an export's signals and every API call your account makes, including creates, commands, file calls, pauses, wakes and snapshots, is sent to <endpoint>/v1/traces as an OTLP span within about a second, with the OpenTelemetry HTTP attributes and the request ID. A call that carries a W3C traceparent header becomes a child of the caller's span; the TypeScript and Python SDKs send that header automatically when OpenTelemetry is set up in your process. Exports now report exportedSpans, and the span pushes' own health in tracesLastSuccessAt, tracesLastError and tracesFailures, apart from the logs' and metrics' lastError and failures. The default signals are unchanged (traces).

Sandbox metrics include disk and network

GET /v1/sandboxes/{id}/metrics and sbx.metrics() now return diskUsedBytes (bytes used on the root file system), diskReadBytesPerSecond, diskWriteBytesPerSecond, diskReadOpsPerSecond, diskWriteOpsPerSecond, networkReceivedBytesPerSecond and networkSentBytesPerSecond for each point and for latest. Disk figures are counted by the sandbox's own kernel; network figures by the host, for every connection the sandbox opens and every request it serves. A value that was not measured is null, never zero. The sandbox's Activity tab charts disk used, disk reads and writes, and network in and out; runtime sandboxes metrics prints them; the Python, Go and TypeScript SDKs return them; and getMetrics() in withruntime/e2b now returns diskUsed. OpenTelemetry exports send them as runtime.sandbox.disk.usage, runtime.sandbox.disk.limit, runtime.sandbox.disk.io, runtime.sandbox.disk.operations and runtime.sandbox.network.io (metrics).

Other days.

Cut your cloud billby 75%.

No card to start. Billed by the second after.

Start free, no card