Reworks how devices connect to an OpenChamber server, end to end. Pairing v2: - One-time pairing links/QR codes (openchamber://connect?v=2) carrying a set of transport candidates (LAN/tunnel/relay) and a single-use secret redeemed server-side; no tokens embedded in links - Add-a-device dialog written for first-time users: intent-based transport choice (Anywhere / Home network only / This computer only) with plain-language descriptions, transparent fallback checkboxes, server-authoritative LAN detection, high-res QR dialog - Private relay folded into pairing as a transport candidate with a demand-driven lifecycle (enables when a relay device is paired, disables when none remain) Multi-transport devices: - A saved device holds all its transports and one token; mobile re-probes on connect, resume, and network change and hot-switches LAN<->relay seamlessly (no re-pairing, no remount, session preserved) - Desktop can import relay pairing links, switch to relay hosts through the E2EE tunnel, and restore a relay default host after relaunch Device management: - Device list (web + desktop) shows live per-device connectivity with the active transport (Connected - Local network / Relay) and platform badges (iOS/Android/macOS/Windows/Linux) - One physical device = one record: stable per-install dedupe keys across pairing and password re-login; typed pairing label names the device, paired devices name the connection by the issuing server hostname - Trusted desktop-local client manages all devices (list, revoke, clear revoked); relay host reaps dead client sockets after 3 missed keepalives Android: - LAN transport unblocked (cleartext + mixed content, mirroring iOS ATS exceptions); resume re-probe retries through network flux and silently auto-reconnects from a disconnected state
154 lines
3.9 KiB
JavaScript
154 lines
3.9 KiB
JavaScript
export const createBootstrapRuntime = (dependencies) => {
|
|
const {
|
|
createUiAuth,
|
|
registerServerStatusRoutes,
|
|
registerCommonRequestMiddleware,
|
|
registerAuthAndAccessRoutes,
|
|
registerTtsRoutes,
|
|
registerNotificationRoutes,
|
|
registerOpenChamberRoutes,
|
|
express,
|
|
} = dependencies;
|
|
|
|
const setupBaseRoutes = (app, options) => {
|
|
const {
|
|
process,
|
|
openchamberVersion,
|
|
runtimeName,
|
|
serverStartedAt,
|
|
gracefulShutdown,
|
|
getHealthSnapshot,
|
|
verboseRequestLogs,
|
|
uiPassword,
|
|
tunnelAuthController,
|
|
remoteClientAuthRuntime,
|
|
clientPairingRuntime,
|
|
getRelayPairingCandidate,
|
|
reconcileRelay,
|
|
getPairingTransports,
|
|
getServerLabel,
|
|
readSettingsFromDiskMigrated,
|
|
normalizeTunnelSessionTtlMs,
|
|
sayTTSCapability,
|
|
ensurePushInitialized,
|
|
ensureGlobalWatcherStarted,
|
|
getOrCreateVapidKeys,
|
|
getUiSessionTokenFromRequest,
|
|
writeSettingsToDisk,
|
|
addOrUpdatePushSubscription,
|
|
removePushSubscription,
|
|
addOrUpdateApnsToken,
|
|
removeApnsToken,
|
|
updateUiVisibility,
|
|
clearPendingPushBadge,
|
|
isUiVisible,
|
|
getUiNotificationClients,
|
|
writeSseEvent,
|
|
sessionRuntime,
|
|
setPushInitialized,
|
|
fs,
|
|
os,
|
|
path,
|
|
server,
|
|
__dirname,
|
|
openchamberDataDir,
|
|
modelsDevApiUrl,
|
|
modelsMetadataCacheTtl,
|
|
fetchFreeZenModels,
|
|
getCachedZenModels,
|
|
setAutoAcceptSession,
|
|
} = options;
|
|
|
|
const uiAuthController = createUiAuth({
|
|
password: uiPassword,
|
|
readSettingsFromDiskMigrated,
|
|
clientAuthController: remoteClientAuthRuntime,
|
|
});
|
|
if (uiAuthController.enabled) {
|
|
console.log('UI password protection enabled for browser sessions');
|
|
}
|
|
|
|
registerServerStatusRoutes(app, {
|
|
express,
|
|
process,
|
|
openchamberVersion,
|
|
runtimeName,
|
|
serverStartedAt,
|
|
gracefulShutdown,
|
|
getHealthSnapshot,
|
|
tunnelAuthController,
|
|
uiAuthController,
|
|
});
|
|
|
|
registerCommonRequestMiddleware(app, { express, verboseRequestLogs });
|
|
|
|
registerAuthAndAccessRoutes(app, {
|
|
express,
|
|
tunnelAuthController,
|
|
uiAuthController,
|
|
remoteClientAuthRuntime,
|
|
clientPairingRuntime,
|
|
getRelayPairingCandidate,
|
|
reconcileRelay,
|
|
getPairingTransports,
|
|
getServerLabel,
|
|
readSettingsFromDiskMigrated,
|
|
normalizeTunnelSessionTtlMs,
|
|
});
|
|
|
|
registerTtsRoutes(app, { sayTTSCapability });
|
|
|
|
registerNotificationRoutes(app, {
|
|
uiAuthController,
|
|
ensurePushInitialized,
|
|
ensureGlobalWatcherStarted,
|
|
getOrCreateVapidKeys,
|
|
getUiSessionTokenFromRequest,
|
|
readSettingsFromDiskMigrated,
|
|
writeSettingsToDisk,
|
|
addOrUpdatePushSubscription,
|
|
removePushSubscription,
|
|
addOrUpdateApnsToken,
|
|
removeApnsToken,
|
|
updateUiVisibility,
|
|
clearPendingPushBadge,
|
|
isUiVisible,
|
|
getUiNotificationClients,
|
|
writeSseEvent,
|
|
getSessionActivitySnapshot: sessionRuntime.getSessionActivitySnapshot,
|
|
getSessionStateSnapshot: sessionRuntime.getSessionStateSnapshot,
|
|
getSessionAttentionSnapshot: sessionRuntime.getSessionAttentionSnapshot,
|
|
getSessionState: sessionRuntime.getSessionState,
|
|
getSessionAttentionState: sessionRuntime.getSessionAttentionState,
|
|
markSessionViewed: sessionRuntime.markSessionViewed,
|
|
markSessionUnviewed: sessionRuntime.markSessionUnviewed,
|
|
markUserMessageSent: sessionRuntime.markUserMessageSent,
|
|
setPushInitialized,
|
|
setAutoAcceptSession,
|
|
});
|
|
|
|
registerOpenChamberRoutes(app, {
|
|
fs,
|
|
os,
|
|
path,
|
|
process,
|
|
server,
|
|
__dirname,
|
|
openchamberDataDir,
|
|
modelsDevApiUrl,
|
|
modelsMetadataCacheTtl,
|
|
readSettingsFromDiskMigrated,
|
|
fetchFreeZenModels,
|
|
getCachedZenModels,
|
|
});
|
|
|
|
return {
|
|
uiAuthController,
|
|
};
|
|
};
|
|
|
|
return {
|
|
setupBaseRoutes,
|
|
};
|
|
};
|