← back to Domain Sniper
dashboard: per-log tracker + idle counter + /api/perlog endpoint
7992d0c447fc4a9061e94ab7549e72de064311ec · 2026-05-12 18:03:59 -0700 · steve
Mirrors brand-typo-watcher's perLog map (fetched/x509/precert/emitted/
idle/lastSeenMs). Broadcast stats payload now includes perLog with a
computed 'stale' boolean (no event in 60s). Also adds GET /api/perlog
for headless inspection. Once public/index.html adds a renderer, browser
clients get the same alive-busy / alive-quiet / wedged diagnostic the
shell watcher has.
Also logged 4.44h slice 2-3 RECHECK#2 datapoint: 0/10 (no late snipes
since 3.57h). v1 cumulative now 0/130.
Files touched
Diff
commit 7992d0c447fc4a9061e94ab7549e72de064311ec
Author: steve <steve@designerwallcoverings.com>
Date: Tue May 12 18:03:59 2026 -0700
dashboard: per-log tracker + idle counter + /api/perlog endpoint
Mirrors brand-typo-watcher's perLog map (fetched/x509/precert/emitted/
idle/lastSeenMs). Broadcast stats payload now includes perLog with a
computed 'stale' boolean (no event in 60s). Also adds GET /api/perlog
for headless inspection. Once public/index.html adds a renderer, browser
clients get the same alive-busy / alive-quiet / wedged diagnostic the
shell watcher has.
Also logged 4.44h slice 2-3 RECHECK#2 datapoint: 0/10 (no late snipes
since 3.57h). v1 cumulative now 0/130.
---
dashboard.js | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/dashboard.js b/dashboard.js
index 16d672b..e25d163 100644
--- a/dashboard.js
+++ b/dashboard.js
@@ -57,12 +57,19 @@ function classify(d) {
const app = express();
app.use(express.static(path.join(__dirname, 'public')));
app.get('/api/keywords', (_req, res) => res.json({ keywords: BRAND_KEYWORDS }));
+app.get('/api/perlog', (_req, res) => {
+ const now = Date.now();
+ const out = {};
+ for (const [k, v] of Object.entries(perLog)) out[k] = { ...v, stale: v.lastSeenMs ? (now - v.lastSeenMs > 60_000) : false };
+ res.json({ stats, perLog: out });
+});
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });
const clients = new Set();
let stats = { seen: 0, hot: 0, brand: 0, startedAt: Date.now() };
+const perLog = {}; // log_name -> { fetched, x509, precert, emitted, idle, lastSeenMs }
wss.on('connection', (ws) => {
clients.add(ws);
@@ -131,6 +138,14 @@ function connectCtLog() {
console.log(`[${new Date().toISOString()}] ctlog seeded ${st.log} at tree_size=${st.position}`);
} else if (st.type === 'resumed') {
console.log(`[${new Date().toISOString()}] ctlog resumed ${st.log} at cursor=${st.position}`);
+ } else if (st.type === 'tick' && st.log) {
+ const p = (perLog[st.log] = perLog[st.log] || { fetched: 0, x509: 0, precert: 0, emitted: 0, idle: 0, lastSeenMs: 0 });
+ if (st.stats) { p.fetched = st.stats.fetched; p.x509 = st.stats.x509; p.precert = st.stats.precert; p.emitted = st.stats.emitted; }
+ p.lastSeenMs = Date.now();
+ } else if (st.type === 'idle' && st.log) {
+ const p = (perLog[st.log] = perLog[st.log] || { fetched: 0, x509: 0, precert: 0, emitted: 0, idle: 0, lastSeenMs: 0 });
+ p.idle++;
+ p.lastSeenMs = Date.now();
}
},
});
@@ -141,7 +156,14 @@ function connectCT() {
return connectCtLog();
}
-setInterval(() => broadcast({ type: 'stats', ...stats }), 2000);
+setInterval(() => {
+ const now = Date.now();
+ const perLogPayload = {};
+ for (const [k, v] of Object.entries(perLog)) {
+ perLogPayload[k] = { ...v, stale: v.lastSeenMs ? (now - v.lastSeenMs > 60_000) : false };
+ }
+ broadcast({ type: 'stats', ...stats, perLog: perLogPayload });
+}, 2000);
server.listen(PORT, '127.0.0.1', () => {
console.log(`\n domain-sniper :: live dashboard`);
← fa1502d brand-typo-watcher: idle counter + stale-log marker in per-l
·
back to Domain Sniper
·
dashboard ui: per-log source band with health dots 24e477f →