Skip to content

Commit

Permalink
docs: Integrating feedback to PR
Browse files Browse the repository at this point in the history
  • Loading branch information
martinheidegger committed Jul 5, 2022
1 parent 216eb96 commit 99bf808
Showing 1 changed file with 37 additions and 171 deletions.
208 changes: 37 additions & 171 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ head:

## Introduction

DNSLink is the specification of a format for [DNS][] [`TXT` records][TXT] that allows the association of [decentralized web][dweb] (dweb) resources with a [domain][].
DNSLink is the specification of a format for [DNS][] [`TXT` records][TXT] that allows the association of arbitrary content paths and identifiers with a [domain][].

![](./img/dns-query.png)

## How does it work?

When you register a domain at your name registrar (eg. namecheap.com) you can set `TXT` entries additionally to `A` or `CNAME`.
When you register a domain at your [name registrar][name-registrar] you can set `TXT` entries additionally to `A` or `CNAME`.

DNSLink specifies a format for `TXT` entries that allow you to _link_ dweb resources to that domain.
DNSLink specifies a format for `TXT` entries that allow you to _link_ resources to that domain.

Using just a DNS client, anyone can quickly lookup the link and request the resource through a decentralized network.

Here is an example for a valid DNSLink `TXT` entry:
Here is an example for a valid DNSLink `TXT` entry pointing at content root on IPFS:

```
dnslink=/ipfs/QmaCveU7uyVKUSczmiCc85N7jtdZuBoKMrmcHbnnP26DCx
Expand All @@ -35,7 +35,7 @@ _dnslink.dnslink-website.on.fleek.co.
"dnslink=/ipfs/QmaCveU7uyVKUSczmiCc85N7jtdZuBoKMrmcHbnnP26DCx"
```

Considering the subtle details of DNSLink however, it is better to use the `dnslink` CLI client provided with either [`@dnslink/js`][js] or [`dnslink-std/go`][go].
There is also a `dnslink` CLI client provided with either [JavaScript][js] or [GO][go] libraries that can be used for in-depth inspection and validation of DNSLink records.

```sh
> dnslink dnslink.dev
Expand All @@ -48,200 +48,67 @@ Considering the subtle details of DNSLink however, it is better to use the `dnsl
DNSLink entries are of the form:

```
dnslink=/<key>/<value>
dnslink=/<namespace>/<identifier>
```

