Menu
Platform
AI
App store purchases
Database
Flags
Jobs and cron
Localization
Monitoring
Notifications
Payments
Queues
Sandboxes
Webhooks
Getting Started
Authentication
KV Store
Deploy & Infrastructure
Reference
Sandboxes quickstart
From an empty environment to a command running in a lease, in five calls
This takes an environment to a machine running your command, and then gives the machine back. Every step exists in the console too, but the calls are the clearest statement of what is happening.
#1. Find a shape
The shape catalog is read-only, and it is the list of machines you may ask for:
sylphx sandboxes sandbox-shapes listcurl "https://api.sylphx.com/v1/sandbox_shapes" \
-H "Authorization: Bearer $SYLPHX_API_KEY"const response = await sylphx.sandboxes.sandboxShapes.list({})A shape carries the operating system, the vCPUs, the memory, the disk, the
Cell capability Placement matches, the kinds it can serve, and
min_lease_duration and max_lease_duration — the shortest and longest lease
of it. offered says whether the shape is sold now, and a lease naming one
that is not is refused SHAPE_NOT_OFFERED. The collection
page lists every field.
#2. Create a lease
A lease is created under an environment, names a shape, and is granted now or
refused. This one asks for sandbox_shapes/linux-xs — one vCPU, 1024 MiB of
memory and a 5 GiB disk on the kata capability — and runs it for ten
minutes:
sylphx sandboxes leases create \
--parent orgs/acme/projects/shop/envs/production \
--spec.shape sandbox_shapes/linux-xs \
--spec.ttl 10mcurl -X POST "https://api.sylphx.com/v1/orgs/acme/projects/shop/envs/production/leases" \
-H "Authorization: Bearer $SYLPHX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"spec":{"shape":"sandbox_shapes/linux-xs","ttl":"10m"}}'const response = await sylphx.sandboxes.leases.create({
lease: { spec: { shape: 'sandbox_shapes/linux-xs', ttl: '10m' } },
parent: 'orgs/acme/projects/shop/envs/production',
})ttl is required: it is the longest the lease may live, counted from the
grant. A microVM takes a ttl from one minute to 24 hours.
create waits for READY and gives up on the wait after 60 seconds; pass
skip_wait_ready to be answered at GRANTED instead and poll the lease
yourself. A machine that cannot be granted this second is refused, not
queued — the answer carries status.refusal, one of no_capacity,
no_matching_cell, shape_not_offered, shape_not_entitled,
image_not_found or abuse_hold.
#3. Run a command
exec runs one command in a ready lease and answers when it finishes, with
exit_code, stdout and stderr:
sylphx sandboxes leases exec \
orgs/acme/projects/shop/envs/production/leases/lease \
--command echo --command hellocurl -X POST "https://api.sylphx.com/v1/orgs/acme/projects/shop/envs/production/leases/lease:exec" \
-H "Authorization: Bearer $SYLPHX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"command":["bash","-lc","echo hello"]}'const response = await sylphx.sandboxes.leases.exec({
command: ['bash', '-lc', 'echo hello'],
name: 'orgs/acme/projects/shop/envs/production/leases/lease',
})The command does not go through a shell unless you ask for one: the request
takes an argument list, and ["bash", "-lc", "…"] is the shape to use when
you want shell syntax. The default timeout is 60 seconds and the longest is
10 minutes; a command that outlives both belongs behind guest_uri, which
serves the guest's own port, not in exec.
Decode the streams before you print them
stdout and stderr are bytes on the wire, so they arrive base64-encoded,
and the generated TypeScript type declares them as plain strings. Decode
before printing, or a byte count will look right and the text will not.
#4. Write and read a file
Files go in with write_file and come back with read_file, at most 16 MiB
at a time. Missing parent directories are created for you.
sylphx sandboxes leases write-file \
orgs/acme/projects/shop/envs/production/leases/lease \
--path /home/user/hello.txt \
--content hellocurl -X POST "https://api.sylphx.com/v1/orgs/acme/projects/shop/envs/production/leases/lease:writeFile" \
-H "Authorization: Bearer $SYLPHX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"path":"/home/user/hello.txt","content":"…"}'const response = await sylphx.sandboxes.leases.writeFile({
name: 'orgs/acme/projects/shop/envs/production/leases/lease',
path: '/home/user/hello.txt',
content: 'hello',
})Reading it back is the same call the other way:
sylphx sandboxes leases read-file \
orgs/acme/projects/shop/envs/production/leases/lease \
--path /home/user/hello.txtcurl -X POST "https://api.sylphx.com/v1/orgs/acme/projects/shop/envs/production/leases/lease:readFile" \
-H "Authorization: Bearer $SYLPHX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"path":"/home/user/hello.txt"}'const response = await sylphx.sandboxes.leases.readFile({
name: 'orgs/acme/projects/shop/envs/production/leases/lease',
path: '/home/user/hello.txt',
})A file written this way lives on the lease's own disk. When the lease ends the disk goes with it — a volume is what makes work outlive a lease.
#5. Release the lease
sylphx sandboxes leases release \
orgs/acme/projects/shop/envs/production/leases/lease \
--yescurl -X POST "https://api.sylphx.com/v1/orgs/acme/projects/shop/envs/production/leases/lease:release" \
-H "Authorization: Bearer $SYLPHX_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'const response = await sylphx.sandboxes.leases.release({
name: 'orgs/acme/projects/shop/envs/production/leases/lease',
})release ends the lease now and answers with the ended lease, whose
end_reason is END_REASON_RELEASED. The machine is destroyed and never
leased again. Leaving the lease alone instead ends it at its ttl, and the
end_reason says which one happened.
The lease's own history outlives it, so the last call of the run is the one that says what happened to the machine:
sylphx sandboxes lease-events list \
orgs/acme/projects/shop/envs/production/leases/lease