macOS target¶
Labstream includes a native macOS target on main for local source builds. The target and
scheme are named LabstreamMac and require macOS 26. It compiles Labstream/Shared/,
Labstream/Capabilities/Downloads/, and its exclusive Labstream/Platforms/macOS/ owner
root, plus the shared PMSKit package.
The Mac target is a pre-release App Store candidate, not yet a compatibility promise. Distribution signing, universal-purchase configuration, sandbox behavior, and live-host acceptance remain release gates. The repository's GPL section-7 exception covers the macOS application path.
Current implementation¶
The target provides a native Mac app shell and adapts the shared browse, search, detail, playback, music, downloads/offline, settings, diagnostics, and backend flows for the host Mac. Mac-specific code supplies window commands, keyboard navigation, fullscreen/player presentation, and system media integration while backend wire behavior and pure policies remain shared.
The app owns exactly one reusable browse/player window plus the singleton system Settings window.
Closing the main window hides it instead of dismantling its SwiftUI graph: Dock reopen, system
entries, and menu commands reactivate that same window deterministically, while active playback,
music, and background downloads continue until the user explicitly stops them or quits the app.
The scene remains a command-suppressed WindowGroup so Finder, Dock, and direct executable launches
all create the initial window reliably; the retained-window controller, hidden-close behavior, and
removed New Window command prevent a second browse/navigation stack.
The Mac root uses a native source-list split view. Home is followed by the active server's visible non-music libraries, an optional Music section whose child routes share an explicit selected-library context, and a standalone Offline destination. Search lives in the native toolbar: Command-F focuses it, Escape or a source-list selection dismisses it, and the prior detail route is restored. The source list collapses to detail-only below 900 points while retaining the native toggle; the window minimum is 760 by 640 points. Account-menu sign-out always requires confirmation.
The Mac music mini-player is a bounded native control card rather than a window-wide media strip. It keeps title/artist/album context, a real scrubber, previous/play/next controls, queue and expand actions, and an explicit stop-and-dismiss action available while the user browses. Pointer help, keyboard shortcuts, accessibility labels, and truncation preserve the primary controls at narrow window sizes; opening Now Playing is separate from stopping playback.
The Offline source-list row shows a single transfer's byte progress directly. With multiple active
transfers it labels the count explicitly and uses a byte-weighted aggregate only when every active
transfer has an exact expected byte total. Unknown or estimated totals suppress the percentage.
The queue toolbar's Pause All / Resume All action is intentionally distinct from per-item pause
and retry controls.
Treat this as pre-release source coverage rather than a released compatibility promise. Real Plex, Jellyfin, and Emby authentication, media-key behavior, playback, sandboxing, and background-download recovery still need platform-specific TestFlight and release acceptance.
Build, stage, and launch¶
There is no macOS simulator lane. Use the host helper, which builds an arm64 Debug app into
worktree-local DerivedData and stages it under the repository rather than installing into
/Applications:
scripts/deploy-macos-to-host.sh # build + stage
scripts/deploy-macos-to-host.sh --launch # build + stage + launch
scripts/deploy-macos-to-host.sh --no-build --launch
By default, the helper derives a development bundle identifier from the worktree, such as
org.labstream.Labstream.dev.issue-228-macos. The staged app lives at
build/macos-host/<identity>/Labstream.app. This keeps parallel worktrees from sharing a sandbox,
offline library, LaunchServices identity, or background-download session.
Noncanonical Mac development identities also use isolated, backup-excluded credential files in their sandbox instead of the production Keychain path. This avoids repeated Keychain prompts as an ad-hoc development app is rebuilt. The canonical production-style identity continues to use the normal Keychain policy, but it should be exercised only for intentional identity testing.
--use-production-bundle-id switches to org.labstream.Labstream. Do not use it for routine
development: multiple production-identity builds share the same LaunchServices identity,
sandbox, Keychain behavior, and logs.
The sandboxed production and development builds enable both outgoing network access and the network-server entitlement required by the media compatibility proxy. The listener remains restricted to the loopback interface; it is not a LAN media server. Without the listener entitlement, proxy startup fails even when direct server browsing and playback work.
Local network permission and Debug launcher identity¶
The host helper sets ENABLE_DEBUG_DYLIB=NO. Xcode's small Debug launcher was observed
to have the same executable UUID in isolated-development and production-identity builds.
macOS uses that UUID when enforcing local network privacy; the collision caused local
connections to fail with Local network prohibited even while Settings showed access enabled.
Rebuilding without the Debug launcher produced distinct main-executable UUIDs. The isolated
helper app regained live server access after normal permission approval. This affects local
helper builds, not archive settings. Production app replacement is not part of this workflow.
If this recurs, compare the main executables with xcrun dwarfdump --uuid, verify signing,
and check System Settings → Privacy & Security → Local Network. Relaunch and retry after
permission changes. Do not disable network security or relax server policy to mask a local
permission failure. Distinct app identities must not share a main executable UUID; see
Apple TN3178
and TN3179.
Live display capability¶
Player Stats separates stream metadata from the Display capability row. The native video
view tracks its window's screen (not NSScreen.main), display moves, and screen-parameter
changes. HDR-capable hardware and current EDR headroom are separate facts: headroom may be
1 while the display still supports HDR. AVPlayer eligibility changes are observed even when
playback is paused or the initial stream probe has finished. Disconnecting the view clears its
screen facts and observers. These are capability diagnostics, not proof of HDR light output.
Hardware acceptance: move a playing and then paused window between HDR and SDR displays, toggle the system HDR setting, and disconnect/reconnect the display. Confirm the Display row updates without a playback restart or encoding-consent bypass. This requires physical display testing; unit tests and a launch smoke do not close that gate.
Current bounded results and open platform gates are in the native HDR investigation.
Cleanup¶
Clean up host-development state after a one-off test or before removing its worktree:
scripts/deploy-macos-to-host.sh --delete
scripts/deploy-macos-to-host.sh --delete-all-staged
scripts/deploy-macos-to-host.sh --reset-container
Development identities use the visible display name Labstream Dev — <identity>, while an
intentional production-identity build remains Labstream. --delete-all-staged terminates and
removes every Mac app staged by the current worktree but preserves containers and Keychain data.
The production-identity host path is Apple-Development-signed and provisions the Mac because its
canonical service reads the synchronized Plex-token Keychain item. An ad-hoc canonical build lacks
an application identifier/keychain group and fails that access with OSStatus -34018; use the
helper rather than launching a generic ad-hoc product for signed-in testing.
--delete removes only the staged app for the effective identity. --reset-container removes
only that identity's sandbox container. The helper never deletes /Applications/Labstream.app,
and resetting the canonical container requires both --use-production-bundle-id and
--allow-production-container-reset.
Validation¶
The Mac scheme owns the LabstreamMacTests target and LabstreamMacTests.xctestplan. The plan
hosts the shared LabstreamTests/ sources in LabstreamMac; run it directly when changing
app-owned persistence, lifecycle, auth-storage, or playback/system-media seams:
scripts/xcodebuild-versioned.sh -project Labstream.xcodeproj \
-scheme LabstreamMac -testPlan LabstreamMacTests \
-destination 'platform=macOS,arch=arm64' test CODE_SIGNING_ALLOWED=NO
The current repeatable Mac sweep is:
For the credential-free semantic app loop itself, run:
That runner stages an isolated development identity, launches the shared synthetic browse fixture,
uses Accessibility to activate the stable Home item, asserts the detail text, and records
window-scoped before/after screenshots, bounded logs, driver.json, and run.json. It terminates
the exact staged process it launched and does not read or mutate the production Labstream
container.
The script retains its issue-era filename for now. It covers static identity checks, the Mac
build, extra visionOS and iPhone xcodebuild compile steps against this worktree's concrete
simulator IDs (those lookups may provision sims; this is not a leased simulator test run),
focused PMSKit DiagnosticLoggingTests, and a bounded host launch smoke through
scripts/smoke-macos-host.sh. Shut down any simulator it left booted before another leased
turn. It does not prove real sign-in, subjective UI
quality, live media playback, system media keys, background-download durability, or the full
app-hosted test plan.
See Testing strategy for the repository-wide validation layers.
Release status¶
The Mac target participates in the coordinated universal-purchase release and uses the
production identifier org.labstream.Labstream. Licensing, universal-purchase topology, identity,
and version policy are decided; they are not remaining design questions. The open gates are a clean
Apple Distribution archive and validation, App Store sandbox/entitlement review, processed
TestFlight build, fresh-install sign-in, real-backend playback, keyboard/media-key and window
lifecycle behavior, download recovery, accessibility, screenshots, metadata, and App Review.
Downloads remain inside the app container. External download folders and security-scoped bookmark migration are deferred product work, not prerequisites for this release.