diff --git a/docs/KONTRA-GUIDE.md b/docs/KONTRA-GUIDE.md new file mode 100644 index 0000000..db5e414 --- /dev/null +++ b/docs/KONTRA-GUIDE.md @@ -0,0 +1,157 @@ +# Kontra — Editing, Media, Admin & Ops + +Single source of truth for working with the Kontra site. Covers: how content is +structured, how to edit it (Obsidian or Decap), how media works, how the admin is +configured, and how the whole thing runs + deploys. + +**Live site:** 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 + +```mermaid +flowchart LR + subgraph editors["Content Editors"] + O[Obsidian vault
/obsidian/kontra_content] -->|git push| G + N[Neovim / CLI] -->|git push| G + D[Decap CMS
admin.kontra.lab] -->|OAuth + git commit| G + end + subgraph gitea["Gitea (.35 Docker)"] + G[(sam/kontra-content
markdown + images = origin)] + end + subgraph prod["Kontra container (.13 :8600)"] + P[git pull loop
every 30s] -->|restart on change| B + B[Go binary: templ + htmx + Goldmark] + end + subgraph media["Media (git files)"] + F[(Images beside articles
in the content repo)] + end + G -->|"SSH git"| P + B -->|"{{media:...}} shortcode"| F + B -->|"HTML"| CB[Caddy master (.35 :443)] + CB --> WWW[browsers] + D -->|login| G +``` + +## 1. Content structure (the rules) + +``` +content/ +├── config/site.yaml # site name, tagline, nav (lowercase keys) +├── subjects/ +│ ├── world/ +│ │ ├── index.md # SUBJECT index — Decap folder marker (not an article) +│ │ ├── _subject.yaml # subject config (name, template, color, ALL lowercase keys) +│ │ ├── chinese-chips/ +│ │ │ ├── index.md # THE ARTICLE (file is always index.md inside its folder) +│ │ │ ├── hero.jpg # images live beside their article +│ │ │ └── figure-2.jpg +│ │ └── quantum-ai/… +│ └── tech/… +├── pages/ # standalone pages (about, 404…) +│ └── about.md +└── _templates/ + └── article.md # Obsidian insert-template (⌘⇧T) +``` + +**Hard rules** +- Article file = **`index.md` inside its own folder** named exactly like the slug + (`subjects///index.md`). The folder name = the URL slug (lowercased). +- Filenames in a folder: **lowercase, no spaces** (`quantum-ai/`, not `Quantum-ai/`). +- Images go in the **same folder as the article** — never the repo root, never a shared folder. +- `_`-prefixed files are skipped by the site (`_subject.yaml`, `_templates/`, `.trash/`). + +## 2. Editing content + +### A. Obsidian (recommended daily editor) +Open **`/home/sam/obsidian/kontra_content`** (a symlink to the content repo) as a vault. +The **GitHub Sync** plugin auto-pushes on save (~15s). + +- **New article:** insert the template via **⌘⇧T → "article"** — this substitutes `{{title}}`/`{{date}}`. + (If you hand-copy the template file, those variables stay literal and break front-matter.) +- **New subject:** create a folder under `subjects/` with an `index.md` (used as the subject index) + and `_subject.yaml`. +- **Publish/hide:** `published: true` → live; `published: false` → hidden everywhere (404 + removed from all lists). Omit = published. +- **Featured:** `featured: true` puts it in the hero lead slot. + +### B. Decap (browser admin) +Login with **Gitea** → you get a nested tree: `Articles → world → chinese-chips …`. +Click an article to edit. Same file → same result. **No manual push needed** — Decap commits to Gitea, autosync publishes. + +## 3. Media (images) + +Images are **git files beside their article** — no S3/object store. + +``` +subjects/world/chinese-chips/ + index.md + hero.jpg ← dragged into Obsidian (or placed in the folder) +``` + +**Referencing** +- Body: `![Alt]({{media:chinese-chips/hero.jpg}})` +- Front-matter hero: `image: "{{media:chinese-chips/hero.jpg}}"` ← **must be quoted** +- Rule: `{{media:/}}` — slug lowercase, **exact filename incl. extension** (`.jpg` ≠ `.jpeg`), **no leading slash**, **no backticks** around a real image markdown line. + +The app serves `/media/` by mapping the slug→subject folder (safe-path guarded). + +## 4. The admin (Decap) config + +- Lives at **`app/src/web/admin/config.yml`** (embedded into the binary at build). +- **Nested collections** — this is how it lists the 2-level tree. Do NOT "simplify" it: +```yaml +collections: + - name: articles + label: Articles + folder: subjects + create: true + slug: "{{slug}}" + nested: { depth: 100, subfolders: false } # subfolders:false is REQUIRED + meta: { path: { widget: string, label: 'Path', index_file: 'index' } } +``` +- **Decap gotchas (do not repeat):** + - `index_file: 'index'` (not `_index`), `subfolders: false` (not true) + - Labels in single quotes: NO apostrophes inside (breaks YAML — use double quotes) + - The media library is disabled (`media_folder: ""`) — images are git files, not uploaded through Decap. +- `subjects` + `pages` collections also exist. Auth is **Gitea OAuth** (app `decap-kontra`, PKCE). + +## 5. Ops — how it runs + +**Machines** +| Machine | IP | Role | +|---|---|---| +| Gitea (origin) | .35 :3001 | `sam/kontra-content` (content+images), `sam/kontra` (app) | +| Kontra container | .13 :8600 | Go binary; pulls content every 30s + restarts on change | +| Caddy master | .35 :443 | TLS; `kontra.lab…` + `admin.kontra…` → `.13:8600` | +| Dev (.27) | .27 | Obsidian vault + app source + build | + +**Build** (after source changes, on .27) +```bash +cd app && ./build.sh # templ generate → go build → app/kontra-bin +``` +The container builds inside Docker (golang stage) — update `config.yml`/assets then redeploy. + +**Deploy to .13** (the container runs from `/home/sam/Docker/Containers/kontra/`) +```bash +# source (needed bits) +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`; and use `--delete` or stale files (like old `media.go`) break the build. + +**Content autosync** — pushes to `sam/kontra-content` are pulled by the container every 30s; server restarts on change. No CI/CD needed. + +**Secrets** — the Gitea deploy token lives in `.env` (repo root, **gitignored**, chmod 600). Never commit. + +## 6. Backups +- Content + images are in the git repo → backed up by Gitea (mirrors + clients) and the production clone on `.13`. +- The app source lives in git on Gitea. +- House-wide offsite backup covers these hosts per the Backup Architecture plan. + +## 7. For AI agents resuming this project +1. Read this file first. +2. The site is **live** and the pipeline works — prefer the smallest change + `rsync --delete` deploy + verify on `https://kontra.lab.audasmedia.com.au`. +3. Decap config: treat `config.yml` as sacred (nested `subfolders:false` + `index_file:index`). +4. Media = git files. `{{media:/}}`, quoted in front-matter, exact filename. +5. Don't reintroduce S3/Garage for media (dropped 2026-09-10). \ No newline at end of file diff --git a/docs/archify/kontra-architecture.architecture.json b/docs/archify/kontra-architecture.architecture.json index 4616f99..0537866 100644 --- a/docs/archify/kontra-architecture.architecture.json +++ b/docs/archify/kontra-architecture.architecture.json @@ -123,8 +123,8 @@ { "id": "garage", "type": "cloud", - "label": "Garage S3", - "sublabel": "kontra-day bucket", + "label": "Git media", + "sublabel": "images beside articles", "pos": [ 680, 160 @@ -310,7 +310,7 @@ "title": "Auth & media", "items": [ "Decap CMS logs in with Gitea OAuth (PKCE)", - "Media resolves from Garage S3 via {{media:}} shortcodes" + "Media = git files beside articles; {{media:slug/file}}" ] } ] diff --git a/docs/diagrams/kontra-architecture.png b/docs/diagrams/kontra-architecture.png index e41d814..0081fef 100644 Binary files a/docs/diagrams/kontra-architecture.png and b/docs/diagrams/kontra-architecture.png differ diff --git a/docs/diagrams/kontra-architecture.svg b/docs/diagrams/kontra-architecture.svg index 3b5cb5e..5f7d435 100644 --- a/docs/diagrams/kontra-architecture.svg +++ b/docs/diagrams/kontra-architecture.svg @@ -1 +1 @@ -

