Skip to content

moritzschaefer/nixos-config

 
 

Repository files navigation

This is a fork! What comes below was only partially written by myself and it is super awesome. I followed this blog post to adapt the config-repo to my needs: https://gist.github.com/domenkozar/b3c945035af53fa816e0ac460f1df853

There are still a lot of manual steps involved:

  1. install basic nixos (see gist above)
  2. add flakes support, change to desired hostname, add moritz user
  3. install this nixos-config (pull moritzschaefer/nixos-config, run nixos-rebuild switch)
  4. Install spacemacs (clone git-spacemacs into ~/.emacs.d as described in the installation instructions of spacemacs README)
  5. connect syncthing
  6. sync wiki and .ssh keys (possibly via usb stick, what else?)
  7. get encrypted private key from secrets: https://snow-dev.com/posts/setup-pass-on-multiple-devices.html
  8. clone my passwords repo to have my pass-logins, transfer gpg-keys (https://www.hjdskes.nl/blog/pass/)
  9. clone my dotfiles and link them using homesick

Hi there! That’s my dotfiles. Most of config files are now generated by org-babel fromonfig files just in case I won’t have access to my Emacs. However, I recommend against looking at them—they’re just a generated mess; you’ll have much better time reading this doc instead—trust me.

Pieces not (yet) covered in this document are:

  • emacs configuration at .emacs.d/;
  • vim configuration at .vimrc and .vim/;
  • awesome wm configuration at .config/awesome/;
  • scripts at bin/;
  • irssi config at .irssi;

NixOS

I’m a NixOS user. What’s cool about it is that I can describe all my system configuration in one file (almost). I can execute a single command and have a system with the same software, system settings, etc.

An outline of configuration looks like this:

{ name, config, pkgs, lib, inputs, ... }:
let
  machine-config = lib.getAttr name {
    moxps = [
      <<machine-moxps>>
    ];
    mobook = [
      <<machine-mobook>>
    ];
    mopad = [
      <<machine-mopad>>
    ];
    moair = [
      <<machine-moair>>
    ];
  };
  # nur-no-pkgs = import (builtins.fetchTarball {
  #   url = "https://github.com/nix-community/NUR/archive/master.tar.gz";
  #   sha256 = "10dq8abmw30lrpwfg7yb1zn6fb5d2q94yhsvg6dwcknn46nilbxs";
  # }) {
  #     nurpkgs = pkgs;
  #     inherit pkgs;
  #     repoOverrides = {
  #       moritzschaefer = import /home/moritz/Projects/nur-packages;
  #     };
  #   };
in
{
  # disabledModules = [ "services/printing/cupsd.nix" ]; 
  imports = [
    # (import "${inputs.nixpkgs-local}/nixos/modules/services/printing/cupsd.nix")
    {
    }

    <<nixos-section>>
  ] ++ machine-config;
}

This <<nixos-section>> is replaced by other parts of this doc.

Flakes support

Enable experimental Flakes support.

{
  nix = {
    package = pkgs.nixVersions.stable;
    extraOptions = ''
      experimental-features = nix-command flakes
    '';
  };
}

Make this repository flake-compatible:

{
  description = "Moritz's NixOS";

  # edition = 201909;

  inputs = {
    nixpkgs = {
      type = "github";
      owner = "NixOS";
      repo = "nixpkgs";
      ref = "nixos-24.11";
    };
    nixpkgs-unstable = {
      type = "github";
      owner = "NixOS";
      repo = "nixpkgs";
      ref = "master";
    };
    nixpkgs-moritz = {
      type = "github";
      # owner = "rasendubi";
      # repo = "nixpkgs";
      # ref = "melpa-2020-04-27";
      owner = "moritzschaefer";
      # repo = "nixpkgs-channels";
      repo = "nixpkgs";
      # rev = "246294708d4b4d0f7a9b63fb3b6866860ed78704";
      # ref = "nixpkgs-unstable";
      ref = "fix-libnvidia-container";
    };
    # nixpkgs-local = {
    #   url = "/home/moritz/Projects/nixpkgs/";
    # };

    nixos-hardware = {
      type = "github";
      owner = "NixOS";
      repo = "nixos-hardware";
    };
    nur = {
      url = github:nix-community/NUR;
    };
    agenix.url = "github:ryantm/agenix";
    apple-silicon = {
      url = "github:tpwrules/nixos-apple-silicon";

      # this line prevents fetching two versions of nixpkgs:
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };
  
# nixpkgs-local
  outputs = { self, nixpkgs, nixpkgs-moritz, nixpkgs-unstable, nixos-hardware, nur, agenix, apple-silicon }@inputs:
    let
      system-wrapper = name: if name == "moair" then "aarch64-linux" else "x86_64-linux";
      pkgs-wrapper = name: import nixpkgs {
        system = system-wrapper name;
        overlays = self.overlays;
        config = { allowUnfree = true;
                    allowBroken = true;
                    nvidia.acceptLicense = true;
                    permittedInsecurePackages = [
                      "adobe-reader-9.5.5"
                      "python3.11-youtube-dl-2021.12.17"
                      "qtwebkit-5.212.0-alpha4"
                      "openjdk-18+36"
                      "python-2.7.18.6"
                    ];
                    };
      };
    in {
      nixosConfigurations =
        let
          hosts = ["moxps" "mobook" "mopad" "moair"];
          mkHost = name:
            nixpkgs.lib.nixosSystem {
              system = system-wrapper name;
              modules = [
                { nixpkgs = { pkgs = (pkgs-wrapper name);  }; }
                (import ./nixos-config.nix)
                { nixpkgs.overlays = [ nur.overlay ]; }
                agenix.nixosModules.default
                {
                  environment.systemPackages = [ agenix.packages.${system-wrapper name}.default ];
                  age.identityPaths = [ "/home/moritz/.ssh/id_ed25519_agenix" ];
                }
              ];
              specialArgs = { inherit name inputs; };
            };
        in nixpkgs.lib.genAttrs hosts mkHost;

      # redundant
      packages.x86_64-linux =
        let
          mergePackages = nixpkgs.lib.foldr nixpkgs.lib.mergeAttrs {};
        in
          mergePackages [
            <<flake-packages>>
          ];
      # redundant
      packages.aarch64-linux =
        let
          mergePackages = nixpkgs.lib.foldr nixpkgs.lib.mergeAttrs {};
        in
          mergePackages [
            <<flake-packages>>
          ];

      overlays = [
        # (_self: _super: builtins.getAttr _super.system self.packages)  # this led to "infinite recursion" but it was actually not needed (weird!)
        <<flake-overlays>>
      ];

        # in nixpkgs.lib.genAttrs hosts mkHost;
    };
}
(final: prev: {
  unstable = import inputs.nixpkgs-unstable {
    system = prev.system;
    overlays = self.overlays; # .${system};
    config = { allowUnfree = true;  allowBroken = true; nvidia.acceptLicense = true; };
  };
  
 
  # mkNvidiaContainerPkg = { name, containerRuntimePath, configTemplate, additionalPaths ? [] }:
  #   let
  #     nvidia-container-runtime = pkgs.callPackage "${inputs.nixpkgs}/pkgs/applications/virtualization/nvidia-container-runtime" {
  #       inherit containerRuntimePath configTemplate;
  #     };
  #   in pkgs.symlinkJoin {
  #     inherit name;
  #     paths = [
  #       # (callPackage ../applications/virtualization/libnvidia-container { })
  #       (pkgs.callPackage "${inputs.nixpkgs-moritz}/pkgs/applications/virtualization/libnvidia-container" { inherit (pkgs.linuxPackages) nvidia_x11; })
  #       nvidia-container-runtime
  #       (pkgs.callPackage "${inputs.nixpkgs}/pkgs/applications/virtualization/nvidia-container-toolkit" {
  #         inherit nvidia-container-runtime;
  #       })
  #     ] ++ additionalPaths;
  #   };
  
  # nvidia-docker = pkgs.mkNvidiaContainerPkg {
  #   name = "nvidia-docker";
  #   containerRuntimePath = "${pkgs.docker}/libexec/docker/runc";
  #   # configTemplate = "${inputs.nixpkgs}/pkgs/applications/virtualization/nvidia-docker/config.toml";
  #   configTemplate = builtins.toFile "config.toml" ''
  #   disable-require = false
  #   #swarm-resource = "DOCKER_RESOURCE_GPU"

  #   [nvidia-container-cli]
  #   #root = "/run/nvidia/driver"
  #   #path = "/usr/bin/nvidia-container-cli"
  #   environment = []
  #   debug = "/var/log/nvidia-container-runtime-hook.log"
  #   ldcache = "/tmp/ld.so.cache"
  #   load-kmods = true
  #   #no-cgroups = false
  #   #user = "root:video"
  #   ldconfig = "@@glibcbin@/bin/ldconfig"
  #   '';
  #   additionalPaths = [ (pkgs.callPackage "${inputs.nixpkgs}/pkgs/applications/virtualization/nvidia-docker" { }) ];
  # };
  # mesa-pin = import inputs.mesa-pin {
  #   inherit system;
  #   overlays = self.overlays; # .${system};
  #   config = { allowUnfree = true; };
  # };
})

NixOS Tools

{
  environment.systemPackages = [ pkgs.nixos-option ];
}

Caching

{
  nix = {
    settings = {
      substituters = [
        "https://nix-community.cachix.org"
        "https://cache.nixos.org/"
      ];
      trusted-public-keys = [
        "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
      ];
    };
  };
}

Make nixpkgs available in NIX_PATH

{
nix.nixPath = [
    "nixpkgs=${inputs.nixpkgs}"
  ];
}

Users

I’m the only user of the system:

{
  users.users.moritz = {
    isNormalUser = true;
    uid = 1000;
    extraGroups = [ "users" "wheel" "input" ];
    initialPassword = "HelloWorld";
  };
}

initialPassword is used only first time when user is created. It must be changed as soon as possible with passwd.

Machines

I currently have only one machine.

moxps

This is my Dell XPS 15. Only use Intel OR Nvidia

Hardware

{
  imports = [
    (import "${inputs.nixos-hardware}/common/cpu/intel")
    (import "${inputs.nixos-hardware}/common/cpu/intel/kaby-lake")
    (import "${inputs.nixos-hardware}/common/pc/laptop")  # tlp.enable = true
    (import "${inputs.nixos-hardware}/common/pc/laptop/acpi_call.nix")  # tlp.enable = true
    (import "${inputs.nixos-hardware}/common/pc/laptop/ssd")
    inputs.nixpkgs.nixosModules.notDetected
  ];

  # from nixos-hardware
  boot.loader.systemd-boot.enable = true;
  boot.loader.systemd-boot.configurationLimit = 10;
  boot.loader.efi.canTouchEfiVariables = false;  # disabled after a boot or two to prevent usage on that kind of ram
  services.thermald.enable = true; 

  # from initial config and other webresources
  boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
  boot.kernelModules = [ "kvm-intel" ];
  boot.kernelParams = [ "acpi_rev_override=5" "i915.enable_guc=2" "pcie_aspm=off" ];  # "nouveau.modeset=0" ];  # 5,6,1 doesn't seem to make a difference. pcie_aspm=off might be required to avoid freezes
  
  # OpenGL accelerateion
  # nixpkgs.config.packageOverrides = pkgs: {
  #   vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; };
  # };
  # hardware.opengl = {
  #   enable = true;
  #   driSupport = true;
  #   extraPackages = with pkgs; [
  #     intel-media-driver # LIBVA_DRIVER_NAME=iHD <- works for VLC
  #     vaapiIntel         # LIBVA_DRIVER_NAME=i965 (older but works better for Firefox/Chromium)
  #     vaapiVdpau
  #     libvdpau-va-gl
  #   ];
  # };

  nix.settings.max-jobs = lib.mkDefault 8;

  services.undervolt = {
    enable = false;  # disabled because it doesn't work anymore after BIOS upgrade
    # coreOffset = 0;
    # gpuOffset = 0;
    coreOffset = -125;
    gpuOffset = -75;
  };
  powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
  powerManagement.enable = true;

  # The NixOS release to be compatible with for stateful data such as databases.
  system.stateVersion = "24.05";
}
Intel only
{
  system.nixos.tags = [ "with-intel" ];
  services.xserver.videoDrivers = [ "intel" ];  # modesetting didn't help
  hardware.nvidiaOptimus.disable = true;
  boot.blacklistedKernelModules = [ "nouveau" "nvidia" ];  # bbswitch
  
  # https://github.com/NixOS/nixpkgs/issues/94315 <- from here. bugfix for this: https://discourse.nixos.org/t/update-to-21-05-breaks-opengl-because-of-dependency-on-glibc-2-31/14218 note, that there are multiple occurences of this
  # hardware.opengl.package = pkgs.nixpkgs-2009.mesa_drivers;
  services.xserver = {
    enable = false;
    displayManager = {
      lightdm.enable = false;
      gdm.enable = false;
    };
  };
}
Nvidia PRIME
{
  system.nixos.tags = [ "with-nvidia" ];
  # environment.systemPackages = let
  #   nvidia-offload = pkgs.writeShellScriptBin "nvidia-offload" ''
  #     export __NV_PRIME_RENDER_OFFLOAD=1
  #     export __NV_PRIME_RENDER_OFFLOAD_PROVIDER=NVIDIA-G0
  #     export __GLX_VENDOR_LIBRARY_NAME=nvidia
  #     export __VK_LAYER_NV_optimus=NVIDIA_only
  #     exec -a "$0" "$@"
  #   '';
  # in [ nvidia-offload ]; 
  # boot.extraModulePackages = [ pkgs.linuxPackages.nvidia_x11 ];
  # Nvidia stuff (https://discourse.nixos.org/t/how-to-use-nvidia-prime-offload-to-run-the-x-server-on-the-integrated-board/9091/13)
  boot.extraModprobeConfig = "options nvidia \"NVreg_DynamicPowerManagement=0x02\"\n";
  services.hardware.bolt.enable = true;
  services.udev.extraRules = ''
    # Remove NVIDIA USB xHCI Host Controller devices, if present
    ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c0330", ATTR{remove}="1"

    # Remove NVIDIA USB Type-C UCSI devices, if present
    ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c8000", ATTR{remove}="1"

    # Remove NVIDIA Audio devices, if present
    ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x040300", ATTR{remove}="1"

    # Enable runtime PM for NVIDIA VGA/3D controller devices on driver bind
    ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto"
    ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto"

    # Disable runtime PM for NVIDIA VGA/3D controller devices on driver unbind
    ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="on"
    ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="on"
    '';
  services.xserver.videoDrivers = [ "nvidia" ];

  hardware.nvidia.modesetting.enable = lib.mkDefault true;
  hardware.nvidia.optimus_prime.enable = lib.mkDefault true;  # warning: The option `hardware.nvidia.optimus_prime.enable' defined in `<unknown-file>' has been renamed to `hardware.nvidia.prime.sync.enable'.
  hardware.nvidia.prime.nvidiaBusId = lib.mkDefault "PCI:1:0:0";
  hardware.nvidia.prime.intelBusId = lib.mkDefault "PCI:0:2:0";

  # hardware.bumblebee.enable = false;
  # hardware.bumblebee.pmMethod = "none";
  services.xserver = {
    displayManager = {
      lightdm.enable = true;
      gdm.enable = false;
    };
  };
}
Nvidia eGPU

This strongly mimics /home/moritz/Projects/nixpkgs/nixos/modules/hardware/video/nvidia.nix

TODO try with xserver (nvidia displayer driver) and with datacenter. both with open driver. I need to get a working version before I go “wild”

xserver
{
  services.xserver.videoDrivers = [ "nvidia" ];
  hardware.nvidia.prime.nvidiaBusId = lib.mkDefault "PCI:1:0:0";
  hardware.nvidia.prime.intelBusId = lib.mkDefault "PCI:0:2:0";
  hardware.nvidia.prime.offload.enable = true;

  services.xserver.enable = true;
  services.hardware.bolt.enable = true;
  hardware.nvidia.open = true;  # required for eGPU maybe?

  # config.boot.kernelPackages.nvidiaPackages
  hardware.nvidia.package = config.boot.kernelPackages.nvidiaPackages.production;
  # "pkgs.os-specific.linux.nvidia_x11.production";  # alternative: stable

  boot.blacklistedKernelModules = [ "nouveau" ];  # bbswitch
  hardware.nvidia.nvidiaPersistenced = true;  # powerdown crashes the eGPU

  hardware.opengl.enable = true;  # needed for nvidia-docker
  services.getty.autologinUser = "moritz";

  hardware.nvidia.powerManagement.enable = false;

  services.udev.extraRules = ''
    # Remove NVIDIA USB xHCI Host Controller devices, if present
    ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c0330", ATTR{remove}="1"

    # Remove NVIDIA USB Type-C UCSI devices, if present
    ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c8000", ATTR{remove}="1"

    # Remove NVIDIA Audio devices, if present
    ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x040300", ATTR{remove}="1"

    # Enable runtime PM for NVIDIA VGA/3D controller devices on driver bind
    ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto"
    ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto"

    # Disable runtime PM for NVIDIA VGA/3D controller devices on driver unbind
    ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="on"
    ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="on"
    '';
}
datacenter
{
  system.nixos.tags = [ "no-xserver-datacenter" ];

  boot.extraModulePackages = with config.boot.kernelPackages; [ acpi_call bbswitch ];

  # https://github.com/NixOS/nixpkgs/issues/94315 <- from here. bugfix for this: https://discourse.nixos.org/t/update-to-21-05-breaks-opengl-because-of-dependency-on-glibc-2-31/14218 note, that there are multiple occurences of this
  # hardware.opengl.package = pkgs.nixpkgs-2009.mesa_drivers;
  services.xserver.enable = false;
  hardware.nvidia.datacenter.enable = true;
  services.hardware.bolt.enable = true;
  hardware.nvidia.open = true;  # required for eGPU

  hardware.nvidia.package = (pkgs.unstable.linuxPackagesFor config.boot.kernelPackages.kernel).nvidiaPackages.dc_535;

  # "pkgs.os-specific.linux.nvidia_x11.production";  # alternative: stable

  boot.blacklistedKernelModules = [ "nouveau" ];  # bbswitch
  hardware.nvidia.nvidiaPersistenced = true;  # disconnect crashes

  hardware.opengl.enable = true;  # needed for nvidia-docker
  services.getty.autologinUser = "moritz";

  hardware.nvidia.powerManagement.enable = false;
}
manual
{
  system.nixos.tags = [ "with-nvidia-egpu" ];
  # environment.systemPackages = let
  #   nvidia-offload = pkgs.writeShellScriptBin "nvidia-offload" ''
  #     export __NV_PRIME_RENDER_OFFLOAD=1
  #     export __NV_PRIME_RENDER_OFFLOAD_PROVIDER=NVIDIA-G0
  #     export __GLX_VENDOR_LIBRARY_NAME=nvidia
  #     export __VK_LAYER_NV_optimus=NVIDIA_only
  #     exec -a "$0" "$@"
  #   '';
  # in [ nvidia-offload ]; 
  # Nvidia stuff (https://discourse.nixos.org/t/how-to-use-nvidia-prime-offload-to-run-the-x-server-on-the-integrated-board/9091/13)
  # boot.extraModprobeConfig = "options nvidia \"NVreg_DynamicPowerManagement=0x02\"\n";
  services.hardware.bolt.enable = true;

  # systemd.tmpfiles.rules =
  #   lib.optional config.virtualisation.docker.enableNvidia
  #     "L+ /run/nvidia-docker/bin - - - - ${nvidia_x11.bin}/origBin";

  services.udev.extraRules = ''
    # Remove NVIDIA USB xHCI Host Controller devices, if present
    ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c0330", ATTR{remove}="1"

    # Remove NVIDIA USB Type-C UCSI devices, if present
    ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c8000", ATTR{remove}="1"

    # Remove NVIDIA Audio devices, if present
    ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x040300", ATTR{remove}="1"

    # Enable runtime PM for NVIDIA VGA/3D controller devices on driver bind
    ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto"
    ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto"

    # Disable runtime PM for NVIDIA VGA/3D controller devices on driver unbind
    ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="on"
    ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="on"


    # Create /dev/nvidia-uvm when the nvidia-uvm module is loaded.
    KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidiactl c 195 255'"
    KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'for i in $$(cat /proc/driver/nvidia/gpus/*/information | grep Minor | cut -d \  -f 4); do mknod -m 666 /dev/nvidia$${i} c 195 $${i}; done'"
    KERNEL=="nvidia_modeset", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-modeset c 195 254'"
    KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm c $$(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 0'"
    KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm-tools c $$(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 1'"
    '';
  # hardware.opengl.package = pkgs.nixpkgs-2009.mesa_drivers;
  services.xserver.videoDrivers = [ "intel" ];
  boot.extraModulePackages = [ pkgs.linuxPackages.nvidia_x11.open ];  # .open added

  boot.blacklistedKernelModules = [ "nouveau" "nvidia_drm" "nvidia_modeset" "nvidia" "nvidiafb" ];
  boot.extraModprobeConfig = ''
    softdep nvidia post: nvidia-uvm
  '';
  environment.systemPackages = [ pkgs.linuxPackages.nvidia_x11.bin ]; # packages # .bin added
  
  hardware.firmware = [ pkgs.linuxPackages.nvidia_x11.firmware ];
  
  boot.kernelParams = [ "nvidia.NVreg_OpenRmEnableUnsupportedGpus=1" ];
  
  # hardware.nvidia.package = pkgs.os-specific.linux.nvidia_x11.production;  # alternative: stable 
  # /home/moritz/Projects/nixpkgs/pkgs/os-specific/linux/nvidia-x11/default.nix <- add version 450
  hardware.nvidia.open = true;
  # hardware.nvidia.datacenter.enable = true;
  hardware.opengl = {
    enable = true;
    driSupport = true;
    extraPackages = with pkgs; [
      # intel-media-driver # LIBVA_DRIVER_NAME=iHD
      # vaapiIntel         # LIBVA_DRIVER_NAME=i965 (older but works better for Firefox/Chromium)
      # vaapiVdpau
      # libvdpau-va-gl
      pkgs.linuxPackages.nvidia_x11.out  # required for nvidia-docker
    ];
    extraPackages32 = [ pkgs.linuxPackages.nvidia_x11.lib32 ];
  };

  services.xserver = {
    displayManager = {
      lightdm.enable = false;
      gdm.enable = true;
    };
  };

}

LVM on LUKS setup for disk encryption.

{
  fileSystems."/" =
    { device = "/dev/disk/by-uuid/8f0a4152-e9f1-4315-8c34-0402ff7efff4";
      fsType = "btrfs";
    };

  fileSystems."/boot" =
    { device = "/dev/disk/by-uuid/A227-1A0D";
      fsType = "vfat";
    };

  swapDevices =
    [
      { device = "/dev/disk/by-uuid/9eca5b06-730e-439f-997b-512a614ccce0"; }
      { device = "/swapfile"; } # size = 48 * 1024 (48G)
    ];

  boot.initrd.kernelModules = [ "mmc_core" ];  # TODO try with USB stick first! https://medium.com/@geis/using-a-raw-usb-device-to-unlock-a-luks-volume-on-nixos-193406ee7474
  boot.initrd.systemd.enable = true;

  boot.initrd.luks.devices = {
    cryptkey.device = "/dev/disk/by-uuid/ccd19ab7-0e4d-4df4-8912-b87139de56af";
    anopassphrasekey = {
         device = "/dev/disk/by-id/mmc-SD02G_0x6035b72d";  # TODO try without
         allowDiscards = true;
         keyFileSize = 4096;
         keyFile = "/dev/mmcblk0";
    };
    cryptroot = {
      device="/dev/disk/by-uuid/88242cfe-48a1-44d2-a29b-b55e6f05d3d3";
      keyFile="/dev/mapper/cryptkey";
    };
    cryptswap = {
      device="/dev/disk/by-uuid/f6fa3573-44a9-41cc-bab7-da60d21e27b3";
      keyFile="/dev/mapper/cryptkey";
    };
  };
}

External hard-drives

{
# 3.5" HDD in fast-swappable case
  fileSystems."/mnt/hdd3tb" =
    { device = "/dev/disk/by-uuid/f6037d88-f54a-4632-bd9f-a296486fc9bc";
      fsType = "ext4";
      options = [ "nofail" ];
    };
# 2.5" SSD ugreen
  fileSystems."/mnt/ssd2tb" =
    { device = "/dev/disk/by-uuid/44d8f482-0ab4-4184-8941-1cf3969c298c";
      fsType = "ext4";
      options = [ "nofail" ];
    };
}

Clickpad and DPI:

{
  services.libinput = {
    enable = true;
    touchpad.accelSpeed = "0.7";
  };
  services.xserver.displayManager.lightdm.greeters.gtk.cursorTheme = {
    name = "Vanilla-DMZ";
    package = pkgs.vanilla-dmz;
    size = 128; # was 64
  };
  environment.variables.XCURSOR_SIZE = "64";
}

Prevent sleeping on lid close

{
  services.upower.ignoreLid = true;
  services.logind = {
    lidSwitchExternalPower = "ignore";
  };
}

TODO switch off/on display on lid close

Initialization script

{
  boot.kernelModules = [ "acpi_call" "bbswitch" ];

  systemd.services.server_init = {
    description = "";

    wantedBy = [ "multi-user.target" ];
    after = [ "docker.service" ]; # Ensure server_init starts after Docker
    requires = [ "docker.service" ]; # Require Docker service to start successfully
    script = ''
      echo -n "0000:01:00.0" | tee /sys/bus/pci/drivers/nvidia/unbind || true
      echo OFF | tee /proc/acpi/bbswitch
      /run/current-system/sw/bin/nvidia-smi -pm 1
      cd /home/moritz/Projects/cellwhisperer/hosting/home
      echo 1 | tee /sys/class/backlight/intel_backlight/brightness
    '';
    serviceConfig.Type = "oneshot";
  };
}

mopad

Thinkpad X1 Extreme gen 4

{
  imports = [
    (import "${inputs.nixos-hardware}/lenovo/thinkpad/p1/3th-gen")
    (import "${inputs.nixos-hardware}/lenovo/thinkpad/p1/3th-gen/nvidia.nix")
    (import "${inputs.nixos-hardware}/lenovo/thinkpad/x1-extreme/gen4/default.nix")  # implies cpu/inel and laptop/ssd
    (import "${inputs.nixos-hardware}/common/pc/laptop")  # tlp.enable = true
    (import "${inputs.nixos-hardware}/common/gpu/nvidia/prime.nix")  # default: offload
    inputs.nixpkgs.nixosModules.notDetected
  ];

  # hardware.nvidia.modesetting.enable = true;
  # hardware.opengl.driSupport32Bit = true;
  # hardware.opengl.enable = true;
  # services.xserver.videoDrivers = [ "nvidia" ];
  # hardware.bumblebee.enable = false;

  services.hardware.bolt.enable = true;
  hardware.nvidia.powerManagement.enable = true;  # might be buggy (https://github.com/NVIDIA/open-gpu-kernel-modules/issues/472)
  hardware.nvidia.powerManagement.finegrained = false;   # TODO is this good or bad?
  hardware.nvidia.prime = {
    # Bus ID of the Intel GPU.
    intelBusId = lib.mkDefault "PCI:0:2:0";
    # Bus ID of the NVIDIA GPU.
    nvidiaBusId = lib.mkDefault "PCI:1:0:0";
    
  };
  hardware.nvidia.open = true;

  specialisation = {
    sync-gpu.configuration = {
      system.nixos.tags = [ "sync-gpu" ];
      hardware.nvidia.prime.offload.enable = lib.mkForce false;
      hardware.nvidia.prime.sync.enable = lib.mkForce true;
      hardware.nvidia.powerManagement.finegrained = lib.mkForce false;
      hardware.nvidia.powerManagement.enable = lib.mkForce false;
    };
  };

  environment.systemPackages = [ pkgs.linuxPackages.nvidia_x11 ];
  boot.initrd.availableKernelModules = [ "xhci_pci" "thunderbolt" "nvme" "usb_storage" "sd_mod" "sdhci_pci" ];
  # boot.blacklistedKernelModules = [ "nouveau" "nvidia_drm" "nvidia_modeset" "nvidia" ];
  boot.initrd.kernelModules = [ ];
  boot.kernelModules = [ "kvm-intel" ];
  boot.extraModulePackages = [ ];

  fileSystems."/" =
    { device = "/dev/disk/by-uuid/aed145a9-e93a-428b-be62-d3220fb1ab0f";
      fsType = "ext4";
    };

  fileSystems."/boot" =
    { device = "/dev/disk/by-uuid/F1D8-DA4A";
      fsType = "vfat";
    };

  # Use the systemd-boot EFI boot loader.
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;
  swapDevices =
    [ { device = "/dev/disk/by-uuid/a048e8ec-3daa-4430-86ad-3a7f5e9acd91"; }
    ];

  powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
  hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
  # high-resolution display

  services.xserver = {
    enable = true;
    displayManager = {
      lightdm.enable = true;
      # gdm.enable = true;
    };
  };
  services.libinput = {
    enable = true;
    touchpad.accelSpeed = "0.7";

    # disabling mouse acceleration
    # mouse = {
    #   accelProfile = "flat";
    # };

    # # disabling touchpad acceleration
    # touchpad = {
    #   accelProfile = "flat";
    # };
  };
  # The NixOS release to be compatible with for stateful data such as databases.
  system.stateVersion = "20.03";
}

Keyboard:

https://nixos.wiki/wiki/Keyboard_Layout_Customization#Using_xmodmap

{
  # TODO the keyboard is not fixed in the right manner (in browsers, alt and enter lead to the original keypresses...)
  services.xserver.displayManager.sessionCommands = let
    myCustomLayout = pkgs.writeText "xkb-layout" ''
    keycode 36 = ISO_Level3_Shift
    '';
  in
     ''
      ${pkgs.xorg.xmodmap}/bin/xmodmap ${myCustomLayout}"
      xsetroot -cursor_name left_ptr
    '';
}

Cursor related: https://discourse.nixos.org/t/how-to-fix-cursor-size/2938

{
  services.xserver.displayManager.lightdm.greeters.gtk.cursorTheme = {
    name = "Vanilla-DMZ";
    package = pkgs.vanilla-dmz;
    size = 64; # was 64
  };
  xsession.pointerCursor = {
      package = pkgs.vanilla-dmz; # pkgs.gnome.adwaita-icon-theme;
      name = "Vanilla-DMZ";
      size = 64;
  };
  environment.variables.XCURSOR_SIZE = "64";
}

Alternative way to fix enter key -> iso_level3

as described in https://nixos.wiki/wiki/Keyboard_Layout_Customization I could also add the following xkb code, but how?

partial modifier_keys xkb_symbols “enter_switch” { key <RTRN> { type[Group1]=”ONE_LEVEL”, symbols[Group1] = [ ISO_Level3_Shift ] }; include “level3(modifier_mapping)” };

Of note, rasendubi somehow did this quite elegantly. Have a look here: home/moritz/nixos-config.config/xkb/my

mobook

This is my late 2013 MBP.

{
  imports = [
    # (import "${inputs.nixos-hardware}/apple/macbook-pro") # messes up the keyboard...
    (import "${inputs.nixos-hardware}/common/pc/laptop/ssd")
    (import "${inputs.nixos-hardware}/common/pc/laptop")  # tlp.enable = true
    (import "${inputs.nixos-hardware}/common/cpu/intel")
    #inputs.nixpkgs.modules.hardware.network.broadcom-43xx # <- using import vs not using import?
   #  <nixpkgs/nixos/modules/hardware/network/broadcom-43xx.nix> <- this is when using channels instead of flakes?
    inputs.nixpkgs.nixosModules.notDetected
  ];
  
  hardware.facetimehd.enable = true;

  # from https://wiki.archlinux.org/index.php/MacBookPro11,x#Powersave
  services.udev.extraRules = let
    # remove_script = pkgs.requireFile {
    #   name = "remove_ignore_usb_devices.sh";
    #   url = "https://gist.githubusercontent.com/anonymous/9c9d45c4818e3086ceca/raw/2aa42b5b7d564868ff089dc72445f24586b6c55e/gistfile1.sh";
    #   sha256 = "b2e1d250b1722ec7d3a381790175b1fdd3344e638882ac00f83913e2f9d27603";
    # };
    remove_script = ''
    # from https://gist.github.com/anonymous/9c9d45c4818e3086ceca
    logger -p info "$0 executed."
    if [ "$#" -eq 2 ];then
        removevendorid=$1
        removeproductid=$2
        usbpath="/sys/bus/usb/devices/"
        devicerootdirs=`ls -1 $usbpath`
        for devicedir in $devicerootdirs; do
            if [ -f "$usbpath$devicedir/product" ]; then
                product=`cat "$usbpath$devicedir/product"`
                productid=`cat "$usbpath$devicedir/idProduct"`
                vendorid=`cat "$usbpath$devicedir/idVendor"`
                if [ "$removevendorid" == "$vendorid" ] && [ "$removeproductid" == "$productid" ];    then
                    if [ -f "$usbpath$devicedir/remove" ]; then
                        logger -p info "$0 removing $product ($vendorid:$productid)"
                    echo 1 > "$usbpath$devicedir/remove"
                        exit 0
          else
                        logger -p info "$0 already removed $product ($vendorid:$productid)"
                        exit 0
          fi
                fi
            fi
        done
    else
        logger -p err "$0 needs 2 args vendorid and productid"
        exit 1
    fi'';
    remove_script_local = pkgs.writeShellScript "remove_ignore_usb-devices_local.sh" remove_script; #(import ./remove_ignore_usb_devices.sh.nix); # (builtins.readFile remove_script)
  in
    ''
    # /etc/udev/rules.d/99-apple_cardreader.rules
    SUBSYSTEMS=="usb", ATTRS{idVendor}=="05ac", ATTRS{idProduct}=="8406", RUN+="${remove_script_local} 05ac 8406"
    # /etc/udev/rules.d/99-apple_broadcom_bcm2046_bluetooth.rules
    SUBSYSTEMS=="usb", ATTRS{idVendor}=="05ac", ATTRS{idProduct}=="8289", RUN+="${remove_script_local} 05ac 8289"
    SUBSYSTEMS=="usb", ATTRS{idVendor}=="0a5c", ATTRS{idProduct}=="4500", RUN+="${remove_script_local} 0a5c 4500"

    # Disable XHC1 wakeup signal to avoid resume getting triggered some time
    # after suspend. Reboot required for this to take effect.
    SUBSYSTEM=="pci", KERNEL=="0000:00:14.0", ATTR{power/wakeup}="disabled"
    '';

  systemd.services.disable-gpe06 = {
    description = "Disable GPE06 interrupt leading to high kworker";
    wantedBy = [ "multi-user.target" ];
    script = ''
      /run/current-system/sw/bin/bash -c 'echo "disable" > /sys/firmware/acpi/interrupts/gpe06'
    '';
    serviceConfig.Type = "oneshot";
  };


  boot.loader.systemd-boot.enable = true;
  boot.loader.systemd-boot.configurationLimit = 10;
  # boot.loader.efi.canTouchEfiVariables = true;
      
  # accelerateion
  # nixpkgs.config.packageOverrides = pkgs: {
  #   vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; };
  # };
  # hardware.opengl = {
  #   enable = true;
  #   extraPackages = with pkgs; [
  #     intel-media-driver # LIBVA_DRIVER_NAME=iHD
  #     vaapiIntel         # LIBVA_DRIVER_NAME=i965 (older but works better for Firefox/Chromium)
  #     vaapiVdpau
  #     libvdpau-va-gl
  #   ];
  # };


  boot.kernelModules = [ "kvm-intel" "wl" ];
  boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usb_storage" "sd_mod" "usbhid" ];
  boot.extraModulePackages = [ config.boot.kernelPackages.broadcom_sta ];

  powerManagement.enable = true;
  powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";

  services.mbpfan = {
    enable = true;
    lowTemp = 60;
    highTemp = 67;
    maxTemp = 84;
  };

  # The NixOS release to be compatible with for stateful data such as databases.
  system.stateVersion = "20.03";
}

LVM on LUKS setup for disk encryption.

{
  fileSystems."/boot" =
    { device = "/dev/disk/by-uuid/E64F-3226";
      fsType = "vfat";
    };

  swapDevices =
    [ { device = "/dev/disk/by-uuid/912c5850-5f71-4d15-8b69-1e0dad5718b0"; }
    ];

  fileSystems."/" =
    { device = "/dev/disk/by-uuid/73edc386-3f1a-46ff-9ae1-76a4fd6c0ea4";
      fsType = "btrfs";
    };

  boot.initrd.luks.devices = {
    cryptkey = {
      device = "/dev/disk/by-uuid/179ecdea-edd4-4dc5-b8c3-5ed760bc2a0d";
    };
    cryptroot = {
      device = "/dev/disk/by-uuid/623db0a5-d0e0-405a-88ae-b83a3d321656";
      keyFile = "/dev/mapper/cryptkey";
    };
    cryptswap = {
      device = "/dev/disk/by-uuid/da63991e-8edd-48db-bc4b-66fbc96917eb";
      keyFile = "/dev/mapper/cryptkey";
    };
  };
}

Clickpad and DPI:

{
  services.libinput = {
    enable = true;
    touchpad.accelSpeed = "0.7";
  };
  # displayManager.lightdm.greeters.gtk.cursorTheme = {  # TODO if home manager cursor doesnt work
  #   name = "Vanilla-DMZ";
  #   package = pkgs.vanilla-dmz;
  #   size = 64;
  # };
}

moair

{
  imports = [
    # apple-silicon hardware support
    inputs.apple-silicon.nixosModules.apple-silicon-support

    # (import "${inputs.apple-silicon}/")
    inputs.nixpkgs.nixosModules.notDetected
  ];

  nixpkgs.overlays = [ inputs.apple-silicon.overlays.apple-silicon-overlay ];

  boot.kernelParams = [ "apple_dcp.show_notch=1" ];

  # Use the systemd-boot EFI boot loader.
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = false; # modified
  boot.initrd.availableKernelModules = [ "usb_storage" ];

  fileSystems."/" =
    { device = "/dev/disk/by-uuid/e24c4ea5-1dd7-4b80-ac54-d6f87e72b3a6";
      fsType = "ext4";
    };

  fileSystems."/boot" =
    { device = "/dev/disk/by-uuid/0747-1012";
      fsType = "vfat";
      options = [ "fmask=0022" "dmask=0022" ];
    };

  fileSystems."/mnt/data" =
    { device = "/dev/disk/by-uuid/66EC-4934";
      fsType = "exfat";
      options = [ "nofail" "uid=1000" "gid=100" "umask=0022" ];
    };

  swapDevices = [ ];

  # # backlight control
  # programs.light.enable = true;  
  # services.actkbd = {
  #   enable = true;
  #   bindings = [
  #     { keys = [ 225 ]; events = [ "key" ]; command = "/run/current-system/sw/bin/light -A 10"; }
  #     { keys = [ 224 ]; events = [ "key" ]; command = "/run/current-system/sw/bin/light -U 10"; }
  #   ];
  # };

  # Reference Asahi/Apple data path (required for flake)
  hardware.asahi.peripheralFirmwareDirectory = ./firmware;
  # Optionally disable their extraction
  # hardware.asahi.extractPeripheralFirmware = false;
  # hardware.asahi = {  # TODO this might improve graphics stuff
  #   withRust = true;
  #   useExperimentalGPUDriver = true;
  #   experimentalGPUInstallMode = "replace";
  #   setupAsahiSound = true;
  # };

  # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
  # (the default) this is the recommended approach. When using systemd-networkd it's
  # still possible to use this option, but it's recommended to use it in conjunction
  # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
  networking.useDHCP = lib.mkDefault true;
  # networking.interfaces.wlan0.useDHCP = lib.mkDefault true;

  networking.hostName = "moair"; # Define your hostname.
  # Pick only one of the below networking options.
  networking.wireless.iwd = {
    enable = true;
    settings.General.EnableNetworkConfiguration = true;
  };
  # networking.wireless.enable = true;  # Enables wireless support via wpa_supplicant.
  networking.networkmanager.enable = true;  # Easiest to use and most distros use this by default.


  nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux";

  services.xserver = {
    enable = true;
    displayManager = {
      lightdm.enable = true;
    };
  };
  services.libinput = {
    enable = true;
    touchpad.accelSpeed = "0.7";
    # mouse.disableWhileTyping = true;
    touchpad.disableWhileTyping = true;
    touchpad.tapping = false;
    # disabling mouse acceleration
    # mouse = {
    #   accelProfile = "flat";
    # };

    # # disabling touchpad acceleration
    # touchpad = {
    #   accelProfile = "flat";
    # };
  };
  # For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
  system.stateVersion = "24.11"; # Do not change!
}

Cursor

{
  services.xserver.displayManager.sessionCommands = ''
    xsetroot -cursor_name left_ptr
  '';
}

related: https://discourse.nixos.org/t/how-to-fix-cursor-size/2938

{
  services.xserver.displayManager.lightdm.greeters.gtk.cursorTheme = {
    name = "Vanilla-DMZ";
    package = pkgs.vanilla-dmz;
    size = 64; # was 64
  };
  xsession.pointerCursor = {
    package = pkgs.vanilla-dmz; # pkgs.gnome.adwaita-icon-theme;
    name = "Vanilla-DMZ";
    size = 64;
  };
  environment.variables.XCURSOR_SIZE = "64";
}

Battery limitation (to save battery live)

{
  services.udev.extraRules = ''
    KERNEL=="macsmc-battery", SUBSYSTEM=="power_supply", ATTR{charge_control_end_threshold}="95"
  '';
  # , ATTR{charge_control_start_threshold}="70" only charge below 70 <- nope :)
}

Local packages

As a responsible NixOS user, I refuse to install software blindly with sudo make install. That’s why I must write my own nix-expressions.

Custom Input font

I like the following settings more than defaults. I also need a custom four-style family because Emacs confuses regular/medium weight otherwise. Use link specified in requireFile to download the font.

./images/20200409192721-screenshot.png

{
  # note it's a new attribute and does not override old one
  input-mono = (pkgs.input-fonts.overrideAttrs (old: {
    src = pkgs.requireFile {
      name = "Input-Font.zip";
      url = "https://input.fontbureau.com/build/?fontSelection=fourStyleFamily&regular=InputMonoNarrow-Regular&italic=InputMonoNarrow-Italic&bold=InputMonoNarrow-Bold&boldItalic=InputMonoNarrow-BoldItalic&a=0&g=0&i=topserif&l=serifs_round&zero=0&asterisk=height&braces=straight&preset=default&line-height=1.2&accept=I+do&email=";
      sha256 = "888bbeafe4aa6e708f5c37b42fdbab526bc1d125de5192475e7a4bb3040fc45a";
    };
    outputHash = "1w2i660dg04nyc6fc6r6sd3pw53h8dh8yx4iy6ccpii9gwjl9val";
  }));
}

Bluetooth

I have a bluetooth headset, so this enables bluetooth audio in NixOS.

{
  hardware.bluetooth.enable = true;
  hardware.bluetooth.powerOnBoot = false;
  services.blueman.enable = true;
  hardware.bluetooth.settings.General.Enable = "Source,Sink,Media,Socket";
}

NTFS & exfat

Install ntfs-3g to mount ntfs volumes in read-write mode.

{
  environment.systemPackages = [
    pkgs.ntfs3g
    pkgs.exfatprogs
  ];
}

Network mounts

For background, see this thread: https://discourse.nixos.org/t/seeking-assistance-with-old-exwm-emacs-version-after-23-11-update/36607/4

{
  environment.systemPackages = [
    pkgs.sshfs
  ];

  age.secrets.muwhpc.file = "/home/moritz/nixos-config/secrets/muwhpc.age";
  fileSystems."/mnt/muwhpc" = {
    device = "//msc-smb.hpc.meduniwien.ac.at/mschae83";
    fsType = "cifs";
    options = [
      "username=mschae83"
      "credentials=${config.age.secrets.muwhpc.path}"
      "domain=smb"
      "x-systemd.automount"
      "noauto"
      "uid=1000"
      "x-systemd.idle-timeout=60"
      "x-systemd.device-timeout=5s"
      "x-systemd.mount-timeout=5s"
    ];
  };
  # mount command fails unfortunately. Use Thunar instead
  # age.secrets.cemm.file = /home/moritz/nixos-config/secrets/cemm.age;
  # fileSystems."/mnt/cemm" = {
  #   device = "//int.cemm.at/files";
  #   fsType = "cifs";
  #   options = [
  #     "username=mschaefer"
  #     "credentials=${config.age.secrets.cemm.path}"
  #     # "domain=int.cemm.at"  # CEMMINT
  #     "x-systemd.automount"
  #     "noauto"
  #     "uid=1000"
  #     "x-systemd.idle-timeout=60"
  #     "x-systemd.device-timeout=5s"
  #     "x-systemd.mount-timeout=5s"
  #   ];
  # };
}

excluded

“vers=1.0” “nounix”

stuff that didn’t work

“vers=3” “sec=ntlmssp” “cache=strict” “noserverino” “nodev” “noexec”

rclone

{
  systemd.services.gdrive_mount = let mountdir = "/mnt/gdrive"; in {
    description = "mount gdrive dirs";
    after = [ "network.target" ];
    wantedBy = [ "multi-user.target" ];
    serviceConfig = {
      ExecStartPre = "/run/current-system/sw/bin/mkdir -p ${mountdir}";
      ExecStart = ''
          ${pkgs.rclone}/bin/rclone mount gdrive: ${mountdir} \
              --dir-cache-time 48h \
              --vfs-cache-max-age 48h \
              --vfs-read-chunk-size 10M \
              --vfs-read-chunk-size-limit 512M \
              --buffer-size 512M
      '';
      ExecStop = "/run/wrappers/bin/fusermount -u ${mountdir}";
      Type = "notify";
      Restart = "always";
      RestartSec = "10s";
      Environment = [ "PATH=/run/wrappers/bin:$PATH" ];
      User = "moritz";
    };
  };
}

Updates

{
  system.autoUpgrade.enable = true;
}

Hibernate on battery low and warn at 20%

{
  environment.systemPackages = with pkgs; [ libnotify ];
  systemd.timers.hibernate-on-low-battery = {
    wantedBy = [ "multi-user.target" ];
    timerConfig = {
      OnUnitActiveSec = "120";
      OnBootSec= "120";
    };
  };
  systemd.services.hibernate-on-low-battery =
    let
      battery-level-sufficient = pkgs.writeShellScriptBin
        "battery-level-sufficient" ''
        #!/bin/bash

        # set environment to allow notify-send to work
        export XAUTHORITY="/home/moritz/.Xauthority"
        export DISPLAY=":0"
        export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/1000/bus"
        export PATH="${pkgs.dbus}/bin:$PATH"

        capacity=$(cat /sys/class/power_supply/BAT0/capacity)
        status=$(cat /sys/class/power_supply/BAT0/status)

        if [ "$capacity" -le 20 ] && [ "$status" = "Discharging" ]; then
          ${pkgs.sudo}/bin/sudo -E -u moritz  ${pkgs.libnotify}/bin/notify-send -t 4000 "Low Battery" "Your battery is below 20%, please plug in your charger."
        fi
        test "$(cat /sys/class/power_supply/BAT0/status)" != Discharging \
          || test "$(cat /sys/class/power_supply/BAT0/capacity)" -ge 10
      '';
    in
      {
        serviceConfig = { Type = "oneshot"; };
        onFailure = [ "hibernate.target" ];
        script = "${battery-level-sufficient}/bin/battery-level-sufficient";
      };
}

Garbage collection/Cleaning

{
  nix.optimise.automatic = true;
  nix.gc.automatic = true;
  nix.gc.options = "--delete-generations +12";
}

“Nice” permissions

{
  security.pam.loginLimits = [{ # http://www.linux-pam.org/Linux-PAM-html/sag-pam_limits.html
    "domain" = "moritz";  # or group @users
    "type" = "-";
    "item" = "nice";
    "value" = "-18";
  }
  # {  # disabled for testing. check if everything works fine after reboot...
  #   "domain" = "moritz";  # or group @users
  #   "type" = "-";
  #   "item" = "priority";
  #   "value" = "-10";
  # }
  ];
}

Services

Network(Manager)

{
  networking = {
    hostName = name;

    firewall.checkReversePath = false;  # required for wireguard (potential security risk. see https://nixos.wiki/wiki/WireGuard#Setting_up_WireGuard_with_NetworkManager for details)
    networkmanager = {
      enable = true;
      plugins = [
        pkgs.networkmanager-openconnect
        pkgs.networkmanager-vpnc
      ];
    };

    # disable wpa_supplicant
    wireless.enable = false;
  };

  users.users.moritz.extraGroups = [ "networkmanager" ];

  environment.systemPackages = [
    pkgs.openconnect
    pkgs.networkmanagerapplet
    pkgs.vpnc
    pkgs.vpnc-scripts
  ];
}

Wireguard: (note: mopad was configured via NetworkManager)

{
  networking.firewall = {
    allowedUDPPorts = [ 51820 ]; # Clients and peers can use the same port, see listenport
  };

  age.secrets.client_wireguard_private.file = "/home/moritz/nixos-config/secrets/wireguard_client_private_key.age";
  # Enable WireGuard
  networking.wireguard.enable = true;
  networking.wireguard.interfaces = {
    # "wg0" is the network interface name. You can name the interface arbitrarily.
    wg0 = {
      # Determines the IP address and subnet of the client's end of the tunnel interface.
      ips = [ "10.100.0.3/24" ];
      listenPort = 51820; # to match firewall allowedUDPPorts (without this wg uses random port numbers)

      # Path to the private key file.
      #
      # Note: The private key can also be included inline via the privateKey option,
      # but this makes the private key world-readable; thus, using privateKeyFile is
      # recommended.
      privateKeyFile = config.age.secrets.client_wireguard_private.path;

      peers = [
        # For this client configuration, one peer entry for the server will suffice.
        {
          # Public key of the server (not a file path).
          publicKey = "KYF+BBuoY7dNYswft+vhlNrAKjAkMIMYnkhBbHcH7Dw=";

          # Forward all the traffic via VPN.
          # allowedIPs = [ "0.0.0.0/0" ];
          # Or forward only particular subnets
          allowedIPs = [ "10.100.0.1" "192.168.0.0/24" ];

          # Set this to the server IP and port.
          endpoint = "moritzs.duckdns.org:51820";

          # Send keepalives every 25 seconds. Important to keep NAT tables alive.
          persistentKeepalive = 25;
        }
      ];
    };
  };
}

Avahi

{
  services.avahi = {
    enable = true;
   allowInterfaces = [ "wlp9s0" "wlan" "tun0" "wg0" ];  # TODO how to add "all"?
    openFirewall = true;
    publish = {
      addresses = true;
      workstation = true;
      enable = true;
    };
    nssmdns4 = true;
  };
}

PulseAudio&Audio

Use pulseaudio (multiple sound sinks, skype calls). pavucontrol is PulseAudio Volume Control—a nice utility for controlling pulseaudio settings.

Also, Pulseaudio is a requirement for Firefox Quantum.

{
  # TODO enable instead of pulseaudio
  # security.rtkit.enable = true;
  services.pipewire = {
    enable = true;
    pulse.enable = true;
  #   alsa.enable = true;
  #   alsa.support32Bit = true;
  #   pulse.enable = true;
  #   # If you want to use JACK applications, uncomment this
  #   jack.enable = true;
  };

  environment.systemPackages = with pkgs; [
  pavucontrol
  # libjack2 jack2 qjackctl jack2 jack_capture
  gst_all_1.gstreamer
  gst_all_1.gst-plugins-good
  gst_all_1.gst-plugins-base
  # gst_all_1.gst-plugins-ugly gst_all_1.gst-plugins-bad
  ffmpeg
  ];

  # services.jack = {
  #   jackd.enable = true;
  #   # support ALSA only programs via ALSA JACK PCM plugin
  #   alsa.enable = false;
  #   # support ALSA only programs via loopback device (supports programs like Steam)
  #   loopback = {
  #     enable = true;
  #     # buffering parameters for dmix device to work with ALSA only semi-professional sound programs
  #     #dmixConfig = ''
  #     #  period_size 2048
  #     #'';
  #   };
  # };
  # boot.kernelModules = [ "snd-seq" "snd-rawmidi" ];

  users.users.moritz.extraGroups = [ "audio" ];  # "jackaudio" 

  # from https://github.com/JeffreyBenjaminBrown/nixos-experiments/blob/6c4be545e2ec18c6d9b32ec9b66d37c59d9ebc1f/audio.nix
  security.sudo.extraConfig = ''
    moritz  ALL=(ALL) NOPASSWD: ${pkgs.systemd}/bin/systemctl
    '';
}
{
  boot.extraModprobeConfig =''  # https://github.com/NixOS/nixpkgs/issues/330685#issuecomment-2279718903
    options snd-hda-intel dmic_detect=0
  '';
}

Printing

https://nixos.wiki/wiki/Printing

{
  services.printing.enable = true;
  services.printing.browsedConf = ''
    CreateIPPPrinterQueues All
  '';
  services.printing.drivers = with pkgs; [
    gutenprint
    # gutenprintBin  # not aarch64
    samsung-unified-linux-driver
    splix
    canon-cups-ufr2
    carps-cups
  ];
  services.system-config-printer.enable = true;
  environment.systemPackages = [
    pkgs.gtklp
  ];
}

Locate

Update locate database daily.

{
  services.locate = {
    enable = true;
    localuser = "moritz";
  };
}

SSH

Needs to be enabled so we have the public key (for agenix).

{
  services.openssh = {
    enable = true;
    settings.PasswordAuthentication = false;
  };
  users.users.moritz.openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMc+scl71X7g21XFygTNB3onyGuION89iHSUw0eYcN2H [email protected]" ];
}

Mosh

Mosh (mobile shell) is a cool addition to ssh.

{
  programs.mosh.enable = true;
}

dnsmasq

Use dnsmasq as a DNS cache.

{
  services.dnsmasq = {
    enable = false;

    # These are used in addition to resolv.conf
    settings = {
      servers = [
        "8.8.8.8"
        "8.8.4.4"
      ];
      listenAddress = "127.0.0.1";
      cacheSize = 1000;
      noNegcache = true;
    };
  };
}

Syncthing

I use Syncthing to sync my org-mode files to my phone.

{
  services.syncthing = {
    enable = true;
    package = pkgs.unstable.syncthing;
    user = "moritz";
    dataDir = "/home/moritz/.config/syncthing";
    configDir = "/home/moritz/.config/syncthing";
    openDefaultPorts = true;
  };
}

OneDrive

I use OneDrive from my job

{
  # services.onedrive = {
  #   enable = true;
  # };
  environment.systemPackages = [
    pkgs.unstable.onedrivegui
  ];
}

Firewall

Enable firewall. This blocks all ports (for ingress traffic) and pings.

{
  networking.firewall = {
    enable = true;
    allowPing = true;  # needed for samba

    connectionTrackingModules = [];
    autoLoadConntrackHelpers = false;
  };
}

Virtualization/Development

{
  virtualisation.virtualbox.host.enable = false;  # slow compile times
  virtualisation.docker.enable = true;
  # virtualisation.docker.enableNvidia = true;  # TODO 

  # hardware.opengl.driSupport32Bit = true;
  environment.systemPackages = [
    pkgs.docker-compose
    pkgs.qemu_kvm
    pkgs.qemu
    # pkgs.nvtop # for nvidia
    pkgs.usbtop
    pkgs.xorg.xhost
  ];

  users.users.moritz.extraGroups = ["libvirtd" "docker"];  # the former is required for qemu I think 
}

Backup

I use borg for backups.

{
  environment.systemPackages =
    let mount_external = pkgs.writeScriptBin "mount-external" ''
      #!${pkgs.stdenv.shell}
      sudo ${pkgs.cryptsetup}/bin/cryptsetup luksOpen /dev/disk/by-uuid/aeebfb90-65b5-4515-bf6e-001d0cfc8a40 encrypted-2tb
      sudo mount /dev/mapper/encrypted-2tb /mnt/encrypted
      '';
    umount_external = pkgs.writeScriptBin "umount-external" ''
      #!${pkgs.stdenv.shell}
      sudo umount /mnt/encrypted
      sudo ${pkgs.cryptsetup}/bin/cryptsetup luksClose encrypted-2tb
      '';
  in
     [ mount_external umount_external pkgs.borgbackup ];
}

ADB

I need to access my Android device.

{
  services.udev.packages = [ pkgs.android-udev-rules ];
  programs.adb.enable = true;
  users.users.moritz.extraGroups = ["adbusers"];
}

fwupd

fwupd is a service that allows applications to update firmware.

{
  services.fwupd.enable = true;
}

lorri + direnv

{
  environment.systemPackages = [
    pkgs.direnv
  ];
  programs.fish.shellInit = ''
    eval (direnv hook fish)
  '';

  services.lorri.enable = true;
}

Automounting

Automatic USB stick mounting

{
  # services.udisks2.enable = true;
  services.devmon.enable = true;
}

Logind

{
  services.logind.extraConfig = ''
    HandlePowerKey=suspend
  '';
}

Samba

{
  networking.firewall.extraCommands = ''iptables -t raw -A OUTPUT -p udp -m udp --dport 137 -j CT --helper netbios-ns'';
  
  services.gvfs = {  # https://nixos.wiki/wiki/Samba#Browsing_samba_shares_with_GVFS
    enable = true;
    package = lib.mkForce pkgs.gnome.gvfs;
  };
  services.samba = {
    enable = true;
    securityType = "user";
    openFirewall = true;
    settings = {
      global = {
        workgroup = "WORKGROUP";
        "wins support" = "no";
        "wins server" = "192.168.1.10";
        "server string" = "smbnix";
        "netbios name" = "smbnix";
        security = "user";
        "hosts allow" = "192.168. localhost";
        "hosts deny" = "0.0.0.0/0";
        "guest account" = "nobody";
        "map to guest" = "bad user";
      };
    };
    shares = {
      # public = {
      #   path = "/mnt/Shares/Public";
      #   browseable = "yes";
      #   "read only" = "no";
      #   "guest ok" = "yes";
      #   "create mask" = "0644";
      #   "directory mask" = "0755";
      #   "force user" = "username";
      #   "force group" = "groupname";
      # };
      moritz = {
        path = "/home/moritz/";
        browseable = "yes";
        "read only" = "no";
        "guest ok" = "no";
        "create mask" = "0644";
        "directory mask" = "0755";
        "force user" = "moritz";
        "force group" = "users";
      };
    };
  };
}

TODO: this one could/should use agenix credentials

{
  # docs: https://nixos.wiki/wiki/Samba#CIFS_mount_configuration
  # could be mounted as user as well (default is root)
  environment.systemPackages = [ pkgs.cifs-utils ];
  fileSystems."/mnt/moxps_ssd2tb" = {
    device = "//192.168.0.52/ssd2tb";
    fsType = "cifs";
    options = let
      # this line prevents hanging on network split
      automount_opts = "x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s";

    in ["${automount_opts},credentials=/etc/nixos/smb-secrets"];
  };
}

Cron (systemd timers)

{
  systemd.services."weekly-git-commit" = {
    script = ''
      set -ev
      cd /home/moritz/wiki/
      export PATH=$PATH:${pkgs.git-lfs}/bin
      ${pkgs.git}/bin/git add .
      ${pkgs.git}/bin/git commit -m "Weekly checkpoint"
    '';
    serviceConfig = {
      Type = "oneshot";
      User = "moritz";
    };
  };
  systemd.timers."weekly-git-commit" = {
    wantedBy = [ "timers.target" ];
    timerConfig = {
      OnCalendar = "Sun 10:00";
      Persistent = true;
      Unit = "weekly-git-commit.service";
    };
  };

  systemd.services."download-paperpile" = {
    script = ''
      set -ev
      cd /home/moritz/wiki/papers
      ${pkgs.wget}/bin/wget --content-disposition -N https://paperpile.com/eb/ghEynTRTJb
    '';
    serviceConfig = {
      Type = "oneshot";
      User = "moritz";
    };
  };
  systemd.timers."download-paperpile" = {
    wantedBy = [ "timers.target" ];
    timerConfig = {
      OnCalendar = "*:0/1";
      Persistent = true;
      Unit = "download-paperpile.service";
    };
  };
}

Office setup

Mbsync

I use mbsync to sync my accounts and make them available offline.

{
  environment.systemPackages = [
    pkgs.isync
  ];
}

Config file is .mbsyncrc.

MaildirStore local
Path ~/Mail/
Inbox ~/Mail/INBOX
SubFolders Verbatim


<<mbsync-gmail(name="gmail", email="[email protected]", path="Personal")>>

I have multiple Gmail accounts, so here is a general template.

(defmacro rasen/interpolate-string (text)
  "Expand text like \"Hello <<name>>\" to (format \"Hello %s\" name)."
  (let ((pattern "<<\\(.*?\\)>>"))
    ;; The regexp matches anything between delimiters, non-greedily
    (with-temp-buffer
      (save-excursion (insert text))
      (let ((matches '()))
        (while (re-search-forward pattern nil t)
          (push (match-string 1) matches)
          (replace-match "%s" t t))
`(format ,(buffer-string) ,@(reverse (mapcar 'read matches)))))))
(rasen/interpolate-string "
IMAPAccount <<name>>
Host imap.gmail.com
User <<email>>
PassCmd \"pass imap.gmail.com/<<email>>\"
SSLType IMAPS
CertificateFile /etc/ssl/certs/ca-certificates.crt

IMAPStore <<name>>-remote
Account <<name>>

Channel sync-<<name>>-all
Master :<<name>>-remote:\"[Gmail]/All Mail\"
Slave :local:<<path>>/all
Create Both
SyncState *

Channel sync-<<name>>-spam
Master :<<name>>-remote:\"[Gmail]/Spam\"
Slave :local:<<path>>/spam
Create Both
SyncState *

Channel sync-<<name>>-sent
Master :<<name>>-remote:\"[Gmail]/Sent Mail\"
Slave :local:<<path>>/sent
Create Both
SyncState *

Group sync-<<name>>
Channel sync-<<name>>-all
Channel sync-<<name>>-spam
Channel sync-<<name>>-sent
")

msmtp

Msmtp is used to send mail.

{
  environment.systemPackages = [
    pkgs.msmtp
  ];
}

Config file is .msmtprc.

defaults
auth on
tls on
tls_starttls off
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile ~/.msmtp.log

<<msmtp-gmail(name="gmail", email="[email protected]")>>

Again, general template for gmail accounts.

(rasen/interpolate-string "
# <<name>>
account <<name>>
host smtp.gmail.com
port 465
from <<email>>
user <<email>>
passwordeval \"pass imap.gmail.com/<<email>>\"
")

notmuch

Notmuch is used for tagging.

{
  environment.systemPackages = [
    pkgs.notmuch
  ];
}

Config file is .notmuch-config.

[user]
name=Moritz Schaefer
[email protected]
[email protected],[email protected],[email protected],[email protected]

[database]
path=/home/moritz/Mail

[new]
tags=inbox;
ignore=.mbsyncstate;.mbsyncstate.lock;.mbsyncstate.new;.mbsyncstate.journal;.uidvalidity;dovecot-uidlist;dovecot-keywords;dovecot.index;dovecot.index.log;dovecot.index.log.2;dovecot.index.cache;/^archive/

[search]
exclude_tags=deleted;spam;muted;

[crypto]
gpg_path=gpg2

MS office365 calendar

Here’s a basic NixOS configuration that should do what you’re asking for. This configuration assumes that you have wget and gawk installed on your system. If not, you should add them to your environment.systemPackages.

# TODO also the awk script is for google calendar, maybe I should try to find an office365-specific script.
# TODO I modified that script such that it does not adjust the time zone (because it was broken: the ical file indicates the wrong timezone but the correct time!). ( return 0 in parse_timezone_offset
# TODO also, filter either ical or org for events older than last month (otherwise org-agenda has to work so much more...)
# TODO note: I disabled syncthing wiki syncing o the `calendar-sync` folder (ignore/exception)
{
  environment.systemPackages = with pkgs; [ wget gawk gnugrep ];

  age.secrets.mcUrl.file = "/home/moritz/nixos-config/secrets/mcUrl.age";
  age.secrets.gcUrl.file = "/home/moritz/nixos-config/secrets/gcUrl.age";
  systemd.services.ics2org = let
    scriptPath = "/home/moritz/wiki/calendar-sync/ical2org.awk";
    mcIcsPath = "/home/moritz/wiki/calendar-sync/mc_office365.ics";
    gcIcsPath = "/home/moritz/wiki/calendar-sync/gc_office365.ics";
    orgPath = "/home/moritz/wiki/calendar-sync/calendars.org";
    # mcUrlFile = config.age.secrets.mcUrl.path;
    # gcUrlFile = config.age.secrets.gcUrl.path;
     in {
    description = "Convert .ics to .org";
    wantedBy = [ "multi-user.target" ];
    serviceConfig = {
      Type = "oneshot";
    };
    script = ''
      # not redownloading the script, because the time-zone adaptation is broken (see TODO above)
      # ${pkgs.wget}/bin/wget https://raw.githubusercontent.com/msherry/ical2org/master/ical2org.awk -O ${scriptPath}
      ${pkgs.wget}/bin/wget `cat ${config.age.secrets.mcUrl.path}` -O ${mcIcsPath}
      ${pkgs.wget}/bin/wget `cat ${config.age.secrets.gcUrl.path}` -O ${gcIcsPath}
      ${pkgs.gawk}/bin/gawk -f ${scriptPath} ${mcIcsPath} | ${pkgs.gnugrep}/bin/grep -v 'CLOCK:' > ${orgPath}
      ${pkgs.gawk}/bin/gawk -f ${scriptPath} ${gcIcsPath} | ${pkgs.gnugrep}/bin/grep -v 'CLOCK:' >> ${orgPath}
    '';
  };

  systemd.timers.ics2org = {
    description = "Run ics2org every 5 minutes";
    wantedBy = [ "timers.target" ];
    timerConfig = {
      OnUnitActiveSec = "5m";
    };
  };
}

Also, please note that this configuration is for a user service and timer. If you want to run this as a system service and timer, you should remove .user from systemd.user.services.ics2org and systemd.user.timers.ics2org, and add wantedBy = [ "multi-user.target" ]; to the service configuration.

Environment

General

Use English as my only supported locale:

{
  i18n.supportedLocales = [ "en_US.UTF-8/UTF-8" ];
}

Setup timezone:

{
  time.timeZone = "Europe/Berlin";
}

Increase sudo timeout

{
  security.sudo.extraConfig = ''
    Defaults        timestamp_timeout=120
  '';
}

Login manager / display manager / Window manager

I needed to hack this to emacs29, because emacs28 failed with my org-mode config. The overlay and code block below can be trashed with version 23.11 (when emacs 29 is default)

{

  boot.crashDump.enable = true;
  services.displayManager = {
    autoLogin = {
      user = "moritz";
      enable = true;
    };
    defaultSession = "none+exwm";  # Firefox works more fluently with plasma5+exwm instead of "none+exwm". or does it??
  };
  services.xserver = {
    displayManager.startx.enable = false;
    windowManager = {
      exwm = {
        enable = true;

        extraPackages = epkgs: with epkgs; [ emacsql-sqlite pkgs.imagemagick pkgs.escrotum epkgs.vterm ];  # unfortunately, adding zmq and jupyter here, didn't work so I had to install them manually (i.e. compiling emacs-zmq)
        # I only managed to compile emacs-zmq once (~/emacs.d/elpa/27.1/develop/zmq-.../emacs-zmq.so). I just copied it from there to mobook
        # careful, 'loadScript option' was merged from Vizaxo into my personal nixpkgs repo.
        loadScript = ''
          (require 'exwm)
          ;; most of it is now in .spacemacs.d/lisp/exwm.el
          (setq exwm-workspace-number 8)
          (require 'exwm-systemtray)
          (require 'exwm-randr)
          ;; (setq exwm-randr-workspace-monitor-plist '(0 "eDP1" 1 "HDMI1" 2 "DP2" 3 "eDP1" 4 "HDMI1" 5 "DP2"))
          ;; (setq exwm-randr-workspace-monitor-plist '(0 "eDP1" 1 "eDP1" 2 "HDMI1" 3 "eDP1" 4 "eDP1" 5 "eDP1"))
          (exwm-randr-enable)  ;; for the old EXWM 0.28 version
          (exwm-systemtray-enable)
          (exwm-enable)
          ;; (exwm-randr-mode)  ;; I think this would be for the new version
        '';
      };
      stumpwm.enable = false;
    };
    desktopManager = {
      xterm.enable = false;
      plasma5.enable = true;
      xfce = {
        enable = true;
        noDesktop= true;
        enableXfwm = true;
      };
    };
  };
  services.picom.enable = false;  # required for KDE connect but does not work anyways... might be responsible for weird/slow behaviour a couple of minutes after boot
}

These packages are used by my desktop setup

{
  environment.systemPackages = [
    pkgs.wmname
    pkgs.xclip
    pkgs.clipit
    pkgs.escrotum
    pkgs.graphviz
  ];
}

Notification Manager

https://github.com/bsag/nixos-config/blob/330e34c40aba37664bbc20550bf4dd427f0e4788/configuration.nix

{
  environment.systemPackages = with pkgs; [
    dunst
  ];
  systemd.user.services."dunst" = {
    enable = true;
    description = "";
    wantedBy = [ "default.target" ];
    serviceConfig.Restart = "always";
    serviceConfig.RestartSec = 2;
    serviceConfig.ExecStart = "${pkgs.dunst}/bin/dunst";
  };
}

Keyboard & Touchpad

Fix enter and iso3

{
  systemd.services.fix-enter-iso3 = {
    script = ''
      /run/current-system/sw/bin/setkeycodes 0x1c 58  # enter 
      /run/current-system/sw/bin/setkeycodes 0x2b 28  # enter
      /run/current-system/sw/bin/setkeycodes e038 86 # map alt gr to less than/greater than international key. should fix some issues in browser-based excel etc.
    '';
    wantedBy = [ "multi-user.target" ];
  };
  services.xserver.xkb.options= "lv5:rwin_switch_lock,terminate:ctrl_alt_bksp,altwin:swap_lalt_lwin";
}

Layouts

{
  services.xserver.xkb.layout = "de,de,us";
  services.xserver.xkb.variant = "bone,,";

  environment.systemPackages = [ pkgs.xorg.xmodmap ];

  # Use same config for linux console
  console.useXkbConfig = true;
}
{
  services.xserver.xkb.options= "lv5:rwin_switch_lock,terminate:ctrl_alt_bksp,altwin:swap_lalt_lwin";
}
{
  services.xserver.xkb.options= "lv5:rwin_switch_lock,terminate:ctrl_alt_bksp";
}

on normal keyboards I might want to deactivate mod5-locking (see links)

https://askubuntu.com/questions/41213/what-does-key-to-choose-5th-level-in-gnome-keyboard-properties-do nixos-section <- continue here i created the a folder in nixos-config https://nixos.wiki/wiki/Keyboard_Layout_Customization

Speed

{
  services.xserver.autoRepeatDelay = 150;
  services.xserver.autoRepeatInterval = 35;

  # Use same config for linux console
  console.useXkbConfig = true;
}

Layout indicator

Touchpad

{
  # services.xserver.synaptics.enable = true;
  # services.xserver.synaptics.dev = "/dev/input/event7";
  # services.xserver.synaptics.tapButtons = false;
  # services.xserver.synaptics.buttonsMap = [ 1 3 2 ];
  # services.xserver.synaptics.twoFingerScroll = true;
  # services.xserver.synaptics.palmDetect = false;
  # services.xserver.synaptics.accelFactor = "0.001";
  # services.xserver.synaptics.additionalOptions = ''
  #   Option "SHMConfig" "on"
  #   Option "VertScrollDelta" "-100"
  #   Option "HorizScrollDelta" "-100"
  #   Option "Resolution" "370"
  # '';
  services.unclutter = {
    enable = true;
  };
}

Mouse

{
  hardware.logitech.wireless.enable = true;
  hardware.logitech.wireless.enableGraphical = true;
}

Redshift

Redshift adjusts the color temperature of the screen according to the position of the sun.

Blue light blocks melatonin (sleep harmone) secretion, so you feel less sleepy when you stare at computer screen. `Redshift` blocks some blue light (making screen more red), which should improve melatonin secretion and sleepiness (which is a good thing).

NOTE: I disabled it because it doesn’t allow time control. seasons (sunrise, sunset) shouldn’t matter actually

{
  services.redshift = {
    enable = true;
    brightness.night = "1";
    temperature.night = 2800;
    extraOptions = [
      "-l manual"
      "-l 0.0:0.0"
    ];
  };

  location.provider = "geoclue2";
  
  systemd.services.resume-redshift-restart = {
    description = "Restart redshift after resume to workaround bug not reacting after suspend/resume";
    wantedBy = [ "sleep.target" ];
    after = [ "systemd-suspend.service" "systemd-hybrid-sleep.service" "systemd-hibernate.service" ];
    script = ''
      /run/current-system/sw/bin/systemctl restart [email protected] --user redshift
    '';
    serviceConfig.Type = "oneshot";
  };
}

Screen brightness

xbacklight stopped working recently. acpilight is a drop-in replacement.

{
  hardware.acpilight.enable = true;
  environment.systemPackages = [
    pkgs.acpilight
    pkgs.brightnessctl
  ];
  users.users.moritz.extraGroups = [ "video" ];
}

Look and Feel

Fonts

I’m not a font guru, so I just stuffed a bunch of random fonts in here.

{
  fonts = {
    # fontDir.enable = true; # 21.03 rename
    fontDir.enable = true;
    enableGhostscriptFonts = false;

    packages = with pkgs; [
      corefonts
      inconsolata
      dejavu_fonts
      source-code-pro
      ubuntu_font_family
      unifont

      # Used by Emacs
      # input-mono
      libertine
    ];
  };
}

Hi-DPI

Also see https://wiki.archlinux.org/title/HiDPI (e.g. for GDK_SCALE)

Be careful: ~/.spacemacs.d/.spacemacs.env does not update and overwrites env-variables…

xserver-dpi is also controlled in ~/.Xresources <- this influences URXVT and emacs/EXWM itself!

{
  console.packages = [
    pkgs.terminus_font
  ];
  environment.variables = {
    GDK_SCALE = "1"; # this one impacts inkscape and only takes integers (1.3 would be ideal..., 2 is too much..)
    GDK_DPI_SCALE = "1.2"; # this only scales text and can take floats
    QT_SCALE_FACTOR = "1.2";  # this one impacts qutebrowser
    QT_AUTO_SCREEN_SCALE_FACTOR = "1.4";
  };
  console.font = "ter-132n";
}
{
  services.xserver.dpi = 220;
}

This one seems to determine chrome

{
  services.xserver.dpi = 140;  # was 130, 
  services.xserver.upscaleDefaultCursor = true;
}
{
  services.xserver.dpi = 200;
}

Applications

Here go applications (almost) every normal user needs.

SSH

{
  programs.ssh = {
    startAgent = true;
  };
  programs.gnupg.agent = {
    enable = true;
    enableSSHSupport = false;
    pinentryPackage = pkgs.pinentry-qt;
  };

  # is it no longer needed?
  
  # systemd.user.sockets.gpg-agent-ssh = {
  #   wantedBy = [ "sockets.target" ];
  #   listenStreams = [ "%t/gnupg/S.gpg-agent.ssh" ];
  #   socketConfig = {
  #     FileDescriptorName = "ssh";
  #     Service = "gpg-agent.service";
  #     SocketMode = "0600";
  #     DirectoryMode = "0700";
  #   };
  # };

  services.pcscd.enable = true;
}

FileZilla

{
  environment.systemPackages = with pkgs; [
    filezilla
  ];
}

KDEconnect

{
  programs.kdeconnect.enable = false;  # segfaulted unfortunately. probably because it was also enabled in home.nix (https://discourse.nixos.org/t/kernel-panic-how-to-retrieve-logs/49983)
}

DLNA

{
  services.minidlna = {
    enable = true;
    openFirewall = true;
    settings.media_dir= [ "/mnt/ssd2tb/Media/Filme" ];
  };
}

Matrix Chat

{
  environment.systemPackages = with pkgs; [
    element-desktop
  ];
}

password-store

Install password-store along with one-time password extension.

{
  environment.systemPackages = with pkgs; [
    (pass.withExtensions (exts: [ exts.pass-otp ]))
    pinentry-curses
    pinentry-qt
    pinentry-emacs
    expect
  ];
  # services.keepassx.enable = true;
}

KDE apps

I don’t use full KDE but some apps are definitely nice.

{
  environment.systemPackages = [
    pkgs.gwenview
    pkgs.filelight
    pkgs.shared-mime-info
  ];
}

KDE apps might have issues with mime types without this:

{
  environment.pathsToLink = [ "/share" ];
}

Browsers

Google Chrome

Google Chrome used to be my default browser and I still use it from time to time.

{
  programs.browserpass.enable = true;
  environment.systemPackages = [
    # pkgs.google-chrome  # not available for aarch64
    pkgs.chromium
  ];
}

Firefox

I use Firefox Quantum as my default browser now.

{
  environment.systemPackages = [
    (pkgs.firefox.override { nativeMessagingHosts = [ pkgs.passff-host ]; })
  ];
}

Qutebrowser

{
  environment.systemPackages =
    let wrapper = pkgs.writeScriptBin "qutebrowser-niced" ''
        #!${pkgs.stdenv.shell}
        exec nice --adjustment="-6" ${pkgs.qutebrowser}/bin/qutebrowser
        '';
    in
    [ pkgs.qutebrowser wrapper ];
  environment.variables.QUTE_BIB_FILEPATH = "/home/moritz/wiki/papers/references.bib";
}

PDF

Zathura is a cool document viewer with Vim-like bindings.

{
  environment.systemPackages = [
    pkgs.zathura
  ];
}

Enable incremental search (Zathura’s config goes to ~/.config/zathura/zathurarc).

set incremental-search true

These are my rebinding for Workman layout (swap j/k):

map j scroll up
map k scroll down
{
  # environment.systemPackages = with pkgs; [ xournalpp  masterpdfeditor qpdfview sioyek evince adobe-reader pdftk scribus ];  # unstable.sioyek fails tzz
}

Drawing

{
  environment.systemPackages = [
    pkgs.weylus
  ];
  networking.firewall.allowedTCPPorts = [ 1701 9001 ];  # syncthing as well, and FTP; and 5000 for vispr
  users.groups.uinput = {};
  users.users.moritz.extraGroups = [ "uinput" ];
  services.udev.extraRules = ''
    KERNEL=="uinput", MODE="0660", GROUP="uinput", OPTIONS+="static_node=uinput"
  '';
}

Screen locking

Slock

Slock is a simple X display locker and should probably not crash as xscreensaver does.

Slock tries to disable OOM killer (so the locker is not killed when memory is low) and this requires a suid flag for executable. Otherwise, you get the following message:

slock: unable to disable OOM killer. Make sure to suid or sgid slock.
{
  programs.slock.enable = true;
}

xss-lock

xss-lock is a small utility to plug a screen locker into screen saver extension for X. This automatically activates selected screensaver after a period of user inactivity, or when system goes to sleep.

{
  environment.systemPackages = [
    pkgs.xss-lock
  ];
}

Science

{
  environment.systemPackages = with pkgs; [
    igv
  ];
}

Spotify

{
  environment.systemPackages =
    let wrapper = pkgs.writeScriptBin "spotify-highres" ''
      #!${pkgs.stdenv.shell}
      exec ${pkgs.spotify}/bin/spotify --force-device-scale-factor=2
      '';
  in
     [ pkgs.spotify wrapper pkgs.playerctl ];
}

TOR

{
  services.tor.enable = false;
  services.tor.client.enable = false;
  # environment.systemPackages = [ pkgs.tor-browser-bundle-bin ];  # aarch64 not supported
}

Steam

{
  environment.systemPackages = [ pkgs.steam-run pkgs.steam ];
  hardware.opengl.driSupport32Bit = true;
  hardware.opengl.extraPackages32 = with pkgs.pkgsi686Linux; [ libva vaapiIntel];
  hardware.pulseaudio.support32Bit = true;
  programs.steam.package = pkgs.steam.override {
    extraLibraries = pkgs: (with config.hardware.opengl;
      if pkgs.hostPlatform.is64bit
      then [ package ] ++ extraPackages
      else [ package32 ] ++ extraPackages32)
      ++ [ pkgs.libxcrypt ];
  };

}
{
  environment.systemPackages = [ pkgs.steam pkgs.steam-run ];
  hardware.opengl.driSupport32Bit = true;
  hardware.opengl.extraPackages32 = with pkgs.pkgsi686Linux; [ libva vaapiIntel];
  hardware.pulseaudio.support32Bit = true;
}

Latex

{

  environment.systemPackages = with pkgs; [
    #haskellPackages.pandoc
    # jabref
    pandoc
    haskellPackages.pandoc-crossref
    # haskellPackages.pandoc-citeproc  # broken...
    texlive.combined.scheme-full
    perl538Packages.LaTeXML
  ];
}

SuperCollider

{
  environment.systemPackages = [ pkgs.supercollider ];
}

Virtualbox

{
   # virtualisation.virtualbox.host.enable = true;
   users.extraGroups.vboxusers.members = [ "moritz" ];
   virtualisation.virtualbox.host.enableExtensionPack = true;
}

EAF & Node

{
  environment.systemPackages = with pkgs; [
    # qt5Full
    aria
    fd
    wmctrl
    unstable.nodejs_20
    unstable.nodePackages_latest.npm
    unstable.nodePackages_latest.eslint  # required for cellxgene
    mupdf
  ];
  environment.variables.QT_QPA_PLATFORM_PLUGIN_PATH = "${pkgs.qt5.qtbase.bin.outPath}/lib/qt-${pkgs.qt5.qtbase.version}/plugins";  # need to rerun 'spacemacs/force-init-spacemacs-env' after QT updates...
}

Davinci

{
  environment.systemPackages = [
    pkgs.davinci-resolve
  ];
}

Thunar

{
  programs.thunar.enable = true;
  # discussed here: https://github.com/NixOS/nixpkgs/issues/61539
  security.pam.services.emacs.enableGnomeKeyring = true;
  services.gnome.gnome-keyring.enable = true;
}

Other applications

Don’t require additional setup.

{
  environment.systemPackages =
    with pkgs;
    [
    # betaflight-configurator  # TODO nwjs not supported for aarch64 -.-
    # spotdl
    homesick
    miraclecast
    xcolor
    xorg.xgamma
    vlc
    aria
    # jetbrains.pycharm-community  # takes a lot of memeory
    obs-studio
    jmtpfs
    qbittorrent
    # unstable.blender
    rclone
    # teams
    # discord  # no aarch64
    inkscape
    arandr
    dmenu
    # # soulseekqt
    gnome.cheese
    gnome.gnome-screenshot
    # sparkleshare_fixed 
    gnome.gpaste
    autorandr
    libnotify
    feh

    # kdenlive  # fails in current unstable
    audacity
    # tdesktop # Telegram
    signal-cli # Signal
    signal-desktop # Signal
    # unstable.zoom-us
    libreoffice
    # wineWowPackages.stable
    # # winetricks  # requires p7zip (which is unsafe...)
    # gimp-with-plugins  # TODO 

    mplayer
    mpv
    smplayer
    lm_sensors
    tcl
    pymol
    ruby
    vscode
    tesseract

    dotool
    lsof
  ];
}

Default applications

  • State “CANCELLED” from [2021-07-14 Wed 12:52]
    this is done by home.nix
{
  xdg.mime.defaultApplications = {
      "inode/directory" = [ "org.xfce.Thunar.desktop" ];
      "application/pdf" = [ "emacsclient.desktop" ];
      "x-scheme-handler/org-protocol" = [ "org-protocol.desktop" ];
      "x-scheme-handler/msteams" = [ "teams.desktop" ];
      "image/png" = [ "feh.desktop" "org.inkscape.Inkscape.desktop" ]; 
      "image/svg+xml" = [ "org.inkscape.Inkscape.desktop" ];
      "x-scheme-handler/http" = [ "chromium.desktop" ];
      "x-scheme-handler/https" = [ "chromium.desktop" ];
      "x-scheme-handler/about" = [ "chromium.desktop" ];  # Added
      "x-scheme-handler/unknown" = [ "chromium.desktop" ];  # Added
      "image/jpeg" = [ "feh.desktop" "gimp.desktop" ];  # Added
      "video/mp4" = [ "vlc.desktop" ];  # Added
  };
}

Development

Nix

{
  environment.systemPackages = [ pkgs.niv ];
}

Website

{
  environment.systemPackages = [ pkgs.hugo ];
}

Flatpak

{
services.flatpak.enable = true;
}

Editors

I’m a seasoned Vim user, but I’ve switched to emacs.

{
  environment.variables.EDITOR = "vim";
  environment.systemPackages = [
    pkgs.vim_configurable # .override { python3 = true; })
    pkgs.neovim
  ];
}

TODO: I think this one is not called/used since I am using exwm Start emacs as a daemon:

{
  services.emacs =
    let emacsConfig = import .config/nixpkgs/emacs.nix { inherit pkgs; };
    in {
      enable = false;  # TODO
      defaultEditor = true;
      package = emacsConfig.finalEmacs;
    };
  environment.systemPackages = [
    pkgs.ripgrep
    (pkgs.aspellWithDicts (dicts: with dicts; [en en-computers en-science ru uk]))

    # pkgs.rustup
    # pkgs.rustracer

    # pkgs.clojure
    # pkgs.leiningen
  ];
  # environment.variables.RUST_SRC_PATH = "${pkgs.rustPlatform.rustcSrc}";
}

CUDA

{
  environment.systemPackages = [
    pkgs.cudaPackages.cuda_nvcc
  ];
}

Kyria keyboard

{
  # leads to trouble only..
  systemd.services.modem-manager.enable = false;
  systemd.services."dbus-org.freedesktop.ModemManager1".enable = false;
  
  services.udev.extraRules = ''
    # Atmel DFU
    ### ATmega16U2
    SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2fef", TAG+="uaccess"
    ### ATmega32U2
    SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ff0", TAG+="uaccess"
    ### ATmega16U4
    SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ff3", TAG+="uaccess"
    ### ATmega32U4
    SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ff4", TAG+="uaccess"
    ### AT90USB64
    SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ff9", TAG+="uaccess"
    ### AT90USB128
    SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ffb", TAG+="uaccess"
    ### Pro Micro 5V/16MHz
    SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b4f", ATTRS{idProduct}=="9205", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
    ## dog hunter AG
    ### Leonardo
    SUBSYSTEMS=="usb", ATTRS{idVendor}=="2a03", ATTRS{idProduct}=="0036", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
    ### Micro
    SUBSYSTEMS=="usb", ATTRS{idVendor}=="2a03", ATTRS{idProduct}=="0037", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
  '';
  environment.systemPackages = [ pkgs.qmk ];  # TODO might need unstable
}

Micromamba (Conda)

{
  environment.systemPackages = with pkgs; [
    (pkgs.buildFHSUserEnv {
      name = "micromamba-fhs";

      targetPkgs = pkgs: [
        pkgs.micromamba
        # Add other packages if needed
        (pkgs.stdenv.mkDerivation {  # for fish conda initialization
            name = "conda-config-files";
            buildCommand = ''
              mkdir -p $out/etc/fish
              mkdir -p $out/etc/conda
              cp $fishrc $out/etc/fish/config.fish
              cp $basecondaenv $out/etc/conda/base.yaml
              cp $bashrc $out/etc/bashrc.local  # bashrc.local is automatically called by /etc/bashrc
            '';
            bashrc = pkgs.writeTextFile {
              name = "bashrc-file";
              text = ''
                eval "$(micromamba shell hook --shell=posix)"
                micromamba activate  # needed, because in the `profile` script the env does not propagate :|
              '';
            };
            fishrc = pkgs.writeTextFile {
              name = "fishrc-file";
              text = ''
                eval "$(micromamba shell hook --shell=fish)"
                micromamba activate  # needed, because in the `profile` script the env does not propagate :|
              '';
            };
            basecondaenv = pkgs.writeTextFile {
              name = "base-conda-env";
              text = ''
                name: base
                channels:
                  - conda-forge
                  - bioconda
                  - nodefaults
                dependencies:
                  - conda
                  - python=3.12
                  - mamba
                  - virtualenv
                  - ca-certificates
                  - certifi
                  - openssl
                  - openh264
                  - argcomplete
                  - snakemake
                  - openpyxl
                  - samtools
                  - pyreadstat
                  - conda-libmamba-solver
                  - ipdb
                  - nb_conda_kernels
                  - libmamba
                  - notebook
                  - ipywidgets
                  - seaborn
                  - matplotlib
                  - ipython
                prefix: /home/moritz/.mamba
              '';
            };
        })
      ];

      profile = ''
        set -e
        eval "$(micromamba shell hook --shell=posix)"
        export MAMBA_ROOT_PREFIX=/home/moritz/.mamba  # alternative: ${builtins.getEnv "PWD"}/.mamba
        if ! test -d $MAMBA_ROOT_PREFIX; then
            micromamba create --yes -q -n base
            micromamba activate base
            micromamba install --yes -f /etc/conda/base.yaml
        fi
        set +e
      '';

      runScript = "fish";
    })
  ];
}
{
  environment.systemPackages =
    let mamba_shell_kernel_commands = pkgs.writeScript "mamba_environment" ''
      #!${pkgs.stdenv.shell}
      micromamba activate base

      set LOG /tmp/mamba_environ_kernel_output
      set SYMLINK /tmp/mamba_kernel.json
      if [ -L $SYMLINK ]; then
        echo "Warning: Removing symlink to old kernel."
        rm $SYMLINK
      fi

      # Redirect the output of the first command to the named pipe and run it in the background
      jupyter kernel --kernel=python 2> $LOG &

      set PATTERN '/[.a-z0-9/\-]\+.json'
      while ! grep -q "$PATTERN" $LOG; do sleep 0.2; done
      target=$(grep -o $PATTERN $LOG)
      echo $target
      ln -s $target $SYMLINK

      wait
      rm $SYMLINK
    '';
    mamba_command = pkgs.writeScript "mamba_environment" ''
      #!${pkgs.stdenv.shell}
      micromamba "$argv"
    '';
    mamba_shell_cmd = pkgs.writeScript "mamba_environment" ''
      #!${pkgs.stdenv.shell}
      micromamba activate base
      $argv
    '';
    kernel_wrapper = pkgs.writeShellScriptBin "mamba_kernel" ''
      /run/current-system/sw/bin/micromamba-fhs ${mamba_shell_kernel_commands}
    '';  # TODO mamba-shell should be provided via a nix variable
    mamba_wrapper = pkgs.writeShellScriptBin "mamba" ''
      /run/current-system/sw/bin/micromamba-fhs ${mamba_command} "$@"
    '';  # TODO mamba-shell should be provided via a nix variable
    repl_wrapper = pkgs.writeShellScriptBin "mamba_repl" ''
      /run/current-system/sw/bin/micromamba-fhs ${mamba_shell_cmd} "python" "$@"
    '';  # TODO mamba-shell should be provided via a nix variable
    cmd_wrapper = pkgs.writeShellScriptBin "mamba_cmd" ''
      /run/current-system/sw/bin/micromamba-fhs ${mamba_shell_cmd} "$@"
    ''; # TODO mamba-shell should be provided via a nix variable
  in [
    pkgs.mamba kernel_wrapper repl_wrapper cmd_wrapper mamba_wrapper
  ];
}
(_self: _super: { conda = _super.conda.override { extraPkgs = [ _super.libffi_3_3 _super.libffi _super.which _super.libxcrypt ]; }; })  # this is an overlay

rxvt-unicode

I use urxvt as my terminal emulator:

{
  environment.systemPackages = [
    pkgs.rxvt-unicode-unwrapped
  ];
}

Urxvt gets its setting from .Xresources file. If you ever want to reload it on-the-fly, type the following (or press C-c C-c if you’re reading this document in emacs now):

xrdb ~/.Xresources

General setup

See rxvt-unicode documentation for the full reference.

urxvt.loginShell:         true
urxvt.saveLines:         65535
urxvt.urgentOnBell:       true

urxvt.scrollBar:         false
urxvt.scrollTtyOutput:   false
urxvt.scrollTtyKeypress:  true
urxvt.secondaryScroll:    true

The next piece disables annoying message when pressing Ctrl+Shift:

urxvt.iso14755: False

Copy-paste with Ctrl+Shift+C, Ctrl+Shift+V:

From urxvt-perls:

Since version 9.20 rxvt-unicode natively supports copying to and pasting from the CLIPBOARD buffer with the Ctrl-Meta-c and Ctrl-Meta-v key bindings. The clipboard.autocopy setting is provided by the selection_to_clipboard extension shipped with rxvt-unicode.

That means, I don’t need perl extensions at all.

Font

I use Terminus font.

{
  fonts = {
    packages = with pkgs; [
      powerline-fonts
      terminus_font
    ];
  };
}
URxvt.font: -*-terminus-medium-r-normal-*-32-*-*-*-*-*-iso10646-1

Color theme

I like Molokai color theme.

URxvt*background: #101010
URxvt*foreground: #d0d0d0
URxvt*color0:     #101010
URxvt*color1:     #960050
URxvt*color2:     #66aa11
URxvt*color3:     #c47f2c
URxvt*color4:     #30309b
URxvt*color5:     #7e40a5
URxvt*color6:     #3579a8
URxvt*color7:     #9999aa
URxvt*color8:     #303030
URxvt*color9:     #ff0090
URxvt*color10:    #80ff00
URxvt*color11:    #ffba68
URxvt*color12:    #5f5fee
URxvt*color13:    #bb88dd
URxvt*color14:    #4eb4fa
URxvt*color15:    #d0d0d0

fish

fish is a cool shell, I use it as my default for day-to-day work.

{
  programs.fish.enable = true;
  users.defaultUserShell = pkgs.fish;

  environment.systemPackages = [
    pkgs.any-nix-shell
    pkgs.mcfly
  ];
  programs.fish.promptInit = ''
    any-nix-shell fish --info-right | source
  '';

  # a lot more is configured in /home/moritz/.homesick/repos/dotfiles/home/.config/fish/config.fish (tracked with homesick)
}

Vi key bindings

Tangle to .config/fish/functions/fish_user_key_bindings.fish.

function fish_user_key_bindings
    fish_vi_key_bindings

    bind -s j up-or-search
    bind -s k down-or-search
    bind -s -M visual j up-line
    bind -s -M visual k down-line

    bind -s '.' repeat-jump
end

git

{
  environment.systemPackages = [
    pkgs.gitFull
    pkgs.gitg
    pkgs.git-lfs
    pkgs.git-filter-repo
  ];
}

Basic info: my name, email, ui, editor, rerere.

[user]
    name = Moritz Schaefer
    email = [email protected]

[sendemail]
    smtpencryption = ssl
    smtpserver = smtp.gmail.com
    smtpuser = [email protected]
    smtpserverport = 465

[color]
    ui = true

[core]
    editor = vim

[push]
    default = simple

[pull]
    rebase = true

[rebase]
    autostash = true

[rerere]
    enabled = true

[advice]
    detachedHead = false

Configure signing with gpg.

[user]
    signingkey = EB3066C3

[gpg]
    program = gpg2

[push]
    gpgSign = if-asked

I have LOTS of aliases:

[alias]
    cl  = clone
    gh-cl = gh-clone
    cr  = cr-fix
    p   = push
    pl  = pull
    f   = fetch
    fa  = fetch --all
    a   = add
    ap  = add -p
    d   = diff
    dl  = diff HEAD~ HEAD
    ds  = diff --staged
    l   = log --show-signature
    l1  = log -1
    lp  = log -p
    c   = commit
    ca  = commit --amend
    co  = checkout
    cb  = checkout -b
    cm  = checkout origin/master
    de  = checkout --detach
    fco = fetch-checkout
    br  = branch
    s   = status
    re  = reset --hard
    r   = rebase
    rc  = rebase --continue
    ri  = rebase -i
    m   = merge
    t   = tag
    su  = submodule update --init --recursive
    bi  = bisect

Always push to github with ssh keys instead of login/password.

[url "[email protected]:"]
    pushInsteadOf = https://github.com/

tmux

Adapted from https://github.com/srid/nixos-config/blob/master/home/tmux.nix

{
  environment.systemPackages = [
    pkgs.powerline
    pkgs.python311Packages.powerline
  ];
  programs.tmux = {
    enable = true;
    shortcut = if name == "moair" then "n" else "b";
    clock24 = true;
    # aggressiveResize = true; -- Disabled to be iTerm-friendly
    baseIndex = 1;
    newSession = true;
    # Force tmux to use /tmp for sockets (WSL2 compat)
    secureSocket = false;
    terminal = "xterm-256color";  # seems to be the same as screen-256color

    plugins = with pkgs; [
      tmuxPlugins.better-mouse-mode
      tmuxPlugins.jump
      tmuxPlugins.sensible
    ];

    extraConfig = ''
      set -ga terminal-overrides ",*256col*:Tc"
      set -ga terminal-overrides '*:Ss=\E[%p1%d q:Se=\E[ q'
      set-environment -g COLORTERM "truecolor"

      # Mouse works as expected
      set-option -g mouse on
      # easy-to-remember split pane commands
      bind | split-window -h -c "#{pane_current_path}"
      bind - split-window -v -c "#{pane_current_path}"
      bind c new-window

      # copied from old tmux
      set -g bell-action none

      # resize
      bind-key -r C-j resize-pane -D 7
      bind-key -r C-k resize-pane -U 7
      bind-key -r C-l resize-pane -R 7
      bind-key -r C-h resize-pane -L 7

      # copy like vim
      bind-key C-u copy-mode \; send -X halfpage-up
      bind-key v copy-mode

      bind-key -T copy-mode-vi 'v' send -X begin-selection
      bind-key -T copy-mode-vi 'y' send -X copy-selection
      bind-key -T copy-mode-vi 'C-d' send -X halfpage-down
      bind-key -T copy-mode-vi 'C-u' send -X halfpage-up
      # Vim style
      bind-key -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "xsel -i -p && xsel -o -p | xsel -i -b"
      bind-key -n C-v run "xsel -ob | tmux load-buffer - ; tmux paste-buffer"

      # act like vim
      setw -g mode-keys vi
      is_vim="ps -o state= -o comm= -t '#{pane_tty}' \
          | grep -iqE '^[^TXZ ]+ +(\\S+\\/)?g?(view|n?vim?x?)(diff)?$'"
      # bind-key -n Left if-shell "$is_vim" "send-keys C-h"  "select-pane -L"
      # bind-key -n Down if-shell "$is_vim" "send-keys C-j"  "select-pane -D"
      # bind-key -n Up if-shell "$is_vim" "send-keys C-k"  "select-pane -U"
      # bind-key -n Right if-shell "$is_vim" "send-keys C-l"  "select-pane -R"
      # bind-key -n C-h if-shell "$is_vim" "send-keys C-h"  "select-pane -L"
      # bind-key -n C-j if-shell "$is_vim" "send-keys C-j"  "select-pane -D"
      # bind-key -n C-k if-shell "$is_vim" "send-keys C-k"  "select-pane -U"
      # bind-key -n C-l if-shell "$is_vim" "send-keys C-l"  "select-pane -R"
      # bind-key -n C-\ if-shell "$is_vim" "send-keys C-\\" "select-pane -l"

      # start window numbers at 1 to match keyboard order with tmux window order
      set -g base-index 1
      set-window-option -g pane-base-index 1

      # renumber windows sequentially after closing any of them
      set -g renumber-windows on

      # soften status bar color from harsh green to light gray
      set -g status-bg '#666666'
      set -g status-fg '#aaaaaa'

      # remove administrative debris (session name, hostname, time) in status bar
      set -g status-left ""
      set -g status-right ""

      # TODO what are these doing?
      set -g prefix2 none
      # prefix -> back-one-character
      bind-key C-b send-prefix
      # prefix-2 -> forward-incremental-history-search
      bind-key C-s send-prefix -2

      # don't suspend-client
      unbind-key C-z

      # don't kill tab on d
      unbind-key C-d

      # need to redefine next
      bind n next-window

      set-option -g default-shell $SHELL

      # reload config
      bind-key r source-file ~/.tmux.conf \; \
            display-message "source-file done"

      # Local config
      if-shell "[ -f ~/.tmux.conf.local ]" 'source ~/.tmux.conf.local'

      ## set the default TERM

      ## update the TERM variable of terminal emulator when creating a new session or attaching a existing session
      set -g update-environment 'DISPLAY SSH_ASKPASS SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY TERM'

      source-file /run/current-system/sw/lib/python3.11/site-packages/powerline/bindings/tmux/powerline.conf
    '';
  };

}

R language

# TODO override R package  (openssl)
{
  environment.systemPackages = let my-r-packages = with pkgs.rPackages; [ ggplot2 eulerr gridExtra INSPEcT XVector S4Vectors MAGeCKFlute openxlsx tidyverse enrichR];
                                   R-with-my-packages = pkgs.rWrapper.override{ packages = my-r-packages; }; 
                                   RStudio-with-my-packages = pkgs.rstudioWrapper.override{ packages = my-r-packages; };
  in [ R-with-my-packages RStudio-with-my-packages  ];
}

Python

{
  environment.systemPackages =
    let python = (with pkgs; python3.withPackages (python-packages: with python-packages;
      let opencvGtk = opencv4.override (old : { enableGtk2 = true; enableGStreamer = true; });
          eaf-deps = [
            # pyqt5 sip
            # pyqtwebengine
            epc lxml
            # eaf-file-browser
            qrcode
            # eaf-browser
            pysocks
            # eaf-pdf-viewer
            pymupdf
            # eaf-file-manager
            pypinyin
            # eaf-system-monitor
            psutil
            # eaf-markdown-previewer
            retry
            markdown
          ];
          orger-pkgs = [
            orger
            hpi
            pdfannots  # required for pdfs
            datasets  # for twint (twitter)
            twint
          ];
          # orger-pkgs ++   # temporarily disabled because of github installation issue
      in eaf-deps ++ [
      # gseapy
      pymol
      umap-learn
      icecream
      plotly
      pytorch
      # ignite
      # pytorch-lightning
      # pytorch-geometric
      python3
      black
      pandas
      XlsxWriter
      # opencvGtk
      openpyxl
      biopython
      scikitlearn
      wandb
      imageio
      matplotlib
      pyproj
      seaborn
      requests
      pillow
      ipdb
      isort
      tox
      tqdm
      xlrd
      pyyaml
      # matplotlib-venn
      networkx
      statsmodels
      # up-set-plot
      # jedi
      # json-rpc
      # service-factory
      debugpy
      # faster-whisper # fails on moair!
      # private-gpt  # fails on moair!

      fritzconnection
      # jupyter
      # jupyter_core
      powerline
      adjust-text
      # up-set-plot
      # moritzsphd
      tabulate
      # swifter
      # gffutils
      # pyensembl  # fails due to serializable
      # pybedtools
      pybigwig
      xdg
      # importmagic epc  # disabled because it runs ages during startup of emacs
      jupyterlab
      jupyter_console
      ipykernel
      pyperclip
      # scikit-plot
      # scikit-bio
      powerline
      python-lsp-server
      smogn
      docker
      absl-py
      hjson
      pygments
      # ptvsd
      ])); in with pkgs.python3Packages; [
    python  # let is stronger than with, which is why this installs the correct python (the one defined above)
    pkgs.rPackages.orca  # required for plotly
    pkgs.pipenv
    pip
    pkgs.pyright
    python-lsp-server
    selenium
    # pkgs.zlib
    #pkgs.zlib.dev
    # nur-no-pkgs.repos.moritzschaefer.python3Packages.cytoflow
  ];
  # Adding libstdc++ to LD_LIB_PATH to fix some python imports (https://nixos.wiki/wiki/Packaging/Quirks_and_Caveats) # TODO might not work anymore because of libgl?
  # environment.variables.LD_LIBRARY_PATH = with pkgs; "$LD_LIBRARY_PATH:${stdenv.cc.cc.lib}/lib";  # for file libstdc++.so.6  # TODO disabled after 23.11 because it was buggy
}

Package overlay

( let
    myOverride = rec {
      packageOverrides = _self: _super: {
      
        # python-socks = _super.buildPythonPackage rec { # overwrite because too old
        #   pname = "python-socks";
        #   version = "2.0.3";

        #   src = _super.fetchPypi {
        #     inherit pname version;
        #     # sha256 = "e3a9ca8e554733862ce4d8ce1d10efb480fd3a3acdafd03393943ec00c98ba8a"; 2.0.3
        #   };

        #   propagatedBuildInputs = with _super; [ trio curio async-timeout anyio ];
        # };

        # aiohttp-socks-new = _super.buildPythonPackage rec {  # if >=0.7 is needed
        #   pname = "aiohttp-socks";
        #   version = "0.7.1";
        #   propagatedBuildInputs = [ _super.aiohttp _super.attrs _self.python-socks];
        #   doCheck = false;
        #   src = _super.fetchPypi {
        #     inherit version;
        #     pname = "aiohttp_socks";
        #     sha256 = "2215cac4891ef3fa14b7d600ed343ed0f0a670c23b10e4142aa862b3db20341a";
        #   };
        # };
        googletransx = _super.buildPythonPackage rec {
          pname = "googletransx";
          version = "2.4.2";
          propagatedBuildInputs = [ _super.requests ];
          doCheck = false;
          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "c46567e3365c2abbe8af1004121b6303f530bf72025d1c3045ed14861902d6da";
          };
        };
        twint = _super.buildPythonPackage rec {
          pname = "twint";
          version = "2.1.22";
          propagatedBuildInputs = with _super; [ aiohttp aiodns beautifulsoup4 cchardet elasticsearch pysocks pandas aiohttp-socks schedule geopy fake-useragent _self.googletransx ];
          
          postPatch = ''
            substituteInPlace setup.py --replace "dataclasses" ""
          '';
          doCheck = false;
          src = builtins.fetchGit {
            url = "https://github.com/twintproject/twint/";
            rev = "e7c8a0c764f6879188e5c21e25fb6f1f856a7221";
          };
        };
        pdfannots = _super.buildPythonPackage rec {
          pname = "pdfannots";
          version = "0.3";
          propagatedBuildInputs = [ _super.pdfminer ];
          nativeBuildInputes = [ _super.setuptools-scm ];
          doCheck = false;
          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "5931fdab0f06283536b58782bec16109a6c193816d6df0ab737924513ea7ed0a";
          };
        };

        cachew = _super.buildPythonPackage rec {
          pname = "cachew";
          version = "0.9.0";
          propagatedBuildInputs = [ _super.setuptools-scm _super.appdirs _super.sqlalchemy ];
          nativeBuildInputes = [ _super.setuptools-scm ];
          doCheck = false;
          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "8d2b82260e35c48e9c27efc8054c46ff3fe2c0a767e7534f1be2719541b5d8a7";
          };
        };
        hpi =_super.buildPythonPackage rec {
          pname = "HPI";
          version = "0.3.20211031";
          propagatedBuildInputs = [ _super.pytz _super.appdirs _super.more-itertools _super.decorator _super.click _super.setuptools-scm _super.logzero _self.cachew _super.mypy ];  # orjson
          nativeBuildInputes = [ _super.setuptools-scm ];
          SETUPTOOLS_SCM_PRETEND_VERSION = version;
          doCheck = false;
          src = builtins.fetchGit {
            url = "git://github.com/karlicoss/HPI";
            rev = "a1f03f9c028df9d1898de2cc14f1df4fa6d8c471";
          };
        };
        orger =_super.buildPythonPackage rec {
          pname = "orger";
          version = "0.3.20210220";
          propagatedBuildInputs = [ _super.appdirs _super.atomicwrites _super.setuptools-scm ];
          nativeBuildInputes = [ _super.setuptools-scm ];
          doCheck = false;
          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "cb6191e685c91f3bb760b2997c386e0f5e94562d13ab0dc69230c60ddbf52cf0";
          };
        };


        service-factory =_super.buildPythonPackage rec {
          pname = "service_factory";
          version = "0.1.6";
          propagatedBuildInputs = [ _super.pytest ];
          doCheck = false;
          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "abd8e715e2d32ee83ea4bbe365d34e0f94e3068ec03683f09f4512f657e1cd64";
          };
        };
      
        json-rpc =_super.buildPythonPackage rec {
          pname = "json-rpc";
          version = "1.13.0";
          buildInputs = [ _super.pytest ];
          propagatedBuildInputs = [ _super.pytest ];
          doCheck = false;
          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "def0dbcf5b7084fc31d677f2f5990d988d06497f2f47f13024274cfb2d5d7589";
          };
        };
        up-set-plot = _super.buildPythonPackage rec {
          pname = "UpSetPlot";
          version = "0.4.1";
          buildInputs = [ _super.pytestrunner ];
          propagatedBuildInputs = [ _super.matplotlib _super.pandas ];
          doCheck = false;
          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "c1e23af4d90ca88d024cdea45dc3a84591cd97a80a6a3dfc18b5e7ad2b93944f";
          };
        };
        adjust-text = _super.buildPythonPackage rec {
          pname = "adjustText";
          version = "0.7.3";
          propagatedBuildInputs = [ _super.matplotlib _super.numpy ];
          doCheck = false;
          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "b90e275a95b4d980cbbac7967914b8d66477c09bc346a0b3c9e2125bba664b06";
          };
        };
        matplotlib-venn = _super.buildPythonPackage rec {
          version = "0.11.5";
          pname = "matplotlib-venn";

          src = builtins.fetchGit {
            url = "git://github.com/konstantint/matplotlib-venn";
            rev = "c26796c9925bdac512edf48387452fbd1848c791";
          };

          checkInputs = [ _super.pytest ];
          propagatedBuildInputs = [ _super.matplotlib _super.numpy _super.scipy ];

          checkPhase = ''
            pytest
          '';

          # Tests require extra dependencies
          doCheck = false;

          # meta = with stdenv.lib; {
          #   homepage = "https://github.com/konstantint/matplotlib-venn";
          #   description = "Area-weighted venn-diagrams for Python/matplotlib";
          #   license = licenses.mit;
          # };
        };
        swifter = _super.buildPythonPackage rec {
          version = "0.304";
          pname = "swifter";

          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "5fe99d18e8716e82bce5a76322437d180c25ef1e29f1e4c5d5dd007928a316e9";
          };

          checkInputs = [ _super.nose ];
          propagatedBuildInputs = [ _super.pandas _super.psutil _super.dask _super.tqdm
                                    _super.ipywidgets _super.numba _super.bleach
                                    _super.parso _super.distributed ];

          disabled = _super.pythonOlder "3.7";

          pythonImportsCheck = [ "swifter" ];
          checkPhase = ''
            nosetests
          '';

          # Tests require extra dependencies
          doCheck = true;

          # meta = with stdenv.lib; {
          #   homepage = "https://github.com/jmcarpenter2/swifter";
          #   description = "A package which efficiently applies any function to a pandas dataframe or series in the fastest available manner";
          #   license = licenses.mit;
          #   maintainers = [ maintainers.moritzs ];
          # };
        };
        # pyensembl = _super. buildPythonPackage rec {
        #   version = "1.8.5";
        #   pname = "pyensembl";

        #   src = _super.fetchPypi {
        #     inherit pname version;
        #     sha256 = "13dd05aba296e4acadb14de5a974e6f73834452851a36b9237917ae85b3e060f";
        #   };

        #   propagatedBuildInputs = [ _super.numpy _super.pandas _self.datacache _super.six _self.memoized-property
        #                             _self.gtfparse _self.tinytimer _self.serializable ];

        #   # pythonImportsCheck = [ "pyensembl" ];
        #   doCheck = false;  # import fails (only) in build environment because pyensembl creates a file in root directory

        #   # meta = with stdenv.lib; {
        #   #   homepage = "https://github.com/openvax/pyensembl";
        #   #   description = " Python interface to access reference genome features (such as genes, transcripts, and exons) from Ensembl ";
        #   #   license = licenses.asl20;
        #   #   maintainers = [ maintainers.moritzs ];
        #   # };
        # };
        gffutils = _super.buildPythonPackage rec {
          version = "0.10.1";
          pname = "gffutils";

          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "a8fc39006d7aa353147238160640e2210b168f7849cb99896be3fc9441e351cb";
          };


          checkInputs = [ _super.nose _super.wget ];
          propagatedBuildInputs = [ _super.pyfaidx _super.six _super.argh _super.argcomplete _super.simplejson ];
          doCheck = false;

          # checkPhase = ''  # unfortunately fails
          #   # sh gffutils/test/data/download-large-annotation-files.sh
          #   # nosetests
          # '';
          pythonImportsCheck = [ "gffutils" ];

          # meta = with stdenv.lib; {
          #   homepage = "https://github.com/daler/gffutils";
          #   description = "GFF and GTF file manipulation and interconversion http://daler.github.io/gffutils";
          #   license = licenses.mit;
          #   maintainers = [ maintainers.moritzs ];
          # };
        };
        gtfparse = _super.buildPythonPackage rec {
          version = "1.2.0";
          pname = "gtfparse";

          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "2f27aa2b87eb43d613edabf27f9c11147dc595c8683b440ac1d88e9acdb85873";
          };

          checkInputs = [ _super.nose _super.six ];
          propagatedBuildInputs = [ _super.numpy _super.pandas ];
          doCheck = false;

          pythonImportsCheck = [ "gtfparse" ];
          # checkPhase = ''
          #   # PYTHONPATH='test' nosetests # fails because six is not found
          # '';

          # meta = with stdenv.lib; {
          #   homepage = "https://github.com/openvax/gtfparse";
          #   description = " Parsing tools for GTF (gene transfer format) files ";
          #   license = licenses.asl20;
          #   maintainers = [ maintainers.moritzs ];
          # };
        };
        memoized-property = _super.buildPythonPackage rec {
          version = "1.0.3";
          pname = "memoized-property";

          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "4be4d0209944b9b9b678dae9d7e312249fe2e6fb8bdc9bdaa1da4de324f0fcf5";
          };


          pythonImportsCheck = [ "memoized_property" ];
          doCheck = false;

          # meta = with stdenv.lib; {
          #   homepage = "https://github.com/estebistec/python-memoized-property";
          #   description = "A simple python decorator for defining properties that only run their fget function once ";
          #   license = licenses.bsd3;
          #   maintainers = [ maintainers.moritzs ];
          # };
        };
        # pybedtools = _super.buildPythonPackage rec {
        #   version = "0.8.1";
        #   pname = "pybedtools";

        #   src = _super.fetchPypi {
        #     inherit pname version;
        #     sha256 = "c035e078617f94720eb627e20c91f2377a7bd9158a137872a6ac88f800898593";
        #   };

        #   checkInputs = [ _super.pytest _super.numpydoc _super.psutil _super.pyyaml _super.sphinx ];
        #   propagatedBuildInputs = [ _super.numpy _super.pandas _super.pysam _super.six pkgs.zlib pkgs.bash pkgs.bedtools ];  # Is it OK to use pkgs here?

        #   checkPhase = ''
        #     # pytest -v --doctest-modules
        #     # ${_super.python.interpreter} -c 'import pybedtools'  # test and import do not work in checkPhase, because the built pyx file cannot be included
        #   '';

        #   # Tests require extra dependencies
        #   doCheck = false;

        #   # meta = with stdenv.lib; {
        #   #   homepage = "https://github.com/daler/pybedtools";
        #   #   description = "Python wrapper -- and more -- for Aaron Quinlan's BEDTools (bioinformatics tools) http://daler.github.io/pybedtools";
        #   #   license = licenses.gpl2;
        #   # };
        # };
        scikit-plot = _super.buildPythonPackage rec {
          version = "0.3.7";
          pname = "scikit-plot";

          src = builtins.fetchGit {
            url = "https://github.com/moritzschaefer/scikit-plot";
            ref = "feature/label-dots";
            rev = "70ea50616366c87ef730f53efb192217b725a9f0";
          };
          
          # src = _super.fetchPypi {
          #   inherit pname version;
          #   sha256 = "2c7948817fd2dc06879cfe3c1fdde56a8e71fa5ac626ffbe79f043650baa6242";
          # };

          checkInputs = [ _super.nose ];
          propagatedBuildInputs = [ _super.matplotlib _self.scikitlearn _super.scipy _super.joblib ];

          checkPhase = ''
            nosetests
          '';
        };
        datacache = _super.buildPythonPackage rec {
          version = "1.1.5";
          pname = "datacache";

          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "b2ca31b2b9d3803a49645ab4f5b30fdd0820e833a81a6952b4ec3a68c8ee24a7";
          };

          propagatedBuildInputs = [ _super.pandas _super.appdirs _super.progressbar33 _super.requests _self.typechecks _super.mock ];

          pythonImportsCheck = [ "datacache" ];
        };
        # serializable = _super.buildPythonPackage rec {
        #   version = "0.2.1";
        #   pname = "serializable";

        #   src = _super.fetchPypi {
        #     inherit pname version;
        #     sha256 = "ec604e5df0c1236c06d190043a407495c4412dd6b6fd3b45a8514518173ed961";
        #   };

        #   checkInputs = [ _super.nose ];
        #   propagatedBuildInputs = [ _self.typechecks _super.six _super.simplejson ];

        #   checkPhase = ''
        #     nosetests
        #   '';

        #   # meta = with stdenv.lib; {
        #   #   homepage = "https://github.com/iskandr/serializable";
        #   #   description = "Base class with serialization methods for user-defined Python objects";
        #   #   license = licenses.asl20;
        #   #   maintainers = [ maintainers.moritzs ];
        #   # };
        # };
        tinytimer = _super.buildPythonPackage rec {
          version = "0.0.0";
          pname = "tinytimer";

          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "6ad13c8f01ab6094e58081a5367ffc4c5831f2d6b29034d2434d8ae106308fa5";
          };

          pythonImportsCheck = [ "tinytimer" ];

          # meta = with stdenv.lib; {
          #   homepage = "https://github.com/iskandr/tinytimer";
          #   description = "Tiny Python benchmarking library";
          #   license = licenses.asl20;
          #   maintainers = [ maintainers.moritzs ];
          # };
        };
        typechecks = _super.buildPythonPackage rec {
          version = "0.1.0";
          pname = "typechecks";

          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "7d801a6018f60d2a10aa3debc3af65f590c96c455de67159f39b9b183107c83b";
          };

          pythonImportsCheck = [ "typechecks" ];

          # meta = with stdenv.lib; {
          #   homepage = "https://github.com/openvax/typechecks";
          #   description = "Helper functions for runtime type checking";
          #   license = licenses.asl20;
          #   maintainers = [ maintainers.moritzs ];
          # };
        };
        easydev = _super.buildPythonPackage rec {
          version = "0.12.0";
          pname = "easydev";

          propagatedBuildInputs = [
              _super.colorama
              _super.pexpect
              _super.colorlog
              ];
          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "f4a340c5ffe193654c387d271bcd466d1fe56bf9850f2704122d3b52b1e6090d";
          };
          checkPhase = ''
            
          '';
        };
        # attrs = _super.buildPythonPackage rec {
        #   pname = "attrs";
        #   version = "21.2.0";

        #   src = _super.fetchPypi {
        #     inherit pname version;
        #     sha256 = "ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb";
        #   };

        #   # macOS needs clang for testing
        #   checkInputs = [
        #     _super.pytest _super.hypothesis _super.zope_interface _super.pympler _super.coverage _super.six
        #   ];

        #   checkPhase = ''
        #     py.test
        #   '';

        #   # To prevent infinite recursion with pytest
        #   doCheck = false;

        # };
        requests-cache = _super.buildPythonPackage rec {
          version = "0.8.0";
          pname = "requests-cache";

          propagatedBuildInputs = [
              _super.requests
              _super.appdirs
              _super.attrs
              _super.cattrs
              _super.url-normalize
              ];
          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "2f80b2a43d6bb886558181133d9b74db12f1eed42c190b53d8e98ab62a0d2231";
          };
          checkPhase = ''
            
          '';
        };

        bioservices = _super.buildPythonPackage rec {
          version = "1.8.0";
          pname = "bioservices";

          propagatedBuildInputs = [
              _super.grequests
              _super.requests
              _self.requests-cache
              _self.easydev
              _super.beautifulsoup4
              _super.xmltodict
              _super.lxml
              _super.suds-jurko
              _super.appdirs
              _super.wrapt
              _super.pandas
              _super.colorlog
              ];
          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "e581f7096b0083afa1e9d5b075c46b5a8e042767ca0fedb617daa50d1e1a739f";
          };
          checkPhase = ''
            
          '';
          # pythonImportsCheck = [ "smogn" ];
        };

        gseapy = _super.buildPythonPackage rec {
          version = "0.10.4";
          pname = "gseapy";

          propagatedBuildInputs = [
              _super.scipy
              _super.matplotlib
              _super.requests
              _super.joblib
              _self.bioservices
              _self.numpy
              _super.pandas ];
          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "6404b79a3b5dc07ed39f6a4f67b3c662df5bd8b0d50829c2819d8921a768dffb";
          };
          checkPhase = ''
            
          '';
        };
        smogn = _super.buildPythonPackage rec {
          version = "0.1.2";
          pname = "smogn";

          propagatedBuildInputs = [ _self.numpy _super.pandas _super.tqdm ];
          src = _super.fetchPypi {
            inherit pname version;
            sha256 = "6555b907f2c9df223eae8813abd09054ad6491fc8509a23fccc9d578b3e76d89";
          };
          checkPhase = ''
            
          '';
          # pythonImportsCheck = [ "smogn" ];
        };
        # I don't know how to overwrite seaborn from unstable. That's why I overwrite it manually..
        # seaborn = _super.buildPythonPackage rec {
        #   pname = "seaborn";
        #   version = "0.11.1";
        #   disabled = _super.pythonOlder "3.6";
        #   doCheck = false;

        #   src = _super.fetchPypi {
        #     inherit pname version;
        #     sha256 = "44e78eaed937c5a87fc7a892c329a7cc091060b67ebd1d0d306b446a74ba01ad";
        #   };

        #   checkInputs = [ _super.nose ];
        #   propagatedBuildInputs = [ _super.pandas _super.matplotlib ];
        # };
        # scikitlearn_0241 = _super.buildPythonPackage rec {
        #   pname = "scikit-learn";
        #   version = "0.24.1";
        #   doCheck = false;

        #   src = _super.fetchPypi {
        #     inherit pname version;
        #     sha256 = "oDNKGALmTWVgIsO/q1anP71r9LEpg0PzaIryFRgQu98=";
        #   };

        #   buildInputs = [
        #     _super.pillow
        #     pkgs.gfortran
        #     pkgs.glibcLocales
        #   ] ++ pkgs.lib.optionals pkgs.stdenv.cc.isClang [
        #     pkgs.llvmPackages.openmp
        #   ];

        #   nativeBuildInputs = [
        #     _super.cython
        #   ];

        #   propagatedBuildInputs = [
        #     _super.numpy
        #     _super.scipy
        #     _super.numpy.blas
        #     _super.joblib
        #     _super.threadpoolctl
        #   ];
        #   LC_ALL="en_US.UTF-8";
        # };
      };
    };
  in _self: _super: rec {
    # Add an override for each required python version. 
    # There’s currently no way to add a package that’s automatically picked up by 
    # all python versions, besides editing python-packages.nix
    python2 = _super.python2.override myOverride;
    python3 = _super.python3.override myOverride;
    python38 = _super.python38.override myOverride;
    python2Packages = python2.pkgs;
    python3Packages = python3.pkgs;
    # python37Packages = python37.pkgs;
    python38Packages = python38.pkgs;
  } )

Clojure

{
  environment.systemPackages = with pkgs; [ clojure leiningen ];
}

Compilers & Libraries

{
  environment.systemPackages = with pkgs; [
    libGL
    zlib
    zstd
    gcc
    pkg-config
    autoconf
    clang-tools
    automake
    autoconf-archive
    libtool
    zeromq
  ];
}

Biotools

{
  environment.systemPackages = with pkgs; [
    bedtools
  ];
}

ESPHome

{
  environment.systemPackages = [ pkgs.unstable.esphome ];  # 1.15.0 fixes bug
 
  # from https://raw.githubusercontent.com/platformio/platformio-core/master/scripts/99-platformio-udev.rules
  # QinHeng Electronics HL-340 USB-Serial adapter
  services.udev.extraRules = ''
    #  CP210X USB UART
    ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"

    # FT231XS USB UART
    ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6015", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"

    # Prolific Technology, Inc. PL2303 Serial Port
    ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"

    # QinHeng Electronics HL-340 USB-Serial adapter
    ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"

    # Arduino boards
    ATTRS{idVendor}=="2341", ATTRS{idProduct}=="[08][02]*", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
    ATTRS{idVendor}=="2a03", ATTRS{idProduct}=="[08][02]*", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"

    # Arduino SAM-BA
    ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="6124", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{MTP_NO_PROBE}="1"

    # Digistump boards
    ATTRS{idVendor}=="16d0", ATTRS{idProduct}=="0753", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"

    # Maple with DFU
    ATTRS{idVendor}=="1eaf", ATTRS{idProduct}=="000[34]", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"

    # USBtiny
    ATTRS{idProduct}=="0c9f", ATTRS{idVendor}=="1781", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"

    # USBasp V2.0
    ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="05dc", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"

    # Teensy boards
    ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
    ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789A]?", ENV{MTP_NO_PROBE}="1"
    SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789ABCD]?", MODE:="0666"
    KERNEL=="ttyACM*", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", MODE:="0666"

    #TI Stellaris Launchpad
    ATTRS{idVendor}=="1cbe", ATTRS{idProduct}=="00fd", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"

    #TI MSP430 Launchpad
    ATTRS{idVendor}=="0451", ATTRS{idProduct}=="f432", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"

    #GD32V DFU Bootloader
    ATTRS{idVendor}=="28e9", ATTRS{idProduct}=="0189", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
    '';
}

Arduino

{
  environment.systemPackages = [ pkgs.arduino ];
  users.users.moritz.extraGroups = [ "dialout" ];
}

Other terminal goodies

{
  environment.systemPackages = with pkgs; [
    cookiecutter
    nix-index
    # gpu-burn
    gdrive
    tldr
    nmap
    sqlite
    gitAndTools.hub
    yt-dlp
    sshfs
    bash
    wget
    htop
    glances
    psmisc
    zip
    p7zip
    unzip
    unrar
    bind
    file
    which
    # utillinuxCurses
    powerstat
    pciutils
    silver-searcher
    ispell
    usbutils
    libv4l
    v4l-utils
    gparted
    # etcher
    powerline-fonts
    xsel
    tree
    gitAndTools.diff-so-fancy
    gitAndTools.git-hub
    # pypi2nix
    lsyncd
    gnupg
    imagemagick
    gdb
    ncdu
    mesa-demos


    patchelf

    cmake
    gnumake
    jq

  ];
  environment.variables.SNAKEMAKE_CONDA_PREFIX = "/home/moritz/.conda";
  environment.variables.SNAKEMAKE_PROFILE = "default";
  # environment.variables.NPM_CONFIG_PREFIX = "$HOME/.npm-global";
  # environment.variables.PATH = "$HOME/.npm-global/bin:$PATH";
}

Man pages

This install a number of default man pages for the linux/posix system.

{
  documentation = {
    man.enable = true;
    dev.enable = true;
  };

  environment.systemPackages = [
    pkgs.man-pages
    pkgs.stdman
    pkgs.posix_man_pages
    pkgs.stdmanpages
    ];
}

Homeserver

Networking

{
  networking = {
    hostName = "moxps";
    useDHCP = false;
    interfaces.eth0.useDHCP = true;
    interfaces.wlp2s0.useDHCP = true;
    wireless.enable = false;
    networkmanager.enable = true;
    nat = {  # NAT for wireguard
      enable = true;
      externalInterface = "wlp2s0";
      internalInterfaces = [ "wg0" ];
    };
    firewall.allowedUDPPorts = [ 51820 ];
    firewall.allowedTCPPorts = [ 51821 8384 21 5000 8086 ];  # syncthing as well, and FTP; and 5000 for vispr; and influxdb2
  };

  services.nscd.enable = true;
  systemd.services.sshd.wantedBy = pkgs.lib.mkForce [ "multi-user.target" ];

  services.openssh = {
    enable = true;
    settings = {
      PermitRootLogin = "yes";
      PasswordAuthentication = false;
    };
  };
}

Wireguard

{
  age.secrets.server_wireguard_private.file = "/home/moritz/nixos-config/secrets/wireguard_server_private_key.age";
  networking.wireguard.interfaces = {
    # "wg0" is the network interface name. You can name the interface arbitrarily.
    wg0 = {
      # Determines the IP address and subnet of the server's end of the tunnel interface.
      ips = [ "10.100.0.1/24" ];

      # The port that WireGuard listens to. Must be accessible by the client.
      listenPort = 51820;

      # This allows the wireguard server to route your traffic to the internet and hence be like a VPN
      # For this to work you have to set the dnsserver IP of your router (or dnsserver of choice) in your clients
      postSetup = ''
        ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 10.100.0.0/24 -o wlp2s0 -j MASQUERADE
      '';

      # This undoes the above command
      postShutdown = ''
        ${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s 10.100.0.0/24 -o wlp2s0 -j MASQUERADE
      '';

      # Path to the private key file.
      #
      # Note: The private key can also be included inline via the privateKey option,
      # but this makes the private key world-readable; thus, using privateKeyFile is
      # recommended.
      privateKeyFile = config.age.secrets.server_wireguard_private.path;
      # server public key is KYF+BBuoY7dNYswft+vhlNrAKjAkMIMYnkhBbHcH7Dw=
      peers = [
        # List of allowed peers.
        { # Phone
          # Public key of the peer (not a file path).
          publicKey = "iHSpBC8syb+vybBX02aDuIL16uyWfoMbljInRtvuEzU=";
          # List of IPs assigned to this peer within the tunnel subnet. Used to configure routing.
          allowedIPs = [ "10.100.0.2/32" ];
        }
        { # Mopad & Moair
          publicKey = "zdDbsCZd65as6OwRlT/PgfgDju9LwpjRhpCRIrfMhWU=";
          # List of IPs assigned to this peer within the tunnel subnet. Used to configure routing.
          allowedIPs = [ "10.100.0.3/32" ];
        }
      ];
    };
  };
}

DuckDNS

{
  age.secrets.duckdns_password.file = "/home/moritz/nixos-config/secrets/duckdns_password.age";
  services.ddclient = {
    enable = true;
    domains = [ "moritzs.duckdns.org" ];
    protocol = "duckdns";
    server = "www.duckdns.org";
    username = "nouser";
    passwordFile = config.age.secrets.duckdns_password.path;
  };

  # TODO I might need nginx to setup letsencrypt (see monix)
}

FTP server

{
  # FTP server
  services.vsftpd = {
    enable = false;
#   cannot chroot && write
#    chrootlocalUser = true;
    writeEnable = true;
    localUsers = true;
    userlist = [ "moritz" ];
    anonymousUserHome = "/mnt/hdd3tb/ftp_anon/";
    userlistEnable = true;
    anonymousUser = true;
    anonymousUploadEnable = true;
    anonymousMkdirEnable = true;
  };
  # networking.firewall.allowedTCPPorts = [ 21 ]; # defined elsewhere
  services.vsftpd.extraConfig = ''
	  pasv_enable=Yes
	  pasv_min_port=51000
	  pasv_max_port=51800
	  '';
  networking.firewall.allowedTCPPortRanges = [ { from = 51000; to = 51800; } ];
}

Node-red TODO

{
  services.node-red = {
    enable = true;
    openFirewall = true;
    withNpmAndGcc = true;
  };
}

Pihole

{
  config.virtualisation.oci-containers.containers.pihole = let serverIP = "192.168.0.10"; in {
    image = "pihole/pihole:latest";
    ports = [
      "${serverIP}:53:53/tcp"
      "${serverIP}:53:53/udp"
      "3080:80"
      "30443:443"
    ];
    volumes = [
      "/var/lib/pihole/:/etc/pihole/"
      "/var/lib/dnsmasq.d:/etc/dnsmasq.d/"
    ];
    environment = {
      ServerIP = serverIP;
    };
    extraOptions = [
      "--cap-add=NET_ADMIN"
      "--dns=127.0.0.1"
      "--dns=1.1.1.1"
    ];
    workdir = "/var/lib/pihole/";
  };
}

Rhasspy TODO

{
  config.virtualisation.oci-containers.containers = {
    rhasspy = {
      image = "rhasspy/rhasspy";
      ports = [ "0.0.0.0:12101:12101" "11111:11111/udp" ];
      extraOptions = ["--device=/dev/snd:/dev/snd"];
      cmd = [ "--user-profiles" "/profiles" "--profile" "en"];
      volumes = [
        "/home/moritz/.config/rhasspy/profiles:/profiles"
        "/etc/localtime:/etc/localtime:ro"
        "/etc/asound.conf:/etc/asound.conf:ro"
      ];
    };
  };
}

Homeassistant & DeConz

influxdb2 was initialized manually: username: moritz, password: moritzsch Organization: HomeAssistant Bucket Home Assistant

{

  environment.systemPackages = with pkgs; [ alsa-utils ];
  virtualisation.oci-containers = {
    # backend = "podman";
    containers = {
      homeassistant = {
        volumes = [ "/var/lib/hass:/config" ];
        environment.TZ = "Europe/Berlin";
        ports = [ "0.0.0.0:8123:8123" ];
        hostname = "homeassistant";
        image = "ghcr.io/home-assistant/home-assistant:stable";  # Warning: if the tag does not change, the image will not be updated
        extraOptions = [
          "--network=hass"
          # "--device=/dev/ttyACM0:/dev/ttyACM0"  # Example, change this to match your own hardware
          # "--device=/dev/ttyUSB0:/dev/ttyUSB0"  # Example, change this to match your own hardware
        ];
      };
      deconz = {
        image = "deconzcommunity/deconz";
        ports = [ "0.0.0.0:8124:80" "0.0.0.0:8125:443" ];
        extraOptions = [ "--device=/dev/ttyUSB0:/dev/ttyUSB0:rwm"  "--expose" "5900" "--expose" "6080" "--network=hass" ];  # I think the exposes can be deleted
        volumes = [
          "/var/lib/deconz:/opt/deCONZ"
          "/etc/localtime:/etc/localtime:ro"
        ];
      };
      wyoming-whisper = {
        image = "rhasspy/wyoming-whisper";
        ports = [ "0.0.0.0:10300:10300" ];
        extraOptions = ["--network=hass"];
        hostname = "wyoming-whisper";
        volumes = [
          "/var/lib/wyoming-whisper:/data"
        ];
        cmd = [ "--model" "base" ];  #  tiny-int8, timy, base-int8, base, and small-int8
      };
      wyoming-piper = {
        image = "rhasspy/wyoming-piper";
        ports = [ "0.0.0.0:10200:10200" ];
        extraOptions = ["--network=hass"];
        hostname = "wyoming-piper";
        volumes = [
          "/var/lib/wyoming-piper:/data"
        ];
        cmd = [ "--voice" "en_US-lessac-medium" ];
      };
      openwakeword = {
        image = "rhasspy/wyoming-openwakeword";
        extraOptions = ["--network=hass"];
        hostname = "openwakeword";
        volumes = [
          "/var/lib/openwakeword:/data"
          # "./wakeword:/custom"
          # "/etc/timezone:/etc/timezone:ro"  # nonexistent on our system
          "/etc/localtime:/etc/localtime:ro"
        ];
        environment.TZ = "Europe/Vienna";
        cmd = [ "--preload-model" "ok_nabu"];  #  "--custom-model-dir" "/custom" 
      };
      # TODO improve by adding configs here: https://github.com/rhasspy/wyoming-satellite (e.g. audio enhancements)
      wyoming-satellite = {
        image = "satellite";  # TODO reference the dockerfile here /home/moritz/wyoming-satellite/Dockerfile
        ports = [ "0.0.0.0:10700:10700" ];
        hostname = "wyoming-satellite";
        extraOptions = [
          "--network=hass"
          "--device=/dev/snd:/dev/snd"
          "--group-add=audio"
        ];
        cmd = [
          "--name" "living_room"
          "--mic-command" "arecord -D plughw:1,0 -r 16000 -c 1 -f S16_LE -t raw"
          "--snd-command" "aplay -D plughw:0,0 -r 22050 -c 1 -f S16_LE -t raw"
          # "--debug"
        ];
      };
    };
  };
}

}; }; services.influxdb2 = { enable = true; };

}

Borg

TODO 1: make sure that sdd2b is accessible (i.e. mounted) TODO 2: make sure to remount the two things if they drop out!

{
  services.borgbackup.jobs =
    let common-excludes = [
          # Largest cache dirs
          "*/venv"
          "*/.venv"
          "*/.conda"
        ];
        borg-dirs = {
          wiki="/home/moritz/wiki";
          wiki_git="/home/moritz/wiki_git";
          media="/mnt/ssd2tb/Media";
          # moxps-home="/home/moritz";
          var-lib="/var/lib";
          var-backup="/var/backup";  # maybe postgresql
        };
        basicBorgJob = name: {
          encryption.mode = "none";
          # environment.BORG_RSH = "ssh -o 'StrictHostKeyChecking=no' -i /home/moritz/.ssh/id_ed25519";
          environment.BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK = "yes";
          extraCreateArgs = "--verbose --stats --checkpoint-interval 600";
          repo = "/mnt/hdd3tb/borg/${name}";
          compression = "zstd,1";
          startAt = "daily";  # this means "*-*-* 00:00:00"
          user = if builtins.match "^/var" name != null then "root" else "moritz";
          prune.keep = {
            within = "1d"; # Keep all archives from the last day
            daily = 7;
            weekly = 4;
            monthly = 6; # half a year monthly
            yearly = -1; # every year one backup forever (maybe I should change this at some point?)
          };
        };
  in builtins.mapAttrs (name: value:
    basicBorgJob name // rec {
      paths = value;
      exclude = map (x: paths + "/" + x) common-excludes;
    }) borg-dirs;
}

Nature filter

{
  services.nature_filter = {
    enable = true; # feedly fucks
  };
}

Samba

{
  services.samba = {
    enable = true;
    securityType = "user";
    openFirewall = true;
    extraConfig = ''
      workgroup = WORKGROUP
      wins support = yes
      server string = smbnix
      netbios name = smbnix
      security = user
      #use sendfile = yes
      #max protocol = smb2
      hosts allow = 192.168.0.1/24 10.100.0.1/24 localhost
      hosts deny = 0.0.0.0/0
      guest account = nobody
      map to guest = bad user
    '';
    shares = {
      ssd2tb = {
        "path" = "/mnt/ssd2tb";
        "guest ok" = "yes";
        "read only" = "no";
        "browseable" = "yes";
        "create mask" = "0644";
        "directory mask" = "0755";
        "force user" = "moritz";
        "force group" = "users";
      };
      moritz = {
        path = "/home/moritz/";
        browseable = "yes";
        "read only" = "no";
        "guest ok" = "no";
        "create mask" = "0644";
        "directory mask" = "0755";
        "force user" = "moritz";
        "force group" = "users";
      };
    };
  };
  services.samba-wsdd = {
    enable = true;
    openFirewall = true;
  };
}

Smart Glass parsing

{
  environment.systemPackages = with pkgs; [
    openai-whisper
    openai-whisper-cpp
    whisper-ctranslate2
  ];

  systemd.services.smart-glass-parsing = {
    description = "Smart Glass MP4 Parsing";
    after = [ "network.target" ];
    wantedBy = [ "multi-user.target" ];
    # unitConfig = {
    #   StartLimitBurst = "0";  # set to 0 to avoid failure. Note 
    # };
    serviceConfig = {
      Type = "simple";
      ExecStart = let
        script = pkgs.writeShellScript "smart-glass-parsing" ''
        #!/bin/sh
        set -e
          VIDEO_DIRECTORY="$HOME/Smart Glasses"
          TARGET_DIRECTORY="$HOME/wiki/video_notes"
          ORG_MODE_FILE="$HOME/wiki/gtd/video_notes.org"
          for file in "$VIDEO_DIRECTORY"/*.mp4; do
            [ -e "$file" ] || continue
            base_name=$(basename "$file" .mp4)  # this is something like 20240407_202217_959eea70.txt
            txt_file="$TARGET_DIRECTORY/$base_name.txt"
            if [ ! -f "$txt_file" ]; then
              ${pkgs.whisper-ctranslate2}/bin/whisper-ctranslate2 --model base.en -f txt --compute_type int8 --threads 4 --logprob_threshold 0.2  --no_speech_threshold 0.3 -f txt -o $TARGET_DIRECTORY "$file"
              # append to the org mode file
              echo "* TODO [[file:$file][$base_name]]" >> "$ORG_MODE_FILE"
              echo "#+created_at: $(date +"[%Y-%m-%d %a %H:%M]")" >> "$ORG_MODE_FILE"
              echo ":PROPERTIES:" >> "$ORG_MODE_FILE"
              echo ":VIDEO_CREATED: $(echo $base_name | sed -E 's/([0-9]{4})([0-9]{2})([0-9]{2})_([0-9]{2})([0-9]{2})([0-9]{2})_.*/[\1-\2-\3 \4:\5:\6]/')" >> "$ORG_MODE_FILE"
              echo ":VIDEO_FILE: $file" >> "$ORG_MODE_FILE"
              echo ":END:" >> "$ORG_MODE_FILE"
              cat "$txt_file" >> "$ORG_MODE_FILE"
              echo "" >> "$ORG_MODE_FILE"
            fi
          done
        '';
      in "${script}";
      User = "moritz";
      Restart = "no";
    };
  };

  systemd.timers."smart-glass-parsing-timer" = {
    wantedBy = [ "timers.target" ];
    timerConfig = {
      OnBootSec = "10min";
      OnUnitInactiveSec = "10min";
      #  OnCalendar = "*:0/10";  # alternative (ever 10 mins)
      Unit = "smart-glass-parsing.service";
    };
  };
  # This led to unit-start-limit-hit'.
  # systemd.paths.smart-glass-watcher = {  
  #   description = "Watch Smart Glasses Video Directory for New Files";
  #   pathConfig = {
  #     PathExistsGlob = "/home/moritz/Smart Glasses/*.mp4";  # TODO better watch the directory (how cares if it is triggered for mp4s)?
  #     Unit = "smart-glass-parsing.service";
  #     # TriggerLimitIntervalSec= "10";  # 2 is the default
  #     TriggerLimitBurst= "0";  # 200 is default. 0 is "no triggering"
  #   };
  #   wantedBy = [ "multi-user.target" ];
  # };
}

Update nginx certbot

Need to run docker compose run –rm certbot renew docker compose exec nginx nginx -s reload

within /home/moritz/Projects/cellwhisperer/hosting/home/docker-compose.yml

About

My dotfiles

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Nix 48.4%
  • Emacs Lisp 19.7%
  • Lua 10.7%
  • Perl 7.8%
  • Python 6.2%
  • Vim Script 3.5%
  • Other 3.7%