First experience with Gemini
These last few days I’ve been playing around with a nice little protocol called Gemini. It positions itself as a simpler, lightweight and privacy-respecting counterpart to the HTML+HTTP web. I am quite charmed by how well this protocol achieves its stated goals: two nights of hacking away sufficed to put online my in-house server built with OCaml and I am now in the process of adapting each page from my website to play nice with Gemini’s constraints.
This page is a sumup of what I have done until now while playing with Gemini, and some thoughts about the protocol and the experience I had writing a small server for it.
Building the server
OCaml has been my go-to language for my side-projects for about a year or two. I find it very satisfying to work with and have built quite a few toy projects with it but never got to the point of putting any of it into production. That’s how it tends to go with my side projects… Anyway recently a friend of mine asked me how comfortable it was to implement web servers in OCaml and I have been looking for an interesting web projects to do in OCaml ever since. Turns out this came in the form of a server which would serve my website both with Gemini and HTTP.
My primary goal writing this server was to get something fairly stable online fast and the simplicity of the Gemini protocol made that a breeze! Requests contain only an URL, responses contain only a status code, a MIME type and the body (in the case of a successful response) and this is literally all there is to it! Additionally I decided to experiment with having the server be a self-contained binary with no need for file IO to load articles or anything else. (I did not bother making it a statically linked binary though so it still has a few dependencies.) I am writing these words in an OCaml source file and it turns out this is far from an unpleasant experience: I was able to make myself a “DSL” (it’s so minimal it may not even deserve that name) and ocamlformat’s ability to wrap strings actually makes for a decent editing experience.
An aspect of the Gemini markup format which surprised me is the way empty lines are handled. Using HTML every day I would have assumed that text lines in Gemini would each be a paragraph and expect client to render them as such, with some vertical margins like those <p> elements get by default. Turns out the specification does not say anything like this, and even defines that blank lines should be rendered as vertical spaces, and that multiple blank lines should not be collapsed like I would expect to be since in HTML all adjacent whitespaces get collapsed. After a few experiments I found a rule for using blank lines which I found attractive enough and was delighted to find that OCaml’s modules made it very elegant to implement it as a functor which wraps the module I use to render my Gemini pages.
Usage of TLS
Gemini enforces usage of TLS. Always. I think this is the part of the specification that was most cumbersome while developing the server. The client I used to test my implementation did not have an option to disable this requirement. Until I actually bake TLS support into my server my quick-and-dirty workaround is to use socat to terminate the TLS connections and pass them directly to the server over the loopback.
This will do until I add real TLS support to my server. Until then I won’t be able to add sessions to parts of my site which may need it, since in Gemini those are based on client certificates and this solution does not allow the server to be aware of those.
I am kind of annoyed that the specification has absolutely no provisions for cases where TLS is absolutely not wanted: I wish to make the Gemini version of my site available over Tor which makes TLS redundant. As far as I know currently, I will need to create a self-signed certificate for that, in order to serve the content over TLS over Tor. I’m not really thrilled by that.
I want to make this server evolve into a piece of software that will serve my website both over Gemini and HTTP, perhaps even Gopher! Since rendering uses a modular design I should easily be able to render HTML instead of Gemini markup; OCaml has nice libraries for HTTP servers and asynchronous processing so I have all the tools I need at my disposal. Further down the line I might try to make this server into a MirageOS unikernel to explore that part of OCaml.