docs: refresh README for the current layout; ASCII sweep on SECURITY.md

This commit is contained in:
2026-08-04 03:45:58 +10:00
parent f89957370e
commit df72a22952
3 changed files with 54 additions and 63 deletions
+42 -54
View File
@@ -1,18 +1,19 @@
# bztmon-site
The source for **[www.bztmon.com](https://www.bztmon.com)** — Jonathon Wright's
portfolio / résumé site. A fast, animated, security-hardened static site for a
platform / infrastructure engineer.
Source for [www.bztmon.com](https://www.bztmon.com) - portfolio and blog for a
platform / infrastructure engineer. Static output, no server runtime, hardened
by default.
> 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.
> This repo is public and lives on a self-hosted Gitea (`git.bztmon.com`),
> isolated from the private GitOps repos. The static site needs no secrets;
> none are committed.
## 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
- Astro (static output), TypeScript, Tailwind v4
- Zero JS by default; small islands for the theme toggle and scroll reveals
- Content and config are data-driven (`src/data/`, `src/content/`) - adding a
post or project never touches a component
## Develop
@@ -20,72 +21,59 @@ platform / infrastructure engineer.
npm install
npm run dev # http://localhost:4321
npm run check # astro check (types + diagnostics)
npm run build # static build dist/
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
## Layout
```
src/
data/ site.ts, socials.ts, skills.ts, projects.ts, experience.ts
components/ Hero, Nav, ThemeToggle, ProjectCard, SkillGroup, ...
content/blog/ posts (Markdown, zod-validated frontmatter)
content/projects/ project dossiers
data/ site.ts, series.ts, skills.ts, projects.ts, experience.ts
components/ Hero, Nav, PostList, ProjectCard, ...
layouts/ Layout.astro (SEO/OG, theme bootstrap)
pages/ index.astro, projects/, 404.astro
pages/ index, blog/, projects/, 404
styles/ tokens.css (theme), global.css
lib/ build-time helpers (cv detection)
scripts/ gen-og.mjs, build-image.sh
scripts/ gen-og.mjs, build-image.sh, new-post.mjs
public/ heroes (blog/), diagrams (diagrams/), og.png, cv
nginx/ default.conf (security headers, caching) baked into the image
Dockerfile Debian build stage nginx-unprivileged runtime
Dockerfile Debian build stage -> nginx-unprivileged runtime
```
## Content TODOs (Jonathon)
## Posts
- 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):
```bash
# 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.
A post is a Markdown file in `src/content/blog/` with zod-validated
frontmatter; a malformed post fails the build, so a bad file never reaches
production. `scripts/new-post.mjs` scaffolds one from flags or a JSON event on
stdin - the seam a CI step can call. Each post belongs to one numbered series
(`src/data/series.ts`), which drives the badge on the index cards.
## CI/CD
`.gitea/workflows/deploy.yml` runs on a self-hosted runner (a dedicated unprivileged
user on the bastion):
`.gitea/workflows/deploy.yml` runs on a self-hosted runner (a dedicated
unprivileged user on a utility host):
```
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
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-merged**`home-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).
The PR is never auto-merged - the deploy repo's `main` is branch-protected, and
merging the PR is what triggers the ArgoCD rollout. The runner holds
least-privilege credentials only: a scoped deploy key, a PR token, and 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.
- `npm run scan` - build-time gate: no secrets, no inline scripts, no
third-party origins in `dist/`
- `.audit-ci.json` - fails the build on high/critical advisories
- `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.
Built into a container image, served by nginx-unprivileged on Kubernetes,
exposed outbound-only via Cloudflare Tunnel. The image is pinned by digest in
the private deploy repo and rolled out by ArgoCD. Manual bootstrap build:
`scripts/build-image.sh push`. Full posture in `SECURITY.md`.