River Rooks | Technical Publication & Systems Architecture Blog
Developer-centric, high-signal, low-noise personal technical publication and systems architecture blog.
Inspired by native terminal environments, modal text editors (Zed, Ghostty), and tiling window managers, this site prioritizes raw performance, keyboard navigability, information density, and structured craftsmanship over generic marketing aesthetics.
1. Quickstart: Production Server Deployment
The production deployment uses a decoupled container architecture. The server does not need Node.js, git, or build dependencies—it only pulls the prebuilt engine from the registry and mounts a local folder containing your Markdown posts.
1.1 Server Directory Structure (~/docker/blog)
~/docker/blog/
├── .env # Environment variables (SITE_DOMAIN, etc.)
├── docker-compose.yml # Production container definition
└── posts/ # Your live Markdown articles (.md)
1.2 Production docker-compose.yml
services:
web:
image: git.riverrooks.dev/venus/river-rooks-blog:latest
container_name: river_rooks_web
restart: unless-stopped
volumes:
- "./posts:/app/src/posts:ro"
environment:
- "SITE_DOMAIN=${SITE_DOMAIN:?Error: SITE_DOMAIN is not set. Please define SITE_DOMAIN in .env}"
- "SITE_URL=${SITE_URL:-https://${SITE_DOMAIN}}"
- "PORT=${PORT:-8080}"
- "WATCH=${WATCH:-false}"
ports:
- "${HOST_PORT:-${PORT:-8080}}:${PORT:-8080}"
networks:
- backend
labels:
- "traefik.enable=true"
- "traefik.http.routers.blog.rule=Host(`riverrooks.dev`) || Host(`www.riverrooks.dev`)"
- "traefik.http.routers.blog.entrypoints=websecure"
- "traefik.http.routers.blog.tls.certresolver=basic"
- "traefik.http.services.blog.loadbalancer.server.port=8080"
- "traefik.docker.network=backend"
networks:
backend:
external: true
1.3 Deployment Commands
# 1. Pull the prebuilt engine from Gitea registry
docker compose pull
# 2. Launch the container
docker compose up -d
# 3. Verify healthcheck status (transitions to healthy in 3-5 seconds)
docker compose ps
2. Local Development & Verification
All templates, styles, and engine code are developed in this repository (~/code/blog).
2.1 Native Node.js Workflow
# Install dependencies
npm install
# Run automated test suite (unit tests, filters, server, and docker assertions)
npm test
# Launch Eleventy dev server with hot-reload (binds to http://localhost:8080)
npm run dev
# Compile static assets to _site/
npm run build
# Start the native static server locally
npm start
2.2 Local Docker Compose Workflow
The local docker-compose.yml provides sensible defaults (localhost, port 8080, and mounts ./src/posts):
# Build and run the local container stack
docker compose up -d --build
# View container logs (Eleventy compilation + server output)
docker compose logs -f
# Stop and clean up
docker compose down
2.3 Development Standards: Strict TDD & Adversarial Verification
This project enforces a strict test-first development lifecycle and verification standard:
- Tests Written Before Functional Code: Automated test cases in
test/must always be written before writing or modifying any functional code, filters, templates, server endpoints, or scripts. - Mandatory Adversarial Testing: Standard behavior for any functional change must always include adversarial test cases (boundary values, malformed/null inputs, unauthorized requests, path traversals, corrupt frontmatter, and design spec violations).
- Pre-Presentation Verification Gate: Every change must be verified by running
npm test. All test cases must pass before presenting the final output or committing code.
3. Push & Update Cycles
Because the system separates the Engine (templates/code) from the Content (markdown notes), updates follow two distinct paths:
3.1 Publishing or Editing Articles (Content Workflow)
When you write a new note or edit an existing article:
- Add or update the Markdown file in
posts/(e.g.posts/my-new-post.md):--- title: "My Article Title" date: 2026-09-14 tags: ["#systems", "#architecture"] excerpt: "A short synopsis of the article." hidden: false # (Optional) Set to true to hide from public lists while keeping direct link active --- # Article Content... - Recompile the site:
Eleventy compiles the mounted markdown in ~150ms and starts serving immediately.
docker compose restart web
Tip
Zero-Restart Live Reload: Set
WATCH=truein your server'sdocker-compose.ymlenvironment. Eleventy will watch theposts/directory in the background. Whenever a note is saved or synced (e.g., from Obsidian), the site automatically updates in memory without running any commands.
3.2 Updating Blog Templates, CSS, or Features (Engine Workflow)
When you modify design tokens, Nunjucks templates, server routes, or dependencies:
- Develop and verify locally:
npm test - Build and push the new engine image to the Gitea container registry:
# Build the engine image docker build -t git.riverrooks.dev/venus/river-rooks-blog:latest . # Push to Gitea docker push git.riverrooks.dev/venus/river-rooks-blog:latest - Update the production server:
docker compose pull docker compose up -d
4. System Architecture & Structure
4.1 High-Level Architecture
┌─────────────────────────────────────────────────────────────┐
│ Dev Machine / Workstation │
│ src/ (templates, CSS, config) + Dockerfile + entrypoint.sh │
└──────────────────────────────┬──────────────────────────────┘
│ docker push
▼
┌─────────────────────────────────────────────────────────────┐
│ Gitea Container Registry (git.riverrooks.dev) │
│ image: venus/river-rooks-blog:latest │
└──────────────────────────────┬──────────────────────────────┘
│ docker pull
▼
┌─────────────────────────────────────────────────────────────┐
│ Production Server │
│ │
│ ~/docker/blog/posts/ ──(volume)──> [ river_rooks_web ] │
│ (Live Markdown Notes) ├── entrypoint.sh │
│ ├── 11ty compiles │
│ Traefik (SSL / Proxy) <──────────── └── server.js (:8080) │
└─────────────────────────────────────────────────────────────┘
4.2 Key Engine Components
entrypoint.sh: Dynamic container entrypoint. Compiles mounted markdown notes with Eleventy on boot, starts optional background file watcher, and spawnsserver.jsas PID 1 for clean signal handling.Dockerfile: Minimalnode:22-alpinecontainer with pre-installed dependencies, baseline static pre-compilation, and baked-inHEALTHCHECKqueryinghttp://127.0.0.1:8080/healthz.server.js: Zero-dependency native Node static web server supporting clean directory URLs, MIME caching, security headers, and the/healthztelemetry endpoint.eleventy.config.js: Eleventy 3.x configuration, custom collections (posts,tagList), markdown-it anchor plugins, and custom date/reading-time filters.
4.3 Directory Map
.
├── .agents/ # Antigravity agent configurations & rules
├── sample-designs/ # Design specifications & HTML/CSS prototypes
├── src/ # Blog engine source
│ ├── _data/ # Site metadata & navigation
│ ├── _filters/ # Modular Eleventy filters (date, readingTime, headings)
│ ├── _includes/
│ │ ├── layouts/ # Base & post Nunjucks templates
│ │ └── partials/ # Global bar, hero tile, post cards, code box
│ ├── assets/
│ │ └── css/ # Modular CSS (tokens, reset, layout, components)
│ └── posts/ # Default/fallback markdown posts
├── test/ # Automated test suite (node:test)
├── docker-compose.yml # Local development compose file
├── sample-docker-compose.yml # Production-ready compose template
├── Dockerfile # Production container image definition
├── entrypoint.sh # Container startup & compilation entrypoint
├── server.js # Native Node static HTTP server
├── eleventy.config.js # Eleventy configuration
├── package.json # Project scripts & dependencies
└── README.md # Documentation & runbook
5. Strict Design Specification
All UI components and stylesheets strictly follow sample-designs/design-specification.md:
| Rule | Constraint |
|---|---|
| Color Scheme | Dark-mode native only. Canvas: #0d0f12, Surface: #121417, Elevated: #1c1f26, Borders: #2b303b. |
| Accents | Terminal accents: Cyan (#00f0ff), Green (#00e676), Amber (#ffb700), Red (#ff5252). |
| Borders & Radius | Crisp 1px solid borders. Zero to 4px maximum border-radius (sharp corners). |
| Strict Anti-Patterns | NO glassmorphism (backdrop-filter), NO drop shadows or glows, NO gradients, NO spring animations. |
| Typography | Monospace for UI & code (IBM Plex Mono, JetBrains Mono); clean sans-serif for body prose (Inter). |
| Interactivity | Instant binary hover snaps (no slow fade transitions); high-contrast focus outlines for keyboard navigation. |