diff --git a/AGENTS.md b/AGENTS.md index 3b5e481..e1da488 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -83,7 +83,7 @@ not maintain a second copy of the machine table here. | RustDesk health (hk2) | [runbooks/rustdesk-health.md](runbooks/rustdesk-health.md) | | AdGuard Home health | [runbooks/adguard-home-health.md](runbooks/adguard-home-health.md) | | Home Assistant maintenance | [runbooks/home-assistant-maintenance.md](runbooks/home-assistant-maintenance.md) + [scripts/ha-maintenance.sh](runbooks/scripts/ha-maintenance.sh) | -| matrix_e2e update (hass.windy.lan) | [runbooks/matrix-e2e-update.md](runbooks/matrix-e2e-update.md) | +| matrix_e2ee update (hass.windy.lan) | [runbooks/matrix-e2ee-update.md](runbooks/matrix-e2ee-update.md) | | Matrix upstream refs | [docs/matrix-upstream.md](docs/matrix-upstream.md) | | Hermes Agent Matrix channel | [docs/hermes-matrix.md](docs/hermes-matrix.md) | | UniFi local-service proxy bypass | [docs/unifi-openclash-localhost.md](docs/unifi-openclash-localhost.md) | diff --git a/docs/home-assistant-matrix.md b/docs/home-assistant-matrix.md index cd749d3..0d06f80 100644 --- a/docs/home-assistant-matrix.md +++ b/docs/home-assistant-matrix.md @@ -5,9 +5,9 @@ to the self-hosted Matrix homeserver at [`synapse.chans.xyz`](../hosts/synapse.c Deliberately contains no Matrix passwords, access tokens, or room encryption material. > **Status (2026-08-15, W1N-139):** the built-in `matrix` integration has been -> **retired** on `hass.windy.lan` and replaced by the custom \*\*\`matrix_e2e\`\*\* +> **retired** on `hass.windy.lan` and replaced by the custom **`matrix_e2ee`** > integration. The sections below on the built-in integration are kept for -> reference only. See [matrix_e2e](#matrix-e2e-custom-e2e-integration) for the +> reference only. See [matrix_e2ee](#matrix-e2ee-custom-e2e-integration) for the > active setup and [Device verification (SAS) model](#device-verification-sas-model) > for how device trust works. @@ -176,19 +176,19 @@ automation: message: "It looks like you wrote !testword" ``` -## matrix_e2e (custom E2E integration) +## matrix_e2ee (custom E2E integration) -Custom integration [`windyboy/ha-matrix-e2e`](https://github.com/windyboy/ha-matrix-e2e), -release **v0.2.6** (W1N-173/W1N-174), deployed on `hass.windy.lan` -2026-08-16 (upgraded from v0.2.4; v0.2.0 was the first Config Flow release, W1N-154). +Custom integration [`windyboy/ha-matrix-e2ee`](https://github.com/windyboy/ha-matrix-e2ee), +release **v0.3.0** (W1N-180/#32: bot-initiated device verification wizard), +deployed on `hass.windy.lan` 2026-08-18 (upgraded from v0.2.10). Runs a dedicated bot with a **persistent E2EE device identity**. -- Domain `matrix_e2e`; Config Flow (UI) with YAML import migration, not in HACS. Does **not** +- Domain `matrix_e2ee`; Config Flow (UI) with YAML import migration, not in HACS. Does **not** override the built-in `matrix` integration. - Dependencies are declared **explicitly** in `manifest.json` to work around Home Assistant's `is_installed` dropping the `[e2e]` extra (W1N-140): `matrix-nio[e2e]==0.26.0` + `vodozemac` + `peewee` + `cachetools` + `atomicwrites`. -- **v0.2.0 migration:** YAML `matrix_e2e:` block was auto-imported into a Config Entry +- **v0.2.0 migration:** YAML `matrix_e2ee:` block was auto-imported into a Config Entry (`source: import`) on first startup, then removed. All settings now managed via **Settings → Devices & Services → Matrix E2EE → Configure**. See [hosts/hass.windy.lan.md](../hosts/hass.windy.lan.md) for the deployed state. @@ -205,15 +205,15 @@ Runs a dedicated bot with a **persistent E2EE device identity**. - `verify_device_by_fingerprint` (`user_id`, `device_id`, `ed25519`; added v0.1.3, renamed from `verify_device` in v0.1.4; requires exact `ed25519` match) - Events: - - `matrix_e2e_command` (`room_id`, `sender`, `command`, `args` only — + - `matrix_e2ee_command` (`room_id`, `sender`, `command`, `args` only — never the raw body) - - `matrix_e2e_error` (codes, no secrets) - - `matrix_e2e_verification` (`stage`, `transaction_id`, `user_id`, `device_id`, + - `matrix_e2ee_error` (codes, no secrets) + - `matrix_e2ee_verification` (`stage`, `transaction_id`, `user_id`, `device_id`, optional `emojis`, optional `expires_at`; `expires_at` added v0.1.3) - - `matrix_e2e_fingerprint` (`user_id`, `device_id`, `ed25519`, `curve25519` — + - `matrix_e2ee_fingerprint` (`user_id`, `device_id`, `ed25519`, `curve25519` — public keys only; added v0.1.3) -- `notify.matrix_e2e` is **not implemented** (upstream deferred) — notifications - must call `matrix_e2e.send_message` (message + room_id). +- `notify.matrix_e2ee` is **not implemented** (upstream deferred) — notifications + must call `matrix_e2ee.send_message` (message + room_id). - Commands fire Home Assistant events only; the integration never calls `domain.service` itself. Map commands in automations. - Encrypted rooms fail-closed on unverified devices. @@ -223,10 +223,10 @@ Runs a dedicated bot with a **persistent E2EE device identity**. ### Storage & recovery -- `.storage/matrix_e2e_session.json` (`user_id`, `device_id`, `access_token`, - `pickle_key`) and `.storage/matrix_e2e_store/` (Olm/Megolm, device trust, +- `.storage/matrix_e2ee_session.json` (`user_id`, `device_id`, `access_token`, + `pickle_key`) and `.storage/matrix_e2ee_store/` (Olm/Megolm, device trust, sync token). Both stay on the HA persistent volume and are in HA backups. -- Soft logout → `matrix_e2e.reauthenticate` (keeps `device_id` + crypto store; +- Soft logout → `matrix_e2ee.reauthenticate` (keeps `device_id` + crypto store; rejected outside soft-logout state since v0.1.3). - Hard logout / store loss → delete session + store, restart with password, re-SAS (a **new device**; old history not decryptable). @@ -240,7 +240,8 @@ matrix-nio [examples](https://matrix-nio.readthedocs.io/en/latest/examples.html) [element-android#6832](https://github.com/vector-im/element-android/issues/6832), Element [device-verification](https://element.io/features/device-verification). -`matrix_e2e` supports two verification paths: +`matrix_e2ee` supports three verification paths (the v0.3.0 wizard automates +the bot-initiated flow): ### 1. SAS (mutual, manual confirmation since v0.1.4) @@ -256,7 +257,7 @@ Element [device-verification](https://element.io/features/device-verification). - Element's "Verify" button only starts **in-DM user verification**; it has no "verify a specific device of another user via to-device" flow (matrix.org recommends hiding per-device verification for other users). -- `matrix_e2e` implements **raw to-device device SAS** (`start_verification`/ +- `matrix_e2ee` implements **raw to-device device SAS** (`start_verification`/ `confirm_verification`), **no cross-signing / in-room**. This is a non-standard cross-user path: works with matrix-nio + Element Web/Desktop (reported in element-android#6832), **not** on Element Android/X. @@ -282,16 +283,18 @@ Element [device-verification](https://element.io/features/device-verification). `_max_age` 5 min); `_repair_dropped_start()` recovers SAS `start` events nio dropped when the peer device was unknown (W1N-170/W1N-172); `VERIFICATION_TIMEOUT_SECONDS` 600→240 (fires before nio's `_max_age`). +- **v0.2.11**: `receive_mac_event` no longer overrides canceled state (W1N-179/#31). +- **v0.3.0**: bot-initiated device verification wizard (W1N-180/#32). - Inbound SAS is gated to `allowed_users` (v0.1.3). ### 2. One-sided fingerprint (added v0.1.3, hardened v0.1.4) -- Call `matrix_e2e.get_fingerprint` to get the bot's own `ed25519` device key - (read it from the `matrix_e2e_fingerprint` event). +- Call `matrix_e2ee.get_fingerprint` to get the bot's own `ed25519` device key + (read it from the `matrix_e2ee_fingerprint` event). - In Element, open the bot user's sessions and use "Manually verify by text". Compare the session key with the fingerprint. - To trust another device from the bot's side, call - `matrix_e2e.verify_device_by_fingerprint` with the peer's `user_id`, `device_id`, + `matrix_e2ee.verify_device_by_fingerprint` with the peer's `user_id`, `device_id`, and `ed25519` key. The match is exact (since v0.1.4's rename from `verify_device`). Feed the **peer** key, not the bot's own key. - This trusts from one side only; the peer still trusts the bot independently. diff --git a/hosts/hass.windy.lan.md b/hosts/hass.windy.lan.md index 312e18c..590762a 100644 --- a/hosts/hass.windy.lan.md +++ b/hosts/hass.windy.lan.md @@ -9,7 +9,7 @@ | IPv4 | `192.168.55.11` (LAN55) | | DNS | `hass.windy.lan` (AdGuard rewrite on `dns.windy.lan`; legacy `hass.local` alias) | | SSH | `ssh hassio@hass.windy.lan` | -| **Host** | **PVE VM 180 (`haos`)** — not a separate physical host (verified 2026-08-09) | +| **Host** | **x88 Pro physical box** (HAOS bare-metal, `machine: green`; verified 2026-08-18) | | Platform | Home Assistant OS; kernel `6.1.115-haos` (aarch64) | | Web UI | `http://hass.windy.lan:8123` (LAN); WAN port-forward `hass` on gw → `:8123` | @@ -209,13 +209,13 @@ Full command family: [runbooks/home-assistant-maintenance.md](../runbooks/home-a ### `china_southern_power_grid_stat` live tree -**v1.3.1** (`55a293fc`, verified 2026-08-14, W1N-107): manual zipball of +**v1.3.2** (`934f58c`, verified 2026-08-15, W1N-118): manual zipball of GitHub release -[v1.3.1](https://github.com/windyboy/china_southern_power_grid_stat/releases/tag/v1.3.1) +[v1.3.2](https://github.com/windyboy/china_southern_power_grid_stat/releases/tag/v1.3.2) copied to `/config/custom_components/china_southern_power_grid_stat`. -Earlier trees: v1.3.0/`69f13c90` (W1N-106), `a433e8c` (W1N-105), `de01914` -(W1N-103), `eb8b174` (W1N-102). Backups: -`/homeassistant/.csg-backups/` (w1n102/104/105/106/107). +Earlier trees: v1.3.1/`55a293fc` (W1N-107), v1.3.0/`69f13c90` (W1N-106), +`a433e8c` (W1N-105), `de01914` (W1N-103), `eb8b174` (W1N-102). Backups: +`/homeassistant/.csg-backups/` (w1n102/104/105/106/107/118). v1.3.0 crashed the coordinator on first refresh (`TypeError: cannot pickle 'mappingproxy' object` in @@ -248,45 +248,41 @@ The `_unrecorded_attributes` mechanism exists in Core 2026.8.1 by recorder `shared_attrs_bytes_from_event`). -### `matrix_e2e` live tree (E2E Matrix bot, verified 2026-08-16, W1N-175) +### `matrix_e2ee` live tree (E2E Matrix bot, verified 2026-08-18, W1N-180) -**v0.2.7** (W1N-176: SAS cancel code/reason logging for diagnosis): -source copy from `/home/windy/project/ha-matrix-e2e` (development clone) deployed -2026-08-16 via SSH rsync from workstation (upgraded from v0.2.6). Custom **`matrix_e2e`** integration — **Config Flow** -(UI). See [docs/home-assistant-matrix.md](../docs/home-assistant-matrix.md). -**Update runbook:** [runbooks/matrix-e2e-update.md](../runbooks/matrix-e2e-update.md). +**v0.3.0** (W1N-180/#32: bot-initiated device verification wizard; also +includes W1N-179/#31 fix `receive_mac_event` cancel-state override): +source copy from `/home/windy/project/ha-matrix-e2ee` main `216cc99` +deployed 2026-08-18 via SSH rsync from workstation (upgraded from v0.2.10, +backup `matrix_e2ee.bak-20260818-v0.2.10`). +Custom **`matrix_e2ee`** integration — **Config Flow** (UI). See +[docs/home-assistant-matrix.md](../docs/home-assistant-matrix.md). +**Update runbook:** [runbooks/matrix-e2ee-update.md](../runbooks/matrix-e2ee-update.md). -- Bot `@hass:chans.xyz` reused (E2EE device `rO1R915ncu`). v0.2.0 YAML import - migrated existing config into a **Config Entry** (`source: import`), then the - YAML block was removed from `configuration.yaml`. All settings now managed via - **Settings → Devices & Services → Matrix E2EE → Configure**. +- Bot `@hass:chans.xyz` reused (E2EE device `rO1R915ncu`). Config Entry + `01M04D7C1M4T2GX5VPG7NVQ7GV` (`source: import`, `state: loaded`). All + settings via **Settings → Devices & Services → Matrix E2EE → Configure**. - Config Entry options: `allowed_rooms` `["!gidvAzpDzwtzfEDrqu:chans.xyz", "!boxfylDSzOvrWkcsyY:chans.xyz"]`, `allowed_users` `["@zhiqiang:chans.xyz"]`, `command_prefix` `"!"`. -- Storage (persistent volume, in HA backups as of upgrade): `/config/.storage/matrix_e2e_session.json` - + `/config/.storage/matrix_e2e_store/`. Config backups: - `/homeassistant/.matrix-e2ee-backups/` (`matrix_e2e.bak-20260816`, - `matrix_e2e.bak-20260816-v0.2.3`, `matrix_e2e.bak-20260816-v0.2.4`); +- Storage: `/config/.storage/matrix_e2ee_session.json` + + `/config/.storage/matrix_e2ee_store/`. Backups: + `/homeassistant/.matrix-e2ee-backups/` (incl. `matrix_e2ee.bak-20260818-v0.2.10`, + `matrix_e2ee.bak-20260816-v0.2.9`, `matrix_e2ee.bak-20260816-v0.2.8`); full HA backup slugs `3d9d36db` (pre-v0.1.4) + `9f223f35` (pre-v0.2.0). -- v0.2.6 changes: `_log_verification_state()` tracks SAS state transitions - with `async_write_ha_state` for diagnosis (W1N-174); `_bridge_verification_request()` - handles inbound `m.key.verification.request` → `m.key.verification.ready` since - nio lacks a `request` framework (W1N-173). -- v0.2.5 changes: bridge `m.key.verification.request` → `ready` (nio lacks - request framework, W1N-173). -- v0.2.4 changes: `_patch_nio_sas_timeout()` workaround for nio 0.26.0 - `_last_event_time` stale-timestamp bug (SAS timed out at 60s regardless of - activity); `_repair_dropped_start()` recovers SAS `start` events nio dropped - when the peer device was unknown; `_query_device_keys()` generalized beyond - own device keys; `VERIFICATION_TIMEOUT_SECONDS` 600→240 (fires before nio's - 5 min `_max_age`). Earlier undocumented deploys: v0.2.3 (W1N-168 sync loop - background task, W1N-169 SAS double-send fix), v0.2.1 (W1N-167 storage I/O - `asyncio.to_thread`, W1N-166 own-keys query). -- Automation `1761188403590`「Matrix 聊天关卫生间灯」unchanged: trigger - `matrix_e2e_command` (command `关卫生间灯`), actions `light.turn_off` + - `matrix_e2e.send_message` (room `!gidvAzpDzwtzfEDrqu`). +- v0.3.0: bot-initiated device verification wizard (W1N-180/#32). + v0.2.11: `receive_mac_event` no longer overrides canceled state (W1N-179/#31). +- v0.2.9: restore SAS emoji rendering after vodozemac migration (W1N-175/#29). + v0.2.8: SAS commitment unpadded base64 for Element interop (W1N-174/#28). + v0.2.7: SAS cancel code/reason logging. v0.2.6: verification state logging + + request→ready bridge. v0.2.4: `_patch_nio_sas_timeout()` + + `_repair_dropped_start()`; `VERIFICATION_TIMEOUT_SECONDS` 600→240. +- Automation `1761188403590`「Matrix 聊天关卫生间灯」: trigger + `matrix_e2ee_command` (command `关卫生间灯`), actions `light.turn_off` + + `matrix_e2ee.send_message` (room `!gidvAzpDzwtzfEDrqu`). - **SAS not yet completed:** every device requires explicit `confirm_verification`. Encrypted-room commands stay fail-closed until `@zhiqiang`'s device is verified. - Two paths available: SAS manual confirm or fingerprint, see + Three paths available: SAS manual confirm, fingerprint, or the v0.3.0 + bot-initiated device verification wizard (W1N-180), see [docs/home-assistant-matrix.md § Device verification](../docs/home-assistant-matrix.md). ## Known issues diff --git a/runbooks/matrix-e2e-update.md b/runbooks/matrix-e2e-update.md deleted file mode 100644 index a61f941..0000000 --- a/runbooks/matrix-e2e-update.md +++ /dev/null @@ -1,126 +0,0 @@ -# matrix_e2e update (hass.windy.lan) - -Update the custom **`matrix_e2e`** integration on `hass.windy.lan`. - -Facts: [hosts/hass.windy.lan.md](../hosts/hass.windy.lan.md) (see § `matrix_e2e` live tree) -Source: `/home/windy/project/ha-matrix-e2e` (development clone on workstation) -Access: `ssh -o BatchMode=yes hassio@hass.windy.lan 'sudo -n -i '` - -## Prerequisites - -- The source repo at `/home/windy/project/ha-matrix-e2e` is on the correct tag/commit. -- The tag exists: `git tag -l 'v*'` shows the target version. -- The remote host is reachable and `sudo -n -i ha core info` succeeds. -- The workstation HTTP proxy does not interfere — LAN hosts must be reachable - without proxying (unset `http_proxy` / `HTTP_PROXY` if needed). - -## Update procedure - -### 1. Backup the live tree - -```bash -ssh -o BatchMode=yes hassio@hass.windy.lan \ - 'sudo -n -i mkdir -p /homeassistant/.matrix-e2e-backups && - sudo -n -i cp -a /homeassistant/custom_components/matrix_e2e \ - /homeassistant/.matrix-e2e-backups/matrix_e2e.bak-$(date +%Y%m%d)-v' -``` - -The backup lives in `/homeassistant/.matrix-e2e-backups/` — a directory -separated from `custom_components/` to avoid HA scanning it as a custom -component domain. - -### 2. Rsync the new source - -```bash -rsync -a --delete -e 'ssh -o BatchMode=yes' \ - /home/windy/project/ha-matrix-e2e/custom_components/matrix_e2e/ \ - hassio@hass.windy.lan:/homeassistant/custom_components/matrix_e2e/ -``` - -The `--delete` cannot remove Core-owned `__pycache__` — that is handled -in the next step. Source `.py` files and `manifest.json` are transferred -correctly even with the `__pycache__` errors. - -### 3. Wipe `__pycache__` (as root) and restart Core - -```bash -ssh -o BatchMode=yes hassio@hass.windy.lan \ - 'sudo -n -i rm -rf /homeassistant/custom_components/matrix_e2e/__pycache__ \ - /homeassistant/custom_components/matrix_e2e/*/__pycache__ && - sudo -n -i ha core restart' -``` - -Stale `cpython-314` bytecode in Core-owned `__pycache__` keeps the old -coordinator in memory until restart. Wipe before restart. - -Wait for `Command completed successfully.` (typically 1–2 min). - -### 4. Verify the deployment - -#### 4a. Confirm manifest version - -```bash -ssh -o BatchMode=yes hassio@hass.windy.lan \ - 'sudo -n -i cat /homeassistant/custom_components/matrix_e2e/manifest.json' -``` - -Expect `"version": ""`. - -#### 4b. Check Core logs for matrix_e2e - -```bash -ssh -o BatchMode=yes hassio@hass.windy.lan \ - 'sudo -n -i ha core logs -n 2500' | grep -E 'matrix_e2e|Setting up matrix' | head -20 -``` - -Expect: -- `Setting up matrix_e2e` -- `matrix_e2e restored existing device; user=@hass:chans.xyz device=rO1R915ncu` -- No `ERROR` level messages from `custom_components.matrix_e2e` -- The `_patch_nio_sas_timeout` WARNING is expected (known nio workaround) - -> The code domain is `matrix_e2e` (E2EE = End-to-End Encryption), so log -> entries and file paths use `matrix_e2e`. The integration's short name is -> `matrix_e2e`. - -#### 4c. Verify the entry is loaded (optional, via Supervisor API) - -```bash -ssh -o BatchMode=yes hassio@hass.windy.lan \ - 'sudo -n -i python3 -c " -import os, json, urllib.request -req = urllib.request.Request( - \"http://supervisor/core/api/config/config_entries/entry/\", - headers={\"Authorization\": \"Bearer \" + os.environ[\"SUPERVISOR_TOKEN\"]}, -) -entries = json.loads(urllib.request.urlopen(req, timeout=30).read()) -for e in entries: - if e[\"domain\"] == \"matrix_e2e\": - print(f\"{e[\"domain\"]}: state={e[\"state\"]} source={e[\"source\"]}\") -"' -``` - -Expect `state: loaded`. - -## Rollback - -If Core fails to start after the update: - -```bash -# Restore the backup -ssh -o BatchMode=yes hassio@hass.windy.lan \ - 'sudo -n -i rm -rf /homeassistant/custom_components/matrix_e2e && - sudo -n -i cp -a /homeassistant/.matrix-e2e-backups/matrix_e2e.bak--v \ - /homeassistant/custom_components/matrix_e2e && - sudo -n -i rm -rf /homeassistant/custom_components/matrix_e2e/__pycache__ && - sudo -n -i ha core restart' -``` - -If a full HA backup exists (pre-update), restore via `ha backups restore `. - -## References - -- [hosts/hass.windy.lan.md](../hosts/hass.windy.lan.md) — current live version and config -- [docs/home-assistant-matrix.md](../docs/home-assistant-matrix.md) — integration architecture and verification model -- [home-assistant-maintenance.md](home-assistant-maintenance.md) — general HA maintenance procedures -- [ha-matrix-e2e source](https://github.com/windyboy/ha-matrix-e2e) — GitHub repo \ No newline at end of file diff --git a/runbooks/matrix-e2ee-update.md b/runbooks/matrix-e2ee-update.md new file mode 100644 index 0000000..391fa81 --- /dev/null +++ b/runbooks/matrix-e2ee-update.md @@ -0,0 +1,156 @@ +# matrix_e2ee update (hass.windy.lan) + +Update the custom **`matrix_e2ee`** integration on `hass.windy.lan`. + +Facts: [hosts/hass.windy.lan.md](../hosts/hass.windy.lan.md) (see § `matrix_e2ee` live tree) +Source: `/home/windy/project/ha-matrix-e2ee` (development clone on workstation) +Access: `ssh -o BatchMode=yes hassio@hass.windy.lan 'sudo -n -i '` + +> **SSH config gotcha (verified 2026-08-18):** on the WSL client the systemwide +> `/etc/ssh/ssh_config.d/20-systemd-ssh-proxy.conf` can be owned by `nobody` +> (e.g. under the agent sandbox), making plain `ssh` fail with +> `Bad owner or permissions on /etc/ssh/ssh_config.d/20-systemd-ssh-proxy.conf`. +> Workaround: add `-F /dev/null` to every ssh/rsync command below (still uses +> the default key and `~/.ssh/known_hosts`). If your plain `ssh hassio@...` +> works, you can drop it. + +> Domain is **`matrix_e2ee`** (double-e). Older notes may say `matrix_e2e`; +> paths, events, and services all use `matrix_e2ee`. + +## Prerequisites + +- The source repo at `/home/windy/project/ha-matrix-e2ee` is on the **target + state**: either a release tag (`git tag -l 'v*'`) or a commit whose + `manifest.json` `version` is the target. Note v0.3.0 was deployed from an + **untagged** `main` HEAD (`216cc99`), so the tag check alone is not enough — + confirm the working-tree `custom_components/matrix_e2ee/manifest.json`. +- The working tree matches HEAD: `git status --short` clean (only ignorables) + and `git diff HEAD -- custom_components/` empty. Record + `git rev-parse HEAD` for the docs/Linear record — HEAD can move during a + session, so re-check right before rsync (verified 2026-08-18: HEAD moved + from a `w1n-180` branch merge to `main` mid-deploy). +- The remote host is reachable and `sudo -n -i ha core info` succeeds. +- The workstation HTTP proxy does not interfere — LAN hosts must be reachable + without proxying (unset `http_proxy` / `HTTP_PROXY` if needed). + +## Update procedure + +### 1. Backup the live tree + +```bash +ssh -o BatchMode=yes hassio@hass.windy.lan \ + 'sudo -n -i mkdir -p /homeassistant/.matrix-e2ee-backups && + sudo -n -i cp -a /homeassistant/custom_components/matrix_e2ee \ + /homeassistant/.matrix-e2ee-backups/matrix_e2ee.bak-$(date +%Y%m%d)-v' +``` + +The backup lives in `/homeassistant/.matrix-e2ee-backups/` — a directory +separated from `custom_components/` to avoid HA scanning it as a custom +component domain. + +### 2. Rsync the new source + +```bash +rsync -a --delete -e 'ssh -o BatchMode=yes' \ + /home/windy/project/ha-matrix-e2ee/custom_components/matrix_e2ee/ \ + hassio@hass.windy.lan:/homeassistant/custom_components/matrix_e2ee/ +``` + +The `--delete` cannot remove Core-owned `__pycache__` — that is handled +in the next step. Source `.py` files and `manifest.json` are transferred +correctly even with the `__pycache__` errors, but **rsync exits with code 23 +(`some files/attrs were not transferred`)** — that is expected, not a failure. +Confirm the transfer by checking the manifest on the host before restarting. + +### 3. Wipe `__pycache__` (as root) and restart Core + +Quote the nested `__pycache__` glob — remote login shell is zsh and will +fail with `no matches found` if left unquoted. + +```bash +ssh -o BatchMode=yes hassio@hass.windy.lan \ + "sudo -n -i rm -rf /homeassistant/custom_components/matrix_e2ee/__pycache__ \ + '/homeassistant/custom_components/matrix_e2ee/*/__pycache__' && + sudo -n -i ha core restart" +``` + +Stale `cpython-314` bytecode in Core-owned `__pycache__` keeps the old +coordinator in memory until restart. Wipe before restart. + +Wait for `Command completed successfully.` (typically 1–2 min). + +### 4. Verify the deployment + +#### 4a. Confirm manifest version + +```bash +ssh -o BatchMode=yes hassio@hass.windy.lan \ + 'sudo -n -i cat /homeassistant/custom_components/matrix_e2ee/manifest.json' +``` + +Expect `"version": ""`. + +#### 4b. Check Core logs for matrix_e2ee + +```bash +ssh -o BatchMode=yes hassio@hass.windy.lan \ + 'sudo -n -i ha core logs -n 2500' | grep -E 'matrix_e2ee|Setting up matrix' | head -20 +``` + +Expect: +- `Setup of domain matrix_e2ee took ...` (older wording `Setting up matrix_e2ee` may appear) +- `matrix_e2ee restored existing device; user=@hass:chans.xyz device=rO1R915ncu` +- No `ERROR` level messages from `custom_components.matrix_e2ee` +- Blocking-call WARNINGs from `_patch_nio_sas_timeout` / nio store I/O are expected + +#### 4c. Verify the entry is loaded (optional, via Supervisor API) + +No trailing slash on the entries URL (trailing `/` returns 404 on Core 2026.8.1). + +```bash +ssh -o BatchMode=yes hassio@hass.windy.lan \ + "sudo -n -i python3 - <<'PY' +import os, json, urllib.request +req = urllib.request.Request( + 'http://supervisor/core/api/config/config_entries/entry', + headers={'Authorization': 'Bearer ' + os.environ['SUPERVISOR_TOKEN']}, +) +entries = json.loads(urllib.request.urlopen(req, timeout=30).read()) +for e in entries: + if e['domain'] == 'matrix_e2ee': + print(f\"{e['domain']}: state={e['state']} source={e['source']}\") +PY" +``` + +Expect `state: loaded`. + +### 5. Record the deployment + +- Update the `matrix_e2ee` live-tree section in + [hosts/hass.windy.lan.md](../hosts/hass.windy.lan.md): new version, source + commit (`git rev-parse HEAD`), backup name, and any new feature notes. +- Record the operation in the Linear `vps` project (scope, action, + verification, follow-up); see [docs/agents/issue-tracker.md](../docs/agents/issue-tracker.md). + +## Rollback + +If Core fails to start after the update: + +```bash +# Restore the backup +ssh -o BatchMode=yes hassio@hass.windy.lan \ + 'sudo -n -i rm -rf /homeassistant/custom_components/matrix_e2ee && + sudo -n -i cp -a /homeassistant/.matrix-e2ee-backups/matrix_e2ee.bak--v \ + /homeassistant/custom_components/matrix_e2ee && + sudo -n -i rm -rf /homeassistant/custom_components/matrix_e2ee/__pycache__ && + sudo -n -i ha core restart' +``` + +If a full HA backup exists (pre-update), restore via `ha backups restore `. + +## References + +- [hosts/hass.windy.lan.md](../hosts/hass.windy.lan.md) — current live version and config +- [docs/home-assistant-matrix.md](../docs/home-assistant-matrix.md) — integration architecture and verification model +- [home-assistant-maintenance.md](home-assistant-maintenance.md) — general HA maintenance procedures +- [ha-matrix-e2ee source](https://github.com/windyboy/ha-matrix-e2ee) — GitHub repo