Skip to content

Commit be64d79

Browse files
feat(live): hide EVENTS/SYS log categories from public /live (node-only)
Per request, the public /live page now shows per-NODE activity only — operator EVENTS (start/stop/save/...) and in-run SYS diagnostics (baseline/balance/ internet/...) are hidden from spectators. Server-side, authoritative: - publicLogBuffer() = logBuffer filtered to cat==='node'. - public SSE live 'log' forwarder drops cat events/sys before send. - public SSE init logs + /api/public/logs use publicLogBuffer(). The admin SSE init still sends the full logBuffer, so the admin dashboard keeps all categories. Reviewed: live-drop (by data.cat) and replay-filter (re-classify the buffer string) are provably consistent — same string, same classifyLogCategory — so no line leaks through one path but not the other; broadcastLive=off still yields []. Test suite green (188/31/45/35).
1 parent 4a642c3 commit be64d79

1 file changed

Lines changed: 11 additions & 2 deletions

File tree

server.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,13 @@ function classifyLogCategory(msg) {
305305
return 'node';
306306
}
307307

308+
// Public /live shows per-NODE activity only — operator EVENTS and in-run SYS
309+
// diagnostics are hidden from spectators (the admin dashboard still shows all).
310+
// Filters the rolling string buffer for the public log endpoints.
311+
function publicLogBuffer() {
312+
return logBuffer.filter(m => classifyLogCategory(m) === 'node');
313+
}
314+
308315
// EVENTS persist to a file (separate from per-run runs/test-NNN/audit.log), with
309316
// a simple 1-file rotation at ~2MB so it can't grow unbounded.
310317
const EVENTS_LOG_FILE = path.join(__dirname, 'results', 'events.log');
@@ -1904,7 +1911,7 @@ app.get('/api/public/events', attachAdminFlag, rlPublicSse, (req, res) => {
19041911
batchMode: activeBatchMode,
19051912
// Persisted log backlog so /live shows full history on refresh, not a blank panel.
19061913
// logBuffer is populated from results/audit-*.log on server boot and updated live.
1907-
logs: liveOn ? logBuffer.slice() : [],
1914+
logs: liveOn ? publicLogBuffer() : [],
19081915
state: initState,
19091916
results: initResults,
19101917
// Report effective-live so the admin's own /live page flips into live mode
@@ -1918,6 +1925,8 @@ app.get('/api/public/events', attachAdminFlag, rlPublicSse, (req, res) => {
19181925
// visitors only get events when broadcastLive is on.
19191926
if (!state.broadcastLive && !isAdminViewer) return;
19201927
if (!PUBLIC_EVENT_WHITELIST.has(data.type)) return;
1928+
// /live is per-node activity only — drop operator EVENTS + in-run SYS lines.
1929+
if (data.type === 'log' && (data.cat === 'events' || data.cat === 'sys')) return;
19211930
send(sanitizeForPublic(data));
19221931
};
19231932
emitter.on('update', handler);
@@ -1941,7 +1950,7 @@ app.get('/api/public/logs', attachAdminFlag, rlPublicRead, (req, res) => {
19411950
const showLive = state.broadcastLive || req.admin === true;
19421951
if (!showLive) return res.json({ logs: [], broadcastLive: false });
19431952
// Report effective-live so admin viewers get the live-mode UI on /live.
1944-
res.json({ logs: logBuffer.slice(), broadcastLive: true });
1953+
res.json({ logs: publicLogBuffer(), broadcastLive: true });
19451954
});
19461955

19471956
/**

0 commit comments

Comments
 (0)