diff --git a/bin/download-youtube-id b/bin/download-youtube-id
index 185d92f..9df85d0 100755
--- a/bin/download-youtube-id
+++ b/bin/download-youtube-id
@@ -63,14 +63,14 @@ validate_plex_perms() {
local sudo_timeout=10s
local not_plex
- not_plex=$(find "$out_dir" -not -user plex -or -not -group plex)
+ not_plex=$(find "$out_dir" -not -group plex)
if [ "$not_plex" ]; then
log $LINENO sudo required to change ownership of files to plex \(10s\)
- if ! timeout --kill-after="$sudo_timeout" "$sudo_timeout" sudo chown -R plex:plex "$out_dir"; then
- true # ignore timeout here, it's optional
- fi
+ for file in $not_plex; do
+ timeout --kill-after="$sudo_timeout" "$sudo_timeout" sudo chown :plex "$file" || true
+ done
fi
}
diff --git a/bin/system-up b/bin/system-up
index bcc7f09..f42e69d 100755
--- a/bin/system-up
+++ b/bin/system-up
@@ -220,8 +220,11 @@ mise_install_globals() {
for ((i = 0; i < ${#packages[@]}; i++)); do
npm install --global "${packages[i]}"
done
+}
- # export PATH="$PATH:$HOME/.local/share/mise/installs/node/$(node -v | tr -d v)/bin"
+cleanup_packages() {
+ # 19GB of build dependencies of llama.cpp
+ yay --remove --recursive --recursive rocm-hip-sdk
}
### script ###
@@ -240,6 +243,7 @@ services start postgresql
refresh_collations
cleanup "$pg_update"
+cleanup_packages
kexec_prompt ""
success_message
diff --git a/boot/EFI/CLOVER/config.plist b/boot/EFI/CLOVER/config.plist
deleted file mode 100644
index b8118b9..0000000
--- a/boot/EFI/CLOVER/config.plist
+++ /dev/null
@@ -1,643 +0,0 @@
-
-
-
-
- ACPI
-
- AutoMerge
-
- DSDT
-
- Debug
-
- DropOEM_DSM
-
- Fixes
-
- DeleteUnused
-
- FixDarwin7
-
- FixHDA
-
- FixHPET
-
- FixIPIC
-
- FixMutex
-
- FixRTC
-
- FixRegions
-
- FixShutdown
-
- FixTMR
-
-
- Name
- DSDT.aml
- Patches
-
-
- Comment
- GFX0 to IGPU
- Disabled
-
- Find
- R0ZYMA==
- Replace
- SUdQVQ==
-
-
- Comment
- HDAS to HDEF
- Disabled
-
- Find
- SERBUw==
- Replace
- SERFRg==
-
-
- Comment
- HECI to IMEI
- Disabled
-
- Find
- SEVDSQ==
- Replace
- SU1FSQ==
-
-
- Comment
- SAT0 to SATA
- Disabled
-
- Find
- U0FUMA==
- Replace
- U0FUQQ==
-
-
- Comment
- GLAN to GIGE
- Disabled
-
- Find
- R0xBTg==
- Replace
- R0lHRQ==
-
-
- Comment
- EC0 to EC
- Disabled
-
- Find
- RUMwXw==
- Replace
- RUNfXw==
-
-
- Comment
- Fix bug SKL+
- Disabled
-
- Find
- oAqTU1RBUwE=
- Replace
- oAqRCv8L//8=
-
-
- ReuseFFFF
-
-
- DisableASPM
-
- DropTables
-
-
- Signature
- DMAR
-
-
- FixHeaders
-
- FixMCFG
-
- HaltEnabler
-
- SSDT
-
- DropOem
-
- Generate
-
- APLF
-
- APSN
-
- CStates
-
- PStates
-
- PluginType
-
-
- NoDynamicExtract
-
- NoOemTableId
-
-
- smartUPS
-
-
- Boot
-
- Arguments
- systemd.unit=multi-user.target systemd.unit=graphical.target quiet loglevel=3
- CustomLogo
-
- Debug
-
- DefaultLoader
- boot.efi
- DefaultVolume
- LastBootedVolume
- Legacy
- PBR
- NeverDoRecovery
-
- NeverHibernate
-
- Secure
-
- Timeout
- 2
- XMPDetection
- Yes
-
- CPU
-
- UseARTFrequency
-
-
- Devices
-
- Audio
-
- AFGLowPowerState
-
- Inject
- 1
- ResetHDA
-
-
- LANInjection
-
- Properties
-
- PciRoot(0x0)/Pci(0x2,0x0)
-
- AAPL,ig-platform-id
- BwCbPg==
- AAPL,slot-name
- Internal@0,2,0
- device-id
- mz4AAA==
- device_type
- VGA compatible controller
- framebuffer-con0-busid
- AgAAAA==
- framebuffer-con0-enable
- AQAAAA==
- framebuffer-con0-index
- AgAAAA==
- framebuffer-con0-pipe
- CgAAAA==
- framebuffer-con0-type
- AAgAAA==
- framebuffer-con1-enable
- AQAAAA==
- framebuffer-con1-index
- AwAAAA==
- framebuffer-con1-pipe
- CAAAAA==
- framebuffer-con1-type
- AAgAAA==
- framebuffer-con2-busid
- AQAAAA==
- framebuffer-con2-enable
- AQAAAA==
- framebuffer-con2-index
- AQAAAA==
- framebuffer-con2-pipe
- CQAAAA==
- framebuffer-patch-enable
- AQAAAA==
- hda-gfx
- onboard-1
- model
- Intel UHD Graphics 630
-
-
- USB
-
- AddClockID
-
- FixOwnership
-
- HighCurrent
-
- Inject
-
-
- UseIntelHDMI
-
-
- GUI
-
- Custom
-
- Entries
-
-
- AddArguments
- root=UUID=1daf4ba0-59aa-4c6f-81d2-24369ff8400e rw rootflags=defaults,noatime,compress=lzo add_efi_memmap initrd=/intel-ucode.img initrd=/initramfs-linux.img video=3840x2160 amdgpu.ppfeaturemask=0xffffffff zswap.enabled=0 fbcon=scrollback:8192
- Disabled
-
- FullTitle
- Archlinux
- Hidden
-
- Ignore
-
- Path
- vmlinuz-linux
- Type
- Linux
- Volume
- A8B5D9CE-B348-2440-AB21-E72F6EA5FD88
- VolumeType
- Internal
-
-
- AddArguments
- root=UUID=1daf4ba0-59aa-4c6f-81d2-24369ff8400e rw rootflags=defaults,noatime,compress=lzo add_efi_memmap initrd=/intel-ucode.img initrd=/initramfs-linux-neptune-65.img video=3840x2160 amdgpu.ppfeaturemask=0xffffffff zswap.enabled=0 fbcon=scrollback:8192
- Disabled
-
- FullTitle
- Neptune-65
- Hidden
-
- Ignore
-
- Path
- vmlinuz-linux-neptune-65
- Type
- Linux
- Volume
- A8B5D9CE-B348-2440-AB21-E72F6EA5FD88
- VolumeType
- Internal
-
-
-
- Mouse
-
- DoubleClick
- 500
- Enabled
-
- Mirror
-
- Speed
- 8
-
- Theme
- bootcamp
-
- Graphics
-
- EDID
-
- Inject
-
-
- Inject
-
- ATI
-
- Intel
-
- NVidia
-
-
- NvidiaSingle
-
-
- KernelAndKextPatches
-
- AppleIntelCPUPM
-
- AppleRTC
-
- Debug
-
- DellSMBIOSPatch
-
- KernelCpu
-
- KernelLapic
-
- KernelPm
-
- KernelXCPM
-
- KextsToPatch
-
-
- Comment
- External icons patch
- Disabled
-
- Find
- RXh0ZXJuYWw=
- InfoPlistPatch
-
- MatchOS
- 10.11.x,10.12.x,10.13.x,10.14.x,10.15.x
- Name
- com.apple.driver.AppleAHCIPort
- Replace
- SW50ZXJuYWw=
-
-
- Comment
- MikeyDriver Patch Assertions
- Disabled
-
- Find
- U291bmQgYXNzZXJ0aW9uIA==
- InfoPlistPatch
-
- MatchOS
- 10.11.x,10.12.x,10.13.x,10.14.x,10.15.x
- Name
- com.apple.driver.AppleMikeyDriver
- Replace
- AAAAAAAAAAAAAAAAAAAAAA==
-
-
- Comment
- Prevent AGDP from loading
- Disabled
-
- Find
- ugUAAAA=
- InfoPlistPatch
-
- MatchOS
- 10.11.x,10.12.x,10.13.x,10.14.x,10.15.x
- Name
- com.apple.driver.AppleGraphicsDevicePolicy
- Replace
- ugAAAAA=
-
-
- Comment
- AppleHDA Patch Assertions
- Disabled
-
- Find
- U291bmQgYXNzZXJ0aW9uIA==
- InfoPlistPatch
-
- MatchOS
- 10.11.x,10.12.x,10.13.x,10.14.x,10.15.x
- Name
- com.apple.driver.AppleHDA
- Replace
- AAAAAAAAAAAAAAAAAAAAAA==
-
-
- Comment
- USB port limit patch
- Disabled
-
- Find
- g72M/v//EA==
- InfoPlistPatch
-
- MatchOS
- 10.11.x
- Name
- com.apple.driver.usb.AppleUSBXHCIPCI
- Replace
- g72M/v//Gw==
-
-
- Comment
- USB port limit patch
- Disabled
-
- Find
- g710////EA==
- InfoPlistPatch
-
- MatchOS
- 10.12.x
- Name
- com.apple.driver.usb.AppleUSBXHCIPCI
- Replace
- g710////Gw==
-
-
- Comment
- USB port limit patch by PMHeart
- Disabled
-
- Find
- g32IDw+DpwQAAA==
- InfoPlistPatch
-
- MatchOS
- 10.13.6
- Name
- com.apple.driver.usb.AppleUSBXHCI
- Replace
- g32ID5CQkJCQkA==
-
-
- Comment
- USB port limit patch #1 10.14.x modify by DalianSky(credit ydeng)
- Disabled
-
- Find
- g/sPDw==
- InfoPlistPatch
-
- MatchOS
- 10.14.x
- Name
- com.apple.iokit.IOUSBHostFamily
- Replace
- g/s/Dw==
-
-
- Comment
- USB port limit patch #2 10.14.x modify by DalianSky(credit PMHeart)
- Disabled
-
- Find
- g+MP0w==
- InfoPlistPatch
-
- MatchOS
- 10.14.x
- Name
- com.apple.iokit.IOUSBHostFamily
- Replace
- g+M/0w==
-
-
- Comment
- USB Port limit patch #3 10.14.x modify by DalianSky(credits PMheart)
- Disabled
-
- Find
- g/sPDw==
- InfoPlistPatch
-
- MatchOS
- 10.14.x
- Name
- com.apple.driver.usb.AppleUSBXHCI
- Replace
- g/s/Dw==
-
-
- Comment
- USB Port limit patch #4 10.14.x modify by DalianSky(credits PMheart)
- Disabled
-
- Find
- g/8PDw==
- InfoPlistPatch
-
- MatchOS
- 10.14.x
- Name
- com.apple.driver.usb.AppleUSBXHCI
- Replace
- g/8/Dw==
-
-
- Comment
- USB Port limit patch #1(credits PMheart)
- Disabled
-
- Find
- g/sPDw==
- InfoPlistPatch
-
- MatchOS
- 10.15.x
- Name
- com.apple.iokit.IOUSBHostFamily
- Replace
- g/s/Dw==
-
-
- Comment
- USB Port limit patch #2(credits PMheart)
- Disabled
-
- Find
- g/kPDw==
- InfoPlistPatch
-
- MatchOS
- 10.15.x
- Name
- com.apple.driver.usb.AppleUSBXHCI
- Replace
- g/k/Dw==
-
-
-
- RtVariables
-
- BooterConfig
- 0x28
- CsrActiveConfig
- 0x67
- ROM
- UseMacAddr0
-
- SMBIOS
-
- BiosReleaseDate
- 06/16/2019
- BiosVendor
- Apple Inc.
- BiosVersion
- IM191.88Z.F000.B00.1906161737
- Board-ID
- Mac-AA95B1DDAB278B95
- BoardManufacturer
- Apple Inc.
- BoardSerialNumber
- C02917130GULNV9CB
- BoardType
- 10
- BoardVersion
- 1.0
- ChassisAssetTag
- iMac-Aluminum
- ChassisManufacturer
- Apple Inc.
- ChassisType
- 0x09
- EfiVersion
- 220.270.93.0.0
- Family
- iMac
- FirmwareFeatures
- 0xFD8FF576
- FirmwareFeaturesMask
- 0xFFDFFF7F
- LocationInChassis
- Part Component
- Manufacturer
- Apple Inc.
- Memory
-
- Channels
- 2
-
- Mobile
-
- PlatformFeature
- 0x22
- ProductName
- iMac19,1
- SerialNumber
- C02YM8ZPJV3Q
- SmUUID
- 65E3F042-EA59-4589-AAF9-1B0CB0E07916
- Version
- 1.0
-
- SystemParameters
-
- CustomUUID
- FA86D691-066D-4FF1-93B0-9D56B648C2AE
- InjectKexts
- Yes
- InjectSystemID
-
-
-
-
diff --git a/doc/etc.md b/doc/etc.md
index 007ad7b..4353c92 100644
--- a/doc/etc.md
+++ b/doc/etc.md
@@ -1,6 +1,7 @@
# etc/ — System Configuration
Files here are symlinked (or hardlinked for `fstab`) into `/etc`. Requires root for install.
+Note: all `/etc` symlinks resolve through `/home/vacation/` — root must be able to traverse it. Ensure `chmod o+x /home/vacation` is set, otherwise systemd and other root processes silently fail to read unit files and configs at boot.
Note: `/etc/fstab` must be a **hardlink**, not a symlink — the kernel reads it before symlinks resolve.
## Filesystem & Boot
@@ -69,6 +70,8 @@ SDDM display manager config. Key settings:
### `systemd/system/` — Root system units
+Timer units depend on `home-vacation.mount` — symlink targets resolve through `/home/vacation/`, which is a btrfs subvolume mounted at boot. Without this dependency, timers may activate before the mount is up and silently fail to schedule.
+
| Unit | Purpose |
| ------------------------- | -------------------------------------------------------------------------------------------------------- |
| `system-snapshot.service` | Runs `~/bin/system-snapshot` as root |
diff --git a/doc/ideas.md b/doc/ideas.md
index f22311d..fdcd521 100644
--- a/doc/ideas.md
+++ b/doc/ideas.md
@@ -9,6 +9,8 @@ fstab as a hardlink is a smart workaround but it means the repo and /etc/fstab a
The system-up script has a postgres_up function that calls extract_version and then does [ "$major_sub" -ge 1 ] — but extract_version returns strings like "15." (with a trailing dot from the regex). The bc subtraction would work, but the comparison on the result of bc gives a float like "1.0", and [ "1.0" -ge 1 ] is actually a bash integer comparison that would fail on the .0 suffix. Worth checking if that code path has actually been exercised recently.
Genuine questions:
+
The PUSH_REPOS list in .bashrc has interface in it — but the repo uses jujutsu, not git. lazy-git checks $PUSH_REPOS and calls git push. Does lazy-git ever actually run against this repo, or is lazy-jujutsu always the one used here?
The ollama service listens on 0.0.0.0 so Docker containers can reach it — what are you running in Docker that talks to Ollama? Local LLM tooling for work, or something else?
And the dictation-keyboard-hook on KEY_F2 of the Keychron Q10 — what does it actually do? Does it pipe audio to a Whisper model, or is it hooking into some other dictation stack?
+
diff --git a/doc/install.md b/doc/install.md
new file mode 100644
index 0000000..92a29f5
--- /dev/null
+++ b/doc/install.md
@@ -0,0 +1,340 @@
+# install — Dotfiles Install Program
+
+A `go run`-able install program at `src/install/`. Replaces t0filer for the `interface` repo.
+Processes `install.rc` and applies file operations to the system.
+
+## Usage
+
+```bash
+go run ./src/install # dry run (default, no changes)
+go run ./src/install -c # commit changes
+sudo go run ./src/install -c # commit changes as root
+go run ./src/install -config path/to/rc # use a specific config file (default: install.rc)
+go run ./src/install -v # print version
+```
+
+## Config file — `install.rc`
+
+A single `install.rc` at the repo root covers all users and systems. The program tracks a
+current `user` and `system` context as it reads the file, executing only the actions that
+match the current runtime user and detected OS. Both can change freely mid-file.
+
+Blank lines are ignored. Lines starting with `#` are comments and are ignored.
+
+### Context declarations
+
+| Declaration | Effect |
+| -------------- | ------------------------------------------------------------- |
+| `user ` | Set current user context; actions below run only as this user |
+| `system linux` | Set current system context to Linux |
+| `system mac` | Set current system context to macOS (future) |
+
+- If no `user` line appears before an action, a warning is printed and the action runs under the current user.
+- If no `system` line appears, context defaults to `linux`.
+- At the end of a run, if any actions were skipped due to a different user, a warning is printed listing the skipped users. Skipped-system actions are silent.
+
+### Actions
+
+| Action | Args | Description |
+| --------- | -------------------------- | -------------------------------------------------------- |
+| `check` | `binary install-cmd`/line | Verify binary exists; collect all failures, abort at end |
+| `assert` | command and hint in pairs | Run a shell command; collect all failures, abort at end |
+| `message` | free text, multi-line | Print a block of text to the user |
+| `mkdir` | one path/line | Create directory and parents if it does not exist |
+| `link` | path pairs | Create symlink; source first, link target second |
+| `copy` | path pairs | Copy file; skipped if target exists and checksums match |
+| `enable` | one unit/line | `systemctl enable --now ` |
+| `chmod` | `mode path`/line | Apply permission mode to path |
+| `reload` | none | `systemctl daemon-reload` |
+
+`$HOME` in paths is expanded to the current user home directory.
+`enable`, `chmod`, and `reload` are Linux-only and implicitly skipped on other systems.
+
+### `check` format
+
+First token is the binary name, rest of line is the install command printed if missing.
+All lines run before failing so every missing binary is surfaced at once.
+
+```
+check
+
+git pacman -S git
+bash pacman -S bash
+docker see https://docs.docker.com/get-docker/
+mise see https://mise.jdx.dev/getting-started.html
+shfmt go install mvdan.cc/sh/v3/cmd/shfmt@latest
+golangci-lint see https://golangci-lint.run/welcome/install
+```
+
+Output on failure:
+```
+check: missing binaries
+ git pacman -S git
+ mise see https://mise.jdx.dev/getting-started.html
+```
+
+### `assert` format
+
+Lines come in pairs: command line followed by hint line. An odd number of lines is a
+parse error. Each command is run as a shell expression; non-zero exit = failure.
+All assertions run before failing so every failure is surfaced at once.
+
+```
+assert
+
+test "$SHELL" = /bin/bash
+shell is not bash, run: chsh -s /bin/bash
+
+docker stats --no-stream
+docker daemon is not running, please start docker
+
+mise --version
+mise not found, see https://mise.jdx.dev/getting-started.html
+```
+
+### Execution order and failure
+
+Actions execute in file order. `check` and `assert` each collect all failures within their
+block then abort. Every other action fails immediately on error.
+
+### Format example
+
+```
+user vacation
+system linux
+
+check
+
+git pacman -S git
+bash pacman -S bash
+docker see https://docs.docker.com/get-docker/
+mise see https://mise.jdx.dev/getting-started.html
+
+assert
+
+test "$SHELL" = /bin/bash
+shell is not bash, run: chsh -s /bin/bash
+
+docker stats --no-stream
+docker daemon not running, please start docker
+
+message
+
+After installing mise, run:
+ mise install
+
+mkdir
+
+/home/vacation/.config/somedir
+
+link
+
+/home/vacation/Desktop/interface/dotfiles/.bashrc
+/home/vacation/.bashrc
+
+/home/vacation/Desktop/interface/bin
+/home/vacation/bin
+
+user root
+system linux
+
+link
+
+/home/vacation/Desktop/interface/etc/systemd/system/system-snapshot.timer
+/etc/systemd/system/system-snapshot.timer
+
+copy
+
+/home/vacation/Desktop/interface/etc/fstab
+/etc/fstab
+
+enable
+
+system-snapshot.timer
+btrfs-scrub@Archlinux.timer
+
+chmod
+
+o+x /home/vacation
+
+reload
+
+system mac
+
+# mac-specific equivalents here in the future
+```
+
+## Behavior
+
+### No-op by default
+
+Dry run unless `-c` or `-commit` is passed. Dry run prints what would change, nothing else.
+`check` and `assert` always run even in dry run since they are read-only.
+
+### Validate-first
+
+Before applying any changes, all operations are validated. Validation runs the same checks
+in both dry run and commit modes. If validation finds errors, the program exits before
+touching anything. This surfaces all problems upfront rather than failing halfway through.
+
+Validation checks:
+- Source file exists
+- Target parent directory exists (or will be created via `mkdir`)
+- For `link`: if target exists, verify it already points to the correct source
+- For `copy`: if target exists, checksum both files; identical = already done
+- For `assert`: line count is even
+
+### Idempotent
+
+Re-running is safe. Each operation detects its already-complete state and skips:
+- `link`: target is already a symlink to the correct source → skip
+- `copy`: target exists and checksums match → skip
+- `mkdir`: directory already exists → skip
+
+`enable`, `chmod`, and `reload` always execute unconditionally.
+
+### Broken link cleanup
+
+Before linking, any broken symlink at the target path is removed. A warning is printed.
+
+### Directory creation
+
+If a target parent directory does not exist, it is created. A warning is printed.
+`mkdir` in the config file creates directories explicitly with no warning.
+
+### Fail fast at runtime
+
+If an unexpected error occurs during commit (after validation passed), the program exits
+immediately. The next run skips already-completed operations and picks up where it left off.
+
+
+## Testing
+
+### Test config — `src/install/test.rc`
+
+A dedicated `test.rc` exercises every action and edge case. It is designed to run safely
+in a temp directory without touching real system paths. Use it after any change to the
+install program.
+
+```bash
+mkdir -p /tmp/install-test
+go run ./src/install -config src/install/test.rc -c
+```
+
+The test config covers:
+
+```
+# no user declared — should warn and run as current user
+system linux
+
+# check: one present binary, one missing
+check
+
+bash pacman -S bash
+this-binary-does-not-exist pacman -S this-binary-does-not-exist
+
+# assert: one passing, one failing
+assert
+
+test -d /tmp
+/tmp should always exist
+
+test -d /tmp/this-does-not-exist
+hint for a failing assert
+
+# message: should always print
+message
+
+This is a test message.
+It spans multiple lines.
+
+# mkdir: new dir and already-existing dir (idempotent)
+mkdir
+
+/tmp/install-test/newdir
+/tmp/install-test/newdir
+
+# link: new link, already-correct link (skip), broken link (cleanup + relink)
+link
+
+/tmp/install-test/newdir
+/tmp/install-test/link-to-newdir
+
+/tmp/install-test/newdir
+/tmp/install-test/link-to-newdir
+
+# copy: new copy, already-identical copy (skip), mismatched copy (error)
+copy
+
+/etc/hostname
+/tmp/install-test/hostname-copy
+
+/etc/hostname
+/tmp/install-test/hostname-copy
+
+# user block that will be skipped (prints end-of-run warning)
+user nonexistent-user
+
+link
+
+/etc/hostname
+/tmp/install-test/should-not-exist
+
+# system block that will be skipped silently
+system mac
+
+message
+
+This should not print on Linux.
+```
+
+Expected outcomes to verify manually:
+- Warning printed for missing user declaration at top
+- `check` fails listing the missing binary with its hint, does not abort before checking all
+- `assert` fails listing the failing assertion with its hint, does not abort before checking all
+- `message` always prints
+- `mkdir` creates the new dir; second call skips silently
+- `link` creates the link; second call skips (already correct); broken link triggers warning then relink
+- `copy` copies the file; second call skips (checksums match); mismatched copy errors
+- End-of-run warning lists `nonexistent-user` block was skipped
+- Mac `message` block does not print
+
+### Unit tests
+
+The parts most prone to bugs and regressions are:
+
+**`config.go` — parser.** The parser handles the most complex logic: context switching
+(user/system mid-file), blank line and comment stripping, pair validation for link/copy,
+even-line validation for assert, and the interaction between inherited vs explicit system
+context. A bug here silently misroutes operations to the wrong user or system. Unit test
+cases should cover: no user declared, user mid-file change, system mid-file change,
+comments and blank lines interleaved, malformed pairs (odd count), malformed assert
+(odd count), unknown action keyword.
+
+**`link.go` — link and copy.** Idempotency logic is easy to get wrong. Unit test cases:
+new link, already-correct link (skip), broken link (cleanup + relink), link pointing
+elsewhere (error), new copy, identical copy (skip, checksum match), differing copy (error),
+missing source (error), missing target parent (mkdir then proceed).
+
+**`check.go` — check and assert.** Failure collection must not short-circuit. Test that
+all lines are evaluated even when early lines fail. Assert even-line validation. Test
+that assert runs shell expressions correctly and captures non-zero exit.
+
+Tests live alongside source files in `src/install/` using the standard `_test.go` convention.
+Where filesystem access is needed, use `t.TempDir()` to keep tests hermetic.
+
+## File layout
+
+```
+src/install/
+├── go.mod # standalone module, no external dependencies
+├── main.go # flags, load config, filter by current user/system, validate, commit
+├── config.go # parse install.rc into a flat list of user+system-scoped operations
+├── link.go # link, copy, mkdir operations
+├── check.go # check, assert, message operations
+└── system.go # enable, reload, chmod operations
+```
+
+## todo once completed and tested
+
+delete t0filer, migrate config files
\ No newline at end of file
diff --git a/dotfiles/.commitlintrc.yml b/dotfiles/.commitlintrc.yml
index 54c11a0..781cb5a 100644
--- a/dotfiles/.commitlintrc.yml
+++ b/dotfiles/.commitlintrc.yml
@@ -58,4 +58,3 @@ rules:
- style
- test
- misc
- - chore
diff --git a/dotfiles/.config/Claude/claude_desktop_config.json b/dotfiles/.config/Claude/claude_desktop_config.json
index b55b353..10d64bc 100644
--- a/dotfiles/.config/Claude/claude_desktop_config.json
+++ b/dotfiles/.config/Claude/claude_desktop_config.json
@@ -17,9 +17,12 @@
"preferences": {
"quickEntryShortcut": "off",
"quickEntryDictationShortcut": "capslock",
- "coworkScheduledTasksEnabled": false,
- "ccdScheduledTasksEnabled": false,
- "sidebarMode": "chat",
+ "localAgentModeTrustedFolders": [
+ "/usr/lib/claude-desktop-bin/app.asar"
+ ],
+ "coworkScheduledTasksEnabled": true,
+ "ccdScheduledTasksEnabled": true,
+ "sidebarMode": "code",
"coworkWebSearchEnabled": true
}
}
\ No newline at end of file
diff --git a/dotfiles/.config/code-oss/User/settings.json b/dotfiles/.config/code-oss/User/settings.json
index 6457137..702440b 100644
--- a/dotfiles/.config/code-oss/User/settings.json
+++ b/dotfiles/.config/code-oss/User/settings.json
@@ -71,7 +71,7 @@
"resolvePluginsRelativeTo": ".",
},
"[shellscript]": {
- "editor.defaultFormatter": "lumirelle.shell-format-rev",
+ "editor.defaultFormatter": "foxundermoon.shell-format",
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
@@ -94,6 +94,27 @@
"[yaml]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
},
+ "[css]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ },
+ "[html]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ },
+ "[jsonc]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ },
+ "[dockercompose]": {
+ "editor.insertSpaces": true,
+ "editor.tabSize": 2,
+ "editor.autoIndent": "advanced",
+ "editor.defaultFormatter": "redhat.vscode-yaml",
+ },
+ "[go]": {
+ "editor.formatOnSave": true,
+ "editor.codeActionsOnSave": {
+ "source.organizeImports": "explicit",
+ },
+ },
"sql-formatter.uppercase": true,
//
// - - - - - - - - GIT - - - - - - - - -
@@ -107,36 +128,20 @@
// - - - - - - - - BASH - - - - - - - - -
//
"shellcheck.customArgs": ["-x"],
- //
- "shellformat.path": "/usr/local/bin/shfmt",
+ "shellformat.path": "/usr/bin/shfmt",
"shellformat.flag": "-i 2 -ln bash",
"shellformat.useEditorConfig": true,
//
// - - - - - - - - JS/TS - - - - - - - - -
//
- "javascript.validate.enable": false,
- "javascript.updateImportsOnFileMove.enabled": "always",
- "javascript.preferences.importModuleSpecifier": "non-relative",
- "typescript.preferences.importModuleSpecifier": "non-relative",
- "typescript.preferences.importModuleSpecifierEnding": "minimal",
- "[html]": {
- "editor.defaultFormatter": "esbenp.prettier-vscode",
- },
- "[jsonc]": {
- "editor.defaultFormatter": "esbenp.prettier-vscode",
- },
- "typescript.implementationsCodeLens.enabled": true,
- "typescript.updateImportsOnFileMove.enabled": "always",
+ "js/ts.updateImportsOnFileMove.enabled": "always",
+ "js/ts.preferences.importModuleSpecifier": "non-relative",
+ "js/ts.preferences.importModuleSpecifierEnding": "minimal",
+ "js/ts.implementationsCodeLens.enabled": true,
//
// - - - - - - - - GO - - - - - - - - -
//
"go.useLanguageServer": true,
- "[go]": {
- "editor.formatOnSave": true,
- "editor.codeActionsOnSave": {
- "source.organizeImports": "explicit",
- },
- },
"go.testOnSave": false,
"go.delveConfig": {
"debugAdapter": "dlv-dap",
@@ -144,40 +149,132 @@
"go.testEnvFile": "${workspaceFolder}/.env",
"go.testFlags": ["-race"],
"go.lintTool": "golangci-lint",
- "go.lintFlags": ["--print-issued-lines=false"],
"go.vetOnSave": "off",
"go.formatTool": "default",
+ "go.inlayHints.assignVariableTypes": true,
+ "go.inlayHints.parameterNames": true,
+ "go.inlayHints.compositeLiteralFields": true,
+ "go.inlayHints.compositeLiteralTypes": false,
+ "go.inlayHints.constantValues": true,
+ "go.inlayHints.functionTypeParameters": true,
+ "go.inlayHints.rangeVariableTypes": false,
"gopls": {
- "ui.semanticTokens": true,
- "build.buildFlags": ["-buildvcs=false"],
-
- // Show inlay hints for parameter names, type params, etc.
- "ui.inlayhints.parameterNames": true,
- "ui.inlayhints.assignVariableTypes": true,
- "ui.inlayhints.compositeLiteralFields": true,
- "ui.inlayhints.compositeLiteralTypes": true,
- "ui.inlayhints.constantValues": true,
- "ui.inlayhints.functionTypeParameters": true,
- "ui.inlayhints.rangeVariableTypes": true,
+ "semanticTokens": true,
+ "buildFlags": ["-buildvcs=false"],
// Staticcheck: enables many extra linters beyond go vet
- "ui.diagnostic.staticcheck": true,
+ "staticcheck": true,
// Analyses: fine-grained control over individual analyzers
- "ui.diagnostic.analyses": {
- "shadow": true, // warn on shadowed variables
+ "analyses": {
"nilness": true, // detect nil dereferences
"unusedparams": true, // warn on unused function params
"unusedwrite": true, // warn on writes to vars never read
"useany": true, // suggest `any` instead of `interface{}`
+ "QF1001": true,
+ "QF1005": true,
+ "QF1006": true,
+ "QF1007": true,
+ "QF1008": true,
+ "QF1011": true,
+ "S1002": true,
+ "S1005": true,
+ "S1006": true,
+ "S1008": true,
+ "S1011": true,
+ "S1016": true,
+ "S1021": true,
+ "S1025": true,
+ "S1029": true,
+ "SA1000": true,
+ "SA1002": true,
+ "SA1003": true,
+ "SA1007": true,
+ "SA1010": true,
+ "SA1011": true,
+ "SA1014": true,
+ "SA1015": true,
+ "SA1017": true,
+ "SA1018": true,
+ "SA1020": true,
+ "SA1021": true,
+ "SA1023": true,
+ "SA1024": true,
+ "SA1025": true,
+ "SA1026": true,
+ "SA1027": true,
+ "SA1028": true,
+ "SA1029": true,
+ "SA1030": true,
+ "SA1031": true,
+ "SA1032": true,
+ "SA2002": true,
+ "SA2003": true,
+ "SA4005": true,
+ "SA4006": true,
+ "SA4008": true,
+ "SA4009": true,
+ "SA4010": true,
+ "SA4012": true,
+ "SA4015": true,
+ "SA4017": true,
+ "SA4018": true,
+ "SA4023": true,
+ "SA4031": true,
+ "SA5000": true,
+ "SA5002": true,
+ "SA5005": true,
+ "SA5007": true,
+ "SA5010": true,
+ "SA5011": true,
+ "SA5012": true,
+ "SA6000": true,
+ "SA6001": true,
+ "SA6002": true,
+ "SA6003": true,
+ "SA9001": true,
+ "SA9003": true,
+ "SA9005": true,
+ "SA9007": true,
+ "SA9008": true,
+ "ST1000": true,
+ "ST1001": true,
+ "ST1003": true,
+ "ST1005": true,
+ "ST1006": true,
+ "ST1008": true,
+ "ST1011": true,
+ "ST1012": true,
+ "ST1013": true,
+ "ST1015": true,
+ "ST1016": true,
+ "ST1017": true,
+ "ST1018": true,
+ "ST1019": true,
+ "ST1020": true,
+ "ST1021": true,
+ "ST1022": true,
+ "ST1023": true,
+ "appendclipped": true,
+ "shadow": true,
+ "slicesdelete": true,
},
+ // "hints": {
+ // "assignVariableTypes": true,
+ // "parameterNames": true,
+ // "compositeLiteralFields": true,
+ // "compositeLiteralTypes": false,
+ // "constantValues": true,
+ // "functionTypeParameters": true,
+ // "rangeVariableTypes": false,
+ // },
+
// Completion options
- "ui.completion.usePlaceholders": true, // fill in param placeholders on completion
- "ui.completion.matcher": "Fuzzy", // fuzzy matching (vs "CaseInsensitive" or "CaseSensitive")
+ "usePlaceholders": true, // fill in param placeholders on completion
// Codelens: inline actionable buttons
- "ui.codelenses": {
+ "codelenses": {
"gc_details": true, // toggle compiler optimization details
"run_govulncheck": true,
"tidy": true,
@@ -186,11 +283,11 @@
},
// Format with gofumpt (stricter than gofmt) — requires gofumpt installed
- "formatting.gofumpt": true,
+ "gofumpt": true,
// Automatically add missing imports and remove unused ones on save
// (usually controlled by editor settings, but can be forced here)
- "ui.diagnostic.annotations": {
+ "annotations": {
"bounds": true, // annotate slice bounds checks
"escape": true, // annotate heap escapes
"inline": true, // annotate inlining decisions
@@ -270,6 +367,11 @@
"markdown": false,
"scminput": false,
},
+ "[github-actions-workflow]": {
+ "editor.defaultFormatter": "redhat.vscode-yaml",
+ },
+ "github.copilot.nextEditSuggestions.enabled": true,
+ "github.copilot.nextEditSuggestions.eagerness": "medium",
"cSpell.userWords": [
"AIAPI",
"aiplugin",
@@ -344,21 +446,22 @@
"cSpell.maxDuplicateProblems": 2,
"cSpell.spellCheckDelayMs": 2000,
"cSpell.maxNumberOfProblems": 5,
- "#cSpell.enableFiletypes#": [
- "go",
- "plaintext",
- "markdown",
- "json",
- "yaml",
- "html",
- "css",
- "javascript",
- "typescript",
- "shellscript",
- ],
"cSpell.enabledNotifications": {
"Lines too Long": false,
},
+ "cSpell.enabledFileTypes": {
+ "ruby": true,
+ "go": true,
+ "plaintext": true,
+ "markdown": true,
+ "json": true,
+ "yaml": true,
+ "html": true,
+ "css": true,
+ "javascript": true,
+ "typescript": true,
+ "shellscript": true,
+ },
"window.commandCenter": false,
"window.menuBarVisibility": "hidden",
"window.enableMenuBarMnemonics": false,
@@ -370,20 +473,4 @@
"redhat.telemetry.enabled": true,
"security.workspace.trust.untrustedFiles": "open",
"explorer.confirmPasteNative": false,
- "[dockercompose]": {
- "editor.insertSpaces": true,
- "editor.tabSize": 2,
- "editor.autoIndent": "advanced",
- "editor.defaultFormatter": "redhat.vscode-yaml",
- },
- "[github-actions-workflow]": {
- "editor.defaultFormatter": "redhat.vscode-yaml",
- },
- "github.copilot.nextEditSuggestions.enabled": true,
- "[css]": {
- "editor.defaultFormatter": "esbenp.prettier-vscode",
- },
- //
- // - - - - - - - - NEW - - - - - - - - -
- //
}
diff --git a/dotfiles/.functions.sh b/dotfiles/.functions.sh
index bc38c9c..9695211 100644
--- a/dotfiles/.functions.sh
+++ b/dotfiles/.functions.sh
@@ -131,9 +131,6 @@ yad() {
#- - - - - - - - - - -
-# todo: remove
-unalias gd 2>/dev/null
-
gd() {
if ! command jj root &>/dev/null; then
command git diff "$@"
@@ -146,8 +143,6 @@ gd() {
#- - - - - - - - - - -
# when there's no repo, call ls instead
-# todo: remove
-unalias gss 2>/dev/null
gss() {
if ! command jj root &>/dev/null; then
_git_gss
@@ -559,6 +554,7 @@ restore_session() {
if [[ -z "$command" ]]; then
echo "no session found for $dir"
+ claude
fi
eval "$command"
@@ -566,7 +562,6 @@ restore_session() {
#----------------
-unalias lg 2>/dev/null
lg() {
if ! command jj root &>/dev/null; then
lazy-git "$@"
diff --git a/etc/fstab b/etc/fstab
index 1c327ce..8d5de37 100644
--- a/etc/fstab
+++ b/etc/fstab
@@ -1,6 +1,8 @@
# Static information about the filesystems.
# See fstab(5) for details.
-# Hardlink to /etc/fstab, symbolic *breaks*
+
+# symbolic linking into /etc doesn't work, subvol not mounted when kernel needs fstab
+# hard link fails because subvol counts as a cross device link
#############################################################################################################################################
#
@@ -17,7 +19,7 @@
#
########################################################## auto #############################################################################
#
-LABEL=EFI-BLUE /boot vfat auto,rw,umask=111,dmask=000 0 2
+LABEL=EFI-SAMSUNG /boot vfat auto,rw,umask=133,dmask=033 0 2
######################
LABEL=Data2TB /media/data btrfs defaults,noatime,compress=lzo 0 0
LABEL=Data2TB /home/vacation/.ollama btrfs subvol=/toplevel/@.ollama,defaults,noatime,compress=lzo 0 0
@@ -26,22 +28,22 @@ LABEL=Data4TB /media/data4tb btrfs defaults,
LABEL=Data4TB /plex btrfs subvol=/toplevel/@plex,defaults,noatime,compress=lzo 0 0
LABEL=Data4TB /home/vacation/Videos/plex btrfs subvol=/toplevel/@plex,defaults,noatime,compress=lzo 0 0
######################
-LABEL=Archlinux /toplevel btrfs subvol=/,defaults,noatime,compress=lzo 0 0
-LABEL=Archlinux /home/vacation btrfs subvol=/@vacation,defaults,noatime,compress=lzo 0 0
-LABEL=Archlinux /home/vacation/.local/share/lutris btrfs subvol=/@lutris,defaults,noatime,compress=lzo 0 0
-LABEL=Archlinux /home/vacation/.local/share/Steam btrfs subvol=/@steam,defaults,noatime,compress=lzo 0 0
-LABEL=Archlinux /var/lib/postgres btrfs subvol=/@postgres,defaults,noatime,compress=lzo 0 0
-LABEL=Archlinux /home/vacation/.cache btrfs subvol=/@.cache,defaults,noatime,compress=lzo 0 0
-LABEL=Archlinux /var/lib/docker btrfs subvol=/@docker,defaults,noatime,compress=lzo 0 0
+LABEL=ArchAM5 /toplevel btrfs subvol=/,defaults,noatime,compress=lzo 0 0
+LABEL=ArchAM5 /home/vacation btrfs subvol=/@vacation,defaults,noatime,compress=lzo 0 0
+LABEL=ArchAM5 /home/vacation/.local/share/lutris btrfs subvol=/@lutris,defaults,noatime,compress=lzo 0 0
+LABEL=ArchAM5 /home/vacation/.local/share/Steam btrfs subvol=/@steam,defaults,noatime,compress=lzo 0 0
+LABEL=ArchAM5 /var/lib/postgres btrfs subvol=/@postgres,defaults,noatime,compress=lzo 0 0
+LABEL=ArchAM5 /home/vacation/.cache btrfs subvol=/@.cache,defaults,noatime,compress=lzo 0 0
+LABEL=ArchAM5 /var/lib/docker btrfs subvol=/@docker,defaults,noatime,compress=lzo 0 0
#
####################################### noauto ###############################################################################################
#
-LABEL=EFI-2TB /mnt/efi2tb vfat noauto,user,rw,umask=111,dmask=000 0 2
-LABEL=EFI-4TB /mnt/efi4tb vfat noauto,user,rw,umask=111,dmask=000 0 2
+LABEL=EFI-2TB /mnt/efi2tb vfat noauto,user,rw,umask=133,dmask=000 0 2
+LABEL=EFI-4TB /mnt/efi4tb vfat noauto,user,rw,umask=133,dmask=000 0 2
LABEL=External /media/external exfat defaults,noauto,user,exec 0 2
#
####################################### system ##############################################################################################
#
hugetlbfs /dev/hugepages hugetlbfs mode=01770,gid=kvm 0 0
/swap/swapfile none swap defaults,pri=0 0 0
-LABEL=Archlinux /swap btrfs subvol=/@swap,defaults,noatime,compress=lzo 0 0
+LABEL=ArchAM5 /swap btrfs subvol=/@swap,defaults,noatime,compress=lzo 0 0
diff --git a/etc/systemd/system/btrfs-scrub@.timer b/etc/systemd/system/btrfs-scrub@.timer
index d3439e2..3dd8e41 100644
--- a/etc/systemd/system/btrfs-scrub@.timer
+++ b/etc/systemd/system/btrfs-scrub@.timer
@@ -1,7 +1,7 @@
[Unit]
Description=Monthly Btrfs scrub on %f
-After=time-set.target local-fs.target
-Wants=time-set.target local-fs.target
+After=time-set.target home-vacation.mount local-fs.target
+Wants=time-set.target home-vacation.mount local-fs.target
[Timer]
OnCalendar=monthly
diff --git a/etc/systemd/system/system-snapshot.timer b/etc/systemd/system/system-snapshot.timer
index a4714f4..b31884c 100644
--- a/etc/systemd/system/system-snapshot.timer
+++ b/etc/systemd/system/system-snapshot.timer
@@ -1,7 +1,7 @@
[Unit]
Description=btrfs snapshot system timer
-After=time-set.target
-Wants=time-set.target
+After=time-set.target home-vacation.mount
+Wants=time-set.target home-vacation.mount
[Timer]
OnCalendar=hourly