Desktop Applications
Maturity: Experimental · verified against Stacks source
bf1245e3· stable targets: 0
Stacks.js includes a Craft-based native-window integration. The audited source can:
- resolve a Craft executable from
CRAFT_BIN, a recognized local checkout, orPATH; - construct a native development-window command for a Stacks URL;
- pass window, hot-reload, developer-tools, dark-mode, and system-tray flags;
- compile a Stacks launcher and bundle it with a Craft runtime;
- emit a
desktop.jsonmanifest containing the application URL and window defaults. - emit exact source/runtime provenance and per-file SHA-256 checksums;
- require Ed25519-signed update manifests before checksum-verified staging;
- refuse a stable release channel unless the target's support row has complete native evidence and enforced signing/notarization.
The machine-readable matrix explicitly lists macOS arm64/x64, Linux x64, and Windows x64 as experimental unpackaged bundles. It establishes no stable OS version, installer, or mobile target. Platform identities and retained native install/update/rollback evidence are tracked in Stacks #2062 and #2063.
Prerequisites
You need a working Craft binary. Set its absolute path when it is not available as craft on PATH:
export CRAFT_BIN=/absolute/path/to/craft
The build path also requires APP_URL or DESKTOP_URL, because the current desktop bundle opens a deployed or locally served Stacks URL.
Development window
buddy dev:desktop
Programmatic use:
import { openDevWindow } from '@stacksjs/desktop'
await openDevWindow(3000, {
url: 'https://stacks.test',
title: 'My App',
width: 1280,
height: 800,
hotReload: true,
devTools: true,
systemTray: false,
})
openDevWindow() launches the Craft process and returns true after dispatching it. That return value does not prove that the native window remained healthy; development tooling should still surface the child process output.
Command construction
For diagnostics or custom launchers:
import {
craftDevCommand,
resolveCraftBinary,
resolveDevWindowUrl,
} from '@stacksjs/desktop'
const binary = resolveCraftBinary(process.env.CRAFT_BIN)
const url = resolveDevWindowUrl(3000, { url: process.env.APP_URL })
const command = craftDevCommand(3000, {
craftBin: binary,
url,
title: 'My App',
systemTray: true,
hideDockIcon: true,
})
The URL resolver validates the port and normalizes the configured URL. The command builder supports:
title,width, andheight;- hot reload and developer tools;
- dark mode;
- system tray, dock-icon hiding, and menubar-only mode;
- an explicit Craft binary.
Build output
APP_URL=https://app.example.com \
CRAFT_BIN=/absolute/path/to/craft \
buddy build:desktop
At the audited revision, the build Action:
- builds
@stacksjs/desktop; - compiles
desktop/src/launcher.tswith Bun; - copies the Craft runtime next to the launcher;
- writes
desktop.json; - writes
provenance.jsonandchecksums.sha256; - places output under
storage/framework/desktop-dist.
This is a runtime bundle, not evidence of a signed/notarized installer. Setting
DESKTOP_RELEASE_CHANNEL=stable fails while the target remains experimental.
Production checklist
Before distributing a desktop application:
- test on every supported OS and architecture;
- decide whether the remote URL model meets offline and update requirements;
- sign and notarize artifacts where the platform requires it;
- verify system-tray and dock behavior per OS;
- define automatic-update signing and rollback;
- restrict navigation and external URL handling;
- review the web/native trust boundary;
- publish checksums and a supported-version matrix.
Current boundary
The Stacks desktop package is useful for local native-window development and experimental Craft bundles. Treat native mobile delivery and universal desktop packaging as planned work until release evidence says otherwise.