npx skills add ...
npx skills add kylelundstedt/dotfiles --skill sprites-dev
Use this skill when the user wants to manage remote Sprites from their local machine — listing sprites, executing commands, managing checkpoints, transferring files, controlling network policy, or coordinating work across multiple sprites.
npx skills add kylelundstedt/dotfiles --skill sprites-dev
You are managing remote Sprite VMs from a local machine using the sprite CLI. This skill covers the local-side CLI for controlling Sprites externally. (Sprites also have built-in agent context at /.sprite/docs/ for agents running inside the Sprite.)
Sprites are persistent, hardware-isolated Linux VMs on Fly.io with durable filesystems backed by object storage.
The sprite CLI controls remote Sprites. The -s flag specifies which Sprite, -o specifies the org.
When a .sprite file exists in the working directory, -s can be omitted.
sprite exec is the primary tool for remote work. Stdout and stderr are returned to the local terminal.
Important: Each sprite exec invocation is a separate session. Environment variables, working directory, and shell state do not persist between calls. For stateful work, use bash -c "..." to chain commands, or use sprite console for interactive sessions.
Environment variables: The -env flag passes env vars to the remote session, but multiple -env flags only apply the last one. Use comma-separated format for multiple vars:
There is no dedicated file transfer command. Use sprite exec with cat for pulling files, or pipe content in:
Checkpoints capture the writable overlay of the Sprite's filesystem. They are fast (sub-second, copy-on-write) and cheap.
Checkpoint aggressively. Before any risky operation, before switching contexts, whenever something is working. Checkpoints are nearly free.
The last 5 checkpoints are also mounted inside the Sprite at /.sprite/checkpoints/ for direct file-level inspection without restoring.
Each Sprite gets a public HTTPS URL at https://<name>-<org>.sprites.dev/, routing to port 8080 by default.
Network egress policy is managed via the Sprites REST API, not the CLI:
Network policy can only be modified externally. The Sprite cannot change its own policy.
Services are long-running processes inside a Sprite that persist across reboots and hibernation.
One service can be designated as the HTTP service (receives proxied requests on the Sprite's URL).
When working across multiple Sprites, use sprite list to see the fleet, then target each by name:
Each Sprite is fully isolated — no shared mutable state. Coordinate through explicit communication: files in shared Tigris buckets, HTTP endpoints, or Git repos.
Sprites auto-sleep when idle (no active commands, TCP connections, TTY sessions, or running Services). They wake automatically on the next sprite exec, sprite console, or HTTP request to their URL. Anything on disk persists across sleep cycles. Running processes do not — use Services for daemons.
After creating a new Sprite, set up Tailscale and install dotfiles. Tailscale provides a stable hostname for SSH and enables agent forwarding from the Mac's 1Password SSH agent.
Pass TS_AUTHKEY and TS_HOSTNAME so install.sh handles Tailscale end-to-end:
Verify SSH connectivity from the Mac:
Do not proceed until this succeeds.
The Mac's SSH config forwards the 1Password agent to *.ts.net hosts (ForwardAgent yes). This means ssh <name> gives the Sprite access to your GitHub SSH keys — no tokens needed. Clone directly:
Git commit signing is automatically enabled on first login via Tailscale SSH (the shell detects the forwarded agent).
When running commands on a Sprite, these paths are useful:
/.sprite/api.sock — internal API Unix socket/.sprite/policy/network.json — read-only network policy/.sprite/checkpoints/ — last 5 checkpoints mounted for inspection/.sprite/logs/services/ — service logs/.sprite/docs/ — platform documentation (agent-context.md, languages.md, docker.md)/home/sprite/ — user home directory# Run a single command
sprite exec -s mysprite -- ls -la /home/sprite
# Run a multi-part command (quote or use --)
sprite exec -s mysprite -- bash -c "cd /project && npm test"
# Pipe output locally
sprite exec -s mysprite -- cat /path/to/file > local-copy.txt# WRONG — only BAR is set
sprite exec -s mysprite -env "FOO=1" -env "BAR=2" -- env
# RIGHT — both are set
sprite exec -s mysprite -env "FOO=1,BAR=2" -- env# Pull a file from a sprite
sprite exec -s mysprite -- cat /path/on/sprite > local-file.txt
# Pull a binary file
sprite exec -s mysprite -- base64 /path/to/binary | base64 -d > local-file.bin
# Push a file to a sprite (via stdin)
cat local-file.txt | sprite exec -s mysprite -- bash -c "cat > /path/on/sprite"
# For heavier file work, use SSHFS via proxy
sprite proxy 2222 # then sshfs sprite@localhost:/home/sprite /mnt/sprite -p 2222# Create a checkpoint (with optional comment)
sprite checkpoint create -s mysprite
sprite checkpoint create -s mysprite -m "before risky refactor"
# List checkpoints
sprite checkpoint list -s mysprite
# Restore to a checkpoint (destructive — drops current session and all changes since)
sprite restore <checkpoint-id> -s mysprite# Show the sprite's URL and auth mode
sprite url -s mysprite
# Make the URL public (no auth required)
sprite url update --auth public -s mysprite
# Restore authenticated mode
sprite url update --auth default -s mysprite
# Forward remote ports to local machine
sprite proxy 8080 3000 -s mysprite# View current network policy (from inside the sprite)
sprite exec -s mysprite -- cat /.sprite/policy/network.json
# Update network policy (from outside, via API)
sprite api -s mysprite POST /policy/network \
-d '{"rules": [{"include": "defaults"}, {"domain": "custom-api.com", "action": "allow"}]}'# List services
sprite exec -s mysprite -- sprite-env services list
# Create a service
sprite exec -s mysprite -- sprite-env services create <name> --cmd <binary> --args "<args>"
# Get service status
sprite exec -s mysprite -- sprite-env services get <name>
# View service logs
sprite exec -s mysprite -- cat /.sprite/logs/services/<name>.stderr.log# Run the same command across all sprites
for s in $(sprite list); do
echo "=== $s ==="
sprite exec -s "$s" -- <command>
done# Create a new sprite
sprite create <name>
sprite create -o <org> <name>
# Destroy a sprite (irreversible)
sprite destroy <name># Mint an ephemeral tag:dev key via the Tailscale OAuth client (see agent_docs/secrets.md)
_tok=$(curl -fsS -u "$(op read 'op://Employee/Tailscale OAuth Dev/Client ID' --account industryvault.1password.com):$(op read 'op://Employee/Tailscale OAuth Dev/Client secret' --account industryvault.1password.com)" -d grant_type=client_credentials https://api.tailscale.com/api/v2/oauth/token | jq -r .access_token)
TS_AUTHKEY=$(curl -fsS -X POST -H "Authorization: Bearer $_tok" -H "Content-Type: application/json" -d '{"capabilities":{"devices":{"create":{"reusable":false,"ephemeral":true,"preauthorized":true,"tags":["tag:dev"]}}},"expirySeconds":600}' https://api.tailscale.com/api/v2/tailnet/-/keys | jq -r .key)
sprite exec -s <name> -env "TS_AUTHKEY=$TS_AUTHKEY,TS_HOSTNAME=<name>" -- bash -c "curl -fsSL https://raw.githubusercontent.com/kylelundstedt/dotfiles/master/install.sh | bash"ssh -o StrictHostKeyChecking=accept-new <name> echo okssh <name> git clone git@github.com:<org>/<repo>.git /home/sprite/<repo>zed ssh://sprite@<name>/home/sprite/<repo>