Skip to main content
The exact rules, for looking things up. Boxes explains the ideas. What a single command does is in the CLI reference, and timers and box sizes are in Configuration.

Plan limits

  • The box cap counts boxes, awake or asleep. At the cap, make, exec, and dev fail with box_limit. tesser rm an idle box or upgrade.
  • An admin sets the spend limit with tesser spend-limit or on the dashboard’s Billing tab. At the limit, the org’s awake boxes sleep, and launches and wakes fail with credit_exhausted until the limit is raised or the month resets.
  • Pool boxes belong to no repo. Any make, exec, or dev in the org claims one of its size.

Snapshots

What a box gets from one

  • A snapshot is a disk image of a repo taken after every service’s setup ran. It holds /home/ubuntu, /var/lib/docker, and /var/lib/containerd.
  • A repo’s current snapshot is the newest ready one of a commit on the default branch. Every new box in the repo starts from it.
  • A claimed pool box mounts the snapshot at claim time and starts while the rest fills in. If the mount fails, the box starts without it.
  • The box reads the image as it needs it while the rest streams in, so it does not wait for the whole disk.
  • The first sync sends only what changed since the snapshot’s commit, and package stores and node_modules are already in place, so the first install is incremental. Without a snapshot, the first setup installs from scratch. Boxes never copy installs from each other.
  • Anything setup installed outside /home/ubuntu and Docker’s data is not on the new box, except the manifest’s apt packages, which the box reinstalls on every start.
  • If a box cannot read its snapshot, it starts on an empty home directory.
  • tesser dev --snapshot <snapshot_id> starts a new box from a specific snapshot of the repo. tesser dev --no-snapshot starts one without any.

Seeing which snapshot a box has

  • tesser ls --json shows the snapshot each box started from.
  • tesser status <box_id> prints it on a snapshot line as <snapshot_id>@<sha> (<age> old), with the first 12 characters of the commit, or - for a box without one. A claimed pool box shows - until its snapshot is mounted.

Building one

tesser snapshot create builds a snapshot of HEAD, or of --sha <commit>:
  1. A builder box launches at the largest size any of the repo’s services uses, starting from the repo’s current snapshot when there is one.
  2. It syncs the commit and runs each service’s setup with the values from tesser env set.
  3. It deletes ~/.tesser, ~/.ssh/authorized_keys, ~/.ssh/known_hosts, ~/.bash_history, ~/.gitconfig, ~/.git-credentials, ~/.netrc, ~/.config/tesser, ~/.config/gh, ~/.aws, ~/.claude, ~/.claude.json, and ~/.codex, so no one’s credentials reach other people’s boxes.
  4. It captures the disk.
  • A repo builds one snapshot at a time, and a build that runs past its time limit fails.
  • The builder needs room under the org’s box cap.
  • --wait streams the setup log from the builder over SSH until the snapshot is ready or fails. The log stays on the builder, so --wait is the only way to read it. Without --wait, tesser snapshot show <snapshot_id> shows the build’s state.

Automatic builds from tesser dev

When tesser dev runs for a service without --snapshot or --no-snapshot, it checks the repo’s snapshots once it has a box, against the default branch’s tip as your clone last fetched it (origin/HEAD, else origin/main, else origin/master).
  • If that commit needs a snapshot, it starts tesser snapshot create in the background and prints tesser: building a snapshot of <repo> at <sha> in the background on stderr, with the path of its log (snapshot-build.log in the state directory).
  • tesser dev does not wait for the build, and Ctrl-C does not stop it.
  • It builds nothing when:
    • no service has a setup at that commit
    • a snapshot of the repo is already building
    • the repo has a current snapshot that is not stale
    • the last build at that commit failed (it tries again once the tip moves)
    • the newest ready snapshot of the default branch is at a commit the tip does not contain
  • A background build leaves room for one more box under the box cap. It stops quietly when the org has no room, is out of credit, has reached a plan limit, or keeps no snapshots.
When tesser dev waits for the server and setup took 3 minutes or more, it prints a hint after the box id:
  • If the service has no shared instance, it names the tesser make <service> --ensure-running <sha> command that would share one box’s setup with everyone.
  • If the default branch’s tip needs a snapshot, it names the tesser snapshot create --sha <sha> command that builds one, or says one is already building.

Listing, staleness, and removal

  • Inside a repo, tesser snapshot ls lists that repo’s snapshots and prints this repo (<repo>): none when it has none. Outside a repo, it lists the org’s. --all lists every repo’s, and --removed includes removed and purged ones.
  • A snapshot of a commit not on the default branch is marked branch. New boxes start from it only with --snapshot.
  • tesser env set marks the repo’s snapshots stale, because their setup ran with the old values. A stale snapshot is still used until a new one is built.
  • Tesser keeps the newest ready snapshot of the default branch and the newest of other branches, and deletes older ones once no box is reading from them. tesser snapshot rm removes one sooner, and boxes made from it keep running.
  • An org that runs boxes in its own AWS account keeps snapshots in its own S3 bucket and has none until it names one.

Sleep and wake

What counts as idle

A box is in use while any of these is true:
  • a visible browser tab is showing it
  • something other than a browser sends it requests (curl, a script, a test run)
  • a CLI command targets it
  • an SSH session is open where someone is typing, or something is running or printing
A background tab, or an open shell with nothing running, does not count.
  • Traffic between boxes does not count on its own, but a box in use keeps the boxes it depends on awake, so a server and its database idle together.
  • A kept box keeps the boxes it depends on awake while its server runs. Once its server stops, they go back to their own idle clock.
  • When an instance box starts or wakes, it wakes the boxes it depends on that you own or that are the org’s shared instances. A dependency wired to a teammate’s box is never woken this way.

What survives sleep

  • A sleeping box keeps its disk and its id. Any command that targets it wakes it first, and opening it on localhost shows a Wake button.
  • Sleep is hibernation: memory is written to disk, and on wake processes carry on where they stopped. A dev server keeps its state and does not run setup again.
  • After a wake, boxd waits for the health check (see Timers). A server that does not answer in time is started again from setup.
  • Connections between boxes close on wake, so your app has to reconnect to its dependencies.
  • These boxes get a plain power-off instead, and their dev server starts again on wake:
    • a box still streaming in its snapshot
    • a box put to sleep in its first minute
    • a box whose image or AWS settings do not support hibernation

Finding and inspecting boxes

tesser ls

  • Piped, one line per box: kind, service, size, power state, whether it is online, when it was made, its IP address, and the local worktree it was made from.
  • In a terminal, a live table with service, worktree, owner, size, state, created, idle, and box columns. Press a column’s number or click its header to sort, press / to fuzzy search, and click a box twice to use it on localhost.
  • A worktree shows as its folder name, followed by its repository in parentheses when the two differ, such as src (zulip). The repository name is the last part of the worktree’s origin URL, or the repository’s folder when there is no origin.
  • tesser status <box_id> adds the instance id, IP addresses, and when it was last active.

tesser ssh and tesser exec

When a command after -- exits 127 and its command word still has quotes or spaces in it, both print one extra line on stderr explaining why. The exit code is passed through unchanged.

tesser util

  • The box samples CPU and memory every second and reports a summary each minute.
  • Each row is one step: mean, 95th percentile, and peak CPU as a percentage of all the box’s vCPUs, plus mean, peak, and total memory in GiB.
  • --since takes values like 30m, 12h, or 90d, and defaults to an hour. Longer windows use wider steps so the output stays at 360 rows or fewer.
  • History is kept 90 days and is gone once the box is removed.
  • --json also includes CPU and memory pressure.