11 KiB
11 KiB
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:
---
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 thekontra-day-contentgit 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 pullevery 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 bucketkontra-day(DECIDED) — do NOT rename/repurposeshared-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-webuimatching Garage v1.x), credentials in.env(admin tokenGARAGE_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
hometemplate 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_dayon .13 (per user decision). - Caddy on .35 is the master reverse proxy. It routes
kontra.day→.13Go binary,admin.kontra.day→ Decap on.13. - .13 Nix config note: The user indicated a note needs adding to
home.nixorconfiguration.nixon .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.nixfor services vshome.nix— to be confirmed when the .13 Caddy/Nix layout is inspected in Phase 3.) - Permissions (.13): web folders use NixOS
usersgroup — new folders needsudo 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)
- Phase One — Planning: tech stack, content model, admin, storage layout ✅ (this file)
- Phase Two — Design: front-end + admin design options, demo images/templates
- Phase Three — Build & test: Go binary, templates, Decap admin, media, deploy
- Phase Four — Create Live: see system-architect skill when ready
- Phase Five — Link email & DNS: see Website Host Migration plan when ready
- Phase Six — Integration: console, backups
- 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 pullevery 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.