Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions docs/custom_cert_management.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,23 @@ key_file = "/data/certs/privkey.pem"

If you use the setup wizard and answer no to Cloudflare, it will write these values for you. You then need to place your certificate files at `data/certs/fullchain.pem` and `data/certs/privkey.pem` before starting the stack.

## Which hostname to cover

The vacuum uses the **stack hostname** (the `api-...` value from `config.toml` → `network.stack_fqdn`) for both SNI in ClientHello and hostname verification against the certificate. It does **not** use the value of the `r` field from the cfgwifi packet (which is the stack hostname with `api-` stripped).

In practice this means:

- A **single-domain** certificate covering only `api-roborock.example.com` is sufficient.
- You do **not** need a SAN for the stripped hostname `roborock.example.com`, nor a wildcard.
- DNS resolution for the stripped hostname is also unnecessary — only `api-roborock.example.com` needs to resolve to the server's LAN IP.

If you are migrating from a multi-domain cert and want to switch to a cheaper single-domain CA (e.g. Actalis free DV quota which issues one hostname per EAB), issue for the stack hostname only.

See [Tested Vacuums](tested_vacuums.md) for a diagnosis flowchart when the vacuum rejects a cert.

## Related Docs

- [Installation](installation.md)
- [Cloudflare setup](cloudflare_setup.md)
- [Onboarding](onboarding.md)
- [Tested Vacuums](tested_vacuums.md)
29 changes: 29 additions & 0 deletions docs/onboarding.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,35 @@ You still need to reset the vacuum's Wi-Fi manually. On many Roborock models tha

Congrats! Once the script reports that the vacuum is connected to the local server, the onboarding flow is complete.

## Troubleshooting

### `samples=0` and the vacuum never makes progress

After sending the cfgwifi packet, the script polls the server for the vacuum's onboarding status (`samples`, `public_key`, `connected`). If `samples` stays at 0 across multiple cycles, the vacuum is making TLS connections but never sending an HTTP request that the server can use to recover the public key. The most common cause is a **certificate the vacuum rejects**.

Check `mqtt_server.log` on the server for the post-handshake-close pattern:

```
TLS handshake ok from 192.168.x.y:NNNNN (TLSv1.3)
[conn N] backend connect 127.0.0.1:18830 from 192.168.x.y:NNNNN
[WARNING] [conn N] client closed before MQTT CONNECT
```

If you see `TLS handshake ok` immediately followed by `client closed before MQTT CONNECT` and `decompiled_http.jsonl` stays empty, the vacuum completed the TLS handshake server-side but rejected the cert during its own validation (CA chain not trusted, or hostname mismatch). See [Tested Vacuums](tested_vacuums.md) → "Diagnosing cert rejection" for the full flowchart.

Common fixes:

- The cert must cover the **stack hostname** (the `api-...` value from `config.toml`), not the value of the `r` field from cfgwifi. See [Custom certificate management](custom_cert_management.md) → "Which hostname to cover".
- The CA must be trusted by your vacuum model. Check [Tested Vacuums](tested_vacuums.md) for the compatibility matrix. Actalis is the safest fallback for older / pickier models.

### `state=missing` for the selected vacuum

The server has the vacuum in its inventory (cloud import done) but has not yet seen any onboarding traffic. Run another cycle: reset the vacuum's Wi-Fi, send the cfgwifi packet again, and wait.

### `public_key=False` with `samples > 0`

Recovery needs at least two query signature samples. If the count is stuck at 1, the vacuum is not making new attempts — give it a few minutes to retry, or run another cfgwifi cycle.

## Web UI (start_onboarding_gui.py)

If you would rather not use the terminal, there is a web UI version of the same flow. It runs a small local server on your machine and opens your browser automatically:
Expand Down
17 changes: 17 additions & 0 deletions docs/tested_vacuums.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Legend:
|---|---:|---:|---:|---:|---:|
| Roborock S5 Max | Not reported | ❌ | ✅ | ❌ | ❓ |
| Roborock S7 | Not reported | ❓ | ✅ | ❌ | ✅ |
| Roborock S7 Pro Ultra (a62) | Not reported | ❓ | ✅ | ❌ | ❓ |
| Roborock S7 MaxV | Not reported | ✅ | ❓ | ✅ | ❓ |
| Roborock S8 | Not reported | ❓ | ❓ | ❓ | ❓ |
| Roborock S8 MaxV Ultra | Not reported | ✅ | ❓ | ❓ | ❓ |
Expand All @@ -57,6 +58,22 @@ Legend:
| QRevo Master | Not reported | ❓ | ❓ | ❓ | ❓ |
| QRevo Plus | Not reported | ✅ | ❓ | ❓ | ❓ |

## Diagnosing cert rejection

When a vacuum rejects the TLS certificate, the failure pattern is **post-handshake close** rather than a TLS alert:

- `mqtt_server.log` shows `TLS handshake ok from <vacuum-ip>` immediately followed by `client closed before MQTT CONNECT`.
- `decompiled_http.jsonl` stays empty (the vacuum never sends an HTTP request to `:555`).
- Packet capture shows the vacuum doing repeated short TLS connections (~30 ms) with TCP FIN and no `tls.alert` in either direction.

This pattern indicates the vacuum completed the TLS handshake server-side but rejected the cert during its own validation (CA chain not trusted, or hostname mismatch). The vacuum then closes the TCP connection without sending any application data.

### Hostname verification

The vacuum uses the **stack hostname** (the `api-...` value from `config.toml` → `network.stack_fqdn`) for both SNI in ClientHello and hostname verification against the certificate. It does **not** use the value of the `r` field from the cfgwifi packet (which is the stack hostname with `api-` stripped).

This means a single-domain certificate covering only the stack hostname (e.g. `api-roborock.example.com`) is sufficient. You do **not** need a SAN for the stripped hostname (`roborock.example.com`) or a wildcard certificate. DNS resolution for the stripped hostname is also unnecessary.

## Unlisted Vacuums

If your model is not listed, start with ZeroSSL unless it is an older model that is likely to need a different trusted chain. If onboarding fails after the DNS and server checks pass, try Actalis or a provided certificate and report the result.
Expand Down