Getting started

Download

On an Apple Silicon Mac with macOS 26 or later, download Jonquil-0.1.0-macos-arm64.dmg (or the .zip) from the 0.1.0 release, open it and drag Jonquil to Applications. It's signed and notarized, and it bundles GTK, so it needs no Homebrew. Running a project's Jolt out of process still needs jolt on your login PATH and git (see Requirements), and each other dialect needs what's listed below. Intel Macs and Linux build from source, as the rest of this page describes.

Requirements

  • Jolt, v0.8.23 or later, tested on v0.8.27. Line counts, the last error's frames, Break on error and breakpoints need v0.8.24 or later. Building Jolt from source needs a threaded Chez Scheme 10.x on PATH; Jolt's own make provisions one if missing.
  • GTK4 and pango, to build or run from source. On macOS, brew install gtk4 pulls in pango and glib. Jonquil loads them at startup rather than linking against them. On Linux they come from your distribution; see Linux. The release download bundles them.

Jolt runs the tasks below (jolt app:run, jolt app:build, the tests) from bb.edn, so you don't need babashka.

Each other dialect needs one more thing, only when you use it:

  • jank (experimental): a jank binary; brew install jank-lang/jank/jank is simplest. See jank.
  • JVM Clojure: Java 17+, the clojure CLI, and an nREPL server you start. See JVM Clojure.
  • ClojureDart (experimental): a running app built with the ClojureDart fork that serves nREPL (private for now). See ClojureDart.
  • ClojureScript (experimental): npx shadow-cljs watch <build> in a shadow-cljs project, or just the Clojure CLI and node for a loose .cljs file. See attaching, node and browser.
  • Any nREPL server: the server, plus .jonquil.edn {:dialect :nrepl :nrepl {:port N}}. See Any nREPL server.
  • Jolt out of process, the default with a deps.edn: git on PATH and a network connection the first time, or a checkout of jolt-lang/nrepl. See Running Jolt out of process.

Running

jolt app:run                # opens an empty window
jolt app:run file.clj       # opens file.clj in the Definitions editor
jolt app:run ~/dev/project  # opens a folder: the file tree shows it, ready to pick a file

Building

jolt app:build   # writes ./jonquil (gitignored); it loads GTK4 and pango from the system at startup

Then ./jonquil, ./jonquil file.clj, ./jonquil a.clj b.clj (one tab each) and ./jonquil <folder> work like jolt app:run. Jolt is needed only for a project that runs Jolt as a child (one with a deps.edn), and Jonquil looks for it on your login shell's PATH; a loose file runs inside the binary itself.

The macOS quarantine note

The binary is unsigned on macOS. jolt app:build prints the fix if you need to hand it to someone else:

jolt build: note — on macOS this binary is unsigned; to share it,
  `xattr -d com.apple.quarantine ./jonquil` on the target, or sign it.

Building and running it on the same machine needs neither step.

The macOS app

jolt app:bundle    # builds ./jonquil, then ./Jonquil.app from it
jolt app:install   # the same, then moves it to ~/Applications/Jonquil.app and registers it

jolt app:bundle writes ./Jonquil.app (gitignored) with the icon and identifier net.b12n.jonquil. It declares Clojure source and Folder document types as Alternate, so Jonquil shows under Open With and as a Dock drop target but is never the default app. jolt app:install replaces an existing ~/Applications/Jonquil.app only if it is a Jonquil bundle.

The app still loads GTK4 and pango from Homebrew, so brew install gtk4 stays a requirement. It isn't notarized either, so the quarantine note applies when you copy Jonquil.app to another Mac. The release download is the self-contained, notarized build (see Download).

After a plain jolt app:bundle, the checkout's ./Jonquil.app may show in Open With next to the installed one. Delete it or run lsregister -u ./Jonquil.app (it lives under /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/).

A self-contained release build

jolt app:release        # target/Jonquil.app and target/Jonquil-<version>-macos-arm64.zip
jolt app:release-check  # launch it with /opt/homebrew unreadable

jolt app:release makes an app that runs on a Mac without Homebrew. The build Mac still needs Homebrew's GTK, because that is what gets bundled, meson and pkgconf (brew install meson pkgconf) for the one library built from source (below), and the Xcode Command Line Tools (xcode-select --install) for otool, codesign and, when notarizing, xcrun notarytool. deps.edn's GTK libraries, and every library they load (42 today, about 27 MB), are copied into Contents/Frameworks and relinked to find each other.

