February 22, 2021


I had mentioned a while back that I had written a new application to serve this blog, written in Elixir, and called it "Eldir". No sooner had I done that than I started goofing around with Go, and wrote a brand new application for serving this blog, which I'm calling "Sn", for "Tin".

Sn is built with the same concept in mind as with Eldir -- to take as input a directory of markdown files (usually part of a local git checkout) and a config file, and serve a dynamic, templatized site from them. The constraints I set for myself are basically the same, where I'd like to load all of the data into memory, and then never touch disk unless serving a static file directly.

Sn already accomplishes all of the goals of Eldir, but has a couple of advantages. Go has the advantage over Elixir of being compiled into a distributable executable, and as such, does not require Go itself to be installed on the server where the application runs. This is pretty cool because I can push builds of the app from Github actions directly to my server.

Another of the advantages is that I've found a library that automatically requests SSL certs from LetsEncrypt, so as soon as you update the config file and turn on the app, the site has a full-blown valid HTTPS endpoint ready to go. You don't need to do anything!

One of the features I'm working on for the medium term is letting you specify a directory in the config that contains an existing git checkout. Sn will be able to automatically update that checkout by requesting a configurable endpoint, pulling the latest commit from the remote repo. This will also not require installing git on the host. Ultimately, it should be possible to create an extremely light docker image with just the latest version of Sn in it, and a local cache directory for the repo, and it should "just work".

Some other small advances I'm working on include a very simple query language that is based on facets to return posts (in Sn, I'm calling them "Items") that can be used when rendering a specific page. For example, you may want to get all of the pages used to render the links in the header, and might specify the "items.links" as a slice (that's Golang for "array") of items that are returned for the query, "+repo:pages +link:true". If this is the home page, you may also want to include "items.posts" as a slice using the simple query, "+repo:posts". It should be possible to include variables from the route in the item query, so if you have a route defined as "/tag/:tag", then the query could be, "+repo:posts +categories:{{tag}}". Since every item is basically a markdown file with yaml frontmatter, the frontmatter can include any number of faceted fields that can be queried upon and then assigned to the page as a slice to iterate over. The templates use handlebars to simply loop over arrays and output content.

I have recently spun up a very simple single-page site using Sn, and it was the easiest site I think I have ever constructed. No Apache config, no Nginx, no haproxy, no tons of libraries... Just spin up a VM, drop a binary, edit the config and template, then... Done! So simple.

I am looking forward to building some defaults for Sn that will create an interactive documentation site as the default when you start up the app with no changes. You should be able to poke through the documentation and even download a new config file that you customize via a fancy front-end interface built into the docs. Save that file where the Sn application can see it, and your site just runs. Pretty cool.

The site you're reading this from (unless this post is very old, and I've moved on to some other new shiny language) is actually served from Sn. Note how fast it is. When I first used the site, I was tryingt to show others how fast it was, and they didn't believe me. And all of this works without using a JamStack solution, where I have to re-render the whole site any time I make a change to the content.

There is still a lot left to do. I have an issues list that's pretty long. But the base features are there enough to use it on a regular basis. It's actually presently at least as functional as my Eldir/Elixir solution, which took a lot more effort to get spun up on a new server. I'm looking forward to seeing what can be done with it in the future!