Architecture Overview
Jotain is organised into distinct layers, each with a clear responsibility.
File Structure
jotain/
├── early-init.el # Pre-package/GUI initialisation
├── init.el # Entry point — loads modules in order
├── lisp/ # Modular Elisp config (init-*.el, one per concern)
├── emacs.nix # GNU Emacs build from source
├── default.nix # Distribution wrapper (Emacs + tree-sitter grammars)
├── module.nix # Home Manager module for the daemon
├── nix/
│ ├── devenv-emacs-lisp.nix # devenv language module for Elisp
│ ├── ellsp.nix # ellsp (Emacs LSP) package definition
│ └── elsa.nix # elsa (Elisp static analyser)
├── devenv.nix # Development shell
├── devenv.yaml # devenv inputs (pinned to flake.lock revs)
├── flake.nix # Flake entry point — source of truth for pins
├── Justfile # Task runner recipes
├── docs/ # Mintlify documentation (this site)
└── journal/ # Development journal entries
Layers
Emacs Lisp Layer
The Elisp configuration is split into three parts:
-
early-init.el — loaded before package.el, before the first frame, before init.el. Handles anything that must happen that early: the startup GC threshold, use-package-always-ensure, frame chrome defaults, native-comp and eln-cache redirection, terminal aliases.
-
init.el — tiny entry point. Registers MELPA/NonGNU ELPA as fallback archives, puts lisp/ on the load-path, points custom-file at var/custom.el (write-only — never loaded back), refreshes archive contents on the first run, and requires each module in order.
-
lisp/init-*.el — one file per concern. See Modules for the full list and their responsibilities.
Nix Layer
The Nix layer provides reproducible Emacs builds:
-
emacs.nix — wraps pkgs.emacs-git (or pkgs.emacs-macport) from nix-community/emacs-overlay with the full set of build flags exposed as arguments. A cache-parity invariant guarantees that import ./emacs.nix {} produces the same store path as pkgs.emacs-git, so the default build is a cache hit against nix-community.cachix.org. Divergent variants (unstable, igc, macport, Darwin patches) run through overrideAttrs and intentionally rebuild from source.
-
emacs-jylhis.nix — builds the pinned github:jylhis/emacs Meson fork. The flake exposes it as a non-default opt-in backend so consumer flakes can install the same Jotain setup on the fork without changing the stable default.
-
default.nix — flake-compat wrapper for the default package set.
-
module.nix — Home Manager module that runs Jotain as a user-session Emacs daemon (services.jotain), generates a desktop entry for emacsclient, and can install itself as EDITOR/VISUAL. Supports systemd on Linux and launchd on macOS, with services.jotain.emacsBackend = "jylhis" for the fork backend.
Development Layer
devenv.nix — development shell. Provides the local Emacs build (so emacs in the shell matches just build-bare), Nerd Fonts, nil, nixfmt-rfc-style, and treefmt. Enables the custom languages.emacs-lisp module from nix/devenv-emacs-lisp.nix, which provides Emacs + eask-cli (with ellsp and elsa off by default).
Justfile — every recipe you run day-to-day: run, debug, tty, check, compile, test, the build-* matrix, fmt, update, verify, clean, clean-all.
- Pinning —
flake.lock is the single source of truth; default.nix and emacs.nix read it directly via fetchTarball so non-flake nix-build consumers get the same pin. devenv.yaml mirrors the flake’s shared input revs (nixpkgs, treefmt-nix), and just update keeps devenv.lock in lockstep.
Last modified on May 25, 2026