# 7. The Runner

A **Runner** runs a folder's **scheduled flows around the clock**, on a machine the folder owner controls (a small server, a NAS, a Raspberry Pi), even when nobody has LeavenIT open.

## 7.1 How it works

- The Runner is a small program, delivered as a **Docker image**: `ghcr.io/alexandrecrbpro-stack/leavenit-runner:1`.
- It belongs to **one data area** (a shared folder, My Workshop, or a folder with separate data) and only acts there.
- It needs that data area to use **your own storage** (chapter 6). It holds **no storage key**: LeavenIT signs each read and write, like for a browser.
- It only makes **outbound** connections: every 5 seconds it asks LeavenIT whether a scheduled flow is due. **No port to open**, no domain name, no certificate.
- It runs **the same SQL engine as the browser** (SQLite 3.49 with the same functions), so a block gives the same result in both. Each block runs in its own process, stopped at its time limit.
- It acts on behalf of the folder owner, within that folder, with the same rules as the website.
- Limits per block on a Runner: 30 minutes, about 3 GB of memory for the engine. Very large tables (tens of millions of rows) are not supported yet.

## 7.2 What you need

- A machine that stays on, with **Docker**, 64-bit (x86-64 or ARM64), 1 GB of memory or more.
- The data area already on **your own storage** (chapter 6).
- To be the **owner** of the folder.

## 7.3 Adding a Runner in LeavenIT

1. **Workshop** → the folder → **⋯** next to its name → **Runners**. (For a folder with separate data: its **⋯** → **Runners**.)
2. **Add a Runner** → name it after the machine ("Hetzner", "Home NAS") → **Get the command**.
3. LeavenIT shows a **pairing code** (`LVN-XXXXX-XXXXX`, single use, valid 30 minutes) and the full command, on one line:

```
docker run -d --name leavenit-runner --restart unless-stopped --read-only --tmpfs /tmp -v leavenit-runner:/data -e LEAVENIT_URL=<LeavenIT address> -e LEAVENIT_ANON_KEY=<LeavenIT public key> -e LEAVENIT_PAIRING_CODE=LVN-XXXXX-XXXXX ghcr.io/alexandrecrbpro-stack/leavenit-runner:1
```

   The address and the public key are LeavenIT's public values, already filled in. The pairing code is personal: don't share it.
4. **Copy the command**, run it on the machine (7.4). The panel shows **"✓ <name> is online."** within seconds.
5. Open a flow of this folder → **Schedule** → under **On**, choose **"<name> · Runner, always on"**, choose when, **Save**.

If the code expired, the panel offers **New code**.

## 7.4 Machine guides

### A small cloud server: Hetzner Cloud (tested end to end)

1. **An SSH key** on your computer (PowerShell on Windows, Terminal on Mac/Linux):
   ```
   ssh-keygen -t ed25519 -C "leavenit-runner"
   ```
   Press Enter at each question. Show the public key: `Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub` (Windows) or `cat ~/.ssh/id_ed25519.pub` (Mac/Linux). Never share the file without `.pub`.
2. **console.hetzner.cloud** → **New project** → **Add Server**:
   - **Location**: Falkenstein or Nuremberg.
   - **Image**: **Ubuntu 24.04**.
   - **Type**: Shared vCPU, the smallest (**CAX11** ARM64, or **CX22** x86).
   - **SSH keys**: **Add SSH key**, paste the `ssh-ed25519 …` line.
   - **Firewalls**: **Create Firewall** keeping only **inbound SSH (port 22)**.
   - **Create & Buy now**. Note the **IPv4 address**.
3. **Connect**: `ssh root@<IPv4 address>` (answer `yes` the first time).
4. **Install Docker**: `curl -fsSL https://get.docker.com | sh`
5. **Paste the command** from LeavenIT (7.3).
6. **Check**: `docker logs leavenit-runner`

Scaleway (Instances) and OVHcloud (VPS or Public Cloud) work the same way: Ubuntu 24.04, your SSH key, SSH only in the firewall, then steps 3 to 6.

### A Synology NAS

1. **Package Center** → install **Container Manager** (only on models that support it).
2. **Control Panel** → **Terminal & SNMP** → enable **SSH**.
3. From a computer: `ssh <your user>@<NAS address>`.
4. Paste the LeavenIT command with `sudo ` in front, then enter your password.
5. Check: `sudo docker logs leavenit-runner`.

### A Raspberry Pi

A Raspberry Pi 4 or 5 with a **64-bit** system (Raspberry Pi OS 64-bit). Install Docker with `curl -fsSL https://get.docker.com | sh`, then paste the command with `sudo ` in front.

