Service provider
Name: Nostr
Short description: Notes and other stuff transmitted by relays.
Website: https://nostr.com/
API documentation: N/A
Proposed verification mechanism
The user should create a note with the content being the proof, and then copy the event ID in the NIP-19 format, and the claim is formatted as an NIP-21 URI.
A new fetcher is needed, that is basically just a wrapper for the wonderful nostr-tools library.
Fetcher
- Decodes the
eventId using nip19.decode(). It ensures the type is 'nevent' and extracts:
- the underlying hex event ID (
data.id)
- any relay URLs listed inside the NIP-19 data (
data.relays)
- Merges relay lists from the
eventId and fallback list, deduplicating them.
- Queries relays using a
SimplePool:
- First, it requests the specific event (
ids: [eventIdHex])
- If successful, it encodes the author’s pubkey as an NIP-19
npub1 string
- Fetches the author profile (kind
0) to look for attached metadata.
- If the profile’s
content contains a nip05 field, it queries it with nip05.queryProfile() to check if the declared pubkey matches the event’s author.
- Assembles a result object with:
- the raw event
- the encoded
npub of the author
- the profile (if available)
- the
nip05 identifier (if validated)
Service provider
-
Regex check:
The claim is validated using the RegEx /^nostr:(nevent1[ac-hj-np-z02-9]+)$/.
This ensures:
- The claim follows the NIP-21 URI format (
nostr: prefix).
- The embedded NIP-19 identifier is of type
nevent1 (event reference).
- Note: Bech32 encoding used in NIP-19 excludes characters
1, b, i, and o.
-
Fallback Relays:
A predefined set of relay URLs is provided as a fallback:
- wss://nostr-pub.wellorder.net
- wss://saltivka.org
- wss://relay.damus.io
- wss://relay.nostr.bg
- wss://nostr.wine
- wss://nos.lol
- wss://nostr.mom
- wss://atlas.nostr.land
- wss://relay.snort.social
- wss://offchain.pub
- wss://relay.primal.net
- wss://relay.nostr.band
- wss://public.relaying.io
-
Service provider object:
The object includes:
- Profile info: Display name initially set as
'Unknown username', and profile URI initially set as null, updated later
- Proof setup:
- The proof link points to the event on https://njump.me
- Retrieval is handled using the newly created
Fetcher.NOSTR, passing the NIP-19 event ID and fallback relays
- Validation checks that the fetched
event.content equals the expected proof string
-
Postprocessing:
In the postprocess phase:
- We determine a unique
userId:
- If the profile contains a valid NIP-05 identifier, we use that.
- Otherwise, we fall back to the NIP-19 encoded public key (
npub1...).
- The profile's display name is updated to this
userId.
- The profile URI is set to the user ID on njump.me.
- NIP-19 event IDs MAY contain additional metadata such as relays
- List of fallback relays taken directly from
relay-config.json on the fiatjaf/njump repo on GitHub.
- Nostr has no official logos, but the community has made some. I will use the icons from
mbarulli/nostr-logo on GitHub for keyoxide-brands
Tasks
### Service provider
Name: Nostr
Short description: **N**otes and **o**ther **s**tuff **t**ransmitted by **r**elays.
Website: https://nostr.com/
API documentation: N/A
### Proposed verification mechanism
The user should create a note with the content being the proof, and then copy the event ID in the [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md) format, and the claim is formatted as an [NIP-21](https://github.com/nostr-protocol/nips/blob/master/21.md) URI.
A new fetcher is needed, that is basically just a wrapper for the wonderful [`nostr-tools`](https://github.com/nbd-wtf/nostr-tools/tree/master) library.
#### Fetcher
1. **Decodes the `eventId`** using `nip19.decode()`. It ensures the type is `'nevent'` and extracts:
* the underlying hex event ID (`data.id`)
* any relay URLs listed inside the NIP-19 data (`data.relays`)
2. **Merges relay lists** from the `eventId` and fallback list, deduplicating them.
3. **Queries relays** using a [`SimplePool`](https://github.com/nbd-wtf/nostr-tools/blob/924075b803dc75c8ebfd5278bce20d8175400fbd/pool.ts#L16):
* First, it requests the specific event (`ids: [eventIdHex]`)
* If successful, it encodes the author’s pubkey as an NIP-19 `npub1` string
4. **Fetches the author profile** (kind `0`) to look for attached metadata.
* If the profile’s `content` contains a `nip05` field, it queries it with `nip05.queryProfile()` to check if the declared pubkey matches the event’s author.
5. **Assembles a result object** with:
* the raw event
* the encoded `npub` of the author
* the profile (if available)
* the `nip05` identifier (if validated)
#### Service provider
1. **Regex check:**
The claim is validated using the RegEx `/^nostr:(nevent1[ac-hj-np-z02-9]+)$/`.
This ensures:
* The claim follows the NIP-21 URI format (`nostr:` prefix).
* The embedded NIP-19 identifier is of type `nevent1` (event reference).
* **Note:** [Bech32 encoding ](https://river.com/learn/terms/b/bech32/) used in NIP-19 excludes characters `1`, `b`, `i`, and `o`.
2. **Fallback Relays:**
A predefined set of relay URLs is provided as a fallback:
* wss://nostr-pub.wellorder.net
* wss://saltivka.org
* wss://relay.damus.io
* wss://relay.nostr.bg
* wss://nostr.wine
* wss://nos.lol
* wss://nostr.mom
* wss://atlas.nostr.land
* wss://relay.snort.social
* wss://offchain.pub
* wss://relay.primal.net
* wss://relay.nostr.band
* wss://public.relaying.io
3. **Service provider object:**
The object includes:
* **Profile info:** Display name initially set as `'Unknown username'`, and profile URI initially set as `null`, updated later
* **Proof setup:**
- The proof link points to the event on https://njump.me
- Retrieval is handled using the newly created `Fetcher.NOSTR`, passing the NIP-19 event ID and fallback relays
- Validation checks that the fetched `event.content` equals the expected proof string
4. **Postprocessing:**
In the postprocess phase:
* We determine a unique `userId`:
* If the profile contains a valid NIP-05 identifier, we use that.
* Otherwise, we fall back to the NIP-19 encoded public key (`npub1...`).
* The profile's display name is updated to this `userId`.
* The profile URI is set to the user ID on [njump.me](https://njump.me).
### Remarks
* NIP-19 event IDs [MAY](https://www.rfc-editor.org/rfc/rfc2119.html) contain additional metadata such as relays
* List of fallback relays taken directly from [`relay-config.json`](https://github.com/fiatjaf/njump/blob/master/relay-config.json.sample) on the [`fiatjaf/njump`](https://github.com/fiatjaf/njump) repo on GitHub.
* Nostr has no official logos, but the community has made some. I will use the icons from [`mbarulli/nostr-logo`](https://github.com/mbarulli/nostr-logo) on GitHub for `keyoxide-brands`
### Tasks
<!-- Leave the following unchecked -->
- [ ] Verification mechanism tested
- [ ] Added to [doip-js](https://codeberg.org/keyoxide/doipjs)
- [ ] Added to [doip-rs](https://codeberg.org/keyoxide/doip-rs)
- [ ] Added proxy routes (if needed)
- [ ] Added to [keyoxide-brands](https://codeberg.org/keyoxide/keyoxide-brands)
- [ ] Added to [documentation](https://codeberg.org/keyoxide/keyoxide-docs)