A Rust implementation of the Matter protocol — controller side.
Status: feature-complete against Matter 1.4 and published to crates.io (
matter-controller0.18.1; the lower-level crates0.3.x–0.7.x). The controller commissions and controls real Matter devices over IP, and over BLE onto Wi-Fi or Thread — all validated against hardware, not just tests.
matter-rust is a workspace of small, focused crates that together let a Rust
application act as a Matter controller — commissioning devices, establishing
secure sessions, and reading, writing, invoking, and subscribing to clusters.
The Rust ecosystem also has rs-matter,
which began on the device side and has been adding controller and
commissioner support since early 2026. matter-rust covers the controller side
only, as small crates you can adopt one at a time.
- Not a Matter device implementation — see
rs-matter. - Not a smart-home platform — this is a protocol library.
- Not a fork of
rs-matter. It is developed independently, with different design choices. - Not a quick MVP. Matter is a security-sensitive protocol. Cutting corners here causes broken homes and leaked credentials. The v1.0 API took roughly eighteen months, as planned, and the pace has not been compressed since.
matter-rust/
├── crates/
│ ├── matter-codec/ # M1 — TLV encode/decode
│ ├── matter-cert/ # M2 — Matter certificate format
│ ├── matter-crypto/ # M3, M4 — PASE (SPAKE2+), CASE (SIGMA), ICD check-in
│ ├── matter-transport/ # M5 — UDP, mDNS, framing, MRP
│ ├── matter-interaction/ # M7 — Interaction Model message framing
│ ├── matter-commissioning/ # M6 — full commissioning state machine
│ ├── matter-clusters/ # M7 — typed cluster definitions (generated)
│ ├── matter-ble/ # M9 — BLE transport: BTP engine + central role
│ ├── matter-ota/ # M9 — OTA Software Update Provider
│ ├── matter-bdx/ # M9 — BDX (bulk data exchange, carries OTA images)
│ └── matter-controller/ # M8 — high-level controller API
├── test-vectors/ # binary fixtures captured from matter.js / chip / spec
├── examples/ # how to use the crates
├── xtask/ # codegen, vector capture, integration harness
└── docs/ # protocol notes, runbooks, spec references, ADRs
Each crate is independently versioned and independently publishable, and depends
only on the layers below it — so a consumer who only wants TLV decoding can take
matter-codec without pulling in commissioning, BLE, or a Tokio runtime.
The work is sequenced so each milestone validates the previous.
| Milestone | Crate | Goal | Target |
|---|---|---|---|
| M0 | — | Repo, workspace, CI, roadmap | ✓ done |
| M1 | matter-codec |
Matter TLV encode/decode | ✓ done |
| M2 | matter-cert |
Matter certificate parsing and chain validation | ✓ done |
| M3 | matter-crypto v0.1 |
PASE / SPAKE2+ (commissioning session establishment) | ✓ done |
| M4 | matter-crypto v0.2 |
CASE / SIGMA (operational session establishment) | ✓ done |
| M5 | matter-transport |
IPv6 UDP, mDNS discovery, message framing, MRP | ✓ done |
| M6 | matter-commissioning |
End-to-end commissioning of a real Matter device | ✓ done |
| M7 | matter-clusters |
Generated cluster definitions (OnOff, Level, …) | ✓ done |
| M8 | matter-controller |
High-level controller API. v1.0. | ✓ done |
| M9 | (all) | Completeness against Matter 1.4 — see below | ✓ done |
Everything the original roadmap deferred past v1.0 has since landed:
- BLE commissioning (
matter-ble) — BTP engine + central role, commissioning a factory-fresh device onto Wi-Fi or Thread over Bluetooth. - Thread network commissioning —
AddOrUpdateThreadNetworkfrom an operational dataset, through a border router. - OTA (
matter-ota+matter-bdx) — this controller can act as an OTA Provider: announce, serve an image over BDX, and drive Apply. - Multi-admin, ACL, groups — fabric management,
AccessControl, and group messaging including multicast. - ICD (intermittently-connected devices), TimeSync, Binding.
- Full Interaction Model — events, chunked reads, subscriptions with chip-faithful auto-resubscribe.
- 37 generated clusters, up from the initial ten.
Still deferred: Scenes Management, no_std (see
ADR 0002), React Native, and Matter 1.5+
features.
Known limitation: live BLE commissioning is Linux-only. On macOS, scanning
works but GATT does not: btleplug 0.12.0 / CoreBluetooth reject the CHIPoBLE
characteristics' descriptor discovery and C1 write with CBError.uuidNotAllowed,
and btleplug drops the errored delegate events so the operation used to hang.
That hang is now bounded to a fast, clear failure rather than an indefinite
stall, but macOS BLE commissioning still cannot complete — it needs an upstream
btleplug/CoreBluetooth fix. Root-cause writeup in
docs/superpowers/audits/ and TODO-1.0.md.
All three commissioning paths are validated against real hardware — see
docs/tested-devices.md for what was run, when, and how
it was independently confirmed.
| Path | Device | Proof |
|---|---|---|
| IP (already on-network) | TP-Link Tapo P110M | trace cross-verified against matter.js: 0 divergent |
| BLE → Wi-Fi | ESP32-C6 (esp-matter) | device joined the WLAN; OnOff toggled over CASE |
| BLE → Thread | ESP32-C6 + Raspberry Pi border router | device joined the mesh as a router; OnOff toggled over CASE |
# IP: a device already on your network
cargo run -p matter-commissioning --example commission_ip --features driver -- --help
# BLE → Wi-Fi, and BLE → Thread (run these from Linux; see the note above)
cargo run -p matter-controller --example commission_ble_wifi --features ble -- --help
cargo run -p matter-controller --example commission_ble_thread --features ble -- --helpIn-process loopback E2E tests prove each flow with no hardware; the runbooks under
docs/runbooks/ perform the real-device runs.
Attestation roots: commissioning any real device requires the PAA and CD trust
roots, and they do not come from the same place —
AttestationTrust::example_device_roots() bundles the CSA test roots plus the
CD signing roots for chip's example devices (including the esp-matter ESP32-C6 we
validate against nightly) — it is not the CSA production trust set, so an arbitrary
certified product will fail against it. Production callers pass real root
directories to AttestationTrust::from_dirs. The runbooks give the specifics.
Matter is well-specified but full of edge cases. We do not trust our own reading of the spec. For every protocol layer:
- Capture binary inputs and outputs from
matter.jsrunning a real operation. Save them undertest-vectors/. - Implement the Rust version.
- Assert byte-for-byte equality with the captured matter.js output.
We also use:
connectedhomeip(the C++ reference) as a second source, both for test vectors and as a live peer —just integrationruns the suite against chip'sall-clusters-app,lock-app, andevse-app,- official spec test vectors where they exist (notably PASE/SPAKE2+ in spec §3.10),
proptestfor roundtrip properties,cargo-fuzzfor parsers,- real Matter hardware (
docs/tested-devices.md).
Hardware earns its place here. The first BLE→Thread commission surfaced two bugs that every local test had passed: a scan filter that silently blinded the Linux Bluetooth backend, and a handshake ordering our own loopback peer was too forgiving to catch.
If Rust output diverges from matter.js, we are wrong by default. Investigate before changing the test.
- We do not implement cryptographic primitives. AES, ECDSA, ECDH, SHA, HKDF, HMAC
come from
ring(oraws-lc-rsif a switch becomes justified). We implement the Matter-defined protocols on top — SPAKE2+, SIGMA — not the math underneath. - Correctness is verified by byte-parity against matter.js and connectedhomeip, plus validation against real devices.
Published on crates.io — depend on the high-level crate directly:
[dependencies]
matter-controller = "0.18"Each crate is independently usable: take matter-codec alone for TLV, or
matter-cert for Matter certificates, without pulling in the higher layers.
The high-level API (M8, matter-controller) looks like:
use std::path::Path;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use matter_controller::{
AttestationTrust, CommandPath, FabricConfig, FileStore, MatterController, MatterTime,
ReadPath, Value,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let store = Arc::new(FileStore::new("controller-state.bin"));
let controller = MatterController::builder(store)
// Real devices need real roots — example_device_roots() verifies no real device.
.attestation_trust(AttestationTrust::from_dirs(
Path::new("paa-roots"),
Path::new("cd-roots"),
)?)
.build()
.await?;
// One-time only — `create_fabric` refuses a `fabric_id` it already has, so gate
// it on `fabrics()` being empty (a run that loaded an existing store has one).
if controller.fabrics().await?.is_empty() {
// `not_before` must be a real wall-clock time, backdated ~1h for device clock
// skew. The Matter epoch (`from_unix_secs(0)`) is rejected — see issue #111.
let now = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
let validity = (
MatterTime::from_unix_secs(now.saturating_sub(3600)),
MatterTime::NO_EXPIRY,
);
controller
.create_fabric(FabricConfig::new(
/* fabric_id */ 1,
/* rcac_id */ 1,
/* commissioner_node_id */ 1,
validity,
))
.await?;
}
let info = controller.commission("MT:...", Some("kitchen plug".into())).await?;
let node = controller.node(info.node_id);
// Commands: OnOff::Toggle on endpoint 1.
let toggle = CommandPath { endpoint: 1, cluster: 0x0006, command: 0x02 };
node.invoke(toggle, Value::Structure(vec![])).await?;
// Wildcard read: every OnOff attribute on endpoint 1.
let report = node.read(&[ReadPath::cluster(1, 0x0006)]).await?;
println!("read {} attributes", report.len());
// Attribute paths, event paths, min/max reporting interval (seconds).
let mut sub = node.subscribe(&[ReadPath::cluster(1, 0x0006)], &[], 1, 30).await?;
while let Some(event) = sub.next().await {
println!("{event:?}"); // reports and status changes
}
Ok(())
}See crates/matter-controller and the
matter.js migration guide.
See CONTRIBUTING.md. The short version:
- Issues labelled
good-first-issueandhelp-wantedare open. - Any PR that changes protocol behaviour must include matter.js test vectors.
- No
unwrap()orexpect()in library code. Test code is fine with a comment justifying the assumption.
rs-matter is the CSA-affiliated
Rust Matter project. It started with devices and, since early 2026, has been
building controller and commissioner support too. Like the C++ SDK, it aims to
cover every role. matter-rust is a separate, independently developed project
with a narrower scope: the controller side only, split into crates that can be
used on their own. The two projects do not currently coordinate.
Yes. The maintainer used AI assistance throughout. Every design decision was made by a human; every line was reviewed; correctness is verified against matter.js and (where applicable) spec test vectors. The code stands on its own merits — read it.