137 lines
5.1 KiB
Markdown
137 lines
5.1 KiB
Markdown
---
|
|
created: 2026-09-09 11:54
|
|
modified: 2026-09-10 16:30
|
|
type: project
|
|
status: active
|
|
tags:
|
|
- website
|
|
- politics
|
|
- portfolio
|
|
- kontra
|
|
- goth
|
|
- golang
|
|
- self-hosted
|
|
- editing
|
|
- media
|
|
- admin
|
|
- ops
|
|
aliases:
|
|
- Kontra
|
|
- Kontra Day
|
|
---
|
|
|
|
# Kontra — Editing, Media, Admin & Ops
|
|
|
|
> **Kontra** is a self-hosted news-opinion site engine. Content + images are plain files in a git
|
|
> repo; a single static Go binary renders them into an SEO-friendly editorial site. Editors work in
|
|
> **Obsidian** (daily) or **Decap** (browser). No database, no S3/object store.
|
|
|
|
**Live:** https://kontra.lab.audasmedia.com.au
|
|
**Admin (Decap):** https://admin.kontra.lab.audasmedia.com.au/web/admin/
|
|
**Architecture map:** https://maps.lab.audasmedia.com.au/kontra_day/docs/kontra-architecture-map.html
|
|
**Repo (public):** https://gitea.lab.audasmedia.com.au/sam/kontra
|
|
|
|
```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 (.35)"]
|
|
G[(sam/kontra-content<br/>md + images = origin)]
|
|
end
|
|
subgraph prod["Kontra container (.13 :8600)"]
|
|
P[git pull 30s] -->|restart on change| B
|
|
B[Go: templ+htmx+Goldmark]
|
|
end
|
|
subgraph media["Media (git files)"]
|
|
F[(images beside articles)]
|
|
end
|
|
G -->|SSH| P
|
|
B -->|"{{media:slug/file}}"| F
|
|
B -->|HTML| CB[Caddy .35 :443] --> WWW[browsers]
|
|
```
|
|
|
|
---
|
|
|
|
## 1. Content structure (rules)
|
|
|
|
```
|
|
content/
|
|
├── config/site.yaml
|
|
├── subjects/
|
|
│ └── world/
|
|
│ ├── index.md # SUBJECT index (Decap folder marker, not an article)
|
|
│ ├── _subject.yaml
|
|
│ ├── chinese-chips/
|
|
│ │ ├── index.md # THE ARTICLE — file is always index.md
|
|
│ │ ├── hero.jpg # images beside their article
|
|
│ └── quantum-ai/
|
|
├── pages/about.md
|
|
└── _templates/article.md
|
|
```
|
|
|
|
- **Article file = `index.md` inside its own folder** (folder name = URL slug, lowercase).
|
|
- Lowercase filenames, no spaces. Images in the **same folder** as the article.
|
|
- `_`-prefixed files are skipped by the site (`_subject.yaml`, `_templates/`, `.trash/`).
|
|
|
|
## 2. Editing
|
|
|
|
**Obsidian (daily):** open `/home/sam/obsidian/kontra_content` as a vault; **GitHub Sync** auto-pushes.
|
|
New article: **⌘⇧T → article** (substitutes `{{title}}`/`{{date}}` — hand-copying leaves them literal & breaks front-matter).
|
|
- `published: false` hides everywhere; omit = published. `featured: true` = hero.
|
|
|
|
**Decap (browser):** login with Gitea → nested tree `Articles → world → chinese-chips…`. Same result, commits to Gitea automatically.
|
|
|
|
## 3. Media
|
|
|
|
Images are **git files** beside their article (no S3).
|
|
- Body: ``
|
|
- Hero front-matter: `image: "{{media:chinese-chips/hero.jpg}}"` (**must be quoted**)
|
|
- Rule: `{{media:<slug>/<exact-file>}}` — lowercase slug, **exact filename incl. extension**, no leading slash, no backticks.
|
|
- App serves `/media/<path>` mapping slug→subject folder (safe-path guarded).
|
|
|
|
## 4. Admin (Decap) config
|
|
|
|
`app/src/web/admin/config.yml` (embedded at build). **Nested collections** list the 2-level tree:
|
|
```yaml
|
|
collections:
|
|
- name: articles
|
|
folder: subjects
|
|
create: true
|
|
slug: "{{slug}}"
|
|
nested: { depth: 100, subfolders: false } # subfolders:false REQUIRED
|
|
meta: { path: { widget: string, label: 'Path', index_file: 'index' } }
|
|
```
|
|
**Gotchas:** `index_file:'index'` not `_index`; `subfolders:false` not true; no apostrophes inside single-quoted labels (use double quotes); media library disabled (`media_folder:""`). Auth = Gitea OAuth (app `decap-kontra`, PKCE).
|
|
|
|
## 5. Ops
|
|
|
|
**Machines:** Gitea .35:3001 (origin) · Kontra container .13:8600 · Caddy master .35:443 · dev .27.
|
|
|
|
**Build** (after source changes, on .27): `cd app && ./build.sh`
|
|
|
|
**Deploy to .13:**
|
|
```bash
|
|
scp app/go.mod app/go.sum sam@192.168.20.13:/home/sam/Docker/Containers/kontra/src/
|
|
rsync -az --delete --exclude bin --exclude kontra-bin --exclude .git \
|
|
app/src/ sam@192.168.20.13:/home/sam/Docker/Containers/kontra/src/src/
|
|
ssh sam@192.168.20.13 'cd /home/sam/Docker/Containers/kontra && docker compose up -d --build'
|
|
```
|
|
- `.go.sum` must move with `go.mod`; use `--delete` (stale files like old `media.go` break builds).
|
|
- Content autosync: push → container pulls 30s → restart. No CI/CD.
|
|
- Secrets: Gitea token in `.env` (gitignored, chmod 600).
|
|
|
|
## 6. Backups
|
|
Content + images in git → backed by Gitea + production clone on .13. App source in git. Offsite per Backup Architecture.
|
|
|
|
## 7. For AI agents resuming
|
|
1. Read this file first.
|
|
2. Site is live; smallest change + `rsync --delete` deploy + verify on the live URL.
|
|
3. Treat `config.yml` as sacred (nested `subfolders:false` + `index_file:index`).
|
|
4. Media = git files; `{{media:<slug>/<file>}}`, quoted in front-matter, exact filename.
|
|
5. Don't reintroduce S3/Garage (dropped 2026-09-10).
|
|
|
|
---
|
|
*Full repo copy of this guide: `docs/KONTRA-GUIDE.md`. Architecture map published via Archify → maps.lab.* |