API reference
Exec
Execution and files: run commands synchronously, read and write files, background processes (follow output by long polling or WebSocket, with reconnection after a disconnect).
Run a command synchronously
POST/v1/environments/{id}/exec
command is run with bash -lc (the same environment as the Agent's login shell); argv is executed as is. Provide exactly one of the two. The default working directory is the agent user's home directory.
The call returns when the process exits; a process exceeding timeout_secs is killed inside the VM (timed_out: true, HTTP is still 200). The control plane waits a further 30 seconds as a fallback,
after which it returns 503 exec_timeout. stdout and stderr each return at most 1 MiB (the rest is cut off and *_truncated is set), decoded as UTF-8 (invalid bytes replaced with U+FFFD).
The process environment is first loaded from /etc/temper/agent.env and the Agent package's /opt/agent/env; env cannot override variables already set in those two files.
If the environment is not running it is woken first; see "Execution and files" in the introduction.
Request body application/json · ExecRequest
| Field | Type | Description |
|---|---|---|
argv | array of string | Executed as is, for example |
command | string | Run with |
env | map of string | Extra environment variables. Names consist of letters, digits and underscores and do not start with a digit; variables already set in agent.env or the Agent package's env cannot be overridden. |
cwd | string | Working directory (a path inside the runtime unit); defaults to the agent user's home directory. |
timeout_secs | integer | |
stdin | string | Standard input for the process (closed afterwards); if omitted, stdin is /dev/null. |
Responses
- 200The process exited (a non-zero exit code is still 200)
ExecResult - 400Invalid parameters: validation failed, the body is not valid JSON, or the query string could not be parsed (all returned in the same JSON shape).
- 401Missing, invalid or revoked key
- 404Does not exist, or does not belong to this developer (`no such resource`); for a nonexistent route `message` is `no such route`
- 409The environment's actual state is failed (`environment_failed`; check its events), or it was suspended for exceeding a monthly usage limit (`limit_exceeded`; see `setLimit`)
- 415Missing `Content-Type: application/json` (`unsupported_media_type`)
- 422The JSON does not match the schema: a missing field, a wrong type or enum value, or an unknown field (`invalid_body`)
- 500Internal error (details are recorded only in server logs)
- 503Temporarily unable to complete the request; `error` tells the cases apart: `environment_not_placed` (not placed on a host within 15 seconds), `environment_not_running` (not up within 90 seconds), `host_unavailable` (host offline), `guest_unavailable` (the VM cannot be reached or disconnected midway), `exec_failed` (the process could not be started inside the VM), `exec_timeout` (a synchronous exec still had not finished after its timeout), `file_operation_failed` (a file operation hit an unexpected error).
Read a file
GET/v1/environments/{id}/files
Returns the raw bytes (application/octet-stream), streamed as they are read. 404 if it does not exist, 400 if it is a directory, 403 if the agent user cannot read it.
An error after streaming has begun (the VM disconnects) can only be signalled by dropping the connection: the client sees an incomplete transfer.
Parameters
| Parameter | In | Type | Description |
|---|---|---|---|
pathrequired | query | string | A path inside the runtime unit. Relative paths are resolved against the agent user's home directory (also the default working directory). Permissions and symbolic links are evaluated as the agent user inside the runtime unit sees them, and paths cannot escape the runtime unit. Must not be empty or contain NUL. |
Responses
- 200File contents
string - 400Invalid parameters: validation failed, the body is not valid JSON, or the query string could not be parsed (all returned in the same JSON shape).
- 401Missing, invalid or revoked key
- 403The agent user inside the runtime unit lacks permission (cannot read, write, or create the parent directories)
- 404Does not exist, or does not belong to this developer (`no such resource`); for a nonexistent route `message` is `no such route`
- 409The environment's actual state is failed (`environment_failed`; check its events), or it was suspended for exceeding a monthly usage limit (`limit_exceeded`; see `setLimit`)
- 500Internal error (details are recorded only in server logs)
- 503Temporarily unable to complete the request; `error` tells the cases apart: `environment_not_placed` (not placed on a host within 15 seconds), `environment_not_running` (not up within 90 seconds), `host_unavailable` (host offline), `guest_unavailable` (the VM cannot be reached or disconnected midway), `exec_failed` (the process could not be started inside the VM), `exec_timeout` (a synchronous exec still had not finished after its timeout), `file_operation_failed` (a file operation hit an unexpected error).
Write a file
PUT/v1/environments/{id}/files
The request body is the content (at most 1 GiB; Content-Type is not checked, application/octet-stream is recommended). Overwrites any existing file.
404 if the parent directory does not exist; with mkdir=true the parent directories are created first (403 if they cannot be); 400 if the target is a directory; 403 if it cannot be written.
Parameters
| Parameter | In | Type | Description |
|---|---|---|---|
pathrequired | query | string | A path inside the runtime unit. Relative paths are resolved against the agent user's home directory (also the default working directory). Permissions and symbolic links are evaluated as the agent user inside the runtime unit sees them, and paths cannot escape the runtime unit. Must not be empty or contain NUL. |
mkdir | query | boolean | Create the parent directories first ( |
Request body application/octet-stream
Responses
- 201Written
WriteFileResult - 400Invalid parameters: validation failed, the body is not valid JSON, or the query string could not be parsed (all returned in the same JSON shape).
- 401Missing, invalid or revoked key
- 403The agent user inside the runtime unit lacks permission (cannot read, write, or create the parent directories)
- 404Does not exist, or does not belong to this developer (`no such resource`); for a nonexistent route `message` is `no such route`
- 409The environment's actual state is failed (`environment_failed`; check its events), or it was suspended for exceeding a monthly usage limit (`limit_exceeded`; see `setLimit`)
- 413The request body exceeds the size limit (`payload_too_large`)
- 500Internal error (details are recorded only in server logs)
- 503Temporarily unable to complete the request; `error` tells the cases apart: `environment_not_placed` (not placed on a host within 15 seconds), `environment_not_running` (not up within 90 seconds), `host_unavailable` (host offline), `guest_unavailable` (the VM cannot be reached or disconnected midway), `exec_failed` (the process could not be started inside the VM), `exec_timeout` (a synchronous exec still had not finished after its timeout), `file_operation_failed` (a file operation hit an unexpected error).
Delete a file or directory
DELETE/v1/environments/{id}/files
404 if it does not exist; 400 if it is a directory and recursive=true was not given. For a symbolic link only the link itself is deleted.
Parameters
| Parameter | In | Type | Description |
|---|---|---|---|
pathrequired | query | string | A path inside the runtime unit. Relative paths are resolved against the agent user's home directory (also the default working directory). Permissions and symbolic links are evaluated as the agent user inside the runtime unit sees them, and paths cannot escape the runtime unit. Must not be empty or contain NUL. |
recursive | query | boolean | Delete a directory together with its contents ( |
Responses
- 204Deleted
- 400Invalid parameters: validation failed, the body is not valid JSON, or the query string could not be parsed (all returned in the same JSON shape).
- 401Missing, invalid or revoked key
- 403The agent user inside the runtime unit lacks permission (cannot read, write, or create the parent directories)
- 404Does not exist, or does not belong to this developer (`no such resource`); for a nonexistent route `message` is `no such route`
- 409The environment's actual state is failed (`environment_failed`; check its events), or it was suspended for exceeding a monthly usage limit (`limit_exceeded`; see `setLimit`)
- 500Internal error (details are recorded only in server logs)
- 503Temporarily unable to complete the request; `error` tells the cases apart: `environment_not_placed` (not placed on a host within 15 seconds), `environment_not_running` (not up within 90 seconds), `host_unavailable` (host offline), `guest_unavailable` (the VM cannot be reached or disconnected midway), `exec_failed` (the process could not be started inside the VM), `exec_timeout` (a synchronous exec still had not finished after its timeout), `file_operation_failed` (a file operation hit an unexpected error).
List a directory
GET/v1/environments/{id}/files/list
One level, not recursive, sorted by name, at most 10000 entries (the rest are cut off and truncated is set). 404 if it does not exist, 400 if it is not a directory.
Parameters
| Parameter | In | Type | Description |
|---|---|---|---|
pathrequired | query | string | A path inside the runtime unit. Relative paths are resolved against the agent user's home directory (also the default working directory). Permissions and symbolic links are evaluated as the agent user inside the runtime unit sees them, and paths cannot escape the runtime unit. Must not be empty or contain NUL. |
Responses
- 200Directory contents
DirectoryListing - 400Invalid parameters: validation failed, the body is not valid JSON, or the query string could not be parsed (all returned in the same JSON shape).
- 401Missing, invalid or revoked key
- 403The agent user inside the runtime unit lacks permission (cannot read, write, or create the parent directories)
- 404Does not exist, or does not belong to this developer (`no such resource`); for a nonexistent route `message` is `no such route`
- 409The environment's actual state is failed (`environment_failed`; check its events), or it was suspended for exceeding a monthly usage limit (`limit_exceeded`; see `setLimit`)
- 500Internal error (details are recorded only in server logs)
- 503Temporarily unable to complete the request; `error` tells the cases apart: `environment_not_placed` (not placed on a host within 15 seconds), `environment_not_running` (not up within 90 seconds), `host_unavailable` (host offline), `guest_unavailable` (the VM cannot be reached or disconnected midway), `exec_failed` (the process could not be started inside the VM), `exec_timeout` (a synchronous exec still had not finished after its timeout), `file_operation_failed` (a file operation hit an unexpected error).
List background processes
GET/v1/environments/{id}/processes
Includes exited processes that have not been deleted. Not paginated (at most 64).
Responses
- 200Processes
ProcessList - 401Missing, invalid or revoked key
- 404Does not exist, or does not belong to this developer (`no such resource`); for a nonexistent route `message` is `no such route`
- 409The environment's actual state is failed (`environment_failed`; check its events), or it was suspended for exceeding a monthly usage limit (`limit_exceeded`; see `setLimit`)
- 500Internal error (details are recorded only in server logs)
- 503Temporarily unable to complete the request; `error` tells the cases apart: `environment_not_placed` (not placed on a host within 15 seconds), `environment_not_running` (not up within 90 seconds), `host_unavailable` (host offline), `guest_unavailable` (the VM cannot be reached or disconnected midway), `exec_failed` (the process could not be started inside the VM), `exec_timeout` (a synchronous exec still had not finished after its timeout), `file_operation_failed` (a file operation hit an unexpected error).
Start a background process
POST/v1/environments/{id}/processes
Same parameters as exec, except that timeout_secs defaults to 0 (no limit), and stdin: true means you will write stdin later through attach (otherwise stdin is /dev/null).
The process is independent of the request; its output is kept in the VM's memory (the most recent 8 MiB per process) and can be read with /output or /attach.
Process records live in the VM's memory: they survive a suspend and resume, but are lost when the VM is replaced (upgrade, failure); after that, queries return 404.
Each environment keeps at most 64 background processes (including exited ones that have not been deleted); beyond that, 429 too_many_processes.
Request body application/json · SpawnRequest
| Field | Type | Description |
|---|---|---|
argv | array of string | |
command | string | |
env | map of string | |
cwd | string | |
timeout_secs | integer | Maximum run time, after which the process is killed; 0 = no limit |
stdin | boolean | You will write stdin later through attach; otherwise stdin is /dev/null |
Responses
- 201Started
Process - 400Invalid parameters: validation failed, the body is not valid JSON, or the query string could not be parsed (all returned in the same JSON shape).
- 401Missing, invalid or revoked key
- 404Does not exist, or does not belong to this developer (`no such resource`); for a nonexistent route `message` is `no such route`
- 409The environment's actual state is failed (`environment_failed`; check its events), or it was suspended for exceeding a monthly usage limit (`limit_exceeded`; see `setLimit`)
- 415Missing `Content-Type: application/json` (`unsupported_media_type`)
- 422The JSON does not match the schema: a missing field, a wrong type or enum value, or an unknown field (`invalid_body`)
- 429A count limit was reached (this is not rate limiting; retrying will not help until something is released)
- 500Internal error (details are recorded only in server logs)
- 503Temporarily unable to complete the request; `error` tells the cases apart: `environment_not_placed` (not placed on a host within 15 seconds), `environment_not_running` (not up within 90 seconds), `host_unavailable` (host offline), `guest_unavailable` (the VM cannot be reached or disconnected midway), `exec_failed` (the process could not be started inside the VM), `exec_timeout` (a synchronous exec still had not finished after its timeout), `file_operation_failed` (a file operation hit an unexpected error).
Get a background process
GET/v1/environments/{id}/processes/{pid}
Whether it is running, its exit status, and the output offset reached so far.
Responses
- 200Process
Process - 401Missing, invalid or revoked key
- 404Does not exist, or does not belong to this developer (`no such resource`); for a nonexistent route `message` is `no such route`
- 409The environment's actual state is failed (`environment_failed`; check its events), or it was suspended for exceeding a monthly usage limit (`limit_exceeded`; see `setLimit`)
- 500Internal error (details are recorded only in server logs)
- 503Temporarily unable to complete the request; `error` tells the cases apart: `environment_not_placed` (not placed on a host within 15 seconds), `environment_not_running` (not up within 90 seconds), `host_unavailable` (host offline), `guest_unavailable` (the VM cannot be reached or disconnected midway), `exec_failed` (the process could not be started inside the VM), `exec_timeout` (a synchronous exec still had not finished after its timeout), `file_operation_failed` (a file operation hit an unexpected error).
Kill and forget a process
DELETE/v1/environments/{id}/processes/{pid}
Kills the process if it is still running, then deletes its record and retained output.
Responses
- 204Deleted
- 401Missing, invalid or revoked key
- 404Does not exist, or does not belong to this developer (`no such resource`); for a nonexistent route `message` is `no such route`
- 409The environment's actual state is failed (`environment_failed`; check its events), or it was suspended for exceeding a monthly usage limit (`limit_exceeded`; see `setLimit`)
- 500Internal error (details are recorded only in server logs)
- 503Temporarily unable to complete the request; `error` tells the cases apart: `environment_not_placed` (not placed on a host within 15 seconds), `environment_not_running` (not up within 90 seconds), `host_unavailable` (host offline), `guest_unavailable` (the VM cannot be reached or disconnected midway), `exec_failed` (the process could not be started inside the VM), `exec_timeout` (a synchronous exec still had not finished after its timeout), `file_operation_failed` (a file operation hit an unexpected error).
Get output (long polling)
GET/v1/environments/{id}/processes/{pid}/output
Reads from offset since. Output already available is returned immediately; otherwise the call waits up to wait_secs seconds for the first chunk, then waits another 0.1 seconds to collect anything that follows right after it.
At most about 4 MiB is returned per call. Pass next back as since on the next call. If since is earlier than the oldest output the VM still retains (process.output_start),
output starts from the oldest retained byte (the first chunk's offset will be greater than since). Once the process has exited, exited: true and exit holds the exit status.
Offsets are in bytes (stdout and stderr combined); data is decoded as UTF-8.
Parameters
| Parameter | In | Type | Description |
|---|---|---|---|
since | query | integer (int64) | Byte offset to start from. |
wait_secs | query | integer | Maximum seconds to wait when there is no new output; values above 30 are treated as 30. |
Responses
- 200A chunk of output
ProcessOutput - 400Invalid parameters: validation failed, the body is not valid JSON, or the query string could not be parsed (all returned in the same JSON shape).
- 401Missing, invalid or revoked key
- 404Does not exist, or does not belong to this developer (`no such resource`); for a nonexistent route `message` is `no such route`
- 409The environment's actual state is failed (`environment_failed`; check its events), or it was suspended for exceeding a monthly usage limit (`limit_exceeded`; see `setLimit`)
- 500Internal error (details are recorded only in server logs)
- 503Temporarily unable to complete the request; `error` tells the cases apart: `environment_not_placed` (not placed on a host within 15 seconds), `environment_not_running` (not up within 90 seconds), `host_unavailable` (host offline), `guest_unavailable` (the VM cannot be reached or disconnected midway), `exec_failed` (the process could not be started inside the VM), `exec_timeout` (a synchronous exec still had not finished after its timeout), `file_operation_failed` (a file operation hit an unexpected error).
Follow output, write stdin and send signals in real time (WebSocket)
GET/v1/environments/{id}/processes/{pid}/attach
A WebSocket upgrade (Connection: Upgrade, Upgrade: websocket), authenticated like every other endpoint (Authorization header).
The server connects to the VM and confirms the process exists before upgrading, so errors such as 404, 409 and 503 are returned as ordinary HTTP responses; a request without the upgrade headers gets 400.
Disconnecting does not affect the process: reconnect with the same process id and the last next as since to continue where you left off.
To "run a command over WebSocket", first POST /processes (with stdin: true if you want to write stdin), then attach.
The server sends only JSON text messages, distinguished by type:
{"type": "attached", "process": Process}: always the first message;{"type": "output", "stream": "stdout" | "stderr", "offset": <byte offset>, "data": "<UTF-8 text>"};{"type": "exit", "exit": ProcessExit, "next": <offset>}: the process exited; the server then closes the connection;{"type": "error", "message": "...", "next"?: <offset>}: the VM disconnected (withnext; the server then closes the connection and you can reconnect), or a message from the client could not be understood (withoutnext; the connection stays open).
The client sends JSON text messages (fields can be combined): {"stdin": "<text>"} writes to stdin (requires stdin: true at spawn),
{"stdin_eof": true} closes stdin, and {"signal": "INT"} sends a signal (TERM / KILL / INT / HUP).
See AttachServerMessage and AttachClientMessage for the message schemas.
Parameters
| Parameter | In | Type | Description |
|---|---|---|---|
since | query | integer (int64) | Byte offset to start replaying from. |
Responses
- 101Upgraded to WebSocket; messages are then exchanged in the format described above. The `application/json` here describes each text message the server sends (`AttachServerMessage`), not a response body; for messages the client sends, see `AttachClientMessage`.
- 400Invalid parameters: validation failed, the body is not valid JSON, or the query string could not be parsed (all returned in the same JSON shape).
- 401Missing, invalid or revoked key
- 404Does not exist, or does not belong to this developer (`no such resource`); for a nonexistent route `message` is `no such route`
- 409The environment's actual state is failed (`environment_failed`; check its events), or it was suspended for exceeding a monthly usage limit (`limit_exceeded`; see `setLimit`)
- 500Internal error (details are recorded only in server logs)
- 503Temporarily unable to complete the request; `error` tells the cases apart: `environment_not_placed` (not placed on a host within 15 seconds), `environment_not_running` (not up within 90 seconds), `host_unavailable` (host offline), `guest_unavailable` (the VM cannot be reached or disconnected midway), `exec_failed` (the process could not be started inside the VM), `exec_timeout` (a synchronous exec still had not finished after its timeout), `file_operation_failed` (a file operation hit an unexpected error).
Send a signal to a background process
POST/v1/environments/{id}/processes/{pid}/signal
Request body application/json · SignalRequest
| Field | Type | Description |
|---|---|---|
signalrequired | Signal |
Responses
- 204Sent
- 400Invalid parameters: validation failed, the body is not valid JSON, or the query string could not be parsed (all returned in the same JSON shape).
- 401Missing, invalid or revoked key
- 404Does not exist, or does not belong to this developer (`no such resource`); for a nonexistent route `message` is `no such route`
- 409The environment's actual state is failed (`environment_failed`; check its events), or it was suspended for exceeding a monthly usage limit (`limit_exceeded`; see `setLimit`)
- 415Missing `Content-Type: application/json` (`unsupported_media_type`)
- 422The JSON does not match the schema: a missing field, a wrong type or enum value, or an unknown field (`invalid_body`)
- 500Internal error (details are recorded only in server logs)
- 503Temporarily unable to complete the request; `error` tells the cases apart: `environment_not_placed` (not placed on a host within 15 seconds), `environment_not_running` (not up within 90 seconds), `host_unavailable` (host offline), `guest_unavailable` (the VM cannot be reached or disconnected midway), `exec_failed` (the process could not be started inside the VM), `exec_timeout` (a synchronous exec still had not finished after its timeout), `file_operation_failed` (a file operation hit an unexpected error).