Saltar a contenido

Sandbox

Sandbox (síncrono) y AsyncSandbox (asyncio). Guías: Ciclo de vida, Comandos, Ejecutar código y Async.

Sandbox(*, info: SandboxInfo, access_token: str, control_plane: ControlPlane, transport: TransportSettings, refresher: TokenRefresher, request_timeout: float, ready_timeout: float, reconnect_timeout: float = DEFAULT_RECONNECT_TIMEOUT_SECONDS, logger: logging.Logger | None = None)

Un MicroVM con rayd dentro. Se crea con create() o connect().

access_token: str property

Guárdalo junto al sandbox_id para poder hacer connect() después.

info: SandboxInfo property

Última SandboxInfo conocida sin llamar a AWS; get_info() la refresca.

launch_info: SandboxInfo property

La SandboxInfo con la que se abrió el handle (la respuesta de run-microvm en create(), el get-microvm de connect()), nunca refrescada y sin llamar a AWS.

resume_generation: int property

Última generación de resume vista en Health (cambia con cada /resume).

metadata: dict[str, str] property

Metadatos de create(metadata=) tal como los devolvió el último Health; inmutables durante la vida del sandbox, así que son definitivos desde el primer agent_ready (vacíos sobre una imagen anterior a M6).

persist: S3Prefix | None property

El S3Prefix (con name) enlazado por create(persist=) o connect(persist=); None si el sandbox no persiste.

transfer: S3Staging | None property

El S3Staging de create(transfer=)/connect(transfer=) o de RAYITO_TRANSFER_BUCKET; None sin bucket de transferencias.

last_restore: RestoreResult | None property

El resultado del restore automático de create(persist=); None si no hubo (primera vida de ese name) o si el sandbox no persiste.

gateways: GatewayHandle property

{nombre: GatewayStatus} de create(gateways=): vacío (y su refresh() no hace nada) sin esa opción. Tras connect() desde otro proceso se reconstruye con ConfigureStatus (nombres y puertos; su refresh() sólo relee el estado, no rota secretos). GatewayStatus.url es "http://127.0.0.1:<puerto>", el host al que apuntar ANTHROPIC_BASE_URL y similares dentro del sandbox.

mounts: dict[str, MountStatus] property

Estado en vivo de cada mounts= (m15-s3-mounts): una ConfigureStatus por lectura, nunca cacheada — un montaje puede pasar de "pending" a "mounted"/"failed" entre dos lecturas de esta propiedad. Vacío si create()/take() no recibió mounts=.

volumes: dict[str, VolumeStatus] property

Estado en vivo de cada volumes= (m15-efs-volumes): una ConfigureStatus por lectura, nunca cacheada — tras un resume() un volumen puede pasar por "remounting"/"degraded". Vacío si create() no recibió volumes=.

commands: Commands property

ProcessService: run, list, kill, connect, send_stdin, close_stdin.

files: Filesystem property

FilesystemService: read, write, write_files, list, exists, get_info, remove, rename, make_dir, watch_dir.

pty: Pty property

PtyService: create, connect, send_input, resize, kill.

agent: Agent property

El agente de IA de este sandbox (ai-agent-core): run, stream, prepare. Tocar esta propiedad no manda ningún RPC. Se construye en el primer uso, así que import rayito no carga rayito._agent.

git: Git property

El módulo git de E2B 2.x (clone, status, commit, push...) sobre commands.run; se construye en el primer uso.

create(template: str | None = None, *, template_version: str | None = None, timeout: int = DEFAULT_TIMEOUT_SECONDS, max_lifetime: int | None = None, on_timeout: TimeoutActionName | None = None, idle: IdlePolicy | None = DEFAULT_IDLE_POLICY, envs: Mapping[str, str] | None = None, metadata: Mapping[str, str] | None = None, cpu_time_limit: int | None = None, execution_role_arn: str | None = None, allowed_ports: Sequence[PortLike] | None = None, ingress: Sequence[str] | None = None, egress: Sequence[str] | None = None, network: NetworkPolicy | NetworkOptions | None = None, allow_internet_access: bool = True, logging: LoggingOption = 'disabled', region: str | None = None, session: boto3.session.Session | None = None, access_token: str | None = None, ready_timeout: float = DEFAULT_READY_TIMEOUT_SECONDS, request_timeout: float = DEFAULT_REQUEST_TIMEOUT_SECONDS, reconnect_timeout: float = DEFAULT_RECONNECT_TIMEOUT_SECONDS, keep_on_failure: bool = False, control_plane: ControlPlane | None = None, transport: TransportSettings | None = None, persist: S3Prefix | None = None, persist_timeout: float = DEFAULT_PERSIST_TIMEOUT_SECONDS, pool: SandboxPool | None = None, transfer: S3Staging | None = None, logger: logging.Logger | None = None, secrets: Mapping[str, str | SecretRef] | None = None, secret_cache: SecretCache | None = None, index: DynamoDbIndex | None = None, tracer_provider: TracerProviderLike | None = None, mounts: Mapping[str, Any] | None = None, volumes: Mapping[str, Any] | None = None, size: str | SizeRequest | None = None, events: Any | None = None, telemetry: Any | None = None, gateways: Mapping[str, Any] | None = None, domain: Any | None = None) -> Self classmethod

run-microvm → token del proxy → sondeo de Health hasta agent_ready y kernel_ready (el kernel por defecto ya rotado).

Sin max_lifetime ni on_timeout, timeout es la vida máxima (running + suspended, tope 8 h) y no se puede cambiar después. Con cualquiera de los dos (exige una imagen 0.3.0 o posterior), timeout es el plazo lógico que impone rayd aunque el cliente muera, movible con set_timeout() y connect(timeout=), y max_lifetime (120..=28800, por defecto timeout + 60) es maximumDurationInSeconds: el plazo nunca pasa de max_lifetime - 60 s desde el arranque. Al vencer, on_timeout='kill' (por defecto) termina el sandbox y on_timeout='pause' lo suspende (necesita idle; idle.auto_resume decide si una petición posterior lo reanuda con un plazo nuevo de max(timeout, 300 s)). Una imagen anterior a 0.3.0 falla con LifecycleUnsupportedException y el VM se termina (salvo keep_on_failure). idle=None desactiva la auto-suspensión. Sin idle, la de 300 s por defecto se desactiva si no cabe antes del timeout (o de max_lifetime), y en modo pause baja a 60 s; un idle explícito que no cabe es InvalidArgumentException. Sin execution_role_arn no hay logs de runtime. reconnect_timeout acota cuánto espera el SDK a que el agente vuelva tras un corte (pausa, auto-resume, 502 del proxy) antes de fallar. metadata son etiquetas no secretas (viajan en el runHookPayload junto a envs, dentro de sus 4096 caracteres) que el agente devuelve en Health: inmutables durante la vida del sandbox, legibles con sbx.metadata, get_info() y Sandbox.list(metadata=...). cpu_time_limit son segundos de CPU, no de pared (1..=28800): cada proceso y PTY del sandbox recibe RLIMIT_CPU con ese tope (SIGXCPU al alcanzarlo, SIGKILL 5 s después); un proceso dormido no lo consume y el kernel de run_code nunca lo recibe. Para un tope de pared usa timeout en cada comando.

pool= es azúcar de pool.take(): el sandbox sale de una plaza suspendida del SandboxPool (ya arrancado) con la configuración de lanzamiento de su PoolConfig; cualquier otro kwarg de lanzamiento o de plano distinto de su valor por defecto es InvalidArgumentException. Pasan los tres timeouts del handle (ready_timeout, request_timeout, reconnect_timeout), secrets/secret_cache, transfer, logger y tracer_provider; gateways= no: llama a pool.take(gateways=...).

