Skip to content

Changelog

1 November 202626 changes

SDKs and CLI 1 · Sandboxes 15 · Platform 2 · Jobs 1 · Billing 1 · CI/CD 3 · Functions 1 · Apps 1 · Agents 1

9 min read

NewerOlderFollow by RSS

Sandboxes: browser, desktop and MCP on every new sandbox

/v1/sandboxes/{id}/browser (:start, GET, :stop), /desktop:start, /desktop:act, /desktop/screenshot, /desktop:stop and /mcp (POST, GET, DELETE) previously returned 409 served_elsewhere for sandboxes created on the new platform. They now return the same request and response shapes as for any other sandbox.

Sandboxes: network rules work for every sandbox

GET /v1/sandboxes/{id}/network and PUT /v1/sandboxes/{id}/network now work for every sandbox. Previously, these requests returned 409 served_elsewhere for some newly created sandboxes. The rules have not changed: a change applies to open connections too, so a connection the new rules refuse is closed, and the response reports enforced: true once the sandbox's server applies the new version. A sandbox created with network rules can now also be created from a snapshot; previously that request returned 501 feature_unavailable for some newly created sandboxes.

Sandboxes: a verb the API key does not allow returns 403, and older keys can wake, extend, update, set retention and restart

A request for a sandbox operation that the API key's grant does not include now returns 403 forbidden; previously some of these requests returned 409 served_elsewhere, and retrying them at the address in the error returned the same error again. API keys created before November 1, 2026 that can create sandboxes can now also call :wake, :extend, :update, :retention, :restart, :fork, :resize and :switch-image on them, as newer keys can. No change to existing keys or code is needed.

Sandboxes: a fork returns 409 fork_unsafe when a copy would repeat the source's random numbers

A fork (forkOf, or POST /v1/sandboxes/{id}:fork) copies every process's memory, including the state of each language runtime's own random number generator. The kernel's generator is reseeded in every copy, but Node's UUID cache and OpenSSL generator, Python's random and ssl generators and Java's SecureRandom are reseeded only when the process loads Runtime's reseed shim. A fork whose source runs a Node, Python or Java process that the shim does not reach now returns 409 fork_unsafe, names each such process, and makes no copy; previously the copies started and could return the same UUIDs, tokens and keys as the source and as each other. While a copy is made, the source's Node, Python and Java processes are paused for a few milliseconds, so no copy runs one of them before it is reseeded.

Sandboxes: a fork with count above 1 makes its copies in parallel

POST /v1/sandboxes/{id}:fork with {"count": 4} previously made each copy after the one before it; it now makes them at the same time and records them in one commit, so a fork of 4 copies of a running sandbox returns in 216 ms at the median instead of 340 ms.

Tailscale: new sandboxes can join a tailnet again

POST /v1/sandboxes/{id}/tailscale, GET /v1/sandboxes/{id}/tailscale, DELETE /v1/sandboxes/{id}/tailscale, and the tailscale field on POST /v1/sandboxes and POST /v1/sandboxes/{id}:update now work for every sandbox. Previously, these requests returned 501 feature_unavailable for some newly created sandboxes. The rules have not changed: the auth key is read from your job secret with your key's own permission to reveal it and is never returned or logged. While a sandbox is on a tailnet, forking it or taking a memory snapshot of it returns 409 tailscale_joined. If a create with tailscale cannot join, the sandbox is stopped and the request returns 422 tailscale_failed.

API: keep-alive connections stay open across Runtime releases

When a server's API process was replaced during a release, a request in progress on a keep-alive connection could be answered with connection: close, and the client had to open a new connection. The connection now moves to the new process between two requests, after the last response is sent in full, and stays open. Connections whose last request had a body, HTTP/1.0 connections, and connections whose response included connection: close are closed after their response instead, as before.

Jobs: a vouch is no longer cleared by secrets scoped to other sandboxes by ID

Any change to an egress secret's scope, including a list of other sandboxes' IDs, cleared every vouch in the organization, and the job's runs carried no job_vouched_by claim in their identity tokens. Scopes by sandbox ID no longer affect a vouch, except for a run whose own sandbox ID is added to one, which carries no job_vouched_by claim. A change to a secret with no scope, a label or App scope, a secret's policy, or the upstream proxy still clears the vouch until an owner or admin vouches again. Vouches that were in effect when this change was released remain in effect.

Keys: POST /v1/keys no longer slows down or returns 502 keys_unavailable in accounts with many keys

In an account holding tens of thousands of keys, creating a key could take 15 seconds or more and return 502 keys_unavailable, and other requests in the account waited while it ran. Creating a key now reads only the chain of keys that made the calling key and the keys under a maker with a daily limit, and completes in under 100 ms in an account with 50,000 keys.

Sandboxes use included free usage before credit in every region

A sandbox's running time is charged to the month's included free usage first, then to credit, in every region where the account runs sandboxes. A sandbox pauses only when both are used up; a wake then returns 402 insufficient_credit until credit is added. At the start of each month, the new month's included usage applies immediately for accounts whose included usage renews monthly.

API keys: keys Runtime creates for your builds, CI/CD checks, app versions, agent sessions and Actions runners no longer count toward the limit of 1,000 live keys per member

Previously, a busy CI/CD repository could fill the limit with its checks' short-lived keys, and POST /v1/keys returned 409 limit_reached to the member who connected it. Those keys are still bounded by the work that uses them and expire with it.

spreadFrom keeps a new sandbox off its peers' servers

