The review passes

Security was reviewed ten times as the project grew, most passes by several reviewers at once. The later ones were run blind: reviewers saw the code and nothing else, because the eyes that had not absorbed the project's reasoning were the ones that found the real bugs.

Every finding was checked against the code before anything changed, and the serious ones were reproduced first - a fix confirmed only by reasoning is the kind that turned out not to work. Several did not: two read deadlines were silently no-ops for months, and the test that was meant to catch it built the handler without the middleware that broke it.

Before the internet: passes one to three

The first review came before remote access was built. It found that a path is an instruction to a backend holding an admin credential - /api/hls/jellyfin/%252e%252e/System/Info reached Jellyfin's admin API through a double-decoded dot segment - and that one Jellyfin account serving films and TV let an id cross between them. It added the first-account setup code, per-account guess limits, origin-based CSRF checks, no-overwrite uploads, a disk reserve, hashed session keys and validated reading positions.

The second, broader pass found little. The third found the largest thing in the whole series and it was architectural: recover() appeared nowhere, so a panic in any goroutine SoundStorm started - per-source search, the ebook scanner, provisioning, certificate renewal - took the whole process down. Every such loop now recovers.

The reader, three times

The same class of bug was found three times through three doors, which is why the reader's defences are layered:

  1. Fourth pass: a book's chapter pointing an absolute <script src> at the server's own book-resource endpoint ran under script-src 'self', as the owner. Fixed by refusing non-fetch destinations and never serving a script type.
  2. Fifth pass: the same through a cover - a declared cover named x.js was served as JavaScript. Fixed centrally for every stream path.
  3. Sixth pass: a chapter loading the app's own app.js, which ran against the book's copy of the page's ids. Fixed by stripping scripts from every chapter before rendering and refusing to run the app in a frame.

After remote access: passes five to seven

Passes eight to ten

Then: guessing from many addresses

Asked about afterwards as Tor. The throttle already counts guesses per account whatever the address - about 1,400 a day - so many addresses buy a guesser little, but 1,400 a day is plenty against "password1". Three things followed (see accounts):

What the passes taught