The implementation backlog is in TODO.md. The notes below record actual desktop troubleshooting results; they are not authorization to apply every workaround on every machine.
Notes updated: 2026-10-06. Verification dates and limitations are recorded per issue; this is not a fresh validation of the whole desktop. Desktop observations unless stated otherwise. Preserve the working baseline while watching for recurrence after ordinary use, suspend/resume and reboot.
Before changing packages or application settings, inspect persistent user/system overrides:
systemctl --user list-unit-files --state=masked,masked-runtime
systemctl list-unit-files --state=masked,masked-runtime
systemctl --user status xdg-desktop-portal.service xdg-desktop-portal-gtk.service xdg-desktop-portal-hyprland.service
journalctl --user -b -u xdg-desktop-portal.service --no-pagerA service definition symlinked to /dev/null is a systemd mask: starting that service is forbidden. It is not output redirection. A mask under ~/.config/systemd/user/ survives reboot and can override the service supplied by NixOS. Some masks are intentional; investigate relevant ones, never blanket-unmask them.
Also check the required API, not just whether a process is running. For external application opening:
busctl --user get-property org.freedesktop.portal.Desktop /org/freedesktop/portal/desktop org.freedesktop.portal.OpenURI versionAfter the repair below OpenURI advertised interface version 5. A future version may differ; successful interface access and a real handoff are the important checks.
- Symptom: Brave displayed its external-application permission prompt, but approving it did nothing. Terminal
xdg-open 'slack://'and a separategio opentest could launch Slack. - Cause demonstrated:
~/.config/systemd/user/xdg-desktop-portal-gtk.servicewas a symlink to/dev/null. The GTK backend was masked/inactive. The main portal and Hyprland backend were running, but the main portal lacked OpenURI, FileChooser and Settings. Logs reported failure to create the GTK application-chooser proxy. - Architecture: the installed configuration prefers
hyprland;gtkper capability. Hyprland handles supported capture/shortcut functions; GTK supplies other capabilities, including the AppChooser backend used by the main portal's OpenURI interface. Keeping this GTK backend does not mean running the GNOME desktop. - Evidence: a token-free local HTML link (
<a href="slack://">Open Slack</a>) reproduced the failure without authentication. A D-Bus metadata trace captured Brave calling OpenURI and receiving an immediate error. Restoring the backend made the test link and actual Slack browser sign-in work.
The repair performed, recorded for reference rather than automatic execution:
systemctl --user unmask xdg-desktop-portal-gtk.service
systemctl --user start xdg-desktop-portal-gtk.service
systemctl --user restart xdg-desktop-portal.serviceThis removed the user-level mask; it did not replace it with another redirect. Both services became active and OpenURI appeared. Portal restarts can interrupt screen sharing. No Nix rebuild, Slack reinstall, MIME change or graphics change was needed. The mask's creator is unknown; no recreating instruction was found in the configuration files searched. Its removal persists unless something recreates it.
Update hypothesis, not proven version regression: Chromium changed Linux external opening to prefer portals on 2026-03-24. A Brave update could have exposed the existing mask. Why the browser's intended xdg-open fallback did not rescue this launch was not established.
- Observed stack: NixOS 25.05, kernel 6.12.50, Mesa 25.0.7, Hyprland 0.49.0 and Brave 1.93.138. These are historical test versions, not recommended upgrade targets.
- Desktop GPUs: Radeon RX580 at PCI
0000:01:00.0and the Ryzen 9 7950X integrated Radeon at0000:11:00.0. Earlier diagnostics observed the monitor on integrated-GPUDP-4; that historical observation does not establish the current connection. - Native Wayland Brave with the RX580 explicitly selected reproduced severe visual corruption/freezing. The confirmed RX580 under XWayland rendered smoothly with hardware acceleration and survived short and overnight suspend tests.
- Preserve
--ozone-platform=x11 --gtk-version=3in config/brave/default.nix. This runs Brave through XWayland inside Hyprland, not a separate X11 desktop. The desktop wrapper selects the GPU defined in hardware/desktop.nix; laptop selection remains automatic. A missing or unreadable desktop render node prevents launch and prints an error to stderr, without a notification popup. - Brave uses the separate
brave-nixpkgspin for 1.93.138. Downgrading to basepkgs.brave1.82.172 caused immediate SIGTRAP crashes with the existing profile, including with extensions disabled; an empty temporary profile stayed open with the same GPU wrapper. Profile downgrade incompatibility is the leading explanation, not a confirmed failing component. The user reports the GPU working well on September 29; the exact launch/profile/renderer validation matrix was not recorded with that report. Configuration changes alone do not activate this setup. - For a controlled desktop-only comparison, after confirming the PCI mapping and render-node path exists, the following previously worked. Fully quit Brave through its UI first so the launch is not absorbed by an existing instance. It uses the normal
Defaultprofile; do not run it automatically or on the laptop.
DRI_PRIME=pci-0000_01_00_0 brave \
--profile-directory=Default \
--ozone-platform=x11 \
--gtk-version=3 \
--render-node-override=/dev/dri/by-path/pci-0000:01:00.0-renderVerify GL_RENDERER in brave://gpu: it must name Radeon RX 580, not Ryzen 9 7950X, for an RX580 test. Recheck after suspend; do not infer GPU switching without comparing renderer reports. Prefer verified PCI/by-path mappings over assuming renderD128 always identifies the same GPU.
Other observations to keep separate:
- GTK 3 launch selection worked around the earlier Brave startup failure. A missing GTK 4 Adwaita-dark CSS import was real, but was not proven to be the cause of every crash. The installed GTK portal uses GTK 3; masking it is not a GTK 4 theme repair.
- Native Wayland also produced the fatal message
Custom primaries aren't supported. DisablingWaylandWpColorManagerV1allowed that test to start, but did not fix RX580 native-Wayland corruption. It is not a necessary addition to the working XWayland baseline. - One separate suspend attempt produced RX580 UVD initialization timeouts and runtime-power-management
error; reboot cleared that state. Later successful tests do not close the PM issue or certify the hardware. Cross-GPU transfer/synchronization remains a hypothesis, not a demonstrated root cause. - Do not reintroduce
--use-fake-ui-for-media-stream, disable GPU acceleration globally, or combine a graphics upgrade, GPU routing change and compositor migration in one experiment.
September 29 observation: the user reported the GPU working well. Desktop Brave GPU selection is implemented in the wrapper and hardware configuration above; keep the working XWayland/GTK 3 settings. Former TODO items 18–19 were closed at that point.
Recurrence, 2026-10-06: Brave's GPU subprocess segfaulted twice around 15:54 Dubai time, with identical stacks in Mesa's Wayland EGL initialization despite --ozone-platform=x11. Crash mappings identify Mesa 26.1.8; the running kernel is 6.18.54, so the earlier working stack above is historical. No corresponding kernel GPU timeout/reset was found in the queried window. An EGL/display-platform mismatch is a hypothesis; the trigger remains unresolved. The connected monitor is now on the RX580. See the crash evidence and next discriminating question. No runtime or configuration change was made.
The earlier native-Wayland corruption and RX580 runtime-PM/UVD failure remain historical evidence, not a claim of a currently broken GPU. No new root cause or comprehensive suspend/laptop validation was established by the current report.
If a graphics or suspend symptom returns, record the time, actual renderer, connector/GPU ownership, runtime-PM state and relevant kernel logs before changing settings. Use the prior mixed evidence (one failed cycle and later successful cycles, including overnight) to choose one discriminating comparison. Idle versus active-browser suspend, scoped runtime-autosuspend behaviour and monitor routing are possible comparisons, not a standing instruction to run all of them. Keep graphics updates, GPU routing changes and compositor migration separate; preserve the working XWayland path and avoid broad AMD flags or hardware conclusions without evidence.
- Hypridle timers, 2026-10-06 — display-off recovered, cause unresolved: neither desktop display-off nor automatic lock initially fired despite Hypridle running and configured timers. No systemd idle blocker or inhibiting mapped window was observed. The installed unpatched 0.1.7 has a known D-Bus inhibitor-count leak fixed upstream in 0.1.8, but local execution is unproven; its output goes to
/dev/null. The user restarted Hypridle and confirmed a temporary one-minute display-off test worked. At their request the repository timeout was restored to 15 minutes; applying it and restarting Hypridle are still required. The 30-minute automatic lock was not validated by that test. See the findings, test result and restoration. No package change or agent-run build/activation occurred. - Brave microphone investigation, updated 2026-10-06: start with the renewed investigation, retained evidence and Firefox comparison plan. The earlier six-file dossier was removed in the October 5 cleanup and remains in Git at
0485e94; the report preserves its conclusions and ledger references. September 10 measured per-stream burst delivery and loss before browser processing. October 1–5 server logs also record Brave capture-buffer overruns. The user confirms recurrence across computers and microphones. The observed PipeWire 1.6.6 package predates the relevant capture-starvation fix verified in 1.6.9, but local causation remains unproven. Firefox is the next practical comparison; no package change, restart or rebuild was performed. Keep newer GPU instability and historical missing-microphone incidents separate. - Clipboard: the earlier Slack freeze/crash report and the September 29 XWayland → Wayland delivery failure remain unresolved known issues, currently on recurrence watch. See the clipboard evidence and recurrence guidance below; the portal repair and new picker shortcuts are not established fixes for either symptom.
- Microphone/dictation: ChatGPT dictation worked during testing, and Brave had an active, unmuted input stream from the default Samson G-Track Pro. Earlier reports involved microphone loss after a period of use in calls and ChatGPT. Current success is not proof of long-term reliability or a portal-related cause.
- Screen sharing: the GTK backend repair may affect supporting dialogs, but no before/after test proved that it fixed sharing, freezes or echo. The Hyprland capture backend was already running.
If one recurs, note the time, app, action, selected devices and whether the machine recently resumed. Where practical, inspect the broken state before restarting or changing routing. For audio, wpctl status shows streams and connected devices without recording sound. Use non-sensitive synthetic content for clipboard tests. Do not collect actual clipboard history, login callback URLs, private recordings or full browser profiles.
Known issue, recovered but not fixed. This section now owns former TODO item 21. Active diagnostics are paused until recurrence; the clipboard picker improvements do not establish a bridge repair.
The clipboard investigation progression and evidence ledger records the ordered tests, hypotheses, decision branches, and acceptance criteria. On recurrence, capture the failing boundary before restarting applications or the desktop. Exiting Hyprland ends the session and closes its applications; it is not a way to preserve and return to the old session.
-
Separate earlier Slack freeze/crash report: copying from Slack and pasting into another application was reported to freeze/crash an app, but the symptom was not reproducible during diagnosis. Slack and Brave were XWayland clients, Kitty/Telegram native Wayland, and CopyQ was running. No matching recent crash dump, OOM kill or clipboard-transfer error was found. If this symptom returns, identify the affected process and destination before testing synthetic plain versus rich content and backend differences. Do not conflate it with the timed-out delivery below or with Slack sign-in. Closure needs repeated successful pastes without hangs/crashes, including after suspend.
-
User reproduction: Brave ↔ Slack copying works; Firefox/terminal copying works; Slack → Firefox or terminal fails, as does Brave → the external clipboard. The later synthetic reverse control, Firefox → Brave with
CB-WL-01, succeeded. The observed failure is XWayland → Wayland only. No freeze/crash was confirmed in this recurrence. -
Correlated synthetic capture: after copying
CB-X11-01from Brave, Firefox pasted nothing. X11 owner changes coincided with new Wayland text offers. A standardxclipread returned the exact marker in 0.003 s, whilewl-pasterequesting UTF-8 plain text timed out after five seconds. This locates the observed failure after offer publication, during delivery; the underlying transfer defect remains unresolved. Details and limitations are in the investigation ledger. -
Non-Chromium control, completed: a temporary Kitty window verified as XWayland reproduced the failure: its synthetic text pasted into Brave but not Firefox.
xclipretrieved the marker in 0.004 s;wl-pastetimed out at five seconds. The observer and temporary window were then closed. The observed fault affected general X11 → Wayland delivery, rather than only Brave/Slack. -
Fresh-session comparison, completed: after the user restarted the graphical session, new Hyprland/Xwayland PIDs and app backends were verified. Brave → Firefox still pasted nothing; Firefox → Brave worked. Normal CopyQ startup occurred. The fault reproduced in the fresh session under the normal startup configuration. The subsequent selection-metadata trace and recovery are recorded below; these are completed experiments, not pending restart instructions.
-
Current status — recovered, cause unresolved: during the selection-metadata trace, the user reported Brave → Firefox worked. After the trace was stopped, the user confirmed three successive fresh markers (
CB-OFF-01,CB-OFF-02,CB-OFF-03) copied from Brave to Firefox correctly with all observers off. Active testing has stopped. No configuration repair or extra bridge was applied; CopyQ remains in use. Neither tracing nor the earlier session restart is established as the recovery cause. On recurrence, preserve the failing state and follow the ledger's standard-reader-before-trace comparison; do not repeat the completed broad tests without new evidence. -
Runtime inventory: Hyprland 0.49.0, Brave 1.93.138, Xwayland 24.1.8. Brave and Slack are XWayland clients; Firefox and Kitty are native Wayland clients. The GPU does not transfer clipboard text; the relevant part of Brave's graphics workaround is its X11 display backend. Preserve the existing GPU, XWayland and GTK 3 settings.
-
Read-only evidence:
wl-paste --list-typesreturned text formats, which proves an offer exists but not that it is current or transferable. The X11 CLIPBOARD and CLIPBOARD_MANAGER selections shared owner window0x200001; its class and PID properties were absent, so it was not identified as CopyQ. Clipboard contents/history were not requested. These observations were not synchronized with a synthetic copy operation. -
CopyQ: loaded-library inspection found CopyQ 10.0.0 using the native Wayland Qt plugin. Its three processes were one parent with two children, not demonstrated duplicate servers. The two configured startup paths still differ: Hyprland runs
copyq --start-server, whereas the XDG entry requestsQT_QPA_PLATFORM=xcb copyq. This discrepancy is not a proven cause. -
Service state: the GTK portal backend is unmasked and active; the earlier Slack authentication mask has not returned. No clipboard-specific cause was established from the service inventory. Hyprland debug logging was disabled.
-
Completed CopyQ isolation test, user approved:
copyq exitreportedTerminating server.and a subsequent process check found no matching CopyQ processes. Brave/Slack/Hyprland were left running. The user reported the same failure on retesting. CopyQ was then restored withenv QT_QPA_PLATFORM=wayland copyq --start-server; process and loaded-library inspection verified it was running with the native Wayland Qt plugin. Stopping CopyQ was insufficient to recover the bridge; this does not exclude CopyQ having contributed to an earlier stuck state. Hyprland and Xwayland had 157 and 41 open file descriptors respectively at the later snapshot, without an obvious large descriptor accumulation. -
Completed focus-delay test: the user reported that keeping Brave focused for five seconds after a fresh copy still did not allow pasting into Firefox. This did not support a simple rapid-focus-switch explanation. In Hyprland v0.49.0's XWM source,
handleSelectionNotifyrejects TARGETS replies whenm_focusedSurfaceis absent, but no runtime trace demonstrated that rejection here. The retained rolling log contained no matching clipboard diagnostics. The same source shows Hyprland creates its own CLIPBOARD_MANAGER owner, reinforcing that the earlier owner ID alone did not identify CopyQ. The failure remains unresolved. Do not restart Xwayland, which would disrupt its applications, or add a bidirectional synchronization script as an untested fix. -
Source context: CopyQ's known issues describe backend-dependent clipboard limitations. Hyprland issue 6132 records a similar boundary failure on an older version; it is not evidence that the same historical regression caused this recurrence.
- Check the registered handler with
xdg-mime query default x-scheme-handler/slack; inspect its desktop entry and executable. - Compare a direct launch using
XDG_UTILS_DEBUG_LEVEL=2 xdg-open 'slack://'with a clickable token-free link in Brave. A typed address may become a search instead. - Inspect service masks, logs and the actual interface as above.
- Start the following in a terminal, then click the test link and approve it while the trace is active:
timeout 300s dbus-monitor --session --profile \
"type='method_call',interface='org.freedesktop.portal.OpenURI'" \
"type='error'"This prints metadata to the terminal, not a trace file or URL payloads. Match calls and replies by serial/reply serial. A sender such as :1.32 is a temporary bus name, not a PID. Resolve the name actually seen in the trace with:
busctl --user call org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus GetConnectionUnixProcessID s ':1.32'An empty or expired capture proves nothing. Do not use unfiltered payload traces during real authentication. After an approved, targeted repair, repeat both the minimal reproduction and the real workflow.
This repository manages Jordan's desktop and laptop through one
nixosConfigurations.default output. flake.nix reads
/etc/machine-id to select the machine's hardware and desktop settings. This is
intentional and requires --impure; an unregistered machine ID aborts evaluation.
The configured NixOS, Home Manager and Nixvim input branches are 26.05. Exact revisions are recorded in flake.lock. Brave has a separate pinned input for the compatibility baseline described above. Input branches and source configuration do not establish which generation is currently running on either machine. Keep dependency updates separate from routine configuration edits.
| Path | Purpose |
|---|---|
| flake.nix | Inputs, Rust overlay, machine selection and NixOS output |
| configuration.nix | System services, user account and integrated Home Manager |
| hardware/ | Machine-specific disks, boot and GPU configuration |
| home.nix | Home Manager imports and current npm activation hook |
| config/packages.nix | User packages, including Foundry and ZapZap |
| config/hyprland/ | Shared desktop behavior and per-machine overlays |
| config/theme/ | Shared palette, native appearance files and template rendering |
| config/quickshell/ | Power, screenshot and tmux menus; service lifecycle |
| config/nixvim/ | Editor configuration and plugins |
| tests/quickshell/ | Safe component and command-adapter tests |
| TODO.md | Remaining work and configured features awaiting acceptance |
| AGENTS.md | Editing, approval and diagnostic rules |
Hyprland is configured as the base desktop, with GDM and GNOME keyring retained;
GNOME Shell is disabled. Home Manager is part of the NixOS configuration. Foundry
comes from pkgs.foundry in the package list, without a separate Foundry flake.
The user runs builds, upgrades and activation. From either registered machine, the normal command is:
sudo nixos-rebuild switch --flake ~/.nix#default --impureIn the configured Zsh shell, nixup (also nu) runs that command and reloads
~/.zshrc after success. Home Manager changes are applied through this system
rebuild; a standalone Home Manager switch is not the normal workflow. Repository
edits alone do not change the running system. Keep system.stateVersion and
home.stateVersion as their existing compatibility values during release updates.
Quickshell reads its generated configuration at startup. After applying changes to that configuration, the user restarts it with:
systemctl --user restart quickshell.serviceSee the Quickshell lifecycle notes for startup, failure recovery and the remaining manual checks. Idle configuration changes also need the running Hypridle instance to reload through a user-managed restart or a fresh session; see the current idle report above.
- Review the matching hardware configuration, including disk paths,
encryption, swap and GPU selection, and register the machine ID in
flake.nixthrough the normal diff-preview process. - Review the user account, home directory, display rules, locking and laptop lid/docked behavior before applying the configuration.
- Restore Brave profiles and authenticate applications, password managers, development tools and Copilot as needed. These are manual account steps.
- Provision SSH keys and authorized keys separately; keep private keys and credentials out of this repository.
- Review local wallpaper and profile assets referenced by the configuration.
- Verify the relevant command paths, graphical session, locking and normal workflows after the user-managed activation.
home.nix contains a Home Manager activation hook for
@nomicfoundation/solidity-language-server, prettier-plugin-solidity and
@openai/codex. It runs when either the language-server directory or Codex
directory is absent, writes the npm prefix to ~/.npmrc, and installs unpinned
packages under ~/.npm-packages. The Zsh configuration adds that directory's
executables and modules to its search paths.
This is activation-time behavior, not a script that runs on every boot. The Prettier plugin is not independently checked, and existing packages are not updated by this hook when both checked directories exist. Reproducibility and activation cleanup remain deferred tasks in TODO.md.
The Quickshell test README records commands, tool requirements, isolation and coverage. Its Nix shell may fetch or build tools and requires explicit delegation for an agent to enter. Already-available tools can run the safe adapter suites directly. Static syntax checks do not establish successful system evaluation, build, boot or graphical-session acceptance.