nostr 0.5.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.adr-dir +1 -0
  3. data/.rubocop.yml +3 -1
  4. data/.rubocop_todo.yml +1 -1
  5. data/.tool-versions +2 -2
  6. data/CHANGELOG.md +63 -1
  7. data/README.md +6 -2
  8. data/adr/0001-record-architecture-decisions.md +19 -0
  9. data/adr/0002-introduction-of-signature-class.md +27 -0
  10. data/adr/0003-logging-methods-vs-logger-class.md +122 -0
  11. data/adr/0004-default-logging-activation.md +66 -0
  12. data/adr/0005-logger-types.md +32 -0
  13. data/docs/.vitepress/config.mjs +3 -0
  14. data/docs/bun.lockb +0 -0
  15. data/docs/common-use-cases/logging-and-debugging.md +60 -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 +1 -1
  19. data/docs/getting-started/overview.md +5 -1
  20. data/docs/index.md +0 -2
  21. data/docs/package.json +3 -3
  22. data/lib/nostr/client/color_logger.rb +69 -0
  23. data/lib/nostr/client/logger.rb +85 -0
  24. data/lib/nostr/client/plain_logger.rb +69 -0
  25. data/lib/nostr/client.rb +32 -6
  26. data/lib/nostr/crypto.rb +82 -6
  27. data/lib/nostr/errors/invalid_key_format_error.rb +1 -1
  28. data/lib/nostr/errors/invalid_key_length_error.rb +1 -1
  29. data/lib/nostr/errors/invalid_key_type_error.rb +1 -1
  30. data/lib/nostr/errors/invalid_signature_format_error.rb +18 -0
  31. data/lib/nostr/errors/invalid_signature_length_error.rb +18 -0
  32. data/lib/nostr/errors/invalid_signature_type_error.rb +16 -0
  33. data/lib/nostr/errors/signature_validation_error.rb +6 -0
  34. data/lib/nostr/errors.rb +4 -0
  35. data/lib/nostr/event.rb +36 -9
  36. data/lib/nostr/event_kind.rb +1 -0
  37. data/lib/nostr/events/encrypted_direct_message.rb +4 -4
  38. data/lib/nostr/filter.rb +10 -7
  39. data/lib/nostr/key.rb +2 -2
  40. data/lib/nostr/key_pair.rb +26 -2
  41. data/lib/nostr/keygen.rb +1 -1
  42. data/lib/nostr/signature.rb +67 -0
  43. data/lib/nostr/version.rb +1 -1
  44. data/lib/nostr.rb +3 -0
  45. data/nostr.gemspec +10 -10
  46. data/sig/nostr/client/color_logger.rbs +6 -0
  47. data/sig/nostr/client/logger.rbs +12 -0
  48. data/sig/nostr/client/plain_logger.rbs +6 -0
  49. data/sig/nostr/client.rbs +2 -0
  50. data/sig/nostr/crypto.rbs +3 -0
  51. data/sig/nostr/errors/invalid_signature_format_error.rbs +5 -0
  52. data/sig/nostr/errors/invalid_signature_length_error.rbs +5 -0
  53. data/sig/nostr/errors/invalid_signature_type_error.rbs +5 -0
  54. data/sig/nostr/errors/signature_validation_error.rbs +4 -0
  55. data/sig/nostr/event.rbs +7 -6
  56. data/sig/nostr/key_pair.rbs +1 -0
  57. data/sig/nostr/signature.rbs +14 -0
  58. data/sig/vendor/event_emitter.rbs +8 -10
  59. data/sig/vendor/schnorr/signature.rbs +16 -0
  60. data/sig/vendor/schnorr.rbs +3 -1
  61. metadata +48 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 168cbde3fb029345d4fa10043eead7c194a57fe3ee23c12b76bbaf79b1bdc71d
4
- data.tar.gz: ff1a1837e897a8c0ad233a61ed4526fe3e4087c04474e54778a00a0d4313e361
3
+ metadata.gz: 707a302ca79f44bd669a84dc9086c5934bc6f753a78d90989f4d3619db5ed997
4
+ data.tar.gz: 81a83ff089c864ac5c77bc7d4b26a23755ff2573ee013f68137a79ed9351f140
5
5
  SHA512:
6
- metadata.gz: e5549507ded84026a2295c3914324e8c74aff3c1ae80322db3cd449e7f65c3475186e9daaa76f7a22241e33b6759c2073da5a31a910a2082c8e49a99081dc5bf
7
- data.tar.gz: 19f69ed10ae3ba42113992541912bb18a4aa3ad8239b582b996e7c7845c4624c4a9ee73d800f51fbd883d2bdd9ea603eeb807ade147bcfc22db7183c1db30ce6
6
+ metadata.gz: 69bf9a378add76760a6955967787602e1bf6e913f22c1194fa9b8d3dc661270b038f26010bb8181378b7d21586b6d5195fe12754007551baa78531b3e4663e16
7
+ data.tar.gz: a49b8eb1acc9978a1f95be74783253d44807078fc97af2255239067c77125a7a31661514e7196f27d18c76d57805c293c54d5cc0c9427cd792c1f0e65349a796
data/.adr-dir ADDED
@@ -0,0 +1 @@
1
+ adr
data/.rubocop.yml CHANGED
@@ -5,9 +5,11 @@ require:
5
5
  - rubocop-rspec
6
6
 
7
7
  AllCops:
8
- TargetRubyVersion: 3.2
8
+ TargetRubyVersion: 3.3
9
9
  DisplayCopNames: true
10
10
  NewCops: enable
11
+ Exclude:
12
+ - docs/**/*
11
13
 
12
14
  # ----------------------- Gemspec -----------------------
13
15
 
data/.rubocop_todo.yml CHANGED
@@ -9,7 +9,7 @@
9
9
  # Offense count: 2
10
10
  # Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods, CountRepeatedAttributes.
11
11
  Metrics/AbcSize:
12
- Max: 23
12
+ Max: 24
13
13
 
14
14
  # Offense count: 2
15
15
  # Configuration parameters: CountComments, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods.
