Files
felhom.eu/documentation/audits/catalog-sweep-2026-10-24/tools/table.py
T
admin 67053335d1
gates / gates (push) Successful in 13m17s
Catalog sweep 2026-10-24: all 65 catalog apps on a fresh box (52 work, 10 with a problem, 0 broken); register 208 -> 217 (R-1269..R-1277 opened; notes on R-739, R-1267); STATUS + D46; map B1 re-checked; evidence
Every catalog app installed, used, backed up, removed keeping data, reinstalled, restored and removed with data on a
fresh box from the public ISO (demo-hp VM 9360, Tester4), endpoint-level. Worst: a reinstall's capture rewrites the
kept unit so its restore breaks the app (R-1269, P2), the restore still reports success (R-1270, P2), and „use my kept
data" loaded another box's future-dated database (R-1271, P2). Drill catalog fixes for the morning list (n8n usage
data; OpenGist 256M, proven by a cgroup probe). VM 9360 and LXC 9361 destroyed; hub host Tester4-aa9466 left for the
operator. REPORT-catalog-sweep-2026-10-24.md.
2026-10-25 00:13:05 +02:00

109 lines
8.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""table.py — the README's 65-row table: the measured facts from each <app>/record.json, and the VERDICT set by hand
after reading that app's evidence (VERDICTS below — the reviewer's call, not the summarize.py hint)."""
import json, os, sys
EV = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
W, P, B, N, T = "works", "works with a problem", "broken", "not installed", "not testable on this box"
# app: (verdict, one line, row/fix)
VERDICTS = {
"actualbudget": (W, "the seeded item back after the restore", ""),
"adventurelog": (P, "works; but the keep-backups → reinstall → restore path breaks it (DB password from the new install over the old data) — reproduced in its re-run", "R-1269, R-1270"),
"audiobookshelf": (W, "the seeded item back after the restore", "drive names: R-1275"),
"bentopdf": (W, "stateless (nothing to keep); installs, answers, removes clean", ""),
"bookstack": (W, "the seeded item back after the restore", ""),
"calcom": (W, "the seeded item back after the restore; 2 start-up messages of the image itself", ""),
"calibre-web": (W, "the book (its own upload) is listed again after the keep-data reinstall; the fixture's download check gets 401 (its sign-in for /download, not the app); the admin name is the box's generated one (re-run 2)", "R-1277"),
"claper": (W, "the seeded item back after the restore", ""),
"code-server": (W, "first install refused by the registry's rate limit, told as „exit code 1”; the re-run installed and worked (no seed: the editor has no API)", "R-1273"),
"crafty-controller": (W, "the seeded item back after the restore", ""),
"dawarich": (W, "the seeded item back after the restore (every-5th: restore also before the reinstall)", ""),
"docmost": (W, "the seeded item back after the restore", ""),
"emby": (W, "installs, answers, removes clean; no seed (no fixture for its wizard)", ""),
"ghost": (W, "the seeded item back after the restore", ""),
"gitea": (W, "the seeded item back after the restore", ""),
"glance": (W, "family gate holds a stranger; config-only (nothing to keep); the app's own headings are English", ""),
"gokapi": (W, "installs, own login, removes clean; no seed (API key only in its UI)", ""),
"grafana": (W, "the seeded item back after the restore", ""),
"gramps-web": (W, "installs, answers, removes clean; no seed fixture", ""),
"grimmory": (P, "first admin, library and an EPUB upload work; setup gate then family gate work as designed; the read-back through the family gate stayed inconclusive (the harness's gate logic, R-1277); remove-with-data leaves the empty parent folder", "R-1276, R-1277"),
"grocy": (W, "the seeded item back after the restore", ""),
"home-assistant": (W, "the seeded item back after the restore", ""),
"homebox": (W, "the seeded item back after the restore", ""),
"homepage": (P, "no gate at all: a stranger sees the box's CPU/RAM/disk (and later the family's links); still the upstream sample page", "R-1272"),
"immich": (P, "album (new fixture) in; „use my kept data” loaded the rehearsal's future-dated database and Immich never answered again (404, 20 min)", "R-1271"),
"jellyfin": (W, "first user (new fixture, its wizard API) back after the restore", ""),
"karakeep": (W, "the seeded item back after the restore", ""),
"kimai": (W, "own login; a Hungarian browser lands on /hu/ (curl without a language gets /en/)", ""),
"komga": (W, "the seeded item back after the restore", "drive names: R-1275"),
"lubelogger": (W, "the seeded item back after the restore", ""),
"mealie": (W, "the seeded item back after the restore (the fixture raced after_install on the first run)", ""),
"metube": (W, "family gate holds a stranger; download queue entry back", ""),
"n8n": (P, "the owner back after the restore; on the live template it tries to send usage data out ([Rudder] lines, 6 in the live re-run) — the drill fix: the variable in the running container, 0 error lines", "drill a601950 (morning list)"),
"navidrome": (W, "the seeded item back after the restore", "drive names: R-1275"),
"nextcloud": (P, "user in; „use my kept data” loaded the rehearsal's future-dated database beside today's files — the user was not there", "R-1271"),
"nzbget": (W, "the seeded item back after the restore; „no news server” until the family adds one (the guide says so)", ""),
"onlyoffice": (W, "document server (no household data to keep); installs, answers, removes clean (every-5th restore)", ""),
"opengist": (P, "the gist owner back after the restore; but 128M is too small: one sign-up + sign-in hit the limit 443 times and swapped 98 MiB (memprobe-128M.txt); at 256M (drill) anon 216 MiB", "drill 405077f (morning list)"),
"outline": (W, "the seeded item back after the restore", ""),
"paperless-ngx": (P, "account in; „use my kept data” loaded the rehearsal's future-dated database — the account was not there", "R-1271, R-1276"),
"papra": (W, "the seeded item back after the restore (Papra 26.6 needs an Origin header — a browser sends it)", ""),
"plant-it": (N, "lifecycle: abandoned — not offered for a new install (by design)", ""),
"plex": (W, "installs, answers, removes clean; no seed (a server is claimed with a plex.tv account)", ""),
"privatebin": (W, "the seeded item back after the restore; D22 put-back PASS", ""),
"prowlarr": (W, "the seeded item back after the restore", ""),
"qbittorrent": (W, "the seeded item back after the restore", ""),
"radarr": (W, "the seeded item back after the restore", ""),
"radicale": (W, "the seeded item back after the restore", ""),
"rallly": (W, "installs, answers, removes clean; no seed: Rallly 4.15 closed password sign-up (login by e-mail code); the guide's polls need no account", "R-1277"),
"recipe-importer": (W, "stateless helper; installs, answers, removes clean (every-5th restore)", ""),
"romm": (P, "item back through „use my kept data” (its own copy); remove-with-data left the empty parent folder", "R-1276"),
"sabnzbd": (W, "the seeded item back after the restore", ""),
"seerr": (W, "installs, answers (307 to its setup), removes clean; no seed (needs a media server)", ""),
"sonarr": (W, "the seeded item back after the restore", ""),
"sparkyfitness": (W, "the seeded item back after the restore", ""),
"tandoor": (P, "works; but the keep-backups → reinstall → restore path breaks it (DB password from the new install over the old data), and the restore still said success — reproduced on its own", "R-1269, R-1270"),
"termix": (W, "installs, answers, removes clean (every-5th restore); no seed fixture", ""),
"transmission": (W, "the seeded item back after the restore", ""),
"uptime-kuma": (W, "the seeded item back after the restore", ""),
"vaultwarden": (W, "installs in 25 s, its own login page, sign-up closed (404); the box admin's invite works (run-live-2), but 1.37.4 has no route for the fixture's invited sign-up, so no item was seeded", "R-1277"),
"vikunja": (W, "the seeded item back after the restore; D22 put-back PASS", ""),
"wanderer": (T, "not testable on this box: its web part calls its database at the PUBLIC name, which tester4.test cannot resolve (known, R-739) — unhealthy, 404", "R-739"),
"wger": (N, "lifecycle: hidden — not offered for a new install (by design)", ""),
"wishlist": (W, "the seeded item back after the restore", ""),
"zipline": (W, "the seeded item back after the restore", ""),
}
def mem(r):
anon = r.get("mem_anon_peak_mib") or {}
src = anon if anon else (r.get("mem_peak_mib") or {})
lim = sum((c.get("limit_mib") or 0) for c in (r.get("containers") or {}).values())
return f"{round(sum(src.values()))}{'' if anon else '*'} / {lim}" if src else "–"
def main():
rows = ["| App | Verdict | Peak memory / limit (MiB) | Install → answering (s) | What we saw | Row / fix |", "|---|---|---|---|---|---|"]
for app in sorted(VERDICTS):
v, line, ref = VERDICTS[app]
f = os.path.join(EV, app, "record.json")
r = json.load(open(f)) if os.path.exists(f) else {}
inst = (r.get("install") or {}).get("seconds_to_front_door")
rows.append(f"| {app} | **{v or 'PENDING'}** | {mem(r) if r else '–'} | {inst if inst is not None else '–'} | {line} | {ref} |")
c = {}
for v, _, _ in VERDICTS.values():
c[v or "PENDING"] = c.get(v or "PENDING", 0) + 1
rows.append("\n" + ", ".join(f"{k}: {n}" for k, n in c.items()) + f" (of {len(VERDICTS)})")
out = "\n".join(rows)
if "--write" in sys.argv: # replace the block between the markers in ../README.md
p = os.path.join(EV, "README.md")
s = open(p).read()
a, b = s.index("<!-- TABLE START -->") + len("<!-- TABLE START -->"), s.index("<!-- TABLE END -->")
open(p, "w").write(s[:a] + "\n" + out + "\n" + s[b:])
print(out)
if __name__ == "__main__":
main()