<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dotfiles/etc/systemd, branch master</title>
<subtitle>My linux config and rc files</subtitle>
<id>https://git.sommerfeld.dev/dotfiles/atom/etc/systemd?h=master</id>
<link rel='self' href='https://git.sommerfeld.dev/dotfiles/atom/etc/systemd?h=master'/>
<link rel='alternate' type='text/html' href='https://git.sommerfeld.dev/dotfiles/'/>
<updated>2026-05-29T10:18:16Z</updated>
<entry>
<title>feat(etc/resolved): forward single-label queries upstream</title>
<updated>2026-05-29T10:18:16Z</updated>
<author>
<name>sommerfeld</name>
<email>sommerfeld@sommerfeld.dev</email>
</author>
<published>2026-05-29T10:18:16Z</published>
<link rel='alternate' type='text/html' href='https://git.sommerfeld.dev/dotfiles/commit/?id=fdba57c9c05f321d3a75ae8f2e46e4053193744f'/>
<id>urn:sha1:fdba57c9c05f321d3a75ae8f2e46e4053193744f</id>
<content type='text'>
Enables ResolveUnicastSingleLabel=yes so non-FQDN names like
'sw-jenkins01' get sent to the configured DNS server instead of
being dropped to LLMNR/mDNS. Needed for corp shortname resolution
via Pi-hole CNAME records that point at *.xsight.ent (resolved by
unbound's forward-zone over the new WireGuard bridge).
</content>
</entry>
<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): re-enable suspend on s2idle, drop diagnostic scaffolding</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=6e0c5c33438e5e898bd075c33a45b3abf9d1b26b'/>
<id>urn:sha1:6e0c5c33438e5e898bd075c33a45b3abf9d1b26b</id>
<content type='text'>
Confirmed root cause: this hardware's S3 (deep) firmware path triggers a
fatal wake-from-suspend hang only on linux-hardened. INIT_ON_FREE + slab
hardening + tighter locking turn a latent driver race that stock linux
gets away with into an unrecoverable panic so early the journal isn't
even flushed. mem_sleep_default=s2idle bypasses the BIOS S3 path
entirely (s0ix is a pure-kernel low-power state) and suspends/resumes
reliably under hardened.

This is a widespread Lenovo S3 firmware issue across post-2018
ThinkPads (see Ubuntu T560, X1C9/10/11 reports). Lenovo themselves
moved newer firmwares to s2idle-only. Not a linux-hardened bug per se;
just hardened being a strict enough kernel to make the bug fatal.

Keep:
* mem_sleep_default=s2idle in etc/kernel/cmdline-linux-hardened.tmpl
  (only the hardened UKI; stock linux keeps unchanged shared cmdline)

Revert (all the diagnostic / speculative scaffolding from the last
few commits):
* MODULES=(intel_lpss_pci) → MODULES=()  — Arch wiki touchpad fix was
  not the cause here
* nmi_watchdog=panic softlockup_panic=1 panic=10 — only needed to
  auto-reboot during diagnosis
* no_console_suspend — diagnostic-only
* etc/systemd/logind.conf.d/20-no-suspend.conf  — masking workaround
* sleep-target masking block in run_onchange_after_deploy-etc.sh.tmpl,
  replaced with a one-shot cleanup that removes any leftover
  /dev/null symlinks from systems that ran the previous version
* systemd-pstore.service from systemd-units/system.txt — added only to
  catch the diagnostic panic
* diagnose-suspend.sh helper (and its .gitignore/.chezmoiignore entries)
* sway suspend → lock-session keybind workaround
* power-menu.sh Suspend entry restoration
* KEYBINDS.md docs
</content>
</entry>
<entry>
<title>feat(suspend): disable system suspend until hardened kernel resume issue is fixed</title>
<updated>2026-05-29T10:18:14Z</updated>
<author>
<name>sommerfeld</name>
<email>sommerfeld@sommerfeld.dev</email>
</author>
<published>2026-05-29T10:18:14Z</published>
<link rel='alternate' type='text/html' href='https://git.sommerfeld.dev/dotfiles/commit/?id=3be68c032c864fa98ed85e54ea5af19976c55ed7'/>
<id>urn:sha1:3be68c032c864fa98ed85e54ea5af19976c55ed7</id>
<content type='text'>
linux-hardened wedges on resume from S3 (NVMe/i915/iwlwifi driver UAF
exposed by INIT_ON_FREE + slab hardening). Until root-caused, take
suspend off the table while keeping lock + DPMS intact.

- etc/systemd/logind.conf.d/20-no-suspend.conf: lid close, suspend
  key, hibernate key all map to 'lock'; IdleAction=ignore (swayidle
  drives DPMS+swaylock independently).
- run_onchange_after_deploy-etc.sh.tmpl: mask sleep.target,
  suspend.target, hibernate.target, hybrid-sleep.target,
  suspend-then-hibernate.target via /etc/systemd/system -&gt; /dev/null
  symlinks. Catches 'systemctl suspend' from any source.
- dot_config/sway/config: XF86Sleep and system-mode 's' now run
  loginctl lock-session instead of systemctl suspend.
- dot_config/sway/executable_power-menu.sh: drop Suspend entry.
- KEYBINDS.md: reflect new behaviour.

To re-enable later: remove the logind drop-in + symlink loop, then
sudo systemctl daemon-reload.
</content>
</entry>
<entry>
<title>feat(suspend): bounce snx-rs around system sleep</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=7ad6f474634f7c359264053bf0f8e93a9bdd37b1'/>
<id>urn:sha1:7ad6f474634f7c359264053bf0f8e93a9bdd37b1</id>
<content type='text'>
snx-rs (Check Point VPN) doesn't notice that its tunnel died during
suspend: the IKE keepalive is interrupted and the SAML cookie may
expire, but the daemon happily sits on dead sockets after resume.
`snxctl status` keeps reporting "Connected" while no traffic
actually flows, so the user has to manually disconnect+reconnect.

Install an /etc/systemd/system-sleep/ hook that stops the user-scope
snx-rs.service before suspend and starts it on resume. The tunnel is
left disconnected after resume; the waybar toggle (or any
`snxctl connect`) re-establishes it, going through SAML only if the
cached cookie has actually expired.

The hook enumerates logged-in users via loginctl and skips any that
don't have snx-rs.service enabled, so it's a no-op on machines that
don't use the VPN.

