Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4d7d6d9
Add ROBOROCK_SERVER_* env var config generation
virtitnerd Sep 27, 2026
02e7a1d
Wire env var config generation into the container entrypoint
virtitnerd Sep 27, 2026
0d5c5e8
Document the env-var-only Docker Compose bootstrap path
virtitnerd Sep 27, 2026
f2544bc
Let configure answers carry an external broker host
virtitnerd Sep 27, 2026
9fcaf31
Add the bootstrap /admin setup wizard
virtitnerd Sep 27, 2026
7769809
Fall into the setup wizard when config.toml is missing or invalid
virtitnerd Sep 27, 2026
36dc9dd
Document the browser-based setup wizard as an alternative to the CLI
virtitnerd Sep 27, 2026
f3b09b0
Point reverse-proxy users at the env-var config path
virtitnerd Sep 27, 2026
bfb94d7
Make the [admin] env vars optional as a group
virtitnerd Sep 27, 2026
66c468d
Split load_config() so [admin] can be diagnosed independently
virtitnerd Sep 27, 2026
d15fcbc
Support completing a partial (env-var-generated) config in the wizard
virtitnerd Sep 27, 2026
9a91992
Document the hybrid env-vars + wizard flow
virtitnerd Sep 27, 2026
0b96769
Document the admin-optional env vars and hybrid setup flow
virtitnerd Sep 27, 2026
a3af149
Add a redacted activity-log reader over the HTTP/MQTT jsonl logs
virtitnerd Sep 27, 2026
28557f9
Wire the Activity panel into the admin dashboard
virtitnerd Sep 27, 2026
c561722
Document the Activity panel and ROBOROCK_SERVER_ACTIVITY_RAW
virtitnerd Sep 27, 2026
1eeb39d
Add an internal endpoint for mitm_redirect.py to sync activity entries
virtitnerd Sep 27, 2026
c806ed7
Show MITM-captured traffic in the Activity panel
virtitnerd Sep 27, 2026
ab38148
Add --activity-sync to mitm_redirect.py
virtitnerd Sep 27, 2026
c27804a
Add a shared Materialize-based visual theme for /admin
virtitnerd Sep 27, 2026
255617e
Redesign the Setup Wizard with the shared theme
virtitnerd Sep 27, 2026
bc0486d
Redesign the admin dashboard with the shared theme
virtitnerd Sep 27, 2026
2baef49
Restore the .hidden utility class dropped during the theme consolidation
virtitnerd Sep 27, 2026
770e6e5
Fix four dashboard UI bugs found in live testing
virtitnerd Sep 28, 2026
0f6a5d3
Stop logging unmatched (catchall) requests as Activity
virtitnerd Sep 28, 2026
5b2a052
Fall back to the Setup Wizard on incomplete env-var config
virtitnerd Sep 27, 2026
00d88d1
Pre-fill the Setup Wizard from any ROBOROCK_SERVER_* env vars already…
virtitnerd Sep 27, 2026
bdaf287
Show one vacuum at a time on the admin dashboard
virtitnerd Oct 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ services:
- "${ROBOROCK_SERVER_HTTPS_PORT:-555}:${ROBOROCK_SERVER_HTTPS_PORT:-555}"
- "${ROBOROCK_SERVER_MQTT_TLS_PORT:-8881}:${ROBOROCK_SERVER_MQTT_TLS_PORT:-8881}"
volumes:
# Remove this line if you're generating config.toml from ROBOROCK_SERVER_*
# env vars instead of running `configure` first — see the "Environment
# Variables Only" section in docs/installation.md.
- ./config.toml:/app/config.toml:ro
- ./data:/data
- ./secrets:/run/secrets:ro
Expand Down
76 changes: 71 additions & 5 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,27 @@ If your model already has certificate notes on the tested-vacuums page, follow t
uv sync
```

3. Run the setup wizard:
3. Run the setup wizard.

```bash
uv run roborock-local-server configure
```
You have two options here, and they write the same `config.toml`:

- **In your browser (no `uv`/Python needed on the host):** skip straight to step 6 and start the container
with no `config.toml` present. It boots into a setup wizard, served in plain HTTP at `/admin` on your
configured HTTPS port (`http://<this host>:555/admin` by default), that asks for the same fields as the
CLI wizard below. Submitting it writes `config.toml`, and the container restarts itself into the full
HTTPS/MQTT stack - reload the page after a few seconds and you're at the real (now HTTPS) admin login.
- **On the command line:**

