.dotfiles/Nix.org
Swarsel bc8c52554d
refactor: revamp whole Emacs.org
This is the first part of consolidating my Emacs and NixOS config
into a single file. This commit refactors the whole of Emacs.org
and adds more bits of prose to the configuration sections.
2024-06-20 01:55:51 +02:00

204 KiB

Nix Configuration

This file holds the entirety of all configuration files for both NixOS as well as home manager across all machines that I currently use.

The only exception is my Emacs configuration which is held in its own .org file - this is done because that allows for upon-switch parsing of that file and installation of all emacs-packages through home-manager. The init.el file cannot be used there as it contains UTF-8 characters that would break the nix function responsible for handling the emacs-package building process.

Noweb-Ref blocks

These blocks are used in several places throughout the configurations, but not on all machines necessarily. For example, the themeing section needs to be in a NixOS block on NixOS machines but in a home-manager block on non-NixOS. This serves to reduce code duplication.

All systems

Themeing

This is where the theme for the whole OS is defined. This noweb-ref section cannot be copied to the general NixOS config for now since they are on different levels in the config, which would make the flake impure.

  stylix = {
    base16Scheme = ../../wallpaper/swarsel.yaml;
    # base16Scheme = "${pkgs.base16-schemes}/share/themes/shapeshifter.yaml";
    polarity = "dark";
    opacity.popups = 0.5;
    cursor = {
      package = pkgs.capitaine-cursors;
      name = "capitaine-cursors";
      size = 16;
    };
    fonts = {
      sizes = {
        terminal = 10;
        applications = 11;
      };
      serif = {
        # package = (pkgs.nerdfonts.override { fonts = [ "FiraMono" "FiraCode"]; });
        package = pkgs.cantarell-fonts;
        # package = pkgs.montserrat;
        name = "Cantarell";
        # name = "FiraCode Nerd Font Propo";
        # name = "Montserrat";
      };

      sansSerif = {
        # package = (pkgs.nerdfonts.override { fonts = [ "FiraMono" "FiraCode"]; });
        package = pkgs.cantarell-fonts;
        # package = pkgs.montserrat;
        name = "Cantarell";
        # name = "FiraCode Nerd Font Propo";
        # name = "Montserrat";
      };

      monospace = {
        package = (pkgs.nerdfonts.override { fonts = [ "FiraCode"]; });
        name = "FiraCode Nerd Font Mono";
      };

      emoji = {
        package = pkgs.noto-fonts-emoji;
        name = "Noto Color Emoji";
      };
    };
  };

Waybar items - LAPTOPS

  programs.waybar.settings.mainBar.modules-right = ["custom/outer-left-arrow-dark"
                                                    "mpris"
                                                    "custom/left-arrow-light"
                                                    "network"
                                                    "custom/left-arrow-dark"
                                                    "pulseaudio"
                                                    "custom/left-arrow-light"
                                                    "custom/pseudobat"
                                                    "battery"
                                                    "custom/left-arrow-dark"
                                                    "group/hardware"
                                                    "custom/left-arrow-light"
                                                    "clock#2"
                                                    "custom/left-arrow-dark"
                                                    "clock#1"
                                                   ];

Waybar items - PC

        programs.waybar.settings.mainBar."custom/pseudobat"= {
          format= "";
          on-click-right= "wlogout -p layer-shell";
        };
        programs.waybar.settings.mainBar.modules-right = ["custom/outer-left-arrow-dark"
                                                          "mpris"
                                                          "custom/left-arrow-light"
                                                          "network"
                                                          "custom/left-arrow-dark"
                                                          "pulseaudio"
                                                          "custom/left-arrow-light"
                                                          "custom/pseudobat"
                                                          "battery"
                                                          "custom/left-arrow-dark"
                                                          "group/hardware"
                                                          "custom/left-arrow-light"
                                                          "clock#2"
                                                          "custom/left-arrow-dark"
                                                          "clock#1"
                                                         ];

NixOS

Sway Startup commands

          { command = "nextcloud --background";}
          # { command = "spotify";}
          { command = "discord --start-minimized";}
          # { command = "element-desktop --hidden";}
          { command = "element-desktop --hidden  -enable-features=UseOzonePlatform -ozone-platform=wayland --disable-gpu-driver-bug-workarounds";}
          { command = "ANKI_WAYLAND=1 anki";}
          { command = "OBSIDIAN_USE_WAYLAND=1 obsidian";}
          { command = "nm-applet";}
          # { command = "sleep 60 && syncthingtray --wait"; }

gpg-agent

  services.gpg-agent = {
    enable = true;
    enableSshSupport = true;
    enableExtraSocket = true;
    pinentryPackage = pkgs.pinentry-gtk2;
    extraConfig = ''
    allow-emacs-pinentry
    allow-loopback-pinentry
    '';
    };

Wrap with hardware-configuration

  imports =
    [
      ./hardware-configuration.nix
    ];

Virtual hosts init

    services.xserver = {
      layout = "us";
      xkbVariant = "altgr-intl";
    };
    nix.settings.experimental-features = ["nix-command" "flakes"];
    proxmoxLXC.manageNetwork = true; # manage network myself
    proxmoxLXC.manageHostName = false; # manage hostname myself
    networking.useDHCP = true;
    networking.enableIPv6 = false;
    services.openssh = {
      enable = true;
      settings.PermitRootLogin = "yes";
      listenAddresses = [{
        port = 22;
        addr = "0.0.0.0";
      }];
    };
    users.users.root.openssh.authorizedKeys.keyFiles = [
      ../../../secrets/keys/authorized_keys
    ];

    system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change

    environment.shellAliases = {
      nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
    };
  imports = [
    (modulesPath + "/virtualisation/proxmox-lxc.nix")
    ./hardware-configuration.nix
  ];

  <<vminitbare>>

flake.nix

Inputs & Inputs@Outputs

  nixpkgs,
  home-manager,
  nix-on-droid,
  nixos-generators,
  emacs-overlay,
  nur,
  nixgl,
  stylix,
  sops-nix,
  lanzaboote,
  pia,
  nixpkgs-mautrix-signal,
  nix-gaming,
  nixos-hardware,
  nix-alien,
  nixpkgs.url = github:nixos/nixpkgs/nixos-unstable;


  # user-level configuration
  home-manager = {
    url = github:nix-community/home-manager;
    inputs.nixpkgs.follows = "nixpkgs";
  };

  # overlay to access bleeding edge emacs
  emacs-overlay = {
    url = github:nix-community/emacs-overlay;
    inputs.nixpkgs.follows = "nixpkgs";
  };

  # nix user repository
  # i use this mainly to not have to build all firefox extensions
  # myself as well as for the emacs-init package (tbd)
  nur.url = github:nix-community/NUR;

  # provides GL to non-NixOS hosts
  nixgl.url = github:guibou/nixGL;

  # manages all themeing using Home-Manager
  stylix.url = github:danth/stylix;

  # nix secrets management
  sops-nix.url = github:Mic92/sops-nix;

  # enable secure boot on NixOS
  lanzaboote.url = github:nix-community/lanzaboote;

  # nix for android
  nix-on-droid = {
    url = github:t184256/nix-on-droid/release-23.05;
    inputs.nixpkgs.follows = "nixpkgs";
  };

  # generate NixOS images
  nixos-generators = {
    url = github:nix-community/nixos-generators;
    inputs.nixpkgs.follows = "nixpkgs";
  };

  # privateinternetaccess nixos integration - not sure if I will keep using
  pia = {
    url = "git+https://git.sr.ht/~rprospero/nixos-pia?ref=development";
    inputs.nixpkgs.follows = "nixpkgs";
  };

  # provides expressions for mautrix-signal
  nixpkgs-mautrix-signal ={
    url = github:niklaskorz/nixpkgs/nixos-23.11-mautrix-signal;
  };

  # patches for gaming on nix
  nix-gaming = {
    url = github:fufexan/nix-gaming;
  };

  # hardware quirks on nix
  nixos-hardware = {
    url = github:NixOS/nixos-hardware/master;
  };

  # dynamic library loading
  nix-alien = {
    url = github:thiagokokada/nix-alien;
  };

let

  system = "x86_64-linux"; # not very portable, but I do not use other architectures at the moment
  pkgs = import nixpkgs { inherit system;
                          overlays = [ emacs-overlay.overlay
                                       nur.overlay
                                       nixgl.overlay
                                     ];
                          config.allowUnfree = true;
                        };

  # for ovm arm hosts
  armpkgs = import nixpkgs { system = "aarch64-linux";
                          overlays = [ emacs-overlay.overlay
                                       nur.overlay
                                       nixgl.overlay
                                     ];
                          config.allowUnfree = true;
                        };

  pkgsmautrix = import nixpkgs-mautrix-signal { inherit system;
                          config.allowUnfree = true;
                        };

  # NixOS modules that can only be used on NixOS systems
  nixModules = [ stylix.nixosModules.stylix
                 ./profiles/common/nixos.nix
                 # dynamic library loading
                 ({ self, system, ... }: {
                   environment.systemPackages = with self.inputs.nix-alien.packages.${system}; [
                     nix-alien
                   ];
                   # needed for `nix-alien-ld`
                   programs.nix-ld.enable = true;
                   })
                 ];

  # Home-Manager modules wanted on non-NixOS systems
  homeModules = [ stylix.homeManagerModules.stylix
                ];
  # Home-Manager modules wanted on both NixOS and non-NixOS systems
  mixedModules = [ sops-nix.homeManagerModules.sops
                   ./profiles/common/home.nix
                 ];

nixosConfigurations

    onett = nixpkgs.lib.nixosSystem {
      specialArgs = {inherit inputs pkgs; };
      modules = nixModules ++ [
        ./profiles/onett/nixos.nix
        home-manager.nixosModules.home-manager
        {
          home-manager.users.swarsel.imports = mixedModules ++ [
            ./profiles/onett/home.nix
          ];
        }
      ];
    };

    sandbox = nixpkgs.lib.nixosSystem {
      pkgs = pkgsmautrix;
      specialArgs.unstable = nixpkgs-mautrix-signal;
      modules = [
        sops-nix.nixosModules.sops
        ./profiles/sandbox/nixos.nix
      ];
    };

    twoson = nixpkgs.lib.nixosSystem {
      specialArgs = {inherit inputs pkgs; };
      modules = nixModules ++ [
        ./profiles/twoson/nixos.nix
        home-manager.nixosModules.home-manager
        {
          home-manager.users.swarsel.imports = mixedModules ++ [
            ./profiles/twoson/home.nix
          ];
        }
      ];
    };

    threed = nixpkgs.lib.nixosSystem {
      specialArgs = {inherit inputs pkgs; };
      modules = nixModules ++ [
        lanzaboote.nixosModules.lanzaboote
        ./profiles/threed/nixos.nix
        home-manager.nixosModules.home-manager
        {
          home-manager.users.swarsel.imports = mixedModules ++ [
            ./profiles/threed/home.nix
          ];
        }
      ];
    };

    fourside = nixpkgs.lib.nixosSystem {
      specialArgs = {inherit inputs pkgs; };
      modules = nixModules ++ [
        nixos-hardware.nixosModules.lenovo-thinkpad-p14s-amd-gen2
        ./profiles/fourside/nixos.nix
        home-manager.nixosModules.home-manager
        {
          home-manager.users.swarsel.imports = mixedModules ++ [
            ./profiles/fourside/home.nix
          ];
        }
      ];
    };

    stand = nixpkgs.lib.nixosSystem {
      specialArgs = {inherit inputs pkgs; };
      modules = nixModules ++ [
        ./profiles/stand/nixos.nix
        home-manager.nixosModules.home-manager
        {
          home-manager.users.homelen.imports = mixedModules ++ [
            ./profiles/stand/home.nix
          ];
        }
      ];
    };

    nginx = nixpkgs.lib.nixosSystem {
      specialArgs = {inherit inputs pkgs; };
      modules = [
        sops-nix.nixosModules.sops
        ./profiles/server1/nginx/nixos.nix
      ];
    };

    calibre = nixpkgs.lib.nixosSystem {
      specialArgs = {inherit inputs pkgs; };
      modules = [
        sops-nix.nixosModules.sops
        ./profiles/server1/calibre/nixos.nix
      ];
    };

    jellyfin = nixpkgs.lib.nixosSystem {
      specialArgs = {inherit inputs pkgs; };
      modules = [
        # sops-nix.nixosModules.sops
        ./profiles/server1/jellyfin/nixos.nix
      ];
    };

    transmission = nixpkgs.lib.nixosSystem {
      specialArgs = {inherit inputs pkgs; };
      modules = [
        sops-nix.nixosModules.sops
        pia.nixosModule
        ./profiles/server1/transmission/nixos.nix
      ];
    };

    matrix = nixpkgs.lib.nixosSystem {
      # specialArgs = {inherit pkgsmautrix; };
      pkgs = pkgsmautrix;
      # this is to import a service module that is not on nixpkgs
      # this way avoids infinite recursion errors
      specialArgs.unstable = nixpkgs-mautrix-signal;
      modules = [
        sops-nix.nixosModules.sops
        ./profiles/server1/matrix/nixos.nix
      ];
    };

    sound = nixpkgs.lib.nixosSystem {
      specialArgs = {inherit inputs pkgs; };
      modules = [
        sops-nix.nixosModules.sops
        ./profiles/server1/sound/nixos.nix
      ];
    };

    spotifyd = nixpkgs.lib.nixosSystem {
      specialArgs = {inherit inputs pkgs; };
      modules = [
        sops-nix.nixosModules.sops
        ./profiles/server1/spotifyd/nixos.nix
      ];
    };

    paperless = nixpkgs.lib.nixosSystem {
      specialArgs = {inherit inputs pkgs; };
      modules = [
        sops-nix.nixosModules.sops
        ./profiles/server1/paperless/nixos.nix
      ];
    };

    #ovm swarsel
    sync = nixpkgs.lib.nixosSystem {
      specialArgs = {inherit inputs pkgs; };
      modules = [
        sops-nix.nixosModules.sops
        ./profiles/remote/oracle/sync/nixos.nix
      ];
    };

    #ovm swarsel
    swatrix = nixpkgs.lib.nixosSystem {
      # specialArgs = {inherit pkgsmautrix; };
      pkgs = pkgsmautrix;
      # this is to import a service module that is not on nixpkgs
      # this way avoids infinite recursion errors
      specialArgs.unstable = nixpkgs-mautrix-signal;
      modules = [
        sops-nix.nixosModules.sops
        ./profiles/remote/oracle/matrix/nixos.nix
      ];
    };

homeConfigurations

  "leons@PCisLee" = home-manager.lib.homeManagerConfiguration {
    inherit pkgs;
    modules = homeModules ++ mixedModules ++ [
      ./profiles/surface/home.nix
    ];
  };

nixOnDroidConfigurations

  default = nix-on-droid.lib.nixOnDroidConfiguration {
    modules = [
      ./profiles/mysticant/configuration.nix
    ];
  };

nixos-generators

  proxmox-lxc = nixos-generators.nixosGenerate {
    inherit system;
    modules = [
       ./profiles/server1/TEMPLATE/nixos.nix
    ];
    format = "proxmox-lxc";
  };

TODO System specific configuration

This section mainly exists house different `configuration.nix` files for system level configurations of NixOS systems as well as `home.nix` for user level configurations on all systems.

Important: Think about if a settings really needs to go into this area - chances are that the settings can also go to the general settings.

Template (for new machines)

This section holds the minimum configuration that is needed on a new host. These assume a NixOS machine (so not standalone home-manager on a non-NixOS host), as this is the setting that I will most likely use in the future now. All of these blocks need to be updated, with entries called TEMPLATE mostly needed to be filled with host-/user-specific values or other inputs. If TEMPLATE is given in a comment section, see the provided values as likely defaults. The TEMPLATE comments should afterwards be deleted for clarity.

If a non-NixOS host must be used, check the Surface configuration for pointers. Most likely the waybar settings need to be adjusted, since non-NixOS (as of writing this) fails to display drawers in the waybar properly.

No matter what you do, check the initial /etc/nixos/configuration.nix for notable changes that might emerge in future versions of nix.

NixOS

  { config, lib, pkgs, inputs, ... }:

  {

    <<wrap>>

    services = {
      getty.autologinUser = "TEMPLATE";
      greetd.settings.initial_session.user="TEMPLATE";
    };

    # Bootloader
    boot.loader.grub.enable = true;
    boot.loader.grub.device = "/dev/sda"; # TEMPLATE - if only one disk, this will work
    boot.loader.grub.useOSProber = true;

    # --------------------------------------
    # you might need a configuration like this instead:
    # Bootloader
    # boot.loader.grub.enable = true;
    # boot.loader.grub.devices = ["nodev" ];
    # boot.loader.grub.useOSProber = true;
    # boot.kernelPackages = pkgs.linuxPackages_latest;
    # --------------------------------------

    networking.hostName = "TEMPLATE"; # Define your hostname.

    stylix.image = ../../wallpaper/TEMPLATEwp.png;
    <<theme>>

    # Configure keymap in X11 (only used for login)
    services.xserver = {
      layout = "us";
      xkbVariant = "altgr-intl";
    };

    users.users.TEMPLATE = {
      isNormalUser = true;
      description = "TEMPLATE";
      extraGroups = [ "networkmanager" "wheel" "lp" "audio" "video" ];
      packages = with pkgs; [];
    };

    environment.systemPackages = with pkgs; [
    ];

    system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change

  }

Home Manager

  { config, pkgs, lib, fetchFromGitHub, ... }:

  {

    <<gpgagent>>
    home = {
      username = "TEMPLATE";
      homeDirectory = "/home/TEMPLATE";
      stateVersion = "23.05"; # TEMPLATE -- Please read the comment before changing.
      keyboard.layout = "us"; # TEMPLATE
      home.packages = with pkgs; [
        # ---------------------------------------------------------------
        # if schildichat works on this machine, use it, otherwise go for element
        # element-desktop
        # ---------------------------------------------------------------
      ];
    };
    # update path if the sops private key is stored somewhere else
    sops.age.sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/sops" ];

    # waybar config - TEMPLATE - update for cores and temp
    programs.waybar.settings.mainBar = {
      #cpu.format = "{icon0} {icon1} {icon2} {icon3}";
      cpu.format = "{icon0} {icon1} {icon2} {icon3} {icon4} {icon5} {icon6} {icon7}";
      temperature.hwmon-path = "/sys/devices/platform/coretemp.0/hwmon/hwmon1/temp3_input";
    };

    # -----------------------------------------------------------------
    # is this machine always connected to power? If yes, use this block:
    # <<waybarpc>>
    # -----------------------------------------------------------------

    # -----------------------------------------------------------------
    # if not always connected to power (laptop), use this (default):
    <<waybarlaptop>>
    # -----------------------------------------------------------------

    wayland.windowManager.sway= {
      config = rec {
        # update for actual inputs here,
        input = {
          "36125:53060:splitkb.com_Kyria_rev3" = {
            xkb_layout = "us";
            xkb_variant = "altgr-intl";
          };
          "1:1:AT_Translated_Set_2_keyboard" = { # TEMPLATE
            xkb_layout = "us";
            xkb_options = "grp:win_space_toggle";
            # xkb_options = "ctrl:nocaps,grp:win_space_toggle";
            xkb_variant = "altgr-intl";
          };
          "type:touchpad" = {
            dwt = "enabled";
            tap = "enabled";
            natural_scroll = "enabled";
            middle_emulation = "enabled";
          };

        };

        output = {
          DP-1 = {
            mode = "2560x1440"; # TEMPLATE
            scale = "1";
            bg = "~/.dotfiles/wallpaper/TEMPLATE.png fill";
          };
        };

        keybindings = let
          modifier = config.wayland.windowManager.sway.config.modifier;
        in {
          # TEMPLATE
          "${modifier}+w" = "exec \"bash ~/.dotfiles/scripts/checkschildi.sh\"";
          # "${modifier}+w" = "exec \"bash ~/.dotfiles/scripts/checkelement.sh\"";
        };

        startup = [
          <<startupnixos>>
        ];
      };
    };
  }

Physical hosts

TODO Surface

My Surface Pro 3, only used for on-the-go university work. Be careful when pushing largechanges to this machine, as it easily runs out of memory on large switches. At the moment the only machine running non-NixOS, so special care must be taken not to break this one during updates.

TODO Channel setup

This installs nixGL, which is needed to run GL apps installed through home-manager, since this machine is not using NixOS.

  • TODO: move this to flake.nix by using an overlay
  1. Install nixGL:
  nix-channel --add https://github.com/guibou/nixGL/archive/main.tar.gz nixgl && nix-channel --update
    nix-env -iA nixgl.auto.nixGLDefault   # or replace `nixGLDefault` with your desired wrapper

This is needed in order to use EGL. Prefix programs that use it with `nixGL`

Home manager
  { config, pkgs, lib, fetchFromGitHub, ... }:

  {
    programs.home-manager.enable = true;
    home.username = "leons";
    home.homeDirectory = "/home/leons";

    home.stateVersion = "23.05"; # Please read the comment before changing.

     stylix.image = ../../wallpaper/surfacewp.png;
    <<theme>>

    nixpkgs = {
      config = {
        allowUnfree = true;
        allowUnfreePredicate = (_: true);
      };
    };
    services.xcape = {
      enable = true;
      mapExpression = {
        Control_L = "Escape";
      };
    };
    #keyboard config
    home.keyboard.layout = "us";

    sops.age.sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/sops" ];

    # waybar config
    programs.waybar.settings.mainBar.cpu.format = "{icon0} {icon1} {icon2} {icon3}";

    programs.waybar.settings.mainBar.temperature.hwmon-path = "/sys/devices/platform/coretemp.0/hwmon/hwmon3/temp3_input";
    programs.waybar.settings.mainBar.modules-right = ["custom/outer-left-arrow-dark" "mpris" "custom/left-arrow-light"
                                                      "network"
                                                      "custom/left-arrow-dark"
                                                      "pulseaudio"
                                                      "custom/left-arrow-light"
                                                      "battery"
                                                      "custom/left-arrow-dark"
                                                      "temperature"
                                                      "custom/left-arrow-light"
                                                      "disk"
                                                      "custom/left-arrow-dark"
                                                      "memory"
                                                      "custom/left-arrow-light"
                                                      "cpu"
                                                      "custom/left-arrow-dark"
                                                      "tray"
                                                      "custom/left-arrow-light"
                                                      "clock#2"
                                                      "custom/left-arrow-dark"
                                                      "clock#1" ];
    services.blueman-applet.enable = true;
    home.packages = with pkgs; [
      # nixgl.auto.nixGLDefault
      evince
      # nodejs_20

      # messaging
      # we use gomuks for RAM preservation, but keep schildi around for files and images
    ];

    programs.zsh.initExtra = "
  export GPG_TTY=\"$(tty)\"
  export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
  gpgconf --launch gpg-agent
        ";

    # sway config
    wayland.windowManager.sway= {
      config = rec {
        input = {
          "*" = {
            xkb_layout = "us";
            xkb_options = "ctrl:nocaps,grp:win_space_toggle";
            xkb_variant = "altgr-intl";
          };
          "type:touchpad" = {
            dwt = "enabled";
            tap = "enabled";
            natural_scroll = "enabled";
            middle_emulation = "enabled";
          };
        };

        output = {
          eDP-1 = {
            mode = "2160x1440@59.955Hz";
            scale = "1";
            bg = "~/.dotfiles/wallpaper/surfacewp.png fill";
          };
        };

        keybindings = let
          modifier = config.wayland.windowManager.sway.config.modifier;
        in {
          "${modifier}+F2"  = "exec brightnessctl set +5%";
          "${modifier}+F1"= "exec brightnessctl set 5%-";
          "${modifier}+n" = "exec sway output eDP-1 transform normal, splith";
          "${modifier}+Ctrl+p" = "exec nixGL wl-mirror eDP-1";
          "${modifier}+t" = "exec sway output eDP-1 transform 90, splitv";
          "${modifier}+XF86AudioLowerVolume" = "exec grim -g \"$(slurp)\" -t png - | wl-copy -t image/png";
          "${modifier}+XF86AudioRaiseVolume" = "exec grim -g \"$(slurp)\" -t png - | wl-copy -t image/png";
          "${modifier}+w" = "exec \"bash ~/.dotfiles/scripts/checkgomuks.sh\"";
        };

        startup = [
          { command = "sleep 60 && nixGL nextcloud --background";}
          # { command = "sleep 60 && nixGL spotify";}
          { command = "sleep 60 && nixGL discord --start-minimized -enable-features=UseOzonePlatform -ozone-platform=wayland";}
          # { command = "sleep 60 && nixGL schildichat-desktop --hidden";}
          { command = "sleep 60 && nixGL syncthingtray --wait"; }
          { command = "sleep 60 && ANKI_WAYLAND=1 nixGL anki";}
          { command = "nm-applet --indicator";}
          { command = "sleep 60 && OBSIDIAN_USE_WAYLAND=1 nixGL obsidian -enable-features=UseOzonePlatform -ozone-platform=wayland";}
        ];

        keycodebindings = {
          "124" = "exec systemctl suspend";
        };
      };

      extraConfig = "
      exec swaymsg input 7062:6917:NTRG0001:01_1B96:1B05 map_to_output eDP-1
      exec swaymsg input 7062:6917:NTRG0001:01_1B96:1B05_Stylus map_to_output eDP-1
      ";
    };
  }

