Skip to content

Configuration ​

The CLI reads an app's configuration from myzig.zon at its root, or myzig.json (not both). Every field is optional; without a file, the defaults below apply. Zig itself never reads it: zig build builds the app from build.zig and build.zig.zon, and the configuration says how the CLI runs, packages and names it.

zig
.{
    .name = "My App",
    .identifier = "com.example.myapp",
    .version = "1.2.0",              // default: build.zig.zon's
    .copyright = "© 2026 Example Inc.",
    .icon = "resources/icon.png",    // default when it exists
    .executable = "my_app",          // the artifact build.zig installs
    .frontend = "frontend",          // the web frontend's sources
    .frontend_dist = "frontend",     // what build.zig embeds
    .dev_url = "http://localhost:5173",
    .dev_command = "bun run dev",
    .build_command = "bun run build",
    .out = "dist",
    .resources = .{"third_party/licenses"},
    .url_schemes = .{"myapp"},
    .file_associations = .{.{ .ext = .{"md"}, .name = "Markdown Document" }},
    .macos = .{ .minimum_system_version = "12.0", .signing_identity = "-" },
    .windows = .{ .installer = true },
    .linux = .{ .maintainer = "Jane <jane@example.com>", .command = "my-app" },
}

Paths are relative to the app's root, or absolute. A field the CLI doesn't know is an error that names it (myzig.zon has no field nmae), as is a value of the wrong kind; a ZON syntax error gives its line and column.

The app ​

fielddefault
namethe directory's namethe name people see: the bundle's, the installer's, the window class's
identifiercom.myzig.<name>a reverse DNS name (letters, digits, . and -), as com.example.myapp: the bundle identifier, the uninstall key, the Windows manifest's name
versionbuild.zig.zon's .version, else 0.1.0the version of packages and installers (Windows takes up to four numbers of it)
copyrightnonemacOS's NSHumanReadableCopyright, Windows' LegalCopyright
iconresources/icon.png when it existsa square PNG (1024 pixels is best): .icns, .ico, Linux's sizes are made from it
executablebuild.zig.zon's .name, else the name in snake casethe artifact build.zig installs (zig-out/bin/<executable>)

name, version and identifier cannot hold quotes or control characters. Keep identifier the same as AppOptions.id in the app's code, which names its single instance lock, URL handler and secrets (see The app).

Development and the frontend ​

fielddefault
frontendfrontend when the directory existsthe web frontend's sources, where generate and dev write myzig.js and myzig.d.ts
frontend_distfrontendthe built frontend that the app embeds; the CLI writes its myzig_assets.zig and passes it as -Dfrontend
dev_urlnonean http:// or https:// URL: myzig dev waits for it and passes it as MYZIG_DEV_URL
dev_commandnonethe dev server myzig dev runs first (bun run dev), in a shell, in the app's root
build_commandnonewhat myzig build runs first to build the frontend (bun run build)
outdistwhere myzig build puts packages
watchnoneaccepted; myzig dev already watches the whole app directory

