60 template directories, 58 offered. Records: onboarding/grocy.md and
onboarding/lubelogger.md, all 61 checks answered, none open. Evidence:
felhom.eu/documentation/audits/new-apps-2026-10-10/.
Grocy 4.7.1 from lscr.io/linuxserver/grocy (grocy publishes no image of its
own). SQLite in one volume, no HDD, ~30 MiB idle, amd64 + arm64, Hungarian UI
91.6 %. First admin class 3: it starts with its documented admin/admin and
after_install replaces that password with a generated one.
LubeLogger v1.7.3 from ghcr.io/hargata/lubelogger. LiteDB in one volume, no
HDD, ~60 MiB idle, amd64 + arm64. First admin class 1 — and it has to be: the
image ships EnableAuth=false, and with that the middleware mints a ticket with
the IsRootUser role for every visitor. Measured on a default start: a stranger
got 200 on /, on /api/vehicles and on /Home/Settings and CREATED A VEHICLE.
The compose entrypoint exports EnableAuth=true and the SHA-256 of a generated
name and password, so the app's own login is on before its first byte (at t+1 s
nothing listening, at t+2 s /api/vehicles already 401).
Two defects found by the walk and fixed before publishing:
1. An after_install command may not contain `$`. The controller runs every
element through os.Expand and refuses one naming anything outside env:, so
PHP cannot be inlined. On 9202 the first attempt came back
`[pw argv dsn db i t e s n q h] not declared in env or has no value — not
run` and the app sat behind its install hold with admin/admin in place. The
code now lives in a file the compose entrypoint writes. Written into
REUSE.md's after_install row as a trap.
2. Grocy's persisted config.php does not follow the image. The image copies
config-dist.php only when that file is absent, so a volume written by 4.6.0
and started under 4.7.1 answered HTTP 500 on every page — AUTH_CLASS names a
class 4.7 moved — while its log said migrations done. The entrypoint now
deletes the file at every start. The 4.6.0 -> 4.7.1 edge failed before this
and is proven after it, on both venues.
Ladders, written by upgrade-test.py --write-ladder from both verdicts:
grocy 4.6.0 -> 4.7.1 bench proven, box proven (guarded Update, 41 s)
lubelogger v1.7.2 -> v1.7.3 bench proven, box proven (25.6 s)
Checklist 6.3 came from a real failure, not a forced one: before the config.php
fix the product undid the same step in 346 s with the data intact.
REUSE.md: a fifth healthcheck family (bash /dev/tcp) for an image with no HTTP
client at all, measured in both directions.
Tool fixes made on the way: check-onboarding.py crashed on a Windows console
while printing which ids were open; upgrade-test.py's read_text/write_text used
the platform encoding and wrote a cp1250 em dash into a template full of
Hungarian.
NOTHING IN THE CATALOG CHANGED. No template, no .felhom.yml, no
templates.json, no image pin. Output is audits/pikapods-scan-2026-10-10/.
THE GREEN HEART, in PikaPods' own words. Hovering it shows the tooltip
"Project has a revenue sharing agreement." (verified by hovering the heart
beside Actual, A1-heart-tooltip.jpg). Their FAQ: "Open source developers have
the option to enter into a revenue sharing agreement to receive 20% of
revenues their app generates." The mark is NOT permission for Felhom -- it is
an agreement with PikaPods, on terms we have not seen. It only shows the
author is open to such an arrangement at all.
Finding the mark took reading the DOM: the hearts are not text and not a
title attribute. Each card carries three Vuetify icons, all three present on
every card, and the green one (rgb(76,175,80)) is display:none where there is
no agreement. Counting "hearts in the page text" would have returned zero on
all 126.
NUMBERS. 126 apps on PikaPods, 39 marked. 58 template dirs here (56 offered +
plant-it abandoned + wger hidden -- which reconciles with the website's 56 +
FileBrowser built-in). 25 on both, 33 of ours not there, 101 of theirs not
ours, 11 of the 25 shared carry the mark.
LICENCE COMFORT: one row moved, and I will not dress it up as more.
sparkyfitness is there WITH the mark, consistent with what its author already
told us (R-784) -- so he demonstrably does commercial-hosting agreements.
tandoor, emby, plex, outline, calcom and wanderer are NOT on PikaPods at all,
so the scan says nothing about them. n8n and docmost ARE hosted there WITHOUT
a mark: a fact about PikaPods' risk appetite, not a permission we can borrow.
TEN CANDIDATES, none added, each licence read at its own repo and cited:
Grocy (MIT), Firefly III (AGPL-3.0), Monica (AGPL-3.0), LubeLogger (MIT),
Storyteller (MIT), PdfDing (AGPL-3.0), Memos (MIT), Wealthfolio (AGPL-3.0),
Kavita (GPL-3.0), PhotoPrism (AGPL-3.0). Developer and company tools were
excluded wholesale. Each would still go through NEW-APP-CHECKLIST.md.
JOPLIN WAS SHORTLISTED AND DROPPED ON ITS LICENCE, which is the most useful
thing in this scan. Its repo root says AGPL-3.0-or-later "unless a directory
contains a LICENSE or LICENSE.md file" -- and packages/server, the part we
would host, carries the Joplin Server Personal Use License: "the Software may
be used for personal non-commercial purposes only", and the licensee may not
"grant others the right to use the Software for a fee". Same shape as
SparkyFitness: open client, restricted server. GitHub's repo-level licence
field says AGPL-3.0 and would have hidden it.
Two other reads worth keeping: PdfDing's GitHub mirror has NO licence file at
all (the project lives on Codeberg, where it is AGPL-3.0) -- trusting the
mirror would have called it unlicensed; and photoprism/joplin both report
NOASSERTION to the API, so both needed the file read rather than the badge.
Register: 0 opened, 0 closed. The brief expected a row only for a licence
problem in an app we already ship; none turned up. No contact with PikaPods or
any author.
NEW-APP-CHECKLIST.md: the reviewer's draft reviewed - 60 rows in 10 groups, each with how/why and a since date;
7 rows added, 16 sharpened, 9 wrong claims fixed. onboarding/_TEMPLATE.md (one line per id), onboarding/wger.md
(the pilot, exempt app, 11 open rows each a register row), onboarding/EXISTING-APPS-GAPS.md (read only, from
scripts/onboarding_gaps.py). Gate onboarding (scripts/check-onboarding.py) in --fast: a template directory not
among the 53 published before 2026-10-01 needs a complete record; decoys in test_gate_decoys.py (16 cases, 5 gate
mutants seen red). CLAUDE.md, REUSE.md 5, README point to it. No template changed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
zipline 4's first-run route is POST /api/setup (measured on the bench 2026-09-30, v4.6.1: GET ->
{"firstSetup":true}, POST {username,password} -> 200 SUPERADMIN, the login works). The two paths the
fixture tried stay as fallbacks. No image moves in this commit.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
update_ladder: in .felhom.yml, one JSON entry per line (spiked live on
controller v0.266.0 and v0.267.0 first). Two gates: check-test-record.py
(static, CI too) and check-test-record-move.py (history + registry for
moved refs only). 16 decoys, 3 red-proofs. The ONLY writer is
upgrade-test.py --write-ladder (bench AND box proven, digests resolved).
Harness v3: box fixtures on the bench, files_may_change.
Backfill: the 21 moves of 2026-09-22, 21 proven from their records.
No image: line moved.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
After an edge reads back, --soak seconds (default 600) of light load while
the kernel's own oom_kill counter is read host-side from the container's
cgroup. A kill or restart turns proven into failed; a peak over 80% of the
limit adds the memory_tight mark. New Romm fixture; edges M1 / M1old.
Red-proof on scratch 9202: M1old (template as promoted, 512M, 4 workers)
OOM-killed at +76 s -> failed. M1 (current, 768M, 2 workers) proven, 0
kills in 608.5 s, peak 81% -> memory_tight.
Test code only; no template changed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Test code only — no template changed and no image: line moved.
The update night walked real within-a-major upstream edges on scratch guest 9202 through the
product's own guarded Update, against a PRIVATE DRILL CATALOG; the live catalog was never
touched. This brings the expensive half of that work — the seed routes — back into the harness
so the same edges can be run here WITH their ABORT step, which the box deliberately does not
offer (09 6.1: whether the old image starts on migrated data is per-app and unpredictable).
- upgrade_fixtures.py: ActualBudget, Navidrome, AudiobookShelf, Vikunja. Each seeds through the
app's OWN interface (R-156); each carries a negative control run on every verify(), so a
readback that has broken into always succeeding fails instead of passing everything.
- upgrade-test.py: edges U1..U7, all real upstream moves existing 2026-09-21 that this catalog
has NOT made, each holding its database engine constant.
- Limitations kept: Navidrome and AudiobookShelf seed the DATABASE half only, and say so.
OWED, stated so it is not mistaken for done: the U1..U7 harness RUNS, and with them the per-app
ABORT answers. The code is in; the runs are not.
Gates: catalog_gates.py --fast — image-pins, engine-major, catalog-since, copy-i18n all OK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
The per-app table, the unverified UI labels for the slice-6 walk, the judgement
calls recorded rather than hidden, the one string deliberately left in Hungarian and
why the ceiling's floor is 1 rather than 0, and the live proof: the English Apps list
shows zero Hungarian app descriptions across all 53.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
The three apps' English text, the per-app table, the unverified UI labels for the
slice-6 walk, the gate's five checks and the three defects its own decoys found in
it, and the live proof on both demo boxes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS