nostr 0.4.0 โ†’ 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.adr-dir +1 -0
  3. data/.editorconfig +1 -1
  4. data/.rubocop.yml +24 -1
  5. data/.tool-versions +2 -1
  6. data/CHANGELOG.md +70 -1
  7. data/README.md +93 -228
  8. data/adr/0001-record-architecture-decisions.md +19 -0
  9. data/adr/0002-introduction-of-signature-class.md +27 -0
  10. data/docs/.gitignore +4 -0
  11. data/docs/.vitepress/config.mjs +114 -0
  12. data/docs/README.md +44 -0
  13. data/docs/api-examples.md +49 -0
  14. data/docs/bun.lockb +0 -0
  15. data/docs/common-use-cases/bech32-encoding-and-decoding-(NIP-19).md +190 -0
  16. data/docs/common-use-cases/signing-and-verifying-events.md +50 -0
  17. data/docs/common-use-cases/signing-and-verifying-messages.md +43 -0
  18. data/docs/core/client.md +108 -0
  19. data/docs/core/keys.md +136 -0
  20. data/docs/core/user.md +43 -0
  21. data/docs/events/contact-list.md +29 -0
  22. data/docs/events/encrypted-direct-message.md +28 -0
  23. data/docs/events/recommend-server.md +32 -0
  24. data/docs/events/set-metadata.md +20 -0
  25. data/docs/events/text-note.md +15 -0
  26. data/docs/events.md +11 -0
  27. data/docs/getting-started/installation.md +21 -0
  28. data/docs/getting-started/overview.md +171 -0
  29. data/docs/implemented-nips.md +9 -0
  30. data/docs/index.md +42 -0
  31. data/docs/markdown-examples.md +85 -0
  32. data/docs/package.json +12 -0
  33. data/docs/relays/connecting-to-a-relay.md +21 -0
  34. data/docs/relays/publishing-events.md +29 -0
  35. data/docs/relays/receiving-events.md +6 -0
  36. data/docs/subscriptions/creating-a-subscription.md +49 -0
  37. data/docs/subscriptions/deleting-a-subscription.md +10 -0
  38. data/docs/subscriptions/filtering-subscription-events.md +115 -0
  39. data/docs/subscriptions/updating-a-subscription.md +4 -0
  40. data/lib/nostr/bech32.rb +203 -0
  41. data/lib/nostr/client.rb +2 -1
  42. data/lib/nostr/crypto.rb +93 -13
  43. data/lib/nostr/errors/error.rb +7 -0
  44. data/lib/nostr/errors/invalid_hrp_error.rb +21 -0
  45. data/lib/nostr/errors/invalid_key_format_error.rb +20 -0
  46. data/lib/nostr/errors/invalid_key_length_error.rb +20 -0
  47. data/lib/nostr/errors/invalid_key_type_error.rb +18 -0
  48. data/lib/nostr/errors/invalid_signature_format_error.rb +18 -0
  49. data/lib/nostr/errors/invalid_signature_length_error.rb +18 -0
  50. data/lib/nostr/errors/invalid_signature_type_error.rb +16 -0
  51. data/lib/nostr/errors/key_validation_error.rb +6 -0
  52. data/lib/nostr/errors/signature_validation_error.rb +6 -0
  53. data/lib/nostr/errors.rb +12 -0
  54. data/lib/nostr/event.rb +40 -13
  55. data/lib/nostr/event_kind.rb +1 -0
  56. data/lib/nostr/events/encrypted_direct_message.rb +8 -7
  57. data/lib/nostr/filter.rb +14 -11
  58. data/lib/nostr/key.rb +100 -0
  59. data/lib/nostr/key_pair.rb +54 -6
  60. data/lib/nostr/keygen.rb +44 -5
  61. data/lib/nostr/private_key.rb +36 -0
  62. data/lib/nostr/public_key.rb +36 -0
  63. data/lib/nostr/relay_message_type.rb +18 -0
  64. data/lib/nostr/signature.rb +67 -0
  65. data/lib/nostr/subscription.rb +2 -2
  66. data/lib/nostr/user.rb +17 -8
  67. data/lib/nostr/version.rb +1 -1
  68. data/lib/nostr.rb +7 -0
  69. data/nostr.gemspec +13 -13
  70. data/sig/nostr/bech32.rbs +14 -0
  71. data/sig/nostr/client.rbs +5 -5
  72. data/sig/nostr/crypto.rbs +8 -5
  73. data/sig/nostr/errors/error.rbs +4 -0
  74. data/sig/nostr/errors/invalid_hrb_error.rbs +6 -0
  75. data/sig/nostr/errors/invalid_key_format_error.rbs +5 -0
  76. data/sig/nostr/errors/invalid_key_length_error.rbs +5 -0
  77. data/sig/nostr/errors/invalid_key_type_error.rbs +5 -0
  78. data/sig/nostr/errors/invalid_signature_format_error.rbs +5 -0
  79. data/sig/nostr/errors/invalid_signature_length_error.rbs +5 -0
  80. data/sig/nostr/errors/invalid_signature_type_error.rbs +5 -0
  81. data/sig/nostr/errors/key_validation_error.rbs +4 -0
  82. data/sig/nostr/errors/signature_validation_error.rbs +4 -0
  83. data/sig/nostr/event.rbs +11 -10
  84. data/sig/nostr/events/encrypted_direct_message.rbs +2 -2
  85. data/sig/nostr/filter.rbs +3 -12
  86. data/sig/nostr/key.rbs +16 -0
  87. data/sig/nostr/key_pair.rbs +8 -3
  88. data/sig/nostr/keygen.rbs +5 -2
  89. data/sig/nostr/private_key.rbs +4 -0
  90. data/sig/nostr/public_key.rbs +4 -0
  91. data/sig/nostr/relay_message_type.rbs +8 -0
  92. data/sig/nostr/signature.rbs +14 -0
  93. data/sig/nostr/user.rbs +4 -8
  94. data/sig/vendor/bech32/nostr/entity.rbs +41 -0
  95. data/sig/vendor/bech32/nostr/nip19.rbs +20 -0
  96. data/sig/vendor/bech32/segwit_addr.rbs +21 -0
  97. data/sig/vendor/bech32.rbs +25 -0
  98. data/sig/vendor/event_emitter.rbs +10 -3
  99. data/sig/vendor/event_machine/channel.rbs +1 -1
  100. data/sig/vendor/faye/websocket/api.rbs +45 -0
  101. data/sig/vendor/faye/websocket/client.rbs +43 -0
  102. data/sig/vendor/faye/websocket.rbs +30 -0
  103. data/sig/vendor/schnorr/signature.rbs +16 -0
  104. data/sig/vendor/schnorr.rbs +3 -1
  105. metadata +102 -28
