Skip to content

feat(mesh): download-on-demand mesh node + membership-gated admission#1586

Draft
michaelneale wants to merge 5 commits into
mainfrom
micn/mesh-node-download
Draft

feat(mesh): download-on-demand mesh node + membership-gated admission#1586
michaelneale wants to merge 5 commits into
mainfrom
micn/mesh-node-download

Conversation

@michaelneale

Copy link
Copy Markdown

What

Replaces the statically-linked mesh-llm node with a download-on-first-use managed sidecar, and adds membership-gated mesh admission enforced by mesh's own ownership/trust layer.

The mesh-llm cargo feature previously compiled the whole mesh node into buzz-desktop (+52.4 MB binary, +17.5 MB compressed download), which is why it stayed off by default behind just mesh=1. This PR removes the mesh-llm-sdk / mesh-llm-host-runtime git deps entirely — the feature is now +0.6 MB (57.3 → 57.9 MB) and could plausibly become default-on.

main (embedded) this PR (managed sidecar)
binary weight of feature +52.4 MB +0.6 MB
mesh crates in dep graph ~full workspace none
node artifact compiled in official pinned release, downloaded on first use (~30 MB, sha256-verified, ed25519 release-attested, cached per-version)
peer admission reachability only (membership-gated discovery + call-me-now pairing) same plus mesh ownership trust: --trust-policy allowlist fed from Buzz membership
mesh-llm rev ebc3ab4c (113 commits stale) current main (49cf0342), incl. server-side tool-call emulation

How it works

Download-on-demand (mesh_llm/node_install.rs): first mesh_start_node fetches the pinned official release from GitHub (MESH_NODE_VERSION = v0.72.2), verifies the published .sha256 sidecar (checksum fetched before the big download), extracts to a versioned cache dir, and emits mesh-node-install-progress app events for the UI. A mesh_node_install_status command lets the UI say "first start downloads the mesh runtime (~30 MB)" up front.

Managed process (mesh_llm/node_process.rs): spawns mesh-llm serve/client --headless as a supervised child (own process group, SIGTERM→SIGKILL teardown, kill_on_drop) and drives it over its local management API (/api/status) — the same HTTP surface the SDK's embedded handle used internally. The node is stamped BUZZ_MANAGED_AGENT and mesh-llm joins KNOWN_AGENT_BINARIES, so the existing orphan sweep reclaims crash leftovers without ever touching a user-run standalone mesh-llm.

Membership-gated admission (mesh_llm/owner_identity.rs + relay): Buzz decides membership; mesh enforces it.

  • Every Buzz-managed node gets an auto-initialized mesh owner identity (mesh-llm auth init, cached) and runs --owner-key --owner-required --trust-policy allowlist.
  • The relay's kind:30621 sanitizer now includes the reporter's verified owner ID (unverified claims dropped), so the membership-gated status pipeline doubles as the trust-allowlist distribution channel.
  • Desktops build --trust-owner lists from those events at node start.
  • Invite tokens remain dial metadata only. Public iroh relays stay disabled (v1 posture); reachability continues to flow through the relay's membership-validated call-me-now pairing.

Any-agent mesh inference (mesh_llm/preset.rs): mesh_agent_preset accepts an optional runtimeId; "goose" emits goose's env dialect (GOOSE_PROVIDER=openai, OPENAI_HOST, GOOSE_MODEL, no sidecar MCP) so goose — including the bundled goose-acp from #1526 — can run on mesh inference. Default stays buzz-agent.

Verification

  • mesh_node_e2e_download_spawn_openai_surface (ignored, network): full first-use flow — download, checksum, cache hit, owner identity create/reuse, spawn, /api/status (attestation + ownership verified), /v1/models, stop.
  • mesh_trust_allowlist_admits_member_rejects_stranger (ignored, 3 nodes): a peer with an allowlisted owner is admitted; a stranger holding the same valid invite token never enters the peer table — proving tokens are dial metadata and ownership is the gate.
  • 1002 desktop lib tests, 23 relay mesh tests, fmt + clippy clean; compiles with the feature on and off.

Known gaps / follow-ups

  • Join-after-start respawns the node with --join appended (no join-at-runtime management endpoint yet). Upstream candidate: POST /api/join in mesh-llm.
  • auth status output is text-parsed; upstream candidates: --json flag + a light typed management-API crate so schema drift fails at pin-bump compile time instead of runtime.
  • Serve-mode with a real model unchanged from main's node behavior; not re-tested here (client/admission/API paths are).
  • Node upgrades are a deliberate MESH_NODE_VERSION bump with the ignored E2Es as the re-validation gate.

