---
title: "App & Runtime"
url: "https://native-sdk.dev/docs/runtime"
docs_index: /llms.txt
lastUpdated: 2026-10-16
---

> For an index of all documentation, see [/llms.txt](/llms.txt).

`App` and `Runtime` are the layer underneath [`UiApp`](/docs/app-model): the app describes what to run, the runtime owns the event loop, windows, native views, security checks, automation, tracing, and platform services. Most native-rendered apps never touch this layer directly — `UiApp` and the generated runner wire it for you. Reach for it when you need custom lifecycle callbacks, imperative window and view management, or a [WebView-based frontend](/docs/frontend).

## The App struct

<table>
  <thead>
    <tr>
      <th>
        Field
      </th>

      <th>
        Type
      </th>

      <th>
        Description
      </th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        `context`
      </td>

      <td>
        `*anyopaque`
      </td>

      <td>
        Pointer to your app state (required)
      </td>
    </tr>

    <tr>
      <td>
        `name`
      </td>

      <td>
        `[]const u8`
      </td>

      <td>
        App name used in traces and automation snapshots (required)
      </td>
    </tr>

    <tr>
      <td>
        `source`
      </td>

      <td>
        `WebViewSource`
      </td>

      <td>
        Initial WebView content for compatibility startup windows; defaults to empty HTML
      </td>
    </tr>

    <tr>
      <td>
        `source_fn`
      </td>

      <td>
        `?fn(*anyopaque) !WebViewSource`
      </td>

      <td>
        Dynamic source resolver (overrides

        `source`

        when set)
      </td>
    </tr>

    <tr>
      <td>
        `scene_fn`
      </td>

      <td>
        `?fn(*anyopaque) !ShellConfig`
      </td>

      <td>
        Declarative native window and view tree; when set, startup uses the scene instead of the compatibility window list
      </td>
    </tr>

    <tr>
      <td>
        `start_fn`
      </td>

      <td>
        `?fn(*anyopaque, *Runtime) !void`
      </td>

      <td>
        Called after the runtime starts, before startup scene or source loading
      </td>
    </tr>

    <tr>
      <td>
        `event_fn`
      </td>

      <td>
        `?fn(*anyopaque, *Runtime, Event) !void`
      </td>

      <td>
        Called on every runtime event (lifecycle + commands)
      </td>
    </tr>

    <tr>
      <td>
        `stop_fn`
      </td>

      <td>
        `?fn(*anyopaque, *Runtime) !void`
      </td>

      <td>
        Called before the runtime shuts down
      </td>
    </tr>
  </tbody>
</table>

All callback fields are optional. A minimal app only needs `context` and `name`; provide `scene_fn` when startup should build a declarative native shell, and `source` or `source_fn` only when the app has WebView content.

## Startup shape

With `scene_fn`, the runtime materializes the returned `ShellConfig` as native shell windows and views — this is the native-first path, and the shape `UiApp` uses through its `scene` option. The first scene window adopts the startup native window; additional scene windows are created through the window service.

Scene windows and views are kept as resize layout bindings, so return slices backed by static or app-owned storage that lives as long as the window.

```zig
const shell_views = [_]native_sdk.ShellView{
    .{ .label = "main-canvas", .kind = .gpu_surface, .fill = true, .gpu_backend = .metal },
    .{ .label = "status", .kind = .statusbar, .edge = .bottom, .height = 28, .text = "Ready" },
};
const shell_windows = [_]native_sdk.ShellWindow{.{
    .label = "main",
    .title = "Acme",
    .width = 1100,
    .height = 760,
    .views = &shell_views,
}};
const shell_scene: native_sdk.ShellConfig = .{ .windows = &shell_windows };

fn scene(context: *anyopaque) anyerror!native_sdk.ShellConfig {
    _ = context;
    return shell_scene;
}
```

Without `scene_fn`, the runtime uses the WebView compatibility path: it loads `source` or `source_fn` into the configured startup window list.

## WebViewSource

For apps that embed web content, three constructors specify what a WebView loads:

- **`.html(content)`** -- inline HTML string, served as `zero://inline`
- **`.url(address)`** -- load a remote or local URL
- **`.assets(options)`** -- serve a local file tree through a custom origin

