Skip to content

What a run leaves

Every run leaves one directory on your machine. It is the whole point of the sandbox keeping a notebook rather than a scrollback, and it is the unit this product is built around: a run is the record a sandbox left, and the sandbox itself is gone by the time you read it.

Runs live under $TORMONI_RUNS_DIR, else $XDG_DATA_HOME/tormoni/runs, else ~/.local/share/tormoni/runs. One directory per run holds:

  • The record file — the posture as settled (every flag the run actually got, not the ones you typed), the timings, and how it ended. --env is the one flag it keeps by halves: the NAME of each entry, never what it was set to. A value is where a token goes, and a record is a file you export, push and paste.
  • The captured output — capped at $TORMONI_OUTPUT_CAP_KIB, 4 MiB by default, with a .truncated sidecar written when the cut happens. A run that printed more than the cap says so; the cut is never silent.
  • results/ — the directory the guest saw as /results, which is where a sandbox puts what it produced.

Ended runs beyond $TORMONI_RUNS_KEEP, 200 by default, are pruned oldest-first when a new run starts. tormoni ls --all lists them, tormoni show reads one, and tormoni rm removes one and everything it captured.

tormoni export packages one run’s directory as a single ustar .tar file that a stock tar extracts. Two properties of that archive matter, because the bytes inside it came from code you did not trust:

  • A symlink is archived as a link entry and never followed. A guest that plants a symlink to /etc/shadow inside results/ gets a symlink in the tar, not the file it points at. A test in tormoni-record (a_symlink_is_archived_as_itself_and_never_followed) holds it there.
  • A file that grows or shrinks mid-export keeps the size its header pinned, so exporting a live run produces a readable archive rather than a corrupt one.

The export is one file, so it copies, attaches and archives like any other.

The output and the files under results/ were written by whatever ran in the sandbox, and that was the code you did not want on your machine. They are data, not documents: a file name can carry control characters, an .html or .svg under results/ renders as a page if you open it in a browser, and output can carry escape sequences your terminal acts on. Extract an export somewhere disposable, and open what is in it the way you would open anything a stranger sent you.

It keeps it. Push the export and this service reads the record out of it for the index — the posture, how the run ended, the sizes, the file names and their types — stores the archive whole, and hands it back byte for byte. Nothing is rewritten and nothing is re-encoded: what comes back down is what went up.

The database holds the index and never the contents. Your output is not a row: it lives inside the stored archive, and the run’s page reads it back out of there each time you open it.

A stored run is immutable. The only things that happen to one are deletion — yours, or its own expiry, which is stamped on it at push and shown on its page — and the owner’s hidden switch, which touches no byte of the archive.

The console is where a pushed run lands, and the API is the lane it travels.

The book’s Running a sandbox page is the authority on the record’s layout, which is pre-v0.1.0 and changes without notice.