/nixos-config

My dotfiles

Primary LanguageNix

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
  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 secretdotfiles (for ssh config)
  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
  10. install home-manager!

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>>
    ];
  };
  # 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")
    (import "${inputs.musnix}")
    {
      # The NixOS release to be compatible with for stateful data such as databases.
      system.stateVersion = "20.03";
    }

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

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

Flakes support

Enable experimental Flakes support.

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

Make this repository flake-compatible:

{
  description = "Moritz's NixOS/home-manager configuration";

  # edition = 201909;

  inputs = {
    nixpkgs = {
      type = "github";
      owner = "NixOS";
      repo = "nixpkgs";
      ref = "nixos-23.11";
    };
    nixpkgs-2009 = {
      type = "github";
      owner = "NixOS";
      repo = "nixpkgs";
      ref = "nixos-20.09";
    };
    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";
      flake = false;
    };
    nur = {
      url = github:nix-community/NUR;
    };
    home-manager = {
      type = "github";
      owner = "nix-community";
      repo = "home-manager";
      ref = "release-23.11";
      inputs.nixpkgs.follows = "nixpkgs";
    };
    agenix.url = "github:ryantm/agenix";
    musnix = {
      type = "github";
      owner = "musnix";
      repo = "musnix";
      flake = false;
    };
  };
  
# nixpkgs-local
  outputs = { self, nixpkgs, nixpkgs-moritz, nixpkgs-2009, nixpkgs-unstable, nixos-hardware, home-manager, nur, agenix, musnix }@inputs:
    let
      system = "x86_64-linux";
      pkgs = import nixpkgs {
        inherit system;
        overlays = self.overlays;
        config = { allowUnfree = true;
                    allowBroken = true;
                    nvidia.acceptLicense = true;
                    permittedInsecurePackages = [
                      "adobe-reader-9.5.5"
                      "qtwebkit-5.212.0-alpha4"
                      "openjdk-18+36"
                      "python-2.7.18.6"
                    ];
                    };
      };
    in {
      nixosConfigurations =
        let
          hosts = ["moxps" "mobook" "mopad"];
          mkHost = name:
            nixpkgs.lib.nixosSystem {
              system = "x86_64-linux";
              modules = [
                { nixpkgs = { inherit pkgs;  }; }
                (import ./nixos-config.nix)
                { nixpkgs.overlays = [ nur.overlay ]; }
                agenix.nixosModules.default
                {
                  environment.systemPackages = [ agenix.packages.${system}.default ];
                  age.identityPaths = [ "/home/moritz/.ssh/id_ed25519_agenix" ];
                }
              ];
              specialArgs = { inherit name inputs; };
            };
        in nixpkgs.lib.genAttrs hosts mkHost;

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

      overlays = [
        (_self: _super: self.packages.x86_64-linux)
        <<flake-overlays>>
      ];

      homeConfigurations.moritz =
        #let
          # hosts = ["MoritzSchaefer"];
          # mkHost = hostname:
            home-manager.lib.homeManagerConfiguration {
              pkgs = nixpkgs.legacyPackages.${system};
              # nixpkgs.config.allowUnfree = true;
              # nixpkgs.overlays = self.overlays;
              modules = [ ./.config/nixpkgs/home.nix {
                home = {
                  username = "moritz";
                  homeDirectory = "/home/moritz";
                  stateVersion = "18.09";
                };
                }
              ];
            };
        # in nixpkgs.lib.genAttrs hosts mkHost;
    };
}
(final: prev: {
  unstable = import inputs.nixpkgs-unstable {
    inherit system;
    overlays = self.overlays; # .${system};
    
    config = { allowUnfree = true;  allowBroken = true; nvidia.acceptLicense = true; };
  };
  nixpkgs-2009 = import inputs.nixpkgs-2009 {
    inherit system;
    overlays = self.overlays; # .${system};
    config = { allowUnfree = 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.

Home manager

Home-manager is used to manage my home directory and user applications (including my python installation). I thought it wouldn’t be required to install it (see flakes section), but it seems to be necessary anyways..

{
  environment.systemPackages = [
    pkgs.home-manager
  ];
}

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;

}
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"; }
    ];


  boot.initrd.luks.devices = {
    cryptkey.device = "/dev/disk/by-uuid/ccd19ab7-0e4d-4df4-8912-b87139de56af";
    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.xserver.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";
  };
}

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;
  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";
    
  };

  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;
    };
    libinput = {
      enable = true;
      touchpad.accelSpeed = "0.7";

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

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

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...)
  let
    myCustomLayout = pkgs.writeText "xkb-layout" ''
    keycode 36 = ISO_Level3_Shift
    '';
  in
    services.xserver.displayManager.sessionCommands = ''
      ${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;
  };
}

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.xserver.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;
  # };
}

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”

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"

        if [ $(cat /sys/class/power_supply/BAT0/capacity) -le 10 ]; then
          ${pkgs.sudo}/bin/sudo -E -u moritz  ${pkgs.libnotify}/bin/notify-send -t 2500 "Low Battery" "Your battery is below 10%, please plug in your charger."
        fi
        test "$(cat /sys/class/power_supply/BAT0/status)" != Discharging \
          || test "$(cat /sys/class/power_supply/BAT0/capacity)" -ge 5
      '';
    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

