[object Object]

← 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

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 →