What is it?

Machine is a deterministic execution environment, a place to run your programs where nothing is left to chance. It controls time, interrupts, random numbers and input from devices, and it records all of them while your program runs. Replay the recording and the program does exactly what it did the first time, instruction for instruction.

That gives you three things to work with:

  • Record. Every run keeps a log of everything that could have turned out differently.
  • Replay. Play the log back and the run repeats itself. If it failed once, it fails every time.
  • Branch. Stop at any point, branch from it, and try something else. The original run stays as it was.

If you have ever chased a test that fails one run in fifty, you know why this is handy. Record the failing run once, then replay it as often as you need.

You work with Machine through a client called machine, or from Python using the SDK. To begin, install the client. It is one command.

Concepts

There are only a few of them, and the rest builds on them.

Disk. What a machine boots. It is your program (or a whole application) packaged with a small Linux system. You build one with machine disks build.

Session. The vCPUs and RAM you allocate, which one or more runs execute in. It is what you are billed for, from the moment you create it until you delete it, so delete it when you are done.

Run. A deterministic machine booting a disk inside a session. Every run has a name. Nearly every action is something you do to a run.

Entry. A run writes its history as a log. Each item in the log is an entry, so for example, entry #42 is always the same point in that run.

Checkpoint. A saved copy of the whole machine at one point in the log. Every run has checkpoint 0, taken at boot, and you can add your own and name them.

Branch. A run that starts from another run at some point. It shares history with its parent up to that point and has its own after that. You create one with machine fork or machine go. Runs that grow out of one another form a tree.

Mark. A second name for a branch, given after the fact. The generated name says where a branch started. A mark says what it turned out to be, like crashed.

Points

A lot of commands take a point, which is a place in a run’s history. You can name one in several ways.

Point Means
^booted the checkpoint named booted
#42 entry 42
1.5s a time on the machine’s clock
-10ms ten milliseconds of machine time back from where the run is now
now where the run is now
parent the point this run branched from
root the start of the tree
%crashed the branch with the crashed mark

A branch can only start from a checkpoint. machine go takes any of these points, and if the one you name isn’t a checkpoint, it starts the branch at the nearest checkpoint before it and replays the rest of the way for you. A machine time such as 1.5s or -10ms names the first entry at the last machine time at or before the one you asked for.

Getting started

The client

You work with Machine through a client called machine. Install it with one command:

1
curl https://testflows.com/machine/install -fsS | bash

That downloads a single binary named machine over HTTPS, checks it against the SHA-256 checksum published next to it, and puts it in ~/.local/bin, with machine-env beside it. If the checksum doesn’t match, nothing is installed. If that folder isn’t on your PATH yet, the installer says so and shows the line to add for your shell. Run the same command again whenever you want to update.

Supported systems. The client runs on Linux, x86_64 and arm64, and on Macs with Apple Silicon. It needs only the OpenSSH client, which macOS and most Linux systems already have, and the installer never asks for root. If ssh is missing, install it with your package manager, for example sudo apt install openssh-client on Debian and Ubuntu. It doesn’t run on Intel Macs or natively on Windows; on Windows you can use it inside WSL. On an ARM machine, a Mac with Apple Silicon or arm64 Linux, the disks you build must still hold x86_64 programs; see building disks on an ARM machine. If you need another platform, contact us and tell us which one. The download page has the full list, and a way to check the download yourself.

On a Mac, use machine-env. macOS ships its own /usr/bin/machine, which prints the processor type, so typing machine may not run the client. Putting the client first on your PATH would hide the system command for every other program. machine-env, which the installer writes on every system, starts a shell in which machine is the client and changes nothing else. Its prompt starts with (machine), your usual shell setup is loaded, and exit leaves. It supports zsh and bash.

1
2
machine-env
machine-env -- machine --version

The second form runs one command in that environment and starts no shell, which suits scripts and CI. To see which machine you get, run which machine: ~/.local/bin/machine is the client and /usr/bin/machine is the system’s. If machine --version prints a processor type instead of a version, you are running the system command. On Linux, if another command on your system is also named machine, use machine-env the same way.

To read the installer first, install a specific version or into another folder, check the download by hand, or uninstall, see the download page.

Run machine with no arguments, or with --help, to see every command. Each command has its own --help too, and that is the place to look when a flag here is not enough.

1
2
machine --help
machine fork --help

machine --version prints the client’s version and its license. Include the version when you report a problem.

1
machine --version
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  ---- o o o ----
| o o |
| 1 o 10010 o 0 |
| o o | TestFlows Machine Client YYYYMMDD-HHMM
--- o o oxx --
/ xx \
/ ^^^ xx \
------------------

