aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/dot_local
Commit message (Collapse)AuthorAgeFilesLines
* refactor(suspend): gate suspend on AC, drop bespoke zellij inhibitLibravatar sommerfeld3 days2-81/+18
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | New, simpler suspend policy: AC plugged in -> never auto-suspends (lid close ignored, idle no-op) On battery only -> 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 -> close lid -> should suspend. # Plug AC -> close lid -> nothing happens.
* fix(suspend): make zellij inhibit watcher resilient to local-only sessionsLibravatar sommerfeld3 days1-7/+49
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | The previous watcher exited immediately whenever no SSH-spawned zellij was present. That caused a start-rate-limit storm: .path triggers service (zellij dir non-empty) -> watcher exits because no SSH zellij -> service stops -> .path retriggers (zellij dir still non-empty) -> ... 5 starts in 10s, systemd stops the path unit -> no inhibitor ever again, even after you SSH in Restructure so the watcher stays alive for the entire zellij socket directory lifetime and acquires/releases its own systemd-inhibit lock dynamically based on SSH-zellij presence: * Watcher now polls and exits only when the zellij socket dir is empty, matching the .path's trigger condition so it never re-fires while zellij is alive. * systemd-inhibit removed from ExecStart - watcher self-inhibits via a child 'systemd-inhibit ... sleep infinity' it can terminate on demand. * StartLimitIntervalSec=0 on the service as belt-and-braces against any future regression of the cycle. Recovery from the rate-limit hit: systemctl --user reset-failed zellij-inhibit-suspend.service zellij-inhibit-suspend.path systemctl --user daemon-reload systemctl --user restart zellij-inhibit-suspend.path
* fix(suspend): only inhibit for SSH-spawned zellij sessionsLibravatar sommerfeld3 days1-11/+30
| | | | | | | | | | | | | | | | | | | A local zellij session (sway terminal, attended) shouldn't keep the laptop awake — that's the user actively in front of the machine, and normal suspend behaviour should apply. Only zellij sessions that were spawned from an SSH context need the persistent inhibit, so detach + disconnect leaves the host awake until the session ends. Use /proc/<pid>/environ to detect SSH-spawned zellij: the daemonised zellij server is exec'd by the client and Linux preserves the exec-time environment for the life of the process, so SSH_CONNECTION= survives the SSH session closing. Walk every running `zellij` pid; hold the lock as long as at least one of them has SSH_CONNECTION in its environ. The .path unit still fires on every zellij socket creation, but if no SSH-spawned zellij exists the watcher exits immediately and the service stops with no harm done — a couple of cheap process spawns per local session start, no inhibitor side-effects.
* feat(suspend): hold inhibit lock while any zellij session existsLibravatar sommerfeld3 days1-0/+20
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* feat(sandbox): bwrap wrappers for mpv, yt-dlp, streamlinkLibravatar sommerfeld3 days4-0/+71
| | | | | | | | | | | | | | | | | | | | | | | | | These three tools are the native (non-flatpak) network parsers in the install set — every other internet-facing app is already flatpak'd. The threat model is a RCE in a subtitle/extractor/muxer that walks $HOME looking for SSH keys, GPG keyring, pass store, cloud tokens, etc. Approach (defence in depth, not full sandboxing): - bwrap --bind / / keeps Wayland, PipeWire, DBus, GPU, hwaccel and all config files working transparently. - --tmpfs over known-sensitive dirs (.ssh, .gnupg, .password-store, .config/gh, .config/op, .aws, .local/share/keyrings) blanks them from the sandbox view; a compromised parser literally cannot see them. - inner PATH stripped of ~/.local/bin so streamlink's spawn of `mpv` resolves to /usr/bin/mpv and does not re-enter the sandbox. - --die-with-parent + --new-session for tidy lifecycle. - Escape hatch: SANDBOX=0 mpv ... bypasses for one invocation. - Graceful degradation if bwrap is missing (warns and execs anyway). bubblewrap added explicitly to meta/base.txt (was implicit via flatpak). Wrappers in ~/.local/bin shadow /usr/bin via dot_zprofile:15 PATH order. Not symlinked into the Ubuntu VM (nix/vm.nix does not touch ~/.local/bin), which is fine: those tools on the headless VM don't need sandboxing.
* revert: drop snxctl-chromium wrapper, snx-rs works with default browser nowLibravatar sommerfeld12 days2-31/+0
| | | | | | | | | | | | User confirms snx-rs's SAML loopback no longer needs chromium routing. Remove: - dot_local/bin/snxctl-chromium (PATH-override wrapper) - dot_local/share/snx-rs/bin/xdg-open (chromium shim) - snx-rs LibreWolf SAML note in user-overrides.js The waybar snx-vpn toggle now just runs `snxctl connect` detached, no wrapper indirection.
* refactor(snxctl-chromium): drop daemon drop-in, override snxctl's PATHLibravatar sommerfeld2026-05-142-32/+15
| | | | | | | | | | | | snx-rs.service is a system unit, not --user, so the prior approach of overriding the daemon's PATH via a systemd drop-in could never apply. And it wasn't needed anyway: snxctl itself runs opener::open(url) in-process, so prepending the shim dir to snxctl's PATH is enough. - Drop dot_config/systemd/user/snx-rs.service.d/10-chromium-saml.conf. - snxctl-chromium now just sets PATH and exec's snxctl connect. - xdg-open shim no longer forces --new-window so chromium can reuse a warm window (faster SAML round-trip).
* feat: teams autostart, llama-cpp-vulkan ignore, snxctl-chromium wrapperLibravatar sommerfeld2026-05-142-0/+48
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* feat(teams): distinct tray icons for Sii (blue S) and XSight (orange X)Libravatar sommerfeld2026-05-134-4/+4
| | | | | | | | | | | | | Adds two generated 256x256 hicolor PNG icons under ~/.local/share/icons/hicolor/256x256/apps/ and wires them up: * Icon=teams-{sii,xsight} -> launcher / waybar / sway use them * --appIcon=<absolute path> -> electron tray icon picks them up (teams-for-linux respects this flag) The flatpak override script gains a --filesystem=xdg-data/icons:ro binding for com.github.IsmaelMartinez.teams_for_linux so the absolute icon path is reachable from inside the sandbox.
* feat(teams): add Sii + XSight Teams-for-Linux profile launchersLibravatar sommerfeld2026-05-133-0/+27
| | | | | | | | | | | Two flatpak-tailored .desktop entries that run separate isolated instances of teams-for-linux via --class / --user-data-dir / --appTitle. Profile data lives under $HOME/.var/app/<id>/config/profile-{sii,xsight}/ which is always sandbox-writable. The upstream flatpak .desktop is shadowed by an XDG_DATA_HOME entry of the same basename with NoDisplay=Hidden=true so only the two profile launchers appear in fuzzel.
* style: apply formatter drift across repoLibravatar sommerfeld2026-05-132-4/+7
| | | | | | | Pre-existing whitespace/style drift caught by `just check`. Touch nothing semantic — pure formatter output (shfmt -i 2 -ci -s, ruff, prettier, taplo). Excludes dot_config/clangd/config.yaml whose manual indentation is intentionally preserved.
* feat(privesc): migrate from opendoas to sudo-rsLibravatar sommerfeld2026-05-132-262/+0
| | | | | | | | | | | | | | | | | | | | | | | | | | | doas's one-shot password and absent 'sudo -v' kept wasting hour-long paru AUR builds. sudo-rs is a memory-safe Rust rewrite (ISRG/Ferrous Systems), drop-in CLI compatible, and the same one Ubuntu 25.10 ships as default. We follow the Arch wiki 'Using sudo-rs without the sudo package' recipe verbatim — no custom shims. - meta/base.txt: -doas-sudo-shim +sudo-rs - etc/sudoers-rs (mode 0440): wiki minimal config + NOPASSWD reboot/poweroff - etc/pam.d/sudo: 4-line copy of upstream sudo's PAM file - run_onchange_after_deploy-etc.sh.tmpl: use real sudo, deploy sudoers-rs at 0440, create /etc/pam.d/sudo-i and /usr/local/bin/{sudo,sudoedit, su,visudo} → sudo-rs symlinks idempotently - delete etc/doas.conf, dot_local/bin/{doasedit,sudo} - zshrc: drop sudo=doas/sudoedit=doasedit aliases; rewrite ss/gimme/ pacdiff/ssys to call sudo - justfile: s/doas/sudo/g (status/diff/restore helpers) - nvim: rename :DoasWrite → :SudoWrite (uses sudo -S) - sway config: reboot/poweroff buttons call sudo - bootstrap.sh: update step-5 comment - README/KEYBINDS/copilot-instructions: flip the privesc convention No Defaults overrides: sudo's defaults (passwd_tries=3, timestamp_timeout=5) already fix the doas pain, and paru SudoLoop (kept) refreshes the 5-min window via real sudo -v.
* feat(doas): smarter sudo shim + paru SudoLoopLibravatar sommerfeld2026-05-131-0/+59
| | | | | | | | | | | | | | | | | | | The packaged doas-sudo-shim is just 'exec doas "$@"', which means 'sudo -v' (used by paru --sudoloop to keep the auth timestamp fresh during long AUR builds) hits doas, which doesn't implement -v, and the loop dies. Then when the build finally finishes and tries the real install, the cached timestamp has long expired, so we reprompt — and opendoas only allows one attempt before bailing, so a single mistype throws an hour of compilation away. Replace it (per-user, via $HOME/.local/bin precedence) with a shim that translates: -v -> doas true (refresh persist timestamp) -k / -K -> doas -L (clear) -E -H -i -S etc -> dropped (no doas equivalent) rest -> doas "$@" Then enable SudoLoop in paru.conf so the timestamp stays fresh.
* feat(browser): migrate librewolf to flatpak for host-isolationLibravatar sommerfeld2026-05-131-1/+1
| | | | | | | | | | | | | | | | | | | | Move LibreWolf from native librewolf-bin to Flathub io.gitlab.librewolf-community. Bubblewrap isolates the browser from $HOME (\\.ssh, password-store, gnupg, ssh-agent socket) at the cost of namespace chroot + IPC/network namespace isolation between content processes (mozilla bug 1756236, P3, considered defense-in-depth). seccomp-bpf — the dominant sandbox layer — is preserved. - meta/flatpak.txt: + io.gitlab.librewolf-community - meta/browser.txt: - librewolf-bin - run_onchange_after_deploy-firefox.sh.tmpl: profile path moves to ~/.var/app/io.gitlab.librewolf-community/.librewolf - dot_config/mimeapps.list: librewolf.desktop -> flatpak app id - dot_local/bin/executable_linkhandler: flatpak run wrapper - README.md: blurb + new profile path arkenfox-user.js + chezmoi user-overrides.js deploy keep working unchanged because the flatpak profile is still on the host fs.
* perf(dictate): switch default model to base for ~5x speedupLibravatar sommerfeld2026-05-131-2/+2
| | | | | | | | | | large-v3-turbo-q5_0 ran ~1-2x realtime on the T490's CPU, making push-to-talk feel sluggish. The base multilingual model is ~142 MB (vs 547 MB) and runs ~7-10x realtime, dropping perceived latency on short utterances from a few seconds to near-instant. Quality on short EN/PT dictation remains usable; bump WHISPER_MODEL to small or large-v3-turbo if accuracy matters more than latency.
* feat(sway): add dictate (whisper.cpp) and ocr (tesseract) keybindsLibravatar sommerfeld2026-05-132-0/+126
| | | | | | | | | | | | Push-to-talk dictation toggle on Super+i: parecord captures 16 kHz mono WAV, whisper-cli transcribes (auto language), output is typed via wtype and copied to the clipboard. Region OCR on Super+Shift+o: slurp + grim feed tesseract (eng+por), result lands in the clipboard with a notification preview. Adds wtype to wayland.txt; tesseract (+eng/por data) and whisper.cpp + the large-v3-turbo-q5_0 model package to extra.txt.
* refactor(mail): drop TUI stack, add headless proton-bridgeLibravatar sommerfeld2026-05-132-20/+1
| | | | | | | | | Remove aerc, khal, khard, vdirsyncer from meta/mail.txt and delete their configs (aerc/, vdirsyncer systemd override, aerc .desktop handler). Point linkhandler mailto at xdg-open until a GUI client is set up. Add systemd user unit for protonmail-bridge --noninteractive, tied to graphical-session.target so it starts with the sway session.
* efistub -> UKI migrationLibravatar sommerfeld2026-04-211-55/+0
| | | | | | | Track /etc/kernel/cmdline and enable default_uki/fallback_uki in linux.preset. Remove create-efi helper (UKI is self-contained; only needed once at install time). Update bootstrap to print the one-off efibootmgr command instead of launching create-efi.
* fix(zprofile): export DISPLAY=:0 for XWayland before exec swayLibravatar sommerfeld2026-04-211-4/+0
| | | | | | | | | | | | | | Sway's XWayland is lazy — DISPLAY isn't set in sway's env until the first X client connects, which means systemctl/dbus import-environment runs too early and nothing downstream sees DISPLAY. Setting DISPLAY=:0 in zprofile before 'exec sway' ensures sway itself inherits it, and therefore so do all its child processes (terminals, scripts, systemd user services via import-environment). XWayland will spawn on demand when a client actually connects to :0. Also drop the redundant fallback from rqr now that the session-wide export covers it.
* fix(rqr): restore zbarcam preview and propagate DISPLAY to XWaylandLibravatar sommerfeld2026-04-211-1/+5
| | | | | | | | | | | zbarcam needs an X11 display for the live camera preview, which sway provides via XWayland. Two changes to make that reliable: - sway/config: add DISPLAY to the systemd/dbus user-environment imports, so anything launched through those paths (not just direct sway execs) can reach XWayland. - rqr: default DISPLAY to :0 (sway's default XWayland socket) when unset, as a belt-and-suspenders fallback.
* fix(rqr): use --nodisplay to avoid X11 dependency on WaylandLibravatar sommerfeld2026-04-211-1/+1
| | | | | | | zbarcam defaults to opening an X11 preview window, which fails on Sway without XWayland ("unable to open X display"). --nodisplay runs the scanner headlessly; we don't need the preview since -1 exits on the first barcode anyway.
* style: silence false-positive lint warningsLibravatar sommerfeld2026-04-211-0/+1
| | | | | | | | | | Inline directives for cases where the linter's shell/language dialect doesn't match reality: - init.lua: _G.P helper is intentional - dot_zprofile: zsh tied arrays, $+commands, optional sourcing - dot_zshrc: zsh brace-group-as-function-body - ipython_config: 'c' is injected by IPython at load time - doasedit: /bin/sh on Arch is bash, -O test is supported
* style(shell): apply shfmt (-i 2 -ci -s)Libravatar sommerfeld2026-04-217-144/+143
|
* refactor: move create-efi.sh into dot_local/bin/Libravatar sommerfeld2026-04-211-0/+56
| | | | | Aligned with the other personal scripts; chezmoi will deploy it as ~/.local/bin/create-efi (executable, no .sh extension).
* refactor: restructure to chezmoi source stateLibravatar sommerfeld2026-04-219-0/+402
Rename home/ contents to chezmoi naming conventions: - dot_ prefix for dotfiles and dot-dirs - private_dot_ for .gnupg and .ssh directories - private_ for 0600 files (nym.pub) - executable_ for scripts in .local/bin and display-toggle.sh - symlink_ for mimeapps.list symlink