tauri: bundle myownmesh daemon as sidecar (zero-install)#204
Merged
Conversation
The earlier daemon-spawn path required the `myownmesh` binary to
already be on `$PATH` or shipped via a separate install — users
who installed the LLM via `.dmg` / `.deb` / `.msi` got "couldn't
find myownmesh" at first launch. We control both repos; this
should Just Work.
Now it does:
**`src-tauri/build.rs`** — bundles the daemon as a Tauri sidecar
at LLM build time. Resolution order:
1. `MYOWNLLM_MESH_BIN` / `MYOWNMESH_BIN` env var pointing at a
pre-built binary (used by release CI to bring in a pre-signed
daemon).
2. Sibling MyOwnMesh checkout's `target/{debug,release}/` (dev
convenience — both repos open as siblings).
3. `cargo install --git https://github.com/mrjeeves/MyOwnMesh
--rev <pinned> myownmesh --root <OUT_DIR>` — fetches the
pinned daemon source, builds it, caches in OUT_DIR keyed by
rev. Subsequent builds short-circuit when the rev hasn't
moved.
The pinned rev lives in `.myownmesh-rev` at the repo root —
single source of truth, matched against the `myownmesh-core`
Cargo.toml dep so daemon IPC wire shape stays in lockstep with
what `src/mesh/daemon.rs` expects. Current pin: 93b53628 (the
merged Phase A daemon-IPC PR).
The built binary lands at
`src-tauri/binaries/myownmesh-<target-triple>{.exe}` — Tauri's
`externalBin` convention. `tauri.conf.json` declares it under
`bundle.externalBin`, so the bundler copies it next to the main
LLM exe in every shipped artifact:
- macOS `.app`: `Contents/MacOS/myownmesh`
- Linux `.deb`: `/usr/lib/MyOwnLLM/myownmesh`
- Windows `.msi`: next to `MyOwnLLM.exe`
**`mesh/daemon.rs::find_daemon_binary`** — sidecar location
(`<exe_dir>/myownmesh{.exe}`) is now the first lookup. End users
never see a "couldn't find" error in the normal install flow.
Env-var overrides + PATH + workspace fallbacks kept for dev /
release-CI / offline-build paths.
**Offline / dev escape hatches**:
- `MYOWNLLM_SKIP_SIDECAR=1` — build.rs skips the daemon fetch
+ writes a zero-byte stub at the sidecar slot so
`tauri_build::build()`'s `externalBin` existence check still
passes. `find_daemon_binary` detects the zero-byte stub via a
`metadata().len() > 0` check and falls through to the next
resolution rule.
- `MYOWNLLM_MESH_BIN` / `MYOWNMESH_BIN` — point at a pre-built
binary (skips the fetch entirely).
- Sibling MyOwnMesh checkout — auto-detected, fastest dev path.
**`.gitignore`**: `src-tauri/binaries/` (build artefact; the
pinned source rev tracked via `.myownmesh-rev` is the real
input).
Validation: `cargo check --bins` clean against both the
sibling-workspace path (real ELF binary copied) and the
SKIP path (zero-byte stub + runtime fallback). The
`find_daemon_binary` change keeps env / PATH / workspace
fallbacks compatible with everything that worked before.
https://claude.ai/code/session_01Vp4cvRTaLYd3162EwwcCXg
f11a754 to
2ba396d
Compare
Two issues your `just dev` hit:
**`cargo install` stderr was eaten** — `build.rs` ran `.status()`
which inherits stderr but cargo build buffers sub-process output
until the build itself fails, and our `Ok` return with a warning
swallowed it. You saw `exit code: 101` with no diagnostic. The
actual failure was probably libsrtp / cmake / openssl missing on
your Windows box — webrtc-rs needs them and CI installs them in
its setup step, but a fresh dev install doesn't.
Now build.rs captures stderr and surfaces the last 60 lines on
failure, so the next time `cargo install myownmesh` blows up
you'll see *what* blew up — almost always a missing native dep
that an install of cmake / vs-build-tools / openssl fixes.
Also dropped `--locked` from the cargo install args. The cloned
checkout's `Cargo.lock` may post-date the rev we pin, which
makes locked install refuse to start. Without it cargo resolves
against crates.io's latest compatible versions — same as what
`cargo build -p myownmesh` would do in a fresh checkout.
**`find_daemon_binary` returned a stale path that doesn't spawn**
— your log showed it resolving to
`C:\Users\Admin\Dev\MyOwnLLM\src-tauri\target\debug\myownmesh.exe`
(probably leftover from an earlier partial cargo install), then
`spawn` failed because the file isn't a valid PE binary. The old
single-binary lookup returned the first path that *existed* and
gave up if that one didn't actually work.
`daemon_binary_candidates()` now returns the full priority list
(sidecar → env vars → PATH → workspace fallbacks), de-duped by
canonical path. `ensure_daemon_running` iterates each — if one
fails to spawn or never binds the control socket, it drops the
child and tries the next. The error message at the end lists
every path that was tried plus the last failure, so when nothing
works the diagnostic is concrete.
This commit alone won't fix the `cargo install` failure on your
machine — that needs the native build deps. But the next `just
dev` will tell you exactly which one's missing, and once that's
installed the sidecar bundle will work.
Follow-up: once MyOwnMesh cuts a release with Phase A's IPC ops
landed (it currently has them on main but no release tag), this
code can switch to downloading the prebuilt
`myownmesh-<triple>.{tar.gz,zip}` release archive instead of
building from source. That'd skip the native-deps requirement
entirely. Tracking separately.
https://claude.ai/code/session_01Vp4cvRTaLYd3162EwwcCXg
You're right — 0.1.2 has Phase A's IPC ops, so we can skip the whole webrtc-rs local-compile dance and just grab the prebuilt daemon from GitHub Releases. Way more reliable on Windows where the native-build chain (libsrtp / cmake / openssl) is brittle. **`.myownmesh-rev`**: now holds the release tag (`v0.1.2`) rather than a commit SHA. The build script branches on whether the value starts with `v` to decide between the release-asset path and the cargo-install fallback. **`src-tauri/Cargo.toml`**: bumped the `myownmesh-core` git pin to `tag = "v0.1.2"` so the library types the LLM compiles against match exactly what the daemon binary ships. `Cargo.lock` resolved to `add5a57b...`. **`src-tauri/build.rs::bundle_myownmesh_sidecar`**: step 3 now prefers `download_release_asset(tag, target_triple, ...)` which shells out to `curl -fL` + `tar -xzf` (Linux/macOS) or PowerShell's `Expand-Archive` (Windows) to grab the matching release archive. Zero new build-time deps. The release matrix in MyOwnMesh's `release.yml` ships one archive per target: - `linux-x86_64` / `linux-aarch64` → `.tar.gz` - `macos-aarch64` / `macos-x86_64` → `.tar.gz` - `windows-x86_64` → `.zip` target-triple → platform-name mapping mirrors that matrix. If the download fails (offline build, GH rate-limited, asset shape changed), falls back to `cargo install --git --tag v0.1.2` with the captured-stderr diagnostic from the prior commit. This path requires the native deps but at least surfaces *which* one is missing instead of swallowing the error. For the user's specific Windows machine: `just dev` will now download `myownmesh-windows-x86_64.zip` (~10 MB), unzip `myownmesh.exe` into `OUT_DIR/myownmesh-staging/`, copy it to `binaries/myownmesh-x86_64-pc-windows-msvc.exe`, and Tauri's bundler ships it next to `MyOwnLLM.exe` in the produced bundle. First-launch then spawns the bundled daemon — no separate install required. https://claude.ai/code/session_01Vp4cvRTaLYd3162EwwcCXg
Three things visible in your last `just dev` log that this
fixes:
**1. `os error 32` during sidecar copy** — your prior `just dev`
was still running and Tauri had the sidecar open. The build's
final `fs::copy` couldn't overwrite a held file. Fixes:
- `binaries/.bundled-rev` sentinel records what's currently at
the sidecar slot. At the top of `bundle_myownmesh_sidecar`, if
the sentinel matches the requested rev AND the file is
non-empty, the whole pipeline (download + extract + copy)
skips. Next build with the parallel LLM still running just
no-ops instead of fighting Windows.
- `write_sidecar_with_retry` exponential-backs-off (150ms →
4.8s, 6 attempts) on `os error 32` specifically. Real "in use"
collisions usually clear in a few hundred ms when the other
process finishes opening the file. After the budget exhausts
it surfaces the error so the user sees something is holding
the file open rather than busy-looping forever.
**2. `find_daemon_binary` only tried `target\debug\myownmesh.exe`
and missed the actual sidecar** — Tauri's dev mode stages
sidecars with the `-<triple>` suffix retained
(`myownmesh-x86_64-pc-windows-msvc.exe`), while production
bundles strip it (`myownmesh.exe`). My old lookup only checked
the stripped name, missing the dev-mode location entirely. Now:
- `build.rs` emits `cargo:rustc-env=DAEMON_SIDECAR_TRIPLE=<triple>`
so the runtime has the exact suffix Tauri uses.
- `daemon_binary_candidates` checks both
`<exe_dir>/myownmesh{.exe}` (prod) AND
`<exe_dir>/myownmesh-<triple>{.exe}` (dev) at the same
priority level.
- New 1b: `<crate_dir>/binaries/myownmesh-<triple>{.exe}` — the
*source* sidecar slot `build.rs` writes to. In `cargo run` /
`tauri dev` paths that don't restage, this is the only place
the bundled binary lives.
- All candidates filtered through a `push_if_usable` helper that
skips zero-byte stubs (the `MYOWNLLM_SKIP_SIDECAR` placeholder
files) — your log had this happen with a stale stub at
`target\debug\myownmesh.exe`.
**3. `warning: function find_daemon_binary is never used`** — leftover
from renaming to `daemon_binary_candidates`. Marked
`#[allow(dead_code)]` since the CLI's diag still needs a
single-binary lookup for its `myownllm mesh` output.
After this lands, your next `just dev`:
- First run: downloads `myownmesh-windows-x86_64.zip`, extracts,
copies to `binaries/myownmesh-x86_64-pc-windows-msvc.exe`,
writes sentinel.
- Second run (while first is still running): sentinel matches,
build.rs no-ops, no file-lock conflict.
- Runtime: finds the sidecar at
`src-tauri/binaries/myownmesh-x86_64-pc-windows-msvc.exe`,
spawns it successfully.
https://claude.ai/code/session_01Vp4cvRTaLYd3162EwwcCXg
You called it: shutdown was leaking processes. Two bugs in one
ungraceful-exit chain that your `Ctrl-C in the terminal` hit
every time:
**1. The LLM was getting orphaned**
`tauri dev` runs `cargo run` which spawns `myownllm.exe`. Tauri
windowed apps detach from the parent console, so a Ctrl-C in
your shell reached cargo but NOT myownllm.exe. The LLM kept
running after your terminal exited — that's what the
`watcher: another myownllm process holds the watcher lock` log
line was telling us. RunEvent::Exit never fired, Drop never ran,
and every file handle the LLM held stayed open (including the
sidecar slot you watched the build fail on).
Fix: a Windows parent-PID watchdog (`windows::install_parent_
watchdog`). At startup it finds our parent PID via
`CreateToolhelp32Snapshot` + Process32W, opens the parent for
`PROCESS_QUERY_LIMITED_INFORMATION`, then polls every second.
When the parent process exits, the watchdog calls
`std::process::exit(0)` — which DOES fire Drop +
RunEvent::Exit, so the daemon child + ollama get cleaned up
and every file handle is released before the LLM actually
terminates.
Skips when the parent is `explorer.exe` (interactive launch
from File Explorer) or `services.exe` (Windows service install
path) so it doesn't false-trigger.
CLI mode (`myownllm status` etc.) also skips since cargo
already manages lifetime properly via the attached console.
**2. The daemon child had no backstop**
Even when Drop DID run, the daemon child's cleanup relied on
the LLM's normal exit path. Any path that skipped Drop
(crash, taskkill /F, OS kill) would orphan the daemon. The
daemon then holds its own .exe file open and the next build's
copy fails with "file in use".
Fix: on Windows, the daemon child is now assigned to a Job
Object with `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE`. The job
handle is leaked intentionally and held for the LLM process's
lifetime. When the LLM exits — graceful or not — Windows
reclaims the handle, the Job closes, every member process is
terminated. OS-level guarantee, not Rust-Drop-level.
Implementation: `windows::assign_to_kill_on_close_job(handle)`
wraps `CreateJobObjectW` + `SetInformationJobObject` +
`AssignProcessToJobObject`. Plumbed via
`DaemonChild { child, job }` in `ensure_daemon_running`.
**For your current orphan state**: open Task Manager (or
`taskkill /F /IM myownllm.exe`) once to clear the stuck process
holding the file. After that, `just dev` should be clean: the
new LLM exits properly when Ctrl-C kills cargo, the daemon dies
with it, no file locks survive.
https://claude.ai/code/session_01Vp4cvRTaLYd3162EwwcCXg
Your diagnosis was right. The download succeeded but something downstream was corrupting it, producing a non-PE file at `target\debug\myownmesh.exe` that spawn rejected with the unhelpful "%1 is not a valid Win32 application" error. The build pipeline was silent about which step failed, so we couldn't tell whether curl, Expand-Archive, or the final copy was the culprit. **Step-by-step diagnostics in build.rs**: Every stage now emits a `cargo:warning` so the log narrates what actually happened: [download] https://github.com/.../myownmesh-windows-x86_64.zip [download] saved C:\...\OUT_DIR\myownmesh-staging\myownmesh-windows-x86_64.zip (10485760 bytes) [extract] C:\...\myownmesh-windows-x86_64.zip [extract] produced C:\...\myownmesh.exe (245678912 bytes) [sidecar] wrote C:\...\binaries\myownmesh-x86_64-pc-windows-msvc.exe (245678912 bytes) PowerShell + tar invocations now capture stderr too, so a malformed zip surfaces the actual error instead of just "exited with 1". **Post-extract validation**: `validate_executable_magic` checks the first 4 bytes for PE (`MZ`), ELF (`\x7fELF`), or Mach-O (`FEED FACE`/`FACF`/`CAFE BABE`/byte-swapped). A corrupt download (HTML error page, empty body, truncated transfer) gets caught at the build step instead of producing a sidecar that spawn rejects at runtime. The downloaded archive also gets a size sanity-check: <1 KiB is almost certainly a GitHub error page rather than the daemon. **Atomic write + corrupt-file self-heal**: `write_sidecar_with_retry` now: - Detects an existing sidecar slot file that fails the magic check (leftover stub, half-written previous attempt) and deletes it before writing — so Tauri's externalBin staging picks up the new content instead of re-staging the corruption. - Writes via `<dst>.tmp-incoming` + atomic rename, so a failure mid-copy never leaves a partial file at the destination. This is what would have produced your truncated `target\debug\myownmesh.exe`: a copy that died partway through left a non-PE-but-non-empty stub at the dest, Tauri staged it, runtime spawn failed cryptically. **Runtime magic-bytes filter**: `mesh/daemon.rs::looks_like_executable` runs the same check before treating a binary as a viable candidate. So even if a corrupt file somehow ends up at any of the candidate paths, the runtime skips it and tries the next instead of producing the "not a valid Win32 application" error. For your stuck state: delete `src-tauri\binaries\myownmesh-x86_64-pc-windows-msvc.exe` and `src-tauri\target\debug\myownmesh.exe` (whichever exist) before the next `just dev`. The fresh build will detect any leftover corruption and refuse to bundle it; the runtime will skip any file that fails the magic check. https://claude.ai/code/session_01Vp4cvRTaLYd3162EwwcCXg
Your last run pinpointed it: download (4 MB) ✓, extract (7.6 MB binary) ✓, magic check ✓, but write to `binaries/` failed with os error 32 (file in use) — the orphan LLM is still locking the slot. Then at runtime the corrupt-but-MZ-starting file at `target\debug\myownmesh.exe` (a partial PE from some earlier failed copy) slipped through my too-lax 4-byte magic check and spawn rejected it with "not a valid Win32 application". Fixes: **Stronger executable validation** — both `build.rs:: validate_executable_magic` and `mesh/daemon.rs:: validate_path_is_executable` now: - Require file size ≥ 1 MiB (real daemon is ~7 MB; a 4 MB truncated PE would still fail this, but realistically a partial download is usually much smaller). A leftover stub or stub-sized garbage file gets rejected here. - On Windows: walk the DOS header to `e_lfanew` (offset 0x3C), seek there, verify the `PE\0\0` signature. A truncated PE that starts with `MZ` but has no real PE header — exactly what your last run's `target\debug\myownmesh.exe` had — fails this check. **Runtime self-heal** — when `daemon_binary_candidates` finds an invalid file at a path the LLM owns (`src-tauri/binaries/`, `src-tauri/target/<profile>/`), it now deletes the file instead of just skipping it. The next build / Tauri dev cycle rewrites cleanly instead of perpetually staging the same corrupt bits. Files at paths we don't own (env-var overrides, PATH lookups, sibling MyOwnMesh target) are skipped but not touched. So your next `just dev`: 1. Build downloads + extracts cleanly (as it did last run). 2. Write to `binaries/` may still fail if the orphan LLM is still locking it — but on the NEXT run, the runtime detects the stale corrupt file at `target\debug\myownmesh.exe`, deletes it, falls through to `binaries/myownmesh-<triple>.exe` which now has the freshly extracted 7.6 MB binary, spawns that. 3. Once the orphan is gone (parent watchdog from the prior commit handles that on first clean exit), everything stabilises. For the orphan that's currently holding the file: a one-time `taskkill /F /IM myownllm.exe` (or just close the LLM window + wait for the watchdog to act on it) is still needed to break the lock initially. After that the new defences keep things clean. https://claude.ai/code/session_01Vp4cvRTaLYd3162EwwcCXg
You were right that an orphan process wasn't the issue. Going back to your actual log: [extract] produced ...\myownmesh.exe (7663104 bytes) myownmesh sidecar bundle failed: ...os error 32 Neither `[sidecar] existing` nor `[sidecar] wrote` printed, so the failure was BEFORE write_sidecar_with_retry — at the intermediate `fs::copy(&bin, &installed_bin)?` step in bundle_myownmesh_sidecar. That copy reads from the freshly- extracted `staging/myownmesh.exe`, which Windows Defender's real-time protection opens to scan as soon as PowerShell creates a new .exe. The fs::copy competes with the scanner and gets os error 32. No process is "running" in the user-visible sense — Defender's scan is invisible to taskkill / Task Manager. Two fixes: **Eliminate the redundant intermediate copy.** The previous flow was: staging/myownmesh.exe → OUT_DIR/myownmesh-staging/myownmesh.exe → binaries/myownmesh-<triple>.exe The OUT_DIR step served no purpose (the sentinel is keyed against the sidecar slot, not OUT_DIR). Now we go directly: staging/myownmesh.exe → binaries/myownmesh-<triple>.exe via write_sidecar_with_retry. One copy instead of two; one failure point instead of two. **Retry on source-locked too, with longer budget.** Previously write_sidecar_with_retry only retried on dst-locked errors; the copy step retried on the COPY itself getting blocked (which I had wrong: the fs::copy success/fail block branched on the COPY's error not the rename's). Now both branches retry with 10 attempts, 200ms → ~102s exponential backoff. Defender on an unsigned 7+ MB exe can take 10s+; the increased budget covers it. Each retry logs which side is locked (`copy from <src>` vs `rename to <dst>`) so the diagnostic points at the actual cause. Now your next `just dev`: - Download → extract → DIRECT sidecar write - If Defender holds the freshly-extracted file: retry with backoff, [sidecar] copy from ... hit sharing violation attempt N/10 logged each round, until Defender releases - After success: [sidecar] wrote ..., sentinel persisted - Subsequent runs short-circuit on the sentinel https://claude.ai/code/session_01Vp4cvRTaLYd3162EwwcCXg
7 tasks
mrjeeves
pushed a commit
that referenced
this pull request
May 28, 2026
PR #204's sidecar bundling prefers a sibling MyOwnMesh checkout's `target/<profile>/myownmesh` binary over the GitHub release download, on the assumption that "if the user has a sibling checkout, they want it." That assumption skipped a check the user hit in the wild: the sibling target/ is whatever the user last built, NOT necessarily the rev pinned in `.myownmesh-rev`. Concrete failure: one device's sibling at v0.1.1 + pin at v0.1.2 → build.rs copied the v0.1.1 binary; the daemon's startup log reported `version="0.1.1"`. The user's other device had no sibling target build → fell through to release download, got v0.1.2. The two daemons couldn't peer because the wire-protocol additions in v0.1.2's PR #16 (the RPC + typed-channel + capability ops) aren't understood by v0.1.1. Fix: when the sibling exists, run `<binary> --version` and compare against the pin. On match, use the sibling. On mismatch or unreadable version, loud warning + fall through to the release download. The escape hatch for users hacking against a non-pinned MyOwnMesh version (env var `MYOWNLLM_MESH_BIN` → explicit binary path, handled in step 1) is unchanged and bypasses the version check entirely. Also write `.bundled-rev` when the sibling path succeeds so the next build's idempotency short-circuit can find it. Standalone `rustc --edition=2021 src-tauri/build.rs ...` clean (the only diagnostic is the expected unresolved `tauri_build` crate from the build-dep that lives outside this sandbox).
mrjeeves
added a commit
that referenced
this pull request
May 28, 2026
…#205) * mesh: wire frontend network catalog into daemon, call start() at boot PRs #203 and #204 landed the daemon plumbing and the sidecar bundle, but left two gaps that left every install stuck pre-join after the migration off Trystero: 1. `meshClient.start()` was never invoked. App.svelte called `meshClient.reconcile()` at boot — which now (in the daemon client) just refreshes peers without subscribing to `mesh://event` or advancing past phase=off. The status pill stayed at "Joining <handle>…" forever. 2. The frontend's saved-network catalog was never pushed into the daemon. The daemon started with `networks=0` regardless of what the user had configured in `~/.myownllm/config.json`, so even when start() ran it dead-ended at `joined_networks[0] ?? ""`. Fix: - App.svelte boot: call `meshClient.start()` instead of `meshClient.reconcile()`. Start owns event subscription, peer snapshot, RPC handler install, capability publish — all the things reconcile() doesn't do. - mesh-daemon.svelte.ts `start()`: bootstrap the daemon's joined-network set from the frontend's active network. Single- active-network UX, so drop any daemon networks that aren't the current active. Idempotent — second launch sees the network already joined (daemon persisted it under MYOWNMESH_HOME) and is a no-op. - mesh-daemon.svelte.ts `reconcile()`: when active network changes under us (Switch button, addNetwork-with-activate), stop + start so handler claims rebind under the new network and the daemon-side leave/join converges. - config.ts: helpers (`networkConfigToDaemonShape`, `daemonAddNetwork`, `daemonRemoveNetwork`, `syncActiveNetworkToDaemon`) translate between the frontend's flat schema (`signaling_servers: string[]`, etc.) and the daemon's structured `myownmesh_core::config::NetworkConfig` shape (`SignalingConfig`, `StunServer { urls }`, etc.). - start() also gains a short retry on `mesh_daemon_status` so a boot that races the daemon-spawn task in Rust's setup() doesn't surface as a hard error during the brief window before the state is `app.manage()`d. - start() serialised via an `inflightStart` promise so a boot call + an early settings click can't double-bootstrap and leak the first event listener. Mid-session settings edits to the active network's STUN / TURN / signaling lists aren't auto-propagated — the daemon has no network-update RPC, only add/remove. Documented in `reconcile()`'s doc; toggle the network off+on to apply changes. `pnpm run check` clean (164 files, 0 errors). `pnpm run build` clean. * build: gate sibling-workspace daemon binary on .myownmesh-rev match PR #204's sidecar bundling prefers a sibling MyOwnMesh checkout's `target/<profile>/myownmesh` binary over the GitHub release download, on the assumption that "if the user has a sibling checkout, they want it." That assumption skipped a check the user hit in the wild: the sibling target/ is whatever the user last built, NOT necessarily the rev pinned in `.myownmesh-rev`. Concrete failure: one device's sibling at v0.1.1 + pin at v0.1.2 → build.rs copied the v0.1.1 binary; the daemon's startup log reported `version="0.1.1"`. The user's other device had no sibling target build → fell through to release download, got v0.1.2. The two daemons couldn't peer because the wire-protocol additions in v0.1.2's PR #16 (the RPC + typed-channel + capability ops) aren't understood by v0.1.1. Fix: when the sibling exists, run `<binary> --version` and compare against the pin. On match, use the sibling. On mismatch or unreadable version, loud warning + fall through to the release download. The escape hatch for users hacking against a non-pinned MyOwnMesh version (env var `MYOWNLLM_MESH_BIN` → explicit binary path, handled in step 1) is unchanged and bypasses the version check entirely. Also write `.bundled-rev` when the sibling path succeeds so the next build's idempotency short-circuit can find it. Standalone `rustc --edition=2021 src-tauri/build.rs ...` clean (the only diagnostic is the expected unresolved `tauri_build` crate from the build-dep that lives outside this sandbox). * fmt: rustfmt collapse of sibling-version error string --------- Co-authored-by: Claude <noreply@anthropic.com>
6 tasks
mrjeeves
added a commit
that referenced
this pull request
May 28, 2026
…on (#207) The migration off Trystero onto the standalone myownmesh daemon (PRs #201 / #203 / #204 / #205 / #206) shipped the code but left every doc still describing the world before the move: - README claimed mesh discovery went "via Trystero over public Nostr relays" and that agent permissions persisted under `Config.agent_permissions.by_device[<device_id>]`. - ARCHITECTURE.md's mesh-module section described `mesh-client.svelte.ts` (deleted), Trystero room ownership (gone), and a TS module table that didn't list any of the files Phase C–D actually shipped (`mesh-daemon.svelte.ts`, `mesh-gossip.ts`, `mesh-inference.ts`, `mesh-file.ts`, `mesh-move.ts`, `mesh-transcribe.ts`, `mesh-governance.ts`). - CONNECTION-ENGINE.md was a 535-line spec for the 4-layer connection engine that no longer lives in this repo — every paragraph referenced `src/mesh-client.svelte.ts` or `mesh-scheduler-worker.ts`, neither of which exists. - DOCS.md's Cloud Mesh section walked the user through Trystero rooms, the legacy on-the-wire `MeshMessage` JSON envelope (`infer_request` / `infer_chunk` / `move_offer` / `file_offer`), and a config example missing every field the per-network schema gained (`label`, `kind`, `topology`, `auto_approve`, `auto_gossip`, `agent_permissions`, `prompts`). - PROGRESS.md was a historical bug-fix doc for a Trystero subscription-state quirk that no longer applies — the engine isn't here anymore. What this commit changes: **README.md**: replace Trystero claim with the bundled `myownmesh` daemon model; correct the agent-permissions storage path to the per-network shape (`Config.cloud_mesh.networks[*]. agent_permissions`) and mention the `auto_gossip` gate. **ARCHITECTURE.md**: rewrite the one-picture diagram to show the daemon sidecar alongside Ollama; rewrite the mesh intro paragraph; rewrite the `mesh/` Rust module row to describe `daemon.rs`, `daemon_commands.rs`, the detect-and-share socket order, and the relationship to `myownmesh_core`; rewrite the TS module table to list every `mesh-*.ts` file actually in the tree with its current role; refresh the CloudMesh sub-tab inventory (Status / Settings / Connections / Graph / Governance / Activity / HTTP); refresh the persistence section to show `daemon.sock` + the per-network config layout. **CONNECTION-ENGINE.md**: rewrite as a short pointer. The 4-layer engine + 7-tier reconnect ladder live in MyOwnMesh now; this doc explains what the LLM still owns on top (the layer-4 LLM-specific protocol), how the LLM talks to the daemon (detect-and-share IPC), and lists the LLM-side RPC methods + typed channels currently in use (`infer`, `transcribe`, `file_offer` / `file_send` + `file_chunks/<id>`, `session_*` / `move_*`, `catalog/announce`, `permissions/snapshot`, `prompts/snapshot`). **DOCS.md Cloud Mesh section**: replace the Trystero transport paragraph with the daemon's detect-and-share model; refresh every What-the-mesh-does-for-you row to match current behavior (click-to-open, click-through Pull, file transfer wire shape, permissions+prompts gossip with the auto_gossip gate, Graph view, Governance view, no Phase-1/Phase-2 split); replace the JSON-over-data-channel wire-protocol box with the daemon RPC + typed-channel surface; refresh the example config to include `label`, `kind`, `topology`, `auto_approve`, `auto_gossip`, `agent_permissions`, `prompts`. **PROGRESS.md**: deleted. The Trystero subscription-state bug it documents doesn't apply post-daemon. Two `// see PROGRESS.md` breadcrumbs in `src-tauri/src/asr/mod.rs` and `src-tauri/src/diarize/cluster.rs` updated to free-standing explanations. Validation: - `pnpm run check`: 164 files, 0 errors, 0 warnings. - `grep -rn "Trystero\|trystero\|mesh-client\.svelte" --include="*.md" .` returns nothing. - `grep -rn "PROGRESS.md" .` returns nothing. https://claude.ai/code/session_01RLu1LdTgtxEDdzhybzqFrk Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR #203 left the daemon spawn path dependent on
myownmeshalready being on the system — users installing the LLM via.dmg/.deb/.msisaw "couldn't find myownmesh" at first launch. Closing that gap: the daemon now bundles with the LLM as a Tauri sidecar, zero separate install.How it works
src-tauri/build.rsbuilds (or copies) the daemon at LLM compile time. Resolution order:MYOWNLLM_MESH_BIN/MYOWNMESH_BINenv var — release CI brings a pre-signed binary.../../MyOwnMesh/target/{debug,release}/— dev convenience when both repos are open as siblings.cargo install --git --rev <pinned>— fetches the daemon source at the pinned rev, builds it, caches inOUT_DIRkeyed by rev. Subsequent builds short-circuit.The built binary lands at
src-tauri/binaries/myownmesh-<target-triple>{.exe}(Tauri'sexternalBinconvention).tauri.conf.json::bundle::externalBindeclares it, so the bundler places it next to the main LLM exe in every shipped artifact (macOSContents/MacOS/, Linux/usr/lib/MyOwnLLM/, Windows next toMyOwnLLM.exe)..myownmesh-revat the repo root pins the daemon revision — single source of truth, currently93b53628(the merged Phase A daemon-IPC commit).find_daemon_binarynow checks the sidecar location (<exe_dir>/myownmesh{.exe}) first. Env-var overrides + PATH + workspace fallbacks retained for dev / CI / offline paths.Offline / dev escape hatches
MYOWNLLM_SKIP_SIDECAR=1—build.rswrites a zero-byte stub at the sidecar slot sotauri_build::build()'sexternalBinexistence check passes;find_daemon_binarydetects the stub vialen() > 0and falls through.MYOWNLLM_MESH_BINenv var — point at a pre-built daemon (skips the fetch).Validation
cargo check --binswith sibling-workspace path: copies the real 245 MB debug ELF into the sidecar slot, build clean.cargo check --binswithMYOWNLLM_SKIP_SIDECAR=1: stub written, build clean.Files
Test plan
cargo check --binsagainst sibling path.cargo check --binsagainst SKIP_SIDECAR stub fallback.tauri buildproduces a.deb/.dmg/.msiwith the daemon bundled next to the main exe (need release-CI validation).https://claude.ai/code/session_01Vp4cvRTaLYd3162EwwcCXg
Generated by Claude Code