NetworkManager

{
  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
  ];
}

Avahi

{
  services.avahi = {
    enable = true;
   allowInterfaces = [ "wlp9s0" "tun0" ];  # TODO how to add "all"?
    openFirewall = true;
    publish = {
      addresses = true;
      workstation = true;
      enable = true;
    };
    nssmdns = 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.

{
  hardware.pulseaudio = {
    enable = true;
    support32Bit = true;
    zeroconf.discovery.enable = true;
    systemWide = false;
    package = pkgs.pulseaudioFull; # .override { jackaudioSupport = true; };  # need "full" for bluetooth
  };

  environment.systemPackages = with pkgs; [ pavucontrol libjack2 jack2 qjackctl jack2Full 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
    '';
  musnix = {
    enable = true;
    alsaSeq.enable = false;

    # If I build with either of these, I get a PREEMPT error, much like
    #   https://github.com/musnix/musnix/issues/100
    # kernel.realtime = true;
    # kernel.optimize = true;

    # das_watchdog.enable = true;
      # I don't think this does anything without the realtime kernel.

    # magic to me
    rtirq = {
      # highList = "snd_hrtimer";
      resetAll = 1;
      prioLow = 0;
      enable = true;
      nameList = "rtc0 snd";
    };
  };
    

}
{
  musnix = {
    # Find this value with `lspci | grep -i audio` (per the musnix readme).
    # PITFALL: This is the id of the built-in soundcard.
    #   When I start using the external one, change it.
    soundcardPciId = "00:1f.3";
  };
}
{
  musnix = {
    # Find this value with `lspci | grep -i audio` (per the musnix readme).
    # PITFALL: This is the id of the built-in soundcard.
    #   When I start using the external one, change it.
    soundcardPciId = "00:1b.0";  # 00:1b.0 or 00:03.0
  };
}

Printing

https://nixos.wiki/wiki/Printing

{
  services.printing.enable = true;
  services.printing.browsedConf = ''
    CreateIPPPrinterQueues All
  '';
  services.printing.drivers = with pkgs; [
    gutenprint
    gutenprintBin
    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 mail+macbook@moritzs.de" ];
}

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;
  };
}

Firewall

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

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

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

Virtualization/Development

{
  virtualisation.virtualbox.host.enable = false;  # slow compile times
  virtualisation.docker.enable = true;
  virtualisation.docker.enableNvidia = true;
  
  systemd.enableUnifiedCgroupHierarchy = false;  # workaround https://github.com/NixOS/nixpkgs/issues/127146
  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.enable = true;
  services.samba = {
    enable = true;
    securityType = "user";
    openFirewall = true;
    extraConfig = ''
      workgroup = WORKGROUP
      wins support = no
      wins server = 192.168.1.10
      server string = smbnix
      netbios name = smbnix
      security = user 
      #use sendfile = yes
      #max protocol = smb2
      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";
      };
    };
  };
}

Cron

