{"id":"e04dc21be953911d74ec247b948613fda94800560c698c2cc56662029417c066","pubkey":"2b39b4ffe62933df970e19366c22c1e092f953f83fcfed754e0f04d5a3b459f9","created_at":1781353189,"kind":30817,"tags":[["d","nip-17"],["title","NIP-17: Private Direct Messages"],["summary","Private direct messages, built from NIP-44 encryption and NIP-59 gift wraps, where the set of participants is what defines a room."],["s","draft"],["t","nostr"],["t","nip"],["k","14","Direct Message"],["k","15","File Message"],["k","10050","Relay list to receive DMs"],["alt","A specification: NIP-17: Private Direct Messages"],["client","openspecs-import"],["published_at","1691535321"],["proxy","https://github.com/nostr-protocol/nips/blob/656cecc7c0a815b6a2b218d3b5d6f078b3f4dbab/17.md","web"],["x","7aa9c3a9c0458a9552c4be2f3bf17f7ec60a4afe88bce7977f03500a7873a64c"]],"content":"NIP-17\n======\n\nPrivate Direct Messages\n-----------------------\n\n`draft` `optional` `relay`\n\nThis NIP defines an encrypted chat scheme which uses [NIP-44](nostr:naddr1qvzqqqrcvypzq2eeknl7v2fnm7tsuxfkds3vrcyjl9fls070a465urcy6k3mgk0eqqrxu6ts956rgat9m6z) encryption and [NIP-59](nostr:naddr1qvzqqqrcvypzq2eeknl7v2fnm7tsuxfkds3vrcyjl9fls070a465urcy6k3mgk0eqqrxu6ts956njrwwjk6) gift-wrapping.\n\nBy convention, `kind 14` direct messages, `kind 15` file messages, and [`kind 7` reactions](nostr:naddr1qvzqqqrcvypzq2eeknl7v2fnm7tsuxfkds3vrcyjl9fls070a465urcy6k3mgk0eqqrxu6ts95er2gglgtw) may be sent to an encrypted chat. See [Message Rumor Definitions](#message-rumor-definitions) below.\n\n## Chat Rooms\n\nThe most common use case is a room with only two peers, but more than one person per room is supported.\n\nThe set of `pubkey` + `p` tags defines a chat room. If a new `p` tag is added or a current one is removed, a new room is created with a clean message history.\n\nAn optional `subject` tag defines the current name/topic of the conversation. Any member can change the topic by simply submitting a new `subject` to an existing `pubkey` + `p` tags room. There is no need to send `subject` in every message. The newest `subject` in the chat room is the subject of the conversation.\n\n## Encrypting\n\nFollowing [NIP-59](nostr:naddr1qvzqqqrcvypzq2eeknl7v2fnm7tsuxfkds3vrcyjl9fls070a465urcy6k3mgk0eqqrxu6ts956njrwwjk6), the **unsigned** chat messages must be sealed (`kind:13`) and then gift-wrapped (`kind:1059`) to each receiver and the sender individually.\n\n```js\n{\n  \"id\": \"<usual hash>\",\n  \"pubkey\": wrapperPublicKey,\n  \"created_at\": randomTimeUpTo2DaysInThePast,\n  \"kind\": 1059, // gift wrap\n  \"tags\": [\n    [\"p\", receiverPublicKey, \"<relay-url>\"] // receiver\n  ],\n  \"content\": nip44.encrypt(\n    {\n      \"id\": \"<usual hash>\",\n      \"pubkey\": senderPublicKey,\n      \"created_at\": randomTimeUpTo2DaysInThePast,\n      \"kind\": 13, // seal\n      \"tags\": [], // no tags\n      \"content\": nip44.encrypt(\n        unsignedMessageRumor,\n        nip44.compute_conversation_key(senderPrivateKey, receiverPublicKey)\n      ),\n      \"sig\": \"<signed by senderPrivateKey>\"\n    },\n    nip44.compute_conversation_key(wrapperPrivateKey, receiverPublicKey)\n  ),\n  \"sig\": \"<signed by randomPrivateKey>\"\n}\n```\n\n`unsignedMessageRumor` is a rumor (an unsigned event, as per [NIP-59](nostr:naddr1qvzqqqrcvypzq2eeknl7v2fnm7tsuxfkds3vrcyjl9fls070a465urcy6k3mgk0eqqrxu6ts956njrwwjk6)), usually a `kind:14`, but could also be a different kind, see [Message Rumor Definitions](#message-rumor-definitions) below.\n\n`wrapperPrivateKey` and `wrappedPublicKey` are a new keypair, generated randomly anew for each message sent.\n\nClients MUST verify if pubkey of the `kind:13` is the same pubkey as that of the `unsignedMessageRumor`, otherwise any sender can impersonate any other by simply changing the pubkey on the rumor.\n\nClients SHOULD randomize `created_at` in up to two days in the past in both the seal and the gift wrap to make sure grouping by `created_at` doesn't reveal any metadata.\n\n## Publishing\n\nKind `10050` indicates the user's preferred relays to receive DMs. The event MUST include a list of `relay` tags with relay URIs.\n\n```yaml\n{\n  \"kind\": 10050,\n  \"tags\": [\n    [\"relay\", \"wss://inbox.nostr.wine\"],\n    [\"relay\", \"wss://myrelay.nostr1.com\"],\n  ],\n  \"content\": \"\",\n  // other fields...\n}\n```\n\nClients MUST only publish events to the relays listed in the recipient’s kind 10050 event. If such a list is not found that indicates the user is not ready to receive messages and clients shouldn't try.\n\n## Relays\n\nRelays SHOULD protect message metadata by only serving `kind:1059` events to users p-tagged on the event (enforced using [NIP-42 AUTH](nostr:naddr1qvzqqqrcvypzq2eeknl7v2fnm7tsuxfkds3vrcyjl9fls070a465urcy6k3mgk0eqqrxu6ts956ryv4r3t9)).\n\nClients SHOULD guide users to keep `kind:10050` lists small (1-3 relays) and SHOULD spread them to as many relays as viable.\n\n## Delete, edit and disappearing messages\n\nIn addition to the deletion behavior specified in [NIP 59](nostr:naddr1qvzqqqrcvypzq2eeknl7v2fnm7tsuxfkds3vrcyjl9fls070a465urcy6k3mgk0eqqrxu6ts956njrwwjk6), in which gift wraps can be deleted by the `p`-tagged recipient, clients MAY also allow users to delete messages by wrapping a `kind:5` delete event in a `kind:1059` gift wrap and sending it to the recipient as part of the conversation. Clients SHOULD remove deleted messages from the conversation, or indicate whether a message has been deleted.\n\nClients MAY implement edit by deleting an event and publishing another one with the same timestamp.\n\nClients MAY offer disappearing messages by setting an `expiration` tag in the gift wrap of each receiver or by not generating a gift wrap to the sender's public key. This tag SHOULD be included on the `kind:13` seal as well, in case it leaks.\n\n## Benefits & Limitations\n\nThis NIP offers the following privacy and security features:\n\n1. **No Metadata Leak**: Participant identities, each message's real date and time, event kinds, and other event tags are all hidden from the public. Senders and receivers cannot be linked with public information alone.\n2. **No Public Group Identifiers**: There is no public central queue, channel or otherwise converging identifier to correlate or count all messages in the same group.\n3. **No Moderation**: There are no group admins: no invitations or bans.\n4. **No Shared Secrets**: No secret must be known to all members that can leak or be mistakenly shared\n5. **Fully Recoverable**: Messages can be fully recoverable by any client with the user's private key\n6. **Optional Forward Secrecy**: Users and clients can opt-in for \"disappearing messages\".\n7. **Uses Public Relays**: Messages can flow through public relays without loss of privacy. Private relays can increase privacy further, but they are not required.\n8. **Cold Storage**: Users can unilaterally opt-in to sharing their messages with a separate key that is exclusive for DM backup and recovery.\n\nThe main limitation of this approach is having to send a separate encrypted event to each receiver. Group chats with more than 10 participants should find a more suitable messaging scheme.\n\n## Spam\n\nSince the wrapper events use random keys, relays cannot apply traditional anti-spam based on pubkey reputation, WoT, etc, therefore a naïve implementation of this NIP is an spam target. The recommended approach for spam mitigation is described in the [Spam Protection section of NIP-59](nostr:naddr1qvzqqqrcvypzq2eeknl7v2fnm7tsuxfkds3vrcyjl9fls070a465urcy6k3mgk0eqqrxu6ts956njrwwjk6#spam-protection).\n\n## Message Rumor Definitions\n\n### Chat Message\n\nKind `14` is a chat message. `p` tags identify one or more receivers of the message.\n\n```yaml\n{\n  \"pubkey\": \"<sender-pubkey>\",\n  \"created_at\": \"<current-time>\",\n  \"kind\": 14,\n  \"tags\": [\n    [\"p\", \"<receiver-1-pubkey>\", \"<relay-url>\"],\n    [\"p\", \"<receiver-2-pubkey>\", \"<relay-url>\"],\n    [\"e\", \"<kind-14-id>\", \"<relay-url>\"] // if this is a reply\n    [\"subject\", \"<conversation-title>\"],\n    // rest of tags...\n  ],\n  \"content\": \"<message-in-plain-text>\",\n}\n```\n\n`.content` MUST be plain text. Fields `id` and `created_at` are required.\n\nAn `e` tag denotes the direct parent message this post is replying to.\n\n`q` tags MAY be used when citing events in the `.content` with [NIP-21](nostr:naddr1qvzqqqrcvypzq2eeknl7v2fnm7tsuxfkds3vrcyjl9fls070a465urcy6k3mgk0eqqrxu6ts95erzkumy4n).\n\n```json\n[\"q\", \"<event-id> or <event-address>\", \"<relay-url>\", \"<pubkey-if-a-regular-event>\"]\n```\n\n## File Message\n\n```yaml\n{\n  \"pubkey\": \"<sender-pubkey>\",\n  \"created_at\": \"<current-time>\",\n  \"kind\": 15,\n  \"tags\": [\n    [\"p\", \"<receiver-1-pubkey>\", \"<relay-url>\"],\n    [\"p\", \"<receiver-2-pubkey>\", \"<relay-url>\"],\n    [\"e\", \"<kind-14-id>\", \"<relay-url>\", \"reply\"], // if this is a reply\n    [\"subject\", \"<conversation-title>\"],\n    [\"file-type\", \"<file-mime-type>\"],\n    [\"encryption-algorithm\", \"<encryption-algorithm>\"],\n    [\"decryption-key\", \"<decryption-key>\"],\n    [\"decryption-nonce\", \"<decryption-nonce>\"],\n    [\"x\", \"<the SHA-256 hexencoded string of the file>\"],\n    // rest of tags...\n  ],\n  \"content\": \"<file-url>\"\n}\n```\n\nKind `15` is used for sending encrypted file event messages:\n\n- `file-type`: Specifies the MIME type of the attached file (e.g., `image/jpeg`, `audio/mpeg`, etc.) before encryption.\n- `encryption-algorithm`: Indicates the encryption algorithm used for encrypting the file. Supported algorithms: `aes-gcm`.\n- `decryption-key`: The decryption key that will be used by the recipient to decrypt the file.\n- `decryption-nonce`: The decryption nonce that will be used by the recipient to decrypt the file.\n- `content`: The URL of the file (`<file-url>`).\n- `x` containing the SHA-256 hexencoded string of the encrypted file.\n- `ox` containing the SHA-256 hexencoded string of the file before encryption.\n- `size` (optional) size of the encrypted file in bytes\n- `dim` (optional) size in pixels in the form `<width>x<height>`\n- `thumbhash`(optional) the [thumbhash](https://evanw.github.io/thumbhash/) to show while the client is loading the file\n- `blurhash`(optional) the [blurhash](https://github.com/woltapp/blurhash) to show while the client is loading the file\n- `thumb` (optional) URL of thumbnail with same aspect ratio (encrypted with the same key, nonce)\n- `fallback` (optional) zero or more fallback file sources in case `url` fails (encrypted with the same key, nonce)\n\n\n## Examples\n\nThis example sends the message `Hola, que tal?` from `nsec1w8udu59ydjvedgs3yv5qccshcj8k05fh3l60k9x57asjrqdpa00qkmr89m` to `nsec12ywtkplvyq5t6twdqwwygavp5lm4fhuang89c943nf2z92eez43szvn4dt`.\n\nThe two final GiftWraps, one to the receiver and the other to the sender, respectively, are:\n\n```json\n{\n   \"id\":\"2886780f7349afc1344047524540ee716f7bdc1b64191699855662330bf235d8\",\n   \"pubkey\":\"8f8a7ec43b77d25799281207e1a47f7a654755055788f7482653f9c9661c6d51\",\n   \"created_at\":1703128320,\n   \"kind\":1059,\n   \"tags\":[\n      [\"p\", \"918e2da906df4ccd12c8ac672d8335add131a4cf9d27ce42b3bb3625755f0788\"]\n   ],\n   \"content\":\"AsqzdlMsG304G8h08bE67dhAR1gFTzTckUUyuvndZ8LrGCvwI4pgC3d6hyAK0Wo9gtkLqSr2rT2RyHlE5wRqbCOlQ8WvJEKwqwIJwT5PO3l2RxvGCHDbd1b1o40ZgIVwwLCfOWJ86I5upXe8K5AgpxYTOM1BD+SbgI5jOMA8tgpRoitJedVSvBZsmwAxXM7o7sbOON4MXHzOqOZpALpS2zgBDXSAaYAsTdEM4qqFeik+zTk3+L6NYuftGidqVluicwSGS2viYWr5OiJ1zrj1ERhYSGLpQnPKrqDaDi7R1KrHGFGyLgkJveY/45y0rv9aVIw9IWF11u53cf2CP7akACel2WvZdl1htEwFu/v9cFXD06fNVZjfx3OssKM/uHPE9XvZttQboAvP5UoK6lv9o3d+0GM4/3zP+yO3C0NExz1ZgFmbGFz703YJzM+zpKCOXaZyzPjADXp8qBBeVc5lmJqiCL4solZpxA1865yPigPAZcc9acSUlg23J1dptFK4n3Tl5HfSHP+oZ/QS/SHWbVFCtq7ZMQSRxLgEitfglTNz9P1CnpMwmW/Y4Gm5zdkv0JrdUVrn2UO9ARdHlPsW5ARgDmzaxnJypkfoHXNfxGGXWRk0sKLbz/ipnaQP/eFJv/ibNuSfqL6E4BnN/tHJSHYEaTQ/PdrA2i9laG3vJti3kAl5Ih87ct0w/tzYfp4SRPhEF1zzue9G/16eJEMzwmhQ5Ec7jJVcVGa4RltqnuF8unUu3iSRTQ+/MNNUkK6Mk+YuaJJs6Fjw6tRHuWi57SdKKv7GGkr0zlBUU2Dyo1MwpAqzsCcCTeQSv+8qt4wLf4uhU9Br7F/L0ZY9bFgh6iLDCdB+4iABXyZwT7Ufn762195hrSHcU4Okt0Zns9EeiBOFxnmpXEslYkYBpXw70GmymQfJlFOfoEp93QKCMS2DAEVeI51dJV1e+6t3pCSsQN69Vg6jUCsm1TMxSs2VX4BRbq562+VffchvW2BB4gMjsvHVUSRl8i5/ZSDlfzSPXcSGALLHBRzy+gn0oXXJ/447VHYZJDL3Ig8+QW5oFMgnWYhuwI5QSLEyflUrfSz+Pdwn/5eyjybXKJftePBD9Q+8NQ8zulU5sqvsMeIx/bBUx0fmOXsS3vjqCXW5IjkmSUV7q54GewZqTQBlcx+90xh/LSUxXex7UwZwRnifvyCbZ+zwNTHNb12chYeNjMV7kAIr3cGQv8vlOMM8ajyaZ5KVy7HpSXQjz4PGT2/nXbL5jKt8Lx0erGXsSsazkdoYDG3U\",\n   \"sig\":\"a3c6ce632b145c0869423c1afaff4a6d764a9b64dedaf15f170b944ead67227518a72e455567ca1c2a0d187832cecbde7ed478395ec4c95dd3e71749ed66c480\"\n}\n```\n\n```json\n{\n   \"id\":\"162b0611a1911cfcb30f8a5502792b346e535a45658b3a31ae5c178465509721\",\n   \"pubkey\":\"626be2af274b29ea4816ad672ee452b7cf96bbb4836815a55699ae402183f512\",\n   \"created_at\":1702711587,\n   \"kind\":1059,\n   \"tags\":[\n      [\"p\", \"44900586091b284416a0c001f677f9c49f7639a55c3f1e2ec130a8e1a7998e1b\"]\n   ],\n   \"content\":\"AsTClTzr0gzXXji7uye5UB6LYrx3HDjWGdkNaBS6BAX9CpHa+Vvtt5oI2xJrmWLen+Fo2NBOFazvl285Gb3HSM82gVycrzx1HUAaQDUG6HI7XBEGqBhQMUNwNMiN2dnilBMFC3Yc8ehCJT/gkbiNKOpwd2rFibMFRMDKai2mq2lBtPJF18oszKOjA+XlOJV8JRbmcAanTbEK5nA/GnG3eGUiUzhiYBoHomj3vztYYxc0QYHOx0WxiHY8dsC6jPsXC7f6k4P+Hv5ZiyTfzvjkSJOckel1lZuE5SfeZ0nduqTlxREGeBJ8amOykgEIKdH2VZBZB+qtOMc7ez9dz4wffGwBDA7912NFS2dPBr6txHNxBUkDZKFbuD5wijvonZDvfWq43tZspO4NutSokZB99uEiRH8NAUdGTiNb25m9JcDhVfdmABqTg5fIwwTwlem5aXIy8b66lmqqz2LBzJtnJDu36bDwkILph3kmvaKPD8qJXmPQ4yGpxIbYSTCohgt2/I0TKJNmqNvSN+IVoUuC7ZOfUV9lOV8Ri0AMfSr2YsdZ9ofV5o82ClZWlWiSWZwy6ypa7CuT1PEGHzywB4CZ5ucpO60Z7hnBQxHLiAQIO/QhiBp1rmrdQZFN6PUEjFDloykoeHe345Yqy9Ke95HIKUCS9yJurD+nZjjgOxZjoFCsB1hQAwINTIS3FbYOibZnQwv8PXvcSOqVZxC9U0+WuagK7IwxzhGZY3vLRrX01oujiRrevB4xbW7Oxi/Agp7CQGlJXCgmRE8Rhm+Vj2s+wc/4VLNZRHDcwtfejogjrjdi8p6nfUyqoQRRPARzRGUnnCbh+LqhigT6gQf3sVilnydMRScEc0/YYNLWnaw9nbyBa7wFBAiGbJwO40k39wj+xT6HTSbSUgFZzopxroO3f/o4+ubx2+IL3fkev22mEN38+dFmYF3zE+hpE7jVxrJpC3EP9PLoFgFPKCuctMnjXmeHoiGs756N5r1Mm1ffZu4H19MSuALJlxQR7VXE/LzxRXDuaB2u9days/6muP6gbGX1ASxbJd/ou8+viHmSC/ioHzNjItVCPaJjDyc6bv+gs1NPCt0qZ69G+JmgHW/PsMMeL4n5bh74g0fJSHqiI9ewEmOG/8bedSREv2XXtKV39STxPweceIOh0k23s3N6+wvuSUAJE7u1LkDo14cobtZ/MCw/QhimYPd1u5HnEJvRhPxz0nVPz0QqL/YQeOkAYk7uzgeb2yPzJ6DBtnTnGDkglekhVzQBFRJdk740LEj6swkJ\",\n   \"sig\":\"c94e74533b482aa8eeeb54ae72a5303e0b21f62909ca43c8ef06b0357412d6f8a92f96e1a205102753777fd25321a58fba3fb384eee114bd53ce6c06a1c22bab\"\n}\n```\n","sig":"79f2a5a12082f6dbf6eed94be97599e095a82e61c909d794a3c2a2dcaa616543e8640c38c7ebfd40f741c03f3ffcdedba328d25fd9c77360ea53e680016b3793"}