Files
python-sdk/api/openapi.yaml
pptx704 03cd2227b5
All checks were successful
ci/woodpecker/push/unit Pipeline was successful
feat: prune SDK to API-key surface, add stats/usage/metrics/events
Strip session-only routes (auth, me, teams, hosts, channels, admin,
audit-logs) from api/openapi.yaml via new scripts/prune_openapi.py,
which is wired into `make generate` and `make prune-spec`. Spec drops
from 4787 to 1717 lines; regenerated _generated.py drops from 906 to
415 lines with all obsolete models removed.

Add SDK methods on CapsulesResource / AsyncCapsulesResource for the
apiKey-usable routes that were previously unwrapped:
  - capsules.stats(range=...)         -> CapsuleStats
  - capsules.usage(from_=..., to=...) -> UsageResponse (accepts date)
  - capsules.metrics(id, range=...)   -> CapsuleMetrics

Add EventsResource / AsyncEventsResource exposing
WrennClient.events.stream() over /v1/events/stream (SSE). Parser
handles `:keepalive` comments and multi-line `data:` frames.

Re-export new models (CapsuleStats, CapsuleMetrics, MetricPoint,
UsageResponse, SSEEvent, Resource, Actor) from wrenn.models.

Drop email-validator runtime dep (no EmailStr remains). Bump version
to 0.3.0 (pyproject + __version__). Add pyyaml to dev deps and
types-PyYAML to the pre-commit mypy hook for the prune script.

Normalize docstrings on new methods/classes to Google style with
Example:: blocks where useful. Regenerate docs/reference.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-23 01:28:54 +06:00

1718 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** (after session is established):\n```json\n{\"type\": \"input\", \"data\": \"<base64-encoded bytes>\"}\n\
{\"type\": \"resize\", \"cols\": 120, \"rows\": 40}\n{\"type\": \"kill\"}\n```\n\n**Server sends**:\n```json\n{\"\
type\": \"started\", \"tag\": \"pty-abc123de\", \"pid\": 42}\n{\"type\": \"output\", \"data\": \"<base64-encoded PTY\
\ bytes>\"}\n{\"type\": \"exit\", \"exit_code\": 0}\n{\"type\": \"error\", \"data\": \"description\", \"fatal\": true}\n\
{\"type\": \"ping\"}\n```\n\nPTY data (input and output) is base64-encoded because it contains raw\nterminal bytes\
\ (escape sequences, control codes) that are not valid UTF-8.\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