freebsd-launchd-mach — a pure-port track that forks ravynOS's Mach + launchd stackA revised plan, after surveying NextBSD and ravynOS as prior art. The primary goal is dragging FreeBSD into the 21st century — better init, better IPC, better service management, better network configuration, better logging, better notification. Apple-source porting is the chosen technique, not the destination. GNUstep stays for the application / framework layer (Foundation NSXxx, AppKit, libobjc2) and lives in the gershwin desktop overlay — not on this ISO. Apple source covers the lower system-services layer (Mach, launchd, configd, notifyd, asl, libdispatch, libxpc, plus pure-C CoreFoundation via swift-corelibs-foundation — see the launchctl-corefoundation-spike for the CF choice and the libcorefoundation-icu-audit for the no-ICU-on-ISO follow-on decision). The entire userland remains FreeBSD ELF.
The earlier freebsd-mach-kmod-plan proposed building a minimal mach.ko from scratch. This plan supersedes that one with a much shorter path: the legacy FreeBSD-based ravynOS tree contains a working, modular, OSF/CMU-licensed Mach implementation derived from NextBSD, packaged via bsd.kmod.mk, with launchd, libxpc, libdispatch, and a Mach-native bootstrap server already wired in. We snapshot it — ravynOS itself has pivoted away from FreeBSD to port XNU directly, so we are inheritors, not collaborators — and out-of-tree-ify it as a new repo, freebsd-launchd-mach, reusing the existing freebsd-launchd CI / ISO / release scaffolding.
mach.ko, with launchd / configd / IPConfiguration call-site audits.lkmnosys cap audit for the kmod's syscall surface (Phase C concern).mach.kofreebsd-launchdVerdict: GO, staged. Committed work right now is Phases A and B only: get a vanilla FreeBSD ISO booting with stock init as PID 1 from the new repo (1 week), then port ravynOS's mach.ko so it kldloads at boot and survives a userland round-trip test (3 weeks). Hard stop after Phase B. Everything else — Apple's launchd, configd, IPConfiguration as PID-1-replacing userland — is post-checkpoint scope, evaluated against what Phase B actually reveals. Down from the v1 plan's 24–32 weeks because we are no longer implementing Mach; we are extracting an already-implemented Mach kernel module from a project that has been quietly maintaining it since 2014.
mach.ko today, on FreeBSD 15.0-CURRENT (__FreeBSD_version 1500066), as a loadable kernel module under sys/modules/mach/Makefile using bsd.kmod.mk. 48 KLoC across sys/compat/mach/. Origin: NextBSD's 2014–15 port of XNU's osfmk/ipc/, all OSF/CMU-licensed (no APSL).liblaunch with libbootstrap.c + libvproc.c, a libxpc that uses <mach/mach.h> + <servers/bootstrap.h>, and libdispatch with a Mach-aware DISPATCH_SOURCE_TYPE_MACH_RECV. 325 commits since January 2025 — actively maintained.configd, SystemConfiguration, SCDynamicStore in their tree return nothing. That gap is exactly what freebsd-launchd/configd/ already imported (configd-963.270.3, currently a Phase-1 skeleton).freebsd-launchd as the AF_UNIX-only track (the original framing in the original plan). Create a new repo freebsd-launchd-mach for the pure-port track. The two repos share CI infrastructure, the live ISO build pipeline (build.sh, vmactions/freebsd-vm@v1), kmodloader, ramdisk staging, overlays. The mach repo adds: extracted ravynOS Mach module, ravynOS userland stack, configd integration.freebsd-launchd-mach is the staging ground; if it works out, it rolls up into the long-term project.mach.ko per FreeBSD major against an unmodified kernel and uploads it as a tarball alongside the ISO, exactly as proposed in the v1 plan.sys/compat/mach/ already builds against stable FreeBSD KPIs (malloc(9), mtx(9), kqueue(9) custom filter via EVFILT_MACHPORT, syscall_helper_register); the per-major build matrix idea from v1 is unchanged.The whole posture flips. v1 was risk-managed scope discipline: build the smallest Mach we can get away with, fight scope creep, hope it's enough. v2 is integration discipline: take a working Mach and a working launchd that's already validated end-to-end on FreeBSD, lift it cleanly into an out-of-tree project, add the configd piece. The risks are different (upstream tracking, divergence, in-place adaptation) but the absolute risk is lower because the load-bearing parts already work in production.
Concrete findings from direct source inspection. Both repos are now cloned at /Users/jmaloney/Documents/launchd/{nextbsd,ravynos}.
| Aspect | Finding |
|---|---|
| Repo shape | Modified FreeBSD source tree with sys/compat/mach/ added in-tree, but packaged as a loadable module via sys/modules/mach/Makefile. Loadable with mach_load="YES". |
| Mach implementation size | ~52 KLoC across sys/compat/mach/: 18 IPC files (16.7 KLoC), kernel/server code (35.3 KLoC including auto-gen MIG), kern (3.6 KLoC). |
| What's PRESENT | mach_msg with port descriptors, OOL memory descriptors, NO_SENDERS / DEAD_NAME / PORT_DESTROYED notifications, mach_port_* family, bootstrap client lib, vm_map_server.c region info / map read/write, EVFILT_MACHPORT kqueue filter. |
| What's STUBBED | Tasks/threads/exception ports (basic hooks only), memory object creation, vouchers, audit-token-on-setuid. |
| Userland | Full launchd at /sbin/launchd (332 KLoC core.c), libmach, libdispatch, libxpc — all forked from Apple ~2015. |
| FreeBSD base | __FreeBSD_version 1200001 (12.0-CURRENT, 2016 era). |
| Activity | Last commit October 2019. Six years of bitrot. FreeBSD 12 is EOL. |
| License | OSF License (1991–1998), CMU License (similar permissive), BSD 2-clause for FreeBSD wrappers. Not APSL. Clean to relicense as BSD. |
| Build status today | Will not build against FreeBSD-CURRENT without porting work. Estimated 6–8 weeks of KPI rework if used directly. |
| Aspect | Finding |
|---|---|
| Repo shape | Soft fork of FreeBSD 15.0-CURRENT (__FreeBSD_version 1500066) with macOS-shaped overlay directories (Frameworks/, CoreServices/, Library/{LaunchDaemons,LaunchAgents}/) plus in-tree Mach + userland. |
| Mach implementation | Direct descendant of NextBSD's sys/compat/mach/, copyright headers throughout: "Copyright (c) 2014-2015, Matthew Macy <mmacy@nextbsd.org>". 27 source files, plus MIG .defs under sys/compat/mach/defs/, plus sys/sys/mach/ headers (78 files) and include/mach/ userspace headers (63 files). |
| Module Makefile | sys/modules/mach/Makefile already uses bsd.kmod.mk, declares KMOD=mach, lists 30+ source files including auto-generated MIG server stubs (host_priv_server.c, mach_host_server.c, mach_port_server.c, mach_vm_server.c, task_server.c, vm_map_server.c, clock_server.c). Uses -DCOMPAT_MACH compile flag. |
| Userland stack | /sbin/launchd/ (Apple Apache 2.0 launchd with core.c, ipc.c, internal.defs, mach_exc.defs, job.defs), /lib/liblaunch/ (with libbootstrap.c, liblaunch.c, libvproc.c, bootstrap.h, vproc.h, vproc_priv.h), /lib/libxpc/ (Mach + nvlist-based, BSD 2-clause from iXsystems), /lib/libdispatch/ (Apple Apache 2.0). |
| Plist format | JSON, in /Library/LaunchDaemons/*.json — not Apple's XML. Easier to author and grep, but a divergence from upstream Apple. |
| FreeBSD base | 15.0-CURRENT, tracking HEAD. |
| Activity | Very active. 325 commits since January 2025. Active CI (Cirrus). Discord + Matrix communities. |
| configd / SystemConfiguration | Absent. Not on the roadmap. Their network configuration falls back to FreeBSD ifconfig + rc.d. |
The big realization: ravynOS spent the last ten years doing the exact thing v1 of this plan was scoping out. They forked NextBSD's Mach, kept it building against modern FreeBSD, integrated launchd + libxpc + libdispatch on top of it, and ship a working desktop OS. What v1 priced at 24–32 weeks of greenfield work, ravynOS has already done. We're not deciding "implement Mach"; we're deciding "extract Mach from ravynOS into an out-of-tree project that runs on stock FreeBSD."
The existing freebsd-launchd repo took the AF_UNIX-everywhere posture deliberately, and the service-ordering doc's recommendation favours that path on effort grounds. The pure-port track is a different bet, with a different risk profile, and it should not destabilize what's already working. Hence two repos:
freebsd-launchd — AF_UNIX track ContinueWatchPaths, Sockets, KeepAlive dict subkeys, LaunchEvents in core.m per the service-ordering doc.freebsd-launchd-mach — pure-port track Newsys/compat/mach/, packaged out-of-tree.freebsd-launchd's existing 963.270.3 import.freebsd-launchd's build.sh, ramdisk, kmodloader, overlays, CI workflows.Critical: the two repos are not competing roadmaps; they are two different products with different audiences. The freebsd-launchd roadmap continues unchanged. The freebsd-launchd-mach repo is additive.
Three sources, each with a tight scope. ravynOS is consulted for the kernel module (and one userland library Apple never open-sourced); everything else userland comes from Apple's actual open-source releases on opensource.apple.com; the existing freebsd-launchd repo provides CI / build / scaffolding only.
| Source path in ravynOS | Destination in freebsd-launchd-mach | Phase |
|---|---|---|
sys/compat/mach/ (27 .c files + defs/ + ipc/ + kern/) |
mach-kmod/src/ |
B (committed) |
sys/sys/mach/ (78 kernel headers) |
mach-kmod/include/sys/mach/ |
B (committed) |
sys/modules/mach/Makefile |
mach-kmod/Makefile |
B (committed) |
include/mach/ (63 userspace headers, already FreeBSD-adapted) |
libmach/include/mach/ |
C (post-checkpoint) |
lib/libxpc/ (iXsystems BSD-2 XPC; Mach-aware) |
libxpc/ |
C (post-checkpoint) |
Why libxpc has to come from ravynOS: Apple never open-sourced libxpc. It's part of the closed libSystem on macOS. The iXsystems implementation in ravynOS's lib/libxpc/ is the only realistic source for a working Mach-aware XPC library on FreeBSD. We document this as a divergence-from-Apple even though we otherwise prefer Apple-direct sources.
| Apple source | Destination | Phase |
|---|---|---|
| launchd (latest open-source release, traditionally launchd-842 or the corresponding Apache 2.0 drop) | launchd/ |
D (post-checkpoint) |
| liblaunch + libbootstrap + libvproc (from the launchd source release) | liblaunch/ |
D (post-checkpoint) |
configd (currently 963.270.3, already imported by freebsd-launchd/configd/; lift that or refresh from Apple) |
configd/ |
E (post-checkpoint) |
| IPConfiguration (DHCPv4 + DHCPv6 + IPv4LL + RA, configd plugin) | configd/Plugins/IPConfiguration/ |
F (post-checkpoint, scope expansion) |
| libdispatch (Apache 2.0; gershwin-on-freebsd already uses Apple's libdispatch — reuse gershwin's build or lift fresh) | libdispatch/ (or via gershwin install path) |
C (post-checkpoint) |
Why Apple direct, not ravynOS, for these: ravynOS's launchd has diverged (JSON plists, ravynOS-specific patches). We want Apple's canonical implementation with Apple's plist format (XML) so the implementation behavior matches Apple's documented behavior, no fork drift to track.
freebsd-launchd repo (scaffolding only)Asset in freebsd-launchd | Destination | Phase |
|---|---|---|
build.sh (chroot livecd staging) |
build.sh |
A (committed) |
.github/workflows/ (vmactions/freebsd-vm@v1) |
.github/workflows/ |
A (committed) |
kmodloader/ |
kmodloader/ |
A (committed) |
boot/, ramdisk/, overlays/ |
same | A (committed) |
tests/, scripts/ |
same | A (committed) |
configd/src/ (existing 963.270.3 Apple import) |
configd/ or refresh from Apple |
E (post-checkpoint) |
The reduced launchd at freebsd-launchd/src/src/ is not reused — reference only. Apple's full launchd replaces it at Phase D.
mach.koravynOS's module Makefile (verbatim from sys/modules/mach/Makefile):
SYSDIR?=${.CURDIR}/../..
.PATH: ${SYSDIR}/compat/mach
.PATH: ${SYSDIR}/compat/mach/ipc
.PATH: ${SYSDIR}/compat/mach/kern
KMOD= mach
DEBUG_FLAGS+= -g -O0
SRCS= bus_if.h device_if.h vnode_if.h opt_compat_mach.h opt_ntp.h opt_capsicum.h
# MIG autogenerated:
SRCS+= host_priv_server.c mach_host_server.c mach_port_server.c mach_vm_server.c
SRCS+= task_server.c vm_map_server.c clock_server.c
# compat/mach
SRCS+= proc_info.c mach_clock.c mach_convert.c mach_host.c mach_host_priv.c
SRCS+= mach_misc.c mach_module.c mach_processor.c mach_semaphore.c
SRCS+= mach_task.c mach_thread.c mach_traps.c mach_vm.c
# compat/mach/ipc
SRCS+= ipc_entry.c ipc_hash.c ipc_init.c ipc_kmsg.c ipc_kobject.c ipc_mqueue.c ipc_notify.c
SRCS+= ipc_object.c ipc_port.c ipc_pset.c ipc_right.c ipc_space.c ipc_table.c ipc_thread.c
SRCS+= mach_debug.c mach_msg.c mach_port.c
# compat/mach/kern
SRCS+= ipc_tt.c ipc_host.c task.c thread_pool.c
.include <bsd.kmod.mk>
CFLAGS+= -I${SYSDIR}/../include/apple -DCOMPAT_MACH
This builds today inside the ravynOS tree. To make it build out-of-tree against a stock FreeBSD source tree (or, ideally, against just the kernel headers in /usr/src/sys/):
SYSDIR assumption. Replace SYSDIR?=${.CURDIR}/../.. with SYSDIR?=/usr/src/sys. Standard out-of-tree pattern.compat/mach/*.c and compat/mach/ipc/*.c live under mach-kmod/src/; .PATH points to ${.CURDIR}/src, not ${SYSDIR}/compat/mach.sys/sys/mach/* goes to mach-kmod/include/sys/mach/; the Makefile gains CFLAGS+= -I${.CURDIR}/include ahead of ${SYSDIR}/sys.*_server.c files. We either commit them generated (simplest), or run mig as a build step (cleaner; needs the open-source mig from Darling or pre-shipped binary).opt_compat_mach.h, opt_ntp.h, opt_capsicum.h — these come from the kernel config tree. Out-of-tree builds usually ship empty stubs of these. We do the same.vmactions/freebsd-vm@v1 action gives us a clean kernel. Build, kldload, run a userland test that does mach_port_allocate + mach_msg(SEND|RCV) round-trip, kldunload. If the round-trip works, the module works.Estimated effort for steps 1–6: 2–4 weeks, dominated by chasing build errors and debugging the inevitable opt_*.h / KPI mismatches between ravynOS's CURRENT-snapshot and stock CURRENT-of-the-day.
The earlier doc's central question was "how do we make configd run." The answer under v2 collapses neatly:
freebsd-launchd's configd/src/ tree already contains configd-963.270.3 from Apple, imported as-is.libmach, liblaunch, libxpc — which the ravynOS extraction provides.com.apple.SystemConfiguration.configd name registers via bootstrap_check_in, exactly as on Apple. scutil (in the configd tree) does bootstrap_look_up and gets a send right. Round-trip works.mach_msg, mach_port_allocate/deallocate/mod_refs/insert_right/request_notification/construct/destruct, vm_allocate/deallocate, mach_task_self, bootstrap_check_in/look_up. All of these are present in ravynOS's mach.ko.The remaining configd work is FreeBSD-specific, not Mach-specific:
PF_ROUTE events on FreeBSD instead of macOS's PF_SYSTEM. ~1 KLoC of plugin work, no Mach involvement.#ifdef __APPLE__ cleanup.The configd integration milestone (Phase D in the new milestone list, Section 9) becomes "compile configd against the extracted libmach / liblaunch / libxpc, write a JSON or XML plist that registers it with launchd, run scutil --get HostName end-to-end."
freebsd-launchdThe new repo should be productive on day one because the build/release plumbing already exists. Concrete reuse:
Asset in freebsd-launchd | How freebsd-launchd-mach uses it |
|---|---|
build.sh (chroot livecd staging, 320+ lines) |
Copy verbatim, modify the "stage userland" steps to invoke make-launchd-mach.sh (which builds the ravynOS-derived launchd), make-libmach.sh, make-libxpc.sh, make-libdispatch.sh, make-mach-kmod.sh, make-configd.sh. |
.github/workflows/ (vmactions/freebsd-vm@v1 + continuous release) |
Copy verbatim. Different repo, different release tag, different artifact naming. |
kmodloader/ |
Lift unchanged. Independent of Mach posture. |
boot/ (loader.conf, BTX, ramdisk staging) |
Lift; add mach_load="YES" to loader.conf so mach.ko preloads before init. |
overlays/ |
Lift the structure; add overlay entries for the new userland binaries. |
tests/ + scripts/ |
Lift the boot-test + smoke-test runners. |
configd/ (Apple configd-963.270.3 import + Phase-1 NCDaemon skeleton) |
Lift the import; replace the Phase-1 skeleton with the real Apple configd build, now possible because libmach exists. |
freebsd-launchd-machfreebsd-launchd-mach/
├── README.md
├── LICENSE # BSD-2-Clause + per-file Apache 2.0 / OSF / CMU
├── NOTICE # provenance: ravynOS / NextBSD / Apple
├── build.sh # adapted from freebsd-launchd
├── boot/ # lifted from freebsd-launchd, mach_load="YES"
├── ramdisk/ # lifted
├── overlays/ # lifted, augmented
├── kmodloader/ # lifted unchanged
├── scripts/, tests/ # lifted
│
├── mach-kmod/ # NEW: ravynOS sys/compat/mach + sys/sys/mach
│ ├── Makefile # bsd.kmod.mk, out-of-tree SYSDIR
│ ├── src/ # the .c files
│ ├── include/sys/mach/ # kernel headers
│ └── defs/ # MIG .defs
│
├── libmach/ # NEW: ravynOS include/mach + thin .c wrappers
├── liblaunch/ # NEW: ravynOS lib/liblaunch
├── libxpc/ # NEW: ravynOS lib/libxpc (BSD-2 from iXsystems)
├── libdispatch/ # NEW: ravynOS lib/libdispatch (Apache 2.0)
│
├── launchd/ # NEW: ravynOS sbin/launchd (Apache 2.0, real Apple)
├── configd/ # FROM freebsd-launchd: Apple configd-963.270.3
│
├── plists/ # XML or JSON LaunchDaemons + LaunchAgents
├── make-mach-kmod.sh # build wrapper; per-major artifact upload
├── make-libmach.sh
├── make-liblaunch.sh
├── make-libxpc.sh
├── make-libdispatch.sh
├── make-launchd-mach.sh
└── make-configd.sh # adapted from freebsd-launchd
The license worry from the v1 plan was XNU's APSL. The good news from the surveys: the parts we want are not APSL.
| Component | License | BSD-compatible? |
|---|---|---|
ravynOS sys/compat/mach/ (lifted from NextBSD, ultimately from MkLinux/CMU) | OSF License (1991–1998), CMU License | Yes — permissive, attribution-only |
ravynOS userspace Mach wrappers (mach_module.c, mach_traps.c) | BSD 2-clause | Yes |
Apple launchd (sbin/launchd/) | Apache 2.0 | Yes — same as Apple's launchd; preserve per-file headers |
| Apple liblaunch / libdispatch | Apache 2.0 | Yes |
iXsystems libxpc (lib/libxpc/) | BSD 2-clause | Yes |
| Apple configd | APSL 2.0 | Yes (with conditions) — FSF and OSI consider APSL 2.0 free; same posture as freebsd-launchd's existing import |
The repo's overall license is BSD-2-Clause, with per-file headers preserved exactly as in their source repos, exactly mirroring freebsd-launchd's existing NOTICE policy. No new license complications relative to what freebsd-launchd already navigated.
Committed scope is Phases A and B only. Total: 4 weeks. After that, a hard stop — we look at what Phase B actually revealed and decide what's worth doing next. Everything beyond Phase B is post-checkpoint scope, sketched but not committed.
freebsd-launchd-mach repo with the layout in Section 7.1.build.sh + boot scaffolding from freebsd-launchd. Build a vanilla FreeBSD ISO from the new repo's pipeline.vmactions/freebsd-vm@v1; assert stock FreeBSD init brings up multi-user mode normally.Empirical result, captured 2026-05-11. Build gate fully
met (CI green at freebsd-launchd-mach commit
daebc30) via ~400 lines of out-of-tree compat shim across
8–9 kernel-side delta categories. Runtime gate also met
after two additional one-line fixes discovered by incremental bisect
on a stock FreeBSD-15.0-RELEASE-p8 VM:
kern/ipc_host.c's SYSINIT moved from
SI_SUB_CPU to SI_SUB_INTRINSIC so it runs
after ipc_bootstrap_sysinit sets up the IPC zones
(otherwise uma_zalloc panics on uninitialized zones).mach_module.c's "cold-only" load check removed.
That check rejected post-boot kldload by returning
EINVAL, which left the SYSINIT-registered eventhandlers
dangling after the linker rolled back the failed load — the
next process exit then page-faulted in those handlers. Letting the
modevent succeed pairs the handlers with a properly-loaded module and
removes the dangling-handler scenario.Both fixes are kmod-side (no kernel patches), one line each.
mach.ko now kldloads cleanly and the system survives
fork/exec/exit stress traffic with all 6 eventhandlers active. Apple
Mach syscalls are not yet wired into the sysent table — that's
a Phase C concern (the compat shim sets all SYS_<name>
to NO_SYSCALL for compile-time stub generation, which
doubles as the helper-register loop sentinel and prevents actual
registration). The mach-kmod-syscall-slots
spike audits the 10-slot lkmnosys cap and lays out how
the Mach syscall surface fits within it.
Full empirical writeup — the 9 shim categories, the bisect
matrix, and what remains for Phase C — lives in the repo at
src/mach_kmod/PHASE_B_FINDINGS.md.
Verified from a clean state: reboot → rm -rf the
local repo and /boot/kernel/mach.ko → git
clone upstream → make →
cp → kldload mach. Works first time.
A 200-concurrent-fork stress test (each fork firing our six
eventhandlers) runs through with the system alive. Zero hand-edits on
the target machine.
ipc_host_init exercises ipc_port_alloc_kernel, uma_zalloc against the IPC zones, and ipc_kobject_set every timeEVFILT_MACHPORT kqueue filter machinerySYS_<name> = NO_SYSCALL doubles as the helper-register loop sentinel; Phase C wires real numbers.EVFILT_MACHPORT filter isn't registered (registration soft-fails because -16 is out of stock FreeBSD's EVFILT_SYSCOUNT bounds).mach_task_init; ravynOS seeded it for every process via kernel patch.Precise summary. At the kernel-IPC-primitive layer: equivalent capability, zero kernel patches. The Mach IPC subsystem (ports, messages, queues, rights) is loaded and demonstrably functional. At the "userland can speak Mach" layer: not yet — ravynOS shipped patched kernel + libmach + Apple launchd, end-to-end; we've cleared the kernel side. Phase C is syscall wiring + libmach.
Roughly 70–80% of ravynOS / NextBSD's Mach kernel capability, with zero kernel modifications. That last clause is the part neither ravynOS nor NextBSD attempted.
sys/compat/mach/ + sys/sys/mach/ + sys/modules/mach/Makefile from ravynOS into mach-kmod/.SYSDIR, bundle MIG-generated stubs, stub opt_*.h.mach.ko in CI; install to /boot/kernel/mach.ko on the cd9660 ISO; set loader.conf: mach_load="YES" so loader preloads it before init runs.mach.ko appears in kldstat (smoke-test asserts this from rc.local), stock init still PID 1, normal multi-user boot completes.mach_port_allocate + mach_msg(SEND|RCV) round-trip.mach.ko-FreeBSD-15.x-amd64.tar.gz, etc.) uploaded to GitHub releases alongside the ISO.init AND mach.ko kldloaded AND userland round-trip works. Mach exists on FreeBSD; nothing in normal userland uses it yet.🛑 Checkpoint — stop here and talk.
Phases C onward bring in real userland (libxpc, libmach headers, Apple's launchd, configd, IPConfiguration). Each is a real commitment. Decide based on what Phase B actually revealed:
mach.ko stable, leak-free, fast enough?lib/libxpc/ is the only realistic source. Do we adopt it, write our own, or skip XPC entirely? This decision blocks Phase C and needs an explicit discussion. The freebsd-libxpc plan works out the adopt-and-port path in detail, with sub-spikes on CF + Foundation implementation choice, the libdispatch Mach patch, and install layout.The phases below are sketches of what would come next if we proceed, not commitments. Each is subject to revision at the checkpoint.
include/mach/ userspace headers from ravynOS.DISPATCH_SOURCE_TYPE_MACH_RECV if libdispatch is Mach-aware.launchd/. Apache 2.0. Includes liblaunch, libbootstrap, libvproc. The freebsd-launchd-842-porting-plan works out the concrete porting steps against the launchd-842 drop.init= to /sbin/launchd. launchd is now PID 1; bootstrap server is live; MachServices activation works; entire plist key vocabulary available (Sockets, WatchPaths, KeepAlive predicates, LaunchEvents) for free because that's Apple's actual implementation.freebsd-launchd bedrock plists to Apple XML format.freebsd-launchd/configd/src/ import (configd-963.270.3) or refresh from opensource.apple.com.MachServices plist.scutil --get HostName round-trips end-to-end.Reader's correction: "ravynos dropped freebsd to focus on porting xnu instead." There is no live ravynOS-on-FreeBSD upstream to coordinate with going forward.
The earlier draft of this section asked how to navigate a friendly-downstream relationship with ravynOS. That question is moot: ravynOS as a project has pivoted away from FreeBSD as a base and is now porting XNU directly. The clone we surveyed (/Users/jmaloney/Documents/launchd/ravynos) is the legacy FreeBSD-based tree — still complete, still self-consistent, still containing the working sys/compat/mach/ + launchd + libxpc + libdispatch we want — but no longer ravynOS's active development line.
Joe owns and maintains this work. The eventual destination is one of:
The choice between those two doesn't have to be made up front. freebsd-launchd-mach is the staging ground; whichever path proves more useful, the work rolls into it. Most likely the gershwin-on-freebsd absorption, given the existing momentum there, but the option to spin a separate distro stays open.
Single-maintainer ownership is a real consideration. Mitigations:
vmactions/freebsd-vm@v1 matrix builds the kmod against each FreeBSD major automatically; KBI breakage surfaces as red builds, not as user-reported panics.| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| ravynOS's mach.ko depends on patches to FreeBSD's kernel proper that aren't in stock | Medium | High | Phase B exit gate is "kldloads on unmodified FreeBSD-CURRENT." If we hit kernel-side patches, document them, decide between (a) a parallel mach-host-patches/ kernel patch series, (b) reimplementing the dependency userspace-side, (c) bailing back to v1's "build minimal" approach. |
| ravynOS's Mach has subtle bugs that they've never hit because of how their userland uses it | Medium | Medium | Configd is a different load profile from ravynOS's window-server / app-launcher use. Build a Mach test suite (port the relevant cases from XNU's tests/mach_*.c per v1 plan) early in Phase B. Find bugs, fix upstream where possible. |
| FreeBSD-CURRENT moves past ravynOS's snapshot KBI-wise, breaking our build — and there's no upstream tracking it for us | Medium | Medium | Per Section 10, ravynOS pivoted away from FreeBSD; we are the maintainers of FreeBSD-flavored Mach going forward. CI matrix builds against the latest snapshot of each FreeBSD major. Failures caught early. Per-major compat/ shim per the v1 plan keeps version churn isolated. |
| Single-maintainer sustainability — only Joe is on the hook for long-term Mach module care | Confirmed | Medium | Stable-KPI diet (Section 5) limits how often things break. CI catches breakage automatically. Release artifacts make wider community testing possible. Long-term, gershwin-on-freebsd absorption (Section 10.2) shares the maintenance load with existing contributors there. |
| Plist-format divergence (XML vs ravynOS's JSON) creates a fork in the launchd ecosystem | Confirmed | Low-Medium | Decide explicitly in Phase D. Recommendation: support both, parse XML by default for Apple-source compat, allow JSON as an opt-in. |
| Apple launchd at ravynOS's snapshot is older or has had patches we don't want | Likely | Low | Phase D includes a diff against Apple's own released launchd source. Cherry-pick only the FreeBSD-portability patches; document the rest. |
| libxpc is iXsystems' implementation, not Apple's; corner cases differ | Medium | Medium | Configd's XPC clients are not its hot path. Identify the exact xpc_* calls configd uses (344 grep hits per v1 inventory, but concentrated in IPMonitorControl / dnsinfo / network_information_server which we can defer). Bring each up incrementally. |
| The Mach kmod panics on a corner case configd hits at boot | Low-Medium | High | VM-only CI; never run untested kmod on hardware. Bisect against ravynOS's commit history when bugs surface. |
| License-headers audit reveals an APSL or proprietary file we missed | Low | Medium | Phase A includes a spdx-headers scan. Reject any file we can't ship under the repo's BSD-2 + Apache 2.0 + OSF/CMU policy. |
Go signal: Phase B (mach.ko kldloads on stock FreeBSD-CURRENT and round-trips a basic Mach message) completes within 4 weeks of starting. That validates the central bet: ravynOS's module really is liftable as a stock-FreeBSD kmod.
Pivot signal: Phase B finds that ravynOS's mach.ko depends on kernel-side patches (changes to FreeBSD's kern/, vm/, sys/proc.h, etc.) that aren't trivially out-of-tree-able. Two pivots: (a) ship a freebsd-mach-kernel-patches repo with a recompiled kernel; (b) fall back to v1's "build minimal mach.ko ourselves" plan, which deliberately stayed in stable-KPI land. Reassess at the 4-week mark.
Bail signal: Phase B reveals architectural problems in ravynOS's Mach (race conditions, unbounded memory growth, dropped messages under load) that are too deep to fix as an extract-and-go project. Walk away from the pure-port track. Continue the freebsd-launchd AF_UNIX track per its existing roadmap. The 4–5 weeks spent are not wasted — the survey work and the v1 plan remain on the shelf for a future attempt.
Reader's questions and the scope they impose:
Direct answers:
imgact_macho, no Phase F, no freebsd-dyld repo.launchctl, configd, IPConfiguration, mDNSResponder, asl, DiskArbitration) are pure C with CF by Apple's original design, not Obj-C / NS. swift-corelibs CF gives them what they need; GNUstep would not. Built with DEPLOYMENT_RUNTIME_SWIFT=0 to avoid pulling the Swift runtime onto every system service. See the launchctl-corefoundation-spike for the full evidence.imgact_elf + linux64.ko arrangement is unchanged. mach.ko is just an IPC kernel module; it doesn't affect execve(2).The reframe: this is a FreeBSD modernization project, not a macOS-on-FreeBSD project. The Apple stack has the better lower-level system services (init, IPC, network configuration, notification, structured logging, async dispatch); we lift those. GNUstep has the better Objective-C framework layer for FreeBSD use (it's already running on FreeBSD; it's the framework Gershwin uses); we keep that. The two layers compose cleanly.
| Layer | Source | Why this side |
|---|---|---|
| Apps and frameworks (Foundation, AppKit, libobjc2, GUI, Back, app bundles) | GNUstep | Already runs on FreeBSD, already used by Gershwin, mature, BSD/LGPL licensing, no Mach-O dependency, ELF-native. |
| CoreFoundation (pure-C CF API: CFString/Array/Dictionary/PropertyList/…) | swift-corelibs-foundation (Apache 2.0), built with DEPLOYMENT_RUNTIME_SWIFT=0 |
Required by every Apple-source system service (launchctl, configd, IPConfiguration, mDNSResponder, asl, DiskArbitration). They're pure C with CF by Apple's design — GNUstep libgnustep-corebase's plist parser is stubbed and would block launchctl. swift-corelibs CF ships real Apple plist + the SPI surface launchd-842 was written against. Installed at /usr/lib/system/libCoreFoundation.so.6. Full reasoning in launchctl-corefoundation-spike; the follow-on libcorefoundation-icu-audit covers why ICU does not ship on the ISO. |
| Async runtime (libdispatch / GCD) | Apple, via ravynOS extraction | Already in freebsd-launchd's build; ravynOS's version is Mach-aware and integrates with libxpc. |
| IPC primitive (Mach ports + messages + bootstrap activation) | Apple, via ravynOS extraction | The point of this plan. No FreeBSD-native equivalent with bootstrap-activation semantics. |
| Higher-level IPC (XPC dictionaries, connection lifecycle) | iXsystems libxpc, via ravynOS extraction | BSD 2-clause; built on Mach; works for FreeBSD use cases. |
| Init / service management (launchd, MachServices, plists) | Apple, via ravynOS extraction | Phase D of this plan. Replaces rc.d with a declarative system that knows about cross-service dependencies. |
| Network configuration (configd / SCDynamicStore) | Apple, via existing freebsd-launchd import |
Phase E. The whole point of justifying Mach in the first place. |
| Notification bus (notifyd / libnotify) | Apple, future port | Small, useful for desktop event coordination, was already on the roadmap. |
| Structured logging (asl) | Apple, future port | Better than syslogd for queryable logs. |
| libc, libm, libpthread, dynamic linker, image activator, kernel proper | FreeBSD | Already good. ELF stays. ld-elf.so.1 stays. Linux compat (linux64.ko) stays unaffected. |
libSystem-as-an-umbrella is not a goal. Most of what libSystem.B.dylib re-exports is either covered by FreeBSD's libc/libm/libpthread (no need to replace), or covered by GNUstep at higher levels (no need to take Apple's version), or related to Mach-O loading and dyld (out of scope). The specific pieces we extract from ravynOS (libdispatch, libxpc, plus the bootstrap/launch parts of liblaunch) are linked individually, not bundled into an Apple-shape libSystem.so. Apps and daemons that want them link them directly.
Concretely not in scope:
ld-elf.so.1 handles all our dynamic loading.mach.ko registers, with the wrappers in libmach./usr/lib/system/libCoreFoundation.so.6 — that's the engine our Apple-source system services need, not the Apple-built macOS framework.The Apple-source porting list shrinks to a tight set: Mach, launchd, configd, notifyd, asl, libdispatch, libxpc, liblaunch + the small bootstrap/vproc client surface + swift-corelibs CoreFoundation as the CF engine. That's it. Everything above that line is GNUstep (gershwin desktop overlay, separate fork); everything below that line is FreeBSD.
Each item from the existing FreeBSD Research roadmap gets a clearer answer with the layer split in place:
| Roadmap item | Effect of Mach being available |
|---|---|
| launchd | Two tracks now. freebsd-launchd stays as AF_UNIX. freebsd-launchd-mach uses Mach activation, MachServices, real bootstrap server. Both are valid; Joe picks per use case. |
| configd / netconfigd | The DO option in that plan is no longer the only path. With Mach available, configd builds and runs nearer to Apple's source. Still ELF. |
| notifyd | Build closer to Apple's source. notify_register_mach_port works. Still ELF; the daemon and its clients are FreeBSD ELF binaries linking against a libnotify that talks to notifyd over Mach. |
| asl | Same as notifyd: closer-to-Apple-source build, ELF userland. |
| mDNSResponder | Already mostly Mach-free thanks to mDNSPosix. Mach availability adds optional Bonjour Mach API; doesn't change the plan materially. |
| DiskArbitration | The DA daemon's Mach RPC and bootstrap registration work as-is. IOKit replacement (with libgeom + devctl) still needed; the daemon shell is easier. |
| IPConfiguration | Still deferred. Mach doesn't change the cost-benefit; dhcpcd still works. |
| kmodloader | Independent of Mach posture. No change. |
The take-away for the v2 plan as written: Mach is the foundation for the system-services layer, not for the framework layer. The 10–12 weeks of work in Phases A–E pay for themselves the moment configd boots and the first reusable bootstrap-activated daemon ships, because every subsequent system-services port (notifyd, asl, future configd plugins) builds on the same module. The framework layer above — Foundation, AppKit, the apps that ship on a Gershwin desktop — remain GNUstep, on ELF, exactly as they do today.
Reader's question: "Would this allow us to use Mach for sshd, cron, and other services as well?"
Direct answer: no, and that's fine — because what those services actually need is orthogonal to Mach. Mach activation is for services that have been written to speak Mach. sshd, cron, syslogd, ntpd, and the rest of the BSD daemon fleet speak Unix sockets, TCP, signals, and exec — not Mach. Bolting a MachServices key onto their plists accomplishes nothing because the daemon never calls bootstrap_check_in; launchd would allocate a port no one ever receives on.
What does help cron and sshd is a different, parallel set of launchd plist keys — Sockets, WatchPaths, KeepAlive dict subkeys, LaunchEvents — which the current freebsd-launchd port hasn't implemented yet. Those keys are independent of Mach and benefit both repos equally.
| Category | Examples | Mach helps? | What actually helps |
|---|---|---|---|
| Apple-source daemons written for Mach | configd, notifyd, asl, distnoted, mDNSResponder's Mach API mode | Yes | They already call bootstrap_check_in and serve clients via mach_msg. Launch-on-demand via MachServices works as designed. |
| Future Mach-native daemons we write | A hypothetical session-bus daemon; a config-cache broker; anything that wants port-rights handoff | Yes | Pick the right tool for the job; write it Mach-aware from day one. |
| BSD daemons that listen on a socket | sshd, inetd-style services, anything that accept()s |
No | Socket activation via the Sockets plist key. launchd opens the listener, holds it, hands the accepted fd to the daemon via SCM_RIGHTS on demand. This is how Apple's sshd works. Native FreeBSD primitives only; no Mach. |
| BSD daemons that just need to run on schedule or at boot | cron, ntpd, ntp-wait, periodic, dhcpcd | No | RunAtLoad + KeepAlive as today. For ordering, the KeepAlive dict predicates (OtherJobEnabled, PathState, SuccessfulExit) and WatchPaths let you express "cron restarts only after varrun successfully exits" declaratively. |
| One-shot housekeeping | varrun (mkdir /var/run), dmesg.boot, sysctl.conf application | No | RunAtLoad, KeepAlive=false. Other jobs that depend on them use KeepAlive.OtherJobEnabled or KeepAlive.SuccessfulExit. |
| Daemons triggered by hardware/network events | USB-insert handlers, NIC-up handlers, removable-media mounters | No | LaunchEvents with a com.apple.iokit.matching-style dict (FreeBSD equivalent: devd matchers, or a thin shim over devctl). No Mach involved. |
| Daemons that want to start when a file appears | spool watchers, drop-box uploaders, config-reload handlers | No | WatchPaths, backed by kqueue(EVFILT_VNODE). Pure FreeBSD KPI. |
Five out of seven categories don't touch Mach. That's not a limitation of the Mach approach; it's a clarification of what Mach is for. Mach is the right answer for kernel-mediated capability transfer between cooperating Mach-aware processes. It is not, and shouldn't be, the way every service on the box gets started.
The launchd plist keys that actually serve the BSD daemon fleet are: Sockets, WatchPaths, KeepAlive dict subkeys, LaunchEvents, QueueDirectories. None of them require Mach.
An earlier draft of this section proposed implementing these keys as a parallel workstream against the reduced launchd in the existing freebsd-launchd repo. That recommendation is dropped. The freebsd-launchd-mach repo brings in Apple's actual launchd at Phase D (post-checkpoint), which already implements every plist key in the vocabulary — that's the canonical implementation, and Apple's launchd is what we're lifting. There's no need to re-implement the keys; they come for free with the launchd source.
The implication: once Phase D ships, the entire BSD daemon fleet has every activation primitive available the moment we write the plists. Mach where Mach fits (configd, notifyd, asl). Sockets where sockets fit (sshd, inetd-style services). WatchPaths where filesystem state is the trigger. OtherJobEnabled where ordering is the only relationship. Alphabetical filename luck retires entirely — not because we wrote the parsing, but because Apple already did.
Mach is for the daemons that know they want Mach. The rest of the box — sshd, cron, syslogd, ntpd, dhcpcd, gettys, varrun — gets its declarative-ordering wins from the other launchd plist keys, which are a parallel workstream that benefits both repos equally and should arguably ship first because it serves more services per week of effort.
This is a FreeBSD modernization plan. The Apple stack has the better lower-level system services (init, IPC, network configuration, notification, structured logging, async dispatch); we lift those. GNUstep has the better Objective-C framework layer for FreeBSD use; we keep that. The userland stays as FreeBSD ELF, with Linux compat unaffected.
The technical mechanism: a new freebsd-launchd-mach repo for the pure-port track, leaving the existing freebsd-launchd AF_UNIX track in place and unaffected. The new repo extracts ravynOS's sys/compat/mach/ as an out-of-tree kmod, lifts their launchd / liblaunch / libxpc / libdispatch userland, integrates the Apple configd source that freebsd-launchd already imported, and ships mach.ko as a per-major-FreeBSD release artifact alongside a new live ISO. The earlier plans asked the right questions but either underestimated what already existed (v1) or overscoped what should be ported (the dyld / libSystem digression). Direct inspection of NextBSD and ravynOS — the latter pivoted away from FreeBSD to port XNU directly, leaving the FreeBSD-flavored work to inheritors — settled both questions.
Committed scope right now is Phase A and Phase B only — 4 weeks: get a vanilla FreeBSD ISO booting with stock init from the new repo, then port ravynOS's mach.ko so it kldloads at boot and a userland round-trip works. Hard stop after Phase B. The rest of the v2 plan (libxpc choice, libdispatch choice, Apple's launchd as PID 1, configd, IPConfiguration) is post-checkpoint scope — sketched in Section 9 but explicitly contingent on what Phase B reveals and on a separate discussion before each commitment. Long-term home: either a unique FreeBSD distro or absorption into gershwin-on-freebsd, owned and maintained by Joe.
Research basis: direct inspection of /Users/jmaloney/Documents/launchd/{nextbsd,ravynos,freebsd-launchd,freebsd-src} on 2026-05-10. Companion to freebsd-launchd-service-ordering.html. Direct successor to the predecessor investigation freebsd-mach-kmod-plan (the earlier "build minimal mach.ko from scratch" framing — the v1 that led to this plan and is retained for its API-surface analysis). The separate AF_UNIX track freebsd-launchd-plan is preserved alongside but is not part of this mach-porting tree. The sub-plans and spikes that elaborate parts of this plan are listed in the card at the top of the page.