nddev-zcode-app can install ZCode from zero and then configure it. Two phases:
bootstrap— downloads and verifies the exact pinned ZCode artifact, installs the desktop app and embedded CLI, and atomically writes the localzcodelauncher. macOS uses the official ZIP update artifact; Ubuntu uses a DEB when the complete dpkg toolchain is available and a locally extracted AppImage otherwise.install— builds a clean~/.zcodefrom the managed setup (config, plugins, skills). Backs up the current one first.
Both work on macOS (desktop) and Ubuntu (desktop/server). Upstream ZCode
distributes macOS as a general-availability download and Linux only through the
vendor Linux beta group, so NDDev supports the Ubuntu build on top of a vendor
beta channel. The machine-readable per-platform support tiers and their
freshness window are recorded in
references/zcode-baseline.json:platform_support.
- Git,
/usr/bin/python3as a regular executable Python 3.9+ interpreter,curl, andnode(the CLI launcher runs the app'szcode.cjsthrough node). - A local
build/.envcopied frombuild/.env.exampleonly when explicit API-key providers, MCP integrations, or custom target settings are needed (see secrets.md). When present, it must be a current-user-owned regular non-symlink with mode0600or stricter. Z.ai account OAuth remains the default. - macOS bootstrap additionally requires the system
ditto,codesign, andspctltools. Ubuntu DEB installation requiresdpkg,dpkg-deb,dpkg-query, andsudowhen not running as root.
# 1. Clone or download the repository.
# 2. Optional: configure explicit API-key providers or local target settings.
# Skip this step when using the default Z.ai account OAuth flow.
cp build/.env.example build/.env
chmod 600 build/.env
$EDITOR build/.env
# 3. Verify and install the ZCode app + CLI from the official CDN:
cli-tools/scripts/install.sh bootstrap --plan # dry-run first
cli-tools/scripts/install.sh bootstrap --apply
# 4. Configure ~/.zcode from the managed setup:
cli-tools/scripts/install.sh list
cli-tools/scripts/install.sh install --plan
cli-tools/scripts/install.sh install --applyAfter step 3, ZCode is installed and the zcode command is on PATH. After
step 4, ~/.zcode is configured and ready to use.
The installed ZCode home contains sensitive runtime state. Do not print,
commit, upload, or attach its .env, rendered provider/MCP configs,
v2/credentials.json, or backup contents.
Plan mode performs no writes, downloads, target mutation, or live zcode
execution. It does parse, substitute, and merge config/setting/provider/MCP/hook
inputs and fails if an active key or value contains a missing or empty
placeholder. Runtime detection is deferred until apply mode.
Bootstrap does not trust an existing installation or an unverified download. Apply mode performs these checks in order:
- Select the platform/architecture artifact object from
build/version.json. - Require the canonical CDN base to be exactly
https://cdn-zcode.z.ai/zcode/electron/releases, construct a credential-free artifact URL, and allow HTTPS-only redirects. - Verify the exact byte size and SHA-512 digest before opening the artifact. Both must match independently; equal size never excuses digest drift.
- On macOS, extract the already verified ZIP artifact privately, then verify Gatekeeper assessment, code signature, Team ID, bundle ID, app version, and bundle version before and after installation.
- On Ubuntu DEB systems, verify package name, architecture, and exact Debian
package version. Extract the payload privately before installation; require
exactly one safe
/opt/ZCode/resources/glm/zcode.cjsentry and verify its pinned CLI version. A privilegeddpkg --dry-run -ipreflight must then succeed before the realdpkg -i. After installation, require the same exact dpkg-owned path and CLI version, and require its SHA-512 to match the verified payload entry. Installation failure is fatal and never falls through to another format. - When complete dpkg tooling is absent, extract the already verified AppImage, verify its embedded CLI, and replace its managed install directory through a same-filesystem stage.
- Hold deterministic locks for every installer-managed app endpoint and the user launcher (with dpkg owning its system package transaction), then stage and swap the app and launcher with rollback state.
- Require exact app/package, entrypoint, launcher, and CLI postconditions. These postconditions define the commit point. Cleanup failure after commit is reported as an error without rolling back verified committed state.
Default runtime paths are:
| Platform/package | Embedded CLI entry |
|---|---|
| macOS ZIP | /Applications/ZCode.app/Contents/Resources/glm/zcode.cjs |
| Ubuntu DEB | /opt/ZCode/resources/glm/zcode.cjs |
| Ubuntu AppImage | ${HOME}/.local/opt/ZCode/resources/glm/zcode.cjs |
All three use ${HOME}/.local/bin/zcode as the user-facing launcher. The
AppImage path can be changed with NDDEV_APPIMAGE_INSTALL_DIR; the macOS app
root can be changed with NDDEV_APPLICATIONS_DIR for isolated environments.
# List available setups for people or automation:
cli-tools/scripts/install.sh list
cli-tools/scripts/install.sh list --json
# Install the managed setup — plan (dry-run) first, then apply:
cli-tools/scripts/install.sh install --plan
# Quit the ZCode desktop app before every apply-mode target mutation.
cli-tools/scripts/install.sh install --apply
# Inspect the managed setup and validated installed-state stamp:
cli-tools/scripts/install.sh status
cli-tools/scripts/install.sh status --json
# Explicitly adopt an existing unstamped ZCode home for the first time.
# Both --adopt-unmanaged and an explicit existing --target are required:
cli-tools/scripts/install.sh install --setup nddev-builder \
--target "$HOME/.zcode" --adopt-unmanaged --plan
cli-tools/scripts/install.sh install --setup nddev-builder \
--target "$HOME/.zcode" --adopt-unmanaged --apply
# Update — re-run install when the managed setup source changed.
# Posture — add --posture safe for a stricter rendered policy.
# Remove — back up and delete the install:
cli-tools/scripts/install.sh remove --apply
# Inspect and restore numbered backup slots:
cli-tools/scripts/install.sh list --backups
cli-tools/scripts/install.sh restore --slot 3 --plan
cli-tools/scripts/install.sh restore --slot 3 --apply
# Custom install directory (default is ~/.zcode):
cli-tools/scripts/install.sh install \
--target "$HOME/.zcode-work" --apply
# ...or set it once in build/.env (ZCODE_TARGET=...) and skip --target.
# Force a platform (otherwise auto-detected from uname):
cli-tools/scripts/install.sh install \
--platform macos --apply| Command | What it does |
|---|---|
bootstrap |
Install the pinned ZCode app and CLI; defaults to plan mode. |
install |
Back up, build from the managed setup, and restore runtime state. |
remove |
Atomically move a stamped target into the backup pool. |
restore |
Restore one backup slot into an empty or stamped target. |
list |
Show setups; add --json for automation or --backups for backup slots. |
status |
Validate and report missing, unmanaged, legacy-managed, or setup-aware managed state, including coordination anchors and cleanup state. |
The installer rejects unknown options, options that do not apply to the chosen
command, and simultaneous --apply plus --plan/--dry-run. It never silently
ignores a recognized flag.
| Option | bootstrap | install | remove | restore | list | status |
|---|---|---|---|---|---|---|
--setup (--marketplace alias) |
— | optional, nddev-builder |
— | — | — | — |
--posture |
— | full-auto or safe |
— | — | — | — |
--target |
— | yes | yes | yes | — | yes |
--platform |
yes | yes | — | — | — | — |
--apply, --plan, --dry-run |
yes | yes | yes | yes | — | — |
--keep-backup |
— | yes | yes | yes | — | — |
--slot |
— | — | — | yes | — | — |
--adopt-unmanaged |
— | yes | — | — | — | — |
--allow-target-relocation |
— | — | — | yes | — | — |
--backups |
— | — | — | — | yes | — |
--json |
— | — | — | — | yes (setups only) | yes |
Use list, -l, or --list to select the list command. -h/--help prints
usage without executing a command.
The install, remove, restore, or status target is resolved in this order:
--target <dir>flag (highest precedence)ZCODE_TARGETinbuild/.env~/.zcode(the standard ZCode location, default)
The backup root defaults to ~/.zcode-backups. For install, remove, and
restore, --keep-backup <dir> selects an explicit backup root for that command;
otherwise ZCODE_BACKUPS_DIR in build/.env is used when present.
The env parser performs no shell expansion. For portability, only
ZCODE_TARGET and ZCODE_BACKUPS_DIR support an exact leading literal
$HOME, $HOME/, ${HOME}, or ${HOME}/ prefix; other values remain literal.
Target and backup roots must be canonical absolute paths with existing real
immediate parent directories. They must be non-root, disjoint, and on one
filesystem. Existing files and symlink endpoints are always refused. The
current user must be able to create and replace entries in both parents; a path
such as /opt/... therefore requires permissions prepared by an administrator.
An existing unstamped directory is also refused unless install receives both
--adopt-unmanaged and an explicit existing --target; this prevents a typo
from silently replacing an unrelated directory.
Every transition backs up the current install first into a rotating pool of
10 slots (0-<VERSION>-old.zcode … 9-<VERSION>-old.zcode), so the total
never grows beyond 10 directories:
- Install — fresh build from the managed setup.
- Update — re-run
installwhen the managed setup source changed. - Remove —
removebacks up and deletes.
Slot selection: the lowest free slot (0–9). When all 10 are full, the oldest
slot (by modification time) is selected regardless of version. Its prior
occupant is held until the new transaction commits and is restored on failure.
Managed backups carry the version in the filename and a validated timestamp in
BUILD-VERSION.
An explicitly adopted unstamped target is stored as
<N>-unmanaged-old.zcode/, containing an owner-only NDDEV-BACKUP.json
envelope and the original content tree under payload/. Adoption normalizes
directory and file permissions to private owner-only modes, so original mode
metadata is intentionally not preserved. The envelope binds the backup to its
original canonical target.
- Boundaries and locks — target and backup roots are validated, then the installer acquires exclusive target and shared backup-pool locks. Concurrent operations cannot race the same live tree or rotation pool. Apply mode also rejects open task/session databases and SQLite recovery sidecars; quit ZCode cleanly before install, remove, or restore.
- Stage — a private sibling staging directory is created on the same
filesystem. A clean target is rendered there from the managed marketplace:
AGENTS.mdand user-scopeskills/,commands/, andagents/are copied as-is; the complete managed marketplace, including its plugins, is copied undermarketplaces/<name>/.cli/config.json,v2/config.json,v2/setting.jsonare rendered from their JSON inputs, with${VAR}values structurally substituted and JSON-escaped. Placeholder-bearing object keys are rejected. Rendered MCP entries are merged intocli/config.json.- Empty runtime directories ZCode expects are created.
- Version stamp — schema-2
BUILD-VERSIONrecordssetup_id,posture, build version, ZCode runtime baseline, platform, and timestamp. Legacy schema 0/1 stamps remain readable for recovery, but their setup identity is reported as unknown. - Restore into stage — selected runtime state is copied from the current
target before any live replacement:
- Always restored:
v2/credentials.json,v2/certs/, the desktopv2/tasks-index.sqlite, legacyv2/sessions/, current and legacy bot definition files,cli/agents/,cli/db/, andcli/artifacts/. The task index and CLI session database are restored together so their cross-store session references stay consistent. - Never restored (regenerated by ZCode):
cli/log/,v2/logs/,v2/crash/,cli/plugins/cache/, transientcli/exec/, model I/Ocli/rollout/, telemetry, and model/plan caches.
- Always restored:
- Verify — managed stamp and JSON schemas, exact stamp/setup/posture identity, marketplace presence, unresolved active config/setting/provider/MCP/hook placeholders in keys or values, symlink/special-file/hardlink absence, and private permissions are checked; the verified stage is fsynced before commit. Only explicitly disabled provider/MCP entries may keep dormant placeholders.
- Commit or rollback — an occupied backup slot is held, the previous
target moves into its backup, and the verified stage is atomically renamed
into place. A failed commit, handled signal, or pre-commit finalization step
restores the previous target and backup-slot occupant. Housekeeping failure
after a verified commit is reported as
cleanup_pending=truewithout discarding committed state. Stage, live, rollback, adoption-envelope, and occupied-slot endpoints remain bound to their recorded filesystem identities through abort and success cleanup. If any endpoint is replaced, foreign state is left untouched and recovery paths plus locks are preserved for explicit inspection.
Open the ZCode desktop app. credentials.json is restored from the backup, so
you stay logged in. If the auth token expired, re-authenticate in the app.
The verified preference uses Z.ai account OAuth. To use an explicit Z.ai API
key instead, populate ZAI_API_KEY, set that custom provider's enabled field
to true, reinstall, and select custom:zai-api-key, which is backed by
https://api.z.ai/api/anthropic. BigModel API-key access remains a separate,
disabled-by-default custom:bigmodel-api-key provider backed by
https://open.bigmodel.cn/api/anthropic; enable it only after populating
BIGMODEL_API_KEY.
A setup install in apply mode resolves and canonicalizes the zcode executable
once, then invokes --version with no stdin, a 3-second timeout, and a 64 KiB
output cap. A missing binary reports not-installed; timeout, nonzero exit,
oversized output, or probe error reports unknown. Those values are advisory
and nonfatal during a normal setup install. Bootstrap uses the same bounded
probe as a strict postcondition and refuses unknown or mismatched CLI state.
Use the guarded restore lifecycle instead of moving backup directories manually:
cli-tools/scripts/install.sh list --backups
cli-tools/scripts/install.sh restore --slot <N> --plan
cli-tools/scripts/install.sh restore --slot <N> --applyIf the target exists, restore first backs it up. The restore source is copied to and verified in a private same-filesystem stage before backup rotation or target replacement, so a full 10-slot pool cannot invalidate the selected source.
Typed adopted-unmanaged envelopes restore only to their recorded original target by default. Relocation is a separate explicit action:
cli-tools/scripts/install.sh restore --slot <N> \
--target /absolute/new/path --allow-target-relocation --plan
cli-tools/scripts/install.sh restore --slot <N> \
--target /absolute/new/path --allow-target-relocation --applyOrdinary errors and handled termination signals trigger deterministic rollback
before the commit point. After a valid final cleanup journal or verified
bootstrap runtime is visible, cleanup failures are pending lifecycle work rather
than rollback failures. Read-only status and dry-run plans expose the actual
product/target coordination anchors plus bounded cleanup-pending metadata, never
cleanup deletion paths, and never repair it. An
uncatchable SIGKILL or power loss can leave a sibling stage, recovery hold, or
lock directory. The installer then fails closed rather than guessing that the
transaction owner is dead. Inspect the lock's owner metadata and reconcile the
live target, backup slot, and held state before removing recovery artifacts;
preserve uncertain state for manual recovery. The locks are advisory against
cooperative installer processes; filesystem identity checks additionally
prevent cleanup from deleting a same-path object that another process substituted
before a guarded mutation.
Ubuntu DEB installation delegates the system package transaction to dpkg.
Before the real transaction, the installer verifies the privately extracted
payload and runs dpkg --dry-run -i. It detects package-manager and
postcondition failures, but it cannot transactionally undo changes made inside
a real dpkg transaction. Repair the system package-manager state before
retrying; bootstrap never masks a failed DEB by falling through to AppImage.
The current macOS install policy is owned by build/version.json and
references/zcode-baseline.json; the DMG bytes are not accepted evidence for
the declared build. The installer requires the declared signing identity,
bundle, build, and assessment source per artifact and requires an exact match plus a successful
Gatekeeper assessment before installation. Any missing, changed, or unexpected
source fails closed before installation.