# Updates and package distribution

> Update channels, npm multi-platform package layout, version sync checks, and binary distribution for @native-sdk packages.

- Repository: vercel-labs/native
- GitHub: https://github.com/vercel-labs/native
- Human docs: https://grok-wiki.com/public/docs/vercel-labs-native-8ccc3580636a
- Complete Markdown: https://grok-wiki.com/public/docs/vercel-labs-native-8ccc3580636a/llms-full.txt

## Source Files

- `docs/src/app/updates/layout.tsx`
- `docs/src/app/packages/layout.tsx`
- `packages/native-sdk/README.md`
- `packages/native-sdk/scripts/check-version-sync.js`
- `packages/native-sdk/scripts/build-binaries.sh`

---

---
title: "Updates and package distribution"
description: "Update channels, npm multi-platform package layout, version sync checks, and binary distribution for @native-sdk packages."
---

The Native SDK splits distribution into two surfaces: **packaged-app update channels** declared in `app.zon` as `UpdateConfig` (`feed_url`, `public_key`, `check_on_start`), and **CLI/SDK delivery** through nine npm packages under `@native-sdk/*` — one meta package (`@native-sdk/cli`) plus eight platform binary packages selected via `optionalDependencies`. The CLI binary is staged by `packages/native-sdk/scripts/build-binaries.sh`, checksummed for GitHub Releases, and resolved at runtime by `packages/native-sdk/bin/native.js` with no install scripts.

## App update channels

`app.zon` carries an optional `.updates` block typed as `UpdateConfig` in the app manifest. Manifest validation runs `validateUpdates` on every full validate pass.

```zig
.updates = .{
    .feed_url = "https://example.com/releases/native-sdk-feed.json",
    .public_key = "base64-ed25519-public-key",
    .check_on_start = true,
},
```

### Fields

| Field | Type | Default | Constraints |
| --- | --- | --- | --- |
| `feed_url` | `?[]const u8` | `null` | When set: exact `http://` / `https://` URL, or a trailing-`*` prefix pattern (`https://…*`, `http://…*`, or `*`). Empty / invalid → `InvalidUrl` |
| `public_key` | `?[]const u8` | `null` | When set: must be non-empty (`MissingRequiredField` if `len == 0`) |
| `check_on_start` | `bool` | `false` | Whether the app should check for updates during startup |

Defaults are empty/null so apps without an update feed validate cleanly. The reserved config is for **discovery and signature material**, not silent auto-install: apps surface checks in their own UI, verify signatures before applying artifacts, and keep platform install behavior explicit.

<Warning>
Setting `public_key` to `""` fails validation. Omit the field or provide a non-empty key string.
</Warning>

## npm multi-platform layout

Install the meta package only:

```bash
npm install -g @native-sdk/cli
```

Install runs **no scripts**. Package managers resolve exactly one of the eight optional platform packages for the host OS/CPU/libc. All nine packages share one exact version (currently stamped from `packages/native-sdk/package.json`).

### Package inventory

| Package | Role | Platform selectors |
| --- | --- | --- |
| `@native-sdk/cli` | `native` shim (`bin/native.js`), SDK source payload, exact pins in `optionalDependencies` | — |
| `@native-sdk/cli-darwin-arm64` | Prebuilt `native` | `os: darwin`, `cpu: arm64` |
| `@native-sdk/cli-darwin-x64` | Prebuilt `native` | `os: darwin`, `cpu: x64` |
| `@native-sdk/cli-linux-arm64-gnu` | Prebuilt `native` | `os: linux`, `cpu: arm64`, `libc: glibc` |
| `@native-sdk/cli-linux-x64-gnu` | Prebuilt `native` | `os: linux`, `cpu: x64`, `libc: glibc` |
| `@native-sdk/cli-linux-arm64-musl` | Prebuilt `native` | `os: linux`, `cpu: arm64`, `libc: musl` |
| `@native-sdk/cli-linux-x64-musl` | Prebuilt `native` | `os: linux`, `cpu: x64`, `libc: musl` |
| `@native-sdk/cli-win32-arm64` | Prebuilt `native.exe` | `os: win32`, `cpu: arm64` |
| `@native-sdk/cli-win32-x64` | Prebuilt `native.exe` | `os: win32`, `cpu: x64` |

Platform packages set `files: ["bin"]` and `preferUnplugged: true`. Do not install a platform package directly; each README points installers at `@native-sdk/cli`.

### What the meta package ships

`@native-sdk/cli` `files` include the dispatcher, type stubs, and the mirrored SDK payload apps build against:

- `bin/native.js` — platform dispatcher
- `src/`, `build/`, `build.zig`, `build.zig.zon`, `app.zon` — framework graph for offline `native init` / `native dev`
- `skills/`, `skill-data/` — agent skill packs
- `native-sdk.d.ts`, `README.md`, `LICENSE`

`prepack` runs `scripts/copy-framework.js`, which mirrors `src`, `build`, `skills`, `skill-data`, `build.zig`, `build.zig.zon`, `app.zon`, and `LICENSE` from the repo root into the package before publish.

### Binary resolution

`bin/native.js` maps host `os` / `arch` / musl detection to a package name, then:

1. `require.resolve('@native-sdk/cli-<platform>/bin/native[.exe]')`
2. Fallback: repo-local legacy path under `packages/native-sdk/bin/native-sdk-<platform>[.exe]` (via `scripts/copy-native.js` after `zig build`)

On success it sets `NATIVE_SDK_PATH` to the meta package root when `src/root.zig` is present (user-set `NATIVE_SDK_PATH` wins), then `spawn`s the binary with inherited stdio and forwarded `SIGINT` / `SIGTERM` / `SIGHUP`.

