security watch sees runtimes (R-1327) and DooPlex (R-1328); HTTP2Shell rows R-1329–R-1332; R-1326 closed
gates / gates (push) Successful in 3m27s

- scripts/image_runtime.py: Node / Python Brotli / brotlicffi / apk / dpkg read from an image's registry config and
  layers (no pull; HEAD for the nightly digest; zstd via the zstd program where present; quay.io, index.docker.io).
- security_watch.py: a „runtime" source (security_watch_runtimes.json), cached by digest in state.json (154 images,
  filled on DooPlex tonight); ghcr.io tag lists are paged; the DooPlex group (own block, homelab next-step).
- dooplex_inventory.py + documentation/security-watch/dooplex-workloads.txt (89 images, homelab-manifests 5aeec8d).
- Tests W10–W12 (58 checks) + test_image_runtime.py (13), each red-proved.
- audits/http2shell-2026-10-27: the Home Assistant lane walk (proven), HOMELAB-LIST.md (19 to update), the run log.
- 08 §11 design line; 09 §3 decision 276 (HA monthly = same main version; decided by CC unattended).
This commit is contained in:
admin committed 2026-10-27 23:23:02 +01:00
1 parent 503692a1da
commit f5a4995da5
21 files changed
+3498 -29

No files matched your search

+29 -4
View File
@@ -230,10 +230,35 @@ def judge(items):
return problems
DOOPLEX_LIST = os.path.join(FELHOM, "documentation", "security-watch", "dooplex-workloads.txt")
def dooplex_items(path=DOOPLEX_LIST, catalog_sources=None):
"""The DooPlex (homelab) workloads (R-1328) — a LIST from scripts/dooplex_inventory.py, group „dooplex": read and
mailed to the operator only; nothing on DooPlex is changed from here. An image the catalog also ships reuses the
catalog's upstream line; any other is a stated gap (the watch still reads its registry tags and its runtime)."""
if not os.path.isfile(path):
return []
items = []
for ln in _read(path).splitlines():
if not ln.strip() or ln.startswith("#"):
continue
ref, _, where = ln.partition("\t")
image, tag = split_ref(ref.strip())
items.append({"group": "dooplex", "owner": where.strip()[:80] or "DooPlex", "image": image, "tag": tag,
"where": "DooPlex (homelab)", "sources_text": (catalog_sources or {}).get(image) or
"gap:DooPlex workload — no notice source mapped; registry and runtime only"})
return items
def full_inventory(felhom_root=FELHOM, catalog=None, controller=None):
catalog = catalog or os.path.join(WORKSPACE, "app-catalog-felhom.eu")
controller = controller or os.path.join(WORKSPACE, "felhom-controller")
items = controller_items(controller) + hub_items(felhom_root) + catalog_items(catalog)
apps = catalog_items(catalog)
items = controller_items(controller) + hub_items(felhom_root) + apps
# the catalog's upstream lines, keyed by image, for the DooPlex images that are the same program
cat = {it["image"]: it["sources_text"] for it in apps if it.get("sources_text")}
items += dooplex_items(catalog_sources=cat)
problems = judge(items)
return items, problems
@@ -277,9 +302,9 @@ def main(argv):
for it in items:
print("%-6s %-22s %-48s %-24s %s" % (it["group"], it["owner"][:22], it["image"][:48], it["tag"][:24],
it.get("sources_text") or "-- MISSING --"))
print("%d pins (%d box/build, %d app); %d problems" % (
len(items), sum(1 for i in items if i["group"] != "app"),
sum(1 for i in items if i["group"] == "app"), len(problems)))
print("%d pins (%d box/build, %d app, %d DooPlex); %d problems" % (
len(items), sum(1 for i in items if i["group"] not in ("app", "dooplex")),
sum(1 for i in items if i["group"] == "app"), sum(1 for i in items if i["group"] == "dooplex"), len(problems)))
return 1 if problems else 0
print(__doc__)
return 2