#+title: 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.

#+begin_src nix :noweb-ref theme


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



#+end_src
*** Waybar items - LAPTOPS

#+begin_src nix :noweb-ref waybarlaptop

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

#+end_src
*** Waybar items - PC

#+begin_src nix :noweb-ref waybarpc

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

#+end_src
** NixOS
*** Sway Startup commands

#+begin_src nix :noweb-ref startupnixos

          { command = "nextcloud --background";}
          # { command = "spotify";}
          { command = "discord --start-minimized";}
          { command = "element-desktop --hidden";}
          { command = "ANKI_WAYLAND=1 anki";}
          { command = "OBSIDIAN_USE_WAYLAND=1 obsidian";}
          { command = "nm-applet";}
          # { command = "sleep 60 && syncthingtray --wait"; }

#+end_src

*** gpg-agent

#+begin_src nix :noweb-ref gpgagent

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

#+end_src

*** Wrap with hardware-configuration

#+begin_src nix :noweb-ref wrap

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

#+end_src

*** Virtual hosts init

#+begin_src nix :noweb-ref vminitbare


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

#+end_src

#+begin_src nix :noweb yes :noweb-ref vminit

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

  <<vminitbare>>

#+end_src

** flake.nix
*** Inputs & Inputs@Outputs

#+begin_src nix :noweb-ref flakeinputsatoutputs

  nixpkgs,
  home-manager,
  nix-on-droid,
  nixos-generators,
  emacs-overlay,
  nur,
  nixgl,
  stylix,
  sops-nix,
  lanzaboote,
  pia,
  nixpkgs-mautrix-signal,
  nix-gaming,
  nixos-hardware,

#+end_src

#+begin_src nix :noweb-ref flakeinputs

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

#+end_src
*** let

#+begin_src nix :noweb-ref flakelet

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

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

#+end_src
*** nixosConfigurations

#+begin_src nix :noweb-ref flakenixosconf

    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
      ];
    };
#+end_src
*** homeConfigurations

#+begin_src nix :noweb-ref flakehomeconf

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

#+end_src

*** nixOnDroidConfigurations

#+begin_src nix :noweb-ref flakedroidconf

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

#+end_src

*** nixos-generators

#+begin_src nix :noweb-ref flakenixosgenerators

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

#+end_src

* 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

#+begin_src nix :noweb yes :tangle profiles/TEMPLATE/nixos.nix

  { 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

  }

#+end_src

*** Home Manager
#+begin_src nix :noweb yes :tangle profiles/TEMPLATE/home.nix

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

#+end_src

** 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:

#+begin_src nix
  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
#+end_src

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

**** Home manager

#+begin_src nix :noweb yes :tangle profiles/surface/home.nix

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

#+end_src

*** Onett (Lenovo Y510P)

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

**** NixOS

#+begin_src nix :noweb yes :tangle profiles/onett/nixos.nix

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


  }

#+end_src

**** Home Manager

#+begin_src nix :noweb yes :tangle profiles/onett/home.nix

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

#+end_src

*** Twoson (Lenovo Thinkpad T14s Gen3)
**** NixOS

#+begin_src nix :noweb yes :tangle profiles/twoson/nixos.nix

  { 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


  }

#+end_src

**** Home Manager
#+begin_src nix :noweb yes :tangle profiles/twoson/home.nix

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

#+end_src

*** 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

#+begin_src nix :noweb yes :tangle profiles/threed/nixos.nix

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

  }

#+end_src

**** Home Manager
#+begin_src nix :noweb yes :tangle profiles/threed/home.nix

  { 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
      ";
    };
  }
#+end_src

*** Fourside (Lenovo Thinkpad P14s Gen2)
**** NixOS

#+begin_src nix :noweb yes :tangle profiles/fourside/nixos.nix

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


  }

#+end_src

**** Home Manager
#+begin_src nix :noweb yes :tangle profiles/fourside/home.nix

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

#+end_src

*** 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

#+begin_src nix :noweb yes :tangle profiles/stand/nixos.nix

  { 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

  }

#+end_src

**** Home Manager
#+begin_src nix :noweb yes :tangle profiles/stand/home.nix

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

#+end_src

** 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

#+begin_src nix :tangle profiles/server1/TEMPLATE/nixos.nix

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

#+end_src

*** NGINX
**** NixOS

#+begin_src nix :tangle profiles/server1/nginx/nixos.nix

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

        };
      };





  }

#+end_src

*** [Manual steps required] Calibre

This machine requires manual setup:
1) 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

#+begin_src nix :tangle profiles/server1/calibre/nixos.nix

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


  }

#+end_src

*** Jellyfin
**** NixOS

#+begin_src nix :tangle profiles/server1/jellyfin/nixos.nix

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

  }

#+end_src

*** [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

#+begin_src nix :tangle profiles/server1/transmission/nixos.nix

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


              }

#+end_src

*** [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.
2) 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
3) Restart postgresql.service, matrix-synapse.service, mautrix-whatsapp.service, mautrix-telegram.service

**** NixOS

#+begin_src nix :noweb yes :tangle profiles/server1/matrix/nixos.nix

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

  }

#+end_src

*** Sound
**** NixOS

