Root README: updated variant table, API docs with joinRoom/leaveRoom, Nostr events, build instructions. Paste README: added paste-nostr description and cross-device sync info. Nostr README: full documentation for dual-sync architecture. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
119 lines
4.6 KiB
Markdown
119 lines
4.6 KiB
Markdown
# FolderSyncDB — Nostr + Folder Variant
|
|
|
|
Local-first browser key-value and document store with **dual-sync**: shared folder for local/offline use, plus Nostr relays for cross-device reach over the internet.
|
|
|
|
## Why this variant?
|
|
|
|
The other variants sync browsers via a shared folder on disk. This only works when both browsers can access the same folder (same machine or network drive).
|
|
|
|
This variant adds **Nostr relay sync** as a second transport. Nostr relays are public WebSocket servers that relay messages between clients. Your browser connects directly to them — no server of your own, no accounts, no setup.
|
|
|
|
```
|
|
Laptop (file://) Phone (browser)
|
|
┌──────────┐ ┌──────────┐
|
|
│ IndexedDB │ │ IndexedDB │
|
|
└─────┬─────┘ └─────┬─────┘
|
|
│ │
|
|
┌────┴────┐ ┌────┴────┐
|
|
│ Sync │ │ Sync │
|
|
│ Engine │ │ Engine │
|
|
└──┬───┬──┘ └──┬───┬──┘
|
|
│ │ │ │
|
|
Folder Nostr ──── wss://relay ──────── Nostr Folder
|
|
```
|
|
|
|
Use either or both:
|
|
- **Folder only** — local multi-browser sync, works offline
|
|
- **Nostr only** — cross-device sync, no folder needed
|
|
- **Both** — folder for local speed, Nostr for internet reach
|
|
|
|
## How it works
|
|
|
|
**On write:**
|
|
1. Update IndexedDB (fast)
|
|
2. Write event file to folder (if folder connected)
|
|
3. Publish event to Nostr relay (if room joined)
|
|
|
|
**On sync:**
|
|
1. Scan folder for new events from other local browsers
|
|
2. Check Nostr relay cache for events from remote devices
|
|
3. Merge both, deduplicate by filename
|
|
4. Apply unseen events to IndexedDB
|
|
5. Bridge: folder events get published to Nostr, Nostr events get written to folder
|
|
|
|
**Real-time push:** When a Nostr subscription receives a new event, `sync()` is triggered immediately — no waiting for the polling interval.
|
|
|
|
## Quick start
|
|
|
|
```ts
|
|
import { FolderSyncDB } from './nostr/src/index.ts';
|
|
|
|
const db = await FolderSyncDB.open({
|
|
autoSyncIntervalMs: 5000,
|
|
relays: [ // optional, defaults to popular public relays
|
|
'wss://relay.damus.io',
|
|
'wss://nos.lol',
|
|
'wss://relay.nostr.band',
|
|
],
|
|
});
|
|
|
|
// Local folder sync (same as other variants)
|
|
await db.selectFolder();
|
|
|
|
// Cross-device sync via Nostr
|
|
await db.joinRoom('my-shared-room-key');
|
|
|
|
// Use normally
|
|
await db.kv.set('theme', 'dark');
|
|
const theme = await db.kv.get('theme');
|
|
|
|
// Listen for events
|
|
db.on('nostr:connected', ({ roomKey }) => console.log('joined:', roomKey));
|
|
db.on('change', (e) => console.log('changed:', e));
|
|
```
|
|
|
|
## API additions
|
|
|
|
On top of the standard FolderSyncDB API, this variant adds:
|
|
|
|
| Method | Description |
|
|
|--------|-------------|
|
|
| `joinRoom(roomKey)` | Join a Nostr sync room. All clients with the same key sync together. |
|
|
| `leaveRoom()` | Disconnect from the current room. |
|
|
| `isConnected()` | Whether a room is currently joined and relay is connected. |
|
|
| `currentRoom` | The current room key, or `null`. |
|
|
|
|
### OpenOptions additions
|
|
|
|
| Option | Type | Default | Description |
|
|
|--------|------|---------|-------------|
|
|
| `relays` | `string[]` | 3 popular public relays | Nostr relay WebSocket URLs |
|
|
| `roomKey` | `string` | none | Auto-join this room on open |
|
|
|
|
### Events additions
|
|
|
|
| Event | Payload | When |
|
|
|-------|---------|------|
|
|
| `nostr:connected` | `{ roomKey }` | Joined a Nostr room |
|
|
| `nostr:disconnected` | -- | Left a Nostr room |
|
|
|
|
## Room key
|
|
|
|
The room key is simply a shared string that identifies your sync group. It's used as a Nostr tag — all clients subscribed to the same tag receive each other's events.
|
|
|
|
- Anyone who knows the room key can join and sync
|
|
- Events are not encrypted (v1) — use random room keys for privacy through obscurity
|
|
- Each client generates its own Nostr keypair (stored in IndexedDB)
|
|
|
|
## Works from `file://`
|
|
|
|
Nostr relays use WebSocket (`wss://`), which works from `file://` origins in Chrome. Unlike WebRTC or `fetch()`, browsers don't block outgoing WebSocket connections from `file://` pages.
|
|
|
|
## Dependencies
|
|
|
|
- `nostr-tools` — lightweight Nostr protocol library (keypair generation, event signing, relay pool management). Pure JS, no WASM.
|
|
|
|
## Local cache
|
|
|
|
Uses IndexedDB (same as the `indexeddb/` variant). Zero-overhead, browser-managed persistence.
|