# pyre CLI changelog

All notable changes to the `pyre` command-line client. Versions follow
semver; the tag is `cli-v<version>`.

## 1.0.25 — 2026-09-28

- `pyre appliance mount` / `remove`: the commands are for the OS the
  appliance was deployed on (recorded at deploy), not the OS running
  pyre; `--os` still overrides, and the first line says which OS and
  why. From a Mac, an NFS appliance on a Linux client had printed the
  macOS block. The `mount --help` example no longer shows a literal
  `\n`.
- Installer (`install.sh`): installs into `~/.local/bin` by default, no
  sudo and no password; `pyre update` and `pyre uninstall` then need
  none either, the same per-user model as the Windows installer.
  `PYRE_INSTALL_DIR=/usr/local/bin` (or a root shell) keeps the
  system-wide install. An older copy in `/usr/local/bin` is pointed out
  at the end of the install so it cannot shadow the new one unnoticed.

## 1.0.24 — 2026-09-27

- `pyre admin hubs list`: a SERVING column -- for the primary, how many of
  its WireGuard peers shook hands in the last 3 minutes over how many it
  has (`recent/peers`); `0/N NOBODY` plus a notice naming inbound UDP
  51820 when a primary serves nobody. A primary whose firewall dropped
  every handshake had looked healthy for 14 minutes.
- `pyre admin hubs poll <hub>`: prints the health detail (role, tenants,
  tunnel identity, and for the primary whom it serves).
- Gate: run 23, every check of the release gate against the live fleet.

## 1.0.23 — 2026-09-26

- `pyre admin hubs replication`: a table (name, role, lag s, measured,
  pending, speed, eta, data, last pass). The lag is the worse of the
  measured frontier delta and the time since the last successful pass,
  as the hub's own promote guard decides; a replica whose passes the hub
  refused shows `FAILED: delete guard ...` with the count.
- `pyre admin hubs accept-deletions <hub>`: let a replica's next data
  pass mirror a primary that shrank (the delete guard at 100% for one
  pass), the answer to a refused pass without a shell on the hub.
- `pyre view open`: exits 1 with the appliance's reason when the instant
  is no longer version-backed, naming the nearest archived instants.
- `pyre volume grow/shrink`: whole GiB, and the nearest sizes named when
  not; sizes need a unit and are at least 1 GB on create too.
- `pyre view list` and `pyre recovery list`: every column filled.
- Every API call errors out after 30 s with the status and the next
  step (`build-status`, `promote-status`, `volume status`, ...).
- Behind the CLI, found by the release gate and fixed in the appliance,
  the hubs and the control plane: a resize no longer restarts the SMB
  service (a reconnected Linux client could not write afterwards) and
  closes client connections again after the remount; a promoted
  recovery attaches at the copy's real size; a released NFS view drops
  its export block; a dead iSCSI target from a killed container is
  swept before the next appliance's; a refused replication pass is
  visible on the dashboard and in the CLI; the onboarding preflight no
  longer deletes a built hub's tunnel firewall rule, answers its probe
  only to real probes, runs in a fifth of the time, and a refused build
  leaves no stale "done"; an account from before a fleet rebuild can
  be deleted; a hub removed from the control plane has its ssh master
  closed.
- Gate: run 22, 370 checks against the live fleet, every CLI call under
  30 s.

## 1.0.22 — 2026-09-26

- The release gate (`cli/e2e/release-gate.sh`): one run of the whole
  product as a customer and an operator meet it, on the real fleet;
  `cli-publish` refuses a tag without its green report. First gated
  release: 242 checks.
- `pyre appliance status` converges after a deploy; views and
  recoveries list what the API sends; recovery listings reconcile
  behind the response; a demote starts the first replication pass at
  once.

## 1.0.21 — 2026-09-25

- `pyre appliance deploy`: asks which OS and prints that OS's install
  step (the Pyre VM on Linux) before the run block.

## 1.0.20 — 2026-09-25

- `pyre admin`: hidden from a customer's help and completion; a
  customer's token gets a plain refusal naming it.

## 1.0.19 — 2026-09-25

- `pyre admin hubs build`: the primary preflights the machine first and
  refuses with the FAIL lines; the CLI prints its own next step.

## 1.0.18 — 2026-09-25

- Admin CLI sanity run (`cli/smoke/admin-hubs.sh`): the real API and CLI
  through the whole hub lifecycle against a fake fleet, in CI.

## 1.0.17 — 2026-09-25

- `pyre admin hubs onboard-key`: prints the block the new machine must
  run (sudo, the primary's onboarding key, the management key, root
  login off), not the bare key.

## 1.0.16 — 2026-09-25

- `pyre admin hubs remove`: sends only the flags that are set (an empty
  `force=` was a 422).

## 1.0.15 — 2026-09-25

- `pyre admin hubs bootstrap`: the first hub of a fleet can be built
  from the CLI (`--start`, `bootstrap-status`, `--rebuild`).