Copyright (C) 2026 Katteli Inc. All rights reserved.
TestFlows.com Open-Source Software Testing Framework (https://testflows.com)

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, ...

If something does not work, machine ping checks that you can reach the service and your sessions, and how long the round trip takes.

Signing up

Create your account on the sign-up page, or from the terminal with machine account signup. Then sign in from the terminal.

1
machine login

Machine emails you a one-time code and asks for it. You stay signed in until you sign out with machine logout. machine account show tells you who you are signed in as.

Then set up account storage with machine account provision. The first session cannot be created until this is done.

1
machine account provision

For scripts and CI there is no need to sign in at all. Create an API key and put it in the TESTFLOWS_MACHINE_API_KEY environment variable.

1
machine account api-keys create ci --expiry 90

Your first run

If you have not yet signed up, signed in and set up account storage, do that first: see Signing up.

Here is a whole trip, from nothing to a branch. The disk can come from the hello-world example if you have Docker, or from a small program you compile if you do not.

First, create a session and make it the one your commands use.

1
machine sessions create first --cpus 2
1
2
✔ Session first is running.
Use machine sessions use first
1
machine sessions use first
1
✔ Now using session first in this terminal.

Next, build a disk named hello. Take one of the two paths.

If you have Docker, pull the hello-world example and build the disk from that image. The --platform flag asks Docker for the x86_64 image a machine runs.

1
2
docker pull --platform linux/amd64 testflows/machine-examples:latest
machine disks build --image testflows/machine-examples:latest hello

If you do not, compile hello_world.c as a static executable. machine disks build --binary wraps that one file, so Docker is not needed. For a program that is not static, such as Python or Node.js, see When the program is not static.

1
2
3
4
5
6
#include <stdio.h>

int main(void) {
printf("hello, world\n");
return 0;
}
1
2
gcc -static -o hello hello_world.c
machine disks build --binary ./hello hello

On an ARM machine that binary still has to be x86_64 Linux. See building disks on an ARM machine.

Either way, create a run from the disk. The machine waits for your commands. With --no-daemon it would boot and run to the end by itself.

1
machine create hello-run --disk hello
1
✔ Started hello-run (whUxdR33Zad9Qxc0ASfig)

Now the run is waiting. It only moves when you drive it, and machine run is how you drive it. The --until tasks part means “keep going until Linux is up.”

1
machine run hello-run --until tasks
1
2
3
4
iterations: 16093  run ic: 271990491  checkpoints: 1

VCPU EXIT ID RUN IC RCB TOTAL IC REGS HASH RIP RCX ITER
0 HYPERCALL 16238 271990491 190026 271990491 0x785930775efa707 0xffffffff81f9eeda 43778048 16092

Read the last five lines the machine printed.

1
machine console hello-run -n -5
1
2
3
4
5
futex hash table entries: 256 (order: 2, 16384 bytes, linear)
NET: Registered PF_NETLINK/PF_ROUTE protocol family
thermal_sys: Registered thermal governor 'step_wise'
thermal_sys: Registered thermal governor 'user_space'
cpuidle: using governor ladder

To see the full console log, use -n :. Ranges use the same colon as a Python slice. Lines are numbered from 1 and both ends count, so -n 1: is the first line through the last.

Save the machine’s state so you can come back to it, and start a branch from that point.

1
machine checkpoint hello-run booted
1
2
  Checkpointing: writing state 100%, 1s
✔ Created checkpoint booted at entry 16239
1
machine fork hello-run --at ^booted --name try-1
1
✔ Forked hello-run/try-1 (whUxfLYSFJMDHzKaiBOvH)

The branch starts exactly where booted was taken. Notice its full name, hello-run/try-1. A branch is named after its parent, a slash, and the name you gave it. Look at the tree.

1
machine branches hello-run
1
2
● hello-run (whUxdR33Zad9Qxc0ASfig) [0, ∞)  ← current
╰─ @16239 0.693926690s+1 → ● hello-run/try-1 (whUxfLYSFJMDHzKaiBOvH) [16240, ∞)

When you are done, delete the runs and then the session. A run keeps its log and checkpoints until you delete it, and a session costs money until you do.

1
2
machine delete hello-run --recursive --stop --yes
machine sessions delete first --yes

That’s the whole loop. You ran a program, saved a point in it and branched from there. Everything else in this page is a variation on it.

Disks

A disk is what a machine boots, and machine disks build makes one. You give it exactly one source.

Source What it takes
--binary path a static x86_64 executable, wrapped in a scratch image
--from ref a public image, by name, with the files you add to it
--image ref a Docker image of your own, as a save tar or a name in your local Docker
--compose dir a Compose project directory and the images its services name

Everything a machine runs runs in Docker Compose, inside the machine. A Compose project is one container for each of its services. An image or a binary is a project of one service, and --entrypoint and the arguments after -- are that service’s entrypoint and command. A binary is no exception: --binary wraps the executable in a scratch image, one layer holding that file and nothing else, with no base image, no loader and no libraries, and the machine runs that image as its one service. machine writes this image itself, so --binary needs no Docker on your computer.

Docker and Compose

An image of your own needs Docker. A Compose project also needs Docker Compose. Two sources need neither. machine disks build --binary wraps a static executable and nothing else, so a program you compile needs no Docker. The executable has to be self-contained. The image has no loader and no libraries, so a dynamically linked program cannot run. --from names a public image, and the build pulls it for you.

1
2
machine disks build --image myapp:latest app
machine disks build --compose ./deploy stack

The name defaults to whatever the source is called. Use --entrypoint to choose the executable the service runs. Flags go before the name, and the service’s own arguments go after a --. This one runs data-race with the argument 3:

1
machine disks build --binary ./data-race race -- 3

When the program is not static

A program that is not a static executable needs an image that already contains what it runs: an interpreter, a runtime, its libraries. Public images have those, so name one with --from and put your files in it with --add. It reads like a Dockerfile, FROM then ADD then the command, and you need no Docker for it. The build pulls the image itself, so only your files are uploaded.

A complex application, several services together, is a Compose project.

Python program

Save this as hello.py.

1
print("hello, world")
1
machine disks build --from python:3.12 --add hello.py hello-py -- python hello.py

Node.js program

Save this as hello.js.

1
console.log("hello, world")
1
machine disks build --from node:22 --add hello.js hello-node -- node hello.js

ClickHouse queries

The ClickHouse image has clickhouse local, which runs SQL with no server. Save this as queries.sql.

1
2
3
CREATE TABLE orders (id UInt32, customer String, amount UInt32) ENGINE = Memory;
INSERT INTO orders VALUES (1, 'ada', 30), (2, 'grace', 45), (3, 'ada', 25);
SELECT customer, sum(amount) AS total FROM orders GROUP BY customer ORDER BY customer;
1
2
machine disks build --from clickhouse/clickhouse-server:24.8 --add queries.sql \
--entrypoint clickhouse ch-sql -- local --queries-file queries.sql

The machine’s console shows the result, and the machine ends when the queries do.

1
2
3
app-1  | ada	55
app-1 | grace 45
app-1 exited with code 0

PostgreSQL queries

The PostgreSQL image starts a server, and it needs a password before it will. --env sets a variable in the service’s environment. The image runs every .sql file in /docker-entrypoint-initdb.d when the server first starts, so add the queries there. Save them as queries.sql.

1
2
3
CREATE TABLE orders (id int, customer text, amount numeric);
INSERT INTO orders VALUES (1, 'ada', 30), (2, 'grace', 45), (3, 'ada', 25);
SELECT customer, sum(amount) AS total FROM orders GROUP BY customer ORDER BY customer;
1
2
machine disks build --from postgres:17 --env POSTGRES_PASSWORD=secret \
--add queries.sql:/docker-entrypoint-initdb.d/ pg
1
2
3
4
5
6
7
8
9
app-1  | /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/queries.sql
app-1 | CREATE TABLE
app-1 | INSERT 0 3
app-1 | customer | total
app-1 | ----------+-------
app-1 | ada | 55
app-1 | grace | 45
app-1 | (2 rows)
app-1 | 2026-03-01 00:00:50.435 UTC [1] LOG: database system is ready to accept connections

The server keeps running after the queries, so this machine does not end by itself. To run queries and have the machine end, use the PostgreSQL project. The disk holds each --env value as written, and a value is not changed on the way: a $ in it arrives as a $.

Both ran in a machine with 1024MB of memory. A server that other services connect to is a Compose project.

Where added files go

--add puts a file or a directory in the image, as one more layer on top of the public one. You can repeat it.

You write It lands at
--add hello.py the image’s working directory, as hello.py
--add conf the working directory, as the directory conf
--add hello.py:/app/ /app/hello.py
--add hello.py:/app/main.py /app/main.py
--add conf:/etc/app/ /etc/app/conf, the directory inside /etc/app
--add conf:/etc/app /etc/app, the directory’s contents

The rule is the one cp -r follows: a destination that ends in / is a directory the path goes into, and any other destination is what the path becomes. A Dockerfile ADD of a directory copies its contents; the last row is how you write that here.

A script that was executable still is. --add works with --binary too, for a program that reads a file beside it.

The image is the one the name pointed at when you ran the build. The build records its digest and pulls exactly that, so a tag that moves later does not change your disk. You can name a digest yourself, as python@sha256:… or the python:3.12@sha256:… that docker pull prints.

The disk’s size defaults to what the image needs plus a gigabyte. Use --dry-run to see the numbers, and --size for more room.

--from takes public images of up to 5GB as they download. For a private image, a larger one, or one you build yourself, use --image:

1
2
docker build --platform linux/amd64 -t myapp:latest .
machine disks build --image myapp:latest app

An image that needs more than one service, such as a database server with a client, is a Compose project.

Compose projects

Machine-Examples has Compose environments under compose/. Clone it, pull each image for linux/amd64 into the local Docker, then build the disk from the project directory. machine disks build --compose packs those images and the project. The machine runs the project with --abort-on-container-exit, so it ends when a service exits.

1
2
git clone https://github.com/testflows/Machine-Examples.git
cd Machine-Examples

The same project can be run on this computer first, from its own directory, which is how to fix it without waiting for a boot. The build commands below are from the repository root.

Two services project

compose/two-services is a busybox server and a client that fetches a file from it by the service name server. Services in a project reach each other by name, on the network Compose gives the project.

depends_on only orders the start. The client retries, because nothing waits for the server to accept connections. A service that takes time to become ready should use a healthcheck, and depends_on with condition: service_healthy.

1
2
docker pull --platform linux/amd64 busybox:1.36
machine disks build --compose compose/two-services two

To run it on this computer first:

1
2
cd compose/two-services
docker compose up --abort-on-container-exit

ClickHouse project

compose/clickhouse is a ClickHouse server and a client that queries it. Both services use one image, so the disk carries that image once. The client waits until the server is healthy, prints its result, and exits, which ends the project.

The server takes tens of seconds of machine time before it accepts a query, so the project uses a healthcheck rather than a sleep. config.xml in the project replaces the image’s config. It listens on IPv4 only, because the machine’s kernel has no IPv6, and it caps the background pools, because the image’s defaults hang startup in a small machine. CLICKHOUSE_SKIP_USER_SETUP stops the entrypoint from restricting the default user to loopback, which would refuse the client.

The config is mounted from ./config.xml, a path inside the project. A bind mount from outside the project directory is refused. A named volume or a tmpfs is created empty when the machine boots.

1
2
docker pull --platform linux/amd64 clickhouse/clickhouse-server:24.8.14.39-alpine
machine disks build --compose compose/clickhouse ch

PostgreSQL project

compose/postgres is a PostgreSQL server and a client that runs queries.sql against it. Both services use one image. The client prints the results and exits, which ends the project, so this is how to run queries against PostgreSQL and have the machine end.

The client waits until the server is healthy. The healthcheck asks over TCP, because the image first starts a temporary server on its Unix socket alone to set the database up, and a check on the socket would pass before the real server is listening. ON_ERROR_STOP makes a failed query the client’s exit code. queries.sql is mounted from the project directory.

1
2
docker pull --platform linux/amd64 postgres:17
machine disks build --compose compose/postgres pg
1
2
3
4
5
6
7
8
client-1    | CREATE TABLE
client-1 | INSERT 0 3
client-1 | customer | total
client-1 | ----------+-------
client-1 | ada | 55
client-1 | grace | 45
client-1 | (2 rows)
client-1 exited with code 0

Carrying images

A service can start containers of its own, through the machine’s Docker. The images it starts are named by no service this project runs, and a machine reaches no registry, so an image that is not named here is not on the disk.

scale: 0 names an image and starts nothing. The build packs every image the project names, including these, and Compose starts none of them. compose/clickhouse-regression carries the images its suite starts this way. Another image, such as ZooKeeper or MinIO, is another service.

1
2
3
4
5
6
7
8
9
10
services:
regression:
image: regression-runner:local
working_dir: /opt/compose
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./:/opt/compose
carried_server:
image: clickhouse/clickhouse-server:24.8.14.39-alpine
scale: 0

The socket is /var/run/docker.sock. /run/docker.sock is not the machine’s socket, and the build refuses it.

./ is mounted at /opt/compose, the path the machine runs the project from. Compose resolves a bind on the machine’s filesystem, not inside the container that asked for it, so the two paths have to be the same. The images still have to be in the local Docker, under those names, before the build.

What a project must satisfy

Every service names an image with a tag or a digest. An image with no tag is a different image tomorrow, and a service that only has build has nothing to pack. Build the image, then set image.

The images have to be linux/amd64 and already in the local Docker. A logging driver, if the project sets one, is json-file, local, or none.

Building disks on an ARM machine

A machine is an x86_64 computer, so a disk holds linux/amd64 programs, whatever machine you build it on. On a Mac with Apple Silicon or on arm64 Linux, Docker builds and pulls arm64 images by default, and those cannot run in a machine. machine disks build checks every image and refuses one that is not linux/amd64 before uploading anything. Ask Docker for linux/amd64:

1
2
3
docker pull --platform linux/amd64 myapp:latest
docker build --platform linux/amd64 -t myapp:latest .
DOCKER_DEFAULT_PLATFORM=linux/amd64 docker compose -f ./deploy/compose.yaml build

The last one builds a Compose project’s images for linux/amd64; setting platform: linux/amd64 on each service does the same. Docker builds them under emulation, which is slower than a native build but works. For --binary, compile for x86_64 Linux, for example GOOS=linux GOARCH=amd64 go build.

Disk size

By default Machine measures what the disk holds and adds a gigabyte. Use --size to set the size you want. Not sure how big it will be? --dry-run reports what the disk would hold without building it.

To manage your disks:

1
2
3
4
machine disks list
machine disks show app
machine disks rename app app-v2
machine disks delete app-v2

Deleting a disk is permanent. Machine refuses to delete one that a run still boots, and --force deletes it anyway and takes those runs with it. machine disks transfers shows the transfers in progress, and machine disks cancel stops one.

Sessions

Runs live in a session, and you create one with machine sessions create. Set up account storage first with machine account provision. The first session cannot be created until that is done. Pick its size when you create it.

1
machine sessions create ci-1 --cpus 4 --mem 8G

--cpus sets the number of vCPUs, --mem the memory and --storage the disk space. --class picks the hardware class, and the default comes from your plan. The name is optional, so leave it off and Machine makes one up. Machine waits until the session is running and tells you when it is. Add --no-wait if you would rather it didn’t.

Creating a session doesn’t switch you to it. Tell Machine which one to use.

1
machine sessions use ci-1

machine sessions use applies to the terminal you ran it in. For scripts, set TESTFLOWS_MACHINE_SESSION=ci-1 instead, and every command goes to that session. If you only have one session, machine sessions use needs no name at all. After that, every command that needs a machine goes to ci-1. You can send one command somewhere else with -s, for example machine -s ci-2 runs.

You manage the rest of your sessions like this:

1
2
3
4
machine sessions list
machine sessions show ci-1
machine sessions rename ci-1 ci-2
machine sessions delete ci-2

✋ Delete sessions you are not using. A session costs from the moment you create it, whether or not anything is running in it.

Running a machine

Creating a run

machine create makes a run from a disk and boots it. The run’s name goes first and is optional. Without one, Machine makes up a name.

1
machine create app --disk app --cpus 2 --mem 512

The defaults are 1 CPU and 256 MB of memory. A few other options are worth knowing about:

Option What it does
--no-daemon boot and run to the end by itself, instead of waiting for your commands
--pin-cpu n run on host CPU n, for this start only (the default picks the least-loaded)
--like run start from another run’s options, then apply the ones you give
--label name=value attach a label to the run, as many times as you like
--rng, --rng-seed n give the machine random numbers, seeded with n (the default is 0)
--fs-sync write to the disk as the program writes, instead of later
--no-wait return as soon as the request is accepted

Change the seed and the program sees different random numbers. Keep it and it sees the same ones every time. This one is only about the random numbers the machine asks for. For exploring thread schedules there is another seed, on machine run, covered next.

Driving it

A run executes only while you drive it. machine run is the way, and --until says when to stop. (A run created with --no-daemon answers ✗ Machine not in control mode, because the machine isn’t waiting for you.)

1
machine run app --until tasks

Each batch of work ends when the machine’s kernel talks to Machine, so a plain machine run can stop sooner than you expect. With --until, Machine keeps going, batch after batch, until what you asked for is true. If it never gets there, the command exits with status 3.

These are the conditions you can wait for. machine run --until and machine wait --for take the same ones.

Condition True when
running, paused, stopped, halted the machine is in that state
ready the machine accepts commands
tasks Linux is up and its task table can be read, which is before it starts anything in userland
console~REGEX the console output matches the pattern
entry>=N the log has reached entry N
entry=+N N more entries have been recorded
/app:exit the program at /app has exited

The last row is one example of a task condition. There are more of them under Steering programs.

--iters limits how many iterations a drive may spend, and the global --timeout limits how long you are willing to wait. Whichever hits first wins.

Now and then the computer running Machine takes a firmware interrupt in the middle of an entry, which makes that entry’s instruction and branch counts unusable. Machine does not record such an entry. It goes back to the entry before, checkpoints there and records the entry again, so the recording stays one a replay can follow. You see nothing except a short pause. --retry sets how many more times one entry is tried, 2 by default. If they are all interrupted the run stops with Host interrupted the run at entry N. It is not recorded. and the machine rewind command that puts the run back at the entry before.

Trying different schedules

A program that uses threads can behave differently depending on how they interleave. Machine can draw how long each stretch of execution lasts, so every run explores a different interleaving, and --seed makes those draws repeatable.

1
machine run app --until /usr/bin/app:exit --mode step,fast --step 1:200 --seed 1

--mode is how the machine runs: fast, step, step-into or free, or a comma list to draw from. --step and --fast give the length of a stretch in each mode, as a number or a range like 1:200. --strategy sets how the draws are made, random (the default), golden or sweep, and --seed is the seed for the random and golden ones.

Combine that with branches. Fork several from one checkpoint, give each its own seed, and keep the ones that crash. The explore_seeds.py example in the SDK does exactly that.

A run takes checkpoints for you too. --auto-checkpoints N takes one every N entries, and 0 turns them off.

If a drive or a checkpoint is taking too long, machine cancel app cancels whichever one is in progress.

Waiting

machine wait doesn’t drive anything. It watches a run, or several, and returns when a condition holds.

1
2
machine wait --for stopped app
machine wait --for 'console~ready' app

It exits with 0 when the condition is true, 2 on an error and 3 on a timeout, so scripts can tell them apart. Remember that it only watches. Waiting for a condition on a run nobody is driving never finishes unless you set --timeout. With --timeout 0 it checks once, and the message tells you why it failed: The machine was waiting for commands.

Looking at it

Command Shows
machine runs your runs, newest first (-w keeps the list live)
machine describe app the full configuration of a run
machine state app its state
machine now app the last entry it recorded, and the time on the machine’s clock
machine console app what the machine printed (-f follows, -n -20 shows the last 20 lines)
machine entries app the entries in its log
machine ops operations on runs (checkpoints, branches, switches) that are pending, unfinished or failed; one you interrupted with Ctrl-C is still listed
machine debug log app the debug output of a run (machine debug err shows its error and crash log)
machine dump app the machine’s registers, code and other state (--show picks the sections, --entry reads an earlier one)

machine console also takes --entries, if you only want what a few entries printed.

Interactive mode

When you are working on one run for a while, you can open a prompt for it.

1
machine control app

You issue commands to the run as slash commands, like /checkpoint, /branches or /rewind. Most of the commands on this page have one. Type /help to see them all. -c runs a single slash command and exits, as in machine control app -c '/state', and machine control log app prints the history of the commands you have issued.

Stopping and starting

1
2
3
4
5
6
machine pause app
machine resume app
machine stop app
machine start app
machine kill app
machine delete app

pause freezes a running machine and resume lets it carry on. kill ends it abruptly, where stop shuts a run down and keeps everything it recorded. start brings it back at the end of its log. If the run had gone past its last checkpoint when it stopped, Machine replays the recorded tail from that checkpoint and tells you how many entries it replayed. Add --no-recover to make it refuse instead.

start can also take a run back to an earlier point. --at replays from an earlier checkpoint, and --read-only opens a checkpoint without running it at all.

A run remembers the options you created it with: --no-daemon, --single-step, --limit and --backstop-limit. Every start and fork repeats them, so you type them once. start takes the same flags to change them for that start only, so machine start app --daemon gives you a machine to drive on a run created with --no-daemon. fork and detach take them too, and the new run keeps what you gave. --pin-cpu is the exception: no run remembers a CPU, so each start and fork picks the least-loaded one unless you name it.

delete only removes stopped runs, unless you add --stop. It also refuses to remove a run that has branches, unless you add --recursive. And since machine delete --all removes every run, it asks you first.

To continue a run in a different session, publish it first from the session that has it.

1
machine sync app

To have a run or a disk waiting in a session before you need it, preload it. Going the other way, machine offload lists the copies in this session that can be dropped to free space, and -a drops every one that isn’t in use. The durable copy stays, and starting the run brings it back.

1
2
3
machine preload run app
machine preload disk app
machine offload

machine cleanup removes stale runs and other leftovers, and --force also kills machines that have been orphaned.

Checkpoints and branches

Branching is cheap, because a branch shares its parent’s history instead of copying it. That is what makes it practical to try many things from one point.

Checkpoints

1
2
machine checkpoint app booted
machine checkpoints app

The name is optional. Leave it off and the checkpoint is named after the entry it was taken at. Checkpoints are how you mark points you plan to come back to, like “just after boot” or “right before the request”.

A name uses letters, digits, ., - and _, and starts with a letter or digit. It can’t be a number, because numbers name the checkpoints taken without one, and it can’t read as a time, like 10ms, because a time names a point too.

Machine takes some of its own as well, named after the entry they were taken at (like 10000), so machine checkpoints app lists yours next to those.

Starting a branch

machine fork creates a new branch at a point. The branch runs in its own machine, alongside its parent, and the parent keeps running.

1
2
3
machine fork app                            # at the current point
machine fork app --at ^booted # at a checkpoint
machine fork app --at ^booted --name try-1 # and give it a name

Give a branch a name with --name. If you don’t, it is named after the entry it landed on. Add --no-start to create the branch without starting it.

A branch’s full name is its parent’s name, a slash, and the name you gave it. So machine fork app --name try-1 makes app/try-1, and that full name is what you use in every other command. machine lineage try-1 says Run not found, and machine lineage app/try-1 works.

Moving around

machine fork gives the new branch a machine of its own and leaves you where you were. go, switch and rewind work in place instead: they change the machine you are on. detach is the odd one out, and is explained below.

Command Does
machine go app ^booted creates a branch at a point and switches this machine onto it, in place
machine switch app app/try-1 moves the machine onto a branch that already exists, and never creates one
machine detach app --at ^booted copies a point into a new root, a self-contained run in a tree of its own
machine rewind app to ^booted puts the run back at a point and throws away what came after

go is the one you will use most. It creates a branch the way fork does, but it doesn’t start another machine. It moves the one you are on, in place.

1
machine go app ^booted --name g1
1
✔ Forked app/g1 (whUxiqseEZ7Si8R4osT8K) at entry 16239 and switched onto it.

After a go or a switch, the run you moved away from is listed as switched and no longer running. machine start app brings it back at its tip, and tells you how many entries it had to replay to get there. The machine you moved is now the new branch, so talk to it by that name: app/g1.

A switch needs a branch that isn’t running somewhere else. Pointing it at a branch that is gives ✗ Already running, so stop that one first or create it with --no-start.

rewind changes the run itself, not a copy of it. Use it when you want to replay from an earlier point. The run keeps its name. Machine refuses to rewind past a branch that starts after that point, since that branch depends on entries that would no longer exist.

switch takes a branch name, a mark (%crashed), parent or root.

detach is for leaving a tree behind. A branch normally shares its ancestors’ data, which is what keeps it cheap. detach copies everything into a new root that owns all of its data and belongs to no tree but its own. That is much slower than a fork, so only reach for it when you want to get rid of the original.

If you only want a branch to have its own copy of the data, run machine fork --rebase, which is quicker. Add --lean and Machine drops the history it copied, so the runs it came from can be deleted.

Seeing the tree

1
2
machine branches app          # the whole tree this run belongs to
machine lineage app/try-1 # the chain of ancestors back to the root

branches lists the newest 25 by default. --running shows only the ones still running, and --depth keeps one level of the tree.

Marks

When a branch turns out to be interesting, mark it.

1
2
machine marks add app/try-1 crashed
machine marks list app

A mark is unique within its tree, so you can use it anywhere a run is named, written with a percent sign.

1
machine console %crashed

Replay

Replay is what makes a recording useful. A branch can play back what its parent recorded and Machine checks, step by step, that it gets the same result.

1
machine replay app/try-1

Replay is strict. An entry that does not reproduce what was recorded means the run has diverged. Before giving up on it, Machine replays that entry again from the nearest checkpoint before it, twice by default. Use --retry to change that, or --retry 0 to try each entry once. --to stops at a given entry instead of the end.

The host can interrupt an entry too. A firmware interrupt on the computer running Machine makes one entry’s instruction and branch counts unusable. That says nothing about your run, so it is not a divergence. Replay prints Interrupted: entry N, crossing 1: host SMI, goes back to the nearest checkpoint before the entry and replays it again, counted against the same --retry. A recording does the same on its own, see Driving it.

If a run does diverge, machine diff shows how.

1
2
machine diff app/try-1
machine diff app/try-1 --entry 5000 --show regs,stack

With no second argument, diff compares the entry against the run it was replayed from, and says ✔ No differences when they match. Otherwise it prints a hash by default, and --show takes any of the sections in its help: registers, stack, page tables, timers, devices, memory and more. Name another run to compare two recordings. A difference there only means the recordings differ. It says nothing about determinism.

There is also machine play, which plays a log back without checking for divergence. It is for when you want to see what happened, not whether it repeated. By default it plays the parent’s log, so it needs a branch. To play another run’s log, name it: machine play app/try-1 app.

1
2
machine play app/try-1 -n 16240:16243
machine play app/try-1 --include irq random

Steering programs

By default the machine schedules itself. Sometimes that isn’t what you want. A bug that needs one thread to run past another will not show up if the scheduler never picks that order. Machine lets you pick it.

Everything here is recorded, so a replay repeats it exactly.

Looking at tasks

1
machine tasks app

That lists the tasks in the machine, one row per thread, with the process each one belongs to. You will want a thread’s id, or a program’s path, in the commands below. machine syscalls app and machine ints app list the kernel’s system calls and interrupt vectors.

Focus

Machine matches a program by its absolute path in the machine. Tell it which programs to watch and steer.

1
2
machine focus app add /app
machine run app --until /app:exit

You can also focus on a cgroup (cgroup=/path) or a single process (tgid=12). focus remove and focus clear take them off again.

Once a program is focused you can wait for things that happen to it. The task conditions take the form program:what, where what is one of:

What Means
exec the program started
exit the program exited
sched one of its threads was given a CPU
desched one of its threads gave its CPU up
on a thread is running its own code
kernel a thread is running in the kernel
off the thread is not on a CPU at all

Holding threads

1
2
3
4
machine tasks app hold 56         # take thread 56 off the run queue
machine tasks app release 56 # put it back
machine tasks app yield 56 # let its peers go first
machine tasks app slice 56 10us # how long it runs between decisions

Each of these takes effect at the next scheduling decision. If you hold the last thread that could run, there is no next decision coming, so force one with machine irq app preempt --vcpu all.

There is also pin, which places a thread on the vCPUs you choose the next time it wakes. Run machine tasks pin --help to see how to name them.

Two more things you can ask of tasks. machine tasks app exits lists the tasks that have left and how each one ended, and machine tasks app cgroups groups them by cgroup. --focused shows only the tasks you are steering.

Plans

When one rule isn’t enough, write a plan file. A plan is a list of clauses, and each one says when it applies and what to do.

1
2
3
when /test:on       mode step,fast   fast=1:100k step=1:200
when /test:kernel task-sched hold
else mode fast

Machine tries the clauses in order and the last match wins, so a later clause overrides an earlier one. else covers whatever nothing matched.

Each when names a state of a program (/test:on is the program running its own code, and /test:kernel is the program running in the kernel). After that come the things to choose for each stretch of execution it covers, such as the mode, how many instructions to run, or what to do with the scheduler. machine plan --help lists all of them.

1
2
machine plan app plan.txt
machine run app

Interrupts and time

Inject an interrupt by name or number.

1
machine irq app preempt --vcpu 1

The interrupt is queued, then delivered at the next boundary and recorded there, so a replay delivers it at the same place.

Machine time is not the host’s. machine vtime app rate changes how fast it passes, and machine vtime app add pushes it forward without running anything.

1
machine vtime app add 5s

There is no way back. The machine’s clock only goes forward, so to return to an earlier time, branch there with machine go app 1.5s.

Reading the disk

You can read files from a run’s disk without touching the run. Reads are pinned to a checkpoint, so they never change how the machine executes.

1
2
3
machine artifacts ls app /var/log
machine artifacts cat app /var/log/app.log
machine artifacts cp -r app /results ./results

Add @ref to the run to read the disk as it was at a checkpoint or an entry.

1
2
machine artifacts cat app@booted /etc/hostname
machine artifacts ls app@4200 /tmp

Account and billing

Most of what the account page on the website does, the client does too.

Command Does
machine account show shows your account
machine account provision sets up account storage, which the first session needs
machine account credits shows your usage credits
machine account activity lists what they were spent on
machine account products list lists what is for sale
machine account buy usage opens checkout for a pack of usage credits, sized in euros as listed by products
machine account buy plan subscribes to a plan, or switches tier if you already have a paid one
machine account upgrade, machine account downgrade moves you to a higher or lower plan
machine account cancel cancels the subscription: the plan stays until it renews, then drops to Free, and your usage credits stay
machine account invoices lists your invoices
machine account orders lists your purchases, and resumes or cancels a pending checkout
machine account payment updates your payment method
machine account portal opens your billing settings
machine account api-keys creates, lists, deletes and sets the expiry of API keys
machine account devices lists where you are signed in (--revoke signs one out)
machine account email changes your email address
machine account close closes the account, in two steps (--cancel stops it)

payment, portal and cancel open a browser. Add --link to print the address instead.

How credits are granted and used is in the usage credit terms.

Runs, logs and disks take up space in your account. machine storage show says how much, and machine storage prune frees the space that deleted runs and disks left behind. machine storage prune --status shows the current or last prune.

Scripting

Everything the client does can be done from a script.

  • -o json prints machine-readable output. -o raw prints text without formatting values. Put the flag before the command, like machine -o json runs.
  • -q turns off progress output and spinners.
  • --timeout s gives up after that many seconds. It goes before the command too. The default is no limit, and 0 means check once.
  • --no-colors turns off colors.
  • Waiting commands exit with 3 on a timeout and 2 on an error.
1
machine -q --timeout 300 run app --until tasks || echo "boot did not finish"

Commands

machine --help lists these commands, in this order. The options apply to every command. Add --help after a command to list its own options. Where a command takes further commands, those follow it, so each one has a heading.

Options

Option Does
-o, --output Output format: text, wide, raw, or json. The default is text. Wide adds columns to list output.
--version Show version and license.
--no-colors Disable terminal color highlighting.
--debug Show the messages exchanged with the service.
--api-url Cloud API base URL. Overrides TESTFLOWS_MACHINE_API_URL and the default.
-s, --session Target session, by name, id, or unique prefix.
--timeout Give up after S seconds, for commands that wait. The default is no limit. 0 means check once.
-q, --quiet No progress or spinners.

machine runs

List runs. See Looking at it.

Lists runs from the durable catalog, newest first.

The listing shows the newest 25. Use --limit for a different page size. Naming a run limits the listing to its tree. Use --roots for one row per tree, and -o wide or --no-trunc for full run ids.

1
2
3
usage: machine runs [-h] [-s [^]state] [--running] [--roots] [--label name=value] [--flag name[=value]] [--disk name|id] [--parents mode] [--synced | --not-synced] [--since date|age] [--until date|age] [--sort field] [--reverse] [--limit n]
[--offset n] [--no-trunc] [--size] [-w]
[root]
Argument Does
root Show one tree, by its root
Option Does
-s, --state [^]state Filter by state: running, paused, recovering, read-only, stopped, killed, terminated, died, failed (repeat = OR; ^ excludes)
--running Show only running runs, the same as -s running
--roots One row per tree instead of every run, with its running N/M
--label name=value Filter by a label (repeatable; matches all)
--flag name[=value] Filter by a run flag (repeatable; matches all)
--disk name|id Filter by the disk a run boots
--parents mode Filter by where a run reads data parents: local, remote
--synced Show only runs published to shared storage
--not-synced Show only runs with nothing published
--since date|age Since a date or age
--until date|age Until a date or age
--sort field Sort field (default: created)
--reverse Reverse sort order
--limit n Show at most n runs (default: 25)
--offset n Skip the first n runs
--no-trunc Show full run ids
--size Measure and show size
-w, --watch Watch for changes

machine ops

List operations. See Looking at it.

Lists operations on runs and how they finished.

The listing shows the newest 25. Use --limit for a different page size. Shows pending, unfinished and failed by default; use -s done for the rest. A rewind left unfinished is owed by its run, and the run’s next start finishes it. An operation outlives the command that started it, so one interrupted with Ctrl-C is still found here by its id. For work on the account’s repository, see machine storage ops.

1
usage: machine ops [-h] [--op-id id] [-s [^]state] [--type type] [--since date|age] [--until date|age] [--limit n] [--offset n] [--no-trunc] [-w] [run]
Argument Does
run Show operations on one run
Option Does
--op-id id Show one operation, by its id (ignores -s and --type)
-s, --state [^]state Filter by state: pending, unfinished, done, failed (default: pending, unfinished, failed; repeat = OR; ^ excludes)
--type type Filter by kind: checkpoint, branch, switch (repeat = OR)
--since date|age Since a date or age
--until date|age Until a date or age
--limit n Show at most n operations (default: 25)
--offset n Skip the first n operations
--no-trunc Show full run ids
-w, --watch Watch for changes

machine describe

Describe a run. See Looking at it.

Show full configuration and details of a run.

1
usage: machine describe [-h] run
Argument Does
run Run name

machine state

Get run state. See Looking at it.

Show the current state of a run.

1
usage: machine state [-h] run
Argument Does
run Run name

machine now

Show where a run is. See Looking at it.

Shows where a run is.

Prints the entry the run last recorded, the vtime it carries and the position within that vtime, then the vCPU that ran and its RIP. The vtime and entry VALUES are what --at accepts; the line around them is not an entry. Nothing moves.

1
usage: machine now [-h] run
Argument Does
run Run name

machine create

Create a new run. See Creating a run.

Creates a new run and brings it up.

The machine waits for commands; --no-daemon runs it to completion instead. The run keeps its run options, --daemon, --single-step, --limit and --backstop-limit, and every later start and fork repeats them unless one names others. Waits until the machine accepts commands. Use --timeout to bound the wait, or --no-wait to return once the request is accepted.

1
2
3
4
usage: machine create [-h] [--like run] [--label name=value] [--no-wait] [--disk disk] [--mem size] [--cpus n] [--mem-type {auto,hugepages}] [--mem-mode {auto,map,lazy}] [--fs-sync | --no-fs-sync] [--fs-dirsync | --no-fs-dirsync]
[--fb type] [--rng | --no-rng] [--rng-seed n] [--daemon | --no-daemon] [--single-step | --no-single-step] [--rdtsc-exit | --no-rdtsc-exit] [--limit n] [--backstop-limit n] [--pin-cpu n] [--vtime-base n]
[--vtime-rate rate] [--vtime-io-cost ns] [--vtime-rdtsc-cost ns] [--vtime-pause-cost ns] [--auto-checkpoints n] [--side-dump | --no-side-dump] [--side-trace | --no-side-trace]
[name]
Argument Does
name Name for the new run, defaulting to a generated one
Option Does
--like run Inherit options from another run, overridden by explicit ones
--label name=value Attach a label, repeatable; an empty value removes an inherited one
--no-wait Return without waiting

Machine options

Option Does
--disk disk Disk to boot
--mem size Memory, MB or with a K/M/G/T suffix (default: 256)
--cpus n Number of CPUs (default: 1)
--mem-type {auto,hugepages} Machine RAM backing: auto (default) or hugepages (local only)
--mem-mode {auto,map,lazy} Cold RAM restore: auto (default), map (4K pages only), or lazy (any backing)
--fs-sync, --no-fs-sync Mount filesystem with sync (default: off)
--fs-dirsync, --no-fs-dirsync Mount filesystem with dirsync (default: off)

Device options

Option Does
--fb type Framebuffer type: vnc, gtk, sdl, or none
--rng, --no-rng Enable virtio RNG
--rng-seed n RNG seed (default: 0)

Execution options

Option Does
--daemon, --no-daemon Wait for commands instead of running to completion (default: on)
--single-step, --no-single-step Run in step mode (applies only for non-daemon runs)
--rdtsc-exit, --no-rdtsc-exit Record an entry for every RDTSC exit (applies only for non-daemon runs)
--limit n Instructions per dispatch, 0 to leave it to the machine (default: 0)
--backstop-limit n Step modes only: machine instructions a dispatch may run without stepping before it ends, 0 for the machine default (default: 0)
--pin-cpu n Pin to this host CPU (default: the least-loaded)
--vtime-base n The vtime base (Unix timestamp, default: 2026-03-01)
--vtime-rate rate The vtime rate (float or num/den, default: 10)
--vtime-io-cost ns IO exit vtime cost in ns (default: 2000)
--vtime-rdtsc-cost ns RDTSC exit vtime cost in ns (default: the IO cost)
--vtime-pause-cost ns Vtime cost of each PAUSE in ns (default: 2000)
--auto-checkpoints n Checkpoint every n entries, 0 for never (default: scaled by memory, 10000 per 512MB)

Run log options

Option Does
--side-dump, --no-side-dump Record a per-entry debug dump in the run log (default: off)
--side-trace, --no-side-trace Record a per-entry execution trace in the run log (default: off)

machine fork

Run a new branch concurrently. See Starting a branch.

Forks a new branch from a run and starts it alongside the parent.

The parent keeps running and the terminal stays attached to it. Without --at, the branch is forked at the current point, reusing a checkpoint there if one exists.

The branch keeps the parent’s run options; --daemon, --single-step, --limit and --backstop-limit replace them, and the branch keeps those instead. Its machine runs on the least-loaded host CPU unless --pin-cpu names one.

Waits until the branch accepts commands. Use --timeout to bound the wait, or --no-wait to return once the request is accepted.

1
2
3
usage: machine fork [-h] [--at point [point ...]] [--name name] [--rebase] [--depth n] [--lean] [--no-start] [--side-dump | --no-side-dump] [--side-trace | --no-side-trace] [--no-wait] [--daemon | --no-daemon]
[--single-step | --no-single-step] [--limit n] [--backstop-limit n] [--pin-cpu n]
run
Argument Does
run Run to fork from
Option Does
--at point [point ...] Point the new branch starts at: ^checkpoint, #entry, a time, -10ms back, now, parent [n], root, or %mark
--name name Name for the new branch, defaulting to an address: the entry it lands on, and for detach the run it copied as well
--rebase Copy the ancestors’ data into the new branch instead of sharing it
--depth n With --rebase, how many root-side ancestors to keep (default 1: the root)
--lean With --rebase, drop the absorbed history so those runs become deletable
--no-start Create the branch without starting it
--side-dump, --no-side-dump Record a per-entry debug dump, whatever the run it comes from records
--side-trace, --no-side-trace Record a per-entry kernel trace, whatever the run it comes from records
--no-wait Return without waiting
--daemon, --no-daemon Wait for commands instead of running to completion (default: the run’s)
--single-step, --no-single-step Run in step mode, for non-daemon runs (default: the run’s)
--limit n Instructions per dispatch, 0 to leave it to the machine (default: the run’s)
--backstop-limit n Step modes only: machine instructions a dispatch may run without stepping before it ends, 0 for the machine default (default: the run’s)
--pin-cpu n Pin to this host CPU (default: the least-loaded)

machine stop

Stop a run. See Stopping and starting.

Stops a running machine gracefully.

Waits until the machine is gone, as with wait --for stopped. A machine that has already exited is a success. Use --timeout to bound the wait, or --no-wait to return once the request is accepted.

1
usage: machine stop [-h] [-a] [--dry-run] [--no-wait] [run]
Argument Does
run Run name
Option Does
-a, --all Stop all running machines
--dry-run List what would be stopped without stopping it
--no-wait Return without waiting

machine pause

Pause a run. See Stopping and starting.

Pauses a running machine.

Waits until it is paused. Use --timeout to bound the wait, or --no-wait to return as soon as the request is accepted.

1
usage: machine pause [-h] [--no-wait] run
Argument Does
run Run name
Option Does
--no-wait Return without waiting

machine resume

Resume a run. See Stopping and starting.

Resumes a paused machine.

Waits until it can accept commands. Use --timeout to bound the wait, or --no-wait to return as soon as the request is accepted.

1
usage: machine resume [-h] [--no-wait] run
Argument Does
run Run name
Option Does
--no-wait Return without waiting

machine kill

Kill a run. See Stopping and starting.

Kills a machine, saving its state first when it still can.

The machine is asked to stop and save, as stop does, and is signalled if it does not go within a few seconds; a machine that did not save has its tip replayed by the next start, and kill says so. Waits until it is gone, the same as wait --for stopped. Already gone is success. Use --timeout to bound the wait, or --no-wait to return as soon as the request is accepted.

1
usage: machine kill [-h] [--no-wait] run
Argument Does
run Run name
Option Does
--no-wait Return without waiting

machine start

Start a run. See Stopping and starting.

Starts an existing run.

A stopped run resumes at its tip. A run that stopped past its last checkpoint replays its recorded tail from that checkpoint to reach the tip, and says how many entries it replayed; --no-recover refuses instead. A branch that was created but never ran starts in a new process, and its parent keeps running.

The machine runs with the run’s own run options; --daemon, --single-step, --limit and --backstop-limit replace them for this start only, and --pin-cpu names the host CPU, the least-loaded by default.

Waits until the machine accepts commands. Use --at to replay from an earlier checkpoint, --read-only to open a checkpoint without replaying, --force to stop a running instance first, --timeout to bound the wait, or --no-wait to return once the request is accepted.

1
usage: machine start [-h] [-f] [--read-only] [--recover | --no-recover] [--at checkpoint] [--parents mode] [--daemon | --no-daemon] [--single-step | --no-single-step] [--limit n] [--backstop-limit n] [--pin-cpu n] [--no-wait] run
Argument Does
run Run name
Option Does
-f, --force Stop a running instance first, then start
--read-only Start read-only at a checkpoint, unable to run or record
--recover, --no-recover Replay a tip that was never checkpointed from the last checkpoint (default: on)
--at checkpoint Checkpoint to replay to the tip from, or to open with --read-only
--parents mode Read data parents from the session (local) or the published tree (remote)
--daemon, --no-daemon Wait for commands instead of running to completion (default: the run’s)
--single-step, --no-single-step Run in step mode, for non-daemon runs (default: the run’s)
--limit n Instructions per dispatch, 0 to leave it to the machine (default: the run’s)
--backstop-limit n Step modes only: machine instructions a dispatch may run without stepping before it ends, 0 for the machine default (default: the run’s)
--pin-cpu n Pin to this host CPU (default: the least-loaded)
--no-wait Return without waiting

machine delete

Delete a run. See Stopping and starting.

Remove a stopped run and its artifacts.

The first operand names the run to remove; -a removes every run, and --disk removes every run that boots one disk, naming it or its id. Use -c -a to resume interrupted delete operations.

1
usage: machine delete [-h] [-a] [--disk name|id] [-r] [-y] [--stop] [-f] [-c] [--full-scan] [--dry-run] [run]
Argument Does
run Run name
Option Does
-a, --all Delete all runs, or cleanup all interrupted deletes (with -c)
--disk name|id Delete every run that boots this disk
-r, --recursive Delete branch and all descendants
-y, --yes Skip the confirmation prompt
--stop Stop running machines before deleting
-f, --force Implies --stop, and deletes a run whose machine is already gone or whose process was replaced
-c, --cleanup Resume interrupted delete operations
--full-scan Scan all branches instead of using index (with -c)
--dry-run List what would be deleted/recovered without acting

machine preload

Preload into a session. See Stopping and starting.

Fetches a run or a disk onto this session.

Takes any run or disk in the catalog, so a run can be brought here to start later, or a disk put in place before a run is created against it. -a fetches the data parents and boot disks this session’s runs need. Already there is fine. Needs a session; the local service has no durable copy to fetch from.

1
usage: machine preload [-h] [-a] {run,disk} ...
Option Does
-a, --all Everything this session’s runs need

machine preload run

Preload a run. See Stopping and starting.

Preloads a run into a session.

Fetches the run from durable storage onto the session so it is there before it is needed. Already there is fine. The disk the run boots is rematerialized with it.

1
usage: machine preload run [-h] [-a] [run]
Argument Does
run Run name
Option Does
-a, --all Every data parent this session needs

machine preload disk

Preload a disk. See Stopping and starting.

Preloads a disk into a session.

Fetches the account disk onto the session so it is there before a start. Already there is fine.

1
usage: machine preload disk [-h] [-a] [disk]
Argument Does
disk Disk name or id
Option Does
-a, --all Every boot disk this session’s runs need

machine offload

Offload session copies. See Stopping and starting.

Lists or drops this session’s local copies.

Bare offload lists this session’s own run folders and disk files that can be dropped to free its storage, and counts the ones in use. The run and disk subcommands drop one kind. -a drops every run and disk not in use. The durable copy stays. Start or preload brings a run folder back; the next start rematerializes a disk. Needs a session; the local service has nowhere to offload to.

1
usage: machine offload [-h] [--runs] [--disks] [-a] {list,run,disk} ...
Option Does
--runs Only session run folders
--disks Only session disk files
-a, --all Every run and disk not in use

machine offload list

List what can be offloaded. See Stopping and starting.

Lists this session’s copies that can be dropped to free its storage.

Only copies on this session, and only the ones not in use; the rest are counted below the table. Use --runs or --disks to show one kind. Omit both, or pass both, to show both.

1
usage: machine offload list [-h] [--runs] [--disks]
Option Does
--runs Only session run folders
--disks Only session disk files

machine offload run

Offload a run. See Stopping and starting.

Offloads a run from a session.

Drops the session’s local folder and frees its storage; the run itself stays on durable storage. Stop does not do this. Start or preload brings the folder back. Does not drop the disk the run boots.

1
usage: machine offload run [-h] [-a] [run]
Argument Does
run Run name
Option Does
-a, --all Every run not in use

machine offload disk

Offload a disk. See Stopping and starting.

Offloads a disk from a session.

Drops the session file; the account disk stays. A live machine that boots it refuses. The next start rematerializes it.

1
usage: machine offload disk [-h] [-a] [disk]
Argument Does
disk Disk name or id
Option Does
-a, --all Every disk not in use

machine branches

Show branch tree. See Seeing the tree.

Shows the branch tree.

The listing shows the newest 25 branches. Use --limit for a different page size. Use --depth to keep one level of the tree, 0 for the roots. Naming any run in a tree, root or branch, limits the listing to that tree.

1
usage: machine branches [-h] [--running] [--depth n] [--since date|age] [--until date|age] [--sort field] [--reverse] [--limit n] [--offset n] [-w] [run]
Argument Does
run Show only this run’s tree
Option Does
--running Show only running branches
--depth n Show only branches at this depth from the root, 0 is the root
--since date|age Since a date or age
--until date|age Until a date or age
--sort field Sort field (default: created)
--reverse Reverse sort order
--limit n Show at most n branches (default: 25)
--offset n Skip the first n branches
-w, --watch Watch for changes

machine lineage

Show branch lineage. See Seeing the tree.

Show the lineage chain for a branch.

1
usage: machine lineage [-h] run
Argument Does
run Run name

machine marks

List marks. See Marks.

Lists marks.

A mark is a second name for a branch, unique within its tree. Written %mark, it names the branch anywhere a run is named. Without a run, every tree’s marks are listed; with one, the marks of that run’s tree. No running machine is needed.

1
usage: machine marks [-h] {list,add,remove} ...

machine marks list

List marks. See Marks.

Lists marks.

Every tree’s, or one run’s tree when a run is named.

1
usage: machine marks list [-h] [run]
Argument Does
run Run name

machine marks add

Add a mark. See Marks.

Puts a mark on a branch.

Marking a branch again with the same word succeeds. A word another branch in the tree already holds is refused and stays with that branch.

1
usage: machine marks add [-h] run mark
Argument Does
run Run name
mark The mark to add

machine marks remove

Remove a mark. See Marks.

Takes a mark off a branch.

Refused when the branch does not carry it.

1
usage: machine marks remove [-h] run mark
Argument Does
run Run name
mark The mark to remove

machine tasks

List tasks in a machine. See Looking at tasks.

Lists the tasks running inside a machine.

One row per task, keyed by tid; tgid is the process. The table is rebuilt whenever the machine forks, execs or exits a task. Use --vcpu to list only one vCPU’s, --focused for only the tasks focus is steering, and cgroups to list the same tasks grouped by cgroup.

hold takes a thread off the runqueue, so the scheduler cannot pick it. yield leaves it runnable but behind its peers, and does nothing when no peer is runnable. release is the inverse of both. All three name a tid.

slice sets how long a thread runs between scheduling decisions, as a duration with a unit: 10us, 0.6ms, 750000ns. Use default for the kernel’s own. It survives release.

Each mark acts at the vCPU’s next scheduling decision. Holding the last runnable thread leaves nothing to run and no decision coming, so a release then needs one forced with irq preempt --vcpu all.

exits lists the tasks that have left the table and how each one ended, newest last. The machine records them as it reaps, so the list survives a checkpoint and a replay; it keeps a bounded number and says how many it dropped.

1
usage: machine tasks [-h] [--vcpu n] [--focused] run {cgroups,exits,hold,yield,release,slice,pin} ...
Argument Does
run Run name
Option Does
--vcpu n Only tasks on this vCPU
--focused Only the tasks focus is steering

machine tasks cgroups

List the tasks grouped by cgroup. See Holding threads.

1
usage: machine tasks run cgroups [-h] [--vcpu n]
Option Does
--vcpu n Only tasks on this vCPU

machine tasks exits

List the tasks that have exited, and how. See Holding threads.

1
usage: machine tasks run exits [-h]

machine tasks hold

Take a thread off the runqueue. See Holding threads.

1
usage: machine tasks run hold [-h] tid
Argument Does
tid Thread id

machine tasks yield

Put a thread behind its peers. See Holding threads.

1
usage: machine tasks run yield [-h] tid
Argument Does
tid Thread id

machine tasks release

Undo a hold or a yield. See Holding threads.

1
usage: machine tasks run release [-h] tid
Argument Does
tid Thread id

machine tasks slice

Set how long a thread runs between decisions. See Holding threads.

1
usage: machine tasks run slice [-h] tid duration|default
Argument Does
tid Thread id
duration|default Duration with a unit, or default

machine tasks pin

Place a thread on one or more vCPUs when it next wakes. See Holding threads.

1
usage: machine tasks run pin [-h] tid set|free|none
Argument Does
tid Thread id
set|free|none vCPUs to place the thread on, such as 1 or 0-2; free for any, none for no vCPU

machine syscalls

List the machine’s system calls. See Looking at tasks.

Lists the machine’s system calls, by number.

A name filters the list to calls whose name contains that text. A task instead -- a tid, tgid, absolute path or cgroup=path -- shows that task’s syscall histogram, counted while its focus entry was armed with focus add +syscalls.

1
usage: machine syscalls [-h] run [name|tid|tgid|path]
Argument Does
run Run name
name|tid|tgid|path Filter the list, or a task to show its histogram

machine ints

List the machine’s interrupt vectors. See Looking at tasks.

Lists the machine’s interrupt vectors and their handlers.

One row per IDT vector the machine has a handler for, the vector in hex as the table is indexed. A name filters to handlers whose name contains that text. A task instead -- a tid, tgid, absolute path or cgroup=path -- shows that task’s interrupt histogram, counted while its focus entry was armed with focus add +ints.

1
usage: machine ints [-h] run [name|tid|tgid|path]
Argument Does
run Run name
name|tid|tgid|path Filter the list, or a task to show its histogram

machine focus

Show or change which tasks are steered. See Focus.

Shows which tasks a machine steers.

An entry is the program’s absolute path, matched exactly, or cgroup= to match the task’s cgroup, or tgid= to name a process by id (a task’s tgid is shown in tasks). Use add and remove to change the list and clear to empty it.

1
usage: machine focus [-h] run {add,remove,clear} ...
Argument Does
run Run name

machine focus add

Steer another program. See Focus.

1
usage: machine focus run add [-h] path|cgroup=path|tgid=n [+syscalls|+ints ...]
Argument Does
path|cgroup=path|tgid=n Program path, cgroup, or process id to match
+syscalls|+ints Count this program’s syscalls or interrupts

machine focus remove

Stop steering a program. See Focus.

1
usage: machine focus run remove [-h] path|cgroup=path|tgid=n
Argument Does
path|cgroup=path|tgid=n Program path, cgroup, or process id to match

machine focus clear

Steer nothing. See Focus.

1
usage: machine focus run clear [-h]

machine irq

Inject an interrupt into a machine. See Interrupts and time.

Injects an interrupt into a machine’s vCPUs.

The vector is a name from the kernel’s irq_vectors.h or a number. preempt is 0xf5, the machine’s reschedule; reschedule is 0xfd, the kernel’s own.

The vector is delivered as given. It reaches the handler the machine’s kernel has installed for it, and a vector with no handler is a spurious interrupt. Vectors below 32 are CPU exceptions and are refused.

The interrupt is queued when the command returns. It is delivered and recorded at the next dispatch boundary, and a replay repeats it there. Step first to choose that boundary.

1
usage: machine irq [-h] [--vcpu n] [--nmi] run [vector]
Argument Does
run Run name
vector Vector: a name such as preempt, or a number 32-255
Option Does
--vcpu n Which vCPU takes it, or all (default: the last entry’s)
--nmi Deliver as an NMI rather than a fixed interrupt

machine vtime

Show or change the machine’s clock. See Interrupts and time.

Shows a machine’s clock.

The rate is nanoseconds of machine time per unit of machine work, and it decides whether a timer is due when a dispatch ends; the instruction limit decides where that is. Both are needed to place a tick. Use rate to change it and add to push the clock forward without running the machine. There is no way back: the clock drives the machine’s TSC, which never goes backwards, so use fork --at to return to an earlier point.

1
usage: machine vtime [-h] run {rate,add} ...
Argument Does
run Run name

machine vtime rate

Set the rate. See Interrupts and time.

1
usage: machine vtime run rate [-h] rate
Argument Does
rate Rate, or num/den

machine vtime add

Push the clock forward. See Interrupts and time.

1
usage: machine vtime run add [-h] time
Argument Does
time Time to add, such as 1s or 250ms; a bare number is nanoseconds

machine checkpoints

List checkpoints. See Checkpoints.

Lists the checkpoints of a branch.

The listing shows the first 25 checkpoints. Use --limit for a different page size. Read from the database, so no active run is required. The durable column reports whether a checkpoint has been synced to durable storage. Use -o json for an object of run_id, hosted and checkpoints rather than a bare list.

1
usage: machine checkpoints [-h] [--durable] [--since date|age] [--until date|age] [--sort field] [--reverse] [--limit n] [--offset n] [-w] run
Argument Does
run Run name
Option Does
--durable Show only durable checkpoints
--since date|age Since a date or age
--until date|age Until a date or age
--sort field Sort field (default: entry)
--reverse Reverse sort order
--limit n Show at most n checkpoints (default: 25)
--offset n Skip the first n checkpoints
-w, --watch Watch for changes

machine cleanup

Remove stale runs. See Stopping and starting.

Remove stale runs and clean up leftovers.

1
usage: machine cleanup [-h] [-f]
Option Does
-f, --force Also hard-kill orphaned machines and remove entries whose machine is already gone

machine debug

View run debug output. See Looking at it.

Shows debug or error output from a run.

1
usage: machine debug [-h] {log,err} ...

machine debug log

Show debug output. See Looking at it.

Shows the debug log of a run.

Use -n to select lines, and -f to follow new output.

1
usage: machine debug log [-h] [-n range] [-f] run
Argument Does
run Run name
Option Does
-n, --lines range Line N, A:B, A:, :B or :, or -N for the last N lines
-f, --follow Follow new output (like tail -f)
1
2
3
4
5
6
7
line selection (-n):
-n -20 Last 20 lines
-n 20 Line 20
-n 100:200 Lines 100 through 200
-n 50: From line 50 to the end
-n :50 Lines 1 through 50
-n : Every line

machine debug err

Show the error and crash log. See Looking at it.

Shows the error and crash log of a run.

Use -n to select lines, and -f to follow new output.

1
usage: machine debug err [-h] [-n range] [-f] run
Argument Does
run Run name
Option Does
-n, --lines range Line N, A:B, A:, :B or :, or -N for the last N lines
-f, --follow Follow new output (like tail -f)
1
2
3
4
5
6
7
line selection (-n):
-n -20 Last 20 lines
-n 20 Line 20
-n 100:200 Lines 100 through 200
-n 50: From line 50 to the end
-n :50 Lines 1 through 50
-n : Every line

machine run

Run vCPU(s). See Driving it.

Runs one or more vCPU iterations.

Use --until to drive batches until a wait predicate holds. console~ means boot-done; halted and activity=idle do not. Use --timeout before the command to cancel the in-flight run. The default is no limit; 0 checks once without batching.

--iters is an upper bound, not a count. A batch also ends at every context switch of a focused task and where a task reaches its own code. Without --until that ends the run; with it the drive continues from the next batch. The response reports how many iterations ran.

An iteration is ONE vCPU dispatch, and every parameter is drawn for it: the mode, the limit, the task marks and the interrupt all apply to the task on that one vCPU. Without --vcpu a scheduling cycle dispatches every vCPU, so it spends up to that many iterations; --iters 100 on four vCPUs buys 100 dispatches, not 400.

With --until, the two are bounds and the first to fire wins: --iters 500 --until on stops at 500 iterations or when a focused task takes a vCPU. Without --iters nothing bounds the drive but --timeout, so --until tasks runs a whole boot. A SPAN bounds nothing either: it draws a batch size per dispatch, so --iters 100:500 --until on keeps going. Exit 3 whenever the predicate is not reached.

--when bounds nothing. It names what must be true for the clause to apply to a dispatch; a dispatch it excludes runs free, up to the crossing into the subject. So --when /test:on --until /test:exit perturbs that program while its own code runs, free runs while it is in the kernel or off the CPU, and stops when it is gone. --iters then counts dispatches the clause applied to. --when takes on, kernel and off: a state, not an edge.

This one clause is the CLI. More than one is a plan file: machine plan , then drive with plain run.

1
2
3
usage: machine run [-h] [--limit span] [--backstop-limit n] [--retry n] [--strategy name] [--seed n] [--vcpu span] [--iters span] [--until predicate] [--rip addr] [--rcx addr] [--target-ic n] [--irqblk] [--rdtsc-exit] [--no-idle-skip]
[--auto-checkpoints n] [--mode list] [--fast span] [--step span] [--step-into span] [--vtime-rate span] [--vtime-add span] [--task-sched choices] [--task-slice choices] [--task-int choices] [--when predicate]
run
Argument Does
run Run name
Option Does
--limit span Default instructions per dispatch under fast, steps under step; fast takes 1000 or more and stops at or after it, late by an amount that depends on what the machine runs; step stops exactly; 0 means no limit under fast and one step under step; default 0
--backstop-limit n Step modes only: machine instructions a dispatch may run without stepping before it ends, 0 for the machine default (default: 0)
--retry n Retries for an entry the host interrupted, default 2; 0 does not retry
--strategy name How spans resolve when they name none: random, golden, sweep (default: random)
--seed n Seed for the random and golden draw strategies (default: 0)
--vcpu span The vCPU, or a span to draw one per dispatch (default: all)
--iters span Iterations, an upper bound; 0 is unlimited (default: 1; with --until, unbounded)
--until predicate Drive until this is true (same predicates as wait --for)
--rip addr Target RIP address
--rcx addr Target RCX value
--target-ic n Target instruction count
--irqblk Block IRQs during run
--rdtsc-exit Record an entry for every RDTSC exit
--no-idle-skip Keep machine time crawling while every vCPU is idle
--auto-checkpoints n Auto-checkpoint every N entries; 0 turns it off, default the machine’s
--mode list Execution mode: fast, step, step-into, free, or a comma list to draw among (default: fast)
--fast span Limit fast draws when it wins (its member), overriding --limit
--step span Limit step draws when it wins (its member), overriding --limit
--step-into span Limit step-into draws when it wins (its member)
--vtime-rate span Vtime rate for this run: n, n/d, or a span of either
--vtime-add span Nanoseconds to push the clock forward before each dispatch
--task-sched choices Draw a scheduler mark per dispatch for the task on that vCPU: keep, hold, yield, drain, drain-all
--task-slice choices Draw that task’s slice per dispatch: keep, default, or a duration range such as 10us:600us
--task-int choices Draw an interrupt for that task per dispatch: none, preempt
--when predicate Apply the clause only while this holds

machine plan

Run a multi-clause plan from a file. See Plans.

Runs a multi-clause plan from a file.

A plan file is a list of clauses. A clause is an optional when predicate and the per-dispatch axes it draws for a dispatch it matches. Clauses are tried in order and the LAST to match supplies each axis, so a later clause overrides an earlier one; else is the clause for a dispatch no when matched. Braces may group a clause, # begins a comment, and blank lines are ignored. A file of - reads the plan from standard input.

Example: when /test:on mode step,fast fast=1:100k step=1:200 when /test:kernel task-sched hold else mode fast

Example, placing thread 56 on vCPU 2 or 3 whenever it wakes, 2 three times as often as 3: else task-pin 56=golden:2[3],3[1]

A clause names one when predicate, then any of these axes. An axis is a comma list of choices, one drawn per dispatch, and [N] is an optional weight on a choice; task-slice is the exception, a colon set that takes no weights:

when apply the clause only while the predicate holds. A predicate is [subject:]state: state on, kernel, off, syscall[=name], int[=vec]; subject /path, tid=N, tgid=N, cgroup=P, or none for any focused task. vcpu=N[,N] ANDs a vCPU set onto it. mode [N],… the modes to draw among: fast, step, step-into, free, or keep. A mode may carry the limit it draws when it wins, =, as in mode step[3],fast[1] step=1:200. free takes no limit. vtime-rate [N],… the machine-time rate: a number N, a fraction N/D, a span of numerators such as 1:10, or keep. vtime-add [N],… time added to the clock before each dispatch: a duration (500, 10us), a span of durations (10us:2ms), or keep. task-sched [N],… a mark for the task on the drawn vCPU: keep, hold, yield, drain, drain-all. task-int [N],… an interrupt for that task: none or preempt. This axis has no keep: none is its do-nothing choice. task-slice ::… that task’s time slice, drawn from a colon set of keep, default and a duration span, in any combination, as in keep:default:10us:600us. task-pin [=][N],… where a thread is placed when it next wakes, each choice a set of vCPUs: 1-3 is all three, 1,2,3 is three choices of one, free is any vCPU and none is no vCPU, which stops it running. A placement policy, not confinement: a thread already queued elsewhere keeps that vCPU until it wakes. = names the thread; without it, the task on the drawn vCPU. none requires =.

A weight makes a choice win more often: mode step[3],fast[1] draws step three times as often as fast. keep is the choice that draws nothing: it holds that axis’s last value, so a clause can leave an axis alone part of the time. A span is a range written lo:hi[:strategy[:step]], strategy one of random, golden or sweep, and one value is drawn from it per dispatch; the mode member limit, vtime-rate, vtime-add, task-slice and the run-level --limit each accept one. limit itself is not a clause axis; it is the default a mode with no member of its own falls back to.

The options on this command are plan-level. --limit and --strategy are the defaults a clause draws with when it names no span of its own, and a clause axis overrides them for the dispatches it matches; a dispatch no clause matches runs free. --seed seeds the draw strategies, and --until, --iters and --vcpu bound and steer the run as on run. The clause axes and when belong in the file, not here. The plan applies to this command’s run only; a later run without it runs without the plan.

1
2
3
usage: machine plan [-h] [--limit span] [--backstop-limit n] [--retry n] [--strategy name] [--seed n] [--vcpu span] [--iters span] [--until predicate] [--rip addr] [--rcx addr] [--target-ic n] [--irqblk] [--rdtsc-exit] [--no-idle-skip]
[--auto-checkpoints n]
run file
Argument Does
run Run name
file Plan file of when and else clauses, and their axes, or - to read standard input
Option Does
--limit span Default instructions per dispatch under fast, steps under step; fast takes 1000 or more and stops at or after it, late by an amount that depends on what the machine runs; step stops exactly; 0 means no limit under fast and one step under step; default 0
--backstop-limit n Step modes only: machine instructions a dispatch may run without stepping before it ends, 0 for the machine default (default: 0)
--retry n Retries for an entry the host interrupted, default 2; 0 does not retry
--strategy name How spans resolve when they name none: random, golden, sweep (default: random)
--seed n Seed for the random and golden draw strategies (default: 0)
--vcpu span The vCPU, or a span to draw one per dispatch (default: all)
--iters span Iterations, an upper bound; 0 is unlimited (default: 1; with --until, unbounded)
--until predicate Drive until this is true (same predicates as wait --for)
--rip addr Target RIP address
--rcx addr Target RCX value
--target-ic n Target instruction count
--irqblk Block IRQs during run
--rdtsc-exit Record an entry for every RDTSC exit
--no-idle-skip Keep machine time crawling while every vCPU is idle
--auto-checkpoints n Auto-checkpoint every N entries; 0 turns it off, default the machine’s

machine vcpu

Get vCPU run state.

Shows the current vCPU run state.

1
usage: machine vcpu [-h] [--vcpu n] run
Argument Does
run Run name
Option Does
--vcpu n The vCPU (default: 0)

machine dump

Dump code, registers, page tables. See Looking at it.

Dump vCPU code, registers, or page tables.

Use -o json to emit one object per --show section, and omit --show for every live section. Use --mem for the mem object, alone unless --show is also set.

1
usage: machine dump [-h] [--entry n] [--show sections] [--mem addr] [--vcpu n] run
Argument Does
run Run name
Option Does
--entry n Entry from run log
--show sections One or more, comma-separated: code, regs, stack, pt, instr, trace, events, timers, debug, xsave, irq, pic, ioapic, devs, mem, disk, hash, entry, io, msrs, fullhash, all
--mem addr Dump raw memory cluster: N (index), pADDR (physical), vADDR (machine address)
--vcpu n The vCPU whose state and page tables are read (default: 0)

machine checkpoint

Create a checkpoint. See Checkpoints.

Create a checkpoint of the current machine state.

1
usage: machine checkpoint [-h] run [name]
Argument Does
run Run name
name Checkpoint name (auto-generated if omitted)

machine sync

Publish a run to shared storage. See Stopping and starting.

Publishes a run to shared storage.

Pushes the run’s folder so any session can start it. Runs in the session that holds the folder.

1
usage: machine sync [-h] run
Argument Does
run Run name

machine cancel

Cancel the active run or checkpoint. See Trying different schedules.

Cancel the currently active run or checkpoint operation.

1
usage: machine cancel [-h] run
Argument Does
run Run name

machine detach

Detach into a new root. See Moving around.

Detaches a new self-contained root from a run and starts it.

Without --at, the root is detached at the current point. The copy includes everything the root still owns and shares nothing with the origin tree. It is slower than fork --rebase.

The new root is named for the run it copied and the point. A second copy of the same point gets a suffix.

The new root keeps the run options of the run it copied; --daemon, --single-step, --limit and --backstop-limit replace them, and the root keeps those instead. Its machine runs on the least-loaded host CPU unless --pin-cpu names one.

1
2
3
usage: machine detach [-h] [--at point [point ...]] [--name name] [--no-start] [--side-dump | --no-side-dump] [--side-trace | --no-side-trace] [--no-wait] [--daemon | --no-daemon] [--single-step | --no-single-step] [--limit n]
[--backstop-limit n] [--pin-cpu n]
run
Argument Does
run Run to detach from
Option Does
--at point [point ...] Point the new branch starts at: ^checkpoint, #entry, a time, -10ms back, now, parent [n], root, or %mark
--name name Name for the new branch, defaulting to an address: the entry it lands on, and for detach the run it copied as well
--no-start Create the branch without starting it
--side-dump, --no-side-dump Record a per-entry debug dump, whatever the run it comes from records
--side-trace, --no-side-trace Record a per-entry kernel trace, whatever the run it comes from records
--no-wait Return without waiting
--daemon, --no-daemon Wait for commands instead of running to completion (default: the run’s)
--single-step, --no-single-step Run in step mode, for non-daemon runs (default: the run’s)
--limit n Instructions per dispatch, 0 to leave it to the machine (default: the run’s)
--backstop-limit n Step modes only: machine instructions a dispatch may run without stepping before it ends, 0 for the machine default (default: the run’s)
--pin-cpu n Pin to this host CPU (default: the least-loaded)

machine switch

Switch to a branch. See Moving around.

Switches a running machine onto a branch that exists.

The first operand names the run that switches, as with fork and detach. The second names where it goes: a branch, a mark written %mark, or parent and root. It never creates; forking a branch and switching onto it is go, and forking one without switching is fork.

1
usage: machine switch [-h] [--no-wait] run [branch ...]
Argument Does
run The running machine that switches
branch Branch to switch onto: a name, a mark written %mark, parent [n], or root
Option Does
--no-wait Return without waiting

machine rewind

Put a run back at a point, discarding the tail. See Moving around.

Puts a run back at a point and discards what came after it.

The run keeps its id and its name. The entries, checkpoints and console output after the point are discarded.

The point is required and is any entry from #0 to the tip: an entry id after #, a checkpoint name after ^, or a machine time. The run lands on it exactly, and the point becomes a checkpoint. Rewinding to the tip changes nothing unless a replay diverged there.

A point before the run was forked moves the run back along the line it was forked from. A run with a branch forked after the point is refused.

1
usage: machine rewind <run> to <point>
Argument Does
run The machine that rewinds
point Point to go back to, from #0 to the tip: ^checkpoint, #entry or a time

machine go

Branch at a point and switch onto it. See Moving around.

Forks a branch at a point and switches onto it.

The move a walk makes most, and the only one that forks and switches in a single step. The point may be a time, -10ms back, #entry, ^checkpoint, now, parent [n], root, or a mark written %mark; each of them names a point on this run’s own line. A point that is not a checkpoint is reached by forking at the nearest one at or before it and replaying the difference.

1
usage: machine go [options] <run> <point>
Argument Does
run The running machine that moves
point Where to fork: a time, -10ms back, #entry, ^checkpoint, now, parent [n], root, or %mark
Option Does
--name name Name for the new branch, defaulting to an address: the entry it lands on, and for detach the run it copied as well
--rebase Copy the ancestors’ data into the new branch instead of sharing it
--depth n With --rebase, how many root-side ancestors to keep (default 1: the root)
--lean With --rebase, drop the absorbed history so those runs become deletable
--side-dump, --no-side-dump Record a per-entry debug dump, whatever the run it comes from records
--side-trace, --no-side-trace Record a per-entry kernel trace, whatever the run it comes from records
--no-wait Return without waiting

machine replay

Replay parent entries. See Replay.

Replays recorded entries from the parent lineage.

Without a line, the parent’s entries are replayed. A fork at an entry leaves that entry with the parent and starts the branch at the next one, so the parent’s entries end at the fork. Replaying past them reports no forward entries.

Naming a line replays the entries that lead to that run, across every fork on the way. The run must be in the same tree. A line that is not this run’s own is not refused; the replay diverges at the first entry that does not reproduce.

An entry that diverges is replayed again from the newest checkpoint before it, twice at most, and not again once it diverges the same way twice. An entry the host interrupted is replayed again the same way and is not a divergence. Use --retry to set how many retries; --retry 0 replays each entry once.

The replay aims its interrupt short of each recorded stop by the machine’s default skid, so an interrupt that skids less still lands in time. Use --skid to aim closer. A smaller skid makes a miss more likely.

1
usage: machine replay [-h] [--to n] [--trace] [--retry n] [--skid n] run [line]
Argument Does
run Run name
line Run whose line to replay, defaulting to the parent’s
Option Does
--to n Replay to entry n, defaulting to the end of the line
--trace Trace each iteration one by one
--retry n Retries for an entry that diverges or the host interrupted, default 2; 0 does not retry
--skid n Aim the interrupt n instructions short of each recorded stop

machine play

Play entries from a run log. See Replay.

Scripted playback from a run’s log. No divergence checking.

The source must be in this run’s tree. Divergence is not checked, so a source that is not this run’s own line is played and not refused.

1
usage: machine play [-h] [-n range] [--include type [type ...]] [--exclude type [type ...]] run [source]
Argument Does
run Run name
source Source run (default: parent)
Option Does
-n, --entries range Entry N, A:B, A:, :B or :, or -N for the last N entries
--include type [type ...] Include only these event types: run, irq, exception, random, device_in, block_done, input, exit_shutdown, system_shutdown, system_reset, checkpoint, cpuid
--exclude type [type ...] Exclude these event types

machine diff

Show replay divergence details. See Replay.

Compares a run log entry against another.

With no second operand the entry is compared against the run it was replayed from, and the answer says whether the replay converged. Naming a run compares against that run, at its own entry when one is given as other:m. A difference between two recordings says that they differ, not that a replay diverged.

1
usage: machine diff [-h] [--show sections] [--entry n] [--context n] run [other[:m]]
Argument Does
run Run name
other[:m] Run to compare against, at its entry m; m defaults to --entry
Option Does
--show sections One or more, comma-separated: code, regs, stack, pt, instr, trace, events, timers, debug, xsave, irq, pic, ioapic, devs, mem, disk, hash, counts, entry, io, msrs, fullhash, all; default hash
--entry n Compare specific entry N (0 = last entry)
--context n Also show the N entries leading up to it, on both sides

machine control

Interactive control mode. See Interactive mode.

Enter interactive command mode for rapid vCPU control.

Use control log with a run to read the command history instead of driving the machine, --entries to select the commands issued at given run entries, and -n to select commands by number.

1
usage: machine control [-h] [--entries range] [-n range] [-c cmd] run|log [run]
Argument Does
run|log Run name, or log to read history
run With log: the run whose history to read
Option Does
--entries range With log: commands at one entry, or an inclusive range
-n, --lines range With log: command N, A:B, A:, :B or :, or -N for the last N commands
-c, --command cmd Slash command (e.g. “/s=0@10”)
1
2
3
4
5
6
7
command selection (-n):
-n -20 Last 20 commands
-n 20 Command 20
-n 100:200 Commands 100 through 200
-n 50: From command 50 to the end
-n :50 Commands 1 through 50
-n : Every command

machine wait

Wait until a run satisfies a predicate. See Waiting.

Block until a run satisfies a --for predicate.

Predicates: running, ready, tasks, stopped, paused, halted, activity=, entry>=N, entry=+N, console~REGEX, or a task predicate. tasks holds once the operating system is up and its task table is readable, which is before it execs anything in userland.

A task predicate is [who:]what. what is exec, exit, sched, desched, on, kernel or off. who is /path, cgroup=path, tgid=N or tid=N, and omitting it means any focused task; a path or a cgroup must already be focused. Four of the seven are EDGES: exec where a task matching who entered the task table, exit where one left it, sched where a thread is given the CPU, desched where one gives it up. Three are STATES, a partition: on while the thread is on cpu in its own code, at CPL 3, which is where a forced decision can land between two of its own instructions; kernel while that thread is on cpu in the kernel; off while it is not the task on cpu at all.

A path or cgroup subject means the process. tgid=N means the process for exit and any of its threads for exec. machine focus names the task that stopped the wait.

Named runs must be in the current session. Use --all to wait for every run in it.

Use --timeout before the command. The default is no limit; 0 checks once.

Exit codes: 0 success, 2 error, 3 timeout.

1
usage: machine wait [-h] [--all] --for predicate [--interval s] [run ...]
Argument Does
run One or more runs (omit with --all)
Option Does
--all Wait for every run in the session
--for predicate running, ready, stopped, entry=+N, console~REGEX, /path:exec, …
--interval s Poll every S seconds (default: 1)

machine console

View run console output. See Looking at it.

Shows the console of a run.

Use --entries to select the output of given entries, -n to select lines, and -f to follow new output. An entry that printed nothing contributes nothing.

1
usage: machine console [-h] [-n range] [--entries range] [-f] run
Argument Does
run Run name
Option Does
-n, --lines range Line N, A:B, A:, :B or :, or -N for the last N lines
--entries range Output of one entry, or of an inclusive range
-f, --follow Follow new output (like tail -f)
1
2
3
4
5
6
7
line selection (-n):
-n -20 Last 20 lines
-n 20 Line 20
-n 100:200 Lines 100 through 200
-n 50: From line 50 to the end
-n :50 Lines 1 through 50
-n : Every line

machine entries

View run log entries. See Looking at it.

Shows run log entries.

Use -n to select entries, and -f to follow new output.

1
usage: machine entries [-h] [-n range] [-f] run
Argument Does
run Run name
Option Does
-n, --entries range Entry N, A:B, A:, :B or :, or -N for the last N entries
-f, --follow Follow new output (like tail -f)
1
2
3
4
5
6
7
entry selection (-n):
-n -20 Last 20 entries
-n 181743 Entry 181743
-n 181740:181745 Entries 181740 through 181745
-n 181743: From entry 181743 to the end
-n :200 Entries 0 through 200
-n : Every entry

machine sessions

List and manage sessions. See Sessions.

Lists and manages sessions.

Create a session, then use it so later commands target it by default.

1
usage: machine sessions [-h] [-w] {list,create,use,show,delete,rename,connect,disconnect} ...
Option Does
-w, --watch Watch for changes

machine sessions list

List sessions. See Sessions.

Lists sessions.

The current session is tagged.

1
usage: machine sessions list [-h] [-w]
Option Does
-w, --watch Watch for changes

machine sessions create

Create a session. See Sessions.

Creates a session.

Waits until it is running. Use --timeout to bound the wait, or --no-wait to return once the request is accepted.

1
usage: machine sessions create [-h] [--cpus n] [--mem size] [--storage size] [--class type] [--no-wait] [name]
Argument Does
name Session name (default: auto-generated)
Option Does
--cpus n Number of vCPUs (default: 1)
--mem size Memory, MB or with a K/M/G/T suffix (default: class baseline)
--storage size The session’s storage, GB or with a K/M/G/T suffix (default: class baseline)
--class type Hardware class (default: plan default)
--no-wait Return without waiting

machine sessions use

Use a session. See Sessions.

Makes a session current so later commands target it by default.

Connects first if needed, then checks it responds before switching. Use --timeout to bound the wait, or --no-wait to fail if a connect is needed and the session is not running yet.

1
usage: machine sessions use [-h] [--no-wait] [session]
Argument Does
session Session name, id, or prefix (default: the only one)
Option Does
--no-wait Fail if a connect is needed and the session is not running yet

machine sessions show

Show a session. See Sessions.

Shows session details.

1
usage: machine sessions show [-h] [session]
Argument Does
session Session name, id, or prefix (default: the only one)

machine sessions delete

Delete a session. See Sessions.

Deletes a session.

The first operand names the session; -a deletes every session. Stops the machine and discards its state. Already gone is success. Waits until a named session is gone. Use --timeout to bound the wait, or --no-wait to return once the request is accepted. A prune is refused while any session is live, so -a is what storage prune needs.

1
usage: machine sessions delete [-h] [-a] [-y] [--no-wait] [session]
Argument Does
session Session name, id, or prefix (default: the only one)
Option Does
-a, --all Delete every session
-y, --yes Skip the confirmation prompt
--no-wait Return without waiting

machine sessions rename

Rename a session. See Sessions.

Renames a session.

The machine and tunnel are unchanged.

1
usage: machine sessions rename [-h] session new-name
Argument Does
session Session name, id, or prefix
new-name New session name

machine sessions connect

Connect to a session. See Sessions.

Connects to a session.

The everyday verb is sessions use. Waits until it is running if it is still provisioning. Use --timeout to bound the wait, or --no-wait to fail if it is not running yet.

1
usage: machine sessions connect [-h] [--no-wait] [session]
Argument Does
session Session name, id, or prefix (default: the only one)
Option Does
--no-wait Fail if the session is not running yet

machine sessions disconnect

Disconnect from a session. See Sessions.

Disconnects from a session.

The session keeps running. The tunnel is shared on this machine, so every terminal loses it.

1
usage: machine sessions disconnect [-h] [session]
Argument Does
session Session (default: the current one)

machine disks

Manage disks. See Disks.

Lists and manages disks.

The listing shows the newest 25. Use --limit for a different page size.

1
usage: machine disks [-h] [-q] [--no-trunc] [--deleted] [--label name=value] [--since date|age] [--limit n] [--offset n] [-w] {list,build,show,transfers,delete,rename,cancel} ...
Option Does
-q, --quiet Only show ids
--no-trunc Full disk id
--deleted Show removed disks
--label name=value Filter by a label (repeatable; matches all)
--since date|age Since a date or age
--limit n Show at most n disks (default: 25)
--offset n Skip the first n disks
-w, --watch Watch for changes

machine disks list

List disks. See Disks.

Lists disks.

The listing shows the newest 25. Use --limit for a different page size.

1
usage: machine disks list [-h] [-q] [--no-trunc] [--deleted] [--label name=value] [--since date|age] [--limit n] [--offset n] [-w]
Option Does
-q, --quiet Only show ids
--no-trunc Full disk id
--deleted Show removed disks
--label name=value Filter by a label (repeatable; matches all)
--since date|age Since a date or age
--limit n Show at most n disks (default: 25)
--offset n Skip the first n disks
-w, --watch Watch for changes

machine disks build

Build a disk. See Disks.

Builds a disk from a binary, an image, or a Compose project.

Exactly one source. --binary wraps a static executable in an image built around it. --from names a public image, which the build pulls; no Docker is needed. --image takes a Docker image as a save tar or a name in the local Docker. --compose takes a project directory and the images its services name.

A binary or an image runs as one container. Use --entrypoint to set the executable, --env to set a variable in its environment, and pass arguments after --. A project runs every service in it and takes none of the three. The disk holds each --env value as written.

With --from or --binary, --add puts a file or a directory in the image, as one more layer. Without a destination the path lands in the image’s working directory under its own name; path:dest names another place, and a dest ending in / is a directory.

The name defaults to what the source is called, and the size to what the pack holds loaded plus a gigabyte, rounded up to a whole GB. Use --dry-run to see those numbers without building anything.

1
usage: machine disks build [-h] (--binary path | --image ref | --from ref | --compose dir) [--size size] [--dry-run] [--entrypoint path] [--add path[:dest]] [--env name=value] [--label name=value] [name] [-- args ...]
Argument Does
name Disk name
-- args Arguments after -- become the service’s command
Option Does
--binary path Static x86_64 executable to wrap
--image ref Docker image, a save tar or a name
--from ref Public image, pulled by the build
--compose dir Compose project directory
--size size Disk size, MB or with a K/M/G/T suffix (default: from the pack, rounded up to a GB)
--dry-run Report what the disk would hold and build nothing
--entrypoint path Executable the service runs
--add path[:dest] File or directory to put in the image, repeatable; dest defaults to the image’s working directory
--env name=value Environment variable the service runs with, repeatable
--label name=value Attach a label, repeatable; an empty value removes one the name carried

machine disks show

Show a disk. See Disks.

Shows disk details.

1
usage: machine disks show [-h] name
Argument Does
name Disk name

machine disks transfers

Show transfers. See Disks.

Shows disk transfers.

Active transfers by default. Use --since to include history, or --since with --active for in-flight transfers started in that window.

1
usage: machine disks transfers [-h] [--active] [--since date|age] [-w]
Option Does
--active Only in-flight transfers
--since date|age Since a date or age
-w, --watch Watch for changes

machine disks delete

Delete disks. See Disks.

Deletes disks.

Deletion is permanent. The name is free at once, and the bytes count against the storage quota until the reclaim removes them. A disk a run still boots is refused; use --force to delete it anyway and lose those runs.

1
usage: machine disks delete [-h] [-y] [-f] name [name ...]
Argument Does
name Disk names
Option Does
-y, --yes Skip the confirmation prompt
-f, --force Delete even while runs still boot it

machine disks rename

Rename a disk. See Disks.

Renames a disk.

1
usage: machine disks rename [-h] name new-name
Argument Does
name Current disk name
new-name New disk name

machine disks cancel

Cancel a transfer. See Disks.

Cancels an in-flight transfer.

1
usage: machine disks cancel [-h] name
Argument Does
name Disk name

machine storage

Manage storage. See Account and billing.

Shows and manages storage.

1
usage: machine storage [-h] [--refresh] {show,ops,prune} ...
Option Does
--refresh Measure again instead of using the last measurement

machine storage show

Show storage the account uses. See Account and billing.

Shows the storage the account uses.

1
usage: machine storage show [-h] [--refresh]
Option Does
--refresh Measure again instead of using the last measurement

machine storage ops

List storage operations.

Lists operations on the account’s storage and how they finished.

The listing shows the newest 25. Use --limit for a different page size. Shows pending and failed by default; use -s done for the rest. These act on the account’s repository rather than on a run, so they are listed here rather than by machine ops, and they answer with no session.

1
usage: machine storage ops [-h] [--op-id id] [-s [^]state] [--type type] [--since date|age] [--until date|age] [--limit n] [--offset n] [-w]
Option Does
--op-id id Show one operation, by its id (ignores -s and --type)
-s, --state [^]state Filter by state: pending, done, failed (default: pending, failed; repeat = OR; ^ excludes)
--type type Filter by kind: measure, prune (repeat = OR)
--since date|age Since a date or age
--until date|age Until a date or age
--limit n Show at most n operations (default: 25)
--offset n Skip the first n operations
-w, --watch Watch for changes

machine storage prune

Reclaim space held by deleted runs and disks. See Account and billing.

Reclaims space held by deleted runs and disks above 5%.

1
usage: machine storage prune [-h] [-y] [--status]
Option Does
-y, --yes Do not ask for confirmation
--status Show the current or last prune and exit

machine artifacts

Browse and copy files from a run’s disk. See Reading the disk.

Lists, copies, or prints files from a run’s disk.

Reads are snapshot-pinned. They never perturb execution.

1
usage: machine artifacts [-h] {ls,cp,cat} ...

machine artifacts ls

List files in a run’s disk. See Reading the disk.

Lists files in a run’s disk.

Pin the listing to a checkpoint or entry with run@ref.

1
usage: machine artifacts ls [-h] [-l] [-a] run[@ref] [path]
Argument Does
run[@ref] Run name/id, optionally @entry_id or @checkpoint
path Path (default: /)
Option Does
-l Long format (mode, size, mtime, name)
-a, --all Include . and ..

machine artifacts cp

Copy a file or directory. See Reading the disk.

Copies a file or directory from a run’s disk.

Pin the copy to a checkpoint or entry with run@ref. Use -r for directories.

1
usage: machine artifacts cp [-h] [-r] run[@ref] path [dest]
Argument Does
run[@ref] Run name/id, optionally @entry_id or @checkpoint
path Path to copy
dest Local destination file or directory (default: .)
Option Does
-r, -R, --recursive Copy directories recursively

machine artifacts cat

Print a file to stdout. See Reading the disk.

Prints a file from a run’s disk.

Pin the read to a checkpoint or entry with run@ref.

1
usage: machine artifacts cat [-h] run[@ref] path
Argument Does
run[@ref] Run name/id, optionally @entry_id or @checkpoint
path File path

machine ping

Check reachability and latency. See The client.

Checks reachability and latency.

Pings the API and connected sessions.

1
usage: machine ping [-h] [-c n]
Option Does
-c, --count n Round-trips per target (default: 3)

machine account

Manage account. See Account and billing.

Shows and manages the account.

1
usage: machine account [-h] {devices,buy,upgrade,downgrade,payment,portal,invoices,cancel,email,products,show,credits,activity,orders,api-keys,provision,signup,close} ...

machine account devices

List the devices signed in to the account. See Account and billing.

Lists signed-in devices.

Use --revoke to sign out one device by id.

1
usage: machine account devices [-h] [--revoke id]
Option Does
--revoke id Sign out one device by its id (or a unique id prefix)

machine account buy

Buy usage credits or a plan. See Account and billing.

Buys usage credits or a plan.

1
usage: machine account buy [-h] {usage,plan} ...

machine account buy usage

Buy usage credits. See Account and billing.

Buys usage credits.

Opens checkout for the named pack size.

1
usage: machine account buy usage [-h] [-y] eur
Argument Does
eur Pack size in euros, as account products lists it
Option Does
-y, --yes Skip the confirmation prompt

machine account buy plan

Subscribe to a plan. See Account and billing.

Subscribes to a plan.

Already on a paid plan upgrades or downgrades to the named tier instead.

1
usage: machine account buy plan [-h] [-y] tier
Argument Does
tier Plan tier (e.g. starter, pro)
Option Does
-y, --yes Skip the confirmation prompt

machine account upgrade

Upgrade to a higher plan. See Account and billing.

Upgrades to a higher plan.

An omitted tier is the next one up. Use --link to print the URL without opening a browser.

1
usage: machine account upgrade [-h] [--link] [-y] [tier]
Argument Does
tier Target tier (default: next up)
Option Does
--link Print the link only (don’t open a browser)
-y, --yes Skip the confirmation prompt

machine account downgrade

Downgrade to a lower plan. See Account and billing.

Downgrades to a lower plan.

An omitted tier is the next one down. Use --link to print the URL without opening a browser.

1
usage: machine account downgrade [-h] [--link] [-y] [tier]
Argument Does
tier Target tier (default: next down)
Option Does
--link Print the link only (don’t open a browser)
-y, --yes Skip the confirmation prompt

machine account payment

Update your payment method. See Account and billing.

Updates the payment method.

Use --link to print the URL without opening a browser.

1
usage: machine account payment [-h] [--link]
Option Does
--link Print the link only (don’t open a browser)

machine account portal

Open billing settings. See Account and billing.

Opens billing settings.

Use --link to print the URL without opening a browser.

1
usage: machine account portal [-h] [--link]
Option Does
--link Print the link only (don’t open a browser)

machine account invoices

List and manage invoices. See Account and billing.

Lists invoices.

The listing shows the newest 25. Use --limit for a different page size.

1
usage: machine account invoices [-h] [--since date|age] [--until date|age] [--status {draft,open,paid,uncollectible,void}] [--limit n] [--sort field] [--reverse] {list,download} ...
Option Does
--since date|age Since a date or age
--until date|age Until a date or age
--status {draft,open,paid,uncollectible,void} Filter by invoice status
--limit n Cap to most recent n (default: 25, max 100)
--sort field Sort by created or amount
--reverse Reverse sort order

machine account invoices list

List invoices. See Account and billing.

Lists invoices.

The listing shows the newest 25. Use --limit for a different page size.

1
usage: machine account invoices list [-h] [--since date|age] [--until date|age] [--status {draft,open,paid,uncollectible,void}] [--limit n] [--sort field] [--reverse]
Option Does
--since date|age Since a date or age
--until date|age Until a date or age
--status {draft,open,paid,uncollectible,void} Filter by invoice status
--limit n Cap to most recent n (default: 25, max 100)
--sort field Sort by created or amount
--reverse Reverse sort order

machine account invoices download

Open an invoice, the PDF and the receipt. See Account and billing.

Opens an invoice.

The hosted page has the PDF and receipt. Use --link to print the URL without opening a browser.

1
usage: machine account invoices download [-h] [--link] invoice-id
Argument Does
invoice-id Invoice id (in_…)
Option Does
--link Print the link only (don’t open a browser)

machine account cancel

Cancel your subscription. See Account and billing.

Cancels the subscription.

The plan stays until renewal, then drops to Free. Usage credits stay. Use --link to print the URL without opening a browser.

1
usage: machine account cancel [-h] [--link]
Option Does
--link Print the link only (don’t open a browser)

machine account email

Change your account email. See Account and billing.

Changes the account email.

A code goes to the current inbox. A link goes to the new one.

1
usage: machine account email [-h] [-y] email
Argument Does
email New email address
Option Does
-y, --yes Skip confirmation prompts

machine account products

List products for sale. See Account and billing.

Lists products for sale.

1
usage: machine account products [-h] {list} ...

machine account products list

List products. See Account and billing.

Lists products for sale.

1
usage: machine account products list [-h]

machine account show

Show your account. See Account and billing.

Shows the account.

1
usage: machine account show [-h]

machine account credits

Show your usage credits. See Account and billing.

Shows usage credits.

1
usage: machine account credits [-h]

machine account activity

Show usage credit activity. See Account and billing.

Shows usage credit activity.

The listing shows the newest 25. Use --limit for a different page size. Naming a session focuses the listing on that session.

1
2
usage: machine account activity [-h] [--since date|age] [--until date|age] [--period date] [--type {included,reserved,settled,expired,expired-removed,credit-purchase}] [--credit-type {included,usage}] [--limit n] [--sort field] [--reverse]
[session]
Argument Does
session Show one session’s transactions, by name, id or id prefix
Option Does
--since date|age Since a date or age
--until date|age Until a date or age
--period date Show credit activity in the period containing DATE
--type {included,reserved,settled,expired,expired-removed,credit-purchase} Filter by activity type
--credit-type {included,usage} Filter by credit type (included or usage)
--limit n Cap to most recent n (default: 25, max 500)
--sort field Sort by sequence or amount
--reverse Reverse sort order

machine account orders

List and manage purchases. See Account and billing.

Lists purchases.

The listing shows the newest 25. Use --limit for a different page size.

1
usage: machine account orders [-h] [--status {pending,processing,completed,cancelled,expired,failed}] [--limit n] {list,resume,cancel} ...
Option Does
--status {pending,processing,completed,cancelled,expired,failed} Filter by status
--limit n Cap to most recent n (default: 25, max 200)

machine account orders list

List orders. See Account and billing.

Lists purchases.

The listing shows the newest 25. Use --limit for a different page size.

1
usage: machine account orders list [-h] [--status {pending,processing,completed,cancelled,expired,failed}] [--limit n]
Option Does
--status {pending,processing,completed,cancelled,expired,failed} Filter by status
--limit n Cap to most recent n (default: 25, max 200)

machine account orders resume

Resume a pending checkout. See Account and billing.

Resumes a pending checkout.

Defaults to the single pending order. Use --link to print the URL without opening a browser.

1
usage: machine account orders resume [-h] [--link]
Option Does
--link Print the link only (don’t open a browser)

machine account orders cancel

Cancel a pending order so you can buy again. See Account and billing.

Cancels a pending order so another buy can start.

Defaults to the single pending order.

1
usage: machine account orders cancel [-h]

machine account api-keys

List and manage API keys. See Account and billing.

Lists and manages API keys.

Keys can run machines, disks, and sessions, not billing or account changes.

1
usage: machine account api-keys [-h] {list,create,delete,update} ...

machine account api-keys list

List API keys. See Account and billing.

Lists API keys.

1
usage: machine account api-keys list [-h]

machine account api-keys create

Create an API key. See Account and billing.

Creates an API key.

The secret is shown once. A sign-in code is required.

1
usage: machine account api-keys create [-h] [--expiry days|never] name
Argument Does
name Label to recognize this key later
Option Does
--expiry days|never Expire after N days (e.g. 30), or “never” (default: never)

machine account api-keys delete

Delete an API key. See Account and billing.

Deletes an API key.

A sign-in code is required.

1
usage: machine account api-keys delete [-h] id
Argument Does
id Key id, as account api-keys lists it

machine account api-keys update

Set an API key’s expiry. See Account and billing.

Sets an API key’s expiry.

A sign-in code is required.

1
usage: machine account api-keys update [-h] id days|never
Argument Does
id Key id, as account api-keys lists it
days|never Number of days from now (e.g. 30) or “never”

machine account provision

Set up account storage. See Signing up.

Sets up cloud storage.

Safe to repeat while it is still working.

1
usage: machine account provision [-h]

machine account signup

Create an account. See Signing up.

Creates an account.

A link is emailed to finish signing up.

1
usage: machine account signup [-h]

machine account close

Close your account. See Account and billing.

Closes the account.

Two steps: start, then confirm after machines stop. Use --cancel to abort a close already in progress.

1
usage: machine account close [-h] [-y] [--cancel]
Option Does
-y, --yes Skip confirmation prompts
--cancel Abort closing and return the account to active

machine login

Sign in. See Signing up.

Signs in.

A sign-in code is emailed. Use account signup if there is no account yet.

1
usage: machine login [-h]

machine logout

Sign out. See Signing up.

Signs out.

Revokes the access token on the server. Use --everywhere to sign out of every device and browser.

1
usage: machine logout [-h] [--everywhere]
Option Does
--everywhere Sign out of every device and browser, not just this one.

Python SDK

The SDK does what the client does, from Python. If you can type it in a terminal, you can call it from a program.

Installing

1
pip3 install testflows.machine

It needs Python 3.11 or later on Linux x86_64, the same systems as the client. If you need another platform, contact us. pip installs the Machine core that the SDK runs on, testflows.machine.core, along with it.

Hello Machine

1
2
3
4
5
6
7
8
9
10
import testflows.machine.v1 as machine

client = machine.Client()

with client.sessions.create(cpus=1) as session:
client.sessions.use(session)
with client.create(disk="hello") as run:
run.run(until="tasks", timeout=300)
for line in run.console(lines=-20):
print(line)

This creates a session, boots a run from the disk hello and prints the last twenty lines of its console. A run waits for commands, so it moves only when you call run.run(...); pass daemon=False to have it run to the end by itself. Leaving a with block deletes what it created, whether the block ends normally or with an error. That is how you make sure a session never outlives your program.

It looks like the client

Every client command has a matching SDK call, and the names follow a simple rule.

In the client In the SDK
a top-level command, machine create --disk app a method on the client, client.create(disk="app")
a group, machine sessions use ci-1 an attribute of the client, client.sessions.use("ci-1")
a command that takes a run, machine fork app --at ^boot a method on the run, run.fork(at="^boot")
a group that takes a run, machine tasks app hold 56 an attribute of the run, run.tasks.hold(56)
a positional argument a positional argument
a flag like --no-wait, -w or -f a keyword argument like wait=False, watch=True or follow=True

So once you know the client, you know most of the SDK. Commands lists them, and the client’s --help says what each call takes.

Signing in

The SDK signs in with an API key from TESTFLOWS_MACHINE_API_KEY if it is set. If it isn’t, it uses the login that machine login stored, which it shares with the client.

A program can sign in on its own too. The one-time code arrives by email.

1
2
machine.login.start("me@example.com")
client = machine.login.verify("me@example.com", code="123456")

machine.login.logout() signs out.

Sessions

A client has a current session, like the CLI does. You can set it when you create the client, or later.

1
2
client = machine.Client(session="ci-1")      # like -s ci-1
client.sessions.use("ci-2") # for this client only

sessions.use only changes the session for this client. It never changes it for the machine command. Without a session you can still read your account’s runs: client.runs(), client.branches() and a run’s describe(), lineage() and checkpoints() all work.

Runs

Here is a typical run, from creating it to branching and replaying.

1
2
3
4
5
6
7
8
run = client.create("app", disk="app", rng=True)
run.run(until="tasks")
run.focus.add("/app")
run.run(until="/app:exit")

booted = run.checkpoint("booted")
branch = run.fork(at="^booted", name="retry")
branch.replay()

And the calls you will reach for most often:

Call Does
client.create(name, disk=..., cpus=..., ...) creates a run and boots it; takes every option machine create does
client.runs(), client.runs["app"] lists runs, or finds one by name
run.run(until=..., iters=...), run.plan(file) drives it
run.checkpoint(name), run.checkpoints() saves a checkpoint, lists them
run.fork(...), run.go(point), run.switch(branch), run.detach(...) branches
run.replay(), run.play(...), run.diff(...) replays and compares
run.describe(), run.state(), run.now(), run.entries(...) reads it
run.console(...) reads what it printed
run.artifacts.ls(path, at=...), .cat(...), .cp(...) reads its disk
run.tasks(), run.focus.add(...), run.irq(...), run.vtime.rate(...) steers the machine
run.start(), run.stop(), run.pause(), run.kill(), run.delete() lifecycle

Every call returns a record with named fields, such as a RunInfo from describe() or a list of Task from tasks(). Call as_json() on one to get a plain dict.

Watching and following

A listing takes watch=True and then gives you a new list every time something changes. A log takes follow=True and gives you new lines as they come, like tail -f.

1
2
3
4
5
for runs in client.runs(states=["running"], watch=True, timeout=60):
print([r.name for r in runs])

for line in run.console(follow=True, timeout=60):
print(line)

Choosing entries

Anything that reads entries takes the same few spellings.

You write Means
200 or (100, 200) that entry, or that range, both ends included
"100:200", "100:", ":200" a range, from 100 on, or up to 200
"-20" the last 20 entries
slice(100, 200) a Python slice, so 100 through 199

Be careful with the last row. A pair and a string include both ends, the way Machine’s own messages do. A slice is Python’s, so it stops before the end. That makes (100, 200) one entry longer than slice(100, 200).

lines= selects lines of output in the same grammar: 20 is line 20, -20 the last 20, and 0 none, which starts a follow at the end.

Cleaning up

A session costs from the moment you create it, and a run keeps its log and checkpoints until you delete it. Both can be used as context managers, as in the first example, and deleting a run takes its branches with it.

If you want to keep a run that found something, don’t wrap it in a with block. Nothing is deleted until you call delete().

Errors

Every exception the SDK raises is a machine.Error, and each one says what went wrong.

Exception Raised when
NotSignedIn there are no credentials
Unauthorized a key or a sign-in code was rejected
Forbidden your account is not allowed to do that
NotFound nothing matches the name or id
AlreadyExists something with that name exists
InvalidArgument an argument is wrong, or a name matches more than one thing
NotRunning it exists, but it is not running
Conflict the request does not fit the thing’s current state
Diverged a replay did not reproduce what was recorded
Timeout a wait ran out of time
ServiceUnavailable the service could not be reached
ApiError the service refused the request
1
2
3
4
try:
branch.replay()
except machine.Diverged:
print(branch.diff())

Examples

The SDK comes with small scripts. Each one runs as python <script> --disk <name>, creates its own session, and deletes it at the end.

Script Shows
hello.py creates a run, boots it, reads its console
checkpoint_and_fork.py a checkpoint after boot, and branches from it
replay_verify.py replays a recording and names what diverged
explore_seeds.py one branch per seed, with the failing ones marked and kept
force_interleaving.py holds one thread while another runs past it
plan_file.py drives a run with when and else clauses
watch_and_follow.py watches a listing and follows a console
syscall_profile.py counts a program’s system calls and interrupts
read_artifacts.py reads and copies files from a checkpoint’s disk
machine_time.py changes the machine’s clock (vtime) rate and pushes it forward
inject_interrupt.py injects an interrupt and replays it
stop_and_resume.py what stop, kill, start and sync keep
resume_elsewhere.py stops a run in one session and starts it in another
ci_job.py a CI job with an API key, a session per job and an exit code
error_handling.py every error and how to recover from it

License

The SDK is licensed under Apache 2.0. The Machine core it runs on, testflows.machine.core, is not part of it and has its own license.