Changelog and Snapshots
Why this exists
Even with push-on-mutation sync and local IndexedDB persistence, teams sometimes need to answer:
- "What changed on this note last week?"
- "Can we restore a note that was deleted by mistake?"
- "Can we recover the board if the database is empty?"
The changelog and snapshot system provides the audit trail and recovery layer.
Changelog entries
Changelog entries are Markdown fragments written to R2.
| Action | Trigger | Content |
|---|---|---|
create_note | Immediate (no debounce) | Full note snapshot |
update_note | 15-second debounce per note | Full note snapshot at fire time |
delete_note | Immediate | Pre-deletion note snapshot |
rename_board | 15-second debounce | New board name |
Why debounced for updates: Writing a changelog entry on every keystroke would be expensive and noisy. The 15-second debounce per note captures the "settled" state after editing stops.
Indexing
After each batch write, the server:
- Appends entries to the KV changelog index (
changelog-index:{boardId}) - Updates per-note version entries in KV (
note-versions:{boardId}:{noteId}) - Saves an updated
current.md(full board snapshot) to R2
Browsing the changelog
The changelog can be browsed in the board UI (if enabled). Entries can be filtered by:
- Note ID
- Action type
- Time range
Board snapshots
A "current snapshot" is the latest full-board Markdown representation stored at current.md in R2.
Point-in-time snapshots may be created:
- Manually via
POST /api/boards/{boardId}/snapshot - Automatically via scheduled triggers (if configured)
Snapshots are stored at snapshots/{timestamp}.md and listed via GET /api/boards/{boardId}/snapshot?list=true.
Recovery strategy
R2 recovery fallback
If a client opens a board and has:
- No local IndexedDB state (first device, cleared browser, new collaborator)
- AND the D1 database is empty for this board
Then BoardHydrator fetches GET /api/boards/{boardId}/snapshot to retrieve the latest current.md from R2 and restore the board state.
Note restore
A specific note version or deleted note can be restored via:
POST /api/boards/{boardId}/log/restoreThis returns the parsed Note object which the client can re-create via the standard sync pipeline.
ChangeLogger client lifecycle
| Event | Action |
|---|---|
| Note created | Immediate queue |
| Note content edited | 15-second debounce per note ID |
| Note deleted | Immediate queue with pre-deletion snapshot |
| Board renamed | 15-second debounce |
| Auto-flush | Every 30 seconds |
| Page unload | navigator.sendBeacon fallback |
| Component unmount | destroy() — flush pending entries |
See also
- Storage Layers — R2 role in the storage hierarchy
- Changelog API — API endpoints for log and snapshot access
- Troubleshooting — Recovering deleted notes