Onett (Lenovo Y510P)

My laptop, sadly soon to be replaced by a new one, since most basic functions are stopping to work lately.

NixOS
  { config, lib, pkgs, inputs, ... }:

  {

    <<wrap>>

    services = {
      greetd.settings.initial_session.user ="swarsel";
      xserver.videoDrivers = ["nvidia"];
    };


    hardware = {
      nvidia = {
        modesetting.enable = true;
        powerManagement.enable = true;
        prime = {
          intelBusId = "PCI:0:2:0";
          nvidiaBusId = "PCI:1:0:0";
          sync.enable = true;
        };
      };
      pulseaudio.configFile = pkgs.runCommand "default.pa" {} ''
                   sed 's/module-udev-detect$/module-udev-detect tsched=0/' \
                     ${pkgs.pulseaudio}/etc/pulse/default.pa > $out
                   '';
      bluetooth.enable = true;
    };

    stylix.image = ../../wallpaper/lenovowp.png;
    <<theme>>

    boot.loader.grub = {
      enable = true;
      device = "/dev/sda";
      useOSProber = true;
    };

    networking.hostName = "onett"; # Define your hostname.
    networking.enableIPv6 = false;

    users.users.swarsel = {
      isNormalUser = true;
      description = "Leon S";
      extraGroups = [ "networkmanager" "wheel" "lp"];
      packages = with pkgs; [];
    };

    system.stateVersion = "23.05"; # Did you read the comment?

    environment.systemPackages = with pkgs; [
    ];


  }
Home Manager
  { config, pkgs, lib, fetchFromGitHub, ... }:

  {

    <<gpgagent>>

    home = {
      username = "swarsel";
      homeDirectory = "/home/swarsel";
      stateVersion = "23.05"; # Please read the comment before changing.
      keyboard.layout = "de";
      packages = with pkgs; [
      ];
    };

    sops.age.sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/sops" ];

    # # waybar config
    programs.waybar.settings.mainBar = {
      cpu.format = "{icon0} {icon1} {icon2} {icon3} {icon4} {icon5} {icon6} {icon7}";
      temperature.hwmon-path = "/sys/devices/platform/coretemp.0/hwmon/hwmon3/temp3_input";
    };
    <<waybarlaptop>>

    services.blueman-applet.enable = true;

    wayland.windowManager.sway= {
      config = rec {
        input = {
          "1:1:AT_Translated_Set_2_keyboard" = {
            xkb_layout = "us";
            xkb_options = "grp:win_space_toggle";
            # xkb_options = "ctrl:nocaps,grp:win_space_toggle";
            xkb_variant = "altgr-intl";
          };
          "2362:33538:ipad_keyboard_Keyboard" = {
            xkb_layout = "us";
            xkb_options = "altwin:swap_lalt_lwin,ctrl:nocaps,grp:win_space_toggle";
            xkb_variant = "colemak_dh";
          };
          "36125:53060:splitkb.com_Kyria_rev3" = {
            xkb_layout = "us";
            xkb_variant = "altgr-intl";
          };

          "type:touchpad" = {
            dwt = "enabled";
            tap = "enabled";
            natural_scroll = "enabled";
            middle_emulation = "enabled";
          };
        };

        output = {
          eDP-1 = {
            mode = "1920x1080";
            scale = "1";
            bg = "~/.dotfiles/wallpaper/lenovowp.png fill";
            position = "1920,0";
          };
          VGA-1 = {
            mode = "1920x1080";
            scale = "1";
            bg = "~/.dotfiles/wallpaper/lenovowp.png fill";
            position = "0,0";
          };
        };

        keybindings = let
          modifier = config.wayland.windowManager.sway.config.modifier;
        in {
          "${modifier}+F2"  = "exec brightnessctl set +5%";
          "${modifier}+F1"= "exec brightnessctl set 5%-";
          "XF86MonBrightnessUp"  = "exec brightnessctl set +5%";
          "XF86MonBrightnessDown"= "exec brightnessctl set 5%-";
          "${modifier}+Ctrl+p" = "exec wl-mirror eDP-1";
          "XF86HomePage" = "exec wtype -P Escape -p Escape";
          "${modifier}+w" = "exec \"bash ~/.dotfiles/scripts/checkschildi.sh\"";
        };
        keycodebindings = {
          "94" = "exec wtype c";
          "Shift+94" = "exec wtype C";
          "Ctrl+94" = "exec wtype -M ctrl c -m ctrl";
          "Ctrl+Shift+94" = "exec wtype -M ctrl -M shift c -m ctrl -m shift";
        };

        startup = [
          <<startupnixos>>
        ];
      };

      extraConfig = "
   ";
    };
  }

Twoson (Lenovo Thinkpad T14s Gen3)

NixOS
  { config, lib, pkgs, inputs, ... }:

  {

    <<wrap>>

    services = {
      getty.autologinUser = "swarsel";
      greetd.settings.initial_session.user="swarsel";
    };

    # Bootloader
    # boot.loader.grub.enable = true;
    # boot.loader.grub.device = "/dev/sda"; # TEMPLATE - if only one disk, this will work
    # boot.loader.grub.useOSProber = true;

    # --------------------------------------
    # you might need a configuration like this instead:
    # Bootloader
    # boot.loader.grub.enable = true;
    # boot.loader.grub.devices = ["nodev" ];
    # boot.loader.grub.useOSProber = true;
    # boot.kernelPackages = pkgs.linuxPackages_latest;
    # --------------------------------------

    networking.hostName = "twoson"; # Define your hostname.

    stylix.image = ../../wallpaper/t14swp.png;
    <<theme>>

    # Configure keymap in X11 (only used for login)
    services.xserver = {
      layout = "us";
      xkbVariant = "altgr-intl";
    };

    users.users.swarsel = {
      isNormalUser = true;
      description = "TEMPLATE";
      extraGroups = [ "networkmanager" "wheel" "lp" "audio" "video" ];
      packages = with pkgs; [];
    };

    environment.systemPackages = with pkgs; [
    ];

    system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change


  }
Home Manager
  { config, pkgs, lib, fetchFromGitHub, ... }:

  {

    <<gpgconf>>
    home = {
      username = "swarsel";
      homeDirectory = "/home/swarsel";
      stateVersion = "23.05"; # TEMPLATE -- Please read the comment before changing.
      keyboard.layout = "us"; # TEMPLATE
      home.packages = with pkgs; [
        # ---------------------------------------------------------------
        # if schildichat works on this machine, use it, otherwise go for element
        # element-desktop
        # ---------------------------------------------------------------
      ];
    };
    # update path if the sops private key is stored somewhere else
    sops.age.sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/sops" ];

    # waybar config - TEMPLATE - update for cores and temp
    programs.waybar.settings.mainBar = {
      #cpu.format = "{icon0} {icon1} {icon2} {icon3}";
      cpu.format = "{icon0} {icon1} {icon2} {icon3} {icon4} {icon5} {icon6} {icon7}";
      temperature.hwmon-path = "/sys/devices/platform/coretemp.0/hwmon/hwmon1/temp3_input";
    };

    # -----------------------------------------------------------------
    # is this machine always connected to power? If yes, use this block:
    # <<waybarpc>>
    # -----------------------------------------------------------------

    # -----------------------------------------------------------------
    # if not always connected to power (laptop), use this (default):
    <<waybarlaptop>>
    # -----------------------------------------------------------------

    wayland.windowManager.sway= {
      config = rec {
        # update for actual inputs here,
        input = {
          "36125:53060:splitkb.com_Kyria_rev3" = {
            xkb_layout = "us";
            xkb_variant = "altgr-intl";
          };
          # "1:1:AT_Translated_Set_2_keyboard" = { # TEMPLATE
          #   xkb_layout = "us";
          #   xkb_options = "grp:win_space_toggle";
          #   # xkb_options = "ctrl:nocaps,grp:win_space_toggle";
          #   xkb_variant = "altgr-intl";
          # };
          "type:touchpad" = {
            dwt = "enabled";
            tap = "enabled";
            natural_scroll = "enabled";
            middle_emulation = "enabled";
          };

        };

        output = {
          DP-1 = {
            mode = "1920x1280"; # TEMPLATE
            scale = "1";
            bg = "~/.dotfiles/wallpaper/t14swp.png fill";
          };
        };

        keybindings = let
          modifier = config.wayland.windowManager.sway.config.modifier;
        in {
          # TEMPLATE
          "${modifier}+w" = "exec \"bash ~/.dotfiles/scripts/checkschildi.sh\"";
          # "${modifier}+w" = "exec \"bash ~/.dotfiles/scripts/checkelement.sh\"";
        };

        startup = [
          <<startupnixos>>
        ];
      };
    };
  }

Threed (Surface Pro 3)

New setup for the SP3, this time using NixOS - another machine will take over the HM-only config for compatibility in the future.

NixOS
  { config, lib, pkgs, inputs, ... }:

  {
    <<wrap>>

    services = {
      getty.autologinUser = "swarsel";
      greetd.settings.initial_session.user="swarsel";
    };

    hardware.bluetooth.enable = true;

    # Bootloader
    boot = {
      loader.systemd-boot.enable = lib.mkForce false;
      lanzaboote = {
        enable = true;
        pkiBundle = "/etc/secureboot";
      };
      loader.efi.canTouchEfiVariables = true;
      # use bootspec instead of lzbt for secure boot. This is not a generally needed setting
      bootspec.enable = true;
      # kernelPackages = pkgs.linuxPackages_latest;
    };

    networking = {
      hostName = "threed";
      enableIPv6 = false;
      firewall.enable = false;
    };

    stylix.image = ../../wallpaper/surfacewp.png;
    <<theme>>

    users.users.swarsel = {
      isNormalUser = true;
      description = "Leon S";
      extraGroups = [ "networkmanager" "wheel" "lp" "audio" "video" ];
      packages = with pkgs; [];
    };

    environment.systemPackages = with pkgs; [
    ];

    system.stateVersion = "23.05";

  }
Home Manager
  { config, pkgs, lib, fetchFromGitHub, ... }:

  {

    <<gpgagent>>

    home = {
      username = "swarsel";
      homeDirectory = "/home/swarsel";
      stateVersion = "23.05"; # Please read the comment before changing.
      keyboard.layout = "us";
      packages = with pkgs; [
      ];
    };

    sops.age.sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/sops" ];

    programs.waybar.settings.mainBar = {
      cpu.format = "{icon0} {icon1} {icon2} {icon3}";
      temperature.hwmon-path = "/sys/devices/platform/coretemp.0/hwmon/hwmon1/temp3_input";
    };
    <<waybarlaptop>>

    wayland.windowManager.sway= {
      config = rec {
        input = {
          "*" = {
            xkb_layout = "us";
            xkb_options = "grp:win_space_toggle";
            xkb_variant = "altgr-intl";
          };
          "type:touchpad" = {
            dwt = "enabled";
            tap = "enabled";
            natural_scroll = "enabled";
            middle_emulation = "enabled";
          };
        };

        output = {
          eDP-1 = {
            mode = "2160x1440@59.955Hz";
            scale = "1";
            bg = "~/.dotfiles/wallpaper/surfacewp.png fill";
          };
        };

        keybindings = let
          modifier = config.wayland.windowManager.sway.config.modifier;
        in {
          "${modifier}+F2"  = "exec brightnessctl set +5%";
          "${modifier}+F1"= "exec brightnessctl set 5%-";
          "${modifier}+n" = "exec sway output eDP-1 transform normal, splith";
          "${modifier}+Ctrl+p" = "exec wl-mirror eDP-1";
          "${modifier}+t" = "exec sway output eDP-1 transform 90, splitv";
          "${modifier}+XF86AudioLowerVolume" = "exec grim -g \"$(slurp)\" -t png - | wl-copy -t image/png";
          "${modifier}+XF86AudioRaiseVolume" = "exec grim -g \"$(slurp)\" -t png - | wl-copy -t image/png";
          "${modifier}+w" = "exec \"bash ~/.dotfiles/scripts/checkschildi.sh\"";
        };

        startup = [
          <<startupnixos>>
        ];

        keycodebindings = {
          "124" = "exec systemctl suspend";
        };
      };

      extraConfig = "
      exec swaymsg input 7062:6917:NTRG0001:01_1B96:1B05 map_to_output eDP-1
      exec swaymsg input 7062:6917:NTRG0001:01_1B96:1B05_Stylus map_to_output eDP-1
      ";
    };
  }

Fourside (Lenovo Thinkpad P14s Gen2)

NixOS
  { config, lib, pkgs, inputs, ... }:

  {

    # <<wrap>>
    imports =
      [
        ./hardware-configuration.nix
      ];

    services = {
      getty.autologinUser = "swarsel";
      greetd.settings.initial_session.user="swarsel";
    };

    boot = {
      loader.systemd-boot.enable = true;
      loader.efi.canTouchEfiVariables = true;
      # kernelPackages = pkgs.linuxPackages_latest;
    };

    networking = {
      hostName = "fourside"; # Define your hostname.
      nftables.enable = true;
      enableIPv6 = false;
      firewall.checkReversePath = false;
      firewall = {
        enable = true;
        allowedUDPPorts = [ 4380 27036 14242 34197 51820 ]; # 34197: factorio; 4380 27036 14242: barotrauma; 51820: wireguard
        allowedTCPPortRanges = [
          {from = 27015; to = 27030;} # barotrauma
          {from = 27036; to = 27037;} # barotrauma
        ];
        allowedUDPPortRanges = [
          {from = 27000; to = 27031;} # barotrauma
          {from = 58962; to = 58964;} # barotrauma
        ];
      };
    };

    virtualisation.virtualbox = {
      host = {
      enable = true;
      enableExtensionPack = true;
      };
      guest = {
        enable = true;
        };
      };

    stylix.image = ../../wallpaper/lenovowp.png;
    <<theme>>

    hardware = {
        opengl = {
          enable = true;
          driSupport = true;
          driSupport32Bit = true;
          extraPackages = with pkgs; [
            vulkan-loader
            vulkan-validation-layers
            vulkan-extension-layer
          ];
        };
        bluetooth.enable = true;
      };

    programs.steam = {
      enable = true;
      extraCompatPackages = [
        pkgs.proton-ge-bin
      ];
    };

      # Configure keymap in X11 (only used for login)

    services.thinkfan = {
      enable = false;
    };
    services.power-profiles-daemon.enable = true;

    users.users.swarsel = {
      isNormalUser = true;
      description = "Leon S";
      extraGroups = [ "networkmanager" "wheel" "lp" "audio" "video" "vboxusers" "scanner" ];
      packages = with pkgs; [];
    };

    environment.systemPackages = with pkgs; [
        # gog games installing
        heroic
        # minecraft
        temurin-bin-17
        (prismlauncher.override {
          glfw = pkgs.glfw-wayland-minecraft;
        })
    ];

    system.stateVersion = "23.05";


  }
Home Manager
  { config, pkgs, lib, fetchFromGitHub, ... }:

  {

    <<gpgagent>>
    home = {
      username = "swarsel";
      homeDirectory = "/home/swarsel";
      stateVersion = "23.05"; # TEMPLATE -- Please read the comment before changing.
      keyboard.layout = "us"; # TEMPLATE
      packages = with pkgs; [
      ];
    };
    sops.age.sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/sops" ];

    # waybar config - TEMPLATE - update for cores and temp
    programs.waybar.settings.mainBar = {
      cpu.format = "{icon0} {icon1} {icon2} {icon3} {icon4} {icon5} {icon6} {icon7}";
      # temperature.hwmon-path = "/sys/devices/pci0000:00/0000:00:18.3/hwmon/hwmon4/temp1_input";
      temperature.hwmon-path.abs = "/sys/devices/platform/thinkpad_hwmon/hwmon/";
      temperature.input-filename = "temp1_input";
    };

    <<waybarlaptop>>

    wayland.windowManager.sway= {
      config = rec {
        # update for actual inputs here,
        input = {
          "36125:53060:splitkb.com_Kyria_rev3" = {
            xkb_layout = "us";
            xkb_variant = "altgr-intl";
          };
          "1:1:AT_Translated_Set_2_keyboard" = { # TEMPLATE
            xkb_layout = "us";
            xkb_options = "grp:win_space_toggle";
            xkb_variant = "altgr-intl";
          };
          "type:touchpad" = {
            dwt = "enabled";
            tap = "enabled";
            natural_scroll = "enabled";
            middle_emulation = "enabled";
          };

        };

        output = {
          DP-1 = {
            mode = "2560x1440"; # TEMPLATE
            scale = "1";
            #bg = "~/.dotfiles/wallpaper/lenovowp.png fill";
          };
        };

        keybindings = let
          modifier = config.wayland.windowManager.sway.config.modifier;
        in {
          "${modifier}+w" = "exec \"bash ~/.dotfiles/scripts/checkelement.sh\"";
          "XF86MonBrightnessUp"  = "exec brightnessctl set +5%";
          "XF86MonBrightnessDown"= "exec brightnessctl set 5%-";
          "XF86Display" = "exec wl-mirror eDP-1";
          # these are left open to use
          # "XF86WLAN" = "exec wl-mirror eDP-1";
          # "XF86Messenger" = "exec wl-mirror eDP-1";
          # "XF86Go" = "exec wl-mirror eDP-1";
          # "XF86Favorites" = "exec wl-mirror eDP-1";
          # "XF86HomePage" = "exec wtype -P Escape -p Escape";
          # "XF86AudioLowerVolume" = "pactl set-sink-volume alsa_output.pci-0000_08_00.6.HiFi__hw_Generic_1__sink -5%";
          # "XF86AudioRaiseVolume" = "pactl set-sink-volume alsa_output.pci-0000_08_00.6.HiFi__hw_Generic_1__sink +5%  ";
          "XF86AudioMute" = "pactl set-sink-mute alsa_output.pci-0000_08_00.6.HiFi__hw_Generic_1__sink toggle";
        };

        startup = [
          <<startupnixos>>
        ];
      };
    };
  }

Stand

My home PC, the most powerful machine. Sadly Sway cannot make good use out of it's NVIDIA card, so it runs a dual boot setup with a kind of broken GRUB that does not autodetect the windows partition.

NixOS
  { config, lib, pkgs, inputs, ... }:

  {

    <<wrap>>

    services = {
      getty.autologinUser = "homelen";
      greetd.settings.initial_session.user="homelen";
    };

    stylix.image = ../../wallpaper/standwp.png;
    <<theme>>

    # Bootloader.
    boot.loader.grub = {
      enable = true;
      devices = ["nodev" ];
      useOSProber = true;
    };

    # boot.kernelPackages = pkgs.linuxPackages_latest;
    networking = {
      hostName = "stand"; # Define your hostname.
      enableIPv6 = false;
      firewall.enable = false;
    # networkmanager.enable = true;
    };

    hardware = {
      bluetooth.enable = true;
    };

    users.users.homelen = {
      isNormalUser = true;
      description = "Leon S";
      extraGroups = [ "networkmanager" "wheel" "lp" "audio" "video" ];
      packages = with pkgs; [];
    };

    environment.systemPackages = with pkgs; [
    ];

    system.stateVersion = "23.05"; # Did you read the comment? Dont change this basically

  }
Home Manager
  { config, pkgs, lib, fetchFromGitHub, ... }:

  {

    <<gpgagent>>

    home = {
      username = "homelen";
      homeDirectory = "/home/homelen";
      stateVersion = "23.05"; # Please read the comment before changing.
      keyboard.layout = "us";
      packages = with pkgs; [
      ];
    };

    sops.age.sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/sops" ];

    services.blueman-applet.enable = true;

    # waybar config
    programs.waybar.settings.mainBar = {
      cpu.format = "{icon0} {icon1} {icon2} {icon3} {icon4} {icon5} {icon6} {icon7}";
      temperature.hwmon-path = "/sys/devices/platform/coretemp.0/hwmon/hwmon1/temp3_input";
    };
    <<waybarpc>>

    wayland.windowManager.sway= {
      config = rec {
        input = {
          "36125:53060:splitkb.com_Kyria_rev3" = {
            xkb_layout = "us";
            xkb_variant = "altgr-intl";
          };
        };

        output = {
          DP-1 = {
            mode = "2560x1440";
            scale = "1";
            bg = "~/.dotfiles/wallpaper/standwp.png fill";
          };
        };

        keybindings = let
          modifier = config.wayland.windowManager.sway.config.modifier;
        in {
          "${modifier}+w" = "exec \"bash ~/.dotfiles/scripts/checkschildi.sh\"";
        };

        startup = [
          <<startupnixos>>
        ];
      };
    };
  }

Virtual hosts

My server setup is built on Proxmox VE; back when I started, I created all kinds of wild Debian/Ubuntu/etc. KVMs and LXCs on there. However, the root disk has suffered a weird failure where it has become unable to be cloned, but it is still functional for now. I am currently rewriting all machines on there to use NixOS instead; this is a ongoing process.

In the long run, I am thinking about a transition to kubernetes or using just a server running NixOS and using the built-in container functionality. For now however, I like the network management provided by Proxmox, as I am a bit intimidated by doing that from scratch.

TEMPLATE

NixOS
  { pkgs, modulesPath, ... }:

  {
    imports = [
      (modulesPath + "/virtualisation/proxmox-lxc.nix")
    ];

    environment.systemPackages = with pkgs; [
      git
      gnupg
      ssh-to-age
    ];

    services.xserver = {
      layout = "us";
      xkbVariant = "altgr-intl";
    };

    nix.settings.experimental-features = ["nix-command" "flakes"];

    proxmoxLXC.manageNetwork = true; # manage network myself
    proxmoxLXC.manageHostName = false; # manage hostname myself
    networking.hostName = "TEMPLATE"; # Define your hostname.
    networking.useDHCP = true;
    networking.enableIPv6 = false;
    networking.firewall.enable = false;
    services.openssh = {
      enable = true;
      settings.PermitRootLogin = "yes";
    };
    users.users.root.openssh.authorizedKeys.keyFiles = [
      ../../../secrets/keys/authorized_keys
    ];
    # users.users.root.password = "TEMPLATE";

    system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change
  }

NGINX