### A computer with Docker Desktop (for a test)

Install **Docker Desktop**, start it, paste the command in a terminal (PowerShell on Windows). The Runner stops when the computer sleeps or shuts down: fine for a test, not for real use.

## 7.5 What the logs say

`docker logs leavenit-runner` (or `docker logs --tail 50 leavenit-runner`):

| Line | Meaning |
|---|---|
| `LeavenIT Runner 1.0.0: engine self-test OK.` | the engine starts, computes and refuses to leave its sandbox |
| `Paired as "<name>".` | the pairing code was accepted (first start only) |
| `Runner "<name>" ready. Checking every 5 s.` | waiting for work |
| `<flow>: run for <time>` then `  <block>: v<n>, <rows> rows` | a scheduled flow ran; each block published a new version of its table |
| `  <block>: failed (<code>) <message>` | a block failed; the flow run is recorded as failed in LeavenIT with this message |
| `<flow>: paused (<reason>)` | the schedule is paused (for example the flow changed since it was scheduled): review it in LeavenIT |
| `Problem: <message>` | a temporary problem (network…); it tries again at the next check |
| `This Runner was removed in LeavenIT. Stopping. Pair it again with a new code to restart it.` | the Runner was removed in LeavenIT: it stopped and forgot its token |
| `This Runner is not paired yet: start it once with LEAVENIT_PAIRING_CODE.` | started without a code and without a saved token: get a new command in LeavenIT |
| `LeavenIT expects Runner protocol <n>; this Runner speaks <m>. Update the image.` | the image is too old: update it (7.6) |
| `Engine self-test failed: …` | the machine can't run the engine: check it's 64-bit with enough memory |

## 7.6 Updating, stopping, removing

- **Automatic updates** (recommended): also run Watchtower, which updates the Runner every night at 4:00:
  ```
  docker run -d --name watchtower --restart unless-stopped -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower --schedule "0 0 4 * * *" leavenit-runner
  ```
- **Update by hand**: `docker pull ghcr.io/alexandrecrbpro-stack/leavenit-runner:1`, then `docker rm -f leavenit-runner` and run the same command again **without** `-e LEAVENIT_PAIRING_CODE=…` (the token is kept in the `leavenit-runner` volume).
- **Pin a version**: replace `:1` by an exact version (`:1.0.0`). Go back to the previous one by changing this tag.
- **Stop for a while**: `docker stop leavenit-runner` (`docker start leavenit-runner` to resume). After one hour offline with active schedules, the owner gets a notification: "Your Runner "<name>" has been offline for over an hour. Its scheduled flows are waiting."
- **Remove for good**: in LeavenIT, **Runners** → **Remove** → confirm. The Runner stops by itself at its next check and its schedules are paused. Then on the machine: `docker rm -f leavenit-runner` and `docker volume rm leavenit-runner`. Delete the cloud server if you rented one, so billing stops.

## 7.7 Security

- The pairing code works once, for 30 minutes. The token the Runner receives is stored only on its machine (in the `leavenit-runner` volume); LeavenIT keeps only a fingerprint of it.
- The token only gives access to **this folder**, only to run its scheduled flows, and is revoked by **Remove**.
- The Runner holds no storage key and no account password.
- Keep the machine updated (`apt update && apt upgrade -y` on Ubuntu), keep only SSH open, and protect your SSH key.

## 7.8 Troubleshooting

| What you see | Cause | Fix |
|---|---|---|
| "A Runner works with your own storage…" in the Runners panel | the folder's tables are in LeavenIT's storage | connect your own storage first (chapter 6), then **Open Storage** |
| The panel keeps "Waiting for the Runner…" | the command didn't run, or the code expired | check `docker logs leavenit-runner`; if needed **New code** and run the new command |
| "This pairing code is not valid. Create a new one in LeavenIT." | code already used, expired, or mistyped | **Add a Runner** again |
| Windows PowerShell rejects the command | the command was cut over several lines | copy it again from LeavenIT: it is on one line |
| `docker: command not found` | Docker isn't installed | install it (7.4) |
| `permission denied … docker.sock` | your user can't use Docker | put `sudo ` in front of the command |
| "This Runner belongs to another folder." when scheduling | a Runner only serves flows of its own folder | add a Runner to this folder |
| A flow fails with "The tables of this folder are no longer in your own storage." | the folder moved back to LeavenIT's storage | move it to your storage again, or schedule the flow on "This computer" |
| The Runner is offline after the server rebooted | Docker didn't start | `systemctl enable --now docker`; the Runner restarts by itself (`--restart unless-stopped`) |
