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:
170
README.md
Normal file
170
README.md
Normal 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)
|
||||
|
||||

|
||||
|
||||
```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.*
|
||||
Reference in New Issue
Block a user