Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
65400a2
2.8: Refactor NodeInfoLite structure and add legacy support for migra…
thebentern May 7, 2026
246ee67
Add NodeEnvironmentEntry message
thebentern May 7, 2026
794d61d
Apply buf format to deviceonly.proto and deviceonly_legacy.proto
Copilot May 7, 2026
b24d92a
Potential fix for pull request finding
thebentern May 7, 2026
1495868
Merge pull request #910 from meshtastic/nodedb-split
thebentern May 9, 2026
559f3c1
Merge branch 'master' into develop
thebentern May 11, 2026
f899d38
Merge branch 'master' into develop
thebentern May 11, 2026
ff5b392
Add precision_bits field to PositionLite message for node precision i…
thebentern May 12, 2026
1089193
Add initial protobufs for XEdDSA (#753)
jp-bennett May 13, 2026
c0e9d0b
Add LockdownAuth.max_session_seconds for uptime-bounded sessions
niccellular May 15, 2026
0db7809
Clarify exposure-ceiling formula re: boots_remaining=0 sentinel
niccellular May 15, 2026
7c44755
Fix off-by-one in exposure-ceiling formula
niccellular May 15, 2026
9ab0b36
Refine session-expiry behavior doc: decrement in place, reboot only o…
niccellular May 15, 2026
7ffb4bb
Merge pull request #916 from niccellular/feature/lockdown-session-cap
thebentern May 15, 2026
e978a18
Merge remote-tracking branch 'origin/master' into develop
caveman99 May 19, 2026
b61834f
Add ITU Region 1 and Region 2/3 Amateur Radio 70cm band identifiers t…
vidplace7 May 22, 2026
a5e522d
Merge pull request #920 from vidplace7/ham-70cm
thebentern May 23, 2026
ad3ecfb
Merge branch 'develop' into master-backmerge
vidplace7 May 26, 2026
1d3a909
Merge pull request #925 from meshtastic/master-backmerge
thebentern May 26, 2026
b9fe83c
Update ham regions, split by ITU
vidplace7 May 24, 2026
7e85cc3
Merge pull request #922 from vidplace7/split-itu
thebentern May 26, 2026
83ce840
Merge branch 'master' into develop
thebentern May 27, 2026
519a0c7
Merge remote-tracking branch 'origin/master' into develop
thebentern May 29, 2026
ce55ba3
feat: add MeshBeacon portnum, wire message, and module config
NomDeTom Jun 2, 2026
6546d16
fix: add missing channel.proto and config.proto imports to module_config
NomDeTom Jun 2, 2026
3d8599d
feat: add mesh_beacon field to LocalModuleConfig (localonly.proto)
NomDeTom Jun 2, 2026
23b869d
mmmmm... beacon
NomDeTom Jun 3, 2026
d682190
legacy mode activate
NomDeTom Jun 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions meshtastic/admin.proto
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,44 @@ message LockdownAuth {
* the locked state. Always honoured regardless of current lock state.
*/
bool lock_now = 4;

/*
* Optional per-boot uptime cap on the unlocked session, in seconds.
* 0 = unlimited (token-only enforcement, suitable for unattended
* tower / infrastructure nodes).
*
* When non-zero, the firmware arms an uptime timer at unlock. On
* each expiry, while there is still boot-count budget, the firmware
* decrements the on-flash boot count in place, revokes per-
* connection admin auth (clients must re-authenticate to see
* content), re-engages the screen lock, and re-arms the timer
* without rebooting. Mesh routing keeps running across session
* boundaries; only when the boot-count budget reaches zero does
* the device hard-lock and reboot.
*
* Total exposure ceiling = ((resolved boot count) + 1) * max_session_seconds.
* The +1 accounts for the initial passphrase-unlocked session
* itself, since boots_remaining is the number of subsequent
* session rolls (each consuming one boot from the rollback ledger).
* The resolved boot count is the value the firmware writes into the
* token at unlock time: the client-supplied boots_remaining when
* non-zero, otherwise the firmware default (TOKEN_DEFAULT_BOOTS).
* Note that boots_remaining == 0 in this message means "use firmware
* default", NOT "zero boots" — a client computing the ceiling for
* display should mirror that resolution rather than multiplying the
* raw request value.
*
* The cap is persisted in the token, so it survives token-based
* auto-unlock across reboots. Explicit operator Lock Now still
* deletes the token and forces passphrase re-entry.
*
* Uses millis() (CPU uptime), not wall-clock time, so the cap is
* immune to GPS spoofing, RTC backup-battery removal, and Faraday
* cage isolation — none of those move the uptime counter. The only
* way to reset the session clock is a reboot, which costs a boot
* from the on-flash, HMAC-bound counter.
*/
uint32 max_session_seconds = 5;
}

/*
Expand Down
18 changes: 18 additions & 0 deletions meshtastic/config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,24 @@ message Config {
* ITU Region 3 Amateur Radio 2m band (144-148 MHz)
*/
ITU3_2M = 33;

/*
* ITU Region 1 Amateur Radio 70cm band (430-440 MHz)
*/
ITU1_70CM = 34;

/*
* ITU Region 2 Amateur Radio 70cm band (420-450 MHz)
* Note: Some countries do not allocate 420-430 MHz or 440-450 MHz.
* Check local law!
*/
ITU2_70CM = 35;

/*
* ITU Region 3 Amateur Radio 70cm band (430-450 MHz)
* Note: Some countries do not allocate 440-450 MHz. Check local law!
*/
ITU3_70CM = 36;
}

