diff options
63 files changed, 181 insertions, 108 deletions
diff --git a/dot_config/git/hooks/_dispatch.sh b/dot_config/git/hooks/_dispatch.sh index ed5acbf..9ba6e3b 100644 --- a/dot_config/git/hooks/_dispatch.sh +++ b/dot_config/git/hooks/_dispatch.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Sourced by every hook in this directory. Runs the per-repo hook of # the same name and then returns control so the calling user-level # hook can do its own work after. diff --git a/dot_config/git/hooks/executable_commit-msg b/dot_config/git/hooks/executable_commit-msg index e484ccb..628ac94 100755 --- a/dot_config/git/hooks/executable_commit-msg +++ b/dot_config/git/hooks/executable_commit-msg @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Strip Co-authored-by trailers whose identity looks like a coding agent # (Copilot, Claude, Codex, ChatGPT, Cursor, Aider, Devin, ...). Various # tools — GitHub Copilot CLI, VS Code chat, etc. — append these @@ -58,7 +58,11 @@ esac # Collapse any trailing blank lines produced by the removal so we don't # leave a dangling blank trailer block. -sed -e :a -e '/^$/{$d;N;ba' -e '}' "$tmp" >"$msg_file" +awk ' + NF { last = NR } + { lines[NR] = $0 } + END { for (i = 1; i <= last; i++) print lines[i] } +' "$tmp" >"$msg_file" printf 'commit-msg: stripped AI Co-authored-by trailer(s).\n' >&2 exit 0 diff --git a/dot_config/git/hooks/executable_post-commit b/dot_config/git/hooks/executable_post-commit index 45f13f0..69d85f9 100755 --- a/dot_config/git/hooks/executable_post-commit +++ b/dot_config/git/hooks/executable_post-commit @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # User-level post-commit. No global checks — exists purely so that # `<repo>/.githooks/post-commit` gets picked up automatically without # the project needing to override core.hooksPath. If there is no diff --git a/dot_config/git/hooks/executable_pre-commit b/dot_config/git/hooks/executable_pre-commit index 548925b..76d623b 100755 --- a/dot_config/git/hooks/executable_pre-commit +++ b/dot_config/git/hooks/executable_pre-commit @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # User-level pre-commit. No global checks — exists purely so that # `<repo>/.githooks/pre-commit` gets picked up automatically without # the project needing to override core.hooksPath. If there is no diff --git a/dot_config/git/hooks/executable_pre-push b/dot_config/git/hooks/executable_pre-push index 286958b..b0915bf 100755 --- a/dot_config/git/hooks/executable_pre-push +++ b/dot_config/git/hooks/executable_pre-push @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Reject pushes that include commits which: # * lack a good signature, or # * have a committer different from this repo's user.name / user.email, or diff --git a/dot_config/sway/executable_brightness-osd.sh b/dot_config/sway/executable_brightness-osd.sh index cc802ad..614fc6f 100644 --- a/dot_config/sway/executable_brightness-osd.sh +++ b/dot_config/sway/executable_brightness-osd.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Print brightness percent to wob's fifo to flash a brightness bar. # Usage: brightness-osd.sh up|down set -eu diff --git a/dot_config/sway/executable_bt-toggle.sh b/dot_config/sway/executable_bt-toggle.sh index 02b72f9..29ec6c6 100644 --- a/dot_config/sway/executable_bt-toggle.sh +++ b/dot_config/sway/executable_bt-toggle.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Toggle Bluetooth power via bluetoothctl. Uses notify-send's synchronous # hint so repeated toggles replace the previous notification instead of # stacking. diff --git a/dot_config/sway/executable_display-toggle.sh b/dot_config/sway/executable_display-toggle.sh index 39e3367..50f84f8 100755 --- a/dot_config/sway/executable_display-toggle.sh +++ b/dot_config/sway/executable_display-toggle.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Display mode manager: laptop-off ↔ side-by-side. # (no arg) toggle between modes (F7 / Super+x d). # apply re-apply the saved mode (used by display-watcher after diff --git a/dot_config/sway/executable_display-watcher.sh b/dot_config/sway/executable_display-watcher.sh index 94ee943..7e446f5 100755 --- a/dot_config/sway/executable_display-watcher.sh +++ b/dot_config/sway/executable_display-watcher.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Watch sway output events. When the set of connected external displays # changes (plug/unplug), re-apply the preferred layout via display-toggle.sh. # Manual F7 toggles don't trip this because they don't change external count. diff --git a/dot_config/sway/executable_emoji-picker.sh b/dot_config/sway/executable_emoji-picker.sh index 2a18283..86a379f 100644 --- a/dot_config/sway/executable_emoji-picker.sh +++ b/dot_config/sway/executable_emoji-picker.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Bemoji wrapper: drop skin-tone variants (U+1F3FB..U+1F3FF) so the # picker isn't cluttered with five copies of every people-emoji. # Bemoji pipes its emoji list to whatever BEMOJI_PICKER_CMD evaluates diff --git a/dot_config/sway/executable_emoji-wofi.sh b/dot_config/sway/executable_emoji-wofi.sh index 8d37240..1c16169 100644 --- a/dot_config/sway/executable_emoji-wofi.sh +++ b/dot_config/sway/executable_emoji-wofi.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Bemoji picker backend: filter out skin-tone variants # (U+1F3FB..U+1F3FF) before handing the emoji list to wofi. set -eu diff --git a/dot_config/sway/executable_power-menu.sh b/dot_config/sway/executable_power-menu.sh index 999259c..40598a8 100644 --- a/dot_config/sway/executable_power-menu.sh +++ b/dot_config/sway/executable_power-menu.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Keyboard-driven power menu via wofi --dmenu (j/k navigation). set -eu diff --git a/dot_config/sway/executable_tb-autostart.sh b/dot_config/sway/executable_tb-autostart.sh index 13c8fe8..3a85dc5 100644 --- a/dot_config/sway/executable_tb-autostart.sh +++ b/dot_config/sway/executable_tb-autostart.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Launch Thunderbird and stash the main window into the scratchpad once sway # marks it. Used at sway startup so TB is running but hidden from the outset. # Invoking Super+t (tb-toggle.sh) while TB isn't running takes a different diff --git a/dot_config/sway/executable_tb-toggle.sh b/dot_config/sway/executable_tb-toggle.sh index 56bb662..13382ed 100644 --- a/dot_config/sway/executable_tb-toggle.sh +++ b/dot_config/sway/executable_tb-toggle.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Toggle the Thunderbird main window between the sway scratchpad and the # current workspace (tiled). If Thunderbird isn't running yet, launch it — # the for_window rule in sway config will mark it and stash it. diff --git a/dot_config/sway/executable_type-vpn-otp.sh b/dot_config/sway/executable_type-vpn-otp.sh index b3f0924..f62ecbc 100644 --- a/dot_config/sway/executable_type-vpn-otp.sh +++ b/dot_config/sway/executable_type-vpn-otp.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Fetch the current VPN TOTP from pass-otp and type it into the focused # surface via wtype. If wtype isn't available or fails (focused surface # lacks virtual-keyboard support, e.g. an Xwayland app), copy the code diff --git a/dot_config/sway/executable_vol-osd.sh b/dot_config/sway/executable_vol-osd.sh index 7e324e0..e593710 100644 --- a/dot_config/sway/executable_vol-osd.sh +++ b/dot_config/sway/executable_vol-osd.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Print 0–100 to wob's fifo to flash a volume bar overlay. # Usage: vol-osd.sh up|down|mute (mute toggles) set -eu diff --git a/dot_config/systemd/user/wob.service b/dot_config/systemd/user/wob.service index d9c0869..559487a 100644 --- a/dot_config/systemd/user/wob.service +++ b/dot_config/systemd/user/wob.service @@ -6,8 +6,8 @@ ConditionEnvironment=WAYLAND_DISPLAY [Service] Type=simple -ExecStartPre=/usr/bin/sh -c 'rm -f "$XDG_RUNTIME_DIR/wob.sock" && mkfifo "$XDG_RUNTIME_DIR/wob.sock"' -ExecStart=/usr/bin/sh -c 'exec tail -f "$XDG_RUNTIME_DIR/wob.sock" | %h/.nix-profile/bin/wob' +ExecStartPre=%h/.nix-profile/bin/dash -c 'rm -f "$XDG_RUNTIME_DIR/wob.sock" && mkfifo "$XDG_RUNTIME_DIR/wob.sock"' +ExecStart=%h/.nix-profile/bin/dash -c 'exec tail -f "$XDG_RUNTIME_DIR/wob.sock" | %h/.nix-profile/bin/wob' ExecStopPost=/usr/bin/rm -f %t/wob.sock Restart=on-failure RestartSec=2s diff --git a/dot_config/waybar/executable_arch-audit-status.sh b/dot_config/waybar/executable_arch-audit-status.sh index 73edf6f..3c25c18 100755 --- a/dot_config/waybar/executable_arch-audit-status.sh +++ b/dot_config/waybar/executable_arch-audit-status.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Waybar custom/arch-audit: shows count of installed packages with known # CVEs that already have a fix available in the repos. Source of truth # is /run/arch-audit.txt, refreshed daily by arch-audit.timer (system diff --git a/dot_config/waybar/executable_clip-picker.sh b/dot_config/waybar/executable_clip-picker.sh index d7f5b61..c94e3de 100644 --- a/dot_config/waybar/executable_clip-picker.sh +++ b/dot_config/waybar/executable_clip-picker.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Clipboard picker on top of cliphist + wofi (vim-nav, hide-search, # Alt-d to delete the highlighted entry). # diff --git a/dot_config/waybar/executable_dock-status.sh b/dot_config/waybar/executable_dock-status.sh index b8093fb..ca6d031 100644 --- a/dot_config/waybar/executable_dock-status.sh +++ b/dot_config/waybar/executable_dock-status.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Detect a Lenovo ThinkPad USB-C Dock Gen2 by its distinctive built-in # ethernet adapter (17ef:a387). The dock's USB hubs share product IDs # with internal ThinkPad hubs on some models, but the ethernet is only diff --git a/dot_config/waybar/executable_failed-units-status.sh b/dot_config/waybar/executable_failed-units-status.sh index da7db49..9066904 100755 --- a/dot_config/waybar/executable_failed-units-status.sh +++ b/dot_config/waybar/executable_failed-units-status.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Waybar custom/failed-units: shows count of failed systemd units across # the system bus and the current user's session bus. Hidden when zero. # Mako fires only on transition upward (count went up since last check), diff --git a/dot_config/waybar/executable_lostfiles-status.sh b/dot_config/waybar/executable_lostfiles-status.sh index f5f1099..be1adae 100755 --- a/dot_config/waybar/executable_lostfiles-status.sh +++ b/dot_config/waybar/executable_lostfiles-status.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Waybar custom/lostfiles: shows count of filesystem entries not owned # by any pacman package (and not on lostfiles' built-in safe-list). # Source of truth is /run/lostfiles.txt, refreshed weekly by diff --git a/dot_config/waybar/executable_mako-status.sh b/dot_config/waybar/executable_mako-status.sh index 4e9f053..bab3765 100644 --- a/dot_config/waybar/executable_mako-status.sh +++ b/dot_config/waybar/executable_mako-status.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Waybar status: count of currently-visible mako notifications. With # default-timeout=0 in mako/config, "visible" == "pending"; once a # notification is dismissed it's gone and never comes back. diff --git a/dot_config/waybar/executable_memory-status.sh b/dot_config/waybar/executable_memory-status.sh index 33ccc04..5b19ca5 100755 --- a/dot_config/waybar/executable_memory-status.sh +++ b/dot_config/waybar/executable_memory-status.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Emit waybar JSON with memory usage. Used% uses a heat scale (green → red), # available% uses the inverse (red → green). Values embedded via Pango span. set -eu diff --git a/dot_config/waybar/executable_pacdiff-status.sh b/dot_config/waybar/executable_pacdiff-status.sh index c9278d2..92eb6a8 100755 --- a/dot_config/waybar/executable_pacdiff-status.sh +++ b/dot_config/waybar/executable_pacdiff-status.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Waybar custom/pacdiff: shows count of unresolved .pacnew/.pacsave files. # Source of truth is `pacdiff -o` (output mode — lists differing files, # does nothing). Hidden when zero. Mako fires once when the count goes diff --git a/dot_config/waybar/executable_snx-vpn-status.sh b/dot_config/waybar/executable_snx-vpn-status.sh index 9088630..f88c3a0 100644 --- a/dot_config/waybar/executable_snx-vpn-status.sh +++ b/dot_config/waybar/executable_snx-vpn-status.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Waybar custom/snx-vpn module: report the snx-rs (Check Point) tunnel # state. `snxctl status` is fast (talks over a local UDS to the daemon) # but might briefly stall during connect; cap it with `timeout`. diff --git a/dot_config/waybar/executable_snx-vpn-toggle.sh b/dot_config/waybar/executable_snx-vpn-toggle.sh index c21643b..80ced4c 100644 --- a/dot_config/waybar/executable_snx-vpn-toggle.sh +++ b/dot_config/waybar/executable_snx-vpn-toggle.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Toggle the snx-rs (Check Point) tunnel. # # Refresh the waybar custom/snx-vpn module immediately with SIGRTMIN+9. diff --git a/dot_config/waybar/executable_tb-unread.sh b/dot_config/waybar/executable_tb-unread.sh index 200cb70..b969215 100644 --- a/dot_config/waybar/executable_tb-unread.sh +++ b/dot_config/waybar/executable_tb-unread.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Emit waybar JSON with the count of unread messages in the protonmail-bridge # IMAP Inbox. Requires bridge credentials in `pass` at the paths below; the # bridge prints them via `protonmail-bridge --cli` → `info`. diff --git a/dot_config/waybar/executable_update-status.sh b/dot_config/waybar/executable_update-status.sh index 2317256..cbd899e 100755 --- a/dot_config/waybar/executable_update-status.sh +++ b/dot_config/waybar/executable_update-status.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Waybar custom/update: gentle reminder that the system hasn't been # upgraded recently. Source of truth is /var/log/pacman.log — the last # "[PACMAN] starting full system upgrade" entry. No daemon, no -Sy diff --git a/dot_config/waybar/executable_vpn-status.sh b/dot_config/waybar/executable_vpn-status.sh index 3e6faba..9a5016f 100644 --- a/dot_config/waybar/executable_vpn-status.sh +++ b/dot_config/waybar/executable_vpn-status.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Waybar custom/vpn module: report whether the wireguard interface # (managed by systemd-networkd) is admin-up. Pango markup makes the # state visually unambiguous (green shield up, red strikethrough down) diff --git a/dot_config/waybar/executable_vpn-toggle.sh b/dot_config/waybar/executable_vpn-toggle.sh index c348971..1a996df 100644 --- a/dot_config/waybar/executable_vpn-toggle.sh +++ b/dot_config/waybar/executable_vpn-toggle.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Toggle the wireguard interface managed by systemd-networkd. Polkit # rule (etc/polkit-1/rules.d/50-networkd-wheel.rules) lets wheel-group # members invoke networkctl up/down without a password prompt. diff --git a/dot_config/waybar/executable_webcam-status.sh b/dot_config/waybar/executable_webcam-status.sh index 2441039..be78774 100644 --- a/dot_config/waybar/executable_webcam-status.sh +++ b/dot_config/waybar/executable_webcam-status.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Emit waybar JSON when any /dev/video* device is held open by a process. # V4L2 capture (firefox, zoom, OBS, etc.) doesn't go through PipeWire's # privacy portal, so the built-in waybar privacy module never sees it. diff --git a/dot_config/waybar/executable_wifi-status.sh b/dot_config/waybar/executable_wifi-status.sh index 93b3048..2b3201b 100755 --- a/dot_config/waybar/executable_wifi-status.sh +++ b/dot_config/waybar/executable_wifi-status.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Emit waybar JSON describing wifi link state. # # Uses iwd's D-Bus API for state + SSID (net.connman.iwd is a documented, diff --git a/dot_config/waybar/executable_wifi-toggle.sh b/dot_config/waybar/executable_wifi-toggle.sh index 2eb27bc..e57f11f 100644 --- a/dot_config/waybar/executable_wifi-toggle.sh +++ b/dot_config/waybar/executable_wifi-toggle.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Toggle wifi (wlan0) Powered state via iwd's D-Bus API. Driven by waybar # on-click on the custom/wifi module. set -eu diff --git a/dot_local/bin/executable__sandbox-net-parser b/dot_local/bin/executable__sandbox-net-parser index 3189d00..6b55d08 100644 --- a/dot_local/bin/executable__sandbox-net-parser +++ b/dot_local/bin/executable__sandbox-net-parser @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/usr/bin/env dash # Sandbox wrapper for tools that parse data from untrusted network # sources (yt-dlp, streamlink). The threat model is RCE in a # subtitle / muxer / extractor that walks the user's home directory diff --git a/dot_local/bin/executable_dictate b/dot_local/bin/executable_dictate index bf1157c..7ad80bb 100644 --- a/dot_local/bin/executable_dictate +++ b/dot_local/bin/executable_dictate @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/usr/bin/env dash # Push-to-talk dictation. Toggle: 1st invocation starts recording; # 2nd stops, transcribes via whisper.cpp, types the result into the focused # window with wtype, and copies it to the clipboard. diff --git a/dot_local/bin/executable_linkhandler b/dot_local/bin/executable_linkhandler index a440634..e44dcc1 100755 --- a/dot_local/bin/executable_linkhandler +++ b/dot_local/bin/executable_linkhandler @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/usr/bin/env dash resolve_url() { if [ -f "$1" ]; then diff --git a/dot_local/bin/executable_ocr b/dot_local/bin/executable_ocr index 5ce8b08..aeadb51 100644 --- a/dot_local/bin/executable_ocr +++ b/dot_local/bin/executable_ocr @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/usr/bin/env dash # OCR a screen region (default) or an image file → clipboard. # # Usage: diff --git a/dot_local/bin/executable_on-battery-suspend b/dot_local/bin/executable_on-battery-suspend index 2f39cc7..93155d6 100644 --- a/dot_local/bin/executable_on-battery-suspend +++ b/dot_local/bin/executable_on-battery-suspend @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Suspend the system, but only if running on battery. Used as the # swayidle `timeout` action so idle suspends save battery without ever # firing while the laptop is plugged in. diff --git a/dot_local/bin/executable_record b/dot_local/bin/executable_record index ff57758..ac88771 100755 --- a/dot_local/bin/executable_record +++ b/dot_local/bin/executable_record @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/usr/bin/env dash pid_file="/tmp/recordpid" log_file="/tmp/record.log" @@ -9,7 +9,7 @@ pid_exists() { is_running() { if pid_exists; then - ps "$(cat "$pid_file")" >/dev/null 2>&1 || return 1 + kill -0 "$(cat "$pid_file")" >/dev/null 2>&1 || return 1 else return 1 fi diff --git a/dot_local/bin/executable_rqr b/dot_local/bin/executable_rqr index 335f55b..76ea698 100755 --- a/dot_local/bin/executable_rqr +++ b/dot_local/bin/executable_rqr @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/usr/bin/env dash text="$(zbarcam -q -1 --raw)" printf '%s' "$text" | wl-copy diff --git a/dot_local/bin/executable_streamlink b/dot_local/bin/executable_streamlink index d13a66d..591fffd 100644 --- a/dot_local/bin/executable_streamlink +++ b/dot_local/bin/executable_streamlink @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/usr/bin/env dash # Thin wrapper: run streamlink inside _sandbox-net-parser. The # sandbox keeps `/` bind-mounted, so streamlink's configured player # (`flatpak run io.mpv.Mpv` — see ~/.config/streamlink/config) is reachable diff --git a/dot_local/bin/executable_togreta b/dot_local/bin/executable_togreta index 9a189f9..3aa0afb 100755 --- a/dot_local/bin/executable_togreta +++ b/dot_local/bin/executable_togreta @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/usr/bin/env dash if [ -z "$1" ]; then url=$(wl-paste) diff --git a/dot_local/bin/executable_tokodi b/dot_local/bin/executable_tokodi index aec226c..469f851 100755 --- a/dot_local/bin/executable_tokodi +++ b/dot_local/bin/executable_tokodi @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/usr/bin/env dash if [ -z "$1" ]; then url=$(wl-paste) diff --git a/dot_local/bin/executable_wqr b/dot_local/bin/executable_wqr index f878fdf..5f9d36d 100755 --- a/dot_local/bin/executable_wqr +++ b/dot_local/bin/executable_wqr @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/usr/bin/env dash if [ -z "$1" ]; then text=$(wl-paste) diff --git a/dot_local/bin/executable_yt-dlp b/dot_local/bin/executable_yt-dlp index 87947a8..df62831 100644 --- a/dot_local/bin/executable_yt-dlp +++ b/dot_local/bin/executable_yt-dlp @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/usr/bin/env dash # Thin wrapper: run yt-dlp inside _sandbox-net-parser. See that # script for the threat model and the SANDBOX=0 escape hatch. exec _sandbox-net-parser "$HOME/.nix-profile/bin/yt-dlp" "$@" diff --git a/etc/pacman.conf b/etc/pacman.conf index 7097940..8f8be59 100644 --- a/etc/pacman.conf +++ b/etc/pacman.conf @@ -24,7 +24,7 @@ Architecture = auto # Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup # llama.cpp-vulkan: AUR rebuilds on every llama.cpp commit (1-2 hour build); # pin and update manually with `paru -S llama.cpp-vulkan` when intended. -IgnorePkg = llama.cpp-vulkan +#IgnorePkg = #IgnoreGroup = #NoUpgrade = diff --git a/etc/systemd/system/syncthing@.service b/etc/systemd/system/syncthing@.service new file mode 100644 index 0000000..ff4eba7 --- /dev/null +++ b/etc/systemd/system/syncthing@.service @@ -0,0 +1,53 @@ +[Unit] +Description=Syncthing - Open Source Continuous File Synchronization for %I +Documentation=man:syncthing(1) +After=network.target +StartLimitIntervalSec=60 +StartLimitBurst=4 + +[Service] +User=%i +Environment="STLOGFORMATTIMESTAMP=" +Environment="STLOGFORMATLEVELSTRING=false" +Environment="STLOGFORMATLEVELSYSLOG=true" +ExecStart=/home/%i/.nix-profile/bin/syncthing serve --no-browser --no-restart --allow-newer-config +Restart=on-failure +RestartSec=1 +SuccessExitStatus=3 4 +RestartForceExitStatus=3 4 + +# Best-effort sandboxing, based on the nixpkgs Syncthing system unit. +ProtectSystem=full +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectKernelLogs=true +ProtectControlGroups=true +ProtectHostname=true +ProtectClock=true +NoNewPrivileges=true +RestrictSUIDSGID=true +MemoryDenyWriteExecute=true +RestrictNamespaces=true +RestrictRealtime=true +RestrictAddressFamilies=AF_INET AF_INET6 AF_NETLINK AF_UNIX +CapabilityBoundingSet= +AmbientCapabilities= +LockPersonality=true +SystemCallArchitectures=native +RemoveIPC=true +PrivateIPC=true +PrivateTmp=disconnected +PrivateDevices=true +DevicePolicy=closed +PrivatePIDs=true +ProtectProc=invisible +ProcSubset=pid +SystemCallFilter=@system-service +SystemCallFilter=~@privileged +SystemCallFilter=~io_uring_setup io_uring_enter io_uring_register +SystemCallErrorNumber=EPERM +UMask=7027 +InaccessiblePaths=-/nonexistent + +[Install] +WantedBy=multi-user.target @@ -6,15 +6,15 @@ default: # Setup # ═══════════════════════════════════════════════════════════════════ -# First-time machine setup: regenerate chezmoi config, install git hooks, deploy dotfiles, install base packages, enable curated units, switch Home-Manager -init: _chezmoi-init _install-hooks apply (pkg-apply "base") unit-apply nix-switch +# First-time machine setup: regenerate chezmoi config, install git hooks, deploy dotfiles, install base packages, switch Home-Manager, enable curated units +init: _chezmoi-init _install-hooks apply (pkg-apply "base") nix-switch unit-apply # ═══════════════════════════════════════════════════════════════════ # Day-to-day # ═══════════════════════════════════════════════════════════════════ -# Reconcile everything: deploy dotfiles + /etc, top up packages, enable curated units, sync Home-Manager -sync: apply pkg-fix unit-apply nix-switch +# Reconcile everything: deploy dotfiles + /etc, top up packages, sync Home-Manager, enable curated units +sync: apply pkg-fix nix-switch unit-apply # Deploy dotfiles AND /etc atomically (chezmoi apply; /etc handled by onchange template) apply: @@ -72,7 +72,7 @@ pkg-update: # Run after this to pick up newer versions of nix-managed tools. nix-update: - #!/bin/sh + #!/usr/bin/env dash set -eu if ! command -v nix >/dev/null 2>&1; then echo "nix not installed; skipping flake update" >&2 @@ -84,7 +84,7 @@ nix-update: # Update all user-scope flatpaks (Flathub apps + URL bundles when their version changes) flatpak-update: - #!/bin/sh + #!/usr/bin/env dash set -eu flatpak update --user -y --noninteractive [ -f meta/flatpak.txt ] || exit 0 @@ -459,7 +459,7 @@ dotfiles-diff *paths: # 3-way merge dotfile conflicts; pass a path for one file, or omit to merge all dotfiles-merge *paths: - #!/bin/sh + #!/usr/bin/env dash if [ -n '{{ paths }}' ]; then chezmoi merge -S . {{ paths }} else @@ -468,7 +468,7 @@ dotfiles-merge *paths: # Show dotfile drift (wraps 'chezmoi status') dotfiles-status: - #!/bin/sh + #!/usr/bin/env dash echo "=== Dotfile drift ===" chezmoi status -S . || true @@ -485,7 +485,7 @@ dotfiles-status: # List curated systemd units with their enabled/active state unit-list: - #!/bin/sh + #!/usr/bin/env dash _render() { scope=$1 file=$2 sctl="systemctl"; [ "$scope" = user ] && sctl="systemctl --user" @@ -514,7 +514,7 @@ unit-list: # Enable all curated systemd units (idempotent, soft-fail per unit); walks system + user lists unit-apply: - #!/bin/sh + #!/usr/bin/env dash if [ -f systemd-units/system.txt ]; then sed -E 's/[[:space:]]*#.*$//; /^[[:space:]]*$/d' systemd-units/system.txt | while read -r u; do sudo systemctl enable --now "$u" \ @@ -530,7 +530,7 @@ unit-apply: # Show drift between curated units and actually-enabled systemd units (system + user) unit-status: - #!/bin/sh + #!/usr/bin/env dash tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT _drift() { scope=$1 label=$2 @@ -568,7 +568,7 @@ unit-status: # inferred by probing `systemctl [--user] cat <unit>` (system wins on tie). unit-add +units: - #!/bin/sh + #!/usr/bin/env dash set -eu _scope() { u=$1 @@ -606,7 +606,7 @@ unit-add +units: # inferred from which list currently contains the unit. unit-forget +units: - #!/bin/sh + #!/usr/bin/env dash set -eu for u in {{ units }}; do scope= @@ -620,7 +620,8 @@ unit-forget +units: continue fi file="systemd-units/${scope}.txt" - sed -i "/^$(printf '%s' "$u" | sed 's/[]\/$*.^[]/\\&/g')\$/d" "$file" + awk -v target="$u" '$0 != target { print }' "$file" > "$file.tmp" + mv "$file.tmp" "$file" echo "removed $u from ${scope}" if [ "$scope" = user ]; then systemctl --user disable --now "$u" \ @@ -985,7 +986,7 @@ etc-restore +paths: # Show package drift: missing packages in adopted groups + undeclared installed packages pkg-status: - #!/bin/sh + #!/usr/bin/env dash flatpaks=$(flatpak list --user --app --columns=application 2>/dev/null || true) echo "=== Package drift ===" just _active-packages | while read -r pkg; do @@ -1002,7 +1003,7 @@ pkg-status: # Print undeclared packages one per line, unindented (pipe to 'paru -Rs -' to remove pacman entries) undeclared: - #!/bin/sh + #!/usr/bin/env dash active=$(just _active-packages) pacman -Qqe | while read -r pkg; do echo "$active" | grep -qxF "$pkg" || echo "$pkg" @@ -1017,7 +1018,7 @@ undeclared: # Show per-group install coverage; pass a group name for a per-package breakdown pkg-list group="": - #!/bin/sh + #!/usr/bin/env dash is_installed() { # $1: group name, $2: package/app id if [ "$1" = "flatpak" ]; then @@ -1076,7 +1077,7 @@ pkg-list group="": # Install one or more package groups, or all groups if none given (e.g. just pkg-apply base intel) pkg-apply *groups: - #!/bin/sh + #!/usr/bin/env dash set -eu # `paru -S --needed` is a no-op for already-installed packages, which # means a package pulled in transitively (and later declared in @@ -1116,7 +1117,7 @@ pkg-apply *groups: # Top up missing packages in groups that are already ≥50% installed (never installs new groups) pkg-fix: - #!/bin/sh + #!/usr/bin/env dash flatpaks=$(flatpak list --user --app --columns=application 2>/dev/null || true) for file in meta/*.txt; do group=$(basename "$file" .txt) @@ -1146,7 +1147,7 @@ pkg-fix: # Append one or more packages to a group list and install them (e.g. just pkg-add base ripgrep fd) pkg-add group +pkgs: - #!/bin/sh + #!/usr/bin/env dash set -eu file="meta/{{ group }}.txt" if [ ! -f "$file" ]; then @@ -1171,7 +1172,7 @@ pkg-add group +pkgs: # Remove one or more packages from a group list (does NOT uninstall; the package may belong to other groups) pkg-forget group +pkgs: - #!/bin/sh + #!/usr/bin/env dash set -eu file="meta/{{ group }}.txt" if [ ! -f "$file" ]; then @@ -1180,7 +1181,8 @@ pkg-forget group +pkgs: fi for pkg in {{ pkgs }}; do if grep -qxF "$pkg" "$file"; then - sed -i "/^$(printf '%s' "$pkg" | sed 's/[]\/$*.^[]/\\&/g')\$/d" "$file" + awk -v target="$pkg" '$0 != target { print }' "$file" > "$file.tmp" + mv "$file.tmp" "$file" echo "removed $pkg from {{ group }}.txt" else echo "$pkg not in {{ group }}.txt" @@ -1208,7 +1210,7 @@ _install-hooks: # new versions of bundle entries). _flatpak-install: - #!/bin/sh + #!/usr/bin/env dash set -eu [ -f meta/flatpak.txt ] || exit 0 flatpak remote-add --if-not-exists --user flathub \ @@ -1233,7 +1235,7 @@ _flatpak-install: # Print packages from pacman groups that are ≥50% installed (adopted), one per line _active-packages: - #!/bin/sh + #!/usr/bin/env dash for file in meta/*.txt; do [ "$(basename "$file")" = "flatpak.txt" ] && continue pkgs=$(sed -E 's/[[:space:]]*#.*$//; /^[[:space:]]*$/d' "$file") diff --git a/meta/base.txt b/meta/base.txt index 756f776..ac7a749 100644 --- a/meta/base.txt +++ b/meta/base.txt @@ -6,14 +6,13 @@ # the distro (man-db/man-pages files), the system runtime (sudo-rs, # base/base-devel), and things needed pre-bootstrap or by other system # packages transitively. User-leaf CLIs (chezmoi, paru, qrencode, -# torsocks, lshw) now come from nix/host.nix. +# torsocks, lshw, xdg-utils, syncthing) now come from nix/host.nix. acpid arch-audit base base-devel btrfs-progs cpupower -dashbinsh dosfstools fwupd iwd @@ -143,7 +142,6 @@ brightnessctl # pulled transitively by flatpak. bubblewrap libfido2 -perl-file-mimeinfo qt5ct qt6ct xdg-user-dirs @@ -155,10 +153,8 @@ arkenfox-user.js # --- mail (host-side bits the org.mozilla.thunderbird flatpak depends on) --- # protonmail-bridge now comes from nix/host.nix (the bridge binary + its # repo-owned user unit dot_config/systemd/user/protonmail-bridge.service). -# git send-email Perl prereqs (SMTP via local Bridge on 127.0.0.1:1025) -perl-authen-sasl -perl-mime-tools -perl-net-smtp-ssl +# git-send-email support is provided by nix/common.nix's git package, which +# wraps git-send-email with the needed SMTP/SASL Perl libraries. # (External Editor Revived's native-messaging host is installed via nix # on the host — see nix/host.nix — so we don't pay the AUR variant's # hard `thunderbird` dependency. The bridge into the TB flatpak is @@ -170,7 +166,6 @@ perl-net-smtp-ssl # Bitcoin wallet — sparrow-wallet — lives in meta/btc.txt.) --- # --- desktop extras --- -syncthing udisks2 # Flatpak runtime (apps tracked in meta/flatpak.txt) diff --git a/meta/btc.txt b/meta/btc.txt index a468de1..096118c 100644 --- a/meta/btc.txt +++ b/meta/btc.txt @@ -1,5 +1,4 @@ bitbox-wallet-app-appimage -python-bitbox02 -python-qdarkstyle +bitbox-udev sparrow-wallet tor diff --git a/nix/common.nix b/nix/common.nix index 9c9e093..50d7f60 100644 --- a/nix/common.nix +++ b/nix/common.nix @@ -54,7 +54,9 @@ glow # Git stack - git + # Includes git-send-email: nixpkgs wraps it with the SMTP/SASL Perl deps + # it needs, so we don't carry distro Perl modules just for email support. + (git.override { sendEmailSupport = true; }) gh delta mergiraf @@ -66,6 +68,15 @@ yq-go # System + dash + # Prefer Rust uutils for the unprefixed replacements that pass repo-local + # usage checks. Keep GNU tar on the system PATH for now; uutils-tar still + # rejects common GNU tar invocations like `tar -czf`. + uutils-coreutils-noprefix + uutils-diffutils + uutils-findutils + uutils-procps + uutils-sed htop fastfetch hyperfine diff --git a/nix/flake.lock b/nix/flake.lock index 25482b2..cdbca21 100644 --- a/nix/flake.lock +++ b/nix/flake.lock @@ -30,16 +30,16 @@ ] }, "locked": { - "lastModified": 1779726825, - "narHash": "sha256-RUkMrREjKDQrA+dA9+xZviGAxM5W1aVdyOr/bSYpHrE=", + "lastModified": 1780370888, + "narHash": "sha256-PRJj9RKTEf/sITycujP1c/BrvLJKMYXzcpwTsXNulXQ=", "owner": "nix-community", "repo": "home-manager", - "rev": "b179bde238977f7d4454fc770b1a727eaf55111c", + "rev": "a7a415883195ffbd4dabec8f098f201e6eaaadf8", "type": "github" }, "original": { "owner": "nix-community", - "ref": "release-26.05", + "ref": "master", "repo": "home-manager", "type": "github" } @@ -69,11 +69,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1779560665, - "narHash": "sha256-tpyBcxPpcQb8ukyNF7DoCwfSY3VPsxHoYwj00Cayv5o=", + "lastModified": 1780243769, + "narHash": "sha256-x5UQuRsH3MqI0U9afaXSNqzTPSeZlRLvFAav2Ux1pNw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "64c08a7ca051951c8eae34e3e3cb1e202fe36786", + "rev": "331800de5053fcebacf6813adb5db9c9dca22a0c", "type": "github" }, "original": { @@ -131,11 +131,11 @@ "utils": "utils" }, "locked": { - "lastModified": 1780096845, - "narHash": "sha256-O3vJgv3HJtizMyjT9PLEL90iqXwRyKaFz0QcaABRQFQ=", + "lastModified": 1780345905, + "narHash": "sha256-4ERFyKR3O2HUfv3oRuUChoGnSdcdppFpCqrHticKi6A=", "owner": "agavra", "repo": "tuicr", - "rev": "9abc01e8ad13ca9e1beb45d469572bdb0334892e", + "rev": "1f585175b59c20c2bfaa8a73ca572218ccfd93ff", "type": "github" }, "original": { diff --git a/nix/flake.nix b/nix/flake.nix index d82d9a4..f452228 100644 --- a/nix/flake.nix +++ b/nix/flake.nix @@ -4,12 +4,11 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; home-manager = { - # Pin to the release branch matching nixpkgs lib.version (the - # `nixos-unstable` snapshot we follow here reports 26.05). Without - # this, HM master races ahead one cycle and emits the - # "mismatched versions" warning at every activation. Bump the - # branch name in lockstep when nixpkgs lib.version rolls over. - url = "github:nix-community/home-manager/release-26.05"; + # Track the Home Manager branch whose release.json matches nixpkgs' + # lib.version. nixos-unstable currently reports 26.11pre-git, while + # home-manager's latest release branch is still 26.05, so master is the + # matching input until release-26.11 exists. + url = "github:nix-community/home-manager/master"; inputs.nixpkgs.follows = "nixpkgs"; }; # tuicr: TUI git-change reviewer. Upstream flake exposes diff --git a/nix/host.nix b/nix/host.nix index 0565f1b..dff9aef 100644 --- a/nix/host.nix +++ b/nix/host.nix @@ -117,6 +117,13 @@ in lshw yt-dlp streamlink + xdg-utils # xdg-open, used by yazi/linkhandler/OPENER + + # ── File sync ─────────────────────────────────────────────────────────────── + # Package-only migration from pacman. The boot-time system service is the + # repo-owned syncthing@sommerfeld.service tracked in systemd-units/system.txt + # and backed by etc/systemd/system/syncthing@.service. + syncthing # chezmoi & paru — both are pure user CLIs. `paru` wraps pacman+makepkg # but doesn't link them; it just shells out. bootstrap.sh installs a diff --git a/run_onchange_after_deploy-etc.sh.tmpl b/run_onchange_after_deploy-etc.sh.tmpl index 5b60972..ba79130 100755 --- a/run_onchange_after_deploy-etc.sh.tmpl +++ b/run_onchange_after_deploy-etc.sh.tmpl @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Deploy system-level configs from etc/ to /etc/. # chezmoi re-runs this script whenever any file under etc/ changes. # etc/ content hash: {{ output "sh" "-c" (printf "cd %q && find etc -type f ! -name .ignore -exec sha256sum {} + | LC_ALL=C sort" .chezmoi.sourceDir) | sha256sum }} diff --git a/run_onchange_after_deploy-firefox.sh.tmpl b/run_onchange_after_deploy-firefox.sh.tmpl index a917026..eaecb68 100755 --- a/run_onchange_after_deploy-firefox.sh.tmpl +++ b/run_onchange_after_deploy-firefox.sh.tmpl @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Deploy Firefox/LibreWolf hardening overrides and custom CSS. # chezmoi re-runs this script whenever any file under firefox/ changes. # firefox/ content hash: {{ output "sh" "-c" (printf "cd %q && find firefox -type f -exec sha256sum {} + | LC_ALL=C sort" .chezmoi.sourceDir) | sha256sum }} diff --git a/run_onchange_after_deploy-flatpak-overrides.sh.tmpl b/run_onchange_after_deploy-flatpak-overrides.sh.tmpl index 8e827fa..44664cf 100644 --- a/run_onchange_after_deploy-flatpak-overrides.sh.tmpl +++ b/run_onchange_after_deploy-flatpak-overrides.sh.tmpl @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Read-only host config bindings for flatpaks that should pick up our # chezmoi-managed ~/.config/<app>/ rather than maintaining a separate # in-sandbox copy. Idempotent; flatpak override merges entries. diff --git a/run_onchange_after_deploy-pteid-pkcs11.sh.tmpl b/run_onchange_after_deploy-pteid-pkcs11.sh.tmpl index 10ebcd2..4f57757 100644 --- a/run_onchange_after_deploy-pteid-pkcs11.sh.tmpl +++ b/run_onchange_after_deploy-pteid-pkcs11.sh.tmpl @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Bridge the pt.gov.autenticacao flatpak's PKCS#11 module into the NSS # database of every Mozilla-family flatpak we use, so cartão de cidadão # authentication / S/MIME signing works despite cross-sandbox isolation. diff --git a/run_onchange_after_deploy-tb-eer.sh.tmpl b/run_onchange_after_deploy-tb-eer.sh.tmpl index 27f1fef..c4dabff 100644 --- a/run_onchange_after_deploy-tb-eer.sh.tmpl +++ b/run_onchange_after_deploy-tb-eer.sh.tmpl @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Bridge the External Editor Revived native messaging host into the # org.mozilla.thunderbird flatpak. The host binary stays installed via # pacman (`external-editor-revived`); we relocate the manifest into the diff --git a/run_onchange_after_deploy-thunderbird.sh.tmpl b/run_onchange_after_deploy-thunderbird.sh.tmpl index 8837ab6..02820f2 100644 --- a/run_onchange_after_deploy-thunderbird.sh.tmpl +++ b/run_onchange_after_deploy-thunderbird.sh.tmpl @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Deploy Thunderbird non-private configuration (user.js, optional chrome/). # chezmoi re-runs this script whenever any file under thunderbird/ changes. # thunderbird/ content hash: {{ output "sh" "-c" (printf "cd %q && find thunderbird -type f -exec sha256sum {} + | LC_ALL=C sort" .chezmoi.sourceDir) | sha256sum }} diff --git a/run_onchange_after_install-copilot-node.sh b/run_onchange_after_install-copilot-node.sh index 82c0254..9f3e72e 100755 --- a/run_onchange_after_install-copilot-node.sh +++ b/run_onchange_after_install-copilot-node.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env dash # Install a Node.js 24 (LTS) runtime under ~/.local/share/copilot-node/ for the # exclusive use of copilot.lua / copilot-lsp inside neovim. System-wide nodejs # (currently 26.x in Arch) is unaffected. diff --git a/systemd-units/system.txt b/systemd-units/system.txt index 582a508..19867f7 100644 --- a/systemd-units/system.txt +++ b/systemd-units/system.txt @@ -35,3 +35,6 @@ nix-daemon.socket # --- libvirt (socket-activated; daemons spawn on first virsh/virt-manager # connect, the .socket is what gets enabled) --- libvirtd.socket + +# --- file sync (system template; binary comes from the user's nix profile) --- +syncthing@sommerfeld.service |
