From 57e9abcd7238fcc800a9be01444d8cb9101dc657 Mon Sep 17 00:00:00 2001 From: castrojo Date: Thu, 22 Oct 2026 02:12:23 +0000 Subject: [PATCH] fix(docs): redesign server page header and streamline hero layout - Remove redundant title text above wordmark on server page - Streamline dinosaur header to single Amargasaurus and wrap prose around it - Strip scale comparison silhouette in README preparation - Add accessible screen-reader heading in ImageIdentity hero mode Assisted-by: github-copilot/gemini-3.8-flash via GitHub Copilot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/server.mdx | 7 +- docs/skills/variant-docs-pages.md | 8 +- scripts/fetch-readmes.mjs | 9 +- scripts/fetch-readmes.test.js | 14 +++ src/components/ImageIdentity.module.css | 110 ++++++++++++++++++++++++ src/components/ImageIdentity.tsx | 76 +++++++++++++++- src/components/image-identities.json | 7 -- src/content/server-readme.md | 53 ++++++++---- 8 files changed, 251 insertions(+), 33 deletions(-) diff --git a/docs/server.mdx b/docs/server.mdx index d228a0a40..ff8dd4b74 100644 --- a/docs/server.mdx +++ b/docs/server.mdx @@ -1,5 +1,6 @@ --- title: Bluefin Server +hide_title: true description: An image-based Linux server OS built like a container, composed from freedesktop-sdk. slug: /server custom_edit_url: https://github.com/projectbluefin/server/edit/main/README.md @@ -13,9 +14,9 @@ import { } from "@site/src/components/ImageProductSection"; import { PerImageUsage } from "@site/src/components/analytics/CountmeAnalyticsCharts"; - - - + + + ## Downloads & Image Versions diff --git a/docs/skills/variant-docs-pages.md b/docs/skills/variant-docs-pages.md index 430b4955c..c3cb03abd 100644 --- a/docs/skills/variant-docs-pages.md +++ b/docs/skills/variant-docs-pages.md @@ -63,11 +63,8 @@ motivation prose — see _Never write in a maintainer's voice_ in `AGENTS.md`. no second route and needs no sidebar or global Markdown configuration changes. Its `mdx.format: md` frontmatter preserves ordinary Markdown and raw HTML; the wrapper retains native headings, TOC, search, and the upstream edit link. -Keep `title` in frontmatter for navigation and the single top-level `h1`. -Do not set `hide_title: true`; `scripts/fetch-readmes.mjs` strips any leading -`# Title` heading from the imported README when `product` is specified so -Docusaurus emits exactly one page title. - +Keep `title` in frontmatter for navigation and metadata. When an image logo already contains the product wordmark, set `hide_title: true` so the visual page header avoids duplicate title text while `` preserves an accessible screen-reader `

