Files
mesh-catalog/modules/xdg/README.md
T
jochen ade4901d7f Remove jetbrains-toolbox: the operator retired JetBrains
The module goes, its link handler leaves xdg's default applications, and the shared desktop helper's
header names the three bundles left.
2026-10-05 15:09:14 +02:00

12 KiB

xdg

The workstations' XDG desktop conventions as a module (novox/hq ADR 0208, ADR 0182): the account's folders, the default applications, and a view of what starts at login. It claims no seat: nothing calls "the conventions" as a role. It needs no display; every tool reads files, asks xdg-mime and gio, or reads /proc. It is for the two workstations only.

  • Packages: xdg-user-dirs and xdg-utils (xdg-open, xdg-mime, which the tools use). dex, which runs the autostart entries, is i3's. desktop-file-utils and shared-mime-info come as other packages' dependencies and are held as found.
  • No contribution, no session code. The window manager's dex --autostart --environment i3 line is i3's, and stays there.

What it owns

path class (ADR 0182) from
~/.config/user-dirs.dirs owned, the found file kept once config/user-dirs.dirs, three folders from settings
~/.config/user-dirs.locale owned, the found file kept once config/user-dirs.locale
~/.config/user-dirs.conf owned, new config/user-dirs.conf: enabled=False
/etc/xdg/mimeapps.list owned, new config/mimeapps.list: the machine's default applications

What it does not own, and why

