Sprites
Sprites provides a direct interface for defining containers at runtime and securely running arbitrary code inside them.
This can be useful if, for example, you want to:
- Execute code generated by a language model
- Create isolated environments for running untrusted code
- Check out a git repository and run a command against it, like a test suite or linter
- Run containers with arbitrary dependencies and setup scripts
Each individual environment is called a Sprite and can be created using the CLI or SDKs:
# Create a spritesprite create my-sprite
# Execute commandssprite exec python -c "print('hello')"
# Stream output from long-running commandssprite exec "for i in {1..10}; do date +%T; sleep 0.5; done"
# Destroy when donesprite destroy my-spriteimport { SpritesClient } from '@fly/sprites';
const client = new SpritesClient(process.env.SPRITE_TOKEN);
const sprite = await client.createSprite('my-sprite');
// Execute a commandconst result = await sprite.execFile('python', ['-c', "print('hello')"]);console.log(result.stdout);
// Stream output from long-running commandsconst cmd = sprite.spawn('bash', ['-c', 'for i in {1..10}; do date +%T; sleep 0.5; done']);for await (const line of cmd.stdout) { process.stdout.write(line);}
await sprite.delete();package main
import ( "context" "fmt" "io" "os"
sprites "github.com/superfly/sprites-go")
func main() { ctx := context.Background() client := sprites.New(os.Getenv("SPRITE_TOKEN"))
sprite, _ := client.CreateSprite(ctx, "my-sprite", nil) defer client.DeleteSprite(ctx, "my-sprite")
// Execute a command cmd := sprite.Command("python", "-c", "print('hello')") output, _ := cmd.Output() fmt.Println(string(output))
// Stream output from long-running commands cmd = sprite.Command("bash", "-c", "for i in {1..10}; do date +%T; sleep 0.5; done") stdout, _ := cmd.StdoutPipe() cmd.Start() io.Copy(os.Stdout, stdout) cmd.Wait()}Lifecycle
Section titled “Lifecycle”Automatic Hibernation
Section titled “Automatic Hibernation”Sprites automatically hibernate when inactive, with no compute charges while idle. When you execute a command or make a request to a Sprite’s URL, it automatically wakes up with all your data intact.
Timeouts
Section titled “Timeouts”Sprites currently hibernate after 30 seconds of inactivity. This timeout is not configurable yet.
Idle Timeouts
Section titled “Idle Timeouts”A Sprite is considered active if any of the following are true:
- It has an active command running (via
exec) - Its stdin is being written to
- It has an open TCP connection over its URL
- A detachable session is running
Configuration
Section titled “Configuration”Sprites currently use a fixed resource configuration (8 vCPUs, 8192 MB RAM, 100 GB storage). These values are not configurable yet.
Working Directory
Section titled “Working Directory”Set the working directory for command execution:
sprite exec -dir /home/sprite/project npm testconst result = await sprite.exec('npm test', { cwd: '/home/sprite/project',});cmd := sprite.Command("npm", "test")cmd.Dir = "/home/sprite/project"output, err := cmd.Output()Environments
Section titled “Environments”Environment Variables
Section titled “Environment Variables”Set environment variables for command execution:
sprite exec -env MY_SECRET=hello bash -c 'echo $MY_SECRET'const result = await sprite.execFile('bash', ['-lc', 'echo $MY_SECRET'], { env: { MY_SECRET: 'hello' },});console.log(result.stdout); // hellocmd := sprite.Command("bash", "-c", "echo $MY_SECRET")cmd.Env = []string{"MY_SECRET=hello"}output, _ := cmd.Output()fmt.Println(string(output)) // helloPre-installed Tools
Section titled “Pre-installed Tools”The default Sprite environment includes:
- Languages: Node.js, Python, Go, Ruby, Rust, Elixir/Erlang, Java, Bun, Deno
- AI Tools: Claude CLI, Gemini CLI, OpenAI Codex, Cursor
- Utilities: Git, curl, wget, vim, and common development tools
Custom Setup
Section titled “Custom Setup”Run setup commands after creating a Sprite:
const sprite = await client.createSprite('my-sprite');
// Install custom dependenciesawait sprite.exec('pip install pandas numpy matplotlib');await sprite.exec('npm install -g typescript');
// Clone a repositoryawait sprite.exec('git clone https://github.com/your/repo.git /home/sprite/project');sprite, _ := client.CreateSprite(ctx, "my-sprite", nil)
// Install custom dependenciessprite.Command("pip", "install", "pandas", "numpy", "matplotlib").Run()sprite.Command("npm", "install", "-g", "typescript").Run()
// Clone a repositorysprite.Command("git", "clone", "https://github.com/your/repo.git", "/home/sprite/project").Run()Interactive Sessions
Section titled “Interactive Sessions”TTY Mode
Section titled “TTY Mode”For interactive applications, enable TTY mode:
# Open interactive shell (TTY enabled by default)sprite console
# Or with execsprite exec -tty bashconst cmd = sprite.spawn('bash', [], { tty: true, rows: 24, cols: 80,});
// Write to stdincmd.stdin.write('echo hello\n');
// Read from stdoutcmd.stdout.on('data', (data) => { process.stdout.write(data);});
// Resize terminalcmd.resize(30, 100);cmd := sprite.Command("bash")cmd.SetTTY(true)cmd.SetTTYSize(24, 80)
cmd.Stdin = os.Stdincmd.Stdout = os.Stdoutcmd.Stderr = os.Stderr
cmd.Start()
// Resize latercmd.Resize(30, 100)
cmd.Wait()Detachable Sessions
Section titled “Detachable Sessions”Create sessions that persist even after disconnecting:
# Create a detachable sessionsprite exec -detachable "npm run dev"
# List active sessionssprite exec
# Attach to a sessionsprite exec -id <session-id>// Create a detachable sessionconst sessionCmd = sprite.createSession('npm', ['run', 'dev']);let sessionId;
sessionCmd.on('message', (msg) => { if (msg && msg.type === 'session_info') { sessionId = msg.session_id; }});
// ... later, attach to itconst attachCmd = sprite.attachSession(sessionId);attachCmd.stdout.pipe(process.stdout);// Create a detachable sessioncmd := sprite.CreateDetachableSession("npm", "run", "dev")cmd.Stdout = os.Stdoutcmd.Start()
// List sessions to get the session IDsessions, _ := client.ListSessions(ctx, "my-sprite")sessionID := sessions[0].ID
// ... later, attach to itcmd = sprite.AttachSessionContext(ctx, sessionID)cmd.Stdout = os.Stdoutcmd.Run()Listing Sessions
Section titled “Listing Sessions”sprite execconst sessions = await sprite.listSessions();for (const session of sessions) { console.log(`${session.id}: ${session.command}`);}sessions, _ := client.ListSessions(ctx, "my-sprite")for _, session := range sessions { fmt.Printf("%s: %s\n", session.ID, session.Command)}Referencing Sprites
Section titled “Referencing Sprites”By Name
Section titled “By Name”If you have the name of a Sprite, you can get a handle to it:
# Set active sprite for current directorysprite use my-sprite
# Commands now use this spritesprite exec echo "hello"// Get sprite by name (doesn't verify it exists)const sprite = client.sprite('my-sprite');
// Get sprite and verify it existsconst sprite = await client.getSprite('my-sprite');// Get sprite by name (doesn't verify it exists)sprite := client.Sprite("my-sprite")
// Get sprite and verify it existssprite, err := client.GetSprite(ctx, "my-sprite")Listing Sprites
Section titled “Listing Sprites”# List all spritessprite list
# List with prefix filtersprite list --prefix "dev-"// List all spritesconst sprites = await client.listAllSprites();
// List with prefix filterconst devSprites = await client.listAllSprites('dev-');
// Paginated listingconst page = await client.listSprites({ maxResults: 50 });console.log(page.sprites);if (page.hasMore) { const nextPage = await client.listSprites({ continuationToken: page.nextContinuationToken, });}// List all spritessprites, _ := client.ListAllSprites(ctx, "")
// List with prefix filterdevSprites, _ := client.ListAllSprites(ctx, "dev-")
// Paginated listingpage, _ := client.ListSprites(ctx, &sprites.ListOptions{MaxResults: 50})fmt.Println(page.Sprites)if page.HasMore { nextPage, _ := client.ListSprites(ctx, &sprites.ListOptions{ ContinuationToken: page.NextContinuationToken, })}HTTP Access
Section titled “HTTP Access”Every Sprite has a unique URL for HTTP access:
# Get sprite URLsprite url
# Make URL public (no auth required)sprite url update --auth public
# Make URL require sprite auth (default)sprite url update --auth default// Get sprite info including URLconst info = await client.getSprite('my-sprite');console.log(info.url);// Get sprite info including URLinfo, _ := client.GetSprite(ctx, "my-sprite")fmt.Println(info.URL)Updating URL settings is available via the CLI, Go SDK, or REST API (the JS SDK does not expose a helper yet).
Port Forwarding
Section titled “Port Forwarding”Forward local ports to your Sprite:
# Forward local port 3000 to sprite port 3000sprite proxy 3000
# Forward multiple portssprite proxy 3000 8080 5432// Forward single portsession, _ := client.ProxyPort(ctx, "my-sprite", 3000, 3000)defer session.Close()// localhost:3000 now forwards to sprite:3000
// Forward multiple portssessions, _ := client.ProxyPorts(ctx, "my-sprite", []sprites.PortMapping{ {LocalPort: 3000, RemotePort: 3000}, {LocalPort: 8080, RemotePort: 80},})Port Notifications
Section titled “Port Notifications”Get notified when ports open in your Sprite:
const cmd = sprite.spawn('npm', ['run', 'dev']);
cmd.on('message', (msg) => { if (msg.type === 'port_opened') { console.log(`Port ${msg.port} opened on ${msg.address} by PID ${msg.pid}`); // Auto-forward or notify user }});cmd := sprite.Command("npm", "run", "dev")cmd.TextMessageHandler = func(data []byte) { var notification sprites.PortNotificationMessage json.Unmarshal(data, ¬ification)
if notification.Type == "port_opened" { fmt.Printf("Port %d opened on %s by PID %d\n", notification.Port, notification.Address, notification.PID) }}cmd.Run()Checkpoints
Section titled “Checkpoints”Save and restore Sprite state:
# Create a checkpointsprite checkpoint create
# List checkpointssprite checkpoint list
# Restore from checkpointsprite restore <checkpoint-id>See Checkpoints and Restore for more details.
Error Handling
Section titled “Error Handling”import { ExecError } from '@fly/sprites';
try { await sprite.execFile('bash', ['-lc', 'exit 1']);} catch (error) { if (error instanceof ExecError) { console.log('Exit code:', error.exitCode); console.log('Stdout:', error.stdout); console.log('Stderr:', error.stderr); }}cmd := sprite.Command("bash", "-lc", "exit 1")err := cmd.Run()
if err != nil { if exitErr, ok := err.(*sprites.ExitError); ok { fmt.Printf("Exit code: %d\n", exitErr.ExitCode()) }}Cleanup
Section titled “Cleanup”Always clean up Sprites when you’re done:
sprite destroy my-spriteawait sprite.delete();// orawait client.deleteSprite('my-sprite');err := client.DeleteSprite(ctx, "my-sprite")