A frontend without a build (plain HTML, as the web template's) has frontend alone: the app embeds its sources. A frontend built by Vite or another tool sets frontend_dist to its output, and dev_command, dev_url and build_command:

zig
.{
    .name = "Notes",
    .frontend = "frontend",
    .frontend_dist = "frontend/dist",
    .dev_command = "bun run --cwd frontend dev",
    .dev_url = "http://localhost:5173",
    .build_command = "bun run --cwd frontend build",
}

See Frontend for the page's side.

Integration ​

fielddefault
resourcesnonefiles and directories shipped beside the app, besides what resources/ holds
url_schemesnoneURL schemes the app opens (myapp for myapp://...): letters, digits, +, - and ., starting with a letter
file_associationsnonefile types the app opens

Everything in resources/ but the icon ships with the app too: see Distribution.

A file association is:

fielddefault
extrequiredextensions without the dot (.{ "md", "markdown" })
name<EXT> filethe type's name in the system
mime_typeapplication/x-<executable>-<ext>the MIME type; Linux packages define the type when it is myzig's
roleEditorEditor or Viewer (macOS)

The packages register these with the system: CFBundleURLTypes and CFBundleDocumentTypes in the macOS bundle, registry keys written by the Windows installer, MimeType= in the Linux desktop entry. The app also names them in its own options, AppOptions.url_schemes and AppOptions.file_extensions, so that launches with such arguments reach on_open_url and on_open_file, and can register them while it runs (myzig.system.registerUrlScheme, registerFileType; see System).

macos ​

fielddefault
minimum_system_version12.0LSMinimumSystemVersion
signing_identity-codesign's identity; - signs ad hoc (runs on the Mac that built it)
entitlementsnonean entitlements file for codesign
provisioning_profilenonea profile embedded as Contents/embedded.provisionprofile before signing; it must match the app's identifier, entitlements and signing identity, and cannot be used with ad-hoc signing. See provisioning profiles
dmg_titlethe namethe disk image's volume name
iconnonean .icns (made with iconutil from an .iconset) the bundle gets in place of the one rendered from icon, for an icon drawn per size; myzig dev shows the PNG
info_plistnonekeys added to Info.plist, replacing myzig's
zig
.macos = .{
    .signing_identity = "Developer ID Application: Example Inc. (TEAMID)",
    .entitlements = "resources/macos/app.entitlements",
    .info_plist = .{ .NSCameraUsageDescription = "Scans documents", .LSUIElement = true },
},

updates ​

Signed updates. public_key and one of github and url are required.

fielddefault
public_keythe contents of myzig-update.pub (myzig keygen)
githubthe public repository (owner/name) whose releases hold the updates; apps read the manifests of its latest release
tag_prefixvthe releases' tags: v1.2.0
urlan HTTPS directory serving the updates instead of GitHub
s3the bucket that serves url, which --upload puts the files into: bucket, prefix (its directory), region, endpoint (a compatible service's, as Cloudflare R2's), path_style
private_keythe path of myzig-update.key (may start with ~/); MYZIG_UPDATER_PRIVATE_KEY comes first
changelogCHANGELOG.md when it existsthe Markdown file whose ## <version> section is the release notes
zig
.updates = .{
    .public_key = "…",
    .url = "https://downloads.example.com/my-app",
    .s3 = .{ .bucket = "downloads", .prefix = "my-app", .endpoint = "https://<account>.r2.cloudflarestorage.com" },
},

windows ​

fielddefault
installertruewrite the NSIS installer's script, and make the installer when makensis is installed
uninstall_runnonearguments the uninstaller runs the app with, and waits for, before taking its files away: for the app to undo what it set up itself (a command on the PATH, File Explorer's menus)
uninstall_datanonefolders of the app's data, as NSIS paths ($APPDATA\\MyApp), the uninstaller offers to delete too, asking first; a silent uninstall keeps them
install_dir_namethe namethe folder in %LOCALAPPDATA%\Programs the installer puts the app in, e.g. an ASCII ZhiLian for an app named 智链; the shortcuts and Settings › Apps keep the name, and an installed app is updated where it is
installer_includenonean NSIS script of the app's own the installer includes, as resources/installer.nsh, after MUI2.nsh and the languages: the macros it defines among customInit, customInstall and customUnInstall run as the installer starts (.onInit), at the end of the install, once the files are in $INSTDIR, and in the uninstall before the app's folder is removed; silent installs run them too

linux ​

fielddefault
maintainerthe namethe Debian package's Maintainer
commentnonethe desktop entry's Comment
categoriesUtilitythe desktop entry's Categories
dependsnonemore Debian dependencies, after GTK 3 and WebKitGTK
commandnonea command in /usr/bin that runs the app (letters, digits, ., _, +, -), or a list of them

myzig.json ​

The same fields in JSON:

json
{
  "name": "Hello",
  "dev_url": "http://localhost:5173",
  "file_associations": [{ "ext": ["md"], "name": "Markdown" }],
  "windows": { "installer": false }
}

How it is read ​

The CLI reads the file with a ZON reader of its own (cli/src/zon.zig, so that it reads the same across Zig versions and says where a mistake is), fills in the defaults (from build.zig.zon and the directory), and checks the result: extensions without dots, URL schemes and the identifier as their systems want them, dev_url an http(s) URL, role Editor or Viewer. Config in cli/src/config.zig is the reference.

Released under the MIT License.