The thing you give a user is a standalone executable, not a script and not a copy of the bunmaska CLI. bunmaska build compiles your app - Bun runtime and your JavaScript fused into one native binary (bun build --compile) - and wraps it as a .app on macOS or an AppDir + .deb on Linux. They double-click it. That’s the whole interaction.
The CLI is for you, not them
bunmaska (init / dev / build / engine / doctor) is a developer tool. It is not bundled into your app and your users never install it. The runtime your app actually uses doesn’t import the CLI at all - they’re cleanly separated. So:
- You run
bunmaska buildto produce the executable. - Your user runs the executable. No terminal, no
npm, nobunmaska, no “first install Bun.”
This is the normal native-app deal: you ship a binary, they run a binary.
What “build” produces
bunmaska build # the host platform's distributable
bunmaska build --target linux # cross-build a Linux AppDir + .deb from macOS
bunmaska build --sign … --dmg # macOS: code-sign + a .dmg disk image
bunmaska build --update # also emit the auto-update feed (update.json + .tar.zst)
- macOS - a
.appbundle (icon converted from your PNG), optional code-signing/notarization, and an optional.dmg. - Linux - an AppDir, a
.tar.gz, and a.deb(which now declares its WebKitGTK + GTK dependencies, so a cleanapt installpulls them in).
No Chromium is bundled either way - the app renders on the system WebKit.
How a pinned engine reaches a user’s machine
If your app uses the default (the system WebKit), there is nothing to deliver - the executable just runs. This is the right answer for almost every app, and it’s a clean standalone binary today.
If your app pins a specific WebKit (the tested == shipped tier), the engine has to get onto the user’s machine somehow - and crucially, the user never runs bunmaska engine install for that. engine install is a developer command. Delivery to an end user is the app’s own job, three ways:
- Embed it in the bundle (
--embed-engine <dir>) - the engine rides inside the distributable. Built for Windows today (a Windows app needs it - there is no system WebKit); on Linux the same flag is the escape hatch when you can’t rely on the distro’s WebKitGTK. - Fetch on first run - the app’s runtime downloads its pinned engine (signature-verified) into the store the first time it launches. Designed; not built yet.
- Fall back to the system WebKit - if the pinned engine isn’t present, the app launches anyway on the system WebKit and says so on stderr. This is today’s behavior, and it means the app always starts.
So the honest state: shipping a clean standalone executable on the system WebKit works now. The pinned-engine delivery (embed / auto-fetch) is the in-progress piece - the runtime’s responsibility, never the user’s.
Rule of thumb:
bunmaska engine …is something you type. Your users never see it.