Relays architecture

This document describes current code. The approved Fetch / Coverage / Sync design and active implementation milestones describe the next revision; NIP-77, range history, 100-event incremental paging, and removal of presence expiry are not implemented yet.

Entrypoints

Persistence

Tables are additive (CREATE TABLE IF NOT EXISTS). Future schema changes should use explicit migrations. Account removal leaves archive facts intact. Tree kind/year/month grouping is derived in UTC; it is not stored as a UI hierarchy.

Event operations boundary

list renders persisted accounts, relay observations, and archive groups. Design-preview mode and sample data have been removed. Backup/publication are active; network existence queries, sync, export, and event selection remain pending. The header owns account removal and JSON editing. Relays owns NIP-65 read/write configuration, connection status, and published-list application. Extra fetch sources is independent. Fetch browses the archive, Coverage summarizes per-write- relay presence, and Sync groups missing deliveries by relay/kind/date.

accountRelayScope derives fetch sources from configured write relays plus extras; sync destinations are configured write relays only. A URL may exist in both lists; editing either list does not silently edit the other. The source union is deduplicated.

Coverage joins an account's archived events to presence facts per destination relay. Absent, explicitly unknown, future-dated, and older-than-one-hour observations count as unknown. Only fresh missing facts populate the sync tree. Counts represent event/relay pairs; account event kinds/date groups remain unique event counts. Socket checks never imply event presence. Future network existence queries must complete before recording missing, and future delivery runners must recheck stale facts and read back accepted publications.

Network boundary

fetch queries configured and environment bootstrap relays using nostr-tools signature-verified event subscriptions and selects the newest replaceable events. The published NIP-65 snapshot is separate from local configuration. check measures socket establishment only; NIP-42 authentication/relay permissions are future work. No account private keys or bunker secrets are persisted.

backup uses configured write relays plus extra sources, not discovery bootstrap relays. It queries each source sequentially with a 500-event limit, eight-second timeout, and 100-page per-source run budget. Saturated windows split into disjoint inclusive timestamp ranges. Dense single-second windows remain pending/partial; other windows can still be explored. EOSE indicates completion of that bounded query, not a global guarantee of account-history completeness. Verified public events are inserted idempotently and presence observations refreshed atomically. Private messaging and ephemeral kinds are excluded.

Publishing uses generic nostr.signEvent immediate signing with allowedPubkeys. The selected account's author/signature and exact current tags are revalidated by the plugin before publication. Client signing alone never counts as publishing. The renderer polls only while metadata refresh or an operation is running.

Progress: implementation plan. Core contracts: plugin system and Web renderer.