How Fluxer Handles the Custom App URL Feature for Self-Hosted Instances
Fluxer stores self-hosted URLs in a local settings.json file, validates them as trusted origins, and reloads the Electron main window to point at the custom instance while preserving WebAuthn and media permissions.
Fluxer is an open-source Electron-based desktop application that can connect to self-hosted web instances instead of its default public URLs. Understanding how the custom app URL feature for self-hosted instances works requires examining the configuration persistence layer, the trust validation system, and the IPC handlers that coordinate between the renderer and main processes.
Storing the Custom URL in settings.json
When the desktop application starts, it attempts to load a per-user configuration file located in the application's data directory. According to the fluxerapp/fluxer source code, the DesktopConfig.tsx module manages this persistence layer.
The loadDesktopConfig() function reads from settings.json located at userDataPath:
// fluxer_desktop/src/common/DesktopConfig.tsx
configPath = path.join(userDataPath, CONFIG_FILE_NAME);
if (fs.existsSync(configPath)) {
const data = fs.readFileSync(configPath, 'utf-8');
config = JSON.parse(data) as DesktopConfig;
}
The JSON structure supports an optional app_url key that overrides the default production endpoints:
{
"app_url": "https://my.selfhosted.instance"
}
If this file is absent or the key is undefined, Fluxer falls back to the official stable or canary URLs defined in Constants.tsx.
Resolving the Effective App URL at Runtime
The getAppUrl() function in DesktopConfig.tsx determines which URL the Electron window should actually load. It implements a simple priority check:
// fluxer_desktop/src/common/DesktopConfig.tsx
export function getAppUrl(): string {
if (config.app_url) {
return config.app_url; // <- custom self‑hosted URL
}
return BUILD_CHANNEL === 'canary' ? CANARY_APP_URL : STABLE_APP_URL;
}
For trust-validation purposes, getCustomAppUrl() returns the stored value (or null) without falling back to defaults. This separation ensures that the security logic can distinguish between user-defined instances and official origins.
Validating the Custom URL as a Trusted Origin
Security in Fluxer relies on an origin allow-list. The isTrustedOrigin() function in Window.tsx extends this trust to self-hosted instances by comparing navigation targets against the stored custom URL:
// fluxer_desktop/src/main/Window.tsx
function isTrustedOrigin(url?: string): boolean {
const origin = getOrigin(url);
if (!origin) return false;
if (trustedWebOrigins.has(origin)) return true;
const customUrl = getCustomAppUrl();
if (customUrl) {
try {
return new URL(customUrl).origin === origin;
} catch {
return false;
}
}
return false;
}
This mechanism ensures that permissions such as WebAuthn, media access, and notifications function identically for self-hosted instances and official Fluxer domains.
Loading the Custom URL in the Main Window
During window creation, the Electron BrowserWindow loads the URL returned by getAppUrl(). The createWindow() function in Window.tsx orchestrates this:
// fluxer_desktop/src/main/Window.tsx
const appUrl = getAppUrl(); // <-- respects custom URL
mainWindow.loadURL(appUrl).catch(error => {
logger.error('Failed to load app URL:', error);
});
All subsequent navigation events—including will-navigate and setWindowOpenHandler—consult isTrustedOrigin() to determine whether to allow in-app navigation or escalate the request to the system's default browser.
Switching Instances via IPC
The renderer process triggers URL changes through the switch-instance-url IPC channel. The handler in IpcHandlers.tsx validates the new origin, persists it, and reloads the window:
// fluxer_desktop/src/main/IpcHandlers.tsx
ipcMain.handle('switch-instance-url', async (_event, options) => {
const instanceOrigin = normalizeInstanceOrigin(options.instanceUrl);
await assertValidFluxerInstance(instanceOrigin);
setCustomAppUrl(instanceOrigin); // store new custom URL
await mainWindow.loadURL(instanceOrigin); // reload the window
});
If the new URL fails to load, the handler clears the stored configuration (setCustomAppUrl(null)) to revert to the default instance. Every call to setCustomAppUrl() invokes saveDesktopConfig(), which atomically writes the updated JSON to disk:
// fluxer_desktop/src/common/DesktopConfig.tsx
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
This guarantees that the custom instance setting survives application restarts.
Practical Implementation Examples
Prompting for a Self-Hosted URL (Renderer Process)
To initiate a switch from the UI layer, invoke the IPC channel with a validated HTTPS URL:
import { ipcRenderer } from 'electron';
async function switchToSelfHosted(url: string) {
if (!/^https?:\/\//.test(url)) {
throw new Error('URL must include protocol (https://)');
}
await ipcRenderer.invoke('switch-instance-url', {
instanceUrl: url,
desktopHandoffCode: null,
});
}
Resetting to the Official Instance
Passing an empty string to the same handler clears the custom configuration and reloads the default URL:
await ipcRenderer.invoke('switch-instance-url', {
instanceUrl: '',
desktopHandoffCode: null,
});
Checking Configuration in the Main Process
Main-process modules can inspect the current configuration using the DesktopConfig helpers:
import { getAppUrl, getCustomAppUrl } from '../common/DesktopConfig';
const currentUrl = getAppUrl(); // Returns custom or default
const customOnly = getCustomAppUrl(); // Returns custom URL or null
Summary
- Persistence: Custom URLs are stored in
settings.jsonviaDesktopConfig.tsxand survive application restarts. - Resolution:
getAppUrl()selects the custom URL over defaults, whilegetCustomAppUrl()enables trust checks. - Security:
isTrustedOrigin()inWindow.tsxtreats self-hosted origins as trusted, enabling WebAuthn and media permissions. - IPC: The
switch-instance-urlchannel inIpcHandlers.tsxhandles runtime switching with validation and rollback on failure. - Core files:
DesktopConfig.tsx,Window.tsx, andIpcHandlers.tsximplement the complete self-hosted instance workflow.
Frequently Asked Questions
Where does Fluxer store the custom self-hosted URL?
Fluxer writes the custom URL to a settings.json file located in the user's application data directory (determined by userDataPath). The DesktopConfig.tsx module handles all reads and writes to this file using standard Node.js fs operations.
How does Fluxer ensure my self-hosted instance is secure?
The isTrustedOrigin() function in Window.tsx validates that navigation targets match either the official Fluxer domains or the exact origin stored in your custom configuration. This prevents phishing attempts while granting your instance the same permissions (WebAuthn, camera, microphone) as the official sites.
Can the custom URL be changed without restarting the application?
Yes. The renderer process can invoke the switch-instance-url IPC channel at any time. The main process validates the new URL, updates settings.json via setCustomAppUrl(), and immediately reloads the main window using mainWindow.loadURL() without requiring an app restart.
What happens if the self-hosted instance becomes unreachable?
If mainWindow.loadURL() fails after switching, the switch-instance-url handler automatically clears the custom configuration by calling setCustomAppUrl(null). This reverts the application to the default stable or canary URL on the next load cycle.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →