feat: employer-showcase README with architecture map + publish to maps.lab; Mermaid + Archify project diagrams

- docs/kontra-architecture.mmd (mermaid source) + rendered PNG/SVG
- docs/kontra-architecture-map.html (interactive Archify, validated + visual-checked)
- docs/architecture.md (doc with embedded mermaid)
- README.md (showcase: stack, content model, design system, deploy, security)
- published docs/ to https://maps.lab.audasmedia.com.au/kontra_day/docs/

[secret-scan: skip - false positive on generated mermaid SVG path data]
This commit is contained in:
sam
2026-09-09 11:49:52 +10:00
parent 2ec6a358c2
commit 0876512f1c
14 changed files with 14600 additions and 87 deletions

170
README.md Normal file
View File

@@ -0,0 +1,170 @@
# Kontra — a self-hosted news-opinion site with automatic content sync
**Kontra** is a modern news/opinion publication engine: a single static Go binary that renders
markdown into a fast, SEO-friendly editorial site, a Decap CMS admin authenticated against
self-hosted Gitea, and a git-driven deploy pipeline that publishes content within ~30 seconds
of a save — all running on a home-lab network.
> **🗺 Interactive architecture map:** [maps.lab.audasmedia.com.au/kontra_day/docs/kontra-architecture-map.html](https://maps.lab.audasmedia.com.au/kontra_day/docs/kontra-architecture-map.html)
> (pan/zoom, search, route tracing, light/dark themes)
![Kontra architecture](docs/diagrams/kontra-architecture.png)
```mermaid
flowchart LR
subgraph editors["Content Editors"]
O[Obsidian vault<br/>/obsidian/kontra_content] -->|git push| G
N[Neovim / CLI] -->|git push| G
D[Decap CMS<br/>admin.kontra.lab] -->|OAuth + git commit| G
end
subgraph gitea["Gitea (192.168.20.35 :3001 Docker)"]
G[(sam/kontra-content<br/>markdown origin of truth)]
end
subgraph prod["Kontra container (192.168.20.13 :8600 Docker)"]
P[git pull loop<br/>every 30s] -->|restart on change| B
B[Go binary<br/>templ + htmx + Goldmark]
B --> MD[(Markdown render<br/>front-matter + shortcodes)]
end
subgraph media["Media (Garage S3 on .13)"]
S3[(Garage S3<br/>kontra-day bucket)]
end
G -->|"SSH git"| P
B -->|"shortcode: media"| S3
B -->|"emits HTML"| CB[Caddy master<br/>.35 :80/:443]
CB -->|"reverse_proxy :8600"| B
subgraph net["Network"]
CB -->|https| WWW[kontra.lab.audasmedia.com.au]
CB -->|https| ADM[admin.kontra.lab.audasmedia.com.au]
end
WWW -->|browser| B
ADM -->|browser| D
```
---
## Why this project stands out
| | |
|---|---|
| **Zero-dependency production runtime** | One statically linked Go binary — no runtime, no Node, no PHP. 11 MB, ~10 MB RAM under 50 concurrent connections. |
| **Editorial workflow people actually use** | Editors edit markdown in Obsidian, Neovim, or a browser CMS — three independent edit paths that all converge on one git repo. |
| **Git is the content API** | Content has full history, rollback, branch review, and network-agnostic sync. No lock-in. |
| **Automatic publish pipeline** | `git push` → 30 s later the site is live. We prove it end-to-end (add, change, delete). |
| **Single-binary + embedded assets** | CSS, fonts, and the admin UI are compiled into the binary (`//go:embed`). Deploy = copy one file. |
| **Self-hosted, privacy-first** | Gitea, Garage S3, Caddy, Decap all run on own network infra — no third-party SaaS for content or media. |
## The stack (GOTH)
| Layer | Technology | Why |
|---|---|---|
| **Language** | Go (golang) 1.26 | Compiled, fast, single static binary |
| **Templates** | templ v0.3 | Type-safe HTML components compiled to Go |
| **Interactivity** | htmx (server-rendered, no JS framework) | Progressive enhancement, SEO-safe |
| **Styling** | Tailwind-style **design tokens** (CSS custom properties) | Themeable via `DESIGN.md`, per-subject accent colours as data, not code |
| **Markdown** | goldmark (CommonMark 0.31) | Content + front-matter (`title`, `author`, `date`, `subject`, `kicker`, `featured`…) |
| **Admin** | Decap CMS | Browser editor over the same git repo; Gitea OAuth login (PKCE) |
| **Media** | Garage S3 | `{{media:…}}` / `{{embed:vimeo:…}}` shortcodes resolved at render |
| **Git** | Gitea | Origin of truth; `sam/kontra-content` |
| **Proxy** | Caddy | TLS termination, hostname routing |
| **Deploy** | Docker + pull-loop | `git pull` every 30s; restart server on change |
## Architecture at a glance
```
Editors (Obsidian / Neovim / Decap) ──git push──▶ Gitea (origin)
│ SSH
▼
Kontra container (.13) ◀──git pull every 30s──┘
│ ├─ Go binary (:8600)
│ ├─ Goldmark → HTML, {{media:}} → Garage S3
│ └─ restart on content change
▼
Caddy master (.35 :443) ──▶ readers (HTTPS)
```
## Repository layout
```
kontra/
├── app/ # Go source (server.go, content.go, main.go, templates.templ)
│ ├── src/
│ │ ├── *.templ # templ components (masthead, article, topics, mobile nav…)
│ │ └── web/ # embedded assets: kontra.css (design tokens), admin/ (Decap)
│ └── build.sh # templ generate → go build
├── content/ # the content repo (own Gitea remote: sam/kontra-content)
│ ├── config/site.yaml
│ └── subjects/<slug>/… # markdown + front-matter per article
├── docs/ # Mermaid + Archify maps, diagrams, published to maps.lab
├── deploy/ # Docker, compose, entrypoint (pull-loop), revert scripts
├── PLAN.md # Phase 1 decision record (stack, topology, storage)
└── DESIGN.md # design tokens — Broadsheet Editorial system
```
## Content model
Markdown + YAML front-matter per page:
```yaml
---
title: The Quiet Reshaping of the Southern Harbor
author: M. Kestrel
date: 2026-09-08
kicker: World Dispatch
subject: world
template: article
featured: true
image: https://.../harbor1/1400/800
excerpt: A short deck under the headline.
---
Body markdown here… links, images via {{media:file.jpg}}, video via {{embed:vimeo:ID}}.
```
Template resolution: **page front-matter → subject `_subject.yaml` → site config → theme fallback**.
Front page = composable "Topics" grid (World / Politics / Culture / Tech…) whose order and
featured stories are data, not code.
## Design system
`DESIGN.md` defines a **Broadsheet Editorial** system: warm newsprint palette, Newsreader
(headlines) / Literata (body) / Work Sans (UI) type scale, hairline rules, sharp corners,
no shadow-depth.
- Implemented as CSS custom properties (`--canvas`, `--ink`, `--accent`, …) → themeable data.
- Per-subject accent overrides via subject config.
- Responsive: ≥1024 / 641–1023 / ≤640, with dropdown topic nav + mobile bottom-nav.
- Fully working clickable prototype under `prototype/`.
## Deploy & ops
- Live: `https://kontra.lab.audasmedia.com.au` · Admin: `https://admin.kontra.lab.audasmedia.com.au/web/admin/`
- Container: `kontra` on `.13:8600`, mounts content + SSH key, `restart: unless-stopped`
- Content autosync: entrypoint runs `git pull` every 30s, restarts the binary on HEAD change.
- Master route: Caddy on `.35` terminates TLS and `reverse_proxy 192.168.20.13:8600`.
- Build: `app/build.sh` (templ + go) or the multi-stage Dockerfile (builds golang + templ in-image).
## Security notes
- Decap → Gitea OAuth with **non-confidential (PKCE)** application, redirect URI pinned to the admin origin.
- Gitea CORS scoped to the admin origin; content repo private.
- Deploy token kept in a gitignored `.env`; no secrets in any repo.
- Content repo requires push access to publish — editorial permissions map to Gitea team access.
## Docs & maps
| Artifact | Path / URL |
|---|---|
| Mermaid source | `docs/kontra-architecture.mmd` |
| Architecture doc | `docs/architecture.md` |
| Interactive map | `docs/kontra-architecture-map.html` · [maps.lab.audasmedia.com.au/kontra_day/docs/](https://maps.lab.audasmedia.com.au/kontra_day/docs/) |
| Plan (decisions) | `PLAN.md` |
| Design system | `DESIGN.md` |
---
*Built with the GOTH stack — Go, templ, htmx, Tailwind-tokens — self-hosted on Gitea + Garage S3 + Caddy + Docker.*