spritzer

module
v0.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 14, 2026 License: MIT

README

spritzer

A standalone, stateful local emulator of the Fly.io Sprites API. Like LocalStack, but for Fly Sprites.

spritzer is the local target for chant's Fly lexicon — its Sprite activities (create, exec, checkpoint, restore) run against spritzer for offline, accountless checkpoint-as-compensation. See the chant docs and the Fly deploy tutorial it powers.

CI Go Reference Release GHCR

Purpose

Testing a Sprites client, such as a workflow that checkpoints a sandbox before a risky step and restores it on failure, requires testing against state: a sprite whose filesystem a command mutates, a checkpoint that captures that filesystem, and a restore that rewinds to it. A schema mock holds no state, so it cannot model these behaviors. spritzer keeps sprites, their filesystems, and their checkpoints in memory and runs a small exec interpreter, so a client's checkpoint-as-compensation logic can be exercised end to end offline.

spritzer is wire-compatible with the in-process Sprites fake in the chant lexicon (sprites-fake.ts): the endpoint shapes and the exec interpreter match it, so the same integration suite passes against the spritzer container image.

Features

  • Stateful in-memory store of sprites keyed by name, each with a filesystem (path → contents) and an ordered list of checkpoints.
  • exec is a control WebSocket at GET /v1/sprites/{id}/exec speaking the real Sprites SDK's framed protocol: each binary message is [streamID][payload] (StreamStdin=0, StreamStdout=1, StreamStderr=2, StreamExit=3, StreamStdinEOF=4). Behind the frames a small scripted interpreter (echo > path, echo, cat, rm, true/false, ./risky.sh, and an echo-back default) writes, overwrites, or fails a filesystem key so the result is observable.
  • Checkpoint / restore: create is POST /v1/sprites/{id}/checkpoint (singular), streaming NDJSON progress and assigning a server version id (v1, v2, …) with an optional caller comment; the list is a bare array with create_time and is_auto; restore takes a checkpoint id in the path, streams NDJSON, replaces the filesystem with that copy, and returns the sprite to running. This is the checkpoint-as-compensation primitive.
  • A destroyed or missing sprite returns 404 on any subsequent operation.
  • A /_spritzer/health endpoint reporting version and implemented paths.
  • Single static binary and distroless container image; no runtime dependencies.

Quick start

Run the container:

docker run --rm -p 4290:4290 ghcr.io/intentius/spritzer:latest

Or install with Go:

go install github.com/intentius/spritzer/cmd/spritzer@latest
spritzer            # listens on :4290 by default

Point a Sprites client at it with the same environment variable the real client uses:

export SPRITES_BASE_URL=http://localhost:4290

The listen address is configurable with -addr or SPRITZER_ADDR (default :4290).

Usage example

BASE=http://localhost:4290

# Create a sprite (its name is its id).
curl -s -X POST "$BASE/v1/sprites" -d '{"name":"demo"}'
# => {"id":"demo","url":"http://localhost:4290/s/demo"}

# Checkpoint the current state. The server assigns the version id and streams
# NDJSON progress; the id is on the terminal complete event.
curl -s -X POST "$BASE/v1/sprites/demo/checkpoint" -d '{"comment":"pre-run"}'
# => {"type":"info","data":"Creating checkpoint..."}
#    {"type":"info","data":"  ID: v1"}
#    {"type":"complete","data":"Checkpoint v1 created successfully"}

# List the checkpoints (creation order) as a bare array.
curl -s "$BASE/v1/sprites/demo/checkpoints"
# => [{"id":"v1","comment":"pre-run","create_time":"2026-07-11T...Z","is_auto":false}]

# Restore rewinds the filesystem to the checkpoint, addressed by id in the path,
# streaming NDJSON progress.
curl -s -X POST "$BASE/v1/sprites/demo/checkpoints/v1/restore"
# => {"type":"info","data":"Restoring checkpoint v1..."}
#    {"type":"complete","data":"Checkpoint v1 restored successfully"}

exec is a control WebSocket at ws://<host>/v1/sprites/{id}/exec. Pass the command as cmd query params (?cmd=echo&cmd=hi, or a single ?cmd=echo hi). Every message is a binary frame [streamID][payload]: the server writes stdout as [1]<bytes>, stderr as [2]<bytes>, then [3]<exitCodeByte>. So echo hi yields [1]"hi\n" then [3]\x00 (exit 0), and ./risky.sh yields [2]"risky.sh: failed\n" then [3]\x01 (exit 1).

Comparison

Capability spritzer Schema mock Real Sprites
Sprite filesystem that exec mutates Yes No Yes
Checkpoint / restore (rewind on failure) Yes No Yes
Destroyed-sprite 404 semantics Yes No Yes
Runs fully offline Yes Yes No
Cost Free Free Billed
Real sandboxes, images, code execution No No Yes

API coverage

Implemented: create, the exec control WebSocket, checkpoint (NDJSON), list checkpoints (bare array), get one checkpoint, restore-by-id (NDJSON), destroy, and an inspection GET, plus a /_spritzer/health report. The full table is in the API coverage docs.

Development

The primary task runner is just; a Makefile mirrors the same targets.

just build      # compile
just test       # go test ./...
just race       # go test -race ./...
just lint       # golangci-lint
just cover      # coverage profile
just docs-serve # preview the doc site

Contributing

Contributions are welcome. See CONTRIBUTING.md and the Code of Conduct.

License

Licensed under the MIT License. Copyright (c) 2026 Intentius.

Directories

Path Synopsis
cmd
spritzer command
Command spritzer runs a standalone, stateful local emulator of the Fly.io Sprites API.
Command spritzer runs a standalone, stateful local emulator of the Fly.io Sprites API.
internal
clock
Package clock provides a small time abstraction so that time-stamped state (a sprite's creation time, and any future timed behavior) can be driven deterministically in tests.
Package clock provides a small time abstraction so that time-stamped state (a sprite's creation time, and any future timed behavior) can be driven deterministically in tests.
id
Package id generates identifiers in the shapes spritzer uses.
Package id generates identifiers in the shapes spritzer uses.
server
Package server wires the sprite store behind a net/http router that speaks the Sprites REST surface over the /v1 path space.
Package server wires the sprite store behind a net/http router that speaks the Sprites REST surface over the /v1 path space.
sprite
Package sprite is a thread-safe, in-memory model of Sprites and their checkpoint/restore semantics.
Package sprite is a thread-safe, in-memory model of Sprites and their checkpoint/restore semantics.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL