Skip to content

feat: add notes and a real-time editing server - #41

Merged
AmrMsCLL merged 1 commit into
mainfrom
feat/realtime-server
Oct 5, 2026
Merged

AmrMsCLL merged 1 commit into
mainfrom
feat/realtime-server

Conversation

@AmrMsCLL

@AmrMsCLL AmrMsCLL commented Oct 5, 2026

Copy link
Copy Markdown
Member

What changed

PR 3c from .claude/.Plans/as2026-10-02-strata-phase-3-technical-design.md. Amr approved decisions 1, 2 and 5 (Yjs, Hocuspocus, real-time inside app). Stacked on #40. Server only; the editor arrives in the next PR.

New dependencies, all MIT: yjs 13.6.33, y-protocols 1.0.7, @hocuspocus/server 4.7.0, ws 8.22.0, plus @hocuspocus/provider 4.7.0 and @types/ws as dev dependencies for the tests. Two departures from the design:

  • Hocuspocus 4 no longer attaches to an existing HTTP server, so the upgrade goes through ws the way Hocuspocus's own server does it. ws was already installed through Hocuspocus; it is now a direct dependency.

  • I ran the installs inside the Linux container. The Windows npm here drops every libc field from the lockfile, which the Alpine images rely on. No existing lockfile lines changed.

  • Notes:

    • notes are items of kind note, so tags, the trash, sharing, search and activity work as for other items;
    • the new notes table holds the page tree (parent, position, icon, pinned);
    • GET and POST /spaces/:id/notes, GET and PATCH /notes/:id. A page's details include whether you can edit it and its breadcrumb path;
    • parents must be in the same space, a page can't move inside itself or its own descendants, and a moved page goes to the end of its new parent's children.
  • Real-time (/api/v1/realtime, same origin as the app):

    • Authentication: the browser sends its usual 15-minute access token in the first message, never in the URL. The server checks it with the same session check as the REST API (JwtStrategy.validate, now exported), and access tokens are not accepted. Upgrades from origins outside AUTH_TRUSTED_ORIGINS get a 403.
    • Permissions: the note must be readable and not trashed. Viewers get a read-only connection, so Hocuspocus drops their changes on the server. Every minute open connections are rechecked, and anyone whose access changed or was removed is disconnected (code 4403).
    • Limits: 2 MB per message, at most 20 open documents per connection, and a 5 MB note. Above that the note isn't saved and editors get a too-large message.
    • Saving: the merged Yjs state goes into note_documents after 2 seconds of quiet, or at most every 10 seconds while typing. Each save also writes the note's plain text into search_documents.body_text, which the existing search reads, and updates who changed the note last.

Migration (runs on deploy)

20261005020000_notes creates notes and note_documents, and adds a notes row for each existing note item:

INSERT INTO "notes" ("item_id") SELECT "id" FROM "items" WHERE "kind" = 'note';

Verification

  • Lint, the server build, tsc --noEmit and test/app.module.spec.ts pass.

  • The new test/document-text.spec.ts covers turning headings, formatted text and nested lists into one line per block.

  • The new test/integration/notes.spec.ts uses real WebSockets with the Hocuspocus client. It covers:

    • the page tree, breadcrumbs, refused loops and cross-space parents, and moving to the top level;
    • viewers can read but not change;
    • two editors syncing live;
    • a viewer's change never reaching the server or the other editor;
    • saving to note_documents, the text becoming searchable, and an outsider being refused;
    • another origin getting a 403;
    • a removed member being disconnected by the access recheck.

    It passes against Postgres on the local stack, together with search.spec.ts and access.spec.ts. Its first run caught moved pages keeping their old position, now fixed.

@github-actions github-actions Bot added the size/xl Over 600 changed lines, excluding lockfiles label Oct 5, 2026
@AmrMsCLL
AmrMsCLL force-pushed the feat/exchange-rates branch from d7c5ae4 to 8459ce4 Compare October 5, 2026 19:03
Base automatically changed from feat/exchange-rates to main October 5, 2026 19:03
@AmrMsCLL
AmrMsCLL force-pushed the feat/realtime-server branch from e2ad95c to 7ca6efc Compare October 5, 2026 19:03
@AmrMsCLL
AmrMsCLL merged commit b1ca9b4 into main Oct 5, 2026
6 checks passed
@AmrMsCLL
AmrMsCLL deleted the feat/realtime-server branch October 5, 2026 19:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xl Over 600 changed lines, excluding lockfiles

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant