8.2 KiB
Nostr Archives integration — rollout checkpoint
Last updated: 2026-06-03
Purpose: Preserve plan position across agent “Fix” runs and chat resets. Resume from Next step below.
API docs: https://nostrarchives.com/docs
Rule file: .cursor/rules/nostr-archives-integration.mdc
Status summary
| Phase | Description | Status |
|---|---|---|
| 0 | Foundation (API client, ingest, graceful failure, search relay constant) | DONE |
| 1 | Profile: follower count + paginated followers list | DONE |
| 2 | search.nostrarchives.com in SEARCHABLE_RELAY_URLS |
DONE (in Phase 0) |
| 3 | General notes search: local + Archives REST merge | DONE |
| 4 | Note stats: prefetch /v1/events/{id}/interactions |
DONE |
| 5a | /v1/search/suggest in profile picker |
DONE |
| 5b | POST /v1/profiles/metadata in search/thread UIs |
DONE |
| 6 | Note page: layered load (session → IDB → Archives → relays) | DONE |
Other recent work (same branch, separate from Archives): Post editor TipTap blank-field fix (stable extensions + placeholder shell) — see commit history on imwald.
Cross-cutting rules (all phases)
- Persist verified events — Any Archives payload with valid Nostr events goes through
persistArchivesEventsIfNew/persistArchivesPayloadEvents→ session cache + IndexedDB archive (client.addEventToCache→queuePersistSeenEvent). Skip if already in session or archive. Slim rows without validsigare not persisted; usegetEventByIdor relay backfill for offline. - Graceful failure —
nostrArchivesApireturnsTArchivesApiResult; never throw. On failure: hide Archives-only UI or fall back to relays/local. Circuit breaker (2 failures → 60s). Setting:storage.getUseNostrArchivesApi()(default on). Hook:useNostrArchivesAvailable(). - Rate limit — 100 req/min client budget via
nostrArchivesApionly; batch metadata and interaction prefetch.
Phase 0 — DONE (foundation files)
| File | Role |
|---|---|
src/constants.ts |
NOSTR_ARCHIVES_API_BASE_URL, NOSTR_ARCHIVES_SEARCH_RELAY_URL, rate limit, StorageKey.USE_NOSTR_ARCHIVES_API, search relay in SEARCHABLE_RELAY_URLS |
src/types/nostr-archives.ts |
TArchivesApiResult, social, interactions, metadata, note page types |
src/lib/nostr-archives-event.ts |
Strip enrichment fields; archivesJsonToVerifiedEvent() |
src/lib/nostr-archives-ingest.ts |
persistArchivesEventsIfNew, persistArchivesPayloadEvents |
src/services/nostr-archives-api.service.ts |
HTTP client, circuit breaker, all endpoint stubs |
src/services/local-storage.service.ts |
getUseNostrArchivesApi / setUseNostrArchivesApi |
src/hooks/useNostrArchivesAvailable.ts |
UI availability |
.cursor/rules/nostr-archives-integration.mdc |
Agent constraints |
Service methods ready: getEventInteractions, getSocialGraph, getEventById, getNotePage, searchNotes, searchGeneral, searchSuggest, fetchProfilesMetadata.
Phase 1 — DONE: Followers on profile
| File | Role |
|---|---|
src/hooks/useNostrArchivesSocial.ts |
Counts via getSocialGraph (followers_limit=0) |
src/components/Profile/SmartFollowers.tsx |
Count + link; hidden when API unavailable or no count |
src/components/Profile/index.tsx |
Renders SmartFollowers next to SmartFollowings |
src/pages/secondary/FollowersListPage/index.tsx |
Paginated list (100/page), infinite scroll |
src/lib/link.ts |
toFollowersList |
src/routes.tsx, PageManager.tsx, navigation.service.ts |
Route + mobile/desktop nav |
i18n en.ts / de.ts |
Followers strings + indexer hint |
Offline: !social.ok or !useNostrArchivesAvailable() → hide count and list unavailable message on list page.
Phase 3 — DONE: General notes search
| File | Role |
|---|---|
src/lib/nostr-archives-search.ts |
searchArchivesNotesForGeneralSearch → /v1/notes/search |
src/components/SearchResult/FullTextSearchByRelay.tsx |
Parallel local + Archives rows; merged hits; source badges |
Offline: Archives progress row hidden when !useNostrArchivesAvailable(); local search unchanged.
Deferred: searchGeneral resolved entity navigation (profile/note picker) — not wired in notes full-text UI yet.
Phase 4 — DONE: Interaction prefetch
| File | Role |
|---|---|
src/lib/note-stats-archives-prefetch.ts |
Batched queue → getEventInteractions |
src/services/note-stats.service.ts |
archivesInteractions on TNoteStats, applyArchivesInteractionCounts, display helpers |
src/components/NoteStats/index.tsx |
prefetchArchivesInteractions when near viewport |
| Stat buttons | displayListCountWithArchives / displayZapSatsWithArchives, noteStatsHasResolvableCounts |
Offline: Prefetch no-op; relay stats unchanged.
Phase 5 — Profiles medium
5a — DONE
src/lib/archives-profile-metadata.ts—archivesMetadataToProfileclient.searchProfilesStaged—searchSuggestafter local, before profile relays
5b — DONE
| File | Role |
|---|---|
src/lib/profile-metadata-batch.ts |
fetchProfilesMetadataBatch — Archives POST metadata, relay gap fill |
FullTextSearchByRelay.tsx |
Search merged profile provider |
ThreadProfileBatchProvider.tsx |
Thread/note panel batch |
ProfileList/index.tsx |
Followers list + other pubkey lists |
ProfileListBySearch/index.tsx |
Profile search results prefetch |
Phase 6 — DONE: Note page pipeline
| File | Role |
|---|---|
src/lib/note-page-load-pipeline.ts |
resolveNoteEventFromArchives, fetchArchivesNotePageBundle, prewarmArchivesNotePage |
src/lib/thread-context-local.ts |
Archives REST after IDB archive, before publication store |
src/hooks/useFetchEvent.tsx |
Local stores + Archives before relay fetchEvent |
src/pages/secondary/NotePage/index.tsx |
Background prewarmArchivesNotePage (replies + interaction counts) |
Deferred: optional IDB bundle cache service; NoteCard hover prewarm (no hover handler in card today).
Suggested PR order (unchanged)
PR0+2: Foundation + search relay(done)- PR1: Phase 1 followers ← resume here after review fixes
- PR3: Notes search merge
- PR4: Interactions prefetch
- PR5: Suggest
- PR6: Metadata batch
- PR7: Note page pipeline
Agent review / Fix button — scratch pad
Use this section to note review findings so the next session does not lose context.
Open issues (fill in when Fix runs)
None — minor gaps addressed 2026-06-03 (settings toggle, search resolved, note bundle profiles, feed metadata batch).
Fixes applied
ARCHIVES_ENGAGEMENT_KEYSincluded'event'— stripped nested{ event: … }wrappers beforearchivesJsonToVerifiedEventcould unwrap them. Removed'event'from the set; test insrc/lib/nostr-archives-event.test.ts.isPersistableNostrEventShapeallowedNaNkind —typeof NaN === 'number'passed; nowNumber.isFinite(ev.kind)(and early return inarchivesJsonToVerifiedEventafter coercion).- Duplicate
getEventByIdinuseFetchEvent— removed secondresolveNoteEventFromArchivescall;resolveThreadContextEventFromLocalStoresalready hits Archives REST. - Settings UI — General settings toggle for
useNostrArchivesApi;nostrArchivesApi.notifySettingsChanged()on change. searchGeneralresolved —tryResolveSearchViaArchivesin SearchPage submit path;src/lib/nostr-archives-search-resolved.ts.- Note page bundle profiles —
ThreadProfileBatchProvider.seedProfiles+prewarmArchivesNotePagecallback on NotePage. - Feed profile batch —
NoteListusesfetchProfilesMetadataBatch.
Resume prompt (paste into a new chat)
Continue Nostr Archives rollout from `.cursor/plans/nostr-archives-rollout.md`.
Nostr Archives rollout phases 0–6 are complete. Use this file for review fixes or follow-ups (e.g. `searchGeneral` resolved navigation, optional note bundle IDB cache).
Respect `.cursor/rules/nostr-archives-integration.mdc`.
Check "Agent review / Fix button" section for any open issues first.