Release notes
The publishing mechanism used to produce this website is a topic likely of little interest, nonetheless, I’d like to, if only to keep a note of the work done and changes made. But first some trivia.
By 2010, I was fed-up with a popular open source web publishing software I’d been using since 2004. Its code base was changing so rapidly that I could not keep up. To quote Tom Preston-Werner (2008):
I already knew a lot about what I didn’t want. I was tired of complicated blogging engines like WordPress and Mephisto. I wanted to write great posts, not style a zillion template pages, moderate comments all day long, and constantly lag behind the latest software release.
On Sunday, October 19th, I sat down in my San Francisco apartment with a glass of apple cider and a clear mind. After a period of reflection, I had an idea. While I’m not specifically trained as an author of prose, I am trained as an author of code. What would happen if I approached blogging from a software development perspective? What would that look like?
Tom came up with Jekyll from that deliberation.
Fatigued by bloat and vulnerabilities in the open source web publishing software stack, by 2011, Tom’s post had caught-on. It helped people realise that writing one was not some dark art that required an army of engineers and volunteers for it to be useful, thereby renewing interest in the development and use of fit-for-purpose static site generators (SSGs). With an SSG, one could also get rid of all the complexities of running and managing database instances, and keep them from corrupting over time — be they from upgrades or from encoding.
These trends helped re-assess my needs too. For instance, questioning the need for traditional web service hosts, or the need for pre-requisites like LAMP stack to be able to a host simple website like this one. Having such software in the cloud and shared across users amplified attack vectors for the hapless publishers. And for what really? For the comfort of publishing on the road by accessing an archaic login system without multi-factor authentication? It is ridiculous in hindsight of course, but there was a time many of us thought that there really was no alternative.
In 2012, after years of grappling with software bloat, security gaffes, and latency of various web publishing services, I decided to take control of my publishing set-up. My objectives were:
- To write and present code, math, and prose effortlessly
- Enable login-less cryptographically-secure publishing
- Ensure portability of notes together with track changes
- Maintain bare bones, readable, safe, and low-dependency code base
- Use a decent template engine to be able to do more with less code
After some searching and evaluating, I found Chisel — a simple python script to turn notes, written in markdown, into html pages, and auto-arrange them in a neatly linked structure. It employed Jinja, a minimal, performant template engine; git for tracking changes in both notes and code; and GitHub for hosting. I subsequently added a few features to Chisel I wanted for myself.
Both the model and the controller are local processes, having decided long ago that I would execute nothing in the cloud. Only the view produced (from a combination of model and controller processes) is pushed to the host via git. This eliminates the need for a feature-rich web server on the host, and is therefore ideal for hosting a locally-generated static site. Today I am able to say that after nine years of sustained use of this above set-up, it remains very usable. The code base has remained the same, with lines of code never exceeding more than a few overall, and it is as readable today as I had first found it.
The only noteworthy maintenance I undertook was in 2019-20, when concerned by the need to phase out python2 code, I sat down to re-factor Chisel to run on python3. I did so without discernible change in code.
This is a set of release notes, covering features or updates, from that exercise done a year ago, and split into MVC design pattern.
Controller
- Date is ISO 8601 compliant (new)
- Fenced code markdown extension enabled (new)
- Generator specific config file (new)
- Re-factored to python 3 (new)
- Select contributions sent upstream via pull requests (update)
- Taxonomy (tag) feature added (new, Nov 2023)
Model
- Feed templates now in JSON (new)
- Keyboard-only navigation Alt + ( /, j, k) with circular return (update)
- Homepage is now also the archive page (update)
- Template parameters: single config file, selective imports (new)
- Templates modularised with child templates (new)
- Home template updated to support tags (new, Nov 2023)
View
- Custom font calls eliminated (update)
- Dark mode honouring OS-set appearance (update)
- Numerous template snippets re-factored to be modular (update)
- Re-design, html5 and css3 compliant (new)
- Render math with KaTeX engine (new)
- Snippets re-formatted using black for improved readability (new)
- Highlight.js and KaTeX combined to only load on demand
- A clutter-free access to text-only web browsers like lynx, links
- UTF-8 encoding compliance in both HTML and JSON feed (new)
Building JSON feed (2025): JSON is an open standard file format, which has been in existence since the early 2000s. Created by Douglas Crockford, it has a simple, dictionary-like structure, e.g.,
{
"key" : "value",
}
In 2017, two entrepreneurs developed a JSON feed spec., leveraging the simplicity of JSON format. The spec is a lightweight alternative to XML-heavy formats (like RSS or Atom), and despite its recency, it enjoys mainstream support across feed reader apps.
The first I wrote a template to produce JSON feed for this web site was in 2020. Over the next few years, I dismantled the Atom feed, leaving only JSON feed as the channel to alert the subscriber of a new note, whenever I produced one.
While the spec allows html content, using content_text I think is more appropriate for a notification system. This is aside from being simpler and readable, and so that is what I use. Jinja has some nice filters (viz., tojson, truncate, and striptags), making it easy to produce a JSON feed.
The template to produce a valid JSON feed for Chisel.
{% from 'sitesettings.j2' import site_title, site_author, site_url, site_desc -%}
{
"version" : "https://jsonfeed.org/version/1.1",
"user_comment" : "This feed lets you know when a new note is published. Copy the following URL -- {{ site_url }}/feed.json -- and add it to your feed reader.",
"title" : "{{ site_author }}",
"home_page_url" : "{{ site_url }}",
"feed_url" : "{{ site_url }}/feed.json",
"description" : "{{ site_desc | title }}",
"authors" : [
{
"name" : "{{ site_author }}",
"url" : "{{ site_url }}"
}
],
"icon" : "{{ site_url }}/inc/apple-touch-icon.png",
"favicon" : "{{ site_url }}/inc/apple-touch-icon.png",
"items": [
{%- for entry in entries %}
{
"id" : "{{ site_url }}/{{ entry.url }}",
"title" : "{{ entry.title }}",
"url" : "{{ site_url }}/{{ entry.url }}",
"content_text" : {{ entry.content|truncate|striptags|tojson }},
"date_published" : "{{ entry.feed_date }}"
}{%- if not loop.last %},{%- endif %}
{%- endfor %}
]
}
Local server (NGINX 2025): The 2025 version of setting up on a home network to serve a static local website is as follows. (Note: the file /etc/nginx/nginx.conf does not need editing at all, since it loads *.conf files under the conf.d folder.)
-
Install nginx
sudo apt update sudo apt install nginx-light -
Upload a folder to be served, e.g.,
ck.loin the example above to be under/var/www/. -
Create a file named
virtual.confunder/etc/nginx/conf.d, and populate it with the following:server { listen 80; server_name ck.lo; location / { root /var/www/ck.lo; index index.html; try_files $uri $uri/ $uri.html =404; } error_page 404 /404.html; }Add more server blocks to the file, if there is a need to serve more than one local website. Test it with
sudo nginx -tto ensure there are no errors. -
Set permissions for the server folder
/var/wwwsudo find /var/www -type d -exec chmod 755 {} \; sudo find /var/www -type f -exec chmod 644 {} \; sudo usermod -a -G www-data $USER sudo chmod g+s /var/www -
Reload nginx
sudo systemctl reload nginx -
Optional: In
/etc/pihole/custom.list, set local DNS like so. The local IP address in the following is but an example.10.0.1.100 ck.lo
With this, the local website should be accessible at http://ck.lo
Author’s age (2025): In lieu of manually updating the age in the bio, I dropped a little filter that picks up the DOB (using month and year) and computes it.
JSON feed ID as a SHA1 hash of the permalink (2025): Another filter to Jinja is added to turn permalink of the note (or post) into a SHA1 hash.
Binary executable (2025): Nearly thirteen years after forking, updating, and personalising, I attempted creating a single binary executable of Chisel — now that my templates and config files are just as I like them to be, tested, and frozen. It was a nice moment when it successfully created a single binary application. Here’s how I went about creating it:
-
Initialise the project with uv:
uv init uv add markdown jinja2 uv pip install pyinstaller -
Activate virtual environment from command prompt (with my shell being fish) from within the project folder:
source .venv/bin/activate.fish -
Run compile command:
pyinstaller --onefile mc.py -
Copy the generated
mcbinary executable from./distfolder to/usr/local/bin, so it’s accessible from anywhere on command prompt.