Skip to content

Latest commit

 

History

History
277 lines (249 loc) · 59.1 KB

File metadata and controls

277 lines (249 loc) · 59.1 KB

PolicyWitness limits

A profile accepted by libsandbox can still exceed PolicyWitness's input capacities, exhaust an execution budget, or produce more evidence than a reply can carry. This inventory covers specimen admission, execution, comparison transport and retained evidence.

Counts of UTF-8 bytes are not counts of characters. Admission limits apply before worker launch. Capture limits usually reduce evidence after work has happened. The diagnostic sbpl-check helper has separate limits and is not the normal worker admission path. Its import inventory does not control libsandbox's own import resolution or compilation.

Interactions that matter

  • Raising --timeout-ms changes the client wait only. The worker and validator retain their own budgets. None of these numbers promises an end-to-end runtime: worker policy transfer precedes polling, synchronous validator work is outside the worker polling budget, and cleanup/reaping can take additional time.
  • The controller's runner-client budget is derived, not tuned: three times the synthesized maximal reply (runner_reply_maximum, the field-complete reply fixture with 256 steps and records and every string at its limit, encoded by the production encoder), rounded up to a whole 4 MiB. The sbpl-check streams keep an independent 8 MiB. These receivers report their budget in capture_limit_bytes; collection buffers the whole stream first. The synthesized number is an upper bound for the schema, since it puts fields that cannot co-occur in one run side by side; the live 256-step corpus is evidence that real replies stay inside it. A reply string key added without a size classification fails runner_unit, so the bound follows the schema.
  • Log collection enforces limits during reads: inner log-show stdout 1 MiB and stderr 128 KiB, observer stdout 32 MiB and stderr 128 KiB. The outer allowance accommodates repeated raw lines, JSON escaping and metadata from bounded inner output; derived structures have independent guards. The runner's reply size and step count cannot bound OS log volume. Both supervisors share one monotonic deadline and a fixed cleanup grace. --log-timeout-ms changes the time allowance, leaving byte limits and the padded query interval unchanged.
  • Service and direct orchestration share admission. Top-level metadata comes first, then plan/parameter counts, worker strings and host query fields. Each string checks its UTF-8 capacity before its native-string constraint. Refusals select one diagnostic but independently sanitize every echoed metadata field. Oversized or invalid identities are omitted or replaced by explicit placeholders, never shortened into apparent submitted identities.
  • Native C strings (source, parameters, step IDs, targets, exec arguments, query operations/values and override paths) reject embedded NUL before any process work. The admission record counts nul_bytes against maximum zero. The worker's reader refuses a NUL in the policy on its own (exit 9, failure code 9, offset in detail) rather than compile a prefix; the shared-memory string slots carry no length, so for them the host rule is the only guard. Other control characters and valid Unicode survive JSON transport exactly; host-only metadata and labels may also contain escaped NUL. The validator rejects raw controls, invalid UTF-8, malformed escapes and lone surrogates, while preserving the next physical probe line. Decoder failures report a bounded category/path instead of arbitrary input-derived exception prose.
  • Exec attempts spend descriptors before the sandbox applies, four per step, so the worker counts free descriptor slots and raises its soft limit to fit the plan plus reserved headroom before opening any pipe. Inherited descriptors count against availability. If the hard limit prevents the plan from fitting, excess exec steps report a descriptor-budget refusal before opening pipes; compilation and other attempts keep their headroom. The refusal is per-step evidence and does not by itself fail the run.
  • Exec attempts share a local active-time budget as well as a per-child deadline. The plan cutoff starts before worker setup, excludes the measured release wait, and never restarts after spawn. This is separate from the host polling budget; the nominal 5,000 ms margin is a configuration allowance, not a guarantee against blocking calls or delayed scheduling.
  • Pipe EOF does not establish child exit. The worker retains the unreaped leader while observing streams so deadline cleanup can still target its process group after a leader exit. Kill/wait/clock errors remain errors; final reaping uses a separate bounded, nonblocking observation window. Group termination cannot cover descendants that leave that group. A worker dying before slot completion leaves exec details unpublished, not proof that no child spawned or that cleanup succeeded.
  • Deny-log capture has no fixed lookback limit. The requested interval is the runner client's wall-clock span: floor(start) - 2 s through ceil(end) + 2 s. Whole-second rounding accommodates log show precision; the additional pad allows for client/archive clock differences. window.pad_seconds records 2. Raw client milliseconds are unchanged. Reversed endpoints prevent the scan; ordered endpoints do not establish clock continuity or complete log delivery. Archive access has been observed to cost seconds even for short spans; scan cost is not guaranteed to be independent of span or log volume.

Values are maxima unless labelled as defaults or fixed allowances.

Specimen admission

