> ## Documentation Index
> Fetch the complete documentation index at: https://checkfu.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Customer-hosted Runners

> Run the Checkfu Runner on your own machine and drive the same durable Sessions the hosted data plane serves.

A customer-hosted Runner in Checkfu is not a separate product. It claims work over the same
public enrollment/claim/settle wire as hosted execution and writes to the same
Session log, dashboard, approvals, and audit trail, while execution custody stays
on a machine you control. An Agent can route to a named RunnerPool, but it never
chooses its own placement.

What is *not* shared is the artifact. `checkfu-runner` — the Runner this page
starts — is the customer-side executor, and it is the one Checkfu ships for the
lanes that are physically outside Checkfu's own infrastructure: your own pools,
local Docker, host-process. Checkfu-hosted execution is ratified to run a
different implementation of the same wire (D217), so treat the protocol as the
compatibility surface and never a shared binary. Nothing about the contract below
changes under that split: it is public, versioned, and the thing both sides
implement.

<Warning>
  Customer-hosted execution is engineering-preview material, not a supported
  deployment product. The source-checkout flow below exercises implemented
  public BYOC protocols. Hosted managed-runtime readiness remains rollout-fenced;
  check the [status page](/getting-started/status) and your deployment's
  `GET /v1/support/capabilities` response before treating an endpoint as
  release-proven. The private Runner archive is qualification evidence, not a
  customer distribution channel.
</Warning>

## How it works

The Runner makes outbound HTTPS requests to the control plane, claims only work
inside its server-owned enrollment ceiling, provisions the admitted sandbox,
and reports observations. Checkfu never dials your machine: no inbound port,
tunnel, or webhook receiver is required.

The Session remains the settlement authority. A Run executed on your laptop
therefore writes to the same durable event log as one executed on hosted
capacity, and the transcript does not depend on that laptop staying awake.
Stopping a Runner fences new claims and drains owned work. Revoking its
credential is the control-plane kill switch for later claims and writes.

## Choose an enrollment scope

Use pairing for a person's machine. An admin creates a short-lived code naming
the active Principal; redeeming it stamps that Principal into a new Runner
credential. The machine can claim only Sessions for that Principal and never
asserts or widens its owner ceiling.

The example uses the customer root key for Organization management. A Workspace-bound
admin key can create the same pairing. The root key, Workspace, and Principal variables come
from [Access and identities](/reference/access).

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST "$CHECKFU_API_URL/v1/runner-pairings" \
  --header "Authorization: Bearer $CHECKFU_ROOT_API_KEY" \
  --header "Checkfu-Version: 2026-08-27" \
  --header "Idempotency-Key: runner-pairing-$(uuidgen)" \
  --header "Content-Type: application/json" \
  --data '{
    "principal_id": "'$CHECKFU_PRINCIPAL_ID'",
    "machine_label": "personal-laptop",
    "expires_in_seconds": 300
  }'
```

The response returns the one-use `code`. Give only that code to the person
starting the Runner; do not give them the root key.

For a shared, Workspace-wide machine, an Organization root key can instead create a
BYOC Runner key directly. The target Workspace is in the body.

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST "$CHECKFU_API_URL/v1/organizations/$CHECKFU_ORGANIZATION_ID/api-keys" \
  --header "Authorization: Bearer $CHECKFU_ROOT_API_KEY" \
  --header "Checkfu-Version: 2026-08-27" \
  --header "Idempotency-Key: runner-key-$(uuidgen)" \
  --header "Content-Type: application/json" \
  --data '{
		"display_name": "shared-runner",
    "role": "runner",
    "scope": { "type": "workspace", "workspace_id": "'$CHECKFU_WORKSPACE_ID'" },
    "rate_limit_per_minute": 600
  }'
```

The direct response returns `secret` once. The resulting credential is a
Workspace-scoped, BYOC-only **bootstrap and recovery secret**, not the bearer
used by steady-state Runner traffic. Both enrollment paths use the server's default
compatibility ceiling unless the authenticated creator supplies an explicit,
valid `runner_enrollment`. The stored enrollment remains the ceiling: an
advertised capability outside it never becomes claim authority.

### Enroll an exact Cursor login

