@@ -30,7 +30,7 @@ Sources of truth: `src/mesh/NodeDB.{h,cpp}`, `src/mesh/WarmNodeStore.h`,
3030 ESP32, 10 on STM32WL (see ` mesh-pb-constants.h ` ).
3131- ** Eviction:** oldest non-protected node when full (` getOrCreateMeshNode ` ). On eviction
3232 the node's essentials are ** absorbed into the warm tier** (see §2); on re-admission the
33- warm record is rehydrated back (` take() ` ), including the signer bit.
33+ warm record is rehydrated back (` take() ` ), including the XEdDSA-signed bit.
3434- ** Persistence:** the node database file in LittleFS, saved on the usual NodeDB cadence.
3535- ** Authority:** key pinning (` updateUser ` 's "Public Key mismatch" drop), signer
3636 provenance, and identity content all originate here. The lookup helpers that other
@@ -52,14 +52,14 @@ Sources of truth: `src/mesh/NodeDB.{h,cpp}`, `src/mesh/WarmNodeStore.h`,
5252 else rebuilds from traffic in seconds.
5353- ** Entry:** exactly 40 bytes - ` num(4) | last_heard(4) | public_key(32) ` . The low 7 bits
5454 of ` last_heard ` are omitted, and replaced with metadata (role: 4 bits, protected
55- category: 2, signer bit: 1), leaving ~ 128 s recency resolution - plenty for LRU ranking.
55+ category: 2, XEdDSA-signed bit: 1), leaving ~ 128 s recency resolution - plenty for LRU ranking.
5656- ** Capacity:** ` WARM_NODE_COUNT ` (100 on constrained parts; platform-tiered).
5757- ** Eviction:** LRU by ` last_heard ` , with keyed entries outranking keyless; keyless
5858 candidates never displace keyed entries.
5959- ** Persistence:** nRF52840 uses a 12 KB raw-flash record-ring below LittleFS
6060 (append/replay/compact); everywhere else ` /prefs/warm.dat ` (LittleFS).
6161- ** Membership invariant:** a node lives in the hot ** XOR** warm tier. ` take() ` removes
62- the warm record when the node is re-admitted hot, restoring role/protected/signer bits.
62+ the warm record when the node is re-admitted hot, restoring role/protected/XEdDSA-signed bits.
6363
6464## 3. TMM unified cache (base, traffic state)
6565
@@ -212,24 +212,26 @@ behaviour - is documented with the module in
212212Side-by-side view of what each store actually holds ("-" = not held). Details and
213213rationale live in the per-store sections above.
214214
215- | Property | 1. Hot store (` NodeInfoLite ` ) | 2. Warm tier (` WarmNodeEntry ` ) | 3. NodeInfo cache (` NodeInfoPayloadEntry ` ) | 4. Unified cache (` UnifiedCacheEntry ` ) |
216- | ---------------------------------- | --------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------- |
217- | Node number | yes | yes | yes (0 = free slot) | yes (0 = free slot) |
218- | Names + user id | yes (flattened fields) | - | yes (full ` User ` , when ` hasFullUser ` ) | - |
219- | Public key (32 B) | yes (authoritative) | yes (keyed entries) | yes (TOFU or proven; pinned against tiers 1-2) | - |
220- | Key provenance - XEdDSA signed | ` HAS_XEDDSA_SIGNED ` bitfield bit | 1 XEdDSA-signed bit (shared with ` last_heard ` ) | ` keyXeddsaSigned ` | - |
221- | Key provenance - manually verified | ` IS_KEY_MANUALLY_VERIFIED ` bitfield bit | - (not carried; collapsed into protected category) | ` keyManuallyVerified ` | - |
222- | Device role | ` role ` field | 4-bit role (metadata steal) | inside the cached ` User ` | 4-bit role in count-byte top bits (final fallback) |
223- | Recency | ` last_heard ` (unix secs) | ` last_heard ` (unix secs, 128 s quantised) | ` obsTick ` (3 min modular tick) + ` hasObserved ` | pos/rate/unknown modular ticks |
224- | Position / telemetry | via satellite copy-out accessors | - | - | 8-bit position _ fingerprint_ only (dedup) |
225- | Protected / favorite | bitfield flags | 2-bit protected category | - (` isMember ` keep-alive instead) | - |
226- | Routing hint (` next_hop ` ) | yes (persisted field) | - | - | ACK-confirmed relay byte (preloaded from tier 1) |
227- | Direct-reply metadata | - | - | ` sourceChannel ` , ` decodedBitfield ` (+ ` hasDecodedBitfield ` ) | - |
228- | Traffic-shaping counters | - | - | - | rate + unknown counts, pos fingerprint |
229- | Entry size | largest (full struct) | 40 B exact | ~ ` sizeof(User) ` +8, platform-padded (no size assert by design) | 10 B exact |
230- | Capacity | ` MAX_NUM_NODES ` (250/120/10) | ` WARM_NODE_COUNT ` (~ 100) | ` kNodeInfoCacheEntries ` (2000) | ` TRAFFIC_MANAGEMENT_CACHE_SIZE ` (2048/500/400/250/0) |
231- | Persistence (durable) | LittleFS (node DB file) | raw flash ring (nRF52840) or LittleFS (` /prefs/warm.dat ` ) | none (rebuilt from seed + traffic) | none |
232- | Storage (runtime) | heap | heap (PSRAM on ESP32 when available) | PSRAM on hardware; heap in native tests | PSRAM when available, else heap |
215+ | Property | 1. Hot store | 2. Warm tier | 3. NodeInfo cache | 4. Unified cache |
216+ | -------------------------- | ------------------------------ | ------------------------------ | ---------------------------------- | ------------------------------- |
217+ | Struct | ` NodeInfoLite ` | ` WarmNodeEntry ` | ` NodeInfoPayloadEntry ` | ` UnifiedCacheEntry ` |
218+ | Node number | yes | yes | yes (0 = free) | yes (0 = free) |
219+ | Names + user id | yes (flattened) | - | yes (full ` User ` ) | - |
220+ | Public key (32 B) | yes (authoritative) | yes (keyed entries) | yes (TOFU/proven; pinned) | - |
221+ | Key source - XEdDSA signed | ` HAS_XEDDSA_SIGNED ` bit | 1 bit (in ` last_heard ` ) | ` keyXeddsaSigned ` | - |
222+ | Key source - manual scan | ` IS_KEY_MANUALLY_VERIFIED ` bit | - (not carried) | ` keyManuallyVerified ` | - |
223+ | Device role | ` role ` field | 4-bit role (metadata steal) | in cached ` User ` | 4-bit role (final fallback) |
224+ | Recency | ` last_heard ` (unix s) | ` last_heard ` (128 s quant.) | ` obsTick ` (3 min) + ` hasObserved ` | modular ticks |
225+ | Position / telemetry | satellite accessors | - | - | 8-bit pos fingerprint (dedup) |
226+ | Protected / favorite | bitfield flags | 2-bit protected category | - (` isMember ` instead) | - |
227+ | Routing hint (` next_hop ` ) | yes (persisted) | - | - | ACK-confirmed relay byte |
228+ | Direct-reply metadata | - | - | ` sourceChannel ` , ` decodedBitfield ` | - |
229+ | Traffic-shaping counters | - | - | - | rate + unknown counts, pos fp |
230+ | Entry size | largest (full struct) | 40 B exact | ~ ` sizeof(User) ` +8 (padded) | 10 B exact |
231+ | Capacity (symbol) | ` MAX_NUM_NODES ` | ` WARM_NODE_COUNT ` | ` kNodeInfoCacheEntries ` | ` TRAFFIC_MANAGEMENT_CACHE_SIZE ` |
232+ | Capacity (entries) | 250/120/10 | ~ 100 | 2000 | 2048/500/400/250/0 |
233+ | Persistence (durable) | LittleFS (node DB) | flash ring (nRF52840)/LittleFS | none (rebuilt) | none |
234+ | Storage (runtime) | heap | heap / PSRAM (ESP32) | PSRAM (hw) / heap (test) | PSRAM / heap |
233235
234236## How a lookup falls through the tiers
235237
@@ -240,7 +242,7 @@ identity/role/key consumer
240242 1. hot store (NodeInfoLite) full identity, authoritative
241243 │ miss
242244 ▼
243- 2. warm tier (WarmNodeStore) key + role/protected/signer bits, persisted
245+ 2. warm tier (WarmNodeStore) key + role/protected/XEdDSA-signed bits, persisted
244246 │ miss
245247 ▼
246248 3. TMM NodeInfo cache (extended) full User payloads + TOFU/proven keys, ephemeral
0 commit comments