Sign Up

Control

GET
/rlp/activity

Counts the daemon's non-terminal processes that hold the sandbox idle clock (PLAN-DAEMON-PROCESS-ACTIVITY): every non-PTY process a caller started — a standalone kind=exec process, a session member, or a kind=code cell, including a cell still queued in starting. PTYs, session shells, language kernels and system processes (the entrypoint, dockerd) never count, and neither do records reconciled lost at boot. The runner polls this route and keeps the sandbox from auto-stopping while busy is true. Read-only and immediate; it never waits.

Response Body

application/json
  1. response

GET /rlp/activity body: whether caller-started processes are running (PLAN-DAEMON-PROCESS-ACTIVITY). The runner polls it and keeps the sandbox's idle clock from expiring while busy is true.

busy*boolean

true when processes > 0.

processes*integer

The number of non-terminal processes that hold the idle clock: non-PTY processes a caller started (standalone kind=exec, session members, kind=code cells, including cells still queued in starting). PTYs, session and language kernels, and system processes (the entrypoint, dockerd) never count.

Formatint32
Range0 <= value
curl -X GET 'http://sandbox:2281/rlp/activity'
{  "busy": true,  "processes": 0}
POST
/rlp/entrypoint-start

Releases the runner-held entrypoint gate. Once released, the Linux daemon runs its one-per-boot startup coordinator: Docker Engine startup (reported on GET /docker/status), then the image entrypoint. fnReady=1 marks the release of a function-ready template build, which skips Docker startup (status deferred) but still starts the entrypoint. Idempotent — repeat calls answer 200 without spawning a second entrypoint. A platform without an image entrypoint (Windows) answers 200 as a no-op.

Query Parameters

fnReady?string

