Abstract
Highlight any webpage, attach a note, and sync it peer-to-peer. No account, no backend, no company reading your research. An Idris 2 CRDT core with dependent types handles conflict-free merge and Merkle integrity; TypeScript + WebRTC power the Chrome extension UX — Hypothesis, but local-first.
Highlights
- —Idris 2 CRDT core with dependent types for conflict-free collaborative merge
- —WebRTC P2P sync with Merkle-tree integrity — no server, no account
- —Chrome extension for highlight-and-annotate any webpage
README
View on GitHubMerkle Notes
Highlight any webpage, attach a note, and sync it directly to your collaborators. No account, no backend, no company in the middle reading your research.
Merkle Notes is a Chrome extension that does P2P collaborative annotations over WebRTC. The hard part (conflict-free merge, sync integrity) lives in an Idris 2 CRDT core with dependent types. The fun part (highlighting, room codes, peer status) is TypeScript.
Think of it as Hypothesis, except your notes stay local-first and sync peer-to-peer.
What you get
- Highlight + annotate any page. Select text, add a note, done.
- P2P sync over WebRTC data channels. STUN only, no TURN server.
- CRDT merge so concurrent edits converge without conflicts.
- Merkle tree for sync integrity. Peers handshake on root hashes, then exchange missing ops.
- Resilient anchors via XPath, CSS selectors, and fuzzy text fallback. Annotations survive minor DOM changes.
- Room codes to find peers. Same code, same BroadcastChannel namespace, same room.
Who is this for?
Journalists reviewing sources together. Study groups reading the same article. Legal researchers marking up filings. Anyone who wants shared reading notes without handing them to a SaaS vendor.
How it works (short version)
You highlight text
-> content script builds an anchor (XPath + CSS + offsets)
-> background worker creates a CRDT op
-> WebRTC data channel sends it to peers
-> peers apply the op, re-render highlights
-> Merkle roots match, everyone converges
The Idris side proves the merge logic is total and follows an LWW join-semilattice. The JavaScript bridge handles browser IO. TypeScript runs the extension shell.
Quick start
You need
- Idris 2 0.8+
- Node.js 18+
- Google Chrome
Build
git clone https://github.com/thedhruvhegde/merkle-notes.git
cd merkle-notes
npm install
npm run build
Load in Chrome
- Go to
chrome://extensions - Turn on Developer mode
- Click Load unpacked
- Pick the
dist/folder
Use it
- Open any webpage
- Click the Merkle Notes icon in the toolbar
- Copy the room code and send it to a collaborator (or join theirs)
- Select text on the page
- Type a note, click Highlight
- Watch it show up on the other peer's browser
Two Chrome windows side by side is the fastest way to test.
Project layout
crdt/src/ Idris 2 CRDT core (the interesting part)
crdt/js/ Browser FFI runtime bridge
extension/src/ TypeScript Chrome extension
docs/ Architecture, lattice proofs, publishing guide
dist/ Built extension (load this in Chrome)
Idris modules
| Module | What it does |
|---|---|
CRDT.Lattice | LWW join-semilattice for timestamp ordering |
CRDT.Core | SiteId, Lamport clocks, causal timestamps |
CRDT.Annotation | Annotation records with dual anchors |
CRDT.Ops | Op-based replication and LWW merge |
CRDT.Merkle | Merkle tree for sync diff detection |
CRDT.Document | Document state machine (insert/update/delete) |
CRDT.Sync | Wire protocol messages |
Proof sketches live in docs/LATTICE.md. Full system design in docs/ARCHITECTURE.md.
Sync protocol
Peers in the same room find each other via BroadcastChannel, then negotiate WebRTC directly:
- Exchange SDP offer/answer + ICE candidates (STUN:
stun.l.google.com:19302) - Open a
merkle-syncdata channel - Send
HS:<merkleRoot>handshake - If roots differ, ship missing ops as
OPS:[...] - Apply ops through the CRDT bridge, re-render highlights
No central server. If both peers are behind symmetric NAT, connection may fail. That is a deliberate tradeoff (no TURN bill, no relay operator).
Threat model (be honest)
- Room codes are the trust boundary. Anyone with the code can join and read notes.
- Annotations are plaintext on the wire. No E2E encryption yet.
- Storage is local (
chrome.storage.local). Uninstalling the extension removes your copy.
Publishing
Chrome Web Store steps: docs/PUBLISHING.md
Version
1.0.0 - initial release. CRDT core, WebRTC sync, highlight UI, room codes.
License
MIT
Connect with Dhruv Hegde
More of Dhruv Hegde's open-source work on GitHub and LinkedIn.