Himansh Raj

Box Suite: Controlling My GPU Box from a Mac

· 3 min read

Box Suite: Controlling My GPU Box from a Mac

I do most of my work on a Mac, but the GPU lives elsewhere — an Ubuntu box with an i9-14900K and an RTX 5060 Ti. Box Suite is the pair of tools I built to control that box from my laptop without ceremony: boxctl, a Python CLI, and BoxDeck, a macOS menu-bar app. Nothing is hardcoded — you point them at your own machine with a small config file.

Two tools, one source of truth

  • boxctl — the CLI. It handles auth (Touch ID / TOTP), LAN↔remote routing, SSH tunnels, health checks, and launching VS Code Remote-SSH on the box.
  • BoxDeck — the macOS app. A menu-bar live telemetry chart, service toggles, and a remote file browser.

The important relationship: BoxDeck drives boxctl. One tunnel keeper, one source of truth. The app doesn't reimplement the connection logic — it delegates to the CLI.

Day-to-day it comes down to a few commands:

boxctl status      # route, auth, key hours, tunnels, serve health, GPU
boxctl doctor      # diagnose + auto-fix
boxctl connect     # renew auth (Touch ID; TOTP fallback)
boxctl code [path] # VS Code Remote-SSH on the box

The non-obvious bits

Most of what I learned building this was in the details that only show up once you actually live with the tool:

  • Two routes, chosen automatically. ssh box probes the LAN first via an mDNS name — so DHCP can move the box without breaking anything — and falls back to the cloudflared domain. box-lan / box-remote force one when you need to.
  • Auth is layered on purpose. A silent 24-hour session key does the day-to-day work, and a Secure Enclave key (Touch ID) renews it. The subtle lesson: order matters. Listing the passkey first caused a biometric prompt on every background reconnect, so the layering had to be deliberate.
  • One tunnel keeper, always. Two keepers means two ssh loops, which means a Touch ID popup storm. That's why BoxDeck delegates to boxctl tunnel start instead of spawning its own.
  • One 16 GB GPU is a shared resource. Services marked gpu: true in ~/services.json on the box stop each other automatically, so two models never fight over the card.

A UI detail worth its own note

The menu-bar chart turned out to be the fiddliest part of BoxDeck. I ended up drawing it as an NSView, not an NSImage — because MenuBarExtra labels can't render a SwiftUI Canvas, and the NSImage + lockFocus route draws at 1× and looks fuzzy on Retina displays. It's a tiny thing, but it's the difference between the chart looking native and looking off.

What I took away

  • Routing and auth are where remote tooling gets hard. The happy path is easy; the failure modes — a moved IP, a background reconnect, a stray second tunnel — are what actually needed design.
  • Let one tool own the truth. Having BoxDeck delegate to boxctl instead of duplicating logic kept the whole thing coherent and stopped the popup storms.

Requirements are modest: macOS 13+, cloudflared, Python 3.12+ (via uv), and optionally Secretive for the Touch ID key.

If you want to look at the code, here it is: boxctl-deck on GitHub.