# Releases & previews

Staging and publishing are two separate verbs — every release gets an immutable version and its own preview URL.

Building and going live are two verbs, not one. `release` uploads a directory
and creates an immutable version; `deploy --to` decides which version production
serves. Adding `--deploy` to a release does both in one step, which is the
common case.

Without `--deploy` the release is *staged*: it exists, it's fully uploaded, and
it's reachable at its own preview URL — but production keeps serving whatever it
was already on. Check the preview, then promote it.

```console title="stage, check, promote"
$ one9x pages release ./dist --site mysite
✓ v9 staged · production still on v8
→ https://v9--mysite.one9x.app/
$ one9x pages deploy --site mysite --to v9
active version v8 → v9
✓ Live · nothing re-uploaded
```

## Preview URLs

A preview URL is `v<n>--<site>.one9x.app` — the same content, the same edge, the
same certificate, served from the same immutable files. It costs nothing extra
to keep around: previews share every unchanged file with production.

> **Preview URLs are public.** Treat them as unlisted rather than private —
> anyone with the link can open one. There is no crawl path to them, but there
> is no login on them either.

A preview only exists once a release does — but it does **not** wait for
anything to go live. A site's very first release is previewable while it is
still staged, which is what makes `one9x pages release` without `--deploy`, and
the GitHub Action's preview comment on a pull request, work on a site nobody has
published yet.

A site with no releases at all has no preview to serve. Its platform host says
so rather than 404ing into a void: the page names the site, says nothing is live
yet, and gives you the command that publishes.

## Naming a release

Every release gets an automatic per-site number — `v1`, `v2`, `v3` — and you can
add your own label with `--tag`. Both work as a `--to` target, as does a
version-hash prefix.

```console title="tags"
$ one9x pages release ./dist --site mysite --tag prod
✓ v10 staged · tag prod
$ one9x pages deploy --site mysite --to prod
✓ Live
```

## How long releases are kept

Retention is count-based, not time-based, so how far you can roll back doesn't
depend on how often you ship. The last 5 untagged releases are kept on the free
tier — paid tiers keep more — and older ones age out.

Tagged releases never age out and don't use up one of those slots, but there's a
cap of 5 tags per site. At the cap a tagged release **fails with an error**
rather than quietly dropping one of your tags. `one9x pages releases rm` frees a
slot.