Files
nanobot/desktop/docs/host-contract.md
T

3.1 KiB

Native Host Contract

desktop is a native host shell around the shared WebUI build. The renderer must not import Electron directly. It receives a minimal bridge at window.nanobotHost.

Runtime API

type HostRuntimeInfo = {
  surface: "native";
  app_version: string;
  engine_status: "starting" | "ready" | "restarting" | "stopped" | "crashed";
  data_dir: string;
  logs_dir: string;
  config_path: string;
  workspace_path: string;
  python: string;
  engine_transport?: "unix_socket";
};

type HostSocketEvent =
  | { id: string; type: "open" }
  | { id: string; type: "message"; data: string }
  | { id: string; type: "error"; message: string }
  | { id: string; type: "close"; code?: number; reason?: string };

type NanobotHost = {
  getRuntimeInfo(): Promise<HostRuntimeInfo>;
  restartEngine(): Promise<void>;
  pickFolder(): Promise<string | null>;
  openLogs(): Promise<void>;
  exportDiagnostics(): Promise<string>;
  checkForUpdates(): Promise<{ supported: boolean; message?: string }>;
  openSocket(url: string): Promise<string>;
  sendSocket(id: string, data: string): Promise<void>;
  closeSocket(id: string): Promise<void>;
  onSocketEvent(listener: (event: HostSocketEvent) => void): () => void;
  onRuntimeStatus(listener: (status: HostRuntimeInfo["engine_status"]) => void): () => void;
};

First Run

The desktop host starts the private engine immediately. If the native data directory has no config.json, nanobot desktop-gateway creates one with defaults before serving the shared WebUI. Provider, model, credential, and login setup stay in WebUI settings instead of Electron-owned HTML.

Socket Bridge

The engine listens on a per-user Unix socket under the app data directory. /webui/bootstrap returns runtime_surface: "native" and a WebSocket URL in the nanobot-host://engine/... scheme. WebUI never opens that URL directly in the browser runtime; it hands the URL to window.nanobotHost.openSocket.

The native host then performs the WebSocket handshake against the Unix socket and forwards events over Electron IPC.

Host Security Boundary

The host bridge is intentionally narrower than a general Electron preload:

  • IPC calls are accepted only from renderer frames loaded from nanobot-app://app/....
  • openSocket accepts only nanobot-host://engine/... URLs.
  • External navigation is denied in the app window; safe web links are opened by the operating system.
  • Native WebUI responses carry a restrictive Content Security Policy and X-Content-Type-Options: nosniff.
  • The renderer runs with nodeIntegration: false, contextIsolation: true, sandbox: true, and webSecurity: true.

Security-sensitive tool behavior still belongs in nanobot core. The host protects the native app boundary; the engine protects file, network, and tool permissions.

Data Directory

The host stores config, workspace, sessions, logs, and transient socket files under Electron's platform app data directory. In development on macOS this is usually:

~/Library/Application Support/@nanobot/desktop/

Packaged builds use the packaged app name.

The app bundle is replaceable. User data is not stored in the bundle.