diff --git a/hosts/idols-ai/preservation.nix b/hosts/idols-ai/preservation.nix index c51a4230..c1256878 100644 --- a/hosts/idols-ai/preservation.nix +++ b/hosts/idols-ai/preservation.nix @@ -110,6 +110,14 @@ in # Keep .cache off tmpfs to avoid high RAM usage; many apps use it and it is storage-heavy. ".cache" + # NOTE: do NOT persist ~/.local/share/Trash here. The trash crate (nushell + # `rm --trash`) picks the home trash only when the file's mount topdir equals + # the trash dir's topdir; a bind-mounted Trash dir would make files straight + # under $HOME fail with EACCES (it would try /.Trash-$uid). The home trash on + # tmpfs is lost on reboot, which is acceptable: scattered per-mount + # .Trash-$uid dirs on persistent volumes are covered by the trash-empty + # retention timer (modules/nixos/base/trash.nix). + # ====================================== # Codes / Work / Playground # ====================================== diff --git a/modules/base/packages.nix b/modules/base/packages.nix index 6e243161..4e4241c9 100644 --- a/modules/base/packages.nix +++ b/modules/base/packages.nix @@ -86,5 +86,6 @@ which tree tealdeer # a very fast version of tldr + trash-cli # freedesktop trash can CLI: trash-list / trash-restore / trash-put / trash-empty ]; } diff --git a/modules/nixos/base/trash.nix b/modules/nixos/base/trash.nix new file mode 100644 index 00000000..1c70598c --- /dev/null +++ b/modules/nixos/base/trash.nix @@ -0,0 +1,61 @@ +{ + # ================================================================== + # + # Trash retention cleanup (nixpkgs `trash-cli`) + # https://github.com/andreafrancia/trash-cli + # + # On a tmpfs root with persistent dirs bind-mounted in, the trash + # crate (nushell `rm --trash`, GUI file managers) scatters items into + # per-mount .Trash-$uid dirs that file managers never show. This timer + # purges trash items older than RETENTION_DAYS across the home trash + # and all mount points. + # + # Usage: + # trash-list # view items in all trash dirs + # trash-restore # restore an item + # trash-rm # remove a single item from the trash + # trash-empty 30 -f # run the retention purge manually + # + # ================================================================== + + myvars, + pkgs, + ... +}: +let + retentionDays = 30; +in +{ + systemd.services.trash-empty = { + description = "Purge trash items older than ${toString retentionDays} days"; + script = '' + ${pkgs.trash-cli}/bin/trash-empty ${toString retentionDays} -f + ''; + # The scattered .Trash-$uid dirs live inside the preservation bind mounts, so we + # must run after preservation.target (reached only once all those mounts are up). + # preservation.mount units use DefaultDependencies=no, so they are NOT ordered + # after local-fs.target on their own; list both to be safe on every host. + # On hosts without preservation, the After=preservation.target edge is a no-op. + after = [ + "local-fs.target" + "preservation.target" + ]; + serviceConfig = { + Type = "oneshot"; + User = myvars.username; + Environment = [ + "HOME=/home/${myvars.username}" + "XDG_DATA_HOME=/home/${myvars.username}/.local/share" + ]; + }; + }; + + systemd.timers.trash-empty = { + description = "Timer for the trash-empty service"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = "daily"; + RandomizedDelaySec = "15min"; + }; + }; +}