Limit Value Counting and consequence Control
Policy source (policy_source) 262,143 UTF-8 bytes Final SBPL source after augments; excludes terminating NUL. Imported file contents are not added to this count. Excess rejects the specimen after decoding and before semantic validation or process work: bad_request with host-owned admission_failure. Fixed; no public override.
Probe steps (probe_steps) 256 items Entries in probe_plan. Excess rejects the specimen after decoding and before semantic validation or process work: bad_request with host-owned admission_failure. Fixed; no public override.
Policy parameters (policy_parameters) 1,024 items Entries in the policy parameter dictionary. Excess rejects the specimen after decoding and before semantic validation or process work: bad_request with host-owned admission_failure. Fixed; no public override.
Step ID (step_id) 63 UTF-8 bytes Each step_id, excluding terminating NUL. A refused step ID is identified by step_index only, never echoed. Excess rejects the specimen after decoding and before semantic validation or process work: bad_request with host-owned admission_failure. Fixed; no public override.
Attempt target (attempt_target) 511 UTF-8 bytes Each attempt target (path, service or sysctl name), excluding terminating NUL. Also the exec argv[0]. Excess rejects the specimen after decoding and before semantic validation or process work: bad_request with host-owned admission_failure. Fixed; no public override.
Supplied exec arguments (exec_arguments) 15 items Arguments supplied in attempt.args; the target occupies the additional argv[0] slot. Excess rejects the specimen after decoding and before semantic validation or process work: bad_request with host-owned admission_failure. Fixed; no public override.
Each supplied exec argument (exec_argument) 127 UTF-8 bytes Each supplied argument, excluding terminating NUL; the target has its own larger limit. Excess rejects the specimen after decoding and before semantic validation or process work: bad_request with host-owned admission_failure. Fixed; no public override.
Parameter key (parameter_key) 127 UTF-8 bytes Each key, excluding terminating NUL. A refused key is identified by field and byte count only, never echoed. Excess rejects the specimen after decoding and before semantic validation or process work: bad_request with host-owned admission_failure. Fixed; no public override.
Parameter value (parameter_value) 383 UTF-8 bytes Each value, excluding terminating NUL. Excess rejects the specimen after decoding and before semantic validation or process work: bad_request with host-owned admission_failure. Fixed; no public override.
Query operation (query_operation) 127 UTF-8 bytes Each sandbox_check.operation, excluding terminating NUL. Host-only: the string goes to the validator line and is echoed per step in the reply; it never enters shared memory. Excess rejects the specimen after decoding and before semantic validation or process work: bad_request with host-owned admission_failure. Fixed; no public override.
Query filter value (query_filter_value) 511 UTF-8 bytes Each sandbox_check.filter.value when present, for every filter kind including none and unrecognized kinds, excluding terminating NUL. Independent of the attempt target: a step may query one path and attempt another, and each string is bounded on its own. Excess rejects the specimen after decoding and before semantic validation or process work: bad_request with host-owned admission_failure. Fixed; no public override.
Probe filter and attempt labels (probe_plan_label) 127 UTF-8 bytes Each sandbox_check.filter.kind, attempt.kind and attempt.action, excluding terminating NUL. Unknown labels within the bound retain their per-step prediction_unavailable or unsupported behavior. Excess rejects the specimen after decoding and before semantic validation or process work: bad_request with host-owned admission_failure. Fixed; no public override.
Specimen ID (specimen_id) 255 UTF-8 bytes The specimen_id string, excluding terminating NUL. Echoed once per reply; a refused ID is replaced by the placeholder <admission_refused>. Excess rejects the specimen after decoding and before semantic validation or process work: bad_request with host-owned admission_failure. Fixed; no public override.
Request labels (request_label) 63 UTF-8 bytes Each of run_kind and policy.format, excluding terminating NUL. Echoed once per reply; a refused run_kind is omitted and a refused format reads unknown. Excess rejects the specimen after decoding and before semantic validation or process work: bad_request with host-owned admission_failure. Fixed; no public override.
Test-seam executable paths (test_override_path) 1,023 UTF-8 bytes Each of _test_overrides.libsandbox_path, worker_executable_path and validator_executable_path, excluding terminating NUL. Mirrored back in test_overrides and named in dlopen and spawn diagnostics; every invalid path is independently dropped from a refusal mirror, even if another field is reported first. Excess rejects the specimen after decoding and before semantic validation or process work: bad_request with host-owned admission_failure. Fixed; no public override.

Execution budgets