Contents/MacOS/Jonquil is a small launcher. It puts that folder on DYLD_LIBRARY_PATH and runs Contents/MacOS/jonquil-bin. A launcher is needed because Jolt reads an @executable_path library path as relative to the project. The binary isn't called jonquil, because macOS file names ignore case and it would be the launcher.

Each bundled library's licence text goes in Contents/Resources/licenses/, with SOURCES.txt naming its version and where its source is.

The app is signed ad hoc. With JONQUIL_SIGN_IDENTITY set, it's signed with that identity instead. The task ends with these checks: - no bundled file loads from, or keeps an rpath into, Homebrew; - every bundled library's only rpath is @loader_path; - the plist lints; - the signature verifies; - the launcher is executable; - GTK's file-chooser schema loads from the bundle.

GTK's GSettings schemas are compiled into Contents/Resources/share/glib-2.0/schemas, and the launcher adds that folder to GSETTINGS_SCHEMA_DIR. Without them, GLib aborts at the first file dialog with "No GSettings schemas are installed on the system".

It fails at the start when this Mac has no Homebrew GTK to bundle. Once Jonquil has loaded, it drops DYLD_LIBRARY_PATH from its own environment (jonquil.bundle-env), so git, node or jank that it starts load their own libraries, not the bundle's.

jolt app:release-check launches the app for 10 seconds in a sandbox that can't read /opt/homebrew, and fails if it exits early or loads a library from anywhere but the bundle and the system.

Limits of this build: - It needs macOS 26 and Apple Silicon. Homebrew builds its libraries for the macOS they were built on, and the plist's minimum version is read from them. - One library is built from source. GTK links cairo's script interpreter, and Homebrew's build of it imports lzo, which is GPL-2.0. So the release builds that one library itself: - from the pinned cairo source, with -Dlzo=disabled; - the source must be the version of Homebrew's cairo; - it's built once and cached in ~/.cache/jonquil/cairo-script/<version>-nolzo/.

A check fails the release if lzo is ever bundled. The task warns when a Homebrew package is older than its source link, or when a package ships no licence file.

Notarized builds

jolt app:release    # build target/Jonquil.app first
jolt app:notarize   # sign, notarize and staple it; write the zip and the dmg

jolt app:notarize turns the app app:release built into one that Gatekeeper opens without a warning. It uploads to Apple's notary service, so it needs a network and an Apple Developer account. It does this:

  1. Signs with the Developer ID. The identity is imported into a throwaway keychain for the run, never the login keychain. Signing goes inside-out: the bundled libraries, jonquil-bin, then the app. It uses the hardened runtime and a secure timestamp.
  2. Uses two entitlements (resources/Jonquil.entitlements), which were measured: - com.apple.security.cs.allow-jit, because Chez Scheme writes machine code at run time, and without it Jonquil crashes at launch; - com.apple.security.cs.allow-dyld-environment-variables, because the launcher's DYLD_LIBRARY_PATH is how the binary finds the bundled GTK.

Library validation stays on, so the app loads only libraries signed by the same team. 3. Notarizes and staples the app with notarytool and an App Store Connect API key, then rebuilds Jonquil-<version>-macos-arm64.zip from it. 4. Builds Jonquil-<version>-macos-arm64.dmg. It holds the app and an Applications shortcut, and is itself signed, notarized and stapled. 5. Checks both with spctl ("Notarized Developer ID") and stapler validate. It fails, keeping Apple's log in target/notarization-<id>.json, if Apple doesn't accept a submission.

Apple's Developer ID G2 intermediate certificate is committed at resources/certs/DeveloperIDG2CA.cer, checked against a pinned SHA-256, so signing works on a Mac that lacks it.

