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 boxprobes 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-remoteforce 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 startinstead of spawning its own. - One 16 GB GPU is a shared resource. Services marked
gpu: truein~/services.jsonon 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.