{% extends "ui/_layout.html" %} {% block title %}Images - bty-web{% endblock %} {% block content %}

Images

{{ unified|length }} entr{{ "y" if unified|length == 1 else "ies" }} | image-root {{ image_root }}
Add image by URL
{# type="text" rather than type="url": some browsers reject non-http schemes (oras://) under HTML5 URL validation. Server-side Pydantic validation still runs (regex covers http/https/oras schemes). #}
Image URL is required. For http(s):// URLs, providing a SHA URL (a sha256sum-style manifest) enables per-machine binding; without it the entry is URL-only (flashable, but not bindable to a machine). For oras:// refs the server resolves the OCI manifest at add time and uses the layer's content-addressed digest -- no SHA URL needed (it's ignored).
Upload image file
{# Streams the file via XHR PUT to /images/ -- the existing API endpoint that takes ``application/octet- stream``. JS handles progress + auth (cookie, same- origin); on success we reload so the new file shows up in the catalog table below. The HashManager picks up the upload via PUT /images/'s post-write hook (auto-enqueue), so a SHA appears in seconds. #}
0%
Pick a file -- it streams directly into the image root via PUT /images/<name>. SHA-256 is auto-computed in the background after upload completes.
{% if not unified %}
No images yet. Add one with the form above, drop a .qcow2 / .img / .img.zst / .img.xz / .img.gz / .img.bz2 file under {{ image_root }}, or author a catalog.toml manifest. Refresh the page after either to see them here.
{% else %}
Unified catalog (dir scan + manifest, SHA-keyed)
{% for u in unified %} {% endfor %}
Name(s) Ref Format Sources Cached Action
{% for n in u.names %} {{ n }}{% if not loop.last %}, {% endif %} {% endfor %} {% if u.sha256 %} {{ u.sha256[:12] }}... {% else %} unhashed {% endif %} {{ u.format or "?" }} {% for s in u.sources %} {% if s.kind == "local" %} {{ s.location }} {% elif s.kind == "url" %} {{ s.location }} {% else %} {{ s.location }} {% endif %} {% if not loop.last %}
{% endif %} {% endfor %}
{% if u.cached %} cached {% else %} available {% endif %} {% if not u.sha256 %} {# Unhashed dir-scan file: a hash button enqueues via the HashManager (background, single-worker by default). #} {% elif not u.cached %} {# Manifest-only entry not yet in cache: a fetch button enqueues via the DownloadManager. #} {% else %} - {% endif %} {# Operator-added catalog_entries rows are identified by a ``url``-kind source. Show a Delete button to remove via the ``DELETE /catalog/entries?src=...`` API. #} {% set url_src = u.sources|selectattr('kind','equalto','url')|map(attribute='location')|first %} {% if url_src %} {% endif %}
Downloads refreshing...
Name Status Progress Bytes Action
No downloads yet.
Hashes background SHA-256 of dir-scan files; default 1 at a time so small hardware (Pi / NUC) stays responsive
Name Status Progress Bytes Action
No hashes yet.
{% endif %} {# Image-relevant event slice (uploads, hashes, catalog adds / deletes). The /ui/events drill-down filters to subject_kind= image; catalog rows are picked up via the broader filter at /ui/events itself. #} {% with events=image_events, title="Recent image-catalog activity", link_to_full="/ui/events?subject_kind=image" %} {% include "ui/_events_card.html" %} {% endwith %} {% endblock %}