morphit/scripts/reachability-check-smoke.ts
2026-08-10 19:33:10 -07:00

62 lines
3.1 KiB
TypeScript

#!/usr/bin/env tsx
/**
* reachability-check — cp694.
*
* A home node behind a router can't detect its own inbound reachability (NAT
* hairpin), so an ISP 80/443 block used to surface only as a stale "Unreachable"
* federation pill hours later. The post-install self-check probes from an
* external Tor exit and tells the operator plainly, and points at the .onion
* (which needs no port-forward) when the clearnet ports are blocked.
*/
import { existsSync, readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const REPO = join(dirname(fileURLToPath(import.meta.url)), '..');
const read = (r: string): string => readFileSync(join(REPO, r), 'utf8');
let pass = 0, fail = 0;
const check = (n: string, c: boolean, d = ''): void => {
if (c) { console.log(`${n}`); pass++; }
else { console.log(`${n}${d ? `: ${d}` : ''}`); fail++; }
};
console.log('\n── reachability-check (cp694) ─────────────────────────\n');
check(
'the reachability self-check script exists',
existsSync(join(REPO, 'ops/scripts/morphit-reachability-check.sh'))
);
const sh = read('ops/scripts/morphit-reachability-check.sh');
check(
'it probes from OUTSIDE via the Tor SOCKS proxy (external vantage, no hairpin)',
/--socks5-hostname/.test(sh) && /127\.0\.0\.1:9050/.test(sh)
);
check(
'it names the ISP/router 80/443 block as the cause when unreachable',
/ISP/.test(sh) && /inbound 80\/443/.test(sh)
);
check(
'it points at the working .onion path (no port-forward needed)',
/\/var\/lib\/tor/.test(sh) && /http:\/\/\$ONION/.test(sh)
);
const wiz = read('apps/ops-cli/src/init/runAnsibleInstall.ts');
check(
'the install runs the self-check on home boxes',
/morphit-reachability-check\.sh/.test(wiz) && /inputs\.mode === 'home'/.test(wiz)
);
check(
'cp697 — gates the probe behind an online check (never verdicts when offline)',
/timeout \d+ curl -fsS[^\n]*morphit\.io\/verify\.json/.test(sh) && /no internet connection right now/.test(sh)
);
check(
'cp697 — captures the HTTP code via a probe() helper (no ||echo-000 double-append → no false REACHABLE)',
/probe\(\) \{/.test(sh) && /code443="\$\(probe /.test(sh) && /case "\$c" in ''\|000\)/.test(sh)
);
{
const w = readFileSync(join(REPO, 'apps/ops-cli/src/init/runAnsibleInstall.ts'), 'utf8');
const li = w.indexOf('List your instance on the public federated directory');
const rv = w.lastIndexOf("step(0, 0, 'Review your node')");
check('cp698 — the install review is the FINAL step (after the listing), so it reflects the canary', li > 0 && rv > li);
check('cp699/cp705 — the wizard pre-fills the canary instance origin (deriveInstanceOrigin: onion for Tor-only, else https://domain — no re-typing)', /MORPHIT_CANARY_INSTANCE_ORIGIN:\s*\n?\s*deriveInstanceOrigin\(inputs\.torOnly, inputs\.domain, '\/opt\/morphit'\) \?\?\s*\n?\s*`https:\/\/\$\{inputs\.domain\}`/.test(w));
}
console.log(`\n${pass} passed, ${fail} failed\n${fail === 0 ? `✓ all ${pass} reachability-check checks passed` : '✗ FAILED'}`);
process.exit(fail === 0 ? 0 : 1);