`. +`scripts/fetch-readmes.mjs` strips leading `# Title` headings and scale comparison silhouettes from imported product READMEs so the landing page retains a clean single-character identity and flow. `scripts/fetch-readmes.mjs` rewrites parsed links/images, not code examples. It reads its source list from `src/components/image-identities.json`. Because `format: md` passes raw HTML through to the page and the seeds are @@ -166,6 +163,7 @@ card entry from `IMAGES` in `ImageCards.tsx`. explicit, and link build-time recipes to upstream repositories. When headings are simplified, place explicit HTML anchors (``) so legacy links do not 404. + ## Common Rationalizations | Rationalization | Reality | diff --git a/scripts/fetch-readmes.mjs b/scripts/fetch-readmes.mjs index 227dc57cf..14091e9f8 100644 --- a/scripts/fetch-readmes.mjs +++ b/scripts/fetch-readmes.mjs @@ -243,7 +243,9 @@ export async function prepareReadme( const resolve = (value, image) => { if (/^(?:[a-z][a-z\d+.-]*:|\/\/|#)/i.test(value)) return value; if (product === "server" && !image) { - const match = value.match(/^(?:docs\/skills\/)?([^/#?]+?\.md)(?:#(.+))?$/); + const match = value.match( + /^(?:docs\/skills\/)?([^/#?]+?\.md)(?:#(.+))?$/, + ); if (match && serverIndexedDocs.has(match[1])) { const slug = match[1].slice(0, -3); return match[2] ? `/server/${slug}/#${match[2]}` : `/server/${slug}/`; @@ -266,6 +268,11 @@ export async function prepareReadme( identity.logoLight, identity.logoDark, ...identity.images.map((image) => image.src), + ...(product === "server" + ? [ + "https://upload.wikimedia.org/wikipedia/commons/d/d8/Dicraeosauridae_Scale.svg", + ] + : []), ].filter(Boolean), ); const matchesArtwork = (url) => artwork.has(resolve(url, true)); diff --git a/scripts/fetch-readmes.test.js b/scripts/fetch-readmes.test.js index 6378b2710..4d3685371 100644 --- a/scripts/fetch-readmes.test.js +++ b/scripts/fetch-readmes.test.js @@ -63,6 +63,20 @@ test("server indexed docs resolve to local documentation routes", async () => { /\[Architecture Roadmap\]\(https:\/\/github.com\/projectbluefin\/server\/blob\/main\/docs\/skills\/architecture-roadmap.md\)/, ); }); +test("server product README strips scale comparison silhouette image", async () => { + const serverSource = { + product: "server", + repo: "projectbluefin/server", + ref: "main", + readmePath: "README.md", + }; + const input = + "> An image-based Linux server OS built like a container\n\n" + + "![armargasaurus](https://upload.wikimedia.org/wikipedia/commons/d/d8/Dicraeosauridae_Scale.svg)\n\n" + + "## Release status\n"; + const result = await prepareReadme(input, serverSource); + assert.doesNotMatch(result, /Dicraeosauridae_Scale\.svg/); +}); test("a failed refresh preserves the offline seed rather than publishing an empty README", async () => { const directory = await mkdtemp(join(tmpdir(), "readme-import-")); diff --git a/src/components/ImageIdentity.module.css b/src/components/ImageIdentity.module.css index 259391d07..f9c3b9933 100644 --- a/src/components/ImageIdentity.module.css +++ b/src/components/ImageIdentity.module.css @@ -2,6 +2,18 @@ margin-bottom: 2rem; } +.srOnly { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + .wordmark { display: inline-block; max-width: 100%; @@ -121,3 +133,101 @@ .sources { margin-bottom: 0; } + +/* ── Hero top of page layout with wrapping text ───────────────────────── */ + +.identityHero { + margin-bottom: 2.5rem; + padding-bottom: 1.5rem; + border-bottom: 1px solid var(--ifm-color-emphasis-200); +} + +.heroTop { + display: flex; + flex-wrap: wrap; + align-items: baseline; + gap: 1rem 1.5rem; + margin-bottom: 1.5rem; +} + +.heroTop .wordmark { + flex-shrink: 0; +} + +.heroTop .species { + margin: 0; + font-size: 0.95rem; + color: var(--ifm-color-emphasis-700); +} + +/* Float character artwork right so sibling prose flows around it */ +.heroContent::after { + content: ""; + display: table; + clear: both; +} + +.heroCharacter { + float: right; + width: 320px; + max-width: 42%; + margin: 0 0 1.5rem 2rem; + text-align: center; +} + +.heroCharacter img { + display: block; + width: 100%; + height: auto; + max-height: 380px; + object-fit: contain; + filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.15)); +} + +[data-theme="dark"] .heroCharacter img { + filter: drop-shadow(0 6px 18px rgba(0, 0, 0, 0.45)); +} +.heroCharacter figcaption { + font-size: 0.8rem; + color: var(--ifm-color-emphasis-600); + margin-top: 0.4rem; +} + +.heroProse { + font-size: 1.05rem; + line-height: 1.65; + color: var(--ifm-font-color-base); +} + +.heroProse blockquote { + margin: 0 0 1rem; + padding: 0.75rem 1rem; + font-size: 1.15rem; + font-weight: 500; + color: var(--ifm-color-primary-darker); + border-left: 4px solid var(--ifm-color-primary); + background: var(--ifm-color-emphasis-100); + border-radius: 0 var(--ifm-global-radius) var(--ifm-global-radius) 0; +} + +[data-theme="dark"] .heroProse blockquote { + color: var(--ifm-color-primary-lightest); + background: var(--ifm-color-emphasis-200); +} + +.heroProse p { + margin-bottom: 1rem; +} + +@media (max-width: 768px) { + .heroTop { + gap: 0.5rem 1rem; + } + + .heroCharacter { + float: none; + width: 100%; + max-width: 260px; + margin: 0 auto 1.5rem; + } +} diff --git a/src/components/ImageIdentity.tsx b/src/components/ImageIdentity.tsx index 1eade0e31..600634af9 100644 --- a/src/components/ImageIdentity.tsx +++ b/src/components/ImageIdentity.tsx @@ -4,10 +4,84 @@ import styles from "./ImageIdentity.module.css"; export type ImageProduct = keyof typeof identities; -export default function ImageIdentity({ product }: { product: ImageProduct }) { +export default function ImageIdentity({ + product, + children, +}: { + product: ImageProduct; + children?: React.ReactNode; +}) { const identity = identities[product]; const readme = `https://github.com/${identity.repo}/blob/${identity.ref}/README.md`; + if (children) { + const primaryImage = identity.images[0]; + return ( +
+

