Files
kontra/PLAN.md

168 lines
11 KiB
Markdown

# Kontra Day — Phase One Plan
## 1. Goal
A news-opinion website ("Kontra Day") with an admin panel. Pages are mainly text with images, links, video. Subject areas in navigation. Admin can add/remove/change pages, set subject areas, control what's on the front page, and edit media. Hosted on the home network (192.168.20.13), exposed via Caddy. SEO-friendly.
## 2. Decisions (from conversation)
| Decision | Choice | Why |
|---|---|---|
| **Stack** | **Go + templ + htmx + Alpine + Tailwind** ("GOTH") | Single static binary, server-rendered (SEO), no runtime deps. htmx for interactivity. |
| **Content format** | **Markdown (.md) + front-matter** | You write in never loses portability. Obsidian/Neovim native. Render via Goldmark (Go's CommonMark lib). |
| **Admin** | **Decap CMS** (at `admin.kontra.day`) | Free, open-source, browser-based UI that edits the same `.md` files directly and commits to git. No custom admin to build/maintain. |
| **Content source of truth** | **Git repo on Gitea (.35)** | History, rollback, multi-editor sync, off-site backup path. Matches your Gitea/Obsidian habits. |
| **Templates** | **templ components** chosen per page (front-matter `template:`) | See §6 — page-level → subject-level → site-level → theme fallback. |
| **Media** | **Garage S3 (on .13)** referenced via `{{media:...}}` shortcodes | Rename/migrate media without touching every article. |
| **Colours** | **CSS design tokens** (CSS custom properties), admin-editable values, not code | Per-subject accent colours as data, no template edits. |
| **Deploy** | **Git push → systemd timer `git pull` every 30s on .13** (no build step; Go renders on request) | Instant content updates; .27 can be offline. Timer chosen over webhook (fewer moving parts). |
| **Email phase (V)** | Revisit later — Go mail libs or hand off to network tooling | Defer until requirements are real. |
## 3. Topology (verified)
| Machine | IP | Role | Relevant bits |
|---|---|---|---|
| **.27** sam-4screen-desktop | 192.168.20.27 | Dev box — Neovim + Obsidian | Obsidian vault at `/home/sam/obsidian/` |
| **.13** nixos-desktop | 192.168.20.13 | **Web host, always-on** | Sites in `/var/www/`. Runs: Docker (Garage S3, Outline, PocketBase, family-home-lab stack), cluster of websites. Go binary + site lives here. |
| **.35** sam-ubuntu1 | 192.168.20.35 | Docker server | **Gitea** (web :3001, SSH :2222) = repo origin. **Caddy master reverse proxy** (`/Docker/Containers/caddy/`). Multiple other containers. |
| **.23** file-server | 192.168.20.23 | NFS file server | Backup target (via `.35` `/mnt/nfs_usb_3tb`). |
| **Garage S3** | — | Object storage **on .13** | S3 API `.13:3900`, admin `.13:3902`, `s3.lab.audasmedia.com.au`. Web UI intended at `garage-ui.lab.audasmedia.com.au`. |
### Key service locations (verified from notes)
| Service | Where | Note |
|---|---|---|
| **Gitea** | Docker on **.35** (`gitea-server-1`) | web `.35:3001`, SSH `.35:2222`, `gitea.lab.audasmedia.com.au` |
| **Caddy (master proxy)** | Docker on **.35** | Caddyfile in `/Docker/Containers/caddy/`. Routes `*.lab.audasmedia.com.au` + `kontra.day` |
| **Garage S3** | Docker on **.13** | `/home/sam/deployment/garage/`. Buckets per user + `shared-media`. v1.0.0 |
| **Outline** | Docker on **.13:3000** | `outline.lab.audasmedia.com.au`. Uses Garage S3 storage. REST API + MCP |
| **Vikunja** | vikunja.lab.audasmedia.com.au | Tasks + kanban. REST API `.../api/v1`. Token in `~/.config/environment.d/10-secrets.conf` |
| **Web files** | `.13:/var/www/` | NixOS `users` group → new folders need `sudo chown -R sam:users /var/www/<folder>` |
## 4. File / storage layout
### 4.1 Content repo (single git repo, origin on Gitea .35)
Proposed repo: `sam/kontra-day-content` on Gitea.
```
kontra-day-content/
├── config/
│ ├── site.yaml # site name, default template, nav, SEO defaults
│ ├── templates.yaml # template list
│ └── frontpage.yaml # front-page composition (blocks, order, featured)
├── subjects/ # one dir per subject area
│ ├── politics/
│ │ ├── _subject.yaml # name, slug, template default, accent colour
│ │ └── post-1.md
│ └── tech/
│ ├── _subject.yaml
│ └── article-2.md
├── articles/ # (optional flat pool w/ subject in front-matter)
├── pages/ # standalone pages (about, contact, 404)
└── media/ # (optional local fallback; primary = Garage S3)
```
Front-matter per page:
```yaml
---
title: My Opinion Piece
author: Sam
date: 2026-01-01
tags: [politics, tech]
template: article # which template
subject: politics # which subject area
image: {{media:cover.jpg}}
excerpt: A short summary...
published: true
publish_at: 2026-01-01 # optional scheduled publish
---
Body markdown here...
```
### 4.2 Distribution of copies
| Machine | Role | Path |
|---|---|---|
| **.35** (Gitea) | **Origin** — single source of truth | `sam/kontra-day-content` |
| **.13** (web host) | **Production clone** the Go binary serves | `/var/www/kontra_day/` |
| **.27** (dev) | **Authoring clone** — Obsidian vault + Neovim | `/home/sam/obsidian/kontra_day/` (separate small vault) |
- **Obsidian (DECIDED: option b)**: the content repo folder is a **separate small Obsidian vault** at `/home/sam/obsidian/kontra_day/`. It IS the `kontra-day-content` git repo → clean git-sync + deploy; main vault untouched. Obsidian's **git-sync plugin** pushes to Gitea automatically. User switches vaults in Obsidian when writing.
- **.13 never depends on .27** — it pulls from Gitea. Dev box can be offline; site stays live.
### 4.3 Sync / deploy flow
```
Editor on .27 Gitea on .35 Production on .13
Neovim / Obsidian ──push──▶ origin ──webhook/poll──▶ git pull
(clone) ▲ │
│ ▼
Decap (admin.kontra.day) ──commit──┘ Go binary renders .md → HTML
```
- **Trigger** (DECIDED): **systemd timer `git pull` every 30s on .13**. No webhook.
- **Obsidian git-sync** plugin auto-syncs (commit+push) on .27 (separate kontra_day vault). Neovim saves → auto-commit via small file-watcher if desired.
## 5. Media (Garage S3, on .13)
- **Bucket**: Garage on `.13` (S3 API `.13:3900`, admin `.13:3902`). **New dedicated bucket `kontra-day`** (DECIDED) — **do NOT rename/repurpose `shared-media`** (other systems depend on it). New bucket gets its own access keys; media isolated, independently wiped/backed up.
- **Access**: public reads via **Caddy route** (`/media/*` → bucket) rather than exposing Garage's port; private buckets can use presigned URLs. S3 root domain `.s3.lab.audasmedia.com.au`.
- **Admin shows media from Garage**: **Decap CMS media library** (DECIDED).
- **Shortcodes in .md**: `{{media:cover.jpg}}`, `{{vimeo:12345}}` → renderer resolves slug → actual URL at render time. Rename/migrate media without touching articles.
- **Reference notes**: [[Garage Web UI — handoff]] (web UI at `garage-ui.lab.audasmedia.com.au`, `khairul169/garage-webui` matching Garage v1.x), credentials in `.env` (admin token `GARAGE_ADMIN_TOKEN`).
## 6. Templates & design control (where template choice lives)
Resolution order: **page front-matter → subject `_subject.yaml` → site config → theme fallback**.
| Level | Field | Stored in |
|---|---|---|
| Page | `template: article` | front-matter |
| Subject | `template: photo-essay` (default for its pages) | `subjects/x/_subject.yaml` |
| Site | default template | `config/site.yaml` |
| Fallback | base template | theme folder |
- **Front page**: a special `home` template whose *composition* (blocks list: which subjects, order, featured stories) is an editable config (`config/frontpage.yaml`) — admin reorders/feeds blocks without touching code.
- **Colours**: CSS custom properties (design tokens like `--accent`). Per-subject overrides via a data attribute. Admin edits token *values*, never templates.
- **New templates = code = a deploy step** (fine; Phase 3).
## 7. Deploy config notes
- **Go binary runs in `/var/www/kontra_day` on .13** (per user decision).
- **Caddy on .35 is the master reverse proxy.** It routes `kontra.day` → `.13` Go binary, `admin.kontra.day` → Decap on `.13`.
- **.13 Nix config note**: The user indicated a note needs adding to `home.nix` or `configuration.nix` on .13 for Caddy/site serving. (Likely a Caddyfile or reverse-proxy snippet on .13 to serve `/var/www/kontra_day`; the exact Nix file — `configuration.nix` for services vs `home.nix` — to be confirmed when the .13 Caddy/Nix layout is inspected in Phase 3.)
- Permissions (.13): web folders use NixOS `users` group — new folders need `sudo chown -R sam:users /var/www/kontra_day`.
- Current sites deploy via manual lsyncd .27→.13; this site uses git-based deploy instead (see §4.3). Retire lsyncd for this site.
## 8. Phase One deliverables (this plan + tasks)
- [ ] This PLAN.md (decisions, topology, file layout)
- [ ] Register tasks in **Vikunja** (projects, stages) and **Outline** (docs) — see PHASE ONE section
- [ ] Confirm open items: exact Caddy/Nix file on .13, webhook vs timer, Decap media library approach, Garage bucket name
- [ ] Phase 2 (design) kickoff when approved
## 9. Phases (from AGENT.md)
1. **Phase One — Planning**: tech stack, content model, admin, storage layout ✅ (this file)
2. **Phase Two — Design**: front-end + admin design options, demo images/templates
3. **Phase Three — Build & test**: Go binary, templates, Decap admin, media, deploy
4. **Phase Four — Create Live**: see system-architect skill when ready
5. **Phase Five — Link email & DNS**: see Website Host Migration plan when ready
6. **Phase Six — Integration**: console, backups
7. **Phase Seven — Marketing**: populate website, marketing
## 10. Decisions & remaining open items
### Decided (confirmed)
- Garage bucket: **new dedicated `kontra-day`** (shared-media untouched)
- Deploy trigger: **systemd timer `git pull` every 30s** on .13
- Admin media: **Decap CMS media library**
- Obsidian: **separate small vault** `/home/sam/obsidian/kontra_day/` = the content repo
### Remaining open items
- Exact Nix file on .13 for Caddy/site (configuration.nix vs home.nix) — inspect in Phase 3
- Whether to auto-commit Neovim saves via file-watcher (vs rely on git-sync in Obsidian + timer on .13)
---
*Cross-references: AGENT.md; Obsidian notes: Garage Web UI — handoff, Backup Architecture — Offsite to AWS S3, Filesystem Drive Map, Dev-Ops Tooling — Mermaid/Archify/Vikunja/Outline, Docker Containers.*