This file records the createos-sandbox control-plane version this SDK has been
reconciled against, and the known wire gaps at that point. The control
plane is a separate service; its published OpenAPI spec is stale, so this
checkpoint is verified against the live server behavior, never the spec.
See AGENTS.md → "Wire types — source of truth".
| SDK version | 0.6.0 |
| createos-sandbox branch | main |
| createos-sandbox commit | 3c3f4b5 (2026-06-12) |
| Audited | 2026-06-16 (delta 12ed1a7..3c3f4b5 reviewed; the only user-facing wire addition was idle auto-pause, already modeled in 0.6.0 — no new drift) |
What "compliant" means here: every endpoint the SDK models is
wire-faithful to the server at the audit above — field names, types, and
omitempty → optional mapping — except for the known drift listed below.
Endpoints the SDK does not model (next two sections) are a coverage
choice, not a fidelity failure, and do not move this checkpoint.
POST /v1/sandboxes/:id/tunnel/:port — keyless port-forward (HTTP
Upgrade) — is a documented deliberate non-goal (AGENTS.md → "Not
modeled"): it needs a raw socket, not fetch.
Two more endpoints are interactive PTYs, neither modeled yet:
POST /v1/sandboxes/:id/shell— PTY over HTTP Upgrade. Same hard blocker astunnel:fetchcannot hand back the upgraded socket, so this is effectively a permanent non-goal for a fetch-only transport.GET /v1/sandboxes/:id/shell-ws— PTY over WebSocket. This one is reachable with zero dependencies via the globalWebSocket(browser / Deno / Bun / workerd / Node 22+). It is a duplex, event-driven surface unlike the request / stream model, so it is a candidate for a future, separately designed addition rather than a permanent non-goal.
Sandbox.sh() is a bash -lc convenience over /exec; it is not the
PTY shell above.
The managed-process resource landed upstream after the 0.6.0 checkpoint and
is not modeled yet. It supersedes both PTY routes above for a fetch-only
transport, because it is a plain-JSON resource plus one NDJSON stream — the
same shape the SDK already handles for streaming /exec, so a coverage gap
rather than a transport blocker:
POST/GET /v1/sandboxes/:id/processes— create (pipe-backed, or PTY-backed whenpty: {rows, cols}is present) and list.GET/DELETE /v1/sandboxes/:id/processes/:process_id— inspect, and terminate the whole process tree (?grace_ms=).GET /v1/sandboxes/:id/processes/:process_id/connect?after=N— NDJSON output stream, replayable from a sequence number. A client disconnect does not kill the process, which is what makes this modelable overfetchwhere/shellis not.POST .../input,.../stdin/close,.../resize,.../signalandGET .../wait?scope=leader|tree— plain-JSON control operations.
Three read-only metrics endpoints landed upstream after the 0.6.0
checkpoint (3c3f4b5) and are not modeled yet — plain-JSON GETs, so a
coverage gap rather than a transport blocker:
GET /v1/sandboxes/:id/metrics— per-sandbox CPU + memory snapshot (SandboxMetricsView), owner-gated.GET /v1/metrics— per-user usage aggregate.GET /v1/metrics/timeseries— per-user usage over a time window.
The current state of the server contract the SDK depends on. Operator- only surface (host enrolment, certificate management, internal billing, and other admin controls) is excluded from the SDK by design and is not tracked here.
- Ownership checks return 404, not 402.
PUT .../egress,POST .../bandwidth/recharge, and the file upload / download routes return404 "sandbox not found"(JSendfail) for a sandbox the caller does not own. The SDK maps 404 →CreateosSandboxNotFoundError. Onrecharge, the owner check precedes the credit gate, so a non-owned id yields 404 rather than 402. - Idle auto-pause.
auto_pause_after_seconds(*int,omitempty→ optional) is accepted on create and patch and returned onSandboxView, alongside adisable_auto_pausebool (clears the nullable timeout —omitemptycan't express "set to null"). Valid range 60–86400, server-validated. SDK: fields onCreateSandboxRequest/SandboxView/PatchSandboxRequest, plusSandbox.setAutoPause(seconds | null). - Credit gating (402). Cost-incurring endpoints (sandbox create /
resume / fork, bandwidth recharge, disk / network / template create)
return
402 Payment Required(JSendfail) when the account has no credit. SDK:errorFromResponsemaps 402 →CreateosSandboxPaymentRequiredError. 402 is not a retry status in either retry class — correct, since retrying without topping up cannot succeed.
A server response-structure change reshaped the wire format. Changes the SDK surfaces to callers:
- List endpoints are now paginated.
GET /v1/sandboxes,/v1/disks,/v1/networks,/v1/templates,/v1/hosts,/v1/shapes, andGET /v1/sandboxes/:id/disksreturn the doubly-nested envelope{ status, data: { data: [...], pagination: { total, limit, offset, count } } }. The legacy{ <key>: [...] }wrappers are deprecated server-side. The SDK's list methods now auto-loop every page (CreateosSandboxHttp.fetchAllPages, accepts paginated / legacy / bare-array shapes) and return the full result set. The server clampslimitto 500; paging advances by the actual item count, not the requested size.listSandboxes({ limit })treatslimitas a cap on handles returned.GET /v1/rootfsis not paginated (still a plainRootfsView). CreateSandboxResponse.moderemoved. The server droppedmodefrom the create response (operational detail of the boot path, not part of the user contract). Themodefield and theSandboxSpawnModetype are gone from the SDK.bandwidth_quota_bytesis not settable at create. The server rejects any non-zero value with400and stamps the cluster default. Removed fromCreateSandboxRequest. Grow the quota post-create withSandbox.rechargeBandwidth()(POST /v1/sandboxes/:id/bandwidth/ recharge, body{ add_bytes }). Still accepted on the fork request, so it stays onForkSandboxRequest.SandboxView.ingress_url_template?added. The server now returns the ingress template on the sandbox view, not just on the create response.
All plain-JSON endpoints the server exposes are now modeled:
POST /v1/sandboxes/:id/ssh-pubkeys→Sandbox.addSSHPubkeys().PATCH /v1/disks/:id(S3 credential rotate) →DisksApi.rotateCredentials().
The only user-facing plain-JSON endpoints currently unmodeled are the three metrics routes listed under "Endpoints not modeled" above (added after the 0.6.0 checkpoint).
Fixed in 0.5.0 — every omitempty-vs-required mismatch on a type the SDK
surfaces to callers:
SandboxView.ip→ip?: string(omitted whilecreating).CreateSandboxRequest.regionadded.Shape.cpu_quota_pct?added (cgroup cpu cap percent,omitempty).HostPublic.rootfses?(omitempty).NetworkMember.ip?/.name?(bothomitempty).
Remaining, intentionally left (low):
CreateSandboxResponsemarksname,rootfs,egressandbandwidth_quota_bytesrequired while the server sends themomitempty.createSandbox()never hands this object to the caller — it reads onlyid/ingress_url_templateand returns aSandboxbuilt from a follow-upGET— so the mismatch is unreachable in normal use. Left as documented drift rather than a forced?.
Directly verified wire-faithful at this audit (server response read and
compared field-by-field): SandboxView, the create / destroy / resize
responses, BandwidthView, EgressView, TemplateView, the disk types
(DiskView, SandboxDiskView, DiskCreateRequest + DiskConfig /
DiskCredentials), the network types (NetworkView / Network,
NetworkMember), Shape, HostPublic, and the exec response envelope
(ExecResponse). All endpoint coverage was checked against the route
table (sandbox lifecycle incl. by-ip, ssh-pubkeys, files, egress,
bandwidth, templates, disks incl. attach / detach / credential rotate,
networks incl. delete).
Not independently re-read in this pass: the exec result and NDJSON
stream-frame shapes are agent-protocol types, not part of the HTTP
wire types. The SDK carries an explicit reconciliation note for them
(src/types.ts, near ExecRequest). Re-verify against the agent proto if
it changes.
- Identify the current
createos-sandboxmainand review the wire surface (request / response types, routes, handlers) changed since the last audit. - Reconcile
src/types.ts(and any new method) against the live server behavior — not the OpenAPI spec. - Update the wire-behavior / drift / coverage sections above, then bump the audit date and SDK version.