// firewall regression test — the root cause of "hypermosh just does not connect".
//
// A bare `hypermosh serve --hyperident` builds an EMPTY allow-list, which the
// firewall reads as DENY-ALL. Before the fix that produced a silent hang / a
// bare PEER_CONNECTION_FAILED with no explanation. This pins the behaviour:
// • empty allow-list -> client is rejected (connection never establishes)
// • --disable-firewall -> client connects (control frame received)
import DHT from 'hyperdht'
import { serveSession, connectSession } from '../lib/session.mjs'
import { execSync } from 'child_process'
let hasMosh = true
try { execSync('command -v mosh-server', { stdio: 'ignore' }) } catch { hasMosh = false }
let failures = 0
const ok = (cond, msg) => { if (cond) console.log(' ✅ ' + msg); else { console.log(' ❌ ' + msg); failures++ } }
// Single-machine self-hairpin DHT connects can be slow cold, so give the
// expected-connect case generous headroom (matches session.test's 40s tolerance).
async function trial (allow, { timeoutMs, retries }) {
const serverKp = DHT.keyPair()
const s = await serveSession({ keyPair: serverKp, allow })
const clientKp = DHT.keyPair()
const outcome = await new Promise((resolve) => {
let done = false
const finish = (r) => { if (!done) { done = true; resolve(r) } }
const t = setTimeout(() => finish('deny'), timeoutMs) // never established => treated as deny/hang
const session = connectSession({
remoteKey: serverKp.publicKey, keyPair: clientKp, retries,
start: { term: 'xterm', cmd: ['true'] },
onstarted: () => { clearTimeout(t); finish('connect') },
onerror: () => { clearTimeout(t); finish('deny') }
})
setTimeout(() => { try { session.close() } catch {} }, timeoutMs + 500)
})
await s.close()
return outcome
}
console.log('[firewall] empty allow-list denies; --disable-firewall connects')
// DENY: empty allow-list (what a plain `serve --hyperident` produces).
ok((await trial([], { timeoutMs: 20000, retries: 1 })) === 'deny', 'empty allow-list rejects the client (root-cause behaviour pinned)')
// ALLOW: firewall disabled. Requires mosh-server to reach the onstarted control frame.
if (hasMosh) {
ok((await trial(true, { timeoutMs: 45000, retries: 3 })) === 'connect', '--disable-firewall lets the client through to a session')
} else {
console.log(' ⏭ disable-firewall connect check SKIPPED (mosh-server not installed)')
}
console.log(failures === 0 ? '\n✅ firewall test passed' : '\n❌ firewall test failed')
process.exit(failures === 0 ? 0 : 1)