From 5118c221fd1785e024182484f5827203aa972443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Fri, 6 Mar 2026 23:39:33 -0800 Subject: [PATCH 1/7] feat(nixos): add shared restic-backup module Extracts restic backup configuration into a reusable NixOS module with shared R2 bucket, credentials, and default prune options. Paths are configured per-host. Co-Authored-By: Claude Opus 4.6 --- modules/nixos/default.nix | 1 + modules/nixos/restic-backup.nix | 42 +++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 modules/nixos/restic-backup.nix diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix index dedc1b46..6486c9c3 100644 --- a/modules/nixos/default.nix +++ b/modules/nixos/default.nix @@ -4,6 +4,7 @@ ./llm ./web ./filebrowser-quantum.nix + ./restic-backup.nix ./users.nix ./ssh.nix ./sudo.nix diff --git a/modules/nixos/restic-backup.nix b/modules/nixos/restic-backup.nix new file mode 100644 index 00000000..a48c0d32 --- /dev/null +++ b/modules/nixos/restic-backup.nix @@ -0,0 +1,42 @@ +{ + config, + lib, + ... +}: let + cfg = config.services.restic-backup; +in { + options.services.restic-backup = { + enable = lib.mkEnableOption "restic backup to Cloudflare R2"; + + paths = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = []; + description = "Paths to back up."; + }; + + pruneOpts = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ + "--keep-daily 7" + "--keep-weekly 5" + "--keep-monthly 12" + ]; + description = "Prune options for restic forget."; + }; + }; + + config = lib.mkIf cfg.enable { + age.secrets.restic-env.file = ../../secrets/restic-env.age; + age.secrets.restic-password.file = ../../secrets/restic-password.age; + + services.restic.backups.daily = { + initialize = true; + + environmentFile = config.age.secrets.restic-env.path; + passwordFile = config.age.secrets.restic-password.path; + + repository = "s3:https://9c12166db465350c0f02410b390d0cbc.r2.cloudflarestorage.com/restic"; + inherit (cfg) paths pruneOpts; + }; + }; +} From 8a0af908ea8abf0dd0c23599f1dffc880560b1c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Fri, 6 Mar 2026 23:39:50 -0800 Subject: [PATCH 2/7] refactor: move restic secrets to shared location Move restic-env and restic-password secrets from hosts/spore/secrets/ to secrets/ so they can be shared across hosts. Update key definitions to allow glyph, spore, and Rhizome to decrypt. Requires `agenix --rekey` to re-encrypt with the updated key set. Co-Authored-By: Claude Opus 4.6 --- lib/secrets/default.nix | 8 ++++++-- lib/secrets/spore.nix | 2 -- {hosts/spore/secrets => secrets}/restic-env.age | 0 {hosts/spore/secrets => secrets}/restic-password.age | 0 4 files changed, 6 insertions(+), 4 deletions(-) rename {hosts/spore/secrets => secrets}/restic-env.age (100%) rename {hosts/spore/secrets => secrets}/restic-password.age (100%) diff --git a/lib/secrets/default.nix b/lib/secrets/default.nix index 6e135138..d98e78ec 100644 --- a/lib/secrets/default.nix +++ b/lib/secrets/default.nix @@ -1,3 +1,7 @@ # Global secrets that multiple hosts need access to -# Currently empty - all secrets are host-specific -{} +let + keys = with (import ../keys.nix); [glyph spore Rhizome]; +in { + "secrets/restic-env.age".publicKeys = keys; + "secrets/restic-password.age".publicKeys = keys; +} diff --git a/lib/secrets/spore.nix b/lib/secrets/spore.nix index a9335320..c1814d85 100644 --- a/lib/secrets/spore.nix +++ b/lib/secrets/spore.nix @@ -12,7 +12,5 @@ in { "hosts/spore/secrets/notifier-smtp-password.age".publicKeys = keys; "hosts/spore/secrets/oauth2-proxy-env.age".publicKeys = keys; "hosts/spore/secrets/pocket-id-encryption-key.age".publicKeys = keys; - "hosts/spore/secrets/restic-env.age".publicKeys = keys; - "hosts/spore/secrets/restic-password.age".publicKeys = keys; "hosts/spore/secrets/tailscale-auth-key.age".publicKeys = keys; } diff --git a/hosts/spore/secrets/restic-env.age b/secrets/restic-env.age similarity index 100% rename from hosts/spore/secrets/restic-env.age rename to secrets/restic-env.age diff --git a/hosts/spore/secrets/restic-password.age b/secrets/restic-password.age similarity index 100% rename from hosts/spore/secrets/restic-password.age rename to secrets/restic-password.age From eb955af289fcbe099052b536047c7144bcac270e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Fri, 6 Mar 2026 23:39:56 -0800 Subject: [PATCH 3/7] refactor(spore): migrate to shared restic-backup module Replace inline restic configuration with the shared module. Backup paths and prune defaults remain the same. Co-Authored-By: Claude Opus 4.6 --- hosts/spore/backup.nix | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/hosts/spore/backup.nix b/hosts/spore/backup.nix index 54b79073..8d30fc99 100644 --- a/hosts/spore/backup.nix +++ b/hosts/spore/backup.nix @@ -1,27 +1,8 @@ -{ - config, - pkgs, - ... -}: { - age.secrets.restic-env.file = ./secrets/restic-env.age; - age.secrets.restic-password.file = ./secrets/restic-password.age; - services.restic.backups = { - daily = { - initialize = true; - - environmentFile = config.age.secrets.restic-env.path; - passwordFile = config.age.secrets.restic-password.path; - - repository = "s3:https://9c12166db465350c0f02410b390d0cbc.r2.cloudflarestorage.com/restic"; - paths = [ - config.services.postgresqlBackup.location - ]; - - pruneOpts = [ - "--keep-daily 7" - "--keep-weekly 5" - "--keep-monthly 12" - ]; - }; +{config, ...}: { + services.restic-backup = { + enable = true; + paths = [ + config.services.postgresqlBackup.location + ]; }; } From 29485de9c22e5b6c702d3fce3d5d159e856ffaa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Fri, 6 Mar 2026 23:40:09 -0800 Subject: [PATCH 4/7] feat(glyph): enable restic backup for basic-memory Back up /var/lib/basic-memory to Cloudflare R2 using the shared restic-backup module. Co-Authored-By: Claude Opus 4.6 --- hosts/glyph/services/default.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hosts/glyph/services/default.nix b/hosts/glyph/services/default.nix index 01df5392..98cd51e6 100644 --- a/hosts/glyph/services/default.nix +++ b/hosts/glyph/services/default.nix @@ -73,6 +73,10 @@ }; services.basic-memory.enable = true; + services.restic-backup = { + enable = true; + paths = ["/var/lib/basic-memory"]; + }; services.mcp-nixos.enable = true; services.kagi-mcp = { enable = true; From f79dd365fed7da4599aad7e528bf51078a2d7628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey=20=28they/them=29?= Date: Fri, 6 Mar 2026 23:49:21 -0800 Subject: [PATCH 5/7] rekey secrets --- secrets/restic-env.age | Bin 460 -> 570 bytes secrets/restic-password.age | 15 ++++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/secrets/restic-env.age b/secrets/restic-env.age index 5075587d7d6e9f3f29f35461198827efd6443ca9..9ed051bee3d004ad731422276b671834820cd024 100644 GIT binary patch delta 544 zcmX@Zyo+UmYJE{~k#>=zf=jZKlVehtp_^erUVwR+YiPbcadC!jYKoDmsiCEUkz+-AVxWRwS!KRidO><#V0}hTs&TGkqKijJdWo4=P(`qj zuV+|pS#fbrV3LoeZ;%0(xuvT`L1tcBly-PtQetk3Uvf&6S&CCmdZ|%}lfJLEeyOi^ zsi$+cWkD#qb;hpY8F_&UIgzCvp=FMl!O4O7X&GjDX;rxeg$8;41_8PCL7`@j=6PP3 zVFCFD`KBR}T;AHj&Q6Zm=3eebk$J`XmLdKT0Y=(6`l&vamKJ6uULGDEP8Ob){!uv= zT)Mit3YK9dW(N65ktr#@Szc}-UID2^zJXPaSpmK-VWy=dwM6W#Y;5T1k3}FC0;T?cxPczAVqUFH)u#?aZXlKb4gMwLvwaaR!4VcO?Y}^Ycfqy zD>g+`Z$o8fMtDRxLTO8HD^f^tX>V6fc~)$5D|dNAQ#4v;S5icEQ%rJrS1(dYQ)goe zWNU6PS8+mGOlw3sLP%CIcX4ZJd2}>HL1$)lQ&KWeQ*&iAL0WTvSw}K#Yg1Nd3Q0_C{6ETj4~{!9 zb;|O~1)A1U%g&v`38cn^A$J8*KkmOz(Wmti0ee?s4@%!ZVl4o{*lp^l?jA{whxacSKH;!@g$m-oB z@w>Y;D%LDhiD`?avPxxF@52#2El#>>Xi>r;9`S${uYjiJdjVWdK^ZjxmyiEZkwb3n H39{-hO=_s7 diff --git a/secrets/restic-password.age b/secrets/restic-password.age index 69b76026..151e7c42 100644 --- a/secrets/restic-password.age +++ b/secrets/restic-password.age @@ -1,8 +1,9 @@ age-encryption.org/v1 --> ssh-ed25519 2AxgaQ Fn3wMjiq3SNG6+zQ3UIcWvkCXR7/3dV4DFl3MiPi2XQ -pNnalVQSPAdghbNSLqFcKGNCpKTKpIivRInycB3NSeU --> ssh-ed25519 3EWhnQ 0VH0W2g6bmcT1vS8mmxGl2iLsBH8zOcEHmEx8TFX1S0 -92DM1pAuSRRJkARK7/Yv07QRqQD5KgN3yl1fYrnEaa4 ---- 5YlEepY3T7K68HHfCbtI9QdYwZRecpfOGrI26hWgEMc -4 -¦0dÐM7‹ƒµ´ÂhŒ�1œ/B¼ŽÚK½ú�ÿJ‡Õ—o’H¢gñ–R+º‘Y5�…½T›ÏlUÊ_?u \ No newline at end of file +-> ssh-ed25519 rSr+rA Z+9XxNGsbQouTkhq32ZBlkFYokGsmUCMLHeR5sP9tWk +qB/oBmbxbEFBAuSBhbpnD6eIUWTm6/c7fGiYQ/xKotI +-> ssh-ed25519 2AxgaQ qoh952wnvfuC5n+/SSsinOkCvKd54VHjpX88hOwzRwc +Be2yGLfBjsu+Cb2GBFmcXqk7G85WUGkepbY0aF3H8yI +-> ssh-ed25519 3EWhnQ cM+TTJ/BW6tb3XjbslHn+LF5wsMEcq/DY6hlwdVBGyk +Mr94UI7pJor2uQAuFP/8bBs8fcV9+LcIXV0iT5f92Hg +--- OFrrfEub5HYBAoHsGi38coK2Jw3qrDyl3sg1gIYYT3c +(–Ã>¶¥�íÊ|’Hp80k3Ô2!Ö¼DgЬ?5êÛ¨ÙÈ3òeA(¯v«,O’[dù“4@bí�¿ßŽ \ No newline at end of file From f2e2d3e6fbbc233c15e7c863eae109315a2cad07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Fri, 6 Mar 2026 23:53:35 -0800 Subject: [PATCH 6/7] refactor: rename services.restic-backup to rc.backup Co-Authored-By: Claude Opus 4.6 --- hosts/glyph/services/default.nix | 2 +- hosts/spore/backup.nix | 2 +- modules/nixos/restic-backup.nix | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hosts/glyph/services/default.nix b/hosts/glyph/services/default.nix index 98cd51e6..506b59dc 100644 --- a/hosts/glyph/services/default.nix +++ b/hosts/glyph/services/default.nix @@ -73,7 +73,7 @@ }; services.basic-memory.enable = true; - services.restic-backup = { + rc.backup = { enable = true; paths = ["/var/lib/basic-memory"]; }; diff --git a/hosts/spore/backup.nix b/hosts/spore/backup.nix index 8d30fc99..381ff47b 100644 --- a/hosts/spore/backup.nix +++ b/hosts/spore/backup.nix @@ -1,5 +1,5 @@ {config, ...}: { - services.restic-backup = { + rc.backup = { enable = true; paths = [ config.services.postgresqlBackup.location diff --git a/modules/nixos/restic-backup.nix b/modules/nixos/restic-backup.nix index a48c0d32..471eeddf 100644 --- a/modules/nixos/restic-backup.nix +++ b/modules/nixos/restic-backup.nix @@ -3,9 +3,9 @@ lib, ... }: let - cfg = config.services.restic-backup; + cfg = config.rc.backup; in { - options.services.restic-backup = { + options.rc.backup = { enable = lib.mkEnableOption "restic backup to Cloudflare R2"; paths = lib.mkOption { From 0afeeb6d7b316ca40d010a6aac4305fb9129cb50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Sat, 7 Mar 2026 00:13:48 -0800 Subject: [PATCH 7/7] feat(glyph): backup beets library and roon-server data Co-Authored-By: Claude Opus 4.6 --- hosts/glyph/default.nix | 10 ++++------ hosts/glyph/services/default.nix | 5 ++++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/hosts/glyph/default.nix b/hosts/glyph/default.nix index 1a6864eb..f5a29752 100644 --- a/hosts/glyph/default.nix +++ b/hosts/glyph/default.nix @@ -1,9 +1,4 @@ -{ - config, - lib, - pkgs, - ... -}: { +{...}: { imports = [ ./hardware.nix ./services @@ -56,5 +51,8 @@ time.timeZone = "America/Los_Angeles"; i18n.defaultLocale = "en_US.UTF-8"; + # Beets library database (beets configured in home-manager) + rc.backup.paths = ["/home/mu/.config/beets/library.db"]; + system.stateVersion = "24.05"; } diff --git a/hosts/glyph/services/default.nix b/hosts/glyph/services/default.nix index 506b59dc..1f8ea9ea 100644 --- a/hosts/glyph/services/default.nix +++ b/hosts/glyph/services/default.nix @@ -75,7 +75,10 @@ services.basic-memory.enable = true; rc.backup = { enable = true; - paths = ["/var/lib/basic-memory"]; + paths = [ + "/var/lib/basic-memory" + "/var/lib/roon-server/backup" + ]; }; services.mcp-nixos.enable = true; services.kagi-mcp = {