{"id":"955f981a98e1a5406895ba824ecb687d39405833e7f3da1512025b3b96ac812d","pubkey":"df67f9a7e41125745cbe7acfbdcd03691780c643df7bad70f5d2108f2d4fc200","created_at":1769797508,"kind":30817,"tags":[["d","nip-trusted-filter-plugins"],["title","Plugins for TRUSTed Filters"],["client","nostrhub.io"]],"content":"Plugins for TRUSTed Filters\n===\n\n`nip-trusted-filter-plugins`\n\n`draft`\n\n`extends` [nip-trusted-filters](https://nostrhub.io/naddr1qvzqqqrcvypzphm8lxn7gyf9w3wtu7k0hhxsx6ghsrry8hmm44c0t5ss3uk5lssqqy2hwumn8ghj7erfw36x7tnsw43z7un9d3shjqqnde5hqtt5wf6hxar9vskkv6tvw3jhyuc7am7eg)\n\n---\n\nThis NIP defines a set of plugins that extend the functionality of TRUSTed Filters. Trust services MAY indicate support for any one or more of these plugins (by `plugin_key`) in their `TRUST` message ping response to connecting clients (as well as in their NIP-11 \"Relay Information Document\").\n\n- **Filtering plugins** operate on candidate events\n   - `W` : defines named keys for user input & optional filtering. (see `nip-trusted-filters`)\n   - `sort_alpha` : sorts candidate events alphabetically.\n   - `sort_numeric` : sorts candidate events numerically.\n   - `sort_frequency` : sorts candidate events by frequency of value appearance.\n\n- **Transformation plugins** extract and process data from filtered events:\n   - `count` : counts filtered events or values according to an EventPath.\n   - `sum` : sums numeric values across filtered events, according to an EventPath.\n   - `avg` : calculates average of numeric values across filtered events, according to an EventPath.\n   - `min` : finds minimum value across filtered events, according to an EventPath.\n   - `max` : finds maximum value across filtered events, according to an EventPath.\n\n- **Flow Control plugins** modify filter execution behavior:\n   - `required` : marks a filter as being required.\n   - `schedule` : schedules automatic execution of a filter.\n   - `iterate` : executes a filter multiple times with different values.\n\n- **Input & Output plugins** manage the `IO` namespace:\n   - `w` : extracts data from filtered events to `IO`. (see `nip-trusted-filters`)\n   - `#` : stores pre-rendered tag arrays. (see `nip-trusted-filters`)\n\n- **Publishing plugins** publishes events from extracted data:\n   - `publish` : creates and publishes Nostr events from `IO` data. (see `nip-trusted-filters`)\n   - `publish_every` : publishes separate events for each value at an `IO` query path.\n   - `page` : selects specific page of tags for pagination.\n\n---\n\n## `sort_alpha`\n\n`sort_alpha` is a **Filtering & Sorting** plugin. It sorts candidate events alphabetically by a value extracted from an EventPath.\n\n**Functions:**\n\n```jsonc\n{\n  \"plugins\": [\n    [\"sort_alpha\", [\"<event_path>\", \"<order?>\"]]\n  ]\n}\n```\n\n**Arguments:**\n\n0. `<event_path>` : `EventPath` : (required) : Path to extract sort value from each event\n1. `<order>` : `string` : (optional) : Sort order - `\"asc\"` or `\"desc\"` (default: `\"asc\"`)\n\n\n**IO:**\n\n- **Writes to:** Does NOT write to `IO`.\n- **Reads from:** Does NOT read from `IO`.\n\n**Behavior:**\n\n- Evaluates `<event_path>` on each candidate event.\n- Sorts events alphabetically by the extracted value.\n- Applies `<order>` to reverse if `\"desc\"`.\n\n---\n\n## `sort_numeric`\n\n`sort_numeric` is a **Filtering & Sorting** plugin. It sorts candidate events numerically by a value extracted from an EventPath.\n\n**Functions:**\n\n```jsonc\n{\n  \"plugins\": [\n    [\"sort_numeric\", [\"<event_path>\", \"<order?>\"]]\n  ]\n}\n```\n\n**Arguments:**\n\n0. `<event_path>` : `EventPath` : (required) : Path to extract numeric sort value from each event\n1. `<order>` : `string` : (optional) : Sort order - `\"asc\"` or `\"desc\"` (default: `\"asc\"`)\n\n\n**IO:**\n\n- **Writes to:** Does NOT write to `IO`.\n- **Reads from:** Does NOT read from `IO`.\n\n**Behavior:**\n\n- Evaluates `<event_path>` on each candidate event.\n- Sorts events numerically by the extracted value.\n- Applies `<order>` to reverse if `\"desc\"`.\n\n---\n\n## `sort_frequency`\n\n`sort_frequency` is a **Filtering & Sorting** plugin. It sorts candidate events by the frequency of value appearance at an EventPath.\n\n**Functions:**\n\n```jsonc\n{\n  \"plugins\": [\n    [\"sort_frequency\", [\"<event_path>\", \"<order?>\"]]\n  ]\n}\n```\n\n**Arguments:**\n\n0. `<event_path>` : `EventPath` : (required) : Path to extract value for frequency counting from each event\n1. `<order>` : `string` : (optional) : Sort order - `\"asc\"` or `\"desc\"` (default: `\"desc\"`)\n\n\n**IO:**\n\n- **Writes to:** Does NOT write to `IO`.\n- **Reads from:** Does NOT read from `IO`.\n\n**Behavior:**\n\n- Evaluates `<event_path>` on each candidate event.\n- Counts occurrences of each unique value.\n- Sorts events by frequency count.\n- Applies `<order>` to reverse if `\"asc\"`.\n\n---\n\n## `count`\n\n`count` is a **Transformation** plugin. It counts filtered events or values at an EventPath and writes the result to `IO.w`.\n\n**Functions:**\n\n```jsonc\n{\n  \"plugins\": [\n    [\"count\", [\"<output_key>\", \"<event_path?>\"]]\n  ]\n}\n```\n\n**Arguments:**\n\n0. `<output_key>` : `string` : (required) : Key for storing count in `IO.w`\n1. `<event_path>` : `EventPath` : (optional) : Path to count values from (if omitted, counts filtered events)\n\n\n**IO:**\n\n- **Writes to:** `IO` namespace at `w:count.<output_key>`\n- **Reads from:** Filtered events (via EventPath)\n\n**Behavior:**\n\n- If `<event_path>` is omitted: counts total filtered events.\n- If `<event_path>` is provided: counts unique values at that path across all filtered events.\n- Writes count value to `IO.w:count.<output_key>`.\n\n---\n\n## `sum`\n\n`sum` is a **Transformation** plugin. It sums numeric values at an EventPath and writes the result to `IO.w`.\n\n**Functions:**\n\n```jsonc\n{\n  \"plugins\": [\n    [\"sum\", [\"<output_key>\", \"<event_path>\"]]\n  ]\n}\n```\n\n**Arguments:**\n\n0. `<output_key>` : `string` : (required) : Key for storing sum in `IO.w`\n1. `<event_path>` : `EventPath` : (required) : Path to extract numeric values from\n\n\n**IO:**\n\n- **Writes to:** `IO` namespace at `w:sum.<output_key>`\n- **Reads from:** Filtered events (via EventPath)\n\n**Behavior:**\n\n- Extracts all numeric values at `<event_path>` from filtered events.\n- Sums the values.\n- Writes sum value to `IO.w:sum.<output_key>`.\n\n---\n\n## `avg`\n\n`avg` is a **Transformation** plugin. It calculates average of numeric values at an EventPath and writes the result to `IO.w`.\n\n**Functions:**\n\n```jsonc\n{\n  \"plugins\": [\n    [\"avg\", [\"<output_key>\", \"<event_path>\"]]\n  ]\n}\n```\n\n**Arguments:**\n\n0. `<output_key>` : `string` : (required) : Key for storing average in `IO.w`\n1. `<event_path>` : `EventPath` : (required) : Path to extract numeric values from\n\n\n**IO:**\n\n- **Writes to:** `IO` namespace at `w:avg.<output_key>`\n- **Reads from:** Filtered events (via EventPath)\n\n**Behavior:**\n\n- Extracts all numeric values at `<event_path>` from filtered events.\n- Calculates average.\n- Writes average value to `IO.w:avg.<output_key>`.\n\n---\n\n## `min`\n\n`min` is a **Transformation** plugin. It finds minimum value at an EventPath and writes the result to `IO.w`.\n\n**Functions:**\n\n```jsonc\n{\n  \"plugins\": [\n    [\"min\", [\"<output_key>\", \"<event_path>\"]]\n  ]\n}\n```\n\n**Arguments:**\n\n0. `<output_key>` : `string` : (required) : Key for storing minimum in `IO.w`\n1. `<event_path>` : `EventPath` : (required) : Path to extract values from\n\n\n**IO:**\n\n- **Writes to:** `IO` namespace at `w:min.<output_key>`\n- **Reads from:** Filtered events (via EventPath)\n\n**Behavior:**\n\n- Extracts all values at `<event_path>` from filtered events.\n- Finds minimum value (numeric or alphabetic).\n- Writes minimum value to `IO.w:min.<output_key>`.\n\n---\n\n## `max`\n\n`max` is a **Transformation** plugin. It finds maximum value at an EventPath and writes the result to `IO.w`.\n\n**Functions:**\n\n```jsonc\n{\n  \"plugins\": [\n    [\"max\", [\"<output_key>\", \"<event_path>\"]]\n  ]\n}\n```\n\n**Arguments:**\n\n0. `<output_key>` : `string` : (required) : Key for storing maximum in `IO.w`\n1. `<event_path>` : `EventPath` : (required) : Path to extract values from\n\n\n**IO:**\n\n- **Writes to:** `IO` namespace at `w:max.<output_key>`\n- **Reads from:** Filtered events (via EventPath)\n\n**Behavior:**\n\n- Extracts all values at `<event_path>` from filtered events.\n- Finds maximum value (numeric or alphabetic).\n- Writes maximum value to `IO.w:max.<output_key>`.\n\n---\n\n## `required`\n\n`required` is a **Flow Control** plugin. It marks a filter as being required to return results. If a required filter returns no results, the entire request SHOULD return empty. This MAY be useful for producing AND logic in an array of OR'd filters.\n\n**Functions:**\n\n```jsonc\n{\n  \"plugins\": [\n    // required plugin has no functions or arguments\n    [\"required\"]\n  ]\n}\n```\n\n**Arguments:**\n\nNone.\n\n**IO:**\n\n- **Writes to:** Does NOT write to `IO`.\n- **Reads from:** Does NOT read from `IO`.\n\n**Behavior:**\n\n- Marks the filter as being required to return results.\n- If this filter returns empty, the entire TRUST request SHOULD return empty.\n\n---\n\n## `schedule`\n\n`schedule` is a **Flow Control** plugin. It schedules automatic re-execution of the filter at specified intervals. Service providers MAY limit filter scheduling on a per subscription basis.\n\n**Functions:**\n\n```jsonc\n{\n  \"plugins\": [\n    [\"schedule\", [\"<interval>\", \"<start?>\", \"<end?>\"]]\n  ]\n}\n```\n\n**Arguments:**\n\n0. `<interval>` : `TimeUnit` : (required) : Interval for automatic updates (e.g., `\"1h\"`, `\"30m\"`, `\"1d\"`)\n1. `<start>` : `timestamp` | `RelativeTime` : (optional) : Start time for the schedule\n2. `<end>` : `timestamp` | `RelativeTime` : (optional) : End time for the schedule\n\n\n**IO:**\n\n- **Writes to:** Does NOT write to `IO`.\n- **Reads from:** Does NOT read from `IO`.\n\n**Behavior:**\n\n- Schedules the filter to re-execute at `<interval>` frequency.\n- If `<start>` is specified, scheduling begins at that time.\n- If `<end>` is specified, scheduling stops at that time.\n- Service providers MAY enforce minimum/maximum intervals.\n- Scheduled execution updates any published events with new data.\n\n---\n\n## `iterate`\n\n`iterate` is a **Filtering & Sorting** plugin. It executes the filter multiple times with different values for a specified field.\n\n**Functions:**\n\n```jsonc\n{\n  \"plugins\": [\n    [\"iterate\", [\"<field>\", \"<event_path>\", \"<max?>\"]]\n  ]\n}\n```\n\n**Arguments:**\n\n0. `<field>` : `string` : (required) : Filter field to iterate over (e.g., `\"authors\"`, `\"#p\"`)\n1. `<event_path>` : `EventPath` : (required) : Path to extract iteration values from previous filter results\n2. `<max>` : `number` : (optional) : Maximum number of iterations\n\n\n**IO:**\n\n- **Writes to:** Does NOT write to `IO`.\n- **Reads from:** Does NOT read from `IO`.\n\n**Behavior:**\n\n- Extracts unique values from `<event_path>` in previous filter results.\n- Executes this filter once for each unique value.\n- Sets `<field>` to that value for each iteration.\n- Combines results from all iterations.\n- If `<max>` is specified, limits to first N values.\n\n---\n\n## `publish_every`\n\n`publish_every` is a **Publishing** plugin. It publishes separate events for each value found at an `IO` query path.\n\n**Functions:**\n\n```jsonc\n{\n  \"plugins\": [\n    [\"publish_every\", \n      // IO query to iterate over (required)\n      [\"<io_query>\", \"<max?>\"],\n      // Event Publisher function (required)\n      [\"<kind>\", \"<relay?>\", ...],\n      // Event Composer functions (optional)\n      [\"<fieldname>\", \"<io_query>\"],\n      // Tag Composer functions (optional)\n      [\"#<tagname>\", \"<io_query>\", ...]\n    ]\n  ]\n}\n```\n\n**Arguments:**\n\n0. `<io_query>` : `IO` : (required) : points to an array of values in `IO` to iterate over\n1. `<max>` : `number` : (optional) : Maximum number of events to publish\n\nAdditional arguments follow the same pattern as the `publish` plugin for Event Publisher, Event Composer, and Tag Composer functions.\n\n\n**IO:**\n\n- **Writes to:** Does NOT write to `IO`.\n- **Reads from:** `IO` namespace at specified query path\n\n**Behavior:**\n\n- Extract array of values from `<io_query>`.\n- Creates and publishes one separate event for each value.\n- Each event is composed using the same Event Publisher, Event Composer, and Tag Composer functions as the `publish` plugin.\n- If `<max>` is specified, limits to first N events.\n- Event `pubkey` and `created_at` fields (and `d` tag) SHOULD be set by the publisher, if not otherwise specified.\n\n---\n\n## `page`\n\n`page` is a **Transformation** plugin. It selects a specific page of tags from `IO.#` for pagination support.\n\n**Functions:**\n\n```jsonc\n{\n  \"plugins\": [\n    [\"page\", [\"<page_number>\", \"<page_size?>\"]]\n  ]\n}\n```\n\n**Arguments:**\n\n0. `<page_number>` : `number` : (required) : Page number (0-indexed)\n1. `<page_size>` : `number` : (optional) : Tags per page (defaults to service limit, e.g., 1000)\n\n\n**IO:**\n\n- **Writes to:** `IO` namespace at `w.page` for tracking page number\n- **Reads from:** `IO.#` namespaces\n\n**Behavior:**\n\n- Reads all tag arrays from `IO.#` namespaces.\n- Paginates tags based on `<page_size>`.\n- Keeps only tags for the specified `<page_number>`.\n- Writes page number to `IO.w.page` for tracking.\n\n---\n\n## Examples\n\n### Example 1: Required Filter\n\n```jsonc\n[\"TRUST\", \"<subscription_pubkey>\", \"<session_id>\",\n  // Must have a kind 3 event\n  {\n    \"kinds\": [3],\n    \"authors\": [\"${pov}\"],\n    \"plugins\": [\n      [\"required\"]\n    ]\n  },\n  // Optional mute list\n  {\n    \"kinds\": [10000],\n    \"authors\": [\"${pov}\"]\n  }\n]\n```\n\n### Example 2: Scheduled Updates\n\n```jsonc\n{\n  \"kinds\": [1],\n  \"since\": \"now-24h\",\n  \"plugins\": [\n    [\"schedule\", [\"1h\", \"now\", \"+7d\"]],\n    [\"count\", [\"hourly_notes\"]],\n    [\"publish\", [\"30078\"]]\n  ]\n}\n```\n\n### Example 3: Iterate Over Network\n\n```jsonc\n[\"TRUST\", \"<subscription_pubkey>\", \"<session_id>\",\n  // Get follows\n  {\n    \"kinds\": [3],\n    \"authors\": [\"${pov}\"]\n  },\n  // Get notes from each follow\n  {\n    \"kinds\": [1],\n    \"since\": \"now-24h\",\n    \"plugins\": [\n      [\"iterate\", [\"authors\", \"#p[][1]\", \"50\"]]\n    ]\n  }\n]\n```","sig":"35f107d8ec9bdfb445c0da9d7865243b34698ec48ac45048621cdcc184cb6894cc20ff4f7497b1700cfe08804d333d4ecc64d110894aeb9196f305e4fce81dbb"}