Everything on Nostr is an event
There is one data structure. A note is an event, a profile is an event, a follow list is an event, a deletion request is an event. Learn the seven fields and the rest of the protocol is a catalogue of what people have agreed to put in them.
An event is a JSON object with seven fields, signed by the key that wrote it. Relays store events and hand them out; clients ask relays for events matching a filter. That is the entire architecture. There is no account, no server-side identity and no canonical copy — an event that exists on three relays and is deleted from one still exists on two.
Two of the seven fields carry all the ambiguity: kind, an integer that says how to interpret the event, and tags, an array of arrays that carries every reference to anything else. The other five are mechanics, and they are mechanics you can learn in one sitting.
Read one
Below is a real note, fetched from a public relay by a scheduled job on the machine that builds this page. Click any field to see what it does and where the specification defines it.
{
} id
The SHA-256 of the event serialised in a fixed order: [0, pubkey, created_at, kind, tags, content]. It is derived, never assigned — change one character of content and it is a different event with a different id.
People assume it is a database key a relay hands out, then wonder why there is no edit. There is no edit because editing would produce a different event. Deletion is a request to relays, not an operation on this id.
Defined in NIP-01 · type 64-character lowercase hex string
pubkey
Who wrote it. This is the whole account: there is no username, no registration and no server that owns it. The same key works in every client, on every relay, at once.
It is shown in clients as an npub, which is the same key in bech32 with a checksum. npub and hex are two spellings of one thing — but nsec, which looks similar, is the PRIVATE key and is never shown anywhere.
Defined in NIP-19 · type 64-character hex string — an x-only secp256k1 public key
created_at
When the author says they wrote it. Clients sort by it, and relays use it to decide which of two replaceable events is newer.
It is a claim, not an observation. Nothing stops a client writing any number, and a note dated next week will sit at the top of a feed until the date passes. Relays may reject timestamps far from now, but they are not required to.
Defined in NIP-01 · type Unix timestamp in seconds
kind
What the event IS. 0 is a profile, 1 is a note, 3 is a follow list, 7 is a reaction, 30023 is a long-form article. Everything Nostr can express is a number in this field plus an agreement about what the rest of the event means.
The ranges have behaviour attached, and it is easy to miss. 10000–19999 are replaceable (a relay keeps only the newest per author), 20000–29999 are ephemeral (relays need not store them at all), 30000–39999 are addressable by a "d" tag. A client that treats a replaceable kind as ordinary shows a history that no longer exists.
Defined in NIP-01 · type integer
tags
Every reference to anything outside this event. "e" points at another event, "p" at another key, "t" is a hashtag, "d" is the identifier that makes an addressable event replaceable. The first string names the tag; the rest are its arguments.
This is where threading goes wrong. The positional convention for replies — a "root" marker and a "reply" marker on "e" tags — is NIP-10, it postdates NIP-01, and clients that predate it or ignore it build the thread differently. Two clients showing the same conversation in a different shape are usually both reading these tags correctly and disagreeing about the convention.
Defined in NIP-10 · type array of arrays of strings
content
The payload, and what it means depends entirely on kind. For a kind 1 it is the text of the note. For a kind 0 it is a JSON document describing the profile. For an encrypted message it is ciphertext.
It is a plain string with no markup and no length limit in the protocol. Clients render it by convention — linkifying URLs, expanding nostr: references, treating newlines as newlines — and no two clients agree exactly. Anything that looks like formatting is the client, not the event.
Defined in NIP-01 · type string
sig
The signature over the id, by the private key matching pubkey. It is what makes the event self-authenticating: anyone holding it can check it, with no relay and no server involved.
Verification is the client's job and not every client does it on every event. A relay that hands you an event with a bad signature is not lying to you in a way the protocol prevents — it is lying in a way you are expected to catch.
Defined in NIP-01 · type 128-character hex string — a BIP-340 Schnorr signature
Fetched from wss://relay.damus.io on 2026-09-16. The id was recomputed from the event's own fields and matches; the Schnorr signature was not verified here. Without JavaScript this is the same JSON with all seven annotations listed underneath it in field order — which is also what prints.
The id is the part worth sitting with. It is not assigned by anything; it is derived. Serialise the event in a fixed order, hash it, and that hash is the identity of the event.2 Change one character of content and it is a different event with a different id.
This is why there is no edit button on Nostr, and why deletion is a request rather than an operation.1 An edit would produce a different event, which is to say a different thing; a deletion is a message asking other people’s servers to forget something they already have. Neither is a limitation someone forgot to fix. They are what “the identity of a message is its content” costs.
Where it goes
Publishing is a websocket message and an acknowledgement. Nothing about it is transactional: each relay independently accepts or rejects, and a client that publishes to five relays may end up on three.
- 01 The client builds the event and computes its id Everything but id and sig is filled in first, because the id is a hash of those fields. The client serialises them in the fixed order and takes the SHA-256.
[0,"<pubkey>",<created_at>,<kind>,<tags>,"<content>"] - 02 The key signs the id A BIP-340 Schnorr signature over the 32 bytes of the id. In a browser this is usually done by an extension holding the key, so the web page never sees it — which is the arrangement NIP-07 exists to standardise.
window.nostr.signEvent(event) → { ...event, sig } - 03 The client sends EVENT to each relay it is connected to One websocket message per relay, sent independently. There is no coordinator and no transaction: this is five separate conversations that happen to carry the same payload.
["EVENT", { "id": "…", "pubkey": "…", "sig": "…", … }] - 04 Each relay answers OK, on its own terms A relay may accept it, reject it for a policy reason, demand payment or proof of work, or require authentication first. The boolean is the verdict and the string is the reason, and the reason is meant to be shown to a person.
["OK","<event-id>",true,""] ["OK","<event-id>",false,"blocked: pubkey not on allowlist"] - 05 Readers receive it only if they asked the right relay A client sees an event because it had an open subscription on a relay that accepted it. Nobody is notified; nothing is pushed to anyone who was not already listening on the right server.
["REQ","sub1",{"kinds":[1],"authors":["<pubkey>"]}] ["EVENT","sub1",{ … }]
Without JavaScript every step is expanded: the block reads as five numbered paragraphs with their wire messages, which is also what prints.
The consequence people find hardest is that there is no delivery guarantee anywhere in this. A client shows you what the relays it happens to be connected to happened to have. Two people reading the same thread can legitimately see different replies, and neither client is wrong.
This is also the protocol’s defence. A relay can refuse you, and the cost of that refusal is that you use another relay. There is nothing to be deplatformed from, because there is nothing you were ever on.
What this does not solve
A guide that only sells is less useful and less credible, and this audience will notice. Three things the event model makes harder rather than easier, stated plainly:
Discovery is genuinely worse. Without a server that has seen everything, “show me interesting people I do not follow” has no obvious implementation. Every current answer is a relay that has, in practice, seen everything — which is the centralisation the design was avoiding, reintroduced as a convenience.
Key management is the whole risk, and it is on you. There is no reset. A leaked private key is a permanently compromised identity, and the mitigations that exist — signing extensions, remote signers, hardware — are all ways of keeping the key away from software rather than ways of recovering from a mistake.
Spam is priced, not prevented. Proof of work, payment and allowlists are the levers, and each one trades openness for quiet. A relay with no lever is unusable; a relay with every lever is a walled garden with extra steps.
None of these are reasons not to use it. They are the things to know before deciding that the trade is worth it for what you want to do.
Notes
- Deletion is a kind 5 event asking relays to drop a referenced event. Relays may honour it, ignore it, or honour it and still have served the event to a client that asked first.NIP-09
- The serialisation used for the id is an array, not an object, precisely so that key order cannot vary between implementations. Two clients that serialise an object in different orders would compute two different ids for one event.
Source & licence
This page adapts no licensed material. It is original work by Delta Gamma LLC and is published under CC BY-SA 4.0; the figures it reports are separately dedicated to the public domain, below.
nostr101 adds no normative claims. Where this page and a NIP disagree, the NIP is correct and this page is a bug. Every page states the revision it was written against: NIP-01 @ 656cecc.
Numbers, titles and kind assignments come from the NIPs repository and belong to its contributors, who state that all NIPs are public domain. This page asserts no licence over that text. Its own prose is CC BY-SA 4.0; attribute to nostr101.com and to the NIPs contributors.