1 (or true) marks the release of a function-ready template build (the runner's template builder releases the gate before capture). The daemon then skips Docker startup and reports Docker as deferred; the image entrypoint still starts. Any other value, or omitted, is an ordinary release.

Response Body

Plain-text ok\n.

text/plain
curl -X POST 'http://sandbox:2281/rlp/entrypoint-start'
Empty
POST
/rlp/fs-freeze

Issues FIFREEZE on the guest's scratch ext4 filesystem — the mount whose mountinfo root is /runtime/containerd (or /runtime/docker), bind-mounted by guest init — which checkpoints the journal and blocks new writers, so the runner's host-side trim can trust the captured copy's block bitmaps. The runner freezes right before pausing the VM and thaws after resume. An auto-thaw fires timeoutMs (default 10000, clamped to 1000..=30000) after the request arrives, so a lost thaw cannot wedge the guest. Idempotent: a repeat call while frozen re-arms the auto-thaw and answers 200. While frozen, other requests that write to that filesystem block until the thaw.

Request Body

application/json
  1. body

POST /rlp/fs-freeze — freeze the guest's scratch filesystem around a disk snapshot (PLAN-DOCKER-AUTOMATIC-SUPPORT). FIFREEZE checkpoints the ext4 journal, so the host reads exact block bitmaps from the captured copy.

timeoutMs?|

Auto-thaw deadline in milliseconds, counted from when the daemon receives the request. Omitted means 10000; values are clamped to 1000..=30000. A repeat freeze while frozen re-arms the deadline.

Formatint32
Range0 <= value

Response Body

application/json
  1. response

POST /rlp/fs-freeze success body.

autoThawMs*integer

The effective auto-thaw window in milliseconds.

Formatint32
Range0 <= value
device*string

The frozen filesystem's device as major:minor (mountinfo field 3).

frozen*boolean

Always true on success.

mountpoint*string

The mount point the freeze was issued through (the scratch filesystem's /runtime/containerd or /runtime/docker bind mount).

curl -X POST 'http://sandbox:2281/rlp/fs-freeze' \  -H 'Content-Type: application/json' \  -d '{}'
{  "autoThawMs": 0,  "device": "string",  "frozen": true,  "mountpoint": "string"}
POST
/rlp/fs-thaw

Issues FITHAW on the filesystem POST /rlp/fs-freeze froze and cancels its auto-thaw. Idempotent: nothing frozen, or a filesystem already thawed, answers 200. Windows sandboxes answer 200 as a no-op.

Response Body

application/json
  1. response

POST /rlp/fs-thaw success body.

frozen*boolean

Always false on success.

curl -X POST 'http://sandbox:2281/rlp/fs-thaw'
{  "frozen": true}

List bucket mounts

GET
/rlp/mounts

Returns the daemon's redacted bucket mount state. Credentials never appear in this response.

Response Body

application/json
  1. response
[index: integer]?

Redacted mount status returned by GET /rlp/mounts.

curl -X GET 'http://sandbox:2281/rlp/mounts'
[  {    "bucket": "string",    "error": "string",    "helper": "string",    "mount_path": "string",    "name": "string",    "status": "string"  }]

Apply bucket mounts

POST
/rlp/mounts

Applies the desired bucket mount set. Gateway credentials are write-only and never echoed back.

Request Body

application/json
  1. body

POST /rlp/mounts — the full desired mount set for this sandbox.

mounts?array<>

The mounts the daemon should ensure are present.

Response Body

Plain-text ok\n.

text/plain
curl -X POST 'http://sandbox:2281/rlp/mounts' \  -H 'Content-Type: application/json' \  -d '{}'
Empty

Unmount one bucket mount

DELETE
/rlp/mounts

Unmounts one bucket mount by absolute path. The legacy name alias is still accepted for runner compatibility.

Query Parameters

path?string

Absolute mount path to unmount.

name?string

Legacy alias for the mount name.

Response Body

Plain-text ok\n.

text/plain
curl -X DELETE 'http://sandbox:2281/rlp/mounts'
Empty
POST
/rlp/secrets

Pushes placeholder environment variables plus an optional PEM CA bundle fragment before the runner releases the entrypoint gate. Reserved keys are refused with 400 naming the key, and nothing from that request is stored: keys starting with RLP_, any DAYTONA_* key other than DAYTONA_SANDBOX_TOKEN, DAYTONA_API_URL, DAYTONA_OTEL_ENDPOINT, DAYTONA_DOCKER_START, DAYTONA_FN_PORT and DAYTONA_FN_HANDLER (the last two are platform-injected by the API's functions service for the tenant's handler process), and keys that are not valid variable names (empty, or containing =, NUL, CR or LF). The sandbox's create env travels on this route too, so PATH, LD_* and the CA-bundle variables (SSL_CERT_FILE, REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE, NODE_EXTRA_CA_CERTS, GIT_SSL_CAINFO, DENO_CERT) are admitted; once an egress CA is installed, the daemon's own bundle paths replace any pushed value under the CA names. No size limit applies here: the API bounds the create env, and the tenant POST /env caps do not apply.

Request Body

application/json
  1. body

POST /rlp/secrets — placeholder env injection plus an optional egress CA.

ca_pem?string

PEM-encoded CA bundle fragment to trust for egress interception. Omitted or empty when no additional CA should be installed.

env?

Environment variables to inject into future child processes.

These values are placeholders (rlps_…), the sandbox's create env, or runner-delivered internal credentials, and are write-only on this surface. Keys starting with RLP_, any DAYTONA_* key other than DAYTONA_SANDBOX_TOKEN, DAYTONA_API_URL, DAYTONA_OTEL_ENDPOINT, DAYTONA_DOCKER_START, DAYTONA_FN_PORT and DAYTONA_FN_HANDLER (the last two are platform-injected by the API's functions service for the tenant's handler), and keys that are not valid variable names (empty, or containing =, NUL, CR or LF) are refused with 400 BAD_REQUEST naming the key; nothing from such a request is stored. The CA-bundle variables (SSL_CERT_FILE, REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE, NODE_EXTRA_CA_CERTS, GIT_SSL_CAINFO, DENO_CERT) may be pushed, but once an egress CA is installed the daemon's own bundle paths replace any pushed value.

Response Body

Plain-text ok\n.

text/plain
curl -X POST 'http://sandbox:2281/rlp/secrets' \  -H 'Content-Type: application/json' \  -d '{}'
Empty
POST
/rlp/self-delete

Arms one process-bound lifecycle action (delete, stop, or pause) for this sandbox.

Request Body

application/json
  1. body

POST /rlp/self-delete — arm one process-bound lifecycle action.

action?string

Lifecycle action to fire on exit: delete, stop, or pause. Empty means delete.

delay_secs?integer

Grace delay in seconds before firing the lifecycle action after the PID exits.

Formatint64
Range0 <= value
pid?integer

PID to watch. The request is rejected unless the process is currently alive.

Formatint32

Response Body

Plain-text ok\n.

text/plain
curl -X POST 'http://sandbox:2281/rlp/self-delete' \  -H 'Content-Type: application/json' \  -d '{}'
Empty
POST
/rlp/token

The runner's first control call on every boot path (PLAN-DAEMON-LISTENER-HARDENING). Accepted without a bearer only while the daemon holds no token (bootstrap); once a token is set, the request must carry it as Authorization: Bearer … and the body's token replaces it (rotation — a clone resumed from a template rotates the template's token to a per-clone one). Re-pushing the token the daemon already holds is a 200 no-op, so a retry after a lost 200 is idempotent. The token lives in daemon memory only and is never echoed, logged or exported. Like every control route this one sits behind the gateway-peer guard (403); unlike the others it bypasses the bearer layer and checks the bearer itself (401). A daemon that predates control tokens answers the plain-text 404 page not found fallback.

Request Body

application/json
  1. body

POST /rlp/token — set (bootstrap) or rotate the control bearer (PLAN-DAEMON-LISTENER-HARDENING, Layer 2).

The runner's first :2281 call on every boot path. Accepted without a bearer only while the daemon holds no token; once set, the request must carry the current token as Authorization: Bearer … and replaces it. Re-pushing the token the daemon already holds is a 200 no-op, which is what makes the runner's retry after a lost 200 idempotent.

token*string

32 to 128 bytes of printable ASCII (no whitespace); the runner sends 32 random bytes base64url-encoded (43 characters). Write-only: the daemon never echoes, logs or exports it.

Length32 <= length <= 128

Response Body

Plain-text ok\n; the token is now the bearer every other control route requires.

text/plain
curl -X POST 'http://sandbox:2281/rlp/token' \  -H 'Content-Type: application/json' \  -d '{  "token": "stringstringstringstringstringst"}'
Empty
POST
/rlp/user

Sets the OS user every later exec, session, SSH login and git operation runs as, creating the account inside the guest if the image lacks it (issue #167). Pushed by the runner before the entrypoint gate releases. One-shot per boot: re-pushing the same name is an idempotent no-op; a different name is refused.

Request Body

application/json
  1. body

POST /rlp/user — the sandbox default OS user (issue #167, PLAN-SANDBOX-OS-USER.md). Pushed by the runner before the entrypoint gate releases; the daemon creates the account if the image lacks it and runs every later exec/session/ssh/git as it. Idempotent.

user*string

POSIX user name ([a-z_][a-z0-9_.-]{0,31}). Required — an omitted name is a DTO shape error (422), not a validation 400. Validated by the daemon; a name that could smuggle a passwd field separator is refused.

Response Body

Plain-text ok\n.

text/plain
curl -X POST 'http://sandbox:2281/rlp/user' \  -H 'Content-Type: application/json' \  -d '{  "user": "string"}'
Empty
POST
/rlp/volume-apply/apply

Request Body

application/json
  1. body

Flat operation binding for GET status query and POST apply JSON.

generation*integer
Formatint64
Range0 <= value
operation_id*string
protocol*integer
Formatint32
Range0 <= value
request_id*string
reservation_id*string
session_id*string
volume_id*string

Response Body

application/json
  1. response
operation*

Flat operation binding for GET status query and POST apply JSON.

publication_blocked*boolean

Always true: this response never releases/implements the host fence.

state*VolumeApplyState

Local progress only, never full-tree verification or host durability evidence. In-flight requests can instead return a generic 409 (busy).

Value in"staging""applying""recovery_required""awaiting_host_confirmation""host_confirmed"
curl -X POST 'http://sandbox:2281/rlp/volume-apply/apply' \  -H 'Content-Type: application/json' \  -d '{  "generation": 0,  "operation_id": "string",  "protocol": 0,  "request_id": "string",  "reservation_id": "string",  "session_id": "string",  "volume_id": "string"}'
{  "operation": {    "generation": 0,    "operation_id": "string",    "protocol": 0,    "request_id": "string",    "reservation_id": "string",    "session_id": "string",    "volume_id": "string"  },  "publication_blocked": true,  "state": "staging"}
POST
/rlp/volume-apply/begin

Request Body

application/json
  1. body
binding*

Internal correlation only, never credentials or ownership evidence. A trusted runner identity/transport and a host-owned publication fence are prerequisites. The runner must derive this binding from its own attachment records, never adopt a binding reported by a guest. Only control port 2281 serves this DTO.

grant*
instructions*array<>

At most 256; exact duplicate begin returns state, never reruns effects. The trusted runner must persist the grant, block all host publication (including automatic/final commits) and prevent concurrent guest writes, renames and mount changes before beginning. Guest stat guards do not enforce these prerequisites.

receipt_ids*

Response Body

application/json
  1. response
operation*

Flat operation binding for GET status query and POST apply JSON.

publication_blocked*boolean

Always true: this response never releases/implements the host fence.

state*VolumeApplyState

Local progress only, never full-tree verification or host durability evidence. In-flight requests can instead return a generic 409 (busy).

Value in"staging""applying""recovery_required""awaiting_host_confirmation""host_confirmed"
curl -X POST 'http://sandbox:2281/rlp/volume-apply/begin' \  -H 'Content-Type: application/json' \  -d '{  "binding": {    "generation": 1,    "protocol": 0,    "reservation_id": "string",    "session_id": "string",    "volume_id": "string"  },  "grant": {    "operation_id": "string",    "request_id": "string",    "target_manifest": "string"  },  "instructions": [    {      "change": {        "digest_hex": "string",        "length": 0,        "metadata": {          "mode": 0,          "mtime_nanos": 0,          "mtime_seconds": 0        },        "type": "replace"      },      "path": "string"    }  ],  "receipt_ids": {    "baseline_id": "string",    "receipt_id": "string"  }}'
{  "operation": {    "generation": 0,    "operation_id": "string",    "protocol": 0,    "request_id": "string",    "reservation_id": "string",    "session_id": "string",    "volume_id": "string"  },  "publication_blocked": true,  "state": "staging"}
POST
/rlp/volume-apply/capture

Request Body

application/json
  1. body
binding*

Internal correlation only, never credentials or ownership evidence. A trusted runner identity/transport and a host-owned publication fence are prerequisites. The runner must derive this binding from its own attachment records, never adopt a binding reported by a guest. Only control port 2281 serves this DTO.

paths*array<string>

At most 256 relative paths. Missing paths and descendants of a missing or non-directory ancestor return null without following symlink ancestry. "." captures the pinned root after reserve; this is a stat guard, not a base/clean-tree proof. Other dot components remain forbidden.

Response Body

application/json
  1. response
binding*

Internal correlation only, never credentials or ownership evidence. A trusted runner identity/transport and a host-owned publication fence are prerequisites. The runner must derive this binding from its own attachment records, never adopt a binding reported by a guest. Only control port 2281 serves this DTO.

paths*array<>
curl -X POST 'http://sandbox:2281/rlp/volume-apply/capture' \  -H 'Content-Type: application/json' \  -d '{  "binding": {    "generation": 1,    "protocol": 0,    "reservation_id": "string",    "session_id": "string",    "volume_id": "string"  },  "paths": [    "string"  ]}'
{  "binding": {    "generation": 1,    "protocol": 0,    "reservation_id": "string",    "session_id": "string",    "volume_id": "string"  },  "paths": [    {      "path": "string",      "snapshot": null    }  ]}
POST
/rlp/volume-apply/confirm

Request Body

application/json
  1. body

Trusted runner assertion AFTER full-tree verification and durable host base/baseline + receipt persistence. Guest recording is not proof of either.

baseline_digest_hex*string

Post-verification record digests, each exactly 64 lowercase hex.

binding*

Internal correlation only, never credentials or ownership evidence. A trusted runner identity/transport and a host-owned publication fence are prerequisites. The runner must derive this binding from its own attachment records, never adopt a binding reported by a guest. Only control port 2281 serves this DTO.

grant*
receipt_digest_hex*string
receipt_ids*

Response Body

application/json
  1. response
confirmation*

Immutable acknowledgement of local recording; exact retries remain stable even after a new operation starts. Not verification evidence.

curl -X POST 'http://sandbox:2281/rlp/volume-apply/confirm' \  -H 'Content-Type: application/json' \  -d '{  "baseline_digest_hex": "string",  "binding": {    "generation": 1,    "protocol": 0,    "reservation_id": "string",    "session_id": "string",    "volume_id": "string"  },  "grant": {    "operation_id": "string",    "request_id": "string",    "target_manifest": "string"  },  "receipt_digest_hex": "string",  "receipt_ids": {    "baseline_id": "string",    "receipt_id": "string"  }}'
{  "confirmation": {    "baseline_digest_hex": "string",    "binding": {      "generation": 1,      "protocol": 0,      "reservation_id": "string",      "session_id": "string",      "volume_id": "string"    },    "grant": {      "operation_id": "string",      "request_id": "string",      "source_manifest": "string",      "target_manifest": "string"    },    "receipt_digest_hex": "string",    "receipt_ids": {      "baseline_id": "string",      "receipt_id": "string"    }  }}
POST
/rlp/volume-apply/reserve

Request Body

application/json
  1. body
binding*

Internal correlation only, never credentials or ownership evidence. A trusted runner identity/transport and a host-owned publication fence are prerequisites. The runner must derive this binding from its own attachment records, never adopt a binding reported by a guest. Only control port 2281 serves this DTO.

mount_path*string

Trusted runner input only: canonical absolute non-root directory, no symlink components. Mount crossings allowed TO this root, never BELOW it. Pinned once; duplicate reserve must match exactly. No adoption or reopen.

Response Body

application/json
  1. response

Internal correlation only, never credentials or ownership evidence. A trusted runner identity/transport and a host-owned publication fence are prerequisites. The runner must derive this binding from its own attachment records, never adopt a binding reported by a guest. Only control port 2281 serves this DTO.

generation*integer

Positive, matched against the current broker registration atomically.

Formatint64
Range1 <= value
protocol*integer
Formatint32
Range0 <= value
reservation_id*string
session_id*string

Every ID is 1..128 ASCII alphanumeric, underscore or hyphen.

volume_id*string
curl -X POST 'http://sandbox:2281/rlp/volume-apply/reserve' \  -H 'Content-Type: application/json' \  -d '{  "binding": {    "generation": 1,    "protocol": 0,    "reservation_id": "string",    "session_id": "string",    "volume_id": "string"  },  "mount_path": "string"}'
{  "generation": 1,  "protocol": 0,  "reservation_id": "string",  "session_id": "string",  "volume_id": "string"}
POST
/rlp/volume-apply/stage

Query Parameters

protocol*integer
Formatint32
Range0 <= value
session_id*string
volume_id*string
generation*integer
Formatint64
Range0 <= value
reservation_id*string
operation_id*string
request_id*string
index*integer
Range0 <= value

Request Body

body*stringapplication/octet-stream

Response Body

application/json
  1. response
operation*

Flat operation binding for GET status query and POST apply JSON.

publication_blocked*boolean

Always true: this response never releases/implements the host fence.

state*VolumeApplyState

Local progress only, never full-tree verification or host durability evidence. In-flight requests can instead return a generic 409 (busy).

Value in"staging""applying""recovery_required""awaiting_host_confirmation""host_confirmed"
curl -X POST 'http://sandbox:2281/rlp/volume-apply/stage?protocol=0&session_id=string&volume_id=string&generation=0&reservation_id=string&operation_id=string&request_id=string&index=0' \  -H 'Content-Type: application/octet-stream' \  -d 'string'
{  "operation": {    "generation": 0,    "operation_id": "string",    "protocol": 0,    "request_id": "string",    "reservation_id": "string",    "session_id": "string",    "volume_id": "string"  },  "publication_blocked": true,  "state": "staging"}
GET
/rlp/volume-apply/status

Query Parameters

protocol*integer
Formatint32
Range0 <= value
session_id*string
volume_id*string
generation*integer
Formatint64
Range0 <= value
reservation_id*string
operation_id*string
request_id*string

Response Body

application/json
  1. response
operation*

Flat operation binding for GET status query and POST apply JSON.

publication_blocked*boolean

Always true: this response never releases/implements the host fence.

state*VolumeApplyState

Local progress only, never full-tree verification or host durability evidence. In-flight requests can instead return a generic 409 (busy).

Value in"staging""applying""recovery_required""awaiting_host_confirmation""host_confirmed"
curl -X GET 'http://sandbox:2281/rlp/volume-apply/status?protocol=0&session_id=string&volume_id=string&generation=0&reservation_id=string&operation_id=string&request_id=string'
{  "operation": {    "generation": 0,    "operation_id": "string",    "protocol": 0,    "request_id": "string",    "reservation_id": "string",    "session_id": "string",    "volume_id": "string"  },  "publication_blocked": true,  "state": "staging"}
POST
/rlp/volume-sync/register

Protocol 1 runner-only correlation state, not a credential or proof of ownership. The first session is sealed for the daemon's lifetime: a different session is refused with 409; repeating the session requires identical bindings.

Request Body

application/json
  1. body
attachments*array<>

At most 64 distinct volume bindings. A repeat registration must carry the sealed session's exact bindings.

protocol*integer
Formatint32
Range0 <= value
session_id*string

One session per daemon lifetime: the first is sealed and a different id is refused.

Response Body

application/json
  1. response
protocol*integer
Formatint32
Range0 <= value
session_id*string
curl -X POST 'http://sandbox:2281/rlp/volume-sync/register' \  -H 'Content-Type: application/json' \  -d '{  "attachments": [    {      "generation": 0,      "volume_id": "string"    }  ],  "protocol": 0,  "session_id": "string"}'
{  "protocol": 0,  "session_id": "string"}
GET
/rlp/volume-sync/requests

Returns the oldest outstanding delivery without removing it. Waits at most 30000 ms. A missing request is null, not proof of execution failure.

Query Parameters

session_id*string
wait_ms?integer

Long-poll duration in milliseconds, from 0 to 30000.

Formatint64
Range0 <= value

Response Body

application/json
  1. response
protocol*integer
Formatint32
Range0 <= value
request?null|
session_id*string
curl -X GET 'http://sandbox:2281/rlp/volume-sync/requests?session_id=string'
{  "protocol": 0,  "request": null,  "session_id": "string"}
POST
/rlp/volume-sync/results

Requires the submitted session, request, volume, generation, operation and delivery id. Identical completions replay; contradictory results refuse. Pending, unavailable and rate_limited finish this delivery but allow internal resubmission with the same effect identity and a new delivery id. Rate admission is enforced by the runner across reconnects.

Request Body

application/json
  1. body
delivery_id*integer

Echo the exact polled delivery counter, including on transport retries.

Formatint64
Range0 <= value
generation*integer
Formatint64
Range0 <= value
manifest_id?|

Pulled requires exactly 64 lowercase hex digits. Unchanged permits a valid manifest or null and describes remote observation, not local writes. Every other status carries null.

message*string

Bounded to 1024 UTF-8 bytes. Must not contain provider credentials.

operation*VolumeSyncOperation
Value in"probe""pull"
protocol*integer
Formatint32
Range0 <= value
request_id*string
retry_after_ms?|

Only rate_limited carries a delay, from 1 to 1000 ms. Otherwise null. The runner enforces the per-attachment rate limit across reconnects.

Formatint64
Range0 <= value
session_id*string
status*VolumeSyncResultStatus
Value in"probed""pulled""unchanged""failed""pending""unavailable""rejected""rate_limited"
volume_id*string

Response Body

application/json
  1. response
protocol*integer
Formatint32
Range0 <= value
session_id*string
curl -X POST 'http://sandbox:2281/rlp/volume-sync/results' \  -H 'Content-Type: application/json' \  -d '{  "delivery_id": 0,  "generation": 0,  "message": "string",  "operation": "probe",  "protocol": 0,  "request_id": "string",  "session_id": "string",  "status": "probed",  "volume_id": "string"}'
{  "protocol": 0,  "session_id": "string"}