Skip to content

Enable prod CRDT + Railway realtime after audit (Notion-like multi-tab sync) #75

Description

@chasehuh

Summary

Audit the already-merged Yjs CRDT + Railway Hocuspocus realtime path (#59 / #60#63). If the implementation is sound for production multi-tab use (Chase’s default: many agentnote.dev tabs open), enable the feature flags on Vercel production and fix Railway origin allowlist for www.agentnote.dev. If the audit finds blockers, do not enable — report and stop.

Why This Matters

Chase keeps multiple agentnote.dev tabs open by default. #73/#74 hardened the legacy whole-document PUT path against silent clobber, but the product goal is Notion-like streaming sync: edits on one tab appear on the other without LWW races. That stack is already built and the collab service is healthy; production simply never flipped the flags. Enabling without a proper audit risks body duplication, origin rejection, or one-way CRDT seeding surprises.

Conversation Context

Current Behavior

  • Prod app uses legacy body path: optimistic concurrency + fix: stop 409-rebased stale buffers from silently clobbering newer note bodies (0804 wipe RCA) #73/fix: stop row-stamped peer drafts from laundering stale buffers into a valid save token (post-#73 multi-tab clobber) #74 guards (baseUpdatedAtRef, base_fingerprint, conflict Overwrite/Use server).
  • CRDT client gate in components/agentnote-app.tsx:
    • CRDT_ENABLED = process.env.NEXT_PUBLIC_AGENTNOTE_CRDT === "1"
    • COLLAB_URL = process.env.NEXT_PUBLIC_AGENTNOTE_COLLAB_URL?.trim() || null
    • When enabled: useNoteDoc({ noteId: activeId, collabUrl: COLLAB_URL, … }); editor binds ytext; legacy dirty/409 path short-circuits.
  • HTTP CRDT: GET/POST /api/notes/:id/doc (+ sync); tables note_doc_updates, note_doc_snapshots via ensureSchema().
  • Realtime: services/collab Hocuspocus; room = canonical note id; Clerk JWT + ownership; shared lib/crdt/note-doc-store.ts with HTTP path.
  • Flag is effectively one-way per note: once a snapshot exists, legacy PUT body returns 409 { reason: "crdt_managed_body" }. Turning the flag off leaves those notes non-body-editable until doc rows are deleted (projected notes.body remains).

Desired Behavior

  1. Audit report (issue comment or PR body) covering correctness, security, ops readiness, and explicit go/no-go.
  2. If go: production flags set and a deploy that picks them up; Railway allowlist includes agentnote.dev; smoke proves two tabs stream the same note.
  3. If no-go: flags remain unset; blockers listed with severity and suggested fix PRs.

Target production env (Vercel project memo / cwhuh/memo, Production)

NEXT_PUBLIC_AGENTNOTE_CRDT=1
NEXT_PUBLIC_AGENTNOTE_COLLAB_URL=wss://agentnote-collab-production.up.railway.app

Target Railway collab env

# Must include the live site origin(s). Prefer explicit list:
AGENTNOTE_ALLOWED_ORIGINS=https://www.agentnote.dev,https://agentnote.dev,https://memo.chasehuh.com

DATABASE_URL + CLERK_SECRET_KEY must already match production Clerk (do not print secrets). Confirm they are set; do not rotate unless broken.

Source Of Truth

Internal

External / live checks

  • Collab health: https://agentnote-collab-production.up.railway.app/health
  • App: https://www.agentnote.dev
  • Vercel: project linked from /Users/huhchaewon/chase/memo/.vercelprj_5vnO1SA0hDdvACA2Rn5vq8jTH0Ah (memo)
  • Railway project: chasehuh (workspace chasehuh), service agentnote-collab

Audit Checklist (must complete before enable)

A. Correctness

  • Seeding is exactly-once / advisory-locked; double-seed cannot duplicate body (tests + code path).
  • HTTP sync and Hocuspocus store merge into one history (no second document lineage).
  • Projection to notes.body stays coherent for publish//p/…, titles, sidebar, revisions.
  • IME composition gate holds remote applies during Hangul composition.
  • Peer-tab BroadcastChannel doc-update + WS path do not fight (origins: remote/broadcast/network).
  • Legacy PUT correctly 409s once CRDT-managed; intentional Overwrite/Use server N/A on CRDT path (merge instead).
  • Existing notes (e.g. 0804.md / dsb-wbhi-aqa) seed from current notes.body without truncation.

B. Security / tenancy

  • Room auth: bad token / wrong user / alias id / missing note → fail closed before DB where designed.
  • Origin allowlist behavior understood; production origins for agentnote.dev included.
  • Clerk secret on collab matches production app Clerk instance.

C. Ops

  • Collab /health green; recent Railway deploy not crash-looping.
  • Compaction cron still configured (CRON_SECRET present on Vercel per earlier env ls).
  • README allowlist note updated if origins change.
  • Rollback: unsetting NEXT_PUBLIC_AGENTNOTE_COLLAB_URL falls back to HTTP CRDT poll; unsetting CRDT leaves already-seeded notes read-only for body — document this in the enable comment.

D. Tests

  • pnpm vitest run (or project test script) green on main after any fix commits.
  • Room-auth + persist merge tests still meaningful vs current code.

Implementation Notes (enable path)

Likely files (only if audit finds gaps)

  • README.md — update allowlisted production origins to agentnote.dev
  • services/collab/** — only if auth/origin/health bugs found
  • lib/crdt/** / components/agentnote-app.tsx — only if audit finds real bugs blocking enable

Ops steps (authorized for production)

  1. Fresh clone of chasehuh/agentnote (not Cursor worktree under shared checkout if using gt; for this repo historical path is worktree/clone + gh pr — prefer gh here; not sumelabs/sume-com).
  2. Run audit locally against main; write findings as issue comment.
  3. If no-go: stop. Do not set Vercel flags.
  4. If go:
    • Set Railway AGENTNOTE_ALLOWED_ORIGINS (redeploy collab if needed).
    • vercel env add / update Production:
      • NEXT_PUBLIC_AGENTNOTE_CRDT=1
      • NEXT_PUBLIC_AGENTNOTE_COLLAB_URL=wss://agentnote-collab-production.up.railway.app
    • Trigger Vercel production redeploy so Next inlines the public env at build time (NEXT_PUBLIC_* require rebuild).
    • Smoke: two browser contexts / tabs on same note — type in A, see stream in B within ~100ms on WS path; confirm header not stuck Offline; confirm no legacy 409 clobber.
  5. If small code/docs fixes are required for go: one PR, then enable after merge+deploy of that PR (order: fix → merge → flags → redeploy).

Authorization

  • Authorized: read production env names (not values dumps of secrets), set the two Vercel public flags, update Railway AGENTNOTE_ALLOWED_ORIGINS, trigger redeploy, read Railway health/logs (no secret printing).
  • Not authorized: DB destructive SQL on production notes, deleting note_doc_* rows, Clerk key rotation, changing DATABASE_URL, CRDT Phase 3 remote cursors/presence.

Edge Cases And Risks

Non-Goals

Acceptance Criteria

  • Written audit with go/no-go and evidence (file paths, test results, health checks).
  • If go: Production has NEXT_PUBLIC_AGENTNOTE_CRDT=1 and collab WSS URL; Vercel redeploy completed.
  • If go: Railway allowlist includes https://www.agentnote.dev (and apex if used).
  • If go: two-tab smoke on a real note shows streaming sync (not 1.5s-only poll feel when WS connected).
  • If no-go: flags unset; blockers filed or listed with next PR suggestion.
  • Operator note: hard-refresh all open agentnote.dev tabs after enable.
  • Issue updated with enable commit/redeploy links; leave open if residuals, else close.

QA Plan

  1. pnpm vitest run (full or at least lib/crdt + collab unit tests).
  2. curl -sS https://agentnote-collab-production.up.railway.app/health
  3. After enable: signed-in two tabs on https://www.agentnote.dev, same note — concurrent type, Hangul IME composition while other tab types, reload mid-edit, offline→online flush if easy.
  4. Confirm publish page still shows projected body for a CRDT-touched note.
  5. Confirm legacy PUT against a seeded note returns crdt_managed_body (optional API check with session).

Suggested PR Scope

  • S if audit-only + env ops (maybe tiny README origin fix PR).
  • M if audit finds a real bug that must land before enable — fix PR first, then ops enable in the same Opus session after merge.

Stage boundary: Opus owns audit + (on go) flag enable + any required fix PR through merge for this agentnote task (Chase asked to turn the flag on). Report done with evidence; hard-refresh reminder for Chase.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions