Files
openchamber/packages/electron
Bohdan Triapitsyn c703db2745 fix: stop forwarding client auth to OpenCode and harden home/session state
Packaged desktop showed no sessions in 1.12.4. Root cause: the sanitized
session-list proxy path added in #1538 forwarded the renderer's
"authorization" header (the OpenChamber UI client token) to the managed
OpenCode upstream alongside the managed "Authorization" credential.
OpenCode does not recognize UI client tokens, so every session-list
request answered 401 — only in the packaged app, because only its
renderer (openchamber-ui:// origin) attaches a bearer token; dev web and
dev Electron run same-origin without one. The legacy http-proxy path
overwrote the header correctly, which is why everything except session
lists kept working.

Proxy fix:
- proxy-headers: filter the client "authorization" header out of
  forwarded request headers; the OpenCode upstream must only ever see
  its own managed credentials. Covered by tests.

Desktop cwd:
- electron: launch the managed OpenCode CLI from the user home instead
  of app userData, matching upstream desktop behavior. userData-as-cwd
  made OpenCode treat the app-data folder as a separate empty workspace.

Home directory poisoning loop:
- directoryPersistence: stop replaying localStorage homeDirectory
  through synchronizeHomeDirectory on boot/auth resync. The persisted
  value is only a boot-time cache; replaying it re-wrote stale values
  (e.g. a project path) into desktop settings on every start, overriding
  the authoritative /api/fs/home resolution.
- persistence: never overwrite an injected window.__OPENCHAMBER_HOME__
  with a persisted value.
- useDirectoryStore: host switches happen in place (no reload), so
  re-resolve home from the new runtime's /api/fs/home on endpoint
  change instead of keeping the previous host's value.
- opencode client: only short-circuit to the injected desktop home when
  the active runtime is local; remote runtimes ask /api/fs/home.

Settings hygiene:
- persistSettings: log field names only — change payloads can carry
  credentials (UI password, client tokens, tunnel tokens) that must not
  reach the log file; drop step-by-step log chatter.
- validateProjectEntries: only stat project paths when the incoming
  update actually touches the projects list, not on every settings save.
- remove the write-only approvedDirectories setting everywhere and add
  a migration that strips the stale key from persisted settings.

Tests:
- usePluginsStore.test: register an own runtime-fetch module mock so the
  suite is independent of process-global mock.module leakage from other
  files, and restore globalThis.fetch after the suite.
- persistence.test: clean up the window global created for the suite.
2026-06-12 01:53:38 +03:00
..
2026-06-11 01:42:41 +03:00
2026-06-05 23:58:43 +03:00

OpenChamber Desktop

Electron desktop runtime for OpenChamber on macOS and Windows.

This package owns the native shell: windows, menus, deep links, native notifications, auto-updates, host switching, SSH connections, tunnel helpers, and packaged desktop builds. The web UI and OpenChamber server logic still live in packages/web and shared React UI lives in packages/ui.

How It Runs

Desktop starts the OpenChamber web server in the same Electron main process. There is no separate sidecar subprocess for the OpenChamber server.

main.mjs imports @openchamber/web/server/index.js and calls startWebUiServer(). The Electron window then loads the UI from the local server in development, or from packaged resources/web-dist assets in packaged builds.

The preload bridge exposes desktop-only APIs to the web UI through window.__OPENCHAMBER_DESKTOP__. Privileged commands are checked in main.mjs, not only in the UI.

Main Files

File Purpose
main.mjs Electron main process, app lifecycle, windows, menus, deep links, native IPC handlers, updates, local server startup
preload.mjs Safe bridge from the rendered UI to Electron IPC
ssh-manager.mjs SSH host import, connection lifecycle, tunnel/port forwarding helpers
scripts/electron-dev.mjs Desktop dev launcher with Vite HMR support
scripts/build-web-assets.mjs Builds packages/web and stages UI assets into resources/web-dist
scripts/bundle-main.mjs Bundles Electron main code into dist-bundle/main.mjs for packaging
scripts/rebuild-native.mjs Rebuilds native modules against the Electron runtime
scripts/package.mjs Runs electron-builder, with unsigned Windows builds when signing env is missing
resources/ Packaged web assets, icons, and macOS entitlements

Development

From the repo root:

bun install
bun run electron:dev

bun run electron:dev starts the web dev server with HMR, then launches Electron against packages/electron/main.mjs.

Useful variants:

bun run electron:dev:bundled
bun run type-check:electron
bun run lint:electron

electron:dev:bundled builds and uses packaged web assets instead of the HMR server. Use it when testing behavior closer to a packaged app.

Packaging

From the repo root:

bun run electron:build

That runs, in order:

  1. build:web-assets to build the web UI and copy it into packages/electron/resources/web-dist.
  2. bundle:main to create packages/electron/dist-bundle/main.mjs.
  3. rebuild:native to rebuild native modules for Electron.
  4. package.mjs to run electron-builder.

Build output goes to packages/electron/dist.

macOS builds produce dmg and zip artifacts. Windows builds produce an NSIS installer.

Platform Notes

macOS packaging needs Xcode/build tools for notarized builds and icon asset compilation.

Windows packaging needs NSIS support through electron-builder. If no Windows signing env is set, package.mjs disables code signing and builds an unsigned installer.

The package supports macOS and Windows desktop features. Some native discovery helpers are platform-specific. For example, app icon fetching and app filtering currently only work on macOS, while opening files in installed apps works on macOS and Windows.

Common Env Vars

Variable Use
OPENCHAMBER_ELECTRON_DEV=1 Marks the runtime as desktop development mode
OPENCHAMBER_ELECTRON_USE_BUNDLED_UI=1 Uses staged web assets instead of the HMR dev server
OPENCHAMBER_HMR_UI_PORT Preferred Vite UI port for desktop dev, default 5173
OPENCHAMBER_HMR_API_PORT Preferred API port for desktop dev, default 3901
OPENCHAMBER_RUNTIME=desktop Set by Electron before starting the web server
OPENCHAMBER_DESKTOP_NOTIFY=true Enables desktop notification flow in the web server
OPENCHAMBER_SKIP_API_COMPRESSION=true Defaulted by Desktop to reduce local CPU overhead
OPENCODE_HOST / OPENCODE_PORT / OPENCODE_SKIP_START Connect Desktop to an external OpenCode server instead of starting one locally

Native Features Owned Here

  • Floating Mini Chat windows.
  • Multiple native windows.
  • Native notifications.
  • One-click open/reveal/open-in-app actions.
  • Desktop host switcher and deep-link imports.
  • Local and remote instance handling.
  • SSH host import, connections, logs, and port forwarding.
  • Tunnel lifecycle integration through the web server runtime.
  • Auto-update checks, downloads, and restart/apply flow.

IPC Pattern

Renderer code should call the desktop bridge exposed by preload.mjs. Do not import Electron from shared UI code.

Add new native capabilities in this order:

  1. Add or update the preload.mjs bridge only if a new renderer-facing shape is needed.
  2. Add the real command handling in main.mjs under openchamber:invoke.
  3. Gate privileged commands in main process logic so remote pages cannot access local filesystem or shell capabilities.
  4. Keep shared UI runtime contracts in packages/ui and server/runtime APIs in packages/web when the behavior is not inherently native.

Logs And Data

Electron uses electron-log. In development, console logs are also visible in the terminal. In packaged apps, logs are written through the platform log path for the OpenChamber app name.

Development builds use a separate user data directory named OpenChamber Dev, so dev state does not overwrite normal packaged app state.

Things To Be Careful With

  • Keep desktop-specific code in this package. Do not move OpenCode feature backend logic into Electron.
  • Use hidden Windows process launches for background helpers. Avoid visible console flashes.
  • Keep @openchamber/web, bun-pty, node-pty, and native modules external in bundle-main.mjs; bundling them can break Electron startup.
  • Rebuild native modules after dependency or Electron version changes.
  • Test both HMR dev mode and bundled UI mode when changing startup, preload, routing, or packaged asset behavior.

Quick Checks

bun run type-check:electron
bun run lint:electron
bun run electron:dev:bundled

For full repo validation before shipping:

bun run type-check
bun run lint