The assets constructor takes a `WebViewAssetSource`:

```zig
.source = native_sdk.WebViewSource.assets(.{
    .root_path = "dist",
    .entry = "index.html",      // default
    .origin = "zero://app",     // default
    .spa_fallback = true,       // default
}),
```

<table>
  <thead>
    <tr>
      <th>
        Field
      </th>

      <th>
        Default
      </th>

      <th>
        Description
      </th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        `root_path`
      </td>

      <td>
        required
      </td>

      <td>
        Path to the directory containing frontend assets
      </td>
    </tr>

    <tr>
      <td>
        `entry`
      </td>

      <td>
        `"index.html"`
      </td>

      <td>
        HTML entry point within the root path
      </td>
    </tr>

    <tr>
      <td>
        `origin`
      </td>

      <td>
        `"zero://app"`
      </td>

      <td>
        Origin used for asset URLs
      </td>
    </tr>

    <tr>
      <td>
        `spa_fallback`
      </td>

      <td>
        `true`
      </td>

      <td>
        Serve entry for unknown routes (SPA mode)
      </td>
    </tr>
  </tbody>
</table>

See [Frontend Projects](/docs/frontend) for the dev-server/bundled-assets workflow.

## Lifecycle events

The runtime dispatches `LifecycleEvent` values through your `event_fn`:

- **`start`** -- the app runtime has started and startup scene or source loading is about to run
- **`activate`** -- the app became the active foreground app
- **`deactivate`** -- the app resigned active foreground status
- **`frame`** -- a frame has been requested (for animations or state updates)
- **`stop`** -- the app is shutting down