{
  # Enable cron service
  services.cron = {
    enable = true;
    systemCronJobs = [
      # Add new files to wiki
      "0 0 * * 0      moritz    ${pkgs.bash}/bin/bash -c '. /etc/profile; cd /home/moritz/wiki/; ${pkgs.git}/bin/git add .; ${pkgs.git}/bin/git commit -m \"Weekly checkpoint\"' >> /tmp/git_out 2>&1"
      # Download paperpile citations
      "* * * * 0      moritz    ${pkgs.bash}/bin/bash -c '. /etc/profile; cd /home/moritz/wiki/papers; wget --content-disposition -N https://paperpile.com/eb/ghEynTRTJb' >> download_paperpile_log 2>&1"
    ];
  };
}

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="mollitz@gmail.com", 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="mollitz@gmail.com")>>

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
primary_email=mollitz@gmail.com
other_email=ashmalko@cybervisiontech.com,ashmalko@kaaiot.io,ashmalko@doctoright.org,me@egoless.tech

[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 also, filter either ical or org for events older than last month (otherwise org-agenda has to work so much more...)
{
  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 = ''
      ${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)

(_self: _super: { emacs = _super.emacs29; exwm-emacs = ((_super.emacsPackagesFor _super.emacs29).emacsWithPackages (epkgs: with epkgs; [ emacsql-sqlite _super.imagemagick _super.escrotum vterm exwm ])); })  # emasc.withPackages is not available :((((
{
  services.emacs.package = pkgs.emacs29;
  services.xserver.windowManager.session = let
  loadScript = pkgs.writeText "emacs-exwm-load" ''
    (require 'exwm)
    ;; most of it is now in .spacemacs.d/lisp/exwm.el
    (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)
    (exwm-systemtray-enable)
    (exwm-enable)
  ''; in [{
    name = "exwm";
    start = ''
      ${pkgs.exwm-emacs}/bin/emacs -l ${loadScript}
    '';
  } ];
  environment.systemPackages = [ pkgs.exwm-emacs ];
}
{
  nixpkgs.config.packageOverrides = pkgs: {
  emacs = pkgs.emacs29.override { gtk = pkgs.gtk3; };
  };
}
{
  services.xserver = {
    # desktopManager.gnome3.enable = true;
    displayManager = {
      startx.enable = false;
      autoLogin = {  # if errors, then disable again
        user = "moritz";
        enable = true;
      };
      defaultSession = "none+exwm";  # Firefox works more fluently with plasma5+exwm instead of "none+exwm". or does it??
    };
    windowManager = {
      exwm = {
        enable = false;  # TODO enable upon 23.11
        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
        enableDefaultConfig = false;  # todo disable and enable loadScript
        # 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
          (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)
          (exwm-systemtray-enable)
          (exwm-enable)
        '';
      };
      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 awesome wm setup:

{
  environment.systemPackages = [
    pkgs.wmname
    pkgs.xclip
    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" ];
  };
}

Layouts

{
  services.xserver.layout = "de,de,us";
  services.xserver.xkbVariant = "bone,,";
  services.xserver.xkbOptions= "lv5:rwin_switch_lock,terminate:ctrl_alt_bksp,altwin:swap_lalt_lwin";

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

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

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"
  # '';
}

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 restore sleepiness (which is a good thing).

{
  services.redshift = {
    enable = true;
    brightness.night = "1";
    temperature.night = 2800;
  };

  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 --machine=moritz@.host --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.dpi = 200;
}

Applications

Here go applications (almost) every normal user needs.

SSH

{
  programs.ssh = {
    startAgent = true;
  };
  programs.gnupg.agent = {
    enable = true;
    enableSSHSupport = false;
    pinentryFlavor = "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 = true;
}

DLNA

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

Matrix Chat

{
  environment.systemPackages = with pkgs; [
    mirage-im
    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
  ];
}

Microsoft Edge

{
  environment.systemPackages = [
    pkgs.microsoft-edge
  ];
}

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 ];
}

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
    nixpkgs-2009.pandoc
    nixpkgs-2009.haskellPackages.pandoc-crossref  # broken...
    nixpkgs-2009.haskellPackages.pandoc-citeproc  # broken...
    texlive.combined.scheme-full  # until 22.05, this installs an old version of ghostscript
  ];
}

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
  ];
}

Other applications

Don’t require additional setup.

{
  environment.systemPackages =
    with pkgs;
    let sparkleshare_fixed = sparkleshare.overrideAttrs ( oldAttrs: {
      postInstall = ''
        wrapProgram $out/bin/sparkleshare \
            --set PATH ${symlinkJoin {
              name = "mono-path";
              paths = [
                coreutils
                bash
                git
                git-lfs
                glib
                mono
                openssh
                openssl
                xdg_utils
              ];
            }}/bin \
            --set MONO_GAC_PREFIX ${lib.concatStringsSep ":" [
              appindicator-sharp
              gtk-sharp-3_0
              webkit2-sharp
            ]} \
            --set LD_LIBRARY_PATH ${lib.makeLibraryPath [
              appindicator-sharp
              gtk-sharp-3_0.gtk3
              webkit2-sharp
              webkit2-sharp.webkitgtk
            ]}
      '';
      } ); in
    [
    betaflight-configurator
    # spotdl
    miraclecast
    xcolor
    xorg.xgamma
    vlc
    aria
    jetbrains.pycharm-community
    obs-studio
    jmtpfs
    qbittorrent
    unstable.blender
    # teams
    discord
    inkscape
    arandr
    dmenu
    # soulseekqt
    gnome3.cheese
    gnome3.gnome-screenshot
    sparkleshare_fixed 
    gnome3.gpaste
    autorandr
    libnotify
    feh

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

    mplayer
    mpv
    smplayer
    lm_sensors
    tcl
    pymol
    ruby
    vscode
    tesseract

    dotool
    lsof
  ];
}

CANCELLED Default applications

  • State “CANCELLED” from [2021-07-14 Wed 12:52]
    this is done by home.nix
{
  xdg.mime.defaultApplications = {
    "image/png" = "inkscape.desktop"; # this is wrong anyways
    "image/svg+xml" = "inkscape.desktop"; # this is wrong anyways

    "application/pdf" = "emacsclient.desktop";
    "x-scheme-handler/org-protocol" = "org-protocol.desktop";
    "text/html" = "google-chrome.desktop";
    "x-scheme-handler/http" = "google-chrome.desktop";
    "x-scheme-handler/https" = "google-chrome.desktop";
    "x-scheme-handler/about" = "google-chrome.desktop";
    "x-scheme-handler/unknown" = "google-chrome.desktop";
  };
  # environment.variables.XDG_CONFIG_DIRS = [ "/etc/xdg" ]; # we should probably have this in NixOS by default
}

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
}

