# Quarry ISO / installer strategy

**Status:** consume-gate on VM 141 is **PASS**. ISO spike **P0–P2.6** (helper image + BIB anaconda-iso + **unattended kickstart smoke PASS** → helper → cosign `install-quarry` `:stable`). Minimal-installer (Option B)
**spike started** — see [`docs/iso-spike.md`](iso-spike.md) for phases,
`scripts/iso/`, and `Containerfile.installer`. Do not block `:stable`
publish on ISO polish; Option A full offline ISO remains later/parallel.

Public image source of truth remains:

`oci.thepeoples.io/desktop/quarry:stable` (cosign-signed).

## Why ISO at all

A raw/qcow disk import is fine for estate QA. End users and bare-metal or
"empty Proxmox VM" flows need either:

1. A **full offline ISO** that already contains a Quarry deployment, or
2. A **minimal installer ISO** that boots a small environment, verifies
   cosign, and installs/pulls the latest signed `:stable` (or a pinned
   `git-<sha>`) onto disk.

## Option A — Full ISO (offline / air-gap friendly)

- Build with `bootc-image-builder` (or equivalent) from the same OCI
  ref we publish: `oci.thepeoples.io/desktop/quarry:<tag>`.
- Output: `iso` (and optionally `qcow2` for cloud/PVE import).
- Pros: one artifact; works without registry at install time; matches
  "burn and boot."
- Cons: large (~10 GiB+); every `:stable` bump wants a new ISO; easy to
  ship a stale ISO that disagrees with registry `:stable`.
- Testing: boot ISO in VM 141-class throwaway → install → first boot →
  `bootc status` shows the expected digest; optional later
  `bootc upgrade` against registry.

## Option B — Minimal installer (pull latest)

- Small live/installer image (Fedora bootc installer, custom Anaconda,
  or a tiny bootc "helper" root) that:
  1. Brings up networking
  2. Optionally configures a **local registry mirror** for
     `oci.thepeoples.io` (see below)
  3. Fetches `https://thepeoples.io/keys/oci.thepeoples.io.pub`
  4. `cosign verify` on the chosen **canonical** ref (default `:stable`)
  5. Select image: **variant picker** on the known catalog, or
     **full name+tag** when a custom/local registry override is set
  6. `bootc install to-disk` (or `bootc switch` on an existing bootc
     system) from the chosen ref (product path via mirror remap when
     configured)
- Pros: ISO stays small; always can target newest signed tag; closer to
  day-2 `bootc upgrade`.
- Cons: needs network + registry at install; more moving parts (cosign
  in the installer environment).
- Testing: same throwaway VM; assert verify fails closed on tampered
  tag; assert success path matches `docs/consume.md`.
- UX: interactive guided install by default; cloud-init optional when a
  seed is present (see below).

## Recommendation (working default)

| Phase | Artifact | Purpose |
| --- | --- | --- |
| Now | fedora-bootc disk → `bootc switch` `:stable` | Prove consume-gate on VM 141 |
| Next | **Option B** minimal installer spike | Primary user-facing install story |
| Parallel / later | **Option A** full ISO for named releases | Optional offline / release media |

Prefer B as the default product path so `:stable` on
`oci.thepeoples.io` stays canonical. Build A for tagged releases if we
want downloadable media that matches a specific `git-<sha>`.

## Local registry mirror (required)

Installers (Option B especially, and Option A when it later runs
`bootc upgrade`) **must** support pulling from a **local registry mirror**,
not only from public `oci.thepeoples.io`.

Canonical image identity stays:

`oci.thepeoples.io/desktop/quarry:<tag>`

Mirror is a *fetch path*, not a rename of the image:

- Installer accepts an explicit mirror endpoint (kernel cmdline,
  installer UI field, or config file), e.g.
  `registry_mirror=https://oci.thepeoples.dev` or a LAN pull-through /
  Zot mirror.
- Cosign verify still targets the **canonical** ref / digest as published
  for `oci.thepeoples.io` (same pubkey:
  `https://thepeoples.io/keys/oci.thepeoples.io.pub`). Do not weaken
  verify when a mirror is used.
- Containers/bootc transport should use mirror configuration that
  remaps `oci.thepeoples.io` → local mirror (podman
  `registries.conf` / `[[registry]]` rewrite, or bootc equivalent), so
  digests match what cosign attested.
- Fail closed if mirror is set but unreachable, or if mirrored content
  does not match the verified digest.
- Estate default for QA: allow pointing at private iterate Zot
  (`oci.thepeoples.dev`) or a LAN cache while public remains the
  product source of truth.

Testing: consume-gate on VM 141-class must include one run with
`registry_mirror` set and one without.

## Desktop environment variants

Default DE is **KDE Plasma** (gold standard). Pipeline and installer must
allow **one alternate DE** (TBD; **not GNOME**) on every desktop-capable
base — see `docs/desktop-environment-variants.md`.

