{"id":"ff4f98837b05f3dbdb1a9ca5d38d889298b552eb60bd55de0ec4a839d4add5b5","pubkey":"df67f9a7e41125745cbe7acfbdcd03691780c643df7bad70f5d2108f2d4fc200","created_at":1766764843,"kind":30817,"tags":[["d","trusted-assertions"],["title","Trusted Assertions (Sovereign Version)"],["s","draft"],["v","0.1.0","Added explicit support for both \"ON_DEMAND\" as well as \"ALL_IN_ONE\" TA providers in published TA events AND user TA preferences. Added more detailed examples."],["versions","1766246798"],["k","30382"],["k","10040"],["i","30817:460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c:trusted-assertions","update"],["k","nip"],["a","30817:460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c:trusted-assertions","","update"],["p","460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c"],["client","nostrhub.io"]],"content":"NIP-85\n======\n\nTrusted Assertions\n------------------\n\n`draft` `optional`\n\nCertain Webs of Trust calculations require access to a large volume of events and/or computing power, making it virtually impossible to perform them directly on clients. This NIP allows users to offload such calculations to declared Trusted Assertion (TA) services, and for these providers to generate signed Trusted Assertion events for their clients' to request and consume. \n\nTA service providers MAY choose EITHER to publish TA events \"ON_DEMAND\" (where every event contains only the results requested in a client's `REQ` filter) OR to publish \"ALL_IN_ONE\" TA events (where every event contains ALL of the results specified in a user's published TA preferences). Either way, calculation results MAY be refined over time (represented as 'newer' events being delivered, with the same address). Requesting clients SHOULD keep `REQ` sessions OPEN to allow results to improve over time.\n\n## TA Event Kinds\n\nTrusted Assertions are always addressable (replaceable) events with the `d` tag being the \"subject of the assertion\". This NIP currently recognizes four kinds of subjects for which TA results can be generated: *pubkeys*, *regular events*, *addressable events*, and *external references*. Each subject kind is mapped to an event kind. When parsing these events for TA data, a subject path is used to match to a WoT verified user (eg: the author or mentioned user) in the event. \n\n| Subject Kind         | Event Kind     | Expected `d` tag format             | Default Subject Path* |\n| ------------------   | -------------- | ----------------------------------- | --------------------- |\n| User                 | 30382          | user : `<pubkey>`                   | `pubkey`              | \n| Event                | 30383          | event : `<id>`                      | `e:1`                 |\n| Addressable Event    | 30384          | `a` tag : `<kind>:<pubkey>:<d_tag>` | `a:1`                 |\n| External Content     | 30385          | nip-73 : `<external_reference>`     | `i:1`                 |\n\n* see [Appendix 5](#appendix-5--pathstring-values) for details about `subjectpath`\n\n## TA Event Tags\n\nTrusted Assertion events MAY contain any number of uppercase `W` tags. This allows `REQ` filtering on `#W` for `query` strings. Trusted Assertion events MAY ALSO contain any number of lowercase `w` tags. This allows `REQ` filtering on `#w` for `context` strings.\n\n```jsonc\n// OPTIONAL W/w filters in a REQ message sent for TA events\n// If NEITHER `#W` nor `#w` are specified, then an \"ALL_IN_ONE\" event MAY be delivered, \n// where query strings are fetched from the user's published TA preferences.\n{\n  \"#W\" : [\"<query>\"],\n  // If `#w` is omitted, then `context` is assumed to be \"default\".\n  \"#w\" : [\"<context>\"],\n  //...\n}\n// OPTIONAL W/w tags in a TA event\n// Any number of TA results are permitted in an event.\n[\n  [\"W\", \"<query>\", \"<context>\", \"<result>\"],\n  // The `w` tag MAY be omitted for results with \"default\" context.\n  [\"w\", \"<context>\", \"<query>\", \"<result>\"]\n  // Legacy TA tags MAY be present in older \"ALL_IN_ONE\" events.\n  [\"<legacy_tag_string>\", \"<result>\"],\n  //...\n]\n```\n\n## A Simple Example :\n\nUsers MAY have published TA preferences OR clients MAY use their own provider :\n\n```jsonc\n{\n  \"kind\": 10040,\n  \"pubkey\": \"a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411e88\",\n  \"tags\" : [\n    [\"default\", \"25f60700378a4879180dcbbb4a5027850411e88a691e98d9987c964521dff600\", \"wss://ta.example.com\"],\n    //...\n  ],\n  //...\n}\n```\n\nThe client sends a `REQ` message for two trusted assertions using the TA service's \"default\" context :\n\n```jsonc\n{\n  \"authors\" : [\"25f60700378a4879180dcbbb4a5027850411e88a691e98d9987c964521dff600\"],\n  \"kinds\" : [\"30382\"],\n  \"#d\" : [\"e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411\"],\n  // Inclusion of a #W filter supports both \"ON_DEMAND\" and \"ALL_IN_ONE\" services\n  \"#W\" : [\"rank\", \"2:latest/1:count:p\"],\n}\n```\n\nThe TA service responds with a single TA event showing that (subject) user has a trust rank of `89` : \n\n```jsonc\n{\n  \"kind\": 30382,\n  \"pubkey\" : \"25f60700378a4879180dcbbb4a5027850411e88a691e98d9987c964521dff600\",\n  \"tags\": [\n    [\"d\", \"e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411\"], \n    // \"ON_DEMAND\" services will ONLY include the requested results in their generated events.\n    [\"W\", \"rank\", \"default\", \"89\"], // user trust rank = 89\n    [\"W\", \"2:latest/1:count:p\", \"default\", \"1234\"], // number of followers = 1234\n    // \"ALL_IN_ONE\" services may include additional results in their published events.\n    [\"W\", \"1:earliest/1:time:created_at\", \"default\", \"2025-12-21T11:57:00Z\"], // earliest post = Dec 21, 2025\n    [\"W\", \"1:all:count\", \"default\", \"4321\"], // total number of posts = 4321\n  ],\n  \"content\": \"\",\n  //...\n}\n```\n\n\n## TA Query and Context strings\n\nQuery and Context strings are how TA events are requested from (and published by) a TA service. The `context` string is used by a TA service to fetch (or generate) a list of \"verified users\", for event processing. The `query` string is used to specify what event kinds to fetch and what data should be \"harvested\" from them. Together, these two strings allow a TA service to generate specific and targeted TA events for any use case.\n\n### Query strings\n\nTA queries do the \"harvesting\" of event data, given a list of \"verified users\". \n\n#### If the TA query consists of ONLY `\"rank\"` as a string value :\n\nThe TA service MUST return the subjects' rank directly from the WoT service (for the given \"context\") as the `result` value (normalized to 0-100).\n\n#### Otherwise, TA queries MUST have the following syntax :\n```\n<kinds>:<filterstring>:<opstring>:<datapath?>@<subjectpath?>\n```\n\n 1. `<kinds>` : `kinds` is a colon `:` separated list of kind numbers identifying what event kinds to query for data. \n\n 2. `:<filterstring>` : `filterstring` is a known string (see [Appendix 3](#appendix-3--filterstring-values)) with optional parameters (all separated by backslashes `/`) used to limit the subset of qualifying query events.\n\n 3. `:<opstring>` : `opstring` is a known string (see [Appendix 4](#appendix-4--opstring-values)) specifying \"what to do\" with a qualifying query event, or the data harvested from it.\n\n 4. `:<datapath>` : `datapath` is a colon `:` separated `pathstring` (see [Appendix 5](#appendix-5--pathstring-values)) specifying HOW to find the desired data within a qualifying query event. This component MAY be empty `\":\"`, and MAY be omitted entirely if `@<subjectpath>` component is also omitted.\n \n 5. `@<subjectpath>` : `subjectpath` is a colon `:` separated `pathstring` (see [Appendix 5](#appendix-5--pathstring-values)) specifying WHERE the subject key (`#d` value) should be found on qualifying query events. The path MUST lead to EITHER a qualified subject identifier (eg: `pubkey`, `e:1`, `a:1`, `i:1`) OR a boolean value OR a number that can be coerced into a boolean value to determine whether the event qualifies to have its data harvested.\n\n* Output format for `result` is determined by the `opstring` OR the expected format found at `datapath`. (See [Appendix 4](#appendix-4--opstring-values) for more details.)\n\n### Context strings\n\nA \"context\" string represents a \"set of WoT filters used to compute a list of verified users\", and can have any arbitrary value. This string MAY come from a TA or WoT service's list of default contexts, OR a user published list of contexts (spec TBD), OR it MAY be input directly by the user. If no context is provided, the value of \"default\" MUST be used. (See [Appendix 1](#appendix-1--trust-service-architecture) about trust services.)\n\n## Declaring Trusted Service Providers\n\nA kind `10040` (replaceable event with preference tags) is how users declare their preferred TA service providers. The expected structure for a preference tag is `[\"<query | prefstring>\", \"<pubkey>\", \"<relayurl>\"]`. \n- The `query` (a query string as defined above) allows users to specify a provider preference for ONLY specific queries. This is useful for SOME services that don't provide \"arbitrary calculations\" (as is the case with many \"ALL_IN_ONE\" providers). As such, individual preference tags SHOULD be added for EACH calculation desired from the service.\n- The `prefstring` (as defined in [Appendix 6](#appendix-6--prefstring-values)) allows users to specify (with varying granularity) individual providers for a variety of query `kinds` and `operations`. Even WoT providers may be specified in this manner. \n- The `pubkey` identifies the provider's identity, and MAY be unique to each user publishing `10040` preferences (or even unique to an individual preference). \n- The `relayurl` is used to identify the preferred relay for fetching the provider's TA events. This MAY be a service specific relay OR a user determined relay.\n\n* Users MAY specify preference tags privately by JSON-stringifying and encrypting a tag list in the `.content` using NIP-44. \n* Users MAY specify their preferences in legacy `10040` string format. These SHOULD be treated as `query` string preferences by TA receiving clients. See [Appendix 6](#appendix-6--legacy-tag-conversion-tables) for conversion details.\n* Users MAY specify SEPARATE preferences for WoT service providers. (See [Appendix 1](#appendix-1--trust-service-architecture) about trust services.)\n\n```js\n{\n  \"kind\": 10040,\n  \"pubkey\": \"a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411e88\",\n  \"tags\": [\n    // user might have a preference for default TA provider\n    [\"default\", \"4fd5e210530e4f6b2cb083795834bfe5108324f1ed9f00ab73b9e8fcfe5f12fe\", \"wss://nip85.nostr.band\"],\n    // user might have a special WoT provider for generating content ranks.\n    [\"30383:30384:30385:context\", \"3d842afecd5e293f28b6627933704a3fb8ce153aa91d790ab11f6a752d44a42d\", \"wss://nostr.wine\"],\n    // user might have a TA provider for processing zap events\n    [\"9735\", \"4fd5e210530e4f6b2cb083795834bfe5108324f1ed9f00ab73b9e8fcfe5f12fe\", \"wss://nip85.nostr.band\"],\n  ],\n  \"content\": nip44Encrypt(JSON.stringify([\n    // user might secretly specify an overriding preference (with `!` prefix) for ALL 30382 queries\n    [\"!30382\", \"4fd5e210530e4f6b2cb083795834bfe5108324f1ed9f00ab73b9e8fcfe5f12fe\", \"wss://nip85.nostr.band\"],\n  ]),\n  //...\n}\n```\n\n## Final Considerations\n\nTA services SHOULD render ONLY the requested assertions in each published TA event. TA services SHOULD update TA events as fast as new information arrives, but only if the results of each calculation actually changes, to avoid re-downloading the same information.\n\nTA services MAY limit access to the results by requiring relay authentication.\n\nIn TAs, `p`, `e`, and `a` tags with the same value as the `d` tag MAY be used to add a relay hint to the home relay of that user or event.\n\nWoT and TA services can offer their own ways to calculate results. For instance, the \"default\" context of one WoT provider might remove the user's muted public keys while another provider keeps them. Users can then choose how they want to see this information by configuring their provider settings (on provider apps) OR picking a different provider in their preferred client. \n\n---\n\n---\n\n## Appendix 1 : Trust Service Architecture\n\nThis NIP allows for two distinct \"trust services\", which MAY or MAY NOT be offered by the same provider:\n\n 1. **Web of Trust (WoT) Services** SHOULD calculate \"contextually aware\" lists of verified users, which are indexed by a `context` string. A WoT service answers the question: \"Who should be verified in a given context?\"\n\n 2. **Trusted Assertion (TA) Services** SHOULD generate \"trustworthy\" assertions about any subject (users, events, etc.) by extracting data (according to `query` parameters) from published events where verified users have interacted with that subject. A TA service answers the question: \"What is the engagement on this content from that list of verified users?\"\n\nWhen a TA service provider receives a `REQ` request for a TA event (with `query` and `context` strings), it SHOULD EITHER :\n\nA. resolve the specified context using its own (internal) WoT service, OR \nB. forward context resolution to the subscribed user's preferred WoT provider \n\n* If the subscribed user has WoT provider preferences published (in a `10040` event), the TA service SHOULD forward context resolution to the preferred WoT provider AND retrieve a list of verified users in return.\n* If a client sends a user's TA requests to THEIR OWN TA provider (eg: the user doesn't have applicable TA prefs published), then the TA provider SHOULD STILL use the subscriber WoT preferences (despite the user having published WoT preferences or not).\n\n**The recommended TA data flow is:**\n```\n  User TA Preferences\n    ↓           ↓\nClient → TA service ←→ WoT Provider\n    ↑           ↓\n  Published TA Event\n```\n\n**This NIP specifies:**\n- ✅ How users declare TA and WoT provider preferences\n- ✅ How TA events can be requested and consumed by clients\n- ✅ A standard TA \"query\" interface across providers. \n\n**This NIP DOES NOT specify:**\n- ❌ How WoT services communicate with TA services\n- ❌ How WoT services communicate with clients\n- ❌ A standard WoT \"context\" interface across providers\n\n---\n\n## Appendix 2 : Handling Legacy NIP-85 TA Events and Preferences\n\nFor backward compatibility, legacy (old NIP-85) 'tag strings' MAY be used in place of query strings. These MAY be included in the `REQ` filter (as non-standard filters) for requesting TA events OR they MAY be included in TA events and TA preference events. \n\n- Clients interfacing with legacy providers, SHOULD NOT include `#W` or `#w` filters in their `REQ` requests for TA events. (You know a legacy provider when `#W`/`#w` filter returns null). Legacy TA services will return a SINGLE TA event with ALL LEGACY TAGS supported by them included in the event (in the format of `[\"<tagstring>\", \"<result>\"]`).\n\n- TA services interfacing with legacy clients will receive `REQ` requests WITHOUT `#W` or `#w` filters, and possibly WITH non-standard filter keys corresponding to legacy tag strings. These providers SHOULD return a SINGLE TA event with ALL LEGACY TAGS 'supported' by them included in the event (in the format of `[\"<tagstring>\", \"<result>\"]`) using their OWN \"default\" WoT context.\n\n- Legacy TA preference events will have preference tags in the format of `[\"<kind>:<tagstring>\", \"<pubkey>\", \"<relayurl>\"]`, where `kind` is a TA event kind (e.g. `30382`, `30383`, `30384`, `30385`) and `tagstring` is a legacy tag string (without kind numbers or colons `:`). Whenever a legacy `tagstring` is found, clients and services SHOULD use the tables in [Appendix 7](#appendix-7--legacy-tag-string-conversions) to get the `querystring` conversion.\n\nPlease consult [Appendix 7](#appendix-7--legacy-tag-string-conversions) for legacy tag string conversions.\n\n\n### Example Support for Legacy TA Services\n\nUser has legacy preferences published :\n\n```jsonc\n{\n  \"kind\": 10040,\n  \"pubkey\": \"a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411e88\",\n  \"tags\" : [\n    [\"30382:rank\", \"e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411e88a691\", \"wss://ta.example.com\"],\n    [\"30382:followers\", \"e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411e88a691\", \"wss://ta.example.com\"],\n    [\"30382:first_created_at\", \"e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411e88a691\", \"wss://ta.example.com\"],\n    // user might have preference in the current format ... that DO NOT override (more specific) legacy preferences\n    [\"default\", \"4fd5e210530e4f6b2cb083795834bfe5108324f1ed9f00ab73b9e8fcfe5f12fe\", \"wss://nip85.nostr.band\"],\n    //...\n  ],\n  //...\n}\n```\n\nThe client discovers the user's legacy preferences and requests an event from the preferred legacy service :\n\n```jsonc\n{\n  \"authors\" : [\"e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411e88a691\"],\n  \"kinds\" : [\"30382\"],\n  \"#d\" : [\"e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411\"],\n}\n```\n\nTA service delivers an \"ALL_IN_ONE\" event WITH legacy tags \n(see [Appendix 7](#appendix-7--legacy-tag-string-conversions) for conversion details)\n\n```jsonc\n{\n  \"kind\": 30382,\n  \"pubkey\" : \"e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411e88a691\",\n  \"tags\": [\n    [\"d\", \"e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411\"], \n    [\"rank\", \"89\"], // user trust rank = 89\n    [\"followers\", \"1234\"], // number of followers = 1234\n    [\"first_created_at\", \"2025-12-21T11:57:00Z\"], // first post = Dec 21, 2025\n    [\"post_cnt\", \"4321\"] // total number of posts = 4321\n    // If the TA service NOW supports the current format, it MAY deliver `W` tags instead\n    [\"W\", \"rank\", \"default\", \"89\"], // user trust rank = 89\n    [\"W\", \"followers\", \"default\", \"1234\"], // number of followers = 1234\n    [\"W\", \"first_created_at\", \"default\", \"2025-12-21T11:57:00Z\"], // first post = Dec 21, 2025\n    [\"W\", \"post_cnt\", \"default\", \"4321\"] // total number of posts = 4321\n  ],\n  \"content\": \"\",\n  //...\n}\n```\n\n---\n\n## Appendix 3 : `filterstring` values\n\nA `filterstring` can have any of the following backslash `/` separated values :\n\n- `all` : all events pass the filter\n- `earliest` : only the first `count` events (by `created_at`) pass the filter\n - `/<count>` = an integer\n- `latest` : only the latest `count` events (by `created_at`) pass the filter\n - `/<count>` = an integer\n- `range` : the events are grouped (by `created_at`) according to `duration`, with (optionally) one or more `subset` and a `count` of the final subset (from current time) passing the filter.\n - `/<duration>` = `years` | `months` | `weeks` | `days` | `hours` | `minutes` | `seconds`\n - `/<subset>` = `start` | `end` | `middle` | `top` | `index`\n - `/<count>` = an integer\n- `top` : the events are sorted according to the occurrence of values discovered by `<datapath>`, with only the top `count` passing the filter.\n - `/<count>` = an integer\n\n### Filterstring Examples\n\n**`all` - Include all events:**\n- `9735:all:sum:description:amount` - Sum all zap amounts\n\n**`earliest/N` - First N events (oldest):**\n- `9735:earliest/5:value[]:description:amount` - Get amounts from the first 5 zaps received\n\n**`latest/N` - Latest N events (most recent):**\n- `1:latest/100:count` - Count the latest 100 posts\n- `1:latest/50:value[]:content` - Get content from the latest 50 posts\n\n**`range/duration` - Group by time period:**\n- `1:range/days:count[]` - Count posts per day (all days)\n- `9735:range/weeks:sum[]:description:amount` - Sum zaps per week\n- `1:range/hours:avg[]:content` - Average content length per hour\n\n**`range/duration/subset` - Select specific time buckets:**\n- `1:range/days/end:count` - Count posts in the most recent day\n- `9735:range/days/end/7:sum[]:description:amount` - Sum zaps for the last 7 days\n- `1:range/hours/top/5:count[]` - Count posts in the top 5 most active hours\n\n**`top/N` - Most frequent values:**\n- `1:top/10:value[]:t` - Get the top 10 most used hashtags\n- `1:top/5:count[]:p` - Count mentions for the top 5 most mentioned users\n- `9735:top/20:sum[]:description:pubkey` - Sum zaps grouped by top 20 senders\n\n---\n\n## Appendix 4 : `opstring` values\n\nAn `opstring` MAY be any ONE of the following string values :\n\n- `count` | `count[]` : returns an `integer` count of events or values found in an array, or an array of `[<value>, <count>]` (`<value>` as found by `<datapath>` in [Appendix 5](#appendix-5--pathstring-values)) if `[]` is used\n- `sum` | `sum[]` : returns a `number` sum of all values found in an array, or an array of `[<value>, <sum>]` (`<value>` as found by `<datapath>` in [Appendix 5](#appendix-5--pathstring-values)) if `[]` is used .\n- `avg` | `avg[]` : returns a `number` average of all values found in an array, or an array of `[<value>, <avg>]` (`<value>` as found by `<datapath>` in [Appendix 5](#appendix-5--pathstring-values)) if `[]` is used.\n- `median` | `median[]` : returns the `number` median of all values found in an array, or an array of `[<value>, <median>]` (`<value>` as found by `<datapath>` in [Appendix 5](#appendix-5--pathstring-values)) if `[]` is used.\n- `value` | `value[]` : returns a `string` or `number` (as found by `<datapath>` in [Appendix 5](#appendix-5--pathstring-values)), or an array of `string[]` or `number[]` if `[]` is used.\n- `time` | `time[]` : returns an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) formatted time string (as found by `<datapath>` in [Appendix 5](#appendix-5--pathstring-values)), or an array of `time[]` if `[]` is used.\n- `any` | `any[]` : returns a `JSON` stringified value (as found by `<datapath>` in [Appendix 5](#appendix-5--pathstring-values)), or an array of `<JSON>` if `[]` is used.\n\n### Opstring Examples\n\n**`count` - Count events or values:**\n- `1:all:count` - Count all kind 1 events from verified users → `\"342\"`\n- `9735:all:count:description:amount` - Count how many zaps have an amount → `\"156\"`\n- `1:all:count[]:t` - Count occurrences of each hashtag → `[[\"nostr\",89], [\"bitcoin\",67], [\"freedom\",45]]`\n\n**`sum` - Sum numeric values:**\n- `9735:all:sum:description:amount` - Total sats received from zaps → `\"2100000\"`\n- `9735:range/days:sum[]:description:amount` - Daily zap totals with timestamps → `[[\"2024-12-16\",\"50000\"], [\"2024-12-17\",\"75000\"], [\"2024-12-18\",\"120000\"]]`\n\n**`avg` - Average numeric values:**\n- `9735:all:avg:description:amount` - Average zap amount → `\"13461.54\"`\n- `1:range/weeks:avg[]:content` - Average content length per week → `[[\"2024-W50\", 280], [\"2024-W51\", 315], [\"2024-W52\", 298]]`\n\n**`median` - Median of numeric values:**\n- `9735:all:median:description:amount` - Median zap amount → `\"5000\"`\n- `1:range/months:median[]:content` - Median content length per month → `[[\"2024-10\", 250], [\"2024-11\", 265], [\"2024-12\", 270]]`\n\n**`value` - Extract single or multiple values:**\n- `1:top/10:value[]:t` - Top 10 hashtags by frequency → `[\"nostr\",\"bitcoin\",\"privacy\",\"freedom\",\"tech\",\"art\",\"music\",\"photography\",\"memes\",\"news\"]`\n- `9735:latest/1:value:description:amount` - Most recent zap amount → `\"21000\"`\n- `1:all:value[]:p` - All mentioned pubkeys → `[\"pubkey1...\",\"pubkey2...\",\"pubkey3...\"]`\n\n**`time` - Extract timestamps:**\n- `1:latest/1:time:created_at` - Timestamp of most recent post → `\"2024-12-19T18:25:00Z\"`\n- `9735:all:time[]:created_at` - All zap timestamps → `[\"2024-12-19T10:00:00Z\",\"2024-12-19T11:30:00Z\"]`\n\n**`any` - Extract any JSON value:**\n- `9735:latest/1:any:description` - Full zap description object → `{\"amount\":21000,\"pubkey\":\"...\",\"content\":\"Great post!\"}`\n- `1984:all:any[]:p:2` - All report reasons → `[\"spam\",\"nudity\",\"impersonation\"]`\n\n**Examples with `range` filter:**\n- `1:range/days:count[]` - Posts per day with dates → `[[\"2024-12-16\", 45], [\"2024-12-17\", 52], [\"2024-12-18\", 38], [\"2024-12-19\", 61]]`\n- `9735:range/weeks:sum[]:description:amount` - Weekly zap totals with week labels → `[[\"2024-W50\", 150000], [\"2024-W51\", 200000], [\"2024-W52\", 175000]]`\n- `1:range/hours/top/3:count[]` - Post count for top 3 hours with timestamps → `[[\"2024-12-19T14:00:00Z\", 12], [\"2024-12-19T18:00:00Z\", 10], [\"2024-12-19T20:00:00Z\", 9]]`\n\n--- \n\n## Appendix 5 : `pathstring` values\n\nA `pathstring` is a colon `:` separated list used to navigate into an event's structure to find data. It can contain ANY of the following JSON safe (and colon escaped) strings, in this order:\n\n1. **Event field name** - e.g., `pubkey`, `created_at`, `content`, `kind`\n2. **Event tag key** - e.g., `p`, `e`, `a`, `t`, `description`\n3. **Event tag index** - e.g., `1`, `2`, `3` (which element of the tag array)\n4. **JSON object key OR array index** - for navigating nested JSON in `content` or tag values\n5. **Conditional suffix** - EITHER `=\"<value>\"` (match check) OR `?` (existence check) to output booleans\n\n### Pathstring Examples\n\n**Example 1: Simple field access**\n- `pubkey` → Returns the event's pubkey field\n- `created_at` → Returns the event's timestamp\n\n**Example 2: Tag navigation**\n- `p:1` → Returns the first value (index 1) of the first `p` tag found\n- `e:2` → Returns the second value (index 2) of the first `e` tag (typically a relay hint)\n- `t` → Returns the first value of a `t` tag (hashtag)\n\n**Example 3: JSON content navigation**\n- `description:amount` → In a zap event's `description` tag, parse the JSON and get the `amount` field\n- `description:pubkey` → In a zap event's `description` tag, get the `pubkey` of who sent the zap\n\n**Example 4: Conditional checks**\n- `p:2=\"nudity\"` → Returns true if the second value of a `p` tag equals \"nudity\"\n- `i:3=\"fork\"` → Returns true if the third value of an `i` tag equals \"fork\"\n- `content?` → Returns true if the content field exists and is non-empty\n\n**Example 5: Complex paths**\n- `description:amount@description:pubkey` → Used in queries where `datapath` is `description:amount` and `subjectpath` is `description:pubkey`\n  - This finds zaps where the subject (user being queried) is the sender, not the receiver\n\n\n---\n\n## Appendix 6 : `prefstring` values\n\nA `prefstring` MUST be either \"default\" OR a colon `:` separated list of the following params :\n\n1. `all` OR `<kindstring>`: \n   - EITHER a single string `all` to indicate less specificity, \n   - OR any number of integer kind numbers to increase specificity \n     - (TA kind numbers indicate a preferred provider for TA events of these kinds.)\n     - (OTHER integer kind numbers indicate a preferred provider for calculating event data on these kinds.)\n   - WITH `!` prefix indicates a preference that should override ALL other less specific preferences. (useful for testing new algos)\n2. `any` OR `<opstring>`: \n   - EITHER a SINGLE string `any` to indicate less specificity, \n   - OR a SINGLE string matching any of `<opstring>` (without `[]` suffix) to limit the scope of the preference to a specific calculation operation. (see [Appendix 4](#appendix-4--opstring-values))\n3. OPTIONAL `context` OR `<context>` :\n   - A SINGLE string as third parameter indicates a user's preference to use a specific WoT provider for ALL queries matching the preference parameters AND the specified context. The string `context` is used to indicate a default WoT provider preference for all contexts.\n\n* The `prefstring` (list of preferences) SHOULD be 'ANDed' together by clients to determine a user's preference and priority. More specific preferences SHOULD have priority, when requesting TA events by query string. \n* To prevent conflicts, the second param SHOULD always be considered more specific than the first (eg: `all:value` takes priority over `1:any` when determining a preferred provider for `1:top/10:value[]:t`, a query string for \"Popular Hashtags\").\n* When a THIRD parameter is provided (anything after `rank` or `<opstring>`), then the provider details (`pubkey` and `relayurl`) MUST be used as the preferred WoT provider to which the TA service sends \"context\" and fetches a list of \"verified users\" BEFORE processing the query string.\n\n\n### Prefstring Examples\n\n**Least specific (catches everything):**\n- `default` - Fallback provider for all queries\n\n**Low specificity (by operation):**\n- `all:value` - Provider for all value extraction operations\n- `all:rank` - WoT provider for all rank calculations\n\n**Medium specificity (by kind):**\n- `!9735` - Provider for kind 9735 (zaps) calculations (with `!` override)\n\n**High specificity (kind + operation):**\n- `1:count` - Provider for counting kind 1 events\n- `9735:sum` - Provider for summing zap amounts (overridden by `!9735`)\n- `30382:rank` - WoT provider for ranking users\n\n**Highest specificity (kind + operation + context):**\n- `30382:rank:only_zombies` - WoT provider for user ranking in \"only_zombies\" context\n\n---\n\n## Appendix 7 : Legacy Tag String Conversions\n\nUse these tables to convert legacy tag strings to TA `query` and `pathstring` formats.\n\n* Output format for all queries is determined by the `opstring`. (See [Appendix 4](#appendix-4--opstring-values))\n\n### For Kind 30382 (users as subject) :\nDefault `subjectpath` is `@pubkey` (subject is the value of the event `pubkey` field).\n\n| Assertion               | Legacy Tag String      | TA Query Conversion         \t                               | Pathstring Conversion |\n| ----------------------- | ---------------------- | ----------------------------------------------------------- | --------------------- |\n| User Rank               | `rank`                 | `rank`                 \t                                   | `30382:rank`          |\n| Follower Count          | `followers`            | `2:latest/1:count:p`                                          | `30382:2:count`       |\n| Earliest Post Time      | `first_created_at`     | `1:earliest/1:time:created_at`                                 | `30382:1:time`        |\n| Post Count              | `post_cnt`             | `1:all:count`             \t                                 | `30382:1:count`       |\n| Post Count This year    | n/a                    | `1:range/years/end:count`                                   | `30382:1:count`       |\n| Posts Per Day           | n/a                    | `1:range/days:avg`             \t                           | `30382:1:avg`         |\n| Comment Count           | `reply_cnt`            | `1111:all:count`           \t                               | `30382:1111:count`    |\n| Reactions Count         | `reactions_cnt`        | `17:all:count`           \t                                 | `30382:17:count`      |\n| Zap Amount Received     | `zap_amt_recd`         | `9735:all:sum:description:amount`                           | `30382:9735:sum`      |\n| Zap Amount Sent         | `zap_amt_sent`         | `9735:all:sum:description:amount@description:pubkey`        | `30382:9735:sum`      |\n| Zap Number Received     | `zap_cnt_recd`         | `9735:all:count`                                            | `30382:9735:count`    |\n| Zap Number Sent         | `zap_cnt_sent`         | `9735:all:count@description:pubkey`                         | `30382:9735:count`    |\n| Avg Zap Amount/day recd | `zap_avg_amt_day_recd` | `9735:range/days:avg:description:amount`                    | `30382:9735:avg`      |\n| Avg Zap Amount/day sent | `zap_avg_amt_day_sent` | `9735:range/days:avg:description:amount@description:pubkey` | `30382:9735:avg`      |\n| Reported Count          | `reports_cnt_recd`     | `1984:all:count@p:1`                                        | `30382:1984:count`    |\n| Reported Count Nudity   | n/a                    | `1984:all:count:p:2=\"nudity\"@p:1`                           | `30382:1984:count`    |\n| Reported Most Common    | n/a                    | `1984:top/5:value[]:p:2@p:1`                                | `30382:1984:value`    |\n| Reports Sent            | `reports_cnt_sent`     | `1984:all:count`                                            | `30382:1984:count`    |\n| Popular Hashtags        | n/a                    | `1:top/10:value[]:t`                                        | `30382:1:value`       |\n| Generally active start  | `active_hours_start`   | `1:range/hours/start/top/1:time:created_at`                 | `30382:1:time`        |\n| Generally active end    | `active_hours_end`     | `1:range/hours/end/top/1:time:created_at`                   | `30382:1:time`        |\n| Generally active hours  | n/a                    | `1:range/hours/top/3:time[]:created_at`                     | `30382:1:time`        |\n\n\n### For Kind 30383 (events as subject) :\nDefault `subjectpath` is `@e:1` (subject is the first index of an `e` tag event id).\n\n| Assertion               | Legacy Tag String      | TA Query Conversion          \t                   | Pathstring Conversion |\n| ----------------------- | ---------------------- | ------------------------------------------------- | --------------------- |\n| Content Rank            | `rank`                 | `rank`                                            | `30383:rank`          |\n| Event Comment Count     | `comment_cnt`          | `1111:all:count`                                  | `30383:1111:count`    |\n| Event Quote Count       | `quote_cnt`            | `1:all:count@q:1`                                 | `30383:1:count`       |\n| Event Repost Count      | `repost_cnt`           | `6:all:count`                                     | `30383:6:count`       |\n| Event Reaction Count    | `reaction_cnt`         | `1:17:all:count`                                  | `30383:17:count`      |\n| Event Zap Count         | `zap_cnt`              | `9735:all:count@description:e:1`                  | `30383:9735:count`    |\n| Event Zap Amount        | `zap_amount`           | `9735:all:sum:description:amount@description:e:1` | `30383:9735:sum`      |\n\n### For Kind 30384 (addressable events as subject) :\nDefault `subjectpath` is `@a:1` (subject is the first index of an `a` tag addressable event reference).\n\n| Assertion               | Legacy Tag String      | TA Query Conversion           \t                   | Pathstring Conversion |\n| ----------------------- | ---------------------- | ------------------------------------------------- | --------------------- |\n| Content Rank            | `rank`                 | `rank`                                            | `30384:rank`          |\n| Event Comment Count     | `comment_cnt`          | `1111:all:count`                                  | `30384:1111:count`    |\n| Event Quote Count       | `quote_cnt`            | `1:all:count@q:1`                                 | `30384:1:count`       |\n| Event Repost Count      | `repost_cnt`           | `6:all:count`                                     | `30384:6:count`       |\n| Event Reaction Count    | `reaction_cnt`         | `1:17:all:count`                                  | `30384:17:count`      |\n| Event Zap Count         | `zap_cnt`              | `9735:all:count@description:a:1`                  | `30384:9735:count`    |\n| Event Zap Amount        | `zap_amount`           | `9735:all:sum:description:amount@description:a:1` | `30384:9735:sum`      |\n\n### For Kind 30385: External identifier as Subject\nDefault `subjectpath` is `@i:1` (subject is the first index of an `i` tag external identifier).\n\nProviders can rate books, locations, movies, websites, and hashtags using NIP-73 identifiers. \nNIP-73 `i/k` tags pairs should be added to the output TA event.\n\n| Assertion               | Legacy Tag String      | TA Query Conversion           | Pathstring Conversion |\n| ----------------------- | ---------------------- | ----------------------------- | --------------------- |\n| Content Rank            | `rank`                 | `rank`                        | `30385:rank`          |\n| Comment Count           | `comment_cnt`          | `1111:all:count`              | `30385:1111:count`    |\n| Reaction Count          | `reaction_cnt`         | `17:all:count`                | `30385:17:count`      |\n| Count Rel 'fork'        | n/a                    | `17:all:count:i:3=\"fork\"`     | `30385:17:count`      |\n\n\n---\n\n## Appendix 8 : More Examples\n\nHere are a couple of more involved examples of typical TA transactions.\n\n### To retrieve the follower count for a subject user, using the context \"only_zombies\":\n\nThe current user has subscribed to the \"only_zombies\" user list (WoT provider) for detecting zombie npubs.\n\n```jsonc\n{\n  \"kind\" : \"10040\",\n  \"tags\" : [\n    [\"all:any:only_zombies\", \"0b04ac14550f78ad19ae493aaab5e0bcf53c9c412b1d2e10ced28aeb48acbd3f\", \"wss://relay.plebsvszombies.cc\"],\n  ]\n}\n```\n\nThe client sends the following `REQ` filter to `wss://relay.plebsvszombies.cc` \nrequesting all of the following WRT pubkeys that match the \"only_zombies\" context:\n\n```jsonc\n{\n  \"authors\": [\"0b04ac14550f78ad19ae493aaab5e0bcf53c9c412b1d2e10ced28aeb48acbd3f\"],\n  \"kinds\": [\"30382\"],\n  \"#d\": [\"e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411\"],\n  \"#W\": [\"2:latest/1:count:p@pubkey\", \"2:all:value[]:pubkey@p:1\"],\n  \"#w\": [\"only_zombies\"]\n}\n```\n\nThe relay returns the following TA event :\n\n```jsonc\n{\n  \"author\": \"25f60700378a4879180dcbbb4a5027850411e88a691e98d9987c964521dff600\",\n  \"kind\": 30382,\n  \"tags\": [\n    [\"d\", \"e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411\"],\n    [\"W\", \"2:latest/1:count:p@pubkey\", \"only_zombies\", \"342\"],\n    [\"w\", \"only_zombies\", \"2:latest/1:count:p@pubkey\", \"342\"],\n    [\"W\", \"2:all:value[]:pubkey@p:1\", \"only_zombies\", \"21\"],\n    [\"w\", \"only_zombies\", \"2:all:value[]:pubkey@p:1\", \"21\"]\n  ],\n  \"content\": \"\",\n  //...\n}\n```\n\nFirst query returns a count of pubkeys that might be zombies in the subject's follow list. \n\n  - `2` = Query kind 2 events (follow lists per NIP-02)\n  - `latest/1` = Only fetch the most recent event\n  - `count` = Count the number\n  - `p` = Of p tags (pubkey references) in the event\n  - `@pubkey` = Where the subject (user being queried) appears as the event author\n\nSecond query returns a list of subject followers that might also be zombies.\n\n  - `2` = Query kind 2 events (follow lists per NIP-02)\n  - `all` = Fetch all events\n  - `value[]` = Get the values\n  - `pubkey` = Of the author pubkey from the events\n  - `@p:1` = Where the subject (user being queried) appears as a `p` tag in the event\n\n\n### To retrieve the ten most common hashtags used by a user\n\n```jsonc\n{\n  \"kind\": 10040,\n  \"tags\": [\n    // user might prefer an AI powered TA provider for processing string values.\n    [\"!all:value\", \"4fd5e210530e4f6b2cb083795834bfe5108324f1ed9f00ab73b9e8fcfe5f12fe\", \"wss://nip85.nostr.band\"],\n    // user might have their own zaps based web of trust algo\n    [\"all:any:web_of_zaps\", \"4fd5e210530e4f6b2cb083795834bfe5108324f1ed9f00ab73b9e8fcfe5f12fe\", \"wss://nip85.nostr.band\"],\n  ]\n}\n```\n\nThe client sends the following `REQ` filter:\n\n```jsonc\n{\n  \"authors\": [\"25f60700378a4879180dcbbb4a5027850411e88a691e98d9987c964521dff600\"],\n  \"kinds\": [\"30382\"],\n  \"#d\": [\"e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411\"],\n  \"#W\": [\"1:top/10:value[]:t\"],\n  \"#w\": [\"default\"]\n}\n```\n\nRelays return the following event published by the TA service:\n\n```jsonc\n{\n  \"author\": \"25f60700378a4879180dcbbb4a5027850411e88a691e98d9987c964521dff600\",\n  \"kind\": 30382,\n  \"tags\": [\n    [\"d\", \"e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411\"],\n    [\"W\", \"1:top/10:value[]:t\", \"default\", '[\"nostr\",\"bitcoin\",\"privacy\",\"freedom\",\"tech\",\"art\",\"music\",\"photography\",\"memes\",\"news\"]'],\n    [\"w\", \"default\", \"1:top/10:value[]:t\", '[\"nostr\",\"bitcoin\",\"privacy\",\"freedom\",\"tech\",\"art\",\"music\",\"photography\",\"memes\",\"news\"]']\n  ],\n  \"content\": \"\",\n  //...\n}\n```\n\n\nQuery returns an array of the top 10 most frequent hashtags from the subject's notes.\n\n- `1` = Query kind 1 events (text notes)\n- `top/10` = Sort by frequency, return top 10\n- `value[]` = Return an array of values\n- `t` = Look at `t` tags (hashtags)\n- (no `@subjectpath` means use default: `@pubkey`)","sig":"3ea8bbc70f0ba20b1dc487fe64bb63eab85ca7c120785c2e5bee2786701a307f7841b8ba99e41f7966058ece664ebb53fcc95a542e9b11693c55b7b9f1e594a5"}