Limit Value Counting and consequence Control
Worker readiness hint wait (worker_ready_wait) 1,000 milliseconds Initial ready-byte polling budget. Expiry alone does not abort: the host still checks shared-memory publication. Production default; test-only controls are not a public tuning interface.
Worker publication wait (worker_sentinel_wait) 120,000 milliseconds Nominal host polling budget from the end of the ready-byte wait until done. Synchronous validator work is outside this count; policy transfer before polling and scheduler delays are separate. The worker uses a separate local active-time budget for exec attempts. Expiry can trigger worker cleanup and runner_timeout with partial evidence. The exec budget is configured below this window but does not guarantee an end-to-end deadline. Production default; test-only controls are not a public tuning interface.
Worker exit grace (worker_exit_grace) 1,000 milliseconds Polling grace after the host requests exit. Expiry triggers a SIGKILL attempt, then reaping. Kill/reap failures remain reported. Production default; test-only controls are not a public tuning interface.
Worker release wait (worker_proceed_wait) 60,000 milliseconds Elapsed CLOCK_MONOTONIC time after successful apply, before host release acknowledgement. Expiry or clock failure publishes a proceed failure and done with no attempts. The existing exit-request spin can outlive a dead host. Production default and internal test equipment; not a public CLI tuning interface.
Nominal release margin (validator_release_margin) 5,000 milliseconds Configuration allowance for host observation, setup, decoding and scheduling, not a separately enforced timer. Production defaults satisfy 60000 > 30000 + 1000 + 5000. This guard does not cover test overrides or bound final blocking reap, host descheduling, prompt replies or eventual orphan cleanup. Production default and internal test equipment; not a public CLI tuning interface.
Validator I/O test override floor (validator_io_override_floor) 50 milliseconds Minimum effective _test_overrides.validator_io_timeout_ms. Changes only the real validator I/O deadline, with no ceiling. Over-budget values may intentionally outlast the worker release wait; expiry cannot revive attempts. The supplied value is mirrored in every reply. Production default and internal test equipment; not a public CLI tuning interface.
Validator I/O deadline (validator_io_wait) 30,000 milliseconds Elapsed CLOCK_MONOTONIC deadline for nonblocking probe writes and verdict reads; wall-clock changes cannot extend it. Retains received verdicts and records an I/O timeout; cleanup follows. Production default; _test_overrides.validator_io_timeout_ms replaces this deadline, floored at 50 ms without a ceiling and mirrored in results.
Validator exit grace (validator_exit_grace) 1,000 milliseconds Polling grace after closing validator pipes. Expiry triggers a SIGKILL attempt, then reaping; failures remain reported. Production default; test-only controls are not a public tuning interface.
Exec child deadline (exec_child_wait) 10,000 milliseconds Child observation time after successful spawn, limited by the earlier of its absolute deadline and the local exec plan deadline. Time spent spawning cannot restart the plan budget. EOF and child exit are observed separately. Deadline or observation failure requests process-group termination while the leader is still owned, even if that leader already exited. A deadline makes the attempt fail while preserving any observed natural exit code. A successful leader reap does not prove every descendant stopped. Production default; test-only controls are not a public tuning interface.
Exec attempt budget (exec_attempt_budget) 115,000 milliseconds Local CLOCK_MONOTONIC active time starting before worker setup. Only the interval returned by the release barrier is excluded. An absolute cutoff is passed to exec attempts; this is not a reconstruction of the host polling clock. An exhausted budget refuses spawn with exec_failed and ETIMEDOUT, no child identity and no sandbox attribution. Clock failure before spawn refuses the attempt; clock failure after spawn triggers cleanup and is retained as an observation error. Blocking spawn and non-exec operations are not preemptible here. Production default leaves a nominal 5,000 ms margin below worker_sentinel_wait. Internal test equipment may shorten it independently; the specimen worker_timeout_ms override does not move it.
Exec child reap grace (exec_reap_grace) 1,000 milliseconds Local monotonic observation window for nonblocking waitpid after exec observation stops. Expiry, clock failure or native wait failure retains an unconfirmed reap without inventing exit status. Failed group termination permits only an immediate nonblocking reap. This does not bound a native syscall or host descheduling. Fixed; no public override.
Exec attempt descriptors (exec_step_descriptors) 4 items Descriptors opened before sandbox application per exec step: both ends of stdout and stderr pipes. Before opening any, the worker scans for free descriptor numbers, accounting for inherited descriptors, and raises its soft limit to fit the plan plus the descriptor reserve. Raises are capped at the hard limit and OPEN_MAX (10,240); an already higher soft limit is preserved. Only exec slots that fit without spending the reserve get pipes. Excess slots report exec_failed with errno 24 and an exec descriptor budget diagnostic naming the limit; no pipe syscall or child spawn is claimed. Actual pipe failures report their own syscall and errno. Budget refusal is per-step evidence, with sandbox attribution unestablished; imports and other attempts retain descriptor headroom. Host-derived hard ceiling; no public override. The worker raises the soft limit and never lowers it.
Exec descriptor reserve (exec_descriptor_reserve) 64 items Free descriptor slots withheld from exec pipe setup, in addition to descriptors already open. The worker scans with fcntl(F_GETFD) to find room for this reserve plus four descriptors per exec step. Preserves headroom for policy compilation/imports, file probes, and spawn file actions. If the inherited/hard limit already leaves fewer free slots than the reserve, exec setup opens no pipes. This bounds exec pipe consumption; it does not guarantee that arbitrary imports or other resource users fit. Fixed; no public override.
Runner RPC wait (client_rpc_wait) 240,000 milliseconds Client wait for the runner reply. An expired wait yields runner_timeout; it does not expand the inner worker or validator budgets. Default; --timeout-ms changes only this wait and floors its value at 1 ms.

Queries and transport

