Add encrypted sync via Nostr relay with NIP-CF changes feed - #148
Conversation
Sync engine (sync.rs) connects to a configured relay via WebSocket, authenticates with NIP-42, and subscribes to NIP-CF CHANGES feed for gift-wrapped (NIP-59) events. Notes are encrypted end-to-end — the relay only sees opaque kind:1059 blobs. - SyncManager as Tauri managed state with auto-start on app launch - Note-to-event mapping: notes become kind:30023 rumors, sealed and gift-wrapped to self with NIP-44 encryption - Bidirectional sync: local mutations push to relay with 2s debounce, remote changes upsert locally with last-write-wins resolution - Echo prevention via recently-pushed event ID tracking - Exponential backoff reconnection on disconnect - Sync state reset on relay or identity change - DB migration adds sync_event_id column for deletion targeting - Sync status indicator in sidebar (cloud icons for connected/syncing/error/disconnected) - Seed script updated with stable test keypair and sync_event_id column
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughIntroduces a Nostr-based sync subsystem: DB schema additions for identities and sync IDs, new dependencies for WebSocket/Nostr support, a SyncManager module with lifecycle and commands, UI sync status indicator, and a small docs commit-style note. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as App/UI
participant SM as SyncManager
participant DB as Local DB
participant Relay as Nostr Relay (WS)
UI->>SM: start() / push(PushNote(id))
SM->>DB: read relay URL & identity
SM->>Relay: open WebSocket
Relay->>SM: AUTH challenge
SM->>SM: sign challenge with nsec
SM->>Relay: AUTH response
Relay->>SM: EOSE / ready
SM->>Relay: subscribe CHANGES with checkpoint
alt Local note push
UI->>SM: push(PushNote(id))
SM->>DB: fetch note
SM->>Relay: send signed rumor/note
Relay->>SM: ack / CHANGES
SM->>DB: update sync_event_id
else Remote change arrives
Relay->>SM: CHANGES/EVENTS
SM->>DB: insert/update note
SM->>UI: emit sync-status / change
end
UI->>SM: stop()
SM->>Relay: close connection
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment Tip Migrating from UI to YAML configuration.Use the |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src-tauri/src/lib.rs`:
- Around line 111-114: Move the remote-delete enqueue to run only after the
local deletion succeeds: in delete_note_permanently, query the sync_event_id
(the same lookup currently inside push_deletion) before calling
notes::delete_note_permanently, then call sync_push_deletion after
notes::delete_note_permanently returns Ok; update sync_push_deletion (or add an
overload) to accept the pre-fetched sync_event_id (or accept Option<String>) so
the lookup is not repeated and the push is skipped if the local delete failed.
In `@src/features/shell/sidebar-pane.tsx`:
- Around line 1-4: The file has formatting/style issues flagged by CI; run the
project's formatter (npm run format) or Prettier to reformat
src/features/shell/sidebar-pane.tsx and commit the changes. Ensure imports and
other code in the SidebarPane component (and the top-level imports using
useEffect, useRef, useState, MouseEvent, LogicalPosition, invoke, listen) are
reformatted according to the repo's Prettier/ESLint configuration so CI style
checks pass.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4835048c-4d94-41f2-9e64-87fcf2572982
⛔ Files ignored due to path filters (1)
src-tauri/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (7)
CLAUDE.mdscripts/seed-db.shsrc-tauri/Cargo.tomlsrc-tauri/src/db.rssrc-tauri/src/lib.rssrc-tauri/src/sync.rssrc/features/shell/sidebar-pane.tsx
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@package.json`:
- Line 59: The runtime package react-highlight-words is at ^0.21.0 while the
type package `@types/react-highlight-words` is ^0.20.1 (no 0.21.x types exist); to
resolve the mismatch, pin the runtime dependency react-highlight-words to
^0.20.0 (or another 0.20.x) so it matches the `@types` version, update
package.json accordingly, and run install to verify types resolve cleanly;
alternatively, if you prefer to keep 0.21.0, remove/replace
`@types/react-highlight-words` and add appropriate manual type declarations or a
compatible typed wrapper.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ac5a814e-6cbc-4027-ae5b-5bcabea5f766
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (2)
package.jsonsrc/features/shell/sidebar-pane.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- src/features/shell/sidebar-pane.tsx
| "@types/node": "^25.4.0", | ||
| "@types/react": "^19.1.8", | ||
| "@types/react-dom": "^19.1.6", | ||
| "@types/react-highlight-words": "^0.20.1", |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
@types/react-highlight-words latest version npm
💡 Result:
Latest published version of @types/react-highlight-words on npm is 0.20.1. [1][2]
npm i -D `@types/react-highlight-words`@latest🌐 Web query:
react-highlight-words npm package versions
💡 Result:
react-highlight-words currently has 32 published versions on npm, with the latest version 0.21.0 (published ~8 months ago on the npm package page). [1]
To list all available versions from the command line:
npm view react-highlight-words versions --jsonOr to see the latest only:
npm view react-highlight-words versionSource: npm package page for react-highlight-words. [1]
🌐 Web query:
@types/react-highlight-words all versions 0.21
💡 Result:
There are no 0.21.x releases of @types/react-highlight-words published to the npm registry (the published versions top out at 0.20.1, with recent versions 0.20.1 and 0.20.0). [1]
If you’re looking for 0.21.0, that version exists for the runtime package react-highlight-words (not the @types/* package). [2]
Sources: [1] [2]
Type definition package version lags behind runtime package.
The @types/react-highlight-words package is pinned to ^0.20.1, but the runtime react-highlight-words package (line 45) is at ^0.21.0. No 0.21.x version of the types package exists on npm—only 0.20.1 is available. This version gap could cause type mismatches or missing type coverage for features added in 0.21.0.
Consider pinning the runtime package to ^0.20.0 to match available type definitions, or be prepared to handle type gaps when using 0.21.0-specific APIs.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@package.json` at line 59, The runtime package react-highlight-words is at
^0.21.0 while the type package `@types/react-highlight-words` is ^0.20.1 (no
0.21.x types exist); to resolve the mismatch, pin the runtime dependency
react-highlight-words to ^0.20.0 (or another 0.20.x) so it matches the `@types`
version, update package.json accordingly, and run install to verify types
resolve cleanly; alternatively, if you prefer to keep 0.21.0, remove/replace
`@types/react-highlight-words` and add appropriate manual type declarations or a
compatible typed wrapper.
The sync_event_id must be read before delete_note_permanently destroys the row, and the deletion push should only fire after the local delete succeeds. Pass the pre-fetched ID through the SyncCommand to avoid a redundant query against a now-missing row.
Sync engine (sync.rs) connects to a configured relay via WebSocket, authenticates with NIP-42, and subscribes to NIP-CF CHANGES feed for gift-wrapped (NIP-59) events. Notes are encrypted end-to-end — the relay only sees opaque kind:1059 blobs.
Summary by CodeRabbit
New Features
Documentation