freebsd-launchd-mach — a pure-port track that forks ravynOS's Mach + launchd stack

A 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.

Companion to freebsd-launchd-service-ordering.html, and direct successor to the predecessor investigation freebsd-mach-kmod-plan — the v1 feasibility study that led to this plan (superseded once direct inspection of NextBSD and ravynOS revealed a working module already exists, retained for the API-surface analysis: the configd Mach-surface inventory in its §8 and the stable-KPI / KBI diet in its §5). Written 2026-05-10 after concrete inspection of the NextBSD and ravynOS source trees. freebsd-launchd-plan is the separate AF_UNIX (non-mach) track preserved alongside — not part of this mach-porting tree.

Sub-plans and spikes that elaborate parts of this plan

Contents

  1. TL;DR — the short version
  2. Prior art — what NextBSD and ravynOS actually have
  3. Two-repo strategy — preserving the AF_UNIX track
  4. What we extract from ravynOS
  5. Out-of-tree-ifying ravynOS's mach.ko
  6. configd is the only missing piece
  7. Scaffolding reuse from freebsd-launchd
  8. License posture — OSF/CMU is BSD-compatible
  9. Revised milestones
  10. Upstream relationship to ravynOS
  11. Risks under the new plan
  12. Decision criteria
  13. What Mach unlocks downstream — dyld, libSystem, and the broader port horizon
  14. What Mach is, and is not, useful for — sshd, cron, and the rest of the BSD daemon fleet

1. TL;DR — the short version

Verdict: 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.

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.

2. Prior art — what NextBSD and ravynOS actually have

Concrete findings from direct source inspection. Both repos are now cloned at /Users/jmaloney/Documents/launchd/{nextbsd,ravynos}.

2.1 NextBSD (Jordan Hubbard / Kip Macy / Matthew Macy, 2014–2019)

AspectFinding
Repo shapeModified 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 PRESENTmach_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 STUBBEDTasks/threads/exception ports (basic hooks only), memory object creation, vouchers, audit-token-on-setuid.
UserlandFull 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).
ActivityLast commit October 2019. Six years of bitrot. FreeBSD 12 is EOL.
LicenseOSF License (1991–1998), CMU License (similar permissive), BSD 2-clause for FreeBSD wrappers. Not APSL. Clean to relicense as BSD.
Build status todayWill not build against FreeBSD-CURRENT without porting work. Estimated 6–8 weeks of KPI rework if used directly.

2.2 ravynOS (active fork, 325 commits in 2025 alone)