#+begin_src nix :noweb yes :tangle profiles/server1/sound/nixos.nix

  { 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"
          ];
        }
      ];
    };
  }
#+end_src

*** Spotifyd
**** NixOS

#+begin_src nix :noweb yes :tangle profiles/server1/spotifyd/nixos.nix

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

  }

#+end_src

*** Sync
**** NixOS

#+begin_src nix :tangle profiles/remote/oracle/sync/nixos.nix

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

  }

#+end_src

*** Swatrix
**** NixOS

#+begin_src nix :tangle profiles/remote/oracle/matrix/nixos.nix

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

    imports = [
      ./hardware-configuration.nix
      (unstable + "/nixos/modules/services/matrix/mautrix-signal.nix")
    ];

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

  }

#+end_src

*** Paperless
**** NixOS

#+begin_src nix :noweb yes :tangle profiles/server1/paperless/nixos.nix

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

  }

#+end_src

*** Sandbox (Lenovo Y510P)

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

**** NixOS

#+begin_src nix :noweb yes :tangle profiles/sandbox/nixos.nix

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










      }

#+end_src
* 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.

#+begin_src nix :tangle profiles/common/nixos.nix

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

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

#+end_src

** System Packages

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

#+begin_src nix :tangle profiles/common/nixos.nix

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

#+end_src

** 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.

#+begin_src nix :tangle profiles/common/nixos.nix

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

#+end_src

** 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.

#+begin_src nix :tangle profiles/common/nixos.nix

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

#+end_src

** 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.

#+begin_src nix :tangle profiles/common/nixos.nix

  programs.ssh.startAgent = false;

  services.pcscd.enable = true;

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

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


#+end_src

** 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

#+begin_src nix :tangle profiles/common/nixos.nix

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

#+end_src

* 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.

#+begin_src nix :tangle profiles/common/home.nix

  { 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

      # b2 backup @backblaze
      restic

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

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

    ];


    #  MIGHT NEED TO ENABLE THIS ON SURFACE!!


#+end_src

** 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

#+begin_src nix :tangle profiles/common/home.nix

  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.caldav = {path = "${config.home.homeDirectory}/.emacs.d/.caldav";};
  # sops.secrets.leon = { };
  # sops.secrets.nautilus = { };
  # sops.secrets.mrswarsel = { };
#+end_src

** 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.

#+begin_src nix :tangle profiles/common/home.nix

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

#+end_src

** 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.

#+begin_src nix :tangle profiles/common/home.nix

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

#+end_src

** 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.

#+begin_src nix :tangle profiles/common/home.nix

  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";
      terminal = false;
      categories = [ "Application"];
    };

  };

#+end_src

** 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.

#+begin_src nix :tangle profiles/common/home.nix

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

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

#+end_src

** Programs
*** General

#+begin_src nix :tangle profiles/common/home.nix

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

#+end_src

*** Fuzzel

#+begin_src nix :tangle profiles/common/home.nix

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

#+end_src

*** Starship

#+begin_src nix :tangle profiles/common/home.nix

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

#+end_src

*** Kitty

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

#+begin_src nix :tangle profiles/common/home.nix

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

#+end_src

*** zsh

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


#+begin_src nix :tangle profiles/common/home.nix

  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;";
    };
    autosuggestion.enable = true;
    enableCompletion = true;
    syntaxHighlighting.enable = true;
    autocd = false;
    cdpath = [
      "~/.dotfiles"
      # "~/Documents/GitHub"
    ];
    defaultKeymap = "emacs";
    dirHashes = {
      dl    = "$HOME/Downloads";
    };
    history = {
      expireDuplicatesFirst = true;
      path = "$HOME/.histfile";
      save = 10000;
      size = 10000;
    };
    historySubstringSearch.enable = true;
    initExtra = ''
      bindkey "^[[1;5D" backward-word
      bindkey "^[[1;5C" forward-word
    '';
  };

#+end_src

*** Mail

#+begin_src nix :tangle profiles/common/home.nix

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

#+end_src

*** Emacs

#+begin_src nix :tangle profiles/common/home.nix

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

      ];
    });
  };

#+end_src

*** 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)

#+begin_src nix :tangle profiles/common/home.nix

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

#+end_src

*** Firefox

#+begin_src nix :tangle profiles/common/home.nix

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

#+end_src

*** Browserpass

#+begin_src nix :tangle profiles/common/home.nix

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

#+end_src

** Services

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

*** General

#+begin_src nix :tangle profiles/common/home.nix

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

*** 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.

#+begin_src nix :tangle profiles/common/home.nix

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

#+end_src

** 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.

#+begin_src nix :tangle profiles/common/home.nix

  wayland.windowManager.sway = {
    enable = true;
    # 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+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 = {
        # disabled, this is too annoying to be of use
        # "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";}
          {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

        ${swayfxSettingsOff}

        ";
  };

#+end_src

* 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.

#+begin_src nix

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

#+end_src

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

#+begin_src nix :tangle profiles/common/home.nix

  }

#+end_src

#+begin_src nix :tangle profiles/common/nixos.nix

  }

#+end_src

** 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.

#+begin_src nix :noweb yes :tangle flake.nix

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

    };
  }

#+end_src