Conda

{
  environment.systemPackages =
    let conda_shell_kernel_commands = pkgs.writeScript "guided_environment" ''
      #!${pkgs.stdenv.shell}
      conda activate ag_binding_diffusion

      LOG=/tmp/guided_environ_kernel_output
      SYMLINK=/tmp/guided_protein_diffusion_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 &

      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
    '';
    conda_command = pkgs.writeScript "guided_environment" ''
      #!${pkgs.stdenv.shell}
      conda "$@"
    '';
    conda_shell_protenv_cmd = pkgs.writeScript "guided_environment" ''
      #!${pkgs.stdenv.shell}
      conda activate single-cellm
      "$@"
    '';
    kernel_wrapper = pkgs.writeShellScriptBin "guided_prot_diff_kernel" ''
      /run/current-system/sw/bin/conda-shell ${conda_shell_kernel_commands}
    '';  # TODO conda-shell should be provided via a nix variable
    conda_wrapper = pkgs.writeShellScriptBin "conda" ''
      /run/current-system/sw/bin/conda-shell ${conda_command} "$@"
    '';  # TODO conda-shell should be provided via a nix variable
    repl_wrapper = pkgs.writeShellScriptBin "guided_prot_diff_repl" ''
      /run/current-system/sw/bin/conda-shell ${conda_shell_protenv_cmd} "python" "$@"
    '';  # TODO conda-shell should be provided via a nix variable
    cmd_wrapper = pkgs.writeShellScriptBin "guided_prot_diff_cmd" ''
      /run/current-system/sw/bin/conda-shell ${conda_shell_protenv_cmd} "$@"
    ''; # TODO conda-shell should be provided via a nix variable
  in [
    pkgs.conda kernel_wrapper repl_wrapper cmd_wrapper conda_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
  ];
}

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
  ];
  programs.fish.promptInit = ''
    any-nix-shell fish --info-right | source
  '';
}

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 = mollitz@gmail.com

[sendemail]
    smtpencryption = ssl
    smtpserver = smtp.gmail.com
    smtpuser = mollitz@gmail.com
    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 "git@github.com:"]
    pushInsteadOf = https://github.com/

