nostr/nips/01.md

5.0 KiB

NIP-01

Basic protocol flow description

draft mandatory

  1. Each user has a keypair. Signatures, public key and encodings are done according to the Schnorr signatures standard for the curve secp256k1.

  2. Users can publish events to any compatible relay by calling POST /save_event with Content-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>,
    }
    
  3. Upon receiving a call like that, the relay MUST compute the id, check the signature over the id and store the event so it can serve it later to other clients.

  4. There are 3 kinds of messages a user can publish (for now, these things are extensible and will be extended):

  • 0: set_metadata: the content 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 past set_metadata events once it gets a new one for the same pubkey.
  • 1: text_note: the content is set to the text content of a note, anything the user wants to say.
  • 2: recommend_server: the content is set to the URL (e.g., https://somerelay.com) of a relay the event creator wants to recommend to its followers.
  1. 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>.
  2. 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 to n.
  • 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 to p.
  • 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 a set_metadata event if the relay has it. Events triggered by this call MUST have their SSE type set to r.
  • 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 to r.

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:

  1. Initiate SSE sessions with every relay it knows;
  2. Send /request_watch to each with all the keys the user "follows";
  3. Send /request_feed afterwards so the user sees a wall of posts upon turning the app on;
  4. Send /request_watch and /request_unwatch if the user follows or unfollows someone;
  5. 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.