Password hashes are self-describing PHC strings, so the cost parameters
can be raised later without locking anyone out: NeedsRehash reports which
hashes to upgrade at the next login, when the plaintext is briefly at hand.
Session, invite, reset and API tokens are stored only as SHA-256 hashes;
the plaintext exists once, in the link or the reply. Unsalted is correct
here and only here — the input is 256 bits of randomness that has to be
found by value in a WHERE clause.
feat(store): project roles with viewer/editor/owner and explicit denial
Roles are strictly ordered so every check is "at least X". Access to a
task is access to its project: being the assignee or the author confers
nothing, or removing someone from a project would leave them holding
tasks inside it. Checks take a Querier so they can run in the same
transaction as the write they guard.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Initial data model for verdande: users, sessions, invites, projects,
sections, tasks, labels, reminders, comments, attachments, activity
and filters, plus personal API tokens.
Search indexes a generated `fold` column alongside the original text.
FTS5's remove_diacritics does not touch ø and æ — Unicode treats them
as letters rather than accented forms — so without it a Danish user
cannot find "grøn" by typing "gron". MatchExpr quotes every term so
free text containing FTS5 operators cannot reach the query planner as
syntax.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>