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.
Jellyfin: KnownProxies 172.16.0.0/12 seeded into network.xml. Emby:
LocalNetworkSubnets 10.0.0.0/8 + 192.168.0.0/16 seeded into system.xml.
Fresh volume or empty list only. Measured on 9202 through the simulated
tunnel: remote-off user 200 -> 403; LAN household still 200.
Held on night-held-2026-10-06 (night fence); not for main tonight.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Without HOMEPAGE_ALLOWED_HOSTS every /api/* call under the box's name was
refused (400), so services and widgets stayed empty. Measured on the
bench. Glance's public page measured; its login is an operator question.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
A killed seed still started the app (upstream's `exec pnpm start` is on
its own line), so sign-ups failed while the box read healthy. Healthy
now needs the role rows and a group or user. Measured on the bench.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Written by upgrade-test.py --write-ladder from both verdicts. Bench 9401: harness v5,
anon peak 16.3 %, seed read back. Box 9202: guarded Update done in 12.3 s, seed read back,
seeded through the admin invite inside the box (decision 149).
CLAUDE.md: stale gate counts, the CI/R-161 and catalog_since-gate sentences, the checklist count,
steps/ in the layout, engine service counts, decoy gate path. Factual only.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
mealie (09 §3 decision 144): five wrong logins lock the account for 1-2 hours,
even for the right password — wait, then sign in again.
Karakeep (decision 148): the official phone app sends crash reports to its
makers (Sentry).
Both are a new last first_steps entry (app_info has no notes field); the
Hungarian freeze admits them with the reason (check-copy-i18n --add-app).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Checklist 3.6 re-measured on 9202 through the simulated tunnel (felhom.eu audits/visitors-2026-10-01/A/bookstack-3.6.txt):
without it, a stranger's 5 wrong tries for admin@admin.com throttled the household from another address too; with it,
the stranger is throttled and the household signs in at once. A rotating forged leftmost address does not escape.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
household-switchable trustProxy reads the leftmost XFF into its logs. Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
household-switchable trustProxy reads the leftmost XFF and passes it on to Jellyfin. Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
logs the raw XFF on a failed login. Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
leftmost XFF into its download log (SaveIp). Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
echo RealIP — leftmost XFF into its failed-auth log lines. Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
leftmost XFF into its failed-login log lines. Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Spring framework strategy — leftmost XFF into its sign-in audit record. Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
leftmost XFF for its /api/event per-IP limit. Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
leftmost XFF keys its per-IP limiter (an unbounded map). Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
better-auth — leftmost XFF; a junk value SKIPS its auth rate limit. Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
django-allauth 65.12.1 — leftmost XFF for its 10/min per-IP login limit. Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Koa proxy — leftmost XFF for its per-IP limits and the sign-in link's IP binding. Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
leftmost XFF decides who is on the LAN (remote-access and IP-filter bypass). Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
uvicorn --forwarded-allow-ips=* — leftmost XFF for its pair-code limits. Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
better-auth — leftmost XFF for its per-IP sign-in limit. Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
django-allauth 0.63.3 — leftmost XFF for its per-IP login limits. Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Express trust proxy true — leftmost XFF; its brute-force buckets are keyed by IP (+username). Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
request-ip — leftmost XFF; its auth limiter (40/10 min) is keyed by IP only. Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Fastify trustProxy true — leftmost XFF; its login limit (10/min) is keyed by IP only. Once traefik trusts the tunnel's fixed address (controller v0.286.0), the leftmost entry is what a
stranger writes; with the chain removed the app reads traefik's X-Real-Ip or its peer, as before — never forgeable.
Measured on 9202: a router with this middleware receives no X-Forwarded-For (felhom.eu audits/visitors-2026-10-01/A/P1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Location history with PostGIS 17 + Redis + Sidekiq. The seeded known login replaced by after_install behind the install
hold; geocoding off; SECRET_KEY_BASE a data_key; smtp_mapping with mail-off boot measured. onboarding/dawarich.md complete.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Bookmarks/articles/notes with a crawler (karakeep-chrome) and search (meilisearch v1.41.0). AI off unless the household
enters a key; setup gate + sign-up closed twice; Chrome healthcheck over bash /dev/tcp; smtp_mapping (plaintext), mail-off
boot measured; web memory 768M -> 1536M on measurements (bench watch, box crawl burst). onboarding/karakeep.md complete.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Calendar and contacts (CalDAV/CardDAV), ghcr.io/kozea/radicale:3.8.1. Login file written from the generated password on
the first start only (R-765: rewriting it at every start lost the household's login on a restore). onboarding/radicale.md
complete; bench + 9202 proven; FIRST-ADMIN, README, Hungarian freeze.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS