Bounded runs on a far machine
How a run on a machine reached over ssh is held to a task, memory and lifetime bound, how to read which bound applied, and how to stop or inspect one.
A run placed on a machine reached over ssh can start processes the server cannot see: a test suite, a build, a shell a tool opened. Without a bound, one of them can fork or allocate until the whole machine runs out of memory, and it can keep running after the run that started it has ended.
So on a far Linux machine whose login account runs a user systemd manager, every run starts in a transient scope of its own, and that scope is stopped when the run returns.
What the bound is
| bound | value | what happens at it |
|---|---|---|
| tasks (processes and threads) | 8192 | a further fork fails; a runaway recursion collapses on its first failed fork |
| memory, throttle | 20 GiB | the run is slowed and reclaimed, not killed |
| memory, hard | 24 GiB | the kernel ends processes inside the run's scope, never outside it |
| lifetime | the run's wall clock plus 10 minutes | the scope is stopped even if the server can no longer reach the machine |
When the run returns, its scope is stopped and every process it left behind ends with it. The run's own exit status is kept.
⚠ The values are fixed in this release. They are not settable per run or per machine.
How many runs a machine carries at once
Separately from the per-run bound, a machine has a live-run ceiling: the number of runs it may carry at once, across all its seats. A dispatch that would exceed it is refused by name.
The default for every machine is the configuration key scheduler.host_max_live. To set one
machine's ceiling, as the owner:
- Read
saphan machine list --workspace <workspace root>and find the machine. - Rehearse:
saphan dry-run machine set-max-live --machine <id> --max <n> --added-by <owner> --workspace <workspace root>. It writes nothing. - Run the same line without
dry-runat the owner's terminal and confirm.saphan machine listnow showsmax-live=<n>on that machine. - To return the machine to the default, repeat steps 2–3 with
--max 0.
A certificate renewal keeps the ceiling. Re-admitting a retired machine does not: set it again afterwards.
⇒ Size the ceiling from memory, not from seat count. A machine carries the memory bound once per
live run, so ceiling × 20 GiB should fit in what the machine has free after its own services.
Reading which bound a run had
Every run's stderr.log carries one line that starts run-bound:, written on the far machine before the run starts. It has two forms.
| line | meaning |
|---|---|
run-bound: saphan-leg-<run id>.scope -p TasksMax=… -p MemoryHigh=… -p MemoryMax=… -p RuntimeMaxSec=… | the run was bounded; the unit name and its bounds follow |
run-bound: not applicable — … | the machine's user manager did not start a bounded scope (it has none, or it refused a bound). The run was not bounded, and a process it leaves behind outlives it |
In the unit name, every character of the run id other than a letter, a digit, - or _ is written as _.
⚠ A macOS machine always takes the second form: it has no user systemd manager. The same is true of a Linux account that has no user session manager running.
Listing and stopping bounded runs on a machine
On the far machine, as the account the runs use:
- List the live runs:
systemctl --user list-units 'saphan-leg-*' - Read one run's usage:
systemctl --user status saphan-leg-<run id>.scope— it prints the task count and memory in use, and the processes in it. - Stop one run and everything it started:
systemctl --user stop saphan-leg-<run id>.scope
⚠ Stopping a scope by hand ends the run's processes, including the agent itself; the run does not finish its work.
Reading why a run ended on its bound
While the scope still exists, its cgroup counters say which bound was met:
- Find the cgroup:
systemctl --user show -p ControlGroup saphan-leg-<run id>.scope - Read
pids.eventsthere: amaxabove 0 means a fork was refused at the task bound. - Read
memory.eventsthere:highcounts throttling at 20 GiB;oom_killabove 0 means the hard bound ended a process.
⚠ The counters go away with the scope. Once the run has returned and its scope is stopped, these files
no longer exist; the machine's journal (journalctl --user -u saphan-leg-<run id>.scope) is what remains.
What this does not cover
- A run on the server's own machine (not reached over ssh) is not bounded this way.
- A process that escapes its scope on purpose — for example by asking the user manager for a scope of its own — is outside the bound.
Agent machines
The three forms an agent machine can take — a host, a container image, or a Linux virtual machine — and the per-language images the product ships as templates.
Adding a vendor CLI to an image
Four ready build files, the three properties they all share, and what each CLI does behind a proxy — because two of the four ignore the variable most people reach for first.