Also teach run_onchange_after_deploy-etc.sh.tmpl to install files
under etc/systemd/system-sleep/ with mode 0755 (systemd ignores
sleep hooks that aren't executable).
</content>
</entry>
<entry>
<title>fix(net): positive-match physical NICs into bond0</title>
<updated>2026-05-22T13:28:17Z</updated>
<author>
<name>sommerfeld</name>
<email>sommerfeld@sommerfeld.dev</email>
</author>
<published>2026-05-22T13:28:17Z</published>
<link rel='alternate' type='text/html' href='https://git.sommerfeld.dev/dotfiles/commit/?id=1dfee96401c03666aa3eeec7a40fbc42ff05e1b3'/>
<id>urn:sha1:1dfee96401c03666aa3eeec7a40fbc42ff05e1b3</id>
<content type='text'>
Name= negation list was failing in practice — veth/waydroid interfaces
were still being enslaved into bond0, taking down host networking.

Switch to positive matching: Path=pci-*|platform-* AND Name=en* AND
Type=ether. Virtual interfaces (veth, virbr, waydroid0, docker0, ...)
have no udev ID_PATH and never start with 'en', so they're cleanly
excluded by the AND of all three keys.
</content>
</entry>
<entry>
<title>fix(net): keep waydroid0 out of bond0, allow it through nftables</title>
<updated>2026-05-22T13:28:17Z</updated>
<author>
<name>sommerfeld</name>
<email>sommerfeld@sommerfeld.dev</email>
</author>
<published>2026-05-22T13:28:17Z</published>
<link rel='alternate' type='text/html' href='https://git.sommerfeld.dev/dotfiles/commit/?id=18277fc1ec921dfcfa61c0b2f0b40fb62cfa070f'/>
<id>urn:sha1:18277fc1ec921dfcfa61c0b2f0b40fb62cfa070f</id>
<content type='text'>
systemd-networkd's Type=ether matcher was enslaving waydroid0 into
bond0 the moment 'waydroid session start' ran, taking down the host's
default route. Mirror the libvirt/docker negation pattern.

Also mirror the existing virbr0 forward accepts for waydroid0 so the
Android container can actually reach the internet through MASQUERADE.
</content>
</entry>
<entry>
<title>fix(networkd): exclude virtual taps/bridges from bond0 enslavement</title>
<updated>2026-05-13T12:43:41Z</updated>
<author>
<name>sommerfeld</name>
<email>sommerfeld@sommerfeld.dev</email>
</author>
<published>2026-05-13T12:43:41Z</published>
<link rel='alternate' type='text/html' href='https://git.sommerfeld.dev/dotfiles/commit/?id=8567dd49e9e688f26dc0b266f211655974539299'/>
<id>urn:sha1:8567dd49e9e688f26dc0b266f211655974539299</id>
<content type='text'>
Type=ether matches ALL L2 ethernet interfaces, including libvirt-created
vnet* tap devices. Without Name= negations, when a VM starts its tap is
pulled into bond0 instead of staying with virbr0, killing DHCP/NAT for
the guest (Windows ends up with a 169.254.x APIPA address).

Add Name= negations to skip libvirt taps/bridges, generic taps, and
common container engine virtual interfaces.
</content>
</entry>
<entry>
<title>feat(lostfiles): filter known/private/cache paths via auto-synced ignore</title>
<updated>2026-05-13T12:43:39Z</updated>
<author>
<name>sommerfeld</name>
<email>sommerfeld@sommerfeld.dev</email>
</author>
<published>2026-05-13T12:43:39Z</published>
<link rel='alternate' type='text/html' href='https://git.sommerfeld.dev/dotfiles/commit/?id=2026f98babdcdd34c09eb59479a02a5603fc70a2'/>
<id>urn:sha1:2026f98babdcdd34c09eb59479a02a5603fc70a2</id>
<content type='text'>
Upstream lostfiles has no extension mechanism; the weekly report ends
up dominated by files this repo intentionally deploys plus host-private
files we deliberately don't track plus regenerated GTK caches.

Add etc/lostfiles.ignore.tmpl which renders /etc/lostfiles.ignore from
two sources:
  1. Every file under etc/ in the repo (auto-enumerated at chezmoi-apply
     time, same find-sort pattern the etc deploy script uses). This
     keeps the ignore list in sync with what we actually deploy with
     zero manual maintenance.
  2. A static block for: the sudo-i symlink, host-private
     systemd-networkd units (99-hodor*, 99-mandibles*) which contain
     WireGuard secrets, the getty@tty1 autologin override which
     contains the username, and known pacman-hook-generated caches
     under /usr/lib/{gdk-pixbuf-2.0,gtk-4.0}/.

Wrap /usr/bin/lostfiles in lostfiles.service via grep -vFxf, with a
fallback when /etc/lostfiles.ignore doesn't yet exist (first deploy).
</content>
</entry>
<entry>
<title>feat(lostfiles): weekly unowned-files refresh + waybar reminder</title>
<updated>2026-05-13T12:43:35Z</updated>
<author>
<name>sommerfeld</name>
<email>sommerfeld@sommerfeld.dev</email>
</author>
<published>2026-05-13T12:43:35Z</published>
<link rel='alternate' type='text/html' href='https://git.sommerfeld.dev/dotfiles/commit/?id=54eacef87b906835f9778d9c4b02128398bf88d0'/>
<id>urn:sha1:54eacef87b906835f9778d9c4b02128398bf88d0</id>
<content type='text'>
Wiring (mirrors arch-audit, with weekly cadence and Nice=19/idle I/O):
  lostfiles.timer (weekly, Persistent=true, RandomizedDelaySec=1h)
   → lostfiles.service
       → /run/lostfiles.txt   (default mode — strict produces too many
                                false positives for a passive reminder)
        → custom/lostfiles waybar module (interval 600s)
           → mako 'normal' once/7d while count &gt; 0
           → on-click: `ghostty -e nvim -R /run/lostfiles.txt`

Default mode (no `strict` argument) is intentional: it already filters
the package's curated false-positive list at /etc/lostfiles.conf, which
is what we want for a low-noise weekly nudge. Switching to `strict` is
a one-line change in lostfiles.service if signal-vs-noise tilts later.
</content>
</entry>
</feed>
