Colophon

The boring details about how this site is put together, for the people who care about that sort of thing.

Stack

  • Application: Flask 3.1 on Python, WSGI via Gunicorn (2 workers, Unix socket), reverse-proxied through Nginx with HTTP/2 and keepalive connections.
  • Database: SQLite 3 with WAL mode. Schema migrations via Flask-Migrate (Alembic). Full-text search via FTS5 with porter tokenizer.
  • Templating: Jinja2 server-side rendering. Markdown processed through Python-Markdown (fenced_code, tables, toc, attr_list, admonition, def_list extensions), sanitized by nh3, a Rust-based HTML cleaner that replaces Bleach.
  • Frontend: Bootstrap 5.3.3 (unused selectors stripped by PurgeCSS at deploy time), HTMX 2.0.4 for partial page updates without a build step or client-side routing. Light/dark theme toggle with OS preference detection via prefers-color-scheme, persisted to a cookie. All colors defined as CSS custom properties; theme switching overrides variables without reloading the page. Command palette (Ctrl+K) with live search, keyboard navigation between entries (arrow keys), and heading anchor links for shareable deep links. No React, no Vue, no webpack; just <script defer> and vanilla JS.
  • Syntax Highlighting: Prism.js 1.29 with line-numbers, line-highlight, and a custom grammar definition for Asterisk dialplan syntax (language-asterisk). Token colors adapt to the active theme via CSS attribute selectors.
  • Content Editing: Editor.js block editor with a custom code tool that preserves language, title, and line highlight metadata through the markdown conversion round-trip. Custom Alert block tool with configurable admonition types (note, tip, info, warning, danger, important). marked.js and DOMPurify handle markdown preview.
  • Content Operations (MCP): The site is managed in part through a Model Context Protocol server that exposes the production database as a set of Claude Code tools. Capabilities include reading and writing content entries, managing the changelog, moderating community submissions and user notes, resolving content flags, managing tags (rename, merge, search by usage), and querying site and search statistics. A separate log analysis interface imports the nginx access log incrementally into a SQLite database every five minutes, then exposes traffic stats, error queries, and path searches as additional MCP tools. The server runs in two modes: local (direct SQLite access during development) and remote (JSON commands piped over SSH to lightweight dispatcher scripts on the production host). No persistent process is required on the server side; each dispatcher reads stdin and exits.
  • User Notes: Community-contributed notes on every content page, moderated before display. Four-layer anti-spam: honeypot fields, rate limiting, email confirmation (trusted contributors bypass), and admin approval. The consulting form uses the same honeypot + rate-limiting approach.
  • Community Voting: Anonymous upvoting on all content pages. Server-side deduplication via SHA-256 hash of IP and user agent, with cookie-based UI state. Sortable by "Most upvoted" on category, tag, and updates pages.
  • Tools: Interactive Dial Pattern Tester (client-side pattern matching engine with specificity ranking) and Audio Converter (server-side conversion via sox and ffmpeg with volume normalization, silence trimming, and G.722 wideband output). Shareable short URLs for pattern test results. Downloadable bundles via wget/curl with 24-hour retention.
  • SEO Pipeline: Structured data (JSON-LD) on every page type. Dynamic sitemap with lastmod. IndexNow integration for instant search engine notification. llms.txt for AI discoverability. Social images auto-generated via ImageMagick. Version compatibility matrix across 880+ reference entries.
  • Mail: Postfix handles forward-only delivery for the domain (virtual aliases, no local mailboxes) and outbound relay for the consulting contact form. TLS where the other side supports it. Spam gets caught early by Spamhaus and SpamCop RBLs before the message body is even transferred.

Typography

Both use font-display: swap. The two primary weights (400) are preloaded via <link rel="preload"> to avoid layout shift. The @font-face declarations are inlined directly into <head> to eliminate one render-blocking request.

Performance

  • CSS purged at deploy time: Bootstrap Icons stripped from ~2,000 icon rules to the ~80 actually used (96% reduction). Bootstrap CSS run through PurgeCSS against the template corpus.
  • All CSS files (Bootstrap, custom theme, tools, icons, syntax highlighting) concatenated into a single combined.css at deploy time, reducing 7 HTTP requests to 1. URL paths rewritten during concatenation so font references resolve correctly from the new location.
  • Static assets minified in-place (rcssmin/rjsmin), then pre-compressed to .gz files for Nginx's gzip_static directive, so Nginx never compresses on the fly.
  • Nginx response cache (5-minute TTL, keyed on URI + HX-Request header + view_mode cookie) with stale-while-revalidate fallback.
  • Static files served with Cache-Control: public, immutable and 30-day expiry. No CDN; single-origin, same box.
  • Google Analytics is deferred until the first user interaction or a 4-second idle timeout, so it doesn't touch LCP.

Security

  • CSRF on all state-changing endpoints (Flask-WTF). Flask-Limiter rate-limits login (10/min) and submissions (5/hr). Nginx limit_req_zone adds a second layer at the proxy.
  • All user-submitted content sanitized through nh3 with an explicit allowlist of tags and attributes.
  • HSTS (2 years, includeSubDomains, preload), X-Content-Type-Options: nosniff, X-Frame-Options: DENY, strict Referrer-Policy and Permissions-Policy.
  • Admin interface runs as a separate Gunicorn instance, accessible only via SSH tunnel. Blocked at the Nginx level on the public vhost (return 404).
  • Honeypot field on submission form for bot detection.
  • Fail2ban jails on SSH, Nginx (bad bots, auth failures, script probes, rate limit violations, vulnerability scanners), and Postfix (connection abuse, SASL brute force).

Deployment

  • Ansible playbooks (~1,900 lines across VM and Incus container targets). Code synced via rsync, virtualenv rebuilt on dependency changes, database backed up pre-migration with 7-day rotation.
  • TLS via Let's Encrypt with certbot auto-renewal timer.
  • Deploy pipeline: rsync code → regenerate llms.txt → purge CSS → minify → gzip-compress → migrate DB → restart Gunicorn → health check. Internal link checker validates all content cross-references post-deploy.
  • IPv4 and IPv6 firewalls via iptables. Systemd hardening with ProtectSystem=strict, RuntimeDirectory-managed runtime paths, read-only application directories at runtime, and NoNewPrivileges.
  • Nginx access and error logs rotated weekly (13-week retention) via an explicit logrotate config. Gunicorn output captured by journald (500 MB cap, 3-month retention).

Open Source

Project License
FlaskBSD 3-Clause
SQLitePublic Domain
BootstrapMIT
Bootstrap IconsMIT
HTMXBSD 2-Clause
Prism.jsMIT
JetBrains MonoSIL OFL 1.1
Source Sans 3SIL OFL 1.1
Python-MarkdownBSD 3-Clause
nh3MIT
Flask-LimiterMIT
Flask-SQLAlchemyBSD 3-Clause
marked.jsMIT
DOMPurifyApache 2.0 / MPL 2.0
Editor.jsApache 2.0
GunicornMIT
NginxBSD 2-Clause
Let's EncryptMPL 2.0
SoXGPL 2+
FFmpegLGPL 2.1+

By the Numbers

958 Published Entries
20 Categories
174 Tags
536 Automated Tests
Python (application + shared + admin + tools)~8,400 lines
Jinja2 templates77 files
Custom CSS~68 KB
Vanilla JavaScript~64 KB
Database migrations22
Ansible deploy playbooks~1,900 lines
Test suites2 (public + admin)