mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-02 02:42:28 +00:00
Merge pull request #2058 from PR0M3TH3AN/codex/refactor-subscriptions-for-nip-44-preference
Prefer NIP-44 encryption hints for subscription lists
This commit is contained in:
3 files changed
+46
-9
No files matched your search
@@ -173,7 +173,7 @@ Video posts should treat `videoRootId` as the stable series identifier that rema
|
||||
| DM relay hints (`NOTE_TYPES.DM_RELAY_LIST`) | `10050` | Repeating `['relay', <relay url>]` tags to advertise delivery relays for NIP-17 gift-wrapped DMs | Empty content |
|
||||
| View counter (`NOTE_TYPES.VIEW_EVENT`) | `WATCH_HISTORY_KIND` (default `30079`) | 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 month (`NOTE_TYPES.WATCH_HISTORY`) | `WATCH_HISTORY_KIND` (default `30079`) | Replaceable list tag `['d', `${WATCH_HISTORY_LIST_IDENTIFIER}:<YYYY-MM>`]` with optional `['month', <YYYY-MM>]` marker plus schema append tags; no chunk pointers required. | JSON payload `{ version, month: 'YYYY-MM', items: [{ id: <eventId\|address>, watched_at?: <unix seconds> }] }` |
|
||||
| Subscription list (`NOTE_TYPES.SUBSCRIPTION_LIST`) | `30000` | `['d', 'subscriptions']` | NIP-04/NIP-44 encrypted JSON array of NIP-51 follow-set tuples (e.g., `[['p', <hex>], …]`) |
|
||||
| Subscription list (`NOTE_TYPES.SUBSCRIPTION_LIST`) | `30000` | `['d', 'subscriptions']`, `['encrypted', 'nip44_v2']` (updated to the negotiated scheme at publish time) | NIP-04/NIP-44 encrypted JSON array of NIP-51 follow-set tuples (e.g., `[['p', <hex>], …]`) |
|
||||
| User block list (`NOTE_TYPES.USER_BLOCK_LIST`) | `10000` | `['d', 'user-blocks']` | NIP-04/NIP-44 encrypted JSON `{ blockedPubkeys: string[] }` |
|
||||
| Hashtag preferences (`NOTE_TYPES.HASHTAG_PREFERENCES`) | `30015` | `['d', 'bitvid:tag-preferences']` plus schema-appended `['encrypted','nip44_v2']` | NIP-44 encrypted JSON `{ version, interests: string[], disinterests: string[] }` |
|
||||
| Admin moderation list (`NOTE_TYPES.ADMIN_MODERATION_LIST`) | `30000` | `['d', 'bitvid:admin:editors']`, repeated `['p', <pubkey>]` entries | Empty content |
|
||||
@@ -210,6 +210,12 @@ events with the shared `['d','subscriptions']` identifier. Builders continue to
|
||||
encrypt the payload so individual follows stay private unless explicitly
|
||||
revealed by the author.
|
||||
|
||||
Publishers should include an explicit `['encrypted', '<scheme>']` tag on
|
||||
subscription list events, preferring `nip44_v2` (or `nip44`) and falling back to
|
||||
`nip04` only when no NIP-44 encryptor is available. This mirrors the encrypted
|
||||
tagging used for hashtag preference lists so consumers can select the correct
|
||||
decryptor quickly.
|
||||
|
||||
NIP-94 compliance: mirror events now normalize the `['m', <mime>]` tag to lowercase—covering both user input and inferred values—so they satisfy [NIP-94's lowercase MIME requirement](./nips/94.md). Related helpers reuse the same normalization to keep future publishers aligned.
|
||||
|
||||
### Direct messages
|
||||
|
||||
+13
-3
@@ -513,7 +513,7 @@ const BASE_SCHEMAS = {
|
||||
name: "d",
|
||||
value: SUBSCRIPTION_LIST_IDENTIFIER,
|
||||
},
|
||||
appendTags: DEFAULT_APPEND_TAGS,
|
||||
appendTags: [["encrypted", "nip44_v2"]],
|
||||
content: {
|
||||
format: "encrypted-tag-list",
|
||||
description:
|
||||
@@ -2405,12 +2405,22 @@ export function buildSubscriptionListEvent(params) {
|
||||
if (identifierName && identifierValue) {
|
||||
tags.push([identifierName, identifierValue]);
|
||||
}
|
||||
appendSchemaTags(tags, schema);
|
||||
const normalizedEncryption =
|
||||
typeof encryption === "string" ? encryption.trim() : "";
|
||||
if (normalizedEncryption) {
|
||||
tags.push(["encrypted", normalizedEncryption]);
|
||||
let updated = false;
|
||||
for (const tag of tags) {
|
||||
if (Array.isArray(tag) && tag[0] === "encrypted") {
|
||||
tag[1] = normalizedEncryption;
|
||||
updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!updated) {
|
||||
tags.push(["encrypted", normalizedEncryption]);
|
||||
}
|
||||
}
|
||||
appendSchemaTags(tags, schema);
|
||||
|
||||
const event = {
|
||||
kind: schema?.kind ?? 30000,
|
||||
|
||||
+26
-5
@@ -40,7 +40,7 @@ import { relaySubscriptionService } from "./services/relaySubscriptionService.js
|
||||
|
||||
const SUBSCRIPTION_SET_KIND =
|
||||
getNostrEventSchema(NOTE_TYPES.SUBSCRIPTION_LIST)?.kind ?? 30000;
|
||||
const DECRYPT_TIMEOUT_MS = 15000;
|
||||
const DECRYPT_TIMEOUT_MS = 90000;
|
||||
const DECRYPT_RETRY_DELAY_MS = 10000;
|
||||
|
||||
function normalizeHexPubkey(value) {
|
||||
@@ -114,17 +114,23 @@ function determineDecryptionOrder(event, availableSchemes) {
|
||||
const available = Array.isArray(availableSchemes) ? availableSchemes : [];
|
||||
const availableSet = new Set(available);
|
||||
const prioritized = [];
|
||||
const hasNip44 =
|
||||
availableSet.has("nip44_v2") || availableSet.has("nip44");
|
||||
const allowNip04 = !hasNip44 && availableSet.has("nip04");
|
||||
|
||||
const hints = extractEncryptionHints(event);
|
||||
const aliasMap = {
|
||||
nip04: ["nip04"],
|
||||
nip44: ["nip44", "nip44_v2"],
|
||||
nip44: ["nip44_v2", "nip44"],
|
||||
nip44_v2: ["nip44_v2", "nip44"],
|
||||
};
|
||||
|
||||
for (const hint of hints) {
|
||||
const candidates = Array.isArray(aliasMap[hint]) ? aliasMap[hint] : [hint];
|
||||
for (const candidate of candidates) {
|
||||
if (candidate === "nip04" && !allowNip04) {
|
||||
continue;
|
||||
}
|
||||
if (availableSet.has(candidate) && !prioritized.includes(candidate)) {
|
||||
prioritized.push(candidate);
|
||||
break;
|
||||
@@ -132,7 +138,11 @@ function determineDecryptionOrder(event, availableSchemes) {
|
||||
}
|
||||
}
|
||||
|
||||
for (const fallback of ["nip44_v2", "nip44", "nip04"]) {
|
||||
const fallbacks = ["nip44_v2", "nip44"];
|
||||
if (allowNip04) {
|
||||
fallbacks.push("nip04");
|
||||
}
|
||||
for (const fallback of fallbacks) {
|
||||
if (availableSet.has(fallback) && !prioritized.includes(fallback)) {
|
||||
prioritized.push(fallback);
|
||||
}
|
||||
@@ -322,7 +332,8 @@ class TinyEventEmitter {
|
||||
|
||||
/**
|
||||
* Manages the user's subscription list (kind=30000 follow set) *privately*,
|
||||
* using encrypted NIP-51 tag arrays (NIP-04/NIP-44) for the content field.
|
||||
* using encrypted NIP-51 tag arrays (prefer NIP-44 v2) for the content field.
|
||||
* Published events should include an explicit ["encrypted", "<scheme>"] tag.
|
||||
* Also handles fetching and rendering subscribed channels' videos
|
||||
* in the same card style as your home page.
|
||||
*/
|
||||
@@ -454,6 +465,8 @@ class SubscriptionsManager {
|
||||
const allowPermissionPrompt = options?.allowPermissionPrompt !== false;
|
||||
const wasBackgroundLoading = this.backgroundLoading;
|
||||
const normalizedUserPubkey = normalizeHexPubkey(userPubkey) || userPubkey;
|
||||
const wasLoadedForUser =
|
||||
this.loaded && this.currentUserPubkey === normalizedUserPubkey;
|
||||
this.lastLoadError = null;
|
||||
|
||||
const readRelays = relayManager.getReadRelayUrls();
|
||||
@@ -487,6 +500,7 @@ class SubscriptionsManager {
|
||||
const cachedSnapshot = parseCachedSubscriptionSnapshot(
|
||||
profileCache.getProfileData(normalizedUserPubkey, "subscriptions"),
|
||||
);
|
||||
const hadCachedSnapshot = cachedSnapshot.hasSnapshot;
|
||||
const shouldForceFullFetch = !cachedSnapshot.hasSnapshot;
|
||||
let events = await nostrClient.fetchListIncrementally({
|
||||
kind: SUBSCRIPTION_SET_KIND,
|
||||
@@ -632,6 +646,12 @@ class SubscriptionsManager {
|
||||
subscribedPubkeys: Array.from(this.subscribedPubkeys),
|
||||
});
|
||||
}
|
||||
if (wasLoadedForUser || hadCachedSnapshot) {
|
||||
userLogger.warn(
|
||||
"[SubscriptionsManager] Preserving cached subscriptions despite decryption failure.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!this.loaded) {
|
||||
this.subscribedPubkeys.clear();
|
||||
this.subsEventId = null;
|
||||
@@ -1104,8 +1124,9 @@ class SubscriptionsManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt (NIP-04) + publish the updated subscription set
|
||||
* Encrypt (prefer NIP-44 v2, fallback to NIP-04) + publish the updated subscription set
|
||||
* as kind=30000 with ["d", "subscriptions"] to be replaceable.
|
||||
* The published event includes an ["encrypted", "<scheme>"] tag.
|
||||
*/
|
||||
async publishSubscriptionList(userPubkey) {
|
||||
if (!userPubkey) {
|
||||
|
||||
Reference in new issue
Block a user