---
title: Config
url: "https://native-sdk.dev/docs/app-zon"
docs_index: /llms.txt
lastUpdated: 2026-10-16
---

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

The `app.json` manifest declares app metadata, permissions, security rules, window layout, and packaging inputs. It is read by the CLI and tooling at build, package, and validation time. New projects use JSON so TypeScript developers get familiar syntax, completion, and inline validation through [`$schema`](https://schema.native-sdk.dev/app/v1.json). Existing `app.zon` manifests remain fully supported and expose the same fields—there is no reduced JSON feature set. The versioned URL stays compatible for the lifetime of the v1 manifest contract; `/app.json` is the current-version alias.

## Example: native-rendered app

The manifest `native init` generates — identity, one shell window with a GPU surface view, and the minimal permission set:

```json title="app.json"
{
  "$schema": "https://schema.native-sdk.dev/app/v1.json",
  "id": "dev.native_sdk.my-app",
  "name": "my-app",
  "display_name": "My App",
  "description": "A counter that lives in one native window.",
  "version": "0.1.0",
  "icons": ["assets/icon.png"],
  "platforms": ["macos"],
  "permissions": ["view", "command"],
  "capabilities": ["native_views", "gpu_surfaces"],
  "shell": {
    "windows": [{
      "label": "main",
      "title": "My App",
      "width": 480,
      "height": 320,
      "views": [{
        "label": "main-canvas",
        "kind": "gpu_surface",
        "fill": true,
        "role": "Counter canvas",
        "accessibility_label": "Counter",
        "gpu_backend": "metal",
        "gpu_pixel_format": "bgra8_unorm",
        "gpu_present_mode": "timer",
        "gpu_alpha_mode": "opaque",
        "gpu_color_space": "srgb",
        "gpu_vsync": true
      }]
    }]
  },
  "security": {
    "navigation": {
      "allowed_origins": ["zero://app", "zero://inline"],
      "external_links": { "action": "deny" }
    }
  },
  "web_engine": "system",
  "cef": { "dir": "third_party/cef/macos", "auto_install": false }
}
```

## Example: app with web content, menus, and shortcuts

A fuller manifest for an app that also [embeds web content](/docs/frontend) and declares commands, shortcuts, menus, and packaging metadata:

```zig title="app.zon"
.{
    .id = "dev.native_sdk",
    .name = "native-sdk",
    .display_name = "native-sdk",
    .version = "0.1.0",
    .icons = .{"assets/icon.png"},
    .platforms = .{ "macos" },
    .permissions = .{ "command", "view", "dialog", "window", "clipboard", "credentials" },
    .capabilities = .{ "webview", "js_bridge", "native_views", "gpu_surfaces", "menus", "shortcuts", "dialog", "clipboard", "credentials" },
    .bridge = .{
        .commands = .{
            .{ .name = "native.ping", .origins = .{ "zero://app" } },
            .{ .name = "native-sdk.window.create", .permissions = .{ "window" }, .origins = .{ "zero://app" } },
            .{ .name = "native-sdk.command.invoke", .permissions = .{ "command" }, .origins = .{ "zero://app" } },
            .{ .name = "native-sdk.view.list", .permissions = .{ "view" }, .origins = .{ "zero://app" } },
            .{ .name = "native-sdk.dialog.showMessage", .permissions = .{ "dialog" }, .origins = .{ "zero://app" } },
            .{ .name = "native-sdk.clipboard.readText", .permissions = .{ "clipboard" }, .origins = .{ "zero://app" } },
            .{ .name = "native-sdk.credentials.get", .permissions = .{ "credentials" }, .origins = .{ "zero://app" } },
        },
    },
    .security = .{
        .navigation = .{
            .allowed_origins = .{ "zero://app", "http://127.0.0.1:5173" },
            .external_links = .{ .action = "deny" },
        },
    },
    .web_engine = "system",
    .cef = .{ .dir = "third_party/cef/macos", .auto_install = false },
    .windows = .{
        .{ .label = "main", .title = "native-sdk", .width = 720, .height = 480, .restore_state = true },
    },
    .commands = .{
        .{ .id = "command.palette", .title = "Command Palette" },
        .{ .id = "app.reload", .title = "Reload" },
    },
    .shortcuts = .{
        .{ .id = "command.palette", .key = "p", .modifiers = .{ "primary", "shift" } },
    },
    .menus = .{
        .{
            .title = "View",
            .items = .{
                .{ .label = "Command Palette", .command = "command.palette", .key = "p", .modifiers = .{ "primary", "shift" } },
                .{ .separator = true },
                .{ .label = "Reload", .command = "app.reload", .key = "r", .modifiers = .{ "primary" } },
            },
        },
    },
    .file_associations = .{
        .{
            .name = "Markdown Document",
            .extensions = .{ "md", ".markdown" },
            .mime_types = .{ "text/markdown" },
            .icon = "assets/markdown.icns",
        },
    },
    .url_schemes = .{
        .{ .scheme = "native-sdk" },
    },
}
```

## Fields

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

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

  <tbody>
    <tr>
      <td>
        `id`
      </td>

      <td>
        Reverse-DNS bundle identifier (e.g.

        `com.example.myapp`

        )
      </td>
    </tr>

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

      <td>
        Short machine name
      </td>
    </tr>

    <tr>
      <td>
        `display_name`
      </td>

      <td>
        Human-readable app name — the application menu, Dock, app switcher, and About panel all use it, in dev runs and packaged bundles alike
      </td>
    </tr>

    <tr>
      <td>
        `description`
      </td>

      <td>
        Optional one-line description shown in the About panel (max 256 bytes, single line)
      </td>
    </tr>

    <tr>
      <td>
        `version`
      </td>

      <td>
        Canonical

        `X.Y.Z`

        version with no leading zeroes — also the version the About panel shows
      </td>
    </tr>

    <tr>
      <td>
        `icons`
      </td>

      <td>
        The app icon: one square

        `.png`

        (1:1, ideally 1024x1024) or

        `.svg`

        source that packaging turns into every platform's icon artifacts — the macOS

        `.icns`

        gets the platform's rounded-rect icon shape applied automatically. A prebuilt

        `.icns`

        /

        `.ico`

        entry ships untouched for its platform instead
      </td>
    </tr>

    <tr>
      <td>
        `platforms`
      </td>

      <td>
        Target platforms:

        `macos`

        ,

        `linux`

        ,

        `windows`

        ,

        `ios`

        ,

        `android`

        ,

        `web`
      </td>
    </tr>

    <tr>
      <td>
        `dmg`
      </td>

      <td>
        Optional macOS DMG presentation: volume name, PNG/JPEG/TIFF background (with automatic adjacent

        `@2x`

        discovery), usable Finder canvas and icon sizes, simple app/Applications positions, or an explicit positioned

        `items`

        list of the app, Applications alias, project files/directories, and absolute links. The zero-config defaults produce a complete drag-to-Applications layout.
      </td>
    </tr>

    <tr>
      <td>
        `permissions`
      </td>

      <td>
        Runtime permissions (see

        <a href="/docs/security">Security</a>

        ). Audio capture uses

        `microphone`

        and

        `system_audio`

        ; macOS packaging emits the matching microphone, audio-capture, and screen-capture usage descriptions only when declared
      </td>
    </tr>

    <tr>
      <td>
        `capabilities`
      </td>

      <td>
        Feature declarations (see

        <a href="/docs/security">Security</a>

        ).

        `"store"`

        links the engine-owned record store;

        `"sqlite"`

        links relational SQL effects. They share one capability-shed SQLite object but use separate databases; see

        <a href="/docs/record-store">Record Store</a>

        and

        <a href="/docs/sqlite">Relational SQLite</a>
      </td>
    </tr>

    <tr>
      <td>
        `dock_visible`
      </td>

      <td>
        Initial macOS Dock and app-switcher presence. Defaults to

        `true`

        . Set

        `false`

        for an Accessory/menu-bar app; the policy is applied before startup windows are created, so no Dock tile flashes. Accessory apps must declare the

        `"tray"`

        capability. Runtime

        `Cmd.setDockPresence`

        can still promote or demote the app later.
      </td>
    </tr>

    <tr>
      <td>
        `persist`
      </td>

      <td>
        Engine-owned Model snapshot config: monotonic

        `version`

        , optional

        `debounce_ms`

        , and the

        `restore`

        Msg routes (

        `ok`

        /

        `none`

        /

        `err`

        ). Requires

        `"persist"`

        in capabilities — see

        <a href="/docs/persistence">Model Persistence</a>
      </td>
    </tr>

    <tr>
      <td>
        `images`
      </td>

      <td>
        Optional registered-image budget:

        `.images = .{ .max_image_pixel_bytes = 8_388_608 }`

        . The default is 1 MiB and accepted values are 1–8 MiB. Encoded photos decode aspect-preservingly to fit; storage is lazy per used slot, but 16 fully used 8 MiB slots are a declared 128 MiB high-water. See

        <a href="/docs/dynamic-images">Dynamic Images</a>

        .
      </td>
    </tr>

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

      <td>
        Bridge command policies (see

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

        )
      </td>
    </tr>

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

      <td>
        Navigation and external link policies (see

        <a href="/docs/security">Security</a>

        )
      </td>
    </tr>

    <tr>
      <td>
        `web_engine`
      </td>

      <td>
        `system`

        or

        `chromium`

        ; Chromium is currently supported for macOS builds (see

        <a href="/docs/web-engines">Web Engines</a>

        )
      </td>
    </tr>

    <tr>
      <td>
        `webview_layer`
      </td>

      <td>
        `auto`

        (default),

        `include`

        , or

        `exclude`

        — whether the build ships the embedded web layer (see below)
      </td>
    </tr>

    <tr>
      <td>
        `theme`
      </td>

      <td>
        Built-in theme pack:

        `house`

        (default) or

        `geist`

        ; an unknown name is a build/check error (see

        <a href="/docs/theming">Theming</a>

        )
      </td>
    </tr>

    <tr>
      <td>
        `theme_accent`
      </td>

      <td>
        Optional

        `#rrggbb`

        accent layered over the pack — recolors the accent, its knockout ink, the focus ring, and the slider active range together; skipped under high contrast; a malformed value is a build/check error
      </td>
    </tr>

    <tr>
      <td>
        `cef`
      </td>

      <td>
        CEF runtime config for Chromium apps:

        `dir`

        and

        `auto_install`
      </td>
    </tr>

    <tr>
      <td>
        `assets`
      </td>

      <td>
        Launch-registered assets for TypeScript-core apps:

        `images`

        entries (

        `.{ .id = 1, .path = "assets/art/cover.jpg" }`

        ) are read once at launch and registered on the installing frame; the

        `id`

        is the

        `ImageId`

        markup avatar bindings reference
      </td>
    </tr>

    <tr>
      <td>
        `windows`
      </td>

      <td>
        Window definitions (see

        <a href="/docs/windows">Windows</a>

        )
      </td>
    </tr>

    <tr>
      <td>
        `shell`
      </td>

      <td>
        Native-first window and view tree declarations
      </td>
    </tr>

    <tr>
      <td>
        `commands`
      </td>

      <td>
        Shared command metadata for menus, shortcuts, native controls, tray items, and bridge calls
      </td>
    </tr>

    <tr>
      <td>
        `shortcuts`
      </td>

      <td>
        Keyboard shortcuts delivered as

        `shortcut`

        events (see

        <a href="/docs/keyboard-shortcuts">Keyboard Shortcuts</a>

        )
      </td>
    </tr>

    <tr>
      <td>
        `menus`
      </td>

      <td>
        Native menu declarations delivered through the command event path (see

        <a href="/docs/menus">Menus</a>

        )
      </td>
    </tr>

    <tr>
      <td>
        `file_associations`
      </td>

      <td>
        File type registration metadata for packaging
      </td>
    </tr>

    <tr>
      <td>
        `url_schemes`
      </td>

      <td>
        Custom URL scheme registration metadata for packaging
      </td>
    </tr>

    <tr>
      <td>
        `frontend`
      </td>

      <td>
        Frontend build/dev config (see

        <a href="/docs/frontend">Frontend Projects</a>

        )
      </td>
    </tr>
  </tbody>
</table>

## `webview_layer`

Under the default `auto`, the build infers whether to ship the embedded web layer from what the app declares: `"webview"` in `.capabilities`, a `.frontend` block, a `.shell` webview view, or a web engine resolved to Chromium (from `.web_engine` or the `-Dweb-engine`/`--web-engine` flags) each count as web intent, and an app with none of them builds native-only. `include` forces the layer into a build that declares nothing yet; `exclude` promises a native-only app. The `-Dweb-layer` build option overrides the manifest setting for one build.

A native-only build carries the platform consequences with it: on Windows the executable never references `WebView2Loader.dll` and no loader ships in the package; on Linux the host compiles and links without WebKitGTK entirely, so building needs no WebKitGTK development package and users need no `libwebkitgtk` installed at runtime.

`exclude` alongside any web declaration is a contradiction, refused with a diagnostic error at validate, build, and package time instead of shipping an app whose declared webviews cannot work:

```zig
.capabilities = .{"webview"},
.webview_layer = "exclude", // rejected: remove the web declarations or drop the exclude
```

## `shell`

The optional `shell` block declares native-first windows with explicit view trees. Existing `windows` entries remain the simple compatibility path; `shell.windows` is the richer contract for native chrome, native controls, WebViews, and GPU surfaces.

Tooling parses and validates the schema. Runtime code can return the same shape from `App.scene_fn`, materialize a parsed shell window with `runtime.createShellWindow(...)`, or attach a shell view list to an existing window with `runtime.createShellViews(...)`; platform hosts implement view kinds progressively, so unsupported native kinds fail at runtime with an explicit unsupported error until that backend grows support.

When an app uses both `windows` and `shell.windows`, labels must stay unique across both lists. Use `windows` for the simple compatibility path or `shell.windows` for native-first structure; do not define two window entries with the same label.

For a scene-first app — a `UiApp` passing its Zig scene (`shell_scene`) to the runner — the scene is authoritative at runtime: it re-applies size, title, and views when it loads. the manifest’s first `shell.windows` entry exists because the host creates the startup window before the scene loads, and create-time-only properties must come from the manifest: `titlebar` chrome, `min_width`/`min_height` floors, overlay presentation (`transparent`, `always_on_top`, `click_through`, and `activate_on_show`), and the show mode (canvas-first windows are created hidden and shown after the first frame presents). The numbers appearing in both places is by design — edit the scene for anything that can change after create (size, title, views), and the manifest for create-time presentation and floors.

```zig
.shell = .{
    .windows = .{
        .{
            .label = "main",
            .title = "Acme",
            .width = 1100,
            .height = 760,
            .views = .{
                .{ .label = "toolbar", .kind = "toolbar", .edge = "top", .height = 44, .role = "toolbar" },
                .{ .label = "body", .kind = "split", .fill = true, .axis = "row" },
                .{ .label = "sidebar", .kind = "sidebar", .parent = "body", .width = 280, .min_width = 220, .max_width = 360 },
                .{ .label = "sidebar-stack", .kind = "stack", .parent = "sidebar", .x = 16, .y = 16, .width = 220, .height = 120, .axis = "column" },
                .{ .label = "sidebar-live", .kind = "checkbox", .parent = "sidebar-stack", .accessibility_label = "Toggle live updates", .text = "Live updates" },
                .{ .label = "content", .kind = "webview", .parent = "body", .url = "zero://app/index.html", .fill = true },
                .{ .label = "canvas", .kind = "gpu_surface", .parent = "body", .width = 480, .gpu_backend = "metal", .gpu_pixel_format = "bgra8_unorm", .gpu_present_mode = "timer", .gpu_alpha_mode = "opaque", .gpu_color_space = "srgb", .gpu_vsync = true },
                .{ .label = "status", .kind = "statusbar", .edge = "bottom", .height = 24, .text = "Ready" },
            },
        },
    },
},
```

Each window takes a `label` plus optional `title`, `width`, `height`, `x`, `y`, `resizable`, `restore_state`, `restore_policy` (`clamp_to_visible_screen` or `center_on_primary`), `initially_hidden` (default false; create the native window ordered out until an explicit show/focus), `allows_fullscreen` (default true; false disables native fullscreen on macOS without disabling ordinary resizing), `min_width`/`min_height` (a content min-size floor the window itself enforces — macOS `contentMinSize`; the first shell window's declaration threads through the startup create like `titlebar`, negative values are a manifest error, 0 means no floor), and `titlebar` (`standard`, `hidden_inset`, `hidden_inset_tall` — the tall variant centers macOS's traffic lights in the 52pt unified band for toolbar-height headers — or `chromeless`, the fully-skinned opt-in that removes all OS chrome including the system buttons; only for apps that draw their own working window controls, see `examples/deck`). `restore_state` defaults to true and controls only whether the state store is consulted: a store hit restores the saved frame, an authored `x` or `y` is explicit placement, and a fresh window with neither is default placement. On macOS the default `clamp_to_visible_screen` policy keeps restored and explicit frames on their matching or nearest display, centers the main fresh window, and cascades fresh secondary windows; `center_on_primary` centers restored and fresh default frames on the primary display. `titlebar = "hidden_inset"` hides the titlebar and extends content under it (macOS keeps the traffic lights) — the first shell window's declaration threads through the STARTUP window create, so the main window's chrome is right from the first frame; the app's own header then takes over dragging and inset padding through the `window-drag` attribute and the `on_chrome` hook (see <a href="/docs/native-ui">Native UI</a>). Platforms without the concept keep standard chrome. The same fields are accepted on top-level `windows` entries. `dock_visible` is top-level app policy, not a window field: setting it false removes the Dock/app-switcher presence but does not hide a window; pair it with `initially_hidden = true` when the app should launch behind its status item.

Overlay presentation is also fixed at create time: `transparent` makes the top-level window alpha-capable, `always_on_top` selects its floating/topmost level, `click_through` passes pointer input to windows underneath, `activate_on_show = false` reveals it without activating the app or taking focus, and `allows_fullscreen = false` removes macOS fullscreen participation while keeping the window resizable. Canvas windows already use present-before-show, so these flags are applied while the window is hidden and its first alpha-correct frame becomes the first visible frame. `initially_hidden = true` is stronger: it suppresses that reveal until `Cmd.showWindow` or explicit focus. Pair `transparent = true` with a non-opaque `gpu_alpha_mode`; `UiApp.WindowDescriptor` makes that canvas-alpha choice and uses an alpha-zero clear automatically. See the <a href="/docs/windows#overlay-windows">overlay window recipe</a>. These fields are accepted on top-level `windows` and `shell.windows`. Runtime `WindowCreateOptions` exposes the same controls but spells the hidden mode `.show = .hidden`; `UiApp.WindowDescriptor` exposes the overlay controls but not the initially-hidden mode.

Windows also take `close_policy` (`quit`, the default — the close affordance really closes, behavior unchanged for every existing app — or `hide`, the menu-bar-app shape: close hides the window and the app keeps running behind its status item). Like `titlebar`, close handling is host window state fixed at create, and the first shell window's declaration threads through the startup create. `hide` is supported on macOS and Windows — on Windows it additionally requires the `"tray"` capability (the tray icon is the only re-show affordance there; a declaration without it is refused at build time with a diagnostic). Linux has no status item to bring a hidden window back, so the declaration is refused at build time with a diagnostic. See <a href="/docs/windows#close-policy">Windows</a> and the <a href="/docs/tray#the-menu-bar-app-lifecycle">tray lifecycle recipe</a>. The same `close_policy` field is accepted on top-level `windows` entries.

Supported `kind` values are `webview`, `toolbar`, `titlebar_accessory`, `sidebar`, `statusbar`, `split`, `stack`, `button`, `icon_button`, `list_item`, `checkbox`, `toggle`, `segmented_control`, `text_field`, `search_field`, `label`, `spacer`, `gpu_surface`, and `progress_indicator`.

Each view has a required `label` and `kind`. WebView views require `url`. Optional layout fields are `parent`, `edge`, `axis`, `x`, `y`, `width`, `height`, `min_width`, `min_height`, `max_width`, `max_height`, `fill`, and `layer`. Optional behavior and accessibility metadata are `visible`, `enabled`, `role`, `accessibility_label`, `text`, and `command`. `gpu_surface` views may also set `gpu_backend`, `gpu_pixel_format`, `gpu_present_mode`, `gpu_alpha_mode`, `gpu_color_space`, and `gpu_vsync`; those fields are rejected on non-GPU view kinds. The macOS system-WebView host presents with Metal, while the Windows host presents representable binary canvas packets with Direct2D/DirectWrite; frame events expose the concrete `metal` or `direct2d` backend. `gpu_backend` accepts `metal` or `software` as portable requests; omit it to select the default. Linux falls back to software presentation, and Windows uses software for unrepresentable commands, transparent layered windows, or when Direct2D is unavailable. The `axis` field accepts `row` or `column` on parent containers such as `toolbar`, `sidebar`, `split`, and `stack`; it defaults to `row`.

`createShellWindow` and `createShellViews` dock `edge` views against the remaining window content, let one or more top-level `fill` views use the final remaining rectangle, clamp resolved frames with any min/max size fields, and lay out parented controls such as toolbar buttons with small native defaults when explicit `x`, `y`, `width`, or `height` values are omitted. Parent containers flow omitted child positions horizontally with `axis = "row"` and vertically with `axis = "column"`. `split` containers use the same axis without inner spacing, so fixed-size children can sit beside a `fill` child. The runtime keeps the shell view slice as a layout binding and reapplies it when the window is resized, so pass data that lives for the lifetime of the window.

## `commands`

The optional `commands` list declares shared command metadata. The runtime still dispatches by command id, but this section gives menus, shortcuts, native controls, tray items, and bridge callers one manifest-level source of truth for user-facing labels and default state:

```zig
.commands = .{
    .{ .id = "app.refresh", .title = "Refresh" },
    .{ .id = "app.sidebar.toggle", .title = "Sidebar", .checked = true },
    .{ .id = "app.sync", .title = "Sync", .enabled = false },
},
```

`id` is required and must be unique. `title` defaults to an empty string. `enabled` defaults to `true`; `checked` defaults to `false`.

An app can define up to 256 commands. Command ids can be up to 128 bytes and titles can be up to 128 bytes.

Generated zero-config TypeScript and Zig-core runners load manifest commands into `RuntimeOptions.commands`; ejected runners use the same fallback. Native code can read the active catalog with `runtime.listCommands(...)`, and trusted WebView code can read it with `window.zero.commands.list()` when the built-in command bridge allows it. Use the catalog to keep menus, shortcuts, toolbar controls, tray items, and bridge callers aligned with the same command ids.

## `shortcuts`

The optional `shortcuts` list defines app-level keyboard shortcuts. Generated zero-config TypeScript and Zig-core runners load these automatically, as do ejected runners:

```zig
.shortcuts = .{
    .{ .id = "command.palette", .key = "p", .modifiers = .{ "primary", "shift" } },
    .{ .id = "reload", .key = "r", .modifiers = .{ "primary" } },
},
```

`id` is the event identifier. `key` accepts letters, digits, the unshifted punctuation keys `=`, `-`, `,`, `.`, `/`, `;`, `'`, `[`, `]`, `\`, and `` ` ``, or a named key. `modifiers` accepts `primary`, `command`, `control`, `option`, `alt`, and `shift`.

Single-character keys and text-entry keys (`enter`, `tab`, `space`, and `backspace`) require at least one modifier.

An app can define up to 64 shortcuts. Shortcut ids can be up to 64 bytes and keys can be up to 32 bytes.

Chromium builds are currently macOS-only; use the Linux system WebView backend when an app needs native shortcut events on Linux.

## `menus`

The optional `menus` list defines native app menus. Generated zero-config TypeScript and Zig-core runners load these automatically, as do ejected runners:

```zig
.menus = .{
    .{
        .title = "View",
        .items = .{
            .{ .label = "Refresh", .command = "app.refresh", .key = "r", .modifiers = .{ "primary" } },
            .{ .separator = true },
            .{ .label = "Sidebar", .command = "app.sidebar.toggle", .checked = true },
        },
    },
},
```

Each menu has a `title` and optional `items`. Command-backed items require `label` and `command`. Optional fields are `key`, `modifiers`, `separator`, `enabled`, and `checked`. `modifiers` accepts the same values as shortcuts.

An app can define up to 16 menus and 128 total menu items. Menu titles can be up to 64 bytes, item labels up to 128 bytes, command ids up to 128 bytes, and keys up to 32 bytes.

## File associations and URL schemes

The optional `file_associations` and `url_schemes` lists declare packaging metadata for document types and custom protocols:

```zig
.file_associations = .{
    .{
        .name = "Markdown Document",
        .role = "editor",
        .extensions = .{ "md", ".markdown" },
        .mime_types = .{ "text/markdown" },
        .icon = "assets/markdown.icns",
    },
},
.url_schemes = .{
    .{ .scheme = "acme-notes" },
},
```

File associations require a `name` and at least one `extensions` or `mime_types` entry. Extensions may include a leading dot, but packaging tools treat the extension as the same value either way. `icon` is optional and must be a relative path.

URL schemes require a lowercase custom `scheme`. Reserved schemes such as `http`, `https`, and `file` are rejected.

`role` defaults to `viewer` and accepts `viewer`, `editor`, `shell`, or `none`. Package generation emits this metadata into macOS `Info.plist` document/protocol declarations, Linux desktop and shared MIME metadata, and a Windows per-user registration script.

## `frontend.dev`

The optional `frontend.dev` block configures the managed dev server for `native dev` and `zig build dev`:

```zig
.frontend = .{
    .dist = "frontend/dist",
    .entry = "index.html",
    .spa_fallback = true,
    .dev = .{
        .url = "http://127.0.0.1:5173/",
        .command = .{ "npm", "--prefix", "frontend", "run", "dev" },
        .ready_path = "/",
        .timeout_ms = 30000,
    },
},
```

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

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

  <tbody>
    <tr>
      <td>
        `url`
      </td>

      <td>
        Dev server URL to load in the WebView during development
      </td>
    </tr>

    <tr>
      <td>
        `command`
      </td>

      <td>
        Command to start the dev server (spawned as a child process)
      </td>
    </tr>

    <tr>
      <td>
        `ready_path`
      </td>

      <td>
        HTTP path to poll until the dev server is ready (default

        `/`

        )
      </td>
    </tr>

    <tr>
      <td>
        `timeout_ms`
      </td>

      <td>
        Milliseconds to wait for the dev server before failing (default

        `30000`

        )
      </td>
    </tr>
  </tbody>
</table>

## Validation

```bash
native validate app.json
native doctor --manifest app.json --strict
```

---

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)