{"id":"a8cdf4bb3bf19ef0ba16c06bb13883d1838808edcb7528c49f14666c0731bc26","pubkey":"df67f9a7e41125745cbe7acfbdcd03691780c643df7bad70f5d2108f2d4fc200","created_at":1772483114,"kind":30817,"tags":[["d","tsm-ranking-services"],["title","TSM Ranking Services"],["k","37573"],["client","nostrhub.io"]],"content":"TSM Ranking Services\n===\n\n`tsm-ranking-services`\n\n`draft`\n\n`extends` [tsm-trust-service-machines](https://nostrhub.io/naddr1qvzqqqrcvypzphm8lxn7gyf9w3wtu7k0hhxsx6ghsrry8hmm44c0t5ss3uk5lssqqy28wumn8ghj7un9d3shjtnyv9kh2uewd9hsqxn5wdkj6arjw4ehgttnv4e8v6trv5kk6ctrdp5kuetnl84x4n)\n\n`kind` `37573` \"TSM Ranked Result Set\"\n\n---\n\n*Web of Trust* style ranking is the foundation on which most Nostr recommendation and discovery services are built. This NIP extends the [tsm-trust-service-machines](https://nostrhub.io/naddr1qvzqqqrcvypzphm8lxn7gyf9w3wtu7k0hhxsx6ghsrry8hmm44c0t5ss3uk5lssqqy28wumn8ghj7un9d3shjtnyv9kh2uewd9hsqxn5wdkj6arjw4ehgttnv4e8v6trv5kk6ctrdp5kuetnl84x4n) NIP with a standard for ranking services that can be implemented and extended by any provider. A ranking service is one that calculates a relative rank (0-100) for subjects, based on interactions discovered from the \"point of view\" of any other subject(s).\n\n- It maps subject interactions (published events) of any kind, starting from one or more unique subject POVs.\n- It ranks subjects (pubkeys, hashtags, urls, ect...) based on their influence within these interactions.\n- It outputs a list of ranked subjects as single letter tags (`p`, `t`, `u`, ect...) in a (set of) kind `37573` event(s).\n\n## Service Expectations\n\nService announcements SHOULD\n- Declare kind `37573` as a `k` tag\n- Declare ALL standard configs (`type`, `pov`, `minrank`) as `config` tags\n- Declare ALL of the `V` tags required for output events\n\nServices providers SHOULD respond to Service Request (kind `37572`) events by:\n- Publishing Service Feedback events (kind `7000`) to inform on request status\n- Parsing the `pov` and `type` values to result in a set of subjects\n- Calculating rank values for ALL subjects using the configured algorithm\n- Sorting results in descending order by rank value\n- Filtering results by `minrank` threshold\n- Paginating results according to provider settings\n- Rendering results in a (paginated set of) kind `37573` output event(s)\n- Updating output events according to requested schedule (if supported)\n\n## Service Announcement Events\n\nProviders implementing ranking services SHOULD publish a standard TSM Service Announcement event (kind `37570`), with the following tags:\n\n```jsonc\n{\n  \"kind\": 37570,\n  \"pubkey\": \"<service_pubkey>\",\n  \"tags\": [\n    // ... OPTIONAL and REQUIRED tags as specified by TSM\n    \n    // REQUIRED output event kind\n    [\"k\", \"37573\"], // Ranked Result Set\n\n    // REQUIRED `type` config\n    // A tag letter for the `pov` subject type AND the type to be ranked\n    // (e.g., \"p\" for pubkeys, \"e\" for event ids, \"u\" for urls, etc.)\n    [\"config\", \"type\", \"tagletter\", \"<description?>\", \"<default?>\", \"<allowed?>\"],\n    \n    // REQUIRED `pov` config\n    // A point of view subject(s) from which to start the ranking.\n    // This may be a single subject (any value from a tag of `type`)\n    // or a `naddr` reference to any event with `type` tags.\n    // If the referenced event has a `page` keyed `v` tag,\n    // the provider SHOULD make sure to get ALL the subject tags\n    // from ALL of the paginated events.\n    [\"config\", \"pov\", \"subject|naddr\", \"<description?>\", \"<default?>\", \"<allowed?>\"],\n    \n    // REQUIRED `minrank` config\n    // Minimum rank threshold for inclusion in output\n    [\"config\", \"minrank\", \"0-100\", \"<description?>\", \"0\", \"<allowed?>\"],\n\n    // OPTIONAL declare v tags for output events\n    [\"V\", \"type\", \"tagletter\", \"Type of subjects in output\"],\n    [\"V\", \"pov\", \"subject|naddr\", \"Point of view reference\"],\n    [\"V\", \"page\", \"integer\", \"Page number for paginated results\"],\n    \n  ],\n  \"content\": \"<optional_service_details_markdown>\"\n}\n```\n\n## Service Output Standards\n\nThe ranking service NIP does NOT make use of output standard events (kind `37571`).\n\n## Service Request Events\n\nUsers may request ranking services from a provider by publishing a standard TSM Service Request event (kind `37572`), with the following tags:\n\n```jsonc\n{\n  \"kind\": 37572,\n  \"pubkey\": \"<request_author_pubkey>\",\n  \"tags\": [\n    // ... OPTIONAL and REQUIRED tags as specified by service announcement\n    \n    // REQUIRED expected output kind\n    [\"k\", \"37573\"], // want Ranked Result Set\n    \n    // CONDITIONAL config tags (as defined by provider)\n    // Must match the allowed values from the service announcement\n    [\"config\", \"type\", \"<tagletter>\"],\n    [\"config\", \"pov\", \"<subject> | <naddr>\"],\n    [\"config\", \"minrank\", \"<0-100>\"],\n  ],\n  \"content\": \"\" // empty or encrypted tags array\n}\n```\n\n\n## Ranked Result Set (kind `37573`)\n\nRanking services SHOULD publish ranked result sets as one or more addressable events of kind `37573` in the following format:\n\n```jsonc\n{\n  \"kind\": 37573,\n  \"pubkey\": \"<service_pubkey> | <subscription_pubkey> | <request_pubkey>\",\n  \"tags\": [\n    // REQUIRED identifier\n    // the `d` tag value SHOULD be equal to `request_id`\n    // (the `d` tag value of the request event that triggered this output)\n    [\"d\", \"<request_id>\"],\n\n    // OPTIONAL `v` tags to help with discovery of request event\n    [\"v\", \"p:<request_author_pubkey>\"],\n    [\"v\", \"d:<request_id>\"],\n    \n    // REQUIRED ranked subjects (sorted by rank descending)\n    // <type> is the tag letter (config `type`) for subjects in this output\n    // <subject> is a unique identifier for each ranked subject\n    // <rank> is the rank value from 0 to 100 assigned to this subject\n    [\"<type>\", \"<subject>\", \"<rank>\"],\n    // ... more ranked subjects\n  ],\n  \"content\": \"\" // empty or JSON with additional metadata\n}\n```\n\n\n## Appendix 2 : Example Implementations\n\nThe following are example ranking algorithm implementations that providers MAY offer. Each example shows a Service Announcement with the standard  config inputs (for this ranking NIP) and some provider specific options. Each example also provides an example request and the expected algorithm behavior.\n\n### Example A: Baseline WoT Ranking\n\nThis example demonstrates a foundational web-of-trust ranking service that analyzes a user's social graph to rank other users based on follows, mutes, and reports. The service uses configurable weights for different interaction types and traverses the network to a specified depth, making it ideal for basic trust-based user discovery and filtering.\n\n**Algorithm Behavior**: \n- Start from POV's follows (kind 3)\n- Iterate through network to specified depth\n- Weight by graph distance and mutual connections\n- Apply configured weights for follows, mutes, and reports\n- Output rank 0-100 based on influence score\n\n**Service Announcement:**\n```jsonc\n{\n  \"kind\": 37570,\n  \"pubkey\": \"provider123...\",\n  \"tags\": [\n    [\"d\", \"baseline_wot\"],\n    [\"title\", \"Baseline Web of Trust Ranking\"],\n    [\"summary\", \"Ranks users based on follows mutes and reports.\"],\n    [\"n\", \".../tsm-ranking.md\"],\n    [\"k\", \"37573\"],\n    [\"r\", \"wss://relay.example.com\"],\n    // type is fixed at 'p' (it only ranks pubkeys)\n    [\"config\", \"type\", \"tagletter\", \"A tag letter for the subject type...\", \"p\", \"[\\\"p\\\"]\"],\n    // pov is required\n    [\"config\", \"pov\", \"subject|naddr\", \"A point of view subjects(s)...\"],\n    // minrank defaults to 0\n    [\"config\", \"minrank\", \"0-100\", \"Minimum rank threshold for inclusion in output...\", \"0\"],\n\n    // uppercase `V` tag is required to announce pagination support\n    [\"V\", \"page\", \"integer\", \"page number for this paginated event, along with a JSON array of d-tag values for all events in this set.\"],\n\n    // provider specified configurable weights for interactions\n    [\"option\", \"depth_follows\", \"integer\", \"The network depth to analyze...\", \"6\"],\n    [\"option\", \"weight_follows\", \"+-1\", \"Weight for follows...\", \"1\"],\n    [\"option\", \"weight_mutes\", \"+-1\", \"Weight for mutes...\", \"0\"],\n    [\"option\", \"weight_report_illegal\", \"+-1\", \"Weight for illegal reports...\", \"-1\"],\n    [\"option\", \"weight_report_impersonation\", \"+-1\", \"Weight for impersonation reports...\", \"-1\"],\n    [\"option\", \"weight_report_other\", \"+-1\", \"Weight for other reports...\", \"-1\"],\n\n    // additional info on ranking algo\n    [\"info\", \"pagesize\", \"1000\", \"Maximum subjects per output event\"],\n    [\"info\", \"algorithm\", \"graperank\", \"Name of the algorithm implemented by this service\"],\n    [\"info\", \"version\", \"1.1.0\", \"Version of the algorithm implemented by this service\"],\n    [\"info\", \"source\", \"https://gitlab.com/...\", \"Source code repository for this service\"],\n  ]\n}\n```\n\n**Service Request:**\n```jsonc\n{\n  \"kind\": 37572,\n  \"pubkey\": \"alice123...\",\n  \"tags\": [\n    [\"d\", \"alice_wot\"],\n    [\"k\", \"37573\"],\n    [\"p\", \"provider123...\"],\n    [\"a\", \"37570:provider123...:baseline_wot\"],\n    [\"r\", \"wss://myrelay.example.com\"],\n    [\"config\", \"pov\", \"alice123...\"],\n    [\"option\", \"depth_follows\", \"4\"],\n    [\"option\", \"weight_mutes\", \"-0.5\"]\n  ]\n}\n```\n\n**Output Event:**\n```jsonc\n{\n  \"kind\": 37573,\n  \"pubkey\": \"provider123...\",\n  \"tags\": [\n    [\"d\", \"alice_wot\"],\n    [\"v\", \"p:alice123...\"],\n    [\"v\", \"page:1\", \"[\\\"alice_wot\\\", \\\"alice_wot:2\\\", \\\"alice_wot:3\\\"]\"],\n    [\"v\", \"total:2100\"],\n    [\"p\", \"bob_pubkey...\", \"95\"],\n    [\"p\", \"carol_pubkey...\", \"87\"],\n    [\"p\", \"dave_pubkey...\", \"76\"]\n    // ... 997 more ranked users (1000 per paginated event)\n  ]\n}\n```\n\n### Example B: WoT + Engagement Ranking\n\nThis example shows how to compose ranking services by using the output of one service as input to another. It takes a baseline WoT ranking (via naddr reference) and re-ranks those users based on engagement metrics (zaps, replies, reactions). This demonstrates the power of service composition, allowing users to layer multiple ranking algorithms to create sophisticated, personalized discovery feeds.\n\n**Algorithm Behavior**:\n- Load subjects from POV naddr (baseline_wot output event)\n- Analyze interactions (zaps sent, replies, reactions) for those subjects since the specified timestamp\n- Weight different interaction types according to configured weights\n- Calculate engagement score per user based on weighted interactions\n- Output rank 0-100 normalized across all ranked subjects from POV\n\n**Service Announcement:**\n```jsonc\n{\n  \"kind\": 37570,\n  \"pubkey\": \"provider456...\",\n  \"tags\": [\n    [\"d\", \"wot_plus_engagement\"],\n    [\"title\", \"Web of Trust + Engagement\"],\n    [\"summary\", \"Ranks users based on engagement (zaps, replies, reactions)\"],\n    [\"n\", \"./tsm-ranking.md\"],\n    [\"k\", \"37573\"],\n    [\"r\", \"wss://relay.example.com\"],\n    \n    // type is fixed at 'p' (it only ranks pubkeys)\n    [\"config\", \"type\", \"tagletter\", \"Subject type to rank (fixed at 'p' for users)\", \"p\", \"[\\\"p\\\"]\"],\n    // pov is required\n    [\"config\", \"pov\", \"subject|naddr\", \"Point of view subject(s) for ranking\"],\n    // minrank defaults to 0\n    [\"config\", \"minrank\", \"0-100\", \"Minimum rank threshold for inclusion in output\", \"0\"],\n    // since is required for engagement analysis\n    [\"config\", \"since\", \"timestamp\", \"Consider events after this timestamp\"],\n    \n    // uppercase `V` tag is required to announce pagination support\n    [\"V\", \"page\", \"integer\", \"page number for this paginated event, along with a JSON array of d-tag values for all events in this set.\"],\n\n    // provider-specific configurable weights for interaction types\n    [\"option\", \"weight_zaps\", \"0-1\", \"Weight for zap interactions in engagement score\", \"0.5\"],\n    [\"option\", \"weight_replies\", \"0-1\", \"Weight for reply interactions in engagement score\", \"0.3\"],\n    [\"option\", \"weight_reactions\", \"0-1\", \"Weight for reaction interactions in engagement score\", \"0.2\"],\n    \n    // additional info on ranking algorithm\n    [\"info\", \"pagesize\", \"1000\", \"Maximum subjects per output event\"],\n    [\"info\", \"algorithm\", \"engagement\", \"Name of the algorithm implemented by this service\"],\n    [\"info\", \"version\", \"1.0.0\", \"Version of the algorithm implemented by this service\"],\n    [\"info\", \"source\", \"https://github.com/example/engagement-ranker\", \"Source code repository for this service\"]\n  ]\n}\n```\n\n**Service Request:**\n```jsonc\n{\n  \"kind\": 37572,\n  \"pubkey\": \"alice123...\",\n  \"tags\": [\n    [\"d\", \"alice_engagement\"],\n    [\"k\", \"37573\"],\n    [\"p\", \"provider456...\"],\n    [\"a\", \"37570:provider456...:wot_plus_engagement\"],\n    [\"r\", \"wss://myrelay.example.com\"],\n    // Using the naddr of alice's baseline_wot output as POV\n    [\"config\", \"pov\", \"naddr1qqxnzd3cxqmrzv3exgmr2wfexyuewdehhxarjxq6nwden...\"],\n    [\"config\", \"since\", \"1704067200\"],\n    [\"option\", \"weight_zaps\", \"0.6\"],\n    [\"option\", \"weight_replies\", \"0.3\"],\n    [\"option\", \"weight_reactions\", \"0.1\"]\n  ]\n}\n```\n\n**Output Event:**\n```jsonc\n{\n  \"kind\": 37573,\n  \"pubkey\": \"provider456...\",\n  \"tags\": [\n    [\"d\", \"alice_engagement\"],\n    [\"v\", \"p:alice123...\"],\n    [\"v\", \"page:1\", \"[\\\"alice_engagement\\\", \\\"alice_engagement:2\\\", \\\"alice_engagement:3\\\"]\"],\n    [\"v\", \"total:2100\"],\n    [\"p\", \"bob_pubkey...\", \"92\"],\n    [\"p\", \"eve_pubkey...\", \"88\"],\n    [\"p\", \"frank_pubkey...\", \"81\"]\n    // ... 997 more ranked users (1000 per paginated event)\n    // ... of 2100 total ranked users from baseline_wot output\n  ]\n}\n```\n\n\n### Example C: Content Quality Ranking\n\nThis example illustrates an AI-powered ranking service that evaluates the quality of kind 1 notes (content) from a set of authors. The service accepts a list of pubkeys (either directly or via naddr reference to an event with `p` tags) as the POV, fetches recent notes from those authors, and ranks the individual notes by quality. This makes it useful for creating a feed of high-quality ranked content from a specific set of authors.\n\n**Algorithm Behavior**:\n- Load pubkeys from POV (either a single pubkey or naddr to an event with `p` tags)\n- Fetch recent kind 1 notes from those authors (up to notes_per_author per author)\n- Analyze each note with configured AI model for quality signals\n- Consider optional topic context if provided\n- Rank individual notes (events) by quality score\n- Output rank 0-100 for each note, normalized across all analyzed content\n\n**Service Announcement:**\n```jsonc\n{\n  \"kind\": 37570,\n  \"pubkey\": \"provider789...\",\n  \"tags\": [\n    [\"d\", \"quality_ranker\"],\n    [\"title\", \"Content Quality Ranker\"],\n    [\"summary\", \"AI-powered content quality ranking based on note analysis from specified authors\"],\n    [\"n\", \"./tsm-ranking.md\"],\n    [\"k\", \"37573\"],\n    [\"r\", \"wss://relay.example.com\"],\n    \n    // type is fixed at 'e' (ranks events by their content quality)\n    // but POV should contain pubkeys (authors whose content will be ranked)\n    [\"config\", \"type\", \"tagletter\", \"Subject type to rank (fixed at 'e' for events)\", \"e\", \"[\\\"e\\\"]\"],\n    // pov should be pubkey(s) or naddr to event with p tags (the authors)\n    [\"config\", \"pov\", \"subject|naddr\", \"Pubkey(s) of authors whose content will be analyzed and ranked\"],\n    // minrank defaults to 0\n    [\"config\", \"minrank\", \"0-100\", \"Minimum rank threshold for inclusion in output\", \"0\"],\n\n    // uppercase `V` tag is required to announce pagination support\n    [\"V\", \"page\", \"integer\", \"page number for this paginated event, along with a JSON array of d-tag values for all events in this set.\"],\n\n    \n    // provider-specific options for AI analysis\n    [\"option\", \"kind\", \"integer\", \"Event kind to analyze\", \"1\"],\n    [\"option\", \"prompt\", \"string\", \"Topic on which to rank notes (e.g., 'bitcoin OGs and influencers', 'chicago DJs and musicians')\", \"\"],\n    [\"option\", \"notes_per_author\", \"integer\", \"Number of recent notes per author to fetch\", \"10\"],\n    [\"option\", \"since\", \"timestamp\", \"Only analyze notes after this timestamp\", \"0\"],\n    \n    // additional info on ranking algorithm\n    [\"info\", \"pagesize\", \"500\", \"Maximum subjects per output event\"],\n    [\"info\", \"algorithm\", \"content_quality\", \"Name of the algorithm implemented by this service\"],\n    [\"info\", \"version\", \"2.0.0\", \"Version of the algorithm implemented by this service\"],\n    [\"info\", \"source\", \"https://github.com/example/quality-ranker\", \"Source code repository for this service\"]\n  ]\n}\n```\n\n**Service Request:**\n```jsonc\n{\n  \"kind\": 37572,\n  \"pubkey\": \"alice123...\",\n  \"tags\": [\n    [\"d\", \"chicago_djs_musicians\"],\n    [\"title\", \"Chicago DJs and Musicians\"],\n    [\"summary\", \"YO! Check it! Da bomb list of my closest friends!\"],\n    [\"k\", \"37573\"],\n    [\"p\", \"provider789...\"],\n    [\"a\", \"37570:provider789...:quality_ranker\"],\n    [\"r\", \"wss://myrelay.example.com\"],\n    // Using the output naddr from alice's baseline_wot as POV\n    // (this references a kind 37573 event with ranked pubkeys as p tags)\n    [\"config\", \"pov\", \"naddr1qqxnzd3cxqmrzv3exgmr2wfe...\"],\n    [\"option\", \"prompt\", \"chicago's top DJs and musicians\"],\n    [\"option\", \"notes_per_author\", \"5\"],\n    [\"option\", \"since\", \"1704067200\"]\n  ]\n}\n```\n\n**Output Event (page 2)**\n```jsonc\n{\n  \"kind\": 37573,\n  \"pubkey\": \"provider789...\",\n  \"tags\": [\n    [\"d\", \"chicago_djs_musicians:2\"],\n    [\"v\", \"p:alice123...\"],\n    [\"v\", \"d:chicago_djs_musicians\"],\n    [\"v\", \"total:150\"],\n    [\"v\", \"page:2\", \"[\\\"chicago_djs_musicians\\\", \\\"chicago_djs_musicians:2\\\"]\"],\n    // Ranked kind 1 events by quality score\n    [\"e\", \"note_event_id_1...\", \"98\"],\n    [\"e\", \"note_event_id_2...\", \"94\"],\n    [\"e\", \"note_event_id_3...\", \"89\"],\n    [\"e\", \"note_event_id_4...\", \"85\"]\n    // ... more ranked notes\n  ]\n}\n```","sig":"9962cf91a917d2bde1cf40caadb439dbca73821c888a9b1174a536162dc52548a52eabe032c4854c10ae087420c465ffe8d9047e60521211cd67e18a60e74d92"}