NixOS
  { config, pkgs, modulesPath, ... }:
  {
    imports = [
      (modulesPath + "/virtualisation/proxmox-lxc.nix")
      ./hardware-configuration.nix
    ];

    environment.systemPackages = with pkgs; [
      git
      gnupg
      ssh-to-age
      lego
      nginx
    ];

    services.xserver = {
      layout = "us";
      xkbVariant = "altgr-intl";
    };

    nix.settings.experimental-features = ["nix-command" "flakes"];

    sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
    sops.defaultSopsFile = "/.dotfiles/secrets/nginx/secrets.yaml";
    sops.validateSopsFiles = false;
    sops.secrets.dnstokenfull = {owner="acme";};
    sops.templates."certs.secret".content = ''
    CF_DNS_API_TOKEN=${config.sops.placeholder.dnstokenfull}
    '';
    proxmoxLXC.manageNetwork = true; # manage network myself
    proxmoxLXC.manageHostName = false; # manage hostname myself
    networking.hostName = "nginx"; # Define your hostname.
    networking.useDHCP = true;
    networking.enableIPv6 = false;
    networking.firewall.enable = false;
    services.openssh = {
      enable = true;
      settings.PermitRootLogin = "yes";
    };
    users.users.root.openssh.authorizedKeys.keyFiles = [
      ../../../secrets/keys/authorized_keys
    ];
    # users.users.root.password = "TEMPLATE";

    system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change

    security.acme = {
      acceptTerms = true;
      preliminarySelfsigned = false;
      defaults.email = "mrswarsel@gmail.com";
      defaults.dnsProvider = "cloudflare";
      defaults.environmentFile = "${config.sops.templates."certs.secret".path}";
    };

    environment.shellAliases = {
      nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
    };

    services.nginx = {
      enable = true;
      recommendedProxySettings = true;
      recommendedTlsSettings = true;
      recommendedOptimisation = true;
      recommendedGzipSettings = true;
      virtualHosts = {

        "stash.swarsel.win" = {
          enableACME = true;
          forceSSL = true;
          acmeRoot = null;
          locations = {
            "/" = {
              proxyPass = "https://192.168.1.5";
              extraConfig = ''
              client_max_body_size 0;
              '';
            };
            # "/push/" = {
              # proxyPass = "http://192.168.2.5:7867";
            # };
            "/.well-known/carddav" = {
              return = "301 $scheme://$host/remote.php/dav";
            };
            "/.well-known/caldav" = {
              return = "301 $scheme://$host/remote.php/dav";
            };
          };
        };

        "matrix2.swarsel.win" = {
          enableACME = true;
          forceSSL = true;
          acmeRoot = null;
          locations = {
            "~ ^(/_matrix|/_synapse/client)" = {
              proxyPass = "http://192.168.1.23:8008";
              extraConfig = ''
                  client_max_body_size 0;
                '';
            };
          };
        };


          "sound.swarsel.win" = {
            enableACME = true;
            forceSSL = true;
            acmeRoot = null;
            locations = {
              "/" = {
                proxyPass = "http://192.168.1.13:4040";
                proxyWebsockets = true;
                extraConfig = ''
                  proxy_redirect          http:// https://;
                  proxy_read_timeout      600s;
                  proxy_send_timeout      600s;
                  proxy_buffering         off;
                  proxy_request_buffering off;
                  client_max_body_size    0;
                '';
              };
            };
          };

          "scan.swarsel.win" = {
            enableACME = true;
            forceSSL = true;
            acmeRoot = null;
            locations = {
              "/" = {
                proxyPass = "http://192.168.1.24:28981";
                extraConfig = ''
                  client_max_body_size 0;
                '';
              };
            };
          };

          "screen.swarsel.win" = {
            enableACME = true;
            forceSSL = true;
            acmeRoot = null;
            locations = {
              "/" = {
                proxyPass = "http://192.168.1.16:8096";
                extraConfig = ''
                  client_max_body_size 0;
                '';
              };
            };
          };

          "matrix.swarsel.win" = {
            enableACME = true;
            forceSSL = true;
            acmeRoot = null;
            locations = {
              "~ ^(/_matrix|/_synapse/client)" = {
                proxyPass = "http://192.168.1.20:8008";
                extraConfig = ''
                  client_max_body_size 0;
                '';
              };
            };
          };

          "scroll.swarsel.win" = {
            enableACME = true;
            forceSSL = true;
            acmeRoot = null;
            locations = {
              "/" = {
                proxyPass = "http://192.168.1.22:8080";
                extraConfig = ''
                  client_max_body_size 0;
                '';
              };
            };
          };

          "blog.swarsel.win" = {
            enableACME = true;
            forceSSL = true;
            acmeRoot = null;
            locations = {
              "/" = {
                proxyPass = "https://192.168.1.7";
                extraConfig = ''
                  client_max_body_size 0;
                '';
              };
            };
          };

        };
      };





  }

[Manual steps required] Calibre

This machine requires manual setup:

  1. (obsolete for now) Set up calibre-web:

    • Create metadata.db with 664 permissions, make sure parent directory is writeable
    • Login @ books.swarsel.win using initial creds:

      • user: admin
      • pw: admin123
    • point to metadata.db file, make sure you can upload
    • Change pw, create normal user
  2. Setup kavita:

    • Login @ scrolls.swarsel.win
    • Create admin user
    • Import Libraries
    • Create normal user

In general, I am not amazed by this setup; Kavita is the reader of choice, calibre-web mostly is there to have a convenient way to fullfill the opinionated folder structure when uploading ebooks (calibre-web does not work on its own since it forces sqlite which does not work nicely with my NFS book store). I hope that in the future Kavita will implement ebook upload, or that calibre-web will ditch the sqlite constraints.

NixOS
  { config, pkgs, modulesPath, ... }:

  {
    imports = [
      (modulesPath + "/virtualisation/proxmox-lxc.nix")
      ./hardware-configuration.nix
    ];

    environment.systemPackages = with pkgs; [
      git
      gnupg
      ssh-to-age
      calibre
    ];

    users.groups.lxc_shares = {
      gid = 10000;
      members = [
              "kavita"
              "calibre-web"
              "root"
            ];
    };

    services.xserver = {
      layout = "us";
      xkbVariant = "altgr-intl";
    };

    nix.settings.experimental-features = ["nix-command" "flakes"];

    sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
    sops.defaultSopsFile = "/.dotfiles/secrets/calibre/secrets.yaml";
    sops.validateSopsFiles = false;
    sops.secrets.kavita = { owner = "kavita";};
    # sops.secrets.smbuser = { };
    # sops.secrets.smbpassword = { };
    # sops.secrets.smbdomain = { };
    # sops.templates."smb.cred".content = ''
    # user=${config.sops.placeholder.smbuser}
    # password=${config.sops.placeholder.smbpassword}
    # domain=${config.sops.placeholder.smbdomain}
    # '';
    proxmoxLXC.manageNetwork = true; # manage network myself
    proxmoxLXC.manageHostName = false; # manage hostname myself
    networking.hostName = "calibre"; # Define your hostname.
    networking.useDHCP = true;
    networking.enableIPv6 = false;
    networking.firewall.enable = false;
    services.openssh = {
      enable = true;
      settings.PermitRootLogin = "yes";
    };
    users.users.root.openssh.authorizedKeys.keyFiles = [
      ../../../secrets/keys/authorized_keys
    ];

    system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change

    environment.shellAliases = {
      nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
    };


      # services.calibre-server = {
      # enable = true;
      # user = "calibre-server";
      # auth.enable = true;
      # auth.userDb = "/srv/calibre/users.sqlite";
      # libraries = [
      #   /media/Books/main
      #   /media/Books/diverse
      #   /media/Books/language
      #   /media/Books/science
      #   /media/Books/sport
      #   /media/Books/novels
      # ];
    # };

    # services.calibre-web = {
    #   enable = true;
    #   user = "calibre-web";
    #   group = "calibre-web";
    #   listen.port = 8083;
    #   listen.ip = "0.0.0.0";
    #   options = {
    #     enableBookUploading = true;
    #     enableKepubify = true;
    #     enableBookConversion = true;
    #   };
    # };

    services.kavita = {
      enable = true;
      user = "kavita";
      port = 8080;
      tokenKeyFile = config.sops.secrets.kavita.path;
    };


  }

Jellyfin

NixOS
  { config, pkgs, modulesPath, ... }:

  {
    imports = [
      (modulesPath + "/virtualisation/proxmox-lxc.nix")
      ./hardware-configuration.nix
    ];

    environment.systemPackages = with pkgs; [
      git
      gnupg
      ssh-to-age
    ];

    users.groups.lxc_shares = {
      gid = 10000;
      members = [
        "jellyfin"
        "root"
      ];
    };

    users.users.jellyfin = {
      extraGroups  = [ "video" "render" ];
    };

    services.xserver = {
      layout = "us";
      xkbVariant = "altgr-intl";
    };

    nix.settings.experimental-features = ["nix-command" "flakes"];

    # sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
    # sops.defaultSopsFile = "/.dotfiles/secrets/jellyfin/secrets.yaml";
    # sops.validateSopsFiles = false;

    proxmoxLXC.manageNetwork = true; # manage network myself
    proxmoxLXC.manageHostName = false; # manage hostname myself
    networking.hostName = "jellyfin"; # Define your hostname.
    networking.useDHCP = true;
    networking.enableIPv6 = false;
    networking.firewall.enable = false;
    services.openssh = {
      enable = true;
      settings.PermitRootLogin = "yes";
    };
    users.users.root.openssh.authorizedKeys.keyFiles = [
      ../../../secrets/keys/authorized_keys
    ];

    system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change

    environment.shellAliases = {
      nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
    };

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

    services.jellyfin = {
      enable = true;
      user = "jellyfin";
      # openFirewall = true; # this works only for the default ports
    };

  }

[WIP/Incomplete/Untested] Transmission

This stuff just does not work, I seem to be unable to create a working VPN Split Tunneling on NixOS. Maybe this is introduced by the wonky Proxmox-NixOS container interaction, I am not sure. For now, this machine does not work at all and I am stuck with my Debian Container that does this for me …

NixOS
              { config, pkgs, modulesPath, ... }:

              {
                imports = [
                  (modulesPath + "/virtualisation/proxmox-lxc.nix")
                  ./hardware-configuration.nix
                  # ./openvpn.nix #this file holds the vpn login data
                ];

                environment.systemPackages = with pkgs; [
                  git
                  gnupg
                  ssh-to-age
                  openvpn
                  jq
                  iptables
                  busybox
                  wireguard-tools
                ];

                users.groups.lxc_shares = {
                  gid = 10000;
                  members = [
                    "vpn"
                    "radarr"
                    "sonarr"
                    "lidarr"
                    "readarr"
                    "root"
                  ];
                };
                users.groups.vpn = {};

                users.users.vpn = {
                  isNormalUser = true;
                  group = "vpn";
                  home = "/home/vpn";
                };

                services.xserver = {
                  layout = "us";
                  xkbVariant = "altgr-intl";
                };

                nix.settings.experimental-features = ["nix-command" "flakes"];

                sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
                sops.defaultSopsFile = "/.dotfiles/secrets/transmission/secrets.yaml";
                sops.validateSopsFiles = false;

                boot.kernelModules = [ "tun" ];
                proxmoxLXC.manageNetwork = true; # manage network myself
                proxmoxLXC.manageHostName = false; # manage hostname myself
                networking.hostName = "transmission"; # Define your hostname.
                networking.useDHCP = true;
                networking.enableIPv6 = false;
                networking.firewall.enable = false;

                services.radarr = {
                  enable = true;
                };

                services.readarr = {
                  enable = true;
                };
                services.sonarr = {
                  enable = true;
                };
                services.lidarr = {
                  enable = true;
                };
                services.prowlarr = {
                  enable = true;
                };

                # networking.interfaces = {
                    # lo = {
                      # useDHCP = false;
                      # ipv4.addresses = [
                        # { address = "127.0.0.1"; prefixLength = 8; }
                      # ];
                    # };
                #
                    # eth0 = {
                      # useDHCP = true;
                    # };
                  # };

                # networking.firewall.extraCommands = ''
                # sudo iptables -A OUTPUT ! -o lo -m owner --uid-owner vpn -j DROP
                # '';
                networking.iproute2 = {
                  enable = true;
                  rttablesExtraConfig = ''
                  200     vpn
                  '';
                };
                # boot.kernel.sysctl = {
                #   "net.ipv4.conf.all.rp_filter" = 2;
                #   "net.ipv4.conf.default.rp_filter" = 2;
                #   "net.ipv4.conf.eth0.rp_filter" = 2;
                # };
                environment.etc = {
                  "openvpn/iptables.sh" =
                    { source = ../../../scripts/server1/iptables.sh;
                      mode = "0755";
                    };
                  "openvpn/update-resolv-conf" =
                    { source = ../../../scripts/server1/update-resolv-conf;
                      mode = "0755";
                    };
                  "openvpn/routing.sh" =
                    { source = ../../../scripts/server1/routing.sh;
                      mode = "0755";
                    };
                  "openvpn/ca.rsa.2048.crt" =
                    { source = ../../../secrets/certs/ca.rsa.2048.crt;
                      mode = "0644";
                    };
                  "openvpn/crl.rsa.2048.pem" =
                    { source = ../../../secrets/certs/crl.rsa.2048.pem;
                      mode = "0644";
                    };
                };
                services.openssh = {
                  enable = true;
                  settings.PermitRootLogin = "yes";
                  listenAddresses = [{
                                     port = 22;
                                     addr = "0.0.0.0";
                                   }];
                };
                users.users.root.openssh.authorizedKeys.keyFiles = [
                  ../../../secrets/keys/authorized_keys
                ];

                system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change
                # users.users.root.password = "TEMPLATE";

                environment.shellAliases = {
                  nswitch = "cd /.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
                };

                sops.secrets.vpnuser = {};
                sops.secrets.rpcuser = {owner="vpn";};
                sops.secrets.vpnpass = {};
                sops.secrets.rpcpass = {owner="vpn";};
                sops.secrets.vpnprot = {};
                sops.secrets.vpnloc = {};
                # sops.secrets.crlpem = {};
                # sops.secrets.capem = {};
                sops.templates."transmission-rpc".owner = "vpn";
                sops.templates."transmission-rpc".content = builtins.toJSON {
                  rpc-username = config.sops.placeholder.rpcuser;
                  rpc-password = config.sops.placeholder.rpcpass;
                };

                sops.templates.pia.content = ''
                ${config.sops.placeholder.vpnuser}
                ${config.sops.placeholder.vpnpass}
                '';

                sops.templates.vpn.content = ''
                  client
                  dev tun
                  proto ${config.sops.placeholder.vpnprot}
                  remote ${config.sops.placeholder.vpnloc}
                  resolv-retry infinite
                  nobind
                  persist-key
                  persist-tun
                  cipher aes-128-cbc
                  auth sha1
                  tls-client
                  remote-cert-tls server

                  auth-user-pass ${config.sops.templates.pia.path}
                  compress
                  verb 1
                  reneg-sec 0

                  crl-verify /etc/openvpn/crl.rsa.2048.pem
                  ca /etc/openvpn/ca.rsa.2048.crt

                  disable-occ
                  dhcp-option DNS 209.222.18.222
                  dhcp-option DNS 209.222.18.218
                  dhcp-option DNS 8.8.8.8
                  route-noexec
                '';

                # services.pia.enable = true;
                # services.pia.authUserPass.username = "na";
                # services.pia.authUserPass.password = "na";


              #     systemd.services.openvpn-vpn = {
              # wantedBy = [ "multi-user.target" ];
              # after = [ "network.target" ];
              # description = "OpenVPN connection to pia";
              # serviceConfig = {
              #   Type = "forking";
              #   RuntimeDirectory="openvpn";
              #   PrivateTmp=true;
              #   KillMode="mixed";
              #   ExecStart = ''@${pkgs.openvpn}/sbin/openvpn openvpn --daemon ovpn-pia --status /run/openvpn/pia.status 10 --cd /etc/openvpn --script-security 2 --config ${config.sops.templates.vpn.path} --writepid /run/openvpn/pia.pid'';
              #   PIDFile=''/run/openvpn/pia.pid'';
              #   ExecReload=''/run/current-system/sw/bin/kill -HUP $MAINPID'';
              #   WorkingDirectory="/etc/openvpn";
              #   Restart="on-failure";
              #   RestartSec=30;
              #   ProtectSystem="yes";
              #   DeviceAllow=["/dev/null rw" "/dev/net/tun rw"];
              # };
           # };
            services.openvpn.servers = {
              pia = {
                autoStart = false;
                updateResolvConf = true;
  #               up = ''
  # export INTERFACE="tun0"
  # export VPNUSER="vpn"
  # export LOCALIP="192.168.1.191"
  # export NETIF="eth0"
  # export VPNIF="tun0"
  # export GATEWAYIP=$(ifconfig $VPNIF | egrep -o '([0-9]{1,3}\.){3}[0-9]{1,3}' | egrep -v '255|(127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})' | tail -n1)
  # iptables -F -t nat
  # iptables -F -t mangle
  # iptables -F -t filter
  # iptables -t mangle -A OUTPUT -j CONNMARK --restore-mark
  # iptables -t mangle -A OUTPUT ! --dest $LOCALIP -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1
  # iptables -t mangle -A OUTPUT --dest $LOCALIP -p udp --dport 53 -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1
  # iptables -t mangle -A OUTPUT --dest $LOCALIP -p tcp --dport 53 -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1
  # iptables -t mangle -A OUTPUT ! --src $LOCALIP -j MARK --set-mark 0x1
  # iptables -t mangle -A OUTPUT -j CONNMARK --save-mark
  # iptables -A INPUT -i $INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
  # iptables -A INPUT -i $INTERFACE -j REJECT
  # iptables -A OUTPUT -o lo -m owner --uid-owner $VPNUSER -j ACCEPT
  # iptables -A OUTPUT -o $INTERFACE -m owner --uid-owner $VPNUSER -j ACCEPT
  # iptables -t nat -A POSTROUTING -o $INTERFACE -j MASQUERADE
  # iptables -A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
  # iptables -A OUTPUT ! --src $LOCALIP -o $NETIF -j REJECT
  # if [[ `ip rule list | grep -c 0x1` == 0 ]]; then
  # ip rule add from all fwmark 0x1 lookup $VPNUSER
  # fi
  # ip route replace default via $GATEWAYIP table $VPNUSER
  # ip route append default via 127.0.0.1 dev lo table $VPNUSER
  # ip route flush cache
                # '';
                # down = "bash /etc/openvpn/update-resolv-conf";
                # these are outsourced to a local file, I am not sure if it can be done with sops-nix
                # authUserPass = {
                  # username = "TODO:secrets";
                  # password = "TODO:secrets";
                # };
                config = "config ${config.sops.templates.vpn.path}";
              };
            };

          services.transmission = {
            enable = true;
            credentialsFile = config.sops.templates."transmission-rpc".path;
            user = "vpn";
            group = "lxc_shares";
            settings = {

            alt-speed-down= 8000;
            alt-speed-enabled= false;
            alt-speed-time-begin= 0;
            alt-speed-time-day= 127;
            alt-speed-time-enabled= true;
            alt-speed-time-end= 360;
            alt-speed-up= 2000;
            bind-address-ipv4= "0.0.0.0";
            bind-address-ipv6= "::";
            blocklist-enabled= false;
            blocklist-url= "http://www.example.com/blocklist";
            cache-size-mb= 4;
            dht-enabled= false;
            download-dir= "/media/Eternor/New";
            download-limit= 100;
            download-limit-enabled= 0;
            download-queue-enabled= true;
            download-queue-size= 5;
            encryption= 2;
            idle-seeding-limit= 30;
            idle-seeding-limit-enabled= false;
            incomplete-dir= "/var/lib/transmission-daemon/Downloads";
            incomplete-dir-enabled= false;
            lpd-enabled= false;
            max-peers-global= 200;
            message-level= 1;
            peer-congestion-algorithm= "";
            peer-id-ttl-hours= 6;
            peer-limit-global= 100;
            peer-limit-per-torrent= 40;
            peer-port= 22371;
            peer-port-random-high= 65535;
            peer-port-random-low= 49152;
            peer-port-random-on-start= false;
            peer-socket-tos= "default";
            pex-enabled= false;
            port-forwarding-enabled= false;
            preallocation= 1;
            prefetch-enabled= true;
            queue-stalled-enabled= true;
            queue-stalled-minutes= 30;
            ratio-limit= 2;
            ratio-limit-enabled= false;
            rename-partial-files= true;
            rpc-authentication-required= true;
            rpc-bind-address= "0.0.0.0";
            rpc-enabled= true;
            rpc-host-whitelist= "";
            rpc-host-whitelist-enabled= true;
            rpc-port= 9091;
            rpc-url= "/transmission/";
            rpc-whitelist= "127.0.0.1,192.168.3.2";
            rpc-whitelist-enabled= true;
            scrape-paused-torrents-enabled= true;
            script-torrent-done-enabled= false;
            seed-queue-enabled= false;
            seed-queue-size= 10;
            speed-limit-down= 6000;
            speed-limit-down-enabled= true;
            speed-limit-up= 500;
            speed-limit-up-enabled= true;
            start-added-torrents= true;
            trash-original-torrent-files= false;
            umask= 2;
            upload-limit= 100;
            upload-limit-enabled= 0;
            upload-slots-per-torrent= 14;
            utp-enabled= false;
            };
          };

        # services.nginx = {
        #       enable = true;
        #       virtualHosts = {

        #         "192.168.1.192" = {
        #           locations = {
        #             "/transmission" = {
        #               proxyPass = "http://127.0.0.1:9091";
        #               extraConfig = ''
        #               proxy_set_header Host $host;
        #               proxy_set_header X-Real-IP $remote_addr;
        #               proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        #               '';
        #             };
        #           };
        #         };
        #       };
        # };


              }

[Manual steps needed] Matrix

  1. After the initial setup, run the

    • /run/secrets-generated/matrix_user_register.sh

command to register a new admin user.

  1. All bridges will fail on first start, copy the registration files using:

    • cp var/lib/mautrix-telegram/telegram-registration.yaml /var/lib/matrix-synapse
    • chown matrix-synapse:matrix-synapse var/lib/matrix-synapse/telegram-registration.yaml

Make sure to also do this for doublepuppet.yaml

  1. Restart postgresql.service, matrix-synapse.service, mautrix-whatsapp.service, mautrix-telegram.service
