The Rust `build` stage previously ran the entire compiler under QEMU x86_64
emulation on the arm64 builder. That was ~15x slower (one crate took >20 min)
and the emulated gcc/linker intermittently SIGSEGV'd or hung mid-link
(observed again building v0.5.2).
Pin the stage to $BUILDPLATFORM (native arm64 on Apple Silicon, amd64 in CI)
and cross-compile to x86_64-unknown-linux-musl with cargo-zigbuild — zig cc
supplies the musl sysroot + linker. rustc runs natively; no emulation. Build
drops from ~30 min to a few minutes and is deterministic. Output is the same
fully static musl binary (verified: x86_64, not a dynamic executable, 0
OpenSSL strings).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Three things, headlined by the long-blocked graphical PXE menu.
## 1. Graphical PXE boot background — the iVentoy feature, finally
iVentoy paints a PNG background on the PXE screen using stock iPXE
built with CONSOLE_FRAMEBUFFER + IMAGE_PNG + CONSOLE_CMD; the public
iPXE binaries omit those, so `console --picture` is a no-op on them.
We now build our own iPXE from upstream with that thin config delta
(deploy/ipxe/local/{general,console}.h).
The 8-release blocker was cc1 segfaulting when an amd64 gcc ran under
QEMU emulation on the arm64 build host. Fix: a new `ipxe-build`
Dockerfile stage pinned to $BUILDPLATFORM (native arch — no emulation)
that cross-compiles x86_64 iPXE with CROSS_COMPILE=x86_64-linux-gnu-.
The compiler runs native and emits x86_64. Validated end-to-end:
png.o + fbcon.o + pixbuf.o all compile and link (confirmed via the
linked-ELF symbol table, not just strings), ~112s, no segfault. Host
tools needed libc6-dev (dropped by --no-install-recommends; without
it the native host compile falls through to iPXE's freestanding
headers and dies on bits/stdint.h — fixed).
Server side:
- pxe_logo.rs is now a full-screen background compositor: a dark field
(matching the WebUI theme) with the operator's uploaded logo across
the top, or — with no upload — a default OpenPXE rainbow disc drawn
with pure pixel math (no font/SVG deps). Always 1024x768 (iPXE
doesn't scale; this is the universal mode). WebP/JPEG/GIF/PNG in,
PNG out (iPXE only eats PNG).
- /branding/pxe-logo always returns a PNG now (default when no logo,
default when SVG) so the menu always has a background.
- render_menu uses `console --picture … --top 290 || console`: paints
the background and reserves the logo band on PNG-capable binaries
(x86_64 UEFI), cleanly falls back to text on the others. The ASCII
wordmark is GONE.
Only x86_64 UEFI is built from source (host-arch-agnostic cross build);
BIOS/i386/arm64 keep upstream-fetched no-PNG binaries + text fallback.
Modern clients are overwhelmingly x86_64 UEFI.
## 2. NFS AUTH_SYS credential — fixes NFS3ERR_ACCES
v0.4.68's privileged-port fix got past MNT3ERR_ACCES (mount); operators
then hit NFS3ERR_ACCES on READDIR because nfs3_client defaults to
AUTH_NONE and virtually every server exports sec=sys. We now present an
AUTH_UNIX credential (uid 0 / gid 0): no_root_squash servers treat us
as root, root_squash servers map us to anon which reads any
world-readable ISO share. Kept fixed (no UI knob) to stay dead-simple.
Hint updated: a remaining NFS3ERR_ACCES is now a server-side
permission/squash issue, not IP/auth-flavor.
## 3. FleetDM-style full-width logo (top-left)
When a custom logo is uploaded the sidebar header drops the bundled
mark + "OpenPXE" wordmark and lets the logo span the header
(left-aligned, capped 200x50, contain). Rendered server-side via a
brand-class in index_html (has_custom_logo) so there's no flash of the
default. The bundled-default case is unchanged.
Tests: 164 passing. clippy -D warnings clean. iPXE build stage
validated in isolation before the full image build.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
v0.4.65 added the SmbShareManager but the Dockerfile only installed
the `samba` package — in Debian 12 that ships the SERVER (smbd) only,
not the `smbclient` CLI the new manager shells out to. Every "Add
share" attempt surfaced:
could not exec smbclient: No such file or directory (os error 2)
Fix is two lines: add `smbclient` to the runtime apt install, drop
the leftover `nfs-common` (no kernel-mount NFS anymore so the helpers
aren't needed).
While in the area, harden the manager so future stripped-down runtime
images get a useful error instead of a bare exec failure:
- `list_isos` and `stream_iso` both detect `ErrorKind::NotFound` on
spawn and emit "smbclient binary not found on $PATH".
- `hint_for` translates the missing-binary pattern into an actionable
hint: "pull OpenPXE v0.4.66+ or add the Debian `smbclient` package
to your runtime stage." So even on a custom build the UI still
surfaces a clear remediation.
Tests: 150 passing (+1 for the new hint). clippy clean.
The image is still ~98 MB — `smbclient` adds <1 MB on top of the
already-installed samba server.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
v0.4.61 source landed in main with the cache fix and the
PXE-logo compositor, plus an aspirational Dockerfile stage that
rebuilds iPXE from source with IMAGE_PNG enabled. The Dockerfile
stage hits intermittent `cc1: internal compiler error: Segmentation
fault` when cross-emulating x86_64 gcc under QEMU on arm64 build
hosts, which is what the build host I was using does. No v0.4.61
image was ever published as a result.
v0.4.62 walks back the iPXE-from-source change and ships a working
image with the same cache fix and the same compositor code in place.
The iPXE rebuild is queued for a follow-up release, to be built and
validated on the actual x86_64 Unraid hardware where the QEMU
instability doesn't apply.
What's in v0.4.62 vs v0.4.6:
- Asset URL versioning: index.html now appends `?v=<openpxe-version>`
to every asset URL (app.css, app.js, logo.svg). Combined with
`Cache-Control: no-cache, must-revalidate` on the asset handlers,
upgrades land in operators' browsers without a hard refresh. This
is the fix for "I pulled v0.4.6 but the UI still looks like v0.4.5".
- New PXE-logo compositor in iso-store::pxe_logo: decodes any raster
the operator uploads, scales-to-fit into a 600×200 bounding box,
pastes it centered at the top of a 1024×768 PNG canvas, and serves
the result at GET /branding/pxe-logo. Wired into render_menu's
`console --picture` directive; takes effect when the shipped iPXE
binaries grow PNG support.
- ASCII OpenPXE wordmark in render_menu retained for v0.4.62 — works
on the boot.ipxe.org pre-builds we currently ship.
Quality:
- 142 tests passing.
- cargo clippy --workspace --all-targets clean.
- No image dependency change since v0.4.61 (the `image = "0.25"` dep
added in v0.4.61 stays — it backs the compositor).
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Two real issues v0.4.6 left on the table:
Asset caching:
- index.html now interpolates the running OpenPXE version into every
asset URL as `?v=<version>` (app.css, app.js, logo.svg). Combined
with `Cache-Control: no-cache, must-revalidate` on the asset
handlers, browsers and intermediary proxies are forced to fetch
fresh on every upgrade. Without this, last release's bundled JS
kept serving the old UI even after the operator pulled the new
image — invisible to anyone who only checks the version chip in
the footer (which is dynamic).
- The Cache-Control header is also applied to logo.svg and loader.svg
so a logo upload reflects immediately rather than after a hard
refresh.
Real-image PXE menu logo (matches iVentoy now):
- New Dockerfile stage `ipxe-build` clones the iPXE source and
compiles all four binaries (undionly.kpxe, snponly.efi for
x86_64/i386, snponly.efi for arm64 via gcc-aarch64-linux-gnu) with
IMAGE_PNG + CONSOLE_FRAMEBUFFER + CONSOLE_VESAFB enabled. Replaces
the boot.ipxe.org fetch — those binaries are built without PNG
support, which is why v0.4.6's `console --picture` line silently
no-op'd.
- `iso-store::pxe_logo::compose_pxe_logo` decodes any operator upload
(PNG / JPEG / WebP / GIF), downscales-to-fit if larger than
600×200, and pastes it onto a transparent 1024×768 canvas
centered horizontally with a 64-pixel top margin. iPXE paints the
result at 1:1 on the typical VESA framebuffer, giving the
iVentoy-style centered-logo look regardless of the operator's
source dimensions.
- GET /branding/pxe-logo now returns the composed PNG. wimboot still
fetches from ipxe/wimboot's GitHub release (separately signed).
- Dropped the ASCII OpenPXE wordmark from render_menu — once the
real image paints, the banner would duplicate it visually. iPXE
builds without PNG (none of ours after this release, but a third-
party undionly might) simply show the menu without a logo, which
is the right graceful-degradation outcome.
Quality:
- 142 tests passing (was 138 in v0.4.6): +4 pxe_logo unit tests
covering canvas dimensions, centered-top placement, oversize
downscale, and unsupported-bytes error handling; existing
integration tests updated to verify the 1024×768 IHDR header from
the composed PNG instead of round-tripping the raw upload.
- cargo clippy --workspace --all-targets clean.
- Image dependency: `image = "0.25"` with only `png/jpeg/webp/gif`
features enabled. No new transitive C deps.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
VMware UEFI / Casper boot fix:
- Linux cmdline for Debian/Ubuntu/Mint/Pop!_OS/elementary now uses the
canonical Casper `iso-url=` option and `ds=nocloud`, matching the
fix Bootimus shipped in v0.1.67. The previous
`boot=casper netboot=url url=… ip=dhcp ---` form booted fine on
bare-metal UEFI but hung at "cloud-init running" on VMware guests
because subiquity / cloud-init can't reach a metadata datasource
through PXE.
Static binary (matches Bootimus v0.1.70):
- Dockerfile build stage now compiles against
x86_64-unknown-linux-musl. The resulting /openpxe has no glibc
dependency at all; the runtime stage still ships Debian slim for the
samba/wimtools/nfs-common shellouts, but a future scratch/distroless
variant is now a one-line swap. Cuts a class of "GLIBC_2.39 not
found" surprises on older RHEL/Rocky hosts.
Forms auth (Sonarr/Radarr-style):
- New AdminStore in openpxe-core: single admin record persisted to
<work_dir>/auth.json, bcrypt-hashed credentials, rotation requires
current password.
- New SessionStore in openpxe-http-api: in-memory UUID-keyed sessions
with 24h sliding TTL, openpxe_session HttpOnly cookie.
- Endpoints: POST /api/setup (first-run), POST /api/login, POST
/api/logout, GET /api/me, PUT /api/me/credentials (rotates and
revokes every other session).
- Auth middleware gates /api/* once the admin is configured;
passes through entirely until then (tests + fresh installs ride this
path). Allowlists PXE-essential paths (/boot.ipxe, /iso/*, /ipxe/*,
/api/queue/join, /api/queue/poll/*) so iPXE clients still work
without a cookie they can't send.
- WebUI: first-run setup card, login card, logout chip in the sidebar
footer, Account card in Settings for rotating creds. Auth screen is
fully styled (centered narrow card, matches Sonarr layout).
SSO config (FleetDM-shaped, storage-only):
- New SsoStore in openpxe-core: { enabled, idp_name, metadata,
metadata_url } persisted to <work_dir>/sso.json with size caps and
URL-scheme validation.
- Endpoints: GET /api/sso, PUT /api/sso. Validation: enabling SSO
without either metadata or metadata_url returns 400.
- WebUI: SSO card in Settings with a URL-vs-XML mode switch and an
inert "Sign in with X" button on the login screen while runtime
flow is pending. Per the brief: no Entity ID field (defaults to the
advertised public_base_url internally when SAML wiring lands).
Quality:
- 132 tests passing (was 106 in v0.4.4): +5 auth unit tests, +5 SSO
unit tests, +7 auth integration tests, +1 SSO integration test, +1
regression guard pinning the new Casper cmdline.
- cargo clippy --workspace --all-targets clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Commit Cargo.lock, copy it into the Docker build stage, and align the Docker Rust base/MSRV with the toolchain required by the locked dependency graph.
Do not copy rust-toolchain.toml into the Docker build stage so the release image uses the Rust toolchain provided by the base image instead of downloading latest stable inside the container.