/*
Expand Down
12 changes: 12 additions & 0 deletions meshtastic/deviceonly.options
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@
*NodeInfoLite.hops_away int_size:8
*NodeInfoLite.next_hop int_size:8

# Flattened user fields. long_name was 40 on UserLite; trimmed to 25 here so the
# slim header gives back ~15 B/node of RAM. Names that exceeded 24 chars on the
# wire (max 40) get truncated on store, with sanitizeUtf8 cleaning up partial
# multi-byte sequences at the boundary.
*NodeInfoLite.long_name max_size:25
*NodeInfoLite.short_name max_size:5
*NodeInfoLite.public_key max_size:32
# hw_model + role used to be 32-bit enums inside UserLite. We pin them to one
# byte each on the slim header to claw back the alignment overhead.
*NodeInfoLite.hw_model int_size:8
*NodeInfoLite.role int_size:8

*UserLite.long_name max_size:40
*UserLite.short_name max_size:5
*UserLite.public_key max_size:32 # public key
Expand Down
103 changes: 73 additions & 30 deletions meshtastic/deviceonly.proto
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
* TODO: REPLACE
*/
Position.LocSource location_source = 5;

/*
* Indicates the bits of precision set by the sending node
*/
uint32 precision_bits = 6;
}

message UserLite {
Expand Down Expand Up @@ -101,77 +106,85 @@
optional bool is_unmessagable = 9;
}

message NodeInfoLite {
/*
* The node number
*/
uint32 num = 1;

/*
* The user info for this node
*/
UserLite user = 2;

/*
* This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true.
* Position.time now indicates the last time we received a POSITION from that node.
*/
PositionLite position = 3;
// Tag 2 was UserLite user; flattened into tags 14..18 (presence via
// NODEINFO_BITFIELD_HAS_USER).
// Tag 3 was PositionLite position; moved to NodeDatabase.positions.
reserved 2, 3;
reserved "user", "position";

/*
* Returns the Signal-to-noise ratio (SNR) of the last received message,
* as measured by the receiver. Return SNR of the last received message in dB
*/
float snr = 4;

/*
* Set to indicate the last time we received a packet from this node
*/
fixed32 last_heard = 5;
/*
* The latest device metrics for the node.
*/
DeviceMetrics device_metrics = 6;

// Tag 6 was DeviceMetrics device_metrics; moved to NodeDatabase.telemetry.
reserved 6;
reserved "device_metrics";

/*
* local channel index we heard that node on. Only populated if its not the default channel.
*/
uint32 channel = 7;

/*
* True if we witnessed the node over MQTT instead of LoRA transport
*/
bool via_mqtt = 8;
// Tags 8/10/11 were via_mqtt/is_favorite/is_ignored bools; packed into bitfield.
reserved 8, 10, 11;
reserved "via_mqtt", "is_favorite", "is_ignored";

/*
* Number of hops away from us this node is (0 if direct neighbor)
*/
optional uint32 hops_away = 9;

/*
* True if node is in our favorites list
* Persists between NodeDB internal clean ups
* Last byte of the node number of the node that should be used as the next hop to reach this node.
*/
bool is_favorite = 10;
uint32 next_hop = 12;

/*
* True if node is in our ignored list
* Persists between NodeDB internal clean ups
* Bitfield for storing booleans. See NODEINFO_BITFIELD_* in src/mesh/NodeDB.h.
*/
bool is_ignored = 11;
uint32 bitfield = 13;

// Flattened user fields (formerly UserLite). macaddr dropped (deprecated 1.2.11).

/*
* Last byte of the node number of the node that should be used as the next hop to reach this node.
* A full name for this user, i.e. "Kevin Hester".
*/
uint32 next_hop = 12;
string long_name = 14;

/*
* Bitfield for storing booleans.
* LSB 0 is_key_manually_verified
* LSB 1 is_muted
* A VERY short name, ideally two characters or an emoji.
* Suitable for a tiny OLED screen.
*/
uint32 bitfield = 13;
string short_name = 15;

/*
* Hardware model the user's device is running.
*/
HardwareModel hw_model = 16;

/*
* The user's role in the mesh.
*/
Config.DeviceConfig.Role role = 17;

/*
* The public key of the user's device, for PKI-based encrypted DMs.
*/
bytes public_key = 18;
}