NixOS
  { config, pkgs, modulesPath, unstable, sops, ... }: let
    matrixDomain = "matrix2.swarsel.win";
  in {
    <<vminitbare>>

    imports = [
      (modulesPath + "/virtualisation/proxmox-lxc.nix")
      ./hardware-configuration.nix
      # we import here a service that is not available yet on normal nixpkgs
      # this module is hence not in the modules list, we add it ourselves
      (unstable + "/nixos/modules/services/matrix/mautrix-signal.nix")
    ];

    networking.hostName = "matrix"; # Define your hostname.
    networking.firewall.enable = false;
    environment.systemPackages = with pkgs; [
      git
      gnupg
      ssh-to-age
      matrix-synapse
      lottieconverter
      ffmpeg
    ];

    sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
    sops.defaultSopsFile = "/.dotfiles/secrets/matrix/secrets.yaml";
    sops.validateSopsFiles = false;
    sops.secrets.matrixsharedsecret = {owner="matrix-synapse";};
    sops.templates."matrix_user_register.sh".content = ''
    register_new_matrix_user -k ${config.sops.placeholder.matrixsharedsecret} http://localhost:8008
    '';
    sops.templates.matrixshared.owner = "matrix-synapse";
    sops.templates.matrixshared.content = ''
    registration_shared_secret: ${config.sops.placeholder.matrixsharedsecret}
    '';
    sops.secrets.mautrixtelegram_as = {owner="matrix-synapse";};
    sops.secrets.mautrixtelegram_hs = {owner="matrix-synapse";};
    sops.secrets.mautrixtelegram_api_id = {owner="matrix-synapse";};
    sops.secrets.mautrixtelegram_api_hash = {owner="matrix-synapse";};
    sops.templates.mautrixtelegram.owner = "matrix-synapse";
    sops.templates.mautrixtelegram.content = ''
    MAUTRIX_TELEGRAM_APPSERVICE_AS_TOKEN=${config.sops.placeholder.mautrixtelegram_as}
    MAUTRIX_TELEGRAM_APPSERVICE_HS_TOKEN=${config.sops.placeholder.mautrixtelegram_hs}
    MAUTRIX_TELEGRAM_TELEGRAM_API_ID=${config.sops.placeholder.mautrixtelegram_api_id}
    MAUTRIX_TELEGRAM_TELEGRAM_API_HASH=${config.sops.placeholder.mautrixtelegram_api_hash}
    '';
    # sops.secrets.mautrixwhatsapp_shared = {owner="matrix-synapse";};
    # sops.templates.mautrixwhatsapp.owner = "matrix-synapse";
    # sops.templates.mautrixwhatsapp.content = ''
    # MAUTRIX_WHATSAPP_BRIDGE_LOGIN_SHARED_SECRET=${config.sops.placeholder.mautrixwhatsapp_shared}
    # '';

    services.postgresql.enable = true;
    services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" ''
      CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
      CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
        TEMPLATE template0
        LC_COLLATE = "C"
        LC_CTYPE = "C";
      CREATE ROLE "mautrix-telegram" WITH LOGIN PASSWORD 'telegram';
      CREATE DATABASE "mautrix-telegram" WITH OWNER "mautrix-telegram"
        TEMPLATE template0
        LC_COLLATE = "C"
        LC_CTYPE = "C";
      CREATE ROLE "mautrix-whatsapp" WITH LOGIN PASSWORD 'whatsapp';
      CREATE DATABASE "mautrix-whatsapp" WITH OWNER "mautrix-whatsapp"
        TEMPLATE template0
        LC_COLLATE = "C"
        LC_CTYPE = "C";
      CREATE ROLE "mautrix-signal" WITH LOGIN PASSWORD 'signal';
      CREATE DATABASE "mautrix-signal" WITH OWNER "mautrix-signal"
        TEMPLATE template0
        LC_COLLATE = "C"
        LC_CTYPE = "C";
    '';

    services.matrix-synapse = {
      settings.app_service_config_files = [
        "/var/lib/matrix-synapse/telegram-registration.yaml"
        "/var/lib/matrix-synapse/whatsapp-registration.yaml"
        "/var/lib/matrix-synapse/signal-registration.yaml"
        "/var/lib/matrix-synapse/doublepuppet.yaml"
      ];
      enable = true;
      settings.server_name = matrixDomain;
      settings.public_baseurl = "https://${matrixDomain}";
      extraConfigFiles = [
        config.sops.templates.matrixshared.path
      ];
      settings.listeners = [
        { port = 8008;
          bind_addresses = [ "0.0.0.0" ];
          type = "http";
          tls = false;
          x_forwarded = true;
          resources = [
            {
              names = [ "client" "federation" ];
              compress = true;
            }
          ];
        }
      ];
    };

    services.mautrix-telegram = {
      enable = true;
      environmentFile = config.sops.templates.mautrixtelegram.path;
      settings = {
        homeserver = {
          address = "http://localhost:8008";
          domain = matrixDomain;
        };
        appservice = {
          address= "http://localhost:29317";
          hostname = "0.0.0.0";
          port = "29317";
          provisioning.enabled = true;
          id = "telegram";
          # ephemeral_events = true; # not needed due to double puppeting
          public = {
            enabled = false;
          };
          database = "postgresql:///mautrix-telegram?host=/run/postgresql";
        };
        bridge = {
          # login_shared_secret_map = {
            # matrixDomain = "as_token:doublepuppet";
          # };
          relaybot.authless_portals = true;
          allow_avatar_remove = true;
          allow_contact_info = true;
          sync_channel_members = true;
          startup_sync = true;
          sync_create_limit = 0;
          sync_direct_chats = true;
          telegram_link_preview = true;
          permissions = {
            "*" = "relaybot";
            "@swarsel:${matrixDomain}" = "admin";
          };
          # Animated stickers conversion requires additional packages in the
          # service's path.
          # If this isn't a fresh installation, clearing the bridge's uploaded
          # file cache might be necessary (make a database backup first!):
          # delete from telegram_file where \
          #   mime_type in ('application/gzip', 'application/octet-stream')
          animated_sticker = {
            target = "gif";
            args = {
              width = 256;
              height = 256;
              fps = 30;               # only for webm
              background = "020202";  # only for gif, transparency not supported
            };
          };
        };
      };
    };
    systemd.services.mautrix-telegram.path = with pkgs; [
      lottieconverter  # for animated stickers conversion, unfree package
      ffmpeg           # if converting animated stickers to webm (very slow!)
    ];

    services.mautrix-whatsapp = {
      enable = true;
      # environmentFile = config.sops.templates.mautrixwhatsapp.path;
      settings = {
        homeserver = {
          address = "http://localhost:8008";
          domain = matrixDomain;
        };
        appservice = {
          address= "http://localhost:29318";
          hostname = "0.0.0.0";
          port = 29318;
          database = {
            type = "postgres";
            uri = "postgresql:///mautrix-whatsapp?host=/run/postgresql";
          };
        };
        bridge = {
          displayname_template = "{{or .FullName .PushName .JID}} (WA)";
          history_sync = {
            backfill = true;
            max_initial_conversations = -1;
            message_count = -1;
            request_full_sync = true;
            full_sync_config = {
              days_limit = 900;
              size_mb_limit = 5000;
              storage_quota_mb = 5000;
            };
          };
          login_shared_secret_map = {
            matrixDomain = "as_token:doublepuppet";
          };
          sync_manual_marked_unread = true;
          send_presence_on_typing = true;
          parallel_member_sync = true;
          url_previews = true;
          caption_in_message = true;
          extev_polls = true;
          permissions = {
            "*" = "relaybot";
            "@swarsel:${matrixDomain}" = "admin";
          };
        };
      };
    };

    services.mautrix-signal = {
      enable = true;
      # environmentFile = config.sops.templates.mautrixwhatsapp.path;
      settings = {
        homeserver = {
          address = "http://localhost:8008";
          domain = matrixDomain;
        };
        appservice = {

          address= "http://localhost:29328";
          hostname = "0.0.0.0";
          port = 29328;
          database = {
            type = "postgres";
            uri = "postgresql:///mautrix-signal?host=/run/postgresql";
          };
        };
        bridge = {
          displayname_template = "{{or .ContactName .ProfileName .PhoneNumber}} (Signal)";
          login_shared_secret_map = {
            matrixDomain = "as_token:doublepuppet";
          };
          caption_in_message = true;
          permissions = {
            "*" = "relaybot";
            "@swarsel:${matrixDomain}" = "admin";
          };
        };
      };
    };

    # restart the bridges daily. this is done for the signal bridge mainly which stops carrying
    # messages out after a while.

    systemd.timers."restart-bridges" = {
      wantedBy = [ "timers.target" ];
      timerConfig = {
        OnBootSec = "1d";
        OnUnitActiveSec = "1d";
        Unit = "restart-bridges.service";
      };
    };

    systemd.services."restart-bridges" = {
      script = ''
      systemctl restart mautrix-whatsapp.service
      systemctl restart mautrix-signal.service
      systemctl restart mautrix-telegram.service
      '';
      serviceConfig = {
        Type = "oneshot";
        User = "root";
      };
    };

  }

Sound

NixOS
  { config, pkgs, modulesPath, ... }:

  {
    <<vminit>>

    proxmoxLXC.privileged = true; # manage hostname myself

    users.groups.lxc_pshares = {
      gid = 110000;
      members = [
        "navidrome"
        "mpd"
        "root"
      ];
    };

    users.groups.navidrome = {
      gid = 61593;
    };

    users.groups.mpd = {};

    users.users.navidrome = {
      isSystemUser = true;
      uid = 61593;
      group = "navidrome";
      extraGroups  = [ "audio" "utmp" ];
    };

    users.users.mpd = {
      isSystemUser = true;
      group = "mpd";
      extraGroups  = [ "audio" "utmp" ];
    };

    sound = {
      enable = true;
    };

    hardware.enableAllFirmware = true;
    networking.hostName = "sound"; # Define your hostname.
    networking.firewall.enable = false;
    environment.systemPackages = with pkgs; [
      git
      gnupg
      ssh-to-age
      pciutils
      alsa-utils
      mpv
    ];

    sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
    sops.defaultSopsFile = "/.dotfiles/secrets/sound/secrets.yaml";
    sops.validateSopsFiles = false;
    sops.secrets.mpdpass = { owner = "mpd";};

    services.navidrome = {
      enable = true;
      settings = {
        Address = "0.0.0.0";
        Port = 4040;
        MusicFolder = "/media";
        EnableSharing = true;
        EnableTranscodingConfig = true;
        Scanner.GroupAlbumReleases = true;
        ScanSchedule = "@every 1d";
        # Insert these values locally as sops-nix does not work for them
        LastFM.ApiKey = TEMPLATE;
        LastFM.Secret = TEMPLATE;
        Spotify.ID = TEMPLATE;
        Spotify.Secret = TEMPLATE;
        UILoginBackgroundUrl = "https://i.imgur.com/OMLxi7l.png";
        UIWelcomeMessage = "~SwarselSound~";
      };
    };
    services.mpd = {
      enable = true;
      musicDirectory = "/media";
      user = "mpd";
      group = "mpd";
      network = {
        port = 3254;
        listenAddress = "any";
      };
      credentials = [
        {
          passwordFile = config.sops.secrets.mpdpass.path;
          permissions = [
            "read"
            "add"
            "control"
            "admin"
          ];
        }
      ];
    };
  }

Spotifyd

NixOS
  { config, pkgs, modulesPath, ... }:

  {
    <<vminit>>

    proxmoxLXC.privileged = true; # manage hostname myself

    users.groups.spotifyd = {
      gid = 65136;
    };

    users.users.spotifyd = {
      isSystemUser = true;
      uid = 65136;
      group = "spotifyd";
      extraGroups  = [ "audio" "utmp" ];
    };

    sound = {
      enable = true;
    };

    hardware.enableAllFirmware = true;
    networking.hostName = "spotifyd"; # Define your hostname.
    networking.firewall.enable = false;
    environment.systemPackages = with pkgs; [
      git
      gnupg
      ssh-to-age
    ];

    # sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
    # sops.defaultSopsFile = "/.dotfiles/secrets/spotifyd/secrets.yaml";
    # sops.validateSopsFiles = false;

    services.spotifyd = {
      enable = true;
      settings = {
        global = {
          dbus_type = "session";
          use_mpris = false;
          device = "default:CARD=PCH";
          device_name = "SwarselSpot";
          mixer = "alsa";
          zeroconf_port = 1025;
        };
      };
    };

  }

Sync

NixOS
  { config, pkgs, modulesPath, ... }:

  {
    imports = [
      ./hardware-configuration.nix
    ];

    environment.systemPackages = with pkgs; [
      git
      gnupg
      ssh-to-age
    ];

    services.xserver = {
      layout = "us";
      xkbVariant = "altgr-intl";
    };

    nix.settings.experimental-features = ["nix-command" "flakes"];

    sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
    sops.defaultSopsFile = "/root/.dotfiles/secrets/sync/secrets.yaml";
    sops.validateSopsFiles = false;
    sops.secrets.swarsel = { owner = "root";};
    sops.secrets.dnstokenfull = {owner="acme";};
    sops.templates."certs.secret".content = ''
    CF_DNS_API_TOKEN=${config.sops.placeholder.dnstokenfull}
    '';

    security.acme = {
      acceptTerms = true;
      preliminarySelfsigned = false;
      defaults.email = "mrswarsel@gmail.com";
      defaults.dnsProvider = "cloudflare";
      defaults.environmentFile = "${config.sops.templates."certs.secret".path}";
    };

    services.nginx = {
      enable = true;
      recommendedProxySettings = true;
      recommendedTlsSettings = true;
      recommendedOptimisation = true;
      recommendedGzipSettings = true;
      virtualHosts = {

        "synki.swarsel.win" = {
          enableACME = true;
          forceSSL = true;
          acmeRoot = null;
          locations = {
            "/" = {
              proxyPass = "http://localhost:27701";
              extraConfig = ''
                  client_max_body_size 0;
                '';
            };
          };
        };

          "sync.swarsel.win" = {
            enableACME = true;
            forceSSL = true;
            acmeRoot = null;
            locations = {
              "/" = {
                proxyPass = "http://localhost:8384/";
                extraConfig = ''
                  client_max_body_size 0;
                '';
              };
            };
          };

          "swagit.swarsel.win" = {
            enableACME = true;
            forceSSL = true;
            acmeRoot = null;
            locations = {
              "/" = {
                proxyPass = "http://localhost:3000";
                extraConfig = ''
                  client_max_body_size 0;
                '';
              };
            };
          };
      };
    };

    boot.tmp.cleanOnBoot = true;
    zramSwap.enable = false;
    networking.hostName = "sync";
    networking.enableIPv6 = false;
    networking.domain = "subnet03112148.vcn03112148.oraclevcn.com";
    networking.firewall.extraCommands = ''
    iptables -I INPUT -m state --state NEW -p tcp --dport 80 -j ACCEPT
    iptables -I INPUT -m state --state NEW -p tcp --dport 443 -j ACCEPT
    iptables -I INPUT -m state --state NEW -p tcp --dport 27701 -j ACCEPT
    iptables -I INPUT -m state --state NEW -p tcp --dport 8384 -j ACCEPT
    iptables -I INPUT -m state --state NEW -p tcp --dport 3000 -j ACCEPT
    iptables -I INPUT -m state --state NEW -p tcp --dport 22000 -j ACCEPT
    iptables -I INPUT -m state --state NEW -p udp --dport 22000 -j ACCEPT
    iptables -I INPUT -m state --state NEW -p udp --dport 21027 -j ACCEPT
    '';
    services.openssh = {
      enable = true;
      settings.PermitRootLogin = "yes";
    };
    users.users.root.openssh.authorizedKeys.keyFiles = [
      ../../../../secrets/keys/authorized_keys
    ];

    system.stateVersion = "23.11"; # TEMPLATE - but probably no need to change

    environment.shellAliases = {
      nswitch = "cd ~/.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
    };

    boot.loader.grub.device = "nodev";

    services.anki-sync-server = {
      enable = true;
      port = 27701;
      address = "0.0.0.0";
      openFirewall = true;
      users = [
        {
        username = "Swarsel";
        passwordFile = config.sops.secrets.swarsel.path;
        }
      ];
    };

    services.syncthing = {
      enable = true;
      guiAddress = "0.0.0.0:8384";
      openDefaultPorts = true;
    };

    services.forgejo = {
      enable = true;
      settings = {
        DEFAULT = {
          APP_NAME = "~SwaGit~";
        };
        server = {
          PROTOCOL = "http";
          HTTP_PORT = 3000;
          HTTP_ADDR = "0.0.0.0";
          DOMAIN = "swagit.swarsel.win";
          ROOT_URL = "https://swagit.swarsel.win";
        };
        service = {
          DISABLE_REGISTRATION = true;
          SHOW_REGISTRATION_BUTTON = false;
        };
      };
    };

  }

[Manual steps required] Swatrix

NixOS

The files mentioned by

    settings.app_service_config_files = [
        "/var/lib/matrix-synapse/telegram-registration.yaml"
        "/var/lib/matrix-synapse/whatsapp-registration.yaml"
        "/var/lib/matrix-synapse/signal-registration.yaml"
        "/var/lib/matrix-synapse/doublepuppet.yaml"
      ]

need to be moved to the corresponding location. The below files are created as soon as the appservice is run once. This means that matrix will crash on the first startup; afterwards run these commands and restart the service.

  cp /var/lib/mautrix-telegram/telegram-registration.yaml /var/lib/matrix-synapse/
  chown matrix-synapse:matrix-synapse /var/lib/matrix-synapse/telegram-registration.yaml
  cp /var/lib/mautrix-signal/signal-registration.yaml /var/lib/matrix-synapse/
  chown matrix-synapse:matrix-synapse /var/lib/matrix-synapse/signal-registration.yaml
  cp /var/lib/mautrix-whatsapp/whatsapp-registration.yaml /var/lib/matrix-synapse/
  chown matrix-synapse:matrix-synapse /var/lib/matrix-synapse/whatsapp-registration.yaml

as for the contents of doublepuppet.yaml:

id: doublepuppet
url:
as_token: doublepuppet
hs_token: notused
sender_localpart: notused
rate_limited: false
namespaces:
  users:
  - regex: '@.*:matrix2\.swarsel\.win'
    exclusive: false

Lastly, the machine that runs matrix needs to regularly update, as otherwise you will lose connectivity to the bridges.

  { config, pkgs, modulesPath, unstable, sops, ... }: let
    matrixDomain = "swatrix.swarsel.win";
  in {

    imports = [
      ./hardware-configuration.nix
      # (unstable + "/nixos/modules/services/matrix/mautrix-signal.nix") # no longer needed; mautrix-signal was added to nixpkgs
    ];

    environment.systemPackages = with pkgs; [
      git
      gnupg
      ssh-to-age
      matrix-synapse
      lottieconverter
      ffmpeg
    ];

    services.xserver = {
      layout = "us";
      xkbVariant = "altgr-intl";
    };

    nix.settings.experimental-features = ["nix-command" "flakes"];

    sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
    sops.defaultSopsFile = "/root/.dotfiles/secrets/omatrix/secrets.yaml";
    sops.validateSopsFiles = false;
    sops.secrets.dnstokenfull = {owner="acme";};
    sops.templates."certs.secret".content = ''
    CF_DNS_API_TOKEN=${config.sops.placeholder.dnstokenfull}
    '';

    documentation = {
      enable = false;
    };

    security.acme = {
      acceptTerms = true;
      preliminarySelfsigned = false;
      defaults.email = "mrswarsel@gmail.com";
      defaults.dnsProvider = "cloudflare";
      defaults.environmentFile = "${config.sops.templates."certs.secret".path}";
    };

    services.nginx = {
      enable = true;
      recommendedProxySettings = true;
      recommendedTlsSettings = true;
      recommendedOptimisation = true;
      recommendedGzipSettings = true;
      virtualHosts = {

        "swatrix.swarsel.win" = {
          enableACME = true;
          forceSSL = true;
          acmeRoot = null;
          locations = {
            "~ ^(/_matrix|/_synapse/client)" = {
              proxyPass = "http://localhost:8008";
              extraConfig = ''
                  client_max_body_size 0;
                '';
            };
          };
        };
      };
    };

    boot.tmp.cleanOnBoot = true;
    zramSwap.enable = false;
    networking.hostName = "swatrix";
    networking.enableIPv6 = false;
    # networking.domain = "subnet03112148.vcn03112148.oraclevcn.com";
    networking.domain = "swarsel.win";
    networking.firewall.extraCommands = ''
    iptables -I INPUT -m state --state NEW -p tcp --dport 80 -j ACCEPT
    iptables -I INPUT -m state --state NEW -p tcp --dport 443 -j ACCEPT
    iptables -I INPUT -m state --state NEW -p tcp --dport 8008 -j ACCEPT
    iptables -I INPUT -m state --state NEW -p tcp --dport 29317 -j ACCEPT
    iptables -I INPUT -m state --state NEW -p tcp --dport 29318 -j ACCEPT
    iptables -I INPUT -m state --state NEW -p tcp --dport 29328 -j ACCEPT
    '';
    services.openssh = {
      enable = true;
      settings.PermitRootLogin = "yes";
    };
    users.users.root.openssh.authorizedKeys.keyFiles = [
      ../../../../secrets/keys/authorized_keys
    ];

    system.stateVersion = "23.11"; # TEMPLATE - but probably no need to change

    environment.shellAliases = {
      nswitch = "cd ~/.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
    };

    boot.loader.grub.device = "nodev";

    sops.secrets.matrixsharedsecret = {owner="matrix-synapse";};
    sops.templates."matrix_user_register.sh".content = ''
    register_new_matrix_user -k ${config.sops.placeholder.matrixsharedsecret} http://localhost:8008
    '';
    sops.templates.matrixshared.owner = "matrix-synapse";
    sops.templates.matrixshared.content = ''
    registration_shared_secret: ${config.sops.placeholder.matrixsharedsecret}
    '';
    sops.secrets.mautrixtelegram_as = {owner="matrix-synapse";};
    sops.secrets.mautrixtelegram_hs = {owner="matrix-synapse";};
    sops.secrets.mautrixtelegram_api_id = {owner="matrix-synapse";};
    sops.secrets.mautrixtelegram_api_hash = {owner="matrix-synapse";};
    sops.templates.mautrixtelegram.owner = "matrix-synapse";
    sops.templates.mautrixtelegram.content = ''
    MAUTRIX_TELEGRAM_APPSERVICE_AS_TOKEN=${config.sops.placeholder.mautrixtelegram_as}
    MAUTRIX_TELEGRAM_APPSERVICE_HS_TOKEN=${config.sops.placeholder.mautrixtelegram_hs}
    MAUTRIX_TELEGRAM_TELEGRAM_API_ID=${config.sops.placeholder.mautrixtelegram_api_id}
    MAUTRIX_TELEGRAM_TELEGRAM_API_HASH=${config.sops.placeholder.mautrixtelegram_api_hash}
    '';
    # sops.secrets.mautrixwhatsapp_shared = {owner="matrix-synapse";};
    # sops.templates.mautrixwhatsapp.owner = "matrix-synapse";
    # sops.templates.mautrixwhatsapp.content = ''
    # MAUTRIX_WHATSAPP_BRIDGE_LOGIN_SHARED_SECRET=${config.sops.placeholder.mautrixwhatsapp_shared}
    # '';

    services.postgresql.enable = true;
    services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" ''
      CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
      CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
        TEMPLATE template0
        LC_COLLATE = "C"
        LC_CTYPE = "C";
      CREATE ROLE "mautrix-telegram" WITH LOGIN PASSWORD 'telegram';
      CREATE DATABASE "mautrix-telegram" WITH OWNER "mautrix-telegram"
        TEMPLATE template0
        LC_COLLATE = "C"
        LC_CTYPE = "C";
      CREATE ROLE "mautrix-whatsapp" WITH LOGIN PASSWORD 'whatsapp';
      CREATE DATABASE "mautrix-whatsapp" WITH OWNER "mautrix-whatsapp"
        TEMPLATE template0
        LC_COLLATE = "C"
        LC_CTYPE = "C";
      CREATE ROLE "mautrix-signal" WITH LOGIN PASSWORD 'signal';
      CREATE DATABASE "mautrix-signal" WITH OWNER "mautrix-signal"
        TEMPLATE template0
        LC_COLLATE = "C"
        LC_CTYPE = "C";
    '';

    services.matrix-synapse = {
      settings.app_service_config_files = [
        "/var/lib/matrix-synapse/telegram-registration.yaml"
        "/var/lib/matrix-synapse/whatsapp-registration.yaml"
        "/var/lib/matrix-synapse/signal-registration.yaml"
        "/var/lib/matrix-synapse/doublepuppet.yaml"
      ];
      enable = true;
      settings.server_name = matrixDomain;
      settings.public_baseurl = "https://${matrixDomain}";
      extraConfigFiles = [
        config.sops.templates.matrixshared.path
      ];
      settings.listeners = [
        { port = 8008;
          bind_addresses = [ "0.0.0.0" ];
          type = "http";
          tls = false;
          x_forwarded = true;
          resources = [
            {
              names = [ "client" "federation" ];
              compress = true;
            }
          ];
        }
      ];
    };

    services.mautrix-telegram = {
      enable = true;
      environmentFile = config.sops.templates.mautrixtelegram.path;
      settings = {
        homeserver = {
          address = "http://localhost:8008";
          domain = matrixDomain;
        };
        appservice = {
          address= "http://localhost:29317";
          hostname = "0.0.0.0";
          port = "29317";
          provisioning.enabled = true;
          id = "telegram";
          # ephemeral_events = true; # not needed due to double puppeting
          public = {
            enabled = false;
          };
          database = "postgresql:///mautrix-telegram?host=/run/postgresql";
        };
        bridge = {
          # login_shared_secret_map = {
            # matrixDomain = "as_token:doublepuppet";
          # };
          relaybot.authless_portals = true;
          allow_avatar_remove = true;
          allow_contact_info = true;
          sync_channel_members = true;
          startup_sync = true;
          sync_create_limit = 0;
          sync_direct_chats = true;
          telegram_link_preview = true;
          permissions = {
            "*" = "relaybot";
            "@swarsel:${matrixDomain}" = "admin";
          };
          # Animated stickers conversion requires additional packages in the
          # service's path.
          # If this isn't a fresh installation, clearing the bridge's uploaded
          # file cache might be necessary (make a database backup first!):
          # delete from telegram_file where \
          #   mime_type in ('application/gzip', 'application/octet-stream')
          animated_sticker = {
            target = "gif";
            args = {
              width = 256;
              height = 256;
              fps = 30;               # only for webm
              background = "020202";  # only for gif, transparency not supported
            };
          };
        };
      };
    };
    systemd.services.mautrix-telegram.path = with pkgs; [
      lottieconverter  # for animated stickers conversion, unfree package
      ffmpeg           # if converting animated stickers to webm (very slow!)
    ];

    services.mautrix-whatsapp = {
      enable = true;
      # environmentFile = config.sops.templates.mautrixwhatsapp.path;
      settings = {
        homeserver = {
          address = "http://localhost:8008";
          domain = matrixDomain;
        };
        appservice = {
          address= "http://localhost:29318";
          hostname = "0.0.0.0";
          port = 29318;
          database = {
            type = "postgres";
            uri = "postgresql:///mautrix-whatsapp?host=/run/postgresql";
          };
        };
        bridge = {
          displayname_template = "{{or .FullName .PushName .JID}} (WA)";
          history_sync = {
            backfill = true;
            max_initial_conversations = -1;
            message_count = -1;
            request_full_sync = true;
            full_sync_config = {
              days_limit = 900;
              size_mb_limit = 5000;
              storage_quota_mb = 5000;
            };
          };
          login_shared_secret_map = {
            matrixDomain = "as_token:doublepuppet";
          };
          sync_manual_marked_unread = true;
          send_presence_on_typing = true;
          parallel_member_sync = true;
          url_previews = true;
          caption_in_message = true;
          extev_polls = true;
          permissions = {
            "*" = "relaybot";
            "@swarsel:${matrixDomain}" = "admin";
          };
        };
      };
    };

    services.mautrix-signal = {
      enable = true;
      registerToSynapse = false; # this has the same effect as registering to app_service_config_file above
      # environmentFile = config.sops.templates.mautrixwhatsapp.path;
      settings = {
        homeserver = {
          address = "http://localhost:8008";
          domain = matrixDomain;
        };
        appservice = {

          address= "http://localhost:29328";
          hostname = "0.0.0.0";
          port = 29328;
          database = {
            type = "postgres";
            uri = "postgresql:///mautrix-signal?host=/run/postgresql";
          };
        };
        bridge = {
          displayname_template = "{{or .ContactName .ProfileName .PhoneNumber}} (Signal)";
          login_shared_secret_map = {
            matrixDomain = "as_token:doublepuppet";
          };
          caption_in_message = true;
          permissions = {
            "*" = "relaybot";
            "@swarsel:${matrixDomain}" = "admin";
          };
        };
      };
    };

    # restart the bridges daily. this is done for the signal bridge mainly which stops carrying
    # messages out after a while.

    systemd.timers."restart-bridges" = {
      wantedBy = [ "timers.target" ];
      timerConfig = {
        OnBootSec = "1d";
        OnUnitActiveSec = "1d";
        Unit = "restart-bridges.service";
      };
    };

    systemd.services."restart-bridges" = {
      script = ''
      systemctl restart mautrix-whatsapp.service
      systemctl restart mautrix-signal.service
      systemctl restart mautrix-telegram.service
      '';
      serviceConfig = {
        Type = "oneshot";
        User = "root";
      };
    };

  }

