Key backup and cryptography
Mesh-Client stores different kinds of keys in different places. Meshtastic and MeshCore Security tabs each back up a full key pair (public + private) per node, indexed by node number (Meshtastic nodeNum, MeshCore nodeId). The private key restores mesh identity after factory reset; public key alone is not enough, but every backup payload includes both keys.
Per-node archives are created only when you click Backup Keys on that protocol’s Security tab. Opening Security, connecting a radio, or importing Radio JSON updates the MeshCore MQTT active cache only — it does not create a per-node backup.
See also Meshtastic vs MeshCore feature parity and the README Security section.
What a backup contains (both protocols)
Every encrypted backup stores:
| Field | Meshtastic | MeshCore |
|---|---|---|
| Public key | 32 bytes (Curve25519) | 32 bytes |
| Private key | 32 bytes | 32- or 64-byte orlp export |
| Index | nodeNum (unsigned 32-bit) |
nodeId (numeric self id) |
Keys are not interchangeable across Meshtastic and MeshCore firmware.
Encryption: Electron safeStorage (OS keychain when available) over JSON in renderer localStorage.
Not included in backup: channels, NodeDB, messages, MeshCore contacts DB, Meshtastic remote admin keys (meshtasticRemoteAdminKey:<nodeNum> in SQLite), device name/owner (optional nodeLabel in the index is display-only).
Restore lists prefix entries with Meshtastic · or MeshCore · so archives are distinguishable when nodeNum and nodeId share the same numeric display.
Meshtastic: DM key backup / restore
Where: Security tab → Key Backup / Restore (local radio only; hidden for remote configure targets).
What it backs up: The connected local radio’s DM public and private keys from device security config.
Storage:
| Item | Location |
|---|---|
| Per-node encrypted blob | localStorage → mesh-client:meshtastic-dm-key-backup:<nodeNum> |
| Index (labels, dates, full public key in plaintext) | localStorage → mesh-client:meshtastic-dm-key-backup-index |
| Legacy (migrated on upgrade) | mesh-client:key-backup → moved into per-nodeNum slot when valid |
Index privacy: Per-node private keys live in safeStorage-encrypted blobs. The index JSON in localStorage is not encrypted: each entry stores plaintext nodeLabel, backedUpAt, and the full publicKeyB64 (the restore UI shows only an 8-character hex prefix). Public keys are not secret, but they are identity/correlation metadata readable by anything with access to this profile’s localStorage.
Scope: per node, Meshtastic only
- Each Meshtastic
nodeNumhas its own backup slot on this computer. - Backing up radio A does not overwrite radio B’s archive or any MeshCore archive.
- Restore Keys applies the archive for the currently connected
nodeNumwhen one exists. - Restore from backup… lists Meshtastic archived backups only.
Restore pipeline
- Decrypt and validate both keys.
applyConfigmerges onlypublicKey/privateKey(admin keys and toggles on the device are preserved).- Commit so keys persist to firmware.
- Post-restore verification compares device public key to backup; success or actionable failure toast.
Implementation: meshtasticDmKeyBackupStorage.ts, KeyBackupRestoreSection.tsx, SecurityPanel.tsx.
MeshCore: key backup / restore and MQTT identity
Where: Security tab (partial — backup/restore, sign, export/import private key; no Meshtastic PKI/admin sections).
What it backs up: Device public key plus private key from exportPrivateKey() (normalized orlp bytes) — only when you click Backup Keys.
Storage:
| Item | Location |
|---|---|
| Per-node encrypted archive | localStorage → mesh-client:meshcore-key-backup:<nodeId> |
| Index | localStorage → mesh-client:meshcore-key-backup-index |
| Active MQTT cache (last connected/restored device) | mesh-client:meshcoreIdentity (+ optional meshcoreIdentityEncPK) |
Index privacy: Same as Meshtastic — encrypted per-node archives hold private keys; the index JSON stores plaintext nodeLabel, backedUpAt, and full publicKeyB64 (UI shows an 8-character hex prefix only).
The active MQTT cache is separate from per-node archives: LetsMesh JWT signing uses whichever identity was last connected or restored. Per-node archives retain full pairs without overwriting each other.
How keys get into the active cache (not a per-node backup)
- Connect a MeshCore radio — session export via
tryPersistMeshcoreIdentityFromRadioExport. - Security → Restore or Restore from backup… —
syncMeshcoreActiveIdentityFromBackupwrites the full pair and dispatchesmeshclient:meshcoreIdentityUpdated. - Radio → Import config JSON — updates MQTT cache only (
RadioPanel.tsx); use Security → Backup Keys to create a per-node archive.
Restore pipeline
- Decrypt and validate both keys.
importPrivateKey(privateKeyBytes)on the connected radio.- Sync active MQTT cache with full pair; identity-updated event refreshes Connection tab LetsMesh username.
- Best-effort verification after reconnect delay.
Implementation: meshcoreKeyBackupStorage.ts.
Details: LetsMesh MQTT authentication.
Meshtastic keys vs MeshCore keys
| Meshtastic DM backup | MeshCore key backup | |
|---|---|---|
| Protocol | Meshtastic PKC / DM | MeshCore orlp keys |
| UI | Security → Backup Keys | Security → Backup Keys |
| Storage pattern | meshtastic-dm-key-backup:<nodeNum> |
meshcore-key-backup:<nodeId> |
| Per node? | Yes — indexed by nodeNum |
Yes — indexed by nodeId |
| Created by opening Security? | No (legacy MT slot migrates once) | No |
| Portable across protocols? | No | No |
Flashing MeshCore firmware on hardware that previously ran Meshtastic replaces on-device key material. Meshtastic archives preserve your old Meshtastic identity for restore if you return to Meshtastic; they do not carry that identity onto the MeshCore mesh.
Use case: backing up keys before moving MT nodes to MC
Before you flash (Meshtastic)
- Switch to Meshtastic and connect each radio one at a time.
- Open Security → Key Backup / Restore.
- For each node: click Backup Keys (requires
safeStorage). Each node gets its own indexed slot — no overwrite when you connect the next radio. - Optional: use Copy / export private key for an off-device copy.
- Remote admin keys remain in SQLite per node; no extra Security step.
Flash and first MeshCore setup
- Flash MeshCore firmware.
- Switch to MeshCore and connect — new MeshCore identity on the mesh.
- Security → Backup Keys archives the new pair under that
nodeId(required; connect/import alone is not enough). - Optional: Radio → Import config JSON for MQTT cache before RF connect; then Security → Backup Keys for a per-node archive.
If you return a radio to Meshtastic later
- Reflash Meshtastic firmware and connect locally.
- Restore Keys if a backup exists for the current
nodeNum, or Restore from backup… to pick an archive from before a factory reset / node-num change.
What this does not do
- Does not migrate Meshtastic identity into MeshCore contacts — peers see a new public key after flash.
- Does not replace MeshCore companion full JSON backup tools; use Security Backup Keys for Mesh-Client per-node archives.
Full companion JSON backup (evaluation, 2026)
The official MeshCore companion can export/import a full device JSON (contacts, channels, radio params, and related fields). mesh-client today supports:
- Per-node Security archives (
mesh-client:meshcore-key-backup:<nodeId>) — public + private key pairs only. - Radio JSON import for a subset of fields (
setRadioParams, channels where APIs exist).
Gap: There is no single-click export/import of the entire companion JSON blob. Implementing parity would require auditing meshcore.js export shape vs RadioPanel import, SQLite contact merge, and conflict rules for channels/contacts — estimated multi-day effort. Deferred until Tier 1–3 parity items ship; track as a Security/Radio follow-up if users need whole-device migration beyond per-node key archives.
Quick reference
| Goal | Action |
|---|---|
| Save Meshtastic DM keys for this node | Meshtastic → connect local radio → Security → Backup Keys |
| Save keys for multiple Meshtastic nodes | Backup each node while connected; each nodeNum keeps its slot |
| Restore Meshtastic DM keys (same node) | Security → Restore Keys |
| Restore after factory reset / different node num | Security → Restore from backup… → confirm |
| Save MeshCore keys for this node | MeshCore → connect → Security → Backup Keys |
| Restore MeshCore archive to connected radio | Restore Keys or Restore from backup… |
| Cache MeshCore keys for MQTT only | Connect radio, restore backup, or Radio JSON import (no per-node archive) |
| Administer a remote Meshtastic node | meshtasticRemoteAdminKey:<nodeNum> via node detail (separate from DM backup) |