Network

Media (Garage S3 on .13)

Kontra container (192.168.20.13 :8600 Docker)

Content Editors

git push

git push

OAuth + git commit

restart on change

SSH git

shortcode: media

emits HTML

reverse_proxy :8600

https

https

browser

browser

Gitea (192.168.20.35 :3001 Docker)

Obsidian vault
/obsidian/kontra_content

sam/kontra-content
markdown origin of truth

Neovim / CLI

Decap CMS
admin.kontra.lab

git pull loop
every 30s

Go binary
templ + htmx + Goldmark

Markdown render
front-matter + shortcodes

Garage S3
kontra-day bucket

Caddy master
.35 :80/:443

kontra.lab.audasmedia.com.au

admin.kontra.lab.audasmedia.com.au

\ No newline at end of file +

Network

Media (git files)

Kontra container (192.168.20.13 :8600 Docker)

Content Editors

git push

git push

OAuth + git commit

restart on change

SSH git

{{media:slug/file}}

emits HTML

reverse_proxy :8600

https

https

browser

browser

Gitea (192.168.20.35 :3001 Docker)

Obsidian vault
/obsidian/kontra_content

sam/kontra-content
markdown origin of truth

Neovim / CLI

Decap CMS
admin.kontra.lab

git pull loop
every 30s

Go binary
templ + htmx + Goldmark

Markdown render
front-matter + shortcodes

Images beside articles
in the content repo

Caddy master
.35 :80/:443

kontra.lab.audasmedia.com.au

admin.kontra.lab.audasmedia.com.au

\ No newline at end of file diff --git a/docs/kontra-architecture-map.html b/docs/kontra-architecture-map.html index 36d1456..8c962ce 100644 --- a/docs/kontra-architecture-map.html +++ b/docs/kontra-architecture-map.html @@ -5007,15 +5007,15 @@ :8600 Docker - - Garage S3 · kontra-day bucket · Kontra container (.13 :8600) + + Git media · images beside articles · Kontra container (.13 :8600) - Garage S3 - kontra-day bucket + Git media + images beside articles @@ -5317,7 +5317,7 @@
  • • Decap CMS logs in with Gitea OAuth (PKCE)
  • -
  • • Media resolves from Garage S3 via {{media:}} shortcodes
  • +
  • • Media = git files beside articles; {{media:slug/file}}
diff --git a/docs/kontra-architecture.mmd b/docs/kontra-architecture.mmd index 78cfbed..4f2e4d5 100644 --- a/docs/kontra-architecture.mmd +++ b/docs/kontra-architecture.mmd @@ -15,12 +15,12 @@ flowchart LR B --> MD[(Markdown render
front-matter + shortcodes)] end - subgraph media["Media (Garage S3 on .13)"] - S3[(Garage S3
kontra-day bucket)] + subgraph media["Media (git files)"] + F[(Images beside articles
in the content repo)] end G -->|"SSH git"| P - B -->|"shortcode: media"| S3 + B -->|"{{media:slug/file}}"| F B -->|"emits HTML"| CB[Caddy master
.35 :80/:443] CB -->|"reverse_proxy :8600"| B