{"id":13832727,"url":"https://github.com/dtonon/nostr-ruby","last_synced_at":"2025-03-15T11:32:58.545Z","repository":{"id":64988559,"uuid":"579788437","full_name":"dtonon/nostr-ruby","owner":"dtonon","description":"A ruby library to interact with the Nostr Protocol","archived":false,"fork":false,"pushed_at":"2024-04-22T13:44:50.000Z","size":50,"stargazers_count":44,"open_issues_count":1,"forks_count":7,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-05-22T22:01:53.462Z","etag":null,"topics":["nostr","nostr-protocol","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dtonon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-18T23:03:30.000Z","updated_at":"2024-05-29T23:55:23.510Z","dependencies_parsed_at":"2024-05-29T23:55:20.586Z","dependency_job_id":"620c25a2-0e3b-48c8-90eb-62aa067a2031","html_url":"https://github.com/dtonon/nostr-ruby","commit_stats":{"total_commits":17,"total_committers":2,"mean_commits":8.5,"dds":0.05882352941176472,"last_synced_commit":"30607171676cd097e510b96499941563509faf08"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dtonon%2Fnostr-ruby","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dtonon%2Fnostr-ruby/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dtonon%2Fnostr-ruby/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dtonon%2Fnostr-ruby/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dtonon","download_url":"https://codeload.github.com/dtonon/nostr-ruby/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243725010,"owners_count":20337659,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["nostr","nostr-protocol","ruby"],"created_at":"2024-08-04T11:00:28.946Z","updated_at":"2025-03-15T11:32:58.539Z","avatar_url":"https://github.com/dtonon.png","language":"Ruby","readme":"# Nostr Ruby\n\nA ruby library to interact with the [Nostr Protocol](https://github.com/nostr-protocol/nostr).\n\n\u003e [!Warning]\n\u003e This version in work in progress and breaks the v0.2.0 API\n\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\n# Gemfile\ngem 'nostr_ruby'\n```\n\nAnd then execute:\n\n```shell\n$ bundle\n```\n\nOr install it yourself as:\n```shell\n$ gem install nostr_ruby\n```\n\n## Usage\n\n### Manage keys\n\n```ruby\nrequire \"nostr_ruby\"\n\nsk = Nostr::Key.generate_private_key\n# =\u003e \"8090fb3fe26e27d539ee349d70890d338c5e2e8b459e04c8e97658f03d2f9f33\"\n\npk = Nostr::Key.get_public_key(sk)\n# =\u003e \"e7ded9bd42e7c74fcc6465962b919b7efcd5774ac6bea2ae6b81b2caa9d4d2e6\"\n```\n\n### Decode entities\n\n```ruby\n\npuplic_key = Nostr::Bech32.decode(npub)\n# =\u003e {:hrp=\u003e\"npub\", :data=\u003e\"e7ded9bd42e7c74fcc6465962b919b7efcd5774ac6bea2ae6b81b2caa9d4d2e6\"}\n\nnprofile_data = Nostr::Bech32.decode(\"nprofile1qqs8hhhhhc3dmrje73squpz255ape7t448w86f7ltqemca7m0p99spgprpmhxue69uhkgar0dehkutnwdaehgu339e3k7mf06ras84\")\n# =\u003e {:hrp=\u003e\"nprofile\", :data=\u003e{:pubkey=\u003e[\"7bdef7be22dd8e59f4600e044aa53a1cf975a9dc7d27df5833bc77db784a5805\"], :relay=\u003e[\"wss://dtonon.nostr1.com/\"]}}\n\nnote_data = Nostr::Bech32.decode(\"note1xzce08egncw3mcm8l8edas6rrhgfj9l5uwwv2hz03zym0m9eg5hsxuyajp\")\n# =\u003e {:hrp=\u003e\"note\", :data=\u003e\"30b1979f289e1d1de367f9f2dec3431dd09917f4e39cc55c4f8889b7ecb9452f\"}\n\nnevent_data = Nostr::Bech32.decode(\"nevent1qqsrpvvhnu5fu8gaudnlnuk7cdp3m5yezl6w88x9t38c3zdhaju52tcpzpmhxue69uhkztnwdaejumr0dshsz9nhwden5te0vfjhvmewdehhxarjxyhxxmmd9uq3wamnwvaz7tmzd96xxmmfdejhytnnda3kjctv9ulrdeva\")\n# =\u003e {:hrp=\u003e\"nevent\", :data=\u003e {:id=\u003e[\"30b1979f289e1d1de367f9f2dec3431dd09917f4e39cc55c4f8889b7ecb9452f\"], :relay=\u003e[\"wss://a.nos.lol/\", \"wss://bevo.nostr1.com/\", \"wss://bitcoiner.social/\"]}}\n\nnaddr_data = Nostr::Bech32.decode(\"naddr1qvzqqqr4gupzq77777lz9hvwt86xqrsyf2jn588ewk5aclf8mavr80rhmduy5kq9qqdkc6t5w3kx2ttvdamx2ttxdaez6mr0denj6en0wfkkzaqxq5r99\")\n# =\u003e =\u003e {:hrp=\u003e\"naddr\", :data=\u003e{:kind=\u003e[30023], :author=\u003e[\"7bdef7be22dd8e59f4600e044aa53a1cf975a9dc7d27df5833bc77db784a5805\"], :identifier=\u003e[\"little-love-for-long-format\"]}}\n```\n\n### Encode entities\n\n```ruby\n\nnsec = Nostr::Bech32.encode_nsec(sk)\n# =\u003e \"nsec1szg0k0lzdcna2w0wxjwhpzgdxwx9ut5tgk0qfj8fwev0q0f0nuessml5ur\"\n\nnpub = Nostr::Bech32.encode_npub(pk)\n# =\u003e \"npub1ul0dn02zulr5lnryvktzhyvm0m7d2a62c6l29tntsxev42w56tnqksrtfu\"\n\nnprofile = Nostr::Bech32.encode_nprofile(\n  pubkey: \"7bdef7be22dd8e59f4600e044aa53a1cf975a9dc7d27df5833bc77db784a5805\",\n  relays: [\"wss://dtonon.nostr1.com\"],\n)\n# =\u003e \"nprofile1qqs8hhhhhc3dmrje73squpz255ape7t448w86f7ltqemca7m0p99spgpzamhxue69uhkgar0dehkutnwdaehgu339e3k7mg60me8x\"\n\nnote = Nostr::Bech32.encode_note(\"30b1979f289e1d1de367f9f2dec3431dd09917f4e39cc55c4f8889b7ecb9452f\")\n# =\u003e \"note1xzce08egncw3mcm8l8edas6rrhgfj9l5uwwv2hz03zym0m9eg5hsxuyajp\"\n\nnevent = Nostr::Bech32.encode_nevent(\n  id: \"30b1979f289e1d1de367f9f2dec3431dd09917f4e39cc55c4f8889b7ecb9452f\",\n  relays: [\"wss://nos.lol\"],\n)\n# =\u003e \"nevent1qqsrpvvhnu5fu8gaudnlnuk7cdp3m5yezl6w88x9t38c3zdhaju52tcpp4mhxue69uhkummn9ekx7mqrqsqqqqqpux7e9q\"\n\nnaddr = Nostr::Bech32.encode_naddr(\n  author: \"7bdef7be22dd8e59f4600e044aa53a1cf975a9dc7d27df5833bc77db784a5805\",\n  identifier: \"little-love-for-long-format\",\n  kind: 30023\n)\n# =\u003e \"naddr1qgs8hhhhhc3dmrje73squpz255ape7t448w86f7ltqemca7m0p99spgrqsqqqa28qqdkc6t5w3kx2ttvdamx2ttxdaez6mr0denj6en0wfkkzaqn2tjdj\"\n```\n\n### Initialize Client\n\n```ruby\nrequire \"nostr_ruby\"\n\n# Detailed version\ns = Nostr::Signer.new(private_key: Nostr::Key.generate_private_key)\nc = Nostr::Client.new(signer: s)\n\n# Compact version, under the hood Client creates a Signer\nc = Nostr::Client.new(private_key: Nostr::Key.generate_private_key)\n\nc.private_key\n# =\u003e \"7402b4b1ee09fb37b64ec2a958f1b7815d904c6dd44227bdef7912ef201af97d\"\n\nc.public_key\n# =\u003e \"a19f3c16b6e857d2b673c67eea293431fc175895513ca2f687a717152a5da466\"\n\nc.nsec\n# =\u003e \"nsec1wsptfv0wp8an0djwc2543udhs9weqnrd63pz00000yfw7gq6l97snckpdq\"\n\nc.npub\n# =\u003e \"npub15x0nc94kapta9dnncelw52f5x87pwky42y729a585ut322ja53nq72yrcr\"\n```\n\n### Create, sign and send an event\n\n```ruby\n# Initialize a client, under the hood Client creates a Signer\nc = Nostr::Client.new(\n  private_key: \"7402b4b1ee09fb37b64ec2a958f1b7815d904c6dd44227bdef7912ef201af97d\",\n  relay: \"wss://nos.lol\"\n)\n\n# Initialize an event\ne = Nostr::Event.new(\n  kind: ...,\n  pubkey: ...,\n  created_at: ...,\n  tags: ...,\n  content: ...,\n  pow: ...,\n  delegation: ...,\n)\n\n# Sign the event\ne = c.sign(e)\n\n# - - - - - - - - - - - - - -\n# Full async mode\n\n# Set the open callback\nc.on :connect do |event|\n  puts 'Publish event...'\nend\n\n# Set the response callback\nc.on :ok do |event|\n  puts \"Event id: #{event.id}\"\n  puts \"Accepted: #{event.success}\"\n  puts \"Message: #{event.message}\"\nend\n\n# Connect and send the event\nc.connect\nc.publish(e)\n# Do more things\nc.close\n\n# - - - - - - - - - - - - - -\n# Compact sync mode\n\nc.connect\nc.publish_and_wait(e)\nc.close\n\n```\n\n### Set the profile\n\n```ruby\nmetadata = {\n  name: \"Mr Robot\",\n  about: \"I walk around the city\",\n  picture: \"https://upload.wikimedia.org/wikipedia/commons/3/35/Mr_robot_photo.jpg\",\n  nip05: \"mrrobot@mrrobot.com\"\n}\n\ne = Nostr::Event.new(\n  kind: Nostr::Kind::METADATA,\n  pubkey: c.public_key,\n  content: metadata.to_json,\n)\n```\n\n### Post a note\n```ruby\ne = Nostr::Event.new(\n  kind: Nostr::Kind::SHORT_NOTE,\n  pubkey: c.public_key,\n  content: \"Hello Nostr!\",\n)\n```\n\n### Share a contact list\n```ruby\ncontact_list = [\n  [\"54399b6d8200813bfc53177ad4f13d6ab712b6b23f91aefbf5da45aeb5c96b08\", \"wss://alicerelay.com/\", \"alice\"],\n  [\"850708b7099215bf9a1356d242c2354939e9a844c1359d3b5209592a0b420452\", \"wss://bobrelay.com/nostr\", \"bob\"],\n  [\"f7f4b0072368460a09138bf3966fb1c59d0bdadfc3aff4e59e6896194594a82a\", \"ws://carolrelay.com/ws\", \"carol\"]\n]\n\ne = Nostr::Event.new(\n  kind: Nostr::Kind::CONTACT_LIST,\n  pubkey: c.public_key,\n  tags: contact_list.map { |c| ['p'] + c },\n)\n```\n\n### Delete an event\n```ruby\nevent_to_delete = \"b91b3fb40128112c38dc54168b9f601c22bf8fcae6e70bb2a5f53e7f3ae44388\"\ne = Nostr::Event.new(\n  kind: Nostr::Kind::DELETION,\n  pubkey: c.public_key,\n  tags: [[\"e\", event_to_delete]],\n)\n```\n\n### React to an event\n```ruby\ne = Nostr::Event.new(\n  kind: Nostr::Kind::REACTION,\n  pubkey: c.public_key,\n  content: \"+\",\n  tags: [[\"e\", target_event]],\n)\n\n# You can also use emoji\ne2 = Nostr::Event.new(\n  kind: Nostr::Kind::REACTION,\n  pubkey: c.public_key,\n  content: \"🔥\",\n  tags: [[\"e\", target_event]],\n)\n```\n\n### Create events with a PoW difficulty\n```ruby\n# Just add the `pow` argument \ne = Nostr::Event.new(\n  kind: Nostr::Kind::SHORT_NOTE,\n  pubkey: c.public_key,\n  content: \"Hello Nostr!\",\n  pow: 15,\n)\n```\n\n### Create an event with a NIP-26 delegation\n```ruby\ndelegator = Nostr::Client.new(private_key: delegator_key)\n\ndelegatee = \"b1d8dfd69fe8795042dbbc4d3f85938a01d4740c54d2daf11088c75c50ff19d9\"\nconditions = \"kind=1\u0026created_at\u003e#{Time.now.to_i}\u0026created_at\u003c#{(Time.now + 60*60).to_i}\"\ndelegation_tag = delegator.generate_delegation_tag(\n  to: delegatee,\n  conditions: conditions\n)\n\n# The `delegation_tag` is given to the delegatee so it can use it\n\ndelegatee = Nostr::Client.new(private_key: delegatee_key)\n\ne = Nostr::Event.new(\n  kind: Nostr::Kind::SHORT_NOTE,\n  pubkey: delegatee.public_key,\n  content: \"Hello Nostr!\",\n  delegation: delegation_tag,\n)\n\ndelegatee.sign(e)\n```\n### Send a direct message\nWarning: This uses NIP-04, that will be deprecated in favor of NIP-17\n```ruby\n\ne = Nostr::Event.new(\n  kind: Nostr::Kind::DIRECT_MESSAGE,\n  pubkey: c.public_key,\n  tags: [[\"p\", Nostr::Bech32.decode(recipient)[:data]]],\n  content: \"Hello Alice!\"\n)\n```\n\n### Decrypt a direct message\nWarning: This uses NIP-04, that will be deprecated in favor of NIP-17\n```ruby\npayload = {\n  :kind=\u003e4,\n  :pubkey=\u003e\"a19f3c16b6e857d2b673c67eea293431fc175895513ca2f687a717152a5da466\",\n  :created_at=\u003e1725387307,\n  :tags=\u003e[[\"p\", \"e7ded9bd42e7c74fcc6465962b919b7efcd5774ac6bea2ae6b81b2caa9d4d2e6\"]],\n  :content=\u003e\"Nd7n/wId1oiprUCC4WWwNw==?iv=7gIRExcyO1xystretLIPnQ==\",\n  :id=\u003e\"b91b3fb40128112c38dc54168b9f601c22bf8fcae6e70bb2a5f53e7f3ae44388\",\n  :sig=\u003e\"73edf5a6acbefdd3d76f28ba90faaabe348a24c798f8fa33797eec29e2404c33a455815a59472ecd023441df38d815f83d81b95b8cb2f2c88a52982c8f7301e9\"\n}\ne = Nostr::Event.new(**payload)\n\nc.decrypt(e)\nputs e.content\n=\u003e \"Hello Alice!\"\n```\n\n### Create a subscribtion to receive events\n\n```ruby\nfilter = Nostr::Filter.new(\n  kinds: [1],\n  authors: [\"a19f3c16b6e857d2b673c67eea293431fc175895513ca2f687a717152a5da466\"],\n  since: Time.now - (60*60*24), # 24 hours ago\n  limit: 10\n)\n\nc.on :connect do |message|\n  puts \"Connected!\"\nend\n\nc.on :event do |message|\n  puts \"\u003e\u003e #{message.content}\"\nend\n\nc.on :eose do |message|\n  puts \"Finished subscription #{message.subscription_id}\"\n  c.close\nend\n\nc.on :close do |message|\n  puts \"Connection closed\"\nend\n\nc.subscribe(filter: filter)\nc.connect\n```","funding_links":[],"categories":["Install from Source","Libraries"],"sub_categories":["Nostr","Client reviews and/or comparisons"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdtonon%2Fnostr-ruby","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdtonon%2Fnostr-ruby","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdtonon%2Fnostr-ruby/lists"}