# 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<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:

```text
~/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.
