Fix: Ensure trust seed mutes are loaded for session actors

- Fixed `ModerationService.clearTrustedMuteTracking` to properly force re-subscription to trusted seeds by passing an empty previous state.
- Enhanced `ModerationService` to filter trusted seeds against the global admin blacklist.
- Added blacklist change event support to `AccessControl` and `TrustBootstrap` to dynamically update seeds when the blacklist changes.
This commit is contained in:
google-labs-jules[bot] committed 2026-01-10 01:29:04 +00:00
1 parent baf9793e3e
commit 1c366a4a78
3 files changed
+49 -4

No files matched your search

+37
View File
@@ -117,6 +117,7 @@ class AccessControl {
this._hydratedFromCache = false;
this._whitelistListeners = new Set();
this._editorListeners = new Set();
this._blacklistListeners = new Set();
this._scheduleHydrateFromCache();
}
@@ -251,6 +252,8 @@ class AccessControl {
this.editors instanceof Set ? new Set(this.editors) : new Set();
const previousWhitelist =
this.whitelist instanceof Set ? new Set(this.whitelist) : new Set();
const previousBlacklist =
this.blacklist instanceof Set ? new Set(this.blacklist) : new Set();
this.editors = new Set(
dedupeNpubs([...ADMIN_EDITORS_NPUBS, ...editors])
@@ -280,6 +283,7 @@ class AccessControl {
return true;
});
this.blacklist = new Set(sanitizedBlacklist);
const blacklistChanged = !areSetsEqual(previousBlacklist, this.blacklist);
const toolkitCandidate =
(typeof window !== "undefined" ? window?.NostrTools : null) ||
@@ -326,6 +330,9 @@ class AccessControl {
if (editorsChanged) {
this._emitEditorsChange(Array.from(this.editors));
}
if (blacklistChanged) {
this._emitBlacklistChange(Array.from(this.blacklist));
}
}
_emitWhitelistChange(whitelistValues) {
@@ -346,6 +353,24 @@ class AccessControl {
}
}
_emitBlacklistChange(blacklistValues) {
if (!this._blacklistListeners.size) {
return;
}
const snapshot = Array.isArray(blacklistValues)
? [...blacklistValues]
: this.getBlacklist();
for (const listener of Array.from(this._blacklistListeners)) {
try {
listener(snapshot);
} catch (error) {
userLogger.error("accessControl blacklist listener failed", error);
}
}
}
_emitEditorsChange(editorsValues) {
if (!this._editorListeners.size) {
return;
@@ -485,6 +510,18 @@ class AccessControl {
};
}
onBlacklistChange(listener) {
if (typeof listener !== "function") {
return () => {};
}
this._blacklistListeners.add(listener);
return () => {
this._blacklistListeners.delete(listener);
};
}
async addModerator(requestorNpub, moderatorNpub) {
if (!this.isSuperAdmin(requestorNpub)) {
return { ok: false, error: "forbidden" };
+8 -4
View File
@@ -520,9 +520,11 @@ export class ModerationService {
}
if (this.trustedSeedContacts instanceof Set) {
const adminSnapshot = this.getAdminListSnapshot();
const blacklistHex = adminSnapshot.blacklistHex;
for (const seed of this.trustedSeedContacts) {
const normalized = normalizeHex(seed);
if (normalized) {
if (normalized && (!blacklistHex || !blacklistHex.has(normalized))) {
merged.add(normalized);
}
}
@@ -635,18 +637,20 @@ export class ModerationService {
setTrustedSeeds(seeds = []) {
const sanitizedSeeds = new Set();
const adminSnapshot = this.getAdminListSnapshot();
const blacklistHex = adminSnapshot.blacklistHex;
if (seeds instanceof Set || Array.isArray(seeds)) {
for (const candidate of seeds) {
const normalized = normalizeToHex(candidate);
if (normalized) {
if (normalized && (!blacklistHex || !blacklistHex.has(normalized))) {
sanitizedSeeds.add(normalized);
}
}
} else if (seeds && typeof seeds[Symbol.iterator] === "function") {
for (const candidate of seeds) {
const normalized = normalizeToHex(candidate);
if (normalized) {
if (normalized && (!blacklistHex || !blacklistHex.has(normalized))) {
sanitizedSeeds.add(normalized);
}
}
@@ -845,7 +849,7 @@ export class ModerationService {
this.emit("trusted-mutes", { total: 0 });
this.rebuildTrustedContacts(new Set(), { previous });
this.rebuildTrustedContacts(new Set(), { previous: new Set() });
}
isTrustedMuteOwner(pubkey) {
+4
View File
@@ -178,4 +178,8 @@ export async function bootstrapTrustedSeeds() {
if (accessControl && typeof accessControl.onEditorsChange === "function") {
accessControl.onEditorsChange(applyOnChange);
}
if (accessControl && typeof accessControl.onBlacklistChange === "function") {
accessControl.onBlacklistChange(applyOnChange);
}
}