From 94477cba435a4fce2ba7e463c33abe85643d10e4 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Thu, 1 Oct 2026 19:51:12 +0200 Subject: [PATCH] Grimmory tested but held (R-775): its fixture added; CHANGELOG, CONTEXT, REPORT for the night's new apps Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS --- CHANGELOG.md | 8 +++ CONTEXT.md | 3 ++ REPORT.md | 30 ++++++----- scripts/upgrade_fixtures_box.py | 94 +++++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d700f77..04cd09f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## Grimmory — tested, NOT published; fixture added (2026-10-01, night) + +- Grimmory (the BookLore successor) passed the checklist on the bench and 9202 except one finding that needs the + operator: a stranger's 5 wrong sign-ins lock EVERY visitor out for 15 minutes (hard-coded; one address behind the + tunnel) — `felhom.eu` R-775. The template is NOT in `templates/`; it waits in + `felhom.eu/documentation/audits/new-apps-2026-10-01/wip/grimmory/`. Its fixture `Grimmory` (first admin, a library, an + uploaded EPUB, three negative controls) is in `upgrade_fixtures_box.py`, so the publishing session reuses it. + ## Dawarich — the third app through the new-app checklist (2026-10-01, evening) - **`templates/dawarich/`** — your own location history (instead of Google Timeline): `freikin/dawarich:1.15.3` (web + diff --git a/CONTEXT.md b/CONTEXT.md index 31adeca..fe45add 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -2,6 +2,9 @@ > Created with the REUSE.md rollout (2026-07-03). History: `CHANGELOG.md`; format spec: `README.md`. +- **2026-10-01 (night) — FIRST NEW APPS through the checklist:** Radicale `195129c`, Karakeep `882ac14`, Dawarich `72247a3` + published with complete records; Grimmory tested but held (R-775, operator); Grimoire/MeTube/Pinchflat stopped on the + fit check; Invidious/moonlight-web fit only (`felhom.eu/documentation/audits/new-apps-2026-10-01/FIT.md`). - **Ruling 2026-10-01 (operator) — wger HIDDEN** (`lifecycle: hidden`) until R-762 and R-763 are fixed; the fix session un-hides it. - **2026-10-01 (evening) — THE NEW-APP CHECKLIST.** `NEW-APP-CHECKLIST.md` (60 rows, `since` per id) + `onboarding/` diff --git a/REPORT.md b/REPORT.md index d6b506f..0e19801 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,15 +1,19 @@ -# REPORT — the new-app checklist: in the catalog, a gate, piloted on wger (2026-10-01, evening) +# REPORT — the first new apps through the checklist (2026-10-01, night) -Full session report: `felhom.eu/REPORT-new-app-checklist-2026-10-01.md`; evidence -`felhom.eu/documentation/audits/new-app-checklist-2026-10-01/`. +Full session report: `felhom.eu/REPORT-new-apps-2026-10-01.md`; evidence `felhom.eu/documentation/audits/new-apps-2026-10-01/` +(fit table `FIT.md`). -- **`NEW-APP-CHECKLIST.md`** reviewed: 60 rows in 10 groups (draft 53); 7 added, 16 sharpened, 9 wrong claims fixed. -- **`onboarding/_TEMPLATE.md`** (one line per id) and **`onboarding/wger.md`** (the pilot; 11 open rows, each a register row). -- **Gate `onboarding`** (`scripts/check-onboarding.py`, `--fast`: hook + CI). The 53 published apps are exempt by name. - 16 decoys judged right; 5 deliberately broken versions of the gate each turned the decoy suite red. -- **`onboarding/EXISTING-APPS-GAPS.md`** from `scripts/onboarding_gaps.py` (read only). -- **The pilot:** the draft caught R-752 and R-755 as written; it missed R-737 (its "how" logged in on the web form only) - and R-738 for a NEW app (nothing to update at the newest tag). Rows 1.4/1.6 sharpened, 1.7/3.9 added — now all four. - The new rows then found three live wger defects: R-762, R-763, R-764. No template was changed. -- Gates: `catalog_gates.py --fast` OK (11); `test_gate_decoys.py` 121 OK; `test_catalog_gates.py` OK; - `decoy_coverage_gate.py` 0 unaccounted. +- **wger hidden** (`lifecycle: hidden`, `55b8c8a`) until R-762 and R-763 are fixed; read back on 9202: not on the app list. +- **Published, each with a complete `onboarding/.md` and its first proven ladder step (bench swap 0 + 9202):** + - **Radicale** (`195129c`) — CalDAV/CardDAV, upstream image; the login file is written on the FIRST start only + (R-765: rewritten at every start, a restore after a remove locked the household out — fixed before publishing). + - **Karakeep** (`882ac14`) — bookmarks + crawler (Chrome) + search; AI off unless the household enters a key; setup gate, + sign-up closed twice; web memory raised to 1536M on measurements. + - **Dawarich** (`72247a3`) — location history, PostGIS 17; the seeded known login replaced by `after_install` behind the + install hold; `SECRET_KEY_BASE` a data_key. +- **Grimmory not published:** a stranger's 5 wrong sign-ins lock every visitor out for 15 min (hard-coded, one address + behind the tunnel) — R-775, the operator's word. Its template waits in the evidence `wip/grimmory/`; its fixture + (`Grimmory`) is in `upgrade_fixtures_box.py`. +- **Not built on the fit check:** Grimoire, MeTube, Pinchflat; Invidious and moonlight-web fit only. +- Fixtures added: `Radicale`, `Karakeep`, `Dawarich`, `Grimmory`. Gates: `catalog_gates.py --fast` OK on every push; the + runtime volume gate CLEAN for each published app (bench). diff --git a/scripts/upgrade_fixtures_box.py b/scripts/upgrade_fixtures_box.py index 9ed4bf0..6207e54 100644 --- a/scripts/upgrade_fixtures_box.py +++ b/scripts/upgrade_fixtures_box.py @@ -2066,6 +2066,99 @@ class Dawarich: return found +# ============================================================================================= +class Grimmory: + """Grimmory (2026-10-01, new app through NEW-APP-CHECKLIST.md). THE FRONT DOOR is the app's own API, the one its web + client calls: the first admin by `POST /api/v1/setup` (through the setup gate on the box, as the household), sign in + with `POST /api/v1/auth/login` (a bearer token), a library on `/books`, and a real EPUB (built here, with a unique + title) uploaded with `POST /api/v1/files/upload` — the app writes it into the household's book folder. Read back: + `GET /api/v1/books` lists the title. Negative controls on every readback: no token 401, a wrong password 401, and a + second `POST /api/v1/setup` refused (403) — the readback is not an open door.""" + sub = "library" + + @staticmethod + def _epub(title): + import io as _io, zipfile + b = _io.BytesIO() + z = zipfile.ZipFile(b, "w") + z.writestr(zipfile.ZipInfo("mimetype"), "application/epub+zip") + z.writestr("META-INF/container.xml", '') + z.writestr("OEBPS/content.opf", f'{title}Felhom{title}hu') + z.writestr("OEBPS/c1.xhtml", 'c