Native file drops dispatch `Event.files_dropped` to `event_fn`; `UiApp` maps them through `Options.on_drop`, and TypeScript cores export `dropMsg(drop: FileDropEvent)` (see [Native UI: File drops](/docs/native-ui#file-drops)). Apps with WebView content also receive `app:activate`, `app:deactivate`, and `drop:files` on each trusted `window.zero` instance:

```ts
window.zero.on("drop:files", (event) => {
  console.log(event.paths);
});
```

## The runner pattern

The generated `src/runner.zig` wires the runtime with platform services:

1. Selects the platform (macOS, Linux, Windows, or null for headless tests)
2. Sets up trace sinks (stdout + file) via `FanoutTraceSink`
3. Installs panic capture so crashes write `last-panic.txt`
4. Initializes window state persistence from `windows.zon`
5. Creates the `Runtime` with all options and calls `runtime.run(app)`

```zig
var runtime = native_sdk.Runtime.init(.{
    .platform = my_platform,
    .trace_sink = fanout.sink(),
    .security = .{
        .permissions = &app_permissions,
        .navigation = .{ .allowed_origins = &.{ "zero://app" } },
    },
    .window_state_store = state_store,
    .automation = if (build_options.automation) automation_server else null,
});
try runtime.run(my_app.app());
```

## RuntimeOptions

<table>
  <thead>
    <tr>
      <th>
        Field
      </th>

      <th>
        Type
      </th>

      <th>
        Default
      </th>

      <th>
        Description
      </th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        `platform`
      </td>

      <td>
        `Platform`
      </td>

      <td>
        required
      </td>

      <td>
        Platform abstraction (macOS, Linux, or NullPlatform)
      </td>
    </tr>

    <tr>
      <td>
        `trace_sink`
      </td>

      <td>
        `?trace.Sink`
      </td>

      <td>
        `null`
      </td>

      <td>
        Destination for structured trace records
      </td>
    </tr>

    <tr>
      <td>
        `log_path`
      </td>

      <td>
        `?[]const u8`
      </td>

      <td>
        `null`
      </td>

      <td>
        Path for persistent log file
      </td>
    </tr>

    <tr>
      <td>
        `extensions`
      </td>

      <td>
        `?ModuleRegistry`
      </td>

      <td>
        `null`
      </td>

      <td>
        Extension modules with lifecycle hooks
      </td>
    </tr>

    <tr>
      <td>
        `bridge`
      </td>

      <td>
        `?BridgeDispatcher`
      </td>

      <td>
        `null`
      </td>

      <td>
        App-defined bridge commands and handlers (WebView apps; see

        <a href="/docs/bridge">Bridge</a>

        )
      </td>
    </tr>

    <tr>
      <td>
        `builtin_bridge`
      </td>

      <td>
        `BridgePolicy`
      </td>

      <td>
        <code>.</code>
      </td>

      <td>
        Policy for built-in bridge commands (dialogs, windows)
      </td>
    </tr>

    <tr>
      <td>
        `security`
      </td>

      <td>
        `SecurityPolicy`
      </td>

      <td>
        <code>.</code>
      </td>

      <td>
        Navigation allowlist, external links, permissions
      </td>
    </tr>

    <tr>
      <td>
        `automation`
      </td>

      <td>
        `?automation.Server`
      </td>

      <td>
        `null`
      </td>

      <td>
        File-based automation server for testing
      </td>
    </tr>

    <tr>
      <td>
        `window_state_store`
      </td>

      <td>
        `?window_state.Store`
      </td>

      <td>
        `null`
      </td>

      <td>
        Persistent window geometry and state
      </td>
    </tr>

    <tr>
      <td>
        `js_window_api`
      </td>

      <td>
        `bool`
      </td>

      <td>
        `false`
      </td>

      <td>
        Expose built-in

        `window.zero`

        helper namespaces for trusted app chrome. Command helpers require origin and

        `command`

        checks, view helpers require origin and

        `view`

        checks, and window, WebView, and platform helpers require origin and

        `window`

        checks. Dialog, OS, clipboard, and credential helpers still require explicit

        `builtin_bridge`

        policy.
      </td>
    </tr>
  </tbody>
</table>

## Runtime methods

<table>
  <thead>
    <tr>
      <th>
        Method
      </th>

      <th>
        Description
      </th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        `init(options) Runtime`
      </td>

      <td>
        Create a runtime
      </td>
    </tr>

    <tr>
      <td>
        `run(app) !void`
      </td>

      <td>
        Enter the platform event loop
      </td>
    </tr>

    <tr>
      <td>
        `createWindow(options) !WindowInfo`
      </td>

      <td>
        Open a new window
      </td>
    </tr>

    <tr>
      <td>
        `listWindows() []WindowInfo`
      </td>

      <td>
        List open windows
      </td>
    </tr>

    <tr>
      <td>
        `focusWindow(id) !void`
      </td>

      <td>
        Bring a window to front
      </td>
    </tr>

    <tr>
      <td>
        `closeWindow(id) !void`
      </td>

      <td>
        Close a window
      </td>
    </tr>

    <tr>
      <td>
        `showWindow(id) !void`
      </td>

      <td>
        Un-hide and activate a window (the counterpart to a

        `close_policy = "hide"`

        hide; also restores a minimized window)
      </td>
    </tr>

    <tr>
      <td>
        `quitApp() !void`
      </td>

      <td>
        Quit gracefully through the same shutdown path a last-window close takes
      </td>
    </tr>

    <tr>
      <td>
        `invalidate()`
      </td>

      <td>
        Request a redraw
      </td>
    </tr>

    <tr>
      <td>
        `invalidateFor(reason, dirty_region)`
      </td>

      <td>
        Request a redraw with reason and optional dirty region
      </td>
    </tr>

    <tr>
      <td>
        `frameDiagnostics() FrameDiagnostics`
      </td>

      <td>
        Return stats from the last frame
      </td>
    </tr>

    <tr>
      <td>
        `setGpuSurfaceInputLatencyBudget(window_id, label, budget_ns)`
      </td>

      <td>
        Set the input-to-frame latency budget reported by GPU surface diagnostics
      </td>
    </tr>

    <tr>
      <td>
        `dispatchEvent(event)`
      </td>

      <td>
        Inject a synthetic event
      </td>
    </tr>

    <tr>
      <td>
        `dispatchPlatformEvent(app, event)`
      </td>

      <td>
        Forward a platform event
      </td>
    </tr>

    <tr>
      <td>
        `automationSnapshot()`
      </td>

      <td>
        Write state to automation directory
      </td>
    </tr>
  </tbody>
</table>

---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)