From lektor to gemlog
Background
I recently get really pissed quite often when I browse the web; I'm nuked with ads and pop-ups or - should I dare to use an adblocker - will not be able to see the content I was looking for at all. With the GDPR in place, living in Europe also reveals the incredible amount of Cookies used for profiling and tracking my browsing behavior. I'm being tracked everywhere, always.
Sure, I can opt-out. That's what they say. And it's a bad joke.
I cannot opt-out of what is called "essential to make the site run", which is obviously a whole lot. Technically it's forbidden to make the opt-out process harder then the opt-in, but I have never seen a single of these modal dialogs that would have offered me an "opt-out of everything" button that was just as easy to find as the "I agree to everything" button. Now even if I opt-out of everything, sites hide their "legitimate interest" settings on another page where I often need to manually de-select zillion items.
That's not the web I learned to love and I want to use as a major source of daily information.
I also recently learned about a new internet protocol that aims to make things different than HTTP/S, called gemini. If you're familiar with the gopher protocol, which has been around for some time now, you can put gemini somewhere between gopher and HTTP/S. By intention it does not much. It will let you display text, basically, that you type in a markdown-like syntax called gemtext. The function set is very limited on purpose, so content counts. It does not support images or inline links, but three levels of headings and code blocks. gemini does also TLS by default and explicitly welcomes self-signed certificates.
Hosting a Gemlog
To serve a gemlog, a server is needed; since TLS is a requirement, it's not sufficient to just serve the file structure. There are a couple of lightweight servers available, I'm using gemserv here, which I found fairly easy to compile and configure.
Generating a Certificate
I use openssl to generate a private key and a certificate signing request (CSR):
openssl req -new -newkey rsa:4096 -nodes \
-keyout private_key.txt \
-out csr.txt \
-subj "/CN=gemini.schubisu.de"
This will generate an X.509 certificate signing request with a new RSA 4096 bit key. The nodes option will skip encryption of the key. You can put more information in the subject name, it's important to have the CN right, however, to let it match your domain.
With the CSR we can generate the certificate:
openssl x509 -req -sha256 \
-days 365 \
-in csr.txt \
-signkey private_key.txt \
-out certificate.txt \
-extensions req_ext
The private_key.txt and certificate.txt will need to be on our server.
Configure gemserv
If available in your packet manager, install gemserv from there, or clone the repository and follow the build instructions. The configuration can be as simple as the following config.toml file:
port = 1965
host = "0.0.0.0"
[[server]]
hostname = "gemini.schubisu.de"
key = "/path/to/private_key.txt"
cert = "/path/to/certificate.txt"
dir = "/path/to/content"
and simply run
/path/to/gemserv config.toml
For local testing you don't even need to have a registered domain, just add a line like this to your /etc/hosts file:
127.0.0.1 gemini.schubisu.de localhost
Converting my lektor blog to gemtext
I write my blog with a lightweight CMS for static websites written in python, called lektor. I always liked about lektor that it's file structure mirrors the resulting web page. Every page I write is represented by a folder that contains at least one content.lr file which consists of a bit yaml and markdown.
This is very much like what gemini expects, which will either show you the subfolders of a directory, or - if present - renders the index.gmi (or index.gemini) file.
I started by renaming all contents.lr files to index.gemini to get the quickest possible working gemlog and an initial idea of what work lies ahead of me. It works, of course, but there is still plenty of room for improvement. What needs to be adjusted are
- Meta data: The contents.lr starts with a yaml block of meta data which could be anything. In my case it's the title, publication date and some tags.
- Inline links and links in general: Gemini does not allow inline links, so my links need to be converted to the correct format.
- Navigation: I want to allow quick navigation to previous and next blog posts, as well as returning to an overview.
I decided to write a small python script, to use lektor's API to access all meta information and contents. I also reviewed all my blog posts to bring the links into a standardized format:
# inline links:
[some text][identifyer:my_link_name]
# and at the bottom of the document:
[identifyer:my_link_name]: https://...
The script will convert that into valid gemini links that are numerically indexed inline when rendered:
# inline links:
some text[1]
# and just below the paragraph:
[1] my_link_name [some text]
I also edited a few minor things in my contents.lr to make the markdown comply with gemtext:
- gemtext does not allow nested bullet lists, so I need to reduce lists to one level wherever I used more
- Different than with when I convert markdown to HTML, gemtext will break a line wherever I inserted a line break. If I want something to show as a nice paragraph, it should not be interrupted by line breaks.
- Inline text formatting to emphasize some words is not possible in gemtext. However, I find markdown with inline formatting still readable enough, so I left that as is.
- I found a whole lot of typos :)
I've uploaded the script to convert the blog here. It's pretty much tailored to my needs. However, it may be useful if you happen to run into the same or similar idea :)
For what it's worth, I can now simply run my conversion script, rsync the files to my gemini server and that's it.
You can grab a gemini browser and checkout the results of my conversion at gemini://gemini.schubisu.de