Walkthrough: image catalog¶
The image catalog is what tells the fleet “what can I flash”. Each
entry is a URL (oras:// or https://) plus optional metadata: a
sha256 (when published alongside the image), a human display
name, a format, a size hint, a one-line description.
Since v0.66.0 bty-web does not own the catalog. Withcache does.
Bty-web reads it from <withcache>/catalog (JSON envelope) through
the in-process WithcacheCatalog snapshot and refreshes it on
demand. Operators add + download entries on the withcache UI; bty
sees only entries whose bytes are already cached (withcache 0.11+
filters GET /catalog to downloaded rows). Bty owns machine
bindings (which MAC gets which catalog entry) and the flash / boot
plan; withcache owns the bytes.
Catalog identity: ref vs sha256¶
Two distinct identifiers on every catalog entry:
ref=sha256(canonicalise_src(src)). A 64-hex digest of the canonical URL. Always present (it’s pure math on the URL). This is what machine bindings target – a rolling oras tag’s ref stays stable across re-pushes, so binding to the tag survives the next rebuild upstream.sha256= the observed content hash of the image bytes. Optional. Rolling-tag entries (oras://...:latest,releases/latest/download/...) have no stable content sha at catalog-publish time and carrysha256 = None. Pinned entries carry the digest the publisher computed.
When an entry has a sha256 (or the source is an oras:// ref, whose
layer digest is resolved at flash time), bty verifies the streamed
bytes against it during the flash and aborts with an error on
mismatch, so a corrupted or tampered download never silently lands on a
disk. The hash is computed in the pipe (curl | tee | sha256sum | dd),
adding no measurable overhead. Entries with no known sha flash without
verification. For PXE flashes the server passes the content sha to the
live env as disk_image_sha in the boot plan, so verification holds
even when the image is served from a cache or direct origin whose URL
doesn’t carry the digest.
The merge collapses entries by ref: two manifest entries with the
same canonical src are one row. Same content under multiple refs
(operator catalogs the same image as both oras://a and
https://b) renders as two rows – different provenance, even if
the bytes match.
Adding entries (on withcache)¶
Since v0.66.0 the catalog lives on withcache; bty-web has no add /
upload / fetch forms of its own. Open the withcache UI (per your
deploy, typically http://<host>:8081/ui/catalog) and use the
subnav’s three inline actions:
Add ORAS — paste an
oras://ghcr.io/owner/repo:tagreference. Withcache resolves the manifest and prefills format / size / arch from the layer annotations.Add HTTPS — paste a plain
https://URL. Withcache derives the entry name from the URL basename and infers the format from the file suffix. sha256 + size land when Download completes.Fetch default — re-parse the currently-configured catalog source (Settings > Catalog source) and merge its entries in.
Then click Download on each row you want bty to see. The
trio-facing GET /catalog API filters to downloaded rows only;
bty’s next WithcacheCatalog.refresh() pulls them in.
There is no upload form for image bytes on either side.
Manifest schema¶
version = 1
[[images]]
name = "nosi-debian-sysdev-x86_64.img.gz"
src = "oras://ghcr.io/safl/nosi/debian-sysdev:latest"
sha256 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
format = "img.gz" # optional: auto-detected from name
size_bytes = 1234567890 # optional
description = "Debian sysdev image, rolling" # optional
Required fields per entry: name, src. Optional: sha256,
format, size_bytes, description. name must be unique within
the manifest. sha256 (when set) must be a 64-char lower-case hex
string.
Validate before deploying:
python3 -c 'import sys; from bty import catalog; catalog.load_source(sys.argv[1])' /path/to/catalog.toml
Browser UI¶
Catalog inspection + mutation moved to withcache in v0.66.0.
Bty-web’s /ui/machines/{mac} image picker lists the current
snapshot (backed by WithcacheCatalog). To add or delete
entries, open the withcache UI at <BTY_WITHCACHE_URL>/ui/catalog
and use its Add ORAS / Add HTTPS / Delete controls. Bty picks up
the new state on the next WithcacheCatalog.refresh().
CLI¶
The bty console script (the wizard) is the operator-facing flash
surface:
bty # interactive wizard, local image-root only
bty --catalog <URL> # interactive wizard with the catalog pre-loaded
bty --server <X> --mac <Y> # server-driven via GET <X>/pxe/<Y>/plan
--catalog accepts a local TOML path, an HTTP URL, an oras://
reference, or a bty-web instance’s /catalog.toml. Local-only mode
(no overlay) scans BTY_IMAGE_ROOT (or /var/lib/bty/images) on
the host running bty – typically the USB-stick BTY_IMAGES
partition or a developer’s directory – and shows whatever flashable
files are there. This is the bty CLI’s image root, distinct from
the deleted bty-web image-store.
HTTP API (catalog surface)¶
Since v0.66.0 bty-web has no add / delete / import routes for catalog entries. All catalog mutation lives on withcache. Bty-web serves the read side needed by the live env + operator UI:
Endpoint |
Method |
Purpose |
|---|---|---|
|
GET |
unified listing of bindable images (backed by |
|
POST |
force |
All endpoints are auth-gated (the same session cookie as the browser UI). Add + Download + Delete happen on the withcache UI; open Settings > Upstream to see the current URL bty-web reads from.
Where things live¶
File / dir |
Owner |
Purpose |
|---|---|---|
withcache’s |
withcache |
cached image bytes, keyed by origin URL |
withcache’s |
withcache |
audit trail for catalog / download activity |
bty-web’s |
bty |
MAC bindings + boot mode + last-seen state |
bty-web’s |
bty |
audit trail for machine / netboot / settings activity |
upstream origin |
publisher |
canonical source when withcache is cold |
Bty-web stores no image bytes; withcache holds them. See walkthrough-image-store.md for the full picture.
Environment variables¶
Var |
Default |
Purpose |
|---|---|---|
|
|
state directory (state.db, session-secret) |
|
(unset) |
withcache HTTP endpoint the |
|
|
GitHub repo the “Fetch netboot artifacts” button pulls kernel/initrd/squashfs from |