mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-10-15 11:39:59 +00:00
build(mqtt): make the preset table byte-identical across channels
The preset table is fleet state, not a build detail. A slot's preset is stored in /mqtt.json by name, and firmware that does not know a name does not merely ignore it: MQTTPrefsSerializer repairs it to "none" and CommonCLI writes the repaired file back to flash. A node that rolls back to a channel missing a preset therefore loses that slot permanently, and re-upgrading does not bring it back. The parity gate compared preset names only, deliberately allowing URL, CA and credential drift. It now requires src/helpers/MQTTPresets.h to be byte-identical between the channels, which also catches that drift — the two channels are meant to dial the same brokers. That is only workable if the file holds no channel-specific code, so mqttPresetEnforcesTokenExp() moves to the new MQTTPresetPolicy.h. It was the sole difference between the two channels' copies, and with it moved they match exactly today. Policy keyed off the table belongs there from now on; the table itself stays pure data. The older name-only comparison stays available without --exact for ad-hoc use, and the checker's self-test now covers both modes, including that --exact rejects a config-only change the name check waves through.
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "MQTTPresets.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// Policy that keys off a preset rather than describing one.
|
||||
//
|
||||
// MQTTPresets.h is data: it is the broker table, and it is kept byte-identical
|
||||
// between the observer-firmware and observer-firmware-dev channels so a node
|
||||
// that rolls back cannot meet a preset name its firmware does not know. An
|
||||
// unknown name is not merely ignored — MQTTPrefsSerializer repairs it to "none"
|
||||
// and the repaired /mqtt.json is written back, so the operator's slot is gone
|
||||
// for good. Channel-specific behaviour therefore lives here instead, where the
|
||||
// two channels are free to differ.
|
||||
|
||||
// True when the broker tears down a live session once its JWT passes exp, so the
|
||||
// renewal must proactively bounce the connection to present a fresh token.
|
||||
//
|
||||
// Default true, because getting this wrong the safe way costs a re-handshake and
|
||||
// getting it wrong the unsafe way costs an outage. waev is the exception: its
|
||||
// operator confirmed (2026-08-11) that their servers do not disconnect on expiry,
|
||||
// so a live session there needs only its credentials refreshed for the next
|
||||
// reconnect. waev is also the only preset with a short token_lifetime, so it was
|
||||
// the only one bouncing often — every ~47 min, and each bounce's re-handshake can
|
||||
// cost ~10 KB of contiguous internal DRAM on a non-PSRAM board.
|
||||
//
|
||||
// Keyed by name rather than a struct field on purpose: adding a field would mean
|
||||
// re-ordering a dozen positional initialisers in the table, where a mistake is
|
||||
// silent.
|
||||
static inline bool mqttPresetEnforcesTokenExp(const MQTTPresetDef* preset) {
|
||||
if (!preset || !preset->name) return true; // custom/audience slots: assume enforced
|
||||
return strcmp(preset->name, "waev") != 0;
|
||||
}
|
||||
@@ -47,24 +47,6 @@ struct MQTTPresetDef {
|
||||
// Braces match topic placeholders ({device}/{iata}); never send this string to the broker.
|
||||
static const char MQTT_USERPASS_USERNAME_PUBKEY[] = "{pubkey}";
|
||||
|
||||
// True when the broker tears down a live session once its JWT passes exp, so the
|
||||
// renewal must proactively bounce the connection to present a fresh token.
|
||||
//
|
||||
// Default true, because getting this wrong the safe way costs a re-handshake and
|
||||
// getting it wrong the unsafe way costs an outage. waev is the exception: its
|
||||
// operator confirmed (2026-08-11) that their servers do not disconnect on expiry,
|
||||
// so a live session there needs only its credentials refreshed for the next
|
||||
// reconnect. waev is also the only preset with a short token_lifetime, so it was
|
||||
// the only one bouncing often — every ~47 min, and each bounce's re-handshake can
|
||||
// cost ~10 KB of contiguous internal DRAM on a non-PSRAM board.
|
||||
//
|
||||
// Keyed by name rather than a struct field on purpose: adding a field would mean
|
||||
// re-ordering a dozen positional initialisers below, where a mistake is silent.
|
||||
static inline bool mqttPresetEnforcesTokenExp(const MQTTPresetDef* preset) {
|
||||
if (!preset || !preset->name) return true; // custom/audience slots: assume enforced
|
||||
return strcmp(preset->name, "waev") != 0;
|
||||
}
|
||||
|
||||
static inline bool mqttPresetUsesDevicePubkeyUsername(const MQTTPresetDef* preset) {
|
||||
return preset && preset->auth_type == MQTT_AUTH_USERPASS &&
|
||||
preset->userpass_username &&
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "helpers/JWTHelper.h"
|
||||
#include "helpers/MQTTPacketFilter.h"
|
||||
#include "helpers/MQTTPresets.h"
|
||||
#include "helpers/MQTTPresetPolicy.h"
|
||||
#include "helpers/MQTTLifecycle.h"
|
||||
#include "helpers/AlertFaultPolicy.h"
|
||||
#include "helpers/MQTTEffectiveConfig.h"
|
||||
|
||||
Reference in New Issue
Block a user