On 17 August 2026 a reader of the Emaculation forum thread that announces AppleBridge on SheepShaver did something more useful than filing a request: he patched the installer and reported what he had to change. He is running SheepShaver on Linux, and the installer is written for macOS.

This article treats that report as what it is — a portability audit performed by somebody with a machine we do not have — and answers the question it raises: is Linux support a major undertaking, or is a slirp-only host a bounded and honest option?

The report

“I’ve tweaked your install_bridge.py, as it’s currently assuming specific macOS filepaths — as I’m trying to leverage SheepShaver on Linux currently, I’ve done: […]”

Four suggestions follow, quoted in substance:

  1. Discovery should be able to ask. “The bundle candidate logic probably needs some improvement, possibly to ask the user to locate the emulator if it isn’t automatically found.”
  2. Linux packaging has a different shape. An .AppImage is a single executable file, not a bundle directory with Contents/MacOS/ inside it.
  3. Missing dependencies should offer a route. Where hfsutils is absent, offer to install it — “this will involve offering to install homebrew or macports on macOS, or checking for apt or another installer on Linux.”
  4. A different discovery strategy. “I wonder if a good way of finding an emulator would be to have the user run the emulator first, then check running processes for SheepShaver or BasiliskII?”

Earlier in the same thread (24 July) the same reader named the reason he is not simply testing the shipped configuration: cost. He wants the bridge driven by a locally hosted model, sees AppleBridge as a candidate replacement for ToolDaemon, asked for the client-side MCP documentation, and described a concrete use case — remote-orchestrated walkthrough videos across System 6.0.8 through Mac OS 9.2.2.

Method

The relevant question is not “does it run on Linux” but “where in the stack does the host operating system appear at all”. So each host-side component was inventoried against one criterion: what does it assume about the machine it runs on? The answer is uneven in a way that turns out to be favourable.

The inventory

LayerComponentHost-OS dependence
Wire protocol, guest daemonmac/, protocol v0.2none — the guest cannot tell what the host runs
Bridge serverhost/host_server.py, stdlib onlynone of consequence
Build and file surfacempw_execute, mac_compile, mac_build, mac_put_file/mac_get_file, mac_screenshotnone — the screen capture happens in the guest and arrives as pixels
Encoding, MacBinary, kit exportencoding_convert.py, macbinary.py, hfsutilshfsutils, which Linux packages and macOS does not
Installerhost/install_bridge.pymacOS bundle layout, launchd, ~/Library
Diagnosishost/bridge_doctor.pylaunchctl, ifconfig, route -n get default
Service supervisioninstall_host_service.sh, run_server.shLaunchAgent plist, /usr/bin/python3, TCC
Real-mouse layerhost/guest_input.py, mac_host_click/_menu/_screenshot, run_applescriptcliclick, osascript, screencapture — macOS through and through

The first four rows are the product. The last four are the machinery around it, and they are where the port lives.

