# Downloads<no value>

<!-- Generated by website/sync-docs.py from docs/install/downloads.md. Edit that file, not this one. -->

Every release publishes four things, all signed:

| | What it is | Use it for |
|---|---|---|
| The OS image | `ghcr.io/corium-os/corium` | Upgrading a node that already exists, and building your own artefacts |
| An installer ISO | `ghcr.io/corium-os/corium-iso` | Bare metal. Installs unattended |
| A qcow2 disk | `ghcr.io/corium-os/corium-qcow2` | Proxmox, KVM, libvirt. This is what [`deploy/proxmox/`](https://github.com/Corium-OS/Corium/tree/main/deploy/proxmox) takes as `DISK_IMAGE` |
| `cctl` | Attached to the release page | Managing nodes from your own machine |

**The exact commands for a given version, with its digests, are on that
version's release page:
[latest release](https://github.com/Corium-OS/Corium/releases/latest).** The
coordinates change every release, so they are published with the release
rather than written down here.

## Installing `cctl`

The client runs on your machine and never on a node. Archives are attached to
each release for linux and macOS, on amd64 and arm64.

With [mise](https://mise.jdx.dev), which picks the archive matching your
machine:

```bash
mise use -g 'github:Corium-OS/Corium[exe=cctl]@0.2.0'
```

Both the quotes and the version are load-bearing, and leaving either out fails
in a way that does not obviously point at them.

The quotes are for your shell: zsh treats `[...]` as a glob, so an unquoted
command never reaches mise at all — it reports `no matches found` before
anything runs.

The version is for two reasons. mise refuses a release younger than its `age`
setting allows, which is a supply-chain guard and means a freshly published
version is not installable for a while (`no versions found ... matching date
filter`). And an unpinned install resolves to the newest tag, **release
candidates included** — pinning is how you say you meant the stable one.

By hand, checking what you downloaded:

```bash
version=0.2.0
base=https://github.com/Corium-OS/Corium/releases/download/v${version}

curl -fsSLO "${base}/cctl_${version}_linux_amd64.tar.gz"
curl -fsSLO "${base}/SHA256SUMS"
sha256sum --check --ignore-missing SHA256SUMS
tar -xzf "cctl_${version}_linux_amd64.tar.gz"
```

`--ignore-missing` because `SHA256SUMS` lists every platform and you downloaded
one. Without it the check fails on the three archives you do not have, which
looks exactly like the failure that would matter.

The checksum file is signed with the same key as the OS image, so verifying it
verifies every binary underneath:

```bash
curl -fsSLO "${base}/SHA256SUMS.sig"
curl -fsSLO https://raw.githubusercontent.com/Corium-OS/Corium/v${version}/build/files/usr/share/corium/cosign.pub
cosign verify-blob --key cosign.pub --signature SHA256SUMS.sig SHA256SUMS
```

There is no Windows build. Not a decision against it — nobody has run `cctl`
there once, and a download page that lists a platform nobody has started is a
download page worth less on every other line.

From a checkout instead, which is what a release binary is built from anyway:

```bash
git clone https://github.com/Corium-OS/Corium.git && cd Corium
mise run build          # produces bin/cctl and bin/corium-agent
```

See [cctl](/docs/reference/cli/) for what it does.

---

## Three ways to the same bytes

```bash
# 1. With oras. One command, and it names the file for you.
oras pull ghcr.io/corium-os/corium-iso:0.1.0

# 2. Without oras. The registry issues pull tokens anonymously for public
#    packages, so curl is enough.
token=$(curl -s "https://ghcr.io/token?scope=repository:corium-os/corium-iso:pull" | jq -r .token)
curl -L -H "Authorization: Bearer ${token}" -o corium-0.1.0-x86_64.iso \
  https://ghcr.io/v2/corium-os/corium-iso/blobs/sha256:<the digest from the release notes>

# 3. From a browser, no tooling at all. The release notes carry the link.
https://<cdn>/corium-0.1.0-x86_64.iso
```

The third is a CDN copy. There is no index to browse, so the link for a given
version lives in that version's release notes rather than being guessable.

## Why the disk artefacts are not attached to the release

A GitHub release asset must be under 2 GiB. The installer ISO is about 2.4 GB,
so it cannot be one. The qcow2 would fit, but attaching only the disk artefact
that happens to fit would read as though the ISO had been forgotten rather than
left out deliberately, so both are published the same way.

That constraint turned out to be worth something. A file attached to a release
page is an opaque blob whose provenance ends at whoever uploaded it. An
artefact in a registry is addressed by digest and can carry a signature, which
is the difference between *this is the file we meant to give you* and *this is
a file*.

`cctl` is attached, because the same reasoning points the other way for it. It
is three megabytes, it is fetched by a person setting up a workstation rather
than by a machine, and the installers people already use — `mise use
github:Corium-OS/Corium[exe=cctl]@0.2.0'` — read release assets and not
registries.
Signing it as a blob rather than as an OCI artefact costs one extra file and
keeps it reachable by the tools that would actually go looking.

## Why there is a mirror

The registry is the right place to keep a release and an awkward place to send
someone who just wants to try an operating system: route 1 means installing a
tool, route 2 means a token and a digest. Neither is a link you can send to a
colleague.

So the same bytes are also served from a CDN. Two things are worth being clear
about:

- **It is allowed to be down.** If it is, the release still happened and the
  first two routes still work. Release notes offer a download link only when
  the upload and a read-back as an anonymous client both succeeded.
- **It is a convenience, not the release.** If the CDN and the registry ever
  disagreed, the registry is right. You do not have to take that on trust —
  the check below is what settles it.

Nothing published there is ever overwritten. Every file carries its version in
its name, so a URL that worked once keeps meaning the same bytes.

## Checking what you downloaded

Two different questions, and they need two different answers.

**Who built this, and from what?** That is the signature. It is attached to the
artefact in the registry, and verifying it needs no key you have to obtain from
somewhere first:

```bash
cosign verify ghcr.io/corium-os/corium-iso@sha256:<manifest digest> \
  --certificate-identity-regexp 'https://github.com/Corium-OS/Corium/.*' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com
```

A passing check tells you which workflow run produced the artefact, from which
commit and which tag, recorded in a public transparency log.

**Did I receive those exact bytes?** That is the hash. Every release publishes
the SHA-256 of each artefact in its notes:

```bash
sha256sum corium-0.1.0-x86_64.iso
# must equal the hash printed in the release notes
```

### Why there is no `.sha256` file next to the download

Because it would not prove anything. A checksum file served from the same place
as the file it describes is only as trustworthy as that place: anyone able to
replace one can replace the other.

The hash in the release notes is different. It is the digest of the artefact's
layer, and that digest is named inside the manifest that `cosign` signed. So
the chain closes:

```
cosign verify   ->  this manifest was produced by that workflow run
the manifest    ->  names this layer digest
sha256sum       ->  the file you hold has that digest
```

Each link is checkable on its own, and none of them depends on the download
host being honest. That is what makes it reasonable to fetch 2.4 GB from
whichever route is fastest for you, and why a checksum file sitting beside the
download would not have added anything.

## Building them yourself

Nothing above is required. `mise run artefacts` produces the qcow2, the raw disk
and the ISO locally from any image you can pull, including one you have
modified. It needs a Linux host and `sudo`, because
[`bootc-image-builder`](https://github.com/osbuild/bootc-image-builder) mounts
the filesystem it creates. See the [quick start](/docs/guides/quickstart/).

A raw disk image is not published. It would be roughly 5 GB, and the ISO
already covers bare metal.