data/docs/core/keys.md ADDED
@@ -0,0 +1,136 @@
1
+ # Keys
2
+
3
+ To [sign events](#signing-an-event), you need a **private key**. To verify signatures, you need a **public key**. The combination of a
4
+ private and a public key is called a **keypair**.
5
+
6
+ Both public and private keys are 64-character hexadecimal strings. They can be represented in bech32 format,
7
+ which is a human-readable format that starts with `nsec` for private keys and `npub` for public keys.
8
+
9
+ There are a few ways to generate a keypair.
10
+
11
+ ## a) Generating a keypair
12
+
13
+ If you don't have any keys, you can generate a keypair using the
14
+ [`Nostr::Keygen`](https://www.rubydoc.info/gems/nostr/Nostr/Keygen) class:
15
+
16
+ ```ruby
17
+ keygen = Nostr::Keygen.new
18
+ keypair = keygen.generate_key_pair
19
+
20
+ keypair.private_key # => '67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa'
21
+ keypair.public_key # => '7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e'
22
+ ```
23
+
24
+ ## b) Generating a private key and a public key
25
+
26
+ Alternatively, if you have already generated a private key, you can extract the corresponding public key by calling
27
+ `Keygen#extract_public_key`:
28
+
29
+ ```ruby
30
+ keygen = Nostr::Keygen.new
31
+
32
+ private_key = keygen.generate_private_key
33
+ public_key = keygen.extract_public_key(private_key)
34
+
35
+ private_key # => '67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa'
36
+ public_key # => '7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e'
37
+ ```
38
+
39
+ ## c) Using existing hexadecimal keys
40
+
41
+ If you already have a private key and a public key in hexadecimal format, you can create a keypair using the
42
+ `Nostr::KeyPair` class:
43
+
44
+ ```ruby
45
+ keypair = Nostr::KeyPair.new(
46
+ private_key: Nostr::PrivateKey.new('67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa'),
47
+ public_key: Nostr::PublicKey.new('7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e'),
48
+ )
49
+
50
+ keypair.private_key # => '67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa'
51
+ keypair.public_key # => '7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e'
52
+ ```
53
+
54
+ ### d) Use existing bech32 keys
55
+
56
+ If you already have a private key and a public key in bech32 format, you can create a keypair using the
57
+ `Nostr::KeyPair` class:
58
+
59
+ ```ruby
60
+ keypair = Nostr::KeyPair.new(
61
+ private_key: Nostr::PrivateKey.from_bech32('nsec1vl029mgpspedva04g90vltkh6fvh240zqtv9k0t9af8935ke9laqsnlfe5'),
62
+ public_key: Nostr::PublicKey.from_bech32('npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg'),
63
+ )
64
+
65
+ keypair.private_key # => '67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa'
66
+ keypair.public_key # => '7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e'
67
+ ```
68
+
69
+ ## e) Using an existing hexadecimal private key
70
+
71
+ If you already have a private key in hexadecimal format, you can create a keypair using the method
72
+ [`Nostr::Keygen#get_key_pair_from_private_key`](https://www.rubydoc.info/gems/nostr/Nostr/Keygen#get_key_pair_from_private_key-instance_method):
73
+
74
+ ```ruby
75
+ private_key = Nostr::PrivateKey.new('67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa')
76
+
77
+ keygen= Nostr::Keygen.new
78
+ keypair = keygen.get_key_pair_from_private_key(private_key)
79
+
80
+ keypair.private_key # => '67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa'
81
+ keypair.public_key # => '7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e'
82
+ ```
83
+
84
+ ## f) Using an existing bech32 private key
85
+
86
+ If you already have a private key in bech32 format, you can create a keypair using the methods
87
+ [`Nostr::PrivateKey.from_bech32`](https://www.rubydoc.info/gems/nostr/Nostr/PrivateKey.from_bech32-class_method) and
88
+ [`Nostr::Keygen#get_key_pair_from_private_key`](https://www.rubydoc.info/gems/nostr/Nostr/Keygen#get_key_pair_from_private_key-instance_method):
89
+
90
+ ```ruby
91
+ private_key = Nostr::PrivateKey.from_bech32('nsec1vl029mgpspedva04g90vltkh6fvh240zqtv9k0t9af8935ke9laqsnlfe5')
92
+
93
+ keygen= Nostr::Keygen.new
94
+ keypair = keygen.get_key_pair_from_private_key(private_key)
95
+
96
+ keypair.private_key # => '67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa'
97
+ keypair.public_key # => '7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e'
98
+ ```
99
+
100
+ ## Signing an event
101
+
102
+ KeyPairs are used to sign [events](../events). To create a signed event, you need to instantiate a
103
+ [`Nostr::User`](https://www.rubydoc.info/gems/nostr/Nostr/User) with a keypair:
104
+
105
+ ```ruby{8,11-14}
106
+ # Use an existing keypair
107
+ keypair = Nostr::KeyPair.new(
108
+ private_key: Nostr::PrivateKey.new('67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa'),
109
+ public_key: Nostr::PublicKey.new('7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e'),
110
+ )
111
+
112
+ # Add the keypair to a user
113
+ user = Nostr::User.new(keypair: keypair)
114
+
115
+ # Create signed events
116
+ text_note = user.create_event(
117
+ kind: Nostr::EventKind::TEXT_NOTE,
118
+ content: 'Your feedback is appreciated, now pay $8'
119
+ )
120
+ ```
121
+
122
+ ::: details Click me to view the text_note
123
+
124
+ ```ruby
125
+ # text_note.to_h
126
+ {
127
+ id: '030fbc71151379e5b58e7428ed6e7f2884e5dfc9087fd64d1dc4cc677f5097c8',
128
+ pubkey: '7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e', # from the keypair
129
+ created_at: 1700119819,
130
+ kind: 1, # Nostr::EventKind::TEXT_NOTE,
131
+ tags: [],
132
+ content: 'Your feedback is appreciated, now pay $8',
133
+ sig: '586877896ef6f7d54fa4dd2ade04e3fdc4dfcd6166dd0df696b3c3c768868c0b690338f5baed6ab4fc717785333cb487363384de9fb0f740ac4775522cb4acb3' # signed with the private key from the keypair
134
+ }
135
+ ```
136
+ :::
data/docs/core/user.md ADDED
@@ -0,0 +1,43 @@
1
+ # User
2
+
3
+ The class [`Nostr::User`](https://www.rubydoc.info/gems/nostr/Nostr/User) is an abstraction to facilitate the creation
4
+ of signed events. It is not required to use it to create events, but it is recommended.
5
+
6
+ Here's an example of how to create a signed event without the class `Nostr::User`:
7
+
8
+ ```ruby
9
+ event = Nostr::Event.new(
10
+ pubkey: keypair.public_key,
11
+ kind: Nostr::EventKind::TEXT_NOTE,
12
+ tags: [],
13
+ content: 'Your feedback is appreciated, now pay $8',
14
+ )
15
+ event.sign(keypair.private_key)
16
+ ```
17
+
18
+ ::: details Click me to view the event
19
+
20
+ ```ruby
21
+ # event.to_h
22
+ {
23
+ id: '5feb10973dbcf5f210cfc1f0aa338fee62bed6a29696a67957713599b9baf0eb',
24
+ pubkey: 'b9b9821074d1b60b8fb4a3983632af3ef9669f55b20d515bf982cda5c439ad61',
25
+ created_at: 1699847447,
26
+ kind: 1, # Nostr::EventKind::TEXT_NOTE,
27
+ tags: [],
28
+ content: 'Your feedback is appreciated, now pay $8',
29
+ sig: 'e30f2f08331f224e41a4099d16aefc780bf9f2d1191b71777e1e1789e6b51fdf7bb956f25d4ea9a152d1c66717a9d68c081ce6c89c298c3c5e794914013381ab'
30
+ }
31
+ ```
32
+ :::
33
+
34
+ And here's how to create it with the class `Nostr::User`:
35
+
36
+ ```ruby
37
+ user = Nostr::User.new(keypair: keypair)
38
+
39
+ event = user.create_event(
40
+ kind: Nostr::EventKind::TEXT_NOTE,
41
+ content: 'Your feedback is appreciated, now pay $8'
42
+ )
43
+ ```
@@ -0,0 +1,29 @@
1
+ # Contact List
2
+
3
+ ## Creating/updating your contact list
4
+
5
+ Every new contact list that gets published overwrites the past ones, so it should contain all entries.
6
+
7
+ ```ruby
8
+ # Creating a contact list event with 2 contacts
9
+ update_contacts_event = user.create_event(
10
+ kind: Nostr::EventKind::CONTACT_LIST,
11
+ tags: [
12
+ [
13
+ "p", # mandatory
14
+ "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245", # public key of the user to add to the contacts
15
+ "wss://alicerelay.com/", # can be an empty string or can be omitted
16
+ "alice" # can be an empty string or can be omitted
17
+ ],
18
+ [
19
+ "p", # mandatory
20
+ "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681", # public key of the user to add to the contacts
21
+ "wss://bobrelay.com/nostr", # can be an empty string or can be omitted
22
+ "bob" # can be an empty string or can be omitted
23
+ ],
24
+ ],
25
+ )
26
+
27
+ # Send it to the Relay
28
+ client.publish(update_contacts_event)
29
+ ```
@@ -0,0 +1,28 @@
1
+ # Encrypted Direct Message
2
+
3
+ ## Sending an encrypted direct message
4
+
5
+ ```ruby
6
+ sender_private_key = '3185a47e3802f956ca5a2b4ea606c1d51c7610f239617e8f0f218d55bdf2b757'
7
+
8
+ encrypted_direct_message = Nostr::Events::EncryptedDirectMessage.new(
9
+ sender_private_key: sender_private_key,
10
+ recipient_public_key: '6c31422248998e300a1a457167565da7d15d0da96651296ee2791c29c11b6aa0',
11
+ plain_text: 'Your feedback is appreciated, now pay $8',
12
+ previous_direct_message: 'ccf9fdf3e1466d7c20969c71ec98defcf5f54aee088513e1b73ccb7bd770d460' # optional
13
+ )
14
+
15
+ encrypted_direct_message.sign(sender_private_key)
16
+
17
+ # #<Nostr::Events::EncryptedDirectMessage:0x0000000104c9fa68
18
+ # @content="mjIFNo1sSP3KROE6QqhWnPSGAZRCuK7Np9X+88HSVSwwtFyiZ35msmEVoFgRpKx4?iv=YckChfS2oWCGpMt1uQ4GbQ==",
19
+ # @created_at=1676456512,
20
+ # @id="daac98826d5eb29f7c013b6160986c4baf4fe6d4b995df67c1b480fab1839a9b",
21
+ # @kind=4,
22
+ # @pubkey="8a9d69c56e3c691bec8f9565e4dcbe38ae1d88fffeec3ce66b9f47558a3aa8ca",
23
+ # @sig="028bb5f5bab0396e2065000c84a4bcce99e68b1a79bb1b91a84311546f49c5b67570b48d4a328a1827e7a8419d74451347d4f55011a196e71edab31aa3d6bdac",
24
+ # @tags=[["p", "6c31422248998e300a1a457167565da7d15d0da96651296ee2791c29c11b6aa0"], ["e", "ccf9fdf3e1466d7c20969c71ec98defcf5f54aee088513e1b73ccb7bd770d460"]]>
25
+
26
+ # Send it to the Relay
27
+ client.publish(encrypted_direct_message)
28
+ ```
@@ -0,0 +1,32 @@
1
+ # Recommend Server
2
+
3
+ The `Recommend Server` event, has a set of tags with the following structure `['e', <event-id>, <relay-url>, <marker>]`
4
+
5
+ Where:
6
+
7
+ - `<event-id>` is the id of the event being referenced.
8
+ - `<relay-url>` is the URL of a recommended relay associated with the reference. Clients SHOULD add a valid `<relay-URL>`
9
+ field, but may instead leave it as `''`.
10
+ - `<marker>` is optional and if present is one of `'reply'`, `'root'`, or `'mention'`.
11
+ Those marked with `'reply'` denote the id of the reply event being responded to. Those marked with `'root'` denote the
12
+ root id of the reply thread being responded to. For top level replies (those replying directly to the root event),
13
+ only the `'root'` marker should be used. Those marked with `'mention'` denote a quoted or reposted event id.
14
+
15
+ A direct reply to the root of a thread should have a single marked `'e'` tag of type `'root'`.
16
+
17
+ ## Recommending a server
18
+
19
+ ```ruby
20
+ recommend_server_event = user.create_event(
21
+ kind: Nostr::EventKind::RECOMMEND_SERVER,
22
+ tags: [
23
+ [
24
+ 'e',
25
+ '461544014d87c9eaf3e76e021240007dff2c7afb356319f99c741b45749bf82f',
26
+ 'wss://relay.damus.io'
27
+ ],
28
+ ]
29
+ )
30
+
31
+ client.publish(recommend_server_event)
32
+ ```
@@ -0,0 +1,20 @@
1
+ # Set Metadata
2
+
3
+ In the `Metadata` event, the `content` is set to a stringified JSON object
4
+ `{name: <username>, about: <string>, picture: <url, string>}` describing the [user](../core/user) who created the event. A relay may
5
+ delete older events once it gets a new one for the same pubkey.
6
+
7
+ ## Setting the user's metadata
8
+
9
+ ```ruby
10
+ metadata_event = user.create_event(
11
+ kind: Nostr::EventKind::SET_METADATA,
12
+ content: {
13
+ name: 'Wilson Silva',
14
+ about: 'Used to make hydrochloric acid bombs in high school.',
15
+ picture: 'https://thispersondoesnotexist.com/'
16
+ }
17
+ )
18
+
19
+ client.publish(metadata_event)
20
+ ```
@@ -0,0 +1,15 @@
1
+ # Text Note
2
+
3
+ In the `Text Note` event, the `content` is set to the plaintext content of a note (anything the user wants to say).
4
+ Content that must be parsed, such as Markdown and HTML, should not be used.
5
+
6
+ ## Sending a text note event
7
+
8
+ ```ruby
9
+ text_note_event = user.create_event(
10
+ kind: Nostr::EventKind::TEXT_NOTE,
11
+ content: 'Your feedback is appreciated, now pay $8'
12
+ )
13
+
14
+ client.publish(text_note_event)
15
+ ```
data/docs/events.md ADDED
@@ -0,0 +1,11 @@
1
+ # Events
2
+
3
+ ## Event Kinds
4
+
5
+ | kind | description | NIP |
6
+ | ------------- |----------------------------------------------------------------| -------------------------------------------------------------- |
7
+ | `0` | [Metadata](./events/set-metadata) | [1](https://github.com/nostr-protocol/nips/blob/master/01.md) |
8
+ | `1` | [Short Text Note](./events/text-note) | [1](https://github.com/nostr-protocol/nips/blob/master/01.md) |
9
+ | `2` | [Recommend Relay](./events/recommend-server) | |
10
+ | `3` | [Contacts](./events/contact-list) | [2](https://github.com/nostr-protocol/nips/blob/master/02.md) |
11
+ | `4` | [Encrypted Direct Messages](./events/encrypted-direct-message) | [4](https://github.com/nostr-protocol/nips/blob/master/04.md) |
@@ -0,0 +1,21 @@
1
+ # Installation
2
+
3
+ Install the gem and add to the application's Gemfile by executing:
4
+
5
+ ```shell
6
+ bundle add nostr
7
+ ```
8
+
9
+ If bundler is not being used to manage dependencies, install the gem by executing:
10
+
11
+ ```shell
12
+ gem install nostr
13
+ ```
14
+
15
+ ## Requiring the gem
16
+
17
+ All examples in this guide assume that the gem has been required:
18
+
19
+ ```ruby
20
+ require 'nostr'
21
+ ```
@@ -0,0 +1,171 @@
1
+ ---
2
+ editLink: true
3
+ ---
4
+
5
+ # Getting started
6
+
7
+ This gem abstracts the complexity that you would face when trying to connect to relays web sockets, send and receive
8
+ events, handle events callbacks and much more.
9
+
10
+ ## Visual overview
11
+
12
+ Begin your journey with an overview of the essential functions. A visual representation below maps out the key
13
+ components we'll delve into in this section.
14
+
15
+ ```mermaid
16
+ classDiagram
17
+ class Client {
18
+ connect(relay)
19
+ publish(event)
20
+ subscribe(subscription_id, filter)
21
+ unsubscribe(subscription_id)
22
+ }
23
+ class Relay {
24
+ url
25
+ name
26
+ }
27
+ class Event {
28
+ pubkey
29
+ created_at
30
+ kind
31
+ tags
32
+ content
33
+ add_event_reference(event_id)
34
+ add_pubkey_reference(pubkey)
35
+ serialize()
36
+ to_h()
37
+ sign(private_key)
38
+ verify_signature()
39
+ }
40
+ class Subscription {
41
+ id
42
+ filter
43
+ }
44
+ class Filter {
45
+ ids
46
+ authors
47
+ kinds
48
+ since
49
+ until
50
+ limit
51
+ to_h()
52
+ }
53
+ class EventKind {
54
+ <<Enumeration>>
55
+ SET_METADATA
56
+ TEXT_NOTE
57
+ RECOMMEND_SERVER
58
+ CONTACT_LIST
59
+ ENCRYPTED_DIRECT_MESSAGE
60
+ }
61
+ class KeyPair {
62
+ private_key
63
+ public_key
64
+ }
65
+ class Keygen {
66
+ generate_key_pair()
67
+ generate_private_key()
68
+ extract_public_key(private_key)
69
+ }
70
+ class User {
71
+ keypair
72
+ create_event(event_attributes)
73
+ }
74
+
75
+ Client --> Relay : connects via <br> WebSockets to
76
+ Client --> Event : uses WebSockets to <br> publish and receive
77
+ Client --> Subscription : receives Events via
78
+ Subscription --> Filter : uses
79
+ Event --> EventKind : is of kind
80
+ User --> KeyPair : has
81
+ User --> Event : creates and signs
82
+ User --> Keygen : uses to generate keys
83
+ Keygen --> KeyPair : generates
84
+ ```
85
+
86
+ ## Code overview
87
+
88
+ Explore the provided code snippet to learn about initializing the Nostr [client](../core/client.md), generating
89
+ a [keypair](../core/keys), [publishing](../relays/publishing-events) an event, and
90
+ efficiently [managing event subscriptions](../subscriptions/creating-a-subscription) (including event reception,
91
+ filtering, and WebSocket event handling).
92
+
93
+ ```ruby
94
+ # Require the gem
95
+ require 'nostr'
96
+
97
+ # Instantiate a client
98
+ client = Nostr::Client.new
99
+
100
+ # a) Use an existing keypair
101
+ keypair = Nostr::KeyPair.new(
102
+ private_key: Nostr::PrivateKey.new('your-hex-private-key'),
103
+ public_key: Nostr::PublicKey.new('your-hex-public-key'),
104
+ )
105
+
106
+ # b) Or build a keypair from a private key
107
+ keygen = Nostr::Keygen.new
108
+ keypair = keygen.get_key_pair_from_private_key(
109
+ Nostr::PrivateKey.new('your-hex-private-key')
110
+ )
111
+
112
+ # c) Or create a new keypair
113
+ keygen = Nostr::Keygen.new
114
+ keypair = keygen.generate_key_pair
115
+
116
+ # Create a user with the keypair
117
+ user = Nostr::User.new(keypair: keypair)
118
+
119
+ # Create a signed event
120
+ text_note_event = user.create_event(
121
+ kind: Nostr::EventKind::TEXT_NOTE,
122
+ content: 'Your feedback is appreciated, now pay $8'
123
+ )
124
+
125
+ # Connect asynchronously to a relay
126
+ relay = Nostr::Relay.new(url: 'wss://nostr.wine', name: 'Wine')
127
+ client.connect(relay)
128
+
129
+ # Listen asynchronously for the connect event
130
+ client.on :connect do
131
+ # Send the event to the Relay
132
+ client.publish(text_note_event)
133
+
134
+ # Create a filter to receive the first 20 text notes
135
+ # and encrypted direct messages from the relay that
136
+ # were created in the previous hour
137
+ filter = Nostr::Filter.new(
138
+ kinds: [
139
+ Nostr::EventKind::TEXT_NOTE,
140
+ Nostr::EventKind::ENCRYPTED_DIRECT_MESSAGE
141
+ ],
142
+ since: Time.now.to_i - 3600, # 1 hour ago
143
+ until: Time.now.to_i,
144
+ limit: 20,
145
+ )
146
+
147
+ # Subscribe to events matching conditions of a filter
148
+ subscription = client.subscribe(filter: filter)
149
+
150
+ # Unsubscribe from events matching the filter above
151
+ client.unsubscribe(subscription.id)
152
+ end
153
+
154
+ # Listen for incoming messages and print them
155
+ client.on :message do |message|
156
+ puts message
157
+ end
158
+
159
+ # Listen for error messages
160
+ client.on :error do |error_message|
161
+ # Handle the error
162
+ end
163
+
164
+ # Listen for the close event
165
+ client.on :close do |code, reason|
166
+ # You may attempt to reconnect to the relay here
167
+ end
168
+ ```
169
+
170
+ Beyond what's covered here, the Nostr protocol and this gem boast a wealth of additional functionalities. For an
171
+ in-depth exploration of these capabilities, proceed to the next page.
@@ -0,0 +1,9 @@
1
+ # Implemented NIPs
2
+
3
+ NIPs stand for Nostr Implementation Possibilities. They exist to document what may be implemented by Nostr-compatible
4
+ relay and client software.
5
+
6
+ - [NIP-01: Basic protocol flow description](https://github.com/nostr-protocol/nips/blob/master/01.md)
7
+ - [NIP-02: Contact List and Petnames](https://github.com/nostr-protocol/nips/blob/master/02.md)
8
+ - [NIP-04: Encrypted Direct Message](https://github.com/nostr-protocol/nips/blob/master/04.md)
9
+ - [NIP-19: Bech32-encoded entities](https://github.com/nostr-protocol/nips/blob/master/19.md)
data/docs/index.md ADDED
@@ -0,0 +1,42 @@
1
+ ---
2
+ # https://vitepress.dev/reference/default-theme-home-page
3
+ layout: home
4
+
5
+ hero:
6
+ name: "Nostr"
7
+ text: "Ruby"
8
+ tagline: "The Nostr protocol implemented in a Ruby gem."
9
+ actions:
10
+ - theme: brand
11
+ text: Getting Started
12
+ link: /getting-started/overview
13
+ - theme: alt
14
+ text: View on Github
15
+ link: https://github.com/wilsonsilva/nostr
16
+ - theme: alt
17
+ text: View on RubyDoc
18
+ link: https://www.rubydoc.info/gems/nostr
19
+ - theme: alt
20
+ text: View on RubyGems
21
+ link: https://rubygems.org/gems/nostr
22
+
23
+ features:
24
+ - title: Asynchronous
25
+ details: Non-blocking I/O for maximum performance.
26
+ icon: โšก
27
+ - title: Lightweight
28
+ details: Minimal runtime dependencies.
29
+ icon: ๐Ÿชถ
30
+ - title: Intuitive
31
+ details: The API mirrors the Nostr protocol specification domain language.
32
+ icon: ๐Ÿ’ก
33
+ - title: Fully documented
34
+ details: All code is documented from both the maintainer's as well as the consumer's perspective.
35
+ icon: ๐Ÿ“š
36
+ - title: Fully tested
37
+ details: All code is tested with 100% coverage.
38
+ icon: ๐Ÿงช
39
+ - title: Fully typed
40
+ details: All code is typed with <a href="https://rubygems.org/gems/rbs" target="_blank">RBS</a> with the help of <a href="https://rubygems.org/gems/typeprof" target="_blank">TypeProf</a>. Type correctness is enforced by <a href="https://rubygems.org/gems/steep" target="_blank">Steep</a>.
41
+ icon: โœ…
42
+ ---
@@ -0,0 +1,85 @@
1
+ # Markdown Extension Examples
2
+
3
+ This page demonstrates some of the built-in markdown extensions provided by VitePress.
4
+
5
+ ## Syntax Highlighting
6
+
7
+ VitePress provides Syntax Highlighting powered by [Shiki](https://github.com/shikijs/shiki), with additional features like line-highlighting:
8
+
9
+ **Input**
10
+
11
+ ````
12
+ ```js{4}
13
+ export default {
14
+ data () {
15
+ return {
16
+ msg: 'Highlighted!'
17
+ }
18
+ }
19
+ }
20
+ ```
21
+ ````
22
+
23
+ **Output**
24
+
25
+ ```js{4}
26
+ export default {
27
+ data () {
28
+ return {
29
+ msg: 'Highlighted!'
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ ## Custom Containers
36
+
37
+ **Input**
38
+
39
+ ```md
40
+ ::: info
41
+ This is an info box.
42
+ :::
43
+
44
+ ::: tip
45
+ This is a tip.
46
+ :::
47
+
48
+ ::: warning
49
+ This is a warning.
50
+ :::
51
+
52
+ ::: danger
53
+ This is a dangerous warning.
54
+ :::
55
+
56
+ ::: details
57
+ This is a details block.
58
+ :::
59
+ ```
60
+
61
+ **Output**
62
+
63
+ ::: info
64
+ This is an info box.
65
+ :::
66
+
67
+ ::: tip
68
+ This is a tip.
69
+ :::
70
+
71
+ ::: warning
72
+ This is a warning.
73
+ :::
74
+
75
+ ::: danger
76
+ This is a dangerous warning.
77
+ :::
78
+
79
+ ::: details
80
+ This is a details block.
81
+ :::
82
+
83
+ ## More
84
+
85
+ Check out the documentation for the [full list of markdown extensions](https://vitepress.dev/guide/markdown).
data/docs/package.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "scripts": {
3
+ "docs:dev": "vitepress dev",
4
+ "docs:build": "vitepress build",
5
+ "docs:preview": "vitepress preview"
6
+ },
7
+ "devDependencies": {
8
+ "mermaid": "^10.6.1",
9
+ "vitepress": "^1.0.0-rc.25",
10
+ "vitepress-plugin-mermaid": "^2.0.15"
11
+ }
12
+ }
@@ -0,0 +1,21 @@
1
+ # Connecting to a Relay
2
+
3
+ You must connect your nostr [Client](../core/client) to a relay in order to send and receive [Events](../events).
4
+ Instantiate a [`Nostr::Client`](https://www.rubydoc.info/gems/nostr/Nostr/Client) and a
5
+ [`Nostr::Relay`](https://www.rubydoc.info/gems/nostr/Nostr/Relay) giving it the `url` of your relay. The `name`
6
+ attribute is just descriptive.
7
+ Calling [`Client#connect`](https://www.rubydoc.info/gems/nostr/Nostr/Client#connect-instance_method) attempts to
8
+ establish a WebSocket connection between the Client and the Relay.
9
+
10
+ ```ruby
11
+ client = Nostr::Client.new
12
+ relay = Nostr::Relay.new(url: 'wss://relay.damus.io', name: 'Damus')
13
+
14
+ # Listen for the connect event
15
+ client.on :connect do
16
+ # When this block executes, you're connected to the relay
17
+ end
18
+
19
+ # Connect to a relay asynchronously
20
+ client.connect(relay)
21
+ ```