__ __ __ ___ | /\ / _` |__) /\ |\ | / _` |__ |___ /~~\ \__> | \ /~~\ | \| \__> |___
Gemini is a simple protocol for serving content over the internet. It specifies a Markdown inspired format allowing basic plain text document markup. Compared to HTTP and HTML, Gemini is vastly simpler and easier to work with.
=> Project Gemini FAQ | Protocol and 'text/gemini' specification
Lagrange is a GUI client for browsing Geminispace. It offers modern conveniences familiar from web browsers, such as smooth scrolling, inline image viewing, multiple tabs, visual themes, Unicode fonts, bookmarks, history, and page outlines.
Like Gemini, Lagrange has been designed with minimalism in mind. It depends on a small number of essential libraries. It is written in C and uses SDL for hardware-accelerated graphics. OpenSSL is used for secure communications.
=> About Lagrange | SDL: Simple DirectMedia Layer | OpenSSL: Cryptography and SSL/TLS Toolkit
Modern web browsers are complex beasts. In fact, they are so complex that one can create a fully functional virtual machine inside one and run another operating system!
=> Windows 95 on DOSBox (using Emscripten)
If one seeks to just read text and view images, this is absurd overkill. Having a universal platform that runs everywhere and on everything is clearly a valuable notion, but it comes with a hefty price tag. The software stack towers ever higher, and hardware needs to be ever more powerful and complicated to run it well. However, everything on the internet doesn't have to rely on this behemoth.
One way to browse Gemini content is via web browser extensions or proxies that translate the content for the web. This may be a sufficient and easy solution for you. However, native clients such as Lagrange also benefit from the simpleness of the protocol and the content. The experience can be optimized, and the software runs well even on simple hardware like the Raspberry Pi.
Lagrange's user interface is modeled after web browsers:
Tip: Try pressing ${CTRL+}5 now to see the page outline.
The user interface has been designed for both mouse and keyboard based use. When using a mouse the most commonly needed features are available via context menus, for example by right-clicking on the currently open page. Keyboard shortcuts on the other hand can provide a very quick and fluid user experience. For example, to open the "CAPCOM" bookmark, one might use the following keyboard shortcuts:
The URL input field can be found in the traditional location in the navigation bar. It can be accessed quickly by pressing ${CTRL+}L. You can then type or paste an URL and press Enter to load the page.
As you enter text in the URL input field, Lagrange starts looking for matches in bookmarks, history, identities, and content of cached pages. This provides an easy way to return to a page you've been on recently. Press Tab or ↓ to switch input focus to the results.
Search within cached pages is limited to the (small) set of pages that Lagrange keeps in memory for back navigation. Search terms are case insensitive, and if many words are entered, they are all required to appear in the specified order in any matched content.
Note that the navigation stack is saved to a file when Lagrange is shut down and restored on the next launch. This means the next time you launch Lagrange, you can still search the contents of past pages. However, navigation stacks are tab-specific, so closing a tab will delete its history as well.
You can also make online search queries via the URL input field. When a search URL is configured on the "Network" tab of Preferences, text entered in the URL field is passed to the search URL as a query parameter. A search query will only occur when Enter is pressed while the [Search Query] indicator is visible.
The type and destination of a link are indicated by the link's icon and color: ➤ links to the same domain, and 🌐 to a different domain. The colors are:
Link colors remain the same regardless of which color theme is being used for page content. (Color themes are discussed in the Customization section.)
When you move the mouse cursor over a link, additional information will appear: the destination domain, with the URL scheme shown for non-Gemini links, and the date of the last visit to the URL.
If a link would normally use the default ➤ icon but there is an Emoji at the beginning of the link label, that Emoji is used as the link icon instead. In these cases, you can always assume that the link is a Gemini link whose destination is the same domain that you're currently on.
When navigating to a new page, the old page is cached in memory. If you navigate back, the cached copy of the page is restored. Think of it as rewinding time — you return to a past time as if nothing had happened. The same applies to forward navigation; cached pages are loaded if available. This allows back and forward navigation to happen instantly, without any network requests.
The page cache is saved to a file when Lagrange is shut down so it can be restored on the next launch.
Maximum size of the cache can be configured on the "Network" tab of Preferences. Note that the entire cache is kept in memory at runtime. When the cache fills up, the oldest and largest page content is removed — both factors contribute to the removal. You can set the maximum size to 0 to disable caching altogether, but that will also disable the page content quick search feature.
Holding down ${CTRL} when left-clicking on a link causes it to open in a new tab. Alternatively, middle-clicking a link has the same effect.
${SHIFT+}${CTRL+}click opens the link in a new background tab.
Right-clicking on a link shows a link-specific context menu. From there, you can also open the link in a new background tab, which will keep the current tab open.
Holding down ${SHIFT} when left-clicking on a link opens it in split view mode on the other side of the split. When in split view mode, your tab pinning preference will determine the tab on which clicked links will open. See section 1.9 for more information about split view modes.
Lagrange has two modes for navigating links with the keyboard. The first uses a modifier key (${ALT} by default): while the modifier is pressed, alphanumeric shortcuts are shown for each visible link in the window. You can then press the corresponding key, still holding the modifier, to open a link. The second mode is based on letters only and focuses on the page row keys. You press and release the activation key ("F" by default) to show the link shortcut letters. In this mode the shortcuts are not in alphabetic order, but instead roughly sorted based on proximity to the F and J keys. You may find this easier to use since it is a simple sequence of key presses without using modifiers, allowing one to keep fingers on or near the home row.
You can give it a try now with the link below. Either hold down ${ALT}, or press and release F.
Press ${CTRL+}T to open a new tab, and ${CTRL+}W to close the current tab. Right-clicking on buttons in the tab bar shows a context menu for additional tab-related functions.
The set of open tabs and their full contents are saved when you quit the application and restored when relaunch it.
When you open a tab, and the URL of that tab is found in a link on another page, that link will be highlighted. This way you can conveniently see which links are currently open in other tabs. These highlights will assist you in navigating hierarchies and index pages.
A tab can be set to auto-reload at given intervals. The setting is remembered until the tab is closed. This is helpful if you keep a periodically updated page open for longer periods of time.
The feature can be found in the page context menu: right-click and select "Set Auto-Reload...".
The sidebars can be toggled via menus or by pressing ${SHIFT+}${CTRL+}L or ${SHIFT+}${CTRL+}P (for the left/right sidebar, respectively). Both sidebars have five tabs:
${CTRL+}1 through ${CTRL+}5 switch between the left sidebar tabs, or hide the sidebar if the current tab's key is pressed. You can also press Escape to dismiss sidebars.
Press ${CTRL+}D to bookmark the currently open URL.
Newly added bookmarks appear at the bottom of the Bookmarks sidebar. You can drag and drop items in the list to change their order.
In addition to a title, bookmarks can have tags. Some tags have a special meaning, but you are free to enter whatever you want in the tags field. In quick search results, tags are given extra weight so they appear higher in results.
By default, bookmarks are assigned random icons. You can enter a custom icon for a bookmark in the bookmark edit dialog to make it easier to recognize a particular site at a glance. The icon must be a single Unicode character. It will appear in the bookmark list, tab titles, feed entry list, quick search results, and page top banners.
Press ${SHIFT+}N to create a new bookmark folder. Newly added folders appear in the bottom of the bookmark list.
You can drag and drop bookmarks in and out of folders. Folders can also be nested inside each other. Clicking on a folder heading will toggle its fold state, either hiding or showing its contents. The fold state is remembered across sessions.
=> The special page "about:bookmarks" is used for exporting bookmarks out of Lagrange.
It has three different modes that can be opened via menus:
The generated page can be saved to Downloads or you can copy all or parts of it to the clipboard.
To import all links on a page as bookmarks, use the page context menu to select "Import Links as Bookmarks...". You can also right-click on individual links and select "Bookmark Link...".
Importing links on a page will not cause any folders to be (re)created. To fully transfer your bookmarks, tags, custom icons, and folders, consider copying the entire "bookmarks.ini" file (see section 3.5).
This version of Lagrange does not support automatic bookmark synchronization. By default all bookmarks are stored locally. However, a remote bookmarks feature is provided to allow accessing a set of bookmarks from everywhere. This way you can set up your own centralized bookmark pages or use any public Gemini page as a source of bookmarks.
A local bookmark can be turned into a source of remote bookmarks via the Bookmarks context menu. Right-click the bookmark and select "Use as Remote Source". The ⤓ icon is used to indicate remote items in the Bookmarks list. Remote sources are checked when launching Lagrange and manually with the "Refresh Remote Sources" menu item.
All links found in the remote sources become remote bookmarks and are included in the Bookmarks list. If a URL is already bookmarked locally, any remote bookmarks with a matching URL are not shown.
Note that remote bookmarks are read-only: they cannot be edited or tagged. This means you cannot subscribe to a remote bookmark without first making a local duplicate of it. If you want to make local copies of all the links in the remote source, open the remote source page and use the "Import Links as Bookmarks..." context menu item on it. After refreshing remote sources, the remote copies of the bookmarks will not be shown any more.
You may be familiar with XML-based RSS and Atom feeds from the web. The Gemini equivalent of these is Gemini feeds. A Gemini feed is simply a regular 'text/gemini' page that contains one or more links whose labels are formatted in a particular way. This makes it very easy to write pages that clients can subscribe to.
=> See "Subscribing to Gemini pages" for more information.
Lagrange supports Gemini and Atom feed subscriptions. Atom feeds are automatically translated to the Gemini feed format so they can be viewed and subscribed to like a normal 'text/gemini' page. RSS feeds are not supported.
Subscriptions are managed via bookmarks. When you subscribe to a feed page, a bookmark is created and the special "subscribed" tag is applied on it. In the Bookmarks list, this is indicated by a ★ icon. There is no other difference between normal bookmarks and feed subscriptions — you may tag any bookmark as "subscribed" and Lagrange will look through it for feed-style links. The bookmark title is used as the feed title. This defaults to the top heading of the feed index page, but you can edit it to suit your needs.
Feeds are refreshed periodically while Lagrange is running, and also immediately after launching if it has been a while since the previous refresh. You may also manually refresh all feeds via the menus or by pressing ${SHIFT+}${CTRL+}R.
The Feeds sidebar tab displays recent feed entries. From there you can open entries and mark them as read/unread.
To see all entries from all feeds, open the "Feed entries" page. This page also shows how long has it been since the previous feed refresh.
=> about:feeds
Gemini uses TLS client certificates for user/session identification purposes. Unlike on the web where user identity tracking is covert and automatic, client certificates must be manually taken into use, and you are able to define how long each certificate remains valid. The term "Identity" is used in Lagrange to refer to client certificates.
The Identities sidebar tab shows all identities known to Lagrange. Press ${CTRL+}4 to show it. This includes identities created in Lagrange and any identities based on imported X.509 certificates.
Click on the 👤 button in the navigation bar and select "New Identity...". The shortcut for this is ${SHIFT+}${CTRL+}N.
Consider any information you enter in the certificate as public — only the Common Name is required to be non-empty. The generated certificate will use the Common Name as the issuer and subject of the certificate, making it clear that the certificate is self-signed. The other required field is the expiration date in "Valid until". Entering a year is sufficient, and means that the certificate is valid until the end of that year.
The "Temporary" option prevents the identity from being saved persistently — it will be deleted as soon as you quit Lagrange. Otherwise, the certificate and its private key are written to .crt and .pem files.
You will need to select an identity when you encounter this error message:
🔑 Certificate Required
Clicking on an identity in the sidebar will toggle it on/off for the currently open URL. On subsequent page loads, the certificate will then be sent to the server when the URL or any URL under it is fetched. You can click on the 👤 button in the navigation bar to see which identity is being used for the current page.
As the sidebar is not keyboard-navigable, note that identities can also be accessed via lookup results. Identities matching the search terms are shown as the last category in the lookup results list. From there, one can toggle an identity for the current page, or stop using it on all pages.
To import an existing X.509 certificate as an identity, press ${SHIFT+}${CTRL+}I or click on 👤 and select "Import..." (macOS: Identity menu → Import...).
When the Import Identity dialog opens, it checks the currently open page for any certificates and private keys in PEM format. If found, these are automatically loaded in. You can also press ${CTRL+}V to paste PEM-formatted certificates and/or private keys, or drag-and-drop a PEM-formatted .crt/.key files on the Lagrange window while the dialog is open.
Alternatively, you can manually copy the certificate files so Lagrange can find them. At launch, Lagrange looks through its "idents" directory to see if any new certificates have been copied there. (See "Runtime files" below for the location; it is platform-specific) The file format must be PEM. Both a certificate (.crt) and its private key (.key) must be found in "idents" and they must have matching file names. For example:
Lagrange will add a note to the imported identities to mark them as "Imported".
Press ${CTRL+}S to save the contents of the current page to the Downloads folder. This works on all pages regardless of whether Lagrange can display the contents or not.
When image or audio content is displayed inline, you can right-click on it and select "Save to Downloads" in the context menu.
The 🔃 button on the right side of the URL input field is the Reload/Stop button — it reloads the current page or stops an ongoing download. During large downloads, an additional progress indicator appears next to the Stop button.
The location where downloaded files are saved can be changed in Preferences. The default location is "Downloads" in your home directory.
Titan is a sister protocol to Gemini that enables sending arbitrary amounts of data from a client to a server. The Gemini protocol itself only enables sending up to 1024 bytes of data in a request. Furthermore, the request URL also counts against that limit, and the sent data must be percent-encoded so it can be parsed as a valid URL. Consequently, Gemini clients can only send very limited amounts of data to a server. Titan solves this by expanding the request so that the request URL is followed by a payload field. When it comes to TLS, Titan is equivalent to Gemini, so the same server and client certificates can be used with both.
=> Titan Protocol (by Alex Schroeder)
While Titan and Gemini are related, Titan is a separate protocol and regular Gemini servers are not expected to support it. Whether it makes sense to allow clients to upload large amounts of data is a service-specific question. For example, a server that hosts a gemlog could enable Titan uploads for submitting new posts, or editing existing posts by uploading a revised version.
As far as Lagrange is concerned, Titan is just one of the supported URL schemes. Whenever you try to open a "titan://" URL, no matter if it is manually entered into the URL field, or by clicking on a link, opening a bookmark, feed entry, or via a redirect, a dialog will open where you can specify the data to upload.
The Titan upload dialog supports two ways to enter the data:
The upload token is a feature of Titan where servers can require a certain token text/passphrase for uploads. It is up to the server how this is interpreted. It could be used as a simple password, or even a command to further instruct the server about what to do with the uploaded data. Please refer to the server's instructions about what to enter here. The token may also be left empty.
The text entered into the upload dialog's main text field is protected against accidental closing of the dialog or the application, or a crash. The previous text is restored when the dialog is reopened. The text field contents are only cleared when the submitted Titan request has been successfully completed.
By default, only one tab is visible at a time in the application window. However, sometimes it is beneficial to see two pages at once. For example, many capsules have top-level menus or lists of articles, and keeping the menu/index visible on the side makes navigation less cumbersome.
Split view mode divides the UI into two equivalent parts. You can have multiple tabs open in each split. Closing all tabs on one side will remove the split and return back to the normal unsplit mode.
View splitting is primarily controlled using the view split menu. The default keybinding for showing it is ${CTRL+}J. For convenience it is recommended to use the shortcuts listed in the menu to change split modes. For example, the sequence ${CTRL+}J 2 can be used to quickly activate a 50% horizontal split, and ${CTRL+}J 1 will merge all open tabs back into the normal unsplit view.
Another way to activate split view mode is to click on a link while holding ${SHIFT}.
Each split has its own sidebars, which means that in split view mode you can have a total of four sidebars open at the same time.
At any given time, one of the splits has keyboard focus. This is indicated by a colored line at the top of the section, and some UI elements will be dimmed out on the unfocused side.
To switch keyboard focus between the sections, you can use the Next/Previous Tab keybindings or Ctrl+Tab. Next/Previous Tab is particularly convenient as it cycles through all open tabs, jumping to the other side of the split when appropriate.
You may also press Tab to cycle input focus between all the URL input fields.
While it is sometimes useful to simply have two independent browsers open side by side, by default view splitting is meant to assist in navigating hierarchies and lists. In the typical use case, you'll have a menu or an index page on the left, and a content page open on the right. Links clicked on the left will automatically open on the right.
This is called "pinning" and the behavior can be configured in Preferences. The "Split view pinning" setting on the "General" tab of Preferences controls where links get opened in a split view. There are three modes available:
The default pinning mode is "Left Tab".
The ◧ indicator is shown in the URL input field when the current tab is pinned.
"file://" URLs can be used for accessing local files and directories. File types known to Lagrange, such as .gmi, .txt., .png, .jpg, and .zip, can be viewed inside the application. In this release, types of local files are detected solely based on the file extension.
When viewing a directory, its contents are shown as a list of links. A similar page is shown when viewing a directory inside a ZIP archive.
Note that ZIP archives are not decompressed while browsing their directory structure. Each request that accesses a compressed file in an archive will cause only that particular file to be decompressed. This may cause slow response times when dealing with large compressed files.
Gempub is an e-book/archival format that is essentially a set of Gemtext files stored in a ZIP archive.
Lagrange can be used as a Gempub reader. When a .gpub file has been saved locally, you can open it in the application to see the book cover page. On the cover page you will find a link that opens the contents of the book. When clicked, split view mode is automatically enabled and both the book index page and the first chapter are shown (unless split view pinning has been disabled).
You can find a number of settings in Preferences to customize the user interface and page contents.
One important characteristic of Gemini is that you remain in control of what gets loaded and when. The browser will not suddenly fetch a ton of images, autoplay videos, or make surreptitious connections to any tracking servers — each network request is purposeful and manually triggered. With this in mind, the "Load image on scroll" option is provided to assist keyboard-only browsing and to facilitate a focused reading experience. When enabled, if there is an image link visible on the page and you press Space or ↓, it will be loaded and shown inline instead of the view scrolling down. This allows you to read and see all the content of the page while only tapping on a single key on the keyboard.
The "Open archive indices" option controls whether index.gmi pages are automatically opened while browsing the contents of a ZIP archive. The purpose is to simulate the behavior of a Gemini server where opening a directory will by default show its index page. Enabling this option makes navigating an archived copy of a capsule a more streamlined experience.
"Split view pinning" controls which tab links will be opened on when browsing in split view mode. The default mode is "Left Tab", which means that the page in the left tab is pinned (remains unchanged) when clicking on a link. For more information, see section 1.9.
Lagrange supports configuring dark and light UI modes separately.
There are four different color schemes for the UI: two for dark mode, and two for light mode. These can be selected on the "Window" tab of the Preferences dialog:
On macOS, Lagrange will automatically switch between dark and light modes if the "Use system theme" setting is enabled. On other platforms you'll need to switch manually.
On Windows, an option is provided to use a custom window frame. This provides a more consistent visual style for the application. However, the custom frame overrides default behaviors of the window, so if you are a Windows power user expect it to not support all the special interactions. For example, when pressing the Windows+Left/Right key, the window is resized to take over one half of the screen. Normally, Windows would then prompt to select another window to fill the rest of the screen, but this does not happen with the custom window frame.
Options for wide window sizes:
Page content color themes are selected on the "Colors" tab of Preferences. The "Dark theme" is active in dark UI mode, and the "Light theme" is active in light UI mode.
This version of Lagrange supports TrueType fonts. To use a new font in the app, simply view the .ttf file in the app and a page footer action is available for performing the installation. For example, try drag-and-dropping a .ttf file on the window. Alternatively, you can manually copy the font to the "fonts" subdirectory of the user-specific configuration directory (see section 3.5).
The "Fonts" tab of the Preferences dialog contains options that affect the appearance of text in page content, the user interface, and text rendering in general.
To be precise, on this tab you can select which typefaces to use for certain elements of the page and the UI; picking a particular font (including its size, weight, style, etc.) for specific elements is not possible. Lagrange controls the size and styling of text by choosing fonts according to your theme and typeface preferences. The term "font" is often used to mean "typeface" in this document and in the app UI.
The fonts for document headings, body text, preformatted blocks, and the user interface can be chosen separately. There is also a separate font for monospaced body text, used when the protocol-specific "Monospace body" option is enabled.
There are two built-in fonts available:
There is also an "Iosevka (compact)" variant that actually uses the font's original line spacing. It is the default for preformatted blocks to avoid gaps between lines of ASCII art, but not for body text for better legibility.
See sections 2.4.4 and 5 for more details about font management, compatibility, and configuration.
The "Monospace body" option causes all pages to be displayed in a monospace font. For example, most Gopher content has been written with the assumption of a monospace font, so it may provide a better reading experience to enable this for Gopher pages. The selected "Monospace font" is applied to the entire document.
Handling of whitespace in page content also changes when this option is enabled: spaces are no longer normalized so if the source uses multiple spaces somewhere, those are shown as-is.
=> ANSI escape code (Wikipedia):
ANSI escape sequences are a standard for in-band signaling to control cursor location, color, font styling, and other options on video text terminals and terminal emulators.
Sometimes these codes are used for things like colored ASCII art. However, Lagrange is not a terminal emulator so only a very minimal set of codes have any effect; the rest are ignored.
The "ANSI escapes" setting controls which ANSI escape codes are enabled:
A warning banner is displayed if any codes are detected on a page. This helps you be aware of potential visual artifacts, like text color that is being forced to black regardless of the page background color. Click on the warning to dismiss it on a per-site basis.
If you serve content via Gemini, please be aware that ANSI escapes may not be supported by clients, and may in fact disrupt the behavior of a client in unexpected ways. For instance, consider a screen reader or a web proxy that doesn't filter out the codes. Only employ ANSI escapes when the user has somehow indicated that is their preference.
The "Glyph warnings" option controls whether a warning will be shown when a page is missing one or more characters. This is typically due to the page being in a language that is not covered by any of the installed fonts. Clicking on the glyph warning banner opens the font management page.
"Smoothing" enables or disables glyph antialiasing. In this version, it is recommended that smoothing is always enabled. This is because the text renderer does not support hinting of any kind, which means that without smoothing, glyph shapes will be severely distorted. You may still want to try disabling this option if antialiasing causes eye strain for you.
Open "about:fonts" to see all the installed fonts, and enable, disable or uninstall individual fontpacks. At the top of the page, there is a link to the skyjake.fi Font Library: a curated collection of fontpacks that can be freely distributed. From there you can conveniently install more fonts specifically tuned for Lagrange.
The rest of the page is divided to two sections: enabled and disabled fontpacks. Individual packs can be disabled, so they remain installed but not in use. (Tip: Use the Outline sidebar to see a list of all packs, if there are many.)
Click on the "View file" links to see what each fontpack contains. The fontpack format is described in section 5: it is simply a ZIP archive with some metadata and a bunch of font files. When you install a TrueType font (i.e., copy it to the user's fonts directory), it is treated as a fontpack containing a single file, although no ZIP archive is created for it. Use the "View fontpack.ini template" link to see the generated metadata that can be used as a basis for creating an actual fontpack.
There are multiple locations where Lagrange looks for fontpacks at launch:
Individual TrueType fonts are automatically loaded only from the last one, (CFG_DIR)/fonts.
If a "fonts.ini" file is present in the user configuration directory, it will be loaded as fontpack metadata (see section 5). This way you can manually configure fonts without creating a ZIP archive. For example:
[couriercode] name = "Courier Code" regular = "/home/jaakko/Fonts/Courier Code/CourierCode-Roman.ttf" glyphscale = 0.85 monospace = true
About compatibility: fonts are complex, and while the TrueType format is not the most advanced one, it still has features that are not supported in Lagrange. The technical reason is that glyphs are rasterized using stb_truetype, a rather simple TrueType font library. (It's nice and small, though.) You may find that some fonts simply fail to load correctly, or look wrong in the app.
TrueType Collections (.ttc) can be used, with the restriction that each declared font uses a single index from the collection. The index must be appended to the file name:
regular = "NotoSansCJK-Regular.ttc:2"
The "Style" tab of the Preferences dialog lets you customize the appearance and layout of page content.
Other style options:
Gemini allows relaying requests via a proxy server. On the "Network" tab, you can configure proxy servers for Gemini, Gopher, and HTTP/HTTPS requests.
When an HTTP proxy server is configured, HTTP/HTTPS links will no longer open in the system's default web browser but will be loaded via the proxy, expecting it to serve a 'text/gemini' version of the link contents.
The "Keys" tab lets you change which keys are bound to UI commands. Click on an item with the mouse and then press the new key combination that you want bound to it. Right-click on a binding to erase it or reset it to the default.
Erasing a binding can be useful to disable certain features. For example, if you have no use for keyboard navigation of links via a modifier key, that binding can be erased and use of the modifier key for other purposes will no longer distract you by showing the link shortcuts.
To reset all bindings to their defaults, quit Lagrange and delete the "bindings.txt" file in Lagrange's runtime directory. This is a platform-dependent location; see below for more information (under "Runtime files").
URLs on the command line are opened after Lagrange has launched. If more than one URL is given, multiple tabs will be opened. For example:
$ lagrange gemini://geminispace.info/
Debugging utility: internal events are printed to stdout.
Print a list of all the available options.
Disable hardware accelerated graphics. Note that software rendering is anyway used as a fallback, so usually this option should not be necessary.
Print the application version.
On macOS and Windows, Lagrange registers itself as a "gemini:" URL scheme handler, so you can click on Gemini links in any application to open Lagrange with that URL.
Likewise (on macOS), .gmi/.gemini file extensions are registered as file formats that Lagrange can view so Finder will know how to open those automatically using Lagrange.
You can drag and drop .gmi files on the Lagrange window to open them in the current tab. Dropping multiple files opens them in separate tabs. This is the recommended way to view local files, because there is no "Open File" menu item. You may also type "file://" URLs in the URL field.
Only one instance of the application is allowed to run at once. This is because each instance uses the same runtime files (section 3.5) without any synchronization. A second instance of the application would overwrite any changes made by the first one, leading to lost bookmarks and other state.
If there already is a running instance you can still use the command line to interact with it: you can open and close tabs, change the currently open URL, or list all the open URLs. See --help for a list of the available options.
Lagrange stores user-specific persistent files in one of the following locations (depending on the operating system):
Windows : C:\Users\Name\AppData\Roaming\fi.skyjake.Lagrange macOS : ~/Library/Application Support/fi.skyjake.Lagrange/ Other Unix : ~/.config/lagrange/
Each line specifies one keybinding:
{binding-id} {keycode} {flags}
s : Shift a : Alt c : Ctrl g : Gui (Windows/Command/Meta key) k : Caps Lock
This file stores all bookmarks and bookmark folders. The format is a subset of TOML: the only supported value types are strings and integers.
[51] url = "gemini://gemini.circumlunar.space/" title = "Project Gemini" tags = "" icon = 0x264a created = 1609936844 # 2021-01-06 parent = 99 order = 288
Cached state of feed subscriptions. The file may be deleted while the application is not running to force a reset of feed contents. Subscriptions themselves are tracked via bookmark tags so deleting the file does not affect which pages are subscribed.
The file has three sections. The first one is simply a UNIX timestamp indicating when feeds were last refreshed. The second section begins after the comment:
# Feeds
In this section, each line identifies a subscription so it can be referenced in the feed entries:
{feed-id} {feed-url}
The third section contains all the discovered feed entries. It begins after the comment:
# Entries
Each feed entry consists of five consecutive lines:
{feed-id}
{posted-time}
{discovery-time}
{url}
{title}
This file is loaded as fontpack metadata (see section 5). It must be manually created.
idents.lgr is a binary file that contains metadata about the client certificates known to the application, for example on which URLs a particular certificate should be active.
The "idents" subdirectory is where the actual client certificates are stored. Each certificate comprises a .crt and a .key file in PEM format. Certificates generated inside the application use the certificate fingerprint as the file name.
Translation table for keyboard modifiers. For example, one could swap the Alt and Ctrl keys by changing their mappings. See the comment in the file that explains the format and usage.
See section 4.
Palette for UI color themes. This configuration file does not exist by default and must be created manually.
The actual colors used in the user interface are derived from the base palette. Therefore, you should not change the colors too drastically, but instead follow the expected pattern: a range of 5 intensities (black-white), followed by two accent color pairs (dim, bright). The last five colors are reserved for certain specific uses, e.g., the green is used for the navbar lock icon to indicate a trusted server certificate. The palettes defined here do not affect page color themes, only the UI. Note that link icons are considered part of the UI, so you may find that changing the palette will reduce visibility of the icons with some of the page color themes.
The file specifies two palettes: one for dark mode and one for light mode. The current palette is selected with the lines:
# Dark
and
# Light
The other lines specify colors:
{label}: {rgb-hex}
{label}: {red} {green} {blue}
The configuration below defines the built-in default palette:
# Dark black: #000000 gray25: #282828 gray50: #505050 gray75: #A0A0A0 white: #FFFFFF brown: 106 80 0 orange: 255 192 0 teal: 0 96 128 cyan: 0 192 255 yellow: 255 255 32 red: 255 64 64 magenta: 255 0 255 blue: 132 132 255 green: 0 200 0 # Light black: 0 0 0 gray25: 75 75 75 gray50: 150 150 150 gray75: 235 235 235 white: 255 255 255 brown: 210 120 10 orange: 235 215 200 teal: 10 110 130 cyan: 170 215 220 yellow: 255 255 32 red: 255 64 64 magenta: 255 0 255 blue: 132 132 255 green: 0 150 0
Persistent configuration variables. The file is rewritten when the application is closed. Each line is interpreted as an internal UI event like those printed with the --echo command line option.
A binary file that contains the current state of the application: open tabs and their scroll positions, and navigation history with cached page content (up to the configured cache limit). You may delete this file when the application is not running to close all tabs and clear the page content cache.
Site-specific settings.
Trusted server certificates. Each line specifies the certificate fingerprint of one server:
{domain};{port} {valid-until} {fingerprint}
Backup of the text entered into the Upload with Titan dialog.
Backup of the server token.
The combined navigation history of all tabs. Each line specifies one URL:
{last-visited} {flags} {url}
0000 : No flags set 0001 : Transient 0002 : Kept
The Transient flag is used to indicate redirections and for marking feed entries as read without actually visiting the URL. URLs with the Transient flag do not appear in the History sidebar tab.
The Kept flag is used for preventing the URL from being discarded due to old age. Normally URLs in the navigation history become stale after several months and are removed from the file.
Override display DPI detection with a user-provided value. The same value is applied to all displays. This is useful if your display's DPI value is not being detected correctly. Example:
LAGRANGE_OVERRIDE_DPI=96
MIME hooks enable piping Gemini responses through external programs for arbitrary processing. This allows leveraging scripting languages and binary executables to perform format conversions, content transformation, and data analysis.
Hooks are configured using the file "mimehooks.txt" in Lagrange's config directory. Each hook has a regexp that is matched against the response MIME type and parameters, and each matching hook is offered the response body via stdin. The called external programs are free to rewrite the entire response, including the MIME type. If one of the hooks returns a valid response, it is used as the final response of the Gemini request.
Example use cases:
When a hook is called, it is given the MIME type and parameters via command line arguments. The response body is provided via stdin. The request's URL is available in the REQUEST_URL environment variable.
The MIME type and parameters are split at semicolons, so "text/gemini; lang=ja" would be called as:
hookprogram text/gemini lang=ja
Output from the program (via stdout) must be a valid "20" Gemini response that includes the new MIME type:
20 text/gemini; lang=en\r\n {...body...}
Any output that does not follow this format is considered to mean that the hook refused to process the contents. The next hook will be offered the response instead.
Like other Lagrange configuration lines, mimehooks.txt has a simple line-oriented syntax. Lagrange must be restarted for changes to the configuration file to take effect.
Each hook is specified as three lines:
For example:
Convert Atom to Gemini feed application/xml /usr/bin/python3;/home/jaakko/atomconv.py
The hook program is executed directly without involving the shell. This means scripts must be invoked via the interpreter executable.
The following simple Python script demonstrates how a MIME hook could be used to parse an Atom XML document using Python 3 and output a Gemini feed index page based on the parsed entries. This is just a simple example; a more robust script could include more content from the Atom feed and handle errors, too.
import sys import xml.etree.ElementTree as ET def atomtag(n): return '{http://www.w3.org/2005/Atom}' + n root = ET.fromstring(sys.stdin.read()) if root.tag != atomtag('feed'): sys.exit(0) feed_title = '' feed_author = '' feed_entries = [] for child in root: if child.tag == atomtag('title'): feed_title = child.text elif child.tag == atomtag('entry'): feed_entries.append(child) print("20 text/gemini\r") print(f'# {feed_title}') for entry in feed_entries: entry_date = '' entry_title = '' entry_link = '' for child in entry: if child.tag == atomtag('updated'): entry_date = child.text[:10] elif child.tag == atomtag('title'): entry_title = child.text elif child.tag == atomtag('link'): entry_link = child.attrib['href'] print(f'=> {entry_link} {entry_date} {entry_title}')
Fontpack is a file format defined by Lagrange that is used to combine a set of font files with configuration metadata. The (unregistered) media type for .fontpack files is 'application/lagrange-fontpack+zip'.
A fontpack is a ZIP archive that must have a "fontpack.ini" file at the root level.
version = 1 [firasans] name = "Fira Sans" glyphscale = 0.85 regular = "FiraSans-Regular.ttf" italic = "FiraSans-Italic.ttf" light = "FiraSans-Light.ttf" semibold = "FiraSans-SemiBold.ttf" bold = "FiraSans-Bold.ttf"
Fontpacks may use the Deflate compression method, but that can slow down launching the app because each font must then be decompressed before loading. Therefore, it is recommended to use the -0 (--compression-method store) option when creating the archive. You may also find the -j (--junk-paths) option helpful if the current working directory is not the one where fontpack.ini is located.
Each fontpack has an ID that comes from the file name: the ID of "firasans.fontpack" would be "firasans". Space characters are not allowed in the ID.
Fontpacks are versioned, i.e., they have a version number in their metadata. This makes it possible to tell when a fontpack is an updated version of an existing one. The fontpack ID (file name) must remain the same, though, regardless of the version number.
The "fontpack.ini" file uses TOML syntax and can specify any number of typefaces. In the example above, the line [firasans]
begins the definition of a typeface called "firasans". If the same typeface is found in multiple fontpacks, only the first one that gets loaded is used; the rest are ignored. Typeface IDs can be the same or different than fontpack IDs; they exist in different namespaces. The typeface ID is what is actually used in "prefs.cfg" to save your font preferences.
## 5.2 Typeface properties ### 5.2.1 General * `name` (string) is the human-readable name of the typeface. This is used in the Preferences dialog and elsewhere in the UI. ### 5.2.2 Prioritization and exclusion The same character's glyph(s) may be found in several fonts, so a prioritization is applied to pick the most appropriate font. Font selection may also depend on the context, for example preformatted blocks must be displayed using monospace fonts. These properties control when and if the typeface will be used. * `priority` (integer) defines a sort order for typefaces. The ones with the highest priority are checked first. * `override` (bool): whether this typeface is always checked first before any other one, in any context. Only one typeface can use this flag. * `monospace` (bool): is this a monospace typeface and therefore suitable for preformatted blocks and monospace body text. * `auxiliary` (bool): if true, this typeface is not included in Preferences as a font choice, but is still checked for glyphs if other fonts do not provide them. Suitable for miscellaneous symbols or additional languages, for examples. * `allowspace` (bool): if true, an auxiliary font's metrics for space characters are used. Sometimes symbol fonts have extra wide spaces, so for example a space next to an Emoji would be too wide. This has no effect for non-auxiliary typefaces. ### 5.2.3 Metrics These properties can be used to modify the height and spacing of the font. * `height` (float) is a multiplier to alter the height of the font. It can be at most 2.0, to double the height. This simply scales the font smaller or larger without altering spacing. * `glyphscale` (float) scales glyphs smaller without changing the height of the font. The value must be 1.0 or smaller. In effect, this lets you increase the spacing between consecutive lines of text. It can be combined with `height` to keep glyphs the same size but just increase line spacing. For example, `glyphscale` 0.833 would counter the scaling caused by `height` 1.2, increasing line spacing by 1.2x. * `voffset` (float) is multiplier applied to the vertical offset that normally centers scaled glyphs on the line. This allows fine-tuning where the baseline of scaled glyphs ends up. Setting this to 1.0 means that glyphs are vertically centered after scaling with `glyphscale`; 0.0 would disable the centering. Reasonable values are likely in the 0.5…1.5 range, depending on the amount of scaling. Changing only the `glyphscale` property is the recommended way to alter line spacing. This makes it easier to mix and match fonts on a single line without having to deal with changes in text height. Compare your adjusted scaling to see if it looks suitable next to normal body text. Each of these properties can be set separately for UI text and page content by using the prefixes `ui.` and `doc.`. For example, to only scale the font when used in the UI, set `ui.height` instead of `height`. ### 5.2.4 Font files A single typeface can use up to five font files. * `regular` (string): path of a font file for the regular style. A relative path means that the file is loaded from inside the fontpack archive. Absolute paths can be used to refer to files on the local system. * `italic` (string): path of a font file for the italic style. * `light` (string): path of a font file for the light weight. * `semibold` (string): path of a font file for the semibold weight. * `bold` (string): path of a font file for the bold weight. Only the regular style is required. Undefined styles fall back to the closest available style. The same file path can be used in multiple styles; only one copy of the file will be loaded to memory. When the path refers to a TrueType Collection (.ttc), the font index to use must be appended to the file name:
regular = "NotoSansCJK-Regular.ttc:2"
text/gemini;charset=utf-8
This content has been proxied by September (ba2dc).