## 1.0.14 — 2026-09-25

- `pyre export`: the physical export. `prepare` (operator, on the hub)
  copies a volume's blocks off the store into per-medium directories
  with an index, README and SHA256SUMS; an encrypted volume leaves as
  the appliance's own ciphertext, a volume that was never encrypted is
  sealed to a public key from `pyre export keygen`, so nobody who
  prepares or carries the media can read it. `show` and `verify` need
  no key; `extract` copies the files out of the media on Linux, macOS
  and Windows with no mount, no image and no Docker (a built-in
  read-only ext4 reader that handles sparse files); `open` writes the
  raw disk image. A wrong key is refused by fingerprint before a block
  is read. Format and runbook: docs/PHYSICAL-EXPORT.md.

## 1.0.13 — 2026-09-24

- `pyre volume rediscover` reports volumes whose deleted row was just
  marked encrypted: a volume older than the storage's own record is
  known to be encrypted by the key-check object beside its blocks, so
  its row now says so and deploy asks for the key file.

## 1.0.12 — 2026-09-24

- `pyre volume rediscover`: your storage now carries its own record of
  every volume (written at create, resize and every appliance attach),
  and this lists any volume it describes that the account has no
  record of -- storage attached with a recovery key from before
  records were kept, a volume lost with a database -- as a deleted
  volume, ready to restore. `pyre account reclaim` runs it by itself
  and names what it found. A volume older than that record is
  described from its first block, type unknown: listed as smb, and
  `pyre volume deleted restore <id> --type nfs|iscsi` says what it was.
- `pyre volume deleted list` shows each volume's type and whether it is
  encrypted (which rows need their key file).

## 1.0.11 — 2026-09-24

- `pyre account close --mode preserve` keeps every volume as a deleted
  volume (the control plane change of the same day) and names them;
  `pyre account reclaim` then says how many came back and points at
  `pyre volume deleted list` / `deleted restore <id>` /
  `appliance deploy <volume> --rebuild`, instead of "create a volume".
  A tenant preserved before 2026-09-24 has no volume records; the
  message then explains the recreate-by-name path.

## 1.0.10 — 2026-09-24

- `pyre account reclaim --key <recovery key>`: attach the storage of an
  account closed with `account close --mode preserve` to the account
  this token belongs to. That account must have nothing of its own yet
  (no storage, volumes, stores or appliances); the key is accepted in
  any spelling (case, dashes, spaces) and is spent on success, never
  by a refusal. Until now the key could only be entered on the
  password sign-up form, which a "Sign in with Google" account never
  sees; the dashboard has the same page under "Recover it with your
  key".

## 1.0.9 — 2026-09-23

- `pyre volume deleted restore` now says what comes next: `pyre
  appliance deploy <volume> --rebuild`. Deleting a volume revokes its
  appliance's identity, so a container started again from its old
  state comes up unable to connect; the rebuild form's fresh token
  re-enrols it and the state volume and key file can stay.

## 1.0.8 — 2026-09-23

- After `pyre volume delete` of a volume that had an appliance, the CLI
  says how to clear it from the computer (`pyre appliance cleanup
  --names <volume>`), as the dashboard's redirect to /cleanup does.
- A command given the name of a DELETED volume (`appliance remove`,
  `appliance disconnect`, `volume show`, ...) now says so: when it was
  deleted, the cleanup line for its appliance, and the `deleted restore`
  line that brings it back. Before, "no volume named" left the customer
  hunting for a way to remove a stale appliance.
- `pyre appliance cleanup` with no `--names` takes every deleted volume
  of the account (logged in). Logged out it still needs `--names`.

## 1.0.7 — 2026-09-23

- `pyre volume create --encryption customer` works and is documented
  (the 1.0.0–1.0.6 example said `customer-key`, which the API refuses;
  it is now accepted as an alias of `customer`, as are `none`/`off`).
  Any other value is refused with exit 2 and the two choices, before
  anything is sent. After creating an encrypted volume the CLI says
  where the key file will be made (`pyre appliance deploy`,
  `~/.pyre/<container>.key`) and that losing it makes the volume
  unrecoverable.
- Every input is checked against the API's own rule before a request
  is built: ids in paths are numeric; volume, recovery and view names,
  labels, policies, points in time, `--since`, `--type` (now also
  `nfs+smb`), `--os`, `--cache-mb`, `--store`, store fields, emails
  and URLs (`--api`, `api_base`, `download_base`, `--endpoint`) are
  validated; a bad value is a usage error (exit 2) naming the choices.
  Validated path segments are additionally percent-escaped.
- `pyre store probe|add`: the provider list is the API's (`aws`,
  `wasabi`, `minio`, `weka`, `other`; default `other`). The old
  default `s3` and the documented `backblaze` were refused by the
  server on every call.
- Exit codes: cobra's own parse errors (`unknown shorthand flag`,
  `flag needs an argument`) are usage errors (exit 2, was 1); a stray
  word after a group command (`pyre volume nosuch`) is a usage error
  (was help with exit 0).
