import { afterEach, describe, it } from 'node:test'; import { strict as assert } from 'node:assert'; import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'; import * as fs from 'node:fs'; import * as http from 'node:http'; import * as net from 'node:net'; import * as os from 'node:os'; import * as path from 'node:path'; import { connect as natsConnect, type NatsConnection } from 'nats'; import { runMxCli, parseLastJson, assertExitCode } from '../helpers/cli-harness.js'; import { hiveCastLoginCommand, hiveCastLogoutCommand } from '../../src/commands/auth.js'; import { readHiveCastLinkStatus, removeHiveCastHostLink, saveHiveCastHostLink } from '../../src/utils/hivecast-link-store.js'; describe('FP-30 mx auth session commands', () => { const tempDirs: string[] = []; const restoreEnvFns: Array<() => void> = []; const TEST_LOCAL_OWNER_ROOT = 'local-test-owner-root'; afterEach(() => { while (restoreEnvFns.length > 0) { const restore = restoreEnvFns.pop(); if (restore) { restore(); } } while (tempDirs.length > 0) { const dir = tempDirs.pop(); if (dir) { fs.rmSync(dir, { recursive: true, force: true }); } } }); function useMatrixHome(matrixHome: string): void { const previousMatrixHome = process.env.MATRIX_HOME; const previousHostHome = process.env.MATRIX_HOST_HOME; const previousCliConfig = process.env.MATRIX_CLI_CONFIG; const previousPackageRegistry = process.env.MATRIX_PACKAGE_REGISTRY; const previousRegistryUrl = process.env.MATRIX_REGISTRY_URL; const previousHivecastRegistry = process.env.HIVECAST_PACKAGE_REGISTRY; const previousNpmUserConfig = process.env.NPM_CONFIG_USERCONFIG; const previousNodeAuthToken = process.env.NODE_AUTH_TOKEN; const previousNpmToken = process.env.NPM_TOKEN; process.env.MATRIX_HOME = matrixHome; delete process.env.MATRIX_HOST_HOME; delete process.env.MATRIX_CLI_CONFIG; delete process.env.MATRIX_PACKAGE_REGISTRY; delete process.env.MATRIX_REGISTRY_URL; delete process.env.HIVECAST_PACKAGE_REGISTRY; delete process.env.NPM_CONFIG_USERCONFIG; delete process.env.NODE_AUTH_TOKEN; delete process.env.NPM_TOKEN; restoreEnvFns.push(() => { restoreEnv('MATRIX_HOME', previousMatrixHome); restoreEnv('MATRIX_HOST_HOME', previousHostHome); restoreEnv('MATRIX_CLI_CONFIG', previousCliConfig); restoreEnv('MATRIX_PACKAGE_REGISTRY', previousPackageRegistry); restoreEnv('MATRIX_REGISTRY_URL', previousRegistryUrl); restoreEnv('HIVECAST_PACKAGE_REGISTRY', previousHivecastRegistry); restoreEnv('NPM_CONFIG_USERCONFIG', previousNpmUserConfig); restoreEnv('NODE_AUTH_TOKEN', previousNodeAuthToken); restoreEnv('NPM_TOKEN', previousNpmToken); }); } function restoreEnv(key: string, value: string | undefined): void { if (value === undefined) { delete process.env[key]; return; } process.env[key] = value; } async function readRequestBody(req: http.IncomingMessage): Promise { const chunks: Buffer[] = []; for await (const chunk of req) { chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); } return Buffer.concat(chunks).toString('utf8'); } function asRecord(value: unknown): Record | null { return value && typeof value === 'object' && !Array.isArray(value) ? value as Record : null; } function optionalString(value: unknown): string | undefined { return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined; } function installToken(installId: string): string { return installId .toUpperCase() .replace(/[^A-Z0-9]+/g, '-') .replace(/^-+|-+$/g, ''); } function installRootToken(installId: string): string { return installId .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-+|-+$/g, ''); } function localRootForInstallId(installId: string): string { return `local-${installRootToken(installId)}`; } function installScopeForInstallId(installId: string): string { return installToken(installId); } function writeInstallIdentity(matrixHome: string, installId: string): void { const credentialsDir = path.join(matrixHome, 'credentials'); fs.mkdirSync(credentialsDir, { recursive: true }); fs.writeFileSync(path.join(credentialsDir, 'hivecast-install.json'), `${JSON.stringify({ version: 1, installId, createdAt: '2026-05-27T00:00:00.000Z', updatedAt: '2026-05-27T00:00:00.000Z', }, null, 2)}\n`, 'utf8'); } function readInstallIdentityId(matrixHome: string): string { const identity = readJsonRecord(path.join(matrixHome, 'credentials', 'hivecast-install.json')); const installId = optionalString(identity.installId); assert.ok(installId); return installId; } async function getAvailablePort(): Promise { return await new Promise((resolve, reject) => { const server = net.createServer(); server.once('error', reject); server.listen(0, '127.0.0.1', () => { const address = server.address(); if (!address || typeof address === 'string') { server.close(() => reject(new Error('Failed to allocate TCP port'))); return; } const port = address.port; server.close((err) => (err ? reject(err) : resolve(port))); }); }); } async function waitForTcpPort(port: number): Promise { const deadline = Date.now() + 10_000; while (Date.now() < deadline) { const open = await new Promise((resolve) => { const socket = net.connect({ host: '127.0.0.1', port }); socket.once('connect', () => { socket.destroy(); resolve(true); }); socket.once('error', () => { socket.destroy(); resolve(false); }); socket.setTimeout(500, () => { socket.destroy(); resolve(false); }); }); if (open) { return; } await new Promise((resolve) => setTimeout(resolve, 50)); } throw new Error(`Timed out waiting for nats-server on port ${port}`); } function resolveNatsServerBinary(): string { const binaryName = process.platform === 'win32' ? 'nats-server.exe' : 'nats-server'; const candidates = [ process.env.NATS_SERVER_BINARY, path.resolve(process.cwd(), 'projects', 'nats-server', binaryName), path.resolve(process.cwd(), '..', '..', '..', 'nats-server', binaryName), path.resolve(process.cwd(), '..', 'hivecast', 'dist', 'bin', binaryName), ...((process.env.PATH ?? '').split(path.delimiter).map((entry) => path.join(entry, binaryName))), ].filter((value): value is string => typeof value === 'string' && value.trim().length > 0); const found = candidates.find((candidate) => fs.existsSync(candidate)); if (!found) { throw new Error('nats-server binary is required for HiveCast running-Host reload proof'); } return found; } async function stopProcess(child: ChildProcessWithoutNullStreams): Promise { if (child.exitCode !== null) { return; } await new Promise((resolve) => { const timeout = setTimeout(() => { if (child.exitCode === null) { child.kill('SIGKILL'); } }, 2_000); child.once('exit', () => { clearTimeout(timeout); resolve(); }); child.kill('SIGTERM'); }); } async function withNatsServer(run: (natsUrl: string) => Promise): Promise { const port = await getAvailablePort(); const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-nats-')); tempDirs.push(dataDir); const configPath = path.join(dataDir, 'nats-server.conf'); fs.writeFileSync(configPath, [ 'host: "127.0.0.1"', `port: ${port}`, '', ].join('\n'), 'utf8'); const child = spawn(resolveNatsServerBinary(), ['-c', configPath], { stdio: 'pipe', env: process.env, }); let stderr = ''; child.stderr.on('data', (chunk: Buffer) => { stderr += chunk.toString('utf8'); }); try { await Promise.race([ waitForTcpPort(port), new Promise((_resolve, reject) => { child.once('exit', (code) => { reject(new Error(`nats-server exited early (${code}): ${stderr}`)); }); }), ]); return await run(`nats://127.0.0.1:${port}`); } finally { await stopProcess(child); } } function writeHostStatusFromConfig(matrixHome: string, natsUrl: string, previousPid = process.pid): void { const hostConfig = asRecord(JSON.parse(fs.readFileSync(path.join(matrixHome, 'host.json'), 'utf8'))); assert.ok(hostConfig); const transport = asRecord(hostConfig.transport); assert.ok(transport); const nats = asRecord(transport.nats); const root = optionalString(transport.root); assert.ok(root); const authorityRoot = optionalString(transport.authorityRoot) ?? root; const addressRoot = optionalString(transport.addressRoot) ?? authorityRoot; const wsUrl = optionalString(nats?.wsUrl); fs.writeFileSync(path.join(matrixHome, 'host.status.json'), `${JSON.stringify({ kind: 'MatrixHostStatus', version: 1, status: 'running', root, authorityRoot, addressRoot, pid: previousPid, home: matrixHome, supervisorMount: 'host.supervisor.PID-TEST', startedAt: '2026-04-28T00:00:00.000Z', updatedAt: new Date().toISOString(), http: { host: '127.0.0.1', port: 3100, origin: 'http://127.0.0.1:3100', }, transport: { root, authorityRoot, addressRoot, ...(optionalString(transport.spaceId) ? { spaceId: optionalString(transport.spaceId) } : {}), ...(optionalString(transport.routeKey) ? { routeKey: optionalString(transport.routeKey) } : {}), ...(optionalString(transport.publicNamespace) ? { publicNamespace: optionalString(transport.publicNamespace) } : {}), nats: { mode: 'external', url: natsUrl, ...(wsUrl ? { wsUrl } : {}), }, }, }, null, 2)}\n`, 'utf8'); } function testWsUrlForNatsUrl(natsUrl: string): string { const parsed = new URL(natsUrl); const port = Number.parseInt(parsed.port, 10); assert.equal(Number.isInteger(port), true); return `ws://127.0.0.1:${port + 1}`; } function writeLocalOwnerHostStatus( matrixHome: string, natsUrl: string, root = TEST_LOCAL_OWNER_ROOT, ): void { fs.mkdirSync(matrixHome, { recursive: true }); const wsUrl = testWsUrlForNatsUrl(natsUrl); fs.writeFileSync(path.join(matrixHome, 'host.status.json'), `${JSON.stringify({ kind: 'MatrixHostStatus', version: 1, status: 'running', root, authorityRoot: root, addressRoot: root, pid: process.pid, home: matrixHome, supervisorMount: 'host.supervisor.PID-TEST', startedAt: '2026-04-28T00:00:00.000Z', updatedAt: new Date().toISOString(), http: { host: '127.0.0.1', port: 3100, origin: 'http://127.0.0.1:3100', }, transport: { root, authorityRoot: root, nats: { mode: 'external', url: natsUrl, wsUrl, }, }, }, null, 2)}\n`, 'utf8'); } function readJsonRecord(filePath: string): Record { const parsed = asRecord(JSON.parse(fs.readFileSync(filePath, 'utf8'))); assert.ok(parsed, `Expected JSON object at ${filePath}`); return parsed; } function assertHostConfigRoot( matrixHome: string, expectedRoot: string, expected: { readonly routeKey?: string; readonly publicNamespace?: string; readonly spaceId?: string; } = {}, ): void { const hostConfig = readJsonRecord(path.join(matrixHome, 'host.json')); const transport = asRecord(hostConfig.transport); assert.ok(transport); const root = optionalString(transport.root); const authorityRoot = optionalString(transport.authorityRoot) ?? root; const addressRoot = optionalString(transport.addressRoot) ?? authorityRoot; assert.equal(root, expectedRoot); assert.equal(authorityRoot, expectedRoot); assert.equal(addressRoot, expectedRoot); assert.equal(transport.routeKey, expected.routeKey); assert.equal(transport.publicNamespace, expected.publicNamespace); assert.equal(transport.spaceId, expected.spaceId); } function assertHostStatusRoot( matrixHome: string, expectedRoot: string, expected: { readonly routeKey?: string; readonly publicNamespace?: string; readonly spaceId?: string; } = {}, ): void { const status = readJsonRecord(path.join(matrixHome, 'host.status.json')); const transport = asRecord(status.transport); assert.ok(transport); assert.equal(status.root, expectedRoot); assert.equal(status.authorityRoot, expectedRoot); assert.equal(status.addressRoot, expectedRoot); assert.equal(transport.root, expectedRoot); assert.equal(transport.authorityRoot, expectedRoot); assert.equal(transport.addressRoot, expectedRoot); assert.equal(transport.routeKey, expected.routeKey); assert.equal(transport.publicNamespace, expected.publicNamespace); assert.equal(transport.spaceId, expected.spaceId); } async function withReloadSupervisor( natsUrl: string, matrixHome: string, run: (reloadCount: () => number) => Promise, onReload?: (reloadCount: number) => void, ): Promise { const nc: NatsConnection = await natsConnect({ servers: [natsUrl], name: 'mx-cli-reload-proof' }); const sub = nc.subscribe('>'); let reloads = 0; const loop = (async () => { for await (const msg of sub) { const decoded = asRecord(JSON.parse(new TextDecoder().decode(msg.data) || '{}')); if (decoded?.op !== 'config.reload') { continue; } reloads += 1; onReload?.(reloads); writeHostStatusFromConfig(matrixHome, natsUrl); const replyTo = optionalString(decoded.replyTo) ?? msg.reply; if (replyTo) { nc.publish(replyTo, new TextEncoder().encode(JSON.stringify({ ok: true, correlationId: optionalString(decoded.correlationId), result: { ok: true, status: 'reloading' }, }))); } } })(); try { return await run(() => reloads); } finally { sub.unsubscribe(); await nc.drain(); await loop.catch(() => undefined); } } async function withTokenExchangeServer( handler: (body: Record) => Record, run: (cloudUrl: string) => Promise, ): Promise { const server = http.createServer(async (req, res) => { if (req.method !== 'POST' || req.url !== '/api/token-exchange') { res.writeHead(404, { 'content-type': 'application/json' }); res.end(JSON.stringify({ ok: false, error: 'not found' })); return; } const body = JSON.parse(await readRequestBody(req)) as Record; const payload = handler(body); res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify(payload)); }); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const address = server.address(); assert(address && typeof address === 'object'); try { return await run(`http://127.0.0.1:${address.port}`); } finally { await new Promise((resolve, reject) => { server.close((err) => { if (err) { reject(err); return; } resolve(); }); }); } } async function withPairFlowServer( config: { readonly pairRequestId: string; readonly approvalCode: string; readonly exchange: Record; readonly expectedRouteKey?: string; readonly autoApprove?: boolean; }, run: ( cloudUrl: string, controls: { readonly getLastLocalReturnUrl: () => string | null; readonly getStartBody: () => Record | null; }, ) => Promise, ): Promise { let origin = ''; let lastLocalReturnUrl: string | null = null; let startBody: Record | null = null; const server = http.createServer(async (req, res) => { if (req.method === 'POST' && req.url === '/_auth/pair/start') { const body = JSON.parse(await readRequestBody(req) || '{}') as Record; startBody = body; if (config.expectedRouteKey !== undefined) { assert.equal(body.routeKey, config.expectedRouteKey); } if (typeof body.localReturnUrl === 'string') { lastLocalReturnUrl = body.localReturnUrl; } res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ ok: true, pairRequestId: config.pairRequestId, approvalUrl: `${origin}/_auth/pair/${config.pairRequestId}`, expiresIn: 600, })); if (config.autoApprove && lastLocalReturnUrl) { const nonce = typeof body.nonce === 'string' ? body.nonce : ''; setImmediate(() => { void fetch(`${lastLocalReturnUrl}?pairRequestId=${config.pairRequestId}&approvalCode=${config.approvalCode}&nonce=${encodeURIComponent(nonce)}`); }); } return; } if (req.method === 'POST' && req.url === '/_auth/pair/exchange') { const body = JSON.parse(await readRequestBody(req)) as Record; assert.equal(body.pairRequestId, config.pairRequestId); assert.equal(body.approvalCode, config.approvalCode); res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ ok: true, ...config.exchange, })); return; } res.writeHead(404, { 'content-type': 'application/json' }); res.end(JSON.stringify({ ok: false, error: 'not found' })); }); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const address = server.address(); assert(address && typeof address === 'object'); origin = `http://127.0.0.1:${address.port}`; try { return await run(origin, { getLastLocalReturnUrl: () => lastLocalReturnUrl, getStartBody: () => startBody, }); } finally { await new Promise((resolve, reject) => { server.close((err) => { if (err) { reject(err); return; } resolve(); }); }); } } async function withDeviceFlowServer( run: (cloudUrl: string) => Promise, config: { readonly authorityCoordinator?: boolean } = {}, ): Promise { const server = http.createServer(async (req, res) => { if (req.method === 'POST' && req.url === '/_auth/device/start') { const body = JSON.parse(await readRequestBody(req) || '{}') as Record; assert.equal(body.routeKey, 'hivecast-lab'); assert.equal(body.hostName, 'test-host'); assert.equal(body.authorityCoordinator, false); assert.equal(Object.prototype.hasOwnProperty.call(body, 'name'), false); res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ ok: true, deviceCode: 'DEVICE-123', userCode: 'K7P9-TQ2M', verificationUri: 'https://hivecast.example.test/activate', verificationUriComplete: 'https://hivecast.example.test/activate?user_code=K7P9-TQ2M', expiresIn: 600, interval: 1, })); return; } if (req.method === 'POST' && req.url === '/_auth/device/poll') { const body = JSON.parse(await readRequestBody(req)) as Record; assert.equal(body.deviceCode, 'DEVICE-123'); res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ ok: true, status: 'approved', interval: 1 })); return; } if (req.method === 'POST' && req.url === '/_auth/device/exchange') { const body = JSON.parse(await readRequestBody(req)) as Record; assert.equal(body.deviceCode, 'DEVICE-123'); res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ ok: true, root: 'SPACE-HIVECAST-LAB', routeKey: 'hivecast-lab', publicNamespace: 'space.hivecast-lab', spaceId: 'space_01DEVICE', principalId: 'p_device', hostName: 'test-host', deviceSlug: 'test-host', hostLink: { id: 'host-link-device-01', hostId: 'DEVICE-123', hostName: 'test-host', deviceSlug: 'test-host', ...(typeof config.authorityCoordinator === 'boolean' ? { authorityCoordinator: config.authorityCoordinator } : {}), heartbeatToken: 'hlink_test_device_token', status: 'active', }, ...(typeof config.authorityCoordinator === 'boolean' ? { authorityCoordinator: config.authorityCoordinator } : {}), nats: { url: 'nats://hivecast.example.test:4222', wsUrl: 'wss://hivecast.example.test/nats-ws', }, credentials: { jwt: 'device.jwt.token', seed: 'SUDEVICESEED', }, })); return; } if (req.method === 'POST' && req.url === '/_auth/host-link/revoke') { assert.equal(req.headers.authorization, 'Bearer hlink_test_device_token'); const body = JSON.parse(await readRequestBody(req)) as Record; assert.equal(body.hostLinkId, 'host-link-device-01'); assert.equal(body.hostId, 'DEVICE-123'); res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ ok: true, hostLink: { id: 'host-link-device-01', hostId: 'DEVICE-123', status: 'revoked', }, })); return; } res.writeHead(404, { 'content-type': 'application/json' }); res.end(JSON.stringify({ ok: false, error: 'not found' })); }); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const address = server.address(); assert(address && typeof address === 'object'); try { return await run(`http://127.0.0.1:${address.port}`); } finally { await new Promise((resolve, reject) => { server.close((err) => { if (err) { reject(err); return; } resolve(); }); }); } } async function withDeviceNamespaceRequiredServer( run: (cloudUrl: string) => Promise, ): Promise { const server = http.createServer(async (req, res) => { if (req.method === 'POST' && req.url === '/_auth/device/start') { const body = JSON.parse(await readRequestBody(req) || '{}') as Record; assert.equal(body.routeKey, 'owned-key'); res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ ok: true, deviceCode: 'DEVICE-OWNED', userCode: 'K7P9-OWND', verificationUri: 'https://hivecast.example.test/activate', verificationUriComplete: 'https://hivecast.example.test/activate?user_code=K7P9-OWND', expiresIn: 600, interval: 1, })); return; } if (req.method === 'POST' && req.url === '/_auth/device/poll') { const body = JSON.parse(await readRequestBody(req)) as Record; assert.equal(body.deviceCode, 'DEVICE-OWNED'); res.writeHead(409, { 'content-type': 'application/json' }); res.end(JSON.stringify({ ok: false, error: 'namespace_required', status: 'namespace_required', setupUrl: '/apps/web/#setup?redirect=%2Factivate%3Fuser_code%3DK7P9-OWND', })); return; } if (req.method === 'POST' && req.url === '/_auth/device/exchange') { res.writeHead(500, { 'content-type': 'application/json' }); res.end(JSON.stringify({ ok: false, error: 'exchange should not be reached' })); return; } res.writeHead(404, { 'content-type': 'application/json' }); res.end(JSON.stringify({ ok: false, error: 'not found' })); }); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const address = server.address(); assert(address && typeof address === 'object'); try { return await run(`http://127.0.0.1:${address.port}`); } finally { await new Promise((resolve, reject) => { server.close((err) => { if (err) { reject(err); return; } resolve(); }); }); } } it('supports login, whoami, and logout lifecycle', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-auth-')); tempDirs.push(root); const credentialsFile = path.join(root, '.matrix', 'credentials.json'); const loginResult = await runMxCli([ 'node', 'mx', 'login', '--username', 'alice', '--token', 'token-123', '--registry', 'https://pkg.matrix.dev', '--credentials-file', credentialsFile, '--json', ], { cwd: root }); assertExitCode(loginResult, 0); const whoamiAfterLogin = await runMxCli([ 'node', 'mx', 'whoami', '--registry', 'https://pkg.matrix.dev', '--credentials-file', credentialsFile, '--json', ], { cwd: root }); assertExitCode(whoamiAfterLogin, 0); const whoamiPayload = parseLastJson(whoamiAfterLogin.logs) as { authenticated: boolean; username?: string }; assert.equal(whoamiPayload.authenticated, true); assert.equal(whoamiPayload.username, 'alice'); const logoutResult = await runMxCli([ 'node', 'mx', 'logout', '--registry', 'https://pkg.matrix.dev', '--credentials-file', credentialsFile, '--json', ], { cwd: root }); assertExitCode(logoutResult, 0); const whoamiAfterLogout = await runMxCli([ 'node', 'mx', 'whoami', '--registry', 'https://pkg.matrix.dev', '--credentials-file', credentialsFile, '--json', ], { cwd: root }); assertExitCode(whoamiAfterLogout, 0); const afterLogoutPayload = parseLastJson(whoamiAfterLogout.logs) as { authenticated: boolean }; assert.equal(afterLogoutPayload.authenticated, false); }); it('reads npm auth token from configured .npmrc for whoami', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-npmrc-auth-')); tempDirs.push(root); useMatrixHome(path.join(root, '.matrix')); const registry = 'https://registry.example.test/api/packages/open-matrix/npm/'; const npmrcPath = path.join(root, '.npmrc'); fs.writeFileSync( npmrcPath, '//registry.example.test/api/packages/open-matrix/npm/:_authToken=test-token-from-npmrc\n', 'utf8', ); process.env.NPM_CONFIG_USERCONFIG = npmrcPath; const whoamiResult = await runMxCli([ 'node', 'mx', 'whoami', '--registry', registry, '--json', ], { cwd: root }); assertExitCode(whoamiResult, 0); const payload = parseLastJson(whoamiResult.logs) as { authenticated: boolean; username?: string; registry: string; }; assert.equal(payload.authenticated, true); assert.equal(payload.username, 'npm-token'); assert.equal(payload.registry, registry); }); it('does not read home .npmrc when NPM_CONFIG_USERCONFIG points at an explicit file', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-explicit-npmrc-auth-')); tempDirs.push(root); const homeDir = path.join(root, 'home'); fs.mkdirSync(homeDir, { recursive: true }); useMatrixHome(path.join(root, '.matrix')); const previousHome = process.env.HOME; const previousUserConfig = process.env.NPM_CONFIG_USERCONFIG; restoreEnvFns.push(() => { restoreEnv('HOME', previousHome); restoreEnv('NPM_CONFIG_USERCONFIG', previousUserConfig); }); process.env.HOME = homeDir; const registry = 'https://registry.example.test/api/packages/open-matrix/npm/'; fs.writeFileSync( path.join(homeDir, '.npmrc'), '//registry.example.test/api/packages/open-matrix/npm/:_authToken=poisoned-home-token\n', 'utf8', ); const explicitNpmrcPath = path.join(root, 'empty.npmrc'); fs.writeFileSync(explicitNpmrcPath, '', 'utf8'); process.env.NPM_CONFIG_USERCONFIG = explicitNpmrcPath; const whoamiResult = await runMxCli([ 'node', 'mx', 'whoami', '--registry', registry, '--json', ], { cwd: root }); assertExitCode(whoamiResult, 0); const payload = parseLastJson(whoamiResult.logs) as { authenticated: boolean; username?: string; registry: string; }; assert.equal(payload.authenticated, false); assert.equal(payload.username, undefined); assert.equal(payload.registry, registry); }); it('uses Matrix CLI config for the default registry', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-config-')); tempDirs.push(root); useMatrixHome(path.join(root, '.matrix')); const configuredRegistry = 'http://127.0.0.1:4567/api/packages/open-matrix/npm/'; const defaultConfig = await runMxCli([ 'node', 'mx', 'config', 'get', 'registry', '--json', ], { cwd: root }); assertExitCode(defaultConfig, 0); const defaultPayload = parseLastJson(defaultConfig.logs) as { value: string; source: string }; assert.equal(defaultPayload.value, 'https://registry.hivecast.ai/api/packages/open-matrix/npm/'); assert.equal(defaultPayload.source, 'default'); const setConfig = await runMxCli([ 'node', 'mx', 'config', 'set', 'registry', configuredRegistry, '--json', ], { cwd: root }); assertExitCode(setConfig, 0); const listConfig = await runMxCli([ 'node', 'mx', 'config', 'list', '--json', ], { cwd: root }); assertExitCode(listConfig, 0); const listPayload = parseLastJson(listConfig.logs) as { values: { registry?: string }; effective: { registry: string; registrySource: string }; }; assert.equal(listPayload.values.registry, configuredRegistry); assert.equal(listPayload.effective.registry, configuredRegistry); assert.equal(listPayload.effective.registrySource, 'config'); const credentialsFile = path.join(root, '.matrix', 'credentials.json'); const loginResult = await runMxCli([ 'node', 'mx', 'login', '--username', 'alice', '--token', 'token-123', '--credentials-file', credentialsFile, '--json', ], { cwd: root }); assertExitCode(loginResult, 0); const loginPayload = parseLastJson(loginResult.logs) as { registry: string }; assert.equal(loginPayload.registry, configuredRegistry); const whoamiResult = await runMxCli([ 'node', 'mx', 'whoami', '--credentials-file', credentialsFile, '--json', ], { cwd: root }); assertExitCode(whoamiResult, 0); const whoamiPayload = parseLastJson(whoamiResult.logs) as { authenticated: boolean; registry: string }; assert.equal(whoamiPayload.authenticated, true); assert.equal(whoamiPayload.registry, configuredRegistry); const resetConfig = await runMxCli([ 'node', 'mx', 'config', 'reset', '--json', ], { cwd: root }); assertExitCode(resetConfig, 0); const afterReset = await runMxCli([ 'node', 'mx', 'config', 'get', 'registry', '--json', ], { cwd: root }); assertExitCode(afterReset, 0); const resetPayload = parseLastJson(afterReset.logs) as { value: string; source: string }; assert.equal(resetPayload.value, 'https://registry.hivecast.ai/api/packages/open-matrix/npm/'); assert.equal(resetPayload.source, 'default'); }); it('prints a HiveCast pair approval URL before approval is available', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-url-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); await withPairFlowServer( { pairRequestId: 'PAIR-URL-123', approvalCode: 'PAIR-URL-APPROVAL', expectedRouteKey: 'hivecast-lab', exchange: { root: 'SPACE-HIVECAST-LAB', routeKey: 'hivecast-lab', publicNamespace: 'space.hivecast-lab', nats: { url: 'wss://hivecast.example.test/nats-ws', }, credentials: { jwt: 'unused.jwt.token', seed: 'SUUNUSED', }, }, }, async (cloudUrl, controls) => { const loginResult = await runMxCli([ 'node', 'mx', 'login', '--hivecast', '--cloud', cloudUrl, '--route-key', 'hivecast-lab', '--no-wait', '--home', matrixHome, '--json', ], { cwd: root }); assertExitCode(loginResult, 0); const payload = parseLastJson(loginResult.logs) as { mode: string; linked: boolean; setupUrl: string; }; assert.equal(payload.mode, 'approval-required'); assert.equal(payload.linked, false); const setupUrl = new URL(payload.setupUrl); assert.equal(setupUrl.origin, cloudUrl); assert.equal(setupUrl.pathname, '/_auth/pair/PAIR-URL-123'); const startBody = controls.getStartBody(); assert.ok(startBody); assert.equal(startBody.routeKey, 'hivecast-lab'); assert.equal(startBody.authorityCoordinator, true); assert.equal(controls.getLastLocalReturnUrl(), null); }, ); }); it('completes HiveCast setup exchange and stores local Host link state', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-link-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); fs.mkdirSync(matrixHome, { recursive: true }); fs.writeFileSync(path.join(matrixHome, 'host.json'), `${JSON.stringify({ kind: 'MatrixHostConfig', version: 1, home: matrixHome, transport: { root: TEST_LOCAL_OWNER_ROOT, nats: { mode: 'embedded', port: 45222, wsPort: 45223, dataDir: 'nats/test-local', pidFile: 'nats/test-local/nats-server.pid', binaryPath: 'bin/nats-server', }, }, }, null, 2)}\n`); await withTokenExchangeServer( (body) => { assert.equal(body.code, 'SETUP-123'); return { ok: true, root: 'SPACE-HIVECAST-LAB', routeKey: 'hivecast-lab', publicNamespace: 'space.hivecast-lab', spaceId: 'space_01TEST', principalId: 'p_test', displayName: 'HiveCast Lab', nats: { url: 'wss://hivecast.example.test/nats-ws', wsUrl: 'wss://hivecast.example.test/nats-ws', }, credentials: { jwt: 'setup.jwt.token', seed: 'SUFAKESEED', }, }; }, async (cloudUrl) => { const loginResult = await runMxCli([ 'node', 'mx', 'login', '--hivecast', '--cloud', cloudUrl, '--setup-code', 'SETUP-123', '--home', matrixHome, '--json', ], { cwd: root }); assertExitCode(loginResult, 0); const loginPayload = parseLastJson(loginResult.logs) as { linked: boolean; authorityRoot: string; routeKey: string; matrixHome: string; }; assert.equal(loginPayload.linked, true); assert.equal(loginPayload.authorityRoot, 'SPACE-HIVECAST-LAB'); assert.equal(loginPayload.routeKey, 'hivecast-lab'); assert.equal(loginPayload.matrixHome, matrixHome); const linkPath = path.join(matrixHome, 'credentials', 'hivecast-link.json'); const natsPath = path.join(matrixHome, 'credentials', 'hivecast-nats.json'); const hostConfigPath = path.join(matrixHome, 'host.json'); assert.equal(fs.existsSync(linkPath), true); assert.equal(fs.existsSync(natsPath), true); assert.equal(fs.existsSync(hostConfigPath), true); const natsCredentials = JSON.parse(fs.readFileSync(natsPath, 'utf8')) as { jwt?: string; seed?: string; accountSeed?: string; }; assert.equal(natsCredentials.jwt, 'setup.jwt.token'); assert.equal(natsCredentials.seed, 'SUFAKESEED'); assert.equal(Object.prototype.hasOwnProperty.call(natsCredentials, 'accountSeed'), false); const hostConfig = JSON.parse(fs.readFileSync(hostConfigPath, 'utf8')) as { transport: { root: string; authorityRoot: string; nats: { mode: string; url: string; credentialsRef?: string; }; }; hivecastLocalOwnerTransport?: { root?: string; nats?: { mode?: string; port?: number; wsPort?: number; }; }; }; assert.equal(hostConfig.transport.root, TEST_LOCAL_OWNER_ROOT); assert.equal(hostConfig.transport.authorityRoot, TEST_LOCAL_OWNER_ROOT); assert.equal(hostConfig.transport.nats.mode, 'embedded'); assert.equal(hostConfig.transport.nats.credentialsRef, undefined); assert.equal(hostConfig.hivecastLocalOwnerTransport?.root, TEST_LOCAL_OWNER_ROOT); assert.equal(hostConfig.hivecastLocalOwnerTransport?.nats?.mode, 'embedded'); assert.equal(hostConfig.hivecastLocalOwnerTransport?.nats?.port, 45222); assert.equal(hostConfig.hivecastLocalOwnerTransport?.nats?.wsPort, 45223); const whoamiResult = await runMxCli([ 'node', 'mx', 'whoami', '--hivecast', '--home', matrixHome, '--json', ], { cwd: root }); assertExitCode(whoamiResult, 0); const whoamiPayload = parseLastJson(whoamiResult.logs) as { linked: boolean; authorityRoot: string; routeKey: string; publicNamespace: string; principalId: string; }; assert.equal(whoamiPayload.linked, true); assert.equal(whoamiPayload.authorityRoot, 'SPACE-HIVECAST-LAB'); assert.equal(whoamiPayload.routeKey, 'hivecast-lab'); assert.equal(whoamiPayload.publicNamespace, 'space.hivecast-lab'); assert.equal(whoamiPayload.principalId, 'p_test'); const statusResult = await runMxCli([ 'node', 'mx', 'link-status', '--home', matrixHome, '--json', ], { cwd: root }); assertExitCode(statusResult, 0); const statusPayload = parseLastJson(statusResult.logs) as { linked: boolean; authorityRoot: string; spaceId: string; natsCredentialsPresent: boolean; hostConfigPresent: boolean; }; assert.equal(statusPayload.linked, true); assert.equal(statusPayload.authorityRoot, 'SPACE-HIVECAST-LAB'); assert.equal(statusPayload.spaceId, 'space_01TEST'); assert.equal(statusPayload.natsCredentialsPresent, true); assert.equal(statusPayload.hostConfigPresent, true); const logoutResult = await runMxCli([ 'node', 'mx', 'logout', '--hivecast', '--home', matrixHome, '--json', ], { cwd: root }); assertExitCode(logoutResult, 0); const logoutPayload = parseLastJson(logoutResult.logs) as { removed: boolean; hostConfigReset: boolean }; assert.equal(logoutPayload.removed, true); assert.equal(logoutPayload.hostConfigReset, true); assert.equal(fs.existsSync(linkPath), false); assert.equal(fs.existsSync(natsPath), false); assert.equal(fs.existsSync(path.join(matrixHome, 'credentials.json')), false); const localOnlyHostConfig = JSON.parse(fs.readFileSync(hostConfigPath, 'utf8')) as { transport: { root: string; authorityRoot?: string; routeKey?: string; publicNamespace?: string; spaceId?: string; nats: { mode: string; port?: number; wsPort?: number; dataDir?: string; credentialsRef?: string; }; }; }; assert.equal(localOnlyHostConfig.transport.root, TEST_LOCAL_OWNER_ROOT); assert.equal(localOnlyHostConfig.transport.authorityRoot, undefined); assert.equal(localOnlyHostConfig.transport.routeKey, undefined); assert.equal(localOnlyHostConfig.transport.publicNamespace, undefined); assert.equal(localOnlyHostConfig.transport.spaceId, undefined); assert.equal(localOnlyHostConfig.transport.nats.mode, 'embedded'); assert.equal(localOnlyHostConfig.transport.nats.port, 45222); assert.equal(localOnlyHostConfig.transport.nats.wsPort, 45223); assert.equal(localOnlyHostConfig.transport.nats.dataDir, 'nats/test-local'); assert.equal(localOnlyHostConfig.transport.nats.credentialsRef, undefined); const afterLogoutStatusResult = await runMxCli([ 'node', 'mx', 'link-status', '--home', matrixHome, '--json', ], { cwd: root }); assertExitCode(afterLogoutStatusResult, 0); const afterLogoutStatus = parseLastJson(afterLogoutStatusResult.logs) as { linked: boolean; natsCredentialsPresent: boolean; hostConfigLinked: boolean; }; assert.equal(afterLogoutStatus.linked, false); assert.equal(afterLogoutStatus.natsCredentialsPresent, false); assert.equal(afterLogoutStatus.hostConfigLinked, false); }, ); }); it('preserves the running local owner NATS ports when linking from a started Host', () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-running-ports-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); fs.mkdirSync(matrixHome, { recursive: true }); const installId = 'install_running_ports'; writeInstallIdentity(matrixHome, installId); fs.writeFileSync(path.join(matrixHome, 'host.json'), `${JSON.stringify({ kind: 'MatrixHostConfig', version: 1, home: matrixHome, transport: { root: TEST_LOCAL_OWNER_ROOT, nats: { mode: 'embedded', port: 4222, wsPort: 4223, dataDir: 'nats/host-default', pidFile: 'nats/host-default/nats-server.pid', binaryPath: 'bin/nats-server', }, }, }, null, 2)}\n`); fs.writeFileSync(path.join(matrixHome, 'host.status.json'), `${JSON.stringify({ kind: 'MatrixHostStatus', version: 1, status: 'running', pid: process.pid, home: matrixHome, supervisorMount: `host.supervisor.PID-${process.pid}`, startedAt: new Date().toISOString(), updatedAt: new Date().toISOString(), http: { host: '127.0.0.1', port: 45100, origin: 'http://127.0.0.1:45100', }, transport: { root: TEST_LOCAL_OWNER_ROOT, authorityRoot: TEST_LOCAL_OWNER_ROOT, nats: { mode: 'external', url: 'nats://127.0.0.1:45222', wsUrl: 'ws://127.0.0.1:45223', }, }, }, null, 2)}\n`); const localScope = installScopeForInstallId(installId); const localEdgeRuntimeId = `RUNTIME-HOST-${localScope}-EDGE`; const staleLinkedRuntimeId = 'RUNTIME-HOST-STALE-LINKED-EDGE'; const staleLinkedChatRuntimeId = 'RUNTIME-HOST-STALE-LINKED-CHAT'; for (const runtimeId of [localEdgeRuntimeId, staleLinkedRuntimeId, staleLinkedChatRuntimeId]) { const recordDir = path.join(matrixHome, 'runtimes', runtimeId); fs.mkdirSync(recordDir, { recursive: true }); fs.writeFileSync(path.join(recordDir, 'runtime.json'), `${JSON.stringify({ runtimeId, packageDir: path.join(matrixHome, 'packages', 'global', 'node_modules', '@open-matrix', 'matrix-edge'), status: 'stopped', startup: 'auto', restart: 'always', startedAt: new Date().toISOString(), stoppedAt: new Date().toISOString(), updatedAt: new Date().toISOString(), localMounts: ['edge'], metadata: {}, }, null, 2)}\n`); } saveHiveCastHostLink({ cwd: root, matrixHome, cloudUrl: 'https://hivecast.example.test', hostId: 'host_running_ports', authorityRoot: 'SPACE-HIVECAST-LAB', routeKey: 'hivecast-lab', publicNamespace: 'space.hivecast-lab', spaceId: 'space_01RUNNINGPORTS', nats: { url: 'wss://hivecast.example.test/nats-ws' }, natsCredentials: { accountSeed: 'SARUNNINGPORTS' }, }); const linkedConfig = JSON.parse(fs.readFileSync(path.join(matrixHome, 'host.json'), 'utf8')) as { transport?: { root?: string; authorityRoot?: string; routeKey?: string; publicNamespace?: string; spaceId?: string; nats?: { credentialsRef?: string; }; }; hivecastLocalOwnerTransport?: { nats?: { mode?: string; url?: string; wsUrl?: string; port?: number; wsPort?: number; }; }; }; assert.equal(linkedConfig.transport?.root, TEST_LOCAL_OWNER_ROOT); assert.equal(linkedConfig.transport?.authorityRoot, TEST_LOCAL_OWNER_ROOT); assert.equal(linkedConfig.transport?.routeKey, undefined); assert.equal(linkedConfig.transport?.publicNamespace, undefined); assert.equal(linkedConfig.transport?.spaceId, undefined); assert.equal(linkedConfig.transport?.nats?.credentialsRef, undefined); assert.equal(linkedConfig.hivecastLocalOwnerTransport?.nats?.mode, 'external'); assert.equal(linkedConfig.hivecastLocalOwnerTransport?.nats?.url, 'nats://127.0.0.1:45222'); assert.equal(linkedConfig.hivecastLocalOwnerTransport?.nats?.wsUrl, 'ws://127.0.0.1:45223'); assert.equal(linkedConfig.hivecastLocalOwnerTransport?.nats?.port, 45222); assert.equal(linkedConfig.hivecastLocalOwnerTransport?.nats?.wsPort, 45223); const localEdgeAfterLink = JSON.parse(fs.readFileSync( path.join(matrixHome, 'runtimes', localEdgeRuntimeId, 'runtime.json'), 'utf8', )) as { startup?: string; restart?: string }; assert.equal(localEdgeAfterLink.startup, 'auto'); assert.equal(localEdgeAfterLink.restart, 'always'); const removed = removeHiveCastHostLink(root, matrixHome); assert.equal(removed.hostConfigReset, true); const localConfig = JSON.parse(fs.readFileSync(path.join(matrixHome, 'host.json'), 'utf8')) as { transport: { authorityRoot?: string; nats: { mode?: string; url?: string; wsUrl?: string; port?: number; wsPort?: number; }; }; }; assert.equal(localConfig.transport.authorityRoot, undefined); assert.equal(localConfig.transport.nats.mode, 'external'); assert.equal(localConfig.transport.nats.url, 'nats://127.0.0.1:45222'); assert.equal(localConfig.transport.nats.wsUrl, 'ws://127.0.0.1:45223'); assert.equal(localConfig.transport.nats.port, 45222); assert.equal(localConfig.transport.nats.wsPort, 45223); const localEdgeAfterLogout = JSON.parse(fs.readFileSync( path.join(matrixHome, 'runtimes', localEdgeRuntimeId, 'runtime.json'), 'utf8', )) as { startup?: string; restart?: string }; const staleLinkedAfterLogout = JSON.parse(fs.readFileSync( path.join(matrixHome, 'runtimes', staleLinkedRuntimeId, 'runtime.json'), 'utf8', )) as { startup?: string; restart?: string }; const staleLinkedChatAfterLogout = JSON.parse(fs.readFileSync( path.join(matrixHome, 'runtimes', staleLinkedChatRuntimeId, 'runtime.json'), 'utf8', )) as { startup?: string; restart?: string }; assert.equal(localEdgeAfterLogout.startup, 'auto'); assert.equal(localEdgeAfterLogout.restart, 'always'); assert.equal(staleLinkedAfterLogout.startup, 'manual'); assert.equal(staleLinkedAfterLogout.restart, 'never'); assert.equal(staleLinkedChatAfterLogout.startup, 'manual'); assert.equal(staleLinkedChatAfterLogout.restart, 'never'); }); it('derives a fresh unlinked Device root from the stable install identity', () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-fresh-local-root-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); const installId = 'install_fresh_device_root'; writeInstallIdentity(matrixHome, installId); saveHiveCastHostLink({ cwd: root, matrixHome, cloudUrl: 'https://hivecast.example.test', hostId: 'host_fresh_local_root', authorityRoot: 'SPACE-HIVECAST-LAB', routeKey: 'hivecast-lab', publicNamespace: 'space.hivecast-lab', spaceId: 'space_01FRESHLOCALROOT', nats: { url: 'wss://hivecast.example.test/nats-ws' }, natsCredentials: { accountSeed: 'SAFRESHLOCALROOT' }, }); const hostConfig = readJsonRecord(path.join(matrixHome, 'host.json')); const transport = asRecord(hostConfig.transport); assert.ok(transport); assert.equal(transport.root, localRootForInstallId(installId)); assert.equal(transport.authorityRoot, localRootForInstallId(installId)); assert.equal(transport.addressRoot, localRootForInstallId(installId)); assert.match(String(transport.root), /^local-install-fresh-device-root$/); }); it('preserves local owner NATS ports from runtime records when device login links a stopped Host', () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-stopped-ports-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); fs.mkdirSync(matrixHome, { recursive: true }); const installId = 'install_stopped_ports'; writeInstallIdentity(matrixHome, installId); fs.writeFileSync(path.join(matrixHome, 'host.json'), `${JSON.stringify({ kind: 'MatrixHostConfig', version: 1, home: matrixHome, transport: { root: TEST_LOCAL_OWNER_ROOT, nats: { mode: 'embedded', port: 4222, wsPort: 4223, dataDir: 'nats/host-default', pidFile: 'nats/host-default/nats-server.pid', binaryPath: 'bin/nats-server', }, }, }, null, 2)}\n`); const localScope = installScopeForInstallId(installId); const localGatewayRuntimeId = `RUNTIME-HOST-${localScope}-GATEWAY`; const recordDir = path.join(matrixHome, 'runtimes', localGatewayRuntimeId); fs.mkdirSync(recordDir, { recursive: true }); fs.writeFileSync(path.join(recordDir, 'runtime.json'), `${JSON.stringify({ runtimeId: localGatewayRuntimeId, packageDir: path.join(matrixHome, 'packages', 'system', '@open-matrix', 'system-gateway-http'), status: 'stopped', startup: 'auto', restart: 'always', startedAt: new Date().toISOString(), stoppedAt: new Date().toISOString(), updatedAt: new Date().toISOString(), localMounts: ['system.gateway.http'], metadata: { transport: { root: TEST_LOCAL_OWNER_ROOT, authorityRoot: TEST_LOCAL_OWNER_ROOT, nats: { mode: 'external', url: 'nats://127.0.0.1:46222', wsUrl: 'ws://127.0.0.1:46223', }, }, }, }, null, 2)}\n`); saveHiveCastHostLink({ cwd: root, matrixHome, cloudUrl: 'https://hivecast.example.test', hostId: 'host_stopped_ports', authorityRoot: 'SPACE-HIVECAST-LAB', routeKey: 'hivecast-lab', publicNamespace: 'space.hivecast-lab', spaceId: 'space_01STOPPEDPORTS', nats: { url: 'wss://hivecast.example.test/nats-ws' }, natsCredentials: { accountSeed: 'SASTOPPEDPORTS' }, }); const linkedConfig = JSON.parse(fs.readFileSync(path.join(matrixHome, 'host.json'), 'utf8')) as { hivecastLocalOwnerTransport?: { nats?: { port?: number; wsPort?: number; }; }; }; assert.equal(linkedConfig.hivecastLocalOwnerTransport?.nats?.port, 46222); assert.equal(linkedConfig.hivecastLocalOwnerTransport?.nats?.wsPort, 46223); const removed = removeHiveCastHostLink(root, matrixHome); assert.equal(removed.hostConfigReset, true); const localConfig = JSON.parse(fs.readFileSync(path.join(matrixHome, 'host.json'), 'utf8')) as { transport: { root?: string; authorityRoot?: string; routeKey?: string; publicNamespace?: string; spaceId?: string; nats: { mode?: string; port?: number; wsPort?: number; }; }; }; assert.equal(localConfig.transport.root, TEST_LOCAL_OWNER_ROOT); assert.equal(localConfig.transport.authorityRoot, undefined); assert.equal(localConfig.transport.routeKey, undefined); assert.equal(localConfig.transport.publicNamespace, undefined); assert.equal(localConfig.transport.spaceId, undefined); assert.equal(localConfig.transport.nats.mode, 'external'); assert.equal(localConfig.transport.nats.url, 'nats://127.0.0.1:46222'); assert.equal(localConfig.transport.nats.wsUrl, 'ws://127.0.0.1:46223'); assert.equal(localConfig.transport.nats.port, 46222); assert.equal(localConfig.transport.nats.wsPort, 46223); }); it('preserves sibling local owner NATS ports from host config when linking a stopped Host', () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-stopped-config-ports-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); fs.mkdirSync(matrixHome, { recursive: true }); fs.writeFileSync(path.join(matrixHome, 'host.json'), `${JSON.stringify({ kind: 'MatrixHostConfig', version: 1, home: matrixHome, transport: { root: TEST_LOCAL_OWNER_ROOT, authorityRoot: TEST_LOCAL_OWNER_ROOT, nats: { mode: 'external', url: 'nats://127.0.0.1:47222', wsUrl: 'ws://127.0.0.1:47223', port: 47222, wsPort: 47223, dataDir: 'nats/host-default', pidFile: 'nats/host-default/nats-server.pid', binaryPath: 'bin/nats-server', }, }, }, null, 2)}\n`); saveHiveCastHostLink({ cwd: root, matrixHome, cloudUrl: 'https://hivecast.example.test', hostId: 'host_stopped_config_ports', authorityRoot: 'SPACE-HIVECAST-LAB', routeKey: 'hivecast-lab', publicNamespace: 'space.hivecast-lab', spaceId: 'space_01STOPPEDCONFIGPORTS', nats: { url: 'wss://hivecast.example.test/nats-ws' }, natsCredentials: { accountSeed: 'SASTOPPEDCONFIGPORTS' }, }); const linkedConfig = JSON.parse(fs.readFileSync(path.join(matrixHome, 'host.json'), 'utf8')) as { hivecastLocalOwnerTransport?: { nats?: { port?: number; wsPort?: number; }; }; }; assert.equal(linkedConfig.hivecastLocalOwnerTransport?.nats?.port, 47222); assert.equal(linkedConfig.hivecastLocalOwnerTransport?.nats?.wsPort, 47223); const removed = removeHiveCastHostLink(root, matrixHome); assert.equal(removed.hostConfigReset, true); const localConfig = JSON.parse(fs.readFileSync(path.join(matrixHome, 'host.json'), 'utf8')) as { transport: { root?: string; authorityRoot?: string; routeKey?: string; publicNamespace?: string; spaceId?: string; nats: { mode?: string; port?: number; wsPort?: number; credentialsRef?: string; }; }; }; assert.equal(localConfig.transport.root, TEST_LOCAL_OWNER_ROOT); assert.equal(localConfig.transport.authorityRoot, undefined); assert.equal(localConfig.transport.routeKey, undefined); assert.equal(localConfig.transport.publicNamespace, undefined); assert.equal(localConfig.transport.spaceId, undefined); assert.equal(localConfig.transport.nats.mode, 'external'); assert.equal(localConfig.transport.nats.url, 'nats://127.0.0.1:47222'); assert.equal(localConfig.transport.nats.wsUrl, 'ws://127.0.0.1:47223'); assert.equal(localConfig.transport.nats.port, 47222); assert.equal(localConfig.transport.nats.wsPort, 47223); assert.equal(localConfig.transport.nats.credentialsRef, undefined); }); it('moves authority-coordinator Host config onto the linked user authority root', () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-linked-coordinator-root-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); fs.mkdirSync(matrixHome, { recursive: true }); const installId = 'install_42fab8eeedac46d6e348'; writeInstallIdentity(matrixHome, installId); fs.writeFileSync(path.join(matrixHome, 'host.json'), `${JSON.stringify({ kind: 'MatrixHostConfig', version: 1, home: matrixHome, transport: { root: TEST_LOCAL_OWNER_ROOT, authorityRoot: TEST_LOCAL_OWNER_ROOT, nats: { mode: 'external', url: 'nats://127.0.0.1:48222', wsUrl: 'ws://127.0.0.1:48223', port: 48222, wsPort: 48223, }, }, }, null, 2)}\n`); const installScope = installScopeForInstallId(installId); const installSystemRuntimeId = `RUNTIME-HOST-${installScope}-SYSTEM`; const installHostOpsRuntimeId = `RUNTIME-HOST-${installScope}-HOST-OPS`; const localScope = 'LOCAL-STALE-HASH-SCOPE'; const localSystemRuntimeId = `RUNTIME-HOST-${localScope}-SYSTEM`; const localHostOpsRuntimeId = `RUNTIME-HOST-${localScope}-HOST-OPS`; for (const runtimeId of [ installSystemRuntimeId, installHostOpsRuntimeId, localSystemRuntimeId, localHostOpsRuntimeId, ]) { const recordDir = path.join(matrixHome, 'runtimes', runtimeId); fs.mkdirSync(recordDir, { recursive: true }); fs.writeFileSync(path.join(recordDir, 'runtime.json'), `${JSON.stringify({ runtimeId, packageDir: path.join(matrixHome, 'packages', 'global', 'node_modules', '@open-matrix', 'system'), status: 'stopped', startup: 'auto', restart: 'always', updatedAt: new Date().toISOString(), localMounts: ['system', 'system.registry', 'system.devices', 'system.runtimes'], metadata: {}, }, null, 2)}\n`); } saveHiveCastHostLink({ cwd: root, matrixHome, cloudUrl: 'https://hivecast.example.test', hostId: 'install_42fab8eeedac46d6e348', authorityRoot: 'test-user-1', routeKey: 'test-user-1', publicNamespace: 'test-user-1', spaceId: 'space_test_user_1', authorityCoordinator: true, nats: { url: 'nats://dev-platform:4222', wsUrl: 'wss://dev-platform/nats-ws', }, natsCredentials: { accountSeed: 'SATESTUSER1' }, }); const linkedConfig = JSON.parse(fs.readFileSync(path.join(matrixHome, 'host.json'), 'utf8')) as { transport?: { root?: string; authorityRoot?: string; addressRoot?: string; routeKey?: string; publicNamespace?: string; spaceId?: string; nats?: { mode?: string; url?: string; wsUrl?: string; port?: number; wsPort?: number; credentialsRef?: string; }; }; hivecastLocalOwnerTransport?: { root?: string; nats?: { url?: string; wsUrl?: string; }; }; }; assert.equal(linkedConfig.transport?.root, 'test-user-1'); assert.equal(linkedConfig.transport?.authorityRoot, 'test-user-1'); assert.equal(linkedConfig.transport?.addressRoot, 'test-user-1'); assert.equal(linkedConfig.transport?.routeKey, 'test-user-1'); assert.equal(linkedConfig.transport?.publicNamespace, 'test-user-1'); assert.equal(linkedConfig.transport?.spaceId, 'space_test_user_1'); assert.equal(linkedConfig.transport?.nats?.mode, 'external'); assert.equal(linkedConfig.transport?.nats?.url, 'nats://dev-platform:4222'); assert.equal(linkedConfig.transport?.nats?.wsUrl, 'wss://dev-platform/nats-ws'); assert.equal(linkedConfig.transport?.nats?.port, undefined); assert.equal(linkedConfig.transport?.nats?.wsPort, undefined); assert.equal( linkedConfig.transport?.nats?.credentialsRef, `file:${path.join(matrixHome, 'credentials', 'hivecast-nats.json')}`, ); assert.equal(linkedConfig.hivecastLocalOwnerTransport?.root, TEST_LOCAL_OWNER_ROOT); assert.equal(linkedConfig.hivecastLocalOwnerTransport?.nats?.url, 'nats://127.0.0.1:48222'); assert.equal(linkedConfig.hivecastLocalOwnerTransport?.nats?.wsUrl, 'ws://127.0.0.1:48223'); const installSystemAfterLink = JSON.parse(fs.readFileSync( path.join(matrixHome, 'runtimes', installSystemRuntimeId, 'runtime.json'), 'utf8', )) as { startup?: string; restart?: string; metadata?: { hivecastDefaultRuntimePolicy?: string } }; assert.equal(installSystemAfterLink.startup, 'auto'); assert.equal(installSystemAfterLink.restart, 'always'); assert.equal(installSystemAfterLink.metadata?.hivecastDefaultRuntimePolicy, 'active'); const localSystemAfterLink = JSON.parse(fs.readFileSync( path.join(matrixHome, 'runtimes', localSystemRuntimeId, 'runtime.json'), 'utf8', )) as { startup?: string; restart?: string; metadata?: { hivecastDefaultRuntimePolicy?: string } }; assert.equal(localSystemAfterLink.startup, 'manual'); assert.equal(localSystemAfterLink.restart, 'never'); assert.equal(localSystemAfterLink.metadata?.hivecastDefaultRuntimePolicy, 'inactive'); const installHostOpsAfterLink = JSON.parse(fs.readFileSync( path.join(matrixHome, 'runtimes', installHostOpsRuntimeId, 'runtime.json'), 'utf8', )) as { startup?: string; restart?: string; metadata?: { hivecastDefaultRuntimePolicy?: string } }; assert.equal(installHostOpsAfterLink.startup, 'auto'); assert.equal(installHostOpsAfterLink.restart, 'always'); assert.equal(installHostOpsAfterLink.metadata?.hivecastDefaultRuntimePolicy, 'active'); const localHostOpsAfterLink = JSON.parse(fs.readFileSync( path.join(matrixHome, 'runtimes', localHostOpsRuntimeId, 'runtime.json'), 'utf8', )) as { startup?: string; restart?: string; metadata?: { hivecastDefaultRuntimePolicy?: string } }; assert.equal(localHostOpsAfterLink.startup, 'manual'); assert.equal(localHostOpsAfterLink.restart, 'never'); assert.equal(localHostOpsAfterLink.metadata?.hivecastDefaultRuntimePolicy, 'inactive'); }); it('refuses authority-coordinator links without an installed local Host transport', () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-linked-coordinator-no-local-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); fs.mkdirSync(matrixHome, { recursive: true }); assert.throws( () => saveHiveCastHostLink({ cwd: root, matrixHome, cloudUrl: 'https://hivecast.example.test', hostId: 'install_ws_only', authorityRoot: 'test-user-1', authorityCoordinator: true, nats: { url: 'wss://hivecast.example.test/nats-ws' }, natsCredentials: { jwt: 'device.jwt.token', seed: 'SUDEVICESEED' }, }), /HIVECAST_LINK_LOCAL_TRANSPORT_MISSING/, ); }); it('does not classify local owner authorityRoot as a HiveCast link', () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-local-authority-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); fs.mkdirSync(matrixHome, { recursive: true }); fs.writeFileSync(path.join(matrixHome, 'host.json'), `${JSON.stringify({ kind: 'MatrixHostConfig', version: 1, home: matrixHome, transport: { root: TEST_LOCAL_OWNER_ROOT, authorityRoot: TEST_LOCAL_OWNER_ROOT, nats: { mode: 'embedded', port: 46222, wsPort: 46223, }, }, }, null, 2)}\n`); const status = readHiveCastLinkStatus(root, matrixHome); assert.equal(status.linked, false); assert.equal(status.hostConfigLinked, false); }); it('does not classify sibling local owner NATS as a HiveCast link', () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-local-sibling-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); fs.mkdirSync(matrixHome, { recursive: true }); fs.writeFileSync(path.join(matrixHome, 'host.json'), `${JSON.stringify({ kind: 'MatrixHostConfig', version: 1, home: matrixHome, transport: { root: TEST_LOCAL_OWNER_ROOT, authorityRoot: TEST_LOCAL_OWNER_ROOT, nats: { mode: 'external', url: 'nats://127.0.0.1:47222', wsUrl: 'ws://127.0.0.1:47223', port: 47222, wsPort: 47223, }, }, }, null, 2)}\n`); const status = readHiveCastLinkStatus(root, matrixHome); assert.equal(status.linked, false); assert.equal(status.hostConfigLinked, false); }); it('completes HiveCast device login and stores local Host link state', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-device-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); await withDeviceFlowServer(async (cloudUrl) => { const loginResult = await runMxCli([ 'node', 'mx', 'login', '--hivecast', '--device', '--cloud', cloudUrl, '--route-key', 'hivecast-lab', '--name', 'test-host', '--home', matrixHome, '--json', ], { cwd: root }); assertExitCode(loginResult, 0); const loginPayload = parseLastJson(loginResult.logs) as { linked: boolean; hostLinkId: string; hostId: string; authorityRoot: string; routeKey: string; publicNamespace: string; spaceId: string; hostName?: string; deviceSlug?: string; }; assert.equal(loginPayload.linked, true); assert.equal(loginPayload.hostLinkId, 'host-link-device-01'); assert.equal(loginPayload.hostId, 'DEVICE-123'); assert.equal(loginPayload.authorityRoot, 'SPACE-HIVECAST-LAB'); assert.equal(loginPayload.routeKey, 'hivecast-lab'); assert.equal(loginPayload.publicNamespace, 'space.hivecast-lab'); assert.equal(loginPayload.spaceId, 'space_01DEVICE'); assert.equal(loginPayload.hostName, 'test-host'); assert.equal(loginPayload.deviceSlug, 'test-host'); assert.equal(fs.existsSync(path.join(matrixHome, 'credentials', 'hivecast-link.json')), true); const natsCredentials = JSON.parse(fs.readFileSync(path.join(matrixHome, 'credentials', 'hivecast-nats.json'), 'utf8')) as { jwt?: string; seed?: string; accountSeed?: string; }; assert.equal(natsCredentials.jwt, 'device.jwt.token'); assert.equal(natsCredentials.seed, 'SUDEVICESEED'); assert.equal(Object.prototype.hasOwnProperty.call(natsCredentials, 'accountSeed'), false); assert.equal(fs.existsSync(path.join(matrixHome, 'host.json')), true); const hostConfig = JSON.parse(fs.readFileSync(path.join(matrixHome, 'host.json'), 'utf8')) as { transport: { root: string; authorityRoot: string; routeKey?: string; publicNamespace?: string; spaceId?: string; nats?: { credentialsRef?: string; }; }; }; const expectedLocalRoot = localRootForInstallId(readInstallIdentityId(matrixHome)); assert.equal(hostConfig.transport.root, expectedLocalRoot); assert.equal(hostConfig.transport.authorityRoot, expectedLocalRoot); assert.equal(hostConfig.transport.routeKey, undefined); assert.equal(hostConfig.transport.publicNamespace, undefined); assert.equal(hostConfig.transport.spaceId, undefined); assert.equal(hostConfig.transport.nats?.credentialsRef, undefined); const whoamiResult = await runMxCli([ 'node', 'mx', 'whoami', '--hivecast', '--home', matrixHome, '--json', ], { cwd: root }); assertExitCode(whoamiResult, 0); const whoamiPayload = parseLastJson(whoamiResult.logs) as { linked: boolean; hostLinkId: string; authorityRoot: string; routeKey: string; publicNamespace: string; principalId: string; hostName?: string; deviceSlug?: string; }; assert.equal(whoamiPayload.linked, true); assert.equal(whoamiPayload.hostLinkId, 'host-link-device-01'); assert.equal(whoamiPayload.authorityRoot, 'SPACE-HIVECAST-LAB'); assert.equal(whoamiPayload.routeKey, 'hivecast-lab'); assert.equal(whoamiPayload.publicNamespace, 'space.hivecast-lab'); assert.equal(whoamiPayload.principalId, 'p_device'); assert.equal(whoamiPayload.hostName, 'test-host'); assert.equal(whoamiPayload.deviceSlug, 'test-host'); const statusResult = await runMxCli([ 'node', 'mx', 'link-status', '--home', matrixHome, '--json', ], { cwd: root }); assertExitCode(statusResult, 0); const statusPayload = parseLastJson(statusResult.logs) as { linked: boolean; hostLinkId: string; authorityRoot: string; routeKey: string; publicNamespace: string; spaceId: string; natsCredentialsPresent: boolean; hostConfigLinked: boolean; }; assert.equal(statusPayload.linked, true); assert.equal(statusPayload.hostLinkId, 'host-link-device-01'); assert.equal(statusPayload.authorityRoot, 'SPACE-HIVECAST-LAB'); assert.equal(statusPayload.routeKey, 'hivecast-lab'); assert.equal(statusPayload.publicNamespace, 'space.hivecast-lab'); assert.equal(statusPayload.spaceId, 'space_01DEVICE'); assert.equal(statusPayload.natsCredentialsPresent, true); assert.equal(statusPayload.hostConfigLinked, true); const logoutResult = await runMxCli([ 'node', 'mx', 'logout', '--hivecast', '--revoke-cloud-link', '--home', matrixHome, '--json', ], { cwd: root }); assertExitCode(logoutResult, 0); const logoutPayload = parseLastJson(logoutResult.logs) as { removed: boolean; cloudRevoke?: { attempted: boolean; revoked: boolean; }; hostConfigReset: boolean; }; assert.equal(logoutPayload.removed, true); assert.equal(logoutPayload.cloudRevoke?.attempted, true); assert.equal(logoutPayload.cloudRevoke?.revoked, true); assert.equal(logoutPayload.hostConfigReset, true); assert.equal(fs.existsSync(path.join(matrixHome, 'credentials', 'hivecast-link.json')), false); }); }); it('prints HiveCast device-code challenge without polling when no-wait is requested', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-device-no-wait-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); await withDeviceFlowServer(async (cloudUrl) => { const loginResult = await runMxCli([ 'node', 'mx', 'login', '--hivecast', '--device', '--cloud', cloudUrl, '--route-key', 'hivecast-lab', '--name', 'test-host', '--home', matrixHome, '--json', '--no-wait', ], { cwd: root }); assertExitCode(loginResult, 0); const loginPayload = parseLastJson(loginResult.logs) as { mode: string; linked: boolean; deviceCode: string; userCode: string; verificationUri: string; verificationUriComplete: string; setupUrl: string; }; assert.equal(loginPayload.mode, 'approval-required'); assert.equal(loginPayload.linked, false); assert.equal(loginPayload.deviceCode, 'DEVICE-123'); assert.equal(loginPayload.userCode, 'K7P9-TQ2M'); assert.equal(loginPayload.verificationUri, 'https://hivecast.example.test/activate'); assert.equal(loginPayload.verificationUriComplete, 'https://hivecast.example.test/activate?user_code=K7P9-TQ2M'); assert.equal(loginPayload.setupUrl, loginPayload.verificationUriComplete); assert.equal(fs.existsSync(path.join(matrixHome, 'credentials', 'hivecast-link.json')), false); assert.equal(fs.existsSync(path.join(matrixHome, 'credentials', 'hivecast-nats.json')), false); }); }); it('treats --headless as the product alias for HiveCast device-code login', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-headless-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); await withDeviceFlowServer(async (cloudUrl) => { const loginResult = await runMxCli([ 'node', 'mx', 'login', '--hivecast', '--headless', '--cloud', cloudUrl, '--route-key', 'hivecast-lab', '--name', 'test-host', '--home', matrixHome, '--json', ], { cwd: root }); assertExitCode(loginResult, 0); const loginPayload = parseLastJson(loginResult.logs) as { linked: boolean; hostLinkId: string; authorityRoot: string; }; assert.equal(loginPayload.linked, true); assert.equal(loginPayload.hostLinkId, 'host-link-device-01'); assert.equal(loginPayload.authorityRoot, 'SPACE-HIVECAST-LAB'); assert.equal(fs.existsSync(path.join(matrixHome, 'credentials', 'hivecast-link.json')), true); }); }); it('keeps headless HiveCast device login on the linked authority root for an installed Host', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-headless-root-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); await withNatsServer(async (natsUrl) => { writeLocalOwnerHostStatus(matrixHome, natsUrl, 'hivecast-headless-local-owner-root'); await withReloadSupervisor(natsUrl, matrixHome, async (reloadCount) => { await withDeviceFlowServer(async (cloudUrl) => { const loginResult = await runMxCli([ 'node', 'mx', 'login', '--hivecast', '--headless', '--cloud', cloudUrl, '--route-key', 'hivecast-lab', '--name', 'test-host', '--home', matrixHome, '--json', ], { cwd: root }); assertExitCode(loginResult, 0); const loginPayload = parseLastJson(loginResult.logs) as { linked: boolean; hostLinkId: string; authorityRoot: string; authorityCoordinator: boolean; }; assert.equal(loginPayload.linked, true); assert.equal(loginPayload.hostLinkId, 'host-link-device-01'); assert.equal(loginPayload.authorityRoot, 'SPACE-HIVECAST-LAB'); assert.equal(loginPayload.authorityCoordinator, false); assert.equal(reloadCount(), 1); assertHostConfigRoot(matrixHome, 'SPACE-HIVECAST-LAB', { routeKey: 'hivecast-lab', publicNamespace: 'space.hivecast-lab', spaceId: 'space_01DEVICE', }); assertHostStatusRoot(matrixHome, 'SPACE-HIVECAST-LAB', { routeKey: 'hivecast-lab', publicNamespace: 'space.hivecast-lab', spaceId: 'space_01DEVICE', }); }, { authorityCoordinator: true }); }); }); }); it('keeps polling HiveCast device login while approval waits for namespace setup', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-device-owned-key-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); await withDeviceNamespaceRequiredServer(async (cloudUrl) => { const result = await runMxCli([ 'node', 'mx', 'login', '--hivecast', '--device', '--cloud', cloudUrl, '--route-key', 'owned-key', '--home', matrixHome, '--json', '--poll-timeout-ms', '50', ], { cwd: root }); assertExitCode(result, 1); assert.match(result.errors.join('\n'), /HIVECAST_DEVICE_TIMEOUT/); assert.equal(fs.existsSync(path.join(matrixHome, 'credentials', 'hivecast-link.json')), false); assert.equal(fs.existsSync(path.join(matrixHome, 'credentials', 'hivecast-nats.json')), false); }); }); it('keeps HiveCast login idempotent and refuses accidental relink to a different route key', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-idempotent-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); let exchangeCalls = 0; await withTokenExchangeServer( (body) => { exchangeCalls += 1; const code = String(body.code ?? ''); if (code === 'FIRST') { return { ok: true, root: 'SPACE-HIVECAST-LAB', routeKey: 'hivecast-lab', publicNamespace: 'space.hivecast-lab', spaceId: 'space_01IDEMPOTENT', nats: { url: 'wss://hivecast.example.test/nats-ws' }, credentials: { jwt: 'first.jwt.token', seed: 'SUFIRSTIDEMPOTENT' }, }; } if (code === 'SECOND') { return { ok: true, root: 'SPACE-OTHER-LAB', routeKey: 'other-lab', publicNamespace: 'space.other-lab', spaceId: 'space_01RELINK', nats: { url: 'wss://hivecast.example.test/nats-ws' }, credentials: { jwt: 'second.jwt.token', seed: 'SUSECONDRELINK' }, }; } return { ok: false, error: `unexpected code ${code}` }; }, async (cloudUrl) => { const first = await hiveCastLoginCommand({ cwd: root, home: matrixHome, cloudUrl, setupCode: 'FIRST', }); assert.equal(first.linked, true); assert.equal(first.routeKey, 'hivecast-lab'); assert.equal(exchangeCalls, 1); const alreadyLinked = await hiveCastLoginCommand({ cwd: root, home: matrixHome, cloudUrl, routeKey: 'hivecast-lab', }); assert.equal(alreadyLinked.linked, true); assert.equal(alreadyLinked.alreadyLinked, true); assert.equal(alreadyLinked.routeKey, 'hivecast-lab'); assert.equal(exchangeCalls, 1); await assert.rejects( () => hiveCastLoginCommand({ cwd: root, home: matrixHome, cloudUrl, routeKey: 'other-lab', }), /HIVECAST_LINK_ROUTE_MISMATCH/, ); await assert.rejects( () => hiveCastLoginCommand({ cwd: root, home: matrixHome, cloudUrl: 'https://other-hivecast.example.test', routeKey: 'hivecast-lab', }), /HIVECAST_LINK_CLOUD_MISMATCH/, ); assert.equal(exchangeCalls, 1); const relinked = await hiveCastLoginCommand({ cwd: root, home: matrixHome, cloudUrl, setupCode: 'SECOND', forceRelink: true, }); assert.equal(relinked.linked, true); assert.equal(relinked.alreadyLinked, undefined); assert.equal(relinked.routeKey, 'other-lab'); assert.equal(relinked.authorityRoot, 'SPACE-OTHER-LAB'); assert.equal(exchangeCalls, 2); }, ); }); it('keeps running Host roots converged across HiveCast login, already-linked, relink, and logout', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-running-host-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); const localOwnerRoot = 'hivecast-proof-local-owner-root'; const hostCredentialsPath = path.join(matrixHome, 'credentials.json'); const credentialPresenceAtReload: boolean[] = []; await withNatsServer(async (natsUrl) => { writeLocalOwnerHostStatus(matrixHome, natsUrl, localOwnerRoot); await withReloadSupervisor(natsUrl, matrixHome, async (reloadCount) => { await withTokenExchangeServer( (body) => { const code = String(body.code ?? ''); if (code === 'RUNNING-HOST') { return { ok: true, root: 'SPACE-HIVECAST-RUNNING', routeKey: 'hivecast-running', publicNamespace: 'space.hivecast-running', spaceId: 'space_01RUNNING', authorityCoordinator: true, nats: { url: 'nats://hivecast.example.test:4222', }, credentials: { jwt: 'running.jwt.token', seed: 'SURUNNINGHOST', }, }; } if (code === 'RELINK-HOST') { return { ok: true, root: 'SPACE-HIVECAST-RELINK', routeKey: 'hivecast-relink', publicNamespace: 'space.hivecast-relink', spaceId: 'space_01RELINKHOST', authorityCoordinator: true, nats: { url: 'nats://hivecast.example.test:4222', }, credentials: { jwt: 'relink.jwt.token', seed: 'SURELINKHOST', }, }; } return { ok: false, error: `unexpected code ${code}`, }; }, async (cloudUrl) => { const result = await hiveCastLoginCommand({ cwd: root, home: matrixHome, cloudUrl, setupCode: 'RUNNING-HOST', }); assert.equal(result.linked, true); assert.equal(result.authorityCoordinator, true); const loginReload = result.hostConfigReload; assert.ok(loginReload); assert.equal(loginReload.attempted, true); assert.equal(loginReload.applied, true); assert.equal(reloadCount(), 1); assertHostConfigRoot(matrixHome, 'SPACE-HIVECAST-RUNNING', { routeKey: 'hivecast-running', publicNamespace: 'space.hivecast-running', spaceId: 'space_01RUNNING', }); assertHostStatusRoot(matrixHome, 'SPACE-HIVECAST-RUNNING', { routeKey: 'hivecast-running', publicNamespace: 'space.hivecast-running', spaceId: 'space_01RUNNING', }); const alreadyLinked = await hiveCastLoginCommand({ cwd: root, home: matrixHome, cloudUrl, routeKey: 'hivecast-running', }); assert.equal(alreadyLinked.linked, true); assert.equal(alreadyLinked.alreadyLinked, true); assert.equal(alreadyLinked.authorityRoot, 'SPACE-HIVECAST-RUNNING'); assert.equal(alreadyLinked.authorityCoordinator, true); assert.equal(reloadCount(), 2); assertHostConfigRoot(matrixHome, 'SPACE-HIVECAST-RUNNING', { routeKey: 'hivecast-running', publicNamespace: 'space.hivecast-running', spaceId: 'space_01RUNNING', }); assertHostStatusRoot(matrixHome, 'SPACE-HIVECAST-RUNNING', { routeKey: 'hivecast-running', publicNamespace: 'space.hivecast-running', spaceId: 'space_01RUNNING', }); const relinked = await hiveCastLoginCommand({ cwd: root, home: matrixHome, cloudUrl, setupCode: 'RELINK-HOST', forceRelink: true, }); assert.equal(relinked.linked, true); assert.equal(relinked.alreadyLinked, undefined); assert.equal(relinked.authorityRoot, 'SPACE-HIVECAST-RELINK'); assert.equal(relinked.authorityCoordinator, true); assert.equal(reloadCount(), 3); assertHostConfigRoot(matrixHome, 'SPACE-HIVECAST-RELINK', { routeKey: 'hivecast-relink', publicNamespace: 'space.hivecast-relink', spaceId: 'space_01RELINKHOST', }); assertHostStatusRoot(matrixHome, 'SPACE-HIVECAST-RELINK', { routeKey: 'hivecast-relink', publicNamespace: 'space.hivecast-relink', spaceId: 'space_01RELINKHOST', }); const logout = await hiveCastLogoutCommand({ cwd: root, home: matrixHome, }); assert.equal(logout.removed, true); const logoutReload = logout.hostConfigReload; assert.ok(logoutReload); assert.equal(logoutReload.attempted, true); assert.equal(logoutReload.applied, true); assert.equal(reloadCount(), 4); assertHostConfigRoot(matrixHome, localOwnerRoot); assertHostStatusRoot(matrixHome, localOwnerRoot); assert.deepEqual(credentialPresenceAtReload, [true, true, true, true]); assert.equal(fs.existsSync(hostCredentialsPath), false); }, ); }, () => { credentialPresenceAtReload.push(fs.existsSync(hostCredentialsPath)); }); }); }); it('rewrites invalid shared bootstrap root to the install-local root before saving linked Host config', () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-root-repair-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); const installId = 'install_root_repair_01'; const expectedLocalRoot = localRootForInstallId(installId); const invalidSharedRoot = 'hivecast-local-development-platform-topic'; writeInstallIdentity(matrixHome, installId); fs.writeFileSync(path.join(matrixHome, 'host.json'), `${JSON.stringify({ kind: 'MatrixHostConfig', version: 1, home: matrixHome, transport: { root: invalidSharedRoot, authorityRoot: invalidSharedRoot, addressRoot: invalidSharedRoot, nats: { mode: 'external', url: 'nats://127.0.0.1:48222', wsUrl: 'ws://127.0.0.1:48223', port: 48222, wsPort: 48223, }, }, }, null, 2)}\n`, 'utf8'); saveHiveCastHostLink({ cwd: root, matrixHome, cloudUrl: 'https://hivecast.example.test', hostId: 'install_root_repair_01', authorityRoot: 'richard-santomauro-nimbletec-com', authorityCoordinator: true, nats: { url: 'nats://hivecast.example.test:4222', }, natsCredentials: { jwt: 'root.repair.jwt', seed: 'SUROOTREPAIR', }, hostLinkToken: 'hostlink-root-repair-token', hostLinkId: 'hostlink_root_repair', principalId: 'principal_root_repair', hostName: 'root-repair-device', }); const linkedConfig = readJsonRecord(path.join(matrixHome, 'host.json')); const localOwnerTransport = asRecord(linkedConfig.hivecastLocalOwnerTransport); assert.ok(localOwnerTransport); assert.equal(localOwnerTransport.root, expectedLocalRoot); assertHostConfigRoot(matrixHome, 'richard-santomauro-nimbletec-com'); const removed = removeHiveCastHostLink(root, matrixHome); assert.equal(removed.hostConfigReset, true); assertHostConfigRoot(matrixHome, expectedLocalRoot); }); it('refuses linked success when a running Host does not apply linked config', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-reload-failure-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); await withNatsServer(async (natsUrl) => { writeLocalOwnerHostStatus(matrixHome, natsUrl, 'hivecast-reload-failure-local-root'); await withTokenExchangeServer( () => ({ ok: true, root: 'SPACE-HIVECAST-RELOAD-FAILED', routeKey: 'hivecast-reload-failed', publicNamespace: 'space.hivecast-reload-failed', spaceId: 'space_01RELOADFAILED', authorityCoordinator: true, nats: { url: 'nats://hivecast.example.test:4222', }, credentials: { jwt: 'reload-failed.jwt.token', seed: 'SURELOADFAILED', }, }), async (cloudUrl) => { await assert.rejects( () => hiveCastLoginCommand({ cwd: root, home: matrixHome, cloudUrl, setupCode: 'RELOAD-FAILED', }), /HIVECAST_LINK_HOST_CONFIG_RELOAD_FAILED: running Host did not apply linked config[\s\S]*expectedRoot=SPACE-HIVECAST-RELOAD-FAILED/, ); }, ); }); }); it('waits for loopback HiveCast pair approval before exchanging the approval code', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-wait-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); const openedApprovalUrls: string[] = []; await withNatsServer(async (natsUrl) => { writeLocalOwnerHostStatus(matrixHome, natsUrl, 'hivecast-loopback-local-owner-root'); await withReloadSupervisor(natsUrl, matrixHome, async (reloadCount) => { await withPairFlowServer( { pairRequestId: 'PAIR-WAIT', approvalCode: 'APPROVAL-WAIT', exchange: { root: 'SPACE-HIVECAST-WAIT', routeKey: 'hivecast-wait', publicNamespace: 'space.hivecast-wait', spaceId: 'space_01WAIT', authorityCoordinator: true, nats: { url: 'nats://hivecast.example.test:4222', }, credentials: { jwt: 'wait.jwt.token', seed: 'SUFAKEWAIT', }, }, }, async (cloudUrl, controls) => { const result = await hiveCastLoginCommand({ cwd: root, home: matrixHome, cloudUrl, waitForCallback: true, noOpen: true, callbackTimeoutMs: 5_000, openApprovalUrl: (approvalUrl) => { openedApprovalUrls.push(approvalUrl); }, onSetupUrl: (setupUrl) => { const parsed = new URL(setupUrl); assert.equal(parsed.origin, cloudUrl); assert.equal(parsed.pathname, '/_auth/pair/PAIR-WAIT'); const localReturnUrl = controls.getLastLocalReturnUrl(); assert.ok(localReturnUrl); const startBody = controls.getStartBody(); assert.equal(typeof startBody?.nonce, 'string'); setImmediate(() => { void fetch(`${localReturnUrl}?pairRequestId=PAIR-WAIT&approvalCode=APPROVAL-WAIT&nonce=${encodeURIComponent(String(startBody?.nonce))}`); }); }, }); assert.equal(result.linked, true); assert.equal(result.authorityRoot, 'SPACE-HIVECAST-WAIT'); assert.equal(result.authorityCoordinator, true); assert.equal(result.routeKey, 'hivecast-wait'); assert.equal(reloadCount(), 1); assertHostConfigRoot(matrixHome, 'SPACE-HIVECAST-WAIT', { routeKey: 'hivecast-wait', publicNamespace: 'space.hivecast-wait', spaceId: 'space_01WAIT', }); assertHostStatusRoot(matrixHome, 'SPACE-HIVECAST-WAIT', { routeKey: 'hivecast-wait', publicNamespace: 'space.hivecast-wait', spaceId: 'space_01WAIT', }); assert.equal(fs.existsSync(path.join(matrixHome, 'credentials', 'hivecast-link.json')), true); assert.deepEqual(openedApprovalUrls, []); }, ); }); }); }); it('opens the HiveCast pair approval URL by default during loopback login', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-open-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); const openedApprovalUrls: string[] = []; await withPairFlowServer( { pairRequestId: 'PAIR-OPEN', approvalCode: 'APPROVAL-OPEN', exchange: { root: 'SPACE-HIVECAST-OPEN', routeKey: 'hivecast-open', publicNamespace: 'space.hivecast-open', nats: { url: 'wss://hivecast.example.test/nats-ws', }, credentials: { jwt: 'open.jwt.token', seed: 'SUFAKEOPEN', }, }, }, async (cloudUrl, controls) => { let announcedSetupUrl = ''; const result = await hiveCastLoginCommand({ cwd: root, home: matrixHome, cloudUrl, waitForCallback: true, callbackTimeoutMs: 5_000, openApprovalUrl: (approvalUrl) => { openedApprovalUrls.push(approvalUrl); }, onSetupUrl: (setupUrl) => { announcedSetupUrl = setupUrl; const localReturnUrl = controls.getLastLocalReturnUrl(); assert.ok(localReturnUrl); const startBody = controls.getStartBody(); assert.equal(typeof startBody?.nonce, 'string'); setImmediate(() => { void fetch(`${localReturnUrl}?pairRequestId=PAIR-OPEN&approvalCode=APPROVAL-OPEN&nonce=${encodeURIComponent(String(startBody?.nonce))}`); }); }, }); assert.equal(result.linked, true); assert.equal(result.authorityRoot, 'SPACE-HIVECAST-OPEN'); assert.equal(result.routeKey, 'hivecast-open'); assert.deepEqual(openedApprovalUrls, [announcedSetupUrl]); }, ); }); it('prints the HiveCast pair approval URL without opening a browser when --print-url is used', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-hivecast-print-url-')); tempDirs.push(root); const matrixHome = path.join(root, '.matrix'); await withPairFlowServer( { pairRequestId: 'PAIR-PRINT', approvalCode: 'APPROVAL-PRINT', autoApprove: true, exchange: { root: 'SPACE-HIVECAST-PRINT', routeKey: 'hivecast-print', publicNamespace: 'space.hivecast-print', nats: { url: 'wss://hivecast.example.test/nats-ws', }, credentials: { jwt: 'print.jwt.token', seed: 'SUFAKEPRINT', }, }, }, async (cloudUrl) => { const result = await runMxCli([ 'node', 'mx', 'login', '--hivecast', '--cloud', cloudUrl, '--home', matrixHome, '--print-url', ], { cwd: root }); assertExitCode(result, 0); assert.equal(result.logs.includes('Open this URL to approve connecting this device to HiveCast:'), true); assert.equal(result.logs.includes(`${cloudUrl}/_auth/pair/PAIR-PRINT`), true); assert.equal(result.logs.some((line) => line.includes('HiveCast device connected: SPACE-HIVECAST-PRINT')), true); }, ); }); });