Architecture
XB Homebrew Vault uses the MVVM pattern with CommunityToolkit.Mvvm and Avalonia UI 12, running on .NET 10.
Layered Architecture
graph TD
subgraph Views["Views (Avalonia XAML)"]
MW[MainWindow]
SW[SplashWindow]
SetupW[SetupWizardWindow]
BV[BrowseView]
IV[InstalledView]
SV[SettingsView]
TV[ToolsView]
LV[LogsView]
FV[FileExplorerView]
InV[InspectorView]
NotifP[NotificationsPanel]
TasksP[TasksPanel]
CW[ConnectionWindow]
NIW[NetworkInfoWindow]
PW[ProcessesWindow]
SIW[SystemInfoWindow]
CDW[CrashDataWindow]
PerfW[PerformanceWindow]
UsbW[UsbPermissionWindow]
LbW[LoopbackExemptWindow]
SS[SScreenshotWindow]
CustW[CustomInstallWindow]
ItemW[ItemDetailWindow]
DelW[DeleteConfirmWindow]
DiscW[DiscordPopup]
SftpW[SftpInfoWindow]
end
subgraph ViewModels["ViewModels (CommunityToolkit.Mvvm)"]
MVM[MainViewModel]
BVM[BrowseViewModel]
IVM[InstalledViewModel]
SVM[SettingsViewModel]
TVM[ToolsViewModel]
FVM[FileExplorerViewModel]
InVM[InspectorViewModel]
CVM[ConnectionViewModel]
NIVM[NetworkInfoViewModel]
PVM[ProcessesViewModel]
SIVM[SystemInfoViewModel]
CDVM[CrashDataViewModel]
PerfVM[PerformanceViewModel]
RVM[RefreshViewModel]
ConfVM[ConfirmViewModel]
CIWM[CustomInstallViewModel]
SWVM[SetupWizardViewModel]
USBVM[UsbPermissionViewModel]
ShVM[ScreenshotViewModel]
LbVM[LoopbackExemptViewModel]
DelVM[DeleteConfirmViewModel]
TkVM[TaskCenterViewModel]
end
subgraph Services["Services"]
Auth[XboxAuthService]
Pkg[XboxPackageService]
Proc[XboxProcessService]
Net[XboxNetworkService]
Sys[XboxSystemService]
Perf[XboxPerformanceService]
Parser[XboxResponseParser]
CAS[CatalogApiService]
PS[PackageInstallService]
SSvc[SettingsService]
CS[CryptoService]
CSvc[CacheService]
UDD[UsbDriveDetector]
Sftp[SftpService]
SftpT[SftpTransferService]
Portal[PortalAppFilesService]
XRay[XrayAgentService]
UpdChk[GitHubReleaseCheckerService]
PO[PackageOverrideService]
BgT[BackgroundTaskService]
ConnMon[ConnectionMonitorService]
NotifC[NotificationCenterService]
PreF[PreFlightChecker]
WinSet[WindowSettingsService]
UpdCache[UpdateVersionCache]
Colorizer[InspectorConsoleColorizer]
PDiag[PlatformDialog]
L[Logger]
end
subgraph Models["Models"]
CI[CatalogItem]
IP[InstalledPackage]
PI[ProcessInfo]
NI[NetworkInfo]
SI[SystemInfo]
CD[CrashDumpInfo]
PSnap[PerformanceSnapshot]
XC[XboxConnection]
AS[AppSettings]
IPI[InstallProgressInfo]
UDI[UsbDriveInfo]
SftpE[SftpEntry]
XA[XrayAgentInfo]
BgTask[BackgroundTask]
Notif[NotificationItem]
end
Views --> ViewModels
ViewModels --> Services
Services --> Models
style Views fill:#1A1D23,stroke:#447F3E,color:#9ACA3C
style ViewModels fill:#1A1D23,stroke:#447F3E,color:#9ACA3C
style Services fill:#447F3E,stroke:#9ACA3C,color:#fff
style Models fill:#2A2D33,stroke:#447F3E,color:#9ACA3C
| Layer | Responsibility |
|---|---|
| Views | Avalonia AXAML windows and user controls — purely declarative |
| ViewModels | Commands, observable state, business-logic orchestration |
| Services | All I/O: HTTP, WebSocket, SSH/SFTP, file system, settings, crypto, caching, WMI |
| Models | Plain data classes — CatalogItem, InstalledPackage, PerformanceSnapshot, etc. |
Data Flow
flowchart LR
U[User Action] --> V[View]
V -->|Command| VM[ViewModel]
VM -->|HTTP/WS| S[Service]
S -->|Parse| M[Model]
M -->|Response| S
S -->|ObservableProperty| VM
VM -->|Binding| V
V -->|Render| U
style U fill:#447F3E,stroke:#9ACA3C,color:#fff
style V fill:#2A2D33,stroke:#447F3E,color:#9ACA3C
style VM fill:#2A2D33,stroke:#447F3E,color:#9ACA3C
style S fill:#447F3E,stroke:#9ACA3C,color:#fff
style M fill:#9ACA3C,stroke:#447F3E,color:#000
App Startup
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#1A1D23', 'primaryBorderColor': '#447F3E', 'lineColor': '#9ACA3C', 'secondBkgColor': '#2A2D33', 'tertiaryColor': '#447F3E'}}}%%
sequenceDiagram
autonumber
participant User
participant Main as Program.Main
participant PF as PreFlightChecker
participant App as App.axaml.cs
participant Splash as SplashWindow
participant SSvc as SettingsService
participant Setup as SetupWizardWindow
participant MainW as MainWindow
User->>Main: Launch
Main->>Main: Single-instance mutex check
alt Another instance running
Main->>Main: Activate existing window, exit
end
Main->>PF: Pre-flight: validate/corrupt-repair settings + cache
Main->>App: BuildAvaloniaApp()
App->>Splash: Show()
Splash->>App: 2s min delay
App->>SSvc: Load settings
alt First run (no settings)
App->>Setup: ShowDialog() — 3-step wizard
Setup->>App: credentials captured
end
App->>App: Compose services (auth, package, system, network, process, performance, sftp, portal, cache, catalog, override)
App->>App: Start BackgroundTaskService + ConnectionMonitorService
App->>MainW: new MainWindow
App->>Splash: Close()
App->>MainW: Show()
MainW->>MainW: Register dialog actions
User->>MainW: Interact
Services
| Service | Responsibility | Lines |
|---|---|---|
XboxAuthService |
WDP connection: HTTP client, Basic auth, CSRF cookie, connection test, SMB password fetch, credential state (IsConfigured, IsConnected) |
334 |
XboxPackageService |
Package lifecycle: list installed, install (single + dependencies), uninstall, launch, suspend, terminate, running-package detection | 504 |
XboxProcessService |
Process info: list processes, kill by PID, running title | 90 |
XboxNetworkService |
Network config + WiFi interface/network listing | 99 |
XboxSystemService |
System info, crash dumps (list/delete/control), screenshot, restart/shutdown | 237 |
XboxPerformanceService |
WebSocket performance stream → PerformanceSnapshot |
87 |
XboxResponseParser |
Shared WDP JSON parsing helpers for the Xbox services | 169 |
CatalogApiService |
Fetches and parses the Emulation Revival catalog.json API (6h TTL, disk cache, stale fallback) |
444 |
PackageInstallService |
Package analysis, dependency resolution, multi-phase install pipeline | 515 |
PackageOverrideService |
Catalog ID lookup by PFN/name, embedded + remote override merging | 183 |
SftpService |
SSH.NET SFTP connection + low-level ops for the File Explorer | 694 |
SftpTransferService |
High-level transfers: upload file/folder/mixed/ZIP-extract, download, progress + cancel | 789 |
PortalAppFilesService |
WDP file API (portal) for the File Explorer: list/upload/download/rename/delete | 449 |
XrayAgentService |
XRay TCP agent discovery + log streaming for the Inspector | 267 |
GitHubReleaseCheckerService |
Auto-update checker — compares installed version against latest GitHub release | 72 |
BackgroundTaskService |
Recurring background job runner + task center registry | 358 |
ConnectionMonitorService |
Periodic connectivity polling → notifications | 90 |
NotificationCenterService |
In-app notification aggregation + dismiss/action routing | 171 |
SettingsService |
Persists AppSettings to %APPDATA%/XBVault/settings.json |
103 |
CryptoService |
XOR + Base64 credential obfuscation | 47 |
CacheService |
In-memory catalog cache with expiry | 116 |
UsbDriveDetector |
Lists USB drives via WMI (System.Management) — Windows-only (#if WINDOWS_BUILD) |
218 |
PreFlightChecker |
Startup settings/cache integrity validation + corrupt-repair | 270 |
WindowSettingsService |
Persists window size/position | 43 |
UpdateVersionCache |
Update-availability memoization | 83 |
InspectorConsoleColorizer |
Log/console colorization for the Inspector | 37 |
PlatformDialog |
Platform-aware file dialogs (WPF/Avalonia interop) | 135 |
Logger |
File + console logging (AttachConsole via DllImport — Windows-only) |
351 |
Verified from
maincode analysis (Aug 2026). Line counts approximate.
Xbox Service Split
The former XboxDeviceService god class (1,433 lines, ~41 public members, complexity 205) was split into focused services, each behind an interface:
| Service | Interface | Domains |
|---|---|---|
XboxAuthService |
IXboxAuthService |
Connection, credentials, SMB password, test |
XboxPackageService |
IXboxPackageService |
Install, uninstall, launch, suspend, terminate, list |
XboxProcessService |
IXboxProcessService |
List, kill, running title |
XboxNetworkService |
IXboxNetworkService |
Network config, WiFi |
XboxSystemService |
IXboxSystemService |
Info, crash dumps, screenshot, restart, shutdown |
XboxPerformanceService |
IXboxPerformanceService |
WebSocket performance stream |
Each takes XboxAuthService (shared connection) as its only constructor dependency. The split removed the god class; ViewModels now inject only the interfaces they need. See Refactor Proposal.
Key connection patterns (inherited from the original design):
- HTTP client recreation on
Configure— a freshHttpClientper connection works aroundBaseAddressimmutability. - Certificate validation bypass — self-signed console certificates; dev-only.
- CSRF token via
CookieContainer— token attached automatically to requests. - WebSocket for performance — real-time metrics stream, separate from the REST surface.
ViewModel → Service Dependency Map
graph LR
MVM[MainViewModel] --> Auth
BVM[BrowseViewModel] --> CAS
BVM --> CSvc
BVM --> Auth
BVM --> Pkg
BVM --> PS
BVM --> PO
IVM[InstalledViewModel] --> Auth
IVM --> Pkg
FVM[FileExplorerViewModel] --> Auth
FVM --> Sftp
FVM --> SftpT
FVM --> Portal
SVM[SettingsViewModel] --> Auth
SVM --> CSvc
TVM[ToolsViewModel] --> Auth
TVM --> Sys
CVM[ConnectionViewModel] --> Auth
CVM --> Net
NIVM[NetworkInfoViewModel] --> Net
PVM[ProcessesViewModel] --> Proc
SIVM[SystemInfoViewModel] --> Auth
SIVM --> Sys
CDVM[CrashDataViewModel] --> Auth
CDVM --> Sys
PerfVM[PerformanceViewModel] --> Auth
PerfVM --> Perf
RVM[RefreshViewModel] --> CAS
CIWM[CustomInstallViewModel] --> Pkg
CIWM --> PS
InVM[InspectorViewModel] --> Auth
InVM --> XRay
ShVM[ScreenshotViewModel] --> Sys
LbVM[LoopbackExemptViewModel] --> Auth
LbVM --> Sftp
LbVM --> Pkg
USBVM[UsbPermissionViewModel]
SWVM[SetupWizardViewModel] --> Auth
TkVM[TaskCenterViewModel] --> BgT
Auth[XboxAuthService]
Pkg[XboxPackageService]
Proc[XboxProcessService]
Net[XboxNetworkService]
Sys[XboxSystemService]
Perf[XboxPerformanceService]
CAS[CatalogApiService]
CSvc[CacheService]
PS[PackageInstallService]
Sftp[SftpService]
SftpT[SftpTransferService]
Portal[PortalAppFilesService]
XRay[XrayAgentService]
PO[PackageOverrideService]
BgT[BackgroundTaskService]
| ViewModel | Window/View | Key services |
|---|---|---|
BrowseViewModel |
BrowseView | CatalogApiService, CacheService, XboxAuthService, XboxPackageService, PackageInstallService, PackageOverrideService |
InstalledViewModel |
InstalledView | XboxAuthService, XboxPackageService |
FileExplorerViewModel |
FileExplorerView | XboxAuthService, SftpService, SftpTransferService, PortalAppFilesService |
ToolsViewModel |
ToolsView | XboxAuthService, XboxSystemService |
InspectorViewModel |
InspectorView | XboxAuthService, XrayAgentService |
PerformanceViewModel |
PerformanceWindow | XboxAuthService, XboxPerformanceService |
ScreenshotViewModel |
ScreenshotWindow | XboxSystemService |
SettingsViewModel |
SettingsView | XboxAuthService, CacheService |
UsbPermissionViewModel |
UsbPermissionWindow | none (WMI directly) |
SetupWizardViewModel |
SetupWizardWindow | XboxAuthService |
TaskCenterViewModel |
TasksPanel | BackgroundTaskService |
ConnectionViewModel |
ConnectionWindow | XboxAuthService, XboxNetworkService |
NetworkInfoViewModel |
NetworkInfoWindow | XboxNetworkService |
ProcessesViewModel |
ProcessesWindow | XboxProcessService |
SystemInfoViewModel |
SystemInfoWindow | XboxAuthService, XboxSystemService |
CrashDataViewModel |
CrashDataWindow | XboxAuthService, XboxSystemService |
DI pattern: manual composition in
App.axaml.cs(no DI container). Services constructed once, shared across VMs; dialog VMs constructed per-open with the services they need.
MVVM Patterns & Conventions
The app uses CommunityToolkit.Mvvm source generators throughout.
Observable properties & commands:
[ObservableProperty]
private string? selectedItem; // generates SelectedItem + change notification
[RelayCommand]
private async Task BrowseItemAsync() { } // generates BrowseItemCommand (IAsyncRelayCommand)
ViewModel lifecycle: constructor injection of services → synchronous setup → async initialization fired from the View (e.g. Loaded) via a [RelayCommand].
public BrowseViewModel(PackageInstallService install, IXboxAuthService auth,
IXboxPackageService packages, CatalogApiService catalog, PackageOverrideService overrides)
{
_install = install;
_auth = auth;
_packages = packages;
_catalog = catalog;
_overrides = overrides;
}
[RelayCommand]
private async Task LoadCatalogAsync()
{
var items = await _catalog.FetchCatalogAsync();
Items.Clear();
Items.AddRange(items); // observable update — must run on UI thread
}
Async threading convention:
- Service layer should use
.ConfigureAwait(false)(no UI affinity required). - ViewModel layer intentionally omits
ConfigureAwait— observable updates must stay on the UI thread.
The codebase does not yet apply
ConfigureAwait(false)in services — tracked in Tech Debt.
Navigation
flowchart TD
MW[MainWindow] --> SB[Sidebar ListBox]
SB -->|SelectedTab=0| BV[BrowseView]
SB -->|SelectedTab=1| IV[InstalledView]
SB -->|SelectedTab=2| FV[FileExplorerView]
SB -->|SelectedTab=3| TV[ToolsView]
SB -->|SelectedTab=4| InV[InspectorView]
SB -->|SelectedTab=5| SV[SettingsView]
SB -->|SelectedTab=6| LV[LogsView]
MW -->|Dialogs| Dialogs
MW -->|Panels| Panels
subgraph Dialogs["Dialog Windows"]
SetupW[SetupWizardWindow]
CW[ConnectionWindow]
NIW[NetworkInfoWindow]
PW[ProcessesWindow]
SIW[SystemInfoWindow]
CDW[CrashDataWindow]
PerfW[PerformanceWindow]
CustW[CustomInstallWindow]
ConfW[ConfirmWindow]
RD[RefreshWindow]
ED[ErrorDialog]
AW[AboutWindow]
SS[ScreenshotWindow]
ItemW[ItemDetailWindow]
UsbW[UsbPermissionWindow]
SftpW[SftpInfoWindow]
InD[InputDialog]
DiscD[DiscordPopup]
LbW[LoopbackExemptWindow]
DelW[DeleteConfirmWindow]
end
subgraph Panels["In-Window Panels"]
NotifP[NotificationsPanel]
TasksP[TasksPanel]
end
Dialogs are opened via delegate actions wired in App.axaml.cs (e.g. ShowConnectAction, ShowConfirmAsync, ShowDetailAction).
Note:
FileExplorerView(tab 2) is a functional SSH/SFTP file explorer, powered bySftpService,SftpTransferService, andPortalAppFilesService. See SSH/SFTP & Path Handling.
Xbox WDP API Integration
The Xbox services communicate with the Xbox Developer Mode Device Portal:
Base URL: https://{xbox-ip}:11443 · Auth: HTTP Basic
| Endpoint | Method | Purpose | Service |
|---|---|---|---|
/api/os/info |
GET | Device info, connection test | XboxAuthService |
/api/app/packagemanager/packages |
GET | List installed packages | XboxPackageService |
/api/app/packagemanager/package |
POST | Install package | XboxPackageService |
/api/app/packagemanager/package |
DELETE | Uninstall package | XboxPackageService |
/api/taskmanager/app |
POST | Launch app by PackageRelativeId | XboxPackageService |
/api/taskmanager/app/state |
POST | Suspend/resume/terminate package | XboxPackageService |
/api/resourcemanager/processes |
GET | List running processes | XboxProcessService |
/api/taskmanager/process |
DELETE | Kill process by PID | XboxProcessService |
/ext/app/runningtitle |
GET | Get currently running title | XboxProcessService |
/api/app/debug/crashdump |
GET | List crash dumps | XboxSystemService |
/api/app/debug/crashdump/{filename} |
DELETE | Delete crash dump | XboxSystemService |
/api/app/debug/crashcontrol |
GET | Get crash dump settings | XboxSystemService |
/api/app/debug/crashcontrol |
POST | Enable/disable crash dumps | XboxSystemService |
/api/networking/networkconfig |
GET | Get network configuration | XboxNetworkService |
/api/wifi/interfaces |
GET | List WiFi interfaces | XboxNetworkService |
/api/wifi/networks?interface={guid} |
GET | List WiFi networks | XboxNetworkService |
/api/systeminfo |
GET | Get system information | XboxSystemService |
/ext/screenshot?download=true&hdr=false |
GET | Capture screenshot | XboxSystemService |
/api/control/restart |
POST | Restart Xbox | XboxSystemService |
/api/control/shutdown |
POST | Shutdown Xbox | XboxSystemService |
Catalog API
CatalogApiService fetches the Emulation Revival catalog from a single JSON endpoint:
https://emulationrevival.github.io/catalog.json
The JSON is parsed into CatalogItem models covering categories: Emulator, Frontend, GamePort, App, Experimental, Media, Utility. Results are cached by CacheService (6h TTL) with a persistent disk cache and stale-fallback on API failure.
Previously: the catalog was scraped from 7 individual HTML pages using HtmlAgilityPack. That approach was replaced when Emulation Revival published the
catalog.jsonAPI.
Performance WebSocket
XboxPerformanceService connects to a WebSocket endpoint for real-time performance:
wss://{xbox-ip}:11443/api/resourcemanager/processes
Receives JSON frames with PerformanceSnapshot data (CPU, memory, GPU clock, temperature per core). Rendered by PerformanceViewModel.
Settings Persistence
SettingsService reads/writes %APPDATA%/XBVault/settings.json. Passwords obfuscated by CryptoService (salt + XOR + Base64) — not encryption, just obfuscation to avoid plaintext in JSON.
USB Permission Wizard
UsbPermissionViewModel + UsbDriveDetector implement a Windows-only wizard that:
- Lists USB drives via WMI (
System.Management) - Grants
ALL APPLICATION PACKAGESNTFS permissions viaicacls - Includes a spinner, 1-second minimum delay, and skips protected system directories
This allows Xbox Dev Mode to read ROM/media files from USB drives.
Loopback Exempt Wizard
LoopbackExemptViewModel + LoopbackExemptWindow (opened from Tools, in both full and quick mode) automate the X-Files package loopback-exempt workflow over SFTP/package services.
Window Pattern
All dialog windows share a common template:
WindowDecorations="None"— no OS chromeBackground="{StaticResource SurfaceBrush}"— dark gray#1A1D23- Root
<Border>withBorderBrush="#447F3E" BorderThickness="2" Margin="1"— green border + 1px gap - Title bar:
LinearGradientBrushfrom#447F3E→#9ACA3C - Close button: transparent default,
#CC3333on hover - Content area: 20px padding
- Drag via
PointerPressed="OnTitleBarPointerPressed"+BeginMoveDrag()
See Window Template for the full AXAML template.
Design Decisions & Rationale
Key architectural decisions and the reasoning behind them:
| # | Decision | Why |
|---|---|---|
| 1 | Multi-phase package install | The Xbox package manager blocks while processing and requires all files present before the install request; pre-analysis avoids redundant uploads and enables progress reporting. |
| 2 | Case-insensitive dependency folders | Creators use Dependencies/, deps/, or dep/ — detected via an OrdinalIgnoreCase set. |
| 3 | Task.Run wrapper for SFTP |
SSH.NET is synchronous; wrapping in Task.Run keeps the UI thread responsive during connect/transfer. |
| 4 | WebSocket for performance metrics | Real-time samples (10+/sec) — server push is more efficient than HTTP polling and matches what WDP exposes. |
| 5 | 6-hour catalog TTL + stale fallback | The catalog changes infrequently; stale data beats no data during brief Emulation Revival downtime and enables offline browsing. |
| 6 | Obfuscation, not encryption, for settings | Any key would be hard-coded in the assembly; XOR+Base64 only prevents casual plaintext inspection — acceptable for a dev tool. |
| 7 | Manual service composition (no DI container) | Transparent and easy to follow at this size; would adopt Microsoft.Extensions.DependencyInjection if the service count grows substantially. |
| 8 | Shared window template | WindowDecorations="None" + green border + gradient title bar + BeginMoveDrag() for consistent Blades styling. See Window Template. |
| 9 | Single credential reuse | The same Xbox credentials drive HTTP Basic (WDP), SFTP (SSH.NET), and SMB (USB folders) — one credential simplifies the UX. |
| 10 | Split XboxDeviceService into focused services |
The 1,433-line god class mixed 8 unrelated domains; per-domain services + interfaces make each testable in isolation and let VMs depend only on what they use. |
CI / Build
CI runs on every push and PR via GitHub Actions:
| Job | Runs on | Steps |
|---|---|---|
build |
Windows + Ubuntu (matrix) | restore → build Release → test |
release |
Windows + Ubuntu + macOS (tag push only) | publish win-x64, win-arm64, linux-x64, linux-arm64, osx-x64, osx-arm64 → ZIP → GitHub Release |
Release artifacts: XBVault-{version}-win-x64.zip, XBVault-{version}-win-arm64.zip, XBVault-{version}-linux-x64.zip, XBVault-{version}-linux-arm64.zip, XBVault-{version}-osx-x64.zip, XBVault-{version}-osx-arm64.zip (all self-contained, no client runtime required).