Use it
Files in and out
Give a program copies of your files, and get back what it writes. No folder on your Mac is ever shared with the box. Files travel as bounded copies, and everything that comes back is checked before it touches your disk.
In one command#
$ pybox exec report.py --input sales.csv --output results/import csv, json
rows = csv.DictReader(open("/tmp/input/sales.csv"))
totals = {}
for r in rows:
totals[r["region"]] = totals.get(r["region"], 0) + int(r["sales"])
json.dump(totals, open("/tmp/output/summary.json", "w"))Afterwards results/summary.json exists on your Mac, and the receipt lists every file that went in and came out:
From the program untrusted: a program can print or write anything Printed 79 bytes on stdout, 0 bytes on stderr Files in 1 file(s), 41 bytes Files out 2 file(s), 47 bytes → results/ Not brought back escape
That program also tried to leave a symbolic link to /etc/passwd in its output folder. Links are never brought back, so it's listed under Not brought back.
The rules#
Going in (--input) | Coming back (--output) | |
|---|---|---|
| Where, in the box | /tmp/input/. A file keeps its name; a folder keeps its folder name, with its contents inside | Whatever the program writes under /tmp/output/ |
| How many | Up to 64 files and 8 MiB in total, each way | |
| Names | Letters, digits, ., _ and -, up to 4 folders deep, no hidden files. Names that differ only in upper and lower case count as the same, because Mac folders usually do. Anything else is refused | |
| Links and special files | Refused: PyBox never follows a link on your Mac | Skipped and listed by name, with any unusual characters shown as \xNN. Empty folders are dropped |
| On your Mac | Your originals are only read. The program gets copies, made read-only in the box | --output must be a new or empty folder. Files are written all at once or not at all, never following a link, never overwriting, and marked like downloads (com.apple.quarantine) so macOS checks them when opened |
Limits are checked on your Mac before any box is built, so an oversized input fails immediately with no run.
How the files travel#
The box still has no mounts, so files travel over the same channel the program uses. For runs that move files (and other opt-in runs), the box's first process starts a small fixed helper instead of plain python -. The exact command is part of the settings PyBox checks before starting. The helper:
- Reads the program and the input files from standard input, and writes the files to
/tmp/input/. - Runs the program as
python -would: as__main__, with the same exit codes and tracebacks. - When the program's main code returns, packs
/tmp/output/(regular files only, within the limits) and appends it to standard output after a marker unique to this run. Files written later by background threads or exit handlers aren't included. The marker keeps ordinary output from being mistaken for the transfer; it isn't a secret from the program.
On your Mac, PyBox splits the program's output from the files at that marker, decodes them strictly, and checks every name and size again. It writes them into a fresh hidden folder next to --output and renames that folder into place only when every file is written, so you never get half a result. For runs that move files, the box's scratch space grows from 16 MiB to 64 MiB.
What comes back is the program's work, so treat it as untrusted. The helper runs as the program's own user, so a program can read its state, change its copies of the inputs, or fake its own outputs. That can't reach your Mac's files, but it means returned files are only as trustworthy as the program. If the program never finishes (it's stopped at the deadline, or exits with os._exit), nothing comes back, and the receipt says no transfer received.
From Python and MCP#
from pybox import run_container
result = run_container(open("report.py").read(), inputs=[("sales.csv", open("sales.csv", "rb").read())],
collect_outputs=True)
for name, data in result.output_files: # (name, bytes), untrusted
print(name, len(data))Over MCP, an agent passes files and return_files. The limits are smaller there so results fit the protocol: 256 KiB of input files, and 48 KiB of returned file contents (larger files are listed with their size and SHA-256). See MCP for AI agents.