---
name: run
description: Start one program from the deployer's programs directory with exact arguments, no shell, a fixed environment, a deadline and an output cap; answers its exit status and output. Opt-in: it can run whatever the directory holds, and it does not sandbox it.
---

# run 0.1.0

Start one program from the deployer's programs directory with exact arguments, no shell, a fixed environment, a deadline and an output cap; answers its exit status and output. Opt-in: it can run whatever the directory holds, and it does not sandbox it.

```
run [--root DIR] [--timeout-ms N] [--max-output N] [--stdin] [--max-stdin N] [--format json|text] -- PROGRAM [ARG...]
```

## Output

One JSON object on one line, schema `run.v1`: `{ok, command, schema, data, error, errors, meta}`. `ok` is false exactly when `errors` is non-empty; `error` is the first.

An error is `{code, rule, message, hint, repair, detail}`. Match on `rule`, not on `message`. When `repair` is `{"kind":"retry","argv":[...]}` the argv can be run as it stands; it never widens what the tool may touch.

`--format text` is for a person and is lossy; do not parse it.

## Flags

- `--root <path>`: start the program in this directory (default: the working directory) and refuse a directory outside it
- `--timeout-ms <nat>`: the longest the program may run, in milliseconds, after which it is killed (1 to 600000) Default `30000`.
- `--max-output <nat>`: the most bytes kept from each of standard output and standard error, and the program is killed at the first byte past it (ceiling 67108864) Default `1048576`.
- `--stdin`: give the program this tool's standard input as its standard input (default: nothing)
- `--max-stdin <nat>`: the most standard input accepted with --stdin (ceiling 67108864) Default `1048576`.
- `--format <choice:json/text>`: json for a program, text writes the program's standard output to standard output and its standard error to standard error Default `json`.

An unknown flag is refused (`args.unknown-flag`), never ignored. POSIX spellings are not promised.

## Guarantees

- **bounded_memory**: peak memory does not grow with the input's size (M9)
- **concurrency**: none; it takes no lock and cannot see what the program it starts then does.

## Exit codes

- `0` SUCCESS: what was asked was done; zero matches is success
- `2` INVALID_ARGS: the invocation is malformed or names something of the wrong kind; nothing was read
- `3` NOT_FOUND: a named input does not exist
- `4` PERMISSION_DENIED: the operating system refused, or the tool's own confinement did
- `8` PRECONDITION_FAILED: the answer is no, or a limit was reached
- `132`: the tool trapped. A bug in the tool; there is no JSON.
- `141`: the reader closed the pipe.

## Rules

- `args.unknown-flag` (exit 2): a flag that is not in the tool's table
- `args.missing-value` (exit 2): a flag that takes a value was given none
- `args.bad-value` (exit 2): a flag's value is not of the flag's kind
- `args.unexpected-value` (exit 2): a flag that takes no value was given one
- `args.duplicate-flag` (exit 2): the same flag was given twice
- `args.missing-operand` (exit 2): an operand the tool needs was not given
- `path.empty` (exit 2): an empty path
- `path.dotdot` (exit 2): a path with a `..` component
- `path.absolute` (exit 2): an absolute path where one relative to --root is wanted
- `path.outside-root` (exit 4): an absolute path that is not under --root
- `path.too-long` (exit 2): a path longer than 4096 bytes
- `path.symlink` (exit 4): a path that runs through a symbolic link, which the tool does not follow
- `io.not-found` (exit 3): a named input does not exist
- `io.not-a-directory` (exit 3): a path runs through something that is not a directory
- `io.permission-denied` (exit 4): the operating system refused access
- `limit.input-too-large` (exit 8): an input longer than --max-bytes
- `run.program-name` (exit 2): the program is not one plain name (it is empty, `.`, `..` or has a `/`)
- `run.program-not-found` (exit 3): the programs directory holds no program of that name
- `run.spawn-failed` (exit 8): the operating system could not start the program or its channels
- `limit.timeout` (exit 8): the program ran past --timeout-ms and was killed
- `limit.output-too-large` (exit 8): the program wrote more than --max-output to one stream and was killed

## Authority

What the compiler says this program can reach (`cancho authority`), embedded at build time:

```json
{"bounded":true,"unbounded_by":[],"effects":["args","child_signal","clock","err_write","exec","fs_read","heap","io_read","io_write","pipe_read","pipe_write","poll"],"labels":[{"name":"args","argument":null,"bounded":true},{"name":"child_signal","argument":null,"bounded":true},{"name":"clock","argument":null,"bounded":true},{"name":"err_write","argument":null,"bounded":true},{"name":"exec","argument":"/opt/cancho-tools/programs","bounded":true},{"name":"fs_read","argument":"","bounded":true},{"name":"heap","argument":null,"bounded":true},{"name":"io_read","argument":null,"bounded":true},{"name":"io_write","argument":null,"bounded":true},{"name":"pipe_read","argument":null,"bounded":true},{"name":"pipe_write","argument":null,"bounded":true},{"name":"poll","argument":null,"bounded":true}],"never_touches":["the network","signals","foreign code"],"foreign_symbols":[],"pure":["alphabet","authority","built","bytes","catalogue","channels","channels_with_errors","child_token","code_meaning","code_name","compiler","count","count","count","count_byte","decode","default_of","digit_of","distance","emits","entries","entry","entry","entry_at","entry_end","entry_in","equal","err_control","err_depth","err_end","err_escape","err_full","err_number","err_syntax","err_trailing","err_utf8","errors_token","escape_width","exit_code","exit_in_entry","exit_of_in","exited","expected","extra","failed","field","field_of","field_of","find","find_in","finish","first","flag_table","get$int","guarantee_keys","guarantees_has","has","has_dotdot","hex4","hex_digit","hex_value","in_choice","in_range","index_of","index_of_short","input_token","io_rule","is_ascii","is_digit","is_digit","is_flag","is_hex","is_named","is_tail","is_utf8","is_valid","is_ws","item","kind_of","lead_bits","list","listed","longest","max_depth","message_of","name_of","nearest","operand_count","output_token","parse_nat","plain_byte","plain_name","programs","read_again","read_end","read_too_much","repairable_in","rule_of","running","scan_for","schema","second_range","sentences","shell_builtin","shut","size","size$int","size_ceiling","slash","start","starts_with","string_end","summary_in","takes_value","tape_len","timed_out","timeout_ceiling","too_much","tool","valid","value_index","width_of"],"folded_operators":27,"folded_calls":0,"functions":218}
```

The row names *kinds* of access, not *extent*: a path from the command line is checked at run time, so it reads `fs_read("")`. Symlinks are followed.