Limit Value Counting and consequence Control
Validator query payload (validator_query_payload) 65,534 bytes Serialized JSON bytes for one probe, before the LF delimiter. Escaping counts. The fixed 65536-byte buffer retains the 65534-byte payload allowance; the reader counts physical bytes, including raw NUL, and drains the rest of an overlong line. An overlong line produces one parse_error with no step ID; that prediction is unavailable. Later lines can still be processed. Admitted specimens cannot reach it: with the operation and filter value admission-bounded, a fully escaped probe line stays a few KiB. Fixed; no public override.
Synthesized maximal reply (runner_reply_maximum) 24,869,018 bytes Encoded size, through the production encoder, of the field-complete reply fixture with 256 steps, 256 validator records and disposition entries, every request- or host-derived string at its documented limit and made of U+0001 (six JSON bytes per byte), the largest worker diagnostic, and the largest slash-heavy compiled-profile receipt. An upper bound for the current response schema: fields that cannot co-occur in one run are all present. Composed host path strings allow 1,535 bytes for a resolved parent plus literal leaf and 1,043 bytes for a realpath plus the supported system firmlink prefix; runner_unit checks both expansions. Not enforced anywhere; it derives the runner client budget. A reply string key added to the fixture without a size classification fails runner_unit, so the number cannot silently fall behind the schema. Recomputed by runner_unit; edit the manifest when the synthesizer's number moves.
Runner client output (controller_output) 75,497,472 bytes Per stdout or stderr stream captured from the runner client. Byte prefix before lossy text decoding; not an envelope-wide cap. Output beyond the prefix is marked truncated. Truncated JSON stdout is not parsed as a complete reply. Derived: three times runner_reply_maximum, rounded up to a whole 4 MiB. runner_unit asserts the relation against the compiled Rust constant's documented value; no public override.
Log observer stdout (log_observer_output) 33,554,432 bytes Raw observer stdout bytes, enforced while reading; includes the JSON report and final newline. Independent stderr has its own cap. One extra byte witnesses overflow; retain only the bounded raw prefix without JSON fragment recovery and withhold correlation. Fixed. Sized for bounded inner text, duplicated deny lines and parsed raw lines, six-byte JSON escaping, event metadata and reply metadata.
Log show stdout (log_show_stdout) 1,048,576 bytes Raw bytes read from log show stdout, already selected by the OS predicate, before PW decoding, parsing or PID filtering; enforced while reading. One extra byte witnesses overflow; retain only the budgeted prefix, stop collection, clean up and withhold correlation. Fixed; no public override.
Log show stderr (log_show_stderr) 131,072 bytes Raw bytes read from log show stderr, before decoding or parsing; enforced while reading. One extra byte witnesses overflow; retain only the budgeted prefix, stop collection, clean up and withhold correlation. Fixed; no public override.
Log observer stderr (log_observer_stderr) 131,072 bytes Raw bytes read from observer stderr, before decoding or parsing; enforced while reading. One extra byte witnesses overflow; retain only the budgeted prefix, stop collection, clean up and withhold correlation. Fixed; no public override.
Observer JSON structure (log_reply_structure) 262,144 items Opening object/array delimiters, commas and colons outside quoted strings, counted before allocating a JSON tree. Excess retains bounded raw diagnostic text and withholds parsing and correlation. Fixed; no public override.
Observer echoed metadata (log_observer_metadata) 4,096 UTF-8 bytes Each echoed show argument: predicate, process name, start, end, last, plan, row and correlation ID. Observer rejects excess before launching log show; controller also rejects oversized reply metadata. Fixed; no public override.
Policy helper output (policy_helper_output) 8,388,608 bytes Per stdout or stderr stream captured from sbpl-check. Byte prefix before lossy text decoding; independent of the runner reply budget. Output beyond the prefix is marked truncated. Truncated JSON stdout is not parsed as a complete reply. Fixed; no public override.
Rejected validator frame context (validator_fault_context) 256 bytes Raw prefix of the first rejected frame, before base64 encoding. The remaining frame is not retained as context; frame_bytes, retained_bytes and context_truncated describe the loss. Fixed; no public override.

Evidence capture

