eip: 7824 (temp)
title: Nostr and NFT Compatible Social Media Publishing Standard
author: Nick Juntilla nick@ownerfy.com
discussions-to: Issues · ethereum/EIPs · GitHub
status: Draft
type: Standards Track
category: ERC
created: 2024-12-18
requires: “721, 1155”
Simple Summary
This proposal defines a standardized format for representing decentralized social media posts as NFTs. The Nostr protocol has already done most of the heavy lifting for creating an open decentralized social media network. This EIP servces the purpose of adapting those standards to the most common blockchain non-fungible standards to take advantage of the reach and longevity of the blockchain. It is genericized here so that it can be easily adapted to the AT protocol, used in other event based decentralized social media, blogs, encrypted messaging, an RSS feed or miscellaneous publication. This model is flexible where the meaning and type of post (original, reply, repost, images, video, text, etc…) is derived from its metadata. Additionally there is a mechanism to edit a post to a new version.
Abstract
This EIP defines a standardized way to create and manage NFTs that represent social media posts. These NFTs can be original posts, replies, reposts, or quote posts. Each NFT encapsulates a single social media post as a Nostr-compatible object, including fields such as pubkey, content, kind, tags, and created_at. By providing a 1:1 mapping of nostr data structures, any NFT minted under this standard can be interpreted by Nostr-style clients, relays, or indexing services with minimal effort. Additionally, this standard defines how replies, reposts, and quotes should reference prior events, enabling rich social media interactions in a multi-chain, multi-contract, multi-platform environment.
Other kinds of common social network primitives including likes, follows and other interactions are easily represented by this standard using Nostr standard kinds
and tags
without changing this standard. This document will not be an exhaustive list of all possible events. Please refer to the Nostr protocol for more information on how to represent other kinds of events and further evolution.
In some places in this standard the word “post” is used and in other places “event” is used. This is because the Nostr protocol uses the word “event” to describe a post. They should be understood as generally the same thing. An NFT using this standard should never create more than one initial PostEvent
upon creation. Many EditPostEvent
s may be created for a single NFT.
This standard defines:
- Representation as ERC-721 or ERC-1155 tokens.
- A single posting mechanism:
createPost
. - Interpretation of post type (original, reply, repost) via the presence of certain tags in the metadata rather than separate functions or events.
- The ability to edit previously published posts with an
editPost
function and correspondingEditPostEvent
. - Multiple media attachments and external references through flexible tags.
- NFT metadata aligned with common NFT practices to so existing NFT apps will display as much of the NFT as possible using the most common metadata structure.
- By implementing a separate public key and signature a third party may also publish a message on behalf of another user.
Posts may be “owned” by their creators. This may be a useful mechanic, but the independent signature of the post allows for a third party to publish a message on behalf of another user. This is useful for the convenince of third party services to publish or enshrine messages. Additionally editing a post may be limited to the original author. This is a design decision that should be made by the implementor.
Motivation
With the continued censorship and manipulation of social media platforms it becomes increasingly important for truly decentralized social media to exist. Unlike other attempts at blockchain decentralized social media, this method does not rely on a single set of smart contracts or even the blockchain itself. Blockchain integration of author-signed event based social media simply adds a powerful substrate for standardized decentralized social media to exist in. The benefits of blockchain include, but are not limited to longevity, censorship resistance, monetary, and neutrality. The NFT standard is the most common and widely used standard for representing unique digital data on the blockchain. Using the NFT standard and standard NFT properties means that every marketplace, wallet and app that makes NFTs viewable is also a publication channel. With the data publicly available custom feed algorithms can be generated to give the control back to users.
Why Nostr
Nostr is a simple and flexible protocol for decentralized social media. By making NFTs Nostr-compatible, this EIP allows NFTs to become a fundamental building block for decentralized many existing and future social platforms.
- Interoperability: Content can be freely moved between Ethereum-based platforms and Nostr-based infrastructure.
- Open permissionless Identity: Anyone can create a private key and start posting.
- Decentralized and Self Authenticating Content: Each post is signed by the author, ensuring authenticity and enabling third-party publishing.
- Cross-Chain and Cross-Platform Referencing: A standardized tagging system enables referencing posts on other blockchains, other contracts, or external URLs.
- Flexible Content: Support for multimedia, external links, and arbitrary metadata, making make this protocol suitable for a wide range of social media applications and publishing applications.
- Strong community: Nostr has a strong community of developers and users who are actively building and using the protocol.
Nostr’s minimal and extensible protocol is ideal for decentralized publishing. Integrating Nostr with NFTs ensures that users own their content while maintaining interoperability with Nostr clients. Supporting multimedia and external links allows richer content, making NFTs viable as building blocks for decentralized social networks, forums, classifieds, messaging, and multimedia platforms.
Specification
Post Structure
Each post includes these fields emitted as an event and in the metadata. This metadata could be saved on-chain as well depending on the implementation:
- id: A unique, deterministic identifier for the post.
- author: The on-chain address of the post creator.
- created_at: Timestamp of creation (e.g., block timestamp at minting).
- kind: An integer representing the post category. This is expanded on below.
- tags: Arrays of arrays of strings that describe attachments, references, and other metadata. There are more example tags below as well as all possible Nostr tags.
- For example,
["e", "<id_of_referenced_post>"]
indicates a reply. ["m", "<media_url>", "<mime_type>"]
indicates attached media.["r", "<URL>"]
references an external URL.["ch", "<chain_id>"]
,["c", "<contract_address>"]
for cross-chain/contract references.
- For example,
- content: The textual content of the post.
Post Types (Examples)
- Original Post: kind=1
- Reply: kind=2 (or reuse kind=1 with reply-indicating tags)
- Repost: kind=6 (for example)
- Quote Post: Another distinct integer (e.g. 30023)
- These are just example values. Different implementations may standardize on their own kind values or sets.
Kinds
Kinds specify how clients should interpret the meaning of each event and the other fields of each event (e.g. an “r” tag may have a meaning in an event of kind 1 and an entirely different meaning in an event of kind 10002). Each NIP may define the meaning of a set of kinds that weren’t defined elsewhere. This NIP defines two basic kinds:
0: user metadata: the content is set to a stringified JSON object {name: , about: , picture: <url, string>} describing the user who created the event. Extra metadata fields may be set. A relay may delete older events once it gets a new one for the same pubkey.
1: text note: the content 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.
Ref: NIP-01 Basic Event Kinds for basic tags and kinds
Ref: Current Full List of Kinds
Tags
tags is a flexible mechanism to attach additional structured data to a post. Each tag is an array of one or more strings, with some conventions around them. Some examples:
-
Media Attachments:
[“m”, “<media_url>”, “<mime_type>”]
Multiple media can be attached by including multiple [“m”, …] tags. -
References to Other Posts:
[“e”, “<id_of_referenced_post>”] -
External URLs:
[“r”, “”] -
Cross-Chain/Contract References:
[“ch”, “<chain_id>”], [“c”, “<contract_address>”]
Generating a Key
Private key: A Nostr compatible key can be generated locally with a command like openssl rand -hex 32
or web3.utils.sha3(web3.utils.randomHex(32))
. This key is used to sign the post and is stored in the pubkey field. An Ethereum private key or mnemonic phrase can also be used, as long as the result is a 32-byte hex string.
Public key: Public keys are based on Taproot + Schnorr, bitcoin BIP-0341. And can be generated with nostril compatible signing tools.
Alternatively bech32-(not-m) can be used to encode private and public keys so that the prefixes npub and nsec can be used to differentiate between the two. Ref: NIP-19
Token Standards Compatibility
- ERC-721: Each post is a unique NFT (
tokenId
). - ERC-1155: A
tokenId
can represent one post event, potentially minted multiple times. Theid
ensures a unique reference to the event data.
Events
There are many possible types of events, but we will focus on posts as the would most likely be turned into an NFT as opposed to a like or a follow. A more complete list can be found here: List of event kinds
CreateEvent
When a new post is created:
event CreateEvent(
address indexed publisher,
uint256 indexed tokenId,
string uri,
bytes32 indexed id,
bytes32 pubkey,
uint256 created_at,
uint32 kind,
string content,
string tags,
string sig,
);
publisher
is the address of the user who created the post. tokenId
is the NFT ID. uri
is the URI of the NFT metadata. id
is the unique identifier of the post. pubkey
is the public key of the post creator. created_at
is the timestamp of creation. kind
is the post category. content
is the textual content of the post. tags
is the structured metadata. sig
is the signature of the post data.
To derive the id
To obtain the id, we sha256 the serialized properties. The serialization is done over the UTF-8 JSON-serialized string (which is described below) of 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>
]
To prevent implementation differences from creating a different event ID for the same event, the following rules MUST be followed while serializing:
- UTF-8 should be used for encoding.
- Whitespace, line breaks or other unnecessary formatting should not be included in the output JSON.
- The following characters in the content field must be escaped as shown, and all other characters must be included verbatim:
- A line break (0x0A), use \n
- A double quote (0x22), use "
- A backslash (0x5C), use \
- A carriage return (0x0D), use \r
- A tab character (0x09), use \t
- A backspace, (0x08), use \b
- A form feed, (0x0C), use \f
Ref: NIP-01
Sign a post
Signatures are based on schnorr signatures standard for the curve secp256k1.
When an existing post is edited:
event EditEvent(
address indexed editor,
uint256 indexed tokenId,
string uri,
bytes32 indexed id,
string newContent,
string newTags,
string sig
);
editor
is the address of the user who edited the post. tokenId
is the NFT ID. uri
is the URI of the NFT metadata. id
is the unique identifier of the post. newContent
is the updated textual content of the post. newTags
is the updated structured metadata. sig
is the signature of the post data.
The contract must update the stored metadata for that token accordingly.
Metadata JSON
Event fields are stored in the NFT’s metadata under properties
. The description
field of the NFT is identical to content
. The name
field includes the author and optionally the platform, formatted as "<author> via <platform> as <type>"
where author should be a human readable name and platform should be a human readable platform name. If no platform is specified the format should be "<author> as <type>"
. Type may be “Social Post” or “Event” or any other type that is appropriate.
{
"name": "<author> via <platform> as <type>",
"description": "<arbitrary string should match the content>",
"image": "https://example.com/thumbnail.jpg",
"external_url": "<optional same as first reference url if it exists>"
"properties": {
"id": "<32-bytes lowercase hex-encoded sha256 of the serialized properties data>",
"pubkey": "<32-bytes lowercase hex-encoded public key of the publicized creator>",
"created_at": <unix timestamp in seconds>,
"kind": <integer between 0 and 65535>,
"tags": [
["e", "<id_of_referenced_post>"],
["m", "https://example.com/image1.jpg", "image/jpeg"],
["m", "https://example.com/image1.jpg", "image/jpeg"],
["r", "https://example.com/external_resource"],
["<arbitrary string>", "<arbitrary string>"],
],
"content": "<optional if this key exists it must match description, else if only description is used then use the description as content>",
"sig": "<64-bytes lowercase hex of the signature of the sha256 hash of the serialized properties data, which is the same as the "id" field>"
}
}
Example Solidity Functions
These examples are possible partial implementations. Only metadata and events are required to be implemented. The rest is up to the implementor.
function createPost(
uint256 tokenId,
string uri,
bytes32 id,
bytes32 pubkey,
uint256 created_at,
uint32 kind,
string content,
string tags,
string sig
) public {
address publisher = msg.sender;
mint(tokenId, uri);
emit CreateEvent(publisher, tokenId, uri, id, pubkey, created_at, kind, content, tags, sig);
}
function editPost(
uint256 tokenId,
string uri,
bytes32 id,
bytes32 pubkey,
string newContent,
string newTags,
string sig
) public {
require(pubKeys[msg.sender] == pubkey, "Not authorized to edit this post");
address editor = msg.sender;
setUri(tokenId, uri);
emit EditEvent(editor, tokenId, uri, id, newContent, newTags, sig);
}
-
createPost
mints a new token. The authorIdentifier and optional platform form the name field. -
editPost
allows the author or an authorized party to modify content and tags. Must enforce access control.
Backwards Compatibility
This is an additive standard on top of ERC-721 and ERC-1155. Existing NFTs remain compatible; clients or platforms that understand this standard can interpret these tokens as social posts.
Security Considerations
Data Integrity:
Ensure that id (nostrId) is consistently derived, e.g., from metadata hash, to prevent forgeries.
Editing Posts:
While editing posts may have any platform-specific rules, events should be signed by the original author to be self verifying.
Spam and Moderation:
Nostr and NFTs both allow permissionless creation of content. Platforms built on this standard should implement their own moderation layers, blocklists, or reputation systems.
References
ERC-721 Non-Fungible Token Standard
ERC-1155 Multi Token Standard
Nostr Kinds and Events
NIP-01 Basic Kinds
NIP-10 Replies and Mentions
NIP-18 Reposts
Copyright
Copyright and related rights waived via CC0.
Update Log
- 2024-12-18: Pull request, commit link to commit