Compatibilidad con E2B¶
rayito.e2b (Python) y rayito/e2b (TypeScript)
son un drop-in a nivel de import para los SDKs de E2B 2.x (contrato:
e2b 2.51.0 y e2b-code-interpreter 2.10.0). Cambia la línea de import y
el resto del programa sigue igual. Qué hay y qué no, fila a fila, está en
Paridad con E2B; esta página explica cómo migrar y en qué
se comporta distinto.
Marcas
E2B es una marca de su titular. Rayito es un proyecto independiente, no
afiliado, patrocinado ni respaldado por E2B; el nombre se usa sólo para
describir con qué SDK es compatible la API de rayito.e2b / rayito/e2b.
Migrar: cambia un import¶
# antes
# from e2b_code_interpreter import Sandbox
from rayito.e2b import Sandbox
with Sandbox.create(timeout=300, metadata={"run": "42"}) as sbx:
print(sbx.run_code("1 + 1").text) # "2"
print(sbx.commands.run("echo hola").stdout) # "hola\n"
sbx.files.write("/home/user/a.txt", "a")
sbx.set_timeout(600) # rayd mueve el plazo
// antes: import { Sandbox } from "@e2b/code-interpreter";
import { Sandbox } from "rayito/e2b";
await using sbx = await Sandbox.create({ timeoutMs: 300_000, metadata: { run: "42" } });
console.log((await sbx.runCode("1 + 1")).text); // "2"
console.log((await sbx.commands.run("echo hola")).stdout);
await sbx.setTimeout(600_000);
Lo que cambia fuera del código:
- Credenciales de AWS en vez de
E2B_API_KEY. El SDK usa la cadena por defecto de boto3 / AWS SDK v3 (AWS_PROFILE=<tu-perfil>,AWS_REGION);api_key,domainy compañía se aceptan y se ignoran con unRayitoCompatWarning. - Una imagen 0.3.0 o posterior en tu cuenta en lugar del template de E2B:
RAYITO_TEMPLATE=rayito-base(otemplate=). Para JS/TS hace faltarayito-base-polyy paraallow_internet_access=False/network=,rayito-base-caps(Imágenes e IAM). upload_url/download_urlnecesitan un bucket tuyo:RAYITO_TRANSFER_BUCKET=amzn-s3-demo-bucket(Ficheros).
Los shims exigen una imagen 0.3.0 o posterior
El shim siempre pide a rayd un plazo lógico. Contra una imagen anterior
a 0.3.0, create() termina el VM y lanza UnimplementedError("lifecycle")
pidiendo una imagen nueva. Publica rayito-base desde este árbol
(rayito image publish) antes de migrar.
Tabla de imports¶
| Antes | Después |
|---|---|
from e2b_code_interpreter import X |
from rayito.e2b import X |
from e2b import X |
from rayito.e2b import X |
from e2b.exceptions import X |
from rayito.e2b.exceptions import X (las excepciones también se exportan desde rayito.e2b) |
Nombres que exporta rayito.e2b (su __all__):
| Grupo | Nombres |
|---|---|
| Sandbox y cliente | Sandbox, AsyncSandbox, E2B, ConnectionConfig, SandboxInfo, SandboxMetrics, SandboxQuery, SandboxState, SandboxPaginator, AsyncSandboxPaginator, ListedSandbox, ALL_TRAFFIC |
| Comandos y PTY | CommandHandle, AsyncCommandHandle, CommandResult, ProcessInfo, PtySize, PtyOutput, Stdout, Stderr, OutputHandler, Username |
| Ficheros | EntryInfo, WriteInfo, WriteEntry, FileType, FilesystemEvent, FilesystemEventType, WatchHandle, AsyncWatchHandle, UploadTicket, DownloadLink |
| Git | Git, GitStatus, GitBranches, GitFileStatus, GitResetMode (Git) |
| Code interpreter | Execution, Result, Logs, OutputMessage, ExecutionError, Context, MIMEType, RunCodeLanguage y los charts ChartType, ScaleType, Chart, Chart2D, PointData, LineChart, ScatterChart, BarChart, BarData, PieChart, PieData, BoxAndWhiskerChart, BoxAndWhiskerData, SuperChart |
| Secretos (CRUD sobre AWS Secrets Manager, ver Secretos) | Secret, AsyncSecret, SecretInfo, SecretPaginator, AsyncSecretPaginator |
| Excepciones | SandboxException, TimeoutException, NotFoundException, FileNotFoundException, SandboxNotFoundException, InvalidArgumentException, AuthenticationException, RateLimitException, CommandExitException, NotEnoughSpaceException, ServiceBusyException, FileUploadException, GitAuthException, GitUpstreamException, TemplateException, BuildException (las nativas que lanza Template.build(); a diferencia de E2B, BuildException es un SandboxException), SecretException, SecretNotFoundException, VolumeException, VolumeNotFoundException, VolumePathNotFoundException |
| Templates declarativos (desde 0.6.0, ver Templates) | Template, AsyncTemplate construyen de verdad; alias_exists/assign_tags/remove_tags/get_tags siguen sin equivalente |
| Volúmenes EFS (desde 0.7.0, experimental, ver Volúmenes EFS) | Volume, AsyncVolume hacen CRUD real con E2B(volume_store=...); sin él, y en las operaciones de contenido, lanzan |
| Sin primitiva (importan, pero toda llamada lanza) | get_signature |
| Sólo Rayito | UnimplementedError, RayitoCompatWarning |
| Antes | Después |
|---|---|
import { Sandbox } from "@e2b/code-interpreter" |
import { Sandbox } from "rayito/e2b" |
import Sandbox from "e2b" |
import Sandbox from "rayito/e2b" (export por defecto) |
import { Sandbox, X } from "e2b" |
import { Sandbox, X } from "rayito/e2b" |
Nombres que exporta rayito/e2b:
| Grupo | Nombres |
|---|---|
| Sandbox y cliente | Sandbox (también por defecto), SandboxPaginator, E2B, ConnectionConfig, ALL_TRAFFIC; tipos SandboxOpts, SandboxConnectOpts, SandboxInstanceConnectOpts, SandboxInfo, SandboxInfoLifecycle, SandboxMetrics, SandboxMetricsOpts, SandboxLifecycle, SandboxOnTimeout, SandboxOnResume, SandboxNetworkOpts, SandboxNetworkInfo, SandboxNetworkSelector, SandboxNetworkUpdate, SandboxListOpts, SandboxPauseOpts, SandboxUrlOpts, SandboxState, ConnectionOpts, E2BClientOpts, Username, Logger |
| Comandos, ficheros y PTY | Filesystem, Pty, FileType, FilesystemEventType, DEFAULT_WATCH_TIMEOUT_MS; tipos CommandHandle, CommandResult, EntryInfo, WriteInfo, FilesystemEvent, WatchOpts, WatchEventCallback, PtySize, PtyCreateOpts, PtyConnectOpts, PtyOutputCallback |
| Git | Git; tipos GitStatus, GitBranches, GitFileStatus, GitResetMode (Git) |
| Code interpreter | Execution, Result; tipos Context, Logs, OutputMessage, ExecutionError |
| Secretos (CRUD sobre AWS Secrets Manager, ver Secretos) | Secret, SecretPaginator; tipos SecretInfo, SecretCreateOpts, SecretUpdateOpts, SecretGetInfoOpts, SecretExistsOpts, SecretDestroyOpts, SecretListOpts, SecretConnectionOpts |
Errores (las clases nativas de rayito: instanceof vale entre los dos) |
SandboxError, TimeoutError, NotFoundError, FileNotFoundError, SandboxNotFoundError, InvalidArgumentError, AuthenticationError, RateLimitError, CommandExitError, NotEnoughSpaceError (= DiskFullError), ServiceBusyError (= CapacityError), FileUploadError, GitAuthError, GitUpstreamError, TemplateError, BuildError, UnimplementedError, SecretError, SecretNotFoundError |
| Templates declarativos (desde 0.6.0, ver Templates) | Template construye de verdad; aliasExists/assignTags/removeTags/getTags siguen sin equivalente |
| Volúmenes EFS (desde 0.7.0, experimental, ver Volúmenes EFS) | Volume (y bindVolume, que lo liga a un VolumeStore) hace CRUD real con new E2B({ volumeStore }); sin él, y en las operaciones de contenido, lanza. Los errores de volumen (VolumeError, VolumeNotFoundError, VolumePathNotFoundError) no se reexportan aquí: impórtalos de rayito |
| Sin primitiva (importan, pero toda llamada lanza) | getSignature |
rayito/e2b es una entrada del mismo paquete npm rayito (ESM y
CommonJS): no hay que instalar nada más.
Valores por defecto¶
Los shims siguen los valores por defecto de E2B, no los del SDK nativo:
timeout=300 es el plazo lógico del sandbox con on_timeout='kill' (lo
impone rayd, más abajo), sin
auto-suspensión por inactividad, endpoint público (ALL_INGRESS) y salida a
internet (INTERNET_EGRESS). El tope de la plataforma (max_lifetime,
kwarg sólo de Rayito; TS maxLifetimeMs) es max(3600, min(timeout + 60,
28800)) si no lo pasas. Si quieres los defaults de Rayito (auto-suspensión a
los 300 s de inactividad, conectores explícitos), usa rayito.Sandbox; el
nativo siempre está en sbx.native. Lo que Lambda MicroVMs no puede hacer
lanza UnimplementedError (un NotImplementedError, nunca SandboxException;
es la misma clase que rayito.UnimplementedError) con la feature y el motivo:
nada se aproxima en silencio.
Funciona sin cambios¶
| E2B | Rayito |
|---|---|
with Sandbox.create() as sbx: / async with await AsyncSandbox.create() / await Sandbox.create() |
igual (RAYITO_TEMPLATE en lugar del template por defecto de E2B) |
sbx.run_code(code) → Execution con text, results, logs.stdout, error; charts (Result.chart, png) |
igual |
run_code(language="python" / "bash"), create_code_context, list_code_contexts, remove_code_context, restart_code_context |
igual; bash sólo en rayito-base-poly |
sbx.commands.run(cmd, background, envs, user, cwd, on_stdout, on_stderr, stdin, timeout, request_timeout) (también posicional), commands.list/kill/connect/send_stdin/close_stdin, CommandHandle.wait(on_pty, on_stdout, on_stderr) |
el Commands nativo |
sbx.files.read/write/write_files/list/exists/get_info/remove/rename/make_dir, files.watch_dir(path, on_event=...) |
igual |
sbx.pty.create(PtySize(rows, cols), on_data=...), send_stdin, resize, kill, connect(pid) |
igual (PtySize en el orden de E2B) |
sbx.get_host(port) |
un str con el hostname más .headers con las cabeceras del proxy (TS: getHost(port) síncrono devuelve el hostname) |
sbx.kill(), Sandbox.kill(id), sbx.is_running(request_timeout=), sbx.sandbox_id, sbx.sandbox_domain |
igual |
sbx.pause() / beta_pause() → bool, Sandbox.connect(id) sobre un sandbox pausado |
suspend-microvm y resume-microvm |
Sandbox.list(query=SandboxQuery(metadata=...)) sobre sandboxes RUNNING |
filtro en cliente, O(n) (coste) |
Las excepciones de e2b.exceptions (TimeoutException, NotFoundException, FileNotFoundException, SandboxNotFoundException, CommandExitException, GitAuthException, ...) |
las mismas clases nativas bajo esos nombres; NotEnoughSpaceException es DiskFullException y se lanza con el disco lleno; ServiceBusyException se lanza ante InsufficientCapacityException; desde 0.6.0, Template.build() lanza TemplateException y BuildException de verdad: las de rayito.e2b (Python) y rayito/e2b (TS) son las mismas clases nativas, así que except/instanceof vale con cualquiera de los dos imports (Templates) |
Se mapea, con una nota¶
| E2B | Rayito |
|---|---|
timeout (300 s por defecto) |
plazo lógico impuesto por rayd bajo max_lifetime (3600 por defecto, ≤ 28800; kwarg sólo de Rayito): max(3600, min(timeout + 60, 28800)) si no lo pasas. Al vencer, rayd sale y la VM pasa a TERMINATED ≈ 15 s después (Q58) |
sbx.set_timeout(t) / Sandbox.set_timeout(id, t) |
SetTimeout EXACT: alarga o acorta el plazo, hasta max_lifetime; más allá, InvalidArgumentException con el plazo intacto. La forma de clase necesita el access token (access_token= o RAYITO_ACCESS_TOKEN) |
Sandbox.connect(id, timeout) / sbx.connect(timeout) |
reanuda si hace falta y sólo alarga el plazo (AT_LEAST, como E2B); la forma de clase necesita el access token, que E2B no tiene |
lifecycle={"on_timeout": ..., "auto_resume": ...} |
on_timeout='kill' exacto; 'pause' suspende al vencer con un cliente vivo y, sin cliente, lo hace la política de idle en max_idle (300 s); auto_resume=True reanuda con un plazo nuevo de max(timeout, 300 s), False lo hace cumplir rayd |
Sandbox.beta_create(auto_pause=True) |
lifecycle={"on_timeout": "pause"} (E2B lo retiró en 2.23.0; se conserva) |
TimeoutException |
también cuando una llamada choca con el plazo (sandbox_timeout) |
sbx.upload_url(path, user, use_signature_expiration) / uploadUrl |
URL de S3 prefirmada con tus credenciales (Ficheros): PUT con el cuerpo en crudo (form=True da los campos de un POST); el fichero aterriza de forma asíncrona (barrera en rayd y ticket.wait()); de un solo uso; caducidad siempre fijada (≤ 7 días y ≤ la vida de tus credenciales); use_signature_expiration <= 0 lanza; exige un bucket de transferencias: el shim no acepta transfer=, así que se configura con RAYITO_TRANSFER_BUCKET (y RAYITO_TRANSFER_PREFIX, RAYITO_TRANSFER_REGION); sin él UnimplementedError; upload_url(path=None) lanza. Python devuelve el UploadTicket nativo (un str con .headers y wait()); TS devuelve la URL como string (sbx.native.files.uploadUrl() da el ticket con wait()) |
sbx.download_url(...) / downloadUrl |
URL GET prefirmada (admite Range) sobre una foto tomada al llamar; un fichero que no existe lanza al llamar; en el shim async ambos métodos son corrutinas |
files.read(gzip=), files.write(gzip=), metadata=, stream_idle_timeout=, use_octet_stream=; JS format: "blob" |
gzip de gRPC (las respuestas lo piden con la cabecera rayito-compress); metadata como xattrs user.rayito.* (claves en minúsculas, sobrescribir reemplaza el conjunto); use_octet_stream se acepta sin efecto (gRPC no tiene formulario multipart) |
sbx.get_metrics(start, end) → [SandboxMetrics] en bytes, con mem_cache |
el historial de rayd (una muestra cada 5 s, anillo de 8 h, hueco mientras está suspendido; Observabilidad); sin start/end, un historial vacío o una imagen anterior a 0.3.0 dan la instantánea de get_metrics() como único punto; con rango en una imagen anterior a 0.3.0, UnimplementedError |
Sandbox.get_metrics(id, start, end) |
necesita el access token del sandbox; sin él, UnimplementedError sin llamar a nada |
Sandbox.list(limit=, next_token=, query=SandboxQuery(state=, started_after=, template=), order=) → SandboxPaginator |
next_token es un cursor opaco sobre el nextToken de list-microvms; order se calcula en cliente (recorre todas las páginas antes del primer item); un token reanudado salta por identidad; state=[PAUSED] = SUSPENDING/SUSPENDED de AWS |
allow_internet_access=False / allowInternetAccess: false |
política deny-all dentro del guest en rayito-base-caps (Red saliente): ninguna conexión sale del VM y, desde 0.3.2, tampoco resuelve el DNS para los procesos del sandbox; en cualquier otra imagen el shim termina el VM y lanza UnimplementedError (sin conector de egress el MicroVM sigue saliendo: AWS_API_NOTES.md Q44, Q60) |
network={"allow_out", "deny_out", "egress_proxy"} (CIDR, IP, ALL_TRAFFIC, *.dominio, selectores invocables) |
la semántica de E2B en rayito-base-caps: rutas por uid y, para nombres de host o egress_proxy, un proxy local de rayd; los clientes que no honran HTTPS_PROXY fallan cerrados |
sbx.update_network(network) / Sandbox.update_network(id, network) |
NetworkService.UpdateNetwork: cambia la política del guest de forma atómica para las conexiones nuevas; devuelve None |
network.https_ports |
se valida (1–65535); una lista no vacía es UnimplementedError: medido, el proxy no reenvía TLS extremo a extremo a un puerto del guest (fila Q67 de AWS_API_NOTES.md); get_host(puerto) sirve HTTP en claro |
network.allow_public_traffic=False |
se acepta sin efecto: es el comportamiento permanente |
run_code(language="javascript" / "js" / "typescript" / "ts") |
el kernel de Deno de rayito-base-poly (Kernels); en otra imagen UnimplementedError nombrando rayito-base-poly |
sbx.git (clone, status, commit, push, pull, dangerously_authenticate, ...) |
la API git de E2B sobre commands.run con git-core de la imagen (Git); las credenciales de dangerously_authenticate quedan en ~/.git-credentials, visibles para el código del sandbox |
sbx.get_info() / Sandbox.get_info(id) → SandboxInfo 2.x |
sandbox_domain = endpoint; cpu_count/memory_mb como los ve el guest; envd_version = agent_version; end_at = el plazo lógico; lifecycle, network y allow_internet_access rellenos; volume_mounts siempre vacío |
sbx.traffic_access_token (TS trafficAccessToken), envd_api_url, envd_direct_url (sólo Python) |
el JWE vigente de x-aws-proxy-auth (rota, TTL ≤ 60 min: es una credencial al portador) y https://<endpoint>; toda petición necesita las cabeceras del proxy |
metadata, envs |
viajan en runHookPayload (4096 caracteres entre los dos); no son secretos |
api_key, domain, debug, api_url, sandbox_url, validate_api_key, api_headers, secure=False |
ignorados con un RayitoCompatWarning cada uno: las credenciales son las de AWS |
proxy= |
se honra (sólo http://host:puerto) en los canales gRPC y en botocore |
headers=, retries=, ConnectionConfig.set_integration, sbx.connection_config |
cabeceras extra en cada RPC (las del proxy, del token y de gRPC están reservadas), reintentos de botocore y user_agent_extra |
headers=, proxy=, retries= en sbx.kill(), sbx.pause() o sbx.connect() (llamadas de instancia) |
0.4.0: RayitoCompatWarning por cada uno (antes se perdían en silencio); sbx.<call>() opera sobre el canal y el plano ya construidos de este sandbox y no puede reconstruirlos, a diferencia de Sandbox.<call>(sandbox_id, ...), que sí los aplica; sbx.pause()/sbx.kill() avisan también de request_timeout= (el nativo no lo acepta ahí), sbx.connect(request_timeout=) sí lo aplica; headers={} no avisa, y los avisos salen en orden alfabético (igual en TS) |
TS: cualquier clave de ConnectionOpts en una llamada de instancia que ese método no aplica |
0.4.0: RayitoCompatWarning por cada una, mismo motivo que Python. Cubre kill, pause, getInfo, isRunning, setTimeout, connect, getMetrics y updateNetwork; sólo signal se aplica siempre. kill/pause también avisan de requestTimeoutMs (el nativo no lo acepta ahí); getInfo/isRunning/setTimeout/connect/getMetrics/updateNetwork sí lo aplican. El resto de claves propias de la conexión (headers, proxy, retries, logger, region, controlPlane, accessToken, transport) avisan siempre que aparecen en la llamada de instancia (unos headers vacíos, {}, no avisan); las variantes estáticas (Sandbox.<método>(sandboxId, opts)) las aplican todas, como antes |
logger= |
los logs del SDK de ese sandbox van al logging.Logger dado (TS: un Logger con debug/info/warn/error), distinto del logging= nativo, que es CloudWatch |
JS sbx.getHost(port) |
síncrono, devuelve el hostname como E2B; las cabeceras del proxy que toda petición necesita salen de await sbx.getHostHeaders(port) |
JS signal (AbortSignal) en ConnectionOpts |
cancela las llamadas del plano de control y los RPC en curso; rechaza con signal.reason |
E2B(...) (cliente ligado) |
liga region, session y control_plane; .Secret/.AsyncSecret (TS: .Secret) usan esa region y esa session contra Secrets Manager; desde 0.6.0 .Template/.AsyncTemplate son la clase real; desde 0.7.0 .Volume/.AsyncVolume hacen CRUD real con volume_store= (sin él, lanzan) |
Kwargs nativos (region, session, execution_role_arn, allowed_ports, ingress, logging, control_plane, transport, ...) |
se pasan tal cual; idle, egress y pool no se aceptan (TypeError) |
Lanza UnimplementedError¶
El mensaje es siempre <feature> no está disponible en Rayito: <motivo>. Ver
docs/site/docs/e2b-compat.md, y el error lleva feature, reason y doc.
Python lanza al llamar; en TypeScript un método asíncrono devuelve la promesa
rechazada. Las claves y los motivos de esta tabla son los de
clients/python/src/rayito/e2b/_unimplemented.py y
clients/typescript/src/e2b/unimplemented.ts, idénticos en los dos SDKs:
| Feature (Python) | Feature (TypeScript) | Motivo, literal |
|---|---|---|
fork, create_snapshot, list_snapshots, delete_snapshot |
fork, createSnapshot, listSnapshots, deleteSnapshot |
ninguna operación de Lambda MicroVMs copia la memoria de un MicroVM en marcha (AWS_API_NOTES.md §1 y §15); el análogo de sólo ficheros es checkpoint_files() + create(persist=) |
connect(on_resume='reboot') |
connect({ onResume: 'reboot' }) |
resume-microvm siempre restaura memoria y disco (AWS_API_NOTES.md §5); el análogo es reincarnate(), con un id nuevo |
pause(keep_memory=False), lifecycle.on_timeout.keep_memory=False |
pause({ keepMemory: false }), lifecycle.onTimeout.keepMemory=false |
suspend-microvm siempre guarda memoria y disco (AWS_API_NOTES.md §5); el análogo es checkpoint_files() + kill() |
network.rules |
network.rules |
no hay un proxy de egress fuera del VM donde inyectar cabeceras: el proxy de Lambda MicroVMs sólo gestiona el ingress (AWS_API_NOTES.md §7) |
network.mask_request_host |
network.maskRequestHost |
el proxy de Lambda MicroVMs siempre reenvía Host: <endpoint> y no lo reescribe (AWS_API_NOTES.md §7) |
network.allow_public_traffic=True |
network.allowPublicTraffic=true |
no existe acceso sin autenticar: toda petición al endpoint exige X-aws-proxy-auth (AWS_API_NOTES.md §3 y §7); allow_public_traffic=False es el comportamiento permanente |
iam |
iam |
los MicroVMs no emiten tokens con audiencia: la única identidad es el execution role por IMDSv2 (AWS_API_NOTES.md §9) |
mcp, get_mcp_url, get_mcp_token |
mcp, getMcpUrl, getMcpToken |
cada petición al endpoint necesita además un JWE en cabecera con TTL de 60 min como máximo (AWS_API_NOTES.md §3 y §7), así que una URL con token fijo no sirve; usa el servidor rayito-mcp |
volume_mounts, Volume (y AsyncVolume) sin volume_store=/volumeStore |
volumeMounts, Volume |
sin un volume_store/volumeStore configurado en el cliente E2B no hay volumen: pasa E2B(volume_store=VolumeStore(...)) (y volume_connector_arn= para volume_mounts, sobre la imagen opcional con amazon-efs-utils, AWS_API_NOTES.md §22; m15-efs-volumes, experimental), o usa persist= (S3) o upload_url/download_url |
volume.content: read_file/write_file/make_dir/list_files/remove/update_metadata de un Volume |
volume.content: readFile/writeFile/makeDir/list/remove/updateMetadata |
no hay plano de datos de ficheros fuera de un MicroVM (SPEC.md §4); conecta un sandbox y monta el volumen, o usa upload_url/download_url sobre persist= |
get_signature |
getSignature |
una firma de envd no autentica en el proxy: el JWE sólo viaja en cabecera o en el subprotocolo WebSocket (AWS_API_NOTES.md §7); usa upload_url/download_url, que firman en S3 |
Template.alias_exists/assign_tags/remove_tags/get_tags (y en AsyncTemplate) |
Template.aliasExists/assignTags/removeTags/getTags |
create/update-microvm-image no admite etiquetas por versión (sólo por imagen, con lambda:TagResource aparte, AWS_API_NOTES.md §27): usa el ARN de la imagen con la CLI de AWS mientras tanto |
agent: no es una entrada de esta tabla
El agente de IA de 0.8.0 (sbx.agent) no es API de E2B, así que
rayito.e2b.Sandbox no tiene atributo agent: tocarlo es un
AttributeError normal de Python (en TypeScript, un error de tipos), no
un UnimplementedError, y no hay clave agent en _unimplemented.py
ni en unimplemented.ts. Usa el sandbox nativo que el shim lleva
dentro: sbx.native.agent.run(...) (igual en TypeScript), o
rayito.Sandbox.connect(sandbox_id) desde otro proceso. El sandbox
necesita la imagen y la pasarela del agente: ver
Agente en el sandbox.
Desde 0.6.0, Template/AsyncTemplate (y E2B(...).Template/
.AsyncTemplate) construyen imágenes de verdad: ver
Templates. E2B(...).Volume sin volume_store=
lanza lo mismo que Volume; Secret.iam_token
(TS: Secret.iamToken) lanza el de iam. Los casos
siguientes dependen de la imagen o de la configuración y llevan su propio
motivo (texto de Python; TypeScript usa el mismo motivo con los nombres de la
feature en camelCase, salvo las diferencias que se listan tras la tabla):
| Caso | Feature | Motivo, literal |
|---|---|---|
run_code(language=...) / create_code_context(language=...) con algo distinto de python, bash, javascript/js, typescript/ts (r, java) |
run_code(language='r') |
kernels disponibles: python en toda imagen; bash, javascript y typescript en la variante rayito-base-poly; R y Java no (SPEC.md §4) |
un kernel que la imagen no trae (bash, javascript o typescript fuera de rayito-base-poly) |
run_code(language='typescript') |
este kernel sólo existe en la variante rayito-base-poly (publícala con make image-publish-poly y úsala como template) |
Sandbox.list(query=SandboxQuery(metadata=...), state=[PAUSED]) sin index= (TS: list({ query: { metadata, state: ["paused"] } }) sin index; con él se mapea, ver abajo) |
list(state=PAUSED, query.metadata) (TS: list(query.state=paused, query.metadata)) |
los metadatos viven en el agente; leerlos despertaría el sandbox. Pasa index=DynamoDbIndex(...) (tabla opcional en tu cuenta, ver optional-features.md) para filtrar sandboxes en pausa por metadatos sin despertarlos (TS: index: new DynamoDbIndex({...})) |
allow_internet_access=False o network= fuera de rayito-base-caps (el VM se termina antes de lanzar) |
allow_internet_access=False / network |
la imagen no aplica política de egress en el guest (Health.egress_enforcement=NONE): usa una imagen 0.3.0 o posterior de rayito-base-caps (additionalOsCapabilities ALL) o, a nivel de plataforma, rayito.Sandbox.create(egress=[<ConnectorArn de infra/egress-connector.yaml>]); el sandbox se ha terminado |
update_network fuera de rayito-base-caps |
update_network |
la imagen no tiene CAP_NET_ADMIN: la política de egress exige una imagen 0.3.0 o posterior de rayito-base-caps (additionalOsCapabilities ALL) |
network.https_ports no vacío |
network.https_ports |
el proxy de Lambda MicroVMs no reenvía TLS extremo a extremo a un puerto del guest (medido, fila 67 QE2 de AWS_API_NOTES.md §16); get_host(puerto) sirve HTTP en claro |
upload_url/download_url sin bucket de transferencias |
upload_url / download_url |
configura transfer=S3Staging(...) o RAYITO_TRANSFER_BUCKET (TS: configura transfer: { bucket } (S3Staging) o RAYITO_TRANSFER_BUCKET) |
una URL de subida o de bajada contra un rayd anterior a 0.3.0 (sin transferencias) |
upload_url / download_url |
actualiza la imagen: este rayd no tiene transferencias |
el historial de métricas por la forma de clase, sin access token ni RAYITO_ACCESS_TOKEN |
Sandbox.get_metrics(sandbox_id) |
rayd exige el access token del sandbox (x-access-token): pásalo con access_token= o define RAYITO_ACCESS_TOKEN |
el historial de métricas con rango (start/end) o por la forma de clase, contra una imagen anterior a 0.3.0 |
get_metrics(start=, end=) / Sandbox.get_metrics(sandbox_id) (TS: getMetrics({ start, end }) / Sandbox.getMetrics(sandboxId)) |
la imagen es anterior a 0.3.0 (rayd sin MetricsHistory): publica una imagen 0.3.0 o posterior |
cualquier create() contra una imagen anterior a 0.3.0 (el VM se termina antes de lanzar) |
lifecycle |
la imagen no impone el timeout del servidor: publica una imagen 0.3.0 o posterior |
cpu/memory por sandbox |
— | no son parámetros de create(): el tamaño es propiedad de la imagen, igual que Template.build(cpu_count=, memory_mb=) en E2B; el SDK nativo elige entre imágenes ya publicadas con size= (Tamaños, Límites) |
| la CLI de snapshots/fork de E2B | — | no existen: fuera del alcance (SPEC.md §4); la de templates es rayito template (CLI) |
El SDK nativo sigue la misma regla: get_metrics_history() (TS
getMetricsHistory()), en instancia y en la forma de clase, contra un rayd
anterior a 0.3.0 lanza UnimplementedError con el mismo motivo (feature
get_metrics_history / Sandbox.get_metrics_history(sandbox_id); TS
getMetricsHistory / Sandbox.getMetricsHistory(sandboxId)), como las
transferencias y el plazo del servidor; el UnimplementedError genérico de
la tabla unaria queda en __cause__ (TS cause), y el error gRPC
UNIMPLEMENTED un nivel más abajo, en __cause__.__cause__ (TS
cause.cause). Sin rango, el get_metrics() del shim sigue cayendo en la
instantánea.
Diferencias reales entre los dos shims:
listconpausedymetadata. Los dos lanzanUnimplementedErrorcon el mismo motivo antes de tocar AWS; la feature eslist(state=PAUSED, query.metadata)en Python ylist(query.state=paused, query.metadata)en TS.getMetricsde clase sin token. SinaccessTokenniRAYITO_ACCESS_TOKEN, TS lanzaAuthenticationError(el mismo error queSandbox.getMetricsHistory(sandboxId)del SDK nativo), noUnimplementedError: en TS la falta de credencial es un error de autenticación a propósito. Python lanzaUnimplementedErrorcon el motivo de la tabla. En los dos casos no se llama a AWS.- Errores de volumen. Python los exporta desde
rayito.e2b(VolumeException,VolumeNotFoundException,VolumePathNotFoundException, las nativas); TS no los reexporta desderayito/e2b: impórtalos derayito(VolumeError...). - Mensaje de
UnimplementedError.featureyreasoncoinciden; el texto destr(error)/error.messagedifiere en la frase de enlace (Python: «no está disponible en Rayito»; TS: «no está disponible»), así que comparafeatureyreason, no el mensaje.
Diferencias por área¶
Plazo y ciclo de vida¶
- El tope
max_lifetimecuenta el tiempo suspendido: 8 h como mucho desde el arranque. E2B guarda sandboxes pausados sin límite. - En modo
pauselos procesos siguen corriendo entre el vencimiento y la suspensión (≈ 3 s con cliente vivo, hastamax_idlesin él), y la política de idle puede suspender antes del plazo: un trabajo desenganchado se congela trasmax_idlesin tráfico. - Tras una salida en modo
killse facturan ≈ 15 s de 502 (Q58). - El plazo nunca pasa de
max_lifetime − 60 sdesde el arranque. - Una suspensión real de menos de 2 s que cruza el plazo no se reconoce como congelación.
- Más allá de
max_lifetimesólo quedarayito.Sandbox.reincarnate()(Persistencia).
Ficheros y URLs¶
- Subida por
PUTcon el cuerpo en crudo en vez delPOSTmultipart de envd (form=Trueda los campos de unPOSTpara navegadores). - La subida aterriza de forma asíncrona: una lectura, un comando o una celda
posterior la esperan hasta 2 s (barrera de
rayd);ticket.wait()la espera entera. - Tickets de un solo uso; la descarga es una foto tomada al llamar (una exportación que se reencola tras una suspensión vuelve a leer el fichero).
- Caducidad siempre fijada; hace falta un bucket de transferencias.
Red saliente¶
- Sólo en
rayito-base-caps; en otras imágenes falla cerrado. - Las reglas por nombre de host sólo valen en 80/443 y a través del proxy local; los clientes que no lo honran fallan cerrados.
- Bajo deny-all (
allow_internet_access=False) ninguna conexión sale del VM y, desde 0.3.2, el DNS de los procesos del sandbox también se bloquea; UDP/QUIC no pasan por el proxy; un cambio afecta a las conexiones nuevas; root no se filtra. Detalle en Red saliente.
Kernels¶
javascriptytypescriptson el kernel de Deno 2.9.7 enrayito-base-poly, con arranque perezoso;ryjavasiguen fuera.
Observabilidad¶
- El historial de métricas tiene un hueco mientras el sandbox está suspendido y
memory_mbes la vista del guest. ordery los filtros delistse calculan en cliente.
Superficie 2.x¶
gites un envoltorio en cliente sobrecommands.run(E2B lo marca como obsoleto).traffic_access_tokenes el JWE del proxy, que rota cada 45 min.
Secretos (Secret, AsyncSecret)¶
Desde 0.5.0, Secret/AsyncSecret (TS: Secret) ya no
lanzan UnimplementedError: son el CRUD de E2B sobre AWS Secrets Manager
en tu cuenta (SecretStore del SDK nativo, ver Secretos).
Contrato: e2b 2.51.0 de PyPI (e2b/secret/{base,secret_sync,
secret_async,types}.py) y e2b 2.51.0 de npm (src/secret.ts en
dist/index.d.ts), descargados y leídos el 2026-09-30; es la misma versión
que el resto de esta página. Mismos nombres, parámetros y resultados
(create, update, get_info/getInfo, list, exists, destroy,
fill, iam_token/iamToken, SecretInfo, SecretPaginator con
has_next/next_token/next_items); cada diferencia, aquí:
| Qué | E2B 2.51 | Rayito |
|---|---|---|
| Dónde vive el secreto | la API de E2B (beta privada) | un secreto de Secrets Manager <secret_prefix><nombre> en tu cuenta (rayito/ por defecto); cuesta $0,40/mes hasta destroy |
SecretInfo.secret_id / secretId |
sec_… |
el ARN de Secrets Manager (cambia si borras y recreas, como en E2B) |
| Nombres | 1–128 [A-Za-z0-9_-], minúsculas, sec_ reservado (lo valida el servidor) |
la misma regla, validada en el SDK antes de llamar a AWS; el error nunca repite el nombre. El shim pasa el nombre a minúsculas (Secret.create("OpenAI", …) guarda rayito/openai), pero secrets= y SecretStore del SDK nativo no normalizan: refiérete a él en minúsculas (secrets={"K": "openai"}), o "OpenAI" da SecretNotFoundException |
secret en update/get_info/exists/destroy |
id o nombre | el nombre o el ARN (secret_id) |
metadata |
≤ 8 KiB | ≤ 2048 caracteres codificados (Description de Secrets Manager) |
| Tope de secretos | 100 por proyecto | sin tope propio (el de tu cuenta de AWS) |
version |
entero creciente | el mismo entero, codificado en el ClientRequestToken (rayito-secret-version-{n:020d}); get_info da 0 si la versión actual no la escribió Rayito (una rotación externa), y update escribe la mayor versión de Rayito que aún lista DescribeSecret + 1 |
fill(name) |
placeholder que el proxy de egress de E2B resuelve en cabeceras de network.rules |
la misma cadena ${e2b.secrets.<name>}, sin llamar a AWS, pero nada la resuelve: no hay inyector de egress (network.rules sigue en UnimplementedError) y ningún camino del SDK sustituye placeholders dentro de envs. Para entregar un secreto usa secrets= del SDK nativo (Secretos) |
iam_token / iamToken |
token de identidad JWT-SVID | UnimplementedError (motivo de iam) |
update muy frecuente |
sin límite documentado | Secrets Manager recomienda ≤ 1 escritura cada 10 min; el SDK avisa (UserWarning/process.emitWarning) la primera vez que se supera |
destroy y recrear el mismo nombre |
sin reintento en el SDK | destroy devuelve False si el nombre no existía, como E2B (lo comprueba con DescribeSecret antes de borrar). DeleteSecret es asíncrono y AWS tarda ~20–30 s en liberar el nombre (medido en la aceptación de 0.5.0): create reintenta con backoff y jitter hasta 60 s mientras AWS responda InvalidRequestException mencionando el borrado (AWS_API_NOTES.md §19); cualquier otro código sale como SecretException sin reintento |
Opciones de conexión (api_key, domain, request_timeout, …) |
las de la API de E2B | no aplican: RayitoCompatWarning y se ignoran; las propias son region, session (TS: credentials), secret_prefix (TS: secretPrefix) y kms_key_id (TS: kmsKeyId) |
SecretException |
hereda de Exception (TS: SecretError de Error) |
hereda de SandboxException (TS: SandboxError); SecretNotFoundException es además la NotFoundException nativa en Python (en TS sólo SecretError: herencia simple) |
SecretPaginator (TS) |
new SecretPaginator(opts?) |
acepta un segundo argumento interno (las opciones ligadas de E2B(...)) |
Migrar desde el shim 1.x¶
Rayito 0.2.0 traía un shim de E2B 1.x. Estos cambios rompen código escrito contra él:
| Antes (shim 1.x) | Ahora (shim 2.x) |
|---|---|
sbx.pause() devolvía el id del sandbox |
devuelve bool (Sandbox.pause(id) igual) |
files.watch_dir(path, on_event) síncrono |
watch_dir(path, on_event=cb); el segundo posicional es user; vive hasta stop() |
pty.create(size, on_data) síncrono |
pty.create(size, on_data=cb); el segundo posicional es user |
proxy= avisaba y se ignoraba |
se honra (sólo http://) |
NotEnoughSpaceException nunca se lanzaba |
alias de DiskFullException, se lanza con el disco lleno |
sbx.connection_config lanzaba UnimplementedError |
devuelve un ConnectionConfig |
set_timeout, upload_url/download_url, get_metrics(start=, end=), list(next_token=), allow_internet_access=False lanzaban UnimplementedError |
se mapean (tabla de arriba) |
mcp=, network=, lifecycle= eran TypeError |
se mapean o lanzan UnimplementedError |
timeout era la vida inmutable del MicroVM |
es el plazo lógico de rayd; max_lifetime es el tope, y hace falta una imagen 0.3.0 o posterior |
TS: el disco lleno era RateLimitError |
DiskFullError (NotEnoughSpaceError en rayito/e2b) |
Paridad¶
La tabla completa (113 filas: implementado, divergente, fuera por SPEC, imposible en la plataforma, con la página de cada una) está en Paridad con E2B.
OTel (fila 108, divergente). La exportación de telemetría del sandbox de
E2B (Enterprise) no existe en Rayito: no hay un servicio que recoja métricas
o logs del MicroVM y los exporte por ti. Lo que sí hay, nativo y opt-in
(desde 0.5.0), son spans del lado del cliente sobre las llamadas que ya haces
(tracer_provider=/tracerProvider, nombres rayito.*), descritos en
OpenTelemetry; el
shim de E2B (rayito.e2b/rayito/e2b) no está instrumentado, así que
from rayito.e2b import Sandbox no emite ningún span por ahora.
El coste de list(query=SandboxQuery(metadata=...))¶
list-microvms no conoce los metadatos. El filtro es O(n) sobre los sandboxes
RUNNING: por cada uno, get-microvm + create-microvm-auth-token + un
Health (≈ 0,6-0,8 s por sandbox, medido: AWS_API_NOTES.md Q45), y cada
sonda cuenta como tráfico para la política de idle de ese sandbox. Filtra por
template antes si tienes muchos.
Con el índice de metadatos (index=, opcional)¶
La extensión de Rayito index=DynamoDbIndex("rayito-sandboxes") (TS
index: new DynamoDbIndex({ tableName: "rayito-sandboxes" })), en
Sandbox.list(...) o ligada al cliente con E2B(index=...), resuelve
query.metadata con una tabla DynamoDB opcional en tu cuenta
(infra/metadata-index.yaml): un dynamodb:BatchGetItem por página de
list-microvms, ningún get-microvm, token ni Health, así que admite
state=[PAUSED] sin despertar ningún sandbox. Diferencias con E2B:
- Sólo aparecen los sandboxes creados con el índice (
rayito.Sandbox.create(index=)del SDK nativo o unPoolConfig(index=)); elcreate()del shim no lo escribe. Un sandbox sin fila se omite: sus metadatos son desconocidos. - El estado sale siempre de
list-microvms(PAUSED=SUSPENDING|SUSPENDED). - Coste: 0,5 RRU por sandbox candidato y 1 WRU por
create(DynamoDB on-demand); detalle, IAM y cómo apagarlo en Funciones opcionales.