Limit Value Counting and consequence Control
Deny-log scan padding per endpoint (log_window_pad) 2 seconds Symmetric padding after flooring the runner client's start and ceiling its end to whole seconds. Allows for differences between the client's wall clock and the archive's displayed event timestamps; does not guarantee delivery or coverage under every clock condition. Queries floor(start) - 2 seconds through ceil(end) + 2 seconds; records in either pad remain eligible for correlation. Raw client milliseconds are unchanged; reversed endpoints still prevent collection. Fixed; no public override. window.pad_seconds records the pad.
Default log collection timeout (log_collection_timeout) 10,000 milliseconds Shared CLOCK_MONOTONIC allowance starting before observer launch; includes startup, inner log show capture and processing. The log child receives the allowance minus the report reserve. Standalone show uses the same finite default. Expiry stops collection and starts the fixed cleanup grace; available diagnostics survive without associations. Override with --log-timeout-ms: positive integer milliseconds representable as a monotonic deadline plus cleanup grace. Validated before runner invocation even with --no-log-capture.
Log cleanup grace (log_cleanup_grace) 1,000 milliseconds Cleanup ends no later than the original collection deadline plus this grace; early failures start the grace immediately. Unconfirmed reaping or group absence is reported; retries never restart the allowance. Fixed; no public override.
Log report reserve (log_report_reserve) 1,000 milliseconds Withheld from the shared deadline at the log show boundary: the observer stops its log child this long before the controller's deadline so it can reap the child and deliver its report. The controller's own deadline is unchanged. Leaves time for an intact observer reply containing the inner cutoff and retained diagnostics. Cleanup and scheduling can consume this reserve; an interrupted report retains only bounded transport diagnostics. An allowance at or below the reserve leaves no time for the query itself. Fixed; no public override. supervision.reserve_ms records 0 at the observer boundary and this value under observer.data.collection.
Parsed deny events (log_deny_events) 8,192 records Parsed deny events in show output and the derived controller array. An additional event makes capture incomplete and correlation unavailable; bounded raw output and available diagnostic events survive. Fixed; no public override.
Candidate associations (log_candidate_count) 4,096 items Total event-to-step candidates, including ambiguous matches. Excess discards the whole derived association result and withholds correlation; retained events remain diagnostic. Fixed; no public override.
Candidate allocation allowance (log_candidate_bytes) 8,388,608 bytes Conservative encoded/allocation charge per candidate: six times the sum of twice the step-ID length plus path, operation, kind and action lengths, plus 1,024 bytes of structure. Excess discards all associations and withholds correlation. Fixed; no public override.
Steps admitted to correlation (log_correlation_steps) 256 items Each of the submitted plan and returned step arrays. Excess withholds log correlation; execution evidence is unchanged. Fixed; no public override.
Exec child output per stream (exec_stream) 1,023 bytes Retained bytes in each stdout/stderr text buffer, excluding NUL. A truncation marker occupies part of this space on overflow. Additional output is drained but not retained. Fixed; no public override.
Primary worker diagnostic (worker_diagnostic) 4,095 bytes Diagnostic payload bytes, excluding NUL. The primary diagnostic is bounded and reports retained length and truncation state; it is not a transcript. Fixed; no public override.
Optional compiled-object capture (applied_profile) 1,048,576 bytes Raw bytecode bytes for a nonempty supported single-profile (type 0) object, before base64 encoding. Oversize or unsupported objects leave capture unavailable without preventing policy application. Fixed; no public override.
Worker observed path (observed_path) 1,023 bytes Per-step C path-buffer payload bytes, excluding NUL. Path observation can be absent or bounded; a host-side path diagnostic is a separate observation. Fixed; no public override.
Worker attempt error text (attempt_error) 255 bytes Per-step error-buffer payload bytes, excluding NUL. Error prose is bounded; structured result/status fields remain separate. Fixed; no public override.

Diagnostic helpers

Limit Value Counting and consequence Control
sbpl-check source admission (helper_source) 4,194,304 bytes Top-level source bytes read by the diagnostic helper; not the runner policy cap. policy_too_large without a compile verdict. Fixed; no public override.
sbpl-check import inventory depth (helper_import_depth) 8 levels Top-level imports start at depth 0. At depth 8 the helper records a depth-limit diagnostic instead of reading/expanding that file. Stops inventory expansion on that branch and marks imports_truncated. Does not impose this depth on libsandbox compilation. Fixed; no public override.
sbpl-check import inventory count (helper_import_count) 64 records Maximum records accumulated by the helper traversal, including unresolved/error records; visited files/names are deduplicated. Stops further inventory traversal and marks imports_truncated. Does not impose this count on libsandbox compilation. Fixed; no public override.
Log observer stream text (observer_stream_text) 1,048,576 bytes Streaming helper mode only (--duration or --follow): retained nonempty, non-prelude log lines with one LF per line. Only whole lines that fit are retained. The first overflowing line sets log_truncated and stops text accumulation. Deny-event arrays and JSONL emission continue separately; this is not a memory or total-report cap. The normal CLI log-show path does not use this inner cap. Fixed byte cap; --no-log-capture disables normal CLI log collection, not this helper capability.

See the user guide for the request and response contracts. Tables are generated from limits.json.

Grounding and coverage

Value checks compare the inventory with compiled constants, constructed defaults or actual returned bytes. Boundary checks exercise a limit and its consequence; path checks cover related behavior without proving the exact boundary. A source reference alone is not a value check. Coverage notes below identify where behavior remains source-inspected.

