Skip to content

← All posts

Release

Under the hood

OperationsAccounts

Under the hood. No new feature in this one, and that is deliberate. It repairs a setting that turned itself around behind your back, makes the lists a great deal faster, and clears out what had quietly piled up in the database.

Fixed

  • A storage limit of 0 turned itself back into "unlimited" on every restart. Setting an account to 0 GB means: this account may not request anything. On the next start Nexview rewrote that to unlimited, the exact opposite, and said nothing about it. The cause was a one-time migration from 0.19, when a stored 0 changed meaning, that kept running on every start instead of once. Nexview now keeps a book of which migrations have already run in this database. For an existing installation the entries are not guessed but derived from what the database looks like on arrival, read before the schema is touched, because the schema update itself destroys the evidence. A migration that breaks off halfway leaves an open entry behind and is picked up on the next start, instead of a gap that the next run fills in wrongly. What already flipped in the past cannot be recovered: a rewritten 0 is indistinguishable from an unlimited set on purpose. Affected installations get one warning in the log naming how many accounts could be involved, and nothing is changed automatically.
  • The badge on the admin menu never went away. It counted findings, and a finding stays true as long as it is true, so the same digit sat there for good even after somebody had looked. It counts unread now. Merely polling the endpoint does not count as seen, or the badge would clear before anyone read it.

Security

  • Four dependencies moved up, and a check that will notice the next one. The pinned versions were correct on the day they were written and had aged quietly since. Four of the eleven carried published advisories, among them the libraries responsible for sign-in and encryption. All four are raised, including one across four major versions. From now on backend/tools/abhaengigkeiten_pruefen.py holds the pins against the public vulnerability database on every build and stops the build rather than warning. It ships with an empty exception list on purpose: the first entry anyone adds will be a deliberate act, not an inheritance.
  • A guard meant to find the next unguarded door did not find it. To test the watchers that walk the whole route table, a backdoor was built on purpose: an endpoint that switches off any account, taking its target from the request body. It passed all 2,482 tests without a single failure. Four watchers had holes of the same kind, and one had silently stopped seeing 90 modules. All four are repaired, a fifth now covers the whole backend instead of the routers alone, and each one carries a floor check that fails if it did not actually examine anything. That was the real defect: a watcher that examines nothing is green forever.

Under the hood

  • Lists fetch their data in one go instead of row by row. The pending requests list asked the database once per row for that row's rating: 157 queries for 144 rows, and it grew with the archive. It needs 11 now, the user list dropped from 22 to 9, the dashboard from 42 to 25. The answers are unchanged byte for byte, checked across 14 address variants against a copy of a real database. A scale in the test run keeps it that way. It does not ask whether the number is small but whether it grows when the data doubles. Arming it immediately turned up three more of the same kind that the targeted repair had missed.
  • The database gives deleted space back on its own. Expired cache rows were deleted but their pages stayed in the file, so it only ever grew: 172 MB of file for 32 MB of content. It now converts once at startup and reclaims gradually afterwards. On the measured database that was 180 MB before and 34 MB after. The first start takes a little longer for it, once, and says so in the log.
  • Backups and restores no longer collide. The hourly backup beat moved off the event loop, which made it possible for it to run into a restore. Both take turns on a shared lock now, and the restore left the event loop as well: until now it blocked every other request while it ran.
  • The settings are read once per request instead of up to eight times. A page like the dashboard fetched them eight times over, deriving the encryption key afresh for every stored credential each time. Measured with NEXVIEW_SECRET_KEY set, one such request drops from 10.7 ms to 1.3 ms.
  • The release notes no longer travel with every page load. The texts behind "Everything that's new" were the largest single block in the language catalogue, read once by one administrator after an update. They arrive on demand now. First load: 822 kB down to 792 kB.
  • A switched-off foreign key check could survive in the connection pool. Connections are reused, and the setting that makes deletes cascade was applied when one was created, not when it was handed out. In the test suite this surfaced as a deleted account leaving its tickets behind, three files away, with no visible connection. It is set on every checkout now.
  • The build checks the Python code, and the test run stopped waiting for bcrypt. Ruff runs as its own step and stops the build instead of warning; ESLint joined with its threshold at zero. Password hashing is deliberately slow, and the test run paid that price 2,482 times over. Tests hash with a low cost now while everything else keeps the full one. The whole build went from 40 minutes to 7.