Cursor is the first harness-owned model lane. It is intentionally narrower than
ordinary Docker enrollment: the API-key `runner_enrollment`, the Runner's live
advertisement, the Agent's pinned HarnessProfile/Release and ModelRoutingProfile, and
the eventual claim must all agree on one non-secret tuple:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "placement": "byoc",
  "runner_pool": "rpool_<32 lowercase hex>",
  "providers": [
    {
      "id": "docker",
      "provider_revision": "checkfu:docker-provider@11",
      "materialization_revision": "checkfu:oci-digest-mounts@4",
      "drivers": ["checkfu:acp-v1@1", "checkfu:oneshot-v1@1"],
      "harness_owned_models": [
        {
          "kind": "cursor_api_key",
          "harness_profile_version": "hprof_<32 lowercase hex>@<version>",
          "harness_release_digest": "sha256:<64 lowercase hex>",
          "provider": "cursor",
          "model": "cursor-auto"
        }
      ],
      "launch_kinds": ["oci"],
      "tiers": ["container"],
      "platforms": [{ "os": "linux", "architecture": "amd64" }],
      "network_modes": ["unrestricted"],
      "retention_modes": ["durable", "ephemeral_zdr"],
      "runtime_assurances": {
        "compute_accounting": true,
        "network_isolation": true,
        "bounded_start": true,
        "bounded_cancel": true,
        "teardown": true
      },
      "snapshot_clone": true,
      "snapshot_custody": "runner",
      "package_materialization": true,
      "max_mounts": 61,
      "mount_kinds": ["uploaded_project", "git", "memory", "skills"],
      "minimum_resources": {
        "cpu_millis": 1,
        "memory_mib": 1,
        "disk_mib": 1,
        "max_duration_seconds": 1
      },
      "maximum_resources": {
        "cpu_millis": 64000,
        "memory_mib": 262144,
        "disk_mib": 1048576,
        "max_duration_seconds": 86400
      }
    }
  ],
  "plane_protocols": ["platform-mcp-v1", "platform-control-mcp-v2"]
}
```

Put that object in the `runner_enrollment` field of the Workspace Runner-key
request above. The named RunnerPool must already exist, and the AgentDefinition
must route to it. Use `arm64` instead of `amd64` when that is the Runner host's
architecture. The Cursor catalog entry remains `image_required`; build the
recipe and use its digest-pinned release before copying the profile version and
release digest into enrollment.

Configure the same tuple locally, with the credential only in the Runner
process environment:

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
export CHECKFU_CURSOR_HARNESS_PROFILE_VERSION="hprof_<32 lowercase hex>@<version>"
export CHECKFU_CURSOR_HARNESS_RELEASE_DIGEST="sha256:<64 lowercase hex>"
export CHECKFU_CURSOR_MODEL="cursor-auto"
export CURSOR_API_KEY="<Cursor credential>"
export CHECKFU_DOCKER_UNRESTRICTED_NETWORK="<Docker egress network>"
```

All four Cursor fields are atomic: defining any subset makes Runner startup
fail and names only the missing field. The API key is injected only into an
exact matched Cursor container. It is absent from enrollment, presence,
ModelRoutingProfile, RunSpec, claim grants and receipts, events, logs, and hosted
execution. A missing login, a stale profile/release/model pin, or any
non-customer placement remains visibly unschedulable.

## Start the Runner

Two flows start the same Runner. From a prepared Checkfu source checkout
(see [CLI access](/getting-started/cli-access)) `checkfu runner start` builds
and starts the Runner. From the private packaged CLI artifact the same command
launches an embedded Runner directly — no build, no checkout. This packaged
lane is a developer-laptop path for exercising the local-agent protocols; it is
**not** the D77 supported customer-hosted deployment product, whose gates stay
open, and the private archive is qualification evidence, not a customer
distribution channel.

For a principal-scoped pairing, pass the one-use code returned above:

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
checkfu runner start \
  --endpoint "$CHECKFU_API_URL" \
  --code "CFU-..."
```

The CLI redeems the code and stores the resulting bootstrap credential in protected local
state for restart recovery. This protected pairing-recovery path currently supports macOS
and Linux. Windows fails before filesystem access until native ACL, owner-SID, and
reparse-point enforcement has a real Windows proof lane. For a Workspace-scoped key, pass the
one-time `secret`; the credential already selects its Workspace:

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
export CHECKFU_RUNNER_TOKEN="<secret from the API-key response>"
checkfu runner start \
  --endpoint "$CHECKFU_API_URL"
```

### Choose a local sandbox tier

The default for every lane — hosted, BYOC, and both local ones — is stated together
with its capability consequences on [Sandbox defaults](/concepts/sandbox-defaults).

