Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 43 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ This is useful for services and individuals who wish to associate their Nostr pu

A `kind 31034` event is used.

The `content` SHOULD be the empty string and is reserved for future use. It could contain strings similar to a DNS record.
The `content` MAY be the empty string, or MAY contain a DNS record set as described in [DNS record set](#dns-record-set) below.

The following tags are defined as REQUIRED:

Expand All @@ -44,11 +44,50 @@ Example event:
}
```

Services and tools that wish to make use of this NIP SHOULD first verify the authenticity of the event by checking the signature and then map the domain name specified in the content to `npub.nostr` or `pubkey.nostr`.
Services and tools that wish to make use of this NIP SHOULD first verify the authenticity of the event by checking the signature and then resolve `<npub>.nostr` or `<pubkey>.nostr` to the domain name specified in the `u` tag.

## DNS record set

The `content` field MAY contain a DNS record set, making the event a complete signed zone for `<pubkey>.nostr` (or `<npub>.nostr`). Because Nostr events are signed, this gives signed record sets in the manner of PKARR's `SignedPacket` without any additional machinery.

The record set is encoded as a JSON object with a `records` array. Each record is an array of four elements:

```
[type, name, ttl, data]
```

* `type` - the DNS record type as a string. Implementations MUST support `A`, `AAAA`, `CNAME`, `TXT` and `SRV`. Records with unknown types MUST be ignored, allowing future extension.
* `name` - the record name, relative to the `<pubkey>.nostr` zone. `@` denotes the zone apex. Names MUST NOT be fully qualified.
* `ttl` - time to live in seconds, as a non-negative integer. Resolvers MAY cap TTLs at a policy-defined maximum.
* `data` - the record data in DNS presentation format, as it would appear in a zone file (e.g. `203.0.113.7` for `A`, `10 5 443 relay.example.com.` for `SRV`).

Example event:

```json
{
"kind": 31034,
"pubkey": "de7ecd1e2976a6adb2ffa5f4db81a7d812c8bb6698aa00dcf1e76adb55efd645",
"created_at": 1751462400,
"content": "{\"records\":[[\"A\",\"@\",3600,\"203.0.113.7\"],[\"AAAA\",\"@\",3600,\"2001:db8::7\"],[\"CNAME\",\"www\",3600,\"example.com.\"],[\"TXT\",\"@\",3600,\"\\\"hello nostr\\\"\"],[\"SRV\",\"_relay._tcp\",3600,\"10 5 443 relay.example.com.\"]]}",
"tags": [
["d", ""],
["u", "https://example.com"]
],
"sig": "exampleSignature"
}
```

A `content` that is empty or does not parse as a JSON object with a `records` array MUST be treated as an empty record set; the `u` tag mapping continues to apply regardless.

### Freshness and conflict resolution

Kind `31034` falls in the parameterized replaceable range: relays SHOULD retain only the newest event per `pubkey` and `d` tag, and clients SHOULD select the event with the highest `created_at` when more than one is returned. This serves the same role as PKARR's BEP44 sequence numbers.

Resolvers SHOULD cache each verified record for its own `ttl` (optionally capped), and MAY apply a freshness policy that treats record sets as stale when `created_at` is older than a policy-defined age, prompting a re-query of relays.

## Implementation

In order to lookup a .nostr domain you simply query the pubkey with kind=31034 and use the field in the content as the domain. Some lookup services may choose to fallback to profile web page or nip-05 origin, as desired.
In order to lookup a .nostr domain you simply query the pubkey with kind=31034 and the `d` tag set to the empty string, then use the `u` tag as the domain, treating the `content` (if present) as the DNS record set. Some lookup services may choose to fallback to profile web page or nip-05 origin, as desired.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 48b5446 — the Use Case section now verifies the u tag, with content as the optional record set.


### did:nostr resolution

Expand Down Expand Up @@ -80,7 +119,7 @@ The default resolver is `https://nostr.social` and can be overridden with the `D

By having a Nostr public key mapped to a domain name, users and services can easily share, reference, or verify the authenticity of a domain based on its associated Nostr public key.

For instance, if Alice wants to verify that `example.com` is genuinely associated with a specific Nostr public key, she can look up the Nostr event with `kind 31034` and verify the domain name in the content.
For instance, if Alice wants to verify that `example.com` is genuinely associated with a specific Nostr public key, she can look up the Nostr event with `kind 31034` and verify the domain name in the `u` tag (and, optionally, any DNS records in the `content`).

## Related Work

Expand Down