Glaze
1 Quick Start
2 Application Lifecycle
run-app
make-api-token
current-api-token
glaze-app?
current-app
app-state
app-id
app-quit!
open-app-window
3 Local Server
start-server
stop-server
open-browser
4 Java  Script Bridge
GET
POST
PUT
DELETE
5 Runtime Capabilities
make-capability
allow-permission
scoped-permission
path-permission
url-permission
command-permission
command-resource
accelerator-permission
capability-authorized?
current-capability-id
current-capability-authorizer
current-capability-authorized?
6 Scoped Filesystem
make-filesystem-routes
fs-read-text
fs-write-text!
fs-read-bytes
fs-write-bytes!
fs-read-dir
fs-create-dir!
fs-remove!
fs-copy!
fs-move!
fs-stat
fs-exists?
7 Scoped Shell and Child Processes
make-shell-routes
shell-output
shell-spawn!
shell-process-id
shell-process-pid
shell-process-info
shell-process-write!
shell-process-close-input!
shell-process-kill!
shell-process-wait
8 Persistent JSON Store
make-store-routes
load-store
store-get
store-set!
store-has-key?
store-delete!
store-clear!
store-reset!
store-keys
store-values
store-entries
store-count
store-snapshot
store-save!
store-reload!
store-close!
9 Capability-Gated System Plugins
make-system-routes
system-information
system-hostname
10 Global Shortcuts
string->hotkey
hotkey->string
hotkey-register!
make-global-shortcut-routes
11 Structured Logging
make-glaze-logger
glaze-logger-history
make-file-log-sink
make-log-routes
12 Application and Resource Paths
make-path-resolver
make-path-routes
app-config-dir
app-data-dir
app-local-data-dir
app-cache-dir
app-log-dir
resource-dir
resolve-resource
path-join
path-resolve
path-normalize
path-basename
path-dirname
path-extname
path-absolute?
13 Scoped HTTP Client
http-request
make-http-routes
14 Scoped SQLite
open-sqlite-database
sql-select
sql-execute!
sql-close!
make-sql-routes
request-json-body
json-response
api-response
error-response
streaming-response
event-stream-response
define-api-routes
15 Event Push
make-event-bus
bus-broadcast!
bus-subscribe!
bus-unsubscribe!
bus-wait
bus-dropped-count
current-event-drop-reporter
16 GLZ1 Bridge Protocol
glz1-version
bridge-error-code?
raise-bridge-error
exn:  fail:  glaze:  bridge
bridge-cancel-event
bridge-cancelled?
16.1 Streaming responses
17 Layer Modules
18 Typed Command Declarations
grecord
genum
glist
goptional
gmap
validate-type
validate-jsexpr
schema-compat?
schema-compat-report
command-compatible?
19 Native Transport
start-native-transport
native-transport-message!
native-delivery
native-transport-set-deliver!
native-transport-stats
native-transport-count-resource!
native-transport-shutdown!
native-app-origin
native-scheme-segments
native-bootstrap-js
native-api-client-js
generate-api-declarations
20 Native Web  View
open-window
open-webview
webview-window-state
webview-set-window-state!
webview-save-state!
webview-restore-state!
webview-supported?
webview-last-error
webview-install-guidance
webview-diagnostic
webview-navigate
webview-close
webview-title
webview-url
webview-capture!
webview-set-title!
webview-set-size!
webview-set-fullscreen!
webview-focus!
webview-set-menu!
webview-closed?
all-webviews
close-all-webviews!
wait-for-webviews
20.1 Startup Dependency Feedback
21 System Integrations
sys-supported?
clipboard-set!
clipboard-get
notify!
open-path
reveal-path
single-instance?
22 System Tray
tray-supported?
make-tray
tray-set-tooltip!
tray-set-icon!
tray-set-menu!
tray-close
23 Security
24 Update Checks
check-update
newer-version?
verify-file-sha256
fetch-update-manifest
select-update
download-update
make-install-plan
make-replace-install-plan
execute-install-plan!
25 File Dialogs
dialog-supported?
pick-file
pick-files
pick-folder
save-file-dialog
dialog-message!
dialog-ask!
make-dialog-routes
26 Deep Links and Launch at Login
ensure-url-scheme!
auto-launch-set!
auto-launch-enabled?
27 Packaging
build-app
28 CLI Commands
9.3

Glaze🔗ℹ

turinglambdaai

Glaze builds desktop applications with a Racket backend and a Web frontend rendered inside a native OS window. Windows uses WebView2, macOS uses WKWebView, and Linux uses WebKitGTK.

Human-first. Agent-native. Local by design. New projects include an agent instruction contract and native-window verification script. The CLI exposes machine-readable project inspection and runtime diagnostics without changing Glaze’s human-readable workflow.

1 Quick Start🔗ℹ

$ raco pkg install --auto glaze

$ raco glaze init myapp

$ cd myapp

$ raco glaze inspect --json

$ raco glaze doctor --json

$ raco glaze verify

$ racket main.rkt

# or: raco glaze dev

A Glaze application is native-GUI only. If its native WebView cannot start, application startup fails with platform-specific installation or repair guidance. Glaze never substitutes a system-browser tab for the desktop window.

2 Application Lifecycle🔗ℹ

 (require glaze/app) package: glaze

procedure