whose why
~/.config/mimeapps.list the person's, held as found every program offering "always open with" rewrites it, and so does xdg-mime default (below)
the folders themselves (~/Downloads, …) the person's, held as found they hold the person's files. Declaring a directory would make its mode the mesh's (ADR 0182), and on one workstation ~/Desktop is a file, not a folder
~/.config/autostart/*, /etc/xdg/autostart/* each application's an autostart entry belongs to the module of the application it starts (below)
~/.config/xdg-desktop-portal/portals.conf adwaita the portal's backends are the theme's business
~/.local/share/applications/*.desktop the person's and their installers' Steam, Telegram and the agent write their own launchers and handlers there

The folders

user-dirs.dirs is owned whole, with the operator's own values. Three folders differ between the workstations, so they are settings; the rest are the same on both.

setting the mesh's layer the laptop the desktop machine
desktop-dir $HOME/Desktop as the mesh's $HOME/
publicshare-dir $HOME/Public as the mesh's $HOME/
videos-dir $HOME/Videos as the mesh's $HOME/

A value is written into the file as it is, in xdg-user-dirs' format: "$HOME/yyy" or "/yyy". $HOME/ is the home itself, xdg-user-dirs' way of saying the folder is not wanted. The settings have no default, so the mesh's layer is set before the first assignment (as power's are). They land in this one file: the module has no merged file and no contribution, so issue 168 cannot carry them anywhere else.

Why xdg-user-dirs-update is disabled. It runs at every login, from its XDG autostart entry, which dex starts. Measured on xdg-user-dirs 0.20 against a copy of the folders:

  • enabled, it reassigns a folder it cannot find to the home (Videos was removed, reassigning VIDEOS to homedir), adds a line and creates the folder for every default it knows that the file lacks (PROJECTS arrived this way), and rewrites the file with its own header. Every login would undo the mesh's file, and every push would undo the login;
  • disabled by enabled=False in the account's user-dirs.conf, it does nothing, even with --force. Only an explicit --set still writes.

The package's systemd user unit xdg-user-dirs.service is enabled by preset but never runs: it is wanted by graphical-session-pre.target, which the xinit session does not reach. Disabled, it would do nothing either way, so it is left as found.

user-dirs.locale (en_US on both) only tells xdg-user-dirs-gtk, which neither workstation has, which language the folders were named in. It is owned so the account's xdg-user-dirs files are one set.

Default applications: the mesh's list is the last one read

Decision. The mesh owns /etc/xdg/mimeapps.list, the system's list, and never writes the person's ~/.config/mimeapps.list. An application reads the person's list first and the system's after it (the XDG MIME Applications specification), so every choice the person makes wins, and the mesh's list answers only what the person has not chosen. xdg_default gets and sets a default, and setting writes the person's list through xdg-mime default, as any program would.

Why not own the person's file, or a block in it. Measured on 2026-10-05 with xdg-utils 1.2.1 and GLib's gio, against copies:

  1. Owned whole fights the person: Firefox's "make default", a file manager's "always open with" and xdg-mime default all rewrite that file, and the next push would take each choice back.
  2. A mesh block at its start (into: block, as zsh and xorg do) breaks three ways, because a mimeapps list is a key file and not a script:
    • the block's [Default Applications] and the person's are one group written twice. xdg-mime reads the first and GLib the last, so xdg-open and every GTK or Electron program answered differently for the same type;
    • gio mime (what GTK programs do on "always open with") rewrote the file as one merged group and dropped the block's markers: the next push would find no block and add a second;
    • xdg-mime default wrote the person's choice into the first group, inside the mesh's block, where the next push removes it.
  3. The system's list has none of this: nothing but the mesh writes it, each reader reads one group per file, and a person's choice in their own file is never touched. A default naming an application that is not installed is passed over, to the next list.

What the mesh's list holds. The operator's own defaults, adopted from both workstations' lists of 2026-10-05 (the two were byte-identical), except entries naming an application installed on neither:

  • dropped: 55 types for code-oss.desktop (no Code-OSS on either machine; plain text opens in Fleet, from its own declaration), jetbrains-rider.desktop, org.kde.korganizer.desktop, Postman.desktop, and a Fleet launcher whose name is the id one machine's JetBrains Toolbox generated (a different one on the other machine);
  • kept: Firefox for the web, HTML and PDF; Thunar for folders; ghostwriter for Markdown; Plexamp, Slack, Telegram for their own links; and mpv, mplayer, vlc for MP4.

The person's list keeps every line as found, the dropped ones included. xdg_check names them.

Autostart: listed, not owned

An XDG autostart entry belongs to the module of the application it starts, as picom does: picom's package entry is its one starter. A future nextcloud-client module owns Nextcloud.desktop (the client writes it itself), a blueman module beside bluetooth owns blueman's, and Slack's and JetBrains Toolbox's entries would be their modules'. This module only says what is there, and what the session does with it.

The session's starter is i3's dex --autostart --environment i3. xdg_autostart decides each entry by dex's own rules (dex 0.10):

  • a user entry hides the system entry of the same name;
  • Hidden=true keeps an entry from starting;
  • an OnlyShowIn without i3 keeps it from starting, and so does a NotShowIn with it;
  • a TryExec that is not installed keeps it from starting;
  • X-GNOME-Autostart-enabled=false and AutostartCondition are GNOME's, and dex ignores them: the entry still starts.

Tools

tool what
xdg_user_dirs r each folder as written, the path it resolves to, whether it exists as a directory or points at the home, the folder xdg-user-dirs would have made; whether the update may rewrite the file, and from which file that is decided; the locale
xdg_default r/a the default for a MIME type or a URL scheme (https, mailto): what xdg-open uses (xdg-mime) and what GTK programs use (gio), flagged when they differ; which list decided it and which named applications were passed over as not installed; the application and whether its program is installed. set makes an installed desktop file the default through xdg-mime default, in the person's list
xdg_open_handlers r the same for http, https, mailto, HTML, folders, plain text, Markdown, PDF, PNG, JPEG, MP4, MP3 and any types given; every mimeapps list in reading order and whose it is; every default naming an application that is not installed; every installed handler whose program is missing
xdg_autostart r every autostart entry, the account's and the system's: what it runs and whether that is installed, Hidden, OnlyShowIn/NotShowIn, whether dex starts it (and if not, why), whether the window manager's configuration starts the same program too, and how many of the account's processes run it now
xdg_check r the folders file is the mesh's, every folder it names exists, the update cannot rewrite it; the mesh's list is in place, every list parses with no group or key written twice, no default names a missing application, no handler's program is missing; no program has two starters at login, every program dex starts is installed

From /proc only the account's own processes are read, and of each only its command line and name.

What it leaves found

The person's ~/.config/mimeapps.list; the folders; every autostart entry; every desktop file under ~/.local/share/applications, including its mimeinfo.cache; /etc/xdg/user-dirs.conf and user-dirs.defaults (the package's); and the xdg-user-dirs.service user unit.

The one-off migration (ADR 0182)

Before the first assignment, set the three settings on the mesh's layer ($HOME/Desktop, $HOME/Public, $HOME/Videos) and on the desktop machine's layer ($HOME/ for all three).

Once xdg is assigned, on the laptop:

  1. Delete ~/.local/share/applications/mimeapps.list and ~/.local/share/applications/defaults.list. Both are links the retired predecessor made into its own tree, which is gone; they point at nothing.
  2. ~/.config/autostart/slack.desktop is the predecessor's too (its comment says so). It still starts Slack, and is left until a Slack module owns it.

On the desktop machine: nothing to remove. ~/Desktop is a text file, not a folder, which is why desktop-dir is the home there; move it aside and change the setting if a Desktop folder is wanted.

What changes when it is assigned

the laptop the desktop machine
packages none (both present) the same
user-dirs.dirs the found file kept once, then the module's: the same eight folders, and PROJECTS becomes ~/projects instead of the home the found file kept once, then the module's: the same eight folders, and a PROJECTS line (~/projects) where there was none
user-dirs.conf new: the update stops rewriting the file at login new: the update, which dex starts here since i3 installed it, would otherwise have added PROJECTS and created ~/Projects at the next login
/etc/xdg/mimeapps.list new; changes no answer today, because the person's list says the same the same
running programs, the person's list, autostart nothing nothing

The one visible change is PROJECTS: xdg-user-dir PROJECTS answers ~/projects, the folder where the operator's repositories are, on both machines. GLib knows only the eight older folders, so GTK programs see no change at all.

What xdg_check will say at first

Measured on the laptop before assignment, and expected on both: the defaults naming applications that are not installed (the 55 Code-OSS types and the four others above) fail "every default names an installed application", until the person removes them from their list or installs the editor. On the laptop, the predecessor's two dead links fail "the mimeapps lists are sane" until migration step 1. It also showed that audio/mpeg opens in Audacity from xdg-open and in mpv from GTK programs, because no list names a default for it: one xdg_default with set settles it.

Blockers

  • The three settings must be set before the first assignment (above).

JetBrains removed (2026-10-05). The operator retired JetBrains: its link handler left the mesh's list, and the Toolbox, its IDEs and launchers were moved off both workstations.