MetalSharp Documentation
Everything from first launch to the VKD3D shader engine — rendered straight from the repository docs. Pick a topic from the sidebar — or the Docs menu on mobile.
How to Use MetalSharp
Install, launch, diagnose, and update flow.
How to Use MetalSharp
Updated: 2026-08-11
Install
- Download the latest MetalSharp DMG from GitHub Releases.
- Drag MetalSharp into
/Applications. - Open it. If macOS blocks the unsigned app, go to System Settings → Privacy & Security and choose Open Anyway.
- Run setup from inside MetalSharp — it will install Homebrew dependencies, the Wine runtime, MetalSharp-owned graphics/runtime assets, and redistributable source material used by bottle repair.
- Start Wine Steam, sign in, and download a Windows game.
GPTK/D3DMetal is not installed during generic setup. MetalSharp installs and uses Homebrew GPTK only when you save a game as a D3DMetal bottle.
Steam Games
After a game is installed, MetalSharp scans the internal Steam library and every
external library listed in Steam's libraryfolders.vdf, then creates a Steam
game bottle such as steam_620. The Library refresh action and the background
installed-game watcher check for new manifests every 15 seconds.
The bottle is the launch-authoritative runtime record. It checks the selected profile, runtime assets, redistributables, DLL expectations, and logs before launch.
For routes such as VKD3D, DXMT, and Mono/FNA, MetalSharp keeps Wine Steam alive in the background when Steamworks ownership/session state is needed, then launches the game executable through the selected bottle-aware MTSP pipeline. The game process receives the prepared prefix or native Mono/FNA environment, cache paths, and Steam identity variables (SteamAppId and SteamGameId) so Steamworks can bind back to the running Wine Steam client where applicable.
Wine Steam desktop shortcuts are redirected to the hidden
~/.metalsharp/steam-desktop/ directory instead of the macOS Desktop. Existing
Steam .url shortcuts are moved there the next time Wine Steam starts; other
URL shortcuts on the macOS Desktop are left untouched.
Internal Steam, Wine, macOS Steam, M32, and raw DXMT routes still exist for diagnostics, compatibility records, and backend fallback behavior, but they are not normal route selector choices. If Wine Steam is not detectable after startup, MetalSharp fails the launch clearly instead of hanging behind the renderer timeout.
Click Play from the Library page. Use the launch mode dropdown when you want to force a route:
| Mode | Use |
|---|---|
| VKD3D | D3D12 to Metal via vkd3d-proton (D3D12 → Vulkan → MoltenVK). DXMT rollback available via the vkd3dBackend setting (Settings) |
| M11 | D3D11 to Metal |
| M10 | D3D10 to Metal |
| VKD3D | D3D9/D3D10/D3D11/D3D12 through vkd3d-proton + DXVK-macOS (Vulkan → MoltenVK) |
| Mono/FNA | Windows XNA/FNA games through MetalSharp's native Mono runtime, staged FNA/XNA assemblies, native dylibs, FMOD/FAudio/FNA3D shims, and Steamworks shim support |
| D3DMetal | Apple Game Porting Toolkit via Homebrew, using a shared GPTK prefix and Homebrew-matched D3DMetal route DLLs |
Use Runtime Doctor on a game card when a game needs VC runtime, DirectX, .NET, WebView2, fonts, or other launch assets.
D3DMetal / GPTK Bottles
D3DMetal is an explicit bottle lane for games that should run through Apple Game Porting Toolkit instead of MetalSharp's DXMT route.
- Save the game as a D3DMetal bottle. MetalSharp installs/trusts Homebrew GPTK (
brew trust --cask gcenx/wine/game-porting-toolkitas needed, thenbrew install game-porting-toolkit) and ensures Rosetta 2 is present. The save stages Steam runtime files (libraryfolder.vdf,steam.dll,steam_appid.txt,steamclient.dll, andsteamclient64.dll) beside the game and selected executable directory. For Elden Ring and ARMORED CORE VI, save also performs the one-timestart_protected_game.exe→start_protected_game.oldpreservation and copies the real game executable to the original launcher path. - Click Repair Redist. This copies MetalSharp-bundled x64+x86 VC runtime DLLs into
~/.metalsharp/prefix-gptk/drive_c/windows/system32andsyswow64, then writes VC runtime registry keys. - Click Seed Prefix. This wineboots
~/.metalsharp/prefix-gptk, copies Homebrew GPTK route DLLs from/Applications/Game Porting Toolkit.appinto prefixsystem32, quarantines app-local D3D/DXGI/NVAPI shims, and writes D3DMetal launch metadata. - Click Play D3DMetal. MetalSharp launches the game executable directly through Homebrew GPTK Wine; it does not launch Steam for this route.
Goldberg Steam Emulator
The Goldberg toggle enables offline play for supported games without Wine Steam running. Toggle it on from the game card — MetalSharp saves the original Steam DLLs as .orig and deploys the emulator with the correct appid. Toggle off to restore the originals.
Supported games include Portal 2 (appid 620) and others that work without Steam DRM validation.
Sharp Library
Sharp Library is for Windows apps, demos, launchers, installers, and non-Steam programs.
Use Install Windows Program to select an .exe or .msi. MetalSharp may import it directly, or create an installer bottle, classify the installer, apply a known launcher recipe when one matches, launch it with the right profile, then scan for installed app candidates.
Apps imported from a bottle keep their bottle_id, launch through that bottle, and write per-bottle logs.
MoonScraper Chart Editor's Inno Setup bootstrapper is handled without its Windows installer UI because that bootstrapper crashes in macOS Wine's WoW64 runtime. MetalSharp uses the native innoextract tool (installing it through Homebrew when needed), extracts the portable application into its dedicated bottle, and adds the detected editor directly to Sharp Library.
Logs and Settings
Use Logs when something fails. The page has drawer sections for live logs, crash reports, and recent log files.
Use Settings to manage Steam API sync, backend restart, cache cleanup, runtime maintenance, and the VKD3D graphics backend (vkd3d-proton default / DXMT fallback).
Sidebar Toggles
The sidebar (near the theme picker) has three runtime toggles, applied on next launch:
- MetalFX — DXMT MetalFX Spatial upscaling strength for the DXMT routes (M10, M10(32), M11, M11(32)): 1.75 / 1.50 / OFF (default 1.50, enabled). VKD3D (vkd3d-proton) and other routes are unaffected.
- msync — Wine msync (Mach-synchronized sync primitives): ON (default) / OFF.
- Controller — input shims: Off / X / D (see below).
Controller Input Shims
The sidebar has a Controller selector (Off / X / D) near the theme picker:
- Off (default) — no input shims are deployed.
- X — XInput shims (
xinput1_1.dll…xinput1_4.dll,xinput9_1_0.dll) are copied into the game folder on launch and into the Steam prefix (system32+syswow64). - D — DInput shims (
dinput.dll,dinput8.dll) are deployed the same way.
Switching between X and D removes the previously deployed set before deploying the new one; switching to Off removes both. Files that already existed (for example a game's own xinput1_3.dll) are backed up and restored when the mode is switched off.
Uninstall
Settings includes a Danger Zone section at the bottom with an Uninstall MetalSharp button. The confirmation dialog shows the resolved MetalSharp data path before removal. For safety, uninstall only removes a marked MetalSharp data directory below the user's home directory; if METALSHARP_HOME points at the home directory, the filesystem root, an external/shared location, or an unmarked directory, MetalSharp refuses the uninstall without deleting data. A permitted uninstall removes all Wine prefixes, bottles, Steam, runtime, caches, and settings, then moves the app to Trash.
Useful Docs
Library & Logs UI
Library source selection, Sharp app tools, and log drawer behavior.
Library and Logs UI
Updated: 2026-07-22
Library
Steam and backend status remain in the title row as the window narrows. Launch, refresh, search, and filter controls reflow below the title without moving those status badges into the action row. Refresh scans the internal Steam library plus external Steam libraries from libraryfolders.vdf; the background watcher checks for new installed manifests every 15 seconds.
Installed Steam game cards show the Steam Emu toggle. The EAC substrate
remains backend-controlled while its compatibility work continues; no EAC
toggle is exposed on game cards for now. Existing per-app EAC state and launch
plumbing remain available for diagnostics and migration, with substrate logs
and module dumps kept under ~/.metalsharp/logs/eac/<appid>/.
The shipped MTSP rules include protected-launcher metadata (eac_exe_names)
and normal executable metadata (exe_names) for all requested EAC cards:
Elden Ring, ARMORED CORE VI, Rocket League, The Outlast Trials, Halo MCC,
Sea of Thieves, Pavlov, Rust, 7 Days to Die, Vermintide 2, Watch Dogs 2,
Fall Guys, Friday the 13th, VRChat, Rogue Company, Hunt: Showdown 1896,
Total Lockdown, Lost Ark, Gears 5, Halo Infinite, For Honor, REMATCH, Stay
Out, Back 4 Blood, Apex Legends, Lords of the Fallen, Throne and Liberty,
Star Wars: Squadrons, NBA 2K26, Next Day: Survival, Suicide Squad, SCP:
ReEnter, Killing Floor 3, Battlefield 2042, Squad, ARC Raiders, and
MultiVersus. New defaults use M11; the existing Elden Ring and AC6 VKD3D rules
remain unchanged. When saving either game as a D3DMetal bottle, MetalSharp
preserves Game/start_protected_game.exe as
Game/start_protected_game.old once, then copies the real game executable to
the original protected-launcher path.
The EAC card control is intentionally hidden for now. The card remains available for every installed Steam game; rule metadata continues to control protected and normal executable selection in backend launch paths.
EAC substrate installation lifecycle
The DMG and split scripts/tools bundle must contain both native boundary
artifacts: metalsharp_eac_substrate.dylib (x86_64 Mach-O) and
metalsharp_eac_libc.so.6 (x86-64 ELF). First-run setup installs the verified
pair into ~/.metalsharp/runtime/eac/ after the scripts/tools bundle. The pair
is staged and committed together, so a failed refresh cannot leave one new
artifact beside one old artifact.
An app update verifies those files before replacing the installed app and then
sets the post-update migration marker. Migration schema 5 treats a missing or
invalid durable pair as runtime repair: it reinstalls the substrate, verifies
both files, and only then marks the migration complete. Per-game EAC state JSON
under sharp-library is preserved; the backend state remains opt-in and does
not launch a game during installation, update, or migration. The app-card
toggle is hidden until the substrate is ready.
Sharp Library
Use the Library source menu to switch between installed Windows applications and GOG games. The installer view keeps its primary actions focused on installing and refreshing applications; redistributable source controls are not shown in this header.
Installed application cards show their app type, install state, and size on one line. Their primary row contains Play, the bottle route selector, and Tools. The Tools panel includes:
- Set Cover to choose custom artwork.
- Add Asset to choose any file, starting in
~/.metalsharp/runtime, and copy it to that application's bottle underdrive_c/metalsharp-assets. - Uninstall to remove the application.
Add Asset is unavailable for applications that are not associated with an app-specific bottle.
Logs
The Logs view presents Live log stream, Crash reports, and Recent log files as three responsive selector buttons. Selecting one opens its content in a bounded panel below the selector row, so the page header and the other selectors stay in place.
The live stream is limited to 1,000 displayed lines. Each time another 1,000-line threshold is reached, the displayed stream clears and begins the next batch while backend polling continues from the last received line.
Development preview
Run npm run preview from app/ to build and open the live Library views against the existing ~/.metalsharp data and the development backend. Preview mode skips the first-launch setup wizard only in an unpackaged development build; packaged app first-run behavior is unchanged.
Install from Source
Build MetalSharp from source without the DMG.
Install from Source
Updated: 2026-08-11
Build MetalSharp from source without using the DMG. Requires macOS 14+ on Apple Silicon.
Prerequisites
# Xcode CLI Tools
xcode-select --install
# Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
eval "$(/opt/homebrew/bin/brew shellenv)"
# Build dependencies
brew install cmake node zstd
Clone
git clone --recurse-submodules https://github.com/aaf2tbz/metalsharp.git
cd metalsharp
Build
# Wine-facing native engine (C++ D3D/Metal layer) - x86_64 for Rosetta 2 PE translation
# Host helpers, host runtime, and the update migrator are built arm64 for the app.
mkdir -p build
cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON
cmake --build build --parallel $(sysctl -n hw.ncpu)
# Rust backend
cd app/src-rust && cargo build --release && cd ../..
# Electron frontend
cd app && npm install && npm run build && cd ..
Prepare the Native Package Tree
npm run pack / npm run dist (and the release workflow) run
npm run prepare:native automatically. For a standalone one-shot native
preparation — CMake configure+build, staging of every shim/executable into
app/native/ via the CMake POST_BUILD commands, host runtime, and must-build
validation — run:
tools/package/prepare-native.sh
The script fails if any must-build artifact is missing, so a fresh checkout can never be packaged without the native surface.
Fetch Runtime Bundles
Downloads MetalSharp-owned runtime assets from the GitHub release: Wine, graphics DLLs (DXMT for DXMT/DXMT(32), vkd3d-proton + DXVK-macOS + VKMT MoltenVK for the VKD3D route), Steam setup files, Mono/FNA support files, Goldberg assets, and other bundled runtime material.
GPTK/D3DMetal is not bundled in MetalSharp release assets. When you save a D3DMetal bottle, MetalSharp installs/trusts Homebrew GPTK separately and uses /Applications/Game Porting Toolkit.app directly.
./tools/dmg/create-bundles.sh
Run
cd app && npx electron .
The Electron main process starts the Rust backend with a fresh per-session
bearer token and attaches it to every backend request. Run the application
through Electron rather than exposing metalsharp-backend directly; requests
without that token are rejected before route handling. The backend binds only
to loopback, and browser-origin requests are limited to the Vite development
origins used by this checkout.
Build a Signed App
For an ad-hoc signed .app (no Apple Developer account needed):
cd app && npm run pack
codesign --force --deep --sign - ../dist/electron/mac-arm64/MetalSharp.app
open ../dist/electron/mac-arm64/MetalSharp.app
For a distributable DMG with hardened runtime (requires Apple Developer certificate):
cd app && npm run dmg
Troubleshooting
cmakefails: Ensure Xcode CLI tools are installed (xcode-select -pshould return a path)npm installfails: Make sure Node 18+ is installed (brew install node)- Missing bundles: Run
./tools/dmg/create-bundles.sh— this downloads MetalSharp-owned runtime assets from GitHub. It does not download GPTK; D3DMetal uses Homebrew GPTK. - App won't open: If you see a Gatekeeper warning, run
xattr -cr /path/to/MetalSharp.app
GPTK (D3DMetal) Guide
Homebrew GPTK setup, D3DMetal bottle actions, prefix seeding, troubleshooting.
GPTK (D3DMetal) Guide
Updated: 2026-07-08
MetalSharp supports Apple's Game Porting Toolkit (GPTK) as the D3DMetal route. This route uses Apple's D3DMetal framework for D3D11/D3D12 translation instead of the MetalSharp DXMT routes (DXMT/DXMT(32)) or the vkd3d-proton VKD3D route.
GPTK is not bundled with MetalSharp. MetalSharp installs and uses the Homebrew GPTK app in place:
/Applications/Game Porting Toolkit.app
Do not copy GPTK into ~/.metalsharp/runtime/gptk, and do not mix GPTK DLLs/frameworks from a different GPTK release with Homebrew GPTK Wine. The D3DMetal route depends on the Homebrew app's matched Wine + D3DMetal payload.
How It Works
The D3DMetal route is intentionally separate from the normal DXMT routes:
- Homebrew-owned GPTK: Uses
/Applications/Game Porting Toolkit.app/Contents/Resources/wine/bin/wine64andwineserver. - Shared GPTK prefix: D3DMetal games share
~/.metalsharp/prefix-gptk/. - Homebrew route DLLs: Prefix seeding copies D3DMetal route DLLs from Homebrew GPTK into
prefix-gptk/drive_c/windows/system32. - Homebrew framework path: Launches set
D3DMETAL_FRAMEWORK_PATHto Homebrew GPTK'sD3DMetal.framework/D3DMetalbinary. - Direct game launch: D3DMetal play launches the game exe through GPTK Wine directly. It does not launch Steam.
Setup Flow
1. Save a D3DMetal bottle
When you save a game as a D3DMetal bottle, MetalSharp enters the explicit D3DMetal/GPTK lane and ensures:
brew trust --cask gcenx/wine/game-porting-toolkit # when Homebrew requires explicit trust
brew install game-porting-toolkit
softwareupdate --install-rosetta --agree-to-license
MetalSharp then verifies the Homebrew GPTK app and exposes the next actions. Saving a D3DMetal bottle does not install VC++ through a Microsoft installer, and it does not seed the prefix yet.
For Elden Ring and ARMORED CORE VI, the save also performs a one-time protected
launcher preparation when start_protected_game.exe exists without
start_protected_game.old: the original launcher is renamed to the .old
backup, and the real game executable (eldenring.exe or armoredcore6.exe)
is copied to the original launcher path. A later save leaves both files alone.
The save also stages libraryfolder.vdf, steam.dll, steamclient.dll,
steamclient64.dll, and the requested steam_appid.txt next to the game and
its selected executable directory. Sources are resolved from the game's
internal or external Steam library, with the MetalSharp Wine Steam install as
a local fallback.
2. Repair Redist
Click Repair Redist in the D3DMetal bottle UI. This does not run vc_redist*.exe.
Instead, MetalSharp copies VC runtime DLLs from its Wine runtime into the GPTK prefix:
- x64 DLLs →
~/.metalsharp/prefix-gptk/drive_c/windows/system32 - x86 DLLs →
~/.metalsharp/prefix-gptk/drive_c/windows/syswow64
It also writes VC runtime registry keys under:
Software\Microsoft\VisualStudio\14.0\VC\Runtimes\x64
Software\Microsoft\VisualStudio\14.0\VC\Runtimes\x86
The action only marks installed after both DLL and registry checks pass.
3. Seed Prefix
Click Seed Prefix after Repair Redist succeeds. MetalSharp will:
- Wineboot
~/.metalsharp/prefix-gptkwith Homebrew GPTK Wine. - Copy Homebrew GPTK route DLLs into prefix
system32: -d3d10.dll-d3d11.dll-d3d12.dll-dxgi.dll-nvapi64.dll-nvngx-on-metalfx.dll - Quarantine app-local D3D/DXGI/NVAPI/Winemetal route shims near the selected game exe so they cannot override the prefix route DLLs.
- Seed Steam/user/config launch material and write D3DMetal launch metadata.
- Verify prefix, route DLL, redist, and launch metadata readiness.
4. Play D3DMetal
When the bottle is ready, Play D3DMetal launches the saved game exe directly through Homebrew GPTK Wine with the proven route shape:
WINEPREFIX=~/.metalsharp/prefix-gptk
WINEARCH=win64
WINEDEBUG=-all
WINEESYNC=1
WINEDLLOVERRIDES=d3d10,d3d11,d3d12,dxgi,nvapi64,nvngx-on-metalfx=n,b;gameoverlayrenderer,gameoverlayrenderer64=d
D3DMETAL_FRAMEWORK_PATH=/Applications/Game Porting Toolkit.app/Contents/Resources/wine/lib/external/D3DMetal.framework/D3DMetal
DYLD_FALLBACK_LIBRARY_PATH=<Homebrew GPTK lib paths>
When to Use D3DMetal vs M11/VKD3D
| D3DMetal | M11/VKD3D | |
|---|---|---|
| Translation | Apple D3DMetal framework | DXMT for M11; vkd3d-proton → Vulkan → MoltenVK for VKD3D (DXMT rollback available) |
| Wine | Homebrew GPTK Wine | MetalSharp Wine |
| Prefix | Shared (prefix-gptk) |
Shared Wine Steam prefix with bottle preflight |
| Best for | Games that need Apple's D3DMetal behavior | Most games, better compatibility tracking |
Use M11 or VKD3D as the default. Switch to D3DMetal if a game has specific rendering issues on the DXMT path or if developer notes say to use GPTK.
GPTK Prefix Location
~/.metalsharp/prefix-gptk/
├── drive_c/
│ ├── windows/
│ │ ├── system32/ Homebrew GPTK route DLLs + x64 VC runtime DLLs
│ │ └── syswow64/ x86 VC runtime DLLs
│ └── metalsharp/d3dmetal/<appid>/launch.json
├── dosdevices/
├── system.reg
└── user.reg
Component Repair
The D3DMetal bottle UI exposes these user-facing actions:
| Action | Purpose |
|---|---|
| Repair Redist | Copy x64+x86 VC runtime DLLs into the GPTK prefix and write registry keys. |
| Seed Prefix / Repair Seed | Wineboot the GPTK prefix, copy Homebrew GPTK route DLLs into system32, quarantine local route shims, and seed launch metadata. |
| Play D3DMetal | Launch the game exe through Homebrew GPTK Wine. |
The underlying bottle component IDs remain compatible with existing bottle tooling (gptk, rosetta, gptk_prefix, vcrun2019_x64, vcrun2019_x86).
Troubleshooting
- Homebrew GPTK missing: Save the D3DMetal bottle again or run
brew install game-porting-toolkitmanually. - Homebrew cask trust failure: Run
brew trust --cask gcenx/wine/game-porting-toolkit, then save the D3DMetal bottle again. - Rosetta missing: Run
softwareupdate --install-rosetta --agree-to-licenseor save the D3DMetal bottle again. - VC++ missing: Click Repair Redist. MetalSharp copies bundled runtime DLLs and writes registry keys; it does not launch a Microsoft redist installer for this lane.
- Prefix not ready: Click Seed Prefix or Repair Seed.
- Game does not see a D3D12 adapter: Re-run Seed Prefix so Homebrew GPTK route DLLs are copied into prefix
system32and app-local D3D/DXGI/NVAPI shims are quarantined. - External drive not visible: Add a dosdevice symlink if needed, for example:
ln -sf /Volumes/YourDrive ~/.metalsharp/prefix-gptk/dosdevices/z:
Supported Games
Working/blocked game evidence and recommended public routes.
Games Supported
Updated: 2026-08-08
Tested and working games organized by pipeline. Only games confirmed playable are listed.
Test System
Games were tested from an external 1TB M.2 SSD (~5000 MB/s over USB-C 3.1) on an M4 MacBook Air with 16GB RAM.
Pipelines
| Pipeline | Backend | Use |
|---|---|---|
| D3DMetal | Homebrew GPTK / Apple D3DMetal | D3D11/D3D12 via Apple's D3DMetal framework. GPTK is installed through Homebrew and is not bundled by MetalSharp. |
| VKD3D | vkd3d-proton + DXVK-macOS | Complete Vulkan pipeline: D3D9/D3D10/D3D11/D3D12 to Metal via Vulkan → MoltenVK |
| DXMT | DXMT | D3D10/D3D11 to Metal (x86_64) |
| DXMT(32) | DXMT | D3D10/D3D11 to Metal (32-bit / i386) |
| Mono/FNA | MonoKickstart + FNA | XNA/FNA/MonoGame via native Mono runtime |
Internal routes (dxmt auto-detect, Wine Steam, macOS Steam, wine_bare) remain backend machinery and are not shown in bottle selectors.
D3DMetal
Games running through Homebrew GPTK and Apple's D3DMetal pipeline. D3DMetal bottles use the explicit Save → Repair Redist → Seed Prefix → Play D3DMetal flow, with route DLLs copied from /Applications/Game Porting Toolkit.app into the shared GPTK prefix. Mainly for Dx12 Gaming.
| Game | AppID | Notes |
|---|---|---|
| Elden Ring | 1245620 | Offline play. |
| ARMORED CORE VI FIRES OF RUBICON | 1888160 | Offline play. |
| High On Life | 1583230 | Also works on VKD3D. |
| Cyberpunk 2077 | 1091500 | Offline play. |
| Ghostrunner | 1139900 | D3DMetal route confirmed. |
| Control - Ultimate Edition | 870780 | |
| Star Wars Jedi: Fallen Order | 1172380 | |
| Shadow Of The Tomb Raider | 750920 |
VKD3D — D3D12 to Metal
The primary/default D3D12 route runs on the vkd3d-proton stack (D3D12 → Vulkan → VKMT MoltenVK → Metal), with the legacy DXMT D3D12 stack available via the vkd3dBackend setting. Confirmed working games are listed in the D3DMetal section where noted (e.g. High On Life "Also works on VKD3D") and via the shipped MTSP rules for tested D3D12 titles.
DXMT — D3D10/D3D11 to Metal
| Game | AppID | Notes |
|---|---|---|
| Repo | 3241660 | |
| Cult of the Lamb | 1313140 | |
| The Witcher 3: Wild Hunt | 292030 | |
| The Wilds | 1028590 | |
| The Long Dark | 305620 | Ultra settings verified. |
| Subnautica | 264710 | |
| Subnautica: Below Zero | 848450 | |
| Rain World | 312520 | |
| Reminiscence | 1675140 | |
| Hollow Knight | 367520 | |
| Party Animals | 1260320 | Save DXMT bottle, launch direct with Steam. |
| Dave the Diver | 1868140 | |
| Totally Accurate Battle Simulator | 508440 | |
| Skul: The Hero Slayer | 1147560 | |
| Crab Game | 1782210 | |
| MECCHA CHAMELEON | 4704690 | |
| SkyIsland | 2302640 | |
| Lethal Company | 1966720 | |
| Insurgency | 222880 | Launch with -steam -secure flags. |
| Graveyard Keeper | 599140 | |
| Brawlhalla | 291550 | |
| Black Myth: Wukong | 2358720 | Compatibility Mode. |
| Beam.ng Drive | 1067430 | |
| Ball X Pit | 2062430 | |
| Schedule I | 3164500 | |
| Nine Sols | 1809540 | |
| Skekiro Shadows Die Twice | 814380 | |
| Sons Of The Forest | 1326470 | |
| Thronefall | 2239150 | |
| 9 Kings | 2784470 | |
| Amid Evil | 673130 | |
| Borderlands 3 | 397540 | |
| Crab Champions | 774801 | |
| Plate Up! | 1599600 | |
| Rv There Yet? | 3949040 | |
| UltraKill | 1229490 | |
| Velheim | 892970 | |
| Untitled Goose Game | 837470 |
DXMT (32-bit) — D3D10/D3D11 to Metal, 32-bit prefix route
| Game | AppID | Notes |
|---|---|---|
| Inscryption | 1092790 | Binary: Inscryption.exe. |
| Hades | 1145360 | Binary: x86/Hades.exe. |
| Balatro | 2379780 | |
| The Binding of Isaac: Rebirth | 250900 | Binary: isaac-ng.exe. Windows OpenGL path verified on an internal drive; requires compatibility-profile context handling. |
DXMT — D3D10 to Metal (covered above)
| Game | AppID | Notes |
|---|---|---|
| Mind Scanners | 1389550 |
VKD3D — D3D9/D3D10/D3D11/D3D12 to Metal (Vulkan)
| Game | AppID | Notes |
|---|---|---|
| Mirror's Edge | 17410 | |
| Half-Life 2 | 220 | |
| Portal 2 | 620 | Steam Emu supported. |
| Among Us | 945360 | Steam online play. |
| Team Fortress 2 | 440 | Steam online play. VAC works. |
| Undertale | 391540 |
Mono/FNA — XNA/FNA/MonoGame/Unity-Mono
| Game | AppID | Notes |
|---|---|---|
| Celeste | 504230 | FNA/XNA assets, FMOD shims, Steamworks shim. x86_64 Mono |
| Necesse | 1169040 | Classic FNA (root FNA.dll + XNA names), baseline Mono |
| Terraria | 105600 | XNA lane (root-level XNA assemblies, no _data/Managed — classic layout), gdiplus/faudio stubs, prebuilt launcher + offline patcher |
| Stardew Valley | 413150 | MonoGame (root-level MonoGame.Framework.dll, net6.0, no _data/Managed), GOG Galaxy + Steamworks deps detected; modern Mono requirement |
| DREDGE | 1562430 | Unity-Mono (Unity 2021.3.5f1, MonoBleedingEdge, x86 PE32), version-matched Unity Mono runtime deployed on save; dual SDKs (Steamworks.NET + Galaxy) |
Discovery & routing
The mono route classifies each installed game by evidence (mono_profile.rs):
Unity-Mono (UnityPlayer.dll + MonoBleedingEdge + *_Data/Managed, Unity
version read from globalgamemanagers), Unity IL2CPP (GameAssembly.dll —
routed to Wine/DXMT, never mono), FNA, MonoGame, XNA, MonoKickstart
(<exe>.bin.osx / osx/libmonosgen), and bare .NET. Bottle save deploys the
version-matched payloads (Unity Mono runtime lane, XNA assembly set, SDL3,
Carbon per profile) and records receipts; launch runs a pre-flight readiness
check (mono arch incl. Rosetta for x86, shims, SDL3, Unity lane) and
dispatches MonoKickstart games to the kickstart launcher.
Mono runtime versions
The app installs baseline Wine Mono with the program; the in-app Mono button upgrades to 11.2.0. Per-game requirement (baseline vs modern) is surfaced in the profile explainer: Unity 2021+/SDL3/MonoGame titles need the modern runtime.
Notes
- Game cards can be tested through the route dropdown in each game's bottle workspace.
- Shader caches are per-appid and can be cleared from Settings.
- Wine Steam remains the background Steam client for installed Windows Steam games.
- Installed Wine Steam games create
steam_<appid>bottle records for runtime asset/component preflight before launch. - Env-dependent Steam routes keep Wine Steam alive as the background client, then launch the game executable directly with the selected pipeline, bottle prefix, route env, and Steam identity variables.
- D3DMetal is the exception to normal bundled-runtime routing: it uses Homebrew GPTK, a shared
~/.metalsharp/prefix-gptk, copied x64+x86 VC runtime DLLs, and Homebrew-matched D3DMetal route DLLs in prefixsystem32.
Proof Targets
Local evidence targets and runtime proof notes.
MetalSharp Proof Targets
Updated: 2026-07-08
Status: Phase 11 active
The roadmap has to be driven by reproducible evidence. configs/proof-targets.json is the starting ledger for installer, launcher, Steam runtime, and anti-cheat proof targets.
Target Groups
- Installer baseline: Minecraft Launcher, GOG offline installer, Itch/Unity demo, Unreal demo.
- Launcher baseline: EA App, Ubisoft Connect, Battle.net, Epic Games Launcher.
- Steam runtime baseline: known DXMT, VKD3D, Steam CommonRedist, and Steam third-party launcher cases.
- Anti-cheat baseline: EAC title with Proton support, BattlEye title with Proton support, policy-blocked title, Windows-kernel-only title.
Evidence Fields
Every target should record:
- source
- appid, installer path, bottle id, compatdata path, runtime profile, launch pipeline, and prefix path
- install result
- launcher open result
- login/session result
- child game process result
- game launch result
- graphics route
- audio status
- input status
- online/session status
- anti-cheat status
- failure classification
- failure evidence paths
- next action
Rule
Do not mark a target as working from vibes. A target moves forward only when the matching bottle log, compatdata record, Launch Doctor report, crash log, or screenshot exists.
Phase 11 Run Log
2026-05-19: Minecraft Launcher installer
Route used:
POST /sharp-library/install {"srcPath":"~/Downloads/MinecraftInstaller.exe","name":"Minecraft Launcher Phase 11"}
Observed result:
- MetalSharp created/reused bottle
installer_057475b8830b64bc. - The bottle manifest classifies the target as
runtime_profile=java_launcher,installer_kind=java,arch=win32. - The launcher route started on the legacy D3D9 pipeline.
- The process exited quickly and no final launcher app was detected.
- Bottle Doctor reports the prefix exists, but tracked components are not ready and app detection is still empty.
- A manual bare-Wine rerun in the same bottle reproduces the same native crash, so this is not only a graphics-route issue.
- A manual rerun with
WINEDLLOVERRIDES=mscompatdb=dstill reproduces the same native crash. geckorepair launched, logged, and is now detected from Wine'ssystem32/gecko/syswow64/geckolocations.corefontsis now repaired by mapping locally installed host fonts into the bottle font directory.webview2remains a missing local runtime asset.- A rerun after font/Gecko cleanup still exits with the same
mscompatdb/ Mono native crash. - The launch log records repeated
mscompatdbload attempts followed by a native crash report in the Mono/native runtime path.
Evidence:
~/.metalsharp/bottles/installer_057475b8830b64bc/bottle.json~/.metalsharp/bottles/installer_057475b8830b64bc/logs/launch-1779249524.log~/.metalsharp/bottles/installer_057475b8830b64bc/logs/launch-1779250143.log~/.metalsharp/bottles/installer_057475b8830b64bc/logs/manual-winebare-1779249756.log~/.metalsharp/bottles/installer_057475b8830b64bc/logs/manual-disable-mscompatdb-1779250267.log~/.metalsharp/bottles/installer_057475b8830b64bc/logs/component-corefonts-1779250127.log~/.metalsharp/bottles/installer_057475b8830b64bc/logs/component-gecko-1779249576.logPOST /bottles/doctor {"id":"installer_057475b8830b64bc"}
Failure classification:
runtime_bugmissing_runtime_asset
Next action:
Fix the Wine Mono/mscompatdb crash and map WebView2 assets, then rerun the installer through the same bottle. This is now a concrete Phase 11 blocker rather than a generic "installer.exe does not launch" report.
2026-05-19: Minecraft Launcher legacy MSI route
Route used:
POST /sharp-library/install {"srcPath":"~/.metalsharp/runtime/redist/Minecraft/MinecraftInstaller.msi","name":"Minecraft Launcher Legacy MSI"}
POST /sharp-library/import-bottle-app {"bottleId":"installer_6a0a76294c1d1364","exePath":".../MinecraftLauncher.exe","name":"Minecraft Launcher"}
POST /sharp-library/launch {"id":"bottle_app_ccf06adb8b050608","engine":"wine_bare"}
Observed result:
- The official Mojang MSI installed successfully into bottle
installer_6a0a76294c1d1364. - The installed launcher was detected at
C:\Program Files (x86)\Minecraft Launcher\MinecraftLauncher.exe. - The launcher created bottle-local state under
C:\users\alexmondello\AppData\Roaming\.minecraft. - CEF initialized successfully, but the initial launcher window rendered blank.
- General CEF compatibility was then applied by preserving
MinecraftLauncher_real.exeand replacingMinecraftLauncher.exewith an architecture-matched wrapper. - The wrapper launches the real executable with
--in-process-gpu --disable-gpu, matching the working Steam CEF strategy. - The wrapper also deploys
metalsharp-cefchildhook.dll; local hook logs show it loads and patches launcher/moduleCreateProcessA/W,GetProcAddress, andShellExecuteimports. - The wrapped launcher still renders blank. After clearing Minecraft's
launch_attempts.jsonretry guard, logs show CEF initialization, successful network calls, successful XAL token initialization, and the main window opening. - The remaining blocker is now lower than top-level wrapping: Minecraft logs renderer/GPU subprocess command lines from
MinecraftLauncher_real.exe, but those launches are not yet passing through the hooked process creation or shell execution paths. - Minecraft's launcher binary exposes
disableGPU,disableGPUCommandLine,disableGPUForced, andadditionalCEFOptionsstrings. A bottle-local settings JSON attempt was accepted but did not appear in emitted CEF child command lines.
Evidence:
~/.metalsharp/bottles/installer_6a0a76294c1d1364/bottle.json~/.metalsharp/bottles/installer_6a0a76294c1d1364/logs/launch-1779252105.log~/.metalsharp/bottles/installer_6a0a76294c1d1364/logs/launch-1779252174.log~/.metalsharp/bottles/installer_6a0a76294c1d1364/prefix/drive_c/users/alexmondello/AppData/Roaming/.minecraft/launcher_cef_log.txt~/.metalsharp/bottles/installer_6a0a76294c1d1364/prefix/drive_c/Program Files (x86)/Minecraft Launcher/.ms_cef_compat_MinecraftLauncher~/.metalsharp/bottles/installer_6a0a76294c1d1364/prefix/drive_c/users/alexmondello/AppData/Local/Temp/metalsharp-cefchildhook.log
Failure classification:
store_bootstrapper_mismatchfor the Microsoft Store.exeroutecef_rendering_pathfor the blank launcher window after MSI installcef_child_creation_pathfor the post-install blank UI state
Next action:
Finish the Minecraft CEF child-process path by either reaching the lower-level process creation call or mapping Minecraft's native CEF preference surface correctly, then use the working MSI/bottle install to continue toward starting Minecraft Java from the same bottle.
Mono follow-up:
- See
docs/runtime/mono-runtime-lanes.md. - Keep Minecraft in the Wine-bottle lane for now because it is a Windows launcher/bootstrapper, not a native FNA game.
- Use the old Terraria/Celeste native Mono lanes as selective fallback profiles for known FNA/XNA games, not as a global replacement for Wine Mono.
2026-05-19: EA App installer
Route used:
POST /sharp-library/install {"srcPath":"~/Downloads/EAappInstaller.exe","name":"EA App"}
POST /bottles/repair-component {"id":"installer_16c2e7d7a6e2d5e7","component":"dotnet48"}
POST /bottles/relaunch-installer {"id":"installer_16c2e7d7a6e2d5e7"}
Observed result:
- MetalSharp created bottle
installer_16c2e7d7a6e2d5e7. - The classifier mapped EA to
runtime_profile=webview. - Before this fix, the 32-bit PE fallback still launched the known launcher through a graphics route.
- The EA bootstrapper downloaded and verified
EAapp-13.700.0.6213-4218.msi. - After the visible install bar completed, the MSI failed with
0x80070643. - EA reports that MSI failure as
INST-14-1603. - The extracted MSI payload includes
Microsoft.Deployment.WindowsInstaller.dllandCustomAction.configwith<supportedRuntime version="v4.0" />, so EA is running .NET v4 custom actions during install. - Installing
dotnet48repaired the bottle component state, but the relaunch still reproducedINST-14-1603. - A fresh proof bottle,
installer_16c2e7d7a6e2d5e7_fresh_1779256322130, confirmed the relaunch path now stays inside the selected proof bottle instead of falling back to the stable source-path bottle. - In the fresh proof bottle,
corefonts,dotnet48,gecko,vcrun2019, andwebview2are installed. - The local WebView2 resolver now honors
~/.metalsharp/runtime/redist/WebView2/, matching the documented redist path. - Even with WebView2 present, the direct MSI log files are created but remain zero bytes, so Wine/MSI appears to fail before the MSI logger records the real custom-action body.
- The outer WiX bootstrapper still maps the failure to
0x80070643 aka 'INST-14-1603'.
Evidence:
~/.metalsharp/bottles/installer_16c2e7d7a6e2d5e7/bottle.json~/.metalsharp/bottles/installer_16c2e7d7a6e2d5e7/prefix/drive_c/users/alexmondello/AppData/Local/Temp/EA_app_20260519233518.log~/.metalsharp/bottles/installer_16c2e7d7a6e2d5e7/prefix/drive_c/users/alexmondello/AppData/Local/Temp/EA_app_20260519233838.log~/.metalsharp/bottles/installer_16c2e7d7a6e2d5e7/prefix/drive_c/users/alexmondello/AppData/Local/Temp/msi56c0.tmp-/CustomAction.config~/.metalsharp/bottles/installer_16c2e7d7a6e2d5e7_fresh_1779256322130/bottle.json~/.metalsharp/bottles/installer_16c2e7d7a6e2d5e7_fresh_1779256322130/prefix/drive_c/users/alexmondello/AppData/Local/Temp/EA_app_20260519235554.log~/.metalsharp/bottles/installer_16c2e7d7a6e2d5e7_fresh_1779256322130/prefix/drive_c/users/alexmondello/AppData/Local/Temp/EA_app_20260519235554_000_EAapp_13.700.0.6213_ae714772e_4bfd0163_4218.msi.log~/.metalsharp/bottles/installer_16c2e7d7a6e2d5e7_fresh_1779256322130/prefix/drive_c/users/alexmondello/AppData/Local/Temp/EA_app_20260520000339.log~/.metalsharp/bottles/installer_16c2e7d7a6e2d5e7_fresh_1779256322130/logs/component-webview2-1779256915.log~/.metalsharp/bottles/installer_16c2e7d7a6e2d5e7_fresh_1779256322130/prefix/drive_c/users/alexmondello/AppData/Local/Temp/msi2e9e.tmp-/CustomAction.config
Failure classification:
ea_inst_14_1603msi_custom_action_failurelauncher_bootstrapper_needs_bare_winewebview_profile_needs_dotnet48msi_log_emptywebview2_installed_not_sufficient
Next action:
Inspect Wine MSI custom-action service/elevation behavior around per-machine package cache writes. WebView2 is now installed in the proof bottle, so the blocker is below the missing-runtime layer.
2026-05-19: BattlEye BERCon artifact
Observed result:
~/Downloads/BERCon.exeis a PE32 console app, not a service installer.- Embedded strings identify it as
BattlEye RCon v0.94 beta. - The binary exposes remote-console command-line help:
BERCon [-host IP ADDRESS / HOSTNAME] [-port PORT] [-pw PASSWORD]. - PE imports are limited to console/process basics plus
WS2_32.dll; there is no visibleBEService, driver, service-install, or anti-cheat runtime payload in this artifact.
Evidence:
file ~/Downloads/BERCon.exeshasum -a 256 ~/Downloads/BERCon.exestrings -a ~/Downloads/BERCon.exei686-w64-mingw32-objdump -p ~/Downloads/BERCon.exe
Failure classification:
artifact_mismatch
Next action:
Do not use BERCon as proof that the BattlEye runtime is installed. Pick a real Steam title that ships BattlEye/BEService assets, then capture the launch-time service behavior from the game bottle and Launch Doctor.
2026-05-20: Ubisoft Connect installer artifact
Observed result:
~/Downloads/UbisoftConnectInstaller.exeis a PE32 GUI installer.fileidentifies it as a Nullsoft Installer self-extracting archive.- SHA-256:
7a6942ef2c96ed516b4e06256b03ac0bb994de22702e944f4ebf2c2507f31a24 - Fresh proof bottle:
~/.metalsharp/bottles/installer_61e534a6d260c814_fresh_1779258414613 - Install completed enough to stage
UbisoftConnect.exeunderProgram Files (x86)/Ubisoft/Ubisoft Game Launcher/. POST /launcher/evidence {"family":"ubisoft"}reportsubisoft_auto_started_then_crash_reporter.- The installer auto-started Ubisoft Game Launcher
171.0.13174. - There is not yet a recorded direct post-install launch of
UbisoftConnect.exe; the next proof pass should launch that detected executable directly after runtime repair. - The launcher auto-started as
upc.exe uplay://open, thenUplayCrashReporter.exeappeared. - Bottle Doctor still reports missing
corefontsandwebview2. - App detection initially surfaced launcher plumbing (
UplayService,UplayWebCore,UpcElevationService,upc.exe), so the filter now excludes those helpers and preservesUbisoftConnect.exe.
Failure classification:
nsis_bootstrapperlauncher_started_then_crash_reporterwebview2_missing_after_installapp_detection_helper_filter
Evidence:
~/.metalsharp/bottles/installer_61e534a6d260c814_fresh_1779258414613/logs/launch-1779258414.log~/.metalsharp/bottles/installer_61e534a6d260c814_fresh_1779258414613/prefix/drive_c/Program Files (x86)/Ubisoft/Ubisoft Game Launcher/logs/launcher_log.txt~/.metalsharp/bottles/installer_61e534a6d260c814_fresh_1779258414613/prefix/drive_c/Program Files (x86)/Ubisoft/Ubisoft Game Launcher/logs/client_crash_reporter.txt
Next action:
Repair corefonts and webview2 in the fresh proof bottle, relaunch UbisoftConnect.exe directly, then compare the crash path against EA's INST-14-1603. The useful signal is whether Ubisoft fails at WebView/CEF rendering, service/elevation, or post-install launcher launch.
2026-05-20: EA and Ubisoft launcher evidence endpoint
MetalSharp now has a launcher evidence report:
POST /launcher/evidence
{"family":"ea"}
POST /launcher/evidence
{"family":"ubisoft"}
Current EA status:
ea_msi_1603- EA reaches MSI apply and fails with
0x80070643 / INST-14-1603. - The failing package is
EAapp_13.700.0.6213_ae714772e_4bfd0163_4218.msi. - Direct EA launcher launch is not expected yet because no launcher executable was installed.
Current Ubisoft status:
ubisoft_auto_started_then_crash_reporterUbisoftConnect.exeexists under the proof bottle.- The installer auto-started Ubisoft Game Launcher
171.0.13174. - Crash reporter evidence is present.
- No direct post-install launch of
UbisoftConnect.exehas been recorded yet.
2026-05-20: Game-local anti-cheat installer scan
Observed local Steam installs:
/Volumes/AverySSD/SteamLibrary/steamapps/common/ELDEN RING/Game/EasyAntiCheat/easyanticheat_eos_setup.exe/Volumes/AverySSD/SteamLibrary/steamapps/common/ELDEN RING/Game/EasyAntiCheat/install_easyanticheat_eos_setup.bat
The Elden Ring install script runs:
EasyAntiCheat_EOS_Setup.exe install 773d3a68f76f4b2ebebc5b4127bbad3e
No real BattlEye service/runtime payload was found in the current AverySSD Steam library scan. BERCon.exe remains excluded because it is an RCon client, not a service installer.
Implementation result:
- Steam game bottle runtime asset detection now sees EAC/BattlEye assets outside
_CommonRedist. - Bottle Doctor can report those assets as
game_runtime_assetcomponent sources. - Component repair can resolve game-local EAC/BattlEye installers instead of looking only in global redistributable folders.
Next action:
Use dry-run repair against steam_1245620 first, then attempt an actual EAC EOS install only after the Doctor resolves the expected local asset path.
2026-05-20: Rubicon EAC EOS proof
Observed local Steam install:
- Appid:
1888160 - Game:
ARMORED CORE VI FIRES OF RUBICON - Install path:
/Volumes/AverySSD/SteamLibrary/steamapps/common/ARMORED CORE VI FIRES OF RUBICON
Important correction:
- Rubicon ships Easy Anti-Cheat EOS, not BattlEye.
- No
BEService,BEClient,BEDaisy, or BattlEye install script was found in the Rubicon game folder.
Detected EAC assets:
/Volumes/AverySSD/SteamLibrary/steamapps/common/ARMORED CORE VI FIRES OF RUBICON/Game/EasyAntiCheat/easyanticheat_eos_setup.exe/Volumes/AverySSD/SteamLibrary/steamapps/common/ARMORED CORE VI FIRES OF RUBICON/Game/EasyAntiCheat/install_easyanticheat_eos_setup.bat/Volumes/AverySSD/SteamLibrary/steamapps/common/ARMORED CORE VI FIRES OF RUBICON/Game/EasyAntiCheat/eacchecker.bat
The install script runs:
EasyAntiCheat_EOS_Setup.exe install 789399aada914e66bb3c3facebc5d709
Implementation result:
- The first dry-run exposed a resolver bug: it picked
eacchecker.batbefore the real install script. - Resolver now parses EAC scripts and only accepts a script when it contains a real
EasyAntiCheat_EOS_Setup.exe install <product-id>command. - Runtime asset paths are canonicalized before storage, because Wine rejected
//Volumes//...installer paths withShellExecuteEx failed: File not found. - After normalization,
steam_1888160dry-run repair resolved the canonical EAC setup executable. - Actual repair started
easyanticheat_eos_setup.exe install 789399aada914e66bb3c3facebc5d709in~/.metalsharp/prefix-steam. - EAC service log reports an elevation relaunch and
Operation 1 completed successfully. ~/.metalsharp/prefix-steam/drive_c/Program Files (x86)/EasyAntiCheat_EOS/EasyAntiCheat_EOS.exenow exists.- Bottle Doctor for
steam_1888160reportsBottle runtime checks passed.
Evidence:
~/.metalsharp/bottles/steam_1888160/logs/component-easyanticheat_eos-1779259468.log~/.metalsharp/bottles/steam_1888160/logs/component-easyanticheat_eos-1779259573.log~/.metalsharp/prefix-steam/drive_c/users/alexmondello/AppData/Roaming/EasyAntiCheat/service.log
Next action:
Launch Rubicon through the Steam game bottle and capture whether the game reaches EAC bootstrap, offline mode, or an online/vendor block.
2026-05-21: Protected EAC module mapping proof
Read-only anti-cheat endpoint probes were run against the completed AverySSD Steam installs for:
1245620/ELDEN RING1888160/ARMORED CORE VI FIRES OF RUBICON
Install evidence:
- Both manifests report
StateFlags 4. - Both protected launchers and game executables have valid
PEheaders. - Both game folders contain game-local
EasyAntiCheat/Settings.jsonpluseasyanticheat_eos_setup.exe.
Important diagnostic correction:
- The shared Wine Steam prefix can contain EAC launcher logs for multiple games.
- Anti-cheat evidence must be scoped by appid and the game-local EAC
productid/deploymentid. - Without that filter, Elden Ring probes can accidentally summarize Rubicon's EAC launcher log.
Current protected-launch status:
- Elden Ring resolves to product
773d3a68f76f4b2ebebc5b4127bbad3eand deploymentd2842e93d53b4c0c98a8f963ebb4c222. - Rubicon resolves to product
789399aada914e66bb3c3facebc5d709and deploymentb978a2afd2254108bbb39201d0a24a98. - Both titles independently select EAC module target
linux64. - Both titles reach Wine module mapping under
wine-11.5. - Both titles fail with launcher exit
206andFailed to load the anti-cheat module.
Current substrate decision:
linux_module_on_darwin_boundaryrequires_linux_user_space_substrate_or_vendor_macos_asset
Next action:
Prototype harmless loader/runtime contract probes for the Linux user-space boundary before changing protected launch behavior.
Implementation follow-up:
POST /steam/anticheat-contract-proberecords a harmless host contract probe.- The probe uses synthetic temporary data only; it does not load protected anti-cheat modules.
- The probe reports memory mapping/protection behavior, synthetic ELF direct-load behavior, Wine loader/wineserver state, and the scoped game EAC identity.
- For both Elden Ring and Rubicon, the expected macOS status is
linux_elf_host_gap_confirmeduntil MetalSharp has either a truthful Linux user-space substrate or vendor-supported macOS module assets.
Local probe result:
- Elden Ring and Rubicon both return
linux_elf_host_gap_confirmed. - Anonymous memory can transition from writable to executable in the backend process.
- The host dynamic loader rejects a synthetic ELF
.soas not valid Mach-O. - This narrows the next implementation target to the Linux ELF module host/substrate boundary rather than M11/VKD3D graphics routing.
Runtime Bundles & Steam Routing
Bundle provenance, wrapper deployment, and the Wine Steam route.
Runtime Bundles and Steam Routing
Updated: 2026-08-11
This is the operational contract for bundle provenance and Wine Steam launch routing.
Bundle Provenance
Runtime assets are downloaded from the bundles GitHub release into app/bundles/ during app packaging and into ~/.metalsharp/cache/bundles/ during installer fallback downloads.
Every archive is published with its SHA-256 in the metalsharp-bundle-manifest.tsv asset of the bundles release. Consumers that execute bundle payloads pin those digests rather than trusting the download channel: the production installer (scripts/install-metalsharp-wine-runtime.sh) pins its archive digests inline, and the CI VKD3D gate (tools/ci/vkd3d-check.sh) verifies each downloaded bundle against the pinned manifest tools/ci/vkd3d-bundle-hashes.tsv via tools/ci/verify-bundle-sha256.sh, failing the gate on any mismatch. Update that manifest in the same change that rotates the bundles release.
The manifest-tracked assets are listed in tools/bundles/asset-manifest.tsv. The verifier checks that each tarball exists and contains the expected baby-named root.
Current split bundle roots:
| Asset | Why it is guarded |
|---|---|
metalsharp-electron.tar.zst |
Contains electron/, the built Electron application payload. |
metalsharp-graphics-dll.tar.zst |
Contains Graphics/dll/, the DXMT surfaces (dxmt) and the VKD3D stack lanes (vkd3d-proton, dxvk, moltenvk-vkmt). |
metalsharp-runtime.tar.zst |
Contains runtime/, the Wine runtime, host ABI, and backend executable. |
metalsharp-assets.tar.zst |
Contains assets/, Mono, GPTK, DXVK, Goldberg, EAC toggle, shims, and runtime support assets. |
metalsharp-scripts-tools.tar.zst |
Contains scripts/tools/, updater scripts, configs, native tools, and CEF helpers. |
metalsharp-steam.tar.zst |
Contains steam/, the Steam installer and Steam CEF wrapper assets. |
metalsharp-d3d12-developer-sdk.tar.zst |
Contains developer-sdk/d3d12/, the D3D12 contracts, probes, scripts, docs, staged developer Wine runtime, DXMT DLLs, Winemetal bridge files, and runtime provenance manifest. |
Verification commands:
tools/bundles/verify-bundles.sh --require mac
tools/bundles/verify-bundles.sh --release
tools/bundles/verify-developer-sdk.sh app/bundles/metalsharp-d3d12-developer-sdk.tar.zst
Installer Acceptance Rules
The installer consumes the split runtime tarballs by root name. metalsharp-graphics-dll.tar.zst is the only source for the active graphics runtime payloads used by VKD3D/DXMT.
The graphics bundle has five runtime surfaces:
Graphics/dll/dxmt/ -> DXMT payload for DXMT/DXMT(32)
Graphics/dll/dxmt-vkd3d/ -> DXMT VKD3D rollback payload (also supplies the
shared nvapi64/nvngx GPU vendor stubs)
Graphics/dll/vkd3d-proton/ -> default VKD3D D3D12 stack (d3d12.dll + d3d12core.dll)
Graphics/dll/dxvk/ -> shared D3D9/D3D10/D3D11/DXGI surface; dxgi.dll for VKD3D
Graphics/dll/moltenvk-vkmt/ -> VKMT patched MoltenVK (libMoltenVK.dylib + ICD)
After install those surfaces live under:
~/.metalsharp/runtime/wine/lib/dxmt/
~/.metalsharp/runtime/wine/lib/dxmt-vkd3d/
~/.metalsharp/runtime/wine/lib/vkd3d-proton/
~/.metalsharp/runtime/wine/lib/dxvk/
~/.metalsharp/runtime/wine/lib/moltenvk-vkmt/
Installed DXMT runtime state is recorded in:
~/.metalsharp/runtime/wine/lib/dxmt/metalsharp-dxmt-runtime.json
Do not trust a runtime by version string alone. Check the manifest, required DLLs, the vkd3d-proton/DXVK/MoltenVK lane artifacts (for the default VKD3D backend) or the dxmt-vkd3d sidecars (for the DXMT rollback), and source archive hash when diagnosing deployment drift.
Downloaded Installer Artifact Integrity
Every downloaded installer that can reach an install or privileged update path must have a non-zero expected size and a SHA-256 digest before it is accepted:
- Update DMGs use the GitHub release asset digest and URL/size/hash sidecar in
~/.metalsharp/cache/updates/. A cache entry is usable only when the sidecar exactly matches the current release metadata and the DMG is hashed again. - Wine Mono 11.2.0 uses the pinned MSI size and SHA-256 in
app/src-rust/src/mono.rs. Partial or mismatched.msi.partfiles are removed before retrying. - SteamSetup.exe is staged only from the verified
metalsharp-steambundle; the bundle archive and extracted installer are both pinned by size and SHA-256 inapp/src-rust/src/steam.rs.
The detached updater receives the DMG size and digest from the backend and
rechecks them immediately before mounting. hdiutil verify remains an
additional structural check, not a replacement for the content hash.
Steam Launch Route
The app launches Wine Steam through:
Renderer button -> POST /steam/launch -> steam::launch_wine_steam()
Game launches that need an explicit public route use VKD3D/DXMT/DXMT(32)/Mono-FNA route IDs. Raw dxmt remains an internal auto-router and legacy compatibility value.
Renderer Play -> POST /steam/launch-game {"launchMethod":"vkd3d"} -> prepare_steam_pipeline_env() -> direct game launch with Wine Steam alive in the background
Wine Steam must be launched by the backend so it gets the managed Wine prefix, runtime library env, DLL overrides, and wrapper deployment. Launching Steam.exe directly from a shell is not equivalent to pressing the app button.
Steam-model titles use the real Steam DLLs instead of Goldberg. For those titles the launcher stages the real Steam API DLLs and the Steam client/overlay components next to the selected executable when they are available:
steam_api64.dll
steam_api.dll
steamclient64.dll
steamclient.dll
GameOverlayRenderer64.dll
GameOverlayRenderer.dll
This applies to Steam launch-model titles such as Party Animals and Source games without forcing -secure onto titles that only need -steam.
Steam Wrapper Rules
Before launching Steam, MetalSharp calls ensure_steam_launch_ready() and redeploys steamwebhelper.exe when Steam has overwritten it. Steam assets come from metalsharp-steam.tar.zst, and the backend only accepts the extracted wrapper if it matches STEAMWEBHELPER_WRAPPER_SHA256 in app/src-rust/src/steam.rs.
The expected deployed Steam CEF layout is:
~/.metalsharp/prefix-steam/drive_c/Program Files (x86)/Steam/bin/cef/cef.win64/
├── steamwebhelper.exe # MetalSharp wrapper
├── steamwebhelper_real.exe # Steam's original helper
└── .ms_wrapper_deployed
If Steam login stops rendering after bundle or wrapper work, verify the wrapper hash before changing launch args.
Mono Runtime Lanes
Mono/FNA/XNA route boundaries and native Mono lane details.
MetalSharp Mono Runtime Lanes
Updated: 2026-07-08
Status: June 2026 route hardening active
MetalSharp now treats Mono/FNA as a first-class public route, not a global dependency and not a synonym for Wine Mono. The visible selector shows one Mono/FNA option while the launcher chooses the appropriate native Mono lane and shim set per game.
Lanes
| Lane | Runtime | Known version | Scope | Notes |
|---|---|---|---|---|
| Wine Mono | Wine prefix component | bundled by Wine prefix | Windows CLR/bootstrapper apps inside a bottle | Used by .NET installers and Windows apps that call mscoree.dll through Wine. Minecraft currently reaches this lane and crashes in the native Mono/runtime path. |
| Native Mono ARM64 | ~/.metalsharp/runtime/mono-arm64/bin/mono |
6.14.1 | Terraria/FNA ARM64 style games | This is the dinosaur path that made Terraria work: native macOS Mono, FNA/XNA dllmaps, native SDL/FNA3D/FAudio/shims, and no Wine prefix ownership. |
| Native Mono x86_64 | ~/.metalsharp/runtime/mono-x86/bin/mono |
6.12.0.122 | Celeste/FNA legacy lane under Rosetta | This lane exists because some older FNA/Mono dependencies were x86_64-only or behaved better with Mono 6.12 and explicit dllmaps. |
Rules
- Sharp Library installer bottles should use Wine Mono only when the target is actually a Windows CLR/bootstrapper app.
- Steam games should request native Mono lanes only for known FNA/XNA targets or proof targets that need the old macOS Mono path.
- Native Mono ARM64/x86_64 should not be injected into every Wine bottle.
- Wine Mono failures should be logged separately from native Mono/FNA failures.
- A hard case like Minecraft should remain in the Wine bottle lane until evidence shows it needs a native launcher bridge or a different Wine Mono strategy.
- The public route label is Mono/FNA. Internal component names such as
fna_arm64,fna_x86,mono-arm64, andmono-x86remain implementation details.
Current Mono/FNA Route Contract
The route stages FNA/XNA assemblies, native FNA3D/FAudio/SDL/Steam libraries when available, FMOD/FMOD Studio stubs, steam_appid.txt, and Steamworks compatibility shims. It also deploys the macOS framework-backed MetalSharp shims expected by the dllmaps: libkernel32.dylib, libuser32.dylib, libCarbon.dylib, the Carbon interpose shim, and bundled CoreAudio/GameController shims such as xaudio2_9.dylib and xinput1_4.dylib when present. Celeste-style games select x86_64 Mono under Rosetta; Terraria-style games can use the Terraria runtime patch/stub path. Celeste and Terraria are now supported through this lane; future Mono/FNA titles should still be promoted only after per-game launch proof.
Phase 11 Minecraft Finding
Minecraft is a useful hard case because it exercises installer classification, bottle readiness, Wine Mono, embedded browser/runtime needs, and app detection. Current evidence shows:
- The bottle classifies as
java_launcherand launches from a stable installer bottle. - Fonts and Gecko can be repaired or detected correctly.
- WebView2 is still not available as a local runtime asset.
- bare Wine and
WINEDLLOVERRIDES=mscompatdb=dreproduce the same native Mono crash.
The next implementation path is to keep Minecraft as the proof target while adding better Wine Mono diagnostics and, if needed, a bottle-scoped alternate Mono strategy rather than changing global runtime behavior.
Wine Architecture
Wine prefix/runtime layout and wrapper behavior.
Wine Architecture
Updated: 2026-08-11
MetalSharp ships a self-contained Wine runtime at:
~/.metalsharp/runtime/wine/
It is used by the public Wine-backed routes VKD3D, DXMT, and DXMT(32). Internal fallback/diagnostic routes such as M32, Steam handoff, and plain Wine also use this runtime. Mono/FNA does not use the Wine runtime.
Layout
~/.metalsharp/runtime/wine/
├── bin/
│ ├── metalsharp-wine
│ ├── wine
│ ├── wineloader
│ └── wineserver
├── lib/
│ ├── wine/
│ │ ├── x86_64-unix/
│ │ ├── x86_64-windows/
│ │ └── i386-windows/
│ ├── dxmt/
│ │ ├── x86_64-unix/
│ │ └── x86_64-windows/
│ ├── dxmt-vkd3d/ (DXMT VKD3D rollback lane)
│ ├── vkd3d-proton/ (default VKD3D D3D12 stack)
│ ├── dxvk/ (shared DXGI; dxgi.dll for VKD3D)
│ └── moltenvk-vkmt/ (VKMT patched MoltenVK)
└── etc/
├── dxmt.conf
└── vulkan/icd.d/MoltenVK_icd.json
Other runtime pieces live beside it:
~/.metalsharp/runtime/
├── redist/
└── wine/
User/runtime state lives beside the runtime root:
~/.metalsharp/
├── prefix-steam/
├── bottles/
├── sharp-library/
├── games/
├── shader-cache/
├── cache/
└── logs/
Route Use
| Route | Wine use |
|---|---|
| VKD3D | Wine + vkd3d-proton D3D12 (default, D3D12 → Vulkan → MoltenVK); DXMT fallback via vkd3dBackend=dxmt |
| M11 | Wine + DXMT D3D11/DXGI |
| M10 | Wine + DXMT D3D10/D3D11/DXGI |
| VKD3D | Wine + vkd3d-proton/DXVK-macOS (D3D9/D3D10/D3D11/D3D12 via Vulkan) |
M32, Steam handoff, and plain Wine remain internal Wine-backed routes for diagnostics, bootstrap cases, legacy records, and installer/custom-app internals.
DLL Deployment
The backend copies graphics DLLs into the game directory before launch.
M11/M10:
d3d11.dll
dxgi.dll
d3d10core.dll
winemetal.dll
M10 deploys Wine's public d3d10.dll and d3d10_1.dll entrypoints for D3D10 imports, then uses DXMT's d3d10core.dll as the D3D10 handoff and shares the D3D11/DXGI/winemetal runtime with M11.
VKD3D:
d3d12.dll (vkd3d-proton forwarder)
d3d12core.dll (vkd3d-proton implementation)
dxgi.dll (DXVK, shared DXGI per vkd3d-proton design)
nvapi64.dll (optional stub)
nvngx.dll (optional stub)
The vkd3d-proton stack translates D3D12 to Vulkan and runs on the VKMT-patched
MoltenVK (Vulkan-on-Metal); VK_ICD_FILENAMES pins the runtime ICD. The
DXVK-macOS set (d3d9/d3d10core/d3d11/dxgi) covers D3D9/D3D10/D3D11 on the
same Vulkan path.
Prefixes
The shared Steam prefix is:
~/.metalsharp/prefix-steam/
Steam is installed inside that prefix. External Steam libraries may be mounted into the prefix by drive letter.
Sharp Library installer/app bottles use dedicated prefixes:
~/.metalsharp/bottles/<id>/prefix/
Steam game bottles are different: they are launch-authoritative readiness records, but their prefix_path currently
points at ~/.metalsharp/prefix-steam/ so Runtime Doctor and repair actions affect the prefix Wine Steam actually uses.
Wine Steam remains the live background client that stays connected for Steam games. Env-dependent Steam game launches
run the game executable directly through the selected MTSP pipeline with this prefix, route env, cache paths, and
SteamAppId/SteamGameId; client-only Steam handoff remains internal for diagnostics/bootstrap cases.
Important Environment
| Variable | Purpose |
|---|---|
WINEPREFIX |
Prefix location |
WINEDLLPATH |
Wine PE DLL lookup |
DYLD_FALLBACK_LIBRARY_PATH |
Unix library lookup for Wine, DXMT, and MoltenVK |
WINEDLLOVERRIDES |
Selects injected/native DLL behavior |
WINEMSYNC |
Wine msync toggle (config-driven, default 1) |
DXMT_SHADER_CACHE_PATH |
DXMT shader cache (DXMT routes) |
DXMT_CONFIG_FILE |
DXMT config file (DXMT routes) |
VKD3D_SHADER_CACHE_PATH / DXVK_STATE_CACHE_PATH |
vkd3d-proton/DXVK caches (default VKD3D route) |
VK_ICD_FILENAMES |
Pins the runtime-bundled VKMT MoltenVK_icd.json (default VKD3D route) |
SteamAppId / SteamGameId |
Steam identity for direct Steam-bottle game launches |
Isolation Contract (read before changing any launch code)
MetalSharp's Wine runtime is hermetic: Steam and every Wine-backed route must resolve MetalSharp's own wine binary, prefix, DLLs, and environment 100% of the time, regardless of which other Wine-based launchers are installed on the host (CrossOver, SakuraGiri, Whisky, Game Porting Toolkit, Homebrew wine…).
Rules:
- Wine binary: always resolve via
platform::runtime_wine_binary()/ms_wine()(i.e.~/.metalsharp/runtime/wine/bin/metalsharp-wine). Never fall back towine/wine64/wineserverfrom PATH,/usr/local/bin(CrossOver installs CLI symlinks there), or/opt/homebrew/bin(GPTK).launch::find_wine()fails loudly instead of falling back. - Environment ownership: the backend sets
WINEPREFIX,WINEDEBUG,WINEDLLOVERRIDES, andDYLD_FALLBACK_LIBRARY_PATHexplicitly on every spawn. Launcher/wrapper scripts must neverunsetthose (it would clobber bottle prefixes and Steam DLL overrides).WINEDLLPATHandDYLD_FALLBACK_LIBRARY_PATHin the wrapper must list MetalSharp's own directories before any inherited value. - No foreign identity: never export
CX_ROOT(CrossOver's identity variable) or mimic another launcher's env vars. - Process ownership: kill/cleanup logic (
stop_wine_steam,is_force_kill_target,update.sh, process-manager helper) must prove ownership from the process executable (argv[0]), not from arbitrary command-line arguments. A target must use an allowlisted executable in~/.metalsharp/runtime/wine/bin/, an explicitly allowlisted MetalSharp runtime helper, or an executable under an MS-owned prefix, bottle, or game root. A MetalSharp path or.exetoken appearing only in arguments is untrusted and never establishes ownership. A barewineserver/wineloader/wine64name match is never sufficient — foreign launchers run processes with those exact names. - Vulkan ICD:
VK_ICD_FILENAMESmust resolve inside the runtime ($MS_ROOT/etc/vulkan/icd.d/MoltenVK_icd.json) or be unset — never a hardcoded Homebrew path, which is absent on CrossOver-only machines.
Process Manager stop behavior
The Process Manager's Quit Game action follows the same ownership boundary
as the rest of the Wine lifecycle. It considers only non-Steam Wine rows whose
command line references the resolved MetalSharp data root or the managed
prefix-steam prefix. A bare wine, wineserver, wineboot, or
drive_c/ match is never enough to stop a process from CrossOver, Whisky,
GPTK, or another Wine installation.
When the shared Steam prefix is not active, the action first invokes
MetalSharp's bundled runtime/wine/bin/wineserver -k with WINEPREFIX set to
the managed prefix. If the prefix is shared with Wine Steam, or the bundled
server is unavailable, it falls back to SIGKILL only for the already-scoped
MetalSharp game PIDs. This preserves the live Wine Steam client while keeping
foreign Wine games outside the kill scope.
The GPTK lane is the working model for isolation: it owns its prefix
(prefix-gptk), its DYLD paths (gptk_seed_dyld), and its route DLLs. The
Steam/plain-Wine lanes must be just as self-contained.
Steam Wrapper
Wine Steam uses the bundled steamwebhelper.exe wrapper. Steam updates may replace it, so MetalSharp redeploys it when preparing or launching Steam.
Launcher Runtime
Sharp Library launcher and CEF/WebView runtime handling.
MetalSharp Launcher Runtime
Updated: 2026-07-08
Status: Phase 3 foundation
MetalSharp treats launcher installers as bottle-managed Windows programs, not as one-off EXEs. The goal is to let launchers keep their login/session state, install child games into the same bottle, and produce logs that explain why a launcher or child game failed.
Known Launcher Recipes
The installer classifier recognizes these launcher families before generic .NET, WebView, MSI, or PE import heuristics:
- Minecraft Launcher -> Java Launcher profile
- EA App / Origin -> WebView profile
- Ubisoft Connect / Uplay -> WebView profile
- Battle.net / Blizzard launcher -> WebView profile
- Epic Games Launcher -> WebView profile
- Rockstar Games Launcher / Social Club -> WebView profile
- GOG Galaxy -> Launcher profile
Known launcher hints are stored in the classifier output as known_launcher:<id> and launcher_name:<display name>. These hints make installer bottles more predictable, especially when launcher bootstrapper binaries also contain generic .NET or WebView strings.
Known launchers default to the bare Wine pipeline during install/bootstrap. That keeps store launchers from inheriting game-specific graphics routes before the actual child game executable exists. Once a launcher installs or starts a game, that child executable still gets its own bottle/runtime route.
Runtime Behavior
Install Windows Program routes launcher-like EXEs and MSI packages into installer bottles. The bottle records:
- source installer path
- installer kind
- runtime profile
- prefix path
- launch log
- launch pid/status
- detected installed app candidates
Minecraft gets a Java Launcher profile even when the bootstrapper includes CLR metadata. Storefront launchers get WebView or Launcher profiles so their bottle dependency set matches the login and embedded-browser surface they actually need. The WebView profile includes Gecko, WebView2, .NET 4.8, VC runtime, and core fonts because EA-style WiX/MSI launchers can execute .NET custom actions after the visible install bar completes.
CEF Compatibility
Steam already uses a wrapped steamwebhelper.exe to force CEF onto a Wine-safe software GPU path. Sharp Library bottles now generalize that behavior for launcher apps that carry CEF or Chromium payloads.
When a bottle app looks like a launcher and its install directory contains CEF assets such as libcef.dll, chrome_*.pak, vk_swiftshader.dll, or app.asar, MetalSharp preserves the original executable as <name>_real.exe and replaces <name>.exe with a small architecture-matched wrapper. The wrapper relaunches the real executable with --in-process-gpu --disable-gpu and deploys a sibling metalsharp-cefchildhook.dll for launchers that spawn renderer, utility, or GPU children from the preserved executable.
The first proof target is Minecraft Launcher:
- the Microsoft Store
.exebootstrapper is a 32-bit .NET/WPF package and can fall into Wine Mono or native .NET setup failure before Minecraft exists - the official Mojang
MinecraftInstaller.msiinstalls cleanly into ajava_launcherbottle MinecraftLauncher.exenow receives the generic CEF wrapper and child hook, but the current proof still renders a blank surface after CEF initializes- local hook logs prove imports are patched, but Minecraft's embedded CEF child creation is not yet passing through the hooked
CreateProcessA/W,GetProcAddress, orShellExecutepaths
EA App is the first Steam-adjacent storefront proof target:
- the installer reaches the EA MSI apply step in bottle
installer_16c2e7d7a6e2d5e7 - the visible install bar completes, then the MSI fails with
0x80070643, which EA reports asINST-14-1603 - extracted MSI custom-action metadata requests
.NET v4.0, so the WebView profile now provisionsdotnet48before the launcher installer runs - known launchers now install through bare Wine first instead of falling back to a graphics route from the 32-bit bootstrapper PE header
- fresh proof bottle relaunches now stay in the selected proof bottle instead of silently falling back to the stable source-path bottle
- the latest EA proof has
corefonts,dotnet48,gecko,vcrun2019, andwebview2installed - the direct MSI log files are still created as zero bytes, so the next EA pass needs deeper Wine MSI/service/elevation inspection around per-machine package cache writes
- WebView2/Edge helper executables are runtime components, not apps; prefix app detection filters them so a runtime repair does not pollute the Sharp Library
Launcher evidence reports:
POST /launcher/evidence
{"family":"ea"}
POST /launcher/evidence
{"family":"ubisoft"}
EA currently reports ea_msi_1603: the bootstrapper reaches MSI apply and fails with 0x80070643 / INST-14-1603.
Ubisoft currently reports ubisoft_auto_started_then_crash_reporter: the installer staged UbisoftConnect.exe, auto-started Ubisoft Game Launcher 171.0.13174, then entered the crash-reporter path. A clean direct launch of UbisoftConnect.exe still needs to be captured after runtime repair.
Remaining Work
- Finish the Minecraft CEF child-process path by either reaching the lower-level process creation call or mapping Minecraft's native CEF preference surface correctly.
- Persist child game processes spawned by launchers as bottle app records.
- Track launcher-owned game install folders separately from the launcher EXE.
- Add launcher-specific repair controls for WebView, Gecko, VC runtime, and session data.
- Add end-to-end smoke cases for at least three launchers once redistributable assets and test installers are available.
Compatdata Architecture
Steam game compatdata ownership.
MetalSharp Compatdata Architecture
Updated: 2026-07-08
Status: Phase 2 foundation
MetalSharp compatdata records are the launch-authoritative runtime records for Steam games. They are inspired by Proton's per-app runtime discipline, but they keep Wine Steam as the account, download, and session provider.
Paths
Steam game compatdata records live at:
~/.metalsharp/compatdata/<appid>/metalsharp-compatdata.json
~/.metalsharp/compatdata/<appid>/logs/
~/.metalsharp/compatdata/<appid>/assets/
Existing Steam bottle manifests still live at:
~/.metalsharp/bottles/steam_<appid>/bottle.json
The current implementation records the shared Wine Steam prefix as the active prefix because Wine Steam must remain a stable, long-lived entity. The compatdata record is still authoritative for launch routing, dependency visibility, runtime assets, and diagnostics.
Runtime migrations preserve compatdata metadata alongside bottle settings, game metadata, Steam prefix settings, and Sharp Library records. They do not stage full Wine prefixes, Steam steamapps/ payloads, or downloaded game installations.
Record Contents
Each Steam compatdata record stores:
- Steam appid and display name
- linked bottle id
- compatdata path
- active prefix path
- Wine Steam prefix path
- game install path
- runtime profile
- launch pipeline
- Steam identity mode
- compatibility tool name and launch command template
- log directory
- detected runtime assets from the game install
- required runtime components
- last launch log path, pid, status, and finish time when known
API
POST /steam/compatdata
Request:
{
"appid": 620,
"pipeline": "vkd3d"
}
The endpoint ensures the Steam game bottle exists, refreshes detected assets, writes the compatdata record, and returns the record.
Steam game launches also attach the current compatdata record to launch responses when available.
Wine-backed MTSP Steam launches write process output to:
~/.metalsharp/compatdata/<appid>/logs/launch-<timestamp>.log
Bottle diagnostics refresh the Steam compatdata record and check that both the compatdata manifest and log directory exist. That makes the Steam game bottle the repair surface while the compatdata record remains the authoritative launch ledger.
Why This Matters
Steam should not be torn down or replaced just to pass route-specific game runtime state. Steam remains responsible for login, ownership, downloads, cloud/session behavior, and staying alive while games run. MetalSharp compatdata owns the game launch contract so DLLs, redists, logs, runtime assets, and selected pipeline state can be repaired and inspected per game.
Future work can move individual game runtime state into compatdata/<appid>/pfx only where the Steam/game process model allows it cleanly.
Host Runtime ABI
Host shim ABI boundaries.
MetalSharp Host Runtime ABI
Updated: 2026-07-08
Status: Phase 1 draft implementation
This document defines the first supported boundary between Windows-facing shims, Wine unixlib modules, and macOS host services. The goal is to turn today's scattered dylibs and per-game shims into a bottle-aware runtime contract.
What The ABI Owns
Host Runtime ABI services are low-level host capabilities that many launch routes need:
- process, environment, and path identity for a bottle
- runtime logging and diagnostics paths
- Steam identity bridge configuration
- managed runtime configuration for Mono/.NET launchers
- graphics, audio, and input dispatch capability reporting
The ABI does not own Steam account/session lifecycle. Wine Steam remains the long-lived Steam entity. MetalSharp owns route authority and injects bottle-specific runtime state into the game process or installer process.
Contract Header
The C ABI lives in include/metalsharp/HostRuntimeABI.h. It provides:
METALSHARP_HOST_ABI_VERSION_MAJORMETALSHARP_HOST_ABI_VERSION_MINORMetalSharpHostRuntimePathsMetalSharpSteamBridgeConfigMetalSharpManagedRuntimeConfigMetalSharpHostCapabilitiesmetalsharp_host_get_abi_version()metalsharp_host_query_capabilities()metalsharp_host_self_test()
Every struct starts with struct_size so newer hosts can append fields without breaking older shims. Callers must reject incompatible major versions and tolerate larger struct sizes.
Phase 1 Runtime Changes
The initial implementation removes two brittle assumptions from the runtime:
src/wine/mscoree_unix.cno longer uses a machine-local absolute Mono path. It acceptsMETALSHARP_MONO_LIB,METALSHARP_MONO_ROOT,METALSHARP_MONO_ASSEMBLY_DIR, andMETALSHARP_MONO_CONFIG_DIR, with portableMETALSHARP_HOME/HOMEfallbacks.src/fna/shims/steam_shim.cno longer hardcodes the Steam bridge port only in native code. It acceptsMETALSHARP_STEAM_BRIDGE_PORT, and the Rust launcher reports/passes the same value.- The backend exposes
GET /runtime/host-abiso the app can inspect the current ABI version, service list, bridge port, and managed runtime environment contract.
These are small changes, but they are the necessary shape: bottle manifests and installer runtime profiles can now configure shims through explicit runtime state instead of requiring patched binaries or one global machine assumption.
Managed PE/unixlib launch contract
The Wine mscoree shim keeps the managed executable identity explicit across
the PE/unix boundary. _CorExeMain fills mscoree_cor_exe_main_params with
the Unix-form executable path and directory, passes that structure to
MSCOREE_FUNC_COR_EXE_MAIN, and exits with the exit_code written by the Unix
handler. The Unix handler must not infer the executable from _ or
/proc/self/cmdline; those are not portable on macOS. A failed bridge call
leaves a non-zero exit code so a managed launch cannot report success without
executing its assembly.
Bottle Manifest Mapping
Future bottle manifests should map directly to ABI structs:
| Manifest field | ABI target |
|---|---|
id |
MetalSharpHostRuntimePaths.bottle_id |
prefix_path |
MetalSharpHostRuntimePaths.bottle_prefix |
game_install_path |
MetalSharpHostRuntimePaths.game_install_path |
| bottle log path | MetalSharpHostRuntimePaths.log_path |
| Steam appid | MetalSharpSteamBridgeConfig.appid |
| bridge port | MetalSharpSteamBridgeConfig.port |
| Mono root/lib dirs | MetalSharpManagedRuntimeConfig |
Self-Test
tests/test_host_runtime_abi.cpp compiles the header and calls the runtime query/self-test surface from src/runtime/host/HostRuntimeABI.cpp. It validates version constants, struct sizing, capabilities, and default Steam bridge configuration. It is intentionally lightweight so CI can catch ABI breakage before runtime packaging work depends on it.
Packaging
The shared host runtime target is metalsharp_host_runtime. macOS builds produce libmetalsharp_host_runtime.dylib.
On Apple Silicon it is an arm64 host artifact; it is not part of the
x86_64 Wine/Rosetta shim lane. See the macOS artifact architecture
matrix for the complete boundary.
tools/package/create-host-runtime.sh stages the package into app/native/host/:
libmetalsharp_host_runtime.dylibor platform equivalentHostRuntimeABI.hmanifest.json
Electron packages this directory as runtime/host/. This gives the app, updater, and future runtime installer a stable place to discover host ABI artifacts without scraping the native library root.
During setup, the backend copies packaged runtime/host/ assets into:
~/.metalsharp/runtime/host/
The setup dependency check treats the host runtime as required. Runtime migration schema 2 also treats a missing host ABI install as a repair condition so existing installs can be refreshed cleanly.
Next Phase Hooks
Phase 2 should use this ABI boundary to generate a per-bottle runtime env block from bottle manifests. That block should be applied to installer launches, direct game launches, and Steam game handoffs that create a child process outside the already-running Steam client.
Redistributable Runtime
Redistributable source and repair policy.
MetalSharp Redistributable Runtime
Updated: 2026-07-08
Status: Phase 4 foundation
Redistributables are modeled as bottle components. MetalSharp looks for legal local assets in Steam Common Redistributables, Sharp Library installer bottles, and ~/.metalsharp/runtime/redist/, then records missing, installed, or repair-needed state in the bottle.
Component IDs
The current repairable component surface includes:
vcrun2019directx_jun2010dotnet48webview2openalxnaphysxwine-monogeckocorefonts
Graphics route components such as d3d9, d3d10, d3d11, d3d12, and dxgi are verified from the MetalSharp runtime instead of downloaded as redistributables.
Asset Discovery
Steam game bottles scan game installs for _CommonRedist, CommonRedist, and installscript.vdf assets. Detected assets infer required bottle components:
- VC redist payloads ->
vcrun2019 - DirectX payloads or install scripts ->
directx_jun2010 - .NET payloads ->
dotnet48 - WebView payloads ->
webview2 - OpenAL payloads ->
openal - XNA payloads or install scripts ->
xna - PhysX payloads or install scripts ->
physx
The install script parser is intentionally conservative: it only infers known redistributable families from obvious names such as DXSETUP.exe, xnafx40_redist.msi, oalinst.exe, or PhysX installers.
Repair Sources
The repair resolver searches:
~/.metalsharp/prefix-steam/drive_c/Program Files (x86)/Steam/steamapps/common/Steamworks Shared/_CommonRedist/
~/.metalsharp/bottles/*/installers/
~/.metalsharp/runtime/redist/
MSI redistributables are launched through msiexec /i; EXE redistributables are launched directly with silent arguments where known. XNA Framework 4.0 repairs also reuse a matching Sharp Library installer-bottle payload when the user has already installed or staged xnafx40_redist.msi through Sharp Library. Every repair writes a per-bottle component log.
Remaining Work
- Persist redist install receipts separately from heuristic file checks.
- Parse more Steam install script actions and conditions.
- Add legal asset download/install UX around the existing redist source guide.
- Add end-to-end verification against Steamworks Common Redistributables on a real Wine Steam install.
Steam Compatibility Tool Surface
The Steam-facing compatibility contract.
MetalSharp Steam Compatibility Tool Surface
Updated: 2026-08-11
Status: Phase 7 foundation
MetalSharp should behave like a compatibility runtime without pretending macOS Steam exposes Linux Proton's exact compatibilitytools.d contract. The current app-owned model stays authoritative: Steam owns account/session/download state, while MetalSharp owns the game process route, bottle, compatdata, logs, and runtime assets.
Current Surface
Steam game compatdata now records:
compat_tool_namelaunch_command_templatelaunch_pipelinesteam_identity_modebottle_idprefix_pathsteam_prefix_path- runtime assets, components, and launch ledger state
The launch command template is intentionally backend-shaped:
POST /steam/launch-game {"appid":<appid>,"launchMethod":"<pipeline>"}
That keeps the contract honest. The current supported path is still MetalSharp launching the game process while Wine Steam remains alive in the background for Steamworks connectivity.
Steam App ID Input Contract
Backend routes that accept a JSON appid use the same boundary validation:
- the value must be a JSON number in the positive
u32range (1..=4,294,967,295); - missing, string, zero, negative, fractional, or out-of-range values receive a
400response; and - the value is validated before pipeline resolution, preparation, launch, diagnostics, game edits, or uninstall work begins.
This prevents a u64 request value from silently truncating into another Steam
game's u32 app ID. The compatibility /game/prepare route, canonical
/mtsp/prepare, /mtsp/recipe, /mtsp/doctor, and /game/launch-auto routes
share this contract with the other Steam game operations.
Why Not Fake Proton
Linux Proton is installed as a Steam compatibility tool under compatibilitytools.d. macOS Steam does not provide that same documented Proton tool surface. Wine Steam also needs to remain a normal Windows Steam client for login, downloads, and session state. For now, MetalSharp records a compatibility-tool-like contract in compatdata and uses its backend as the launcher.
Remaining Work
- Verify whether any current macOS Steam or Wine Steam path honors compatibility tool metadata in a useful way.
- Generate optional
compatibilitytool.vdfscaffolding only for experiments, not as the default app path. - Add last-known-good runtime rollback per appid.
- Add a visible per-game route template/debug view so users can see exactly what MetalSharp will launch.
Vendor Trust Kit
Vendor runtime evidence and trust boundaries.
MetalSharp Vendor Trust Kit
Updated: 2026-07-08
Status: Phase 9 foundation
The anti-cheat path is cooperative vendor trust, not bypass. A vendor or game developer should be able to inspect MetalSharp's runtime identity, launch model, logs, and non-evasion policy without reverse-engineering the repository.
Kit Contents
The vendor kit should include:
- runtime identity and version
- host ABI manifest
- signed/notarized artifact evidence when available
- Steam identity model
- compatdata sample
- launch log sample
- process tree and environment handoff explanation
- anti-cheat classification output
- no-bypass/no-spoof policy
- known unsupported boundaries
- contact and reproduction instructions
Generator
tools/package/create-vendor-trust-kit.sh creates a local bundle under:
dist/vendor-trust-kit/
The generator copies the core policy/runtime docs and writes a manifest.json that records the current git commit, version files, and included documents. It does not claim a game is supported; it prepares the evidence package for a vendor conversation.
Required Before External Use
- Replace placeholder signing/notarization state with real notarized artifact evidence.
- Attach real launch logs from a target game.
- Attach real anti-cheat classification JSON from Launch Doctor.
- Add explicit vendor/game contact context.
- Remove any stale "bypass" terminology from shipped UX and docs.
Host Shim Inventory
Current host/runtime shim inventory.
MetalSharp Host Shim Inventory
Created: 2026-05-19 Updated: 2026-08-11
Purpose: Phase 0 inventory of existing C, C++, and Objective-C host shims that can seed a formal MetalSharp Host Runtime ABI.
Classification Key
stable: useful foundation that can move toward a supported ABI with limited redesignprototype: useful experiment, but needs cleanup, tests, relocation, or broader semanticsgame-specific: useful for a known game/runtime path but should not become a general ABI unchangedlegacy-risk: naming or behavior should be revisited before anti-cheat/vendor-facing workobsolete: should be removed or replaced
Inventory
| Area | File | Current role | Classification | Phase 1 action |
|---|---|---|---|---|
| Steam identity bridge | src/fna/shims/steam_shim.c |
Exports Steam API symbols and talks to a localhost bridge on port 18733. |
prototype | Generalize into Host Runtime ABI service for Steam identity/session calls. Make port/config dynamic and per-bottle aware. |
| FNA Steamworks offline shim | src/fna/shims/csteamworks_shim.c, src/fna/shims/SteamworksOffline.cs |
Provides offline CSteamworks/Steamworks.NET success stubs by default, with optional native libsteam_api.dylib passthrough via METALSHARP_FNA_STEAM_PASSTHROUGH=1. |
game-specific | Keep for FNA/Mono/XNA games; do not treat as complete Steamworks compatibility. |
| Win32 process/env/time shim | src/fna/shims/kernel32_shim.c |
Provides POSIX/macOS-backed kernel32-style functions for console, env, timing, file, process, and thread basics. |
prototype | Split reusable process/env/time services into Host Runtime ABI; leave FNA-specific exports behind. |
| Window/message shim | src/fna/shims/user32_shim.c |
Provides lightweight user32 stubs for windows, messages, focus, key state, and basic UI calls. |
prototype | Promote only explicit window/input services; document which calls are no-op compatibility stubs. |
| Carbon interpose | src/fna/shims/carbon_interpose.c |
Intercepts dlopen for Carbon and redirects to METALSHARP_CARBON_SHIM. |
game-specific | Keep as compatibility shim for legacy FNA/Mono/macOS paths. Do not make this a general injection pattern. |
| Carbon HIView shim | src/fna/shims/carbon_hiview_shim.m |
Supplies Carbon/HIView compatibility symbols. | game-specific | Keep with FNA/legacy macOS shims; test only with known consumers. |
| FMOD stubs | src/fna/shims/fmod_stub.c, src/fna/shims/fmodstudio_stub.c |
Stub FMOD libraries for FNA/game compatibility. | game-specific | Keep as per-game runtime assets, not ABI services. |
| Mono/.NET unixlib bridge | src/wine/mscoree_unix.c |
Wine unixlib-style bridge that loads Mono via dlopen and executes managed assemblies. |
prototype | Remove hardcoded paths, make bottle/runtime path configurable, add tests with known .NET installer/app cases. |
| Wine D3D11 unix bridge | src/wine/metalsharp_unix.h, src/wine/metalsharp_unix.mm, src/wine/d3d11_pe.cpp, src/wine/metalsharp_d3d11_pe.cpp, src/wine/dxgi_pe.cpp |
PE-side D3D/DXGI shims dispatch to Objective-C++ Metal backend through Wine unixlib style calls. | stable | Use as the strongest model for Host Runtime ABI shape: PE-facing shim, host-side dispatch table, versioned structs. |
| Wine D3D9 unix bridge | src/wine/d3d9_pe.cpp, src/wine/d3d9_unix.h, src/wine/d3d9_unix.mm |
PE-side D3D9 shim dispatches to Metal backend via unixlib calls. | stable | Align D3D9 and D3D11 dispatch/versioning patterns. |
| CoreAudio bridge | src/audio/CoreAudioBackend.mm, src/audio/XAudio2Engine.cpp, src/audio/DirectSoundBackend.cpp |
Maps XAudio2/DirectSound-like behavior to CoreAudio. | stable | Expose audio capability and diagnostics through Host Runtime ABI. |
| GameController bridge | src/input/GameControllerBridge.mm, src/input/XInputEngine.cpp |
Maps XInput-like behavior to Apple's GameController framework. | stable | Expose input capability and device state diagnostics through Host Runtime ABI. |
| PE loader shim registry | src/loader/PELoader.cpp, src/loader/D3DShims.cpp |
Registers shim DLL exports and resolves imports for the native PE loader path. | stable | Keep as native-loader foundation; document boundary with Wine PE/unixlib path. |
| Win32 shim layer | src/win32/kernel32/*.cpp, include/metalsharp/Win32Types.h, include/metalsharp/ExtraShims.h |
Larger native-loader Win32 shim set for kernel32/ntdll/network/extra APIs. | stable | Use selectively for ABI service mapping; avoid duplicating behavior between FNA C shims and C++ Win32 shims. |
| Anti-cheat database | include/metalsharp/AntiCheatDB.h, src/runtime/DRMDetector.cpp |
Static detection tables now use evidence-backed support status strings instead of compatible/incompatible booleans. | diagnostic | Keep static statuses aligned with launch-recipe evidence and live protected-launch proof. |
| Offline EAC mode naming | app/src-rust/src/installer.rs, app/src-rust/src/main.rs |
Installer/UI text now avoids bypass wording, but endpoint names still expose the legacy eac-toggle route. |
legacy-risk | Audit behavior, keep naming explicit if legitimate compatibility flag, remove if bypass-like. |
| Controller input shims (PR #375) | tools/bundles/update-lib-metalsharp.py (xinput/dinput PE shims), deployed per controllerInput config (off/x/d) |
XInput shims (xinput1_1.dll…xinput1_4.dll, xinput9_1_0.dll) and DInput shims (dinput.dll, dinput8.dll) shipped in lib/metalsharp and copied into the game folder + Steam prefix system32/syswow64 on launch; previous set removed on switch, pre-existing game files backed up/restored. |
stable | Keep the shim set per-mode; extend the manifest pattern below to versioned selection. |
| Runtime deploy glue | app/src-rust/src/mtsp/launcher.rs, app/src-rust/src/setup.rs |
Copies shims into runtime/game folders and assembles launch env. The Mono/FNA launcher now has a native shim manifest for kernel32/user32/Carbon interpose plus bundled CoreAudio/GameController dylibs. | prototype | Extend the manifest pattern into versioned Host Runtime ABI asset selection and self-tests. |
Native-loader Environment Contract
The C++ kernel32 environment exports in src/win32/kernel32/Kernel32Extra.cpp
model the Windows environment block separately from the macOS process
environment:
- variable names are canonicalized to uppercase for both the A and W exports;
- inherited macOS variables are discovered by a case-insensitive scan of the
host environment, rather than by the case-sensitive
getenv(name)lookup; GetEnvironmentVariableA/W(name, nullptr, 0)returns the required character count including the terminator;- a non-zero-size buffer that cannot hold the value returns zero, sets
ERROR_BUFFER_OVERFLOW, and is left untouched; and SetEnvironmentVariableA/Wupdates the Windows-side map without callingsetenvorunsetenv, so a PE mutation cannot leak a synthetic uppercase variable into the launcher's POSIX namespace. A Windows-side deletion masks an inherited host variable for subsequent PE lookups.
The shared kernel32 last-error state is used by both the core exports and these
extended exports, so callers observe buffer and missing-variable errors through
the normal GetLastError path.
Strongest Existing Pattern
The D3D9/D3D11 Wine PE-to-unix split is the best model for Phase 1:
Windows PE side -> versioned structs -> dispatch id -> host Objective-C++ implementation -> Metal/CoreAudio/GameController/macOS APIs
That pattern is safer than ad hoc interposition because:
- the boundary is explicit
- structs can be versioned
- host code can be tested independently
- logs can name each dispatch
- the ABI can be packaged as a known runtime artifact
Weakest Current Pattern
The weakest remaining pattern is per-game dylib/stub deployment outside the Mono/FNA native shim manifest:
- hardcoded ports
- hardcoded user paths
- implicit
@loader_pathassumptions - no ABI version
- no uniform self-test
- unclear difference between no-op compatibility stubs and real host-backed services
Phase 1 should fix this by creating a versioned Host Runtime ABI and a manifest-driven shim selection layer.
Required Phase 1 Decisions
-
Which services are ABI services? - process/env/path/time/logging: yes - Steam identity/session bridge: yes - graphics/audio/input dispatch: yes - per-game FMOD/FNA stubs: no, keep as runtime assets - Carbon interpose: no, keep as legacy compatibility asset
-
Which runtime owns process launch? - Rust backend should own high-level launch orchestration. - Host ABI should own low-level host service calls. - Wine Steam should own account/session/download state. - Game bottle/compatdata should own per-game runtime state.
-
How are shims configured? - no hardcoded user paths - all paths derive from bottle/compatdata/runtime manifests - all local IPC ports or sockets are dynamic or manifest-configured - every shim can report version/capabilities
Phase 0 Conclusions
- MetalSharp already has credible host shim foundations.
- The D3D PE/unixlib bridge should become the architectural reference.
- Several early shims are good experiments but must be made relocatable, tested, and versioned.
- Anti-cheat-facing names and compatibility claims need cleanup before vendor-facing work.
Darwin Sync Map
macOS runtime sync notes.
MetalSharp Darwin Sync Map
Updated: 2026-08-11
Status: Phase 6 foundation
Linux Proton can lean on Linux synchronization primitives and, where available, /dev/ntsync. macOS does not have Linux futexes or an ntsync device. MetalSharp has to map Windows synchronization behavior onto Darwin primitives without pretending macOS is Linux.
Source Surface
The first source-backed map lives in:
include/metalsharp/DarwinSyncMap.hsrc/runtime/host/DarwinSyncMap.cpptests/test_darwin_sync_map.cpp
It classifies each primitive by strategy, whether a kernel/system component is required, and whether the mapping is safe to call shipping-ready today.
Current Classification
| Primitive | Strategy | Shipping-ready | Notes |
|---|---|---|---|
| Event | pthread_mutex_t + pthread_cond_t |
yes | Good for tracked in-process manual/auto reset events. |
| Semaphore | dispatch/pthread counted semaphore | yes | Current use is covered; Mach semaphore benchmarking remains useful. |
| Mutex | pthread_mutex_t |
yes | Current in-process ownership semantics are covered. |
| CriticalSection | pthread_mutex_t |
yes | Already represented by existing kernel32/ntdll shims. |
| SRWLock | out-of-line pthread_rwlock_t keyed by guest address |
yes | Preserves zero/static initialization and shared/exclusive modes without writing a host pthread object into the guest's pointer-sized storage. |
| ConditionVariable | out-of-line pthread_cond_t plus wait mutex keyed by guest address |
yes | The wait mutex closes the release/wait race; SleepConditionVariableSRW reacquires the mapped SRW lock before returning. |
| WaitAny | condition-variable wakeups over tracked handles | yes | In-process only; cross-process semantics need more work. |
| WaitAll | coordinated wait over tracked handles | no | Needs correctness tests for mixed event/semaphore/mutex waits. |
| Futex | Darwin ulock candidate |
no | Research candidate only; not Linux ABI compatible. |
| NtSyncDevice | unsupported Linux-specific device | no | macOS has no /dev/ntsync equivalent. |
Boundary
This map is not an anti-cheat bypass and not a kernel plan. It is a compatibility inventory. If a future game proves we need cross-process NT object semantics or lower-latency wait behavior, that evidence decides whether to prototype a user-space ulock strategy, a Mach-backed strategy, or an Apple-approved system extension investigation.
Remaining Work
- Add mixed-object WaitAll correctness tests.
- Benchmark pthread condition variables against Mach semaphores and
ulockwhere legally usable. - Identify which real games stress synchronization enough to justify deeper work.
- Document entitlement, signing, notarization, install, and user-consent requirements before any system extension prototype.
macOS Artifact Matrix
Native host and Wine/Rosetta artifact boundaries.
macOS artifact architecture matrix
Updated: 2026-08-11
MetalSharp ships one arm64 macOS application while its Wine-facing native
artifacts remain x86_64 for Rosetta-backed Windows execution. The architecture
boundary is explicit in CMakeLists.txt and is validated after native builds
and before packaging.
| Artifact | Architecture | Process boundary |
|---|---|---|
| Electron app and Rust backend | arm64 | Native macOS application |
metalsharp_launcher |
arm64 | Native helper that starts MetalSharp Wine |
metalsharp_host_runtime and MetalSharpMigrator |
arm64 | Native host/runtime helpers |
test_host_runtime_abi |
arm64 | Host ABI regression test |
metalsharp_core and metalsharp_loader |
x86_64 | Wine/Rosetta native PE loader |
| D3D11, D3D12, DXGI, audio/input, and OpenGL shims | x86_64 | Wine/Rosetta DLL surface |
metalsharp, EAC substrate, and other Wine-side native artifacts |
x86_64 | Wine/Rosetta launch surface |
| Other native CTest targets | x86_64 | Tests for the Wine-side surface |
METALSHARP_WINE_ARCH and METALSHARP_HOST_ARCH are the CMake knobs for
these two lanes. The project default remains the Wine architecture so vendor
libraries and Wine-side tests resolve consistently; host targets use
metalsharp_host_target() explicitly. The host launcher intentionally does
not link metalsharp_core, which is an x86_64 PE-facing library.
The macOS architecture regression test uses lipo -info/lipo -archs to
check the configured target outputs. The native-shim, host-runtime, bundle,
and DMG validators repeat the relevant checks so a wrong-architecture binary
cannot silently enter the packaged arm64 application.
Launch Architecture
Pipeline selection and launch ownership.
Launch Architecture
Updated: 2026-07-08
MetalSharp launches games through the Rust backend and the current MTSP pipeline resolver.
Flow
Play clicked
-> renderer calls backend
-> backend resolves a pipeline
-> backend syncs/preflights the runtime bottle when one applies
-> backend builds a LaunchRecipe
-> backend preflights runtime assets
-> backend prepares DLLs/env/cache beside the selected executable
-> selected MTSP route starts the game; internal Steam/Wine/macOS handoffs are used only when the backend selects them
The launch recipe is the backend contract for click-to-play. It records the appid, selected pipeline, game directory, selected executable, launch arguments, environment, DLL placement, runtime asset status, anti-cheat markers, and warnings. Manual launch methods still work; they force the pipeline before the recipe is built.
Runtime bottles add the user-facing readiness contract. Sharp Library installer/app bottles own their own prefixes under
~/.metalsharp/bottles/<id>/prefix. Steam game bottles use ids like steam_620 and are launch-authoritative preflight
records over the shared Wine Steam prefix, so Steam remains the running launcher/session owner while MetalSharp checks
runtime assets, redistributables, component state, and launch health.
Steam game bottle sync scans _CommonRedist, CommonRedist, and installscript.vdf payloads, then infers repairable
components such as VC runtime, DirectX June 2010, .NET 4.8, WebView2, OpenAL, XNA, and PhysX. Repair actions resolve
legal local assets from Steamworks Shared or ~/.metalsharp/runtime/redist/ and write per-bottle component logs.
For env-dependent Steam routes, MetalSharp keeps Wine Steam running as the background client, then launches the game
executable directly through the selected MTSP pipeline with the bottle prefix, route env, cache paths, and
SteamAppId/SteamGameId. Internal client-only Steam handoff still exists for diagnostics and bootstrap cases, but it is not exposed as a normal bottle option.
D3DMetal is an explicit GPTK lane rather than a generic bottle repair path. Saving a D3DMetal bottle installs/trusts Homebrew GPTK and Rosetta, stages the Steam runtime files and steam_appid.txt beside the mapped game/executable path, Repair Redist copies x64+x86 VC runtime DLLs plus registry keys into ~/.metalsharp/prefix-gptk, Seed Prefix copies Homebrew GPTK route DLLs into prefix system32, and Play D3DMetal launches the game exe directly through Homebrew GPTK Wine.
Current Pipelines
| Public route | Backend | Launch path |
|---|---|---|
| VKD3D | vkd3d-proton + DXVK-macOS | Direct Wine launch; deploys vkd3d-proton D3D12/D3D12core plus DXVK-macOS D3D11/D3D10core/D3D9/DXGI to the game folder, routed via n,b overrides on VKMT MoltenVK |
| DXMT | DXMT | Direct Wine launch with legacy dxmt D3D10/D3D11/DXGI DLLs (x86_64) |
| DXMT(32) | DXMT | Direct Wine launch with legacy dxmt D3D10/D3D11/DXGI DLLs (32-bit / i386) |
| Mono/FNA | Native Mono | Native FNA/XNA/Mono runtime with FNA/XNA assemblies, native dylib staging, FMOD/FAudio/FNA3D shims, and Steamworks shim support |
| D3DMetal | Homebrew GPTK | Direct GPTK Wine launch with Homebrew D3DMetal framework and prefix-seeded Homebrew route DLLs |
Internal route IDs such as dxmt, steam, macos_steam, wine_bare, m32, and m13 remain parseable for old records, diagnostics, and backend fallback behavior. m13 is treated as legacy GPTK/D3DMetal compatibility and should not be used as a separate public route. Internal routes are intentionally hidden from normal bottle selectors.
Resolution
The resolver checks, in order:
configs/mtsp-rules.toml- Managed .NET/FNA eligibility
- PE header analysis
- Installed game directory markers
- VKD3D fallback
Common marker behavior:
| Marker | Pipeline |
|---|---|
| Known XNA/FNA managed game | Mono/FNA |
Unity, Unreal, Source, RE Engine, or steam_api*.dll markers |
DXMT |
d3dx9_43.dll or D3D9 import |
VKD3D |
| PE imports D3D12 | VKD3D |
| PE imports D3D11 | DXMT |
| 64-bit PE imports D3D10 | DXMT |
| PE imports D3D9 | VKD3D |
D3D10 PE imports are checked before broad Unity, Unreal, Source, RE Engine, and Steam marker heuristics so D3D10 games stay on the DXMT route.
Runtime Prep
Runtime prep is recipe-driven. DXMT/Wine DLL overrides are deployed next to the selected executable rather than blindly
into the game root, which keeps nested layouts such as Binaries/Win64 and launcher-heavy games from loading the wrong
binary or missing local overrides.
DXMT/DXMT(32) read from the legacy runtime surface:
~/.metalsharp/runtime/wine/lib/dxmt
DXMT shader Metal version
For DXMT shader-compiling routes M10, M10(32), M11, and M11(32), the backend reads the host product version through sw_vers -productVersion at launch and writes a final DXMT_CONFIG overlay. This overlay has precedence over DXMT_CONFIG_FILE while preserving unrelated DXMT options:
| macOS major version | DXMT overlay | Rationale |
|---|---|---|
| 14 | dxmt.shaderMetalVersion=310 |
Metal 3.1 |
| 15 through 25 | dxmt.shaderMetalVersion=320 |
Metal 3.2 |
| 26 or newer | no shader-version overlay | DXMT automatically selects Metal 4 |
Malformed or unavailable version output fails open: no shader-version overlay is set, so DXMT keeps its own supported-host detection. The policy is applied after game recipe and caller configuration on direct/bottle paths, preventing stale per-game shader-version values from overriding the host capability.
VKD3D reads from its default vkd3d-proton/DXVK/MoltenVK surface:
~/.metalsharp/runtime/wine/lib/vkd3d-proton (d3d12.dll + d3d12core.dll)
~/.metalsharp/runtime/wine/lib/dxvk (dxgi.dll)
~/.metalsharp/runtime/wine/lib/moltenvk-vkmt (VKMT MoltenVK)
With the vkd3dBackend=dxmt rollback, VKD3D reads from the isolated DXMT surface
instead:
~/.metalsharp/runtime/wine/lib/dxmt-vkd3d
M11/M10 copy:
d3d11.dlldxgi.dlld3d10core.dllwinemetal.dll
M10 is selected by 64-bit d3d10.dll, d3d10_1.dll, or d3d10core.dll imports. It deploys Wine's public d3d10.dll and d3d10_1.dll entrypoints plus DXMT's d3d10core.dll, so public D3D10 imports and the DXMT core handoff are both owned by the x86_64 M10 runtime contract.
VKD3D (default backend) copies:
d3d12.dlld3d12core.dlldxgi.dll(DXVK lane)nvapi64.dll/nvngx.dll(GPU vendor stubs, shareddxmt_vkd3dlane)
VKD3D also adds the route's unix library directories to the fallback library path: the default backend resolves lib/wine/x86_64-unix and lib/moltenvk-vkmt (Vulkan -> MoltenVK presentation, VK_ICD_FILENAMES pinned to the runtime ICD); the DXMT rollback resolves lib/dxmt-vkd3d/x86_64-unix so winemetal.so and its bundled C++ sidecars are found. vkd3d-proton ships Windows DLLs only and has no unix sidecar.
VKD3D copies (deployed to the game folder):
d3d12.dll,d3d12core.dll(vkd3d-proton lane)d3d9.dll,d3d10core.dll,d3d11.dll,dxgi.dll(DXVK-macOS lane)
D3D9 imports resolve to VKD3D, which runs D3D9 through DXVK-macOS on the
same Vulkan → MoltenVK path. The legacy dxvk_metal32, m9_gl, and
m32_vk aliases are rejected.
D3DMetal does not use MetalSharp's bundled GPTK assets because there are none. It uses Homebrew GPTK at /Applications/Game Porting Toolkit.app, copies the matched route DLLs (d3d10, d3d11, d3d12, dxgi, nvapi64, nvngx-on-metalfx) into ~/.metalsharp/prefix-gptk/drive_c/windows/system32, sets D3DMETAL_FRAMEWORK_PATH, and launches through Homebrew GPTK Wine.
Mono/FNA does not use Wine. Wine Steam remains the background client for Windows Steam ownership/session state, while the selected MTSP route owns the game process.
Bottles
| Bottle type | Prefix behavior | Used for |
|---|---|---|
| Installer / Sharp Library | Dedicated bottle prefix | Windows installers, launchers, demos, imported apps |
| Steam game | Shared ~/.metalsharp/prefix-steam |
Steam game preflight, runtime assets, component repair, launch health |
| D3DMetal game | Shared ~/.metalsharp/prefix-gptk |
Homebrew GPTK route, copied VC runtime DLL/registry seed, D3DMetal direct-game launch |
Steam game bottles do not replace Steam. They prepare the runtime state the game will use and keep Wine Steam alive as the background Steamworks client/session owner. Env-dependent pipeline launches run the game executable directly with Steam identity env; client-only Steam handoff remains internal for diagnostics/bootstrap cases.
When a title uses the Steam launch model and Goldberg is disabled, the launcher stages real Steam API, Steam client, and overlay DLLs next to the selected executable. Titles that only need Steam identity get -steam; -secure is reserved for games that explicitly require the secure launch model.
Process Lifecycle
- Running games are tracked by the backend.
- Stop/kill actions terminate only backend-registered process roots and their
child processes. Game stops resolve the PID from the registered
appid(or the legacy PID-only route accepts only a registered game root); caller- supplied PIDs are never used as a fallback. Sharp Library apps use an app-id-scoped stop route with command-line ownership validation. - Steam process management lives in
steam.rs. - Launching a Steam game keeps Wine Steam alive for Steam connectivity. Env-dependent routes apply route-specific environment to the spawned game process rather than trying to make an already-running Steam client inherit it.
- Wine Steam readiness checks fail clearly if Steam never becomes detectable, keeping launch requests below the renderer backend timeout instead of silently proceeding without a Steam client.
- Shader cache paths are per appid under
~/.metalsharp/shader-cache/. - Wine-backed launch logs include the host ABI version, host runtime path, Wine runtime path, Steam bridge port, and compatdata manifest path when the launch is tied to a Steam appid.
- Launch recipes classify detected anti-cheat markers into statuses such as
blocked_pending_vendor_support,unsupported_kernel_driver,vendor_supported_on_proton_assets_present,unknown, anduser_mode_possible.
Backend HTTP Contract
Bounded JSON request bodies and client error responses.
Backend HTTP Request Contract
Updated: 2026-08-11
MetalSharp's Rust backend listens on the loopback HTTP interface and receives
JSON requests from the Electron bridge and local diagnostic tools. Every route
that consumes a JSON body uses the same bounded body reader in
app/src-rust/src/main.rs.
Request-body rules
- The request body must be a JSON object. Empty bodies, malformed JSON, and valid JSON values that are not objects are invalid requests.
- The body limit is 16 MiB (
16 * 1024 * 1024bytes). A knownContent-Lengthabove the limit is rejected before reading from the socket. Chunked or otherwise unknown-length bodies are read through a limit of one extra byte so the backend can detect overflow without buffering the rest. - A body over the limit returns HTTP 413 Payload Too Large.
- Body read failures and JSON parse/type failures return HTTP 400 Bad Request. The route handler is not called after a body error.
- POST routes that do not consume a JSON body retain their existing empty-body behavior; the contract applies when a route calls the shared body reader.
Responses use the existing JSON envelope:
{"ok":false,"error":"..."}
This boundary prevents a local caller or a browser-driven request from making
the backend buffer an unbounded payload. It also keeps malformed input from
being silently converted into {} and then reported as a misleading
route-specific validation error.
Regression coverage for valid objects, malformed/non-object JSON, bounded
streams, known oversized content lengths, and status codes lives in the Rust
backend tests in app/src-rust/src/main.rs.
Electron IPC Security
Renderer trust boundary, backend allowlist, updater validation.
Electron IPC Security Contract
Updated: 2026-08-11
MetalSharp's renderer is an untrusted caller. A renderer compromise must not turn the preload bridge into a generic command runner, arbitrary local HTTP client, or privileged installer launcher.
Trust-boundary rules
backend:requestaccepts only the exactGETandPOSTmethod/path pairs used by the renderer. Query-bearing endpoints validate their query keys and values (appid,prefix, and log offsets) instead of accepting arbitrary URLs. Migration cleanup and update-cache cleanup are main-process-only routes.app:install-depsaccepts a discriminated action union. Homebrew actions select one of the checked-in package names; script actions select one of the checked-in script names. The main process resolves scripts from the one packaged/developmentscriptsdirectory and never parses a renderer command string.updater:spawn-installtakes no renderer-controlled path, PID, or version. The main process asks the backend for the downloaded artifact, then requires a regular, non-symlink DMG under the configured MetalSharp update cache whose filename version exactly matches the backend-reported version. The backend PID and application PID are obtained by trusted main-process code.
The allowlist is intentionally explicit. Adding a renderer backend call
requires adding its method/path and, when applicable, a query validator to
app/src/main/ipc-security.ts, plus a renderer call-site test or review of the
corresponding backend contract.
Compatibility
The renderer's normal backend routes and update workflow remain unchanged from the user's perspective. The updater preload method is now parameterless, and the unused free-form dependency command interface is replaced by the typed action union. Backend route implementations remain the source of truth for their request bodies and existing process/PID containment checks.
D3D12 Pipeline Map
VKD3D path: vkd3d-proton + DXVK-macOS.
VKD3D Pipeline Map
Updated: 2026-08-05
VKD3D is the D3D12 route used by the game launcher. Its default backend is
vkd3d-proton: D3D12 -> vkd3d-proton -> Vulkan -> VKMT MoltenVK -> Metal.
The legacy DXMT D3D12 stack remains available as an instant rollback via
the vkd3dBackend setting (vkd3d-proton default / dxmt). The current
MetalSharp tree also contains a native metalsharp_d3d12 implementation and a
Cocoa/CAMetalLayer viewer path, but those are not the same runtime path that
VKD3D uses for Wine-launched games.
Runtime Ownership
| Layer | Current owner | Evidence | Status |
|---|---|---|---|
| Game detection | app/src-rust/src/mtsp/pe.rs, rules.rs |
D3D12 imports select PipelineId::VKD3D for 64-bit games. |
Present in current project |
| Pipeline definition | app/src-rust/src/mtsp/engine.rs |
Two VKD3D nodes: backend: "vkd3d-proton" (default, vkd3d_effective_node()) and the DXMT fallback node selected when vkd3dBackend=dxmt. Default deploys vkd3d-proton/DXVK/MoltenVK artifacts and sets d3d12,d3d12core,dxgi overrides. |
Present in current project |
| Launcher handoff | app/src-rust/src/mtsp/launcher.rs |
VKD3D routes through launch_dxmt_metal (name is legacy), which branches on node.backend: vkd3d-proton pins VK_ICD_FILENAMES + VKD3D_SHADER_CACHE_PATH/DXVK_STATE_CACHE_PATH; DXMT sets DXMT_CONFIG_FILE/DXMT_WINEMETAL_UNIXLIB. |
Present in current project |
| Shader/cache routing | app/src-rust/src/mtsp/shader_cache.rs |
VKD3D uses vkd3d and dxmt-metal12 cache directories. |
Present in current project |
| VKD3D default artifact surface | ~/.metalsharp/runtime/wine/lib/{vkd3d-proton,dxvk,moltenvk-vkmt} |
VKD3D loads d3d12.dll + d3d12core.dll (vkd3d-proton lane), dxgi.dll (DXVK lane), and VKMT's patched MoltenVK (libMoltenVK.dylib + MoltenVK_icd.json). |
Present in current project |
| VKD3D DXMT rollback surface | ~/.metalsharp/runtime/wine/lib/dxmt-vkd3d |
Only when vkd3dBackend=dxmt; also supplies the shared GPU vendor stubs (nvapi64.dll, nvngx.dll). |
Present in current project |
| DXMT surface | ~/.metalsharp/runtime/wine/lib/dxmt |
DXMT/DXMT(32) use the legacy DXMT payload. | Present in current project |
| DXMT D3D12 implementation | External DXMT source tree | Conformance branch contains the real DXMT D3D12/DXIL/winemetal work, used only by the VKD3D DXMT rollback lane. | External source tree |
| vkd3d-proton implementation | VKMT vkd3d-proton build | build-vkmt-win64-filtered produces the d3d12.dll forwarder + d3d12core.dll implementation shipped in the bundle. |
External source tree |
| Native D3D12 target | include/metalsharp/D3D12Device.h, src/d3d/d3d12/* |
Builds build/d3d12.dylib and exposes D3D12CreateDevice. |
In-tree, smoke-tested |
| Cocoa surface | src/win32/user32/WindowManager.mm, src/dxgi/DXGISwapChain.mm |
Creates NSWindow/CAMetalLayer for the native loader path. | In-tree, not the Wine VKD3D surface |
| Wine VKD3D surface | Default: Vulkan -> VKMT MoltenVK; rollback: DXMT winemetal.so |
Default VKD3D presents through MoltenVK; the DXMT rollback presents through Wine/winemetal. Neither uses the native WindowManager path. |
External runtime path |
VKD3D Launch Flow
- The PE scanner sees
d3d12.dlland rules selectPipelineId::VKD3D. - The launcher resolves the game directory and Wine prefix.
- VKD3D resolves its effective node from the
vkd3dBackendconfig (vkd3d_effective_node()), defaulting to vkd3d-proton. - Default VKD3D deploys from the vkd3d-proton/DXVK lanes into the game
directory:
d3d12.dll,d3d12core.dll,dxgi.dll, plus the GPU vendor stubsnvapi64.dll/nvngx.dll(from the shareddxmt_vkd3dlane). Rollback VKD3D deploys the DXMT set fromlib/dxmt-vkd3d/x86_64-windows. - VKD3D sets
WINEDLLOVERRIDESso Wine prefers the deployed native DLLs (d3d12,d3d12core,dxgi=n,b). - VKD3D adds the route's unix library paths to
DYLD_FALLBACK_LIBRARY_PATH(default:lib/wine/x86_64-unix,lib/moltenvk-vkmt; vkd3d-proton itself has no unix sidecar — its DLLs live in the windows lane only). - VKD3D sets shader/pipeline cache paths under the MetalSharp cache root and —
for the default backend — pins
VK_ICD_FILENAMESto the runtime-bundled VKMTMoltenVK_icd.json. - Wine launches the executable without a forced DirectX command-line flag.
dx12andd3d12are route aliases for selecting VKD3D, not universal game args. - vkd3d-proton translates D3D12 to Vulkan; MoltenVK presents on Metal. The
DXMT rollback instead compiles DXIL/MSL and presents through
winemetal.
Current Verification
These checks were run from the repository root:
cmake --build build --target test_d3d12
cmake --build build --target test_d3d12_entrypoint test_d3d12
./build/tests/test_d3d12
./build/tests/test_d3d12_entrypoint
ctest --test-dir build -R "d3d12|d3d12_entrypoint|phase18|phase19" --output-on-failure
nm -gU build/d3d12.dylib | rg "D3D12CreateDevice|D3D12GetDebugInterface|D3D12SerializeRootSignature"
otool -L build/d3d12.dylib
Results:
test_d3d12passed: 50 passed, 0 failed.test_d3d12_entrypointpassed: 5 passed, 0 failed.ctestpassedd3d12,d3d12_entrypoint,phase18, andphase19.build/d3d12.dylibexportsD3D12CreateDevice.build/d3d12.dyliblinks Metal, Foundation, QuartzCore, AppKit, andlibmetalirconverter.
The external DXMT source tree also rebuilt successfully with:
ninja -C <dxmt-source>/build src/winemetal/unix/winemetal.so src/d3d12/d3d12.dll
The current release-hosted graphics bundle carries five lanes:
Graphics/dll/dxmt: the DXMT surface used by DXMT/DXMT(32).Graphics/dll/dxmt-vkd3d: the DXMT VKD3D rollback surface (also the source of the sharednvapi64.dll/nvngx.dllGPU vendor stubs), includingwinemetal.so,libc++.1.dylib,libc++abi.1.dylib, andlibunwind.1.dylib.Graphics/dll/vkd3d-proton: the default VKD3D D3D12 stack (d3d12.dllforwarder +d3d12core.dll, VKMT win64-filtered build).Graphics/dll/dxvk: shared D3D9/D3D10/D3D11/DXGI surface;dxgi.dllfor the VKD3D route.Graphics/dll/moltenvk-vkmt: VKMT's patched MoltenVK (libMoltenVK.dylibMoltenVK_icd.json).
Completion State
| Area | State | Notes |
|---|---|---|
| VKD3D app routing | Primary/stable | Current project maps D3D12 games to VKD3D before broad directory heuristics, uses VKD3D as the unresolved default, and invokes the backend launcher path. |
| VKD3D backend handoff | Present | The handoff is backend-aware: vkd3d-proton copies vkd3d-proton/DXVK/MoltenVK artifacts and pins VK_ICD_FILENAMES; DXMT rollback copies the dxmt-vkd3d set. |
| Subnautica-class VKD3D runtime | Demonstrated by local use | This validates the launcher/runtime path, not the native CMake D3D12 dylib. |
| Avery DXMT probes | Strongest external proof (DXMT lane) | tests/ROADMAP.md in dxmt-src marks probes 2-6 complete, including compute, triangle, indexed draw, depth, and texture sampling. |
| Deployed runtime parity | Split surface | DXMT/DXMT(32) stay on the dxmt surface; VKD3D uses the vkd3d-proton/DXVK-macOS/MoltenVK lanes. |
| Avery source cleanliness | Needs cleanup | dxmt-src has dirty debug/probe changes and notes that prior dirty changes broke Steam launching. |
| Native in-tree D3D12 | Expanded coverage | Smoke, C entrypoint, MSL compute PSO dispatch, and MSL indexed draw tests pass. |
| Native compute PSO | Implemented for MSL/DXBC/DXIL paths | The in-tree native CreateComputePipelineState now creates a real Metal compute pipeline when shader bytecode is available. |
| Native indexed draw | Covered by offscreen test | GPU virtual-address lookup now binds real Metal vertex/index buffers and the test executes an indexed draw. |
| Native raytracing/mesh | Stubbed | Advanced D3D12 calls return success or placeholders without full Metal execution. |
| Native Cocoa viewer | Implemented separately | The NSWindow/CAMetalLayer path exists for native-loader presentation; VKD3D Wine games present through MoltenVK (default) or DXMT/winemetal (rollback). |
Stability Gaps To Close
- Add a first-class VKD3D runtime verification command in this repo that launches
a small D3D12 probe through the same
launch_dxmt_metalenvironment used by games. — Addressed (Phase 3):GET /diagnostics/vkd3d/dry-run?appid=...andGET /diagnostics/pipeline/dry-run?appid=...&pipeline=vkd3dreport the exact env pairs, artifact hashes, and unix sidecars VKD3D would load, using the samesteam_pipeline_env_pairsbuilder aslaunch_dxmt_metal, without launching Steam or the game. The existingPOST /steam/d3d12-runtime-doctorruns the SDK mini-probe suite through that same environment. - Add a native Cocoa viewer test target if the goal is to exercise the in-tree
metalsharp_d3d12implementation through CAMetalLayer rather than through Wine/winemetal. - Expand native D3D12 tests beyond the current graphics/compute coverage: texture sampling, depth compare, and swapchain present.
Practical Conclusion
The current MetalSharp project treats VKD3D as the vkd3d-proton D3D12 route
by default (D3D12 -> Vulkan -> VKMT MoltenVK -> Metal) while keeping the DXMT
D3D12 stack available as the vkd3dBackend=dxmt rollback. D3D12 PE import
detection selects VKD3D, the backend handoff deploys the vkd3d-proton/DXVK/
MoltenVK runtime by default (the dxmt-vkd3d runtime under rollback), and
DXMT/DXMT(32) use the dxmt surface that is known to work for
current Steam/Wine titles.
VKD3D Artifact and Launch Verification (Phase 3)
A reviewer can prove VKD3D loaded the intended artifacts without launching a full
game using the read-only dry-run verifier. It runs through the same environment
builder (steam_pipeline_env_pairs) as launch_dxmt_metal, so the reported env
pairs and artifact sources are exactly what a real VKD3D launch would use.
GET /diagnostics/vkd3d/dry-run?appid=<appid>— VKD3D-specific dry-run. For the default backend it reports the vkd3d-proton/DXVK/MoltenVK artifacts and env (VKD3D_SHADER_CACHE_PATH,DXVK_STATE_CACHE_PATH,VK_ICD_FILENAMES); for the DXMT rollback it reports thelib/dxmt-vkd3d/x86_64-unixsidecars (winemetal.so,libc++.1.dylib,libc++abi.1.dylib,libunwind.1.dylib) andDXMT_WINEMETAL_UNIXLIB.GET /diagnostics/pipeline/dry-run?appid=<appid>&pipeline=vkd3d|m11|...— generic pipeline dry-run for comparing lanes.
The dry-run reports, per artifact: resolved source path, presence, sha256, and
size; required artifacts that are missing produce a structured ok: false with
a missing[] array rather than a silent fallback.
Contract guarantees covered by tests:
- Default VKD3D deploys
d3d12.dll,d3d12core.dll,dxgi.dll(DXVK lane), and thenvapi64.dll/nvngx.dllstubs (dxmt_vkd3d lane) from the runtime lanes; the DXMT rollback deploys thelib/dxmt-vkd3d/x86_64-windowsset. - M11 does not deploy
d3d12.dlland points only atlib/dxmt, neverlib/dxmt-vkd3d. - VKD3D dry-run includes
d3d12.dll; M11 dry-run does not.
D3D10 Pipeline Map
Historical D3D10 route documentation.
M10 Pipeline Map
Updated: 2026-07-08
M10 is the stable D3D10 engine path. It launches Windows D3D10 titles through Wine and DXMT, then hands rendering to Metal through DXMT's winemetal bridge.
Runtime Shape
D3D10 game
-> Wine
-> Wine d3d10.dll / d3d10_1.dll public entrypoints
-> DXMT d3d10core.dll
-> DXMT d3d11.dll + dxgi.dll
-> winemetal.dll / winemetal.so
-> Metal command buffers
-> Apple GPU
M10 deploys Wine's public D3D10 entrypoint DLLs for games that import d3d10.dll or d3d10_1.dll, then routes the core handoff through DXMT's d3d10core.dll plus the same DXMT D3D11, DXGI, and winemetal runtime used by M11.
M10 deploys these public D3D10 entrypoints from ~/.metalsharp/runtime/wine/lib/wine/x86_64-windows/:
d3d10.dlld3d10_1.dll
M10 deploys these DXMT handoff DLLs from ~/.metalsharp/runtime/wine/lib/dxmt/x86_64-windows/:
d3d11.dlldxgi.dlld3d10core.dllwinemetal.dll
M10 deliberately does not deploy d3d12.dll.
Engine Contract
| Field | Value |
|---|---|
| Pipeline | M10 |
| Backend | dxmt |
| Launch args | none by default; dx10/d3d10 select M10 as route aliases |
| Wine overrides | d3d10,d3d10_1,dxgi,d3d11,d3d10core=n,b;gameoverlayrenderer,gameoverlayrenderer64=d |
| Shader cache subdir | m10 |
| Preset fallback family | m10, then dxmt-metal |
M10 uses the same DXMT Unix library search path as M11:
lib/wine/x86_64-unix
lib/dxmt/x86_64-unix
Selection Rules
The backend resolves M10 from 64-bit PE imports before broad directory heuristics. That keeps 64-bit D3D10 games from being demoted to M11 just because their folder also includes common engine or Steam markers. 32-bit D3D10 executables are not routed into M10 because this runtime contract deploys the x86_64 D3D10/DXMT payload.
Recognized D3D10 imports:
d3d10.dlld3d10_1.dlld3d10core.dll
If a game imports both D3D12 and D3D10 compatibility DLLs, D3D12 still wins and maps to VKD3D for 64-bit executables.
DXMT & Vulkan Architecture
DXMT/DXMT(32)/VKD3D and Vulkan fallback boundaries.
DXMT and Vulkan Architecture
Updated: 2026-07-08
MetalSharp has two graphics translation families:
- DXMT launch family: DXMT/DXMT(32) to Metal
- Vulkan family: VKD3D (vkd3d-proton + DXVK-macOS) to Metal via MoltenVK
Pipeline Map
| Public route | Translation |
|---|---|
| VKD3D | D3D9/D3D10/D3D11/D3D12 -> vkd3d-proton + DXVK-macOS -> Vulkan -> MoltenVK -> Metal |
| DXMT | D3D10/D3D11 -> DXMT -> Metal (x86_64) |
| DXMT(32) | D3D10/D3D11 -> DXMT -> Metal (32-bit / i386) |
dxmt is the internal auto-router that selects VKD3D/DXMT/DXMT(32). M32, Wine, and macOS Steam are backend fallback/diagnostic paths, not normal graphics-route buttons.
DXMT
The DXMT launch family is used by DXMT and DXMT(32). It deploys Wine's public d3d10.dll and d3d10_1.dll entrypoints for imported D3D10 APIs, then routes the core handoff through DXMT's d3d10core.dll and the shared D3D11/DXGI/winemetal stack.
DXMT-family DLLs:
| DLL | Used by |
|---|---|
d3d10.dll, d3d10_1.dll |
DXMT public Wine D3D10 entrypoints |
d3d10core.dll |
DXMT, DXMT(32) |
d3d11.dll, dxgi.dll |
DXMT, DXMT(32) |
winemetal.dll |
DXMT, DXMT(32) |
winemetal.so |
Unix Metal bridge |
Basic flow:
Game
-> DXMT PE DLL
-> winemetal.so
-> Metal command buffers
-> Apple GPU
DXMT uses per-game shader caches under:
~/.metalsharp/shader-cache/dxmt/<appid>/
~/.metalsharp/shader-cache/dxmt_32/<appid>/
~/.metalsharp/shader-cache/vkd3d/<appid>/
Older dxmt-metal and dxmt-metal12 cache family names may still exist on disk from previous builds, but current MTSP
routes prefer the explicit DXMT/DXMT(32)/VKD3D cache namespaces.
VKD3D
VKD3D is the complete Vulkan pipeline. It deploys vkd3d-proton's d3d12.dll + d3d12core.dll plus the DXVK-macOS d3d9.dll, d3d10core.dll, d3d11.dll, and dxgi.dll into the game folder, routed via n,b overrides on the VKMT MoltenVK lane. D3D9 games run through DXVK-macOS on the same Vulkan → MoltenVK path.
Basic flow:
Game
-> vkd3d-proton / DXVK-macOS PE DLL
-> Vulkan
-> MoltenVK
-> Metal
VKD3D cache path:
~/.metalsharp/shader-cache/vkd3d/<appid>/
Current Game Notes
| Game | Best/current pipeline |
|---|---|
| Schedule 1 | VKD3D recommended |
| Subnautica | DXMT |
| Subnautica: Below Zero | VKD3D recommended, DXMT optimized |
| Rain World | DXMT, VKD3D also works |
| Undertale | VKD3D |
| Portal 2 | VKD3D |
| Nidhogg 2 | VKD3D |
| Ghostrunner | VKD3D only if needed |
| DREDGE | Not a DXMT/FNA target yet; 32-bit Unity embedded Mono crash |
| Goat Simulator | VKD3D, blocked before graphics by native .NET 4.0 CLR install |
Notes
- DXMT is the internal direct-Metal auto-router, not a visible route selector option.
- VKD3D is the complete Vulkan pipeline (vkd3d-proton + DXVK-macOS on MoltenVK), covering D3D9/D3D10/D3D11/D3D12.
- DXMT and DXMT(32) share the
dxmt-metalpreset fallback family. - 32-bit and Wine fallback cases remain backend internals unless promoted to a public route.
VKD3D Shader Engine
Shader compilation pipeline notes.
VKD3D D3D12 Shader Engine
Updated: 2026-07-08
VKD3D treats shader translation as a defined engine, not as a single converter function. The engine boundary starts when a D3D12 game supplies DXBC or DXIL bytecode and ends when a Metal render or compute pipeline is created, cached, bound, and used by a draw, dispatch, or present pass.
The shader engine is also a deployment contract. Install and migration must put known-good shader-engine material on disk, launch must seed the selected VKD3D cache from that material, and diagnostics must write to logs instead of mixing proof output into shader or pipeline caches.
Engine Surfaces
- DXIL intake:
dxil_container,llvm_bitcode, anddxil_irparse DXIL containers, LLVM bitcode, shader model, entrypoint, values, types, and resource handles. - MSL lowering:
msl_loweringis the primary typed DXIL-to-MSL path. It owns typed value coercion, DX op lowering, vertex input handling, binding manifests, and targeted compatibility fallbacks.dxil_to_mslremains the fallback path and must follow the same resource-binding rules. - D3D12 shader compiler:
d3d12_shader_compilerowns runtime compilation, cache lookup, converter selection, generated MSL sidecars, metallib creation, and compile diagnostics. - PSO build:
d3d12_deviceandd3d12_pipeline_statemap D3D12 graphics and compute PSO descriptors to Metal pipeline states. - Runtime binding: command list and command queue code bind descriptor tables, root constants, direct buffers, vertex inputs, draw args, and present resources before command encoding.
- Cache and diagnostics: shader cache paths, MSL/metallib sidecars, root-signature reports, PSO manifests, and focused D3D12 trace output are part of the engine contract because stale or incomplete cache data can make a fixed shader look broken.
- Installed corpus: checked-in shader corpora under
tools/d3d12-metal-sdk/shader-corpus/are packaged into the runtime and scripts/tools bundles. Install and migration validate the expected corpus proof file and runtime-safe material before VKD3D is considered ready.
Runtime Material
VKD3D shader-engine material can be sourced from:
~/.metalsharp/runtime/wine/share/d3d12-metal-sdk/shader-corpus/
~/.metalsharp/runtime/d3d12-metal-sdk/shader-corpus/
~/.metalsharp/scripts/tools/d3d12-metal-sdk/shader-corpus/
The install/migration readiness proof is the source-controlled corpus checksum file:
elden-ring-present-vb-pull-20260612/proof/SHA256SUMS
That proof must exist under at least one installed corpus source, and the source
must also contain runtime-safe shader-engine material. This prevents a partial
archive with one stray .metallib from passing VKD3D readiness.
When a game uses the VKD3D cache namespace, shader_cache.rs copies runtime-safe
files into:
~/.metalsharp/shader-cache/vkd3d/<appid>/
The copied file classes are:
.metallib
.air
.msl
.dxbc
.dxil
.cso
.json
.module.txt
.dxil_report.txt
The cache seeding step intentionally skips proof, result, log, and direct Metal error directories. Runtime caches should contain shader-engine inputs and sidecars, not the entire developer proof tree.
Logs are separate:
~/.metalsharp/logs/vkd3d-pipeline/<appid>/
Pipeline caches are also separate:
~/.metalsharp/pipeline-cache/vkd3d/<appid>/
This split matters. A shader fix is not proven by a stale .metallib, and a
runtime log is not a shader cache input.
Invariants
- A DXIL
createHandleregression must be proven against generated MSL, not inferred from a game frame. The present-blend regression for Elden shader6f0e7d2f3cfff83cproves the engine samplestex0andtex1, not the old double-countedtex2path. - Every typed MSL shader must emit
metalsharp.binding_manifest.v1so offline tooling can audit direct buffers, textures, samplers, and descriptor ranges. - Root-signature coverage, PSO manifests, and shader manifests are separate proof layers. Passing one layer does not imply the others are correct.
- Live game captures are diagnostics only. Contract status comes from source contracts, converter tests, corpus replay, PSO audits, and SDK probes.
- Shader cache invalidation is part of correctness. When lowering changes,
stale
.msl,.metallib, and pipeline-cache outputs must not be used as proof of current behavior. - A present or fullscreen fallback shader must preserve the draw shape it is replacing. For example, the procedural fallback distinguishes a 4-vertex triangle-strip fullscreen quad from a 3-vertex fullscreen triangle by reading the bound draw args instead of clamping every draw to three vertices.
- A binding-completeness failure is not only a shader failure. Root signature, descriptor table, direct root buffer, vertex input, render-target, and PSO metadata must be checked together before blaming lowering.
Proof Layers
The engine has separate proof layers. They are cumulative, not interchangeable.
| Layer | Artifact | Proves |
|---|---|---|
| DXIL intake | .dxbc, .dxil, .module.txt, DXIL reports |
The bytecode was found, parsed, and classified. |
| Lowering | .msl, binding manifests |
DXIL values, resources, semantics, and bindings lowered into MSL. |
| Metal compile | .air, .metallib, compiler logs |
Generated MSL compiles and links under Apple's Metal toolchain. |
| Root signature | root-signature reports | Shader bindings can be matched to D3D12 root parameters and descriptor ranges. |
| PSO manifests | pso-render-*.json, pso-compute-*.json |
Render/compute pipeline descriptors have enough format, topology, shader, and binding metadata. |
| Runtime binding | focused vkd3d.log diagnostics |
Command lists bind descriptors, buffers, draw args, render targets, and resources before encoding. |
| Developer probes | tools/d3d12-metal-sdk/results/*.json |
The pipeline behavior is reproducible without a commercial game launch. |
Required Gates
Run the structural engine check first:
python3 tools/d3d12-metal-sdk/scripts/validate-shader-engine.py \
--json tools/d3d12-metal-sdk/results/shader-engine-audit-metalsharp.json
Then run the focused native converter gate against the relevant captured corpus:
ninja -C vendor/dxmt/build-metalsharp-x64-tests tests/test_dxil_converter
vendor/dxmt/build-metalsharp-x64-tests/tests/test_dxil_converter <shader-cache>
For generated MSL directories, run:
python3 tools/d3d12-metal-sdk/scripts/dxil-binding-manifest-audit.py \
--msl-dir <generated-msl> \
--markdown tools/d3d12-metal-sdk/results/binding-manifest-audit.md
For captured runtime PSO/root-signature corpora, run the root-signature, graphics PSO, and compute PSO audits before a game launch is used as evidence.
The full SDK render proof remains:
tools/ci/vkd3d-check.sh
For release-bundle proof, also run:
tools/bundles/verify-bundles.sh --bundle-dir app/bundles --require mac
tools/bundles/verify-developer-sdk.sh app/bundles/metalsharp-d3d12-developer-sdk.tar.zst
vkd3d-check.sh verifies the downloaded runtime/graphics archives against the
pinned digests in tools/ci/vkd3d-bundle-hashes.tsv (via
tools/ci/verify-bundle-sha256.sh) before staging them, so the gate can never
silently execute tampered bundle material.
Contract Authority
The machine-readable source of truth is
tools/d3d12-metal-sdk/contracts/d3d12-shader-engine-contract.json.
It names the source surfaces, required offline gates, runtime artifacts, and
review evidence. validate-contracts.py includes this contract, while
validate-shader-engine.py verifies the engine-specific source and gate shape.
Developer Stress Executables
Two Windows executables exist so VKD3D development does not depend on repeated commercial game launches:
vkd3d_game.exe: the PR/CI cube proof built bytools/ci/vkd3d-check.sh.vkd3d_stress_game.exe: the higher-pressure title-like scene built bytools/d3d12-metal-sdk/scripts/vkd3d-dev.sh stress-game.
vkd3d_stress_game.exe is deliberately broader than a unit probe. It starts with
a splash/movie-style pass, then renders a beach scene with water, sun, boat,
tree geometry, text, textures, shadows, unusual vertices, and repeated presents.
It should be used when changing the shader engine, PSO creation, vertex binding,
texture sampling, or present path and a game run would be too unstable.
DXMT Proton Parity
Active roadmap.
DXMT Proton-Parity Roadmap
Created: 2026-05-29 Status: Proposed
Status note (2026-08-05): This roadmap's core premise has been superseded. MetalSharp now ships vkd3d-proton as the default VKD3D backend (D3D12 → Vulkan → VKMT MoltenVK → Metal, PR #377), which resolves the "why not pivot" question (lines 10-15) in-tree: VKMT's patched MoltenVK provides the Vulkan extensions vkd3d-proton needs, and the double-translation tax is accepted for D3D12 while the DXMT routes (DXMT/DXMT(32)) keep the direct D3D→Metal path. The DXIL→MSL converter work below now only serves the
vkd3dBackend=dxmtrollback lane. Keep this roadmap for its runtime-discipline patterns; treat the "VKD3D-Proton cannot run on macOS" premise as resolved.
This roadmap doubles down on MetalSharp's direct D3D→Metal architecture (the correct path for macOS) while systematically adopting Proton's proven runtime discipline patterns. Every item is grounded in current codebase analysis.
Why Not Pivot to Proton/Linux
Proton runs Windows PE binaries through Wine on Linux with D3D→Vulkan translation. MetalSharp runs Windows PE binaries through Wine on macOS with D3D→Metal translation. These are the same architectural pattern on different platforms. Proton cannot run on macOS because: - VKD3D-Proton requires Vulkan extensions MoltenVK doesn't provide — resolved in-tree (2026-08-05): VKMT's patched MoltenVK supplies the required extensions; vkd3d-proton is the default VKD3D backend - Proton's Wine fork has no macdrv backend - Steam Linux Runtime uses Linux namespaces (no macOS equivalent)
MetalSharp's direct D3D→Metal path avoids the double-translation tax (D3D→Vulkan→Metal) and doesn't depend on Apple's closed-source D3DMetal. This is the right architecture for the D3D9/D3D10/D3D11 routes; for D3D12 the default is now the vkd3d-proton → Vulkan → MoltenVK route, with the DXMT direct path retained as rollback. The play is to accelerate DXMT's coverage and mine Proton's ecosystem for patterns.
Phase 1: DXIL Converter Critical Fixes
Why first: The DXIL→MSL converter is the #1 blocker for D3D12 games. 75% of shaders fail. Three fundamental issues make most real shaders produce incorrect MSL.
Impact: Unblocks every D3D12 game that uses DXIL shaders (SM 6.0+), which is the majority of modern titles.
1A. Control Flow Graph Reconstruction — DONE
Completed: 2026-05-29. Branch codex/beta7-dxmt-cohesion on aaf2tbz/metalsharp.
What was done:
Parser fixes (llvm_bitcode.cpp/hpp):
- Track basic block value IDs in DECLAREBLOCKS so PHI block references resolve to block indices
- Store both incoming value AND incoming block in PHI operands (was losing block refs)
- Parse Switch instructions (was falling through to default: break)
Converter rewrite (dxil_to_msl.cpp):
- Build successor/predecessor CFG from branch terminators
- Collect PHI info with (value, predecessor block) pairs
- Pre-declare PHI variables at function scope with typed defaults (float, double, int vector, float vector)
- Emit blocks with labels (bb0:, bb1:, ...) and goto-based control flow
- Conditional Br → if (cond) goto bb_true; else goto bb_false;
- Unconditional Br to next block → fall through (no goto)
- Unconditional Br to non-next → goto bbN
- Switch → switch/case/goto dispatch
- PHI assignments at predecessor block exits before terminator
- Ret/Unreachable handled in CFG emission, not emitInstruction
- Diagnostic trace for unresolved PHI block references
Syntax validation: clang++ -fsyntax-only -Wall -Wextra -Werror passes on all three changed files.
Remaining: Unit tests (Phase 1E) and live shader validation against real DXIL game shaders.
1B. Struct Field Extraction Fix — DONE
Completed: 2026-05-29. Branch codex/beta7-dxmt-cohesion on aaf2tbz/metalsharp.
What was done:
Value type tracking (dxil_to_msl.hpp):
- Added value_type_ids parallel vector to EmitContext — maps value IDs to their LLVM type IDs
- Populated from: Call instruction results (texture load returns), InsertValue results, PHI results, constants
Struct-aware ExtractValue (dxil_to_msl.cpp):
- Looks up aggregate type from value_type_ids to distinguish struct vs vector
- ExtractValue on struct field 0: passes through aggregate expression (e.g., tex.read(coord) returns float4 — the whole thing IS the value)
- ExtractValue on struct field 1+: emits typed default based on struct field type (0, 0.0f, 0.0)
- ExtractValue on vectors: retains .x/.y/.z/.w component suffix (unchanged behavior)
- ExtractValue on unknown types: falls back to current behavior
Struct-aware InsertValue:
- Propagates aggregate type from source to result via value_type_ids
- Skips .x/.y/.z/.w field writes for struct aggregates (struct field assignment not yet fully supported)
Impact: Fixes "member reference on device float" errors from chained ExtractValue on ResRet structs (texture load → extract field 0 → extract component .x). The second ExtractValue now gets a proper float4 instead of a scalar.
Remaining: Nested struct field writes in InsertValue, multi-level ExtractValue indices.
1C. Groupshared Memory Fix
Current state: Alloca handler allocates thread char storage[256] (line 3003-3005). This is thread-local, not threadgroup-shared. Any compute shader using shared memory silently uses per-thread copies.
Files:
- dxmt-src/src/airconv/dxil/dxil_to_msl.cpp lines 2997-3005
Work:
- Detect DXIL groupshared address space from LLVM alloca metadata
- Emit threadgroup char storage[N] for groupshared allocations
- Size the allocation from the DXIL alloca size, not hardcoded 256
- Handle alignment requirements
Validation: - Test compute shader that writes to groupshared, barriers, then reads from another thread - Verify threadgroup barrier + groupshared interaction
Milestone: Compute shaders with groupshared memory produce correct shared-memory MSL.
1D. Threadgroup Size from Metadata
Current state: Threadgroup size hardcoded to {1,1,1} in converter output (line 3384-3386). The caller must override from DXIL metadata.
Files:
- dxmt-src/src/airconv/dxil/dxil_to_msl.cpp lines 3384-3386
- dxmt-src/src/airconv/dxil/dxil_container.cpp (parse entry point metadata)
Work:
- Parse [numthreads(X,Y,Z)] from DXIL entry point metadata
- Emit correct threadgroup size in MSL compute kernel attribute
- Pass through to MSLShader::tg_size
Milestone: Compute shaders emit correct threadgroup size without caller override.
1E. DXIL Converter Test Infrastructure
Current state: Zero unit tests for DXIL path. All 6 conformance probes test DXBC SM 5.0 only.
Work:
- Create dxmt-src/tests/dxil/ directory
- Build HLSL→DXC→DXIL→MSL→metallib pipeline test
- Fixture shaders covering: basic compute, vertex+pixel pair, texture sampling, structured buffer, constant buffer, branch/loop, groupshared
- Add to CI alongside existing DXBC probes
- Regression tests for the May 26 scalar/vector fixes
Milestone: 10+ DXIL test shaders compile and produce valid Metal shaders.
Phase 2: Per-Game Prefix Isolation
Why second: The shared prefix-steam is the biggest Proton pattern gap. One game's VC++ install breaks another's. Registry conflicts between games. DXSetup can't be run safely.
Impact: Eliminates the class of bugs where "game A worked until I installed game B."
2A. Per-Appid Wine Prefix Creation
Current state: All Steam games use ~/.metalsharp/prefix-steam/. The compatdata doc says per-game prefixes are "future work."
Files:
- metalsharp-repo/app/src-rust/src/mtsp/launcher.rs line 963 (shared prefix)
- metalsharp-repo/app/src-rust/src/steam.rs line 15 (steam_prefix())
- metalsharp-repo/app/src-rust/src/mtsp/bottles.rs line 324 (steam_launch_prefix())
- metalsharp-repo/docs/runtime/compatdata-architecture.md line 23
Work:
- Create ~/.metalsharp/compatdata/<appid>/pfx/ per Steam game on first launch
- Copy prefix-steam as template for new per-game prefixes (Proton's default_pfx pattern)
- Wine Steam itself stays in prefix-steam (only game processes move to per-game prefixes)
- Update steam_launch_prefix() to return per-game path when available
- Mount Steam library drives into per-game prefixes (drive letter mapping)
- Migration: existing prefix-steam remains as the Steam client prefix; new launches create per-game
Milestone: Two different Steam games can install conflicting VC++ runtimes without breaking each other.
2B. Prefix Template with Base Redistributables
Current state: MetalSharp detects missing components but doesn't auto-install them. Proton copies base redists into every new prefix.
Files:
- metalsharp-repo/app/src-rust/src/mtsp/recipe.rs (recipe_component_satisfied)
- metalsharp-repo/app/src-rust/src/bottles.rs (component system)
Work:
- Create ~/.metalsharp/runtime/redist/ central DLL cache (d3dcompiler_, d3dx9_, xinput1_3, xaudio*, vcruntime140, msvcp140, ucrtbase, atl, openal, physx)
- Symlink or copy from central cache into new per-game prefixes (Proton's builtin_dll_copy pattern)
- Source DLLs from: Steam CommonRedist, MetalSharp runtime, DXC redistributable
- Component receipts: track what was installed in each prefix
Milestone: New per-game prefix is pre-populated with standard redists. Game that needs d3dx9_43.dll finds it without manual install.
2C. Redistributable Auto-Install
Current state: Component repair requires manual trigger or Runtime Doctor. Proton auto-installs CommonRedist during game install.
Files:
- metalsharp-repo/app/src-rust/src/mtsp/setup.rs (runtime setup)
- metalsharp-repo/app/src-rust/src/bottles.rs (component repair)
Work:
- On first game launch, check component requirements from mtsp-rules.toml
- Auto-run silent VC++ / DirectX installers into per-game prefix when missing
- Track install receipts per prefix
- Handle DXSetup.exe, vcredist_x64.exe, vcredist_x86.exe via Wine with /Q silent flags
- Source from Steam CommonRedist or ~/.metalsharp/runtime/redist/
Milestone: Game requiring vcrun2019 + directx_jun2010 auto-installs both on first launch without user intervention.
Phase 3: Compat Config Expansion
Why third: With per-game prefixes working and DXIL converter fixed, the next multiplier is game coverage. MetalSharp has 64 game rules. Proton has 200+ hardcoded plus thousands via Steam cloud.
Impact: Goes from "works for games we've manually tested" to "works for most D3D11/D3D12 games."
3A. Compat Config Flag System
Current state: Per-game fixes are hardcoded in Rust (Subnautica 2 UE5 args in launcher.rs, preferred exe names in recipe.rs). Proton uses a flag system that Steam can toggle from the cloud.
Files:
- metalsharp-repo/configs/mtsp-rules.toml (current 64-game rules)
- metalsharp-repo/app/src-rust/src/mtsp/rules.rs (parser)
- metalsharp-repo/app/src-rust/src/mtsp/launcher.rs (hardcoded fixes)
Work:
- Extend mtsp-rules.toml with flag fields:
toml
[overrides.1962700]
pipeline = "vkd3d"
name = "Subnautica 2"
flags = ["disable_nanite", "disable_pso_cache", "transcode_movies"]
gpu_vendor_stubs = ["nvidia"]
heap_delay_free = false
preferred_exe = ["Subnautica2.exe"]
config_patches = [
{ path = "Saved/Config/{platform}/Engine.ini", section = "SystemSettings", keys = { "r.Nanite" = "0" } }
]
launch_args = ["-NoNanite", "-NoShaderPipelineCache"]
- Migrate hardcoded fixes from Rust to TOML data
- Eliminate scripts/setup-*-deps.sh shell scripts (move logic into TOML + Rust)
Milestone: All per-game behavior is expressed in TOML. Zero game-specific hardcoded logic in Rust.
3B. Proton Game Fix Mining
Current state: 64 games in mtsp-rules.toml. Proton has ~200+ game-specific fixes in its Python script.
Work:
- Parse Proton's default_compat_config() appid→flag mapping
- Cross-reference with MetalSharp's existing rules
- For each Proton game fix, determine:
- Is it a Linux/Vulkan-specific fix? (skip these)
- Is it a Wine/DLL/env fix applicable to macOS? (port these)
- Is it a new game not in MetalSharp's rules? (add entry)
- Categories to port: disablenvapi, heapdelayfree, heapzeromemory, WINEDLLOVERRIDES tweaks, WINE_CPU_TOPOLOGY, launch arg appends
- Categories to skip: PROTON_USE_WINED3D, PROTON_NO_FSYNC, Steam Linux Runtime flags
Target: Expand mtsp-rules.toml from 64 to 200+ game entries.
Milestone: Top 200 Steam games by player count all have TOML entries (even if just pipeline = "vkd3d").
3C. Config File Patching Generalization
Current state: Subnautica 2 has hardcoded write_marked_config_block() for Engine.ini. No other games get config patches.
Files:
- metalsharp-repo/app/src-rust/src/mtsp/launcher.rs (write_marked_config_block)
Work: - Generalize to data-driven config patch system in TOML - Support: INI format, JSON format, XML format - Marked block replacement (only rewrite between MetalSharp markers) - Per-game config paths with platform variable substitution - UE5 Engine.ini, GameUserSettings.ini patterns as templates
Milestone: Any game can get INI config patches via TOML without code changes.
Phase 4: Steam Bridge Completion
Why fourth: The lsteamclient bridge only forwards 6 Steam API functions. Proton auto-generates hundreds of interface thunks. Broken Steam integration means no Steam Overlay, no achievements, no Workshop, no cloud saves for most games.
Impact: Full Steam functionality (overlay, friends, achievements, workshop, cloud saves) for games running through MetalSharp.
4A. Vendor Proton lsteamclient Source
Current state: src/steam/build.sh references src/steam/proton/lsteamclient/ but the directory doesn't exist. The build infrastructure is scaffolded but the Proton source isn't vendored.
Files:
- metalsharp-repo/src/steam/build.sh (scaffolded build script)
- metalsharp-repo/src/steam/bridge/unix_steamclient.c (hand-written 6-function bridge)
Work:
- Vendor Proton's lsteamclient/ source tree into src/steam/proton/lsteamclient/
- Pin to Proton 11.0 branch (matching Wine 11.x baseline)
- Adapt build.sh for macOS: arm64 Unix dylib + x86_64 PE DLL
- Generate cppISteam*.cpp interface thunks from Steam API headers
- Build both steamclient64.dll (PE) and lsteamclient.dylib (macOS native)
Milestone: Proton lsteamclient builds on macOS with full Steam API surface.
4B. Steam API Integration Testing
Work: - Test ISteamUser (login state), ISteamFriends (overlay), ISteamUserStats (achievements) - Test ISteamRemoteStorage (cloud saves), ISteamUGC (workshop) - Test ISteamApps (app ownership), ISteamUtils (overlay toggle) - Verify Steam callbacks flow correctly through the bridge - Test with a game that uses Steam API heavily (e.g., a Steamworks SDK example app)
Milestone: A Steam game running through MetalSharp shows Steam overlay, records achievements, and syncs cloud saves.
Phase 5: DXIL Converter Expansion
Why fifth: After critical fixes (Phase 1) unblock basic games, this phase expands shader coverage to handle more complex D3D12 titles.
5A. Texture Dimension Coverage
Current state: All textures are texture2d<float>. No 1D, 3D, cube, array, or MSAA textures.
Files:
- dxmt-src/src/airconv/dxil/dxil_to_msl.cpp (texture type emission, sampling, store)
Work:
- Detect texture dimension from DXIL resource kind (Texture1D/2D/3D/Cube/1DArray/2DArray/CubeArray/MS)
- Emit correct Metal texture types: texture1d, texture2d, texture3d, texture_cube, texture2d_array, texturecube_array, texture2d_ms
- Handle cube map sampling with sample(cube, float3)
- Handle array sampling with sample(array, float2, uint/array_index)
- Handle MSAA with read(uint2, uint/sample)
Milestone: Cube map reflections, texture arrays, and 3D volume textures work in DXIL shaders.
5B. Wave/Subgroup Operations
Current state: Wave operations are advertised via CheckFeatureSupport (OPTIONS1: WaveOps=TRUE, WaveLaneCount=32) but the converter emits nothing for wave intrinsics.
Work:
- Map DXIL wave intrinsics to Metal SIMD-group operations:
- WaveIsFirstLane → simd_is_first()
- WaveGetLaneIndex → simd_lane_id
- WaveGetLaneCount → simd_lane_count
- WaveActiveAllTrue/AnyTrue → simd_all()/simd_any()
- WaveActiveBallot → simd_ballot()
- WaveReadLaneFirst/At → simd_broadcast() / simd_shuffle()
- WaveActiveOp (sum/bitand/bitor/bitxor/min/max) → simd_reduce_*()
- WavePrefixOp → simd_prefix_inclusive_*()
- QuadReadLaneAt → quad_shuffle()
- Handle quad-wide operations for pixel shaders
Milestone: Compute shader using WaveActiveSum and WavePrefixSum produces correct results.
5C. Atomic Operation Completeness
Current state: Only atomic_fetch_add_explicit and atomic_load_explicit are implemented. Missing: min, max, and, or, xor, exchange.
Work: - Implement all DXIL AtomicBinOp sub-opcodes: Add, Sub, And, Or, Xor, IMin, IMax, UMin, UMax - Implement atomic compare-exchange with proper loop pattern - Handle 64-bit atomics (int64 shader ops are advertised) - Test with real game compute shaders that use atomic counters
Milestone: All DXIL atomic operations produce correct Metal atomic calls.
5D. Geometry and Tessellation Shaders
Current state: Geometry shader passthrough exists in the command queue (via Metal object+mesh pipeline) but the DXIL converter doesn't handle geometry shaders. Tessellation has 5 TESS TODO markers.
Files:
- dxmt-src/src/airconv/dxil/dxil_to_msl.cpp (no GS/HS/DS in emitFunctionPrologue)
- dxmt-src/src/airconv/dxbc_converter_ts.cpp (DXBC tessellation, 5 TODOs)
- dxmt-src/src/airconv/dxbc_converter_gs.cpp (DXBC geometry)
Work: - Add GS handling to DXIL converter emitFunctionPrologue - Add HS/DS handling for tessellation - Map DXIL geometry shader output to Metal mesh shader thread dispatch - Map hull/domain shaders to Metal tessellation (post-tessellation vertex shader)
Milestone: Game using geometry shaders (e.g., particle systems) renders correctly through DXIL path.
Phase 6: Advanced Features
Why last: These features are needed for specific modern games but aren't blocking the majority of titles.
6A. Enhanced Barriers
Current state: EnhancedBarriersSupported=FALSE in CheckFeatureSupport. ResourceBarrier is implemented but BEGIN/END split barriers are not.
Impact: UE5 games increasingly use enhanced barriers.
6B. Stream Output
Current state: SOSetTargets is empty (line 524). Stream output (transform feedback) is not implemented.
Impact: Games using SO for particle systems or GPU-driven rendering.
6C. Mesh Shaders / Amplification Shaders
Current state: DispatchMesh is empty. Mesh/amplification shader kinds fall through to unknown_main in the converter. Metal 3 supports mesh shaders via object and mesh function roles.
Impact: Games using mesh shaders (Nanite in UE5, DirectX 12 Ultimate titles).
6D. Ray Tracing (DXR)
Current state: All ray tracing command list methods are empty. No DXR intrinsics in the converter. Metal 3 supports ray tracing via the raytracing namespace.
Impact: Games using DXR ray tracing (Cyberpunk 2077, RE4 ray tracing, etc.). Long-term feature.
Success Metrics
| Metric | Current | Phase 1 | Phase 2 | Phase 3 | Phase 4 |
|---|---|---|---|---|---|
| DXIL shaders compiling correctly | ~25% | ~60% | ~60% | ~65% | ~70% |
| Per-game prefix isolation | No | No | Yes | Yes | Yes |
| Game rules in mtsp-rules.toml | 64 | 64 | 64 | 200+ | 200+ |
| Steam API functions bridged | 6 | 6 | 6 | 6 | 100+ |
| Redist auto-install | No | No | Yes | Yes | Yes |
| D3D12 games reaching main menu | 1-2 | 5-10 | 5-10 | 15-20 | 15-20 |
Dependency Graph
Phase 1A (CFG) ──→ Phase 1E (Tests)
Phase 1B (Struct) ─┘
Phase 1C (Groupshared) ─┘
Phase 1D (Threadgroup) ─┘
Phase 2A (Per-game prefix) ──→ Phase 2B (Redist template) ──→ Phase 2C (Auto-install)
Phase 3A (Flag system) ──→ Phase 3B (Proton mining) ──→ Phase 3C (Config patches)
Phase 4A (lsteamclient) ──→ Phase 4B (Testing)
Phase 1E ──→ Phase 5A (Textures) ──→ Phase 5B (Wave ops) ──→ Phase 5C (Atomics) ──→ Phase 5D (GS/Tess)
Phase 5D ──→ Phase 6A (Barriers) ──→ Phase 6B (SO) ──→ Phase 6C (Mesh) ──→ Phase 6D (DXR)
Phases 1, 2, 3, and 4 can proceed in parallel with different developers. Phase 5 depends on Phase 1 completion. Phase 6 depends on Phase 5.
Installer Runtime
Active roadmap.
MetalSharp Assorted Installer Runtime Roadmap
Updated: 2026-07-08
Status: Phase 9 active
This roadmap tracks the work needed to make Install Windows Program reliable for assorted Windows installers by treating installers as bottle-managed programs with installer-specific runtime prep, logs, repair actions, and post-install app discovery.
Core rule: the installer bootstrap route is not the game/app runtime route. Installer and launcher bootstrap work should happen in a stable bottle route first; the final detected app or game executable can then choose VKD3D, DXMT, bare Wine, or another route from its own evidence.
Phase 1: Classify Installers Correctly
Build the installer classifier around real families:
- Generic
.msi - Generic 32-bit
.exe - Generic 64-bit
.exe - Java launchers, like Minecraft
- WebView launchers, like EA, Ubisoft, Battle.net, Epic, and Rockstar
- Electron/Squirrel launchers, like GOG Galaxy or itch-style apps
- Legacy .NET installers
- Console/admin tools, like
BERCon.exe
Known launchers should install through WineBare, not VKD3D/DXMT. Graphics pipelines belong to the final launched game executable, not the bootstrap installer.
Phase 2: Runtime Profiles Per Installer Family
Each installer family needs a default bottle runtime:
- Minecraft / Java launcher: Wine bottle, Java launcher profile, CEF compatibility
- EA / Ubisoft / Epic / Battle.net: WebView profile with
webview2,gecko,dotnet48,vcrun2019, andcorefonts - GOG / Electron: Launcher profile with CEF/Electron wrapper support
- Generic
.msi: Game install profile with MSI logging enabled - Legacy .NET: Win32/Win64 .NET profile with real
dotnet48 - BattlEye/EAC tools: only classify as anti-cheat runtime if the artifact actually contains service/runtime assets
Phase 3: Deep Installer Logs
Every installer launch should automatically create and surface:
- top-level bottle launch log
- Wine stderr/stdout log
- MSI log if an MSI is invoked
- bootstrapper log discovery, like EA's
EA_app_*.log - detected failure code summary
- extracted custom-action/runtime hints when available
For EA specifically, the system should surface:
EA App failed after MSI apply:
0x80070643 -> INST-14-1603
Likely failing inside MSI/custom-action stage.
Phase 4: Fresh Bottle Retest Matrix
Use clean bottles for each proof target:
- Minecraft MSI
- EA App installer
- Ubisoft Connect installer
- Battle.net installer
- Epic Games Launcher installer
- GOG Galaxy installer
- One generic small game/demo
.exe - One generic
.msi - Real BattlEye Steam title with shipped
BEServiceassets
Each test records:
- install result
- detected app result
- launch result
- login window result
- child game process result
- graphics route
- missing runtime/component
- failure evidence path
Phase 5: Post-Install App Detection
After an installer exits, scan the bottle for real installed apps while filtering junk.
Ignore:
- uninstallers
- updater helpers
- Windows Media Player
- Windows NT/system tools
- crash reporters
- redistributable installers
- helper services unless explicitly relevant
Prefer:
- launcher executable
- game executable
- signed/product-named executable
- Start Menu shortcuts
- uninstall registry display names
Phase 6: Launcher CEF/WebView Survival
For launchers that install but render blank:
- detect CEF assets
- wrap launcher exe
- preserve real exe as
<name>_real.exe - inject CEF-safe flags
- hook child process creation
- add WebView2/Gecko repair buttons
- log renderer/GPU subprocess command lines
Minecraft is the hard proof case here. EA, Ubisoft, and Epic likely reuse the same class of fix.
Phase 7: Steam-Adjacent Launcher Flow
For Steam games requiring EA, Ubisoft, or Battle.net:
- Steam remains the session/identity owner
- the game bottle becomes launch-authoritative for runtime assets
- third-party launchers install into the game's bottle or linked launcher bottle
- Steam launch should not fail because assets are outside the active bottle
- child launcher/game process should inherit the route-specific environment correctly
Phase 8: Repair Actions Per Failure
Add targeted repairs instead of blind reruns:
dotnet48webview2geckovcrun2019directx_jun2010corefontswinver- MSI logging retry
- CEF wrapper reapply
- launcher cache reset
- bottle component doctor
For EA, the immediate next repair path is: fresh WebView bottle, bare Wine bootstrap, dotnet48 preinstalled, then inspect the direct MSI log.
Phase 9: Anti-Cheat Proof Path
Do not use random downloads as proof unless they are actual runtime/service assets.
Implementation checkpoint:
- Steam game bottles now scan ordinary game folders, not just
_CommonRedist, for Easy Anti-Cheat and BattlEye setup/service assets. - Runtime assets infer bottle components for
easyanticheat_eosandbattleye. - Bottle Doctor source policies can now report a game-local installer asset path for those components.
- Component repair resolves game-local EAC/BattlEye assets before reporting an asset as missing.
- EAC EOS
.batinstallers are parsed into directEasyAntiCheat_EOS_Setup.exe install <product-id>calls so the repair path avoids script pauses. - Runtime asset paths are canonicalized before repair because Wine rejects doubled
//Volumes//...paths for executable launch.
For BattlEye:
BERCon.exeis only an RCon client- real proof needs a Steam title that ships BattlEye
- inspect game folder for
BEService.exe,BEClient*.dll, launcher bootstrap, and service install commands - test what fails: service creation, driver expectation, network auth, Unix runtime absence, or vendor block
- current AverySSD Steam scan has not found a real BattlEye service/runtime payload yet
For EAC:
- Elden Ring is the first local game proof target because it ships
EasyAntiCheat/easyanticheat_eos_setup.exe - Rubicon is the first completed EAC EOS install proof: it ships EAC EOS, not BattlEye, and
EasyAntiCheat_EOS_Setup.exe install 789399aada914e66bb3c3facebc5d709completed successfully in~/.metalsharp/prefix-steam - dry-run component repair before attempting an actual service install
- verify whether Unix EAC assets exist before treating online anti-cheat as supportable
- compare what Proton expects versus what macOS/Wine can provide
Phase 10: UI Productization
The Sharp Library should show understandable state:
- Installing Windows Program
- Installed Apps
- Installer Logs
- Runtime Components
- Repair
- Import App
- Relaunch Installer
- Open Bottle
Failed cards should show human-readable failure summaries, for example:
EA App install failed at MSI stage.
Error: INST-14-1603 / 0x80070643
Likely runtime: .NET custom action or MSI service behavior.
Immediate Execution Order
- Fresh EA bottle with the bare-Wine/WebView/
dotnet48route. - Inspect whether the direct MSI log now has content.
- Ubisoft Connect fresh proof installed launcher files, detected
UbisoftConnect.exe, then hit the crash-reporter path withcorefontsandwebview2still pending. - Elden Ring EAC EOS dry-run repair resolves the game-local setup executable through
steam_1245620. - Rubicon EAC EOS actual repair completed successfully in
steam_1888160; launch Rubicon next and capture whether failure moves to EAC bootstrap, offline mode, or online/vendor block. - Repair Ubisoft
corefontsandwebview2, relaunchUbisoftConnect.exe, and capture whether the next failure is rendering, service/elevation, or auth. - Run Battle.net or Epic after that.
- Save BattlEye for a real Steam title, because Rubicon and Elden Ring are EAC EOS targets and
BERCon.exeis not the runtime installer.
Implementation Notes
POST /sharp-library/installacceptsfreshBottle: truefor proof runs that must avoid stale prefix state from a previous run of the same installer path.POST /launcher/evidencecollects launcher-installer proof for EA and Ubisoft bottles. It reports installer launch logs, launcher-specific logs, detected launcher executables, known EA MSI failures, Ubisoft crash-reporter evidence, and whether a direct post-install launcher launch has actually been recorded.- Steam game runtime asset detection now covers
_CommonRedist,installscript.vdf,EasyAntiCheat,EasyAntiCheat_EOS,BEService,BEClient, andBEDaisymarkers. - Anti-cheat proof should begin with Doctor/dry-run evidence, because running service installers can leave stale Wine service state behind.
Anti-Cheat Hard Route
Active roadmap.
WTMKT Anti-Cheat Hard-Route Roadmap
Updated: 2026-07-08
This roadmap covers the post-VM plan for MetalSharp anti-cheat/runtime support. The current local Linux VMs are ARM64, so they cannot serve as clean x86_64 Steam/Proton control machines. MetalSharp has to collect its own evidence and reduce the failure to concrete runtime contracts.
Phase 1: Anti-Cheat Evidence Collector
Build a backend report that gathers the launch evidence for a Steam appid:
- Easy Anti-Cheat service logs and launcher logs.
- BattlEye launcher/service logs when present.
- Steam
gameprocess_log.txtandrunprocess_log.txt. - EAC settings context: product id, sandbox id, deployment id, executable path, launcher title, downloaded module target, Wine version, exit code, and module mapping status.
- Clear next-action hints for known failures such as
Failed to map the anti-cheat module.
The goal is not to declare success. The goal is to turn "anti-cheat failed" into a repeatable report we can diff after every Wine/runtime change.
Initial backend surface:
POST /steam/anticheat-evidence
{"appid":1888160}
The report returns a normalized status, a human summary, EAC fields, Steam protected-launch fields, collected artifact paths, log tails, and next-action hints. For Rubicon, the expected current status is module_mapping_failed, with EAC setup exit 0, module target linux64, Wine version 11.5, launcher exit 206, and Steam tracking start_protected_game.exe.
Phase 2: Wine Module-Mapping Probe
Create a small probe that exercises the same class of module mapping that protected launchers expect from Wine:
- host module mapping,
- executable memory mapping,
- syscall dispatch expectations,
- loader transitions,
- dyld/dylib boundary failures on macOS,
- log output that can be attached to the appid evidence report.
Initial safe backend surface:
POST /steam/anticheat-probe
{"appid":1888160}
This probe does not load or tamper with anti-cheat modules. It classifies the host/runtime boundary from inspectable evidence: host OS and architecture, Wine runtime paths, EAC module target, game-local anti-cheat module assets, binary magic (ELF, PE, or Mach-O), and whether the selected module implies a Linux-user-space substrate requirement on macOS. If Steam has only staged a download under steamapps/downloading/<appid> and the protected launcher or game executables still have unknown/null headers, the probe returns staged_download_incomplete instead of treating that payload as launchable. For Rubicon with completed protected-launch evidence, the expected current status is linux_module_on_darwin_boundary.
Phase 3: Proton/Wine Delta Audit
Map MetalSharp Wine against Proton and upstream Wine behavior:
ntdllloader and syscall dispatch,- wineserver process and handle behavior,
steamclientandlsteamclientbehavior,- mmap and memory protection behavior,
- pressure-vessel/container assumptions,
- anti-cheat runtime file layout and module target selection.
Initial backend surface:
POST /steam/anticheat-delta-audit
{"appid":1888160}
This report groups the local runtime into audit surfaces:
- Wine loader/syscall baseline:
wine,wineserver, Unixntdll.so, and Windowsntdll.dlllanes. - Wineserver state: whether a live
wineserverprocess and per-user socket directory are present during runtime observation. Absence is expected in a clean idle install, but protected launch evidence should show the correct shared server boundary. - Win32 translation contract: PE
kernel32.dll,user32.dll, andntdll.dllplus Unix-sidentdll.so, proving that Windows API calls have the Wine translation lanes required before any graphics or anti-cheat diagnosis is meaningful. - Steam runtime bridge: Windows
steamclient.dll/steamclient64.dlland whether a Proton-stylelsteamclientbridge exists. - Linux runtime assumptions: pressure-vessel, seccomp, and Linux namespaces, which are comparison rows on macOS rather than direct requirements.
- Darwin executable module boundary: whether the host can directly load Linux ELF modules, whether any vendor Mach-O module is present, and whether shipped Linux ELF assets imply a Linux user-space substrate.
- Graphics runtime adjacency: DXMT, DXVK, and MoltenVK assets that must stay intact while protected launch is debugged.
- Anti-cheat module contract: whether EAC selected a Linux module, whether Darwin can directly load it, and whether a vendor macOS module is present.
For Rubicon, the expected status is blocking_delta_found: the ordinary Wine/DXMT runtime pieces exist, but the protected launcher selected linux64, no vendor Mach-O module was found, and macOS cannot directly load Linux ELF modules.
Phase 4: macOS Runtime Substrate Decision
Choose the truthful compatibility path:
- vendor-supported macOS anti-cheat module path if available,
- or a signed Linux-user-space compatibility substrate that can satisfy the protected module loader without spoofing, hiding, tamper evasion, or bypass behavior.
Initial backend surface:
POST /steam/anticheat-substrate-decision
{"appid":1888160}
The decision report synthesizes the evidence, probe, and delta audit into one explicit result. For Rubicon, the expected current decision is requires_linux_user_space_substrate_or_vendor_macos_asset.
Phase 4: Harmless Host Contract Probe
Add an endpoint that records the host contract without loading protected modules:
POST /steam/anticheat-contract-probe
{"appid":1888160}
This endpoint uses the appid only for scoping existing logs and game-local identity. The host probe itself uses synthetic temporary data:
- anonymous read/write memory mapping followed by read/execute protection transition,
- synthetic ELF direct-load attempt through the host dynamic loader,
- Wine loader and wineserver path/state evidence,
- selected EAC module target from scoped protected-launch logs.
Expected macOS result for Elden Ring and Rubicon is linux_elf_host_gap_confirmed: EAC selected linux64, Wine reached module mapping, and the host dynamic loader does not accept Linux ELF modules directly. That does not prove anti-cheat support is impossible; it proves the next implementation target is a truthful Linux user-space substrate or vendor-supported macOS module assets, not another graphics route.
Allowed paths:
- Build a signed Linux user-space compatibility substrate for ELF module hosting.
- Obtain or document vendor-supported macOS anti-cheat module assets.
- Work with publisher/vendor enablement instead of spoofing trust.
Rejected paths:
- spoof anti-cheat host identity,
- hide MetalSharp or Wine from the protected launcher,
- fake kernel driver support,
- tamper with protected modules,
- claim online anti-cheat support before the protected module maps and launches with vendor-supported assets.
Current Proof Target
Rubicon showed useful progress but not success: EAC EOS setup completed, protected launch downloaded the linux64 module, Wine module mapping started under Wine 11.5, and then EAC failed with Failed to map the anti-cheat module / exit code 206. The custom Darwin substrate now closes that loader boundary; the proof below is the current gate.
Current Implementation Surface
The backend now exposes all five read-only evidence surfaces described above:
/steam/anticheat-evidence/steam/anticheat-probe/steam/anticheat-delta-audit/steam/anticheat-substrate-decision/steam/anticheat-contract-probe
The collector records the selected EAC system, CDN response, downloaded byte
count, Wine version, mapping result, launcher exit code, protected-launch log
context, and metadata for cached .eac vendor containers without inspecting
their contents. METALSHARP_ANTICHEAT_PREFIX can point at an absolute,
disposable prefix for reproducible launch evidence; normal calls use the
configured Steam bottle.
The installer also repairs the exact /tmp/metalsharp-wine-deps/lib/ install
names found in older macOS Wine bundles, including the GnuTLS crypto closure,
to @loader_path and ad-hoc signs changed dylibs. This is required for the
EAC CDN request to reach HTTP 200, but it is not itself EAC module support.
The completion gate remains intentionally strict: a launcherLoadClaim, a
successful download, or a synthetic host probe is not treated as protected
module proof. The gate requires an explicit vendor-module mapping/load result
and a protected game transition. No identity spoofing, module tampering, fake
kernel support, or bypass route is part of this surface.
The pre-substrate Elden Ring baseline recorded the following concrete boundary:
Start_protected_game.exeselectedlinux64under Wine 11.5.- The repaired TLS path reached CDN HTTP 200 and downloaded 9,168,824 bytes.
- Wine then reported
Failed to map the anti-cheat moduleand launcher exit code206; the current explicit substrate proof no longer reports that mapping failure. - Direct
mac_x64andmac_arm64deployment requests returned HTTP 403, so no vendor macOS module asset is available for this deployment. - The synthetic macOS host probe accepted anonymous RW→RX memory but rejected a
synthetic ELF through dyld, producing
linux_elf_host_gap_confirmed.
These observations proved the diagnostic surface and isolated the remaining
module-hosting boundary. The boundary is now implemented by
src/anticheat/linux_substrate.c, built as the x86_64
metalsharp_eac_substrate.dylib CMake target, and copied into app/native/
for the existing Electron native-resource packaging path. The substrate is
used only by the explicit proof/launch environment; normal launches do not
start an anti-cheat process automatically.
Real MetalSharp Wine 11.5 Module Proof
tools/anticheat/run_eac_proof.py is the bounded, opt-in proof command. It
accepts the external Steam-library game directory explicitly, refuses a Wine
binary outside the selected .metalsharp/runtime/wine/bin/wine tree, starts
only Start_protected_game.exe, enforces a thirty-second maximum, runs
wineserver -k, and kills all Windows-shaped Wine helpers that remain,
including truncated comm names such as steamwebhelper.exe and
winedevice.exe. It does not start Steam or eldenring.exe.
The CMake substrate target also generates the MetalSharp-owned ET_DYN symbol
image app/native/metalsharp_eac_libc.so.6 from
tools/anticheat/generate_linux_libc_elf.py. This image is the Linux symbol
namespace consumed by the Darwin boundary; it is not vendor libc or an EAC
payload.
The command was run against the real external-drive launcher and the exact
installed MetalSharp Wine runtime. The resulting evidence (schema:
metalsharp.eac-proof.v1) records:
- Wine module mapping selected
linux64, received CDN response200, and reported Wine11.5(exact runtime SHA-256e621bf88dd07872b391198aee50bf1503fe18d43b7a9c0183fa23075efc61395). - The built x86_64 substrate was SHA-256
52258a6433d41bcf09028763377a4493ba27f45fe886ff3dbc9db123ccb3b1b7, and its generated ELF symbol image was SHA-256d8c1008d0ddf70287023c9d4b16840fb0dd4039ae055bff0a10a400f1ebb9886. - The real downloaded module was an ELF64 x86-64 image, 9,168,824 bytes,
SHA-256
4fdb641276de2a5f94c0fc4e10be28f4b4a53c47b8ecaa40461e09c322c75a8a. - The substrate completed the full RELA pass (
1,677entries) and PLT pass (151entries), invokedDT_INITand all sixDT_INIT_ARRAYconstructors, applied the ELFPT_LOADprotections, and resolved the launcher's reala,b,c, anddexports. - The real EAC export
areturned1through the Darwin TSD/ELF substrate. - Cleanup left no Windows-shaped Wine process from the proof run, including Wine services, winedbg, wineserver, conhost, explorer, and detached Steam web helpers. The launcher is intentionally terminated at the thirty-second bound because the standalone probe does not provide the game transition.
This is a real protected-module load/relocation/constructor/export proof, not a
synthetic ELF probe, identity spoof, vendor-module edit, fake kernel result,
GPTK/VKMT path, alternate Wine build, or compatibility shim. It proves the
Linux EAC module surface through the exact MetalSharp Wine 11.5 → macOS
translation boundary. It does not yet prove a protected eldenring.exe
transition or an online session; those remain separate completion evidence and
must not be inferred from this module proof. An explicit Wine Steam handoff
attempt was also bounded and cleaned up; the installed Wine Steam client
reported SteamAPI_Init() failed; connect to global user failed before it
created a protected game process, so that attempt is recorded as an
unproven Steam-account handoff rather than being misrepresented as an EAC
failure or success.
D3D12 Offline ABI Surface
Active roadmap.
D3D12 Offline ABI Surface Matrix
Date: 2026-06-12
Scope: phase 6 of the offline D3D12 finish roadmap. This file uses GPTK PE decompilation evidence only for exported entry points, object category names, and dispatch/vtable surface. It does not treat GPTK PE unix-dispatch bodies as implementation algorithms.
Evidence Inputs
- Offline GPTK markdown:
/Users/alexmondello/Desktop/dx12-ghidra-analysis/markdown/gptk-d3d12.md/Users/alexmondello/Desktop/dx12-ghidra-analysis/markdown/gptk-d3d11.md/Users/alexmondello/Desktop/dx12-ghidra-analysis/markdown/gptk-dxgi.md- DXMT source:
vendor/dxmt/src/d3d12vendor/dxmt/src/d3d11vendor/dxmt/src/dxgi- Built DXMT PE artifacts:
vendor/dxmt/build-metalsharp-x64/src/d3d12/d3d12.dllvendor/dxmt/build-metalsharp-x64/src/dxgi/dxgi_dxmt.dll
Export Surface
Validated with:
x86_64-w64-mingw32-objdump -p vendor/dxmt/build-metalsharp-x64/src/d3d12/d3d12.dll
x86_64-w64-mingw32-objdump -p vendor/dxmt/build-metalsharp-x64/src/dxgi/dxgi_dxmt.dll
| Surface | GPTK evidence | DXMT evidence | Status |
|---|---|---|---|
D3D12CreateDevice |
gptk-d3d12.md names D3D12CreateDevice; GPTK dispatch table includes GFXT_CreateD3D12Device_Type. |
Exported by built d3d12.dll; implemented in vendor/dxmt/src/d3d12/d3d12.cpp. |
Covered |
| Root signature deserializers | GPTK D3D12 exposes D3D12 root-signature object/serializer surface. | Built d3d12.dll exports D3D12CreateRootSignatureDeserializer, D3D12CreateVersionedRootSignatureDeserializer, D3D12SerializeRootSignature, and D3D12SerializeVersionedRootSignature. |
Covered |
| D3D12 SDK helpers | D3D12 Agility-style loader surface must tolerate SDK path/version queries. | Built d3d12.dll exports D3D12SDKPath and D3D12SDKVersion. |
Covered |
| D3D12 debug/interface helpers | GPTK D3D12 includes debug/interface labels and public IIDs. | Built d3d12.dll exports D3D12GetDebugInterface, D3D12GetInterface, and D3D12EnableExperimentalFeatures. |
Covered |
CreateDXGIFactory* |
gptk-dxgi.md names CreateDXGIFactory, CreateDXGIFactory1, and CreateDXGIFactory2. |
Built dxgi_dxmt.dll exports all three; vendor/dxmt/src/dxgi/dxgi.def declares ordinals 9-11. |
Covered |
| DXGI debug helpers | GPTK DXGI has debug/interface public surface. | Built dxgi_dxmt.dll exports DXGIGetDebugInterface and DXGIGetDebugInterface1. |
Covered |
D3D12 Object Surface
| GPTK object category evidence | DXMT source surface | Status |
|---|---|---|
D3D12Device, ID3D12Device, unix d3d12_device markers |
MTLD3D12Device : public ID3D12Device12Compat in vendor/dxmt/src/d3d12/d3d12_device.hpp; QueryInterface covers ID3D12Device through ID3D12Device12. |
Covered |
D3D12CommandQueue, unix d3d12_command_queue markers |
MTLD3D12CommandQueue : public ID3D12CommandQueue in vendor/dxmt/src/d3d12/d3d12_command_queue.hpp. |
Covered |
D3D12GraphicsCommandListMTL, ID3D12GraphicsCommandList, unix d3d12_command_list markers |
MTLD3D12GraphicsCommandList : public ID3D12GraphicsCommandList6 in vendor/dxmt/src/d3d12/d3d12_command_list.hpp. |
Covered |
D3D12PipelineState, unix d3d12_pipeline_state markers |
MTLD3D12PipelineState : public ID3D12PipelineState in vendor/dxmt/src/d3d12/d3d12_pipeline_state.hpp. |
Covered |
unix d3d12_root_signature markers |
MTLD3D12RootSignature : public ID3D12RootSignature in vendor/dxmt/src/d3d12/d3d12_root_signature.hpp. |
Covered |
unix d3d12_descriptor_heap markers |
MTLD3D12DescriptorHeap : public ID3D12DescriptorHeap in vendor/dxmt/src/d3d12/d3d12_descriptor_heap.hpp. |
Covered |
unix d3d12_buffer / d3d12_texture markers |
MTLD3D12Resource : public ID3D12Resource in vendor/dxmt/src/d3d12/d3d12_resource.hpp; resource creation paths are in d3d12_device.cpp. |
Covered |
unix d3d12_fence markers |
MTLD3D12Fence : public ID3D12Fence in vendor/dxmt/src/d3d12/d3d12_fence.hpp. |
Covered |
| command allocator/signature/query heap/pipeline library/heap markers | DXMT has d3d12_command_allocator, d3d12_device.cpp command signature and pipeline library classes, d3d12_query_heap.hpp, and d3d12_heap.hpp. |
Covered |
| state object / acceleration structure / work graph markers | DXMT has partial MTLD3D12StateObject and related no-op/compat surfaces in d3d12_device.cpp; full DXR/work-graph behavior remains out of scope for the current vertex/PSO repair. |
Partial, acceptable for current goal |
D3D11 Object Surface
GPTK D3D11/DXGI evidence names unix d3d11_device, d3d11_device_context,
d3d11_input_layout, buffers, textures, shaders, views, and state objects.
DXMT has matching D3D11 object categories:
| GPTK object category evidence | DXMT source surface | Status |
|---|---|---|
| D3D11 device | MTLD3D11Device : public ID3D11Device5 in vendor/dxmt/src/d3d11/d3d11_device.hpp; implementation in d3d11_device.cpp. |
Covered |
| D3D11 device context | MTLD3D11DeviceContextImplBase in vendor/dxmt/src/d3d11/d3d11_context_impl.cpp. |
Covered |
| D3D11 input layout | MTLD3D11InputLayout in vendor/dxmt/src/d3d11/d3d11_pipeline_cache.cpp; public input-layout declarations in d3d11_input_layout.hpp. |
Covered |
| Buffers/textures/views | D3D11 buffer, texture, RTV, DSV, SRV, and UAV sources exist under vendor/dxmt/src/d3d11. |
Covered |
| Shaders | D3D11 shader object/cache code exists under vendor/dxmt/src/d3d11, with DXBC conversion through the established DXMT shader path. |
Covered |
| States | d3d11_state_object.cpp implements sampler, blend, rasterizer, and depth/stencil state objects. |
Covered |
Translation Rules Confirmed
- Translate GPTK evidence as ABI and object-boundary confirmation only.
- Do not copy
__wine_unix_call_dispatcherbodies into DXMT. - Do not treat GPTK PE vtable setup as evidence for Metal binding behavior.
- Do not infer vertex pulling or MSL lowering algorithms from GPTK PE stubs.
- Continue using DXMT D3D11's own vertex/input-layout contract as the algorithmic reference for DXMT D3D12.
Phase 6 Result
The public D3D12/DXGI export surface and the D3D12/D3D11 object categories that matter for the current DX12 vertex/PSO repair are present in DXMT. GPTK provides no stronger offline evidence that should override the phase 1-5 DXMT-internal vertex metadata, PSO, and queue replay contracts.
D3D12 Offline Cache Preflight
Active roadmap.
D3D12 Offline Cache Preflight
Date: 2026-06-12
Scope: phase 8 follow-up for PR #200. This keeps the next validation step offline-only and makes the phase 7 PSO replay limitation machine-readable.
Command
python3 vendor/dxmt/tests/dxil/replay_d3d12_pso_metadata.py \
--cache /Users/alexmondello/.metalsharp/shader-cache/vkd3d/2050650 \
--output /tmp/replay_d3d12_pso_metadata_phase8.json
Current RE4 Cache Result
- Status:
partial - OK:
true - Render PSOs inspected:
582 - Failures:
0 - Warnings:
537 - Access issues:
537 - Warning kinds:
input-layout-without-msl-pull - Access issue kinds:
missing-captured-input-layout
The new replayability report classifies the current cache as:
{
"vertex_metadata_status": "legacy-cache",
"input_layout_pso_count": 537,
"captured_input_layout_pso_count": 0,
"missing_input_layout_pso_count": 537,
"legacy_capture_count": 537,
"can_strictly_replay_vertex_metadata": false,
"needs_offline_recapture_for_strict_vertex_replay": true
}
Interpretation
The current RE4 PSO cache is still useful for shader sidecar presence and broad
manifest replay checks. It is not sufficient for strict vertex/PSO metadata
replay because every render PSO with input-layout elements predates the captured
input_layout.elements schema.
This is an artifact coverage limitation, not a newly discovered D3D12 source failure. The replay tool still exits successfully when there are no hard failures, but the JSON report now states that strict vertex replay needs a new offline capture/dump artifact before it can be considered complete.
Strict Replay Criteria
The cache is strict-replay ready when:
failure_countis0replayability.vertex_metadata_statusiscompletereplayability.can_strictly_replay_vertex_metadataistruereplayability.missing_input_layout_pso_countis0
Until those are true, use the current cache result as a shader-sidecar and legacy manifest check, not as proof that every captured RE4 input-layout PSO was replayed against exact D3D12 vertex metadata.
D3D12 Offline Gate Results
Active roadmap.
D3D12 Offline Gate Results
Date: 2026-06-12
Scope: phase 7 of the offline D3D12 finish roadmap. No game, Steam, Wine,
MetalSharp backend, winemetal.dll, or winemetal.so launch/deployment step was
used.
Gates Run
| Gate | Command | Result |
|---|---|---|
| D3D12 DLL build | ninja -C vendor/dxmt/build-metalsharp-x64 src/d3d12/d3d12.dll |
Pass |
| D3D12 vertex contract | /tmp/test_d3d12_vertex_contract |
Pass |
| Current RE4 DXIL cache | /tmp/test_dxil_converter /Users/alexmondello/.metalsharp/shader-cache/vkd3d/2050650 |
442 pass, 0 fail, 0 skip |
| Backup RE4 DXIL cache | /tmp/test_dxil_converter /Users/alexmondello/.metalsharp/shader-cache/vkd3d/2050650.vertexpull-backup-20260612022154 |
299 pass, 0 fail, 0 skip |
| PSO metadata replay | python3 vendor/dxmt/tests/dxil/replay_d3d12_pso_metadata.py --cache /Users/alexmondello/.metalsharp/shader-cache/vkd3d/2050650 --output /tmp/replay_d3d12_pso_metadata_phase7.json |
Partial, 0 failures |
| Representative MSL compile | xcrun -sdk macosx metal -c .../570793ee26c3a8a4.msl -o /tmp/570793ee26c3a8a4.air |
Exit 0, warning only |
| Representative MSL compile | xcrun -sdk macosx metal -c .../b2b7ba555e7d10dc.msl -o /tmp/b2b7ba555e7d10dc.air |
Exit 0, warning only |
| Process residue check | ps -axo ... | egrep -i 'wine|steam|metalsharp|...' |
No matching leftover processes |
Replay Harness Details
Report: /tmp/replay_d3d12_pso_metadata_phase7.json
- Status:
partial - Pipelines inspected: 582
- Failures: 0
- Warnings: 537
input-layout-without-msl-pull - Access issues: 537
missing-captured-input-layout
Interpretation: the current RE4 PSO cache contains many render manifests captured
before the phase 3/4 input_layout.elements sidecar schema existed. The replay
harness therefore cannot prove exact table metadata for those older manifests
without a new offline-only capture/dump artifact. It did not find unexplained
vertex metadata failures in the artifacts it could inspect.
Phase 8 follow-up: docs/roadmaps/d3d12-offline-cache-preflight.md records the
same cache with the replay tool's machine-readable replayability summary. The
current RE4 cache is classified as legacy-cache: shader sidecar checks are
still useful, but strict vertex/PSO metadata replay needs a new offline
capture/dump artifact because all 537 input-layout PSOs are missing captured
input_layout.elements.
Result
The offline gates required for phases 0-7 are complete for this PR branch. The only remaining limitation is artifact age in the pre-existing RE4 PSO manifest cache, not a failing code path in the current source-level, DXIL-converter, or build gates.
Proton Runtime Research
Proton Runtime Research
Created: 2026-05-19
Purpose: Phase 0 evidence lock for the MetalSharp macOS Proton roadmap. This document records what Proton actually provides, what CrossOver proves about installer bottles, and what MetalSharp should copy or intentionally avoid.
Source Baseline
- Valve describes Proton as a compatibility layer for Windows games on Linux that uses a modified Wine plus high-performance graphics API implementations. Source: Steam Deck and Proton.
- Proton creates a per-game Wine prefix under
steamapps/compatdata/<appid>/pfx. Source: Proton FAQ. - Proton exposes runtime options through Steam launch environment such as
PROTON_LOG,PROTON_USE_WINED3D, and other compatibility toggles. Source: Proton README. - Proton build/release artifacts are structured as redistributable compatibility tools installable under Steam's
compatibilitytools.d. Source: Proton README. - Steam Common Redistributables exist because games need shared components such as Visual C++, .NET, DirectX 9, OpenAL, XNA, and PhysX installed before they run. Source: Steamworks Common Redistributables.
What Proton Is
Proton is not just "Wine with graphics DLLs." It is a Steam-integrated runtime contract:
- a patched Wine distribution
- per-game prefix ownership
- default prefix templates
- Steam-provided game command and appid context
- launch environment generation
- compatibility flags
- graphics translation layers
- audio/input/media compatibility work
- CommonRedist and install-script expectations
- debug logging conventions
- crash/debug hooks
- build artifacts Steam can discover as a compatibility tool
For MetalSharp, the core idea to copy is not Linux-specific container internals. The core idea is: every game launch has an authoritative runtime record, an isolated prefix, a known route, a repairable dependency set, and a reproducible log.
How Proton Launches A Game
Observed model from public Proton docs:
- Steam owns the appid, install path, launch command, and user/session context.
- Proton is selected as the compatibility tool for that game.
- Proton creates or reuses
steamapps/compatdata/<appid>/pfx. - Proton applies config and compatibility flags for that appid.
- The Windows executable is launched through Wine inside that prefix.
- Logs can be enabled with
PROTON_LOG=1 %command%.
MetalSharp translation:
- Wine Steam should remain the account/download/session provider.
- MetalSharp should own the game process route when the game needs route-specific env, DLLs, shims, or bottle assets.
- Each Steam game should have a MetalSharp compatdata record under
~/.metalsharp/compatdata/<appid>/. - The compatdata record should point at the bottle/prefix, executable, route, dependencies, runtime assets, Steam identity mode, and logs.
Steam Identity Must Stay Separate From Runtime Authority
The recent Steam bottle work already exposed the important split:
- Steam must stay alive as Steam.
- Steam should not be globally mutated every time one game needs a different graphics/runtime route.
- The game process must still receive correct appid identity and ownership context.
- A game route should be able to fail before launch with a clear readiness error instead of hanging after a socket failure.
For Phase 2, treat this as a hard invariant:
Steam identity/session provider != game runtime authority
CrossOver Evidence For Installer Bottles
CrossOver's Mac guide describes a bottle as a virtual Windows environment with a C: drive, registry, CrossOver settings, Windows applications, and user data. It also supports unlisted apps and standalone .exe files by creating/running them inside bottles. Source: CrossOver Mac User Guide.
This proves the installer/launcher side of the roadmap is not speculative. The successful pattern is:
- installers run inside bottles
- apps can be installed into an existing bottle
- bottles isolate undesirable settings
- apps can be launched by saved commands
- debug logs and launch options are attached to bottle execution
MetalSharp should continue the Beta 7 bottle model and extend it toward Proton-like compatdata for games.
Common Redistributables
Steam's documented redistributables include Microsoft Visual C++, .NET, DirectX 9, OpenAL, XNA, and PhysX. Steam also supports install scripts for custom redists. Source: Steamworks Common Redistributables.
MetalSharp needs a local equivalent:
- component detection per bottle
- component receipts per bottle
- repair/reinstall controls
- legal local asset discovery under Steam CommonRedist or MetalSharp runtime redist folders
- logs for redist installer exit codes
macOS Difference From Linux Proton
Linux Proton can rely on Linux interfaces and, increasingly, Linux kernel support such as ntsync for NT synchronization semantics. The Linux kernel docs describe ntsync as a compatibility driver for user-space NT emulators, implemented in software and exposed through /dev/ntsync. Source: Linux ntsync documentation.
macOS does not expose /dev/ntsync, Linux futexes, or Linux kernel modules. The macOS plan must map Windows behavior onto Darwin, Mach, pthreads, ulock, system frameworks, and Apple-approved extension mechanisms where appropriate.
MetalSharp Implications
The Proton-like path for MetalSharp is:
- Keep Wine Steam as identity/session/download provider.
- Create authoritative compatdata for each Steam appid.
- Reuse installer bottles for non-Steam and launcher apps.
- Promote current C/Objective-C shims into a versioned host runtime ABI.
- Route D3D/audio/input/Steam identity through stable host services.
- Make launch logs and crash bundles first-class artifacts.
- Treat anti-cheat as a vendor trust surface, not a graphics/runtime toggle.
Phase 0 Conclusions
- Proton's useful lesson is runtime discipline: prefix, route, config, logs, redists, appid identity.
- CrossOver's useful lesson is bottle discipline: installers and launchers need stable bottle state, not ad hoc executable launching.
- MetalSharp already has enough bottle and shim code to proceed to Phase 1.
- The next design target is not "make anti-cheat work"; it is "make MetalSharp runtime truth inspectable enough that anti-cheat support has a legitimate path."
Anti-Cheat Boundaries
Anti-Cheat Compatibility Boundaries
Created: 2026-05-19
Purpose: Phase 0 evidence lock for what MetalSharp can legitimately do for anti-cheat compatibility on macOS.
Summary
MetalSharp should pursue anti-cheat compatibility through runtime correctness, transparency, signed artifacts, and vendor/developer enablement. It should not pursue bypass, spoofing, tamper evasion, hidden hooks, or fake kernel trust.
The practical distinction:
- Runtime compatibility means the game, launcher, graphics, media, input, Steam identity, and dependencies work correctly.
- Anti-cheat trust means the anti-cheat vendor and game developer accept the runtime as a supported environment.
MetalSharp can own the first. MetalSharp can prepare the second. MetalSharp cannot unilaterally grant itself trust for protected online games.
Evidence From Valve And Epic
Valve's Steamworks Proton documentation says Proton supports some common anti-cheat middleware, including Easy Anti-Cheat and BattlEye, but the support is not automatic for every title. EAC requires support to be enabled and Linux/Unix client module assets to be shipped with the game build. BattlEye requires title-specific manual configuration with Valve or BattlEye contacts. Source: Steam Deck and Proton.
Epic's EOS anti-cheat announcement says Wine/Proton support is included for Linux and that developers can activate anti-cheat support for Linux via Wine or Proton through the EOS Developer Portal. Source: Epic Online Services EAC announcement.
Valve also recommends user-space anti-cheat components for best Proton results and says kernel-space solutions are not currently supported. Source: Steam Deck and Proton.
Wine Boundary
Wine describes itself as a compatibility layer that translates Windows API calls into POSIX calls instead of simulating internal Windows logic like a VM or emulator. Source: WineHQ.
That matters for anti-cheat:
- Wine does not provide a real Windows kernel.
- Wine can implement Windows user-mode APIs and DLL behavior.
- Wine can expose limited driver-like stubs where user-mode compatibility is enough.
- Wine cannot run arbitrary Windows kernel drivers as trusted Windows kernel code.
So a Windows .sys anti-cheat driver is not solved by renaming a Linux .so to a macOS .dylib. The host OS trust boundary is different.
macOS Boundary
Apple's modern system-extension model allows some software to extend macOS without kernel-level access. DriverKit, NetworkExtension, and EndpointSecurity are Apple-approved mechanisms, but deployment requires entitlement review, signing/notarization, user approval, and in many cases explicit Apple-granted capabilities. Source: Apple System Extensions and DriverKit.
For MetalSharp:
- A user-space
.dylibcan provide host services to Wine or the MetalSharp runtime. - A
.dylibcannot become a Windows kernel driver. - A macOS system extension cannot honestly claim to be the Windows kernel.
- EndpointSecurity can observe/block macOS events, but using it for anti-cheat compatibility would require Apple entitlement and vendor trust.
- Any deeper host component must be transparent, signed, notarized, and user-approved.
Supported Work
Allowed and useful:
- improve Wine/runtime correctness
- ship signed and notarized runtime libraries
- preserve Steam appid/session identity accurately
- detect anti-cheat technology and report status
- detect whether a game includes Proton-compatible EAC/BattlEye assets
- classify games as vendor-supported, unsupported, unknown, or offline-only
- build crash/log bundles for vendors
- prepare an anti-cheat vendor compatibility kit
- contact game developers or anti-cheat vendors with reproducible evidence
Forbidden Work
Do not build:
- anti-cheat bypasses
- fake Windows kernel drivers
- hidden process, module, or debugger spoofing
- kernel trust impersonation
- tamper evasion
- signature forgery
- tricks that hide MetalSharp from anti-cheat
- "make the anti-cheat think this is Windows" hacks
This is both a user-safety rule and a product strategy rule. A runtime that gets users banned is not a win.
Current Repo Risks
The current tree still has one legacy endpoint naming issue to clean up during the anti-cheat classification phase:
app/src-rust/src/installer.rsnow presents the legacy toggle asset asOffline EAC Mode.app/src-rust/src/main.rsexposes/eac-toggle/statusand/eac-toggle/toggle.include/metalsharp/AntiCheatDB.hnow uses evidence-backed support status strings instead of a broad compatible/incompatible boolean.
Recommended correction:
- Keep user-facing names explicit and non-evasive when the legacy offline toggle asset is retained.
- Reframe the toggle endpoint as a compatibility flag only if it is not bypass behavior.
- Keep static and launch-recipe status values aligned. Launch recipes expose structured anti-cheat status entries for detected game folders:
vendor_supportedvendor_supported_on_proton_assets_presentblocked_pending_vendor_supportunsupported_kernel_driverunknownoffline_onlyuser_mode_possiblenative_macos_supported
Each entry includes a reason, evidence paths, allowed actions, and forbidden actions. This keeps diagnostics explicit: Proton-compatible assets are evidence, not permission to bypass vendor policy.
MetalSharp Classification Rule
Every detected protected game should get a reason, not just a launch failure:
Anti-cheat detected: Easy Anti-Cheat
Detected assets: EasyAntiCheat_x64.dll, no easyanticheat_x64.so
Known Proton status: requires developer opt-in and Unix module depot
MetalSharp status: blocked pending vendor/developer support
Allowed next actions: offline mode if supported, collect logs, submit compatibility request
Forbidden next actions: bypass/tamper/spoof
Phase 0 Conclusions
- Proton anti-cheat success is a trust/enablement story, not a bypass story.
- macOS makes this harder than Linux because Darwin and Apple's extension model differ from Linux kernel facilities.
- MetalSharp should focus on becoming a correct, signed, inspectable runtime and then make vendor support possible.
- Any anti-cheat roadmap item that sounds like hiding, spoofing, or bypassing should be rejected or renamed.
Release Signing
Release Signing and Notarization
Updated: 2026-07-08
MetalSharp DMG releases must be signed with a Developer ID Application certificate and notarized before upload. Without that, macOS Gatekeeper can show the "Apple could not verify this app is free of malware" prompt and force users through Security & Privacy.
Configure these GitHub Actions secrets to produce a signed and notarized DMG:
MACOS_CERTIFICATE_P12: base64-encoded Developer ID Application.p12MACOS_CERTIFICATE_PASSWORD: password for the.p12
Configure one notarization credential set:
- Apple ID credentials:
APPLE_ID,APPLE_APP_SPECIFIC_PASSWORD,APPLE_TEAM_ID - App Store Connect API key credentials:
APPLE_API_KEY_P8_BASE64,APPLE_API_KEY_ID,APPLE_API_ISSUER
The release job imports the certificate into a temporary keychain, runs Electron Builder with Developer ID signing, notarizes through app/build/notarize.cjs, then validates the stapled app and DMG with tools/dmg/verify-notarization.sh. The notarization verifier requires a Developer ID Application identity on the app, a stapled ticket, accepted Gatekeeper assessment, and a structurally valid DMG image before upload.
If the Apple secrets are not configured yet, Release CI falls back to an unsigned/ad-hoc DMG instead of skipping the release entirely. The fallback disables Electron Builder certificate discovery, packages the DMG, verifies the embedded runtime assets, and uploads a DMG-SIGNING.txt marker beside the DMG. This keeps release artifacts available during credential setup, but the unsigned DMG can still trigger Gatekeeper warnings until the Developer ID and notarization secrets are configured.