(run-app [#:public-dir public-dir 
  #:api api 
  #:port port 
  #:title title 
  #:width width 
  #:height height 
  #:events events 
  #:api-token api-token 
  #:capability capability 
  #:max-body-size max-body-size 
  #:on-close on-close 
  #:on-error on-error 
  #:check-update check-update 
  #:current-version current-version 
  #:app-id app-id 
  #:window-state window-state 
  #:transport transport 
  #:on-ready on-ready]) 
 → 
'webview procedure?
  public-dir : (or/c string? path?) = "public"
  api : (listof route?) = '()
  port : (or/c #f exact-nonnegative-integer?) = #f
  title : string? = "Glaze"
  width : exact-positive-integer? = 1024
  height : exact-positive-integer? = 768
  events : (or/c #f event-bus?) = #f
  api-token : (or/c #f string? #t) = #t
  capability : (or/c #f capability?) = #f
  max-body-size : exact-nonnegative-integer? = 8388608
  on-close : (-> any) = (lambda () (void))
  on-error : (or/c #f procedure?) = #f
  check-update : (or/c #f string?) = #f
  current-version : string? = "0.0.0"
  app-id : (or/c #f string?) = #f
  window-state : (or/c #f #t path-string?) = #f
  transport : (or/c 'http 'native) = 'http
  on-ready : procedure? = (lambda (wv url) (void))
The one-call application entry point. It selects a free loopback port unless #:port is supplied, starts the static/API server, opens the native WebView window, invokes on-ready with the webview? handle and clean application URL, and blocks until the window closes.

When the native window closes, the local server is stopped and the procedure returns (values 'webview shutdown). If native WebView startup fails, Glaze first stops the local server and then propagates an actionable startup error. There is intentionally no browser-fallback option.

The bridge is token-protected by default: #:api-token defaults to #t, which generates a random capability token and uses a one-time bootstrap URL to set an HttpOnly cookie for the embedded frontend, so a loopback port is never an open API for other local processes or web pages. Pass an explicit string to choose the token, or #:api-token #f to opt out (dev tooling, trusted kiosk setups). #:max-body-size caps the request body the server will buffer (413 above the limit). #:on-error receives API handler failures. #:check-update wires an update manifest into the application lifecycle.

#:transport 'native serves the page and API entirely inside the WebView’s process boundary (Native Transport): the page loads from the controlled glaze://app/ origin, invokes ride the WebView’s message handler, no TCP port is bound, and no token is minted — the message handler is the trust boundary. #:port is an error in this mode. The default 'http transport keeps the loopback server and is the development vehicle.

The lifecycle is exception-safe: an #:on-ready callback that raises tears the window and the server down before propagating, and an #:on-close hook that raises is reported through #:on-error while the close still completes — a callback bug can neither leak the server nor wedge the application.

When #:capability is supplied, run-app automatically creates an API token if necessary. Every API route then requires a declared and granted permission; this mode is default-deny.

With #:window-state #t, #:app-id selects a platform config path where Glaze saves outer position, size, and maximized state at close and restores them on the next launch. An explicit path may be supplied instead. Stale geometry is clamped to the current virtual desktop so a disconnected monitor cannot strand the window.

procedure

(make-api-token) → string?

Returns a random 32-hex-character capability token.

parameter

(current-api-token) → string?

(current-api-token token) → void?
  token : string?
Bound by run-app so callbacks can read the active API token; the value is the empty string when API-token protection is disabled.

The application is a state machine over a custodian: 'starting → 'ready → 'running → 'stopping → 'stopped. Every app owns a custodian, so resources spawned for it (background threads such as the update check) are reaped deterministically at quit. State transitions broadcast an 'app-state event on the bus when one is wired. With #:quit-on-last-window? #f the app survives its last window closing (tray-resident mode) and ends only when app-quit! is called; additional windows attach through open-app-window.

procedure

(glaze-app? v) → boolean?

  v : any/c
Recognizes the app handle.

parameter

(current-app) → (or/c #f glaze-app?)

(current-app app) → void?
  app : (or/c #f glaze-app?)
Bound by run-app for the duration of the application; readable in #:on-ready, callbacks, and tray menus.

procedure

(app-state [app]) → symbol?

  app : glaze-app? = (current-app)
The lifecycle state.

procedure

(app-id [app]) → string?

  app : glaze-app? = (current-app)
The application identity (#:app-id or generated).

procedure

(app-quit! [app]) → void?

  app : glaze-app? = (current-app)
Quits the app: transitions to 'stopping, closes leftover windows, stops the server, and reaps the app custodian.

procedure

(open-app-window url    
  [#:title title    
  #:width width    
  #:height height    
  #:window-state window-state    
  #:on-close on-close]) → webview?
  url : string?
  title : string? = "Glaze"
  width : exact-positive-integer? = 1024
  height : exact-positive-integer? = 768
  window-state : (or/c #f path-string?) = #f
  on-close : (-> any) = (lambda () (void))
Opens a window attached to the running app. Requires current-app to be bound (inside #:on-ready or a callback of a running app).

3 Local Server🔗ℹ

 (require glaze/server) package: glaze
 (require glaze/browser)

procedure

(start-server [#:port port 
  #:public-dir public-dir 
  #:api api 
  #:events events 
  #:api-token api-token 
  #:capability capability 
  #:serve-api-client? serve-api-client? 
  #:max-body-size max-body-size]) 
 → 
exact-nonnegative-integer? procedure?
  port : exact-nonnegative-integer? = 8080
  public-dir : (or/c string? path?) = "public"
  api : (listof route?) = '()
  events : (or/c #f event-bus?) = #f
  api-token : (or/c #f string?) = #f
  capability : (or/c #f capability?) = #f
  serve-api-client? : boolean? = #t
  max-body-size : exact-nonnegative-integer? = 8388608
Starts the loopback HTTP server that powers the embedded frontend. Static resources, SPA index fallback, JSON routes, generated API client, and optional SSE event stream share the same origin. The return values are the actual port and a shutdown procedure. start-dev-server remains a compatibility alias for this low-level server primitive; it does not define a browser-based application mode.

The low-level server requires an explicit, non-empty #:api-token whenever #:capability is supplied, keeping runtime authority bound to the embedded WebView rather than an unauthenticated loopback caller.

Every request passes a Host-header check (DNS rebinding), a request-body limit, and — for API routes and the event stream — a cross-site guard: a foreign Origin header or a Sec-Fetch-Site value other than "same-origin"/​"none" is rejected with 403, which stops other web pages from posting to the loopback bridge. Static file serving is contained to #:public-dir, including through percent-encoded .. segments and symlinks; route names that cannot be expressed in the generated JavaScript client (collisions, invalid identifiers) fail at startup. Handler exceptions answer a generic 500 body — the full exception goes to the current-glaze-error-reporter, never to the page.

procedure

(stop-server shutdown-proc) → void?

  shutdown-proc : procedure?
Stops the server.

procedure

(open-browser url) → void?

  url : string?
Explicitly opens an external URL in the user’s default browser. This helper is appropriate for documentation, OAuth, support pages, and similar external resources. run-app, open-window, and open-webview do not use it as a fallback.

4 JavaScript Bridge🔗ℹ

 (require glaze/api) package: glaze
 (require glaze/api-macros)

The embedded frontend calls Racket through ordinary same-origin HTTP requests. This keeps the bridge easy to inspect and test with normal developer tools.

procedure

(GET path    
  handler    
  [#:permission permission    
  #:resource resource]) → route?
  path : string?
  handler : procedure?
  permission : (or/c #f symbol? string?) = #f
  resource : (or/c #f procedure?) = #f

procedure

(POST path    
  handler    
  [#:permission permission    
  #:resource resource]) → route?
  path : string?
  handler : procedure?
  permission : (or/c #f symbol? string?) = #f
  resource : (or/c #f procedure?) = #f

procedure

(PUT path    
  handler    
  [#:permission permission    
  #:resource resource]) → route?
  path : string?
  handler : procedure?
  permission : (or/c #f symbol? string?) = #f
  resource : (or/c #f procedure?) = #f

procedure

(DELETE path    
  handler    
  [#:permission permission    
  #:resource resource]) → route?
  path : string?
  handler : procedure?
  permission : (or/c #f symbol? string?) = #f
  resource : (or/c #f procedure?) = #f

The resource procedure receives the same request and captured path parameters as the route handler. Its result is checked against the active scoped permission before the handler can run. define-api-routes accepts the same #:permission and #:resource options after its route path.

5 Runtime Capabilities🔗ℹ

 (require glaze/capability) package: glaze

procedure

(make-capability id permissions) → capability?

  id : string?
  permissions : list?
Creates a named authority from permission identifiers and scoped permissions.

procedure

(allow-permission id) → any/c

  id : (or/c symbol? string?)
Creates an unscoped permission grant. A bare symbol or string in make-capability has the same meaning.

procedure

(scoped-permission id authorize) → any/c

  id : (or/c symbol? string?)
  authorize : procedure?
Creates a generic resource permission. authorize receives the route resource and must return a true value to authorize the request.

procedure

(path-permission id    
  #:allow allow-roots    
  [#:deny deny-roots]) → any/c
  id : (or/c symbol? string?)
  allow-roots : list?
  deny-roots : list? = '()
Allows resources inside the listed roots except those inside a denied root. Paths are simplified through existing symlinks; deny entries take precedence. The checked resource may be one path or a non-empty list of paths; every path in a list must be authorized.

procedure

(url-permission id    
  #:allow allowed-patterns    
  [#:deny denied-patterns]) → any/c
  id : (or/c symbol? string?)
  allowed-patterns : list?
  denied-patterns : list? = '()
Allows URL resources that match an exact string or an explicitly supplied regular expression. Deny patterns take precedence. Strings never gain implicit wildcards.

procedure

(command-permission id    
  #:allow allow-programs    
  [#:deny deny-programs    
  #:arguments arguments-ok?]) → any/c
  id : (or/c symbol? string?)
  allow-programs : list?
  deny-programs : list? = '()
  arguments-ok? : procedure? = (lambda (arguments) #t)
Allows exact executable names or paths and optionally validates their argument list. Deny entries take precedence.

procedure

(command-resource program arguments) → command-resource?

  program : path-string?
  arguments : list?
Constructs the resource returned by a command route’s #:resource procedure.

procedure

(accelerator-permission id    
  #:allow allowed-patterns    
  #:deny denied-patterns) → any/c
  id : (or/c symbol? string?)
  allowed-patterns : list?
  denied-patterns : '()
Allows global-shortcut accelerators that match an exact string or an explicitly supplied regular expression. Both the declared patterns and the requested accelerator are matched in canonical form, so CmdOrCtrl+Shift+D and COMMANDORCONTROL+shift+d authorize the same hotkey on the same platform. Plain strings must parse as accelerators at construction time; deny entries take precedence.

procedure

(capability-authorized? capability    
  permission    
  [resource]) → boolean?
  capability : capability?
  permission : (or/c symbol? string?)
  resource : any/c = #f
Checks runtime authority without invoking a route.

parameter

(current-capability-id) → (or/c #f string?)

(current-capability-id id) → void?
  id : (or/c #f string?)
Bound to the active capability ID while a protected route handler or resource extractor runs.

parameter

(current-capability-authorizer) → (or/c #f procedure?)

(current-capability-authorizer authorize) → void?
  authorize : (or/c #f procedure?)
Bound to the active capability’s authorizer while a protected route resource extractor or handler runs. This permits a handler to authorize resources that are only discovered during execution, such as HTTP redirect targets.

procedure

(current-capability-authorized? permission    
  [resource]) → boolean?
  permission : (or/c symbol? string?)
  resource : any/c = #f
Checks the dynamically active capability. Returns false outside a protected route.

6 Scoped Filesystem🔗ℹ

 (require glaze/filesystem) package: glaze

procedure

(make-filesystem-routes [#:prefix prefix]) → (listof route?)

  prefix : string? = "api/fs"
Creates frontend routes for text and binary file I/O, directories, metadata, existence checks, file copy, move, and removal. The routes declare 'fs:read or 'fs:write; use path-permission grants in the active capability. Copy and move expose both paths as one resource, so both source and destination must be in scope.

The default prefix generates client functions such as glaze.api.fsReadText, glaze.api.fsWriteFile, and glaze.api.fsMove. Binary payloads use base64 strings in JSON.

procedure

(fs-read-text path) → string?

  path : path-string?

procedure

(fs-write-text! path text) → void?

  path : path-string?
  text : string?
Writes via a same-directory temporary file and atomic replacement.

procedure

(fs-read-bytes path) → bytes?

  path : path-string?

procedure

(fs-write-bytes! path data) → void?

  path : path-string?
  data : bytes?
Writes via a same-directory temporary file and atomic replacement.

procedure

(fs-read-dir path) → (listof hash?)

  path : path-string?

procedure

(fs-create-dir! path    
  [#:recursive? recursive?]) → void?
  path : path-string?
  recursive? : boolean? = #t

procedure

(fs-remove! path [#:recursive? recursive?]) → void?

  path : path-string?
  recursive? : boolean? = #f

procedure

(fs-copy! source    
  destination    
  [#:replace? replace?]) → void?
  source : path-string?
  destination : path-string?
  replace? : boolean? = #f

procedure

(fs-move! source    
  destination    
  [#:replace? replace?]) → void?
  source : path-string?
  destination : path-string?
  replace? : boolean? = #f

procedure

(fs-stat path) → hash?

  path : path-string?

procedure

(fs-exists? path) → boolean?

  path : path-string?

7 Scoped Shell and Child Processes🔗ℹ

 (require glaze/shell) package: glaze

procedure

(make-shell-routes [#:prefix prefix 
  #:max-output max-output 
  #:max-processes max-processes 
  #:cwd-roots cwd-roots 
  #:allow-environment allowed-environment 
  #:retention-seconds retention-seconds]) 
 → (listof route?)
  prefix : string? = "api/shell"
  max-output : exact-positive-integer? = (* 1024 1024)
  max-processes : exact-positive-integer? = 32
  cwd-roots : (listof path-string?) = '()
  allowed-environment : list? = '()
  retention-seconds : positive-real? = 300
Creates frontend routes for direct command execution. 'shell:execute authorizes command-resource values before synchronous or background execution; 'shell:manage authorizes background status, stdin, and kill operations. Handles are additionally bound to the capability that spawned them. Programs are never passed through a system shell. Frontend working directories and environment-variable names are denied by default; applications must opt in with cwd-roots and allowed-environment. The registry retains at most max-processes handles, evicting completed handles first.

The default prefix generates glaze.api.shellOutput, shellSpawn, shellStatus, shellWrite, shellCloseStdin, and shellKill. Completed handles remain available for retention-seconds.

procedure

(shell-output program    
  [arguments    
  #:cwd cwd    
  #:env environment    
  #:timeout timeout    
  #:max-output max-output]) → hash?
  program : path-string?
  arguments : (listof string?) = '()
  cwd : (or/c #f path-string?) = #f
  environment : (or/c #f hash?) = #f
  timeout : (or/c #f positive-real?) = 30
  max-output : exact-positive-integer? = (* 1024 1024)
Executes a program directly, closes stdin, waits up to the timeout, and returns exit status plus bounded UTF-8 stdout and stderr.

procedure

(shell-spawn! program    
  [arguments    
  #:cwd cwd    
  #:env environment    
  #:max-output max-output]) → shell-process?
  program : path-string?
  arguments : (listof string?) = '()
  cwd : (or/c #f path-string?) = #f
  environment : (or/c #f hash?) = #f
  max-output : exact-positive-integer? = (* 1024 1024)
Starts a background child process without invoking a system shell.

procedure

(shell-process-id child) → string?

  child : shell-process?

procedure

(shell-process-pid child) → exact-integer?

  child : shell-process?

procedure

(shell-process-info child) → hash?

  child : shell-process?

procedure

(shell-process-write! child data) → void?

  child : shell-process?
  data : (or/c string? bytes?)

procedure

(shell-process-close-input! child) → void?

  child : shell-process?

procedure

(shell-process-kill! child [force?]) → void?

  child : shell-process?
  force? : boolean? = #t

procedure

(shell-process-wait child [timeout]) → boolean?

  child : shell-process?
  timeout : (or/c #f nonnegative-real?) = #f

8 Persistent JSON Store🔗ℹ

 (require glaze/store) package: glaze

procedure

(make-store-routes #:root root    
  [#:prefix prefix    
  #:defaults defaults    
  #:auto-save auto-save    
  #:events events]) → (listof route?)
  root : path-string?
  prefix : string? = "api/store"
  defaults : hash? = (hasheq)
  auto-save : (or/c boolean? nonnegative-real?) = 100
  events : (or/c #f event-bus?) = #f
Creates capability-gated frontend routes for persistent JSON key-value stores. Read operations declare 'store:read and mutations declare 'store:write. Frontend paths must be relative and cannot escape the required root. Mutations optionally publish 'store:change on events. Auto-save uses a debounce interval in milliseconds; #f disables it.

The generated client includes storeLoad, storeGet, storeSet, storeHas, storeDelete, storeClear, storeReset, storeKeys, storeValues, storeEntries, storeLength, storeSave, storeReload, and storeClose.

procedure

(load-store path    
  [#:defaults defaults    
  #:auto-save auto-save    
  #:create-new? create-new?    
  #:override-defaults? override-defaults?]) → store?
  path : path-string?
  defaults : hash? = (hasheq)
  auto-save : (or/c boolean? nonnegative-real?) = 100
  create-new? : boolean? = #f
  override-defaults? : boolean? = #f
Loads a JSON object from disk or creates an in-memory store from defaults. The file is created on the first save.

procedure

(store-get storage key [default]) → any/c

  storage : store?
  key : (or/c string? symbol?)
  default : any/c = #f

procedure

(store-set! storage key value) → void?

  storage : store?
  key : (or/c string? symbol?)
  value : jsexpr?

procedure

(store-has-key? storage key) → boolean?

  storage : store?
  key : (or/c string? symbol?)

procedure

(store-delete! storage key) → boolean?

  storage : store?
  key : (or/c string? symbol?)

procedure

(store-clear! storage) → void?

  storage : store?

procedure

(store-reset! storage) → void?

  storage : store?

procedure

(store-keys storage) → (listof string?)

  storage : store?

procedure

(store-values storage) → list?

  storage : store?

procedure

(store-entries storage) → list?

  storage : store?

procedure

(store-count storage) → exact-nonnegative-integer?

  storage : store?

procedure

(store-snapshot storage) → hash?

  storage : store?

procedure

(store-save! storage) → void?

  storage : store?

procedure

(store-reload! storage    
  [#:ignore-defaults? ignore-defaults?]) → void?
  storage : store?
  ignore-defaults? : boolean? = #f

procedure

(store-close! storage) → void?

  storage : store?

9 Capability-Gated System Plugins🔗ℹ

 (require glaze/system) package: glaze

procedure

(make-system-routes [#:prefix prefix]) → (listof route?)

  prefix : string? = "api/system"
Creates frontend routes for clipboard text, desktop notifications, opening or revealing paths, opening URLs, and OS information. The routes use distinct permissions: 'clipboard:read, 'clipboard:write, 'notification:send, 'opener:open-path, 'opener:reveal-path, 'opener:open-url, 'os:read, and 'os:hostname. Path operations should use path-permission grants and URL opening should use url-permission. Resource scopes are checked before native handlers run.

The default prefix generates systemClipboardRead, systemClipboardWrite, systemNotificationSend, systemOpenerOpenPath, systemOpenerRevealPath, systemOpenerOpenUrl, systemOsInfo, and systemOsHostname.

procedure

(system-information) → hash?

Returns platform, OS type, family, architecture, executable extension, locale, and the runtime-reported system version string.

procedure

(system-hostname) → string?

Returns the local host name. Frontend access uses the separate 'os:hostname permission.

10 Global Shortcuts🔗ℹ

 (require glaze/global-shortcut) package: glaze

System-wide hotkeys with Tauri-style accelerators such as CmdOrCtrl+Shift+D. The native backends are Win32 RegisterHotKey on Windows, Carbon RegisterEventHotKey on macOS, and X11 XGrabKey on Linux (XWayland sessions included; a Wayland session without X fails registration rather than raising). Linux grabs use exact modifiers, so pressed lock keys such as Caps Lock suppress the event there.

procedure

(string->hotkey s) → (or/c hotkey? #f)

  s : string?
Parses an accelerator. Modifier spellings are case-insensitive: Command/Cmd/Super/Meta, Control/Ctrl, Option/Alt, Shift, and the per-platform CommandOrControl/ CmdOrCtrl (cmd on macOS, ctrl elsewhere). Keys accept bare and KeyA/Digit7/Code-style spellings, F1 through F24, numpad keys, and named keys such as Space or BracketLeft. Returns #f for any malformed input.

procedure

(hotkey->string hk) → string?

  hk : hotkey?
Renders the canonical form, for example Ctrl+Shift+D on Windows and Linux.

procedure

(hotkey-register! hk thunk) → boolean?

  hk : hotkey?
  thunk : procedure?
Registers a system-wide hotkey; the thunk runs each time the combination is pressed anywhere in the session. Duplicate registrations report #f. hotkey-unregister!, hotkey-unregister-all!, and hotkey-registered? manage the lifecycle.

procedure

(make-global-shortcut-routes [#:prefix prefix 
  #:backend backend 
  #:events event-bus]) 
 → (listof route?)
  prefix : string? = "api/global-shortcut"
  backend : hotkey-backend? = default-hotkey-backend
  event-bus : (or/c event-bus? #f) = #f
Creates register, unregister, unregister-all, and is-registered routes under the permissions 'global-shortcut:register, 'global-shortcut:unregister, 'global-shortcut:unregister-all, and 'global-shortcut:is-registered; pair them with accelerator-permission scopes so a page can only grab the combinations the app declared. Triggers reach the page as global-shortcut SSE events carrying the canonical accelerator. The generated client contains glaze.api.globalShortcutRegister, glaze.api.globalShortcutUnregister, glaze.api.globalShortcutUnregisterAll, and glaze.api.globalShortcutIsRegistered.

11 Structured Logging🔗ℹ

 (require glaze/log) package: glaze

Tauri-plugin-log-style logging: one logger fans structured records out to sinks with per-sink minimum levels, keeps a bounded in-memory history, and pushes records to the page as log SSE events when an event bus is attached.

procedure

(make-glaze-logger [#:min-level min-level    
  #:sinks sinks    
  #:file-root file-root    
  #:events event-bus    
  #:history history-limit]) → glaze-logger?
  min-level : symbol? = 'info
  sinks : list? = '()
  file-root : (or/c #f path-string?) = #f
  event-bus : (or/c event-bus? #f) = #f
  history-limit : (or/c #f exact-positive-integer?) = 1000
Creates a logger with a stderr sink plus the optional rotating file sink (#:file-root), SSE push (#:events), and custom sinks. Records below min-level are dropped everywhere; each sink can additionally filter by its own minimum level. log-trace, log-debug, log-info, log-warn, and log-error emit records with optional #:data jsexprs.

procedure

(glaze-logger-history logger [limit]) → (listof log-record?)

  logger : glaze-logger?
  limit : exact-positive-integer? = 100
Returns the most recent records, oldest first.

procedure

(make-file-log-sink root    
  [#:name name    
  #:max-bytes max-bytes    
  #:keep keep    
  #:min-level min-level]) → log-sink?
  root : path-string?
  name : string? = "glaze.log"
  max-bytes : exact-positive-integer? = (* 1024 1024)
  keep : exact-nonnegative-integer? = 3
  min-level : symbol? = 'trace
A rotating file sink: when a line would push the current file past max-bytes, files shift (name.k-1 becomes name.k, the oldest is dropped) instead of truncating, so a crash mid-write never destroys earlier logs.

procedure

(make-log-routes logger [#:prefix prefix]) → (listof route?)

  logger : glaze-logger?
  prefix : string? = "api/log"
Creates a 'log:write-gated write route and a 'log:read-gated bounded history route. Frontend records are tagged 'frontend with the active capability ID, so a page cannot forge backend log lines, and they respect the logger’s minimum level exactly like backend records. The generated client contains glaze.api.logWrite and glaze.api.logHistory.

12 Application and Resource Paths🔗ℹ

 (require glaze/path) package: glaze

procedure

(make-path-resolver app-id 
  #:resource-root resource-root 
  [#:app-directories-override overrides]) 
 → path-resolver?
  app-id : string?
  resource-root : path-string?
  overrides : (or/c #f path-string? hash?) = #f
Creates a resolver for Tauri-style application directories. A path override uses one portable root: config/data/local-data use the root, cache uses its caches child, and log uses logs. A hash may override 'config, 'data, 'localData, 'cache, and 'log individually. Overrides accept the documented $HOME, $DATA, $LOCALDATA, and other base-directory variables.

procedure

(make-path-routes #:app-id app-id 
  #:resource-root resource-root 
  [#:app-directories-override overrides 
  #:prefix prefix]) 
 → (listof route?)
  app-id : string?
  resource-root : path-string?
  overrides : (or/c #f path-string? hash?) = #f
  prefix : string? = "api/path"
Creates granular, default-deny frontend routes for application/user/resource directories and path utilities. Resource resolution is confined to the configured resource root even through existing symbolic links.

procedure

(app-config-dir resolver) → path?

  resolver : path-resolver?

procedure

(app-data-dir resolver) → path?

  resolver : path-resolver?

procedure

(app-local-data-dir resolver) → path?

  resolver : path-resolver?

procedure

(app-cache-dir resolver) → path?

  resolver : path-resolver?

procedure

(app-log-dir resolver) → path?

  resolver : path-resolver?

procedure

(resource-dir resolver) → path?

  resolver : path-resolver?

procedure

(resolve-resource resolver relative-path) → path?

  resolver : path-resolver?
  relative-path : path-string?

procedure

(path-join path ...) → path?

  path : path-string?

procedure

(path-resolve path ...) → path?

  path : path-string?

procedure

(path-normalize path) → path?

  path : path-string?

procedure

(path-basename path) → string?

  path : path-string?

procedure

(path-dirname path) → string?

  path : path-string?

procedure

(path-extname path) → string?

  path : path-string?

procedure

(path-absolute? path) → boolean?

  path : path-string?

13 Scoped HTTP Client🔗ℹ

 (require glaze/http) package: glaze

procedure

(http-request url    
  [#:method method    
  #:headers headers    
  #:body body    
  #:timeout timeout    
  #:max-request-bytes max-request-bytes    
  #:max-response-bytes max-response-bytes    
  #:max-redirects max-redirects    
  #:authorize-url? authorize-url?]) → hash?
  url : string?
  method : (or/c string? symbol? bytes?) = "GET"
  headers : hash? = (hasheq)
  body : (or/c #f string? bytes?) = #f
  timeout : real? = 30
  max-request-bytes : exact-positive-integer? = (* 10 1024 1024)
  max-response-bytes : exact-positive-integer?
   = (* 10 1024 1024)
  max-redirects : exact-nonnegative-integer? = 5
  authorize-url? : procedure? = (lambda (candidate) #t)
Performs a bounded HTTP or HTTPS request and returns the status, final URL, headers, UTF-8 replacement-decoded text, and base64 body. The timeout is a total deadline across redirects. Every URL, including every redirect target, is checked before connecting. Cross-origin redirects remove Authorization and Cookie.

procedure

(make-http-routes [#:prefix prefix 
  #:max-request-bytes max-request-bytes 
  #:max-response-bytes max-response-bytes 
  #:timeout timeout 
  #:max-redirects max-redirects]) 
 → (listof route?)
  prefix : string? = "api/http"
  max-request-bytes : exact-positive-integer? = (* 10 1024 1024)
  max-response-bytes : exact-positive-integer?
   = (* 10 1024 1024)
  timeout : real? = 30
  max-redirects : exact-nonnegative-integer? = 5
Creates 'http:request-protected frontend access. The generated client contains glaze.api.httpRequest. Initial and redirected URLs are checked against the active URL-scoped capability before connecting. Frontend callers may lower configured limits but cannot raise them. Connection-managed headers such as Host and Content-Length are rejected.

14 Scoped SQLite🔗ℹ

 (require glaze/sql) package: glaze

procedure

(open-sqlite-database path 
  [#:busy-retry-limit busy-retry-limit]) 
 → sql-database?
  path : path-string?
  busy-retry-limit : exact-nonnegative-integer? = 1000
Opens or creates a SQLite database. The parent directory must already exist.

procedure

(sql-select database    
  statement    
  [parameters    
  #:max-rows max-rows    
  #:max-cell-bytes max-cell-bytes]) → (listof hash?)
  database : sql-database?
  statement : string?
  parameters : list? = '()
  max-rows : exact-positive-integer? = 10000
  max-cell-bytes : exact-positive-integer? = (* 10 1024 1024)
Runs a parameterized SELECT or WITH query. Results are bounded by row count. SQL NULL becomes JSON null and BLOB values become hashes containing 'blobBase64.

procedure

(sql-execute! database statement [parameters]) → hash?

  database : sql-database?
  statement : string?
  parameters : list? = '()
Runs a parameterized statement and returns 'rowsAffected and 'lastInsertId.

procedure

(sql-close! database) → void?

  database : sql-database?
Closes the connection. Repeated close calls are harmless.

procedure

(make-sql-routes #:root root 
  [#:prefix prefix 
  #:max-connections max-connections 
  #:max-rows max-rows 
  #:max-cell-bytes max-cell-bytes 
  #:busy-retry-limit busy-retry-limit]) 
 → (listof route?)
  root : path-string?
  prefix : string? = "api/sql"
  max-connections : exact-positive-integer? = 16
  max-rows : exact-positive-integer? = 10000
  max-cell-bytes : exact-positive-integer? = (* 10 1024 1024)
  busy-retry-limit : exact-nonnegative-integer? = 1000
Creates 'sql:load, 'sql:select, 'sql:execute, and 'sql:close protected routes. Database paths are relative to root, are checked through existing symbolic links, and connections are cached separately for each active capability. The generated client contains sqlLoad, sqlSelect, sqlExecute, and sqlClose.

A route handler receives the web-server request followed by any captured :param path values. Returning a jsexpr produces a JSON 200 response; a full response value may also be returned, including a streaming response (streaming-response / event-stream-response below).

procedure

(request-json-body req) → jsexpr?

  req : request?
Parses a JSON request body. Missing, empty, or malformed input yields an empty hash so route validation can produce a clean client error. JSON object keys in Racket jsexprs are symbols, for example (hash-ref body 'delta).

procedure

(json-response data) → response?

  data : jsexpr?

procedure

(api-response data) → response?

  data : jsexpr?

procedure

(error-response status message) → response?

  status : exact-nonnegative-integer?
  message : string?

procedure

(streaming-response writer    
  [#:mime mime    
  #:headers headers]) → response?
  writer : (-> output-port? any)
  mime : bytes? = #"application/octet-stream"
  headers : (listof header?) = '()
A chunked 200 response. writer runs on the connection thread after the status line and headers go out — the same mechanism as the built-in /glaze/events SSE endpoint. Write to the port and call (flush-output out) after each chunk that should be delivered immediately; returning from the writer ends the response.

A handler that raises before returning still maps to a 500 JSON (nothing is on the wire yet); an exception inside the writer closes the connection mid-stream, so wrap your own errors there if truncation is unacceptable. The typical use is proxying a streaming LLM endpoint — tokens reach the page as they arrive, API keys stay in Racket, and the page never makes a cross-origin call:

(GET "api/llm/chat"
 (lambda (req)
   (streaming-response
    #:mime #"application/x-ndjson"
    (lambda (out)
      (for ([delta (in-llm-deltas (request-json-body req))])
        (displayln (jsexpr->string (hasheq 'delta delta)) out)
        (flush-output out))))))

procedure

(event-stream-response sender    
  [#:headers headers]) → response?
  sender : procedure?
  headers : (listof header?) = '()
The SSE flavor of streaming-response: sender receives a send callback, and each (send name data) emits one

event: name\ndata: <json>

frame and flushes. Consuming with EventSource in the page works out of the box; Cache-Control: no-cache is added automatically.

(GET "api/sse/demo"
 (lambda (req)
   (event-stream-response
    (lambda (send)
      (send 'delta (hasheq 'text "Hel"))
      (send 'delta (hasheq 'text "lo"))
      (send 'done (hasheq 'ok #t))))))

syntax

(define-api-routes id clause ...)

Declares a callable Racket procedure, a validated HTTP route, and a generated JavaScript client entry from one route clause.

(define-api-routes api
  [(POST "api/counter/bump")
   (bump [delta exact-nonnegative-integer? 1])
   (hasheq 'count (add1 delta))])

The generated /glaze/api.js exposes route-specific functions plus glaze.call(...) and glaze.on(...).

15 Event Push🔗ℹ

 (require glaze/events) package: glaze

Glaze uses same-origin Server-Sent Events for backend-to-frontend push.

procedure

(make-event-bus) → event-bus?

Creates a broadcast event bus.

procedure

(bus-broadcast! bus name data) → void?

  bus : event-bus?
  name : (or/c symbol? string?)
  data : jsexpr?
Broadcasts an event without blocking the producer. When a slow subscriber’s backlog (256 events) is full, the subscriber’s OLDEST event is dropped to make room — a lagging UI wants the freshest state. Drops are never silent: bus-dropped-count reports the total and the first drop in a burst is reported through current-event-drop-reporter (at most once every five seconds per bus).

procedure

(bus-subscribe! bus) → async-channel?

  bus : event-bus?

procedure

(bus-unsubscribe! bus channel) → void?

  bus : event-bus?
  channel : async-channel?

procedure

(bus-wait channel [seconds]) → any/c

  channel : async-channel?
  seconds : real? = 10

procedure

(bus-dropped-count bus) → exact-nonnegative-integer?

  bus : event-bus?

parameter

(current-event-drop-reporter)

 → (-> exact-nonnegative-integer? (or/c symbol? string?) any)
(current-event-drop-reporter reporter) → void?
  reporter : (-> exact-nonnegative-integer? (or/c symbol? string?) any)

16 GLZ1 Bridge Protocol🔗ℹ

 (require glaze/bridge) package: glaze

GLZ1 is Glaze’s versioned bridge protocol: a request envelope with an id, a closed typed-error taxonomy, cancellation, and timeouts, wrapped around the registered routes. The HTTP endpoints are GET /glaze/hello, POST /glaze/invoke, and POST /glaze/cancel; the generated api.js exposes glaze.hello() / glaze.invoke(path, args, opts) / glaze.cancel(id). The envelope layer is transport-agnostic — the native message-handler transport (Native Transport) dispatches the same envelopes through the WebView’s message handler, with no TCP port.

The protocol is aligned with keel, the transport-neutral application contract shared with Rivet: every invoke response names its terminal 'outcome ("succeeded" / "failed" / "cancelled" / "timed-out"); the error object is keel-shaped (kebab-case code, message, data); /glaze/hello carries a keel app-schema rendering of the route surface; and the app lifecycle state machine validates every transition against keel’s lifecycle contract.

value

glz1-version : exact-nonnegative-integer?

The protocol version (currently 1).

procedure

(bridge-error-code? v) → boolean?

  v : any/c
Recognizes the closed error-taxonomy codes.

procedure

(raise-bridge-error code [message data]) → any

  code : bridge-error-code?
  message : (or/c #f string?) = #f
  data : any/c = #f
Raise inside an invoke handler to answer a typed bridge error; data carries structured, non-sensitive detail — never exception text.

struct

(struct exn:fail:glaze:bridge (code data))

  code : bridge-error-code?
  data : any/c

procedure

(bridge-cancel-event) → (or/c #f semaphore?)

The cancel event of the in-flight request, or #f outside an invoke. Syncing on it blocks until POST /glaze/cancel fires for this request.

procedure

(bridge-cancelled?) → boolean?

Non-blocking cancellation poll. A handler that accepts the request should raise (raise-bridge-error 'cancelled "request cancelled") so its invoke response owns the "cancelled" terminal outcome. Returning a normal value remains a successful completion even if cancellation was observed.

16.1 Streaming responses🔗ℹ

A streaming handler (streaming-response / event-stream-response) cannot be one invoke envelope — its bytes arrive over time. GLZ1 delivers it as a frame sequence on the same channel: an open frame marks the request id as a stream, one chunk frame carries each piece of decoded text (window.__glz1Chunk(id, text)), and a terminal frame (window.__glz1End(id)) settles the invoking promise with the full joined text. A handler that fails before returning the streaming response still answers a normal typed-error envelope, so both terminal shapes resolve the same page-side promise.

The page client exposes glaze.invokeStream(path, args, opts): opts.onDelta fires per chunk as it arrives, and opts.signal (an AbortSignal) rejects the promise immediately and sends the cancel op, which closes the stream’s pipe server side. Streams carry no timeout — they settle when the writer finishes. Chunk boundaries respect UTF-8: a multi-byte character split across internal reads is held back until it completes rather than delivered corrupted. A writer exception truncates the stream (the terminal frame still arrives), mirroring the HTTP adapter’s connection close.

Every frame travels in both carriers like a full response: a self-contained JavaScript snippet for evaluateJavaScript-style hosts (WKWebView, WebKitGTK), and a {glz, stream, id[, text]} JSON object for PostWebMessageAsJson-style hosts (WebView2).

17 Layer Modules🔗ℹ

Beyond the curated glaze/core face, every facade name is reachable through its aggregation layer. Applications that want one surface without the full glaze facade require the layer directly; each layer’s export set is guarded by CI, so drift fails instead of widening silently.

 (require glaze/window) package: glaze
Windows, webview handles, and window state (glaze/webview/main and glaze/window-state re-exported).

The capability services: filesystem scopes, the key-value store, SQL, shell, the HTTP proxy, logging, dialogs, tray, system commands, global shortcuts, hotkeys, system information, and the browser opener.

Packaging, signing, updates, and bundled assets — everything that carries an application from a source tree to an installed, self-updating product.

Development-time tooling: the loopback dev server (the HTTP adapter for hot reload and curl) and FFI discovery diagnostics.

Capabilities that are shipped but NOT frozen: glaze/deeplink (OS URL-scheme handover) and glaze/autolaunch (login items). The surface may change in any release without a compat baseline; a module graduates into its layer after surviving a release cycle unchanged under real product usage.

18 Typed Command Declarations🔗ℹ

 (require glaze/types) package: glaze

Typed declarations give a route’s parameters and results one expression that both types them in the keel command schema and validates the wire value — the declaration is the validation. Descriptors are accepted anywhere define-api-routes takes a predicate:

(define Item (grecord 'Item "id" GInt64 "name" GString))
 
(define-api-routes api
  [(POST "api/counter/bump")
   (bump [delta GInt64 1])
   (begin (bump! delta) (hasheq 'count (unbox count)))]
  [(GET "api/items/:id" #:returns Item)
   (item id)
   (hasheq 'id id)])

The scalar descriptors are GNull, GBool, GInt64, GFloat64, GString, GBytes (a base64 string on the wire), and GValue (any jsexpr). Combinators are glist, goptional, and gmap. Named types — (grecord 'Item "id" GInt64 ...) and (genum 'Color 'red 'green 'blue) — are referenced by value: using Item inside another declaration is the reference, and named types render as TypeScript interfaces and unions.

Wire conventions: null is JSON null, unknown object keys are tolerated so newer clients keep working against older apps, and a wire value that fails its descriptor is a typed invalid-args error naming the parameter — both transports share the same validator (validate-type / validate-jsexpr).

procedure

(grecord name field type ... ...) → grecord?

  name : symbol?
  field : string?
  type : type-descriptor?

procedure

(genum name case ...) → genum?

  name : symbol?
  case : symbol?

procedure

(glist element) → type-descriptor?

  element : type-descriptor?

procedure

(goptional element) → type-descriptor?

  element : type-descriptor?

procedure

(gmap value-type) → type-descriptor?

  value-type : type-descriptor?

procedure

(validate-type descriptor value) → 
boolean? any/c
  descriptor : type-descriptor?
  value : jsexpr?
Wire check; on failure the second value names the offending position.

procedure

(validate-jsexpr type value) → 
boolean? any/c
  type : any/c
  value : jsexpr?
The same check for plain keel types, used by the invoke pipeline for schema commands declared without descriptors.

Declared types surface in three places automatically: /glaze/hello carries real keel types for every typed command, /glaze/api.d.ts (served by both transports) carries the generated TypeScript client declaration — generate-ts-declarations renders it — and glaze/schema-compat classifies an old app-schema against a new one:

 (require glaze/schema-compat) package: glaze

procedure

(schema-compat? v) → boolean?

  v : any/c

procedure

(schema-compat-report old new) → (hash/c symbol? any/c)

  old : any/c
  new : any/c
Deterministic compatibility report between two app schemas: every breaking change (removed command, renamed parameter, changed wire type) is named, so raco glaze schema check can gate a release.

procedure

(command-compatible? old new) → boolean?

  old : any/c
  new : any/c

19 Native Transport🔗ℹ

 (require glaze/native-transport) package: glaze

The native transport serves the app’s page and API entirely inside the WebView’s process boundary: the page loads from a controlled origin (glaze://app/ on macOS and Linux, http://glaze.localhost/ on Windows, because WebView2 refuses top-level navigation to custom schemes) and invokes the backend through the WebView’s message handler — production binds no TCP port. GLZ1 envelopes ride the message handler in both directions, so typed errors, timeouts, cancellation, capability checks, and streaming work identically on both transports; the HTTP adapter remains the development vehicle, and an app’s existing <script src="/glaze/api.js"> works unchanged because the scheme handler serves the native client at the same path.

procedure

(start-native-transport #:api routes 
  [#:authority authority 
  #:events bus 
  #:public-dir public-dir]) 
 → native-transport?
  routes : (listof route?)
  authority : (or/c #f capability?) = #f
  bus : (or/c #f event-bus?) = #f
  public-dir : path-string? = "public"
Starts one transport. #:api routes dispatch through the GLZ1 envelope layer exactly as over HTTP. #:events subscribes the bus so broadcasts reach the page as sequenced batches. #:public-dir backs the controlled scheme’s static resources with the same symlink containment and index.html fallback as the HTTP adapter. The dispatcher inherits the caller’s parameterization (custodian, error reporter) and spawns one thread per message, so a slow or timing-out invoke never blocks other invokes or cancels.

procedure

(native-transport-message! t raw) → void?

  t : native-transport?
  raw : string?
Entry point the platform wiring calls with each message-handler payload, from the UI thread; never blocks. Payloads queue on an async channel and each dispatches on its own thread.

struct

(struct native-delivery (js json))

  js : string?
  json : string?
One delivery to the page in both carriers: native-delivery-js is a self-contained JavaScript snippet for evaluateJavaScript-style hosts (WKWebView, WebKitGTK); native-delivery-json is the bare JSON text for PostWebMessageAsJson-style hosts (WebView2). Host wiring picks its carrier and hops it to the UI thread.

procedure

(native-transport-set-deliver! t deliver!) → void?

  t : native-transport?
  deliver! : (-> native-delivery? any)
Installs the host’s delivery hook once a WebView handle exists. deliver! runs on transport threads and must hop to the UI thread.

procedure

(native-transport-stats t)

 → (hash/c symbol? exact-nonnegative-integer?)
  t : native-transport?
Counters: resources-served, messages-received, responses-delivered, events-delivered.

procedure

(native-transport-count-resource! t) → void?

  t : native-transport?
Called by the host’s scheme handler for each served resource, so tests can observe that the page really loaded through the controlled origin.

procedure

(native-transport-shutdown! t) → void?

  t : native-transport?
Stops the dispatcher and event threads. Idempotent.

procedure

(native-app-origin) → string?

The controlled origin the page runs on, per platform — for tests and assertions.

procedure

(native-scheme-segments url-str) → (or/c #f (listof string?))

  url-str : string?
Parses a controlled-scheme URL (glaze://app/sub/x.js?query#frag) into percent-decoded path segments, accepting the Windows http://glaze.localhost/... shape as well. A hostile or malformed URL produces #f, which hosts map to their 404 path.

value

native-bootstrap-js : string?

The page-side bootstrap, injected at document start by every platform’s wiring. Defines the delivery plumbing (__glz1Deliver, the streaming hooks) and a transport-level client (__glz1.invoke / invokeStream / cancel / hello / on); platform differences are confined to the sink selection (WebKit message handlers versus WebView2 postMessage).

procedure

(native-api-client-js routes) → string?

  routes : (listof route?)
The native twin of the HTTP api.js, served by the scheme handler at /glaze/api.js: binds glaze.call / glaze.invoke / glaze.invokeStream / glaze.cancel / glaze.hello / glaze.on and one generated function per route over the transport bindings.

procedure

(generate-api-declarations routes) → string?

  routes : (listof route?)
Renders the TypeScript declarations for the app’s typed routes (routes that declare #:body-types/#:returns/#:errors schemas and their glaze/types descriptors), served by the scheme handler at /glaze/api.d.ts. Function names match the generated client.

20 Native WebView🔗ℹ

 (require glaze/webview/main) package: glaze

The native WebView is an application prerequisite, not an optional rendering mode. The backends are WebView2 on Windows, WKWebView on macOS, and WebKitGTK on Linux.

procedure

(open-window url    
  [#:title title    
  #:width width    
  #:height height    
  #:devtools? devtools?    
  #:window-state window-state    
  #:on-close on-close]) → webview?
  url : string?
  title : string? = "Glaze"
  width : exact-positive-integer? = 1024
  height : exact-positive-integer? = 768
  devtools? : boolean? = #f
  window-state : (or/c #f path-string?) = #f
  on-close : (-> any) = (lambda () (void))
Opens a native desktop window and loads url. If the backend or its runtime dependency is unavailable, this procedure raises. Before raising in an interactive desktop process, Glaze also attempts to show an OS-level error dialog so packaged GUI applications without a console still give the user an actionable explanation. CI environments suppress the dialog and retain the exception text in logs.

There is no #:fallback-browser? keyword.

procedure

(open-webview url    
  [#:title title    
  #:width width    
  #:height height    
  #:devtools? devtools?    
  #:window-state window-state    
  #:on-close on-close]) → webview?
  url : string?
  title : string? = "Glaze"
  width : exact-positive-integer? = 1024
  height : exact-positive-integer? = 768
  devtools? : boolean? = #f
  window-state : (or/c #f path-string?) = #f
  on-close : (-> any) = (lambda () (void))
Lower-level synonym of open-window with the same fail-fast contract.

procedure

(webview-window-state webview) → (or/c #f window-state?)

  webview : webview?
Returns the native window’s outer position, size, and maximized state.

procedure

(webview-set-window-state! webview state) → any

  webview : webview?
  state : window-state?
Moves/resizes the native window and restores its maximized state.

procedure

(webview-save-state! webview path) → (or/c #f path?)

  webview : webview?
  path : path-string?

procedure

(webview-restore-state! webview path) → any

  webview : webview?
  path : path-string?
Reads a saved state, clamps it to webview-screen-area, and applies it. Malformed or missing state files are ignored.

procedure

(webview-supported?) → boolean?

Non-throwing capability probe for the current platform backend. Actual window creation remains the authoritative runtime check.

procedure

(webview-last-error) → any/c

Returns the most recent backend probe/startup error.

procedure

(webview-install-guidance) → string?

Returns platform-specific dependency guidance. Windows guidance names the Microsoft Edge WebView2 Evergreen Runtime; Linux guidance names GTK 3 and WebKitGTK packages; macOS explains that WKWebView is part of the OS.

procedure

(webview-diagnostic) → string?

Formats the current error and guidance.

procedure

(webview-navigate wv url) → void?

  wv : webview?
  url : string?

procedure

(webview-close wv) → void?

  wv : webview?

procedure

(webview-title wv) → (or/c #f string?)

  wv : webview?

procedure

(webview-url wv) → (or/c #f string?)

  wv : webview?

procedure

(webview-capture! wv [dest]) → (or/c #f path?)

  wv : webview?
  dest : (or/c #f string? path?) = #f

procedure

(webview-set-title! wv title) → void?

  wv : webview?
  title : string?

procedure

(webview-set-size! wv width height) → void?

  wv : webview?
  width : exact-positive-integer?
  height : exact-positive-integer?

procedure

(webview-set-fullscreen! wv on?) → void?

  wv : webview?
  on? : boolean?

procedure

(webview-focus! wv) → void?

  wv : webview?

procedure

(webview-set-menu! wv menus) → void?

  wv : webview?
  menus : list?

procedure

(webview-closed? wv) → boolean?

  wv : webview?

procedure

(all-webviews) → (listof webview?)

procedure

(close-all-webviews!) → void?

procedure

(wait-for-webviews [timeout-seconds]) → boolean?

  timeout-seconds : (or/c #f real?) = #f

20.1 Startup Dependency Feedback🔗ℹ

When native startup fails, Glaze reports the backend error and remediation. Typical guidance includes:

  • Windows: install or repair Microsoft Edge WebView2 Runtime (Evergreen), with a winget command and Microsoft’s official download page.

  • Debian/Ubuntu: sudo apt install libgtk-3-0 libwebkit2gtk-4.1-0.

  • Fedora: sudo dnf install gtk3 webkit2gtk4.1.

  • Arch: sudo pacman -S gtk3 webkit2gtk-4.1.

  • macOS: WKWebView is built in; use a logged-in graphical session and report the preserved backend error if initialization still fails.

Set environment variable GLAZE_NO_STARTUP_DIALOG to 1 to suppress the interactive error dialog while retaining the exception. Dialogs are also suppressed automatically under common CI environments.

21 System Integrations🔗ℹ

 (require glaze/sys) package: glaze

procedure

(sys-supported?) → boolean?

procedure

(clipboard-set! text) → boolean?

  text : string?

procedure

(clipboard-get) → string?

procedure

(notify! title [body #:subtitle subtitle]) → boolean?

  title : string?
  body : string? = ""
  subtitle : string? = ""

procedure

(open-path path-or-url) → boolean?

  path-or-url : (or/c path? string?)

procedure

(reveal-path path) → boolean?

  path : (or/c path? string?)

procedure

(single-instance? app-id) → boolean?

  app-id : any/c

These helpers are best-effort integrations. Their failure semantics are separate from the WebView startup contract: the WebView is required for the application itself, while an optional integration may report failure without changing the application’s rendering model.

22 System Tray🔗ℹ

 (require glaze/tray) package: glaze

procedure

(tray-supported?) → boolean?

procedure

(make-tray #:icon icon    
  #:tooltip tooltip    
  #:menu menu    
  [#:on-event on-event]) → tray?
  icon : any/c
  tooltip : string?
  menu : list?
  on-event : procedure? = (lambda (e) (void))

procedure

(tray-set-tooltip! tray tooltip) → void?

  tray : tray?
  tooltip : string?

procedure

(tray-set-icon! tray icon) → void?

  tray : tray?
  icon : any/c

procedure

(tray-set-menu! tray menu) → void?

  tray : tray?
  menu : list?

procedure

(tray-close tray) → void?

  tray : tray?

The tray remains an optional capability. If its native backend is unavailable, Glaze may use an inert tray stub; this does not weaken the mandatory native WebView contract for the main window.

23 Security🔗ℹ

The local server binds to loopback and validates Host headers against 127.0.0.1, localhost, and [::1] to reduce DNS rebinding risk.

With #:api-token, API routes and the SSE stream require a capability. run-app opens the native WebView at a one-time bootstrap URL; the server exchanges the token for an HttpOnly cookie and redirects to the clean path. Programmatic clients may use the X-Glaze-Token header.

With #:capability, the server additionally enforces named permissions and resource scopes. Routes without #:permission, routes whose permission is absent, and resources outside a granted scope return 403 before the handler runs. Grant 'glaze:events to authorize the SSE endpoint.

This is defense in depth against casual local callers, not isolation from other processes running as the same OS user.

24 Update Checks🔗ℹ

 (require glaze/update) package: glaze

procedure

(check-update manifest-url 
  [#:current-version current-version]) 
 → (or/c #f hash?)
  manifest-url : string?
  current-version : string? = "0.0.0"
Checks a JSON manifest for a newer version. An optional sha256 field is passed through for artifact verification. Absolute and relative redirects are followed for at most ten hops.

procedure

(newer-version? candidate current) → boolean?

  candidate : string?
  current : string?

procedure

(verify-file-sha256 path expected-hex) → boolean?

  path : (or/c string? path?)
  expected-hex : string?
Returns #t only for a verified digest; #f also covers cases where verification could not be performed.

For installed applications, the signed updater is the preferred path. An update-manifest binds the application id, SemVer version, release channel, minimum supported version, staged rollout percentage, and a set of platform/architecture update-artifact values into one Ed25519-signed payload. Artifact records include a bounded size, SHA-256 digest, installer kind and arguments, and an optional second Ed25519 signature.

procedure

(fetch-update-manifest manifest-url 
  public-key 
  [#:key-id key-id 
  #:maximum-bytes maximum-bytes]) 
 → update-manifest?
  manifest-url : string?
  public-key : path-string?
  key-id : (or/c #f string?) = #f
  maximum-bytes : exact-positive-integer? = (* 1024 1024)
Downloads an HTTPS manifest with a hard byte limit and verifies its signature. If key-id is provided, a valid signature from any other release key is rejected. Redirects are followed for at most ten hops, and every target must remain HTTPS.

procedure

(select-update config manifest) → (or/c #f update-candidate?)

  config : updater-config?
  manifest : update-manifest?
Checks application identity, channel, exact SemVer precedence, minimum version, rollout bucket, platform, and architecture.

procedure

(download-update config    
  candidate    
  destination) → path?
  config : updater-config?
  candidate : update-candidate?
  destination : path-string?
Downloads to a partial file over HTTPS, enforces the configured maximum and signed artifact size, verifies SHA-256 plus the optional artifact signature, then atomically renames the verified file into place. Redirects are followed for at most ten hops, and every target must remain HTTPS.

procedure

(make-install-plan candidate    
  downloaded-path    
  [#:backup-path backup-path]    
  #:install install    
  [#:restart restart    
  #:rollback rollback]) → install-plan?
  candidate : update-candidate?
  downloaded-path : path-string?
  backup-path : (or/c #f path-string?) = #f
  install : procedure?
  restart : procedure? = void
  rollback : procedure? = void

procedure

(make-replace-install-plan candidate 
  downloaded-path 
  target-path 
  #:backup-path backup-path 
  [#:restart restart]) 
 → install-plan?
  candidate : update-candidate?
  downloaded-path : path-string?
  target-path : path-string?
  backup-path : path-string?
  restart : procedure? = void
Creates the atomic portable-artifact strategy, suitable for AppImage-style or self-contained deployments. Native installers use make-install-plan with a platform adapter that owns elevation and process handoff.

procedure

(execute-install-plan! plan) → any

  plan : install-plan?
Runs install and restart. If installation raises and the signed manifest allows rollback, the rollback callback runs before the original exception is raised.

Release automation can validate and sign a payload with raco glaze manifest-sign, then verify the wrapper and pinned key id with raco glaze manifest-verify.

25 File Dialogs🔗ℹ

 (require glaze/dialogs) package: glaze

procedure

(dialog-supported?) → boolean?

procedure

(pick-file [#:title title    
  #:directory directory    
  #:filters filters]) → (or/c #f path?)
  title : (or/c #f string?) = #f
  directory : (or/c #f path-string?) = #f
  filters : list? = '()

procedure

(pick-files [#:title title    
  #:directory directory    
  #:filters filters]) → (listof path?)
  title : (or/c #f string?) = #f
  directory : (or/c #f path-string?) = #f
  filters : list? = '()

procedure

(pick-folder [#:title title    
  #:directory directory]) → (or/c #f path?)
  title : (or/c #f string?) = #f
  directory : (or/c #f path-string?) = #f

procedure

(save-file-dialog [#:title title 
  #:default-name default-name 
  #:directory directory 
  #:filters filters]) 
 → (or/c #f path?)
  title : (or/c #f string?) = #f
  default-name : (or/c #f string?) = #f
  directory : (or/c #f path-string?) = #f
  filters : list? = '()

procedure

(dialog-message! title [body #:kind kind]) → boolean?

  title : string?
  body : string? = ""
  kind : symbol? = 'info
Shows a native message box; the kind is 'info, 'warning, or 'error.

procedure

(dialog-ask! title [body #:kind kind]) → boolean?

  title : string?
  body : string? = ""
  kind : symbol? = 'info
Shows a native yes/no box and returns the answer.

Dialogs are modal on the calling thread: while one is open, other Racket-side request handling waits (the WebView itself stays responsive in its own native loop).

procedure

(make-dialog-routes [#:prefix prefix    
  #:backend backend]) → (listof route?)
  prefix : string? = "api/dialog"
  backend : dialog-backend? = default-dialog-backend
Capability-gated frontend access: 'dialog:open and 'dialog:save — scope them with path-permission over the starting directory; a request without a start directory has nothing to authorize and is denied under a scoped grant — plus 'dialog:message and 'dialog:ask. JSON filters take the shape {"name": "Text", "extensions": ["txt", "*.md"]}; bare extensions gain the *. prefix and cancels map to JSON null. The generated client contains glaze.api.dialogOpen, glaze.api.dialogSave, glaze.api.dialogMessage, and glaze.api.dialogAsk. The backend is an injectable value so tests and headless CI never open real dialogs.

26 Deep Links and Launch at Login🔗ℹ

 (require glaze/deeplink) package: glaze
 (require glaze/autolaunch)

procedure

(ensure-url-scheme! scheme    
  [#:app-name app-name]) → any/c
  scheme : string?
  app-name : string? = scheme
Windows registers a user-scope URL protocol, Linux writes a desktop entry and uses xdg-mime when available, and macOS URL schemes are declared in the bundle at build time.

procedure

(auto-launch-set! name enabled?) → void?

  name : string?
  enabled? : boolean?

procedure

(auto-launch-enabled? name) → any/c

  name : string?

27 Packaging🔗ℹ

 (require glaze/build) package: glaze

procedure

(build-app [#:entry entry    
  #:name name    
  #:version version    
  #:publisher publisher    
  #:identifier identifier    
  #:icon icon    
  #:out-dir out-dir    
  #:embed-dlls? embed-dlls?    
  #:installer? installer?    
  #:sign sign    
  #:entitlements entitlements    
  #:no-hardened-runtime? no-hardened-runtime?    
  #:timestamp-url timestamp-url    
  #:notarize-profile notarize-profile    
  #:url-schemes url-schemes]) → path?
  entry : (or/c string? path?) = "main.rkt"
  name : (or/c #f string?) = #f
  version : (or/c #f string?) = #f
  publisher : (or/c #f string?) = #f
  identifier : (or/c #f string?) = #f
  icon : any/c = #f
  out-dir : (or/c string? path?) = "dist"
  embed-dlls? : boolean? = #f
  installer? : boolean? = #f
  sign : (or/c #f string?) = #f
  entitlements : any/c = #f
  no-hardened-runtime? : boolean? = #f
  timestamp-url : (or/c #f string?) = #f
  notarize-profile : (or/c #f string?) = #f
  url-schemes : list? = '()
Builds and distributes the application with raco exe and raco distribute. Windows GUI builds use raco exe --gui, so they may not have a visible console; this is why native WebView startup failures also attempt an OS-level error dialog.

Installer-toolchain absence may degrade an installer request to an archive with a loud warning. That packaging fallback is unrelated to runtime startup: the built application still requires its native WebView.

The publisher is written to native installer metadata. Keep the identifier stable across releases: it deterministically defines the WiX UpgradeCode and the NSIS Add/Remove Programs identity.

28 CLI Commands🔗ℹ

raco glaze init <name>                Create a native desktop project

raco glaze dev                        Run the project's native main.rkt

raco glaze build                      Build a distributable / installer

raco glaze help                       Show help

There is intentionally no browser-mode dev or serve command. Development and production use the same native WebView startup path.