5.1 KiB
created, modified, type, status, tags, aliases
| created | modified | type | status | tags | aliases | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2026-09-09 11:54 | 2026-09-10 16:30 | project | active |
|
|
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
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.mdinside 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: falsehides 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:
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:
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.summust move withgo.mod; use--delete(stale files like oldmedia.gobreak 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
- Read this file first.
- Site is live; smallest change +
rsync --deletedeploy + verify on the live URL. - Treat
config.ymlas sacred (nestedsubfolders:false+index_file:index). - Media = git files;
{{media:<slug>/<file>}}, quoted in front-matter, exact filename. - 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.