Skip to content

Moderation Policies ​

Policies allow you to prevent spam on your relay (or in your client).

Nostrify introduces a model for writing policies and composing them in pipelines. Policies are fully configurable and it's easy to add your own or install more from anywhere on the net!

Usage ​

Policies inspect one event at a time, which they either accept or reject. It's up to the application to decide how to handle the result.

ts
import { AntiDuplicationPolicy, FiltersPolicy, HellthreadPolicy, KeywordPolicy, PipePolicy, PowPolicy, RegexPolicy } from '@nostrify/policies';

const policy = new PipePolicy([
  new FiltersPolicy([{ kinds: [0, 1, 3, 5, 7, 1984, 9734, 9735, 10002] }]),
  new KeywordPolicy(['https://t.me/']),
  new RegexPolicy(/(🟠|🔥|😳)ChtaGPT/i),
  new PubkeyBanPolicy(['e810fafa1e89cdf80cced8e013938e87e21b699b24c8570537be92aec4b12c18']),
  new HellthreadPolicy({ limit: 100 }),
  new AntiDuplicationPolicy({ kv: await Deno.openKv(), expireIn: 60000, minLength: 50 }),
]);

const [_, eventId, ok, reason] = await policy.call(event);

Pipelines can be used to chain policies together, to accept or reject events based on multiple criteria.

The NPolicy Interface ​

Policies use a simple interface, NPolicy, which accepts an event and returns a relay 'OK' message.

ts
interface NPolicy {
  call(event: NostrEvent): Promise<NostrRelayOK>;
}

If the NostrRelayOK message returns false, the event should be rejected (or not shown to users).

Included Policies ​

Nostrify ships with a few policies to get you started.

PolicyDescriptionExample Options
AntiDuplicationPolicyPrevent messages with the exact same content from being submitted repeatedly.{ kv: await Deno.openKv(), expireIn: 60000, minLength: 50 }
AnyPolicyAccepts an event if any policy accepts it.[new PowPolicy(), new KeywordPolicy()]
DomainPolicyFilters events by the author's NIP-05 domain.{ blacklist: ['replyguy.dev'] }
FiltersPolicyReject events that don't match the filters.[{ kinds: [0, 1, 3, 5, 6, 7] }]
HashtagPolicyReject events containing any of the banned hashtags.['nsfw']
HellthreadPolicyReject messages that tag too many participants.{ limit: 15 }
InvertPolicyInverts the result of another policy.new PubkeyBanPolicy([...])
KeywordPolicyReject events containing any of the strings in its content.['moo', 'oink', 'honk']
NoOpPolicyMinimal sample policy for demonstration purposes. Allows all events through.
OpenAIPolicyPasses event content to OpenAI and then rejects flagged events.{ apiKey: '123...' }
PipePolicyCompose multiple policies into a single policy.[new PowPolicy(), new KeywordPolicy()]
PowPolicyReject events which don't meet Proof-of-Work (NIP-13) criteria.{ difficulty: 20 }
PubkeyBanPolicyBan individual pubkeys from publishing events to the relay.['e810...', 'fafa...', '1e89...']
ReadOnlyPolicyThis policy rejects all messages.
RegexPolicyReject events whose content matches the regex./(🟠|🔥|😳)ChtaGPT/i
SizePolicyReject events that are too large.{ maxBytes: 8192 }
WhitelistPolicyAllows only the listed pubkeys to post to the relay. All other events are rejected.['e810...', 'fafa...', '1e89...']

See All Policies for more information.

Custom Policies ​

You can create your own policy by implementing NPolicy. This allows you to reject events based on any criteria you choose.

ts
import { NostrEvent, NostrRelayOK, NPolicy } from '@nostrify/nostrify';

class MyPolicy implements NPolicy {
  constructor(/* your options */) {
    // Use the constructor to add any additional information you need.
  }

  async call(event: NostrEvent): Promise<NostrRelayOK> {
    if (/* check if should reject */) {
      return ['OK', event.id, false, 'blocked: event did not meet criteria'];
    }

    // Allow other events.
    return ['OK', event.id, true, ''];
  }
}

Other Uses ​

Policies are useful for preventing spam or enforcing rules on a relay. But they can also be used to collect statistics, trigger side-effects, and more.

Soapbox