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

# Air-Gapped Mode

> Enterprise-only runner mode for regulated and on-prem deployments. Runner connects only to the Roboticks platform — no GitHub, no public PyPI required.

# Air-gapped mode

Air-gapped mode is the **Enterprise tier** posture for environments where the runner cannot — or must not — reach the public internet. Defence, medical-devices, automotive functional-safety teams use it to satisfy network-segmentation requirements.

<Info>
  Air-gapped pools are available only on the **Enterprise** tier. The platform itself can be SaaS, single-tenant SaaS, or fully on-prem. The runner side of the contract is identical across all three.
</Info>

## What changes

In air-gapped mode the runner only ever opens connections to:

* `https://<your-roboticks-platform>` (could be `api.roboticks.io`, your single-tenant subdomain, or an on-prem `https://roboticks.internal`)
* The S3-compatible object store presigned-URLs returned by the platform

It does **not** clone from GitHub. It does **not** `pip install` from public PyPI. It does **not** pull base images from Docker Hub at job time.

```mermaid theme={null}
%%{init: {"theme": "neutral", "themeVariables": {"primaryColor": "#4040ff"}} }%%
flowchart LR
    Dev["Developer<br/>(rbtk CLI)"] -->|rbtk test run --push| Plat["Roboticks Platform<br/>(SaaS or on-prem)"]
    Plat -->|Job + payload tarball| Runner["roboticks-runner v2<br/>(air-gapped)"]
    Runner -->|MCAP + JUnit upload| ObjStore["On-prem S3-compatible<br/>(MinIO / Pure / EMC)"]
    Runner -->|heartbeat only| Plat
    Runner -.X.-> GH["GitHub.com"]
    Runner -.X.-> PyPI["pypi.org"]
    Runner -.X.-> DH["docker.io"]
```

## Set it up

### Step 1 — declare an air-gapped pool

```bash theme={null}
rbtk pool create \
  --project regulated-arm \
  --name onprem-airgapped \
  --type self-hosted \
  --airgapped
```

The `--airgapped` flag annotates the pool. Jobs routed to it must have `airgapped: true` on the project — the router refuses cross-mode dispatch.

### Step 2 — flip the project flag

```bash theme={null}
rbtk project update --slug regulated-arm --set airgapped=true
```

Once set, **no job in this project is ever dispatched to a hosted pool**, even if a self-hosted pool is unavailable. Jobs queue until a self-hosted runner picks them up.

### Step 3 — install the runner on the isolated network

Mirror the Cosign-signed binary into your internal artifact store, then install on the host:

```bash theme={null}
# On the air-gapped host (binary already side-loaded to /tmp)
sudo install -m 0755 /tmp/rbtk-runner-linux-amd64 /usr/local/bin/rbtk-runner

# Configure to talk to your on-prem platform
export ROBOTICKS_API_ENDPOINT=https://roboticks.internal
rbtk-runner register \
  --project regulated-arm \
  --pool onprem-airgapped \
  --token rbtk_pool_reg_xx... \
  --name airgap-runner-01
```

### Step 4 — set `network.airgapped: true` in runner.yaml

The runner enforces network-egress restrictions when this flag is set:

```yaml theme={null}
network:
  airgapped: true
  allowlist:
    - roboticks.internal
    - s3.internal
```

Any outbound connection to a non-allowlisted host fails fast with an audit-log row.

## Pushing tests without GitHub

In air-gapped mode the runner cannot `git clone`. Instead the **CLI uploads the test payload directly**:

```bash theme={null}
# From a workstation with network access to BOTH GitHub and the Roboticks platform
rbtk test run \
  --project regulated-arm \
  --pool onprem-airgapped \
  --push ./           # tars the working dir and uploads to the platform
```

The platform stores the payload as an immutable blob, dispatches the job to the air-gapped pool, and the runner pulls the blob (not the git repo) before execution.

For commit-traceable runs, attach the commit SHA explicitly:

```bash theme={null}
rbtk test run --push ./ --git-sha $(git rev-parse HEAD) --git-ref main
```

The SHA is recorded in the evidence pack so auditors can reconstruct the source state.

## Firewall rules

Open one outbound rule on the runner host. Everything else can be denied.

| Direction | Destination                                  | Port | Purpose                       |
| --------- | -------------------------------------------- | ---- | ----------------------------- |
| Outbound  | `roboticks.internal` (or your platform host) | 443  | Heartbeat, poll, MCAP presign |
| Outbound  | `s3.internal` (or your object store)         | 443  | MCAP and JUnit uploads        |

That is the entire firewall surface.

## On-prem `roboticks` SDK mirror (optional)

If your tests `pip install roboticks` at job time and the runner cannot reach pypi.org, host an internal mirror:

```bash theme={null}
# On the platform side
rbtk admin sdk mirror sync --to s3://roboticks-pypi-mirror/

# In runner.yaml
resources:
  pip_index_url: https://pypi.internal/simple/
```

Or — preferred — **bake the SDK into your test image** so the runner never needs PyPI:

```dockerfile theme={null}
FROM osrf/ros:humble-desktop
RUN pip install roboticks==1.4.2
```

Push the baked image to your internal registry and reference it from the test config:

```yaml theme={null}
test:
  image: registry.internal/roboticks/test-base:1.4.2
```

## Verification

After setup, prove isolation:

```bash theme={null}
# On the runner host
rbtk-runner doctor --airgapped

✓ Network: only roboticks.internal and s3.internal reachable
✗ Network: external host github.com is reachable — air-gap violation
```

A red line is a finding, not a runner error — but you should fix the firewall before relying on the posture for audit.

## Limits and trade-offs

|                           | Air-gapped self-hosted                    | Standard self-hosted | Hosted   |
| ------------------------- | ----------------------------------------- | -------------------- | -------- |
| Internet egress           | None                                      | Optional             | Required |
| GitHub App can drive jobs | No (CLI push only)                        | Yes                  | Yes      |
| LLM triage                | Only if platform on-prem with bundled LLM | Yes                  | Yes      |
| Tier required             | Enterprise                                | Any paid             | Any paid |
| Setup effort              | High                                      | Low                  | Zero     |

## Next steps

<CardGroup cols={2}>
  <Card title="Pool management" icon="layer-group" href="/runners/pool-management">
    Tokens, draining, audit log.
  </Card>

  <Card title="Service install" icon="rotate" href="/runners/service">
    systemd / launchd / Windows service.
  </Card>
</CardGroup>