Limit ID Implementation Permanent checks Behavioral coverage
policy_source PW_SHM_POLICY_BYTES; workerAdmissionFailure value: ABI_LIMITS; value: runLimitsContractTests; boundary: admission At-limit, over-limit and multibyte admission controls through the CLI.
probe_steps PW_SHM_MAX_STEPS; workerAdmissionFailure value: ABI_LIMITS; value: runLimitsContractTests; boundary: admission At-limit, over-limit and multibyte admission controls through the CLI.
policy_parameters PW_SHM_MAX_PARAMS; workerAdmissionFailure value: ABI_LIMITS; value: runLimitsContractTests; boundary: admission At-limit, over-limit and multibyte admission controls through the CLI.
step_id PW_SHM_STEP_ID_MAX; workerAdmissionFailure value: ABI_LIMITS; value: runLimitsContractTests; boundary: admission At-limit, over-limit and multibyte admission controls through the CLI.
attempt_target PW_SHM_TARGET_MAX; workerAdmissionFailure value: ABI_LIMITS; value: runLimitsContractTests; boundary: admission At-limit, over-limit and multibyte admission controls through the CLI.
exec_arguments PW_SHM_MAX_ARGV; workerAdmissionFailure value: ABI_LIMITS; value: runLimitsContractTests; boundary: admission At-limit, over-limit and multibyte admission controls through the CLI.
exec_argument PW_SHM_ARGV_BYTES; workerAdmissionFailure value: ABI_LIMITS; value: runLimitsContractTests; boundary: admission At-limit, over-limit and multibyte admission controls through the CLI.
parameter_key PW_SHM_PARAM_KEY_MAX; workerAdmissionFailure value: ABI_LIMITS; value: runLimitsContractTests; boundary: admission At-limit, over-limit and multibyte admission controls through the CLI.
parameter_value PW_SHM_PARAM_VALUE_MAX; workerAdmissionFailure value: ABI_LIMITS; value: runLimitsContractTests; boundary: admission At-limit, over-limit and multibyte admission controls through the CLI.
query_operation sandboxCheckOperationMaxBytes; queryAdmissionFailure value: runLimitsContractTests; boundary: admission; boundary: runQueryAdmissionTests At-limit, over-limit and multibyte admission controls through the CLI; constructed orchestrator controls refuse before any process work and cover every filter kind.
query_filter_value sandboxCheckFilterValueMaxBytes; queryAdmissionFailure value: runLimitsContractTests; boundary: admission; boundary: runQueryAdmissionTests At-limit, over-limit and multibyte admission controls through the CLI; constructed orchestrator controls refuse before any process work and cover every filter kind.
probe_plan_label probePlanLabelMaxBytes; queryAdmissionFailure value: runLimitsContractTests; boundary: admission; boundary: runQueryAdmissionTests Exact, over-limit and multibyte boundaries through the CLI; 256-step oversized-label regressions require a small refusal and unchanged write targets.
specimen_id specimenIdMaxBytes; requestAdmissionFailure value: runLimitsContractTests; boundary: admission; boundary: runQueryAdmissionTests Exact, over-limit and multibyte boundaries through the CLI and in constructed controls; the refusal reply never contains the refused string.
request_label requestLabelMaxBytes; requestAdmissionFailure value: runLimitsContractTests; boundary: admission; boundary: runQueryAdmissionTests Exact, over-limit and multibyte boundaries through the CLI and in constructed controls; an at-limit format passes admission and fails as bad_policy.
test_override_path testOverridePathMaxBytes; requestAdmissionFailure value: runLimitsContractTests; boundary: admission; boundary: runQueryAdmissionTests Exact, over-limit and multibyte boundaries through the CLI and in constructed controls; at-limit nonexistent paths pass admission and reach their own seam failures.
worker_ready_wait CWorkerInput value: runLimitsContractTests; path: runCWorkerTests Normal readiness is tested; expiry without aborting is source-inspected.
worker_sentinel_wait timeoutMsForCWorker value: runLimitsContractTests; path: runCWorkerTests Deadline and partial-publication paths use shortened test budgets; the production duration is source-inspected.
worker_exit_grace CWorkerInput value: runLimitsContractTests; path: runCWorkerLifecycleTests Deadline/cleanup paths use controlled children; the production duration is source-inspected.
worker_proceed_wait PW_PROCEED_WAIT_MS_DEFAULT value: runLimitsContractTests; value: NATIVE_LIMITS; path: proceed_wait_budget_short; path: runOrderingTests Short-budget, delayed-release, clock-failure and delayed-cleanup controls establish fail-closed behavior; production duration is value-checked.
validator_release_margin validatorReleaseMarginMs value: runLimitsContractTests Short-budget, delayed-release, clock-failure and delayed-cleanup controls establish fail-closed behavior; production duration is value-checked.
validator_io_override_floor timeoutMsForValidator value: runLimitsContractTests Short-budget, delayed-release, clock-failure and delayed-cleanup controls establish fail-closed behavior; production duration is value-checked.
validator_io_wait ValidatorClientInput value: runLimitsContractTests; path: runValidatorEvidenceTests I/O deadline and partial-evidence paths use shortened budgets; the production duration is source-inspected.
validator_exit_grace ValidatorClientInput value: runLimitsContractTests; path: runValidatorEvidenceTests Cleanup paths use controlled children; the production duration is source-inspected.
exec_child_wait PW_EXEC_CHILD_DEADLINE_MS_DEFAULT value: NATIVE_LIMITS; path: runCWorkerTests Live controls close streams while both processes remain alive and exit the leader while a descendant retains streams. Independent socket credentials, OS exit events and file effects establish the behavior.
exec_attempt_budget PW_EXEC_ATTEMPT_BUDGET_MS_DEFAULT; attempt_budget_start value: NATIVE_LIMITS; value: runLimitsContractTests; boundary: run_exec_attempt_budget Compiled values agree with the host configuration. Deterministic native controls vary spawn latency, release exclusion, intervening work, exact exhaustion, clock failures and cleanup. Real short-budget plans preserve completed prefix and trailing file effects through host assembly and serialization.
exec_reap_grace PW_EXEC_REAP_GRACE_MS value: NATIVE_LIMITS; boundary: main Controlled pending/EINTR waits exhaust the grace; kill, wait and clock failures retain missing status. Every wait is required to be nonblocking.
exec_step_descriptors PW_EXEC_DESCRIPTORS_PER_STEP; setup_exec_resources value: NATIVE_LIMITS; boundary: main; path: run_exec_descriptor_limit; path: EXEC_STEPS The per-step count is value-checked against the compiled worker. A 256-exec plan is pinned live through the CLI. Native harness cases cover a raised soft limit, 80 extra inherited descriptors, and hard caps 64 and 126 through 129 with policy imports and reads before/after execs. Every prepared child must exit cleanly and excess slots must report budget refusal. The OPEN_MAX clamp is source-inspected.
exec_descriptor_reserve PW_EXEC_DESCRIPTOR_RESERVE; prepare_exec_descriptor_budget value: NATIVE_LIMITS; path: run_exec_descriptor_limit Value-checked against the compiled worker. Capped mixed plans require successful policy imports and file reads even when all exec slots are refused, and clean exits for every prepared exec. The inherited-descriptor case requires all 32 execs with ample hard-limit capacity.
client_rpc_wait DEFAULT_TIMEOUT_MS; defaultClientTimeoutMs; defaultClientTimeoutMs value: runLimitsContractTests; value: documented_controller_limits The production wait and timeout response are source-inspected; compiled default agreement is tested.
validator_query_payload LINE_MAX_BYTES; runValidator value: NATIVE_LIMITS; boundary: query_boundary; path: validator_overlong_request Exact payload boundary, over-limit drain/recovery, valid raw and escaped Unicode, malformed escapes/UTF-8/raw controls and raw-NUL physical framing. Every rejected probe is followed by a valid recovery probe.
runner_reply_maximum maximalReplyEncodedSize; pwRunnerEncodeJSON value: runLimitsContractTests; boundary: runReplyMaximumTests; path: main The synthesizer's value is compared with the manifest; the budget relation is asserted; an injected unclassified key is detected. Live 256-step workloads are measured against this bound.
controller_output RUNNER_CAPTURE_BYTES value: documented_controller_limits; path: mod tests; path: main Exact prefix and truncation-boundary controls, including small arbitrary budgets and every cut within multibyte scalars. Collection buffers the entire stream first; this is not a memory limit. Five admitted 256-step CLI workloads at the admitted maxima, including the control-character exec workload with 511-byte targets and queries, 63-byte step IDs, maximal metadata and live profile capture, must each stay under runner_reply_maximum; the budget then holds a complete reply with a threefold margin by construction.
log_observer_output OBSERVER_STDOUT_BYTES value: documented_controller_limits; boundary: both_raw_streams_are_bounded_at_exact_edges; boundary: inner_byte_limit_with_maximal_json_escaping_fits_outer_serialization_limit; boundary: controlled_256_step_capture_keeps_every_long_target_candidate Streaming cap controls cover both pipes. A 256-record corpus with 511-byte targets preserves all records and candidate references through parser, supervised receiver, assembly and consumer recovery under the production default allowance. Exact inner stdout/stderr caps with maximum escaping fit the bounded serializer. Interrupted or failed capture changes log evidence only; live volume has no bound derived from step count.
log_show_stdout LOG_STDOUT_BYTES value: documented_collection_limits; boundary: both_raw_streams_are_bounded_at_exact_edges Both stream boundaries are tested at, below and above a substituted small cap, including concurrent stdout/stderr. Counts describe bytes actually read, never the unavailable remainder.
log_show_stderr LOG_STDERR_BYTES value: documented_collection_limits; boundary: both_raw_streams_are_bounded_at_exact_edges Both stream boundaries are tested at, below and above a substituted small cap, including concurrent stdout/stderr. Counts describe bytes actually read, never the unavailable remainder.
log_observer_stderr OBSERVER_STDERR_BYTES value: documented_collection_limits; boundary: both_raw_streams_are_bounded_at_exact_edges Both stream boundaries are tested at, below and above a substituted small cap, including concurrent stdout/stderr. Counts describe bytes actually read, never the unavailable remainder.
log_window_pad LOG_WINDOW_PAD_SECONDS value: documented_controller_limits; boundary: run_span_window_floors_start_ceils_end_and_never_collapses; boundary: requested_intervals_select_independently_timed_events; boundary: padded_records_survive_assembly_and_consumer_recovery Independent timestamp fixtures cover both padding regions, exact and exterior bounds, equal spans and rollback. Complete full, early-only, late-only and empty replies preserve eligible candidates and missing-record diagnostics through assembly, serialization and consumer recovery.
log_collection_timeout DEFAULT_LOG_TIMEOUT_MS value: documented_collection_limits; boundary: larger_allowance_buys_time_only_and_still_bounds_hangs Independent slow-success and permanent-hang fixtures prove that a larger finite allowance buys waiting time only; shared absolute deadlines are not restarted by the observer.
log_cleanup_grace CLEANUP_GRACE_MS value: documented_collection_limits; path: cleanup_failures_and_lost_ownership_remain_unconfirmed Owned orphan controls cover leader death before reply, pipes open or closed, group absence, failed signalling/probing and lost ownership. The bound concerns supervised waiting, not OS scheduling.
log_report_reserve LOG_REPORT_RESERVE_MS value: documented_collection_limits; boundary: inner_timeout_preserves_available_record_and_actual_child_wait; boundary: timeout_override_changes_waiting_only_across_both_boundaries A short inner allowance cuts off the log child while the observer's report, including its inner cutoff, still reaches the controller; a stalled query exhausts the reserved inner allowance and is reported the same way.
log_deny_events MAX_DENY_EVENTS value: documented_collection_limits; boundary: event_limit_withholds_completion_without_discarding_retained_diagnostics; boundary: maximum_event_volume_correlates_within_the_default_allowance Exact and over-limit fixtures retain diagnostics and distinguish complete capture from overflow. The maximum event volume correlates against a 256-step plan inside the default collection allowance.
log_candidate_count MAX_ASSOCIATIONS value: documented_collection_limits; boundary: derived_json_and_candidate_allocations_are_bounded A 256-record unique-path control preserves all associations; repeated matching attempts exhaust the candidate budget.
log_candidate_bytes MAX_ASSOCIATION_BYTES value: documented_collection_limits; boundary: derived_json_and_candidate_allocations_are_bounded Long repeated paths exercise the allocation allowance independently of the candidate count. This is a derived-data bound, not a peak-process-memory claim.
log_correlation_steps MAX_CORRELATION_STEPS value: documented_collection_limits; boundary: derived_json_and_candidate_allocations_are_bounded A 256-step control preserves unique candidates; over-limit arrays reject correlation.
log_reply_structure MAX_OBSERVER_JSON_TOKENS value: documented_collection_limits; boundary: derived_json_and_candidate_allocations_are_bounded Independent oversized JSON array trips the structural guard before Value allocation.
log_observer_metadata MAX_OBSERVER_METADATA_BYTES value: documented_collection_limits; path: supervised_receiver_retains_failed_inner_evidence_and_rejects_bad_shapes Receiver malformed-shape controls cover excess metadata; direct argument admission is source-inspected.
policy_helper_output HELPER_CAPTURE_BYTES value: documented_controller_limits; boundary: arbitrary_budgets_preserve_counts_at_every_unicode_cut The shared receiver accepts an explicit per-stream retention budget for this producer; exact byte counts, truncation and JSON parse suppression are tested independently of the runner budget.
validator_fault_context decodeValidatorFrames value: runLimitsContractTests; boundary: runLimitsContractTests Exact and over-limit invalid-frame context controls; no rejected bytes become verdicts.
exec_stream PW_SHM_CHILD_OUTPUT_BYTES value: ABI_LIMITS; value: runLimitsContractTests; path: runCWorkerTests Small stdout/stderr capture is tested. Overflow truncation and the exact retained-prefix/marker split are source-inspected.
worker_diagnostic PW_SHM_DIAGNOSTIC_BYTES value: ABI_LIMITS; value: runLimitsContractTests; path: runWorkerEvidenceTests Bounded diagnostic/truncation controls in the Swift worker evidence tests.
applied_profile PW_SHM_CAPTURE_BYTES value: ABI_LIMITS; value: runLimitsContractTests; boundary: main Independently constructed object controls cover exact, excessive and unsupported captures.
observed_path PW_SHM_OBSERVED_PATH_MAX value: ABI_LIMITS; value: runLimitsContractTests Source-inspected buffer use; no exact path-length behavior claim is tested.
attempt_error PW_SHM_ERROR_MAX value: ABI_LIMITS; value: runLimitsContractTests Source-inspected bounded formatting; no exact error-text boundary claim is tested.
helper_source MAX_SBPL_SOURCE_BYTES value: documented_helper_limits; path: mod tests Oversized input is tested through the shipped helper and fallback CLI; exact boundary behavior is source-inspected.
helper_import_depth IMPORT_MAX_DEPTH value: documented_helper_limits; path: mod tests Deep import-chain control in the helper unit tests.
helper_import_count IMPORT_MAX_COUNT value: documented_helper_limits; path: mod tests Import-count exhaustion control in the helper unit tests.
observer_stream_text MAX_CAPTURE_BYTES value: documented_observer_limits Compiled value agreement is tested. Streaming accumulation and overflow behavior are source-inspected.

