<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dotfiles/systemd-units/user.txt, branch master</title>
<subtitle>My linux config and rc files</subtitle>
<id>https://git.sommerfeld.dev/dotfiles/atom/systemd-units/user.txt?h=master</id>
<link rel='self' href='https://git.sommerfeld.dev/dotfiles/atom/systemd-units/user.txt?h=master'/>
<link rel='alternate' type='text/html' href='https://git.sommerfeld.dev/dotfiles/'/>
<updated>2026-05-29T10:18:15Z</updated>
<entry>
<title>refactor(suspend): gate suspend on AC, drop bespoke zellij inhibit</title>
<updated>2026-05-29T10:18:15Z</updated>
<author>
<name>sommerfeld</name>
<email>sommerfeld@sommerfeld.dev</email>
</author>
<published>2026-05-29T10:18:15Z</published>
<link rel='alternate' type='text/html' href='https://git.sommerfeld.dev/dotfiles/commit/?id=ec3734c5ef9fcfe97c21cd19f198ec779ab5f052'/>
<id>urn:sha1:ec3734c5ef9fcfe97c21cd19f198ec779ab5f052</id>
<content type='text'>
New, simpler suspend policy:

  AC plugged in   -&gt; never auto-suspends (lid close ignored, idle no-op)
  On battery only -&gt; lid close suspends, swayidle suspends at 30 min idle

This replaces the SSH/zellij-aware inhibit machinery with a rule that
matches the user's mental model: if you don't want the machine to
sleep, plug it in. Long-running tasks (builds, downloads, SSH
sessions, headless services) just need AC.

Changes:

* etc/systemd/logind.conf.d/20-lid-ac.conf: set
  HandleLidSwitchExternalPower=ignore so logind itself handles the AC
  case at the source. No userspace daemon, no race, no rate-limit risk.
* dot_local/bin/on-battery-suspend: tiny POSIX wrapper that exits 0
  when any /sys/class/power_supply/{AC,ADP}*/online == 1, else execs
  `systemctl suspend`.
* dot_config/systemd/user/swayidle.service: add
  `timeout 1800 on-battery-suspend`. Idle suspend now exists, but only
  when on battery.
* Delete zellij-inhibit-suspend.{path,service} + watcher script and
  remove the entry from systemd-units/user.txt. The .path
  re-trigger storm bug is moot because the whole mechanism is gone.

Manual suspends (sway XF86Sleep keybind, sway power submode `s`,
`systemctl suspend` over SSH) still always work regardless of AC --
explicit user intent wins.

Also drop /migrate-podman-to-btrfs.sh from .gitignore; the one-off
migration script has been deleted now that the user has switched their
podman storage to the btrfs driver.

On-host steps to apply:

  chezmoi apply -v
  systemctl --user daemon-reload
  systemctl --user reset-failed zellij-inhibit-suspend.service zellij-inhibit-suspend.path || true
  systemctl --user stop zellij-inhibit-suspend.path zellij-inhibit-suspend.service || true
  systemctl --user disable zellij-inhibit-suspend.path || true
  systemctl --user restart swayidle.service
  # logind drop-in is reloaded automatically by the etc deploy script.

Verify:

  systemctl status systemd-logind | grep -i lid
  loginctl show-session $XDG_SESSION_ID | grep -i lid
  # Unplug AC -&gt; close lid -&gt; should suspend.
  # Plug AC   -&gt; close lid -&gt; nothing happens.
</content>
</entry>
<entry>
<title>feat(suspend): hold inhibit lock while any zellij session exists</title>
<updated>2026-05-29T10:18:13Z</updated>
<author>
<name>sommerfeld</name>
<email>sommerfeld@sommerfeld.dev</email>
</author>
<published>2026-05-29T10:18:13Z</published>
<link rel='alternate' type='text/html' href='https://git.sommerfeld.dev/dotfiles/commit/?id=01df321e907b6c8568bb8622eb44a5c1486a0631'/>
<id>urn:sha1:01df321e907b6c8568bb8622eb44a5c1486a0631</id>
<content type='text'>
The SSH-shell inhibitor in dot_zprofile is bound to the lifetime of the
login shell, so it disappears the moment the user detaches a zellij
session and disconnects — defeating the whole point of using zellij for
persistent remote work.

Add a user-scope path+service+watcher trio that ties the inhibit lock
to the existence of zellij sessions instead:

  - dot_local/bin/executable_zellij-inhibit-watcher
      Polls `zellij list-sessions --short` every 15s, exits when none
      remain. Override poll interval via $ZELLIJ_INHIBIT_POLL.

  - dot_config/systemd/user/zellij-inhibit-suspend.service
      Wraps the watcher in `systemd-inhibit --what=sleep:idle:handle-lid-switch
      --mode=block`. When the watcher exits, the service stops and the
      lock is released.

  - dot_config/systemd/user/zellij-inhibit-suspend.path
      Activates the service whenever $XDG_RUNTIME_DIR/zellij becomes
      non-empty (i.e. zellij creates its first session socket). Re-fires
      on every empty→non-empty transition.

