DNSLink - linking content and services with dns

DNSLink is a very simple protocol to link content and services directly from DNS.

DNSLink leverages the powerful distributed architecture of DNS for a variety of systems that require internet scale mutable names or pointers.

How does it work?

With DNSLink, you can store a link using any DNS domain name. First, put the link in a TXT record at a specific subdomain. Then, you can resolve the link from any program by looking up the TXT record value. Your programs and systems can parse out the record value, and follow the link wherever it may go. Yes, it is that simple.

DNSLink values are of the form:

dnslink=<value>

As RFC 1034 says:

Of course, by the robustness principle, domain software should not fail when presented with CNAME chains or loops; CNAME chains should be followed and CNAME loops signalled as an error.

Tools should support DNSLink chaining and detect loops as errors.

> dig +short TXT _dnslink.test3.dnslink.dev
dnslink=/dnslink/test4.dnslink.dev

Should go on to resolve

> dig +short TXT _dnslink.test4.dnslink.dev
dnslink=/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU

Tutorial

In this tutorial, we'll link to the libp2p website, on IPFS. The website has the IPFS address:

/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU

You can view this on the global IPFS gateway: https://ipfs.io/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU

And even download it with ipfs:

ipfs get /ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU

For this tutorial, I'll use the domain name libp2p.io. You can use whatever domain you control. I happen to be using an ALIAS record on this domain as well (to make the website load directly via the IPFS gateway), so i'll also have to prefix the domain with _dnslink..

So the full domain name I'll set the record on is: _dnslink.libp2p.io.

Let's set the link by creating a TXT record on the domain name. This is going to be specific to your DNS tooling. Unfortunately there is no standard cli tool or web service to set domain names :(. Every registrar and name server has its own web app, and API tooling. (yes, this is widely regarded as a poor state of affairs).

Consider setting the record, via an imaginary dns cli tool:

> my-dns-tool set --type=TXT --ttl=60 --domain=libp2p.io --name=_dnslink --value="dnslink=/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU"
_dnslink.libp2p.io TXT 60 dnslink=/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU

Or directly in a Digital Ocean prompt:

Now, let's try resolving the link!

You can get the link value manually using dig or another dns lookup tool:

> dig +short TXT _dnslink.libp2p.io
dnslink=/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU

Extract the value with sed:

> dig +short TXT _dnslink.libp2p.io | sed -E 's/"dnslink=(.*)"/\1/g'
/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU

Then, you can feed the output of that to ipfs:

> ipfs ls /ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU
QmeotoX2bE7fVgMvUS9ZXL2RMoZQHiuQVZZR1Hts3JVmUT 265   _previous-versions
QmP5JpytsFEQ5Y8oQ875kPrdA1dAtyXAH6U8eKbTUbptNd -     bundles/
QmcRUrMyFePBNnvoqZB5Uk7y6aoWGoUW6EB8JWUxztKTma -     categories/
QmeVM9YZStiFcjjQYpzJ1KWJsaFGcRWaeMAymSfLydu9mh -     css/
QmRJjyE1Bi64AD7MSpt4xKHaiWXq7WGAne8KTzn7UyYeWq -     fonts/
Qma6Eg1JMAPjBg827ywDG1nx4TBwxYWxQxeb1CXUkDnrHk -     img/
QmdB9xXJHNXnaiikCXVpomHriNGXwvSUqdkC1krtFq4WWW -     implementations/
QmXCq4KMZC4mdxovpnrHU9K92LVBLSExLEsrvwTGNEswnv 62880 index.html
QmQNjTorGWRTqEwctqmdBfuBBRTj8vQD3iGjNNCu7vA5i9 3138  index.xml
QmPsosZeKZTUcBkcucPtPnk3fF4ia4vBdJ6str9CRr6VTQ -     js/
QmYBUY8Y8uXEAPJSvMTdpfGoL8bujNo4RKoxkCnnKXoTD9 -     media/
QmUZ23DEtL3aUFaLgCEQv5yEDigGP2ajioXPVZZ6S7DYVa 561   sitemap.xml
QmRgig5gnP8XJ16PWJW8qdjvayY7kQHaJTPfYWPSe2BAyN -     tags/

Or chain it all together:

