Son of Anton Docs
Configuration

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):

  1. GILF_VALIDATION_EXECUTOR (explicit)
  2. Per-repo validation.provider, then legacy GILF_VALIDATION_PROVIDER
  3. Default: managed-crabbox
ValueAliasesRuns whereReady when
managed-crabboxcrabboxCompute selected by the operator's Crabbox configurationcrabbox binary available at run time, with trusted configuration
managed-e2be2bHosted E2B sandboxE2B_API_KEY set
managed-cf-sandboxcf-sandbox, cloudflare-sandbox, cfsandboxCloudflare Sandbox SDK, Cloudflare-native runtime onlySandbox Durable Object binding present
self-hostselfhost, self_hostYour own container, VM or microVMGILF_SELF_HOST_VALIDATION=1 and GILF_SELF_HOST_EXECUTOR_CMD
localWorker host, trusted first-party repos onlyGILF_ALLOW_LOCAL_VALIDATION=1
shadowLegacy combined Crabbox/E2B validation resultBoth 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

VariableDefaultPurpose
GILF_CRABBOX_BINcrabboxBinary to invoke
GILF_CRABBOX_ARGS[]Extra args as a JSON array; choose flags supported by the installed Crabbox binary
GILF_CRABBOX_CONFIGunsetPinned config path, passed as CRABBOX_CONFIG so repo-local config in cwd is not the authority
GILF_CRABBOX_TIMEOUT_MS2700000 (45 min)Per-run timeout
GILF_CRABBOX_ALLOW_REPO_CONFIGunsetSet 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

VariableDefaultPurpose
E2B_API_KEYrequiredE2B credential
GILF_E2B_TEMPLATEemptySandbox template
GILF_E2B_CPU_COUNT2vCPUs
GILF_E2B_MEMORY_MB512Memory
GILF_E2B_TIMEOUT_MSCollector fallback 600000; effective repo config can supply 2700000Per-run timeout
GILF_E2B_ALLOW_INTERNETtruefalse or 0 cuts all egress
GILF_E2B_ALLOWED_DOMAINSunsetJSON 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-sandbox

Your 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.
VariableDefaultPurpose
GILF_SELF_HOST_VALIDATIONunsetMust be 1
GILF_SELF_HOST_EXECUTOR_CMDunsetCommand to invoke
GILF_SELF_HOST_NETWORKunsetFree-form egress intent, forwarded to the command
GILF_SELF_HOST_TIMEOUT_MS2700000 (45 min)Timeout passed as the third argument
GILF_SELF_HOST_REPO_DIRset by runnerCheckout 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=1

Shadow 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.

VariableDefaultPurpose
GILF_VALIDATION_SHADOW_EXECUTORunsetOne of SUPPORTED_EXECUTORS. Must differ from the primary or the lane does not run
GILF_VALIDATION_SHADOW_TIMEOUT_MS600000Caps lane timeout config and bounds the wait; a timeout is reported as shadow telemetry
GILF_E2B_BUDGET_USD50Native 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_RATESunsetJSON 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.

On this page