CodeDocs Vault

Desktop Runtime — Electron, the terminal "TUI", deep links

The desktop app (packages/desktop) is a hardened Electron shell around the same web editor. The owner asked specifically about an "integrated browser" — the honest finding is there is no embedded browser pane. What the README calls a "built-in TUI in the Desktop app" is a real node-pty terminal, and the agent-facing surfaces are the web preview plus the terminal where a harness CLI can run. This doc maps what's actually there.


1. Electron security posture

The main window is locked down (packages/desktop/src/main/index.ts:279-282):

webPreferences: { contextIsolation: true, nodeIntegration: false, sandbox: true }

All main↔renderer traffic goes through compile-time-typed IPC: a single createInvoker() factory constrains channel names and argument types to a RequestChannels contract (packages/desktop/src/shared/ipc-invoke.ts, ipc-channels.ts). A custom Biome lint rule, no-loosely-typed-webcontents-ipc, fails the build on any raw ipcRenderer.invoke() call (packages/desktop/tests/integration/no-loosely-typed-webcontents-ipc.test.ts:18), so loosely-typed IPC can't sneak back in. The renderer is a thin shell that loads the React bundle from packaged extra-resources (packages/desktop/src/renderer/index.html:11).

On the "integrated browser": searches for BrowserView / WebContentsView / <webview> come up empty. The only browser-adjacent code is embed-referer.ts:3-8, which rewrites the HTTP Referer header for embedded YouTube iframes inside the editor. The "browser" surface agents use is OpenKnowledge's own web preview (the :39847 UI), which agents open via the "Open with AI" handoff — not a separate Chromium pane.


2. The terminal — the real "TUI"

OK runs a full PTY-backed terminal inside the desktop app, split across two processes for isolation:

This is how a coding-agent CLI runs inside OpenKnowledge: it's a normal login shell, so you can launch claude, codex, or opencode in it. claudePreflight checks whether Claude is ready in the project and rewireClaudeMcp re-runs the MCP wiring on demand (main/index.ts ok:terminal:claude-assist handler near :2310, mcp-wiring.ts:1-30). So the terminal isn't just a convenience — it's a host for the agent that OK then talks to over MCP.


3. Desktop ↔ harness handoff


4. Secrets — native keyring

Credentials (the GitHub OAuth token, embedding API keys) live in the OS keychain via @napi-rs/keyring — macOS Keychain, Windows Credential Manager, Linux Secret Service (packages/desktop/src/utility/keyring-smoke.ts). Keyring access runs in the utility process, reached over correlation-ID'd debug IPC (main/debug-ipc.ts), never from the renderer. A packaging check (scripts/verify-keyring-in-packaged-dmg.mjs) verifies the native binding survives into the signed DMG. No homegrown crypto, no plaintext config secrets.


What's worth stealing (for a desktop meta-harness)

  1. Typed IPC enforced by lint. One createInvoker contract + a build-failing rule means every main↔renderer channel is documented and refactor-safe — exactly what you want when the desktop app is the trust boundary hosting multiple agents.
  2. PTY in a forked utility process with backpressure. Isolate each agent CLI's terminal so a flood or crash can't take down the renderer; pause/resume keeps memory bounded.
  3. A login-shell terminal is the universal agent host. Rather than embed each agent, give it a real terminal and talk to it over MCP. claudePreflight / rewireClaudeMcp show how to make that host self-healing.
  4. Native keyring, accessed only from privileged processes. Don't roll your own secret store; reach it over typed IPC, never from the renderer.
  5. Inbound deep links + outbound spawn give you a two-way bridge to external IDEs without copy-paste.