Montajes S3¶
Monta uno o más buckets S3 (o un prefijo suyo) como una carpeta normal
dentro del sandbox, con Mountpoint for Amazon S3
(mount-s3). Sólo sobre rayito-base-caps (o una variante derivada por
tamaño): mount(2) necesita CAP_SYS_ADMIN, que sólo esa variante da al
agente, y el daemon necesita las credenciales del execution role por IMDS.
En cualquier otra imagen, create() termina el VM y lanza
UnimplementedError.
Coste y activación
- Por defecto: apagado. Sin
mounts=(TypeScript:mounts)raydno abre/dev/fuseni lanza nada. - Activa:
mounts={"/mnt/data": S3Mount(bucket="...", prefix="...")}enSandbox.create(). Necesita tres cosas: una imagenrayito-base-capspublicada con el bucket en su allowlist (RAYITO_ALLOWED_MOUNT_BUCKETS, ver abajo),execution_role_arn=y la política IAM de la pilas3-mountsen ese rol. - Recursos y llamadas AWS: ninguno nuevo por sí solo.
mount-s3hace las llamadas normales de S3 (GetObject/ListObjectsV2, yPutObject/DeleteObjectcon escritura) contra el bucket que montes, bajo las credenciales del execution role — las mismas que ya pagarías llamando a S3 por tu cuenta. - Coste aproximado (us-east-1, consultado 2026-10-01, precios de S3): $0 propio de Rayito; pagas las peticiones y el almacenamiento normales de S3 del bucket que montes.
- IAM: la política gestionada
RayitoS3MountAccess(infra/s3-mounts.yaml, componenteOptionalStacks3-mounts) en el execution role del sandbox, acotada a un bucket por pila. - Cómo apagarla: no pases
mounts=;rayito stack destroy s3-mountsquita la política (no borra ningún objeto ni el bucket).
Medido en AWS real
Sandbox.create(mounts=)/create({ mounts }) y sbx.mounts pasaron
la aceptación en AWS real (S3M-1..S3M-4, AWS_API_NOTES.md
Q100–Q104): lectura y escritura, pause()/resume() con el montaje
vivo, allow_internet_access=False, y el VM terminado ante un fallo
de montaje. mount-s3 1.24.0 añade 72,7 MB a la imagen.
Cuándo usarlo¶
- Tu código ya sabe trabajar con rutas normales (
pandas.read_csv,ls,grep, una librería que sólo acepta un path) y el dato vive en S3: sin montaje, tendrías que descargarlo entero primero. - Quieres que la salida de un agente (ficheros que escribe) aparezca directamente en S3, sin un paso extra de subida.
- Cuándo no: necesitas semántica POSIX completa (locks,
mmapescribible, ficheros muy pequeños y muy numerosos con muchofsync) — S3 no es un filesystem, ymount-s3tiene el mismo límite que cualquier FUSE sobre un object store. Para eso, copia el fichero alHOMEdel sandbox confiles.write/sandbox.giten vez de montarlo.
Rutas de montaje¶
La clave de mounts=/mounts es la ruta absoluta donde aparece el bucket
dentro del guest: bajo /mnt/ o /home/user/, sin solaparse con otro
montaje (la misma regla que usa volumes= de los
volúmenes EFS), y como mucho 4 montajes en total por
sandbox, sumando mounts= y volumes=.
Antes de empezar: el allowlist de la imagen (obligatorio)¶
rayd sólo monta los buckets que la propia imagen declara en
RAYITO_ALLOWED_MOUNT_BUCKETS (coma-separados). Es configuración de la
imagen, no una opción por sandbox: así el código que llama a create() no
puede montar un bucket que quien publica la imagen no autorizó. Vacío o
ausente deniega todos los buckets: create() termina el VM y lanza
MountException(code="not_allowed") (MountError con
code: "not_allowed" en TypeScript), cuyo mensaje dice que el bucket no
está en el allowlist de la imagen y cómo publicarla con él (nunca nombra el
bucket).
rayito image publish --artifact rayito-image.zip --base-image-version 1 \
--image-name rayito-base-caps --os-capabilities ALL \
--env RAYITO_ALLOWED_MOUNT_BUCKETS=mi-bucket,otro-bucket
Desde el repositorio, make image-publish-caps MOUNT_BUCKETS=mi-bucket,otro-bucket
(o image-publish-caps-efs) añade ese --env por ti.
Si construyes la imagen con un Dockerfile propio sobre
rayito-base-caps, ENV RAYITO_ALLOWED_MOUNT_BUCKETS=... hace lo mismo.
Cambiar el allowlist es publicar una versión nueva de la imagen.
Desplegar la política IAM¶
rayito stack deploy s3-mounts --param BucketName=mi-bucket \
--param Prefixes='team7/*,runs/*' --param ReadOnly=false
La política RayitoS3MountAccess cubre un solo bucket (una pila por
bucket) y, dentro de él, sólo los prefijos de Prefixes (hasta 4,
acabados en *; * por defecto es todo el bucket), también para leer,
escribir y borrar objetos: un sandbox que sólo debe escribir runs/42/ no
puede tocar objetos de otro prefijo. rayito stack status s3-mounts
muestra el ARN de la política ya creada; añádelo al execution role del
sandbox. rayito stack destroy s3-mounts la quita (nunca borra el bucket
ni sus objetos).
Ejemplo¶
create() no vuelve hasta que cada montaje está montado: si alguno falla
(o sigue sin responder tras 15 s), termina el VM y lanza MountException
con el motivo en code (iam_denied, not_found, not_allowed,
invalid_path, helper_missing, network o timeout; unknown si el
agente manda una clase que tu versión del SDK aún no conoce). En cuanto
create() vuelve, la carpeta ya se puede leer.
from rayito import S3Mount, Sandbox
sbx = Sandbox.create(
"rayito-base-caps",
execution_role_arn="arn:aws:iam::<cuenta>:role/mi-execution-role",
mounts={
"/mnt/data": S3Mount(bucket="mi-bucket", prefix="team7/"), # (1)!
"/mnt/out": S3Mount(
bucket="mi-bucket", prefix="runs/42/", read_only=False, allow_overwrite=True
),
},
)
sbx.commands.run("python -c \"import pandas; pandas.read_csv('/mnt/data/datos.csv')\"")
print(sbx.mounts) # {"/mnt/data": MountStatus(state="mounted"), ...}
sbx.kill()
read_only=Truepor defecto: sólo lectura salvo que pidas lo contrario explícitamente.
import { Sandbox, S3Mount } from "rayito";
const sbx = await Sandbox.create({
template: "rayito-base-caps",
executionRoleArn: "arn:aws:iam::<cuenta>:role/mi-execution-role",
mounts: {
"/mnt/data": new S3Mount({ bucket: "mi-bucket", prefix: "team7/" }),
"/mnt/out": new S3Mount({
bucket: "mi-bucket",
prefix: "runs/42/",
readOnly: false,
allowOverwrite: true,
}),
},
});
await sbx.commands.run("python3 -c \"import pandas; pandas.read_csv('/mnt/data/datos.csv')\"");
console.log(await sbx.mounts()); // Map { "/mnt/data" => { state: "mounted" }, ... }
await sbx.kill();
mounts también acepta un Map en TypeScript.
sbx.mounts (una propiedad en Sandbox; await sbx.mounts() en
AsyncSandbox y en TypeScript) lee el estado en vivo en cada llamada: si el daemon de un montaje muere más tarde, rayd lo
relanza solo y, mientras tanto, ese montaje aparece como "pending" o
"failed".
Divergencias con E2B¶
E2B no tiene un equivalente a mounts= (fila 111 de
Paridad con E2B): el shim no añade ningún método nuevo
para esto.
Seguridad¶
- El daemon
mount-s3corre como el usuario dedicadorayito-mount(uid 990), nunca como uid 1000 ni como root tras arrancar; sus credenciales llegan por su propio acceso a IMDS, nunca por argv ni por el entorno del proceso queraydlanza (AWS_REGION/PATHson las únicas variables). - El bucket y el prefijo no son secretos (como la
metadatadel sandbox): un proceso uid 1000 puede ver el nombre del bucket y el prefijo en la línea de órdenes del daemon (/proc/<pid>/cmdline), nunca una credencial; su entorno (/proc/<pid>/environ) ni siquiera es legible, y tampoco puede enviarle señales. - El bucket debe estar en
RAYITO_ALLOWED_MOUNT_BUCKETSde la imagen (config de imagen, no una opción por sandbox); vacío o ausente deniega todos los buckets. - La sonda de disponibilidad que
raydlanza como uid 1000 tras cada montaje (/usr/bin/stat <ruta>) y el propiomount-s3arrancan con un entorno construido desde cero (sóloPATH, másAWS_REGIONenmount-s3), sin heredar ningún descriptor deraydni sus señales ignoradas: el entorno derayd(el ARN de la imagen, variablesRAYITO_*o--envde la imagen) nunca llega a un proceso que otro proceso uid 1000 pueda leer en/proc/<pid>/environ. raydmonta como root, pero nunca sigue un enlace simbólico en la ruta de montaje (ni al crearla ni al relanzar un montaje): si el código del sandbox cambia/home/user/<carpeta>por un enlace a un directorio del sistema, el montaje falla coninvalid_pathen vez de montar el bucket encima de ese directorio.- La política IAM se acota a los prefijos que declares, también para
escribir y borrar: la contención no depende sólo de
mount-s3 --prefixdentro de un guest que ejecuta código no confiable.
Detalle completo: Seguridad, amenaza T20.