diff --git a/README.md b/README.md index c829bce..bd6b4c5 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,21 @@ # indigo-matter -Matter device support for the [Indigo](https://www.indigodomo.com) home automation server. - -Add Matter devices from the **Domio** iOS app or the plugin's own menu; control them as -first-class Indigo devices (triggers, schedules, action groups, control pages, API). The -plugin manages a [`matter-server`](https://github.com/matter-js/matterjs-server) instance, -holds the Indigo-owned Matter fabric, translates Matter clusters ↔ Indigo device types, -and owns runtime control for each device's lifetime. +Matter device support for the [Indigo](https://www.indigodomo.com) home automation server — +**in both directions**. + +**Matter in (the controller).** Add Matter devices from the **Domio** iOS app or the +plugin's own menu; control them as first-class Indigo devices (triggers, schedules, action +groups, control pages, API). The plugin manages a +[`matter-server`](https://github.com/matter-js/matterjs-server) instance, holds the +Indigo-owned Matter fabric, translates Matter clusters ↔ Indigo device types, and owns +runtime control for each device's lifetime. + +**Matter out (the bridge).** Publish selected *Indigo* devices — Z-Wave, Insteon, Zigbee, +MQTT, anything with a device record — outward as Matter accessories, so Apple Home can see +and control them locally. Opt-in per device, nothing exported by default, from a second +managed process. Apple Home is the ecosystem this is built and documented for; Alexa, +Google Home and SmartThings are untested and unclaimed. See +[Exporting Indigo devices](#exporting-indigo-devices-matter-out). > **This is an independent, uncertified implementation.** It is not certified by the > Connectivity Standards Alliance (CSA), and is not affiliated with or endorsed by them. @@ -17,7 +26,9 @@ and owns runtime control for each device's lifetime. ```text Domio (iOS) ─┐ ├── setup code ──▶ indigo-matter ──WebSocket──▶ matter-server ──IP──▶ Wi-Fi + Thread devices -plugin menu ─┘ +plugin menu ─┘ │ + └──WebSocket──▶ bridge node ──Matter──▶ Apple Home + (export, opt-in) ``` ## Adding devices @@ -34,9 +45,39 @@ device reaches one of them: - **Already on your network, never commissioned** — its printed code works directly, from either entry point. +## Exporting Indigo devices (Matter out) + +The plugin can also wear the Matter **device** role: a set of Indigo devices you choose is +published as accessories on a Matter bridge that Apple Home commissions like any other +accessory. One bridge, one pairing, however many devices. + +- **Opt-in per device, default empty.** A fresh install exports nothing, pairs nothing and + runs no bridge process. Exporting is publishing — the device becomes operable from every + ecosystem the bridge is paired with — so it is always a deliberate act. +- **You declare what each device is.** Indigo cannot tell a plug from a lamp from a lock, + so the export dialog asks, and defaults to the safest reading. Relays, dimmers, colour + and colour-temperature lights, window coverings, locks, the seven sensor types and + thermostats are supported; valves, fans, garage doors, sprinklers and MultiIO are not, + and appear in the picker **with the reason**. +- **Expect an "uncertified accessory" prompt** and choose *Add Anyway*. The bridge presents + a development attestation certificate — as Homebridge, matterbridge and Home Assistant's + bridge all do — and outbound attestation is the commissioning ecosystem's policy, not + something this plugin can change. +- **Apple Home only, as a claim.** Alexa, Google Home and SmartThings are untested and + unclaimed; nothing here says they will or won't work. +- **A second npm package** (`indigo-matter-bridge`), installed from its own menu item. + Note it is **not on the npm registry yet**, so export cannot be brought up until it is. + +Setup, pairing, recovery and troubleshooting: +[full install guide](https://simons-plugins.github.io/indigo-matter/INSTALL.html). How it +works and what it can represent: +[MATTER.md → Indigo as a Matter bridge](docs/MATTER.md#indigo-as-a-matter-bridge--the-other-direction). +(The typeset [Field Notes № 1](https://simons-plugins.github.io/indigo-matter/matter.html) +page predates the export half and does not cover it yet.) + ## Status -**Built and live-validated, including with real hardware.** The full device-class +**Matter in — built and live-validated, including with real hardware.** The full device-class catalogue is shipped — lighting (on/off/dimmer/colour), sensors, thermostats, fans, window coverings, locks, valves, buttons, smoke/CO, air quality, energy metering, battery levels, and bridges — plus failure hardening, fabric backup/restore, and a @@ -52,6 +93,22 @@ during the Domio flow); the plugin then joins over IP via that ecosystem's borde fit; validation specifics live in [Handover](https://simons-plugins.github.io/indigo-matter/HANDOVER.html). +**Matter out — built, unit-tested on both sides, partly validated live.** The allow-list, +the role mapping, endpoint persistence, pairing and fabric management, and the managed +LaunchAgent are all in place, and the bridge node's own suite stands up a real Matter stack. +On live hardware the bridge **has** been commissioned into Apple Home (uncertified prompt +and all), an on/off light and a dimmer **have** been controlled in both directions, and +accessory identity **has** survived an upgrade without duplicating (2026-08-04 / 05, on +the reference server). Still outstanding: pairing through the plugin's own menu rather +than the node's console, a second controller alongside Apple Home, the reboot leg of the +identity check, and the managed LaunchAgent end to end — that last one needs the package +installed, which until `indigo-matter-bridge` is published means the local-install +workaround rather than the shipped install menu. Treat the export half as new. See +[MATTER.md](docs/MATTER.md#indigo-as-a-matter-bridge--the-other-direction) for how it +works, and +[Handover](https://simons-plugins.github.io/indigo-matter/HANDOVER.html) for the +validation specifics. + ## Setup The plugin doesn't speak Matter directly — it drives **matter-server**, a separate @@ -86,12 +143,19 @@ reconciled N Matter node(s) manual mode, fabric backups, upgrading and uninstalling, is covered in the [full install guide](https://simons-plugins.github.io/indigo-matter/INSTALL.html). +To **export** Indigo devices as well, add a fourth step — **Plugins ▸ Matter ▸ +Install/update the Matter export bridge** — and then pick your devices in *Manage Matter +Exports…*. The bridge starts itself once something is exported. Full walkthrough in the +install guide. + ## Requirements - Indigo 2025.2+ on macOS. - **Node.js ≥ 22.13.0** (Homebrew is the paved road; nvm works too) and the `matter-server` npm package — Beta, **exact-pinned to 1.2.2** (`DEFAULT_INSTALL_SPEC`; the Install action uses this). +- For export only: a second npm package, `indigo-matter-bridge`, exact-pinned and installed + by its own menu item. Not yet on the npm registry. - Python dep: `websockets` (see `Contents/Server Plugin/requirements.txt`). - The **Domio** iOS app for adding devices — optional: the plugin menu's *Commission device by setup code…* does the same job from Indigo. @@ -122,7 +186,10 @@ Developer reference, in the repo — not intended as user documentation: - [`docs/IMPLEMENTATION.md`](https://github.com/simons-plugins/indigo-matter/blob/main/docs/IMPLEMENTATION.md) — protocols, scaffold, cluster handlers. - [`docs/API.md`](https://github.com/simons-plugins/indigo-matter/blob/main/docs/API.md) — the Domio ↔ plugin HTTP contract (v1.3). -- [`docs/PRD-indigo-matter-plugin.md`](https://github.com/simons-plugins/indigo-matter/blob/main/docs/PRD-indigo-matter-plugin.md) — product requirements and milestones. +- [`docs/BRIDGE_PROTOCOL.md`](https://github.com/simons-plugins/indigo-matter/blob/main/docs/BRIDGE_PROTOCOL.md) — the plugin ⇄ bridge-node local protocol (export side). +- [`docs/PRD-indigo-matter-plugin.md`](https://github.com/simons-plugins/indigo-matter/blob/main/docs/PRD-indigo-matter-plugin.md) — product requirements and milestones (inbound). +- [`docs/PRD-indigo-matter-export.md`](https://github.com/simons-plugins/indigo-matter/blob/main/docs/PRD-indigo-matter-export.md) — the same for the export bridge. +- [`bridge-node/`](https://github.com/simons-plugins/indigo-matter/tree/main/bridge-node) — the bridge node itself (TypeScript; the only place matter.js is imported). `npm run build && npm test`. - [`CLAUDE.md`](https://github.com/simons-plugins/indigo-matter/blob/main/CLAUDE.md) — architecture and workspace conventions. ## Trademarks and certification @@ -150,6 +217,12 @@ What that means in practice: attestation so uncertified devices (development boards, Homebridge/Matterbridge bridges) can be commissioned. It is off by default, warns on every start, and should be turned off once the device is paired. +- **The export bridge is uncertified in the other direction, and visibly so.** It + advertises with the specification's test vendor ID, so every ecosystem shows an + "uncertified accessory" warning when you add it and you choose *Add Anyway*. That is the + normal state for this class of software — Homebridge, matterbridge and Home Assistant's + bridge all ship the same way — and it is not something the plugin can change: outbound, + the trust policy belongs to the ecosystem doing the commissioning. The icon bundles the Matter symbol from [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Logo_of_Matter_connectivity_standard.svg), @@ -159,4 +232,3 @@ which is public domain for copyright purposes. See ## License MIT — see [LICENSE](https://github.com/simons-plugins/indigo-matter/blob/main/LICENSE). - diff --git a/bridge-node/README.md b/bridge-node/README.md index 839581f..489027c 100644 --- a/bridge-node/README.md +++ b/bridge-node/README.md @@ -4,8 +4,9 @@ The Matter **bridge node** for the [indigo-matter](https://github.com/simons-plugins/indigo-matter) Indigo plugin. It exports selected [Indigo](https://www.indigodomo.com) devices as Matter -accessories, so Apple Home, Alexa, Google Home and SmartThings see them as -ordinary Matter devices on a bridge. +accessories, so an ecosystem sees them as ordinary Matter devices on a bridge. +**Apple Home** is the ecosystem this is built and documented for; Alexa, Google +Home and SmartThings are untested and unclaimed. > **This package is not useful on its own.** It has no configuration of its own, > no discovery, and no idea what an Indigo device is. It is one half of a pair: diff --git a/docs/HANDOVER.md b/docs/HANDOVER.md index 84ba62b..e7fae05 100644 --- a/docs/HANDOVER.md +++ b/docs/HANDOVER.md @@ -1,14 +1,15 @@ # indigo-matter — Build Handover -**Last updated:** 2026-08-05 23:21 UTC -**Active work:** `feat/e6-e7-pairing-and-agent` — E6 + E7, the last functional -export milestones (E8 is docs only). See the section immediately below; the -`main` summary in this header describes the last merge, not that branch. +**Last updated:** 2026-08-05 23:58 UTC +**Active work:** `docs/e8-export-documentation` (PR #129, stacked on +`feat/e6-e7-pairing-and-agent` / PR #128) — E8, docs only. See the E6+E7 section +immediately below; the `main` summary in this header describes the last merge, +not those branches. **Branch:** `main` — PRs #106, #107, #108 all merged. **Version:** `2026.7.13` **Tests:** 1005 passing (`cd indigo-matter && /Library/Frameworks/Python.framework/Versions/Current/bin/python3 -m pytest -q`) -**Deployed:** jarvis is **unchanged** — still running the #103 code (v2026.7.10 era). **Nothing from #106/#107/#108 has been deployed**, so the #104 supervision fixes are NOT yet live on jarvis. -**Status:** **Wi-Fi AND Thread validated with real hardware.** #104's three server-supervision faults are fixed and merged (#107). #105's diagnostic is merged (#108) but **#105 stays open** — the bridged-endpoint path still has no real-bridge validation. `domio-code` #236 is fixed and closed (domio-code PR #237). Known-open: #105, plus the older #43, #46, #21–#24. +**Deployed:** jarvis is running the #103-era plugin (v2026.7.10) plus **E5's export half, deployed 2026-08-05 with 3 accessories live in Apple Home**. **Nothing from #106/#107/#108, and no part of E6 or E7, has been deployed** — so the #104 supervision fixes are NOT yet live on jarvis. +**Status:** **Wi-Fi AND Thread validated with real hardware**, and the **export half's E0 pairing gate PASSED on 2026-08-04** with live two-way control on 2026-08-05 (see *Live validation on jarvis*). #104's three server-supervision faults are fixed and merged (#107). #105's diagnostic is merged (#108) but **#105 stays open** — the bridged-endpoint path still has no real-bridge validation. `domio-code` #236 is fixed and closed (domio-code PR #237). Known-open: #105, plus the older #43, #46, #21–#24. --- @@ -396,10 +397,55 @@ exactly where `LaunchAgent._server_entry()` looks for it. `npm link` works too. Do this and everything in E7 runs unchanged; just remember the plugin's install menu will still try the registry spec. +**So the managed LaunchAgent is not, strictly, blocked on `npm publish`.** It is +blocked on the package being *installed*, and the local-install recipe above +installs it. What the publish unblocks is the shipped route — the +"Install/update the Matter export bridge" menu item — which is what a user would +use and is therefore what "end to end" means. Do not restate the blocker as +"npm" when the distinction matters. + +### Live validation on jarvis — what has actually happened + +Recorded here because README, MATTER.md and INSTALL.md all point at this file +for the specifics. Dates are the observation dates, not the write-up dates. + +**E0's gate: PASSED (2026-08-04).** An uncertified bridge was commissioned into +a real Apple Home and the uncertified-accessory prompt was accepted. This is the +question that could have killed the feature outright — *will any ecosystem pair +an uncertified test-VID bridge?* — and the answer is yes. Note the pairing was +driven from the **bridge node's own console**, before the plugin's pairing menu +existed; see the outstanding list below. + +**E3's live E2E: PASSED (2026-08-05).** An on/off light and a dimmer, exported +through the **Manage Matter Exports…** dialog, controlled from Apple Home and +from Indigo, each direction showing up on the other side. That is XAC4 in both +directions and XAC3's Apple Home control. + +**E5 deployed to jarvis (2026-08-05), and the upgrade migration observed.** +Three exported accessories kept endpoint numbers **3, 5 and 4** across a plugin +and bridge-node upgrade — their pre-upgrade values, and deliberately *not* in +creation order, which is what makes the observation worth anything. No duplicates +appeared in Apple Home. That is XAC5's upgrade leg. + +**Still outstanding, and it must stay marked so:** + +- **Pairing through the plugin's own menu.** E0's pairing was driven from the + node's console. *Pair Matter Bridge…* → the IWS code page → Apple Home has not + been walked end to end. +- **The second-admin / multi-fabric proof** — XAC3's second half. No second + controller has been added alongside Apple Home. +- **XAC5's reboot leg.** A plugin reload and a bridge-node restart have both been + survived; a full Mac reboot has not. +- **E6 and E7 end to end**, which need the install menu and therefore the npm + publish (or the local-install recipe above for a dry run). + ### Deferred / not done -- **XAC3's live pairing is unverified.** Every code path is unit-tested, but - nothing here has met a real ecosystem. That is the jarvis script below. +- **XAC3 is PART-verified — do not read this list as "nothing has been + validated".** The pairing gate and live two-way control have both been done on + jarvis (see the section immediately above); what is unverified is pairing + driven from the *plugin's own menu*, and the second-admin half. The jarvis + script below is still the route for those. - **A locally-opened commissioning window still does not update the `AdministratorCommissioning` cluster's `windowStatus`/`adminFabricIndex`.** Unchanged from E5: matter.js 0.17.8's cluster command asserts a remote @@ -413,13 +459,21 @@ menu will still try the registry spec. gives the stop/start seam the E5 note said was missing) but is **not wired**: `fabric_backup.restore_backup` still reports and skips the `bridge-node/` members. Wiring it is a small, self-contained follow-up. +- **The published Field Notes site does not know export exists.** + `docs/matter.html` is a hand-built page generated from an earlier `MATTER.md` + and carries **zero** export content, and `docs/index.html`'s standfirst still + offers "two long reads" with no route to the export material or to + `INSTALL.md`. E8 therefore points README's export lede at `docs/MATTER.md` + directly rather than at `matter.html`. **Regenerating both pages is + outstanding**, and until it is done the GitHub Pages site under-sells the + feature to anyone who starts there. - **Still open from earlier:** #105, #83, #84, #62 follow-up, #43, #46, #21–#24. ### Open, unexplained, and Apple-side: room changes in Apple Home -Observed on jarvis with E5 deployed and 3 accessories live: on/off control works, -but Apple Home refuses to change an accessory's **room** ("can't change -settings"). Simon had changed those same accessories' rooms successfully before, +Observed on jarvis with E5 deployed and 3 accessories live (2026-08-05): on/off +control works, but Apple Home refuses to change an accessory's **room** ("can't +change settings"). Simon had changed those same accessories' rooms successfully before, so this is a change in Apple-side state, not in bridge behaviour. **No bridge-side change can address this, and the reasoning matters.** Room @@ -435,8 +489,9 @@ presented as a remedy for it. ### Deploying E6 + E7 to jarvis -Nothing in E5, E6 or E7 has been deployed. In order, because each step gates -the next: +**E5 is deployed** (2026-08-05, with three accessories live — see *Live +validation on jarvis* above). **E6 and E7 are not.** In order, because each step +gates the next: 1. **Publish `indigo-matter-bridge@0.5.0`** (above). Without it step 4 fails. For a dry run, use the local-install workaround instead. diff --git a/docs/INSTALL.md b/docs/INSTALL.md index d2681ec..683b24f 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -1,13 +1,53 @@ -# Installing matter-server for indigo-matter +# Installing indigo-matter's two Node runtimes -This guide walks through installing the **matter-server** runtime that the -`indigo-matter` plugin drives, and configuring the plugin to connect to it. It is -derived from the live managed-LaunchAgent bring-up on the reference Indigo server. +The plugin has two halves, and each one is a **separate Node process with its own +npm package**. This guide covers both: + +- **matter-server** — the inbound controller. The plugin drives it so that Matter + devices in your house become Indigo devices. **Steps 1–4**, and this is what a + normal install needs. These steps are derived from the live + managed-LaunchAgent bring-up on the reference Indigo server. +- **the Matter export bridge** — the optional outbound half, which publishes + selected *Indigo* devices to Apple Home as Matter accessories. **Steps E1–E4**. + **It cannot be installed today**: its npm package has not been published — see + [Exporting Indigo devices](#exporting-indigo-devices-indigo-as-a-matter-bridge). If you just installed the plugin and Indigo is logging `Connect call failed ('127.0.0.1', 5580)`, you are in the right place — the plugin is running but matter-server is not. +## Contents + +- [Overview](#overview) +- [Prerequisites](#prerequisites) +- [Step 1 — Install Node.js 22](#step-1--install-nodejs-22) +- [Step 2 — Install matter-server](#step-2--install-matter-server) +- [Step 3 — Configure the plugin](#step-3--configure-the-plugin) +- [Step 4 — Verify](#step-4--verify) +- [**Exporting Indigo devices (Indigo as a Matter bridge)**](#exporting-indigo-devices-indigo-as-a-matter-bridge) + - [Export prerequisites](#export-prerequisites) + - [Step E1 — Install the export bridge](#step-e1--install-the-export-bridge) + - [Step E2 — Export your first device](#step-e2--export-your-first-device) + - [Step E3 — Open a pairing window](#step-e3--open-a-pairing-window) + - [Step E4 — Add the bridge in Apple Home](#step-e4--add-the-bridge-in-apple-home-and-expect-the-uncertified-prompt) + - [The export settings in Configure…](#the-export-settings-in-configure) + - [Removing an export, unpairing, and stopping the bridge](#removing-an-export-unpairing-and-stopping-the-bridge) + - [The two destructive recovery actions](#the-two-destructive-recovery-actions) +- [Backups](#backups) — and, for exporters, + [back up the bridge's storage too](#if-you-export-devices-back-up-the-bridges-storage-too) +- [Security](#security) — and, before exporting, + [before you pair the export bridge](#before-you-pair-the-export-bridge) +- [Troubleshooting](#troubleshooting) — + [the controller](#the-controller-matter-server) · + [the export bridge](#the-export-bridge) +- [Upgrading matter-server](#upgrading-matter-server) +- [Uninstall](#uninstall) + +> Export material deliberately appears in four places: the walkthrough below, +> plus its own subsection under Backups, under Security, and under +> Troubleshooting. The links above are the shortcuts to the three that are not +> under the export heading. + --- ## Overview @@ -15,10 +55,17 @@ running but matter-server is not. The `indigo-matter` plugin does not speak Matter directly. It manages a separate Node.js process — **matter-server** (npm package [`matter-server`](https://github.com/matter-js/matterjs-server), currently **Beta -1.2.2**) — which holds the Indigo-owned Matter fabric and exposes a WebSocket control +1.2.2**) — which holds the Indigo-owned Matter **fabric** and exposes a WebSocket control API. The plugin connects to that WebSocket and translates Matter clusters into first-class Indigo devices. +> **Fabric** is the word that recurs throughout this guide. A fabric is one +> controller's trust domain: the set of cryptographic credentials that controller +> installs on a device. A Matter device can belong to several at once — that is +> what lets Apple Home and Indigo both control the same plug, independently. Full +> explanation in +> [MATTER.md → Fabrics and multi-admin](./MATTER.md#fabrics-and-multi-admin-one-device-many-controllers). + A few things to know before you start: - **Wi-Fi and Thread devices both work.** What this plugin can't do is *first-admin* @@ -40,6 +87,13 @@ A few things to know before you start: However it was added, the plugin owns runtime control for the device's lifetime. - **matter-server is Beta software.** Pin the version, back up the fabric, and expect occasional rough edges. +- **There is a second, optional half.** The plugin can also work the other way round and + publish selected *Indigo* devices to Apple Home as Matter accessories, from a separate + bridge process with its own npm package. **That package is not on the npm registry yet, + so this half cannot be installed today.** When it is: nothing is exported and no bridge + process runs until you add a device to the export list, and it needs Node (Step 1) and + a storage path (Step 3) — *not* a working matter-server. See + [Exporting Indigo devices](#exporting-indigo-devices-indigo-as-a-matter-bridge). --- @@ -225,6 +279,284 @@ Commission device by setup code…** — and it will appear as a native Indigo d --- +## Exporting Indigo devices (Indigo as a Matter bridge) + +> **You cannot install this yet.** The export bridge needs a second npm package, +> `indigo-matter-bridge`, and it **has not been published to the npm registry**. +> Until it is, **Install/update the Matter export bridge** cannot resolve the +> version the plugin pins, and the install fails. Nothing else in the plugin is +> affected — the inbound controller, your commissioned devices and every Indigo +> device carry on as normal — but export cannot be brought up. Everything below +> is what you are waiting for. + +Everything above is the plugin's **inbound** half: Matter devices becoming Indigo +devices. The plugin also runs the opposite direction — a selected set of your +*existing* Indigo devices (Z-Wave, Insteon, Zigbee, MQTT, anything a plugin owns) +published outward as Matter accessories on a bridge, so Apple Home can see and +control them. No cloud, no second bridge app. + +**This adds a second Node process, and almost everything doubles.** The export +bridge is not matter-server doing extra work. It is its own npm package, its own +launchd job, its own storage directory, its own log files, its own two ports and +its own menu items. One can be broken while the other works, and an export +failure never touches the Matter devices Indigo controls. Wherever this guide +names a log, a port, a backup or a LaunchAgent from here on, check which of the +two it belongs to. + +Export is opt-in per device: **nothing is exported and no bridge process runs +until you add a device** to the list in *Manage Matter Exports…*. (The +**Enable Matter export** tickbox in Configure… is on by default. It is a master +*off*-switch, not the thing that starts anything — an empty export list is what +keeps a fresh install inert.) See [MATTER.md](./MATTER.md) → *Indigo as a Matter +bridge* for how it works and what it can and cannot represent; this section is +the setup. + +Three things to know before you start: + +- **Some of this has been proven on real hardware, and some has not.** What has: + the bridge **has** been commissioned into Apple Home and the uncertified prompt + accepted; an on/off light and a dimmer **have** been controlled from Apple Home + and from Indigo, each direction showing up on the other side; and three + exported accessories **kept their identities** across a plugin and bridge-node + upgrade, with no duplicates in Apple Home. What has not: pairing driven from + the plugin's own menu (the pairing above was driven from the bridge node's + console), a second ecosystem alongside Apple Home, a full Mac reboot, and the + managed LaunchAgent end to end. Treat the export half as new. +- **Apple Home is the ecosystem this is built and documented for.** Alexa, Google + Home and SmartThings are **untested and unclaimed** — the bridge is a standard + multi-admin Matter bridge and there is no reason they should not work, but + nobody here has hardware to try them on, so nothing is promised. If you pair + one, please say so on the forum. +- **The bridge is not certified**, and every ecosystem will say so when you add + it. That is expected, not a fault — see + [Step E4](#step-e4--add-the-bridge-in-apple-home-and-expect-the-uncertified-prompt). + +### Export prerequisites + +- **Node.js ≥ 22.13.0** — the same Node you installed in Step 1. The bridge runs + on it, and the plugin resolves it exactly as it does for matter-server + (auto-detect, or the **Node bin directory** you pinned). +- **A storage path** (Step 3). The bridge's own storage directory is *derived* + from the controller's — it is the `bridge-node/` folder beside it — so the + **matter-server storage path** field decides where the bridge keeps its + ecosystem pairings and its endpoint map, whether or not you run matter-server. +- **A second npm package**, `indigo-matter-bridge`. It is not part of the plugin + bundle — the plugin ships no JavaScript — and it is not the same package as + `matter-server`. It is installed from its own menu item (Step E1), once it is + published. + +What export does **not** need is a working matter-server. The two are separate +processes with separate packages, ports, storage directories and launchd jobs, +and nothing in the export path talks to the controller. So if you only want to +publish Indigo devices outward, Step 2 is not yours — Steps 1 and 3 are. The +plugin will go on reporting that it cannot reach matter-server, which is log +noise in that configuration, not an export fault. + +### Step E1 — Install the export bridge + +**Plugins ▸ Matter ▸ Install/update the Matter export bridge.** + +It installs into the same `~/indigo-matter` npm root as matter-server, with the +same Node, and takes a minute; watch the Event Log. On success you will see: + +``` +Matter export bridge installed. It is NOT being started: nothing is exported yet, +and the bridge only runs while the export list is non-empty. +``` + +That is correct — the bridge process starts itself the moment you export +something, and stops again when you stop exporting. **Do this step before Step +E2**: exporting a device with the package missing writes no LaunchAgent and logs +an error pointing you straight back here. + +### Step E2 — Export your first device + +**Plugins ▸ Matter ▸ Manage Matter Exports…** + +The dialog is a picker plus a detail pane, and it has no Execute button — the +buttons inside it do the work, and **Close** when you are done. + +1. **Filter by name** (optional), then click **Apply filter / refresh**. The + picker lists **up to 300 matching Indigo devices** — past that a tail row says + so, and narrowing the filter is how you reach the rest. A `●` marks one that + is already exported. Devices that cannot be exported are listed too, reading + *"— not exportable: …"* **with the reason**, rather than being quietly + omitted and left to hunt for. The one exception is devices this plugin + created itself: those are absent altogether, because a Matter device is never + re-exported over Matter. +2. **Pick a device.** Its saved settings load, or sensible defaults appear. +3. **Export as** — the role. Indigo cannot tell a plug from a lamp from a lock, + so you declare it; the first option in the menu is always the safest default. + Only roles that device can legitimately take are offered. +4. **Name in ecosystems** (optional) — leave empty to use the Indigo device name. +5. **Invert position** — window coverings only. Matter's convention is 100 % = + fully open; tick this if your Indigo device is the other way round. +6. **Add / update export.** + +The **Status** line reports what happened, and **Currently exported** lists the +allow-list. Behind that one click — on the empty-to-non-empty transition — the +plugin writes the bridge node's LaunchAgent, starts it, and connects. The log +should show: + +``` +Matter export: bridge node LaunchAgent is running (protocol port 5581, Matter port 5540) +Matter export: bridge node attached — 1 endpoint(s) live, not yet paired +``` + + + +> **You only pair the bridge once per ecosystem.** There is one bridge +> accessory, and every export is an endpoint on it — so devices you export +> *later* appear in every already-paired ecosystem automatically, with no action +> from you. Do **not** open a fresh pairing window to add a device: a pairing +> window is a live credential that exposes everything you export to anyone who +> can read it (see [Security](#before-you-pair-the-export-bridge)). You open one +> only to add a *new ecosystem*. + +> **Changing a device's role later re-creates the accessory.** Matter does not +> allow an endpoint to change device type, so the plugin removes it and adds it +> back — and every paired ecosystem treats that as a brand-new accessory, losing +> the name, room, scenes and automations you gave it there. The dialog says so +> when you do it. + +### Step E3 — Open a pairing window + +**Plugins ▸ Matter ▸ Pair Matter Bridge…** → **Open pairing window**. + +Read [Before you pair the export bridge](#before-you-pair-the-export-bridge) +under **Security** first. In short: the pairing code is a live credential, the +page that shows it is only password-protected if you have turned Indigo Web +Server authentication on, and anyone who reaches either can add *your* exported +devices to *their* Apple Home account. + +- The window lasts **180–900 seconds** (3–15 minutes); 900 is the default and the + maximum. A value outside that band is rejected in the dialog rather than + silently changed. +- You must have exported something first. The bridge only runs while the export + list is non-empty, so with nothing exported the dialog answers *"Export at + least one device in 'Manage Matter Exports…' first"*. +- **The code goes to the Event Log**, not into the dialog: Indigo dialogs cannot + display a value their own button computes. The log line carries the manual + pairing code, the raw `MT:` QR payload, the expiry, and the URL of a page that + shows the same things. +- Open that URL on the phone you are about to pair with. The page shows the + manual code large enough to read across a room, the payload for copying, and a + link to the Matter project's own QR viewer for anyone who would rather scan. + **No QR image is generated locally** — that link sends the payload to a + third-party page and needs internet access, which the page says on itself. + +A Matter passcode is **not durable**. Once the first ecosystem commissions the +bridge, that code is dead; every further ecosystem needs a fresh window from this +same menu item. That is why it is called "open a pairing window" and not "show +the code". + + + +### Step E4 — Add the bridge in Apple Home, and expect the uncertified prompt + +In the Home app, add an accessory and take the option to enter a code by hand +(or scan the QR from the page), then type the manual pairing code from the Event +Log. Apple Home finds the accessory as **Indigo Matter Bridge** — and then warns +you that it is an **uncertified accessory**. + +**That warning is expected. Choose "Add Anyway".** + +Read what the warning actually asserts. It does not say the accessory may be +hostile, or that Apple has found anything wrong with it. It says that nobody has +paid the Connectivity Standards Alliance to certify this product — that is the +entire claim. Matter device attestation runs one way, the *commissioner* +checking the *device's* certificate against the CSA's list, so the trust policy +here belongs to Apple, not to Indigo. The bridge presents a development/test +attestation certificate, which is exactly what **Homebridge**, **matterbridge** +and **Home Assistant's** own bridge present, and shipping uncertified is the +normal state for this class of software: a CSA certificate requires paid +membership plus per-product certification, which is not proportionate for a free +community plugin. The plugin can neither suppress the prompt nor work around it; +it is uncertified in *both* directions and says so in the README. + +Once you accept it, every device on your export list appears in Apple Home as an +accessory of the role you declared. + + + +> **Apple takes two fabric slots, not one.** An Apple Home pairing creates an +> *Apple Home* fabric **and** an *Apple Keychain* fabric (iCloud Keychain sync). +> Both will appear in the plugin's readouts and in the unpair picker; that is +> normal, and neither is a duplicate you should remove. + +Re-open **Plugins ▸ Matter ▸ Configure…** afterwards and the **Status** line in +the Matter export section names what you are paired with. + +### The export settings in Configure… + +| Field | Default | Purpose | +|---|---|---| +| **Enable Matter export** | on | The wholesale switch. Unticking it stops the bridge and its accessories go unavailable — it does **not** unpair anything or delete accessories, so ticking it again brings them straight back. Nothing runs until at least one device is exported. This is the one export setting that acts immediately on save. | +| **Status** | *(read-only)* | How many devices are exported, whether the plugin is connected to the bridge node, which ecosystems it last reported being paired with, and whether a pairing window is open. Computed when the dialog opens, from what the plugin already knows — it never blocks on the bridge. | +| **Bridge control port (loopback)** | `5581` | How the plugin talks to the bridge node. Loopback only; nothing outside this Mac can reach it. Change only if something else already uses 5581. | +| **Matter port** | `5540` | The UDP port ecosystems reach the bridge on. 5540 is the Matter default. Move it only if another Matter stack on this Mac already holds it (see Troubleshooting). | + +**Changing either port needs a plugin reload** — they are read when the bridge +node and its LaunchAgent are built. The **Matter port** field says so in its own +description; the **Bridge control port** field does not, but the same is true +of it. + +### Removing an export, unpairing, and stopping the bridge + +| You want to | Do this | What happens | +|---|---|---| +| Stop exporting one device | *Manage Matter Exports…* → pick it → **Remove export** | Its endpoint goes, and the accessory disappears from every paired ecosystem. Your pairings stand. | +| Stop exporting everything | Remove every export | All the endpoints go, the bridge node is stopped and its LaunchAgent removed (so a reboot cannot bring it back), and **your pairings are kept**. Re-export anything and it all comes back. | +| Pause export without unpairing | *Configure…* → untick **Enable Matter export** | The bridge stops and accessories show as unavailable. Nothing is unpaired, nothing is deleted, and the allow-list is untouched. | +| Remove one ecosystem | *Plugins ▸ Matter ▸ Unpair an Ecosystem…* | Every accessory Indigo exports leaves **that** ecosystem, with the names, rooms, scenes and automations you built on them there. Other ecosystems are unaffected — **but if it is your only one, this factory-resets the whole bridge** (see below the table). Two confirmation boxes. | +| Get the bridge process gone right now | *Plugins ▸ Matter ▸ Stop the Matter export bridge…* | Stops the node and removes its LaunchAgent. Pairings and export list are unchanged; accessories go unavailable until the next export change starts the bridge again by itself. Mainly for when you are about to disable the plugin, which would otherwise leave the node running with nothing supervising it. | + +Two notes on unpairing. The picker names the *vendor* and the fabric index +(`Apple Home (index 1)`) because that is what the bridge removes a pairing by, +and it is built from the last state the bridge reported — so an ecosystem that +dropped the bridge since you opened the dialog may still be listed. And +**unpairing the last remaining ecosystem resets the whole bridge**: Matter's own +rules mean the node factory-resets itself when its fabric set empties, so it +starts advertising for commissioning again, exactly as if you had used *Reset +Matter Export Pairings…*. + +### The two destructive recovery actions + +Both are in the plugin menu, both make you tick a box (or two), and neither is +something you should reach for casually. They are described here in the same +words the dialogs use, **worse one first** — the difference between them is +whether the damage can be undone. + +**Rebuild Matter Endpoint Map… — UNRECOVERABLE.** What it destroys cannot be put +back by repeating any step. Use it **only** when the plugin says the bridge node +is refusing to export because its endpoint-number map is unreadable. The map +records which Matter accessory number belongs to which Indigo device; rebuilding +it accepts whatever numbers exist now as correct. *This **will** duplicate +accessories in ecosystems that are already paired.* The old accessories do not +disappear — they stop working, you have to delete them by hand, and any names, +rooms, scenes and automations built on them are lost. Your pairings themselves +are not touched and no Indigo device is changed. If the bridge is **not** +refusing, the plugin refuses the rebuild: there is no fault to recover from, and +a rebuild would throw away the retained endpoint numbers of every device you are +not currently exporting — the ones that make re-adding a device restore the same +accessory rather than create a second one. + +**Reset Matter Export Pairings… — recoverable, at the cost of pairing again.** +It *removes **every** ecosystem pairing from the Matter export bridge and starts +advertising for commissioning again.* Apple Home, and anything else you have +paired, lose all of the accessories Indigo exports, and you have to pair the +bridge again from scratch. This is the export side only: Matter devices that +Indigo **controls** are not affected, and no Indigo device is changed or deleted. +Endpoint numbers are kept, so re-pairing the same ecosystems restores the same +accessory identities where it can. Use it when you want a clean start on the +pairing side, or when the bridge's pairings are in a state you can't otherwise +clear. + +--- + ## Backups > **The single most important thing in this whole setup.** @@ -239,9 +571,20 @@ This directory holds the fabric root CA private key and every device's operation **Losing it means re-commissioning every Matter device from scratch.** Indigo's own database backup does **not** cover it. -The plugin's in-app backup menu item is currently a stub -([issue #26](https://github.com/simons-plugins/indigo-matter/issues/26)), so back it up -yourself. With the plugin (or matter-server) stopped, copy the directory: +Use **Plugins ▸ Matter ▸ Export fabric backup…**. It writes a timestamped zip into a +`backups/` directory beside the storage dir and prunes old ones, and +**Restore fabric backup…** puts one back (moving the current fabric aside first, so a +bad restore is reversible). Copy the zips somewhere off-machine as well — a backup on +the same disk is not a backup. + +> **Restore has a precondition, and its dialog states it: *"Requires 'Manage +> LaunchAgent' to be on."*** Restoring stops matter-server, swaps the storage +> directory and starts it again, so it needs the plugin to be the thing that +> runs matter-server. In Mode B (manual) the restore is refused — stop +> matter-server yourself and swap the directory by hand instead. + +If you would rather do it by hand, stop the plugin (or matter-server) and copy the +directory: ```bash cp -R \ @@ -249,7 +592,28 @@ cp -R \ "$HOME/Backups/indigo-matter-fabric-$(date -u +%Y%m%d)" ``` -Keep it somewhere safe and off-machine. +### If you export devices, back up the bridge's storage too + +The export bridge keeps its own directory, beside the controller's: + +``` +~/Library/Application Support/com.simons-plugins.indigo-matter/bridge-node/ +``` + +It is every bit as sacred, for two separate reasons. It holds the credentials of +every ecosystem the bridge has been paired with — losing them un-pairs the lot — +and the record of which Matter accessory number belongs to which Indigo device. +Losing *that* is the number-one real-world cause of duplicated accessories: every +ecosystem re-creates every accessory, and their names, rooms and automations go +with the old ones. + +**Export fabric backup…** includes this directory in the zip. **Restore does not +yet put it back** — it reports the bridge files it found and skips them, because +restoring them safely needs the bridge node stopped and that wiring is a +follow-up. So for now, if you need to restore the bridge side, copy +`bridge-node/` out of the zip by hand with the bridge stopped (*Stop the Matter +export bridge…*). Your export **list** is not in here at all — it lives in the +plugin's preferences and rides along with Indigo's own database backup. --- @@ -297,6 +661,8 @@ does say so on the page and in the log line. ## Troubleshooting +### The controller (matter-server) + | Symptom | Cause | Fix | |---|---|---| | `npm error could not determine executable to run` | Old plugin (< 2026.0.6) launched matter-server via `npx`, which no longer works (the package has no bin). | Upgrade the plugin to ≥ 2026.0.6. | @@ -311,11 +677,60 @@ does say so on the page and in the log line. | Log says the server started, but it behaves like the old one | A matter-server started **outside** this plugin — or with a different `--storage-path` — is holding the port. The plugin's orphan reaper only matches servers using *its* package dir and storage path, so it never stops that one, and the fresh instance dies on the port bind (`EADDRINUSE` in `matter-server.err.log`) while the stray keeps answering on 5580 with its own arguments. (A stray with the *same* storage path fails earlier and differently — see the storage-lock row above.) | Find the owner with `lsof -nP -iTCP:5580 -sTCP:LISTEN`, confirm with `ps -ww -o pid=,command= -p `, then `kill `. Note matter-server may exit 0 on that failure, which `KeepAlive` treats as a clean exit, so launchd will not respawn it — use Plugins ▸ Matter ▸ **Restart matter-server** afterwards. | | Hand-edited plist keeps reverting | The plugin regenerates the plist from config on restart. | Change settings via **Configure…**, not the plist. | +### The export bridge + +The bridge node keeps its own logs, separate from matter-server's: + +``` +~/Library/Logs/indigo-matter/bridge-node.log +~/Library/Logs/indigo-matter/bridge-node.err.log +``` + +Those files are **appended to and never truncated**, so check the timestamps — the +last lines can be from a crash loop days ago that has since been fixed. The plugin +quotes their tail into the Indigo log when it cannot reach the node. + +Everything below fails **export only**. Your Indigo devices, and the Matter devices +Indigo controls, are never affected by any of it. + +**What you can see.** Symptoms that show up in an ecosystem app or in the export +dialog, most-likely-first — the top two are what a first export usually hits. + +| Symptom | Cause | Fix | +|---|---|---| +| You exported a device and **nothing appeared** in Apple Home | The bridge has never been paired. Exporting publishes a device *on* the bridge; an ecosystem sees nothing at all until the bridge itself has been added to it. | Do Step E3 then Step E4. You do this **once per ecosystem** — after that, later exports appear by themselves with no further pairing. | +| Apple Home's **Add Accessory** cannot find the bridge | One of three: nothing is exported (the bridge only runs while the export list is non-empty), or no pairing window is open, or **Primary network interface** in Configure… names an interface this Mac is not actually using. That field is shared with matter-server and the bridge pins its mDNS advertising to it, so a wrong value advertises where nobody is listening. Matter discovery is also mDNS + link-local IPv6, so the phone must be on the same subnet as the Mac. | Export at least one device; open a window with **Pair Matter Bridge…**; check the interface with `ifconfig \| grep "status: active"` and **reload the plugin** after changing it; put the phone on the same subnet/VLAN as the Indigo Mac. | +| An accessory shows as **unavailable** in Apple Home | Either the Indigo device is disabled or unconfigured (the bridge deliberately marks it unreachable rather than letting the ecosystem time out), or export is switched off, or the bridge node is not running. | Re-enable the Indigo device; or Configure… ▸ **Enable Matter export**; or work the log rows below. | +| Accessories **stay** in Apple Home after you stopped exporting them | The removal never reached the node — it was down, or the plugin was reloaded mid-request. | It is recorded and finished automatically the next time the plugin connects to the node: export a device again, or reload the plugin. There is no retry loop in between, so nothing happens before then. | +| A thermostat or sensor exports numbers that look wrong | Indigo records no units anywhere in its device model, so the bridge takes each reading as already being in the unit Matter wants (°C, %RH, lux, m³/h). A °F thermostat therefore exports the Fahrenheit number labelled as Celsius. **Barometric pressure is the one exception**: Indigo's convention is hPa, and the bridge divides by 10 to reach Matter's kPa — so a barometer reading in hPa is already correct. Do **not** "fix" it to report kPa; that is what produces a reading ten times too high. | Not fixable from Indigo's device model today — it needs a declared unit per export. Export devices that already read in metric (and leave barometers in hPa), or leave that one un-exported. | +| A **pressure or flow** sensor never appears in Apple Home | Apple Home has no UI for those Matter device types. | **Nothing is broken and there is nothing to fix.** The accessories exist on the bridge and other ecosystems can use them; Apple simply does not render them. If Apple Home is all you run, don't export those sensors. | +| Apple Home refuses to change an exported accessory's **room** | Room assignment is a HomeKit/iCloud home-database concept. It never crosses Matter and never reaches the bridge. | **Nothing the bridge can do, and nothing worth trying.** No bridge-side setting, restart or re-export affects it, and the node log showing no traffic at the moment of the attempt is the expected state rather than evidence of a fault. It is Apple's to fix. | + +**What the log says.** Verbatim strings from the Indigo Event Log and +`bridge-node.err.log`. + +| Log line | Cause | Fix | +|---|---|---| +| `Matter export: the bridge node is not responding after N attempts on port 5581` | The node is not running, is crash-looping, or was never installed. All three look identical at the socket, so the plugin appends what the LaunchAgent found. | Read the sentence that follows it in the log — it names the real cause. Then: install the package (below), free the port (below), or check `bridge-node.err.log`. | +| The log names **Install/update the Matter export bridge** | The `indigo-matter-bridge` package is not installed, so no LaunchAgent was written. This is the normal first-run state. | Run that menu item. Note it cannot succeed until the package is published to npm — see *Export prerequisites*. | +| An `EADDRINUSE` / address-already-in-use failure in `bridge-node.err.log`. The node labels which startup step failed, so the line says whether it was the Matter port or the loopback protocol port | **Matter port (UDP 5540):** another Matter *device* stack on this Mac already holds it — Homebridge 2.x, matterbridge, or a Home Assistant container in host mode. **Protocol port (TCP 5581):** an orphaned bridge node from an earlier LaunchAgent is still running, or an unrelated service has the port. | **Matter port:** stop the other stack, or Configure… ▸ **Show export port settings** ▸ **Matter port**, pick another, and **reload the plugin**. Find the holder with `lsof -nP -iUDP:5540`. Moving off 5540 is a real trade — it is the port ecosystems expect. **Protocol port:** **Stop the Matter export bridge…** then re-save an export (the plugin reaps its own orphans on start). If something else holds it, find it with `lsof -nP -iTCP:5581 -sTCP:LISTEN`, and either stop it or change **Bridge control port** and reload the plugin. | +| `Matter export: the bridge node's LaunchAgent did not start` / `could not be loaded by launchd` | launchd accepted the job and the process died, or refused the plist. | Check `bridge-node.err.log` first. Then `launchctl print gui/$(id -u)/com.simons-plugins.indigo-matter.bridge`. **Stop the Matter export bridge…** followed by re-saving an export rebuilds the plist from scratch. | +| `Matter export: the bridge node is serving NOTHING because its endpoint-number map is unreadable` | The bridge's `endpoint-map.json` is present but corrupt. It refuses to serve rather than silently renumber every accessory. | **Rebuild Matter Endpoint Map…** — and read what it costs first (above). The unusable file is kept as `endpoint-map.json.corrupt-` in the bridge storage folder; if you have a backup of the original, restoring it is better than rebuilding. | +| `…because its identity file is unreadable` | `identity.json` is corrupt. This is a *different* fault with a different remedy, and the node refuses a rebuild for it. | Restore or repair `identity.json.unreadable-` from the bridge storage folder and restart the bridge. **Deleting it starts a brand-new bridge**, which every paired ecosystem sees as a different device. | +| The node reports it was commissioned but its Matter fabric storage is gone | The bridge storage directory was moved, restored partially, or lost. | Restore the whole `bridge-node/` directory from a backup. If you have none, accept the loss: reset the pairings and pair each ecosystem again. | +| `Matter export: endpoint-number DRIFT detected` | **Most likely: you have just reset the pairings, or unpaired the last ecosystem.** Both factory-reset the bridge, which wipes matter.js's own endpoint allocation — so the numbers are handed out afresh and no longer match the preserved map. That is *expected*, and saying so is exactly what the preserved map is for. **Otherwise:** storage loss on the bridge side, in which case exported accessories may have swapped identities in paired ecosystems. | **After a reset or a last-ecosystem unpair: no action.** The map is reporting what it exists to report, and re-pairing proceeds normally. **Otherwise:** drift is never repaired automatically, deliberately — an auto-repair would bless the loss and hide the next one. Check the named accessories in each ecosystem, and restore the bridge storage from a backup if you have one. | +| `Matter export: the bridge client is HALTED` | Either the node speaks a different protocol version than this plugin (an old node left running across a plugin upgrade), or it refused a request that would have removed every accessory at once. | Nothing retries on its own. For version skew: **Stop the Matter export bridge…**, then **Install/update the Matter export bridge**, then re-save an export. The reason is on the line itself. | +| `Matter export: the pairing window has expired without an ecosystem completing commissioning` | Nobody paired within the window (up to 15 minutes). The code it showed is now dead. | Open a new one: **Pair Matter Bridge…**. Have the phone in your hand first. | +| The pairing menu says the bridge is *already advertising with its original code* | The bridge has never been paired, so its first commissioning window is already open with its own code — deriving a new one would kill a code you may already be typing. | Use the code in the log. This is not an error. | +| `Matter export: device N is in the export list but will NOT be bridged` | That Indigo device was deleted, disabled beyond recognition, re-typed, or no longer offers the role you exported it as. The rest of the export list is unaffected. | Open **Manage Matter Exports…**, pick the device, and either re-choose a role or **Remove export**. | +| `Matter export: … stopped reporting ` | The Indigo device has gone quiet — a flat battery, a plugin that lost it. Matter has no way to say "I no longer know", so the ecosystem keeps showing the last value it was given, indefinitely. | Fix the underlying device. Note this warning is said **once**, when it happens; it is not repeated after a plugin reload, so its absence is not evidence that everything is reporting. | +| `Matter export: '' for device N has not returned after 30s` | Something you pressed in Apple Home reached an Indigo device that is not answering. Commands run one at a time, so everything behind it is queued. | Fix that device or its plugin. The ecosystem already shows the command as done and nothing corrects that until the device next reports. | + --- ## Upgrading matter-server -matter-server is Alpha; test before adopting a new version. With the plugin (or +matter-server is Beta; test before adopting a new version. With the plugin (or matter-server) stopped, and after **backing up the fabric** (above): ```bash @@ -336,5 +751,12 @@ The plugin **never** deletes the storage directory. Your fabric at `~/Library/Application Support/com.simons-plugins.indigo-matter/matter-server/` stays put — so a reinstall keeps all your devices. Delete it manually only if you intend a full factory reset (and re-commission everything). - - + +The same is true of the export bridge. Its LaunchAgent +(`~/Library/LaunchAgents/com.simons-plugins.indigo-matter.bridge.plist`) goes with the +plugin, and its storage at +`~/Library/Application Support/com.simons-plugins.indigo-matter/bridge-node/` stays — +so a reinstall keeps your ecosystem pairings and your accessories' identities. If you are +leaving for good, unpair the bridge from each ecosystem *before* you remove the plugin, +or you will be deleting a dead "Indigo" bridge from each ecosystem's app by hand +afterwards. diff --git a/docs/MATTER.md b/docs/MATTER.md index 69b8e1b..9cd8ee1 100644 --- a/docs/MATTER.md +++ b/docs/MATTER.md @@ -6,6 +6,11 @@ ecosystem — **Indigo**, the **indigo-matter plugin**, **matter-server**, [INSTALL.md](./INSTALL.md); if you want the wire contract, see [API.md](./API.md). This document is the "why does it work this way". +The plugin works in **both directions**, and most of this page is about the +first: Matter devices becoming Indigo devices. The second — selected Indigo +devices published outward as Matter accessories — has its own section, +[Indigo as a Matter bridge](#indigo-as-a-matter-bridge--the-other-direction). + --- ## What is Matter? @@ -59,7 +64,7 @@ multi-sensor (Thread, via a HomePod border router). Here's why. The one Thread operation the plugin's controller stack ([`matter-server`](https://github.com/matter-js/matterjs-server), matter.js, -Alpha) can't do is *first-admin* commissioning — handing a factory-fresh +Beta) can't do is *first-admin* commissioning — handing a factory-fresh device the Thread network credentials over BLE. In the share model that step is always the admin-1 ecosystem's job: Apple Home (or Alexa, or Google Home — see below) provisions the device onto **its own** Thread mesh using its own @@ -68,8 +73,8 @@ ecosystem's border router routes IPv6 between the Thread mesh and the LAN and proxies the device's mDNS records, so when the plugin joins as a second admin over IP it neither knows nor cares that the last hop is Thread. -Practical caveats: matter-server is Alpha, and battery Thread devices are -"sleepy" (they wake on long intervals), which is where an alpha controller is +Practical caveats: matter-server is Beta, and battery Thread devices are +"sleepy" (they wake on long intervals), which is where a beta controller is most likely to be flaky — though the first validated Thread device was exactly such a sleepy battery sensor and behaved (live unprompted attribute reports). Thread devices *bridged* into Matter by a hub you own (Aqara, Hue, SwitchBot) are a separate, hub-dependent route — see @@ -277,6 +282,252 @@ A node exposing several capabilities gets several Indigo devices (e.g. a multi-sensor becomes one device per measurement); secondary capabilities like energy metering and battery merge into the primary device's states. +## Indigo as a Matter bridge — the other direction + +Everything up to here is Indigo as a Matter **controller**: other people's Matter +devices becoming Indigo devices. The plugin also runs the reverse. A set of +Indigo devices you choose — Z-Wave, Insteon, Zigbee behind a plugin, MQTT, +anything with an Indigo device record — can be **exported**: published outward as +Matter accessories, so Apple Home sees and controls them like any other Matter +kit. Locally, with no cloud relay, and with no per-ecosystem bridge plugin. + +If you have run Homebridge, this is the same idea over Matter's protocol instead +of HomeKit's — with the difference that Matter is one implementation reaching +every ecosystem that speaks it, rather than one bridge per ecosystem. + +**It is not installable yet.** The bridge is a second npm package, +`indigo-matter-bridge`, and it has not been published to the npm registry — so +the plugin's install menu item cannot resolve it, and export cannot be brought +up on any machine today. Everything below describes what is built and waiting on +that. Setup steps, for when it lands, are in [INSTALL.md](./INSTALL.md); this +section is the shape of it. + +The two questions this page gets asked most are "can I export my lock?" and "why +isn't my *X* in the list?", so those tables come first; the reasoning follows +them. + +### What can be exported (v1) + +Indigo does not record what a device *is*. A relay may be a lamp, a plug, a lock, +a valve, a fan or a garage door, and nothing in the device model tells them +apart. So export asks you to **declare a role** per device, and defaults to the +safest reading rather than guessing. Only roles a device can legitimately take +are offered. + +| Indigo device | Roles offered | Appears in ecosystems as | +| --- | --- | --- | +| Relay | **Plug** *(default)*, Light, Lock | On/Off Plug-in Unit · On/Off Light · Door Lock | +| Dimmer | **Dimmable light** *(default)*, Window covering | Dimmable Light · Window Covering | +| Dimmer, colour-temperature capable | **Colour-temperature light** *(default)*, Dimmable light, Window covering | Color Temperature Light | +| Dimmer, full colour | **Full-colour light** *(default)*, Dimmable light, Colour-temperature light, Window covering | Extended Color Light | +| Sensor, on/off | **Occupancy** *(default)*, Contact | Occupancy Sensor · Contact Sensor | +| Sensor, numeric | Temperature, Humidity, Light (lux), Pressure, Flow — the default is guessed from the device's units, and you can correct it | the matching Matter sensor type | +| Thermostat | Thermostat | Thermostat (setpoints and modes; no fan in v1) | + +Two notes on that table. **Window covering** is offered for any dimmer because +Indigo represents blinds as dimmers; Matter's convention is 100 % = fully open, +and there is a per-export tick-box if your device runs the other way. And the +**numeric sensor** guess reads whatever unit hints the device carries (its +plugin's properties, its displayed value, then its name) — it is only a default, +and all five roles stay selectable. + +**Locks export, and they do not auto-confirm anything.** A lock or unlock from an +ecosystem is passed to Indigo and nothing else: no optimistic state, no +synthesised confirmation. What the ecosystem shows moves only when Indigo's own +state moves, because the bolt is the authority. + +### What cannot be exported, and why + +Excluded devices still **appear in the picker with their reason** — they are +never silently missing, so you are never left hunting for a device that will +never show up. (The picker shows up to 300 matching devices at a time; past that +it says so and asks you to narrow the name filter.) + +| Not exportable | Why | +| --- | --- | +| Anything this plugin created — including its own energy-meter devices | Loop guard: a Matter device is not re-exported over Matter. Checked on the owning plugin id, before the device's type is looked at, so it catches every one of the plugin's device types alike. These are filtered out of the picker entirely rather than shown with a reason. | +| **Valves** (as a relay role) | matter.js's valve cluster is an empty stub — the whole command surface would have to be written from scratch — and ecosystem support for the type is poor. v2 candidate. | +| **Fans** — both dimmer-backed and speed-control devices | Same reason: matter.js's fan cluster only seeds a default mode, so all fan behaviour would be ours to implement. v2 candidate. This is also why the exported thermostat has no fan control. | +| **Garage doors** (as a relay role) | Needs polarity data Indigo does not carry (`onState` true meaning *closed*, "on" meaning *close*), and getting it backwards is a physical-safety problem, not a cosmetic one. Blocked on the device-catalog work. | +| **Sprinkler** devices | Matter has no irrigation-controller type. Per-zone water valves would be a lossy fit, and water valves are descoped anyway. | +| **MultiIO** devices | No coherent way to represent one as a single accessory. | +| Sensors whose units aren't in the table above | No faithful Matter sensor type to map them to. | +| Sensors that report neither an on/off state nor a value | Nothing to publish. | +| Devices with no resolvable role | The device is not a relay, dimmer, sensor, thermostat, speed-control, sprinkler or MultiIO — typically another plugin's custom device class. There is nothing in the Indigo device model to map, so there is no role to offer. | + +Beyond the device types, four v1 limits worth knowing up front: + +- **Units are taken on trust.** Indigo declares no units anywhere, so a reading + is assumed to already be in the unit Matter wants (°C, %RH, lux, m³/h). + A Fahrenheit thermostat will export a wrong-looking number, and there is no + fix inside Indigo's device model today. **Barometric pressure is the one + exception**: Indigo's convention is hPa, the plugin knows it, and it converts + to Matter's kPa — so a barometer reading in hPa is right as it stands, and + "correcting" it to kPa is what produces a value ten times too high. +- **Pressure and flow sensors are exported but Apple Home has no UI for those + Matter types**, so expect them not to appear there. +- **Energy and power are not exported.** Matter's electrical-sensor type is + ignored by Apple Home's UI, so it buys nothing in the ecosystem this is + written for. v2 candidate. +- **Action groups, schedules, triggers and variables are not exported.** Only + devices. + +### Two processes, opposite jobs + +```text + Matter devices ──▶ matter-server ──▶ ┐ + (in the house) (controller, │ + Indigo's fabric) │ + ├──▶ indigo-matter plugin ──▶ Indigo + Apple Home ◀── bridge node ◀─────────┘ (Python) + (and any other (device role, + ecosystem) matter.js) +``` + +There are now **two** Node processes, and they share a Node runtime and nothing +else. `matter-server` is the controller: it holds Indigo's own fabric and +commissions other people's devices. The **bridge node** is a Matter *device* — +it holds no fabric of its own, and instead gets commissioned *into* other +ecosystems' fabrics, exactly as a Matter plug does. Separate process, separate +storage, separate launchd job, separate port. One can be broken while the other +works, and an export failure never touches the devices Indigo controls. + +**Why a separate process at all**, rather than the plugin doing Matter itself: +Indigo plugins are Python and the Matter library the plugin uses (matter.js) is +TypeScript. Keeping the whole Matter stack behind a process boundary is what lets +the plugin stay plain Python and speak Indigo devices, and it is the same +discipline the controller side already follows. The two halves talk over a +loopback WebSocket that nothing outside this Mac can reach, and the protocol is +versioned: a bridge node speaking a different **protocol** version is refused +rather than guessed at. That version is bumped only when the wire contract +itself changes, so most plugin upgrades need no new bridge node — but when it +does change, an old node left running across an upgrade is halted with a reason +rather than talked to on a guess. + +Both halves present the bridge as one accessory containing an **aggregator** with +one child endpoint per exported device — the standard Matter bridge topology, and +the same shape the plugin already *consumes* inbound from Aqara, Hue and +SwitchBot hubs. One bridge, one pairing operation, one uncertified prompt, however +many devices you export. + +### Nothing is exported until you say so + +Export is **opt-in per device, default empty**, and that is a deliberate policy +rather than a UI convenience. + +Exporting a device is *publishing* it. It becomes visible and operable from every +ecosystem the bridge is paired with, and from those ecosystems' accounts — +which means, for a voice assistant, from outside the house. Locks, valves, +garage doors and alarm-adjacent devices make an export-everything default +indefensible, so there isn't one: a fresh install exports nothing, pairs nothing +and runs no bridge process at all. + +The consequence worth planning around: **there is no per-ecosystem export set in +v1**. There is one bridge and one allow-list, so a device you export is exported +to *every* ecosystem the bridge is paired with. If you want a device in one place +and not another, do not export it. (Per-ecosystem sets, and multiple bridges, +are a v2 idea.) + +Devices this plugin created itself can never be exported — they are filtered out +of the picker entirely, so a Matter device cannot be re-exported over Matter into +a loop. + +### Fabrics, again — and Apple takes two slots + +The same multi-admin machinery described above works in this direction too. Each +ecosystem that pairs the bridge installs its credentials as a **fabric** on it, +and the bridge serves them all at once and independently. There is no small +fabric limit here of the kind physical devices have: the bridge is software and +its ceiling is far above any realistic number of ecosystems. + +One thing to expect: **an Apple Home pairing creates two fabrics**, not one — +*Apple Home* and *Apple Keychain* (iCloud Keychain sync). Both show up in the +plugin's readouts and in the unpair picker. Neither is a stray, and removing +either is not a tidy-up. + +Unpairing the **last** ecosystem resets the bridge completely: Matter's rules +make the node factory-reset itself when its fabric set empties, so it goes back +to advertising for commissioning. That is correct behaviour, and it is the same +outcome as *Reset Matter Export Pairings…* — worth knowing before you remove +what turns out to be the only one. + +### Accessory identity, and why it must never move + +**Back up +`~/Library/Application Support/com.simons-plugins.indigo-matter/bridge-node/`**, +and read the recovery notes in [INSTALL.md](./INSTALL.md) before using either of +the two destructive repair actions. That is the whole practical instruction; the +rest of this subsection is why it matters more than it looks. + +Every exported device gets a stable identity derived from its immutable Indigo +device ID, and a Matter endpoint number allocated once against that identity — +never from its position in a list. Ecosystems remember both. The name you gave an accessory in Apple Home, +the room you put it in, the scenes and automations you built on it: all of that +is keyed to the identity, not to the accessory's name. + +So the bridge treats its storage directory as sacred, and keeps an independent +record of which number belongs to which device purely so it can **notice** if +they ever disagree. If they do, it says so loudly and **does not repair it** — an +automatic repair would bless whatever went wrong and make the next occurrence +invisible too. And if that record is unreadable on a bridge that has been paired, +it refuses to export anything at all rather than quietly renumbering — silent +renumbering is how every accessory ends up duplicated in every ecosystem, with +the originals dead and removable only by hand. (Before the bridge has ever been +paired there is nothing to protect, so it just carries on.) + +There is one everyday case that changes identity on purpose: **changing an +export's role**. Matter does not allow an endpoint to change device type, so the +accessory is removed and re-added, and every ecosystem treats it as brand new — +losing the name and room it had. The dialog warns you at the time. + +### Which ecosystems this actually works with + +Honestly stated, because the difference matters: + +- **Apple Home** is the ecosystem this bridge is designed for, documented for, + and the only one anything is claimed about. It is also the strictest + attestation experience we can test — the uncertified prompt. +- **Alexa, Google Home and SmartThings are untested and unclaimed.** Not + supported, not unsupported: nobody here has the hardware, no decision hangs on + the answer, and the only remedy for a refusal — a real CSA vendor ID — is out + of scope for a free plugin. The bridge is an ordinary multi-admin Matter + bridge and there is no reason in principle they should refuse it, but a reason + in principle is not a test. Google Home is the most likely to be awkward: it + has historically been strictest about test-range vendor IDs. If you pair any of + them, please report it. +- **Any Matter controller you already own** counts too, including Home Assistant + and Indigo's own controller — the bridge does not care who commissions it. + +**What has and has not been proven on live hardware** is stated once, in the +project [README](../README.md#status), rather than restated here where the two +copies would drift apart. The short version: the bridge has been commissioned +into a real Apple Home and exported devices have been controlled both ways, but +several legs — pairing driven from the plugin's own menu, a second ecosystem, a +full reboot — are still outstanding. Treat the export half as new. + +### Why every ecosystem calls it "uncertified" + +Because it is, deliberately, and there is nothing the plugin can do about it. + +Matter attestation runs one way: the **commissioner checks the device's** +certificate. Inbound, the plugin is the commissioner, so it has a setting for +this (*Allow test/development device certificates*). Outbound the roles are +swapped — the bridge is the device and Apple is the commissioner — so the trust +policy belongs to Apple and there is no flag we could ship for someone else's +ecosystem, ever. + +The bridge advertises with the specification's **test vendor ID** (`0xFFF1`), the +same posture Homebridge, matterbridge and Home Assistant's bridge all ship with. +Certification means CSA membership plus per-product testing, which is not +proportionate for a free community plugin. So: expect the warning, choose "Add +Anyway", and know that it says nothing about whether the bridge works. + +(That test vendor ID is also why Apple Home shows the *controller* fabric as +"Matter Test", described earlier. Same number, two entirely different uses — one +is our controller's fabric identity, the other is the bridge's attestation +identity — and neither is a claim about the other.) + + ## Firmware updates (and why they matter more than usual) Matter firmware arrives by two routes: the device **vendor's app**, and the @@ -329,6 +580,10 @@ Common failure modes, in rough order of likelihood: re-pair. The plugin keeps rotating zips and a restore is menu-driven and reversible. +**Exporting devices** has its own failure modes, its own logs and its own +recovery actions — see the *export bridge* table in +[INSTALL.md → Troubleshooting](./INSTALL.md#the-export-bridge). + --- *Further reading:* [INSTALL.md](./INSTALL.md) (setup) · diff --git a/docs/PRD-indigo-matter-export.md b/docs/PRD-indigo-matter-export.md index b4c1579..f39d3b6 100644 --- a/docs/PRD-indigo-matter-export.md +++ b/docs/PRD-indigo-matter-export.md @@ -5,8 +5,9 @@ **Governing ADR:** [`../../docs/adr/0006-indigo-as-matter-bridge.md`](../../docs/adr/0006-indigo-as-matter-bridge.md) (accepted 2026-08-03; workspace-level — the path resolves in the multi-repo workspace checkout, not on GitHub), as amended by ADR-0007 (validation-evidence criterion) **Companion PRD:** [`PRD-indigo-matter-plugin.md`](./PRD-indigo-matter-plugin.md) (historical — the inbound/controller build) **Local protocol spec:** [`BRIDGE_PROTOCOL.md`](./BRIDGE_PROTOCOL.md) -**Last updated:** 2026-08-04 (research pass: matter.js 0.17.8 verified by execution; scope and -packaging decisions taken — see §5.2 exclusions, §10, §11) +**Last updated:** 2026-08-05 (E8 docs pass: §5.2's sensor rows, §5.3's distribution +wording and §9's E7/E8 rows corrected against the shipped code — see the marked +rows. Earlier: 2026-08-04 research pass, matter.js 0.17.8 verified by execution) ## 1. Summary @@ -213,16 +214,14 @@ safest interpretation (plug/light) rather than guessing. | Relay | Valve | *Not exportable in v1* | Descoped 2026-08-04; see below | | Relay | Garage door | *Not exportable in v1* | Polarity + safety; see below | | Dimmer | Light | Dimmable Light | | -| Dimmer (colour) | Light | Extended Color Light | Colour-temp-only devices → Color Temperature Light | +| Dimmer (colour) | Light | Extended Color Light | Colour-temp-only devices → Color Temperature Light. A colour-capable dimmer is *also* offered the colour-temp-only role, so a user can downgrade a bulb an ecosystem renders badly | | Dimmer | Window covering | Window Covering | Polarity declared per export (100% = open, inbound convention). Must implement `handleMovement()` — matter.js's default snaps to target instantly | | Dimmer | Fan | *Not exportable in v1* | Descoped 2026-08-04; see below | -| Sensor (binary, motion) | — | Occupancy Sensor | | -| Sensor (binary, contact) | — | Contact Sensor | | -| Sensor (numeric, °C) | — | Temperature Sensor | | -| Sensor (numeric, %RH) | — | Humidity Sensor | | -| Sensor (numeric, lux) | — | Light Sensor | | -| Sensor (numeric, pressure) | — | Pressure Sensor | Apple Home ignores this type (Google supports it); exported anyway, documented | -| Sensor (numeric, flow) | — | Flow Sensor | Apple Home ignores this type (Google supports it); exported anyway, documented | +| Sensor (binary) | Occupancy *(default)* | Occupancy Sensor | Indigo does not distinguish motion from contact any more than it distinguishes a plug from a lamp, so this is declared too. Corrected 2026-08-05 (E8): the row used to read "—" in both directions, implying a mapping the code does not make | +| Sensor (binary) | Contact | Contact Sensor | | +| Sensor (numeric) | Temperature / Humidity / Light / Pressure / Flow | the matching Matter sensor type | **All five are offered for any numeric sensor**; the unit heuristic (pluginProps, then the formatted UI value, then the device name) picks only the *default*, and a sensor no hint matches is the "units outside the table" exclusion below. Corrected 2026-08-05 (E8): the rows used to read as one unit → one type | +| Sensor (numeric, pressure) | Pressure | Pressure Sensor | Apple Home ignores this type (Google supports it); exported anyway, documented. Indigo's barometer convention is hPa and §4.2's key is kPa, so this is the one unit the handler converts | +| Sensor (numeric, flow) | Flow | Flow Sensor | Apple Home ignores this type (Google supports it); exported anyway, documented | | Thermostat | — | Thermostat | Setpoints, modes. No fan in v1 (the FanControl descope applies here too); v2 candidate. matter.js provides the cluster machinery; the HVAC logic is ours | | SpeedControl | Fan | *Not exportable in v1* | Descoped 2026-08-04; see below | @@ -242,6 +241,7 @@ them into a PRD where they can rot. | `custom` devices with no resolvable role | Includes the plugin's own energy-meter type | | Garage doors | Needs the polarity handling the catalog doesn't yet carry (`onState` true = closed, turnOn = close), and mis-mapping is a physical-safety issue. Blocked on the catalog role/polarity work | | Sensors with units outside the table | No faithful Matter sensor type | +| Sensors reporting neither an on/off state nor a value | Nothing to publish (`export_catalog.REASON_SENSOR_NO_VALUE`; added to this table 2026-08-05, E8 — it was always in the code) | Excluded devices must **appear in the picker as excluded, with reasons** (XAC9), not silently missing. @@ -255,10 +255,11 @@ topology the plugin already consumes inbound (the "Bridges" section of `MATTER.md`; in code, `matter_model.py`'s BridgedDeviceBasicInformation handling and `device_sync.py`'s `DEVICE_TYPE_AGGREGATOR`). -- **Distribution:** the bridge node is a **published npm package** +- **Distribution:** the bridge node is distributed as an **npm package** (`indigo-matter-bridge`, TypeScript, decided 2026-08-04), exact-pinned by the plugin the same way `matter-server@1.2.2` is — the existing - `npm install --prefix` machinery works unchanged. matter.js itself is + `npm install --prefix` machinery works unchanged. It is **not on the registry + yet** (§9 E7); until it is, the install action cannot resolve the pin. matter.js itself is **exact-pinned** (no caret): patch releases have changed what Apple Home renders with zero code change on the bridge side. - **Max fabrics:** matter.js defaults `supportedFabrics` to 254, so ≥5 @@ -367,15 +368,15 @@ not slot arithmetic. Per-export settings live in the §5.1 dialog. | # | Milestone | Gating criterion | |---|---|---| -| E0 | Bridge node skeleton — **the validation gate** | Node process starts, exposes an aggregator with one hard-coded endpoint, and **pairs into Apple Home**. If an uncertified bridge will not pair here, the design is dead and nothing after this matters | +| E0 | Bridge node skeleton — **the validation gate** · **GATE PASSED (2026-08-04)** | Node process starts, exposes an aggregator with one hard-coded endpoint, and **pairs into Apple Home**. If an uncertified bridge will not pair here, the design is dead and nothing after this matters. **Done on jarvis 2026-08-04: an uncertified bridge was commissioned into a real Apple Home and the uncertified-accessory prompt accepted.** The pairing was driven from the bridge node's own console, before E6's menu existed — so pairing through the plugin's menu is still unproven | | E1 | Local protocol + plugin client | Plugin drives endpoint create/remove over WS | | E2 | Allow-list + UI-D dialog | Devices selectable with role; loop guard live (XAC6, XAC9) | -| E3 | Relay + dimmer export | XAC4 both directions, and XAC3's Apple Home control, against a manually started bridge node (start-on-export is E7's; the code display is E6's) | +| E3 | Relay + dimmer export — **LIVE E2E PASSED (2026-08-05)** | XAC4 both directions, and XAC3's Apple Home control, against a manually started bridge node (start-on-export is E7's; the code display is E6's). **Done on jarvis 2026-08-05: an on/off light and a dimmer, exported via "Manage Matter Exports…", controlled from Apple Home and from Indigo with each change appearing on the other side** | | E4 | Sensors + thermostat export | Mapping table complete for v1 | -| E5 | Endpoint persistence | XAC5 — the highest-risk correctness requirement | -| E6 | Pairing/unpairing UX + fabric readout — **BUILT** (2026-08-05, plugin `2026.8.3`) | §6 complete, including XAC3's displayed-code pairing flow. "Pair Matter Bridge…" (§3.8, duration 180–900s), the QR page over IWS, "Unpair an Ecosystem…" (§3.9, two gates), the §5.5 Export config section, and the §5 pairing events surfaced in the log. **XAC3's live pairing is unverified** — it needs jarvis and a real ecosystem | -| E7 | launchd agent + failure recovery — **BUILT** (2026-08-05, plugin `2026.8.3`, bridge `0.5.0`) | §7, XAC1, XAC2, XAC7, XAC8. `bridge_agent.BridgeProcess` is the second `AgentSpec`; it is installed and started by the empty→non-empty allow-list transition and stopped after the un-export lands. `remove_package` is per-package. **`indigo-matter-bridge` is not yet on the npm registry** — publishing is Simon's action (`docs/HANDOVER.md` → "Publishing the bridge node"), and until then the install action cannot resolve the pinned spec | -| E8 | Docs | `INSTALL.md` export section, uncertified-prompt explanation, ecosystems-untested note (§10), `MATTER.md` outbound architecture | +| E5 | Endpoint persistence — **DEPLOYED to jarvis 2026-08-05; upgrade leg PASSED** | XAC5 — the highest-risk correctness requirement. **Upgrade migration observed on jarvis 2026-08-05: three exported accessories kept endpoint numbers 3, 5 and 4 across a plugin and bridge-node upgrade — their pre-upgrade values, deliberately not in creation order — with no duplicates in Apple Home.** XAC5's **reboot** leg is still outstanding: a plugin reload and a bridge-node restart have been survived, a full Mac reboot has not | +| E6 | Pairing/unpairing UX + fabric readout — **BUILT** (2026-08-05, plugin `2026.8.3`) | §6 complete, including XAC3's displayed-code pairing flow. "Pair Matter Bridge…" (§3.8, duration 180–900s), the QR page over IWS, "Unpair an Ecosystem…" (§3.9, two gates), the §5.5 Export config section, and the §5 pairing events surfaced in the log. **XAC3 is part-verified:** the pairing gate itself passed on jarvis (E0 row), but that pairing was driven from the node's console — *pairing through this menu* is unverified, as is the second-admin half | +| E7 | launchd agent + failure recovery — **BUILT** (2026-08-05, plugin `2026.8.3`, bridge `0.5.0`) | §7, XAC1, XAC2, XAC7, XAC8. `bridge_agent.BridgeProcess` is the second `AgentSpec`; it is installed and started by the empty→non-empty allow-list transition and stopped after the un-export lands. `remove_package` is per-package. Three lifecycle menu items land with it — "Install/update the Matter export bridge", "Reinstall the Matter export bridge (clean)…" and "Stop the Matter export bridge…" — the last two added in the PR #128 review batch to close the gap where the controller had recovery exits and the bridge had none. **`indigo-matter-bridge` is not yet on the npm registry** — publishing is Simon's action (`docs/HANDOVER.md` → "Publishing the bridge node"), and until then the install action cannot resolve the pinned spec | +| E8 | Docs — **BUILT** (2026-08-05) | `INSTALL.md` retitled for both runtimes, with a contents list and an export section (prerequisites, first export, pairing, the uncertified prompt, removal/unpairing, the two destructive recovery actions ordered by recoverability, bridge-storage backups, uninstall) plus an export-bridge troubleshooting table split into observable symptoms and verbatim log strings; `MATTER.md` outbound architecture, with the v1 role table and the excluded set hoisted to the front; `README.md` two-role lede. The ecosystems-untested note (§10 / ADR-0007) is stated in all three. The docs state the live-validation position **as it actually stands** — E0's pairing gate and E3's two-way control passed, E5 deployed and its upgrade leg observed, with pairing-through-the-menu, the second admin, the reboot leg and E6/E7 end-to-end still marked outstanding — plus the unpublished npm package, the IWS-auth caveat on the pairing page, and the unit assumption | **E0 is the whole validation loop.** It answers the only question that can kill the feature — *will any ecosystem pair an uncertified bridge?* — on hardware diff --git a/indigo-matter.indigoPlugin/Contents/Server Plugin/bridge_agent.py b/indigo-matter.indigoPlugin/Contents/Server Plugin/bridge_agent.py index 5cd388c..f9f102e 100644 --- a/indigo-matter.indigoPlugin/Contents/Server Plugin/bridge_agent.py +++ b/indigo-matter.indigoPlugin/Contents/Server Plugin/bridge_agent.py @@ -25,14 +25,17 @@ module can reach it. Callers wrap agent operations so that a launchd fault never propagates into an Indigo callback. -**Distribution.** ``indigo-matter-bridge`` is a published npm package, exact- -pinned by :data:`DEFAULT_INSTALL_SPEC` exactly as ``matter-server@1.2.2`` is — -which is precisely the parameterisation the ``AgentSpec`` extraction was for: +**Distribution.** ``indigo-matter-bridge`` is an npm package, exact-pinned by +:data:`DEFAULT_INSTALL_SPEC` exactly as ``matter-server@1.2.2`` is — which is +precisely the parameterisation the ``AgentSpec`` extraction was for: installing it is ``npm install --prefix ~/indigo-matter `` with a different string. The plugin bundle ships **no JavaScript**; ``bridge-node/`` is -a top-level source directory in the repo and is published from there. See +a top-level source directory in the repo and is published from there. + +**It is not on the registry yet**, so :meth:`BridgeProcess.install` cannot +resolve :data:`DEFAULT_INSTALL_SPEC` on any machine today. See ``docs/HANDOVER.md`` → "Publishing the bridge node" for the release steps and -for the local-install recipe that stands in before a version is on the registry. +for the local-install recipe that stands in until then. """ from __future__ import annotations