The module goes, its link handler leaves xdg's default applications, and the shared desktop helper's header names the three bundles left.
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-dirsandxdg-utils(xdg-open,xdg-mime, which the tools use).dex, which runs the autostart entries, isi3's.desktop-file-utilsandshared-mime-infocome as other packages' dependencies and are held as found. - No contribution, no session code. The window manager's
dex --autostart --environment i3line isi3'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 (PROJECTSarrived 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=Falsein the account'suser-dirs.conf, it does nothing, even with--force. Only an explicit--setstill 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:
- Owned whole fights the person: Firefox's "make default", a file manager's "always open with" and
xdg-mime defaultall rewrite that file, and the next push would take each choice back. - A mesh block at its start (
into: block, aszshandxorgdo) 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, soxdg-openand 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 defaultwrote the person's choice into the first group, inside the mesh's block, where the next push removes it.
- the block's
- 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=truekeeps an entry from starting;- an
OnlyShowInwithouti3keeps it from starting, and so does aNotShowInwith it; - a
TryExecthat is not installed keeps it from starting; X-GNOME-Autostart-enabled=falseandAutostartConditionare 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:
- Delete
~/.local/share/applications/mimeapps.listand~/.local/share/applications/defaults.list. Both are links the retired predecessor made into its own tree, which is gone; they point at nothing. ~/.config/autostart/slack.desktopis 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.