Check failure on line 187 in meshtastic/deviceonly.proto

View workflow job for this annotation

GitHub Actions / build

Previously present field "8" with name "via_mqtt" on message "NodeInfoLite" was deleted.

Check failure on line 187 in meshtastic/deviceonly.proto

View workflow job for this annotation

GitHub Actions / build

Previously present field "6" with name "device_metrics" on message "NodeInfoLite" was deleted.

Check failure on line 187 in meshtastic/deviceonly.proto

View workflow job for this annotation

GitHub Actions / build

Previously present field "3" with name "position" on message "NodeInfoLite" was deleted.

Check failure on line 187 in meshtastic/deviceonly.proto

View workflow job for this annotation

GitHub Actions / build

Previously present field "2" with name "user" on message "NodeInfoLite" was deleted.

Check failure on line 187 in meshtastic/deviceonly.proto

View workflow job for this annotation

GitHub Actions / build

Previously present field "11" with name "is_ignored" on message "NodeInfoLite" was deleted.

Check failure on line 187 in meshtastic/deviceonly.proto

View workflow job for this annotation

GitHub Actions / build

Previously present field "10" with name "is_favorite" on message "NodeInfoLite" was deleted.

/*
* This message is never sent over the wire, but it is used for serializing DB
Expand Down Expand Up @@ -236,6 +249,29 @@
repeated NodeRemoteHardwarePin node_remote_hardware_pins = 13;
}

// Satellite per-node entries; stored alongside the slim NodeInfoLite so nodes
// that never report don't pay the embedded cost.

message NodePositionEntry {
uint32 num = 1;
PositionLite position = 2;
}

message NodeTelemetryEntry {
uint32 num = 1;
DeviceMetrics device_metrics = 2;
}

message NodeEnvironmentEntry {
uint32 num = 1;
EnvironmentMetrics environment_metrics = 2;
}

message NodeStatusEntry {
uint32 num = 1;
StatusMessage status = 2;
}

message NodeDatabase {
/*
* A version integer used to invalidate old save files when we make
Expand All @@ -248,6 +284,13 @@
* New lite version of NodeDB to decrease memory footprint
*/
repeated NodeInfoLite nodes = 2 [(nanopb).callback_datatype = "std::vector<meshtastic_NodeInfoLite>"];

// Per-NodeNum satellite arrays. Constrained platforms (e.g. STM32WL) omit
// these via MESHTASTIC_EXCLUDE_*DB build flags.
repeated NodePositionEntry positions = 3 [(nanopb).callback_datatype = "std::vector<meshtastic_NodePositionEntry>"];
repeated NodeTelemetryEntry telemetry = 4 [(nanopb).callback_datatype = "std::vector<meshtastic_NodeTelemetryEntry>"];
repeated NodeStatusEntry status = 5 [(nanopb).callback_datatype = "std::vector<meshtastic_NodeStatusEntry>"];
repeated NodeEnvironmentEntry environment = 6 [(nanopb).callback_datatype = "std::vector<meshtastic_NodeEnvironmentEntry>"];
}

