auth.ValidatePassword checks every way a password is set: setup,
registration, profile, admin create and reset, reset by mail. Same rule
in the browser before sending (lib/password.ts), with our own German
message instead of the browser popup. Existing shorter passwords still
log in. Profile and admin reset no longer trim the password, which made
passwords with surrounding spaces unusable for login.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pure moves, no behaviour change: the storage layer now has users,
apikeys, thoughts, tasklists, documents, folders, tags, lists, teams and
system files next to sqlite.go (store, schema, migrations); the API has
handlers_*.go per area plus realtime.go and alarms.go next to server.go
(server, router, middleware). Declarations were moved verbatim with an
AST-based tool; imports pruned with goimports.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Due dates can carry a time (YYYY-MM-DDTHH:MM). Alarms fire at that time,
or 09:00 for all-day tasks, minus the user's lead time, in TZ (default
Europe/Berlin, tzdata embedded). A new due date or reopening a task
re-arms the alarm. Recurrence keeps the time; Markdown export/import
carry it. The UI shows relative labels ("Heute, 14:30"), marks overdue
tasks to the minute and groups by local day instead of the UTC date.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Until API keys get scopes they have exactly one right: POST /api/v1/inbox
(thought or task via target) and the webhook ingest; every other endpoint
answers 403 for a key. Middleware and webhook ingest share one token check
(revocation list, token_version, active account), closing FLAW-08.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Discord and custom webhook URLs are checked when saved and before every
call: https only, Discord URLs must point to discord.com. A guarded HTTP
client resolves hosts itself and connects only to allowed IPs; localhost,
link-local and cloud metadata are always blocked, LAN ranges (incl.
Tailscale CGNAT) and plain http only with the new admin switch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without JWT_SECRET, or with one of the published placeholders, the server
generates 32 random bytes once and keeps them in the data directory
(jwt.secret, mode 0600), so tokens cannot be forged and logins survive
restarts. Instances that ran on the old default log in once more.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
All installations are past schema version 2, so the one-time reset is gone;
data is never deleted on start (demo mode still resets). user_version is kept
for future migrations. Legal test uses an in-memory DB (cleanup race).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Server admins manage organizations (one per user); new admin section,
organization on user creation and invites, setup creates the first one
- Sharing modes private / organizations / open, enforced centrally
(CanSee, CanShareWith) for directory, all shares and groups; unknown and
invisible targets give the same error
- Losing visibility (org change, mode change, leaving a group) prunes shares,
group memberships and task assignments; dry run warns before tightening
- Only owners share; editors can no longer reshare lists or remove others'
shares; legacy task_lists.team_id and old /organizations endpoints removed
(FLAW-06, FLAW-10, FLAW-24)
- Schema version: outdated databases are wiped and recreated on start
- Fix setup sample data (createSampleData was ignored); setup and admin
use a shared ToggleSwitch instead of checkboxes
- Docs: sharing-and-permissions rewritten, ui-patterns, REST, DB, flaws
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Background job at start and every 6 h: log entries older than 30 days
(private notes: 7) are removed, documents.log_floor marks the boundary;
clients behind it get resync and keep their queued changes
- Deleted blocks are purged 30 days after deleted_at via a "purge" log
effect, so open editors drop them without reloading; a tombstone stays
while a deleted child still points to it
- Text for a purged block becomes a new block at the end; inserts under a
purged parent go to the top level (server and browser model)
- doc_op_results kept for 90 days; expired lease/session maps are swept
- Tests: storage, API, engine resync, simulation with compactions
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Phase 3 of the collaboration plan:
- Blocks held by someone else show a lock with the name and are read-only;
Enter in such a block adds a new block below. Delete, merge, indent and
drag are not started when a involved block (or one in its branch) is
locked; a short notice explains why (fixes FLAW-25).
- Presence: the editor header lists who else has the note open.
- Conflict copies are highlighted ("Konflikt – Version von …") with
Übernehmen (replaces the original or the title) and Verwerfen. Both check
the locks of the original and of the copy first.
- Undo/redo of own changes (Ctrl/Cmd+Z, Ctrl+Shift+Z, Ctrl+Y, beforeinput
history events): typing is grouped per block until a 1 s pause, up to 200
steps per tab; a step is skipped with a notice if someone else changed or
holds the block meanwhile. New clearConflict props patch on the server.
- New blocks get focus synchronously on mount (fixes FLAW-26).
- Fix: after reopening a note from local storage the engine polled every
30 s instead of 3 s until the next change, because the delay was decided
before the first response told it the note is shared.
- Tests: undo/conflict engine tests, lock/undo/conflict UI tests and
e2e/lock.e2e.mjs (two browsers); all three browser tests pass against a
freshly built image.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The share dialog for notes and shared folders now offers "Bearbeiten" and
"Nur Lesen" and stores write/read accordingly. Editors already edit through
the sync engine since phase 2.
Adds e2e/share.e2e.mjs: shares a note as editor through the dialog and checks
that the recipient can edit it; e2e/run.sh runs both browser tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Phase 2 of the collaboration plan:
- frontend/src/lib/docSync: TypeScript port of the block tree, operations,
effects and order keys (kept identical to Go via shared test vectors),
diff from the editor's flat list to operations, IndexedDB persistence,
HTTP transport and DocSyncEngine (one request in flight, idempotent
resends, backoff, 3 s/30 s polling, keepalive flush and leave on exit,
block leases for focus, 20 s grace and new blocks).
- Text changes carry the version the user saw when typing (first change
wins when coalescing; a stale focused field keeps its old base), so a
concurrent edit becomes a conflict copy instead of a silent overwrite.
- DocumentEditor uses the engine instead of the 800 ms whole-document PUT;
editors (not only owners) can edit, remote changes appear live.
- Server: task IDs of linked blocks are delivered again, linked tasks follow
ticked/deleted blocks after sync, note tags are derived, title changes
notify other users; block IDs already used in another note are renamed
(starter content with fixed IDs was empty for every further user).
- Fix: unlinking tasks from blocks wrote to non-existent columns and never
worked (error was swallowed).
- Tests: Go/TS order key vectors, diff round trips, engine races (lost
responses, offline + reload, edits arriving between typing and sending,
stale focused field, duplicate first block), plus a Playwright test with
two browsers against the real image (e2e/run.sh).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Phase 1 of the collaboration plan (server side):
- internal/collab: block tree with parent IDs and fractional order keys,
operations with conflict rules (version check, block leases, conflict
copies instead of lost text, restore on late edits, cycle checks),
in-memory leases/sessions and long-poll notifier.
- Storage: per-note lock + transaction, append-only effect log (doc_ops),
idempotent results per opId (doc_op_results), snapshot, log paging,
central DocRole. Old flat block data is converted to the tree.
- The existing whole-document save (PUT/PATCH) runs as an adapter through
the same engine, so the current editor keeps working and writes to the log.
- API: GET /docs/{id}/snapshot, POST /docs/{id}/sync, GET /docs/{id}/changes
(long poll), POST /docs/{id}/leave; DOC_SEQ poke for WebSocket/SSE rooms.
- Backend accepts editor (write) shares for notes and folders; the forced
read-only migration is gone. The share dialog still offers read only.
- Tests: conflict matrix, order keys, leases, storage adapter round trip,
idempotency, API protocol incl. long poll, and a randomized simulation
(4 clients, lost/duplicated requests and responses, offline phases,
lease expiry) checking convergence, no lost text and exactly-once results.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Phase 0 of the collaboration plan:
- Remove BroadcastToAll; task, task list and workspace folder events now
only reach users who can see the resource. Task list events carry only
the list ID, clients reload with their own permissions.
- Reject unauthenticated WebSocket/SSE connections (401). Every room join
(URL room, JOIN_ROOM, SSE rooms) is checked via a room authorizer;
/events/rooms requires login and only accepts the caller's own sseId.
- Reorder rejects tasks the caller cannot access or edit (IDOR).
- Lift the server WriteTimeout for SSE streams so they are not cut after 30s.
- Fix folder share removal sending DOC_DELETED to the share ID.
- Tests for all of the above; docs and flaw list updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Add vitest, jsdom and Testing Library as dev dependencies,
replace the placeholder test:run script with vitest run.
- Add outliner tests for Backspace merging and caret placement on click.
- Run the frontend tests in the Dockerfile before the Vite build.
- Update docs (README, architecture, FLAW-19).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Remove outdated claims (CRDT, DocRooms, presence, .md files on disk) and
document the actual architecture, API, permissions and known flaws.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>