Skip to content

Quivvo CLI

quivvo is the command-line client for Quivvo machines. It needs Node.js 22 or later and has no dependencies.

command -v quivvo >/dev/null || npm i -g quivvo@latest

npx quivvo@latest works without installing, but leaves nothing on your PATH, so every later command has to start with it too.

Credentials live in ~/.config/quivvo/credentials.json (mode 0600). QUIVVO_API_KEY and QUIVVO_API_URL override them. The default API URL is https://tryquivvo.com. The CLI talks to that and, for ssh, cp, push and port-forward, directly to the machine at *.fly.dev, so a restricted network has to allow both.

How do scripts and agents use it?

  • --json on any command prints one JSON document on stdout, errors included, and nothing on stderr. Warnings go inside the document. Following a stream (exec --stream, jobs logs -f) prints one JSON document per line instead.
  • Errors under --json have code, message, retryable, retry_after_seconds, status, exit_code, hint, docs_url, request_id and owner_action. Errors lists the codes. If hint names a step you may take, take it first, such as suspending a machine to make room. owner_action is what only the workspace's owner can do, so pass that on when nothing else works.
  • Remote exit codes pass through. exec, run, ssh, jobs wait and jobs logs -f exit with the remote command's code, which can be 2, 3 or 75 too. Under --json, look for the error key rather than going by the code alone. The one difference: jobs wait keeps 124 for "still running", so a job that hit its own timeout exits 1 there and 124 from jobs logs -f.
  • No terminal. sh opens no shell and waits for nobody: with no key it exits 3 and names quivvo login --agent --no-wait --json, and with a key it gets the machine running and prints the next exec. quivvo sh dev -- uname -a runs that command and exits with its code. ssh does the same, on a machine that exists.
  • Tool timeouts. exec prints output as it's produced, so what ran before a kill is already on stdout, but the kill stops the command. Under --json without --stream, or with --tail-bytes, nothing prints until the end, so get it back with quivvo history and then quivvo output. If it might outlast your tool's timeout (often 120 seconds), use --background. run waits for the whole command too: run --no-wait starts a job instead.
  • Safe retries. A create that fails leaves nothing behind, with one exception: when --wait-setup fails, the machine is there, running and billing, so you can read the script's log. Delete it with quivvo rm. --idempotency-key gets the first answer back within 24 hours, and a different request with the same key is a conflict. --if-not-exists uses the machine if it's already there, and created under --json says which. It won't run a setup script a second time. Repeating rm or snapshot rm succeeds. fork lists partial failures in failed and exits with the first failure's code, so a 5 doesn't mean nothing was made: machines lists the copies it did make, which are running and billing, and hint says how to delete them.
  • Limits. A command that waits for its result stops at 280 seconds, which is also its default. With --background, the default is 3600 and the most is 86400. See recipes.
  • The queue. quivvo queue prints your place, how many have joined from your link, and the link. While a workspace is waiting, whoami shows the same, and whatever creates a machine exits 5 with the code queued. Pass the link to the owner and stop: retrying won't help.
  • Keys. quivvo login gets a key that drives machines. keys and claim also need keys:write, which you tick when you approve the sign-in. It's off by default because anyone can start a sign-in and send you the link. A key with only machines:read can cp a single file off a running machine and nothing else of what opens a connection: copying a directory or uploading, push, ssh and port-forward need machines:write.
  • Secrets. A bare --env KEY forwards your local value, and it stays out of history. The command text doesn't.
  • stdin. A command gets none unless you pass --stdin, even from a pipe, because a stdin that never closes would hang it. When a pipe or file was given without it, the CLI warns that it was ignored. --stdin takes up to 1 MB.
  • Binary data. exec is for text. Its plain stdout and --stdin carry binary bytes unchanged, but --json and --tail-bytes can't, and say so in warnings. Use cp and push for files.
  • push and cp differ. push sends what git tracks and new files, with no .git and none of what usually holds a credential, tracked or not: .env and its variants such as .env.* and prod.env, .ssh, .aws, *.pem, .netrc, *.tfvars, .mcp.json, cloud service-account files, and the logins in .claude and .codex. That's a list of names, so it can't catch everything. It says what it left out, and --include sends it. cp sends everything you point it at, and copying into an existing directory nests, like cp. Bringing a directory back from a machine leaves out .git/hooks, because an agent had root there and the next git commit on your computer would run them. .git/config, .envrc and .vscode/tasks.json can run commands too, so read what you bring back before you work in it.

