name: nostr description: This skill should be used when working with the Nostr protocol, implementing Nostr clients or relays, handling Nostr events, or discussing Nostr Implementation Possibilities (NIPs). Provides comprehensive knowledge of Nostr's decentralized protocol, event structure, cryptographic operations, and all standard NIPs.
This skill provides expert-level assistance with the Nostr protocol, a simple, open protocol for global, decentralized, and censorship-resistant social networks. The protocol is built on relays and cryptographic keys, enabling direct peer-to-peer communication without central servers.
Activate this skill when:
Nostr operates on two main components:
Key principles:
All data in Nostr is represented as events. An event is a JSON object with this structure:
{
"id": "<32-bytes lowercase hex-encoded sha256 of the serialized event data>",
"pubkey": "<32-bytes lowercase hex-encoded public key of the event creator>",
"created_at": "<unix timestamp in seconds>",
"kind": "<integer identifying event type>",
"tags": [
["<tag name>", "<tag value>", "<optional third param>", "..."]
],
"content": "<arbitrary string>",
"sig": "<64-bytes lowercase hex of the schnorr signature of the sha256 hash of the serialized event data>"
}
Standard event kinds (from various NIPs):
0 - Metadata (user profile)1 - Text note (short post)2 - Recommend relay3 - Contacts (following list)4 - Encrypted direct messages5 - Event deletion6 - Repost7 - Reaction (like, emoji reaction)40 - Channel creation41 - Channel metadata42 - Channel message43 - Channel hide message44 - Channel mute user1000-9999 - Regular events10000-19999 - Replaceable events20000-29999 - Ephemeral events30000-39999 - Parameterized replaceable eventsCommon tag types:
["e", "<event-id>", "<relay-url>", "<marker>"] - Reference to an event["p", "<pubkey>", "<relay-url>"] - Reference to a user["a", "<kind>:<pubkey>:<d-tag>", "<relay-url>"] - Reference to a replaceable event["d", "<identifier>"] - Identifier for parameterized replaceable events["r", "<url>"] - Reference/link to a web resource["t", "<hashtag>"] - Hashtag["g", "<geohash>"] - Geolocation["nonce", "<number>", "<difficulty>"] - Proof of work["subject", "<subject>"] - Subject/title["client", "<client-name>"] - Client application usedFor detailed specifications, refer to references/nips-overview.md.
The foundation of Nostr. Defines:
Event kind 3 for following lists:
p tag represents a followed userEvent kind 4 for private messages:
p tag for recipient pubkeyInternet identifier format: name@domain.com
.well-known/nostr.json endpointEvent kind 5 to request deletion:
e tags for events to deleteConventions for e and p tags in replies:
HTTP endpoint for relay metadata:
Event kind 7 for reactions:
e tag for reacted eventp tag for event authorClient authentication to relays:
22242 signed auth eventCRITICAL: Clients MUST wait for OK response after AUTH
true confirms the relay has stored the authenticated pubkeyfalse indicates authentication failed: 1. Alert the user that authentication failed
2. Assume the relay will reject subsequent events requiring auth
3. Check the reason field for error details (e.g., "error: failed to parse auth event")
true is receivedQuery filter extension for full-text search:
search field in REQ filtersHuman-readable identifiers:
npub: public keynsec: private key (sensitive!)note: note/event IDnprofile: profile with relay hintsnevent: event with relay hintsnaddr: replaceable event coordinateImproved encryption for direct messages:
Event kind 10002 for relay lists:
EVENT - Publish an event:
["EVENT", <event JSON>]
REQ - Request events (subscription):
["REQ", <subscription_id>, <filters JSON>, <filters JSON>, ...]
CLOSE - Stop a subscription:
["CLOSE", <subscription_id>]
AUTH - Respond to auth challenge:
["AUTH", <signed event kind 22242>]
EVENT - Send event to client:
["EVENT", <subscription_id>, <event JSON>]
OK - Acceptance/rejection notice:
["OK", <event_id>, <true|false>, <message>]
EOSE - End of stored events:
["EOSE", <subscription_id>]
CLOSED - Subscription closed:
["CLOSED", <subscription_id>, <message>]
NOTICE - Human-readable message:
["NOTICE", <message>]
AUTH - Authentication challenge:
["AUTH", <challenge>]
Filters select events in REQ messages:
{
"ids": ["<event-id>", ...],
"authors": ["<pubkey>", ...],
"kinds": [<kind number>, ...],
"#e": ["<event-id>", ...],
"#p": ["<pubkey>", ...],
"#a": ["<coordinate>", ...],
"#t": ["<hashtag>", ...],
"since": <unix timestamp>,
"until": <unix timestamp>,
"limit": <max number of events>
}
Filtering rules:
#<single-letter> matches tag valuesids and authorsSteps to create a signed event:
id and sigidid with schnorr signature → sigSerialization format for ID calculation:
[
0,
<pubkey>,
<created_at>,
<kind>,
<tags>,
<content>
]
Steps to verify an event:
limit, since, until for queriesconst event = {
pubkey: userPublicKey,
created_at: Math.floor(Date.now() / 1000),
kind: 1,
tags: [],
content: "Hello Nostr!",
}
// Calculate ID and sign
event.id = calculateId(event)
event.sig = signEvent(event, privateKey)
// Publish to relay
ws.send(JSON.stringify(["EVENT", event]))
const filter = {
kinds: [1],
authors: [followedPubkey1, followedPubkey2],
limit: 50
}
ws.send(JSON.stringify(["REQ", "my-sub", filter]))
const reply = {
kind: 1,
tags: [
["e", originalEventId, relayUrl, "root"],
["p", originalAuthorPubkey]
],
content: "Great post!",
// ... other fields
}
const reaction = {
kind: 7,
tags: [
["e", eventId],
["p", eventAuthorPubkey]
],
content: "+", // or emoji
// ... other fields
}
Start with these NIPs in order:
For comprehensive NIP details, see:
When implementing Nostr: