# Local Grill Monitoring Setup

<p class="callout info"><strong>System Overview:</strong> The <strong>Local Grill Monitoring Setup</strong> provides a 100% self-hosted, cloud-free telemetry and historical graphing pipeline for the <strong>FireBoard 2 (<code>FBX2-GJ8RC3JRD</code>)</strong> 6-probe grill thermometer. The FireBoard 2 itself has <strong>no IP address and no Wi-Fi connection</strong>. Instead, an internal <strong>Seeed Studio XIAO ESP32-C3</strong> bridge (hostname <code>fireboardctl.drheck.dev</code>, static DHCP IP <code>10.0.0.71</code>, Wi-Fi MAC <code>80:f1:b2:51:49:20</code>), switched on and off in hardware by an <strong>IRLB8721PBF</strong> N-Channel MOSFET driven directly from the FireBoard's <code>[Pin 35: TXD0]</code> pad, reads all probe channels over Bluetooth Low Energy GATT (<code>94:54:c5:f7:1c:fa</code>) and posts live updates over Wi-Fi to <a href="https://grill.drheck.dev">https://grill.drheck.dev</a> (<code>http://10.0.0.3:8088/api/ingest</code>).</p>

## 1. Hardware Wiring & Flashing Guide (`Seeed Studio XIAO ESP32-C3` + `IRLB8721PBF` MOSFET)

