<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://hpcc.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://hpcc.dev/" rel="alternate" type="text/html" /><updated>2026-05-23T21:32:22+00:00</updated><id>https://hpcc.dev/feed.xml</id><title type="html">hpcc</title><subtitle>A distributed compiler cache that a regulated enterprise&apos;s security team will actually approve. One Firecracker microVM per tenant. KVM boundary, no NIC, full audit trail.</subtitle><author><name>Afshin Arani</name></author><entry><title type="html">No SMB across the partition boundary</title><link href="https://hpcc.dev/blog/2026/05/17/no-smb-across-the-partition-boundary/" rel="alternate" type="text/html" title="No SMB across the partition boundary" /><published>2026-05-17T00:00:00+00:00</published><updated>2026-05-17T00:00:00+00:00</updated><id>https://hpcc.dev/blog/2026/05/17/no-smb-across-the-partition-boundary</id><content type="html" xml:base="https://hpcc.dev/blog/2026/05/17/no-smb-across-the-partition-boundary/"><![CDATA[<p>The Windows runtime landed last week. It’s the second isolation backend in the tree, sitting behind the same <code class="language-plaintext highlighter-rouge">Runtime</code> interface as the Linux/Firecracker side: containerd + runhcs, Hyper-V isolation for the security story, process isolation for CI hosts without nested virt. What’s worth a post is the one decision in it that took the most time to defend — and that I expect to be asked about repeatedly.</p>

<h2 id="the-obvious-path-is-vsmb">The obvious path is VSMB</h2>

<p>If you want to share a host directory into a Hyper-V isolated container, the documented Microsoft path is <strong>VSMB</strong> — Virtual SMB. The host runs an SMB server, the utility VM mounts it across the partition boundary, your container sees a normal filesystem. It’s what every Windows-container tutorial reaches for. It works.</p>

<p>It’s also the same protocol family that absorbed EternalBlue, and that has produced a steady drip of kernel-mode RCE CVEs for two decades. The reason it keeps producing them isn’t an indictment of the SMB maintainers — it’s that SMB is an enormous parser surface (auth, dialect negotiation, oplocks, leases, named pipes, DFS) running inside a kernel that has to honor wire-format compatibility back to the 1990s.</p>

<p>hpcc’s entire pitch is that the kernel+VM boundary is a line a security review actually recognises. Stapling an SMB parser across that line gives the threat model back. The auditor question stops being <em>“is this isolated?”</em> and becomes <em>“what’s your patch cadence for the SMB stack inside the utility VM?”</em> — which is not a question anyone wants to answer in a regulated environment.</p>

<h2 id="what-the-linux-side-does-mirrored">What the Linux side does, mirrored</h2>

<p>On Linux, the host↔guest channel is one vsock device carrying a single bidirectional gRPC stream — <code class="language-plaintext highlighter-rouge">AgentService.Exec</code>. Header + input file chunks in, stdio + result + output file chunks back. The wire protocol is one we own. There is no filesystem.</p>

<p>The Windows port is the same shape:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">hpcc-agent.exe</code> is staged into the container at <code class="language-plaintext highlighter-rouge">C:\.hpcc</code> and set as PID 1.</li>
  <li>The agent listens on <strong>HvSocket</strong> — Hyper-V’s analogue of vsock, a socket family that crosses the partition boundary without involving any filesystem protocol.</li>
  <li>After <code class="language-plaintext highlighter-rouge">Task.Start</code>, the worker resolves the utility VM’s GUID from <code class="language-plaintext highlighter-rouge">hcsshim.GetContainers</code> and dials the agent over HvSocket.</li>
  <li>The same <code class="language-plaintext highlighter-rouge">AgentService.Exec</code> bidi stream carries the compile.</li>
</ul>

<p>No SMB anywhere. No share. No mount across the partition boundary. The attack surface on the boundary is the gRPC parser plus our protobuf schema — code we wrote, on a wire format we control, with one function that handles one request type.</p>

<h2 id="the-boring-parts-that-took-the-most-time">The boring parts that took the most time</h2>

<p>The interesting decision was easy. The work was elsewhere:</p>

<ul>
  <li><strong>Path normalization.</strong> Windows hands you paths with <code class="language-plaintext highlighter-rouge">\\?\</code> prefixes for long-path support, UNC paths for network locations, mixed casing from case-insensitive filesystems, and short (8.3) names from legacy APIs. We strip <code class="language-plaintext highlighter-rouge">\\?\</code>, reject UNC up front (no remote sources, ever), and case-fold inside the cache digest so a checkout under <code class="language-plaintext highlighter-rouge">C:\Users\Afshin\src\…</code> and one under <code class="language-plaintext highlighter-rouge">C:\users\afshin\src\…</code> hash identically.</li>
  <li><strong>Reproducibility flags for MSVC.</strong> GCC has <code class="language-plaintext highlighter-rouge">-ffile-prefix-map=/src=.</code> and <code class="language-plaintext highlighter-rouge">-Werror=date-time</code>. MSVC has <code class="language-plaintext highlighter-rouge">/d1trimfile:/src</code> and <code class="language-plaintext highlighter-rouge">/PDBSourcePath:/src</code>. Both families are auto-injected per-detected-compiler so embedded paths in <code class="language-plaintext highlighter-rouge">.obj</code> and <code class="language-plaintext highlighter-rouge">.pdb</code> files don’t pin a job’s hash to its staging directory.</li>
  <li><strong>Process isolation as a peer, not a fallback.</strong> CI runners without nested virt still need to run the tests. Process isolation keeps <code class="language-plaintext highlighter-rouge">pause</code> + <code class="language-plaintext highlighter-rouge">Task.Exec</code> + <code class="language-plaintext highlighter-rouge">copyTree</code> behind the same <code class="language-plaintext highlighter-rouge">Runtime</code> interface — same Exec stream shape, weaker boundary, honestly labeled in the audit row.</li>
</ul>

<h2 id="whats-next">What’s next</h2>

<p>The Windows side is feature-complete behind the <code class="language-plaintext highlighter-rouge">Runtime</code> interface, but there are two open items on the phase 4 list that touch it:</p>

<ul>
  <li><strong>VM-crash reaping with scheduler reroute.</strong> The runtime surfaces process exit cleanly; the worker doesn’t yet notify the scheduler to drop a dead VM so a follow-on dial doesn’t race.</li>
  <li><strong>Toolchain parity check.</strong> An operator currently pins the image’s compiler patch version against the host by hand. An automatic probe — host gcc/cl version reported back to the scheduler at session start, compared against the image’s declared version — is the last cross-developer-hit-rate hazard on Windows.</li>
</ul>

<p>Neither is a security item. The boundary story is done.</p>]]></content><author><name>Afshin Arani</name></author><summary type="html"><![CDATA[Why hpcc's Windows path runs an agent over HvSocket instead of taking Microsoft's default VSMB share into a Hyper-V isolated container.]]></summary></entry></feed>