Skip to content

feat: preserve normal radio profiles across event firmware#11110

Open
RCGV1 wants to merge 7 commits into
meshtastic:developfrom
RCGV1:codex/event-channel-profile-firmware
Open

feat: preserve normal radio profiles across event firmware#11110
RCGV1 wants to merge 7 commits into
meshtastic:developfrom
RCGV1:codex/event-channel-profile-firmware

Conversation

@RCGV1

@RCGV1 RCGV1 commented Jul 20, 2026

Copy link
Copy Markdown
Member

Event-only scope

This PR does not add or change channel QR behavior. Existing QR Replace and Add remain the app workflow.

What changes

  • normal firmware continues to use /prefs/config.proto, /prefs/channels.proto, and /backups/backup.proto
  • event firmware uses /prefs/event-config.proto, /prefs/event-channels.proto, and /backups/event-backup.proto
  • on its first boot, event firmware preserves the normal identity and non-radio settings, but applies the event build LoRa defaults and creates event channels from the build defaults
  • flashing back to normal firmware naturally resumes the prior channels and LoRa configuration, with no app required

Storage safety

  • first event-profile creation reserves 2,954 bytes: maximum config/channel payloads plus their full-atomic temporary files
  • if the filesystem has less space, event firmware runs from its in-memory event defaults but does not write event config or channels; this avoids saveToDisk() formatting the filesystem on a failed write
  • the next event boot retries profile creation when enough space is available
  • no extra persistent RAM is used; only the active profile is loaded
  • normal firmware removes the event config, channels, and event backup at boot, releasing all event-only storage; a later event flash starts a fresh event profile

Safety

  • event firmware never copies normal channels into the event table
  • corrupt normal config remains on the existing degraded/no-new-identity recovery path
  • encrypted-storage migration and lockdown disable cover both normal and event profile files and backups
  • backups are profile-specific

Verification

  • pio test -e native-macos -f test_event_profile_storage
  • pio run -e native-macos
  • pio run -e rak3172 (the 14 KiB STM32WL filesystem family)
  • native simulator: eight distinct AES-256 normal channels plus custom 250 kHz/SF9/CR6 LoRa -> official Open Sauce 2026 two-channel US Short Turbo/channel-31 profile -> normal firmware; normal config and channel files had identical SHA-256 hashes before, during, and after the event boot
  • full aggregate native suite was started but intentionally stopped because it rebuilds each of 38 test targets independently; focused coverage and both relevant target builds completed successfully

Design background: meshtastic/design#120 (comment)

Summary by CodeRabbit

  • New Features
    • Added event-mode–specific configuration, channel, and backup storage, including preflight checks to ensure safe atomic event-profile writes.
    • Automatically derives an event configuration from the standard one while preserving identity/security details and applying event-specific radio settings.
  • Bug Fixes
    • Improved boot persistence behavior by deferring automatic saves until configuration is successfully verified, and adding safe recovery when the event configuration is missing.
    • Enhanced encrypted-storage lockdown handling by migrating the relevant standard/event radio-profile files accordingly.
  • Tests
    • Added embedded tests covering file isolation, identity preservation, capacity boundaries, and boot persistence deferral/recovery.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Event-mode radio profiles now use dedicated persistence files. Boot loading initializes missing event configuration from the standard profile, tracks storage availability and config readiness, defers unsafe writes, and includes event files in encrypted-storage migrations. Unity tests cover the new helpers and storage rules.

Changes

Event profile persistence

Layer / File(s) Summary
Profile paths and configuration helpers
src/mesh/NodeDB.h, test/test_event_profile_storage/test_main.cpp
Adds standard/event storage path selection, event configuration derivation, storage reservation checks, boot-defer predicates, and Unity coverage.
Boot profile recovery and persistence
src/mesh/NodeDB.cpp, src/mesh/NodeDB.h
Detects missing or space-constrained event storage, derives missing event configuration, tracks config readiness, and defers or suppresses boot-time writes.
Encrypted-storage profile migration
src/mesh/NodeDB.cpp
Includes event config, channel, and backup files in encrypted-storage and plaintext migration passes.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant NodeDB
  participant Storage
  participant StandardConfig
  participant EventConfig
  NodeDB->>Storage: Check event profile file and available space
  NodeDB->>StandardConfig: Load standard configuration when event config is missing
  StandardConfig-->>NodeDB: Return configuration and decode status
  NodeDB->>EventConfig: Derive event LoRa configuration
  NodeDB->>EventConfig: Persist initial event configuration
  NodeDB->>Storage: Defer or suppress boot writes when recovery is incomplete
Loading

Possibly related issues

  • meshtastic/Meshtastic-Apple#2133 — Related to event/standard radio-profile configuration persistence and restoration across profile transitions.

Suggested reviewers: vidplace7, thebentern, jp-bennett

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change.
Description check ✅ Passed The PR description is thorough and covers scope, behavior, storage safety, safety, and verification, matching the template's intent.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

⚡ Try this PR in the Web Flasher

Note

Building this pull request… the flash button, badges and supported-board
list will appear here automatically once CI finishes.

