glyx.config.ts
The single configuration file for your Glyx app. Lives at the project root and is executed by the CLI at build/dev time to produce a resolved JSON config.
import { defineConfig } from '@glyx-dev/config'
export default defineConfig({
// ... all fields
})defineConfig prints the resolved config as JSON to stdout when the file is executed. The CLI reads this output — do not add side effects to glyx.config.ts.
name
name?: stringMachine-readable app identifier. No spaces — use hyphens (e.g. 'my-notes').
Used by glyx build and glyx package as:
- The output binary filename (
my-notes.exe,my-notes.app) - The installer slug (
my-notes-1.0.0-Setup.exe) - The macOS bundle ID prefix (
com.glyx.my-notes)
This is the canonical name source. When name is set here, Glyx ignores Cargo.toml and package.json names for packaging purposes. If omitted, the CLI falls back to Cargo.toml name (native projects) or package.json name (JS-only).
name is intentionally separate from window.title — name is the machine slug (no spaces), window.title is the human-readable string shown in the title bar and can be anything.
version
version?: stringApp version string, e.g. '1.2.0'. Exposed at runtime via updater.getVersion() and embedded in installer metadata.
engine
engine?: 'v8' | 'quickjs'JsRuntime backend. Mutually exclusive — a build links exactly one; picking one drops the other's dependency from the binary entirely, it's not a runtime toggle.
'v8'(default) — full-featured, larger binary (~58 MB floor on desktop). Supports the V8 snapshot fast-start path (glyx build --mode snapshot).'quickjs'— no JIT, ~3x smaller binary (a "Hello World" build measures ~20 MB vs. ~60 MB for the same app on V8). No snapshot fast-start — QuickJS has no equivalent mechanism, so--mode snapshotstill runs but the snapshot step is a no-op for startup time. Better suited to size-constrained or mobile targets; noticeably slower than V8 for JS-heavy workloads (list-heavy UI reconciliation in particular).
export default defineConfig({
engine: 'quickjs',
})Read by glyx build/glyx dev/glyx runtime to select the --features v8 or --features quickjs build of glyx-runner (or, for native Rust projects, of your own crate). Omit to use the default ('v8').
app
Installer and store metadata. Used by glyx package when generating NSIS (Windows), DMG (macOS), and AppImage (Linux) installers.
app?: {
publisher?: string // Company or author name shown in installer and OS app listings
description?: string // Short description of the app
website?: string // App website URL, e.g. 'https://myapp.com'
license?: string // Path to a license file, e.g. 'LICENSE.txt' — copied into the install directory
}icon
icon?: stringPath to a PNG icon (512×512 or 1024×1024 recommended). Used as the window icon and in installers.
locales
locales?: string[]ICU locale data to bundle. Controls which locales Intl.* and toLocaleString() format correctly — numbers, dates, times, currency, and plurals.
Defaults to ['en'] if omitted. Declare every locale your UI uses, and Glyx trims the bundled icudtl.dat down to just those locales at build time, keeping packaged apps light:
locales: ['en', 'de', 'ja'],Locales you don't list still work, but they fall back to the root/English formatting data rather than their own locale-specific rules. Add a locale and rebuild to pick it up.
Locale data is trimmed during glyx build / glyx dev, not embedded in full. See the ICU data integration guide for how trimming works and how to verify the resulting icudtl.dat.
window
Controls the initial window appearance and rendering backend.
window?: {
title?: string
width?: number // Default: 1280
height?: number // Default: 800
startupMode?: 'windowed' | 'maximized' | 'fullscreen'
decorations?: boolean // OS title bar (default: true). false = frameless window
resizable?: boolean // User can resize the window (default: true). false = fixed size
background?: string // GPU clear color before first JS frame: '#rrggbb' or '#rrggbbaa'
renderMode?: 'auto' | 'gpu' | 'cpu' | 'skia' | 'direct2d' // Rendering backend (default: 'auto')
maxJsHeapMb?: number // V8 heap cap in MB, 24–512 (default: auto — see below)
preventDuplicateWindows?: boolean // glyxWindow.create with an open title focuses it instead (default: false)
}window.background
The GPU clear color shown before the first React frame renders. Match this to your app's root background color to eliminate the white flash on startup.
window: {
background: '#1e1e2e', // matches dark root background
}window.decorations
Set to false for a frameless window with a custom title bar.
window: {
decorations: false,
}window.resizable
Set to false to lock the window at its configured width × height — no drag-to-resize, and maximize is disabled. Useful for fixed-layout tools like a calculator or a compact utility panel.
window: {
width: 320,
height: 480,
resizable: false,
}window.renderMode
Controls the rendering backend:
| Value | Description |
|---|---|
'auto' | Pick per machine (default): TinySkia on integrated/no GPU, Vello on discrete GPUs |
'skia' | TinySkia CPU rasterizer + OS software present — no wgpu at all, ~35 MB RSS |
'gpu' | Vello GPU compute via wgpu (best visual quality for heavy 2D scenes, requires DX12/Metal/Vulkan) |
'direct2d' | Windows-only, experimental. OS/driver-managed Direct2D — GPU-accelerated output with TinySkia-like flat memory behavior, never selected by 'auto'. Falls back to 'skia' with a warning on non-Windows. See Rendering Backends. |
'cpu' | Vello's CPU fallback path — not recommended. It inherits Vello's scene-buffer memory cost without the GPU throughput that cost is meant to buy, landing worse on both memory and speed than 'skia'. Only useful where Vello's feature set is required but no GPU is available. |
TinySkia can also be forced at runtime via the GLYX_CPU_RENDER=1 environment variable. Useful for CI, testing, or devices without a supported GPU. Canvas3D upgrades a TinySkia window to the wgpu present path automatically on first use (and releases it again after 60 s of 3D inactivity); Direct2D windows don't yet support this upgrade — Canvas3D content is skipped there (see the Direct2D section below).
See Rendering Backends for the full memory story.
window.maxJsHeapMb
Controls the maximum V8 JavaScript heap size. When omitted, Glyx auto-calculates based on the compiled bundle size:
| Mode | Formula | Floor |
|---|---|---|
| Production | bundle_size_mb × 12 | 24 MB |
Dev (glyx dev) | same formula | 32 MB (higher floor for HMR churn) |
The cap is always bounded to 512 MB maximum. Use an explicit value when the auto floor is too tight (e.g. a large dependency graph at small bundle size) or too generous (memory-constrained targets).
window: {
maxJsHeapMb: 64, // explicit cap — overrides auto-calculation
}The cap prevents V8 from speculatively reserving the OS default (~1.5 GB). It does not pre-allocate that memory — V8 starts at 2 MB and grows on demand up to the cap.
window.preventDuplicateWindows
When true, glyxWindow.create({ title }) for a title that is already open focuses the existing window (restoring it if minimized) and resolves with its handle, instead of opening a twin. Child windows with distinct titles are never affected; per-call allowDuplicate: true or an explicit key give fine-grained control. See the window API.
window: {
preventDuplicateWindows: true,
}capabilities
Declares which system APIs your app can access. All capabilities are opt-in — undeclared APIs throw at runtime.
capabilities?: {
// File system
fs?: { read?: string[]; write?: string[]; delete?: string[] }
// Network
network?: { allow?: string[] } // origin allowlist
// Environment variables
env?: { allow?: string[] } // variable name allowlist
// Deep linking
deeplink?: { scheme: string; singleInstance?: boolean }
// Simple toggles
db?: boolean // SQLite
dialog?: boolean // file/folder picker dialogs
clipboard?: boolean // read/write clipboard
notification?: boolean // desktop notifications
tray?: boolean // system tray icons
battery?: boolean // battery status
usb?: boolean // USB device enumeration
shell?: boolean // open external URLs via openExternal (shell.exec planned)
mdns?: boolean // mDNS service discovery
system?: boolean // OS info, displays, memory
power?: boolean // sleep/wake events
storage?: boolean // app data directory access
gamepads?: boolean // gamepad/controller input
globalShortcuts?: boolean // OS-level keyboard shortcuts
credentials?: boolean // OS keychain (DPAPI/Keychain/Secret Service)
audio?: boolean // audio playback
video?: boolean // video playback
camera?: boolean // camera capture
microphone?: boolean // microphone recording
ai?: boolean // local AI (embed, generate, transcribe)
hid?: boolean // HID device access
updater?: boolean // auto-updater
crash?: boolean // crash reporting
}File system scoping
Paths use glob patterns relative to the app's working directory:
capabilities: {
fs: {
read: ['**'], // read anywhere
write: ['documents/**'], // write only under documents/
delete: ['documents/trash/**'], // delete only under trash/
},
}delete is optional. When omitted it falls back to the write globs. Set it to [] to allow writes but deny all deletion.
Deep linking
capabilities: {
deeplink: {
scheme: 'myapp', // handles myapp:// URLs
singleInstance: true, // focus existing window instead of opening a new one
},
}dev
Build settings used by glyx dev and glyx build.
dev?: {
entry?: string // JS entry point (default: 'src/app.jsx')
output?: string // Compiled bundle path (default: 'dist/app.js')
watch?: string[] // Directories to watch for HMR (default: ['src'])
}The CLI bundles entry → output on file changes. The output file is loaded by the Glyx runtime at startup.
dist/ is gitignored by default in new projects. Commit your source but not the compiled bundle. Native projects use ui/app.jsx as the entry — override dev.entry accordingly.
splash
Optional splash screen shown during JS startup.
splash?: {
image?: string // Path to a PNG image, centered on the splash background
background?: string // Hex color, e.g. '#1e1e2e'. Defaults to black
minimumMs?: number // Minimum display time before hideSplash() takes effect
imageScale?: number // Max fraction of the smaller window dimension `image` may fill (default 0.5)
}imageScale matters more than it looks. A full-bleed source image (an app
icon with no transparent margin, for example) will otherwise scale up to
fill nearly the entire window under the default fit-to-bounds behavior,
visually swallowing background — it'll read as "a giant icon crammed
into the window," not a small centered logo. The default (0.5) caps the
image at half the smaller window dimension regardless of the source
image's own shape. Raise it toward 1.0 only for an image intentionally
designed as a full splash background.
Call glyxWindow.hideSplash() from JS when your app is ready:
import { glyxWindow } from '@glyx-dev/react'
// After your data loads:
glyxWindow.hideSplash()A 30-second safety timeout auto-hides the splash if hideSplash() is never called.
updater
Target GitHub repo for the auto-updater's updater.check()/updater.update().
Requires capabilities.updater: true too — this block is app metadata (where
to check), the capability flag is the permission gate (whether checking is
allowed at all).
updater?: {
owner: string // GitHub organization or username
repo: string // Repository name
binName: string // Release asset filename prefix — matches `{binName}-{platform}`,
// exactly what `glyx package` produces
}{
"capabilities": { "updater": true },
"updater": {
"owner": "acme-inc",
"repo": "my-app",
"binName": "my-app"
}
}Read once at startup from glyx.config.json — not baked into the binary at
compile time. Without this block, updater.check()/updater.update()
reject with "Update origin not configured" regardless of the updater
capability. See the updater API reference for the
full signing/trust model.
plugins
JS plugin extensions. Each plugin's exported async functions become callable via backend.<name>.<fn>() from JS.
plugins?: Array<{
entry: string // Path to the plugin JS entry point (bundled at startup)
name?: string // Optional namespace prefix for the plugin's commands
capabilities?: string[] // Capabilities the plugin requires
}>plugins: [
{ entry: 'plugins/storage.js', name: 'storage' },
{ entry: 'plugins/analytics.js', name: 'analytics' },
]See JS Plugins for how to write a plugin.
packageManager
packageManager?: 'npm' | 'pnpm' | 'yarn' | 'bun'Override the package manager used by the CLI for installs, scripts, and bundling. When omitted, the CLI auto-detects from the lockfile. See Package manager support.
Full example
import { defineConfig } from '@glyx-dev/config'
export default defineConfig({
name: 'my-notes',
version: '1.0.0',
// engine: 'quickjs', // uncomment for a smaller, no-JIT build (default: 'v8')
app: {
publisher: 'Acme Corp',
description: 'A fast, native notes app.',
website: 'https://myapp.com',
license: 'LICENSE.txt',
},
icon: 'assets/icon.png',
locales: ['en'],
window: {
title: 'My Notes',
width: 1200,
height: 800,
startupMode: 'windowed',
background: '#171923',
decorations: true,
resizable: true,
renderMode: 'auto',
// maxJsHeapMb: 64, // uncomment to override the auto-calculated heap cap
},
capabilities: {
fs: { read: ['**'], write: ['documents/**'], delete: ['documents/trash/**'] },
network: { allow: ['https://api.myservice.com'] },
db: true,
dialog: true,
clipboard: true,
notification: true,
credentials: true,
crash: true,
deeplink: { scheme: 'mynotes', singleInstance: true },
},
dev: {
entry: 'src/app.jsx',
output: 'dist/app.js',
watch: ['src'],
},
splash: {
background: '#171923',
minimumMs: 800,
},
})