Szia!

') + z.close() + return b.getvalue() + + def _token(self, w, sub, user, pw): + rc, code, out = w.app_curl(sub, "/api/v1/auth/login", "-H", "Content-Type: application/json", + data=json.dumps({"username": user, "password": pw}), method="POST") + try: + return code, json.loads(out).get("accessToken") + except Exception: + return code, None + + def seed(self, w, sub, say): + import tempfile + if not w.wait_app(sub, "/api/v1/healthcheck", want=("200",), tries=90): + self.tried = "/api/v1/healthcheck never answered 200" + return None + user, pw = "admin", "Gm-" + secrets.token_hex(10) + rc, code, out = w.app_curl(sub, "/api/v1/setup", "-H", "Content-Type: application/json", + data=json.dumps({"username": user, "password": pw, "email": "admin@felhom.invalid", "name": "Felhom"}), method="POST") + say(f" grimmory: POST /api/v1/setup (the first admin) http={code}") + if code != "200": + self.tried = f"setup -> {code} {(out or '')[:120]}" + return None + code, tok = self._token(w, sub, user, pw) + if not tok: + self.tried = f"login -> {code}" + return None + auth = ["-H", f"Authorization: Bearer {tok}"] + rc, code, out = w.app_curl(sub, "/api/v1/libraries", *auth, "-H", "Content-Type: application/json", + data=json.dumps({"name": "Konyvek", "icon": "book", "iconType": "LUCIDE", "paths": [{"path": "/books"}], + "watch": True, "metadataSource": "EMBEDDED", "organizationMode": "BOOK_PER_FILE"}), method="POST") + try: + lib = json.loads(out) + lid, pid = lib["id"], lib["paths"][0]["id"] + except Exception: + self.tried = f"create library -> {code} {(out or '')[:120]}" + return None + title = "Felhom Teszt " + secrets.token_hex(4) + fn = tempfile.mktemp(suffix=".epub") + open(fn, "wb").write(self._epub(title)) + if hasattr(w, "put_file"): + fn = w.put_file(fn) + rc, code, out = w.app_curl(sub, "/api/v1/files/upload", *auth, "-F", f"file=@{fn};type=application/epub+zip", + "-F", f"libraryId={lid}", "-F", f"pathId={pid}", method="POST") + say(f" grimmory: library {lid}, upload of an EPUB http={code}") + if code not in ("200", "201", "204"): + self.tried = f"upload -> {code} {(out or '')[:120]}" + return None + return {"user": user, "pw": pw, "title": title} + + def verify(self, w, sub, t, say): + if not w.wait_app(sub, "/api/v1/healthcheck", want=("200",), tries=90): + say(" grimmory: /api/v1/healthcheck never answered") + return False + rc, c_none, _ = w.app_curl(sub, "/api/v1/books") + c_wrong, _ = self._token(w, sub, t["user"], t["pw"] + "x") + rc, c_setup, _ = w.app_curl(sub, "/api/v1/setup", "-H", "Content-Type: application/json", + data=json.dumps({"username": "x" + secrets.token_hex(2), "password": "Gm-" + secrets.token_hex(8), + "email": "x@felhom.invalid", "name": "X"}), method="POST") + if c_none != "401" or c_wrong != "401" or c_setup != "403": + say(f" grimmory: READBACK UNUSABLE — no token {c_none}, wrong password {c_wrong}, second setup {c_setup}") + return False + code, tok = self._token(w, sub, t["user"], t["pw"]) + found = False + for _ in range(12): + rc, code, out = w.app_curl(sub, "/api/v1/books", "-H", f"Authorization: Bearer {tok}") + found = code == "200" and t["title"] in (out or "") + if found: + break + time.sleep(5) + say(f" grimmory: readback http={code} found={found} (controls: no token {c_none}, wrong {c_wrong}, second setup {c_setup})") + return found + + FIXTURES = { "uptime-kuma": UptimeKuma(), "crafty-controller": Crafty(), @@ -2105,4 +2198,5 @@ FIXTURES = { "radicale": Radicale(), "karakeep": Karakeep(), "dawarich": Dawarich(), + "grimmory": Grimmory(), }