# Website plan — `https://kimmo.cloud/ghostlock/`

Publish the GhostLock tracker as a single-page static site, generated by
**Hugo** locally and rsync'd to a personal nginx server (`haig`).  The build
infrastructure mirrors the sibling januscape / itscape / ipv6_frag_escape
trackers; only the content and a few config strings differ.

## Architecture

- **Source repo:** `github.com/suominen/ghostlock` (this repo).
- **Generator:** Hugo extended, ≥ 0.146.0.  Run locally; nothing built in
  CI.
- **Source layout:** Hugo project under `site/`.  The tracker is a single
  page at `site/content/_index.md`.
- **Theme:** PaperMod, integrated as a Hugo Module (no submodule).
- **Dev environment:** Nix flake (`flake.nix`) provides hugo + go + git +
  resvg (for `make banner`) + curl/libarchive/zstd (the auto-update agent's
  RPM-unpacking tools).  Auto-activates via `.envrc` if direnv is installed.
- **Build:** `make build` → `hugo --minify --gc --cleanDestinationDir`,
  output in `site/public/`.
- **Publish:** `make dist` → `rsync -avz --delete site/public/` →
  `haig:/ghostlock/`.
- **Web server:** existing nginx vhost on `kimmo.cloud` serves
  `htdocs/ghostlock/` directly at the URL path `/ghostlock/`.

## Naming — nickname slug (CVE assigned)

The bug has a marketable name (**GhostLock**) *and* a CVE
(**CVE-2026-43499**) from the outset.  Following the sibling precedent —
which runs both ways (januscape/itscape kept nicknames; Dirty Frag was
renamed to `/CVE-2026-43284/`) — the site uses the **nickname** as its slug
everywhere, with the CVE carried in the tracker body, Summary, and title:

- repo: `~/src/ghostlock`, `github.com/suominen/ghostlock`
- Go module path: `github.com/suominen/ghostlock/site`
- URL: `https://kimmo.cloud/ghostlock/`
- systemd units: `ghostlock-tracker-update.{service,timer}`

No redirect is needed; the slug never moves.

## No companion tracker

Unlike the januscape/itscape pair (x86 + arm64 KVM escapes), GhostLock is a
single architecture-independent kernel-locking bug — there is no sibling
tracker to cross-reference.  It is part of Nebula Security's *IonStack*
research series (this is part II), which is credited in the Summary and
References but is not a separate tracker.

## Tracker content

- One kernel bug, **single verdict axis**: the `3bfdc63936dd` fix (v7.1) is
  the only thing that flips a row.  There is **no** userspace reachability
  gate and **no** exposure discriminator — the trigger is plain `futex(2)`,
  reachable by any unprivileged local task, so there is nothing to record as
  a second axis or a `:warning:` downgrade.
- **Patch status** (renamed 2026-07-25; seeded as *Distribution status*
  plus a separate *Upstream fixed versions* table) uses a single
  **combined table** for upstream and distros (`Distribution | Release |
  Current kernel | First fixed | Fixed since | Status`): the upstream
  kernel is the first `Linux kernel` group; opt-in kernels (bullseye
  `linux-6.1`, PVE opt-in series) are their own rows, while the niche
  EL `kernel-rt` variant stays prose-only (rows were tried 2026-07-25
  and dropped — the long NVRs bloated the table); per-distro `###`
  headings are retained only where there is an audience-relevant note.
  *Current kernel* is live, *First fixed* / *Fixed since* are sticky.
  In the browser the Distribution column renders as full-width group
  heading rows (see "Client-side table tweaks" below).
  GhostLock pilots this single-table layout —
  once settled it is to be folded into the tracker template and
  januscape (the other sibling trackers are archived and stay on the
  old layout).