`--sandbox` selects the local execution tier. The packaged laptop lane defaults
to `host-process`, which runs harnesses as ordinary OS processes with **your
machine's own network — no container isolation and no egress enforcement**. That
is a deliberately weaker posture than the guarded Docker path (D130/D167); the
Runner advertises exactly that realized behavior, and managed Runs still admit
it only against exact conformance evidence. It is the zero-Docker default so a
laptop with no daemon still serves work. Pass `--sandbox docker` to use a
configured Docker daemon with a pinned model relay (auto-detected when the
daemon is running), or `--sandbox none` to disable the CLI-controlled
host-process tier while preserving other explicit provider configuration. Inherited
`CHECKFU_ENABLE_HOST_PROCESS_SANDBOX=true` always wins — weak isolation is
chosen, never silently inherited. Exact `false` is also accepted; any other
defined value fails before launch.

`checkfu doctor` covers the lane end to end: it proves API readiness and lists
the Workspace's live Runner presences, including each paired Runner's
server-stamped `owner` principal, lifecycle status, placement, and advertised
capabilities.

From a source checkout, `runner start` builds the Runner; from the packaged
artifact it launches the embedded one. Either way it attaches to the control
plane, streams its output, and turns Ctrl-C into an owned drain. The process
runs until interrupted.
On every start it exchanges the bootstrap credential once for a distinct
15-minute workload identity bound to the Workspace, RunnerPool, owner,
RunnerGeneration, an in-memory process nonce, and the admitted capability
ceiling. Presence, claims, heartbeats, observations, and control calls use only
that short-lived identity. Rotation is automatic; concurrent use of one
RunnerGeneration under another process nonce revokes that generation as a
clone, and explicit revocation immediately fences both the current bearer and
its bounded in-flight overlap.
`CHECKFU_RUNNER_IDLE_TIMEOUT_MS` is the maximum silence inside one harness turn,
not a process idle-exit timer. Docker is needed only for admitted container
sandboxes; the opt-in host-process tier has a deliberately smaller capability
envelope and still requires exact conformance evidence.

## Verify readiness

List the Workspace's unexpired Runner presences with a read-capable credential.
This example reuses the customer root key, which must select the Workspace:

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl "$CHECKFU_API_URL/v1/runner-pools/presences" \
  --header "Authorization: Bearer $CHECKFU_ROOT_API_KEY" \
  --header "Checkfu-Version: 2026-08-27" \
```

A presence reports the Runner identity, generation, pool, placement, lifecycle
status, and sanitized capability advertisement. A Runner enrolled through
pairing additionally carries a server-stamped `owner` — the Principal and
optional machine label the pairing named. The owner comes from the
credential's enrollment, never from the Runner's heartbeat, so a machine
cannot claim to be someone else's. Pass `?owner=<principal id>` to filter the
list to that person's paired machines — the "your machine is connected" check
an application renders per end user; Workspace-scoped Runners never match an
owner filter. From a backend, the SDK's
`client.runnerPools.createRunnerPairing` mints the code and the standalone
`redeemRunnerPairing` export performs the machine-side redeem the CLI itself
uses. A presence is an expiring liveness
projection, not lease authority or proof that every harness is usable. A
`ready` Runner can claim only Sessions whose admitted target intersects its
enrollment and advertised capabilities and has the required conformance
evidence. `pnpm exec checkfu doctor --api-url "$CHECKFU_API_URL"` performs the
bounded API-and-presence readiness check when the source-checkout CLI is
configured with an authenticated connection. For the variables used on this
page:

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
CHECKFU_API_KEY="$CHECKFU_ROOT_API_KEY" \
  pnpm exec checkfu doctor --api-url "$CHECKFU_API_URL"
```

Everything else about driving an admitted Session — creating it, streaming
events, handling approvals, and interrupting work — follows the
[hosted quickstart](/getting-started/quickstart).

## What stays honest

* **Conformance is evidence, not configuration.** An exact
  release/driver/provider tuple serves managed Runs only after the server-owned
  conformance suite proves its realized behavior.
* **Capabilities are intersected ceilings.** The Runner advertises what it can
  realize, the credential bounds what it may claim, and the Session freezes the
  admitted target. Self-advertisement alone is never platform truth.
* **Owner scope only narrows.** Pairing stamps the owning Principal on the
  server. A person cannot use the code or claim body to select someone else's
  Sessions.
* **Presence is disposable.** Missing or expired presence means no recent
  heartbeat arrived; it does not rewrite Session history or settlement.
* **Revocation is immediate for later control requests.** Revoking the Runner
  key blocks subsequent claims, observations, and settlement writes from that
  credential.

## Implemented preview building blocks

Current source also includes bounded claim long-polling, explicit RunnerPool
creation and Agent routing, an opt-in host-process provider, harness readiness
probes, a harness-owned model lane that records usage as absent rather than
estimating it, and a reproducible private sidecar archive. These are
implemented protocol and qualification building blocks, not a promise that a
customer-VPC package or external artifact channel is available.
