# 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 ```ts 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; restartEngine(): Promise; pickFolder(): Promise; openLogs(): Promise; exportDiagnostics(): Promise; checkForUpdates(): Promise<{ supported: boolean; message?: string }>; openSocket(url: string): Promise; sendSocket(id: string, data: string): Promise; closeSocket(id: string): Promise; 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 the platform app data directory. On macOS this is: ```text ~/Library/Application Support/nanobot/ ``` The app bundle is replaceable. User data is not stored in the bundle.