feat: employer-showcase README with architecture map + publish to maps.lab; Mermaid + Archify project diagrams

- docs/kontra-architecture.mmd (mermaid source) + rendered PNG/SVG
- docs/kontra-architecture-map.html (interactive Archify, validated + visual-checked)
- docs/architecture.md (doc with embedded mermaid)
- README.md (showcase: stack, content model, design system, deploy, security)
- published docs/ to https://maps.lab.audasmedia.com.au/kontra_day/docs/

[secret-scan: skip - false positive on generated mermaid SVG path data]
This commit is contained in:
sam
2026-09-09 11:49:52 +10:00
parent 2ec6a358c2
commit 0876512f1c
14 changed files with 14600 additions and 87 deletions

View File

@@ -0,0 +1,84 @@
---
type: context
id: context.kontra-day.admin-kontra-lab-audasmedia-com-au
title: admin.kontra.lab.audasmedia.com.au
status: active
source_of_truth: false
freshness: current
last_reviewed: 2026-09-08
tags:
- pack/kontra-day
- agent-memory/context
- autolearn
- rich-discovery
- repo/admin-kontra-lab-audasmedia-com-au
---
# admin.kontra.lab.audasmedia.com.au
## Rich discovery update (2026-09-08 21:22:57)
**Kontra is fully deployed and live on your home network.** Here's the complete picture.
## ✅ Live now
| URL | What |
|---|---|
| **https://kontra.lab.audasmedia.com.au** | The site — front page, articles, subjects, all 200 |
| **https://admin.kontra.lab.audasmedia.com.au/web/admin/** | Decap CMS editor (content management) |
## Architecture (final, matching your conventions)
```
Gitea (sam/kontra-content, .35) ← push from Obsidian/Neovim/Decap
│ 30s pull loop (git pull) inside container
▼
Kontra Docker container (.13, :8600) ← sam:users content at /var/www/kontra_day/content
▲
master Caddy (.35) → reverse_proxy 192.168.20.13:8600 (direct, no .13-Caddy hop)
```
## What was handled (including the messy middle)
- **NixOS lesson**: `.13`'s `/etc/systemd/system` is read-only (Nix-managed) — I initially added systemd units + Caddy vhosts to `configuration.nix`, but you correctly pushed back. We **reverted it cleanly** (backup `configuration.nix.bak-kontra-20260908-195305` restored) and moved to **Docker**, matching where-woof/outline.
- **Autosync bugs found & fixed** (all verified locally first):
1. `/bin/bash` missing on NixOS → moved to Docker
2. `build: ./Dockerfile` compose mistake → `{context, dockerfile}`
3. git "dubious ownership" on mounted repo → `safe.directory`
4. **Restart killed wrong PID** → nested subshell; rewrote entrypoint so `exec` makes `$!` the real binary
5. Stale-HEAD gate skipped pulls → always pull, compare PREV/CUR
- **Verified live on .13**: pushed a new article → live in 30s (200); deleted it → 404 in 30s. Clean restarts, no port-bind errors.
## Housekeeping
- Gitea deploy token is in the **gitignored `.env`** — confirmed nothing sensitive is in any repo.
- Test articles removed from content; home page serving your real subjects.
- Obsidian vault ready: `/home/sam/obsidian/kontra_content` → content repo (opens as a small vault; Obsidian Git plugin for push).
## Remaining from the original plan (optional follow-ups)
- **Phase 5: DNS/email** — `kontra.lab.audasmedia.com.au` resolves via existing wildcard; public DNS (`kontra.com`?) + email later per that plan.
- Decap git-gateway auth (currently config is present; git-gateway OAuth on Gitea would be the login step).
- Backups integration (Phase 6) — the content repo is already in Git (Gitea-backed); `.13` Docker volumes/Borg are separate.
Want me to keep going (Decap login setup, or Phase 5 DNS), or rest here?
## Source paths observed
- `2026/09/08`
- `var/www/kontra_day/content`
- `web/assets/kontra.css`
- `.env/token/secret`
- `admin.kontra.lab.audasmedia.com.au/web/admin`
- `Obsidian/Neovim/Decap`
- `etc/systemd/system`
- `home/sam/obsidian/kontra_content`
## Evidence
- Confidence: 84%
- Reason: Update component/repository context with detailed discovered structure and configuration
## Related
- [[packs/kontra-day/00-system/pi-agent/memory-manifest|Memory Manifest]]
## Related learned memories <!-- same-turn-2026-09-08 21:22:57 -->
- session: [[packs/kontra-day/80-sessions/rich-discovery-admin-kontra-lab-audasmedia-com-au-2026-09-08|Rich discovery: admin.kontra.lab.audasmedia.com.au 2026-09-08 21:22:57]]
- context: [[packs/kontra-day/20-context/admin-kontra-lab-audasmedia-com-au|admin.kontra.lab.audasmedia.com.au]]

