Back to Blog

xsubi Architecture: Blazor, FastAPI, Bare Metal

xsubi's architecture follows a three-layer model: browser, portal, and host. Each layer has a clear responsibility, a defined API surface, and no unnecessary coupling to the others. This post walks through the full stack.

Layer 1: Browser. The customer-facing interface is a Blazor Server application running on .NET 9. Blazor was chosen over React/Vue/Angular for a specific reason — the customer portal (portal.xsubi.com) is also Blazor, and sharing the same framework means shared authentication, shared components, and a single language (C#) across the entire frontend. The marketing site (xsubi.com) and portal share Identity via DataProtection keys scoped to .xsubi.com.

Layer 2: Portal. The Blazor portal handles VM lifecycle management — create, start, stop, resize, delete. It doesn't talk to hypervisors directly. Instead, it dispatches commands to FastAPI host agents running on each physical server. This decoupling means the portal doesn't need to know whether a VM is KVM or Hyper-V — the agent abstracts that.

Layer 3: Host Agents. Each hypervisor runs a FastAPI service (Python) that translates portal commands into hypervisor-specific operations. On Linux hosts, this means libvirt API calls for KVM. On Windows hosts, it means PowerShell Hyper-V cmdlets executed via subprocess. The agent handles provisioning, disk management, networking, and status reporting.

Security model. The portal authenticates users via ASP.NET Identity. Portal-to-agent communication will use mTLS (mutual TLS) — both sides verify certificates. Agents only accept connections from known portal instances. No agent endpoint is exposed to the public internet.

Why this works. The three-layer model means each component can be developed, tested, and deployed independently. The portal doesn't care how VMs are provisioned. The agents don't care about user authentication. The browser doesn't care about hypervisor topology. Each layer does one thing well.

An unhandled error has occurred. Reload 🗙