Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/apps/desktop/src/main/sidecar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,49 @@ function buildNetworkEnv(): Record<string, string> {
return env
}

function buildSandboxEnv(): Record<string, string> {
const env: Record<string, string> = {}
const vmDir = path.join(os.homedir(), '.arkloop', 'vm')

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 = ''
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 || !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
}
if (rootfsPath) {
env.ARKLOOP_SANDBOX_ROOTFS = rootfsPath
}
return env
}

function buildBrowserSearchEnv(): Record<string, string> {
const baseUrl = getBrowserSearchBaseUrl()
if (!baseUrl) return {}
Expand Down Expand Up @@ -1270,6 +1313,7 @@ async function launchOnPort(port: number, portMode: LocalPortMode): Promise<Side
...buildBrowserSearchEnv(),
...buildMemoryEnv(projectDir),
...buildNetworkEnv(),
...buildSandboxEnv(),
},
stdio: ['ignore', 'pipe', 'pipe'],
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,15 @@ func desktopCurrentSandboxAvailable() bool {
}

func desktopDockerSandboxAvailable() bool {
return desktopSandboxHealthCheck(desktopDockerSandboxAddr)
// Desktop 模式下使用 VZ embedded sandbox,不存在独立的 Docker sandbox 服务。
// 不应假设 127.0.0.1:19002 上有 sandbox 运行。
return false
}

func desktopFirecrackerAvailable() bool {
// Desktop 模式下 VZ embedded sandbox 使用随机端口,只要地址非空且健康即为可用。
addr := strings.TrimSpace(desktop.GetSandboxAddr())
return addr != "" && addr != desktopDockerSandboxAddr && desktopSandboxHealthCheck(addr)
return addr != "" && desktopSandboxHealthCheck(addr)
}

func probeDesktopSandboxHealth(addr string) bool {
Expand Down
12 changes: 10 additions & 2 deletions src/services/bridge/internal/app/app_desktop.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Comment on lines 64 to +66

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject VM mode when no sandbox is available

When the sidecar starts without VM images this startup-only fallback switches a previously persisted vm mode to local, but the POST /v1/execution-mode handler below can still accept vm later in the same no-sandbox session. In that state desktop.GetSandboxAddr() remains empty, and DynamicShellExecutor.resolveBackend routes vm+empty addr to local execution, so a user who toggles VM isolation after startup sees mode=vm persisted while commands run on the host. Please reject vm or immediately reset to local whenever no sandbox address is available.

Useful? React with 👍 / 👎.

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()})
})
Expand Down
17 changes: 14 additions & 3 deletions src/services/desktop/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 != "" {
Expand Down
Loading