Skip to content
Console
Menu

Queues

Workflows

Getting Started

Authentication

KV Store

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:

Shell
sylphx sandboxes sandbox-shapes list
Shell
curl "https://api.sylphx.com/v1/sandbox_shapes" \
  -H "Authorization: Bearer $SYLPHX_API_KEY"
TypeScript
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:

Shell
sylphx sandboxes leases create \
  --parent orgs/acme/projects/shop/envs/production \
  --spec.shape sandbox_shapes/linux-xs \
  --spec.ttl 10m
Shell
curl -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"}}'
TypeScript
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:

Shell
sylphx sandboxes leases exec \
  orgs/acme/projects/shop/envs/production/leases/lease \
  --command echo --command hello
Shell
curl -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"]}'
TypeScript
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.

Shell
sylphx sandboxes leases write-file \
  orgs/acme/projects/shop/envs/production/leases/lease \
  --path /home/user/hello.txt \
  --content hello
Shell
curl -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":"…"}'
TypeScript
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:

Shell
sylphx sandboxes leases read-file \
  orgs/acme/projects/shop/envs/production/leases/lease \
  --path /home/user/hello.txt
Shell
curl -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"}'
TypeScript
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

Shell
sylphx sandboxes leases release \
  orgs/acme/projects/shop/envs/production/leases/lease \
  --yes
Shell
curl -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 '{}'
TypeScript
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:

Shell
sylphx sandboxes lease-events list \
  orgs/acme/projects/shop/envs/production/leases/lease

#Next