From fa01832cdbefc83e9e2ef94ace2847cd462a84c7 Mon Sep 17 00:00:00 2001 From: DivX Date: Sat, 30 May 2026 11:42:03 +0800 Subject: [PATCH 1/3] fix(bridge): remove hardcoded sandbox fallback and auto-switch to local mode When embedded VZ sandbox is not available (no kernel/rootfs configured), Bridge was incorrectly falling back to 127.0.0.1:19002, assuming a Docker sandbox was running. This caused Worker to send requests to a non-existent address, resulting in connection refused errors. Instead of hardcoding a fake address, switch execution mode back to local when sandbox is unavailable, ensuring file operations and shell commands run on the host machine. Co-Authored-By: Claude Opus 4.8 --- src/services/bridge/internal/app/app_desktop.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/services/bridge/internal/app/app_desktop.go b/src/services/bridge/internal/app/app_desktop.go index 7d0492519..ec777a187 100644 --- a/src/services/bridge/internal/app/app_desktop.go +++ b/src/services/bridge/internal/app/app_desktop.go @@ -59,10 +59,18 @@ func (a *Application) RunDesktop(ctx context.Context) error { // Desktop-only: execution-mode endpoint(模式由侧car main 从磁盘恢复,此处不再强制 local) // 若上游已注入真实 sandbox 地址(如 embedded firecracker),这里不覆盖。 + // 当 sandbox 不可用时,不应假设 127.0.0.1:19002 上有 Docker sandbox; + // 此时应回退到 local 模式,避免 Worker 向不存在的地址发起请求。 if desktop.GetSandboxAddr() == "" { - desktop.SetSandboxAddr("127.0.0.1:19002") + if desktop.GetExecutionMode() == "vm" { + desktop.SetExecutionMode("local") + if err := desktop.PersistExecutionMode("local"); err != nil { + slog.Warn("bridge desktop: failed to persist local execution mode", "error", err) + } + slog.Info("bridge desktop: sandbox unavailable, switched to local mode") + } } - slog.Debug("bridge desktop: sandbox addr set", "addr", desktop.GetSandboxAddr()) + slog.Debug("bridge desktop: sandbox addr", "addr", desktop.GetSandboxAddr()) mux.HandleFunc("GET /v1/execution-mode", func(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(map[string]string{"mode": desktop.GetExecutionMode()}) }) From 34d9523cf33e0eb2cc7782603b36391579ee665f Mon Sep 17 00:00:00 2001 From: DivX Date: Sat, 30 May 2026 11:53:44 +0800 Subject: [PATCH 2/3] fix(desktop): pass VZ sandbox env to sidecar and remove API hardcoded 19002 P0: Sidecar now auto-detects kernel/rootfs in ~/.arkloop/vm/ and sets ARKLOOP_SANDBOX_KERNEL_IMAGE / ARKLOOP_SANDBOX_ROOTFS so that StartEmbeddedSandbox() can actually launch the VZ VM pool. P1: Remove desktopDockerSandboxAvailable()'s hardcoded health check to 127.0.0.1:19002. In Desktop mode there is no standalone Docker sandbox; only VZ embedded sandbox exists. Also simplify desktopFirecrackerAvailable() by removing the incorrect addr!=19002 guard. Co-Authored-By: Claude Opus 4.8 --- src/apps/desktop/src/main/sidecar.ts | 31 +++++++++++++++++++ ...rovider_effective_state_helpers_desktop.go | 7 +++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/apps/desktop/src/main/sidecar.ts b/src/apps/desktop/src/main/sidecar.ts index 62200a915..f578fad63 100644 --- a/src/apps/desktop/src/main/sidecar.ts +++ b/src/apps/desktop/src/main/sidecar.ts @@ -747,6 +747,36 @@ function buildNetworkEnv(): Record { return env } +function buildSandboxEnv(): Record { + const env: Record = {} + const vmDir = path.join(os.homedir(), '.arkloop', 'vm') + + if (!fs.existsSync(vmDir)) return env + + const entries = fs.readdirSync(vmDir) + let kernelPath = '' + let rootfsPath = '' + + for (const entry of entries) { + const fullPath = path.join(vmDir, entry) + if (!fs.statSync(fullPath).isFile()) continue + const lower = entry.toLowerCase() + if (lower === 'vmlinux' || lower.startsWith('vmlinux.')) { + kernelPath = fullPath + } else if (lower.endsWith('.ext4') || lower.endsWith('.img')) { + rootfsPath = fullPath + } + } + + if (kernelPath) { + env.ARKLOOP_SANDBOX_KERNEL_IMAGE = kernelPath + } + if (rootfsPath) { + env.ARKLOOP_SANDBOX_ROOTFS = rootfsPath + } + return env +} + function buildBrowserSearchEnv(): Record { const baseUrl = getBrowserSearchBaseUrl() if (!baseUrl) return {} @@ -1270,6 +1300,7 @@ async function launchOnPort(port: number, portMode: LocalPortMode): Promise Date: Sat, 30 May 2026 12:08:51 +0800 Subject: [PATCH 3/3] feat(desktop): add clear prompts when VZ sandbox images are missing When VZ sandbox kernel/rootfs images are not found: - Sidecar now logs actionable hints to stdout showing where images should be placed (~/.arkloop/vm/) and that local mode will be used. - StartEmbeddedSandbox provides structured slog warnings with 'hint' and 'fallback' fields for easier debugging. This helps users understand why VM isolation is unavailable instead of silently falling back to local execution. Co-Authored-By: Claude Opus 4.8 --- src/apps/desktop/src/main/sidecar.ts | 15 ++++++++++++++- src/services/desktop/runtime/runtime.go | 17 ++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/apps/desktop/src/main/sidecar.ts b/src/apps/desktop/src/main/sidecar.ts index f578fad63..dd74f233c 100644 --- a/src/apps/desktop/src/main/sidecar.ts +++ b/src/apps/desktop/src/main/sidecar.ts @@ -751,7 +751,12 @@ function buildSandboxEnv(): Record { const env: Record = {} const vmDir = path.join(os.homedir(), '.arkloop', 'vm') - if (!fs.existsSync(vmDir)) return env + if (!fs.existsSync(vmDir)) { + console.info('[sidecar] VZ sandbox directory not found:', vmDir) + console.info('[sidecar] To enable VM isolation, place vmlinux + rootfs.ext4 in this directory,') + console.info('[sidecar] or download them via Desktop Settings → Updates when available.') + return env + } const entries = fs.readdirSync(vmDir) let kernelPath = '' @@ -768,6 +773,14 @@ function buildSandboxEnv(): Record { } } + if (!kernelPath || !rootfsPath) { + const missing = [!kernelPath && 'vmlinux', !rootfsPath && 'rootfs.ext4'].filter(Boolean) + console.warn('[sidecar] VZ sandbox images incomplete:', missing.join(', '), 'missing in', vmDir) + console.info('[sidecar] VM isolation unavailable; commands will run on host (local mode).') + } else { + console.info('[sidecar] VZ sandbox images found:', { kernel: kernelPath, rootfs: rootfsPath }) + } + if (kernelPath) { env.ARKLOOP_SANDBOX_KERNEL_IMAGE = kernelPath } diff --git a/src/services/desktop/runtime/runtime.go b/src/services/desktop/runtime/runtime.go index ff26643ed..84878df83 100644 --- a/src/services/desktop/runtime/runtime.go +++ b/src/services/desktop/runtime/runtime.go @@ -197,16 +197,27 @@ func StartEmbeddedSandbox(ctx context.Context) { socketDir := strings.TrimSpace(os.Getenv("ARKLOOP_SANDBOX_SOCKET_DIR")) if kernelPath == "" || rootfsPath == "" { - slog.Warn("sandbox: kernel/rootfs paths not configured, falling back to trusted mode") + slog.Warn("sandbox: VZ sandbox images not configured", + slog.String("hint", "Place vmlinux and rootfs.ext4 in ~/.arkloop/vm/ or download via Desktop Settings → Updates"), + slog.String("fallback", "local execution mode"), + ) return } if _, err := os.Stat(kernelPath); err != nil { - slog.Warn("sandbox: kernel not found, falling back to trusted mode", "path", kernelPath) + slog.Warn("sandbox: kernel image not found", + slog.String("path", kernelPath), + slog.String("hint", "Ensure vmlinux exists at the configured path"), + slog.String("fallback", "local execution mode"), + ) return } if _, err := os.Stat(rootfsPath); err != nil { - slog.Warn("sandbox: rootfs not found, falling back to trusted mode", "path", rootfsPath) + slog.Warn("sandbox: rootfs image not found", + slog.String("path", rootfsPath), + slog.String("hint", "Ensure rootfs.ext4 exists at the configured path"), + slog.String("fallback", "local execution mode"), + ) return } if initrdPath != "" {