5.0 KiB
NIP-01
Basic protocol flow description
draft
mandatory
-
Each user has a keypair. Signatures, public key and encodings are done according to the Schnorr signatures standard for the curve
secp256k1
. -
Users can publish
events
to any compatible relay by callingPOST /save_event
withContent-Type: application/json
and a JSON object describing an event:{ id: <32-bytes sha256 of the the serialized event data, optional as will be recomputed by the relay> pubkey: <32-bytes hex-encoded public key of the event creator>, created_at: <unix timestamp>, kind: <integer>, ref: <32-bytes hex of the id of another event, optional>, content: <arbitrary string>, sig: <64-bytes signature of the sha256 hash of the serialized event data, which is the same as the "id" field>, }
-
Upon receiving a call like that, the relay MUST compute the
id
, check the signature over theid
and store the event so it can serve it later to other clients. -
There are 3 kinds of messages a user can publish (for now, these things are extensible and will be extended):
0
:set_metadata
: thecontent
is set to a stringified JSON object{name: <string>, about: <string>, picture: <url, string>}
describing the user who created the event. A relay may delete pastset_metadata
events once it gets a new one for the same pubkey.1
:text_note
: thecontent
is set to the text content of a note, anything the user wants to say.2
:recommend_server
: thecontent
is set to the URL (e.g.,https://somerelay.com
) of a relay the event creator wants to recommend to its followers.
- A relay MUST serve an SSE (Server-Sent Events) stream at the path
GET /listen_events
. As querystring parameters it MUST expect?session=<arbitrary id chosen by the client>
. - The relay MUST also expect the following calls:
POST /request_watch?session=<session id>
with body{"keys": [<32-byte hex-encoded pubkey>, ...]}
Upon receiving this, the relay SHOULD begin watching for new events being saved coming from the given pubkeys and return them in the SSE stream identified by the given session id. Events triggered by this call MUST have their SSE type set ton
.POST /request_unwatch?session=<session id>
with body{"keys": [<32-byte hex-encoded pubkey>, ...]}
Upon receiving this, the relay SHOULD stop notifying the given SSE stream for updates from the given pubkeys.POST /request_feed?session=<session id>
with optional body{"limit": 50, "offset": 0}
Upon receiving this, the relay MUST return in the same SSE stream previously opened identified by the given session id recent past events from all the pubkeys it may be watching on behalf of the that SSE stream. Events triggered by this call MUST have their SSE type set top
.POST /request_user?session=<session id>
with body{"pubkey": ..., "limit": 50, "offset": 0}
Upon receiving this, the relay MUST return in the same SSE stream previously opened identified by the given session id recent past events from the specified user, including aset_metadata
event if the relay has it. Events triggered by this call MUST have their SSE type set tor
.POST /request_event?session=<session id>
with body{"id": ..., "limit": 10}
Same as above, but instead the relay returns the specified event and/or related events it has (events that reference it or events that it references). Events triggered by this call MUST have their SSE type set tor
.
The limit
and offset
parameters are optional. When they're not specified the relay is free to use any defaults it want. If limit
is specified the relay MUST use any number equal or smaller than limit
, never greater. If offset
is specified the relay must use it too, and if offset
is too big for the relay it can just do as if no events were found at that range.
Format of the SSE event (example)
```
type: p
data: {"id": "000...", "pubkey": ... etc.}
```
Serialization of the event
The serialization of the event and sha256
hashing of the result is what produces the event id. Then that id (which will be a 32-byte string) is signed according to BIP-340 standards to produce the event signature.
For more information, see the source code implementation of the serialization function, as it will be simpler to understand than whatever I can write here.
Implementation notes
What a basic client should do:
- Initiate SSE sessions with every relay it knows;
- Send
/request_watch
to each with all the keys the user "follows"; - Send
/request_feed
afterwards so the user sees a wall of posts upon turning the app on; - Send
/request_watch
and/request_unwatch
if the user follows or unfollows someone; - Receive all events from the single SSE stream and filter/organize them locally, taking into account the fact that they may come as duplicates, out of order etc.