Paperless

NixOS
  { config, pkgs, modulesPath, ... }:

  {
    <<vminit>>

    users.groups.lxc_shares = {
      gid = 10000;
      members = [
        "paperless"
        "root"
      ];
    };

    environment.systemPackages = with pkgs; [
      git
      gnupg
      ssh-to-age
    ];

    networking.hostName = "paperless"; # Define your hostname.
    networking.firewall.enable = false;

    sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
    sops.defaultSopsFile = "/root/.dotfiles/secrets/paperless/secrets.yaml";
    sops.validateSopsFiles = false;
    sops.secrets.admin = { owner = "paperless";};

    services.paperless = {
      enable = true;
      mediaDir = "/media";
      user = "paperless";
      port = 28981;
      passwordFile = config.sops.secrets.admin.path;
      address = "0.0.0.0";
      extraConfig = {
        PAPERLESS_OCR_LANGUAGE = "deu+eng";
        PAPERLESS_URL = "scan.swarsel.win";
        PAPERLESS_OCR_USER_ARGS = builtins.toJSON {
          optimize = 1;
          pdfa_image_compression = "lossless";
        };
      };
    };

  }

Sandbox (Lenovo Y510P)

My laptop, sadly soon to be replaced by a new one, since most basic functions are stopping to work lately.

NixOS
      { config, pkgs, modulesPath, unstable, sops, ... }: let
      matrixDomain = "swatrix.swarsel.win";
    in {

      imports = [
        ./hardware-configuration.nix
        # we import here a service that is not available yet on normal nixpkgs
        # this module is hence not in the modules list, we add it ourselves
        (unstable + "/nixos/modules/services/matrix/mautrix-signal.nix")
      ];

        boot.loader.grub = {
          enable = true;
          device = "/dev/sda";
          useOSProber = true;
        };

        users.users.swarsel = {
          isNormalUser = true;
          description = "Leon S";
          extraGroups = [ "networkmanager" "wheel" "lp"];
          packages = with pkgs; [];
        };

    # actual config starts here

      fileSystems."/mnt/Eternor" = {
        device = "//192.168.1.3/Eternor";
        fsType = "cifs";
        options = let
          # this line prevents hanging on network split
          automount_opts = "x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s";
        in ["${automount_opts},credentials=/etc/nixos/smb-secrets,uid=1000,gid=100"];
      };

        environment.systemPackages = with pkgs; [
        git
        gnupg
        ssh-to-age
        lego
        nginx
        calibre
        openvpn
        jq
        iptables
        busybox
        wireguard-tools
        matrix-synapse
        lottieconverter
        ffmpeg
        pciutils
        alsa-utils
        mpv
        zfs
        ];

        services.xserver = {
          layout = "us";
          xkbVariant = "altgr-intl";
        };

        nix.settings.experimental-features = ["nix-command" "flakes"];

        services.openssh = {
          enable = true;
          settings.PermitRootLogin = "yes";
          listenAddresses = [{
            port = 22;
            addr = "0.0.0.0";
          }];
        };
        users.users.root.openssh.authorizedKeys.keyFiles = [
          ../../secrets/keys/authorized_keys
        ];

        system.stateVersion = "23.05"; # TEMPLATE - but probably no need to change

        environment.shellAliases = {
          nswitch = "cd ~/.dotfiles; git pull; nixos-rebuild --flake .#$(hostname) switch; cd -;";
        };

  boot.supportedFilesystems = [ "zfs" ];
  boot.zfs.forceImportRoot = false;
  networking.hostId = "8a8ad84a";

        networking.hostName = "sandbox"; # Define your hostname.
        networking.enableIPv6 = true;
        networking.firewall.enable = false;

        documentation = {
          enable = false;
        };

      sops.age.sshKeyPaths = [ "/etc/ssh/sops" ];
      sops.defaultSopsFile = "/root/.dotfiles/secrets/sandbox/secrets.yaml";
      sops.validateSopsFiles = false;
      sops.secrets.dnstokenfull = {owner="acme";};
      sops.templates."certs.secret".content = ''
      CF_DNS_API_TOKEN=${config.sops.placeholder.dnstokenfull}
      '';

      security.acme = {
        acceptTerms = true;
        preliminarySelfsigned = false;
        defaults.email = "mrswarsel@gmail.com";
        defaults.dnsProvider = "cloudflare";
        defaults.environmentFile = "${config.sops.templates."certs.secret".path}";
      };

      services.nginx = {
        enable = true;
        recommendedProxySettings = true;
        recommendedTlsSettings = true;
        recommendedOptimisation = true;
        recommendedGzipSettings = true;
        virtualHosts = {

          "stash.swarsel.win" = {
            enableACME = true;
            forceSSL = true;
            acmeRoot = null;
            locations = {
              "/" = {
                proxyPass = "https://192.168.1.5";
                extraConfig = ''
                client_max_body_size 0;
                '';
              };
              # "/push/" = {
                # proxyPass = "http://192.168.2.5:7867";
              # };
              "/.well-known/carddav" = {
                return = "301 $scheme://$host/remote.php/dav";
              };
              "/.well-known/caldav" = {
                return = "301 $scheme://$host/remote.php/dav";
              };
            };
          };

          "swatrix.swarsel.win" = {
            enableACME = true;
            forceSSL = true;
            acmeRoot = null;
            locations = {
              "~ ^(/_matrix|/_synapse/client)" = {
                proxyPass = "http://127.0.0.1:8008";
                extraConfig = ''
                    client_max_body_size 0;
                  '';
              };
            };
          };


            "sound.swarsel.win" = {
              enableACME = true;
              forceSSL = true;
              acmeRoot = null;
              locations = {
                "/" = {
                  proxyPass = "http://127.0.0.1:4040";
                  proxyWebsockets = true;
                  extraConfig = ''
                    proxy_redirect          http:// https://;
                    proxy_read_timeout      600s;
                    proxy_send_timeout      600s;
                    proxy_buffering         off;
                    proxy_request_buffering off;
                    client_max_body_size    0;
                  '';
                };
              };
            };

            "scan.swarsel.win" = {
              enableACME = true;
              forceSSL = true;
              acmeRoot = null;
              locations = {
                "/" = {
                  proxyPass = "http://127.0.0.1:28981";
                  extraConfig = ''
                    client_max_body_size 0;
                  '';
                };
              };
            };

            "screen.swarsel.win" = {
              enableACME = true;
              forceSSL = true;
              acmeRoot = null;
              locations = {
                "/" = {
                  proxyPass = "http://127.0.0.1:8096";
                  extraConfig = ''
                    client_max_body_size 0;
                  '';
                };
              };
            };

            "scroll.swarsel.win" = {
              enableACME = true;
              forceSSL = true;
              acmeRoot = null;
              locations = {
                "/" = {
                  proxyPass = "http://127.0.0.1:8080";
                  extraConfig = ''
                    client_max_body_size 0;
                  '';
                };
              };
            };


          };
        };


      sops.secrets.kavita = { owner = "kavita";};

      services.kavita = {
        enable = true;
        user = "kavita";
        port = 8080;
        tokenKeyFile = config.sops.secrets.kavita.path;
      };

      users.users.jellyfin = {
        extraGroups  = [ "video" "render" ];
      };

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

      services.jellyfin = {
        enable = true;
        user = "jellyfin";
        # openFirewall = true; # this works only for the default ports
      };

                  users.groups.vpn = {};

                  users.users.vpn = {
                    isNormalUser = true;
                    group = "vpn";
                    home = "/home/vpn";
                  };

                  boot.kernelModules = [ "tun" ];

                  services.radarr = {
                    enable = true;
                  };

                  services.readarr = {
                    enable = true;
                  };
                  services.sonarr = {
                    enable = true;
                  };
                  services.lidarr = {
                    enable = true;
                  };
                  services.prowlarr = {
                    enable = true;
                  };

                  networking.firewall.extraCommands = ''
                  sudo iptables -A OUTPUT ! -o lo -m owner --uid-owner vpn -j DROP
                  '';
                  networking.iproute2 = {
                    enable = true;
                    rttablesExtraConfig = ''
                    200     vpn
                    '';
                  };
                  boot.kernel.sysctl = {
                    "net.ipv4.conf.all.rp_filter" = 2;
                    "net.ipv4.conf.default.rp_filter" = 2;
                    "net.ipv4.conf.enp7s0.rp_filter" = 2;
                  };
                  environment.etc = {
                    "openvpn/iptables.sh" =
                      { source = ../../scripts/server1/iptables.sh;
                        mode = "0755";
                      };
                    "openvpn/update-resolv-conf" =
                      { source = ../../scripts/server1/update-resolv-conf;
                        mode = "0755";
                      };
                    "openvpn/routing.sh" =
                      { source = ../../scripts/server1/routing.sh;
                        mode = "0755";
                      };
                    "openvpn/ca.rsa.2048.crt" =
                      { source = ../../secrets/certs/ca.rsa.2048.crt;
                        mode = "0644";
                      };
                    "openvpn/crl.rsa.2048.pem" =
                      { source = ../../secrets/certs/crl.rsa.2048.pem;
                        mode = "0644";
                      };
                  };

                  sops.secrets.vpnuser = {};
                  sops.secrets.rpcuser = {owner="vpn";};
                  sops.secrets.vpnpass = {};
                  sops.secrets.rpcpass = {owner="vpn";};
                  sops.secrets.vpnprot = {};
                  sops.secrets.vpnloc = {};
                  # sops.secrets.crlpem = {};
                  # sops.secrets.capem = {};
                  sops.templates."transmission-rpc".owner = "vpn";
                  sops.templates."transmission-rpc".content = builtins.toJSON {
                    rpc-username = config.sops.placeholder.rpcuser;
                    rpc-password = config.sops.placeholder.rpcpass;
                  };

                  sops.templates.pia.content = ''
                  ${config.sops.placeholder.vpnuser}
                  ${config.sops.placeholder.vpnpass}
                  '';

                  sops.templates.vpn.content = ''
                    client
                    dev tun
                    proto ${config.sops.placeholder.vpnprot}
                    remote ${config.sops.placeholder.vpnloc}
                    resolv-retry infinite
                    nobind
                    persist-key
                    persist-tun
                    cipher aes-128-cbc
                    auth sha1
                    tls-client
                    remote-cert-tls server

                    auth-user-pass ${config.sops.templates.pia.path}
                    compress
                    verb 1
                    reneg-sec 0

                    crl-verify /etc/openvpn/crl.rsa.2048.pem
                    ca /etc/openvpn/ca.rsa.2048.crt

                    disable-occ
                  '';

              services.openvpn.servers = {
                pia = {
                  autoStart = true;
                  updateResolvConf = false;
                  config = "config ${config.sops.templates.vpn.path}";
                };
              };

            services.transmission = {
              enable = true;
              credentialsFile = config.sops.templates."transmission-rpc".path;
              user = "vpn";
              settings = {

              alt-speed-down= 8000;
              alt-speed-enabled= false;
              alt-speed-time-begin= 0;
              alt-speed-time-day= 127;
              alt-speed-time-enabled= true;
              alt-speed-time-end= 360;
              alt-speed-up= 2000;
              bind-address-ipv4= "0.0.0.0";
              bind-address-ipv6= "::";
              blocklist-enabled= false;
              blocklist-url= "http://www.example.com/blocklist";
              cache-size-mb= 256;
              dht-enabled= false;
              download-dir= "/test";
              download-limit= 100;
              download-limit-enabled= 0;
              download-queue-enabled= true;
              download-queue-size= 5;
              encryption= 2;
              idle-seeding-limit= 30;
              idle-seeding-limit-enabled= false;
              incomplete-dir= "/var/lib/transmission-daemon/Downloads";
              incomplete-dir-enabled= false;
              lpd-enabled= false;
              max-peers-global= 200;
              message-level= 1;
              peer-congestion-algorithm= "";
              peer-id-ttl-hours= 6;
              peer-limit-global= 100;
              peer-limit-per-torrent= 40;
              peer-port= 22371;
              peer-port-random-high= 65535;
              peer-port-random-low= 49152;
              peer-port-random-on-start= false;
              peer-socket-tos= "default";
              pex-enabled= false;
              port-forwarding-enabled= false;
              preallocation= 1;
              prefetch-enabled= true;
              queue-stalled-enabled= true;
              queue-stalled-minutes= 30;
              ratio-limit= 2;
              ratio-limit-enabled= false;
              rename-partial-files= true;
              rpc-authentication-required= true;
              rpc-bind-address= "0.0.0.0";
              rpc-enabled= true;
              rpc-host-whitelist= "";
              rpc-host-whitelist-enabled= true;
              rpc-port= 9091;
              rpc-url= "/transmission/";
              rpc-whitelist= "127.0.0.1,192.168.3.2";
              rpc-whitelist-enabled= true;
              scrape-paused-torrents-enabled= true;
              script-torrent-done-enabled= false;
              seed-queue-enabled= false;
              seed-queue-size= 10;
              speed-limit-down= 6000;
              speed-limit-down-enabled= true;
              speed-limit-up= 500;
              speed-limit-up-enabled= true;
              start-added-torrents= true;
              trash-original-torrent-files= false;
              umask= 2;
              upload-limit= 100;
              upload-limit-enabled= 0;
              upload-slots-per-torrent= 14;
              utp-enabled= false;
              };
            };

          # services.nginx = {
          #       enable = true;
          #       virtualHosts = {

          #         "192.168.1.192" = {
          #           locations = {
          #             "/transmission" = {
          #               proxyPass = "http://127.0.0.1:9091";
          #               extraConfig = ''
          #               proxy_set_header Host $host;
          #               proxy_set_header X-Real-IP $remote_addr;
          #               proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          #               '';
          #             };
          #           };
          #         };
          #       };
          # };


      # sops.secrets.matrixsharedsecret = {owner="matrix-synapse";};
      # sops.templates."matrix_user_register.sh".content = ''
      # register_new_matrix_user -k ${config.sops.placeholder.matrixsharedsecret} http://localhost:8008
      # '';
      # sops.templates.matrixshared.owner = "matrix-synapse";
      # sops.templates.matrixshared.content = ''
      # registration_shared_secret: ${config.sops.placeholder.matrixsharedsecret}
      # '';
      # sops.secrets.mautrixtelegram_as = {owner="matrix-synapse";};
      # sops.secrets.mautrixtelegram_hs = {owner="matrix-synapse";};
      # sops.secrets.mautrixtelegram_api_id = {owner="matrix-synapse";};
      # sops.secrets.mautrixtelegram_api_hash = {owner="matrix-synapse";};
      # sops.templates.mautrixtelegram.owner = "matrix-synapse";
      # sops.templates.mautrixtelegram.content = ''
      # MAUTRIX_TELEGRAM_APPSERVICE_AS_TOKEN=${config.sops.placeholder.mautrixtelegram_as}
      # MAUTRIX_TELEGRAM_APPSERVICE_HS_TOKEN=${config.sops.placeholder.mautrixtelegram_hs}
      # MAUTRIX_TELEGRAM_TELEGRAM_API_ID=${config.sops.placeholder.mautrixtelegram_api_id}
      # MAUTRIX_TELEGRAM_TELEGRAM_API_HASH=${config.sops.placeholder.mautrixtelegram_api_hash}
      # '';




      # ----------------
      # sops.secrets.mautrixwhatsapp_shared = {owner="matrix-synapse";};
      # sops.templates.mautrixwhatsapp.owner = "matrix-synapse";
      # sops.templates.mautrixwhatsapp.content = ''
      # MAUTRIX_WHATSAPP_BRIDGE_LOGIN_SHARED_SECRET=${config.sops.placeholder.mautrixwhatsapp_shared}
      # '';

      services.postgresql.enable = true;
      services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" ''
        CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
        CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
          TEMPLATE template0
          LC_COLLATE = "C"
          LC_CTYPE = "C";
        CREATE ROLE "mautrix-telegram" WITH LOGIN PASSWORD 'telegram';
        CREATE DATABASE "mautrix-telegram" WITH OWNER "mautrix-telegram"
          TEMPLATE template0
          LC_COLLATE = "C"
          LC_CTYPE = "C";
        CREATE ROLE "mautrix-whatsapp" WITH LOGIN PASSWORD 'whatsapp';
        CREATE DATABASE "mautrix-whatsapp" WITH OWNER "mautrix-whatsapp"
          TEMPLATE template0
          LC_COLLATE = "C"
          LC_CTYPE = "C";
        CREATE ROLE "mautrix-signal" WITH LOGIN PASSWORD 'signal';
        CREATE DATABASE "mautrix-signal" WITH OWNER "mautrix-signal"
          TEMPLATE template0
          LC_COLLATE = "C"
          LC_CTYPE = "C";
      '';

      services.matrix-synapse = {
        settings.app_service_config_files = [
          "/var/lib/matrix-synapse/telegram-registration.yaml"
          "/var/lib/matrix-synapse/whatsapp-registration.yaml"
          "/var/lib/matrix-synapse/signal-registration.yaml"
          "/var/lib/matrix-synapse/doublepuppet.yaml"
        ];
        enable = false;
        settings.server_name = matrixDomain;
        settings.public_baseurl = "https://${matrixDomain}";
        extraConfigFiles = [
          config.sops.templates.matrixshared.path
        ];
        settings.listeners = [
          { port = 8008;
            bind_addresses = [ "0.0.0.0" ];
            type = "http";
            tls = false;
            x_forwarded = true;
            resources = [
              {
                names = [ "client" "federation" ];
                compress = true;
              }
            ];
          }
        ];
      };

      services.mautrix-telegram = {
        enable = false;
        environmentFile = config.sops.templates.mautrixtelegram.path;
        settings = {
          homeserver = {
            address = "http://localhost:8008";
            domain = matrixDomain;
          };
          appservice = {
            address= "http://localhost:29317";
            hostname = "0.0.0.0";
            port = "29317";
            provisioning.enabled = true;
            id = "telegram";
            # ephemeral_events = true; # not needed due to double puppeting
            public = {
              enabled = false;
            };
            database = "postgresql:///mautrix-telegram?host=/run/postgresql";
          };
          bridge = {
            # login_shared_secret_map = {
              # matrixDomain = "as_token:doublepuppet";
            # };
            relaybot.authless_portals = true;
            allow_avatar_remove = true;
            allow_contact_info = true;
            sync_channel_members = true;
            startup_sync = true;
            sync_create_limit = 0;
            sync_direct_chats = true;
            telegram_link_preview = true;
            permissions = {
              "*" = "relaybot";
              "@swarsel:${matrixDomain}" = "admin";
            };
            # Animated stickers conversion requires additional packages in the
            # service's path.
            # If this isn't a fresh installation, clearing the bridge's uploaded
            # file cache might be necessary (make a database backup first!):
            # delete from telegram_file where \
            #   mime_type in ('application/gzip', 'application/octet-stream')
            animated_sticker = {
              target = "gif";
              args = {
                width = 256;
                height = 256;
                fps = 30;               # only for webm
                background = "020202";  # only for gif, transparency not supported
              };
            };
          };
        };
      };
      # systemd.services.mautrix-telegram.path = with pkgs; [
        # lottieconverter  # for animated stickers conversion, unfree package
        # ffmpeg           # if converting animated stickers to webm (very slow!)
      # ];

      services.mautrix-whatsapp = {
        enable = false;
        # environmentFile = config.sops.templates.mautrixwhatsapp.path;
        settings = {
          homeserver = {
            address = "http://localhost:8008";
            domain = matrixDomain;
          };
          appservice = {
            address= "http://localhost:29318";
            hostname = "0.0.0.0";
            port = 29318;
            database = {
              type = "postgres";
              uri = "postgresql:///mautrix-whatsapp?host=/run/postgresql";
            };
          };
          bridge = {
            displayname_template = "{{or .FullName .PushName .JID}} (WA)";
            history_sync = {
              backfill = true;
              max_initial_conversations = -1;
              message_count = -1;
              request_full_sync = true;
              full_sync_config = {
                days_limit = 900;
                size_mb_limit = 5000;
                storage_quota_mb = 5000;
              };
            };
            login_shared_secret_map = {
              matrixDomain = "as_token:doublepuppet";
            };
            sync_manual_marked_unread = true;
            send_presence_on_typing = true;
            parallel_member_sync = true;
            url_previews = true;
            caption_in_message = true;
            extev_polls = true;
            permissions = {
              "*" = "relaybot";
              "@swarsel:${matrixDomain}" = "admin";
            };
          };
        };
      };

      services.mautrix-signal = {
        enable = false;
        # environmentFile = config.sops.templates.mautrixwhatsapp.path;
        settings = {
          homeserver = {
            address = "http://localhost:8008";
            domain = matrixDomain;
          };
          appservice = {

            address= "http://localhost:29328";
            hostname = "0.0.0.0";
            port = 29328;
            database = {
              type = "postgres";
              uri = "postgresql:///mautrix-signal?host=/run/postgresql";
            };
          };
          bridge = {
            displayname_template = "{{or .ContactName .ProfileName .PhoneNumber}} (Signal)";
            login_shared_secret_map = {
              matrixDomain = "as_token:doublepuppet";
            };
            caption_in_message = true;
            permissions = {
              "*" = "relaybot";
              "@swarsel:${matrixDomain}" = "admin";
            };
          };
        };
      };

      # restart the bridges daily. this is done for the signal bridge mainly which stops carrying
      # messages out after a while.

      systemd.timers."restart-bridges" = {
        wantedBy = [ "timers.target" ];
        timerConfig = {
          OnBootSec = "1d";
          OnUnitActiveSec = "1d";
          Unit = "restart-bridges.service";
        };
      };

      systemd.services."restart-bridges" = {
        script = ''
        systemctl restart mautrix-whatsapp.service
        systemctl restart mautrix-signal.service
        systemctl restart mautrix-telegram.service
        '';
        serviceConfig = {
          Type = "oneshot";
          User = "root";
        };
      };


      users.groups.navidrome = {
        gid = 61593;
      };

      users.groups.mpd = {};

      users.users.navidrome = {
        isSystemUser = true;
        uid = 61593;
        group = "navidrome";
        extraGroups  = [ "audio" "utmp" ];
      };

      users.users.mpd = {
        isSystemUser = true;
        group = "mpd";
        extraGroups  = [ "audio" "utmp" ];
      };

      sound = {
        enable = true;
      };

      hardware.enableAllFirmware = true;

      sops.secrets.mpdpass = { owner = "mpd";};

      services.navidrome = {
        enable = true;
        settings = {
          Address = "0.0.0.0";
          Port = 4040;
          MusicFolder = "/mnt/";
          EnableSharing = true;
          EnableTranscodingConfig = true;
          Scanner.GroupAlbumReleases = true;
          ScanSchedule = "@every 24h";
          # Insert these values locally as sops-nix does not work for them
          # LastFM.ApiKey = TEMPLATE;
          # LastFM.Secret = TEMPLATE;
          # Spotify.ID = TEMPLATE;
          # Spotify.Secret = TEMPLATE;
          UILoginBackgroundUrl = "https://i.imgur.com/OMLxi7l.png";
          UIWelcomeMessage = "~SwarselSound~";
        };
      };
      services.mpd = {
        enable = true;
        musicDirectory = "/mnt/Eternor/Musik";
        user = "mpd";
        group = "mpd";
        network = {
          port = 3254;
          listenAddress = "any";
        };
        credentials = [
          {
            passwordFile = config.sops.secrets.mpdpass.path;
            permissions = [
              "read"
              "add"
              "control"
              "admin"
            ];
          }
        ];
      };


      users.groups.spotifyd = {
        gid = 65136;
      };

      users.users.spotifyd = {
        isSystemUser = true;
        uid = 65136;
        group = "spotifyd";
        extraGroups  = [ "audio" "utmp" ];
      };

      services.spotifyd = {
        enable = true;
        settings = {
          global = {
            dbus_type = "session";
            use_mpris = false;
            device = "default:CARD=PCH";
            device_name = "SwarselSpot";
            mixer = "alsa";
            zeroconf_port = 1025;
          };
        };
      };

        # Network shares
        # add a user with sudo smbpasswd -a <user>
        services.samba = {
          package = pkgs.samba4Full;
          extraConfig = ''
          workgroup = WORKGROUP
          server role = standalone server
          dns proxy = no

          pam password change = yes
          map to guest = bad user
          create mask = 0664
          force create mode = 0664
          directory mask = 0775
          force directory mode = 0775
          follow symlinks = yes
          '';

          # ^^ `samba4Full` is compiled with avahi, ldap, AD etc support compared to the default package, `samba`
          # Required for samba to register mDNS records for auto discovery
          # See https://github.com/NixOS/nixpkgs/blob/592047fc9e4f7b74a4dc85d1b9f5243dfe4899e3/pkgs/top-level/all-packages.nix#L27268
          enable = true;
          # openFirewall = true;
          shares.test = {
            browseable = "yes";
            "read only" = "no";
            "guest ok" = "no";
            path = "/test2";
            writable = "true";
            comment = "Eternor";
            "valid users" = "@smbtest2";
          };
        };


        services.avahi = {
          publish.enable = true;
          publish.userServices = true;
          # ^^ Needed to allow samba to automatically register mDNS records without the need for an `extraServiceFile`
          nssmdns = true;
          # ^^ Not one hundred percent sure if this is needed- if it aint broke, don't fix it
    enable = true;
        };

        services.samba-wsdd = {
        # This enables autodiscovery on windows since SMB1 (and thus netbios) support was discontinued
          enable = true;
        };










      }