- Because the bug dates to **v2.6.39**, every tracked kernel is in-window —
  there are **no** "not affected" rows here.  Rows seed `:x:` unless a
  backport is confirmed.  The RHEL-family and Amazon rows are
  backport-versioned (base version doesn't map to an upstream point release)
  and had no advisory at seed, so they seed `:x:` with a prose caveat.  No
  unconfirmed "fixed" claims.
- Social/OpenGraph banner — `site/assets/ghostlock-tracker.svg` rasterised
  to `site/static/ghostlock-tracker.png` by `make banner` (resvg), wired
  into the `cover:` front-matter.

## Steps

### 1. Hugo project — scaffolded from januscape

The Hugo skeleton, theme integration, PaperMod overrides, CSS, and i18n
were copied wholesale from the januscape tracker (the newest, most-refined
single-table sibling) on 2026-07-09.  Only `baseURL`, `title`, the Go
module path, the banner, the systemd unit names, the timer slot, and the
rsync destination were retargeted, and the content + CLAUDE.md +
auto-update prompt were rewritten for this bug.  The januscape KVM framing
(`/dev/kvm`, nested virt, arch split, ITScape companion) and its
`~/src/linux/kvm` subsystem clone were dropped — GhostLock is a generic
locking fix that lands directly in `~/src/linux/stable`, so `stable` +
`vulns` suffice.

### 2. Publish

- [ ] First `make build` and inspect `site/public/` locally.
- [ ] First `make dist` to push to `haig` — `htdocs/ghostlock/` created
      (let rsync make it).
- [ ] Verify the site renders at `https://kimmo.cloud/ghostlock/`,
      including the RSS feed and OG metadata.

### 3. Automated maintenance

- [ ] `git worktree add -b auto-update ~/src/auto-update/ghostlock main`
- [ ] Install and enable
      `systemd/ghostlock-tracker-update.{service,timer}` as user units
      (see CLAUDE.md for the exact `ln -sr` + `systemctl --user` recipe).
- [ ] Confirm the timer slot `Tue,Fri 05:20` does not collide with the
      siblings (ipv6 `:05`, januscape `:50`, itscape `06,18:05`).

## Decisions

- **Hosting:** own nginx on `haig`, *not* GitHub Pages.  Same as the
  sibling trackers.
- **URL:** `https://kimmo.cloud/ghostlock/` (nickname slug; see "Naming").
- **Theme integration:** Hugo Modules; theme PaperMod.
- **Canonical source:** `site/content/_index.md`.
- **Automated maintenance:** a user-level systemd timer
  (`systemd/ghostlock-tracker-update.timer`, Tuesday and Friday
  mornings) runs
  `scripts/auto-update`, which merges `origin/main` into a dedicated
  long-lived `auto-update` branch in a separate worktree and hands off to
  headless Claude with `scripts/auto-update-prompt.txt`.  The agent only
  commits onto `auto-update` — it does not push or open PRs.  Merges of
  `auto-update` into `main` are done manually.  The wrapper prints a
  `GhostLock tracker auto-update starting …` banner as its first output
  line so aggregated syslog can tell the trackers apart.

## Risks & gotchas

- **Anchor drift.** Hugo/Goldmark's heading slugger differs from GitHub's
  for headings with em dashes, parens, plus signs, or slashes.  Pin brittle
  headings with `### Heading {#stable-id}` if external bookmarks need to
  survive renames.
- **Subpath baseURL.** `baseURL = "https://kimmo.cloud/ghostlock/"` must
  include the trailing slash and the path component.
- **`.cleanDestinationDir`** wipes `site/public/` before each build.
- **rsync `--delete`** removes server-side files not present in the build
  output.  Don't store unrelated content under `htdocs/ghostlock/`.
- **Client-side table tweaks.** `layouts/partials/extend_footer.html`
  rewrites tables in the browser: in the `.distros` table it replaces
  the Distribution column with full-width group heading rows (one per
  distribution; Markdown can't express colspan, so the source keeps a
  plain column); other tables get consecutive duplicate first-column
  cells collapsed via `rowSpan`; status-emoji cells are tagged so
  `custom.css` can hang-indent them.  With JavaScript disabled the
  tweaks are skipped — the table still renders correctly, with the
  Distribution column visible and repeated.

## Social banner

The OpenGraph / social-preview image is generated from an SVG source:
`site/assets/ghostlock-tracker.svg` (1200×630) is rasterised to
`site/static/ghostlock-tracker.png` by `make banner`, which runs `resvg`.
The PNG is committed so ordinary `make build` / `make dist` runs need no
rasteriser.  `resvg` needs the Roboto fonts (`fonts-roboto-unhinted` on
Debian) — without them it silently drops the SVG's sans-serif text.

## Known gaps

- [ ] **Favicons.** PaperMod's `head.html` emits five icon `<link>` tags
      that 404 unless the icon files exist in `site/static/`.  The sibling
      trackers have the same open item.

## Out of scope

- Search and multi-page navigation.  (RSS and the dark/light toggle are
  already wired up.)
