diff --git a/docs/relay-trust-bootstrap.md b/docs/relay-trust-bootstrap.md new file mode 100644 index 00000000..3436ac35 --- /dev/null +++ b/docs/relay-trust-bootstrap.md @@ -0,0 +1,62 @@ +# Relay trust bootstrap + +This document describes how `js/services/trustBootstrap.js` seeds trusted relays/actors for moderation, how it waits for relay availability, and how the relay list and admin lists are refreshed. + +## Overview + +`bootstrapTrustedSeeds()` is a lightweight bootstrapping step that: + +1. Waits briefly for admin list hydration and relay readiness. +2. Builds a trusted seed set from admin configuration and defaults. +3. Hands that trusted seed set to `moderationService` so trusted reports, mutes, and summary stats are computed consistently. +4. Re-runs when admin/editor lists change. + +## Configuration inputs + +Trusted seed inputs are pulled from configuration and feature flags: + +- `FEATURE_TRUST_SEEDS` (flag in `js/constants.js`): gates whether bootstrapping runs at all. +- `ADMIN_SUPER_NPUB` (from `config/instance-config.js` via `js/config.js`): always included when set. +- `accessControl.getEditors()` (from `js/accessControl.js`): editor list sourced from admin lists or defaults. +- `DEFAULT_TRUST_SEED_NPUBS` (from `config/instance-config.js` via `js/constants.js`): used only if there are no super/admin seeds. + +## Seed selection and trust computation + +`buildTrustedSeeds()` composes a `Set` of Nostr `npub` identifiers in this order: + +1. Add the super admin (`ADMIN_SUPER_NPUB`). +2. Add editor npubs (`accessControl.getEditors()`). +3. If no seeds exist yet, fall back to `DEFAULT_TRUST_SEED_NPUBS`. + +The resulting `Set` is passed to `moderationService.setTrustedSeeds(seeds)`. +`moderationService` uses these seeds as the baseline for trusted contacts and downstream trust calculations (trusted report counts, trusted mute lists, etc.), and `recomputeAllSummaries()` is called immediately after applying seeds so moderation views reflect the updated trust graph. + +## Relay readiness and fallback behavior + +Bootstrapping is intentionally defensive so the UI can initialize quickly: + +- **Access control hydration**: `waitForAccessControl()` races `accessControl.ensureReady()` against a 3.5s timeout. If hydration times out, bootstrapping logs a dev warning and falls back to defaults. +- **Immediate apply**: Seeds are applied immediately after the first hydration attempt, even if the admin list hydration failed, so moderation does not block on relay availability. +- **Retry when relays are ready**: If hydration failed, `bootstrapTrustedSeeds()` checks for relay readiness via `nostrService.nostrClient.relays` (and pool existence). If relays become available within 3.5s, it retries admin list hydration once. If relays never become ready, it logs a dev warning and re-applies the current seeds to keep moderation functional. +- **Change listeners**: `accessControl.onWhitelistChange()` and `accessControl.onEditorsChange()` are registered so any subsequent admin/editor updates re-run seed application and recompute moderation summaries. + +## Relay list persistence and refresh + +`trustBootstrap` does **not** persist or update relay lists itself. Instead it relies on the shared Nostr client state maintained by the relay manager and access control: + +- **Relay list loading**: `js/relayManager.js` loads a profile’s relay list from NIP-65 (kind `10002`) events, normalizes relay URLs, and falls back to `DEFAULT_RELAY_URLS` if nothing is found or the pool is unavailable. The relay manager updates the active `nostrClient` relay list in memory via `nostrClient.applyRelayPreferences()` (or by assigning to `nostrClient.relays`). +- **Relay list refresh**: Relay list changes are published to relays via `relayManager.publishRelayList()` and reloaded during profile refresh flows, which updates `nostrClient.relays` that `trustBootstrap` later observes. +- **Admin list persistence**: `js/accessControl.js` hydrates admin/editor lists from cached state in `js/adminListStore.js` (local storage). It then refreshes from relay-backed admin lists when `ensureReady()` is called. The cache ensures `bootstrapTrustedSeeds()` can use stored admin lists before relays are available. + +## Data flow summary + +``` +config/instance-config.js + └─> js/config.js -> js/constants.js (FEATURE_TRUST_SEEDS, DEFAULT_TRUST_SEED_NPUBS) + └─> js/services/trustBootstrap.js + ├─> accessControl.ensureReady() (admin lists + cache) + ├─> nostrService.nostrClient.relays (relay readiness) + └─> moderationService.setTrustedSeeds() + recomputeAllSummaries() +``` + +When the relay list or admin lists change, `accessControl` refreshes from cache/relays and the trust bootstrapper re-applies seeds so moderation stays up to date.