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