Common NixOS

These are system-level settings specific to NixOS machines. All settings that are required on all machines go here.

General

Section for all settings that are not really deserving of their own section.

  { config, lib, pkgs, inputs, ... }:

  {

    home-manager.useGlobalPkgs = true;
    home-manager.useUserPackages = true;

    # login keymap
    services.xserver = {
      xkb.layout = "us";
      xkb.variant = "altgr-intl";
    };

    # mount NAS drive
    # works only at home, but w/e
    # fileSystems."/mnt/smb" = {
    #   device = "//192.168.1.3/Eternor";
    #   fsType = "cifs";
    #   options = let
    #     # this line prevents hanging on network split
    #     automount_opts = "x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s";
    #   in ["${automount_opts},credentials=/etc/nixos/smb-secrets,uid=1000,gid=100"];
    # };

    # # enable flakes - urgent line!!
    nix.settings.experimental-features = ["nix-command" "flakes"];
    environment.sessionVariables.NIXOS_OZONE_WL = "1";

    # wordlist for look
    environment.wordlist.enable = true;
    # gstreamer plugins for nautilus (used for file metadata)
    environment.sessionVariables.GST_PLUGIN_SYSTEM_PATH_1_0 = lib.makeSearchPathOutput "lib" "lib/gstreamer-1.0" (with pkgs.gst_all_1; [
      gst-plugins-good
      gst-plugins-bad
      gst-plugins-ugly
      gst-libav
    ]);

    # correct time between linux and windows
    time.hardwareClockInLocalTime = true;

    # dont style GRUB with stylix
    stylix.targets.grub.enable = false; # the styling makes grub more ugly

    # cura fix
    # xdg.portal = {
    #   enable = true;
    #   extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
    #   wlr.enable = true;
    #   config = {
    #     common = {
    #       default = [
    #         "*"
    #       ];
    #     };
    #   };
    # };
    # wayland-related
    security.polkit.enable = true;
    hardware.opengl = {
      enable = true;
      driSupport = true;
      driSupport32Bit = true;
    };

    # systemd
    systemd.extraConfig = ''
    DefaultTimeoutStartSec=60s
    DefaultTimeoutStopSec=15s
  '';

    # audio
    sound.enable = true;
    # nixpkgs.config.pulseaudio = true;
    hardware.pulseaudio= {
      enable = true;
      package = pkgs.pulseaudioFull;
    };
    hardware.enableAllFirmware = true;
    hardware.bluetooth.powerOnBoot = true;
    hardware.bluetooth.settings = {
      General = {
        Enable = "Source,Sink,Media,Socket";
      };
    };
    # networking.wireless.enable = true;  # Enables wireless support via wpa_supplicant.
    networking.networkmanager.enable = true;

    time.timeZone = "Europe/Vienna";

    i18n.defaultLocale = "en_US.UTF-8";
    i18n.extraLocaleSettings = {
      LC_ADDRESS = "de_AT.UTF-8";
      LC_IDENTIFICATION = "de_AT.UTF-8";
      LC_MEASUREMENT = "de_AT.UTF-8";
      LC_MONETARY = "de_AT.UTF-8";
      LC_NAME = "de_AT.UTF-8";
      LC_NUMERIC = "de_AT.UTF-8";
      LC_PAPER = "de_AT.UTF-8";
      LC_TELEPHONE = "de_AT.UTF-8";
      LC_TIME = "de_AT.UTF-8";
    };

System Packages

Mostly used to install some compilers and lps's that I want to have available when not using a devShell flake.

    environment.systemPackages = with pkgs; [
      # yubikey packages
      gnupg
      yubikey-personalization
      yubikey-personalization-gui
      yubico-pam
      # yubioath-flutter
      # yubikey-manager
      # yubikey-manager-qt
      yubico-piv-tool
      # pinentry

      # theme related
      gnome.adwaita-icon-theme

      # kde-connect
      xdg-desktop-portal

      # bluetooth
      bluez

      # lsp-related -------------------------------
      # nix
      # latex
      texlab
      ghostscript_headless
      # wireguard
      wireguard-tools
      # rust
      rust-analyzer
      clippy
      rustfmt
      # cpp
      clang-tools
      # + cuda
      cudatoolkit
      #lsp-bridge / python
      gcc
      gdb
      (python3.withPackages(ps: with ps; [ jupyter ipython pyqt5 epc orjson sexpdata six setuptools paramiko numpy pandas scipy matplotlib requests debugpy flake8 gnureadline python-lsp-server]))
      # (python3.withPackages(ps: with ps; [ jupyter ipython pyqt5 numpy pandas scipy matplotlib requests debugpy flake8 gnureadline python-lsp-server]))
      # --------------------------------------------

  (stdenv.mkDerivation {
      name = "oama";

      src = pkgs.fetchurl {
          name = "oama";
          url = "https://github.com/pdobsan/oama/releases/download/0.13.1/oama-0.13.1-Linux-x86_64-static.tgz";
          sha256 = "sha256-OTdCObVfnMPhgZxVtZqehgUXtKT1iyqozdkPIV+i3Gc=";
      };

      phases = [
        "unpackPhase"
      ];

      unpackPhase = ''
      mkdir -p $out/bin
      tar xvf $src -C $out/
      mv $out/oama-0.13.1-Linux-x86_64-static/oama $out/bin/
      '';

  })




    ];

Programs (including zsh setup)

Some programs profit from being installed through dedicated NixOS settings on system-level; these go here. Notably the zsh setup goes here and cannot be deleted under any circumstances.

  programs.dconf.enable = true;
  programs.evince.enable = true;
  programs.kdeconnect.enable = true;


  # zsh section, do not delete ------
  programs.zsh.enable = true;
  users.defaultUserShell = pkgs.zsh;
  environment.shells = with pkgs; [ zsh ];
  environment.pathsToLink = [ "/share/zsh" ];
  # ---------------------------------

Services

Setting up some hardware services as well as keyboard related settings. Here we make sure that we can use the CAPS key as a ESC/CTRL double key, which is a lifesaver.

    services.blueman.enable = true;

    # enable scanners over network
    hardware.sane = {
      enable = true;
      extraBackends = [ pkgs.sane-airscan ];
    };

    # enable discovery and usage of network devices (esp. printers)
    services.printing.enable = true;
    services.printing.drivers = [
      pkgs.gutenprint
      pkgs.gutenprintBin
    ];
    services.printing.browsedConf = ''
  BrowseDNSSDSubTypes _cups,_print
  BrowseLocalProtocols all
  BrowseRemoteProtocols all
  CreateIPPPrinterQueues All

  BrowseProtocols all
      '';
    services.avahi = {
      enable = true;
      nssmdns4 = true;
      openFirewall = true;
    };

    # nautilus file manager
    services.gvfs.enable = true;

    # Make CAPS work as a dual function ESC/CTRL key
    services.interception-tools = {
      enable = true;
      udevmonConfig = let
        dualFunctionKeysConfig = builtins.toFile "dual-function-keys.yaml" ''
          TIMING:
            TAP_MILLISEC: 200
            DOUBLE_TAP_MILLISEC: 0

          MAPPINGS:
            - KEY: KEY_CAPSLOCK
              TAP: KEY_ESC
              HOLD: KEY_LEFTCTRL
        '';
      in ''
        - JOB: |
            ${pkgs.interception-tools}/bin/intercept -g $DEVNODE \
              | ${pkgs.interception-tools-plugins.dual-function-keys}/bin/dual-function-keys -c ${dualFunctionKeysConfig} \
              | ${pkgs.interception-tools}/bin/uinput -d $DEVNODE
          DEVICE:
            EVENTS:
              EV_KEY: [KEY_CAPSLOCK]
      '';
    };

Yubikey settings

It makes sense to house these settings in their own section, since they are all needed really. Note that the starting of the gpg-agent is done in the sway settings, to also perform this step of the setup for non NixOS-machines at the same time.

The exception is the system packages, since that cannot be defined twice in the same file (common.nix). The comment is left in as a remider for that.

  programs.ssh.startAgent = false;

  services.pcscd.enable = true;

  # environment.systemPackages = with pkgs; [
  # --- IN SYSTEM PACKAGES SECTION ---
  # ];

  services.udev.packages = with pkgs; [
    yubikey-personalization
  ];

System Login

This section houses the greetd related settings. I do not really want to use a display manager, but it is useful to have setup in some ways - in my case for starting sway on system startup. Notably the default user login setting that is commented out here goes into the system specific settings, make sure to update it there

  services.greetd = {
    enable = true;
    settings = {
      initial_session.command = "sway";
      # initial_session.user ="swarsel";
      default_session.command = ''
                  ${pkgs.greetd.tuigreet}/bin/tuigreet \
                    --time \
                    --asterisks \
                    --user-menu \
                    --cmd sway
                '';
    };
  };

  environment.etc."greetd/environments".text = ''
                sway
              '';

Common Home-Manager

Installed packages

Here are defined some packages that I would like to use across all my machines. Most of these should not require further setup. Notably the cura package is severely outdated on nixpkgs, so I just fetch a more recent AppImage and run that instead.

Also, I define some useful shell scripts here.

  { config, pkgs, lib, fetchFromGitHub , ... }:

  {
    home.packages = with pkgs; [

      # audio stuff
      spek # spectrum analyzer
      losslessaudiochecker
      ffmpeg_5-full
      flac
      mediainfo
      picard
      audacity
      sox

      # printing
      cups
      gnome.simple-scan

      # dict
      (aspellWithDicts (dicts: with dicts; [ de en en-computers en-science ]))

      # utilities
      util-linux
      nmap
      lsof

      # local file sharing
      wormhole-rs

      # b2 backup @backblaze
      restic

      # "big" programs
      gimp
      inkscape
      zoom-us
      # nomacs
      libreoffice-qt
      xournalpp
      obsidian
      spotify
      discord
      nextcloud-client
      spotify-player
      element-desktop-wayland
      nicotine-plus
      transmission
      mktorrent
      hexchat
      hugo

      # kyria
      qmk
      qmk-udev-rules

      # games
      lutris
      wine
      libudev-zero
      dwarfs
      fuse-overlayfs
      # steam
      # steam-run
      patchelf
      gamescope
      vulkan-tools
      moonlight-qt

      # firefox related
      tridactyl-native

      # mako related
      mako
      libnotify

      # general utilities
      unrar
      samba
      cifs-utils
      zbar # qr codes
      readline
      autotiling
      brightnessctl
      libappindicator-gtk3
      sqlite
      speechd
      networkmanagerapplet
      psmisc # kill etc
      lm_sensors
      # jq # used for searching the i3 tree in check<xxx>.sh files

      # specifically needed for anki
      # mpv
      anki-bin

      # dirvish file previews
      fd
      imagemagick
      poppler
      ffmpegthumbnailer
      mediainfo
      gnutar
      unzip

      #nautilus
      gnome.nautilus
      xfce.tumbler
      libgsf

      # wayland stuff
      wtype
      wl-clipboard
      wl-mirror

      # screenshotting tools
      grim
      slurp

      # the following packages are used (in some way) by waybar
      playerctl
      pavucontrol
      pamixer
      # gnome.gnome-clocks
      # wlogout
      # jdiskreport
      syncthingtray
      # monitor

      #keychain
      qalculate-gtk
      gcr # needed for gnome-secrets to work
      gnome.seahorse

      # sops-related
      sops
      ssh-to-age

      # mail related packages
      mu

      # latex and related packages
      (pkgs.texlive.combine {
        inherit (pkgs.texlive) scheme-full
          dvisvgm dvipng # for preview and export as html
          wrapfig amsmath ulem hyperref capt-of;
      })

      # font stuff
      (nerdfonts.override { fonts = [ "FiraMono" "FiraCode" "NerdFontsSymbolsOnly"]; })
      noto-fonts-emoji
      font-awesome_5
      noto-fonts
      noto-fonts-cjk-sans

      # cura
      (let cura5 = appimageTools.wrapType2 rec {
             name = "cura5";
             version = "5.4.0";
             src = fetchurl {
               url = "https://github.com/Ultimaker/Cura/releases/download/${version}/UltiMaker-Cura-${version}-linux-modern.AppImage";
               hash = "sha256-QVv7Wkfo082PH6n6rpsB79st2xK2+Np9ivBg/PYZd74=";
             };
             extraPkgs = pkgs: with pkgs; [ ];
           }; in writeScriptBin "cura" ''
            #! ${pkgs.bash}/bin/bash
            # AppImage version of Cura loses current working directory and treats all paths relateive to $HOME.
            # So we convert each of the files passed as argument to an absolute path.
            # This fixes use cases like `cd /path/to/my/files; cura mymodel.stl anothermodel.stl`.
            args=()
            for a in "$@"; do
                if [ -e "$a" ]; then
                   a="$(realpath "$a")"
                fi
                args+=("$a")
            done
            exec "${cura5}/bin/cura5" "''${args[@]}"
            '')

      #E: hides scratchpad depending on state, calls emacsclient for edit and then restores the scratchpad state
      (pkgs.writeShellScriptBin "e" ''
         bash ~/.dotfiles/scripts/editor_nowait.sh "$@"
         '')
      (pkgs.writeShellScriptBin "timer" ''
         sleep "$1"; while true; do spd-say "$2"; sleep 0.5; done;
         '')

      (pkgs.writeScriptBin "project" ''
   #! ${pkgs.bash}/bin/bash
   if [ "$1" == "rust" ]; then
   cp ~/.dotfiles/templates/rust_flake.nix ./flake.nix
   cp ~/.dotfiles/templates/toolchain.toml .
   elif [ "$1" == "cpp" ]; then
   cp ~/.dotfiles/templates/cpp_flake.nix ./flake.nix
   elif [ "$1" == "python" ]; then
   cp ~/.dotfiles/templates/py_flake.nix ./flake.nix
   elif [ "$1" == "cuda" ]; then
   cp ~/.dotfiles/templates/cu_flake.nix ./flake.nix
   elif [ "$1" == "other" ]; then
   cp ~/.dotfiles/templates/other_flake.nix ./flake.nix
   elif [ "$1" == "latex" ]; then
     if [ "$2" == "" ]; then
     echo "No filename specified, usage: 'project latex <NAME>'"
     exit 0
     fi
   cp ~/.dotfiles/templates/tex_standard.tex ./"$2".tex
   exit 0
   else
   echo "No valid argument given. Valid arguments are rust cpp python, cuda"
   exit 0
   fi
   echo "use flake" >> .envrc
   direnv allow
   '')

      (pkgs.writeShellApplication {
        name = "pass-fuzzel";
        runtimeInputs = [ pkgs.pass pkgs.fuzzel ];
        text = ''
         shopt -s nullglob globstar

         typeit=0
         if [[ $# -ge 1 && $1 == "--type" ]]; then
           typeit=1
           shift
         fi

         export PASSWORD_STORE_DIR=~/.local/share/password-store
         prefix=''${PASSWORD_STORE_DIR-~/.local/share/password-store}
         password_files=( "$prefix"/**/*.gpg )
         password_files=( "''${password_files[@]#"$prefix"/}" )
         password_files=( "''${password_files[@]%.gpg}" )

         password=$(printf '%s\n' "''${password_files[@]}" | fuzzel --dmenu "$@")

         [[ -n $password ]] || exit

         if [[ $typeit -eq 0 ]]; then
           pass show -c "$password" &>/tmp/pass-fuzzel
         else
           pass show "$password" | { IFS= read -r pass; printf %s "$pass"; } | wtype -
         fi
         notify-send -u critical -a pass -t 1000 "Copied/Typed Password"
       '';
      })

      (pkgs.writeShellApplication {
        name = "pass-fuzzel-otp";
        runtimeInputs = [ pkgs.fuzzel (pkgs.pass.withExtensions (exts: [exts.pass-otp]))];
        text = ''
         shopt -s nullglob globstar

         typeit=0
         if [[ $# -ge 1 && $1 == "--type" ]]; then
           typeit=1
           shift
         fi

         export PASSWORD_STORE_DIR=~/.local/share/password-store
         prefix=''${PASSWORD_STORE_DIR-~/.local/share/password-store}
         password_files=( "$prefix"/otp/**/*.gpg )
         password_files=( "''${password_files[@]#"$prefix"/}" )
         password_files=( "''${password_files[@]%.gpg}" )

         password=$(printf '%s\n' "''${password_files[@]}" | fuzzel --dmenu "$@")

         [[ -n $password ]] || exit

         if [[ $typeit -eq 0 ]]; then
           pass otp -c "$password" &>/tmp/pass-fuzzel
         else
           pass otp "$password" | { IFS= read -r pass; printf %s "$pass"; } | wtype -
         fi
         notify-send -u critical -a pass -t 1000 "Copied/Typed OTPassword"
       '';
      })

      (pkgs.writeShellApplication {
        name = "cdw";
        runtimeInputs = [ pkgs.fzf ];
        text = ''
      cd "$(git worktree list | fzf | awk '{print $1}')"
      '';
      })

      (pkgs.writeShellApplication {
        name = "cdb";
        runtimeInputs = [ pkgs.fzf ];
        text = ''
      git checkout "$(git branch --list | grep -v "^\*" | fzf | awk '{print $1}')"
      '';
      })

      # (pkgs.writeShellApplication {
      #   name = "cdr";
      #   runtimeInputs = [ pkgs.fzf ];
      #   text = ''
      #   cdr () {
      # cd "$( (find /home/swarsel/Documents/GitHub -maxdepth 1 && echo /home/swarsel/.dotfiles) | fzf )"
      # }
      # cdr
      # '';
      # })

      (pkgs.writeShellApplication {
        name = "bak";
        text = ''
        cp "$1"{,.bak}
      '';
      })

  ];


    #  MIGHT NEED TO ENABLE THIS ON SURFACE!!

sops

I use sops-nix to handle secrets that I want to have available on my machines at all times. Procedure to add a new machine:

  • `ssh-keygen -t ed25519 -C "NAME sops"` in .ssh directory (or wherever) - name e.g. "sops"
  • cat ~/.ssh/sops.pub | ssh-to-age | wl-copy
  • add the output to .sops.yaml
  • cp ~/.ssh/sops.pub ~/.dotfiles/secrets/keys/NAME.pub
  • update entry for sops.age.sshKeyPaths
  sops.defaultSopsFile = "${config.home.homeDirectory}/.dotfiles/secrets/general/secrets.yaml";
  sops.validateSopsFiles = false;

  # sops.age.keyFile = "${config.home.homeDirectory}/.ssh/key.txt";
  # This will generate a new key if the key specified above does not exist
  # sops.age.generateKey = true;

  # sops.gnupg.home = "/home/swarsel/.dotfiles/secrets/keys";
  # since we are using the home-manager implementation, we need to specify the runtime path for each secret
  sops.secrets.mrswarsel = {path = "/run/user/1000/secrets/mrswarsel";};
  sops.secrets.nautilus = {path = "/run/user/1000/secrets/nautilus";};
  sops.secrets.leon = {path = "/run/user/1000/secrets/leon";};
  sops.secrets.swarselmail = {path = "/run/user/1000/secrets/swarselmail";};
  sops.secrets.caldav = {path = "${config.home.homeDirectory}/.emacs.d/.caldav";};
  # sops.secrets.leon = { };
  # sops.secrets.nautilus = { };
  # sops.secrets.mrswarsel = { };

SSH Machines

It is very convenient to have SSH aliases in place for machines that I use. This is mainly used for some server machines and some university clusters.

  programs.ssh= {
    enable = true;
    extraConfig = "SetEnv TERM=xterm-256color";
    matchBlocks = {
      "nginx" = {
        hostname = "192.168.1.14";
        user = "root";
      };
      "jellyfin" = {
        hostname = "192.168.1.16";
        user = "root";
      };
      "pfsense" = {
        hostname = "192.168.1.1";
        user = "root";
      };
      "proxmox" = {
        hostname = "192.168.1.2";
        user = "root";
      };
      "transmission" = {
        hostname = "192.168.1.6";
        user = "root";
      };
      "fetcher" = {
        hostname = "192.168.1.7";
        user = "root";
      };
      "omv" = {
        hostname = "192.168.1.3";
        user = "root";
      };
      "webbot" = {
        hostname = "192.168.1.11";
        user = "root";
      };
      "nextcloud" = {
        hostname = "192.168.1.5";
        user = "root";
      };
      "sound" = {
        hostname = "192.168.1.13";
        user = "root";
      };
      "spotify" = {
        hostname = "192.168.1.17";
        user = "root";
      };
      "wordpress" = {
        hostname = "192.168.1.9";
        user = "root";
      };
      "turn" = {
        hostname = "192.168.1.18";
        user = "root";
      };
      "hugo" = {
        hostname = "192.168.1.19";
        user = "root";
      };
      "matrix" = {
        hostname = "192.168.1.23";
        user = "root";
      };
      "scroll" = {
        hostname = "192.168.1.22";
        user = "root";
      };
      "minecraft" = {
        hostname = "130.61.119.129";
        user = "opc";
      };
      "sync" = {
        hostname = "193.122.53.173";
        user = "root"; #this is a oracle vm server but needs root due to nixos-infect
      };
      "pkv" = {
        hostname = "46.232.248.161";
        user = "root";
      };
      "nebula" = {
        hostname = "128.131.171.15";
        user = "amp23s56";
        compression = true;
        identityFile = "~/.ssh/id_ed25519";
        proxyCommand = "ssh -p 1022 -i ~/.ssh/id_ed25519 -q -W %h:%p %r@venus.par.tuwien.ac.at";
        extraOptions = {
          "TCPKeepAlive" = "yes";
        };
      };
      "efficient" = {
        hostname = "g0.complang.tuwien.ac.at";
        forwardAgent = true;
        user = "ep01427399";

        # leaving the below lines in for future reference

        # remoteForwards = [
        #   {
        #     bind.address = "/run/user/21217/gnupg/S.gpg-agent";
        #     host.address = "/run/user/1000/gnupg/S.gpg-agent.extra";
        #   }
        #   {
        #     bind.address = "/run/user/21217/gnupg/S.gpg-agent.ssh";
        #     host.address = "/run/user/1000/gnupg/S.gpg-agent.ssh";
        #   }
        # ];
        # extraOptions = {
        # "RemoteForward" = "/run/user/21217/gnupg/S.gpg-agent /run/user/1000/gnupg/S.gpg-agent.extra";
        # "StreamLocalBindUnlink" = "yes";
        # "RemoteForward" = "/run/user/21217/gnupg/S.gpg-agent.ssh /run/user/1000/gnupg/S.gpg-agent.ssh";
        # };
        # setEnv = {
        #   "TERM" = "xterm";
        # };
      };
      "hydra" = {
        hostname = "128.131.171.215";
        user = "hpc23w33";
        compression = true;
        forwardAgent = true;
        # identityFile = "~/.ssh/id_tuwien_hpc";
        # proxyCommand = "ssh -p 1022 -i ~/.ssh/id_tuwien_hpc -q -W %h:%p %r@venus.par.tuwien.ac.at";
        proxyCommand = "ssh -p 1022 -q -W %h:%p %r@venus.par.tuwien.ac.at";
        extraOptions = {
          "TCPKeepAlive" = "yes";
        };
      };
    };
  };

Fonts + Theme

These section allows home-manager to allow theme settings, and handles some other appearance-related settings like cursor styles. Interestingly, system icons (adwaita) still need to be setup on system-level, and will break if defined here.

  stylix.targets.emacs.enable = false;

    # fonts.fontconfig.enable = true;
    # gtk = {
    #   enable = true;

    #   theme = {
    #     name = "Arc-Dark";
    #     package = pkgs.arc-theme;
    #   };

    #   cursorTheme = {
    #     name = "capitaine-cursors";
    #     package = pkgs.capitaine-cursors;
    #   };

    #   gtk3.extraConfig = {
    #     Settings = ''
    #    gtk-application-prefer-dark-theme=1
    #    '';
    #   };

    #   gtk4.extraConfig = {
    #     Settings = ''
    #    gtk-application-prefer-dark-theme=1
    #    '';
    #   };
    # };

TODO Desktop Entries

Some programs lack a dmenu launcher - I define them myself here.

TODO: Non-NixOS machines (=sp3) should not use these by default, but instead the programs prefixed with "nixGL". I need to figure out how to automate this process, as it is not feasible to write desktop entries for all programs installed on that machine.

  xdg.desktopEntries = {

    cura = {
      name = "Ultimaker Cura";
      genericName = "Cura";
      exec = "cura";
      terminal = false;
      categories = [ "Application"];
    };

    anki = {
      name = "Anki Flashcards";
      genericName = "Anki";
      exec = "anki";
      terminal = false;
      categories = [ "Application"];
    };

    # schlidichat = {
    #   name = "SchildiChat Matrix Client";
    #   genericName = "SchildiChat";
    #   exec = "schildichat-desktop -enable-features=UseOzonePlatform -ozone-platform=wayland --disable-gpu-driver-bug-workarounds";
    #   terminal = false;
    #   categories = [ "Application"];
    # };

    # currently unused but kept for possible future use-case
    # not needed as long as schildichat is working properly
    element = {
      name = "Element Matrix Client";
      genericName = "Element";
      exec = "element-desktop -enable-features=UseOzonePlatform -ozone-platform=wayland --disable-gpu-driver-bug-workarounds";
      terminal = false;
      categories = [ "Application"];
    };

  };

TODO Sourcing dotfiles and environment variables

This section should be used in order to symlink already existing configuration files using `home.file` and setting session variables using `home.sessionVariables`.

TODO At the moment this is only used for emacs' init.el and early-init.el - I plan to migrate them to a NUR emacs-init setup at some point in the future, but that is a lot of work and will take possibly more time than I am able to spend on this right now

As for the `home.sessionVariables`, it should be noted that environment variables that are needed at system start should NOT be loaded here, but instead in `programs.zsh.config.extraSessionCommands` (in the home-manager programs section). This is also where all the wayland related variables are stored.

  home.file = {
    "init.el" = {
      source = ../../programs/emacs/init.el;
      target = ".emacs.d/init.el";
    };
    "early-init.el" = {
      source = ../../programs/emacs/early-init.el;
      target = ".emacs.d/early-init.el";
    };
    # on NixOS, Emacs does not find the aspell dicts easily. Write the configuration manually
    ".aspell.conf" = {
      source = ../../programs/config/.aspell.conf;
      target = ".aspell.conf";
    };
    ".gitmessage" = {
      source = ../../programs/git/.gitmessage;
      target = ".gitmessage";
    };
  };

  home.sessionVariables = {
    EDITOR = "bash ~/.dotfiles/scripts/editor.sh";
    EDITORBAK = "bash ~/.dotfiles/scripts/editor.sh";
    # GTK_THEME = "Arc-Dark";
  };

Programs

General

  programs.password-store = {
    enable = true;
    package = pkgs.pass.withExtensions (exts: [exts.pass-otp]);
  };
  # zsh Integration is enabled by default for these
  programs.bottom.enable = true;
  programs.imv.enable = true;
  programs.sioyek.enable = true;
  programs.bat.enable = true;
  programs.carapace.enable = true;
  programs.wlogout.enable = true;
  programs.swayr.enable = true;
  programs.yt-dlp.enable = true;
  programs.mpv.enable = true;
  programs.jq.enable = true;
  programs.nix-index.enable = true;
  programs.ripgrep.enable = true;
  programs.pandoc.enable = true;
  programs.fzf.enable = true;
  programs.direnv = {
    enable = true;
    nix-direnv.enable = true;
    };
  programs.zoxide.enable = true;
  programs.eza = {
    enable = true;
    icons = true;
    git = true;
    extraOptions = [
      "-l"
      "--group-directories-first"
    ];
  };
  programs.git = {
    enable = true;
    aliases = {
      a = "add";
      c = "commit";
      cl = "clone";
      co = "checkout";
      b = "branch";
      i = "init";
      m = "merge";
      s = "status";
      r = "restore";
      p = "pull";
      pp = "push";
    };
    signing = {
      key = "0x76FD3810215AE097";
      signByDefault = true;
    };
    userEmail = "leon.schwarzaeugl@gmail.com";
    userName = "Swarsel";
    difftastic.enable = true;
    lfs.enable = true;
    includes = [
      {
        contents = {
          github = {
            user = "Swarsel";
          };
          commit = {
            template = "~/.gitmessage";
          };
        };
      }
    ];
  };

Fuzzel

  programs.fuzzel = {
    enable = true;
    settings = {
      main = {
        layer = "overlay";
        # font = "Monospace:size=8";
        lines = "10";
        width = "40";
      };
      colors = {
        # background="293744dd";
        # text="f8f8f2ff";
        # match="8be9fdff";
        # selection-match="8be9fdff";
        # selection="44475add";
        # selection-text="f8f8f2ff";
        # border="ffd700ff";
      };
      border.radius = "0";
    };
  };

Starship

  programs.starship = {
    enable = true;
    enableZshIntegration = true;
    settings = {
      add_newline = false;
      format = "$character";
      right_format = "$all";
      command_timeout = 3000;

      directory.substitutions = {
        "Documents" = "󰈙 ";
        "Downloads" = " ";
        "Music" = " ";
        "Pictures" = " ";
      };

      git_status = {
        style = "bg:#394260";
        format = "[[($all_status$ahead_behind )](fg:#769ff0 bg:#394260)]($style)";
      };

      character = {
        success_symbol = "[λ](bold green)";
        error_symbol = "[λ](bold red)";
      };

      aws.symbol = "  ";
      buf.symbol = " ";
      c.symbol = " ";
      conda.symbol = " ";
      dart.symbol = " ";
      directory.read_only = " 󰌾";
      docker_context.symbol = " ";
      elixir.symbol = " ";
      elm.symbol = " ";
      fossil_branch.symbol = " ";
      git_branch.symbol = " ";
      golang.symbol = " ";
      guix_shell.symbol = " ";
      haskell.symbol = " ";
      haxe.symbol = " ";
      hg_branch.symbol = " ";
      hostname.ssh_symbol = " ";
      java.symbol = " ";
      julia.symbol = " ";
      lua.symbol = " ";
      memory_usage.symbol = "󰍛 ";
      meson.symbol = "󰔷 ";
      nim.symbol = "󰆥 ";
      nix_shell.symbol = " ";
      nodejs.symbol = " ";

      os.symbols = {
        Alpaquita = " ";
        Alpine = " ";
        Amazon = " ";
        Android = " ";
        Arch = " ";
        Artix = " ";
        CentOS = " ";
        Debian = " ";
        DragonFly = " ";
        Emscripten = " ";
        EndeavourOS = " ";
        Fedora = " ";
        FreeBSD = " ";
        Garuda = "󰛓 ";
        Gentoo = " ";
        HardenedBSD = "󰞌 ";
        Illumos = "󰈸 ";
        Linux = " ";
        Mabox = " ";
        Macos = " ";
        Manjaro = " ";
        Mariner = " ";
        MidnightBSD = " ";
        Mint = " ";
        NetBSD = " ";
        NixOS = " ";
        OpenBSD = "󰈺 ";
        openSUSE = " ";
        OracleLinux = "󰌷 ";
        Pop = " ";
        Raspbian = " ";
        Redhat = " ";
        RedHatEnterprise = " ";
        Redox = "󰀘 ";
        Solus = "󰠳 ";
        SUSE = " ";
        Ubuntu = " ";
        Unknown = " ";
        Windows = "󰍲 ";
      };

      package.symbol = "󰏗 ";
      pijul_channel.symbol = " ";
      python.symbol = " ";
      rlang.symbol = "󰟔 ";
      ruby.symbol = " ";
      rust.symbol = " ";
      scala.symbol = " ";
    };
  };

Kitty

Kitty is the terminal emulator of choice for me, it is nice to configure using nix, fast, and has a nice style.

  programs.kitty = {
    enable = true;
    keybindings = {
      "ctrl+shift+left" = "no_op";
      "ctrl+shift+right" = "no_op";
      "ctrl+shift+home" = "no_op";
      "ctrl+shift+end" = "no_op";
    };
    # theme = "citylights";
  };

zsh

zsh is clearly the most convenient shell for me and it happens to be super neat to configure within home manager.

  programs.zsh = {
    enable = true;
    shellAliases = {
      hg = "history | grep";
      hmswitch = "cd ~/.dotfiles; home-manager --flake .#$(whoami)@$(hostname) switch; cd -;";
      nswitch = "cd ~/.dotfiles; sudo nixos-rebuild --flake .#$(hostname) switch; cd -;";
      edithome = "bash ~/.dotfiles/scripts/editor.sh ~/.dotfiles/Nix.org";
      magit = "emacsclient -nc -e \"(magit-status)\"";
      config="git --git-dir=$HOME/.cfg/ --work-tree=$HOME";
      g="git";
      c="git --git-dir=$HOME/.dotfiles/.git --work-tree=$HOME/.dotfiles/";
      passpush = "cd ~/.local/share/password-store; git add .; git commit -m 'pass file changes'; git push; cd -;";
      passpull = "cd ~/.local/share/password-store; git pull; cd -;";
      hotspot = "nmcli connection up local; nmcli device wifi hotspot password 12345678;";
      cd="z";
      cdr = "cd \"$( (find /home/swarsel/Documents/GitHub -maxdepth 1 && echo /home/swarsel/.dotfiles) | fzf )\"";
    };
    autosuggestion.enable = true;
    enableCompletion = true;
    syntaxHighlighting.enable = true;
    autocd = false;
    cdpath = [
      "~/.dotfiles"
      # "~/Documents/GitHub"
    ];
    defaultKeymap = "emacs";
    dirHashes = {
      dl    = "$HOME/Downloads";
      gh    = "$HOME/Documents/GitHub";
    };
    history = {
      expireDuplicatesFirst = true;
      path = "$HOME/.histfile";
      save = 10000;
      size = 10000;
    };
    historySubstringSearch.enable = true;
    plugins = [
      {
        name = "fzf-tab";
        src = pkgs.zsh-fzf-tab;
      }
    ];
    initExtra = ''
      bindkey "^[[1;5D" backward-word
      bindkey "^[[1;5C" forward-word
    '';
  };

Mail

  programs.mbsync = {
    enable = true;
  };
  # this is needed so that mbsync can use the passwords from sops
  systemd.user.services.mbsync.Unit.After = [ "sops-nix.service" ];

  programs.msmtp = {
    enable = true;
  };

  programs.mu = {
    enable = true;
  };

  accounts.email = {
    maildirBasePath = "Mail";
    accounts.leon = {
      primary = true;
      address = "leon.schwarzaeugl@gmail.com";
      userName = "leon.schwarzaeugl@gmail.com";
      realName = "Leon Schwarzäugl";
      passwordCommand = "cat ${config.sops.secrets.leon.path}";
      # passwordCommand = "gpg --quiet --for-your-eyes-only --no-tty --decrypt ~/.local/share/password-store/mail/mbsync/leon.schwarzaeugl@gmail.com.gpg";
      gpg = {
        key = "0x76FD3810215AE097";
        signByDefault = true;
      };
      imap.host = "imap.gmail.com";
      smtp.host = "smtp.gmail.com";
      mu.enable = true;
      msmtp = {
        enable = true;
      };
      mbsync = {
        enable = true;
        create= "maildir";
        expunge = "both";
        patterns = ["*" "![Gmail]*" "[Gmail]/Sent Mail" "[Gmail]/Starred" "[Gmail]/All Mail"];
        extraConfig = {
          channel = {
            Sync = "All";
          };
          account = {
            Timeout = 120;
            PipelineDepth = 1;
          };
        };
      };
    };

    accounts.swarsel = {
      address = "leon@swarsel.win";
      userName = "8227dc594dd515ce232eda1471cb9a19";
      realName = "Leon Schwarzäugl";
      passwordCommand = "cat ${config.sops.secrets.swarselmail.path}";
      smtp = {
        host = "in-v3.mailjet.com";
        port = 587;
        tls = {
          enable = true;
          useStartTls = true;
        };
      };
      mu.enable = false;
      msmtp = {
        enable = true;
      };
      mbsync = {
        enable = false;
        };
    };

    accounts.nautilus = {
      primary = false;
      address = "nautilus.dw@gmail.com";
      userName = "nautilus.dw@gmail.com";
      realName = "Nautilus";
      passwordCommand = "cat ${config.sops.secrets.nautilus.path}";
      # passwordCommand = "gpg --quiet --for-your-eyes-only --no-tty --decrypt ~/.local/share/password-store/mail/mbsync/nautilus.dw@gmail.com.gpg";
      imap.host = "imap.gmail.com";
      smtp.host = "smtp.gmail.com";
      msmtp.enable = true;
      mu.enable = true;
      mbsync = {
        enable = true;
        create= "maildir";
        expunge = "both";
        patterns = ["*" "![Gmail]*" "[Gmail]/Sent Mail" "[Gmail]/Starred" "[Gmail]/All Mail"];
        extraConfig = {
          channel = {
            Sync = "All";
          };
          account = {
            Timeout = 120;
            PipelineDepth = 1;
          };
        };
      };
    };
    accounts.mrswarsel = {
      primary = false;
      address = "mrswarsel@gmail.com";
      userName = "mrswarsel@gmail.com";
      realName = "Swarsel";
      # passwordCommand = "gpg --quiet --for-your-eyes-only --no-tty --decrypt ~/.local/share/password-store/mail/mbsync/mrswarsel@gmail.com.gpg";
      passwordCommand = "cat ${config.sops.secrets.mrswarsel.path}";
      imap.host = "imap.gmail.com";
      smtp.host = "smtp.gmail.com";
      msmtp.enable = true;
      mu.enable = true;
      mbsync = {
        enable = true;
        create= "maildir";
        expunge = "both";
        patterns = ["*" "![Gmail]*" "[Gmail]/Sent Mail" "[Gmail]/Starred" "[Gmail]/All Mail"];
        extraConfig = {
          channel = {
            Sync = "All";
          };
          account = {
            Timeout = 120;
            PipelineDepth = 1;
          };
        };
      };
    };
  };

Emacs

  # enable emacs overlay for bleeding edge features
  # also read init.el file and install use-package packages
  programs.emacs = {
    enable = true;
    package = (pkgs.emacsWithPackagesFromUsePackage {
      config = ../../Emacs.org; # tangling my Emacs.org file here instead of directly putting init.el allows avoidance of automatically installing packages in blocks using UTF-8 characters, which would break the nix evaluation happening in this line. This line is also the reason why (for now) the Emacs configuration lives in a different .org file
      package = pkgs.emacs-pgtk;
      alwaysEnsure = true;
      alwaysTangle = true;
      extraEmacsPackages = epkgs: [
        epkgs.mu4e
        epkgs.use-package
        epkgs.lsp-bridge
        epkgs.doom-themes

        # build the rest of the packages myself
        # org-calfw is severely outdated on MELPA and throws many warnings on emacs startup
        # build the package from the haji-ali fork, which is well-maintained
         (epkgs.trivialBuild rec {
          pname = "calfw";
          version = "1.0.0-20231002";
          src = pkgs.fetchFromGitHub {
            owner = "haji-ali";
            repo = "emacs-calfw";
            rev = "bc99afee611690f85f0cd0bd33300f3385ddd3d3";
            hash = "sha256-0xMII1KJhTBgQ57tXJks0ZFYMXIanrOl9XyqVmu7a7Y=";
          };
          packageRequires = [ epkgs.howm ];
        })

         (epkgs.trivialBuild rec {
          pname = "fast-scroll";
          version = "1.0.0-20191016";
          src = pkgs.fetchFromGitHub {
            owner = "ahungry";
            repo = "fast-scroll";
            rev = "3f6ca0d5556fe9795b74714304564f2295dcfa24";
            hash = "sha256-w1wmJW7YwXyjvXJOWdN2+k+QmhXr4IflES/c2bCX3CI=";
          };
          packageRequires = [];
        })

      ];
    });
  };

Waybar

Again I am just using the first bar option here that I was able to find good understandable documentation for. Of note is that the `cpu` section's `format` is not defined here, but in section 1 (since not every machine has the same number of cores)

   programs.waybar = {

     enable = true;
     # systemd.enable = true;
     settings = {
       mainBar = {
         layer = "top";
         position = "top";
         modules-left = [ "sway/workspaces" "custom/outer-right-arrow-dark" "sway/window"];
         modules-center = [ "sway/mode" "custom/configwarn" ];
         "sway/mode" = {
           format = "<span style=\"italic\" font-weight=\"bold\">{}</span>";
         };

         "custom/configwarn" = {
           exec= "bash ~/.dotfiles/scripts/checkconfigstatus.sh";
           interval= 60;
         };

         "group/hardware" = {
           orientation = "inherit";
           drawer = {
             "transition-left-to-right" = false;
           };
           modules = [
             "tray"
             "temperature"
             "power-profiles-daemon"
             "custom/left-arrow-light"
             "disk"
             "custom/left-arrow-dark"
             "memory"
             "custom/left-arrow-light"
             "cpu"
             "custom/left-arrow-dark"
           ];
         };

         power-profiles-daemon = {
           format= "{icon}";
           tooltip-format= "Power profile: {profile}\nDriver: {driver}";
           tooltip= true;
           format-icons= {
             "default"= "";
             "performance"= "";
             "balanced"= "";
             "power-saver"= "";
           };
         };

         temperature = {
           critical-threshold = 80;
           format-critical = " {temperatureC}°C";
           format = " {temperatureC}°C";

         };

         mpris = {
           format= "{player_icon} {title} <small>[{position}/{length}]</small>";
           format-paused=  "{player_icon}  <i>{title} <small>[{position}/{length}]</small></i>";
           player-icons=  {
             "default" = "▶ ";
             "mpv" = "🎵 ";
             "spotify" = " ";
           };
           status-icons= {
             "paused"= " ";
           };
           interval = 1;
           title-len = 20;
           artist-len = 20;
           album-len = 10;
         };
         "custom/left-arrow-dark" = {
           format = "";
           tooltip = false;
         };
         "custom/outer-left-arrow-dark"= {
           format = "";
           tooltip = false;
         };
         "custom/left-arrow-light"= {
           format= "";
           tooltip= false;
         };
         "custom/right-arrow-dark"= {
           format= "";
           tooltip= false;
         };
         "custom/outer-right-arrow-dark"= {
           format= "";
           tooltip= false;
         };
         "custom/right-arrow-light"= {
           format= "";
           tooltip= false;
         };
         "sway/workspaces"= {
           disable-scroll= true;
           format= "{name}";
         };

         "clock#1"= {
           min-length= 8;
           interval= 1;
           format= "{:%H:%M:%S}";
           # on-click-right= "gnome-clocks";
           tooltip-format= "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>";
         };

         "clock#2"= {
           format= "{:%d. %B %Y}";
           # on-click-right= "gnome-clocks";
           tooltip-format= "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>";
         };


         pulseaudio= {
           format= "{icon} {volume:2}%";
           format-bluetooth= "{icon} {volume}%";
           format-muted= "MUTE";
           format-icons= {
             headphones= "";
             default= [
               ""
               ""
             ];
           };
           scroll-step= 1;
           on-click= "pamixer -t";
           on-click-right= "pavucontrol";
         };
         memory= {
           interval= 5;
           format= " {}%";
           tooltip-format= "Memory: {used:0.1f}G/{total:0.1f}G\nSwap: {swapUsed}G/{swapTotal}G";
         };
         cpu= {
           min-length= 6;
           interval= 5;
           format-icons = ["▁" "▂" "▃" "▄" "▅" "▆" "▇" "█"];
           # on-click-right= "com.github.stsdc.monitor";
           on-click-right= "kitty -o confirm_os_window_close=0 btm";

         };
         battery= {
           states= {
             "warning"= 60;
             "error"= 30;
             "critical"= 15;
           };
           interval=5;
           format= "{icon} {capacity}%";
           format-charging= "{capacity}% ";
           format-plugged= "{capacity}% ";
           format-icons= [
             ""
             ""
             ""
             ""
             ""
           ];
           on-click-right= "wlogout -p layer-shell";
         };
         disk= {
           interval= 30;
           format= "Disk {percentage_used:2}%";
           path= "/";
           states= {
             "warning"= 80;
             "critical"= 90;
           };
           tooltip-format = "{used} used out of {total} on {path} ({percentage_used}%)\n{free} free on {path} ({percentage_free}%)";
         };
         tray= {
           icon-size= 20;
         };
         network= {
           interval = 5;
           format-wifi= "{signalStrength}% ";
           format-ethernet= "";
           format-linked= "{ifname} (No IP) ";
           format-disconnected= "Disconnected ⚠";
           format-alt= "{ifname}: {ipaddr}/{cidr}";
           tooltip-format-ethernet= "{ifname} via {gwaddr}: {essid} {ipaddr}/{cidr}\n\n⇡{bandwidthUpBytes} ⇣{bandwidthDownBytes}";
           tooltip-format-wifi= "{ifname} via {gwaddr}: {essid} {ipaddr}/{cidr} \n{signaldBm}dBm @ {frequency}MHz\n\n⇡{bandwidthUpBytes} ⇣{bandwidthDownBytes}";
         };
       };
     };

     style = ''
     @define-color foreground #fdf6e3;
     @define-color background #1a1a1a;
     @define-color background-alt #292b2e;
     @define-color foreground-warning #268bd2;
     @define-color background-warning @background;
     @define-color foreground-error red;
     @define-color background-error @background;
     @define-color foreground-critical gold;
     @define-color background-critical blue;


     ,* {
         border: none;
         border-radius: 0;
         font-family: "FiraCode Nerd Font Propo", "Font Awesome 5 Free";
         font-size: 14px;
         min-height: 0;
         margin: -1px 0px;
     }

     window#waybar {
             background: transparent;
             color: @foreground;
             transition-duration: .5s;
     }

     window#waybar.hidden {
         opacity: 0.2;
     }


     #mpris {
         padding: 0 10px;
         background-color: transparent;
         color: #1DB954;
         font-family: Monospace;
         font-size: 12px;
     }

     #custom-right-arrow-dark,
     #custom-left-arrow-dark {
             color: @background;
             background: @background-alt;
             font-size: 24px;
     }

     #window {
             font-size: 12px;
             padding: 0 20px;
     }

     #mode {
         background: @background-critical;
         color: @foreground-critical;
         padding: 0 3px;
     }

     #custom-configwarn {
         color: black;
         padding: 0 3px;
         animation-name: configblink;
         animation-duration: 0.5s;
         animation-timing-function: linear;
         animation-iteration-count: infinite;
         animation-direction: alternate;
     }

     #custom-outer-right-arrow-dark,
     #custom-outer-left-arrow-dark {
             color: @background;
             font-size: 24px;
     }

     #custom-outer-left-arrow-dark,
     #custom-left-arrow-dark,
     #custom-left-arrow-light {
             margin: 0 -1px;
     }

     #custom-right-arrow-light,
     #custom-left-arrow-light {
             color: @background-alt;
             background: @background;
             font-size: 24px;
     }

     #workspaces,
     #clock.1,
     #clock.2,
     #clock.3,
     #pulseaudio,
     #memory,
     #cpu,
     #temperature,
     #power-profiles-daemon,
     #mpris,
     #tray {
             background: @background;
     }

     #network,
     #clock.2,
     #battery,
     #cpu,
     #custom-pseudobat,
     #disk {
             background: @background-alt;
     }


     #workspaces button {
             padding: 0 2px;
             color: #fdf6e3;
     }
     #workspaces button.focused {
             color: @foreground-warning;
     }

     #workspaces button:hover {
         background: @foreground;
         color: @background;
             border: @foreground;
             padding: 0 2px;
             box-shadow: inherit;
             text-shadow: inherit;
     }

     #workspaces button.urgent {
         color: @background-critical;
         background: @foreground-critical;
     }

     #network {
         color: #cc99c9;
     }

     #temperature,
     #power-profiles-daemon {
         color: #9ec1cf;
     }

     #disk {
         /*color: #b58900;*/
         color: #9ee09e;
     }

     #disk.warning {
         color:            @foreground-error;
         background-color: @background-error;
     }
     #disk.critical,
     #temperature.critical {
         color:            @foreground-critical;
         background-color: @background-critical;
         animation-name: blink;
         animation-duration: 0.5s;
         animation-timing-function: linear;
         animation-iteration-count: infinite;
         animation-direction: alternate;
     }
     #pulseaudio.muted {
         color: @foreground-error;
     }
     #memory {
             /*color: #2aa198;*/
             color: #fdfd97;
     }
     #cpu {
         /*color: #6c71c4;*/
         color: #feb144;
     }

     #pulseaudio {
         /*color: #268bd2;*/
         color: #ff6663;
     }

     #battery,
     #custom-pseudobat {
             color: cyan;
     }
     #battery.discharging {
         color:      #859900;
     }

     @keyframes blink {
         to {
             color:            @foreground-error;
             background-color: @background-error;
         }
     }
     @keyframes configblink {
         to {
             color:            @foreground-error;
             background-color: transparent;
         }
     }

     #battery.critical:not(.charging) {
         color:            @foreground-critical;
         background-color: @background-critical;
         animation-name: blink;
         animation-duration: 0.5s;
         animation-timing-function: linear;
         animation-iteration-count: infinite;
         animation-direction: alternate;
     }

     #clock.1,
     #clock.2,
     #clock.3 {
         font-family: Monospace;
     }

     #clock,
     #pulseaudio,
     #memory,
     #cpu,
     #tray,
     #temperature,
     #power-profiles-daemon,
     #network,
     #mpris,
     #battery,
     #custom-pseudobat,
     #disk {
             padding: 0 3px;
     }
         '';
   };

