go-fabric is a pure-Go implementation of the Matter protocol, packaged as
a library a host application embeds. On the device side it brings the
TLV codec, the Interaction Model, PASE and CASE session establishment, MRP
over IPv6 UDP and TCP, DNS-SD advertisement, the cluster servers a bridge
needs (including the media clusters, the OTA requestor and provider, and
DiagnosticLogs over BDX), and the endpoint assembler that turns a host's own
device list into a Matter topology. The host keeps its device model; this
module owns the wire format and hands back the port contracts (contract/)
a device implements to appear as a bridged endpoint. On the controller
side (controller/, ADR 0020)
it is a full commissioner: discovery, attestation (with the CSA Distributed
Compliance Ledger, opt-in), NOC issuance, the client interaction and a typed
image of a remote node, bindings and the group sender —
examples/reference-controller drives it from the command line. It
implements the Matter specification (currently 1.6.1) and follows
matter.js as its reference
implementation — not a translation of its TypeScript, but its behaviour.
matter.js is our executable specification: where its production-hardened
behaviour departs from the spec text, we follow matter.js and record the
departure; the spec still defines what is mandatory. Cluster IDs, revisions,
attribute IDs, constraints and wire shape are mirrored from matter.js HEAD
rather than transcribed from the specification, and the extracted matter.js
element model ships embedded so parity is a test, not a claim.
As of v0.22.2 (2026-11-12); the starting point of the milestones is the roadmap's baseline of 2026-10-17.
| Area | State |
|---|---|
| Device side | all 135 clusters of the matter.js snapshot served, all 91 device types generated (comparison, roadmap) |
| Controller side | full commissioner (BLE, Wi-Fi and Thread network steps, keys behind crypto.Signer, the embedded DCL snapshot), controller role stages 1–6c, Joint Commissioning after connectedhomeip (comparison) |
| BLE | controller: done with milestone 1 — ble/bluez on Linux, ble/esphome through ESPHome Bluetooth proxies, pair --ble, proven against a matter.js node over virtual controllers; device: node.Network.BLE with ble/bluez as the GATT peripheral on Linux, commissioned over BLE onto Wi-Fi (guide, milestone 7); proven by chip-tool under chip's BlueZ and wpa_supplicant mocks (make chiptool-ble-wifi) and by the weekly btvirt job in both directions, the hardware test follows |
| Wi-Fi | device: node.Network.WiFi.Driver joins the network a commissioner provisions; wifi/wpasupplicant is the driver on Linux (wpa_supplicant over D-Bus, a nested module), examples/ble-wifi-light the example node with BLE and Wi-Fi (operations, milestone 7) |
| Thread | a Thread Border Router over an OTBR and ThreadNetworkDirectory on the device side (guide); the commissioner provisions datasets; no Thread radio (comparison) |
| ICD | shipped with milestone 5: the device side (node.Config.ICD, Check-In) and the controller's ICD client (guide) |
| Certification | not pursued; certifiability is a goal (certifiability) |
| Verified ecosystems | Apple Home, Home Assistant; Google Home and Alexa pending (roadmap) |
This is an independent open-source implementation. It is not certified by
the Connectivity Standards Alliance, and it is not a project of the
Alliance. No warranty or assurance is made about rights that may be required
to implement it. Using it does not certify anything: it does not assure
compliance with the Matter specification, and it does not convey any right to
describe a device, product or service built with it as Matter compliant,
certified or similar. Certification requires membership in the Alliance and
compliance with Alliance policy; so does any use of Alliance trademarks and
logos. See licenses/NOTICE-matter.js.txt,
reproduced from matter.js, which states the same terms for the project this
one is ported from.
"Matter" and the Matter logo are trademarks of the Connectivity Standards
Alliance. Their use — in a product, project or repository name as much as
on a device — is reserved to Alliance members and certified products under
the Connectivity Standards Alliance Trademark and Logo Usage Guidelines
and Terms (https://csa-iot.org/resources/governing-documents/; the
terms are restated in licenses/NOTICE-matter.js.txt:
"Only the Alliance and its members may use Alliance trademarks and logos,
including, without limitation, the Matter trademarks and logos"). This
project is neither, so the mark is not part of its name or of the names of
the repositories built on it (go-ha2fabric, go-hamapping); the word
appears only descriptively — a library that implements the Matter
specification, a bridge for Matter controllers — and never to suggest
certification or endorsement. The name go-fabric is the specification's
own term for a commissioned network of nodes.
Certification is not pursued by this project. Certifiability is a goal: the
module is meant to let a product built on it pass certification, and the CSA
certification test families run against the reference daemon to hold it
there. What passes, what does not and why, and what a product owner must
supply that a library cannot is in
docs/certifiability.md
(ADR 0011).
The site (rendered from docs/) is at
https://sukramj.github.io/go-fabric/; the API reference is
pkg.go.dev.
docs/index.md |
Where to start, by role: embedding the device side, driving the controller, evaluating the architecture. |
docs/architecture.md |
The two roles, the layers, the request path, the package map and the scope boundaries that are decisions rather than backlog. |
docs/getting-started/ |
A minimal device host and a minimal controller, each a compiling example. |
docs/guides/host-view.md |
A bridge as a host writes it — two declared devices, the change's origin, the device's lifecycle — every line explained. |
docs/guides/ |
Host ports, concurrency, persistence. |
docs/security.md |
The security model of both roles. |
docs/matterjs-comparison.md |
Feature-by-feature against matter.js, with a verdict on whether each gap is worth closing here. |
docs/certifiability.md |
What the CSA certification families say about this module, family by family. |
docs/matter-parity-contract.md |
What parity means and which standing guards enforce it. Read before your first change. |
docs/matter-ecosystem-observations.md |
What Apple Home, Google Home and Alexa were actually observed to do. |
docs/adr/ |
Architecture decision records. |
docs/history/ |
Earlier planning concepts (German), kept for their reasoning. |
notes/parity/by_design.md |
The catalogue of intentional divergences from matter.js. |
notes/parity/matter_behaviour_findings.md |
Known behaviour gaps that are not intentional, as scoped fix packages. |
examples/reference-bridge is the reference
daemon: a bridge on this module, the binary the chip-tool suite runs
against. examples/reference-controller
is the controller role as a command-line tool — pair a node by its QR or
manual code, read, write, invoke, subscribe, provision groups and bindings,
serve an OTA image — with its fabric kept in one SQLite file; its
README lists every command.
The module carries its own SemVer lane, independent of any host that embeds it.
Every exported identifier outside internal/ is public API. The module
already draws that line for itself: internal/ holds three kinds of
package, and Go makes all of them unimportable from outside — the one-way
seams through which the facade, the test-support packages, the reference
programs and the module's own tests reach package internals (bridgeseam,
channelseam, coreseam, deviceseam, exchangeseam, fabrictestseam,
nodeseam, surfaceseam, udpseam, inflight, clockvar); shared
internals behind them (serialqueue, forward, netwatch, valuejson);
and test support (paritytest, chipdm, the chip-tool suite chiptool). Nothing else is private by convention, so nothing
else may be changed as if it were.
Depend freely on bdx/, binding/, bootid/, bridge/, client/…, clock/, cluster/…,
commissioning/, contract/, controller/…, device/, diag/, diagevent/,
eligibility/, endpoint/ (including endpoint/sqlitestore), groups/,
icd/, im/, im/subscription, logring/, mdns/, node/,
parity/, schema/, secure/…, store/, surface/, thread/, tlv/ and transport/…. The
device layer device/ and the node facade node/ are public API since
v0.3.0; that release freezes nothing, and the v1.0.0 freeze is still
ahead.
bridge/bridgetest, endpoint/endpointtest, controller/ble/bletest,
clock/clocktest, surface/surfacetest, store/storetest and fabrictest are public API on the same terms as the
rest, not a lower tier. They exist for the reason
net/http/httptest exists — a consumer's tests need to place a bridge in a
state the production API is not shaped to reach — and a consumer's test suite
is as expensive to break as its production code.
Three things outside internal/ are still not API. conformance/ is this
module's own suite, shaped for the tests in this repository rather than for a
consumer -- one of its three exported identifiers takes a *testing.T, and the
other two exist to serve it. examples/reference-bridge,
examples/reference-controller and everything under script/ are programs
and the packages that serve them (examples/reference-controller/cli, the
guards and generators' own packages under script/) — runnable, not a
library. Copy from them; do not build against them.
One value-level caveat on parity/ and schema/: their Go surface is
covered by everything below, but the data behind it is a matter.js HEAD
extract. Cluster revisions, attribute IDs and constraint values move when
upstream corrects them. That is a behaviour change, not a signature change,
and it is announced the same way — see the exception under
Pre-1.0.
An identifier on its way out is marked, kept working, and only then removed.
- Marked. Its doc comment gains a paragraph beginning
Deprecated:, naming the replacement and the release the removal becomes permissible in. That exact prefix is whatgopls,staticcheck(SA1019) and the Go documentation renderer key on, so a consumer sees it in an editor without reading a changelog. - Kept. A deprecated identifier keeps its exact behaviour for the whole window. It is not degraded, does not start logging, and does not become a no-op. A rename lands the new name first and leaves the old one delegating to it.
- Removed no earlier than the second minor release after the one that
marked it. Deprecated in
v0.4.0means removable inv0.6.0, not before. Consumers who upgrade minor-by-minor therefore see at least one release in which their code still compiles and the warning is already visible.
Suspended until a second consumer or v1.0.0 (owner decision,
2026-10-21). While the reference consumer is the module's only one and
migrates in one step, a replaced identifier is removed in the next minor
release, without the window, once
docs/guides/migrating-to-the-facade.md
names its replacement — a removal without its line there is a defect. The
removal is announced under ### Removed as below. The window returns, as
written above, with the second consumer or with v1.0.0, whichever comes
first.
Currently deprecated, each with its replacement and the release its removal becomes permissible in:
| Deprecated | Replacement | Removable in |
|---|
A patch release (v0.x.y) never removes or renames a public identifier and
never changes a signature. Deprecations may be marked in a patch release;
they are only ever acted on in a minor one.
The module is at v0; v0.1.0 is its first tag. Before it, main was the
only consumable version and could break in any commit, which is why consumers
tracked a pseudo-version. main is still consumable the same way — see
CONTRIBUTING.md — but a tagged version is what a consumer should pin.
SemVer permits a v0 module to break on any minor bump. This module does not use that permission as a default. The window
above applies at v0 exactly as it would at v1 — outside its suspension
until a second consumer or v1.0.0 (above): a breaking change is a
minor bump, never a patch, and wherever the old identifier can be kept
working it is kept working for the full window.
v0.3.0 is the first release to act on the window: it removes the
identifiers v0.2.0 marked for removal in it, each listed with its
replacement under ### Removed in CHANGELOG.md.
Two changes bypass the window, and both are named as such in their entry:
- A matter.js parity correction. matter.js HEAD is the gold standard for every cluster ID, revision, attribute ID and wire shape in this module. When upstream corrects one, this module follows it in the next release without a window. Holding a value stable because it is stable would mean knowingly shipping a wrong one, and the failure it produces is a silent pairing abort in a controller nobody can attach a debugger to.
- A security fix that cannot be made without an API change. It ships in
the next release, marked
### Security.
What v0 does not excuse: an unannounced break. Every one of these lands
in the changelog before a consumer meets it.
CHANGELOG.md is the single place. It follows
Keep a Changelog, so the headings
already carry the meaning:
### Deprecated— the release that marks an identifier. The entry gives the full import path and name, the replacement, and the earliest release the removal may happen in.### Removed— the release that removes it. The entry repeats the replacement, so a consumer who skipped the deprecation release still finds it here rather than in a diff.### Changed— behaviour that moved under an unchanged signature. A parity correction lands here and says which matter.js element changed.### Security— a fix that forced an API change out of band.
A release note carries, for the version it names: every entry from those four headings, and an explicit statement of whether upgrading requires a source change. "No source change required" is a claim worth making when it is true, because it is the only sentence most consumers need to read.
The rule behind all of the above: a break that appears only in a commit message, a doc comment or a diff has not been announced. If it is not in the changelog, it does not ship.
A release pull request (head branch release/*) is also the gate of the
certification run: it runs every chip-tool job and certification family the
nightly run does, and cannot merge with one of them red
(Certifiability).
A guard holds the rule: make apidiff compares every public package with
the last release tag (golang.org/x/exp/cmd/apidiff, a tool dependency of
the module) and prints the incompatible changes, and make apidiff-check,
a CI job, fails when one of them is not named under [Unreleased] in
CHANGELOG.md (### Changed, ### Removed or ### Deprecated).
Apache License 2.0 — see LICENSE and NOTICE
(ADR 0023: the same licence as
matter.js and connectedhomeip, with their patent grant; v0.4.0 and earlier
were released under MIT and stay so). Upstream notices and the dependency
licences are recorded in
THIRD-PARTY-NOTICES.md. Contributions need
the contributor licence agreement in CLA.md, agreed once per
account in the first pull request.