summary refs log tree commit diff
path: root/nixos/modules/programs
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/programs')
-rw-r--r--nixos/modules/programs/adb.nix30
-rw-r--r--nixos/modules/programs/appgate-sdp.nix25
-rw-r--r--nixos/modules/programs/atop.nix155
-rw-r--r--nixos/modules/programs/autojump.nix33
-rw-r--r--nixos/modules/programs/bandwhich.nix31
-rw-r--r--nixos/modules/programs/bash-my-aws.nix25
-rw-r--r--nixos/modules/programs/bash/bash-completion.nix37
-rw-r--r--nixos/modules/programs/bash/bash.nix217
-rw-r--r--nixos/modules/programs/bash/inputrc37
-rw-r--r--nixos/modules/programs/bash/ls-colors.nix20
-rw-r--r--nixos/modules/programs/bash/undistract-me.nix36
-rw-r--r--nixos/modules/programs/bcc.nix9
-rw-r--r--nixos/modules/programs/browserpass.nix32
-rw-r--r--nixos/modules/programs/calls.nix27
-rw-r--r--nixos/modules/programs/captive-browser.nix152
-rw-r--r--nixos/modules/programs/ccache.nix85
-rw-r--r--nixos/modules/programs/cdemu.nix62
-rw-r--r--nixos/modules/programs/chromium.nix112
-rw-r--r--nixos/modules/programs/clickshare.nix21
-rw-r--r--nixos/modules/programs/cnping.nix21
-rw-r--r--nixos/modules/programs/command-not-found/command-not-found.nix95
-rw-r--r--nixos/modules/programs/command-not-found/command-not-found.pl77
-rw-r--r--nixos/modules/programs/criu.nix27
-rw-r--r--nixos/modules/programs/dconf.nix66
-rw-r--r--nixos/modules/programs/digitalbitbox/default.nix39
-rw-r--r--nixos/modules/programs/digitalbitbox/doc.xml74
-rw-r--r--nixos/modules/programs/dmrconfig.nix38
-rw-r--r--nixos/modules/programs/droidcam.nix16
-rw-r--r--nixos/modules/programs/environment.nix67
-rw-r--r--nixos/modules/programs/evince.nix51
-rw-r--r--nixos/modules/programs/extra-container.nix17
-rw-r--r--nixos/modules/programs/feedbackd.nix33
-rw-r--r--nixos/modules/programs/file-roller.nix48
-rw-r--r--nixos/modules/programs/firejail.nix97
-rw-r--r--nixos/modules/programs/fish.nix320
-rw-r--r--nixos/modules/programs/fish_completion-generator.patch14
-rw-r--r--nixos/modules/programs/flashrom.nix26
-rw-r--r--nixos/modules/programs/flexoptix-app.nix25
-rw-r--r--nixos/modules/programs/freetds.nix61
-rw-r--r--nixos/modules/programs/fuse.nix37
-rw-r--r--nixos/modules/programs/gamemode.nix98
-rw-r--r--nixos/modules/programs/geary.nix24
-rw-r--r--nixos/modules/programs/git.nix69
-rw-r--r--nixos/modules/programs/gnome-disks.nix50
-rw-r--r--nixos/modules/programs/gnome-documents.nix54
-rw-r--r--nixos/modules/programs/gnome-terminal.nix38
-rw-r--r--nixos/modules/programs/gnupg.nix154
-rw-r--r--nixos/modules/programs/gpaste.nix36
-rw-r--r--nixos/modules/programs/gphoto2.nix30
-rw-r--r--nixos/modules/programs/hamster.nix15
-rw-r--r--nixos/modules/programs/htop.nix58
-rw-r--r--nixos/modules/programs/iftop.nix20
-rw-r--r--nixos/modules/programs/iotop.nix19
-rw-r--r--nixos/modules/programs/java.nix58
-rw-r--r--nixos/modules/programs/k40-whisperer.nix40
-rw-r--r--nixos/modules/programs/kbdlight.nix21
-rw-r--r--nixos/modules/programs/kclock.nix13
-rw-r--r--nixos/modules/programs/kdeconnect.nix35
-rw-r--r--nixos/modules/programs/less.nix134
-rw-r--r--nixos/modules/programs/liboping.nix24
-rw-r--r--nixos/modules/programs/light.nix27
-rw-r--r--nixos/modules/programs/mininet.nix39
-rw-r--r--nixos/modules/programs/mosh.nix43
-rw-r--r--nixos/modules/programs/msmtp.nix106
-rw-r--r--nixos/modules/programs/mtr.nix41
-rw-r--r--nixos/modules/programs/nano.nix42
-rw-r--r--nixos/modules/programs/nbd.nix19
-rw-r--r--nixos/modules/programs/neovim.nix166
-rw-r--r--nixos/modules/programs/nm-applet.nix31
-rw-r--r--nixos/modules/programs/noisetorch.nix28
-rw-r--r--nixos/modules/programs/npm.nix54
-rw-r--r--nixos/modules/programs/oblogout.nix11
-rw-r--r--nixos/modules/programs/pantheon-tweaks.nix19
-rw-r--r--nixos/modules/programs/partition-manager.nix19
-rw-r--r--nixos/modules/programs/phosh.nix162
-rw-r--r--nixos/modules/programs/plotinus.nix36
-rw-r--r--nixos/modules/programs/plotinus.xml30
-rw-r--r--nixos/modules/programs/proxychains.nix165
-rw-r--r--nixos/modules/programs/qt5ct.nix31
-rw-r--r--nixos/modules/programs/screen.nix33
-rw-r--r--nixos/modules/programs/seahorse.nix46
-rw-r--r--nixos/modules/programs/sedutil.nix18
-rw-r--r--nixos/modules/programs/shadow.nix129
-rw-r--r--nixos/modules/programs/singularity.nix34
-rw-r--r--nixos/modules/programs/slock.nix31
-rw-r--r--nixos/modules/programs/spacefm.nix55
-rw-r--r--nixos/modules/programs/ssh.nix346
-rw-r--r--nixos/modules/programs/ssmtp.nix190
-rw-r--r--nixos/modules/programs/starship.nix51
-rw-r--r--nixos/modules/programs/steam.nix63
-rw-r--r--nixos/modules/programs/sway.nix150
-rw-r--r--nixos/modules/programs/sysdig.nix14
-rw-r--r--nixos/modules/programs/system-config-printer.nix32
-rw-r--r--nixos/modules/programs/systemtap.nix29
-rw-r--r--nixos/modules/programs/thefuck.nix39
-rw-r--r--nixos/modules/programs/tmux.nix201
-rw-r--r--nixos/modules/programs/traceroute.nix28
-rw-r--r--nixos/modules/programs/tsm-client.nix287
-rw-r--r--nixos/modules/programs/turbovnc.nix54
-rw-r--r--nixos/modules/programs/udevil.nix19
-rw-r--r--nixos/modules/programs/usbtop.nix21
-rw-r--r--nixos/modules/programs/vim.nix33
-rw-r--r--nixos/modules/programs/virtualbox.nix8
-rw-r--r--nixos/modules/programs/wavemon.nix30
-rw-r--r--nixos/modules/programs/waybar.nix20
-rw-r--r--nixos/modules/programs/weylus.nix47
-rw-r--r--nixos/modules/programs/wireshark.nix42
-rw-r--r--nixos/modules/programs/wshowkeys.nix27
-rw-r--r--nixos/modules/programs/xfs_quota.nix110
-rw-r--r--nixos/modules/programs/xonsh.nix86
-rw-r--r--nixos/modules/programs/xss-lock.nix45
-rw-r--r--nixos/modules/programs/xwayland.nix50
-rw-r--r--nixos/modules/programs/yabar.nix163
-rw-r--r--nixos/modules/programs/zmap.nix18
-rw-r--r--nixos/modules/programs/zsh/oh-my-zsh.nix146
-rw-r--r--nixos/modules/programs/zsh/oh-my-zsh.xml155
-rw-r--r--nixos/modules/programs/zsh/zinputrc42
-rw-r--r--nixos/modules/programs/zsh/zsh-autoenv.nix28
-rw-r--r--nixos/modules/programs/zsh/zsh-autosuggestions.nix73
-rw-r--r--nixos/modules/programs/zsh/zsh-syntax-highlighting.nix107
-rw-r--r--nixos/modules/programs/zsh/zsh.nix303
121 files changed, 7857 insertions, 0 deletions
diff --git a/nixos/modules/programs/adb.nix b/nixos/modules/programs/adb.nix
new file mode 100644
index 00000000000..83bcfe886aa
--- /dev/null
+++ b/nixos/modules/programs/adb.nix
@@ -0,0 +1,30 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  meta.maintainers = [ maintainers.mic92 ];
+
+  ###### interface
+  options = {
+    programs.adb = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to configure system to use Android Debug Bridge (adb).
+          To grant access to a user, it must be part of adbusers group:
+          <code>users.users.alice.extraGroups = ["adbusers"];</code>
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+  config = mkIf config.programs.adb.enable {
+    services.udev.packages = [ pkgs.android-udev-rules ];
+    # Give platform-tools lower priority so mke2fs+friends are taken from other packages first
+    environment.systemPackages = [ (lowPrio pkgs.androidenv.androidPkgs_9_0.platform-tools) ];
+    users.groups.adbusers = {};
+  };
+}
diff --git a/nixos/modules/programs/appgate-sdp.nix b/nixos/modules/programs/appgate-sdp.nix
new file mode 100644
index 00000000000..12cb542f4d0
--- /dev/null
+++ b/nixos/modules/programs/appgate-sdp.nix
@@ -0,0 +1,25 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+{
+  options = {
+    programs.appgate-sdp = {
+      enable = mkEnableOption "AppGate SDP VPN client";
+    };
+  };
+
+  config = mkIf config.programs.appgate-sdp.enable {
+    boot.kernelModules = [ "tun" ];
+    environment.systemPackages = [ pkgs.appgate-sdp ];
+    services.dbus.packages = [ pkgs.appgate-sdp ];
+    systemd = {
+      packages = [ pkgs.appgate-sdp ];
+      # https://github.com/NixOS/nixpkgs/issues/81138
+      services.appgatedriver.wantedBy = [ "multi-user.target" ];
+      services.appgate-dumb-resolver.path = [ pkgs.e2fsprogs ];
+      services.appgate-resolver.path = [ pkgs.procps pkgs.e2fsprogs ];
+      services.appgatedriver.path = [ pkgs.e2fsprogs ];
+    };
+  };
+}
diff --git a/nixos/modules/programs/atop.nix b/nixos/modules/programs/atop.nix
new file mode 100644
index 00000000000..ad75ab27666
--- /dev/null
+++ b/nixos/modules/programs/atop.nix
@@ -0,0 +1,155 @@
+# Global configuration for atop.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.programs.atop;
+
+in
+{
+  ###### interface
+
+  options = {
+
+    programs.atop = rec {
+
+      enable = mkEnableOption "Atop";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.atop;
+        defaultText = literalExpression "pkgs.atop";
+        description = ''
+          Which package to use for Atop.
+        '';
+      };
+
+      netatop = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Whether to install and enable the netatop kernel module.
+            Note: this sets the kernel taint flag "O" for loading out-of-tree modules.
+          '';
+        };
+        package = mkOption {
+          type = types.package;
+          default = config.boot.kernelPackages.netatop;
+          defaultText = literalExpression "config.boot.kernelPackages.netatop";
+          description = ''
+            Which package to use for netatop.
+          '';
+        };
+      };
+
+      atopgpu.enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to install and enable the atopgpud daemon to get information about
+          NVIDIA gpus.
+        '';
+      };
+
+      setuidWrapper.enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to install a setuid wrapper for Atop. This is required to use some of
+          the features as non-root user (e.g.: ipc information, netatop, atopgpu).
+          Atop tries to drop the root privileges shortly after starting.
+        '';
+      };
+
+      atopService.enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable the atop service responsible for storing statistics for
+          long-term analysis.
+        '';
+      };
+      atopRotateTimer.enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable the atop-rotate timer, which restarts the atop service
+          daily to make sure the data files are rotate.
+        '';
+      };
+      atopacctService.enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable the atopacct service which manages process accounting.
+          This allows Atop to gather data about processes that disappeared in between
+          two refresh intervals.
+        '';
+      };
+      settings = mkOption {
+        type = types.attrs;
+        default = { };
+        example = {
+          flags = "a1f";
+          interval = 5;
+        };
+        description = ''
+          Parameters to be written to <filename>/etc/atoprc</filename>.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable (
+    let
+      atop =
+        if cfg.atopgpu.enable then
+          (cfg.package.override { withAtopgpu = true; })
+        else
+          cfg.package;
+    in
+    {
+      environment.etc = mkIf (cfg.settings != { }) {
+        atoprc.text = concatStrings
+          (mapAttrsToList
+            (n: v: ''
+              ${n} ${toString v}
+            '')
+            cfg.settings);
+      };
+      environment.systemPackages = [ atop (lib.mkIf cfg.netatop.enable cfg.netatop.package) ];
+      boot.extraModulePackages = [ (lib.mkIf cfg.netatop.enable cfg.netatop.package) ];
+      systemd =
+        let
+          mkSystemd = type: cond: name: restartTriggers: {
+            ${name} = lib.mkIf cond {
+              inherit restartTriggers;
+              wantedBy = [ (if type == "services" then "multi-user.target" else if type == "timers" then "timers.target" else null) ];
+            };
+          };
+          mkService = mkSystemd "services";
+          mkTimer = mkSystemd "timers";
+        in
+        {
+          packages = [ atop (lib.mkIf cfg.netatop.enable cfg.netatop.package) ];
+          services =
+            mkService cfg.atopService.enable "atop" [ atop ]
+            // mkService cfg.atopacctService.enable "atopacct" [ atop ]
+            // mkService cfg.netatop.enable "netatop" [ cfg.netatop.package ]
+            // mkService cfg.atopgpu.enable "atopgpu" [ atop ];
+          timers = mkTimer cfg.atopRotateTimer.enable "atop-rotate" [ atop ];
+        };
+
+      security.wrappers = lib.mkIf cfg.setuidWrapper.enable {
+        atop =
+          { setuid = true;
+            owner = "root";
+            group = "root";
+            source = "${atop}/bin/atop";
+          };
+      };
+    }
+  );
+}
diff --git a/nixos/modules/programs/autojump.nix b/nixos/modules/programs/autojump.nix
new file mode 100644
index 00000000000..ecfc2f65807
--- /dev/null
+++ b/nixos/modules/programs/autojump.nix
@@ -0,0 +1,33 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.autojump;
+  prg = config.programs;
+in
+{
+  options = {
+    programs.autojump = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable autojump.
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment.pathsToLink = [ "/share/autojump" ];
+    environment.systemPackages = [ pkgs.autojump ];
+
+    programs.bash.interactiveShellInit = "source ${pkgs.autojump}/share/autojump/autojump.bash";
+    programs.zsh.interactiveShellInit = mkIf prg.zsh.enable "source ${pkgs.autojump}/share/autojump/autojump.zsh";
+    programs.fish.interactiveShellInit = mkIf prg.fish.enable "source ${pkgs.autojump}/share/autojump/autojump.fish";
+  };
+}
diff --git a/nixos/modules/programs/bandwhich.nix b/nixos/modules/programs/bandwhich.nix
new file mode 100644
index 00000000000..610d602ad2c
--- /dev/null
+++ b/nixos/modules/programs/bandwhich.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.programs.bandwhich;
+in {
+  meta.maintainers = with maintainers; [ Br1ght0ne ];
+
+  options = {
+    programs.bandwhich = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to add bandwhich to the global environment and configure a
+          setcap wrapper for it.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = with pkgs; [ bandwhich ];
+    security.wrappers.bandwhich = {
+      owner = "root";
+      group = "root";
+      capabilities = "cap_net_raw,cap_net_admin+ep";
+      source = "${pkgs.bandwhich}/bin/bandwhich";
+    };
+  };
+}
diff --git a/nixos/modules/programs/bash-my-aws.nix b/nixos/modules/programs/bash-my-aws.nix
new file mode 100644
index 00000000000..15e429a7549
--- /dev/null
+++ b/nixos/modules/programs/bash-my-aws.nix
@@ -0,0 +1,25 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  prg = config.programs;
+  cfg = prg.bash-my-aws;
+
+  initScript = ''
+    eval $(${pkgs.bash-my-aws}/bin/bma-init)
+  '';
+in
+  {
+    options = {
+      programs.bash-my-aws = {
+        enable = mkEnableOption "bash-my-aws";
+      };
+    };
+
+    config = mkIf cfg.enable {
+      environment.systemPackages = with pkgs; [ bash-my-aws ];
+
+      programs.bash.interactiveShellInit = initScript;
+    };
+  }
diff --git a/nixos/modules/programs/bash/bash-completion.nix b/nixos/modules/programs/bash/bash-completion.nix
new file mode 100644
index 00000000000..b8e5b1bfa33
--- /dev/null
+++ b/nixos/modules/programs/bash/bash-completion.nix
@@ -0,0 +1,37 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  enable = config.programs.bash.enableCompletion;
+in
+{
+  options = {
+    programs.bash.enableCompletion = mkEnableOption "Bash completion for all interactive bash shells" // {
+      default = true;
+    };
+  };
+
+  config = mkIf enable {
+    programs.bash.promptPluginInit = ''
+      # Check whether we're running a version of Bash that has support for
+      # programmable completion. If we do, enable all modules installed in
+      # the system and user profile in obsolete /etc/bash_completion.d/
+      # directories. Bash loads completions in all
+      # $XDG_DATA_DIRS/bash-completion/completions/
+      # on demand, so they do not need to be sourced here.
+      if shopt -q progcomp &>/dev/null; then
+        . "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh"
+        nullglobStatus=$(shopt -p nullglob)
+        shopt -s nullglob
+        for p in $NIX_PROFILES; do
+          for m in "$p/etc/bash_completion.d/"*; do
+            . "$m"
+          done
+        done
+        eval "$nullglobStatus"
+        unset nullglobStatus p m
+      fi
+    '';
+  };
+}
diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix
new file mode 100644
index 00000000000..7281126979e
--- /dev/null
+++ b/nixos/modules/programs/bash/bash.nix
@@ -0,0 +1,217 @@
+# This module defines global configuration for the Bash shell, in
+# particular /etc/bashrc and /etc/profile.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfge = config.environment;
+
+  cfg = config.programs.bash;
+
+  bashAliases = concatStringsSep "\n" (
+    mapAttrsFlatten (k: v: "alias ${k}=${escapeShellArg v}")
+      (filterAttrs (k: v: v != null) cfg.shellAliases)
+  );
+
+in
+
+{
+  imports = [
+    (mkRemovedOptionModule [ "programs" "bash" "enable" ] "")
+  ];
+
+  options = {
+
+    programs.bash = {
+
+      /*
+      enable = mkOption {
+        default = true;
+        description = ''
+          Whenever to configure Bash as an interactive shell.
+          Note that this tries to make Bash the default
+          <option>users.defaultUserShell</option>,
+          which in turn means that you might need to explicitly
+          set this variable if you have another shell configured
+          with NixOS.
+        '';
+        type = types.bool;
+      };
+      */
+
+      shellAliases = mkOption {
+        default = {};
+        description = ''
+          Set of aliases for bash shell, which overrides <option>environment.shellAliases</option>.
+          See <option>environment.shellAliases</option> for an option format description.
+        '';
+        type = with types; attrsOf (nullOr (either str path));
+      };
+
+      shellInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code called during bash shell initialisation.
+        '';
+        type = types.lines;
+      };
+
+      loginShellInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code called during login bash shell initialisation.
+        '';
+        type = types.lines;
+      };
+
+      interactiveShellInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code called during interactive bash shell initialisation.
+        '';
+        type = types.lines;
+      };
+
+      promptInit = mkOption {
+        default = ''
+          # Provide a nice prompt if the terminal supports it.
+          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
+              # Emacs term mode doesn't support xterm title escape sequence (\e]0;)
+              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
+        '';
+        description = ''
+          Shell script code used to initialise the bash prompt.
+        '';
+        type = types.lines;
+      };
+
+      promptPluginInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code used to initialise bash prompt plugins.
+        '';
+        type = types.lines;
+        internal = true;
+      };
+
+    };
+
+  };
+
+  config = /* mkIf cfg.enable */ {
+
+    programs.bash = {
+
+      shellAliases = mapAttrs (name: mkDefault) cfge.shellAliases;
+
+      shellInit = ''
+        if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]; then
+            . ${config.system.build.setEnvironment}
+        fi
+
+        ${cfge.shellInit}
+      '';
+
+      loginShellInit = cfge.loginShellInit;
+
+      interactiveShellInit = ''
+        # Check the window size after every command.
+        shopt -s checkwinsize
+
+        # Disable hashing (i.e. caching) of command lookups.
+        set +h
+
+        ${cfg.promptInit}
+        ${cfg.promptPluginInit}
+        ${bashAliases}
+
+        ${cfge.interactiveShellInit}
+      '';
+
+    };
+
+    environment.etc.profile.text =
+      ''
+        # /etc/profile: DO NOT EDIT -- this file has been generated automatically.
+        # This file is read for login shells.
+
+        # Only execute this file once per shell.
+        if [ -n "$__ETC_PROFILE_SOURCED" ]; then return; fi
+        __ETC_PROFILE_SOURCED=1
+
+        # Prevent this file from being sourced by interactive non-login child shells.
+        export __ETC_PROFILE_DONE=1
+
+        ${cfg.shellInit}
+        ${cfg.loginShellInit}
+
+        # Read system-wide modifications.
+        if test -f /etc/profile.local; then
+            . /etc/profile.local
+        fi
+
+        if [ -n "''${BASH_VERSION:-}" ]; then
+            . /etc/bashrc
+        fi
+      '';
+
+    environment.etc.bashrc.text =
+      ''
+        # /etc/bashrc: DO NOT EDIT -- this file has been generated automatically.
+
+        # Only execute this file once per shell.
+        if [ -n "$__ETC_BASHRC_SOURCED" ] || [ -n "$NOSYSBASHRC" ]; then return; fi
+        __ETC_BASHRC_SOURCED=1
+
+        # If the profile was not loaded in a parent process, source
+        # it.  But otherwise don't do it because we don't want to
+        # clobber overridden values of $PATH, etc.
+        if [ -z "$__ETC_PROFILE_DONE" ]; then
+            . /etc/profile
+        fi
+
+        # We are not always an interactive shell.
+        if [ -n "$PS1" ]; then
+            ${cfg.interactiveShellInit}
+        fi
+
+        # Read system-wide modifications.
+        if test -f /etc/bashrc.local; then
+            . /etc/bashrc.local
+        fi
+      '';
+
+    # Configuration for readline in bash. We use "option default"
+    # priority to allow user override using both .text and .source.
+    environment.etc.inputrc.source = mkOptionDefault ./inputrc;
+
+    users.defaultUserShell = mkDefault pkgs.bashInteractive;
+
+    environment.pathsToLink = optionals cfg.enableCompletion [
+      "/etc/bash_completion.d"
+      "/share/bash-completion"
+    ];
+
+    environment.shells =
+      [ "/run/current-system/sw/bin/bash"
+        "/run/current-system/sw/bin/sh"
+        "${pkgs.bashInteractive}/bin/bash"
+        "${pkgs.bashInteractive}/bin/sh"
+      ];
+
+  };
+
+}
diff --git a/nixos/modules/programs/bash/inputrc b/nixos/modules/programs/bash/inputrc
new file mode 100644
index 00000000000..f339eb649ed
--- /dev/null
+++ b/nixos/modules/programs/bash/inputrc
@@ -0,0 +1,37 @@
+# inputrc borrowed from CentOS (RHEL).
+
+set bell-style none
+
+set meta-flag on
+set input-meta on
+set convert-meta off
+set output-meta on
+set colored-stats on
+
+#set mark-symlinked-directories on
+
+$if mode=emacs
+
+# for linux console and RH/Debian xterm
+"\e[1~": beginning-of-line
+"\e[4~": end-of-line
+"\e[5~": beginning-of-history
+"\e[6~": end-of-history
+"\e[3~": delete-char
+"\e[2~": quoted-insert
+"\e[5C": forward-word
+"\e[5D": backward-word
+"\e[1;5C": forward-word
+"\e[1;5D": backward-word
+
+# for rxvt
+"\e[8~": end-of-line
+
+# for non RH/Debian xterm, can't hurt for RH/DEbian xterm
+"\eOH": beginning-of-line
+"\eOF": end-of-line
+
+# for freebsd console
+"\e[H": beginning-of-line
+"\e[F": end-of-line
+$endif
diff --git a/nixos/modules/programs/bash/ls-colors.nix b/nixos/modules/programs/bash/ls-colors.nix
new file mode 100644
index 00000000000..254ee14c477
--- /dev/null
+++ b/nixos/modules/programs/bash/ls-colors.nix
@@ -0,0 +1,20 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  enable = config.programs.bash.enableLsColors;
+in
+{
+  options = {
+    programs.bash.enableLsColors = mkEnableOption "extra colors in directory listings" // {
+      default = true;
+    };
+  };
+
+  config = mkIf enable {
+    programs.bash.promptPluginInit = ''
+      eval "$(${pkgs.coreutils}/bin/dircolors -b)"
+    '';
+  };
+}
diff --git a/nixos/modules/programs/bash/undistract-me.nix b/nixos/modules/programs/bash/undistract-me.nix
new file mode 100644
index 00000000000..0e6465e048a
--- /dev/null
+++ b/nixos/modules/programs/bash/undistract-me.nix
@@ -0,0 +1,36 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.bash.undistractMe;
+in
+{
+  options = {
+    programs.bash.undistractMe = {
+      enable = mkEnableOption "notifications when long-running terminal commands complete";
+
+      playSound = mkEnableOption "notification sounds when long-running terminal commands complete";
+
+      timeout = mkOption {
+        default = 10;
+        description = ''
+          Number of seconds it would take for a command to be considered long-running.
+        '';
+        type = types.int;
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    programs.bash.promptPluginInit = ''
+      export LONG_RUNNING_COMMAND_TIMEOUT=${toString cfg.timeout}
+      export UDM_PLAY_SOUND=${if cfg.playSound then "1" else "0"}
+      . "${pkgs.undistract-me}/etc/profile.d/undistract-me.sh"
+    '';
+  };
+
+  meta = {
+    maintainers = with maintainers; [ kira-bruneau ];
+  };
+}
diff --git a/nixos/modules/programs/bcc.nix b/nixos/modules/programs/bcc.nix
new file mode 100644
index 00000000000..e475c6ceaa6
--- /dev/null
+++ b/nixos/modules/programs/bcc.nix
@@ -0,0 +1,9 @@
+{ config, pkgs, lib, ... }:
+{
+  options.programs.bcc.enable = lib.mkEnableOption "bcc";
+
+  config = lib.mkIf config.programs.bcc.enable {
+    environment.systemPackages = [ pkgs.bcc ];
+    boot.extraModulePackages = [ pkgs.bcc ];
+  };
+}
diff --git a/nixos/modules/programs/browserpass.nix b/nixos/modules/programs/browserpass.nix
new file mode 100644
index 00000000000..e1456d3c184
--- /dev/null
+++ b/nixos/modules/programs/browserpass.nix
@@ -0,0 +1,32 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  options.programs.browserpass.enable = mkEnableOption "Browserpass native messaging host";
+
+  config = mkIf config.programs.browserpass.enable {
+    environment.etc = let
+      appId = "com.github.browserpass.native.json";
+      source = part: "${pkgs.browserpass}/lib/browserpass/${part}/${appId}";
+    in {
+      # chromium
+      "chromium/native-messaging-hosts/${appId}".source = source "hosts/chromium";
+      "chromium/policies/managed/${appId}".source = source "policies/chromium";
+
+      # chrome
+      "opt/chrome/native-messaging-hosts/${appId}".source = source "hosts/chromium";
+      "opt/chrome/policies/managed/${appId}".source = source "policies/chromium";
+
+      # vivaldi
+      "opt/vivaldi/native-messaging-hosts/${appId}".source = source "hosts/chromium";
+      "opt/vivaldi/policies/managed/${appId}".source = source "policies/chromium";
+
+      # brave
+      "opt/brave/native-messaging-hosts/${appId}".source = source "hosts/chromium";
+      "opt/brave/policies/managed/${appId}".source = source "policies/chromium";
+    };
+    nixpkgs.config.firefox.enableBrowserpass = true;
+  };
+}
diff --git a/nixos/modules/programs/calls.nix b/nixos/modules/programs/calls.nix
new file mode 100644
index 00000000000..08a223b408d
--- /dev/null
+++ b/nixos/modules/programs/calls.nix
@@ -0,0 +1,27 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.calls;
+in {
+  options = {
+    programs.calls = {
+      enable = mkEnableOption ''
+        Whether to enable GNOME calls: a phone dialer and call handler.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    programs.dconf.enable = true;
+
+    environment.systemPackages = [
+      pkgs.calls
+    ];
+
+    services.dbus.packages = [
+      pkgs.callaudiod
+    ];
+  };
+}
diff --git a/nixos/modules/programs/captive-browser.nix b/nixos/modules/programs/captive-browser.nix
new file mode 100644
index 00000000000..aad554c2bd6
--- /dev/null
+++ b/nixos/modules/programs/captive-browser.nix
@@ -0,0 +1,152 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.programs.captive-browser;
+
+  inherit (lib)
+    concatStringsSep escapeShellArgs optionalString
+    literalExpression mkEnableOption mkIf mkOption mkOptionDefault types;
+
+  browserDefault = chromium: concatStringsSep " " [
+    ''env XDG_CONFIG_HOME="$PREV_CONFIG_HOME"''
+    ''${chromium}/bin/chromium''
+    ''--user-data-dir=''${XDG_DATA_HOME:-$HOME/.local/share}/chromium-captive''
+    ''--proxy-server="socks5://$PROXY"''
+    ''--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE localhost"''
+    ''--no-first-run''
+    ''--new-window''
+    ''--incognito''
+    ''-no-default-browser-check''
+    ''http://cache.nixos.org/''
+  ];
+
+  desktopItem = pkgs.makeDesktopItem {
+    name = "captive-browser";
+    desktopName = "Captive Portal Browser";
+    exec = "/run/wrappers/bin/captive-browser";
+    icon = "nix-snowflake";
+    categories = [ "Network" ];
+  };
+
+in
+{
+  ###### interface
+
+  options = {
+    programs.captive-browser = {
+      enable = mkEnableOption "captive browser";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.captive-browser;
+        defaultText = literalExpression "pkgs.captive-browser";
+        description = "Which package to use for captive-browser";
+      };
+
+      interface = mkOption {
+        type = types.str;
+        description = "your public network interface (wlp3s0, wlan0, eth0, ...)";
+      };
+
+      # the options below are the same as in "captive-browser.toml"
+      browser = mkOption {
+        type = types.str;
+        default = browserDefault pkgs.chromium;
+        defaultText = literalExpression (browserDefault "\${pkgs.chromium}");
+        description = ''
+          The shell (/bin/sh) command executed once the proxy starts.
+          When browser exits, the proxy exits. An extra env var PROXY is available.
+
+          Here, we use a separate Chrome instance in Incognito mode, so that
+          it can run (and be waited for) alongside the default one, and that
+          it maintains no state across runs. To configure this browser open a
+          normal window in it, settings will be preserved.
+
+          @volth: chromium is to open a plain HTTP (not HTTPS nor redirect to HTTPS!) website.
+                  upstream uses http://example.com but I have seen captive portals whose DNS server resolves "example.com" to 127.0.0.1
+        '';
+      };
+
+      dhcp-dns = mkOption {
+        type = types.str;
+        description = ''
+          The shell (/bin/sh) command executed to obtain the DHCP
+          DNS server address. The first match of an IPv4 regex is used.
+          IPv4 only, because let's be real, it's a captive portal.
+        '';
+      };
+
+      socks5-addr = mkOption {
+        type = types.str;
+        default = "localhost:1666";
+        description = "the listen address for the SOCKS5 proxy server";
+      };
+
+      bindInterface = mkOption {
+        default = true;
+        type = types.bool;
+        description = ''
+          Binds <package>captive-browser</package> to the network interface declared in
+          <literal>cfg.interface</literal>. This can be used to avoid collisions
+          with private subnets.
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [
+      (pkgs.runCommandNoCC "captive-browser-desktop-item" { } ''
+        install -Dm444 -t $out/share/applications ${desktopItem}/share/applications/*.desktop
+      '')
+    ];
+
+    programs.captive-browser.dhcp-dns =
+      let
+        iface = prefixes:
+          optionalString cfg.bindInterface (escapeShellArgs (prefixes ++ [ cfg.interface ]));
+      in
+      mkOptionDefault (
+        if config.networking.networkmanager.enable then
+          "${pkgs.networkmanager}/bin/nmcli dev show ${iface []} | ${pkgs.gnugrep}/bin/fgrep IP4.DNS"
+        else if config.networking.dhcpcd.enable then
+          "${pkgs.dhcpcd}/bin/dhcpcd ${iface ["-U"]} | ${pkgs.gnugrep}/bin/fgrep domain_name_servers"
+        else if config.networking.useNetworkd then
+          "${cfg.package}/bin/systemd-networkd-dns ${iface []}"
+        else
+          "${config.security.wrapperDir}/udhcpc --quit --now -f ${iface ["-i"]} -O dns --script ${
+          pkgs.writeShellScript "udhcp-script" ''
+            if [ "$1" = bound ]; then
+              echo "$dns"
+            fi
+          ''}"
+      );
+
+    security.wrappers.udhcpc = {
+      owner = "root";
+      group = "root";
+      capabilities = "cap_net_raw+p";
+      source = "${pkgs.busybox}/bin/udhcpc";
+    };
+
+    security.wrappers.captive-browser = {
+      owner = "root";
+      group = "root";
+      capabilities = "cap_net_raw+p";
+      source = pkgs.writeShellScript "captive-browser" ''
+        export PREV_CONFIG_HOME="$XDG_CONFIG_HOME"
+        export XDG_CONFIG_HOME=${pkgs.writeTextDir "captive-browser.toml" ''
+                                  browser = """${cfg.browser}"""
+                                  dhcp-dns = """${cfg.dhcp-dns}"""
+                                  socks5-addr = """${cfg.socks5-addr}"""
+                                  ${optionalString cfg.bindInterface ''
+                                    bind-device = """${cfg.interface}"""
+                                  ''}
+                                ''}
+        exec ${cfg.package}/bin/captive-browser
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/programs/ccache.nix b/nixos/modules/programs/ccache.nix
new file mode 100644
index 00000000000..0f7fd0a3683
--- /dev/null
+++ b/nixos/modules/programs/ccache.nix
@@ -0,0 +1,85 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.programs.ccache;
+in {
+  options.programs.ccache = {
+    # host configuration
+    enable = mkEnableOption "CCache";
+    cacheDir = mkOption {
+      type = types.path;
+      description = "CCache directory";
+      default = "/var/cache/ccache";
+    };
+    # target configuration
+    packageNames = mkOption {
+      type = types.listOf types.str;
+      description = "Nix top-level packages to be compiled using CCache";
+      default = [];
+      example = [ "wxGTK30" "ffmpeg" "libav_all" ];
+    };
+  };
+
+  config = mkMerge [
+    # host configuration
+    (mkIf cfg.enable {
+      systemd.tmpfiles.rules = [ "d ${cfg.cacheDir} 0770 root nixbld -" ];
+
+      # "nix-ccache --show-stats" and "nix-ccache --clear"
+      security.wrappers.nix-ccache = {
+        owner = "root";
+        group = "nixbld";
+        setuid = false;
+        setgid = true;
+        source = pkgs.writeScript "nix-ccache.pl" ''
+          #!${pkgs.perl}/bin/perl
+
+          %ENV=( CCACHE_DIR => '${cfg.cacheDir}' );
+          sub untaint {
+            my $v = shift;
+            return '-C' if $v eq '-C' || $v eq '--clear';
+            return '-V' if $v eq '-V' || $v eq '--version';
+            return '-s' if $v eq '-s' || $v eq '--show-stats';
+            return '-z' if $v eq '-z' || $v eq '--zero-stats';
+            exec('${pkgs.ccache}/bin/ccache', '-h');
+          }
+          exec('${pkgs.ccache}/bin/ccache', map { untaint $_ } @ARGV);
+        '';
+      };
+    })
+
+    # target configuration
+    (mkIf (cfg.packageNames != []) {
+      nixpkgs.overlays = [
+        (self: super: genAttrs cfg.packageNames (pn: super.${pn}.override { stdenv = builtins.trace "with ccache: ${pn}" self.ccacheStdenv; }))
+
+        (self: super: {
+          ccacheWrapper = super.ccacheWrapper.override {
+            extraConfig = ''
+              export CCACHE_COMPRESS=1
+              export CCACHE_DIR="${cfg.cacheDir}"
+              export CCACHE_UMASK=007
+              if [ ! -d "$CCACHE_DIR" ]; then
+                echo "====="
+                echo "Directory '$CCACHE_DIR' does not exist"
+                echo "Please create it with:"
+                echo "  sudo mkdir -m0770 '$CCACHE_DIR'"
+                echo "  sudo chown root:nixbld '$CCACHE_DIR'"
+                echo "====="
+                exit 1
+              fi
+              if [ ! -w "$CCACHE_DIR" ]; then
+                echo "====="
+                echo "Directory '$CCACHE_DIR' is not accessible for user $(whoami)"
+                echo "Please verify its access permissions"
+                echo "====="
+                exit 1
+              fi
+            '';
+          };
+        })
+      ];
+    })
+  ];
+}
diff --git a/nixos/modules/programs/cdemu.nix b/nixos/modules/programs/cdemu.nix
new file mode 100644
index 00000000000..142e2934240
--- /dev/null
+++ b/nixos/modules/programs/cdemu.nix
@@ -0,0 +1,62 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.programs.cdemu;
+in {
+
+  options = {
+    programs.cdemu = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          <command>cdemu</command> for members of
+          <option>programs.cdemu.group</option>.
+        '';
+      };
+      group = mkOption {
+        type = types.str;
+        default = "cdrom";
+        description = ''
+          Group that users must be in to use <command>cdemu</command>.
+        '';
+      };
+      gui = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to install the <command>cdemu</command> GUI (gCDEmu).
+        '';
+      };
+      image-analyzer = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to install the image analyzer.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    boot = {
+      extraModulePackages = [ config.boot.kernelPackages.vhba ];
+      kernelModules = [ "vhba" ];
+    };
+
+    services = {
+      udev.extraRules = ''
+        KERNEL=="vhba_ctl", MODE="0660", OWNER="root", GROUP="${cfg.group}"
+      '';
+      dbus.packages = [ pkgs.cdemu-daemon ];
+    };
+
+    environment.systemPackages =
+      [ pkgs.cdemu-daemon pkgs.cdemu-client ]
+      ++ optional cfg.gui pkgs.gcdemu
+      ++ optional cfg.image-analyzer pkgs.image-analyzer;
+  };
+
+}
diff --git a/nixos/modules/programs/chromium.nix b/nixos/modules/programs/chromium.nix
new file mode 100644
index 00000000000..8a1653318ab
--- /dev/null
+++ b/nixos/modules/programs/chromium.nix
@@ -0,0 +1,112 @@
+{ config, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.chromium;
+
+  defaultProfile = filterAttrs (k: v: v != null) {
+    HomepageLocation = cfg.homepageLocation;
+    DefaultSearchProviderEnabled = cfg.defaultSearchProviderEnabled;
+    DefaultSearchProviderSearchURL = cfg.defaultSearchProviderSearchURL;
+    DefaultSearchProviderSuggestURL = cfg.defaultSearchProviderSuggestURL;
+    ExtensionInstallForcelist = cfg.extensions;
+  };
+in
+
+{
+  ###### interface
+
+  options = {
+    programs.chromium = {
+      enable = mkEnableOption "<command>chromium</command> policies";
+
+      extensions = mkOption {
+        type = types.listOf types.str;
+        description = ''
+          List of chromium extensions to install.
+          For list of plugins ids see id in url of extensions on
+          <link xlink:href="https://chrome.google.com/webstore/category/extensions">chrome web store</link>
+          page. To install a chromium extension not included in the chrome web
+          store, append to the extension id a semicolon ";" followed by a URL
+          pointing to an Update Manifest XML file. See
+          <link xlink:href="https://cloud.google.com/docs/chrome-enterprise/policies/?policy=ExtensionInstallForcelist">ExtensionInstallForcelist</link>
+          for additional details.
+        '';
+        default = [];
+        example = literalExpression ''
+          [
+            "chlffgpmiacpedhhbkiomidkjlcfhogd" # pushbullet
+            "mbniclmhobmnbdlbpiphghaielnnpgdp" # lightshot
+            "gcbommkclmclpchllfjekcdonpmejbdp" # https everywhere
+            "cjpalhdlnbpafiamejdnhcphjbkeiagm" # ublock origin
+          ]
+        '';
+      };
+
+      homepageLocation = mkOption {
+        type = types.nullOr types.str;
+        description = "Chromium default homepage";
+        default = null;
+        example = "https://nixos.org";
+      };
+
+      defaultSearchProviderEnabled = mkOption {
+        type = types.nullOr types.bool;
+        description = "Enable the default search provider.";
+        default = null;
+        example = true;
+      };
+
+      defaultSearchProviderSearchURL = mkOption {
+        type = types.nullOr types.str;
+        description = "Chromium default search provider url.";
+        default = null;
+        example =
+          "https://encrypted.google.com/search?q={searchTerms}&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:searchClient}{google:sourceId}{google:instantExtendedEnabledParameter}ie={inputEncoding}";
+      };
+
+      defaultSearchProviderSuggestURL = mkOption {
+        type = types.nullOr types.str;
+        description = "Chromium default search provider url for suggestions.";
+        default = null;
+        example =
+          "https://encrypted.google.com/complete/search?output=chrome&q={searchTerms}";
+      };
+
+      extraOpts = mkOption {
+        type = types.attrs;
+        description = ''
+          Extra chromium policy options. A list of available policies
+          can be found in the Chrome Enterprise documentation:
+          <link xlink:href="https://cloud.google.com/docs/chrome-enterprise/policies/">https://cloud.google.com/docs/chrome-enterprise/policies/</link>
+          Make sure the selected policy is supported on Linux and your browser version.
+        '';
+        default = {};
+        example = literalExpression ''
+          {
+            "BrowserSignin" = 0;
+            "SyncDisabled" = true;
+            "PasswordManagerEnabled" = false;
+            "SpellcheckEnabled" = true;
+            "SpellcheckLanguage" = [
+                                     "de"
+                                     "en-US"
+                                   ];
+          }
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = lib.mkIf cfg.enable {
+    # for chromium
+    environment.etc."chromium/policies/managed/default.json".text = builtins.toJSON defaultProfile;
+    environment.etc."chromium/policies/managed/extra.json".text = builtins.toJSON cfg.extraOpts;
+    # for google-chrome https://www.chromium.org/administrators/linux-quick-start
+    environment.etc."opt/chrome/policies/managed/default.json".text = builtins.toJSON defaultProfile;
+    environment.etc."opt/chrome/policies/managed/extra.json".text = builtins.toJSON cfg.extraOpts;
+  };
+}
diff --git a/nixos/modules/programs/clickshare.nix b/nixos/modules/programs/clickshare.nix
new file mode 100644
index 00000000000..9980a7daf52
--- /dev/null
+++ b/nixos/modules/programs/clickshare.nix
@@ -0,0 +1,21 @@
+{ config, lib, pkgs, ... }:
+
+{
+
+  options.programs.clickshare-csc1.enable =
+    lib.options.mkEnableOption ''
+      Barco ClickShare CSC-1 driver/client.
+      This allows users in the <literal>clickshare</literal>
+      group to access and use a ClickShare USB dongle
+      that is connected to the machine
+    '';
+
+  config = lib.modules.mkIf config.programs.clickshare-csc1.enable {
+    environment.systemPackages = [ pkgs.clickshare-csc1 ];
+    services.udev.packages = [ pkgs.clickshare-csc1 ];
+    users.groups.clickshare = {};
+  };
+
+  meta.maintainers = [ lib.maintainers.yarny ];
+
+}
diff --git a/nixos/modules/programs/cnping.nix b/nixos/modules/programs/cnping.nix
new file mode 100644
index 00000000000..d208d2b0704
--- /dev/null
+++ b/nixos/modules/programs/cnping.nix
@@ -0,0 +1,21 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.cnping;
+in
+{
+  options = {
+    programs.cnping = {
+      enable = mkEnableOption "Whether to install a setcap wrapper for cnping";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    security.wrappers.cnping = {
+      source = "${pkgs.cnping}/bin/cnping";
+      capabilities = "cap_net_raw+ep";
+    };
+  };
+}
diff --git a/nixos/modules/programs/command-not-found/command-not-found.nix b/nixos/modules/programs/command-not-found/command-not-found.nix
new file mode 100644
index 00000000000..4d2a89b5158
--- /dev/null
+++ b/nixos/modules/programs/command-not-found/command-not-found.nix
@@ -0,0 +1,95 @@
+# This module provides suggestions of packages to install if the user
+# tries to run a missing command in Bash.  This is implemented using a
+# SQLite database that maps program names to Nix package names (e.g.,
+# "pdflatex" is mapped to "tetex").
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.command-not-found;
+  commandNotFound = pkgs.substituteAll {
+    name = "command-not-found";
+    dir = "bin";
+    src = ./command-not-found.pl;
+    isExecutable = true;
+    inherit (cfg) dbPath;
+    perl = pkgs.perl.withPackages (p: [ p.DBDSQLite p.StringShellQuote ]);
+  };
+
+in
+
+{
+  options.programs.command-not-found = {
+
+    enable = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether interactive shells should show which Nix package (if
+        any) provides a missing command.
+      '';
+    };
+
+    dbPath = mkOption {
+      default = "/nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite" ;
+      description = ''
+        Absolute path to programs.sqlite.
+
+        By default this file will be provided by your channel
+        (nixexprs.tar.xz).
+      '';
+      type = types.path;
+    };
+  };
+
+  config = mkIf cfg.enable {
+    programs.bash.interactiveShellInit =
+      ''
+        # This function is called whenever a command is not found.
+        command_not_found_handle() {
+          local p='${commandNotFound}/bin/command-not-found'
+          if [ -x "$p" ] && [ -f '${cfg.dbPath}' ]; then
+            # Run the helper program.
+            "$p" "$@"
+            # Retry the command if we just installed it.
+            if [ $? = 126 ]; then
+              "$@"
+            else
+              return 127
+            fi
+          else
+            echo "$1: command not found" >&2
+            return 127
+          fi
+        }
+      '';
+
+    programs.zsh.interactiveShellInit =
+      ''
+        # This function is called whenever a command is not found.
+        command_not_found_handler() {
+          local p='${commandNotFound}/bin/command-not-found'
+          if [ -x "$p" ] && [ -f '${cfg.dbPath}' ]; then
+            # Run the helper program.
+            "$p" "$@"
+
+            # Retry the command if we just installed it.
+            if [ $? = 126 ]; then
+              "$@"
+            else
+              return 127
+            fi
+          else
+            # Indicate than there was an error so ZSH falls back to its default handler
+            echo "$1: command not found" >&2
+            return 127
+          fi
+        }
+      '';
+
+    environment.systemPackages = [ commandNotFound ];
+  };
+
+}
diff --git a/nixos/modules/programs/command-not-found/command-not-found.pl b/nixos/modules/programs/command-not-found/command-not-found.pl
new file mode 100644
index 00000000000..72e246c81ae
--- /dev/null
+++ b/nixos/modules/programs/command-not-found/command-not-found.pl
@@ -0,0 +1,77 @@
+#! @perl@/bin/perl -w
+
+use strict;
+use DBI;
+use DBD::SQLite;
+use String::ShellQuote;
+use Config;
+
+my $program = $ARGV[0];
+
+my $dbPath = "@dbPath@";
+
+my $dbh = DBI->connect("dbi:SQLite:dbname=$dbPath", "", "")
+    or die "cannot open database `$dbPath'";
+$dbh->{RaiseError} = 0;
+$dbh->{PrintError} = 0;
+
+my $system = $ENV{"NIX_SYSTEM"} // $Config{myarchname};
+
+my $res = $dbh->selectall_arrayref(
+    "select package from Programs where system = ? and name = ?",
+    { Slice => {} }, $system, $program);
+
+my $len = !defined $res ? 0 : scalar @$res;
+
+if ($len == 0) {
+    print STDERR "$program: command not found\n";
+} elsif ($len == 1) {
+    my $package = @$res[0]->{package};
+    if ($ENV{"NIX_AUTO_RUN"} // "") {
+        if ($ENV{"NIX_AUTO_RUN_INTERACTIVE"} // "") {
+            while (1) {
+                print STDERR "'$program' from package '$package' will be run, confirm? [yn]: ";
+                chomp(my $comfirm = <STDIN>);
+                if (lc $comfirm eq "n") {
+                    exit 0;
+                } elsif (lc $comfirm eq "y") {
+                    last;
+                }
+            }
+        }
+        exec("nix-shell", "-p", $package, "--run", shell_quote("exec", @ARGV));
+    } else {
+        print STDERR <<EOF;
+The program '$program' is not in your PATH. You can make it available in an
+ephemeral shell by typing:
+  nix-shell -p $package
+EOF
+    }
+} else {
+    if ($ENV{"NIX_AUTO_RUN"} // "") {
+        print STDERR "Select a package that provides '$program':\n";
+        for my $i (0 .. $len - 1) {
+            print STDERR "  [", $i + 1, "]: @$res[$i]->{package}\n";
+        }
+        my $choice = 0;
+        while (1) { # exec will break this loop
+            no warnings "numeric";
+            print STDERR "Your choice [1-${len}]: ";
+            # 0 can be invalid user input like non-number string
+            # so we start from 1
+            $choice = <STDIN> + 0;
+            if (1 <= $choice && $choice <= $len) {
+                exec("nix-shell", "-p", @$res[$choice - 1]->{package},
+                    "--run", shell_quote("exec", @ARGV));
+            }
+        }
+    } else {
+        print STDERR <<EOF;
+The program '$program' is not in your PATH. It is provided by several packages.
+You can make it available in an ephemeral shell by typing one of the following:
+EOF
+        print STDERR "  nix-shell -p $_->{package}\n" foreach @$res;
+    }
+}
+
+exit 127;
diff --git a/nixos/modules/programs/criu.nix b/nixos/modules/programs/criu.nix
new file mode 100644
index 00000000000..1714e1331a4
--- /dev/null
+++ b/nixos/modules/programs/criu.nix
@@ -0,0 +1,27 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.programs.criu;
+in {
+
+  options = {
+    programs.criu = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Install <command>criu</command> along with necessary kernel options.
+        '';
+      };
+    };
+  };
+  config = mkIf cfg.enable {
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      (isYes "CHECKPOINT_RESTORE")
+    ];
+    boot.kernel.features.criu = true;
+    environment.systemPackages = [ pkgs.criu ];
+  };
+
+}
diff --git a/nixos/modules/programs/dconf.nix b/nixos/modules/programs/dconf.nix
new file mode 100644
index 00000000000..265c41cbbbc
--- /dev/null
+++ b/nixos/modules/programs/dconf.nix
@@ -0,0 +1,66 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.dconf;
+  cfgDir = pkgs.symlinkJoin {
+    name = "dconf-system-config";
+    paths = map (x: "${x}/etc/dconf") cfg.packages;
+    postBuild = ''
+      mkdir -p $out/profile
+      mkdir -p $out/db
+    '' + (
+      concatStringsSep "\n" (
+        mapAttrsToList (
+          name: path: ''
+            ln -s ${path} $out/profile/${name}
+          ''
+        ) cfg.profiles
+      )
+    ) + ''
+      ${pkgs.dconf}/bin/dconf update $out/db
+    '';
+  };
+in
+{
+  ###### interface
+
+  options = {
+    programs.dconf = {
+      enable = mkEnableOption "dconf";
+
+      profiles = mkOption {
+        type = types.attrsOf types.path;
+        default = {};
+        description = "Set of dconf profile files, installed at <filename>/etc/dconf/profiles/<replaceable>name</replaceable></filename>.";
+        internal = true;
+      };
+
+      packages = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        description = "A list of packages which provide dconf profiles and databases in <filename>/etc/dconf</filename>.";
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf (cfg.profiles != {} || cfg.enable) {
+    environment.etc.dconf = mkIf (cfg.profiles != {} || cfg.packages != []) {
+      source = cfgDir;
+    };
+
+    services.dbus.packages = [ pkgs.dconf ];
+
+    systemd.packages = [ pkgs.dconf ];
+
+    # For dconf executable
+    environment.systemPackages = [ pkgs.dconf ];
+
+    # Needed for unwrapped applications
+    environment.sessionVariables.GIO_EXTRA_MODULES = mkIf cfg.enable [ "${pkgs.dconf.lib}/lib/gio/modules" ];
+  };
+
+}
diff --git a/nixos/modules/programs/digitalbitbox/default.nix b/nixos/modules/programs/digitalbitbox/default.nix
new file mode 100644
index 00000000000..cabdf260cda
--- /dev/null
+++ b/nixos/modules/programs/digitalbitbox/default.nix
@@ -0,0 +1,39 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.digitalbitbox;
+in
+
+{
+  options.programs.digitalbitbox = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Installs the Digital Bitbox application and enables the complementary hardware module.
+      '';
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.digitalbitbox;
+      defaultText = literalExpression "pkgs.digitalbitbox";
+      description = "The Digital Bitbox package to use. This can be used to install a package with udev rules that differ from the defaults.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+    hardware.digitalbitbox = {
+      enable = true;
+      package = cfg.package;
+    };
+  };
+
+  meta = {
+    doc = ./doc.xml;
+    maintainers = with lib.maintainers; [ vidbina ];
+  };
+}
diff --git a/nixos/modules/programs/digitalbitbox/doc.xml b/nixos/modules/programs/digitalbitbox/doc.xml
new file mode 100644
index 00000000000..c63201628db
--- /dev/null
+++ b/nixos/modules/programs/digitalbitbox/doc.xml
@@ -0,0 +1,74 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="module-programs-digitalbitbox">
+ <title>Digital Bitbox</title>
+ <para>
+  Digital Bitbox is a hardware wallet and second-factor authenticator.
+ </para>
+ <para>
+  The <literal>digitalbitbox</literal> programs module may be installed by
+  setting <literal>programs.digitalbitbox</literal> to <literal>true</literal>
+  in a manner similar to
+<programlisting>
+<xref linkend="opt-programs.digitalbitbox.enable"/> = true;
+</programlisting>
+  and bundles the <literal>digitalbitbox</literal> package (see
+  <xref
+      linkend="sec-digitalbitbox-package" />), which contains the
+  <literal>dbb-app</literal> and <literal>dbb-cli</literal> binaries, along
+  with the hardware module (see
+  <xref
+      linkend="sec-digitalbitbox-hardware-module" />) which sets up the
+  necessary udev rules to access the device.
+ </para>
+ <para>
+  Enabling the digitalbitbox module is pretty much the easiest way to get a
+  Digital Bitbox device working on your system.
+ </para>
+ <para>
+  For more information, see
+  <link xlink:href="https://digitalbitbox.com/start_linux" />.
+ </para>
+ <section xml:id="sec-digitalbitbox-package">
+  <title>Package</title>
+
+  <para>
+   The binaries, <literal>dbb-app</literal> (a GUI tool) and
+   <literal>dbb-cli</literal> (a CLI tool), are available through the
+   <literal>digitalbitbox</literal> package which could be installed as
+   follows:
+<programlisting>
+<xref linkend="opt-environment.systemPackages"/> = [
+  pkgs.digitalbitbox
+];
+</programlisting>
+  </para>
+ </section>
+ <section xml:id="sec-digitalbitbox-hardware-module">
+  <title>Hardware</title>
+
+  <para>
+   The digitalbitbox hardware package enables the udev rules for Digital Bitbox
+   devices and may be installed as follows:
+<programlisting>
+<xref linkend="opt-hardware.digitalbitbox.enable"/> = true;
+</programlisting>
+  </para>
+
+  <para>
+   In order to alter the udev rules, one may provide different values for the
+   <literal>udevRule51</literal> and <literal>udevRule52</literal> attributes
+   by means of overriding as follows:
+<programlisting>
+programs.digitalbitbox = {
+  <link linkend="opt-programs.digitalbitbox.enable">enable</link> = true;
+  <link linkend="opt-programs.digitalbitbox.package">package</link> = pkgs.digitalbitbox.override {
+    udevRule51 = "something else";
+  };
+};
+</programlisting>
+  </para>
+ </section>
+</chapter>
diff --git a/nixos/modules/programs/dmrconfig.nix b/nixos/modules/programs/dmrconfig.nix
new file mode 100644
index 00000000000..d2a5117c48e
--- /dev/null
+++ b/nixos/modules/programs/dmrconfig.nix
@@ -0,0 +1,38 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.dmrconfig;
+
+in {
+  meta.maintainers = [ maintainers.etu ];
+
+  ###### interface
+  options = {
+    programs.dmrconfig = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to configure system to enable use of dmrconfig. This
+          enables the required udev rules and installs the program.
+        '';
+        relatedPackages = [ "dmrconfig" ];
+      };
+
+      package = mkOption {
+        default = pkgs.dmrconfig;
+        type = types.package;
+        defaultText = literalExpression "pkgs.dmrconfig";
+        description = "dmrconfig derivation to use";
+      };
+    };
+  };
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+    services.udev.packages = [ cfg.package ];
+  };
+}
diff --git a/nixos/modules/programs/droidcam.nix b/nixos/modules/programs/droidcam.nix
new file mode 100644
index 00000000000..9843a1f5be2
--- /dev/null
+++ b/nixos/modules/programs/droidcam.nix
@@ -0,0 +1,16 @@
+{ lib, pkgs, config, ... }:
+
+with lib;
+
+{
+  options.programs.droidcam = {
+    enable = mkEnableOption "DroidCam client";
+  };
+
+  config = lib.mkIf config.programs.droidcam.enable {
+    environment.systemPackages = [ pkgs.droidcam ];
+
+    boot.extraModulePackages = [ config.boot.kernelPackages.v4l2loopback ];
+    boot.kernelModules = [ "v4l2loopback" "snd-aloop" ];
+  };
+}
diff --git a/nixos/modules/programs/environment.nix b/nixos/modules/programs/environment.nix
new file mode 100644
index 00000000000..a448727be77
--- /dev/null
+++ b/nixos/modules/programs/environment.nix
@@ -0,0 +1,67 @@
+# This module defines a standard configuration for NixOS global environment.
+
+# Most of the stuff here should probably be moved elsewhere sometime.
+
+{ config, lib, ... }:
+
+with lib;
+
+let
+
+  cfg = config.environment;
+
+in
+
+{
+
+  config = {
+
+    environment.variables =
+      { NIXPKGS_CONFIG = "/etc/nix/nixpkgs-config.nix";
+        # note: many programs exec() this directly, so default options for less must not
+        # be specified here; do so in the default value of programs.less.envVariables instead
+        PAGER = mkDefault "less";
+        EDITOR = mkDefault "nano";
+        XDG_CONFIG_DIRS = [ "/etc/xdg" ]; # needs to be before profile-relative paths to allow changes through environment.etc
+      };
+
+    # since we set PAGER to this above, make sure it's installed
+    programs.less.enable = true;
+
+    environment.profiles = mkAfter
+      [ "/nix/var/nix/profiles/default"
+        "/run/current-system/sw"
+      ];
+
+    # TODO: move most of these elsewhere
+    environment.profileRelativeSessionVariables =
+      { PATH = [ "/bin" ];
+        INFOPATH = [ "/info" "/share/info" ];
+        KDEDIRS = [ "" ];
+        QT_PLUGIN_PATH = [ "/lib/qt4/plugins" "/lib/kde4/plugins" ];
+        QTWEBKIT_PLUGIN_PATH = [ "/lib/mozilla/plugins/" ];
+        GTK_PATH = [ "/lib/gtk-2.0" "/lib/gtk-3.0" "/lib/gtk-4.0" ];
+        XDG_CONFIG_DIRS = [ "/etc/xdg" ];
+        XDG_DATA_DIRS = [ "/share" ];
+        MOZ_PLUGIN_PATH = [ "/lib/mozilla/plugins" ];
+        LIBEXEC_PATH = [ "/lib/libexec" ];
+      };
+
+    environment.pathsToLink = [ "/lib/gtk-2.0" "/lib/gtk-3.0" "/lib/gtk-4.0" ];
+
+    environment.extraInit =
+      ''
+         unset ASPELL_CONF
+         for i in ${concatStringsSep " " (reverseList cfg.profiles)} ; do
+           if [ -d "$i/lib/aspell" ]; then
+             export ASPELL_CONF="dict-dir $i/lib/aspell"
+           fi
+         done
+
+         export NIX_USER_PROFILE_DIR="/nix/var/nix/profiles/per-user/$USER"
+         export NIX_PROFILES="${concatStringsSep " " (reverseList cfg.profiles)}"
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/programs/evince.nix b/nixos/modules/programs/evince.nix
new file mode 100644
index 00000000000..c033230afb1
--- /dev/null
+++ b/nixos/modules/programs/evince.nix
@@ -0,0 +1,51 @@
+# Evince.
+
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let cfg = config.programs.evince;
+
+in {
+
+  # Added 2019-08-09
+  imports = [
+    (mkRenamedOptionModule
+      [ "services" "gnome3" "evince" "enable" ]
+      [ "programs" "evince" "enable" ])
+  ];
+
+  ###### interface
+
+  options = {
+
+    programs.evince = {
+
+      enable = mkEnableOption
+        "Evince, the GNOME document viewer";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.evince;
+        defaultText = literalExpression "pkgs.evince";
+        description = "Evince derivation to use.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.programs.evince.enable {
+
+    environment.systemPackages = [ cfg.package ];
+
+    services.dbus.packages = [ cfg.package ];
+
+    systemd.packages = [ cfg.package ];
+
+  };
+
+}
diff --git a/nixos/modules/programs/extra-container.nix b/nixos/modules/programs/extra-container.nix
new file mode 100644
index 00000000000..c10ccd76916
--- /dev/null
+++ b/nixos/modules/programs/extra-container.nix
@@ -0,0 +1,17 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.programs.extra-container;
+in {
+  options = {
+    programs.extra-container.enable = mkEnableOption ''
+      extra-container, a tool for running declarative NixOS containers
+      without host system rebuilds
+    '';
+  };
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.extra-container ];
+    boot.extraSystemdUnitPaths = [ "/etc/systemd-mutable/system" ];
+  };
+}
diff --git a/nixos/modules/programs/feedbackd.nix b/nixos/modules/programs/feedbackd.nix
new file mode 100644
index 00000000000..4194080c8a7
--- /dev/null
+++ b/nixos/modules/programs/feedbackd.nix
@@ -0,0 +1,33 @@
+{ pkgs, lib, config, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.feedbackd;
+in {
+  options = {
+    programs.feedbackd = {
+      enable = mkEnableOption ''
+        Whether to enable the feedbackd D-BUS service and udev rules.
+
+        Your user needs to be in the `feedbackd` group to trigger effects.
+      '';
+      package = mkOption {
+        description = ''
+          Which feedbackd package to use.
+        '';
+        type = types.package;
+        default = pkgs.feedbackd;
+        defaultText = literalExpression "pkgs.feedbackd";
+      };
+    };
+  };
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+
+    services.dbus.packages = [ cfg.package ];
+    services.udev.packages = [ cfg.package ];
+
+    users.groups.feedbackd = {};
+  };
+}
diff --git a/nixos/modules/programs/file-roller.nix b/nixos/modules/programs/file-roller.nix
new file mode 100644
index 00000000000..3c47d598165
--- /dev/null
+++ b/nixos/modules/programs/file-roller.nix
@@ -0,0 +1,48 @@
+# File Roller.
+
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let cfg = config.programs.file-roller;
+
+in {
+
+  # Added 2019-08-09
+  imports = [
+    (mkRenamedOptionModule
+      [ "services" "gnome3" "file-roller" "enable" ]
+      [ "programs" "file-roller" "enable" ])
+  ];
+
+  ###### interface
+
+  options = {
+
+    programs.file-roller = {
+
+      enable = mkEnableOption "File Roller, an archive manager for GNOME";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.gnome.file-roller;
+        defaultText = literalExpression "pkgs.gnome.file-roller";
+        description = "File Roller derivation to use.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ cfg.package ];
+
+    services.dbus.packages = [ cfg.package ];
+
+  };
+
+}
diff --git a/nixos/modules/programs/firejail.nix b/nixos/modules/programs/firejail.nix
new file mode 100644
index 00000000000..76b42168c19
--- /dev/null
+++ b/nixos/modules/programs/firejail.nix
@@ -0,0 +1,97 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.firejail;
+
+  wrappedBins = pkgs.runCommand "firejail-wrapped-binaries"
+    { preferLocalBuild = true;
+      allowSubstitutes = false;
+    }
+    ''
+      mkdir -p $out/bin
+      ${lib.concatStringsSep "\n" (lib.mapAttrsToList (command: value:
+      let
+        opts = if builtins.isAttrs value
+        then value
+        else { executable = value; profile = null; extraArgs = []; };
+        args = lib.escapeShellArgs (
+          opts.extraArgs
+          ++ (optional (opts.profile != null) "--profile=${toString opts.profile}")
+          );
+      in
+      ''
+        cat <<_EOF >$out/bin/${command}
+        #! ${pkgs.runtimeShell} -e
+        exec /run/wrappers/bin/firejail ${args} -- ${toString opts.executable} "\$@"
+        _EOF
+        chmod 0755 $out/bin/${command}
+      '') cfg.wrappedBinaries)}
+    '';
+
+in {
+  options.programs.firejail = {
+    enable = mkEnableOption "firejail";
+
+    wrappedBinaries = mkOption {
+      type = types.attrsOf (types.either types.path (types.submodule {
+        options = {
+          executable = mkOption {
+            type = types.path;
+            description = "Executable to run sandboxed";
+            example = literalExpression ''"''${lib.getBin pkgs.firefox}/bin/firefox"'';
+          };
+          profile = mkOption {
+            type = types.nullOr types.path;
+            default = null;
+            description = "Profile to use";
+            example = literalExpression ''"''${pkgs.firejail}/etc/firejail/firefox.profile"'';
+          };
+          extraArgs = mkOption {
+            type = types.listOf types.str;
+            default = [];
+            description = "Extra arguments to pass to firejail";
+            example = [ "--private=~/.firejail_home" ];
+          };
+        };
+      }));
+      default = {};
+      example = literalExpression ''
+        {
+          firefox = {
+            executable = "''${lib.getBin pkgs.firefox}/bin/firefox";
+            profile = "''${pkgs.firejail}/etc/firejail/firefox.profile";
+          };
+          mpv = {
+            executable = "''${lib.getBin pkgs.mpv}/bin/mpv";
+            profile = "''${pkgs.firejail}/etc/firejail/mpv.profile";
+          };
+        }
+      '';
+      description = ''
+        Wrap the binaries in firejail and place them in the global path.
+        </para>
+        <para>
+        You will get file collisions if you put the actual application binary in
+        the global environment (such as by adding the application package to
+        <code>environment.systemPackages</code>), and applications started via
+        .desktop files are not wrapped if they specify the absolute path to the
+        binary.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    security.wrappers.firejail =
+      { setuid = true;
+        owner = "root";
+        group = "root";
+        source = "${lib.getBin pkgs.firejail}/bin/firejail";
+      };
+
+    environment.systemPackages = [ pkgs.firejail ] ++ [ wrappedBins ];
+  };
+
+  meta.maintainers = with maintainers; [ peterhoeg ];
+}
diff --git a/nixos/modules/programs/fish.nix b/nixos/modules/programs/fish.nix
new file mode 100644
index 00000000000..8dd7101947f
--- /dev/null
+++ b/nixos/modules/programs/fish.nix
@@ -0,0 +1,320 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfge = config.environment;
+
+  cfg = config.programs.fish;
+
+  fishAbbrs = concatStringsSep "\n" (
+    mapAttrsFlatten (k: v: "abbr -ag ${k} ${escapeShellArg v}")
+      cfg.shellAbbrs
+  );
+
+  fishAliases = concatStringsSep "\n" (
+    mapAttrsFlatten (k: v: "alias ${k} ${escapeShellArg v}")
+      (filterAttrs (k: v: v != null) cfg.shellAliases)
+  );
+
+  envShellInit = pkgs.writeText "shellInit" cfge.shellInit;
+
+  envLoginShellInit = pkgs.writeText "loginShellInit" cfge.loginShellInit;
+
+  envInteractiveShellInit = pkgs.writeText "interactiveShellInit" cfge.interactiveShellInit;
+
+  sourceEnv = file:
+  if cfg.useBabelfish then
+    "source /etc/fish/${file}.fish"
+  else
+    ''
+      set fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d $fish_function_path
+      fenv source /etc/fish/foreign-env/${file} > /dev/null
+      set -e fish_function_path[1]
+    '';
+
+  babelfishTranslate = path: name:
+    pkgs.runCommand "${name}.fish" {
+      nativeBuildInputs = [ pkgs.babelfish ];
+    } "${pkgs.babelfish}/bin/babelfish < ${path} > $out;";
+
+in
+
+{
+
+  options = {
+
+    programs.fish = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to configure fish as an interactive shell.
+        '';
+        type = types.bool;
+      };
+
+      useBabelfish = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If enabled, the configured environment will be translated to native fish using <link xlink:href="https://github.com/bouk/babelfish">babelfish</link>.
+          Otherwise, <link xlink:href="https://github.com/oh-my-fish/plugin-foreign-env">foreign-env</link> will be used.
+        '';
+      };
+
+      vendor.config.enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether fish should source configuration snippets provided by other packages.
+        '';
+      };
+
+      vendor.completions.enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether fish should use completion files provided by other packages.
+        '';
+      };
+
+      vendor.functions.enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether fish should autoload fish functions provided by other packages.
+        '';
+      };
+
+      shellAbbrs = mkOption {
+        default = {};
+        example = {
+          gco = "git checkout";
+          npu = "nix-prefetch-url";
+        };
+        description = ''
+          Set of fish abbreviations.
+        '';
+        type = with types; attrsOf str;
+      };
+
+      shellAliases = mkOption {
+        default = {};
+        description = ''
+          Set of aliases for fish shell, which overrides <option>environment.shellAliases</option>.
+          See <option>environment.shellAliases</option> for an option format description.
+        '';
+        type = with types; attrsOf (nullOr (either str path));
+      };
+
+      shellInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code called during fish shell initialisation.
+        '';
+        type = types.lines;
+      };
+
+      loginShellInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code called during fish login shell initialisation.
+        '';
+        type = types.lines;
+      };
+
+      interactiveShellInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code called during interactive fish shell initialisation.
+        '';
+        type = types.lines;
+      };
+
+      promptInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code used to initialise fish prompt.
+        '';
+        type = types.lines;
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    programs.fish.shellAliases = mapAttrs (name: mkDefault) cfge.shellAliases;
+
+    # Required for man completions
+    documentation.man.generateCaches = lib.mkDefault true;
+
+    environment = mkMerge [
+      (mkIf cfg.useBabelfish
+      {
+        etc."fish/setEnvironment.fish".source = babelfishTranslate config.system.build.setEnvironment "setEnvironment";
+        etc."fish/shellInit.fish".source = babelfishTranslate envShellInit "shellInit";
+        etc."fish/loginShellInit.fish".source = babelfishTranslate envLoginShellInit "loginShellInit";
+        etc."fish/interactiveShellInit.fish".source = babelfishTranslate envInteractiveShellInit "interactiveShellInit";
+     })
+
+      (mkIf (!cfg.useBabelfish)
+      {
+        etc."fish/foreign-env/shellInit".source = envShellInit;
+        etc."fish/foreign-env/loginShellInit".source = envLoginShellInit;
+        etc."fish/foreign-env/interactiveShellInit".source = envInteractiveShellInit;
+      })
+
+      {
+        etc."fish/nixos-env-preinit.fish".text =
+        if cfg.useBabelfish
+        then ''
+          # source the NixOS environment config
+          if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]
+            source /etc/fish/setEnvironment.fish
+          end
+        ''
+        else ''
+          # This happens before $__fish_datadir/config.fish sets fish_function_path, so it is currently
+          # unset. We set it and then completely erase it, leaving its configuration to $__fish_datadir/config.fish
+          set fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d $__fish_datadir/functions
+
+          # source the NixOS environment config
+          if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]
+            fenv source ${config.system.build.setEnvironment}
+          end
+
+          # clear fish_function_path so that it will be correctly set when we return to $__fish_datadir/config.fish
+          set -e fish_function_path
+        '';
+      }
+
+      {
+        etc."fish/config.fish".text = ''
+        # /etc/fish/config.fish: DO NOT EDIT -- this file has been generated automatically.
+
+        # if we haven't sourced the general config, do it
+        if not set -q __fish_nixos_general_config_sourced
+          ${sourceEnv "shellInit"}
+
+          ${cfg.shellInit}
+
+          # and leave a note so we don't source this config section again from
+          # this very shell (children will source the general config anew)
+          set -g __fish_nixos_general_config_sourced 1
+        end
+
+        # if we haven't sourced the login config, do it
+        status --is-login; and not set -q __fish_nixos_login_config_sourced
+        and begin
+          ${sourceEnv "loginShellInit"}
+
+          ${cfg.loginShellInit}
+
+          # and leave a note so we don't source this config section again from
+          # this very shell (children will source the general config anew)
+          set -g __fish_nixos_login_config_sourced 1
+        end
+
+        # if we haven't sourced the interactive config, do it
+        status --is-interactive; and not set -q __fish_nixos_interactive_config_sourced
+        and begin
+          ${fishAbbrs}
+          ${fishAliases}
+
+          ${sourceEnv "interactiveShellInit"}
+
+          ${cfg.promptInit}
+          ${cfg.interactiveShellInit}
+
+          # and leave a note so we don't source this config section again from
+          # this very shell (children will source the general config anew,
+          # allowing configuration changes in, e.g, aliases, to propagate)
+          set -g __fish_nixos_interactive_config_sourced 1
+        end
+      '';
+      }
+
+      {
+        etc."fish/generated_completions".source =
+        let
+          patchedGenerator = pkgs.stdenv.mkDerivation {
+            name = "fish_patched-completion-generator";
+            srcs = [
+              "${pkgs.fish}/share/fish/tools/create_manpage_completions.py"
+              "${pkgs.fish}/share/fish/tools/deroff.py"
+            ];
+            unpackCmd = "cp $curSrc $(basename $curSrc)";
+            sourceRoot = ".";
+            patches = [ ./fish_completion-generator.patch ]; # to prevent collisions of identical completion files
+            dontBuild = true;
+            installPhase = ''
+              mkdir -p $out
+              cp * $out/
+            '';
+            preferLocalBuild = true;
+            allowSubstitutes = false;
+          };
+          generateCompletions = package: pkgs.runCommand
+            "${package.name}_fish-completions"
+            (
+              {
+                inherit package;
+                preferLocalBuild = true;
+                allowSubstitutes = false;
+              }
+              // optionalAttrs (package ? meta.priority) { meta.priority = package.meta.priority; }
+            )
+            ''
+              mkdir -p $out
+              if [ -d $package/share/man ]; then
+                find $package/share/man -type f | xargs ${pkgs.python3.interpreter} ${patchedGenerator}/create_manpage_completions.py --directory $out >/dev/null
+              fi
+            '';
+        in
+          pkgs.buildEnv {
+            name = "system_fish-completions";
+            ignoreCollisions = true;
+            paths = map generateCompletions config.environment.systemPackages;
+          };
+      }
+
+      # include programs that bring their own completions
+      {
+        pathsToLink = []
+        ++ optional cfg.vendor.config.enable "/share/fish/vendor_conf.d"
+        ++ optional cfg.vendor.completions.enable "/share/fish/vendor_completions.d"
+        ++ optional cfg.vendor.functions.enable "/share/fish/vendor_functions.d";
+      }
+
+      { systemPackages = [ pkgs.fish ]; }
+
+      {
+        shells = [
+          "/run/current-system/sw/bin/fish"
+          "${pkgs.fish}/bin/fish"
+        ];
+      }
+    ];
+
+    programs.fish.interactiveShellInit = ''
+      # add completions generated by NixOS to $fish_complete_path
+      begin
+        # joins with null byte to acommodate all characters in paths, then respectively gets all paths before (exclusive) / after (inclusive) the first one including "generated_completions",
+        # splits by null byte, and then removes all empty lines produced by using 'string'
+        set -l prev (string join0 $fish_complete_path | string match --regex "^.*?(?=\x00[^\x00]*generated_completions.*)" | string split0 | string match -er ".")
+        set -l post (string join0 $fish_complete_path | string match --regex "[^\x00]*generated_completions.*" | string split0 | string match -er ".")
+        set fish_complete_path $prev "/etc/fish/generated_completions" $post
+      end
+      # prevent fish from generating completions on first run
+      if not test -d $__fish_user_data_dir/generated_completions
+        ${pkgs.coreutils}/bin/mkdir $__fish_user_data_dir/generated_completions
+      end
+    '';
+
+  };
+
+}
diff --git a/nixos/modules/programs/fish_completion-generator.patch b/nixos/modules/programs/fish_completion-generator.patch
new file mode 100644
index 00000000000..fa207e484c9
--- /dev/null
+++ b/nixos/modules/programs/fish_completion-generator.patch
@@ -0,0 +1,14 @@
+--- a/create_manpage_completions.py
++++ b/create_manpage_completions.py
+@@ -879,10 +879,6 @@ def parse_manpage_at_path(manpage_path, output_directory):
+                 )
+                 return False
+ 
+-        # Output the magic word Autogenerated so we can tell if we can overwrite this
+-        built_command_output.insert(
+-            0, "# " + CMDNAME + "\n# Autogenerated from man page " + manpage_path
+-        )
+         # built_command_output.insert(2, "# using " + parser.__class__.__name__) # XXX MISATTRIBUTES THE CULPABLE PARSER! Was really using Type2 but reporting TypeDeroffManParser
+ 
+         for line in built_command_output:
+
diff --git a/nixos/modules/programs/flashrom.nix b/nixos/modules/programs/flashrom.nix
new file mode 100644
index 00000000000..f026c2e31cd
--- /dev/null
+++ b/nixos/modules/programs/flashrom.nix
@@ -0,0 +1,26 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.flashrom;
+in
+{
+  options.programs.flashrom = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Installs flashrom and configures udev rules for programmers
+        used by flashrom. Grants access to users in the "flashrom"
+        group.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.udev.packages = [ pkgs.flashrom ];
+    environment.systemPackages = [ pkgs.flashrom ];
+    users.groups.flashrom = { };
+  };
+}
diff --git a/nixos/modules/programs/flexoptix-app.nix b/nixos/modules/programs/flexoptix-app.nix
new file mode 100644
index 00000000000..5e169be2d89
--- /dev/null
+++ b/nixos/modules/programs/flexoptix-app.nix
@@ -0,0 +1,25 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.flexoptix-app;
+in {
+  options = {
+    programs.flexoptix-app = {
+      enable = mkEnableOption "FLEXOPTIX app + udev rules";
+
+      package = mkOption {
+        description = "FLEXOPTIX app package to use";
+        type = types.package;
+        default = pkgs.flexoptix-app;
+        defaultText = literalExpression "pkgs.flexoptix-app";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+    services.udev.packages = [ cfg.package ];
+  };
+}
diff --git a/nixos/modules/programs/freetds.nix b/nixos/modules/programs/freetds.nix
new file mode 100644
index 00000000000..d95c44d756a
--- /dev/null
+++ b/nixos/modules/programs/freetds.nix
@@ -0,0 +1,61 @@
+# Global configuration for freetds environment.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.environment.freetds;
+
+in
+{
+  ###### interface
+
+  options = {
+
+    environment.freetds = mkOption {
+      type = types.attrsOf types.str;
+      default = {};
+      example = literalExpression ''
+        { MYDATABASE = '''
+            host = 10.0.2.100
+            port = 1433
+            tds version = 7.2
+          ''';
+        }
+      '';
+      description =
+        ''
+        Configure freetds database entries. Each attribute denotes
+        a section within freetds.conf, and the value (a string) is the config
+        content for that section. When at least one entry is configured
+        the global environment variables FREETDSCONF, FREETDS and SYBASE
+        will be configured to allow the programs that use freetds to find the
+        library and config.
+        '';
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf (length (attrNames cfg) > 0) {
+
+    environment.variables.FREETDSCONF = "/etc/freetds.conf";
+    environment.variables.FREETDS = "/etc/freetds.conf";
+    environment.variables.SYBASE = "${pkgs.freetds}";
+
+    environment.etc."freetds.conf" = { text =
+      (concatStrings (mapAttrsToList (name: value:
+        ''
+        [${name}]
+        ${value}
+        ''
+      ) cfg));
+    };
+
+  };
+
+}
diff --git a/nixos/modules/programs/fuse.nix b/nixos/modules/programs/fuse.nix
new file mode 100644
index 00000000000..c15896efbb5
--- /dev/null
+++ b/nixos/modules/programs/fuse.nix
@@ -0,0 +1,37 @@
+{ config, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.fuse;
+in {
+  meta.maintainers = with maintainers; [ primeos ];
+
+  options.programs.fuse = {
+    mountMax = mkOption {
+      # In the C code it's an "int" (i.e. signed and at least 16 bit), but
+      # negative numbers obviously make no sense:
+      type = types.ints.between 0 32767; # 2^15 - 1
+      default = 1000;
+      description = ''
+        Set the maximum number of FUSE mounts allowed to non-root users.
+      '';
+    };
+
+    userAllowOther = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Allow non-root users to specify the allow_other or allow_root mount
+        options, see mount.fuse3(8).
+      '';
+    };
+  };
+
+  config =  {
+    environment.etc."fuse.conf".text = ''
+      ${optionalString (!cfg.userAllowOther) "#"}user_allow_other
+      mount_max = ${toString cfg.mountMax}
+    '';
+  };
+}
diff --git a/nixos/modules/programs/gamemode.nix b/nixos/modules/programs/gamemode.nix
new file mode 100644
index 00000000000..a377a1619aa
--- /dev/null
+++ b/nixos/modules/programs/gamemode.nix
@@ -0,0 +1,98 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.gamemode;
+  settingsFormat = pkgs.formats.ini { };
+  configFile = settingsFormat.generate "gamemode.ini" cfg.settings;
+in
+{
+  options = {
+    programs.gamemode = {
+      enable = mkEnableOption "GameMode to optimise system performance on demand";
+
+      enableRenice = mkEnableOption "CAP_SYS_NICE on gamemoded to support lowering process niceness" // {
+        default = true;
+      };
+
+      settings = mkOption {
+        type = settingsFormat.type;
+        default = {};
+        description = ''
+          System-wide configuration for GameMode (/etc/gamemode.ini).
+          See gamemoded(8) man page for available settings.
+        '';
+        example = literalExpression ''
+          {
+            general = {
+              renice = 10;
+            };
+
+            # Warning: GPU optimisations have the potential to damage hardware
+            gpu = {
+              apply_gpu_optimisations = "accept-responsibility";
+              gpu_device = 0;
+              amd_performance_level = "high";
+            };
+
+            custom = {
+              start = "''${pkgs.libnotify}/bin/notify-send 'GameMode started'";
+              end = "''${pkgs.libnotify}/bin/notify-send 'GameMode ended'";
+            };
+          }
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment = {
+      systemPackages = [ pkgs.gamemode ];
+      etc."gamemode.ini".source = configFile;
+    };
+
+    security = {
+      polkit.enable = true;
+      wrappers = mkIf cfg.enableRenice {
+        gamemoded = {
+          owner = "root";
+          group = "root";
+          source = "${pkgs.gamemode}/bin/gamemoded";
+          capabilities = "cap_sys_nice+ep";
+        };
+      };
+    };
+
+    systemd = {
+      packages = [ pkgs.gamemode ];
+      user.services.gamemoded = {
+        # The upstream service already defines this, but doesn't get applied.
+        # See https://github.com/NixOS/nixpkgs/issues/81138
+        wantedBy = [ "default.target" ];
+
+        # Use pkexec from the security wrappers to allow users to
+        # run libexec/cpugovctl & libexec/gpuclockctl as root with
+        # the the actions defined in share/polkit-1/actions.
+        #
+        # This uses a link farm to make sure other wrapped executables
+        # aren't included in PATH.
+        environment.PATH = mkForce (pkgs.linkFarm "pkexec" [
+          {
+            name = "pkexec";
+            path = "${config.security.wrapperDir}/pkexec";
+          }
+        ]);
+
+        serviceConfig.ExecStart = mkIf cfg.enableRenice [
+          "" # Tell systemd to clear the existing ExecStart list, to prevent appending to it.
+          "${config.security.wrapperDir}/gamemoded"
+        ];
+      };
+    };
+  };
+
+  meta = {
+    maintainers = with maintainers; [ kira-bruneau ];
+  };
+}
diff --git a/nixos/modules/programs/geary.nix b/nixos/modules/programs/geary.nix
new file mode 100644
index 00000000000..407680c30dc
--- /dev/null
+++ b/nixos/modules/programs/geary.nix
@@ -0,0 +1,24 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.geary;
+
+in {
+  meta = {
+    maintainers = teams.gnome.members;
+  };
+
+  options = {
+    programs.geary.enable = mkEnableOption "Geary, a Mail client for GNOME 3";
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.gnome.geary ];
+    programs.dconf.enable = true;
+    services.gnome.gnome-keyring.enable = true;
+    services.gnome.gnome-online-accounts.enable = true;
+  };
+}
+
diff --git a/nixos/modules/programs/git.nix b/nixos/modules/programs/git.nix
new file mode 100644
index 00000000000..06ce374b199
--- /dev/null
+++ b/nixos/modules/programs/git.nix
@@ -0,0 +1,69 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.git;
+in
+
+{
+  options = {
+    programs.git = {
+      enable = mkEnableOption "git";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.git;
+        defaultText = literalExpression "pkgs.git";
+        example = literalExpression "pkgs.gitFull";
+        description = "The git package to use";
+      };
+
+      config = mkOption {
+        type = with types; attrsOf (attrsOf anything);
+        default = { };
+        example = {
+          init.defaultBranch = "main";
+          url."https://github.com/".insteadOf = [ "gh:" "github:" ];
+        };
+        description = ''
+          Configuration to write to /etc/gitconfig. See the CONFIGURATION FILE
+          section of git-config(1) for more information.
+        '';
+      };
+
+      lfs = {
+        enable = mkEnableOption "git-lfs";
+
+        package = mkOption {
+          type = types.package;
+          default = pkgs.git-lfs;
+          defaultText = literalExpression "pkgs.git-lfs";
+          description = "The git-lfs package to use";
+        };
+      };
+    };
+  };
+
+  config = mkMerge [
+    (mkIf cfg.enable {
+      environment.systemPackages = [ cfg.package ];
+      environment.etc.gitconfig = mkIf (cfg.config != {}) {
+        text = generators.toGitINI cfg.config;
+      };
+    })
+    (mkIf (cfg.enable && cfg.lfs.enable) {
+      environment.systemPackages = [ cfg.lfs.package ];
+      programs.git.config = {
+        filter.lfs = {
+          clean = "git-lfs clean -- %f";
+          smudge = "git-lfs smudge -- %f";
+          process = "git-lfs filter-process";
+          required = true;
+        };
+      };
+    })
+  ];
+
+  meta.maintainers = with maintainers; [ figsoda ];
+}
diff --git a/nixos/modules/programs/gnome-disks.nix b/nixos/modules/programs/gnome-disks.nix
new file mode 100644
index 00000000000..4b128b47126
--- /dev/null
+++ b/nixos/modules/programs/gnome-disks.nix
@@ -0,0 +1,50 @@
+# GNOME Disks.
+
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+{
+
+  meta = {
+    maintainers = teams.gnome.members;
+  };
+
+  # Added 2019-08-09
+  imports = [
+    (mkRenamedOptionModule
+      [ "services" "gnome3" "gnome-disks" "enable" ]
+      [ "programs" "gnome-disks" "enable" ])
+  ];
+
+  ###### interface
+
+  options = {
+
+    programs.gnome-disks = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable GNOME Disks daemon, a program designed to
+          be a UDisks2 graphical front-end.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.programs.gnome-disks.enable {
+
+    environment.systemPackages = [ pkgs.gnome.gnome-disk-utility ];
+
+    services.dbus.packages = [ pkgs.gnome.gnome-disk-utility ];
+
+  };
+
+}
diff --git a/nixos/modules/programs/gnome-documents.nix b/nixos/modules/programs/gnome-documents.nix
new file mode 100644
index 00000000000..43ad3163efd
--- /dev/null
+++ b/nixos/modules/programs/gnome-documents.nix
@@ -0,0 +1,54 @@
+# GNOME Documents.
+
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+{
+
+  meta = {
+    maintainers = teams.gnome.members;
+  };
+
+  # Added 2019-08-09
+  imports = [
+    (mkRenamedOptionModule
+      [ "services" "gnome" "gnome-documents" "enable" ]
+      [ "programs" "gnome-documents" "enable" ])
+  ];
+
+  ###### interface
+
+  options = {
+
+    programs.gnome-documents = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable GNOME Documents, a document
+          manager application for GNOME.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.programs.gnome-documents.enable {
+
+    environment.systemPackages = [ pkgs.gnome.gnome-documents ];
+
+    services.dbus.packages = [ pkgs.gnome.gnome-documents ];
+
+    services.gnome.gnome-online-accounts.enable = true;
+
+    services.gnome.gnome-online-miners.enable = true;
+
+  };
+
+}
diff --git a/nixos/modules/programs/gnome-terminal.nix b/nixos/modules/programs/gnome-terminal.nix
new file mode 100644
index 00000000000..71a6b217880
--- /dev/null
+++ b/nixos/modules/programs/gnome-terminal.nix
@@ -0,0 +1,38 @@
+# GNOME Terminal.
+
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+
+  cfg = config.programs.gnome-terminal;
+
+in
+
+{
+
+  meta = {
+    maintainers = teams.gnome.members;
+  };
+
+  # Added 2019-08-19
+  imports = [
+    (mkRenamedOptionModule
+      [ "services" "gnome3" "gnome-terminal-server" "enable" ]
+      [ "programs" "gnome-terminal" "enable" ])
+  ];
+
+  options = {
+    programs.gnome-terminal.enable = mkEnableOption "GNOME Terminal";
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.gnome.gnome-terminal ];
+    services.dbus.packages = [ pkgs.gnome.gnome-terminal ];
+    systemd.packages = [ pkgs.gnome.gnome-terminal ];
+
+    programs.bash.vteIntegration = true;
+    programs.zsh.vteIntegration = true;
+  };
+}
diff --git a/nixos/modules/programs/gnupg.nix b/nixos/modules/programs/gnupg.nix
new file mode 100644
index 00000000000..b41f30287ea
--- /dev/null
+++ b/nixos/modules/programs/gnupg.nix
@@ -0,0 +1,154 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.programs.gnupg;
+
+  xserverCfg = config.services.xserver;
+
+  defaultPinentryFlavor =
+    if xserverCfg.desktopManager.lxqt.enable
+    || xserverCfg.desktopManager.plasma5.enable then
+      "qt"
+    else if xserverCfg.desktopManager.xfce.enable then
+      "gtk2"
+    else if xserverCfg.enable || config.programs.sway.enable then
+      "gnome3"
+    else
+      null;
+
+in
+
+{
+
+  options.programs.gnupg = {
+    package = mkOption {
+      type = types.package;
+      default = pkgs.gnupg;
+      defaultText = literalExpression "pkgs.gnupg";
+      description = ''
+        The gpg package that should be used.
+      '';
+    };
+
+    agent.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enables GnuPG agent with socket-activation for every user session.
+      '';
+    };
+
+    agent.enableSSHSupport = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enable SSH agent support in GnuPG agent. Also sets SSH_AUTH_SOCK
+        environment variable correctly. This will disable socket-activation
+        and thus always start a GnuPG agent per user session.
+      '';
+    };
+
+    agent.enableExtraSocket = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enable extra socket for GnuPG agent.
+      '';
+    };
+
+    agent.enableBrowserSocket = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enable browser socket for GnuPG agent.
+      '';
+    };
+
+    agent.pinentryFlavor = mkOption {
+      type = types.nullOr (types.enum pkgs.pinentry.flavors);
+      example = "gnome3";
+      default = defaultPinentryFlavor;
+      defaultText = literalDocBook ''matching the configured desktop environment'';
+      description = ''
+        Which pinentry interface to use. If not null, the path to the
+        pinentry binary will be passed to gpg-agent via commandline and
+        thus overrides the pinentry option in gpg-agent.conf in the user's
+        home directory.
+        If not set at all, it'll pick an appropriate flavor depending on the
+        system configuration (qt flavor for lxqt and plasma5, gtk2 for xfce
+        4.12, gnome3 on all other systems with X enabled, ncurses otherwise).
+      '';
+    };
+
+    dirmngr.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enables GnuPG network certificate management daemon with socket-activation for every user session.
+      '';
+    };
+  };
+
+  config = mkIf cfg.agent.enable {
+    # This overrides the systemd user unit shipped with the gnupg package
+    systemd.user.services.gpg-agent = mkIf (cfg.agent.pinentryFlavor != null) {
+      serviceConfig.ExecStart = [ "" ''
+        ${cfg.package}/bin/gpg-agent --supervised \
+          --pinentry-program ${pkgs.pinentry.${cfg.agent.pinentryFlavor}}/bin/pinentry
+      '' ];
+    };
+
+    systemd.user.sockets.gpg-agent = {
+      wantedBy = [ "sockets.target" ];
+    };
+
+    systemd.user.sockets.gpg-agent-ssh = mkIf cfg.agent.enableSSHSupport {
+      wantedBy = [ "sockets.target" ];
+    };
+
+    systemd.user.sockets.gpg-agent-extra = mkIf cfg.agent.enableExtraSocket {
+      wantedBy = [ "sockets.target" ];
+    };
+
+    systemd.user.sockets.gpg-agent-browser = mkIf cfg.agent.enableBrowserSocket {
+      wantedBy = [ "sockets.target" ];
+    };
+
+    systemd.user.sockets.dirmngr = mkIf cfg.dirmngr.enable {
+      wantedBy = [ "sockets.target" ];
+    };
+
+    services.dbus.packages = mkIf (cfg.agent.pinentryFlavor == "gnome3") [ pkgs.gcr ];
+
+    environment.systemPackages = with pkgs; [ cfg.package ];
+    systemd.packages = [ cfg.package ];
+
+    environment.interactiveShellInit = ''
+      # Bind gpg-agent to this TTY if gpg commands are used.
+      export GPG_TTY=$(tty)
+
+    '' + (optionalString cfg.agent.enableSSHSupport ''
+      # SSH agent protocol doesn't support changing TTYs, so bind the agent
+      # to every new TTY.
+      ${cfg.package}/bin/gpg-connect-agent --quiet updatestartuptty /bye > /dev/null
+    '');
+
+    environment.extraInit = mkIf cfg.agent.enableSSHSupport ''
+      if [ -z "$SSH_AUTH_SOCK" ]; then
+        export SSH_AUTH_SOCK=$(${cfg.package}/bin/gpgconf --list-dirs agent-ssh-socket)
+      fi
+    '';
+
+    assertions = [
+      { assertion = cfg.agent.enableSSHSupport -> !config.programs.ssh.startAgent;
+        message = "You can't use ssh-agent and GnuPG agent with SSH support enabled at the same time!";
+      }
+    ];
+  };
+
+  # uses attributes of the linked package
+  meta.buildDocsInSandbox = false;
+}
diff --git a/nixos/modules/programs/gpaste.nix b/nixos/modules/programs/gpaste.nix
new file mode 100644
index 00000000000..cff2fb8d003
--- /dev/null
+++ b/nixos/modules/programs/gpaste.nix
@@ -0,0 +1,36 @@
+# GPaste.
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  # Added 2019-08-09
+  imports = [
+    (mkRenamedOptionModule
+      [ "services" "gnome3" "gpaste" "enable" ]
+      [ "programs" "gpaste" "enable" ])
+  ];
+
+  ###### interface
+  options = {
+     programs.gpaste = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable GPaste, a clipboard manager.
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+  config = mkIf config.programs.gpaste.enable {
+    environment.systemPackages = [ pkgs.gnome.gpaste ];
+    services.dbus.packages = [ pkgs.gnome.gpaste ];
+    systemd.packages = [ pkgs.gnome.gpaste ];
+    # gnome-control-center crashes in Keyboard Shortcuts pane without the GSettings schemas.
+    services.xserver.desktopManager.gnome.sessionPath = [ pkgs.gnome.gpaste ];
+  };
+}
diff --git a/nixos/modules/programs/gphoto2.nix b/nixos/modules/programs/gphoto2.nix
new file mode 100644
index 00000000000..93923ff3133
--- /dev/null
+++ b/nixos/modules/programs/gphoto2.nix
@@ -0,0 +1,30 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  meta.maintainers = [ maintainers.league ];
+
+  ###### interface
+  options = {
+    programs.gphoto2 = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to configure system to use gphoto2.
+          To grant digital camera access to a user, the user must
+          be part of the camera group:
+          <code>users.users.alice.extraGroups = ["camera"];</code>
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+  config = mkIf config.programs.gphoto2.enable {
+    services.udev.packages = [ pkgs.libgphoto2 ];
+    environment.systemPackages = [ pkgs.gphoto2 ];
+    users.groups.camera = {};
+  };
+}
diff --git a/nixos/modules/programs/hamster.nix b/nixos/modules/programs/hamster.nix
new file mode 100644
index 00000000000..0bb56ad7ff3
--- /dev/null
+++ b/nixos/modules/programs/hamster.nix
@@ -0,0 +1,15 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  meta.maintainers = pkgs.hamster.meta.maintainers;
+
+  options.programs.hamster.enable =
+    mkEnableOption "hamster, a time tracking program";
+
+  config = lib.mkIf config.programs.hamster.enable {
+    environment.systemPackages = [ pkgs.hamster ];
+    services.dbus.packages = [ pkgs.hamster ];
+  };
+}
diff --git a/nixos/modules/programs/htop.nix b/nixos/modules/programs/htop.nix
new file mode 100644
index 00000000000..5c197838e47
--- /dev/null
+++ b/nixos/modules/programs/htop.nix
@@ -0,0 +1,58 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.programs.htop;
+
+  fmt = value:
+    if isList value then concatStringsSep " " (map fmt value) else
+    if isString value then value else
+    if isBool value || isInt value then toString value else
+    throw "Unrecognized type ${typeOf value} in htop settings";
+
+in
+
+{
+
+  options.programs.htop = {
+    package = mkOption {
+      type = types.package;
+      default = pkgs.htop;
+      defaultText = "pkgs.htop";
+      description = ''
+        The htop package that should be used.
+      '';
+    };
+
+    enable = mkEnableOption "htop process monitor";
+
+    settings = mkOption {
+      type = with types; attrsOf (oneOf [ str int bool (listOf (oneOf [ str int bool ])) ]);
+      default = {};
+      example = {
+        hide_kernel_threads = true;
+        hide_userland_threads = true;
+      };
+      description = ''
+        Extra global default configuration for htop
+        which is read on first startup only.
+        Htop subsequently uses ~/.config/htop/htoprc
+        as configuration source.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [
+      cfg.package
+    ];
+
+    environment.etc."htoprc".text = ''
+      # Global htop configuration
+      # To change set: programs.htop.settings.KEY = VALUE;
+    '' + concatStringsSep "\n" (mapAttrsToList (key: value: "${key}=${fmt value}") cfg.settings);
+  };
+
+}
diff --git a/nixos/modules/programs/iftop.nix b/nixos/modules/programs/iftop.nix
new file mode 100644
index 00000000000..c74714a9a6d
--- /dev/null
+++ b/nixos/modules/programs/iftop.nix
@@ -0,0 +1,20 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.iftop;
+in {
+  options = {
+    programs.iftop.enable = mkEnableOption "iftop + setcap wrapper";
+  };
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.iftop ];
+    security.wrappers.iftop = {
+      owner = "root";
+      group = "root";
+      capabilities = "cap_net_raw+p";
+      source = "${pkgs.iftop}/bin/iftop";
+    };
+  };
+}
diff --git a/nixos/modules/programs/iotop.nix b/nixos/modules/programs/iotop.nix
new file mode 100644
index 00000000000..b7c1c69f9dd
--- /dev/null
+++ b/nixos/modules/programs/iotop.nix
@@ -0,0 +1,19 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.iotop;
+in {
+  options = {
+    programs.iotop.enable = mkEnableOption "iotop + setcap wrapper";
+  };
+  config = mkIf cfg.enable {
+    security.wrappers.iotop = {
+      owner = "root";
+      group = "root";
+      capabilities = "cap_net_admin+p";
+      source = "${pkgs.iotop}/bin/iotop";
+    };
+  };
+}
diff --git a/nixos/modules/programs/java.nix b/nixos/modules/programs/java.nix
new file mode 100644
index 00000000000..4e4e0629e5d
--- /dev/null
+++ b/nixos/modules/programs/java.nix
@@ -0,0 +1,58 @@
+# This module provides JAVA_HOME, with a different way to install java
+# system-wide.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.java;
+in
+
+{
+
+  options = {
+
+    programs.java = {
+
+      enable = mkEnableOption "java" // {
+        description = ''
+          Install and setup the Java development kit.
+          <note>
+          <para>This adds JAVA_HOME to the global environment, by sourcing the
+            jdk's setup-hook on shell init. It is equivalent to starting a shell
+            through 'nix-shell -p jdk', or roughly the following system-wide
+            configuration:
+          </para>
+          <programlisting>
+            environment.variables.JAVA_HOME = ''${pkgs.jdk.home}/lib/openjdk;
+            environment.systemPackages = [ pkgs.jdk ];
+          </programlisting>
+          </note>
+        '';
+      };
+
+      package = mkOption {
+        default = pkgs.jdk;
+        defaultText = literalExpression "pkgs.jdk";
+        description = ''
+          Java package to install. Typical values are pkgs.jdk or pkgs.jre.
+        '';
+        type = types.package;
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ cfg.package ];
+
+    environment.shellInit = ''
+      test -e ${cfg.package}/nix-support/setup-hook && source ${cfg.package}/nix-support/setup-hook
+    '';
+
+  };
+
+}
diff --git a/nixos/modules/programs/k40-whisperer.nix b/nixos/modules/programs/k40-whisperer.nix
new file mode 100644
index 00000000000..3163e45f57e
--- /dev/null
+++ b/nixos/modules/programs/k40-whisperer.nix
@@ -0,0 +1,40 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.k40-whisperer;
+  pkg = cfg.package.override {
+    udevGroup = cfg.group;
+  };
+in
+{
+  options.programs.k40-whisperer = {
+    enable = mkEnableOption "K40-Whisperer";
+
+    group = mkOption {
+      type = types.str;
+      description = ''
+        Group assigned to the device when connected.
+      '';
+      default = "k40";
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.k40-whisperer;
+      defaultText = literalExpression "pkgs.k40-whisperer";
+      example = literalExpression "pkgs.k40-whisperer";
+      description = ''
+        K40 Whisperer package to use.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.groups.${cfg.group} = {};
+
+    environment.systemPackages = [ pkg ];
+    services.udev.packages = [ pkg ];
+  };
+}
diff --git a/nixos/modules/programs/kbdlight.nix b/nixos/modules/programs/kbdlight.nix
new file mode 100644
index 00000000000..8a2a0057cf2
--- /dev/null
+++ b/nixos/modules/programs/kbdlight.nix
@@ -0,0 +1,21 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.kbdlight;
+
+in
+{
+  options.programs.kbdlight.enable = mkEnableOption "kbdlight";
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.kbdlight ];
+    security.wrappers.kbdlight =
+      { setuid = true;
+        owner = "root";
+        group = "root";
+        source = "${pkgs.kbdlight.out}/bin/kbdlight";
+      };
+  };
+}
diff --git a/nixos/modules/programs/kclock.nix b/nixos/modules/programs/kclock.nix
new file mode 100644
index 00000000000..42d81d2798b
--- /dev/null
+++ b/nixos/modules/programs/kclock.nix
@@ -0,0 +1,13 @@
+{ lib, pkgs, config, ... }:
+with lib;
+let
+  cfg = config.programs.kclock;
+  kclockPkg = pkgs.libsForQt5.kclock;
+in {
+  options.programs.kclock = { enable = mkEnableOption "Enable KClock"; };
+
+  config = mkIf cfg.enable {
+    services.dbus.packages = [ kclockPkg ];
+    environment.systemPackages = [ kclockPkg ];
+  };
+}
diff --git a/nixos/modules/programs/kdeconnect.nix b/nixos/modules/programs/kdeconnect.nix
new file mode 100644
index 00000000000..df698e84dd7
--- /dev/null
+++ b/nixos/modules/programs/kdeconnect.nix
@@ -0,0 +1,35 @@
+{ config, pkgs, lib, ... }:
+with lib;
+{
+  options.programs.kdeconnect = {
+    enable = mkEnableOption ''
+      kdeconnect.
+
+      Note that it will open the TCP and UDP port from
+      1714 to 1764 as they are needed for it to function properly.
+      You can use the <option>package</option> to use
+      <code>gnomeExtensions.gsconnect</code> as an alternative
+      implementation if you use Gnome.
+    '';
+    package = mkOption {
+      default = pkgs.kdeconnect;
+      defaultText = literalExpression "pkgs.kdeconnect";
+      type = types.package;
+      example = literalExpression "pkgs.gnomeExtensions.gsconnect";
+      description = ''
+        The package providing the implementation for kdeconnect.
+      '';
+    };
+  };
+  config =
+    let
+      cfg = config.programs.kdeconnect;
+    in
+      mkIf cfg.enable {
+        environment.systemPackages = [ cfg.package ];
+        networking.firewall = rec {
+          allowedTCPPortRanges = [ { from = 1714; to = 1764; } ];
+          allowedUDPPortRanges = allowedTCPPortRanges;
+        };
+      };
+}
diff --git a/nixos/modules/programs/less.nix b/nixos/modules/programs/less.nix
new file mode 100644
index 00000000000..794146b19fa
--- /dev/null
+++ b/nixos/modules/programs/less.nix
@@ -0,0 +1,134 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.programs.less;
+
+  configText = if (cfg.configFile != null) then (builtins.readFile cfg.configFile) else ''
+    #command
+    ${concatStringsSep "\n"
+      (mapAttrsToList (command: action: "${command} ${action}") cfg.commands)
+    }
+    ${if cfg.clearDefaultCommands then "#stop" else ""}
+
+    #line-edit
+    ${concatStringsSep "\n"
+      (mapAttrsToList (command: action: "${command} ${action}") cfg.lineEditingKeys)
+    }
+
+    #env
+    ${concatStringsSep "\n"
+      (mapAttrsToList (variable: values: "${variable}=${values}") cfg.envVariables)
+    }
+  '';
+
+  lessKey = pkgs.writeText "lessconfig" configText;
+
+in
+
+{
+  options = {
+
+    programs.less = {
+
+      # note that environment.nix sets PAGER=less, and
+      # therefore also enables this module
+      enable = mkEnableOption "less";
+
+      configFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = literalExpression ''"''${pkgs.my-configs}/lesskey"'';
+        description = ''
+          Path to lesskey configuration file.
+
+          <option>configFile</option> takes precedence over <option>commands</option>,
+          <option>clearDefaultCommands</option>, <option>lineEditingKeys</option>, and
+          <option>envVariables</option>.
+        '';
+      };
+
+      commands = mkOption {
+        type = types.attrsOf types.str;
+        default = {};
+        example = {
+          h = "noaction 5\\e(";
+          l = "noaction 5\\e)";
+        };
+        description = "Defines new command keys.";
+      };
+
+      clearDefaultCommands = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Clear all default commands.
+          You should remember to set the quit key.
+          Otherwise you will not be able to leave less without killing it.
+        '';
+      };
+
+      lineEditingKeys = mkOption {
+        type = types.attrsOf types.str;
+        default = {};
+        example = {
+          e = "abort";
+        };
+        description = "Defines new line-editing keys.";
+      };
+
+      envVariables = mkOption {
+        type = types.attrsOf types.str;
+        default = {
+          LESS = "-R";
+        };
+        example = {
+          LESS = "--quit-if-one-screen";
+        };
+        description = "Defines environment variables.";
+      };
+
+      lessopen = mkOption {
+        type = types.nullOr types.str;
+        default = "|${pkgs.lesspipe}/bin/lesspipe.sh %s";
+        defaultText = literalExpression ''"|''${pkgs.lesspipe}/bin/lesspipe.sh %s"'';
+        description = ''
+          Before less opens a file, it first gives your input preprocessor a chance to modify the way the contents of the file are displayed.
+        '';
+      };
+
+      lessclose = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          When less closes a file opened in such a way, it will call another program, called the input postprocessor, which may  perform  any  desired  clean-up  action (such  as deleting the replacement file created by LESSOPEN).
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.less ];
+
+    environment.variables = {
+      LESSKEYIN_SYSTEM = toString lessKey;
+    } // optionalAttrs (cfg.lessopen != null) {
+      LESSOPEN = cfg.lessopen;
+    } // optionalAttrs (cfg.lessclose != null) {
+      LESSCLOSE = cfg.lessclose;
+    };
+
+    warnings = optional (
+      cfg.clearDefaultCommands && (all (x: x != "quit") (attrValues cfg.commands))
+    ) ''
+      config.programs.less.clearDefaultCommands clears all default commands of less but there is no alternative binding for exiting.
+      Consider adding a binding for 'quit'.
+    '';
+  };
+
+  meta.maintainers = with maintainers; [ johnazoidberg ];
+
+}
diff --git a/nixos/modules/programs/liboping.nix b/nixos/modules/programs/liboping.nix
new file mode 100644
index 00000000000..4433f9767d6
--- /dev/null
+++ b/nixos/modules/programs/liboping.nix
@@ -0,0 +1,24 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.liboping;
+in {
+  options.programs.liboping = {
+    enable = mkEnableOption "liboping";
+  };
+  config = mkIf cfg.enable {
+    environment.systemPackages = with pkgs; [ liboping ];
+    security.wrappers = mkMerge (map (
+      exec: {
+        "${exec}" = {
+          owner = "root";
+          group = "root";
+          capabilities = "cap_net_raw+p";
+          source = "${pkgs.liboping}/bin/${exec}";
+        };
+      }
+    ) [ "oping" "noping" ]);
+  };
+}
diff --git a/nixos/modules/programs/light.nix b/nixos/modules/programs/light.nix
new file mode 100644
index 00000000000..9f2a03e7e76
--- /dev/null
+++ b/nixos/modules/programs/light.nix
@@ -0,0 +1,27 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.light;
+
+in
+{
+  options = {
+    programs.light = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to install Light backlight control command
+          and udev rules granting access to members of the "video" group.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.light ];
+    services.udev.packages = [ pkgs.light ];
+  };
+}
diff --git a/nixos/modules/programs/mininet.nix b/nixos/modules/programs/mininet.nix
new file mode 100644
index 00000000000..6e90e7669ac
--- /dev/null
+++ b/nixos/modules/programs/mininet.nix
@@ -0,0 +1,39 @@
+# Global configuration for mininet
+# kernel must have NETNS/VETH/SCHED
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg  = config.programs.mininet;
+
+  generatedPath = with pkgs; makeSearchPath "bin"  [
+    iperf ethtool iproute2 socat
+  ];
+
+  pyEnv = pkgs.python.withPackages(ps: [ ps.mininet-python ]);
+
+  mnexecWrapped = pkgs.runCommand "mnexec-wrapper"
+    { buildInputs = [ pkgs.makeWrapper pkgs.pythonPackages.wrapPython ]; }
+    ''
+      makeWrapper ${pkgs.mininet}/bin/mnexec \
+        $out/bin/mnexec \
+        --prefix PATH : "${generatedPath}"
+
+      ln -s ${pyEnv}/bin/mn $out/bin/mn
+
+      # mn errors out without a telnet binary
+      # pkgs.telnet brings an undesired ifconfig into PATH see #43105
+      ln -s ${pkgs.telnet}/bin/telnet $out/bin/telnet
+    '';
+in
+{
+  options.programs.mininet.enable = mkEnableOption "Mininet";
+
+  config = mkIf cfg.enable {
+
+    virtualisation.vswitch.enable = true;
+
+    environment.systemPackages = [ mnexecWrapped ];
+  };
+}
diff --git a/nixos/modules/programs/mosh.nix b/nixos/modules/programs/mosh.nix
new file mode 100644
index 00000000000..e08099e21a0
--- /dev/null
+++ b/nixos/modules/programs/mosh.nix
@@ -0,0 +1,43 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg  = config.programs.mosh;
+
+in
+{
+  options.programs.mosh = {
+    enable = mkOption {
+      description = ''
+        Whether to enable mosh. Note, this will open ports in your firewall!
+      '';
+      default = false;
+      type = lib.types.bool;
+    };
+    withUtempter = mkOption {
+      description = ''
+        Whether to enable libutempter for mosh.
+        This is required so that mosh can write to /var/run/utmp (which can be queried with `who` to display currently connected user sessions).
+        Note, this will add a guid wrapper for the group utmp!
+      '';
+      default = true;
+      type = lib.types.bool;
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = with pkgs; [ mosh ];
+    networking.firewall.allowedUDPPortRanges = [ { from = 60000; to = 61000; } ];
+    security.wrappers = mkIf cfg.withUtempter {
+      utempter = {
+        source = "${pkgs.libutempter}/lib/utempter/utempter";
+        owner = "root";
+        group = "utmp";
+        setuid = false;
+        setgid = true;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/programs/msmtp.nix b/nixos/modules/programs/msmtp.nix
new file mode 100644
index 00000000000..9c067bdc969
--- /dev/null
+++ b/nixos/modules/programs/msmtp.nix
@@ -0,0 +1,106 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.msmtp;
+
+in {
+  meta.maintainers = with maintainers; [ pacien ];
+
+  options = {
+    programs.msmtp = {
+      enable = mkEnableOption "msmtp - an SMTP client";
+
+      setSendmail = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to set the system sendmail to msmtp's.
+        '';
+      };
+
+      defaults = mkOption {
+        type = types.attrs;
+        default = {};
+        example = {
+          aliases = "/etc/aliases";
+          port = 587;
+          tls = true;
+        };
+        description = ''
+          Default values applied to all accounts.
+          See msmtp(1) for the available options.
+        '';
+      };
+
+      accounts = mkOption {
+        type = with types; attrsOf attrs;
+        default = {};
+        example = {
+          "default" = {
+            host = "smtp.example";
+            auth = true;
+            user = "someone";
+            passwordeval = "cat /secrets/password.txt";
+          };
+        };
+        description = ''
+          Named accounts and their respective configurations.
+          The special name "default" allows a default account to be defined.
+          See msmtp(1) for the available options.
+
+          Use `programs.msmtp.extraConfig` instead of this attribute set-based
+          option if ordered account inheritance is needed.
+
+          It is advised to use the `passwordeval` setting to read the password
+          from a secret file to avoid having it written in the world-readable
+          nix store. The password file must end with a newline (`\n`).
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra lines to add to the msmtp configuration verbatim.
+          See msmtp(1) for the syntax and available options.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.msmtp ];
+
+    services.mail.sendmailSetuidWrapper = mkIf cfg.setSendmail {
+      program = "sendmail";
+      source = "${pkgs.msmtp}/bin/sendmail";
+      setuid = false;
+      setgid = false;
+      owner = "root";
+      group = "root";
+    };
+
+    environment.etc."msmtprc".text = let
+      mkValueString = v:
+        if v == true then "on"
+        else if v == false then "off"
+        else generators.mkValueStringDefault {} v;
+      mkKeyValueString = k: v: "${k} ${mkValueString v}";
+      mkInnerSectionString =
+        attrs: concatStringsSep "\n" (mapAttrsToList mkKeyValueString attrs);
+      mkAccountString = name: attrs: ''
+        account ${name}
+        ${mkInnerSectionString attrs}
+      '';
+    in ''
+      defaults
+      ${mkInnerSectionString cfg.defaults}
+
+      ${concatStringsSep "\n" (mapAttrsToList mkAccountString cfg.accounts)}
+
+      ${cfg.extraConfig}
+    '';
+  };
+}
diff --git a/nixos/modules/programs/mtr.nix b/nixos/modules/programs/mtr.nix
new file mode 100644
index 00000000000..3cffe0fd8b2
--- /dev/null
+++ b/nixos/modules/programs/mtr.nix
@@ -0,0 +1,41 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.mtr;
+
+in {
+  options = {
+    programs.mtr = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to add mtr to the global environment and configure a
+          setcap wrapper for it.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.mtr;
+        defaultText = literalExpression "pkgs.mtr";
+        description = ''
+          The package to use.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = with pkgs; [ cfg.package ];
+
+    security.wrappers.mtr-packet = {
+      owner = "root";
+      group = "root";
+      capabilities = "cap_net_raw+p";
+      source = "${cfg.package}/bin/mtr-packet";
+    };
+  };
+}
diff --git a/nixos/modules/programs/nano.nix b/nixos/modules/programs/nano.nix
new file mode 100644
index 00000000000..5837dd46d7c
--- /dev/null
+++ b/nixos/modules/programs/nano.nix
@@ -0,0 +1,42 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.programs.nano;
+  LF = "\n";
+in
+
+{
+  ###### interface
+
+  options = {
+    programs.nano = {
+
+      nanorc = lib.mkOption {
+        type = lib.types.lines;
+        default = "";
+        description = ''
+          The system-wide nano configuration.
+          See <citerefentry><refentrytitle>nanorc</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+        '';
+        example = ''
+          set nowrap
+          set tabstospaces
+          set tabsize 2
+        '';
+      };
+      syntaxHighlight = lib.mkOption {
+        type = lib.types.bool;
+        default = true;
+        description = "Whether to enable syntax highlight for various languages.";
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = lib.mkIf (cfg.nanorc != "" || cfg.syntaxHighlight) {
+    environment.etc.nanorc.text = lib.concatStrings [ cfg.nanorc
+      (lib.optionalString cfg.syntaxHighlight ''${LF}include "${pkgs.nano}/share/nano/*.nanorc"'') ];
+  };
+
+}
diff --git a/nixos/modules/programs/nbd.nix b/nixos/modules/programs/nbd.nix
new file mode 100644
index 00000000000..fea9bc1ff71
--- /dev/null
+++ b/nixos/modules/programs/nbd.nix
@@ -0,0 +1,19 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.nbd;
+in
+{
+  options = {
+    programs.nbd = {
+      enable = mkEnableOption "Network Block Device (nbd) support";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = with pkgs; [ nbd ];
+    boot.kernelModules = [ "nbd" ];
+  };
+}
diff --git a/nixos/modules/programs/neovim.nix b/nixos/modules/programs/neovim.nix
new file mode 100644
index 00000000000..4649662542d
--- /dev/null
+++ b/nixos/modules/programs/neovim.nix
@@ -0,0 +1,166 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.neovim;
+
+  runtime' = filter (f: f.enable) (attrValues cfg.runtime);
+
+  runtime = pkgs.linkFarm "neovim-runtime" (map (x: { name = x.target; path = x.source; }) runtime');
+
+in {
+  options.programs.neovim = {
+    enable = mkEnableOption "Neovim";
+
+    defaultEditor = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        When enabled, installs neovim and configures neovim to be the default editor
+        using the EDITOR environment variable.
+      '';
+    };
+
+    viAlias = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Symlink <command>vi</command> to <command>nvim</command> binary.
+      '';
+    };
+
+    vimAlias = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Symlink <command>vim</command> to <command>nvim</command> binary.
+      '';
+    };
+
+    withRuby = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Enable Ruby provider.";
+    };
+
+    withPython3 = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Enable Python 3 provider.";
+    };
+
+    withNodeJs = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable Node provider.";
+    };
+
+    configure = mkOption {
+      type = types.attrs;
+      default = {};
+      example = literalExpression ''
+        {
+          customRC = '''
+            " here your custom configuration goes!
+          ''';
+          packages.myVimPackage = with pkgs.vimPlugins; {
+            # loaded on launch
+            start = [ fugitive ];
+            # manually loadable by calling `:packadd $plugin-name`
+            opt = [ ];
+          };
+        }
+      '';
+      description = ''
+        Generate your init file from your list of plugins and custom commands.
+        Neovim will then be wrapped to load <command>nvim -u /nix/store/<replaceable>hash</replaceable>-vimrc</command>
+      '';
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.neovim-unwrapped;
+      defaultText = literalExpression "pkgs.neovim-unwrapped";
+      description = "The package to use for the neovim binary.";
+    };
+
+    finalPackage = mkOption {
+      type = types.package;
+      visible = false;
+      readOnly = true;
+      description = "Resulting customized neovim package.";
+    };
+
+    runtime = mkOption {
+      default = {};
+      example = literalExpression ''
+        { "ftplugin/c.vim".text = "setlocal omnifunc=v:lua.vim.lsp.omnifunc"; }
+      '';
+      description = ''
+        Set of files that have to be linked in <filename>runtime</filename>.
+      '';
+
+      type = with types; attrsOf (submodule (
+        { name, config, ... }:
+        { options = {
+
+            enable = mkOption {
+              type = types.bool;
+              default = true;
+              description = ''
+                Whether this /etc file should be generated.  This
+                option allows specific /etc files to be disabled.
+              '';
+            };
+
+            target = mkOption {
+              type = types.str;
+              description = ''
+                Name of symlink.  Defaults to the attribute
+                name.
+              '';
+            };
+
+            text = mkOption {
+              default = null;
+              type = types.nullOr types.lines;
+              description = "Text of the file.";
+            };
+
+            source = mkOption {
+              type = types.path;
+              description = "Path of the source file.";
+            };
+
+          };
+
+          config = {
+            target = mkDefault name;
+            source = mkIf (config.text != null) (
+              let name' = "neovim-runtime" + baseNameOf name;
+              in mkDefault (pkgs.writeText name' config.text));
+          };
+
+        }));
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [
+      cfg.finalPackage
+    ];
+    environment.variables.EDITOR = mkIf cfg.defaultEditor (mkOverride 900 "nvim");
+
+    programs.neovim.finalPackage = pkgs.wrapNeovim cfg.package {
+      inherit (cfg) viAlias vimAlias withPython3 withNodeJs withRuby;
+      configure = cfg.configure // {
+
+        customRC = (cfg.configure.customRC or "") + ''
+          set runtimepath^=${runtime}/etc
+        '';
+      };
+    };
+  };
+}
diff --git a/nixos/modules/programs/nm-applet.nix b/nixos/modules/programs/nm-applet.nix
new file mode 100644
index 00000000000..5bcee30125b
--- /dev/null
+++ b/nixos/modules/programs/nm-applet.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs, ... }:
+
+{
+  meta = {
+    maintainers = lib.teams.freedesktop.members;
+  };
+
+  options.programs.nm-applet = {
+    enable = lib.mkEnableOption "nm-applet";
+
+    indicator = lib.mkOption {
+      type = lib.types.bool;
+      default = true;
+      description = ''
+        Whether to use indicator instead of status icon.
+        It is needed for Appindicator environments, like Enlightenment.
+      '';
+    };
+  };
+
+  config = lib.mkIf config.programs.nm-applet.enable {
+    systemd.user.services.nm-applet = {
+      description = "Network manager applet";
+      wantedBy = [ "graphical-session.target" ];
+      partOf = [ "graphical-session.target" ];
+      serviceConfig.ExecStart = "${pkgs.networkmanagerapplet}/bin/nm-applet ${lib.optionalString config.programs.nm-applet.indicator "--indicator"}";
+    };
+
+    services.dbus.packages = [ pkgs.gcr ];
+  };
+}
diff --git a/nixos/modules/programs/noisetorch.nix b/nixos/modules/programs/noisetorch.nix
new file mode 100644
index 00000000000..f76555289f1
--- /dev/null
+++ b/nixos/modules/programs/noisetorch.nix
@@ -0,0 +1,28 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let cfg = config.programs.noisetorch;
+in {
+  options.programs.noisetorch = {
+    enable = mkEnableOption "noisetorch + setcap wrapper";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.noisetorch;
+      defaultText = literalExpression "pkgs.noisetorch";
+      description = ''
+        The noisetorch package to use.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    security.wrappers.noisetorch = {
+      owner = "root";
+      group = "root";
+      capabilities = "cap_sys_resource=+ep";
+      source = "${cfg.package}/bin/noisetorch";
+    };
+  };
+}
diff --git a/nixos/modules/programs/npm.nix b/nixos/modules/programs/npm.nix
new file mode 100644
index 00000000000..d79c6c73400
--- /dev/null
+++ b/nixos/modules/programs/npm.nix
@@ -0,0 +1,54 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.npm;
+in
+
+{
+  ###### interface
+
+  options = {
+    programs.npm = {
+      enable = mkEnableOption "<command>npm</command> global config";
+
+      package = mkOption {
+        type = types.package;
+        description = "The npm package version / flavor to use";
+        default = pkgs.nodePackages.npm;
+        defaultText = literalExpression "pkgs.nodePackages.npm";
+        example = literalExpression "pkgs.nodePackages_13_x.npm";
+      };
+
+      npmrc = mkOption {
+        type = lib.types.lines;
+        description = ''
+          The system-wide npm configuration.
+          See <link xlink:href="https://docs.npmjs.com/misc/config"/>.
+        '';
+        default = ''
+          prefix = ''${HOME}/.npm
+        '';
+        example = ''
+          prefix = ''${HOME}/.npm
+          https-proxy=proxy.example.com
+          init-license=MIT
+          init-author-url=http://npmjs.org
+          color=true
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = lib.mkIf cfg.enable {
+    environment.etc.npmrc.text = cfg.npmrc;
+
+    environment.variables.NPM_CONFIG_GLOBALCONFIG = "/etc/npmrc";
+
+    environment.systemPackages = [ cfg.package ];
+  };
+
+}
diff --git a/nixos/modules/programs/oblogout.nix b/nixos/modules/programs/oblogout.nix
new file mode 100644
index 00000000000..a039b0623b5
--- /dev/null
+++ b/nixos/modules/programs/oblogout.nix
@@ -0,0 +1,11 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  imports = [
+    (mkRemovedOptionModule [ "programs" "oblogout" ] "programs.oblogout has been removed from NixOS. This is because the oblogout repository has been archived upstream.")
+  ];
+
+}
diff --git a/nixos/modules/programs/pantheon-tweaks.nix b/nixos/modules/programs/pantheon-tweaks.nix
new file mode 100644
index 00000000000..0b8a19ea22c
--- /dev/null
+++ b/nixos/modules/programs/pantheon-tweaks.nix
@@ -0,0 +1,19 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  meta = {
+    maintainers = teams.pantheon.members;
+  };
+
+  ###### interface
+  options = {
+    programs.pantheon-tweaks.enable = mkEnableOption "Pantheon Tweaks, an unofficial system settings panel for Pantheon";
+  };
+
+  ###### implementation
+  config = mkIf config.programs.pantheon-tweaks.enable {
+    services.xserver.desktopManager.pantheon.extraSwitchboardPlugs = [ pkgs.pantheon-tweaks ];
+  };
+}
diff --git a/nixos/modules/programs/partition-manager.nix b/nixos/modules/programs/partition-manager.nix
new file mode 100644
index 00000000000..1be2f0a69a1
--- /dev/null
+++ b/nixos/modules/programs/partition-manager.nix
@@ -0,0 +1,19 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  meta.maintainers = [ maintainers.oxalica ];
+
+  ###### interface
+  options = {
+    programs.partition-manager.enable = mkEnableOption "KDE Partition Manager";
+  };
+
+  ###### implementation
+  config = mkIf config.programs.partition-manager.enable {
+    services.dbus.packages = [ pkgs.libsForQt5.kpmcore ];
+    # `kpmcore` need to be installed to pull in polkit actions.
+    environment.systemPackages = [ pkgs.libsForQt5.kpmcore pkgs.partition-manager ];
+  };
+}
diff --git a/nixos/modules/programs/phosh.nix b/nixos/modules/programs/phosh.nix
new file mode 100644
index 00000000000..ad875616ac9
--- /dev/null
+++ b/nixos/modules/programs/phosh.nix
@@ -0,0 +1,162 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.phosh;
+
+  # Based on https://source.puri.sm/Librem5/librem5-base/-/blob/4596c1056dd75ac7f043aede07887990fd46f572/default/sm.puri.OSK0.desktop
+  oskItem = pkgs.makeDesktopItem {
+    name = "sm.puri.OSK0";
+    desktopName = "On-screen keyboard";
+    exec = "${pkgs.squeekboard}/bin/squeekboard";
+    categories = [ "GNOME" "Core" ];
+    onlyShowIn = [ "GNOME" ];
+    noDisplay = true;
+    extraConfig = {
+      X-GNOME-Autostart-Phase = "Panel";
+      X-GNOME-Provides = "inputmethod";
+      X-GNOME-Autostart-Notify = "true";
+      X-GNOME-AutoRestart = "true";
+    };
+  };
+
+  phocConfigType = types.submodule {
+    options = {
+      xwayland = mkOption {
+        description = ''
+          Whether to enable XWayland support.
+
+          To start XWayland immediately, use `immediate`.
+        '';
+        type = types.enum [ "true" "false" "immediate" ];
+        default = "false";
+      };
+      cursorTheme = mkOption {
+        description = ''
+          Cursor theme to use in Phosh.
+        '';
+        type = types.str;
+        default = "default";
+      };
+      outputs = mkOption {
+        description = ''
+          Output configurations.
+        '';
+        type = types.attrsOf phocOutputType;
+        default = {
+          DSI-1 = {
+            scale = 2;
+          };
+        };
+      };
+    };
+  };
+
+  phocOutputType = types.submodule {
+    options = {
+      modeline = mkOption {
+        description = ''
+          One or more modelines.
+        '';
+        type = types.either types.str (types.listOf types.str);
+        default = [];
+        example = [
+          "87.25 720 776 848  976 1440 1443 1453 1493 -hsync +vsync"
+          "65.13 768 816 896 1024 1024 1025 1028 1060 -HSync +VSync"
+        ];
+      };
+      mode = mkOption {
+        description = ''
+          Default video mode.
+        '';
+        type = types.nullOr types.str;
+        default = null;
+        example = "768x1024";
+      };
+      scale = mkOption {
+        description = ''
+          Display scaling factor.
+        '';
+        type = types.nullOr types.ints.unsigned;
+        default = null;
+        example = 2;
+      };
+      rotate = mkOption {
+        description = ''
+          Screen transformation.
+        '';
+        type = types.enum [
+          "90" "180" "270" "flipped" "flipped-90" "flipped-180" "flipped-270" null
+        ];
+        default = null;
+      };
+    };
+  };
+
+  optionalKV = k: v: if v == null then "" else "${k} = ${builtins.toString v}";
+
+  renderPhocOutput = name: output: let
+    modelines = if builtins.isList output.modeline
+      then output.modeline
+      else [ output.modeline ];
+    renderModeline = l: "modeline = ${l}";
+  in ''
+    [output:${name}]
+    ${concatStringsSep "\n" (map renderModeline modelines)}
+    ${optionalKV "mode" output.mode}
+    ${optionalKV "scale" output.scale}
+    ${optionalKV "rotate" output.rotate}
+  '';
+
+  renderPhocConfig = phoc: let
+    outputs = mapAttrsToList renderPhocOutput phoc.outputs;
+  in ''
+    [core]
+    xwayland = ${phoc.xwayland}
+    ${concatStringsSep "\n" outputs}
+    [cursor]
+    theme = ${phoc.cursorTheme}
+  '';
+in {
+  options = {
+    programs.phosh = {
+      enable = mkEnableOption ''
+        Whether to enable, Phosh, related packages and default configurations.
+      '';
+      phocConfig = mkOption {
+        description = ''
+          Configurations for the Phoc compositor.
+        '';
+        type = types.oneOf [ types.lines types.path phocConfigType ];
+        default = {};
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [
+      pkgs.phoc
+      pkgs.phosh
+      pkgs.squeekboard
+      oskItem
+    ];
+
+    systemd.packages = [ pkgs.phosh ];
+
+    programs.feedbackd.enable = true;
+
+    security.pam.services.phosh = {};
+
+    hardware.opengl.enable = mkDefault true;
+
+    services.gnome.core-shell.enable = true;
+    services.gnome.core-os-services.enable = true;
+    services.xserver.displayManager.sessionPackages = [ pkgs.phosh ];
+
+    environment.etc."phosh/phoc.ini".source =
+      if builtins.isPath cfg.phocConfig then cfg.phocConfig
+      else if builtins.isString cfg.phocConfig then pkgs.writeText "phoc.ini" cfg.phocConfig
+      else pkgs.writeText "phoc.ini" (renderPhocConfig cfg.phocConfig);
+  };
+}
diff --git a/nixos/modules/programs/plotinus.nix b/nixos/modules/programs/plotinus.nix
new file mode 100644
index 00000000000..2c90a41ba02
--- /dev/null
+++ b/nixos/modules/programs/plotinus.nix
@@ -0,0 +1,36 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.plotinus;
+in
+{
+  meta = {
+    maintainers = pkgs.plotinus.meta.maintainers;
+    doc = ./plotinus.xml;
+  };
+
+  ###### interface
+
+  options = {
+    programs.plotinus = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to enable the Plotinus GTK 3 plugin. Plotinus provides a
+          popup (triggered by Ctrl-Shift-P) to search the menus of a
+          compatible application.
+        '';
+        type = types.bool;
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment.sessionVariables.XDG_DATA_DIRS = [ "${pkgs.plotinus}/share/gsettings-schemas/${pkgs.plotinus.name}" ];
+    environment.variables.GTK3_MODULES = [ "${pkgs.plotinus}/lib/libplotinus.so" ];
+  };
+}
diff --git a/nixos/modules/programs/plotinus.xml b/nixos/modules/programs/plotinus.xml
new file mode 100644
index 00000000000..8fc8c22c6d7
--- /dev/null
+++ b/nixos/modules/programs/plotinus.xml
@@ -0,0 +1,30 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="module-program-plotinus">
+ <title>Plotinus</title>
+ <para>
+  <emphasis>Source:</emphasis>
+  <filename>modules/programs/plotinus.nix</filename>
+ </para>
+ <para>
+  <emphasis>Upstream documentation:</emphasis>
+  <link xlink:href="https://github.com/p-e-w/plotinus"/>
+ </para>
+ <para>
+  Plotinus is a searchable command palette in every modern GTK application.
+ </para>
+ <para>
+  When in a GTK 3 application and Plotinus is enabled, you can press
+  <literal>Ctrl+Shift+P</literal> to open the command palette. The command
+  palette provides a searchable list of of all menu items in the application.
+ </para>
+ <para>
+  To enable Plotinus, add the following to your
+  <filename>configuration.nix</filename>:
+<programlisting>
+<xref linkend="opt-programs.plotinus.enable"/> = true;
+</programlisting>
+ </para>
+</chapter>
diff --git a/nixos/modules/programs/proxychains.nix b/nixos/modules/programs/proxychains.nix
new file mode 100644
index 00000000000..3f44e23a93e
--- /dev/null
+++ b/nixos/modules/programs/proxychains.nix
@@ -0,0 +1,165 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+
+  cfg = config.programs.proxychains;
+
+  configFile = ''
+    ${cfg.chain.type}_chain
+    ${optionalString (cfg.chain.type == "random")
+    "chain_len = ${builtins.toString cfg.chain.length}"}
+    ${optionalString cfg.proxyDNS "proxy_dns"}
+    ${optionalString cfg.quietMode "quiet_mode"}
+    remote_dns_subnet ${builtins.toString cfg.remoteDNSSubnet}
+    tcp_read_time_out ${builtins.toString cfg.tcpReadTimeOut}
+    tcp_connect_time_out ${builtins.toString cfg.tcpConnectTimeOut}
+    localnet ${cfg.localnet}
+    [ProxyList]
+    ${builtins.concatStringsSep "\n"
+      (lib.mapAttrsToList (k: v: "${v.type} ${v.host} ${builtins.toString v.port}")
+        (lib.filterAttrs (k: v: v.enable) cfg.proxies))}
+  '';
+
+  proxyOptions = {
+    options = {
+      enable = mkEnableOption "this proxy";
+
+      type = mkOption {
+        type = types.enum [ "http" "socks4" "socks5" ];
+        description = "Proxy type.";
+      };
+
+      host = mkOption {
+        type = types.str;
+        description = "Proxy host or IP address.";
+      };
+
+      port = mkOption {
+        type = types.port;
+        description = "Proxy port";
+      };
+    };
+  };
+
+in {
+
+  ###### interface
+
+  options = {
+
+    programs.proxychains = {
+
+      enable = mkEnableOption "installing proxychains configuration";
+
+      chain = {
+        type = mkOption {
+          type = types.enum [ "dynamic" "strict" "random" ];
+          default = "strict";
+          description = ''
+            <literal>dynamic</literal> - Each connection will be done via chained proxies
+            all proxies chained in the order as they appear in the list
+            at least one proxy must be online to play in chain
+            (dead proxies are skipped)
+            otherwise <literal>EINTR</literal> is returned to the app.
+
+            <literal>strict</literal> - Each connection will be done via chained proxies
+            all proxies chained in the order as they appear in the list
+            all proxies must be online to play in chain
+            otherwise <literal>EINTR</literal> is returned to the app.
+
+            <literal>random</literal> - Each connection will be done via random proxy
+            (or proxy chain, see <option>programs.proxychains.chain.length</option>) from the list.
+          '';
+        };
+        length = mkOption {
+          type = types.nullOr types.int;
+          default = null;
+          description = ''
+            Chain length for random chain.
+          '';
+        };
+      };
+
+      proxyDNS = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Proxy DNS requests - no leak for DNS data.";
+      };
+
+      quietMode = mkEnableOption "Quiet mode (no output from the library).";
+
+      remoteDNSSubnet = mkOption {
+        type = types.enum [ 10 127 224 ];
+        default = 224;
+        description = ''
+          Set the class A subnet number to use for the internal remote DNS mapping, uses the reserved 224.x.x.x range by default.
+        '';
+      };
+
+      tcpReadTimeOut = mkOption {
+        type = types.int;
+        default = 15000;
+        description = "Connection read time-out in milliseconds.";
+      };
+
+      tcpConnectTimeOut = mkOption {
+        type = types.int;
+        default = 8000;
+        description = "Connection time-out in milliseconds.";
+      };
+
+      localnet = mkOption {
+        type = types.str;
+        default = "127.0.0.0/255.0.0.0";
+        description = "By default enable localnet for loopback address ranges.";
+      };
+
+      proxies = mkOption {
+        type = types.attrsOf (types.submodule proxyOptions);
+        description = ''
+          Proxies to be used by proxychains.
+        '';
+
+        example = literalExpression ''
+          { myproxy =
+            { type = "socks4";
+              host = "127.0.0.1";
+              port = 1337;
+            };
+          }
+        '';
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  meta.maintainers = with maintainers; [ sorki ];
+
+  config = mkIf cfg.enable {
+
+    assertions = singleton {
+      assertion = cfg.chain.type != "random" && cfg.chain.length == null;
+      message = ''
+        Option `programs.proxychains.chain.length`
+        only makes sense with `programs.proxychains.chain.type` = "random".
+      '';
+    };
+
+    programs.proxychains.proxies = mkIf config.services.tor.client.enable
+      {
+        torproxy = mkDefault {
+          enable = true;
+          type = "socks4";
+          host = "127.0.0.1";
+          port = 9050;
+        };
+      };
+
+    environment.etc."proxychains.conf".text = configFile;
+    environment.systemPackages = [ pkgs.proxychains ];
+  };
+
+}
diff --git a/nixos/modules/programs/qt5ct.nix b/nixos/modules/programs/qt5ct.nix
new file mode 100644
index 00000000000..88e861bf403
--- /dev/null
+++ b/nixos/modules/programs/qt5ct.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  meta.maintainers = [ maintainers.romildo ];
+
+  ###### interface
+  options = {
+    programs.qt5ct = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to enable the Qt5 Configuration Tool (qt5ct), a
+          program that allows users to configure Qt5 settings (theme,
+          font, icons, etc.) under desktop environments or window
+          manager without Qt integration.
+
+          Official home page: <link xlink:href="https://sourceforge.net/projects/qt5ct/">https://sourceforge.net/projects/qt5ct/</link>
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+  config = mkIf config.programs.qt5ct.enable {
+    environment.variables.QT_QPA_PLATFORMTHEME = "qt5ct";
+    environment.systemPackages = with pkgs; [ libsForQt5.qt5ct ];
+  };
+}
diff --git a/nixos/modules/programs/screen.nix b/nixos/modules/programs/screen.nix
new file mode 100644
index 00000000000..728a0eb8cea
--- /dev/null
+++ b/nixos/modules/programs/screen.nix
@@ -0,0 +1,33 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib) mkOption mkIf types;
+  cfg = config.programs.screen;
+in
+
+{
+  ###### interface
+
+  options = {
+    programs.screen = {
+
+      screenrc = mkOption {
+        default = "";
+        description = ''
+          The contents of /etc/screenrc file.
+        '';
+        type = types.lines;
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf (cfg.screenrc != "") {
+    environment.etc.screenrc.text = cfg.screenrc;
+
+    environment.systemPackages = [ pkgs.screen ];
+    security.pam.services.screen = {};
+  };
+
+}
diff --git a/nixos/modules/programs/seahorse.nix b/nixos/modules/programs/seahorse.nix
new file mode 100644
index 00000000000..c0a356bff57
--- /dev/null
+++ b/nixos/modules/programs/seahorse.nix
@@ -0,0 +1,46 @@
+# Seahorse.
+
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+{
+
+ # Added 2019-08-27
+  imports = [
+    (mkRenamedOptionModule
+      [ "services" "gnome3" "seahorse" "enable" ]
+      [ "programs" "seahorse" "enable" ])
+  ];
+
+
+  ###### interface
+
+  options = {
+
+    programs.seahorse = {
+
+      enable = mkEnableOption "Seahorse, a GNOME application for managing encryption keys and passwords in the GNOME Keyring";
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.programs.seahorse.enable {
+
+    programs.ssh.askPassword = mkDefault "${pkgs.gnome.seahorse}/libexec/seahorse/ssh-askpass";
+
+    environment.systemPackages = [
+      pkgs.gnome.seahorse
+    ];
+
+    services.dbus.packages = [
+      pkgs.gnome.seahorse
+    ];
+
+  };
+
+}
diff --git a/nixos/modules/programs/sedutil.nix b/nixos/modules/programs/sedutil.nix
new file mode 100644
index 00000000000..7efc80f4abb
--- /dev/null
+++ b/nixos/modules/programs/sedutil.nix
@@ -0,0 +1,18 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.sedutil;
+
+in {
+  options.programs.sedutil.enable = mkEnableOption "sedutil";
+
+  config = mkIf cfg.enable {
+    boot.kernelParams = [
+      "libata.allow_tpm=1"
+    ];
+
+    environment.systemPackages = with pkgs; [ sedutil ];
+  };
+}
diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix
new file mode 100644
index 00000000000..963cd8853db
--- /dev/null
+++ b/nixos/modules/programs/shadow.nix
@@ -0,0 +1,129 @@
+# Configuration for the pwdutils suite of tools: passwd, useradd, etc.
+
+{ config, lib, utils, pkgs, ... }:
+
+with lib;
+
+let
+
+  /*
+  There are three different sources for user/group id ranges, each of which gets
+  used by different programs:
+  - The login.defs file, used by the useradd, groupadd and newusers commands
+  - The update-users-groups.pl file, used by NixOS in the activation phase to
+    decide on which ids to use for declaratively defined users without a static
+    id
+  - Systemd compile time options -Dsystem-uid-max= and -Dsystem-gid-max=, used
+    by systemd for features like ConditionUser=@system and systemd-sysusers
+  */
+  loginDefs =
+    ''
+      DEFAULT_HOME yes
+
+      SYS_UID_MIN  400
+      SYS_UID_MAX  999
+      UID_MIN      1000
+      UID_MAX      29999
+
+      SYS_GID_MIN  400
+      SYS_GID_MAX  999
+      GID_MIN      1000
+      GID_MAX      29999
+
+      TTYGROUP     tty
+      TTYPERM      0620
+
+      # Ensure privacy for newly created home directories.
+      UMASK        077
+
+      # Uncomment this and install chfn SUID to allow non-root
+      # users to change their account GECOS information.
+      # This should be made configurable.
+      #CHFN_RESTRICT frwh
+
+    '';
+
+  mkSetuidRoot = source:
+    { setuid = true;
+      owner = "root";
+      group = "root";
+      inherit source;
+    };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    users.defaultUserShell = lib.mkOption {
+      description = ''
+        This option defines the default shell assigned to user
+        accounts. This can be either a full system path or a shell package.
+
+        This must not be a store path, since the path is
+        used outside the store (in particular in /etc/passwd).
+      '';
+      example = literalExpression "pkgs.zsh";
+      type = types.either types.path types.shellPackage;
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    environment.systemPackages =
+      lib.optional config.users.mutableUsers pkgs.shadow ++
+      lib.optional (types.shellPackage.check config.users.defaultUserShell)
+        config.users.defaultUserShell;
+
+    environment.etc =
+      { # /etc/login.defs: global configuration for pwdutils.  You
+        # cannot login without it!
+        "login.defs".source = pkgs.writeText "login.defs" loginDefs;
+
+        # /etc/default/useradd: configuration for useradd.
+        "default/useradd".source = pkgs.writeText "useradd"
+          ''
+            GROUP=100
+            HOME=/home
+            SHELL=${utils.toShellPath config.users.defaultUserShell}
+          '';
+      };
+
+    security.pam.services =
+      { chsh = { rootOK = true; };
+        chfn = { rootOK = true; };
+        su = { rootOK = true; forwardXAuth = true; logFailures = true; };
+        passwd = {};
+        # Note: useradd, groupadd etc. aren't setuid root, so it
+        # doesn't really matter what the PAM config says as long as it
+        # lets root in.
+        useradd = { rootOK = true; };
+        usermod = { rootOK = true; };
+        userdel = { rootOK = true; };
+        groupadd = { rootOK = true; };
+        groupmod = { rootOK = true; };
+        groupmems = { rootOK = true; };
+        groupdel = { rootOK = true; };
+        login = { startSession = true; allowNullPassword = true; showMotd = true; updateWtmp = true; };
+        chpasswd = { rootOK = true; };
+      };
+
+    security.wrappers = {
+      su        = mkSetuidRoot "${pkgs.shadow.su}/bin/su";
+      sg        = mkSetuidRoot "${pkgs.shadow.out}/bin/sg";
+      newgrp    = mkSetuidRoot "${pkgs.shadow.out}/bin/newgrp";
+      newuidmap = mkSetuidRoot "${pkgs.shadow.out}/bin/newuidmap";
+      newgidmap = mkSetuidRoot "${pkgs.shadow.out}/bin/newgidmap";
+    } // lib.optionalAttrs config.users.mutableUsers {
+      chsh   = mkSetuidRoot "${pkgs.shadow.out}/bin/chsh";
+      passwd = mkSetuidRoot "${pkgs.shadow.out}/bin/passwd";
+    };
+  };
+}
diff --git a/nixos/modules/programs/singularity.nix b/nixos/modules/programs/singularity.nix
new file mode 100644
index 00000000000..db935abe4bb
--- /dev/null
+++ b/nixos/modules/programs/singularity.nix
@@ -0,0 +1,34 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.programs.singularity;
+  singularity = pkgs.singularity.overrideAttrs (attrs : {
+    installPhase = attrs.installPhase + ''
+      mv $out/libexec/singularity/bin/starter-suid $out/libexec/singularity/bin/starter-suid.orig
+      ln -s /run/wrappers/bin/singularity-suid $out/libexec/singularity/bin/starter-suid
+    '';
+  });
+in {
+  options.programs.singularity = {
+    enable = mkEnableOption "Singularity";
+  };
+
+  config = mkIf cfg.enable {
+      environment.systemPackages = [ singularity ];
+      security.wrappers.singularity-suid =
+      { setuid = true;
+        owner = "root";
+        group = "root";
+        source = "${singularity}/libexec/singularity/bin/starter-suid.orig";
+      };
+      systemd.tmpfiles.rules = [
+        "d /var/singularity/mnt/session 0770 root root -"
+        "d /var/singularity/mnt/final 0770 root root -"
+        "d /var/singularity/mnt/overlay 0770 root root -"
+        "d /var/singularity/mnt/container 0770 root root -"
+        "d /var/singularity/mnt/source 0770 root root -"
+      ];
+  };
+
+}
diff --git a/nixos/modules/programs/slock.nix b/nixos/modules/programs/slock.nix
new file mode 100644
index 00000000000..ce80fcc5d4a
--- /dev/null
+++ b/nixos/modules/programs/slock.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.slock;
+
+in
+{
+  options = {
+    programs.slock = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to install slock screen locker with setuid wrapper.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.slock ];
+    security.wrappers.slock =
+      { setuid = true;
+        owner = "root";
+        group = "root";
+        source = "${pkgs.slock.out}/bin/slock";
+      };
+  };
+}
diff --git a/nixos/modules/programs/spacefm.nix b/nixos/modules/programs/spacefm.nix
new file mode 100644
index 00000000000..f71abcaa332
--- /dev/null
+++ b/nixos/modules/programs/spacefm.nix
@@ -0,0 +1,55 @@
+# Global configuration for spacefm.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.programs.spacefm;
+
+in
+{
+  ###### interface
+
+  options = {
+
+    programs.spacefm = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to install SpaceFM and create <filename>/etc/spacefm/spacefm.conf</filename>.
+        '';
+      };
+
+      settings = mkOption {
+        type = types.attrs;
+        default = {
+          tmp_dir = "/tmp";
+          terminal_su = "${pkgs.sudo}/bin/sudo";
+        };
+        defaultText = literalExpression ''
+          {
+            tmp_dir = "/tmp";
+            terminal_su = "''${pkgs.sudo}/bin/sudo";
+          }
+        '';
+        description = ''
+          The system-wide spacefm configuration.
+          Parameters to be written to <filename>/etc/spacefm/spacefm.conf</filename>.
+          Refer to the <link xlink:href="https://ignorantguru.github.io/spacefm/spacefm-manual-en.html#programfiles-etc">relevant entry</link> in the SpaceFM manual.
+        '';
+      };
+
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.spaceFM ];
+
+    environment.etc."spacefm/spacefm.conf".text =
+      concatStrings (mapAttrsToList (n: v: "${n}=${toString v}\n") cfg.settings);
+  };
+}
diff --git a/nixos/modules/programs/ssh.nix b/nixos/modules/programs/ssh.nix
new file mode 100644
index 00000000000..b31fce91524
--- /dev/null
+++ b/nixos/modules/programs/ssh.nix
@@ -0,0 +1,346 @@
+# Global configuration for the SSH client.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg  = config.programs.ssh;
+
+  askPassword = cfg.askPassword;
+
+  askPasswordWrapper = pkgs.writeScript "ssh-askpass-wrapper"
+    ''
+      #! ${pkgs.runtimeShell} -e
+      export DISPLAY="$(systemctl --user show-environment | ${pkgs.gnused}/bin/sed 's/^DISPLAY=\(.*\)/\1/; t; d')"
+      exec ${askPassword} "$@"
+    '';
+
+  knownHosts = attrValues cfg.knownHosts;
+
+  knownHostsText = (flip (concatMapStringsSep "\n") knownHosts
+    (h: assert h.hostNames != [];
+      optionalString h.certAuthority "@cert-authority " + concatStringsSep "," h.hostNames + " "
+      + (if h.publicKey != null then h.publicKey else readFile h.publicKeyFile)
+    )) + "\n";
+
+  knownHostsFiles = [ "/etc/ssh/ssh_known_hosts" "/etc/ssh/ssh_known_hosts2" ]
+    ++ map pkgs.copyPathToStore cfg.knownHostsFiles;
+
+in
+{
+  ###### interface
+
+  options = {
+
+    programs.ssh = {
+
+      enableAskPassword = mkOption {
+        type = types.bool;
+        default = config.services.xserver.enable;
+        defaultText = literalExpression "config.services.xserver.enable";
+        description = "Whether to configure SSH_ASKPASS in the environment.";
+      };
+
+      askPassword = mkOption {
+        type = types.str;
+        default = "${pkgs.x11_ssh_askpass}/libexec/x11-ssh-askpass";
+        defaultText = literalExpression ''"''${pkgs.x11_ssh_askpass}/libexec/x11-ssh-askpass"'';
+        description = "Program used by SSH to ask for passwords.";
+      };
+
+      forwardX11 = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to request X11 forwarding on outgoing connections by default.
+          This is useful for running graphical programs on the remote machine and have them display to your local X11 server.
+          Historically, this value has depended on the value used by the local sshd daemon, but there really isn't a relation between the two.
+          Note: there are some security risks to forwarding an X11 connection.
+          NixOS's X server is built with the SECURITY extension, which prevents some obvious attacks.
+          To enable or disable forwarding on a per-connection basis, see the -X and -x options to ssh.
+          The -Y option to ssh enables trusted forwarding, which bypasses the SECURITY extension.
+        '';
+      };
+
+      setXAuthLocation = mkOption {
+        type = types.bool;
+        description = ''
+          Whether to set the path to <command>xauth</command> for X11-forwarded connections.
+          This causes a dependency on X11 packages.
+        '';
+      };
+
+      pubkeyAcceptedKeyTypes = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "ssh-ed25519" "ssh-rsa" ];
+        description = ''
+          Specifies the key types that will be used for public key authentication.
+        '';
+      };
+
+      hostKeyAlgorithms = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "ssh-ed25519" "ssh-rsa" ];
+        description = ''
+          Specifies the host key algorithms that the client wants to use in order of preference.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra configuration text prepended to <filename>ssh_config</filename>. Other generated
+          options will be added after a <code>Host *</code> pattern.
+          See <citerefentry><refentrytitle>ssh_config</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+          for help.
+        '';
+      };
+
+      startAgent = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to start the OpenSSH agent when you log in.  The OpenSSH agent
+          remembers private keys for you so that you don't have to type in
+          passphrases every time you make an SSH connection.  Use
+          <command>ssh-add</command> to add a key to the agent.
+        '';
+      };
+
+      agentTimeout = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "1h";
+        description = ''
+          How long to keep the private keys in memory. Use null to keep them forever.
+        '';
+      };
+
+      agentPKCS11Whitelist = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = literalExpression ''"''${pkgs.opensc}/lib/opensc-pkcs11.so"'';
+        description = ''
+          A pattern-list of acceptable paths for PKCS#11 shared libraries
+          that may be used with the -s option to ssh-add.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.openssh;
+        defaultText = literalExpression "pkgs.openssh";
+        description = ''
+          The package used for the openssh client and daemon.
+        '';
+      };
+
+      knownHosts = mkOption {
+        default = {};
+        type = types.attrsOf (types.submodule ({ name, config, options, ... }: {
+          options = {
+            certAuthority = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                This public key is an SSH certificate authority, rather than an
+                individual host's key.
+              '';
+            };
+            hostNames = mkOption {
+              type = types.listOf types.str;
+              default = [ name ] ++ config.extraHostNames;
+              defaultText = literalExpression "[ ${name} ] ++ config.${options.extraHostNames}";
+              description = ''
+                DEPRECATED, please use <literal>extraHostNames</literal>.
+                A list of host names and/or IP numbers used for accessing
+                the host's ssh service.
+              '';
+            };
+            extraHostNames = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              description = ''
+                A list of additional host names and/or IP numbers used for
+                accessing the host's ssh service.
+              '';
+            };
+            publicKey = mkOption {
+              default = null;
+              type = types.nullOr types.str;
+              example = "ecdsa-sha2-nistp521 AAAAE2VjZHN...UEPg==";
+              description = ''
+                The public key data for the host. You can fetch a public key
+                from a running SSH server with the <command>ssh-keyscan</command>
+                command. The public key should not include any host names, only
+                the key type and the key itself.
+              '';
+            };
+            publicKeyFile = mkOption {
+              default = null;
+              type = types.nullOr types.path;
+              description = ''
+                The path to the public key file for the host. The public
+                key file is read at build time and saved in the Nix store.
+                You can fetch a public key file from a running SSH server
+                with the <command>ssh-keyscan</command> command. The content
+                of the file should follow the same format as described for
+                the <literal>publicKey</literal> option. Only a single key
+                is supported. If a host has multiple keys, use
+                <option>programs.ssh.knownHostsFiles</option> instead.
+              '';
+            };
+          };
+        }));
+        description = ''
+          The set of system-wide known SSH hosts.
+        '';
+        example = literalExpression ''
+          {
+            myhost = {
+              extraHostNames = [ "myhost.mydomain.com" "10.10.1.4" ];
+              publicKeyFile = ./pubkeys/myhost_ssh_host_dsa_key.pub;
+            };
+            "myhost2.net".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILIRuJ8p1Fi+m6WkHV0KWnRfpM1WxoW8XAS+XvsSKsTK";
+          }
+        '';
+      };
+
+      knownHostsFiles = mkOption {
+        default = [];
+        type = with types; listOf path;
+        description = ''
+          Files containing SSH host keys to set as global known hosts.
+          <literal>/etc/ssh/ssh_known_hosts</literal> (which is
+          generated by <option>programs.ssh.knownHosts</option>) and
+          <literal>/etc/ssh/ssh_known_hosts2</literal> are always
+          included.
+        '';
+        example = literalExpression ''
+          [
+            ./known_hosts
+            (writeText "github.keys" '''
+              github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
+              github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
+              github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
+            ''')
+          ]
+        '';
+      };
+
+      kexAlgorithms = mkOption {
+        type = types.nullOr (types.listOf types.str);
+        default = null;
+        example = [ "curve25519-sha256@libssh.org" "diffie-hellman-group-exchange-sha256" ];
+        description = ''
+          Specifies the available KEX (Key Exchange) algorithms.
+        '';
+      };
+
+      ciphers = mkOption {
+        type = types.nullOr (types.listOf types.str);
+        default = null;
+        example = [ "chacha20-poly1305@openssh.com" "aes256-gcm@openssh.com" ];
+        description = ''
+          Specifies the ciphers allowed and their order of preference.
+        '';
+      };
+
+      macs = mkOption {
+        type = types.nullOr (types.listOf types.str);
+        default = null;
+        example = [ "hmac-sha2-512-etm@openssh.com" "hmac-sha1" ];
+        description = ''
+          Specifies the MAC (message authentication code) algorithms in order of preference. The MAC algorithm is used
+          for data integrity protection.
+        '';
+      };
+    };
+
+  };
+
+  config = {
+
+    programs.ssh.setXAuthLocation =
+      mkDefault (config.services.xserver.enable || config.programs.ssh.forwardX11 || config.services.openssh.forwardX11);
+
+    assertions =
+      [ { assertion = cfg.forwardX11 -> cfg.setXAuthLocation;
+          message = "cannot enable X11 forwarding without setting XAuth location";
+        }
+      ] ++ flip mapAttrsToList cfg.knownHosts (name: data: {
+        assertion = (data.publicKey == null && data.publicKeyFile != null) ||
+                    (data.publicKey != null && data.publicKeyFile == null);
+        message = "knownHost ${name} must contain either a publicKey or publicKeyFile";
+      });
+
+    warnings = mapAttrsToList (name: _: ''programs.ssh.knownHosts.${name}.hostNames is deprecated, use programs.ssh.knownHosts.${name}.extraHostNames'')
+      (filterAttrs (name: {hostNames, extraHostNames, ...}: hostNames != [ name ] ++ extraHostNames) cfg.knownHosts);
+
+    # SSH configuration. Slight duplication of the sshd_config
+    # generation in the sshd service.
+    environment.etc."ssh/ssh_config".text =
+      ''
+        # Custom options from `extraConfig`, to override generated options
+        ${cfg.extraConfig}
+
+        # Generated options from other settings
+        Host *
+        AddressFamily ${if config.networking.enableIPv6 then "any" else "inet"}
+        GlobalKnownHostsFile ${concatStringsSep " " knownHostsFiles}
+
+        ${optionalString cfg.setXAuthLocation ''
+          XAuthLocation ${pkgs.xorg.xauth}/bin/xauth
+        ''}
+
+        ForwardX11 ${if cfg.forwardX11 then "yes" else "no"}
+
+        ${optionalString (cfg.pubkeyAcceptedKeyTypes != []) "PubkeyAcceptedKeyTypes ${concatStringsSep "," cfg.pubkeyAcceptedKeyTypes}"}
+        ${optionalString (cfg.hostKeyAlgorithms != []) "HostKeyAlgorithms ${concatStringsSep "," cfg.hostKeyAlgorithms}"}
+        ${optionalString (cfg.kexAlgorithms != null) "KexAlgorithms ${concatStringsSep "," cfg.kexAlgorithms}"}
+        ${optionalString (cfg.ciphers != null) "Ciphers ${concatStringsSep "," cfg.ciphers}"}
+        ${optionalString (cfg.macs != null) "MACs ${concatStringsSep "," cfg.macs}"}
+      '';
+
+    environment.etc."ssh/ssh_known_hosts".text = knownHostsText;
+
+    # FIXME: this should really be socket-activated for über-awesomeness.
+    systemd.user.services.ssh-agent = mkIf cfg.startAgent
+      { description = "SSH Agent";
+        wantedBy = [ "default.target" ];
+        unitConfig.ConditionUser = "!@system";
+        serviceConfig =
+          { ExecStartPre = "${pkgs.coreutils}/bin/rm -f %t/ssh-agent";
+            ExecStart =
+                "${cfg.package}/bin/ssh-agent " +
+                optionalString (cfg.agentTimeout != null) ("-t ${cfg.agentTimeout} ") +
+                optionalString (cfg.agentPKCS11Whitelist != null) ("-P ${cfg.agentPKCS11Whitelist} ") +
+                "-a %t/ssh-agent";
+            StandardOutput = "null";
+            Type = "forking";
+            Restart = "on-failure";
+            SuccessExitStatus = "0 2";
+          };
+        # Allow ssh-agent to ask for confirmation. This requires the
+        # unit to know about the user's $DISPLAY (via ‘systemctl
+        # import-environment’).
+        environment.SSH_ASKPASS = optionalString cfg.enableAskPassword askPasswordWrapper;
+        environment.DISPLAY = "fake"; # required to make ssh-agent start $SSH_ASKPASS
+      };
+
+    environment.extraInit = optionalString cfg.startAgent
+      ''
+        if [ -z "$SSH_AUTH_SOCK" -a -n "$XDG_RUNTIME_DIR" ]; then
+          export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent"
+        fi
+      '';
+
+    environment.variables.SSH_ASKPASS = optionalString cfg.enableAskPassword askPassword;
+
+  };
+}
diff --git a/nixos/modules/programs/ssmtp.nix b/nixos/modules/programs/ssmtp.nix
new file mode 100644
index 00000000000..b454bf35229
--- /dev/null
+++ b/nixos/modules/programs/ssmtp.nix
@@ -0,0 +1,190 @@
+# Configuration for `ssmtp', a trivial mail transfer agent that can
+# replace sendmail/postfix on simple systems.  It delivers email
+# directly to an SMTP server defined in its configuration file, without
+# queueing mail locally.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.ssmtp;
+
+in
+{
+
+  imports = [
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "directDelivery" ] [ "services" "ssmtp" "enable" ])
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "hostName" ] [ "services" "ssmtp" "hostName" ])
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "domain" ] [ "services" "ssmtp" "domain" ])
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "root" ] [ "services" "ssmtp" "root" ])
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "useTLS" ] [ "services" "ssmtp" "useTLS" ])
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "useSTARTTLS" ] [ "services" "ssmtp" "useSTARTTLS" ])
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "authUser" ] [ "services" "ssmtp" "authUser" ])
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "authPassFile" ] [ "services" "ssmtp" "authPassFile" ])
+    (mkRenamedOptionModule [ "networking" "defaultMailServer" "setSendmail" ] [ "services" "ssmtp" "setSendmail" ])
+
+    (mkRemovedOptionModule [ "networking" "defaultMailServer" "authPass" ] "authPass has been removed since it leaks the clear-text password into the world-readable store. Use authPassFile instead and make sure it's not a store path")
+    (mkRemovedOptionModule [ "services" "ssmtp" "authPass" ] "authPass has been removed since it leaks the clear-text password into the world-readable store. Use authPassFile instead and make sure it's not a store path")
+  ];
+
+  options = {
+
+    services.ssmtp = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Use the trivial Mail Transfer Agent (MTA)
+          <command>ssmtp</command> package to allow programs to send
+          e-mail.  If you don't want to run a “real” MTA like
+          <command>sendmail</command> or <command>postfix</command> on
+          your machine, set this option to <literal>true</literal>, and
+          set the option
+          <option>services.ssmtp.hostName</option> to the
+          host name of your preferred mail server.
+        '';
+      };
+
+      settings = mkOption {
+        type = with types; attrsOf (oneOf [ bool str ]);
+        default = {};
+        description = ''
+          <citerefentry><refentrytitle>ssmtp</refentrytitle><manvolnum>5</manvolnum></citerefentry> configuration. Refer
+          to <link xlink:href="https://linux.die.net/man/5/ssmtp.conf"/> for details on supported values.
+        '';
+        example = literalExpression ''
+          {
+            Debug = true;
+            FromLineOverride = false;
+          }
+        '';
+      };
+
+      hostName = mkOption {
+        type = types.str;
+        example = "mail.example.org";
+        description = ''
+          The host name of the default mail server to use to deliver
+          e-mail. Can also contain a port number (ex: mail.example.org:587),
+          defaults to port 25 if no port is given.
+        '';
+      };
+
+      root = mkOption {
+        type = types.str;
+        default = "";
+        example = "root@example.org";
+        description = ''
+          The e-mail to which mail for users with UID &lt; 1000 is forwarded.
+        '';
+      };
+
+      domain = mkOption {
+        type = types.str;
+        default = "";
+        example = "example.org";
+        description = ''
+          The domain from which mail will appear to be sent.
+        '';
+      };
+
+      useTLS = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether TLS should be used to connect to the default mail
+          server.
+        '';
+      };
+
+      useSTARTTLS = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether the STARTTLS should be used to connect to the default
+          mail server.  (This is needed for TLS-capable mail servers
+          running on the default SMTP port 25.)
+        '';
+      };
+
+      authUser = mkOption {
+        type = types.str;
+        default = "";
+        example = "foo@example.org";
+        description = ''
+          Username used for SMTP auth. Leave blank to disable.
+        '';
+      };
+
+      authPassFile = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/run/keys/ssmtp-authpass";
+        description = ''
+          Path to a file that contains the password used for SMTP auth. The file
+          should not contain a trailing newline, if the password does not contain one
+          (e.g. use <command>echo -n "password" > file</command>).
+          This file should be readable by the users that need to execute ssmtp.
+        '';
+      };
+
+      setSendmail = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Whether to set the system sendmail to ssmtp's.";
+      };
+
+    };
+
+  };
+
+
+  config = mkIf cfg.enable {
+
+    assertions = [
+      {
+        assertion = cfg.useSTARTTLS -> cfg.useTLS;
+        message = "services.ssmtp.useSTARTTLS has no effect without services.ssmtp.useTLS";
+      }
+    ];
+
+    services.ssmtp.settings = mkMerge [
+      ({
+        MailHub = cfg.hostName;
+        FromLineOverride = mkDefault true;
+        UseTLS = cfg.useTLS;
+        UseSTARTTLS = cfg.useSTARTTLS;
+      })
+      (mkIf (cfg.root != "") { root = cfg.root; })
+      (mkIf (cfg.domain != "") { rewriteDomain = cfg.domain; })
+      (mkIf (cfg.authUser != "") { AuthUser = cfg.authUser; })
+      (mkIf (cfg.authPassFile != null) { AuthPassFile = cfg.authPassFile; })
+    ];
+
+    # careful here: ssmtp REQUIRES all config lines to end with a newline char!
+    environment.etc."ssmtp/ssmtp.conf".text = with generators; toKeyValue {
+      mkKeyValue = mkKeyValueDefault {
+        mkValueString = value:
+          if value == true then "YES"
+          else if value == false then "NO"
+          else mkValueStringDefault {} value
+        ;
+      } "=";
+    } cfg.settings;
+
+    environment.systemPackages = [pkgs.ssmtp];
+
+    services.mail.sendmailSetuidWrapper = mkIf cfg.setSendmail {
+      program = "sendmail";
+      source = "${pkgs.ssmtp}/bin/sendmail";
+      setuid = false;
+      setgid = false;
+      owner = "root";
+      group = "root";
+    };
+
+  };
+
+}
diff --git a/nixos/modules/programs/starship.nix b/nixos/modules/programs/starship.nix
new file mode 100644
index 00000000000..83d2272003c
--- /dev/null
+++ b/nixos/modules/programs/starship.nix
@@ -0,0 +1,51 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.starship;
+
+  settingsFormat = pkgs.formats.toml { };
+
+  settingsFile = settingsFormat.generate "starship.toml" cfg.settings;
+
+in {
+  options.programs.starship = {
+    enable = mkEnableOption "the Starship shell prompt";
+
+    settings = mkOption {
+      inherit (settingsFormat) type;
+      default = { };
+      description = ''
+        Configuration included in <literal>starship.toml</literal>.
+
+        See https://starship.rs/config/#prompt for documentation.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    programs.bash.promptInit = ''
+      if [[ $TERM != "dumb" && (-z $INSIDE_EMACS || $INSIDE_EMACS == "vterm") ]]; then
+        export STARSHIP_CONFIG=${settingsFile}
+        eval "$(${pkgs.starship}/bin/starship init bash)"
+      fi
+    '';
+
+    programs.fish.promptInit = ''
+      if test "$TERM" != "dumb" -a \( -z "$INSIDE_EMACS" -o "$INSIDE_EMACS" = "vterm" \)
+        set -x STARSHIP_CONFIG ${settingsFile}
+        eval (${pkgs.starship}/bin/starship init fish)
+      end
+    '';
+
+    programs.zsh.promptInit = ''
+      if [[ $TERM != "dumb" && (-z $INSIDE_EMACS || $INSIDE_EMACS == "vterm") ]]; then
+        export STARSHIP_CONFIG=${settingsFile}
+        eval "$(${pkgs.starship}/bin/starship init zsh)"
+      fi
+    '';
+  };
+
+  meta.maintainers = pkgs.starship.meta.maintainers;
+}
diff --git a/nixos/modules/programs/steam.nix b/nixos/modules/programs/steam.nix
new file mode 100644
index 00000000000..ff4deba2bf0
--- /dev/null
+++ b/nixos/modules/programs/steam.nix
@@ -0,0 +1,63 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.steam;
+
+  steam = pkgs.steam.override {
+    extraLibraries = pkgs: with config.hardware.opengl;
+      if pkgs.hostPlatform.is64bit
+      then [ package ] ++ extraPackages
+      else [ package32 ] ++ extraPackages32;
+  };
+in {
+  options.programs.steam = {
+    enable = mkEnableOption "steam";
+
+    remotePlay.openFirewall = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Open ports in the firewall for Steam Remote Play.
+      '';
+    };
+
+    dedicatedServer.openFirewall = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Open ports in the firewall for Source Dedicated Server.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    hardware.opengl = { # this fixes the "glXChooseVisual failed" bug, context: https://github.com/NixOS/nixpkgs/issues/47932
+      enable = true;
+      driSupport = true;
+      driSupport32Bit = true;
+    };
+
+    # optionally enable 32bit pulseaudio support if pulseaudio is enabled
+    hardware.pulseaudio.support32Bit = config.hardware.pulseaudio.enable;
+
+    hardware.steam-hardware.enable = true;
+
+    environment.systemPackages = [ steam steam.run ];
+
+    networking.firewall = lib.mkMerge [
+      (mkIf cfg.remotePlay.openFirewall {
+        allowedTCPPorts = [ 27036 ];
+        allowedUDPPortRanges = [ { from = 27031; to = 27036; } ];
+      })
+
+      (mkIf cfg.dedicatedServer.openFirewall {
+        allowedTCPPorts = [ 27015 ]; # SRCDS Rcon port
+        allowedUDPPorts = [ 27015 ]; # Gameplay traffic
+      })
+    ];
+  };
+
+  meta.maintainers = with maintainers; [ mkg20001 ];
+}
diff --git a/nixos/modules/programs/sway.nix b/nixos/modules/programs/sway.nix
new file mode 100644
index 00000000000..01b04728134
--- /dev/null
+++ b/nixos/modules/programs/sway.nix
@@ -0,0 +1,150 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.sway;
+
+  wrapperOptions = types.submodule {
+    options =
+      let
+        mkWrapperFeature  = default: description: mkOption {
+          type = types.bool;
+          inherit default;
+          example = !default;
+          description = "Whether to make use of the ${description}";
+        };
+      in {
+        base = mkWrapperFeature true ''
+          base wrapper to execute extra session commands and prepend a
+          dbus-run-session to the sway command.
+        '';
+        gtk = mkWrapperFeature false ''
+          wrapGAppsHook wrapper to execute sway with required environment
+          variables for GTK applications.
+        '';
+    };
+  };
+
+  swayPackage = pkgs.sway.override {
+    extraSessionCommands = cfg.extraSessionCommands;
+    extraOptions = cfg.extraOptions;
+    withBaseWrapper = cfg.wrapperFeatures.base;
+    withGtkWrapper = cfg.wrapperFeatures.gtk;
+    isNixOS = true;
+  };
+in {
+  options.programs.sway = {
+    enable = mkEnableOption ''
+      Sway, the i3-compatible tiling Wayland compositor. You can manually launch
+      Sway by executing "exec sway" on a TTY. Copy /etc/sway/config to
+      ~/.config/sway/config to modify the default configuration. See
+      <link xlink:href="https://github.com/swaywm/sway/wiki" /> and
+      "man 5 sway" for more information'';
+
+    wrapperFeatures = mkOption {
+      type = wrapperOptions;
+      default = { };
+      example = { gtk = true; };
+      description = ''
+        Attribute set of features to enable in the wrapper.
+      '';
+    };
+
+    extraSessionCommands = mkOption {
+      type = types.lines;
+      default = "";
+      example = ''
+        # SDL:
+        export SDL_VIDEODRIVER=wayland
+        # QT (needs qt5.qtwayland in systemPackages):
+        export QT_QPA_PLATFORM=wayland-egl
+        export QT_WAYLAND_DISABLE_WINDOWDECORATION="1"
+        # Fix for some Java AWT applications (e.g. Android Studio),
+        # use this if they aren't displayed properly:
+        export _JAVA_AWT_WM_NONREPARENTING=1
+      '';
+      description = ''
+        Shell commands executed just before Sway is started. See
+        <link xlink:href="https://github.com/swaywm/sway/wiki/Running-programs-natively-under-wayland" />
+        and <link xlink:href="https://github.com/swaywm/wlroots/blob/master/docs/env_vars.md" />
+        for some useful environment variables.
+      '';
+    };
+
+    extraOptions = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = [
+        "--verbose"
+        "--debug"
+        "--unsupported-gpu"
+        "--my-next-gpu-wont-be-nvidia"
+      ];
+      description = ''
+        Command line arguments passed to launch Sway. Please DO NOT report
+        issues if you use an unsupported GPU (proprietary drivers).
+      '';
+    };
+
+    extraPackages = mkOption {
+      type = with types; listOf package;
+      default = with pkgs; [
+        swaylock swayidle foot dmenu
+      ];
+      defaultText = literalExpression ''
+        with pkgs; [ swaylock swayidle foot dmenu ];
+      '';
+      example = literalExpression ''
+        with pkgs; [
+          i3status i3status-rust
+          termite rofi light
+        ]
+      '';
+      description = ''
+        Extra packages to be installed system wide. See
+        <link xlink:href="https://github.com/swaywm/sway/wiki/Useful-add-ons-for-sway" /> and
+        <link xlink:href="https://github.com/swaywm/sway/wiki/i3-Migration-Guide#common-x11-apps-used-on-i3-with-wayland-alternatives" />
+        for a list of useful software.
+      '';
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = cfg.extraSessionCommands != "" -> cfg.wrapperFeatures.base;
+        message = ''
+          The extraSessionCommands for Sway will not be run if
+          wrapperFeatures.base is disabled.
+        '';
+      }
+    ];
+    environment = {
+      systemPackages = [ swayPackage ] ++ cfg.extraPackages;
+      # Needed for the default wallpaper:
+      pathsToLink = [ "/share/backgrounds/sway" ];
+      etc = {
+        "sway/config".source = mkOptionDefault "${swayPackage}/etc/sway/config";
+        "sway/config.d/nixos.conf".source = pkgs.writeText "nixos.conf" ''
+          # Import the most important environment variables into the D-Bus and systemd
+          # user environments (e.g. required for screen sharing and Pinentry prompts):
+          exec dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP
+        '';
+      };
+    };
+    security.polkit.enable = true;
+    security.pam.services.swaylock = {};
+    hardware.opengl.enable = mkDefault true;
+    fonts.enableDefaultFonts = mkDefault true;
+    programs.dconf.enable = mkDefault true;
+    # To make a Sway session available if a display manager like SDDM is enabled:
+    services.xserver.displayManager.sessionPackages = [ swayPackage ];
+    programs.xwayland.enable = mkDefault true;
+    # For screen sharing (this option only has an effect with xdg.portal.enable):
+    xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-wlr ];
+  };
+
+  meta.maintainers = with lib.maintainers; [ primeos colemickens ];
+}
diff --git a/nixos/modules/programs/sysdig.nix b/nixos/modules/programs/sysdig.nix
new file mode 100644
index 00000000000..fbbf2906556
--- /dev/null
+++ b/nixos/modules/programs/sysdig.nix
@@ -0,0 +1,14 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.sysdig;
+in {
+  options.programs.sysdig.enable = mkEnableOption "sysdig";
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.sysdig ];
+    boot.extraModulePackages = [ config.boot.kernelPackages.sysdig ];
+  };
+}
diff --git a/nixos/modules/programs/system-config-printer.nix b/nixos/modules/programs/system-config-printer.nix
new file mode 100644
index 00000000000..34592dd7064
--- /dev/null
+++ b/nixos/modules/programs/system-config-printer.nix
@@ -0,0 +1,32 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    programs.system-config-printer = {
+
+      enable = mkEnableOption "system-config-printer, a Graphical user interface for CUPS administration";
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.programs.system-config-printer.enable {
+
+    environment.systemPackages = [
+      pkgs.system-config-printer
+    ];
+
+    services.system-config-printer.enable = true;
+
+  };
+
+}
diff --git a/nixos/modules/programs/systemtap.nix b/nixos/modules/programs/systemtap.nix
new file mode 100644
index 00000000000..360e106678e
--- /dev/null
+++ b/nixos/modules/programs/systemtap.nix
@@ -0,0 +1,29 @@
+{ config, lib, ... }:
+
+with lib;
+
+let cfg = config.programs.systemtap;
+in {
+
+  options = {
+    programs.systemtap = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Install <command>systemtap</command> along with necessary kernel options.
+        '';
+      };
+    };
+  };
+  config = mkIf cfg.enable {
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      (isYes "DEBUG")
+    ];
+    boot.kernel.features.debug = true;
+    environment.systemPackages = [
+      config.boot.kernelPackages.systemtap
+    ];
+  };
+
+}
diff --git a/nixos/modules/programs/thefuck.nix b/nixos/modules/programs/thefuck.nix
new file mode 100644
index 00000000000..b909916158d
--- /dev/null
+++ b/nixos/modules/programs/thefuck.nix
@@ -0,0 +1,39 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  prg = config.programs;
+  cfg = prg.thefuck;
+
+  initScript = ''
+    eval $(${pkgs.thefuck}/bin/thefuck --alias ${cfg.alias})
+  '';
+in
+  {
+    options = {
+      programs.thefuck = {
+        enable = mkEnableOption "thefuck";
+
+        alias = mkOption {
+          default = "fuck";
+          type = types.str;
+
+          description = ''
+            `thefuck` needs an alias to be configured.
+            The default value is `fuck`, but you can use anything else as well.
+          '';
+        };
+      };
+    };
+
+    config = mkIf cfg.enable {
+      environment.systemPackages = with pkgs; [ thefuck ];
+
+      programs.bash.interactiveShellInit = initScript;
+      programs.zsh.interactiveShellInit = mkIf prg.zsh.enable initScript;
+      programs.fish.interactiveShellInit = mkIf prg.fish.enable ''
+        ${pkgs.thefuck}/bin/thefuck --alias | source
+      '';
+    };
+  }
diff --git a/nixos/modules/programs/tmux.nix b/nixos/modules/programs/tmux.nix
new file mode 100644
index 00000000000..74b3fbd9ac0
--- /dev/null
+++ b/nixos/modules/programs/tmux.nix
@@ -0,0 +1,201 @@
+{ config, pkgs, lib, ... }:
+
+let
+  inherit (lib) mkOption mkIf types;
+
+  cfg = config.programs.tmux;
+
+  defaultKeyMode  = "emacs";
+  defaultResize   = 5;
+  defaultShortcut = "b";
+  defaultTerminal = "screen";
+
+  boolToStr = value: if value then "on" else "off";
+
+  tmuxConf = ''
+    set  -g default-terminal "${cfg.terminal}"
+    set  -g base-index      ${toString cfg.baseIndex}
+    setw -g pane-base-index ${toString cfg.baseIndex}
+
+    ${if cfg.newSession then "new-session" else ""}
+
+    ${if cfg.reverseSplit then ''
+    bind v split-window -h
+    bind s split-window -v
+    '' else ""}
+
+    set -g status-keys ${cfg.keyMode}
+    set -g mode-keys   ${cfg.keyMode}
+
+    ${if cfg.keyMode == "vi" && cfg.customPaneNavigationAndResize then ''
+    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 ${toString cfg.resizeAmount}
+    bind -r J resize-pane -D ${toString cfg.resizeAmount}
+    bind -r K resize-pane -U ${toString cfg.resizeAmount}
+    bind -r L resize-pane -R ${toString cfg.resizeAmount}
+    '' else ""}
+
+    ${if (cfg.shortcut != defaultShortcut) then ''
+    # rebind main key: C-${cfg.shortcut}
+    unbind C-${defaultShortcut}
+    set -g prefix C-${cfg.shortcut}
+    bind ${cfg.shortcut} send-prefix
+    bind C-${cfg.shortcut} last-window
+    '' else ""}
+
+    setw -g aggressive-resize ${boolToStr cfg.aggressiveResize}
+    setw -g clock-mode-style  ${if cfg.clock24 then "24" else "12"}
+    set  -s escape-time       ${toString cfg.escapeTime}
+    set  -g history-limit     ${toString cfg.historyLimit}
+
+    ${lib.optionalString (cfg.plugins != []) ''
+    # Run plugins
+    ${lib.concatMapStringsSep "\n" (x: "run-shell ${x.rtp}") cfg.plugins}
+
+    ''}
+
+    ${cfg.extraConfig}
+  '';
+
+in {
+  ###### interface
+
+  options = {
+    programs.tmux = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whenever to configure <command>tmux</command> system-wide.";
+        relatedPackages = [ "tmux" ];
+      };
+
+      aggressiveResize = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Resize the window to the size of the smallest session for which it is the current window.
+        '';
+      };
+
+      baseIndex = mkOption {
+        default = 0;
+        example = 1;
+        type = types.int;
+        description = "Base index for windows and panes.";
+      };
+
+      clock24 = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Use 24 hour clock.";
+      };
+
+      customPaneNavigationAndResize = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Override the hjkl and HJKL bindings for pane navigation and resizing in VI mode.";
+      };
+
+      escapeTime = mkOption {
+        default = 500;
+        example = 0;
+        type = types.int;
+        description = "Time in milliseconds for which tmux waits after an escape is input.";
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        description = ''
+          Additional contents of /etc/tmux.conf
+        '';
+        type = types.lines;
+      };
+
+      historyLimit = mkOption {
+        default = 2000;
+        example = 5000;
+        type = types.int;
+        description = "Maximum number of lines held in window history.";
+      };
+
+      keyMode = mkOption {
+        default = defaultKeyMode;
+        example = "vi";
+        type = types.enum [ "emacs" "vi" ];
+        description = "VI or Emacs style shortcuts.";
+      };
+
+      newSession = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Automatically spawn a session if trying to attach and none are running.";
+      };
+
+      reverseSplit = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Reverse the window split shortcuts.";
+      };
+
+      resizeAmount = mkOption {
+        default = defaultResize;
+        example = 10;
+        type = types.int;
+        description = "Number of lines/columns when resizing.";
+      };
+
+      shortcut = mkOption {
+        default = defaultShortcut;
+        example = "a";
+        type = types.str;
+        description = "Ctrl following by this key is used as the main shortcut.";
+      };
+
+      terminal = mkOption {
+        default = defaultTerminal;
+        example = "screen-256color";
+        type = types.str;
+        description = "Set the $TERM variable.";
+      };
+
+      secureSocket = mkOption {
+        default = true;
+        type = types.bool;
+        description = ''
+          Store tmux socket under /run, which is more secure than /tmp, but as a
+          downside it doesn't survive user logout.
+        '';
+      };
+
+      plugins = mkOption {
+        default = [];
+        type = types.listOf types.package;
+        description = "List of plugins to install.";
+        example = lib.literalExpression "[ pkgs.tmuxPlugins.nord ]";
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment = {
+      etc."tmux.conf".text = tmuxConf;
+
+      systemPackages = [ pkgs.tmux ] ++ cfg.plugins;
+
+      variables = {
+        TMUX_TMPDIR = lib.optional cfg.secureSocket ''''${XDG_RUNTIME_DIR:-"/run/user/$(id -u)"}'';
+      };
+    };
+  };
+
+  imports = [
+    (lib.mkRenamedOptionModule [ "programs" "tmux" "extraTmuxConf" ] [ "programs" "tmux" "extraConfig" ])
+  ];
+}
diff --git a/nixos/modules/programs/traceroute.nix b/nixos/modules/programs/traceroute.nix
new file mode 100644
index 00000000000..6e04057ac50
--- /dev/null
+++ b/nixos/modules/programs/traceroute.nix
@@ -0,0 +1,28 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.traceroute;
+in {
+  options = {
+    programs.traceroute = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to configure a setcap wrapper for traceroute.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    security.wrappers.traceroute = {
+      owner = "root";
+      group = "root";
+      capabilities = "cap_net_raw+p";
+      source = "${pkgs.traceroute}/bin/traceroute";
+    };
+  };
+}
diff --git a/nixos/modules/programs/tsm-client.nix b/nixos/modules/programs/tsm-client.nix
new file mode 100644
index 00000000000..28db9625387
--- /dev/null
+++ b/nixos/modules/programs/tsm-client.nix
@@ -0,0 +1,287 @@
+{ config, lib, pkgs, ... }:
+
+let
+
+  inherit (builtins) length map;
+  inherit (lib.attrsets) attrNames filterAttrs hasAttr mapAttrs mapAttrsToList optionalAttrs;
+  inherit (lib.modules) mkDefault mkIf;
+  inherit (lib.options) literalExpression mkEnableOption mkOption;
+  inherit (lib.strings) concatStringsSep optionalString toLower;
+  inherit (lib.types) addCheck attrsOf lines nonEmptyStr nullOr package path port str strMatching submodule;
+
+  # Checks if given list of strings contains unique
+  # elements when compared without considering case.
+  # Type: checkIUnique :: [string] -> bool
+  # Example: checkIUnique ["foo" "Foo"] => false
+  checkIUnique = lst:
+    let
+      lenUniq = l: length (lib.lists.unique l);
+    in
+      lenUniq lst == lenUniq (map toLower lst);
+
+  # TSM rejects servername strings longer than 64 chars.
+  servernameType = strMatching ".{1,64}";
+
+  serverOptions = { name, config, ... }: {
+    options.name = mkOption {
+      type = servernameType;
+      example = "mainTsmServer";
+      description = ''
+        Local name of the IBM TSM server,
+        must be uncapitalized and no longer than 64 chars.
+        The value will be used for the
+        <literal>server</literal>
+        directive in <filename>dsm.sys</filename>.
+      '';
+    };
+    options.server = mkOption {
+      type = nonEmptyStr;
+      example = "tsmserver.company.com";
+      description = ''
+        Host/domain name or IP address of the IBM TSM server.
+        The value will be used for the
+        <literal>tcpserveraddress</literal>
+        directive in <filename>dsm.sys</filename>.
+      '';
+    };
+    options.port = mkOption {
+      type = addCheck port (p: p<=32767);
+      default = 1500;  # official default
+      description = ''
+        TCP port of the IBM TSM server.
+        The value will be used for the
+        <literal>tcpport</literal>
+        directive in <filename>dsm.sys</filename>.
+        TSM does not support ports above 32767.
+      '';
+    };
+    options.node = mkOption {
+      type = nonEmptyStr;
+      example = "MY-TSM-NODE";
+      description = ''
+        Target node name on the IBM TSM server.
+        The value will be used for the
+        <literal>nodename</literal>
+        directive in <filename>dsm.sys</filename>.
+      '';
+    };
+    options.genPasswd = mkEnableOption ''
+      automatic client password generation.
+      This option influences the
+      <literal>passwordaccess</literal>
+      directive in <filename>dsm.sys</filename>.
+      The password will be stored in the directory
+      given by the option <option>passwdDir</option>.
+      <emphasis>Caution</emphasis>:
+      If this option is enabled and the server forces
+      to renew the password (e.g. on first connection),
+      a random password will be generated and stored
+    '';
+    options.passwdDir = mkOption {
+      type = path;
+      example = "/home/alice/tsm-password";
+      description = ''
+        Directory that holds the TSM
+        node's password information.
+        The value will be used for the
+        <literal>passworddir</literal>
+        directive in <filename>dsm.sys</filename>.
+      '';
+    };
+    options.includeExclude = mkOption {
+      type = lines;
+      default = "";
+      example = ''
+        exclude.dir     /nix/store
+        include.encrypt /home/.../*
+      '';
+      description = ''
+        <literal>include.*</literal> and
+        <literal>exclude.*</literal> directives to be
+        used when sending files to the IBM TSM server.
+        The lines will be written into a file that the
+        <literal>inclexcl</literal>
+        directive in <filename>dsm.sys</filename> points to.
+      '';
+    };
+    options.extraConfig = mkOption {
+      # TSM option keys are case insensitive;
+      # we have to ensure there are no keys that
+      # differ only by upper and lower case.
+      type = addCheck
+        (attrsOf (nullOr str))
+        (attrs: checkIUnique (attrNames attrs));
+      default = {};
+      example.compression = "yes";
+      example.passwordaccess = null;
+      description = ''
+        Additional key-value pairs for the server stanza.
+        Values must be strings, or <literal>null</literal>
+        for the key not to be used in the stanza
+        (e.g. to overrule values generated by other options).
+      '';
+    };
+    options.text = mkOption {
+      type = lines;
+      example = literalExpression
+        ''lib.modules.mkAfter "compression no"'';
+      description = ''
+        Additional text lines for the server stanza.
+        This option can be used if certion configuration keys
+        must be used multiple times or ordered in a certain way
+        as the <option>extraConfig</option> option can't
+        control the order of lines in the resulting stanza.
+        Note that the <literal>server</literal>
+        line at the beginning of the stanza is
+        not part of this option's value.
+      '';
+    };
+    options.stanza = mkOption {
+      type = str;
+      internal = true;
+      visible = false;
+      description = "Server stanza text generated from the options.";
+    };
+    config.name = mkDefault name;
+    # Client system-options file directives are explained here:
+    # https://www.ibm.com/docs/en/spectrum-protect/8.1.13?topic=commands-processing-options
+    config.extraConfig =
+      mapAttrs (lib.trivial.const mkDefault) (
+        {
+          commmethod = "v6tcpip";  # uses v4 or v6, based on dns lookup result
+          tcpserveraddress = config.server;
+          tcpport = builtins.toString config.port;
+          nodename = config.node;
+          passwordaccess = if config.genPasswd then "generate" else "prompt";
+          passworddir = ''"${config.passwdDir}"'';
+        } // optionalAttrs (config.includeExclude!="") {
+          inclexcl = ''"${pkgs.writeText "inclexcl.dsm.sys" config.includeExclude}"'';
+        }
+      );
+    config.text =
+      let
+        attrset = filterAttrs (k: v: v!=null) config.extraConfig;
+        mkLine = k: v: k + optionalString (v!="") "  ${v}";
+        lines = mapAttrsToList mkLine attrset;
+      in
+        concatStringsSep "\n" lines;
+    config.stanza = ''
+      server  ${config.name}
+      ${config.text}
+    '';
+  };
+
+  options.programs.tsmClient = {
+    enable = mkEnableOption ''
+      IBM Spectrum Protect (Tivoli Storage Manager, TSM)
+      client command line applications with a
+      client system-options file "dsm.sys"
+    '';
+    servers = mkOption {
+      type = attrsOf (submodule [ serverOptions ]);
+      default = {};
+      example.mainTsmServer = {
+        server = "tsmserver.company.com";
+        node = "MY-TSM-NODE";
+        extraConfig.compression = "yes";
+      };
+      description = ''
+        Server definitions ("stanzas")
+        for the client system-options file.
+      '';
+    };
+    defaultServername = mkOption {
+      type = nullOr servernameType;
+      default = null;
+      example = "mainTsmServer";
+      description = ''
+        If multiple server stanzas are declared with
+        <option>programs.tsmClient.servers</option>,
+        this option may be used to name a default
+        server stanza that IBM TSM uses in the absence of
+        a user-defined <filename>dsm.opt</filename> file.
+        This option translates to a
+        <literal>defaultserver</literal> configuration line.
+      '';
+    };
+    dsmSysText = mkOption {
+      type = lines;
+      readOnly = true;
+      description = ''
+        This configuration key contains the effective text
+        of the client system-options file "dsm.sys".
+        It should not be changed, but may be
+        used to feed the configuration into other
+        TSM-depending packages used on the system.
+      '';
+    };
+    package = mkOption {
+      type = package;
+      default = pkgs.tsm-client;
+      defaultText = literalExpression "pkgs.tsm-client";
+      example = literalExpression "pkgs.tsm-client-withGui";
+      description = ''
+        The TSM client derivation to be
+        added to the system environment.
+        It will called with <literal>.override</literal>
+        to add paths to the client system-options file.
+      '';
+    };
+    wrappedPackage = mkOption {
+      type = package;
+      readOnly = true;
+      description = ''
+        The TSM client derivation, wrapped with the path
+        to the client system-options file "dsm.sys".
+        This option is to provide the effective derivation
+        for other modules that want to call TSM executables.
+      '';
+    };
+  };
+
+  cfg = config.programs.tsmClient;
+
+  assertions = [
+    {
+      assertion = checkIUnique (mapAttrsToList (k: v: v.name) cfg.servers);
+      message = ''
+        TSM servernames contain duplicate name
+        (note that case doesn't matter!)
+      '';
+    }
+    {
+      assertion = (cfg.defaultServername!=null)->(hasAttr cfg.defaultServername cfg.servers);
+      message = "TSM defaultServername not found in list of servers";
+    }
+  ];
+
+  dsmSysText = ''
+    ****  IBM Spectrum Protect (Tivoli Storage Manager)
+    ****  client system-options file "dsm.sys".
+    ****  Do not edit!
+    ****  This file is generated by NixOS configuration.
+
+    ${optionalString (cfg.defaultServername!=null) "defaultserver  ${cfg.defaultServername}"}
+
+    ${concatStringsSep "\n" (mapAttrsToList (k: v: v.stanza) cfg.servers)}
+  '';
+
+in
+
+{
+
+  inherit options;
+
+  config = mkIf cfg.enable {
+    inherit assertions;
+    programs.tsmClient.dsmSysText = dsmSysText;
+    programs.tsmClient.wrappedPackage = cfg.package.override rec {
+      dsmSysCli = pkgs.writeText "dsm.sys" cfg.dsmSysText;
+      dsmSysApi = dsmSysCli;
+    };
+    environment.systemPackages = [ cfg.wrappedPackage ];
+  };
+
+  meta.maintainers = [ lib.maintainers.yarny ];
+
+}
diff --git a/nixos/modules/programs/turbovnc.nix b/nixos/modules/programs/turbovnc.nix
new file mode 100644
index 00000000000..e6f8836aa36
--- /dev/null
+++ b/nixos/modules/programs/turbovnc.nix
@@ -0,0 +1,54 @@
+# Global configuration for the SSH client.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.turbovnc;
+in
+{
+  options = {
+
+    programs.turbovnc = {
+
+      ensureHeadlessSoftwareOpenGL = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to set up NixOS such that TurboVNC's built-in software OpenGL
+          implementation works.
+
+          This will enable <option>hardware.opengl.enable</option> so that OpenGL
+          programs can find Mesa's llvmpipe drivers.
+
+          Setting this option to <code>false</code> does not mean that software
+          OpenGL won't work; it may still work depending on your system
+          configuration.
+
+          This option is also intended to generate warnings if you are using some
+          configuration that's incompatible with using headless software OpenGL
+          in TurboVNC.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.ensureHeadlessSoftwareOpenGL {
+
+    # TurboVNC has builtin support for Mesa llvmpipe's `swrast`
+    # software rendering to implemnt GLX (OpenGL on Xorg).
+    # However, just building TurboVNC with support for that is not enough
+    # (it only takes care of the X server side part of OpenGL);
+    # the indiviudual applications (e.g. `glxgears`) also need to directly load
+    # the OpenGL libs.
+    # Thus, this creates `/run/opengl-driver` populated by Mesa so that the applications
+    # can find the llvmpipe `swrast.so` software rendering DRI lib via `libglvnd`.
+    # This comment exists to explain why `hardware.` is involved,
+    # even though 100% software rendering is used.
+    hardware.opengl.enable = true;
+
+  };
+}
diff --git a/nixos/modules/programs/udevil.nix b/nixos/modules/programs/udevil.nix
new file mode 100644
index 00000000000..0dc08c435df
--- /dev/null
+++ b/nixos/modules/programs/udevil.nix
@@ -0,0 +1,19 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.udevil;
+
+in {
+  options.programs.udevil.enable = mkEnableOption "udevil";
+
+  config = mkIf cfg.enable {
+    security.wrappers.udevil =
+      { setuid = true;
+        owner = "root";
+        group = "root";
+        source = "${lib.getBin pkgs.udevil}/bin/udevil";
+      };
+  };
+}
diff --git a/nixos/modules/programs/usbtop.nix b/nixos/modules/programs/usbtop.nix
new file mode 100644
index 00000000000..c1b6ee38caa
--- /dev/null
+++ b/nixos/modules/programs/usbtop.nix
@@ -0,0 +1,21 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.usbtop;
+in {
+  options = {
+    programs.usbtop.enable = mkEnableOption "usbtop and required kernel module";
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = with pkgs; [
+      usbtop
+    ];
+
+    boot.kernelModules = [
+      "usbmon"
+    ];
+  };
+}
diff --git a/nixos/modules/programs/vim.nix b/nixos/modules/programs/vim.nix
new file mode 100644
index 00000000000..1695bc99473
--- /dev/null
+++ b/nixos/modules/programs/vim.nix
@@ -0,0 +1,33 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.vim;
+in {
+  options.programs.vim = {
+    defaultEditor = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        When enabled, installs vim and configures vim to be the default editor
+        using the EDITOR environment variable.
+      '';
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.vim;
+      defaultText = literalExpression "pkgs.vim";
+      example = literalExpression "pkgs.vimHugeX";
+      description = ''
+        vim package to use.
+      '';
+    };
+  };
+
+  config = mkIf cfg.defaultEditor {
+    environment.systemPackages = [ cfg.package ];
+    environment.variables = { EDITOR = mkOverride 900 "vim"; };
+  };
+}
diff --git a/nixos/modules/programs/virtualbox.nix b/nixos/modules/programs/virtualbox.nix
new file mode 100644
index 00000000000..be96cf23b39
--- /dev/null
+++ b/nixos/modules/programs/virtualbox.nix
@@ -0,0 +1,8 @@
+let
+  msg = "Importing <nixpkgs/nixos/modules/programs/virtualbox.nix> is "
+      + "deprecated, please use `virtualisation.virtualbox.host.enable = true' "
+      + "instead.";
+in {
+  config.warnings = [ msg ];
+  config.virtualisation.virtualbox.host.enable = true;
+}
diff --git a/nixos/modules/programs/wavemon.nix b/nixos/modules/programs/wavemon.nix
new file mode 100644
index 00000000000..e5ccacba75d
--- /dev/null
+++ b/nixos/modules/programs/wavemon.nix
@@ -0,0 +1,30 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.wavemon;
+in {
+  options = {
+    programs.wavemon = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to add wavemon to the global environment and configure a
+          setcap wrapper for it.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = with pkgs; [ wavemon ];
+    security.wrappers.wavemon = {
+      owner = "root";
+      group = "root";
+      capabilities = "cap_net_admin+ep";
+      source = "${pkgs.wavemon}/bin/wavemon";
+    };
+  };
+}
diff --git a/nixos/modules/programs/waybar.nix b/nixos/modules/programs/waybar.nix
new file mode 100644
index 00000000000..22530e6c7d4
--- /dev/null
+++ b/nixos/modules/programs/waybar.nix
@@ -0,0 +1,20 @@
+{ lib, pkgs, config, ... }:
+
+with lib;
+
+{
+  options.programs.waybar = {
+    enable = mkEnableOption "waybar";
+  };
+
+  config = mkIf config.programs.waybar.enable {
+    systemd.user.services.waybar = {
+      description = "Waybar as systemd service";
+      wantedBy = [ "graphical-session.target" ];
+      partOf = [ "graphical-session.target" ];
+      script = "${pkgs.waybar}/bin/waybar";
+    };
+  };
+
+  meta.maintainers = [ maintainers.FlorianFranzen ];
+}
diff --git a/nixos/modules/programs/weylus.nix b/nixos/modules/programs/weylus.nix
new file mode 100644
index 00000000000..ea92c77e7c3
--- /dev/null
+++ b/nixos/modules/programs/weylus.nix
@@ -0,0 +1,47 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.weylus;
+in
+{
+  options.programs.weylus = with types; {
+    enable = mkEnableOption "weylus";
+
+    openFirewall = mkOption {
+      type = bool;
+      default = false;
+      description = ''
+        Open ports needed for the functionality of the program.
+      '';
+    };
+
+     users = mkOption {
+      type = listOf str;
+      default = [ ];
+      description = ''
+        To enable stylus and multi-touch support, the user you're going to use must be added to this list.
+        These users can synthesize input events system-wide, even when another user is logged in - untrusted users should not be added.
+      '';
+    };
+
+    package = mkOption {
+      type = package;
+      default = pkgs.weylus;
+      defaultText = "pkgs.weylus";
+      description = "Weylus package to install.";
+    };
+  };
+  config = mkIf cfg.enable {
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ 1701 9001 ];
+    };
+
+    hardware.uinput.enable = true;
+
+    users.groups.uinput.members = cfg.users;
+
+    environment.systemPackages = [ cfg.package ];
+  };
+}
diff --git a/nixos/modules/programs/wireshark.nix b/nixos/modules/programs/wireshark.nix
new file mode 100644
index 00000000000..f7b0727cb2b
--- /dev/null
+++ b/nixos/modules/programs/wireshark.nix
@@ -0,0 +1,42 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.wireshark;
+  wireshark = cfg.package;
+in {
+  options = {
+    programs.wireshark = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to add Wireshark to the global environment and configure a
+          setcap wrapper for 'dumpcap' for users in the 'wireshark' group.
+        '';
+      };
+      package = mkOption {
+        type = types.package;
+        default = pkgs.wireshark-cli;
+        defaultText = literalExpression "pkgs.wireshark-cli";
+        description = ''
+          Which Wireshark package to install in the global environment.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ wireshark ];
+    users.groups.wireshark = {};
+
+    security.wrappers.dumpcap = {
+      source = "${wireshark}/bin/dumpcap";
+      capabilities = "cap_net_raw+p";
+      owner = "root";
+      group = "wireshark";
+      permissions = "u+rx,g+x";
+    };
+  };
+}
diff --git a/nixos/modules/programs/wshowkeys.nix b/nixos/modules/programs/wshowkeys.nix
new file mode 100644
index 00000000000..f7b71d2bb0c
--- /dev/null
+++ b/nixos/modules/programs/wshowkeys.nix
@@ -0,0 +1,27 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.wshowkeys;
+in {
+  meta.maintainers = with maintainers; [ primeos ];
+
+  options = {
+    programs.wshowkeys = {
+      enable = mkEnableOption ''
+        wshowkeys (displays keypresses on screen on supported Wayland
+        compositors). It requires root permissions to read input events, but
+        these permissions are dropped after startup'';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    security.wrappers.wshowkeys =
+      { setuid = true;
+        owner = "root";
+        group = "root";
+        source = "${pkgs.wshowkeys}/bin/wshowkeys";
+      };
+  };
+}
diff --git a/nixos/modules/programs/xfs_quota.nix b/nixos/modules/programs/xfs_quota.nix
new file mode 100644
index 00000000000..c03e59a5b4a
--- /dev/null
+++ b/nixos/modules/programs/xfs_quota.nix
@@ -0,0 +1,110 @@
+# Configuration for the xfs_quota command
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.programs.xfs_quota;
+
+  limitOptions = opts: concatStringsSep " " [
+    (optionalString (opts.sizeSoftLimit != null) "bsoft=${opts.sizeSoftLimit}")
+    (optionalString (opts.sizeHardLimit != null) "bhard=${opts.sizeHardLimit}")
+  ];
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    programs.xfs_quota = {
+      projects = mkOption {
+        default = {};
+        type = types.attrsOf (types.submodule {
+          options = {
+            id = mkOption {
+              type = types.int;
+              description = "Project ID.";
+            };
+
+            fileSystem = mkOption {
+              type = types.str;
+              description = "XFS filesystem hosting the xfs_quota project.";
+              default = "/";
+            };
+
+            path = mkOption {
+              type = types.str;
+              description = "Project directory.";
+            };
+
+            sizeSoftLimit = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = "30g";
+              description = "Soft limit of the project size";
+            };
+
+            sizeHardLimit = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = "50g";
+              description = "Hard limit of the project size.";
+            };
+          };
+        });
+
+        description = "Setup of xfs_quota projects. Make sure the filesystem is mounted with the pquota option.";
+
+        example = {
+          projname = {
+            id = 50;
+            path = "/xfsprojects/projname";
+            sizeHardLimit = "50g";
+          };
+        };
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf (cfg.projects != {}) {
+
+    environment.etc.projects.source = pkgs.writeText "etc-project"
+      (concatStringsSep "\n" (mapAttrsToList
+        (name: opts: "${toString opts.id}:${opts.path}") cfg.projects));
+
+    environment.etc.projid.source = pkgs.writeText "etc-projid"
+      (concatStringsSep "\n" (mapAttrsToList
+        (name: opts: "${name}:${toString opts.id}") cfg.projects));
+
+    systemd.services = mapAttrs' (name: opts:
+      nameValuePair "xfs_quota-${name}" {
+        description = "Setup xfs_quota for project ${name}";
+        script = ''
+          ${pkgs.xfsprogs.bin}/bin/xfs_quota -x -c 'project -s ${name}' ${opts.fileSystem}
+          ${pkgs.xfsprogs.bin}/bin/xfs_quota -x -c 'limit -p ${limitOptions opts} ${name}' ${opts.fileSystem}
+        '';
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ ((replaceChars [ "/" ] [ "-" ] opts.fileSystem) + ".mount") ];
+
+        restartTriggers = [ config.environment.etc.projects.source ];
+
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+        };
+      }
+    ) cfg.projects;
+
+  };
+
+}
diff --git a/nixos/modules/programs/xonsh.nix b/nixos/modules/programs/xonsh.nix
new file mode 100644
index 00000000000..6e40db51cdb
--- /dev/null
+++ b/nixos/modules/programs/xonsh.nix
@@ -0,0 +1,86 @@
+# This module defines global configuration for the xonsh.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.programs.xonsh;
+
+in
+
+{
+
+  options = {
+
+    programs.xonsh = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to configure xonsh as an interactive shell.
+        '';
+        type = types.bool;
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.xonsh;
+        defaultText = literalExpression "pkgs.xonsh";
+        example = literalExpression "pkgs.xonsh.override { configFile = \"/path/to/xonshrc\"; }";
+        description = ''
+          xonsh package to use.
+        '';
+      };
+
+      config = mkOption {
+        default = "";
+        description = "Control file to customize your shell behavior.";
+        type = types.lines;
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.etc.xonshrc.text = ''
+      # /etc/xonshrc: DO NOT EDIT -- this file has been generated automatically.
+
+
+      if not ''${...}.get('__NIXOS_SET_ENVIRONMENT_DONE'):
+          # The NixOS environment and thereby also $PATH
+          # haven't been fully set up at this point. But
+          # `source-bash` below requires `bash` to be on $PATH,
+          # so add an entry with bash's location:
+          $PATH.add('${pkgs.bash}/bin')
+
+          # Stash xonsh's ls alias, so that we don't get a collision
+          # with Bash's ls alias from environment.shellAliases:
+          _ls_alias = aliases.pop('ls', None)
+
+          # Source the NixOS environment config.
+          source-bash "${config.system.build.setEnvironment}"
+
+          # Restore xonsh's ls alias, overriding that from Bash (if any).
+          if _ls_alias is not None:
+              aliases['ls'] = _ls_alias
+          del _ls_alias
+
+
+      ${cfg.config}
+    '';
+
+    environment.systemPackages = [ cfg.package ];
+
+    environment.shells =
+      [ "/run/current-system/sw/bin/xonsh"
+        "${cfg.package}/bin/xonsh"
+      ];
+
+  };
+
+}
+
diff --git a/nixos/modules/programs/xss-lock.nix b/nixos/modules/programs/xss-lock.nix
new file mode 100644
index 00000000000..aba76133e5e
--- /dev/null
+++ b/nixos/modules/programs/xss-lock.nix
@@ -0,0 +1,45 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.xss-lock;
+in
+{
+  options.programs.xss-lock = {
+    enable = mkEnableOption "xss-lock";
+
+    lockerCommand = mkOption {
+      default = "${pkgs.i3lock}/bin/i3lock";
+      defaultText = literalExpression ''"''${pkgs.i3lock}/bin/i3lock"'';
+      example = literalExpression ''"''${pkgs.i3lock-fancy}/bin/i3lock-fancy"'';
+      type = types.separatedString " ";
+      description = "Locker to be used with xsslock";
+    };
+
+    extraOptions = mkOption {
+      default = [ ];
+      example = [ "--ignore-sleep" ];
+      type = types.listOf types.str;
+      description = ''
+        Additional command-line arguments to pass to
+        <command>xss-lock</command>.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.user.services.xss-lock = {
+      description = "XSS Lock Daemon";
+      wantedBy = [ "graphical-session.target" ];
+      partOf = [ "graphical-session.target" ];
+      serviceConfig.ExecStart = with lib;
+        strings.concatStringsSep " " ([
+            "${pkgs.xss-lock}/bin/xss-lock" "--session \${XDG_SESSION_ID}"
+          ] ++ (map escapeShellArg cfg.extraOptions) ++ [
+            "--"
+            cfg.lockerCommand
+        ]);
+    };
+  };
+}
diff --git a/nixos/modules/programs/xwayland.nix b/nixos/modules/programs/xwayland.nix
new file mode 100644
index 00000000000..3a8080fa4c4
--- /dev/null
+++ b/nixos/modules/programs/xwayland.nix
@@ -0,0 +1,50 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.xwayland;
+
+in
+
+{
+  options.programs.xwayland = {
+
+    enable = mkEnableOption "Xwayland (an X server for interfacing X11 apps with the Wayland protocol)";
+
+    defaultFontPath = mkOption {
+      type = types.str;
+      default = optionalString config.fonts.fontDir.enable
+        "/run/current-system/sw/share/X11/fonts";
+      defaultText = literalExpression ''
+        optionalString config.fonts.fontDir.enable "/run/current-system/sw/share/X11/fonts"
+      '';
+      description = ''
+        Default font path. Setting this option causes Xwayland to be rebuilt.
+      '';
+    };
+
+    package = mkOption {
+      type = types.path;
+      default = pkgs.xwayland.override (oldArgs: {
+        inherit (cfg) defaultFontPath;
+      });
+      defaultText = literalExpression ''
+        pkgs.xwayland.override (oldArgs: {
+          inherit (config.programs.xwayland) defaultFontPath;
+        })
+      '';
+      description = "The Xwayland package to use.";
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    # Needed by some applications for fonts and default settings
+    environment.pathsToLink = [ "/share/X11" ];
+
+    environment.systemPackages = [ cfg.package ];
+
+  };
+}
diff --git a/nixos/modules/programs/yabar.nix b/nixos/modules/programs/yabar.nix
new file mode 100644
index 00000000000..a8fac41e899
--- /dev/null
+++ b/nixos/modules/programs/yabar.nix
@@ -0,0 +1,163 @@
+{ lib, pkgs, config, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.yabar;
+
+  mapExtra = v: lib.concatStringsSep "\n" (mapAttrsToList (
+    key: val: "${key} = ${if (isString val) then "\"${val}\"" else "${builtins.toString val}"};"
+  ) v);
+
+  listKeys = r: concatStringsSep "," (map (n: "\"${n}\"") (attrNames r));
+
+  configFile = let
+    bars = mapAttrsToList (
+      name: cfg: ''
+        ${name}: {
+          font: "${cfg.font}";
+          position: "${cfg.position}";
+
+          ${mapExtra cfg.extra}
+
+          block-list: [${listKeys cfg.indicators}]
+
+          ${concatStringsSep "\n" (mapAttrsToList (
+            name: cfg: ''
+              ${name}: {
+                exec: "${cfg.exec}";
+                align: "${cfg.align}";
+                ${mapExtra cfg.extra}
+              };
+            ''
+          ) cfg.indicators)}
+        };
+      ''
+    ) cfg.bars;
+  in pkgs.writeText "yabar.conf" ''
+    bar-list = [${listKeys cfg.bars}];
+    ${concatStringsSep "\n" bars}
+  '';
+in
+  {
+    options.programs.yabar = {
+      enable = mkEnableOption "yabar";
+
+      package = mkOption {
+        default = pkgs.yabar-unstable;
+        defaultText = literalExpression "pkgs.yabar-unstable";
+        example = literalExpression "pkgs.yabar";
+        type = types.package;
+
+        # `yabar-stable` segfaults under certain conditions.
+        apply = x: if x == pkgs.yabar-unstable then x else flip warn x ''
+          It's not recommended to use `yabar' with `programs.yabar', the (old) stable release
+          tends to segfault under certain circumstances:
+
+          * https://github.com/geommer/yabar/issues/86
+          * https://github.com/geommer/yabar/issues/68
+          * https://github.com/geommer/yabar/issues/143
+
+          Most of them don't occur on master anymore, until a new release is published, it's recommended
+          to use `yabar-unstable'.
+        '';
+
+        description = ''
+          The package which contains the `yabar` binary.
+
+          Nixpkgs provides the `yabar` and `yabar-unstable`
+          derivations since 18.03, so it's possible to choose.
+        '';
+      };
+
+      bars = mkOption {
+        default = {};
+        type = types.attrsOf(types.submodule {
+          options = {
+            font = mkOption {
+              default = "sans bold 9";
+              example = "Droid Sans, FontAwesome Bold 9";
+              type = types.str;
+
+              description = ''
+                The font that will be used to draw the status bar.
+              '';
+            };
+
+            position = mkOption {
+              default = "top";
+              example = "bottom";
+              type = types.enum [ "top" "bottom" ];
+
+              description = ''
+                The position where the bar will be rendered.
+              '';
+            };
+
+            extra = mkOption {
+              default = {};
+              type = types.attrsOf types.str;
+
+              description = ''
+                An attribute set which contains further attributes of a bar.
+              '';
+            };
+
+            indicators = mkOption {
+              default = {};
+              type = types.attrsOf(types.submodule {
+                options.exec = mkOption {
+                  example = "YABAR_DATE";
+                  type = types.str;
+                  description = ''
+                     The type of the indicator to be executed.
+                  '';
+                };
+
+                options.align = mkOption {
+                  default = "left";
+                  example = "right";
+                  type = types.enum [ "left" "center" "right" ];
+
+                  description = ''
+                    Whether to align the indicator at the left or right of the bar.
+                  '';
+                };
+
+                options.extra = mkOption {
+                  default = {};
+                  type = types.attrsOf (types.either types.str types.int);
+
+                  description = ''
+                    An attribute set which contains further attributes of a indicator.
+                  '';
+                };
+              });
+
+              description = ''
+                Indicators that should be rendered by yabar.
+              '';
+            };
+          };
+        });
+
+        description = ''
+          List of bars that should be rendered by yabar.
+        '';
+      };
+    };
+
+    config = mkIf cfg.enable {
+      systemd.user.services.yabar = {
+        description = "yabar service";
+        wantedBy = [ "graphical-session.target" ];
+        partOf = [ "graphical-session.target" ];
+
+        script = ''
+          ${cfg.package}/bin/yabar -c ${configFile}
+        '';
+
+        serviceConfig.Restart = "always";
+      };
+    };
+  }
diff --git a/nixos/modules/programs/zmap.nix b/nixos/modules/programs/zmap.nix
new file mode 100644
index 00000000000..2e27fce4d7c
--- /dev/null
+++ b/nixos/modules/programs/zmap.nix
@@ -0,0 +1,18 @@
+{ pkgs, config, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.zmap;
+in {
+  options.programs.zmap = {
+    enable = mkEnableOption "ZMap";
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.zmap ];
+
+    environment.etc."zmap/blacklist.conf".source = "${pkgs.zmap}/etc/zmap/blacklist.conf";
+    environment.etc."zmap/zmap.conf".source = "${pkgs.zmap}/etc/zmap.conf";
+  };
+}
diff --git a/nixos/modules/programs/zsh/oh-my-zsh.nix b/nixos/modules/programs/zsh/oh-my-zsh.nix
new file mode 100644
index 00000000000..9d7622bd328
--- /dev/null
+++ b/nixos/modules/programs/zsh/oh-my-zsh.nix
@@ -0,0 +1,146 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.programs.zsh.ohMyZsh;
+
+  mkLinkFarmEntry = name: dir:
+    let
+      env = pkgs.buildEnv {
+        name = "zsh-${name}-env";
+        paths = cfg.customPkgs;
+        pathsToLink = "/share/zsh/${dir}";
+      };
+    in
+      { inherit name; path = "${env}/share/zsh/${dir}"; };
+
+  mkLinkFarmEntry' = name: mkLinkFarmEntry name name;
+
+  custom =
+    if cfg.custom != null then cfg.custom
+    else if length cfg.customPkgs == 0 then null
+    else pkgs.linkFarm "oh-my-zsh-custom" [
+      (mkLinkFarmEntry' "themes")
+      (mkLinkFarmEntry "completions" "site-functions")
+      (mkLinkFarmEntry' "plugins")
+    ];
+
+in
+  {
+    imports = [
+      (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "enable" ] [ "programs" "zsh" "ohMyZsh" "enable" ])
+      (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "theme" ] [ "programs" "zsh" "ohMyZsh" "theme" ])
+      (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "custom" ] [ "programs" "zsh" "ohMyZsh" "custom" ])
+      (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "plugins" ] [ "programs" "zsh" "ohMyZsh" "plugins" ])
+    ];
+
+    options = {
+      programs.zsh.ohMyZsh = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enable oh-my-zsh.
+          '';
+        };
+
+        package = mkOption {
+          default = pkgs.oh-my-zsh;
+          defaultText = literalExpression "pkgs.oh-my-zsh";
+          description = ''
+            Package to install for `oh-my-zsh` usage.
+          '';
+
+          type = types.package;
+        };
+
+        plugins = mkOption {
+          default = [];
+          type = types.listOf(types.str);
+          description = ''
+            List of oh-my-zsh plugins
+          '';
+        };
+
+        custom = mkOption {
+          default = null;
+          type = with types; nullOr str;
+          description = ''
+            Path to a custom oh-my-zsh package to override config of oh-my-zsh.
+            (Can't be used along with `customPkgs`).
+          '';
+        };
+
+        customPkgs = mkOption {
+          default = [];
+          type = types.listOf types.package;
+          description = ''
+            List of custom packages that should be loaded into `oh-my-zsh`.
+          '';
+        };
+
+        theme = mkOption {
+          default = "";
+          type = types.str;
+          description = ''
+            Name of the theme to be used by oh-my-zsh.
+          '';
+        };
+
+        cacheDir = mkOption {
+          default = "$HOME/.cache/oh-my-zsh";
+          type = types.str;
+          description = ''
+            Cache directory to be used by `oh-my-zsh`.
+            Without this option it would default to the read-only nix store.
+          '';
+        };
+      };
+    };
+
+    config = mkIf cfg.enable {
+
+      # Prevent zsh from overwriting oh-my-zsh's prompt
+      programs.zsh.promptInit = mkDefault "";
+
+      environment.systemPackages = [ cfg.package ];
+
+      programs.zsh.interactiveShellInit = ''
+        # oh-my-zsh configuration generated by NixOS
+        export ZSH=${cfg.package}/share/oh-my-zsh
+
+        ${optionalString (length(cfg.plugins) > 0)
+          "plugins=(${concatStringsSep " " cfg.plugins})"
+        }
+
+        ${optionalString (custom != null)
+          "ZSH_CUSTOM=\"${custom}\""
+        }
+
+        ${optionalString (stringLength(cfg.theme) > 0)
+          "ZSH_THEME=\"${cfg.theme}\""
+        }
+
+        ${optionalString (cfg.cacheDir != null) ''
+          if [[ ! -d "${cfg.cacheDir}" ]]; then
+            mkdir -p "${cfg.cacheDir}"
+          fi
+          ZSH_CACHE_DIR=${cfg.cacheDir}
+        ''}
+
+        source $ZSH/oh-my-zsh.sh
+      '';
+
+      assertions = [
+        {
+          assertion = cfg.custom != null -> cfg.customPkgs == [];
+          message = "If `cfg.custom` is set for `ZSH_CUSTOM`, `customPkgs` can't be used!";
+        }
+      ];
+
+    };
+
+    meta.doc = ./oh-my-zsh.xml;
+  }
diff --git a/nixos/modules/programs/zsh/oh-my-zsh.xml b/nixos/modules/programs/zsh/oh-my-zsh.xml
new file mode 100644
index 00000000000..14a7228ad9b
--- /dev/null
+++ b/nixos/modules/programs/zsh/oh-my-zsh.xml
@@ -0,0 +1,155 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="module-programs-zsh-ohmyzsh">
+ <title>Oh my ZSH</title>
+ <para>
+  <literal><link xlink:href="https://ohmyz.sh/">oh-my-zsh</link></literal> is a
+  framework to manage your <link xlink:href="https://www.zsh.org/">ZSH</link>
+  configuration including completion scripts for several CLI tools or custom
+  prompt themes.
+ </para>
+ <section xml:id="module-programs-oh-my-zsh-usage">
+  <title>Basic usage</title>
+
+  <para>
+   The module uses the <literal>oh-my-zsh</literal> package with all available
+   features. The initial setup using Nix expressions is fairly similar to the
+   configuration format of <literal>oh-my-zsh</literal>.
+<programlisting>
+{
+  programs.zsh.ohMyZsh = {
+    enable = true;
+    plugins = [ "git" "python" "man" ];
+    theme = "agnoster";
+  };
+}
+</programlisting>
+   For a detailed explanation of these arguments please refer to the
+   <link xlink:href="https://github.com/robbyrussell/oh-my-zsh/wiki"><literal>oh-my-zsh</literal>
+   docs</link>.
+  </para>
+
+  <para>
+   The expression generates the needed configuration and writes it into your
+   <literal>/etc/zshrc</literal>.
+  </para>
+ </section>
+ <section xml:id="module-programs-oh-my-zsh-additions">
+  <title>Custom additions</title>
+
+  <para>
+   Sometimes third-party or custom scripts such as a modified theme may be
+   needed. <literal>oh-my-zsh</literal> provides the
+   <link xlink:href="https://github.com/robbyrussell/oh-my-zsh/wiki/Customization#overriding-internals"><literal>ZSH_CUSTOM</literal></link>
+   environment variable for this which points to a directory with additional
+   scripts.
+  </para>
+
+  <para>
+   The module can do this as well:
+<programlisting>
+{
+  programs.zsh.ohMyZsh.custom = "~/path/to/custom/scripts";
+}
+</programlisting>
+  </para>
+ </section>
+ <section xml:id="module-programs-oh-my-zsh-environments">
+  <title>Custom environments</title>
+
+  <para>
+   There are several extensions for <literal>oh-my-zsh</literal> packaged in
+   <literal>nixpkgs</literal>. One of them is
+   <link xlink:href="https://github.com/spwhitt/nix-zsh-completions">nix-zsh-completions</link>
+   which bundles completion scripts and a plugin for
+   <literal>oh-my-zsh</literal>.
+  </para>
+
+  <para>
+   Rather than using a single mutable path for <literal>ZSH_CUSTOM</literal>,
+   it's also possible to generate this path from a list of Nix packages:
+<programlisting>
+{ pkgs, ... }:
+{
+  programs.zsh.ohMyZsh.customPkgs = [
+    pkgs.nix-zsh-completions
+    # and even more...
+  ];
+}
+</programlisting>
+   Internally a single store path will be created using
+   <literal>buildEnv</literal>. Please refer to the docs of
+   <link xlink:href="https://nixos.org/nixpkgs/manual/#sec-building-environment"><literal>buildEnv</literal></link>
+   for further reference.
+  </para>
+
+  <para>
+   <emphasis>Please keep in mind that this is not compatible with
+   <literal>programs.zsh.ohMyZsh.custom</literal> as it requires an immutable
+   store path while <literal>custom</literal> shall remain mutable! An
+   evaluation failure will be thrown if both <literal>custom</literal> and
+   <literal>customPkgs</literal> are set.</emphasis>
+  </para>
+ </section>
+ <section xml:id="module-programs-oh-my-zsh-packaging-customizations">
+  <title>Package your own customizations</title>
+
+  <para>
+   If third-party customizations (e.g. new themes) are supposed to be added to
+   <literal>oh-my-zsh</literal> there are several pitfalls to keep in mind:
+  </para>
+
+  <itemizedlist>
+   <listitem>
+    <para>
+     To comply with the default structure of <literal>ZSH</literal> the entire
+     output needs to be written to <literal>$out/share/zsh.</literal>
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Completion scripts are supposed to be stored at
+     <literal>$out/share/zsh/site-functions</literal>. This directory is part
+     of the
+     <literal><link xlink:href="http://zsh.sourceforge.net/Doc/Release/Functions.html">fpath</link></literal>
+     and the package should be compatible with pure <literal>ZSH</literal>
+     setups. The module will automatically link the contents of
+     <literal>site-functions</literal> to completions directory in the proper
+     store path.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     The <literal>plugins</literal> directory needs the structure
+     <literal>pluginname/pluginname.plugin.zsh</literal> as structured in the
+     <link xlink:href="https://github.com/robbyrussell/oh-my-zsh/tree/91b771914bc7c43dd7c7a43b586c5de2c225ceb7/plugins">upstream
+     repo.</link>
+    </para>
+   </listitem>
+  </itemizedlist>
+
+  <para>
+   A derivation for <literal>oh-my-zsh</literal> may look like this:
+<programlisting>
+{ stdenv, fetchFromGitHub }:
+
+stdenv.mkDerivation rec {
+  name = "exemplary-zsh-customization-${version}";
+  version = "1.0.0";
+  src = fetchFromGitHub {
+    # path to the upstream repository
+  };
+
+  dontBuild = true;
+  installPhase = ''
+    mkdir -p $out/share/zsh/site-functions
+    cp {themes,plugins} $out/share/zsh
+    cp completions $out/share/zsh/site-functions
+  '';
+}
+</programlisting>
+  </para>
+ </section>
+</chapter>
diff --git a/nixos/modules/programs/zsh/zinputrc b/nixos/modules/programs/zsh/zinputrc
new file mode 100644
index 00000000000..6121f3e21f1
--- /dev/null
+++ b/nixos/modules/programs/zsh/zinputrc
@@ -0,0 +1,42 @@
+# Stolen from ArchWiki
+
+# create a zkbd compatible hash;
+# to add other keys to this hash, see: man 5 terminfo
+typeset -A key
+
+key[Home]=${terminfo[khome]}
+
+key[End]=${terminfo[kend]}
+key[Insert]=${terminfo[kich1]}
+key[Delete]=${terminfo[kdch1]}
+key[Up]=${terminfo[kcuu1]}
+key[Down]=${terminfo[kcud1]}
+key[Left]=${terminfo[kcub1]}
+key[Right]=${terminfo[kcuf1]}
+key[PageUp]=${terminfo[kpp]}
+key[PageDown]=${terminfo[knp]}
+
+# setup key accordingly
+[[ -n "${key[Home]}"     ]]  && bindkey  "${key[Home]}"     beginning-of-line
+[[ -n "${key[End]}"      ]]  && bindkey  "${key[End]}"      end-of-line
+[[ -n "${key[Insert]}"   ]]  && bindkey  "${key[Insert]}"   overwrite-mode
+[[ -n "${key[Delete]}"   ]]  && bindkey  "${key[Delete]}"   delete-char
+[[ -n "${key[Up]}"       ]]  && bindkey  "${key[Up]}"       up-line-or-history
+[[ -n "${key[Down]}"     ]]  && bindkey  "${key[Down]}"     down-line-or-history
+[[ -n "${key[Left]}"     ]]  && bindkey  "${key[Left]}"     backward-char
+[[ -n "${key[Right]}"    ]]  && bindkey  "${key[Right]}"    forward-char
+[[ -n "${key[PageUp]}"   ]]  && bindkey  "${key[PageUp]}"   beginning-of-buffer-or-history
+[[ -n "${key[PageDown]}" ]]  && bindkey  "${key[PageDown]}" end-of-buffer-or-history
+
+# Finally, make sure the terminal is in application mode, when zle is
+# active. Only then are the values from $terminfo valid.
+if (( ${+terminfo[smkx]} )) && (( ${+terminfo[rmkx]} )); then
+    function zle-line-init () {
+        printf '%s' "${terminfo[smkx]}"
+    }
+    function zle-line-finish () {
+        printf '%s' "${terminfo[rmkx]}"
+    }
+    zle -N zle-line-init
+    zle -N zle-line-finish
+fi
diff --git a/nixos/modules/programs/zsh/zsh-autoenv.nix b/nixos/modules/programs/zsh/zsh-autoenv.nix
new file mode 100644
index 00000000000..62f497a45dd
--- /dev/null
+++ b/nixos/modules/programs/zsh/zsh-autoenv.nix
@@ -0,0 +1,28 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.zsh.zsh-autoenv;
+in {
+  options = {
+    programs.zsh.zsh-autoenv = {
+      enable = mkEnableOption "zsh-autoenv";
+      package = mkOption {
+        default = pkgs.zsh-autoenv;
+        defaultText = literalExpression "pkgs.zsh-autoenv";
+        description = ''
+          Package to install for `zsh-autoenv` usage.
+        '';
+
+        type = types.package;
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    programs.zsh.interactiveShellInit = ''
+      source ${cfg.package}/share/zsh-autoenv/autoenv.zsh
+    '';
+  };
+}
diff --git a/nixos/modules/programs/zsh/zsh-autosuggestions.nix b/nixos/modules/programs/zsh/zsh-autosuggestions.nix
new file mode 100644
index 00000000000..2e53e907d54
--- /dev/null
+++ b/nixos/modules/programs/zsh/zsh-autosuggestions.nix
@@ -0,0 +1,73 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.zsh.autosuggestions;
+in
+{
+  imports = [
+    (mkRenamedOptionModule [ "programs" "zsh" "enableAutosuggestions" ] [ "programs" "zsh" "autosuggestions" "enable" ])
+  ];
+
+  options.programs.zsh.autosuggestions = {
+
+    enable = mkEnableOption "zsh-autosuggestions";
+
+    highlightStyle = mkOption {
+      type = types.str;
+      default = "fg=8"; # https://github.com/zsh-users/zsh-autosuggestions/tree/v0.4.3#suggestion-highlight-style
+      description = "Highlight style for suggestions ({fore,back}ground color)";
+      example = "fg=cyan";
+    };
+
+    strategy = mkOption {
+      type = types.listOf (types.enum [ "history" "completion" "match_prev_cmd" ]);
+      default = [ "history" ];
+      description = ''
+        `ZSH_AUTOSUGGEST_STRATEGY` is an array that specifies how suggestions should be generated.
+        The strategies in the array are tried successively until a suggestion is found.
+        There are currently three built-in strategies to choose from:
+
+        - `history`: Chooses the most recent match from history.
+        - `completion`: Chooses a suggestion based on what tab-completion would suggest. (requires `zpty` module)
+        - `match_prev_cmd`: Like `history`, but chooses the most recent match whose preceding history item matches
+            the most recently executed command. Note that this strategy won't work as expected with ZSH options that
+            don't preserve the history order such as `HIST_IGNORE_ALL_DUPS` or `HIST_EXPIRE_DUPS_FIRST`.
+      '';
+    };
+
+    async = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Whether to fetch suggestions asynchronously";
+      example = false;
+    };
+
+    extraConfig = mkOption {
+      type = with types; attrsOf str;
+      default = {};
+      description = "Attribute set with additional configuration values";
+      example = literalExpression ''
+        {
+          "ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" = "20";
+        }
+      '';
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    programs.zsh.interactiveShellInit = ''
+      source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh
+
+      export ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="${cfg.highlightStyle}"
+      export ZSH_AUTOSUGGEST_STRATEGY=(${concatStringsSep " " cfg.strategy})
+      ${optionalString (!cfg.async) "unset ZSH_AUTOSUGGEST_USE_ASYNC"}
+
+      ${concatStringsSep "\n" (mapAttrsToList (key: value: ''export ${key}="${value}"'') cfg.extraConfig)}
+    '';
+
+  };
+}
diff --git a/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix b/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix
new file mode 100644
index 00000000000..1eb53ccae52
--- /dev/null
+++ b/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix
@@ -0,0 +1,107 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.zsh.syntaxHighlighting;
+in
+{
+  imports = [
+    (mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ])
+    (mkRenamedOptionModule [ "programs" "zsh" "syntax-highlighting" "enable" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ])
+    (mkRenamedOptionModule [ "programs" "zsh" "syntax-highlighting" "highlighters" ] [ "programs" "zsh" "syntaxHighlighting" "highlighters" ])
+    (mkRenamedOptionModule [ "programs" "zsh" "syntax-highlighting" "patterns" ] [ "programs" "zsh" "syntaxHighlighting" "patterns" ])
+  ];
+
+  options = {
+    programs.zsh.syntaxHighlighting = {
+      enable = mkEnableOption "zsh-syntax-highlighting";
+
+      highlighters = mkOption {
+        default = [ "main" ];
+
+        # https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters.md
+        type = types.listOf(types.enum([
+          "main"
+          "brackets"
+          "pattern"
+          "cursor"
+          "root"
+          "line"
+        ]));
+
+        description = ''
+          Specifies the highlighters to be used by zsh-syntax-highlighting.
+
+          The following defined options can be found here:
+          https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters.md
+        '';
+      };
+
+      patterns = mkOption {
+        default = {};
+        type = types.attrsOf types.str;
+
+        example = literalExpression ''
+          {
+            "rm -rf *" = "fg=white,bold,bg=red";
+          }
+        '';
+
+        description = ''
+          Specifies custom patterns to be highlighted by zsh-syntax-highlighting.
+
+          Please refer to the docs for more information about the usage:
+          https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters/pattern.md
+        '';
+      };
+      styles = mkOption {
+        default = {};
+        type = types.attrsOf types.str;
+
+        example = literalExpression ''
+          {
+            "alias" = "fg=magenta,bold";
+          }
+        '';
+
+        description = ''
+          Specifies custom styles to be highlighted by zsh-syntax-highlighting.
+
+          Please refer to the docs for more information about the usage:
+          https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters/main.md
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = with pkgs; [ zsh-syntax-highlighting ];
+
+    assertions = [
+      {
+        assertion = length(attrNames cfg.patterns) > 0 -> elem "pattern" cfg.highlighters;
+        message = ''
+          When highlighting patterns, "pattern" needs to be included in the list of highlighters.
+        '';
+      }
+    ];
+
+    programs.zsh.interactiveShellInit = with pkgs;
+      lib.mkAfter (lib.concatStringsSep "\n" ([
+        "source ${zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh"
+      ] ++ optional (length(cfg.highlighters) > 0)
+        "ZSH_HIGHLIGHT_HIGHLIGHTERS=(${concatStringsSep " " cfg.highlighters})"
+        ++ optionals (length(attrNames cfg.patterns) > 0)
+          (mapAttrsToList (
+            pattern: design:
+            "ZSH_HIGHLIGHT_PATTERNS+=('${pattern}' '${design}')"
+          ) cfg.patterns)
+        ++ optionals (length(attrNames cfg.styles) > 0)
+          (mapAttrsToList (
+            styles: design:
+            "ZSH_HIGHLIGHT_STYLES[${styles}]='${design}'"
+          ) cfg.styles)
+      ));
+  };
+}
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
new file mode 100644
index 00000000000..5fe98b6801b
--- /dev/null
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -0,0 +1,303 @@
+# This module defines global configuration for the zshell.
+
+{ config, lib, options, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfge = config.environment;
+
+  cfg = config.programs.zsh;
+  opt = options.programs.zsh;
+
+  zshAliases = concatStringsSep "\n" (
+    mapAttrsFlatten (k: v: "alias ${k}=${escapeShellArg v}")
+      (filterAttrs (k: v: v != null) cfg.shellAliases)
+  );
+
+  zshStartupNotes = ''
+    # Note that generated /etc/zprofile and /etc/zshrc files do a lot of
+    # non-standard setup to make zsh usable with no configuration by default.
+    #
+    # Which means that unless you explicitly meticulously override everything
+    # generated, interactions between your ~/.zshrc and these files are likely
+    # to be rather surprising.
+    #
+    # Note however, that you can disable loading of the generated /etc/zprofile
+    # and /etc/zshrc (you can't disable loading of /etc/zshenv, but it is
+    # designed to not set anything surprising) by setting `no_global_rcs` option
+    # in ~/.zshenv:
+    #
+    #   echo setopt no_global_rcs >> ~/.zshenv
+    #
+    # See "STARTUP/SHUTDOWN FILES" section of zsh(1) for more info.
+  '';
+
+in
+
+{
+
+  options = {
+
+    programs.zsh = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to configure zsh as an interactive shell. To enable zsh for
+          a particular user, use the <option>users.users.&lt;name?&gt;.shell</option>
+          option for that user. To enable zsh system-wide use the
+          <option>users.defaultUserShell</option> option.
+        '';
+        type = types.bool;
+      };
+
+      shellAliases = mkOption {
+        default = { };
+        description = ''
+          Set of aliases for zsh shell, which overrides <option>environment.shellAliases</option>.
+          See <option>environment.shellAliases</option> for an option format description.
+        '';
+        type = with types; attrsOf (nullOr (either str path));
+      };
+
+      shellInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code called during zsh shell initialisation.
+        '';
+        type = types.lines;
+      };
+
+      loginShellInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code called during zsh login shell initialisation.
+        '';
+        type = types.lines;
+      };
+
+      interactiveShellInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code called during interactive zsh shell initialisation.
+        '';
+        type = types.lines;
+      };
+
+      promptInit = mkOption {
+        default = ''
+          # Note that to manually override this in ~/.zshrc you should run `prompt off`
+          # before setting your PS1 and etc. Otherwise this will likely to interact with
+          # your ~/.zshrc configuration in unexpected ways as the default prompt sets
+          # a lot of different prompt variables.
+          autoload -U promptinit && promptinit && prompt suse && setopt prompt_sp
+        '';
+        description = ''
+          Shell script code used to initialise the zsh prompt.
+        '';
+        type = types.lines;
+      };
+
+      histSize = mkOption {
+        default = 2000;
+        description = ''
+          Change history size.
+        '';
+        type = types.int;
+      };
+
+      histFile = mkOption {
+        default = "$HOME/.zsh_history";
+        description = ''
+          Change history file.
+        '';
+        type = types.str;
+      };
+
+      setOptions = mkOption {
+        type = types.listOf types.str;
+        default = [
+          "HIST_IGNORE_DUPS"
+          "SHARE_HISTORY"
+          "HIST_FCNTL_LOCK"
+        ];
+        example = [ "EXTENDED_HISTORY" "RM_STAR_WAIT" ];
+        description = ''
+          Configure zsh options. See
+          <citerefentry><refentrytitle>zshoptions</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+        '';
+      };
+
+      enableCompletion = mkOption {
+        default = true;
+        description = ''
+          Enable zsh completion for all interactive zsh shells.
+        '';
+        type = types.bool;
+      };
+
+      enableBashCompletion = mkOption {
+        default = false;
+        description = ''
+          Enable compatibility with bash's programmable completion system.
+        '';
+        type = types.bool;
+      };
+
+      enableGlobalCompInit = mkOption {
+        default = cfg.enableCompletion;
+        defaultText = literalExpression "config.${opt.enableCompletion}";
+        description = ''
+          Enable execution of compinit call for all interactive zsh shells.
+
+          This option can be disabled if the user wants to extend its
+          <literal>fpath</literal> and a custom <literal>compinit</literal>
+          call in the local config is required.
+        '';
+        type = types.bool;
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    programs.zsh.shellAliases = mapAttrs (name: mkDefault) cfge.shellAliases;
+
+    environment.etc.zshenv.text =
+      ''
+        # /etc/zshenv: DO NOT EDIT -- this file has been generated automatically.
+        # This file is read for all shells.
+
+        # Only execute this file once per shell.
+        if [ -n "$__ETC_ZSHENV_SOURCED" ]; then return; fi
+        __ETC_ZSHENV_SOURCED=1
+
+        if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]; then
+            . ${config.system.build.setEnvironment}
+        fi
+
+        HELPDIR="${pkgs.zsh}/share/zsh/$ZSH_VERSION/help"
+
+        # Tell zsh how to find installed completions.
+        for p in ''${(z)NIX_PROFILES}; do
+            fpath+=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions)
+        done
+
+        # Setup custom shell init stuff.
+        ${cfge.shellInit}
+
+        ${cfg.shellInit}
+
+        # Read system-wide modifications.
+        if test -f /etc/zshenv.local; then
+            . /etc/zshenv.local
+        fi
+      '';
+
+    environment.etc.zprofile.text =
+      ''
+        # /etc/zprofile: DO NOT EDIT -- this file has been generated automatically.
+        # This file is read for login shells.
+        #
+        ${zshStartupNotes}
+
+        # Only execute this file once per shell.
+        if [ -n "$__ETC_ZPROFILE_SOURCED" ]; then return; fi
+        __ETC_ZPROFILE_SOURCED=1
+
+        # Setup custom login shell init stuff.
+        ${cfge.loginShellInit}
+
+        ${cfg.loginShellInit}
+
+        # Read system-wide modifications.
+        if test -f /etc/zprofile.local; then
+            . /etc/zprofile.local
+        fi
+      '';
+
+    environment.etc.zshrc.text =
+      ''
+        # /etc/zshrc: DO NOT EDIT -- this file has been generated automatically.
+        # This file is read for interactive shells.
+        #
+        ${zshStartupNotes}
+
+        # Only execute this file once per shell.
+        if [ -n "$__ETC_ZSHRC_SOURCED" -o -n "$NOSYSZSHRC" ]; then return; fi
+        __ETC_ZSHRC_SOURCED=1
+
+        ${optionalString (cfg.setOptions != []) ''
+          # Set zsh options.
+          setopt ${concatStringsSep " " cfg.setOptions}
+        ''}
+
+        # Setup command line history.
+        # Don't export these, otherwise other shells (bash) will try to use same HISTFILE.
+        SAVEHIST=${toString cfg.histSize}
+        HISTSIZE=${toString cfg.histSize}
+        HISTFILE=${cfg.histFile}
+
+        # Configure sane keyboard defaults.
+        . /etc/zinputrc
+
+        ${optionalString cfg.enableGlobalCompInit ''
+          # Enable autocompletion.
+          autoload -U compinit && compinit
+        ''}
+
+        ${optionalString cfg.enableBashCompletion ''
+          # Enable compatibility with bash's completion system.
+          autoload -U bashcompinit && bashcompinit
+        ''}
+
+        # Setup custom interactive shell init stuff.
+        ${cfge.interactiveShellInit}
+
+        ${cfg.interactiveShellInit}
+
+        # Setup aliases.
+        ${zshAliases}
+
+        # Setup prompt.
+        ${cfg.promptInit}
+
+        # Disable some features to support TRAMP.
+        if [ "$TERM" = dumb ]; then
+            unsetopt zle prompt_cr prompt_subst
+            unset RPS1 RPROMPT
+            PS1='$ '
+            PROMPT='$ '
+        fi
+
+        # Read system-wide modifications.
+        if test -f /etc/zshrc.local; then
+            . /etc/zshrc.local
+        fi
+      '';
+
+    # Bug in nix flakes:
+    # If we use `.source` here the path is garbage collected also we point to it with a symlink
+    # see https://github.com/NixOS/nixpkgs/issues/132732
+    environment.etc.zinputrc.text = builtins.readFile ./zinputrc;
+
+    environment.systemPackages = [ pkgs.zsh ]
+      ++ optional cfg.enableCompletion pkgs.nix-zsh-completions;
+
+    environment.pathsToLink = optional cfg.enableCompletion "/share/zsh";
+
+    #users.defaultUserShell = mkDefault "/run/current-system/sw/bin/zsh";
+
+    environment.shells =
+      [
+        "/run/current-system/sw/bin/zsh"
+        "${pkgs.zsh}/bin/zsh"
+      ];
+
+  };
+
+}