Skip to content

API coverage

spritzer implements the Sprites surface a checkpoint-as-compensation workflow exercises: create a sprite, run commands in it, checkpoint and restore its filesystem, and destroy it. A missing or destroyed sprite answers 404 with a clear JSON error.

Implemented

Method Path Notes
POST /v1/sprites Create a sprite; name is required and becomes the id. Returns {id, url}.
GET (WS) /v1/sprites/{id}/exec Control WebSocket. Reconstructs the command from the query string and streams framed [streamID][payload] messages. See the exec control WebSocket.
POST /v1/sprites/{id}/checkpoint Deep-copy the filesystem under a server-assigned version id (v1, v2, …). Body is {comment?}; streams NDJSON progress ending in {"type":"complete","data":"Checkpoint v<N> created successfully"}.
GET /v1/sprites/{id}/checkpoints List the checkpoints in creation order as a bare JSON array [{id, comment, create_time, is_auto}].
GET /v1/sprites/{id}/checkpoints/{cid} A single checkpoint's metadata: {id, comment, create_time, is_auto}; 404 if the id is unknown.
POST /v1/sprites/{id}/checkpoints/{cid}/restore Replace the filesystem with checkpoint {cid} and return the sprite to running; streams NDJSON progress. 404 if the id is unknown.
DELETE /v1/sprites/{id} Destroy a sprite. Subsequent operations return 404.
GET /v1/sprites/{id} Inspect a sprite: {id, status, url, fs, checkpoints} (checkpoints as [{id, comment, create_time, is_auto}]).
GET /_spritzer/health Version and coverage report (spritzer-only).

Checkpoints are addressed by a server-assigned version id, not a caller label. The caller supplies only an optional comment; the store assigns v1, v2, … in creation order per sprite, stamping a create_time and an is_auto flag (false for manual checkpoints). A compensation workflow can therefore use the comment as a stable handle — list the checkpoints and restore the newest one whose comment matches — while restore itself always takes an explicit id in the path. Create and restore reply with streaming NDJSON progress (application/x-ndjson): one or more {"type":"info","data":...} lines then a terminal {"type":"complete","data":...}. Mirroring real Sprites, the version id is carried in the message text (ID: v<N>, Checkpoint v<N> created successfully) rather than a structured field.

The exec control WebSocket

exec is a control WebSocket at GET /v1/sprites/{id}/exec, matching the real Sprites SDK (superfly/sprites-go, websocket.go). The command is reconstructed from the query string: each repeated cmd param is one argv element (joined with spaces), or a single cmd param is taken as the whole command line; path (argv[0]) is the fallback when no cmd is present. stdin=false skips stdin draining.

Non-PTY framing: every WebSocket message is a binary frame whose first byte is a stream id and whose remaining bytes are the payload.

Stream Id Direction Payload
StreamStdin 0 client → server stdin bytes
StreamStdout 1 server → client stdout bytes
StreamStderr 2 server → client stderr bytes
StreamExit 3 server → client one byte: the exit code
StreamStdinEOF 4 client → server end of stdin

The server runs the exec interpreter, writes stdout as [1]<bytes>, stderr as [2]<bytes>, then a final [3]<exitCodeByte> and closes the connection. The handshake response advertises sprite-capabilities: control-ws. A client with no stdin passes stdin=false (or sends a single [4] frame); the interpreter does not read stdin, so any stdin frames are drained and discarded.

The exec interpreter

exec is not a real shell. A command is split on ; into segments that run in order; the exit code is the last segment's. Each segment is matched against a small set of forms:

Form Effect Exit
echo <text> > <path> write fs[path] = unquote(text) 0
echo <text> append unquote(text) + "\n" to stdout 0
cat <path> append fs[path] (or empty) to stdout 0
rm [-f] <path> delete fs[path] 0
false (no effect) 1
true (no effect) 0
./risky.sh set fs["/work/output"] = "partial-corrupt", append "risky.sh: failed\n" to stderr 1
anything else append <segment> + "\n" to stdout 0

unquote strips a single pair of matching single or double quotes.

Wire fidelity

spritzer mirrors the real Sprites API surface reverse-engineered from superfly/sprites-go: the control-WebSocket exec framing ([streamID][payload] with StreamStdin/Stdout/Stderr/Exit/StdinEOF), the singular NDJSON checkpoint create, and the bare-array checkpoint list with create_time and is_auto. The exec interpreter's behavior still matches chant's in-process Sprites fake (sprites-fake.ts) so a client's checkpoint-as-compensation logic can be exercised end to end offline.