diff --git a/abavorana/standin.nix b/abavorana/standin.nix new file mode 100644 index 0000000..297e756 --- /dev/null +++ b/abavorana/standin.nix @@ -0,0 +1,10 @@ +{ ... }: +{ + config = { + frogeye = { + name = "abavorana"; + storageSize = "big"; + syncthing.name = "Abavorana"; + }; + }; +} diff --git a/common/disko/single_uefi_btrfs.nix b/common/disko/single_uefi_btrfs.nix index 8b91602..f9880d0 100644 --- a/common/disko/single_uefi_btrfs.nix +++ b/common/disko/single_uefi_btrfs.nix @@ -1,11 +1,11 @@ { pkgs, lib, config, ... }: let - passwordFile = "/tmp/dotfiles_${config.networking.hostName}_password"; + passwordFile = "/tmp/dotfiles_${config.frogeye.name}_password"; in { disko.devices = { disk = { - "${config.networking.hostName}" = { + "${config.frogeye.name}" = { type = "disk"; content = { type = "gpt"; @@ -27,7 +27,7 @@ in size = "100%"; content = { type = "luks"; - name = "${config.networking.hostName}"; + name = "${config.frogeye.name}"; passwordFile = passwordFile; settings = { # Not having SSDs die fast is more important than crypto diff --git a/cranberry/default.nix b/cranberry/default.nix index f8b43df..9168bb2 100644 --- a/cranberry/default.nix +++ b/cranberry/default.nix @@ -1,8 +1,8 @@ { pkgs, lib, config, ... }: { config = { - disko.devices.disk."${config.networking.hostName}".device = "/dev/disk/by-id/nvme-UMIS_RPJTJ128MEE1MWX_SS0L25188X3RC12121TP"; - networking.hostName = "cranberry"; + frogeye.name = "cranberry"; + disko.devices.disk."${config.frogeye.name}".device = "/dev/disk/by-id/nvme-UMIS_RPJTJ128MEE1MWX_SS0L25188X3RC12121TP"; }; imports = [ ../common/disko/single_uefi_btrfs.nix diff --git a/curacao/default.nix b/curacao/default.nix index e820e76..4ec5413 100644 --- a/curacao/default.nix +++ b/curacao/default.nix @@ -1,7 +1,7 @@ { pkgs, lib, config, ... }: { config = { - networking.hostName = "curacao"; + frogeye.name = "curacao"; }; imports = [ ./backup diff --git a/curacao/disko.nix b/curacao/disko.nix index e960fab..43b30b2 100644 --- a/curacao/disko.nix +++ b/curacao/disko.nix @@ -10,7 +10,7 @@ let "space_cache" "ssd" ]; - passwordFile = "/tmp/dotfiles_${config.networking.hostName}_password"; + passwordFile = "/tmp/dotfiles_${config.frogeye.name}_password"; in { disko.devices = { diff --git a/curacao/features.nix b/curacao/features.nix index 2c7daaa..2826e22 100644 --- a/curacao/features.nix +++ b/curacao/features.nix @@ -9,5 +9,6 @@ }; extra = true; gaming = true; + storageSize = "big"; }; } diff --git a/curacao/usb.nix b/curacao/usb.nix index e9b5673..92adaa3 100644 --- a/curacao/usb.nix +++ b/curacao/usb.nix @@ -2,8 +2,8 @@ { config = { boot.loader.efi.canTouchEfiVariables = false; - disko.devices.disk."${config.networking.hostName}".device = "/dev/disk/by-id/usb-Kingston_DataTraveler_3.0_E0D55EA57414F510489F0F1A-0:0"; - networking.hostName = "curacao-usb"; + disko.devices.disk."${config.frogeye.name}".device = "/dev/disk/by-id/usb-Kingston_DataTraveler_3.0_E0D55EA57414F510489F0F1A-0:0"; + frogeye.name = "curacao-usb"; }; imports = [ ../common/disko/single_uefi_btrfs.nix diff --git a/flake.nix b/flake.nix index 2dfdffb..8dab4dd 100644 --- a/flake.nix +++ b/flake.nix @@ -59,6 +59,7 @@ { nixpkgs = nixpkgsConfig; home-manager = homeManagerConfig; + frogeye.toplevel = { _type = "override"; content = self; priority = 1000; }; } ]; }; @@ -145,5 +146,15 @@ modules = [ ./cranberry ]; }; nixOnDroidConfigurations.sprinkles = lib.nixOnDroidConfiguration { }; + # Fake systems + nixosConfigurations.abavorana = lib.nixosSystem { + system = "x86_64-linux"; + modules = [ ./abavorana/standin.nix ]; + }; + nixosConfigurations.sprinkles = lib.nixosSystem { + system = "aarch64-linux"; + modules = [ ./sprinkles/standin.nix ]; + }; + # TODO devices/ or configs/ folders } // (lib.flakeTools { inherit self; }); } diff --git a/hm/desktop/default.nix b/hm/desktop/default.nix index 35d26b2..9481309 100644 --- a/hm/desktop/default.nix +++ b/hm/desktop/default.nix @@ -64,17 +64,23 @@ }; xdg = { - userDirs = { - enable = true; # TODO Which ones do we want? - createDirectories = true; - # French, because then it there's a different initial for each, making navigation easier - desktop = null; - download = "${config.home.homeDirectory}/Téléchargements"; - pictures = "${config.home.homeDirectory}/Images"; - publicShare = null; - templates = null; - videos = "${config.home.homeDirectory}/Vidéos"; - }; + userDirs = + let + wellKnownUserDirs = [ "desktop" "documents" "download" "music" "pictures" "publicShare" "templates" "videos" ]; + wellKnownUserDirsNulled = builtins.listToAttrs (builtins.map (name: { inherit name; value = null; }) wellKnownUserDirs); + allFolders = builtins.attrValues config.frogeye.folders; + folders = builtins.filter (folder: folder.xdgUserDirVariable != null && folder.user == config.home.username) allFolders; + in + { + enable = true; + createDirectories = true; + extraConfig = builtins.listToAttrs (builtins.map + (folder: { + name = folder.xdgUserDirVariable; + value = "${config.home.homeDirectory}/${folder.path}"; + }) + folders); + } // wellKnownUserDirsNulled; # Don't use defaults dirs }; services = { blueman-applet.enable = true; diff --git a/hm/desktop/frobar/frobar/updaters.py b/hm/desktop/frobar/frobar/updaters.py index 1afc016..9f55fe6 100644 --- a/hm/desktop/frobar/frobar/updaters.py +++ b/hm/desktop/frobar/frobar/updaters.py @@ -131,11 +131,6 @@ class PeriodicUpdater(Updater): class InotifyUpdaterEventHandler(pyinotify.ProcessEvent): def process_default(self, event: pyinotify.Event) -> None: - # DEBUG - # from pprint import pprint - # pprint(event.__dict__) - # return - assert event.path in InotifyUpdater.paths if 0 in InotifyUpdater.paths[event.path]: diff --git a/hm/desktop/mpd/default.nix b/hm/desktop/mpd/default.nix index bea9ceb..1de7ed8 100644 --- a/hm/desktop/mpd/default.nix +++ b/hm/desktop/mpd/default.nix @@ -20,6 +20,7 @@ extraConfig = '' restore_paused "yes" ''; + musicDirectory = "${config.home.homeDirectory}/Musiques"; }; xdg = { configFile = { @@ -36,7 +37,6 @@ ''; }; }; - userDirs.music = "${config.home.homeDirectory}/Musiques"; }; xsession.windowManager.i3.config.keybindings = { diff --git a/hm/dev/common.nix b/hm/dev/common.nix index 7d66e10..fa4bd43 100644 --- a/hm/dev/common.nix +++ b/hm/dev/common.nix @@ -27,6 +27,7 @@ ] ++ lib.optionals config.frogeye.desktop.xorg [ # Common zeal-qt6 # Offline documentation + sqlitebrowser # Network wireshark-qt diff --git a/hm/extra.nix b/hm/extra.nix index d793f0e..3f8ba54 100644 --- a/hm/extra.nix +++ b/hm/extra.nix @@ -61,8 +61,5 @@ # https://hydra.nixos.org/job/nixos/release-23.11/nixpkgs.blender.aarch64-linux blender ]); - services = { - syncthing.enable = true; - }; }; } diff --git a/options.nix b/options.nix index a9c16e6..20f917b 100644 --- a/options.nix +++ b/options.nix @@ -1,6 +1,17 @@ -{ lib, config, ... }: +{ pkgs, lib, config, ... }: +let + capitalizeFirstLetter = str: (lib.strings.toUpper (builtins.substring 0 1 str)) + (builtins.substring 1 (builtins.stringLength str) str); +in { options.frogeye = { + name = lib.mkOption { + description = "Name of the system"; + type = lib.types.str; + }; + toplevel = lib.mkOption { + description = "top-level flake"; + type = lib.types.attrs; + }; extra = lib.mkEnableOption "Big software"; gaming = lib.mkEnableOption "Games"; polarity = lib.mkOption { @@ -40,11 +51,98 @@ prose = lib.mkEnableOption "Writing stuff"; python = lib.mkEnableOption "Python dev stuff"; }; + storageSize = lib.mkOption { + default = "small"; + type = lib.types.enum [ "small" "big" "phone" ]; + description = "Type of storage for the device. Used to determine which folder to include."; + }; + folders = lib.mkOption { + default = { }; + description = "Folders used by users"; + # Top-level so Syncthing can work for all users. Also there's no real home-manager syncthing module. + type = lib.types.attrsOf (lib.types.submodule ({ config, name, ... }: { + options = { + name = lib.mkOption { + type = lib.types.str; + default = name; + readOnly = true; + internal = true; + description = "Name accessing this entry"; + }; + enable = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to use this folder."; + }; + path = lib.mkOption { + type = lib.types.str; + description = "Path relative to HOME. Prefer French names which has more varied initials, easing navigation."; + }; + label = lib.mkOption { + type = lib.types.str; + description = "Friendly name"; + default = capitalizeFirstLetter config.path; + }; + user = lib.mkOption { + type = lib.types.str; + default = "geoffrey"; + description = "User using this directory."; + }; + fullPath = lib.mkOption { + type = lib.types.str; + default = "/home/${config.user}/${config.path}"; + # Hardcoded due to outside of any user context + description = "Absolute path."; + }; + xdgUserDirVariable = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = "XDG_${lib.strings.toUpper name}_DIR"; + description = "Name for the XDG user dir variable. If set, will automatically create the directory."; + }; + syncthing = lib.mkOption { + default = { }; + description = "Syncthing configuration for the folder"; + type = lib.types.submodule ({ ... }: { + freeformType = (pkgs.formats.json { }).type; + options = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.enable && config.syncthing.id != null; + description = "Whether to sync."; + }; + id = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "Syncthing folder id"; + }; + }; + }); + }; + versionsMaxDays = lib.mkOption { + type = lib.types.nullOr lib.types.int; + default = 365; + description = "For big systems, how many days of staggered versions should be kept"; + }; + }; + })); + }; hooks.lock = lib.mkOption { type = lib.types.lines; default = ""; description = "Bash commands to execute on locking the session."; }; + syncthing = { + id = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "Full syncthing id"; + }; + name = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = "Geoffrey ${capitalizeFirstLetter config.frogeye.name}"; + description = "Syncthing name"; + }; + }; }; config = { @@ -53,6 +151,7 @@ prose = lib.mkDefault true; python = lib.mkDefault true; }; + folders.download.path = "Téléchargements"; }; }; } diff --git a/os/common.nix b/os/common.nix index cf68386..713484f 100644 --- a/os/common.nix +++ b/os/common.nix @@ -1,6 +1,9 @@ -{ pkgs, lib, ... }: +{ pkgs, lib, config, ... }: { - networking.domain = "geoffrey.frogeye.fr"; + networking = { + hostName = config.frogeye.name; + domain = "geoffrey.frogeye.fr"; + }; time.timeZone = "Europe/Amsterdam"; diff --git a/os/rebuild.sh b/os/rebuild.sh index 600557f..d0cee42 100644 --- a/os/rebuild.sh +++ b/os/rebuild.sh @@ -32,6 +32,7 @@ do done <<< "$(ls /nix/var/nix/profiles/system/specialisation)" # Apply +confirm="n" if [ "$verb" = "confirm" ] then echo "Apply configuration? [y/N]" diff --git a/os/syncthing/default.nix b/os/syncthing/default.nix index dcff744..b602718 100644 --- a/os/syncthing/default.nix +++ b/os/syncthing/default.nix @@ -4,16 +4,28 @@ let service = "syncthing"; secretsDir = "/etc/secrets/${service}"; password = { - path = "syncthing/${config.networking.hostName}"; + path = "syncthing/${config.frogeye.name}"; selector = "@"; generator = ''(t="$(mktemp -d)" && ${lib.getExe pkgs.syncthing} generate --home="$t" &> /dev/null && cat "$t"/{cert,key}.pem && rm -rf "$t")''; }; + + capitalizeFirstLetter = str: (lib.strings.toUpper (builtins.substring 0 1 str)) + (builtins.substring 1 (builtins.stringLength str) str); + + nixosDevices = builtins.map (system: system.config.frogeye) (builtins.attrValues config.frogeye.toplevel.nixosConfigurations); + allDevices = nixosDevices; + syncingDevices = builtins.filter (device: device.syncthing.id != null) allDevices; + peerDevices = builtins.filter (device: device.name != config.frogeye.name) syncingDevices; + + # Can't use the module's folders enable option, as it still requests things somehow + allFolders = builtins.attrValues config.frogeye.folders; + syncedFolders = builtins.filter (folder: folder.syncthing.enable) allFolders; + + folderShouldSyncWith = folder: device: (lib.hasAttrByPath [ folder.name ] device.folders) && device.folders.${folder.name}.syncthing.enable; + folderDeviceEntry = folder: device: { deviceID = device.syncthing.id; }; in { config = lib.mkIf cfg.enable { - services.${service} = { - guiAddress = "127.0.0.1:8385"; # DEBUG - + services.${service} = { openDefaultPorts = true; configDir = "/var/lib/${service}"; databaseDir = "/var/cache/${service}"; @@ -21,14 +33,46 @@ in key = "${secretsDir}/key.pem"; cert = "${secretsDir}/cert.pem"; + + settings = { + devices = builtins.listToAttrs (builtins.map (device: { inherit (device) name; value = device.syncthing; }) syncingDevices); + folders = builtins.listToAttrs (builtins.map + (folder: { + inherit (folder) name; + value = + { + label = "${capitalizeFirstLetter folder.user} ${folder.label}"; + path = "${config.users.users.${folder.user}.home}/${folder.path}"; + # Despite further in the code indicating this is possible, it is, actually not + # devices = builtins.map (folderDeviceEntry folder) (builtins.filter (folderShouldSyncWith folder) peerDevices); + devices = builtins.map (device: device.name) (builtins.filter (folderShouldSyncWith folder) peerDevices); + versioning = + if (config.frogeye.storageSize == "big" && folder.versionsMaxDays != null) then { + type = "staggered"; + params.maxAge = builtins.toString (folder.versionsMaxDays * 24 * 3600); + # TODO Increase cleanupIntervalS to 1 day or so + } else null; + rescanIntervalS = 10 * 3600; # Using watcher, should be good enough + copyRangeMethod = "all"; # Prevents duplication + copyOwnershipFromParent = true; + } // folder.syncthing; + }) + syncedFolders); + options = rec { + urAccepted = 3; + urSeen = urAccepted; + }; + }; }; - systemd.services.${service} = { - serviceConfig.ExecStartPre = [ + systemd.services.${service}.serviceConfig = { + ExecStartPre = [ "+${pkgs.writeShellScript "syncthing-create-folders" '' - install -Dm700 -o ${cfg.user} -g ${cfg.group} -d ${cfg.configDir} - install -Dm700 -o ${cfg.user} -g ${cfg.group} -d ${cfg.databaseDir} - ''}" + install -Dm700 -o ${cfg.user} -g ${cfg.group} -d ${cfg.configDir} + install -Dm700 -o ${cfg.user} -g ${cfg.group} -d ${cfg.databaseDir} + ''}" ]; + PrivateUsers = lib.mkForce false; + AmbientCapabilities = ["CAP_CHOWN" "CAP_DAC_OVERRIDE" "CAP_FOWNER"]; }; vivarium.passwordFiles = { ${cfg.key}.password = password // { diff --git a/pindakaas/default.nix b/pindakaas/default.nix index 5177728..0317aba 100644 --- a/pindakaas/default.nix +++ b/pindakaas/default.nix @@ -1,8 +1,8 @@ { pkgs, lib, config, ... }: { config = { - disko.devices.disk."${config.networking.hostName}".device = "/dev/disk/by-id/mmc-DA4064_0x931f080f"; - networking.hostName = "pindakaas"; + disko.devices.disk."${config.frogeye.name}".device = "/dev/disk/by-id/mmc-DA4064_0x931f080f"; + frogeye.name = "pindakaas"; }; imports = [ ../common/disko/single_uefi_btrfs.nix diff --git a/pindakaas/sd.nix b/pindakaas/sd.nix index 8a09d34..8fd90b1 100644 --- a/pindakaas/sd.nix +++ b/pindakaas/sd.nix @@ -2,8 +2,8 @@ { config = { boot.loader.efi.canTouchEfiVariables = false; - disko.devices.disk."${config.networking.hostName}".device = "/dev/disk/by-id/mmc-SN32G_0xfb19ae99"; - networking.hostName = "pindakaas-sd"; + disko.devices.disk."${config.frogeye.name}".device = "/dev/disk/by-id/mmc-SN32G_0xfb19ae99"; + frogeye.name = "pindakaas-sd"; }; imports = [ ../common/disko/single_uefi_btrfs.nix diff --git a/sprinkles/standin.nix b/sprinkles/standin.nix new file mode 100644 index 0000000..7d2dab9 --- /dev/null +++ b/sprinkles/standin.nix @@ -0,0 +1,9 @@ +{ ... }: +{ + config = { + frogeye = { + name = "sprinkles"; + storageSize = "phone"; + }; + }; +}