-
released this
2026-07-16 15:56:49 -04:00 | 0 commits to main since this releaseCompatibility & ease-of-use pass, inspired by Bootimus. Add images from the internet in one field, and stand up a fresh container with no setup wizard.
docker pull gitea.milesward.dev/mward4/openpxe:0.8.1 docker pull gitea.milesward.dev/mward4/openpxe:latestAdd ISO by URL
The Storage upload card gains an Or add by URL field. Paste any
http(s).isolink and the server streams it straight into storage and auto-detects it — same result as a drag-drop, no download-then-reupload. Progress (and Cancel) show as rows, just like uploads..iso-only, 16 GiB cap, credentials stripped from any displayed URL. Operator-gated and never runs at boot, so offline boot stays intact.Zero-touch admin bootstrap
Set
OPENPXE_ADMIN_USERNAME+OPENPXE_ADMIN_PASSWORD(orOPENPXE_ADMIN_PASSWORD_FILEfor a Docker/K8s secret) and a fresh container comes up with the admin already created — no first-run wizard. Seeds the first run only; once an admin exists it's a no-op, so a lingering env var can't reset a rotated password.On the reference projects
Dnsmasq-PXE is a manual
dnsmasq+ TFTP + NFS setup guide — the exact stack OpenPXE replaces, so nothing to port. Bootimus is the true peer; OpenPXE already matched it on proxyDHCP, web-only UI, distro auto-detect, Windows wimboot + SMB, per-MAC pins, WoL, boot log, REST API, and Secure Boot — and still leads on NFS/SFTP libraries and Queued Deployment. This release closes the two ease-of-use gaps that remained: getting images onto the box, and first-run friction.Under the "fewer options, things just work" principle we deliberately skipped a hardcoded tool catalog (URL rot + menu clutter — the URL field covers it) and generic kernel/initrd guessing for unknown ISOs (keeps the "sanboot beats a broken guess" rule from v0.7.4).
Downloads
-
released this
2026-06-21 23:55:35 -04:00 | 1 commits to main since this releaseDependency prune, a memtest mislabel fix, concurrent multi-file uploads, and an operator API key for driving the HTTP API outside the browser.
docker pull gitea.milesward.dev/mward4/openpxe:0.8.0 docker pull gitea.milesward.dev/mward4/openpxe:latestOperator API key (
x-api-key)/api/*is no longer cookie-only. A persisted key — generated on first run, shown in Settings → Advanced → API with Copy + Regenerate — authenticates API calls when sent as thex-api-keyheader, with the same access as a logged-in operator. Drive uploads, deletes, and config from Postman / scripts / CI without a browser session. Constant-time verified; regenerating invalidates the old key immediately.Concurrent, stackable uploads
Drop several
.isofiles at once — each gets its own progress row and uploads independently (the browser's per-origin connection cap paces them) instead of the second drop clobbering the first. A page-leave warning fires while uploads are in flight, and apagehidekeepalive-abort tells the server to clean up if you navigate away.Abandoned uploads self-heal
Refreshing or closing the tab mid-upload used to strand a
.partialthat blocked the next attempt with "already uploading."begin_uploadnow reclaims a stale.partial(refresh / crash / dropped connection all safe) — the chunked protocol can't resume a dead session anyway.memtest86 (and other signed bootables) no longer flagged Windows
The bulk Windows scan dropped its over-broad
"microsoft"UTF-16 marker, which matched the Microsoft-signed Secure-Boot EFI loader that memtest86 — and signed BSDs / firmware tools — ship (the string lives in the loader's FAT long-filename entries). The remaining markers are all Windows-exclusive filenames.INTROSPECT_REV3 → 4, so existing local ISOs re-probe on first startup and shed the bogus label automatically.Leaner dependency tree
14 unused dependency declarations removed across 7 crates (ponytail audit);
quick-xmlandx509-parserleave the build entirely. No behavior change — just less to compile and audit.Downloads
-
v0.7.5 — Joliet fallback + tolerant El Torito: appliance ISOs classify; Unattended pagination Stable
released this
2026-06-12 17:05:26 -04:00 | 3 commits to main since this releaseCloses the two false-negative holes found while triaging the v0.7.4 dashboard, and pages the Unattended files list.
docker pull gitea.milesward.dev/mward4/openpxe:0.7.5 docker pull gitea.milesward.dev/mward4/openpxe:latestAbout those "Non-bootable images"
The dashboard card is working as designed for genuine data ISOs — VMware VCSA and similar appliance bundles ship no El Torito boot catalog at all (you mount them on a workstation; they were never PXE-bootable, and before v0.7.4 they silently got sanboot entries that failed on real hardware). But the triage surfaced two ways a genuinely bootable image could be mislabeled, both fixed here:
- Joliet namespace fallback. The ISO9660 walker now falls back to the Joliet supplementary namespace when the primary tree misses. Windows-oriented mastering tools — common for vendor/appliance ISOs — write a minimal or mangled primary tree and keep the real filenames only in Joliet; those images probed as "no installer files" and their in-ISO kernel fetches 404'd. Applies to introspection (local + NFS/SFTP) and
/iso/{id}/{*path}serving. - Gap-tolerant El Torito walk. The boot-catalog scan no longer aborts at the first non-
CD001sector or stops at a Set Terminator — sloppy mastering leaves zeroed filler sectors inside the descriptor area that used to hide a real boot record. All 16 descriptor sectors are examined (the 25-byte exact signature can't false-positive). The PVD/volume-label scan got the same tolerance. - Everything re-checks itself:
INTROSPECT_REV→ 3, so local ISOs re-probe on first startup and the remote caches self-invalidate — anything the stricter v0.7.4 logic flagged gets a second look with the new walker automatically. If an image still shows in Non-bootable after this release, it really has no boot catalog and no installer tree.
Storage
Unattended files get the exact pager Available images got in v0.7.4: 5 per page,
Showing X–Y of Nwith Prev/Next, composed with the filter, page resets on every keystroke.
Validation: clippy (pedantic) clean, fmt clean, 319 workspace tests green (+3: Joliet fallback lookup, Joliet-only classification, filler-sector boot record), web UI syntax-checked.
Downloads
- Joliet namespace fallback. The ISO9660 walker now falls back to the Joliet supplementary namespace when the primary tree misses. Windows-oriented mastering tools — common for vendor/appliance ISOs — write a minimal or mangled primary tree and keep the real filenames only in Joliet; those images probed as "no installer files" and their in-ISO kernel fetches 404'd. Applies to introspection (local + NFS/SFTP) and
-
released this
2026-06-12 15:26:10 -04:00 | 4 commits to main since this releaseProbe-based introspection — remote-share ISOs finally classify, the gparted false-Windows bug is fixed, and Storage pages its library.
docker pull gitea.milesward.dev/mward4/openpxe:0.7.4 docker pull gitea.milesward.dev/mward4/openpxe:latestIntrospection, rebuilt
Detection no longer greps raw sectors for filename strings — it walks the ISO9660 directory tree and checks that the well-known boot files actually exist. The same probes run over NFS (READ3 at offset) and SFTP (seekable handles), so share-hosted ISOs classify like local ones instead of registering as
Unknown:- Remote ISOs: each scan registers entries instantly with a filename-based family, then a background pass probes every ISO over the share and upgrades it in place — volume label, real family, verified kernel/initrd, El Torito. Results persist in a per-share cache keyed on
path@size, so container restarts re-probe only new or replaced files. Your 40-ISO NFS wall of "Unknown" becomes AlmaLinux / CentOS / RHEL / RHCOS / OpenShift rows on the first scan after upgrade. - The gparted bug: any Linux ISO shipping GRUB/syslinux chainload modules contains the literal string
bootmgr, which the old byte-scan read as Windows. Linux probes now run first; gparted-live (and Clonezilla, Kali live, …) classify as Debian-family. Local ISOs re-probe automatically on first startup — no re-upload. - No more guessed boot paths: kernel entries are emitted only when kernel+initrd verifiably exist in the image. Shapes whose boot protocol needs arguments we don't render yet (Debian-live, d-i netinst, CoreOS/RHCOS/agent ISOs — the latter need their embedded ignition) classify for the UI but keep their working sanboot entries.
- Remote in-ISO serving:
/iso/{id}/{*path}now locates and streams files from inside NFS/SFTP-hosted ISOs, so a verified kernel entry on a remote RHEL DVD boots end-to-end. - SMB:
smbclientcan't seek, so SMB ISOs get the filename-token family and an honest "awaiting introspection" label, keeping their optimistic sanboot entry.
Storage
The Available images table is paged 5 per page with a quiet footer pager (
Showing 1–5 of 50· Prev/Next). The filter from v0.7.2 composes with it — search narrows, then pages, and the page resets on every keystroke.Hosts / Queue
The deployment-profile picker now reads “Unattended file (in Storage → Advanced)” so it's obvious where those files are managed.
Validation: clippy (pedantic) clean, fmt clean, 316 workspace tests green (+17 new: ISO9660 walker, probe shapes including the gparted regression and CoreOS, filename-token table, cache round-trips), web UI syntax-checked.
Downloads
- Remote ISOs: each scan registers entries instantly with a filename-based family, then a background pass probes every ISO over the share and upgrades it in place — volume label, real family, verified kernel/initrd, El Torito. Results persist in a per-share cache keyed on
-
released this
2026-06-09 22:44:31 -04:00 | 5 commits to main since this releaseDesign-language cleanup of v0.7.2 — no behaviour, protocol, or boot-path changes.
docker pull gitea.milesward.dev/mward4/openpxe:0.7.3 docker pull gitea.milesward.dev/mward4/openpxe:latestImage digest
sha256:72552bc3f3bf9f3a12f6cfe9951c289d2b699fbe8f1f23af4c4e2ba1459476a5(both tags), commit934cfba.Hosts
The Pin MAC to boot target form collapses to a single aligned 4-up row — MAC · Label · Architecture · Boot binary — with Target dropping full-width onto its own line beneath them. The per-field hints that broke the row's alignment moved into the explanatory note, so every control shares one baseline.
Storage
The image and unattended filter inputs are now wrapped in a
label.field, so they inherit the standard text-field chrome — border, radius, height, focus ring — instead of the raw browser<input type=search>look. They span the full card width for continuity with the rest of the page.Network
The Link row (operstate · speed · duplex · port MAC) moved below Public base URL. Its joined value runs long, so placing it last lets it wrap at the bottom of the list without shoving the other rows around.
Validation: clippy clean, fmt clean, 299 workspace tests green, web UI syntax-checked.
Downloads
-
released this
2026-06-09 21:47:48 -04:00 | 6 commits to main since this releaseOperator-experience release.
Storage
- Filter inputs for the Available images table (matches filename, family, category, source) and the Unattended files list. Forty-image libraries are now a keystroke away instead of a scroll.
Hosts — one form, one mental model
- The separate Boot rules card is gone. The Pin form gained Architecture (optional) next to Label (and kept the v0.7.1 boot-binary pin): a full MAC = per-host pin exactly as before; a MAC prefix and/or an architecture = first-match-wins group rule. Saved rules show in a compact Group rules card with remove buttons.
- The boot-decision webhook keeps working via the API (
/api/boot-rules) but no longer has a UI knob.
Network
- New Link row under NIC name:
link up · 1000 Mb/s · full duplex · port aa:bb:cc:…— read from sysfs at startup, so you can confirm which physical port answers PXE in multi-NIC or trunked environments. Shows—where unavailable (virtual NICs).
About
- Hero copy rewritten — positioning lead, three-pillar feature grid, privacy + no-test-cert principles stated crisply.
Fit & finish
- The doubled 30px gap above Queue Launch for all waiting and Network Save collapsed to the standard 16px every other card action uses.
Validation
clippy clean · fmt clean · 299 workspace tests green · WebUI syntax-checked.
Image
docker pull gitea.milesward.dev/mward4/openpxe:0.7.2:0.7.2/:latest— digestsha256:558849673250115eacd2af6f98789dd214dd49372328dd9a388b5985c17daf84(linux/amd64).Downloads
-
v0.7.1 — some devices just hate iPXE Pre-Release
released this
2026-06-09 21:16:22 -04:00 | 7 commits to main since this releaseOperational refinement of the v0.7.0 escalation ladder, answering "can a machine try all three boot binaries in one go?" The protocol can't carry three boot files in one cycle — but we can make the learning walk a once-per-machine-ever event, and let operators skip it entirely.
What changed
- Learned driver modes persist (
driver_modes.jsonin the work dir). A MAC that reaches the Shim rung, or proves Builtin works via the iPXE handoff, is remembered across restarts and idle periods — no more re-walking the ladder every 30 minutes. The file only records exceptions; a healthy fleet never writes it. Corrupt/missing file starts clean. - Rules can pin the boot binary. The Boot rules table (Hosts tab) gains a Boot binary column: auto (learn) / Firmware NIC / iPXE drivers / Secure Boot (shim). A pinned rule is consulted before the ladder — a known Secure-Boot rack boots the signed chain on its very first cycle, zero failures. Mode pins and target rules coexist on the same client.
- Same-boot self-healing in the shim stage. The GRUB config now tries to chainload full iPXE before showing the signed menu: with Secure Boot off the chainload succeeds and the client gets the complete iPXE feature set back in that same boot (and the handoff pins the working mode); with Secure Boot on, shim refuses it inline — no reboot — and the signed menu appears.
Why not literally one cycle for everyone
DHCP/PXE hands the firmware exactly one boot file per cycle, the Secure-Boot refusal happens after our part is done (no error report comes back), and the broken-NIC case specifically requires the firmware itself to load builtin-driver iPXE — GRUB's network rides the same broken firmware stack, so routing everything through shim-first would strand exactly the machines the Builtin rung exists for. Worst case remains 2 failed cycles (~15–30 s) — now paid once per machine per lifetime, or never with a rule pin.
Validation
clippy clean · fmt clean · 299 workspace tests green (+9 new: persistence round-trip across restart, Shim/Builtin pins surviving the TTL, corrupt-file recovery, default-mode-never-persisted, rule-pin matching with unknown-mode tolerance and pin/target coexistence, GRUB chainload-before-menu ordering, API round-trip of the pin field).
Image
docker pull gitea.milesward.dev/mward4/openpxe:0.7.1:0.7.1/:latest— digestsha256:4bc3da30528177c6895e2ec9e8ccdced7e06145a4c4765d95c61821f2888a163(linux/amd64).Downloads
- Learned driver modes persist (
-
released this
2026-06-09 20:17:27 -04:00 | 8 commits to main since this releaseThree features. All zero-toggle, all inside the core principles (single static binary, container-first, no test certs, no client trust-store changes).
Secure Boot — automatic signed shim+GRUB chain
The v0.6.1 escalation ladder gains a third rung: Firmware → Builtin → Shim. Secure-Boot firmware downloads our unsigned iPXE but refuses to execute it — after two unconfirmed attempts that MAC is automatically offered Fedora's Microsoft-signed
shim, which loads the signed GRUB, which fetches a server-rendered menu. Fully signed chain, Secure Boot stays ON, no firmware settings to change on the client.- Signed binaries (
shimx64.efi/grubx64.efi+ arm64 equivalents) are extracted from official Fedora 43 packages at image build and shipped byte-for-byte unmodified. - The GRUB menu is rendered live from your boot entries (Linux installers — a signed GRUB only boots signed kernels; sanboot/wimboot entries are iPXE-only and omitted there).
- Watch for
driver=Shimin the DHCP log when an SB client walks the ladder.
Boot rules + decision webhook (Matrix Boot, but open)
Ordered, first-match-wins rules — MAC prefix (OUI) and/or client architecture → boot target — generalizing per-MAC pins to whole racks or device families. Plus an optional boot-decision webhook: unmatched boots
GET <url>?mac=…&arch=…; a200 {"target":"<id>"}chains to that target, anything else falls through to the menu (2-second budget, fail-open — a dead endpoint can never block PXE). Editor on the Hosts tab. Decision order: exact pin → rules → webhook → menu.Hardened answer-file delivery
Answer files embed credentials; serving them openly is what got WDS hands-free deployment killed (CVE-2026-0386). Every generated unattended URL now carries a short-lived boot-scoped token, and
/unattended/*requires it (or a logged-in operator session) once an admin exists. Automatic — no setting, nothing to migrate; setup-mode installs stay open.Validation
clippy clean · fmt clean · 290 workspace tests green (+18 new) · image build verified the live Fedora fetch (shim-x64 15.8-3 + grub2 2.12-40.fc43, x86_64 and arm64).
Image
docker pull gitea.milesward.dev/mward4/openpxe:0.7.0:0.7.0/:latest— digestsha256:db8cfde9b27a0602981066bcfea52cd5399e90fa1945bf2449b3b8cfde51857f(linux/amd64).Real-world validation notes: the Shim rung needs a Secure-Boot-enabled machine to confirm end-to-end (it adds two quick failed PXE attempts before the signed chain kicks in — expected); rules/webhook and tokens are fully covered by integration tests.
Downloads
- Signed binaries (
-
v0.6.3 — russh security bump + axum 0.8 Pre-Release
released this
2026-06-09 19:47:13 -04:00 | 9 commits to main since this releaseSecurity-driven dependency release. Recommended upgrade for anyone using SFTP remote libraries.
Security
- russh 0.55 → 0.61.2 — closes a batch of advisories reachable from our SFTP client path: unbounded / allocation-first packet parsing (CVE-2026-48110, CVE-2026-46702, CVE-2026-46673 — HIGH) plus CVE-2026-48107 in client auth. Previously a malicious or compromised SFTP server an operator pointed OpenPXE at could OOM the server. Bonus: ~21% SSH throughput improvement upstream (mlock removed on non-secret buffers) — directly in the remote-share ISO streaming path.
- bergshamra 0.4 → 0.5.1 (SAML) — the long-standing russh version pin existed only because bergshamra-crypto used release-candidate RustCrypto crates; 0.5 moved to the stable generation, so the pin is gone. 0.5 is also secure-by-default for XML-DSig (settings we already enforced explicitly) and fixes an XML-Enc DerivedKey fallthrough.
Framework
- axum 0.7 → 0.8.9 — route captures migrated to
{param}syntax (router +/api/docs). Gains the HEAD responsecontent-lengthfix (iPXE/sanboot clients probe with HEAD before issuing Range requests) and returns us to the maintained release line.
Validation
clippy clean · fmt clean · all 272 workspace tests green · static musl image build (the authoritative gate for the russh bump) passed.
Image
docker pull gitea.milesward.dev/mward4/openpxe:0.6.3:0.6.3/:latest— digestsha256:f35e6b76aa6ae31bcc24505a91eef55f4dc4df86567a820dd3d40d2e49902eee(linux/amd64).Downloads
-
v0.6.2 - Fable validation + security exam Pre-Release
released this
2026-06-09 16:45:11 -04:00 | 10 commits to main since this releaseFull-codebase review and polish pass — every loose end finished or removed, the serving hot paths optimized, and the dependency tree refreshed for reliability. No behavior changes for working clients; legacy clients get clearer protocol errors.
Performance (per-boot hot paths)
- Zero-copy iPXE serving — TFTP and HTTP now serve the embedded boot binaries straight from the binary's rodata instead of heap-copying ~1 MiB per request.
- PXE menu background cached — the composited
console --picturePNG was re-encoded (~50–200 ms) for every booting client; it's now composed once per logo change. - bcrypt off the async runtime — boot-password checks and login/setup/rotation run on the blocking pool, so CPU-heavy auth can't stall ISO streaming to imaging machines.
- Per-range-request overhead halved on
/iso/…(single registry lock + clone instead of two), O(1) DHCP driver-escalation bookkeeping, one-allocation MAC formatting, and a fixed introspection buffer that no longer forces a guaranteed 32 MiB realloc.
Correctness & legacy-client polish
- TFTP speaks up instead of going silent: write requests get a proper Illegal TFTP operation error and non-octet (netascii) transfers a clear only octet mode is supported — legacy clients fail fast with a reason instead of retrying into a timeout.
- HTTP Range handling is now RFC 7233-correct: malformed
Range:headers are ignored (200 + full body) instead of answered with a bogus 206.
Cleanup
- Removed the never-enforced
mac_allowlist/subnet_allowlistconfig fields (line-of-sight serving is the design; nothing should advertise access control that doesn't exist). - Removed dead client-registry API and never-emitted event variants; rustfmt sweep over six drifted files.
Dependencies
- dhcproto 0.12 → 0.15 — drops the deprecated/unmaintained
trust-dns-protofrom the tree (replaced byhickory-proto) and picks up three releases of DHCP option coverage. - socket2 0.6 (dedupes the tree), bcrypt 0.19, tower-http 0.6.11 (sheds
iri-string), tokio 1.52.3 / hyper 1.10 lockfile refresh; deadnomworkspace entry deleted.
Validation
cargo fmtclean · clippy (pedantic profile) clean · all 272 workspace tests green on the new dependency stack · WebUI JS syntax-checked.Image
docker pull gitea.milesward.dev/mward4/openpxe:0.6.2:0.6.2/:latest— digestsha256:53f33a65129d1b49106f5b376c0517bcbee117fe743e22e8497a7c2f55563b99(linux/amd64).Downloads