The About-tab "check for updates" returned "repository URL not
configured at build time" because the http-api crate didn't inherit the
workspace `repository` field, leaving CARGO_PKG_REPOSITORY empty. Add
`repository.workspace = true` so the Gitea releases API URL derives
correctly, and strengthen the unit test to assert the URL is present.
Caught by the v0.5.0 container smoke test before publish.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Closes the v0.4.x chapter — NFS works end to end. Five additions:
## Wake-on-LAN (Hosts → Bound hosts)
- New core::wol module: parse any MAC form, build the 102-byte magic
packet, broadcast it. No special capability needed (ephemeral source
port; SO_BROADCAST). Sends to the limited broadcast (255.255.255.255)
AND the server's own subnet broadcast (computed from advertised IP +
detected mask) so it reaches the right VLAN.
- POST /api/hosts/:mac/wol — only fires for *bound* MACs (404 otherwise)
so it's not an open packet sprayer.
- Bound-hosts table grows a "Wake" button with inline Waking…/Sent ✓
state.
## Webhook notifications (Advanced tab)
- core::notify: NotifyConfig + NotifyStore (notify.json), one provider
at a time — Slack / Discord / Teams (incoming-webhook JSON) or SMTP.
SMTP password is persisted but redacted on GET behind a __keep__
sentinel the UI round-trips so the secret never leaves the box.
- http-api::notify: delivery — reqwest POST for chat (provider-shaped
bodies), lettre for SMTP (rustls, STARTTLS/implicit TLS, no plaintext).
10s timeout; every send is best-effort.
- GET/PUT /api/notify, POST /api/notify/test.
- Fired fire-and-forget on the canonical "machine is imaging" boot event
and on WoL — never blocks the boot path.
## UI: Advanced tab
- New nav item. Holds the webhook config card and the API reference
block (relocated from the bottom of Settings).
## UI: login/setup logo (FleetDM treatment)
- /api/me now returns has_custom_logo + logo_rev (public bootstrap).
The login, setup, and connection-error cards render the uploaded logo
full-width with the "OpenPXE" wordmark dropped — matching the sidebar.
## About: update check + licenses
- "Check for updates" button → GET /api/updates/check queries the Gitea
releases API (derived from CARGO_PKG_REPOSITORY) and compares to the
running version. Strictly on-demand — no background polling, keeps the
air-gapped promise.
- License card documents the MIT OR Apache-2.0 dual license with links,
plus a note on bundled components (iPXE GPLv2/UBDL, samba, wimtools).
Deps: lettre (SMTP, rustls) + reqwest gains the json feature. Both
rustls so the static musl binary stays OpenSSL-free.
Tests: 179 passing (+notify round-trip/redaction, webhook validation,
WoL-unbound-404, WoL packet loopback, version-compare). clippy clean.
Co-Authored-By: Claude Opus 4.8 (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]>
Add browser-safe chunked ISO uploads with progress, partial-file visibility, offset validation, and abort cleanup while keeping the legacy multipart endpoint for API clients.
Record host-log validation coverage, keep the queue/status UI copy clean, move release docs to 0.4.1, and tighten the dark theme to a near-black Netbox-style palette.