From e58e5503e80c27f0ba94cd790c6cf2a544bb8582 Mon Sep 17 00:00:00 2001 From: legop3 Date: Sun, 10 May 2026 01:55:32 -0400 Subject: [PATCH] lockdown audit done --- server/src/services/liftService/index.js | 8 +++++++ .../services/llmCommentaryService/index.js | 22 +++++++++++++++++-- .../services/llmCommentaryService/runner.js | 16 ++++++++++++++ server/src/services/neatoService/index.js | 13 +++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/server/src/services/liftService/index.js b/server/src/services/liftService/index.js index 2d5671e5..887ee81e 100644 --- a/server/src/services/liftService/index.js +++ b/server/src/services/liftService/index.js @@ -6,6 +6,8 @@ const io = require('../../globals/io'); const logger = require('../../globals/logger').child('liftService'); const { loadConfig } = require('../../helpers/configLoader'); const { isVerified } = require('../verificationService'); +const { getMode, MODES } = require('../modeManager'); +const { isLockdownAdmin } = require('../roleService'); const { homeAssistantEvents, getRawEntitySnapshot, @@ -178,6 +180,9 @@ homeAssistantEvents.on('status', emitUpdate); io.on('connection', (socket) => { socket.on('lift:up', async (_, cb = () => {}) => { try { + if (getMode() === MODES.LOCKDOWN && !isLockdownAdmin(socket)) { + throw new Error('Server in lockdown'); + } if (!isVerified(socket)) throw new Error('VIP verification required'); const resp = await moveUp(socket.id || 'socket'); cb({ success: true, ...resp }); @@ -188,6 +193,9 @@ io.on('connection', (socket) => { socket.on('lift:down', async (_, cb = () => {}) => { try { + if (getMode() === MODES.LOCKDOWN && !isLockdownAdmin(socket)) { + throw new Error('Server in lockdown'); + } if (!isVerified(socket)) throw new Error('VIP verification required'); const resp = await moveDown(socket.id || 'socket'); cb({ success: true, ...resp }); diff --git a/server/src/services/llmCommentaryService/index.js b/server/src/services/llmCommentaryService/index.js index 168c6f28..84bd4b97 100644 --- a/server/src/services/llmCommentaryService/index.js +++ b/server/src/services/llmCommentaryService/index.js @@ -7,6 +7,7 @@ const io = require('../../globals/io'); const logger = require('../../globals/logger').child('llmCommentary'); const { loadConfig } = require('../../helpers/configLoader'); const { getRole, roleEvents } = require('../roleService'); +const { getMode, MODES, modeEvents } = require('../modeManager'); const roverManager = require('../roverManager'); const { getActiveDrivers } = require('../turnService'); const { getNickname } = require('../nicknameService'); @@ -258,7 +259,9 @@ const runner = createRunner({ updateStatus, }); -if (enabled && model && ollamaUrl) { +const canRunFromConfig = enabled && model && ollamaUrl; + +if (canRunFromConfig) { registerHooks({ io, roleEvents, @@ -272,7 +275,22 @@ if (enabled && model && ollamaUrl) { onRoverRemoved: snapshotEngine.removeRover, }); - runner.start(); + const mode = getMode(); + if (mode === MODES.LOCKDOWN) { + runner.stop('paused during lockdown'); + logger.info('LLM commentary paused due to lockdown mode'); + } else { + runner.start(); + } + + modeEvents.on('change', (nextMode) => { + if (nextMode === MODES.LOCKDOWN) { + runner.stop('paused during lockdown'); + logger.info('LLM commentary paused due to lockdown mode'); + return; + } + runner.start(); + }); } else { const disabledReason = !enabled ? 'llmCommentary.enabled is false' diff --git a/server/src/services/llmCommentaryService/runner.js b/server/src/services/llmCommentaryService/runner.js index 9c8aee5b..c167580d 100644 --- a/server/src/services/llmCommentaryService/runner.js +++ b/server/src/services/llmCommentaryService/runner.js @@ -47,6 +47,21 @@ function createRunner(deps) { scheduleNextTick(runTick, 0); } + function stop(reason = 'stopped') { + if (runtime.timer) { + clearTimeout(runtime.timer); + runtime.timer = null; + } + updatePhase('paused', { + running: false, + inFlight: false, + currentRunId: null, + nextRunAt: null, + lastOutcome: 'paused', + lastReason: reason, + }); + } + function clearRuntimeHistory() { runtime.contextResetAt = Date.now(); runtime.clearCount += 1; @@ -331,6 +346,7 @@ function createRunner(deps) { return { start, + stop, runTick, clearRuntimeHistory, wakeForDriverActivity: () => wakeForDriverActivity(runTick), diff --git a/server/src/services/neatoService/index.js b/server/src/services/neatoService/index.js index ff2b9f80..fd96212c 100644 --- a/server/src/services/neatoService/index.js +++ b/server/src/services/neatoService/index.js @@ -6,6 +6,8 @@ const io = require('../../globals/io'); const logger = require('../../globals/logger').child('neatoService'); const { loadConfig } = require('../../helpers/configLoader'); const { isVerified } = require('../verificationService'); +const { getMode, MODES } = require('../modeManager'); +const { isLockdownAdmin } = require('../roleService'); const { createLidarRuntime } = require('./lidarRuntime'); const { homeAssistantEvents, @@ -315,8 +317,15 @@ if (lidarRuntime) { } io.on('connection', (socket) => { + function assertLockdownAccess() { + if (getMode() === MODES.LOCKDOWN && !isLockdownAdmin(socket)) { + throw new Error('Server in lockdown'); + } + } + socket.on('neato:start', async (_, cb = () => {}) => { try { + assertLockdownAccess(); if (!isVerified(socket)) { throw new Error('VIP verification required'); } @@ -329,6 +338,7 @@ io.on('connection', (socket) => { socket.on('neato:sendHome', async (_, cb = () => {}) => { try { + assertLockdownAccess(); if (!isVerified(socket)) { throw new Error('VIP verification required'); } @@ -341,6 +351,7 @@ io.on('connection', (socket) => { socket.on('neato:locate', async (_, cb = () => {}) => { try { + assertLockdownAccess(); if (!isVerified(socket)) { throw new Error('VIP verification required'); } @@ -353,6 +364,7 @@ io.on('connection', (socket) => { socket.on('neato:clearErrors', async (_, cb = () => {}) => { try { + assertLockdownAccess(); if (!isVerified(socket)) { throw new Error('VIP verification required'); } @@ -365,6 +377,7 @@ io.on('connection', (socket) => { socket.on('neato:powerCycle', async (_, cb = () => {}) => { try { + assertLockdownAccess(); if (!isVerified(socket)) { throw new Error('VIP verification required'); }