export const createTunnelRoutesRuntime = (dependencies) => { const { crypto, URL, tunnelService, tunnelProviderRegistry, tunnelAuthController, readSettingsFromDiskMigrated, readManagedRemoteTunnelConfigFromDisk, normalizeTunnelProvider, normalizeTunnelMode, normalizeOptionalPath, normalizeManagedRemoteTunnelHostname, normalizeTunnelBootstrapTtlMs, normalizeTunnelSessionTtlMs, isSupportedTunnelMode, upsertManagedRemoteTunnelToken, resolveManagedRemoteTunnelToken, TUNNEL_MODE_QUICK, TUNNEL_MODE_MANAGED_LOCAL, TUNNEL_MODE_MANAGED_REMOTE, TUNNEL_PROVIDER_CLOUDFLARE, TunnelServiceError, getActivePort, getRuntimeManagedRemoteTunnelHostname, setRuntimeManagedRemoteTunnelHostname, getRuntimeManagedRemoteTunnelToken, setRuntimeManagedRemoteTunnelToken, getActiveTunnelController, setActiveTunnelController, } = dependencies; const resolveActiveNormalizedTunnelMode = () => { const mode = tunnelService.resolveActiveMode(); if (mode === TUNNEL_MODE_MANAGED_LOCAL) { return TUNNEL_MODE_MANAGED_LOCAL; } if (mode === TUNNEL_MODE_MANAGED_REMOTE) { return TUNNEL_MODE_MANAGED_REMOTE; } return TUNNEL_MODE_QUICK; }; const resolveNormalizedTunnelHost = (publicUrl) => { if (typeof publicUrl !== 'string' || publicUrl.trim().length === 0) { return null; } try { return new URL(publicUrl).hostname.toLowerCase(); } catch { return null; } }; const resolvePreferredTunnelProvider = async (reqBody = null) => { if (typeof reqBody?.provider === 'string' && reqBody.provider.trim().length > 0) { return normalizeTunnelProvider(reqBody.provider); } const activeProvider = tunnelService.resolveActiveProvider(); if (activeProvider) { return normalizeTunnelProvider(activeProvider); } const settings = await readSettingsFromDiskMigrated(); return normalizeTunnelProvider(settings?.tunnelProvider); }; const startTunnelWithNormalizedRequest = async ({ provider, mode, intent, hostname, token, configPath, selectedPresetId, selectedPresetName, }) => { if (provider === TUNNEL_PROVIDER_CLOUDFLARE && mode === TUNNEL_MODE_MANAGED_REMOTE) { setRuntimeManagedRemoteTunnelHostname(hostname); setRuntimeManagedRemoteTunnelToken(token); if (token && hostname) { await upsertManagedRemoteTunnelToken({ id: selectedPresetId || hostname, name: selectedPresetName || hostname, hostname, token, }); } } const result = await tunnelService.start({ provider, mode, intent, configPath, token, hostname, }); console.log(`Tunnel active (${result.provider}): ${result.publicUrl}`); return { publicUrl: result.publicUrl, mode: result.activeMode, provider: result.provider, providerMetadata: result.providerMetadata, }; }; const createGenericModeChecks = ({ modeKey, requiredFields, doctorRequest, startupReady }) => { const checks = [ { id: 'startup_readiness', label: 'Provider startup readiness', status: startupReady ? 'pass' : 'fail', detail: startupReady ? 'Provider dependency checks passed.' : 'Resolve provider checks before starting tunnels.', }, ]; for (const field of requiredFields) { const value = doctorRequest?.[field]; const present = typeof value === 'string' ? value.trim().length > 0 : Boolean(value); checks.push({ id: `requirement_${field}`, label: `Required: ${field}`, status: present ? 'pass' : 'fail', detail: present ? `${field} is configured.` : `${field} is required for ${modeKey}.`, }); } const failures = checks.filter((entry) => entry.status === 'fail').length; const warnings = checks.filter((entry) => entry.status === 'warn').length; return { mode: modeKey, checks, summary: { ready: failures === 0, failures, warnings, }, ready: failures === 0, blockers: checks .filter((entry) => entry.status === 'fail' && entry.id !== 'startup_readiness') .map((entry) => entry.detail || entry.label || entry.id), }; }; const runTunnelDoctor = async ({ providerId, modeFilter, doctorRequest }) => { const provider = tunnelProviderRegistry.get(providerId); if (!provider) { throw new TunnelServiceError('provider_unsupported', `Unsupported tunnel provider: ${providerId}`); } const capabilities = provider.capabilities || {}; const modeKeys = Array.isArray(capabilities.modes) ? capabilities.modes.map((entry) => entry?.key).filter((key) => typeof key === 'string' && key.length > 0) : []; if (modeFilter && !modeKeys.includes(modeFilter)) { throw new TunnelServiceError('mode_unsupported', `Provider '${providerId}' does not support mode '${modeFilter}'`); } if (typeof provider.diagnose === 'function') { const diagnosed = await provider.diagnose({ ...doctorRequest, mode: modeFilter || doctorRequest?.mode, }, { capabilities, }); const providerChecks = Array.isArray(diagnosed?.providerChecks) ? diagnosed.providerChecks : []; const allModes = Array.isArray(diagnosed?.modes) ? diagnosed.modes : []; const modes = modeFilter ? allModes.filter((entry) => entry?.mode === modeFilter) : allModes; return { ok: true, provider: providerId, providerChecks, modes, }; } const availability = await tunnelService.checkAvailability(providerId); const dependencyAvailable = Boolean(availability?.available); const providerChecks = [{ id: 'dependency', label: 'Provider dependency', status: dependencyAvailable ? 'pass' : 'fail', detail: dependencyAvailable ? (availability?.version || 'available') : (availability?.message || 'Required provider dependency is unavailable.'), }]; const targetModes = (Array.isArray(capabilities.modes) ? capabilities.modes : []) .filter((entry) => !modeFilter || entry?.key === modeFilter); const modes = targetModes.map((entry) => createGenericModeChecks({ modeKey: entry.key, requiredFields: Array.isArray(entry?.requires) ? entry.requires : [], doctorRequest, startupReady: dependencyAvailable, })); return { ok: true, provider: providerId, providerChecks, modes, }; }; const registerRoutes = (app) => { app.get('/api/openchamber/tunnel/check', async (req, res) => { try { const requestedProvider = typeof req?.query?.provider === 'string' && req.query.provider.trim().length > 0 ? normalizeTunnelProvider(req.query.provider) : await resolvePreferredTunnelProvider(); const result = await tunnelService.checkAvailability(requestedProvider); res.json({ available: result.available, provider: requestedProvider, version: result.version || null, dependency: result.dependency || null, installCommand: result.installCommand || null, installUrl: result.installUrl || null, platform: result.platform || process.platform, message: result.message || null, }); } catch (error) { console.warn('Tunnel dependency check failed:', error); res.json({ available: false, provider: null, version: null, dependency: null, installCommand: null, installUrl: null, platform: process.platform, message: null }); } }); const handleTunnelDoctor = async (req, res) => { try { const params = req.query || {}; const body = req.body || {}; const providerId = typeof params.provider === 'string' && params.provider.trim().length > 0 ? normalizeTunnelProvider(params.provider) : await resolvePreferredTunnelProvider(); const modeFilter = typeof params.mode === 'string' && params.mode.trim().length > 0 ? params.mode.trim().toLowerCase() : null; const settings = await readSettingsFromDiskMigrated(); const selectedPresetId = typeof params.managedRemoteTunnelPresetId === 'string' ? params.managedRemoteTunnelPresetId.trim() : ''; const requestConfigPath = normalizeOptionalPath(params.configPath) ?? normalizeOptionalPath(settings?.managedLocalTunnelConfigPath); const requestManagedRemoteHostname = normalizeManagedRemoteTunnelHostname(params.managedRemoteTunnelHostname); const requestTunnelHostname = normalizeManagedRemoteTunnelHostname(params.tunnelHostname); const requestHostname = normalizeManagedRemoteTunnelHostname(params.hostname); const hostnameFromSettings = normalizeManagedRemoteTunnelHostname(settings?.managedRemoteTunnelHostname); const hostname = requestHostname || requestTunnelHostname || requestManagedRemoteHostname || hostnameFromSettings; const requestManagedRemoteToken = typeof body.managedRemoteTunnelToken === 'string' ? body.managedRemoteTunnelToken.trim() : ''; const requestTunnelToken = typeof body.tunnelToken === 'string' ? body.tunnelToken.trim() : ''; const requestToken = typeof body.token === 'string' ? body.token.trim() : ''; const requestTokenProvided = body.managedRemoteTunnelTokenProvided === true || body.tunnelTokenProvided === true || body.tokenProvided === true; const requestHostnameProvided = body.managedRemoteTunnelHostnameProvided === true || body.tunnelHostnameProvided === true || body.hostnameProvided === true; const storedManagedRemoteToken = typeof settings?.managedRemoteTunnelToken === 'string' ? settings.managedRemoteTunnelToken.trim() : ''; const managedRemoteTunnelConfig = await readManagedRemoteTunnelConfigFromDisk(); const serverHasSavedManagedRemoteProfile = managedRemoteTunnelConfig.tunnels.some((entry) => { const savedHostname = normalizeManagedRemoteTunnelHostname(entry?.hostname); const savedToken = typeof entry?.token === 'string' ? entry.token.trim() : ''; return Boolean(savedHostname && savedToken); }); const cliHasSavedManagedRemoteProfile = params.hasSavedManagedRemoteProfile === '1'; const hasSavedManagedRemoteProfile = serverHasSavedManagedRemoteProfile || cliHasSavedManagedRemoteProfile; const configManagedRemoteToken = providerId === TUNNEL_PROVIDER_CLOUDFLARE ? await resolveManagedRemoteTunnelToken({ presetId: selectedPresetId, hostname }) : ''; const runtimeHostname = getRuntimeManagedRemoteTunnelHostname(); const runtimeToken = getRuntimeManagedRemoteTunnelToken(); const token = requestToken || requestTunnelToken || requestManagedRemoteToken || ((runtimeHostname && hostname && runtimeHostname === hostname) ? runtimeToken : '') || configManagedRemoteToken || storedManagedRemoteToken; const doctorRequest = { mode: modeFilter, hostname, token, tokenProvided: requestTokenProvided, hostnameProvided: requestHostnameProvided, configPath: requestConfigPath, hasSavedManagedRemoteProfile, }; const result = await runTunnelDoctor({ providerId, modeFilter, doctorRequest, }); return res.json(result); } catch (error) { if (error instanceof TunnelServiceError) { return res.status(400).json({ ok: false, error: error.message, code: error.code }); } console.warn('Tunnel doctor failed:', error); return res.status(500).json({ ok: false, error: 'Failed to run tunnel doctor' }); } }; app.post('/api/openchamber/tunnel/doctor', handleTunnelDoctor); app.get('/api/openchamber/tunnel/doctor', handleTunnelDoctor); app.get('/api/openchamber/tunnel/providers', (_req, res) => { const providers = tunnelProviderRegistry.listCapabilities(); return res.json({ providers }); }); app.get('/api/openchamber/tunnel/status', async (_req, res) => { try { const settings = await readSettingsFromDiskMigrated(); const normalizedMode = normalizeTunnelMode(settings?.tunnelMode); const managedRemoteHostname = normalizeManagedRemoteTunnelHostname(settings?.managedRemoteTunnelHostname); const managedRemoteTunnelConfig = await readManagedRemoteTunnelConfigFromDisk(); const managedRemoteTunnelPresetSummaries = managedRemoteTunnelConfig.tunnels.map((entry) => ({ id: entry.id, name: entry.name, hostname: entry.hostname, })); const hasStoredManagedRemoteToken = typeof settings?.managedRemoteTunnelToken === 'string' && settings.managedRemoteTunnelToken.trim().length > 0; const hasManagedRemoteTunnelToken = getRuntimeManagedRemoteTunnelToken().length > 0 || managedRemoteTunnelConfig.tunnels.length > 0 || hasStoredManagedRemoteToken; const bootstrapTtlMs = settings?.tunnelBootstrapTtlMs === null ? null : normalizeTunnelBootstrapTtlMs(settings?.tunnelBootstrapTtlMs); const sessionTtlMs = normalizeTunnelSessionTtlMs(settings?.tunnelSessionTtlMs); const activeSessions = tunnelAuthController.listTunnelSessions(); const activeProvider = tunnelService.resolveActiveProvider(); const provider = activeProvider || normalizeTunnelProvider(settings?.tunnelProvider); const publicUrl = tunnelService.getPublicUrl(); if (!publicUrl) { return res.json({ active: false, url: null, mode: normalizedMode, provider, providerMetadata: null, hasManagedRemoteTunnelToken, managedRemoteTunnelHostname: managedRemoteHostname || null, managedRemoteTunnelPresets: managedRemoteTunnelPresetSummaries, managedRemoteTunnelTokenPresetIds: managedRemoteTunnelConfig.tunnels.map((entry) => entry.id), hasBootstrapToken: false, bootstrapExpiresAt: null, policy: 'tunnel-gated', activeTunnelMode: tunnelAuthController.getActiveTunnelMode() || null, activeSessions, localPort: getActivePort(), ttlConfig: { bootstrapTtlMs, sessionTtlMs, }, }); } const activeNormalizedMode = resolveActiveNormalizedTunnelMode(); const activeTunnelId = tunnelAuthController.getActiveTunnelId(); const activeTunnelHost = tunnelAuthController.getActiveTunnelHost(); const resolvedTunnelHost = resolveNormalizedTunnelHost(publicUrl); const activeTunnelMode = tunnelAuthController.getActiveTunnelMode(); const needsActiveTunnelSync = !activeTunnelId || !activeTunnelHost || !resolvedTunnelHost || activeTunnelHost !== resolvedTunnelHost || activeTunnelMode !== activeNormalizedMode; if (needsActiveTunnelSync) { tunnelAuthController.setActiveTunnel({ tunnelId: activeTunnelId || crypto.randomUUID(), publicUrl, mode: activeNormalizedMode, }); } const bootstrapStatus = tunnelAuthController.getBootstrapStatus(); const providerMetadata = tunnelService.getProviderMetadata(); return res.json({ active: true, url: publicUrl, mode: activeNormalizedMode, provider, providerMetadata, hasManagedRemoteTunnelToken, managedRemoteTunnelHostname: managedRemoteHostname || null, managedRemoteTunnelPresets: managedRemoteTunnelPresetSummaries, managedRemoteTunnelTokenPresetIds: managedRemoteTunnelConfig.tunnels.map((entry) => entry.id), hasBootstrapToken: bootstrapStatus.hasBootstrapToken, bootstrapExpiresAt: bootstrapStatus.bootstrapExpiresAt, policy: 'tunnel-gated', activeTunnelMode: activeNormalizedMode, activeSessions: tunnelAuthController.listTunnelSessions(), localPort: getActivePort(), ttlConfig: { bootstrapTtlMs, sessionTtlMs, }, }); } catch (error) { return res.status(500).json({ error: 'Failed to get tunnel status' }); } }); app.put('/api/openchamber/tunnel/managed-remote-token', async (req, res) => { try { const presetId = typeof req?.body?.presetId === 'string' ? req.body.presetId.trim() : ''; const presetName = typeof req?.body?.presetName === 'string' ? req.body.presetName.trim() : ''; const managedRemoteTunnelHostname = normalizeManagedRemoteTunnelHostname(req?.body?.managedRemoteTunnelHostname); const managedRemoteTunnelToken = typeof req?.body?.managedRemoteTunnelToken === 'string' ? req.body.managedRemoteTunnelToken.trim() : ''; if (!presetId || !presetName || !managedRemoteTunnelHostname || !managedRemoteTunnelToken) { return res.status(400).json({ ok: false, error: 'presetId, presetName, managedRemoteTunnelHostname and managedRemoteTunnelToken are required' }); } await upsertManagedRemoteTunnelToken({ id: presetId, name: presetName, hostname: managedRemoteTunnelHostname, token: managedRemoteTunnelToken, }); const managedRemoteTunnelConfig = await readManagedRemoteTunnelConfigFromDisk(); return res.json({ ok: true, managedRemoteTunnelTokenPresetIds: managedRemoteTunnelConfig.tunnels.map((entry) => entry.id) }); } catch (error) { return res.status(500).json({ ok: false, error: 'Failed to save managed remote tunnel token' }); } }); app.post('/api/openchamber/tunnel/start', async (_req, res) => { try { const settings = await readSettingsFromDiskMigrated(); if (typeof _req?.body?.provider === 'string' && _req.body.provider.trim().length > 0) { const rawProvider = _req.body.provider.trim().toLowerCase(); if (!tunnelProviderRegistry.get(rawProvider)) { return res.status(422).json({ ok: false, error: `Unsupported tunnel provider: ${rawProvider}`, code: 'provider_unsupported' }); } } const provider = normalizeTunnelProvider(_req?.body?.provider ?? settings?.tunnelProvider); const modeInput = _req?.body?.mode ?? settings?.tunnelMode; const intent = typeof _req?.body?.intent === 'string' ? _req.body.intent.trim().toLowerCase() : undefined; const mode = typeof modeInput === 'string' ? modeInput.trim().toLowerCase() : normalizeTunnelMode(modeInput); if (typeof _req?.body?.mode === 'string' && _req.body.mode.trim().length > 0 && !isSupportedTunnelMode(mode)) { return res.status(422).json({ ok: false, error: `Unsupported tunnel mode: ${mode}`, code: 'mode_unsupported' }); } const selectedPresetId = typeof _req?.body?.managedRemoteTunnelPresetId === 'string' ? _req.body.managedRemoteTunnelPresetId.trim() : ''; const selectedPresetName = typeof _req?.body?.managedRemoteTunnelPresetName === 'string' ? _req.body.managedRemoteTunnelPresetName.trim() : ''; const requestConfigPath = normalizeOptionalPath(_req?.body?.configPath) ?? normalizeOptionalPath(settings?.managedLocalTunnelConfigPath); const requestManagedRemoteHostname = normalizeManagedRemoteTunnelHostname(_req?.body?.managedRemoteTunnelHostname); const requestTunnelHostname = normalizeManagedRemoteTunnelHostname(_req?.body?.tunnelHostname); const requestHostname = normalizeManagedRemoteTunnelHostname(_req?.body?.hostname); const hostnameFromSettings = normalizeManagedRemoteTunnelHostname(settings?.managedRemoteTunnelHostname); const hostname = requestHostname || requestTunnelHostname || requestManagedRemoteHostname || hostnameFromSettings; const requestManagedRemoteToken = typeof _req?.body?.managedRemoteTunnelToken === 'string' ? _req.body.managedRemoteTunnelToken.trim() : ''; const requestTunnelToken = typeof _req?.body?.tunnelToken === 'string' ? _req.body.tunnelToken.trim() : ''; const requestToken = typeof _req?.body?.token === 'string' ? _req.body.token.trim() : ''; const storedManagedRemoteToken = typeof settings?.managedRemoteTunnelToken === 'string' ? settings.managedRemoteTunnelToken.trim() : ''; const configManagedRemoteToken = provider === TUNNEL_PROVIDER_CLOUDFLARE ? await resolveManagedRemoteTunnelToken({ presetId: selectedPresetId, hostname }) : ''; const runtimeHostname = getRuntimeManagedRemoteTunnelHostname(); const runtimeToken = getRuntimeManagedRemoteTunnelToken(); const token = requestToken || requestTunnelToken || requestManagedRemoteToken || ((runtimeHostname && hostname && runtimeHostname === hostname) ? runtimeToken : '') || configManagedRemoteToken || storedManagedRemoteToken; const requestConnectTtlMs = typeof _req?.body?.connectTtlMs === 'number' && Number.isFinite(_req.body.connectTtlMs) ? normalizeTunnelBootstrapTtlMs(_req.body.connectTtlMs) : undefined; const requestSessionTtlMs = typeof _req?.body?.sessionTtlMs === 'number' && Number.isFinite(_req.body.sessionTtlMs) ? normalizeTunnelSessionTtlMs(_req.body.sessionTtlMs) : undefined; const bootstrapTtlMs = requestConnectTtlMs ?? (settings?.tunnelBootstrapTtlMs === null ? null : normalizeTunnelBootstrapTtlMs(settings?.tunnelBootstrapTtlMs)); const sessionTtlMs = requestSessionTtlMs ?? normalizeTunnelSessionTtlMs(settings?.tunnelSessionTtlMs); const previousTunnelId = tunnelAuthController.getActiveTunnelId(); const previousMode = tunnelAuthController.getActiveTunnelMode(); const previousProvider = tunnelService.resolveActiveProvider(); const previousUrl = tunnelService.getPublicUrl(); const { publicUrl, provider: activeProvider, providerMetadata } = await startTunnelWithNormalizedRequest({ provider, mode, intent, hostname, token, configPath: requestConfigPath, selectedPresetId, selectedPresetName, }); const replacedTunnel = Boolean(previousTunnelId) && ( previousMode !== mode || previousProvider !== activeProvider || previousUrl !== publicUrl ); let revokedBootstrapCount = 0; let invalidatedSessionCount = 0; if (replacedTunnel && previousTunnelId) { const revoked = tunnelAuthController.revokeTunnelArtifacts(previousTunnelId); revokedBootstrapCount = revoked.revokedBootstrapCount; invalidatedSessionCount = revoked.invalidatedSessionCount; } tunnelAuthController.setActiveTunnel({ tunnelId: replacedTunnel || !previousTunnelId ? crypto.randomUUID() : previousTunnelId, publicUrl, mode, }); const bootstrapToken = tunnelAuthController.issueBootstrapToken({ ttlMs: bootstrapTtlMs }); const connectUrl = `${publicUrl.replace(/\/$/, '')}/connect?t=${encodeURIComponent(bootstrapToken.token)}`; const managedRemoteTunnelConfig = await readManagedRemoteTunnelConfigFromDisk(); const isCloudflareProvider = activeProvider === TUNNEL_PROVIDER_CLOUDFLARE; return res.json({ ok: true, url: publicUrl, mode, provider: activeProvider, providerMetadata, managedRemoteTunnelHostname: isCloudflareProvider ? (hostname || null) : null, managedRemoteTunnelTokenPresetIds: isCloudflareProvider ? managedRemoteTunnelConfig.tunnels.map((entry) => entry.id) : [], connectUrl, bootstrapExpiresAt: bootstrapToken.expiresAt, replacedTunnel, replaced: replacedTunnel ? { mode: previousMode, provider: previousProvider, url: previousUrl, } : null, revokedBootstrapCount, invalidatedSessionCount, policy: 'tunnel-gated', activeTunnelMode: mode, activeSessions: tunnelAuthController.listTunnelSessions(), localPort: getActivePort(), ttlConfig: { bootstrapTtlMs, sessionTtlMs, }, }); } catch (error) { console.error('Failed to start tunnel:', error); setActiveTunnelController(null); tunnelAuthController.clearActiveTunnel(); if (error instanceof TunnelServiceError) { const status = error.code === 'missing_dependency' ? 400 : (error.code === 'validation_error' || error.code === 'provider_unsupported' || error.code === 'mode_unsupported' ? 422 : 500); return res.status(status).json({ ok: false, error: error.message, code: error.code }); } return res.status(500).json({ ok: false, error: 'Failed to start tunnel', code: 'startup_failed' }); } }); app.post('/api/openchamber/tunnel/stop', (_req, res) => { let revokedBootstrapCount = 0; let invalidatedSessionCount = 0; const activeTunnelId = tunnelAuthController.getActiveTunnelId(); if (activeTunnelId) { const revoked = tunnelAuthController.revokeTunnelArtifacts(activeTunnelId); revokedBootstrapCount = revoked.revokedBootstrapCount; invalidatedSessionCount = revoked.invalidatedSessionCount; } if (getActiveTunnelController()) { console.log('Stopping active tunnel (user requested)...'); tunnelService.stop(); } tunnelAuthController.clearActiveTunnel(); res.json({ ok: true, revokedBootstrapCount, invalidatedSessionCount }); }); }; return { registerRoutes, startTunnelWithNormalizedRequest, }; };