diff --git a/.changeset/lemon-views-know.md b/.changeset/lemon-views-know.md new file mode 100644 index 00000000..bf4e8fc4 --- /dev/null +++ b/.changeset/lemon-views-know.md @@ -0,0 +1,5 @@ +--- +"nostream": minor +--- + +new user-facing config field diff --git a/resources/default-settings.yaml b/resources/default-settings.yaml index e49ad366..1f8948a5 100755 --- a/resources/default-settings.yaml +++ b/resources/default-settings.yaml @@ -62,6 +62,17 @@ nip05: domainBlacklist: [] nip45: enabled: true +wot: + # Web of Trust filtering. When enabled, only events from pubkeys within + # the relay owner's 2-hop follow graph are accepted. + enabled: false + # The relay owner's pubkey in hex. This is the root of the trust graph. + # Required when enabled is true. + seedPubkey: "" + # A pubkey must be followed by at least this many 1-hop accounts to be trusted. + minimumFollowers: 1 + # Hours between full trust graph rebuilds. + refreshIntervalHours: 24 network: maxPayloadSize: 524288 # Uncomment only when using a trusted reverse proxy and configuring trustedProxies. diff --git a/src/@types/settings.ts b/src/@types/settings.ts index d4463dc9..348efdae 100644 --- a/src/@types/settings.ts +++ b/src/@types/settings.ts @@ -266,6 +266,25 @@ export interface Nip05Settings { domainBlacklist?: string[] } +export interface WoTSettings { + enabled: boolean + /** + * The relay owner's pubkey (hex). The trust graph is rooted here. + * Required when enabled is true. + */ + seedPubkey: Pubkey + /** + * Minimum number of 1-hop follows a pubkey must have to enter the trust filter. + * Defaults to 1. + */ + minimumFollowers: number + /** + * How many hours between full trust graph rebuilds. + * Defaults to 24. + */ + refreshIntervalHours: number +} + export interface Settings { info: Info payments?: Payments @@ -276,4 +295,5 @@ export interface Settings { mirroring?: Mirroring nip05?: Nip05Settings nip45?: Nip45Settings + wot?: WoTSettings } diff --git a/test/unit/utils/settings.spec.ts b/test/unit/utils/settings.spec.ts index ca428bdf..349943a0 100644 --- a/test/unit/utils/settings.spec.ts +++ b/test/unit/utils/settings.spec.ts @@ -2,6 +2,8 @@ import { expect } from 'chai' import fs from 'fs' import { join } from 'path' import Sinon from 'sinon' +import { mergeDeepRight } from 'ramda' +import { Settings } from '../../../src/@types/settings' import { SettingsFileTypes, SettingsStatic } from '../../../src/utils/settings' @@ -258,4 +260,39 @@ describe('SettingsStatic', () => { ) }) }) + + describe('WoT settings defaults', () => { + it('default-settings.yaml contains a wot block with enabled: false', () => { + const defaults = SettingsStatic.loadAndParseYamlFile( + SettingsStatic.getDefaultSettingsFilePath() + ) + expect(defaults).to.have.nested.property('wot.enabled', false) + expect(defaults).to.have.nested.property('wot.seedPubkey', '') + expect(defaults).to.have.nested.property('wot.minimumFollowers', 1) + expect(defaults).to.have.nested.property('wot.refreshIntervalHours', 24) + }) + + it('merging an empty user config preserves wot defaults', () => { + const defaults = SettingsStatic.loadAndParseYamlFile( + SettingsStatic.getDefaultSettingsFilePath() + ) + const merged = mergeDeepRight(defaults, {}) as Settings + expect(merged.wot?.enabled).to.equal(false) + expect(merged.wot?.minimumFollowers).to.equal(1) + expect(merged.wot?.refreshIntervalHours).to.equal(24) + }) + + it('user config wot block overrides defaults', () => { + const defaults = SettingsStatic.loadAndParseYamlFile( + SettingsStatic.getDefaultSettingsFilePath() + ) + const userConfig = { wot: { enabled: true, seedPubkey: 'abc123', minimumFollowers: 3 } } + const merged = mergeDeepRight(defaults, userConfig) as Settings + expect(merged.wot?.enabled).to.equal(true) + expect(merged.wot?.seedPubkey).to.equal('abc123') + expect(merged.wot?.minimumFollowers).to.equal(3) + // non-overridden fields stay as defaults + expect(merged.wot?.refreshIntervalHours).to.equal(24) + }) + }) })