The GNU Name System#
The GNU Name System (GNS) is secure and decentralized naming system. It allows its users to register names as top-level domains (TLDs) and resolve other namespaces within their TLDs.
GNS is designed to provide:
Secure name resolution
Compatibility with DNS
Unlike DNS, GNS does not rely on central root zones or authorities. Instead any user administers their own root and can can create arbitrary name value mappings. Furthermore users can delegate resolution to other users’ zones just like DNS NS records do. Zones are uniquely identified via public keys and resource records are signed using the corresponding public key. Delegation to another user’s zone is done using special delegation records and petnames. A petname is a name that can be freely chosen by the user. This results in non-unique name-value mappings as www.bob to one user might be www.friend for someone else.
For a complete specification of the protocol, we refer to LSD0001.
In GNS you are the master of your own Root.
This means that you are able (and encouraged!) to manage your own Start Zones.
A Start Zone is a mapping from a name suffix (e.g.
a GNS zone key.
A user’s configuration of Start Zones is the equivalent of DNS’s Root Zone.
In other words, in GNS you can be your very own ICANN.
There are three types of Start Zones:
Mappings to remote GNS zones.
Mappings to your own local GNS zones.
Explicit zone Top-Level-Domains (zTLDs)
Your GNUnet installation ships with a default configuration of remote
The first is
.gnunet.org, which points to the authoritative zone of the
This Start Zone allows you to resolve names ending with
It can be used to resolve, for example,
You can try it out for yourself (if you are connected to the peer-to-peer network):
$ gnunet-gns --lookup=www.gnunet.org
Another Start Zone configured by default is
It points to a special zone also managed by the GNUnet project.
Users may register subdomains on a first-come first-served-basis at https://fcfs.gnunet.org.
gnunet-config -s gns to view the GNS configuration, including all
configured external zones that are operated by other users.
The respective configuration entry names start with a
You can change this default configuration at any time and add Start Zone to the respective zones of your friends! For this, simply obtain the respective public key (you will learn how below) and extend the configuration:
$ gnunet-config -s gns -o .myfriend -V PUBLIC_KEY
In order to create a local GNS zone you have to create a so-called
ego in GNUnet.
Egos may correspond to pseudonyms or real-world identities.
It is recommended to use separate egos for separate activities.
All egos can potentially serve as a GNS zone, but you do not have to use
all egos as GNS zones.
All your local zones are used as Start Zones.
Technically, an ego is first of all a public-private key pair, and thus egos also always correspond to a GNS zone. Egos are managed through the IDENTITY service and its tooling. The IDENTITY service is used to create and stores private keys with associated human-readable nicknames. Those mappings serve as local Start Zones.
For example, you can create a new ego (or zone) with name myzone using the
gnunet-identity tool using:
$ gnunet-identity --create="myzone"
Henceforth, on your system you control the TLD
You are able to resolve GNS names ending with
However, the zone is still empty.
We will look at local zone maintenance in the next section.
Note that by default, this will create ECDSA key pairs.
GNS also supports the use of EdDSA keys.
In order to create a EdDSA zone, execute:
$ gnunet-identity --create="myedzone" --eddsa
All of your zones can be listed (displayed) using the gnunet-identity command line tool as well:
$ gnunet-identity --display
If you know the public key of a GNS zone, you can resolve records in that zone
without a Start Zone mapping.
To do so, you must provide the string-representation of the zone key
as Top-Level Domain. Consider the zone key of
.gnunet.org in the default
configuration. In order to resolve the same record as above using its respective
zTLD, you can do so as follows:
$ gnunet-gns --lookup=www.000G0047M3HN599H57MPXZK4VB59SWK4M9NRD68E1JQFY3RWAHDMKAPN30
The use of zTLDs is mostly useful in the absence of a Start Zone configuration for that zone or when querying names programmatically.
Local zone maintenance#
So you are ready to maintain your own GNS zone? Great.
First you should review and optionally configure your NAMESTORE database which
will hold your zone’s resource records.
You have the choice between
postgres as databases.
Most users will be served well with the
sqlite backend and with its default
To change the database backend of your NAMESTORE to
$ gnunet-config -s namestore -o DATABASE -V postgres
You can now configure the
postgres database backend to find your database.
namestore-postgres configuration section on possible options:
$ gnunet-config -s namestore-postgres
If you ever need to reset your zone database, you can use
DANGER: This command will delete ALL of your records in all zones!
It can happen that you accidentally or semi-accidentally delete your ego, e.g.
In this case the records stored in the zone are still in the database but the
missing ego causes them to be orphaned.
Orphaned records are still published in the DHT but access to them is limited. For example, managing records through the NAMESTORE API is not longer possible.
You can check for orphaned records using:
$ gnunet-namestore --list-orphans
This will output any orphaned records in the format
In order to recover (and un-orphan) the records, you can re-add the ego by executing
$ gnunet-identity --create=<myzone> --privkey=$PRIVATE_KEY
If instead you should decide that you want to purge all orphaned records, you can execute
$ gnunet-namestore --purge-orphans
At this point you can add (or edit, or remove) records in your GNS zone using the using the gnunet-namestore command-line tool. Your records will be stored in a database of the NAMESTORE service. Note that for multi-user setups, the NAMESTORE database will include the combined records of all users. However, users will not be able to see each other’s records if they are marked as private.
To provide a short example for editing your own zone, suppose you have
your own web server with the IP
220.127.116.11. Then you can put an A record (A
records in DNS are for IPv4 IP addresses) into your local zone
using the command:
$ gnunet-namestore --zone=myzone \ --add \ --name=www \ --type=A \ --value=18.104.22.168 \ --expiration=1d
Similar commands will work for other types of DNS and GNS records, the syntax largely depending on the type of the record. Naturally, most users may find editing the zones using the gnunet-namestore-gtk GUI to be easier.
Note that by default, records are private. This means that they are not
published and cannot be resolved by other users that may have configured your
zone as one of their
In order to create a public record, provide the
--public switch to the
gnunet-namestore command above.
You can now try to resolve your own GNS record. The method we found
to be the most uncomplicated is to do this by explicitly resolving using
In the shell, type:
$ gnunet-gns -u www.myzone
You should receive the record as configured above.
At this point, you can try addition additional records to
www or add other
names to your zone.
Each zone in GNS has a public-private key. Usually, gnunet-namestore and gnunet-setup will access your private key as necessary, so you do not have to worry about those. What is important is your public key (or rather, the hash of your public key), as you will likely want to give it to others so that they can securely link to you.
A central operation in GNS is the ability to securely delegate to other zones. Basically, by adding a delegation you make all of the names from the other zone available to yourself. This section describes how to create delegations.
Suppose you have a friend who you call ’bob’ who also uses GNS. You can then delegate resolution of names to Bob’s zone by adding a PKEY record to their local zone:
$ gnunet-namestore -a -n bob --type PKEY -V $BOBKEY -e 1d -z myzone
$BOBKEY in the command above must be replaced with the hash of
Bob’s public key (the output your friend obtained using the
gnunet-identity command from the previous section and told you, for
example by giving you a business card containing this information as a
Assuming Bob has an
A record for their website under the name of
in his zone, you can then access Bob’s website under
as well as any (public) GNS record that Bob has in their zone by
replacing www with the respective name of the record in Bob’s zone.
Furthermore, if Bob has themselves a (public) delegation to Carol’s zone under carol, you can access Carol’s records under NAME.carol.bob.myzone (where NAME is the name of Carol’s record you want to access).
You can find more detailed information in available record types in the Resource Records section.
Now, in the situation of an attacker gaining access to the private key of one of your egos, the attacker can create records in the respective GNS zone and publish them as if you published them. Anyone resolving your domain will get these new records and when they verify they seem authentic because the attacker has signed them with your key.
To address this potential security issue, you can pre-compute a revocation certificate corresponding to your ego. This certificate, when published on the P2P network, flags your private key as invalid, and all further resolutions or other checks involving the key will fail.
A revocation certificate is thus a useful tool when things go out of
control, but at the same time it should be stored securely. Generation
of the revocation certificate for a zone can be done through
gnunet-revocation. For example, the following command (as unprivileged
user) generates a revocation file
revocation.dat for the zone
$ gnunet-revocation -f revocation.dat -R myzone
The above command only pre-computes a revocation certificate. It does
not revoke the given zone. Pre-computing a revocation certificate
involves computing a proof-of-work and hence may take up to 4 to 5 days
on a modern processor. Note that you can abort and resume the
calculation at any time. Also, even if you did not finish the
calculation, the resulting file will contain the signature, which is
sufficient to complete the revocation process even without access to the
private key. So instead of waiting for a few days, you can just abort
CTRL-C, backup the revocation certificate and run the calculation
only if your key actually was compromised. This has the disadvantage of
revocation taking longer after the incident, but the advantage of saving
a significant amount of energy. So unless you believe that a key
compromise will need a rapid response, we urge you to wait with
generating the revocation certificate. Also, the calculation is
deliberately expensive, to deter people from doing this just for fun (as
the actual revocation operation is expensive for the network, not for
the peer performing the revocation).
To avoid TL;DR ones from accidentally revoking their zones, we are not
giving away the command, but it is uncomplicated: the actual revocation
is performed by using the
-p option of
One should always backup their files, especially in these SSD days (our team has suffered 3 SSD crashes over a span of 2 weeks). Backing up peer identity and zones is achieved by copying the following files:
The peer identity file can be found in
The private keys of your egos are stored in the directory
~/.local/share/gnunet/identity/egos/. They are stored in files whose
filenames correspond to the zones’ ego names. These are probably the
most important files you want to backup from a GNUnet installation.
Note: All these files contain cryptographic keys and they are stored without any encryption. So it is advisable to backup encrypted copies of them.
This does NOT backup your record data. Your record data is stored in the respective database backend, and hence you may want to backup the respective databases as well.
Large zones in DNS such as Top-Level Domains can grow to millions of names.
For example, the
.fr DNS zone contained over 3.9 million entries in 2022.
Such use cases require good performance and careful consideration of a couple
of choices in GNS and NAMESTORE.
The cryptographic operations of EdDSA are significantly faster than their ECDSA counterparts. Most users will be unable to observe this difference. But, if you plan on managing large zones, you may want to consider using EdDSA zones.
For large zones, using the PostgreSQL NAMESTORE database backend is a must. Further, when importing large numbers of records at once, it is advised to make use of the bulk import functionality of the NAMESTORE API. There is a C API for bulk imports. There is also a REST API endpoint which calls this API.
Zones are published and signed by the ZONEMASTER service. In order to speed up this process for large zones, you can edit the number of spawned worker threads for the service:
$ gnunet-config -s zonemaster -o WORKER_COUNT -V $COUNT
It is reasonable to choose your number of CPUs as
Once you have verified that resolution of names using
we can start integrating GNS with applications.
We can distinguish between two types of applications:
GNS-unaware applications: Applications that implicitly assume the names are resolved through DNS. Such applications use OS-specific APIs or query configured DNS servers directly.
In the following, we will discuss integration paths of GNS-unaware applications.
Integration with Browsers (DNS2GNS service)#
Most OSes allow you to either modify your
resolvectl. We are going to configure the dns2gns service
in order to translate DNS name queries by applications to GNS name
queries where applicable and else fall back to DNS.
Optionally, you may want to configure your dns2gns service to run on a
non-privileged port like 5353. But, in case you are going to edit
/etc/resolv.conf directly, the dns2gns service MUST run on port 53
as you cannot specify the port number. A
should be a DNS server you trust such as your local router:
$ gnunet-config -s dns2gns -o OPTIONS -V "-d $FALLBACK_DNS -p 5252" $ gnunet-arm -i dns2gns
If you edit your resolv.conf directly, it should contain and entry like this:
In any case, it is very likely that the method of modification of your resolver is OS specific. Recently, the combination of NetworkManager and systemd-resolved is becoming increasingly popular.
If you use resolvectl and systemd-resolved you can temporarily set the nameserver like this:
$ resolvectl $INTERFACE 127.0.0.1:5353
$INTERFACE is your network interface such as
In order to automatically set the DNS2GNS server if it is running already you can use NetworkManager-dispatcher. First, enable it:
$ sudo systemctl enable NetworkManager-dispatcher.service $ sudo systemctl start NetworkManager-dispatcher.service
Then, create a script
#!/bin/sh interface=$1 status=$2 if [ "$interface" = "eth0" ]; then case $status in up) if nc -u -z 127.0.0.1 5353; then resolvectl dns $interface 127.0.0.1:5353 fi ;; down) ;; esac fi
Make sure the script is owned by root and executable:
$ sudo root:root /etc/NetworkManager/dispatch.d/10-dns2gns.sh $ sudo +x /etc/NetworkManager/dispatch.d/10-dns2gns.sh
You can test accessing this website using your browser or curl:
$ curl www.gnunet.org
Note that gnunet.org is a domain that also exists in DNS and for which the GNUnet project webservers can provide trusted TLS certificates. When using non-DNS names with GNS or aliases, this may result in issues when accessing HTTPS websites with browsers. In order learn how to provide relief for this issue, read on.
Integration with Browsers (SOCKS proxy)#
While we recommend integrating GNS using the DNS2GNS service or the NSSwitch plugin, you can also integrate GNS directly with your browser via the gnunet-gns-proxy. This method can have the advantage that the proxy can validate TLS/X.509 records and thus strengthen web security; however, the proxy is still a bit brittle, so expect subtle failures. We have had reasonable success with Chromium, and various frustrations with Firefox in this area recently.
The first step is to start the proxy. As the proxy is (usually) not started by default, this is done as a unprivileged user using gnunet-arm -i gns-proxy. Use gnunet-arm -I as a unprivileged user to check that the proxy was actually started. (The most common error for why the proxy may fail to start is that you did not run gnunet-gns-proxy-setup-ca during installation.) The proxy is a SOCKS5 proxy running (by default) on port 7777. Thus, you need to now configure your browser to use this proxy. With Chromium, you can do this by starting the browser as a unprivileged user using chromium –proxy-server=*socks5://localhost:7777* For Firefox (or Icecat), select Edit-Preferences in the menu, and then select the Advanced tab in the dialog and then Network:
Here, select Settings… to open the proxy settings dialog. Select Manual proxy configuration and enter localhost with port 7777 under SOCKS Host. Furthermore, set the checkbox Proxy DNS when using SOCKS v5 at the bottom of the dialog. Finally, push OK.
You must also go to about:config and change the browser.fixup.alternate.enabled option to false, otherwise the browser will autoblunder an address like www.gnu to www.gnu.com. If you want to resolve @ in your own TLDs, you must additionally set browser.fixup.dns_first_use_for_single_words to true.
After configuring your browser, you might want to first confirm that it continues to work as before. (The proxy is still experimental and if you experience odd failures with some webpages, you might want to disable it again temporarily.) Next, test if things work by typing http://test.gnu/ into the URL bar of your browser. This currently fails with (my version of) Firefox as Firefox is super-smart and tries to resolve http://www.test.gnu/ instead of test.gnu. Chromium can be convinced to comply if you explicitly include the http:// prefix — otherwise a Google search might be attempted, which is not what you want. If successful, you should see a simple website.
Note that while you can use GNS to access ordinary websites, this is more an experimental feature and not really our primary goal at this time. Still, it is a possible use-case and we welcome help with testing and development.
To activate the plugin, you need to edit your
/etc/nsswitch.conf where you should find a line like this:
hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4
The exact details may differ a bit, which is fine. Add the text
gns [NOTFOUND=return] after
hosts: files gns [NOTFOUND=return] mdns4_minimal [NOTFOUND=return] dns mdns4
Note that some systems, in particular those running systemd-resolvd, manual modification of
/etc/nsswitch.conf is not possible as it is autogenerated.
In such cases it is better to use the DNS2GNS service approach in combination with
resolvectl as explained above.
So you have decided to maintain and publish one or more GNS zones. You are probably asking yourself how your zone finds its way into the Start Zone configuration of other users.
Below are pointers on how to make your zone known.
The .pin zone#
First, you may want to visit https://fcfs.gnunet.org and register your zone(s)
.pin top-level domain shipped by default with GNUnet.
This will allow other GNUnet users to resolve your zone under the name you
managed to acquire for yourself.
The registration policy of PIN is “first-come, first-served”.
Creating a business card#
Before we can really use GNS, you should create a business card. Note that this requires having LaTeX installed on your system. If you are using a Debian GNU/Linux based operating system, the following command should install the required components. Keep in mind that this requires 3GB of downloaded data and possibly even more when unpacked. On a GNU Guix based system texlive 2017 has returns a DAG size of 5032.4 MiB. The packages which are confirmed to be required are:
We welcome any help in identifying the required components of the TexLive Distribution. This way we could just state the required components without pulling in the full distribution of TexLive.
apt-get install texlive-full
Start creating a business card by clicking the Copy button in gnunet-namestore-gtk. Next, you should start the gnunet-bcd program (in the terminal, on the command-line). You do not need to pass any options, and please be not surprised if there is no output:
Then, start a browser and point it to http://localhost:8888/ where gnunet-bcd is running a Web server!
First, you might want to fill in the GNS Public Key field by right-clicking and selecting Paste, filling in the public key from the copy you made in gnunet-namestore-gtk. Then, fill in all of the other fields, including your GNS NICKname. Adding a GPG fingerprint is optional. Once finished, click Submit Query. If your LaTeX installation is incomplete, the result will be disappointing. Otherwise, you should get a PDF containing fancy 5x2 double-sided translated business cards with a QR code containing your public key and a GNUnet logo. We’ll explain how to use those a bit later. You can now go back to the shell running gnunet-bcd and press CTRL-C to shut down the Web server.
The GNUnet default Start Zones#
GNS supports the majority of the DNS records as defined in RFC 1035. Additionally, GNS defines some new record types the are unique to the GNS system. For example, GNS-specific resource records are used to give petnames for zone delegation, revoke zone keys and provide some compatibility features.
For some DNS records, GNS does extended processing to increase their usefulness in GNS. In particular, GNS introduces special names referred to as zone relative names. Zone relative names are allowed in some resource record types (for example, in NS and CNAME records) and can also be used in links on webpages. Zone relative names end in .+ which indicates that the name needs to be resolved relative to the current authoritative zone. The extended processing of those names will expand the .+ with the correct delegation chain to the authoritative zone (replacing .+ with the name of the location where the name was encountered) and hence generate a valid GNS name.
The GNS currently supports the record types as defined in GANA. In addition, GNS supports DNS record types, such as A, AAAA or TXT.
For a complete description of the records, please refer to the specification at LSD0001.
In the following, we discuss GNS records with specific behavior or special handling in GNUnet.
GNS allows easy access to services provided by the GNUnet Virtual Public Network. When the GNS resolver encounters a VPN record it will contact the VPN service to try and allocate an IPv4/v6 address (if the queries record type is an IP address) that can be used to contact the service.
I want to provide access to the VPN service web.gnu. on port 80 on peer ABC012: Name: www; RRType: VPN; Value: 80 ABC012 web.gnu.
The peer ABC012 is configured to provide an exit point for the service web.gnu. on port 80 to it’s server running locally on port 8080 by having the following lines in the gnunet.conf configuration file:
[web.gnunet.] TCP_REDIRECTS = 80:localhost4:8080
If you want to support GNS but the master database for a zone is only available and maintained in DNS, GNUnet includes the gnunet-zoneimport tool to monitor a DNS zone and automatically import records into GNS. Today, the tool does not yet support DNS AF(X)R, as we initially used it on the .fr zone which does not allow us to perform a DNS zone transfer. Instead, gnunet-zoneimport reads a list of DNS domain names from stdin, issues DNS queries for each, converts the obtained records (if possible) and stores the result in the namestore.
The zonemaster service then takes the records from the namestore, publishes them into the DHT which makes the result available to the GNS resolver. In the GNS configuration, non-local zones can be configured to be intercepted by specifying .tld = PUBLICKEY in the configuration file in the [gns] section.
Note that the namestore by default also populates the namecache. This pre-population is cryptographically expensive. Thus, on systems that only serve to import a large (millions of records) DNS zone and that do not have a local gns service in use, it is thus advisable to disable the namecache by setting the option DISABLE to YES in section [namecache].
In order to import DNS zones which are already defined in a
zone file in use
with existing DNS daemons such as BIND you may try to import your DNS zones
into DNS using
Assuming your zonefile is called
$ gnunet-namestore-zonefile < myzonefile
As you can see, the program reads the zonefile from standard input.
Upon encountering an
$ORIGIN directive a new ego will be created with that
name unless it already exists.
$ORIGIN is a FQDN and ends with a
The ego will be called
example.com (without the
All records following the directive will be imported into the zone identified by
$ORIGIN directives within the same zone file are supported and
egos will be created accordingly.
$ORIGIN is explicitly specified in the beginning of the file
(as is sometimes the case in BIND setups) or if you want to override the (first)
occurence, you can provide an ego to use:
$ gnunet-namestore-zonefile -z myorigin < myzonefile
Note that in the conversion process some information found in the zone file
is lost and other information is less precise than in GNS.
For example, records stored in the
$ORIGIN can be specified in multiple
$ORIGIN example.com. $TTL 3600 example.com. IN MX 10 mail.example.com. ; mail.example.com is the mailserver for example.com @ IN MX 20 mail2.example.com. ; equivalent to above line, "@" represents zone origin
GNS will map the origin
example.com. to the empty label
@ whenever possible.
$TTL is given in seconds. GNS record expirations are defined in
microseconds. The seconds will be converted to their equivalent in microseconds.
Note how this means that conversion of GNS records (in particular their expirations)
to a DNS zone file cannot be done without loss of information.
This also means that converting the GNS zone back into a zone file (a function
currently not implemented) would not yield the same zone file that was imported.
Ascension is a tool to migrate existing DNS zones into GNS.
Compared to the gnunet-zoneimport tool it strictly uses AXFR or IXFR depending on whether or not there exists a SOA record for the zone. If that is the case it will take the serial as a reference point and request the zone. The server will either answer the IXFR request with a correct incremental zone or with the entire zone, which depends on the server configuration.
Before you can migrate any zone though, you need to start a local GNUnet peer. To migrate the Syrian top level domain - one of the few top level domains that support zone transfers - into GNS use the following command:
$ ascension sy. -n ns1.tld.sy. -p
The -p flag will tell GNS to put these records on the DHT so that other users may resolve these records by using the public key of the zone.
Once the zone is migrated, Ascension will output a message telling you, that it will refresh the zone after the time has elapsed. You can resolve the names in the zone directly using GNS or if you want to use it with your browser, check out the GNS manual section. Configuring the GNU Name System. To resolve the records from another system you need the respective zones PKEY. To get the zones public key, you can run the following command:
$ gnunet-identity -dqe sy
Where sy is the name of the zone you want to migrate.
You can share the PKEY of the zone with your friends. They can then resolve records in the zone by doing a lookup replacing the zone label with your PKEY:
$ gnunet-gns -t SOA -u "$PKEY"
The program will continue to run as a daemon and update once the refresh time specified in the zones SOA record has elapsed.
DNSCurve style records are supported in the latest release and they are added as a PKEY record to be referred to the respective GNS public key. Key distribution is still a problem but provided someone else has a public key under a given label it can be looked up.
There is an unofficial Debian package called python3-ascension that adds a system user ascension and runs a GNUnet peer in the background.
Ascension-bind is also an unofficial Debian package that on installation checks for running DNS zones and whether or not they are transferable using DNS zone transfer (AXFR). It asks the administrator which zones to migrate into GNS and installs a systemd unit file to keep the zone up to date. If you want to migrate different zones you might want to check the unit file from the package as a guide.