chore(idols-ai): move tmp state off RAM onto btrfs and harden tmp mounts (#348)

* feat(idols-ai): persist /var/tmp and small state dirs, wipe /var/tmp at boot

Move the strata model staging area (~3G) off the root tmpfs onto the
persistent btrfs volume to free RAM, preserve btrbk/cups/logrotate
state, and wipe /var/tmp at boot so it only accumulates between
reboots.

* chore(idols-ai): add nosuid,nodev to root tmpfs and tmp btrfs mounts

Hardens /, /tmp and /snapshots; safe because /dev, /run, /nix and
/run/setuid-programs are separate mounts, so nothing there needs suid
bits or device nodes.

* chore(idols-ai): add noatime to the @tmp btrfs mount

Temp files do not need access times; matches the @nix subvolume style
and saves btrfs metadata writes.

* chore(idols-ai): harden the /var/tmp bind mount with nosuid,nodev

World-writable staging data should not honor suid bits or device
nodes, same as /tmp and /snapshots.

* fix(idols-ai): preserve logrotate state as a file, fix persisted dir ownership

- /var/lib/logrotate.status is a regular file, not a directory: move it from
  preservation directories to files. A missing state entry makes logrotate
  treat every log as freshly rotated, so it must survive reboots.
- keep /var/lib/btrbk owned by btrbk:btrbk 0750 so preservation does not
  override the btrbk module's rule with root:root.
- set /var/tmp mode 1777 explicitly instead of relying on systemd's tmp.conf
  ordering.
- rewrite comments to describe the paths generically.

* chore(idols-ai): cap journal, wipe coredumps at boot, persist upower/noctalia

- services.journald.settings.Journal: SystemMaxUse=10G, RuntimeMaxUse=256M,
  matching the cap already set on idols-kana/idols-ruby.
- wipe /var/lib/systemd/coredump at boot: it lives in the persisted
  /var/lib/systemd and crash dumps can contain secrets copied from memory.
- persist /var/lib/upower (device battery history) and
  ~/.local/state/noctalia (shell settings, notification/clipboard history).
This commit is contained in:
ryan4yin | 二花 authored and GitHub committed 2026-10-02 21:58:21 +08:00
1 parent a26ab3be63
commit 9254b63a18
3 files changed
+57

No files matched your search

+7
View File
@@ -62,6 +62,13 @@ in
modules.btrbk.enable = true;
# systemd-journal - cap disk usage
# https://www.freedesktop.org/software/systemd/man/latest/journald.conf.html
services.journald.settings.Journal = {
SystemMaxUse = "10G";
RuntimeMaxUse = "256M";
};
# QEMU/KVM(HostCpuOnly) for local VMs.
environment.systemPackages = [ pkgs.qemu_kvm ];
+7
View File
@@ -17,6 +17,8 @@
nodev."/" = {
fsType = "tmpfs";
mountOptions = [
"nosuid" # Ignore setuid bit; no suid binaries live on the root tmpfs
"nodev" # Ignore device files; nothing here needs to be a device node
"size=4G"
"relatime" # Update inode access times relative to modify/change time
"mode=755"
@@ -96,12 +98,17 @@
"@snapshots" = {
mountpoint = "/snapshots";
mountOptions = [
"nosuid"
"nodev"
"compress-force=zstd:1"
];
};
"@tmp" = {
mountpoint = "/tmp";
mountOptions = [
"nosuid"
"nodev"
"noatime" # Temp files don't need access times; saves btrfs metadata writes
"compress-force=zstd:1"
];
};
+43
View File
@@ -51,10 +51,36 @@ in
"/var/log"
# system caches (e.g. restic, plocate; slow to rebuild)
"/var/cache"
# General scratch space. It is re-creatable and can grow large, so keep it
# off the root tmpfs and on the persistent volume. Contents only
# accumulate between reboots: the tmpfiles rule below wipes it at boot.
# World-writable scratch, so harden the bind mount like /tmp; the mode
# must stay 1777 to match that `D!` rule.
{
directory = "/var/tmp";
mode = "1777";
mountOptions = [
"nosuid"
"nodev"
];
}
# btrbk state/home (SSH keys, cache). The owner must match the btrbk
# module's `d /var/lib/btrbk 0750 btrbk btrbk` rule, which preservation
# would otherwise override with the default root:root.
{
directory = "/var/lib/btrbk";
user = "btrbk";
group = "btrbk";
mode = "0750";
}
# CUPS state (the print spool lives in /var/spool/cups, not here)
"/var/lib/cups"
# system-core
"/var/lib/nixos"
"/var/lib/systemd"
# upower device battery history (e.g. Magic Trackpad)
"/var/lib/upower"
{
directory = "/var/lib/private";
mode = "0700";
@@ -82,6 +108,10 @@ in
file = "/etc/machine-id";
inInitrd = true;
}
# logrotate's state file (a regular file, not a directory). It records the
# last rotation per log; a missing entry makes logrotate treat the log as
# freshly rotated, so the state must survive reboots.
{ file = "/var/lib/logrotate.status"; }
];
# the following directories will be passed to /persistent/home/$USER
@@ -127,6 +157,7 @@ in
".local/state/home-manager"
".local/state/nix/profiles"
".local/state/noctalia" # shell settings, notification/clipboard history
".local/share/nix"
# ======================================
@@ -372,6 +403,18 @@ in
"/home/${username}/.terraform.d".d = permission;
};
# /var/tmp is re-creatable scratch, but it must not consume RAM, so it is
# bind-mounted onto the persistent volume above. Wipe it at boot (mirrors
# nixpkgs `boot.tmp.cleanOnBoot` for /tmp) so contents only accumulate
# between reboots.
#
# Crash dumps live in the persisted /var/lib/systemd and can contain secrets
# copied out of process memory, so don't keep them across reboots either.
systemd.tmpfiles.rules = [
"D! /var/tmp 1777 root root"
"D! /var/lib/systemd/coredump 0755 root root"
];
# systemd-machine-id-commit.service would fail but it is not relevant
# in this specific setup for a persistent machine-id so we disable it
#