Fixed a few issues that would cause syncing to break:
- Crash: a malformed CBOR frame in approximateHeadSeq killed the process — exercised by the UI's 15s status poll; now skipped like the other stream handlers
- Lost updates: flushDirty cleared the dirty set before its network calls; a transient PDS error silently dropped pending account refreshes for up to 15 min — failed flushes now re-queue
- Lost events: the account firehose never persisted its cursor, so every restart/reconnect dropped events and permanently undercounted the non-rebuildable activity
table — cursor now stored in sync_state and resumed via ?cursor= - Leaks/shutdown: closed sockets are now removed from bookkeeping; new Syncer.stop() wired to SIGTERM/SIGINT for graceful shutdown (pairs with the recent dumb-init
change) - Overlap: fullSync is single-flight, preventing interleaved transactions and duplicate DM notifications on slow syncs
- Audit API: a torn trailing line in audit.log (crash mid-append) 500'd every /api/audit request — unparseable lines are now skipped