Firefox

  programs.firefox = {
    enable = true;
    package = pkgs.firefox.override {
      nativeMessagingHosts = [
        pkgs.tridactyl-native
        pkgs.browserpass
        pkgs.plasma5Packages.plasma-browser-integration
      ];
    };
    policies = {
        CaptivePortal = false;
        DisableFirefoxStudies = true;
        DisablePocket = true;
        DisableTelemetry = true;
        DisableFirefoxAccounts = false;
        NoDefaultBookmarks = true;
        OfferToSaveLogins = false;
        OfferToSaveLoginsDefault = false;
        EnableTrackingProtection = true;
        };
    profiles.default = {
      isDefault = true;
      extensions = with pkgs.nur.repos.rycee.firefox-addons; [
        tridactyl
        browserpass
        clearurls
        darkreader
        enhancer-for-youtube
        istilldontcareaboutcookies
        translate-web-pages
        ublock-origin
        reddit-enhancement-suite
        pushbullet
        sponsorblock
        web-archives
        single-file
        widegithub
        enhanced-github
        unpaywall
        # fastforwardteam
        don-t-fuck-with-paste
        plasma-integration

        # build the rest of my firefox addons myself
        # app id can be found in the manifest.json file of the .xpi
        # (.xpi is just a normal archive)
        # url can be found by copy url of the "add extension" button on the addon page
        # the rest of the information is also found in the manifest.json, but might not be
        # needed

    #     (let version = "3.4.5.0";
    #                             in buildFirefoxXpiAddon {
    # pname = "bypass-paywalls-clean";
    # inherit version;
    # addonId = "magnolia@12.34";
    # url =
    #   "https://gitlab.com/magnolia1234/bpc-uploads/-/raw/master/bypass_paywalls_clean-3.4.5.0.xpi";
    # sha256 = "703d30c15b88291bd0305cc59013693aea5f75a40ea98fb8e252d1c7bfb43514";
    # meta = with lib; {
    #   homepage =
    #     "https://gitlab.com/magnolia1234/bypass-paywalls-firefox-clean";
    #   description = "Bypass Paywalls of (custom) news sites";
    #   license = licenses.mit;
    #   platforms = platforms.all;
    # };
    # })


        (buildFirefoxXpiAddon {
          pname = ":emoji:";
          version = "0.1.3";
          addonId = "gonelf@gmail.com";
          url = "https://addons.mozilla.org/firefox/downloads/file/3365324/emojidots-0.1.3.xpi";
          sha256 = "4f7cc25c478fe52eb82f37c9ff4978dcaa3f95020398c5b184e517f6efa2c201";
          meta = with lib;
            {
              description = "emoji autocomplete anywhere on the internet";
              mozPermissions = [ "https://gist.githubusercontent.com/gonelf/d8ae3ccb7902b501c4a5dd625d4089da/raw/5eeda197ba92f8c8139e846a1225d5640077e06f/emoji_pretty.json" "tabs" "storage"];
              platforms = platforms.all;
            };
        })

      ];
      search.engines = {
        "Nix Packages" = {
          urls = [{
            template = "https://search.nixos.org/packages";
            params = [
              { name = "type"; value = "packages"; }
              { name = "query"; value = "{searchTerms}"; }
            ];
          }];
          icon = "${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
          definedAliases = [ "@np" ];
        };

        "NixOS Wiki" = {
          urls = [{
            template = "https://nixos.wiki/index.php?search={searchTerms}";
          }];
          iconUpdateURL = "https://nixos.wiki/favicon.png";
          updateInterval = 24 * 60 * 60 * 1000; # every day
          definedAliases = [ "@nw" ];
        };

        "NixOS Options" = {
          urls = [{
            template = "https://search.nixos.org/options";
            params = [
              { name = "query"; value = "{searchTerms}"; }
            ];
          }];

          icon = "${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
          definedAliases = [ "@no" ];
        };

        "Home Manager Options" = {
          urls = [{ template = "https://home-manager-options.extranix.com/";
                    params = [
                      { name = "query"; value = "{searchTerms}"; }
                    ];
                  }];

          icon = "${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
          definedAliases = [ "@hm" "@ho" "@hmo" ];
        };

        "Google".metaData.alias = "@g";
      };
      search.force = true; # this is required because otherwise the search.json.mozlz4 symlink gets replaced on every firefox restart
    };
  };

