On this page

Host and request limits

The limits a host declares in its configuration beside the first-run profile: what a mobile client may ask, how loaded or swapped a host may be before a scan or job waits, where the Tailscale client is, and how large a source archive may be.

Mobile egress request boundary

The loopback proxy requires the deployment's listen address and header budget. The budget includes the request line, header fields and terminating blank line, not the request body or the bytes carried by a CONNECT tunnel.

stado egress mobile serve --interface "$INTERFACE" --bind "$BIND" --port "$PORT" \
  --max-header-bytes "$HEADER_BYTES"

BIND must be a loopback address and HEADER_BYTES a positive whole number, both chosen by the deployment. A missing --bind, a non-loopback address, or a missing, zero or invalid budget is refused by the CLI before a listener starts. Managed service declarations must include --bind, --port and --max-header-bytes with their values in the service's argument list. There is no built-in address or byte budget.

A request line that is not METHOD TARGET HTTP/… is answered 400 Bad Request. A CONNECT target must name its port (host:port); one without it is refused and logged, never sent to an assumed port.

A header ending exactly at the declared boundary is accepted. A larger one closes the client connection before opening an upstream connection; the service log names --max-header-bytes, the declared limit and the observed byte count. EOF before the terminating blank line is reported as an incomplete request. Bodies and CONNECT payload bytes that arrive with the header are forwarded without truncation. Readiness reports the actual bound socket address.

The real qualification journey is node tests/egress/headers.mjs in the Stado checkout, with STADO_BIN naming the candidate executable, STADO_TEST_SOURCE_REVISION its exact committed source revision and STADO_EGRESS_TEST_INTERFACE a real interface with a non-loopback IPv4 address on the qualification host. It launches an isolated proxy and TCP receiver, checks a boundary-sized header with a body, oversized and incomplete refusals, and CONNECT payload preservation. It keeps binary and test hashes, commands, exit statuses, received bytes and the verdict under .build/egress-headers-*. Missing prerequisites do not pass.

API request boundaries

How an API listener bounds every request head, body, operator console request and fleet join report before reading it, and where those bounds are declared: API request boundaries.

The scan load ceiling

host.scan_load_ceiling (WC_HOST_SCAN_LOAD_CEILING for one process) is the one-minute load average above which the host process skips its scans of the host's own disk — a preview of the janitor's build-cache walk, the beacon's launchd event reads, the inventory walk of a space report on its own host — and publishes the skip in the beacon's scans block (Beacon). A janitor pass on a volume at the rule's full threshold walks the build caches whatever the load, at background priority, because that walk is what frees the disk; its reading is recorded as an admission:

stado config set host.scan_load_ceiling "$LOAD_CEILING"
stado config get host.scan_load_ceiling

The value is a load average: a number, not negative, not infinite; anything else refuses the scan with host.scan_load_ceiling = "<value>" is not a load average and the beacon carries the refusal. A host that declares nothing is bounded by its logical core count, measured by the process that scans, and the beacon names that source as measured_cores.

The swap ceiling

host.swap_ceiling_pct (WC_HOST_SWAP_CEILING_PCT for one process) is the share of the host's swap, in percent, above which the host is out of memory for new work: the host process skips the scans the load ceiling governs, and stado product install, update, cargo and every swift operation wait before they export or compile, as a czekam: wait that names the reading (swap <used>% of <total> used, above host.swap_ceiling_pct <ceiling>) and starts the work once swap falls under the ceiling. A compile started on a host whose swap is full only pages: a Stado build that takes minutes took over half an hour with its compiler at two percent of a core. The janitor's relief walk on a full volume is admitted whatever the swap, as it is whatever the load.

stado config set host.swap_ceiling_pct 90
stado config get host.swap_ceiling_pct

The value is a percentage from 1 to 100; anything else refuses with host.swap_ceiling_pct = "<value>" is not a percentage from 1 to 100. A host that declares nothing has no swap ceiling, and its swap is only reported. A host whose swap cannot be read (vm.swapusage or /proc/meminfo did not answer) is admitted, and the cause is kept beside the reading in the beacon's scans block.

The Tailscale client paths

tailnet.clients (STADO_TAILSCALE_CLIENTS, comma-separated, for one process) lists the paths this host's Tailscale client may be at; the first that is a file is the client. A host that declares nothing uses /usr/bin/tailscale, /usr/local/bin/tailscale, /opt/homebrew/bin/tailscale and /Applications/Tailscale.app/Contents/MacOS/Tailscale. A path that cannot be examined is the beacon's failed cause; a list of which no path holds a file is absent.

stado config set tailnet.clients '["/usr/local/bin/tailscale"]'
stado config get tailnet.clients

Machine source archive boundaries

machine.source_limits declares archive_bytes, extracted_bytes, members and trailing_bytes for machine submissions that carry a source archive. WC_MACHINE_SOURCE_LIMITS supplies the same document for one process. There is no built-in size or member budget; stado config validate checks a stored declaration, and source admission checks the effective declaration before reserving a request. CLI and Desktop configuration use the same key. See Source archive bounds for the command, refusal semantics and real qualification journey.

Source: this website