Maintaining this document

Edit limits.json, then run python3 docs/generate_limits.py from the repository root. The same command copies the marked shared section into PolicyWitness.md; edit its explanations here. Review the handwritten explanations as well as the tables. The same command also copies the shared questions from QUESTIONS.md into the guide's Questions section; edit the questions there, writing links into the guide as PolicyWitness.md#anchor. The JSON is a reviewed description; production code does not load it.

python3 docs/generate_limits.py --check verifies the manifest's shape, source and check references, generated text in both documents, the copied questions, and the guide's internal links. The copied sections must contain every limit and every shared question and require no companion files or web pages. --stage-guide PATH performs the same checks before copying the guide; it refuses stale documents without regenerating them. The build checks freshness before compilation and stages the guide before packaging.

These checks cannot establish implementation agreement by themselves. source_drift exercises invalid and stale inputs, copying and staging, a standalone guide, and refusal to build with stale documentation. runner_abi_layout compares compiled C values and exercises native query boundaries; runner_unit checks compiled Swift values/defaults and rejected-frame capture. Rust unit tests check controller and helper constants. Existing behavioral suites remain independent of the manifest, so changing a documented number cannot change their oracles.

For a limit change, run cargo test --manifest-path controller/Cargo.toml and tests/run.sh --suite source_drift --suite runner_abi_layout --suite runner_unit --suite runner_c_worker_harness --suite failure_boundaries against a normal signed build, plus the affected behavior owners named above. New entries need an implementation-value check and an honest coverage note; a link to source is not enough. Do not label a shortened timeout control as a test of the production duration, or a constant comparison as proof of its operational consequences.