jwright d26e981d7a blog: expand the series to six pillars; add the catalogue generator
Drafted from the last month of real work - OpenShift fleet hardening, edge AI
and GPU, network policy enforcement, supply-chain security, automation and
observability - weighted toward the work stack the site advertises. Each has
a photo prompt in the house palette and a diagram brief.

Adds BLOG-LEDGER.md, generated from disk so it cannot drift, listing every
live and drafted idea in one place. Search it before drafting; claim the idea
when the folder is created rather than at publish, so two sessions cannot
collide. Expands the series from three arcs to six now the catalogue is deep
enough to split edge AI, automation and observability out of PLATFORM.
2026-08-04 03:21:49 +10:00
2026-06-17 17:20:54 +10:00

bztmon-site

The source for www.bztmon.com — Jonathon Wright's portfolio / résumé site. A fast, animated, security-hardened static site for a platform / infrastructure engineer.

This repo is public. It lives on a self-hosted public Gitea (git.bztmon.com), isolated from the private homelab GitOps. Never commit secrets — the static site needs none.

Stack

  • Astro (static output) + TypeScript + Tailwind v4
  • Zero JS by default; tiny islands for the theme toggle + scroll reveals
  • Content & config are data-driven (src/data/) — adding a project never touches a component

Develop

npm install
npm run dev        # http://localhost:4321
npm run check      # astro check (types + diagnostics)
npm run build      # static build → dist/
npm run preview    # serve the build locally
npm run gen:og     # regenerate the social-preview image (public/og.png)

Project layout

src/
  data/          site.ts, socials.ts, skills.ts, projects.ts, experience.ts
  components/     Hero, Nav, ThemeToggle, ProjectCard, SkillGroup, ...
  layouts/        Layout.astro (SEO/OG, theme bootstrap)
  pages/          index.astro, projects/, 404.astro
  styles/         tokens.css (theme), global.css
  lib/            build-time helpers (cv detection)
scripts/          gen-og.mjs, build-image.sh
nginx/            default.conf (security headers, caching) baked into the image
Dockerfile        Debian build stage → nginx-unprivileged runtime

Content TODOs (Jonathon)

  • Drop a real CV at public/cv.pdf — the Download CV button appears automatically.
  • Fill the TODO(Jonathon) markers in src/data/experience.ts, projects.ts, socials.ts (employer names, dates, GitHub/LinkedIn handles).

Publishing a post

A post is just a Markdown file in src/content/blog/. Write one by hand, or generate a schema-valid one with the publish helper (this is the seam an IaC/CI step calls):

# from flags
node scripts/new-post.mjs --title "My post" --summary "One line" \
     --tags "kubernetes,gpu" [--draft] [--bodyFile notes.md]

# from a JSON event (e.g. an Ansible/AWX deploy summary)
echo '{"title":"...","summary":"...","tags":["x"],"body":"## Hi\n..."}' \
  | node scripts/new-post.mjs --stdin

Commit the file to main → CI rebuilds and ships. A malformed post fails the build (frontmatter is zod-validated), so a bad pipeline event never reaches production.

CI/CD

.gitea/workflows/deploy.yml runs on a self-hosted runner (a dedicated unprivileged user on the bastion):

npm ci → astro check → audit-ci (high/critical gate) → build → scan dist →
SBOM (CycloneDX) → buildah build+push → open a digest-bump PR to home-ops

The PR is never auto-mergedhome-ops main is branch-protected; merging it is what triggers the ArgoCD rollout. The runner holds only least-privilege creds (a home-ops-scoped deploy key + a PR token + a registry push token).

  • npm run scan — build-time gate: no secrets, no inline scripts, no third-party origins.
  • .audit-ci.json — fails on high/critical advisories. One allowlisted: GHSA-gv7w-rqvm-qjhr (esbuild install-integrity; build-time only, mitigated by the committed lockfile + trusted registry).
  • renovate.json — keeps npm deps and the digest-pinned base images current.

Deploy

Built into a container image, served by nginx-unprivileged on a homelab Kubernetes cluster, exposed via Cloudflare Tunnel. The image is pinned by digest in the private home-ops repo and rolled out by ArgoCD. Manual/bootstrap build: scripts/build-image.sh push. See SECURITY.md for the full security posture.

S
Description
Source for www.bztmon.com
Readme 8.2 MiB
Languages
Astro 57%
TypeScript 14.8%
CSS 11.8%
JavaScript 10.3%
Shell 5.1%
Other 1%