{identity.name}

+
+ {identity.logoLight && ( + + {identity.name} + {identity.name} + + )} +

+ + {identity.species} + +

+
+
+ {primaryImage && ( +
+ + {primaryImage.alt} + +
+ {primaryImage.attribution} +
+
+ )} +
{children}
+
+

+ Upstream README + {" · "} + Artwork source + {" · "} + + Artwork license + + {identity.testsuiteRun && ( + <> + {" · "} + QA verification run + + )} +

+
+ ); + } + return (
{identity.logoLight && ( diff --git a/src/components/image-identities.json b/src/components/image-identities.json index 67e321b75..026c75a27 100644 --- a/src/components/image-identities.json +++ b/src/components/image-identities.json @@ -18,13 +18,6 @@ "source": "/dinosaurs#kubernetes", "attribution": "Jacob Schnurr · Project Bluefin artwork", "diagram": false - }, - { - "src": "https://upload.wikimedia.org/wikipedia/commons/d/d8/Dicraeosauridae_Scale.svg", - "alt": "Dicraeosauridae silhouette size comparison, including Amargasaurus and a human figure", - "source": "https://commons.wikimedia.org/wiki/File:Dicraeosauridae_Scale.svg", - "attribution": "Slate Weasel · Public domain", - "diagram": true } ] }, diff --git a/src/content/server-readme.md b/src/content/server-readme.md index 78705329b..ef4c7b159 100644 --- a/src/content/server-readme.md +++ b/src/content/server-readme.md @@ -3,7 +3,7 @@ mdx: format: md --- -**An image-based Linux server OS built like a container, composed from freedesktop-sdk.** +> An image-based Linux server OS built like a container, composed from freedesktop-sdk. Bluefin Server targets the same workload space as Flatcar Container Linux, Fedora CoreOS, and Talos. Built with [BuildStream 2](https://buildstream.build/), its userspace, kernel, and boot chain compose from [freedesktop-sdk](https://freedesktop-sdk.freedesktop.org/) (FSDK 26.08, systemd v261) components. No other distribution binaries ship in the base image; third-party userspace extensions (such as Flatcar System Extension Bakery images) require `systemd-sysext merge --force` per [systemd-sysext-extensions.md](/server/systemd-sysext-extensions/). @@ -21,14 +21,35 @@ Bluefin Server is currently in **Alpha**: ## Architecture -* **Diskless boot** — Netboot UKI pulls `bluefin-server_.raw` into RAM with `rd.systemd.pull` (`verify=signature`), mounts a dm-verity erofs `/usr`, and runs from tmpfs. Nodes update by rebooting into a newer image, flagging `/run/reboot-required` when an updated image is offered. +* **Diskless boot** — Netboot UKI pulls `bluefin-server_.raw` into RAM with `rd.systemd.pull` (`verify=signature`), mounts a dm-verity erofs `/usr`, and runs from tmpfs. Nodes update by rebooting into a newer image, flagging `/run/reboot-required` when an updated image is offered. This is the recommended configuration for Kubernetes worker nodes. * **Optional disk install with A/B rollback** — A running diskless node installs itself via [ddi-installer.md](/server/ddi-installer/) (`systemd-sysinstall` copies `/usr` into slot A). First disk boot creates slot B and an xfs root. `systemd-sysupdate` fills the inactive slot and reboots nightly (Kubernetes nodes defer reboots to kured); UKI boot counting automatically rolls back failed boots. Updates can be held via `/run/reboot-lock` or `/etc/reboot-lock`. + * This is the recommended mode for Kubernetes Control Planes, and Home Servers. * **Secure Boot by default** — Signed boot chain with `lockdown=integrity`. [Booty](https://github.com/jeefy/booty) serves UEFI HTTP boot assets and per-node Ignition configs. * **Declarative provisioning** — Pass an `ignition.config` credential (or `bluefin-node.ign` next to the UKI); Ignition runs in the initrd on every boot. Configs must be idempotent. * **Opt-in sysexts** — k0s, kubeadm, homelab automation, OpenZFS, and NVIDIA drivers/CDI ship as separate `systemd-sysext` images outside the base `/usr`. > **Remote diagnostics:** OpenSSH is installed for diagnostics but disabled by default via presets. Start manually with `systemctl start sshd` when access is required. See [`docs/skills/factory-integration.md`](https://github.com/projectbluefin/server/blob/main/docs/skills/factory-integration.md). +## Installation Profiles + +Choose your profile during installation. Start small and build up or go full lazy mode ootb. + +### Core and Kubernetes Profiles + +| Boot Menu Option | Best For | Included Software | First Boot Login | +| ------------------------------ | --------------------------------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | +| **Bluefin Server** *(Default)* | Minimal servers and automated deployments | Base OS only (read-only system root, systemd, SSH). No container engines or Kubernetes. | Prompts on the local console (tty1) to set the root password. | +| **Homelab: Control Plane** | Primary node for a multi-machine Kubernetes cluster | Base OS + kubeadm, containerd, Argo Workflows, and a KubeStellar dashboard | Prompts on tty1 for a root password. Prints the cluster join passphrase on the screen. | +| **Homelab: Node** | Worker nodes joining an existing Homelab cluster | Base OS + kubeadm, containerd, and auto-join service. | Prompts for the cluster join passphrase during install; prompts on tty1 for a root password on first boot. | + +### Home Server Profile + +This is lazy mode, which includes `brew` for that lazy Ubuntu Server vibe. This mode is great for the home to run everything, or if you just need a box/VM that needs an OS to run one thing. Having brew baked in makes this perfect for VMs/machines to hold your agents/tmux, etc. + +| Boot Menu Option | Best For | Included Software | First Boot Login | +| ----------------------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| **Bluefin Home Server** | Standalone personal server running containers and tools | Base OS + Podman (default) + Homebrew inside an isolated nspawn system container. Root stays locked. | Prompts on the local console to create a regular user account and password (or accepts an SSH key via credential). | + ## Downloads Each push to `main` publishes an immutable [GitHub Release](https://github.com/projectbluefin/server/releases) (`v`) and an OCI artifact at `ghcr.io/projectbluefin/bluefin-server:`. @@ -60,20 +81,20 @@ just dogfood-install # diskless boot, install to disk, verify rollback (QEMU) Start here to explore architecture and operations guides in [`docs/skills/`](https://github.com/projectbluefin/server/blob/main/docs/skills/index.md): -| Guide | Scope | -| ------------------------------------------------------------------------------ | ------------------------------------------------------------------- | -| [`ddi-installer.md`](/server/ddi-installer/) | Boot, installation, and A/B sysupdate lifecycle | -| [`usb-installer.md`](/server/usb-installer/) | Offline USB installer, interactive prompts, and unattended installs | -| [`booty-integration.md`](/server/booty-integration/) | UEFI HTTP boot, iPXE chainloading, and network boot provisioning | -| [`diskless-troubleshooting.md`](/server/diskless-troubleshooting/) | RAM sizing, initrd failure modes, and Ignition debugging | -| [`systemd-sysext-extensions.md`](/server/systemd-sysext-extensions/) | System extensions, bakery compatibility, and version locking | -| [`kubeadm-sysext.md`](/server/kubeadm-sysext/) | Kubeadm worker and single-node control-plane extensions | -| [`k0s-sysext-ops.md`](/server/k0s-sysext-ops/) | K0s cluster operations, tokens, and troubleshooting | -| [`homelab-profile.md`](/server/homelab-profile/) | Homelab Ignition templates, mDNS cluster join, and add-ons | -| [`nvidia-sysext.md`](/server/nvidia-sysext/) | NVIDIA open kernel modules and Container Toolkit CDI extensions | -| [`systemd-sysupdate-verification.md`](/server/systemd-sysupdate-verification/) | Release signatures, sysupdate policies, and SBOM attestations | -| [`secure-boot-keys.md`](/server/secure-boot-keys/) | Key enrollment, signing workflows, and shim integration | -| [`enabling-extensions.md`](/server/enabling-extensions/) | Enabling extensions (ZFS, NVIDIA, Kubernetes, Podman, Docker) | +| Guide | Scope | +| ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | +| [`ddi-installer.md`](/server/ddi-installer/) | Boot, installation, and A/B sysupdate lifecycle | +| [`usb-installer.md`](/server/usb-installer/) | Offline USB installer, interactive prompts, and unattended installs | +| [`booty-integration.md`](/server/booty-integration/) | UEFI HTTP boot, iPXE chainloading, and network boot provisioning | +| [`diskless-troubleshooting.md`](/server/diskless-troubleshooting/) | RAM sizing, initrd failure modes, and Ignition debugging | +| [`systemd-sysext-extensions.md`](/server/systemd-sysext-extensions/) | System extensions, bakery compatibility, and version locking | +| [`kubeadm-sysext.md`](/server/kubeadm-sysext/) | Kubeadm worker and single-node control-plane extensions | +| [`k0s-sysext-ops.md`](/server/k0s-sysext-ops/) | K0s cluster operations, tokens, and troubleshooting | +| [`homelab-profile.md`](/server/homelab-profile/) | Homelab Ignition templates, mDNS cluster join, and add-ons | +| [`nvidia-sysext.md`](/server/nvidia-sysext/) | NVIDIA open kernel modules and Container Toolkit CDI extensions | +| [`systemd-sysupdate-verification.md`](/server/systemd-sysupdate-verification/) | Release signatures, sysupdate policies, and SBOM attestations | +| [`secure-boot-keys.md`](/server/secure-boot-keys/) | Key enrollment, signing workflows, and shim integration | +| [`gap-analysis-distros.md`](https://github.com/projectbluefin/server/blob/main/docs/skills/gap-analysis-distros.md) | Comparison to Flatcar, Fedora CoreOS, Ubuntu, and Talos | Complete manifest and developer commands are listed in [`AGENTS.md`](https://github.com/projectbluefin/server/blob/main/AGENTS.md) and [`CONTRIBUTING.md`](https://github.com/projectbluefin/server/blob/main/CONTRIBUTING.md).