This guide covers the local-development traps that show up when you have both a
released Mesh.app in /Applications and a source checkout you are running with
just run, just backend, or a locally bundled app.
A developer machine often has all of these at the same time:
/Applications/Mesh.app— the installed, signed/notarized app.target/release/bundle/macos/Mesh.app— a locally bundled Tauri app fromjust bundleorjust release-signed-only.target/debug/mesh-console/target/debug/mesh-consoled— source-run binaries fromjust run,just backend, or Playwright real tests.
They all use the same default user caches and app state:
~/Library/Caches/mesh-llm/native-runtimes— downloaded native runtime dylibs.~/.mesh-llm— mesh identity/runtime metadata.~/Library/Application Support/mesh-console— app UI and goose state.
They can also leave helper processes behind. Before debugging model startup or native-runtime load failures, make sure you know which binary is actually running.
ps -eo pid,args | grep -E 'Mesh\.app|mesh-console|mesh-consoled' | grep -v grep
lsof -nP -iTCP:4640 -iTCP:9337 -iTCP:3131 2>/dev/null || trueTo start clean:
pkill -f 'Mesh\.app|mesh-console|mesh-consoled' || trueWhen choosing a model, the app can fail with a long dlopen(...) error like:
loading native runtime libraries: load native runtime
meshllm-native-runtime-darwin-aarch64-metal from
~/Library/Caches/mesh-llm/native-runtimes/...:
libggml-base.0.15.3.dylib' not valid for use in process:
mapping process and mapped file (non-platform) have different Team IDs
The UI shows this as “Something went wrong.” while preparing the AI engine.
Mesh desktop uses mesh-llm-host-runtime with the dynamic-native-runtime
feature. On macOS, model serving loads downloaded Metal/ggml/llama dylibs from
~/Library/Caches/mesh-llm/native-runtimes at runtime.
If the process is signed with the hardened runtime and macOS library validation
is still enabled, dyld refuses to load those cached dylibs unless they are
signed by the same Apple Team ID as the app. Release apps and local signed apps
therefore need this entitlement:
<key>com.apple.security.cs.disable-library-validation</key>
<true/>The entitlement lives in src-tauri/entitlements.plist and is referenced from
src-tauri/tauri.conf.json / the release config. The failure usually means one
of these is true:
- You launched an older
/Applications/Mesh.appthat was signed without the entitlement. - A stale
mesh-console/mesh-consoledprocess from a previous build is still running. - You manually signed a local bundle or helper binary with hardened runtime but
did not pass
src-tauri/entitlements.plist.
Check the main app and any helper binaries in the bundle:
APP=/Applications/Mesh.app
codesign -dvvv "$APP/Contents/MacOS/mesh-console" 2>&1 | sed -n '1,45p'
codesign -d --entitlements - "$APP" 2>/dev/null
for bin in "$APP"/Contents/MacOS/*; do
[ -f "$bin" ] && [ -x "$bin" ] || continue
echo "--- $bin"
codesign -d --entitlements - "$bin" 2>/dev/null || true
doneThe entitlements output should include:
com.apple.security.cs.disable-library-validation = true
For a local bundle, set APP to:
APP=target/release/bundle/macos/Mesh.appUse this when the installed app is the one you are launching:
pkill -f 'Mesh\.app|mesh-console|mesh-consoled' || true
cd /Users/sandro/Development/mesh-llm-group/desktop-app
codesign --force --deep --options runtime \
--entitlements src-tauri/entitlements.plist \
--sign - /Applications/Mesh.app
rm -rf "$HOME/Library/Caches/mesh-llm/native-runtimes"
open /Applications/Mesh.appThe cache removal is not normally required for correctness, but it is useful when validating the fix because it forces the runtime install/load path to run again from a clean state.
cd /Users/sandro/Development/mesh-llm-group/desktop-app
just bundle
codesign --force --deep --options runtime \
--entitlements src-tauri/entitlements.plist \
--sign - target/release/bundle/macos/Mesh.app
open target/release/bundle/macos/Mesh.appjust release-signed-only and just release should apply the same entitlements
through the release config path. Use the verification commands above if you are
unsure.
For day-to-day source development, prefer one of these loops:
just run # UI build + native Tauri window from source
just backend # headless backend on localhost, useful with curl or ui-dev
just ui-dev # Vite HMR, paired with just backendBefore switching between /Applications/Mesh.app and source runs, kill stale
helpers:
pkill -f 'Mesh\.app|mesh-console|mesh-consoled' || trueThis avoids confusing cases where the visible app is new but an older helper is still bound to a port or still owns the mesh runtime.
Invite links add one more wrinkle: macOS may route the mesh:// URL scheme to
whichever installed app registered it most recently, often /Applications/Mesh.app.
When testing invite-link behavior from source, either open the source app first
or paste the invite token directly into the join flow instead of relying on the
system URL handler.
The native runtime cache is shared by installed and source builds:
~/Library/Caches/mesh-llm/native-runtimesClear it when you need to force a fresh runtime install, when changing the mesh-llm version used by the app, or when validating signing/library-validation fixes:
rm -rf "$HOME/Library/Caches/mesh-llm/native-runtimes"Do not clear the entire ~/Library/Caches/mesh-llm directory casually unless you
also want to discard other mesh-llm cache data.
pkill -f 'Mesh\.app|mesh-console|mesh-consoled' || true- Verify which app you are launching (
/Applicationsvstarget/.../Mesh.app). - Verify entitlements with
codesign -d --entitlements - <app-or-binary>. - Re-sign with
src-tauri/entitlements.plistif library validation is missing. - Clear
~/Library/Caches/mesh-llm/native-runtimesand retry model startup.