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

# Bridge mode

> Run the launchkit-worker daemon next to your intranet sandbox plane

In bridge mode your sandbox plane stays on your intranet, unreachable from outside. You run the **`launchkit-worker`** daemon on a host next to the plane; it makes **outbound HTTPS calls only**, long-polls LaunchKit for sandbox work, executes each operation against your **local** plane, and posts results back.

```
Your network                                LaunchKit cloud
──────────────────────────────              ────────────────────
launchkit-worker daemon        ── HTTPS ──► work queue (poll)
  │  executes ops locally      ◄─ work ───  per-task operations
  ▼                            ── results ► task continues
E2B-compatible plane
(CubeSandbox / E2B)
```

Nothing inbound is ever opened, and **LaunchKit never holds your plane's credentials**: the worker reads them from its own environment.

## Requirements

* Python **3.11+**.
* Outbound HTTPS to the LaunchKit cloud API.
* Network access to the local sandbox plane.
* Persistent disk for the worker's result journal (see [Recovery](#the-result-journal-and-recovery)).

## Install

`launchkit-worker` is published on PyPI; your LaunchKit contact provides your **runtime id** and **worker key** separately. For a system service, install the pinned wheel into a root-managed virtual environment:

```bash theme={null}
sudo python3 -m venv /opt/launchkit-worker
sudo /opt/launchkit-worker/bin/pip install launchkit-worker==0.5.0
```

Pin the version so upgrades stay a deliberate operator action: the worker is **operator-pinned** and never auto-updates. `pipx` or `uv tool` are fine for an interactive user, but their binaries normally live under that user's home directory and are intentionally inaccessible to the hardened systemd unit below.

Releases are published from LaunchKit CI via [PyPI Trusted Publishing](https://docs.pypi.org/trusted-publishers/) and carry Sigstore [PEP 740 attestations](https://docs.pypi.org/attestations/) for provenance verification. The package is source-available under a restricted-use license (see `LICENSE`): you may run it to connect to LaunchKit, but not redistribute or modify it.

## Configuration

LaunchKit settings, read by the worker:

| Variable                          | Required | Default                                              | Meaning                                                                                                                 |
| --------------------------------- | -------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `LAUNCHKIT_API_URL`               | yes      |                                                      | LaunchKit cloud API base URL, `https://api.trylaunchkit.ai` (https required; plain http only for localhost development) |
| `LAUNCHKIT_RUNTIME_ID`            | yes      |                                                      | The runtime this worker serves                                                                                          |
| `LAUNCHKIT_RUNTIME_KEY`           | yes      |                                                      | The runtime worker key (`lkw01_...`)                                                                                    |
| `LAUNCHKIT_WORKER_CONCURRENCY`    | no       | `4`                                                  | Concurrent work slots                                                                                                   |
| `LAUNCHKIT_WORKER_IDLE_TIMEOUT_S` | no       | `330`                                                | Idle seconds before a claim is released                                                                                 |
| `LAUNCHKIT_WORKER_STATE_DB`       | no       | `~/.local/state/launchkit-worker/op-results.sqlite3` | Durable result-journal path; mount its directory in containers                                                          |
| `LAUNCHKIT_WORKER_LOG_LEVEL`      | no       | `INFO`                                               | Python log level                                                                                                        |

Sandbox-plane settings, read by the `e2b` SDK itself (the worker deliberately does not touch them, which is what keeps it portable across a real E2B cloud and a CubeSandbox plane):

| Variable               | Meaning                                                                                               |
| ---------------------- | ----------------------------------------------------------------------------------------------------- |
| `E2B_API_KEY`          | The local plane's API key                                                                             |
| `E2B_DOMAIN`           | The plane host, for example a CubeSandbox intranet domain                                             |
| `E2B_API_URL`          | Explicit control-plane URL (overrides `E2B_DOMAIN`)                                                   |
| `E2B_VALIDATE_API_KEY` | Set `false` for planes whose keys are not E2B-format; see [Compatibility](/en/runtimes/compatibility) |
| `SSL_CERT_FILE`        | Custom CA bundle for a private-CA intranet plane                                                      |

## About the worker key

The `lkw01_...` key is minted by LaunchKit for exactly one runtime and shown once; you receive it from your LaunchKit contact. It authenticates only this runtime's work queue (a key for one runtime can never poll another's). Store it in a root-owned file with mode `600`. Rotation is a re-mint: you receive a new key and swap it into the environment file.

## Run under systemd