@RCGV1
RCGV1 force-pushed the codex/event-channel-profile-firmware branch from b5919c4 to 65f92fc Compare July 20, 2026 20:07
@RCGV1

RCGV1 commented Jul 20, 2026

Copy link
Copy Markdown
Member Author

Review follow-up: force-pushed 65f92fca with three fixes before broad review:

  • decrypt/migrate both normal and event profile files plus their backups across lockdown transitions
  • preserve DECODE_FAILED when the normal config is corrupt during first event boot, retaining the existing degraded/no-new-identity behavior
  • isolate event backups at /backups/event-backup.proto so normal restore cannot consume event data

The draft remains blocked only on hardware verification and the existing macOS native-test argp.h host-toolchain failure.

@RCGV1
RCGV1 force-pushed the codex/event-channel-profile-firmware branch from 65f92fc to 0bc1176 Compare July 20, 2026 20:14
@RCGV1

RCGV1 commented Jul 20, 2026

Copy link
Copy Markdown
Member Author

Updated to 0bc11766: applied the repository Trunk formatter; trunk fmt src/mesh/NodeDB.cpp now reports no issues.

The remaining check-label failure is metadata-only. My fork token cannot add labels to the upstream PR; could a maintainer add enhancement? The build/test matrix is still running.

@RCGV1

RCGV1 commented Jul 20, 2026

Copy link
Copy Markdown
Member Author

macOS native simulator verification on 0bc11766 (same persistent VFS, no erase between firmware images):

  • regular firmware first boot created /prefs/config.proto and /prefs/channels.proto
  • event firmware boot loaded the ordinary config, logged Initialized event config without modifying /prefs/config.proto, created /prefs/event-config.proto and /prefs/event-channels.proto, and ran the compiled US / EventSim / SHORT_FAST radio profile
  • regular firmware booted again and loaded the original ordinary config and channels, resuming UNSET / LongFast
  • the ordinary config and channel SHA-256 values were identical before the event boot and after the return:
    • config: 9582708236b98ecd5ba5b4db7e74a2850cbe09c4661bc2531666cc890795a299
    • channels: d2b27729b144786829d0175c6e6dbc029a7bd735baf20ef49d310cc3f33e8bdf

The stable PKI-derived node number also remained 0x3678de25; the different number at fresh normal first boot was the expected pre-PKI MAC-derived provisional ID. No source changes from this verification.

@RCGV1

RCGV1 commented Jul 20, 2026

Copy link
Copy Markdown
Member Author

Follow-up verification found and fixed a real degraded-boot persistence bug.

When the normal config was corrupt on the first event boot, the original change kept config.proto and channels.proto intact but still rewrote device.proto and nodes.proto. That could change the saved node identity despite the intended "freezing identity" behavior.

This update defers automatic protobuf writes during initialization until config loading is verified. A decode-failed boot now remains degraded and radio-silent without writing normal files, event profile files, device state, or the node database. Explicit runtime configuration saves remain available after initialization.

Verification:

  • TDD: added the focused boot-persistence regression test; pio test -e native-macos -f test_event_profile_storage passes (4 tests).
  • Built both normal and event-configured native-macos firmware successfully.
  • Native simulator: normal -> event -> normal retains the separate profiles and expected radio settings.
  • Corrupt normal config before the first event boot: config, channels, device state, and node database hashes stayed unchanged; no event config or channels file was created.
  • Corrupt existing event config: normal config/channels/device/node hashes stayed unchanged, and the corrupt event config was not replaced.

The event-only userPrefs file used for the simulator remains a local test fixture and is not part of the PR.

@RCGV1
RCGV1 marked this pull request as ready for review July 20, 2026 21:33
@RCGV1

RCGV1 commented Jul 20, 2026

Copy link
Copy Markdown
Member Author

@jp-bennett @vidplace7 Please review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/mesh/NodeDB.h (2)

115-117: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Condense code comments to comply with maximum length guidelines.

As per coding guidelines, code comments in C++ files should be minimal—one or two lines maximum.

  • src/mesh/NodeDB.h#L115-L117: Condense this 3-line comment to one or two lines.
  • src/mesh/NodeDB.h#L137-L139: Condense this 3-line comment to one or two lines.
  • src/mesh/NodeDB.h#L583-L585: Condense this 3-line comment to one or two lines.
  • src/mesh/NodeDB.cpp#L2172-L2174: Condense this 3-line comment to one or two lines.
  • src/mesh/NodeDB.cpp#L2399-L2402: Condense this 4-line comment to one or two lines.
  • src/mesh/NodeDB.cpp#L2610-L2612: Condense this 3-line comment to one or two lines.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mesh/NodeDB.h` around lines 115 - 117, Condense the comments at
src/mesh/NodeDB.h lines 115-117, 137-139, and 583-585, and src/mesh/NodeDB.cpp
lines 2172-2174, 2399-2402, and 2610-2612 to one or two lines each while
preserving their existing meaning; no code behavior changes are needed.

Source: Coding guidelines


118-123: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use UPPER_SNAKE_CASE for constants.

