Skip to main content

Remote Runners

Remote Runners let EvalGate dispatch prompts, scorers, tools, workflows, and agent entrypoints into infrastructure that you control. The control plane retains job identity, policy, retry, and evidence authority; the worker receives one signed, immutable execution envelope at a time. Open Remote Runners at /remote-runners to register workers, inspect capacity and heartbeats, drain or revoke identities, monitor jobs, request cancellation, and review each job’s claims, state transitions, stream custody, terminal completion, and reconciliation decisions.
A remote job binds an exact release artifact ID, version ID, SHA-256 content hash, and canonical dev, staging, or prod environment. A runner cannot substitute another version, change the environment, or resolve “latest.”

Permissions

Runner endpoints do not accept a user session or ordinary API key as worker authority. The worker first exchanges its one-time bootstrap secret for a credential that expires within ten minutes and is bound to one organization and runner identity.

Security and identity model

Each registered runner has:
  • A stable organization-scoped runnerKey
  • A declared protocol version and content-hashed capability document
  • A maximum concurrent-job count enforced in the database
  • A one-time bootstrap token stored only as a SHA-256 hash
  • Short-lived, revocable worker credentials with least-privilege scopes
  • A derived HMAC-SHA256 job-signing key and visible key ID
  • Heartbeat, capacity, last-contact, drain, loss, and revocation state
Rotating a bootstrap token revokes all outstanding short-lived credentials. Revocation is permanent for that identity; register a replacement runner rather than reactivating a revoked credential boundary.
The bootstrap token is displayed once. Save it directly into your secret manager before closing the dialog. EvalGate cannot recover the plaintext value.

Register a runner

  1. Open /remote-runners and choose Register runner.
  2. Enter a display name and stable runner key.
  3. Declare immutable runtime identifiers, such as nodejs20 or python3.12.
  4. Select the supported execution kinds.
  5. Set the maximum concurrent-job capacity.
  6. Review the default-deny isolation boundary and choose Register identity.
  7. Copy the displayed environment variables and worker start command into your deployment secret and workload configuration.
The current registration flow declares network deny_all, streaming and cancellation support, one-hour CPU, 64 GiB memory, 24-hour wall time, and a 100 MiB output ceiling. The API accepts a narrower capability document when the worker has smaller limits or a reviewed HTTPS origin allowlist.

Capability negotiation

Before a claim is created, the control plane compares every job requirement with the runner declaration: An incompatible runner simply does not receive the job. EvalGate does not silently relax a resource or network requirement to make a claim succeed.

Start a TypeScript worker

The TypeScript SDK verifies the organization binding, key ID, validity window, canonical payload hash, HMAC signature, claim identity, and nonce before returning an assignment to application code.

Start a Python worker

The Python SDK implements the same canonical hashing, signing verification, replay protection, credential refresh, and endpoint contract.

Enqueue an immutable job

Use POST /api/remote-runners/jobs with a runs:write API key or authenticated session. The request contains:
  • A stable idempotency key
  • Subject kind, artifact ID, exact version ID, and dev, staging, or prod environment
  • A JSON input snapshot
  • Execution kind, runtime, resource limits, network policy, and streaming/cancellation requirements
The server loads the exact release version in the caller’s organization, computes and persists its content hash when needed, checks the version’s environment, and creates the queued job and audit event atomically. Repeating the same idempotency key with identical intent returns the original job; different intent returns a conflict.

Signed claim lifecycle

Claim insertion is serialized at the database boundary. A job can have only one active claim and one claim per dispatch attempt. The signed envelope includes the organization, job, attempt, idempotency key, exact subject identity, input hash, requirements, issue/expiry timestamps, and a single-use nonce. The first heartbeat transitions a claimed job to running and extends the lease for 15–120 seconds. It also returns cancelRequested and the human cancellation rationale so the worker can stop at a safe boundary.

Stream logs and trajectories

Each log or trajectory chunk includes:
  • Job, claim, and dispatch-attempt identity
  • A zero-based contiguous sequence
  • The prior chunk checksum, or null for sequence zero
  • A deterministic payload hash
  • A checksum over identity, sequence, kind, prior checksum, payload hash, and emission time
The service and database both reject gaps, reordered chunks, changed claim identity, duplicate sequence numbers, duplicate checksums, and appends to an expired claim. The terminal completion must close the exact last stored checksum.

Complete with normal EvalGate evidence

A completion is accepted only for the active claim and attempt. It binds the input hash, result hash, terminal status, final stream checksum, and normal EvalGate evidence:
  • Evaluation run ID
  • Test-result IDs
  • Cost-record IDs
  • Model-call ledger IDs
  • Trace ID
All referenced evidence must belong to the same organization and the exact evaluation run named by the completion; each evidence row may be attached to only one accepted remote completion. Completion insertion, evidence joins, job finalization, claim release, runner-capacity decrement, and the terminal event occur in one transaction. Exact completion replay returns duplicate. A different completion after acceptance returns conflict. A completion from an earlier dispatch attempt returns stale. These decisions are stored with their checksum, rationale, and reported costs rather than discarded.

Cancellation and runner loss

Queued and lost jobs cancel immediately. Claimed and running jobs enter cancel_requested; the worker sees that state on heartbeat. If cancellation wins the terminal race, the job becomes cancelled while already-incurred provider or sandbox costs remain authoritative. When a heartbeat lease expires, the reaper:
  1. Marks the claim and runner lost.
  2. Releases the runner’s active capacity.
  3. Appends a durable loss event.
  4. Requeues the job with no active claim when fewer than three attempts have been dispatched.
  5. Uses a new nonce and incremented attempt for the next compatible runner.
EvalGate invokes bounded recovery from the existing CRON_SECRET-authenticated job runner. Organization admins can also trigger the same bounded recovery from the control plane during incident response; both paths retain the durable loss and requeue events. An older worker cannot complete the replacement attempt. Accepted results, test results, model calls, and cost records have unique custody constraints, preventing duplicate results or charges.

Operate and troubleshoot

Use the Jobs tab and Evidence action to inspect the complete state timeline and reconciliation history before retrying or rotating a worker.