BREAKING CHANGE: PASSWORD is gone and AUTH and CERTRESOLVER are required.
The server's compose.yaml and .env must be updated in the same deploy — the
new image ignores PASSWORD, and the old one refuses to start without it.
Authelia now sits in front of Traefik, so the app was asking for a second
password at the same door. Two prompts, and the weaker of the two was the one
holding a single shared secret with no sessions, no MFA and no revocation.
Deleting it is the whole change: Authelia already does this properly, once,
for every service on the host.
Gone: auth(), challenge(), the whole of throttle.go and its tests, and
golang.org/x/time with them. routes() returns the bare mux, /healthz is an
ordinary route on it, and the smoke script drops sixty -u flags. Roughly 230
lines removed and nothing written to replace them.
What holds the app up now, both asserted in compose.yaml:
- The router names the Authelia middleware through AUTH. Traefik takes a
router out of service when its middleware does not resolve, so a typo or an
unset variable fails shut rather than serving the app open.
- The container still publishes no ports, so the proxy is the only thing that
can reach it. Publishing 8080 would now bypass authentication outright, not
merely TLS — the comment there says so.
certresolver replaces the bare tls=true, parameterised as CERTRESOLVER: the
server had been carrying that label by hand since the first deploy. Naming a
resolver implies tls=true, so it stays one label.
TestAuth and TestHealthzSkipsAuth are replaced by one test asserting every
route answers without credentials — a 401 from here would now mean auth had
crept back in.
Merging a pull request into main is now the whole release. A Gitea
Actions workflow derives the CalVer tag, builds the image and pushes
it with :latest, so nothing is built locally any more.
That made image/push/release redundant, and with them the .release-tag
state file and the main-branch guard — the workflow only runs on main,
which is protected, so the guard had nothing left to catch. The digest
verification went too: it guarded a `make -j` race between image and
push that cannot happen in a single CI job.
seed, icons and vendor ran a few times a year and are written out in
the README instead. Makefile: 151 lines to 71.
Docs referenced the removed targets in sixteen places, including a
CONTRIBUTING note claiming the branch check "has to be local".
Picking a date from before the oldest log entry rendered nothing at all.
history() truncates its window at min(date) — correct, there is no history
before the first entry to be missing — and loadDays widened the window by
inflating the day count, which that truncation then undid. No row for the
selected day meant no board to log it in, so the one thing you would want an
old empty day for was the one thing you could not do. The same happened past
maxHistoryDays, reachable straight from the date picker.
history() now takes the selected day as an explicit floor instead of the
caller guessing a day count, and date() clamps the past at maxHistoryDays the
way it already clamped the future — the list runs unbroken from today down to
the selection, so a picker set to 1994 would otherwise ask for eleven thousand
rows.
While in there:
- entriesBetween replaces the day-at-a-time entryFor loop. Two queries for
the whole window rather than two per day; the widest window a URL can ask
for was 3,600 round trips through a pool of exactly one connection.
- softDeleteMain and updateMain filter on special = 0. The catalog never
lists Tähteet, but a stale tab or a hand-made POST could still have removed
the row migration 0002 guarantees.
- WriteTimeout and IdleTimeout on the server. With one database connection, a
reader stalling on a long history response blocks everything behind it.
Two tests, both of which fail on the old code: a day 100 back with only today
logged, and sides landing on their own day now that they arrive in one query.
The variable rename landed in the repository but not on the server, which
keeps its own compose.yaml and .env. Neither is pulled from here, so the
container came up against the old names. A release touching either now has to
say so in its notes.
The script hardcoded 2026-09-05, so at the next midnight that became "some
day in the past" and the assertions quietly changed meaning: today turned
into an unlogged row, and a refute on "Ei merkintää" started matching it
instead of the day under test. Dates are now computed at run time.
That refute was wrong regardless. It denied the string across the whole page,
but every unlogged day legitimately renders one. It now asserts positively
that the entry is still shown while the delete is being confirmed.