···8080);
8181CREATE INDEX IF NOT EXISTS idx_tags_did_repo ON tags(did, repository);
82828383-CREATE TABLE IF NOT EXISTS firehose_cursor (
8484- id INTEGER PRIMARY KEY CHECK (id = 1),
8585- cursor INTEGER NOT NULL,
8686- updated_at TIMESTAMP NOT NULL
8787-);
8888-8989-CREATE TABLE IF NOT EXISTS backfill_state (
9090- id INTEGER PRIMARY KEY CHECK (id = 1),
9191- start_cursor INTEGER NOT NULL,
9292- current_cursor INTEGER NOT NULL,
9393- completed BOOLEAN NOT NULL DEFAULT 0,
9494- updated_at TIMESTAMP NOT NULL
9595-);
9696-9783CREATE TABLE IF NOT EXISTS oauth_sessions (
9884 session_key TEXT PRIMARY KEY,
9985 account_did TEXT NOT NULL,
+22-2
pkg/appview/jetstream/worker.go
···4444 pongsReceived int64
4545 lastPongTime time.Time
4646 pongMutex sync.Mutex
4747+4848+ // In-memory cursor tracking for reconnects
4949+ lastCursor int64
5050+ cursorMutex sync.RWMutex
4751}
48524953// NewWorker creates a new Jetstream worker
···8387 q.Add("wantedCollections", collection)
8488 }
85898686- // Add cursor if specified (for backfilling historical data)
9090+ // Add cursor if specified (for backfilling historical data or reconnects)
8791 if w.startCursor > 0 {
8892 q.Set("cursor", fmt.Sprintf("%d", w.startCursor))
8989- fmt.Printf("Starting from cursor: %d (replaying historical events)\n", w.startCursor)
9393+9494+ // Calculate lag (cursor is in microseconds)
9595+ now := time.Now().UnixMicro()
9696+ lagSeconds := float64(now-w.startCursor) / 1_000_000.0
9797+ fmt.Printf("Jetstream: Starting from cursor %d (%.1f seconds behind live)\n", w.startCursor, lagSeconds)
9098 }
919992100 // Disable compression for now to debug
···263271 w.eventCallback = cb
264272}
265273274274+// GetLastCursor returns the last processed cursor (time_us) for reconnects
275275+func (w *Worker) GetLastCursor() int64 {
276276+ w.cursorMutex.RLock()
277277+ defer w.cursorMutex.RUnlock()
278278+ return w.lastCursor
279279+}
280280+266281// processMessage processes a single Jetstream event
267282func (w *Worker) processMessage(message []byte) error {
268283 var event JetstreamEvent
269284 if err := json.Unmarshal(message, &event); err != nil {
270285 return fmt.Errorf("failed to unmarshal event: %w", err)
271286 }
287287+288288+ // Update cursor for reconnects (do this first, even if processing fails)
289289+ w.cursorMutex.Lock()
290290+ w.lastCursor = event.TimeUS
291291+ w.cursorMutex.Unlock()
272292273293 // Call callback if set
274294 if w.eventCallback != nil {