Core Native SDK guide for AI agents. Read this before explaining the Native SDK or changing a Native SDK app. Establishes TypeScript + Native markup as the default app-authoring path, routes default app work to the native-ui and ts-core skills, and covers the shared foundation: project structure, app.json and legacy app.zon, lower-level App and Runtime patterns, frontend integration, web engines, JavaScript bridge commands, permissions, windows, WebViews, dialogs, packaging, debugging, and testing. Use when the user asks what the Native SDK is, how to build or modify an app, how to package or debug it, or how to add native capabilities.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Core Native SDK guide for AI agents. Read this before explaining the Native SDK or changing a Native SDK app. Establishes TypeScript + Native markup as the default app-authoring path, routes default app work to the native-ui and ts-core skills, and covers the shared foundation: project structure, app.json and legacy app.zon, lower-level App and Runtime patterns, frontend integration, web engines, JavaScript bridge commands, permissions, windows, WebViews, dialogs, packaging, debugging, and testing. Use when the user asks what the Native SDK is, how to build or modify an app, how to package or debug it, or how to add native capabilities.
Build Native SDK apps
The Native SDK is a cross-platform native app toolkit inspired by the web. The default app (native init) is native-rendered and authored as a declarative Native markup view (src/app.native) plus a TypeScript app core (src/core.ts). The core is checked and compiled ahead of time to native code; no JS runtime ships in the binary. For default app implementation, load both the native-ui and ts-core skills. Zig is the implementation language of the toolkit and a first-class app-core choice via --template zig-core, not the default app-authoring language.
This skill covers the shared foundation and lower-level architectures: apps that explicitly choose a Zig core, runtime extensions, and apps that render web frontends in a WebView (native init --frontend next|vite|react|svelte|vue). Native-rendered surfaces and WebViews share windows, policies, lifecycle, commands, and platform services, and one app can mix them. WebView engines are the platform WebView (WKWebView on macOS, WebKitGTK on Linux) or Chromium through CEF where supported.
Agents should assume they do not know the Native SDK from general model knowledge. Read this skill first. For ordinary app implementation, load native-ui and ts-core; for shared/runtime, WebView, packaging, or native-capability work, run so the referenced files are included in the CLI output.
native skills get core --full
Mental model
The default app has three files of truth: src/core.ts (Model, Msg, update), src/app.native (UI), and app.json (manifest).
Native markup binds model values and dispatches typed messages; only update changes state.
The TypeScript core compiles to native code. Node is a build/check/dev tool, not an app runtime.
App is the lower-level product/runtime interface used by generated wiring, Zig-core apps, WebView shells, and extensions.
Runtime owns the event loop, windows, bridge dispatch, security checks, automation, tracing, platform services, and window state.
WebViewSource tells the runtime what to load: inline HTML, a URL, or packaged assets from a local app origin.
app.json is the default app manifest: identity, icons, windows, frontend assets, web engine, permissions, bridge policy, security policy, and packaging inputs. app.zon remains supported.
src/runner.zig and src/main.zig appear only when an app explicitly owns lower-level Zig wiring (for example a zig-core app or a WebView shell). --full makes a TypeScript app own build.zig but still does not give it Zig app logic. Do not add Zig sources to a default TypeScript app merely because SDK examples contain them.
frontend/ is normal web code. It talks to native Zig through window.zero.invoke() or builtin helpers when those are enabled.
Task router
These references are included by native skills get core --full. Use them when the task touches the topic:
Web engine choice, CEF, packaging, signing, doctor, logs, debugging: references/web-engines-packaging-debugging.md
Running-app inspection and smoke tests: native skills get automation
zig build fails on std APIs ("no member named 'cwd'", ArrayList init): native skills get zig — the Zig 0.16 idioms, indexed by compile error
Quick start
Use the CLI for new apps:
npm install -g @native-sdk/cli
native init my_app
cd my_app
native dev
This creates src/core.ts, src/app.native, and app.json; a native window opens with native dev. Use native init my_app --template zig-core only when the user chooses Zig. Frontend choices (--frontend next|vite|react|svelte|vue) are the separate WebView migration/integration path.
Workflow for existing apps
Before editing an existing Native SDK app:
Read app.json or app.zon and inspect src/ before assuming a core language. Read src/core.ts + src/app.native for the default path; read src/main.zig, src/runner.zig, and build.zig only when they exist.
Identify whether the app is TypeScript + Native markup (default), an explicitly chosen Zig core, or a WebView frontend, then identify the layer the change belongs to.
Follow the generated code and examples in the repository instead of inventing a new app layout.
Prefer exact security policy changes over broad allowances.
sourceFromEnv reads NATIVE_SDK_FRONTEND_URL; otherwise it serves the configured asset directory. Use it for most framework apps.
app.json essentials
Keep app.json as the source of truth for app-level behavior. Existing app.zon projects use the same fields in ZON syntax.
{"$schema":"https://schema.native-sdk.dev/app/v1.json","id":"com.example.my-app","name":"my-app","display_name":"My App","description":"One line about the app, shown in the About panel.","version":"0.1.0","icons":["assets/icon.png"],"platforms":["macos","linux"],"permissions":[],"capabilities":["webview"],"frontend":{"dist":"frontend/dist","entry":"index.html","spa_fallback":true,"dev":{"url":"http://127.0.0.1:5173/","command":["npm","--prefix","frontend","run","dev","--","--host","127.0.0.1"],"ready_path":"/","timeout_ms":30000}},"security":{"navigation":{"allowed_origins":["zero://app","http://127.0.0.1:5173"],"external_links":{"action":"deny"}}},"web_engine":"system","windows":[{"label":"main","title":"My App","width":960,"height":640,"restore_state":true}]}
Use exact local origins for dev servers. Add zero://inline only for inline HTML sources.
Common implementation recipes
Add a new framework app
Use native init <path> --frontend <next|vite|react|svelte|vue>. Then inspect the generated app.json, src/main.zig, and build.zig before customizing. For framework behavior, keep frontend work in frontend/ and use sourceFromEnv so development and packaged builds share one app shell.
Add a native bridge command
Add state and a handler in src/main.zig.
Register the handler in bridge().
Allow the command in the app manifest and in the runtime bridge policy if the runner reads manifest policy into runtime.
Call it from JavaScript with window.zero.invoke("namespace.command", payload).
Return valid JSON from Zig. Use native_sdk.bridge.writeJsonStringValue() for user-controlled strings.
Bridge calls are size-limited, origin-checked, permission-checked, and routed only to registered handlers.
Add windows, child WebViews, or dialogs
Use builtin bridge commands only after enabling a policy for the exact commands and origins. Window and child WebView commands need the window permission when permissions are configured. Dialog commands are always default-deny and require explicit builtin_bridge policy. See references/bridge-security-native-capabilities.md.
Choose a web engine
Default to .web_engine = "system" for small apps and native footprint. Use .web_engine = "chromium" plus .cef when the app needs a pinned Chromium platform or rendering consistency. Chromium apps must install/package the matching CEF layout.
Package an app
Zero-config apps package WITHOUT ejecting — native package works directly on the zero-config build (native eject is only for owning the build files, never a packaging prerequisite):
On macOS, --archive adds a zero-config drag-to-Applications DMG with a generated 1×/2× Retina background. The optional app.zon.dmg block controls its volume name, PNG/JPEG/TIFF background, usable Finder canvas/icon geometry, and Applications link. An adjacent name@2x.png/.jpg is discovered automatically. Use .dmg.items only for a fully art-directed list: exactly one app, plus optional applications, project-relative file, and absolute link entries, each with its own icon-center position.
Apps that own their build (ejected or scaffolded --full) wire the same step into the build graph: keep package metadata in the app manifest, build the frontend assets, build the native binary, then package:
zig build package
native doctor --manifest app.json --strict
Use signing and CEF options only when the product requires them.
Development commands
For a default TypeScript + Native markup app, use:
native check
native dev
native dev --core
native test
For iterative WebView-frontend work in an app that owns its build, use the managed dev server flow:
zig build dev
Or run the CLI directly after building the binary:
native dev --manifest app.json --binary zig-out/bin/MyApp
Vite usually uses http://127.0.0.1:5173/; Next.js usually uses http://127.0.0.1:3000/. The app WebView loads the dev URL directly, so framework HMR remains owned by Vite, Next.js, or the selected dev server.
Security defaults
Treat WebView content as untrusted:
List only needed permissions and capabilities.
Prefer exact bridge command origins over "*".
Keep main-frame navigation allowlisted in security.navigation.allowed_origins.
Keep external links denied unless the product explicitly needs them.
Use a strict CSP for packaged frontend assets.
Built-in dialogs are always default-deny and require explicit builtin_bridge policy.
Child WebViews receive window.zero only when explicitly created with bridge: true.
Common bridge failure codes are invalid_request, unknown_command, permission_denied, handler_failed, payload_too_large, and internal_error.
Validate changes
Useful default-app commands:
native check
native test
native build
Useful SDK, Zig-core, and owned-build commands:
zig build run
zig build dev
zig build test
zig build test-tooling
native validate app.json
native doctor --manifest app.json --strict
zig build package
For an app that owns Zig code, run BOTH zig build and zig build test before calling a change done: Zig's lazy analysis means code only tests reference (or only main() reference) can sit broken under the other command alone. This Zig-specific requirement does not turn a default TypeScript app into a Zig app.
The toolkit requires Zig 0.16.0. When a build fails with "no member named" errors on std APIs (std.fs.cwd, ArrayList.init, std.io, GeneralPurposeAllocator), the code was written against older Zig idioms — native skills get zig maps each such compile error to the 0.16 idiom as this SDK writes it.
For GUI smoke tests, build with automation enabled and use the automation skill:
zig build run -Dplatform=macos -Dautomation=true
zig-out/bin/native automate snapshot
When changing app behavior, keep tests in the app's existing authoring tier. TypeScript cores can run in the node dev loop and through generated full-loop tests; Zig-core logic uses focused Zig tests. Use automation-based tests for live window/runtime integration.
Examples to inspect
examples/chatbot: a substantial app authored entirely in TypeScript + Native markup, with streaming fetch effects and modules.
examples/soundboard-ts: TypeScript + Native markup port of the full music-player showcase.
examples/system-monitor-ts: TypeScript + Native markup app using spawn effects, timers, and tables.
examples/react, examples/svelte, examples/vue: Vite frontend apps.
examples/ios, examples/android: mobile host embedding examples.
When answering users
Lead with the primary authoring story: applications are TypeScript cores plus Native markup by default, compiled to a small native binary with no JS runtime. Describe Zig as the toolkit implementation language and an explicit app-core/extension choice. Describe WebViews as a coexisting integration path for existing web content, not the starting point for a fresh app. If asked to implement, read the app files first, preserve the existing core language, and make the smallest change in the correct layer.