Z-Wave is a low power wireless protocol for home and light commercial control that runs in licence exempt sub-GHz bands (908.42 and 916 MHz in the US, the 868 MHz band in Europe) as a source routed mesh of up to 232 nodes. It is an open standard governed by the Z-Wave Alliance. A Z-Wave network is secure when every device joins with Security 2 (S2), the DSK is checked, legacy S0 is avoided and the controller is backed up.
This post sits next to our explainers on Zigbee, Thread and Matter, Home Assistant self hosting and how a smart lock is made. Here we stay on Z-Wave itself: history and governance, the radio and mesh, the network model, S2 and why S0 is legacy, SmartStart, Long Range, open source, and a lab with the open source Z-Wave JS driver.
Key takeaways
- Z-Wave runs below 1 GHz, away from the crowded 2.4 GHz band. Its PHY, MAC, SAR and LLC layers are published as ITU-T Recommendation G.9959; the in force edition is G.9959 (01/15).
- Classic Z-Wave is a source routed mesh of up to 232 nodes. The controller decides routes, and always listening devices relay frames.
- The Z-Wave Alliance calls itself an independent, member driven Standards Development Organization. It publishes the specification twice a year (2026A is the current in force package) and runs certification.
- S2 uses an elliptic curve Diffie Hellman (ECDH) key exchange on Curve25519, AES-128 in CCM mode for frames, and a Device Specific Key (DSK) with a 5 digit PIN to prove you are including the device in your hand.
- S0 is legacy: weak key delivery and no device authentication. Grant it only to old devices that support nothing else.
- SmartStart turns inclusion into a provisioning list: scan the QR code once, and the device joins itself with the classes you granted.
- Z-Wave Long Range (ZWLR) adds a star topology with a 12 bit address space (up to 4,000 nodes), up to +30 dBm and only the two strongest S2 classes. It joins through SmartStart only and coexists with the mesh.
- Back up the controller's non volatile memory (NVM), your keys and the provisioning list, or a dead USB stick means re-including every device.
Where did Z-Wave come from, and who governs it now?
Z-Wave began at Zensys, a Danish company building low cost wireless control for lighting and home automation, and later became part of Sigma Designs. For years its protocol lived inside one company's chips, which is why older articles call it proprietary. In April 2018 Silicon Labs completed its acquisition of the Sigma Designs Z-Wave business for $240 million, according to its own press release. The Z-Wave Alliance, formed in 2005 by a group of founding manufacturers according to its About page, ran interoperability certification throughout.
The radio layers had already been standardised by the ITU as G.9959, first published as G.9959 (02/12) and replaced by the edition approved on 13 January 2015, which added the SAR and LLC layers. The rest of the stack, including the network layer, security and the application command classes, now sits in a specification package the Alliance develops and publishes. The Alliance states that "Z-Wave is not proprietary" and that it is "an open standard governed by the independent, member-driven Z-Wave Alliance". Its specifications page says the package is updated twice a year (labelled xxxxA and xxxxB) and becomes "in-force" once implemented in the Certification Portal and the Compliance Test Tool (CTT).
The source code followed. In November 2022 the Alliance announced that the Z-Wave source code project was complete, with source and sample applications for members under the BSD 3 licence, contributions supervised by a new OS Work Group (OSWG), and quality enforced by a new mandatory Silicon and Stack certification. Its 2023 year in review reported 273 commits and more than 130,000 lines of changed code, plus work on a new security protocol version, Security S2 V2, and on Long Range frequencies for Europe.
Today the Alliance names three member silicon providers that build Z-Wave chips. A flaw in one stack therefore does not automatically sit in every Z-Wave product.
Timeline
| Date | Event | Source |
|---|---|---|
| 2005 | Founding manufacturers form the Z-Wave Alliance | Z-Wave Alliance, About Us |
| February 2012 | ITU-T G.9959 (02/12), PHY and MAC layers | ITU-T G.9959 page |
| October 2013 | G.9959 Amendment 1 | ITU-T G.9959 page |
| 13 January 2015 | G.9959 (01/15) adds SAR and LLC layers; in force today | ITU-T G.9959 (01/15) page |
| 18 April 2018 | Silicon Labs completes acquisition of the Sigma Designs Z-Wave business | Silicon Labs press release |
| September 2020 | Alliance announces the updated specification introducing Z-Wave Long Range | Z-Wave Alliance, ZWLR explainer |
| November 2022 | Source code project complete for members under BSD 3; OSWG formed | Z-Wave Alliance announcement |
| December 2023 | Year in review: 273 commits; work on Security S2 V2 and EU Long Range frequencies | Z-Wave Alliance year in review |
| February 2025 | Alliance outlines 2024B: User Credential command class, CTT 3.9.1, EU Long Range moving to 864 and 866 MHz | Z-Wave Alliance 2024B post |
| August 2025 | First Z-Wave Long Range end device for the EU announced | Z-Wave Alliance member news |
| 2026 | 2026A is the in force specification package | Z-Wave Alliance specifications page |
How does the Z-Wave radio work?
Z-Wave is a narrow band radio in the licence exempt short range device bands below 1 GHz. The ITU recommendation's title says it plainly: "Short range narrow-band digital radiocommunication transceivers". Each region has its own frequencies, which is why a European stick will not talk to a US lock.
Frequencies and data rates
G.9959 defines three data rates, R1, R2 and R3, and specifies FSK for R1 and R2, GFSK for R3, Manchester coding for R1 and NRZ for R2 and R3. The Alliance's Long Range explainer gives the US frequencies.
| Mode | Data rate | Modulation | US frequency |
|---|---|---|---|
| R1 | 9.6 kbit/s | FSK, Manchester coded | 908.42 MHz |
| R2 | 40 kbit/s | FSK, NRZ | 908.42 MHz |
| R3 | 100 kbit/s | GFSK, NRZ | 916 MHz |
| Long Range | 100 kbit/s | DSSS OQPSK | 912 and 920 MHz |
In Europe, classic Z-Wave uses the 868 MHz short range device band, and the Alliance's 2024B-3 update shifts Long Range from 864.4 and 866.4 MHz to 864 and 866 MHz. The Z-Wave JS driver's region list also covers Australia and New Zealand, Hong Kong, India, Israel, Russia, China, Japan and Korea, with separate "USA (Long Range)" and "Europe (Long Range)" settings.
Why sub-GHz? Lower frequencies travel further for the same power and pass through walls better than 2.4 GHz. The cost is bandwidth: 100 kbit/s is plenty for "door opened" and useless for streaming.
What the band rules say
In the US, 47 CFR 15.249 covers low power intentional radiators in 902 to 928 MHz and limits the fundamental to 50 millivolts per metre field strength, with harmonics at 500 microvolts per metre. Higher power Long Range products rely on other parts of the FCC rules, and each product is certified individually.
In Europe, the harmonised standard for short range devices from 25 MHz to 1,000 MHz is ETSI EN 300 220-2, which sets duty cycle and power limits per sub-band; the ETSI archive lists v3.3.1 as its newest version. The Alliance says the 2024B-3 frequency change was made "ensuring compliance with new ETSI standards". Never import a controller or device for a different region: it may be illegal to operate, and it will not interoperate.
Source routed mesh, repeaters and node limits
Classic Z-Wave is a mesh, but not the kind where every node decides. G.9959 describes the network layer as "a source routing protocol" that "allows an originator to reach a destination node via one or more repeaters", with a reactive discovery algorithm to find new routes. The controller keeps a picture of which nodes hear which, computes routes and puts the route into the frame; each repeater forwards to the next listed hop.
Mains powered, always listening devices (plug in modules, wall switches) repeat. Battery devices usually sleep and wake on a schedule, or listen on a short duty cycle (G.9959 calls these frequently listening nodes), which is how a battery lock answers quickly. Sleeping devices do not route for others.
Consequences in the field:
- Routes are only as good as the controller's knowledge. Move or unplug a repeater and frames follow stale routes until new ones are learned, so rebuilding routes after layout changes is normal maintenance.
- Route length is limited, so a large property needs repeaters in the right places, or Long Range.
- Routing is a security topic. A 2017 Computers and Security paper by Badenhop and colleagues, "The Z-Wave routing protocol and its security implications", analysed the routing layer. S2 protects the application payload end to end between controller and device, but routing and availability are a separate problem.
G.9959 states that each domain "may contain up to 232 nodes" and uses NodeIDs 1 to 232. The Alliance contrasts that 232 with the 4,000 nodes of Long Range.
What is the Z-Wave network model?
| Element | What it is | Why it matters for security |
|---|---|---|
| Home ID | A 32 bit network identifier (G.9959 calls it the HomeID) | Separates your network from a neighbour's; not a secret, visible over the air |
| Node ID | Each device's address (1 to 232 in the mesh; Long Range IDs start at 256 in Z-Wave JS) | Assigned at inclusion; kept until exclusion |
| Primary controller | The controller that created the network and assigns IDs | Holds network keys and routing tables; the crown jewels |
| Secondary controllers | A handheld remote or second gateway | Every extra controller is another holder of keys |
| End devices | Sensors, switches, locks, thermostats | Each is granted a set of security classes at inclusion |
| Command classes | Standardised application messages, such as Binary Switch, Door Lock, Notification, Firmware Update Meta Data | Define what a device can do and what a controller can tell it |
Inclusion and exclusion
Inclusion is how a device joins: put the controller into inclusion mode, then press a button on the device. The controller assigns a Node ID, and only then does security bootstrapping run. Z-Wave JS's documentation warns that "a common misconception is that an error causes the inclusion to be aborted"; the driver "only learns about a node after it was already included". A bootstrapping failure leaves the device in the network with lower security than you intended, unless you notice.
Exclusion is the reverse: the controller enters exclusion mode and the device is told to leave. Excluding a used device before including it is a common first step. A factory reset on the device alone leaves a dead entry on the old controller, which a "remove failed node" function cleans up.
Command classes and certification
A command class is a numbered set of commands for one function. The Alliance's specifications page says every device has a Device Type, a Role Type and a list of command classes, and the Certification Portal identifies the mandatory requirements. Because the application layer is standardised, a certified switch from one maker works with a certified controller from another. The 2024B package added a User Credential command class for access control, covering PIN codes, passwords, RFID, BLE, NFC, UWB and biometrics per user.
Two command classes matter for security: Security 2, which carries the S2 key exchange and the encrypted encapsulation, and Firmware Update Meta Data, which lets a controller push firmware images to a device over the air. That is a useful update path and one worth controlling.
How does S2 security work?
Security 2 replaced S0. Z-Wave JS's documentation says "modern Z-Wave devices are required to support Security S2" and recommends using it by default.
Security classes
S2 separates devices into classes, each with its own 16 byte network key, so a device holding only a lower key cannot read or forge traffic for a higher class.
| Class | Typical devices | Authentication at inclusion | On Long Range |
|---|---|---|---|
| S2 Access Control | Door locks, garage door openers | DSK checked with PIN | Yes |
| S2 Authenticated | Sensors, sirens, lighting, thermostats | DSK checked with PIN | Yes |
| S2 Unauthenticated | Devices with no printed PIN or QR code | None | No |
| S0 Legacy | Older locks and sensors without S2 | None, weak key exchange | No |
| None | No security support | Not applicable | Not applicable |
Z-Wave JS takes four classic keys (securityKeys) and two Long Range keys (securityKeysLongRange), each exactly 16 bytes, and its Long Range guide says both LR classes "must use a different security key than their Z-Wave Classic counterparts". Generate every key separately.
The key exchange: ECDH on Curve25519
When a device joins with S2, controller and device each generate a key pair and run ECDH. The Z-Wave JS crypto code shows X25519 key handling, an ECDH shared secret fed into AES-CMAC based derivation of a temporary CCM key, and AES-128 CCM for encryption. Curve25519 and X25519 are specified in RFC 7748, published in January 2016 as an informational RFC representing the consensus of the IRTF Crypto Forum Research Group; X25519 takes and returns 32 byte strings.
The temporary key protects the rest of bootstrapping, over which the controller sends each granted network key. A passive listener who records the exchange cannot derive the keys, the core improvement over S0. But ECDH alone does not tell you who you are talking to: an active attacker in range could sit in the middle. That is what the DSK is for.
The DSK and the PIN
Every S2 device has a Device Specific Key printed on the device, packaging or manual, often with a QR code. Z-Wave JS writes it as eight groups of five digits: aaaaa-bbbbb-ccccc-ddddd-eeeee-fffff-11111-22222.
During authenticated inclusion the controller shows the DSK it received with the first five digits missing, and you type those digits, the PIN, from the label. If they match, the key in the exchange belongs to the device in your hand. Z-Wave JS requires applications to label that input "PIN", "since this is what's printed on devices users will have", and each dialog has a 240 second timeout before inclusion aborts. Only Access Control and Authenticated use this check; S2 Unauthenticated gets encryption but no proof of identity.
Frame encryption: AES-128 CCM and nonces
After bootstrapping, S2 encrypts and authenticates commands with AES-128 in CCM mode, so a tampered or forged frame fails verification. CCM needs nonces that never repeat under one key, so S2 nodes keep nonce state per peer, with separate key material for multicast (the Z-Wave JS code derives "MPAN" keys for this). After a reboot nodes resynchronise before continuing, and replayed frames fail. For operators this mostly appears as occasional nonce messages in debug logs.
What S2 does not protect
S2 protects the confidentiality and integrity of the application payload. It does not:
- protect routing and topology information outside the encrypted payload;
- stop radio jamming, which affects every licence exempt wireless system;
- help devices included with S0 or no security; a network is only as strong as the weakest class you granted;
- protect the controller host, its web UI, MQTT broker or cloud connection, where many real smart home compromises happen.
Academic work has looked at those edges: fuzzing Z-Wave devices for implementation flaws (VFuzz, IEEE Access, 2022), anomaly detection for attacks that use legacy devices or unencrypted frames (ZMAD, IEEE Access, 2023), and formal models of the protocol (Braghin, Lilli and Riccobene, Computers and Security, 2023). The common thread is that mixed networks, unencrypted fallbacks and device bugs leave room around the cryptography.
Why S0 is legacy
S0 was Z-Wave's first security layer. It used AES-128 and one network key for every secure device. As published security research describes it, the weakness was key delivery: the network key travels to the new device encrypted under a temporary key that is fixed and well known, so anyone who records an S0 inclusion can recover the network key. S0 also cannot authenticate the joining device, and it needs an extra round trip per nonce.
Z-Wave JS treats S0 bootstrapping in an S2 capable network as a warning sign: its S0Downgrade reason "indicates a possible downgrade attack and MUST be exposed to the user". The one honest reason to grant S0 is an old device that supports nothing better. Z-Wave JS notes that some devices advertise S2 but do not answer the key exchange, so its UI keeps a "use only S0" option. Use it deliberately, close to the controller, and once.
What do SmartStart and QR code provisioning change for installers?
SmartStart turns inclusion from a live, two handed event into data entry. You add the device to the controller's provisioning list in advance, with its DSK and the classes you grant. In Z-Wave JS's words, when a listed node "announces itself, it will automatically be included in the network within a couple of minutes". Scanning the QR code fills the entry in one step, including which protocols the device supports. For installers:
- Devices can be provisioned before they are mounted and join when power comes on.
- The DSK comes from the label, so authenticated S2 is the default rather than an extra step.
- The provisioning list is data you can export and back up, which speeds replacement.
It also changes your risk. The list holds every device's DSK and granted classes, so treat it like a key list. Z-Wave JS notes that removing an entry for an already included node does not exclude it; exclude it separately.
What is Z-Wave Long Range, and how does it coexist with the mesh?
Z-Wave Long Range is an addition to the protocol, announced in September 2020, aimed at large homes, outbuildings, multi dwelling units and light commercial sites. From the Alliance's pages:
- Topology: star. Each LR device talks directly to the controller. Z-Wave JS adds that LR nodes "cannot be used to route messages for non-ZWLR nodes".
- Scale: a 12 bit address space and up to 4,000 nodes, against 232 for the mesh.
- Radio: "an extra 100kbps DSSS OQPSK modulation" treated as an additional channel in gateway scanning, up to +30 dBm, and dynamic power control that adjusts output power on every transmission.
- Range: the Alliance's LR page quotes a maximum of 1.5 miles at full power; its earlier explainer reported 1 mile (1.6 km) line of sight from the first implementation at +14 dBm. A 2025 EU product announcement claimed up to 1 km in open field. Treat all of these as best case.
- Regions: the earlier explainer said LR was US only at the time; 2024B-3 set EU channels at 864 and 866 MHz, and the first EU LR end device was announced in August 2025.
One controller can run the mesh and an LR star together, and Z-Wave JS gives LR nodes IDs from 256 up. LR has only S2 Access Control and S2 Authenticated, with separate keys, and LR inclusion "works exclusively through Smart Start". Every LR device joins authenticated, with no S0 or unauthenticated fallback. Z-Wave JS says choosing LR should be "an opt-in rather than an opt-out", because the device will not be part of the mesh.
| Situation | Mesh | Long Range |
|---|---|---|
| Dense home with many mains powered switches | Good fit; switches strengthen the mesh | Not needed |
| Detached garage or gate far from the hub | Needs repeaters along the way | Good fit if both ends support LR |
| Very large site with hundreds of devices | Limited to 232 nodes | Up to 4,000 nodes |
| Device must relay for others | Yes (mains powered) | No; LR nodes never route |
| Must include without a QR code or DSK | Possible (S2 Unauthenticated, S0 or none) | Not possible; SmartStart only |
What open source exists, and who governs it?
The protocol stack. The Z-Wave source code project is available to Alliance members under BSD 3, supervised by the OSWG. The Alliance's announcement lists source for the mesh and Long Range network and upper MAC layers, sample applications, and a platform abstraction layer that lets members build lower MAC and PHY layers for their own silicon. The Alliance's public GitHub organisation also publishes BSD 3 repositories such as a stack Docker environment, a PC controller tool and Zniffer specifications. The specification itself is a downloadable package; 2026A is in force today.
The controller software. Z-Wave JS (the zwave-js npm package, from the MIT licensed zwave-js/zwave-js GitHub repository) describes itself as a "modern and standards-compliant Z-Wave device driver that runs almost anywhere". It talks to a USB controller over its serial API and implements S2, SmartStart, QR codes, Long Range, NVM backup and restore and firmware updates. It is a community project on GitHub, not governed by the Alliance.
From a supply chain point of view, the controller software is where most of your exposure sits. A hub running Z-Wave JS is a Node.js application with many transitive npm dependencies, often packaged as a container image, listening on a local web port. That is the part you can inventory and patch yourself.
How does Z-Wave compare with Zigbee, Thread with Matter and Bluetooth mesh?
| Z-Wave (mesh) | Z-Wave Long Range | Zigbee | Thread with Matter | Bluetooth mesh | |
|---|---|---|---|---|---|
| Band | Sub-GHz | Sub-GHz LR channels | Mostly 2.4 GHz | 2.4 GHz | 2.4 GHz |
| PHY basis | ITU-T G.9959 | Alliance LR PHY (DSSS OQPSK) | IEEE 802.15.4 | IEEE 802.15.4 | Bluetooth Low Energy |
| Topology | Source routed mesh | Star | Mesh with coordinator and routers | IPv6 mesh with border routers | Managed flooding mesh |
| Network size | 232 nodes | Up to 4,000 nodes | Large | Large | Large |
| Join security | S2: ECDH Curve25519 plus DSK PIN | S2 via SmartStart only | Install codes or a well known trust centre key, by version | Matter commissioning with device attestation | ECDH provisioning, optional out of band authentication |
| Legacy weak mode | S0, no security | None | Default link key in older profiles | None in Matter | No out of band authentication |
| Governance | Z-Wave Alliance; ITU for lower layers | Z-Wave Alliance | Connectivity Standards Alliance | Thread Group; Matter by CSA | Bluetooth SIG |
Z-Wave's strengths are sub-GHz range, mandatory certification that makes interoperability predictable, and a mature application layer. Its weaknesses are lower data rates, regional radio variants and a network that is not IP native, so everything goes through a controller. That controller is your most important security boundary.
Step by step: include an S2 device with Z-Wave JS, verify its security class, back up the NVM
This lab uses Z-Wave JS directly from Node.js on a Linux host, with a Z-Wave USB controller for your region and one S2 device. In a home automation UI the same steps appear as dialogs.
1. Identify the controller by a stable path
Names like /dev/ttyUSB0 can change between boots. Use the stable by-id path and confirm it before anything writes to the controller.
# List stable serial paths; note the one that matches your Z-Wave stick
ls -l /dev/serial/by-id/
# Confirm which tty it points to and that nothing else is using it
readlink -f /dev/serial/by-id/usb-YOUR_STICK_ID
sudo fuser -v "$(readlink -f /dev/serial/by-id/usb-YOUR_STICK_ID)" || echo "port is free"
Stop any other software that holds the port. Two programs on one controller corrupt state.
2. Generate distinct keys and store them safely
You need six random 16 byte keys: four for the mesh classes and two for Long Range.
umask 077
mkdir -p ~/zwave-lab && cd ~/zwave-lab
for k in S0_Legacy S2_Unauthenticated S2_Authenticated S2_AccessControl LR_S2_Authenticated LR_S2_AccessControl; do
printf '%s=%s\n' "$k" "$(openssl rand -hex 16)"
done > keys.env
chmod 600 keys.env
cat keys.env | cut -d= -f1 # print names only, never paste keys into chat or tickets
Back keys.env up encrypted and away from the controller. Lose the keys and the controller together, and every secure device must be included again.
3. Install the driver
npm init -y
npm i zwave-js
npm ls zwave-js # record the exact version you installed
Pin the version in your lockfile. The driver and its dependencies are now part of your attack surface.
4. Start the driver with all keys and S2 callbacks
Create include.mjs. The callbacks are a minimal console version of the dialogs Z-Wave JS requires. It changes nothing until inclusion starts.
// include.mjs
import { Driver, InclusionStrategy, SecurityClass } from "zwave-js";
import { readFileSync, writeFileSync } from "node:fs";
import readline from "node:readline/promises";
const env = Object.fromEntries(
readFileSync("keys.env", "utf8").trim().split("\n").map((l) => l.split("="))
);
const key = (n) => Buffer.from(env[n], "hex");
const port = process.argv[2]; // pass the /dev/serial/by-id/... path
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const driver = new Driver(port, {
securityKeys: {
S0_Legacy: key("S0_Legacy"),
S2_Unauthenticated: key("S2_Unauthenticated"),
S2_Authenticated: key("S2_Authenticated"),
S2_AccessControl: key("S2_AccessControl"),
},
securityKeysLongRange: {
S2_Authenticated: key("LR_S2_Authenticated"),
S2_AccessControl: key("LR_S2_AccessControl"),
},
});
driver.on("error", (e) => console.error(e));
driver.once("driver ready", async () => {
console.log("Home ID:", driver.controller.homeId?.toString(16));
for (const [id, node] of driver.controller.nodes) {
const hi = node.getHighestSecurityClass();
console.log(`node ${id}: highest class ${hi === undefined ? "unknown" : SecurityClass[hi]}`);
}
driver.controller.on("node added", (node, result) => {
const hi = node.getHighestSecurityClass();
console.log(`added node ${node.id}, highest class: ${hi === undefined ? "unknown" : SecurityClass[hi]}`);
if (result.lowSecurity) {
console.warn(`WARNING: lower security than intended, reason code ${result.lowSecurityReason}`);
}
});
await driver.controller.beginInclusion({
strategy: InclusionStrategy.Default,
userCallbacks: {
async grantSecurityClasses(requested) {
console.log("Device requests:", requested.securityClasses.map((c) => SecurityClass[c]));
const ans = await rl.question("Grant all requested S2 classes? (yes/no) ");
return ans.trim() === "yes" ? requested : false;
},
async validateDSKAndEnterPIN(dsk) {
console.log("DSK received (first 5 digits hidden):", dsk);
const pin = await rl.question("Type the 5 digit PIN printed on the device label: ");
return pin.trim();
},
abort() {
console.log("Inclusion aborted");
},
},
});
console.log("Inclusion mode on. Press the device's inclusion button now.");
});
await driver.start();
Run it with the stable path you confirmed in step 1:
node include.mjs /dev/serial/by-id/usb-YOUR_STICK_ID
Compare the printed DSK with the label. If the groups after the PIN do not match exactly, press Ctrl+C: you are not talking to the device you think you are.
5. Verify the granted class
The script prints the node's highest class. For a lock expect S2_AccessControl; for a sensor or switch, usually S2_Authenticated. If you see S2_Unauthenticated, S0_Legacy or a lowSecurity warning, exclude the device, factory reset it, move it near the controller and include it again.
6. Back up the controller NVM
The controller's NVM holds the Home ID, node list and routes. Z-Wave JS reads it with backupNVMRaw; the radio is off during the backup, which takes several seconds up to a few minutes.
// backup.mjs (run with the same Driver options as include.mjs, after "driver ready")
const nvm = await driver.controller.backupNVMRaw((done, total) =>
process.stdout.write(`\r${done}/${total} bytes`)
);
const name = `nvm-${driver.controller.homeId?.toString(16)}-${new Date().toISOString().slice(0, 10)}.bin`;
writeFileSync(name, nvm, { mode: 0o600 });
console.log(`\nSaved ${name}`);
await driver.destroy();
sha256sum nvm-*.bin > nvm-backups.sha256
gpg --symmetric --cipher-algo AES256 nvm-*.bin # store the .gpg copy off the host
Warning: restoring an NVM overwrites the controller. Z-Wave JS warns that a failure during restore "may brick your controller". Before any restore, repeat the step 1 path check, prefer restoreNVM (which converts formats) over restoreNVMRaw, and keep the original backup until the restored network is tested.
7. Export the SmartStart provisioning list
driver.controller.getProvisioningEntries() returns the SmartStart provisioning list. Save it with the same permissions as keys.env and store it with your key backup.
8. Keep the software side current
npm outdated zwave-js
npm audit --omit=dev
Update the driver on a schedule and rebuild any container image that bundles it.
Common mistakes
- Accepting S0 or no security when S2 was available. Some controllers fall back silently. Check the granted class after inclusion and treat an S0 grant on an S2 capable device as a possible downgrade.
- Skipping the DSK check. Typing the PIN without comparing the rest of the DSK defeats authenticated inclusion.
- Granting S2 Unauthenticated to everything. Use it only for devices with no printed DSK.
- Reusing one key for several classes, or copying example keys from documentation. Generate every key separately, and keep Long Range keys apart from mesh keys.
- No controller backup. A failed USB stick without an NVM backup and the keys means re-including every device, and possibly resetting locks by hand.
- Buying devices for the wrong region. They will not talk to your controller and may break local radio rules.
- Leaving old controllers and remotes in the network. Every secondary controller holds keys. Exclude what you no longer use.
- Exposing the controller UI or WebSocket server beyond the local network. S2 does not protect the hub's web and API ports.
FAQ
Is Z-Wave proprietary?
Not any more. The Z-Wave Alliance, an independent, member driven Standards Development Organization, governs the specification and certification and states that Z-Wave "is not proprietary". The lower layers are ITU-T G.9959, and the protocol source code is available to Alliance members under BSD 3.
What frequency does Z-Wave use in the US and Europe?
In the US, classic Z-Wave uses 908.42 MHz (9.6 and 40 kbit/s) and 916 MHz (100 kbit/s), and Long Range uses 912 and 920 MHz. In Europe, classic Z-Wave uses the 868 MHz band and Long Range uses 864 and 866 MHz after the 2024B-3 update. Devices and controllers must match the region.
How many devices can a Z-Wave network have?
Up to 232 nodes in a classic mesh. Z-Wave Long Range raises this to up to 4,000 nodes through a 12 bit address space, and both can run on one controller.
Is Z-Wave S2 secure?
S2 uses ECDH on Curve25519, AES-128 CCM, separate keys per class and DSK PIN authentication, and no practical break of that cryptography is publicly known. Research has focused on what sits around it: routing, legacy or unencrypted devices on the same network, and device bugs. Use authenticated classes, avoid S0 and keep firmware and controller software updated.
What is the difference between S0 and S2?
S0 delivers one network key protected by a fixed, well known temporary key and cannot authenticate the joining device. S2 delivers per class keys over an ECDH protected channel and lets you authenticate the device with its DSK PIN.
What is the Z-Wave DSK, and where do I find it?
The Device Specific Key is printed on the device, packaging or manual, as eight groups of five digits, often with a QR code. The first five digits are the PIN you type during authenticated inclusion. Keep a record of it for re-inclusion.
How far can Z-Wave Long Range reach?
The Alliance quotes up to 1.5 miles at full power, and an earlier Alliance explainer reported 1 mile (1.6 km) line of sight at +14 dBm. Indoor range depends on walls, antenna placement and local rules.
How do I back up a Z-Wave network?
Back up the controller's NVM (in Z-Wave JS with backupNVMRaw, or your hub's backup function), the network keys and the SmartStart provisioning list. Encrypt them, store them off the controller host, and test a restore on spare hardware.
Where Safeguard fits
Safeguard does not monitor Z-Wave radio traffic, manage network keys, scan device firmware or include devices. The fit is the software around the radio. A controller built on Z-Wave JS is a Node.js application, often a container image, with a long tree of npm dependencies. Safeguard can scan that repository or image, list the components and versions inside, match them against vulnerability data, show which projects carry a given package version, apply policies and gates in CI, and open fix pull requests when you switch autonomous remediation on. Firmware on locks and sensors still has to be updated through each device maker's process.
A controller rebuilt after a failure pulls its dependencies again, and the driver, web UI and bridge services all ship open source components that receive fixes on their own schedule. That is why the next section still applies to a smart home radio protocol.
Fix it without waiting for upstream
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation categories include dependencies, licenses, security posture and findings, and each one is set separately. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds.
3. Gold Registry: start from components that are already fixed
Safeguard Gold rebuilds patched versions of open-source packages and images from source, signs each build, attaches an SBOM, SLSA provenance and VEX, and serves them from registry.safeguard.sh to your organization. Every Gold artifact is held continuously to zero exploitable critical or high CVEs and a completed license check, with signed build provenance (signed with Safeguard's key and verifiable with cosign, SLSA Build Level 3 as the design target) and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. Ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | Safeguard policies and gates in CI |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
These demos show dependency, SBOM and remediation workflows, not Z-Wave radio monitoring or device management. You can download Safeguard and follow the CLI docs to try them on your own projects.
This demo sets up the Safeguard runner on your own machine, approves it with its device code and scans from the terminal. Pointed at the container image or repository of a Z-Wave controller stack, the same scan lists the npm packages and versions inside and their known vulnerabilities, so you know what to rebuild after an advisory.
Click through it at your own pace, or open it full screen.
This demo is a real CLI session: you ask it to fix the lodash CVE in a repository, and it reads the finding and the advisory, asks before any command that changes something, moves lodash to 4.17.21, runs npm install and npm test with your approval, and pushes nothing. The same flow applies when a dependency of a Node.js hub or bridge service built on Z-Wave JS needs a version bump.
Click through it at your own pace, or open it full screen.
This demo connects Claude to the Safeguard MCP server with OAuth and lets you choose which tools Claude can use. You can then ask which of your projects or images ship a given package version, such as a specific zwave-js release, and get the answer from your own scan data rather than from memory.
Click through it at your own pace, or open it full screen.
Quick check
You include a new S2 door lock and your controller reports the highest granted class as S0 Legacy, with a low security warning. What should you do?
Treat it as a failed secure inclusion and a possible downgrade. Exclude the lock, factory reset it, bring it close to the controller and include it again, checking the DSK and entering the PIN so it receives S2 Access Control. Only fall back to S0 deliberately, for a lock that genuinely cannot complete S2.
A detached garage is 400 metres from the house. You add a Z-Wave Long Range sensor there with SmartStart. Can it relay messages for a mesh switch in the garden?
No. Long Range is a star topology, so the LR sensor talks directly to the controller and does not route for classic mesh nodes. The garden switch needs a path through mains powered mesh devices, or it should be a Long Range device itself.
Sources
- Z-Wave Alliance: Z-Wave Specifications (2026A in force package, update process)
- Z-Wave Alliance: About Us
- Z-Wave Alliance: Z-Wave Long Range technology
- Z-Wave Alliance: What is Z-Wave Long Range and how does it differ from Z-Wave?
- Z-Wave Alliance: Two Truths and a Lie, is Z-Wave proprietary?
- Z-Wave Alliance: The Power of Choice, multiple silicon providers
- Z-Wave Alliance: Z-Wave Source Code Project completed for Alliance members (November 2022)
- Z-Wave Alliance: Year in Review 2023
- Z-Wave Alliance: Exploring the Z-Wave 2024B Specification
- Z-Wave Alliance member news: first Z-Wave Long Range node for the EU (August 2025)
- Z-Wave Alliance on GitHub (BSD 3 repositories)
- Silicon Labs: completion of the Sigma Designs Z-Wave business acquisition (18 April 2018)
- ITU-T Recommendation G.9959, editions and status
- ITU-T G.9959 (01/15)
- RFC 7748: Elliptic Curves for Security (Curve25519 and X25519)
- eCFR: 47 CFR 15.249
- ETSI: EN 300 220-2 published versions
- Z-Wave JS: GitHub repository
- Z-Wave JS: Supporting Security S2 in applications
- Z-Wave JS: Supporting Z-Wave Long Range in applications
- Z-Wave JS: Controller API (provisioning, NVM backup and restore)
- Z-Wave JS: Driver options (security keys)
- Z-Wave JS: ZWaveNode API (security classes)
- Z-Wave JS: S2 crypto operations
- Z-Wave JS: RF region definitions
- Badenhop and colleagues: The Z-Wave routing protocol and its security implications, Computers and Security (2017)
- Nkuba, Kim, Dietrich and Lee: Riding the IoT Wave With VFuzz, IEEE Access (2022)
- Nkuba, Woo, Lee and Dietrich: ZMAD, IEEE Access (2023)
- Braghin, Lilli and Riccobene: the Z-Wave case study, Computers and Security (2023)
Last reviewed: 25 October 2026. The Z-Wave specification package is revised twice a year and Z-Wave JS releases often; check the pages above for the versions your controller and devices implement.