TacidOS
Developers
Documentation

Decision 0053

53. One compositor, and it is Hyprland

Architecture decision record 0053: 53. One compositor, and it is Hyprland

Status
accepted
Date
2026-09-06

Supersedes the compositor half of ADR-0019 · Refines ADR-0021

Context

ADR-0019 chose Niri and rejected Hyprland, and closed with a line that came due: “Hyprland users will ask; the answer is in this record.” The question arrived — Omarchy has put Hyprland in front of a large audience — and the first answer given was the wrong one twice over.

The first wrong answer was to make it an option. tacid.desktop.compositor was written, both sessions were generated from the same tokens and the same bindings, and a second reference machine was added so CI would evaluate the branch. It was defensible in the small and wrong in the large. A machine whose window manager is a setting is a machine with two faces to design, two to test and two to answer questions about, and the second one is always the one nobody has looked at lately. Everything else here works the other way round: the genome declares what the machine is. An option was a way of not deciding, dressed up as flexibility.

The second wrong answer was the reason given for the default. The record said Hyprland renames configuration keys between releases, and that on a system whose whole claim is that a machine is a value you can boot back to, this turns a rollback into a session that will not start.

That is false, and it was the load-bearing argument. A NixOS generation pins the compositor binary and the configuration generated for it in the same closure. Generation N−1 boots N−1’s Hyprland with N−1’s configuration; they were built together and they always match. There is no state in which a new compositor meets an old configuration by rolling back. What the churn actually costs is maintenance at upgrade time — a nixpkgs bump may need an edit here — and checks.*.session reads this machine’s generated configuration with Hyprland’s own parser on every push, so a rename lands on a pull request rather than on somebody’s greeter. That check earned itself on its first run, failing on three layerrule lines that an older Hyprland accepted and 0.56 does not, and again on the window rules for dialogs and picture-in-picture. Both were the same rename: 0.56 takes a window or layer rule as a named block with a match block inside it, so the one-line windowrule = float, class:… that every guide still shows is not a rule quietly ignored — it is a parse error, seven of them, on a file the compositor would have started with.

It has one blind spot, and the fix for it is beside it rather than in it. Hyprland resolves a key name through xkbcommon when the key is pressed, not when the file is read, so a binding on a name no keysym has parses perfectly and can never fire. Nine did — the media and brightness keys, bound to what is written on them rather than to what X11 called the symbol. The check now looks every bound name up in xkbcommon’s own header, which is the only thing that will say so, and the module keeps two columns instead of one: what a person reads and what the compositor is given. A mouse button is not a keysym and no header has one, so bindm’s are set aside by name and added back into the count the check holds itself to — a binding cannot leave it through the gap.

With that argument gone, what remained for Niri was that it is written in Rust — and against it, the thing it is actually for. Niri is a scrollable-tiling compositor: one endless horizontal strip of columns that a person scrolls sideways through. It is a striking idea and it is a poor place to keep a day’s work, and it is not a setting that can be turned off. It is the whole concept.

Prior decisions

ADR-0019 named Niri and rejected Hyprland. ADR-0021 says one token file generates every surface. ADR-0047 made tacid-shell a layer-shell client that knows nothing about the compositor under it. ADR-0001 is the claim the wrong argument was made in the name of: a machine is a value you can build, diff and boot back to.

Decision

Hyprland, and no option. programs.hyprland.enable is unconditional under tacid.desktop.enable, /etc/hypr/hyprland.conf is composed from the genome, and greetd runs /run/wrappers/bin/Hyprland --config on it — the wrapper rather than the store path, because that is where the cap_sys_nice lives that lets a compositor raise its own scheduling priority. Workspaces are discrete and numbered one to ten; windows behave the way windows behave.

The colours come from design/tokens.toml through tacid-design, which emits hyprland/dark.conf and hyprland/light.conf. What that fragment mostly does is subtract: animations off, rounding zero, blur off, shadow off. Those are not omissions, they are the tokens — motion only when it explains a state change, and nothing decorative. Hyprland’s own logo and splash are off too, because what is behind the windows on this machine is this machine’s answer and not its compositor’s.

Where the compositor lacks something the module supplies it rather than letting a key go quiet: Print runs grim into the directory a screenshot belongs in, and tacid.desktop.wallpaper — an option the classifier has always treated as a real reversible change, and which until recently rendered nowhere — starts swaybg.

Niri is gone, along with the generator that produced its theme. A generator with no consumer is a file that stops being true without anybody noticing.

sway stays, as apparatus. checks.*.face draws the shell under sway with the Pixman renderer and a headless backend, in seconds, on every push. That is not a second face: it is the compositor that can certainly render with no card at all, which is what makes it the right place to prove that the shell draws. The shell has now been photographed under Niri, under sway, and under a headless wlroots, unmodified each time, which is the finding that made the compositor replaceable in the first place.

Consequences

  • One face to design, test and answer questions about. The cost of the option was not the code that implemented it; it was that from then on every question about the desktop would have had two answers.
  • A wrong argument is out of the record. It appeared in ADR-0053 as first written and in the pull request that carried it, and it would have gone on being cited. The correction is here rather than quietly dropped, because the next person to weigh a compositor will meet the same claim somewhere else.
  • Neither compositor ever had a software renderer, so nothing is lost there and nothing is gained: a machine with no working GPU driver gets a tty and the TUI, which ADR-0020 requires to be complete, and the greeter is tuigreet on the console for the same reason. Hyprland’s aquamarine wants GLES like Smithay’s renderer does; whether llvmpipe over an emulator’s card node is enough for it is what checks.*.desktop now asks.
  • Hyprland’s churn is a maintenance cost, not a correctness one, and it is paid by checks.*.session on every push rather than by a person at a greeter.
  • C++ in the session. It is not Tacid’s C++ and it never links into anything of Tacid’s — the shell is a separate process speaking Wayland — but it is on the machine, and ADR-0003’s “Rust for every Tacid component” was always about Tacid’s components rather than about everything that runs.

Alternatives rejected

  • Keeping the option. Rejected above, and rejected by the maintainer in the same words the manifesto uses: no easy choices.
  • Niri. The scrollable strip is its concept and cannot be configured away, and the argument that had been holding it in place did not survive being checked.
  • sway as the shipped compositor. The most conservative engineering answer: an i3-compatible configuration format that has barely moved in a decade, a software renderer, and already in CI. It loses on what people arriving actually know and on what the desktop is expected to look like in 2026, and the correctness argument that would have justified the conservatism turned out not to exist.
  • A floating or stacking compositor. Closer still to “ordinary windows”, at the cost of the keyboard-first tiling this system is otherwise built around, and of a much smaller ecosystem.
  • A compositor of Tacid’s own on Smithay. Still the honest long-term answer if Hyprland ever stops, and still not worth the years while it has not.