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
after_install renames admin to the generated ADMIN_USER in app.db, sets the password with cps.py -s, and proves both
before its success line. Proven on 9202: 40 wrong tries on admin, the household still in at once (form and OPDS).
Hungarian freeze re-captured for the five changed calibre-web strings only.
Evidence: felhom.eu/documentation/audits/calibre-name-and-prune-2026-10-01/A/
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
django-axes keyed on ip_address, and behind the tunnel every visitor has the tunnel container's address (R-753).
Measured on 9202 (felhom.eu/documentation/audits/lockouts-2026-10-01/B/): live template — 10 wrong tries on admin
locked the second member too; with AXES_LOCKOUT_PARAMETERS=username, AXES_COOLOFF_TIME=5 and the database handler —
the second member unaffected, admin in again at 7.5 min after one retry during the lock, a wrong password still
refused. Settings only: no image moves, no ladder entry (gates OK).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
mealie locks the ACCOUNT after 5 wrong logins and its login names (admin, changeme@example.com) are public, so a
stranger could lock the household out for a day. Measured on 9202 with SECURITY_USER_LOCKOUT_TIME=1: the right
password answered 423 for 120 min (the hourly job lifts it after the hour), then 200; a wrong one still 401.
Evidence: felhom.eu/documentation/audits/rulings-2026-10-01/C/. CHANGELOG also records today's re-test run and fixes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
The template set no JWT_PRIVATE_KEY/JWT_PUBLIC_KEY, so the app login (the mobile app's route) answered 500
on a CORRECT password. The deploy's generators cannot make an RSA pair, so the start command makes it ONCE
with wger's own `manage.py generate-jwt-keys`, keeps it 0600 on wger's data volume (a restore brings it
back), and loads it before the image's own entrypoint. Measured on the bench (2.7): right password 200 with
an access token that reads the API (200); wrong password 400 (allauth's answer); the key survives a
restart. No image moves.
Evidence: felhom.eu/documentation/audits/night-rulings-2026-09-30/D/
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
The route 4.8.0 itself asks for (its prisma -> drizzle migration needs 4.7.x first). Bench 9401
(harness v4, swap 0; two earlier attempts stopped at the FROM pull because the bench's own disk was
full — images removed by name, no prune): the user logs in before and after, 10-min watch 0 kills
(anon peak 34.3 %); the abort starts and serves. Box 9202: done in 32.8 s, the user logs in.
4.7.0 keeps its definition in steps/. Written by upgrade-test.py --write-ladder.
Evidence: felhom.eu/documentation/audits/more-night-apps-2026-09-30/
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
4.8.0 cannot be reached in one step: it refuses to start until the database has run the release
before it (prisma -> drizzle; measured on both venues; the box undid it by itself in 20 s). So the
first step is 4.7.0. Bench 9401 (harness v4, swap 0): the first user made through /api/setup and
logging in before and after, 10-min watch 0 kills (anon peak 41.7 %); the abort starts and serves.
Box 9202: done in 103.6 s through the setup gate, the user logs in. PostgreSQL 16 does not move.
Written by upgrade-test.py --write-ladder.
Evidence: felhom.eu/documentation/audits/more-night-apps-2026-09-30/
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
That step still pinned immich-postgres at 512M. It re-runs the geodata import (v3.0.3 ships geodata
dated 2026-07-13, v3.2.2 2026-08-30 — read inside both images), the load R-732 measured at ~575 MB.
Re-proof on bench 9401, swap 0, on the step's OWN definition with only the limit changed (768M;
mem_limit 4096M -> 4480M): `Starting geodata import` ... `Geodata import completed` in 17 s, the
database's kill counter 0 from its birth, the album read back, 10-min watch 0 kills (anon peak 29.6 %).
Written by upgrade-test.py --restep; the ladder entry is untouched. No box reporting to the hub runs
immich (hub /apps, same day).
Evidence: felhom.eu/documentation/audits/more-night-apps-2026-09-30/D/
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
The app half only; PostgreSQL 18 does not move. Bench 9401 (harness v4, swap 0): sign-up with the
e-mail code from its own table, a poll seeded and read back before and after, 10-min watch 0 kills
(anon peak 60.6 %); putting 4.11.1 back after the migration loses the poll (recorded; the box's undo
restores the pre-update copy). Box 9202: done in 64.7 s through the setup gate, the poll read back.
Written by upgrade-test.py --write-ladder.
Evidence: felhom.eu/documentation/audits/more-night-apps-2026-09-30/
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS