ui.js now (a) decides setup-vs-unlock from GET /api/records emptiness,
(b) runs the setup flow (double entry check, min length, TOCTOU re-check
that the DB stayed empty), (c) detects a fully failed unlock (all rows
undecryptable = wrong passphrase) and returns to a clean state, and
(d) blocks double submits while Argon2id derives the key.
Verified: node --check passes, live server serves both screens and a
crypto roundtrip proves setup passphrase unlocks on second visit while
a wrong passphrase is rejected.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Vanilla HTML/CSS/JS frontend served by Flask:
- Unlock screen: passphrase -> Argon2id session key, held in memory
only (never sent to the server); "Sperren" wipes key and DOM rows.
- Records table (Person A/B, start/end, action) rendered via
textContent only (no HTML injection); undecryptable rows are flagged.
- Create/edit modal encrypts payloads with XChaCha20-Poly1305 before
any network call; updates carry the server version (optimistic lock,
conflict toast on 409); delete fetches current version first.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>