Roadmap¶
Set by the owner on 2026-10-17, at v0.6.0, after the module's node had
been paired and driven by Apple Home and by Home Assistant's Matter server
(both on the reference bridge and OpenCCU-Loom). This page is the one place
where the order of the remaining work is written down: what each milestone
contains, roughly how it is phased, what it is worth and to whom, and when
it counts as closed. The detail of a phase lives in its own document,
written when the phase is next (see How a phase is planned).
The goal¶
Complete Matter 1.6.1, wherever matter.js implements it. matter.js is
the gold standard (CLAUDE.md);
the measure of "complete" is therefore its HEAD, not the specification
text: every cluster, device type, protocol feature and behaviour matter.js
has, this module has — semantically, with the same defaults, the same
constraints and the same wire shape — and nothing it lacks is invented here
unless a connectedhomeip reference exists, as the parity contract already
allows (DiagnosticLogs, FanControl coupling). A generated matter.js stub
counts as an implementation: it is served here by the generated default
server (ADR 0017), which
is exactly as much as matter.js offers for it.
Beyond the standard: the embedding layer. matter.js is the gold standard; this module is meant to be the diamond one — not a larger protocol surface, but a smaller distance between a host's device model and a paired, working Matter node. The consumers that set that bar:
| Consumer | State | What it needs from this module |
|---|---|---|
| OpenCCU-Loom | in production; today imports 20 packages of this module directly | a complete node/ + device/ + contract/ surface so that a bridge needs nothing below it; depth in the clusters HomeMatic devices map to (Switch, Thermostat, DoorLock, WindowCovering) |
| go-ha-matter-bridge (working name; planned separately) | not started; its concept is written when milestone 4 is due | the declarative device kit, the in-process test pair, the adapter module below |
the go-*2mqtt family (daikin, homeconnect, mtec, unifi, zendure) via a shared adapter module (working name go-hamatter: go-hamqtt's Home-Assistant vocabulary → Matter device types; lives outside this module, so contract/ stays stdlib-only) |
not started | the same device kit; energy and appliance device types, which already exist |
examples/reference-controller and a host acting as commissioner |
shipped, controller stages 1–6b | the field features (milestone 1), ICD peers and the rest of OTA push (milestone 5) |
Priority follows from that table: a feature a consumer uses comes before one none uses, and both come before completeness for its own sake.
How to read this page¶
- A milestone closes a topic area completely; it is not a sprint. One
minor version per milestone (
v0.7.0is milestone 1), patch versions for the waves inside it. Thev1.0.0cut is the owner's call and is not scheduled here; the0.xseries continues until then, and the freeze ofdevice/,node/andcontroller/stays bound to that cut. - Phases continue the numbering of the concepts (concept v3 ends at phase 13). A phase is one to three implementation waves, one pull request per wave (one PR per wave).
- Definition of done is the same three pillars for every milestone, and
a milestone is not closed while one is missing:
- the chip certification families the milestone names run green in
the harness (
make chiptool-families), or are classified incertifiability.md; - the matter.js test files the milestone names are ported as Go tests and recorded in the test-porting register with its ratchet (milestone 2 creates both);
- the rows of
matterjs-comparison.mdthe milestone names move to ✅, or the gap moves toby_design.mdwith its reasoning.
- the chip certification families the milestone names run green in
the harness (
- Every milestone opens with a pin check. matter.js HEAD moves while a
milestone runs (the checkout is 52 commits past the pin
0ceb202dtoday); the first wave of each milestone lifts the pin, classifies the snapshot diff and regenerates, as concept v2 phase 6 did. A fact about matter.js quoted in this page is at the pin named in the milestone.
Baseline (2026-10-17, v0.6.0)¶
- 118 of the snapshot's 135 clusters served, 79 of its 91 device types
generated; the matter.js servers with hand-written logic number 25
(≥150 lines) and their depth here varies from a full port (ColorControl,
LevelControl, WindowCovering, OnOff, OTA) to none (Switch: the generated
definition and the wire type only, against
SwitchServer.ts486 lines and a 3 527-line test). - Controller role stages 1–6b landed; Joint Fabric (6c) deferred by decision (ADR 0020).
- Concept v3 phases 11–13 open:
crypto.Signer, window-open from the reference controller, the DCL snapshot and BLE commissioning (ADR 0024) — none of it in the tree yet. - Verified ecosystems: Apple Home, Home Assistant (owner, 2026-10-17); Google Home and Alexa await the owner at the device (ecosystem observations).
- matter.js's own tests, the proof for devices nobody has on the bench:
209
*Test.tsunderpackages/node/test(54 of them behaviour tests in 36 cluster directories, 49 node-level), 114 underpackages/protocol/test, 68 certification cases undersupport/chip-testing/test/cert. None of them is ported as such; the module's parity tests were written from the servers, not from these files.
Milestones¶
| # | Milestone | Version | Phases | Value, in one line |
|---|---|---|---|---|
| 1 | The commissioner in the field | v0.7.0 |
11–13 | a boxed Wi-Fi or Thread product is commissioned by this module and handed on to Apple Home |
| 2 | The test foundation | v0.8.0 |
14–16 | every later milestone is proved against matter.js's own tests; consumers test their bridge without hardware |
| 3 | Behaviour depth | v0.9.0 |
17–19 | the 25 matter.js servers with logic are ported line for line — remotes, thermostats, locks behave as the ecosystems expect |
| 4 | The embedding layer | v0.10.0 |
20–22 | a bridge is node/ + device/ + contract/; a new consumer is a day's work, not a port |
| 5 | ICD and the controller remainder | v0.11.0 |
23–25 | battery devices stay reachable; the controller pushes firmware and uses TCP |
| 6 | Completeness | v0.12.0 |
26–27 | 135/135 clusters, 91/91 device types, every matter.js row of the comparison closed |
| 7 | BLE on the device side | v0.13.0 |
28 | a node of this module without a network is commissioned over BLE, as matter.js's nodejs-ble node is |
Milestones 2 and 3 are the owner's order of 2026-10-17: the test foundation first, because milestones 3–7 are proved on it; the behaviour depth before the embedding layer, so that the API frozen in milestone 4 has seen what Switch, Thermostat and ICD need from it.
Milestone 1: The commissioner in the field¶
Detail: concept v3 (German, in the history since the milestone closed) and ADR 0024.
| Phase | Content | Proof |
|---|---|---|
| 11 | crypto.Signer behind the controller CA; window-open from the reference controller (multi-admin from our side); the embedded DCL snapshot with modes dev / prod / none; the status table in the README |
in-repo tests; the hand-over to Apple Home in the manual test plan |
| 12 | BTP (transport/btp), controller/ble, the network steps of the commissioning flow (Wi-Fi credentials, Thread operational dataset, ConnectNetwork), a simulated Wi-Fi driver in the reference daemon; the decision where the Thread dataset comes from (operator-supplied, or the Thread border-router client of milestone 5) |
BTP vectors against BtpSessionHandlerTest.ts (1 526 lines) and BtpCodecTest.ts; network steps against the daemon |
| 13 | the sub-module ble/bluez, pair --ble, the btvirt job with chip's all-clusters-app |
BLE end to end, virtual; a real device in the test plan |
Value. A commissioner that only reaches devices already on the network is a laboratory tool. After this milestone a host built on this module commissions a boxed product and hands it on. Consumers: the reference controller, a loom installation acting as commissioner.
Done when. The comparison rows BLE/BTP and DCL move to ✅ for the
controller role; TC-DD and TC-SC BLE cases are classified; the
btvirt job passes.
Status 2026-10-18. Done, released as v0.7.0: phases 11–13 landed
(pull requests 143, 144, 147, 149); the ble-virtual job is green against a
matter.js node (ADR 0024 §5 as amended); the BLE and Thread cases of DD and
SC are classified out of scope for the device side
(certifiability). The field proof with a boxed
product is the owner's manual test plan.
Milestone 2: The test foundation¶
Detail: concept v4 (German), written 2026-10-18 when the milestone opened, in the history since it closed.
Status 2026-10-18. Phase 14 done, released as v0.7.1 (pull requests 151–154): pin 862ad75f, the upstream fixes classified, the test-porting register (134 planned on its ratchet) and the citation guard (266 drifts on its ratchet) in CI. Phase 15 done, released as v0.7.2 (pull requests 157, 158, 160): every time source of the module behind clock.Clock, clocktest.Fake after MockTime and the no-raw-time guard; the public fabrictest package — in-process devices and controllers on an in-memory network and one clock, after MockSite — with the controller's own tests on it and the testing-in-process guide. Phase 16, first wave (brief BH, pull request 164), released as v0.7.3: the port of packages/protocol/test — 987 cases in Go, every missing case with its reason, 13 files planned with an until, the fixes the ports found and the X.520 DN gap registered first among eighteen findings; the second wave (brief BI, pull request 168), released as v0.8.0: the port of packages/node/test/node onto fabrictest — device side and controller side, every missing case with its reason, 69 files planned on the ratchet (the four node files left with until M5 phase 25), 22 fixes the ports found, the controller's Restart/CommissionNode/Discover in fabrictest. Milestone 2 is closed with v0.8.0 (2026-10-19).
| Phase | Content | Proof |
|---|---|---|
| 14 | pin lift to matter.js HEAD (snapshot diff classified, generators, citations re-checked, chipdm-check green); the test-porting register notes/parity/matterjs_tests.md: every *Test.ts under packages/node/test and packages/protocol/test mapped to the Go test(s) that port it or to a classified exclusion (controller-only at this stage, JS-runtime-only, Joint Fabric), with a ratchet guard like reachability-check |
the guard fails when a matter.js test file is unmapped |
| 15 | the public test package (working name fabrictest, next to bridgetest and endpointtest): an in-process node, an in-process controller, the commissioned pair (MockSite.addCommissionedPair), a fake clock injected through one Clock port instead of the five ad-hoc fakes in the tree today (MockTime: advance, resolve, yield), the wire-path helpers (interaction.read/write/invoke/subscribe of node-helpers.ts) |
the module's own suites run on it; the concurrency guide names it |
| 16 | the protocol ports: packages/protocol/test (114 files — MRP, MessageExchange, MessageReceptionState, SessionManager, SecureSession, PASE/CASE pairing, MessageCodec, Certificates, DeviceAttestationValidator, Bdx, TcpChannel, Mdns*, FailsafeContext …) and the node-level suite packages/node/test/node (49 files — the Attribute/Command/Event response tests, the commissioning tests, ServerSubscription) as Go tests against this stack |
register at 100 % for the two trees, behaviour tests excepted (milestone 3) |
Value. The owner's premise: the bench will never hold every device, so the proof has to come from matter.js's tests — which encode the ecosystem fixes matter.js collected. The package of phase 15 is also the first piece of the embedding layer: a consumer (loom, the HA bridge, a 2mqtt daemon) tests its own device mapping against an in-process controller, with no chip-tool and no radio. Consumers: every one.
Done when. The register exists, the guard holds, phases 15–16 are at 100 %, and the per-package coverage floors did not drop.
Milestone 3: Behaviour depth¶
The 25 matter.js servers with hand-written logic, each audited against its Go counterpart with the matter.js test file ported as the proof. Grouped by the surface they serve; the figures are matter.js lines of server / of test.
| Phase | Content | Proof |
|---|---|---|
| 17 | the device surface loom and the HA bridge map to: Switch — a real server (486 / 3 527: the multi-press and long-press state machine, SwitchServer.ts), Thermostat — presets, schedules, atomic writes (1 790 / 1 776 in six files), DoorLock (1 628 / 1 946), WindowCovering (654 / 145), OnOff (343 / 352), LevelControl (875 / 944), ColorControl (2 046 / 610), Identify (156 / 187), ServiceArea (300 / 234) |
SWTCH, TSTAT, DRLK, WNCV, OO, LVL, CC, I families green; the nine test files ported |
| 18 | the system set: Descriptor (303 / 685), GeneralDiagnostics (631 / 235), AccessControl (775 / 619), OperationalCredentials (632 / 360), GroupKeyManagement (618 / 209), Groups (232 / 100), Groupcast (938 / 2 243), Binding (214 / 2 455), BasicInformation (213 / 214), GeneralCommissioning (285), AdministratorCommissioning (267), ScenesManagement (1 109 / 99) | ACL, ACE, DESC, DGGEN, OPCREDS, GRPKEY, G, S, BIND, BINFO, CGEN, CADMIN green; the test files ported |
| 19 | OTA and the remaining behaviour tests: OTA requestor and provider (1 436 + 697 / 2 819 + utils), and the behaviour tests of the generated servers (boolean-state, fan-control, measurement, occupancy-sensing, power-source, hepa-filter-monitoring, concentration-measurement, energy-evse-mode, rvc-operational-state, commodity-tariff, thermostat-user-interface-configuration, time-format-localization, unit-localization, window-covering) | SU green; the register at 100 % for packages/node/test/behaviors |
Value. This is where the ecosystems judge a bridge: a HomeMatic remote
that double-clicks, a thermostat whose presets Apple Home writes
atomically, a KeyMatic that reports its lock state the way DoorLockServer
does. Consumers: loom (switches, thermostats, locks, covers), the HA bridge
(the same surface from the Home Assistant side).
Done when. The behaviour-test register is at 100 %, the named families are green, and no ◐ remains in the comparison's cluster section for a cluster with a hand-written matter.js server.
Status 2026-10-19. Phase 17 planned: concept v5 (German) with the owner's decisions 0.1–0.4 — a real Switch server after SwitchServer.ts beside the host-gesture path, the full Thermostat (presets, atomic writes, occupancy) and DoorLock (users, credentials, schedules) scope as selectable features, four waves in two parallel pairs (BJ ∥ BK, BL ∥ BM) as v0.8.1–v0.8.4, starting after v0.8.0. Phase 17a (brief BJ, pull request 170) released as v0.8.1: the behaviour-test ports of OnOff, LevelControl, ColorControl, Identify, WindowCovering and ServiceArea — 66 of 76 cases, three fixes the ports found (window-covering enum conformance at construction, the level-control quiesce, the colour-temperature coupling floor), fabrictest.Endpoint; Phase 17b (brief BK, pull request 171) released as v0.8.2: cluster/switch, the port of SwitchServer.ts with matter.js's position input beside the kept gesture path, the reference daemon's button on positions, SwitchServerTest.ts 69 of 69 cases, TC-SWTCH-2.5 passing; Phase 17c (brief BL, pull request 175) released as v0.8.3: the Thermostat's Presets, atomic writes and Occupancy after ThermostatServer.ts, AtomicWriteHandler.ts and AtomicWriteState.ts, the seven thermostat test files 70 of 82 cases, presets persisted on the store (migration v5), KeypadLockout persistence, the reference daemon's thermostat in the shape of matter.js's CHIP test instance with TC-TSTAT-4.2 passing; Phase 17d (brief BM, pull request 176) released as v0.8.4: the DoorLock server after DoorLockServer.ts, LockAuth.ts and LockSchedule.ts with every feature selectable (PIN, RID, FGP, COTA, USR, WDSCH, YDSCH, HDSCH, DPS, UBOLT), users, credentials and schedules persisted on the store (migration v6, credential data encrypted at rest), the DRLK families against the reference daemon's lock. Phase 17 is complete. Phase 18 planned: concept v5 Teil E with the owner's decisions 18.1–18.3 — Opus implements all four waves, Fable reads every cited fix line of the security servers against the pin before the merge; AccessControlCustomExtensionTest.ts deferred to milestone 4; four sequential waves v0.8.5–v0.8.8 (18a Descriptor, GeneralDiagnostics, BasicInformation, Groups, ScenesManagement and the protocol group tests; 18b AccessControl, OperationalCredentials, GroupKeyManagement and the system commissioning tests; 18c Groupcast; 18d Binding); brief BN (18a) written.
Milestone 4: The embedding layer¶
Detail: written when milestone 3 is closed, together with the concept of the go-ha-matter-bridge and the adapter module, because the three shape each other.
| Phase | Content | Proof |
|---|---|---|
| 20 | the node/ facade complete: everything loom reaches below it today (bridge, store, secure/*, mdns, im) is reachable through node/, device/ and contract/, or is documented as an advanced seam; the declarative device kit — a bridged device from a device type, its values and its command handlers in one declaration, with attribute change propagation, transitions and the device-type validator behind it; an apidiff guard from this milestone on, so that a break is announced before it ships |
loom's import list shrinks to the facade; the reference bridge is rewritten on the kit |
| 21 | observability and persistence as ports: slog attribute conventions, a metrics hook, the diagnostic event ring; the Store seam proved by a second implementation (in-memory, for tests and small daemons); hardening a daemon in the field needs (session and subscription limits documented, PASE attempt limit after §5.5, fuzz targets for the controller decoders) |
the persistence and concurrency guides describe the ports; make fuzz covers the new targets |
| 22 | packaging and the cookbook: container image and systemd unit for the reference apps, arm64, the "embed in a daemon" guide written against a go-*2mqtt project; the concept documents of the go-ha-matter-bridge and the adapter module |
a 2mqtt daemon exposes one device type end to end on the kit |
Value. The diamond-standard claim lives here: matter.js asks a host to learn behaviours, agents and transactions; this module should ask for a device type and a handful of callbacks. Consumers: every one; the HA bridge and the 2mqtt family cannot start before it.
Done when. The comparison's "device model" and "runtime" sections have no ◐ left except the behaviour-layer idiom row; loom and one 2mqtt daemon build on the facade alone.
Milestone 5: ICD and the controller remainder¶
| Phase | Content | Proof |
|---|---|---|
| 23 | the ICD server (IcdManagementServer.ts 838 + IcdMode.ts 152: SIT / LIT, the check-in sender, UserActiveModeTrigger, the registration table), mounted where the device type asks for it; the check-in protocol (protocol/src/icd, 1 162) |
ICDM and ICDB families; IcdManagementServerTest (1 415) and the eight protocol/test/icd files ported |
| 24 | the controller side: IcdClient (1 140), the check-in receiver (FabricIcd.processCheckIn), peer wakefulness, the sustained subscription parking on a LIT peer and re-subscribing when it wakes (SustainedSubscription.ts 672 / test 1 712), RebootResubscribeArmer |
the module's controller holds a subscription to the module's own LIT node across its idle cycles |
| 25 | the controller remainder: the rest of OTA push from the controller (ota serve --announce sends AnnounceOTAProvider today; writing DefaultOTAProviders and the provider's per-node consent record do not exist), the TCP initiator in controller/, the Thread border-router client (packages/thread-br-client: discovery, dataset codec, MeshCoP / OTBR diagnostics) if phase 12 chose it as the dataset source |
ClientNodeTcpTest, the peer/ tests ported; the reference controller updates the reference daemon over the air |
Value. The battery sensor, lock or button commissioned over BLE in milestone 1 is an ICD; without this milestone the controller loses it at its first idle period. Consumers: the controller role; the device side of loom and the HA bridge is mains-powered and gains the server only for completeness.
Done when. The comparison's ICD and CASE-initiator rows are ✅; the ICDM / ICDB families are green or classified.
Milestone 6: Completeness¶
| Phase | Content | Proof |
|---|---|---|
| 26 | the 15 clusters matter.js serves as generated stubs, through the generated default server: Camera (7), TLS (2), Thread and network infrastructure (4), AmbientContextSensing, WaterTankLevelMonitoring; WebRtcTransportRequestor with its logic (224 / 605) as the one camera-side server matter.js hand-writes; the 12 device types (Camera, SnapshotCamera, FloodlightCamera, CameraController, VideoDoorbell, AudioDoorbell, Intercom, Chime, CastingVideoClient, ThreadBorderRouter, NetworkInfrastructureManager, …); Thread network diagnostics |
chipdm-check green; every snapshot device type in a devicegen list |
| 27 | the comparison swept: every remaining ◐ / ○ row is ✅ or a by_design entry; the findings register empty; the test-porting register at 100 % for packages/node/test (endpoint, validation, storage) |
the three registers agree |
Excluded by decision, restated so they are not re-opened by accident:
Joint Fabric (two stub clusters and no behaviour at the pin; deferred
past v1.0.0, ADR 0020 stage 6c), ARL / managed aggregator (no
matter.js logic exists, BD-Matter-ARL-NotMounted), the Thread radio
(hardware), certification itself (ADR 0011).
Value. The claim in the first sentence of this page becomes checkable: 135/135, 91/91, no row of the comparison older than its milestone. No consumer asked for a camera, which is why this is milestone 6 and not 2.
Milestone 7: BLE on the device side¶
| Phase | Content | Proof |
|---|---|---|
| 28 | the peripheral role of BTP (shared with milestone 1's central), ble/bluez as GATT peripheral, the commissionable advertisement over BLE, NetworkCommissioning with the Wi-Fi feature on a Linux host (the wpa_supplicant driver behind a host port) |
the reference daemon on a Pi without a cable is commissioned by the reference controller and by Apple Home over BLE |
Value. matter.js's nodejs-ble node does it, so by the premise this
module does too; the owner's decision of 2026-10-17 reopens ADR 0024's
device-side exclusion as a late milestone. Consumers: a future Go device
without a network at first boot — none of the current bridges.
Done when. The comparison's BLE row is ✅ for both roles; the DD and SC BLE cases run.
Standing work, outside the milestones¶
- Pin lifts open every milestone (above).
- The chip harness keeps running on every wave that moves the daemon's composition or a FeatureMap; a wave that does not is said so in its report.
- The findings register takes every gap an audit finds; closing one removes it. Ecosystem findings go to the observations page.
- Google Home and Alexa runs happen when the owner is at the device; their findings enter the register and may re-order a milestone's phases, not the milestones.
- Security paths (
secure/,commissioning/, ACL, the four hand-written core servers) are reviewed by the strongest model whatever the milestone, asCLAUDE.mdassigns them.
How a phase is planned¶
A phase gets its detail document when it is next, not before: German,
under docs/ while it is current (as concept v3), moved to
docs/history/ when its milestone closes. It names the matter.js files
at the pin with line ranges, the Go packages it touches, the decisions it
needs from the owner (asked as a wizard with a recommendation), the waves
with one agent each, and the three pillars it has to show. An architecture
decision or a deliberate divergence inside it becomes an ADR or a
by_design entry in the same pull request. The adapter module and the
go-ha-matter-bridge get their own concept documents in their own
repositories, written in milestone 4.