← Back to overview

One binary + directories

All logic lives in a single binary (~25 MB). The site is described by a few directories:

  • content/ — Markdown files with TOML frontmatter
  • base/themes/{theme}/ — Tera templates, SCSS and i18n of the active theme
  • base/static/ — user overlay: wins over the theme for static assets (CSS, JS, fonts, images)
  • config.toml — configuration
  • .env — OAuth keys

Themes are mandatory and switched via theme in config: all templates, styles and translations come only from the active theme, while the user base/static/ overrides the theme for other assets.

MVC layers

The code is organized into layers:

  • model/ — data and domain logic (Page/Section/Taxonomy, Markdown parser, search, XML escaping)
  • view/ — view-model (build_*_context) and the Tera render engine (render_page, render_404)
  • controller/ — HTTP handlers (home, section, tags, search, rss, sitemap, auth)
  • service/ — AppState/SharedState, sessions, OAuth, static files, SQLite, SCSS, metrics
  • router/ and middleware/ — routing and middleware layers (counter, ratelimit, security, timing)
  • server/ — startup orchestration: initialization split into independent subsystems (server/init/), TLS with four modes via dedicated modules (server/tls/)

Startup and hot reload

On startup the server reads the config, scans content/, compiles Tera templates and SCSS, and opens SQLite. Everything lives in memory. A watcher tracks changes in content/ and theme directories — the site updates without a rebuild.

Performance

A Rust server starts in milliseconds, uses minimal memory, and keeps all content in memory — no external services. SQLite (WAL, batched writes) is used for the page view counter, sessions, and login audit. Fully rendered HTML is cached (LRU) for logged-out visitors.