The identity and the API key are read with pass from the store in PASSWORD_STORE_DIR (~/.password-store when it's unset). The entry names are in dev/app_notarize.clj: the Developer ID .p12 and its password, and the App Store Connect key, its ID and its issuer. No secret is printed. Each is decoded into a 0600 file, or held in memory, and the throwaway keychain and every decoded file are deleted when the run ends, whether it succeeds or fails.

Where tools come from in a Finder launch

A Finder or Dock launch inherits a bare PATH, so jolt, jank, clojure and node wouldn't be found. When PATH has none of /opt/homebrew/bin, /usr/local/bin, ~/.local/bin and ~/bin, Jonquil asks your login shell once ($SHELL -il -c, zsh if unset) and passes the result to the Jolt child, jank and ClojureScript. If the shell fails or prints nothing usable, Jonquil adds whichever of those folders exist. The console says which happened:

; PATH from your login shell (zsh).
; PATH from well-known folders (your login shell timed out).
; PATH from well-known folders (your login shell failed).
; PATH from well-known folders (your login shell printed no PATH).

A terminal launch usually has one of those folders already, so no lookup runs and no line appears.

Linux

Jonquil builds and runs on Linux (tested on Ubuntu 26.04 with GTK 4.22, in Docker on arm64 and amd64, see Development).

Requirements

  • GTK 4 and pango from your distribution. On Ubuntu that is libgtk-4-1 and libpango-1.0-0, plus adwaita-icon-theme. Nothing is linked at build time.
  • Jolt v0.8.23 or later. On x86_64 take the binary from Jolt's release page. There is no Linux arm64 binary, so build it from source (make provisions its own Chez Scheme).
  • A desktop session. Checks ran under X11; Wayland is untested.

Build and install

jolt app:build       # writes ./jonquil
jolt app:install     # the same build, then installs it for your user
jolt app:uninstall   # takes it out again

./jonquil, ./jonquil file.clj, ./jonquil a.clj b.clj and ./jonquil <folder> behave as they do on macOS.

jolt app:install writes four paths, all under ~/.local/share:

PathWhat it is
~/.local/share/jonquil/jonquilthe binary
~/.local/share/applications/net.b12n.jonquil.desktopthe launcher
~/.local/share/icons/hicolor/256x256/apps/net.b12n.jonquil.pngthe icon
~/.local/share/mime/packages/net.b12n.jonquil.xmlthe MIME types

The MIME package defines text/x-clojure, application/edn and text/x-jank, so Jonquil appears in Open With but is never the default. Nothing outside ~/.local/share is touched, and the installer runs update-mime-database and update-desktop-database when they are on PATH. Running it again replaces the earlier install; app:install and app:uninstall refuse a launcher without its X-Jonquil-Install=1 line. To start from a terminal afterwards, run ~/.local/share/jonquil/jonquil [file or folder...], or gtk4-launch net.b12n.jonquil [file or folder...] (Ubuntu's libgtk-4-bin; GTK 3's gtk-launch works too).

Where tools come from in a launcher start

The lookup above runs here too. When SHELL is unset, it uses the shell in your /etc/passwd entry, or /bin/sh (always, for LDAP accounts). It is skipped when any of the four folders is already on PATH, so a tool living only in ~/.local/bin isn't found if /usr/local/bin is on it. Start from a terminal, or put the tool in /usr/local/bin.

Keys

Ctrl is the command key, so the shortcuts the guide writes as Cmd/Ctrl work as Ctrl chords. Ctrl+Q is quick doc, as on macOS, and does not quit. Ctrl+W closes the tab, and on the last tab it closes the window, which quits. There's no separate quit chord.

Settings

Jonquil keeps a few choices between runs in $XDG_CONFIG_HOME/jonquil/settings.edn, or ~/.config/jonquil/settings.edn when that variable is unset, blank, or not an absolute path:

{:theme :navy
 :file-tree {:visible? true :width 240}
 :dialect :jolt
 :reopen-tabs? true
 :remember-session? true}
  • :theme: the theme you last picked with Cmd/Ctrl+Shift+M, one of :light, :dark, :solarized, :navy or :auto (follow macOS's light or dark setting).
  • :file-tree: whether the tree was showing and how wide, saved when you toggle it and when you quit.
  • :dialect (hand-written, optional): the dialect an untitled buffer or a file outside any project starts in (see Choosing a dialect).
  • :reopen-tabs? (hand-written): false stops Jonquil saving and reopening the last session's tabs. Omitted or true keeps it on.
  • :remember-session? (hand-written): false stops Jonquil recording what each project evaluated and offering it back as a replay (see Last session's replay). Omitted or true keeps it on. A project's .jonquil.edn can override it either way.
  • :mcp-listen? (hand-written): true makes each window serve Jonquil's MCP tools on a loopback socket, so an agent can attach to an open window with jonquil --mcp-attach (see Driving Jonquil from an agent).

The tabs live next door in sessions.edn, one entry per project folder, newest first, at most 20 (see Tabs from last time). It changes as you open and close tabs, which is why it's separate from the file you edit by hand.

A missing file means the defaults; an unreadable file or unusable value falls back per key with one console note. Saving changes only its own key, refuses to overwrite a file it couldn't read, and is atomic. Delete the file to reset everything, and sessions.edn to forget remembered tabs. The replay records are in replays/ next to it.

See Using Jonquil for the rest of the window, and Development for the test suite, the self-test harnesses, and the lint and format tasks.

This page is docs/guide/getting-started.md in the Jonquil repository.

Enter to open · Esc to close