Glaze
1 Quick Start
2 Application Lifecycle
run-app
make-api-token
current-api-token
3 Local Server
start-server
stop-server
open-browser
4 Java  Script Bridge
GET
POST
PUT
DELETE
request-json-body
json-response
api-response
error-response
define-api-routes
5 Event Push
make-event-bus
bus-broadcast!
bus-subscribe!
bus-unsubscribe!
bus-wait
6 Native Web  View
open-window
open-webview
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
6.1 Startup Dependency Feedback
7 System Integrations
sys-supported?
clipboard-set!
clipboard-get
notify!
open-path
reveal-path
single-instance?
8 System Tray
tray-supported?
make-tray
tray-set-tooltip!
tray-set-icon!
tray-set-menu!
tray-close
9 Security
10 Update Checks
check-update
newer-version?
verify-file-sha256
11 Licensing
issue-license
validate-license
license-valid?
machine-id
days-until-expiry
12 File Dialogs
dialog-supported?
pick-file
pick-files
pick-folder
save-file-dialog
13 Deep Links and Launch at Login
ensure-url-scheme!
auto-launch-set!
auto-launch-enabled?
14 Packaging
build-app
15 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.

1 Quick Start🔗ℹ

$ raco pkg install --auto glaze

$ raco glaze init myapp

$ cd myapp

$ 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 
  #:on-close on-close 
  #:on-error on-error 
  #:check-update check-update 
  #:current-version current-version 
  #: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) = #f
  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"
  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.

#:api-token may be a string or #t. With #t, Glaze generates a random capability token and uses a one-time bootstrap URL to set an HttpOnly cookie for the embedded frontend. #:on-error receives API handler failures. #:check-update wires an update manifest into the application lifecycle.

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.

3 Local Server🔗ℹ

 (require glaze/server) package: glaze

procedure

(start-server [#:port port 
  #:public-dir public-dir 
  #:api api 
  #:events events 
  #:api-token api-token 
  #:serve-api-client? serve-api-client?]) 
 → 
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
  serve-api-client? : boolean? = #t
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.

procedure

(stop-server shutdown-proc) → void?

  shutdown-proc : procedure?
Stops the server.

 (require glaze/browser) package: glaze

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

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) → route?

  path : string?
  handler : procedure?

procedure

(POST path handler) → route?

  path : string?
  handler : procedure?

procedure

(PUT path handler) → route?

  path : string?
  handler : procedure?

procedure

(DELETE path handler) → route?

  path : string?
  handler : procedure?

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.

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?

 (require glaze/api-macros) package: glaze

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(...).

5 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; a full per-subscriber backlog drops that event for the slow subscriber only.

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

6 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?    
  #:on-close on-close]) → webview?
  url : string?
  title : string? = "Glaze"
  width : exact-positive-integer? = 1024
  height : exact-positive-integer? = 768
  devtools? : boolean? = #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?    
  #:on-close on-close]) → webview?
  url : string?
  title : string? = "Glaze"
  width : exact-positive-integer? = 1024
  height : exact-positive-integer? = 768
  devtools? : boolean? = #f
  on-close : (-> any) = (lambda () (void))
Lower-level synonym of open-window with the same fail-fast contract.

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

6.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.

7 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.

8 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.

9 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.

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

10 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.

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.

11 Licensing🔗ℹ

 (require glaze/license) package: glaze

Glaze includes an offline RSA-2048/SHA-256 licensing helper backed by the system openssl command.

procedure

(issue-license #:private-key private-key    
  #:product product    
  #:subject subject    
  [#:expiry expiry    
  #:machine-id machine-id    
  #:out output]) → path?
  private-key : path-string?
  product : string?
  subject : string?
  expiry : (or/c #f string?) = #f
  machine-id : (or/c #f string?) = #f
  output : (or/c string? path?) = "app.license"

procedure

(validate-license license-file    
  #:public-key public-key    
  #:product product    
  [#:machine-id machine-id]) → hash?
  license-file : (or/c string? path?)
  public-key : path-string?
  product : string?
  machine-id : string? = (machine-id)

procedure

(license-valid? license-file    
  #:public-key public-key    
  #:product product    
  [#:machine-id machine-id]) → boolean?
  license-file : (or/c string? path?)
  public-key : path-string?
  product : string?
  machine-id : string? = (machine-id)

procedure

(machine-id) → string?

procedure

(days-until-expiry expiry) → exact-integer?

  expiry : string?

12 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? = '()

13 Deep Links and Launch at Login🔗ℹ

 (require glaze/deeplink) package: glaze

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.

 (require glaze/autolaunch) package: glaze

procedure

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

  name : string?
  enabled? : boolean?

procedure

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

  name : string?

14 Packaging🔗ℹ

 (require glaze/build) package: glaze

procedure

(build-app [#:entry entry    
  #:name name    
  #:version version    
  #: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
  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.

15 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 keygen [--out <dir>]       Create an RSA keypair for licenses

raco glaze license sign|verify        Sign or verify license files

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.