Exit codes

Code Meaning
0 ok
1 something else went wrong
2 the command line or a request was wrong
3 not signed in, or this key isn't allowed to
4 no such machine, job, snapshot or file
5 a plan limit, or out of credit: the error says who can fix it
75 nothing changed and it may work later: run it again
76 login: the code expired; run it again for a new one
77 login: the workspace's owner denied it
124 a wait or the command's timeout ran out
125 exec or run itself failed, so the command's result is unknown
otherwise exec, run, ssh, jobs wait and jobs logs -f exit with the remote code

Environment

Variable Meaning
QUIVVO_API_KEY Use this key instead of the saved credentials
QUIVVO_API_URL API to talk to (default https://tryquivvo.com)
QUIVVO_DEBUG Log every request to stderr
NO_COLOR Plain output

Commands

<m> is a machine's name or id. Every command also takes --json and --help. Run quivvo <command> --help for what each flag does, or quivvo help --json for all of it as data.

quivvo sh [name] [--template T] [--size S] [--region R] [-- cmd...]
    Sign in if needed, create or resume a machine (default "dev"), open a shell
quivvo init [--client claude|codex|cursor ...] [--yes] [--dry-run] [--project] [--inline-key] [--no-skill] [--no-mcp]
    Set up the skill and the MCP server for the AI tools on this computer (shows a checklist first)
quivvo login [--agent] [--name N] [--scope S ...] [--no-wait] [--no-browser]
    Sign in with a device code (opens the browser unless --agent)
quivvo logout [--revoke]
    Remove the saved credentials
quivvo whoami
    Show the workspace, plan limits, and what this key may do
quivvo queue
    Show your place in the queue and your invite link
quivvo create [--name N] [--template T] [--size S | --vcpus N --mem-mib M] [--disk G] [--region R] [--snapshot S] [--setup-file PATH] [--env K[=V] ...] [--auto-suspend MIN] [--delete-after MIN] [--if-not-exists] [--idempotency-key KEY] [--wait-setup] [--no-wait]
    Create a machine (waits until it's running)
quivvo ls [--all] [--state S]
    List machines
quivvo get <m>
    Show a machine (with its daemon's status when it's running)
quivvo exec <m> [--stream] [--background] [--cwd D] [--env K[=V] ...] [--timeout SEC] [--tail-bytes N] [--stdin] [--] <cmd...>
    Run a command and print its output; exits with the command's exit code
quivvo run [--push DIR] [--timeout SEC] [--keep] [--no-wait] [--cwd D] [--env K[=V] ...] [--template T] [--size S] [--region R] [--name N] [--] <cmd...>
    also: --include PATTERN, --exclude PATTERN
    One command on a throwaway machine: create, optionally push, run, delete
quivvo ssh <m> [--print] [--cwd D] [-- cmd...]
    Interactive shell on the machine; --print shows an OpenSSH command
quivvo ssh-proxy <m> [--port N]
    Pipe stdin/stdout to the machine's port 22 (an OpenSSH ProxyCommand)
quivvo cp <src> <dst> [--exclude PATTERN ...]
    Copy files or directories to or from a machine (`m:/path` marks the machine side)
quivvo push <local-dir> <m>[:path] [--exclude PATTERN ...] [--include PATTERN ...]
    Upload a directory, honouring .gitignore (default: ~/<its name> on the machine)
quivvo jobs ls <m>
    List a machine's jobs, newest first
quivvo jobs logs <m> <job> [-f] [--tail N] [--offset BYTES]
    Print a job's output (stdout and stderr interleaved)
quivvo jobs wait <m> <job> [--timeout SEC]
    Wait for a job to end; exits with its exit code
quivvo jobs kill <m> <job> [--signal TERM|KILL|INT]
    Signal a job's process group (default TERM)
quivvo ps <m>
    List processes on a machine
quivvo history <m> [--limit N]
    Commands run with exec, newest first (works while stopped)
quivvo output <m> <exec-id>
    Print a past exec's output (the first 16 KiB and last 48 KiB of each stream)
quivvo logs <m> [--lines N]
    Boot and daemon log; of a machine that isn't running, its state, error and the provider's events
quivvo status <m>
    The machine daemon's status: uptime, idle time, sessions, memory, disk
quivvo suspend <m> [--no-wait]
    Suspend a machine (keeps memory and processes when it can; otherwise it stops)
quivvo resume <m> [--no-wait]
    Resume a suspended machine
quivvo stop <m> [--no-wait]
    Stop a machine (the disk is kept)
quivvo start <m> [--no-wait]
    Start a stopped machine
quivvo restart <m> [--no-wait]
    Power-cycle a machine
quivvo rm <m> [--yes]
    Delete a machine and its disk (asks first)
quivvo snapshot save <m> --name N [--no-wait]
    Save a machine's disk as a snapshot
quivvo snapshot ls
    List snapshots
quivvo snapshot rm <name> [--yes]
    Delete a snapshot (by name or id)
quivvo restore <m> --snapshot N [--yes] [--no-wait]
    Replace a machine's disk with a snapshot's (same id, name and URL). What's on /data now is lost, and anything installed outside /data may be too
quivvo fork <m> [--count N] [--name N] [--no-wait]
    Copy a machine's disk into new ones
quivvo port publish <m> <port> [--private]
    Serve a port on the machine's URL
quivvo port unpublish <m> <port>
    Stop serving a port
quivvo port ls <m>
    List published ports
quivvo port-forward <m> <remote-port>[:<local-port>]
    Forward a local port to a port inside the machine (private, no publishing)
quivvo desktop <m> [--stop] [--no-browser]
    Start a machine's desktop and open it in the dashboard (--stop stops it)
quivvo auto-suspend <m> <minutes>
    Suspend after this many idle minutes (0 turns it off)
quivvo resize <m> [--vcpus N --mem-mib M] [--disk G]
    Change CPU and memory (while stopped) or grow the disk (any time)
quivvo usage
    This period's cost, credit balance and per-machine usage
quivvo catalog
    Templates, sizes and regions you can create machines with
quivvo keys ls
    List API keys
quivvo keys create --name N [--scope S ...]
    Create an API key (the secret is printed once)
quivvo keys rm <id>
    Revoke an API key
quivvo feedback <message...> [--machine M] [--context TEXT]
    Tell us something went wrong or could be better
quivvo claim <CODE> [--yes] [--deny] [--scope S ...]
    Approve (or --deny) a sign-in code shown by `quivvo login --agent`
quivvo mcp init claude|cursor|codex [--project] [--inline-key]
    Write the hosted MCP server into an AI tool's config, referring to $QUIVVO_API_KEY
quivvo mcp start
    A stdio MCP server that forwards to the hosted one
quivvo agent prompt <m> [--provider claude|codex] [--cwd D] [--env K[=V] ...] [--model M] [--continue RUN] [--name KEY] [--timeout SEC] [--budget USD] [--] <text>
    Start a run; prints its id
quivvo agent wait <m> <run> [--timeout SEC]
    Wait for a run: 0 done, 1 the agent failed, 124 still running
quivvo agent result <m> <run> [--diff]
    Print what a run came back with; exits like `agent wait`
quivvo version
    Print the CLI version
Updated 8 November 2026

Give your agent a computer

npx quivvo@latest sh
Get a free VM