NextBSD
-
User account management — which directory to build Spike · epic shape
Recommendation: option D, a new design built from plists, Kerberos and NFSv4, with no LDAP and no directory daemon. Local accounts are Apple-style per-record plists with root-only PBKDF2 hashes, read by
nss_ds and pam_ds the way FreeBSD reads master.passwd. Network passwords exist only in the KDC (MIT Kerberos, in base). Records reach joined clients as a copy over NFSv4 krb5p, homes are served the same way, and servers are found with Bonjour instead of GNUstep’s gdomap. The spike compares four options: port Apple DirectoryService, a C/CF Gershwin rewrite, NetInfo, and D. It documents exactly which Open Directory parts Apple kept closed (slapconfig, PasswordService, Server Admin, the AD plugin), how every system from NetInfo to macOS stored its passwords, command examples for each task, and MIG versus Unix sockets. The requirements are users in /Local/Users, zsh, no-prompt sudo only for password-less admins, promote/join, /Network/Users, and no human accounts in /etc/passwd. Planning only.
-
Debian userland: debootstrap and the
apt wrapper Research · plan
Type apt install google-chrome-stable; if there is no Linux root, NextBSD bootstraps one and runs Debian’s own apt inside it. The default is Devuan 6 Excalibur minbase in /compat/linux. It has the same Mesa 25.0.7 and libdrm 2.4.124 as Debian trixie, built against kernel 6.12, an exact match for NextBSD’s drm-kmod 6.12-lts, and no systemd, whose postinst is the one thing that has failed under the Linuxulator. Debian trixie is the zero-vendoring alternative, and Ubuntu LTS is opt-in. Pin the codename, never follow stable. apt runs in a chroot, and installed programs run directly through the alt root. Findings: NVIDIA acceleration for Linux programs is impossible as built (the kext has no Linux ioctl bridge); the Pi 5 GPU is invisible to Linux libdrm (linsysfs is PCI-only); and update-alternatives’ absolute symlinks escape the root unless FreeBSD main’s 459ac3044b is backported. Planning only.
-
Linux ABI mounts — pre-create and always mount, like
rc.d/linux Research · plan
One boot-time LaunchDaemon, org.nextbsd.linux, creates $emul/{proc,sys,dev} and always mounts the five Linux filesystems, even with no Linux userland installed. This is stock FreeBSD’s behaviour, and it removes the need for any trigger. debootstrap works into the mounted root; it only unmounts proc, which the wrapper restores. An empty but mounted root helps: the alt-root ENOENT fallback gives static AppImage runtimes Linux /proc, /sys, /dev/fd and /dev/shm, so the native set becomes optional. WatchPaths is inert on launchd-842; restoring it from 392.39 is an optional ticket. It also sets a /dev/shm size policy (50%/25% of RAM), gives a full tool spec with exit codes, and lists 11 acceptance tests.
-
NextBSD power management — pmconfigd as the root domain, over an in-kernel ACPI bridge Research plan · new sub-project
Darwin-shaped power management from Apple's own code, and a live sleep bug found on the way. FreeBSD's
acpi_ReqSleepState enters S3 immediately when nothing holds /dev/devctl open (“If devd(8) is not running, immediately enter the sleep state”) — and devd is gone, so today every sleep request suspends with no process told and no Resume event. The design: Apple's pmconfigd (PowerManagement-1846.120.8.0.1) runs as the com.apple.powerd MachService and plays xnu's IOPMrootDomain toward applications — CanSystemSleep, WillSleep, HasPoweredOn with 30 s acknowledgements — over a small in-kernel bridge that publishes IOPMrootDomain0 and IOPMPowerSource nodes into the IORegistry NextBSD already runs in the kernel. ACPI events are consumed in-kernel on the devctl_notify hook: the multi-reader netlink route FreeBSD's own powerd uses (nlsysevent) is module-only and absent here. CPU policy becomes Intel HWP's dev.hwpstate_intel.N.epp knob rather than powerd's loop — and nothing launches powerd today. One decision gates Apple's MIG interface: out-of-line data is broken in this kernel (nextbsd#66). Hybrid by design, and the kernel-later path is cheaper than assumed now that hwregd is retired. An architecture diagram, the sleep/wake sequence, five risks, five open questions and the E17 ticket map (E16 was taken by Bluetooth). Planning only.
-
E17 ticket drafts — every issue, as filed Filed 2026-09-21
The epic, the new
area:power label and all 28 child issues, written out in full and now filed as nextbsd#490, with the children attached as sub-issues and on the Roadmap under Epic = E17 — title, repo, labels, dependencies and the exact Markdown body for each — across the kernel bridge (K1–K7), the MIG decision (M1), pmconfigd (D1–D7), libIOKit (L1–L4), pmset and caffeinate, shutdown and scheduling, display and idle, CI, packaging, and the in-kernel root-domain spike. Every field is also embedded as JSON so the issues can be filed by script.
-
Bluetooth —
bluetoothd, pairing, HID and audio — research & epic plan Research · epic plan
No Darwin source exists, so E16 copies the WLAN shape: FreeBSD’s netgraph stack underneath, and our own bluetoothd (org.nextbsd.bluetoothd) plus a bluetooth CLI on top. Five parallel deep dives, one per area. Kernel: seven stock options plus vkbd and cuse, all compiled in because there’s no .ko tree. The Pi 5’s onboard Bluetooth is unsupported at every layer, so it uses a USB dongle for now. Engine: the old research page was wrong, and FreeBSD 15.1’s kernel already exposes LE, including the ATT and SMP fixed channels as sockets. The gap is userland, so LE becomes a thin host inside bluetoothd rather than NimBLE or BlueZ. Daemon: it replaces hcsecd (which has no SSP), supervises sdpd/bthidd, and builds the graph that devd used to build. HID: keyboards work pre-login through kbdmux’s evdev node; mice in a running X are blocked on input hotplug. Audio: A2DP rides virtual_oss, which moved into FreeBSD 15 base; HFP needs kernel eSCO and is parked behind a decision. Epic filed as nextbsd#473, decisions in #412.
-
E16 ticket drafts — every issue, filed All filed
All 38 child tickets of #473, written out in full across nextbsd, nextbsd-kernel and nextbsd-userland, with the new
area:bluetooth label: title, repo, labels, size, track, dependencies (also as native blocked-by links) and the exact Markdown body. Six stages: kernel and image, hand verification, the daemon core, HID, audio, then LE, plus decisions, hardening and parked work. Every field is also embedded as JSON.
-
Mounts, fstab and DiskArbitration — research spike & epic plan Research · epic plan
Stop shipping
/etc/fstab: Apple stopped in 10.4, and the boot hook NextBSD vendored runs mount -vat nonfs only if the file exists (launchctl.c:2401). A full consumer audit shows nothing on either medium needs it to boot, fsck, remount root or shut down, so removing it fixes #467 by construction, with two small companions: noatime into launchd's remount, and a single owner for the root fsck. From DiskArbitration-535 source: fstab is a policy overlay keyed by UUID=/LABEL=, never a mount list; every other spec, root and Linux lines included, is ignored. Linux ABI mounts follow Apple's pattern, where the subsystem's own daemon provisions its mounts (vminitd, the Rosetta guest, xsand). #54's “manual” decision survives as “admin creates fstab”, with an org.nextbsd.linux one-shot as the upgrade path. WatchPaths is shown to be inert on NextBSD, and autofs to be possible but hazardous. USB sticks: vermaden's automount is a reference table, not the mechanism (needs devd, no nosuid, fsck -y on every plug, and its detach step would delete Gershwin's /Volumes mount points). Gershwin already discovers /Volumes by polling, and its real gap is unmount/eject without sudo. Proposes the DiskArbitration epic (21 tickets in four stages), corrects seven stale claims across tickets and pages, and supersedes the May DiskArbitration plan. Epic filed as nextbsd#471.
-
E15 ticket drafts — every issue, ready to file All filed
The epic (#471, on the roadmap), the new
area:storage label and all 21 child tickets, written out in full — title, target repo, labels, dependencies and the exact Markdown body for each — plus the migration of #467, twelve linked issues and seven correction comments for tickets whose facts are wrong. Four stages: A fstab and boot (closes #467, no DiskArbitration code), B Linux ABI mounts (B2 parked per #54), C the daemon (GEOM disk model, fs bundles, /Volumes executor, fstab overlay, a kernel iconv/UDF ticket), D framework, unmount/eject, the Gershwin bridge and console user. Found along the way: Gershwin posts unmount notifications on the wrong centre, so its own observers never see them. Every field is also embedded as JSON so the issues can be filed by script.
-
NextBSD swap and paging without a partition — one extent layer, two filesystems Research plan · new sub-project
No swap partition, no dump partition, working crash dumps, and ZFS — all with one mechanism. Apple and illumos independently arrived at the same design: pin an object's blocks, extract a physical extent map once, and bypass the filesystem at I/O time. After
swapon the filesystem is off the write path entirely, so a swapfile performs like a partition and cannot deadlock the way today's md(4) path does — there the md kthread is not pageproc, and inside VOP_WRITE → getblk it sleeps in vm_wait behind the pagedaemon, which is waiting on its own bio. Crash dumps need no dumperinfo change: netdump already registers mediaoffset = 0, mediasize = 0 (netdump_client.c:584), and ndadump() gives a polled NVMe path on the Pi 500+. ZFS is restoration, not invention: illumos shipped zvol_dumpify from 2008, OpenZFS stripped it, and FreeBSD 12 carried it under #ifdef illumos without ever compiling it — yet dmu_buf_will_not_fill() and the DB_NOFILL checksum assert survive as live code (dbuf.c:2973, :5500). Explains why a memory reserve is not the safety argument: illumos built all of it and still ships pageout_deadman, which panics after 90 s, and on FreeBSD KM_PUSHPAGE is #defined to M_WAITOK while arc_memory_throttle() returns 0. Also corrects freebsd-apple-userland-cmds §7: modern dynamic_pager is a 114-line shim, macx_swapon returns ENOTSUP, and the port needs no Mach traps. Settles the layout at /private/var/vm, with zroot/private/var/vm under canmount=off parents so the boot environment stays intact, and flags a pre-existing zfs_putpages deadlock exposure on every ZFS root. Two diagrams, a phase-by-phase walkthrough, seven risks, six open questions, and the E14 ticket map. Planning only.
-
E14 ticket drafts — every issue, as filed Filed 2026-09-20
The epic, the
area:vm label and all 23 child issues, written out in full and now filed as nextbsd#468, with the children attached as GitHub sub-issues across nextbsd-kernel, nextbsd-freebsd-compat, nextbsd-userland and nextbsd. Each entry gives the title, repo, labels, dependencies, the exact Markdown body that was filed, and a link to the live issue. Both pre-filing checks are done: soft-updates cannot see bypass writes (R7), and the nextbsd-kernel audit found no local swap_pager.c/md.c changes. #393 (wired memory at idle) was moved here from E11. Every field is also embedded as JSON, so the issues can be recreated by script.
-
NextBSD under Apple Virtualization.framework — boot, console, and the fbdev gap Plan · new sub-project
The one arm64 target where NextBSD has never booted — and the cause was not ACPI, GIC, or graphics. It was USB. Apple’s virtual xHCI aborts the whole VMM (
Security Assertion Hit → __os_crash → brk #0x1 on the vCPU thread) when a guest writes a register it does not model, and FreeBSD’s xhci(4) does exactly that — so any VZUSBKeyboardConfiguration is fatal. Removing USB took the guest from a 6–8 s crash to 121 s alive. But booting is only unblocked, not fixed: stock FreeBSD 16.0-CURRENT fails identically (loader menu renders, kernel loads, then two vCPUs pegged at 199 % with no DHCP), which removes NextBSD from the suspect list and makes networkextension’s out-of-tree patches a hard dependency rather than an optimisation — rebasing cannot fix this. Measured on the platform: ACPI is present (acpi.rsdp = 0xefbf0018, OEM APPLE) so the loader’s “no device tree blob” warning is a red herring; there is no GOP at all (gop list → Graphics Output Protocol not present), so vt_efifb can never attach; and no SPCR, no DBG2, no UART in the DSDT — leaving virtio_console(4), which is tty-only in FreeBSD, as the only console path, which is why the guest is mute after ExitBootServices. Three routes to a console costed (port the ~250-LOC consdev; Apple’s private _VZPL011SerialPortConfiguration at 0x20010000, which is entitlement-gated and lab-only; or the virtio-gpu KMS console). Also closes graphics plan §10: enabling drm_fbdev_shmem.c yields exactly 8 errors and no missing subsystem — deferred I/O is not needed at all, since drm_fbdev_ttm.c already #ifdef __linux__s the identical calls and the console damage path (fb_imageblit → drm_fb_helper_damage_area → fb_dirty → drm_atomic_helper_dirtyfb) is already implemented and exported, so it is ~20–40 LOC of stubs with wulf@’s 2ac98d2787 as precedent. Amends Option D and constraint V7: both assume a firmware framebuffer exists, which is false on a VM whose only display is the virtio-gpu. Six blockers ranked with the cheapest test for each, four phases, and six open calls.
-
NextBSD on the Raspberry Pi 500+ — native boot, and the driver map Survey / options · new sub-project
Booting NextBSD on real arm64 hardware, without UEFI. The target is a Pi 500+: BCM2712 D0 silicon, 16 GB, built-in NVMe, and a keyboard five layers deep behind PCIe→RP1→dwc3→xHCI→HID. worproject/rpi5-uefi cannot boot it for three verified reasons — archived since Feb 2025, releases target pre-D0
BCM2712C1 only, and board type 0x19 was never added (16 GB is not the cause). The headline: FreeBSD can already be booted directly by the Pi firmware with zero new boot code — std.arm64:70 sets LINUX_BOOT_ABI, Makefile.arm64:87 has a kernel.bin target, arm_kernel_boothdr.awk emits a real arm64 Image_header, and the firmware parses exactly that. Second headline: OpenBSD 7.8 already ships the whole BCM2712+RP1 stack in 4,113 LOC of ISC (rpone, bcmmip, bcmpcie, bcmstbpinctrl…) — ~4× smaller than Linux's equivalent and licence-clean, so Tier A ports from OpenBSD, not Linux. Settles the LinuxKPI question too: platform_driver_register() literally returns -ENXIO and there is no clk/OF/pinctrl/regulator shim, so devicetree drivers must be native newbus/FDT. Includes the full hardware inventory measured over ssh, four boot routes costed, a per-device port/graft/net-new map, the kext three-tier split, the vc4-vs-v3d graphics analysis (building on #33's arm64 drm-kmod result), an 8-phase sequence, and the upstream dependency on D58105 (dma-ranges). A survey, not a decision — nine open calls are gathered at the end (which boot route ships, whether the image can be loader-less, kext granularity on arm64, vc4 baseline, EEPROM update first) with what would settle each. Planning only.
-
The Pi 500+ has an internal debug connector Finding · how-to
Undocumented, widely assumed absent, and it is there. A 3-pin JST-SH on the main board immediately above the 40-pin header, carrying UART10 inside the BCM2712. No teardown of a Pi 500+ showing it had ever been published; this page’s author had already concluded it was not fitted, twice, having zoomed past it and called it a crystal. Why it matters: the 40-pin console is RP1 UART0, which lives behind PCIe — so it dies the instant a driver takes ownership of that controller, and a working driver becomes indistinguishable from a dead one. That hard-blocked the entire RP1 branch: ethernet, both USB controllers and the built-in keyboard all hang off it. This connector is in the SoC, so PCIe can be reset as often as the work needs. Confirmed by the ordering of its own output — SDRAM training prints before RP1 is loaded with firmware and before PCIe is scanned, which a UART behind PCIe could not do; Linux reports
ttyAMA10 and the alias resolves to /soc@107c000000/serial@7d001000. It is also strictly the better console: boot ROM, OTP, USB-PD negotiation and TF-A’s BL31 banner all appear here and never reached the header. Includes where it is on the board, why the two-contact BATTERY connector nearby must not be confused with it, which of the Debug Probe’s two cables to use, and the config.txt lines to remove. Trade-off: it is internal, so the case stays open.
-
Wiring a serial console to the Raspberry Pi 500+ How-to
Phase 0, in full — and it needs no disassembly. Three wires from a Raspberry Pi Debug Probe to the external rear GPIO header. The common wrong turn is hunting for the 3-pin JST-SH connector a Pi 5 B exposes between its micro-HDMI ports; on a 500+ that one is inside the keyboard enclosure and may not even be populated. Drawn diagrams of the probe end (
U is UART, D is SWD and unused), the whole 40-pin header, and a close-up of the only six positions you touch. Orientation is settled by the ▲40 silkscreen on the case: pin 1 is at the Ethernet end, so the three wires land in the bottom row at positions 3, 4 and 5 — and the two positions immediately to their right are 5 V, which is the one way to destroy the probe. Also covers the firmware lines (enable_rp1_uart + pciex4_reset=0), why /dev/cu. and not /dev/tty., and how to hold the port open for unattended capture. Closes with what this console cannot do: pins 8/10 are RP1 UART0, behind PCIe, so it dies the moment a driver takes ownership of that controller.
-
The Pi 500+ boot partition, and where the image gets built Plan · sub-plan
Written after phase 0 was proven on the real board — a serial console is live on the 40-pin header, so the bring-up now has its only input channel. Answers the three questions that came out of it. What
kernel8.img is: the FreeBSD kernel, ELF-stripped, with a 64-byte arm64 Image header — cp kernel.bin kernel8.img — and the measured gap is that nothing in nextbsd-kernel CI invokes the kernel.bin target, which is the whole distance to a banner. Why no ISO, ever: the BCM2712 bootloader looks for a FAT partition containing config.txt, so build.sh’s .iso.zip lane is meaningless here. Which repo: nextbsd/build.sh (shipping images, package-installed, aarch64 lane already exists) — nextbsd-userland/ci/assemble-image.sh calls itself a CI-only port of that recipe; the root half needs no change and only the boot half is new (MBR not GPT, plain FAT32 not an ESP, four files not loader.efi). The consequence that is not plumbing: route (a) has no loader, so no .ko loads at boot — yet build.sh installs kexts from packages, which would sit on disk unused. That makes the parent’s §5 kext-granularity question a hard constraint and implies a board kernel config. Includes the measured partition table and EEPROM state (BOOT_ORDER 0xf461→0xf614 so USB precedes the internal NVMe), and flags a likely correction to the parent: the firmware reports RP1 UART at 0x1C_0003_0000, not 0x1F_0003_0000. Names the published artifact: NextBSD-rpi500-arm64-<date>.img.zip — board token first, because build.yml:276’s IMG=$(ls out/NextBSD-${ARCH}-*.img.zip) is a bare ls into a scalar, so an arch-first Pi name would break the existing arm64 lane by matching two files. Measured claims are marked apart from reasoned ones throughout.
-
How an installer should decide what kind of boot to install Plan · sub-plan
Written after four attempts at the detection got it wrong in four different ways. NextBSD’s installer writes GPT+ESP unconditionally, which a Raspberry Pi cannot boot — its firmware reads
config.txt off a FAT partition and enters the kernel directly, with no UEFI, no BIOS and no loader. Surveys every runtime signal available and says what each actually proves: machdep.bootmethod is x86-only and machdep.efi_map is amd64-only (testing it would have written MBR onto every arm64 UEFI machine); “has a device tree” proves nothing because loader.efi installs one from the EFI configuration table; ofwdump -S truncates at the first NUL so brcm,bcm2712 is invisible behind raspberrypi,500, where hw.fdt.compatible returns the whole property and needs no root. Separates requirement from convention — MBR and type 0x0c are real; the active flag is not (every Raspberry Pi OS image from 2012 to 2026 has status byte 0x00). Records that FreeBSD’s own Pi image does not do firmware-direct boot at all — it chainloads u-boot.bin to manufacture a UEFI environment, which is why no upstream installer has a case for us. Decides: test for a UEFI loader first, then the board, and always reproduce the medium’s boot setup rather than generating one. Leaves the EDK2-on-Pi case explicitly unresolved — it is a Pi and UEFI, and needs a fourth layout nobody has tested.
-
NextBSD DirectoryServices — the file-level porting plan (
directoryservicesd) Plan · top-level goal
The top-level goal. Give NextBSD network users spanning Mac OS X 10.3 → macOS 26, via a net-new directoryservicesd — a Path-B C daemon wrapping DirectoryService-621.12's in-process plugins behind the C API, with pam_directoryservices + an nss_directoryservices nsswitch module and org.nextbsd.* Mach names (from a 4-agent source-grounded scope). Home directories for those users are served by NFS (preferred, NextBSD↔NextBSD) and AFP (optional, legacy Macs to macOS 26) — the sequence nested below. Filesystem decisions: UFS is the live-ISO filesystem (plus the case-insensitivity patch for Mac shares); ZFS stays a supported installed-system option delivered via a future separate zfs-img (never the live ISO — NextBSD may eventually ship iso / ufs-img / zfs-img variants); HFS+ is a footnote (read-only Mac-disk migration). Next piece to wire: NFS as launchd daemons. Planning only.
-
Directory services before
opendirectoryd — research & source-availability audit Research
Background & the audit that drives the port. What Apple's directory stack was across three generations — NeXT NetInfo → OS X Open Directory → the closed opendirectoryd — with local-vs-network user records, storage paths, and home-mount protocols per era, plus the verified reusable code (DirectoryService-621.12, OpenDirectory-146, APSL 2.0; opendirectoryd never released). ~100 primary sources.
-
Native AFP server (
afpd) — optional legacy-Mac home/file serving Plan
The optional AFP home/file server for legacy Macs (10.3 → macOS 26). A clean-room DSI/AFP daemon (netatalk is GPL → design reference only) with an FS-agnostic fork backend (UFS2 extended attributes / AppleDouble / HFS+-native) so the same server works over any filesystem; auth arm's-length via pam_directoryservices; Bonjour _afpovertcp._tcp + launchd. ~6–9 months. Strategic bound: Apple is removing the AFP client (deprecated macOS 15.5, gone in macOS 27) — so this serves old Macs, never 27+.
-
UFS case-insensitivity patch — Mac case behavior for the shares Plan
Mac-friendly case-insensitivity for UFS shares — a tightly-bounded
nextbsd-kernel patch: three changes (ufs_lookup compare + ufs_dirhash key/verify) and an FS_CASEFOLD superblock flag. ~4–6 weeks for the ASCII-only version (vs years for an HFS+ port). Precedent is ext4/msdosfs, not Apple — Apple's own UFS was case-sensitive, and even ravynos skipped this. Caveats: NextBSD-only volumes, and fsck_ffs collision-detection is the tail.
-
HFS+ — read-only Mac-disk interop Footnote / deferred
Footnote — deferred, not a default/alternate filesystem. Worth revisiting only as read-only disk interop: mounting real HFS+ disks /
.dmg / Time Machine volumes to migrate Mac data into NextBSD — and likely moot once UFS is patched. The detailed port research (the entire HFS+ stack is open, APSL 2.0 in apple-oss-distributions/hfs) is retained as reference for that interop case; the original "alternate main filesystem / replace UFS" framing is superseded.
-
NextBSD monorepo — one freebsd-src tree, feasibility & setup Draft · feasibility
Working draft (several topics deliberately unsettled). Feasibility + setup for collapsing the ~10
nextbsd-redux repos into one pkgdemon/nextbsd tree seeded from fresh FreeBSD main with our changes committed on top. Core calls, all precedented: track freebsd/freebsd-src main as a git remote and merge (HardenedBSD model) — never rebase/patch-files/in-network-fork — seeded from a squashed single-branch snapshot (~1–1.5 GB), first merge --allow-unrelated-histories, a scheduled Action opens the sync PR (via git CLI + gh, not the reset-hard fork recipe). Userland into buildworld via the LOCAL_DIRS/LOCAL_LIB_DIRS/LOCAL_MODULES hooks (recon correction: the userland is already mostly bsdmake — only libdispatch + swift-foundation-icu are CMake). pkgbase “compose, don’t strip” — leave unused base (init in must-have FreeBSD-runtime) in-tree, curate ISOs via pkg install, switch nextbsd-freebsd-compat from installworld+tar to make packages. syslog/ASL replaced by stock syslogd + a thin libasl write-shim. Selective/incremental builds (META_MODE+filemon+ccache), Linux cross-build of world/kernel (tools/build/make.py) with ISOs on a real NextBSD host, optional BuildKit layer caching, and a detect-changes CI topology. Explicitly unsettled — slated for breakouts: IOKit / kld-vs-kext (§4, now broken out below), make release targets & the media matrix (live / UFS & ZFS installer / RPi & other images — coordinating with the images survey below), and pkgbase package naming. Planning only.
-
Driver delivery — kld / kext / IOKit scoping Draft · sidebar
The IOKit sidebar (provisional; IOKit is the open topic). Should NextBSD reconstruct every driver as a kext, only firmware drivers, or a mixture? And does any of it need Apple's in-kernel IOKit C++ runtime? Verdict: a deliberate three-tier mixture — compiled-in (boot minimum) / plain kld (devmatch-autoloaded: input, ethernet, most drivers) / kext (firmware ∨ multi-version) — and NO IOKit runtime, which is already the direction of the shipped design (the in-kernel matcher is flat-C on newbus, not OSMetaClass; the ko→kext plan scoped the real runtime out as a 3–5 engineer-year newbus collision). Unifies ~14 existing plans + the live
ko2kext tooling. Key refinement: of the catalog's 343 kext candidates, only ~50–60 actually need kext treatment (firmware ∨ version); the other ~250 stay plain klds that native devmatch already matches — add a form column as single source of truth, generate build-excludes for one-form-per-device. Firmware offline is solved pkg-free by firmware(9) (FreeBSD 15's raw /boot/firmware); the wired-memory win is honestly tens of MB, not hundreds (the vm_page array dominates), so lead with version-coexistence + modularity. nvidia coexistence works on-disk/per-boot but never two-branches-resident (an EEXIST wall IOKit can't fix either). make buildkernel already emits klds; a NextBSD-owned make extensions target formalizes ko2kext with zero upstream edits. Planning only.
-
The firmware & multi-version driver short-list — the ~50 kext candidates in a table Reference table
Which drivers actually earn kext treatment, enumerated. Under
kext ⇐ has-firmware ∨ needs-multi-version: 23 WiFi + 3 GPU DRM (i915/amdgpu/radeon) + 6 nvidia branches + 6 ethernet/storage/crypto = ~38 driver kexts, plus ~13 separate firmware-image kexts ≈ ~50 total (so "50–60" holds once the firmware blobs count as their own kexts; the driver count alone is ~38). Per-driver: chips/vendor, whether firmware is base-owned (free, in sys/contrib/dev) or a ports/pkg blob you must bake into the image (net/wifi-firmware-*, graphics/gpu-firmware-*), the proposed org.nextbsd.* kext name, and whether a gen-*-personalities.sh exists. Includes the "looks like firmware but isn't" exclusion list (bxe/qlnxe compile fw in; mlx/sfxge/oce/RAID HBAs are on-card — a catalog over-count to fix), the restricted-license wifi (bwn/bwi/malo/upgt, unbakeable), and the correction that nvidia 304/340/390 all still build on FreeBSD 15/16. Planning only.
-
Kext naming manifest — the static before→after name list Manifest
The curated
KMOD → kext mapping that drives the build, grounded against FreeBSD main. See also the build model (below) that consumes it. Decision: naming follows build origin — base in-tree drivers take their FreeBSD name (if_iwlwifi.kext, mechanically auto-derived, ~280 modules, zero judgment), out-of-tree ports take custom curated names (IntelGraphics.kext, NVIDIAGraphics595.kext — only 12 hand-named, since they already need bespoke build recipes). Every row shows the FreeBSD KMOD, the prior June-catalog name, and the new name + org.nextbsd.* id. Corrections the main grounding forced: adds the 4 missing USB wifi drivers (run/rsu/uath/upgt), expands mt76 to its 6 real KMODs, drops the non-existent iwmfw, moves nvidia from one NVIDIA to per-branch, and confirms zero driver deltas between main and releng/15.1 for this set. Static manifest = names + wiring; the IOKitPersonalities match tables stay auto-generated. Planning only.
-
Restricted wifi firmware — the 4 drivers no one can ship firmware for Reference
Why
bwn/bwi/malo/upgt sit apart, and how every OS handles them in 2026. Their firmware isn't redistributable (not merely "non-free"), so nobody bakes it into an install image — not Debian's firmware ISO, not OpenBSD's fw_update. Per-chip verdict (b43 = extract-from-Broadcom-driver via b43-fwcutter; PrismGT/upgt = third-party daemonizer.de blob, NOT in linux-firmware — corrects an earlier claim; Marvell 88W8335/malo = no distribution grant + Linux-orphaned, BSD-only). A distro×driver matrix (Debian/Ubuntu/Fedora/Arch/openSUSE/Gentoo/NixOS/FreeBSD/OpenBSD/NetBSD) showing the universal "ship the driver, not the blob" dodge; the OpenBSD fw_update gray-area model; the b43-fwcutter mechanism; and an EOL note (all dead 2000s silicon, the b43 route itself bit-rotting as Broadcom pulled the URLs). For NextBSD: drop them or carry-driver-only — the wall is redistribution rights no image can climb, so a firmware-included ISO (right for the ~19 redistributable wifi drivers) simply doesn't reach these four. Planning only.
-
Build model — three targets, three tiers Plan
How the compiled-in kernel, the loadable klds, and the kexts all come from one tree without patching a stock FreeBSD build file.
make buildkernel NO_MODULES=yes makes tier 1 (kernel image, stays stock via a flag); a NextBSD-owned make buildextensions builds in-tree sys/modules → plain klds + firmware kexts; make buildgraphics handles the out-of-tree drm-kmod + nvidia ports (they're not sys/modules, and already need bespoke fetch/patch recipes); make buildworld is untouched. All feed a shared bsd.kext.mk (formalized ko2kext) into /System/Library/Extensions. Load-bearing open decision: Pure vs Mixed — keep /boot/kernel holding the ~250 plain klds (native devmatch carries them, matcher scoped to ~38) vs an empty /boot/kernel with everything a kext (your matcher owns all matching). Also: build-time fetch of nvidia/drm-kmod does not violate base-never-requires-pkg (that's a runtime/target constraint), and an alternatives section for local ergonomics (a make nextbsd umbrella + build.sh, or "local stays stock, kexts are release-only" — the heavy target graph is mostly a Pure-model tax; Mixed lets buildkernel stay stock and bootable). Planning only.
-
NextBSD images — shipping UFS and ZFS (options survey) Survey / options
How NextBSD could support both UFS and ZFS across its media, judged against three hard constraints — simple installer (keep the cpdup clone, no wizard), no img-vs-installed divergence, and the live ISO stays UFS. A survey, not a decision (deferred). Baseline:
build.sh already ships a live-UFS iso + a dd-able rw-UFS img, and the cpdup installer is already filesystem-agnostic — so adding ZFS is a third build artifact (makefs -t zfs; the loader reads ZFS directly, so it's the same GPT + gptzfsboot), gated only on OpenZFS being compiled into the NO_MODULES kernel. Recommended framing: three editions — live (run, UFS) / installer (boots to installer, installs UFS or ZFS) / disk (prebuilt dd/VM image, UFS + ZFS variants) — with ZFS gated to the non-live editions, which cleanly solves both the USB write-endurance problem (ZFS never runs off cheap flash) and the divergence worry (no live ZFS to diverge from). Includes the full artifact filename table, four naming schemes (live/installer/disk preferred), the option-by-option scoring, the ZFS-on-USB-flash caveats (write amplification + flush-lying), and FreeBSD/GhostBSD/TrueNAS precedent.
-
NextBSD WLAN —
wlan_xauth, wland, and the DHCP gate Plan
Source-level review of all six nextbsd-redux repos plus gershwin-components. The headline is a one-line kernel bug: wlan_xauth is absent from config/NEXTBSD, and because assemble-image.sh deletes /boot/kernel/*.ko, net80211’s kldload autoload can’t self-heal — so every WPA/WPA2/WPA3 association fails EINVAL at the first ioctl. Open networks work. Everything else in the protocol stack is already built: wpa_supplicant 2.11 ships in FreeBSD base untouched, IntelWiFi.kext is firmware-bundled and boot-proven in CI, LinuxKPI’s mac80211 shim is statically linked. But there is no zero-glue path: nothing creates the wlan0 VAP (that’s rc.conf’s job, and NextBSD has no /etc/rc.d), and ipconfigd fires DHCP at net80211 RUN state — before the 4-way handshake — then never retries, because the link is already Active. Design: keep wpa_supplicant stock, from base (don’t vendor it, don’t teach it Mach — it never needs to know configd exists); a C + CoreFoundation wland owns the VAP lifecycle, drives the control socket, and publishes State:/Network/Interface/wlan0/AirPort; ipconfigd grows a ~70-LOC gate requiring Link{Active} and AirPort{Authenticated}, with key-absent meaning ready so wired NICs are untouched. Key constraints found in the code: Objective-C does not exist in nextbsd-userland (so the CoreWLAN-shaped API lives above the Mach line, in Gershwin, reached via a C CLI like ipconfig); MIG out-of-line data is broken in this kernel; the store rejects any value over 8 KiB (so scan results can’t go in it); and there is no keychain, so PSKs are plaintext either way. Also documents two latent ipconfigd bugs WLAN forces us to fix — a dead re-arm after lease expiry, and a global single-flight that caps the daemon at one bound interface for its lifetime.
-
WLAN on NextBSD — the Apple stack, and why we use
wpa_supplicant Research
Background for the plan above. What macOS actually runs (closed wifid + IO80211Family + CoreWLAN; the only open piece is eapolclient, which does the EAP exchange and nothing else — no scanning, no association, no 4-way handshake, so there is no seed for a custom supplicant). Function-by-function comparison of Apple’s stack against wpa_supplicant and iwd, and why iwd loses decisively on the two non-negotiable rows — it’s welded to nl80211/cfg80211 with no net80211 backend, and it’s LGPL-2.1 — while still being the better design reference for the autojoin brain wpa_supplicant lacks (its Station/Network/KnownNetwork/Agent objects map almost one-to-one onto CoreWLAN). iwd contributes ideas; wpa_supplicant contributes code. Includes a glossary of the whole 802.11 security alphabet — AWDL, EAP/EAPOL, the 4-way handshake, PMK/PTK/GTK, SAE, OWE, PMF, 802.11r/k/v.
-
NextBSD configd — porting plan Serving
The Mach-IPC
configd port: a MIG server on com.apple.SystemConfiguration owning the SCDynamicStore, with IPConfiguration as a separate daemon beneath it (not an in-process plugin, since there is no plugin loader). Renamed from freebsd-configd-plan — this was never FreeBSD-only work. Reconciled with the WLAN plan (2026-07-14): configd is the store, not the author — wland publishes State:/Network/Interface/<iface>/AirPort itself, so no configd change is needed for WLAN. Two stale claims corrected: an earlier draft had configd reading the wpa_supplicant control socket and populating a …/WiFi key (wrong key, wrong owner), and argued for security/wpa_supplicant from ports superseding base (rejected — it's the same 2.11 and shadows base for nothing; we keep it stock from base).
-
NextBSD IPConfiguration — porting plan Shipped through iter 8
Apple's
IPConfiguration (DHCPv4/v6, RA/SLAAC, RFC 5227 ARP) as a standalone Mach daemon replacing dhcpcd, publishing leases to configd over Mach RPC. Renamed from freebsd-ipconfiguration-plan. Reconciled with the WLAN plan (2026-07-14): WLAN association state does not come from a wpa_supplicant hook (NextBSD has no dhcpcd, so no such hook exists) — it arrives as the AirPort key wland publishes, and ipconfigd grows a ~70-LOC gate requiring Link{Active} and AirPort{Authenticated}, key-absent meaning ready so wired NICs are untouched. Also now records that "single interface, Ethernet only" is not merely unbuilt but two real bugs — a dead re-arm after lease expiry (#37) and a global single-flight capping the daemon at one bound interface for its lifetime (#38) — both hard blockers for the gate.
-
NextBSD & Gershwin — uniquely name the base
libdispatch fork Research / decision
Why the Gershwin global menu stopped following app-switches on NextBSD, and how it grew two builds of libdispatch. Root cause: NextBSD’s Mach-enabled libdispatch compiles EV_UDATA_SPECIFIC as 0x0100, which on FreeBSD kqueue collides with EV_FORCEONESHOT — so the menu’s file-descriptor watch fired once and was never re-armed (and the “second login fixes it” clue was a red herring: a stale active-window value left on the never-restarted login X display). Settled by on-hardware testing: the base can’t move to Gershwin’s stock build (six components need Mach sources and won’t link), and Gershwin can’t move to the Mach build — the menu still fails even with the event.c/event_kevent.c kqueue patches. So both builds must exist; the only real bug is that they share the name libdispatch.so. From a multi-agent audit across nextbsd-userland, gershwin-developer, and gershwin-components: the fix is a single rename — give the base NextBSD fork the unique soname libsystem_dispatch.so (a build-harness change; the fork is permanently owned and never synced with Apple again) and relink the base consumers onto it, while Gershwin’s stock Apple libdispatch.so is never touched. Distinct names also kill the original launchctl/dbus/sshd “Undefined symbol” poisoning. Safety rule: no single process loads both, guarded by a CI DT_NEEDED check — safe because the only base lib Gershwin consumes, libdns_sd, is a dispatch-free AF_UNIX stub. Includes a plain-language “how to read the per-library disposition” guide, the full disposition table, mDNS reconciliation, risks, and open questions. Companion to the fork-necessity review below.
-
Is the NextBSD Mach
libdispatch fork still needed? Research / review
The prerequisite question behind the rename decision above: can NextBSD just drop its Mach libdispatch fork and use Gershwin’s stock build everywhere? No — six production components (libxpc, launchd, notifyd, and friends) depend on libdispatch’s Mach sources (DISPATCH_SOURCE_TYPE_MACH_RECV / dispatch_mach_t), and a stock HAVE_MACH=0 build can’t even compile or link them; swapping would break boot and all XPC IPC. But the instinct is half-right: the “fork” is barely a fork — a handful of compile-time deltas over stock — which is exactly why it can safely coexist with Gershwin’s stock copy once it carries a distinct soname. Component-by-component review of what actually needs Mach and why the fork earns its keep.
-
NextBSD quiet console — kernel/IOKit spew off ttyv0, and an optional boot splash Plan / mockups
Make the login console behave like macOS: no kernel / IOKit / driver text on ttyv0 on a normal boot (or on USB-eject), but full output under
boot -v, everything still in dmesg+syslog, and panics always visible. The surprise: FreeBSD already does all the hard parts — every kernel printf splits into an always-captured TOLOG sink and a cn_mute-gated TOCONS sink, panic() force-unmutes, and RB_MUTE/RB_MUTEMSGS self-cancel under RB_VERBOSE. So the base case is a one-line loader change (boot_mute="YES" in the nextbsd-overlays fragment); the only real work is making CI boot verbose so the boot-test still sees its markers. Closes with a feasibility read on an Apple-style boot splash and four to-scale mockups (grey/black × static/spinner, real nextbsd.org cube + a CSS 12-spoke throbber) — framed as a layer on top of #272 (seamless vt(4)→drm-fbdev deferred takeover), since a splash shipped before #272 would flash to black when the GPU driver attaches. Ticket nextbsd#363.
-
NextBSD path-domain conformance — adopt the gershwin four-domain layout Plan / audit
Bounded follow-up to the layout spike (that decision tabled): conform NextBSD's paths to gershwin's four domains — every bare Apple
/Library/X becomes /Local/Library/X; /System/Library stays. Org-wide audit of all 9 nextbsd-redux repos: six clean, freebsd-src out of scope by rule, and the rename surface confined to the Darwin userland source — ~12 runtime-behavioral sites across 6 subsystems (launchd PID-1 scan, SCPreferences, CoreFoundation, kext_tools, ASL). Headline finding: that source is duplicated byte-for-byte across nextbsd-userland/src (CI cross-build) and nextbsd/src (the ISO builder, which compiles its own copy), so the change is doubled and must land in lockstep. Includes the two hardcoded strlen traps (20→26, 14), the judgment calls (StartupItems/Logs/Spotlight), and the full file:line surface. Planning only.
-
NextBSD system path layout — the Gershwin layout vs a POSIX layout Research / spike
Gershwin ships an official named
tools-make FilesystemLayout (--with-layout=gershwin) that roots the desktop at /System — and it collides with NextBSD on two exact directories: /System/Library/Libraries (Gershwin's ObjC install target vs NextBSD's rtld Lever-B search path) and /System/Library/LaunchDaemons (Gershwin's session plists interleave with NextBSD's 11 boot daemons). Gershwin's own make uninstall already refuses rm -rf /System on NextBSD. So: keep the domains and move Gershwin off /System (a Gershwin-layout fork — not NextBSD's call), or move NextBSD to POSIX paths (/etc/launchd.d, /usr/local/etc/launchd.d, /boot/kernel/extensions, /var/db) so rm -rf /System removes only the desktop? From a 3-agent source sweep + on-target verification: full per-path inventory, both layouts side-by-side, honest cost accounting, and the exact file:line change surface. Key findings: no /System/Library/Frameworks on the NextBSD side (libs in /usr/lib/system — no dyld/ABI knot), the kernel is filesystem-blind for kexts (zero kernel changes), and the kext cache self-disables on the move. Planning only.
-
A cross-built
nextbsd-userland repo — split the Apple stack out, slim nextbsd to an ISO assembler Plan ready
Move NextBSD's ~2,260 lines of in-chroot Apple-userland compilation out of nextbsd/build.sh into a new nextbsd-userland repo that cross-compiles on a Linux host (like nextbsd-freebsd-compat), publishes a 4th rolling continuous artifact, and drops the in-image build toolchain (cmake/ninja/pkgconf/llvm19) entirely — leaving nextbsd a thin assembler (download 4 tarballs → layer → uzip → ISO, ~3,245→~700 lines). From a 3-agent scope. Key feasibility wins: only 2 components are CMake (libdispatch, swift-foundation-icu); the other 18+ are bsd.lib.mk Makefiles that cross-compile in make.py buildenv with zero source changes. The real work is harness-level: stage the sysroot from compat's continuous, relocate two build-time runs of cross binaries (test_corefoundation/kextdeps) to the boot-test, replace ~26 chroot ldd/ldconfig checks with readelf, and write 2 CMake cross-toolchain files. Phased rollout (parallel non-cascading → dual-source flag → flip cascade → delete) keeps the green chain intact.
-
FreeBSD-compat 3-way audit — what we install, overwrite, and could port from Apple Audit
Every one of the 99 components the
nextbsd-freebsd-compat closure installs into the NextBSD base (srclist.txt), cross-referenced against (1) what NextBSD overwrites with Apple-sourced code and (2) what could be ported from Apple open source instead — each with a port-difficulty rating (Trivial→Impractical). From a 4-agent audit. Headlines: 0/99 currently overwritten (Apple's reboot/shutdown/login/su/mtree are vendored from system_cmds/shell_cmds/file_cmds but deferred behind IOPMLib/BSM shims); libbsm already is Apple's OpenBSM; the UFS tooling (newfs/tunefs/fsck_ffs) is Impractical because Apple has no UFS code (only newfs_hfs/apfs); the libc/loader ABI is Mach-O-incompatible; and krb5 is MIT (not Apple's Heimdal), so that “easy Apple port” doesn’t exist.
-
Drop CI caching & purge caches across the build chain Plan ready
Remove every caching layer from the
nextbsd-redux build chain (ccache in kernel/compat/modules, the distfiles cache in nextbsd, the Docker BuildKit GHA layer cache in nextbsd-kernel-toolchain) via direct commits in build-chain order, then purge the stored caches. From a 3-agent investigation with a key correction: dropping ccache is not just deleting the cache step — each make.py wires the cache in via --cross-bindir=${CCACHE_CROSS_BINDIR}, so every call must be repointed to ${CROSS_BINDIR} (the real Clang 19) or the build rebuilds its own LLVM. And the premise correction that matters: the “freebsd-version stuck at 15.0” symptom was not a ccache replay (the kernel sets RELEASE to a timestamp and keeps REVISION stock at 15.0 by design, so vers.c always misses) — it came from a stale baked GHCR toolchain image plus genuine version pins (nextbsd-kernel-modules on releng/15.0, build.sh default 15.0), none of which a cache purge touches. Cache-drop buys determinism; the version fix is the toolchain-image refresh + the 15.1 base bump.
-
NextBSD graphics — bumping
drm-kmod 6.6 → 6.12 (and the 15.1 gate) Decision plan
Can we move the Intel/AMD/Radeon kexts to the Linux-6.12 DRM backport, and do we need FreeBSD 15.1? A three-agent review of the ports tree, freebsd-src LinuxKPI, and the NextBSD graphics build. Answers: (1) yes, graphics/drm-612-kmod exists (DRM from Linux 6.12 LTS, 6.12.85); (2) yes, 15.1 is mandatory — the port hard-requires __FreeBSD_version ≥ 1500509 and NextBSD’s releng/15.0 (1500068) is excluded by the port’s IGNORE clause and lacks the 6.12 LinuxKPI symbols (fd_file(), fop_flags, the <linux/unaligned.h> move, 6.12 kvrealloc(), expanded folio/dma-mapping/xarray) present on 15.1 and absent from 15.0; (3) the kext bump itself is a one-line --branch 6.6-lts→6.12-lts flip + FW_TAG bump, but it’s gated on moving the kernel base 15.0→15.1 (pkg ABI FreeBSD:15 preserved). Covers NextBSD’s current build (drm-kmod 6.6-lts, baked LinuxKPI, per-vendor firmware bundling), the decoupled date-versioned gpu-firmware, the 6.6→6.12 hardware delta (Meteor/Arrow Lake i915, newer amdgpu — but still no Xe2 / Battlemage / Lunar Lake), a sequenced two-ticket plan, and the baked-MODULE_DEPEND/firmware-size risks.
-
NextBSD — executing the
releng/15.1 bump & drm-kmod 6.12 (per-repo runbook) Execution plan
The execution follow-on to the decision plan above: it takes the verdict as settled (drm-612-kmod hard-requires __FreeBSD_version ≥ 1500509, so releng/15.1 is mandatory, pkg ABI FreeBSD:15:amd64 preserved) and answers exactly what to change, in which repo, in what order, and what to verify for issues #336 (base 15.0→15.1) and #337 (graphics kexts 6.6→6.12 + firmware). From a six-agent scope of the whole build chain: the dispatch topology, per-repo scope across the six nextbsd-redux repos (freebsd-src sync fork, nextbsd-kernel-toolchain, nextbsd-kernel, nextbsd-kernel-modules where both tickets land, nextbsd-freebsd-compat, and the nextbsd ISO assembler), a seven-step ordered runbook (sync → manual checkpoint → toolchain → kernel → base → kext bump → assemble & boot-test), a ranked risk register, and acceptance criteria mapped to the tickets.
-
NextBSD input & touch drivers —
hms vs utouch spike Research / spike
A four-agent code review of freebsd-src and wulf7/utouch answering whether choosing hms over utouch loses any device support — no: same author, hms is the in-base successor and a strict superset (relative + I2C mice plus the absolute VM-tablet pointers utouch existed for), and utouch’s own README says “deprecated on 13+, use hms(4).” The historical “base didn’t work” pain was a default-OFF config (hw.usb.usbhid.enable) flipped ON in 15.0, not a missing driver. Covers why NextBSD must compile in the leaves (no .ko tree to auto-load — GENERIC ships only the keyboard leaves + transports), the hms/ums mouse path with absolute-axis handling and the sysmouse/HUG_TWHEEL/INPUT_PROP_DIRECT gaps, the touch/touchpad/pen stack (hmt/hpen/iichid+ig4/ietp/hconf, with IICHID_SAMPLING mandatory on amd64), QEMU/KVM (USB tablet works, no native virtio-input guest driver), Microsoft Surface (IPTS/ITHC/SAM — won’t work in base), Lenovo ThinkPad I2C-HID + PS/2 TrackPoint, the import timeline (D27777 → 13.0 → 15.0 default-on), and the exact config/NEXTBSD lines to bake in. Background for issue #335 / PR nextbsd-kernel#46.
-
NextBSD unionfs inode-collision fix — design plan Design / proposal
Permanent kernel-side fix for issue #332: the live unionfs root gives every layer the same
st_dev and passes underlying inode numbers through, so an upper-layer header and an unrelated lower-layer header collide on (st_dev, st_ino) — and clang, which keys #pragma once/#import/include-guard dedup on file identity not path, silently skips the second (the GS_EXPORT / cups_lang_t / OBJC_PUBLIC / encode_NSInteger failures, none reproducible on stock FreeBSD). No clang flag can fix it (upstream LLVM calls it a filesystem bug). Fix modelled on Linux overlayfs xino: keep one st_dev, reserve the high bit of the 64-bit fileid as a layer tag in unionfs_getattr(), mirror it onto d_fileno in unionfs_readdir() (two layers ⇒ collision-free by construction). Exact union_vnops.c/union.h touch points, alternatives (per-layer st_dev, separate build FS, the inode-bust sweep), a full risks & implications section (32-bit ino_t EOVERFLOW, readdir bounce-buffer cost, copy-up churn, ZFS bit-63 exhaustion, NFS export), a deliberate-overlap kyua regression test + collision detector, and rollout. Retires the inode-bust build-script workaround.
-
Why a live ISO won’t boot under Ventoy / iPXE — the initramfs research Research
Why the FreeBSD ISO fails under Ventoy/iPXE (and sometimes real hardware): the bootloader–kernel handoff wall —
/dev/iso9660/<LABEL> is created by the kernel after the loader has handed off and exited, so swapping bootloaders (GRUB, Apple’s boot-132) can’t help, and at ExitBootServices the firmware Block I/O dies (amd64 long mode can’t call INT 13h either). Two physics classes: real block devices (CD/DVD/dd-USB/VM — work, modulo discovery timing) vs. firmware-emulated/fragmented media (Ventoy/iPXE/MEMDISK — the kernel can’t re-reach them). How Linux actually fixes it — the initramfs, not a mounted ISO: reconstruct from the real stick (exFAT loop-mount / dm-linear) for Ventoy, network-fetch (dracut livenet / casper netboot=url / NFS / iSCSI+iBFT) for iPXE. The NextBSD equivalent: mfsroot+/init is the initramfs — turn /init into an ordered probe. Includes the geom_ventoy (BSD-2-Clause) / exFAT-gap / isboot assessment, a phased plan, and the single-user disambiguation test for real hardware. Background for #288.
-
NextBSD Server Installer — dialog-based installer design Design doc
Screen-by-screen design for a text-mode, dialog-driven server installer — amber-on-black TUI mockups for each phase rendered as illustrative box-drawing renders. Flow: Install / Upgrade (Upgrade gated until an existing install is probed), whole-disk selection showing volume labels + filesystem type broken out per disk (not just device ids), account + hostname (username/password/confirm with a hostname auto-suggested as
joe-thinkpad-t460 from username + DMI model, still editable), a cpdup clone progress gauge (live filesystem cloned to the destination volume, no archive extraction), and a finish screen with Reboot / Shutdown. Build identity follows the continuous rolling release (timestamp + short commit). Includes the hostname-derivation logic, disk-column source mapping, and a palette/visual-language reference for a bsddialog(1) / ncurses implementation.
-
ZFS pool setup — extending the installer for mirrors and raidz Preliminary design · to be refined
A first draft, not the final design — it will be refined before implementation. On the disk picker, F8 switches from a single UFS disk to a marked set of ZFS disks, which are then arranged into vdevs on a two-pane layout screen: ← → moves a disk into or out of a vdev, and T cycles single, mirror, raidz1, raidz2 and raidz3, with usable size and failure tolerance updating live. The minimum disk counts come from the ZFS source (
vdev_raidz_open() rejects fewer than parity + 1). One finding shapes the encryption option: FreeBSD's loader can open a pool that uses encryption but has no code to decrypt a dataset, so an encrypted root would not boot — the draft encrypts user data and keeps the system readable. E14's zroot/private/var/vm stays unencrypted, which ZFS allows under an encrypted parent, and there is no swap partition on any path. Tracked as U4; depends on Z0 (ZFS in the kernel) and A6 (mounting datasets at boot). Seven screens and five open decisions.
-
NextBSD CI Pipeline Plan Live
Automated FreeBSD cross-build pipeline on GitHub Actions free-tier Linux runners: daily fork sync, Docker-layered toolchain containers on GHCR, kernel/modules/base cross-builds, and the
nextbsd ISO assembler. Targets releng/15.0 for pkg ABI compatibility; zero self-hosted runners, no open ports. Now live end-to-end and refactored around rolling, CI-gated continuous releases (producers publish on green main; consumers ingest the release, never in-flight PR artifacts), with PR validation on every repo, arm64 base parity, and a PR-only kernel boot smoke test. See §0 for the June 2026 status.
-
Gershwin on NextBSD — build & live-ISO pipeline Design plan
How the (currently empty)
pkgdemon/gershwin-on-nextbsd repo grabs the latest NextBSD .img.zip, builds the Gershwin desktop into its rootfs, and repackages the result into a live ISO that boots identically to NextBSD’s own — 2-stage MFS, on-demand uzip, unionfs, and vfs.pivot. Covers why the repackage must run inside a FreeBSD VM, the repo layout, the build-iso.yml pipeline and live-ISO repackage stage, and the three launchd services (loginwindow, dshelper, gdomap) the desktop overlay must ship.
-
gershwin-developer — NextBSD build CI (vmactions chroot) Plan
Add a fourth build target to
gershwin-developer’s build.yml — alongside FreeBSD, Arch, and Debian — that verifies Gershwin compiles on NextBSD. The trick earlier lacked: don’t boot the NextBSD image, chroot-build into its rootfs inside a FreeBSD VM. Documents where the job fits in build.yml, the one make-or-break risk, and what the check does and doesn’t prove. The job scrapped two iterations ago, now actually feasible.
-
NextBSD — cross-build the Apple userland + image on native Linux Single-PR plan
Move the NextBSD image build off the
vmactions/freebsd-vm qemu VM (~17 min) onto a native Linux runner that cross-compiles the entire Apple/Darwin userland for FreeBSD with Clang 19 — zero FreeBSD packages, no chroot, the bootable UFS image assembled on Linux, qemu used only to boot-test (which already runs on ubuntu). Reuses the proven cross-build pattern from nextbsd-kernel-toolchain / -kernel / -kernel-modules / -freebsd-compat. Component-by-component plan for all 32 src/ components + mach.ko, the two net-new CMake toolchain files (libdispatch, ICU), Linux image assembly (makefs/mkimg), and a boot-contract parity oracle. Unlocks a faster arm64 ISO as a free matrix leg.
-
NextBSD CI — testing coupled cross-repo changes (strategy options) Decision plan Backbone shipped
How to boot-test changes that span two repos before either merges — motivating case: a
nextbsd-kernel PR raising the dynamic syscall-slot limit plus a nextbsd PR dropping mach.ko’s syscall multiplexor. Visual comparison of five strategies (continuous-release + latest-green pin, artifact-override inputs, full monorepo, partial monorepo, hybrid integration meta-workflow) with a per-stage time-to-test cost model across today’s VM and native-cross worlds, a ratings matrix, and a NOW-vs-after-crossbuild recommendation. Confirms 5 current CI gaps (no PR triggers / no kernel boot test / latest-main-only ingest / a modules patch-ref desync / arm64 asymmetry).
-
mach.ko syscall slots — implementation plan (widen the band, drop the mux) Plan ready
Now that NextBSD ships its own kernel, the fix for the 10-slot Mach syscall ceiling is to widen FreeBSD’s
lkmnosys band in the NEXTBSD kernel (a syscalls.master patch in nextbsd-kernel) so each Mach trap gets its own slot and the multiplexor goes away. Measured need ~32 slots; provision ~48. Includes a measured effort-scope of the XNU-style separate trap table (the kernel hook is a cheap C-only change, but first-of-its-kind on FreeBSD and its Apple-canonical-number benefit is moot because libmach resolves by name), and confirms IOKit needs no XNU libsyscall. Two-PR rollout: widen the band, then drop the mux. Supersedes the audit spike’s “kernel-patch disqualified” verdict.
-
IOKit busyState / waitQuiet — implementation plan (Apple-shape kmod autoload) Plan ready
The “right way” to drive kmod autoload: a real device-quiescence signal (
IOServiceWaitQuiet / IORegistryEntryGetBusyState / IOKitWaitQuiet) over Mach, replacing FreeBSD’s devctl-plus-a-timer heuristic. A balanced device_match_start/device_match_end hook wrapping device_probe_and_attach() in the NEXTBSD kernel feeds a mach.ko bus_busy counter + a mach_wait_quiet syscall; hwregd flips on quiescence and libIOKit implements the Apple APIs. Makes concrete (and corrects the counter design of) the deferred plan in the #67 options doc, now that NextBSD ships its own kernel. Tracked by #176; two-PR rollout like the syscall work.
-
FreeBSD
.ko → Apple .kext conversion Plan
Convert NextBSD’s kernel modules from FreeBSD .ko to Apple .kext bundles in /System/Library/Extensions — the Apple-shaped format, established before the kextd loader port. Code-grounded 3-agent scope: a .kext bundle wraps the unmodified .ko (still kld-loaded), Info.plist IOKitPersonalities generated from MODULE_PNP_INFO feed the userspace matcher NextBSD already has (libIOKit→hwregd); a full XNU OSKext is explicitly out of scope (the path ravynOS left FreeBSD for). Includes the firmware answer (gpu .ko-firmware vs wifi /boot/firmware → Contents/Resources) and the boot-critical caveat. Novel — nobody packages .ko as .kext today.
-
Eliminating standalone hwregd — Apple-shaped device matching & autoload Convergence plan
How to retire NextBSD’s standalone devctl-socket + timer
hwregd daemon and converge toward Apple’s shape — in-kernel matching + match-notifications & busyState/waitQuiet over Mach + a launchd-activated loader (Apple keeps kextd/kernelmanagerd; the win is event-driven, not zero daemons). Umbrella over #67 (settle window), #176 (busyState), and #168 (EVFILT_MACHPORT / fold into configd). Phased roadmap from a 3-agent scope of Apple XNU/IOKit + kext_tools, the NextBSD hwregd/configd/launchd/mach.ko tree, and FreeBSD kqueue internals.
-
NextBSD graphics — virtual-GPU DRM kexts, an end-to-end CI test, and the NVIDIA/VirtualBox tracks Porting plan
The drm-kmod → Intel/AMD/Radeon kexts autoload via the in-kernel matcher, but that path is untested end-to-end — qemu emulates no real GPU. Port a virtual-GPU DRM driver (bochs first — it binds qemu’s default
-vga std as a raw IOPCIPrimaryMatch node) to unlock KMS-in-VMs and the first true match→autoload→bind CI test. Scopes bochs/vboxvideo/virtio-gpu/vmwgfx, the shared blocker (drm-kmod’s missing drm_gem_vram/shmem helpers → a drm_extra_helpers.ko), the repo split (unmodified drm-kmod fork + a nextbsd-graphics kext factory + a recipe-only NVIDIA-legacy repo), and a phased roadmap. From a 7-agent scope.
-
arm64 KMS — six routes to
/dev/dri/card0 in an ARM virtual machine Decision record
The graphics plan's “virtio-gpu and vmwgfx are larger follow-ons” framing held for amd64 — bochs and vboxvideo both ship now — but not for arm64, where neither binds anything an ARM VM presents. Two independent research sweeps found zero reports of /dev/dri in any arm64 FreeBSD VM, ever. Six routes costed with evidence: bochs on arm64 (ruled out — ArmVirtQemu ships no QemuVideoDxe, so the screen is black from power-on until kldload), a native firmware-framebuffer driver, porting Linux's simpledrm, D55012's scfb path, virtio-gpu, and vmwgfx. Also settles that the DRM core does build on aarch64 (PR #33) and documents an upstream defect found doing it: drm-kmod gates DRM_AMD_DC_FP to amd64 while leaving the references compiled, so its amdgpu.ko builds clean and cannot kldload on aarch64. Corrects two claims made earlier in the surrounding work.
-
virtio-gpu DRM — accelerated OpenGL in virtual machines Execution plan
The chosen route, and the priority work — the real driver, not a framebuffer stand-in:
/dev/dri/card0 and a render node, dynamic resolution, and hardware-accelerated OpenGL through Mesa's virgl. Serves amd64 VMs as much as arm64. Two findings make it far smaller than first costed: the LinuxKPI virtio shim needs no FreeBSD base change (FreeBSD's public virtqueue_notify() already is Linux's virtqueue_kick(), suppression check included, and virtio-gpu never calls the un-split form — so the two static helpers that stalled D32371 for five years are simply not needed), and the entire userland half already ships (graphics/mesa-dri builds the virgl Gallium driver unconditionally on every arch; libdrm installs virtgpu_drm.h). Includes an explicit §4 comparison of extending base's virtio_gpu(4) vs building on drm-kmod vs a native BSD driver on drm-kmod's core — decided on uapi fidelity, since Mesa's virgl talks an ABI we do not control. Plus the shim symbol map, the depth-1 MODULE_DEPEND constraint, a six-phase plan, and three risks that would each pass a smoke test while being wrong (including an sglist coalescing trap that would silently give the device write access to our command buffer).
-
Fallback graphics — KMS on machines with no supported GPU Scoping plan
Not an arm64 problem — a hardware-coverage one. A machine whose GPU is in none of our match tables (364 Intel ids, 308 amdgpu, 550 radeon) gets a text console and nothing else: no
card0, so no Wayland and no Xorg modesetting. Linux solved this with SimpleDRM, which binds the framebuffer the firmware already set up. The surprising part is the trigger: Linux does not gate the load on “has anything else claimed the display?” — it loads unconditionally and lets the real driver evict it, so there is never a window without KMS. Four candidate triggers costed against our IOKit matcher, whose match-form support was read directly from iokit_catalogue.c: only the vendor:device form exists, but the scan already reads pci_get_class(), already tests PCIC_DISPLAY, already carries probe_score, and already answers “does this display device have DRM bound?” via iocat_drmn_bound(). Also: this is not EFI-only — FreeBSD's efifb/vbefb/ofwfb/simplefb map one-to-one onto Linux's efidrm/vesadrm/ofdrm/simpledrm, so legacy BIOS is a front-end branch, not a second driver.
-
NextBSD graphics — porting the NVIDIA driver as per-branch co-existing kexts Porting plan
The dedicated follow-on to the graphics plan’s NVIDIA track: one
NVIDIAGraphics<NNN>.kext per FreeBSD nvidia branch (595/580/470/390/340/304), tiered by method — drm-kms (595/580, bind drmn, /dev/dri), modeset-only (470/390), core-only (340/304) — each carrying its own de-overlapped PCI match table and (595/580 only) raw GSP firmware, built from NVIDIA’s driver tarball like FreeBSD’s nvidia ports (which ship as binary pkgs). From a 9-agent research+review workflow that caught four defects: a kext wraps multiple .kos so CFBundleExecutable is the top of the MODULE_DEPEND chain per tier (not uniformly nvidia); 390 is modeset-only not core-only; 595 is the proprietary module with GSP default-off (not open-modules/GSP-mandatory); firmware is raw blobs in Resources/firmware/. The permanent wall: every branch’s core is module nvidia, so one branch resident per kernel — multi-GPU works only if a single branch covers all cards. Since validated against real source: matcher confirmed PCI-id personalities; one kext per .ko chained; nvidia-drm-612-kmod/595.84 confirmed real; the drm-kmod stack (Intel/AMD/Radeon + NVIDIA) shares one drm.ko core so going 6.12 moves all four together (gated on the 15.1 base). Step 1 (gen-nvidia-personalities) merged as nextbsd-kernel-modules#17. Shipped end-to-end: the drm-6.12 bump + the full NVIDIAGraphics595 kext (nvidia/nvidia-modeset/nvidia-drm + GSP firmware) are in nextbsd-pkg (build #18–22 + nextbsd-pkg#7).
-
NextBSD graphics — the NVIDIA userland / display layer Porting plan
The kexts autoload and
/dev/dri/card0 is up, but the screen is black — a GPU driver is two layers and only the kernel one shipped. This companion settles the userland/display layer end-to-end from five parallel research passes + on-hardware testing (RTX 3060 Ti). Confirmed dead: modesetting-on-nvidia-drm (EOPNOTSUPP on the generic dumb-buffer path + kernel panic). Recommended: package NVIDIA’s X11 userland from the same 595.84 tarball and drive it with Driver "nvidia" (talks to /dev/nvidia0 directly, never DRM/GBM). Two research surprises: (1) XLibre is not the ABI dead-end it looked like — its ABI-28 aside, it deliberately auto-loads the closed NVIDIA driver (≥570) since 25.0.0.16, and classic xorg-server-21.1.24 (ABI 25.2) is the rock-solid fallback; (2) Wayland dissolves both the X-ABI and GLX problems (compositor uses EGL/GBM/KMS, no DDX, no GLX) but moves the blocker to a FreeBSD userland GBM backend that’s Linux-centric and barely present. Also answers: userland files are a mix of version-unique libs (co-exist) + fixed-name entry points (collide) → per-branch sibling bundles + an install-time activator (Apple ships userland GPU bundles as siblings to kexts, not libs-in-kext); libGLX_nvidia.so.0 is GLX-only (2D/EGL/Vulkan/CUDA run without its code); and 340/304 can’t render X on any modern server (ABI ≤24) so the userland story covers 595/580/470/390.
-
NextBSD graphics — GPU readiness &
LoginWindow: what actually panics Fix plan
Issue #390 asked how LoginWindow should know the GPU is ready — poll ioreg? add Mach/IOKit support? The answer, from a 68-agent workflow plus two rounds of hand-verification: readiness is the wrong question, and the panic is a kernel defect nobody upstream has root-caused. For a vidmem-backed NVKMS surface nvidia-drm hands LinuxKPI the raw VRAM BAR1 physical address as a PFN; on amd64 PHYS_TO_VM_PAGE() bounds-checks against the array span, not validity, and the gap-filling init loop in vm_page.c:783 is #if defined(__i386__) — amd64 never initializes the vm_page_array entries for PCI holes, and pmap_page_array_startup() never zeroes them. So the NULL guard passes and vm_page_busy_acquire() dereferences boot-time DRAM garbage → trap 9 in _rw_wowned (which is literally the frame in drm-kmod#308). Intermittent because the garbage varies boot to boot — the one symptom no deterministic story explains. Two false trails are documented and retracted: NVKMS does not keep initializing after card0 (drm_dev_register() is the last step of a synchronous kldload, so settle delays and drmModeGetResources probes are vacuous, and IOKitWaitQuiet can’t even see nvidia-drm); and the “integer underflow” is real but inert (pfn += page_offset is compiled out on FreeBSD) — so the upstream ports patch (“Fix GPF in some configs”) cannot fix this, and as merged is still wrong on FreeBSD (it drops TTM’s + vma->vm_pgoff term). The real fix is what nvidia-drm alone omits: register_fictitious_range() over the VRAM aperture (amdgpu/i915/radeon all call it), plus hardening lkpi_vmf_insert_pfn_prot_locked(). Separately, four real ordering bugs survive — X starting before kextd has even requested the load (launchd has no ordering key), wrong-DDX selection, hybrid card0 misattribution, and no failsafe — fixed with zero new C by a synchronous kextload barrier (kldfind(2) takes kld_sx exclusive across MOD_LOAD) plus a driver-keyed terminal “no kext claims this GPU” verdict making VirtualBox/VMware/QEMU/headless a sub-second fact. Also: how Apple really does it (registerService(), IOKitWaitQuiet, LaunchEvents→com.apple.iokit.matching) and why an opaque vendor blob can never sign that contract.
-
NextBSD — NVIDIA autoload & Linux binaries: LinuxKPI vs the Linuxulator Fix plan
Why
NVIDIAGraphics595.kext won’t autoload on real hardware (RTX 3060 Ti) and pkg install claude-code aborts — both trace to NextBSD having no Linuxulator (Linux userland ABI), distinct from the LinuxKPI driver shim that’s already baked in (which is why i915/drm/wifi load fine). Verified crux: on amd64 the module name linux is the i386 module and linux64 the 64-bit ABI — and NVIDIA depends on linux (never linux64), so the two symptoms need different modules. Recommended split: disable NV_SUPPORT_LINUX_COMPAT (one line) so the NVIDIA chain autoloads like i915/drm (the compat is only for Linux processes ioctl-ing /dev/nvidia, not KMS); and separately bake linux_common+linux64 into nextbsd-kernel (mirroring the linuxkpi_video/compat_mach precedent) to run Linux binaries. Skips the hard linux32 bake. From an 8-agent workflow + 3 review lenses, grounded in freebsd releng/15.1, the 595.84 kmod source, and on-hardware diagnosis.
-
In-kernel IOKit for NextBSD — feasibility & the matching-location decision Decision doc
The single fork under the kextd milestone: does driver–device matching live in the kernel (Apple-faithful — the kernel decides, kextd just loads) or in userland (faster, but a divergence)? Deep 2-agent review of FreeBSD newbus internals + Apple’s XNU IOKit / IOCatalogue / kextd, with per-option effort & risk and the “do I edit the kernel to add device IDs?” clarification (no — IDs live in kext personalities). Companion to the hwregd convergence plan; for joint review before any kextd code.
-
Restoring MIG as NextBSD’s kernel RPC substrate Programme plan · v2
Scoped as one stubbed function; it is seven subsystems and 27,455 lines of generated C nobody can regenerate.
mach_port, task, vm_map, host_priv, mach_host, mach_vm and clock were all generated in 2015 by a MIG we no longer have and checked in as source with no .defs beside them — so userland papered over the gap with ~18 libmach entry points that return KERN_SUCCESS having done nothing. mach_port is now reconstructed and gate-verified: dispatch table 36/36 identical to the frozen server including the skip; tombstone at 3225, wire structs differing only by the expected msgh_body, and generated client vs generated server 35/35 exact — MIG’s guarantee restored by construction. The syscall band settles the MIG-vs-trap question arithmetically: patch 0001 widened FreeBSD’s lkmnosys band from 10 slots to 58 because “slots (210-219) are exhausted by the Mach trap set”, and ~156 routines would need ~156 slots — while MIG costs zero, since everything rides the single already-wired mach_msg_trap. So the widening was a symptom, not a fix, and converting the 3 traps with no Apple counterpart actually frees slots. Settles ownership from Apple’s own shipping behaviour — XNU owns osfmk/mach/*.defs and installs 28 of them into the SDK, so the kernel owns the seven kernel-interface descriptions and userland’s existing 24 (userland↔userland, no kernel side) stay put; headers may keep diverging between repos, wire descriptions may not. Also flags the finding that reorders everything: green CI currently proves almost nothing (#81 — a boot with zero daemons reports success), so that lands first or nothing below it is trustworthy. Eight phases, per-ticket disposition, and three named risks including that making mach_port_mod_refs real is a behavioural change that will start actually dropping references.
-
The
mach_port MIG client — Apple’s layering, measured Finding · plan
Kernel MIG works on NextBSD, and has all along — the previous plan on this page recommended the opposite and was withdrawn. Two hand-built messages sent to mach_task_self() on real hardware both returned correct data: mach_port_get_attributes (id 3217) came back with qlimit=5, which is MACH_PORT_QLIMIT_DEFAULT and therefore real kernel state rather than a zeroed buffer; and mach_port_get_set_status (id 3214) — the exact call libmach fakes — returned a complex reply carrying out-of-line memory with the precise port that had just been inserted into the set. So no kernel change is required for correctness, and the stale mach_test.c comment warning that a cold round-trip “panics with strange destination rights” describes a different path. The defect is entirely userland: src/libmach/mach_traps.c holds 14 real syscall traps and ~18 entry points that fabricate KERN_SUCCESS without doing anything — among them mach_port_get_set_status, which is why on-demand Mach launch is dead, and mach_port_mod_refs, which silently no-ops reference counting. Settles the “MIG or traps?” question from Apple’s own sources: Apple uses neither exclusively — MIG is the universal floor and traps are an accelerator that falls back to it (if (rv == MACH_SEND_INVALID_DEST) rv = _kernelrpc_mach_port_allocate(…)), routines returning arrays have no trap at all, and Apple even ships our exact case: /* REMOVE once XBS kernel has new trap */ maps a missing trap onto MACH_SEND_INVALID_DEST so control falls through to MIG. Checked all 12 NextBSD traps against the 130-entry mach_trap_table: 9 match Apple and stay, 3 (host_set_special_port, task_get_special_port, task_set_special_port) are inventions to convert. Six phases, with a hard verification gate — regenerate the server from the reconstructed .defs and diff the dispatch table routine-by-routine, because a single misplaced skip; silently renumbers every msgh_id after it and the failure mode is calling the wrong kernel function, not an error. Flags the one genuine behavioural risk: making mach_port_mod_refs real will start actually dropping references, so any latent over-release in the tree stops being harmless.
-
MIG explained — Mach RPC from first principles Reference
What MIG is, why it exists, and how to read the code it generates — built entirely on our own tree as the worked example. Covers ports, rights and dispositions; the wire layout of a message; the
.defs IDL; what lands in *User.c versus *Server.c; how msgh_id is just subsystem-base + index (and why skip; tombstones are load-bearing); the NDR byte-order record; and the full client→kernel→server round trip step by step. The section that earns its keep is simple vs complex: setting MACH_MSGH_BITS_COMPLEX inserts a 4-byte mach_msg_body_t after the header and shifts every subsequent offset, so a receiver handed the wrong shape does not see a slightly different message — it sees garbage everywhere. That is exactly what bit us: the frozen 2015 kernel stubs demand COMPLEX with a descriptor count of zero, while the in-tree migcom emits a simple request, because IS_KERN_PROC_DATA ranks MACH_MSG_TYPE_PORT_NAME (15) below the port-type floor (16). Includes a candid post-mortem of this page’s own earlier conclusion — a true premise (we cannot generate a client matching the frozen server) carried one step too far into a false one (therefore MIG is unusable), when the frozen server could simply be regenerated as well.
-
Native
EVFILT_MACHPORT migration & pipe-bridge removal — #168 plan Plan ready
Move all Mach-event delivery onto the kernel’s native EVFILT_MACHPORT filter (slot -16) and delete the module-era pipe-bridge (task #39 Path B: register_event_bell + self-pipe + EVFILT_READ) — the workaround from when a .ko couldn’t add a kqueue filter slot, now obsolete since patches/0003 reserves the slot. Synthesized from a 7-agent survey of nextbsd-kernel + nextbsd + freebsd-src. Root-causes the PR #250 boot panic (a use-after-free on the port set in filt_machportattach — drops the pset lock with no reference, then knlist_add touches freed/reallocated memory at 0x70 under concurrent move_member, amplified by uninitialized worker-thread Mach state, #148) and gives the exact kernel fix. Fixes task #41 (unreliable DISPATCH_SOURCE_TYPE_MACH_RECV) at the root, retiring the pthread+mach_msg polling loops in sc_link_watch/IOKitNotify/SCNotify. 6-stage PR-gated rollout + a new concurrency stress test that reproduces the panic.
-
Mach IPC — what has been eliminated Open, narrowed
The negative-results page for the Mach message-loss bug, written so the ground already settled by measurement is not re-tread. Six mechanisms measured clean with kernel counters — lost wakeups on port sets and on bare ports, silent drops to dead and circular destinations, the handoff posted/received balance, and delivery accounting that reconciles. Seven hypotheses killed, including notifyd causality (which held on one userland and not on another), the EVFILT_MACHPORT bridge, launchd’s upfront receive rights, and the port-name decay model whose signature turned out to be an artifact of a bad userland. Re-characterises the bug as ~1 RPC in 700 hanging, any RPC, not a logging fault — which is why five path-specific fixes each corrected a real defect and moved the measured rate by nothing. Also records the method lesson: every instrument that encoded an assumption went silent, every instrument that just counted was right. Filed under Launchd → Mach.
-
Dropping libxpc and libdispatch — scoping Scoping · surveys partial
Can NextBSD ship pre-XPC, open-source Apple components instead of maintaining reimplementations? Scoping, with the measured parts kept separate from the assumed ones. libxpc was never open-sourced by Apple — there is no upstream to revert to at any vintage, so it is a reimplementation by necessity and the strongest deletion candidate. The page also records the premise it had wrong: “pre-XPC” does not mean 10.6. XPC adoption lagged its 10.7 ship date by years and did so per-component, so “revert to 10.6” is a category error for much of the tree — corrected with a per-component adoption timeline. launchd is the sharpest case: it needs deletion, not reverting. Adds a vintage map showing the tree spans ~10.9 to 2026-original code, and the archetype failure that mixed vintage already caused — a macOS 15/26-era
mig emitting MACH_RCV_TRAILER_AV against this tree’s ~10.9 message.h, which returned MIG_TRAILER_ERROR (-309) from every bootstrap_look_up. Survey-verified so far: the configd / networking / libmach group uses no XPC at all (the only hits are two comments), and libmach bridges for libdispatch rather than depending on it — so dropping libdispatch leaves that file unused, not broken. Splits into two plans rather than one, pairs with epic #145, and files six documentation-drift items as #154. Filed under Launchd → Mach.
-
The inverted run loop — SCNotify, CFMachPort and the teardown crash Root cause found
Why
scrltest segfaults, traced to a real core dump rather than inspection. NextBSD builds SCDynamicStoreCreateRunLoopSource on top of libdispatch; Apple builds it on CFMachPort and has since before libdispatch existed. Dated precisely from Apple’s own sources: SCDNotifierInformViaCallback.c goes from dispatch_ = 0 in configd-212.2 (10.5) to 23 in configd-289 (10.6) — but CFMachPort references go up (17→22) and CFRunLoopSource stays flat at 7, so Apple added dispatch as a parallel mode and never replaced the run-loop path. The inversion predicts which test fails, and the backtrace confirms it: the fault is in _dispatch_kevent_merge on libdispatch’s manager thread during EVFILT_MACHPORT knote teardown, with _dispatch_bug_kevent_client on a sibling stack proving the kernel returned an error from the unregister — not SCNotify, not CoreFoundation, and two strong inspection-based hypotheses killed. Reproduces 13 times in 30 bare but passes under lldb. Also settles CFMachPort’s provenance (upstream shipped it, renamed it in 2024, dropped it before our vendor SHA — we never removed it) and why recovering it would not help: Apple’s CFMachPort is itself built on libdispatch. Records how boot_soft: true reported five segfaulting runs as green, and the innocent PR that got reverted as a result. Root cause found by two surveys converging from opposite directions — a bisect over all 200 CI boot logs (81/81 passing until 2026-08-30T02:02Z, then branch-correlated failure) and an independent source reading, which name the same code: userland #106 turned reg_release_pset from a no-op into a real port-set release, and libmach/dispatch_kevent.c then leaks the resulting EV_ONESHOT event — carrying a udata it has already free()d — straight through kevent_qos() into libdispatch, which casts it to a unote and dereferences it. The fix is under 50 lines in one file, with no kernel or libdispatch change. Also dissolves the GNUstep question (libs-corebase is not built or shipped; gnustep-base uses poll(2) and links no CoreFoundation, so the two never meet) and closes the CFMachPort option for a decisive reason: mach_port_request_notification is a no-op here, so a CFMachPort could silently never invalidate. Filed under Launchd → Mach.
-
Mach namespace separation — the plan Plan ready
The fix for the IPC reliability defect, scoped into tickets. Mach port names are file descriptors in this port and the generation counter is compiled out, so a recycled name is undetectably stale — the one deviation from XNU that causes the message loss. Undoing it is two stages: first make
EVFILT_MACHPORT resolve through the task’s ipc_space instead of the fd table (it is f_isfd = 1 today, so kqueue fgets the port name), then make names table indices with per-slot generations. Includes full file:line change maps, the ordering trap that the two name leaks must not be fixed first (they are what make the failure transient rather than permanent), fork-inheritance and implicit-fileport fallout, and an honest statement that the fix is expected but not demonstrated — four predicted fixes during the investigation each addressed a real defect and none moved the number. Umbrella nextbsd-kernel#145. Filed under Launchd → Mach.
-
Mach IPC reliability — why messages are lost Root cause found
Mach messages are dropped non-deterministically: a client’s send succeeds, the server never receives it, and the client blocks forever (confirmed with a 90-second timeout — a permanent wedge, not slowness). Measured on arm64 hardware: 40–60% of sends wedge from a fresh boot, falling to zero after ~70–80 cumulative sends, unaffected by elapsed time, and reset when either daemon restarts. Root cause: port names carry no generation counter (
MACH_PORT_GEN(name) is (0)) — they are raw fds recycled lowest-first from 16, so a name freed by one RPC is handed back bit-identical to the next, and ipc_object_copyin() wraps any non-Mach fd into a port with nobody receiving on it. Two name leaks push the fd band upward, which is what makes the failure decay rather than persist — so they must not be fixed before the generation counter. Also documents a second, independent lost wakeup in ipc_mqueue_deliver() (nextbsd-kernel #144) and eight hypotheses ruled out with evidence. Filed under Launchd → Mach.
-
mDNS Network browse over Mach — the empty Network-list fix Plan ready
A concrete consumer of the
EVFILT_MACHPORT doctrine above. The Workspace File Viewer’s Network sidebar is empty on NextBSD: mDNSResponder advertises its own .local name (peers see ThinkPad-T460s) but the dns_sd client protocol was never finished over Mach — mach_bridge.c only claims com.apple.mDNSResponder and discards the receive right, and libdns_sd ships as the AF_UNIX stub bound to a /var/run/mDNSResponder socket the nobody daemon can never create. Fix (Mach, not sockets): a dns_sd.defs MIG IDL + real Mach server wired through EVFILT_MACHPORT, plus a Mach client transport in libdns_sd. File Viewer and NetworkBrowser.app share the same NSNetServiceBrowser backend, so the UI needs zero changes. Primary repo: nextbsd-redux/nextbsd (src/mDNSResponder/); Sharing-pane launchd shim is a separate follow-on in gershwin-desktop/gershwin-components. Filed under Launchd → Mach.
-
mach.ko → kernel built-in (
options COMPAT_MACH) Scoping
Compile the out-of-tree Mach module into the NEXTBSD kernel instead of loading it as a .ko — the answer to “how do we load mach.ko once loader.conf is gone?” and the foundational step of #180 (eliminate loader.conf). Mostly build-system wiring: the MIG server stubs are pre-committed (no codegen in the kernel build) and the syscalls already register via a SYSINIT/kern_syscall_register (no syscalls.master regen). Source migrates nextbsd/src/mach_kmod → nextbsd-kernel sys/compat/mach/, wired by a small sys/conf/{options,files} patch; the fork is never touched. Gated by the boot smoke test.
-
Prove kexts work — minimal kextload + converter + PR CI Scoping
Walking-skeleton proof that a FreeBSD
.ko wrapped as a .kext loads via an Apple-shaped tool, gated by CI. Cheap because nextbsd already ships CFBundle (opens the bundle + Info.plist) and libIOKit — so the proof needs no OSKext: a minimal kextload/kextstat/kextunload trio over kld (Phase 1, in nextbsd) + a converter and PR CI that kextloads one leaf module in a booted VM (Phase 2, in nextbsd-kernel-modules). The full kext_tools/OSKext port is deferred but ticketed so it isn’t lost. Approach (a) chosen. Proven & merged
-
Faithful kext_tools / OSKext port Plan (#182)
Port Apple’s real
OSKext engine (bundle discovery, dependency-graph resolution, validation, personalities) onto NextBSD with a kld backend — the faithful follow-on to the PoC, gating bulk conversion (#179) and kextd (#177). Code-grounded scope: OSKext.c ≈ 21k LOC but ~75% ports unchanged (CFBundle parsing, dep-graph math, validation); only ~25% re-backs from XNU (kext_request→kldload, IOCatalogueSendData→hwregd, KXLD/mkext deleted). Pre-SIP base avoids codesign/kernelcache. Multi-week, phased; Phase 1 (dependency resolution) is the first shippable milestone.
-
kld → kext: naming + categorization Plan (#179)
Groundwork for the bulk
.ko→.kext conversion: a complete catalog of all 474 FreeBSD kernel modules in the tree — what each is, its category (driver / filesystem / network / crypto / KPI / …), and a proposed Apple-style org.nextbsd.* bundle id + .kext name. Built from a read-only review of freebsd-src/sys/modules by parallel agents. Surfaces the open decisions before #179: naming style, kext-vs-built-into-kernel split, sub-component/firmware handling, exclusions.
-
Raw module catalog — all 474 modules
The unedited, first-pass per-module table behind the naming plan: one row per
sys/modules/ entry — ko name, dir, category, purpose, proposed bundle id + .kext name, notes. Kept verbatim as the groundwork record the polished plan was distilled from.