> dig +short TXT _dnslink.libp2p.io | sed -E 's/"dnslink=(.*)"/\1/g' | ipfs ls
QmeotoX2bE7fVgMvUS9ZXL2RMoZQHiuQVZZR1Hts3JVmUT 265   _previous-versions
QmP5JpytsFEQ5Y8oQ875kPrdA1dAtyXAH6U8eKbTUbptNd -     bundles/
QmcRUrMyFePBNnvoqZB5Uk7y6aoWGoUW6EB8JWUxztKTma -     categories/
QmeVM9YZStiFcjjQYpzJ1KWJsaFGcRWaeMAymSfLydu9mh -     css/
QmRJjyE1Bi64AD7MSpt4xKHaiWXq7WGAne8KTzn7UyYeWq -     fonts/
Qma6Eg1JMAPjBg827ywDG1nx4TBwxYWxQxeb1CXUkDnrHk -     img/
QmdB9xXJHNXnaiikCXVpomHriNGXwvSUqdkC1krtFq4WWW -     implementations/
QmXCq4KMZC4mdxovpnrHU9K92LVBLSExLEsrvwTGNEswnv 62880 index.html
QmQNjTorGWRTqEwctqmdBfuBBRTj8vQD3iGjNNCu7vA5i9 3138  index.xml
QmPsosZeKZTUcBkcucPtPnk3fF4ia4vBdJ6str9CRr6VTQ -     js/
QmYBUY8Y8uXEAPJSvMTdpfGoL8bujNo4RKoxkCnnKXoTD9 -     media/
QmUZ23DEtL3aUFaLgCEQv5yEDigGP2ajioXPVZZ6S7DYVa 561   sitemap.xml
QmRgig5gnP8XJ16PWJW8qdjvayY7kQHaJTPfYWPSe2BAyN -     tags/

Usage Examples

Example: User-friendly name resolution within IPFS

IPFS has DNSLink resolution built in though, so you could just do the following to achieve the same result as in the tutorial above.

Given this dnslink:

> dig +short TXT _dnslink.libp2p.io
dnslink=/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU

ipfs uses DNSLink to resolve it natively:

> ipfs ls /ipns/libp2p.io
QmeotoX2bE7fVgMvUS9ZXL2RMoZQHiuQVZZR1Hts3JVmUT 265   _previous-versions
QmP5JpytsFEQ5Y8oQ875kPrdA1dAtyXAH6U8eKbTUbptNd -     bundles/
QmcRUrMyFePBNnvoqZB5Uk7y6aoWGoUW6EB8JWUxztKTma -     categories/
QmeVM9YZStiFcjjQYpzJ1KWJsaFGcRWaeMAymSfLydu9mh -     css/
QmRJjyE1Bi64AD7MSpt4xKHaiWXq7WGAne8KTzn7UyYeWq -     fonts/
Qma6Eg1JMAPjBg827ywDG1nx4TBwxYWxQxeb1CXUkDnrHk -     img/
QmdB9xXJHNXnaiikCXVpomHriNGXwvSUqdkC1krtFq4WWW -     implementations/
QmXCq4KMZC4mdxovpnrHU9K92LVBLSExLEsrvwTGNEswnv 62880 index.html
QmQNjTorGWRTqEwctqmdBfuBBRTj8vQD3iGjNNCu7vA5i9 3138  index.xml
QmPsosZeKZTUcBkcucPtPnk3fF4ia4vBdJ6str9CRr6VTQ -     js/
QmYBUY8Y8uXEAPJSvMTdpfGoL8bujNo4RKoxkCnnKXoTD9 -     media/
QmUZ23DEtL3aUFaLgCEQv5yEDigGP2ajioXPVZZ6S7DYVa 561   sitemap.xml
QmRgig5gnP8XJ16PWJW8qdjvayY7kQHaJTPfYWPSe2BAyN -     tags/

You can find out more at the IPFS DNSLink documentation.

Example: IPFS Gateway

You can also just check it out on the web. The IPFS gateway resolves DNSLink automatically too. Check it out at https://ipfs.io/ipns/libp2p.io

How does that work? The gateway takes the part after /ipns/, if it is a DNS domain name, it checks for a DNSLink at either the domain name, or _dnslink. prefixed version. In this case it finds our DNSLink at _dnslink.libp2p.io and resolves that.

