Skip to content

feat(api): add OpenAPI 3.1.1 spec for cloud sandbox REST API#254

Merged
DorianZheng merged 3 commits intomainfrom
feat/rest-api-spec
Feb 13, 2026
Merged

feat(api): add OpenAPI 3.1.1 spec for cloud sandbox REST API#254
DorianZheng merged 3 commits intomainfrom
feat/rest-api-spec

Conversation

@DorianZheng
Copy link
Copy Markdown
Member

@DorianZheng DorianZheng commented Feb 13, 2026

Summary

  • Add OpenAPI 3.1.1 specification for the BoxLite cloud sandbox REST API at boxlite/openapi/rest-sandbox-open-api.yaml
  • API design follows industry-standard REST patterns (versioned prefix, multi-tenancy, capability discovery, idempotency keys, standard error model, pagination)
  • 24 endpoints across 7 resource groups, all schemas mapping 1:1 to existing Rust types

API Design

Group Endpoints Description
Configuration GET /v1/config Server capability discovery
Auth POST /v1/oauth/tokens OAuth2 client credentials
Boxes 7 endpoints CRUD + start/stop lifecycle
Execution 6 endpoints Async exec, SSE streaming, stdin, signals, TTY (WebSocket)
Files 2 endpoints Upload/download via tar streams
Metrics 2 endpoints Runtime-wide + per-box metrics
Images 4 endpoints Pull, list, get, exists check

Key Design Decisions

  • Multi-tenancy: {prefix} path segment = organization/workspace ID
  • Execution: Async only — POST /exec returns execution_id, stream via SSE
  • TTY: WebSocket at /exec/tty for interactive terminal sessions
  • Files: Binary tar stream (matches existing FilesInterface)
  • Errors: Standard {message, type, code} model mapping all BoxliteError variants

Test plan

  • YAML syntax validated (Python yaml.safe_load)
  • All $ref references resolve correctly (24 schemas, 7 parameters)
  • All BoxliteError variants mapped to error types
  • All BoxOptions fields represented in CreateBoxRequest
  • All BoxInfo fields represented in Box response schema
  • Lint with npx @redocly/cli lint (optional, CI)
  • Generate preview docs with npx @redocly/cli preview-docs (optional)

@DorianZheng
Copy link
Copy Markdown
Member Author

@yingjunwu PTAL

Define the RESTful API specification for exposing BoxLite as a cloud
sandbox service.

Key design decisions:
- Versioned prefix (/v1/) with multi-tenant {prefix} path segment
- Async execution model (POST /exec returns execution_id, SSE streaming)
- WebSocket endpoint for interactive TTY sessions
- OAuth2 client credentials authentication
- Idempotency keys for safe mutation retries
- Standard error model mapping to BoxliteError variants

Endpoints: 24 across 7 resource groups (boxes, execution, files,
metrics, images, config, auth). All schemas map 1:1 to existing
Rust types (BoxOptions, BoxInfo, BoxCommand, BoxMetrics, etc.).
- Replace incorrect ASF copyright header with BoxLite Contributors header
- Fix contact URL: bxlite → boxlite-ai
- Rename hyphenated `next-page-token` to `next_page_token` (snake_case)
  to avoid code generation issues in most languages

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Copy link
Copy Markdown
Contributor

@yingjunwu yingjunwu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: OpenAPI REST API Spec

Overall this is a well-structured, professional spec. Good use of idempotency keys, pagination, SSE streaming, OAuth2 scopes, and WebSocket TTY. The schema mappings to Rust types (BoxInfo, BoxOptions, BoxCommand, RuntimeMetrics, BoxMetrics, ImageInfo) are accurate.

I pushed a commit fixing 3 bugs:

  • ASF license header → replaced with BoxLite Contributors header (this isn't an ASF project)
  • Contact URLbxliteboxlite-ai
  • next-page-tokennext_page_token (hyphenated JSON keys break code generation in Go/Python/Java/Rust)

Remaining suggestions

Should fix:

  1. Box.updated_at vs Rust BoxInfo.last_updated — the Rust struct uses last_updated. Consider aligning the naming (either rename the Rust field or the spec field) to avoid confusion when implementing the adapter.

  2. ErrorModel.type enum gaps — the spec says it "maps directly to BoxliteError variants" but:

    • Missing RpcTransport variant (exists in Rust)
    • UnsupportedError is ambiguous: Rust has both UnsupportedEngine (no payload) and Unsupported(String)
    • UnauthorizedError is cloud-only (doesn't exist in Rust) — worth documenting as such
  3. labels fieldCreateBoxRequest accepts labels and Box response returns them, but BoxOptions in Rust has no labels field. BoxInfo::new() hardcodes labels: HashMap::new(). Either add label storage to the core library or remove from spec until implemented.

Nice to have:

  1. Missing GET /{prefix}/boxes/{box_id}/executions — there's GET /executions/{exec_id} to get one execution, but no way to list all executions for a box. Useful for debugging and monitoring.

  2. Missing PATCH /{prefix}/boxes/{box_id} — no way to update mutable fields (labels, name) after creation.

  3. network single-value enumCreateBoxRequest.network is enum: [isolated]. Consider using plain type: string with a default: isolated instead, so adding new modes later doesn't break the enum contract.

  4. BootTiming nesting — the spec groups boot timing into a nested BootTiming object, while Rust keeps stage_*_ms fields flat in BoxMetrics. This is a reasonable REST design choice, just noting the structural difference for implementers.

@yingjunwu
Copy link
Copy Markdown
Contributor

LGTM

yingjunwu
yingjunwu previously approved these changes Feb 13, 2026
Copy link
Copy Markdown
Contributor

@yingjunwu yingjunwu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

…work enum

- Add RpcTransportError to ErrorModel.type enum (matches RpcTransport variant)
- Document UnauthorizedError as server-layer only (not a BoxliteError variant)
- Remove labels from CreateBoxRequest (BoxOptions has no labels field)
- Change network from enum to plain string for forward compatibility
@DorianZheng
Copy link
Copy Markdown
Member Author

Thanks for the thorough review and the fixes you pushed. Addressed the remaining items in 0149a90:

Fixed:

  1. RpcTransportError — added to ErrorModel.type enum. Was an oversight.
  2. UnauthorizedError — documented as server-layer only (auth middleware, not a BoxliteError variant).
  3. labels on CreateBoxRequest — removed. BoxOptions has no labels field. Kept on Box response since BoxInfo does have the field (empty for now).
  4. network enum — changed to plain type: string with documented default. Better for forward compatibility when adding new modes.

Kept as-is (with rationale):

  • updated_at vs last_updated: updated_at is the REST convention (GitHub, Stripe, AWS, K8s). The adapter layer maps the internal last_updated field. Internal naming shouldn't leak into the public API.
  • UnsupportedError single type: From the API consumer's perspective, "unsupported" is one concept. The message field carries the detail. Two error types would add noise without value.
  • Nice-to-haves (Bump version to 0.2.2 for all packages #4-Bump version to 0.2.3 #7): Skipped for now — list executions and PATCH boxes require runtime-level changes. BootTiming nesting is fine as agreed.

@DorianZheng DorianZheng merged commit 882bb71 into main Feb 13, 2026
7 checks passed
@DorianZheng DorianZheng deleted the feat/rest-api-spec branch February 13, 2026 07:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants