Keybase Sites offers a way to host web sites whose content is stored in Keybase private or team directories, using URLs which are in other domains (such as domains you may own already). It works by using the Keybase user "kbpbot" to retrive the files as they are requested.
In addition, the first time somebody retrieves a page in a new hostname, kbpbot will automatically request an SSL certificate from Let's Encrypt, and use that to answer the request (and any future requests for the same site). This means the first time you try to visit the site after setting it up, it may take an extra 10-30 seconds before you see the page.
These are some very simple sites I created under the jms1.net domain, whose contents are stored in Keybase in different ways.
Site | Location |
---|---|
✅ jms1.pub | /keybase/public/jms1/ |
→ kb-public.jms1.net | /keybase/public/jms1/kb-public.jms1.net/ |
→ kb-shared.jms1.net | /keybase/private/jms1#kbpbot/kb-shared.jms1.net/ |
→ kb-team.jms1.net | /keybase/team/jms1team.sites/kb-team.jms1.net/ |
→ kb-git.jms1.net | keybase://team/jms1team.sites/kb.jms1.net/ - "master" branch |
→ kb-git-other.jms1.net | keybase://team/jms1team.sites/kb.jms1.net/ - "other" branch |
Most of this is already documented, however it took me a bit of experimentation to get my first site working. I'm writing this page (well, adding this information to the page which already existed here) because I personally would have found things easier to understand if it had been written this way. Please don't think I'm trying to disparage the official documentation.
There are three primary tasks involved in setting up a web site using Keybase Sites:
Create the content and store it in Keybase. Creating the content should be fairly obvious, but as explained above, there are several ways to *store* the content so that kbpbot can read it.
Tell the world that the site is hosted using Keybase Sites. This is done by adding a DNS record which "points to" kbp.keybaseapi.com. The mechanics of this depend on whether the hostname is the "root" of a domain or a "hostname" within a domain.
Tell kbpbot where to find the site's content. This is also done using a DNS record
These are explained in more detail below.
Creating the content is up to you. Feel free to use whatever tools you like - for example, I'm using BBEdit, a text editor for macOS, to write the text you're reading right now.
The only requirements are:
The site's content must consist of only static files. If the site requires any kind of server-side processing (other than serving the files themselves), it will not work with Keybase Sites.
Each directory within the site must have an index.html file. Keybase Sites will not automatically generate indexes of the files in a directory, nor will it automatically convert Markdown to HTML on the fly.
Storing the files in Keybase can be done in one of several ways.
This is probably the simplest way to do it.
Store the files under your /keybase/public/jms1/ directory. You can store the site's index.html file right in the public directory (as I did for the site you're reading right now), or you can create a sub-directory and store it there.
This involves storing the files in a private directory where you and the kbpbot user both have access. Normally you would use a directory name which only allows the kbpbot user to have read access.
This involves storing the files in a team directory, and making the kbpbot user a member of the team. Normally you would make them a "reader", so they only have read access.
This involves storing the files in a git repo under a team, and making the kbpbot user a member of the team. Normally you would make them a "reader", so they only have "pull" access, and can't push new commits into the repo.
If the site's name will be within another domain, such as "www.example.com", create a CNAME record pointing to "kbp.keybaseapi.com".
If you're using tinydns, add the following to your data file:
... and then run "tinydns-data" to re-generate the data.cdb file. (In my case the command is part of a Makefile which runs tinydns-data and then replicates the data file to my other nameservers, so I just run "make".)
If you're using BIND, add the following to the appropriate zone file:
... and then run the appropriate command to "reload" the data. (I haven't used BIND in years, I want to say it's something like "rndc reload"? Don't trust me on this, figure out the correct command for your DNS servers. And if you want to be nice and let me know what the right command is, I'll update this page accordingly.)
In the steps below which talk about adding DNS records...
I'm going to assume you know how to "activate changes" in your DNS system, and not mention needing to run a command after adding records.
The DNS records in this document are shown with a 30 second TTL value. I normally do this when I'm testing something, so if I make a mistake I don't have to wait an hour or more for other nameservers to load any corrected data. When things are working and I know the records are good, I usually go back and change the TTL values to something higher, like an hour.
If the site's name will be the same as the root of a domain, such as "example.com" ... things get tricky because of the rules around CNAME records.
A name which exists as a CNAME, cannot also exist as any other kind of record.
A name (like "jms1.pub") which is the root of a domain, must have SOA and NS records. This means that the same name cannot also have a CNAME record.
I've seen documentation about this which says you can use a "DNS ALIAS record", but I've searched through RFC 1035 and all 28 of its updates (which define the DNS record types), and there is no such record type defined.
As near as I can figure, some specific DNS providers (such as AWS "Route53") offer these as "virtual records", where instead of returning an IP from a "zone file" or a database, the authoritative nameserver will do a recursive lookup of the target name and return the results of that query, as if they were the actual results of the original ALIAS lookup. Basically, instead of the IPs being fixed in a database or zone file, the IPs are looked up at the time the query is received.
If you're using a DNS provider who offers such a thing, and you're sure you're going to stick with them, then create an "ALIAS record" pointing to the name "kbp.keybaseapi.com". (I have no way to try this myself, so if it doesn't work, keep reading.)
Otherwise, you can manually look up the set of IP(s) that the name kbp.keybaseapi.com points to, and create an A or AAAA record pointing to each of those IPs. Currently there's only one IPv4 address, 18.214.166.21, which belongs to AWS.
If you do this, you will need to periodically re-check the list of IPs that kbp.keybaseapi.com points to, and update your A and AAAA records to match those changes. (I doubt the list of IPs will change very often, unless Keybase decides to add IPv6 support, migrate from AWS to some other cloud provider, or set up services in other parts of the world for various legal reasons.)
Telling kbpbot where to find the content is done by creating another DNS record. This needs to be a TXT record, with the name "_keybase_pages." followed by the site's custom hostname. The content of the record depends on where the site's files are stored.
In this case, the files need to be stored in or under a /keybase/public/xxx directory. I say "in or under" becuase you can set up multiple "web sites" under the same Keybase public directory by pointing each web site to a different sub-directory within the public directory.
For my jms1.pub site, the files are stored in /keybase/public/jms1/.
Create and populate the directory containing the content.
Add the DNS record.
Run the appropriate "tinydns-data" command to rebuild your data.cdb file.
Run the appropriate command to make the running named process reload your zone files.
In this case, the files need to be stored in a /keybase/private/xxx directory, where xxx includes your Keybase username, kbpbot, and possibly others.
For my kb-shared.jms1.net site, the files are stored in /keybase/private/jms1#kbpbot/kb-shared.jms1.net/.
Create and populate the directory containing the content.
Add the DNS record.
Run the appropriate "tinydns-data" command to rebuild your data.cdb file.
Run the appropriate command to make the running named process reload your zone files.
In this case, the files need to be stored in a /keybase/team/xxx directory, where xxx is the name of a team, and kbpbot is a member of that team.
For my kb-team.jms1.net site, the files are stored in /keybase/team/jms1team.sites/kb-team.jms1.net/.
If it isn't already a member, add Keybase user kbpbot to the team. In my case, I made it a reader, so if there's a bug in the software it wouldn't be able to make any changes.
Create and populate the directory containing the content.
Add the DNS record.
In this case, the files are stored in a git repo, owned by a team that the kbpbot user is a member of. The files are stored in a branch called "master" within the repo.
For my kb-git.jms1.net repo, the files are in the repo keybase://team/jms1team.sites/kb-git.jms1.net, in the master branch.
If it isn't already a member, add Keybase user kbpbot to the team. In my case, I made it a reader, so if there's a bug in the software it wouldn't be able to make any changes.
Create the repo on your workstation. The site's files will need to be in a branch called "master", so it makes sense to use this name as the initial branch. (This means the literal word "master", not a term for "the repo's primary branch".)
Set up the content.
Add the files and commit them.
If it doesn't already exist, create the empty Keybase git repo.
Set the local working directory's origin upstream to point to the new Keybase git repo.
Push the repo "up" to Keybase.
Add the DNS record.
This is just like the case above - the files are stored in a git repo, owned by a team that the kbpbot is a member of. The difference is, in this case the files we're using for the web site are stored in a branch with name other than "master". I'm using the name "other" for this example, obviously feel free to use whatever name you like for your site.
Keybase Sites wasn't designed to use branches other than master when dealing with a git repo. In order to make it work, we're actually setting it up to read from a team directory, but the directory name we're going to point it to is a "magic" directory whose contents "contain" the HEAD of a branch within a Keybase-hosted git repo.
I want to publicly thank Keybase user "dxb" for telling me about this. This is not something I would have figured out on my own. The "magic" filenames in KBFS are not clearly documented, and to be honest it never occurred to me to search through the client code to figure them out for myself - although now that I'm looking at the code, I want to make a list of these "magic" names and document them. I know I would find such a list useful, and I suspect others would as well.
So ... thank you dxb! 😎For my kb-git-other.jms1.net site, the files are in the repo keybase://team/jms1team.sites/kb-git.jms1.net, in a branch called "other". This is the same git repo I'm using for kb-git.jms1.net, just a different branch. If you're curious, the commit history looks like this:
Clone the repo on your workstation, and make sure your repo control files are up to date with the "server" (i.e. the repo stored in Keybase).
Create or check out the desired branch.
Set up the content.
If you've added or changed any files, commit the changes and push them. (Use the "-u" option if the branch doesn't exist in Keybase yet.)
Add the DNS record.