- _body() read the full declared Content-Length into memory before any size
check, so MAX_UPLOAD_BYTES could only reject an upload already held in
RAM. nginx caps this on the proxied path, but the container also listens
on the LAN, so the app now enforces its own 20MB ceiling and closes the
connection rather than reading.
- SQLite ran with the default rollback journal and 5s lock timeout, chosen
when a row was a few KB; rows now carry the original photos, so two
people grading at once could block each other's History. WAL + 30s.
- No socket timeout meant a stalled keep-alive connection held a worker
thread indefinitely.
- An edge excluded as a different material was dropped from the UI with no
explanation, presenting three sides as though they were all four.