built 20–40 min verified 503cd17 (2026-09-13)

Install Lightning Mesh on a router

You build two binaries on your computer, then one script stages everything on the router and applies it with a safety net. If the router doesn't come back healthy, it restores its previous state by itself.

Status: built. Same scripts the maintainers use for the live fleet.

What you need

Thing Why
Docker (or OrbStack) Cross-compiles the aarch64 router binaries
Bun Only for the optional hello.mesh front desk
Root SSH to the router at 192.168.1.1 From the previous page

Step 1: Build the router daemon

Do:

deploy/openwrt/build.sh

Expect: deploy/openwrt/mjolnir-meshd-aarch64 exists, a static binary of about 9–10 MB. The first build takes a couple of minutes.

If not: make sure the Docker daemon is running. The daemon can't be built natively on macOS; Docker is the supported path.

The front desk is the web page people see at hello.mesh. The mesh runs without it, but new users won't have a front door.

Do:

deploy/openwrt/build-hello.sh

Expect: deploy/openwrt/mjolnir-hello-aarch64 exists. The script builds the web app, embeds it, then cross-compiles. That order matters: a stale web build ships a stale page.

Step 3: Set the fleet Wi-Fi settings

Every node in one mesh must share these values, or the mesh won't form and phones won't roam.

Do:

cp deploy/openwrt/fleet-secrets/wireless.env.example deploy/openwrt/fleet-secrets/wireless.env
$EDITOR deploy/openwrt/fleet-secrets/wireless.env
Variable What it is Live fleet value
MESH_ID 802.11s backhaul mesh id (routers only; phones never join it) mjolnir-mesh
MESH_KEY Backhaul encryption. Empty means an open backhaul empty (open)
CLIENT_SSID Network name phones join Lightning Mesh (factory default is )
CLIENT_ENC / CLIENT_KEY Client Wi-Fi security. none, or psk2/sae-mixed plus a key none (open)
COUNTRY Regulatory domain. Required; set yours US
FT_KEY Optional 802.11r fast-roaming secret (openssl rand -hex 32) empty

The backhaul runs on 5 GHz, channel 36, unless you change it. fleet-secrets/wireless.env is gitignored. Don't commit it.

Joining your own mesh to an existing one: copy that mesh's MESH_ID, MESH_KEY, band and channel exactly.

Step 4: Review the SSH keys you're about to install

Read this before installing on a router you own. The repo ships `deploy/openwrt/files/etc/dropbear/authorized_keys`, and the installer merges those public keys into the router's root SSH keys. Unless you edit that file first, the project maintainers get root SSH access to your router.

Do: replace the file's contents with your own public key(s), or delete the lines you don't want.

Expect: the file contains only keys you trust with root on this router.

Step 5: Install

Do:

deploy/openwrt/install-node.sh --wireless deploy/openwrt/fleet-secrets/wireless.env root@192.168.1.1

What happens:

  1. Stage. Copies binaries, configs, setup-wireless.sh and the applier to /root/mjolnir-stage, and pre-fetches packages. A router with no internet gets them from deploy/openwrt/pkg-cache/.
  2. Apply, detached. mjolnir-apply snapshots the current state, applies, then checks health. Your SSH session may drop when Wi-Fi restarts. That's expected; the script reconnects.
  3. Health gate. On a router that's already meshed, the overlay address and a neighbour must come back within 120 s, or it rolls back. A brand-new router has no baseline, so the gate is skipped.

Expect: the script prints >> OK: applied … and then the "Next (fresh node only)" hints.

If not: the router restored its previous config. Read the log:

ssh root@192.168.1.1 'cat /root/mjolnir-stage/apply.log'

The installer's closing hint says to keep backhaul_iface 'br-mesh', the template default. br-lan is only for a wired test bench.

Step 6: Check the install

Do:

ssh root@192.168.1.1 'cat /root/mjolnir-stage/result; sha256sum /usr/bin/mjolnir-meshd'
sha256sum deploy/openwrt/mjolnir-meshd-aarch64

Expect: the result file starts with OK, and both hashes match.

The daemon isn't started yet on a fresh router. You'll give it peers first.

Next: Join the mesh.

Next: Join the mesh →