flowchart LR
  A["Guest daemon
System 7 / Mac OS 9"] -->|"TCP 9000"| B["host_server.py
stdlib, portable"] B -->|"TCP 9001"| C["MCP surface
30 tools"] D["install_bridge.py
bridge_doctor.py
service install"] -.->|"configures"| B E["guest_input.py
cliclick / osascript"] -.->|"optional"| B style D fill:#f6d5a8,stroke:#b8763e style E fill:#f2b8b8,stroke:#a85252

Orange is the platform seam a Linux port has to add. Red is the part that would have to be rewritten rather than abstracted, and is optional.

Finding 1: the feature he proposes already exists — its pattern does not

The suggestion to find the emulator by looking at running processes is already the installer’s first and most authoritative source. probe_emulator_bundle() runs pgrep -fl "BasiliskII|SheepShaver" before it consults any candidate path, on the stated principle that a running process beats every guess.

What fails on Linux is one regular expression:

m = re.search(r"(/.*?\.app)/Contents/MacOS/", line)

That pattern extracts a bundle from a process command line. Given /usr/bin/SheepShaver or /home/user/SheepShaver.AppImage it matches nothing, so the probe falls through to the well-known-locations list — which is /Applications/BasiliskII.app and three variations of it. The reported symptom is therefore an accurate description of a small defect, not of a missing design.

The same applies to the bundle test. is_emulator_bundle() deliberately identifies an emulator by the executable inside Contents/MacOS, not by the bundle’s name — a rule adopted after a real folder produced wrong answers in both directions (Kanji-2020-01-22.app is an emulator, BasiliskIIGUI.app is a front-end). The principle — identify by the executable — survives on Linux unchanged. Only the path template around it is macOS-shaped.

Correction the report earns: the operator-supplied path exists too, as --emulator-app, and is documented as deliberately a flag rather than a prompt, because the installer must be able to run with nobody at the keyboard. That is a defensible design, but it is not discoverable from the failure. When discovery finds nothing, the program should name the flag in the error rather than leaving the reader to find it in --help.

Finding 2: the prefs file is Basilisk-only, and that bites on macOS too

The single most consequential thing this report surfaces is not Linux-specific at all.

Every path in the host stack that reads or writes emulator preferences uses one hardcoded name:

PREFS_PATH = os.path.expanduser("~/.basilisk_ii_prefs")

It appears in install_bridge.py, bridge_doctor.py, guest_input.py and bench_transport.py. SheepShaver’s preferences file is not that file. A search of the repository finds no reference to a SheepShaver preferences path anywhere.

The consequence is well within the project’s own definition of its worst failure class — an operation that reports success and does nothing. On a SheepShaver host, install_bridge.py would write ether slirp into a Basilisk preferences file that the running emulator never reads, then verify its own write by reading the same file back, and report a correctly configured backend. The verification passes because it is reading what it just wrote, not what the emulator uses.

This is a defect on macOS today. It happens to be a Linux user who stood in the right place to see it.

Finding 3: supervision and probes are the real platform code

Three concrete places, all shallow, none subtle:

  • Service supervision. The host server runs as a LaunchAgent: ~/Library/LaunchAgents/de.390er.applebridge-host.plist, started with launchctl, logging to ~/Library/Logs/AppleBridge, with a deployed copy under ~/Library/Application Support/AppleBridge. The Linux equivalent is a systemd --user unit and the XDG directories. Note that the reason for the deployed copy is macOS-specific: the repository lives under ~/Documents, which is TCC-protected, so launchd cannot read it without Full Disk Access. On Linux that reason simply does not exist, and the server can run from the checkout.
  • Network probes. bridge_doctor.probe_network() parses ifconfig output and asks route -n get default for the default-route interface. Both are BSD forms; Linux needs ip -o addr and ip route show default. But see the next section for how much of this matters.
  • The interpreter. run_server.sh hardcodes /usr/bin/python3 and explains why: the macOS application firewall blocks the un-allowlisted venv binary, so the system interpreter is the one whose inbound connections survive. On Linux that constraint evaporates; the other half of the rule — stdlib only — is what should travel.

Nothing here is architecture. It is a platform module with three implementations of five questions: where does the emulator live, where do its preferences live, how is a background service installed, which addresses does this host have, which interface carries the default route.

Finding 4: what a Linux host does not need, because it cannot have it

This is where the assessment turns favourable, and it is worth being precise about why.

Nearly every hard-won piece of host knowledge in this project belongs to the etherhelper branch: that the host address must sit on the default-route interface, or the guest’s NAT return path splits across interfaces and freezes the emulator at 100 % CPU; that a single-NIC host cannot form a guest-to-host connection at all; that the bridge interface must exist before the emulator starts; that the branch costs two interactive password prompts per launch and therefore cannot start unattended.

None of it applies. etherhelpertool is a macOS binary shipped in the macOS bundle — confirmed in source the following day: BasiliskII/src/Unix/ether_unix.cpp compiles that backend only under #ifdef ENABLE_MACOSX_ETHERHELPER.

Corrected 2026-08-18. This paragraph originally continued: “A Linux emulator’s bridged path is a different mechanism entirely — TUN/TAP with a kernel module — and the installer’s own requirement R9 already says the supported path must not require a kernel extension. On Linux that requirement points at slirp before anyone asks the question.” That is wrong, and it is left standing here rather than quietly deleted. The Unix build offers tap*, tun, slirp, vde and sheep_net, and only sheep_net needs a kernel module. tun needs none — it runs the tunconfig script, which requires sudo NOPASSWD entries for ifconfig and iptables (a one-time sudoers change, not a per-launch prompt) and configures IP masquerading, so it is a routed path rather than a bridge. vde needs neither module nor root on the guest side. AppleTalk is therefore reachable on Linux — directly over sheep_net, or routed through netatalk over ethertap; see Does the Linux Build of Basilisk II Support AppleTalk?. What survives is the narrower and duller claim: none of those installs unattended, so the installer still targets slirp — exactly as it does on macOS, and for D-018’s reason rather than for a missing mechanism. Recorded as D-024, which supersedes D-023.

What slirp gives is better than mere availability: its addressing is internal to the emulator, so it is identical on every host operating system. The guest gets 10.0.2.15, mask 255.255.255.0, router 10.0.2.2, name server 10.0.2.3 — by DHCP, which slirp answers itself. The daemon may dial 10.0.2.2, which slirp forwards to the host’s loopback; the server hears it because it binds 0.0.0.0. There is no host address to derive, no alias to place, no interface to choose, and therefore nothing for the BSD-shaped network probes to get right.

The measurements behind those values were taken on macOS hosts (28 July and 31 July 2026). The mechanism producing them lives inside the emulator, which is the basis for expecting them to hold on Linux — an expectation, stated as one, not a measurement.

The same reasoning disposes of two more macOS-only concerns: Gatekeeper app translocation, which the installer carefully refuses to record as configuration, and the refusal that protects a working etherhelper host from being converted. On Linux the first cannot occur and the second has nothing to protect.

Finding 5: what a Linux host would lose

Honesty about the cost, in the order it would be noticed:

  • AppleTalk — no Chooser, no AFP mounts, no mac_appletalk_browse. Exactly the cost the macOS slirp branch already pays and states; TCP keeps working, which is how that gap disguises itself.
  • The real-mouse layer. mac_host_click, mac_host_menu, mac_host_screenshot and the Route-B menu trigger are cliclick, osascript and screencapture. They exist because menus, Standard File dialogs and modal loops poll the hardware pointer, so no synthetic event reaches them. An X11 host could plausibly be served by xdotool plus an X screen grab; Wayland has no general answer. Without this layer a Linux host keeps the whole build-and-run surface and loses the ability to drive tracking loops.
  • run_applescript has no Linux meaning and should be absent rather than failing.

The one thing that gets easier

hfsutils — required by the kit export, the preferences seeder and the test-guest builder — is not part of macOS and must be fetched from Homebrew or MacPorts. On Debian and its derivatives it is a packaged apt install hfsutils. The suggestion to offer installation is therefore more actionable on Linux than on the platform the installer was written for. The probe that would drive it already exists and already reports the missing binaries by name, precisely because a missing hmount once produced an error naming an innocent disk image with no reason attached.

Assessment

A slirp-only Linux host is a bounded piece of work, and the boundary is drawn by the same decision that already restricts the installer to one branch on macOS: it configures the path that can come up unattended, and names the other one instead of pretending to support it. On Linux there is only the one path.

WorkShapeConfidence
Platform module: emulator discovery, prefs path, service install, address/route probesnew seam, three shallow implementations behind five questionshigh — every call site is already injectable for testing
Per-emulator preferences path (Basilisk vs SheepShaver)correctness fix, needed on macOS regardlesshigh
hfsutils install advice per platformextends an existing probehigh
AppImage and plain-ELF discovery, --emulator-app named in the failuresmallhigh
Real-mouse layer on X11separate piece, larger, no Wayland storylow — unscoped

The reason this is not a rewrite is structural rather than lucky: the bridge never knew what host operating system it was on. What knows is the code that installs and diagnoses it, and that code was written with injectable run/read/write/exists so its branches could be driven from canned output in tests. A platform seam is the same shape of change as the test seam that is already there.

What is not settled

Two things, and neither is technical.

First, nothing here has been executed on Linux. There is no Linux host on this project. Every claim above is either a reading of this repository’s own source — which is exact — or an inference from how slirp works, which is not. A port asserted without a test machine is precisely the class of claim this project keeps a decisions file to avoid.

Second, the person who reported the problem is already running the configuration, has already written a version of the fix, and wants the bridge for a documented purpose across System 6.0.8 to Mac OS 9.2.2. The sensible arrangement writes itself: the platform seam is built here, and the machine that decides whether it is right is his. That also settles the smaller questions — AppImage layout, which init system, whether the X11 input layer is worth the effort — by measurement rather than by guessing at somebody else’s desktop.

The remaining request from the same thread, the client-side MCP documentation, is independent of all of this and is the cheapest item on the list.

Addendum, 2026-08-18 — the branch question is now decided

Same day, after this assessment: D-023 in DECISIONS.md records that on a Linux host the backend is ether slirp and there is no second branch — not set aside, as etherhelper is on macOS under D-018 and D-019, but absent, since etherhelpertool is a macOS binary and the Linux bridged path needs a kernel module that R9 already forbids requiring.

What that changes is the shape of the work rather than its size: a port builds a platform seam and explicitly not a backend choice, and the AppleTalk cost is stated as permanent on that platform instead of as a hand-configuration away. What it does not change is the limit this article ends on — the decision fixes the shape of Linux support; it does not claim Linux works, and nothing has yet been executed on one.

Superseded the same day — D-024. Six hours later a survey of the Linux build’s Ethernet backends refuted the sentence above, and a source read confirmed the refutation: only sheep_net needs a kernel module, tun and vde do not, and AppleTalk is reachable on Linux. So the AppleTalk cost is not permanent on that platform — it is not installed by default and is available by hand, which is what macOS has said all along. The installer’s target does not move: none of those backends comes up unattended, so slirp remains what it configures, now for D-018’s reason rather than for a missing mechanism. The correction is set out in the box under Finding 4, and D-023 is kept in DECISIONS.md with a superseded banner rather than edited away. It stood for one afternoon and its own falsifier retired it, which is the arrangement working rather than failing.

Pit

Quellen

  1. Forum thread “AppleBridge now also works on SheepShaver”, emaculation.com: https://www.emaculation.com/forum/viewtopic.php?t=12754
  2. Installing AppleBridge: https://td5.390er.de/applebridge/installing-applebridge/
  3. What You Can Do With It: https://td5.390er.de/applebridge/what-you-can-do-with-applebridge/
  4. kanjitalk755/macemu pull request 314 (thin-arm64 etherhelpertool): https://github.com/kanjitalk755/macemu/pull/314