data/.tool-versions CHANGED
@@ -1,2 +1,2 @@
1
- ruby 3.2.2
2
- bun 1.0.11
1
+ ruby 3.3.0
2
+ bun 1.1.3
data/CHANGELOG.md CHANGED
@@ -4,13 +4,73 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.1.1/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.7.0] 2024-04-13
8
+
9
+ ### Added
10
+
11
+ - Added the `Nostr::Client::Logger` class to log connection events, messages sent and received, errors, and connection
12
+ closures.
13
+ - Added the `Nostr::Client::ColorLogger` class to log events in color using ANSI escape codes.
14
+ - Added the `Nostr::Client::PlainLogger` class to log events without color coding.
15
+ - Added Architecture Decision Records (ADRs) to document the decision process for logging functionality.
16
+ - Added a new common use case document for logging and debugging.
17
+
18
+ ### Changed
19
+
20
+ - Updated the `Nostr::Client` class to use the `ColorLogger` by default for logging client interactions with relays.
21
+ - Updated the `Nostr::Client#connect` method to emit a `:send` event when sending a message.
22
+ - Updated the `Nostr::Client#on` method to pass the `relay` parameter to the `:connect` event handler.
23
+ - Updated bun to version `1.1.3` (was `1.0.30`).
24
+ - Updated the gem `json` to version `2.7` (was `2.6`).
25
+ - Updated the gem `mermaid` to version `10.9` (was `10.6`).
26
+ - Updated the gem `rubocop-rspec` to version `2.29` (was `2.27`).
27
+ - Updated the gem `steep` to version `1.7.dev3` (was `1.6`).
28
+ - Updated the gem `vitepress` to version `1.1` (was `1.0.0-rc.25`).
29
+ - Updated the gem `vitepress-plugin-mermaid` to version `2.0.16` (was `2.0.15`).
30
+ - Updated the error message in `Nostr::InvalidSignatureTypeError` to provide more detail.
31
+
32
+ ### Fixed
33
+
34
+ Fixed a type-checking issue in `Nostr::Event#verify_signature` by removing a workaround after the steep gem author,
35
+ [@soutaro](https://github.com/soutaro) resolved [the problem I reported](https://github.com/soutaro/steep/issues/1079).
36
+
37
+ ## [0.6.0] 2024-03-15
38
+
39
+ ### Added
40
+
41
+ - Added Architecture Decision Records (ADRs) to document architectural decisions
42
+ - Added the `Signature` class to fix the primitive obsession with signatures and to make it easier to work with them
43
+ - Added `valid_sig?` and `check_sig!` to the `Crypto` class to verify whether an event's signature is valid
44
+ - Added `sign_message` to the `Crypto` class to sign a message
45
+ - Added `verify_signature?` to the `Event` class to verify whether an event's signature is valid
46
+ - Added `#to_ary` to the `KeyPair` class to enable keypair destructuring
47
+ - Added RBS types for `schnorr`
48
+
49
+ ### Changed
50
+
51
+ - Updated the required Ruby version to `3.3.0` (was `3.2.0`)
52
+ - Updated the gem `dotenv` to version `3.1` (was `2.8`)
53
+ - Updated the gem `bip-schnorr` to version `0.7` (was `0.6`)
54
+ - Updated the gem `overcommit` to version `0.63` (was `0.59`)
55
+ - Updated the gem `rbs` to version `3.4` (was `3.3`)
56
+ - Updated the gem `rspec` to version `3.13` (was `3.12`)
57
+ - Updated the gem `rspec-rubocop` to version `2.27` (was `2.25`)
58
+ - Updated the gem `rubocop` to version `1.62` (was `1.57`)
59
+
60
+ ### Fixed
61
+
62
+ - Fixed a typo in the README (`generate_keypair` -> `generate_key_pair`)
63
+ - Fixed a typo in the YARD documentation of `Nostr::Key#initialize` (`ValidationError` -> `KeyValidationError`)
64
+ - Fixed YARD example rendering issues in `InvalidKeyFormatError#initialize`, `InvalidKeyLengthError#initialize`,
65
+ `InvalidKeyTypeError#initialize`, `Event#initialize`, `EncryptedDirectMessage#initialize` and `Filter#to_h`
66
+
7
67
  ## [0.5.0] 2023-11-20
8
68
 
9
69
  ### Added
10
70
 
11
71
  - Added relay message type enums `Nostr::RelayMessageType`
12
72
  - Compliance with [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md) - bech32-formatted strings
13
- - `Nostr::PrivateKey` and `Nostr::PublicKey` to represent private and public keys, respectively
73
+ - Added `Nostr::PrivateKey` and `Nostr::PublicKey` to represent private and public keys, respectively
14
74
  - Added a validation of private and public keys
15
75
  - Added an ability to convert keys to and from Bech32 format
16
76
  - Added RBS types for `faye-websocket` and `bech32`
@@ -82,6 +142,8 @@ principles of immutability and was a major source of internal complexity as I ne
82
142
 
83
143
  - Initial release
84
144
 
145
+ [0.7.0]: https://github.com/wilsonsilva/nostr/compare/v0.6.0...v0.7.0
146
+ [0.6.0]: https://github.com/wilsonsilva/nostr/compare/v0.5.0...v0.6.0
85
147
  [0.5.0]: https://github.com/wilsonsilva/nostr/compare/v0.4.0...v0.5.0
86
148
  [0.4.0]: https://github.com/wilsonsilva/nostr/compare/v0.3.0...v0.4.0
87
149
  [0.3.0]: https://github.com/wilsonsilva/nostr/compare/v0.2.0...v0.3.0
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Nostr
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/nostr.svg)](https://badge.fury.io/rb/nostr)
4
+ ![Build](https://github.com/wilsonsilva/nostr/actions/workflows/main.yml/badge.svg)
4
5
  [![Maintainability](https://api.codeclimate.com/v1/badges/c7633eb2c89eb95ee7f2/maintainability)](https://codeclimate.com/github/wilsonsilva/nostr/maintainability)
5
6
  [![Test Coverage](https://api.codeclimate.com/v1/badges/c7633eb2c89eb95ee7f2/test_coverage)](https://codeclimate.com/github/wilsonsilva/nostr/test_coverage)
6
7
 
@@ -63,7 +64,7 @@ keypair = keygen.get_key_pair_from_private_key(
63
64
 
64
65
  # c) Or create a new keypair
65
66
  keygen = Nostr::Keygen.new
66
- keypair = keygen.generate_keypair
67
+ keypair = keygen.generate_key_pair
67
68
 
68
69
  # Create a user with the keypair
69
70
  user = Nostr::User.new(keypair: keypair)
@@ -79,7 +80,7 @@ relay = Nostr::Relay.new(url: 'wss://nostr.wine', name: 'Wine')
79
80
  client.connect(relay)
80
81
 
81
82
  # Listen asynchronously for the connect event
82
- client.on :connect do
83
+ client.on :connect do |relay|
83
84
  # Send the event to the Relay
84
85
  client.publish(text_note_event)
85
86
 
@@ -117,6 +118,9 @@ end
117
118
  client.on :close do |code, reason|
118
119
  # You may attempt to reconnect to the relay here
119
120
  end
121
+
122
+ # This line keeps the background client from exiting immediately.
123
+ gets
120
124
  ```
121
125
 
122
126
  ## 📚 Documentation
@@ -0,0 +1,19 @@
1
+ # 1. Record architecture decisions
2
+
3
+ Date: 2024-03-13
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ We need to record the architectural decisions made on this project.
12
+
13
+ ## Decision
14
+
15
+ We will use Architecture Decision Records, as [described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions).
16
+
17
+ ## Consequences
18
+
19
+ See Michael Nygard's article, linked above. For a lightweight ADR toolset, see Nat Pryce's [adr-tools](https://github.com/npryce/adr-tools).
@@ -0,0 +1,27 @@
1
+ # 2. introduction-of-signature-class
2
+
3
+ Date: 2024-03-14
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ I noticed significant overuse of primitive strings for signatures, which led to widespread and repetitive validation logic, increasing the potential for errors and making the system harder to manage and maintain.
12
+
13
+ ## Decision
14
+
15
+ I introduced the Nostr::Signature class, choosing to subclass String to leverage string-like behavior while embedding specific validation rules for signatures. This move was aimed at streamlining validation, ensuring consistency, and maintaining the usability of strings.
16
+
17
+ ## Consequences
18
+
19
+ ### Positive
20
+
21
+ - This design choice has made the codebase cleaner and more robust, reducing the chances of errors related to signature handling. It ensures that all signature instances are valid at creation, leveraging the familiarity and flexibility of string operations without sacrificing the integrity of the data. Moreover, it sets a strong foundation for extending signature-related functionality in the future.
22
+
23
+ ### Negative
24
+
25
+ - __Performance Concerns:__ Subclassing String might introduce slight performance overheads due to the additional validation logic executed upon instantiation of a Signature object.
26
+ - __Integration Challenges:__ Integrating this class into existing systems where strings were used indiscriminately for signatures requires careful refactoring to ensure compatibility. There's also the potential for issues when passing Nostr::Signature objects to libraries or APIs expecting plain strings without the additional constraints.
27
+ - __Learning Curve:__ For new team members or contributors, understanding the necessity and functionality of the Nostr::Signature class adds to the learning curve, potentially slowing down initial development efforts as they familiarize themselves with the custom implementation.
@@ -0,0 +1,122 @@
1
+ # 3. Logging methods vs logger class
2
+
3
+ Date: 2024-03-19
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ I'm deciding between integrating logging directly into the main class or creating a dedicated logger class.
12
+
13
+ ### Option 1: Logging methods
14
+
15
+ The first approach weaves logging actions into the operational code, resulting in a tight coupling of functionality and
16
+ logging. Classes should be open for extension but closed for modification, and this strategy violates that principle.
17
+
18
+ ```ruby
19
+ class Client
20
+ def connect(relay)
21
+ execute_within_an_em_thread do
22
+ client = build_websocket_client(relay.url)
23
+ parent_to_child_channel.subscribe do |msg|
24
+ client.send(msg)
25
+ emit(:send, msg)
26
+ log_send(msg) # <------ new code
27
+ end
28
+
29
+ client.on :open do
30
+ child_to_parent_channel.push(type: :open, relay:)
31
+ log_connection_opened(relay) # <------ new code
32
+ end
33
+
34
+ client.on :message do |event|
35
+ child_to_parent_channel.push(type: :message, data: event.data)
36
+ log_message_received(event.data) # <------ new code
37
+ end
38
+
39
+ client.on :error do |event|
40
+ child_to_parent_channel.push(type: :error, message: event.message)
41
+ log_error(event.message) # <------ new code
42
+ end
43
+
44
+ client.on :close do |event|
45
+ child_to_parent_channel.push(type: :close, code: event.code, reason: event.reason)
46
+ log_connection_closed(event.code, event.reason) # <------ new code
47
+ end
48
+ end
49
+
50
+ # ------ new code below ------
51
+
52
+ def log_send(msg)
53
+ logger.info("Message sent: #{msg}")
54
+ end
55
+
56
+ def log_connection_opened(relay)
57
+ logger.info("Connection opened to #{relay.url}")
58
+ end
59
+
60
+ def log_message_received(data)
61
+ logger.info("Message received: #{data}")
62
+ end
63
+
64
+ def log_error(message)
65
+ logger.error("Error: #{message}")
66
+ end
67
+
68
+ def log_connection_closed(code, reason)
69
+ logger.info("Connection closed with code: #{code}, reason: #{reason}")
70
+ end
71
+ end
72
+ end
73
+ ```
74
+
75
+ ### Option 2: Logger class
76
+
77
+ The second strategy separates logging into its own class, promoting cleaner code and adherence to the Single
78
+ Responsibility Principle. Client already exposes events that can be tapped into, so the logger class can listen to these
79
+ events and log accordingly.
80
+
81
+ ```ruby
82
+ class ClientLogger
83
+ def attach_to(client)
84
+ logger_instance = self
85
+
86
+ client.on(:connect) { |relay| logger_instance.on_connect(relay) }
87
+ client.on(:message) { |message| logger_instance.on_message(message) }
88
+ client.on(:send) { |message| logger_instance.on_send(message) }
89
+ client.on(:error) { |message| logger_instance.on_error(message) }
90
+ client.on(:close) { |code, reason| logger_instance.on_close(code, reason) }
91
+ end
92
+
93
+ def on_connect(relay); end
94
+ def on_message(message); end
95
+ def on_send(message); end
96
+ def on_error(message); end
97
+ def on_close(code, reason); end
98
+ end
99
+
100
+ client = Nostr::Client.new
101
+ logger = Nostr::ClientLogger.new
102
+ logger.attach_to(client)
103
+ ```
104
+
105
+ This approach decouples logging from the main class, making it easier to maintain and extend the logging system without
106
+ affecting the core logic.
107
+
108
+ ## Decision
109
+
110
+ I've chosen the dedicated logger class route. This choice is driven by a desire for extensibility in the logging system.
111
+ With a separate logger, I can easily modify logging behavior—like changing formats, adjusting verbosity levels,
112
+ switching colors, or altering output destinations (files, networks, etc.) — without needing to rewrite any of the main
113
+ operational code.
114
+
115
+ ## Consequences
116
+
117
+ Adopting a dedicated logger class offers greater flexibility and simplifies maintenance, making it straightforward to
118
+ adjust how and what I log independently of the core logic. This separation of concerns means that any future changes
119
+ to logging preferences or requirements can be implemented quickly and without risk to the main class's functionality.
120
+ However, it's important to manage the integration carefully to avoid introducing complexity, such as handling
121
+ dependencies and ensuring seamless communication between the main operations and the logging system.
122
+
@@ -0,0 +1,66 @@
1
+ # 3. Default Logging Activation
2
+
3
+ Date: 2024-03-19
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ Logging provides visibility into the gem's behavior and helps to diagnose issues. The decision centered on whether
12
+ to enable logging by default or require manual activation.
13
+
14
+ ### Option 1: On-demand Logging
15
+
16
+ ```ruby
17
+ client = Nostr::Client.new
18
+ logger = Nostr::ClientLogger.new
19
+ logger.attach_to(client)
20
+ ```
21
+
22
+ #### Advantages:
23
+
24
+ - Offers users flexibility and control over logging.
25
+ - Conserves resources by logging only when needed.
26
+
27
+ #### Disadvantages:
28
+
29
+ - Potential to miss critical logs if not enabled.
30
+ - Requires users to read and understand documentation to enable logging.
31
+ - Requires users to manually activate and configure logging.
32
+
33
+ ### Option 2: Automatic Logging
34
+
35
+ ```ruby
36
+ class Client
37
+ def initialize(logger: ClientLogger.new)
38
+ @logger = logger
39
+ logger&.attach_to(self)
40
+ end
41
+ end
42
+
43
+ client = Nostr::Client.new
44
+ ```
45
+
46
+ #### Advantages:
47
+
48
+ - Ensures comprehensive logging without user intervention.
49
+ - Simplifies debugging and monitoring.
50
+ - Balances logging detail with performance impact.
51
+
52
+ #### Disadvantages:
53
+
54
+ - Needs additional steps to disable logging.
55
+
56
+ ## Decision
57
+
58
+ Logging will be enabled by default. Users can disable logging by passing a `nil` logger to the client:
59
+
60
+ ```ruby
61
+ client = Nostr::Client.new(logger: nil)
62
+ ```
63
+
64
+ ## Consequences
65
+
66
+ Enabling logging by default favors ease of use and simplifies the developer's experience.
@@ -0,0 +1,32 @@
1
+ # 3. Logger Types
2
+
3
+ Date: 2024-04-01
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ In developing the `Nostr::Client` logging mechanism, I identified a need to cater to multiple development environments
12
+ and developer preferences. The consideration was whether to implement a singular logger type or to introduce
13
+ multiple, specialized loggers. The alternatives were:
14
+
15
+ - A single `ClientLogger` providing a one-size-fits-all solution.
16
+ - Multiple logger types:
17
+ - `Nostr::Client::Logger`: The base class for logging, providing core functionalities.
18
+ - `Nostr::Client::ColorLogger`: An extension of the base logger, introducing color-coded outputs for enhanced readability in environments that support ANSI colors.
19
+ - `Nostr::Client::PlainLogger`: A variation that produces logs without color coding, suitable for environments lacking color support or for users preferring plain text.
20
+
21
+ ## Decision
22
+
23
+ I decided to implement the latter option: three specific kinds of loggers (`Nostr::Client::Logger`,
24
+ `Nostr::Client::ColorLogger`, and `Nostr::Client::PlainLogger`). This approach is intended to offer flexibility and
25
+ cater to the varied preferences and requirements of our users, recognizing the diverse environments in which our
26
+ library might be used.
27
+
28
+ ## Consequences
29
+
30
+ - `Developer Choice`: Developers gain the ability to select the one that best matches their needs and environmental constraints, thereby enhancing the library's usability.
31
+ - `Code Complexity`: While introducing multiple logger types increases the library's code complexity, this is offset by the significant gain in flexibility and user satisfaction.
32
+ - `Broad Compatibility`: This decision ensures that the logging mechanism is adaptable to a wide range of operational environments, enhancing the library's overall robustness and accessibility.
@@ -78,7 +78,10 @@ export default defineConfig(withMermaid({
78
78
  text: 'Common use cases',
79
79
  collapsed: false,
80
80
  items: [
81
+ { text: 'Logging and debugging', link: '/common-use-cases/logging-and-debugging' },
81
82
  { text: 'Bech32 enc/decoding (NIP-19)', link: '/common-use-cases/bech32-encoding-and-decoding-(NIP-19)' },
83
+ { text: 'Signing/verifying messages', link: '/common-use-cases/signing-and-verifying-messages' },
84
+ { text: 'Signing/verifying events', link: '/common-use-cases/signing-and-verifying-events' },
82
85
  ]
83
86
  },
84
87
  {
data/docs/bun.lockb CHANGED
Binary file
@@ -0,0 +1,60 @@
1
+ # Logging and debugging
2
+
3
+ The `Nostr::Client` class provides built-in logging functionality to help you debug and monitor client interactions with
4
+ relays. By default, the client uses the `ColorLogger`, which logs events in color. However, you can customize the
5
+ logging behavior or disable it entirely.
6
+
7
+ ## Disabling logging
8
+
9
+ To instantiate a client without any logging, simply pass `logger: nil` when creating the client instance:
10
+
11
+ ```ruby
12
+ client = Nostr::Client.new(logger: nil)
13
+ ```
14
+
15
+ This will disable all logging for the client.
16
+
17
+ ## Formatting the logging
18
+
19
+ The `Nostr::Client::Logger` class is the base class for logging functionality. It defines the following methods for
20
+ logging different events:
21
+
22
+ - `on_connect(relay)`: Logs when the client connects to a relay.
23
+ - `on_message(message)`: Logs a message received from the relay.
24
+ - `on_send(message)`: Logs a message sent to the relay.
25
+ - `on_error(message)`: Logs an error message.
26
+ - `on_close(code, reason)`: Logs when the connection with a relay is closed.
27
+
28
+ You can create your own logger by subclassing `Nostr::Client::Logger` and overriding these methods to customize the
29
+ logging format.
30
+
31
+ The `Nostr::Client::ColorLogger` is a built-in logger that logs events in color. It uses ANSI escape codes to add color
32
+ to the log output. Here's an example of how the ColorLogger formats the log messages:
33
+
34
+ - Connection: `"\u001b[32m\u001b[1mConnected to the relay\u001b[22m #{relay.name} (#{relay.url})\u001b[0m"`
35
+ - Message received: `"\u001b[32m\u001b[1m◄-\u001b[0m #{message}"`
36
+ - Message sent: `"\u001b[32m\u001b[1m-►\u001b[0m #{message}"`
37
+ - Error: `"\u001b[31m\u001b[1mError: \u001b[22m#{message}\u001b[0m"`
38
+ - Connection closed: `"\u001b[31m\u001b[1mConnection closed: \u001b[22m#{reason} (##{code})\u001b[0m"`
39
+
40
+ ## Plain text logging
41
+
42
+ If you prefer plain text logging without colors, you can use the `Nostr::Client::PlainLogger`. This logger formats the
43
+ log messages in a simple, readable format without any ANSI escape codes.
44
+
45
+ To use the `PlainLogger`, pass it as the `logger` option when creating the client instance:
46
+
47
+ ```ruby
48
+ client = Nostr::Client.new(logger: Nostr::Client::PlainLogger.new)
49
+ ```
50
+
51
+ The `PlainLogger` formats the log messages as follows:
52
+
53
+ - Connection: `"Connected to the relay #{relay.name} (#{relay.url})"`
54
+ - Message received: `"◄- #{message}"`
55
+ - Message sent: `"-► #{message}"`
56
+ - Error: `"Error: #{message}"`
57
+ - Connection closed: `"Connection closed: #{reason} (##{code})"`
58
+
59
+ By using the appropriate logger or creating your own custom logger, you can effectively debug and monitor your Nostr
60
+ client's interactions with relays.
@@ -0,0 +1,50 @@
1
+ # Signing and verifying events
2
+
3
+ Signing an event in Nostr proves it was sent by the owner of a specific private key.
4
+
5
+ ## Signing an event
6
+
7
+ To sign an event, use the private key associated with the event's creator. Here's how to sign a message using a
8
+ predefined keypair:
9
+
10
+ ```ruby{14}
11
+ require 'nostr'
12
+
13
+ private_key = Nostr::PrivateKey.new('67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa'),
14
+ public_key = Nostr::PublicKey.new('7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e'),
15
+
16
+ event = Nostr::Event.new(
17
+ pubkey: public_key.to_s,
18
+ kind: Nostr::EventKind::TEXT_NOTE,
19
+ content: 'We did it with security, now we’re going to do it with the economy.',
20
+ created_at: Time.now.to_i,
21
+ )
22
+
23
+ # Sign the event with the private key
24
+ event.sign(private_key)
25
+
26
+ puts "Event ID: #{event.id}"
27
+ puts "Event Signature: #{event.sig}"
28
+ ```
29
+
30
+ ## Verifying an event's signature
31
+
32
+ To verify an event, you must ensure the event's signature is valid. This indicates the event was created by the owner
33
+ of the corresponding public key.
34
+
35
+ When the event was signed with the private key corresponding to the public key, the `verify_signature` method will
36
+ return `true`.
37
+
38
+ ```ruby
39
+ event.verify_signature # => true
40
+ ```
41
+
42
+ And when the event was not signed with the private key corresponding to the public key, the `verify_signature` method
43
+ will return `false`.
44
+
45
+ An event without an `id`, `pubkey`, `sig` is considered invalid and will return `false` when calling `verify_signature`.
46
+
47
+ ```ruby
48
+ other_public_key = Nostr::PublicKey.new('10be96d345ed58d923a734560680f1adfd2b1006c28ac93b8e1b032a9a32c6e9')
49
+ event.verify_signature # => false
50
+ ```
@@ -0,0 +1,43 @@
1
+ # Signing and verifying messages
2
+
3
+ Signing a message in Nostr proves it was sent by the owner of a specific private key.
4
+
5
+ ## Signing a message
6
+
7
+ To sign a message, you'll need a private key. Here's how to sign a message using a predefined keypair:
8
+
9
+ ```ruby{9}
10
+ require 'nostr'
11
+
12
+ private_key = Nostr::PrivateKey.new('67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa'),
13
+ public_key = Nostr::PublicKey.new('7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e'),
14
+
15
+ message = 'We did it with security, now we’re going to do it with the economy.' # The message you want to sign
16
+
17
+ crypto = Nostr::Crypto.new
18
+ signature = crypto.sign_message(message, private_key)
19
+ signature # => "d7a0aac1fadcddf1aa2949bedfcdf25ce0c1604e648e55d31431fdacbff8e8256f7c2166d98292f80bc5f79105a0b6e8a89236a47d97cf5d0e7cc1ebf34dea5c"
20
+ ```
21
+
22
+ ## Verifying a signature
23
+
24
+ To verify a signature, you need the original message, the public key of the signer, and the signature.
25
+
26
+ ```ruby
27
+ crypto.valid_sig?(message, public_key, signature) # => true
28
+ crypto.check_sig!(message, public_key, signature) # => true
29
+ ```
30
+
31
+ When the message was not signed with the private key corresponding to the public key, the `valid_sig?` method will return `false`.
32
+
33
+ ```ruby
34
+ other_public_key = Nostr::PublicKey.new('10be96d345ed58d923a734560680f1adfd2b1006c28ac93b8e1b032a9a32c6e9')
35
+ crypto.valid_sig?(message, public_key, signature) # => false
36
+ ```
37
+
38
+ And when the message was not signed with the private key corresponding to the public key, the `check_sig!` method will raise an error.
39
+
40
+ ```ruby
41
+ other_public_key = Nostr::PublicKey.new('10be96d345ed58d923a734560680f1adfd2b1006c28ac93b8e1b032a9a32c6e9')
42
+ crypto.check_sig!(message, other_public_key, signature) # => Schnorr::InvalidSignatureError: signature verification failed
43
+ ```
data/docs/core/client.md CHANGED
@@ -28,7 +28,7 @@ The `:connect` event is fired when a connection with a WebSocket is opened. You
28
28
  client = Nostr::Client.new
29
29
  relay = Nostr::Relay.new(url: 'wss://relay.damus.io', name: 'Damus')
30
30
 
31
- client.on :connect do
31
+ client.on :connect do |relay|
32
32
  # When this block executes, you're connected to the relay
33
33
  end
34
34
 
@@ -35,6 +35,7 @@ classDiagram
35
35
  serialize()
36
36
  to_h()
37
37
  sign(private_key)
38
+ verify_signature()
38
39
  }
39
40
  class Subscription {
40
41
  id
@@ -110,7 +111,7 @@ keypair = keygen.get_key_pair_from_private_key(
110
111
 
111
112
  # c) Or create a new keypair
112
113
  keygen = Nostr::Keygen.new
113
- keypair = keygen.generate_keypair
114
+ keypair = keygen.generate_key_pair
114
115
 
115
116
  # Create a user with the keypair
116
117
  user = Nostr::User.new(keypair: keypair)
@@ -164,6 +165,9 @@ end
164
165
  client.on :close do |code, reason|
165
166
  # You may attempt to reconnect to the relay here
166
167
  end
168
+
169
+ # This line keeps the background client from exiting immediately.
170
+ gets
167
171
  ```
168
172
 
169
173
  Beyond what's covered here, the Nostr protocol and this gem boast a wealth of additional functionalities. For an
data/docs/index.md CHANGED
@@ -39,6 +39,4 @@ features:
39
39
  - title: Fully typed
40
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
41
  icon: ✅
42
-
43
42
  ---
44
-
data/docs/package.json CHANGED
@@ -5,8 +5,8 @@
5
5
  "docs:preview": "vitepress preview"
6
6
  },
7
7
  "devDependencies": {
8
- "mermaid": "^10.6.1",
9
- "vitepress": "^1.0.0-rc.25",
10
- "vitepress-plugin-mermaid": "^2.0.15"
8
+ "mermaid": "^10.9.0",
9
+ "vitepress": "^1.1.0",
10
+ "vitepress-plugin-mermaid": "^2.0.16"
11
11
  }
12
12
  }