← back to Dw Signup Fulfillment
add Option C go-live readiness preflight (PASS/WARN/FAIL, no-send George auth probe)
927545c05a1ab04ca4dd875545ef6a0d250a1829 · 2026-08-14 13:40:44 -0700 · steve
Files touched
M .gitignoreA scripts/golive-preflight.js
Diff
commit 927545c05a1ab04ca4dd875545ef6a0d250a1829
Author: steve <steve@designerwallcoverings.com>
Date: Fri Aug 14 13:40:44 2026 -0700
add Option C go-live readiness preflight (PASS/WARN/FAIL, no-send George auth probe)
---
.gitignore | 1 +
scripts/golive-preflight.js | 90 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 91 insertions(+)
diff --git a/.gitignore b/.gitignore
index e7e11d3..be78ded 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,4 @@ __pycache__/
data/minted-cards-honor-worklist.jsonl
data/honor-reissue-ledger*.jsonl
data/*.bak-*
+data/latest.json
diff --git a/scripts/golive-preflight.js b/scripts/golive-preflight.js
new file mode 100644
index 0000000..28e1fff
--- /dev/null
+++ b/scripts/golive-preflight.js
@@ -0,0 +1,90 @@
+#!/usr/bin/env node
+'use strict';
+// golive-preflight.js — read-only readiness check for the Option C go-live
+// (verify email → verified-sample tag → Regios). Verifies every dependency WITHOUT
+// sending an email or writing anything, so it's safe to run anytime. Emits PASS/WARN/FAIL
+// (the fleet-health-rollup vocabulary) and writes data/latest.json for the rollup panel.
+//
+// PASS = every hard dependency green, DRY_RUN already off → safe to serve live.
+// WARN = launch-blocking config still pending (e.g. DRY_RUN=1, no PUBLIC_URL, Regios manual).
+// FAIL = a hard dependency is broken (George auth down, missing secret/token).
+//
+// Run: node scripts/golive-preflight.js
+const fs = require('fs');
+const path = require('path');
+const http = require('http');
+const config = require('../lib/config');
+
+const checks = [];
+const add = (name, level, detail) => checks.push({ name, level, detail });
+
+// Small authenticated GET helper (used only for George's health probe — no email send).
+function getJSON(url, headers) {
+ return new Promise((resolve) => {
+ let u;
+ try { u = new URL(url); } catch (e) { return resolve({ status: 0, error: e.message }); }
+ const req = http.request({ hostname: u.hostname, port: u.port || 80, path: u.pathname, method: 'GET', headers }, (res) => {
+ let d = ''; res.on('data', (c) => (d += c));
+ res.on('end', () => resolve({ status: res.statusCode, body: d.slice(0, 200) }));
+ });
+ req.on('error', (e) => resolve({ status: 0, error: e.message }));
+ req.setTimeout(8000, () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
+ req.end();
+ });
+}
+
+async function main() {
+ // 1) George Basic-auth — the exact thing that caused the outage. Authenticated GET to
+ // George's health endpoint: 200 = auth good, 401 = the empty-password bug is back.
+ const auth = config.GEORGE_BASIC_AUTH || 'admin:';
+ const basic = auth.startsWith('Basic ') ? auth : 'Basic ' + Buffer.from(auth.includes(':') ? auth : 'admin:' + auth).toString('base64');
+ const gh = await getJSON((config.GEORGE_URL || 'http://127.0.0.1:9850') + '/api/health', { Authorization: basic });
+ if (gh.status === 200) add('george_basic_auth', 'PASS', 'George reachable + Basic-auth accepted');
+ else if (gh.status === 401) add('george_basic_auth', 'FAIL', 'George 401 — Basic-auth wrong (empty-password bug?). Set GEORGE_AUTH in secrets.');
+ else add('george_basic_auth', 'FAIL', `George unreachable/error (status ${gh.status}${gh.error ? ' ' + gh.error : ''})`);
+
+ // 2) External-send approval token (needed for external welcome emails).
+ add('george_send_token', config.GEORGE_EXTERNAL_SEND_TOKEN ? 'PASS' : 'FAIL',
+ config.GEORGE_EXTERNAL_SEND_TOKEN ? 'set (…' + String(config.GEORGE_EXTERNAL_SEND_TOKEN).slice(-4) + ')' : 'missing — external sends will be blocked');
+
+ // 3) Verify-token signing secret (a dev fallback exists ONLY in DRY_RUN; live needs a real one).
+ add('verify_secret', config.VERIFY_SECRET ? 'PASS' : (config.DRY_RUN ? 'WARN' : 'FAIL'),
+ config.VERIFY_SECRET ? 'DW_SIGNUP_VERIFY_SECRET set' : 'missing — set DW_SIGNUP_VERIFY_SECRET before live (dev fallback is DRY-only)');
+
+ // 4) Shopify token (needed to write the verified-sample tag).
+ add('shopify_token', config.SHOPIFY_FULFILLMENT_TOKEN ? 'PASS' : 'FAIL',
+ config.SHOPIFY_FULFILLMENT_TOKEN ? 'set' : 'missing — the tag write (read_customers+write_customers) will no-op');
+
+ // 5) Public URL (verify links + webhook must resolve off localhost).
+ const pu = config.PUBLIC_URL;
+ add('public_url', pu && /^https:\/\//.test(pu) && !/localhost|127\.0\.0\.1/.test(pu) ? 'PASS' : 'WARN',
+ pu ? `PUBLIC_URL=${pu}` : 'PUBLIC_URL unset — verify links + webhook won\'t resolve for customers');
+
+ // 6) Reward tag name (informational).
+ add('verified_tag', 'PASS', `VERIFIED_TAG=${config.VERIFIED_TAG} (must equal the Regios rule tag)`);
+
+ // 7) DRY_RUN state — must be OFF to actually serve live.
+ add('dry_run', config.DRY_RUN ? 'WARN' : 'PASS', config.DRY_RUN ? 'DRY_RUN=1 — service is inert; flip to 0 to go live' : 'DRY_RUN=0 (live)');
+
+ // 8) Webhook token (only if using the customers/create webhook entry).
+ add('webhook_url_token', config.WEBHOOK_URL_TOKEN ? 'PASS' : 'WARN',
+ config.WEBHOOK_URL_TOKEN ? 'set' : 'unset — only needed if using the webhook trigger (the /claim form works without it)');
+
+ // 9) Local service health.
+ const sh = await getJSON(`http://127.0.0.1:${config.PORT}/healthz`, {});
+ add('service_health', sh.status === 200 ? 'PASS' : 'WARN', sh.status === 200 ? `:${config.PORT}/healthz 200` : `:${config.PORT}/healthz status ${sh.status}`);
+
+ // 10) Regios rule — not API-verifiable; always a manual gate.
+ add('regios_rule', 'WARN', 'MANUAL: confirm a Regios rule makes samples free for tag ' + config.VERIFIED_TAG + ', scoped to the Sample VARIANT only (roll stays full price).');
+
+ const worst = checks.some((c) => c.level === 'FAIL') ? 'FAIL' : checks.some((c) => c.level === 'WARN') ? 'WARN' : 'PASS';
+ const out = { skill: 'dw-signup-golive-preflight', verdict: worst, status: worst, checked_at: new Date().toISOString(), checks };
+ try { fs.mkdirSync(path.join(__dirname, '..', 'data'), { recursive: true }); fs.writeFileSync(path.join(__dirname, '..', 'data', 'latest.json'), JSON.stringify(out, null, 2)); } catch (e) {}
+
+ console.log(`\nOption C go-live preflight — verdict: ${worst}\n`);
+ for (const c of checks) console.log(` ${c.level === 'PASS' ? '✅' : c.level === 'WARN' ? '🟠' : '❌'} ${c.name.padEnd(18)} ${c.detail}`);
+ console.log(`\n${worst === 'PASS' ? 'Ready to serve live.' : worst === 'WARN' ? 'Launch-blocking items remain (WARN) — resolve, then flip DRY_RUN=0.' : 'A hard dependency is broken (FAIL) — fix before launch.'}`);
+ console.log('wrote data/latest.json');
+ process.exit(worst === 'FAIL' ? 1 : 0);
+}
+main();
← 7169240 auto-data-snapshot: 2026-08-14T12:54:45 (1 data files) — pac
·
back to Dw Signup Fulfillment
·
ecosystem: set PUBLIC_URL for Option C verify links + webhoo ce1c7d7 →