POST /v1/sandboxes accepts spreadFrom again: 1 to 16 IDs of your own sandboxes. The new sandbox starts on a server that none of them is running on, and stays apart from them each time it is placed again. When no other server has room, the create returns 503 capacity_unavailable immediately and nothing is made or charged. An ID this key cannot read returns 400 invalid_request. Snapshot IDs are not accepted in spreadFrom, and a create that copies a snapshot returns 501 feature_unavailable when it sets spreadFrom.

Sandbox creates in a full region fail immediately and name the size

When no server in the region has room for a new sandbox, POST /v1/sandboxes now returns 503 capacity_unavailable immediately, before any state is written, and the error message states the vCPUs and memory that could not be placed. Retry after the Retry-After delay with the same Idempotency-Key, or request a smaller sandbox.

Sandboxes: creates from an existing image no longer return 404 image_not_found for some accounts

POST /v1/sandboxes could return 404 image_not_found (or snapshot_not_found, volume_not_found) for an image, snapshot or volume that exists in the account. The create now succeeds in the same request, and a retry with the same Idempotency-Key returns the same sandbox.

Sandboxes: creates no longer return 501 or an immediate 503 for some accounts

POST /v1/sandboxes could return 501 (for example env_unavailable or feature_unavailable) or an immediate 503 for request bodies the API supports. A create rejected before anything was applied now succeeds in the same request, and a retry with the same Idempotency-Key returns the same sandbox. A 503 busy whose create may have been applied is still returned, so a retry never creates a second sandbox.

Correct clock after a restore

A sandbox created from a template, forked, or woken from a pause now reads the correct wall-clock time from its first instruction. Previously, such a sandbox could read the time at which its memory snapshot was saved.

Creates from an image that cannot boot return 404

POST /v1/sandboxes now returns 404 image_not_found, naming the image and the reason, when the image is not the organization's or was deleted. Previously it returned 503 busy, so retries failed the same way. Nothing is created.

Requests that carry a secret's placeholder are never sent unreplaced

If the server running a sandbox has not received secret updates in the last 15 seconds, a request that carries a secret's placeholder, or that a secret's header rules apply to, returns 503 with Retry-After: 1 and X-Runtime-Egress: secret-not-current instead of being sent with the placeholder. Retry the request.

Files: watches work in sandboxes created from the default image

POST /v1/sandboxes/{id}/files/watches (and files.watch() in the SDKs) returned 500 watch_failed ("The watch exited while starting") in sandboxes created from the default image. A watch now runs in every image, and when one fails to start, the error message includes the exit code and the reason the sandbox reported.

Sandbox creates move off a failed host immediately

When the host chosen for a new sandbox has failed and refuses connections, POST /v1/sandboxes now places the sandbox on another host immediately, instead of returning 503 until the failed host's lease expires (about 2 seconds). If that host had already started creating the sandbox, the request still waits for it, so each Idempotency-Key creates at most one sandbox.

A Function with no running copy starts one for its next request

A request to a Function that has scaled to zero waits at the router while a copy is started from the version's saved copy, then is answered; it no longer returns 503 machine_no_capacity after 10 seconds. sleepAfterMs now applies: a copy that requests no longer need keeps running for sleepAfterMs after its last request before it stops, so requests a moment apart reach a running copy.

A crashed process restarts immediately

When the process of an App, a Function or a container exits, its first restart now happens immediately instead of after 1 second; an App's process killed with SIGKILL answers requests again about 60 ms later. A process that exits again still waits 1 second, then 2, 4 and so on up to 60 seconds, and ten exits within 10 minutes still mark it failed.

CI/CD: always run a pull request's own tests

A split step with own: true (split: { files: "**/*.test.{ts,tsx}", own: true }) runs the test files a change adds or changes that no other step's split names. With such a step in runtime.yml, a pull request that adds or changes a test file no step runs (any language: TypeScript and JavaScript, Python, Go, or a Rust crate's files) fails with a tests not run job that lists each file and the reason. Configs without an own step are unchanged.

Parallel forks no longer return 503 busy while a snapshot copy arrives

When a snapshot's own server was full, creating many sandboxes from that snapshot at once could return 503 busy ("Cloud is busy; retry"). It happened when a create joined a copy of the snapshot that was arriving on another server. These creates now succeed, and the copy finishes arriving alongside them.

CI/CD checks every push of the branches you list

Add pushes: [{ branch: next, steps: [...] }] to the production branch's .runtime/checks.yml to check each push of a branch that has no pull request of its own, such as a staging branch. Each such check runs only the named steps, appears on GitHub as runtime @ <branch> rather than runtime, and once started it runs to its own verdict instead of being replaced by a newer push.

Slack triggers in the v2 Agents API

POST /v2/agents/slack/installs returns a link that adds Runtime's Slack app to a workspace, valid for 15 minutes; POST /v2/agents/slack/installs:finish completes it with Slack's code and the link's state, and accepts only a key of the project that created the link. GET /v2/agents/slack/teams lists the project's workspaces and DELETE /v2/agents/slack/teams/{team} removes one. POST /v2/agents/{id}/triggers with kind: "slack" and config: {team, channel} adds a trigger: each new message in the channel starts a session, each reply in its thread is sent to the same session, and the session's replies, status, questions and result are posted in that thread. Triggers are read with GET /v2/agents/{id}/triggers and GET /v2/triggers/{id}, changed with PATCH /v2/triggers/{id}, paused and resumed with POST /v2/triggers/{id}:pause and :resume, and deleted with DELETE /v2/triggers/{id}.

Other days.

Cut your cloud billby 75%.

No card to start. Billed by the second after.

Start free, no card