### 1.1 Parts Used
* **Microcontroller (`fireboardctl.drheck.dev`)**: **Seeed Studio XIAO ESP32-C3** ($21\text{ mm} \times 17.5\text{ mm}$, Wi-Fi STA MAC `80:f1:b2:51:49:20`, static DHCP IP `10.0.0.71`, built-in Li-Ion battery pads on underside + external `U.FL` antenna socket).
* **External Antenna**: Included **Seeed `U.FL` (IPEX) 2.4 GHz flexible dipole sticker antenna** (snap onto the XIAO's `ANT` socket before powering on; adhere inside the plastic FireBoard shell away from the metal Li-Ion pouch cell).
* **Hardware Power Switch MOSFET**: **`IRLB8721PBF`** TO-220 Logic-Level N-Channel MOSFET ($V_{\text{GS(th)}} = 1.35\text{ V–}2.35\text{ V}$, $R_{\text{DS(on)}} \approx 0.013\text{ }\Omega$ at $V_{\text{GS}} = 3.3\text{ V}$).

### 1.2 Step-by-Step Pin Connections (4 Wires Total)

<p class="callout warning"><strong>MOSFET Pin Orientation (<code>IRLB8721PBF</code>):</strong> Hold the MOSFET with the <strong>black printed label facing you</strong> and the <strong>metal heatsink tab facing away</strong> (legs pointing down). From left to right, the 3 legs are:<br>
• <strong>Pin 1 (Left Leg):</strong> <code>Gate (G)</code> — Trigger input ($3.3\text{ V}$ ON / $0.0\text{ V}$ OFF)<br>
• <strong>Pin 2 (Middle Leg):</strong> <code>Drain (D)</code> — Switched ground going to XIAO <code>BAT-</code><br>
• <strong>Pin 3 (Right Leg):</strong> <code>Source (S)</code> — True ground going to FireBoard Battery <code>-</code> (<code>GND</code>)</p>

1. **Wire 1 (`BAT+` Power)**: **FireBoard Battery `+` (Red)** $\longrightarrow$ **XIAO underside `BAT+` pad**.
2. **Wire 2 (`Source` Ground)**: **FireBoard Battery `-` (Black / `GND`)** $\longrightarrow$ **MOSFET Pin 3 (Right Leg, `Source`)**.
3. **Wire 3 (`Drain` Switched Ground)**: **MOSFET Pin 2 (Middle Leg, `Drain`)** $\longrightarrow$ **XIAO underside `BAT-` pad**.
4. **Wire 4 (`Gate` 3.3V Trigger)**: **FireBoard 6-pin header top-left pad (`[Pin 35: TXD0]`)** $\longrightarrow$ **MOSFET Pin 1 (Left Leg, `Gate`)**.
   * When the FireBoard 2 is turned **ON**, `[Pin 35: TXD0]` outputs **$3.30\text{ V}$**, turning the `IRLB8721PBF` MOSFET **ON** and connecting the XIAO's `BAT-` pad to ground so the XIAO boots immediately.
   * When the FireBoard 2 is turned **OFF**, `[Pin 35: TXD0]` drops to **$0.00\text{ V}$**, turning the `IRLB8721PBF` MOSFET **OFF** and completely disconnecting the XIAO from the battery ($0.000\text{ mA}$ standby drain).

### 1.3 Complete 4-Wire Schematic

```text
  FIREBOARD 2 MAIN PCB / BATTERY                   IRLB8721PBF MOSFET              SEEED XIAO ESP32-C3
  ==============================                   (Label Facing You)              ===================

  1S Li-Ion Battery (+) [3.7V-4.2V] ---------------------------------------------> Underside [BAT+] Pad

  6-Pin Header [35 TXD0] (3.3V ON / 0V OFF) ----> Pin 1 [Gate (G) - Left]

  1S Li-Ion Battery (-) / [GND] ----------------> Pin 3 [Source (S) - Right]

                                                  Pin 2 [Drain (D) - Middle] ----> Underside [BAT-] Pad
```

<p class="callout info"><strong>Why Power Connects to the Underside <code>BAT+</code> / <code>BAT-</code> Pads (Not the Top <code>5V</code> Pin):</strong> On the Seeed Studio XIAO ESP32-C3, the top <code>5V</code> (<code>VBUS</code>) pin passes through a protection Schottky diode ($\Delta V_{\text{diode}} \approx 0.34\text{ V}$) before feeding the <code>XC6206P332MR</code> $3.3\text{ V}$ LDO ($\Delta V_{\text{drop}} \approx 0.25\text{ V}$), requiring at least $3.85\text{ V}$ on <code>5V</code> before the $3.3\text{ V}$ rail sags. Soldering to the <strong>underside <code>BAT+</code> pad</strong> bypasses the Schottky diode and feeds the LDO directly ($V_{\text{min}} = 3.30\text{ V} + 0.25\text{ V} = 3.55\text{ V}$), running cleanly across the entire $3.5\text{ V–}4.2\text{ V}$ discharge curve of the 1S Li-Ion cell.</p>

### 1.4 Building, Flashing & Bench Testing (`PlatformIO`)
1. Snap the `U.FL` antenna onto the XIAO ESP32-C3 before powering on.
2. Copy `include/secrets.example.h` to `include/secrets.h` in `~/git/grill-sync` and set your 2.4 GHz Wi-Fi credentials:
   ```cpp
   #define WIFI_SSID "Your_2.4GHz_WiFi_SSID"
   #define WIFI_PASSWORD "Your_WiFi_Password"
   ```
3. Compile, flash over USB-C, and open the live serial monitor (`115200` baud):
   ```shell
   $ pio run -t upload -t monitor
   ```
4. Turn on the FireBoard 2 nearby with a probe plugged into Channel 1. Within 5 seconds, the serial console outputs:
   ```text
   === FireBoard 2 BLE -> Wi-Fi Sync (fireboardctl.drheck.dev) ===
   [WiFi] Connected, IP: 10.0.0.71, MAC: 80:F1:B2:51:49:20
   [BLE] Found FireBoard (94:54:c5:f7:1c:fa, RSSI -42 dBm)
   [BLE] Connected to FireBoard 2
   [BLE] Subscribed and sent 0x01 start command
   [BLE] Ch 1: 72.5 F
   [HTTP] POST http://10.0.0.3:8088/api/ingest -> 200
   ```

---

## 2. FireBoard 2 Hardware Forensics & PCB Pinout Reference

### 2.1 Internal ESP32-WROVER-E Photograph & Pin Labels

<p align="center">
  <a href="https://wiki.drheck.dev/uploads/images/gallery/2026-09/esp32.jpg" target="_blank">
    <img src="https://wiki.drheck.dev/uploads/images/gallery/2026-09/scaled-1680-/esp32.jpg" alt="FireBoard 2 ESP32-WROVER-E Pinout" width="75%" style="border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); display: block; margin: 0 auto;">
  </a>
  <em style="display: block; text-align: center; margin-top: 6px; font-size: 0.9em; color: #666;">Figure 1: FireBoard 2 internal ESP32-WROVER-E (MGN8R8) module and unpopulated 6-pin 1.27mm UART bootloader header with verified pin numbers.</em>
</p>

### 2.2 Unpopulated 6-Pin `1.27mm` (`0.050"`) Debug Header Pinout

Located immediately below the `ESP32-WROVER-E` module on the main PCB. All pads were verified by continuity against the castellated `ESP32-WROVER-E` pins:

```text
      TOP ROW (Closer to ESP32 bottom edge)
   +----------------+----------------+----------------+
   |  [Pin 35] *    |  [Pin 25]      |  [GND]         |
   |  UART0 TXD0    |  GPIO0 (BOOT)  |  Ground        |
   |  (Right #23)   |  (Right #14)   |  (Shield/1/15) |
   +----------------+----------------+----------------+
   |  [Pin 34]      |  [Pin 3]       |  [Pin 24]      |
   |  UART0 RXD0    |  EN / RST      |  GPIO2 (Strap) |
   |  (Right #22)   |  (Left #3)     |  (Right #13)   |
   +----------------+----------------+----------------+
      BOTTOM ROW (Closer to PCB edge)

   * [Pin 35: TXD0] (top-left pad) outputs 3.30V when FireBoard is ON
     and 0.00V when FireBoard is OFF — wire this pad to MOSFET Pin 1 (Gate).
```

| Header Pad | ESP32 Module Pin | Function | Voltage (FireBoard ON) | Voltage (FireBoard OFF) | Notes |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **`[35 TXD0]`** | Pin 35 (`Right #23`) | `GPIO1` / `U0TXD` | **$3.30\text{ V}$** (Idle High) | **$0.00\text{ V}$** | **Used as the $3.3\text{ V}$ ON/OFF trigger for MOSFET Pin 1 (`Gate`)** |
| **`[34 RXD0]`** | Pin 34 (`Right #22`) | `GPIO3` / `U0RXD` | **$3.30\text{ V}$** | **$0.00\text{ V}$** | `UART0` receive input |
| **`[25 IO0]`** | Pin 25 (`Right #14`) | `GPIO0` (Boot Strap) | **$3.30\text{ V}$** | **$0.00\text{ V}$** | Hold at $0\text{ V}$ (`GND`) while pulsing `[3 RST]` to enter UART Download Mode |
| **`[3 RST]`** | Pin 3 (`Left #3`) | `CHIP_PU` (`EN` / `RST`) | **$3.30\text{ V}$** | **$0.00\text{ V}$** | Active-low hardware reset ($0\text{ V}$ resets ESP32) |
| **`[24 IO2]`** | Pin 24 (`Right #13`) | `GPIO2` (Boot Strap) | **$0.00\text{ V}$** | **$0.00\text{ V}$** | Must be low/floating to enter serial bootloader |
| **`[GND]`** | Pins 1, 15, 38, 39 | Common Ground | **$0.00\text{ V}$** | **$0.00\text{ V}$** | Connected to ESP32 RF shield can and battery negative (`-`) |

*Additional Module Reference Pad*: **`Left #2` (`Pin 2: 3V3`)** carries the regulated **$3.30\text{ V}$** rail when the FireBoard is powered on and drops to **$0.00\text{ V}$** when powered off.

### 2.3 Hardware & Network Identity Reference

| Device / Component | Property | Value | Notes |
| :--- | :--- | :--- | :--- |
| **XIAO ESP32-C3 Bridge** | **Hostname (`FQDN`)** | `fireboardctl.drheck.dev` (`fireboardctl`) | Only device on Wi-Fi; resolved in Unbound on `danknasty` |
| **XIAO ESP32-C3 Bridge** | **Static DHCP IPv4** | `10.0.0.71` | Reserved in `/etc/dhcpd.conf` (`~/git/router_config/dhcpd.conf`) on `danknasty` |
| **XIAO ESP32-C3 Bridge** | **Wi-Fi STA MAC** | `80:f1:b2:51:49:20` | Seeed Studio XIAO ESP32-C3 2.4 GHz Wi-Fi interface |
| **FireBoard 2 Unit** | **Device ID / Serial** | `FBX2-GJ8RC3JRD` | FireBoard 2 6-Channel Thermometer (`ESP32-WROVER-E` / `ESP32-D0WD-V3 rev v3.1`) |
| **FireBoard 2 Unit** | **Bluetooth LE MAC (`Base + 2`)** | `94:54:c5:f7:1c:fa` | Active whenever the FireBoard 2 is powered on |
| **FireBoard 2 Unit** | **Wi-Fi STA MAC (`Base`)** | `94:54:c5:f7:1c:f8` *(Unconfigured / No IP)* | FireBoard 2 is not connected to Wi-Fi and holds no IP address |
| **FireBoard 2 Unit** | **Battery Pack** | `1S Li-Ion Pouch Cell` ($3.7\text{ V}$ nominal, $4.20\text{ V}$ full) | Powers both the FireBoard 2 and the `IRLB8721PBF` MOSFET-switched XIAO ESP32-C3 |

### 2.4 Why the Stock FireBoard Firmware Is Bypassed via BLE
1. **Closed Local Network Surface**: Full 65,535-port TCP/UDP `nmap` scans and packet captures confirmed the stock FireBoard 2 firmware listens on zero local ports and requires remote cloud servers when on Wi-Fi. Keeping the FireBoard 2 off Wi-Fi and reading its local BLE GATT server eliminates all external network traffic.
2. **Locked Factory Silicon (`eFuses`)**: Interrogation over the 6-pin UART bootloader header proved all hardware security eFuses on the FireBoard's internal `ESP32-WROVER-E` are permanently burned:
   * **Flash Encryption (`FLASH_CRYPT_CNT = 127` / `0b1111111`)**: All 7 bits burned and write-protected; plaintext flashing renders the chip unbootable.
   * **UART Crypto Disabled (`DISABLE_DL_ENCRYPT = True`, `DISABLE_DL_DECRYPT = True`)**: The ROM bootloader is physically barred from encrypting new images or decrypting flash over UART.
   * **Secure Boot v1 (`ABS_DONE_0 = True`)**: Only images signed with FireBoard's private key (`BLOCK2`, read/write protected) are booted.
   * **Silent UART Console (`MTDO` Strapped Low + `ESP_LOG_NONE`)**: Zero application log bytes are emitted on `UART0` during normal boot.

<details>
  <summary><strong>Click to expand full ESP32-D0WD-V3 Security eFuse Register Dump (espefuse summary)</strong></summary>

```text
FLASH_CRYPT_CNT        (BLOCK0) = 127 R/- (0b1111111)  [Flash encryption permanently enabled; odd bit count = 7; Write-Protected]
FLASH_CRYPT_CONFIG     (BLOCK0) = 15  R/W (0xf)        [Full AES flash encryption tweak configuration]
ABS_DONE_0             (BLOCK0) = True R/W             [Secure Boot V1 permanently enforced on boot]
DISABLE_DL_ENCRYPT     (BLOCK0) = True R/W             [UART bootloader flash encryption disabled]
DISABLE_DL_DECRYPT     (BLOCK0) = True R/W             [UART bootloader flash decryption disabled]
DISABLE_DL_CACHE       (BLOCK0) = True R/W             [UART bootloader MMU flash cache disabled]
JTAG_DISABLE           (BLOCK0) = True R/W             [Hardware JTAG debugging permanently disabled]
CONSOLE_DEBUG_DISABLE  (BLOCK0) = True R/W             [ROM BASIC fallback console disabled]
BLOCK1 (Flash Key)              = ?? ?? ... -/-        [256-bit AES Flash Encryption Key: Read & Write Protected]
BLOCK2 (Secure Boot Key)        = ?? ?? ... -/-        [256-bit Secure Boot Key: Read & Write Protected]
```

</details>

---

## 3. FireBoard 2 BLE GATT Protocol Reference

<p class="callout success"><strong>Zero Cloud Dependency:</strong> The FireBoard 2 BLE GATT server remains active with zero Wi-Fi configuration, allowing the XIAO ESP32-C3 (<code>fireboardctl.drheck.dev</code>) to stream all 6 channels locally.</p>

| GATT Attribute | UUID / Value | Properties | Description |
| :--- | :--- | :--- | :--- |
| **`FIREBOARD_DATA_CHAR_UUID`** | `c2f780ec-45e1-452b-a879-327e3140d1f1` | `Notify` | Streams UTF-8 JSON temperature packets every `3–5s` per active probe channel |
| **`FIREBOARD_CONTROL_CHAR_UUID`** | `c2f780ec-45e1-452b-a879-327e3140d1e8` | `Write` | Writing single byte **`0x01`** starts the notification stream on `d1f1` |
| **`FIREBOARD_START_STREAM_BYTE`** | `0x01` (`b'\x01'`) | `1 Byte` | Must be written after enabling notifications (`CCCD`) on `d1f1` |

### BLE Handshake & Notification Payload Format
1. **Scan & Connect**: Connect to BLE MAC `94:54:c5:f7:1c:fa` (or any device matching prefix `94:54:c5:f7:1c:` / name `FBX`). No pairing PIN or bond is required.
2. **Subscribe**: Enable GATT notifications on `c2f780ec-45e1-452b-a879-327e3140d1f1`.
3. **Start Stream**: Write `0x01` (`1 byte`, with response) to `c2f780ec-45e1-452b-a879-327e3140d1e8`.
4. **Parse JSON Notifications**: The FireBoard immediately begins emitting UTF-8 JSON notifications for each connected thermistor probe (`channel` `1` through `6`):
   ```json
   {
     "channel": 1,
     "temp": 225.4,
     "degreetype": 2,
     "date": "2026-09-28T20:30:00Z"
   }
   ```
   * `channel`: Probe port number (`1..6`).
   * `temp`: Floating-point temperature (`null` or `<= 0` when unplugged).
   * `degreetype`: `2` = Fahrenheit (`°F`), `1` = Celsius (`°C`).

---

## 4. Firmware Repository (`~/git/grill-sync`) & Magic Constants (`include/config.h`)

All hardware, BLE protocol, and network constants are isolated in **`include/config.h`** (with Wi-Fi credentials in gitignored **`include/secrets.h`**).

### Repository Layout
* `platformio.ini` — PlatformIO configuration targeting `seeed_xiao_esp32c3` (`huge_app.csv` 3.1 MB partition table, `NimBLE-Arduino`, `ArduinoJson`).
* `include/config.h` — Central configuration file containing all network URLs, bridge identity (`fireboardctl.drheck.dev`, `10.0.0.71`, `80:f1:b2:51:49:20`), and BLE GATT UUIDs.
* `include/secrets.example.h` / `include/secrets.h` — Local Wi-Fi credentials (`WIFI_SSID`, `WIFI_PASSWORD`) and optional local overrides.
* `src/main.cpp` — Non-blocking BLE GATT client, JSON parser, and HTTP POST transmitter.

### Complete Reference of Constants in `include/config.h`

| Constant | Default Value | Purpose & When to Change |
| :--- | :--- | :--- |
| **`BRIDGE_DHCP_HOSTNAME`** | `"fireboardctl"` | DHCP client hostname announced by the XIAO ESP32-C3. |
| **`BRIDGE_FQDN`** | `"fireboardctl.drheck.dev"` | Local DNS name in Unbound (`10.0.0.71`). |
| **`BRIDGE_WIFI_MAC`** | `"80:f1:b2:51:49:20"` | Wi-Fi STA MAC of the Seeed Studio XIAO ESP32-C3 reserved as `10.0.0.71` in `/etc/dhcpd.conf` on `danknasty`. |
| **`BRIDGE_STATIC_IPV4`** | `"10.0.0.71"` | Static DHCP IPv4 address assigned to `fireboardctl.drheck.dev`. |
| **`INGEST_URL`** | `"http://10.0.0.3:8088/api/ingest"` | Target HTTP POST URL on host `ul` (`10.0.0.3` ports `8088` and `8000`). |
| **`HTTP_POST_INTERVAL_MS`** | `5000UL` (`5s`) | How frequently the XIAO posts temperature snapshots to `ul` when BLE is connected. |
| **`HTTP_HEARTBEAT_INTERVAL_MS`** | `15000UL` (`15s`) | How frequently the XIAO posts status heartbeats when scanning for BLE. |
| **`WIFI_CONNECT_TIMEOUT_MS`** | `10000UL` (`10s`) | Maximum time spent waiting for Wi-Fi association per attempt. |
| **`FIREBOARD_DEVICE_ID`** | `"FBX2-GJ8RC3JRD"` | Device ID reported in JSON payloads to `ul`. |
| **`FIREBOARD_BLE_MAC`** | `"94:54:c5:f7:1c:fa"` | Bluetooth LE MAC of the FireBoard 2 (`Base MAC + 2`). Change if pairing with a replacement FireBoard unit. |
| **`FIREBOARD_MAC_PREFIX`** | `"94:54:c5:f7:1c:"` | OUI/prefix matched during BLE scanning. |
| **`FIREBOARD_LOCK_TO_MAC`** | `0` | Set to `1` to only connect to `FIREBOARD_BLE_MAC`, or `0` to also connect to any nearby device advertising `"FBX"` / `"FireBoard"`. |
| **`FIREBOARD_DATA_CHAR_UUID`** | `"c2f780ec-45e1-452b-a879-327e3140d1f1"` | FireBoard 2 BLE GATT `Notify` characteristic that outputs JSON temperature readings. |
| **`FIREBOARD_CONTROL_CHAR_UUID`** | `"c2f780ec-45e1-452b-a879-327e3140d1e8"` | FireBoard 2 BLE GATT `Write` characteristic that starts the stream when `0x01` is written. |
| **`FIREBOARD_START_STREAM_BYTE`** | `0x01` | Magic wake byte written to `FIREBOARD_CONTROL_CHAR_UUID` upon BLE connect. |
| **`FIREBOARD_MAX_CHANNELS`** | `6` | Maximum probe channel index (`Channels 1..6`). |
| **`FIREBOARD_PROBE_STALE_MS`** | `30000UL` (`30s`) | Time without a BLE notification before a channel is considered unplugged. |

---

## 5. Dashboard Infrastructure Reference on Host `ul` ([https://grill.drheck.dev](https://grill.drheck.dev))

| Component | Path / Address | Details |
| :--- | :--- | :--- |
| **Public URL (SSO Protected)** | [https://grill.drheck.dev](https://grill.drheck.dev) ([https://fireboard.drheck.dev](https://fireboard.drheck.dev)) | Protected by **Authentik ForwardAuth** (`Grill Monitor (FireBoard 2)` application tile in user portal) |
| **BLE-to-Wi-Fi Bridge Host** | `fireboardctl.drheck.dev` (`10.0.0.71` • MAC `80:f1:b2:51:49:20`) | Seeed Studio XIAO ESP32-C3; static DHCP lease in `/etc/dhcpd.conf` and local DNS in `/var/unbound/etc/unbound.conf` on `danknasty` |
| **Unauthenticated Ingest API** | `http://10.0.0.3:8088/api/ingest` / `http://10.0.0.3:8000/api/ingest` / `https://grill.drheck.dev/api/ingest` | Accepts `POST` JSON (`{"device_id": "FBX2-GJ8RC3JRD", "ble_connected": true, "rssi": -42, "probes": {"1": 225.4, "2": 165.2}}`) |
| **Podman Quadlet & App Source** | `/home/docker-data/.config/containers/systemd/grill.container`, `/data/docker/grill/app.py`, `/data/docker/grill/static/index.html` | Rootless Podman container `grill` (`docker-data`, `UID 1100`) running FastAPI + SQLite + Chart.js live/historical multi-channel dashboard (100% local, zero cloud functionality) |
| **Persistent SQLite Database** | `/data/docker/grill/data/grill.db` | Stores historical temperature series (`15m`, `1h`, `3h`, `6h`, `12h`, `24h` views) and bridge ping RTT history |
| **Dedicated Podman ULA IPv6** | `fd77:7777:5010:0:1337:f1ac:d00d:fb02` (Port `8000`) | Bound on `eth77` (`10.0.0.3:8088` and `10.0.0.3:8000` mapped on IPv4 LAN for `fireboardctl.drheck.dev`) |
| **DNS Configuration** | `~/git/router_config/unbound.conf` & `update_dns.py` | Local Unbound (`10.0.0.100` / `fd77:7777:5010::1337:f1ac:d00d:aaaa`) on `danknasty` + public deSEC IPv6 GUA sync |
| **Nginx Site Config** | `/etc/nginx/sites-available/grill` | Proxies `/` with Authentik ForwardAuth and `/api/ingest` with `auth_request off` |
| **Backup & Restore Scripts** | `/home/luke/git/backups/ul/scripts/backup/grill`, `/home/luke/git/backups/ul/scripts/restore/grill` | `shellcheck`-verified backups with `par2 create -r10` parity archives in `/data/backups/services/current/grill/` |