Enable via systemd-units/user.txt (the .path unit; the service is
on-demand).

The existing SSH-shell inhibitor is kept as a backstop for non-zellij
remote sessions and is now documented as such.

VM (nix/vm.nix) deliberately not updated: the Ubuntu remote-dev VM
never suspends, so the inhibit machinery would be inert there.
</content>
</entry>
<entry>
<title>feat: teams autostart, llama-cpp-vulkan ignore, snxctl-chromium wrapper</title>
<updated>2026-05-14T09:58:37Z</updated>
<author>
<name>sommerfeld</name>
<email>sommerfeld@sommerfeld.dev</email>
</author>
<published>2026-05-14T09:58:37Z</published>
<link rel='alternate' type='text/html' href='https://git.sommerfeld.dev/dotfiles/commit/?id=3fc4705b6c711871a920290feef138694e72d29a'/>
<id>urn:sha1:3fc4705b6c711871a920290feef138694e72d29a</id>
<content type='text'>
systemd/user/teams-{sii,xsight}.service: autostart both Teams flatpak
profiles on sway-session.target login. KillMode=mixed so SIGTERM hits
only the wrapper process — both instances share the same flatpak app
id, so killing by app id would take down the sibling instance. A 15s
SIGKILL fallback covers the case where Electron tray-hides instead of
quitting. Both units listed in systemd-units/user.txt.

etc/pacman.conf: IgnorePkg = llama-cpp-vulkan. The AUR package rebuilds
on every llama.cpp commit (multi-hour build). Update manually with
`paru -S llama-cpp-vulkan` when intended.

snxctl-chromium wrapper:
- dot_local/share/snx-rs/bin/xdg-open: shim that flatpak-runs
  ungoogled-chromium, used only by snx-rs.
- dot_config/systemd/user/snx-rs.service.d/10-chromium-saml.conf:
  drop-in prepending that dir to the daemon's PATH so snx-rs's
  opener-crate call to xdg-open lands in chromium, without affecting
  xdg-open for any other process.
- dot_local/bin/snxctl-chromium: convenience wrapper that
  daemon-reloads and restarts snx-rs.service if the drop-in isn't yet
  applied, then execs `snxctl connect`.

firefox/user-overrides.js: revert the dom.security.https_only_mode.
upgrade_local and network.lna.local-network-to-localhost.skip-checks
prefs — they didn't actually fix the SAML flow. Replaced with a
comment pointing to the wrapper instead.
</content>
</entry>
<entry>
<title>feat(sway): enable swayr auto-tile via systemd user unit</title>
<updated>2026-05-13T12:43:42Z</updated>
<author>
<name>sommerfeld</name>
<email>sommerfeld@sommerfeld.dev</email>
</author>
<published>2026-05-13T12:43:42Z</published>
<link rel='alternate' type='text/html' href='https://git.sommerfeld.dev/dotfiles/commit/?id=fd512ffcd1206260e8cf17e8bed0273c64658d30'/>
<id>urn:sha1:fd512ffcd1206260e8cf17e8bed0273c64658d30</id>
<content type='text'>
Vanilla sway only has splith/splitv with no auto-orientation, so new
windows always split along whatever axis the parent container is set
to (default splith). The result: opening a third window in a workspace
that's already split horizontally just keeps stacking horizontally,
even when each pane is now narrower than it is tall.

swayr's daemon (swayrd) subscribes to sway IPC and, with
[layout].auto_tile = true, issues splith or splitv on the focused
container based on its width-vs-height before sway places the next
window. The result is the i3/awesome-style spiral tiling: each new
window splits the focused pane along its longest side.

Run swayrd as a systemd user service bound to sway-session.target so
it starts/stops with the session (matching the pattern used by
waybar, swayidle, mako, etc.). No keybind changes; only the placement
algorithm.
</content>
</entry>
<entry>
<title>fix(secrets): use pass-secret-service-bin and enable user unit</title>
<updated>2026-05-13T12:43:38Z</updated>
<author>
<name>sommerfeld</name>
<email>sommerfeld@sommerfeld.dev</email>
</author>
<published>2026-05-13T12:43:38Z</published>
<link rel='alternate' type='text/html' href='https://git.sommerfeld.dev/dotfiles/commit/?id=cf62ad254ba4c4024b84e1e86009110e1cd88572'/>
<id>urn:sha1:cf62ad254ba4c4024b84e1e86009110e1cd88572</id>
<content type='text'>
The python pass-secret-service AUR package is unmaintained. Switch to
grimsteel's actively-maintained Rust implementation (-bin variant for
faster install) and enable the shipped user systemd unit so the
service is visible to systemctl --user status, not just lazily
D-Bus-activated.
</content>
</entry>
<entry>
<title>feat(desktop): xdg-desktop-portal pinning, wob OSD, mako DND toggle</title>
<updated>2026-05-13T12:43:37Z</updated>
<author>
<name>sommerfeld</name>
<email>sommerfeld@sommerfeld.dev</email>
</author>
<published>2026-05-13T12:43:37Z</published>
<link rel='alternate' type='text/html' href='https://git.sommerfeld.dev/dotfiles/commit/?id=12b08e19cbd65988c51de40ffd06ce01418c2bb6'/>
<id>urn:sha1:12b08e19cbd65988c51de40ffd06ce01418c2bb6</id>
<content type='text'>
- xdg-desktop-portal: pin wlr for ScreenCast/Screenshot, gtk for the
  rest, so flatpak browsers (Meet, Slack, Discord) get a working
  screen-share path instead of whatever the portal frontend happens to
  pick first.
