Fluxer Deep Linking Architecture: How It Handles Instance URL Switching

Fluxer implements deep linking through a three-layer Electron architecture that captures custom protocol URLs in the main process, exposes them to the renderer via a preload bridge, and validates instance switches through a dedicated IPC handler that reloads the window to the new origin.

Fluxer is an Electron-based desktop application that supports custom protocol deep linking to enable seamless navigation and instance switching. According to the fluxerapp/fluxer source code, the deep linking architecture coordinates between the main process, preload scripts, and renderer to handle both cold starts and warm handoffs. This system allows users to open specific Fluxer instances via APP_PROTOCOL:// links and dynamically switch between self-hosted deployments without restarting the application.

The deep linking flow begins in fluxer_desktop/src/main/DeepLinks.tsx, where the application registers itself as the default handler for a custom protocol.

Registering the Custom Protocol

The main process calls app.setAsDefaultProtocolClient during initialization to associate the operating system with the APP_PROTOCOL:// scheme.

// fluxer_desktop/src/main/DeepLinks.tsx
if (process.defaultApp) {
  app.setAsDefaultProtocolClient(APP_PROTOCOL, process.execPath, [process.argv[1]]);
} else {
  app.setAsDefaultProtocolClient(APP_PROTOCOL);
}

Capturing URLs on Cold Start

When Fluxer launches from a deep link, the URL appears in process.argv. The main process scans for arguments starting with the registered protocol and stores the match in initialDeepLink for later retrieval.

// fluxer_desktop/src/main/DeepLinks.tsx
const deepLinkArg = process.argv.find(arg => arg.startsWith(`${APP_PROTOCOL}://`));
if (deepLinkArg) initialDeepLink = deepLinkArg;

For links received while the app is running, the main process listens for the second-instance event (Windows/Linux) and the open-url event (macOS). Each captured URL is forwarded to the renderer via the deep-link IPC channel, or cached if the window is not yet ready.

// fluxer_desktop/src/main/DeepLinks.tsx
export function handleOpenUrl(url: string) {
  const mainWindow = getMainWindow();
  if (mainWindow && !mainWindow.isDestroyed()) {
    mainWindow.webContents.send('deep-link', url);
    showWindow();
  } else {
    initialDeepLink = url;
  }
}

Renderer-Side Subscription via Preload Bridge

The renderer cannot access Electron APIs directly; instead, fluxer_desktop/src/preload/index.tsx exposes controlled functions through contextBridge.

Exposing IPC Helpers

The preload script registers two critical methods: getInitialDeepLink retrieves the URL that launched the app, while onDeepLink subscribes to future deep link events.

// fluxer_desktop/src/preload/index.tsx
onDeepLink: (cb) => {
  const handler = (_e, url) => cb(url);
  ipcRenderer.on('deep-link', handler);
  return () => ipcRenderer.removeListener('deep-link', handler);
},
getInitialDeepLink: () => ipcRenderer.invoke('get-initial-deep-link'),

The renderer calls electron.getInitialDeepLink() once during startup to handle cold launches, then registers electron.onDeepLink(callback) to react to links received while the app is already open.

Instance URL Switching Architecture

When a deep link points to a different Fluxer deployment, the application must validate the target and migrate the session. This logic resides in fluxer_desktop/src/main/IpcHandlers.tsx.

Validating Target Instances

The switch-instance-url handler first normalizes the supplied URL to a fully-qualified origin, then fetches /.well-known/fluxer to verify the presence of required endpoints.api and endpoints.gateway fields.

// fluxer_desktop/src/main/IpcHandlers.tsx
ipcMain.handle('switch-instance-url', async (_ev, opts) => {
  const instanceOrigin = normalizeInstanceOrigin(opts.instanceUrl);
  await assertValidFluxerInstance(instanceOrigin);
  // ... proceed with switch
});

Executing the Switch

After validation, the handler updates the custom app URL via setCustomAppUrl(instanceOrigin) and reloads the main window to the new origin. If loading fails, the custom URL is cleared and the error propagates to the renderer.

// fluxer_desktop/src/main/IpcHandlers.tsx
const mainWindow = getMainWindow();
pendingDesktopHandoffCode = opts.desktopHandoffCode ?? null;
setCustomAppUrl(instanceOrigin);
await mainWindow.loadURL(instanceOrigin);

Summary

  • Protocol Registration: fluxer_desktop/src/main/DeepLinks.tsx registers APP_PROTOCOL:// and captures initial URLs from process.argv.
  • IPC Bridge: fluxer_desktop/src/preload/index.tsx exposes getInitialDeepLink and onDeepLink to safely transport URLs from main to renderer.
  • Instance Validation: The switch-instance-url handler verifies new deployments via /.well-known/fluxer before accepting the switch.
  • Dynamic Reload: Validated switches trigger mainWindow.loadURL() to migrate the user to the new instance without manual restart.

Frequently Asked Questions

When a second instance is spawned or the OS emits an open-url event, the main process forwards the URL to the existing window via mainWindow.webContents.send('deep-link', url). The renderer receives this through the onDeepLink callback registered in the preload bridge.

The assertValidFluxerInstance function fetches the well-known endpoint and validates the JSON structure. If the origin lacks the required endpoints.api or endpoints.gateway fields, the promise rejects before loadURL is called, preventing navigation to non-Fluxer URLs.

No. The renderer must invoke electron.getInitialDeepLink(), which returns a promise that resolves to the cached initialDeepLink value stored during main process startup. This isolation prevents untrusted code from accessing process arguments directly.

Where is the instance switching logic implemented?

The switch-instance-url IPC channel is defined in fluxer_desktop/src/main/IpcHandlers.tsx. It coordinates URL normalization, instance discovery, and window reloading, ensuring the application only switches to verified Fluxer deployments.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →