fix(sync): prevent master state from clearing when a peer pairs
CI / Build & Test (push) Successful in 4m10s

When a device pairs with the master, the master's autopass can emit an update
and applyRemoteUpdate would sometimes apply an empty or weaker snapshot from the
merged view, overwriting the master's state. Skip applying remote state when
we're the master and the incoming snapshot has less content (servers + vhosts +
serviceTunnels) than current state.
This commit is contained in:
Raven Scott
2026-03-15 06:35:04 -04:00
parent ad9c22f1b6
commit 68b830393f
+15 -1
View File
@@ -204,6 +204,13 @@ function onRemoteUpdate() {
}, REMOTE_UPDATE_DEBOUNCE_MS); }, REMOTE_UPDATE_DEBOUNCE_MS);
} }
function snapshotContentSize(snapshot) {
if (!snapshot || typeof snapshot !== 'object') return 0;
return (Array.isArray(snapshot.servers) ? snapshot.servers.length : 0) +
(Array.isArray(snapshot.virtualHosts) ? snapshot.virtualHosts.length : 0) +
(Array.isArray(snapshot.serviceTunnels) ? snapshot.serviceTunnels.length : 0);
}
async function applyRemoteUpdate() { async function applyRemoteUpdate() {
if (weJustPushed || !pass || !holesailManager) return; if (weJustPushed || !pass || !holesailManager) return;
try { try {
@@ -219,13 +226,20 @@ async function applyRemoteUpdate() {
return; return;
} }
const statePath = getStateFilePath(); const statePath = getStateFilePath();
let current = null;
if (statePath && fs.existsSync(statePath)) { if (statePath && fs.existsSync(statePath)) {
try { try {
const currentRaw = fs.readFileSync(statePath, 'utf8'); const currentRaw = fs.readFileSync(statePath, 'utf8');
const current = JSON.parse(currentRaw); current = JSON.parse(currentRaw);
if (stateFingerprint(current) === stateFingerprint(snapshot)) return; if (stateFingerprint(current) === stateFingerprint(snapshot)) return;
} catch (_) {} } catch (_) {}
} }
const identity = loadIdentity();
if (identity && identity.isMaster) {
const currentSize = current ? snapshotContentSize(current) : snapshotContentSize(holesailManager.getStateSnapshot());
const snapshotSize = snapshotContentSize(snapshot);
if (snapshotSize < currentSize) return;
}
await applySyncedState(snapshot); await applySyncedState(snapshot);
lastSyncedAt = Date.now(); lastSyncedAt = Date.now();
if (emitEvent) emitEvent('syncApplied', {}); if (emitEvent) emitEvent('syncApplied', {});