diff --git a/core/proto/build.gradle.kts b/core/proto/build.gradle.kts
index e60195e199..c3650d26b3 100644
--- a/core/proto/build.gradle.kts
+++ b/core/proto/build.gradle.kts
@@ -27,7 +27,11 @@ kotlin {
// Override minSdk for ATAK compatibility (standard is 26)
android { minSdk = 21 }
- sourceSets { commonMain.dependencies { api(libs.wire.runtime) } }
+ sourceSets {
+ commonMain {
+ dependencies { api(libs.wire.runtime) }
+ }
+ }
}
wire {
@@ -45,6 +49,10 @@ wire {
// Codebase is already written to use the nullable properties (e.g. packet.decoded vs
// packet.payload_variant.decoded).
boxOneOfsMinSize = 5000
+
+ // Emit Kotlin annotations from scalar proto field options (e.g. diy_only).
+ // Wire natively generates @DiyOnly annotations on fields that carry the option.
+ emitAppliedOptions = true
}
root("meshtastic.*")
prune("meshtastic.MeshPacket#delayed")
diff --git a/core/proto/src/commonMain/kotlin/org/meshtastic/core/proto/FieldMetadataDemo.kt b/core/proto/src/commonMain/kotlin/org/meshtastic/core/proto/FieldMetadataDemo.kt
new file mode 100644
index 0000000000..f701aceeb6
--- /dev/null
+++ b/core/proto/src/commonMain/kotlin/org/meshtastic/core/proto/FieldMetadataDemo.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2025-2026 Meshtastic LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.meshtastic.core.proto
+
+import org.meshtastic.proto.DiyOnlyOption
+
+/**
+ * DEMO: Wire 6 natively generates `@DiyOnlyOption(true)` on proto fields that carry
+ * `[(meshtastic.diy_only) = true]` — zero custom build code needed.
+ *
+ * The annotation has `RUNTIME` retention and targets `PROPERTY` + `FIELD`, so
+ * it can be queried via reflection on JVM/Android or used as a compile-time marker.
+ *
+ * Generated code in `Config.PositionConfig`:
+ * ```kotlin
+ * @DiyOnlyOption(true)
+ * @field:WireField(tag = 8, ...)
+ * public val rx_gpio: Int = 0,
+ * ```
+ *
+ * See: https://github.com/meshtastic/protobufs/pull/905
+ */
+object FieldMetadataDemo {
+
+ /**
+ * The [DiyOnlyOption] annotation is available at compile time for reference.
+ * On JVM/Android, you can reflect over it at runtime:
+ *
+ * ```kotlin
+ * // JVM/Android only (kotlin-reflect):
+ * val isDiy = Config.PositionConfig::rx_gpio
+ * .findAnnotation()?.value ?: false
+ * ```
+ *
+ * For KMP-safe access without reflection, use a constants map:
+ */
+ val diyOnlyFields: Set = setOf("rx_gpio", "tx_gpio")
+
+ /**
+ * UI gating — hide settings that are diy_only when device isn't DIY:
+ *
+ * ```kotlin
+ * if ("rx_gpio" !in FieldMetadataDemo.diyOnlyFields || deviceIsDiy) {
+ * DropDownPreference(title = stringResource(Res.string.gps_receive_gpio), ...)
+ * }
+ * ```
+ */
+ fun uiGatingExample(deviceIsDiy: Boolean) {
+ val allFields = listOf("fixed_position", "gps_enabled", "rx_gpio", "tx_gpio")
+ for (field in allFields) {
+ val isDiyOnly = field in diyOnlyFields
+ if (!isDiyOnly || deviceIsDiy) {
+ println("Showing $field setting")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/proto/src/main/proto b/core/proto/src/main/proto
index 10a16897b4..fc5a1a25de 160000
--- a/core/proto/src/main/proto
+++ b/core/proto/src/main/proto
@@ -1 +1 @@
-Subproject commit 10a16897b46914854bb46bd94bedb16c6fad3a8b
+Subproject commit fc5a1a25de8a79fd28dbf1b6ec7bd61100f7eb71
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index c603a00e1c..53629727bd 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -167,6 +167,7 @@ maps-compose-widgets = { module = "com.google.maps.android:maps-compose-widgets"
mlkit-barcode-scanning = { module = "com.google.mlkit:barcode-scanning", version.ref = "mlkit-barcode-scanning" }
play-services-maps = { module = "com.google.android.gms:play-services-maps", version = "20.0.0" }
wire-runtime = { module = "com.squareup.wire:wire-runtime", version.ref = "wire" }
+wire-schema = { module = "com.squareup.wire:wire-schema", version.ref = "wire" }
zxing-core = { module = "com.google.zxing:core", version = "3.5.4" }
qrcode-kotlin = { module = "io.github.g0dkar:qrcode-kotlin", version.ref = "qrcode-kotlin" }