Files
python-sdk/api/openapi.yaml
pptx704 f3fb5a59c4
All checks were successful
ci/woodpecker/push/unit Pipeline was successful
fix: send and receive PTY data over binary frames
PTY input and output now travel as raw WebSocket binary frames to
match the updated server protocol. Control messages stay as JSON.
2026-06-23 04:35:33 +06:00

1722 lines
46 KiB
YAML

openapi: 3.1.0
info:
title: Wrenn API
description: API for Wrenn — the runtime where AI coding agents live, work, and ship.
version: 0.2.2
servers:
- url: http://localhost:8080
description: Local development
security: []
paths:
/v1/capsules:
post:
summary: Create a capsule
operationId: createCapsule
tags:
- capsules
security:
- apiKeyAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateCapsuleRequest'
responses:
'202':
description: Capsule creation initiated (status will be "starting")
content:
application/json:
schema:
$ref: '#/components/schemas/Capsule'
'400':
$ref: '#/components/responses/BadRequest'
'403':
$ref: '#/components/responses/Forbidden'
'409':
$ref: '#/components/responses/FailedPrecondition'
'502':
description: Host agent error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
$ref: '#/components/responses/ServiceUnavailable'
get:
summary: List capsules for your team
operationId: listCapsules
tags:
- capsules
security:
- apiKeyAuth: []
responses:
'200':
description: List of capsules
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Capsule'
/v1/capsules/stats:
get:
summary: Get capsule usage stats for your team
operationId: getCapsuleStats
tags:
- capsules
security:
- apiKeyAuth: []
parameters:
- name: range
in: query
required: false
schema:
type: string
enum:
- 5m
- 1h
- 6h
- 24h
- 30d
default: 1h
description: Time window for the time-series data.
responses:
'200':
description: Capsule stats for the team
content:
application/json:
schema:
$ref: '#/components/schemas/CapsuleStats'
'400':
$ref: '#/components/responses/BadRequest'
/v1/capsules/usage:
get:
summary: Get daily CPU and RAM usage for your team
operationId: getCapsuleUsage
tags:
- capsules
security:
- apiKeyAuth: []
parameters:
- name: from
in: query
required: false
schema:
type: string
format: date
description: Start date (YYYY-MM-DD). Defaults to 30 days ago.
- name: to
in: query
required: false
schema:
type: string
format: date
description: End date (YYYY-MM-DD). Defaults to today.
responses:
'200':
description: Daily usage data for the team
content:
application/json:
schema:
$ref: '#/components/schemas/UsageResponse'
'400':
$ref: '#/components/responses/BadRequest'
/v1/capsules/{id}:
parameters:
- name: id
in: path
required: true
schema:
type: string
get:
summary: Get capsule details
operationId: getCapsule
tags:
- capsules
security:
- apiKeyAuth: []
responses:
'200':
description: Capsule details
content:
application/json:
schema:
$ref: '#/components/schemas/Capsule'
'400':
$ref: '#/components/responses/BadRequest'
'404':
description: Capsule not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
delete:
summary: Destroy a capsule
operationId: destroyCapsule
tags:
- capsules
security:
- apiKeyAuth: []
responses:
'202':
description: Capsule destruction initiated
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/FailedPrecondition'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/v1/capsules/{id}/exec:
parameters:
- name: id
in: path
required: true
schema:
type: string
post:
summary: Execute a command
operationId: execCommand
tags:
- capsules
security:
- apiKeyAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ExecRequest'
responses:
'200':
description: Command output (foreground exec)
content:
application/json:
schema:
$ref: '#/components/schemas/ExecResponse'
'202':
description: Background process started
content:
application/json:
schema:
$ref: '#/components/schemas/BackgroundExecResponse'
'400':
$ref: '#/components/responses/BadRequest'
'404':
description: Capsule not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Capsule not running
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/v1/capsules/{id}/processes:
parameters:
- name: id
in: path
required: true
schema:
type: string
get:
summary: List running processes
operationId: listProcesses
tags:
- capsules
security:
- apiKeyAuth: []
description: 'Returns all running processes inside the capsule, including background
processes and any processes started by templates or init scripts.
'
responses:
'200':
description: Process list
content:
application/json:
schema:
$ref: '#/components/schemas/ProcessListResponse'
'400':
$ref: '#/components/responses/BadRequest'
'404':
description: Capsule not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Capsule not running
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/v1/capsules/{id}/processes/{selector}:
parameters:
- name: id
in: path
required: true
schema:
type: string
- name: selector
in: path
required: true
description: Process PID (numeric) or tag (string)
schema:
type: string
delete:
summary: Kill a process
operationId: killProcess
tags:
- capsules
security:
- apiKeyAuth: []
parameters:
- name: signal
in: query
required: false
description: Signal to send (SIGKILL or SIGTERM, default SIGKILL)
schema:
type: string
enum:
- SIGKILL
- SIGTERM
default: SIGKILL
responses:
'204':
description: Process killed
'400':
$ref: '#/components/responses/BadRequest'
'404':
description: Capsule or process not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Capsule not running
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/v1/capsules/{id}/processes/{selector}/stream:
parameters:
- name: id
in: path
required: true
schema:
type: string
- name: selector
in: path
required: true
description: Process PID (numeric) or tag (string)
schema:
type: string
get:
summary: Stream process output via WebSocket
operationId: connectProcess
tags:
- capsules
security:
- apiKeyAuth: []
description: 'Opens a WebSocket connection to stream stdout/stderr from a running
background process. The selector can be a numeric PID or a string tag.
Server sends JSON messages:
- `{"type": "start", "pid": 42}` — connected to process
- `{"type": "stdout", "data": "..."}` — stdout output
- `{"type": "stderr", "data": "..."}` — stderr output
- `{"type": "exit", "exit_code": 0}` — process exited
- `{"type": "error", "data": "..."}` — error message
'
responses:
'101':
description: WebSocket upgrade
/v1/capsules/{id}/ping:
parameters:
- name: id
in: path
required: true
schema:
type: string
post:
summary: Reset capsule inactivity timer
operationId: pingCapsule
tags:
- capsules
security:
- apiKeyAuth: []
description: 'Resets the last_active_at timestamp for a running capsule, preventing
the auto-pause TTL from expiring. Use this as a keepalive for capsules
that are idle but should remain running.
'
responses:
'204':
description: Ping acknowledged, inactivity timer reset
'400':
$ref: '#/components/responses/BadRequest'
'404':
description: Capsule not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Capsule not running
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/v1/capsules/{id}/metrics:
parameters:
- name: id
in: path
required: true
schema:
type: string
get:
summary: Get per-capsule resource metrics
operationId: getCapsuleMetrics
tags:
- capsules
security:
- apiKeyAuth: []
description: 'Returns time-series CPU, memory, and disk metrics for a capsule.
Three tiers are available with different granularity and retention:
- `10m`: 500ms samples, last 10 minutes
- `2h`: 30-second averages, last 2 hours
- `24h`: 5-minute averages, last 24 hours
For running capsules, data comes from the host agent''s in-memory
ring buffer. For paused capsules, data is read from persisted
snapshots in the database. Stopped/destroyed capsules return 404.
'
parameters:
- name: range
in: query
required: false
schema:
type: string
enum:
- 5m
- 10m
- 1h
- 2h
- 6h
- 12h
- 24h
default: 10m
description: Time range filter to query
responses:
'200':
description: Metrics retrieved
content:
application/json:
schema:
$ref: '#/components/schemas/CapsuleMetrics'
'400':
description: Invalid range parameter
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Capsule not found or metrics not available
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/v1/capsules/{id}/pause:
parameters:
- name: id
in: path
required: true
schema:
type: string
post:
summary: Pause a running capsule
operationId: pauseCapsule
tags:
- capsules
security:
- apiKeyAuth: []
description: 'Takes a snapshot of the capsule (VM state + memory + rootfs), then
destroys all running resources. The capsule exists only as files on
disk and can be resumed later.
'
responses:
'202':
description: Capsule pause initiated (status will be "pausing")
content:
application/json:
schema:
$ref: '#/components/schemas/Capsule'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'409':
description: Capsule not running
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/v1/capsules/{id}/resume:
parameters:
- name: id
in: path
required: true
schema:
type: string
post:
summary: Resume a paused capsule
operationId: resumeCapsule
tags:
- capsules
security:
- apiKeyAuth: []
description: 'Restores a paused capsule from its snapshot. Cloud Hypervisor is
relaunched in --restore mode with memory_restore_mode=ondemand so
guest pages fault in lazily via userfaultfd. The original network
slot (and host-reachable IP) is preserved across pause/resume.
'
responses:
'202':
description: Capsule resume initiated (status will be "resuming")
content:
application/json:
schema:
$ref: '#/components/schemas/Capsule'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'409':
description: Capsule not paused
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/v1/snapshots:
post:
summary: Create a snapshot template
operationId: createSnapshot
tags:
- snapshots
security:
- apiKeyAuth: []
description: 'Snapshot a capsule, processed asynchronously. The call returns
immediately with the capsule in the `snapshotting` state, then it
returns to its original state on completion. The capsule must be
`running` or `paused`.
A `running` capsule is snapshotted live: it briefly pauses while its VM
state + memory + flattened rootfs are written to a new template, then
resumes to `running`. A `paused` capsule is snapshotted directly from
its on-disk state without reviving the VM, and stays `paused`.
Because it is async, the response does NOT contain the template. Watch
for the `template.snapshot.create` SSE event (its `outcome` reports
success or failure) or poll `GET /v1/snapshots` to observe completion.
Snapshots are immutable: each call must use a fresh name. Re-using
an existing name returns 409 Conflict.
'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateSnapshotRequest'
responses:
'202':
description: Snapshot accepted; capsule is now snapshotting
content:
application/json:
schema:
$ref: '#/components/schemas/Capsule'
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'409':
description: Name already exists, or capsule is not running or paused
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
get:
summary: List templates for your team
operationId: listSnapshots
tags:
- snapshots
security:
- apiKeyAuth: []
parameters:
- name: type
in: query
required: false
schema:
type: string
enum:
- base
- snapshot
description: Filter by template type.
responses:
'200':
description: List of templates
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Template'
/v1/snapshots/{name}:
parameters:
- name: name
in: path
required: true
schema:
type: string
delete:
summary: Delete a snapshot template
operationId: deleteSnapshot
tags:
- snapshots
security:
- apiKeyAuth: []
description: Removes the snapshot files from disk and deletes the database record.
responses:
'204':
description: Snapshot deleted
'400':
$ref: '#/components/responses/BadRequest'
'403':
description: Cannot delete a platform-owned / system base template
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Template not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Delete failed (e.g. owning host offline)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/v1/capsules/{id}/files/write:
parameters:
- name: id
in: path
required: true
schema:
type: string
post:
summary: Upload a file
operationId: uploadFile
tags:
- capsules
security:
- apiKeyAuth: []
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required:
- path
- file
properties:
path:
type: string
description: Absolute destination path inside the capsule
file:
type: string
format: binary
description: File content
responses:
'204':
description: File uploaded
'400':
$ref: '#/components/responses/BadRequest'
'404':
$ref: '#/components/responses/NotFound'
'409':
description: Capsule not running
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'413':
description: File too large
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/v1/capsules/{id}/files/read:
parameters:
- name: id
in: path
required: true
schema:
type: string
post:
summary: Download a file
operationId: downloadFile
tags:
- capsules
security:
- apiKeyAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ReadFileRequest'
responses:
'200':
description: File content
content:
application/octet-stream:
schema:
type: string
format: binary
'400':
$ref: '#/components/responses/BadRequest'
'404':
description: Capsule or file not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Capsule not running
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/v1/capsules/{id}/files/list:
parameters:
- name: id
in: path
required: true
schema:
type: string
post:
summary: List directory contents
operationId: listDir
tags:
- capsules
security:
- apiKeyAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ListDirRequest'
responses:
'200':
description: Directory listing
content:
application/json:
schema:
$ref: '#/components/schemas/ListDirResponse'
'400':
$ref: '#/components/responses/BadRequest'
'404':
description: Capsule not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Capsule not running
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/v1/capsules/{id}/files/mkdir:
parameters:
- name: id
in: path
required: true
schema:
type: string
post:
summary: Create a directory
operationId: makeDir
tags:
- capsules
security:
- apiKeyAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/MakeDirRequest'
responses:
'200':
description: Directory created
content:
application/json:
schema:
$ref: '#/components/schemas/MakeDirResponse'
'400':
$ref: '#/components/responses/BadRequest'
'404':
description: Capsule not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: 'Capsule not running, or a directory already exists at the target path (error code `already_exists`).
'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/v1/capsules/{id}/files/remove:
parameters:
- name: id
in: path
required: true
schema:
type: string
post:
summary: Remove a file or directory
operationId: removePath
tags:
- capsules
security:
- apiKeyAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RemoveRequest'
responses:
'204':
description: File or directory removed
'400':
$ref: '#/components/responses/BadRequest'
'404':
description: Capsule not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Capsule not running
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/v1/capsules/{id}/exec/stream:
parameters:
- name: id
in: path
required: true
schema:
type: string
get:
summary: Stream command execution via WebSocket
operationId: execStream
tags:
- capsules
security:
- apiKeyAuth: []
description: 'Opens a WebSocket connection for streaming command execution.
**Client sends** (first message to start the process):
```json
{"type": "start", "cmd": "tail", "args": ["-f", "/var/log/syslog"]}
```
**Client sends** (to stop the process):
```json
{"type": "stop"}
```
**Server sends** (process events as they arrive):
```json
{"type": "start", "pid": 1234}
{"type": "stdout", "data": "line of output\n"}
{"type": "stderr", "data": "warning message\n"}
{"type": "exit", "exit_code": 0}
{"type": "error", "data": "description of error"}
```
The connection closes automatically after the process exits.
Note: capsule-not-found and not-running conditions encountered after the
WebSocket upgrade are delivered as in-band `{"type": "error"}` frames
rather than HTTP status codes.
'
responses:
'101':
description: WebSocket upgrade
'400':
$ref: '#/components/responses/BadRequest'
'404':
description: Capsule not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Capsule not running
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/v1/capsules/{id}/pty:
parameters:
- name: id
in: path
required: true
schema:
type: string
get:
summary: Interactive PTY session via WebSocket
operationId: ptySession
tags:
- capsules
security:
- apiKeyAuth: []
description: "Opens a WebSocket connection for an interactive PTY (terminal) session.\nSupports creating new sessions,\
\ sending input, resizing, killing, and\nreconnecting to existing sessions.\n\n**Client sends** (first message — start\
\ a new PTY):\n```json\n{\n \"type\": \"start\",\n \"cmd\": \"/bin/bash\",\n \"args\": [],\n \"cols\": 80,\n \
\ \"rows\": 24,\n \"envs\": {\"TERM\": \"xterm-256color\"},\n \"cwd\": \"/home/user\",\n \"user\": \"user\"\n}\n\
```\nAll fields except `type` are optional. Defaults: cmd=\"/bin/bash\", cols=80, rows=24.\n\n**Client sends** (first\
\ message — reconnect to existing PTY):\n```json\n{\"type\": \"connect\", \"tag\": \"pty-abc123de\"}\n```\n\n**Client\
\ sends** (control messages, after session is established — JSON text frames):\n```json\n{\"type\": \"resize\", \"\
cols\": 120, \"rows\": 40}\n{\"type\": \"kill\"}\n```\n\nKeystroke input is sent as **raw binary WebSocket frames**\
\ containing the\nterminal bytes verbatim — not JSON, not base64. Every binary frame from\nthe client is treated as\
\ PTY input.\n\n**Server sends** (control messages — JSON text frames):\n```json\n{\"type\": \"started\", \"tag\"\
: \"pty-abc123de\", \"pid\": 42}\n{\"type\": \"exit\", \"exit_code\": 0}\n{\"type\": \"error\", \"data\": \"description\"\
, \"fatal\": true}\n{\"type\": \"ping\"}\n```\n\nPTY output is sent as **raw binary WebSocket frames** containing\
\ the\nterminal bytes verbatim — not JSON, not base64. Sending input and output\nas binary avoids the ~33% inflation\
\ of base64 over the JSON path.\n\nThe connection negotiates permessage-deflate (RFC 7692) compression\nautomatically;\
\ rapid output (e.g. full-screen TUI repaints) is coalesced\ninto fewer, larger frames before compression.\n\nSessions\
\ persist across WebSocket disconnections — the process keeps\nrunning in the capsule. Use the `tag` from the \"started\"\
\ response to\nreconnect later.\n"
responses:
'101':
description: WebSocket upgrade
'400':
$ref: '#/components/responses/BadRequest'
'404':
description: Capsule not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Capsule not running
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/v1/capsules/{id}/files/stream/write:
parameters:
- name: id
in: path
required: true
schema:
type: string
post:
summary: Upload a file (streaming)
operationId: streamUploadFile
tags:
- capsules
security:
- apiKeyAuth: []
description: 'Streams file content to the capsule without buffering in memory.
Suitable for large files. Uses the same multipart/form-data format
as the non-streaming upload endpoint.
'
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required:
- path
- file
properties:
path:
type: string
description: Absolute destination path inside the capsule
file:
type: string
format: binary
description: File content
responses:
'204':
description: File uploaded
'400':
$ref: '#/components/responses/BadRequest'
'404':
description: Capsule not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Capsule not running
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'502':
description: Host agent error while streaming the upload
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/v1/capsules/{id}/files/stream/read:
parameters:
- name: id
in: path
required: true
schema:
type: string
post:
summary: Download a file (streaming)
operationId: streamDownloadFile
tags:
- capsules
security:
- apiKeyAuth: []
description: 'Streams file content from the capsule without buffering in memory.
Suitable for large files. Returns raw bytes with chunked transfer encoding.
'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ReadFileRequest'
responses:
'200':
description: File content streamed in chunks
content:
application/octet-stream:
schema:
type: string
format: binary
'400':
$ref: '#/components/responses/BadRequest'
'404':
description: Capsule or file not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Capsule not running
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/v1/events/stream:
get:
summary: Real-time lifecycle event stream
operationId: streamEvents
tags:
- events
description: 'Server-Sent Events stream of capsule, template, and host lifecycle
events scoped to the caller''s active team. Browsers send the
wrenn_sid cookie automatically on EventSource connections; SDKs
authenticate via X-API-Key.
Frame format follows the standard SSE protocol:
```
event: capsule.create
data: {"event":"capsule.create","outcome":"success","resource":{"id":"sb-..."},"sandbox":{...},"timestamp":"2026-05-19T02:00:00Z"}
: keepalive
```
A `: keepalive` comment is emitted every 30s.
'
security:
- apiKeyAuth: []
responses:
'200':
description: SSE stream opened
content:
text/event-stream:
schema:
$ref: '#/components/schemas/SSEEvent'
'401':
description: Missing or invalid auth
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
responses:
BadRequest:
description: Invalid request parameters
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Forbidden:
description: Authenticated but not permitted (e.g. non-admin on /v1/admin/*)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
NotFound:
description: Resource not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
FailedPrecondition:
description: Resource state does not allow this operation (e.g. exec on a paused capsule)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
ServiceUnavailable:
description: The host serving this capsule is unreachable (host_unavailable)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
securitySchemes:
apiKeyAuth:
type: apiKey
in: header
name: X-API-Key
description: API key for capsule lifecycle operations. Create via POST /v1/api-keys.
schemas:
CreateCapsuleRequest:
type: object
properties:
template:
type: string
default: minimal-ubuntu
vcpus:
type: integer
default: 1
memory_mb:
type: integer
default: 512
timeout_sec:
type: integer
minimum: 0
default: 0
description: 'Auto-pause TTL in seconds. The capsule is automatically paused after this duration of inactivity (no
exec or ping). 0 means no auto-pause. Positive values below 60 are silently clamped to 60 (the agent''s startup
envelope).
'
metadata:
type: object
additionalProperties:
type: string
nullable: true
description: 'Optional user-supplied key/value labels attached to the capsule. Max 20 keys; keys match [a-zA-Z0-9][a-zA-Z0-9._-]{0,63};
values are at most 64 characters. Reserved system keys (kernel_version, vmm_version, agent_version, envd_version)
are rejected. Metadata is set at create-time only.
'
UsageResponse:
type: object
properties:
from:
type: string
format: date
to:
type: string
format: date
points:
type: array
items:
type: object
properties:
date:
type: string
format: date
cpu_minutes:
type: number
ram_mb_minutes:
type: number
CapsuleStats:
type: object
properties:
range:
type: string
enum:
- 5m
- 1h
- 6h
- 24h
- 30d
current:
type: object
properties:
running_count:
type: integer
vcpus_reserved:
type: integer
memory_mb_reserved:
type: integer
peaks:
type: object
description: Maximum values over the last 30 days.
properties:
running_count:
type: integer
vcpus:
type: integer
memory_mb:
type: integer
series:
type: object
description: Parallel arrays for chart rendering.
properties:
labels:
type: array
items:
type: string
format: date-time
running:
type: array
items:
type: integer
vcpus:
type: array
items:
type: integer
memory_mb:
type: array
items:
type: integer
Capsule:
type: object
properties:
id:
type: string
status:
type: string
enum:
- pending
- starting
- running
- pausing
- paused
- snapshotting
- resuming
- stopping
- hibernated
- stopped
- missing
- error
template:
type: string
vcpus:
type: integer
memory_mb:
type: integer
timeout_sec:
type: integer
created_at:
type: string
format: date-time
started_at:
type: string
format: date-time
nullable: true
last_active_at:
type: string
format: date-time
nullable: true
last_updated:
type: string
format: date-time
metadata:
type: object
additionalProperties:
type: string
nullable: true
description: 'User-supplied key/value labels attached at create-time. Once the
capsule is running this also carries agent-side system version info
(kernel_version, vmm_version, agent_version, envd_version); those
reserved keys are owned by the system and cannot be set by users.
'
disk_size_mb:
type: integer
description: Maximum disk capacity in MiB.
disk_used_mb:
type: integer
format: int64
description: Current disk usage in MiB. Only populated on individual capsule GET; omitted in list responses.
CreateSnapshotRequest:
type: object
required:
- sandbox_id
properties:
sandbox_id:
type: string
description: ID of the running capsule to snapshot.
name:
type: string
description: Name for the snapshot template. Auto-generated if omitted.
Template:
type: object
properties:
name:
type: string
type:
type: string
enum:
- base
- snapshot
vcpus:
type: integer
nullable: true
memory_mb:
type: integer
nullable: true
size_bytes:
type: integer
format: int64
created_at:
type: string
format: date-time
platform:
type: boolean
description: 'True when the template is platform-managed (visible to all teams,
e.g. the built-in `minimal-ubuntu` rootfs). False for team-owned
snapshot templates.
'
protected:
type: boolean
description: 'True for built-in system base templates (minimal-ubuntu,
minimal-alpine, minimal-arch, minimal-fedora). Protected templates
cannot be deleted.
'
metadata:
type: object
additionalProperties:
type: string
nullable: true
ExecRequest:
type: object
required:
- cmd
properties:
cmd:
type: string
args:
type: array
items:
type: string
timeout_sec:
type: integer
default: 30
description: Timeout in seconds (foreground exec only, default 30)
background:
type: boolean
default: false
description: If true, starts the process in the background and returns immediately with a PID and tag (HTTP 202)
tag:
type: string
description: Optional user-chosen tag for the background process. Auto-generated if omitted. Only used when background
is true.
envs:
type: object
additionalProperties:
type: string
description: Environment variables for the process (applies to both foreground and background exec)
cwd:
type: string
description: Working directory for the process (applies to both foreground and background exec)
BackgroundExecResponse:
type: object
properties:
sandbox_id:
type: string
cmd:
type: string
pid:
type: integer
tag:
type: string
ProcessEntry:
type: object
properties:
pid:
type: integer
tag:
type: string
cmd:
type: string
args:
type: array
items:
type: string
ProcessListResponse:
type: object
properties:
processes:
type: array
items:
$ref: '#/components/schemas/ProcessEntry'
ExecResponse:
type: object
properties:
sandbox_id:
type: string
cmd:
type: string
stdout:
type: string
stderr:
type: string
exit_code:
type: integer
duration_ms:
type: integer
encoding:
type: string
enum:
- utf-8
- base64
description: Output encoding. "base64" when stdout/stderr contain binary data.
ReadFileRequest:
type: object
required:
- path
properties:
path:
type: string
description: Absolute file path inside the capsule
ListDirRequest:
type: object
required:
- path
properties:
path:
type: string
description: Directory path inside the capsule
depth:
type: integer
default: 1
description: Recursion depth (0 = non-recursive, 1 = immediate children)
ListDirResponse:
type: object
properties:
entries:
type: array
items:
$ref: '#/components/schemas/FileEntry'
FileEntry:
type: object
properties:
name:
type: string
path:
type: string
type:
type: string
enum:
- file
- directory
- symlink
size:
type: integer
format: int64
mode:
type: integer
permissions:
type: string
description: Human-readable permissions (e.g. "-rwxr-xr-x")
owner:
type: string
group:
type: string
modified_at:
type: integer
format: int64
description: Unix timestamp (seconds)
symlink_target:
type: string
nullable: true
MakeDirRequest:
type: object
required:
- path
properties:
path:
type: string
description: Directory path to create inside the capsule
MakeDirResponse:
type: object
properties:
entry:
$ref: '#/components/schemas/FileEntry'
RemoveRequest:
type: object
required:
- path
properties:
path:
type: string
description: Path to remove inside the capsule
CapsuleMetrics:
type: object
properties:
sandbox_id:
type: string
range:
type: string
enum:
- 5m
- 10m
- 1h
- 2h
- 6h
- 12h
- 24h
points:
type: array
items:
$ref: '#/components/schemas/MetricPoint'
MetricPoint:
type: object
properties:
timestamp_unix:
type: integer
format: int64
cpu_pct:
type: number
format: double
description: CPU utilization percentage (0-100), normalized to vCPU count
mem_bytes:
type: integer
format: int64
description: Resident memory in bytes (VmRSS of Cloud Hypervisor process)
disk_bytes:
type: integer
format: int64
description: Allocated disk bytes for the CoW sparse file
Error:
type: object
properties:
error:
type: object
properties:
code:
type: string
message:
type: string
SSEEvent:
type: object
description: 'Wire format of one SSE message body. The event name (`event:` line) is
the `kind` and the JSON below is the `data:` line.
'
properties:
event:
type: string
enum:
- connected
- capsule.create
- capsule.pause
- capsule.resume
- capsule.destroy
- capsule.state.changed
- template.snapshot.create
- template.snapshot.delete
- host.up
- host.down
outcome:
type: string
enum:
- success
- error
description: 'Present for action events (capsule.* except state.changed,
template.snapshot.*). Absent for host.up/down, capsule.state.changed,
and the connected sentinel.
'
resource:
type: object
properties:
id:
type: string
type:
type: string
actor:
type: object
properties:
type:
type: string
enum:
- user
- api_key
- system
id:
type: string
name:
type: string
metadata:
type: object
additionalProperties:
type: string
description: 'Event-specific context. Examples: `reason` (ttl_expired,
host_failure, cleanup_after_create_error, orphaned),
`host_ip`, `from`/`to` (for capsule.state.changed).
'
error:
type: string
description: Failure reason; only set when outcome=error.
sandbox:
allOf:
- $ref: '#/components/schemas/Capsule'
nullable: true
description: Populated for capsule.* events; null if DB lookup failed.
timestamp:
type: string
format: date-time