{"id":13832705,"url":"https://github.com/anasfik/nostr","last_synced_at":"2025-08-21T03:32:41.708Z","repository":{"id":152473321,"uuid":"626125861","full_name":"anasfik/nostr","owner":"anasfik","description":"Develop Scalable Dart/Flutter Nostr clients quickly and easily","archived":false,"fork":false,"pushed_at":"2025-08-06T15:29:39.000Z","size":1089,"stargazers_count":45,"open_issues_count":7,"forks_count":16,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-08-18T23:57:13.491Z","etag":null,"topics":["dart","flutter","nostr","nostr-client","nostr-dev-kit","nostr-protocol","nostr-relay","nostr-sdk","nostr-tools"],"latest_commit_sha":null,"homepage":"","language":"Dart","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/anasfik.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null}},"created_at":"2023-04-10T21:13:29.000Z","updated_at":"2025-08-06T15:29:42.000Z","dependencies_parsed_at":"2024-02-11T23:24:54.680Z","dependency_job_id":"60a3b74e-4879-4189-b61e-5acc895cd3cc","html_url":"https://github.com/anasfik/nostr","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/anasfik/nostr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anasfik%2Fnostr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anasfik%2Fnostr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anasfik%2Fnostr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anasfik%2Fnostr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anasfik","download_url":"https://codeload.github.com/anasfik/nostr/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anasfik%2Fnostr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271078587,"owners_count":24695473,"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","status":"online","status_checked_at":"2025-08-18T02:00:08.743Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["dart","flutter","nostr","nostr-client","nostr-dev-kit","nostr-protocol","nostr-relay","nostr-sdk","nostr-tools"],"created_at":"2024-08-04T11:00:28.360Z","updated_at":"2025-08-21T03:32:41.306Z","avatar_url":"https://github.com/anasfik.png","language":"Dart","funding_links":[],"categories":["Libraries"],"sub_categories":["Client reviews and/or comparisons"],"readme":"# Dart Nostr\n\nThis is a Dart/Flutter toolkit for developing [Nostr](https://nostr.com/) client apps faster and easier.\n\n## Table of Contents\n\n- [Supported NIPs](#supported-nips)\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Singleton instance vs. multiple instances](#singleton-instance-vs-multiple-instances)\n  - [Keys](#keys)\n    - [Generate private and public keys](#generate-private-and-public-keys)\n    - [Generate a key pair from a private key](#generate-a-key-pair-from-a-private-key)\n    - [Sign \u0026 verify with a private key](#sign--verify-with-a-private-key)\n    - [More functionalities](#more-functionalities)\n  - [Events \u0026 Relays](#events--relays)\n    - [Create an event](#create-an-event)\n    - [Connect to relays](#connect-to-relays)\n    - [Listen to events](#listen-to-events)\n      - [As a stream](#as-a-stream)\n      - [As a future (resolves on EOSE)](#as-a-future-resolves-on-eose)\n    - [Reconnect \u0026 disconnect](#reconnect--disconnect)\n    - [Send an event](#send-an-event)\n    - [Send NIP45 COUNT](#send-nip45-count)\n    - [Relay Metadata NIP11](#relay-metadata-nip11)\n    - [More functionalities](#more-functionalities-1)\n  - [More utils](#more-utils)\n\n## Supported NIPs\n\nif you are working on a Nostr client, app... you will be able to apply and use all the following NIPs (Updated 2024-01-18):\n\n- [NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md)\n- [NIP-02](https://github.com/nostr-protocol/nips/blob/master/02.md)\n- [NIP-03](https://github.com/nostr-protocol/nips/blob/master/03.md)\n- [NIP-04](https://github.com/nostr-protocol/nips/blob/master/04.md)\n- [NIP-05](https://github.com/nostr-protocol/nips/blob/master/05.md)\n- [NIP-06](https://github.com/nostr-protocol/nips/blob/master/06.md) \n- [NIP-08](https://github.com/nostr-protocol/nips/blob/master/08.md)\n- [NIP-09](https://github.com/nostr-protocol/nips/blob/master/09.md)\n- [NIP-10](https://github.com/nostr-protocol/nips/blob/master/10.md)\n- [NIP-11](https://github.com/nostr-protocol/nips/blob/master/11.md)\n- [NIP-13](https://github.com/nostr-protocol/nips/blob/master/13.md)\n- [NIP-14](https://github.com/nostr-protocol/nips/blob/master/14.md)\n- [NIP-15](https://github.com/nostr-protocol/nips/blob/master/15.md)\n- [NIP-18](https://github.com/nostr-protocol/nips/blob/master/18.md)\n- [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md)\n- [NIP-21](https://github.com/nostr-protocol/nips/blob/master/21.md)\n- [NIP-23](https://github.com/nostr-protocol/nips/blob/master/23.md)\n- [NIP-24](https://github.com/nostr-protocol/nips/blob/master/24.md)\n- [NIP-25](https://github.com/nostr-protocol/nips/blob/master/25.md)\n- [NIP-27](https://github.com/nostr-protocol/nips/blob/master/27.md)\n- [NIP-28](https://github.com/nostr-protocol/nips/blob/master/28.md)\n- [NIP-30](https://github.com/nostr-protocol/nips/blob/master/30.md)\n- [NIP-31](https://github.com/nostr-protocol/nips/blob/master/31.md)\n- [NIP-32](https://github.com/nostr-protocol/nips/blob/master/32.md)\n- [NIP-36](https://github.com/nostr-protocol/nips/blob/master/36.md)\n- [NIP-38](https://github.com/nostr-protocol/nips/blob/master/38.md)\n- [NIP-39](https://github.com/nostr-protocol/nips/blob/master/39.md)\n- [NIP-40](https://github.com/nostr-protocol/nips/blob/master/40.md)\n- NIP-42 (not yet implemented)\n- NIP-44 (not yet implemented)\n- [NIP-45](https://github.com/nostr-protocol/nips/blob/master/45.md)\n- [NIP-47](https://github.com/nostr-protocol/nips/blob/master/47.md)\n- [NIP-48](https://github.com/nostr-protocol/nips/blob/master/48.md)\n- [NIP-50](https://github.com/nostr-protocol/nips/blob/master/50.md)\n- [NIP-51](https://github.com/nostr-protocol/nips/blob/master/51.md)\n- [NIP-52](https://github.com/nostr-protocol/nips/blob/master/52.md)\n- [NIP-53](https://github.com/nostr-protocol/nips/blob/master/53.md)\n- [NIP-56](https://github.com/nostr-protocol/nips/blob/master/56.md)\n- [NIP-57: Lightning Zaps](57.md)\n- [NIP-58](https://github.com/nostr-protocol/nips/blob/master/58.md)\n\u003c!-- - [NIP-65](https://github.com/nostr-protocol/nips/blob/master/65.md) --\u003e\n- [NIP-72](https://github.com/nostr-protocol/nips/blob/master/72.md)\n- [NIP-75](https://github.com/nostr-protocol/nips/blob/master/75.md)\n- [NIP-78](https://github.com/nostr-protocol/nips/blob/master/78.md)\n- [NIP-84](https://github.com/nostr-protocol/nips/blob/master/84.md)\n- [NIP-89](https://github.com/nostr-protocol/nips/blob/master/89.md)\n- [NIP-94](https://github.com/nostr-protocol/nips/blob/master/94.md)\n- [NIP-98](https://github.com/nostr-protocol/nips/blob/master/98.md)\n- [NIP-99](https://github.com/nostr-protocol/nips/blob/master/99.md)\n\nNIPs marked as \"not yet implemented\" are not supported yet.\n\nSome existant NIPs are platform specific or can't just be supported directly like [NIP 07](https://github.com/nostr-protocol/nips/blob/master/07.md) which is only web-specific, or [NIP 90](https://github.com/nostr-protocol/nips/blob/master/90.md) which is related to Data Vending machines.\n\n## Installation\n\nInstall the package by adding the following to your `pubspec.yaml` file:\n\n```yaml\ndependencies:\n  dart_nostr: any\n```\n\nOtherwise you can install it from the command line:\n\n```bash\n# Flutter project\nflutter pub add dart_nostr\n\n# Dart project\ndart pub add dart_nostr\n```\n\n## Usage\n\n### Singleton instance vs. multiple instances\n\nThe base and only class you need to remember is `Nostr`, all methods and utilities are available through it.\n\nThe `Nostr` class is accessible through two ways, a singleton instance which you can access by calling `Nostr.instance` and a constructor which you can use to create multiple instances of `Nostr`.\n\nEach instance (including the singleton instance) is independent from each other, so everything you do with one instance will be accessible only through that instance including relays, events, caches, callbacks, etc.\n\nUse the singleton instance if you want to use the same instance across your app, so as example once you do connect to a set of relays, you can access and use them (send and receive events) from anywhere in your app.\n\nUse the constructor if you want to create multiple instances of `Nostr`, as example if you want to connect to different relays in different parts of your app, or if you have extensive Nostr relays usage (requests) and you want to separate them into different instances so you avoid relays limits.\n\n```dart\n/// Singleton instance\nfinal instance = Nostr.instance;\n\n/// Constructor\nfinal instance = Nostr();\n```\n\n### Keys\n\n#### Generate private and public keys\n\n```dart\nfinal newKeyPair = instance.keysService.generateKeyPair();\n\nprint(newKeyPair.public); // Public key\nprint(newKeyPair.private); // Private key\n```\n\n#### Generate a key pair from a private key\n\n```dart\nfinal somePrivateKey = \"HERE IS MY PRIVATE KEY\";\n\nfinal newKeyPair = instance.keysService\n      .generateKeyPairFromExistingPrivateKey(somePrivateKey);\n\nprint(somePrivateKey == newKeyPair.private); // true\nprint(newKeyPair.public); // Public key\n```\n\n#### Sign \u0026 verify with a private key\n\n```dart\n\n/// sign a message with a private key\nfinal signature = instance.keysService.sign(\n  privateKey: keyPair.private,\n  message: \"hello world\",\n);\n\nprint(signature);\n\n/// verify a message with a public key\nfinal verified = instance.keysService.verify(\n  publicKey: keyPair.public,\n  message: \"hello world\",\n  signature: signature,\n);\n\nprint(verified); // true\n```\n\nNote: `dart_nostr` provides even more easier way to create, sign and verify Nostr events, see the relays and events sections below.\n\n#### More functionalities\n\nThe package exposes more useful methods, like:\n  \n```dart\n// work with nsec keys\ninstance.keysService.encodePrivateKeyToNsec(privateKey);\ninstance.keysService.decodeNsecKeyToPrivateKey(privateKey);\n\n// work with npub keys\ninstance.keysService.encodePublicKeyToNpub(privateKey);\ninstance.keysService.decodeNpubKeyToPublicKey(privateKey);\n\n// more keys derivations and validations methods\ninstance.keysService.derivePublicKey(privateKey);\ninstance.keysService.generatePrivateKey(privateKey);\ninstance.keysService.isValidPrivateKey(privateKey);\n\n// general utilities that related to keys\ninstance.utilsService.decodeBech32(bech32String);\ninstance.utilsService.encodeBech32(bech32String);\ninstance.utilsService.pubKeyFromIdentifierNip05(bech32String);\n```\n\n### Events \u0026 Relays\n\n#### Create an event\n\nQuickest way to create an event is by using the `NostrEvent.fromPartialData` constructor, it does all the heavy lifting for you, like signing the event with the provided private key, generating the event id, etc.\n\n```dart\nfinal event = NostrEvent.fromPartialData(\n  kind: 1,\n  content: 'example content',\n  keyPairs: keyPair, // will be used to sign the event\n  tags: [\n    ['t', currentDateInMsAsString],\n  ],\n);\n\nprint(event.id); // event id\nprint(event.sig); // event signature\n\nprint(event.serialized()); // event as serialized JSON\n```\n\nNote: you can also create an event from scratch with the `NostrEvent` constructor, but you will need to do the heavy lifting yourself, like signing the event, generating the event id, etc.\n\n#### Connect to relays\nfor a single `Nostr` instance, you can connect and reconnect to multiple relays once or multiple times, so you will be able to send and receive events later.\n\n```dart\ntry {\n \n final relays = ['wss://relay.damus.io'];\n \n await instance.relaysService.init(\n  relaysUrl: relays,\n );\n\nprint(\"connected successfully\")\n} catch (e) {\n  print(e);\n}\n```\n\nif anything goes wrong, you will get an exception with the error message.\nNote: the `init` method is highly configurable, so you can control the behavior of the connection, like the number of retries, the timeout, wether to throw an exception or not, register callbacks for connections or events...\n\n#### Listen to events\n\n##### As a stream \n\n```dart\n```dart\n// Creating a request to be sent to the relays. (as example this request will get all events with kind 1 of the provided public key)\nfinal request = NostrRequest(\n  filters: [\n    NostrFilter(\n      kinds: const [1],\n      authors: [keyPair.public],\n    ),\n  ],\n);\n\n\n// Starting the subscription and listening to events\nfinal nostrStream = Nostr.instance.services.relays.startEventsSubscription(\n  request: request,\n  onEose: (ease) =\u003e print(ease),\n);\n\nprint(nostrStream.subscriptionId); // The subscription id\n\n// Listening to events\nnostrStream.stream.listen((NostrEvent event) {\n  print(event.content);\n});\n\n// close the subscription later\nnostrStream.close();\n```\n\n##### As a future (resolves on EOSE)\n\n```dart\n// Creating a request to be sent to the relays. (as example this request will get all events with kind 1 of the provided public key)\nfinal request = NostrRequest(\n  filters: [\n    NostrFilter(\n      kinds: const [1],\n      authors: [keyPair.public],\n    ),\n  ],\n);\n\n// Call the async method and wait for the result\nfinal events =\n    await Nostr.instance.services.relays.startEventsSubscriptionAsync(\n  request: request,\n);\n\n// print the events\nprint(events.map((e) =\u003e e.content));\n```\n\nNote: `startEventsSubscriptionAsync` will be resolve with an `List\u003cNostrEvent\u003e` as soon as a relay sends an EOSE command.\n\n#### Reconnect \u0026 disconnect\n\n```dart\n// reconnect\nawait Nostr.instance.services.relays.reconnectToRelays(\n       onRelayListening: onRelayListening,\n       onRelayConnectionError: onRelayConnectionError,\n       onRelayConnectionDone: onRelayConnectionDone,\n       retryOnError: retryOnError,\n       retryOnClose: retryOnClose,\n       shouldReconnectToRelayOnNotice: shouldReconnectToRelayOnNotice,\n       connectionTimeout: connectionTimeout,\n       ignoreConnectionException: ignoreConnectionException,\n       lazyListeningToRelays: lazyListeningToRelays,\n     );\n    \n// disconnect\nawait Nostr.instance.services.relays.disconnectFromRelays();\n```\n\n#### Send an event\n\n```dart\n// sending synchronously\nNostr.instance.services.relays.sendEventToRelays(\n  event,\n  onOk: (ok) =\u003e print(ok),\n);\n\n// sending synchronously with a custom timeout\nfinal okCommand = await Nostr.instance.services.relays.sendEventToRelaysAsync(\n  event,\n  timeout: const Duration(seconds: 3),\n);\n\nprint(okCommand);\n```\n\nNote: `sendEventToRelaysAsync` will be resolve with an `OkCommand` as soon as one relay accepts the event.\n\n#### Send NIP45 COUNT\n\n```dart\n// create a count event\nfinal countEvent = NostrCountEvent.fromPartialData(\n  eventsFilter: NostrFilter(\n    kinds: const [0],\n    authors: [keyPair.public],\n  ),\n);\n\n// Send the count event synchronously\nNostr.instance.services.relays.sendCountEventToRelays(\n  countEvent,\n  onCountResponse: (countRes) {\n    print('count: $countRes');\n  },\n);\n\n// Send the count event asynchronously\nfinal countRes = await Nostr.instance.services.relays.sendCountEventToRelaysAsync(\n  countEvent,\n  timeout: const Duration(seconds: 3),\n);\n\nprint(\"found ${countRes.count} events\");\n```\n\n#### Relay Metadata NIP11\n\n```dart\nfinal relayDoc = await Nostr.instance.services.relays.relayInformationsDocumentNip11(\n  relayUrl: \"wss://relay.damus.io\",\n);\n\nprint(relayDoc?.name);\nprint(relayDoc?.description);\nprint(relayDoc?.contact);\nprint(relayDoc?.pubkey);\nprint(relayDoc?.software);\nprint(relayDoc?.supportedNips);\nprint(relayDoc?.version);\n```\n\n#### More functionalities\n\nThe package exposes more useful methods, like:\n\n```dart\n// work with nevent and nevent\nfinal nevent = Nostr.instance.utilsService.encodeNevent(\n  eventId: event.id,\n  pubkey: pubkey,\n  userRelays: [],\n);\n  \nprint(nevent);\n\nfinal map = Nostr.instance.utilsService.decodeNeventToMap(nevent);\nprint(map);\n\n\n// work with nprofile\nfinal nprofile = Nostr.instance.utilsService.encodeNProfile(\n  pubkey: pubkey,\n  userRelays: [],\n);\n\nprint(nprofile);\n\nfinal map = Nostr.instance.utilsService.decodeNprofileToMap(nprofile);\nprint(map);\n\n```\n\n### More utils\n\n#### Generate random 64 hex\n\n```dart\nfinal random = Nostr.instance.utilsService.random64HexChars();\nfinal randomButBasedOnInput = Nostr.instance.utilsService.consistent64HexChars(\"input\");\n\nprint(random);\nprint(randomButBasedOnInput);\n```\n\n#### NIP05 related\n\n```dart\n/// verify a nip05 identifier\nfinal verified = await Nostr.instance.utilsService.verifyNip05(\n  internetIdentifier: \"something@domain.com\",\n  pubKey: pubKey,\n);\n\nprint(verified); // true\n\n  \n/// Validate a nip05 identifier format\nfinal isValid = Nostr.instance.utilsService.isValidNip05Identifier(\"work@gwhyyy.com\");\nprint(isValid); // true\n\n/// Get the pubKey from a nip05 identifier\nfinal pubKey = await Nostr.instance.utilsService.pubKeyFromIdentifierNip05(\n  internetIdentifier: \"something@somain.c\",\n);\n  \nprint(pubKey);\n```\n\n### NIP13 hex difficulty\n\n```dart\nNostr.instance.utilsService.countDifficultyOfHex(\"002f\");\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanasfik%2Fnostr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanasfik%2Fnostr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanasfik%2Fnostr/lists"}