tmux

{
  environment.systemPackages = [
    pkgs.tmux
    pkgs.python39Packages.powerline
  ];
}

Use C-a as a prefix.

set -g prefix C-a
unbind-key C-b
bind-key C-a send-prefix

Move windows (tabs) around. Stealed from here.

bind-key S-left swap-window -t -1
bind-key S-right swap-window -t +1

TODO describe other settings

# To make vim work properly
set -g default-terminal "screen-256color"

set -g status-keys vi
setw -g mode-keys vi

set -g history-limit 10000

# Start numbering from 1
set -g base-index 1

# Allows for faster key repetition
set -s escape-time 0

bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R

bind-key s split-window
bind-key v split-window -h

bind r source-file ~/.tmux.conf \; display-message "Config reloaded..."

set-window-option -g automatic-rename

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

      fritzconnection
      # jupyter
      # jupyter_core
      powerline
      adjust-text
      # up-set-plot
      # moritzsphd
      tabulate
      # swifter
      gffutils
      # pyensembl  # fails due to serializable
      # pybedtools
      pybigwig
      xdg
      epc
      importmagic
      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.unstable.nodePackages_latest.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
  ];
}

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
    tmux
    # 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
    ];
}

Meta

Setup

There is a setup.sh script in this directory. It just links all files to $HOME:

FILES=".vimrc .vim .nvimrc .nvim .gitconfig .zshrc .zsh .tmux.conf .Xresources .config/awesome .config/nvim .nethackrc .emacs.d .ssh bin .config/zathura .irssi .config/xkb .config/fish .msmtprc .notmuch-config .mbsyncrc .config/nixpkgs"

DEST=$1

if [ -z "$DEST" ]; then
    DEST="$HOME"
fi

BASE=$(cd "$(dirname "$0")" && pwd)

ask_install() {
    FILENAME=$1

    LINK="$DEST/$FILENAME"
    TARGET="$BASE/$FILENAME"

    if [ -e $LINK ]; then
        echo "$LINK exists. Skipping..."
    else
        read -r -p "Link $LINK to $TARGET? [y/N] " response
        case $response in
            [yY][eE][sS]|[yY])
                ln -v -s "$TARGET" "$LINK"
                ;;
        esac
    fi
}

for FILE in $FILES; do
    ask_install $FILE
done

Install fisherman

Fisherman is a plugin manager for fish.

if [ ! -e "$DEST/.config/fish/functions/fisher.fish" ]; then
    read -r -p "Install fisherman and all plugins? [y/N] " response
    case $response in
        [yY][eE][sS]|[yY])
            curl -Lo "$DEST/.config/fish/functions/fisher.fish" --create-dirs \
                https://raw.githubusercontent.com/fisherman/fisherman/master/fisher.fish
            fish -c fisher
            ;;
    esac
fi

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.
      # TODO use age
      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
          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;
  };
}

deCONZ

{
  config.virtualisation.oci-containers.containers = {
    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"];  # I think the exposes can be deleted
      volumes = [
       "/var/lib/deconz:/opt/deCONZ"
        "/etc/localtime:/etc/localtime:ro"
      ];
    };
  }; 
}

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

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

{
  virtualisation.oci-containers = {
    # backend = "podman";
    containers.homeassistant = {
      volumes = [ "/var/lib/hass:/config" ];
      environment.TZ = "Europe/Berlin";
      ports = [ "0.0.0.0:8123:8123" ];
      image = "ghcr.io/home-assistant/home-assistant:stable";  # Warning: if the tag does not change, the image will not be updated
      extraOptions = [
        # "--network=host"
        # "--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
      ];
    };
  };
  services.influxdb2 = {
    enable = true;
  };
}

Borg

{
  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

{
  networking.firewall.extraCommands = ''iptables -t raw -A OUTPUT -p udp -m udp --dport 137 -j CT --helper netbios-ns'';
  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  localhost
      hosts deny = 0.0.0.0/0
      guest account = nobody
      map to guest = bad user
    '';
    #shares = {
      #nas = {
        #"path" = "/mnt/ssd2tb";
        #"guest ok" = "yes";
        #"read only" = "no";
      #};
    #};
  };
}