about summary refs log tree commit diff
path: root/modules/common
diff options
context:
space:
mode:
Diffstat (limited to 'modules/common')
-rw-r--r--modules/common/alacritty.nix116
-rw-r--r--modules/common/aria2.nix36
-rw-r--r--modules/common/bat.nix40
-rw-r--r--modules/common/beets.nix73
-rw-r--r--modules/common/chromium.nix27
-rw-r--r--modules/common/common/default.nix11
-rw-r--r--modules/common/common/documentation.nix28
-rw-r--r--modules/common/common/home-manager.nix24
-rw-r--r--modules/common/common/locale.nix6
-rw-r--r--modules/common/common/networking.nix3
-rw-r--r--modules/common/common/nix/default.nix163
-rw-r--r--modules/common/common/nix/patches/alejandra-no-ads.patch33
-rw-r--r--modules/common/common/shell/default.nix139
-rw-r--r--modules/common/common/shell/functions.bash73
-rw-r--r--modules/common/common/users.nix8
-rw-r--r--modules/common/curl.nix38
-rw-r--r--modules/common/default.nix29
-rw-r--r--modules/common/direnv.nix22
-rw-r--r--modules/common/emacs/default.nix163
-rw-r--r--modules/common/emacs/doom/config.el223
-rw-r--r--modules/common/emacs/doom/init.el119
-rw-r--r--modules/common/emacs/doom/packages.el17
-rw-r--r--modules/common/fonts.nix91
-rw-r--r--modules/common/git.nix117
-rw-r--r--modules/common/gnupg.nix58
-rw-r--r--modules/common/htop.nix57
-rw-r--r--modules/common/mpv.nix135
-rw-r--r--modules/common/nmap.nix84
-rw-r--r--modules/common/openconnect.nix83
-rw-r--r--modules/common/openssh.nix58
-rw-r--r--modules/common/password-store.nix33
-rw-r--r--modules/common/profiles/default.nix90
-rw-r--r--modules/common/profiles/dev/containers.nix76
-rw-r--r--modules/common/profiles/dev/default.nix89
-rw-r--r--modules/common/profiles/dev/editorconfig.ini83
-rw-r--r--modules/common/profiles/dev/gdbinit41
-rw-r--r--modules/common/profiles/dev/ghci.conf35
-rw-r--r--modules/common/profiles/dev/pystartup.py121
-rw-r--r--modules/common/profiles/dev/sql.nix101
-rw-r--r--modules/common/profiles/headful.nix107
-rw-r--r--modules/common/profiles/headless.nix23
-rw-r--r--modules/common/qutebrowser.nix536
-rw-r--r--modules/common/subversion.nix52
-rw-r--r--modules/common/tmux.nix60
-rw-r--r--modules/common/vim/default.nix50
-rw-r--r--modules/common/vim/rc.vim253
-rw-r--r--modules/common/vscode.nix167
-rw-r--r--modules/common/wget.nix35
-rw-r--r--modules/common/zathura.nix120
49 files changed, 4146 insertions, 0 deletions
diff --git a/modules/common/alacritty.nix b/modules/common/alacritty.nix
new file mode 100644
index 0000000..142f6c5
--- /dev/null
+++ b/modules/common/alacritty.nix
@@ -0,0 +1,116 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.alacritty;
+in {
+  options.nixfiles.modules.alacritty.enable =
+    mkEnableOption "Alacritty terminal emulator";
+
+  config = mkIf cfg.enable {
+    hm.programs.alacritty = {
+      enable = true;
+      settings = with config.nixfiles.modules; {
+        window = {
+          padding = with config.fontScheme.monospaceFont; {
+            x = size;
+            y = size;
+          };
+          dynamic_padding = false;
+          decorations = "full";
+        };
+        font = with config.fontScheme.monospaceFont; {
+          normal = {
+            inherit family;
+            style = "Regular";
+          };
+          bold = {
+            inherit family;
+            style = "Bold";
+          };
+          italic = {
+            inherit family;
+            style = "Italic";
+          };
+          bold_italic = {
+            inherit family;
+            style = "Bold Italic";
+          };
+          inherit size;
+        };
+        colors = with config.colourScheme; {
+          primary = {inherit background foreground;};
+          cursor = {
+            text = "CellBackground";
+            cursor = "CellForeground";
+          };
+          vi_mode_cursor = {
+            text = "CellBackground";
+            cursor = "CellForeground";
+          };
+          search = {
+            matches = {
+              foreground = white;
+              background = red;
+            };
+            focused_match = {
+              foreground = red;
+              background = black;
+            };
+            footer_bar = {
+              foreground = black;
+              background = white;
+            };
+          };
+          hints = {
+            start = {
+              foreground = black;
+              background = yellow;
+            };
+            end = {
+              foreground = yellow;
+              background = black;
+            };
+            line_indicator = {
+              foreground = null;
+              background = null;
+            };
+            selection = {
+              text = "CellBackground";
+              background = "CellForeground";
+            };
+            normal = {
+              inherit black red green yellow blue magenta cyan white;
+            };
+            bright = {
+              inherit
+                brightBlack
+                brightRed
+                brightGreen
+                brightYellow
+                brightBlue
+                brightMagenta
+                brightCyan
+                brightWhite
+                ;
+            };
+          };
+        };
+        bell = {
+          duration = 0;
+          command = null; # TODO notify-send?
+        };
+        cursor = {
+          style = {
+            shape = "Block";
+            blinking = "Off";
+          };
+          vi_mode_style = "Block";
+        };
+        selection.save_to_clipboard = true; # TODO autocutsel?
+      };
+    };
+  };
+}
diff --git a/modules/common/aria2.nix b/modules/common/aria2.nix
new file mode 100644
index 0000000..f33acb9
--- /dev/null
+++ b/modules/common/aria2.nix
@@ -0,0 +1,36 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.aria2;
+in {
+  options.nixfiles.modules.aria2.enable = mkEnableOption "aria2";
+
+  config = mkIf cfg.enable {
+    hm.programs.aria2 = {
+      enable = true;
+
+      settings = {
+        bt-max-peers = 128;
+        bt-save-metadata = true;
+        continue = true;
+        enable-dht = true;
+        enable-peer-exchange = true;
+        enable-rpc = false;
+        follow-torrent = true;
+        log-level = "info";
+        max-connection-per-server = 16;
+        max-overall-upload-limit = "1K";
+        max-tries = 5;
+        max-upload-limit = "1K";
+        seed-ratio = 0.1;
+        seed-time = 0.1;
+        stream-piece-selector = "default";
+        timeout = 60;
+      };
+    };
+  };
+}
diff --git a/modules/common/bat.nix b/modules/common/bat.nix
new file mode 100644
index 0000000..2b31d16
--- /dev/null
+++ b/modules/common/bat.nix
@@ -0,0 +1,40 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.bat;
+in {
+  options.nixfiles.modules.bat.enable =
+    mkEnableOption "bat, an alternative to cat";
+
+  config = mkIf cfg.enable {
+    hm.programs = {
+      bat = {
+        enable = true;
+        config = {
+          style = "plain";
+          tabs = "4";
+          theme = "base16";
+          wrap = "never";
+        };
+      };
+
+      bash = {
+        shellAliases = let
+          bat = "${pkgs.bat}/bin/bat";
+        in {
+          bay = "${bat} --language=yaml --tabs 2";
+          baj = "${bat} --language=json --tabs 2";
+        };
+
+        initExtra = mkAfter ''
+          _complete_alias bay _bat bat
+          _complete_alias baj _bat bat
+        '';
+      };
+    };
+  };
+}
diff --git a/modules/common/beets.nix b/modules/common/beets.nix
new file mode 100644
index 0000000..83cbff1
--- /dev/null
+++ b/modules/common/beets.nix
@@ -0,0 +1,73 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.beets;
+in {
+  options.nixfiles.modules.beets.enable =
+    mkEnableOption "beets";
+
+  config = mkIf cfg.enable {
+    hm = let
+      beetsdir = "${config.dirs.data}/beets";
+    in {
+      home.sessionVariables.BEETSDIR = beetsdir;
+
+      programs = {
+        beets = {
+          enable = true;
+
+          settings = {
+            library = "${beetsdir}/library.db";
+            directory = config.userDirs.music;
+            plugins = "badfiles edit fetchart info mbsync scrub";
+            original_date = true;
+            import = {
+              write = true;
+              copy = true;
+              move = false;
+              bell = true;
+              from_scratch = true;
+            };
+            match = {
+              preferred = {
+                countries = [
+                  "JP"
+                  "KR"
+                  "TW"
+                  "HK"
+                  "CN"
+                  "RU"
+                  "NL"
+                  "DE"
+                  "AT"
+                  "GB|UK"
+                  "CA"
+                  "AU"
+                  "NZ"
+                  "US"
+                ];
+                original_year = true;
+              };
+            };
+            edit = {
+              albumfields = "album artist albumartist";
+              itemfields = "track title album artist albumartist day month year genre";
+            };
+            fetchart = {
+              auto = true;
+              cautious = true;
+              cover_names = "cover Cover folder Folder art Art album Album front Front";
+              sources = "filesystem coverart itunes amazon albumart wikipedia";
+            };
+            scrub.auto = true;
+          };
+        };
+
+        bash.shellAliases.beet = "${config.hm.programs.beets.package}/bin/beet --config ${config.dirs.config}/beets/config.yaml";
+      };
+    };
+  };
+}
diff --git a/modules/common/chromium.nix b/modules/common/chromium.nix
new file mode 100644
index 0000000..4f0ae12
--- /dev/null
+++ b/modules/common/chromium.nix
@@ -0,0 +1,27 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.chromium;
+in {
+  options.nixfiles.modules.chromium.enable = mkEnableOption "Chromium";
+
+  config = mkIf cfg.enable {
+    hm = {
+      home.packages = with pkgs; [profile-cleaner];
+
+      programs.chromium = {
+        enable = true;
+
+        package = pkgs.chromium;
+
+        extensions = [
+          {id = "cjpalhdlnbpafiamejdnhcphjbkeiagm";} # uBlock Origin
+        ];
+      };
+    };
+  };
+}
diff --git a/modules/common/common/default.nix b/modules/common/common/default.nix
new file mode 100644
index 0000000..2bfe7e8
--- /dev/null
+++ b/modules/common/common/default.nix
@@ -0,0 +1,11 @@
+_: {
+  imports = [
+    ./documentation.nix
+    ./home-manager.nix
+    ./locale.nix
+    ./networking.nix
+    ./nix
+    ./shell
+    ./users.nix
+  ];
+}
diff --git a/modules/common/common/documentation.nix b/modules/common/common/documentation.nix
new file mode 100644
index 0000000..55f6138
--- /dev/null
+++ b/modules/common/common/documentation.nix
@@ -0,0 +1,28 @@
+{
+  config,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; {
+  config = mkMerge [
+    (mkIf this.isHeadful {
+      hm.manual = {
+        html.enable = false;
+        json.enable = false;
+        manpages.enable = true;
+      };
+
+      documentation = {
+        enable = true;
+        doc.enable = false;
+        info.enable = false;
+      };
+    })
+    (mkIf this.isHeadless {
+      hm.manual.manpages.enable = false;
+      documentation.enable = false;
+    })
+  ];
+}
diff --git a/modules/common/common/home-manager.nix b/modules/common/common/home-manager.nix
new file mode 100644
index 0000000..b28260a
--- /dev/null
+++ b/modules/common/common/home-manager.nix
@@ -0,0 +1,24 @@
+{
+  inputs,
+  lib,
+  localUsername ? lib.my.username,
+  ...
+}:
+with lib; {
+  imports = [
+    (mkAliasOptionModule ["hm"] ["home-manager" "users" localUsername])
+  ];
+
+  hm = {
+    news.display = "silent";
+    home.stateVersion = with builtins;
+      head (split "\n" (readFile "${inputs.nixpkgs}/.version"));
+  };
+
+  home-manager = {
+    backupFileExtension = "bak";
+    useUserPackages = true;
+    useGlobalPkgs = true;
+    verbose = true;
+  };
+}
diff --git a/modules/common/common/locale.nix b/modules/common/common/locale.nix
new file mode 100644
index 0000000..bcb577a
--- /dev/null
+++ b/modules/common/common/locale.nix
@@ -0,0 +1,6 @@
+_: {
+  hm.home.language = {
+    collate = "C";
+    messages = "C";
+  };
+}
diff --git a/modules/common/common/networking.nix b/modules/common/common/networking.nix
new file mode 100644
index 0000000..e5d27d8
--- /dev/null
+++ b/modules/common/common/networking.nix
@@ -0,0 +1,3 @@
+{pkgs, ...}: {
+  environment.systemPackages = with pkgs; [myip];
+}
diff --git a/modules/common/common/nix/default.nix b/modules/common/common/nix/default.nix
new file mode 100644
index 0000000..0898457
--- /dev/null
+++ b/modules/common/common/nix/default.nix
@@ -0,0 +1,163 @@
+{
+  config,
+  inputs,
+  lib,
+  localUsername ? lib.my.username,
+  pkgs,
+  pkgsPR,
+  this,
+  ...
+}:
+with lib; {
+  _module.args = let
+    importNixpkgs = nixpkgs:
+      import nixpkgs {inherit (config.nixpkgs) config localSystem;};
+  in rec {
+    pkgsLocal = importNixpkgs "${config.my.home}/src/nixpkgs"; # Impure!
+    pkgsMaster = importNixpkgs inputs.nixpkgs-master;
+    pkgsStable = importNixpkgs inputs.nixpkgs-stable;
+    pkgsRev = rev: hash:
+      importNixpkgs (pkgs.fetchFromGitHub {
+        owner = "NixOS";
+        repo = "nixpkgs";
+        inherit rev hash;
+      });
+    pkgsPR = pr: pkgsRev "refs/pull/${toString pr}/head";
+  };
+
+  nix = let
+    filteredInputs = filterAttrs (n: _: n != "self") inputs;
+  in {
+    settings = {
+      # https://github.com/NixOS/nix/blob/master/src/libutil/experimental-features.cc
+      experimental-features = concatStringsSep " " [
+        "ca-derivations"
+        "flakes"
+        "nix-command"
+        "recursive-nix"
+      ];
+      keep-derivations =
+        if this.isHeadful
+        then "true"
+        else "false";
+      keep-outputs =
+        if this.isHeadful
+        then "true"
+        else "false";
+      flake-registry = "${inputs.flake-registry}/flake-registry.json";
+      warn-dirty = false;
+    };
+
+    nixPath =
+      mapAttrsToList (n: v: "${n}=${v}") filteredInputs
+      ++ ["nixfiles=${config.my.home}/src/nixfiles"];
+
+    registry =
+      mapAttrs (_: flake: {inherit flake;}) filteredInputs
+      // {nixfiles.flake = inputs.self;};
+
+    settings = {
+      trusted-users = ["root" localUsername];
+
+      substituters = [
+        "https://azahi.cachix.org"
+        "https://cache.iog.io"
+        "https://cachix.cachix.org"
+        "https://nix-community.cachix.org"
+        "https://pre-commit-hooks.cachix.org"
+      ];
+      trusted-public-keys = [
+        "azahi.cachix.org-1:2bayb+iWYMAVw3ZdEpVg+NPOHCXncw7WMQ0ElX1GO3s="
+        "cachix.cachix.org-1:eWNHQldwUO7G2VkjpnjDbWwy4KQ/HNxht7H4SSoMckM="
+        "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ="
+        "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
+        "pre-commit-hooks.cachix.org-1:Pkk3Panw5AW24TOv6kz3PvLhlH8puAsJTBbOPmBo7Rc="
+      ];
+    };
+  };
+
+  nixpkgs.overlays = with inputs; [
+    self.overlays.default
+    (_: super:
+      {
+        nix-bash-completions = super.nix-bash-completions.overrideAttrs (_: _: {
+          postPatch = ''
+            substituteInPlace _nix \
+              --replace 'nix nixos-option' 'nixos-option'
+          '';
+        });
+
+        helm = with super;
+          kubernetes-helm-wrapped.override {
+            plugins = with kubernetes-helmPlugins; [helm-secrets];
+          };
+
+        alejandra = super.alejandra.overrideAttrs (_: _: {
+          patches = [./patches/alejandra-no-ads.patch];
+        });
+
+        logcli = super.grafana-loki.overrideAttrs (_: _: {
+          subPackages = ["cmd/logcli"];
+        });
+
+        inherit (pkgsPR "215704" "sha256-o2F/ZAugljJKlVIAHMTBK6+Lj6BiBwteA5OuaWPKXm0=") dendrite;
+
+        inherit (pkgsPR "216465" "sha256-ik3e+KM27hGkKWTOOUWj1YtiqHTJvl04dx0/X08P1A0=") amdvlk;
+      }
+      // (with super; let
+        np = nodePackages;
+      in {
+        # Normalises package names. This is done purely for aesthetics.
+        css-language-server = np.vscode-css-languageserver-bin;
+        dhall-language-server = dhall-lsp-server;
+        dockerfile-language-server = np.dockerfile-language-server-nodejs;
+        editorconfig = editorconfig-core-c;
+        go-language-server = gopls;
+        html-language-server = np.vscode-html-languageserver-bin;
+        inherit (np) bash-language-server;
+        inherit (np) vim-language-server;
+        inherit (np) yaml-language-server;
+        json-language-server = np.vscode-json-languageserver-bin;
+        k3d = kube3d;
+        kubelogin = kubelogin-oidc;
+        lua-language-server = sumneko-lua-language-server;
+        nix-language-server = rnix-lsp;
+        omnisharp = omnisharp-roslyn;
+        telepresence = telepresence2;
+        tor-browser = tor-browser-bundle-bin;
+      }))
+    emacs-overlay.overlay
+    nur.overlay
+  ];
+
+  environment.systemPackages = with pkgs;
+    optionals this.isHeadful [
+      nix-top
+      nix-tree
+    ];
+
+  hm.home = {
+    packages = with pkgs; [nix-index];
+
+    file.".nix-defexpr/default.nix".text =
+      optionalString this.isHeadful
+      (
+        let
+          hostname = strings.escapeNixIdentifier this.hostname;
+        in ''
+          let
+            self = builtins.getFlake "nixfiles";
+            configurations = self.nixosConfigurations;
+            local = configurations.${hostname};
+          in rec {
+            inherit self;
+            inherit (self) inputs lib;
+            inherit (lib) my;
+            this = my.configurations.${hostname};
+            inherit (local) config;
+            inherit (local.config.system.build) toplevel vm vmWithBootLoader manual;
+          } // configurations // local._module.args
+        ''
+      );
+  };
+}
diff --git a/modules/common/common/nix/patches/alejandra-no-ads.patch b/modules/common/common/nix/patches/alejandra-no-ads.patch
new file mode 100644
index 0000000..6eaac66
--- /dev/null
+++ b/modules/common/common/nix/patches/alejandra-no-ads.patch
@@ -0,0 +1,33 @@
+diff --git i/src/alejandra_cli/src/cli.rs w/src/alejandra_cli/src/cli.rs
+index bab102c..b90bf1d 100644
+--- i/src/alejandra_cli/src/cli.rs
++++ w/src/alejandra_cli/src/cli.rs
+@@ -7,7 +7,6 @@ use futures::future::RemoteHandle;
+ use futures::stream::FuturesUnordered;
+ use futures::task::SpawnExt;
+ 
+-use crate::ads::random_ad;
+ use crate::verbosity::Verbosity;
+ 
+ /// The Uncompromising Nix Code Formatter.
+@@ -203,11 +202,6 @@ pub fn main() -> std::io::Result<()> {
+                     (true, false) => "requires formatting",
+                 }
+             );
+-
+-            if in_place {
+-                eprintln!();
+-                eprint!("{}", random_ad());
+-            }
+         }
+ 
+         std::process::exit(if in_place { 0 } else { 2 });
+@@ -218,8 +212,6 @@ pub fn main() -> std::io::Result<()> {
+         eprintln!(
+             "Congratulations! Your code complies with the Alejandra style."
+         );
+-        eprintln!();
+-        eprint!("{}", random_ad());
+     }
+ 
+     std::process::exit(0);
diff --git a/modules/common/common/shell/default.nix b/modules/common/common/shell/default.nix
new file mode 100644
index 0000000..1eebfa2
--- /dev/null
+++ b/modules/common/common/shell/default.nix
@@ -0,0 +1,139 @@
+{
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; {
+  hm = {
+    programs = {
+      bash = {
+        enable = true;
+
+        shellOptions = [
+          "autocd"
+          "cdspell"
+          "checkjobs"
+          "checkwinsize"
+          "dirspell"
+          "extglob"
+          "globstar"
+          "histappend"
+          "histreedit"
+          "histverify"
+        ];
+
+        profileExtra = ''
+          export _PROFILE_SOURCED=1
+        '';
+
+        initExtra = ''
+          set -o notify
+
+          if [ "$TERM" != "dumb" ] || [ -n "$INSIDE_EMACS" ]; then
+            PROMPT_COLOR="1;31m"
+            ((UID)) && PROMPT_COLOR="1;32m"
+            if [ -n "$INSIDE_EMACS" ] || [ "$TERM" = "eterm" ] || [ "$TERM" = "eterm-color" ]; then
+              PS1="\n\[\033[$PROMPT_COLOR\][\u@\h:\w]\\$\[\033[0m\] "
+            else
+              PS1="\n\[\033[$PROMPT_COLOR\][\[\e]0;\u@\h: \w\a\]\u@\h:\w]\\$\[\033[0m\] "
+            fi
+            if test "$TERM" = "xterm"; then
+              PS1="\[\033]2;\h:\u:\w\007\]$PS1"
+            fi
+          fi
+
+          ${readFile ./functions.bash}
+
+          GRC_ALIASES=true
+          source ${pkgs.grc}/etc/profile.d/grc.sh
+
+          if [ -z "$_PROFILE_SOURCED" ] && [ -f "$HOME/.profile" ]; then
+            source "$HOME/.profile"
+          fi
+        '';
+
+        shellAliases =
+          listToAttrs
+          (map
+            ({
+              name,
+              value,
+            }:
+              nameValuePair name (with pkgs; let
+                pkg =
+                  if this.isHeadful
+                  then
+                    (coreutils.overrideAttrs (_: super: {
+                      patches =
+                        super.patches
+                        ++ [
+                          (fetchpatch {
+                            url = "https://raw.githubusercontent.com/jarun/advcpmv/ea268d870b475edd5960dcd55d5378abc9705958/advcpmv-0.9-9.1.patch";
+                            hash = "sha256-d+SRT/R4xmfHLAdOr7m4R3WFiW64P5ZH6iqDvErYCyg=";
+                          })
+                        ];
+                    }))
+                  else coreutils;
+              in "${pkg}/bin/coreutils --coreutils-prog=${value}"))
+            (
+              let
+                mkAlias = {
+                  name ? head command,
+                  command,
+                }: {
+                  inherit name;
+                  value = concatStringsSep " " command;
+                };
+
+                progressBar = optionalString this.isHeadful "--progress-bar";
+              in [
+                (mkAlias {command = ["cp" "--interactive" "--recursive" progressBar];})
+                (mkAlias {command = ["mv" "--interactive" progressBar];})
+                (mkAlias {command = ["rm" "--interactive=once"];})
+                (mkAlias {command = ["ln" "--interactive"];})
+                (mkAlias {command = ["mkdir" "--parents"];})
+                (mkAlias {command = ["rmdir" "--parents"];})
+                (mkAlias {
+                  name = "lower";
+                  command = ["tr" "'[:upper:]'" "'[:lower:]'"];
+                })
+                (mkAlias {
+                  name = "upper";
+                  command = ["tr" "'[:lower:]'" "'[:upper:]'"];
+                })
+                (mkAlias {
+                  name = "disk";
+                  command = [
+                    "df"
+                    "--human-readable"
+                    "--exclude-type=tmpfs"
+                    "--exclude-type=devtmpfs"
+                    "2>/dev/null"
+                  ];
+                })
+              ]
+            ))
+          // genAttrs ["grep" "egrep" "fgrep"]
+          (name: "${pkgs.gnugrep}/bin/${name} --color=always");
+
+        historyControl = ["ignoredups" "ignorespace"];
+      };
+
+      command-not-found.enable = false;
+
+      dircolors.enable = true;
+    };
+
+    home.packages = with pkgs; [grc];
+  };
+
+  environment.systemPackages = with pkgs; [
+    bash-completion
+    bc
+    gawk
+    hr
+    moreutils
+    pv
+  ];
+}
diff --git a/modules/common/common/shell/functions.bash b/modules/common/common/shell/functions.bash
new file mode 100644
index 0000000..c18104f
--- /dev/null
+++ b/modules/common/common/shell/functions.bash
@@ -0,0 +1,73 @@
+_complete_alias() {
+    local alias_name=$1
+    local base_function=$2
+    local function_name=_alias_$alias_name
+    shift 2
+    eval "$function_name() {
+        COMP_WORDS=( ${*@Q} \"\${COMP_WORDS[@]:1}\" )
+        (( COMP_CWORD += $# - 1 ))
+        _completion_loader $1
+        $base_function
+    }"
+    complete -F "$function_name" "$alias_name"
+}
+
+function where() {
+    local s
+    s="$(type -P "$1")"
+    realpath "$s"
+}
+_complete_alias where _complete complete
+
+function what() {
+    local s
+    s="$(where "$1")"
+    printf "%s\n" "${s%/*/*}"
+}
+_complete_alias what _complete complete
+
+function cat() {
+    if (($# == 1)) && [[ -d $1 ]]; then
+        ll "$1"
+    else
+        command cat "$@"
+    fi
+}
+
+function cd() {
+    builtin cd "$@" &&
+        if ((${#FUNCNAME[@]} == 1)); then
+            ls
+        fi
+}
+
+function mkcd() {
+    mkdir -p "$1" && builtin cd "$1"
+}
+
+function mvcd() {
+    mv -i -- "$PWD" "$1" && builtin cd .
+}
+
+function bak() {
+    local f
+    for f; do
+        cp -ai -- "$f" "$f.bak"
+    done
+}
+
+function ubak() {
+    local f
+    for f; do
+        [[ $f == *.bak ]] || f="$f.bak"
+        mv -i -- "$f" "${f%.bak}"
+    done
+}
+
+function dec2hex() {
+    printf "0x%X\n" "$1"
+}
+
+function hex2dec() {
+    printf "%d\n" "0x$1"
+}
diff --git a/modules/common/common/users.nix b/modules/common/common/users.nix
new file mode 100644
index 0000000..aee0e38
--- /dev/null
+++ b/modules/common/common/users.nix
@@ -0,0 +1,8 @@
+{
+  lib,
+  localUsername ? lib.my.username,
+  ...
+}:
+with lib; {
+  imports = [(mkAliasOptionModule ["my"] ["users" "users" localUsername])];
+}
diff --git a/modules/common/curl.nix b/modules/common/curl.nix
new file mode 100644
index 0000000..e7bee31
--- /dev/null
+++ b/modules/common/curl.nix
@@ -0,0 +1,38 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.curl;
+in {
+  options.nixfiles.modules.curl.enable =
+    mkEnableOption "Wether to enable cURL.";
+
+  config = mkIf cfg.enable {
+    hm.home.file.".curlrc".text = ''
+      connect-timeout = 60
+      progress-bar
+      referer = ";auto"
+      remote-time
+      show-error
+    '';
+
+    environment.systemPackages = with pkgs; [
+      curl
+      (writeShellScriptBin "0x0" ''
+        url="https://0x0.st"
+        form="file=@"
+
+        if [ -t 0 ] && [ -n "$1" ]; then
+            form="$form$1"
+        else
+            form="$form-"
+        fi
+
+        ${curl}/bin/curl --form "$form" "$url"
+      '')
+    ];
+  };
+}
diff --git a/modules/common/default.nix b/modules/common/default.nix
new file mode 100644
index 0000000..e6040cd
--- /dev/null
+++ b/modules/common/default.nix
@@ -0,0 +1,29 @@
+_: {
+  imports = [
+    ./alacritty.nix
+    ./aria2.nix
+    ./bat.nix
+    ./beets.nix
+    ./chromium.nix
+    ./common
+    ./curl.nix
+    ./direnv.nix
+    ./emacs
+    ./fonts.nix
+    ./git.nix
+    ./gnupg.nix
+    ./htop.nix
+    ./mpv.nix
+    ./nmap.nix
+    ./openssh.nix
+    ./password-store.nix
+    ./profiles
+    ./qutebrowser.nix
+    ./subversion.nix
+    ./tmux.nix
+    ./vim
+    ./vscode.nix
+    ./wget.nix
+    ./zathura.nix
+  ];
+}
diff --git a/modules/common/direnv.nix b/modules/common/direnv.nix
new file mode 100644
index 0000000..b235cee
--- /dev/null
+++ b/modules/common/direnv.nix
@@ -0,0 +1,22 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.direnv;
+in {
+  options.nixfiles.modules.direnv.enable =
+    mkEnableOption "direnv";
+
+  config = mkIf cfg.enable {
+    hm = {
+      programs.direnv = {
+        enable = true;
+        nix-direnv.enable = true;
+      };
+
+      home.sessionVariables.DIRENV_LOG_FORMAT = "";
+    };
+  };
+}
diff --git a/modules/common/emacs/default.nix b/modules/common/emacs/default.nix
new file mode 100644
index 0000000..7395c51
--- /dev/null
+++ b/modules/common/emacs/default.nix
@@ -0,0 +1,163 @@
+{
+  config,
+  inputs,
+  lib,
+  pkgs,
+  pkgsStable,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.emacs;
+in {
+  options.nixfiles.modules.emacs.enable = mkEnableOption "GNU Emacs";
+
+  config = mkIf cfg.enable {
+    nixfiles.modules = {
+      fonts.enable = true;
+      git.client.enable = true;
+      gnupg.enable = true;
+    };
+
+    hm = {
+      xdg.configFile = {
+        "doom/init.el".source = ./doom/init.el;
+        "doom/packages.el".source = ./doom/packages.el;
+        "doom/config.el" = {
+          text = concatStringsSep "\n" [
+            (let
+              # NOTE gopls will require the "go" executable which must be provided
+              # by the project's flake/shell.
+              extraBins = with pkgs;
+                [
+                  enchant # :checkers (spell +enchant)
+                  (python3.withPackages (p:
+                    with p; [
+                      black # :lang python :editor format
+                      isort # :lang python
+                      pyflakes # :lang python
+                      python-lsp-server # :lang (python +lsp)
+                    ]))
+                  asmfmt # :editor format
+                  bash-language-server # :lang (sh +lsp)
+                  clang-tools # :lang (cc +lsp) :editor format
+                  cmake-format # :lang cc :editor format
+                  cmigemo # :lang japanese
+                  css-language-server # :lang (web +lsp)
+                  dhall-language-server # :lang (dhall +lsp)
+                  dockerfile-language-server # :tools (docker +lsp)
+                  editorconfig # :tools editorconfig
+                  fd # doom!
+                  gnuplot # :lang (org +gnuplot)
+                  gnutls # doom!
+                  go-language-server # :lang (go +lsp)
+                  gomodifytags # :lang go
+                  gore # :lang go
+                  gotests # :lang go
+                  gotools # :lang go
+                  graphviz # :lang (org +roam2) :lang plantuml
+                  html-language-server # :lang (web +lsp)
+                  html-tidy # :lang web
+                  jre # :lang plantuml
+                  json-language-server # :lang (json +lsp)
+                  nix-language-server # :lang (nix +lsp)
+                  nixfmt # :lang nix :editor format
+                  nodePackages.js-beautify # :lang web
+                  nodePackages.prettier # :editor format
+                  nodePackages.stylelint # :lang web
+                  nodejs # :tools debugger
+                  pandoc # :lang org markdown latex
+                  pinentry-emacs # doom!
+                  pre-commit # :tools magit
+                  ripgrep # doom!
+                  rust-analyzer # :lang (rust +lsp)
+                  rustfmt # :lang rust
+                  shellcheck # :lang sh
+                  shfmt # :lang sh :editor format
+                  sqlite # :lang (org +roam2) :tools lookup
+                  texlab # lang (tex +lsp)
+                  texlive.combined.scheme-full # :lang org tex
+                  unzip # :tools debugger
+                  wordnet # :tools (lookup +dictionary +offline)
+                  yaml-language-server # :lang (yaml +lsp)
+                  zls # :lang (zig +lsp)
+                  zstd # :emacs undo
+                ]
+                ++ (
+                  #
+                  # GDB doesn't support[1] Apple Silicon on MacOS.
+                  #
+                  # [1]: https://inbox.sourceware.org/gdb/6b48224b-9e2e-518d-793b-df4fc5514884@arm.com/
+                  if (this.system != "aarch64-darwin")
+                  then [gdb] # :tools debugger
+                  else [lldb] # :tools debugger
+                )
+                ++ optionals (!pkgs.stdenv.isDarwin)
+                [
+                  # NOTE Haskell is pretty much broken every couple of days on
+                  # MacOS and I usually don't write anything in Haskell while
+                  # I'm on my work laptop, so... ShellCheck seems to be working,
+                  # though.
+                  haskellPackages.ormolu # :lang haskell :editor format
+                  haskellPackages.haskell-language-server # :lang (haskell +lsp)
+                  haskellPackages.cabal-fmt # :lang haskell :editor format
+                  haskellPackages.cabal-install # :lang haskell
+                  haskellPackages.hoogle # :lang haskell
+                ];
+            in ''
+              ;; This will integrate packages which are required by various
+              ;; modules without polluting the user's profile.
+              (setq exec-path (append exec-path '(${
+                concatMapStringsSep " " (x: ''"${x}/bin"'') extraBins
+              })))
+              (setenv "PATH" (concat (getenv "PATH") ":${
+                concatMapStringsSep ":" (x: "${x}/bin") extraBins
+              }"))
+
+              ;; Font must be set to N+2 because otherwise it looks too small.
+              (setq doom-font (font-spec :family "${config.fontScheme.monospaceFont.family}"
+                                          :size ${toString (config.fontScheme.monospaceFont.size + 2)})
+                    doom-unicode-font doom-font)
+
+              (setq user-full-name "${my.fullname}"
+                    user-mail-address "${my.email}")
+
+              ;; :app irc
+              (setq circe-default-nick "${my.username}"
+                    circe-default-realname "${my.email}"
+                    circe-default-user circe-default-nick)
+
+              ;; :lang plantuml
+              (setq org-plantuml-jar-path "${pkgs.plantuml}/lib/plantuml.jar")
+
+              ;; :input japanese
+              (setq migemo-dictionary "${pkgs.cmigemo}/share/migemo/utf-8/migemo-dict")
+
+              ;; :input japanese
+              (setq skk-large-jisyo "${pkgs.skk-dicts}/share/skk/SKK-JISYO.L")
+            '')
+            (builtins.readFile ./doom/config.el)
+          ];
+          onChange = with config.hm.programs; ''
+            if [[ -x "''${XDG_CONFIG_HOME:~/.config}/emacs/bin/doom" ]]; then
+               oldpath="$PATH"
+               export PATH="''${PATH:-/bin}:${emacs.package}/bin:${git.package}/bin"
+
+               "''${XDG_CONFIG_HOME:~/.config}/emacs/bin/doom" sync
+
+               export PATH="$oldpath"
+               unset oldpath
+            fi
+          '';
+        };
+      };
+
+      programs.emacs = {
+        enable = true;
+        package = pkgs.emacs28; # Pin to avoid surprises.
+        # For some reason latest libvterm is not picked up by Emacs.
+        extraPackages = _: with pkgsStable.emacsPackages; [vterm];
+      };
+    };
+  };
+}
diff --git a/modules/common/emacs/doom/config.el b/modules/common/emacs/doom/config.el
new file mode 100644
index 0000000..9284e0b
--- /dev/null
+++ b/modules/common/emacs/doom/config.el
@@ -0,0 +1,223 @@
+;;
+;;; Misc
+;;
+
+(setq display-line-numbers-type t)
+
+(setq scroll-margin 10
+      hscroll-margin 10)
+
+(setq browse-url-generic-program (executable-find "firefox")
+      browse-url-browser-function 'browse-url-generic)
+
+;;
+;;; Doom-specific
+;;
+
+;; Make it more default-ey.
+(setq frame-title-format '("Emacs")
+      icon-title-format frame-title-format)
+
+(setq doom-theme 'doom-tomorrow-night)
+
+(setq +format-on-save-enabled-modes '(not nix-mode json-mode yaml-mode))
+
+;;
+;;; LSP
+;;
+
+(setq lsp-enable-suggest-server-download nil)
+
+;;
+;;; Org
+;;
+
+(setq org-directory "~/doc/org/")
+(after! org
+  (setq org-todo-keywords '((sequence
+                             "PROJ(p)" ; A master task.
+                             "TODO(t)" ; A task that needs to be done with statues:
+                             "STRT(s@)" ; - In progress.
+                             "HOLD(l@/!)" ; - Paused because of me.
+                             "WAIT(w@/!)" ; - Paused because of not me.
+                             "|"
+                             "DONE(d@/!)" ; Complete state.
+                             "KILL(k@/!)")) ; Incomplete state.
+        org-todo-keyword-faces '(("PROJ" . +org-todo-project)
+                                 ("TODO" . +org-todo-active)
+                                 ("STRT" . +org-todo-active)
+                                 ("HOLD" . +org-todo-onhold)
+                                 ("WAIT" . +org-todo-onhold)
+                                 ("DONE" . +org-todo-cancel)
+                                 ("KILL" . +org-todo-cancel))))
+
+(add-hook! 'org-mode-hook 'auto-fill-mode)
+
+(add-hook! 'org-mode-hook (lambda (&rest _)
+                            (setq fill-column 80)))
+
+(setq org-roam-directory "~/doc/roam/"
+      org-roam-db-location (concat org-roam-directory ".db"))
+
+(use-package! org-roam-ui
+  :requires websocket
+  :after org-roam
+  :config
+  (setq org-roam-ui-sync-theme t
+        org-roam-ui-follow t
+        org-roam-ui-update-on-save t
+        org-roam-ui-open-on-start t))
+
+;;
+;;; Elisp
+;;
+
+(setq-default flycheck-disabled-checkers '(emacs-lisp-checkdoc))
+
+;;
+;;; Haskell
+;;
+
+(setq lsp-haskell-formatting-provider "ormolu")
+
+;;
+;;; Nix
+;;
+
+(after! lsp-mode
+  (add-to-list 'lsp-language-id-configuration '(nix-mode . "nix"))
+  (lsp-register-client
+   (make-lsp-client :new-connection (lsp-stdio-connection '("rnix-lsp"))
+                    :major-modes '(nix-mode)
+                    :server-id 'nix)))
+
+;;
+;;; YAML
+;;
+
+(add-hook! 'yaml-mode-hook
+  (defun +disable-flycheck-for-yaml-helm-templates ()
+    (when (and buffer-file-name
+              (string-match-p "/templates/" buffer-file-name)
+              (or (string-suffix-p ".yaml" buffer-file-name)
+                  (string-suffix-p ".yml" buffer-file-name)))
+      (remove-hook! 'yaml-mode-local-vars-hook #'lsp!)
+      (pushnew! flycheck-disabled-checkers 'yaml-jsyaml 'yaml-ruby 'yaml-yamllint))))
+
+(setq-hook! 'yaml-mode-hook +format-with-lsp nil)
+
+;;
+;;; Elfeed
+;;
+
+(setq elfeed-db-directory "~/.elfeed"
+      elfeed-enclosure-default-dir (concat elfeed-db-directory "/enclosures")
+      rmh-elfeed-org-files (list (concat elfeed-db-directory "/index.org"))
+      elfeed-goodies/powerline-default-separator nil
+      elfeed-goodies/entry-pane-size 0.75
+      elfeed-goodies/entry-pane-position 'bottom)
+
+;;
+;;; mu4e
+;;
+
+(setq mu4e-root-maildir "~/mail"
+      mu4e-context-policy 'ask-if-none
+      mu4e-compose-context-policy 'always-ask
+      mu4e-compose--org-msg-toggle-next nil
+      mu4e-update-interval 60
+      sendmail-program (executable-find "msmtp")
+      send-mail-function #'smtpmail-send-it
+      message-sendmail-f-is-evil t
+      message-sendmail-extra-arguments '("--read-envelope-from")
+      message-send-mail-function #'message-send-mail-with-sendmail
+      +mu4e-personal-addresses (list "frodo@gondor.net"
+                                     "frodo@rohan.net"
+                                     "azahi@shire.me"
+                                     "admin@shire.me"
+                                     "ceo@shire.me"
+                                     "a.gondor@yahoo.com"
+                                     "a.gondor@yahoo.com"))
+
+(set-email-account! "shire"
+                    '((mu4e-drafts-folder . "/shire/Drafts")
+                      (mu4e-refile-folder . "/shire/Archive")
+                      (mu4e-sent-folder   . "/shire/Sent")
+                      (mu4e-trash-folder  . "/shire/Trash")
+                      (smtpmail-smtp-user . "azahi"))
+                    t)
+
+(set-email-account! "yahoo"
+                    '((mu4e-sent-folder   . "/yahoo/Sent")
+                      (mu4e-drafts-folder . "/yahoo/Drafts")
+                      (mu4e-trash-folder  . "/yahoo/Trash")
+                      (mu4e-refile-folder . "/yahoo/Archive")
+                      (smtpmail-smtp-user . "a.gondor"))
+                    t)
+
+(setq +mu4e-compose-org-msg-toggle-next nil)
+
+;;
+;;; Circe
+;;
+
+(defun nixfiles/irc-bouncer-password-f (&rest _)
+  (+pass-get-secret "server/soju.manwe.shire.me/azahi"))
+
+(set-irc-server! "libera"
+  `(:host "shire.me"
+    :port 6667
+    :user "azahi/libera"
+    :pass nixfiles/irc-bouncer-password-f))
+
+(set-irc-server! "oftc"
+  `(:host "shire.me"
+    :port 6667
+    :user "azahi/oftc"
+    :pass nixfiles/irc-bouncer-password-f))
+
+(set-irc-server! "hackint"
+  `(:host "shire.me"
+    :port 6667
+    :user "azahi/hackint"
+    :pass nixfiles/irc-bouncer-password-f))
+
+(set-irc-server! "rizon"
+  `(:host "shire.me"
+    :port 6667
+    :user "azahi/rizon"
+    :pass nixfiles/irc-bouncer-password-f))
+
+;; (use-package! hledger-mode
+;;   :mode ("\\.journal\\'")
+;;   :hook ((hledger-view-mode . hl-line-mode)
+;;          (hledger-view-mode . center-text-for-reading))
+;;   :init
+;;   (setq hledger-jfile "~/doc/accounting/current.journal")
+;;   :config
+;;   (set-company-backend! 'hledger-mode 'hledger-company)
+;;   (add-hook! 'hledger-mode-hook
+;;     (lambda (&rest _)
+;;       (make-local-variable 'company-backends)
+;;       (add-to-list 'company-backends 'hledger-company))))
+
+;; (use-package! hledger-input
+;;   :hook ((hledger-input-post-commit . hledger-show-new-balances)
+;;          (hledger-input-mode . auto-fill-mode)
+;;          (hledger-input-mode . (lambda (&rest _)
+;;                                  (make-local-variable 'compay-idle-delay)
+;;                                  (setq-local company-idle-delay 0.1))))
+;;   :init
+;;   (setq hledger-input-buffer-height 20))
+
+;; (use-package! kubernetes
+;;   :defer t
+;;   :commands (kubernetes-overview)
+;;   :init (setq kubernetes-poll-frequency 3600
+;;               kubernetes-redraw-frequency 3600))
+
+;; (use-package! kubernetes-evil
+;;   :after kubernetes-overview)
+
+;; (use-package! kubernetes-tramp
+;;   :defer t)
diff --git a/modules/common/emacs/doom/init.el b/modules/common/emacs/doom/init.el
new file mode 100644
index 0000000..cdab069
--- /dev/null
+++ b/modules/common/emacs/doom/init.el
@@ -0,0 +1,119 @@
+(doom! :input
+       ;; japanese
+
+       :completion
+       company
+       vertico
+
+       :ui
+       ;; deft
+       doom
+       ;; doom-dashboard
+       ;; doom-quit
+       ;; emoji
+       hl-todo
+       indent-guides
+       ;; ligatures
+       ;; modeline
+       ;; nav-flash
+       ophints
+       (popup +defaults)
+       ;; tabs
+       ;; unicode
+       (vc-gutter +diff-hl +pretty)
+       window-select
+       workspaces
+
+       :editor
+       (evil +everywhere)
+       file-templates
+       fold
+       format
+       ;; lispy
+       ;; multiple-cursors
+       parinfer
+       ;; rotate-text
+       snippets
+       word-wrap
+
+       :emacs
+       dired
+       ;; electric
+       ibuffer
+       undo
+       vc
+
+       :term
+       eshell
+       vterm
+
+       :checkers
+       syntax
+       (spell +enchant
+              +everywhere
+              +flyspell)
+       grammar
+
+       :tools
+       ansible
+       (debugger +lsp)
+       direnv
+       (docker +lsp)
+       editorconfig
+       (eval +overlay)
+       gist
+       (lookup +dictionary +offline)
+       (lsp +peek)
+       (magit +forge)
+       make
+       (pass +auth)
+       ;; pdf
+       terraform
+       tree-sitter
+       upload
+
+       :os
+       (:if IS-MAC macos)
+       ;; (tty +osc)
+
+       :lang
+       (cc +lsp +tree-sitter)
+       (common-lisp +lsp +tree-sitter)
+       (csharp +lsp +tree-sitter)
+       data
+       (dhall +lsp +tree-sitter)
+       (emacs-lisp +lsp +tree-sitter)
+       (go +lsp +tree-sitter)
+       ;; graphql
+       (:if IS-LINUX (haskell +lsp +tree-sitter))
+       (java +lsp +tree-sitter)
+       (javascript +lsp +tree-sitter)
+       (json +lsp +tree-sitter)
+       (kotlin +lsp +tree-sitter)
+       (latex +lsp +tree-sittter)
+       (markdown +lsp +tree-sitter)
+       (nix +lsp)
+       (org +pandoc +roam2)
+       plantuml
+       (python +lsp +tree-sitter)
+       (:if IS-LINUX (racket +lsp +tree-sitter))
+       ;; rst
+       (rust +lsp +tree-sitter)
+       (:if IS-LINUX (scheme +lsp +tree-sitter +racket))
+       (sh +lsp +tree-sitter)
+       web
+       (yaml +lsp +tree-sitter)
+       (zig +lsp +tree-sitter)
+
+       :email
+       mu4e
+
+       :app
+       calendar
+       ;; emms
+       ;; everywhere
+       irc
+       (rss +org)
+
+       :config
+       (default +bindings +smartparens))
diff --git a/modules/common/emacs/doom/packages.el b/modules/common/emacs/doom/packages.el
new file mode 100644
index 0000000..298a203
--- /dev/null
+++ b/modules/common/emacs/doom/packages.el
@@ -0,0 +1,17 @@
+(disable-packages! writegood-mode)
+
+(unpin! org-roam)
+(package! org-roam
+  :recipe (:host github
+           :repo "org-roam/org-roam"
+           :branch "main"))
+(package! org-roam-ui
+  :recipe (:host github
+           :repo "org-roam/org-roam-ui"
+           :branch "main"))
+
+;; (package! hledger-mode)
+
+;; (package! kubernetes)
+;; (package! kubernetes-evil)
+;; (package! kubernetes-tramp)
diff --git a/modules/common/fonts.nix b/modules/common/fonts.nix
new file mode 100644
index 0000000..483de0d
--- /dev/null
+++ b/modules/common/fonts.nix
@@ -0,0 +1,91 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.fonts;
+in {
+  imports = [
+    (mkAliasOptionModule ["fontScheme"] [
+      "nixfiles"
+      "modules"
+      "fonts"
+      "fontScheme"
+    ])
+  ];
+
+  options.nixfiles.modules.fonts = {
+    enable = mkEnableOption "fonts and their configurations";
+
+    fontScheme = let
+      mkFont = {
+        family,
+        style,
+        size,
+      }: {
+        family = mkOption {
+          description = "Family of the font.";
+          type = types.str;
+          default = family;
+        };
+        style = mkOption {
+          description = "Style of the font.";
+          type = types.str;
+          default = style;
+        };
+        size = mkOption {
+          description = "Size of the font.";
+          type = types.int;
+          default = size;
+        };
+      };
+    in {
+      serifFont = mkFont {
+        family = "Iosevka Etoile";
+        style = "Regular";
+        size = 14;
+      };
+
+      serifFontFallback = mkFont {
+        family = "Sarasa Gothic J";
+        style = "Regular";
+        size = 14;
+      };
+
+      sansSerifFont = mkFont {
+        family = "Iosevka Aile";
+        style = "Regular";
+        size = 14;
+      };
+
+      sansSerifFontFallback = mkFont {
+        family = "Sarasa Gothic J";
+        style = "Regular";
+        size = 14;
+      };
+
+      monospaceFont = mkFont {
+        family = "Iosevka";
+        style = "Regular";
+        size = 14;
+      };
+
+      monospaceFontFallback = mkFont {
+        family = "Sarasa Mono J";
+        style = "Regular";
+        size = 14;
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    fonts.fonts = with pkgs; [
+      iosevka-bin
+      (iosevka-bin.override {variant = "aile";})
+      (iosevka-bin.override {variant = "etoile";})
+      sarasa-gothic
+    ];
+  };
+}
diff --git a/modules/common/git.nix b/modules/common/git.nix
new file mode 100644
index 0000000..2c1dd1f
--- /dev/null
+++ b/modules/common/git.nix
@@ -0,0 +1,117 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.git;
+in {
+  options.nixfiles.modules.git.client.enable =
+    mkEnableOption "Git client";
+
+  config = mkIf cfg.client.enable {
+    hm = {
+      home.packages = with pkgs; [glab hut];
+
+      programs = {
+        git = {
+          enable = true;
+
+          package = pkgs.git.override {
+            doInstallCheck = false;
+            pythonSupport = false;
+            sendEmailSupport = true;
+            withLibsecret = false;
+            withSsh = true;
+          };
+
+          userName = my.fullname;
+          userEmail = my.email;
+          signing = {
+            inherit (my.pgp) key;
+            signByDefault = true;
+          };
+
+          extraConfig =
+            {
+              advice.detachedHead = false;
+              color.ui = true;
+              core.whitespace = "trailing-space";
+              diff = {
+                mnemonicPrefix = true;
+                renames = "copies";
+                submodule = "log";
+              };
+              init.defaultBranch = "master";
+              status.submoduleSummary = true;
+            }
+            // mapAttrs'
+            (n: v: nameValuePair ''url "git@${v}:"'' {insteadOf = "${n}:";}) {
+              "alpine" = "gitlab.alpinelinux.org";
+              "bitbucket" = "bitbucket.com";
+              "codeberg" = "codeberg.org";
+              "freedesktop" = "gitlab.freedesktop.org";
+              "github" = "github.com";
+              "gitlab" = "gitlab.com";
+              "gnome" = "gitlab.gnome.org";
+              "haskell" = "gitlab.haskell.org";
+              "kde" = "invent.kde.org";
+              "notabug" = "notabug.org";
+              "opencode" = "opencode.net";
+              "sourcehut" = "git.sr.ht";
+              "videolan" = "code.videolan.org";
+            };
+
+          aliases = let
+            git = "${config.hm.programs.git.package}/bin/git";
+            curl = "${pkgs.curl}/bin/curl";
+          in {
+            fuck = "!${git} reset --hard && ${git} clean -fdx";
+            gud = ''commit -m "git gud"'';
+            wtc = "!${curl} -sq whatthecommit.com/index.txt | ${git} commit -F -";
+          };
+
+          # All helper tools/editor generated files should go here. This must
+          # be kept relatively clean and void of any project-specific residual
+          # files.
+          ignores = [
+            "*~"
+            ".DS_Store"
+            ".cache/clangd/"
+            ".ccls-cache/"
+            ".dir-locals.el"
+            ".gdb_history"
+            ".netrwhist"
+            ".projectile"
+            "[._]*.s[a-v][a-z]"
+            "[._]*.sw[a-p]"
+            "[._]s[a-rt-v][a-z]"
+            "[._]ss[a-gi-z]"
+            "[._]sw[a-p]"
+            "\#*\#"
+            "compile_commands*.json"
+            "cscope.*"
+            "vgcore.*"
+          ];
+        };
+
+        gh = {
+          enable = true;
+          settings.git_protocol = "ssh";
+        };
+
+        bash = {
+          shellAliases = {
+            gl = "${pkgs.glab}/bin/glab";
+            ht = "${pkgs.hut}/bin/hut";
+          };
+          initExtra = mkAfter ''
+            _complete_alias gl __start_glab glab
+            _complete_alias ht __start_hut hut
+          '';
+        };
+      };
+    };
+  };
+}
diff --git a/modules/common/gnupg.nix b/modules/common/gnupg.nix
new file mode 100644
index 0000000..c0f10f9
--- /dev/null
+++ b/modules/common/gnupg.nix
@@ -0,0 +1,58 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.gnupg;
+in {
+  options.nixfiles.modules.gnupg.enable = mkEnableOption "GnuPG";
+
+  config = mkIf cfg.enable {
+    hm.programs.gpg = {
+      enable = true;
+
+      settings =
+        {
+          display-charset = "utf-8";
+          enable-progress-filter = true;
+          fixed-list-mode = true;
+          keyid-format = "0xlong";
+          no-comments = true;
+          no-emit-version = true;
+          no-greeting = true;
+          with-fingerprint = true;
+          throw-keyids = false;
+
+          use-agent = true;
+
+          armor = true;
+
+          no-random-seed-file = true;
+
+          list-options = "show-uid-validity";
+          verify-options = "show-uid-validity";
+        }
+        // (let
+          cipherAlgos = ["AES256" "AES192" "AES"];
+          digestAlgos = ["SHA512" "SHA384" "SHA256" "SHA224"];
+          compressionAlgos = ["ZLIB" "BZIP2" "ZIP" "Uncompressed"];
+
+          cs = concatStringsSep " ";
+        in {
+          default-preference-list =
+            cs (cipherAlgos ++ digestAlgos ++ compressionAlgos);
+
+          personal-cipher-preferences = cs cipherAlgos;
+          personal-digest-preferences = cs digestAlgos;
+          personal-compress-preferences = cs compressionAlgos;
+
+          s2k-cipher-algo = head cipherAlgos;
+          s2k-digest-algo = head digestAlgos;
+
+          digest-algo = head digestAlgos;
+          cert-digest-algo = head digestAlgos;
+        });
+    };
+  };
+}
diff --git a/modules/common/htop.nix b/modules/common/htop.nix
new file mode 100644
index 0000000..bf3f1e4
--- /dev/null
+++ b/modules/common/htop.nix
@@ -0,0 +1,57 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.htop;
+in {
+  options.nixfiles.modules.htop.enable =
+    mkEnableOption "htop";
+
+  config = mkIf cfg.enable {
+    hm.programs.htop = {
+      enable = true;
+
+      settings = with config.hm.lib.htop; {
+        fields = with fields; [
+          PID
+          USER
+          PRIORITY
+          NICE
+          M_SIZE
+          M_RESIDENT
+          M_SHARE
+          STATE
+          PERCENT_CPU
+          PERCENT_MEM
+          TIME
+          COMM
+        ];
+        account_guest_in_cpu_meter = 1;
+        detailed_cpu_time = 0;
+        enable_mouse = 0;
+        find_comm_in_cmdline = 1;
+        header_margin = 1;
+        hide_function_bar = 1;
+        hide_kernel_threads = 1;
+        hide_userland_threads = 1;
+        highlight_base_name = 1;
+        highlight_changes = 0;
+        highlight_changes_delay_secs = 1;
+        highlight_deleted_exe = 1;
+        highlight_megabytes = 1;
+        highlight_threads = 1;
+        shadow_other_users = 1;
+        show_cpu_frequency = 1;
+        show_cpu_usage = 1;
+        show_program_path = 0;
+        show_thread_names = 0;
+        strip_exe_from_cmdline = 1;
+        tree_view = 1;
+        tree_view_always_by_pid = 1;
+        update_process_names = 1;
+      };
+    };
+  };
+}
diff --git a/modules/common/mpv.nix b/modules/common/mpv.nix
new file mode 100644
index 0000000..afab1dd
--- /dev/null
+++ b/modules/common/mpv.nix
@@ -0,0 +1,135 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.mpv;
+in {
+  options.nixfiles.modules.mpv.enable = mkEnableOption "mpv";
+
+  config = mkIf cfg.enable {
+    hm.programs = {
+      mpv = {
+        enable = true;
+
+        package = with pkgs;
+          wrapMpv mpv-unwrapped {
+            scripts = with mpvScripts; [
+              autoload
+              mpv-autosub
+              sponsorblock
+            ];
+          };
+
+        bindings = {
+          "RIGHT" = "seek 10";
+          "LEFT" = "seek -10";
+          "UP" = "seek 60";
+          "DOWN" = "seek -60";
+
+          "Shift+RIGHT" = "no-osd seek 1 exact";
+          "Shift+LEFT" = "no-osd seek -1 exact";
+          "Shift+UP" = "no-osd seek 5 exact";
+          "Shift+DOWN" = "no-osd seek -5 exact";
+
+          "Alt+k" = "add sub-scale +0.1";
+          "Alt+j" = "add sub-scale -0.1";
+
+          "B" = ''cycle-values background "#000000" "#ffffff"'';
+        };
+
+        profiles = {
+          "protocol.http".force-window = "immediate";
+          "protocol.https".profile = "protocol.http";
+        };
+
+        config = let
+          lang = comcat [
+            "Japanese"
+            "japanese"
+            "jp"
+            "jpn"
+            "jaJP"
+            "ja-JP"
+            "English"
+            "english"
+            "en"
+            "eng"
+            "enUS"
+            "en-US"
+            "Russian"
+            "russian"
+            "ru"
+            "rus"
+            "ruRU"
+            "ru-RU"
+          ];
+        in {
+          audio-display = "no";
+          autofit-larger = "100%x95%";
+          cursor-autohide = 1000;
+          force-seekable = "no";
+          fullscreen = true;
+          msg-color = true;
+          msg-module = true;
+          prefetch-playlist = true;
+          save-position-on-quit = false;
+          screenshot-format = "png";
+          screenshot-template = "%F [%p]";
+          stop-screensaver = true;
+          term-osd-bar = true;
+          use-filedir-conf = true;
+
+          osd-bar-align-y = 0;
+          osd-bar-h = 2;
+          osd-bar-w = 60;
+          osd-border-color = "#FF262626";
+          osd-border-size = 2.5;
+          osd-color = "#FFFFFFFF";
+          osd-duration = 2500;
+          osd-font-size = 40;
+          osd-fractions = true;
+          osd-level = 1;
+          osd-shadow-color = "#33000000";
+
+          osc = false;
+
+          sub-auto = "fuzzy";
+          sub-file-paths-append = "srt";
+          sub-ass-force-margins = true;
+          sub-ass-force-style = "kerning=yes";
+          sub-fix-timing = true;
+          sub-use-margins = true;
+          sub-font-size = 40;
+          sub-color = "#FFFFFFFF";
+          sub-border-color = "#FF262626";
+          sub-border-size = 2.5;
+          sub-shadow-offset = 1;
+          sub-shadow-color = "#33000000";
+          sub-spacing = 0.5;
+          blend-subtitles = true;
+
+          audio-file-auto = "fuzzy";
+          volume = 100;
+          volume-max = 200;
+
+          alang = lang;
+          slang = lang;
+
+          ytdl = true;
+          ytdl-raw-options = ''sub-lang="${lang}",write-sub='';
+        };
+      };
+
+      bash = {
+        shellAliases.cam = "${config.hm.programs.mpv.package}/bin/mpv av://v4l2:/dev/video0";
+
+        initExtra = mkAfter ''
+          _complete_alias cam _mpv mpv
+        '';
+      };
+    };
+  };
+}
diff --git a/modules/common/nmap.nix b/modules/common/nmap.nix
new file mode 100644
index 0000000..73f948c
--- /dev/null
+++ b/modules/common/nmap.nix
@@ -0,0 +1,84 @@
+{
+  config,
+  lib,
+  pkgs,
+  inputs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.nmap;
+in {
+  options.nixfiles.modules.nmap.enable = mkEnableOption "Nmap";
+
+  config = mkIf cfg.enable {
+    hm = {
+      home = {
+        file = {
+          ".nmap/scripts/vulners/vulners.nse".source = "${inputs.nmap-vulners}/vulners.nse";
+          ".nmap/scripts/vulscan/vulscan.nse".source = "${inputs.nmap-vulscan}/vulscan.nse";
+        };
+
+        packages = with pkgs; [nmap nmap-formatter];
+
+        activation.regenerateNmapScripts = with pkgs; ''
+          ${nmap}/bin/nmap --script-updatedb
+        '';
+      };
+
+      programs.bash = {
+        shellAliases = let
+          base = "${pkgs.nmap}/bin/nmap -sV";
+        in {
+          nmap-vulners = "${base} --script=vulners/vulners.nse";
+          nmap-vulscan = "${base} --script=vulscan/vulscan.nse";
+        };
+        initExtra = mkAfter ''
+          _complete_alias nmap-vulners _nmap nmap
+          _complete_alias nmap-vulscan _nmap nmap
+        '';
+      };
+
+      systemd.user = {
+        services.update-nmap-vulscan-lists = {
+          Service = {
+            ExecStart = let
+              pkg = with pkgs;
+                writeShellApplication {
+                  name = "update-nmap-vulscan-lists";
+                  runtimeInputs = [curl];
+                  text = ''
+                    declare -a vulscandbs=(
+                      "cve"
+                      "exploitdb"
+                      "openvas"
+                      "osvdb"
+                      "scipvuldb"
+                      "securityfocus"
+                      "securitytracker"
+                      "xforce"
+                    )
+                    for i in "''${vulscandbs[@]}"; do
+                      ${curl}/bin/curl \
+                        -o "${config.my.home}/.nmap/scripts/vulscan/$i.csv" \
+                        "https://www.computec.ch/projekte/vulscan/download/$i.csv"
+                    done
+                  '';
+                };
+            in "${pkg}/bin/update-nmap-vulscan-lists";
+          };
+        };
+
+        timers.update-nmap-vulscan-lists = {
+          # TODO Figure out how to check for network-online.target for user
+          # timers.
+          Timer = {
+            OnCalendar = "daily";
+            Persistent = true;
+            Unit = "update-nmap-vulscan-lists.service";
+          };
+          Install.WantedBy = ["timers.target"];
+        };
+      };
+    };
+  };
+}
diff --git a/modules/common/openconnect.nix b/modules/common/openconnect.nix
new file mode 100644
index 0000000..780f93f
--- /dev/null
+++ b/modules/common/openconnect.nix
@@ -0,0 +1,83 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.openconnect;
+in {
+  options.nixfiles.modules.openconnect.enable =
+    mkEnableOption "OpenConnect VPN";
+
+  config = mkIf.enable {
+    assertions = [
+      {
+        assertion = config.networking.networkmanager.enable;
+        message = "NetworkManager is required";
+      }
+    ];
+
+    # Spent three days trying to make this work but still getting "No SSO
+    # handler" even on the HEAD version that 100% has SSO support baked in.
+    # It's all so tiresome[1]... aaand KDE is not supported[2].
+    #
+    # I fucking hate AnyConnect, truly an example of how shit is is non-free
+    # software. SAML also sucks balls. I also hate my company for using this
+    # shit, guess I have no other choice but to use the absolute dogshit laptop
+    # they gave me.
+    #
+    # [1]: https://gitlab.gnome.org/GNOME/NetworkManager-openconnect
+    # [1]: https://gitlab.com/openconnect/openconnect/-/issues/424
+    # [2]: https://groups.google.com/g/linux.debian.bugs.dist/c/lK8u-LMY7n4
+    # [2]: https://bugs.kde.org/show_bug.cgi?id=448153
+
+    networking.networkmanager.plugins = with pkgs; [
+      ((networkmanager-openconnect.override {
+          withGnome = false;
+          openconnect = openconnect.overrideAttrs (_: super: {
+            version = "unstable-2022-10-23";
+            src = fetchFromGitLab {
+              owner = "openconnect";
+              repo = "openconnect";
+              rev = "acdfc753f7885b2a539f99036ac41ba1b78cc7ae";
+              hash = "sha256-ub+Z4WFD77h5YMQTb+TLc7EyY2KjBWglF1QVTirCHJM=";
+            };
+          });
+        })
+        .overrideAttrs (_: super: {
+          version = "unstable-2022-09-10";
+          src = fetchFromGitLab {
+            domain = "gitlab.gnome.org";
+            owner = "GNOME";
+            repo = "NetworkManager-openconnect";
+            rev = "3c1590786518e9acca33c250660ad21cae565acd";
+            hash = "sha256-YTUN46QHsHkXPAhImPG/MMLMqjlSRknapVO8u43nnWk=";
+          };
+          buildInputs =
+            super.buildInputs
+            ++ [
+              (webkitgtk_4_1.override {
+                inherit (gnome) libsoup;
+              })
+            ];
+          nativeBuildInputs =
+            super.nativeBuildInputs
+            ++ [
+              autoreconfHook
+            ];
+          postPatch = ''
+            substituteInPlace configure.ac \
+              --replace "PKG_CHECK_MODULES(LIBSECRET, libsecret-1 >= 0.18)" ""
+          '';
+          preAutoreconf = ''
+            autoupdate
+          '';
+          preConfigure = ''
+            NOCONFIGURE=x ./autogen.sh
+            touch gtk4/nm-openconnect-dialog.ui
+          '';
+        }))
+    ];
+  };
+}
diff --git a/modules/common/openssh.nix b/modules/common/openssh.nix
new file mode 100644
index 0000000..4b80809
--- /dev/null
+++ b/modules/common/openssh.nix
@@ -0,0 +1,58 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.openssh;
+in {
+  options.nixfiles.modules.openssh.client.enable =
+    mkEnableOption "OpenSSH client";
+
+  config = mkIf cfg.client.enable {
+    hm = {
+      home.packages = with pkgs; [mosh sshfs];
+
+      programs.ssh = {
+        enable = true;
+
+        hashKnownHosts = true;
+
+        controlMaster = "auto";
+        controlPersist = "24H";
+
+        serverAliveCountMax = 30;
+        serverAliveInterval = 60;
+
+        matchBlocks = let
+          mkBlock = name: {
+            hostname ? name,
+            port ? 22022, # NOTE This is not the default OpenSSH port.
+            user ? my.username,
+            identityFile ? "${config.my.home}/.ssh/${my.username}_${my.ssh.type}",
+            extraAttrs ? {},
+          }:
+            nameValuePair name ({inherit hostname port user identityFile;}
+              // extraAttrs);
+
+          internalServers =
+            mapAttrs' mkBlock
+            (mapAttrs (name: _: {
+                hostname = "${name}.${my.domain.shire}";
+              }) (filterAttrs (_: attr:
+                hasAttr "wireguard" attr
+                && attr.isHeadless)
+              my.configurations));
+        in
+          internalServers
+          // (mapAttrs' mkBlock {
+            gitolite = {
+              user = "git";
+              hostname = "git.${my.domain.shire}";
+            };
+          });
+      };
+    };
+  };
+}
diff --git a/modules/common/password-store.nix b/modules/common/password-store.nix
new file mode 100644
index 0000000..1de8a55
--- /dev/null
+++ b/modules/common/password-store.nix
@@ -0,0 +1,33 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.password-store;
+in {
+  options.nixfiles.modules.password-store.enable =
+    mkEnableOption "the standard UNIX password manager";
+
+  config = mkIf cfg.enable {
+    hm.programs = {
+      password-store = {
+        enable = true;
+
+        package = pkgs.pass.withExtensions (p: with p; [pass-otp]);
+
+        settings.PASSWORD_STORE_DIR = "${config.my.home}/.password-store";
+      };
+
+      # https://github.com/NixOS/nixpkgs/issues/183604
+      bash.initExtra = let
+        completions = "${config.hm.programs.password-store.package}/share/bash-completion/completions";
+      in
+        mkAfter ''
+          source ${completions}/pass-otp
+          source ${completions}/pass
+        '';
+    };
+  };
+}
diff --git a/modules/common/profiles/default.nix b/modules/common/profiles/default.nix
new file mode 100644
index 0000000..06ddaf4
--- /dev/null
+++ b/modules/common/profiles/default.nix
@@ -0,0 +1,90 @@
+{
+  config,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.profiles.default;
+in {
+  imports = [
+    ./dev
+    ./headful.nix
+    ./headless.nix
+    (mkAliasOptionModule ["colourScheme"] [
+      "nixfiles"
+      "modules"
+      "profiles"
+      "default"
+      "colourScheme"
+    ])
+  ];
+
+  options.nixfiles.modules.profiles.default = {
+    enable =
+      mkEnableOption "The most default profile of them all."
+      // {
+        default = true;
+      };
+
+    colourScheme = let
+      mkColour = default:
+        mkOption {
+          type = types.str;
+          inherit default;
+          description = "Colour in a standard hexadecimal notation.";
+          example = "#000000";
+        };
+    in rec {
+      black = mkColour "#161719";
+      red = mkColour "#cc6666";
+      green = mkColour "#b5bd68";
+      yellow = mkColour "#f0c674";
+      blue = mkColour "#81a2be";
+      magenta = mkColour "#b294bb";
+      cyan = mkColour "#8abeb7";
+      white = mkColour "#c5c8c6";
+
+      brightBlack = mkColour "#969896";
+      brightRed = mkColour "#cc6666";
+      brightGreen = mkColour "#b5bd68";
+      brightYellow = mkColour "#f0c674";
+      brightBlue = mkColour "#81a2be";
+      brightMagenta = mkColour "#b294bb";
+      brightCyan = mkColour "#8abeb7";
+      brightWhite = mkColour "#ffffff";
+
+      background = black;
+      foreground = white;
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = !(with this; isHeadless && isHeadful);
+        message = ''
+          The configuration cannot be both "headful" and "headless" at the same
+          time.
+        '';
+      }
+    ];
+
+    nixfiles.modules = {
+      htop.enable = true;
+      tmux.enable = true;
+      vim.enable = true;
+    };
+
+    time.timeZone = "Europe/Moscow";
+
+    environment.systemPackages = with pkgs; [
+      ddrescue
+      file
+      git
+      gnupg
+      tree
+    ];
+  };
+}
diff --git a/modules/common/profiles/dev/containers.nix b/modules/common/profiles/dev/containers.nix
new file mode 100644
index 0000000..cc24ab3
--- /dev/null
+++ b/modules/common/profiles/dev/containers.nix
@@ -0,0 +1,76 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.profiles.dev.containers;
+in {
+  options.nixfiles.modules.profiles.dev.containers.enable =
+    mkEnableOption "Tools for working with containers and container orchestration"
+    // {
+      default = config.nixfiles.modules.profiles.dev.default.enable;
+    };
+
+  config = mkIf cfg.enable {
+    hm = {
+      home = {
+        sessionVariables = {
+          MINIKUBE_IN_STYLE = "false";
+          WERF_DEV = "true";
+          WERF_INSECURE_REGISTRY = "true";
+          WERF_LOG_DEBUG = "true";
+          WERF_LOG_PRETTY = "false";
+          WERF_LOG_VERBOSE = "true";
+          WERF_SYNCHRONIZATION = ":local";
+          WERF_TELEMETRY = "false";
+        };
+
+        packages = with pkgs; [
+          argocd
+          chart-testing
+          clusterctl
+          cmctl
+          datree
+          helm
+          istioctl
+          kubeconform
+          kubectl
+          kubectl-doctor
+          kubectl-images
+          kubectl-tree
+          kubectx
+          kubelogin
+          kubent
+          kubescape
+          kubeseal
+          kubespy
+          minikube
+          skaffold
+          skopeo
+          stern
+          telepresence
+          werf
+        ];
+      };
+
+      programs.bash = {
+        shellAliases = with pkgs; {
+          b = "${buildah}/bin/buildah";
+          h = "${helm}/bin/helm";
+          k = "${kubectl}/bin/kubectl";
+          kns = "${kubectx}/bin/kubens";
+          ktx = "${kubectx}/bin/kubectx";
+        };
+        initExtra = mkAfter ''
+          _complete_alias b _buildah buildah
+          _complete_alias h __start_helm helm
+          _complete_alias k __start_kubectl kubectl
+          _complete_alias kns _kube_namespaces kubens
+          _complete_alias ktx _kube_contexts kubectx
+        '';
+      };
+    };
+  };
+}
diff --git a/modules/common/profiles/dev/default.nix b/modules/common/profiles/dev/default.nix
new file mode 100644
index 0000000..b05aeac
--- /dev/null
+++ b/modules/common/profiles/dev/default.nix
@@ -0,0 +1,89 @@
+{
+  config,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.profiles.dev.default;
+in {
+  imports = [
+    ./containers.nix
+    ./sql.nix
+  ];
+
+  options.nixfiles.modules.profiles.dev.default.enable =
+    mkEnableOption "Catch-all profile for stuff related to software development and etc.";
+
+  config = mkIf cfg.enable {
+    nixfiles.modules = {
+      bat.enable = true;
+      curl.enable = true;
+      direnv.enable = true;
+      git.client.enable = true;
+      gnupg.enable = true;
+      nmap.enable = true;
+      wget.enable = true;
+    };
+
+    hm.home = {
+      file = {
+        ".editorconfig".source = ./editorconfig.ini;
+
+        ".gdbinit".source = ./gdbinit;
+
+        ".ghc/ghci.conf".source = ./ghci.conf;
+
+        ".stack/config.yaml".text = generators.toYAML {} {
+          templates.params = rec {
+            author-name = my.fullname;
+            author-email = my.email;
+            copyright = "Copyright (c) ${author-name} <${author-email}>";
+            github-username = my.username;
+          };
+        };
+
+        ".stack/global-project/stack.yaml".text = generators.toYAML {} {
+          packages = [];
+          resolver = "lts-20.3";
+        };
+      };
+
+      sessionVariables = with config.dirs; rec {
+        CABAL_DIR = "${config.my.home}/.cabal";
+        CABAL_CONFIG = pkgs.writeText "cabal-config" ''
+          repository hackage.haskell.org
+            url: https://hackage.haskell.org/
+            secure: True
+
+          jobs: $ncpus
+
+          remote-repo-cache: ${CABAL_DIR}/packages
+
+          world-file: ${CABAL_DIR}/world
+
+          logs-dir: ${CABAL_DIR}/logs
+          build-summary: ${CABAL_DIR}/logs/build.log
+
+          installdir: ${CABAL_DIR}/bin
+          extra-prog-path: ${CABAL_DIR}/bin
+        '';
+
+        STACK_ROOT = "${config.my.home}/.stack";
+
+        CARGO_HOME = "${config.my.home}/.cargo";
+
+        GOPATH = "${config.my.home}/.go";
+
+        PYTHONSTARTUP = ./pystartup.py;
+      };
+
+      packages = with pkgs; [
+        htmlq
+        jq
+        yq
+      ];
+    };
+  };
+}
diff --git a/modules/common/profiles/dev/editorconfig.ini b/modules/common/profiles/dev/editorconfig.ini
new file mode 100644
index 0000000..17b0317
--- /dev/null
+++ b/modules/common/profiles/dev/editorconfig.ini
@@ -0,0 +1,83 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+insert_final_newline = true
+max_line_length = 80
+trim_trailing_whitespace = true
+
+[*.nix]
+indent_size = 2
+indent_style = space
+
+[*.{S,s,asm}]
+indent_size = 4
+indent_style = tab
+
+[*.{C,H,c,c++,cc,cpp,cxx,h,h++,hh,hpp,hxx}]
+indent_size = 4
+indent_style = tab
+
+[*.{cl,clj,el,l,lisp,lsp,rkt,scm,ss}]
+indent_size = 2
+indent_style = space
+
+[*.go]
+indent_size = 4
+indent_style = tab
+
+[*.{py,pyx}]
+indent_size = 4
+indent_style = space
+
+[*.{hs,lhs}]
+indent_size = 2
+indent_style = space
+
+[*.{html,xhtml,xml}]
+indent_size = 4
+indent_style = tab
+
+[*.json]
+indent_size = 2
+indent_style = space
+
+[*.{yaml,yml}]
+indent_size = 2
+indent_style = space
+
+[*.{toml,tml}]
+indent_size = 4
+indent_style = space
+
+[*.{py,pyx}]
+indent_size = 4
+indent_style = space
+max_line_length = 72
+
+[*.zig]
+indent_size = 4
+indent_style = tab
+
+[configure.ac]
+indent_size = 4
+indent_style = tab
+
+[{Makefile*,*.mk}]
+indent_size = 4
+indent_style = tab
+
+[{CMakeLists.txt,*.cmake}]
+indent_size = 8
+indent_style = tab
+
+[*.tex]
+indent_size = 4
+indent_style = tab
+
+[*.{md,adoc,rtf,txt}]
+indent_size = 4
+indent_style = tab
diff --git a/modules/common/profiles/dev/gdbinit b/modules/common/profiles/dev/gdbinit
new file mode 100644
index 0000000..e266236
--- /dev/null
+++ b/modules/common/profiles/dev/gdbinit
@@ -0,0 +1,41 @@
+set confirm off
+set verbose off
+set editing off
+
+set history expansion on
+
+set height 0
+set width  0
+
+handle SIGALRM nostop print nopass
+handle SIGBUS    stop print nopass
+handle SIGPIPE nostop print nopass
+handle SIGSEGV   stop print nopass
+
+set print address on
+set print elements 0
+set print object on
+set print pretty on
+set print repeats 0
+set print static-members on
+set print vtbl on
+
+set output-radix 10
+
+set demangle-style gnu-v3
+
+set disassembly-flavor intel
+
+alias iv=info variables
+
+alias da=disassemble
+
+define fs
+    finish
+    step
+end
+
+define btc
+    backtrace
+    continue
+end
diff --git a/modules/common/profiles/dev/ghci.conf b/modules/common/profiles/dev/ghci.conf
new file mode 100644
index 0000000..d672167
--- /dev/null
+++ b/modules/common/profiles/dev/ghci.conf
@@ -0,0 +1,35 @@
+:set -XBinaryLiterals
+:set -XFlexibleContexts
+:set -XNoMonomorphismRestriction
+
+:seti -XConstraintKinds
+:seti -XDataKinds
+:seti -XDeriveFunctor
+:seti -XFlexibleInstances
+:seti -XFunctionalDependencies
+:seti -XGADTs
+:seti -XLambdaCase
+:seti -XMagicHash
+:seti -XMultiParamTypeClasses
+:seti -XMultiWayIf
+:seti -XOverloadedLabels
+:seti -XPackageImports
+:seti -XPolyKinds
+:seti -XRankNTypes
+:seti -XScopedTypeVariables
+:seti -XStandaloneDeriving
+:seti -XTupleSections
+:seti -XTypeFamilies
+:seti -XTypeOperators
+:seti -XUndecidableInstances
+
+:set +c
+:set +m
+:set +r
+:set +s
+:set +t
+
+:set prompt      "\ESC[1;34m>\ESC[m\STX "
+:set prompt-cont "\ESC[1;94m|\ESC[m\STX "
+
+:def hoogle \x -> pure (":!hoogle --color --count=10 \"" ++ x ++ "\"")
diff --git a/modules/common/profiles/dev/pystartup.py b/modules/common/profiles/dev/pystartup.py
new file mode 100644
index 0000000..adde66c
--- /dev/null
+++ b/modules/common/profiles/dev/pystartup.py
@@ -0,0 +1,121 @@
+import atexit
+import os
+import readline
+import rlcompleter
+import sys
+from code import InteractiveConsole
+from tempfile import mkstemp
+
+readline.parse_and_bind("tab: complete")
+
+
+class TermColors(dict):
+    color_templates = (
+        ("Normal", "0"),
+        ("Black", "0;30"),
+        ("Red", "0;31"),
+        ("Green", "0;32"),
+        ("Brown", "0;33"),
+        ("Blue", "0;34"),
+        ("Purple", "0;35"),
+        ("Cyan", "0;36"),
+        ("LightGray", "0;37"),
+        ("DarkGray", "1;30"),
+        ("LightRed", "1;31"),
+        ("LightGreen", "1;32"),
+        ("Yellow", "1;33"),
+        ("LightBlue", "1;34"),
+        ("LightPurple", "1;35"),
+        ("LightCyan", "1;36"),
+        ("White", "1;37"),
+    )
+    color_base = "\001\033[%sm\002"
+
+    def __init__(self):
+        self.update(dict([(k, self.color_base % v) for k, v in self.color_templates]))
+
+
+class Completer(object):
+    def save_history(self):
+        import readline
+
+        readline.write_history_file(self.python_histfile)
+
+    def __init__(self):
+        self.python_dir = os.path.expanduser("%s/python" % os.environ["XDG_DATA_HOME"])
+
+        if not os.path.exists(self.python_dir):
+            os.mkdir(self.python_dir)
+
+        self.python_histfile = os.path.expanduser("%s/history" % self.python_dir)
+
+        if os.path.exists(self.python_histfile):
+            readline.read_history_file(self.python_histfile)
+
+        readline.set_history_length(1000)
+        atexit.register(self.save_history)
+
+
+def DisplayHook(value):
+    if value is not None:
+        try:
+            import __builtin__
+
+            __builtin__._ = value
+        except ImportError:
+            __builtins__._ = value
+
+        import pprint
+
+        pprint.pprint(value)
+        del pprint
+
+
+class EditableBufferInteractiveConsole(InteractiveConsole):
+    def __init__(self, *args, **kwargs):
+        self.last_buffer = []
+        InteractiveConsole.__init__(self, *args, **kwargs)
+
+    def runsource(self, source, *args):
+        self.last_buffer = [source.encode("utf-8")]
+        return InteractiveConsole.runsource(self, source, *args)
+
+    def raw_input(self, *args):
+        line = InteractiveConsole.raw_input(self, *args)
+
+        if line == EDIT_CMD:
+            tmp_fd, tmp_file = mkstemp(".py")
+
+            os.write(tmp_fd, b"\n".join(self.last_buffer))
+            os.close(tmp_fd)
+
+            os.system("%s %s" % (EDITOR, tmp_file))
+
+            line = open(tmp_file).read()
+
+            os.unlink(tmp_file)
+            tmp_file = ""
+
+            lines = line.split("\n")
+
+            for i in range(len(lines) - 1):
+                self.push(lines[i])
+
+            line = lines[-1]
+        return line
+
+
+TC = TermColors()
+ps1 = "%sλ%s %s>%s "
+sys.ps1 = ps1 % (TC["Blue"], TC["Normal"], TC["White"], TC["Normal"])
+ps2 = "    %s…%s %s>%s "
+sys.ps2 = ps2 % (TC["Blue"], TC["Normal"], TC["White"], TC["Normal"])
+sys.displayhook = DisplayHook
+
+C = Completer()
+EDITOR = os.environ.get("EDITOR", "vim")
+EDIT_CMD = ":e"
+C = EditableBufferInteractiveConsole(locals=locals())
+C.interact(banner="")
+
+sys.exit()
diff --git a/modules/common/profiles/dev/sql.nix b/modules/common/profiles/dev/sql.nix
new file mode 100644
index 0000000..7a2a09c
--- /dev/null
+++ b/modules/common/profiles/dev/sql.nix
@@ -0,0 +1,101 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.profiles.dev.sql;
+in {
+  options.nixfiles.modules.profiles.dev.sql.enable =
+    mkEnableOption "SQL stuff and database management tools"
+    // {
+      default = config.nixfiles.modules.profiles.dev.default.enable;
+    };
+
+  config = mkIf cfg.enable {
+    hm = {
+      home.packages = with pkgs; [
+        dbeaver
+        pgcli
+        litecli
+      ];
+
+      xdg = let
+        mainSection = {
+          destructive_warning = "True";
+          enable_pager = "True";
+          keyword_casing = "auto";
+          less_chatty = "True";
+          log_file = "/dev/null";
+          log_level = "CRITICAL";
+          multi_line = "False";
+          syntax_style = "default";
+          table_format = "fancy_grid";
+        };
+
+        colorsSection = with config.colourScheme; {
+          "arg-toolbar" = "noinherit bold";
+          "arg-toolbar.text" = "nobold";
+          "bottom-toolbar" = "bg:${black} ${white}";
+          "bottom-toolbar.off" = "bg:${black} ${brightBlack}";
+          "bottom-toolbar.on" = "bg:${black} ${brightWhite}";
+          "bottom-toolbar.transaction.failed" = "bg:${black} ${red} bold";
+          "bottom-toolbar.transaction.valid" = "bg:${black} ${green} bold";
+          "completion-menu.completion" = "bg:${black} ${white}";
+          "completion-menu.completion.current" = "bg:${white} ${black}";
+          "completion-menu.meta.completion" = "bg:${black} ${yellow}";
+          "completion-menu.meta.completion.current" = "bg:${yellow} ${black}";
+          "completion-menu.multi-column-meta" = "bg:${yellow} ${black}";
+          "scrollbar" = "bg:${black}";
+          "scrollbar.arrow" = "bg:${black}";
+          "search" = "bg:${magenta} ${brightWhite}";
+          "search-toolbar" = "noinherit bold";
+          "search-toolbar.text" = "nobold";
+          "search.current" = "bg:${green} ${brightWhite}";
+          "selected" = "bg:${blue} ${brightWhite}";
+          "system-toolbar" = "noinherit bold";
+        };
+
+        mkCliConfig = {
+          name,
+          custom,
+        }: {
+          "${name}/config" = {
+            text = generators.toINI {} {
+              main = mainSection // custom;
+              colors = mapAttrs (_: v: "'${v}'") colorsSection;
+            };
+          };
+        };
+      in {
+        configFile = mkMerge (map mkCliConfig [
+          {
+            name = "pgcli";
+            custom = {
+              auto_expand = "True";
+              casing_file = "/dev/null";
+              expand = "True";
+              history_file = "/dev/null";
+              keyring = "False";
+              multi_line_mode = "psql";
+              on_error = "STOP";
+              prompt = "'\\u@\\h:\\d> '";
+              vi = "True";
+            };
+          }
+          {
+            name = "litecli";
+            custom = {
+              audit_log = "/dev/null";
+              key_bindings = "vi";
+              prompt = "'\\d> '";
+              prompt_continuation = "'-> '";
+              auto_vertical_output = "True";
+            };
+          }
+        ]);
+      };
+    };
+  };
+}
diff --git a/modules/common/profiles/headful.nix b/modules/common/profiles/headful.nix
new file mode 100644
index 0000000..eec14c5
--- /dev/null
+++ b/modules/common/profiles/headful.nix
@@ -0,0 +1,107 @@
+{
+  config,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.profiles.headful;
+in {
+  options.nixfiles.modules.profiles.headful.enable =
+    mkEnableOption "headful profile" // {default = this.isHeadful;};
+
+  config = mkIf cfg.enable {
+    nixfiles.modules = {
+      profiles.dev.default.enable = true;
+
+      alacritty.enable = true;
+      aria2.enable = true;
+      emacs.enable = true;
+      mpv.enable = true;
+      openssh.client.enable = true;
+      password-store.enable = true;
+    };
+
+    hm = {
+      home = {
+        file.".digrc".text = ''
+          +answer
+          +multiline
+          +recurse
+        '';
+
+        packages = with pkgs; [
+          fd
+          ripgrep
+          ripgrep-all
+          sd
+        ];
+      };
+
+      accounts.email = {
+        maildirBasePath = "${config.my.home}/mail";
+
+        accounts = let
+          mkAccount = attrs:
+            mkMerge [
+              {
+                mbsync = {
+                  enable = true;
+                  create = "both";
+                  expunge = "both";
+                  patterns = ["*"];
+                };
+                msmtp.enable = true;
+                mu.enable = true;
+              }
+              attrs
+            ];
+
+          pass = path: "${pkgs.pass}/bin/pass show ${path}";
+        in {
+          shire = mkAccount {
+            address = my.email;
+            gpg = {
+              inherit (my.pgp) key;
+              signByDefault = true;
+              encryptByDefault = false;
+            };
+
+            primary = true;
+
+            imap.host = "shire.me";
+            smtp.host = "shire.me";
+            userName = "azahi@shire.me";
+            passwordCommand = pass "email/shire.me/azahi";
+          };
+
+          yahoo = mkAccount {
+            address = "a.gondor@yahoo.com";
+
+            imap.host = "imap.yahoo.com";
+            smtp.host = "smtp.yahoo.com";
+            userName = "a.gondor@yahoo.com";
+            passwordCommand = pass "email/yahoo.com/a.gondor";
+          };
+        };
+      };
+
+      programs = {
+        mbsync.enable = true;
+        msmtp.enable = true;
+        mu.enable = true;
+      };
+    };
+
+    environment.systemPackages = with pkgs; [
+      (aspellWithDicts (p: with p; [en ru]))
+      arping
+      dnsutils
+      inetutils
+      ldns
+      socat
+      tcpdump
+    ];
+  };
+}
diff --git a/modules/common/profiles/headless.nix b/modules/common/profiles/headless.nix
new file mode 100644
index 0000000..cc7c326
--- /dev/null
+++ b/modules/common/profiles/headless.nix
@@ -0,0 +1,23 @@
+{
+  config,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.profiles.headless;
+in {
+  options.nixfiles.modules.profiles.headless.enable =
+    mkEnableOption "headless profile" // {default = this.isHeadless;};
+
+  config = mkIf cfg.enable {
+    hm.home.file = {
+      ".hushlogin".text = "";
+      ".bash_history".source =
+        config.hm.lib.file.mkOutOfStoreSymlink "/dev/null";
+    };
+
+    environment.systemPackages = with pkgs; [alacritty.terminfo];
+  };
+}
diff --git a/modules/common/qutebrowser.nix b/modules/common/qutebrowser.nix
new file mode 100644
index 0000000..68a41a5
--- /dev/null
+++ b/modules/common/qutebrowser.nix
@@ -0,0 +1,536 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.qutebrowser;
+in {
+  options.nixfiles.modules.qutebrowser.enable = mkEnableOption "Qutebrowser";
+
+  config = mkIf cfg.enable {
+    hm.programs.qutebrowser = with config.nixfiles.modules; {
+      enable = true;
+
+      package = pkgs.qutebrowser.override {
+        withMediaPlayback = false;
+        withPdfReader = false;
+      };
+
+      keyBindings.normal = mkIf mpv.enable {
+        "z" = let
+          mpv = "${config.hm.programs.mpv.package}/bin/mpv";
+        in "hint links spawn --detach ${mpv} {hint-url}";
+      };
+
+      searchEngines = rec {
+        aliexpress = "https://www.aliexpress.com/wholesale?SearchText={}";
+        ansible = "https://galaxy.ansible.com/search?keywords={}";
+        arch = "https://wiki.archlinux.org/?search={}";
+        crates = "https://crates.io/search?q={}";
+        crawl = "http://crawl.chaosforge.org/index.php?search={}";
+        discogs = "https://www.discogs.com/search/?q={}";
+        dockerdocs = "https://docs.docker.com/search/?q={}";
+        dockerhub = "https://hub.docker.com/search?q={}";
+        doublegis = "https://2gis.ru/search/{}";
+        duckduckgo = "https://duckduckgo.com/?q={}'";
+        dwarffortress = "https://dwarffortresswiki.org/index.php?search={}";
+        ebay = "https://www.ebay.com/sch/i.html?_nkw={}";
+        ecosia = "https://www.ecosia.org/search?q={}";
+        factorio = "https://wiki.factorio.com/index.php?search={}";
+        genius = "https://genius.com/search?q={}";
+        github = "https://github.com/search?q={}";
+        godocs = "https://godocs.io/?q={}";
+        gogdb = "https://www.gogdb.org/products?search={}";
+        google = "https://www.google.com/search?q={}";
+        google-images = "https://www.google.com/search?q={}&tbm=isch";
+        gopkgs = "https://pkg.go.dev/search?q={}";
+        habr = "https://habr.com/ru/search/?q={}";
+        hackage = "https://hackage.haskell.org/packages/search?terms={}";
+        hackernews = "https://hn.algolia.com/?q={}";
+        headhunter = "https://hh.ru/search/vacancy?st=searchVacancy&text={}";
+        hoogle = "https://hoogle.haskell.org/?hoogle={}";
+        jisho = "https://jisho.org/search/{}";
+        kotobank = "https://kotobank.jp/gs/?q={}";
+        kubernetes = "https://kubernetes.io/search/?q={}";
+        lastfm = "https://www.last.fm/search?q={}";
+        lobsters = "https://lobste.rs/search?q=test{}";
+        mdn = "https://developer.mozilla.org/en-US/search?q={}";
+        melpa = "https://melpa.org/#/?q={}";
+        moddb = "https://www.moddb.com/search?q={}";
+        musicbrainz = "https://musicbrainz.org/search?query={}";
+        nix-issues = "https://github.com/NixOS/nix/issues?q={}";
+        nix-prs = "https://github.com/NixOS/nix/pulls?q={}";
+        nixos-flakes = "https://search.nixos.org/flakes?query={}";
+        nixos-options = "https://search.nixos.org/options?query={}";
+        nixos-packages = "https://search.nixos.org/packages?query={}";
+        nixos-wiki = "https://nixos.wiki/index.php?search={}";
+        nixpkgs-issues = "https://github.com/NixOS/nixpkgs/issues?q={}";
+        nixpkgs-prs = "https://github.com/NixOS/nixpkgs/pulls?q={}";
+        openstreetmap = "https://www.openstreetmap.org/search?query={}";
+        ozon = "https://www.ozon.ru/search/?text={}";
+        protondb = "https://www.protondb.com/search?q={}";
+        pypi = "https://pypi.org/search/?q={}";
+        pythondocs = "https://docs.python.org/3/search.html?q={}";
+        rateyourmusic = "https://rateyourmusic.com/search?searchterm={}";
+        riichi = "https://riichi.wiki/index.php?search={}";
+        rustdoc = "https://doc.rust-lang.org/std/?search={}";
+        searx = "https://searx.tiekoetter.com/search?q={}";
+        slashdot = "https://slashdot.org/index2.pl?fhfilter={}";
+        sourcehut = "https://sr.ht/projects?search={}";
+        steam = "https://store.steampowered.com/search/?term={}";
+        steamdb = "https://steamdb.info/search/?a=app&q={}";
+        ubuntu = "https://wiki.ubuntu.com/Home?action=fullsearch&value={}";
+        wikipedia-en = "https://en.wikipedia.org/w/index.php?search={}";
+        wikipedia-ru = "https://ru.wikipedia.org/w/index.php?search={}";
+        wikipedia-ja = "https://ja.wikipedia.org/w/index.php?search={}";
+        wolphramalpha = "https://www.wolframalpha.com/input/?i={}";
+        yahoo = "https://yahoo.com/search/?text={}";
+        yahoo-images = "https://yahoo.com/images/search?text={}";
+        yahoo-market = "https://market.yahoo.com/search?text={}";
+        youtube = "https://yewtu.be/search?q={}";
+
+        aw = arch;
+        d = duckduckgo;
+        do = dockerhub;
+        docker = dockerhub;
+        dod = dockerdocs;
+        g = google;
+        gh = github;
+        h = hoogle;
+        k = kubernetes;
+        mb = musicbrainz;
+        n = nixos-options;
+        nw = nixos-wiki;
+        py = pypi;
+        pyd = pythondocs;
+        rym = rateyourmusic;
+        s = searx;
+        sh = sourcehut;
+        sr = sourcehut;
+        w = wikipedia-en;
+        wen = wikipedia-en;
+        wja = wikipedia-ja;
+        wru = wikipedia-ru;
+        y = yahoo;
+        yt = youtube;
+      };
+
+      settings = {
+        changelog_after_upgrade = "never";
+
+        content = {
+          autoplay = false;
+          cookies.accept = "all";
+          default_encoding = "utf-8";
+          desktop_capture = "ask";
+          dns_prefetch = false;
+          geolocation = false;
+          headers.do_not_track = true;
+          javascript.enabled = true;
+          prefers_reduced_motion = true;
+          webgl = true;
+
+          blocking = {
+            enabled = true;
+            method = "adblock";
+            adblock.lists = [
+              "https://easylist.to/easylist/easylist.txt"
+              "https://easylist.to/easylist/easyprivacy.txt"
+              "https://easylist.to/easylist/fanboy-social.txt"
+              "https://secure.fanboy.co.nz/fanboy-annoyance.txt"
+              "https://secure.fanboy.co.nz/fanboy-cookiemonster.txt"
+            ];
+          };
+        };
+
+        completion = {
+          height = "50%";
+          show = "auto";
+          shrink = true;
+          timestamp_format = "%y-%m-%d";
+          min_chars = 3;
+          open_categories = ["bookmarks" "quickmarks" "history"];
+
+          scrollbar = {
+            width = 0;
+            padding = 0;
+          };
+        };
+
+        downloads = {
+          location = {
+            directory = config.userDirs.download;
+            prompt = true;
+          };
+          remove_finished = 0;
+        };
+
+        editor.command = [
+          (
+            if alacritty.enable
+            then "${pkgs.alacritty}/bin/alacritty"
+            else "${pkgs.xterm}/bin/xterm"
+          )
+          "-e"
+          "${config.programs.vim.package}/bin/vim"
+          "-f"
+          "{}"
+        ];
+
+        hints = {
+          auto_follow = "unique-match";
+          auto_follow_timeout = 0;
+          border = "0px";
+          min_chars = 1;
+          scatter = false;
+          uppercase = false;
+        };
+
+        hints.radius = 0;
+        keyhint.radius = 0;
+        prompt.radius = 0;
+
+        scrolling = {
+          bar = "never";
+          smooth = false;
+        };
+
+        spellcheck.languages = ["en-US" "en-GB" "ru-RU"];
+
+        statusbar.position = "bottom";
+
+        tabs = {
+          position = "top";
+
+          title = {
+            alignment = "left";
+            format = "{audio}{index} : {current_title}";
+            format_pinned = "{audio}{index}";
+          };
+
+          min_width = -1;
+          max_width = -1;
+
+          indicator.width = 0;
+
+          pinned = {
+            shrink = true;
+            frozen = false;
+          };
+
+          close_mouse_button = "middle";
+          mousewheel_switching = false;
+
+          background = true;
+          select_on_remove = "next";
+          new_position = {
+            related = "next";
+            unrelated = "last";
+          };
+
+          favicons = {
+            show = "pinned";
+            scale = 0.75;
+          };
+        };
+
+        url = rec {
+          default_page = "about:blank";
+          start_pages = [default_page];
+        };
+
+        window = {
+          hide_decoration = false;
+          title_format = "{perc}{current_title}{title_sep}qutebrowser";
+        };
+
+        colors = with config.colourScheme; {
+          completion = rec {
+            fg = white;
+            match.fg = red;
+            odd.bg = black;
+            even.bg = odd.bg;
+            category = {
+              fg = white;
+              bg = black;
+              border = {
+                top = black;
+                bottom = black;
+              };
+            };
+            item.selected = {
+              fg = black;
+              bg = white;
+              border = {
+                top = white;
+                bottom = white;
+              };
+            };
+            scrollbar = {
+              fg = white;
+              bg = black;
+            };
+          };
+          contextmenu = {
+            menu = {
+              fg = white;
+              bg = black;
+            };
+            selected = {
+              fg = black;
+              bg = white;
+            };
+            disabled = {
+              fg = brightBlack;
+              bg = black;
+            };
+          };
+          downloads = {
+            bar.bg = black;
+            start = {
+              fg = green;
+              bg = black;
+            };
+            stop = {
+              fg = yellow;
+              bg = black;
+            };
+            error = {
+              fg = red;
+              bg = black;
+            };
+            system = {
+              fg = "none";
+              bg = "none";
+            };
+          };
+          hints = {
+            fg = white;
+            match.fg = red;
+            bg = black;
+          };
+          keyhint = {
+            fg = white;
+            suffix.fg = red;
+            bg = black;
+          };
+          messages = {
+            error = rec {
+              bg = black;
+              fg = red;
+              border = bg;
+            };
+            info = rec {
+              fg = blue;
+              bg = black;
+              border = bg;
+            };
+            warning = rec {
+              fg = yellow;
+              bg = black;
+              border = bg;
+            };
+          };
+          prompts = rec {
+            fg = white;
+            bg = black;
+            selected = {
+              fg = black;
+              bg = white;
+            };
+            border = bg;
+          };
+          statusbar = {
+            normal = {
+              bg = black;
+              fg = white;
+            };
+            command = {
+              bg = black;
+              fg = white;
+            };
+            insert = {
+              bg = green;
+              fg = black;
+            };
+            passthrough = {
+              bg = blue;
+              fg = black;
+            };
+            private = {
+              bg = magenta;
+              fg = black;
+            };
+            url = {
+              fg = blue;
+              hover.fg = brightBlue;
+              success = {
+                http.fg = brightGreen;
+                https.fg = brightGreen;
+              };
+              warn.fg = brightYellow;
+              error.fg = brightRed;
+            };
+          };
+          tabs = rec {
+            bar.bg = black;
+            even = {
+              bg = black;
+              fg = white;
+            };
+            odd = with even; {inherit bg fg;};
+            selected = rec {
+              even = {
+                bg = white;
+                fg = black;
+              };
+              odd = with even; {inherit bg fg;};
+            };
+            pinned = rec {
+              even = {
+                bg = brightBlack;
+                fg = brightWhite;
+              };
+              odd = with even; {inherit bg fg;};
+            };
+            indicator = {
+              start = green;
+              stop = yellow;
+              error = red;
+              system = "none";
+            };
+          };
+          webpage = {
+            bg = "white";
+            darkmode = {
+              enabled = false;
+              algorithm = "lightness-cielab";
+              contrast = 0.0;
+              grayscale = {
+                all = false;
+                images = 0.0;
+              };
+              policy = {
+                images = "smart";
+                page = "smart";
+              };
+              threshold = {
+                background = 0;
+                text = 256;
+              };
+            };
+            preferred_color_scheme = "auto";
+          };
+        };
+
+        fonts =
+          (with config.fontScheme.monospaceFont; {
+            default_family = family;
+            default_size = (toString size) + "pt";
+          })
+          // {
+            web = with config.fontScheme; {
+              family = rec {
+                standard = sans_serif;
+                fixed = monospaceFont.family;
+                serif = serifFont.family;
+                sans_serif = sansSerifFont.family;
+                cursive = null;
+                fantasy = null;
+              };
+              size = rec {
+                default = sansSerifFont.size;
+                default_fixed = monospaceFont.size;
+                minimum = 0;
+                minimum_logical = default / 2;
+              };
+            };
+          }
+          // mapListToAttrs (_: "default_size default_family") [
+            "completion.category"
+            "completion.entry"
+            "contextmenu"
+            "debug_console"
+            "downloads"
+            "hints"
+            "keyhint"
+            "messages.error"
+            "messages.info"
+            "messages.warning"
+            "prompts"
+            "statusbar"
+          ];
+
+        qt = mkIf kde.enable {
+          force_platform = null;
+          force_platformtheme = "KDE";
+        };
+      };
+
+      extraConfig =
+        (let
+          mkPaddingDictionary = {
+            name,
+            bottom,
+            left,
+            right,
+            top,
+          }: let
+            n = "c.${name}.padding";
+            b = "'bottom': ${toString bottom}";
+            l = "'left': ${toString left}";
+            r = "'right': ${toString right}";
+            t = "'top': ${toString top}";
+          in "${n} = {${b}, ${l}, ${r}, ${t}}";
+
+          final = map mkPaddingDictionary [
+            {
+              name = "hints";
+              bottom = 3;
+              left = 3;
+              right = 3;
+              top = 3;
+            }
+            {
+              name = "statusbar";
+              bottom = 1;
+              left = 0;
+              right = 3;
+              top = 1;
+            }
+            {
+              name = "tabs";
+              bottom = 1;
+              left = 6;
+              right = 6;
+              top = 1;
+            }
+          ];
+        in
+          concatStringsSep "\n" final + "\n")
+        + (let
+          allowSetting = setting: url: "config.set('content.${setting}', True, '${url}')";
+
+          allowMediaCaptureSetting = url: [
+            (allowSetting "desktop_capture" url)
+            (allowSetting "media.audio_video_capture" url)
+          ];
+          allowedMediaCapture = flatten (map allowMediaCaptureSetting [
+            "https://discord.com"
+            "https://web.telegram.org"
+          ]);
+
+          allowNotificationsSetting = allowSetting "notifications.enabled";
+          allowedNotifications = map allowNotificationsSetting [
+            "https://discord.com"
+            "https://web.telegram.org"
+          ];
+
+          final = allowedMediaCapture ++ allowedNotifications;
+        in
+          concatStringsSep "\n" final + "\n");
+    };
+  };
+}
diff --git a/modules/common/subversion.nix b/modules/common/subversion.nix
new file mode 100644
index 0000000..077f449
--- /dev/null
+++ b/modules/common/subversion.nix
@@ -0,0 +1,52 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.subversion;
+in {
+  options.nixfiles.modules.subversion.enable = mkEnableOption "Subversion";
+
+  config = mkIf cfg.enable {
+    nixfiles.modules.gnupg.enable = true;
+
+    hm.home = {
+      file = {
+        ".subversion/config".text = generators.toINI {} {
+          auth = {
+            password-stores = "gpg-agent";
+            ssl-client-cert-file-prompt = "no";
+            store-passwords = "yes";
+            store-auth-creds = "yes";
+          };
+          helpers = {
+            editor-cmd = "${config.programs.vim.package}/bin/vim";
+            diff-cmd = "${pkgs.colordiff}/bin/colordiff";
+          };
+          miscellany = {
+            global-ignores = with config.hm.programs.git;
+              optionalString (ignores != []) (concatStringsSep " " ignores);
+            diff-ignore-content-type = "no";
+          };
+          working-copy = {
+            exclusive-locking-clients = "svn";
+            exclusive-locking = true;
+            busy-timeout = 10000;
+          };
+        };
+
+        ".subversion/servers".text = generators.toINI {} {
+          global = {
+            store-auth-creds = "yes";
+            store-passwords = "yes";
+            store-plaintext-passwords = "yes";
+          };
+        };
+      };
+
+      packages = with pkgs; [(subversionClient.override {saslSupport = true;})];
+    };
+  };
+}
diff --git a/modules/common/tmux.nix b/modules/common/tmux.nix
new file mode 100644
index 0000000..e978f72
--- /dev/null
+++ b/modules/common/tmux.nix
@@ -0,0 +1,60 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.tmux;
+in {
+  options.nixfiles.modules.tmux.enable =
+    mkEnableOption "tmux";
+
+  config = mkIf cfg.enable {
+    hm.programs.tmux = {
+      enable = true;
+
+      aggressiveResize = true;
+      baseIndex = 1;
+      clock24 = true;
+      disableConfirmationPrompt = true;
+      escapeTime = 0;
+      historyLimit = 50000;
+      newSession = true;
+      resizeAmount = 10;
+      terminal = "screen-256color";
+
+      extraConfig = ''
+        set -g set-titles on
+
+        set -g status-left  ""
+        set -g status-right ""
+
+        set -g detach-on-destroy off
+
+        set -g status-keys emacs
+        set -g mode-keys   vi
+
+        bind h select-pane -L
+        bind j select-pane -D
+        bind k select-pane -U
+        bind l select-pane -R
+
+        bind -r H resize-pane -L 10
+        bind -r J resize-pane -D 10
+        bind -r K resize-pane -U 10
+        bind -r L resize-pane -R 10
+
+        bind < swap-pane -D
+        bind > swap-pane -U
+
+        bind , swap-window -t -1
+        bind . swap-window -t +1
+
+        bind Tab last-window
+
+        bind _ split-window -v
+        bind | split-window -h
+      '';
+    };
+  };
+}
diff --git a/modules/common/vim/default.nix b/modules/common/vim/default.nix
new file mode 100644
index 0000000..0328e10
--- /dev/null
+++ b/modules/common/vim/default.nix
@@ -0,0 +1,50 @@
+{
+  config,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.vim;
+in {
+  options.nixfiles.modules.vim.enable = mkEnableOption "Vim";
+
+  config = mkIf cfg.enable {
+    programs.vim.package = with pkgs;
+      (vim_configurable.override {
+        features = "normal";
+        cscopeSupport = false;
+        darwinSupport = pkgs.stdenv.isDarwin;
+        guiSupport = false;
+        luaSupport = false;
+        multibyteSupport = false;
+        netbeansSupport = false;
+        nlsSupport = false;
+        perlSupport = false;
+        pythonSupport = false;
+        rubySupport = false;
+        tclSupport = false;
+        ximSupport = false;
+      })
+      .customize {
+        name = "vim";
+        vimrcConfig = {
+          customRC = readFile ./rc.vim;
+          packages.myVimPackage.start = with vimPlugins; [
+            editorconfig-vim
+            vim-eunuch
+            vim-nix
+            vim-sleuth
+            vim-surround
+            vim-unimpaired
+          ];
+        };
+      };
+
+    environment = {
+      systemPackages = [config.programs.vim.package];
+      variables.EDITOR = mkOverride 100 "vim";
+    };
+  };
+}
diff --git a/modules/common/vim/rc.vim b/modules/common/vim/rc.vim
new file mode 100644
index 0000000..945643a
--- /dev/null
+++ b/modules/common/vim/rc.vim
@@ -0,0 +1,253 @@
+set nocompatible
+
+let $VIMFILES = expand('<sfile>:p:h')
+
+let g:skip_defaults_vim = 1
+
+let g:netrw_dirhistmax = 0
+
+set autoread
+set backspace=indent,eol,start
+
+set clipboard=unnamedplus
+set diffopt+=iwhite
+set hidden
+set history=256
+set lazyredraw
+set mouse=
+set path+=**
+set tabpagemax=50
+set viminfo=
+
+set cmdheight=1
+set display+=lastline
+set fillchars=vert:\ "
+set laststatus=2
+set modeline
+set noshowmode
+set ruler
+set shortmess+=I
+set textwidth=0
+set title
+
+set sessionoptions-=options
+set viewoptions-=options
+
+set noerrorbells
+set novisualbell
+
+set splitbelow
+set splitright
+
+set complete=
+set complete+=.
+set complete+=b
+set complete+=t
+set completeopt=
+set completeopt+=menu
+set completeopt+=longest
+
+set gdefault
+set hlsearch
+set incsearch
+set iskeyword+=-
+set magic
+
+set foldmethod=marker
+set nofoldenable
+
+set shortmess=
+set shortmess+=I
+set shortmess+=T
+set shortmess+=a
+set shortmess+=c
+set shortmess+=t
+
+set nolist
+set nowrap
+
+set scrolloff=10
+set sidescrolloff=10
+
+set number
+if v:version >= 700
+    set numberwidth=3
+endif
+
+set wildignorecase
+set wildmenu
+set wildignore=
+
+set nobackup
+set noswapfile
+set noundofile
+set nowritebackup
+
+set smartcase
+set ignorecase
+
+set autoindent
+set breakindent
+set smartindent
+
+set expandtab
+set shiftround
+set shiftwidth=4
+set smarttab
+set softtabstop=4
+set tabstop=4
+
+autocmd BufEnter *.* :set colorcolumn=
+
+if &t_Co == 8 && $TERM !~# '^Eterm'
+    set t_Co=16
+endif
+
+if &listchars ==# 'eol:$'
+    set listchars=tab:>\ ,trail:-,extends:>,precedes:<,nbsp:+
+endif
+
+if v:version > 703 || v:version == 703 && has("patch541")
+    set formatoptions+=j
+endif
+
+if has('path_extra')
+    setglobal tags-=./tags tags-=./tags; tags^=./tags;
+endif
+
+if !has('nvim') && &ttimeoutlen == -1
+    set ttimeout
+    set ttimeoutlen=100
+endif
+
+try
+    set encoding=utf-8
+    scriptencoding utf-8
+catch
+endtry
+
+try
+    if &fileencodings !~? "utf-8"
+        let g:added_fenc_utf8 = 1
+        set fileencodings+=utf-8
+    endif
+catch
+endtry
+
+if has('autocmd')
+    filetype plugin indent on
+
+    if exists("+omnifunc")
+        autocmd Filetype *
+                    \ if &omnifunc == "" |
+                    \ setlocal omnifunc=syntaxcomplete#Complete |
+                    \ endif
+    endif
+
+    autocmd BufEnter * set noreadonly
+endif
+
+if has('syntax') && !exists('g:syntax_on')
+    syntax enable
+endif
+
+if !exists('g:loaded_matchit') && findfile('plugin/matchit.vim', &rtp) ==# ''
+    runtime! macros/matchit.vim
+endif
+
+nnoremap        <SPACE>     <nop>
+let mapleader=" "
+
+nnoremap        :W          :w
+nnoremap        :W!         :w!
+nnoremap        :Q          :q
+nnoremap        :Q!         :q!
+
+nnoremap <Expr> j           v:count ? 'j' : 'gj'
+nnoremap <Expr> k           v:count ? 'k' : 'gk'
+
+nnoremap        J           gt
+nnoremap        K           gT
+
+nnoremap        <C-a>       ^h
+vnoremap        <C-a>       ^h
+nnoremap        H           ^h
+vnoremap        H           ^h
+
+nnoremap        <C-e>       $
+vnoremap        <C-e>       $
+nnoremap        L           $
+vnoremap        L           $
+
+nnoremap        N           Nzzzv
+nnoremap        n           nzzzv
+
+inoremap        <C-u>       <C-g>u<C-u>
+inoremap        <C-w>       <C-g>u<C-w>
+
+vnoremap        <           <gv
+vnoremap        >           >gv
+vnoremap        <Tab>       >gv
+vnoremap        <S-Tab>     <gv
+nnoremap        <Tab>       >>_
+nnoremap        <S-Tab>     <<_
+
+nnoremap        ]b          :<C-u>bnext<CR>
+nnoremap        [b          :<C-u>bprevious<CR>
+
+nnoremap        <C-h>       <C-w>h
+nnoremap        <C-j>       <C-w>j
+nnoremap        <C-k>       <C-w>k
+nnoremap        <C-l>       <C-w>l
+
+nnoremap        *           /\<<C-r>=expand('<cword>')<CR>\><CR>
+nnoremap        #           ?\<<C-r>=expand('<cword>')<CR>\><CR>
+
+nnoremap        <C-L>       :<C-u>nohlsearch<C-r>=has('diff')?'<Bar>diffupdate':''<CR><CR><C-l>
+
+cnoremap        ;/          <C-r>=expand('%:p:h').'/'<CR>
+cnoremap        ;;          <C-r>=expand('%:t')<CR>
+cnoremap        ;.          <C-r>=expand('%:p:r')<CR>
+
+nnoremap        <Leader>.   :<C-u>lcd %:p:h<CR>
+
+nnoremap        Q           @q
+
+nnoremap        <Leader>c   ^v$h
+nnoremap        <Leader>v   ggVG
+
+nnoremap        <Leader>y   "+y
+nnoremap        <Leader>Y   "+Y
+
+nnoremap        <Leader>p   "+p
+nnoremap        <Leader>P   "+P
+
+inoremap        <C-v>       <C-c>"+pi
+cnoremap        <C-v>       <C-r>+
+
+nmap            <Leader>w   :<C-u>w!<CR>
+nmap            <Leader>wq  :<C-u>wq!<CR>
+
+nnoremap        ZX          :<C-u>qa!<CR>
+
+nnoremap        <Leader>q   :<C-u>q<CR>
+
+command         WS          w !sudo tee "%" >/dev/null
+
+function! SwitchCase()
+    normal! ~
+    if strlen(getline('.')) != virtcol('.')
+        normal! h
+    endif
+endfunction
+nnoremap        ~           :<C-u>call SwitchCase()<CR>
+
+function! s:GM()
+    execute 'normal! ^'
+    let first_col = virtcol('.')
+    execute 'normal! g_'
+    let last_col  = virtcol('.')
+    execute 'normal! ' . (first_col + last_col) / 2 . '|'
+endfunction
+nnoremap        gm          :<C-u>call <SID>GM()<CR>
+onoremap        gm          :<C-u>call <SID>GM()<CR>
diff --git a/modules/common/vscode.nix b/modules/common/vscode.nix
new file mode 100644
index 0000000..6671973
--- /dev/null
+++ b/modules/common/vscode.nix
@@ -0,0 +1,167 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.vscode;
+in {
+  options.nixfiles.modules.vscode = {
+    enable = mkEnableOption "VSCode";
+
+    package = with pkgs;
+      mkOption {
+        type = types.enum [vscodium vscode vscode-fhs];
+        default = vscodium;
+        description = "Which package to use as a VSCode implementation.";
+      };
+
+    vim.enable = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Whether to enable Vim emulation.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    hm.programs.vscode = with config.nixfiles;
+    with modules;
+    with profiles; {
+      enable = true;
+
+      inherit (cfg) package;
+
+      extensions = with pkgs;
+      with vscode-extensions;
+        [
+          editorconfig.editorconfig
+          file-icons.file-icons
+          gitlab.gitlab-workflow
+          ms-kubernetes-tools.vscode-kubernetes-tools
+          redhat.vscode-xml
+          redhat.vscode-yaml
+          streetsidesoftware.code-spell-checker
+        ]
+        ++ optional cfg.vim.enable vscodevim.vim;
+
+      userSettings = let
+        font = config.fontScheme.monospaceFont;
+        fontFamily = font.family;
+        fontSize = font.size;
+      in {
+        editor =
+          {
+            inherit fontFamily fontSize;
+            inlayHints = {inherit fontFamily fontSize;};
+            codeLens = false;
+            cursorStyle = "block";
+            detectIndentation = true;
+            minimap.enabled = false;
+            renderWhitespace = "trailing";
+            rulers = [80 120];
+            smoothScrolling = false;
+            tabCompletion = "on";
+          }
+          // (let
+            surround = 10;
+          in {
+            cursorSurroundingLines = surround;
+            scrollBeyondLastColumn = surround;
+          });
+
+        keyboard.dispatch = "keyCode";
+
+        diffEditor.codeLens = false;
+
+        files = {
+          autoSave = "off";
+          enableTrash = false;
+        };
+
+        workbench = {
+          activityBar.visible = false;
+          editor.highlightModifiedTabs = true;
+          enableExperiments = false;
+          settings.enableNaturalLanguageSearch = false;
+          startupEditor = "none";
+          tips.enabled = false;
+          tree.indent = 4;
+          welcomePage = {
+            walkthroughs.openOnInstall = false;
+            preferReducedMotion = true;
+          };
+        };
+
+        debug.console = {inherit fontFamily fontSize;};
+
+        scm = {
+          inputFontFamily = fontFamily;
+          inputFontSize = fontSize;
+        };
+
+        extensions = {
+          autoCheckUpdates = false;
+          autoUpdate = false;
+          ignoreRecommendations = true;
+        };
+
+        terminal = {
+          external.linuxExec =
+            if alacritty.enable
+            then "${pkgs.alacritty}/bin/alacritty"
+            else "${pkgs.xterm}/bin/xterm}";
+
+          integrated = {
+            inherit fontFamily fontSize;
+            enableBell = true;
+          };
+        };
+
+        update = {
+          mode = "none";
+          showReleaseNotes = false;
+        };
+
+        telemetry = {
+          enableCrashReporter = false;
+          enableTelemetry = false;
+        };
+
+        security.workspace.trust.enabled = false;
+
+        git.allowForcePush = true;
+
+        vim = let
+          applyInputMethod = {
+            "ibus" = let
+              bin = "${pkgs.ibus}/bin/ibus";
+            in {
+              enable = true;
+              defaultIM = "xkb:us::eng";
+              obtainIMCmd = "${bin} engine";
+              switchIMCmd = "${bin} engine {im}";
+            };
+            "fcitx" = let
+              bin = "${pkgs.fcitx}/bin/fcitx-remote";
+            in {
+              enable = true;
+              defaultIM = "1";
+              obtainIMCmd = bin;
+              switchIMCmd = "${bin} -t {im}";
+            };
+          };
+        in
+          mkIf cfg.vim.enable rec {
+            easymotion = true;
+            easymotionMarkerFontFamily = fontFamily;
+            easymotionMarkerFontSize = fontSize;
+
+            leader = " ";
+
+            useSystemClipboard = true;
+          };
+      };
+    };
+  };
+}
diff --git a/modules/common/wget.nix b/modules/common/wget.nix
new file mode 100644
index 0000000..9a16fcc
--- /dev/null
+++ b/modules/common/wget.nix
@@ -0,0 +1,35 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.wget;
+in {
+  options.nixfiles.modules.wget.enable = mkEnableOption "wget";
+
+  config = mkIf cfg.enable {
+    hm = {
+      programs.bash.shellAliases.wget = "${pkgs.wget}/bin/wget --hsts-file=/tmp/wget-hsts";
+
+      home.sessionVariables.WGETRC = pkgs.writeText "wgetrc" ''
+        adjust_extension = on
+        dirstruct = off
+        follow_ftp = on
+        passive_ftp = off
+        progress = bar
+        quota = inf
+        reclevel = 5
+        recursive = off
+        robots = off
+        timestamping = off
+        tries = 5
+        wait = 0
+        waitretry = 10
+      '';
+    };
+
+    environment.systemPackages = with pkgs; [wget];
+  };
+}
diff --git a/modules/common/zathura.nix b/modules/common/zathura.nix
new file mode 100644
index 0000000..f78a9e9
--- /dev/null
+++ b/modules/common/zathura.nix
@@ -0,0 +1,120 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.zathura;
+in {
+  options.nixfiles.modules.zathura.enable =
+    mkEnableOption "Zathura PDF reader";
+
+  config = mkIf cfg.enable {
+    hm.programs.zathura = with config.nixfiles.modules; {
+      enable = true;
+
+      options =
+        (with config.colourScheme; {
+          default-fg = white;
+          default-bg = black;
+
+          statusbar-fg = black;
+          statusbar-bg = white;
+
+          inputbar-fg = black;
+          inputbar-bg = brightGreen;
+
+          notification-fg = black;
+          notification-bg = brightBlue;
+
+          notification-warning-fg = black;
+          notification-warning-bg = brightYellow;
+
+          notification-error-fg = black;
+          notification-error-bg = brightRed;
+
+          highlight-color = brightYellow;
+          highlight-active-color = yellow;
+
+          completion-fg = brightWhite;
+          completion-bg = brightBlack;
+
+          completion-highlight-fg = black;
+          completion-highlight-bg = brightRed;
+
+          completion-group-fg = black;
+          completion-group-bg = brightRed;
+
+          recolor-darkcolor = black;
+          recolor-lightcolor = white;
+        })
+        // {
+          recolor = true;
+          recolor-keephue = false;
+          recolor-reverse-video = false;
+
+          highlight-transparency = "0.3";
+
+          font = config.fontScheme.monospaceFont.family;
+
+          n-completion-items = 10;
+
+          guioptions = "";
+
+          statusbar-basename = true;
+          statusbar-home-tilde = true;
+
+          statusbar-h-padding = 0;
+          statusbar-v-padding = 0;
+
+          window-height = 800;
+          window-width = 600;
+
+          window-icon = "";
+
+          abort-clear-search = true;
+
+          incremental-search = true;
+
+          adjust-open = "best-fit";
+
+          advance-pages-per-row = false;
+
+          database = "sqlite";
+
+          dbus-service = false;
+
+          page-padding = 0;
+
+          pages-per-row = 1;
+
+          render-loading = false;
+
+          show-directories = true;
+          show-hidden = true;
+          show-recent = 10;
+
+          link-zoom = true;
+          link-hadjust = true;
+
+          window-title-basename = true;
+          window-title-home-tilde = true;
+          window-title-page = true;
+
+          zoom-center = false;
+          zoom-max = 1000;
+          zoom-min = 10;
+          zoom-step = 10;
+
+          scroll-hstep = -1;
+          scroll-step = 40;
+          scroll-full-overlap = 0;
+          scroll-wrap = true;
+          scroll-page-aware = false;
+
+          selection-clipboard = "clipboard";
+          selection-notification = false;
+        };
+    };
+  };
+}

Consider giving Nix/NixOS a try! <3