Known-catalog entries should carry a `de` field (`kde` today). Alternate
DE appears as additional catalog rows / tags when chosen — never as plain
`:stable`.

## Variants vs custom/local mirror

### Known catalog (default / public path)

When the installer uses the **product** registry path
(`oci.thepeoples.io`, optionally remapped through an estate pull-through
that preserves that hostname), it **should present multiple Quarry
variants** if the catalog can enumerate them — e.g. desktop `:stable` /
`git-<sha>`, later `quarry-server`, NVIDIA flavors, etc.

- Prefer a curated allowlist from docs/flavors (or a small signed
  catalog file on the apex) over scraping every tag.
- Default selection: `desktop/quarry:stable`.
- User can still pin an explicit tag within a known variant.

### Custom / local mirror override

When the operator points the installer at a **custom or local mirror
registry** (hostname/path that is *not* the known product remap of
`oci.thepeoples.io`), the installer **must not** assume a variants
list:

- Do **not** offer the curated variant picker for that registry.
- Require the operator to enter the **full image name and tag**
  (or digest), e.g. `my.registry.lan/some/path/quarry:my-tag`.
- Cosign policy for custom registries is separate: either skip product
  pubkey verify only when explicitly opted out for private labs, or
  require a user-supplied pubkey — product default remains fail-closed
  verify against `oci.thepeoples.io` signatures when the canonical
  name is used via remap.

Rationale: we will not know which variants (or naming layout) exist on
arbitrary local registries.

### Summary

| Registry mode | Image selection |
| --- | --- |
| Public / known product (`oci.thepeoples.io`, optional hostname remap) | Variant picker + tag (default `:stable`) |
| Custom / local mirror override | Full image reference required (name + tag or digest) |

## Easy installer UX (default) + optional cloud-init

**Default product path is an easy interactive installer**, not a
cloud-init-only flow. Operators who never touch user-data should still
get a guided install: network, registry mode (known catalog vs
custom/local full ref), variant or image+tag, disk target, local user /
SSH key, timezone/hostname basics, then install.

### cloud-init (supported, not required)

- Images **should include** `cloud-init` (or equivalent) so VM/cloud
  and NoCloud seeds work when present.
- Datasources to support when used: **NoCloud** (CIDATA `user-data` /
  `meta-data` / `network-config`) and common hypervisor channels
  (Proxmox/QEMU).
- If cloud-init user-data is present, it may supply or override the same
  knobs the UI collects (users/SSH keys, hostname, network-config,
  write_files, runcmd) without making cloud-init mandatory.
- If no datasource/user-data appears, first boot must still be usable
  from what the interactive installer configured.
- Orthogonal to known-catalog variants vs custom/local full image+tag.
- Consume-gate: (1) interactive install without cloud-init seed succeeds;
  (2) optional check: NoCloud seed applies when provided.

Estate QA may still bake SSH keys via `bootc install
--root-ssh-authorized-keys`; that is not the primary end-user story.

## Implementation notes (when started)

Spike entrypoints (P0):
- `scripts/iso/install-quarry.sh` — interactive/flags helper
- `scripts/iso/catalog.json` — curated variants
- `scripts/iso/mirror-registries.conf.tmpl` — mirror remap
- `Containerfile.installer` — helper image stub
- `just iso-validate` / `.gitea/workflows/iso-spike.yml`
- Plan: [`docs/iso-spike.md`](iso-spike.md)


- Reuse platform cosign key material already published on the apex.
- Mirror support is mandatory for the chosen installer path (see above).
- Easy interactive UX is the default; cloud-init is supported but optional (see above).
- Builder: `quarry-builder-01` (`desktop-quarry`); do not bake ISOs on
  daily drivers.
- Publish ISOs via Shape A / existing thepeoples.io object patterns if
  they become public artifacts (separate from the OCI registry layout).
- Until Rekor exists, installer verify may need
  `--insecure-ignore-tlog` (same as `docs/consume.md`).
- Keep gaming NVIDIA ISOs (`bazzite-nvidia-open`) as a **flavor** later;
  v1 QA tracks the main Bazzite-cut `:stable` first.

## Exit criteria for "ISO strategy done"

1. Disk-image consume-gate on VM 141 is green.
2. One installer path (A or B) boots a throwaway VM to a cosign-verified
   Quarry system without hand-importing a raw disk.
3. Same installer path succeeds with a **local registry mirror** configured
   (digest matches direct-pull / cosign attestation).
4. Known-catalog path exposes a **variant picker**; custom/local registry
   override requires **full image name + tag** (no assumed variants).
5. Interactive installer UX works **without** cloud-init (local user /
   SSH key / hostname from the UI).
6. cloud-init is **supported** when a NoCloud (or equivalent) seed is
   present; not required for a successful install.
7. Docs: link from `docs/consume.md` / `docs/consume-gate.md` to the
   chosen installer README; flavors matrix notes media per edition;
   mirror + variant + optional cloud-init knobs documented.