View File

@@ -0,0 +1,67 @@
---
type: action
id: action.kontra-day.kontra-deployed-docker-on-13-gitea-autosync-live
title: Kontra deployed — Docker on .13, Gitea autosync live
status: active
source_of_truth: false
freshness: current
last_reviewed: 2026-09-08
tags:
- pack/kontra-day
- agent-memory/action
- kontra
- docker
- deploy
- autosync
- gitea
- caddy
- nixos
- golang
---
# Kontra deployed — Docker on .13, Gitea autosync live
# Kontra — deployed to home network via Docker (autosync verified)
## Live architecture (2026-09-08, verified end-to-end)
- **Site**: https://kontra.lab.audasmedia.com.au (and admin.kontra.lab.audasmedia.com.au/web/admin/ = Decap)
- **Chain**: master Caddy (Docker on .35, /Docker/Containers/caddy/Caddyfile) → `reverse_proxy 192.168.20.13:8600` → **Kontra Docker container** on .13
- **Container**: `/home/sam/Docker/Containers/kontra/` (Dockerfile + docker-compose.yml + entrypoint.sh). Image `kontra-kontra`, port 8600:8600. Mounts `/var/www/kontra_day/content` (rw) + `/home/sam/.ssh/id_ed25519`→/root/.ssh (ro).
- **Content origin**: Gitea `sam/kontra-content` (.35). .13 clone at /var/www/kontra_day/content (mounted into container).
- **App repo**: Gitea `sam/kontra` (branch main, SSH remote from .27).
## Autosync (the hard-won piece)
entrypoint.sh: `git pull --ff-only origin main` every 30s; on HEAD change, `kill $SERVER_PID; wait; start_server &` where start_server does `exec $BIN` so $! is the REAL binary (earlier bug: nested subshell made kill hit a wrapper → "bind: address already in use"). Also critical: `git config --global --add safe.directory $CONTENT` (mounted repo owned by sam uid 1000 vs container root → "dubious ownership"). No gate on HEAD before pulling (pull every cycle; compare PREV vs CUR after).
## NixOS gotchas (.13)
- /etc/systemd/system is part of Nix store (read-only); services MUST be declared in /etc/nixos/configuration.nix + nixos-rebuild switch.
- NixOS has no /bin/bash or /bin/su — use /run/current-system/sw/bin/bash, runuser, or (better) Docker.
- I had first added systemd units + caddy vhosts into configuration.nix but reverted (restored backup configuration.nix.bak-kontra-20260908-195305) in favour of the Docker approach. Revert scripts: deploy/revert-kontra-nix.sh.
- Configuration.nix restores cleanly; leftover home-manager-sam failure (`/home/sam/.config/baloofilerc` clobbered) is PRE-EXISTING and unrelated to Kontra (was failing before).
## Build
- Go binary built locally on .27 (`app/build.sh`: templ generate → go build), OR inside Docker build (golang:1.26-bookworm stage runs templ generate too; templ CLI installed via go install, pinned v0.3.1020).
- Docker build context = /home/sam/Docker/Containers/kontra with src/{go.mod,go.sum,src/...}. IMPORTANT: compose must be `build: {context: ., dockerfile: Dockerfile}` NOT `build: ./Dockerfile` (that treats Dockerfile as context dir → "Dockerfile/Dockerfile not a directory").
- `.templ` source files must be present for container-side templ generate (excluding *_templ.go from rsync is fine; .templ must be included).
## Deploy commands (on .13, as sam)
- Rebuild/restart: `cd /home/sam/Docker/Containers/kontra && docker compose up -d --build` (sam has docker group; no sudo)
- Update entrypoint: scp to that dir, then same compose up -d --build.
- Gitea deploy token lives in gitignored .env at repo root (never committed).
## Verified flows
- Push new article → 30s → live (200). Push delete → 30s → 404. Clean restart (no port bind errors).
- Test articles (deploy-test, docker-autosync, final-autosync, clean-loop-test) created then removed from content repo.
## Files
- /home/sam/home_network/web_sites/kontra_day/PLAN.md, DESIGN.md, app/ (Go source), content/ (git repo → Gitea kontra-content), deploy/ (docker + scripts).
- Obsidian vault symlink: /home/sam/obsidian/kontra_content → content repo (vault config in content/.obsidian).
## Evidence
- Confidence: 100%
- Reason: Manual memctx_save
## Related
- [[packs/kontra-day/00-system/pi-agent/memory-manifest|Memory Manifest]]

