# CLI releases and the update pointer

> How the rnp CLI release pointer works: what GET /rnp/version/latest returns, how channels drive the auto-updater, and how releases are published and rolled back.

The `rnp` CLI does not ship with a hardcoded update location. It asks
ClutchCall for a **release pointer** — a small JSON document that names one
version, one channel, and one download URL. The install scripts read the same
pointer.

The **Releases** screen in the tunnel console is a read-only view of that
pointer. It shows what the pointer currently serves and what has been published
before it. Publishing does not happen from the console; it happens through the
release scripts.

## The release pointer endpoint

The pointer is served publicly at:

```
GET /rnp/version/latest
```

It is public on purpose: the CLI and the install scripts must read it before
they hold any credential. There is one pointer per product, and the only
product served here is `rnp-cli`, the cross-platform tunnel client.

The pointer carries these fields, which are the same fields the console renders
for the live version:

| Field     | Meaning                                                             |
| --------- | ------------------------------------------------------------------- |
| `version` | The version the pointer currently serves, rendered as `v<version>`. |
| `channel` | Which channel this version is published on.                         |
| `url`     | The download URL for this version's artifact.                       |

The console reads the pointer through `adminTunnel.releases.latest`, which
returns the live pointer keyed by product. The URL is shown with a copy button
so you can fetch the exact artifact the updater would fetch.

There is no separate "pointer is empty" state baked into the pointer itself.
The console distinguishes two cases that look the same once rendered:

- **No published pointer yet** — the query succeeded, but this product has no
  live pointer. The action is to publish one.
- **Releases unavailable** — the query returned no data at all. The action is
  to refresh, or investigate the control plane if it persists.

Treat these as different incidents. The first means nothing has shipped; the
second means you cannot tell what shipped.

## Channels and what the updater picks

Every published release names a channel. The pointer serves one version per
channel, and the CLI's auto-updater follows the channel it is configured for —
it takes whatever version the pointer currently names for that channel.

The console treats `stable` as the ordinary case and marks any other channel as
a warning, both on the live pointer and in each history row. If the live
pointer is on a non-`stable` channel, that is visible at a glance: the channel
tag next to the version changes tone.

Consequences worth internalising:

- Publishing a pointer is what makes a version reachable by the updater.
  Building an artifact is not enough.
- A non-`stable` pointer is a live pointer. Clients on that channel will pick
  it up.
- Because the pointer names exactly one version per channel, "the current
  release" is always a single answer, not a range.

## Release history

Alongside the live pointer, the console lists prior releases for the product
via `adminTunnel.releases.history`, scoped by `product`. Each row records:

| Column         | Source field                                        |
| -------------- | --------------------------------------------------- |
| Version        | `version`, rendered `v<version>`                    |
| Notes          | `notes`, shown under the version when present       |
| Channel        | `channel`                                           |
| Published      | `published_at`                                      |
| By             | `published_by`, or `—` when the publisher is unknown |

The row whose `version` matches the live pointer is tagged **live**. That is
the single source of truth for "what is the updater serving right now" — the
newest row is not necessarily the live one, because the pointer can be moved
backwards.

As with the pointer, an empty history and an unreadable history are separate
states: **No releases yet** means nothing has been published for this product,
while **History unavailable** means the history query returned nothing.

## Publishing with the release scripts

Publishing is done with the release scripts, not from this screen. The console
is deliberately read-only: it tells you what the pointer serves and what
shipped before, and nothing on it mutates the pointer.

The shape of a publish is therefore:

1. Build and upload the artifact for the version.
2. Run the release script to publish a pointer naming that version, its
   channel, and its artifact URL.
3. Reload the Releases screen and confirm the live pointer shows the version
   and channel you intended, and that the new row carries the **live** tag.

Step 3 is the acceptance test. Until the pointer moves, the updater still
serves the previous version regardless of what has been built or uploaded.

## Rolling back a bad pointer

Rollback is a pointer move, not a deletion. Because the pointer names one
version per channel and the history retains prior releases, you roll back by
publishing a pointer that names a known-good earlier version on the same
channel.

To roll back:

1. On the Releases screen, find the last known-good row in the history table
   for that product and channel. Copy its version.
2. Publish a pointer for that version on the same channel using the release
   scripts.
3. Confirm the live pointer and the **live** tag have moved to that row.

Points to keep in mind:

- Clients that already updated are not reverted by moving the pointer. The
  pointer governs what the updater fetches next, not what is already installed.
- The rolled-back-to version stays visible in history as a prior release; the
  **live** tag is what distinguishes "was published" from "is being served".
- Keep the `notes` on a rollback pointer meaningful. The history table shows
  notes under the version, and it is the fastest signal for the next person
  asking why the live version is older than the newest row.

## Where QuickDesk releases live now

QuickDesk releases are no longer managed here. They moved to the QuickDesk
operator console, at `quickdesk.<brand>.dev`, and are served by the
`adminQuickdesk.releases` procedures.

The tunnel Releases screen lists only `rnp-cli`. If you are looking for a
QuickDesk version pointer or QuickDesk release history, use the QuickDesk
operator console.

## Related

- [Authentication](/concepts/authentication) — the pointer endpoint is public; the console procedures are not
- [Telemetry](/platform/telemetry) — correlating an update rollout with operational data