```text
npm install -g @native-sdk/cli
        │
        ├─ @native-sdk/cli          (shim + SDK source)
        └─ @native-sdk/cli-<host>   (one optionalDep binary)
                    │
                    ▼
              bin/native.js  ──exec──►  native CLI
                    │
                    └── NATIVE_SDK_PATH → package root (if not set)
```

### Failure modes

| Symptom | Cause | Fix |
| --- | --- | --- |
| `No native binary found… Expected package: @native-sdk/cli-…` | Install with `--no-optional` / `--omit=optional` | Reinstall with optional deps enabled |
| `Unsupported platform: …` | Host OS/arch outside the eight targets | Use a supported host or build from source |
| Binary not executable (non-Windows) | Missing `+x` | Dispatcher attempts `chmod 755`; otherwise `chmod +x` the resolved path |

## Version sync

**Source of truth:** `packages/native-sdk/package.json` → `version`.

`npm run version:sync` (`scripts/sync-version.js`) stamps that version into:

1. `tools/native-sdk/main.zig` — `const version = "…";` (what `native version` prints)
2. Every `packages/native-sdk/npm/*/package.json` → `version`
3. Every `optionalDependencies` pin on the meta package (exact match, same version)

`npm run version:check` (`scripts/check-version-sync.js`) verifies all of the above plus that each npm directory’s `name` is `@native-sdk/cli-<dir>` and appears in `optionalDependencies`. On failure it exits non-zero and prints:

```text
Run "npm run version:sync" in packages/native-sdk to fix.
```

CI runs `version:check` (and `scripts:check`) before publish so a half-bumped release cannot ship. npm’s `version` lifecycle script also runs `version:sync` and stages the stamped files for commit.

```bash
# After bumping packages/native-sdk/package.json
npm --prefix packages/native-sdk run version:sync
npm --prefix packages/native-sdk run version:check
```

## Binary build and GitHub assets

`packages/native-sdk/scripts/build-binaries.sh` cross-compiles the CLI for all eight targets with `zig build cli -Dtarget=… -Doptimize=ReleaseSmall` and stages:

| Destination | Naming |
| --- | --- |
| `packages/native-sdk/npm/<key>/bin/native[.exe]` | npm platform package payload |
| `zig-out/release/native-sdk-<platform>[.exe]` | flat GitHub release assets |

Release asset names:

- `native-sdk-darwin-arm64`, `native-sdk-darwin-x64`
- `native-sdk-linux-arm64`, `native-sdk-linux-x64`
- `native-sdk-linux-musl-arm64`, `native-sdk-linux-musl-x64`
- `native-sdk-win32-arm64.exe`, `native-sdk-win32-x64.exe`
- `CHECKSUMS.txt` (SHA-256 of the assets above)

Pass a subset of npm keys to build fewer targets, e.g. `bash packages/native-sdk/scripts/build-binaries.sh darwin-arm64`.

Repo-local single-host pack (not for publish):

```bash
npm --prefix packages/native-sdk run build:native
# zig build + copy-native.js → packages/native-sdk/bin/native-sdk-<host>
```

## Release and publish pipeline

Maintainer release prep is manual (version bump + changelog markers). On push to `main`, `.github/workflows/release.yml`:

1. **check-release** — compares `packages/native-sdk/package.json` version to `npm view @native-sdk/cli version`. Mismatch → publish path. Same version but missing GitHub assets → rebuild/upload assets only.
2. **github-release** — runs `build-binaries.sh`, extracts notes between `<!-- release:start -->` / `<!-- release:end -->` in `CHANGELOG.md`, creates/updates tag `v<version>`, uploads `zig-out/release/*` with `--clobber`.
3. **publish** — re-checks version/scripts sync; downloads release assets and verifies `CHECKSUMS.txt`; stages binaries into `npm/*/bin/`; publishes **platform packages first**, then `@native-sdk/cli`, each with `npm publish --provenance --access public`. Already-published `name@version` entries are skipped.

Publishing uses npm trusted publishing (OIDC) under environment `Release` — no npm token secret. Each of the nine packages must have a trusted publisher for repository `vercel-labs/zero-native`, workflow `release.yml`, environment `Release`. Missing OIDC config fails `npm publish` for that package before upload.

There is no separate npm dist-tag channel matrix in-repo: the release gate is **version equality** against the registry, not `latest`/`canary` tags.

## Operations cheat sheet

| Task | Command / location |
| --- | --- |
| Install CLI | `npm install -g @native-sdk/cli` |
| Print CLI version | `native version` |
| Stamp versions after bump | `npm --prefix packages/native-sdk run version:sync` |
| Verify version consistency | `npm --prefix packages/native-sdk run version:check` |
| Cross-build all CLI binaries | `npm --prefix packages/native-sdk run build:binaries` |
| Mirror SDK into package (prepack) | `node packages/native-sdk/scripts/copy-framework.js` |
| Override SDK root | `NATIVE_SDK_PATH=/path/to/checkout` |
| App update feed config | `app.zon` → `.updates` |

## Related pages

<CardGroup>
  <Card title="Installation" href="/installation">
    Install @native-sdk/cli, host prerequisites, and success signals for a working binary.
  </Card>
  <Card title="Packaging" href="/packaging">
    From ReleaseFast app binaries to distributable app bundles and asset layout.
  </Card>
  <Card title="Code signing" href="/code-signing">
    Codesign tooling and signing constraints for macOS package pipelines.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    native commands including version, package, doctor, and build.
  </Card>
  <Card title="Contributing" href="/contributing">
    Local setup, release prep, and changelog fragment merge.
  </Card>
</CardGroup>
