Validation Executors
Choose and configure the sandbox that runs PR code: Crabbox, E2B, Cloudflare Sandbox or your own isolated executor.
The executor runs the PR's declared validation plan, which can include install, test, typecheck, lint and build steps. Selection and dispatch live in src/validation-executor.js. An unknown or unavailable sandbox is not permission to fall back to the worker host. The explicit local route is an exception: it runs on the host only after operator opt-in. A self-host command must enforce isolation itself.
Unless otherwise marked, source paths refer to son-of-anton-review on feat/cloudflare-native. The operator API on son-of-anton-operator-parity (feat/greptile-operator-parity) is separate and unmerged. See Validation and Evidence.
Selecting an executor
Set GILF_VALIDATION_EXECUTOR. Resolution order (resolveValidationExecutor):
GILF_VALIDATION_EXECUTOR(explicit)- Per-repo
validation.provider, then legacyGILF_VALIDATION_PROVIDER - Default:
managed-crabbox
| Value | Aliases | Runs where | Ready when |
|---|---|---|---|
managed-crabbox | crabbox | Compute selected by the operator's Crabbox configuration | crabbox binary available at run time, with trusted configuration |
managed-e2b | e2b | Hosted E2B sandbox | E2B_API_KEY set |
managed-cf-sandbox | cf-sandbox, cloudflare-sandbox, cfsandbox | Cloudflare Sandbox SDK, Cloudflare-native runtime only | Sandbox Durable Object binding present |
self-host | selfhost, self_host | Your own container, VM or microVM | GILF_SELF_HOST_VALIDATION=1 and GILF_SELF_HOST_EXECUTOR_CMD |
local | Worker host, trusted first-party repos only | GILF_ALLOW_LOCAL_VALIDATION=1 | |
shadow | Legacy combined Crabbox/E2B validation result | Both configured |
shadow and local are legacy routes, not in SUPPORTED_EXECUTORS, and cannot be a shadow lane (below).
Do not confuse legacy GILF_VALIDATION_PROVIDER=shadow with GILF_VALIDATION_SHADOW_EXECUTOR. The former combines both providers' summaries and missing validations, includes findings when both produced findings, and refuses host fallback when both are unavailable. It is not the telemetry-only shadow lane (src/codex-review-runner.js:1451-1482).
GILF_VALIDATION_EXECUTOR=managed-e2b
E2B_API_KEY=e2b_...managed-crabbox
| Variable | Default | Purpose |
|---|---|---|
GILF_CRABBOX_BIN | crabbox | Binary to invoke |
GILF_CRABBOX_ARGS | [] | Extra args as a JSON array; choose flags supported by the installed Crabbox binary |
GILF_CRABBOX_CONFIG | unset | Pinned config path, passed as CRABBOX_CONFIG so repo-local config in cwd is not the authority |
GILF_CRABBOX_TIMEOUT_MS | 2700000 (45 min) | Per-run timeout |
GILF_CRABBOX_ALLOW_REPO_CONFIG | unset | Set 1 to allow an in-repo Crabbox config |
Crabbox executes an in-repo crabbox.yaml, crabbox.yml, .crabbox.yaml or .crabbox.yml as project automation: it can pick a host-local provider or in-repo helper binaries. Reviews run on untrusted PRs, including forks, so the run is refused when such a file exists unless GILF_CRABBOX_ALLOW_REPO_CONFIG=1. Review and pin the config first.
managed-e2b
| Variable | Default | Purpose |
|---|---|---|
E2B_API_KEY | required | E2B credential |
GILF_E2B_TEMPLATE | empty | Sandbox template |
GILF_E2B_CPU_COUNT | 2 | vCPUs |
GILF_E2B_MEMORY_MB | 512 | Memory |
GILF_E2B_TIMEOUT_MS | Collector fallback 600000; effective repo config can supply 2700000 | Per-run timeout |
GILF_E2B_ALLOW_INTERNET | true | false or 0 cuts all egress |
GILF_E2B_ALLOWED_DOMAINS | unset | JSON array allowlist, e.g. ["registry.npmjs.org"] |
Do not treat the collector's 10-minute fallback as the effective timeout everywhere. DEFAULT_REPO_CONFIG.validation.e2b.timeoutMs is 2700000 (45 minutes), and runSingleValidation passes the merged repo timeout to the collector. The native manifest explicitly sets GILF_E2B_TIMEOUT_MS=600000. Choose an explicit timeout rather than relying on this mismatch (src/config.js:18-28, src/validation-executor.js:508-517, src/codex-review-runner.js:1318-1328, cloudflare-native/wrangler.jsonc:143-150).
Egress defaults open so registry installs work.
managed-cf-sandbox
The shipped native entrypoint wires this collector on its Cloudflare-backed store path, and buildContainerEnv supplies GILF_VALIDATION_EXECUTOR=managed-cf-sandbox by default plus GILF_CF_SANDBOX_ENABLED=1. Setting the readiness flag on Node does not wire the collector (cloudflare-native/src/container-env.js:72-86; cloudflare-native/container/entrypoint.mjs:325-343).
The lane needs the Sandbox Durable Object in cloudflare-native/wrangler.jsonc:
"containers": [
{ "class_name": "Sandbox", "image": "./sandbox.Dockerfile", "instance_type": "standard-3", "max_instances": 10 }
],
"durable_objects": { "bindings": [ { "name": "Sandbox", "class_name": "Sandbox" } ] },
"migrations": [ { "tag": "v2", "new_sqlite_classes": ["Sandbox"] } ]sandbox.Dockerfile:12-19 extends docker.io/cloudflare/sandbox:0.12.9 and installs git and CA certificates. Validation commands run in that sandbox, not the model-key-holding review container. This describes the validation lane, not a guarantee about every possible model/worker tool.
Without the collector, the seam reports has no Cloudflare Sandbox binding available (getSandbox collector not wired). Timeout: GILF_CF_SANDBOX_TIMEOUT_MS, collector default 1800000 (30 minutes). That timeout variable is not forwarded from the Worker to the container (src/validation-executor.js:524-544; cloudflare-native/src/cf-sandbox-validation.js:68-83; cloudflare-native/src/container-env.js:15-70).
self-host
Runs the packaged repo and validation shell inside compute you control. Both settings are required; missing either refuses.
GILF_VALIDATION_EXECUTOR=self-host
GILF_SELF_HOST_VALIDATION=1
GILF_SELF_HOST_EXECUTOR_CMD=/path/to/run-in-sandboxYour command is invoked as:
<cmd> <archivePath> <shellPath> <timeoutMs>It must start isolated compute, extract the archive, run the shell and return the __GILF_VALIDATION_*__ markers on stdout. The runner waits timeoutMs plus 5 minutes. The command is split on whitespace, not parsed by a shell; use a wrapper executable rather than shell quoting or pipelines in this variable (src/validation-executor.js:304-360).
Isolation contract (SELF_HOST_ISOLATION_CONTRACT):
- Secrets: child env reduced to a safe allowlist. No GitHub App or token, webhook, queue or model keys reach the command.
- Network: egress is yours to enforce inside the container. Default-deny recommended.
- Cleanup: the packaging temp dir is removed after the run. Your command tears down its own compute.
| Variable | Default | Purpose |
|---|---|---|
GILF_SELF_HOST_VALIDATION | unset | Must be 1 |
GILF_SELF_HOST_EXECUTOR_CMD | unset | Command to invoke |
GILF_SELF_HOST_NETWORK | unset | Free-form egress intent, forwarded to the command |
GILF_SELF_HOST_TIMEOUT_MS | 2700000 (45 min) | Timeout passed as the third argument |
GILF_SELF_HOST_REPO_DIR | set by runner | Checkout path, exported to the command env |
local
Refused by default: it runs PR code with the worker's secrets in reach. Trusted first-party repos only:
GILF_VALIDATION_EXECUTOR=local
GILF_ALLOW_LOCAL_VALIDATION=1Shadow lane
A second executor runs the same PR concurrently for comparison. Its result becomes telemetry and is not merged into primary findings, missing validations or the published result. The wait is bounded, but the lane still consumes compute. The validation-executor Test Lab definition lives on the separate operator-parity branch.
| Variable | Default | Purpose |
|---|---|---|
GILF_VALIDATION_SHADOW_EXECUTOR | unset | One of SUPPORTED_EXECUTORS. Must differ from the primary or the lane does not run |
GILF_VALIDATION_SHADOW_TIMEOUT_MS | 600000 | Caps lane timeout config and bounds the wait; a timeout is reported as shadow telemetry |
GILF_E2B_BUDGET_USD | 50 | Native dispatch only: sums validation.e2b span costs and withholds the exact managed-e2b shadow selector at the ceiling. D1 read errors log e2b_budget_read_failed and leave it enabled. Not a primary or monthly budget. |
GILF_VALIDATION_COST_RATES | unset | JSON rate-card override per executor for estimate:wall-clock costs |
Lane timeouts are clamped to the shadow budget. Rollback: remove GILF_VALIDATION_SHADOW_EXECUTOR and redeploy.
Per-repo validation config
Per-repo config is stored as repo_configs.config_json in D1 or SQLite; the Node server seeds validation config from its environment. DEFAULT_REPO_CONFIG in src/config.js:11-28 contains:
{
"validation": {
"provider": "crabbox",
"codeChangesOnly": true,
"crabbox": { "args": [], "timeoutMs": 2700000 },
"e2b": {
"timeoutMs": 2700000,
"cpuCount": 2,
"memoryMB": 512,
"template": "",
"allowInternet": true,
"allowedDomains": []
}
}
}provider feeds step 2 of executor resolution, but first the runner merges configuration. Top-level default config wins over run config, while nested Crabbox/E2B run fields win over their defaults. Thus environment-derived provider/code-changes-only defaults can override stored top-level choices. codeChangesOnly: true skips docs-only and generated-artifact-only diffs; the env parser disables it only for GILF_VALIDATION_CODE_CHANGES_ONLY=false, not 0 (src/validation-config.js:11-31; src/codex-review-runner.js:1529-1543,2528-2561).
Native forwards the E2B key, shape/time limits and shadow selector, but not the E2B egress variables, Crabbox configuration or self-host command/opt-in. Do not assume the presence of an executor selector means all of its configuration reaches the container. See Environment reference.
Readiness view
describeValidationExecutors(env) reports selection and configuration hints without secrets. Crabbox reports configured before binary availability is checked; E2B checks key presence; Cloudflare checks the readiness flag; self-host reports opt-in and command presence. These are not live connectivity tests (src/validation-executor.js:99-134).
The parity /operator/api/settings handler returns review policy, not this executor descriptor (son-of-anton-operator-parity/cloudflare/src/review-policy-api.js:48-63). Refusal wording differs by route: the generic seam emits REFUSED validation (...), local emits REFUSED local validation, and managed collectors supply their own summary before sandbox-unavailable normalization. Inspect missingValidations and validation_refused, not one universal string.
Source evidence: src/validation-executor.js:30-96,251-282,287-360,378-430,485-579; src/codex-review-runner.js:1254-1340; cloudflare-native/src/container-env.js:100-141. A refusal is an infrastructure gap, not a passed validation or proof of a PR regression.