Browserpass

  # programs.browserpass = {
  #   enable = true;
  #   browsers = [
  #     "firefox"
  #     ];
  #   };

Services

Services that can be defined through home-manager should be defined here.

General

  services.gnome-keyring = {
    enable = true;
  };

  services.mbsync = {
    enable = true;
  };


  services.kdeconnect = {
    enable = true;
    indicator = true;
  };

  services.syncthing = {
    enable = true;
    tray = {
      enable = false; # we enable this by installing the syncthingtray package instead, it works better.
    };
  };

  # this enables the emacs server
  services.emacs = {
    enable = true;
    # socketActivation.enable = false;
    # startWithUserSession = "graphical";
  };

Mako

The `extraConfig` section here CANNOT be reindented. This has something to do with how nix handles multiline strings, when indented Mako will fail to start. This might be a mako bug as well.

  services.mako = {
    enable = true;
    # backgroundColor = "#2e3440";
    # borderColor = "#88c0d0";
    borderRadius = 15;
    borderSize = 1;
    defaultTimeout = 5000;
    height = 150;
    icons = true;
    ignoreTimeout = true;
    layer = "overlay";
    maxIconSize = 64;
    sort = "-time";
    width = 300;
    # font = "monospace 10";
    extraConfig = "[urgency=low]
  border-color=#cccccc
  [urgency=normal]
  border-color=#d08770
  [urgency=high]
  border-color=#bf616a
  default-timeout=3000
  [category=mpd]
  default-timeout=2000
  group-by=category
  ";
  };

Sway

I am currently using SwayFX, which adds some nice effects to sway, like rounded corners and hiding the separator between title and content of a window.

  wayland.windowManager.sway = {
    enable = true;
    checkConfig = false; # delete this line once SwayFX is fixed upstream
    package = pkgs.swayfx;
    # package = pkgs.sway;
    systemd.enable = true;
    systemd.xdgAutostart = true;
    wrapperFeatures.gtk = true;
    config = rec {
      modifier = "Mod4";
      terminal = "kitty";
      menu = "fuzzel";
      bars = [{ command = "waybar";}];
      keybindings = let
        modifier = config.wayland.windowManager.sway.config.modifier;
      in {
        "${modifier}+q" = "kill";
        "${modifier}+f" = "exec firefox";
        "${modifier}+Space" = "exec fuzzel";
        "${modifier}+Shift+Space" = "floating toggle";
        "${modifier}+e" = "exec emacsclient -nquc -a emacs -e \"(dashboard-open)\"";
        "${modifier}+Shift+m" = "exec emacsclient -nquc -a emacs -e \"(mu4e)\"";
        "${modifier}+Shift+c" = "exec emacsclient -nquc -a emacs -e \"(swarsel/open-calendar)\"";
        "${modifier}+Shift+s" = "exec \"bash ~/.dotfiles/scripts/checkspotify.sh\"";
        "${modifier}+m" = "exec \"bash ~/.dotfiles/scripts/checkspotifytui.sh\"";
        "${modifier}+x" = "exec \"bash ~/.dotfiles/scripts/checkkitty.sh\"";
        "${modifier}+d" = "exec \"bash ~/.dotfiles/scripts/checkdiscord.sh\"";
        "${modifier}+Shift+r" = "exec \"bash ~/.dotfiles/scripts/restart.sh\"";
        "${modifier}+Shift+t" = "exec \"bash ~/.dotfiles/scripts/toggle_opacity.sh\"";
        "${modifier}+Shift+F12" = "move scratchpad";
        "${modifier}+F12" = "scratchpad show";
        "${modifier}+c" = "exec qalculate-gtk";
        "${modifier}+p" = "exec pass-fuzzel";
        "${modifier}+o" = "exec pass-fuzzel-otp";
        "${modifier}+Shift+p" = "exec pass-fuzzel --type";
        "${modifier}+Shift+o" = "exec pass-fuzzel-otp --type";
        "${modifier}+Escape" = "mode $exit";
        # "${modifier}+Shift+Escape" = "exec com.github.stsdc.monitor";
        "${modifier}+Shift+Escape" = "exec kitty -o confirm_os_window_close=0 btm";
        "${modifier}+s" = "exec grim -g \"$(slurp)\" -t png - | wl-copy -t image/png";
        "${modifier}+i" = "exec \"bash ~/.dotfiles/scripts/startup.sh\"";
        "${modifier}+1" = "workspace 1:一";
        "${modifier}+Shift+1" = "move container to workspace 1:一";
        "${modifier}+2" = "workspace 2:二";
        "${modifier}+Shift+2" = "move container to workspace 2:二";
        "${modifier}+3" = "workspace 3:三";
        "${modifier}+Shift+3" = "move container to workspace 3:三";
        "${modifier}+4" = "workspace 4:四";
        "${modifier}+Shift+4" = "move container to workspace 4:四";
        "${modifier}+5" = "workspace 5:五";
        "${modifier}+Shift+5" = "move container to workspace 5:五";
        "${modifier}+6" = "workspace 6:六";
        "${modifier}+Shift+6" = "move container to workspace 6:六";
        "${modifier}+7" = "workspace 7:七";
        "${modifier}+Shift+7" = "move container to workspace 7:七";
        "${modifier}+8" = "workspace 8:八";
        "${modifier}+Shift+8" = "move container to workspace 8:八";
        "${modifier}+9" = "workspace 9:九";
        "${modifier}+Shift+9" = "move container to workspace 9:九";
        "${modifier}+0" = "workspace 10:十";
        "${modifier}+Shift+0" = "move container to workspace 10:十";
        "XF86AudioRaiseVolume" = "exec pactl set-sink-volume @DEFAULT_SINK@ +5%";
        "XF86AudioLowerVolume" = "exec pactl set-sink-volume @DEFAULT_SINK@ -5%";
        "${modifier}+Left" = "focus left";
        "${modifier}+Right" = "focus right";
        "${modifier}+Down" = "focus down";
        "${modifier}+Up" = "focus up";
        "${modifier}+Shift+Left" = "move left 40px";
        "${modifier}+Shift+Right" = "move right 40px";
        "${modifier}+Shift+Down" = "move down 40px";
        "${modifier}+Shift+Up" = "move up 40px";
        "${modifier}+h" = "focus left";
        "${modifier}+l" = "focus right";
        "${modifier}+j" = "focus down";
        "${modifier}+k" = "focus up";
        "${modifier}+Shift+h" = "move left 40px";
        "${modifier}+Shift+l" = "move right 40px";
        "${modifier}+Shift+j" = "move down 40px";
        "${modifier}+Shift+k" = "move up 40px";
        "${modifier}+Ctrl+Shift+c" = "reload";
        "${modifier}+Shift+e" = "exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'";
        "${modifier}+r" = "mode resize";
        "${modifier}+Return" = "exec kitty";
      };
      modes = {
        resize = {
          Down = "resize grow height 10 px or 10 ppt";
          Escape = "mode default";
          Left = "resize shrink width 10 px or 10 ppt";
          Return = "mode default";
          Right = "resize grow width 10 px or 10 ppt";
          Up = "resize shrink height 10 px or 10 ppt";
        };
      };
      defaultWorkspace = "workspace 1:一";
      startup = [
        { command = "kitty -T kittyterm";}
        { command = "sleep 60; kitty -T spotifytui -o confirm_os_window_close=0 spotify_player";}
      ];
      window = {
        border = 1;
        titlebar = false;
      };
      assigns = {
        "1:一" = [{ app_id = "firefox"; }];
      };
      colors = {
        focused = {
          # background = "#080808";
          # border = "#80a0ff";
          # childBorder = "#80a0ff";
          # indicator = "#080808";
          # text = "#ffd700";
        };
        unfocused = {
          # background = "#080808";
          # border = "#80a0ff";
          # childBorder = "#303030";
          # indicator = "#80a0ff";
          # text = "#c6c6c6";
        };
      };
      floating = {
        border = 1;
        criteria = [
          {title = "^Picture-in-Picture$";}
          {app_id = "qalculate-gtk";}
          {app_id = "org.gnome.clocks";}
          {app_id = "com.github.stsdc.monitor";}
          {app_id = "blueman";}
          {app_id = "pavucontrol";}
          {app_id = "syncthingtray";}
          {title = "Syncthing Tray";}
          {app_id = "SchildiChat";}
          {app_id = "Element";}
          {app_id = "com.nextcloud.desktopclient.nextcloud";}
          {app_id = "gnome-system-monitor";}
          {title = "(?:Open|Save) (?:File|Folder|As)";}
          {title = "^Add$";}
          {title = "com-jgoodies-jdiskreport-JDiskReport";}
          {app_id = "discord";}
          {window_role = "pop-up";}
          {window_role = "bubble";}
          {window_role = "dialog";}
          {window_role = "task_dialog";}
          {window_role = "menu";}
          {window_role = "Preferences";}
        ];
        titlebar = false;
      };
      window = {
        commands = [
          {
            command = "opacity 0.95";
            criteria = {
              class = ".*";
            };
          }
          {
            command = "opacity 0.95";
            criteria = {
              app_id = ".*";
            };
          }
          {
            command = "opacity 1";
            criteria = {
              app_id = "Gimp-2.10";
            };
          }
          {
            command = "opacity 0.99";
            criteria = {
              app_id = "firefox";
            };
          }
          {
            command = "sticky enable, shadows enable";
            criteria = {
              title="^Picture-in-Picture$";
            };
          }
          {
            command = "opacity 0.8, sticky enable, border normal, move container to scratchpad";
            criteria = {
              title="kittyterm";
            };
          }
          {
            command = "opacity 0.95, sticky enable, border normal, move container to scratchpad";
            criteria = {
              title="spotifytui";
            };
          }
          # {
          #   command = "resize set width 60 ppt height 60 ppt, sticky enable, move container to scratchpad";
          #   criteria = {
          #     app_id="^$";
          #     class="^$";
            # };
          # }
          {

            command = "resize set width 60 ppt height 60 ppt, sticky enable, move container to scratchpad";
            criteria = {
              class="Spotify";
            };
          }
          {
            command = "sticky enable";
            criteria = {
              app_id = "discord";
            };
          }
          {
            command = "resize set width 60 ppt height 60 ppt, sticky enable";
            criteria = {
              class = "Element";
            };
          }
          {
            command = "resize set width 60 ppt height 60 ppt, sticky enable";
            criteria = {
              app_id = "SchildiChat";
            };
          }
        ];
      };
      gaps = {
        inner = 5;
      };
    };
    extraSessionCommands =''
        export SDL_VIDEODRIVER=wayland
        export QT_QPA_PLATFORM=wayland
        export QT_WAYLAND_DISABLE_WINDOWDECORATION="1"
        export _JAVA_AWT_WM_NONREPARENTING=1
        export XDG_CURRENT_DESKTOP=sway
        export XDG_SESSION_DESKTOP=sway
        export QTWEBENGINE_CHROMIUM_FLAGS="--no-sandbox";
        export ANKI_WAYLAND=1;
        export OBSIDIAN_USE_WAYLAND=1;
      '';
    # extraConfigEarly = "
    # exec systemctl --user import-environment DISPLAY WAYLAND_DISPLAY SWAYSOCK
    # exec hash dbus-update-activation-environment 2>/dev/null && dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY SWAYSOCK
    # ";
    extraConfig =let
      modifier = config.wayland.windowManager.sway.config.modifier;
      swayfxSettings = "
          blur enable
          blur_xray disable
          blur_passes 1
          blur_radius 1
          shadows enable
          corner_radius 2
          titlebar_separator disable
          default_dim_inactive 0.02
      ";
      swayfxSettingsOff = "";
    in "
        exec_always autotiling
        set $exit \"exit: [s]leep, [p]oweroff, [r]eboot, [l]ogout\"
        mode $exit {

            bindsym --to-code {
                s exec \"systemctl suspend\", mode \"default\"
                p exec \"systemctl poweroff\"
                r exec \"systemctl reboot\"
                l exec \"swaymsg exit\"

                Return mode \"default\"
                Escape mode \"default\"
                ${modifier}+x mode \"default\"
            }
        }

        exec systemctl --user import-environment

        ${swayfxSettings}

        ";
  };

TODO Manual tasks, Useful bits, flake.nix skeleton and Closing Parenthesis (this needs to be the last heading)

Here are listed some tasks that I was not able to automate yet, these need to be done automatically for now. Also, this section exists to add an extra closing parenthesis to common.nix so that I do not need to think about this anymore if I ever decide to add more headings towards the end of this file ;)

  • TODO: Check if the below tasks can be automated
  1. In blueman, toggle the `ConnectionNotifier` plugin to off (since it is highly annoing)

The last blocks exist to close the opening parenthesis of modules/common.nix (home-manager) and profiles/common.nix (NixOS):

Useful bits

Here I have gathered some snippets that I have deleted from my configuration but figured they might be of use to someone still :)

let-block for overriding a package in nixpkgs (here: replacing airsonic with airsonic-advanced)

This can be useful if a module does not let you use your own package yourself.

  pkgs = import nixpkgs { inherit system;
                          overlays = [ emacs-overlay.overlay
                                       nur.overlay
                                       nixgl.overlay
                                       (self: super: {
                                         airsonic = super.airsonic.overrideAttrs (_: rec {
                                           version = "11.0.2-kagemomiji";
                                           name = "airsonic-advanced-${version}";
                                           src = super.fetchurl {
                                             url = "https://github.com/kagemomiji/airsonic-advanced/releases/download/11.0.2/airsonic.war";
                                             sha256 = "PgErtEizHraZgoWHs5jYJJ5NsliDd9VulQfS64ackFo=";
                                           };
                                         });
                                       })
                                     ];
                          config.allowUnfree = true;
                        };

Closing parentheses for common/home.nix and common/nixos.nix

  }
  }

flake.nix

This tangles the flake.nix file; This block only needs to be touched when updating the general structure of the flake. For everything else, see the respective noweb-ref block.

    {
    description = "SwarseFlake - Nix Flake for all SwarselSystems";

    inputs = {
      <<flakeinputs>>
    };

    outputs = inputs@{
      self,
        <<flakeinputsatoutputs>>
        ...
    }: let
      <<flakelet>>
    in {

      # NixOS setups - run home-manager as a NixOS module for better compatibility
      # another benefit - full rebuild on nixos-rebuild switch
      # run rebuild using `nswitch`

      # NEW HOSTS: For a new host, decide whether a NixOS (nixosConfigurations) or non-NixOS (homeConfigurations) is used.
      # Make sure to move hardware-configuration to the appropriate location, by default it is found in /etc/nixos/.

      nixosConfigurations = {
        <<flakenixosconf>>
      };

      # pure Home Manager setups - for non-NixOS machines
      # run rebuild using `hmswitch`

      homeConfigurations = {
        <<flakehomeconf>>
      };

      nixOnDroidConfigurations = {
        <<flakedroidconf>>
      };

      packages.x86_64-linux = {
        <<flakenixosgenerators>>
      };

    };
  }