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

# The runner

> How the customer-hosted runner advertises capabilities, resolves credentials, and executes signed work.

The Polycore runner is the execution half of the system. It is a long-running
Node.js process deployed in your infrastructure, close to the databases and APIs
it is allowed to reach.

<Columns cols={2}>
  <Card title="It executes" icon="play">
    The runner validates inputs, resolves local credentials, calls your
    downstream systems, and returns structured results.
  </Card>

  <Card title="It does not govern" icon="scale">
    Caller authentication, routing, human approval, and audit persistence live
    in the control plane. The runner accepts signed dispatches from that plane.
  </Card>
</Columns>

## One runner, one target

Each runner is bound to one Polycore **project** and one **environment**.

* The project identifies the product or system whose catalog the runner
  advertises.
* The environment identifies the concrete call target, such as `dev`,
  `staging`, or `prod`.
* Separate runners for separate environments preserve network and credential
  isolation.

The environment is fixed during enrollment. A request cannot tell a production
runner to swap to a different secret bundle.

```mermaid theme={"system"}
flowchart LR
  P["Project: alpha"]
  D["Environment: dev"] --> RD["Dev runner"] --> DD[("Dev data")]
  S["Environment: staging"] --> RS["Staging runner"] --> DS[("Staging data")]
  R["Environment: prod"] --> RP["Production runner"] --> DP[("Production data")]
  P --- D
  P --- S
  P --- R
```

## What the runner loads

The deployed runner combines built-in, customer-authored, and
optional surfaces:

<Columns cols={4}>
  <Card title="Built-in queries" icon="database-zap">
    Generic, read-only capability families supplied by the runner SDK
    (`@polycore/runner`), currently including Postgres and Firestore. Firestore
    can additionally be granted approval-gated generic mutations.
  </Card>

  <Card title="Customer actions" icon="braces">
    Named TypeScript operations from the `actions/` directory. Each action has
    typed input and output, a hazard, and an explicit secret declaration.
  </Card>

  <Card title="Project context" icon="file-text">
    A concise, committed document that explains customer-specific domain terms
    and data conventions to the agent for this project.
  </Card>

  <Card title="Delegated workloads" icon="container">
    An optional sandbox configuration advertises `code.execute` with hazard
    `unknown`. It is disabled unless explicitly configured.
  </Card>
</Columns>

The runner advertises capability metadata and optional project context when it
connects. It does not advertise secret values.

## Configuration and runtime bindings

Structural configuration is committed as `polycore.json`. Deployment supplies
identity, enrollment credentials, datastore coordinates, and secrets.

<CodeGroup>
  ```json polycore.json theme={"system"}
  {
    "integrations": {
      "postgres": {},
      "firebase": { "firestore": {} }
    },
    "context": { "file": "./context/product.md" }
  }
  ```

  ```text Runtime bindings theme={"system"}
  # Path to polycore.json, unless the command receives --config
  POLYCORE_RUNNER_CONFIG

  POLYCORE_PROJECT_SLUG
  POLYCORE_CONTROL_PLANE_URL
  POLYCORE_RUNNER_ID
  POLYCORE_JOIN_TOKEN
  POLYCORE_SIGNING_SECRET

  # When the relevant query family is enabled
  POLYCORE_POSTGRES_URL
  FIRESTORE_PROJECT_ID
  GOOGLE_CLOUD_PROJECT
  # GCLOUD_PROJECT is also accepted as a Firestore project fallback
  ```
</CodeGroup>

This split is deliberate:

* **Code defines authority.** Query families, actions, action schemas, and
  project context are reviewed in your repository.
* **Deployment defines identity.** The process is bound to one project and one
  enrolled environment.
* **Your secret system provides values.** Credentials arrive through workload
  identity or deployment-time secret injection.

See [Runner configuration](/authoring/runner-config) for the supported fields.

## Credential resolution

<Tabs>
  <Tab title="Cloud identity">
    On GCP, Firestore can use Application Default Credentials. The runner
    process receives a service account, and GCP IAM decides which project and
    operations it can access. No service-account key file is required in the
    normal hosted path.
  </Tab>

  <Tab title="Connection strings">
    Postgres reads use `POLYCORE_POSTGRES_URL`, normally injected from your
    secret manager by the deployment layer. The value is not part of
    `polycore.json` and is not advertised to the control plane.
  </Tab>

  <Tab title="Action secrets">
    An action names the secret keys it needs. At invocation time, the runner
    selects those values from its local secret map and passes only that subset
    to the action.
  </Tab>
</Tabs>

<Warning>
  Avoid committing values under the optional `secrets` field. The file format
  supports local evaluation, but production secrets should be supplied by your
  deployment and secret-management system.
</Warning>

## Connection lifecycle

<Steps>
  <Step title="Load and validate">
    The runner parses `polycore.json`, resolves file paths relative to the
    config, discovers actions, and validates the complete capability registry.
  </Step>

  <Step title="Resolve runtime identity">
    Environment variables bind the process to a project, control-plane runner
    registration, and enabled datastore query families.
  </Step>

  <Step title="Dial the control plane">
    The runner opens a persistent outbound WebSocket and enrolls with its
    provisioned identity.
  </Step>

  <Step title="Advertise the catalog">
    The runner sends capability names, kinds, hazards, input and output schemas,
    and optional project context. Secret values are excluded.
  </Step>

  <Step title="Serve signed dispatches">
    For each valid dispatch, the runner checks the signature and schema, runs
    the capability, and returns either a structured result or a structured
    failure.
  </Step>

  <Step title="Reconnect">
    Application-level heartbeats detect stale sessions. The runner reconnects
    with exponential backoff and advertises its current catalog again.
  </Step>
</Steps>

## Registration lifecycle

The dashboard groups registrations with the same slug as one runner namespace,
but each environment still has a separate runner ID and credential set. Use the
same slug for every deployment of one logical runner. Capability calls always
use `<runner>/<capability>` and never infer a default runner. The Integrations
inspector shows every project environment as `Online`, `Offline`, `Never
connected`, or `Not configured`.

* **Add an environment** issues a new one-time credential set for that exact
  environment. Store it in that environment's secret manager before closing
  the result.
* **Reissue credentials** revokes an offline or never-connected registration
  before issuing its replacement. The old credentials stop working.
* **Remove from an environment** revokes only that registration and closes its
  active connection. Other environments remain independent.
* **Remove from all environments** is a separate danger-zone operation that
  atomically revokes the namespace's active registrations.

Revocation preserves runner identity in historical approvals and capability
invocations. A revoked registration disappears from the active directory, and
the same slug can later be enrolled again for that environment.

<Warning>
  Runner credentials are shown once and cannot be recovered from Polycore. If
  they are lost, revoke and reissue the affected environment registration.
</Warning>

## Hosting requirements

The runner needs a Linux environment suitable for a persistent worker:

* Node.js 22 or the Polycore runner container.
* Outbound TLS access to the Polycore control plane.
* Network access to the specific downstream systems it serves.
* A workload identity or secret-manager path for scoped credentials.
* A process supervisor or container restart policy.
* For managed rollouts, a restricted health probe that is ready only while the
  runner has an active control-plane session.

For GCP integrations, Polycore maintains a Container-Optimized OS managed
instance group preset in
[`polycore/runner-gcp`](https://github.com/polycore/runner-gcp). Its health
endpoint accepts no dispatches and is reachable only by Google Cloud health
probes. During early access, we adapt and deploy it with your team.

<Tip>
  Treat runner availability as part of the target environment. If the production
  runner is offline, Polycore should report the operation as unavailable, not
  silently reroute it to staging or another credential set.
</Tip>
