Document watch history v2 pipeline

This commit is contained in:
thePR0M3TH3AN committed 2025-10-02 15:26:49 -04:00
1 parent 02bd23ccaf
commit 09fc89bda6
2 files changed
+83 -5

No files matched your search

+20 -3
View File
@@ -49,8 +49,8 @@ read/write split.
| NIP-94 mirror (`NOTE_TYPES.VIDEO_MIRROR`) | `1063` | Tags forwarded from `publishVideo` (URL, mime type, thumbnail, alt text, magnet) | Plain text alt description |
| Relay list (`NOTE_TYPES.RELAY_LIST`) | `10002` | Repeating `['r', <relay url>]` tags, optionally with a marker of `'read'` or `'write'` to scope the relay; marker omitted for read/write relays | Empty content |
| View counter (`NOTE_TYPES.VIEW_EVENT`) | `WATCH_HISTORY_KIND` (default `30079`, clients also read legacy `30078`) | Canonical tag set: `['t','view']`, a pointer tag (`['e', <eventId>]` or `['a', <address>]`), and a stable dedupe tag `['d', <view identifier>]`, with optional `['session','true']` when a session actor signs; schema overrides may append extra tags. `['video', ...]` is supported for legacy overrides only. | Optional plaintext message |
| Watch history index (`NOTE_TYPES.WATCH_HISTORY_INDEX`, legacy) | `WATCH_HISTORY_KIND` (default `30079`, clients also read legacy `30078`) | `['d', WATCH_HISTORY_LIST_IDENTIFIER]`, `['snapshot', <id>]`, `['chunks', <total>]`, repeated `['a', <address>]` pointers to each chunk event plus schema append tags | JSON payload `{ snapshot, totalChunks }` |
| Watch history chunk (`NOTE_TYPES.WATCH_HISTORY_CHUNK`, legacy) | `WATCH_HISTORY_KIND` (default `30079`, clients also read legacy `30078`) | `['d', <chunk identifier>]`, `['encrypted','nip04']`, `['snapshot', <id>]`, `['chunk', <index>, <total>]`, pointer tags for each entry, plus schema append tags | NIP-04 encrypted JSON chunk (`{ version, snapshot, chunkIndex, totalChunks, items[] }`) |
| Watch history index (`NOTE_TYPES.WATCH_HISTORY_INDEX`) | `WATCH_HISTORY_KIND` (default `30079`, clients also read legacy `30078`) | `['d', WATCH_HISTORY_LIST_IDENTIFIER]`, `['snapshot', <id>]`, `['chunks', <total>]`, repeated `['a', <chunk address>]` pointers plus schema append tags | JSON payload `{ snapshot, totalChunks }` (may be empty when using tags only) |
| Watch history chunk (`NOTE_TYPES.WATCH_HISTORY_CHUNK`) | `WATCH_HISTORY_KIND` (default `30079`, clients also read legacy `30078`) | `['d', <snapshotId:index>]`, `['encrypted','nip04']`, `['snapshot', <id>]`, `['chunk', <index>, <total>]`, optional leading `['head','1']` on the first chunk, pointer tags for each item, plus schema append tags | NIP-04 encrypted JSON chunk (`{ version, snapshot, chunkIndex, totalChunks, items[] }`) |
| Subscription list (`NOTE_TYPES.SUBSCRIPTION_LIST`) | `30002` | `['d', 'subscriptions']` | NIP-04 encrypted JSON `{ subPubkeys: string[] }` |
| User block list (`NOTE_TYPES.USER_BLOCK_LIST`) | `30002` | `['d', 'user-blocks']` | NIP-04 encrypted JSON `{ blockedPubkeys: string[] }` |
| Admin moderation list (`NOTE_TYPES.ADMIN_MODERATION_LIST`) | `30000` | `['d', 'bitvid:admin:editors']`, repeated `['p', <pubkey>]` entries | Empty content |
@@ -63,4 +63,21 @@ builders inherit the same debugging knobs.
### Watch history identifiers
The watch history publishing pipeline has been retired. These identifiers remain documented so existing relays can keep serving legacy payloads, but new clients no longer emit or refresh watch-history events.
The encrypted watch history pipeline is gated by the `FEATURE_WATCH_HISTORY_V2`
runtime flag. When the flag is disabled, clients continue emitting view events
but skip publishing snapshots; the UI still resolves legacy `watch-history:v2:index`
lists so operators can stage the rollout per deployment.【F:config/instance-config.js†L69-L94】【F:js/watchHistoryService.js†L82-L140】【F:js/watchHistoryService.js†L948-L985】
Active identifiers include the default `WATCH_HISTORY_LIST_IDENTIFIER`
(`"watch-history"`) and the legacy aliases enumerated in
`WATCH_HISTORY_LEGACY_LIST_IDENTIFIERS`. Chunk events derive their `d` tag from
`<snapshotId>:<index>`, advertise `['snapshot', <id>]`, and carry `['chunk', <index>, <total>]`
plus an optional leading `['head','1']` marker so relays can prioritize the first
ciphertext. All chunk content is encrypted with NIP-04 and stores only pointer
entries; richer metadata remains on-device via the
[`WatchHistoryService`](../js/watchHistoryService.js) APIs, which default to
pointer-only writes and local-only metadata caches.【F:config/instance-config.js†L60-L78】【F:js/nostrEventSchemas.js†L157-L189】【F:js/nostr.js†L2329-L2369】【F:js/watchHistoryService.js†L331-L376】
Refer to the [`WatchHistoryService`](../js/watchHistoryService.js) for queue
management hooks, manual snapshot helpers, and metadata toggle controls that
complement these schema definitions.【F:js/watchHistoryService.js†L695-L776】【F:js/watchHistoryService.js†L1040-L1093】
+63 -2
View File
@@ -1,3 +1,64 @@
# Watch History Logging Cheatsheet
# Watch history logging (v2 pipeline)
The legacy watch history publisher has been removed. Clients no longer emit watch-history snapshot events, and related console logs have been retired alongside the feature.
BitVid's watch history sync is orchestrated by the
[`WatchHistoryService`](../js/watchHistoryService.js). The service collects
per-video pointer data during playback, batches it in session storage, and emits
encrypted snapshots to relays only when a publish is triggered. Relay payloads
stick to pointer identifiers so title, thumbnail, and profile context remain on
the client unless an operator opts into local metadata caching.
## Snapshot composition
Snapshots publish two event types to the shared `WATCH_HISTORY_KIND` stream:
1. **Chunk events** — Each chunk carries a deterministic identifier in its
`d` tag, advertises its `snapshot` and `chunk` position, and is tagged with
`['encrypted','nip04']`. The first chunk is additionally marked as
`['head','1']` and may append `"a"` tags that reference previous chunk
addresses, allowing readers to reconstruct the full set even when relays
deduplicate aggressively.【F:js/nostrEventSchemas.js†L175-L189】【F:js/nostr.js†L2329-L2369】
2. **Pointer event** — After chunks are accepted, BitVid emits a compact index
event containing the canonical list identifier (`['d', WATCH_HISTORY_LIST_IDENTIFIER]`),
the `snapshot` label, the total chunk count, and an `a` tag for each chunk
address.【F:js/nostrEventSchemas.js†L157-L170】【F:js/nostr.js†L2425-L2441】
Chunk content is a NIP-04 encrypted JSON envelope of `{ version: 2, snapshot,
chunkIndex, totalChunks, items[] }`. Items are pointer descriptors (`e` or `a`
references plus optional relay hints) so that relays never learn which titles
were played—only which events or addresses the client can dereference later.【F:js/nostr.js†L2337-L2364】
## Snapshot cadence
`WatchHistoryService.publishView` queues pointer entries while the feature flag
is enabled, merging repeats with a one-minute throttle and persisting the queue
in `sessionStorage`. Snapshots run when `watchHistoryService.snapshot()` is
invoked (e.g., on `beforeunload`, `visibilitychange`, or manual sync actions in
the history UI) and clear the queue on success.【F:js/watchHistoryService.js†L872-L917】【F:js/app.js†L6041-L6081】【F:js/app.js†L7467-L7484】
If no items are pending, the snapshot resolves with an empty result so UI calls
remain idempotent.【F:js/watchHistoryService.js†L932-L944】
## Republish and backoff
When a snapshot publish fails but the response is retryable, the service stores
the returned `snapshotId`, records the originating reason, and delegates to the
Nostr client to retry with exponential backoff. Retries start after two seconds,
double per attempt with 25% jitter, cap at five minutes, and stop after eight
attempts unless a publish succeeds sooner.【F:js/watchHistoryService.js†L948-L985】【F:js/watchHistoryService.js†L723-L777】【F:js/nostr.js†L1988-L2087】
## Legacy payload handling
Readers hydrate history by combining the pointer event, any encrypted chunks,
and fallback metadata embedded in legacy watch-history lists. The resolver still
accepts the `watch-history:v2:index` identifier, merges pointer tags from the
index event, and falls back to plaintext content when decryption fails so older
relays remain compatible.【F:config/instance-config.js†L60-L78】【F:js/nostr.js†L2704-L2779】【F:js/nostr.js†L5216-L5264】
## Local metadata toggles
By default the service only persists pointer data to relays; richer metadata is
stored locally (and opt-in). Users can toggle the "store watch history metadata"
preference, which flips a localStorage flag, clears cached entries when
disabled, and never pushes the sanitized video/profile cache over the network.【F:js/watchHistoryService.js†L82-L140】【F:js/watchHistoryService.js†L232-L313】【F:js/watchHistoryService.js†L331-L376】
See the [`WatchHistoryService` API](../js/watchHistoryService.js) for
event hooks, queue inspection helpers, and manual snapshot controls.