The site doesn’t use any JavaScript or resources from other domains and there are no referral links or cookies.


This site is generated with a Rust program, from a directory structure that looks like this:


This structure makes it easy to update the site with a common text editor, as opposed to a web interface normally found in blog engines. There’s support for articles and updates, but it’s difficult to maintain a steady stream of substantial, write-once content, so those sources are disabled.

Pages and notes are written in Markdown, to make adding emphasis, code, headings, and links easy. The Rust program uses pulldown-cmark to convert the Markdown to HTML and Askama to incorporate that HTML into templates that provide a consistent look and feel for each page. Tokio provides a multi-threading runtime for regenerating notes, allowing I/Os to be scheduled asynchronously and multiple CPUs to help convert notes. For notes that need it, KaTeX can typeset mathematical expressions, at the cost of almost 100KB in additional page assets, which typically dwarfs the content itself.

These notes are stored in a directory called site-content, which is supported by other directories, in a structure like this:


The site-out directory holds the HTML and CSS results of generating the website and is pushed to GitHub, which Netlify makes available across the internet. The HTML and CSS are small enough to leave un-minified, so it’s easier to inspect for curious visitors.

The Rust code and build tool are in site-rs and site-tools contains some shell scripts that build and publish the site, keeping the site-content and site-out directories in sync, like this script:


set -o pipefail
set -o errexit
set -o nounset

len=`echo "$msg" | wc -c | sed -E 's/^ +//'`
if [ $len -gt 50 ]; then
	> /dev/stderr echo "error: message is $len characters"
	exit 1

if [ -z "$(git -C site-content status --porcelain)" ]; then
	> /dev/stderr echo error: site-content is clean
	exit 1

if [ -z "$(git -C site-out status --porcelain)" ]; then
	> /dev/stderr echo error: site-out is clean
	echo consider implementing automatic rebuild
	exit 1

git -C site-content add .
git -C site-content commit -m "$msg"

git -C site-out add .
git -C site-out commit -m "$msg"
git -C site-out push

To make those scripts accessible, I made a short Makefile in the site’s parent directory:

default: all

	site-tools/ $(m)

b: build

a: amend

	site-tools/ $(m)
p: publish

.PHONY: default all build b amend a publish p

So building and publishing the site is as simple as make m='The commit message'.

Nova integration

Since I do most of my editing of the site in The Nova text editor, I also wrote a small extension that adds an action to publish the site using this Makefile. The main JavaScript file is just:

function publish(workspace) {
  var options = {
    "placeholder": "Commit message",
    "prompt": "Publish"
  nova.workspace.showInputPanel("Publish Site", options, (result) => {
    if (result) {
      siteToolsPath = `${nova.workspace.path}/site-tools`
      let p = new Process(`${siteToolsPath}/`, {
        args: [result],
        cwd: nova.workspace.path,
        shell: true
      let errorLines = []
      let lines = []
      p.onStderr((line) => { errorLines.push(line) })
      p.onStdout((line) => { lines.push(line) })
      p.onDidExit((code) => {
        let request = new NotificationRequest(
        if (code != 0) {
          request.title = nova.localize('Failed to Publish')
          request.body = nova.localize(errorLines.join('\n'))
        } else {
          request.title = nova.localize('Published')
          request.body = nova.localize(lines.join('\n'))

nova.commands.register("net.mattwidmann.publish", publish);

In order for Git to unlock my SSH key and push changes to GitHub, I had to add my SSH key to 1Password. With just ssh-agent, it can’t ask for the passphrase over stdin and the push will fail.

The 404 page includes this HTML for a JavaScript-free site-specific search:

<form class="search" action="" method="GET">
	<input name="sites" type="hidden" value="">
	<input name="q" type="text">
	<input type="submit" value="Search">

Updated on 26 November 2022.