← back to Dw Yolo Loop
tls-expiry canary: read-only fleet cert-expiry check (DTD 3/3; PASS, all certs >=41d; grasscloth.com parked off-fleet)
587c4e4a8432ce1332c09d0a5694e27c5ad92465 · 2026-06-16 07:18:50 -0700 · Steve Abrams
Files touched
A scripts/tls-expiry/tls-expiry-canary.mjs
Diff
commit 587c4e4a8432ce1332c09d0a5694e27c5ad92465
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jun 16 07:18:50 2026 -0700
tls-expiry canary: read-only fleet cert-expiry check (DTD 3/3; PASS, all certs >=41d; grasscloth.com parked off-fleet)
---
scripts/tls-expiry/tls-expiry-canary.mjs | 77 ++++++++++++++++++++++++++++++++
1 file changed, 77 insertions(+)
diff --git a/scripts/tls-expiry/tls-expiry-canary.mjs b/scripts/tls-expiry/tls-expiry-canary.mjs
new file mode 100644
index 0000000..d391ee0
--- /dev/null
+++ b/scripts/tls-expiry/tls-expiry-canary.mjs
@@ -0,0 +1,77 @@
+// tls-expiry-canary — read-only TLS handshake across the core DW web fleet,
+// report days-to-expiry per host, flag any cert expired or expiring soon.
+// An expired cert is a hard, total, customer-facing outage (browser interstitial
+// → zero conversions) that arrives on a KNOWN clock — the one failure mode where
+// early detection has asymmetric payoff. dw-uptime-probe checks reachability but
+// a soon-to-expire cert passes uptime right up until the cliff; nothing else in
+// the canary fleet checks expiry. This fills that gap.
+//
+// node tls-expiry-canary.mjs [--warn 21] [--fail 7] [--hosts a.com,b.com]
+// READ-ONLY (TLS handshake only, no HTTP body, no writes). $0.
+import tls from 'node:tls';
+import fs from 'node:fs';
+
+const args = process.argv.slice(2);
+const WARN = parseInt(args.find((_,i,a)=>a[i-1]==='--warn') || '21', 10) || 21; // days
+const FAIL = parseInt(args.find((_,i,a)=>a[i-1]==='--fail') || '7', 10) || 7; // days
+const HOSTS = (args.find((_,i,a)=>a[i-1]==='--hosts') || [
+ 'www.designerwallcoverings.com','designerwallcoverings.com',
+ 'apartmentwallpaper.com','philipperomano.com','novasuede.com','wallco.ai',
+ 'architecturalwallcoverings.com','thesetdecorator.com',
+ 'corkwallcovering.com','silkwallpaper.com','linenwallpaper.com','grasscloth.com',
+ 'raffiawallpaper.com','glitterwallpaper.com','hospitalitywallcoverings.com',
+].join(',')).split(',').map(h=>h.trim()).filter(Boolean);
+const OUT = `${process.env.HOME}/.claude/yolo-queue/tls-expiry-2026-06-16.json`;
+const MD = `${process.env.HOME}/.claude/yolo-queue/tls-expiry-2026-06-16.md`;
+
+function checkHost(host) {
+ return new Promise((resolve) => {
+ const socket = tls.connect({ host, port: 443, servername: host, timeout: 12000, rejectUnauthorized: false }, () => {
+ const cert = socket.getPeerCertificate();
+ if (!cert || !cert.valid_to) { socket.end(); return resolve({ host, state: 'UNKNOWN', detail: 'no cert' }); }
+ const validTo = new Date(cert.valid_to).getTime();
+ const days = Math.floor((validTo - Date.now()) / 86400000);
+ const authorized = socket.authorized;
+ socket.end();
+ resolve({ host, state: 'OK', days_to_expiry: days, valid_to: cert.valid_to,
+ issuer: (cert.issuer && (cert.issuer.O || cert.issuer.CN)) || '?', chain_authorized: authorized });
+ });
+ socket.on('error', (e) => resolve({ host, state: 'UNKNOWN', detail: String(e.message).slice(0,50) }));
+ socket.on('timeout', () => { socket.destroy(); resolve({ host, state: 'UNKNOWN', detail: 'timeout' }); });
+ });
+}
+
+(async () => {
+ const results = [];
+ for (const h of HOSTS) results.push(await checkHost(h));
+ const decided = results.filter(r => r.state === 'OK');
+ const expired = decided.filter(r => r.days_to_expiry < 0);
+ const critical = decided.filter(r => r.days_to_expiry >= 0 && r.days_to_expiry < FAIL);
+ const warning = decided.filter(r => r.days_to_expiry >= FAIL && r.days_to_expiry < WARN);
+ const unreachable = results.filter(r => r.state === 'UNKNOWN');
+ const verdict = (expired.length || critical.length) ? 'FAIL' : warning.length ? 'WARN' : 'PASS';
+
+ const report = { generated_at: new Date().toISOString(), checked: results.length, warn_at_days: WARN, fail_at_days: FAIL,
+ verdict, expired: expired.map(r=>({host:r.host,days:r.days_to_expiry})), critical: critical.map(r=>({host:r.host,days:r.days_to_expiry})),
+ warning: warning.map(r=>({host:r.host,days:r.days_to_expiry})), unreachable: unreachable.map(r=>({host:r.host,detail:r.detail})),
+ all: results.map(r=>({host:r.host, state:r.state, days:r.days_to_expiry ?? null, valid_to:r.valid_to ?? null, issuer:r.issuer ?? null, chain_ok:r.chain_authorized ?? null, detail:r.detail ?? null })) };
+ fs.writeFileSync(OUT, JSON.stringify(report, null, 2));
+
+ const emoji = verdict==='FAIL'?'🔴':verdict==='WARN'?'🟠':'🟢';
+ let md = `# TLS cert-expiry canary — ${new Date().toISOString().slice(0,16)}\n\n`;
+ md += `Checked **${results.length}** fleet hosts. **READ-ONLY (TLS handshake only), $0.** Thresholds: WARN <${WARN}d · FAIL <${FAIL}d or expired.\n\n`;
+ md += `## ${emoji} ${verdict}\n`;
+ if (expired.length) md += `🔴 **EXPIRED:** ${expired.map(r=>`${r.host} (${r.days_to_expiry}d)`).join(', ')}\n`;
+ if (critical.length) md += `🔴 **<${FAIL}d:** ${critical.map(r=>`${r.host} (${r.days_to_expiry}d)`).join(', ')}\n`;
+ if (warning.length) md += `🟠 **<${WARN}d:** ${warning.map(r=>`${r.host} (${r.days_to_expiry}d)`).join(', ')}\n`;
+ if (verdict==='PASS') md += `All certs valid ≥${WARN} days. 🟢\n`;
+ md += `\n| Host | State | Days→expiry | Issuer | Chain | Valid-to |\n|---|---|---:|---|---|---|\n`;
+ for (const r of report.all) md += `| ${r.host} | ${r.state} | ${r.days ?? '—'} | ${r.issuer ?? '—'} | ${r.chain_ok===null?'—':(r.chain_ok?'ok':'⚠')} | ${r.valid_to ?? r.detail ?? '—'} |\n`;
+ md += `\n_UNKNOWN = DNS/connect/timeout (host may be parked, behind a different edge, or not on this list's apex). chain ⚠ = self-signed/untrusted chain even if not expired._\n`;
+ fs.writeFileSync(MD, md);
+
+ console.log(`[tls-expiry] ${emoji} ${verdict} · checked=${results.length} · expired=${expired.length} crit=${critical.length} warn=${warning.length} unreachable=${unreachable.length}`);
+ for (const r of [...expired,...critical,...warning]) console.log(` ⚠️ ${r.host}: ${r.days_to_expiry}d`);
+ console.log(`Report: ${MD}`);
+ process.exit(verdict==='FAIL'?2:0);
+})().catch(e => { console.error('FATAL', e.message); process.exit(1); });
← 781635c handle-freshness canary: storefront 404 check on mirror-ACTI
·
back to Dw Yolo Loop
·
harden 3 read-only canaries (DTD-A 3/3): dynamic-dated repor 3343eea →