Writing a gemini server

The gemini protocol was not what I thought it was. I mean, it says on the page, lighter than the web but heavier than gopher... Well, there's A LOT of space in that gap! I've worked with the IMAP protocol, which is lighter than the web and heavier than gopher... and it's an absolute pain to work with!! So I was skeptic...

But after being bored last night I decided to give it a try. You see, last night I watched a movie and was disappointed yet again. I've been realizing that movies is no longer something that I enjoy as I used to, been leaning A LOT more towards things that I have to think/do vs just entertained... but that's a story for another day.

Anyways, I wrote a gemini server! Last week while waiting for some people I decided to read the protocol specs on my phone and it was all pretty straightforward EXCEPT for big and scary TLS. Now, coming from the "web" TLS is something I don't understand, nay, something I fear! Is the things that expires and if I forget to renew it the company site goes down. It's the thing that you "buy" and then copy paste some snippet on nginx or apache and hope to never have to debug anything because you have no clue what is happening. It's the things that has a "challenge" and sometimes you get it and sometimes you don't (Let's Encrypt) and it all is because of some obscure stuff somewhere.

But then I decided to take a quick look at it and it wasn't all ugly and scary as I thought! I'm no expert in TLS but for gemini you don't really have to be. It can be used kind of like an ssh key: 1. generate it, 2. throw it at the stuff that is asking for it, 3. forget about it.

Here's how to generate a certificate. From this snippet all that you really need to change is the CN and if you are picky (like me!) change the file names on -out and -keyout to match your domain.

 openssl req -x509 \
            -out localhost.crt \
            -keyout localhost.key \
            -newkey rsa:2048 \
            -nodes \
            -sha256 \
            -subj '/CN=localhost'

I decided to use golang just because is the language I'm the most familiar with and everything that I needed was already on the standard library.

And to prove the point of how small and easy, here's the whole server:

package main

import (

func main() {
	crt, _ := tls.LoadX509KeyPair("localhost.crt", "localhost.key")
	cfg := tls.Config{
		Certificates: []tls.Certificate{crt},
		MinVersion:   tls.VersionTLS12,
	ln, _ := tls.Listen("tcp", ":1965", &cfg)

	for {
		conn, _ := ln.Accept()

		conn.Write([]byte("20\x20text/gemini\r\n## hello gemini\r\nhello gemini"))

I skipped error checking and I'm still figuring out how to read the client request and certificates but it's definitively a start! And the "big scary monster" of TLS is not so after all!

It's possible to connect to the server with a client like Lagrange or just with openssl

openssl s_client -connect localhost:1965

The point is, gemini protocol is really a very simple and easy to work with protocol to build on top of and I can't wait to start making some cool stuff with it!


Written with Smol Pub

Send me a message at benj AT sdf.org :-)