As per coding guidelines, C++ constants and defines must use UPPER_SNAKE_CASE.

  • src/mesh/NodeDB.h#L118-L123: Rename these filename constants to UPPER_SNAKE_CASE (e.g., STANDARD_CONFIG_FILE_NAME).
  • src/mesh/NodeDB.h#L140-L140: Rename eventProfileStorageReservationBytes to EVENT_PROFILE_STORAGE_RESERVATION_BYTES.
  • src/mesh/NodeDB.h#L161-L165: Rename radioProfileStorage to RADIO_PROFILE_STORAGE on lines 162 and 164.

(Note: If you decide to rename eventProfileStorageReservationBytes, you will also need to update its downstream usages, such as in test/test_event_profile_storage/test_main.cpp)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mesh/NodeDB.h` around lines 118 - 123, Rename the filename constants in
src/mesh/NodeDB.h lines 118-123 to UPPER_SNAKE_CASE, including
STANDARD_CONFIG_FILE_NAME and corresponding channel, event, and backup names.
Rename eventProfileStorageReservationBytes to
EVENT_PROFILE_STORAGE_RESERVATION_BYTES at line 140 and update all downstream
references, including test/test_event_profile_storage/test_main.cpp. Rename
radioProfileStorage to RADIO_PROFILE_STORAGE at lines 161-165 and update its
references.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/mesh/NodeDB.h`:
- Around line 115-117: Condense the comments at src/mesh/NodeDB.h lines 115-117,
137-139, and 583-585, and src/mesh/NodeDB.cpp lines 2172-2174, 2399-2402, and
2610-2612 to one or two lines each while preserving their existing meaning; no
code behavior changes are needed.
- Around line 118-123: Rename the filename constants in src/mesh/NodeDB.h lines
118-123 to UPPER_SNAKE_CASE, including STANDARD_CONFIG_FILE_NAME and
corresponding channel, event, and backup names. Rename
eventProfileStorageReservationBytes to EVENT_PROFILE_STORAGE_RESERVATION_BYTES
at line 140 and update all downstream references, including
test/test_event_profile_storage/test_main.cpp. Rename radioProfileStorage to
RADIO_PROFILE_STORAGE at lines 161-165 and update its references.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 61751f2f-a470-48cb-b8d8-0ce8f8aa548a

📥 Commits

Reviewing files that changed from the base of the PR and between 0199a1f and dc6033e.

📒 Files selected for processing (3)
  • src/mesh/NodeDB.cpp
  • src/mesh/NodeDB.h
  • test/test_event_profile_storage/test_main.cpp

Comment thread src/mesh/NodeDB.h Outdated
@caveman99 caveman99 added the enhancement New feature or request label Jul 21, 2026
Comment thread src/mesh/NodeDB.cpp
@jp-bennett

Copy link
Copy Markdown
Collaborator

In general I really think this is the right approach.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/mesh/NodeDB.cpp (2)

2183-2202: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Reset eventProfileStorageUnavailable on every load.

The flag is assigned only when eventConfigMissing is true. After a prior capacity failure, a repeated loadFromDisk() can retain true even after the event config exists, causing later config/channel writes to remain suppressed.

Proposed fix
 `#if` USERPREFS_EVENT_MODE
     bool eventConfigMissing = false;
+    eventProfileStorageUnavailable = false;
 `#ifdef` FSCom
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mesh/NodeDB.cpp` around lines 2183 - 2202, Reset
eventProfileStorageUnavailable at the start of each load in the
USERPREFS_EVENT_MODE initialization flow, before checking eventConfigMissing, so
a successful reload clears any prior capacity-failure state. Preserve the
existing storage-space calculation and error logging for missing configurations.

2618-2644: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Propagate EncryptedStorage::migrateFile() failures here. It returns false on read/encrypt/write errors; ignoring that can leave these files plaintext while lockdown stays active.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mesh/NodeDB.cpp` around lines 2618 - 2644, Check the return value of
EncryptedStorage::migrateFile() in both the backup migration and inactive radio
profile loop, and propagate failure through the surrounding save/migration flow.
Do not continue as if migration succeeded when it returns false, ensuring
lockdown cannot proceed while any targeted file remains plaintext.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/mesh/NodeDB.cpp`:
- Around line 2183-2202: Reset eventProfileStorageUnavailable at the start of
each load in the USERPREFS_EVENT_MODE initialization flow, before checking
eventConfigMissing, so a successful reload clears any prior capacity-failure
state. Preserve the existing storage-space calculation and error logging for
missing configurations.
- Around line 2618-2644: Check the return value of
EncryptedStorage::migrateFile() in both the backup migration and inactive radio
profile loop, and propagate failure through the surrounding save/migration flow.
Do not continue as if migration succeeded when it returns false, ensuring
lockdown cannot proceed while any targeted file remains plaintext.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 312c0ef7-e6dc-490c-a27f-6ae20c79bd0a

📥 Commits

Reviewing files that changed from the base of the PR and between c29f228 and fed61a8.

📒 Files selected for processing (1)
  • src/mesh/NodeDB.cpp

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants