Skip to content

Rollback

Status: target state, partial. There is no mx rollback or hivecast rollback command in the current code tree. The install path keeps a transient backup during a single install operation but does not retain previous versions long-term. This page documents the manual rollback recipe that works today and the target-state surface that would make rollback first-class.

Present-state recipe — re-install the previous version

bash
HOST=/tmp/matrix-home
PKG=@open-matrix/chat

# Stop the broken runtime
hivecast down RUNTIME-HOST-CHAT --home "$HOST"

# Install the known-good previous version
mx install "$PKG@0.2.8" \
  --packages-dir "$HOST/packages/global" \
  --force

# Bring the runtime back
hivecast up "$PKG" --home "$HOST"

# Confirm
hivecast runtimes --home "$HOST" | jq '.[] | select(.packageRef | contains("@0.2.8"))'

This works because the registry retains historical versions. As long as the previous version is still resolvable, you can bring it back with a versioned install.

What's safe and what isn't

Safe:

  • Reverting to a prior version of the same package, with no cross-package change.
  • Reverting to a prior version when no install.migrate hook ran during the bad upgrade (so no schema state needs to be undone).

Risky:

  • Reverting after a successful install.migrate that altered persistent state. The previous code may not handle the new state shape. CLAUDE.md Rule 9.9 says schema evolution is additive only, which means most additive migrations are roll-back-safe — but case-by-case judgment applies.

  • Reverting one package out of a coordinated multi-package upgrade. If chat@0.3.0 and system@1.1.0 shipped together because chat depends on a new system op, rolling chat back without rolling system back may leave the system holding an op nobody calls.

Transient backup during install

packages/mx-cli/src/commands/install.ts:341-408 does keep a backup of the previous version inside a single install operation:

ts
if (hadExistingTarget) {
  fs.renameSync(targetDir, backupDir);
}
fs.renameSync(stageDir, targetDir);
// ... lifecycle hooks ...
// On success: fs.rmSync(backupDir, ...)
// On failure: restore the backup

This backup is removed as soon as the lifecycle hooks complete. After that, only the registry copy of the previous version exists. Don't rely on the backup as a rollback mechanism — it lives only during a single install command.

Target state — first-class rollback

A future revision could add:

  1. Version retention. The package store keeps the last N versions of each package under <host-home>/packages/<store>/node_modules/<scope>/<name>/ alongside symlinked "current". Rollback flips the symlink.
  2. hivecast rollback <pkg> that uses the retained version without going back to the registry.
  3. Atomic multi-package rollback. Group package changes into a "release" and roll the whole release back together.
  4. Migration reverse hooks. A package could declare an install.rollback hook that handles state undo for cases where a prior install.migrate made non-additive changes.

None of this is in code today. Use the re-install recipe above.

Disaster-recovery posture

For now, the disaster-recovery story is:

  1. Keep the registry available — it is the canonical store of all versions.
  2. Keep operator-side records of the previously-known-good versions (deployment logs, the operator's own tracking).
  3. Test the re-install recipe before you need it. CLAUDE.md "REBUILD BEFORE TESTING" applies: try a roll-forward and roll-back drill on a scratch Host home before committing to a risky upgrade.

See also

Source: Backup-during-install in projects/matrix-3/packages/mx-cli/src/commands/install.ts:341-408. No rollback subcommand exists in the current mx-cli or hivecast wrapper.