But what about https://libp2p.io? Yes, https://libp2p.io also works, that uses a combination of DNSLink, a ALIAS record in libp2p.io, and the ipfs gateway. Basically:

  1. the browser first checks for A records for libp2p.io. dns finds an ALIAS to gateway-int.ipfs.io, and those A records:
     > dig A libp2p.io
     gateway.ipfs.io.        119     IN      A       209.94.90.1
  2. the browser then connects to http://209.94.90.1, using a HOST: libp2p.io HTTP header.
  3. the ipfs gateway reads the HOST: libp2p.io header, and -- recognizing a DNS name -- checks for an associated DNSLink at either libp2p.io or _dnslink.libp2p.io.
     > dig +short TXT _dnslink.libp2p.io
     "dnslink=/ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU"
  4. The gateway finds the link at _dnslink.libp2p.io leading to /ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU.
  5. The gateway fetches the IPFS web content at /ipfs/Qmc2o4ZNtbinEmRF9UGouBYTuiHbtCSShMFRbBY5ZiZDmU and serves it to the browser.
  6. The browser renders it happily, preserving the original pretty name of https://libp2p.io

Example: IPFS Companion

Similar to the IPFS Gateway, IPFS Companion uses DNSLink natively within the browser to resolve IPFS web content. IPFS Companion has a feature that tests domain names for the presence of dnslink TXT records, and if it finds them, then it serves content via IPFS instead.

You can find out more about how it works at IPFS Companion's DNSLink documentation.

Tools

There are a number of tools related to DNSLink.

https://github.com/ipfs/go-dnslink - dnslink resolution in go (used by go-ipfs)

https://www.npmjs.com/package/dnslink - dnslink resolution in javascript

https://github.com/ipfs/dnslink-deploy - dnslink-deploy a tool for setting DNSLinks on Digital Ocean (and maybe more someday others):

Docs

Known Users

Systems

IPFS

IPFS is a heavy user of DNSLink. It is used in the core API, as part of IPNS, the name system IPFS uses. It is also used in a variety of tools around IPFS.

Learn more at the IPFS and DNSLink documentation.

IPFS Gateway

The IPFS Gateway resolves DNSLinks automatically.

IPFS Companion

IPFS Companion can resolve DNSLinks automatically. See more in the IPFS Companion documentaion.

add yours here

Add yours on github.

Websites

Many websites use DNSLink and IPFS. Check some of them out:

Best Practices

Set a low TTL in the TXT record.

The default TTL for DNS records is usually 3600, which is 1hr. We recommend setting a low TTL in the TXT record, something like 60 seconds or so. This makes it so you can update your name quickly, and use it for website deploys.

FAQ

Is there an IETF RFC spec?

Not yet. We should write one.

Yes absolutely. You can use these formats wherever you want, and map the functionality to that service. We would say that's a derivative system though, and not DNSLink proper. For example, you can use DNSLink with the Ethereum Name System (ENS), and use the same value format. One can still support looking but both name.ens and _dnslink.name.ens. But since ENS doesn't use records (like TXT), that part would be obviated. If you use DNSLink for something cool like this, be sure to add it to the Users section of this doc.

Why not just use _dnslink.domain always, instead of both _dnslink.domain and domain?

Some domain names are very long. Since DNS has a character limit, it could be that the domain name is far too long to support adding the _dnslink. prefix. We also historically started with supporting it in the normal domain, and only found the issue with CNAME and ALIAS records later on.

The prefix dnslink= is there to signal that this TXT record value is a DNSLink. This is important because many systems use TXT records, and there is a convention of storing multiple space separated values in a single TXT record. Following this format allows your DNSLink resolver to parse through whatever is in the TXT record and use the first entry prefixed with dnslink=.

Why not use other DNS record types, like SRV?

Special purpose records enforce some structure to their values, and these are not flexible enough. We wanted a simple protocol that allowed us to link to any other system or service, in perpetuity. This can only be achieved with systems designed to allow structure within the input. The TXT record is a good example of this -- it imposes a string value, but it allows the user to make sense out of anything within that string. Sets of users can imposese different rules for resolution. In our case, this is handled through Multiaddr.

Why not just extend DNS with more record types?

It seems that what DNSLink does could be accomplished by a verity of new special purpose record types. But extending DNS is a very expensive process -- in terms of time and adoption. We wanted to find a way to use all the existing infrastructure without requiring any changes at all.

Why DNS?

The Domain Name System (DNS) is a hierarchical and decentralized naming system for computers, services, or other resources connected to the Internet or a private network. ... By providing a worldwide, distributed directory service, the Domain Name System has been an essential component of the functionality of the Internet since 1985.

DNS is:

As a name system, DNS has pragmatic solutions for:

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.

Contributors

Developed with the support of Protocol Labs

Acknowledgements

This website is based heavily on underscorejs.org.