mirror of
https://github.com/jamesstringer90/appsandbox.git
synced 2026-10-23 11:33:37 -05:00
Ubuntu 26.04 LTS guest end-to-end:
- cidata-driven autoinstall pipeline (subiquity user-data + nested
cloud-init bootcmd) in disk_util.c. Static NAT IP, ssh-via-vsock
marker, serial-debug kernel cmdline injected into ESP grub.cfg by
iso-patch.exe. Five separate firstboot pipeline fixes get the VM
to GDM unattended on Hyper-V Gen 2.
- Linux guest stack under tools/linux/:
- appsandbox-agent (vsock :1 control + clipboard helper spawn)
- appsandbox-audio (snd-aloop -> vsock :4)
- appsandbox-display (KMS framebuffer capture -> vsock :2)
- appsandbox-input (uinput injection <- vsock :3)
- appsandbox-clipboard (XCB selection bridge <-> vsock :5/:6)
Helper binaries staged via release/resources/linux/.
- asb_drm DRM module: virtual single-CRTC display sink the host
captures over vsock. Platform-bus, DRIVER_GEM/MODESET/ATOMIC/RENDER,
linear shmem framebuffers. asb-evict-simpledrm.service unbinds the
EFI simple-framebuffer before display-manager so mutter binds
asb_drm as the sole DRM card.
- dxgkrnl vendored byte-identical from microsoft/WSL2-Linux-Kernel
linux-msft-wsl-6.18.y with our DKMS/out-of-tree shim (dxgkrnl_compat.h
+ Kbuild + dkms.conf). Autoinstall late-commands dkms-builds against
every /target/lib/modules/<kver> so kernel-version drift between ISO
and post-update reboot still leaves a working .ko.
- wsl-mesa custom build (Mesa 25.3 + d3d12 gallium + microsoft-
experimental dzn Vulkan). Installs to /opt/wsl-mesa parallel to
Ubuntu's stock Mesa. Prebuilt tarball for ubuntu-26.04-amd64
shipped under tools/linux/wsl-mesa/prebuilt/ (21 MB).
- Per-VM GPU acceleration policy:
- /etc/systemd/user-environment-generators/50-appsandbox-gpu
emits GALLIUM_DRIVER=d3d12 + LD_LIBRARY_PATH + VK_DRIVER_FILES
into the user@1000 systemd-user environment when /dev/dxg and
/opt/wsl-mesa are present. Inherited by every app-*.scope a
.desktop launch creates.
- /etc/systemd/user/org.gnome.Shell@.service.d/no-gpu.conf
UnsetEnvironment= strips these for the compositor — mutter
stays on llvmpipe so asb_drm scanout works (the d3d12 pipe has
no GBM compatibility with asb_drm).
- /usr/local/bin/appsandbox-gpu wrapper for terminal-launched
opt-in GPU acceleration (probes /dev/dxg; no-op on no-GPU VMs).
- Host changes:
- vm_display_idd.c, vm_clipboard.c, vm_agent.c, vm_ssh_proxy.c,
asb_display.c: hcs_service_guid(os_type, port) so the same code
paths reach the per-OS service identifiers (HV_GUID for Windows,
vsock template for Linux).
- asb_core.c: linux_create_thread builds an empty VHDX, cidata
ISO with extras (agent ELFs, dxgkrnl src, asb_drm src, wsl-mesa
tarball, wsl-deps libs), and boots the desktop ISO + cidata.
- hcs_vm.c: HostLxssLib Plan9 share appended for Linux VMs;
VideoMonitor + ComPorts retained for serial debug.
- gpu_enum.c/h: gpu_append_lxsslib_share helper.
- vm_clipboard.c file-drop support (CF_HDROP / x-special/gnome-
copied-files round-trip).
- vm_display_idd.c: 32-bit S32_LE PCM audio path matches the
Linux daemon's snd-aloop capture format.
- iso-patch.c gains a patch_ubuntu_esp_grub_for_debug pass that
rewrites /EFI/ubuntu/grub.cfg via the FAT32 ESP, plus a qcow2->
vhdx converter with a RFC 1951 DEFLATE inflater for compressed
cluster handling.
- wsl-deps: Microsoft's libd3d12.so / libd3d12core.so / libdxcore.so
fetched on demand from the public NuGet feed
pkgs.dev.azure.com/shine-oss/wsl (no auth). Staged at
/opt/appsandbox/wsl-deps on the guest and put on /etc/ld.so.conf.d
rather than /usr/lib/wsl/lib (which is overlaid by the agent's
9P mount).
UI: web/{app.js,index.html} expose Linux VM creation + GPU selection,
distribution dropdown for cached Ubuntu ISOs, and the ISO picker for
custom images.
191 lines
8.2 KiB
HTML
191 lines
8.2 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>App Sandbox</title>
|
|
<link rel="stylesheet" href="style.css">
|
|
</head>
|
|
<body>
|
|
|
|
<!-- Virtual Machines -->
|
|
<section id="vm-section">
|
|
<div class="section-header">
|
|
<h2>Sandboxes</h2>
|
|
<button id="btn-new-sandbox" class="primary" onclick="openCreateModal()">+ New Sandbox</button>
|
|
</div>
|
|
<div class="vm-table-wrap">
|
|
<table id="vm-table">
|
|
<thead><tr>
|
|
<th>Name</th>
|
|
<th>OS</th>
|
|
<th>Status</th>
|
|
<th>Agent</th>
|
|
<th>CPU</th>
|
|
<th>RAM (MB)</th>
|
|
<th>HDD (GB)</th>
|
|
<th>GPU</th>
|
|
<th>Network</th>
|
|
<th class="win-only">Snapshot</th>
|
|
<th class="icon-col">▶️</th>
|
|
<th class="icon-col" title="IDD Display">📺</th>
|
|
<th class="icon-col" title="SSH">>_</th>
|
|
<th class="icon-col">⏻</th>
|
|
<th class="icon-col">✕️</th>
|
|
<th class="icon-col">🗑️</th>
|
|
<th class="icon-col">✏️</th>
|
|
</tr></thead>
|
|
<tbody id="vm-tbody"></tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Log -->
|
|
<section id="log-section">
|
|
<h2 class="collapsible" onclick="toggleSection('log-section')"><span class="collapse-arrow" id="log-section-arrow">▼</span> Log</h2>
|
|
<div id="log-panel"></div>
|
|
</section>
|
|
|
|
<!-- Prerequisite modal -->
|
|
<div class="modal-overlay" id="prereq-overlay">
|
|
<div class="modal">
|
|
<h3>Virtual Machine Platform Required</h3>
|
|
<p id="prereq-message">App Sandbox requires the <strong>Virtual Machine Platform</strong> Windows feature to create and run VMs. This feature is not currently enabled.</p>
|
|
<div class="modal-buttons" id="prereq-buttons">
|
|
<button onclick="document.getElementById('prereq-overlay').classList.remove('active')">Cancel</button>
|
|
<button class="primary" onclick="enableFeature()">Enable</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Create Sandbox modal -->
|
|
<div class="modal-overlay" id="create-vm-overlay">
|
|
<div class="modal wide">
|
|
<h3>New Sandbox</h3>
|
|
<div class="form-grid">
|
|
<label for="vm-name">VM Name:</label>
|
|
<div class="form-row">
|
|
<input type="text" id="vm-name" value="MyAppSandbox" class="input-md">
|
|
<span class="field-warn" id="vm-name-warn"></span>
|
|
</div>
|
|
|
|
<label for="os-type">OS Type:</label>
|
|
<div class="form-row">
|
|
<select id="os-type" onchange="applyOsTypeUI()">
|
|
<option value="Windows" selected>Windows</option>
|
|
<option value="Linux">Linux</option>
|
|
<option value="macOS">macOS</option>
|
|
</select>
|
|
</div>
|
|
|
|
<label class="needs-iso">OS Image:</label>
|
|
<div class="image-row needs-iso">
|
|
<input type="text" id="image-path" placeholder="Path to ISO or VHDX...">
|
|
<button id="btn-browse" onclick="sendCmd('browseImage')">Browse...</button>
|
|
<span class="or-separator win-only">OR</span>
|
|
<label class="win-only" style="text-align:left">Template:</label>
|
|
<div class="template-dropdown win-only" id="template-dropdown">
|
|
<div class="template-dropdown-selected" id="template-dropdown-selected">(None)</div>
|
|
<div class="template-dropdown-list" id="template-dropdown-list"></div>
|
|
<input type="hidden" id="template-select" value="">
|
|
</div>
|
|
</div>
|
|
|
|
<label class="needs-linux-version">Distribution:</label>
|
|
<div class="form-row needs-linux-version">
|
|
<select id="linux-version">
|
|
<option value="ubuntu-26.04" selected>Ubuntu 26.04 LTS</option>
|
|
</select>
|
|
<span class="host-info">Downloaded on first create (~600 MB), cached on the host</span>
|
|
</div>
|
|
|
|
<label for="hdd-size">HDD Size:</label>
|
|
<div class="form-row">
|
|
<input type="number" id="hdd-size" value="64" min="1" class="input-sm"> GB
|
|
<span class="host-info" id="host-hdd"></span>
|
|
</div>
|
|
|
|
<label for="ram-size">RAM Size:</label>
|
|
<div class="form-row">
|
|
<input type="number" id="ram-size" value="16384" min="512" class="input-sm"> MB
|
|
<span class="host-info" id="host-ram"></span>
|
|
</div>
|
|
|
|
<label for="cpu-cores">CPU Cores:</label>
|
|
<div class="form-row">
|
|
<input type="number" id="cpu-cores" value="8" min="1" class="input-sm">
|
|
<span class="host-info" id="host-cpu"></span>
|
|
</div>
|
|
|
|
<label for="gpu-mode">GPU:</label>
|
|
<div class="form-row">
|
|
<select id="gpu-mode">
|
|
<option value="0">None</option>
|
|
<option value="1" selected>Default GPU</option>
|
|
<option value="2">Try all</option>
|
|
</select>
|
|
</div>
|
|
|
|
<label for="net-mode">Network:</label>
|
|
<div class="form-row">
|
|
<select id="net-mode" onchange="onNetModeChange()">
|
|
<option value="0">None</option>
|
|
<option value="1" selected>NAT</option>
|
|
<option value="2">External</option>
|
|
<option value="3">Internal</option>
|
|
</select>
|
|
<label id="net-adapter-label" style="text-align:left">Adapter:</label>
|
|
<select id="net-adapter"></select>
|
|
</div>
|
|
|
|
<label>Username:</label>
|
|
<div class="cred-row">
|
|
<input type="text" id="admin-user" value="user" class="input-md" autocomplete="off">
|
|
<span class="field-warn" id="admin-user-warn"></span>
|
|
</div>
|
|
|
|
<label>Password:</label>
|
|
<div class="cred-row">
|
|
<input type="password" id="admin-pass" value="test123">
|
|
<label>Confirm:</label>
|
|
<input type="password" id="admin-confirm" value="test123">
|
|
<span class="toggle-pass" onmousedown="showPassword()" onmouseup="hidePassword()" onmouseleave="hidePassword()">👁</span>
|
|
</div>
|
|
|
|
<label>Options:</label>
|
|
<div class="form-row">
|
|
<label class="win-only" title="Disables Secure Boot — required for test-signed VDD driver"><input type="checkbox" id="test-mode" checked> Test Mode</label>
|
|
<label title="Exposes the VM's sshd on a host-loopback port, tunnelled over vsock"><input type="checkbox" id="ssh-enabled"> SSH Server</label>
|
|
</div>
|
|
</div>
|
|
<div class="modal-buttons">
|
|
<button onclick="closeCreateModal()">Cancel</button>
|
|
<button id="btn-create-template" class="win-only" disabled onclick="onCreateTemplate()">Build Template</button>
|
|
<button id="btn-create" class="primary" disabled onclick="onCreateVm()">Create Sandbox</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Confirm modal -->
|
|
<div class="modal-overlay" id="modal-overlay">
|
|
<div class="modal">
|
|
<h3 id="modal-title">Confirm</h3>
|
|
<p id="modal-message"></p>
|
|
<label style="display:none; font-size:12px; color:var(--text-dim); cursor:pointer; margin-bottom:8px;">
|
|
<input type="checkbox" id="modal-dont-show"> Don't show this again
|
|
</label>
|
|
<div id="modal-input-row" style="display:none; margin-bottom:12px;">
|
|
<label style="font-size:12px; color:var(--text-dim);" id="modal-input-label">Name:</label>
|
|
<input type="text" id="modal-input" style="width:100%; margin-top:4px;">
|
|
</div>
|
|
<div class="modal-buttons">
|
|
<button onclick="modalResolve(false)">Cancel</button>
|
|
<button class="danger" onclick="modalResolve(true)" id="modal-confirm-btn">Delete</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="app.js"></script>
|
|
</body>
|
|
</html>
|