Upgrade Zhin, not just a version string
An upgrade changes dependencies, Feature APIs, configuration, durable data and the running plugin versions. The safe flow is: rehearse first, back up, then verify against what actually runs.
A routine patch upgrade usually needs only:
npx zhin migrate --dry-run # see what would change
npx zhin migrate # upgrade
npx zhin doctor && pnpm test # check
npx zhin runtime start --once --mode test # assembly smoke testFor production, follow the full flow below and take a backup first. Stop the Bot before upgrading so dependency directories are not replaced under a running process.
Upgrading from 1.1.13 or earlier: configVersion 2
Breaking config change
Starting with @zhin.js/cli 1.1.14 the Runtime only accepts configVersion: 2 (flat plugin configuration). After a ^1.1.x project picks up the new packages, the old config is rejected at startup with an error containing configVersion must be 2; newer CLIs say "Detected a v1 configuration" directly. Migrate in this order and do not skip steps.
Stop the Bot before upgrading dependencies. pnpm removes old version directories during the upgrade, so a running process can crash mid-upgrade or load a mix of old and new files.
bashzhin stop # also stop foreground, systemd, pm2 or launchd managed processesUpgrade to a CLI that has the migration command (1.1.14 or later).
zhin migratein 1.1.13 and earlier only upgrades dependencies and scripts and has noinstancessubcommand.bashpnpm add zhin.js@latest @zhin.js/cli@latest pnpm installOlder
zhin migrateversions also rewrite exact pinned versions tolatest, so check with--dry-runfirst; newer versions keep pins by default.Preview the config migration (writes nothing and prints no config values):
bashzhin migrate instancesApply it (originals are backed up first):
bashzhin migrate instances --applyOriginals are kept in
.zhin/instance-declarations-migration/; newer CLIs also copy them to.zhin/backups/config-v2-<timestamp>/and print that path.Check and start again:
bashzhin config check zhin doctor pnpm start
Restore: keep the Bot stopped and run zhin migrate instances --recover rollback, or copy the files back to the paths listed in the backup's manifest.json and delete .zhin/instance-declarations-migration. If a migration was interrupted, --recover continue finishes it. To run the old version again you also need to restore dependencies and the lockfile to 1.1.13.
Cases the tool refuses to convert automatically (nested plugins, per-mount optional, instance names colliding with reserved keys) are covered in Migrating to flat plugin configuration.
Compatibility boundaries
| Boundary | Contract |
|---|---|
| Node | >=22.19.0 |
| Root and plugin | semver in package.json#zhin.engine |
| Feature consumer/provider | features[].api and Feature featureApi |
| Zhin package family | one resolved release combination in the lockfile |
| Console | public Host REST/RPC/SSE contract and public client types |
| Durable data | version-specific schema, Journal, and migration notes |
Commit the production lockfile and never boot from a drifting latest resolution. zhin migrate targets non-workspace Zhin dependencies at latest; it is an explicit upgrade action, not a deployment command.
Explicit workspace: dependencies are always preserved, including when migration runs from a workspace child package. A missing pnpm-workspace.yaml in the current directory does not turn a local link into a registry dependency. Fix workspace declarations and package names when resolution fails rather than changing the dependency source during migration.
migrate installs dependencies by default; only --no-install skips installation. The pnpm install refreshes the lockfile to match the migrated manifest (--no-frozen-lockfile, including in CI). If the current directory has only an npm lockfile but declares workspace: dependencies, migration stops before changing files because npm cannot install that protocol. Check the pnpm workspace configuration first, or explicitly use --no-install to migrate only the manifest and handle installation separately.
Before upgrading
zhin migrate --dry-run
zhin doctorRead root Changesets/CHANGELOG and affected package notes. Check breaking manifests, Feature APIs, removed config, database migrations, and Adapter connection changes.
Create one checkpoint: Git commit, package.json, lockfile, config, .env key inventory, database snapshot, Workroom store, and data/schedule-jobs.json. Protect secret values separately.
Execute
- Run
zhin migrate --dry-runon a copy or staging environment. - Enter a maintenance window and stop the Bot and any ingress that creates new side effects (before upgrading dependencies).
- Run
zhin migrateand review package, script, and directory changes. - Run doctor, type checks, tests, and
runtime start --once --mode test. - Start production and accept Sandbox, a real Endpoint, Agent, and Workroom.
Never share one writable SQLite file or file Journal between live instances. Blue/green release needs isolated copies and an explicit cutover; only one instance may own write authority.
Rollback decision
Stop the release if the candidate cannot publish, Endpoints cannot recover, SSE history has an unrebuildable gap, Workroom authority fails, or a data migration has no recovery plan.
Restore the complete checkpoint. Database recovery cannot undo real messages, approvals, Git operations, or payments. Use idempotency keys, Effect Ledger, or human compensation for external effects.
Plugin author responsibility
- Use a major changeset for breaking public API.
- Update provider, consumer requirements, and API surface snapshots with a Feature API.
- Reject removed config with a migration target; do not retain a silent dual path.
- Install the real tarball before release and verify JS entry,
files, peers, and README.