DNSLink on pkarr
A previous post explained how to integrate pkarr into our system’s name resolver. In this post we’ll use DNSLink to integrate it with IPFS.
DNSLink
DNSLink allows a DNS record to reference an arbitrary identifier for content outside the domain name system. Since pkarr uses DNS records, we can use DNSLink with pkarr too.
A DNSLink record is simply a TXT record with a
value like dnslink=/foo/bar (it could just as well have
been a new record type; but using TXT is easier).
The /foo/bar part is a multiformat, with a “namespace” and
identifier. By convention, namespaces come from the multicodec
table.
For this example we’ll use the ipfs namespace and a CID as
identifier.
Git in IPFS
As I showed in my Git in Nix via IPFS post, we can use IPFS to store Git data; so that’s what I’ll be using in this example:
$ mkdir example-repo
$ cd example-repo
$ git init -q
$ echo "Hello world" > README
$ git add README
$ git commit -m "Initial commit"
[master (root-commit) 51cfe18] Initial commit
1 file changed, 1 insertion(+)
create mode 100644 README
$ git2ipfs master
baf4bcfcrz7qytrzujpbtb4ucscqij23bqivplqq
baf4bcfca3ppvlx6uazkcerrmy5fjjesuv35jolq
baf4bcfeafgjmiiqn4gnja5t7gaakpgrrxggq35y
That git2ipfs tool comes from my git-on-ipfs repo: it puts a git object,
and everything it references, into a local Kubo node. In this case it
printed out three CIDs: one for the commit object, one for its tree and
one for the README blob.
We can resolve and traverse this data using Kubo’s ipfs
commands, e.g.
$ ipfs dag get /ipfs/baf4bcfcrz7qytrzujpbtb4ucscqij23bqivplqq/tree | jq .
{
"README": {
"hash": {
"/": "baf4bcfeafgjmiiqn4gnja5t7gaakpgrrxggq35y"
},
"mode": "100644"
}
}
For this example we’ll put the commit’s CID in our DNSLink record, so
its value will be
dnslink=/ipfs/baf4bcfcrz7qytrzujpbtb4ucscqij23bqivplqq.
DNSLink in pkarr
Now we need to construct a pkarr record and publish it to the Mainline DHT. First, our record will look like this:
$TTL 60
_dnslink IN TXT dnslink=/ipfs/baf4bcfcrz7qytrzujpbtb4ucscqij23bqivplqq
By convention, our DNSLink TXT record lives on a
_dnslink subdomain. We’ll write this to a file called
example.zone.
Next, we’ll use the pkdns-cli command (provided by
pkdns) to create a fresh pkarr address and publish this record to
it:
$ pkdns-cli generate > seed
$ pkdns-cli publickey seed
rthyxssxwcwnpkfnfq17woucenizkc868gt9ea6jdzufpyygpnuo
$ pkdns-cli publish seed example.zone
Packet rthyxssxwcwnpkfnfq17woucenizkc868gt9ea6jdzufpyygpnuo
Name TTL Type Data
_dnslink 60 TXT dnslink=/ipfs/baf4bcfcrz7qytrzujpbtb4ucscqij23bqivplqq
2026-05-08 01:21:40 UTC Successfully announced.
Now we should be able to resolve this pkarr record directly from the Mainline DHT:
$ pkdns-cli resolve rthyxssxwcwnpkfnfq17woucenizkc868gt9ea6jdzufpyygpnuo
Resolve dns records of pk:rthyxssxwcwnpkfnfq17woucenizkc868gt9ea6jdzufpyygpnuo
Packet rthyxssxwcwnpkfnfq17woucenizkc868gt9ea6jdzufpyygpnuo
Name TTL Type Data
_dnslink 60 TXT dnslink=/ipfs/baf4bcfcrz7qytrzujpbtb4ucscqij23bqivplqq
Last updated at: 2026-05-08 01:21:40 UTC
Great! Also, since the previous post configured our system to resolve pkarr addresses via pkdns, we can look up these records using ordinary DNS too:
$ dig +short TXT _dnslink.rthyxssxwcwnpkfnfq17woucenizkc868gt9ea6jdzufpyygpnuo
"dnslink=/ipfs/baf4bcfcrz7qytrzujpbtb4ucscqij23bqivplqq"
Finally, we can use Kubo to look up these records, then look up the DNSLink identifer, then traverse its contents:
$ ipfs dag get /ipns/rthyxssxwcwnpkfnfq17woucenizkc868gt9ea6jdzufpyygpnuo/tree | jq .
{
"README": {
"hash": {
"/": "baf4bcfeafgjmiiqn4gnja5t7gaakpgrrxggq35y"
},
"mode": "100644"
}
}
What’s going on here is the following:
dagtells Kubo to work at the level of linked-data (in our case, git objects). Rather than, say, the raw bytes of a git object (that would use theblockcommand, instead).gettells Kubo to show the contents on stdout. Rather than, say, the addresses (that would use theresolvecommand, instead).- The
ipnsnamespace tells Kubo to treat the identifier as a “name” rather than a CID. In this case, that “name” looks like a domain name, so Kubo will look up its DNSLink. - Since the DNSLink value
/ipfs/baf4bcfcrz7qytrzujpbtb4ucscqij23bqivplqquses a namespace (ipfs) that Kubo supports, it will next look up that identifier too. - Looking up the CID results in a Git commit object; which Kubo parses using its git codec.
treeis looked up in that Git commit object, to find the associated Git tree object.- Kubo serialises a JSON representation of that Git tree object to
stdout, which we pretty-print by piping through
jq
Note that the ipns namespace was originally used for IPNS, which is IPFS’s
own DHT-based name system. I tried to use IPNS a decade ago but I’ve always
found it quite unreliable; hence my more recent use of pkarr instead. At
the very least, I like how pkarr is a small, standalone piece of
technology which accomplishes one task and inter-operates with existing
systems; unlike IPNS, which tends to be strongly-coupled to Kubo, for
implementation; key management; etc.
In any case, it seems like DNSLink was created as a compatability
shim for using IPFS content on DNS Web sites; and was shoehorned into
the ipns namespace since that was already “resolving
names”. That makes even less sense in my setup since I’m not using the
usual DNS database. Subsequent work has resulted in more-precise
approaches, like multiaddr, which also
generalise to more protocols; so perhaps one day we can use a
pk namespace/multiaddr instead of the decreasingly-relevant
ipns?