├── 01.md ├── 02.md ├── 03.md ├── 04.md └── README.md /01.md: -------------------------------------------------------------------------------- 1 | DIP-01 2 | ====== 3 | 4 | Image Metadata 5 | -------------- 6 | 7 | `draft` `optional` `author:jb55` 8 | 9 | Image metadata is additional information associated with urls in notes that 10 | clients can use to provide a better experience when loading images. 11 | 12 | Image metadata is an `imeta` tag that looks like this: 13 | 14 | ```json 15 | { 16 | "content": "More image metadata tests don’t mind me https://nostr.build/i/863321bb1ae68830ebcf9a343ca0a5e0ce2323d0a55b7fbe86f7a886cf61db8d.jpg ", 17 | "kind": 1, 18 | "tags": [ 19 | [ 20 | "imeta", 21 | "url https://nostr.build/i/863321bb1ae68830ebcf9a343ca0a5e0ce2323d0a55b7fbe86f7a886cf61db8d.jpg", 22 | "blurhash eVF$^OI:${M{o#*0-nNFxakD-?xVM}WEWB%iNKxvR-oetmo#R-aen$", 23 | "dim 3024x4032" 24 | "alt A scenic photo overlooking the coast of Costa Rica" 25 | ] 26 | ] 27 | } 28 | ``` 29 | 30 | There can be multiple imeta tags for each image url in the content. 31 | 32 | The `imeta` tag MUST have a `url`, and MUST have at least one piece of 33 | metadata (`blurhash`, `dim`, etc). Otherwise it is invalid. 34 | 35 | ## Fields 36 | 37 | `imeta` can have the following metadata fields: 38 | 39 | - `blurhash`: A small hash representing a blurred version of the photo. 40 | This can be used to create blurry previews before the image is 41 | displayed. 42 | 43 | - `dim`: The dimensions of the image. You can use the aspect ratio of 44 | the dimensions to properly size the blurhash 45 | 46 | - `sha256`: The sha256 hash of the image. This can be used to detect if 47 | the content changes. 48 | 49 | - `alt`: Accessible text label for the vision-impaired or text clients. 50 | 51 | ## Recommended client behavior 52 | 53 | When uploading images during a new post, clients can include this metadata 54 | after the image is uploaded and included in the post. 55 | 56 | When pasting urls during post composition, the client could download the image 57 | and add this metadata before the post is sent. 58 | -------------------------------------------------------------------------------- /02.md: -------------------------------------------------------------------------------- 1 | DIP-02 2 | ====== 3 | 4 | # Quote Reposts 5 | 6 | `draft` `optional` 7 | 8 | Quote reposts are `kind 1` events with an embedded `q` tag of the note id being 9 | quote reposted. The `q` tag ensures quote reposts are not pulled and included 10 | as replies in threads. It also allows you to easily pull and count all of the 11 | quotes for a post. 12 | -------------------------------------------------------------------------------- /03.md: -------------------------------------------------------------------------------- 1 | DIP-03 2 | ====== 3 | 4 | # Private Zaps 5 | 6 | Private lightning zaps are a way to send a zap while not revealing the zap author. Only the receiver of the zap can see who sent the zap, and may include an optional private message. 7 | 8 | ## Private Zap Request Event 9 | 10 | A `private zap request` is used to send a zap to a user but only revealing the sender's identity to the receiver. 11 | An `anon` tag is added to the `zap request` event with the encrypted `private zap request` event. 12 | 13 | The `private zap event` is encrypted using [NIP-04](04.md) between the `zap request` key and the recipient's pubkey. The `zap request` key is the ephemeral key used to sign the `zap request` event. 14 | The encrypted `private zap request` event is then added to the `zap request` event as the `anon` tag bech32 encoded with the `hrp` of `pzap1` and the `iv` values of the encryption are added with an underscore followed by the iv being bech32 encoded wit the `hrp` of `iv`. 15 | 16 | Example Private Zap Event: 17 | 18 | ```json 19 | { 20 | "id": "9bf0e1d812e10faa5fd3d2560637f8e99fcbfff478583d2c835754d791acfda5", 21 | "pubkey": "385c3a6ec0b9d57a4330dbd6284989be5bd00e41c535f9ca39b6ae7c521b81cd", 22 | "created_at": 1708467377, 23 | "kind": 9733, 24 | "tags": [ 25 | [ 26 | "p", 27 | "aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4" 28 | ] 29 | ], 30 | "content": "Private Zap message!", 31 | "sig": "48cc725483f41207e83c48f3d01a4e5d341bedcf556e158c19b9faac42bca30e92113049268522cd5052ab3c69850c5df7e2b4fb40c72ce770996edee5a57c8d" 32 | } 33 | 34 | ``` 35 | 36 | Example Private Zap Request Event: 37 | 38 | ```json 39 | { 40 | "id": "4ef9df0a6c7dd8b761145acc7055ae077df716bd8c374496d1f598c5b63bcd7a", 41 | "pubkey": "79d4773fded68a3ea9a30f13e651ff83e150957dacb0c2f6038883d837e1b17b", 42 | "created_at": 1708466564, 43 | "kind": 9734, 44 | "tags": [ 45 | [ 46 | "p", 47 | "aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4" 48 | ], 49 | [ 50 | "relays", 51 | "wss://relay.damus.io" 52 | ], 53 | [ 54 | "anon", 55 | "pzap1n0pkup9fxc9w3yd2tvfr03shffrapfz8rtzu9kkq6v222jw5rgtp9myyh378gdtwptpnls8f0rv0v2dyapgt7sssu4263puepgshsj9g4u9y5lvfv9fsujlgvywsuejvftlfzcanu5fmnf2a3grlelwe8v0z4mdkyhr9mddxpswtvp7mtlc4acdys7740t0x5ej36qs5amfzwz5dpwlaf4gsl69lzhqdgc3hgt62xw4y8384a6zvsnf96l3ardkd2vkk6cm77p6v7ul3gwgjr7tra7uzpkvf4hncxp5qd75h6cdadf6n2d7edhc3dyyy7qpdka2mgqhvckhzhd2gcaux34jyw6qfk3nxhaaqs6pqkuy6z34wu2p2fvqqvg55eyqlrndjlgekm7xu08lqc3g0nje59uqu0adqerv2puypez3eck9xzupg4vxyfclk37qfqxra8nt4tk9ydc2tzhpnl4wpf7jf2nrkchknfnfgmezfyqe074dexe5mkxgw67j7zn8s24tae8tml747qnq0edw5jxsx6xfc4qhshf3man0s5duw6wm63ue8fese8c7hanqzphjna3g0ee4jgpwceqzk9jgrvf9rnkt89tkvh75qm65nvtqpud30vecwlqzdlu9fhcaj7jv89gpy32y2k828vsj7x8hmlq55rleeq23e062apenymv96tkvltv266ww6kly2q2t7k6z_iv189a0s9afn7ehz4gpeanueh56cv6t79qk" 56 | ] 57 | ], 58 | "content": "", 59 | "sig": "95984f4403a4f414436270584a2ea1e3b83c988a0794ed8438f7d214714de3d982edf5eaf494362333ee07b2ce49c64c1d1206ec3914e9c7d7d4bf958d8bbfad" 60 | } 61 | ``` 62 | 63 | ## Deterministic Ephemeral Keys 64 | 65 | Ephemeral keys can be generated deterministically by generating a private key like so: 66 | 67 | ```swift 68 | func generate_private_keypair(our_privkey: Privkey, id: NoteId, created_at: UInt32) -> FullKeypair? { 69 | let to_hash = our_privkey.hex() + id.hex() + String(created_at) 70 | guard let dat = to_hash.data(using: .utf8) else { 71 | return nil 72 | } 73 | let privkey_bytes = sha256(dat) 74 | let privkey = Privkey(privkey_bytes) 75 | guard let pubkey = privkey_to_pubkey(privkey: privkey) else { return nil } 76 | 77 | return FullKeypair(pubkey: pubkey, privkey: privkey) 78 | } 79 | ``` 80 | 81 | The private key is generated using the sha256 of the following data concatenated together: 82 | 83 | - our_privkey: is the user sending the notes private key 84 | - id: the target note that you are zapping's note id 85 | - created_at: the target note's created_at as a string 86 | 87 | This allows you to decode private zaps that you have sent yourself by attempting to generate a matching pubkey from public data on the target note 88 | 89 | Note that this will not work on the web, since NIP-07 does not support hashing the private key into other data. 90 | -------------------------------------------------------------------------------- /04.md: -------------------------------------------------------------------------------- 1 | DIP-04 2 | ====== 3 | 4 | # Quote Highlights 5 | 6 | This is an extension to [nip84] 7 | 8 | A `comment` tag may be added to create a quote highlight. This SHOULD be rendered like a quote repost with the highlight as the quoted note. 9 | 10 | This is to prevent the creation and multiple notes (highlight + kind 1) for a single highlight action, which looks bad in micro-blogging clients where these notes may appear in succession. 11 | 12 | p-tag mentions MUST have a `mention` attribute to distinguish it from authors and editors. 13 | 14 | r-tag urls MUST have a `mention` attribute to distinguish from the highlighted source url. The source url MUST have the `source` attribute. 15 | 16 | [nip84]: https://github.com/nostr-protocol/nips/blob/master/84.md 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Damus Implementation Possibilities 3 | ================================== 4 | 5 | The purpose of this repo is to document nostr specifications that damus 6 | implements that didn't quite reach agreement amoung other nostr client devs. 7 | These will always be backwards compatible changes that can be safely ignored by 8 | other clients (optimizations, etc) 9 | 10 | - [DIP-01: Image Metadata][dip1] 11 | - [DIP-02: Quote Reposts][dip2] 12 | - [DIP-03: Private Zaps][dip3] 13 | - [DIP-04: Quote Highlights][dip4] 14 | 15 | [dip1]: 01.md 16 | [dip2]: 02.md 17 | [dip3]: 03.md 18 | [dip4]: 04.md 19 | --------------------------------------------------------------------------------