> ## Documentation Index
> Fetch the complete documentation index at: https://xoxno.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Upgrades and downgrades

> Roll new mx-chain-go binaries across nodes. Stale upgrade locks self-heal. Downgrade by re-pointing at any cached binary tag.

Roll new mx-chain-go binaries across nodes. Stale upgrade locks self-heal. Downgrade by re-pointing at any cached binary tag.

## Shortest commands

```bash theme={"system"}
mxnode upgrade                              # latest tag, every node, rolling
mxnode upgrade --binary-tag v1.11.5         # pin the tag
mxnode upgrade --binary-tag v1.11.4         # downgrade — uses cached binary if present
```

To downgrade, pass any `--binary-tag T` already in the binstore. The acquirer reuses the cached binary instead of re-acquiring it, so the operation collapses to a local symlink swap + restart per node.

## Options

| Flag                           | Default              | Effect                                                |
| ------------------------------ | -------------------- | ----------------------------------------------------- |
| `--binary-tag <T>`             | resolved from GitHub | Pin the `mx-chain-go` tag.                            |
| `--config-tag <T>`             | leave untouched      | Pin the `mx-chain-mainnet-config` tag.                |
| `--proxy-tag <T>`              | leave untouched      | Pin the proxy tag (only when a proxy is installed).   |
| `--strategy rolling\|parallel` | `rolling`            | Per-node ordering.                                    |
| `--max-parallel N`             | `1`                  | Cap when `--strategy parallel`.                       |
| `--select '<expr>'`            | `--all`              | Selector grammar. See [lifecycle](/mxnode/lifecycle). |
| `--node N`                     | all                  | Limit to specific nodes (repeatable).                 |
| `--shard <S>`                  | all                  | Limit to one shard.                                   |
| `--skip-validators`            | off                  | Skip validator-role nodes.                            |
| `--dry-run`                    | off                  | Print the resolved plan without doing anything.       |

## Examples

```bash theme={"system"}
# Just shard 0
mxnode upgrade --binary-tag v1.11.5 --shard 0

# A single node
mxnode upgrade --binary-tag v1.11.5 --node 2

# Parallel up to 2 (faster, less safe)
mxnode upgrade --binary-tag v1.11.5 --strategy parallel --max-parallel 2

# Squad-aware upgrade (reapplies observer-shape config edits)
mxnode upgrade squad --binary-tag v1.11.5 --proxy-tag v1.3.4

# Proxy only
mxnode upgrade proxy --proxy-tag v1.3.4
```

## Per-node steps

Each node goes through five idempotent steps:

| Step             | What                                                                             |
| ---------------- | -------------------------------------------------------------------------------- |
| `Resolving`      | Acquire the binary into the binstore (skipped if cached)                         |
| `Stopped`        | `systemctl stop`                                                                 |
| `ConfigApplied`  | Re-stamp `prefs.toml` for squad / observer edits                                 |
| `BinaryReplaced` | Atomic symlink swap of `<workdir>/node` to the new tag                           |
| `Started`        | `systemctl start`                                                                |
| `NonceVerified`  | Poll the local REST API until nonce is within 5 of siblings or `is-syncing == 0` |

Idempotency means it's safe to re-run after a crash — every step is a no-op when the desired state is already in place.

## Downgrade

`upgrade --binary-tag <older>` reuses the binstore if the tag is cached, making the operation a local symlink swap + restart per node. If the tag isn't cached, mxnode re-acquires it.

Pruning policy is controlled by `[install].binary_keep` in [`config.toml`](/mxnode/configuration); the default of `3` keeps the last three tags.

## Self-healing inflight.toml

Every upgrade writes a `~/.local/state/mxnode/inflight.toml` lock at the start, updates `current_step` as the orchestrator advances, and removes it on completion. The file records:

* `op`, `started_at`, `strategy`, `selected` nodes
* `completed` nodes, `current` node, `current_step`
* `target_binary_tag`, `target_config_tag`, `target_proxy_tag`
* `[identity]` — owner pid + start token

Two purposes: **lock** (a second upgrade won't clobber a running one) and **crash breadcrumb** (`cat inflight.toml` after a death tells you what step the upgrade died on).

When `mxnode upgrade` starts and finds an existing `inflight.toml`, it classifies the recorded pid:

| Pid status    | mxnode behaviour                                                                   |
| ------------- | ---------------------------------------------------------------------------------- |
| Live          | Refuse — another mxnode is genuinely running.                                      |
| Dead          | Clear the file, log `→ clearing stale inflight.toml from previous run …`, proceed. |
| Indeterminate | Same as dead.                                                                      |

A crashed upgrade therefore self-recovers on the next invocation; rerunning the same `mxnode upgrade` command is the recovery procedure. Every per-node step is idempotent, so steps that already ran become no-ops the second time.

## Audit log

Every upgrade attempt appends a `MigrationEntry` to `state.toml`'s `migrations.entries[]`:

```toml theme={"system"}
[[migrations.entries]]
at = "2026-04-26T18:37:04Z"
to_binary = "v1.11.5"
strategy = "rolling"
trigger = "cli"
result = "ok"           # or "partial", "rolled-back"
duration_secs = 142
nodes_done = [0, 1, 2, 3]
nodes_failed = []
```

## Dry run

```bash theme={"system"}
mxnode upgrade --binary-tag v1.11.5 --dry-run
```

Resolves tags, builds the plan, prints what *would* run, then exits without touching anything.