- A server-side validation answer (HTTP 422) is rendered as
  `field: message`, not as a JSON dump.
- Tests: the customer command matrix (`internal/cmd/permutations_test.go`,
  over 220 rows: valid, invalid and injection cases against a fake control
  plane) and new smoke checks against the real API; design and
  pass/fail criteria in `docs/CLI-TESTING.md`.

## 1.0.6 — 2026-09-23

- Encryption is shown as "customer key" or "none" from the API's
  `encryption` field (server section 10); a server that does not send
  the field shows "unknown" instead of a false "none". Also fills the
  `pyre volume list` ENCRYPTION column.

## 1.0.5 — 2026-09-23

- `pyre update` says which file it replaces ("replacing /path/pyre"),
  checks that directory is writable BEFORE downloading and names it in
  the error (with `sudo pyre update` only when that path needs it), and
  after updating lists any other `pyre` on PATH it did not touch.
  `pyre version` shows the binary's path and any others on PATH.
- Sizes match the dashboard: decimal GB with one decimal
  (26843545600 bytes → 26.8 GB); appliance measurements that arrive
  as strings are formatted too (no more raw byte counts).
- `volume show` / `volume status`: the Offline line appears only while
  the appliance is offline; buffer fill and disk-low facts sit on the
  Sync line otherwise, and an empty buffer is not mentioned.

## 1.0.4 — 2026-09-23

- `pyre volume show` is readable: one line each for Volume, Appliance,
  Sync, Offline, Storage, Errors and Measured, sizes in the dashboard's
  units, times local. Nothing in table mode prints raw JSON any more
  (nested data becomes dotted lines); `-o json` is unchanged, `-o plain`
  flattens to `key<TAB>value`.
- New `pyre volume status <volume>`: the live block alone, with
  `--watch [--every 5s]` redrawing until Ctrl-C on macOS, Linux and
  Windows (no watch(1) needed).
- `pyre appliance status` uses the same rendering.
- `pyre completion` without a shell now says
  `pyre completion <bash|zsh|fish|powershell> — e.g. pyre completion zsh`.
- `pyre update` rewrites the completion files it finds (bash, zsh,
  fish, PowerShell) with the new binary, clears stale `~/.zcompdump*`,
  and says when zsh completion is installed but `~/.zshrc` does not
  load it.

## 1.0.3 — 2026-09-23

- Homebrew: the generated formula carries a top-level `url` so current
  Homebrew accepts the tap (it validates every platform, Linux arm64
  included, and refused 1.0.2's formula with "requires at least a URL").
  No change to the binary.

## 1.0.2 — 2026-09-23

(1.0.1 was tagged on the same day but never published: its installer
still had the PATH gap below. Everything listed here is new since 1.0.0.)

- install.sh: installs into /usr/local/bin through sudo (the password
  prompt works under `curl | sh`); the ~/.local/bin fallback now adds
  itself to PATH and the zsh completion dir to fpath in your shell rc,
  and ends with "Open a new terminal (or run: source ~/.zshrc)". Found
  by the first real install on a Mac: `pyre` was not on PATH.

- Token lifecycle: `pyre auth token create --expires 30d|1y|never`
  (server default 90 days), `list` shows state and expiry, `remove`
  deletes a revoked or expired row (`--force` revokes first), `prune`
  drops every such row. Operators: `pyre admin tokens
  list|revoke|remove|prune` across tenants.
- `pyre uninstall` now removes every man page it installed
  (`pyre-<command>.1.gz`), not only `pyre.1.gz`.
- Install docs: `brew trust pyredata/pyre` before `brew install pyre`
  (current Homebrew refuses formulae from an untrusted tap).

## 1.0.0 — 2026-09-23

First packaged release, a rewrite in Go of the 0.1 Python prototype.

- One static binary per platform: macOS (Apple Silicon and Intel),
  Linux x86-64, Windows x86-64.
- Install channels: apt repository (signed), Homebrew tap, `curl | sh`,
  PowerShell one-liner, Scoop manifest, plain archives.
- `pyre update` (signed releases, in-place replace; defers to apt /
  Homebrew / Scoop when they installed it) and `pyre uninstall`.
- Shell completion for bash, zsh, fish and PowerShell; man pages.
- Scripting: `-o json` / `-o plain`, `--yes`, stable exit codes,
  `pyre batch` to run a file of commands with one login.
- Profiles for more than one control plane (`--profile`, `pyre config`),
  `--ca-cert` / `--insecure` for self-hosted managers, every host a
  config value.
- Customer commands: auth (token per user), volume (incl. deleted
  volumes), restore-points, restore, recovery, view, history, snapshot,
  appliance (deploy/mount/remove/status/disconnect/flush/cleanup),
  store (bring your own S3), account.
- Operator commands (`pyre admin`): hubs (scan/add/preflight/build/
  promote/demote/remove/bootstrap/...), users (approve/reject/delete),
  tenants.
