Subj : docs/superpowers/specs/2026-07-31-hashed-password-storage-design.md To : Git commit to main/sbbs/master From : Rob Swindell (on Debian Linux) Date : Sun Aug 16 2026 22:55:13 https://gitlab.synchro.net/main/sbbs/-/commit/3899ab7cae4222373328a5ca Modified Files: docs/superpowers/specs/2026-07-31-hashed-password-storage-design.md Log Message: docs: webv4 breaks under hashed passwords, in two ways The cleartext-sink list did not reach the web UIs, which is where the most visible breakage is. The page-content half is the RLogin auto-login convention: the password travels in the client-user-name field, compared against useron.pass at answer.cpp:170, so webv4/pages/003-games.xjs:64 writes user.security.password straight into the served HTML as Options.RLoginClientUsername. It is rendered into JavaScript source, sent to the browser, and sent back through the WebSocket-to-RLogin proxy when a door is launched. web/root/members/externals.ssjs:60 does the same in the v3 UI, web/lib/leftnav_html.ssjs:8 builds ftp://alias:password@host into the left nav of every page, and rlogin.js:86,90 is the door-side form -- its -h variant is no escape, since sha1(password + ...) still has to read the cleartext to compute it. The larger half is session resumption. validateSession() (webv4/lib/auth.js:106) re-establishes a cookie-carried session by reading the stored password back out and passing it to login(); guest login (:229) does the same with the Guest account's password. No pass.tab value makes either succeed, so under hashing every webv4 session fails on the request after the form login and the guest path fails before anyone logs in. Both go in the sinks table, in the key-or-token table, and in a prose block splitting the two failures; the answer to both is a short-lived single-use token bound to user number and client IP, accepted in the client-user-name slot, with the session record's existing key made the authenticated object. That also corrects a claim in Constraints. webv4's form login runs the KDF once, but the cookie path re-authenticates through login() on every request, so as written it costs a derivation per request exactly as HTTP Basic does -- making the session the authenticated object is a cost fix there as well as a correctness fix under hashing. Sequencing step 4 gains the note that neither webv4 site has a compare-only form, so both are their own work and must land before the storage change rather than folding into the mechanical verify_password() conversion. Co-Authored-By: Claude Opus 5 (1M context) --- SBBSecho 3.37-Linux * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705) .