# Relay Events IndexedDB Plan

## Goal
Store relay debug history in IndexedDB so [`www/relays.html`](../www/relays.html) can render event history from persistent storage (including startup events) without depending on replay payloads from worker RPC.

## Current State
- Relay history is captured in-memory in [`relayEventLog`](../www/ndk-worker.js).
- Relay table data is fetched through [`getRelayData()`](../www/js/init-ndk.mjs).
- UI keeps in-memory [`relayEventHistory`](../www/relays.html) and appends live events from [`ndkRelayEvent`](../www/js/init-ndk.mjs).
- Startup events can be missed by late listeners.

## Proposed Architecture

### 1) SharedWorker owns event writes
In [`www/ndk-worker.js`](../www/ndk-worker.js):
- Add dedicated DB: `relay-events`.
- Add store: `events` with keyPath `id` (autoIncrement).
- Add indexes:
  - `relayUrl` (non-unique)
  - `timestamp` (non-unique)
  - optional compound `relayUrl_timestamp` for efficient per-relay sorted reads.
- Update [`logRelayEvent()`](../www/ndk-worker.js) to persist every event (connect/disconnect/error/auth/authed/notice/reconnect-error/netDebug send/recv/message).
- Keep write path fire-and-forget to avoid blocking network handling.

### 2) Retention and pruning
In [`www/ndk-worker.js`](../www/ndk-worker.js):
- Retention target: ~200 newest events per relay.
- Prune on interval (e.g., every 30–60 seconds) or opportunistically after N writes.
- Prune strategy:
  - Read events for relay ordered by timestamp descending.
  - Delete rows beyond retention threshold.
- Optional global cap safety (e.g., 20k rows) if needed.

### 3) relays page reads directly from IndexedDB
In [`www/relays.html`](../www/relays.html):
- Add local DB helpers to open/query `relay-events`.
- On page init:
  - Load selected relay history from IndexedDB into [`relayEventHistory`](../www/relays.html).
  - Render immediately.
- On relay row selection:
  - Re-query IndexedDB for that relay and re-render.
- Keep existing live listener [`window.addEventListener('ndkRelayEvent', ...)`](../www/relays.html) for real-time updates between polls.

### 4) Simplify UI dedupe path
In [`www/relays.html`](../www/relays.html):
- Remove `lastRelayErrorSeen` synthetic injection path from relay table refresh.
- Let history be solely from:
  - IndexedDB persisted records
  - real-time live stream appends

### 5) Worker RPC contract changes
- [`getRelayData`](../www/ndk-worker.js) remains relay status/type/connection summary endpoint.
- It should **not** carry full event history payload once IndexedDB is authoritative source for history UI.

## Implementation Steps
1. Add IndexedDB schema/helpers in [`www/ndk-worker.js`](../www/ndk-worker.js).
2. Wire all event emission points through updated [`logRelayEvent()`](../www/ndk-worker.js).
3. Add pruning scheduler in worker.
4. Add IndexedDB read helpers in [`www/relays.html`](../www/relays.html).
5. Load selected relay history from IndexedDB at startup + selection change.
6. Remove table-refresh-driven error injection (`lastRelayErrorSeen`) in UI.
7. Validate startup failures appear after page reload/tab reopen.

## Validation Checklist
- Startup connect errors appear for selected relay after opening [`www/relays.html`](../www/relays.html) late.
- Switching relay rows immediately shows historical events from DB.
- Live events append in near-real-time.
- Reads/writes counters continue updating.
- DB does not grow unbounded (retention respected).

## Risks and Mitigations
- **Extra I/O from high event volume**: use fire-and-forget writes + pruning cadence.
- **Race between DB read and live append**: merge by timestamp/id and dedupe in UI.
- **Schema migration conflicts**: isolate into dedicated DB name to avoid existing cache adapters.

## Out of Scope
- Cross-device sync of debug logs.
- Encrypting relay debug event history.
- Export UI for event log files.
