Skip to content
sistemo.io beta

Exec

Run a command inside a running machine. There is one engine: a job on the guest. Scope: full. The machine must be running (otherwise 409).

SDK — sb.run()

Waits until the command finishes. Default timeout 120 seconds, ceiling 24 hours, enforced in the machine. The SDK talks to /execs so a proxy cannot kill the job by dropping one HTTP request.

r = sb.run("python3 -c 'print(2+2)'", timeout=30)
r = sb.run("npm ci && npm test", timeout=3600)
await sb.run("node -e 'console.log(2+2)'", 30);
await sb.run("npm ci && npm test", 3600);
Field Python JS
Exit code exit_code exitCode
Output stdout / stderr stdout / stderr
Success? ok —
Hit 32 MiB cap? truncated truncated

REST — the job

POST   /v1/machines/{id}/execs          → 202
GET    /v1/machines/{id}/execs
GET    /v1/machines/{id}/execs/{exec_id}
GET    /v1/machines/{id}/execs/{exec_id}/output?stream=stdout&offset=0
DELETE /v1/machines/{id}/execs/{exec_id}

You mint exec_id. Re-sending the same id is 409, not a second run.

curl -sS -X POST "https://api.sistemo.io/v1/machines/$ID/execs" \
  -H "Authorization: Bearer $SISTEMO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"exec_id":"build-1","script":"npm ci && npm run build","timeout_sec":3600}'
Field Default Notes
exec_id required 1–64 letters, digits, ., -, _
script required Shell command
timeout_sec 120 Max 86400 (24 h)

States: running (exit_code is null) · succeeded · failed · cancelled · expired (hit timeout_sec, exit 124) · lost (we cannot say; exit_code stays null).

Output is base64, byte offsets, 32 MiB per stream, truncated if cut. Cancel is SIGTERM, then SIGKILL after 10 s. Up to 16 jobs at once per machine — the 17th answers 409.

job = sb.start("npm ci && npm run build", timeout=3600)
print(job.wait().exit_code)

Notes

  • Stopping or destroying the machine ends every job on it.
  • Exec has its own rate limit; on 429, back off (Errors).

Limits

Concurrent commands per machine 16
Default lifetime 120 s
Maximum lifetime 24 h
Output kept per stream 32 MiB
Start rate 5/s, burst 20, per account

Starting a 17th command on one machine returns 409 — wait for one to finish, or spread the work over more machines. sb.run() counts against this too: it is a job like any other, so sixteen parallel run() calls on one machine is the ceiling.

When the machine cannot say what happened

A command whose machine died — rebooted, ran out of memory, was destroyed — comes back as lost, not failed.

failed means it ran and returned non-zero. lost means we can say neither that it ran nor that it did not, so it is not safe to retry blindly and not safe to treat as done.

job = sb.start("./migrate.sh")
job.wait()
if job.lost:
    ...        # find out what actually happened before re-running
elif job.ok:
    ...

Use the job handle from sb.start() for this check: lost and ok are on it in every published SDK release. ⚠ exit_code stays None (null in JS) on a lost job — never 0, never -1 — and ok is false. sb.run() reports a lost command as exit_code == -1, which is a placeholder rather than a status: -1 is also something a real command can exit with, so do not branch on it.