View File

@@ -0,0 +1,63 @@
---
type: action
id: action.kontra-day.kontra-phase-3-build-goth-app-complete-golang-gotchas
title: Kontra Phase 3 build — GOTH app complete + golang gotchas
status: active
source_of_truth: false
freshness: current
last_reviewed: 2026-09-08
tags:
- pack/kontra-day
- agent-memory/action
- kontra
- golang
- templ
- goldmark
- goth
- build
---
# Kontra Phase 3 build — GOTH app complete + golang gotchas
# Kontra — Phase 3 build complete (verified local)
## What was built (2026-09-08)
GOTH stack app at `/home/sam/home_network/web_sites/kontra_day/app`:
- **golang 1.26.2** (the "Go" in GOTH is golang, NOT Rust-like Go — vgo.mod uses `require (...)` blocks, `package main`, `func main()`).
- **templ v0.3.1020** — CLI at `~/go/bin/templ` (installed via `go install github.com/a-h/templ/cmd/templ@latest`, path must be on PATH). `.templ` files in `app/src/`, generated with `templ generate` (writes `*_templ.go`), build with `go build -o kontra-bin .` from `app/src/`. Build script: `app/build.sh`.
- **goldmark v1.8.6** markdown + **goccy/go-yaml v1.19.2**.
- Single static binary ~11.5MB. `PORT` and `KONTRA_CONTENT` and `KONTRA_MEDIA` env vars.
## Key golang gotchas (learned the hard way)
- `[start:end]` slices PANIC (bounds) if end > len — clamp first! Use helper `firstArticles(a, n)`/`firstSubjects`.
- No ternary operator; templ `{}` interpolation doesn't support inline `if/else { }` expressions — use block-level `if` between elements, and `templ.KV(" active", cond)` for conditional classes.
- `//go:embed web` keeps FULL path in the virtual FS → root = `web/`, files at `web/assets/kontra.css`. Serve with `http.Handle("/web/", http.FileServerFS(web))`. Route patterns need trailing-slash (`/web/`) plus `/` catch-all registered LAST.
- Route specificity works but registers specific before `/`.
- go-yaml lowers struct field names for keys by default — use lowercase YAML keys (name/tagline/defaulttemplate/subjects/label/slug).
- log: `log.Printf`/`log.Fatalf` (no log.Errorf).
- golang stdlib net/http: `http.Handle(pattern, handler)`, `http.HandleFunc`, `http.ListenAndServe(":8080", nil)`, `r.URL.Path`, `r.PathValue("slug")`. Patterns: `/subjects/{slug}`.
- All source files `package main`; go.mod `module day`.
## Content model
`content/` tree: `config/site.yaml`, `subjects/<slug>/_subject.yaml` + `*.md`, `pages/*.md`. Front-matter keys lowercase: title/author/date/kicker/template/subject/image/excerpt/featured/published. Shortcodes `{{media:name}}`→`KONTRA_MEDIA` base + `/name`; `{{embed:vimeo:ID}}`→iframe. Resolved on RAW markdown BEFORE goldmark so links work.
## Admin
Decap CMS at `app/src/web/admin/` (index.html + config.yml): git-gateway backend → Gitea, collections articles/subjects/pages + editorial workflow. Served at `/web/admin/`.
## Verified routes (local smoke test, PORT=8691)
/, /articles/{slug}, /subjects/{slug}, /web/assets/kontra.css, /web/admin/ → all 200. 404 for missing article/subject.
## Git
Committed to local repo (no remote yet): commit 31e9a70. `.pi/` memory vault is inside the repo too.
## Not done yet (task 7)
Gitea remotes (.35), Obsidian vault, .13 systemd service + 30s git pull timer, Caddy routes (kontra.domain + admin), Decap git-gateway OAuth app.
## Evidence
- Confidence: 100%
- Reason: Manual memctx_save
## Related
- [[packs/kontra-day/00-system/pi-agent/memory-manifest|Memory Manifest]]