/*
Expand Down
7 changes: 7 additions & 0 deletions meshtastic/deviceonly_legacy.options
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# nanopb options for the legacy migration descriptor.
# Mirrors deviceonly.options sizing for the NodeInfoLite_Legacy message so old
# /prefs/nodes.proto saves parse with the same byte layout.

*NodeInfoLite_Legacy.channel int_size:8
*NodeInfoLite_Legacy.hops_away int_size:8
*NodeInfoLite_Legacy.next_hop int_size:8
50 changes: 50 additions & 0 deletions meshtastic/deviceonly_legacy.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
syntax = "proto3";

package meshtastic;

/* trunk-ignore(buf-lint/COMPILE) */
import "meshtastic/deviceonly.proto";
import "meshtastic/telemetry.proto";
import "nanopb.proto";

option csharp_namespace = "Meshtastic.Protobufs";
option go_package = "github.com/meshtastic/go/generated";
option java_outer_classname = "DeviceOnlyLegacy";
option java_package = "org.meshtastic.proto";
option swift_prefix = "";
option (nanopb_fileopt).include = "<vector>";

/*
* Legacy NodeInfoLite descriptor used only to decode pre-split
* /prefs/nodes.proto saves during the v24 -> v25 migration boot.
* This preserves the original NodeInfoLite-compatible field numbers needed
* to parse old wire bytes cleanly, including user (2), position (3),
* device_metrics (6), and the legacy-only compatibility fields via_mqtt (8),
* is_favorite (10), and is_ignored (11). Steady-state code does not use
* this struct; it is dropped after migration completes. This file should be
* removed once DEVICESTATE_MIN_VER advances past 24.
*/
message NodeInfoLite_Legacy {
uint32 num = 1;
UserLite user = 2;
PositionLite position = 3;
float snr = 4;
fixed32 last_heard = 5;
DeviceMetrics device_metrics = 6;
uint32 channel = 7;
bool via_mqtt = 8;
optional uint32 hops_away = 9;
bool is_favorite = 10;
bool is_ignored = 11;
uint32 next_hop = 12;
uint32 bitfield = 13;
}

/*
* Legacy NodeDatabase shape: one repeated array of fat NodeInfoLite_Legacy
* with no satellite position/telemetry arrays.
*/
message NodeDatabase_Legacy {
uint32 version = 1;
repeated NodeInfoLite_Legacy nodes = 2 [(nanopb).callback_datatype = "std::vector<meshtastic_NodeInfoLite_Legacy>"];
}
5 changes: 5 additions & 0 deletions meshtastic/localonly.proto
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ message LocalModuleConfig {
*/
ModuleConfig.TAKConfig tak = 17;

/*
* MeshBeacon Config
*/
ModuleConfig.MeshBeaconConfig mesh_beacon = 18;

/*
* A version integer used to invalidate old save files when we make
* incompatible changes This integer is set at build time and is private to
Expand Down
1 change: 1 addition & 0 deletions meshtastic/mesh.options
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
# outside of this envelope
*Data.payload max_size:233
*Data.bitfield int_size:8
*Data.xeddsa_signature max_size:64

*NodeInfo.channel int_size:8
*NodeInfo.hops_away int_size:8
Expand Down
21 changes: 19 additions & 2 deletions meshtastic/mesh.proto
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
syntax = "proto3";

package meshtastic;
Expand Down Expand Up @@ -899,8 +899,8 @@
HELTEC_MESH_NODE_T1 = 133;

/*
* B&Q Consulting Station G3: TBD
*/
* B&Q Consulting Station G3: TBD
*/
STATION_G3 = 134;

/*
Expand Down Expand Up @@ -1210,6 +1210,11 @@
* Bitfield for extra flags. First use is to indicate that user approves the packet being uploaded to MQTT.
*/
optional uint32 bitfield = 9;

/*
* XEdDSA signature for the payload
*/
bytes xeddsa_signature = 10;
}

/*
Expand Down Expand Up @@ -1789,6 +1794,11 @@
* Indicates which transport mechanism this packet arrived over
*/
TransportMechanism transport_mechanism = 21;

/*
* Indicates whether the packet has a valid signature
*/
bool xeddsa_signed = 22;
}

/*
Expand Down Expand Up @@ -1925,6 +1935,13 @@
* Persistes between NodeDB internal clean ups
*/
bool is_muted = 13;

/*
* True if node is signing its packets via XEdDSA
* Persists between NodeDB internal clean ups
* LSB 1 of the bitfield
*/
bool has_xeddsa_signed = 14;
}

/*
Expand Down
3 changes: 3 additions & 0 deletions meshtastic/mesh_beacon.options
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*MeshBeacon.message max_size:101
*MeshBeacon.offer_channel.name max_size:12
*MeshBeacon.offer_channel.psk max_size:32
Loading
Loading