Skip to content

Install event firmware in-app behind a signed release contract - #2223

Draft
RCGV1 wants to merge 7 commits into
meshtastic:mainfrom
RCGV1:codex/event-firmware-ota
Draft

Install event firmware in-app behind a signed release contract#2223
RCGV1 wants to merge 7 commits into
meshtastic:mainfrom
RCGV1:codex/event-firmware-ota

Conversation

@RCGV1

@RCGV1 RCGV1 commented Jul 29, 2026

Copy link
Copy Markdown
Member

Summary

Event metadata is display-only and can't safely authorize a firmware install, so today event firmware means a trip to the web flasher. This adds an in-app installer for event editions — and a return-to-standard path — gated behind an Ed25519-signed release contract, separate from the metadata feed.

Blocked until two things exist outside this repo: the contract needs ratifying (meshtastic/design#133) and the API needs the signed endpoint plus a provisioned signing key (meshtastic/api#110). The app embeds only the trusted public key and fails closed without one — no production behavior changes until the key is deliberately provisioned.

What changed

  • An event-firmware installer under Firmware Updates, with a return-to-standard-firmware path.
  • A signed schema-v1 release contract pinning the exact pioEnv, hardware model, architecture, source-version floor, artifact version and format, byte count, and SHA-256.
  • Downloads reject redirects, oversize streams, size mismatches, checksum mismatches, expired contracts, unknown keys, and bad signatures. The connected node is re-checked and the target reselected after download, right before OTA handoff.
  • Verified artifacts go to the existing ESP32 OTA and nRF DFU installers; anything the app can't prove an exact path for falls back to flasher.meshtastic.org.
  • A simulator-only signed fixture drives a mock install flow; none of it compiles into production trust.

Testing

43 unit tests in 5 suites: contract verification, exact-target selection, download verification, installer handoff policy, and availability. Not yet run against a real radio — a physical ESP32 and nRF install (and return-to-standard) are required before this leaves draft, along with the upstream contract and key provisioning.

Copilot AI review requested due to automatic review settings July 29, 2026 04:05

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Event firmware handling now uses resilient manifest merging, HTTPS and image validation, policy-based notifications, event-specific presentation, and signed OTA contracts with verified artifact downloads. The UI exposes event branding and installation flows while preserving device targeting and cache behavior.

Changes

Event firmware metadata and presentation

Layer / File(s) Summary
Manifest decoding and cache merge
Meshtastic/API/MeshtasticAPI.swift, Meshtastic/Model/EventFirmwareEntity.swift, MeshtasticTests/EventFirmwareMetadataTests.swift
Manifest entries decode independently, refresh attempts are throttled, bundled data does not overwrite live cache data, and partial live responses preserve existing fields and editions.
Validation and event branding
Meshtastic/Model/EventFirmwareEntity.swift, Meshtastic/Model/EventFirmwarePresentation.swift, Meshtastic/Views/Connect/*, Meshtastic/Views/Helpers/MeshtasticLogo.swift
HTTPS URLs, colors, dates, palettes, contrast, and images are validated; event artwork, tinting, and event information presentation are wired into the app.
Notification policy
Meshtastic/Model/EventFirmwareNotificationPolicy.swift, Meshtastic/Accessory/Accessory Manager/AccessoryManager+FromRadio.swift, Meshtastic/Views/Onboarding/DeviceOnboarding.swift
Notification defaults and user overrides are represented as policy-driven settings and persisted through new defaults.
Signed OTA installation
Meshtastic/Model/EventFirmwareOTA*.swift, Meshtastic/Model/EventFirmwareArtifactDownloader.swift, Meshtastic/Views/Settings/Firmware/*
Signed contracts are verified, compatible artifacts are selected and downloaded with size/checksum validation, and installation actions are restricted to the expected active device.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers: garthvh, bruschill

Sequence Diagram(s)

sequenceDiagram
    participant FirmwareScreen
    participant AvailabilityResolver
    participant ContractVerifier
    participant ArtifactDownloader
    participant Installer

    FirmwareScreen->>AvailabilityResolver: Resolve event OTA availability
    AvailabilityResolver->>ContractVerifier: Verify signed OTA contract
    ContractVerifier-->>AvailabilityResolver: Return verified contract
    AvailabilityResolver-->>FirmwareScreen: Return compatible artifact
    FirmwareScreen->>ArtifactDownloader: Prepare artifact
    ArtifactDownloader-->>FirmwareScreen: Return verified local file
    FirmwareScreen->>Installer: Start installation for expected device
Loading

Poem

A rabbit guards the firmware cache,
With HTTPS links tucked safe and snug.
Signed OTA hops through moonlit paths,
While event colors brighten each bug.
“Install,” I whisper, “but check the node—
Then nibble tests and share the code!”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.32% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly summarizes the main change: installing event firmware in-app under a signed release contract.
Description check ✅ Passed The description explains the changes, rationale, dependencies, security behavior, fallback, and testing; optional screenshots and checklist details are not critical.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks

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.

Copilot AI review requested due to automatic review settings July 29, 2026 04:33

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@RCGV1

RCGV1 commented Jul 29, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copilot AI review requested due to automatic review settings July 29, 2026 04:41

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@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.

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
Meshtastic/Views/Settings/Firmware/Firmware.swift (1)

411-421: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Pass expectedNodeNum to local-file firmware install sheets.

Firmware.swift passes node.num for row installs, but the Catalyst file picker still opens NRFDFUSheet and ESP32OTAIntroSheet without the target node. When expectedNodeNum is nil, these sheets fall back to treating any connected device as valid, while row installs require the correct node.

🤖 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 `@Meshtastic/Views/Settings/Firmware/Firmware.swift` around lines 411 - 421,
Update the local-file firmware sheet switch in the showInstallationSheet flow to
pass the selected target node’s number as expectedNodeNum to NRFDFUSheet and
ESP32OTAIntroSheet, matching the row-install paths; leave UF2MassStorageView
unchanged unless it already supports the same parameter.
🧹 Nitpick comments (7)
Meshtastic/Views/Connect/EventFirmwareInfoView.swift (1)

278-288: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid byte-at-a-time accumulation for the icon download.

for try await byte in bytes runs one async-sequence iteration per byte — up to ~2M iterations for a max-size icon. Since the cap is already enforced, URLSession.data(for:) plus a length check is both simpler and far cheaper; if you want streaming, iterate bytes.chunks style buffers instead.

♻️ Simpler bounded fetch
-			let (bytes, response) = try await URLSession.shared.bytes(for: request)
+			let (data, response) = try await URLSession.shared.data(for: request)
 			guard let response = response as? HTTPURLResponse,
 				  (200..<300).contains(response.statusCode),
 				  response.expectedContentLength <= Int64(EventFirmwareImageValidator.maximumEncodedBytes),
-				  response.mimeType == "image/png" || response.mimeType == "image/jpeg" else {
+				  response.mimeType == "image/png" || response.mimeType == "image/jpeg",
+				  data.count <= EventFirmwareImageValidator.maximumEncodedBytes else {
 				return nil
 			}
-			var data = Data()
-			if response.expectedContentLength > 0 {
-				data.reserveCapacity(Int(response.expectedContentLength))
-			}
-			for try await byte in bytes {
-				guard data.count < EventFirmwareImageValidator.maximumEncodedBytes else {
-					return nil
-				}
-				data.append(byte)
-			}
 			return EventFirmwareImageValidator.image(from: data)

Note this buffers the full body before the cap check; keep the streaming form if you must abort mid-transfer, but buffer in chunks rather than per byte.

🤖 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 `@Meshtastic/Views/Connect/EventFirmwareInfoView.swift` around lines 278 - 288,
Update the icon download logic around the byte iteration to avoid per-byte async
accumulation. Prefer a bounded URLSession data fetch followed by a maximum-size
check before calling EventFirmwareImageValidator.image(from:), or, if early
streaming cancellation is required, consume buffered chunks while preserving the
existing cap enforcement and validation behavior.
Meshtastic/Model/EventFirmwareOTASelector.swift (1)

1-2: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing file-level header/MARK comment on new Model files. Both new files start directly with their import statements with no // MARK: FileName or copyright header, per repo convention.

  • Meshtastic/Model/EventFirmwareOTASelector.swift#L1-L2: add a // MARK: EventFirmwareOTASelector.swift (or copyright) header before import Foundation.
  • Meshtastic/Model/EventFirmwareOTAService.swift#L1-L2: add a // MARK: EventFirmwareOTAService.swift (or copyright) header before import CryptoKit.

As per coding guidelines, "Add // MARK: FileName or a file-level copyright comment at the top of files."

🤖 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 `@Meshtastic/Model/EventFirmwareOTASelector.swift` around lines 1 - 2, Add a
file-level MARK or copyright header before the imports in both
EventFirmwareOTASelector.swift (lines 1-2) and EventFirmwareOTAService.swift
(lines 1-2), using each file’s name for the MARK comment.

Source: Coding guidelines

Meshtastic/Model/EventFirmwareArtifactDownloader.swift (1)

1-3: 📐 Maintainability & Code Quality | 🔵 Trivial

Missing file-level header comment.

This new file has no // MARK: FileName or copyright header at the top, unlike EventFirmwareNotificationPolicy.swift in the same PR. As per coding guidelines, "Add // MARK: FileName or a file-level copyright comment at the top of files."

🤖 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 `@Meshtastic/Model/EventFirmwareArtifactDownloader.swift` around lines 1 - 3,
Add the project-standard file-level header at the top of
EventFirmwareArtifactDownloader.swift, using either a // MARK: FileName comment
or the same copyright-header format established by
EventFirmwareNotificationPolicy.swift. Keep it before the import statements.

Source: Coding guidelines

Meshtastic/Model/EventFirmwareOTAContract.swift (1)

1-3: 📐 Maintainability & Code Quality | 🔵 Trivial

Missing file-level header comment.

Same as EventFirmwareArtifactDownloader.swift — no // MARK: FileName or copyright header at the top of this new file. As per coding guidelines, "Add // MARK: FileName or a file-level copyright comment at the top of files."

🤖 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 `@Meshtastic/Model/EventFirmwareOTAContract.swift` around lines 1 - 3, Add a
file-level header at the top of EventFirmwareOTAContract.swift, using either the
project’s standard // MARK: FileName format or the established copyright header
pattern from EventFirmwareArtifactDownloader.swift, before the imports.

Source: Coding guidelines

Meshtastic/Views/Settings/Firmware/EventFirmwareInstallerView.swift (2)

302-309: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Log the underlying error before collapsing it to a generic message.

Download/checksum/signature failures are exactly the cases that need diagnostics, and the caught error is discarded. Add a typed Logger call (per the project's Logger extensions) while keeping the user-facing text generic.

🪵 Proposed fix
 			} catch {
+				Logger.services.error("Event firmware artifact preparation failed: \(error.localizedDescription, privacy: .public)")
 				await MainActor.run {
 					preparationState = .failed(
 						"The firmware package could not be downloaded and verified."
 					)

As per coding guidelines, "Use OSLog / Logger for all logging... Prefer the typed loggers defined in Meshtastic/Extensions/Logger.swift."

🤖 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 `@Meshtastic/Views/Settings/Firmware/EventFirmwareInstallerView.swift` around
lines 302 - 309, In the catch block of the firmware preparation flow, log the
caught error using the project’s typed Logger extension before updating
preparationState. Keep the existing generic user-facing failure message and
preparationTask cleanup unchanged.

Source: Coding guidelines


1-27: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add a file header and // MARK: - separators.

This file bundles the policy enum, the main view, a DI helper, a format extension, and two simulator-only types with no section markers or top-of-file comment.

As per coding guidelines, "Use // MARK: - comments to separate logical sections within a file" and "Add // MARK: FileName or a file-level copyright comment at the top of files."

🤖 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 `@Meshtastic/Views/Settings/Firmware/EventFirmwareInstallerView.swift` around
lines 1 - 27, Add a file-level header comment at the top of
EventFirmwareInstallerView.swift, then organize the policy enum, main view,
dependency-injection helper, format extension, and simulator-only types into
logical sections using // MARK: - separators. Keep the existing declarations and
behavior unchanged.

Source: Coding guidelines

Meshtastic/Views/Settings/Firmware/ESP32 OTA/ESP32OTAIntroSheet.swift (1)

38-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Optional-expectedNodeNum fallback is reimplemented in both OTA sheets. Both views wrap EventFirmwareInstallerPolicy.isExpectedDeviceActive with an identical guard let that degrades to "any device is connected" when expectedNodeNum is nil. Move that fallback into the policy so the weaker semantics are defined once and can be tested.

  • Meshtastic/Views/Settings/Firmware/ESP32 OTA/ESP32OTAIntroSheet.swift#L38-L46: replace the computed property body with a single call to a policy overload accepting Int64?.
  • Meshtastic/Views/Settings/Firmware/NRF DFU/NRFDFUSheet.swift#L20-L28: apply the same replacement.
🤖 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 `@Meshtastic/Views/Settings/Firmware/ESP32` OTA/ESP32OTAIntroSheet.swift around
lines 38 - 46, Move the optional expectedNodeNum fallback into
EventFirmwareInstallerPolicy by adding or using an overload accepting Int64?,
preserving “any device connected” behavior when nil. Replace the computed
property bodies in Meshtastic/Views/Settings/Firmware/ESP32
OTA/ESP32OTAIntroSheet.swift lines 38-46 and
Meshtastic/Views/Settings/Firmware/NRF DFU/NRFDFUSheet.swift lines 20-28 with a
single policy call, leaving the non-optional policy logic centralized and
reusable.
🤖 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.

Inline comments:
In `@Meshtastic/API/MeshtasticAPI.swift`:
- Around line 1073-1126: Update the manifest-to-entity assignments in the
payload mapping block so every remaining plain string field only overwrites
cached values when its payload value is non-empty. Apply this to displayName,
welcomeMessage, tag, eventStart, eventEnd, timeZone, location, domain, theme
name/tagline, theme fonts, and firmware slug/version/id/title/releaseNotes,
while preserving the existing URL and color validation behavior.

In `@Meshtastic/Assets.xcassets/EventFirmwareHAMVENTION.imageset/Contents.json`:
- Around line 3-7: Remove the "scale" entry from the hamvention.png image record
in the EventFirmwareHAMVENTION asset metadata, leaving the filename and idiom
unchanged so this single-scale imageset matches EventFirmwareDEFCON and
EventFirmwareFAB.

In `@Meshtastic/Views/Settings/Firmware/EventFirmwareInstallerView.swift`:
- Around line 204-219: Update the String-returning helpers actionTitle,
actionIcon, actionFooter, and unavailableMessage so each user-facing literal is
localized with String(localized:), preserving the existing conditional and
switch behavior. Ensure the corresponding Text and Label call sites receive the
localized strings rather than raw untranslated literals.

---

Outside diff comments:
In `@Meshtastic/Views/Settings/Firmware/Firmware.swift`:
- Around line 411-421: Update the local-file firmware sheet switch in the
showInstallationSheet flow to pass the selected target node’s number as
expectedNodeNum to NRFDFUSheet and ESP32OTAIntroSheet, matching the row-install
paths; leave UF2MassStorageView unchanged unless it already supports the same
parameter.

---

Nitpick comments:
In `@Meshtastic/Model/EventFirmwareArtifactDownloader.swift`:
- Around line 1-3: Add the project-standard file-level header at the top of
EventFirmwareArtifactDownloader.swift, using either a // MARK: FileName comment
or the same copyright-header format established by
EventFirmwareNotificationPolicy.swift. Keep it before the import statements.

In `@Meshtastic/Model/EventFirmwareOTAContract.swift`:
- Around line 1-3: Add a file-level header at the top of
EventFirmwareOTAContract.swift, using either the project’s standard // MARK:
FileName format or the established copyright header pattern from
EventFirmwareArtifactDownloader.swift, before the imports.

In `@Meshtastic/Model/EventFirmwareOTASelector.swift`:
- Around line 1-2: Add a file-level MARK or copyright header before the imports
in both EventFirmwareOTASelector.swift (lines 1-2) and
EventFirmwareOTAService.swift (lines 1-2), using each file’s name for the MARK
comment.

In `@Meshtastic/Views/Connect/EventFirmwareInfoView.swift`:
- Around line 278-288: Update the icon download logic around the byte iteration
to avoid per-byte async accumulation. Prefer a bounded URLSession data fetch
followed by a maximum-size check before calling
EventFirmwareImageValidator.image(from:), or, if early streaming cancellation is
required, consume buffered chunks while preserving the existing cap enforcement
and validation behavior.

In `@Meshtastic/Views/Settings/Firmware/ESP32` OTA/ESP32OTAIntroSheet.swift:
- Around line 38-46: Move the optional expectedNodeNum fallback into
EventFirmwareInstallerPolicy by adding or using an overload accepting Int64?,
preserving “any device connected” behavior when nil. Replace the computed
property bodies in Meshtastic/Views/Settings/Firmware/ESP32
OTA/ESP32OTAIntroSheet.swift lines 38-46 and
Meshtastic/Views/Settings/Firmware/NRF DFU/NRFDFUSheet.swift lines 20-28 with a
single policy call, leaving the non-optional policy logic centralized and
reusable.

In `@Meshtastic/Views/Settings/Firmware/EventFirmwareInstallerView.swift`:
- Around line 302-309: In the catch block of the firmware preparation flow, log
the caught error using the project’s typed Logger extension before updating
preparationState. Keep the existing generic user-facing failure message and
preparationTask cleanup unchanged.
- Around line 1-27: Add a file-level header comment at the top of
EventFirmwareInstallerView.swift, then organize the policy enum, main view,
dependency-injection helper, format extension, and simulator-only types into
logical sections using // MARK: - separators. Keep the existing declarations and
behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7a32c34d-3e5c-4dd0-93a2-d1a1c1de68a7

📥 Commits

Reviewing files that changed from the base of the PR and between de26cc4 and 181e38e.

⛔ Files ignored due to path filters (3)
  • Meshtastic/Assets.xcassets/EventFirmwareDEFCON.imageset/defcon34.png is excluded by !**/*.png
  • Meshtastic/Assets.xcassets/EventFirmwareFAB.imageset/fab26.png is excluded by !**/*.png
  • Meshtastic/Assets.xcassets/EventFirmwareHAMVENTION.imageset/hamvention.png is excluded by !**/*.png
📒 Files selected for processing (81)
  • Meshtastic/API/MeshtasticAPI.swift
  • Meshtastic/Accessory/Accessory Manager/AccessoryManager+FromRadio.swift
  • Meshtastic/Assets.xcassets/EventFirmwareDEFCON.imageset/Contents.json
  • Meshtastic/Assets.xcassets/EventFirmwareFAB.imageset/Contents.json
  • Meshtastic/Assets.xcassets/EventFirmwareHAMVENTION.imageset/Contents.json
  • Meshtastic/Enums/FirmwareEditionEnum.swift
  • Meshtastic/Extensions/UserDefaults.swift
  • Meshtastic/MeshtasticApp.swift
  • Meshtastic/Model/EventFirmwareArtifactDownloader.swift
  • Meshtastic/Model/EventFirmwareEntity.swift
  • Meshtastic/Model/EventFirmwareNotificationPolicy.swift
  • Meshtastic/Model/EventFirmwareOTAContract.swift
  • Meshtastic/Model/EventFirmwareOTASelector.swift
  • Meshtastic/Model/EventFirmwareOTAService.swift
  • Meshtastic/Model/EventFirmwarePresentation.swift
  • Meshtastic/Persistence/MarketingCapture.swift
  • Meshtastic/Persistence/Persistence.swift
  • Meshtastic/Persistence/UpdateSwiftData.swift
  • Meshtastic/Resources/docs/developer/architecture.html
  • Meshtastic/Resources/docs/developer/swiftdata.html
  • Meshtastic/Resources/docs/index.json
  • Meshtastic/Resources/docs/markdown/developer/architecture.md
  • Meshtastic/Resources/docs/markdown/developer/swiftdata.md
  • Meshtastic/Resources/docs/markdown/user/firmware.md
  • Meshtastic/Resources/docs/user/firmware.html
  • Meshtastic/Resources/event_firmware.json
  • Meshtastic/Views/Connect/Connect.swift
  • Meshtastic/Views/Connect/EventFirmwareInfoView.swift
  • Meshtastic/Views/ContentView.swift
  • Meshtastic/Views/Helpers/Compact Widgets/ParticulateMatterCompactWidget.swift
  • Meshtastic/Views/Helpers/Compact Widgets/RadiationCompactWidget.swift
  • Meshtastic/Views/Helpers/MeshtasticLogo.swift
  • Meshtastic/Views/Nodes/Helpers/Map/MapSettingsForm.swift
  • Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift
  • Meshtastic/Views/Nodes/Helpers/NodeListFilter.swift
  • Meshtastic/Views/Onboarding/DeviceOnboarding.swift
  • Meshtastic/Views/Provisioning/WifiProvisioningView.swift
  • Meshtastic/Views/Settings/AppData.swift
  • Meshtastic/Views/Settings/AppSettings.swift
  • Meshtastic/Views/Settings/Channels/ChannelForm.swift
  • Meshtastic/Views/Settings/Config/BluetoothConfig.swift
  • Meshtastic/Views/Settings/Config/DeviceConfig.swift
  • Meshtastic/Views/Settings/Config/DisplayConfig.swift
  • Meshtastic/Views/Settings/Config/LoRaConfig.swift
  • Meshtastic/Views/Settings/Config/Module/AmbientLightingConfig.swift
  • Meshtastic/Views/Settings/Config/Module/AudioConfig.swift
  • Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift
  • Meshtastic/Views/Settings/Config/Module/DetectionSensorConfig.swift
  • Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift
  • Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift
  • Meshtastic/Views/Settings/Config/Module/MeshBeaconConfig.swift
  • Meshtastic/Views/Settings/Config/Module/NeighborInfoConfig.swift
  • Meshtastic/Views/Settings/Config/Module/PaxCounterConfig.swift
  • Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift
  • Meshtastic/Views/Settings/Config/Module/SerialConfig.swift
  • Meshtastic/Views/Settings/Config/Module/StoreForwardConfig.swift
  • Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift
  • Meshtastic/Views/Settings/Config/Module/TrafficManagementConfig.swift
  • Meshtastic/Views/Settings/Config/NetworkConfig.swift
  • Meshtastic/Views/Settings/Config/PositionConfig.swift
  • Meshtastic/Views/Settings/Config/PowerConfig.swift
  • Meshtastic/Views/Settings/Config/SecurityConfig.swift
  • Meshtastic/Views/Settings/Discovery/DiscoveryScanView.swift
  • Meshtastic/Views/Settings/Firmware/ESP32 OTA/ESP32OTAIntroSheet.swift
  • Meshtastic/Views/Settings/Firmware/EventFirmwareInstallerView.swift
  • Meshtastic/Views/Settings/Firmware/Firmware.swift
  • Meshtastic/Views/Settings/Firmware/NRF DFU/NRFDFUSheet.swift
  • Meshtastic/Views/Settings/Routes.swift
  • Meshtastic/Views/Settings/ShareChannels.swift
  • Meshtastic/Views/Settings/TAKServerConfig.swift
  • Meshtastic/Views/Settings/UserConfig.swift
  • MeshtasticTests/EventFirmwareArtifactDownloaderTests.swift
  • MeshtasticTests/EventFirmwareInstallerViewTests.swift
  • MeshtasticTests/EventFirmwareMetadataTests.swift
  • MeshtasticTests/EventFirmwareNotificationTests.swift
  • MeshtasticTests/EventFirmwareOTAContractTests.swift
  • MeshtasticTests/EventFirmwareOTASelectorTests.swift
  • MeshtasticTests/EventFirmwareOTAServiceTests.swift
  • docs/developer/architecture.md
  • docs/developer/swiftdata.md
  • docs/user/firmware.md
💤 Files with no reviewable changes (26)
  • Meshtastic/Views/Helpers/Compact Widgets/ParticulateMatterCompactWidget.swift
  • Meshtastic/Views/Settings/Config/Module/NeighborInfoConfig.swift
  • Meshtastic/Views/Settings/Config/Module/AmbientLightingConfig.swift
  • Meshtastic/Views/Settings/AppData.swift
  • Meshtastic/Views/Settings/Config/Module/AudioConfig.swift
  • Meshtastic/Views/Settings/ShareChannels.swift
  • Meshtastic/Views/Settings/Config/Module/MeshBeaconConfig.swift
  • Meshtastic/Views/Settings/Config/Module/DetectionSensorConfig.swift
  • Meshtastic/Views/Helpers/Compact Widgets/RadiationCompactWidget.swift
  • Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift
  • Meshtastic/Views/Settings/Config/Module/StoreForwardConfig.swift
  • Meshtastic/Views/Settings/Config/SecurityConfig.swift
  • Meshtastic/Views/Settings/Config/DisplayConfig.swift
  • Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift
  • Meshtastic/Views/Provisioning/WifiProvisioningView.swift
  • Meshtastic/Views/Settings/Config/Module/SerialConfig.swift
  • Meshtastic/Views/Settings/Config/Module/TrafficManagementConfig.swift
  • Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift
  • Meshtastic/Views/Settings/Discovery/DiscoveryScanView.swift
  • Meshtastic/Views/Settings/Config/NetworkConfig.swift
  • Meshtastic/Views/Settings/Config/LoRaConfig.swift
  • Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift
  • Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift
  • Meshtastic/Views/Settings/Config/PositionConfig.swift
  • Meshtastic/Views/Settings/AppSettings.swift
  • Meshtastic/Views/Settings/Config/PowerConfig.swift

Comment thread Meshtastic/API/MeshtasticAPI.swift Outdated
Comment thread Meshtastic/Views/Settings/Firmware/EventFirmwareInstallerView.swift
@RCGV1

RCGV1 commented Aug 13, 2026

Copy link
Copy Markdown
Member Author

Addressed the remaining review follow-ups in f4363a6 and merged the current upstream main without rewriting branch history.

Changes include:

  • Empty live manifest strings no longer erase valid cached event metadata.
  • Event installer titles, footers, unavailable reasons, and preparation failures are localized; SF Symbol identifiers remain raw.
  • Local-file ESP32 and nRF install flows retain the selected node identity, with optional-device fallback centralized in the installer policy.
  • Event icon downloads now use a byte-capped download delegate and temporary file instead of byte-at-a-time or unbounded in-memory buffering.
  • Installer failures are logged, new event OTA files have repository-standard headers/sections, and the HAMVENTION asset uses scale-less metadata.

Validation:

  • git diff --check passed.
  • Changed Swift files passed swiftc -parse.
  • Localizable.xcstrings and the HAMVENTION asset JSON passed jq validation.
  • Changed-file SwiftLint completed with only the pre-existing EventFirmwareMetadataTests type-length warning.
  • Focused EventFirmwareInstallerViewTests and EventFirmwareCacheMergeTests were requested on iPhone 16 Pro / iOS 18.6, but current upstream fails before test execution at SecurityConfig.swift:53 with the compiler type-check timeout. The xcresult contains 0 test failures and confirms that single baseline compiler error plus cancellation. No hardware testing was performed.

@garthvh garthvh changed the title feat(firmware): add verified event OTA installer Install event firmware in-app behind a signed release contract Aug 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants