feat(security): add packet authenticity policy aligned with Android - #2219
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
🚧 Files skipped from review as they are similar to previous changes (7)
📝 WalkthroughWalkthroughThe PR adds packet authenticity policy support across protobufs, persistence, security settings UI, localization, documentation, and tests. It also regenerates interdevice protocol bindings and adds firmware and telemetry enum values. ChangesPacket authenticity policy
Generated protocol refresh
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related issues
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (3 warnings)
✅ Passed checks (2 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 |
Re-implements the device-enforced packet authenticity policy from design#121 against the merged Android implementation (Meshtastic-Android#6178), replacing the stale #2066 draft. Bumps the protobufs submodule 9d589c1 -> b6ad0e5 (the merge of protobufs#983) and regenerates the Swift sources with the repo-pinned protoc-gen-swift 1.36.1. b6ad0e5 is the earliest commit carrying the schema, so the incidental additive churn (interdevice SD-card commands, HM330X sensor, FAB edition, an EU_868 deprecation comment) is the minimum available. Presents the three levels as a picker with a per-policy summary, gated on the tri-state DeviceMetadata.has_xeddsa capability, and confirms before Strict. This drops #2066's bespoke drag-gesture "policy rail" — a control type used nowhere else in the app and nowhere on Android — in favour of the dropdown both clients agreed to present. Binding the protobuf enum directly follows the existing Team/MemberRole pickers in TAKServerConfig. All ten user-facing policy labels, summaries and the Strict warning are byte-identical to Android's strings.xml. Persists the policy and the read-only capability. Both entities already live in the unreleased MeshtasticSchemaV1, so these additive properties need no new VersionedSchema or MigrationStage. The policy is always written back on save so a level set by newer firmware round-trips instead of being reset to Compatible, and .UNRECOGNIZED is included in the picker's options when a device reports one so the control can render the policy actually in force. Per the constitution, the entity accessor lives in Extensions/SwiftData/ and the protobuf display accessors in Extensions/Protobufs/. The SecurityConfig wiring sits in an extension because that struct was already at SwiftLint's type_body_length ceiling. No hardware verification: the policy is enforced by firmware, so a read/write/read-back round trip against a capable radio is still required. Closes #2065. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2d4cbac to
eba2406
Compare
There was a problem hiding this comment.
Pull request overview
Note
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Adds a new firmware-enforced Packet Authenticity (“packet signature policy”) setting to iOS Security settings, aligned with the merged Android/Desktop behavior and wire contract.
Changes:
- Bumps protobufs and regenerates SwiftProtobuf sources to include
SecurityConfig.packet_signature_policyandDeviceMetadata.has_xeddsa. - Adds SwiftUI UI + state machine for selecting Compatible/Balanced/Strict (with Strict confirmation), gated by tri-state XEdDSA capability.
- Persists policy + capability in SwiftData and backup import paths; adds unit + snapshot tests and updates bundled docs.
Reviewed changes
Copilot reviewed 23 out of 33 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| protobufs | Updates protobuf submodule pin to bring in packet authenticity schema and related additions. |
| docs/user/whats-new.md | Documents the new Packet Authenticity setting in release notes. |
| docs/user/settings.md | Adds user documentation for Protection Level behavior and implications. |
| MeshtasticTests/SwiftUIViewSnapshotTests.swift | Adds snapshot coverage for the Packet Authenticity section across states and themes. |
| MeshtasticTests/PacketAuthenticityTests.swift | Adds wire-contract, persistence, ingestion, and selection state-machine tests. |
| MeshtasticProtobufs/Sources/meshtastic/telemetry.pb.swift | Regenerated protobuf output (adds telemetry enum case). |
| MeshtasticProtobufs/Sources/meshtastic/mesh.pb.swift | Regenerated protobuf output (adds firmware edition + has_xeddsa). |
| MeshtasticProtobufs/Sources/meshtastic/interdevice.pb.swift | Regenerated protobuf output (interdevice protocol expansion). |
| MeshtasticProtobufs/Sources/meshtastic/config.pb.swift | Regenerated protobuf output (adds packet_signature_policy field + enum). |
| Meshtastic/Views/Settings/Config/SecurityConfig.swift | Wires packet authenticity section into Security config UI and write-back. |
| Meshtastic/Views/Settings/Config/PacketAuthenticity.swift | Implements capability tri-state, selection state machine, and the SwiftUI section UI. |
| Meshtastic/Resources/docs/user/whats-new.html | Rebuilt bundled HTML docs including the new release note entry. |
| Meshtastic/Resources/docs/user/settings.html | Rebuilt bundled HTML docs including Packet Authenticity section. |
| Meshtastic/Resources/docs/markdown/user/whats-new.md | Rebuilt bundled markdown docs including the new release note entry. |
| Meshtastic/Resources/docs/markdown/user/settings.md | Rebuilt bundled markdown docs including Packet Authenticity section. |
| Meshtastic/Resources/docs/index.json | Rebuilt docs search index to include new content/keywords. |
| Meshtastic/Persistence/UpdateSwiftData.swift | Persists packet signature policy on SecurityConfig upsert (insert + update paths). |
| Meshtastic/Persistence/NodeBackupManager+Import.swift | Imports the hasXeddsa capability as part of node backup restore. |
| Meshtastic/Model/DeviceMetadataEntity.swift | Persists hasXeddsa and copies it during metadata ingestion. |
| Meshtastic/Model/ConfigModels.swift | Adds SwiftData storage for SecurityConfigEntity.packetSignaturePolicy. |
| Meshtastic/Extensions/SwiftData/SecurityConfigEntityExtension.swift | Adds decoding helper back into the protobuf enum, preserving unknown values. |
| Meshtastic/Extensions/Protobufs/Config+PacketSignaturePolicy.swift | Adds localized titles/descriptions and picker option helpers for the policy enum. |
| Localizable.xcstrings | Adds new localization keys for policy titles/descriptions and Strict confirmation copy. |
Comments suppressed due to low confidence (3)
docs/user/settings.md:1
- The table has an extra leading
|on every row (|| ...), which creates an unintended empty first column in standard Markdown renderers (e.g. GitHub). Update the table rows to use a single leading pipe (| Level | Behavior |, etc.) so the docs render consistently.
Localizable.xcstrings:1 - These new user-facing strings have no translator comment, unlike the rest of the new Packet Authenticity strings. Add brief
commentfields (e.g., “Security section header for packet authenticity policy” / “Label for packet authenticity policy picker”) to improve translation quality and consistency.
Localizable.xcstrings:1 - These new user-facing strings have no translator comment, unlike the rest of the new Packet Authenticity strings. Add brief
commentfields (e.g., “Security section header for packet authenticity policy” / “Label for packet authenticity policy picker”) to improve translation quality and consistency.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
Meshtastic/Model/ConfigModels.swift (1)
342-344: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider documenting the new persisted attribute.
SecurityConfigEntitygains a persisted property, but the PR's doc changes appear limited todocs/user/*. Add a note indocs/developer/swiftdata.md(and regenerate bundled HTML) if the schema doc enumerates entity attributes.As per path instructions: "Update
docs/developer/swiftdata.mdordocs/developer/architecture.mdwhen model code changes affect persistence or architecture."🤖 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/ConfigModels.swift` around lines 342 - 344, Document the new persisted SecurityConfigEntity attribute packetSignaturePolicy in docs/developer/swiftdata.md, following the existing entity-attribute format; regenerate the bundled HTML documentation if that file is generated from the Markdown source.Source: Path instructions
Meshtastic/Model/DeviceMetadataEntity.swift (1)
21-23: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUpdate
docs/developer/swiftdata.mdfor the new persisted field.
hasXeddsais a new stored property on theDeviceMetadataEntitySwiftData model, gating the Packet Authenticity selector downstream. No update todocs/developer/swiftdata.md(orarchitecture.md) is included in this PR's file set.As per coding guidelines, "Meshtastic/Model/**/*.swift: Update docs/developer/swiftdata.md or docs/developer/architecture.md when model code changes affect persistence or architecture."
Also applies to: 40-40
🤖 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/DeviceMetadataEntity.swift` around lines 21 - 23, Update the SwiftData documentation in docs/developer/swiftdata.md to document DeviceMetadataEntity.hasXeddsa as a persisted read-only capability field and explain its role in gating the Packet Authenticity policy selector. Follow the existing model documentation conventions and include the corresponding architecture documentation only if required by those conventions.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.
Inline comments:
In `@Localizable.xcstrings`:
- Around line 14588-14590: Update the packet-authenticity labels in
Localizable.xcstrings at lines 14588-14590 and 92641-92643: change “Balanced —
Prefer authenticated” to “Balanced — Prefer signed” and “Strict — Require
authenticated” to “Strict — Require signed,” preserving the existing
localization entries.
---
Nitpick comments:
In `@Meshtastic/Model/ConfigModels.swift`:
- Around line 342-344: Document the new persisted SecurityConfigEntity attribute
packetSignaturePolicy in docs/developer/swiftdata.md, following the existing
entity-attribute format; regenerate the bundled HTML documentation if that file
is generated from the Markdown source.
In `@Meshtastic/Model/DeviceMetadataEntity.swift`:
- Around line 21-23: Update the SwiftData documentation in
docs/developer/swiftdata.md to document DeviceMetadataEntity.hasXeddsa as a
persisted read-only capability field and explain its role in gating the Packet
Authenticity policy selector. Follow the existing model documentation
conventions and include the corresponding architecture documentation only if
required by those conventions.
🪄 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: 20d2e09e-6d3b-44b6-ab13-8d59b61bb4ba
⛔ Files ignored due to path filters (10)
MeshtasticTests/__Snapshots__/SwiftUIViewSnapshotTests/packetAuthenticity_balanced_dark.pngis excluded by!**/*.pngMeshtasticTests/__Snapshots__/SwiftUIViewSnapshotTests/packetAuthenticity_balanced_light.pngis excluded by!**/*.pngMeshtasticTests/__Snapshots__/SwiftUIViewSnapshotTests/packetAuthenticity_compatible_dark.pngis excluded by!**/*.pngMeshtasticTests/__Snapshots__/SwiftUIViewSnapshotTests/packetAuthenticity_compatible_light.pngis excluded by!**/*.pngMeshtasticTests/__Snapshots__/SwiftUIViewSnapshotTests/packetAuthenticity_strict_dark.pngis excluded by!**/*.pngMeshtasticTests/__Snapshots__/SwiftUIViewSnapshotTests/packetAuthenticity_strict_light.pngis excluded by!**/*.pngMeshtasticTests/__Snapshots__/SwiftUIViewSnapshotTests/packetAuthenticity_unknown_dark.pngis excluded by!**/*.pngMeshtasticTests/__Snapshots__/SwiftUIViewSnapshotTests/packetAuthenticity_unknown_light.pngis excluded by!**/*.pngMeshtasticTests/__Snapshots__/SwiftUIViewSnapshotTests/packetAuthenticity_unsupported_dark.pngis excluded by!**/*.pngMeshtasticTests/__Snapshots__/SwiftUIViewSnapshotTests/packetAuthenticity_unsupported_light.pngis excluded by!**/*.png
📒 Files selected for processing (23)
Localizable.xcstringsMeshtastic/Extensions/Protobufs/Config+PacketSignaturePolicy.swiftMeshtastic/Extensions/SwiftData/SecurityConfigEntityExtension.swiftMeshtastic/Model/ConfigModels.swiftMeshtastic/Model/DeviceMetadataEntity.swiftMeshtastic/Persistence/NodeBackupManager+Import.swiftMeshtastic/Persistence/UpdateSwiftData.swiftMeshtastic/Resources/docs/index.jsonMeshtastic/Resources/docs/markdown/user/settings.mdMeshtastic/Resources/docs/markdown/user/whats-new.mdMeshtastic/Resources/docs/user/settings.htmlMeshtastic/Resources/docs/user/whats-new.htmlMeshtastic/Views/Settings/Config/PacketAuthenticity.swiftMeshtastic/Views/Settings/Config/SecurityConfig.swiftMeshtasticProtobufs/Sources/meshtastic/config.pb.swiftMeshtasticProtobufs/Sources/meshtastic/interdevice.pb.swiftMeshtasticProtobufs/Sources/meshtastic/mesh.pb.swiftMeshtasticProtobufs/Sources/meshtastic/telemetry.pb.swiftMeshtasticTests/PacketAuthenticityTests.swiftMeshtasticTests/SwiftUIViewSnapshotTests.swiftdocs/user/settings.mddocs/user/whats-new.mdprotobufs
The path instructions map Meshtastic/Model/ changes to docs/developer/swiftdata.md or architecture.md, which the previous commit missed while updating only the user guide. Documents both additive V1 properties alongside the existing TelemetryEntity particulate-field example, notes why an added property's default must match what an absent value means on the wire, and adds the developer What's New entry for the protobuf bump. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
c09cb67 to
76a9408
Compare
…g title Exporting omitted SecurityConfig.packet_signature_policy (added on main in meshtastic#2219 after this branch's last update), so every iOS-exported profile carried Compatible and importing it back silently downgraded a Balanced/Strict radio. The verifier compares through the same entity->proto converter, so it also reported a false mismatch after importing a profile with a non-default policy. One-line converter fix covers export and verification; imports of foreign profiles already passed the decoded proto through unchanged. Tests cover the three known policies and an unrecognized future value. Also shortens the import warning callout title per review.
What changed?
Adds the device-enforced packet authenticity policy from design#121 to Security settings, implemented against the merged Android/Desktop version (Meshtastic-Android#6178).
Supersedes #2066. That draft, by @RCGV1, did the groundwork this builds on — it drove protobufs#983, coordinated the cross-client contract in design#121, and established the "authenticated" rather than "signed" terminology that both clients now use. It had drifted ~100 commits behind
mainand conflicted, and its UI had diverged from what Android shipped, so this is a fresh implementation rather than a rebase.9d589c1 → b6ad0e5(the merge of protobufs#983), Swift sources regenerated with the repo-pinnedprotoc-gen-swift1.36.1 viascripts/gen_protos.sh. Wire contract:COMPATIBLE = 0,BALANCED = 1,STRICT = 2onSecurityConfig.packet_signature_policy(field 9);DeviceMetadata.has_xeddsa(field 14).has_xeddsacapability, with a confirmation before Strict.SecurityConfigEntity.packetSignaturePolicy) and the read-only capability (DeviceMetadataEntity.hasXeddsa), including the backup import path. Both entities already live in the unreleasedMeshtasticSchemaV1, so these additive properties need no newVersionedSchema/MigrationStage.strings.xml, verified programmatically.Differences from #2066
matchedGeometryEffect, and a drag gesture snapping to the nearest policyPicker+ summary, matching Android'sDropDownPreferenceand every other picker in Settings.UNRECOGNIZEDabsent from the picker's optionsMeshPacketsDeviceMetadataEntity.update(from:), wheremainnow centralises metadata ingestionThe rail was the main reason to redo rather than rebase: a control type used nowhere else in this app and nowhere on Android, for a setting both clients agreed to present as a dropdown.
Deliberate divergences from Android
.UNRECOGNIZED. Wire generates a closed Kotlin enum so Android has no unrecognized case; SwiftProtobuf's open enum does.Pickerin aFormreads as broken rather than not-yet-available.Why did it change?
The policy is enforced by firmware and is already merged in protobufs, firmware (firmware#10967) and Android. Without this, iOS users on capable firmware have no way to see or change the receive policy their radio is applying, and the two apps present different contracts for the same device setting.
Compatible is the protobuf zero value, deliberately, so that an absent field means Compatible rather than silently implying a stricter policy than the radio is running. The tests pin that.
How is this tested?
xcodebuild teston iPhone 17 Pro Simulator — 42 tests in 7 suites passed.MeshtasticTests/PacketAuthenticityTests.swift(32 cases): exact field-9 wire bytes per level, Compatible omitted as the zero value, an unknown level surviving a decode/encode round trip,has_xeddsafield 14, the tri-state capability, the persistence round trip through both branches ofupsertSecurityConfigPacket, and the selection state machine including the disconnect/capability-loss race.SwiftUIViewSnapshotTests(10 references): each policy plus the unsupported and unknown states, light and dark.scripts/gen_protos.sh b6ad0e5f4e4716f50ec414cf4e28efca289cdce3— regenerated with the pinned plugin, no generator drift.bash scripts/build-docs.sh --output Meshtastic/Resources/docs— 31 pages rebuilt.jq empty Localizable.xcstringspasses; the 15 new keys are a pure insertion (41 added lines, 0 deletions, no existing entry moved).Not tested: hardware
No radio verification has been performed. The policy is enforced by firmware, so a read/write/read-back round trip against a capable device is still required before merge — the same gap #2066 carried. Opened for review so the code, wire contract, and cross-client alignment can be assessed in parallel; please treat the hardware round trip as an outstanding merge condition rather than something already covered.
Screenshots/Videos (when applicable)
Committed snapshot references, which are regression tests rather than one-off captures:
packetAuthenticity_compatible_light.pngpacketAuthenticity_compatible_dark.pngpacketAuthenticity_balanced_light.pngpacketAuthenticity_balanced_dark.pngpacketAuthenticity_strict_light.pngpacketAuthenticity_strict_dark.pngpacketAuthenticity_unsupported_light.pngpacketAuthenticity_unsupported_dark.pngpacketAuthenticity_unknown_light.pngpacketAuthenticity_unknown_dark.pngAll under
MeshtasticTests/__Snapshots__/SwiftUIViewSnapshotTests/. The Strict confirmation is an.alert, which a windowless host does not render, so it is covered by the state-machine tests instead.Conventions and constitution
Audited against
.specify/memory/constitution.md,.github/copilot-instructions.md, the surrounding code, and design standards v1.4:Extensions/SwiftData/SecurityConfigEntityExtension.swiftand the protobuf display accessors inExtensions/Protobufs/Config+PacketSignaturePolicy.swift. (The constitution namesExtensions/CoreData/, which the repo has since renamed toSwiftData/;Extensions/Protobufs/is new, created as the constitution specifies.)SecurityConfighad pushed it from 400 to 410 lines and trippedtype_body_length, so its packet-authenticity wiring lives in an extension. The pre-commit hook fromscripts/setup-hooks.shis installed and ran on this commit..orange, whichExtensions/Color+Brand.swiftshadows to the Meshtastic Warning token#E8A33E, and matches the existing admin-key warning in the same screen.id: \.rawValuefollows the existingTeam/MemberRolepickers inTAKServerConfig.swift. Most config pickers instead wrap the value in a local enum underMeshtastic/Enums/; that pattern cannot represent an unrecognized value, which is the case this setting has to preserve.@Suite,@Test,#expect,#require. No XCTest.docs/user/whats-new.mdgains a Jul 2026 entry and the bundled HTML was rebuilt.Meshtastic/Extensions,Meshtastic/ViewsandMeshtasticTestsaresyncedFoldertargets, so the new files and the newProtobufs/directory need noproject.pbxprojchange.Open question for review
Android shows this control disabled with an explanation when a radio has not reported XEdDSA support, and this PR follows that. The alternative, which design standards v1.4 ("Null Data Suppression") and the neighbouring
LockdownSectionwould both suggest, is to hide the section outright on firmware that cannot use it. I kept the Android behaviour because packet authenticity is a mainstream feature rolling out with new firmware, where "update your firmware" is actionable, whereas lockdown is a niche hardened build. Happy to switch if reviewers prefer suppression.Incidental protobuf churn
b6ad0e5is the earliest commit carrying the packet-signature schema, so this is the minimum bump available. It also pulls the commits between it and the old pin:interdeviceSD-card commands, theHM330Xtelemetry sensor, theFABfirmware edition, and an EU_868 deprecation comment. All purely additive — no removed or renamed cases.Checklist
docs/user/ordocs/developer/, and updated accordingly —docs/user/settings.mdgains a Packet Authenticity section and the bundled docs were rebuilt.Closes #2065.
Summary by CodeRabbit