Lagrange, a C client for Geminispace that lets you switch its text stack off
A Beautiful Gemini Client
At a glance
- What is it?
- Lagrange is a cross-platform C client for the Gemini network, adding tabs, bookmarks, outlines and TLS client certificate identities to a protocol that ships none of them. Its CMake build can compile, borrow or delete the HarfBuzz and GNU FriBidi text layers, and that is where the real design choices sit.
- Who is it for?
- Adopt Lagrange if you read Gemini daily and want tabs, outlines and per-domain TLS identities without a browser extension, and keep the old renderer only when your text is plain left-to-right. Stay on your current client if you also need the web, since no HTTP client is documented.
- Can I use it commercially?
- Yes. BSD-2-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 37 days ago.
- What is it written in?
- Mainly C, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Gemini documents with tabs, outlines and TLS identities
Geminispace is a small link-oriented network, and a Gemini response gives you a header line, a body and a mimetype. Lagrange wraps that in the habits people expect from a web browser: smooth scrolling, inline image viewing, multiple tabs, visual themes, Unicode fonts, bookmarks, history and page outlines. Two interfaces live in the same codebase, an SDL window for desktop use and a Curses-based TUI for a machine with only a terminal. Identities are objects rather than a global setting, each one a TLS client certificate created inside the app and picked per domain, and typing a URL raises suggestions that search bookmarks, history and identities at once. Every Gemini domain also gets an autogenerated page style and symbol, so you can tell where you are before the text arrives.
A short C11 stack where the macOS dependency line drops zip
Compiling needs a C11 compiler such as Clang or GCC, CMake, `pkg-config` and zip. Against libraries it needs SDL 2.0.8 or newer, OpenSSL, libpcre, libunistring, GNU FriBidi and zlib. Two of the dependency lines in the guide diverge on zip, which the tools list requires. The Fedora recipe installs it:
sudo dnf install cmake zip SDL2-devel openssl-devel pcre-devel zlib-devel libunistring-devel fribidi-develwhile the macOS Homebrew line installs cmake, automake, sdl2, `[email protected]`, pcre, libunistring and fribidi but no zip:
brew install cmake automake sdl2 [email protected] pcre libunistring fribidiSo the Homebrew line on its own does not satisfy what the build asks for on step one. Audio is a separate decision: MP3 needs the mpg123 decoder, packaged as `mpg123` on Homebrew, `libmpg123-dev` on Ubuntu and `mpg123-devel` on Fedora, while Ogg Vorbis and WAV need nothing extra.
A clone command that pins a branch other than the default one
Two ways in produce different trees. A source tarball from the project's downloads page already contains HarfBuzz, GNU FriBidi and `the_Foundation`, while tarballs generated by GitHub or Gitea omit all three submodules, so check which archive you unpacked before blaming the build. Cloning recursively fetches them:
git clone --recursive --branch release https://git.skyjake.fi/gemini/lagrangeThat URL points at git.skyjake.fi, the author's own Gitea host, rather than GitHub; the branch it checks out is release even though the default branch is dev, and the last push was on 2026-09-16 with v1.21.1 dated 2026-09-03 as the newest build.
CMake configures in an empty build directory, not the source tree
Steps 4 through 7 are the whole build. Create a build directory, leave it empty, point CMake at the sources with the build type flag set for an optimised binary, then compile:
cmake {path_of_lagrange_sources} -DCMAKE_BUILD_TYPE=Releasecmake --build .The result is one executable, `lagrange` on POSIX systems, `lagrange.exe` on Windows, or `Lagrange.app` on macOS. Nothing is installed at this stage, so a build directory can be deleted and rebuilt whenever the source branch moves, which matters more here than in most projects because the submodules are pinned in the tarball you downloaded.
HarfBuzz and FriBidi: compile them, borrow them or drop shaping
If HarfBuzz and GNU FriBidi are missing from the system, both are compiled into the app, and the guide calls that a build without any additional dependencies. The same page then states that extra tools are required when those two libraries are compiled, and names them: HarfBuzz needs a C++ compiler, and GNU FriBidi cannot be compiled by CMake at all, so Meson and Ninja have to be present first. Those two statements do not agree, so assume the Meson, Ninja and C++ toolchain route. Setting `ENABLE_HARFBUZZ_MINIMAL` and `ENABLE_FRIBIDI_BUILD` to NO switches to `pkg-config` lookup instead, with the stated drawback that a system HarfBuzz is normally built against FreeType and GLib or ICU. Switch both libraries off and the old renderer returns, and its documented limit is exact: combining glyphs and complex scripts stop rendering, leaving non-complex left-to-right text such as English correct.
Package managers ship a binary and split macOS at 10.13
Every packaged route skips the build decisions entirely. macOS users get a cask,
brew install --cask lagrangeand the prebuilt binaries need macOS 10.13 or later, so anyone on 10.12 or older is redirected to MacPorts. Fedora and the RHEL or CentOS Stream 8 and 9 derivatives with EPEL enabled take the distro package,
sudo dnf install lagrangeopenSUSE Tumbleweed uses `zypper install lagrange`, GNU Guix uses `guix install lagrange`, and Flathub carries the app as `fi.skyjake.Lagrange` for Linux. A cask or a distro package offers no way to set `ENABLE_GAMEPAD`, change the frame or drop the text libraries, so packaging is a sensible default and a dead end for unusual builds.
A custom install prefix still deploys an XDG desktop file
Redirecting the install takes one variable, and the install target does one thing you did not ask for:
cmake {path_of_lagrange_sources} -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/dest/pathcmake --build . --target install`CMAKE_INSTALL_PREFIX` chooses where the compiled app lands, and the target also deploys an XDG .desktop file for launching it, a Linux convention that means nothing on macOS or Windows. Two further options carry real consequences. `ENABLE_DOWNLOAD_EDIT` lets the Preferences dialog move the Downloads directory, and the documentation marks it as something to set OFF in sandboxed environments where downloads must land in one specific place. `ENABLE_IDLE_SLEEP` makes the main thread sleep instead of waiting for events, which the guide ties to SDL 2.0.16 or earlier, where `SDL_WaitEvent()` carries a relatively high CPU cost. Since the stated minimum is SDL 2.0.8, a stock system SDL can sit in exactly that range.
The wrong tool for the web, for rollback, or for an iOS build
Three boundaries are worth stating plainly. First, Lagrange speaks Gemini: there is no HTTP or HTTPS client in the documentation and no fallback for links that leave Geminispace, so anyone who also reads the ordinary web keeps a second browser. Second, no rollback path is documented for the cask, the distro packages or the CMake install, and `ENABLE_DOWNLOAD_EDIT` changes where downloads land but nothing about where configuration is stored. Third, the repository carries an ios/ directory and three patch files, `sdl2-macos-ios.diff`, `sdl2.0.18-macos-ios.diff` and `sdl2.26-macos-ios.diff`, yet the guide explains no iOS build and no step for applying them. The build option table also ends mid-sentence at `ENABLE_IDLE_SLEEP`, so every option sorting after it stays undocumented.
Editorial conclusion
Adopt Lagrange if you read Gemini daily and want tabs, outlines and per-domain TLS identities without a browser extension, and keep the old renderer only when your text is plain left-to-right. Stay on your current client if you also need the web, since no HTTP client is documented. Check three things before switching: whether your distribution still packages SDL 2.0.8 or newer, whether ENABLE_HARFBUZZ stays on for the scripts you read, and whether you want the install target writing an XDG desktop file into your chosen prefix.
Frequently asked questions
Does Lagrange support the ordinary web, or only Geminispace?
Only Geminispace. The documentation has no HTTP or HTTPS client and no fallback for links that leave the network, so a web page opened from a Gemini link has no documented destination inside the app.
How do I build Lagrange against the system copy of HarfBuzz and FriBidi?
Set `ENABLE_HARFBUZZ_MINIMAL` and `ENABLE_FRIBIDI_BUILD` to NO, and CMake uses `pkg-config` to locate both libraries. The stated cost is that a system HarfBuzz is normally built against FreeType and GLib, or ICU.
Which macOS versions get a prebuilt Lagrange?
The prebuilt binaries need macOS 10.13 or later and arrive through the cask `brew install --cask lagrange`. Users on macOS 10.12 or older are pointed to MacPorts instead.
What breaks if I turn off text shaping in Lagrange?
Disabling both HarfBuzz and GNU FriBidi falls back to an older renderer that handles only non-complex left-to-right scripts such as English. Complex scripts and combining glyphs stop rendering correctly.
Which branch should I clone to build Lagrange?
The documented clone command uses the release branch at git.skyjake.fi, while the repository's default branch is dev. Cloning without pinning gives you a different tree from the one the build steps describe.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/skyjake-lagrange)Was this useful?