11 KiB
NIP-01
Nostr Protocol
draft
mandatory
This NIP defines the complete protocol that MUST be implemented by everybody. New NIPs may add new optional (or mandatory) fields, messages and features to the structures and flows described here.
User and Events
Each user has a keypair and is identified by their public key.
Event is only object type available. It is a hashed and signed payload with the following format:
{
"id": <32-byte lowercase hex-encoded sha256 of the serialized event data>,
"pubkey": <32-byte lowercase hex-encoded public key used to sign>,
"created_at": <unix timestamp in seconds>,
"kind": <integer between 0 and 65535>,
"tags": [
[<arbitrary string>...],
...
],
"content": <arbitrary string>,
"sig": <64-byte lowercase hex of the signature of the "id" field>
}
Signature
Signatures and encodings are done according to the Schnorr signatures standard for the curve secp256k1
.
To obtain the hash .id
, we sha256
the UTF-8 bytearray of a JSON-serialized string with the following structure:
[
0,
<pubkey, as a lowercase hex string>,
<created_at, as a number>,
<kind, as a number>,
<tags, as an array of arrays of non-null strings>,
<content, as a string>
]
The JSON serialization MUST follow this rules:
- Minified: No whitespace, line breaks or other unnecessary formatting used
- Escape Set: ONLY the following characters MUST be escaped:
- line break,
0x0A
, as\n
- double quote,
0x22
, as\"
- backslash,
0x5C
, as\\
- carriage return,
0x0D
, as\r
- tab character,
0x09
, as\t
- backspace,
0x08
, as\b
- form feed,
0x0C
, as\f
- line break,
Tags
Each tag is an array of strings of arbitrary size. Their meaning is determined by the event .kind
and defined in NIPs in this repository.
{
...,
"tags": [
["e", "5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36", "wss://nostr.example.com"],
["p", "f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca"],
["a", "30023:f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca:abcd", "wss://nostr.example.com"],
["alt", "reply"],
...
],
...
}
The first element of the tag array is referred to as the tag name or key and the second as the tag value. All elements after the second do not have a conventional name.
This NIP defines the format of 3 standard tags that can be used across all event kinds:
- The
e
tag refers to an event:["e", <32-byte lowercase hex of the id of another event>, <recommended relay URL, optional>]
- The
p
tag refers to a pubkey:["p", <32-byte lowercase hex of a pubkey>, <recommended relay URL, optional>]
- The
a
tag refers to a replaceable event- for a parameterized replaceable event:
["a", <kind integer>:<32-byte lowercase hex of a pubkey>:<d tag value>, <recommended relay URL, optional>]
- for a non-parameterized replaceable event:
["a", <kind integer>:<32-byte lowercase hex of a pubkey>:, <recommended relay URL, optional>]
- for a parameterized replaceable event:
All single-letter (only english alphabet letters: a-z, A-Z) key tags indexed by relays for faster queries.
Kinds
Kinds specify the meaning of an event and its tags. Tags with the same name might have entirely different meanings in diferent kinds.
This NIP defines two kinds:
0
: user metadata: thecontent
is set to a stringified JSON object{name: <username>, about: <string>, picture: <url, string>}
describing the user who created the event. A relay may delete older events once it gets a new one for the same pubkey.1
: text note: thecontent
is set to the plaintext content of a note (anything the user wants to say). Content that must be parsed, such as Markdown and HTML, should not be used. Clients should also not parse content as those.
Kind ranges define storage behaviours. The current ranges are:
Name | Range | SHOULD retain |
---|---|---|
Regular | 1000 <= kind < 10000 |
All events |
Replaceable | 10000 <= kind < 20000 OR 0, 3 |
The newest event for each pubkey and kind |
Ephemeral | 20000 <= kind < 30000 |
Nothing |
Parameterized | 30000 <= kind < 40000 |
The newest event for each pubkey , kind and d -tag |
In case of replaceable events with the same timestamp, the event with the lowest .id
(first in lexical order) SHOULD be retained. Older versions MAY be kept but SHOULD not be returned on queries.
Relay Protocol
Nostr has two main components: Clients & Relays. Users run a client to fetch/subscribe to events from one or more Relays via websockets. Relays are not expected to communicate with one another. It's the Client's responsibility to discover which relay has the event set their user wants to see. Both sides SHOULD verify the hash and the signature of each event upon receipt.
Clients SHOULD open a single websocket connection to each relay and use it for all their subscriptions. Relays MAY limit number of connections from specific IP/client/etc. All messages are defined as JSON arrays.
Subscriptions
Clients send a subscription message with one or more filters. The Relay MUST query its database, return all events that match the filter and keep applying the filter to all connections, returning new events as they arrive. Subscriptions stay indefinatelly open until either side closes the subscription or the connection.
Subscription Requests
To open, update and close subscriptions, Clients MUST use the following formats:
["REQ", <subscription_id>, <filter1>, <filter2>, ...]
, used to request events and subscribe to new updates.["CLOSE", <subscription_id>]
, used to stop previous subscriptions.
<subscription_id>
is an non-empty string with maximum length of 64 chars. Relays MUST manage <subscription_id>
s independently for each WebSocket connection. <subscription_id>
s are not globally unique. A REQ
message on an existing subscription overwrides the previous subscription.
<filterX>
is a JSON object that determines what events will be sent in that subscription, it can have the following attributes:
{
"ids": [<id1>, <id2>, ...],
"authors": [pubkey1, pubkey2, ...],
"kinds": [kind1, kind2, ...],
"#<single-letter-tag-key (a-zA-Z)>": [tag value1, tag value2, ...],
"since": <an integer unix timestamp in seconds, events must be newer than this to pass>,
"until": <an integer unix timestamp in seconds, events must be older than this to pass>,
"limit": <maximum number of events to return, ordered by created_at>
}
A REQ
message may contain multiple filters which are interpreted as an OR statement: events that match any of the filters are to be returned.
Properties in each filter are a logical AND statement: all present properties must match for the filter to pass.
Array properties (i.e., ids
, authors
, kinds
and tag filters) represent a logical OR statement. At least one of the arrays' values must match the respective field in the event to be considered a match. In the case of tag attributes such as #e
, for which an event may have multiple values, the event and filter condition values must have at least one item in common.
The ids
, authors
, #e
and #p
filter lists MUST contain exact 64-character lowercase hex values.
The since
and until
properties are used to specify the time range of events returned in the subscription. An event matches the filter if since <= created_at <= until
holds.
The limit
property operates over the stored events and is ignored afterwards.
Receiving Events
Relays send 3 types of messages during a subscription:
["EVENT", <subscription_id>, <JSON-serialized string of the event>]
, used to send events requested by clients.["EOSE", <subscription_id>]
, used to indicate the end of stored events and the beginning of events newly received in real-time.["CLOSED", <subscription_id>, <message>]
, used to indicate that a subscription was ended on the server side.
All messages MUST be sent with the subscription_id initiated by the client (using the REQ
message above).
CLOSED
messages MUST be sent in response to a REQ
when the relay refuses to fulfill it. It can also be sent when a relay decides to kill a subscription on its side before a client has disconnected or sent a CLOSE
. The message MUST be a string formed by a machine-readable single-word prefix followed by a :
and then a human-readable message.
Broadcasting an Event
To send an event to the Relay, Clients send a broadcast message in the format:
["EVENT", <JSON-serialized string of the event>]
Relays reply with an ACK message in the format of:
["OK", <event_id>, <accepted:true|false>, <message>]
, used to indicate acceptance (true) or denial (false) of anEVENT
message.
OK
messages MUST be sent in response to EVENT
messages received from clients, they must have the 3rd parameter set to true
when an event has been accepted by the relay, false
otherwise. The 4th parameter MUST always be present, but MAY be an empty string when the 3rd is true
.
If present, the message MUST be a string formed by a machine-readable single-word prefix followed by a :
and then a human-readable message.
Notices
Notices are warnings that might help explain or debug the behaviour of a given relay.
["NOTICE", <message>]
, used to send human-readable error messages or other things to clients.
Error Codes
The standardized machine-readable prefixes for OK
and CLOSED
are: duplicate
, pow
, blocked
, rate-limited
, invalid
, and error
for when none of that fits.
Some examples:
["OK", "b1a649ebe8...", true, ""]
["OK", "b1a649ebe8...", true, "pow: difficulty 25>=24"]
["OK", "b1a649ebe8...", true, "duplicate: already have this event"]
["OK", "b1a649ebe8...", false, "blocked: you are banned from posting here"]
["OK", "b1a649ebe8...", false, "blocked: please register your pubkey at https://my-expensive-relay.example.com"]
["OK", "b1a649ebe8...", false, "rate-limited: slow down there chief"]
["OK", "b1a649ebe8...", false, "invalid: event creation date is too far off from the current time"]
["OK", "b1a649ebe8...", false, "pow: difficulty 26 is less than 30"]
["OK", "b1a649ebe8...", false, "error: could not connect to the database"]
["CLOSED", "sub1", "duplicate: sub1 already opened"]
["CLOSED", "sub1", "unsupported: filter contains unknown elements"]
["CLOSED", "sub1", "error: could not connect to the database"]
["CLOSED", "sub1", "error: shutting down idle subscription"]