Add Hermes agent, self-host fonts, new blog post

- Add Hermes (Nous Research LLM agent) with Telegram gateway,
  Ansible provisioning, and Makefile targets
- Self-host JetBrains Mono and Spectral fonts (remove Google Fonts)
- Add "An Experiment in Self-Hosting" blog post
- Update CLAUDE.md with high-level server overview

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Louis Simoneau
2026-04-10 16:06:48 +10:00
parent ab050fddd7
commit 3a9e3a7916
19 changed files with 226 additions and 5 deletions

View File

@@ -0,0 +1,19 @@
---
title: "An Experiment in Self-Hosting"
date: 2026-04-10T00:00:00+10:00
draft: false
---
One of the things I wanted to do with this site is to see how much tooling I could self-host on a small VPS, in particular with the acceleration afforded by AI coding through Claude Code.
So far I have:
* A [Hugo](https://gohugo.io/) static site
* [Caddy](https://caddyserver.com/) webserver
* Self-hosted feed reader with [Miniflux](https://miniflux.app/)
* Analytics using [Goatcounter](https://www.goatcounter.com/)
* Git server using [Gitea](https://about.gitea.com/) (you can check out the source for the whole project, inception-style, at [git.monotrope.au/louis/monotrope](https://git.monotrope.au/louis/monotrope))
The only external dependency for the whole setup is the server itself (a DigitalOcean droplet), and I'm sure I'll come up with more tools I can add to the server over time.
All of this I would estimate took less than 4 hours to set up and deploy. I think previously even the small amount of effort required to deploy a static blog would have pushed me towards free platforms like Medium or GitHub Pages. This mode of production has the potential be a great thing for the Web if more tinkerers can build and host their own stuff instead of relying on centralised platforms where you are the product.

View File

@@ -6,9 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ if not .IsHome }}{{ .Title }} · {{ end }}{{ .Site.Title }}</title>
<meta name="description" content="{{ with .Description }}{{ . }}{{ else }}{{ .Site.Params.description }}{{ end }}">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,300;0,400;0,500;1,400&family=Spectral:ital,wght@0,400;0,600;1,400&display=swap" rel="stylesheet">
<link rel="preload" href="/fonts/jetbrains-mono-latin.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/spectral-400-latin.woff2" as="font" type="font/woff2" crossorigin>
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="/css/main.css">
{{ range .AlternativeOutputFormats -}}

View File

@@ -1,5 +1,96 @@
/* ── Fonts ─────────────────────────────────────── */
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,300;0,400;0,500;1,400&family=Spectral:ital,wght@0,400;0,600;1,400&display=swap');
/* JetBrains Mono — latin-ext */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 300 500;
font-display: swap;
src: url('/fonts/jetbrains-mono-latin-ext.woff2') format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* JetBrains Mono — latin */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 300 500;
font-display: swap;
src: url('/fonts/jetbrains-mono-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* JetBrains Mono italic — latin-ext */
@font-face {
font-family: 'JetBrains Mono';
font-style: italic;
font-weight: 400;
font-display: swap;
src: url('/fonts/jetbrains-mono-italic-400-latin-ext.woff2') format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* JetBrains Mono italic — latin */
@font-face {
font-family: 'JetBrains Mono';
font-style: italic;
font-weight: 400;
font-display: swap;
src: url('/fonts/jetbrains-mono-italic-400-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* Spectral — latin-ext */
@font-face {
font-family: 'Spectral';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/fonts/spectral-400-latin-ext.woff2') format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* Spectral — latin */
@font-face {
font-family: 'Spectral';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/fonts/spectral-400-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* Spectral 600 — latin-ext */
@font-face {
font-family: 'Spectral';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url('/fonts/spectral-600-latin-ext.woff2') format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* Spectral 600 — latin */
@font-face {
font-family: 'Spectral';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url('/fonts/spectral-600-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* Spectral italic — latin-ext */
@font-face {
font-family: 'Spectral';
font-style: italic;
font-weight: 400;
font-display: swap;
src: url('/fonts/spectral-italic-400-latin-ext.woff2') format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* Spectral italic — latin */
@font-face {
font-family: 'Spectral';
font-style: italic;
font-weight: 400;
font-display: swap;
src: url('/fonts/spectral-italic-400-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* ── Reset ─────────────────────────────────────── */
*, *::before, *::after {

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.