persist=S3Prefix(...) (requiere execution_role_arn) enlaza el HOME del sandbox a s3://bucket/prefix/name/: con name dado, tras Health el SDK restaura el checkpoint que haya bajo el prefijo (sbx.last_restore; None si no había ninguno) y sin name lo fija al sandbox_id. persist_timeout es el deadline del restore automático y de reincarnate(). Un restore que falla (salvo "no hay checkpoint") cierra y termina el sandbox como un fallo de readiness.

allow_internet_access=False y network={"allow_out", "deny_out", "egress_proxy"} (semántica de E2B: sin deny_out todo está permitido y allow_out gana a deny_out; ALL_TRAFFIC es todo) se aplican en el guest y exigen una imagen 0.3.0 o posterior de rayito-base-caps: rayd instala deny-all antes de /run y create() manda la política real con UpdateNetwork antes de devolver. Sobre una imagen que no la aplica (Health.egress_enforcement NONE), el SDK termina el VM aunque haya keep_on_failure y lanza UnimplementedError. Una entrada de nombre de host ("api.example.com", "*.example.com") o egress_proxy activan el proxy local de rayd, que sólo alcanzan los clientes que respetan HTTPS_PROXY/ALL_PROXY; el resto falla cerrado.

transfer=S3Staging(...) (o RAYITO_TRANSFER_BUCKET con None) es el
bucket de files.upload_url/download_url y de los ficheros grandes
configuración del cliente, nada viaja al VM, también con pool=. Con persist en el mismo bucket, los prefijos deben ser disjuntos o es InvalidArgumentException antes de llamar a AWS.

secrets={"ENV": "nombre" | SecretRef} guarda en el handle sólo las referencias y entrega los valores como variables de entorno de cada commands.run, pty.create, run_code (contextos Python) y create_code_context, nunca en el runHookPayload, metadata ni el entorno de la imagen. Se resuelven (y quedan en SecretCache) antes de run-microvm: un secreto que falta falla sin lanzar un VM. Se admite con pool= (lo enlaza take()).

Coste y activación


