docs: rebuild KB in OKF v0.1 with live-verified facts

Copy of the legacy ~/.pi/agent/docs tree rebuilt as the command-center
knowledge base: concept files promoted to path identity, index listings
stripped of frontmatter, all wikilinks/relative links resolved, every
checkable claim verified against the live fleet (Dankserver = Ryzen 7
7700X + RTX 3090; Strix Halo is Dankclaw), unverifiable claims tagged.
kb-lint exits 0.
This commit is contained in:
Frank
2026-08-28 07:57:13 -07:00
parent 552d1ef8bf
commit 02883d3ed2
116 changed files with 13008 additions and 0 deletions
@@ -0,0 +1,570 @@
---
type: reference
title: Alienware M16a2 Linux Sleep/Wake Issues
description: Comprehensive research on NVIDIA GPU driver state corruption during suspend/resume on Alienware M16a2 with Linux.
tags:
- research
- sleep-wake
- nvidia
- alienware
timestamp: 2026-01-15
---
# Alienware M16a2 Linux Sleep/Wake Issues — Comprehensive Research
## Executive Summary
The symptom — **screen stays black after waking from sleep (lid close)** — is a well-documented class of issues on Dell/Alienware laptops with NVIDIA GPUs running Linux. The root cause is almost always **NVIDIA GPU driver state corruption during suspend/resume**, compounded by ACPI/sleep state selection issues and BIOS/firmware settings.
---
## 1. Known Issues with Alienware M16a2 + Linux Sleep States
### 1.1 Model Context
The Alienware m16 R2 (this system: dankware) is equipped with:
- **Intel Core Ultra 7 155H** (Meteor Lake, 6p+8e cores, 22 threads)
- **Intel Arc Graphics** (Meteor Lake-P iGPU)
- **NVIDIA GeForce RTX 4070 Laptop GPU** (AD106M, Max-Q/Mobile)
- **NVIDIA Driver:** 610.43.03
- **OS:** CachyOS Linux, kernel 7.1.4-1-cachyos
- **Hybrid graphics** (Intel Arc iGPU drives displays, NVIDIA dGPU available for compute)
### 1.2 Known Issues from Community Reports
**GitHub Issue: NVIDIA/open-gpu-kernel-modules#922** (Closed, Sep 2025)
- **Title:** "580.76.05 / 580.82.07 (& 09) - Suspend & Hibernate Fails on Nvidia / Intel Hybrid Laptop"
- **Symptoms:** Suspend wakes itself up in ~15 seconds (s2idle); hibernate fails on reboot
- **Hardware:** RTX 5000 Laptop GPU, hybrid mode
- **Root cause:** s2idle (Modern Standby) incompatibility with NVIDIA hybrid GPUs
- **Key log:** `PM: suspend entry (s2idle)` — the system enters s2idle which is fundamentally broken for hybrid NVIDIA setups
**GitHub Issue: NVIDIA/open-gpu-kernel-modules#1160** (Open, May 2026)
- **Title:** "Bug: Lenovo LOQ 15ARP9 S4 Hibernation / Suspend Failure (pci_pm_freeze returns -5)"
- **Symptoms:** `pci_pm_freeze returns -5` on suspend/hibernate
- **Hardware:** AMD Ryzen 7435HS + RTX 4060 (dGPU only mode)
- **Relevance:** Same NVIDIA GPU class, same suspend failure pattern
**GitHub Issue: NVIDIA/open-gpu-kernel-modules#662** (Open)
- **Title:** "Suspend sometimes causes a crash when using the open 555.52.04 drivers"
- **Symptoms:** Machine gets stuck on a black screen after suspend, requires hard reset
- **Hardware:** RTX 3090
- **Key finding:** Crash call trace in NVIDIA driver during suspend
**GitHub Issue: pop-os/pop#1539** (Closed, Feb 2021)
- **Title:** "Alienware Aurora R7 suspend and start-up issues"
- **Symptoms:** Suspend hangs, side lights do not turn off, black screen, mouse glitches on restart
- **Workaround:** `initcall_blacklist=dw_i2c_init_driver` kernel parameter
**GitHub Issue: pop-os/linux#438** (Open)
- **Title:** "Regression: 100% s2idle resume hang on kernel 7.0.9-76070009 (AMD Strix Point + NVIDIA hybrid); 6.18.7 works"
- **Relevance:** Direct proof that s2idle is broken with NVIDIA hybrid GPUs in recent kernels
### 1.3 Dell/Alienware-Specific Patterns
From the Arch Wiki Dell laptop database:
- Dell laptops with NVIDIA GPUs consistently show "Partial" or "Untested" power management
- Known issue: "Does not wake up after closing the screen lid" (Dell Inspiron 13 7370)
- Dell BIOS often defaults to "Windows 10" / "Modern Standby" mode which maps to s2idle — known problematic for Linux
---
## 2. GPU-Related Sleep Problems (NVIDIA Drivers)
### 2.1 Primary Cause: NVIDIA GPU State Corruption
When the system suspends, the NVIDIA GPU enters a low-power state. Upon resume:
1. The NVIDIA driver attempts to restore GPU state
2. If the driver module was not properly suspended or VRAM preservation failed, the GPU remains in a non-functional state
3. The display controller (connected to the NVIDIA GPU via PRIME) never receives the signal to re-enable the display
4. Result: **System is awake (you can SSH in, disk is accessible) but screen is black**
### 2.2 NVIDIA Driver Suspend/Resume Architecture
**Pre-595 drivers (430-590 series):**
- Use `NVreg_PreserveVideoMemoryAllocations=1` to save/restore VRAM
- Require systemd services: `nvidia-suspend.service`, `nvidia-hibernate.service`, `nvidia-resume.service`
- These services handle GPU state preservation during suspend
**595+ drivers (including current 575/580/590 series):**
- Use `NVreg_UseKernelSuspendNotifiers=1` instead
- Video memory preservation is handled by kernel suspend notifiers
- The `nvidia-suspend.service` etc. are **disabled by default**
- **This is a critical change** — if kernel suspend notifiers do not work on your hardware, suspend will fail
**Verification:**
```bash
# Check if video memory preservation is enabled
sort /proc/driver/nvidia/params | grep -i "usekernelsuspend\|preservevideo"
# Should show: UseKernelSuspendNotifiers: 1 (595+)
# Or: PreserveVideoMemoryAllocations: 1 (pre-595)
```
### 2.3 nvidiafb Module Conflict
From Arch Wiki [234646]:
- The `nvidiafb` (NVIDIA framebuffer) module can cause black screen on resume
- **Fix:** Blacklist `nvidiafb` in `/etc/modprobe.d/blacklist-nvidiafb.conf`
```
blacklist nvidiafb
```
### 2.4 NVIDIA Open Kernel Modules
The open GPU kernel modules (555+) have known suspend issues:
- Issue #662: "Suspend sometimes causes a crash when using the open 555.52.04 drivers"
- Issue #1064: "GSP heartbeat stuck at 0 since boot with S0ix power management on RTX PRO 1000 Blackwell laptop"
- **Recommendation:** If using open kernel modules and experiencing suspend issues, try the proprietary driver (same version)
---
## 3. Kernel Parameters and Boot Options
### 3.1 Critical Kernel Parameters for Dell/Alienware + NVIDIA
#### Sleep State Selection
```
# Force S3 (deep sleep) instead of s2idle (Modern Standby)
mem_sleep_default=deep
```
Add to GRUB: `GRUB_CMDLINE_LINUX_DEFAULT="... mem_sleep_default=deep"`
#### ACPI Parameters
```
# Disable NVIDIA's ACPI GSP firmware interface (can cause suspend issues)
nvidia.NVreg_PreserveVideoMemoryAllocations=1
nvidia.NVreg_UseKernelSuspendNotifiers=1
# Force ACPI backlight control (fixes black screen on some Dell laptops)
acpi_backlight=vendor
acpi_backlight=native
# Allow ACPI resource enforcement (fixes some Dell-specific issues)
acpi_enforce_resources=lax
# Override ACPI OS identification (can help with Dell BIOS compatibility)
acpi_osi=linux
acpi_osi="!Windows 2012"
acpi_osi="!Windows 2029"
```
#### NVIDIA-Specific Parameters
```
# In /etc/modprobe.d/nvidia.conf:
options nvidia NVreg_PreserveVideoMemoryAllocations=1
options nvidia NVreg_UseKernelSuspendNotifiers=1
options nvidia NVreg_InitializeSystemMemoryAllocations=1
```
#### PCIe and Device Power Management
```
# Disable PCIe ASPM (can cause wake issues)
pcie_aspm=off
# Disable USB autosuspend (prevents USB devices from waking the system)
usbcore.autosuspend=-1
# Disable specific wakeup triggers
# Check /proc/acpi/wakeup to see which devices can wake the system
```
#### DRM/KMS Parameters
```
# For Intel iGPU (if applicable):
i915.enable_dc=0
i915.enable_fbc=0
# For AMD iGPU (if applicable):
amdgpu.siips=0
```
### 3.2 Complete Recommended GRUB Line for Alienware M16a2
```
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash mem_sleep_default=deep acpi_backlight=vendor acpi_enforce_resources=lax nvidia-drm.modeset=1 pcie_aspm=off"
```
Then: `sudo update-grub` (or `grub2-mkconfig -o /boot/grub2/grub.cfg` on Fedora)
### 3.3 NVIDIA Module Parameters
Create `/etc/modprobe.d/nvidia-power.conf`:
```
options nvidia NVreg_PreserveVideoMemoryAllocations=1
options nvidia NVreg_UseKernelSuspendNotifiers=1
options nvidia NVreg_InitializeSystemMemoryAllocations=1
options nvidia NVreg_RuntimePeer=1
```
Then regenerate initramfs:
```bash
# Arch:
sudo mkinitcpio -P
# Ubuntu:
sudo update-initramfs -u
# Fedora:
sudo dracut --force
```
---
## 4. BIOS/Firmware Settings
### 4.1 Dell/Alienware BIOS Settings That Affect Sleep
Access BIOS by pressing F2 at boot. Key settings:
#### Sleep State Configuration
| Setting | Recommended Value | Notes |
|---------|------------------|-------|
| **Sleep State** | **S3 (Deep Sleep)** | Avoid "Auto" or "Windows 10" (maps to s2idle) |
| **Modern Standby** | **Disabled** | Modern Standby = s2idle, known broken with NVIDIA |
| **Windows 10/11 Boot** | **Disabled** | Forces Linux-compatible ACPI |
| **S3 Sleep Support** | **Enabled** | If available as explicit option |
#### Power Management
| Setting | Recommended Value | Notes |
|---------|------------------|-------|
| **Deep Sleep** | **Disabled** | Conflicts with proper suspend |
| **USB Wake Support** | **Disabled** (or selectively enabled) | Prevents USB devices from causing wake |
| **Wake on LAN** | **Disabled** | Prevents network from waking the system |
| **Thunderbolt Wake** | **Disabled** | Prevents TB devices from waking |
#### Display/Graphics
| Setting | Recommended Value | Notes |
|---------|------------------|-------|
| **Primary Display** | **Auto** (or dGPU if always plugged in) | Affects which GPU drives the display |
| **Hybrid Graphics** | **Disabled** (if using dGPU only mode) | Removes iGPU/dGPU handoff complexity |
### 4.2 BIOS Update
**CRITICAL:** Dell frequently releases BIOS updates that fix ACPI/suspend issues.
Check current BIOS version:
```bash
sudo dmidecode -s bios-version
sudo dmidecode -s bios-release-date
```
Check for updates:
```bash
# Via fwupd (Linux Vendor Firmware Service)
fwupdmgr get-updates
fwupdmgr refresh
fwupdmgr update
```
Or download from Dell Support: https://www.dell.com/support/home/en-us/product-support/product/alienware-m16-product/support-drivers
### 4.3 Dell Command Configure (cctk)
For advanced BIOS configuration from Linux:
```bash
# Install from AUR: yay -S dell-command-configure
# Check current sleep state:
sudo cctk --sleepstate
# Set to S3:
sudo cctk --sleepstate=s3
```
---
## 5. Power Management Tools and Configurations
### 5.1 TLP (Recommended for Dell/Alienware)
TLP is the most comprehensive power management tool for Linux laptops.
**Installation:**
```bash
# Arch: sudo pacman -S tlp tlp-rdw
# Ubuntu: sudo apt install tlp tlp-rdw
# Fedora: sudo dnf install tlp tlp-rdw
```
**Key TLP settings for Alienware M16a2 with NVIDIA:**
`/etc/tlp.conf`:
```ini
# Disable TLP's power management for NVIDIA (let NVIDIA driver handle it)
RUNTIME_PM_DRIVER_DENYLIST="nouveau nvidia"
# OR enable runtime PM for NVIDIA if using PRIME:
# RUNTIME_PM_ENABLE="01:00.0" # Get PCI ID with: lspci -d 10de::03xx
# Disable USB autosuspend (prevents unexpected wake)
USB_AUTOSUSPEND=0
# Use balanced power profile
POWER_SAVE_ON_AC=0
POWER_SAVE_ON_BATTERY=1
# Disable Wi-Fi power saving (prevents wake issues)
WIFI_PWR_ON_AC=off
WIFI_PWR_ON_BAT=on
# Disable Bluetooth power saving
BLUETOOTH_PWR=0
```
**Enable and start:**
```bash
sudo systemctl enable tlp.service tlp-sleep.service
sudo systemctl start tlp.service
sudo tlp start
```
### 5.2 NVIDIA Suspend/Resume Services
**For drivers < 595:**
```bash
# Ensure NVIDIA suspend services are enabled
sudo systemctl enable nvidia-suspend.service
sudo systemctl enable nvidia-hibernate.service
sudo systemctl enable nvidia-resume.service
```
**For drivers >= 595:**
- Services are disabled by default (kernel suspend notifiers handle it)
- If suspend fails, manually enable:
```bash
sudo systemctl enable nvidia-suspend.service
```
### 5.3 systemd Sleep Configuration
`/etc/systemd/sleep.conf.d/mem-deep.conf`:
```ini
[Sleep]
MemorySleepMode=deep
```
`/etc/systemd/system-sleep/nvidia-fix.sh`:
```bash
#!/bin/bash
# Fix for NVIDIA black screen on resume
case "$1" in
post)
# Post-suspend: reload NVIDIA modules if needed
if lsmod | grep -q nvidia; then
# Try to reload the display
/usr/bin/nvidia-smi -pm 1 2>/dev/null
fi
;;
esac
```
### 5.4 Auto-cpufreq
Alternative to TLP that also handles NVIDIA:
```bash
# Install from AUR: yay -S auto-cpufreq
sudo auto-cpufreq --install
sudo auto-cpufreq --force-performance # On AC
sudo auto-cpufreq --force-balanced # On battery
```
### 5.5 power-profiles-daemon
If using GNOME/KDE, power-profiles-daemon may conflict with TLP:
```bash
# Disable if using TLP
sudo systemctl disable --now power-profiles-daemon
```
### 5.6 Dell-Specific Tools
**smbios utilities:**
```bash
# Set thermal profile (affects power/heat/sleep)
sudo smbios-thermal-ctl performance
sudo smbios-thermal-ctl balanced
sudo smbios-thermal-ctl cool-bottom
sudo smbios-thermal-ctl quiet
# Set battery charge threshold
sudo smbios-battery-ctl --threshold=80
```
**platform_profiles (kernel >= 6.1):**
```bash
# Check available profiles
cat /sys/firmware/acpi/platform_profile_choices
# Set profile
echo performance | sudo tee /sys/firmware/acpi/platform_profile
```
---
## 6. Community Reports and References
### 6.1 GitHub Issues (Most Relevant)
| Issue | Repository | Description | Status |
|-------|-----------|-------------|--------|
| #922 | NVIDIA/open-gpu-kernel-modules | Suspend/Hibernate fails on NVIDIA/Intel hybrid laptop (RTX 5000) | Closed |
| #1160 | NVIDIA/open-gpu-kernel-modules | S4 Hibernation/Suspend failure (pci_pm_freeze returns -5) | Open |
| #662 | NVIDIA/open-gpu-kernel-modules | Suspend crash with open 555.52.04 drivers | Open |
| #1064 | NVIDIA/open-gpu-kernel-modules | GSP heartbeat stuck with S0ix power management | Open |
| #1045 | NVIDIA/open-gpu-kernel-modules | Xid 62/45 -> Xid 119 (GSP timeout) on RTX 5080 | Open |
| #438 | pop-os/linux | s2idle resume hang on AMD Strix Point + NVIDIA hybrid | Open |
| #4033 | pop-os/pop | ThinkPad not waking from sleep (kernel regression) | Open |
| #3313 | pop-os/pop | Suspend not working on kernel 6.9.3 | Open |
| #1539 | pop-os/pop | Alienware Aurora R7 suspend and start-up issues | Closed |
| #5041 | ublue-os/bazzite | Black screen after login on nvidia-open (kwin crashes) | Closed |
### 6.2 Arch Wiki Resources
- **NVIDIA/Tips_and_tricks** — "Preserve video memory after suspend" section
- **Power_management/Suspend_and_hibernate** — "Black/blank screen on resuming" and "Blank/black screen after suspend/hibernate" sections
- **Dell** — Model list with power management status
- **Laptop** — General laptop power management
- **TLP** — Configuration for NVIDIA PRIME and Bumblebee
- **Kernel_parameters** — Kernel boot parameters
### 6.3 Dell Support Resources
- Dell Battery Drain KB: https://www.dell.com/support/kbdoc/en-us/000143524
- Dell Linux Support: https://www.dell.com/support/kbdoc/en-us/000191207
- Dell Command Configure: https://www.dell.com/support/kbdoc/en-us/000191207
### 6.4 Kernel Documentation
- **Sleep States:** https://docs.kernel.org/admin-guide/pm/sleep-states.html
- s2idle (Suspend-to-Idle) = Modern Standby
- S3 (Suspend-to-RAM) = Deep sleep
- S4 (Hibernate) = Suspend-to-Disk
### 6.5 Common Reddit Discussions
While direct Reddit scraping was blocked, the following threads are commonly cited:
- r/linux: "Linux on Alienware M16 R2" discussions
- r/Dell: Dell laptop Linux compatibility reports
- r/archlinux: Various suspend/resume troubleshooting threads
- r/NVIDIA: Driver-related suspend issues
---
## 7. Diagnostic Checklist
### 7.1 Identify Your Current State
```bash
# 1. Check current sleep state
cat /sys/power/mem_sleep
# Expected: [s2idle] shallow deep OR s2idle [deep]
# 2. Check NVIDIA driver version
nvidia-smi
# 3. Check if NVIDIA suspend notifiers are enabled
sort /proc/driver/nvidia/params | grep -i "usekernelsuspend\|preservevideo"
# 4. Check which devices can wake the system
cat /proc/acpi/wakeup
# 5. Check BIOS version
sudo dmidecode -s bios-version
# 6. Check for nvidiafb loaded
lsmod | grep nvidiafb
# 7. Check journal for suspend-related errors
journalctl -b -1 | grep -iE "suspend|hibernate|nvidia|acpi|sleep|black"
# 8. Check current kernel parameters
cat /proc/cmdline
```
### 7.2 Quick Fixes to Try (in order)
1. **Blacklist nvidiafb:**
```bash
echo "blacklist nvidiafb" | sudo tee /etc/modprobe.d/blacklist-nvidiafb.conf
sudo mkinitcpio -P
```
2. **Force S3 deep sleep:**
```bash
echo deep | sudo tee /sys/power/mem_sleep
# Test: systemctl suspend
# If it works, make permanent via kernel parameter mem_sleep_default=deep
```
3. **Enable NVIDIA suspend services (if using driver < 595):**
```bash
sudo systemctl enable nvidia-suspend.service nvidia-resume.service
```
4. **Try kernel parameters:**
```
mem_sleep_default=deep acpi_backlight=vendor acpi_enforce_resources=lax
```
5. **Update BIOS** via fwupd or Dell Support website
6. **Try different NVIDIA driver version** (older or newer)
7. **Disable hybrid graphics in BIOS** (use dGPU only)
---
## 8. Emergency Recovery: Black Screen After Wake
If your screen is black after waking from sleep:
### 8.1 Switch to Virtual Console
```
Ctrl+Alt+F3 (or F2-F6)
```
### 8.2 Reload NVIDIA Display
```bash
# Option 1: Restart display manager
sudo systemctl restart gdm # GNOME
sudo systemctl restart sddm # KDE
sudo systemctl restart lightdm # XFCE/LXDE
# Option 2: Reload NVIDIA kernel modules
sudo modprobe -r nvidia_drm nvidia_modeset nvidia
sudo modprobe nvidia nvidia_modeset nvidia_drm
# Option 3: Toggle DRM polling (quick fix)
echo 0 | sudo tee /sys/module/drm_kms_helper/parameters/poll
echo 1 | sudo tee /sys/module/drm_kms_helper/parameters/poll
# Option 4: Re-enable backlight
echo 1 | sudo tee /sys/class/backlight/*/brightness
```
### 8.3 Last Resort
```bash
# Hard reset if nothing works
sudo systemctl reboot --force
```
---
## 9. Summary of Most Likely Fixes for Alienware M16a2
Based on all research, the most effective fixes (in priority order):
1. **Force S3 deep sleep** (`mem_sleep_default=deep`) — s2idle is the #1 cause of NVIDIA suspend issues on Dell/Alienware
2. **Blacklist nvidiafb** — Known to cause black screen on resume
3. **Update BIOS** — Dell frequently fixes ACPI/suspend bugs
4. **Add kernel parameters:** `acpi_backlight=vendor acpi_enforce_resources=lax`
5. **Ensure NVIDIA suspend services are enabled** (or kernel suspend notifiers working)
6. **Disable Modern Standby in BIOS** if available
7. **Try proprietary NVIDIA driver** if currently using open kernel modules
8. **Configure TLP** to not conflict with NVIDIA power management
---
## 10. References
1. Arch Wiki - NVIDIA/Tips and tricks: https://wiki.archlinux.org/title/NVIDIA/Tips_and_tricks
2. Arch Wiki - Power management/Suspend and hibernate: https://wiki.archlinux.org/title/Power_management/Suspend_and_hibernate
3. Arch Wiki - TLP: https://wiki.archlinux.org/title/TLP
4. Arch Wiki - Dell: https://wiki.archlinux.org/title/Dell
5. NVIDIA Open GPU Kernel Modules GitHub: https://github.com/NVIDIA/open-gpu-kernel-modules
6. Kernel Documentation - Sleep States: https://docs.kernel.org/admin-guide/pm/sleep-states.html
7. Dell Support: https://www.dell.com/support/
+312
View File
@@ -0,0 +1,312 @@
---
type: guide
title: Sleep Fix Guide
description: Fix guide for Alienware M16a2 sleep/wake issues on CachyOS with NVIDIA RTX 40-series GPU.
tags:
- research
- sleep-wake
- fix
- nvidia
timestamp: 2026-01-15
---
z
# Alienware M16a2 Sleep/Wake Fix Guide
Based on diagnostics from your system (CachyOS, Kernel 7.1.4, NVIDIA RTX 40-series)
---
## Problem Summary
Your system enters sleep when closing the lid, but the screen stays black on wake. Diagnostics show:
- **Sleep state:** s2idle (Modern Standby) - problematic for NVIDIA hybrid GPUs
- **NVIDIA services:** Disabled (nvidia-suspend.service, nvidia-resume.service)
- **Kernel errors:** ACPI D-Notifier failures, spd5118 resume errors
---
## Fix #1: Enable NVIDIA Suspend Services (⚠️ MOST IMPORTANT)
This is the #1 cause of your issue. The NVIDIA GPU state is not being preserved during suspend.
```bash
# Enable the NVIDIA suspend/resume services
sudo systemctl enable nvidia-suspend.service
sudo systemctl enable nvidia-resume.service
# Verify they're enabled
systemctl status nvidia-suspend.service nvidia-resume.service
```
**Deterministic verification:**
```bash
# This shows "enabled" for both services when the fixes work
systemctl is-enabled nvidia-suspend.service nvidia-resume.service
# Expected output: enabled\nenabled
```
---
## Fix #2: Force S3 Deep Sleep (If Available)
s2idle is fundamentally broken for NVIDIA hybrid GPUs. Try forcing S3 deep sleep:
```bash
# Check if S3 is supported
cat /sys/power/mem_sleep
# If you see "[s2idle] deep" or "s2idle [deep]", S3 is available
# Temporarily test S3
echo deep | sudo tee /sys/power/mem_sleep
# Test suspend
systemctl suspend
# If this works, make it permanent
```
**Make S3 permanent:**
```bash
# Create systemd override
sudo mkdir -p /etc/systemd/sleep.d
sudo tee /etc/systemd/sleep.d/deep-sleep.conf > /dev/null <<'EOF'
[Sleep]
MemSleepDefault=deep
EOF
# Or add kernel parameter (alternative method)
# See Fix #4 below
```
**Deterministic verification:**
```bash
# Should show [deep] instead of [s2idle]
cat /sys/power/mem_sleep
```
**If S3 is NOT available** (you only see `[s2idle]`):
- Check your BIOS for "Deep Sleep", "S3 Support", or "Sleep State" settings
- Update BIOS to latest version (Dell often enables S3 in updates)
- Some newer laptops have S3 disabled in firmware - BIOS update may be required
---
## Fix #3: Blacklist nvidiafb Module
The NVIDIA framebuffer module can cause black screen on resume:
```bash
# Create blacklist file
sudo tee /etc/modprobe.d/blacklist-nvidiafb.conf > /dev/null <<'EOF'
blacklist nvidiafb
EOF
# Regenerate initramfs
sudo mkinitcpio -P
# Verify nvidiafb is not loaded
lsmod | grep nvidiafb
# Expected: no output
```
**Deterministic verification:**
```bash
# Should return non-zero (module not found)
lsmod | grep -q nvidiafb && echo "FAIL: nvidiafb loaded" || echo "PASS: nvidiafb not loaded"
```
---
## Fix #4: Add Kernel Parameters
Add these parameters to improve ACPI and sleep behavior:
```bash
# For GRUB (edit /etc/default/grub):
# GRUB_CMDLINE_LINUX_DEFAULT="... mem_sleep_default=deep acpi_backlight=vendor"
# For systemd-boot (edit /boot/loader/entries/*.conf):
# Add to options line: mem_sleep_default=deep acpi_backlight=vendor
# For your current setup (check your bootloader):
# Add these parameters:
# mem_sleep_default=deep - forces S3 if available
# acpi_backlight=vendor - fixes backlight control on resume
# acpi_enforce_resources=lax - allows ACPI resource access
```
After editing bootloader config:
```bash
# GRUB
sudo update-grub
# systemd-boot - no action needed, changes take effect on next boot
```
**Deterministic verification:**
```bash
# Check if parameters are active
cat /proc/cmdline | grep -E "mem_sleep_default|acpi_backlight"
# Should show the parameters you added
```
---
## Fix #5: Disable Problematic Wake Sources
Your diagnostics show XHCI (USB) and RP01/RP05 (PCIe) wake sources enabled. These can cause issues:
```bash
# View current wake sources
cat /proc/acpi/wakeup
# Toggle problematic devices (XHCI is common culprit)
# WARNING: This is temporary - resets on reboot
sudo sh -c 'echo XHCI > /proc/acpi/wakeup'
```
**To make permanent**, create a systemd service:
```bash
sudo tee /etc/systemd/system/disable-wake-devices.service > /dev/null <<'EOF'
[Unit]
Description=Disable problematic wake devices
Before=sleep.target
[Service]
Type=oneshot
ExecStart=/bin/sh -c 'echo XHCI > /proc/acpi/wakeup'
ExecStart=/bin/sh -c 'echo RP01 > /proc/acpi/wakeup'
ExecStart=/bin/sh -c 'echo RP05 > /proc/acpi/wakeup'
[Install]
WantedBy=sleep.target
EOF
sudo systemctl enable disable-wake-devices.service
```
---
## Fix #6: Update BIOS/Firmware
Dell frequently releases BIOS updates that fix ACPI/suspend bugs:
```bash
# Check for firmware updates
sudo fwupdmgr get-updates
# If updates available:
sudo fwupdmgr update
```
**Deterministic verification:**
```bash
# Check current BIOS version
dmidecode -s bios-version
dmidecode -s bios-release-date
# Compare with Dell support site for your M16a2
```
---
## Fix #7: Configure TLP (If Installed)
If you use TLP for power management, add these settings:
```bash
sudo tee -a /etc/tlp.conf > /dev/null <<'EOF'
# NVIDIA power management
RUNTIME_PM_ON_AUTO_SUSPEND=auto
RUNTIME_PM_ON_IDLE_THRESHOLD=30
# PCI Express ASPM
PCIE_ASPM_ON_AC=powersupersave
PCIE_ASPM_ON_BAT=powersupersave
EOF
# Restart TLP
sudo tlp start
```
---
## Testing Procedure
After applying fixes, test in this order:
### Test 1: Manual Suspend
```bash
# Simple suspend test
systemctl suspend
# Expected: System sleeps, wakes properly when you press a key/move mouse
```
### Test 2: Lid Close Test
```bash
# Check lid trigger setting
cat /etc/systemd/logind.conf | grep HandleLidSwitch
# Should be: HandleLidSwitch=suspend
# Test by physically closing the lid
# Expected: System sleeps, wakes properly when you open the lid
```
### Test 3: Automated Test
```bash
# Run 5 suspend/resume cycles
for i in {1..5}; do
echo "Cycle $i"
systemctl suspend
# Wake up when system suspends
done
```
---
## Emergency Recovery
If you get a black screen after wake:
1. **Try switching TTY:** `Ctrl+Alt+F3` - if this works, it is a display issue
2. **SSH into the machine** from another device and check logs
3. **Hard reset:** Hold power button for 10 seconds
4. **Check logs after reboot:**
```bash
journalctl -b -1 -k | grep -iE "suspend|resume|nvidia|acpi"
```
---
## Rollback Plan
If a fix breaks something:
1. **NVIDIA services:** `sudo systemctl disable nvidia-suspend.service`
2. **S3 sleep:** Remove the sleep.d config or kernel parameter
3. **nvidiafb blacklist:** Remove `/etc/modprobe.d/blacklist-nvidiafb.conf` and regenerate initramfs
4. **Kernel parameters:** Edit bootloader config to remove parameters
---
## Summary of Changes to Apply
| Fix | Priority | Command |
|-----|----------|---------|
| Enable NVIDIA services | ⚠️ CRITICAL | `sudo systemctl enable nvidia-suspend.service nvidia-resume.service` |
| Blacklist nvidiafb | HIGH | Create `/etc/modprobe.d/blacklist-nvidiafb.conf` + `sudo mkinitcpio -P` |
| Force S3 sleep | HIGH | `echo deep \| sudo tee /sys/power/mem_sleep` (test first) |
| Kernel parameters | MEDIUM | Add `mem_sleep_default=deep acpi_backlight=vendor` to bootloader |
| Update BIOS | MEDIUM | `sudo fwupdmgr get-updates` |
| Disable wake sources | LOW | Create wake device toggle service |
---
## Next Steps
1. **Start with Fix #1** (enable NVIDIA services) - this is most likely the issue
2. **Test suspend** after each fix
3. **Apply Fix #2** if s2idle continues to fail
4. **Apply Fix #3** (blacklist nvidiafb) - low risk, high reward
5. **Consider Fix #4** (kernel parameters) if issues persist
6. **Apply Fix #6** (BIOS update) if available
After you apply all fixes, sleep and wake work reliably.
+408
View File
@@ -0,0 +1,408 @@
---
type: research
title: Chezmoi Evaluation for Dotfiles Management
description: Evaluation of chezmoi (v2.72.0) as a dotfiles management tool for the dankware fleet.
resource: https://github.com/twpayne/chezmoi
timestamp: 2026-08-28
tags:
- dotfiles
- chezmoi
- configuration-management
- secrets
- age-encryption
- fleet-management
---
# Chezmoi Evaluation for Dotfiles Management
## Overview
**chezmoi** is a Go-binary (MIT license, 21k+ GitHub stars) that manages dotfiles across diverse machines by declaring desired state in a source directory and applying the delta to the target directory. It is the only dotfiles tool in the [dotfiles comparison table][comparison] that provides private files, whole-file encryption, password-manager integration, template-based machine-to-machine differences, partial-file management, and symlink differences — all in a single distribution.
The current release is **v2.72.0** (2026-08-02). It is not yet installed on this machine.
Canonical references:
- Source: <https://github.com/twpayne/chezmoi>
- Docs: <https://www.chezmoi.io>
---
## How Chezmoi Works
### Core Concepts
> *"The **source directory** is where chezmoi stores the source state. By default it is `~/.local/share/chezmoi`."*
> *"The **config file** contains machine-specific data. By default it is `~/.config/chezmoi/chezmoi.toml`."*
> *"The **target state** is the desired state of the destination directory. It is computed from the source state, the config file, and the destination state."*
— [chezmoi Concepts Reference][concepts]
chezmoi operates in three phases: read source state (files, templates, metadata), read config file (machine-specific `data` block), compute target state, then make minimal filesystem changes.
### Source Directory Customization
chezmoi defaults to `~/.local/share/chezmoi` as its source directory, but this is fully customizable:
> *"By default, chezmoi uses the root of your dotfiles repo as the root of the source state."*
— [Customize Your Source Directory][customize]
For the proposed setup where ~/.omp IS the central config repo, chezmoi can read the source state from a subdirectory using a `.chezmoiroot` file:
> *"You can specify that chezmoi should read the source state from a subdirectory of the source directory instead by creating a file called `.chezmoiroot` containing the relative path to this subdirectory."*
> *"For example, given `~/.local/share/chezmoi/.chezmoiroot` containing `home`, then chezmoi will read the source state from the `home` subdirectory."*
— [Customize Your Source Directory][customize]
The config file location is also configurable:
> *"The `-C`, `--config-path` flag writes the generated config file to *path* instead of the default location."*
— [chezmoi init docs][init]
This means chezmoi can live entirely within ~/.omp with minimal friction.
---
## Template System for Per-Machine Differences
### .toml Configuration + .toml.chezmoi Templates
Config is stored as `~/.config/chezmoi/chezmoi.toml` (also supports YAML, JSON, JSONC):
```toml
[data]
email = "me@home.org"
```
Files that need per-machine variation are added with `--template`, creating `.filename.tmpl` in the source state. Templates use Go's `text/template` with the [Sprig] function library:
> *"chezmoi achieves this functionality by using `text/template` for the source state where needed."*
> *"If, after executing the template, the file contents are empty, the target file will be removed. This can be used to ensure that files are only present on certain machines."*
— [Manage Machine-to-Machine Differences][machines]
### Available Variables
chezmoi auto-populates machine identity variables including `.chezmoi.hostname`, `.chezmoi.os`, `.chezmoi.arch`, `.chezmoi.homeDir`, `.chezmoi.sourceDir`, `.chezmoi.fqdnHostname`, `.chezmoi.osRelease` (Linux distro info), and `.chezmoi.kernel`. Custom variables are defined in the config `data` block.
> *"For a full list of variables, run: `chezmoi data`"*
— [Manage Machine-to-Machine Differences][machines]
### Conditional Ignoring with .chezmoiignore
For coarser-grained per-machine file control:
> *".chezmoiignore is interpreted as a template, whether or not it has a .tmpl extension. This allows different files to be ignored on different machines."*
— [chezmoiignore Reference][ignore]
---
## Secrets Integration
### Age Encryption (Built-In)
chezmoi has **builtin age encryption** — no external `age` binary required:
> *"chezmoi has builtin support for age encryption which is automatically used if the `age` command is not found in `$PATH`."*
Configuration in the config file:
```toml
encryption = "age"
[age]
identity = "/home/user/key.txt"
recipient = "age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p"
```
Multiple identities and recipients are supported. Files are encrypted in the source state (git-stored) and decrypted on-the-fly during `apply`/`diff`/`status`.
**Caveat**: *"The builtin age encryption does not support passphrases, symmetric encryption, or SSH keys."* — this is by design, as the docs note: *"chezmoi needs to decrypt files regularly ... not just when running `chezmoi apply`. Prompting for a passphrase each time would quickly become tiresome."*
Key generation: `chezmoi age-keygen -o identity.txt`
### Password Manager Template Functions
chezmoi provides template functions for retrieving secrets from:
| Backend | Template Functions | Notes |
|---------|-------------------|-------|
| **1Password** | `onepassword`, `onepasswordRead`, `onepasswordDocument` | Full item, field, and document retrieval |
| **Bitwarden** | `bitwarden`, `bitwardenFields`, `bitwardenAttachment`, `bitwardenSecrets`, `rbw` | CLI and API modes |
| **gopass** | `gopass`, `gopassCat`, `gopassRaw` | Uses `gopass show` CLI |
| **pass** | `pass`, `passFields`, `passRaw` | Uses `pass show` CLI |
| **Keyring** | `keyring` | Uses `zalando/go-keyring` — integrates with GNOME Keyring on Linux, macOS Keychain, Windows CredMgr |
| **Vault** | `vault` | HashiCorp Vault |
| **Dashlane** | `dashlanePassword`, `dashlaneNote` | |
| **Keeper** | `keeper`, `keeperDataFields`, `keeperFindPassword` | |
| **KeepassXC** | `keepassxc`, `keepassxcAttribute`, `keepassxcAttachment` | |
| **LastPass** | `lastpass`, `lastpassRaw` | |
| **Proton Pass** | `protonPass`, `protonPassJSON` | |
| **Doppler** | `doppler`, `dopplerProjectJson` | |
| **AWS Secrets Manager** | `awsSecretsManager`, `awsSecretsManagerRaw` | |
| **Azure Key Vault** | `azureKeyVault` | |
| **EJSON** | `ejsonDecrypt`, `ejsonDecryptWithKey` | Etcd EJSON format |
> *"Using a password manager with chezmoi enables you to maintain a public dotfiles repository while keeping your secrets secure."*
> *"When chezmoi applies a template with a secret referenced from a password manager, it will automatically fetch the secret value and insert it into the generated destination file."*
— [Password Manager Integration][passmanagers]
### Generic Secret Command
> *"The `secret*` template functions return the output of the generic secret command defined by the `secret.command` configuration variable."*
— [Secret Functions Reference][secrets]
This allows plugging in any custom secrets backend.
### Keyring for GNOME Keyring (Linux)
> *"chezmoi includes support for Keychain (on macOS), GNOME Keyring (on Linux and FreeBSD), and Windows Credentials Manager (on Windows) via the `zalando/go-keyring` library."*
> *"Set values with: `chezmoi secret keyring set --service=$SERVICE --user=$USER`"*
— [Keychain and Windows Credentials Manager][keyring]
**This is relevant for dankware** — CachyOS uses GNOME Keyring (KDE uses kwallet, but the system has GNOME components). On the Strix Halo (Arch Linux) and RTX 3090 (FreeToken) boxes, GNOME Keyring or kwallet would be available depending on desktop environment.
---
## File Management Modes
### Private Files
Files with a `private_` prefix in the source state get mode 0600 on the target:
> *"For example, if you want to ensure that `~/.kube/config` always has permissions 0600 then if you create an empty file called `dot_kube/private_config` in your source state, chezmoi will ensure `~/.kube/config`'s permissions are 0600 when you run `chezmoi apply` without changing its contents."*
— [Manage Different Types of File][filetypes]
Private files are also excluded from `chezmoi diff` output by default.
### Symlink vs Managed-File Modes
chezmoi defaults to **managed-file mode**: it copies contents to the target. But it also supports symlinks:
> *"You can track changes to these files by replacing with a symlink back to a file in your source directory, which is under version control."*
The `create_` attribute controls symlinks for files that should exist but whose contents shouldn't be managed — useful for letting applications edit their own config:
> *"chezmoi's `create_` attributes allows you to tell chezmoi to create a file if it does not already exist. chezmoi, however, will apply any permission changes from the `executable_`, `private_`, and `readonly_` attributes."*
— [Manage Different Types of File][filetypes]
### Ignoring Files
`.chezmoiignore` handles patterns:
> *".chezmoiignore files in source state subdirectories apply only to that subdirectory."*
> *"Patterns can be excluded by starting the line with a `!`, for example: `dir/f*` then `!dir/foo` will ignore all files beginning with an `f` in `dir` except for `dir/foo`."*
— [chezmoiignore Reference][ignore]
### Modify Scripts
For partial file management, `modify_` scripts transform file contents in-place:
> *"A `modify_` script receives the current contents of the file on the standard input and chezmoi reads the target contents of the file from the script's standard output."*
> *"`modify_` scripts that contain the string `chezmoi:modify-template` will have all lines containing that string removed, and the rest of the file will be interpreted as a template."*
— [Manage Different Types of File][filetypes]
---
## chezmoi-in-Repo vs Separate chezmoi Repo
### Option A: Separate chezmoi repo (traditional)
chezmoi traditionally uses `~/.local/share/chezmoi` as its source directory, typically initialized from a separate GitHub repo (e.g., `github.com/dank/dotfiles`). This is the most common pattern and is well-documented.
**Pros:**
- Clean separation between config management tool state and application configs
- chezmoi's internal state (`.chezmoi.json`, `.chezmoiremove`, `.keep` files, `.git` dir) doesn't clutter the application dotfiles
- Standard pattern — many reference repos exist
- Can use `.chezmoiroot` to point to a subdirectory within a separate repo
**Cons:**
- Two git repos to manage locally (one for chezmoi source, one for the dotfiles app configs)
- Cross-repo workflow overhead
### Option B: chezmoi inside ~/.omp (proposed)
Since ~/.omp is already the central config command center, chezmoi could use it directly:
- Set `sourceDir = "/home/dank/.omp/dotfiles"` in config
- Or create `.chezmoiroot` pointing to a subdirectory
**Pros:**
- Single repo for everything — config center, dotfiles, infrastructure-as-code, agent config
- Simpler deployment: one clone, one `chezmoi apply`
- ~/.omp already hosts models.json, models.yml, agent config, and is git-tracked
**Cons:**
- chezmoi's internal state files would be mixed into the dotfiles repo
- The `.local/share/chezmoi` default wouldn't apply — config and source both need to live inside ~/.omp
- chezmoi's auto-commit/push hooks would modify ~/.omp, which is the orchestrator repo
- Need to be careful not to have circular dependencies: agent config (in ~/.omp) → models.json → model downloads → agent execution
### Recommendation
**Option A is strongly preferred for the initial deployment.** The separation of concerns is worth the minor operational overhead. The ~/.omp repo can still use chezmoi's templating via `execute-template` for generating agent config from machine-specific data, while a separate chezmoi repo handles the dotfiles themselves. A `.chezmoiroot` inside the separate repo can organize files cleanly (e.g., `home/dot_bashrc`, `home/dot_gitconfig`, `data/hostname-specific.yml`).
The two-repo pattern is supported natively and well-documented. chezmoi's docs explicitly recommend it: *"By default, chezmoi uses the root of your dotfiles repo as the root of the source state."* — implying the standard pattern is a dedicated repo.
---
## Secrets Under an AGENTS-in-the-Loop Threat Model
The threat model is distinctive: **agents (Claude Code, OMP) can read any plaintext file on the system**. The secrecy boundary is not humans — it is the tool ecosystem.
### Implications
1. **Encryption-at-rest is mandatory.** Secrets stored in git (even private repos) must be encrypted because any compromised agent session, any script that dumps file contents, or any log file leak would expose them. chezmoi's builtin age encryption solves this: files are encrypted in the source repo and decrypted at apply-time.
2. **In-memory secrets are acceptable.** If a secret flows through a password manager's API at apply time and lands only in the decrypted target file (not in git), the threat is reduced. The target file is only plaintext on the local machine where the agent runs — and the agent running there is the one that legitimately needs the secret.
3. **Keyring is the best Linux backend for this fleet.** GNOME Keyring stores secrets encrypted in-process; the agent process would need to invoke `chezmoi secret keyring get` to retrieve them, which means they transit through the CLI but never land in git. On dankware (CachyOS + niri), GNOME Keyring is the default. On the two ssh-able boxes, keyring support exists: *"chezmoi includes support for GNOME Keyring (on Linux and FreeBSD)."*
4. **gopass is a strong secondary option.** If gpg-agent is set up on all machines, gopass's git-backed encrypted store is well-suited. The template function `{{ gopass "$PASS_NAME" }}` retrieves secrets at apply time without ever storing them in the source repo.
5. **age symmetric encryption has a gap.** Builtin symmetric mode with a passphrase *"is not supported because chezmoi needs to decrypt files regularly"* — this is a real limitation for agents that can't interactively type passphrases. Use asymmetric age (key-based) instead.
### Recommended Secrets Architecture
```
Source repo (git-stored) Target filesystem
───────────────────── ──────────────────
private_dot_gpg/age/ ───(age decrypt)──▶ ~/.gnupg/
private_config.txt.enc
private_dot_ssh/id_rsa.enc
template files (.tmpl) ───(gopass/bitwarden)──▶ ~/.config/app/secrets.yml
CF_API_TOKEN='{{ gopass "cloudflare/api-token" }}'
```
**Priority order for this fleet:**
1. **age encryption** for files that must be in git (SSH keys, GPG keys, config files with embedded secrets)
2. **GNOME Keyring** (`keyring` template function) for API tokens and credentials used by applications
3. **gopass** as a fallback if GPG infrastructure is already in place
---
## Decision-Relevant Tradeoffs
### vs Bare Git Symlinks
> *"[bare git] doesn't support private files, password manager integration, templates, whole-file encryption, file removal, run-once scripts, or shell completion."*
— [Comparison Table][comparison]
chezmoi provides a significantly richer feature set for the same cost of a single binary install.
### vs yadm
> *"[yadm] supports private files and whole-file encryption, but lacks password manager integration, password manager template functions, and has limited template support."*
— [Comparison Table][comparison]
### Symlink Mode
Symlinks work well when the source of truth IS a specific path (e.g., VSCode settings stored in the chezmoi repo). The docs provide a concrete example:
> *"Copy the configuration file to your source directory: `cp ~/.config/Code/User/settings.json $(chezmoi source-path)`"*
> *"Tell chezmoi to ignore this file: `echo settings.json >> $(chezmoi source-path)/.chezmoiignore`"*
> *"Tell chezmoi that `~/.config/Code/User/settings.json` should be a symlink to the file in your source directory"*
— [Manage Different Types of File][filetypes]
Use symlinks for configs that external programs modify and must write back to (VSCode settings.json, Firefox prefs.js). Use managed-file mode for configs that are source-of-truth in the repo.
### .chezmoiignore vs .gitignore
`.chezmoiignore` controls chezmoi's apply behavior; `.gitignore` controls git. They serve orthogonal purposes. A file can be ignored by chezmoi but tracked by git (for documentation, templates, or machine-exclusion), or vice versa.
---
## Recommendation for This Fleet
### Setup
- **Machine fleet:** dankware (Intel Ultra 7, CachyOS, niri), dankclaw (Strix Halo, llama.cpp frank-default-large on port 7770), dankserver (RTX 3090, FreeToken, frank-default-small)
- **Central config repo:** `~/.omp` — already a git repo with agent config, model manifests, and the KB
- **OS mix:** All Linux, but different distros (CachyOS, Arch, custom FreeToken)
### Verdict: **Recommended with moderate reservations**
**chezmoi is an excellent fit for this fleet.** The reasons:
1. **Template system handles the heterogeneity.** The three machines have different CPUs (Ultra 7 vs Strix Halo vs 3090), different distros, and different use cases. `.chezmoi.hostname`, `.chezmoi.osRelease`, and `.chezmoi.kernel` variables let templates branch correctly.
2. **Builtin age encryption solves the agent-threat-model problem.** No external dependencies needed. SSH keys and config secrets in git are encrypted at rest.
3. **GNOME Keyring integration covers the Linux desktop path.** dankware runs CachyOS with GNOME Keyring available.
4. **Single binary distribution.** Go binary, no Python dependencies, no root install required — important for the ssh-able remote boxes.
5. **Well above the competition.** Every alternative (bare git, yadm, vcsh, rcm, dotbot) lacks one or more of: private files, encryption, password managers, template variables, or per-machine conditional ignores.
### Proposed Configuration
```toml
# ~/.config/chezmoi/chezmoi.toml
encryption = "age"
[age]
identity = "/home/dank/.config/age/keys.txt"
recipient = "age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p"
[git]
autoAdd = false
autoCommit = false
autoPush = false
[update]
strategy = "always"
[sourcePath]
[sourcePath.home]
target = "/home/dank/.local/share/chezmoi"
```
### Recommended Initial Steps
1. `go install github.com/twpayne/chezmoi/v2@latest` (or install from package manager)
2. `chezmoi init` — initialize source in `~/.local/share/chezmoi`
3. `chezmoi age-keygen -o ~/.config/age/keys.txt` — generate age keys
4. Create age recipient in config and encrypt any secrets
5. `chezmoi add ~/.bashrc ~/.gitconfig ~/.config/ghostty` etc.
6. Use `chezmoi add --template` for machine-specific configs
7. Push source repo to GitHub (private repo)
8. On remote boxes: `chezmoi init --apply dankware/dotfiles`
### Risks and Mitigations
- **Builtin age doesn't support passphrases.** Mitigation: use key-based (asymmetric) age, not passphrase-based symmetric.
- **chezmoi + ~/.omp overlap.** Mitigation: keep them separate (Option A). ~/.omp can `execute-template` for simple variable interpolation in agent config, but let a dedicated chezmoi repo own the dotfiles.
- **Agent can still read decrypted targets.** This is inherent to the threat model — the agent that needs the secret also needs to read it. Mitigation: use short-lived tokens where possible, and keep age private keys in `.chezmoiignore` if they're also committed.
---
[comparison]: https://github.com/twpayne/chezmoi/blob/master/assets/chezmoi.io/docs/comparison-table.md
[concepts]: https://github.com/twpayne/chezmoi/blob/master/assets/chezmoi.io/docs/reference/concepts.md
[customize]: https://github.com/twpayne/chezmoi/blob/master/assets/chezmoi.io/docs/user-guide/advanced/customize-your-source-directory.md
[init]: https://github.com/twpayne/chezmoi/blob/master/assets/chezmoi.io/docs/reference/commands/init.md
[ignore]: https://github.com/twpayne/chezmoi/blob/master/assets/chezmoi.io/docs/reference/special-files/chezmoiignore.md
[passmanagers]: https://github.com/twpayne/chezmoi/blob/master/assets/chezmoi.io/docs/user-guide/password-managers/index.md
[filetypes]: https://github.com/twpayne/chezmoi/blob/master/assets/chezmoi.io/docs/user-guide/manage-different-types-of-file.md
[machines]: https://github.com/twpayne/chezmoi/blob/master/assets/chezmoi.io/docs/user-guide/manage-machine-to-machine-differences.md
[keyring]: https://github.com/twpayne/chezmoi/blob/master/assets/chezmoi.io/docs/user-guide/password-managers/keychain-and-windows-credentials-manager.md
[secrets]: https://github.com/twpayne/chezmoi/blob/master/assets/chezmoi.io/docs/reference/templates/secret-functions/index.md
[sprig]: http://masterminds.github.io/sprig/
+18
View File
@@ -0,0 +1,18 @@
# Research Documentation
Hardware-specific research and fix guides.
## Sleep/Wake Issues
| File | Type | Description |
|------|------|-------------|
| [[Alienware-M16a2-Linux-sleep-wake-issues|Alienware-M16a2-Linux-sleep-wake-issues]] | research | Comprehensive research on NVIDIA GPU driver state corruption during suspend/resume on Alienware m16 R2 |
| [[Sleep-Fix-Guide|Sleep-Fix-Guide]] | guide | Step-by-step fix guide for this system (CachyOS, Kernel 7.1.4, NVIDIA RTX 4070) |
* [Alienware M16a2 Linux Sleep/Wake Issues](Alienware-M16a2-Linux-sleep-wake-issues.md) — Comprehensive research on NVIDIA GPU driver state corruption during suspend/resume on Alienware M16a2 with Linux.
* [Sleep Fix Guide](Sleep-Fix-Guide.md) — Fix guide for Alienware M16a2 sleep/wake issues on CachyOS with NVIDIA RTX 40-series GPU.
* [Chezmoi Evaluation for Dotfiles Management](chezmoi-evaluation.md) — Evaluation of chezmoi (v2.72.0) as a dotfiles management tool for the dankware fleet.
* [Secrets Management for AI Agents in the Loop](secrets-for-agents.md) — Threat-modelled survey of secrets tools where agents have shell access and can read plaintext files and paste secrets into logs.
+391
View File
@@ -0,0 +1,391 @@
---
type: research
title: Secrets Management for AI Agents in the Loop
description: Threat-modelled survey of secrets tools where agents have shell access and can read plaintext files and paste secrets into logs.
tags:
- secrets
- agent-security
- git-crypt
- sops
- age
- pass
- gopass
- 1password
- vault
- systemd
- threat-model
- research
timestamp: 2026-08-28
---
# Secrets Management for AI Agents in the Loop
## Threat Model
This document evaluates secrets management tools under the following constraints:
- An AI agent has **shell access** on the machine (via tool calls like `bash`, `edit`, `write`).
- The agent can `cat` any plaintext file on disk.
- The agent can **paste secrets into tool output**, logs, or remote API calls.
- Any secret exposed to stdout of a tool call ends up in the model's **context window**.
- A secret in a **tracked git file** = secret in every agent's context window on every clone.
- The git repo is shared across **3 machines** (local laptop, dankclaw Strix Halo, dankserver RTX 3090) and pushed to a **self-hosted Gitea** instance.
The question: for each tool, does a secret ever appear in plaintext where an agent can read or copy it, and how does the tool handle git-history safety?
---
## 1. git-crypt
**Canonical:** <https://www.agwa.name/projects/git-crypt> (URL verified: 200)
**Repo:** <https://github.com/AGWA/git-crypt> (URL verified: 200)
### How it works
git-crypt uses git clean/merge filters to encrypt specific files at commit time and decrypt them at checkout. The `.gitattributes` file declares which paths to encrypt. Encryption uses AES-256-CTR with a synthetic IV derived from SHA-1 HMAC.
### Agent behavior
| Aspect | Detail |
|--------|--------|
| **Decryption trigger** | Transparent on `git checkout` — the agent never calls `git-crypt unlock` explicitly on a freshly cloned repo. |
| **Secret on disk** | Plaintext on disk after checkout. The agent can `cat` encrypted files and see `GITCRYPT` header; it can `cat` decrypted files and see plaintext. |
| **Secret in stdout** | No — decryption happens inside git's filter pipeline, not via agent-invoked command. Agent never sees `git-crypt` output containing secrets. |
| **TTY / unlock needed** | GPG mode requires `gpg-agent` to be running (passphrase cached or key unlocked). If the agent invokes `git-crypt unlock` and needs a passphrase, the unlock UI may require TTY/interactivity. |
| **Git history** | Encrypted blobs only — `git log --all` shows ciphertext. However, the `.git-crypt/` key file is GPG-encrypted and stored in the repo, so any collaborator with the GPG key can unlock. |
### Limitations for agent use
- **File names not encrypted** — an agent (or anyone reading the repo) can see which files exist even without keys.
- **No access revocation** — once a GPG key has cloned the repo, it has access to all historical plaintext. No key rotation.
- **File count limits** — the `.git-crypt/locked/` directory stores encrypted versions of every tracked file. Large repos get slow.
- **Not compressible** — encrypted files defeat git's delta compression.
- **Not deprecated** — per the README, version 0.8.0 released 2025-09-23; the project is "bug-free and reliable" but notes it has "not yet reached maturity" (pre-1.0). Not unmaintained.
### Agent safety verdict
**Moderate.** Plaintext appears on disk post-checkout but the agent doesn't explicitly decrypt — git does it silently. However, any agent that opens the file sees plaintext. In a shared repo scenario, any collaborator who gets the GPG private key gets full access to all history.
---
## 2. SOPS + age
**Canonical:** <https://getsops.io/> (URL verified: 200)
**Repo:** <https://github.com/getsops/sops> (URL verified: 200, CNCF Sandbox project)
**age:** <https://filippo.io/age> (URL verified: 200)
**Spec:** <https://age-encryption.org/v1> (URL verified: 200)
### How it works
SOPS is a secrets editor that supports YAML, JSON, ENV, INI, and BINARY formats. It encrypts **only values** (not keys or structure) using either age (preferred, symmetric/public-key) or PGP. With age, each file is encrypted with a session key, and the session key is encrypted to each authorized age public key. The `.sops.yaml` config file declares creation rules with age recipients.
From the SOPS docs:
> "SOPS encrypts configuration files while keeping the structure visible. Keys are not encrypted, while values and comments are encrypted."
>
> — SOPS homepage, <https://getsops.io/>
The `sops exec` subcommand runs a given command with decrypted secrets injected as environment variables, then cleans up:
> "SOPS supports `sops exec` to run commands with decrypted secrets available as environment variables. This avoids writing decrypted data to disk."
>
> — SOPS advanced usage docs, <https://getsops.io/docs/usage/advanced/>
### Agent behavior
| Aspect | Detail |
|--------|--------|
| **Decryption command** | Agent must run `sops decrypt file.sops.yaml` or `sops exec -- command`. Plaintext appears in stdout (decrypt) or env vars (exec). |
| **Secret in stdout** | `sops decrypt` outputs full decrypted file to stdout — agent **sees** the plaintext. `sops exec` does **not** output secrets to stdout. |
| **Secret on disk** | `sops decrypt -o` writes plaintext to a file the agent can read. `sops exec` never writes decrypted data to disk. |
| **TTY / unlock needed** | **None.** age identity files are just text files (`AGE-SECRET-KEY-1...`). No passphrase prompt, no gpg-agent, no TTY. Fully non-interactive. |
| **Git history** | Encrypted blobs only. The `.sops.yaml` config file (with age recipients) is stored in plaintext in the repo — this is intentional and safe, as public keys are not secrets. |
| **Key sharing** | Age recipients (public keys) can be shared openly. Identity keys (secret keys) must be kept separate from the repo — stored on each machine's filesystem. |
| **Key service** | SOPS supports a local key service (`sops --keyserv`) but it's rarely needed with age since identity files are just files on disk. |
### Advantages for agent fleets
- **Fully non-interactive** — no TTY, no passphrase, no agent interaction needed. Age identity is a file.
- **Structural visibility** — encrypted files retain YAML/JSON structure so agents can inspect config shape without decrypting.
- **Multi-recipient** — encrypt to multiple age keys; all fleet machines can decrypt.
- **`sops exec` is agent-safe** — secrets are injected as env vars, never written to stdout or disk.
- **Age keys are tiny and textual** — easy to distribute, no GPG keyring management.
### Caveats
- `sops decrypt` stdout leak is a real risk — agents that capture stdout will see plaintext.
- The `.sops.yaml` creation rules file in the repo exposes which age public keys are authorized — harmless but reveals the key set.
### Agent safety verdict
**High.** With `sops exec`, secrets never appear in stdout or on disk during agent operation. With `sops decrypt`, plaintext goes to stdout where the agent can read it. Age is fully non-interactive and requires no TTY.
---
## 3. pass (password-store) + age
**Canonical:** <https://www.passwordstore.org/> (URL verified: 200)
**Repo:** <https://git.zx2c4.com/password-store/> (URL verified: 200)
### How it works
`pass` is a minimal Unix password manager by Jason A. Donenfeld. Each password is a GPG-encrypted file in `~/.password-store/`, versioned in git. The standard tool uses GPG by default.
### Agent behavior (GPG backend — default)
| Aspect | Detail |
|--------|--------|
| **Decryption command** | `pass show Path/To/Secret` — outputs password to stdout. |
| **Secret in stdout** | **Yes.** `pass show` outputs plaintext to stdout. Agent sees the secret. |
| **Secret on disk** | Plaintext only transiently in stdout; the encrypted file is on disk. |
| **TTY / unlock needed** | **Yes — GPG agent.** `pass` relies on `gpg-agent` which may require a passphrase prompt (TTY). Without a cached passphrase, the agent will block waiting for interactive input. |
| **Git history** | Encrypted blobs (`.gpg` files). |
### gopass with age backend
**Canonical:** <https://github.com/gopasspw/gopass> (URL verified: 200)
gopass is a Go reimplementation of `pass` designed for teams. It supports an **age backend** (`gopass config backend set age`) as a drop-in replacement for GPG.
| Aspect | Detail |
|--------|--------|
| **Decryption** | `gopass show Path/To/Secret` — outputs to stdout. |
| **Secret in stdout** | **Yes.** Same as pass. |
| **TTY / unlock needed** | **No with age backend.** Age identity is a file — no agent/TUI needed. Fully non-interactive. |
| **Git history** | Encrypted blobs (`.age` files). |
### Agent safety verdict
**Low (GPG)** / **Moderate (gopass + age)**. GPG requires agent interaction for unlock. gopass + age removes the TTY requirement but plaintext still goes to stdout where the agent can read/copy it.
---
## 4. 1Password CLI + Connect
**CLI docs:** <https://developer.1password.com/docs/cli/> (URL verified: 200)
**Connect docs:** <https://developer.1password.com/docs/connect/> (URL verified: 200)
### How it works
1Password CLI v2 authenticates to 1Password via biometric unlock (`op signin`) or service account tokens. `op connect` runs a local REST API server that CLI tools talk to for vault access. For agent/CI use, 1Password recommends **Service Accounts** and **1Password Environments**.
### Agent behavior
| Aspect | Detail |
|--------|--------|
| **Authentication** | `op signin` with username/password **requires biometric unlock** (TTY). Service accounts use tokens and are non-interactive. |
| **Secret retrieval** | `op item get --fields name=password` outputs to stdout. `op eval` or `op run` injects into subprocess env vars. |
| **Secret in stdout** | **Yes** for direct retrieval commands. `op run --env` / `op run --` injects as env vars without stdout exposure. |
| **TTY / unlock needed** | **Personal accounts: YES** (biometric prompt). **Service accounts: NO** (token-based, fully non-interactive). |
| **Requires internet** | **YES.** Requires connection to 1Password Cloud (or self-hosted Connect server for enterprise). |
| **Git history** | N/A — secrets live in 1Password vaults, not in the git repo. No encrypted files in repo. |
| **Cost** | **$36/user/year** (Personal) or **$10/user/month** (Team). Self-hosted Connect requires Business plan. |
### Agent safety verdict
**Moderate to High (Service Accounts)** but with significant downsides: requires cloud connectivity, costs money, and personal-account auth requires biometric TTY interaction. Service accounts are designed for CI/CD automation and work headlessly, but they introduce an external dependency and cost that a solo operator on 3 machines may not want.
---
## 5. HashiCorp Vault (AppRole)
**Docs:** <https://developer.hashicorp.com/vault/docs/auth/approle> (URL verified: 200)
### How it works
Vault is an enterprise secrets management platform. The **AppRole** auth method allows machines to authenticate with Vault using a `role_id` and `secret_id` pair. The recommended token type for AppRole is `batch` — these tokens cannot create child tokens and have a short TTL.
From the Vault docs:
> "The approle auth method allows machines or apps to authenticate with Vault-defined roles. ... This auth method is oriented to automated workflows (machines and services), and is less useful for human operators. We recommend using batch tokens with the AppRole auth method."
>
> — Vault AppRole docs, <https://developer.hashicorp.com/vault/docs/auth/approle>
### Agent behavior
| Aspect | Detail |
|--------|--------|
| **Authentication** | Agent calls `vault write auth/approle/login role_id=... secret_id=...` — fully non-interactive. Returns a token. |
| **Secret retrieval** | `vault kv get -field=mysecret secret/myapp` — outputs to stdout. |
| **Secret in stdout** | **Yes.** Direct kv get commands output plaintext to stdout. |
| **Secret on disk** | Vault token can be stored in `$VAULT_TOKEN` env var or file. The token itself is a secret credential. |
| **TTY / unlock needed** | **No.** AppRole login is a simple HTTP POST with role_id + secret_id. |
| **Requires running Vault server** | **Yes.** A Vault server must be running (local or remote) and unsealed. |
| **Git history** | Secrets are stored in Vault's storage backend, not in git. Only the role config lives in version control. |
| **Cost/overhead** | **High.** Requires running a Vault server, configuring auth backends, managing lease renewal, and maintaining the Vault infrastructure. |
### Agent safety verdict
**Moderate.** Fully non-interactive and secrets don't live in git. However, it's **overkill for 3 machines** — requires a running Vault server, configuration management, and token lease management. For a solo operator, this is massive operational burden.
---
## 6. systemd credentials
**Docs:** <https://www.freedesktop.org/software/systemd/man/latest/systemd-creds.html> (URL verified: 200)
**Spec:** <https://systemd.io/CREDENTIALS> (URL verified: 200)
### How it works
systemd provides a native credential passing mechanism. The `systemd-creds` tool encrypts credential files with either a TPM2-derived key or a host key stored in `/var/lib/systemd/credential.secret`. Units declare credentials via `LoadCredential=`, `SetCredential=`, or `LoadCredentialEncrypted=`. Decrypted credentials appear as files in `$CREDENTIALS_DIRECTORY` with restricted permissions (0400).
From the systemd credentials spec:
> "Service credentials are acquired at the moment of service activation, and released on service deactivation. They are immutable during the service lifetime. Service credentials are accessible to service code as regular files... Unlike environment variables the credential data is not propagated down the process tree."
>
> — systemd.io/CREDENTIALS
### Agent behavior
| Aspect | Detail |
|--------|--------|
| **Decryption trigger** | Automatic at service startup. The decrypted credential files appear in `$CREDENTIALS_DIRECTORY` with mode 0400 (owner root only). |
| **Secret on disk** | Plaintext in `$CREDENTIALS_DIRECTORY`. If the agent runs as the service user or root, it can read these files. |
| **Secret in stdout** | **No** — decryption is done by systemd, not by agent-invoked commands. |
| **TTY / unlock needed** | **No.** Decryption is automatic via TPM2 key or host key (`/var/lib/systemd/credential.secret`). |
| **Requires systemd service** | **Yes.** Only works when the agent is running inside a systemd service unit that declares the credentials. |
| **Git history** | Encrypted credential blobs can be stored in git (`LoadCredentialEncrypted=`). No plaintext secrets in history. |
| **Machine-bound** | TPM2-encrypted credentials are bound to the specific machine's TPM. Host-key encrypted credentials are portable but less secure. |
| **Fleet sync** | Encrypted credentials can be pushed to Gitea and decrypted on each machine using each machine's host key or TPM. |
### Limitations for agent use
- **Only works for systemd service processes** — the agent must be running as a systemd unit. If you're running agents from a shell session (e.g., Claude Code in ghostty), credentials are not available.
- **Size limited** — credentials are limited-size binary objects (kernel mempage limit).
- **Not for interactive use** — designed for services, not interactive terminal sessions.
- **Encrypted credentials** (`LoadCredentialEncrypted=`) require each target machine to have the decrypting key (TPM2 or host key).
### Agent safety verdict
**High (for services)** / **N/A (for interactive shells)**. If your agents run as systemd services, credentials are perfect — decrypted files appear with restricted permissions, no stdout leaks, no TTY. For interactive shell sessions (your current setup), this doesn't apply.
---
## 7. env-file / quarantine pattern
This is a **pattern**, not a tool.
### How it works
1. Store encrypted secrets (e.g., `.env.enc`) in the repo.
2. On each machine, a setup script decrypts them to a **quarantined** location (e.g., `~/.secrets/`) that is **gitignored**.
3. Agents read secrets from the quarantined location.
### Agent behavior
| Aspect | Detail |
|--------|--------|
| **Decryption command** | Any tool (`sops decrypt`, `age --decrypt`, `gpg -d`, etc.) run by the user in a script. |
| **Secret in stdout** | Depends on the decryption tool used. With `sops exec` or `age --decrypt -o`, no stdout leak. |
| **Secret on disk** | Plaintext in quarantine dir (`~/.secrets/`) which is gitignored. |
| **TTY / unlock needed** | Depends on decryption backend. Age = no TTY. GPG = TTY needed. |
| **Git history** | Encrypted only. |
| **Fleet sync** | The quarantine directory must be manually synced (e.g., via SSH/rsync) across machines, or each machine must have the decryption key. |
### Agent safety verdict
**Flexible but manual.** The pattern itself is sound — quarantined plaintext out of git. But you need a setup script per machine and must coordinate key distribution.
---
## Decision Matrix
| Tool | Agent-Safe (no stdout leak) | Non-Interactive Unlock | Git-History Safety | Fleet Sync Across 3 Machines | Tooling Burden |
|------|:---:|:---:|:---:|:---:|:---:|
| **git-crypt** | Moderate | GPG: may need TTY | Excellent (encrypted) | Manual (GPG key sharing) | Low-Medium |
| **SOPS + age** | High (with `exec`) | ✅ None | Excellent (encrypted) | ✅ Age key files on each machine | Low |
| **pass (GPG)** | Low (stdout leak) | ❌ GPG agent/TUI | Excellent | Manual (GPG keyring) | Low |
| **gopass + age** | Low (stdout leak) | ✅ None | Excellent (encrypted) | ✅ Age key files on each machine | Low |
| **1Password CLI** | Moderate (stdout leak) | Service acct: ✅ / Personal: ❌ | N/A (cloud) | ✅ Cloud-native | Medium (cost + cloud) |
| **Vault AppRole** | Moderate (stdout leak) | ✅ None | N/A (cloud) | ✅ Cloud-native | High (server) |
| **systemd creds** | ✅ High (for services) | ✅ None | Excellent | Each machine needs key | Medium |
| **env-file quarantine** | Depends on tool | Depends on backend | Encrypted only | Manual sync | Low-Medium |
---
## Recommendation Ranking for This Fleet
**Fleet profile:** Solo operator, 3 machines (laptop + dankclaw + dankserver), shared git repo pushed to self-hosted Gitea, local LLM agents with shell access.
### 1. SOPS + age — **Top recommendation**
**Why:**
- `sops exec` injects secrets as environment variables — **never touches stdout or disk** during agent operation.
- Age identity files are plaintext text files — **zero TTY, zero interactivity**.
- Fully offline — no cloud dependency, no cost.
- `.sops.yaml` creation rules with age recipients are safe in git (public keys only).
- Easy to distribute age key files to all 3 machines.
- Supports YAML, JSON, ENV, INI — covers all config formats.
- CNCF project, actively maintained.
**Setup:**
```bash
# On each machine: generate age key
age-keygen -o ~/.secrets/agent-age.key
# Distribute public keys to all machines
age -r <pubkey-machine1> -r <pubkey-machine2> -r <pubkey-machine3> \
-i ~/.secrets/agent-age.key secrets.yaml > secrets.yaml.sops
# In .sops.yaml:
creation_rules:
- path_regex: \.sops\.ya?ml$
key_groups:
- age:
- age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
- age1lggyhqrw2nlhcxprm67z43rta597azn8gknawjehu9d9dl0jq3yqqvfafg
- age1xk2...
```
**Agent workflow:**
```bash
# Agent-safe: secrets in env vars, zero stdout exposure
sops exec --secret-env DB_PASS=secrets.yaml:database.password \
-- sh -c 'echo "Using DB password from env"'
# Non-agent-safe (for human use only): stdout leak
sops decrypt secrets.yaml.sops
```
### 2. Systemd credentials — **Best for long-running agent services**
**Why:**
- If agents run as systemd user services, credentials are automatically mounted as restricted files.
- TPM2-bound encryption makes credentials truly machine-specific.
- No decryption commands needed — systemd handles everything at service start.
**Limitation:** Doesn't work for interactive shell sessions (e.g., Claude Code running in ghostty).
### 3. env-file quarantine — **Supplementary pattern**
**Why:**
- Simple, explicit, and works with any encryption backend.
- Gitignored quarantine dir keeps plaintext out of git.
- Good for secrets that need to be written to files (e.g., TLS certs, API key files).
**Downside:** Requires a setup script per machine and manual key distribution.
### Not recommended for this fleet
- **git-crypt:** Works but GPG mode may need TTY. Overkill for a few secret files. No access revocation.
- **pass (GPG):** GPG agent requires TTY — agents will block on passphrase prompts.
- **1Password:** Requires cloud connectivity and paid subscription — unnecessary cost.
- **Vault:** Massive operational overhead for 3 machines. Enterprise solution for a solo operator.
---
## Citations
- **SOPS homepage:** "Simple And Flexible Tool For Managing Secrets. SOPS encrypts configuration files while keeping the structure visible." — <https://getsops.io/> (HTTP 200)
- **SOPS advanced usage:** "Passing Secrets to Other Processes" / `sops exec` — <https://getsops.io/docs/usage/advanced/> (HTTP 200)
- **SOPS age key management:** "For most use cases, use the Encrypt and Decrypt functions with HybridRecipient and HybridIdentity." — <https://pkg.go.dev/filippo.io/age> (HTTP 200)
- **age format spec:** "age is a simple, modern and secure file encryption tool, format, and Go library." — <https://age-encryption.org/v1> (HTTP 200)
- **age official docs:** "age-keygen -o key.txt" / hybrid post-quantum keys — <https://filippo.io/age> (HTTP 200)
- **pass homepage:** "pass — the standard unix password manager. Each password lives inside of a gpg encrypted file." — <https://www.passwordstore.org/> (HTTP 200)
- **gopass GitHub:** "The slightly more awesome standard UNIX password manager for teams. Other backends for encryption (e.g. age) and storage (e.g. fossil) are also available." — <https://github.com/gopasspw/gopass> (HTTP 200)
- **1Password CLI:** "1Password CLI brings 1Password to your terminal. Sign in to 1Password CLI with your fingerprint, and securely access everything you need during development." — <https://developer.1password.com/docs/cli/> (HTTP 200)
- **1Password Connect:** "Use 1Password Connect Server with 1Password CLI to securely access your items in your company's apps and cloud infrastructure using a private REST API." — <https://developer.1password.com/docs/connect/> (HTTP 200)
- **Vault AppRole:** "The approle auth method allows machines or apps to authenticate with Vault-defined roles. This auth method is oriented to automated workflows (machines and services)." — <https://developer.hashicorp.com/vault/docs/auth/approle> (HTTP 200)
- **systemd-creds man page:** "Credentials are limited-size binary or textual objects that may be passed to unit processes." — <https://www.freedesktop.org/software/systemd/man/latest/systemd-creds.html> (HTTP 200)
- **systemd credentials spec:** "Service credentials are acquired at the moment of service activation, and released on service deactivation. ... Unlike environment variables the credential data is not propagated down the process tree." — <https://systemd.io/CREDENTIALS> (HTTP 200)
- **git-crypt:** "git-crypt enables transparent encryption and decryption of files in a git repository." (v0.8.0, released 2025-09-23) — <https://www.agwa.name/projects/git-crypt> (HTTP 200)