Black screen when you RDP into your own Linux desktop? Give the console to a second user

Log in at the physical console, then RDP in as the same user, and you get a black screen. Worse, unlike Windows, the remote connection can't kick the console session — so any machine you walked away from while logged in is a machine you can't reach.

Neither is really an RDP problem, and one trick fixes both: create a second Unix account that owns the console.

sudo adduser desk

desk logs in at the display manager and holds the physical machine. Your real user never logs in locally at all — its desktop exists only as an xrdp session. When you're sitting at the machine, desk connects to it over loopback:

xfreerdp3 /v:127.0.0.1 /u:youruser +clipboard /multimon

Now every client is equal. Connect from a laptop on the other side of the world and it takes over the same session your local xfreerdp was showing; the local one just exits and desk's desktop is still sitting there underneath. Don't wrap that command in an auto-reconnect loop, or the machine at home will keep stealing the session back and you'll never get in from outside.

The black screen was never about RDP — it was one user running two concurrent desktop sessions, colliding on all the per-user singletons: the session D-Bus, xfce4-session, ibus, gvfs. The second session's window manager never comes up. Two different uids have none of that.

The takeover failure is a separate mechanism. "Taking over" is really xrdp-sesman reconnecting you to a session in its own table. A console session started by lightdm was never in that table, so nothing can ever reattach to it — which is also why a second RDP client can kick the first: those two are the same session.

…more

Want tmux on Windows without leaving your Git Bash setup? Fix MSYS2's HOME and PATH

Git Bash doesn't ship tmux. A standalone MSYS2 install does — but out of the box it ignores your Windows $HOME, doesn't inherit the Windows PATH, and if you wire it into Windows Terminal, silently ignores the config you think is fixing it.

Two settings make MSYS2 match Git Bash:

HOME — edit C:\msys64\etc\nsswitch.conf:

db_home: env windows cygwin desc
db_shell: env windows cygwin desc
db_gecos: env windows cygwin desc

Default MSYS2 uses cygwin desc, which invents a separate /home/<user> instead of your real Windows profile. This is the exact line Git for Windows ships with, so it makes MSYS2 pick up C:\Users\<you> — same .bashrc, .gitconfig, .ssh as Git Bash, no copying needed.

PATH — MSYS2 doesn't inherit the Windows PATH by default, so anything installed via scoop/chocolatey/etc. won't resolve. Set MSYS2_PATH_TYPE=inherit in the relevant launcher .ini (mingw64.ini, ucrt64.ini, next to the exe in C:\msys64).

The gotcha: if you launch MSYS2 through Windows Terminal with the commonly documented

msys2_shell.cmd -defterm -here -no-start -ucrt64

the .ini file is never read. Only the GUI launchers (mingw64.exe, ucrt64.exe) read it — msys2_shell.cmd has its own separate flag:

msys2_shell.cmd -defterm -here -no-start -ucrt64 -use-full-path

Add -use-full-path straight to the Windows Terminal profile's commandline. Or, for a fix that works no matter how MSYS2 gets launched (Windows Terminal, VS Code, a script), set it once at the system level from an elevated shell:

[Environment]::SetEnvironmentVariable('MSYS2_PATH_TYPE', 'inherit', 'Machine')

Restart the terminal for it to take effect. Then pacman -S tmux, and you've got tmux running in an environment that otherwise looks and feels exactly like Git Bash.

GNU tar treats `Temp\2` as octal escape on Windows Git Bash

mktemp -d on Windows Git Bash returns a path like C:\Users\you\AppData\Local\Temp\2/tmp.XXX. The 2 after Temp\ is not a separator — GNU tar reads it as the start of an octal escape (\2\002), so any tar -C <that-path> silently rewrites the destination to a non-existent path and the extract aborts with Cannot open: No such file or directory.

The fix is to stop passing the bad path as an argument at all:

# Wrong — passes $dst as -C argument, tar mangles the backslashes
tar -xf archive.tar.gz -C "${dst}"

# Right — cd into it first, reference files by basename
( cd "${dst}" && tar -xf archive.tar.gz )

This is the same shape as --force-local, the other GNU-tar-on-Windows gotcha: GNU tar sees the leading C: of a Windows path and reads it as host:spec for a remote archive. --force-local only fixes the second one; only cd-ing into the destination fixes the first.

How a Baton test killed every tmux session with `kill(-1)`

A desktop restart can look like an XRDP or systemd problem. In this incident, the real culprit was a Rust test for Baton: it accidentally broadcast SIGTERM to every process owned by the test user.

The audit record showed:

proctitle=kill -TERM -1072950
syscall=kill ... a0=0xffffffff a1=SIGTERM
ppid=1072584 ... comm=kill exe=/usr/bin/kill

The parent process was named baton-5b9d31243. That is a cargo test binary under target/debug/deps, not a resident Baton service. The test was exercising max-duration handling with a sleep 5 child, so Baton really was only active because an agent was running tests on the Baton project.

The code intended to signal a process group:

kill -TERM -1234

On Linux, procps parses the negative group ID as another option unless the option list is terminated. The resulting syscall is kill(-1, SIGTERM), which signals every process the caller is allowed to signal. That killed the desktop session, kitty, browsers, the systemd user manager, and tmux sessions. mat-keeper could not protect itself because it ran as the same user.

The safe form is:

kill -TERM -- -1234

The fix was applied to both the Rust Baton implementation and my-ai-team's shell Baton transport, including test cleanup paths. The RDP connection switch was a coincidence in the timeline; it did not directly invoke Baton. Full Baton tests and the relevant my-ai-team regressions pass with the corrected argument handling.

XRDP session teardown can kill your default tmux server

An XRDP/Xfce connection is tied to a desktop and user-manager lifecycle, not only to the RDP window. If that session tears down, the default tmux server, kitty processes, and user services can disappear together without anyone running tmux kill-server.

On XPS, xrdp-sesman logged the window manager exiting with signal 15, then cleaned up the X server. XFCE reported broken PipeWire, D-Bus, and ICE connections; systemd subsequently stopped the user manager and kitty scopes. A simultaneous NVIDIA “fallen off the bus” failure was a strong suspect, but the old logs could not identify the original signal sender.

Keep long-lived agents on an explicit tmux socket supervised outside the desktop session:

sudo systemctl enable --now [email protected]
tmux -L mat list-sessions
tmux -L mat attach -t <session>

The mat-keeper session is only the service sentinel. MAT agent sessions share the mat server and should survive an XRDP switch. The default tmux a command still refers to the desktop-bound default socket, so use -L mat when recovering the fleet.

To make the next teardown attributable, enable auditd with a persistent signal rule and inspect the caller:

sudo ausearch -k process-signal -ts recent -i
sudo ausearch -k service-control -ts recent -i

Look for comm, exe, pid, ppid, and the target in OBJ_PID; these distinguish xrdp-sesman, systemd, and other senders. Auditd cannot reconstruct an earlier incident, and a desktop process can also exit because of D-Bus or GPU failure without a signal, so correlate its records with /var/log/xrdp-sesman.log, .xsession-errors, and journalctl.