Activa: secrets= inyecta secretos de Secrets Manager como variables de entorno; secret_cache=SecretCache(ttl_seconds=300) fija la caché (si no, una compartida por región y sesión, TTL 300 s). Recursos y llamadas AWS: secretsmanager:GetSecretValue una vez por secreto y TTL (un acierto de caché no llama a AWS); ningún recurso nuevo. Sin secrets= no se crea ningún cliente secretsmanager. Coste aproximado: $0,05 por 10 000 llamadas + $0,40 por secreto y mes (Secrets Manager, us-east-1, 2026-09-30); ≈ $0,04/mes por secreto y proceso con el TTL por defecto. IAM: secretsmanager:GetSecretValue sobre ...:secret:rayito/* en las credenciales del llamante (no el execution role). Cómo apagarla: secrets=None y secret_cache=None (por defecto). Ejemplo: SecretStore().create("openai", key) sbx = Sandbox.create(secrets={"OPENAI_API_KEY": "openai"}) sbx.commands.run("python agent.py")

index=DynamoDbIndex("tabla") escribe, tras run-microvm y antes de la sonda de readiness, una fila inmutable con metadata, la imagen y startedAt en la tabla opcional del índice, para que Sandbox.list(metadata=..., index=...) encuentre el sandbox también suspendido y sin sondearlo. Si PutItem falla, con on_write_failure='terminate' (por defecto) el VM se termina (salvo keep_on_failure=True) y se lanza IndexWriteException; con 'warn' sólo se avisa. Con pool= es InvalidArgumentException: se configura en PoolConfig(index=...). kill() no toca el índice.

Coste y activación


Activa: index=DynamoDbIndex(...) escribe la fila del índice de metadatos de este sandbox. Recursos y llamadas AWS: dynamodb:PutItem una vez (condicional attribute_not_exists(pk)); ningún recurso nuevo (la tabla la despliegas tú con infra/metadata-index.yaml). Sin index= ningún cliente dynamodb. Coste aproximado: ~1 WRU ≈ $0,000000625 por sandbox + $0,25/GB-mes (DynamoDB on-demand, us-east-1, consultado 2026-09-30). IAM: dynamodb:PutItem sobre la tabla (RayitoIndexWriter) en las credenciales del llamante. Cómo apagarla: index=None (por defecto). Ejemplo: idx = DynamoDbIndex("rayito-sandboxes") sbx = Sandbox.create(metadata={"user": "42"}, index=idx) sbx.pause() items = list(Sandbox.list(metadata={"user": "42"}, states=["SUSPENDED"], index=idx))

tracer_provider= guarda en el handle una Instrumentation que instrumenta sus operaciones (commands.run, run_code, files.*, kill/pause/resume de instancia); create() en sí es un único span rayito.sandbox.create que envuelve run-microvm, la espera de Health, la escritura del índice y la política de red. Con pool= se enlaza al handle que entrega take(), sin span propio (no hay lanzamiento que medir). None (por defecto) es NOOP: cero import de opentelemetry y cero coste por llamada.

Coste y activación


Activa: tracer_provider= (un TracerProvider de opentelemetry.trace, o cualquier objeto con get_tracer(name, version)). Recursos y llamadas AWS: ninguno; rayito sólo crea spans en el proveedor del llamante. Coste aproximado: $0 de AWS; el coste (si lo hay) es el del backend de exportación que configure el llamante. IAM: ninguno adicional. Cómo apagarla: no pases tracer_provider= (por defecto None). Ejemplo: from opentelemetry import trace sbx = Sandbox.create(tracer_provider=trace.get_tracer_provider()) sbx.commands.run("echo hola") # span "rayito.commands.run" sbx.kill()

De las siete opciones 0.6 (M15), sólo domain= sigue sin cablear (para un dominio propio usa CustomDomain, experimental): ponerla a algo distinto de None lanza UnimplementedError nombrando m15-custom-domain, antes de run-microvm (_feature_options.plan_features) y sin ninguna llamada a AWS. mounts=, volumes=, size=, events=, telemetry= y gateways= ya son reales: cada una lleva su bloque "Coste y activación" aquí debajo o en su clase (SecretGateway). Con todas en None (su valor por defecto) el comportamiento es exactamente el de 0.5.x.

mounts= (m15-s3-mounts) monta cada S3Mount en su ruta (bajo /mnt/ o /home/user/, como mucho 4) con mount-s3, en el mismo ConfigureSandbox que el resto de secciones; sólo en rayito-base-caps (o una variante por tamaño) y sólo los buckets del allowlist de la imagen. Si un montaje falla, termina el sandbox (salvo keep_on_failure) y relanza el error; sbx.mounts da el estado en vivo.

Coste y activación


Activa: mounts={"/mnt/data": S3Mount(bucket="...")} en create(), con execution_role_arn= y una imagen rayito-base-caps cuyo RAYITO_ALLOWED_MOUNT_BUCKETS incluya el bucket. Recursos y llamadas AWS: ningún recurso nuevo; mount-s3 hace las peticiones S3 normales (GetObject/ListObjectsV2, y PutObject/DeleteObject con read_only=False) con el execution role. La política sale de la pila s3-mounts (rayito stack deploy s3-mounts, sólo IAM). Coste aproximado: $0 propio de Rayito; pagas las peticiones y el almacenamiento normales de S3 del bucket montado (us-east-1, consultado 2026-10-01); la pila s3-mounts es $0 (sólo IAM). IAM: RayitoS3MountAccess (infra/s3-mounts.yaml) en el execution role, acotada a un bucket y a sus prefijos. Cómo apagarla: no pases mounts= (por defecto None); rayito stack destroy s3-mounts quita la política (no borra objetos). Ejemplo: from rayito import S3Mount

 sbx = Sandbox.create("rayito-base-caps", execution_role_arn=role_arn,
                      mounts={"/mnt/data": S3Mount(bucket="mi-bucket")})
 sbx.commands.run("ls /mnt/data")

volumes= (m15-efs-volumes, experimental) monta cada EfsVolume en su ruta (bajo /mnt/ o /home/user/, como mucho 4) con amazon-efs-utils (TLS + IAM), en el mismo ConfigureSandbox que el resto de secciones y antes de que create() vuelva. Exige execution_role_arn=, exactamente un conector propio en egress= (un MicroVM sólo admite uno, así que el sandbox no tiene INTERNET_EGRESS) y una imagen con amazon-efs-utils (rayito-base-caps-efs); si un volumen no monta, termina el sandbox (salvo keep_on_failure) y lanza VolumeMountException, y sobre otra imagen UnimplementedError. sbx.volumes da el estado en vivo y reincarnate() los vuelve a montar en el sucesor.

Coste y activación


Activa: volumes={"/mnt/datos": vol} en create(), con execution_role_arn=, egress=[<ConnectorArn>] y la imagen opcional rayito-base-caps-efs (rayito image publish --with-efs --os-capabilities ALL). Recursos y llamadas AWS: ningún recurso nuevo por create(); si un EfsVolume no trae mount_target_ip, una DescribeMountTargets por sistema de ficheros y create(), con las credenciales del llamante. El sistema de ficheros, sus mount targets y el conector los crea la pila efs-volumes (EfsVolumes.deploy()), aparte; el tráfico NFS va con el execution role. Coste aproximado: $0 propio de Rayito; pagas el almacenamiento y el rendimiento normales de EFS (Elastic) de lo que leas y escribas, y la imagen con amazon-efs-utils ocupa ≈ 198 MB más de código instalado (us-east-1, consultado 2026-10-04, https://aws.amazon.com/efs/pricing/). IAM: el llamante, elasticfilesystem:DescribeMountTargets (sólo sin mount_target_ip); el execution role, la política CallerPolicyArn de la pila efs-volumes (ClientMount, y ClientWrite salvo sólo lectura). Cómo apagarla: no pases volumes= (por defecto None); EfsVolumes.destroy() quita la pila. Ejemplo: vol = VolumeStore(file_system_id=fs_id).create("datos-agente-7") sbx = Sandbox.create("rayito-base-caps-efs", execution_role_arn=role_arn, egress=[connector_arn], volumes={"/mnt/datos": vol}) sbx.commands.run("ls /mnt/datos")

size= (m15-sizes-catalog) elige, en el cliente y sin ninguna llamada a AWS, la imagen de ese tamaño del catálogo cerrado (512mb/1gb/2gb/4gb/8gb; SizeRequest(memory_mib=...) redondea hacia arriba): rayito-base con size="4gb" lanza rayito-base-4gb, que tiene que estar publicada. No cambia la memoria de ninguna imagen ni de un sandbox en marcha; ver rayito._sizing.

Coste y activación


Activa: size="4gb" (o size=SizeRequest(memory_mib=...)) en create(), tras rayito image publish --sizes 4gb. Recursos y llamadas AWS: ninguno nuevo al lanzar (el tamaño va en el nombre de la imagen); get_info() hace como mucho una GetMicrovmImageVersion gratuita por versión de imagen y proceso. Cada tamaño publicado es una imagen más en tu cuenta. Coste aproximado: un MicroVM más grande cuesta más por hora, de $0,0315/h (512 MiB) a $0,5044/h (8192 MiB) en baseline (limits.md, us-east-1, consultado 2026-09-30); cada tamaño publicado añade storage de snapshot, ≈ $0,04/semana por versión. IAM: ninguno adicional; la pila opcional sizes-guard niega RunMicrovm fuera de las imágenes que listes. Cómo apagarla: no pases size= (por defecto None); las versiones de cada imagen de tamaño se borran con rayito image prune --image-name rayito-base-4gb. Ejemplo: sbx = Sandbox.create(size="4gb") sbx.get_info() # size="4gb", baseline_memory_mib=4096

events= (m15-events-webhooks, ADR-020) ya es real: un LifecycleEvents/AsyncLifecycleEvents (con su pila events-webhooks ya desplegada) hace que el SDK derive la clave de este sandbox (k_sbx, de la clave del stack y el sandbox_id) y la mande a rayd en el mismo ConfigureSandbox que el resto de secciones, justo después de que el agente esté listo; desde ahí rayd emite created/paused/resumed/killed firmados. Exige un logging= que llegue a CloudWatch (InvalidArgumentException antes de lanzar si no). Si la pila no está desplegada, la clave no se puede leer o la imagen es anterior a 0.6.0, termina el sandbox (salvo keep_on_failure) y relanza el error.

Coste y activación


Activa: events=LifecycleEvents(...) en create(), tras LifecycleEvents().deploy(...) (o rayito events deploy). Recursos y llamadas AWS: un secretsmanager:GetSecretValue de la clave del stack por instancia de LifecycleEvents (más un cloudformation:DescribeStacks si no se desplegó con esa misma instancia); la pila hace el resto (forwarder, deliverer, reconciliador). Coste aproximado: el de la pila (~$0,40/mes el secreto, el resto por uso; ver LifecycleEvents.deploy); la opción en sí, una lectura de Secrets Manager ($0,05 por 10 000). IAM: EventsLauncherPolicy (salida de la pila) en las credenciales del llamante; el execution role necesita escribir en el log group de la imagen (logging="cloudwatch"). Cómo apagarla: no pases events= (por defecto None); LifecycleEvents().destroy() borra la pila. Ejemplo: from rayito import LifecycleEvents

 events = LifecycleEvents()
 sbx = Sandbox.create(execution_role_arn=role_arn,
                      logging="cloudwatch", events=events)
 events.get_events(sandbox_id=sbx.sandbox_id)  # created, ...

telemetry= (m15-rayd-otlp, ADR-021) es la séptima y ya es real: un TelemetryExport hace que rayd exporte 7 gauges de CPU, memoria y disco a CloudWatch cada interval_s (15-300 s, 60 por defecto), firmados con OtlpAuth.execution_role() (SigV4 sobre el execution role, exige rayito-base-caps) o con OtlpAuth.bearer(secret_name=...) (experimental: una API key de CloudWatch Metrics, funciona en rayito-base). Se envía como una sección de ConfigureSandbox justo después de que el agente esté listo; una imagen anterior a 0.6.0, o una 0.6.0 sin el exportador todavía implementado, termina el sandbox (salvo keep_on_failure) y lanza UnimplementedError.

Coste y activación


Activa: telemetry=TelemetryExport(...) en create(). Recursos y llamadas AWS: ninguno propio más allá de lo que tú adjuntes: con OtlpAuth.execution_role() necesitas la política RayitoOtlpExport (infra/otlp-export.yaml, rayito stack deploy otlp-export) en el execution role; con OtlpAuth.bearer(...) no hace falta ninguna política nueva en el execution role, pero sí el permiso para leer el secreto. rayd hace un PutMetricData por lote exportado (uno por interval_s, agrupando las 7 gauges). Coste aproximado: $0 por la opción en sí; CloudWatch factura la ingesta OTLP a $0,50/GB: ≈ $0,00002 por sandbox-hora con interval_s=60 (639 bytes por lote, AWS_API_NOTES.md Q120). IAM: cloudwatch:PutMetricData sobre el dataset OTLP por defecto de la cuenta (no se puede acotar por namespace, research OT9); con OtlpAuth.bearer(...), en su lugar el permiso de lectura del secreto que guarda el token. Cómo apagarla: no pases telemetry= (por defecto None); borra la pila otlp-export si ya no la usa ningún sandbox. Ejemplo: from rayito import OtlpAuth, TelemetryExport

 sbx = Sandbox.create(
     "rayito-base-caps",
     execution_role_arn=role_arn,
     telemetry=TelemetryExport(interval_s=60, service_name="agente",
                               auth=OtlpAuth.execution_role()),
 )
 sbx.get_telemetry_status()  # TelemetryHealth(exported, dropped, last_error_class)

connect(*, timeout: int | None = None, request_timeout: float | None = None, secrets: Mapping[str, str | SecretRef] | None = None, secret_cache: SecretCache | None = None, tracer_provider: TracerProviderLike | None = None) -> Sandbox

Reabre este handle: get-microvm, resume-microvm si está SUSPENDED sin auto-resume, el sondeo de Health y la extensión del plazo de Sandbox.connect(sandbox_id, timeout=). Devuelve self. secrets=/secret_cache= sustituyen los del handle; None los conserva y secrets={} los borra todos. tracer_provider= sustituye la Instrumentation del handle; None conserva la que ya tenía ($0 de AWS: ver el bloque de create()).

Coste y activación

Activa: secrets= (y secret_cache=) inyectan secretos de Secrets Manager en cada comando, PTY, celda Python y contexto de este handle; sin ellos, connect() no construye ningún cliente secretsmanager (el camino de 0.4.0). Recursos y llamadas AWS: GetSecretValue una vez por secreto y TTL de SecretCache (300 s por defecto), resuelto aquí antes de get-microvm; ningún recurso nuevo. Coste aproximado: $0,05 por 10 000 llamadas (≈ $0,04/mes por secreto y proceso con TTL 300) + $0,40 por secreto y mes (us-east-1, 2026-09-30). IAM: secretsmanager:GetSecretValue en las credenciales del llamante (RayitoSecretsReader de infra/secrets-access.yaml); kms:Decrypt con una CMK. Cómo apagarla: no pases secrets= ni secret_cache=; secrets={} quita los que el handle ya tenía. Ejemplo: sbx.connect(secrets={"OPENAI_API_KEY": "openai"}) sbx.commands.run("python agent.py") # 0 llamadas: ya en caché

list(*, template: str | None = None, template_version: str | None = None, states: Iterable[str] | None = None, metadata: Mapping[str, str] | None = None, started_after: datetime | None = None, order: ListOrder | None = None, region: str | None = None, session: boto3.session.Session | None = None, control_plane: ControlPlane | None = None, transport: TransportSettings | None = None, request_timeout: float = METADATA_PROBE_TIMEOUT_SECONDS, index: DynamoDbIndex | None = None) -> Iterator[SandboxListItem] classmethod

Pagina list-microvms (páginas de 50) de forma perezosa. Sin states omite TERMINATING|TERMINATED, que AWS sigue listando ~20 min después de morir. template viaja como filtro de servidor; states y started_after ("en o después") se filtran en cliente.

Con metadata filtra en cliente y es O(n): list-microvms no conoce los metadatos, así que por cada sandbox RUNNING hace, en orden y uno a uno, get-microvm + create-microvm-auth-token + un Health (5 s de plazo, canal dedicado) ≈ 0,5-1 s por sandbox, y cada sonda cuenta como tráfico para la política de idle de ese sandbox (pospone su auto-suspensión una ventana). Devuelve sólo los items cuyo agente respondió agent_ready y cuyos metadatos contienen cada par pedido (subconjunto exacto), con metadata relleno; un sandbox que dejó de estar RUNNING o que aún arranca se omite; un Health que falla es SandboxException con su id (nunca una lista incompleta en silencio). states sólo admite RUNNING: sondear un sandbox suspendido lo despertaría. Filtra por template antes si hay muchos.

order ("asc"/"desc" por startedAt) se calcula en cliente: AWS no ordena, así que recorre todas las páginas antes del primer item. Para reanudar un listado usa paginate().

Con metadata e index=DynamoDbIndex(...) no hay sondas: por cada página de list-microvms, un BatchGetItem de los candidatos y la unión con sus filas; ningún get-microvm, token ni Health, así que nada se despierta. states admite cualquier estado no terminal (por defecto todos). El estado es siempre el de list-microvms; sólo aparecen los sandboxes creados con ese índice (un sandbox sin fila se omite). index sin metadata no cambia nada.

Coste y activación

Activa: index=DynamoDbIndex(...) junto a metadata=. Recursos y llamadas AWS: dynamodb:BatchGetItem una vez por página de list-microvms (≤ 50 claves, eventualmente consistente). Coste aproximado: 0,5 RRU por candidato ($0,125 por millón de RRU, DynamoDB on-demand, us-east-1, consultado 2026-09-30). IAM: dynamodb:BatchGetItem sobre la tabla (RayitoIndexReader). Cómo apagarla: index=None (por defecto): la sonda O(n) de siempre. Ejemplo: idx = DynamoDbIndex("rayito-sandboxes") for item in Sandbox.list(metadata={"user": "42"}, states=["SUSPENDED"], index=idx): print(item.sandbox_id, item.state, item.metadata)

paginate(*, template: str | None = None, template_version: str | None = None, states: Iterable[str] | None = None, metadata: Mapping[str, str] | None = None, started_after: datetime | None = None, order: ListOrder | None = None, limit: int | None = None, next_token: str | None = None, region: str | None = None, session: boto3.session.Session | None = None, control_plane: ControlPlane | None = None, transport: TransportSettings | None = None, request_timeout: float = METADATA_PROBE_TIMEOUT_SECONDS, index: DynamoDbIndex | None = None) -> SandboxListPaginator classmethod

Los filtros de list() como un listado reanudable: cada next_items() devuelve hasta limit items y next_token (opaco, ligado a estos filtros) continúa en un paginate() nuevo. Valida todo sin llamar a AWS; el template se resuelve en el primer next_items().

Al reanudar vuelve a pedir la página del cursor y salta por identidad los items ya servidos de ella. Con order el primer next_items() recorre todas las páginas (O(páginas)) y la reanudación es por clave (startedAt, id). index= como en list() (dynamodb:BatchGetItem por página, sin sondas); el next_token queda ligado a la tabla.

kill() -> bool

terminate-microvm (idempotente). False sólo si ya no existe.

get_info() -> SandboxInfo

get-microvm fresco y, si el sandbox está RUNNING con plazo lógico gestionado, un Health (registrado) que lo refresca: expires_at es el plazo vigente aunque otro cliente lo haya movido. metadata y los hechos del guest vienen del último Health sin RPC extra (quedan fijos en /run), y nunca se sondea un sandbox que no está RUNNING (la sonda lo despertaría). Los hechos del guest sólo van en el valor devuelto: sbx.info los deja en None. size/baseline_memory_mib/baseline_cpu sólo aparecen cuando create(size=...) se usó (m15-sizes-catalog): esa única llamada a GetMicrovmImageVersion se cachea por versión de imagen (_size_catalog.DEFAULT_SIZE_CATALOG), así que repetir get_info() no repite la llamada a AWS. Esto vive en self._launch_options (relleno sólo por create()/take()): Sandbox.connect(id).get_info() deja size/baseline_memory_mib/ baseline_cpu en None aunque la imagen tenga el sufijo de un tamaño, porque el handle de connect() nunca pasó por create() en este proceso (no hay un requested_size que confirmar). No se deriva del sufijo del nombre de la imagen a propósito: un nombre que termine en -4gb por convención propia del operador, no por --sizes, confirmaría un tamaño que nadie pidió.

pause(*, wait: bool = True) -> bool

suspend-microvm (2 TPS). False si ya estaba SUSPENDING|SUSPENDED (get-microvm se lee antes: la API acepta un suspend repetido).

El agente cierra todos los streams abiertos con un final suspending y los handles vivos se reenganchan solos tras resume() (o tras el auto-resume) la próxima vez que se lean. Mientras este Sandbox tenga la pausa pendiente, ningún stream en curso (ni siquiera un run_code o un run en foreground) sondea Health: la pausa no se deshace sola.

resume(*, wait: bool = True) -> None

resume-microvm, reacuña el JWE y espera a Health con kernel_ready, registrando la nueva resume_generation.

Un ConflictException (el sandbox ya estaba RUNNING, por ejemplo porque el auto-resume se adelantó) no es un error. Los handles, watches y run_code en curso se reenganchan por su cuenta. Con wait, un sandbox reanudado después de su plazo lógico (resume_grace o expired) se reabre con su propio timeout, como connect().

set_timeout(timeout: int, *, request_timeout: float | None = None) -> None

Fija el plazo lógico en ahora + timeout segundos (SetTimeout EXACT): puede alargarlo o acortarlo, y reabre un sandbox en modo pause cuyo plazo venció pero que aún no se suspendió. El tope es max_lifetime (fijo desde create(), como mucho 28800 s): más allá, InvalidArgumentException con el plazo intacto y reincarnate() como salida. Sin max_lifetime/on_timeout en create() también es InvalidArgumentException.

is_running(*, request_timeout: float | None = None) -> bool

True si rayd responde a Health con agent_ready. request_timeout acota esa sonda (nunca por encima de 5 s).

get_health(*, request_timeout: float | None = None) -> SandboxHealth

HealthService.Health: readiness, resume_generation, clock_offset_ms y kernel_state_lost del agente.

get_telemetry_status(*, request_timeout: float | None = None) -> TelemetryHealth

ConfigureService.ConfigureStatus → TelemetryExportStatus (m15-rayd-otlp). Siempre TelemetryHealth() (todo ceros) si nunca pasaste telemetry= a create()/connect(), o si la imagen no soporta la función: nunca lanza por eso. Llamada explícita, no parte de get_health(), para que el camino sin telemetry= nunca pague esta llamada extra.

upload_url(path: str, user: str | None = None, use_signature_expiration: int | None = None) -> UploadTicket

La forma de E2B de files.upload_url: use_signature_expiration son los segundos de vida de la URL (3600 con None; <= 0 es InvalidArgumentException, una URL de S3 siempre caduca).

download_url(path: str, user: str | None = None, use_signature_expiration: int | None = None) -> DownloadLink

La forma de E2B de files.download_url, con la misma regla para use_signature_expiration que upload_url.

get_host(port: int) -> HostAccess

Hostname + cabeceras del proxy para port (token acuñado bajo demanda).

get_metrics(*, request_timeout: float | None = None) -> SandboxMetrics

HealthService.Metrics (procfs). Cuesta ≈ 100 ms: el agente toma dos muestras de /proc/stat para calcular cpu_used_pct.

get_metrics_history(*, start: datetime | None = None, end: datetime | None = None, max_points: int | None = None, request_timeout: float | None = None) -> builtins.list[SandboxMetrics]

HealthService.MetricsHistory: la serie que rayd muestrea cada 5 s desde /run (un anillo de 8 h), en orden ascendente.

start y end son inclusivos (un datetime naive es hora local); max_points reduce la serie a ese número de tramos: cada punto es la última muestra de su tramo con cpu_used_pct promediado. En cada muestra cpu_used_pct es la media desde la anterior (≈ 5 s), no la ventana de 100 ms de get_metrics(). Mientras el sandbox está suspendido no se muestrea: la serie tiene un hueco. Una imagen anterior a 0.3.0 es UnimplementedError.

close() -> None

Libera recursos locales (hilo del refresher, canales). No toca el MicroVM. Una reconexión en curso termina con SandboxException.

update_network(network: NetworkPolicy | NetworkOptions | None = None, *, allow_internet_access: bool | None = None, request_timeout: float | None = None) -> NetworkState

NetworkService.UpdateNetwork: sustituye la política entera de forma atómica y afecta a las conexiones nuevas (las abiertas siguen). None o {} es sin restricciones; allow_internet_access= False añade ALL_TRAFFIC a deny_out. Una imagen sin CAP_NET_ADMIN o con un rayd anterior a 0.3.0 es UnimplementedError; una entrada mal formada, InvalidArgumentException con la lista y el índice.

get_network(*, request_timeout: float | None = None) -> NetworkState

NetworkService.GetNetwork: la política vigente y cómo se aplica, sin la dirección ni las credenciales del proxy del operador.

run_code(code: str, *, language: str | None = None, context: ContextLike | None = None, on_stdout: StdoutCallback | None = None, on_stderr: StdoutCallback | None = None, on_result: ResultCallback | None = None, on_error: ErrorCallback | None = None, envs: Mapping[str, str] | None = None, timeout: float | None = DEFAULT_CODE_TIMEOUT_SECONDS, request_timeout: float | None = None, secrets: Mapping[str, str | SecretRef] | None = None) -> Execution

Ejecuta código en un kernel Jupyter con estado (CodeService.Execute).

language elige el kernel: python (por defecto), bash, javascript (alias js) o typescript (alias ts). bash y los dos últimos, que sirve el kernel Jupyter de Deno, viven en la variante de imagen rayito-base-poly y arrancan en la primera celda de ese lenguaje (≈ 1 s dentro del timeout); en rayito-base la llamada falla con UnimplementedError (el mensaje nombra rayito-base-poly). language y context son excluyentes; envs por ejecución sólo en contextos Python.

Devuelve una Execution con results (mime bundles: text, png, chart, data...), logs.stdout/logs.stderr, error y execution_count; execution.text es el valor de la última expresión. Un error del kernel es dato en error, nunca excepción. timeout (300 s por defecto) lo impone el agente sobre el reloj corrido (una pausa no lo consume) y termina en error.name == "ExecutionTimeout" con el contexto intacto salvo que el kernel no responda al interrupt. context es un CodeContext o su id; None es el contexto por defecto. Si el sandbox se pausa a mitad de la celda, el SDK espera al resume y continúa con Reattach sin volver a ejecutarla.

secrets= (y los del handle) viajan en ExecuteRequest.envs sólo durante esta celda y sólo en contextos Python: con otro language es InvalidArgumentException (usa create_code_context(secrets=)), y los del handle no se añaden a celdas de otros lenguajes. Con context= dado como id (str), los del handle sólo van si el contexto es default o uno que este handle creó o listó como Python; un id desconocido (creado desde otro handle) va sin ellos.

Coste y activación

Activa: secrets= inyecta secretos como variables de entorno de la celda. Recursos y llamadas AWS: secretsmanager:GetSecretValue sólo en un fallo de SecretCache (TTL 300 s); un acierto no llama a AWS. Coste aproximado: $0,05 por 10 000 llamadas (us-east-1, 2026-09-30). IAM: secretsmanager:GetSecretValue en las credenciales del llamante. Cómo apagarla: secrets=None (por defecto). Ejemplo: sbx.run_code("import os; os.getenv('OPENAI_API_KEY') is not None", secrets={"OPENAI_API_KEY": "openai"})

checkpoint_files(*, target: S3Prefix | None = None, exclude: Sequence[str] = (), timeout: float = DEFAULT_PERSIST_TIMEOUT_SECONDS, on_progress: CheckpointProgressCallback | None = None, user: str | None = None) -> CheckpointResult

FilesystemService.Checkpoint: rayd empaqueta el HOME del usuario (tar.gz, sin .cache, __pycache__, .ipynb_checkpoints ni el runtime de Jupyter) y lo sube como root con el execution role a s3://bucket/prefix/name/home.tar.gz + manifest.json. target por defecto es sbx.persist. exclude son directorios relativos al HOME (sin globs, máx. 64). Un solo checkpoint o restore a la vez por sandbox (PersistenceException(code="failed_precondition")). Los procesos que escriban durante el checkpoint pueden dejar copias truncadas: pausa tu trabajo antes.

restore_files(*, source: S3Prefix | None = None, timeout: float = DEFAULT_PERSIST_TIMEOUT_SECONDS, on_progress: RestoreProgressCallback | None = None, user: str | None = None) -> RestoreResult

FilesystemService.Restore: descarga el checkpoint de source (por defecto sbx.persist) y lo extrae sobre el HOME (los ficheros existentes se sobrescriben, los directorios se fusionan, nada sale del HOME). NotFoundException si no hay checkpoint. Un fallo a mitad deja el HOME parcialmente restaurado: kill() + create(persist=) es la recuperación.

reincarnate(*, exclude: Sequence[str] = (), persist_timeout: float = DEFAULT_PERSIST_TIMEOUT_SECONDS) -> Self

La respuesta a lo que set_timeout no puede dar, pasar de max_lifetime: checkpoint_files() → create(persist=) con las mismas opciones de lanzamiento (que restaura) → kill() de este sandbox, y devuelve el nuevo. El nuevo tiene 8 h frescas, otro sandbox_id, otro access token (salvo que el original fuera explícito) y los mismos metadata (con index= en el create(), el nuevo escribe su propia fila en el mismo índice) y las mismas opciones que acaban en ConfigureSandbox (mounts=, volumes=, events=, telemetry=, gateways=, domain=; size= ya va en el ARN de la imagen): el sucesor las vuelve a planear y aplicar en su único Configure por el mismo camino que create(), con sus propios hechos — events= deriva k_sbx del nuevo sandbox_id, mounts= espera otra vez a mounted, volumes= resuelve otra vez las IPs de los mount targets, telemetry= usa la imagen y la memoria del sucesor y cada cabecera de gateways= se resuelve otra vez, nunca reenviando un valor ya leído; las variables del kernel, los procesos y las PTY no sobreviven, sólo los ficheros del HOME. Si el create() falla, este sandbox sigue vivo y la excepción lleva una nota con la uri del checkpoint ya completo. Sólo sobre un sandbox de create(persist=): un handle de connect() no conoce el lanzamiento.

create_code_context(*, cwd: str | None = None, language: str | None = None, envs: Mapping[str, str] | None = None, request_timeout: float | None = None, secrets: Mapping[str, str | SecretRef] | None = None) -> CodeContext

Kernel nuevo con su propio scope, cwd y envs (máximo 8 por sandbox); language es python, bash, javascript (alias js) o typescript (alias ts). Los tres últimos sólo existen en rayito-base-poly (JS y TS con el kernel de Deno, arrancado al pedirlo, nunca antes de /ready). secrets= (y los del handle) quedan en el entorno del kernel de este contexto, de cualquier lenguaje, mientras viva.

Coste y activación

Activa: secrets= inyecta secretos en el entorno del kernel nuevo. Recursos y llamadas AWS: secretsmanager:GetSecretValue sólo en un fallo de SecretCache (TTL 300 s). Coste aproximado: $0,05 por 10 000 llamadas (us-east-1, 2026-09-30). IAM: secretsmanager:GetSecretValue en las credenciales del llamante. Cómo apagarla: secrets=None (por defecto). Ejemplo: ctx = sbx.create_code_context(language="bash", secrets={"TOKEN": "gh"}) sbx.run_code("echo ${#TOKEN}", context=ctx)

remove_code_context(context: ContextLike, *, request_timeout: float | None = None) -> None

Mata el kernel del contexto; el default no se puede borrar.

restart_code_context(context: ContextLike, *, request_timeout: float | None = None) -> None

Kernel nuevo con el mismo id; el estado del contexto se pierde.

AsyncSandbox(*, info: SandboxInfo, access_token: str, control_plane: ControlPlane, transport: TransportSettings, refresher: AsyncTokenRefresher, request_timeout: float, ready_timeout: float, reconnect_timeout: float = DEFAULT_RECONNECT_TIMEOUT_SECONDS, logger: logging.Logger | None = None)

Un MicroVM con rayd dentro. Se crea con await AsyncSandbox.create().

launch_info: SandboxInfo property

La SandboxInfo con la que se abrió el handle, nunca refrescada.

persist: S3Prefix | None property

El S3Prefix (con name) enlazado por create(persist=) o connect(persist=); None si el sandbox no persiste.

transfer: S3Staging | None property

Misma semántica que Sandbox.transfer.

last_restore: RestoreResult | None property

El resultado del restore automático de create(persist=); None si no hubo (primera vida de ese name) o si el sandbox no persiste.

gateways: GatewayHandle property

Misma semántica que Sandbox.gateways.

agent: AsyncAgent property

El agente de IA de este sandbox (ai-agent-core): run, stream, prepare. Tocar esta propiedad no manda ningún RPC. Se construye en el primer uso, así que import rayito no carga rayito._agent.

git: AsyncGit property

El módulo git de E2B 2.x (clone, status, commit, push...) sobre commands.run; se construye en el primer uso.

create(template: str | None = None, *, template_version: str | None = None, timeout: int = DEFAULT_TIMEOUT_SECONDS, max_lifetime: int | None = None, on_timeout: TimeoutActionName | None = None, idle: IdlePolicy | None = DEFAULT_IDLE_POLICY, envs: Mapping[str, str] | None = None, metadata: Mapping[str, str] | None = None, cpu_time_limit: int | None = None, execution_role_arn: str | None = None, allowed_ports: Sequence[PortLike] | None = None, ingress: Sequence[str] | None = None, egress: Sequence[str] | None = None, network: NetworkPolicy | NetworkOptions | None = None, allow_internet_access: bool = True, logging: LoggingOption = 'disabled', region: str | None = None, session: boto3.session.Session | None = None, access_token: str | None = None, ready_timeout: float = DEFAULT_READY_TIMEOUT_SECONDS, request_timeout: float = DEFAULT_REQUEST_TIMEOUT_SECONDS, reconnect_timeout: float = DEFAULT_RECONNECT_TIMEOUT_SECONDS, keep_on_failure: bool = False, control_plane: ControlPlane | None = None, transport: TransportSettings | None = None, persist: S3Prefix | None = None, persist_timeout: float = DEFAULT_PERSIST_TIMEOUT_SECONDS, pool: AsyncSandboxPool | None = None, transfer: S3Staging | None = None, logger: logging.Logger | None = None, secrets: Mapping[str, str | SecretRef] | None = None, secret_cache: SecretCache | None = None, index: DynamoDbIndex | None = None, tracer_provider: TracerProviderLike | None = None, mounts: Mapping[str, Any] | None = None, volumes: Mapping[str, Any] | None = None, size: str | SizeRequest | None = None, events: Any | None = None, telemetry: Any | None = None, gateways: Mapping[str, Any] | None = None, domain: Any | None = None) -> Self async classmethod

Misma semántica que Sandbox.create (incluidos metadata, pool=, persist=, transfer=, el plazo lógico de max_lifetime/on_timeout y la política de egress de network=/allow_internet_access, que termina el VM aunque haya keep_on_failure si la imagen no la aplica, y secrets=/secret_cache=, resueltos antes de run-microvm).

Coste y activación

Activa: secrets= inyecta secretos de Secrets Manager como variables de entorno de cada comando, PTY o celda; secret_cache= fija la caché (si no, una compartida por región y sesión, TTL 300 s). Recursos y llamadas AWS: secretsmanager:GetSecretValue una vez por secreto y TTL; ningún recurso nuevo y ningún cliente sin secrets=. Coste aproximado: $0,05 por 10 000 llamadas + $0,40 por secreto y mes (us-east-1, 2026-09-30). IAM: secretsmanager:GetSecretValue en las credenciales del llamante. Cómo apagarla: secrets=None y secret_cache=None (por defecto). Ejemplo: sbx = await AsyncSandbox.create(secrets={"OPENAI_API_KEY": "openai"}) await sbx.commands.run("python agent.py")

index=DynamoDbIndex(...) como en Sandbox.create (fila del índice tras run-microvm, IndexWriteException y VM terminado si falla con on_write_failure='terminate').

Coste y activación

Activa: index=DynamoDbIndex(...) escribe la fila del índice. Recursos y llamadas AWS: dynamodb:PutItem una vez por sandbox. Coste aproximado: ~1 WRU ≈ $0,000000625 (on-demand, us-east-1, consultado 2026-09-30). IAM: dynamodb:PutItem sobre la tabla (RayitoIndexWriter). Cómo apagarla: index=None (por defecto). Ejemplo: idx = DynamoDbIndex("rayito-sandboxes") sbx = await AsyncSandbox.create(metadata={"user": "42"}, index=idx)

tracer_provider= como en Sandbox.create: create() en sí es un único span rayito.sandbox.create; el handle instrumenta sus demás operaciones. None (por defecto) es NOOP.

Coste y activación

Activa: tracer_provider= (un TracerProvider de opentelemetry.trace, o cualquier objeto con get_tracer(name, version)). Recursos y llamadas AWS: ninguno. Coste aproximado: $0 de AWS. IAM: ninguno adicional. Cómo apagarla: no pases tracer_provider= (por defecto None). Ejemplo: from opentelemetry import trace sbx = await AsyncSandbox.create(tracer_provider=trace.get_tracer_provider())

telemetry= (m15-rayd-otlp, ADR-021) hace que rayd exporte 7 gauges de CPU, memoria y disco a CloudWatch cada interval_s (15-300 s), como en Sandbox.create.

Coste y activación

Activa: telemetry=TelemetryExport(...) en create(). Recursos y llamadas AWS: PutMetricData por lote exportado; con OtlpAuth.execution_role() necesitas la política RayitoOtlpExport (rayito stack deploy otlp-export) en el execution role. Coste aproximado: $0 por la opción; CloudWatch factura la ingesta OTLP a $0,50/GB: ≈ $0,00002 por sandbox-hora con interval_s=60 (AWS_API_NOTES.md Q120). IAM: cloudwatch:PutMetricData sobre el dataset OTLP por defecto de la cuenta (no se puede acotar por namespace, research OT9). Cómo apagarla: no pases telemetry= (por defecto None); borra la pila otlp-export si ya no la usa ningún sandbox. Ejemplo: from rayito import OtlpAuth, TelemetryExport sbx = await AsyncSandbox.create( "rayito-base-caps", execution_role_arn=role_arn, telemetry=TelemetryExport(auth=OtlpAuth.execution_role()), )

domain= sigue sin cablear (UnimplementedError antes de run-microvm; para un dominio propio usa AsyncCustomDomain, experimental); mounts=, volumes=, size=, events=, telemetry= y gateways= son reales, como en Sandbox.create.

volumes= (m15-efs-volumes, experimental) monta cada EfsVolume con amazon-efs-utils antes de que create() vuelva, igual que Sandbox.create (mismas exigencias: execution_role_arn=, un solo conector propio en egress=, imagen rayito-base-caps-efs); await sbx.volumes() da el estado en vivo.

Coste y activación

Activa: volumes={"/mnt/datos": vol} en create(), con execution_role_arn=, egress=[<ConnectorArn>] y la imagen opcional rayito-base-caps-efs. Recursos y llamadas AWS: ningún recurso nuevo; una DescribeMountTargets por sistema de ficheros y create() si un EfsVolume no trae mount_target_ip (credenciales del llamante); la pila efs-volumes va aparte. Coste aproximado: $0 propio; almacenamiento y rendimiento de EFS, y ≈ 198 MB más de código instalado en la imagen (us-east-1, consultado 2026-10-04, https://aws.amazon.com/efs/pricing/). IAM: el llamante, elasticfilesystem:DescribeMountTargets; el execution role, CallerPolicyArn de la pila efs-volumes. Cómo apagarla: no pases volumes= (por defecto None). Ejemplo: sbx = await AsyncSandbox.create( "rayito-base-caps-efs", execution_role_arn=role_arn, egress=[connector_arn], volumes={"/mnt/datos": vol}, )

mounts= (m15-s3-mounts) monta cada S3Mount con mount-s3, sólo en rayito-base-caps y sólo los buckets del allowlist de la imagen.

Coste y activación

Activa: mounts={"/mnt/data": S3Mount(bucket="...")} en create(), con execution_role_arn= y el bucket en RAYITO_ALLOWED_MOUNT_BUCKETS de la imagen. Recursos y llamadas AWS: ningún recurso nuevo; las peticiones S3 normales de mount-s3 con el execution role; la política sale de la pila s3-mounts (sólo IAM). Coste aproximado: $0 propio; las peticiones y el almacenamiento de S3 del bucket montado (us-east-1, consultado 2026-10-01). IAM: RayitoS3MountAccess (infra/s3-mounts.yaml) en el execution role. Cómo apagarla: no pases mounts= (por defecto None); rayito stack destroy s3-mounts quita la política. Ejemplo: sbx = await AsyncSandbox.create( "rayito-base-caps", execution_role_arn=role_arn, mounts={"/mnt/data": S3Mount(bucket="mi-bucket")}, )

size= (m15-sizes-catalog) elige en el cliente la imagen de ese tamaño (rayito-base-4gb), que tiene que estar publicada.

Coste y activación

Activa: size="4gb" (o SizeRequest(memory_mib=...)) en create(), tras rayito image publish --sizes 4gb. Recursos y llamadas AWS: ninguno nuevo al lanzar; cada tamaño publicado es una imagen más en tu cuenta. Coste aproximado: de $0,0315/h (512 MiB) a $0,5044/h (8192 MiB) en baseline (limits.md, us-east-1, consultado 2026-09-30), más ≈ $0,04/semana de snapshot por versión de imagen publicada. IAM: ninguno adicional (sizes-guard es opcional). Cómo apagarla: no pases size= (por defecto None). Ejemplo: sbx = await AsyncSandbox.create(size="4gb")

events= (m15-events-webhooks, ADR-020) manda a rayd la clave de este sandbox (k_sbx) en el mismo ConfigureSandbox, como en Sandbox.create; acepta AsyncLifecycleEvents o LifecycleEvents y exige un logging= que llegue a CloudWatch.

Coste y activación

Activa: events=AsyncLifecycleEvents(...) en create(), con la pila events-webhooks ya desplegada. Recursos y llamadas AWS: un secretsmanager:GetSecretValue de la clave del stack por instancia (más un DescribeStacks si no se desplegó con ella); la pila hace el resto. Coste aproximado: el de la pila (~$0,40/mes el secreto, el resto por uso); la opción en sí, una lectura de Secrets Manager. IAM: EventsLauncherPolicy en las credenciales del llamante. Cómo apagarla: no pases events= (por defecto None). Ejemplo: from rayito import AsyncLifecycleEvents events = AsyncLifecycleEvents() sbx = await AsyncSandbox.create( execution_role_arn=role_arn, logging="cloudwatch", events=events, )

connect(*, timeout: int | None = None, request_timeout: float | None = None, secrets: Mapping[str, str | SecretRef] | None = None, secret_cache: SecretCache | None = None, tracer_provider: TracerProviderLike | None = None) -> AsyncSandbox async

Misma semántica que sbx.connect() de Sandbox: reabre este handle y extiende el plazo (AT_LEAST). Devuelve self. secrets=/secret_cache= sustituyen los del handle; None los conserva y secrets={} los borra todos. tracer_provider= sustituye la Instrumentation del handle; None conserva la que ya tenía ($0 de AWS: ver el bloque de create()).

Coste y activación

Activa: secrets= (y secret_cache=) inyectan secretos de Secrets Manager en cada comando, PTY, celda Python y contexto de este handle; sin ellos, connect() no construye ningún cliente secretsmanager (el camino de 0.4.0). Recursos y llamadas AWS: GetSecretValue una vez por secreto y TTL de SecretCache (300 s por defecto), resuelto aquí antes de get-microvm; ningún recurso nuevo. Coste aproximado: $0,05 por 10 000 llamadas (≈ $0,04/mes por secreto y proceso con TTL 300) + $0,40 por secreto y mes (us-east-1, 2026-09-30). IAM: secretsmanager:GetSecretValue en las credenciales del llamante (RayitoSecretsReader de infra/secrets-access.yaml); kms:Decrypt con una CMK. Cómo apagarla: no pases secrets= ni secret_cache=; secrets={} quita los que el handle ya tenía. Ejemplo: await sbx.connect(secrets={"OPENAI_API_KEY": "openai"}) await sbx.commands.run("python agent.py") # 0 llamadas: ya en caché

list(*, template: str | None = None, template_version: str | None = None, states: Iterable[str] | None = None, metadata: Mapping[str, str] | None = None, started_after: datetime | None = None, order: ListOrder | None = None, region: str | None = None, session: boto3.session.Session | None = None, control_plane: ControlPlane | None = None, transport: TransportSettings | None = None, request_timeout: float = METADATA_PROBE_TIMEOUT_SECONDS, index: DynamoDbIndex | None = None) -> builtins.list[SandboxListItem] async classmethod

Misma semántica y mismo coste O(n) con metadata que Sandbox.list; las sondas de Health van por grpc.aio, una tras otra. Con index=DynamoDbIndex(...) y metadata, sin sondas: un dynamodb:BatchGetItem por página (ver Sandbox.list).

paginate(*, template: str | None = None, template_version: str | None = None, states: Iterable[str] | None = None, metadata: Mapping[str, str] | None = None, started_after: datetime | None = None, order: ListOrder | None = None, limit: int | None = None, next_token: str | None = None, region: str | None = None, session: boto3.session.Session | None = None, control_plane: ControlPlane | None = None, transport: TransportSettings | None = None, request_timeout: float = METADATA_PROBE_TIMEOUT_SECONDS, index: DynamoDbIndex | None = None) -> AsyncSandboxListPaginator classmethod

Sandbox.paginate con next_items() asíncrono. No es async: construir el paginador valida sin hacer E/S.

get_info() -> SandboxInfo async

Misma semántica que Sandbox.get_info: un Health sólo si el sandbox está RUNNING con plazo lógico gestionado, para refrescarlo. size/baseline_memory_mib/baseline_cpu sólo aparecen cuando create(size=...) se usó (m15-sizes-catalog); ver la versión sync para el porqué de la única llamada cacheada a GetMicrovmImageVersion, y para por qué AsyncSandbox.connect(id) los deja en None aunque la imagen lleve sufijo de tamaño.

pause(*, wait: bool = True) -> bool async

Misma semántica que Sandbox.pause: mientras la pausa esté pendiente ningún stream en curso sondea Health.

resume(*, wait: bool = True) -> None async

Misma semántica que Sandbox.resume (incluida la reapertura de un sandbox reanudado después de su plazo lógico).

set_timeout(timeout: int, *, request_timeout: float | None = None) -> None async

Misma semántica que Sandbox.set_timeout (SetTimeout EXACT).

is_running(*, request_timeout: float | None = None) -> bool async

Como Sandbox.is_running.

get_telemetry_status(*, request_timeout: float | None = None) -> TelemetryHealth async

Como Sandbox.get_telemetry_status (m15-rayd-otlp).

mounts() -> dict[str, MountStatus] async

Estado en vivo de cada mounts= (m15-s3-mounts): una ConfigureStatus por lectura, nunca cacheada. Vacío si create()/take() no recibió mounts=.

volumes() -> dict[str, VolumeStatus] async

Estado en vivo de cada volumes= (m15-efs-volumes): una ConfigureStatus por lectura, nunca cacheada — tras un resume() un volumen puede pasar por "remounting"/"degraded". Vacío si create() no recibió volumes=.

upload_url(path: str, user: str | None = None, use_signature_expiration: int | None = None) -> AsyncUploadTicket async

Misma semántica que Sandbox.upload_url.

download_url(path: str, user: str | None = None, use_signature_expiration: int | None = None) -> DownloadLink async

Misma semántica que Sandbox.download_url.

get_metrics_history(*, start: datetime | None = None, end: datetime | None = None, max_points: int | None = None, request_timeout: float | None = None) -> builtins.list[SandboxMetrics] async

Misma semántica que Sandbox.get_metrics_history.

update_network(network: NetworkPolicy | NetworkOptions | None = None, *, allow_internet_access: bool | None = None, request_timeout: float | None = None) -> NetworkState async

Misma semántica que Sandbox.update_network: sustituye la política entera (None o {} es sin restricciones) y afecta a las conexiones nuevas.

get_network(*, request_timeout: float | None = None) -> NetworkState async

Misma semántica que Sandbox.get_network.

run_code(code: str, *, language: str | None = None, context: ContextLike | None = None, on_stdout: StdoutCallback | None = None, on_stderr: StdoutCallback | None = None, on_result: ResultCallback | None = None, on_error: ErrorCallback | None = None, envs: Mapping[str, str] | None = None, timeout: float | None = DEFAULT_CODE_TIMEOUT_SECONDS, request_timeout: float | None = None, secrets: Mapping[str, str | SecretRef] | None = None) -> Execution async

Misma semántica que Sandbox.run_code; los callbacks son síncronos.

Coste y activación

Activa: secrets= inyecta secretos en el entorno de la celda (sólo contextos Python). Recursos y llamadas AWS: secretsmanager:GetSecretValue sólo en un fallo de SecretCache (TTL 300 s). Coste aproximado: $0,05 por 10 000 llamadas (us-east-1, 2026-09-30). IAM: secretsmanager:GetSecretValue en las credenciales del llamante. Cómo apagarla: secrets=None (por defecto). Ejemplo: await sbx.run_code("import os", secrets={"OPENAI_API_KEY": "openai"})

checkpoint_files(*, target: S3Prefix | None = None, exclude: Sequence[str] = (), timeout: float = DEFAULT_PERSIST_TIMEOUT_SECONDS, on_progress: CheckpointProgressCallback | None = None, user: str | None = None) -> CheckpointResult async

Misma semántica que Sandbox.checkpoint_files.

restore_files(*, source: S3Prefix | None = None, timeout: float = DEFAULT_PERSIST_TIMEOUT_SECONDS, on_progress: RestoreProgressCallback | None = None, user: str | None = None) -> RestoreResult async

Misma semántica que Sandbox.restore_files.

reincarnate(*, exclude: Sequence[str] = (), persist_timeout: float = DEFAULT_PERSIST_TIMEOUT_SECONDS) -> Self async

Misma semántica que Sandbox.reincarnate: checkpoint → create(persist=) con las mismas opciones (restaura, y reaplica mounts=/volumes=/ events=/telemetry=/gateways=/domain= en el Configure del sucesor; size= ya va en el ARN) → kill() de este sandbox.

create_code_context(*, cwd: str | None = None, language: str | None = None, envs: Mapping[str, str] | None = None, request_timeout: float | None = None, secrets: Mapping[str, str | SecretRef] | None = None) -> CodeContext async

Misma semántica que Sandbox.create_code_context, lenguajes, variante de imagen y secrets= incluidos.

Coste y activación

Activa: secrets= inyecta secretos en el entorno del kernel nuevo. Recursos y llamadas AWS: secretsmanager:GetSecretValue sólo en un fallo de SecretCache (TTL 300 s). Coste aproximado: $0,05 por 10 000 llamadas (us-east-1, 2026-09-30). IAM: secretsmanager:GetSecretValue en las credenciales del llamante. Cómo apagarla: secrets=None (por defecto). Ejemplo: ctx = await sbx.create_code_context(language="bash", secrets={"T": "gh"})