…esh preset

- Bump mesh-llm pin ebc3ab4c -> 49cf0342 (113 commits: server-side
  tool-call emulation for models without native tool support, reasoning
  format fixes, iroh 1.0 stable, and more)
- Adapt to initialize_host_runtime() becoming async upstream
- mesh_agent_preset now takes an optional runtimeId: "goose" emits
  goose's env dialect (GOOSE_PROVIDER=openai, OPENAI_HOST without /v1,
  GOOSE_MODEL) with no sidecar MCP, so goose — including the bundled
  goose-acp sidecar — can run inference on the relay mesh. Default and
  unknown ids preserve the buzz-agent behavior.
The mesh-llm feature previously statically linked the whole mesh node
(~52 MB binary weight, +17.5 MB compressed) which kept it off by default
behind 'just mesh=1' builds. Replace the in-process embedding with the
pattern Buzz already uses for every other capability binary:

- mesh_llm/node_install.rs: first use downloads the official pinned
  mesh-llm release (~30 MB), verifies the published sha256, extracts to
  a versioned cache dir, and emits progress app events for the UI.
- mesh_llm/node_process.rs: spawns 'mesh-llm serve/client --headless'
  as a supervised child process (own process group, SIGTERM-then-kill
  teardown) and drives it over its local management API — the same
  HTTP surface the SDK's embedded handle used internally.
- DesktopMeshRuntime keeps its exact API; dial_endpoint_addr respawns
  with the extra --join token (the standalone node has no join-at-
  runtime endpoint yet; upstream candidate: POST /api/join).
- mesh_node_install_status command so the UI can say 'first start will
  download the mesh runtime' up front.
- mesh-llm-sdk/host-runtime git deps removed entirely; the mesh-llm
  cargo feature is now a few KB of client code (57.9 MB binary with
  the feature on vs 57.3 without — was 109.7 MB).

Admission/coordination is unchanged and mesh-native: nostr discovery,
invite-token admission, iroh transport — private by default (no
--publish), same flags as the embedded config used.

Ignored e2e test covers the full first-use flow (download, checksum,
cache hit, spawn, status + attestation verification, /v1/models, stop):
cargo test --features mesh-llm -- --ignored mesh_node_e2e
Buzz decides membership; mesh enforces it — the idiomatic split:

- Every Buzz-managed node gets an auto-initialized mesh owner identity
  (mesh-llm auth init, cached at mesh-node/owner.key) and runs with
  --owner-key --owner-required --trust-policy allowlist.
- The relay's kind:30621 sanitizer now carries the reporter's VERIFIED
  owner id (unverified claims are dropped), so the membership-gated
  status pipeline doubles as the trust-allowlist distribution channel.
- Desktops build --trust-owner lists from those events at node start
  (trusted_owner_ids_from_events); own owner id always included.
- Invite tokens remain dial metadata only. Proven by an ignored 3-node
  e2e (mesh_trust_allowlist_admits_member_rejects_stranger): a peer
  with an allowlisted owner is admitted; a stranger holding the SAME
  valid invite token never enters the peer table.
- mesh-llm added to KNOWN_AGENT_BINARIES and the node stamped with
  BUZZ_MANAGED_AGENT, so the orphan sweep reclaims crash leftovers
  without touching user-run standalone mesh-llm processes.
- Public iroh relays stay disabled (v1 posture): reachability continues
  to flow through the relay's membership-validated call-me-now pairing.
…e.rs

The desktop no longer links mesh native code (the node is a downloaded
release binary), so the mesh-llm rev resolution + llama.cpp Metal
prebuild + cache steps in ci.yml/release.yml are dead weight — and were
failing at the new mesh rev. The Tauri release build keeps
--features mesh-llm, which is now a few KB of client code.

Also split KNOWN_AGENT_BINARIES + name matchers into
managed_agents/known_binaries.rs — runtime.rs was at its file-size
budget and the mesh-llm sweep entry pushed it over.
@michaelneale michaelneale force-pushed the micn/mesh-node-download branch from d77ceac to 6eaf404 Compare July 7, 2026 16:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant