mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-09 15:07:10 +02:00
docs: first draft of NIP
This commit is contained in:
202
NIP-xx.md
Normal file
202
NIP-xx.md
Normal file
@@ -0,0 +1,202 @@
|
||||
NIP-xx
|
||||
======
|
||||
|
||||
Spells
|
||||
------
|
||||
|
||||
`draft` `optional`
|
||||
|
||||
## Abstract
|
||||
|
||||
This NIP defines `kind:777` events ("spells") that encode Nostr relay query filters as portable, shareable events. A spell stores a REQ or COUNT filter with optional runtime variables and relative timestamps, allowing users to publish, discover, and execute saved queries across clients.
|
||||
|
||||
## Event Format
|
||||
|
||||
A spell is a regular (non-replaceable) event with `kind:777`.
|
||||
|
||||
The `content` field contains a human-readable description of the query in plain text. It MAY be an empty string.
|
||||
|
||||
### Required Tags
|
||||
|
||||
| tag | values | description |
|
||||
| ----- | ------------- | ---------------------- |
|
||||
| `cmd` | `REQ`\|`COUNT` | Query command type |
|
||||
|
||||
A spell MUST contain at least one filter tag (see below).
|
||||
|
||||
### Filter Tags
|
||||
|
||||
Filter tags encode the fields of a Nostr REQ filter.
|
||||
|
||||
| tag | values | REQ filter field | notes |
|
||||
| --------- | --------------------------------------- | ---------------- | ---------------------------------- |
|
||||
| `k` | `<kind number>` | `kinds` | One tag per kind for queryability |
|
||||
| `authors` | `<pubkey1>`, `<pubkey2>`, ... | `authors` | Single tag, multiple values |
|
||||
| `ids` | `<id1>`, `<id2>`, ... | `ids` | Single tag, multiple values |
|
||||
| `tag` | `<letter>`, `<val1>`, `<val2>`, ... | `#<letter>` | See [Tag Filters](#tag-filters) |
|
||||
| `limit` | `<integer>` | `limit` | |
|
||||
| `since` | `<timestamp>` or `<relative>` | `since` | See [Relative Timestamps](#relative-timestamps) |
|
||||
| `until` | `<timestamp>` or `<relative>` | `until` | See [Relative Timestamps](#relative-timestamps) |
|
||||
| `search` | `<query string>` | `search` | [NIP-50](50.md) |
|
||||
| `relays` | `<wss://url1>`, `<wss://url2>`, ... | — | Target relay URLs |
|
||||
|
||||
All filter tag values are strings. Numeric values (kinds, limit, timestamps) MUST be encoded as decimal strings.
|
||||
|
||||
### Tag Filters
|
||||
|
||||
Filter conditions on event tags are encoded as `["tag", <letter>, <value>, ...]` rather than using the tag letter directly (e.g., `["e", ...]` or `["p", ...]`). This prevents semantic collision — a `["p", <pubkey>]` tag on a Nostr event normally means "this event references this pubkey," which would cause relays and clients to misinterpret filter parameters as social graph references.
|
||||
|
||||
The `k` tag is the exception: it uses the tag letter directly (`["k", "1"]`) to enable relay-side indexing and discovery of spells by the kinds they query.
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
["tag", "t", "bitcoin", "nostr"] → filter: {"#t": ["bitcoin", "nostr"]}
|
||||
["tag", "p", "abcd...", "ef01..."] → filter: {"#p": ["abcd...", "ef01..."]}
|
||||
["tag", "e", "abcd..."] → filter: {"#e": ["abcd..."]}
|
||||
```
|
||||
|
||||
### Metadata Tags
|
||||
|
||||
| tag | values | description |
|
||||
| ---------------- | ---------- | ------------------------------------------------------------ |
|
||||
| `name` | `<string>` | Human-readable spell name |
|
||||
| `alt` | `<string>` | [NIP-31](31.md) alternative text |
|
||||
| `t` | `<topic>` | Topic tag for categorization (multiple allowed) |
|
||||
| `close-on-eose` | `""` | Clients SHOULD close the subscription after EOSE |
|
||||
| `e` | `<event-id>` | Fork provenance: references the parent spell event |
|
||||
|
||||
Note: `["t", "bitcoin"]` as a top-level tag categorizes the spell itself, while `["tag", "t", "bitcoin"]` is a filter condition matching events with `#t = bitcoin`. Both may appear in the same event.
|
||||
|
||||
## Runtime Variables
|
||||
|
||||
The `authors` tag and `tag` filter values MAY contain runtime variables that are resolved at execution time.
|
||||
|
||||
| variable | resolves to |
|
||||
| ------------ | ----------------------------------------------------- |
|
||||
| `$me` | The executing user's pubkey |
|
||||
| `$contacts` | All pubkeys from the executing user's kind 3 contact list |
|
||||
|
||||
Variables are case-sensitive and MUST be lowercase.
|
||||
|
||||
If a client cannot resolve a variable (no logged-in user for `$me`, no contact list for `$contacts`), it MUST NOT send the REQ and SHOULD display a message explaining the unresolved dependency.
|
||||
|
||||
## Relative Timestamps
|
||||
|
||||
The `since` and `until` tags MAY contain relative time expressions instead of Unix timestamps.
|
||||
|
||||
Grammar:
|
||||
|
||||
```
|
||||
value = unix-timestamp / relative-time / "now"
|
||||
relative-time = 1*DIGIT unit
|
||||
unit = "s" / "m" / "h" / "d" / "w"
|
||||
```
|
||||
|
||||
| unit | meaning | seconds |
|
||||
| ---- | ------- | ------- |
|
||||
| `s` | seconds | 1 |
|
||||
| `m` | minutes | 60 |
|
||||
| `h` | hours | 3600 |
|
||||
| `d` | days | 86400 |
|
||||
| `w` | weeks | 604800 |
|
||||
|
||||
`now` resolves to the current Unix timestamp. A relative time `Nd` resolves to `now - N * 86400`.
|
||||
|
||||
Clients MUST resolve relative timestamps to absolute Unix timestamps before constructing a REQ message.
|
||||
|
||||
## Executing a Spell
|
||||
|
||||
To execute a spell, a client:
|
||||
|
||||
1. Parses the event tags to reconstruct a filter object
|
||||
2. Resolves runtime variables (`$me`, `$contacts`) using the executing user's identity
|
||||
3. Resolves relative timestamps to absolute Unix timestamps
|
||||
4. Constructs a REQ or COUNT message (per the `cmd` tag) with the resolved filter
|
||||
5. Determines target relays (see [Relay Resolution](#relay-resolution))
|
||||
6. Sends the REQ or COUNT message to the resolved relays
|
||||
7. If `close-on-eose` is present, closes the subscription after receiving EOSE from all connected relays
|
||||
|
||||
### Relay Resolution
|
||||
|
||||
If the spell contains a `relays` tag, the client SHOULD send the query to those relays.
|
||||
|
||||
If no `relays` tag is present, the client SHOULD use [NIP-65](65.md) relay lists to determine where to send the query, falling back to the executing user's NIP-65 read relays.
|
||||
|
||||
## Discovering Spells
|
||||
|
||||
Clients can discover spells using standard Nostr queries:
|
||||
|
||||
- By author: `{"kinds": [777], "authors": ["<pubkey>"]}`
|
||||
- By topic: `{"kinds": [777], "#t": ["bitcoin"]}`
|
||||
- By queried kind: `{"kinds": [777], "#k": ["1"]}`
|
||||
|
||||
## Examples
|
||||
|
||||
A spell that finds recent notes about Bitcoin from the user's contacts:
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": 777,
|
||||
"content": "Notes about Bitcoin from my contacts",
|
||||
"tags": [
|
||||
["cmd", "REQ"],
|
||||
["name", "Bitcoin from contacts"],
|
||||
["alt", "Spell: notes about Bitcoin from contacts"],
|
||||
["k", "1"],
|
||||
["authors", "$contacts"],
|
||||
["tag", "t", "bitcoin"],
|
||||
["since", "7d"],
|
||||
["limit", "50"],
|
||||
["t", "bitcoin"],
|
||||
["t", "social"]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
When executed by a user with 3 contacts, this resolves to:
|
||||
|
||||
```json
|
||||
["REQ", "<sub-id>", {
|
||||
"kinds": [1],
|
||||
"authors": ["aabb...", "ccdd...", "eeff..."],
|
||||
"#t": ["bitcoin"],
|
||||
"since": 1740585600,
|
||||
"limit": 50
|
||||
}]
|
||||
```
|
||||
|
||||
A COUNT spell with multiple kinds and an absolute timestamp:
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": 777,
|
||||
"content": "",
|
||||
"tags": [
|
||||
["cmd", "COUNT"],
|
||||
["k", "1"],
|
||||
["k", "6"],
|
||||
["k", "7"],
|
||||
["authors", "$me"],
|
||||
["since", "1704067200"],
|
||||
["close-on-eose", ""]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
A spell querying specific relays with a NIP-50 search:
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": 777,
|
||||
"content": "Search for Nostr dev discussions",
|
||||
"tags": [
|
||||
["cmd", "REQ"],
|
||||
["name", "Nostr dev search"],
|
||||
["k", "1"],
|
||||
["search", "nostr development"],
|
||||
["relays", "wss://relay.damus.io", "wss://nos.lol"],
|
||||
["limit", "100"]
|
||||
]
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user