feat(settings): warn before enabling licensed (ham) mode - #6632
Conversation
Firmware 2.8.0 signs licensed-mode traffic and derives NodeNum from the identity key, so enabling ham mode can migrate the node's identity once. Stage the toggle behind a confirmation that explains authenticated-but- plaintext operation and the migration, gated on DeviceMetadata.has_xeddsa. Reference: meshtastic/design#122 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughThe licensed amateur-radio setting now uses a dedicated Compose component. Enabling requires confirmation with signing-specific warnings. Disabling remains immediate. Localized resources and Compose tests cover the new behavior. ChangesLicensed mode settings
Estimated code review effort: 3 (Moderate) | ~20 minutes Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant User
participant LicensedModeSetting
participant ConfirmationDialog
participant SettingsState
User->>LicensedModeSetting: Toggle licensed mode on
LicensedModeSetting->>ConfirmationDialog: Show signing-dependent warning
User->>ConfirmationDialog: Confirm enable
ConfirmationDialog->>LicensedModeSetting: Return confirmation
LicensedModeSetting->>SettingsState: Enable licensed mode if still available
🚥 Pre-merge checks | ✅ 6✅ Passed checks (6 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
❌ 1 Tests Failed:
View the top 1 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
This comment has been minimized.
This comment has been minimized.
Same four facts required by design#122 (no PSK/admin channel, signed plaintext, one-time NodeNum migration, licence responsibility) with the clauses joined rather than any content dropped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mirrors the signed variant's structure so only the signing status differs between firmware versions. States the converse explicitly: pre-2.8 licensed mode publishes no key, so peers cannot verify the sender. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Why
Firmware 2.8.0 changes what licensed (ham) mode means. meshtastic/firmware#10969 (merged Jul 27, tracked by meshtastic/design#122) now generates and publishes an identity keypair in licensed mode and XEdDSA-signs every locally originated plaintext packet, including direct messages. Traffic stays readable on air — signatures authenticate origin, they do not encrypt — but because
NodeNumis derived ascrc32(public_key), enabling licensed mode on an existing node can change its node number one time. design#122 asks clients to warn before that migration and to say that favorites, message history, remote-admin references, and peer caches may initially treat the node as new.Today the Android toggle flips the form immediately, carrying only the pre-2.8 one-liner: "Enabling this option disables encryption and is not compatible with the default Meshtastic network." That is now both incomplete (no migration warning) and, on signing firmware, misleading about what the node actually transmits. Per garth's Aug 11 status update on design#122, this client-side copy work had not been started on any client.
🌟 What this adds
Enabling licensed mode is now staged behind a confirmation dialog; disabling still applies immediately (no reason to gate the safe direction). The message is chosen from the firmware capability, reusing the exact pattern
PacketAuthenticitySettingestablished in #6178 —MeshtasticResourceDialogplus a nullable tri-state capability gate.On firmware that signs licensed traffic (
DeviceMetadata.has_xeddsa), the dialog explains authenticated-but-plaintext operation and the identity migration. On older firmware — or before metadata is known — it keeps a plaintext-only warning and makes no migration claim, since neither signing nor the NodeNum derivation applies there.🛠️ Implementation notes
The switch moved out of
UserConfigItemListinto a newLicensedModeSettingcomponent, which holds the staged-confirmation state; the callsign-clearing behaviour on enable is unchanged, just deferred until after confirm. The pending dialog is dropped if the device disconnects, so a stale confirm can never apply a config change to a device that went away.has_xeddsa(field 14) is the only signing-capability bit inDeviceMetadata— there is no design#122-specific flag — so it is used as the proxy for "this firmware signs licensed traffic," consistent with how #6178 gates the Strict policy. Worth noting for reviewers: past "packet authenticity disabled on nightly" reports were old-firmware artifacts, not app bugs, and the same gate applies here.Neither design thread specifies wording, so the four new strings are my draft against the requirements in design#122's Identity migration section and its jurisdiction disclaimer. Please review before this merges and translations are seeded:
licensed_mode_enable_title— Enable licensed (Ham) mode?licensed_mode_enable_confirm— Enable licensed modelicensed_mode_enable_warning(pre-2.8 / capability unknown) — Licensed mode removes channel encryption keys and disables the admin channel, so all traffic is sent as plaintext that anyone can read — and this firmware cannot sign it, so other nodes cannot verify it came from you. This is not compatible with the default Meshtastic network, and you remain responsible for meeting your amateur radio license requirements and local regulations.licensed_mode_enable_warning_signed(2.8.0+) — Licensed mode removes channel encryption keys and disables the admin channel, so all traffic is sent as plaintext that anyone can read — but it is digitally signed, letting other nodes verify it came from you. Your node number may change once to match your identity key, so favorites, message history, remote admin, and other nodes may initially treat it as new; you remain responsible for meeting your amateur radio license requirements and local regulations.Open questions for you:
The signed variant is long (four sentences).Resolved: both variants tightened to two sentences at James's request, joining clauses rather than dropping required facts. They now share an opening and an em-dash pivot to the signing status, so the only visible difference between firmware versions is the fact that actually differs.One thing to eyeball: matching the structure meant filling the slot where the signed variant says "but it is digitally signed…" — so the non-signing variant now states the converse (this firmware cannot sign, so peers cannot verify the sender). That is new information not in the original string, though accurate: design#122's Current firmware behavior notes licensed mode blocks key generation and strips
User.public_key, which is exactly why Strict rejects pre-2.8 licensed traffic. If you would rather the older-firmware dialog not editorialise about signing, drop that clause and let sentence one end at "anyone can read" — shorter, but the variants stop mirroring.design#122 also mentions callsign-identification requirements. The screen already repurposes the long-name field as the callsign with its own summary, so I folded this into the general licence-responsibility line rather than restating it. Enough?
Should the migration warning be conditional on actually migrating (i.e. only when the node's current number is not already
crc32(public_key))? The app cannot cheaply predict this pre-enable, so the copy hedges with "may change." Left as-is deliberately.Existing strict-policy copy says "licensed or ham nodes without PKI keys… may disappear." That remains accurate for pre-2.8 licensed nodes, but 2.8 licensed nodes now pass Strict. Follow-up to reword, or leave until 2.8 is broadly deployed?
Deliberately out of scope: shield/lock semantics are unchanged (design#122 confirms licensed DMs use the verified-signature shield, not the PKI lock — already the app's behaviour), and no screenshot references were added, to keep this copy-only.
🧹 Testing Performed
LicensedModeSettingTest— 7 tests, all passing:truehas_xeddsa = truefalsenullFull baseline green on this branch:
./gradlew spotlessApply spotlessCheck detekt assembleDebug test allTests.Reference: meshtastic/design#122
🤖 Generated with Claude Code
Summary by CodeRabbit