After publishing SheafGate: Building Desktop Apps with SvelteKit and Bun, Without Electron, I’ve been thinking about the questions people are likely to have when they first look at the architecture. Some of these are questions I asked myself during development. Some are the kind of thing a code reviewer would flag. All of them are worth answering properly.
…why Go for the launcher and not TypeScript?
You could write the launcher in TypeScript and run it on Bun. It would work, technically. But you’d be running the launcher and the app in the same process, which defeats the fundamental point — the launcher watching over the app is the whole architecture. The separation of concerns is the feature.
Beyond that, Bun’s event loop is excellent for the app itself but a process supervisor that needs to reliably monitor, signal, and potentially outlive a child process is a better fit for Go’s threading model. Goroutines make the heartbeat loop, the process monitor, and the HTTP client genuinely concurrent without async/await complexity.
Go also compiles to a single self-contained binary with no runtime dependency. The standard library has everything needed — os/exec for process management, crypto/rand for the password, net/http for the heartbeat — without pulling in external dependencies. The launcher is about 250 lines. It’s not complicated.
…why does it use an HTTP port at all?
Because SvelteKit is a web framework and HTTP is what it does. There’s no embedded webview, no native bridge, no stripped-down runtime. The finished app is just an ordinary web app, which means you get everything that implies: full browser devtools, standard web APIs, no framework-specific constraints on what you can build. The “desktop” part is the launcher opening your system browser pointed at localhost.
…why not Electron, Tauri, or Wails?
Those are all reasonable choices for the right project. The difference is that SheafGate makes no modifications to your SvelteKit app whatsoever. There’s no embedded webview to target, no Rust bridge to learn, no IPC layer to design around. If you already have a SvelteKit app and you want it to behave like a desktop app on machines you control, SheafGate adds that with a handful of files without touching your existing code.
…why not HTTPS?
The server only ever binds to 127.0.0.1 on a randomly chosen port. Traffic never leaves the machine, which makes interception genuinely difficult. The one-time password is sent in the clear on that first login request, yes — but only to localhost. Its job isn’t to protect against network eavesdropping, it’s to stop another process that happens to find the same random port from being able to authenticate. For a local desktop app that threat model is entirely adequate.
…why the shared UUID?
The UUID is baked into both the launcher and the app at compile time from the same generated value. This ties a specific launcher binary to a specific app build. If another process is sitting on the same port, it won’t know the UUID and the login fails immediately. It’s a pairing mechanism, not a secret.
…is the UUID secret?
Not necessarily. Its value isn’t in being secret — it’s in being shared only between the launcher and the app it was built with. You could treat it as an additional key if you wanted to, but that’s not its purpose. The real security comes from the combination of the random port, the one-time password, and the localhost-only binding working together.
…can I debug it like a normal web app?
Yes, and this is one of the better properties of the approach. Run the app in dev mode and the one-time password appears in your terminal output. Use that password along with the build UUID to hit the login endpoint directly, and from that point you have a normal authenticated browser session with full devtools available. Nothing about the auth mechanism interferes with the standard web debugging workflow.
…why the /sheafgate-control endpoint name?
It needed to be something unlikely to clash with routes in an existing application. A generic name like /auth or /login would conflict with apps that already have those routes. /sheafgate-control is specific enough to be collision-resistant without being obscure.
…why not compile it to a single binary?
That would break SvelteKit. The framework expects its build output to be a collection of files — JS chunks, assets, the server entry point — and collapsing that into a single binary would require either an embedded filesystem or a custom bundler that fights against how SvelteKit works. Keeping the Bun app as ordinary build output sitting alongside the launcher means nothing about the SvelteKit build process needs to change.
…is it safe to distribute?
Yes. The TypeScript source files are present in the distribution but minified, so casual inspection won’t reveal much. If you want stronger guarantees the Go launcher can be modified to verify checksums of the engine files before starting — that would detect tampering and refuse to launch if anything has been modified. That’s not in the demo but it’s a natural extension for anyone distributing to untrusted environments.
…how do I build it?
Set the engine URL in the Makefile variable. The UUID lives in env files that are committed as defaults — update them manually to match, or just delete them and they get recreated automatically when you build. Then from the launcher folder, in Git Bash:
bash
make dist
That generates a fresh UUID, passes it to both the Go and Bun builds, recreates the env files, and produces the distribution: a minified SvelteKit app in the build folder, the Bun runtime, and the Go launcher executable.
…why Git Bash on Windows?
The Makefile uses Unix shell syntax. Command Prompt and PowerShell both handle this badly. Git Bash gives you a proper Unix shell on Windows without needing WSL, and most developers working with Go and Bun on Windows will already have it installed.
The Makefile deliberately uses built-in shell commands rather than Linux-specific ones, so native Windows tooling could work in principle. In practice, getting make running natively on Windows requires setting up MSYS2 or similar, and that overhead isn’t worth it when Git Bash is already the right answer.
…what platforms does it run on?
Version 0.1 has been tested on Windows, which is actually where this pattern is most useful — Windows users have the fewest good options for running web apps as lightweight desktop apps without heavyweight frameworks. Linux support is implemented and coming soon. The code is there but not fully tested yet.
…is this a framework or a pattern?
Mostly a pattern. The launcher is ~250 lines of Go and the engine-side auth library is a small handful of TypeScript files — both are designed to be copied into your own project and adapted rather than installed as a dependency. You own the code, you can change it, and the protocol between the two sides is simple enough that you could implement the engine in any language. Java and Kotlin implementations are on the roadmap.
The demo repo is at github.com/robdeas/sheafgatedemo. If you have a question that isn’t answered here, drop it in the comments and I’ll add it to the list.