AspectFinding
Repo shapeSoft 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 implementationDirect 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 Makefilesys/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 formatJSON, in /Library/LaunchDaemons/*.json — not Apple's XML. Easier to author and grep, but a divergence from upstream Apple.
FreeBSD base15.0-CURRENT, tracking HEAD.
ActivityVery active. 325 commits since January 2025. Active CI (Cirrus). Discord + Matrix communities.
configd / SystemConfigurationAbsent. 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."

3. Two-repo strategy — preserving the AF_UNIX track

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 Continue

  • Keep the existing AF_UNIX framed protocol as the launchctl control channel.
  • Implement WatchPaths, Sockets, KeepAlive dict subkeys, LaunchEvents in core.m per the service-ordering doc.
  • configd port stays Phase-1 (skeleton) or migrates to GNUstep DO if the service-ordering doc's Option 3 ever ships.
  • Audience: people who want a small, AF_UNIX-only init replacement on FreeBSD without any Mach in the kernel.

freebsd-launchd-mach — pure-port track New

  • Fresh repo. Imports ravynOS's sys/compat/mach/, packaged out-of-tree.
  • Imports ravynOS's launchd, liblaunch, libxpc, libdispatch.
  • Imports configd from freebsd-launchd's existing 963.270.3 import.
  • Reuses freebsd-launchd's build.sh, ramdisk, kmodloader, overlays, CI workflows.
  • Audience: people who want an Apple-source-compatible userland on FreeBSD; people who want to port further Apple components (notifyd, asl, mDNSResponder) without re-IPC'ing them.

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.

4. What we lift from where

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.

4.1 From ravynOS (kernel module, plus libxpc which Apple never released)

Source path in ravynOSDestination in freebsd-launchd-machPhase
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.

4.2 From Apple directly (opensource.apple.com)

Apple sourceDestinationPhase
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.

4.3 From the existing freebsd-launchd repo (scaffolding only)

Asset in freebsd-launchdDestinationPhase
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.

5. Out-of-tree-ifying ravynOS's mach.ko

ravynOS'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/):

  1. Drop the in-tree SYSDIR assumption. Replace SYSDIR?=${.CURDIR}/../.. with SYSDIR?=/usr/src/sys. Standard out-of-tree pattern.
  2. Bundle the Mach source in-repo. All compat/mach/*.c and compat/mach/ipc/*.c live under mach-kmod/src/; .PATH points to ${.CURDIR}/src, not ${SYSDIR}/compat/mach.
  3. Bundle the headers. sys/sys/mach/* goes to mach-kmod/include/sys/mach/; the Makefile gains CFLAGS+= -I${.CURDIR}/include ahead of ${SYSDIR}/sys.
  4. Auto-generate MIG stubs at build time. The current Makefile lists pre-generated *_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).
  5. Pin a kernel header SDK. The kmod build expects 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.
  6. Smoke-test on stock FreeBSD-CURRENT in CI. The 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.

6. configd is the only missing piece

The earlier doc's central question was "how do we make configd run." The answer under v2 collapses neatly:

The remaining configd work is FreeBSD-specific, not Mach-specific:

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."

7. Scaffolding reuse from freebsd-launchd

The new repo should be productive on day one because the build/release plumbing already exists. Concrete reuse:

Asset in freebsd-launchdHow 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.

7.1 Repo layout for freebsd-launchd-mach

freebsd-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

8. License posture — OSF/CMU is BSD-compatible

The license worry from the v1 plan was XNU's APSL. The good news from the surveys: the parts we want are not APSL.

ComponentLicenseBSD-compatible?
ravynOS sys/compat/mach/ (lifted from NextBSD, ultimately from MkLinux/CMU)OSF License (1991–1998), CMU LicenseYes — permissive, attribution-only
ravynOS userspace Mach wrappers (mach_module.c, mach_traps.c)BSD 2-clauseYes
Apple launchd (sbin/launchd/)Apache 2.0Yes — same as Apple's launchd; preserve per-file headers
Apple liblaunch / libdispatchApache 2.0Yes
iXsystems libxpc (lib/libxpc/)BSD 2-clauseYes
Apple configdAPSL 2.0Yes (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.

9. Milestones

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.

Phase A — ISO boots with stock FreeBSD init (1 week) Committed

Phase B — mach.ko ported, kldloaded at boot (3 weeks) Build + runtime gates met

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:

  1. 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).
  2. 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.

Where this puts us vs ravynOS / NextBSD

Verified from a clean state: reboot → rm -rf the local repo and /boot/kernel/mach.kogit clone upstream → makecpkldload 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.

What we have that ravynOS / NextBSD didn't

  • Same ~99 KLoC ravynOS-derived Mach IPC source
  • Same kernel-internal primitives functional at load — ipc_host_init exercises ipc_port_alloc_kernel, uma_zalloc against the IPC zones, and ipc_kobject_set every time
  • Same EVFILT_MACHPORT kqueue filter machinery
  • Same struct definitions, same MIG-generated server stubs
  • Zero kernel patches. Stock FreeBSD-15.0-RELEASE-p8 GENERIC, untouched. mach.ko is a normal out-of-tree kmod.

What we don't yet have that they did

  • Apple Mach syscalls not reachable from userland. Compat shim's SYS_<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).
  • Per-process Mach state is opt-in via mach_task_init; ravynOS seeded it for every process via kernel patch.
  • No userland: no libmach, no launchd-with-Mach, no libxpc, no bootstrap server. Phase D+.

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.

🛑 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:

The phases below are sketches of what would come next if we proceed, not commitments. Each is subject to revision at the checkpoint.

Phase C — userland Mach stack (post-checkpoint, ~2 weeks if committed)

Phase D — Apple's launchd, fresh from opensource.apple.com (post-checkpoint, ~2 weeks if committed)

Phase E — Apple's configd, fresh (post-checkpoint, ~3 weeks if committed)

Phase F — Apple's IPConfiguration (post-checkpoint, ~6–9 person-months if committed)

Committed-now total: 4 weeks. Post-checkpoint scope: 7+ months if everything proceeds.

10. Project ownership and long-term home

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.

10.1 What this means in practice

10.2 Long-term home

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.

10.3 Maintenance posture

Single-maintainer ownership is a real consideration. Mitigations:

11. Risks under the new plan

RiskLikelihoodImpactMitigation
ravynOS's mach.ko depends on patches to FreeBSD's kernel proper that aren't in stock MediumHigh 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 MediumMedium 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 MediumMedium 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 ConfirmedMedium 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 ConfirmedLow-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 LikelyLow 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 MediumMedium 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-MediumHigh 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 LowMedium 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.

12. Decision criteria

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.

13. What Mach unlocks downstream — staying ELF, GNUstep at the framework layer

Reader's questions and the scope they impose:

Direct answers:

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.

13.1 The scope split — which layers come from where

LayerSourceWhy 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.

13.2 What we don't take from Apple

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:

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.

13.3 Effect on the existing FreeBSD-modernization roadmap

Each item from the existing FreeBSD Research roadmap gets a clearer answer with the layer split in place:

Roadmap itemEffect 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.

14. What Mach is, and is not, useful for — sshd, cron, and the rest of the BSD daemon fleet

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.

14.1 Which services benefit from Mach, and which don't

CategoryExamplesMach 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.

14.2 Why we don't need a separate plist-key implementation effort

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.

14.3 One-line summary

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.

Closing summary

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.