+ {/* Header */}
+
+
+
+ Experimental. Pool GPUs with others
+ for decentralized LLM inference — no API keys, no cloud. Start a private mesh, join one
+ with an invite token, or discover public meshes.{' '}
+
+ docs.anarchai.org
+
+
+
+
+
+ {error &&
{error}
}
+
+
+ {/* Not installed — offer download or install link */}
+ {status === 'not-installed' && (
+
+
Get started
+
+ mesh-llm is a small download (~19 MB) that manages local inference and mesh networking.
+ Models are downloaded separately when you start a mesh.
+
+
+
+ )}
+
+ {/* Downloading */}
+ {status === 'downloading' && (
+
+
Downloading mesh-llm...
+
+ Downloading and installing to ~/.mesh-llm/. This should only take a moment.
+
+
+ )}
+
+ {/* Setup panel — shown when stopped and installed */}
+ {(status === 'stopped' || status === 'unknown') && (
+
+ {/* Mode selector */}
+
+
+ setMode('auto')}
+ />
+
+
+ Auto-discover a public mesh
+
+
+ Find and join the best available mesh automatically.
+
+
+ Public meshes are run by volunteers. Your prompts are sent to their hardware — no
+ privacy guarantees.
+
+
+
+
+
+ setMode('join')}
+ />
+
+
+ Join with invite token
+
+
+ Join a private mesh someone shared with you.
+
+
+
+
+
+ setMode('new')}
+ />
+
+
+ Start a new private mesh
+
+
+ Create your own mesh. Share the invite token with others to pool GPUs.
+
+
+
+
+
+ {/* Mode-specific options */}
+ {mode === 'new' && (
+
+
Model to serve
+
setSelectedModel(e.target.value)}
+ className="text-sm bg-background-default border border-border-subtle rounded px-2 py-1.5 text-text-default w-full max-w-sm"
+ >
+ {MODEL_CATALOG.map((m) => (
+
+ {m.name} ({m.size})
+
+ ))}
+
+
+ Downloads automatically if not already cached. Larger models need more VRAM.
+
+
+ )}
+
+ {mode === 'join' && (
+
+ Invite token
+ setJoinToken(e.target.value)}
+ placeholder="Paste invite token here"
+ className="max-w-md"
+ />
+
+ )}
+
+ {(mode === 'auto' || mode === 'join') && (
+
+ setContributeGpu(e.target.checked)}
+ />
+
+ Contribute GPU
+ (serve models for others too)
+
+
+ )}
+
+
+
+ Start Mesh
+
+
+ )}
+
+ {/* Starting indicator */}
+ {status === 'starting' && (
+
+
Starting mesh-llm...
+
+ Connecting to the mesh and loading models. This may take a minute on first run.
+
+
+ )}
+
+ {/* Running state */}
+ {status === 'running' && (
+ <>
+ {/* Invite token */}
+ {statusInfo.token && (
+
+
+
+
Invite token
+
+ Share this with others so they can join your mesh.
+
+
+
+ {copiedToken ? (
+ <>
+
+ Copied
+ >
+ ) : (
+ <>
+
+ Copy
+ >
+ )}
+
+
+
+ {statusInfo.token}
+
+
+ )}
+
+ {/* Model list */}
+ {statusInfo.models.length > 0 && (
+
+
Available Models
+
+ Select a model to use it as your Goose provider.
+
+
+ {statusInfo.models.map((modelId) => {
+ const isActive = activeModel === modelId;
+ return (
+
!saving && activateModel(modelId)}
+ >
+
+
+ {modelId}
+ live
+
+ {isActive ? (
+
Active
+ ) : (
+
{
+ e.stopPropagation();
+ activateModel(modelId);
+ }}
+ disabled={saving}
+ >
+
+ Use
+
+ )}
+
+
+ );
+ })}
+
+
+ )}
+
+ {statusInfo.models.length === 0 && (
+
+ Mesh is running but no models are available yet. A model may still be loading.
+
+ )}
+
+ {/* Actions row */}
+
+ >
+ )}
+
+ {/* Advanced settings */}
+
+
setShowAdvanced(!showAdvanced)}
+ className="flex items-center gap-1 text-sm text-text-muted hover:text-text-default transition-colors"
+ >
+ {showAdvanced ? (
+
+ ) : (
+
+ )}
+ Advanced
+
+
+ {showAdvanced && (
+
+ {statusInfo.binaryPath && (
+
+ Binary
+ {statusInfo.binaryPath}
+
+ )}
+
+ API endpoint
+ http://localhost:{MESH_API_PORT}/v1
+
+
+ Console
+ http://localhost:{MESH_CONSOLE_PORT}
+
+
+ )}
+
+
+ {/* Refresh */}
+
+
+
+ Refresh
+
+
+
+ );
+};
diff --git a/ui/desktop/src/main.ts b/ui/desktop/src/main.ts
index 04b0db97..e9de61e9 100644
--- a/ui/desktop/src/main.ts
+++ b/ui/desktop/src/main.ts
@@ -23,7 +23,8 @@ import fsSync from 'node:fs';
import started from 'electron-squirrel-startup';
import path from 'node:path';
import os from 'node:os';
-import { spawn } from 'child_process';
+import { execFile, execFileSync, execSync, spawn } from 'child_process';
+import http from 'node:http';
import 'dotenv/config';
import { checkServerStatus } from './goosed';
import { startGoosed } from './goosed';
@@ -1564,6 +1565,248 @@ ipcMain.handle('select-file-or-directory', async (_event, defaultPath?: string)
return null;
});
+// ── Mesh-LLM lifecycle ──────────────────────────────────────────────
+
+const MESH_API_PORT = 9337;
+const MESH_CONSOLE_PORT = 3131;
+const MESH_DOWNLOAD_URL =
+ 'https://github.com/michaelneale/mesh-llm/releases/latest/download/mesh-bundle.tar.gz';
+
+async function findMeshBinary(): Promise