- wob: small wayland overlay bar fed via a fifo. New vol-osd.sh /
  brightness-osd.sh wrappers replace the bare pactl/brightnessctl
  invocations in keybinds so adjusting volume or backlight flashes a
  bar at the bottom of the screen. wob.service owns the fifo lifecycle
  (mkfifo before, rm after).
- mako: add a [mode=do-not-disturb] section that hides notifications
  while the mode is active, plus a Super+x n submode binding to toggle
  it. Notifications still accumulate in history; just no popups.
</content>
</entry>
<entry>
<title>feat(sway): browser-aware idle inhibits + post-resume lock grace</title>
<updated>2026-05-13T12:43:36Z</updated>
<author>
<name>sommerfeld</name>
<email>sommerfeld@sommerfeld.dev</email>
</author>
<published>2026-05-13T12:43:36Z</published>
<link rel='alternate' type='text/html' href='https://git.sommerfeld.dev/dotfiles/commit/?id=d7fac4bbf607a894219065dc9b87f6d495d9ebab'/>
<id>urn:sha1:d7fac4bbf607a894219065dc9b87f6d495d9ebab</id>
<content type='text'>
Two related session-idle improvements:

1. ScreenSaver inhibit bridge. Browsers (LibreWolf/Chromium flatpaks)
   ask the session not to idle via the legacy
   org.freedesktop.ScreenSaver D-Bus API during video calls and
   fullscreen video; swayidle only honors logind's BlockInhibited
   property. Add inhibridge as a user unit to translate the former
   into the latter, so e.g. a Google Meet tab now keeps the screen
   from locking, dimming and (downstream) suspending.

2. Post-resume grace period. Locking on before-sleep meant every wake
   demanded the password even for a quick check. Replace with:
     before-sleep -&gt; only pause media
     after-resume -&gt; resume-lock-grace.sh 30
   The grace script runs a one-shot swayidle that locks iff the user
   stays idle for 30s after the wake, with a watchdog that exits as
   soon as swaylock comes up (or after a hard cap) so it never lingers
   alongside the main swayidle. The 5-min main idle-lock and explicit
   loginctl lock-session paths are unchanged.
</content>
</entry>
<entry>
<title>refactor(meta): flatten groups; only break out optional/hw-specific</title>
<updated>2026-05-13T12:43:32Z</updated>
<author>
<name>sommerfeld</name>
<email>sommerfeld@sommerfeld.dev</email>
</author>
<published>2026-05-13T12:43:32Z</published>
<link rel='alternate' type='text/html' href='https://git.sommerfeld.dev/dotfiles/commit/?id=2970b9dddc6b89705a6836fa0864fb841925ff6f'/>
<id>urn:sha1:2970b9dddc6b89705a6836fa0864fb841925ff6f</id>
<content type='text'>
Drop the per-domain group fragmentation in meta/ and the parallel
group-per-file structure in systemd-units/.

  meta/ (18 -&gt; 6 groups):
    keep    base, flatpak (magic), intel, nvidia, work, btc
    fold    browser, bt, cpp, dev, extra, fonts, mail, media, nix,
            sound, wayland -&gt; base (with `# --- section ---` comments
            preserving at-a-glance structure)
    drop    fortran (niche; install ad-hoc when needed)

  systemd-units/:
    flatten to a single system.txt + user.txt; .ignore files move up
    one level; group concept and pairing rule removed.

  justfile:
    unit-list/unit-apply/unit-status no longer take a group argument.
    unit-add/unit-forget infer scope by probing
    `systemctl [--user] cat &lt;unit&gt;` (system wins on tie). Top-level
    add/forget dispatcher updated: any unit-suffixed arg routes to
    unit-* without requiring a leading GROUP.

  docs:
    .github/copilot-instructions.md and README.md updated to describe
    the new flat layout. Pairing rule and group-token grammar gone.

Pure layout refactor - no package contents change.
</content>
</entry>
</feed>