Save this unit as `/etc/systemd/system/launchkit-worker.service`:

```ini theme={null}
[Unit]
Description=LaunchKit bridge worker (sandbox daemon)
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
DynamicUser=yes
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictNamespaces=yes
RestrictSUIDSGID=yes
LockPersonality=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
CapabilityBoundingSet=
SystemCallArchitectures=native
EnvironmentFile=/etc/launchkit-worker.env
StateDirectory=launchkit-worker
StateDirectoryMode=0700
Environment=LAUNCHKIT_WORKER_STATE_DB=/var/lib/launchkit-worker/op-results.sqlite3
ExecStart=/opt/launchkit-worker/bin/launchkit-worker run
Restart=always
RestartPreventExitStatus=2
RestartSec=5
KillSignal=SIGTERM
TimeoutStopSec=60

[Install]
WantedBy=multi-user.target
```

Then create the environment file and start the daemon:

```bash theme={null}
sudo touch /etc/launchkit-worker.env && sudo chmod 600 /etc/launchkit-worker.env
sudo "$EDITOR" /etc/launchkit-worker.env        # fill in the variables above
sudo systemctl daemon-reload
sudo systemctl enable --now launchkit-worker
sudo journalctl -u launchkit-worker -f
```

The unit uses a persistent state directory for the journal and restarts the daemon automatically, except after a version rejection (see below). On `SIGTERM` the worker drains gracefully: it stops claiming, finishes in-flight operations, and reports its active claims before exiting.

## Pre-flight checks: doctor

Run before enabling the daemon, and again after any plane upgrade:

```bash theme={null}
launchkit-worker doctor
```

Each check prints `OK`, `WARN`, or `FAIL` (non-zero exit on any `FAIL`):

1. Cloud reachability, key validity, and key-to-runtime binding.
2. Clock skew against the server (warn above 30s, fail above 300s).
3. Local sandbox-plane reachability.
4. Pause capability (a reminder unless `--probe` is used).
5. Snapshot capability (CubeSandbox >= v0.5.1; a reminder unless `--probe` is used).
6. Free disk space.

```bash theme={null}
launchkit-worker doctor --probe
```

`--probe` additionally runs the **live** sandbox checks: it creates a real sandbox on your plane, verifies that a paused sandbox's disk survives and resumes correctly, then runs the snapshot round-trip (snapshot the sandbox, boot a fork **from** the snapshot, verify the disk carried over) — destroying everything it created. These are the functional gates for pause and snapshot support, and the snapshot check is the **same** round-trip the LaunchKit cloud probe runs through this worker, so the doctor and the cloud can never disagree. Run it once at onboarding and after every plane upgrade. See [Compatibility](/en/runtimes/compatibility) for why this matters on CubeSandbox.

## Version pinning and HTTP 426

The cloud enforces a deployment-wide minimum worker version. A worker below the floor is turned away with **HTTP 426** and exits with a non-zero status that systemd deliberately does **not** restart (no crash-restart loops on a permanent condition). Upgrade the pinned package (`sudo /opt/launchkit-worker/bin/pip install --upgrade launchkit-worker==0.5.0`) and restart the daemon (`sudo systemctl restart launchkit-worker`). Worker **0.5.0** adds the snapshot operations (snapshot, delete-snapshot, snapshot probe, template delete), so the deployment-wide floor is 0.5.0.

## The result journal and recovery

Every operation's outcome is committed to a local SQLite journal **before** being reported. This makes results crash-safe:

* If the daemon or host restarts, a re-delivered operation **replays the journaled result** instead of executing twice.
* If an operation is re-delivered to a **different host** (for example after failover to a second worker machine) and that host has no journaled outcome, the worker reports the execution as **indeterminate** rather than repeating a command or provisioning a duplicate sandbox.

<Warning>
  Keep the journal on persistent storage (the `LAUNCHKIT_WORKER_STATE_DB` path). If you run the
  worker in a container, mount the journal directory as a volume; a journal lost with its container
  turns clean same-host recovery into indeterminate results.
</Warning>

## Tuning

* `LAUNCHKIT_WORKER_CONCURRENCY`: raise it if your plane has headroom and tasks queue up; each slot handles one sandbox's operations at a time.
* `LAUNCHKIT_WORKER_IDLE_TIMEOUT_S`: how long an idle claim is held before its slot is released. The default (330s) is chosen to sit above the cloud's per-operation budget; lower it only if slot starvation is an issue.
