Skip to content

What's new in 0.6.0

Published on 2026-10-03: Python SDK rayito 0.6.0, TypeScript SDK rayito 0.6.0 and rayd 0.6.0.

0.6.0 brings six new features and a single convention for deploying their infrastructure in your account. All of them are off by default: without the corresponding option, a sandbox behaves byte for byte as in 0.5.x and the SDK makes no new call to AWS (a golden trace in each SDK's tests checks it).

Upgrade to 0.6.1

0.6.1 fixes that redeploying an optional stack sent its parameters back to their defaults and that reincarnate() lost mounts=, events= and telemetry=: What's new in 0.6.1.

At a glance

Feature Python TypeScript Image Idle cost Guide
S3 mounts mounts={"/mnt/x": S3Mount(...)} mounts: { "/mnt/x": new S3Mount({...}) } rayito-base-caps, rayd 0.6.0 $0 (IAM only) S3 mounts
Sizes size="4gb" size: "4gb" one image per size (rayito image publish --sizes) snapshot per published image Sizes
Events and webhooks events=LifecycleEvents() events: new LifecycleEvents() any with rayd 0.6.0 ≈ $0.40/month (events-webhooks stack) Events and webhooks
OTLP export telemetry=TelemetryExport(...) telemetry: new TelemetryExport({...}) rayd 0.6.0; caps with OtlpAuth.execution_role() $0 (IAM only) OTLP export
Declarative templates Template.build(t, "name", bucket=...) Template.build(t, "name", { bucket }) composed on top of a published rayito-base snapshot per built version Templates
Secret gateway gateways={"api": SecretGateway(...)} gateways: { api: new SecretGateway({...}) } any with rayd 0.6.0 $0 (Secrets Manager reads by usage) Secret gateway
Optional stacks OptionalStacks().deploy(...) new OptionalStacks().deploy(...) — that of each component Optional stacks

All accepted against real AWS; each guide cites its measurements in AWS_API_NOTES.md. The figures and permissions of each, in a single table: Optional features and their cost.

S3 mounts

A bucket (or a prefix) as a normal folder in the sandbox, with Mountpoint for Amazon S3. create() does not return the sandbox until each mount is mounted; if any fails, it terminates it and raises MountException (TypeScript: MountError) with a closed code.

from rayito import S3Mount, Sandbox

with Sandbox.create(
    "rayito-base-caps",
    execution_role_arn="arn:aws:iam::123456789012:role/rayito-sandbox-caps",
    mounts={"/mnt/data": S3Mount(bucket="amzn-s3-demo-bucket", prefix="team7/")},
) as sbx:
    print(sbx.commands.run("ls /mnt/data").stdout)
    print(sbx.mounts["/mnt/data"].state)  # "mounted"
import { S3Mount, Sandbox } from "rayito";

await using sbx = await Sandbox.create({
  template: "rayito-base-caps",
  executionRoleArn: "arn:aws:iam::123456789012:role/rayito-sandbox-caps",
  mounts: { "/mnt/data": new S3Mount({ bucket: "amzn-s3-demo-bucket", prefix: "team7/" }) },
});
console.log((await sbx.commands.run("ls /mnt/data")).stdout);
console.log((await sbx.mounts()).get("/mnt/data")?.state); // "mounted"

It needs the s3-mounts stack's policy on the execution role and the bucket in the image's allowlist (RAYITO_ALLOWED_MOUNT_BUCKETS).

Size catalog

Five named sizes (512mb, 1gb, 2gb, 4gb, 8gb) that the SDK resolves on the client, with no RPC: size="4gb" launches the rayito-base-4gb image. An intermediate size is rounded up with a warning. rayito image publish --sizes 512mb,4gb publishes the images of each size from the same artifact.

from rayito import Sandbox

with Sandbox.create(size="4gb") as sbx:
    info = sbx.get_info()
    print(info.size, info.baseline_memory_mib, info.baseline_cpu)  # 4gb 4096 8
import { Sandbox } from "rayito";

await using sbx = await Sandbox.create({ size: "4gb" });
const info = await sbx.getInfo();
console.log(info.size, info.baselineMemoryMib, info.baselineCpu); // 4gb 4096 8
rayito image publish --artifact rayito-image.zip --base-image-version 1 \
  --bucket amzn-s3-demo-bucket --sizes 512mb,4gb
rayito image sizes

Lifecycle events and webhooks

rayd signs each transition (created, paused, resumed, killed) and a stack in your account stores them in DynamoDB and delivers them to your webhooks with E2B's signature: a receiver written for E2B verifies them unchanged (how to verify the signature).

from rayito import LifecycleEvents, Sandbox

events = LifecycleEvents()  # the `rayito events deploy` stack
with Sandbox.create(
    execution_role_arn="arn:aws:iam::123456789012:role/rayito-sandbox",
    logging="cloudwatch",
    events=events,
) as sbx:
    sbx.pause()
    sbx.resume()
for event in events.get_events(sandbox_id=sbx.sandbox_id, order="asc"):
    print(event.type)  # sandbox.lifecycle.created, .paused, .resumed, .killed
import { LifecycleEvents, Sandbox } from "rayito";

const events = new LifecycleEvents(); // the `rayito events deploy` stack
const sbx = await Sandbox.create({
  executionRoleArn: "arn:aws:iam::123456789012:role/rayito-sandbox",
  logging: "cloudwatch",
  events,
});
await sbx.pause();
await sbx.resume();
await sbx.kill();
for (const event of await events.getEvents({ sandboxId: sbx.sandboxId, order: "asc" })) {
  console.log(event.kind); // created, paused, resumed, killed
}
rayito events deploy --artifact-bucket amzn-s3-demo-bucket --log-group-name /rayito/rayito-base
rayito events webhook add https://hooks.example.com/rayito --secret-name my-webhook \
  --type sandbox.lifecycle.killed

OTLP export from rayd

rayd exports 7 CPU, memory and disk metrics to CloudWatch over OTLP/HTTP every interval_s (60 s by default), signed with SigV4 (the execution role) or with a token stored in Secrets Manager. About $0.00002 per sandbox-hour.

from rayito import OtlpAuth, Sandbox, TelemetryExport

with Sandbox.create(
    "rayito-base-caps",
    execution_role_arn="arn:aws:iam::123456789012:role/rayito-sandbox-caps",
    telemetry=TelemetryExport(auth=OtlpAuth.execution_role()),
) as sbx:
    print(sbx.get_telemetry_status().exported)
import { OtlpAuth, Sandbox, TelemetryExport } from "rayito";

await using sbx = await Sandbox.create({
  template: "rayito-base-caps",
  executionRoleArn: "arn:aws:iam::123456789012:role/rayito-sandbox-caps",
  telemetry: new TelemetryExport({ auth: OtlpAuth.executionRole() }),
});
console.log((await sbx.getTelemetryStatus()).exported);

Declarative templates

E2B v2's Template DSL, compiled in your process to a deterministic Dockerfile on top of a published rayito-base image, and built with Lambda MicroVMs. No control plane of its own. In the shim, Template and AsyncTemplate really build with E2B's signature.

from rayito import Sandbox, Template, wait_for_port

t = (
    Template()
    .from_base_image("rayito-base")
    .pip_install(["pandas==2.2.3"])
    .set_start_cmd("python3 -m http.server 8000", wait_for_port(8000))
)
info = Template.build(t, "my-api", bucket="amzn-s3-demo-bucket")
with Sandbox.create(info.template_id) as sbx:
    print(sbx.commands.run("curl -s localhost:8000").exit_code)
import { Sandbox, Template, waitForPort } from "rayito";

const t = new Template()
  .fromBaseImage("rayito-base")
  .pipInstall(["pandas==2.2.3"])
  .setStartCmd("python3 -m http.server 8000", waitForPort(8000));
const info = await Template.build(t, "my-api", { bucket: "amzn-s3-demo-bucket" });
await using sbx = await Sandbox.create({ template: info.templateId });
console.log((await sbx.commands.run("curl -s localhost:8000")).exitCode);
rayito template build my_template.py --name my-api --bucket amzn-s3-demo-bucket
rayito template logs my-api

Secret gateway

The sandbox's code uses a credential without being able to read it: rayd opens a loopback gateway that checks a method and path allowlist, injects the real header (read from Secrets Manager) and forwards to the upstream you declared. sbx.gateways.refresh() rotates the secret without recreating the sandbox.

from rayito import Sandbox, SecretGateway

with Sandbox.create(
    gateways={
        "anthropic": SecretGateway(
            upstream="https://api.anthropic.com",
            headers={"x-api-key": "anthropic"},  # secret name, not its value
            allow=[("POST", "/v1/messages")],
        )
    }
) as sbx:
    url = sbx.gateways["anthropic"].url  # http://127.0.0.1:<port>
    sbx.commands.run("python agent.py", envs={"ANTHROPIC_BASE_URL": url})
import { Sandbox, SecretGateway } from "rayito";

await using sbx = await Sandbox.create({
  gateways: {
    anthropic: new SecretGateway({
      upstream: "https://api.anthropic.com",
      headers: { "x-api-key": "anthropic" }, // secret name, not its value
      allow: [["POST", "/v1/messages"]],
    }),
  },
});
const url = sbx.gateways.get("anthropic")?.url ?? "";
await sbx.commands.run("python agent.py", { envs: { ANTHROPIC_BASE_URL: url } });

Optional stacks and rayito stack

Each feature with its own infrastructure is an OptionalStack component: a CloudFormation template that you deploy yourself, with OptionalStacks.deploy/status/destroy or rayito stack. deploy always prints the cost and asks for confirmation; nothing deploys itself.

from rayito import OptionalStacks

stacks = OptionalStacks(region="us-east-1")
print([c.name for c in stacks.components() if c.supported])  # without calling AWS
stacks.deploy("otlp-export")
import { OptionalStacks } from "rayito";

const stacks = new OptionalStacks({ region: "us-east-1" });
console.log(stacks.components().filter((c) => c.supported).map((c) => c.name));
await stacks.deploy("otlp-export");
rayito stack list
rayito stack deploy otlp-export
rayito stack status otlp-export

Under the hood: ConfigureSandbox

The features that live inside the sandbox (mounts, events, telemetry and gateways) reach rayd through a single new channel, ConfigureSandbox, right after the first Health and before create() returns the sandbox. If a section fails, create() terminates the MicroVM it just launched (unless keep_on_failure=True) and rethrows the error: you never receive a half-configured sandbox. Against an image with a rayd earlier than 0.6.0, those options fail with UnimplementedError. Design: ADR-015 and ADR-016 in ARCHITECTURE.md.

How to upgrade from 0.5.x

  1. Upgrade the SDK (pip install -U "rayito[cli]" or pnpm add rayito@latest). Without new options, your code stays the same.
  2. To use mounts, events, telemetry, gateways or the templates' set_start_cmd(), republish your image on rayd 0.6.x (Images) and check compatibility with rayito doctor.
  3. Deploy only the stacks of the features you use (rayito stack deploy <component>).

There are no incompatible changes in the public API from 0.5.x. The only thing that changes for the E2B shim: Template, AsyncTemplate, TemplateException and BuildException go from raising UnimplementedError to really working (E2B parity).

What 0.6.0 does not bring

EFS volumes (volumes=) and custom domain (domain=) were still in development: their options existed in create()'s signature and raised UnimplementedError without calling AWS. EFS volumes and CustomDomain (experimental) arrive in 0.7.0; domain= remains in development.

Full details: Changelog.