- The prefix `dnslink=/` is there to signal that this `TXT` record value is a DNSLink. This is important because `TXT` records are used for many purposes that DNSLink should not interfere with.
- As there are more than one decentralized system, the `<key>` indicates which decentralized system you want to use.
- The `<value>` is the link you want to set. The `<value>` is any link you wish to use. It could be a URL or a path.
- The prefix `dnslink=` is there to signal that this `TXT` record value is a DNSLink. This is important because `TXT` records are used for many purposes that DNSLink should not interfere with.
- As there are more than one decentralized system, the value after `=` is an opaque "content path" where each segment is separated by `/`
- The the very first segment indicates `namespace` of the decentralized system you want to use for content resolution, and the remainder is an `identifier` specific to that system. To illustrate, in IPFS namespace is `ipfs` and identifier is a [CID](https://docs.ipfs.io/concepts/content-addressing/): `/ipfs/bafkqae2xmvwgg33nmuqhi3zajfiemuzahiwss`

### `_dnslink` subdomain

DNSLink entries can be specified directly on the domain or on the `_dnslink` subdomain. The entries specified in the **`_dnslink` subdomain are given priority!**

For example, lets say you specify two entries like this:

```shell
> dig +short TXT fictive.domain
dnslink=/ipfs/QmAAAA....AAAA

> dig +short TXT _dnslink.fictive.domain
dnslink=/ipfs/QmBBBB....BBBB
```

Looking up `fictive.domain` will return the entry of `_dnslink.fictive.domain`!

```shell
> dnslink fictive.domain
/ipfs/QmBBBB....BBBB [ttl=60]
```

Using a `_dnslink` subdomain prevents conflicts with `CNAME` and `ALIAS` records. It is also allows you to give control over your DNSLink records to a third party without giving away full control over the original DNS zone.

`TXT` records on the `domain` without the `_dnslink` subdomain are discouraged and exist for legacy reasons. We historically started with supporting records on the normal domain, and only found the issue with `CNAME` and `ALIAS` records later on.

It is _not required_ but strongly recommended to **set the dnslink entry on the `_dnslink` subdomain!**
DNSLink entries should be specified on the `_dnslink` subdomain.

### Multiple entries

#### Multiple `dnslink=` `TXT` entries per domain

There is more than one kind of dweb resource out there. [`ipfs`][ipfs] and [`hyper`][hyper] as just two popular examples. In order for domains to be able to provide multiple representations, you can specify more than one `dnslink=/<key>/<value>` TXT entry per domain. DNSLink compatible implementations are encouraged to return a result that groups results by `key`.
There is more than one kind of dweb resource out there. [`ipfs`][ipfs] and [`hyper`][hyper] as just two popular examples. Everyone is free to specify more than one `dnslink=/<namespace>/<identifier>` TXT entry per domain:

```js
const { links } = await resolveN('dnslink.dev')
links.ipfs //...
links.hyper //...
```console
$ dig +short TXT _dnslink.example.com
"dnslink=/ipfs/bafy.."
"dnslink=/hyper/.."
"dnslink=/foo/.."
```

#### Multiple values per key
#### Multiple values per namespace

Most dweb formats have a 1:1 relationship. For example, one domain is supposed to point to one `ipfs` resource. But some systems - like [multiaddr][] - may make use of multiple links to link more than one well-known peer. Other systems may add meta information. For that resason DNSLink clients need to return a list of links per `key`.

```js
Array.isArray(links.ipfs) // true if ipfs links are given.
```
Publishing more than one record for the same namespace is allowed by DNS clients and DNSLink libs and specs, but system responsible for processing those records is free to reject such state as invalid.

#### Sorting

DNSLink values need to be sorted alphanumerically by lower entries first. This is relevant because conventional DNS clients - like `dig` - do not need to return TXT entries in a sorted manner!

```sh
# OK
dnslink=/foo/B
dnslink=/foo/C
dnslink=/bar/A # Its okay for the key to be unsorted!

# NOT OK
dnslink=/foo/C # ... but the value needs to be sorted!
dnslink=/foo/B
dnslink=/bar/A
```

### Chaining and redirects
To ensure DNSLink records are processed in deterministic manner, make sure to sort TXT record values in sort them in alphabetical order. This is important because DNS TXT records are not sorted and can arrive in different order.

Additionally to the regular `CNAME` method for redirects, DNSLink supports redirects using the special `dnslink` key.

The redirect can be of the format:
### Redirects and delegation

```
dnslink=/dnslink/<redirect>
```
#### CNAME delegation

Here is an example for how a redirect works:
Use of `CNAME` is the suggested way for delegating DNSLink resolution from one DNS name to another.

```sh
> dig +short TXT _dnslink.t09.dnslink.dev
dnslink=/dnslink/b.t09.dnslink.dev

> dig +short TXT _dnslink.b.t09.dnslink.dev
dnslink=/ipfs/AADE
Example below delegates DNSLink record resolution for `example.com` to TXT records at `_dnslink.external-service.example.net`

> dnslink t09.dnslink.dev
/ipfs/AADE [ttl=3600]
```console
$ dig +noall +answer TXT _dnslink.example.com
_dnslink.dnslink.dev. 1612 IN CNAME _dnslink.external-service.example.net
_dnslink.external-service.example.net. 112 IN TXT "dnslink=/ipfs/bafkqae2xmvwgg33nmuqhi3zajfiemuzahiwss"
```

#### Precendence

Redirects take priority over other `dnslink=` entries! If a redirect is present the other entries will be ignored!
<!-- TODO: hide ALIAS section below for now - needs more explanation in context of regular A and gateways.
```sh
> dig +short TXT t14.dnslink.dev
"dnslink=/dnslink/1.t14.dnslink.dev"
"dnslink=/ipfs/mnop"
#### ALIAS delegation
> dig +short TXT 1.t14.dnslink.dev
"dnslink=/ipns/AALM"
DWeb protocols often use CNAME technique along with an `ALIAS` record to also delegate resolution of `A` and `AAAA` records used for HTTP gateway to some third-party CDN hosting for legacy HTTP clients:
> dnslink t14.dnslink.dev
/ipns/AALM [ttl=3600]
```console
$ dig +noall +answer TXT example.com
example.com. 118 IN TXT "ALIAS for http-gateway.example.net"
```
#### Limits to Redirects

DNSLink implementations need to limit redirects to **at most 32**. Counting even fallbacks from `_dnslink.<domain>` to `<domain>` as redirect. Redirects exceeding that number _(or are identified as circluar)_ are treated as errors.


The number of redirects needs to be consistent between all implementations for a predictable client behavior.

Redirects need to point to valid dns domains. This means that they can have at most `253` characters in total with `63` characters max per subdomain. No domain part may be empty.

```sh
# invalid: empty tld
dnslink=/dnslink/.
# invalid because of empty subdomain
dnslink=/dnslink/foo..bar
# too long subdomain (exceeds 63 chars)
dnslink=/dnslink/abc{ +54 chars }efg.com
# too long (exceeds 253 chars)
dnslink=/dnslink/_dnslink.abc{ +239 chars }efg
# too long because _dnslink. prefix is added
dnslink=/dnslink/abc{ +239 chars }efg
```

#### Paths

When specifying a redirect you may also specify a deep link on the redirect:

```
dnslink=/dnslink/target.domain/some/path?foo=bar
```

In this example **`/some/path?foo=bar`** is a deep link. DNSLink does not specify _how_ these paths are used by the different dweb formats. `ifps` may use these differently from `hyper`. You need to lookup the respective specification or implementation to see how paths are used.

The DNSLink implementations will return any given path specification as separate `path` list that needs to be combined by the user:

```js
const { links, path } = await resolveN('redirecting.domain')

path[0]
// {
// pathname: '/some/path',
// search: {
// foo: ['bar']
// }
// }
```

The standard libraries are encouraged to provide a `reducePath` function that can be used to combine/reduce a given path.

```js
const { links, path } = await resolveN('redirecting.domain')
reducePath(links.ipfs[0], path) // <value>/some/path?foo=bar
```

#### TTL's with redirects

When a redirect is followed, the `ttl` of all links found will be maxed at the `ttl` of the redirect entry.

### Encoding

DNSLink entries are limited to [ASCII][] characters. For Unicode/UTF-8 characters, DNSLink clients support [percent encoding][%-encoding] transparently.

```shell
# Invalid
dnslink=/aフ/bar
dnslink=/foo/bホ

# Valid
dnslink=/%E3%83%95%E3%82%B2/bar
dnslink=/foo/%E3%83%9B%E3%82%AC
```

The valid entries will be decoded by the libraries:

```javascript
const { links } = await resolveN('...')

deepEqual(links, {
'フゲ': ['bar'],
'foo': ['ホガ']
})
```

_Note:_ Technically `TXT` entries are transported as binary data and could be interpreted as UTF-8 characters. As the encoding outside the ASCII
space is not specified DNSLink does not allow these, to prevent compilations with other DNS tools.
-->

[name-registrar]: https://en.wikipedia.org/wiki/Domain_name_registrar
[DNS]: https://en.wikipedia.org/wiki/Domain_Name_System
[TXT]: https://en.wikipedia.org/wiki/TXT_record
[dweb]: https://en.wikipedia.org/wiki/Decentralized_web
Expand Down Expand Up @@ -547,10 +414,9 @@ Of course, DNS has shortcomings:
These are all good reasons to develop new, better name systems. But until those work better than DNS, you'll probably use DNS.
## Contribute
The DNSLink project is organized through github's ["dnslink-std" organization](https://github.com/dnslink-std/community).
## Community
See resources at [dnslink-std/community](https://github.com/dnslink-std/community).
### Contributors
Expand Down

0 comments on commit 99bf808

Please sign in to comment.