---
okf: "0.1"
type: node
visibility: private
status: active
updated: 2026-05-27
links:
  - ../runbooks/chelsty-deploy-recovery.md
---

# CHELSTY Runtime

This document describes the runtime environment and deployment flow for CHELSTY, an offline-capable home automation edge node split across two VMs.

| Node | Role | Services |
|------|------|----------|
| `chelsty-infra` | LTE edge hypervisor | Mosquitto, Zigbee2MQTT, stability-agent, node-agent |
| `chelsty-ha` | Home Assistant VM | homeassistant (no node-agent — see below) |

Both nodes share an LTE uplink and must function fully offline (Zigbee, MQTT, HA automations) without any connectivity to SATURN, VPS, or Forgejo.

## Runtime Layout

```
/opt/homelab/
├── config/          # Service-specific configs and secrets (not in Git)
│   ├── mosquitto/
│   └── zigbee2mqtt/
├── data/            # Persistent service data
│   ├── mosquitto/   # Persistence DB, password file
│   └── zigbee2mqtt/
│       └── data/    # z2m config, coordinator backup, network key
└── logs/
```

## SLZB-06U Integration

CHELSTY uses a SMLIGHT SLZB-06U Zigbee coordinator connected over Ethernet/TCP.

- **Coordinator IP**: `192.168.1.105`
- **Port**: `6638`
- **Adapter**: `ezsp` (deprecated — migration to `ember` recommended, requires only changing `adapter: ember` in `configuration.yaml`)
- **Zigbee2MQTT config key**: `serial.port: tcp://192.168.1.105:6638`

⚠️ Never use `/dev/ttyUSB0` — the coordinator is always TCP-only on this site.

## Networking Constraints

### Mosquitto — `network_mode: host`
Mosquitto runs with `network_mode: host` so that all containers on the same host can reach it at `localhost:1883`. **Do not change this.**

### Zigbee2MQTT — bridge network + extra_hosts
Zigbee2MQTT runs in a bridge-networked container (needed for port mapping compatibility with docker-compose v1). To reach the host-networked Mosquitto:

```yaml
# hosts/chelsty-infra/runtime/zigbee2mqtt/docker-compose.override.yml
services:
  zigbee2mqtt:
    extra_hosts:
      - "mosquitto:host-gateway"
```

This maps the `mosquitto` hostname inside the z2m container to the Docker host gateway IP, so `mqtt://mosquitto:1883` reaches the host-networked Mosquitto process.

**Why not `network_mode: host` for z2m?**  
chelsty-infra runs docker-compose v1 (1.29.2). In v1, `network_mode: host` cannot coexist with `ports:` declared in the base `docker-compose.yml` — raises `InvalidArgument`. The `extra_hosts` approach avoids this.

## Zigbee2MQTT Config Location

The `configuration.yaml` **must be writable** — z2m migrates and rewrites it on startup. It lives in the data directory:

```
/opt/homelab/data/zigbee2mqtt/data/configuration.yaml
```

This path is mounted read-write by the base `docker-compose.yml`:
```yaml
volumes:
  - /opt/homelab/data/zigbee2mqtt/data:/app/data
```

Do **not** mount `configuration.yaml` as a separate `:ro` volume — z2m will fail with `EROFS`.

### Minimal configuration.yaml
```yaml
homeassistant: true
permit_join: false
mqtt:
  base_topic: zigbee2mqtt
  server: mqtt://mosquitto:1883
serial:
  port: tcp://192.168.1.105:6638
  adapter: ezsp
frontend:
  port: 8080
advanced:
  log_level: info
```

## chelsty-ha — No node-agent

`chelsty-ha` does not have a node-agent deployed. Home Assistant is monitored indirectly: if MQTT goes silent on `chelsty-infra`, HA is likely down.

In `hosts/chelsty-ha/services.yaml`:
```yaml
services:
  homeassistant:
    monitor: false   # No node-agent; suppresses supervisor action generation
```

Remove `monitor: false` once node-agent is bootstrapped on this VM.

## Critical Backup Sets

| Data | Path |
|------|------|
| HA config + DB | `/opt/homelab/data/homeassistant/` on chelsty-ha |
| z2m config + coordinator backup + network key | `/opt/homelab/data/zigbee2mqtt/data/` |
| Mosquitto persistence + password file | `/opt/homelab/data/mosquitto/` |
| SLZB-06U coordinator state | Backup via SLZB-06U web UI at `192.168.1.105` |

> ⚠️ The Zigbee network key is in `configuration.yaml` or `coordinator_backup.json` — losing it requires re-pairing all devices.