```bash
uv run roborock-local-server configure
```

You can also combine the two: if `ROBOROCK_SERVER_*` env vars generate `config.toml`'s network/broker/TLS
settings but the admin ones (`ROBOROCK_SERVER_ADMIN_PASSWORD`, `ROBOROCK_SERVER_PROTOCOL_LOGIN_EMAIL`,
`ROBOROCK_SERVER_PROTOCOL_LOGIN_PIN`) are left unset, the container still boots into `/admin`, but the
wizard notices the rest is already configured and only asks for admin credentials. Useful for a
reverse-proxy or ACME setup you'd rather express as env vars/IaC, while still picking the admin password
in a browser instead of a compose file.

The wizard asks for:

Expand All @@ -111,7 +127,7 @@ If your model already has certificate notes on the tested-vacuums page, follow t

It then writes `config.toml`, generates `admin.password_hash` and `admin.session_secret`, and if you chose Cloudflare it also writes `secrets/cloudflare_token`. If you also chose `acme_server = actalis`, it writes `secrets/acme_eab_kid` and `secrets/acme_eab_hmac_key`.

4. If you chose external MQTT, fill in `broker.host` in `config.toml` before starting the stack. See [Custom MQTT](custom_mqtt.md).
4. If you chose external MQTT with the CLI wizard, fill in `broker.host` in `config.toml` before starting the stack (the browser wizard asks for the broker host directly, so this step doesn't apply there). See [Custom MQTT](custom_mqtt.md).

5. If you skipped Cloudflare, put your certificate files in `data/certs/fullchain.pem` and `data/certs/privkey.pem` (relative to the repository root on the host, which maps to `/data/certs/` inside the container). This is the path to use when your vacuum works better with a certificate chain you manage yourself. See [Custom certificate management](custom_cert_management.md).

Expand Down Expand Up @@ -139,6 +155,54 @@ If your model already has certificate notes on the tested-vacuums page, follow t

For reverse proxy setups, keep `network.https_port` and `network.mqtt_tls_port` set to the backend listener ports and use `network.advertised_https_port` / `network.advertised_mqtt_tls_port` for the public ports.

### Alternative: Environment Variables Only (No `uv` Toolchain)

If you don't want to install `uv`/Python on the host just to run `configure`, you can set `ROBOROCK_SERVER_*` environment variables instead. On first boot, if `/data/config.toml` doesn't already exist, the container generates one from these variables and starts normally. If `config.toml` already exists (mounted or previously generated), it always wins and the env vars are ignored — this never overwrites a config you've already set up.

Required variables:

- `ROBOROCK_SERVER_STACK_FQDN`
- Either `ROBOROCK_SERVER_CERT_FILE` + `ROBOROCK_SERVER_KEY_FILE` (bring your own certificate), or `ROBOROCK_SERVER_TLS_MODE=cloudflare_acme` + `ROBOROCK_SERVER_TLS_BASE_DOMAIN` + `ROBOROCK_SERVER_TLS_EMAIL` + `ROBOROCK_SERVER_CLOUDFLARE_TOKEN` (or `_CLOUDFLARE_TOKEN_FILE` for a Docker secret)

The admin credentials are optional as a group:

- Set all three of `ROBOROCK_SERVER_ADMIN_PASSWORD`, `ROBOROCK_SERVER_PROTOCOL_LOGIN_EMAIL`, and
`ROBOROCK_SERVER_PROTOCOL_LOGIN_PIN` (6 digits) for a fully headless boot straight into the running stack.
- Leave all three unset, and the container still generates `config.toml`'s network/broker/TLS settings from
the env vars above, then boots into the setup wizard at `/admin` - which notices the rest is already
configured and asks only for admin credentials. Handy if you're driving the network/TLS/reverse-proxy side
from env vars or IaC, but would still rather pick the admin password in a browser.
- Setting some but not all three is rejected with a clear error, to avoid a half-set credential.

Commonly-set optional variables: `ROBOROCK_SERVER_HTTPS_PORT`, `ROBOROCK_SERVER_MQTT_TLS_PORT`, `ROBOROCK_SERVER_ADVERTISED_HTTPS_PORT`, `ROBOROCK_SERVER_ADVERTISED_MQTT_TLS_PORT`, `ROBOROCK_SERVER_LISTENER_MODE`, `ROBOROCK_SERVER_BROKER_MODE` + `ROBOROCK_SERVER_BROKER_HOST`. See `env_config.py` for the full list.

**Important:** if you go this route, remove (or comment out) the `./config.toml:/app/config.toml:ro` line from `compose.yaml`. Docker will otherwise bind-mount a nonexistent host path as an empty file, which the container treats as an existing (but invalid) config and never falls through to the env vars. Keep the `./data:/data` and `./secrets:/run/secrets:ro` mounts.

Fully headless (no browser step at all):

```bash
export ROBOROCK_SERVER_STACK_FQDN=api-roborock.example.com
export ROBOROCK_SERVER_ADMIN_PASSWORD=super-secret-password
export ROBOROCK_SERVER_PROTOCOL_LOGIN_EMAIL=user@example.com
export ROBOROCK_SERVER_PROTOCOL_LOGIN_PIN=123456
export ROBOROCK_SERVER_TLS_MODE=cloudflare_acme
export ROBOROCK_SERVER_TLS_BASE_DOMAIN=example.com
export ROBOROCK_SERVER_TLS_EMAIL=acme@example.com
export ROBOROCK_SERVER_CLOUDFLARE_TOKEN=your-cloudflare-api-token
docker compose up -d --build
```

Hybrid (network/TLS from env vars, admin credentials in the browser wizard at `/admin`):

```bash
export ROBOROCK_SERVER_STACK_FQDN=api-roborock.example.com
export ROBOROCK_SERVER_TLS_MODE=cloudflare_acme
export ROBOROCK_SERVER_TLS_BASE_DOMAIN=example.com
export ROBOROCK_SERVER_TLS_EMAIL=acme@example.com
export ROBOROCK_SERVER_CLOUDFLARE_TOKEN=your-cloudflare-api-token
docker compose up -d --build
```

## Method 2: Home Assistant Add-on

Use [Home Assistant](home_assistant.md) as the installation guide if you want to run the stack as a Home Assistant add-on instead of Docker Compose.
Expand All @@ -159,6 +223,8 @@ Use [Home Assistant](home_assistant.md) as the installation guide if you want to

4. For any routines that use zones, re-save them so the server stores the zone data correctly. In the Roborock app, open each routine that has zones, open the zone, tap **Edit**, open any **Zone Cleaning** entry, then tap **Save**. Repeat for each zone in the routine.

5. The dashboard's **Activity** panel shows recent HTTP/MQTT traffic between your vacuum(s)/app/Home Assistant and this server - useful for confirming a device is actually talking to the local stack. It only shows redacted metadata (method/path/topic/RPC method name, sizes, timestamps) by default, since the underlying logs can contain device keys and decrypted command payloads. Set `ROBOROCK_SERVER_ACTIVITY_RAW=1` if you need full unredacted entries for debugging.

## Next Steps

- [Onboarding](onboarding.md) for pairing a new vacuum
Expand Down
10 changes: 9 additions & 1 deletion docs/reverse_proxy.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Reverse Proxy

Reverse proxy support is mainly useful when public or LAN clients reach the stack on different ports than the backend listeners, or when you already run a proxy (Caddy, Traefik, nginx) that owns your TLS certificates. I do not use a reverse proxy for my own setup, so please report any issues.
Reverse proxy support is mainly useful when public or LAN clients reach the stack on different ports than the backend listeners, or when you already run a proxy (Caddy, Traefik, nginx, SWAG) that owns your TLS certificates. I do not use a reverse proxy for my own setup, so please report any issues.

> **Neither the `configure` CLI wizard nor the browser Setup Wizard (`/admin` on first boot) can generate `listener_mode`, `advertised_*_port`, or `trusted_proxies` today** — they only produce the simple `local_tls` case. If your setup needs any of the settings on this page (which it will, for a proxy like SWAG that terminates TLS itself), generate `config.toml` from `ROBOROCK_SERVER_*` environment variables instead (see [Installation](installation.md)) - that path covers everything below directly, with no manual `config.toml` edits needed. The TOML shown on this page is what those env vars produce; it's included so you can see (or hand-edit) the result either way.

Whatever endpoint a vacuum or the Roborock app connects to **must present a valid, trusted TLS certificate** — vacuums refuse to connect otherwise. That endpoint can be the server itself or the proxy in front of it; the rest of this page is about choosing which one terminates TLS.

Expand Down Expand Up @@ -29,6 +31,8 @@ With that config the server listens on `*:555` / `*:8881`, but responses adverti
- `https://api-roborock.example.com`
- `ssl://api-roborock.example.com:8883`

Equivalent env vars: `ROBOROCK_SERVER_ADVERTISED_HTTPS_PORT=443`, `ROBOROCK_SERVER_ADVERTISED_MQTT_TLS_PORT=8883`.

## TLS Termination Modes

### `local_tls` (default) — the server terminates TLS
Expand Down Expand Up @@ -60,6 +64,8 @@ listener_mode = "external_tls"
mode = "provided"
```

Equivalent env vars: `ROBOROCK_SERVER_LISTENER_MODE=external_tls`, `ROBOROCK_SERVER_TLS_MODE=provided`.

Requirements:

- HTTPS: the proxy terminates TLS, preserves the original `Host` header, and forwards plain HTTP to `https_port`.
Expand Down Expand Up @@ -133,6 +139,8 @@ List every proxy or SNAT address that forwards traffic to the server in `trusted
trusted_proxies = ["10.42.0.0/16", "10.1.1.10"]
```

Equivalent env var: `ROBOROCK_SERVER_TRUSTED_PROXIES=10.42.0.0/16,10.1.1.10` (comma-separated).

- **HTTP:** requests from a trusted proxy use the `X-Forwarded-For` header to find the vacuum's real IP. The header is ignored from any other client, so it can't be spoofed. Traefik and Caddy set it by default; nginx needs `proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;`.
- **MQTT:** during an active onboarding session, a new MQTT login is normally accepted only from the IP that completed `/region` and `/nc`. A connection from a trusted proxy is also accepted, because a stream proxy hides the vacuum's address. The session must still have completed `/region` and `/nc`, and the first publish must be on that vacuum's topic.

Expand Down
10 changes: 10 additions & 0 deletions docs/roborock_app.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ The launcher can auto-load a sync secret from `config.toml` beside `mitm_redirec

The launcher now preflights that callback before starting `mitmweb`. If the `--local-api` host cannot be reached, if the TLS certificate does not validate for that host, or if the sync secret is rejected, the script exits immediately instead of letting you proceed into a broken login flow.

### Watching App Traffic In The Dashboard

Add `--activity-sync` (off by default) to also send a copy of each redirected request/response to your server's admin dashboard, so you can watch what the official app is doing from the **Activity** panel without leaving the browser:

```bash
uv run mitm_redirect.py --local-api api-roborock.example.com --sync-secret YOUR_ADMIN_SESSION_SECRET --activity-sync
```

This uses the same `--sync-secret` as the login sync above - no separate secret to manage. The Activity panel redacts these entries (method/host/path/status only) by default, since this traffic includes real Roborock account tokens; set `ROBOROCK_SERVER_ACTIVITY_RAW=1` on the server if you need full headers/bodies for debugging. Because each captured request blocks briefly on a network call to your server, leave this off for normal daily use and only turn it on for an active debugging session.

### iPhone

1. Log out of the app on your phone.
Expand Down
Loading