Problem
Users have no visibility into the sync state of their collections:
- Is this collection shared or local?
- Did I create it or import it from someone else?
- Are any peers currently online?
- Is sync happening?
The P2P infrastructure (iroh-docs) works, but it's a black box to users.
Solution
Expose sync state information that iroh already tracks, plus derive ownership from entry authorship.
Data Available from iroh (no extra storage needed)
| Info |
Source |
How |
| Capability |
docs.list() |
Returns (NamespaceId, CapabilityKind) - Write or Read |
| Ownership |
_collection entry |
Compare entry.author() with our author_id |
| Peers online |
Event stream |
Track NeighborUp / NeighborDown events |
| Sync activity |
Event stream |
SyncFinished, InsertRemote, ContentReady events |
UI States
| Capability |
Owner |
Peers |
Display |
| Write |
Us |
0 |
Local |
| Write |
Us |
N |
Sharing · N online |
| Write |
Other |
0 |
Joined (offline) |
| Write |
Other |
N |
Joined · N online |
| Read |
Other |
0 |
Read-only (offline) |
| Read |
Other |
N |
Read-only · N online |
Implementation
Backend Changes
1. New module: sync_state.rs
Track per-collection sync state derived from iroh events:
pub struct CollectionSyncState {
pub capability: CapabilityKind, // Write or Read
pub is_creator: bool, // We signed _collection entry
pub neighbors: HashSet<PublicKey>, // Currently connected peers
pub last_sync: Option<SystemTime>, // Last SyncFinished event
}
2. Modify SyncWatcher (jobs/watcher.rs)
Currently only handles InsertRemote for document processing. Extend to also track:
NeighborUp(PublicKey) → add to neighbors set
NeighborDown(PublicKey) → remove from neighbors set
SyncFinished → update last_sync timestamp
3. New storage method
Add to Storage:
pub async fn get_collection_owner(&self, namespace_id: NamespaceId) -> Result<AuthorId>
Returns the author who signed the _collection entry.
4. New Tauri commands
#[tauri::command]
pub async fn get_collection_sync_status(collection_id: String) -> CollectionSyncStatus
#[tauri::command]
pub async fn get_node_info() -> NodeInfo // Our node_id, for debugging/display
5. New Tauri events
Emit to frontend when state changes:
sync-neighbor-changed - peer count changed
sync-finished - sync completed with a peer
Frontend Changes
1. New store: sync-state.svelte.ts
interface CollectionSyncState {
status: 'local' | 'sharing' | 'joined' | 'read-only';
peerCount: number;
lastSync: Date | null;
}
// Map of collection ID → sync state
const syncStates = $state<Map<string, CollectionSyncState>>(new Map());
Pattern follows existing stores (model-state.svelte.ts, import-state.svelte.ts):
- Module-level
$state for persistence across navigation
setupListeners() for Tauri events
- Getter functions for reactive access
2. Update files page (routes/(app)/files/+page.svelte)
Add sync status badge to collection cards:
<span class="text-xs text-neutral-500">
{#if syncState.status === 'local'}
Local
{:else if syncState.status === 'sharing'}
Sharing · {syncState.peerCount} online
{:else if syncState.status === 'joined'}
Joined {syncState.peerCount > 0 ? `· ${syncState.peerCount} online` : '(offline)'}
{:else}
Read-only {syncState.peerCount > 0 ? `· ${syncState.peerCount} online` : '(offline)'}
{/if}
</span>
Files to Modify
Backend
crates/insight-core/src/lib.rs - Add sync state tracking to AppState
crates/insight-core/src/jobs/watcher.rs - Track neighbor events
crates/insight-core/src/storage/mod.rs - Add get_collection_owner() method
src-tauri/src/commands/mod.rs - New commands for sync status
Frontend
src/lib/stores/sync-state.svelte.ts - New store (create)
src/routes/(app)/files/+page.svelte - Display sync status on collections
Non-Goals
- Persisting "ever shared" state - not important, rely on iroh's live state
- Detailed sync progress bars - simple status is enough for now
- Per-document sync status - collection-level is sufficient
Problem
Users have no visibility into the sync state of their collections:
The P2P infrastructure (iroh-docs) works, but it's a black box to users.
Solution
Expose sync state information that iroh already tracks, plus derive ownership from entry authorship.
Data Available from iroh (no extra storage needed)
docs.list()(NamespaceId, CapabilityKind)-WriteorRead_collectionentryentry.author()with ourauthor_idNeighborUp/NeighborDowneventsSyncFinished,InsertRemote,ContentReadyeventsUI States
Implementation
Backend Changes
1. New module:
sync_state.rsTrack per-collection sync state derived from iroh events:
2. Modify
SyncWatcher(jobs/watcher.rs)Currently only handles
InsertRemotefor document processing. Extend to also track:NeighborUp(PublicKey)→ add to neighbors setNeighborDown(PublicKey)→ remove from neighbors setSyncFinished→ update last_sync timestamp3. New storage method
Add to
Storage:Returns the author who signed the
_collectionentry.4. New Tauri commands
5. New Tauri events
Emit to frontend when state changes:
sync-neighbor-changed- peer count changedsync-finished- sync completed with a peerFrontend Changes
1. New store:
sync-state.svelte.tsPattern follows existing stores (
model-state.svelte.ts,import-state.svelte.ts):$statefor persistence across navigationsetupListeners()for Tauri events2. Update files page (
routes/(app)/files/+page.svelte)Add sync status badge to collection cards:
Files to Modify
Backend
crates/insight-core/src/lib.rs- Add sync state tracking to AppStatecrates/insight-core/src/jobs/watcher.rs- Track neighbor eventscrates/insight-core/src/storage/mod.rs- Addget_collection_owner()methodsrc-tauri/src/commands/mod.rs- New commands for sync statusFrontend
src/lib/stores/sync-state.svelte.ts- New store (create)src/routes/(app)/files/+page.svelte- Display sync status on collectionsNon-Goals