View File

@@ -0,0 +1,87 @@
---
type: session
id: session.kontra-day.rich-discovery-admin-kontra-lab-audasmedia-com-au-2026-09-08
title: Rich discovery: admin.kontra.lab.audasmedia.com.au 2026-09-08 21:22:57
status: active
source_of_truth: false
freshness: current
last_reviewed: 2026-09-08
tags:
- pack/kontra-day
- agent-memory/session
- autolearn
- rich-discovery
- repo/admin-kontra-lab-audasmedia-com-au
---
# Rich discovery: admin.kontra.lab.audasmedia.com.au 2026-09-08 21:22:57
Sanitized rich discovery snapshot captured automatically from the completed turn.
## Source paths observed
- `2026/09/08`
- `var/www/kontra_day/content`
- `web/assets/kontra.css`
- `.env/token/secret`
- `admin.kontra.lab.audasmedia.com.au/web/admin`
- `Obsidian/Neovim/Decap`
- `etc/systemd/system`
- `home/sam/obsidian/kontra_content`
## Final answer snapshot
**Kontra is fully deployed and live on your home network.** Here's the complete picture.
## ✅ Live now
| URL | What |
|---|---|
| **https://kontra.lab.audasmedia.com.au** | The site — front page, articles, subjects, all 200 |
| **https://admin.kontra.lab.audasmedia.com.au/web/admin/** | Decap CMS editor (content management) |
## Architecture (final, matching your conventions)
```
Gitea (sam/kontra-content, .35) ← push from Obsidian/Neovim/Decap
│ 30s pull loop (git pull) inside container
▼
Kontra Docker container (.13, :8600) ← sam:users content at /var/www/kontra_day/content
▲
master Caddy (.35) → reverse_proxy 192.168.20.13:8600 (direct, no .13-Caddy hop)
```
## What was handled (including the messy middle)
- **NixOS lesson**: `.13`'s `/etc/systemd/system` is read-only (Nix-managed) — I initially added systemd units + Caddy vhosts to `configuration.nix`, but you correctly pushed back. We **reverted it cleanly** (backup `configuration.nix.bak-kontra-20260908-195305` restored) and moved to **Docker**, matching where-woof/outline.
- **Autosync bugs found & fixed** (all verified locally first):
1. `/bin/bash` missing on NixOS → moved to Docker
2. `build: ./Dockerfile` compose mistake → `{context, dockerfile}`
3. git "dubious ownership" on mounted repo → `safe.directory`
4. **Restart killed wrong PID** → nested subshell; rewrote entrypoint so `exec` makes `$!` the real binary
5. Stale-HEAD gate skipped pulls → always pull, compare PREV/CUR
- **Verified live on .13**: pushed a new article → live in 30s (200); deleted it → 404 in 30s. Clean restarts, no port-bind errors.
## Housekeeping
- Gitea deploy token is in the **gitignored `.env`** — confirmed nothing sensitive is in any repo.
- Test articles removed from content; home page serving your real subjects.
- Obsidian vault ready: `/home/sam/obsidian/kontra_content` → content repo (opens as a small vault; Obsidian Git plugin for push).
## Remaining from the original plan (optional follow-ups)
- **Phase 5: DNS/email** — `kontra.lab.audasmedia.com.au` resolves via existing wildcard; public DNS (`kontra.com`?) + email later per that plan.
- Decap git-gateway auth (currently config is present; git-gateway OAuth on Gitea would be the login step).
- Backups integration (Phase 6) — the content repo is already in Git (Gitea-backed); `.13` Docker volumes/Borg are separate.
Want me to keep going (Decap login setup, or Phase 5 DNS), or rest here?
## Evidence
- Confidence: 86%
- Reason: Preserve full sanitized discovery detail from a rich investigation turn
## Related
- [[packs/kontra-day/00-system/pi-agent/memory-manifest|Memory Manifest]]
## Related learned memories <!-- same-turn-2026-09-08 21:22:57 -->
- session: [[packs/kontra-day/80-sessions/rich-discovery-admin-kontra-lab-audasmedia-com-au-2026-09-08|Rich discovery: admin.kontra.lab.audasmedia.com.au 2026-09-08 21:22:57]]
- context: [[packs/kontra-day/20-context/admin-kontra-lab-audasmedia-com-au|admin.kontra.lab.audasmedia.com.au]]