about summary refs log tree commit diff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/default.nix1
-rw-r--r--modules/home/default.nix89
-rw-r--r--modules/nix/default.nix31
-rw-r--r--modules/nixos-hardware/.gitignore9
-rw-r--r--modules/nixos-hardware/.travis.yml3
-rw-r--r--modules/nixos-hardware/CODEOWNERS7
-rw-r--r--modules/nixos-hardware/CONTRIBUTING.md31
-rw-r--r--modules/nixos-hardware/COPYING111
-rw-r--r--modules/nixos-hardware/README.md110
-rw-r--r--modules/nixos-hardware/acer/aspire/4810t/README.wiki110
-rw-r--r--modules/nixos-hardware/acer/aspire/4810t/default.nix29
-rw-r--r--modules/nixos-hardware/airis/n990/default.nix21
-rw-r--r--modules/nixos-hardware/apple/default.nix12
-rw-r--r--modules/nixos-hardware/apple/macbook-air/3/default.nix15
-rw-r--r--modules/nixos-hardware/apple/macbook-air/4/default.nix16
-rw-r--r--modules/nixos-hardware/apple/macbook-air/6/default.nix18
-rw-r--r--modules/nixos-hardware/apple/macbook-air/default.nix8
-rw-r--r--modules/nixos-hardware/apple/macbook-pro/10-1/README.wiki9
-rw-r--r--modules/nixos-hardware/apple/macbook-pro/10-1/default.nix21
-rw-r--r--modules/nixos-hardware/apple/macbook-pro/11-5/README.md32
-rw-r--r--modules/nixos-hardware/apple/macbook-pro/11-5/default.nix27
-rw-r--r--modules/nixos-hardware/apple/macbook-pro/12-1/default.nix14
-rw-r--r--modules/nixos-hardware/apple/macbook-pro/default.nix7
-rw-r--r--modules/nixos-hardware/asus/wireless-ac1300.nix6
-rw-r--r--modules/nixos-hardware/audio-gd/compass2.nix7
-rw-r--r--modules/nixos-hardware/beagleboard/pocketbeagle/default.nix10
-rw-r--r--modules/nixos-hardware/common/cpu/amd/default.nix6
-rw-r--r--modules/nixos-hardware/common/cpu/intel/default.nix15
-rw-r--r--modules/nixos-hardware/common/cpu/intel/kaby-lake/default.nix8
-rw-r--r--modules/nixos-hardware/common/cpu/intel/sandy-bridge/default.nix9
-rw-r--r--modules/nixos-hardware/common/pc/default.nix5
-rw-r--r--modules/nixos-hardware/common/pc/hdd/default.nix7
-rw-r--r--modules/nixos-hardware/common/pc/laptop/acpi_call.nix10
-rw-r--r--modules/nixos-hardware/common/pc/laptop/default.nix13
-rw-r--r--modules/nixos-hardware/common/pc/laptop/hdd/default.nix8
l---------modules/nixos-hardware/common/pc/laptop/ssd1
-rw-r--r--modules/nixos-hardware/common/pc/ssd/default.nix9
-rw-r--r--modules/nixos-hardware/default.nix3
-rw-r--r--modules/nixos-hardware/dell/e7240/README.md8
-rw-r--r--modules/nixos-hardware/dell/e7240/default.nix8
-rw-r--r--modules/nixos-hardware/dell/latitude/3480/default.nix14
-rw-r--r--modules/nixos-hardware/dell/xps/13-7390/README.wiki16
-rw-r--r--modules/nixos-hardware/dell/xps/13-7390/default.nix13
-rw-r--r--modules/nixos-hardware/dell/xps/13-9360/default.nix29
-rw-r--r--modules/nixos-hardware/dell/xps/13-9360/qca6174-firmware.nix26
-rw-r--r--modules/nixos-hardware/dell/xps/13-9370/README.wiki16
-rw-r--r--modules/nixos-hardware/dell/xps/13-9370/default.nix20
-rw-r--r--modules/nixos-hardware/dell/xps/13-9380/README.wiki20
-rw-r--r--modules/nixos-hardware/dell/xps/13-9380/default.nix18
-rw-r--r--modules/nixos-hardware/dell/xps/15-7590/README.wiki54
-rw-r--r--modules/nixos-hardware/dell/xps/15-7590/default.nix51
-rw-r--r--modules/nixos-hardware/dell/xps/15-9550/README.wiki52
-rw-r--r--modules/nixos-hardware/dell/xps/15-9550/default.nix18
-rw-r--r--modules/nixos-hardware/dell/xps/15-9560/README.wiki54
-rw-r--r--modules/nixos-hardware/dell/xps/15-9560/default.nix24
-rw-r--r--modules/nixos-hardware/dell/xps/15-9560/intel/default.nix18
-rw-r--r--modules/nixos-hardware/dell/xps/15-9560/nvidia/default.nix20
-rw-r--r--modules/nixos-hardware/dell/xps/15-9560/xps-common.nix12
-rw-r--r--modules/nixos-hardware/inversepath/usbarmory/README.txt6
-rw-r--r--modules/nixos-hardware/inversepath/usbarmory/default.nix46
-rw-r--r--modules/nixos-hardware/inversepath/usbarmory/host.nix19
-rw-r--r--modules/nixos-hardware/inversepath/usbarmory/kernel.config995
-rw-r--r--modules/nixos-hardware/inversepath/usbarmory/kernel.nix20
-rw-r--r--modules/nixos-hardware/inversepath/usbarmory/usbarmory-dts.patch418
-rw-r--r--modules/nixos-hardware/lenovo/ideapad/default.nix3
-rw-r--r--modules/nixos-hardware/lenovo/ideapad/z510/default.nix11
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/default.nix11
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/e495/default.nix11
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/l13/default.nix10
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/p53/default.nix10
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/t410/default.nix36
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/t420/default.nix9
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/t430/default.nix16
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/t440p/default.nix16
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/t440s/default.nix14
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/t450s/default.nix9
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/t460s/default.nix9
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/t470s/default.nix9
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/t480s/default.nix11
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/t490/default.nix31
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/t490/t490-profile-set.conf107
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/t495/default.nix16
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/tp-smapi.nix11
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/x1-extreme/default.nix8
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/x1-extreme/gen2/default.nix48
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/x1/6th-gen/QHD/default.nix36
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/x1/6th-gen/default.nix20
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/x1/default.nix6
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/x140e/default.nix14
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/x220/default.nix10
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/x230/default.nix19
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/x250/default.nix7
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/x260/default.nix12
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/x270/default.nix6
-rw-r--r--modules/nixos-hardware/lenovo/thinkpad/x280/default.nix12
-rw-r--r--modules/nixos-hardware/microsoft/hyper-v/README.md63
-rw-r--r--modules/nixos-hardware/microsoft/surface-pro/3/README.wiki41
-rw-r--r--modules/nixos-hardware/microsoft/surface-pro/3/default.nix9
-rw-r--r--modules/nixos-hardware/pcengines/apu/default.nix8
-rw-r--r--modules/nixos-hardware/purism/librem/13v3/README.md87
-rw-r--r--modules/nixos-hardware/purism/librem/13v3/default.nix13
l---------modules/nixos-hardware/purism/librem/15v31
-rw-r--r--modules/nixos-hardware/raspberry-pi/2/README.org23
-rw-r--r--modules/nixos-hardware/raspberry-pi/2/default.nix32
-rw-r--r--modules/nixos-hardware/samsung/np900x3c/README.wiki94
-rw-r--r--modules/nixos-hardware/samsung/np900x3c/default.nix7
-rw-r--r--modules/nixos-hardware/supermicro/a1sri-2758f/default.nix9
-rw-r--r--modules/nixos-hardware/supermicro/x10sll-f/default.nix12
-rw-r--r--modules/nixos-hardware/tests/build-profile.nix19
-rwxr-xr-xmodules/nixos-hardware/tests/run.py102
-rw-r--r--modules/nixos-hardware/toshiba/swanky/README.md66
-rw-r--r--modules/nixos-hardware/toshiba/swanky/default.nix27
-rw-r--r--modules/nixos-hardware/tuxedo/infinitybook/v4/default.nix5
-rw-r--r--modules/server/default.nix14
-rw-r--r--modules/server/dns/default.nix13
-rw-r--r--modules/server/ftp/default.nix47
-rw-r--r--modules/server/irc/bitlbee/default.nix6
-rw-r--r--modules/server/irc/default.nix5
-rw-r--r--modules/server/irc/znc/default.nix7
-rw-r--r--modules/server/nginx/default.nix21
-rw-r--r--modules/server/tor/default.nix23
-rw-r--r--modules/server/xmpp/default.nix23
-rw-r--r--modules/shell/cargo/default.nix9
-rw-r--r--modules/shell/default.nix40
-rw-r--r--modules/shell/direnv/default.nix7
-rw-r--r--modules/shell/git/attributes2
-rw-r--r--modules/shell/git/config.in50
-rw-r--r--modules/shell/git/default.nix32
-rw-r--r--modules/shell/git/diff/mozlz4.in4
-rw-r--r--modules/shell/git/ignore27
-rw-r--r--modules/shell/kakoune/default.nix7
-rw-r--r--modules/shell/kakoune/kakrc86
-rw-r--r--modules/shell/less/default.nix7
-rw-r--r--modules/shell/lynx/default.nix16
-rw-r--r--modules/shell/lynx/lynx.cfg17
-rw-r--r--modules/shell/lynx/lynx.lss105
-rw-r--r--modules/shell/pass/default.nix11
-rw-r--r--modules/shell/tmux/config.nix90
-rw-r--r--modules/shell/tmux/default.nix8
-rw-r--r--modules/shell/zsh/default.nix24
-rw-r--r--modules/shell/zsh/zshrc.nix206
-rw-r--r--modules/ssh/default.nix62
-rw-r--r--modules/ssh/keys/edef.keys1
-rw-r--r--modules/ssh/keys/github.keys1
-rw-r--r--modules/ssh/keys/gitlab.keys2
-rw-r--r--modules/ssh/keys/qyliss.keys4
-rw-r--r--modules/users/default.nix32
-rw-r--r--modules/workstation/default.nix16
-rw-r--r--modules/workstation/dict/default.nix6
-rw-r--r--modules/workstation/dino/default.nix7
-rw-r--r--modules/workstation/documentation/default.nix6
-rw-r--r--modules/workstation/emacs/default.el0
-rw-r--r--modules/workstation/emacs/default.nix14
-rw-r--r--modules/workstation/fonts/default.nix7
-rw-r--r--modules/workstation/gnupg/default.nix30
-rw-r--r--modules/workstation/gnupg/dirmngr.conf1
-rw-r--r--modules/workstation/gnupg/gpg.conf3
-rw-r--r--modules/workstation/hardware/default.nix9
-rw-r--r--modules/workstation/hardware/keyboard/MAPPINGS7
-rw-r--r--modules/workstation/hardware/keyboard/default.nix22
-rw-r--r--modules/workstation/hardware/keyboard/events.dyon14
-rw-r--r--modules/workstation/hardware/yubikey/default.nix13
-rw-r--r--modules/workstation/hardware/yubikey/u2f_keys1
-rw-r--r--modules/workstation/locale/default.nix6
-rw-r--r--modules/workstation/lorri/default.nix10
-rw-r--r--modules/workstation/mail/default.nix47
-rw-r--r--modules/workstation/mail/mbsyncrc.in22
-rw-r--r--modules/workstation/mail/mutt/default.nix9
-rw-r--r--modules/workstation/mail/mutt/muttrc56
-rw-r--r--modules/workstation/mail/notmuch/config15
-rw-r--r--modules/workstation/mail/notmuch/default.nix12
-rw-r--r--modules/workstation/mail/postfix/default.nix32
-rw-r--r--modules/workstation/mail/rss2email/default.nix16
-rw-r--r--modules/workstation/networking/default.nix46
-rw-r--r--modules/workstation/physical/default.nix7
-rw-r--r--modules/workstation/podman/default.nix47
-rw-r--r--modules/workstation/weechat/default.nix121
-rw-r--r--modules/workstation/windowing/alacritty/config.yml340
-rw-r--r--modules/workstation/windowing/alacritty/default.nix11
-rw-r--r--modules/workstation/windowing/default.nix7
-rw-r--r--modules/workstation/windowing/firefox/default.nix13
-rw-r--r--modules/workstation/windowing/firefox/profiles.ini8
-rw-r--r--modules/workstation/windowing/firefox/user.js1
-rw-r--r--modules/workstation/windowing/gnome-mines/default.nix7
-rw-r--r--modules/workstation/windowing/sway/choose_workspace.sh.in17
-rw-r--r--modules/workstation/windowing/sway/config.in110
-rw-r--r--modules/workstation/windowing/sway/default.nix52
-rw-r--r--modules/workstation/windowing/sway/status.cpp159
-rw-r--r--modules/workstation/windowing/sway/swayidle/default.nix23
-rw-r--r--modules/xdg/default.nix44
190 files changed, 6412 insertions, 0 deletions
diff --git a/modules/default.nix b/modules/default.nix
new file mode 100644
index 000000000000..eae333ea34b2
--- /dev/null
+++ b/modules/default.nix
@@ -0,0 +1 @@
+import ../nixpkgs/nixos
diff --git a/modules/home/default.nix b/modules/home/default.nix
new file mode 100644
index 000000000000..780bac85b334
--- /dev/null
+++ b/modules/home/default.nix
@@ -0,0 +1,89 @@
+{ lib, config, ... }:
+
+let
+  inherit (lib) attrValues concatStringsSep mapAttrsToList mkOption
+                optionalString recursiveUpdate;
+  inherit (lib.types) bool loaOf nullOr str submodule;
+
+  dirOpts = { ... }: {
+    options = {
+      owner = mkOption {
+        default = null;
+        type = nullOr str;
+      };
+      group = mkOption {
+        default = null;
+        type = nullOr str;
+      };
+      permissions = mkOption {
+        default = "0700";
+        type = str;
+      };
+      activationScripts = mkOption {
+        default = {};
+        type = loaOf str;
+      };
+    };
+  };
+
+  applyDirConfig = user: dir:
+    let
+      owner = if dir.owner == null
+              then user
+              else config.users.users.${dir.owner};
+
+      group = if dir.group == null then owner.group else dir.group;
+    in
+      ''
+        chmod ${dir.permissions} .
+        chown ${owner.name}:${group} .
+        ${concatStringsSep "\n" (attrValues dir.activationScripts)}
+      '';
+
+in
+  {
+    options = {
+      home = mkOption {
+        default = {};
+        type = loaOf (submodule (args: recursiveUpdate (dirOpts args) {
+          options = {
+            imperativeNix = mkOption {
+              default = false;
+              type = bool;
+            };
+            dirs = mkOption {
+              default = {};
+              type = loaOf (submodule dirOpts);
+            };
+          };
+        }));
+      };
+    };
+
+    config = {
+      system.activationScripts.home = {
+        deps = [];
+        text = concatStringsSep "\n" (mapAttrsToList
+          (key: home:
+            let
+              user = config.users.users.${key};
+
+            in ''
+              ${optionalString (!home.imperativeNix) ''
+                rm -rf ${user.home}/.nix-{defexpr,profile}
+              ''}
+
+              pushd ${user.home} >/dev/null
+              ${applyDirConfig user home}
+              ${concatStringsSep "\n" (mapAttrsToList (name: dir: ''
+                mkdir -p ${name}
+                pushd ${name} >/dev/null
+                ${applyDirConfig user dir}
+                popd >/dev/null
+              '') home.dirs)}
+              popd >/dev/null
+            ''
+        ) config.home);
+      };
+    };
+  }
diff --git a/modules/nix/default.nix b/modules/nix/default.nix
new file mode 100644
index 000000000000..f2d9ea5dc183
--- /dev/null
+++ b/modules/nix/default.nix
@@ -0,0 +1,31 @@
+{ config, pkgs, options, lib, ... }:
+
+let
+  # Most of the standard Darwin-detection methods cause infinite recursion.
+  isDarwin = options.environment ? "darwinConfig";
+
+in {
+  nix.nixPath = [
+    "nixos-config=/run/current-system/nixlib/sys/${config.networking.hostName}.nix"
+    "/run/current-system/nixlib"
+  ];
+
+  nix.autoOptimiseStore = true;
+  nix.daemonNiceLevel = 2;
+  nix.trustedUsers = [ "@wheel" ];
+
+  system.extraSystemBuilderCmds = ''
+    ln -s ${lib.cleanSource ../..} $out/nixlib
+  '';
+
+  nixpkgs.overlays =
+    let
+      inherit (builtins) attrNames readDir;
+      dir = ../../nixpkgs-overlays;
+      names = attrNames (readDir dir);
+    in
+      map (o: import "${dir}/${o}") names;
+
+  services = lib.optionalAttrs isDarwin
+    { nix-daemon.enable = true; };
+}
diff --git a/modules/nixos-hardware/.gitignore b/modules/nixos-hardware/.gitignore
new file mode 100644
index 000000000000..2151b35ff51e
--- /dev/null
+++ b/modules/nixos-hardware/.gitignore
@@ -0,0 +1,9 @@
+*~
+,*
+.*.swp
+.*.swo
+result
+result-*
+.version-suffix
+
+.DS_Store
diff --git a/modules/nixos-hardware/.travis.yml b/modules/nixos-hardware/.travis.yml
new file mode 100644
index 000000000000..91cf21da023e
--- /dev/null
+++ b/modules/nixos-hardware/.travis.yml
@@ -0,0 +1,3 @@
+language: nix
+script:
+  ./tests/run.py
diff --git a/modules/nixos-hardware/CODEOWNERS b/modules/nixos-hardware/CODEOWNERS
new file mode 100644
index 000000000000..4f2bb2cc6463
--- /dev/null
+++ b/modules/nixos-hardware/CODEOWNERS
@@ -0,0 +1,7 @@
+beagleboard/pocketbeagle @yegortimoshenko
+dell/xps/13-9370 @moredread
+dell/xps/13-9380 @kalbasit
+lenovo/thinkpad/x230 @makefu @yegortimoshenko
+lenovo/thinkpad/x250 @Mic92
+pcengines/apu @yegortimoshenko
+purism/librem/13v3 @yegortimoshenko
diff --git a/modules/nixos-hardware/CONTRIBUTING.md b/modules/nixos-hardware/CONTRIBUTING.md
new file mode 100644
index 000000000000..ea468923ae10
--- /dev/null
+++ b/modules/nixos-hardware/CONTRIBUTING.md
@@ -0,0 +1,31 @@
+# Writing profiles
+
+When setting an option, use `lib.mkDefault` unless:
+- The option *must* be set and the user should get an error if they try to override it.
+- The setting should merge with the user's settings (typical for list or set options).
+
+For example:
+
+```nix
+{ lib }: {
+  # Using mkDefault, because the user might want to disable tlp
+  services.tlp.enable = lib.mkDefault true;
+  # No need to use mkDefault, because the setting will merge with the user's setting
+  boot.kernelModules = [ "tmp_smapi" ];
+}
+```
+
+Try to avoid "opinionated" settings relating to optional features like sound, bluetooth, choice of bootloader etc.
+
+Where possible, use module imports to share code between similar hardware variants.
+
+# Performance
+
+Profiles should favor usability and stability, so performance improvements should either be conservative or 
+be guarded behind additional NixOS module options.
+
+If it makes sense to have a performance-focussed config, it can be declared in a separate profile.
+
+# Testing
+
+Because profiles can only be tested with the appropriate hardware, quality assurance is up to *you*.
diff --git a/modules/nixos-hardware/COPYING b/modules/nixos-hardware/COPYING
new file mode 100644
index 000000000000..47c6e27d1700
--- /dev/null
+++ b/modules/nixos-hardware/COPYING
@@ -0,0 +1,111 @@
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator and
+subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for the
+purpose of contributing to a commons of creative, cultural and scientific
+works ("Commons") that the public can reliably and without fear of later
+claims of infringement build upon, modify, incorporate in other works, reuse
+and redistribute as freely as possible in any form whatsoever and for any
+purposes, including without limitation commercial purposes. These owners may
+contribute to the Commons to promote the ideal of a free culture and the
+further production of creative, cultural and scientific works, or to gain
+reputation or greater distribution for their Work in part through the use and
+efforts of others.
+
+For these and/or other purposes and motivations, and without any expectation
+of additional consideration or compensation, the person associating CC0 with a
+Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
+and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
+and publicly distribute the Work under its terms, with knowledge of his or her
+Copyright and Related Rights in the Work and the meaning and intended legal
+effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not limited
+to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display, communicate,
+  and translate a Work;
+
+  ii. moral rights retained by the original author(s) and/or performer(s);
+
+  iii. publicity and privacy rights pertaining to a person's image or likeness
+  depicted in a Work;
+
+  iv. rights protecting against unfair competition in regards to a Work,
+  subject to the limitations in paragraph 4(a), below;
+
+  v. rights protecting the extraction, dissemination, use and reuse of data in
+  a Work;
+
+  vi. database rights (such as those arising under Directive 96/9/EC of the
+  European Parliament and of the Council of 11 March 1996 on the legal
+  protection of databases, and under any national implementation thereof,
+  including any amended or successor version of such directive); and
+
+  vii. other similar, equivalent or corresponding rights throughout the world
+  based on applicable law or treaty, and any national implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention of,
+applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
+unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
+and Related Rights and associated claims and causes of action, whether now
+known or unknown (including existing as well as future claims and causes of
+action), in the Work (i) in all territories worldwide, (ii) for the maximum
+duration provided by applicable law or treaty (including future time
+extensions), (iii) in any current or future medium and for any number of
+copies, and (iv) for any purpose whatsoever, including without limitation
+commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
+the Waiver for the benefit of each member of the public at large and to the
+detriment of Affirmer's heirs and successors, fully intending that such Waiver
+shall not be subject to revocation, rescission, cancellation, termination, or
+any other legal or equitable action to disrupt the quiet enjoyment of the Work
+by the public as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason be
+judged legally invalid or ineffective under applicable law, then the Waiver
+shall be preserved to the maximum extent permitted taking into account
+Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
+is so judged Affirmer hereby grants to each affected person a royalty-free,
+non transferable, non sublicensable, non exclusive, irrevocable and
+unconditional license to exercise Affirmer's Copyright and Related Rights in
+the Work (i) in all territories worldwide, (ii) for the maximum duration
+provided by applicable law or treaty (including future time extensions), (iii)
+in any current or future medium and for any number of copies, and (iv) for any
+purpose whatsoever, including without limitation commercial, advertising or
+promotional purposes (the "License"). The License shall be deemed effective as
+of the date CC0 was applied by Affirmer to the Work. Should any part of the
+License for any reason be judged legally invalid or ineffective under
+applicable law, such partial invalidity or ineffectiveness shall not
+invalidate the remainder of the License, and in such case Affirmer hereby
+affirms that he or she will not (i) exercise any of his or her remaining
+Copyright and Related Rights in the Work or (ii) assert any associated claims
+and causes of action with respect to the Work, in either case contrary to
+Affirmer's express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+  a. No trademark or patent rights held by Affirmer are waived, abandoned,
+  surrendered, licensed or otherwise affected by this document.
+
+  b. Affirmer offers the Work as-is and makes no representations or warranties
+  of any kind concerning the Work, express, implied, statutory or otherwise,
+  including without limitation warranties of title, merchantability, fitness
+  for a particular purpose, non infringement, or the absence of latent or
+  other defects, accuracy, or the present or absence of errors, whether or not
+  discoverable, all to the greatest extent permissible under applicable law.
+
+  c. Affirmer disclaims responsibility for clearing rights of other persons
+  that may apply to the Work or any use thereof, including without limitation
+  any person's Copyright and Related Rights in the Work. Further, Affirmer
+  disclaims responsibility for obtaining any necessary consents, permissions
+  or other rights required for any use of the Work.
+
+  d. Affirmer understands and acknowledges that Creative Commons is not a
+  party to this document and has no duty or obligation with respect to this
+  CC0 or use of the Work.
diff --git a/modules/nixos-hardware/README.md b/modules/nixos-hardware/README.md
new file mode 100644
index 000000000000..e62a19dcaa12
--- /dev/null
+++ b/modules/nixos-hardware/README.md
@@ -0,0 +1,110 @@
+NixOS profiles covering hardware quirks.
+
+## Setup
+
+Add and update `nixos-hardware` channel:
+
+```
+$ sudo nix-channel --add https://github.com/NixOS/nixos-hardware/archive/master.tar.gz nixos-hardware
+$ sudo nix-channel --update
+```
+
+Then import an appropriate profile path from the table below. For example, to
+enable ThinkPad X220 profile, your `imports` in `/etc/nixos/configuration.nix`
+should look like:
+
+```
+imports = [
+  <nixos-hardware/lenovo/thinkpad/x220>
+  ./hardware-configuration.nix
+];
+```
+
+## Incomplete list of Profiles
+
+See code for all available configurations.
+
+| Model                             | Path                                               |
+| --------------------------------- | -------------------------------------------------- |
+| [Acer Aspire 4810T][]             | `<nixos-hardware/acer/aspire/4810t>`               |
+| Airis N990                        | `<nixos-hardware/airis/n990>`                      |
+| Apple MacBook Air 3,X             | `<nixos-hardware/apple/macbook-air/3>`             |
+| Apple MacBook Air 4,X             | `<nixos-hardware/apple/macbook-air/4>`             |
+| Apple MacBook Air 6,X             | `<nixos-hardware/apple/macbook-air/6>`             |
+| [Apple MacBook Pro 10,1][]        | `<nixos-hardware/apple/macbook-pro/10-1>`          |
+| Apple MacBook Pro 12,1            | `<nixos-hardware/apple/macbook-pro/12-1>`          |
+| BeagleBoard PocketBeagle          | `<nixos-hardware/beagleboard/pocketbeagle>`        |
+| Dell Latitude 3480                | `<nixos-hardware/dell/latitude/3480>`              |
+| [Dell XPS E7240][]                | `<nixos-hardware/dell/e7240>`                      |
+| [Dell XPS 13 7390][]              | `<nixos-hardware/dell/xps/13-7390>`                |
+| [Dell XPS 13 9360][]              | `<nixos-hardware/dell/xps/13-9360>`                |
+| [Dell XPS 13 9370][]              | `<nixos-hardware/dell/xps/13-9370>`                |
+| [Dell XPS 13 9380][]              | `<nixos-hardware/dell/xps/13-9380>`                |
+| [Dell XPS 15 7590][]              | `<nixos-hardware/dell/xps/15-7590>`                |
+| [Dell XPS 15 9550][]              | `<nixos-hardware/dell/xps/15-9550>`                |
+| [Dell XPS 15 9560][]              | `<nixos-hardware/dell/xps/15-9560>`                |
+| [Dell XPS 15 9560, intel only][]  | `<nixos-hardware/dell/xps/15-9560/intel>`          |
+| [Dell XPS 15 9560, nvidia only][] | `<nixos-hardware/dell/xps/15-9560/nvidia>`         |
+| [Inverse Path USB armory][]       | `<nixos-hardware/inversepath/usbarmory>`           |
+| Lenovo IdeaPad Z510               | `<nixos-hardware/lenovo/ideapad/z510>`             |
+| Lenovo ThinkPad E495              | `<nixos-hardware/lenovo/thinkpad/e495>`            |
+| Lenovo ThinkPad L13               | `<nixos-hardware/lenovo/thinkpad/l13>`             |
+| Lenovo ThinkPad P53               | `<nixos-hardware/lenovo/thinkpad/p53>`             |
+| Lenovo ThinkPad T410              | `<nixos-hardware/lenovo/thinkpad/t410>`            |
+| Lenovo ThinkPad T420              | `<nixos-hardware/lenovo/thinkpad/t420>`            |
+| Lenovo ThinkPad T430              | `<nixos-hardware/lenovo/thinkpad/t430>`            |
+| Lenovo ThinkPad T440s             | `<nixos-hardware/lenovo/thinkpad/t440s>`           |
+| Lenovo ThinkPad T440p             | `<nixos-hardware/lenovo/thinkpad/t440p>`           |
+| Lenovo ThinkPad T450s             | `<nixos-hardware/lenovo/thinkpad/t450s>`           |
+| Lenovo ThinkPad T460s             | `<nixos-hardware/lenovo/thinkpad/t460s>`           |
+| Lenovo ThinkPad T470s             | `<nixos-hardware/lenovo/thinkpad/t470s>`           |
+| Lenovo ThinkPad T480s             | `<nixos-hardware/lenovo/thinkpad/t480s>`           |
+| Lenovo ThinkPad T490              | `<nixos-hardware/lenovo/thinkpad/t490>`            |
+| Lenovo ThinkPad T495              | `<nixos-hardware/lenovo/thinkpad/t495>`            |
+| Lenovo ThinkPad X140e             | `<nixos-hardware/lenovo/thinkpad/x140e>`           |
+| Lenovo ThinkPad X220              | `<nixos-hardware/lenovo/thinkpad/x220>`            |
+| Lenovo ThinkPad X230              | `<nixos-hardware/lenovo/thinkpad/x230>`            |
+| Lenovo ThinkPad X250              | `<nixos-hardware/lenovo/thinkpad/x250>`            |
+| [Lenovo ThinkPad X260][]          | `<nixos-hardware/lenovo/thinkpad/x260>`            |
+| Lenovo ThinkPad X270              | `<nixos-hardware/lenovo/thinkpad/x270>`            |
+| Lenovo ThinkPad X280              | `<nixos-hardware/lenovo/thinkpad/x280>`            |
+| [Lenovo ThinkPad X1 (6th Gen)][]  | `<nixos-hardware/lenovo/thinkpad/x1/6th-gen>`      |
+| Lenovo ThinkPad X1 Extreme Gen 2  | `<nixos-hardware/lenovo/thinkpad/x1-extreme/gen2>` |
+| [Microsoft Surface Pro 3][]       | `<nixos-hardware/microsoft/surface-pro/3>`         |
+| PC Engines APU                    | `<nixos-hardware/pcengines/apu>`                   |
+| [Raspberry Pi 2][]                | `<nixos-hardware/raspberry-pi/2>`                  |
+| [Samsung Series 9 NP900X3C][]     | `<nixos-hardware/samsung/np900x3c>`                |
+| [Purism Librem 13v3][]            | `<nixos-hardware/purism/librem/13v3>`              |
+| [Purism Librem 15v3][]            | `<nixos-hardware/purism/librem/15v3>`              |
+| Supermicro A1SRi-2758F            | `<nixos-hardware/supermicro/a1sri-2758f>`          |
+| Supermicro X10SLL-F               | `<nixos-hardware/supermicro/x10sll-f>`             |
+| [Toshiba Chromebook 2 `swanky`][] | `<nixos-hardware/toshiba/swanky>`                  |
+| [Tuxedo InfinityBook v4][]        | `<nixos-hardware/tuxedo/infinitybook/v4>`          |
+
+[Acer Aspire 4810T]: acer/aspire/4810t
+[Apple MacBook Pro 10,1]: apple/macbook-pro/10-1
+[Dell XPS E7240]: dell/e7240
+[Dell XPS 13 7390]: dell/xps/13-7390
+[Dell XPS 13 9360]: dell/xps/13-9360
+[Dell XPS 13 9370]: dell/xps/13-9370
+[Dell XPS 13 9380]: dell/xps/13-9380
+[Dell XPS 15 7590]: dell/xps/15-7590
+[Dell XPS 15 9550]: dell/xps/15-9550
+[Dell XPS 15 9560]: dell/xps/15-9560
+[Dell XPS 15 9560, intel only]: dell/xps/15-9560/intel
+[Dell XPS 15 9560, nvidia only]: dell/xps/15-9560/nvidia
+[Inverse Path USB armory]: inversepath/usbarmory
+[Lenovo ThinkPad X1 (6th Gen)]: lenovo/thinkpad/x1/6th-gen
+[Lenovo ThinkPad X260]: lenovo/thinkpad/x260
+[Microsoft Surface Pro 3]: microsoft/surface-pro/3
+[Raspberry Pi 2]: raspberry-pi/2
+[Samsung Series 9 NP900X3C]: samsung/np900x3c
+[Purism Librem 13v3]: purism/librem/13v3
+[Purism Librem 13v5]: purism/librem/13v5
+[Toshiba Chromebook 2 `swanky`]: toshiba/swanky
+
+## How to contribute a new device profile
+
+1. Add your device profile expression in the appropriate directory
+2. Link it in the table in README.md
+3. Run ./tests/run.py to test it. The test script script will parse all the profiles from the README.md
diff --git a/modules/nixos-hardware/acer/aspire/4810t/README.wiki b/modules/nixos-hardware/acer/aspire/4810t/README.wiki
new file mode 100644
index 000000000000..0b045b6e4da8
--- /dev/null
+++ b/modules/nixos-hardware/acer/aspire/4810t/README.wiki
@@ -0,0 +1,110 @@
+= Overview =
+* CPU: Intel Core 2 Solo U3500 @ 1.40GHz (speeds: 0.8, 1.2 and 1.4GHz), 3MB L2
+* Screen: 1366x768 LED
+
+= BIOS versions =
+* 1.10 - average ACPI temperature around 34°C, good GL playing
+* 1.20 - average ACPI temperature around 39°C, good GL playing
+* 1.31 - big slowdown in X windows from time to time, GL games with annoying slowdowns regularly
+
+The most recent BIOS updates (1.20 onwards) come with a DOS flasher; older come only with a windows flasher. I used a [http://www.pcengines.ch/freedos.htm freedos image] stored in a USB flash memory to boot freedos and flash the BIOS.
+
+= Drivers =
+All provided in linux 2.6.32 kernels.
+
+Linux 2.6.29 has problems at least with the wire ethernet card (Attansic Technology Corp. Device 1063). Loading and unloading the ethernet card driver module (atl1c) helps getting a working link, although it will change from eth0 to eth1, eth2, or another interface name.
+
+== lspci ==
+ 00:00.0 Host bridge: Intel Corporation Mobile 4 Series Chipset Memory Controller Hub (rev 07)
+ 00:02.0 VGA compatible controller: Intel Corporation Mobile 4 Series Chipset Integrated Graphics Controller (rev 07)
+ 00:02.1 Display controller: Intel Corporation Mobile 4 Series Chipset Integrated Graphics Controller (rev 07)
+ 00:1a.0 USB Controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #4 (rev 03)
+ 00:1a.1 USB Controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #5 (rev 03)
+ 00:1a.7 USB Controller: Intel Corporation 82801I (ICH9 Family) USB2 EHCI Controller #2 (rev 03)
+ 00:1b.0 Audio device: Intel Corporation 82801I (ICH9 Family) HD Audio Controller (rev 03)
+ 00:1c.0 PCI bridge: Intel Corporation 82801I (ICH9 Family) PCI Express Port 1 (rev 03)
+ 00:1c.1 PCI bridge: Intel Corporation 82801I (ICH9 Family) PCI Express Port 2 (rev 03)
+ 00:1d.0 USB Controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #1 (rev 03)
+ 00:1d.1 USB Controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #2 (rev 03)
+ 00:1d.2 USB Controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #3 (rev 03)
+ 00:1d.3 USB Controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #6 (rev 03)
+ 00:1d.7 USB Controller: Intel Corporation 82801I (ICH9 Family) USB2 EHCI Controller #1 (rev 03)
+ 00:1e.0 PCI bridge: Intel Corporation 82801 Mobile PCI Bridge (rev 93)
+ 00:1f.0 ISA bridge: Intel Corporation ICH9M-E LPC Interface Controller (rev 03)
+ 00:1f.2 IDE interface: Intel Corporation ICH9M/M-E 2 port SATA IDE Controller (rev 03)
+ 00:1f.3 SMBus: Intel Corporation 82801I (ICH9 Family) SMBus Controller (rev 03)
+ 00:1f.5 IDE interface: Intel Corporation ICH9M/M-E 2 port SATA IDE Controller (rev 03)
+ 00:1f.6 Signal processing controller: Intel Corporation 82801I (ICH9 Family) Thermal Subsystem (rev 03)
+ 01:00.0 Ethernet controller: Attansic Technology Corp. Device 1063 (rev c0)
+ 02:00.0 Network controller: Intel Corporation Wireless WiFi Link 5100
+
+= Configuration =
+
+ {
+  # Make te network WLAN card (wlan0) firmware available
+  require = [ ./nixos/modules/hardware/network/intel-5000.nix ];
+ 
+  boot = rec {
+    initrd = {
+      kernelModules = [ "ata_piix" "reiserfs" ];
+    };
+    kernelParams = [
+       "selinux=0"
+       "apm=on"
+       "acpi=on"
+       "vga=0x317"   
+       "console=tty1"
+       "video=vesafb:ywrap"
+       # Important, to disable Kernel Mode Setting for the graphics card
+       # This will allow backlight regulation
+       "nomodeset"
+    ];
+    kernelModules = [
+      "acpi-cpufreq"
+      "cpufreq-ondemand" # not autoloaded in 2.6.32.2
+      "kvm-intel"
+    ];
+    resumeDevice = "8:2";
+    extraKernelParams = ["resume=/dev/sda2"];
+    kernelPackages = pkgs.kernelPackages_2_6_32;
+    postBootCommands = ''
+      echo ondemand > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
+    '';
+  };
+ 
+  services = {
+    xserver = {
+      enable = true;
+      defaultDepth = 24;
+      videoDriver = "intel";
+      exportConfiguration = true;
+      enableTCP = true;
+      autorun = true;
+      driSupport = false;
+      synaptics = {
+        enable = true;
+        dev = "/dev/input/event8";
+      };
+   };
+ };
+
+= X settings =
+
+== Backlight ==
+Having disabled Kernel Mode Setting for the i915 kernel drm module (kernel parameter ''nomodeset''), use ''xrandr'' to set:
+ xrandr --output LVDS --set BACKLIGHT_CONTROL combination
+ xrandr --output LVDS --set BACKLIGHT 300  # ~1000, max light.
+
+If those steps make your mouse cursor not appear, switch to another VT, and then back to X, and it will come.
+
+== Synaptics pad ==
+I like the pad speed set by:
+ synclient MaxTapTime=0 MaxSpeed=1.12 MinSpeed=0.6 AccelFactor=0.001 VertEdgeScroll=1
+Install the ''xf86-input-synaptics-1.2.0'' for ''synclient'' to be in your PATH.
+
+= Suspending and hibernating =
+Usual pm-utils work, with pm-suspend and pm-hibernate.
+
+[[Category:Installation]]
+[[Category:Hardware]]
+[[Category:Laptops]]
diff --git a/modules/nixos-hardware/acer/aspire/4810t/default.nix b/modules/nixos-hardware/acer/aspire/4810t/default.nix
new file mode 100644
index 000000000000..d40b454b3aa4
--- /dev/null
+++ b/modules/nixos-hardware/acer/aspire/4810t/default.nix
@@ -0,0 +1,29 @@
+{ lib, ... }:
+
+{
+  imports = [
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop
+  ];
+
+  boot = {
+    initrd.kernelModules = [ "ata_piix" ];
+    kernelParams = [
+      "apm=on"
+      "acpi=on"
+      "vga=0x317"
+      "video=vesafb:ywrap"
+
+      # Important, disable KMS to fix backlight regulation:
+      "nomodeset"
+    ];
+  };
+
+  # TODO: reverse compat
+  hardware.opengl.driSupport = false;
+
+  # TODO: reverse compat
+  services.xserver = {
+    defaultDepth = lib.mkDefault 24;
+  };
+}
diff --git a/modules/nixos-hardware/airis/n990/default.nix b/modules/nixos-hardware/airis/n990/default.nix
new file mode 100644
index 000000000000..ae06813131a8
--- /dev/null
+++ b/modules/nixos-hardware/airis/n990/default.nix
@@ -0,0 +1,21 @@
+{ lib, pkgs, ... }:
+
+{
+  imports = [ ../../common/pc/laptop ];
+
+  boot = {
+    initrd.kernelModules = [ "pata_via" ];
+
+    kernelParams = [
+      "apm=on"
+      "acpi=on"
+      "vga=0x317"  # 1024x768
+      "console=tty1"
+      "video=vesafb:ywrap"  # Faster scroll
+     ];
+  };
+
+  hardware.firmware = with pkgs; [ intel2200BGFirmware ];
+
+  services.xserver.videoDrivers = [ "openchrome" ];
+}
diff --git a/modules/nixos-hardware/apple/default.nix b/modules/nixos-hardware/apple/default.nix
new file mode 100644
index 000000000000..a2ee26230aaa
--- /dev/null
+++ b/modules/nixos-hardware/apple/default.nix
@@ -0,0 +1,12 @@
+{ config, lib, ... }:
+
+{
+  boot.kernelParams = [
+    "hid_apple.iso_layout=0"
+  ];
+
+  hardware.facetimehd.enable = lib.mkDefault
+    (config.nixpkgs.config.allowUnfree or false);
+
+  services.mbpfan.enable = lib.mkDefault true;
+}
diff --git a/modules/nixos-hardware/apple/macbook-air/3/default.nix b/modules/nixos-hardware/apple/macbook-air/3/default.nix
new file mode 100644
index 000000000000..03221edcd0b0
--- /dev/null
+++ b/modules/nixos-hardware/apple/macbook-air/3/default.nix
@@ -0,0 +1,15 @@
+{ config, lib, ... }:
+
+{
+  imports = [ 
+    ../../.
+    ../../../common/pc/laptop
+    ../../../common/pc/ssd
+  ];
+
+  # Built-in iSight is recognized by the generic uvcvideo kernel module
+  hardware.facetimehd.enable = false;
+
+  hardware.cpu.intel.updateMicrocode =
+    lib.mkDefault config.hardware.enableRedistributableFirmware;
+}
diff --git a/modules/nixos-hardware/apple/macbook-air/4/default.nix b/modules/nixos-hardware/apple/macbook-air/4/default.nix
new file mode 100644
index 000000000000..181a099799a1
--- /dev/null
+++ b/modules/nixos-hardware/apple/macbook-air/4/default.nix
@@ -0,0 +1,16 @@
+{ lib, ... }:
+
+{
+  imports = [
+    ../.
+    ../../../common/cpu/intel/sandy-bridge
+  ];
+
+  boot.kernelParams = [
+    "acpi_backlight=vendor"
+  ];
+
+  services.xserver.deviceSection = lib.mkDefault ''
+    Option "TearFree" "true"
+  '';
+}
diff --git a/modules/nixos-hardware/apple/macbook-air/6/default.nix b/modules/nixos-hardware/apple/macbook-air/6/default.nix
new file mode 100644
index 000000000000..555d70418b85
--- /dev/null
+++ b/modules/nixos-hardware/apple/macbook-air/6/default.nix
@@ -0,0 +1,18 @@
+{ config, lib, ... }:
+
+{
+  imports = [ ../. ];
+
+  boot = {
+    extraModulePackages = with config.boot.kernelPackages; [ mba6x_bl ];
+    kernelModules = [ "mba6x_bl" ];
+
+    # Divides power consumption by two.
+    kernelParams = [ "acpi_osi=" ];
+  };
+
+  services.xserver.deviceSection = lib.mkDefault ''
+    Option "Backlight" "mba6x_backlight"
+    Option "TearFree" "true"
+  '';
+}
diff --git a/modules/nixos-hardware/apple/macbook-air/default.nix b/modules/nixos-hardware/apple/macbook-air/default.nix
new file mode 100644
index 000000000000..ea15175d0319
--- /dev/null
+++ b/modules/nixos-hardware/apple/macbook-air/default.nix
@@ -0,0 +1,8 @@
+{
+  imports = [
+    ../.
+    ../../common/cpu/intel
+    ../../common/pc/laptop
+    ../../common/pc/laptop/ssd
+  ];
+}
diff --git a/modules/nixos-hardware/apple/macbook-pro/10-1/README.wiki b/modules/nixos-hardware/apple/macbook-pro/10-1/README.wiki
new file mode 100644
index 000000000000..149b30e6ed22
--- /dev/null
+++ b/modules/nixos-hardware/apple/macbook-pro/10-1/README.wiki
@@ -0,0 +1,9 @@
+= Apple MacBook Pro 10,1 =
+
+I presume the associated configuration also will work with to other 10,x models.
+
+If you don't have an Ethernet adapter, make sure your NixOS installer has a sufficiently modern kernel or you will be stuck without internet and unable to installer.
+
+Note that (with NixPkgs circa late October 2016) Linux 4.8.1 does not work: everything will boot through X, but then manually-run commands accessing peripherals (nmcli, lspci, etc) will hang inexplicably.
+
+Thankfully no tweaking of the BIOS or similar was needed. Do note that if your MacOS partition is encrypted, you will have an easier time shrinking it from within MacOS, but this is standard advice.
diff --git a/modules/nixos-hardware/apple/macbook-pro/10-1/default.nix b/modules/nixos-hardware/apple/macbook-pro/10-1/default.nix
new file mode 100644
index 000000000000..e029ef21ecac
--- /dev/null
+++ b/modules/nixos-hardware/apple/macbook-pro/10-1/default.nix
@@ -0,0 +1,21 @@
+{ lib, pkgs, ... }:
+
+{
+  imports = [
+    ../.
+    ../../../common/pc/laptop/ssd
+  ];
+
+  # TODO: boot loader
+  boot.loader.systemd-boot.enable = true;
+  boot.loader.efi.canTouchEfiVariables = true;
+
+  # TODO: reverse compat
+  hardware.opengl.driSupport32Bit = true;
+
+  services.xserver = {
+    # TODO: we should not enable unfree drivers
+    # when there is an alternative (i.e. nouveau)
+    videoDrivers = [ "nvidia" ];
+  };
+}
diff --git a/modules/nixos-hardware/apple/macbook-pro/11-5/README.md b/modules/nixos-hardware/apple/macbook-pro/11-5/README.md
new file mode 100644
index 000000000000..17e5ef1d36e0
--- /dev/null
+++ b/modules/nixos-hardware/apple/macbook-pro/11-5/README.md
@@ -0,0 +1,32 @@
+# Apple MacBook Pro 11,5
+
+This configuration will **not** work with MacBook Pro 11,2 or 11,3 models.
+
+## Notable features
+
+* Prevent intermittent USB 3.0 controller wakeup signal when the lid is closed. Without this fix your laptop may get very hot and drain the battery after waking up in your laptop bag.
+
+  You can see for yourself which devices are allowed to wake up your laptop using the command:
+
+  ```shell
+  cat /proc/acpi/wakeup
+  ```
+
+  This fix works for Linux kernel 3.13 and above.
+
+  Sources:
+
+  * [Fix unwanted laptop resume after lid is closed](https://medium.com/@laurynas.karvelis_95228/install-arch-linux-on-macbook-pro-11-2-retina-install-guide-for-year-2017-2034ceed4cb2#66ba)
+  * [Arch wiki: MacBookPro11,x Suspend](https://wiki.archlinux.org/index.php/MacBookPro11,x#Suspend)
+  * [simonvandel/dotfiles (nix config)](https://github.com/simonvandel/dotfiles/blob/f254a4a607257faee295ce798ed215273c342850/nixos/vandel-macair/configuration.nix#L45)
+
+## Graphics
+
+The [MacBookPro11,4 and MacBookPro11,5](https://support.apple.com/kb/SP719) models ship with a discrete ATI graphics card (whereas MacBookPro11,2 and MacBookPro11,3 ship with NVidia cards). This is alongside the usual integrated Intel GPU.
+
+You may wish to look into dynamic switching between integrated and discrete graphics, but this config doesn't attempt it.
+
+## Additional resources
+
+* Arch linux wiki: [MacBookPro11,x](https://wiki.archlinux.org/index.php/MacBookPro11,x)
+* Kernel patches: [MacBookPro11,x](https://bugzilla.kernel.org/buglist.cgi?quicksearch=macbookpro11)
diff --git a/modules/nixos-hardware/apple/macbook-pro/11-5/default.nix b/modules/nixos-hardware/apple/macbook-pro/11-5/default.nix
new file mode 100644
index 000000000000..a53b5d8adeb4
--- /dev/null
+++ b/modules/nixos-hardware/apple/macbook-pro/11-5/default.nix
@@ -0,0 +1,27 @@
+{ lib, config, pkgs, ... }:
+
+let
+
+  kernelPackages = config.boot.kernelPackages;
+
+in
+
+{
+  imports = [
+    ../.
+    ../../../common/pc/laptop/ssd
+    <nixpkgs/nixos/modules/hardware/network/broadcom-43xx.nix>
+  ];
+
+  # Apparently this is currently only supported by ati_unfree drivers, not ati
+  hardware.opengl.driSupport32Bit = false;
+
+  services.xserver.videoDrivers = [ "ati" ];
+
+  services.udev.extraRules =
+    # Disable XHC1 wakeup signal to avoid resume getting triggered some time
+    # after suspend. Reboot required for this to take effect.
+    lib.optionalString
+      (lib.versionAtLeast kernelPackages.kernel.version "3.13")
+      ''SUBSYSTEM=="pci", KERNEL=="0000:00:14.0", ATTR{power/wakeup}="disabled"'';
+}
diff --git a/modules/nixos-hardware/apple/macbook-pro/12-1/default.nix b/modules/nixos-hardware/apple/macbook-pro/12-1/default.nix
new file mode 100644
index 000000000000..02c6f328cf5a
--- /dev/null
+++ b/modules/nixos-hardware/apple/macbook-pro/12-1/default.nix
@@ -0,0 +1,14 @@
+{ lib, ... }:
+
+{
+  imports = [
+    ../.
+    ../../../common/pc/laptop/ssd
+    <nixpkgs/nixos/modules/hardware/network/broadcom-43xx.nix>
+  ];
+
+  # USB subsystem wakes up MBP right after suspend unless we disable it.
+  services.udev.extraRules = lib.mkDefault ''
+    SUBSYSTEM=="pci", KERNEL=="0000:00:14.0", ATTR{power/wakeup}="disabled"
+  '';
+}
diff --git a/modules/nixos-hardware/apple/macbook-pro/default.nix b/modules/nixos-hardware/apple/macbook-pro/default.nix
new file mode 100644
index 000000000000..4ced65a382e0
--- /dev/null
+++ b/modules/nixos-hardware/apple/macbook-pro/default.nix
@@ -0,0 +1,7 @@
+{
+  imports = [
+    ../.
+    ../../common/cpu/intel
+    ../../common/pc/laptop
+  ];
+}
diff --git a/modules/nixos-hardware/asus/wireless-ac1300.nix b/modules/nixos-hardware/asus/wireless-ac1300.nix
new file mode 100644
index 000000000000..6e9b815a0eb0
--- /dev/null
+++ b/modules/nixos-hardware/asus/wireless-ac1300.nix
@@ -0,0 +1,6 @@
+{ config, ... }:
+
+{
+  boot.extraModulePackages = with config.boot.kernelPackages; [ rtl8812au ];
+  boot.kernelModules = [ "8812au" ];
+}
diff --git a/modules/nixos-hardware/audio-gd/compass2.nix b/modules/nixos-hardware/audio-gd/compass2.nix
new file mode 100644
index 000000000000..5268c1dab8d1
--- /dev/null
+++ b/modules/nixos-hardware/audio-gd/compass2.nix
@@ -0,0 +1,7 @@
+{
+  boot = {
+    extraModprobeConfig = lib.mkDefault ''
+      options snd slots=snd_usb_audio,snd-hda-intel
+    '';
+  };
+}
diff --git a/modules/nixos-hardware/beagleboard/pocketbeagle/default.nix b/modules/nixos-hardware/beagleboard/pocketbeagle/default.nix
new file mode 100644
index 000000000000..bc426300f458
--- /dev/null
+++ b/modules/nixos-hardware/beagleboard/pocketbeagle/default.nix
@@ -0,0 +1,10 @@
+{ lib, ... }:
+
+{
+  boot.kernelParams = [ "console=ttyO0,115200n8" ];
+
+  boot.loader = {
+    generic-extlinux-compatible.enable = lib.mkDefault true;
+    grub.enable = lib.mkDefault false;
+  };
+}
diff --git a/modules/nixos-hardware/common/cpu/amd/default.nix b/modules/nixos-hardware/common/cpu/amd/default.nix
new file mode 100644
index 000000000000..d8c48a263081
--- /dev/null
+++ b/modules/nixos-hardware/common/cpu/amd/default.nix
@@ -0,0 +1,6 @@
+{ config, lib, ... }:
+
+{
+  hardware.cpu.amd.updateMicrocode =
+    lib.mkDefault config.hardware.enableRedistributableFirmware;
+}
diff --git a/modules/nixos-hardware/common/cpu/intel/default.nix b/modules/nixos-hardware/common/cpu/intel/default.nix
new file mode 100644
index 000000000000..ad945f394a6c
--- /dev/null
+++ b/modules/nixos-hardware/common/cpu/intel/default.nix
@@ -0,0 +1,15 @@
+{ config, lib, pkgs, ... }:
+
+{
+  boot.initrd.kernelModules = [ "i915" ];
+
+  hardware.cpu.intel.updateMicrocode =
+    lib.mkDefault config.hardware.enableRedistributableFirmware;
+  
+  hardware.opengl.extraPackages = with pkgs; [
+    vaapiIntel
+    vaapiVdpau
+    libvdpau-va-gl
+    intel-media-driver
+  ];
+}
diff --git a/modules/nixos-hardware/common/cpu/intel/kaby-lake/default.nix b/modules/nixos-hardware/common/cpu/intel/kaby-lake/default.nix
new file mode 100644
index 000000000000..e6a2d1ce8e6e
--- /dev/null
+++ b/modules/nixos-hardware/common/cpu/intel/kaby-lake/default.nix
@@ -0,0 +1,8 @@
+{
+  imports = [ ../. ];
+
+  boot.kernelParams = [
+    "i915.enable_fbc=1"
+    "i915.enable_psr=2"
+  ];
+}
diff --git a/modules/nixos-hardware/common/cpu/intel/sandy-bridge/default.nix b/modules/nixos-hardware/common/cpu/intel/sandy-bridge/default.nix
new file mode 100644
index 000000000000..682815cee473
--- /dev/null
+++ b/modules/nixos-hardware/common/cpu/intel/sandy-bridge/default.nix
@@ -0,0 +1,9 @@
+{
+  imports = [ ../. ];
+
+  # Enables RC6, RC6p and RC6pp.
+  # Last two are only available on Sandy Bridge CPUs (circa 2011).
+  boot.kernelParams = [
+    "i915.enable_rc6=7"
+  ];
+}
diff --git a/modules/nixos-hardware/common/pc/default.nix b/modules/nixos-hardware/common/pc/default.nix
new file mode 100644
index 000000000000..0b498ff1d20e
--- /dev/null
+++ b/modules/nixos-hardware/common/pc/default.nix
@@ -0,0 +1,5 @@
+{ lib, ... }:
+
+{
+  services.xserver.libinput.enable = lib.mkDefault true;
+}
diff --git a/modules/nixos-hardware/common/pc/hdd/default.nix b/modules/nixos-hardware/common/pc/hdd/default.nix
new file mode 100644
index 000000000000..8cc5ada0deb9
--- /dev/null
+++ b/modules/nixos-hardware/common/pc/hdd/default.nix
@@ -0,0 +1,7 @@
+{ lib, ... }:
+
+{
+  boot.kernel.sysctl = {
+    "vm.swappiness" = lib.mkDefault 10;
+  };
+}
diff --git a/modules/nixos-hardware/common/pc/laptop/acpi_call.nix b/modules/nixos-hardware/common/pc/laptop/acpi_call.nix
new file mode 100644
index 000000000000..f8c17dcc4901
--- /dev/null
+++ b/modules/nixos-hardware/common/pc/laptop/acpi_call.nix
@@ -0,0 +1,10 @@
+# acpi_call makes tlp work for newer thinkpads
+
+{ config, ... }:
+
+{
+  boot = {
+    kernelModules = [ "acpi_call" ];
+    extraModulePackages = with config.boot.kernelPackages; [ acpi_call ];
+  };
+}
diff --git a/modules/nixos-hardware/common/pc/laptop/default.nix b/modules/nixos-hardware/common/pc/laptop/default.nix
new file mode 100644
index 000000000000..ebc4931dbc29
--- /dev/null
+++ b/modules/nixos-hardware/common/pc/laptop/default.nix
@@ -0,0 +1,13 @@
+{ config, lib, ... }:
+
+{
+  imports = [ ../. ];
+
+  # TODO: fix in NixOS/nixpkgs
+  # Disable governor set in hardware-configuration.nix,
+  # required when services.tlp.enable is true:
+  powerManagement.cpuFreqGovernor =
+    lib.mkIf config.services.tlp.enable (lib.mkForce null);
+
+  services.tlp.enable = lib.mkDefault true;
+}
diff --git a/modules/nixos-hardware/common/pc/laptop/hdd/default.nix b/modules/nixos-hardware/common/pc/laptop/hdd/default.nix
new file mode 100644
index 000000000000..63f29d458c29
--- /dev/null
+++ b/modules/nixos-hardware/common/pc/laptop/hdd/default.nix
@@ -0,0 +1,8 @@
+{ lib, ... }:
+
+{
+  imports = [ ../../hdd ];
+
+  # Hard disk protection if the laptop falls:
+  services.hdapsd.enable = lib.mkDefault true;
+}
diff --git a/modules/nixos-hardware/common/pc/laptop/ssd b/modules/nixos-hardware/common/pc/laptop/ssd
new file mode 120000
index 000000000000..e313834a4372
--- /dev/null
+++ b/modules/nixos-hardware/common/pc/laptop/ssd
@@ -0,0 +1 @@
+../ssd
\ No newline at end of file
diff --git a/modules/nixos-hardware/common/pc/ssd/default.nix b/modules/nixos-hardware/common/pc/ssd/default.nix
new file mode 100644
index 000000000000..00922e649938
--- /dev/null
+++ b/modules/nixos-hardware/common/pc/ssd/default.nix
@@ -0,0 +1,9 @@
+{ lib, ... }:
+
+{
+  boot.kernel.sysctl = {
+    "vm.swappiness" = lib.mkDefault 1;
+  };
+
+  services.fstrim.enable = lib.mkDefault true;
+}
diff --git a/modules/nixos-hardware/default.nix b/modules/nixos-hardware/default.nix
new file mode 100644
index 000000000000..fbb5b77061ff
--- /dev/null
+++ b/modules/nixos-hardware/default.nix
@@ -0,0 +1,3 @@
+# This file is necessary so nix-env -qa does not break,
+# when nixos-hardware is used as a channel
+{}
diff --git a/modules/nixos-hardware/dell/e7240/README.md b/modules/nixos-hardware/dell/e7240/README.md
new file mode 100644
index 000000000000..b712f4d2bc25
--- /dev/null
+++ b/modules/nixos-hardware/dell/e7240/README.md
@@ -0,0 +1,8 @@
+On some kernel versions user ashgillman has experiences suspend issues
+(see https://bugzilla.redhat.com/show_bug.cgi?id=1597481).
+
+Try:
+
+```nix
+boot.kernelPackages = pkgs.linuxPackages_4_14;
+```
diff --git a/modules/nixos-hardware/dell/e7240/default.nix b/modules/nixos-hardware/dell/e7240/default.nix
new file mode 100644
index 000000000000..3334a745f8c3
--- /dev/null
+++ b/modules/nixos-hardware/dell/e7240/default.nix
@@ -0,0 +1,8 @@
+{ lib, pkgs, ... }:
+
+{
+  imports = [
+    ../../common/cpu/intel
+    ../../common/pc/laptop
+  ];
+}
diff --git a/modules/nixos-hardware/dell/latitude/3480/default.nix b/modules/nixos-hardware/dell/latitude/3480/default.nix
new file mode 100644
index 000000000000..86be458a1d96
--- /dev/null
+++ b/modules/nixos-hardware/dell/latitude/3480/default.nix
@@ -0,0 +1,14 @@
+{ lib, pkgs, ... }:
+
+{
+  imports = [
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop
+    ../../../common/pc/laptop/ssd
+  ];
+
+  # touchpad goes over i2c
+  boot.blacklistedKernelModules = [ "psmouse" ];
+
+  services.xserver.videoDrivers = lib.mkDefault [ "intel" ];
+}
diff --git a/modules/nixos-hardware/dell/xps/13-7390/README.wiki b/modules/nixos-hardware/dell/xps/13-7390/README.wiki
new file mode 100644
index 000000000000..6ec37d0f4476
--- /dev/null
+++ b/modules/nixos-hardware/dell/xps/13-7390/README.wiki
@@ -0,0 +1,16 @@
+= Dell XPS 13 7390 =
+
+== Installation == 
+
+You need to disable RAID and use AHCI instead as described [https://wiki.ubuntu.com/Dell/XPS/XPS-13-7390-2-in-1 here].
+
+== Firmware upgrades ==
+
+Note that this device is supported by [https://fwupd.org/ fwupd].
+To perform firmware upgrades just activate the service
+
+<code>
+services.fwupd.enable = true;
+</code>
+
+Then use <code>fwupdmgr</code> to perform updates.
diff --git a/modules/nixos-hardware/dell/xps/13-7390/default.nix b/modules/nixos-hardware/dell/xps/13-7390/default.nix
new file mode 100644
index 000000000000..991e4352cb50
--- /dev/null
+++ b/modules/nixos-hardware/dell/xps/13-7390/default.nix
@@ -0,0 +1,13 @@
+{ lib, pkgs, ... }:
+
+{
+  imports = [
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop
+    ../../../common/pc/laptop/ssd
+  ];
+
+  boot.kernelParams = [ "mem_sleep_default=deep" ];
+
+  services.thermald.enable = true;
+}
diff --git a/modules/nixos-hardware/dell/xps/13-9360/default.nix b/modules/nixos-hardware/dell/xps/13-9360/default.nix
new file mode 100644
index 000000000000..5ff1e54a46b6
--- /dev/null
+++ b/modules/nixos-hardware/dell/xps/13-9360/default.nix
@@ -0,0 +1,29 @@
+{ lib, pkgs, ... }:
+
+{
+  imports = [
+    ../../../common/cpu/intel/kaby-lake
+    ../../../common/pc/laptop
+  ];
+
+  boot.blacklistedKernelModules = [ "psmouse" ]; # touchpad goes over i2c
+
+  # TODO: decide on boot loader policy
+  boot.loader = {
+    efi.canTouchEfiVariables = lib.mkDefault true;
+    systemd-boot.enable = lib.mkDefault true;
+  };
+
+  hardware.firmware = lib.mkBefore [ pkgs.qca6174-firmware ];
+
+  # TODO: move to general HiDPI profile
+  i18n.consoleFont = lib.mkDefault "latarcyrheb-sun32"; # 4K screen, use bigger console font
+
+  # TODO: upstream to NixOS/nixpkgs
+  nixpkgs.overlays = [(final: previous: {
+    qca6174-firmware = final.callPackage ./qca6174-firmware.nix {};
+  })];
+
+  # This will save you money and possibly your life!
+  services.thermald.enable = true;
+}
diff --git a/modules/nixos-hardware/dell/xps/13-9360/qca6174-firmware.nix b/modules/nixos-hardware/dell/xps/13-9360/qca6174-firmware.nix
new file mode 100644
index 000000000000..c5da6c183280
--- /dev/null
+++ b/modules/nixos-hardware/dell/xps/13-9360/qca6174-firmware.nix
@@ -0,0 +1,26 @@
+{ stdenv, fetchurl }:
+
+stdenv.mkDerivation rec {
+  name = "${target}-firmware-${version}";
+  version = "${branch}-00042";
+
+  branch = "4.4.1.c1";
+  target = "QCA6174";
+
+  src = fetchurl {
+    url = "https://github.com/kvalo/ath10k-firmware/raw/952afa4949cb34193040cd4e7441e1aee50ac731/${target}/hw3.0/${branch}/firmware-6.bin_RM.${version}-QCARMSWP-1";
+    sha256 = "01vvz3qhqw5l3yilcqgk1spk4y9k4qy7na7a57cbl037r231szdh";
+  };
+
+  buildCommand = ''
+    install -D $src $out/lib/firmware/ath10k/${target}/hw3.0/firmware-6.bin
+  '';
+
+  meta = with stdenv.lib; {
+    description = "Updated firmware for the qca6174 wireless chip";
+    homepage = "https://github.com/kvalo/ath10k-firmware/tree/master/QCA6174/hw3.0";
+    license = licenses.unfreeRedistributable;
+    maintainers = with maintainers; [ yorickvp ];
+    platforms = platforms.linux;
+  };
+}
diff --git a/modules/nixos-hardware/dell/xps/13-9370/README.wiki b/modules/nixos-hardware/dell/xps/13-9370/README.wiki
new file mode 100644
index 000000000000..57b5e08d3850
--- /dev/null
+++ b/modules/nixos-hardware/dell/xps/13-9370/README.wiki
@@ -0,0 +1,16 @@
+= Dell XPS 13 9370 =
+
+== Battery drain when sleeping ==
+
+The laptop uses the S2 sleep mode by default instead of S3, which leads to
+draining a lot of battery during sleep.
+
+Though this is intentional by the manufacturer and might cause lockups when
+forced  I - @moredread - never had any issues with it.
+
+See https://wiki.archlinux.org/index.php/Dell_XPS_13_(9370)#Power_Management
+and https://bugzilla.kernel.org/show_bug.cgi?id=199689#c3 for reference
+
+== Additional resources ==
+
+* Arch linux wiki: [https://wiki.archlinux.org/index.php/Dell_XPS_13_(9370) Dell XPS 13 9370]
diff --git a/modules/nixos-hardware/dell/xps/13-9370/default.nix b/modules/nixos-hardware/dell/xps/13-9370/default.nix
new file mode 100644
index 000000000000..dbe852f0c27f
--- /dev/null
+++ b/modules/nixos-hardware/dell/xps/13-9370/default.nix
@@ -0,0 +1,20 @@
+{ lib, pkgs, ... }:
+
+{
+  imports = [
+    ../../../common/cpu/intel/kaby-lake
+    ../../../common/pc/laptop
+    ../../../common/pc/laptop/acpi_call.nix
+  ];
+
+  # Force S3 sleep mode. See README.wiki for details.
+  boot.kernelParams = [ "mem_sleep_default=deep" ];
+
+  # touchpad goes over i2c
+  boot.blacklistedKernelModules = [ "psmouse" ];
+
+  services.throttled.enable = lib.mkDefault true;
+
+  # This will save you money and possibly your life!
+  services.thermald.enable = true;
+}
diff --git a/modules/nixos-hardware/dell/xps/13-9380/README.wiki b/modules/nixos-hardware/dell/xps/13-9380/README.wiki
new file mode 100644
index 000000000000..c5ad61e3fd63
--- /dev/null
+++ b/modules/nixos-hardware/dell/xps/13-9380/README.wiki
@@ -0,0 +1,20 @@
+= Dell XPS 13 9380 =
+
+== Firmware upgrades ==
+
+Note that this device is supported by [https://fwupd.org/ fwupd].
+To perform firmware upgrades just activate the service
+
+<code>
+services.fwupd.enable = true;
+</code>
+
+Then use <code>fwupdmgr</code> to perform updates.
+
+== Battery drain when sleeping ==
+
+The laptop uses the S2 sleep mode by default instead of S3, which leads to
+draining a lot of battery during sleep.
+
+See https://wiki.archlinux.org/index.php/Dell_XPS_13_(9370)#Power_Management
+and https://bugzilla.kernel.org/show_bug.cgi?id=199689#c3 for reference
diff --git a/modules/nixos-hardware/dell/xps/13-9380/default.nix b/modules/nixos-hardware/dell/xps/13-9380/default.nix
new file mode 100644
index 000000000000..162cc2618eb1
--- /dev/null
+++ b/modules/nixos-hardware/dell/xps/13-9380/default.nix
@@ -0,0 +1,18 @@
+{ lib, pkgs, ... }:
+
+{
+  imports = [
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop
+    ../../../common/pc/laptop/acpi_call.nix
+  ];
+
+  # Force S3 sleep mode. See README.wiki for details.
+  boot.kernelParams = [ "mem_sleep_default=deep" ];
+
+  # touchpad goes over i2c
+  boot.blacklistedKernelModules = [ "psmouse" ];
+
+  # This will save you money and possibly your life!
+  services.thermald.enable = true;
+}
diff --git a/modules/nixos-hardware/dell/xps/15-7590/README.wiki b/modules/nixos-hardware/dell/xps/15-7590/README.wiki
new file mode 100644
index 000000000000..052df2911f1b
--- /dev/null
+++ b/modules/nixos-hardware/dell/xps/15-7590/README.wiki
@@ -0,0 +1,54 @@
+= Dell XPS 15 7590 =
+*Mostly copied from 15-9550
+
+== Tested Hardware ==
+
+* CPU: Intel(R) Core(TM) i9-9980HK
+* RAM: 32 GB
+* HDD: 1 TiB SSD
+* Screen: 15" 4k (3840✕2160)
+* Input: Touchscreen and trackpad.
+
+== Firmware Configuration ==
+
+Not much tweaking of NixOS itself was needed. But we currently cannot automate the firmware setup, so this must be done by hand.
+
+=== Before installation ===
+
+These settings are needed both for booting the final install, and installer itself. Therefore, they must be done first.
+
+* ''Disable Secure Boot (but keep UEFI Boot).'' Thankfully doing so is as easy as changing any other simple setting.
+
+* ''Disable Intel hardware RAID and use AHCI instead.'' Intel doesn't seem to provide a working linux driver for this.  (If you just have SSD it's pointless and just slows things down needlessly anyways.)
+
+=== Wifi ===
+~~Wifi does not work with kernels older than 5.1 (firmware not present) or newer~~ (https://bbs.archlinux.org/viewtopic.php?id=247705)
+
+Update: The 48.ucode causes the Killer wifi card to crash. The iwlfwifi-cc-a0-46.ucode works perfectly. 
+default.nix contains an overlay that removes the offending ucode from the linux-firmware bundle.
+To use it one also needs to enable unfree firmware in their own configuration (<code>hardware.enableRedistributableFirmware = true;</code>)
+
+```
+  # Use the systemd-boot EFI boot loader.
+  boot.loader.systemd-boot.enable = true;
+  boot.loader.grub = {
+    device = "nodev";
+    efiSupport = true;
+    efiInstallAsRemovable = true;
+  };
+  boot.loader.efi.canTouchEfiVariables = true;
+  boot.kernelPackages = pkgs.linuxPackages_5_1;
+```
+Disable the `canTouchEfiVariables` after a boot or two to prevent NVRAM wearout.
+
+
+=== After installation ===
+
+* ''Add systemd-boot to UEFI boot list.'' The (uneditable anyways) settings mapping drive UUIDs to HD* work fine.
+
+=== Optional ===
+
+* ''Update BIOS.'' According to Reddit, this helps with battery life.
+
+=== Troubleshooting ===
+
diff --git a/modules/nixos-hardware/dell/xps/15-7590/default.nix b/modules/nixos-hardware/dell/xps/15-7590/default.nix
new file mode 100644
index 000000000000..eeb69e204ee9
--- /dev/null
+++ b/modules/nixos-hardware/dell/xps/15-7590/default.nix
@@ -0,0 +1,51 @@
+{ lib, ... }:
+# Earlier font-size setup.
+# Virtual console options were renamed in 20.03; use the right option depending
+# on the OS version; keep this here at least until 20.03 is stable.
+lib.recursiveUpdate
+(if lib.versionAtLeast (lib.versions.majorMinor lib.version) "20.03" then {
+  console.earlySetup = true;
+} else {
+  boot.earlyVconsoleSetup = true;
+}) {
+  imports = [
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop
+    ../../../common/pc/ssd
+  ];
+
+  # Set to true for just the first run, then disable it.
+  # boot.loader.efi.canTouchEfiVariables = lib.mkDefault true;
+
+  # Load GPU drivers.
+  # hardware.bumblebee.enable = lib.mkDefault true;
+
+  # High DPI for X users. 175 "looks reasonable" but I didn't do the actual DPI
+  # calculation.
+  # services.xserver.dpi = lib.mkDefault 175;
+
+  # Earlier font-size setup
+  console.earlySetup = true;
+
+  # Prevent small EFI partiion from filling up
+  boot.loader.grub.configurationLimit = 10;
+
+  # The 48.ucode causes the Killer wifi card to crash.
+  # The iwlfwifi-cc-a0-46.ucode works perfectly
+  nixpkgs.overlays = [
+    (self: super: {
+      firmwareLinuxNonfree = super.firmwareLinuxNonfree.overrideAttrs (old: {
+        src = super.fetchgit {
+          url =
+            "https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git";
+          rev = "bf13a71b18af229b4c900b321ef1f8443028ded8";
+          sha256 = "1dcaqdqyffxiadx420pg20157wqidz0c0ca5mrgyfxgrbh6a4mdj";
+        };
+        postInstall = ''
+          rm $out/lib/firmware/iwlwifi-cc-a0-48.ucode
+        '';
+        outputHash = "0dq48i1cr8f0qx3nyq50l9w9915vhgpwmwiw3b4yhisbc3afyay4";
+      });
+    })
+  ];
+}
diff --git a/modules/nixos-hardware/dell/xps/15-9550/README.wiki b/modules/nixos-hardware/dell/xps/15-9550/README.wiki
new file mode 100644
index 000000000000..c0e9158a883e
--- /dev/null
+++ b/modules/nixos-hardware/dell/xps/15-9550/README.wiki
@@ -0,0 +1,52 @@
+= Dell XPS 15 9550 =
+
+Most of this I presume also applies to the XPS 13 1530, the 13" variant.
+
+== Tested Hardware ==
+
+* CPU: Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz
+* RAM: 16 GB
+* HDD: 512 GiB SSD
+* Screen: 15" 4k (3840✕2160)
+* Graphics: NVIDIA Corporation GM107M, with Intel Graphics too.
+* Input: Touchscreen and trackpad.
+
+
+== Firmware Configuration ==
+
+Not much tweaking of NixOS itself was needed. But we currently cannot automate the firmware setup, so this must be done by hand.
+
+=== Before installation ===
+
+These settings are needed both for booting the final install, and installer itself. Therefore, they must be done first.
+
+* ''Disable Secure Boot (but keep UEFI Boot).'' Thakfully doing so is as easy as changing any other simple setting.
+
+* ''Disable Intel hardware RAID and use AHCI instead.'' Intel doesn't seem to provide a working linux driver for this.  (If you just have SSD it's pointless and just slows things down needlessly anyways.)
+
+
+=== After installation ===
+
+* ''Add systemd-boot to UEFI boot list.'' The (uneditable anyways) settings mapping drive UUIDs to HD* work fine.
+
+=== Optional ===
+
+* ''Disable C-States.'' This is a processor idling thing. It seems to cause random crashes (Blank screen, no normal panic debug dump). Unfortunately, without it, the computer cannot be suspended. On the other hand, it doesn't seem to affect acpi's estimation of battery life when the computer is running with minimal load, but I haven't tested battery life in practice. I list it as optional as there's a tradeoff, and the crashes are rare enough one can probably get through installation just fine.
+
+* ''Update BIOS.'' According to Reddit, this helps with battery life.
+
+* ''Update Intel's Thunderbolt firmware.'' Without this, the Thunderbolt port will only work as power source, and not transfer data.
+
+=== Troubleshooting ===
+
+==== rcu_sched freezing problems ====
+
+After a recent update my machine became unstable. X couldn't start and even running `lspci` would lock the machine. I'm not sure what the root cause was, but I found the fix here: https://wiki.archlinux.org/index.php/Dell_XPS_15_9560#Troubleshooting
+
+TL;DR I added this line:
+
+```
+boot.kernelParams = [ "acpi_rev_override=1" "pcie_aspm=off" "nouveau.modeset=0" ];
+```
+
+Some more detail about the problem can be found here: https://bbs.archlinux.org/viewtopic.php?id=223056
diff --git a/modules/nixos-hardware/dell/xps/15-9550/default.nix b/modules/nixos-hardware/dell/xps/15-9550/default.nix
new file mode 100644
index 000000000000..9677972a6b70
--- /dev/null
+++ b/modules/nixos-hardware/dell/xps/15-9550/default.nix
@@ -0,0 +1,18 @@
+{ lib, ... }:
+
+{
+  imports = [
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop
+  ];
+
+  # TODO: boot loader
+  boot.loader.systemd-boot.enable = lib.mkDefault true;
+  boot.loader.efi.canTouchEfiVariables = lib.mkDefault true;
+
+  # This will save you money and possibly your life!
+  services.thermald.enable = true;
+
+  # To just use Intel integrated graphics with Intel's open source driver
+  # hardware.nvidiaOptimus.disable = true;
+}
diff --git a/modules/nixos-hardware/dell/xps/15-9560/README.wiki b/modules/nixos-hardware/dell/xps/15-9560/README.wiki
new file mode 100644
index 000000000000..faf4ced8bec2
--- /dev/null
+++ b/modules/nixos-hardware/dell/xps/15-9560/README.wiki
@@ -0,0 +1,54 @@
+= Dell XPS 15 9560 =
+
+== Tested Hardware ==
+
+* CPU: Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
+* RAM: 16 GB
+* HDD: 512 GiB SSD
+* Screen: 15.6" FHD(1920x1080) InfinityEdge
+* Graphics: NVIDIA Corporation GTX1050 4GB GDDR5, with Intel Graphics too.
+* Input: trackpad
+
+
+== Firmware Configuration ==
+
+Not much tweaking of NixOS itself was needed. But we currently cannot automate the firmware setup, so this must be done by hand.
+
+=== Before installation ===
+
+These settings are needed both for booting the final install, and installer itself. Therefore, they must be done first.
+
+* ''Disable Secure Boot (but keep UEFI Boot).'' Thakfully doing so is as easy as changing any other simple setting.
+
+* ''Disable Intel hardware RAID and use AHCI instead.'' Intel doesn't seem to provide a working linux driver for this.  (If you just have SSD it's pointless and just slows things down needlessly anyways.)
+
+
+=== After installation ===
+
+* ''Add systemd-boot to UEFI boot list.'' The (uneditable anyways) settings mapping drive UUIDs to HD* work fine.
+
+=== Optional ===
+
+* ''Disable C-States.'' This is a processor idling thing. It seems to cause random crashes (Blank screen, no normal panic debug dump). Unfortunately, without it, the computer cannot be suspended. On the other hand, it doesn't seem to affect acpi's estimation of battery life when the computer is running with minimal load, but I haven't tested battery life in practice. I list it as optional as there's a tradeoff, and the crashes are rare enough one can probably get through installation just fine.
+
+* ''Update BIOS.'' According to Reddit, this helps with battery life.
+
+* ''Update Intel's Thunderbolt firmware.'' Without this, the Thunderbolt port will only work as power source, and not transfer data.
+
+=== Troubleshooting ===
+
+==== rcu_sched freezing problems ====
+
+After a recent update my machine became unstable. X couldn't start and even running `lspci` would lock the machine. I'm not sure what the root cause was, but I found the fix here: https://wiki.archlinux.org/index.php/Dell_XPS_15_9560#Troubleshooting
+
+TL;DR I added this line:
+  I am leaving these params here, I haven't test these so use on your own risk.
+  I am using different method to disable nvidia.
+  ("acpi_rev_override=1" param is save and I have been using it for a while now,
+  no crashes.)
+
+```
+boot.kernelParams = [ "acpi_rev_override=1" "pcie_aspm=off" "nouveau.modeset=0" ];
+```
+
+Some more detail about the problem can be found here: https://bbs.archlinux.org/viewtopic.php?id=223056
diff --git a/modules/nixos-hardware/dell/xps/15-9560/default.nix b/modules/nixos-hardware/dell/xps/15-9560/default.nix
new file mode 100644
index 000000000000..e7ec6c29d53e
--- /dev/null
+++ b/modules/nixos-hardware/dell/xps/15-9560/default.nix
@@ -0,0 +1,24 @@
+{ lib, pkgs, ... }:
+
+{
+  imports = [
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop
+    ./xps-common.nix
+  ];
+
+  # This configuration makes intel default and optionaly applications could run nvidia with optirun.
+  # To Optimize for your use case import intel or nvidia only configuration instead
+  # xps-9560/intel
+  # or
+  # xps-9560/nvidia
+
+
+ ##### bumblebee working, needs reboot to take affect and to use it run: optirun "<application>"
+ services.xserver.videoDrivers = lib.mkDefault [ "intel" "nvidia" ];
+ boot.blacklistedKernelModules = lib.mkDefault [ "nouveau" "bbswitch" ];
+ boot.extraModulePackages = lib.mkDefault [ pkgs.linuxPackages.nvidia_x11 ];
+ hardware.bumblebee.enable = lib.mkDefault true;
+ hardware.bumblebee.pmMethod = lib.mkDefault "none";
+
+}
diff --git a/modules/nixos-hardware/dell/xps/15-9560/intel/default.nix b/modules/nixos-hardware/dell/xps/15-9560/intel/default.nix
new file mode 100644
index 000000000000..f20379786ddb
--- /dev/null
+++ b/modules/nixos-hardware/dell/xps/15-9560/intel/default.nix
@@ -0,0 +1,18 @@
+{ lib, pkgs, ... }:
+
+{
+  imports = [
+    ../../../../common/cpu/intel
+    ../../../../common/pc/laptop
+    ../xps-common.nix
+  ];
+
+
+  # This runs only Intel and nvidia does not drain power.
+
+  ##### disable nvidia, very nice battery life.
+  hardware.nvidiaOptimus.disable = lib.mkDefault true;
+  boot.blacklistedKernelModules = lib.mkDefault [ "nouveau" "nvidia" ];
+  services.xserver.videoDrivers = lib.mkDefault [ "intel" ];
+
+}
diff --git a/modules/nixos-hardware/dell/xps/15-9560/nvidia/default.nix b/modules/nixos-hardware/dell/xps/15-9560/nvidia/default.nix
new file mode 100644
index 000000000000..2cd5d1b158dc
--- /dev/null
+++ b/modules/nixos-hardware/dell/xps/15-9560/nvidia/default.nix
@@ -0,0 +1,20 @@
+{ lib, pkgs, ... }:
+
+{
+  imports = [
+    ../../../../common/cpu/intel
+    ../../../../common/pc/laptop
+    ../xps-common.nix
+  ];
+
+
+  # This runs only nvidia, great for games or heavy use of render applications
+
+  ##### disable intel, run nvidia only and as default
+  services.xserver.videoDrivers = lib.mkDefault ["nvidia"];
+  hardware.nvidia.modesetting.enable = lib.mkDefault true;
+  hardware.nvidia.optimus_prime.enable = lib.mkDefault true;
+  hardware.nvidia.optimus_prime.nvidiaBusId = lib.mkDefault "PCI:1:0:0";
+  hardware.nvidia.optimus_prime.intelBusId = lib.mkDefault "PCI:0:2:0";
+
+}
diff --git a/modules/nixos-hardware/dell/xps/15-9560/xps-common.nix b/modules/nixos-hardware/dell/xps/15-9560/xps-common.nix
new file mode 100644
index 000000000000..62aae078545d
--- /dev/null
+++ b/modules/nixos-hardware/dell/xps/15-9560/xps-common.nix
@@ -0,0 +1,12 @@
+{ lib, ... }:
+
+{
+
+  # Boot loader
+  boot.loader.systemd-boot.enable = lib.mkDefault true;
+  boot.loader.efi.canTouchEfiVariables = lib.mkDefault true;
+  boot.kernelParams = lib.mkDefault [ "acpi_rev_override" ];
+
+  # This will save you money and possibly your life!
+  services.thermald.enable = lib.mkDefault true;
+}
diff --git a/modules/nixos-hardware/inversepath/usbarmory/README.txt b/modules/nixos-hardware/inversepath/usbarmory/README.txt
new file mode 100644
index 000000000000..31922a8d7519
--- /dev/null
+++ b/modules/nixos-hardware/inversepath/usbarmory/README.txt
@@ -0,0 +1,6 @@
+USB Armory network interface support
+
+- rename the Armory USB network interface
+- set Armory inteface ip to 10.0.0.2/24
+- enable NAT and forward Armory interface
+- add the name 'armory' to /etc/hosts
diff --git a/modules/nixos-hardware/inversepath/usbarmory/default.nix b/modules/nixos-hardware/inversepath/usbarmory/default.nix
new file mode 100644
index 000000000000..5c329f8286d3
--- /dev/null
+++ b/modules/nixos-hardware/inversepath/usbarmory/default.nix
@@ -0,0 +1,46 @@
+{ config, lib, pkgs, ... }:
+
+{
+  boot = {
+    extraModprobeConfig = lib.mkDefault ''
+      options g_ether use_eem=0 dev_addr=1a:55:89:a2:69:41 host_addr=1a:55:89:a2:69:42
+    '';
+
+    kernelModules = [ "ledtrig_heartbeat" "ci_hdrc_imx" "g_ether" ];
+    kernelPackages = lib.mkDefault pkgs.linuxPackages_usbarmory;
+    kernelParams =  [ "console=ttymxc0,115200" ];
+
+    loader.generic-extlinux-compatible.enable = lib.mkDefault true;
+  };
+
+  networking = {
+    defaultGateway = "172.16.0.1";
+    firewall.enable = lib.mkDefault false;
+    hostName = "usbarmory";
+
+    interfaces.usb0.ip4 = [
+      { address = "172.16.0.2"; prefixLength = 24; }
+    ];
+
+    nameservers = [ "8.8.8.8" ];
+  };
+
+  nix = {
+    binaryCaches = [ "http://nixos-arm.dezgeg.me/channel" ];
+    binaryCachePublicKeys = [ "nixos-arm.dezgeg.me-1:xBaUKS3n17BZPKeyxL4JfbTqECsT+ysbDJz29kLFRW0=%" ];
+  };
+
+  nixpkgs.overlays = [(final: previous: {
+    linuxPackages_usbarmory = final.recurseIntoAttrs
+      (final.linuxPackagesFor (import ./kernel.nix {
+        inherit (final) stdenv buildLinux fetchurl;
+      }));
+  })];
+
+  sound.enable = lib.mkDefault false;
+
+  services = {
+    openssh.enable = lib.mkDefault true;
+    openssh.permitRootLogin = lib.mkDefault "without-password";
+  };
+}
diff --git a/modules/nixos-hardware/inversepath/usbarmory/host.nix b/modules/nixos-hardware/inversepath/usbarmory/host.nix
new file mode 100644
index 000000000000..42cef490c6f8
--- /dev/null
+++ b/modules/nixos-hardware/inversepath/usbarmory/host.nix
@@ -0,0 +1,19 @@
+{ dev ? "armory0" }:
+
+{
+  services.udev.extraRules = ''
+    SUBSYSTEM=="net", ACTION=="add", ATTRS{idVendor}=="0525", ATTRS{idProduct}=="a4a2", NAME="${staticDevName}"
+  '';
+
+  networking = {
+    interfaces."${staticDevName}".ip4 = [{
+      address = "10.0.0.2";
+      prefixLength = 24;
+    }];
+    nat = {
+      enable = true;
+      internalInterfaces = [ dev ];
+    };
+    extraHosts = "10.0.0.1 armory";
+  };
+}
diff --git a/modules/nixos-hardware/inversepath/usbarmory/kernel.config b/modules/nixos-hardware/inversepath/usbarmory/kernel.config
new file mode 100644
index 000000000000..e2fa29737937
--- /dev/null
+++ b/modules/nixos-hardware/inversepath/usbarmory/kernel.config
@@ -0,0 +1,995 @@
+CONFIG_ARM=y
+CONFIG_ARM_HAS_SG_CHAIN=y
+CONFIG_MIGHT_HAVE_PCI=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_HAVE_PROC_CPU=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_FIX_EARLYCON_MEM=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_ARCH_SUPPORTS_UPROBES=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_ARM_PATCH_PHYS_VIRT=y
+CONFIG_GENERIC_BUG=y
+CONFIG_PGTABLE_LEVELS=2
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_IRQ_WORK=y
+CONFIG_BUILDTIME_EXTABLE_SORT=y
+CONFIG_DMIID=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_CROSS_COMPILE=""
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_HAVE_KERNEL_XZ=y
+CONFIG_HAVE_KERNEL_LZO=y
+CONFIG_HAVE_KERNEL_LZ4=y
+CONFIG_KERNEL_GZIP=y
+CONFIG_DEFAULT_HOSTNAME="usbarmory"
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_CROSS_MEMORY_ATTACH=y
+CONFIG_FHANDLE=y
+CONFIG_USELIB=y
+CONFIG_AUDIT=y
+CONFIG_HAVE_ARCH_AUDITSYSCALL=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_GENERIC_IRQ_SHOW_LEVEL=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_CHIP=y
+CONFIG_IRQ_DOMAIN=y
+CONFIG_HANDLE_DOMAIN_IRQ=y
+CONFIG_IRQ_FORCED_THREADING=y
+CONFIG_SPARSE_IRQ=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ_COMMON=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_TICK_CPU_ACCOUNTING=y
+CONFIG_PREEMPT_RCU=y
+CONFIG_SRCU=y
+CONFIG_RCU_STALL_COMMON=y
+CONFIG_BUILD_BIN2C=y
+CONFIG_IKCONFIG=m
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_GENERIC_SCHED_CLOCK=y
+CONFIG_CGROUPS=y
+CONFIG_NAMESPACES=y
+CONFIG_UTS_NS=y
+CONFIG_IPC_NS=y
+CONFIG_PID_NS=y
+CONFIG_NET_NS=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_RD_GZIP=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+CONFIG_HAVE_UID16=y
+CONFIG_BPF=y
+CONFIG_EXPERT=y
+CONFIG_UID16=y
+CONFIG_MULTIUSER=y
+CONFIG_SYSFS_SYSCALL=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+CONFIG_ADVISE_SYSCALLS=y
+CONFIG_MEMBARRIER=y
+CONFIG_EMBEDDED=y
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_PERF_USE_VMALLOC=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_COMPAT_BRK=y
+CONFIG_SLUB=y
+CONFIG_HAVE_OPROFILE=y
+CONFIG_JUMP_LABEL=y
+CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
+CONFIG_ARCH_USE_BUILTIN_BSWAP=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_OPTPROBES=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_DMA_ATTRS=y
+CONFIG_HAVE_DMA_CONTIGUOUS=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_GENERIC_IDLE_POLL_SETUP=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_PERF_REGS=y
+CONFIG_HAVE_PERF_USER_STACK_DUMP=y
+CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
+CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
+CONFIG_SECCOMP_FILTER=y
+CONFIG_HAVE_CC_STACKPROTECTOR=y
+CONFIG_CC_STACKPROTECTOR_NONE=y
+CONFIG_HAVE_CONTEXT_TRACKING=y
+CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
+CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
+CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
+CONFIG_MODULES_USE_ELF_REL=y
+CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
+CONFIG_CLONE_BACKWARDS=y
+CONFIG_OLD_SIGSUSPEND3=y
+CONFIG_OLD_SIGACTION=y
+CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_BLOCK=y
+CONFIG_LBDAF=y
+CONFIG_BLK_CMDLINE_PARSER=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_MSDOS_PARTITION=y
+CONFIG_EFI_PARTITION=y
+CONFIG_EFI_STUB=y
+CONFIG_CMDLINE_PARTITION=y
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_DEADLINE=m
+CONFIG_IOSCHED_CFQ=m
+CONFIG_DEFAULT_NOOP=y
+CONFIG_DEFAULT_IOSCHED="noop"
+CONFIG_ASN1=m
+CONFIG_UNINLINE_SPIN_UNLOCK=y
+CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
+CONFIG_FREEZER=y
+CONFIG_MMU=y
+CONFIG_ARCH_MULTIPLATFORM=y
+CONFIG_ARCH_MULTI_V7=y
+CONFIG_ARCH_MULTI_V6_V7=y
+CONFIG_ARCH_MXC=y
+CONFIG_MXC_TZIC=y
+CONFIG_HAVE_IMX_SRC=y
+CONFIG_SOC_IMX5=y
+CONFIG_SOC_IMX53=y
+CONFIG_CPU_V7=y
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_32v7=y
+CONFIG_CPU_ABRT_EV7=y
+CONFIG_CPU_PABRT_V7=y
+CONFIG_CPU_CACHE_V7=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_TLB_V7=y
+CONFIG_CPU_HAS_ASID=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+CONFIG_ARM_THUMB=y
+CONFIG_ARM_VIRT_EXT=y
+CONFIG_KUSER_HELPERS=y
+CONFIG_OUTER_CACHE=y
+CONFIG_OUTER_CACHE_SYNC=y
+CONFIG_MIGHT_HAVE_CACHE_L2X0=y
+CONFIG_CACHE_L2X0=y
+CONFIG_ARM_L1_CACHE_SHIFT_6=y
+CONFIG_ARM_L1_CACHE_SHIFT=6
+CONFIG_ARM_DMA_MEM_BUFFERABLE=y
+CONFIG_ARM_HEAVY_MB=y
+CONFIG_MULTI_IRQ_HANDLER=y
+CONFIG_HAVE_SMP=y
+CONFIG_VMSPLIT_2G=y
+CONFIG_PAGE_OFFSET=0x80000000
+CONFIG_ARCH_NR_GPIO=0
+CONFIG_PREEMPT=y
+CONFIG_PREEMPT_COUNT=y
+CONFIG_HZ_FIXED=0
+CONFIG_HZ_100=y
+CONFIG_HZ=100
+CONFIG_SCHED_HRTICK=y
+CONFIG_AEABI=y
+CONFIG_HAVE_ARCH_PFN_VALID=y
+CONFIG_CPU_SW_DOMAIN_PAN=y
+CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_HAVE_MEMBLOCK=y
+CONFIG_NO_BOOTMEM=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+CONFIG_COMPACTION=y
+CONFIG_MIGRATION=y
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_KSM=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+CONFIG_NEED_PER_CPU_KM=y
+CONFIG_CLEANCACHE=y
+CONFIG_ZPOOL=m
+CONFIG_ZBUD=m
+CONFIG_ZSMALLOC=m
+CONFIG_FORCE_MAX_ZONEORDER=11
+CONFIG_ALIGNMENT_TRAP=y
+CONFIG_SECCOMP=y
+CONFIG_SWIOTLB=y
+CONFIG_IOMMU_HELPER=y
+CONFIG_USE_OF=y
+CONFIG_ATAGS=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="noinitrd console=ttymxc0,115200 root=/dev/mmcblk0p1 rw rootfstype=ext4 ip=off"
+CONFIG_CMDLINE_FROM_BOOTLOADER=y
+CONFIG_AUTO_ZRELADDR=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_GOV_COMMON=y
+CONFIG_CPU_FREQ_STAT=m
+CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=m
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
+CONFIG_CPUFREQ_DT=y
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_CPU_IDLE_GOV_MENU=y
+CONFIG_VFP=y
+CONFIG_VFPv3=y
+CONFIG_NEON=y
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_SCRIPT=y
+CONFIG_COREDUMP=y
+CONFIG_HIBERNATE_CALLBACKS=y
+CONFIG_HIBERNATION=y
+CONFIG_PM_STD_PARTITION=""
+CONFIG_PM_SLEEP=y
+CONFIG_PM=y
+CONFIG_PM_OPP=y
+CONFIG_PM_CLK=y
+CONFIG_CPU_PM=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARM_CPU_SUSPEND=y
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+CONFIG_NET=y
+CONFIG_NET_INGRESS=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+CONFIG_XFRM_ALGO=m
+CONFIG_XFRM_USER=m
+CONFIG_XFRM_SUB_POLICY=y
+CONFIG_XFRM_MIGRATE=y
+CONFIG_XFRM_STATISTICS=y
+CONFIG_XFRM_IPCOMP=m
+CONFIG_NET_KEY=m
+CONFIG_NET_KEY_MIGRATE=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE_DEMUX=m
+CONFIG_NET_IP_TUNNEL=m
+CONFIG_NET_IPGRE=m
+CONFIG_NET_IPGRE_BROADCAST=y
+CONFIG_SYN_COOKIES=y
+CONFIG_NET_IPVTI=m
+CONFIG_NET_UDP_TUNNEL=m
+CONFIG_NET_FOU=m
+CONFIG_NET_FOU_IP_TUNNELS=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_XFRM_TUNNEL=m
+CONFIG_INET_TUNNEL=m
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+CONFIG_INET_UDP_DIAG=m
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_BIC=m
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_TCP_CONG_WESTWOOD=m
+CONFIG_TCP_CONG_HTCP=m
+CONFIG_TCP_CONG_HSTCP=m
+CONFIG_TCP_CONG_HYBLA=m
+CONFIG_TCP_CONG_VEGAS=m
+CONFIG_TCP_CONG_SCALABLE=m
+CONFIG_TCP_CONG_LP=m
+CONFIG_TCP_CONG_VENO=m
+CONFIG_TCP_CONG_YEAH=m
+CONFIG_TCP_CONG_ILLINOIS=m
+CONFIG_DEFAULT_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+CONFIG_TCP_MD5SIG=y
+CONFIG_IPV6=m
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_IPV6_MIP6=m
+CONFIG_INET6_XFRM_TUNNEL=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=m
+CONFIG_IPV6_VTI=m
+CONFIG_IPV6_SIT=m
+CONFIG_IPV6_SIT_6RD=y
+CONFIG_IPV6_NDISC_NODETYPE=y
+CONFIG_IPV6_TUNNEL=m
+CONFIG_IPV6_GRE=m
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_IPV6_MROUTE=y
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_ADVANCED=y
+CONFIG_BRIDGE_NETFILTER=m
+CONFIG_NETFILTER_INGRESS=y
+CONFIG_NETFILTER_NETLINK=m
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_COMMON=m
+CONFIG_NF_CONNTRACK_MARK=y
+CONFIG_NF_CONNTRACK_PROCFS=y
+CONFIG_NF_CT_PROTO_GRE=m
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_H323=m
+CONFIG_NF_CONNTRACK_IRC=m
+CONFIG_NF_CONNTRACK_BROADCAST=m
+CONFIG_NF_CONNTRACK_NETBIOS_NS=m
+CONFIG_NF_CONNTRACK_SNMP=m
+CONFIG_NF_CONNTRACK_PPTP=m
+CONFIG_NF_CONNTRACK_SANE=m
+CONFIG_NF_CONNTRACK_SIP=m
+CONFIG_NF_CONNTRACK_TFTP=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NF_CT_NETLINK_TIMEOUT=m
+CONFIG_NF_NAT=m
+CONFIG_NF_NAT_NEEDED=y
+CONFIG_NF_NAT_FTP=m
+CONFIG_NF_NAT_IRC=m
+CONFIG_NF_NAT_SIP=m
+CONFIG_NF_NAT_TFTP=m
+CONFIG_NF_NAT_REDIRECT=m
+CONFIG_NETFILTER_SYNPROXY=m
+CONFIG_NETFILTER_XTABLES=m
+CONFIG_NETFILTER_XT_TARGET_HL=m
+CONFIG_NETFILTER_XT_TARGET_LED=m
+CONFIG_NETFILTER_XT_NAT=m
+CONFIG_NETFILTER_XT_TARGET_NETMAP=m
+CONFIG_NETFILTER_XT_TARGET_REDIRECT=m
+CONFIG_NETFILTER_XT_TARGET_TEE=m
+CONFIG_NETFILTER_XT_TARGET_TPROXY=m
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_BPF=m
+CONFIG_NETFILTER_XT_MATCH_CGROUP=m
+CONFIG_NETFILTER_XT_MATCH_ECN=m
+CONFIG_NETFILTER_XT_MATCH_ESP=m
+CONFIG_NETFILTER_XT_MATCH_HL=m
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
+CONFIG_NETFILTER_XT_MATCH_LENGTH=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_OWNER=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
+CONFIG_NETFILTER_XT_MATCH_STATE=m
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
+CONFIG_NETFILTER_XT_MATCH_STRING=m
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_TIME=m
+CONFIG_NETFILTER_XT_MATCH_U32=m
+CONFIG_NF_DEFRAG_IPV4=m
+CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_NF_CONNTRACK_PROC_COMPAT=y
+CONFIG_NF_DUP_IPV4=m
+CONFIG_NF_LOG_ARP=m
+CONFIG_NF_LOG_IPV4=m
+CONFIG_NF_REJECT_IPV4=m
+CONFIG_NF_NAT_IPV4=m
+CONFIG_NF_NAT_MASQUERADE_IPV4=m
+CONFIG_NF_NAT_SNMP_BASIC=m
+CONFIG_NF_NAT_PROTO_GRE=m
+CONFIG_NF_NAT_PPTP=m
+CONFIG_NF_NAT_H323=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_RPFILTER=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_SYNPROXY=m
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_NETMAP=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_CLUSTERIP=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_NF_DEFRAG_IPV6=m
+CONFIG_NF_CONNTRACK_IPV6=m
+CONFIG_NF_DUP_IPV6=m
+CONFIG_NF_REJECT_IPV6=m
+CONFIG_NF_LOG_IPV6=m
+CONFIG_NF_NAT_IPV6=m
+CONFIG_NF_NAT_MASQUERADE_IPV6=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_AH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_MH=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_REJECT=m
+CONFIG_IP6_NF_TARGET_SYNPROXY=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_IP6_NF_NAT=m
+CONFIG_IP6_NF_TARGET_MASQUERADE=m
+CONFIG_IP6_NF_TARGET_NPT=m
+CONFIG_L2TP=m
+CONFIG_STP=m
+CONFIG_BRIDGE=m
+CONFIG_BRIDGE_IGMP_SNOOPING=y
+CONFIG_HAVE_NET_DSA=y
+CONFIG_VLAN_8021Q=m
+CONFIG_LLC=m
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_RED=m
+CONFIG_NET_SCH_NETEM=m
+CONFIG_NET_CLS=y
+CONFIG_NET_CLS_FW=m
+CONFIG_NET_CLS_CGROUP=m
+CONFIG_NET_CLS_BPF=m
+CONFIG_NET_SCH_FIFO=y
+CONFIG_CGROUP_NET_CLASSID=y
+CONFIG_NET_RX_BUSY_POLL=y
+CONFIG_BQL=y
+CONFIG_BPF_JIT=y
+CONFIG_FIB_RULES=y
+CONFIG_WIRELESS=y
+CONFIG_WEXT_CORE=y
+CONFIG_WEXT_PROC=y
+CONFIG_CFG80211=m
+CONFIG_CFG80211_DEFAULT_PS=y
+CONFIG_CFG80211_CRDA_SUPPORT=y
+CONFIG_CFG80211_WEXT=y
+CONFIG_MAC80211=m
+CONFIG_MAC80211_HAS_RC=y
+CONFIG_MAC80211_RC_MINSTREL=y
+CONFIG_MAC80211_RC_MINSTREL_HT=y
+CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y
+CONFIG_MAC80211_RC_DEFAULT="minstrel_ht"
+CONFIG_MAC80211_STA_HASH_MAX_SIZE=0
+CONFIG_HAVE_BPF_JIT=y
+CONFIG_UEVENT_HELPER=y
+CONFIG_UEVENT_HELPER_PATH=""
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_FW_LOADER=m
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+CONFIG_SOC_BUS=y
+CONFIG_REGMAP=y
+CONFIG_REGMAP_I2C=m
+CONFIG_REGMAP_MMIO=m
+CONFIG_MTD=m
+CONFIG_MTD_OF_PARTS=m
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+CONFIG_MTD_M25P80=m
+CONFIG_MTD_BLOCK2MTD=m
+CONFIG_MTD_SPI_NOR=m
+CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=y
+CONFIG_DTC=y
+CONFIG_OF=y
+CONFIG_OF_FLATTREE=y
+CONFIG_OF_EARLY_FLATTREE=y
+CONFIG_OF_ADDRESS=y
+CONFIG_OF_IRQ=y
+CONFIG_OF_NET=y
+CONFIG_OF_MTD=y
+CONFIG_OF_RESERVED_MEM=y
+CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
+CONFIG_BLK_DEV=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=8
+CONFIG_SRAM=y
+CONFIG_EEPROM_93CX6=m
+CONFIG_SCSI_MOD=m
+CONFIG_SCSI=m
+CONFIG_SCSI_DMA=y
+CONFIG_SCSI_PROC_FS=y
+CONFIG_BLK_DEV_SD=m
+CONFIG_SCSI_LOWLEVEL=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM_BUILTIN=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_BUFIO=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_VERITY=y
+CONFIG_NETDEVICES=y
+CONFIG_NET_CORE=y
+CONFIG_DUMMY=m
+CONFIG_VXLAN=m
+CONFIG_TUN=m
+CONFIG_VETH=m
+CONFIG_PPP=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MPPE=m
+CONFIG_PPPOE=m
+CONFIG_PPPOL2TP=m
+CONFIG_SLHC=m
+CONFIG_USB_NET_DRIVERS=m
+CONFIG_WLAN=y
+CONFIG_RTL8187=m
+CONFIG_RT2X00=m
+CONFIG_RT2500USB=m
+CONFIG_RT73USB=m
+CONFIG_RT2800USB=m
+CONFIG_RT2800USB_RT33XX=y
+CONFIG_RT2800USB_RT35XX=y
+CONFIG_RT2800USB_RT3573=y
+CONFIG_RT2800USB_RT53XX=y
+CONFIG_RT2800USB_RT55XX=y
+CONFIG_RT2800USB_UNKNOWN=y
+CONFIG_RT2800_LIB=m
+CONFIG_RT2X00_LIB_USB=m
+CONFIG_RT2X00_LIB=m
+CONFIG_RT2X00_LIB_FIRMWARE=y
+CONFIG_RT2X00_LIB_CRYPTO=y
+CONFIG_RT2X00_LIB_LEDS=y
+CONFIG_RT2X00_DEBUG=y
+CONFIG_RTL_CARDS=m
+CONFIG_RTL8192CU=m
+CONFIG_RTLWIFI=m
+CONFIG_RTLWIFI_USB=m
+CONFIG_RTLWIFI_DEBUG=y
+CONFIG_RTL8192C_COMMON=m
+CONFIG_RTL8XXXU=m
+CONFIG_INPUT=y
+CONFIG_INPUT_LEDS=m
+CONFIG_INPUT_MOUSEDEV=m
+CONFIG_INPUT_MOUSEDEV_PSAUX=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+CONFIG_INPUT_EVDEV=m
+CONFIG_INPUT_KEYBOARD=y
+CONFIG_KEYBOARD_ATKBD=y
+CONFIG_INPUT_MOUSE=y
+CONFIG_MOUSE_PS2=m
+CONFIG_MOUSE_PS2_FOCALTECH=y
+CONFIG_MOUSE_SERIAL=m
+CONFIG_MOUSE_SYNAPTICS_USB=m
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_PROPERTIES=y
+CONFIG_TOUCHSCREEN_USB_COMPOSITE=m
+CONFIG_TOUCHSCREEN_USB_E2I=y
+CONFIG_SERIO=y
+CONFIG_SERIO_SERPORT=y
+CONFIG_SERIO_LIBPS2=y
+CONFIG_TTY=y
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_VT_CONSOLE_SLEEP=y
+CONFIG_HW_CONSOLE=y
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+CONFIG_DEVMEM=y
+CONFIG_DEVKMEM=y
+CONFIG_SERIAL_EARLYCON=y
+CONFIG_SERIAL_IMX=y
+CONFIG_SERIAL_IMX_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_HW_RANDOM=m
+CONFIG_I2C=m
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=m
+CONFIG_I2C_HELPER_AUTO=y
+CONFIG_I2C_IMX=m
+CONFIG_SPI=y
+CONFIG_SPI_DEBUG=y
+CONFIG_SPI_MASTER=y
+CONFIG_SPI_BITBANG=m
+CONFIG_SPI_IMX=m
+CONFIG_SPI_SPIDEV=m
+CONFIG_PPS=y
+CONFIG_PINCTRL=y
+CONFIG_PINMUX=y
+CONFIG_PINCONF=y
+CONFIG_PINCTRL_IMX=y
+CONFIG_PINCTRL_IMX53=y
+CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
+CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_DEVRES=y
+CONFIG_OF_GPIO=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_GENERIC=y
+CONFIG_GPIO_GENERIC_PLATFORM=y
+CONFIG_GPIO_MXC=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_POWER_AVS=y
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_CORE=y
+CONFIG_SOFT_WATCHDOG=m
+CONFIG_GPIO_WATCHDOG=m
+CONFIG_IMX2_WDT=m
+CONFIG_SSB_POSSIBLE=y
+CONFIG_BCMA_POSSIBLE=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_LTC3589=m
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_HID=y
+CONFIG_HID_GENERIC=m
+CONFIG_USB_HID=m
+CONFIG_USB_HIDDEV=y
+CONFIG_USB_KBD=m
+CONFIG_USB_MOUSE=m
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_COMMON=m
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB=m
+CONFIG_USB_DEFAULT_PERSIST=y
+CONFIG_USB_OTG=y
+CONFIG_USB_EHCI_HCD=m
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_EHCI_TT_NEWSCHED=y
+CONFIG_USB_STORAGE=m
+CONFIG_USB_CHIPIDEA=m
+CONFIG_USB_CHIPIDEA_OF=m
+CONFIG_USB_CHIPIDEA_UDC=y
+CONFIG_USB_CHIPIDEA_HOST=y
+CONFIG_USB_CHIPIDEA_DEBUG=y
+CONFIG_USB_PHY=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_USB_GADGET=m
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2
+CONFIG_USB_LIBCOMPOSITE=m
+CONFIG_USB_F_ACM=m
+CONFIG_USB_F_SS_LB=m
+CONFIG_USB_U_SERIAL=m
+CONFIG_USB_U_ETHER=m
+CONFIG_USB_F_SERIAL=m
+CONFIG_USB_F_OBEX=m
+CONFIG_USB_F_NCM=m
+CONFIG_USB_F_ECM=m
+CONFIG_USB_F_EEM=m
+CONFIG_USB_F_SUBSET=m
+CONFIG_USB_F_RNDIS=m
+CONFIG_USB_F_MASS_STORAGE=m
+CONFIG_USB_F_FS=m
+CONFIG_USB_F_HID=m
+CONFIG_USB_F_PRINTER=m
+CONFIG_USB_CONFIGFS=m
+CONFIG_USB_CONFIGFS_SERIAL=y
+CONFIG_USB_CONFIGFS_ACM=y
+CONFIG_USB_CONFIGFS_OBEX=y
+CONFIG_USB_CONFIGFS_NCM=y
+CONFIG_USB_CONFIGFS_ECM=y
+CONFIG_USB_CONFIGFS_ECM_SUBSET=y
+CONFIG_USB_CONFIGFS_RNDIS=y
+CONFIG_USB_CONFIGFS_EEM=y
+CONFIG_USB_CONFIGFS_MASS_STORAGE=y
+CONFIG_USB_CONFIGFS_F_LB_SS=y
+CONFIG_USB_CONFIGFS_F_FS=y
+CONFIG_USB_CONFIGFS_F_HID=y
+CONFIG_USB_CONFIGFS_F_PRINTER=y
+CONFIG_USB_ZERO=m
+CONFIG_USB_ETH=m
+CONFIG_USB_ETH_RNDIS=y
+CONFIG_USB_ETH_EEM=y
+CONFIG_USB_G_NCM=m
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_FUNCTIONFS=m
+CONFIG_USB_FUNCTIONFS_ETH=y
+CONFIG_USB_FUNCTIONFS_RNDIS=y
+CONFIG_USB_FUNCTIONFS_GENERIC=y
+CONFIG_USB_MASS_STORAGE=m
+CONFIG_USB_G_SERIAL=m
+CONFIG_USB_G_PRINTER=m
+CONFIG_USB_CDC_COMPOSITE=m
+CONFIG_USB_G_ACM_MS=m
+CONFIG_USB_G_MULTI=m
+CONFIG_USB_G_MULTI_RNDIS=y
+CONFIG_USB_G_MULTI_CDC=y
+CONFIG_USB_G_HID=m
+CONFIG_MMC=y
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_MINORS=8
+CONFIG_MMC_BLOCK_BOUNCE=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_IO_ACCESSORS=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_ESDHC_IMX=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=m
+CONFIG_LEDS_GPIO=m
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=m
+CONFIG_LEDS_TRIGGER_ONESHOT=m
+CONFIG_LEDS_TRIGGER_HEARTBEAT=m
+CONFIG_LEDS_TRIGGER_BACKLIGHT=m
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_LEDS_TRIGGER_GPIO=m
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=m
+CONFIG_LEDS_TRIGGER_TRANSIENT=m
+CONFIG_EDAC_ATOMIC_SCRUB=y
+CONFIG_EDAC_SUPPORT=y
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+CONFIG_RTC_SYSTOHC=y
+CONFIG_RTC_SYSTOHC_DEVICE="rtc0"
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+CONFIG_RTC_INTF_DEV_UIE_EMUL=y
+CONFIG_RTC_DRV_IMXDI=y
+CONFIG_RTC_DRV_MXC=y
+CONFIG_DMADEVICES=y
+CONFIG_DMA_ENGINE=y
+CONFIG_DMA_OF=y
+CONFIG_IMX_SDMA=m
+CONFIG_CLKDEV_LOOKUP=y
+CONFIG_HAVE_CLK_PREPARE=y
+CONFIG_COMMON_CLK=y
+CONFIG_CLKSRC_OF=y
+CONFIG_CLKSRC_PROBE=y
+CONFIG_CLKSRC_MMIO=y
+CONFIG_CLKSRC_IMX_GPT=y
+CONFIG_IOMMU_SUPPORT=y
+CONFIG_EXTCON=m
+CONFIG_IRQCHIP=y
+CONFIG_ARCH_HAS_RESET_CONTROLLER=y
+CONFIG_DCACHE_WORD_ACCESS=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_USE_FOR_EXT2=y
+CONFIG_JBD2=y
+CONFIG_FS_MBCACHE=y
+CONFIG_EXPORTFS=y
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY_USER=y
+CONFIG_AUTOFS4_FS=y
+CONFIG_FUSE_FS=m
+CONFIG_OVERLAY_FS=y
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+CONFIG_NTFS_FS=m
+CONFIG_NTFS_RW=y
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_KERNFS=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_TMPFS_XATTR=y
+CONFIG_CONFIGFS_FS=y
+CONFIG_MISC_FILESYSTEMS=y
+CONFIG_ECRYPT_FS=m
+CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_JFFS2_FS=m
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_RTIME=y
+CONFIG_SQUASHFS=y
+CONFIG_SQUASHFS_FILE_DIRECT=y
+CONFIG_SQUASHFS_DECOMP_SINGLE=y
+CONFIG_SQUASHFS_ZLIB=y
+CONFIG_SQUASHFS_LZ4=y
+CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=m
+CONFIG_NFS_V2=m
+CONFIG_NFS_V3=m
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+CONFIG_GRACE_PERIOD=m
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=m
+CONFIG_CIFS=m
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_UTF8=y
+CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+CONFIG_DEBUG_FS=y
+CONFIG_SECTION_MISMATCH_WARN_ONLY=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_HAVE_DEBUG_KMEMLEAK=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=1
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=0
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=1
+CONFIG_PANIC_ON_OOPS_VALUE=0
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_SCHED_DEBUG=y
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_RCU_CPU_STALL_TIMEOUT=21
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_TRACING_SUPPORT=y
+CONFIG_LKDTM=m
+CONFIG_HAVE_ARCH_KGDB=y
+CONFIG_STRICT_DEVMEM=y
+CONFIG_ARM_UNWIND=y
+CONFIG_DEBUG_IMX_UART_PORT=1
+CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S"
+CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
+CONFIG_KEYS=y
+CONFIG_ENCRYPTED_KEYS=m
+CONFIG_SECURITY=y
+CONFIG_SECURITYFS=y
+CONFIG_INTEGRITY=y
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_DEFAULT_SECURITY=""
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+CONFIG_CRYPTO_AEAD=m
+CONFIG_CRYPTO_AEAD2=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_BLKCIPHER2=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_RNG=m
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_RNG_DEFAULT=m
+CONFIG_CRYPTO_PCOMP=m
+CONFIG_CRYPTO_PCOMP2=y
+CONFIG_CRYPTO_AKCIPHER2=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+CONFIG_CRYPTO_USER=m
+CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
+CONFIG_CRYPTO_GF128MUL=y
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_NULL2=y
+CONFIG_CRYPTO_WORKQUEUE=y
+CONFIG_CRYPTO_AUTHENC=m
+CONFIG_CRYPTO_TEST=m
+CONFIG_CRYPTO_CCM=m
+CONFIG_CRYPTO_GCM=m
+CONFIG_CRYPTO_SEQIV=m
+CONFIG_CRYPTO_ECHAINIV=m
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_CTR=m
+CONFIG_CRYPTO_ECB=y
+CONFIG_CRYPTO_XTS=y
+CONFIG_CRYPTO_CMAC=m
+CONFIG_CRYPTO_HMAC=m
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_GHASH=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_DES=m
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_ZLIB=m
+CONFIG_CRYPTO_LZO=m
+CONFIG_CRYPTO_ANSI_CPRNG=m
+CONFIG_CRYPTO_DRBG_MENU=m
+CONFIG_CRYPTO_DRBG_HMAC=y
+CONFIG_CRYPTO_DRBG_HASH=y
+CONFIG_CRYPTO_DRBG_CTR=y
+CONFIG_CRYPTO_DRBG=m
+CONFIG_CRYPTO_JITTERENTROPY=m
+CONFIG_CRYPTO_USER_API=m
+CONFIG_CRYPTO_USER_API_HASH=m
+CONFIG_CRYPTO_USER_API_SKCIPHER=m
+CONFIG_CRYPTO_USER_API_RNG=m
+CONFIG_CRYPTO_HASH_INFO=y
+CONFIG_CRYPTO_HW=y
+CONFIG_CRYPTO_DEV_SAHARA=y
+CONFIG_ASYMMETRIC_KEY_TYPE=m
+CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=m
+CONFIG_PUBLIC_KEY_ALGO_RSA=m
+CONFIG_X509_CERTIFICATE_PARSER=m
+CONFIG_PKCS7_MESSAGE_PARSER=m
+CONFIG_BITREVERSE=y
+CONFIG_HAVE_ARCH_BITREVERSE=y
+CONFIG_RATIONAL=y
+CONFIG_GENERIC_STRNCPY_FROM_USER=y
+CONFIG_GENERIC_STRNLEN_USER=y
+CONFIG_GENERIC_NET_UTILS=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_IO=y
+CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
+CONFIG_CRC_CCITT=m
+CONFIG_CRC16=y
+CONFIG_CRC_ITU_T=m
+CONFIG_CRC32=y
+CONFIG_CRC32_SLICEBY8=y
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_LZ4_DECOMPRESS=y
+CONFIG_DECOMPRESS_GZIP=y
+CONFIG_GENERIC_ALLOCATOR=y
+CONFIG_TEXTSEARCH=y
+CONFIG_TEXTSEARCH_KMP=m
+CONFIG_TEXTSEARCH_BM=m
+CONFIG_TEXTSEARCH_FSM=m
+CONFIG_ASSOCIATIVE_ARRAY=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT_MAP=y
+CONFIG_HAS_DMA=y
+CONFIG_DQL=y
+CONFIG_NLATTR=y
+CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
+CONFIG_CLZ_TAB=y
+CONFIG_MPILIB=m
+CONFIG_LIBFDT=y
+CONFIG_OID_REGISTRY=m
+CONFIG_ARCH_HAS_SG_CHAIN=y
diff --git a/modules/nixos-hardware/inversepath/usbarmory/kernel.nix b/modules/nixos-hardware/inversepath/usbarmory/kernel.nix
new file mode 100644
index 000000000000..4cf6fd7fdb8a
--- /dev/null
+++ b/modules/nixos-hardware/inversepath/usbarmory/kernel.nix
@@ -0,0 +1,20 @@
+{ stdenv, buildLinux, fetchurl }:
+
+buildLinux {
+  inherit stdenv;
+  version = "4.4.0";
+
+  src = fetchurl {
+    url = "mirror://kernel/linux/kernel/v4.x/linux-4.4.tar.xz";
+    sha256 = "401d7c8fef594999a460d10c72c5a94e9c2e1022f16795ec51746b0d165418b2";
+  };
+
+  configfile = ./kernel.config;
+
+  kernelPatches = [{
+    patch = ./usbarmory-dts.patch;
+    name = "usbarmory-dts";
+  }];
+
+  allowImportFromDerivation = true;
+}
diff --git a/modules/nixos-hardware/inversepath/usbarmory/usbarmory-dts.patch b/modules/nixos-hardware/inversepath/usbarmory/usbarmory-dts.patch
new file mode 100644
index 000000000000..e29c6dd331db
--- /dev/null
+++ b/modules/nixos-hardware/inversepath/usbarmory/usbarmory-dts.patch
@@ -0,0 +1,418 @@
+diff -Nru linux-4.4-orig/arch/arm/boot/dts/imx53-usbarmory-common.dtsi linux-4.4/arch/arm/boot/dts/imx53-usbarmory-common.dtsi
+--- linux-4.4-orig/arch/arm/boot/dts/imx53-usbarmory-common.dtsi	1969-12-31 18:00:00.000000000 -0600
++++ linux-4.4/arch/arm/boot/dts/imx53-usbarmory-common.dtsi	2016-02-04 08:31:38.730794858 -0600
+@@ -0,0 +1,241 @@
++/*
++ * USB armory MkI device tree include file
++ * https://inversepath.com/usbarmory
++ *
++ * Copyright (C) 2015, Inverse Path
++ * Andrej Rosano <andrej@inversepath.com>
++ *
++ * Licensed under GPLv2
++ */
++
++#include "imx53.dtsi"
++
++/ {
++	model = "Inverse Path USB armory";
++	compatible = "inversepath,imx53-usbarmory", "fsl,imx53";
++};
++
++/ {
++	chosen {
++		stdout-path = &uart1;
++	};
++
++	memory {
++		reg = <0x70000000 0x20000000>;
++	};
++
++	leds {
++		compatible = "gpio-leds";
++		pinctrl-names = "default";
++		pinctrl-0 = <&led_pin_gpio4_27>;
++
++		user {
++			label = "LED";
++			gpios = <&gpio4 27 0>;
++			linux,default-trigger = "heartbeat";
++		};
++	};
++
++	soc {
++		aips@60000000 {
++			sahara: crypto@63ff8000 {
++				compatible = "fsl,imx53-sahara";
++				reg = <0x63ff8000 0x4000>;
++				interrupts = <19 20>;
++				clocks = <&clks IMX5_CLK_SAHARA_IPG_GATE>,
++					 <&clks IMX5_CLK_SAHARA_IPG_GATE>;
++				clock-names = "ipg", "ahb";
++			};
++		};
++	};
++};
++
++&cpu0 {
++	device_type = "cpu";
++	compatible = "arm,cortex-a8";
++	reg = <0x0>;
++	clocks = <&clks IMX5_CLK_ARM>;
++	clock-latency = <61036>;
++	voltage-tolerance = <5>;
++	operating-points = <
++		/* kHz */
++		 166666  850000
++		 400000  900000
++		 800000 1050000
++	>;
++};
++
++&esdhc1 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_esdhc1>;
++	status = "okay";
++};
++
++&iomuxc {
++	pinctrl-names = "default";
++
++	imx53-usbarmory {
++		led_pin_gpio4_27: led_gpio4_27@0 {
++			fsl,pins = <
++				MX53_PAD_DISP0_DAT6__GPIO4_27 0x80000000
++			>;
++		};
++
++		pinctrl_esdhc1: esdhc1grp {
++			fsl,pins = <
++				MX53_PAD_SD1_DATA0__ESDHC1_DAT0		0x1d5
++				MX53_PAD_SD1_DATA1__ESDHC1_DAT1		0x1d5
++				MX53_PAD_SD1_DATA2__ESDHC1_DAT2		0x1d5
++				MX53_PAD_SD1_DATA3__ESDHC1_DAT3		0x1d5
++				MX53_PAD_SD1_CMD__ESDHC1_CMD		0x1d5
++				MX53_PAD_SD1_CLK__ESDHC1_CLK		0x1d5
++			>;
++		};
++
++		pinctrl_i2c1_pmic: i2c1grp_pmic {
++			fsl,pins = <
++				MX53_PAD_EIM_D21__I2C1_SCL	0xc0000000
++				MX53_PAD_EIM_D28__I2C1_SDA	0xc0000000
++			>;
++		};
++
++		/*
++		UART mode pin header configration:
++		pin number:	1     2     3     4     5      6      7
++		function:	GND   5V    ?     ?     TX     RX     ?
++		*/
++		pinctrl_uart1: uart1grp {
++			fsl,pins = <
++				MX53_PAD_CSI0_DAT10__UART1_TXD_MUX	0x1e4
++				MX53_PAD_CSI0_DAT11__UART1_RXD_MUX	0x1e4
++			>;
++		};
++
++		/*
++		GPIO mode pin header configuration:
++		1     2     3         4         5         6         7
++		GND   5V    GPIO5[26] GPIO5[27] GPIO5[28] GPIO5[29] GPIO5[30]
++		*/
++		pinctrl_gpio5: gpio5grp {
++			fsl,pins = <
++				MX53_PAD_CSI0_DAT8__GPIO5_26	0xc0
++				MX53_PAD_CSI0_DAT9__GPIO5_27	0xc0
++				MX53_PAD_CSI0_DAT10__GPIO5_28	0xc0
++				MX53_PAD_CSI0_DAT11__GPIO5_29	0xc0
++				MX53_PAD_CSI0_DAT12__GPIO5_30	0xc0
++			>;
++		};
++
++		/*
++		SPI mode pin header configuration:
++		1     2     3      4      5      6      7
++		GND   5V    SCLK   MOSI   MISO   /SS0   /SS1
++		*/
++		pinctrl_ecspi2: ecspi2grp {
++			fsl,pins = <
++				MX53_PAD_CSI0_DAT8__ECSPI2_SCLK		0x80000000
++				MX53_PAD_CSI0_DAT9__ECSPI2_MOSI		0x80000000
++				MX53_PAD_CSI0_DAT10__ECSPI2_MISO	0x80000000
++				MX53_PAD_CSI0_DAT11__GPIO5_29		0x80000000
++				MX53_PAD_CSI0_DAT12__GPIO5_30		0x80000000
++			>;
++		};
++
++		/*
++		I2C mode pin header configuration:
++		1     2    3     4     5          6          7
++		GND   5V   SDA   SCL   GPIO5[28]  GPIO5[29]  GPIO5[30]
++		*/
++		pinctrl_i2c1_pinheader: i2c1grp_pinheader {
++			fsl,pins = <
++				MX53_PAD_CSI0_DAT8__I2C1_SDA	0xc0000000
++				MX53_PAD_CSI0_DAT9__I2C1_SCL	0xc0000000
++				MX53_PAD_CSI0_DAT10__GPIO5_28	0x80000000
++				MX53_PAD_CSI0_DAT11__GPIO5_29	0x80000000
++				MX53_PAD_CSI0_DAT12__GPIO5_30	0x80000000
++			>;
++		};
++	};
++};
++
++&uart1 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_uart1>;
++	status = "okay";
++};
++
++&vpu {
++	status = "okay";
++};
++
++&i2c1 {
++	pinctrl-0 = <&pinctrl_i2c1_pmic>;
++	status = "okay";
++	ltc3589: pmic@34 {
++		compatible = "lltc,ltc3589-2";
++		reg = <0x34>;
++		regulators {
++			sw1_reg: sw1 {
++				regulator-min-microvolt = <591930>;
++				regulator-max-microvolt = <1224671>;
++				lltc,fb-voltage-divider = <100000 158000>;
++				regulator-ramp-delay = <7000>;
++				regulator-boot-on;
++				regulator-always-on;
++			};
++
++			sw2_reg: sw2 {
++				regulator-min-microvolt = <704123>;
++				regulator-max-microvolt = <1456803>;
++				lltc,fb-voltage-divider = <180000 191000>;
++				regulator-ramp-delay = <7000>;
++				regulator-boot-on;
++				regulator-always-on;
++			};
++
++			sw3_reg: sw3 {
++				regulator-min-microvolt = <1341250>;
++				regulator-max-microvolt = <2775000>;
++				lltc,fb-voltage-divider = <270000 100000>;
++				regulator-ramp-delay = <7000>;
++				regulator-boot-on;
++				regulator-always-on;
++			};
++
++			bb_out_reg: bb-out {
++				regulator-min-microvolt = <3387341>;
++				regulator-max-microvolt = <3387341>;
++				lltc,fb-voltage-divider = <511000 158000>;
++				regulator-boot-on;
++				regulator-always-on;
++			};
++			ldo1_reg: ldo1 {
++				regulator-min-microvolt = <1306329>;
++				regulator-max-microvolt = <1306329>;
++				lltc,fb-voltage-divider = <100000 158000>;
++				regulator-boot-on;
++				regulator-always-on;
++			};
++
++			ldo2_reg: ldo2 {
++				regulator-min-microvolt = <704123>;
++				regulator-max-microvolt = <1456806>;
++				lltc,fb-voltage-divider = <180000 191000>;
++				regulator-ramp-delay = <7000>;
++				regulator-boot-on;
++				regulator-always-on;
++			};
++
++			ldo3_reg: ldo3 {
++				regulator-min-microvolt = <2800000>;
++				regulator-max-microvolt = <2800000>;
++				regulator-boot-on;
++			};
++
++			ldo4_reg: ldo4 {
++				regulator-min-microvolt = <1200000>;
++				regulator-max-microvolt = <3200000>;
++			};
++		};
++	};
++};
+diff -Nru linux-4.4-orig/arch/arm/boot/dts/imx53-usbarmory.dts linux-4.4/arch/arm/boot/dts/imx53-usbarmory.dts
+--- linux-4.4-orig/arch/arm/boot/dts/imx53-usbarmory.dts	1969-12-31 18:00:00.000000000 -0600
++++ linux-4.4/arch/arm/boot/dts/imx53-usbarmory.dts	2016-02-04 08:31:38.730794858 -0600
+@@ -0,0 +1,18 @@
++/*
++ * USB armory MkI device mode device tree file
++ * https://inversepath.com/usbarmory
++ *
++ * Copyright (C) 2015, Inverse Path
++ * Andrej Rosano <andrej@inversepath.com>
++ *
++ * Licensed under GPLv2
++ */
++
++/dts-v1/;
++
++#include "imx53-usbarmory-common.dtsi"
++
++&usbotg {
++	dr_mode = "peripheral";
++	status = "okay";
++};
+diff -Nru linux-4.4-orig/arch/arm/boot/dts/imx53-usbarmory-gpio.dts linux-4.4/arch/arm/boot/dts/imx53-usbarmory-gpio.dts
+--- linux-4.4-orig/arch/arm/boot/dts/imx53-usbarmory-gpio.dts	1969-12-31 18:00:00.000000000 -0600
++++ linux-4.4/arch/arm/boot/dts/imx53-usbarmory-gpio.dts	2016-02-04 08:31:38.730794858 -0600
+@@ -0,0 +1,26 @@
++/*
++ * USB armory MkI device mode device tree file
++ * https://inversepath.com/usbarmory
++ *
++ * Copyright (C) 2015, Inverse Path
++ * Andrej Rosano <andrej@inversepath.com>
++ *
++ * Licensed under GPLv2
++ */
++
++/dts-v1/;
++
++#include "imx53-usbarmory-common.dtsi"
++
++&usbotg {
++	dr_mode = "peripheral";
++	status = "okay";
++};
++
++&iomuxc {
++	pinctrl-0 = <&pinctrl_gpio5>;
++};
++
++&uart1 {
++	status = "disabled";
++};
+diff -Nru linux-4.4-orig/arch/arm/boot/dts/imx53-usbarmory-host.dts linux-4.4/arch/arm/boot/dts/imx53-usbarmory-host.dts
+--- linux-4.4-orig/arch/arm/boot/dts/imx53-usbarmory-host.dts	1969-12-31 18:00:00.000000000 -0600
++++ linux-4.4/arch/arm/boot/dts/imx53-usbarmory-host.dts	2016-02-04 08:31:38.730794858 -0600
+@@ -0,0 +1,18 @@
++/*
++ * USB armory MkI host mode device tree file
++ * https://inversepath.com/usbarmory
++ *
++ * Copyright (C) 2015, Inverse Path
++ * Andrej Rosano <andrej@inversepath.com>
++ *
++ * Licensed under GPLv2
++ */
++
++/dts-v1/;
++
++#include "imx53-usbarmory-common.dtsi"
++
++&usbotg {
++	dr_mode = "host";
++	status = "okay";
++};
+diff -Nru linux-4.4-orig/arch/arm/boot/dts/imx53-usbarmory-i2c.dts linux-4.4/arch/arm/boot/dts/imx53-usbarmory-i2c.dts
+--- linux-4.4-orig/arch/arm/boot/dts/imx53-usbarmory-i2c.dts	1969-12-31 18:00:00.000000000 -0600
++++ linux-4.4/arch/arm/boot/dts/imx53-usbarmory-i2c.dts	2016-02-04 08:31:38.730794858 -0600
+@@ -0,0 +1,32 @@
++/*
++ * USB armory MkI device mode device tree file
++ * https://inversepath.com/usbarmory
++ *
++ * Copyright (C) 2015, Inverse Path
++ * Andrej Rosano <andrej@inversepath.com>
++ *
++ * Licensed under GPLv2
++ */
++
++/dts-v1/;
++
++#include "imx53-usbarmory-common.dtsi"
++
++&usbotg {
++	dr_mode = "peripheral";
++	status = "okay";
++};
++
++&uart1 {
++	status = "disabled";
++};
++
++&iomuxc {
++	pinctrl-0 = <&pinctrl_i2c1_pinheader>;
++};
++
++&i2c1 {
++	ltc3589: pmic@34 {
++		status = "disabled";
++	};
++};
+diff -Nru linux-4.4-orig/arch/arm/boot/dts/imx53-usbarmory-spi.dts linux-4.4/arch/arm/boot/dts/imx53-usbarmory-spi.dts
+--- linux-4.4-orig/arch/arm/boot/dts/imx53-usbarmory-spi.dts	1969-12-31 18:00:00.000000000 -0600
++++ linux-4.4/arch/arm/boot/dts/imx53-usbarmory-spi.dts	2016-02-04 08:31:38.730794858 -0600
+@@ -0,0 +1,45 @@
++/*
++ * USB armory MkI device mode device tree file
++ * https://inversepath.com/usbarmory
++ *
++ * Copyright (C) 2015, Inverse Path
++ * Andrej Rosano <andrej@inversepath.com>
++ *
++ * Licensed under GPLv2
++ */
++
++/dts-v1/;
++
++#include "imx53-usbarmory-common.dtsi"
++
++&usbotg {
++	dr_mode = "peripheral";
++	status = "okay";
++};
++
++&uart1 {
++	status = "disabled";
++};
++
++&iomuxc {
++	pinctrl-0 = <&pinctrl_ecspi2>;
++};
++
++&ecspi2 {
++	fsl,spi-num-chipselects = <2>;
++	cs-gpios = <&gpio5 29 0>, <&gpio5 30 0>;
++	status = "okay";
++
++	flash: m25p40@0 {
++		#address-cells = <1>;
++		#size-cells = <1>;
++		compatible = "st,m25p40", "st,m25p";
++		spi-max-frequency = <20000000>;
++		reg = <0>;
++
++		partition@0 {
++			label = "test-partition";
++			reg = <0x0 0x80000>;
++		};
++	};
++};
+diff -Nru linux-4.4-orig/arch/arm/boot/dts/Makefile linux-4.4/arch/arm/boot/dts/Makefile
+--- linux-4.4-orig/arch/arm/boot/dts/Makefile	2016-02-04 13:29:20.880919101 -0600
++++ linux-4.4/arch/arm/boot/dts/Makefile	2016-02-04 13:27:10.733849955 -0600
+@@ -278,6 +278,10 @@
+ 	imx53-smd.dtb \
+ 	imx53-tx53-x03x.dtb \
+ 	imx53-tx53-x13x.dtb \
++	imx53-usbarmory.dtb \
++	imx53-usbarmory-host.dtb \
++	imx53-usbarmory-i2c.dtb \
++	imx53-usbarmory-gpio.dtb \
+ 	imx53-voipac-bsb.dtb
+ dtb-$(CONFIG_SOC_IMX6Q) += \
+ 	imx6dl-apf6dev.dtb \
diff --git a/modules/nixos-hardware/lenovo/ideapad/default.nix b/modules/nixos-hardware/lenovo/ideapad/default.nix
new file mode 100644
index 000000000000..d774ba3793af
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/ideapad/default.nix
@@ -0,0 +1,3 @@
+{
+  imports = [ ../../common/pc/laptop ];
+}
diff --git a/modules/nixos-hardware/lenovo/ideapad/z510/default.nix b/modules/nixos-hardware/lenovo/ideapad/z510/default.nix
new file mode 100644
index 000000000000..83f656806854
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/ideapad/z510/default.nix
@@ -0,0 +1,11 @@
+{ lib, ... }:
+
+{
+  imports = [
+    ../.
+    ../../../common/cpu/intel
+  ];
+
+  # https://github.com/NixOS/nixpkgs/issues/18356
+  boot.blacklistedKernelModules = [ "nouveau" ];
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/default.nix b/modules/nixos-hardware/lenovo/thinkpad/default.nix
new file mode 100644
index 000000000000..99d4937e49c7
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/default.nix
@@ -0,0 +1,11 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [ ../../common/pc/laptop ];
+
+  hardware.trackpoint.enable = lib.mkDefault true;
+  hardware.trackpoint.emulateWheel = lib.mkDefault config.hardware.trackpoint.enable;
+
+  # Fingerprint reader: login and unlock with fingerprint (if you add one with `fprintd-enroll`)
+  # services.fprintd.enable = true;
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/e495/default.nix b/modules/nixos-hardware/lenovo/thinkpad/e495/default.nix
new file mode 100644
index 000000000000..51ddb5cfd010
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/e495/default.nix
@@ -0,0 +1,11 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ../.
+    ../../../common/cpu/amd
+  ];
+
+  # see https://github.com/NixOS/nixpkgs/issues/69289
+  boot.kernelPackages = lib.mkIf (lib.versionOlder pkgs.linux.version "5.2") pkgs.linuxPackages_latest;
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/l13/default.nix b/modules/nixos-hardware/lenovo/thinkpad/l13/default.nix
new file mode 100644
index 000000000000..3c09f5091ed8
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/l13/default.nix
@@ -0,0 +1,10 @@
+{ nixos, lib, pkgs, config, stdenv, ... }:
+{
+  imports = [
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop/acpi_call.nix
+    ../.
+  ];
+
+  services.throttled.enable = lib.mkDefault true;
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/p53/default.nix b/modules/nixos-hardware/lenovo/thinkpad/p53/default.nix
new file mode 100644
index 000000000000..a499d5c2d51e
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/p53/default.nix
@@ -0,0 +1,10 @@
+{ nixos, pkgs, lib, config, stdenv, ... }:
+{
+  imports = [
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop/acpi_call.nix
+    ../.
+  ];
+
+  services.throttled.enable = lib.mkDefault true;
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/t410/default.nix b/modules/nixos-hardware/lenovo/thinkpad/t410/default.nix
new file mode 100644
index 000000000000..c40801ffc0bc
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/t410/default.nix
@@ -0,0 +1,36 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ../.
+    ../tp-smapi.nix
+    ../../../common/cpu/intel
+  ];
+
+  boot = {
+    # TODO: this configuration seems to be very aggressive.
+    # Ask @peti if it's stable or not.
+    kernelParams = [
+      "drm.debug=0"
+      "drm.vblankoffdelay=1"
+      "i915.semaphores=1"
+      "i915.modeset=1"
+      "i915.use_mmio_flip=1"
+      "i915.powersave=1"
+      "i915.enable_ips=1"
+      "i915.disable_power_well=1"
+      "i915.enable_hangcheck=1"
+      "i915.enable_cmd_parser=1"
+      "i915.fastboot=0"
+      "i915.enable_ppgtt=1"
+      "i915.reset=0"
+      "i915.lvds_use_ssc=0"
+      "i915.enable_psr=0"
+      "vblank_mode=0"
+      "i915.i915_enable_rc6=1"
+    ];
+    blacklistedKernelModules = [
+      "sierra_net" "cdc_mbim" "cdc_ncm" "btusb"
+    ];
+  };
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/t420/default.nix b/modules/nixos-hardware/lenovo/thinkpad/t420/default.nix
new file mode 100644
index 000000000000..5d3efe831c96
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/t420/default.nix
@@ -0,0 +1,9 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ../.
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop/acpi_call.nix
+  ];
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/t430/default.nix b/modules/nixos-hardware/lenovo/thinkpad/t430/default.nix
new file mode 100644
index 000000000000..bdad1be9878a
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/t430/default.nix
@@ -0,0 +1,16 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ../.
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop/acpi_call.nix
+  ];
+
+  boot = {
+    kernelParams = [
+      # fixes brightness keys, see https://wiki.archlinux.org/index.php/Lenovo_ThinkPad_T430s
+      "acpi_osi\='!Windows 2012'"
+    ];
+  };
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/t440p/default.nix b/modules/nixos-hardware/lenovo/thinkpad/t440p/default.nix
new file mode 100644
index 000000000000..4dd4085794fa
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/t440p/default.nix
@@ -0,0 +1,16 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ../.
+    ../../../common/cpu/intel
+  ];
+
+  boot = {
+    extraModprobeConfig = lib.mkDefault ''
+      options bbswitch use_acpi_to_detect_card_state=1
+    '';
+    # TODO: probably enable tcsd? Is this line necessary?
+    kernelModules = [ "tpm-rng" ];
+  };
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/t440s/default.nix b/modules/nixos-hardware/lenovo/thinkpad/t440s/default.nix
new file mode 100644
index 000000000000..c40ad90f9b3d
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/t440s/default.nix
@@ -0,0 +1,14 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ../.
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop/acpi_call.nix
+  ];
+
+  boot = {
+    # TODO: probably enable tcsd? Is this line necessary?
+    kernelModules = [ "tpm-rng" ];
+  };
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/t450s/default.nix b/modules/nixos-hardware/lenovo/thinkpad/t450s/default.nix
new file mode 100644
index 000000000000..e62337171b9a
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/t450s/default.nix
@@ -0,0 +1,9 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop/acpi_call.nix
+    ../.
+  ];
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/t460s/default.nix b/modules/nixos-hardware/lenovo/thinkpad/t460s/default.nix
new file mode 100644
index 000000000000..e62337171b9a
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/t460s/default.nix
@@ -0,0 +1,9 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop/acpi_call.nix
+    ../.
+  ];
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/t470s/default.nix b/modules/nixos-hardware/lenovo/thinkpad/t470s/default.nix
new file mode 100644
index 000000000000..e62337171b9a
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/t470s/default.nix
@@ -0,0 +1,9 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop/acpi_call.nix
+    ../.
+  ];
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/t480s/default.nix b/modules/nixos-hardware/lenovo/thinkpad/t480s/default.nix
new file mode 100644
index 000000000000..1a14afa6b5e4
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/t480s/default.nix
@@ -0,0 +1,11 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop/acpi_call.nix
+    ../.
+  ];
+
+  services.throttled.enable = lib.mkDefault true;
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/t490/default.nix b/modules/nixos-hardware/lenovo/thinkpad/t490/default.nix
new file mode 100644
index 000000000000..3578da66c73d
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/t490/default.nix
@@ -0,0 +1,31 @@
+{ nixos, lib, pkgs, config, stdenv, ... }:
+{
+  imports = [
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop/acpi_call.nix
+    ../.
+  ];
+
+  services = {
+    # This fixes the pulseaudio profiles of the Thinkpad T490.
+    # The laptop contains a single audio card with 5 sub-devices. Default pulseaudio only offers a single sink
+    # which can only be switched between speaker/HDMI via a manual profile change.
+    # This configures a profile set for pulseaudio which offers multiple sinks corresponding to the
+    # speaker + 4 HDMI ports. This allows the user to play audio streams on the speaker and any of the 4 HDMI/USB-C
+    # ports at the same time.
+    udev.extraRules = let
+      t490ProfileSet = ./t490-profile-set.conf;
+    in ''
+    SUBSYSTEM!="sound", GOTO="pulseaudio_end"
+    ACTION!="change", GOTO="pulseaudio_end"
+    KERNEL!="card*", GOTO="pulseaudio_end"
+
+    # Lenovo T490
+    ATTRS{vendor}=="0x8086" ATTRS{device}=="0x9dc8" ATTRS{subsystem_vendor}=="0x17aa", ATTRS{subsystem_device}=="0x2279", ENV{PULSE_PROFILE_SET}="${t490ProfileSet}"
+
+    LABEL="pulseaudio_end"
+    '';
+    
+    throttled.enable = lib.mkDefault true;
+  };
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/t490/t490-profile-set.conf b/modules/nixos-hardware/lenovo/thinkpad/t490/t490-profile-set.conf
new file mode 100644
index 000000000000..98f8f0318de1
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/t490/t490-profile-set.conf
@@ -0,0 +1,107 @@
+[Profile speaker+hdmi-stereo]
+description = Speaker + HDMI Stereo
+output-mappings = analog-stereo hdmi-stereo hdmi-stereo-extra1 hdmi-stereo-extra2
+input-mappings = analog-stereo
+
+[Mapping analog-stereo]
+device-strings = front:%f
+channel-map = left,right
+paths-output = analog-output analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2
+paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line analog-input-headphone-mic analog-input-headset-mic
+priority = 15
+
+[Mapping hdmi-stereo]
+description = Stereo (HDMI)
+device-strings = hdmi:%f
+paths-output = hdmi-output-0
+channel-map = left,right
+priority = 6
+direction = output
+
+[Mapping hdmi-stereo-extra1]
+description = Stereo (HDMI 2)
+device-strings = hdmi:%f,1
+paths-output = hdmi-output-1
+channel-map = left,right
+priority = 8
+direction = output
+
+[Mapping hdmi-stereo-extra2]
+description = Stereo (HDMI 3)
+device-strings = hdmi:%f,2
+paths-output = hdmi-output-2
+channel-map = left,right
+priority = 6
+direction = output
+
+[Profile speaker+hdmi-surround]
+description = Speaker + HDMI 5.1
+output-mappings = analog-stereo hdmi-surround hdmi-surround-extra1 hdmi-surround-extra2
+input-mappings = analog-stereo
+
+[Mapping analog-stereo]
+device-strings = front:%f
+channel-map = left,right
+paths-output = analog-output analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2
+paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line analog-input-headphone-mic analog-input-headset-mic
+priority = 15
+
+[Mapping hdmi-surround]
+description = Digital Surround 5.1 (HDMI)
+device-strings = hdmi:%f
+paths-output = hdmi-output-0
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 6
+direction = output
+
+[Mapping hdmi-surround-extra1]
+description = Digital Surround 5.1 (HDMI 2)
+device-strings = hdmi:%f,1
+paths-output = hdmi-output-1
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 8
+direction = output
+
+[Mapping hdmi-surround-extra2]
+description = Digital Surround 5.1 (HDMI 3)
+device-strings = hdmi:%f,2
+paths-output = hdmi-output-2
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
+priority = 6
+direction = output
+
+[Profile speaker+hdmi-surround71]
+description = Speaker + HDMI 7.1
+output-mappings = analog-stereo hdmi-surround71 hdmi-surround71-extra1 hdmi-surround71-extra2
+input-mappings = analog-stereo
+
+[Mapping analog-stereo]
+device-strings = front:%f
+channel-map = left,right
+paths-output = analog-output analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2
+paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line analog-input-headphone-mic analog-input-headset-mic
+priority = 15
+
+[Mapping hdmi-surround71]
+description = Digital Surround 7.1 (HDMI)
+device-strings = hdmi:%f
+paths-output = hdmi-output-0
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
+priority = 6
+direction = output
+
+[Mapping hdmi-surround71-extra1]
+description = Digital Surround 7.1 (HDMI 2)
+device-strings = hdmi:%f,1
+paths-output = hdmi-output-1
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
+priority = 8
+direction = output
+
+[Mapping hdmi-surround71-extra2]
+description = Digital Surround 7.1 (HDMI 3)
+device-strings = hdmi:%f,2
+paths-output = hdmi-output-2
+channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
+priority = 6
+direction = output
diff --git a/modules/nixos-hardware/lenovo/thinkpad/t495/default.nix b/modules/nixos-hardware/lenovo/thinkpad/t495/default.nix
new file mode 100644
index 000000000000..64957e6e06c7
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/t495/default.nix
@@ -0,0 +1,16 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ../.
+    ../../../common/cpu/amd
+    ../../../common/pc/laptop/acpi_call.nix
+  ];
+
+  # Force use of the thinkpad_acpi driver for backlight control.
+  # This allows the backlight save/load systemd service to work.
+  boot.kernelParams = [ "acpi_backlight=native" ];
+
+  # see https://github.com/NixOS/nixpkgs/issues/69289
+  boot.kernelPackages = lib.mkIf (lib.versionOlder pkgs.linux.version "5.2") pkgs.linuxPackages_latest;
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/tp-smapi.nix b/modules/nixos-hardware/lenovo/thinkpad/tp-smapi.nix
new file mode 100644
index 000000000000..451dd28ece03
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/tp-smapi.nix
@@ -0,0 +1,11 @@
+# tp_smapi works on ThinkPads made before 2013. See compat table:
+# https://www.thinkwiki.org/wiki/Tp_smapi#Model-specific_status
+
+{ config, ... }:
+
+{
+  boot = {
+    kernelModules = [ "tp_smapi" ];
+    extraModulePackages = with config.boot.kernelPackages; [ tp_smapi ];
+  };
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/x1-extreme/default.nix b/modules/nixos-hardware/lenovo/thinkpad/x1-extreme/default.nix
new file mode 100644
index 000000000000..09a0ec1700a5
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/x1-extreme/default.nix
@@ -0,0 +1,8 @@
+{
+  imports = [
+    ../.
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop/acpi_call.nix
+    ../../../common/pc/laptop/ssd
+  ];
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/x1-extreme/gen2/default.nix b/modules/nixos-hardware/lenovo/thinkpad/x1-extreme/gen2/default.nix
new file mode 100644
index 000000000000..6b0ebbd5d1e3
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/x1-extreme/gen2/default.nix
@@ -0,0 +1,48 @@
+{ config, lib, ... }:
+
+with lib;
+
+{
+  imports = [
+    ../.
+  ];
+
+  # Fixes an issue with incorrect battery reporting. See
+  # https://wiki.archlinux.org/index.php/Lenovo_ThinkPad_X1_Extreme_(Gen_2)#Invalid_Stats_Workaround
+  boot.initrd.availableKernelModules = [ "battery" ];
+
+  # New ThinkPads have a different TrackPoint manufacturer/name.
+  # See also https://certification.ubuntu.com/catalog/component/input/5313/input%3ATPPS/2ElanTrackPoint/
+  hardware.trackpoint.device = "TPPS/2 Elan TrackPoint";
+
+  # Since the HDMI port is connected to the NVIDIA card.
+  hardware.bumblebee.connectDisplay = true;
+
+  nixpkgs.overlays = [
+    (self: super: {
+      bumblebee = super.bumblebee.override {
+        extraNvidiaDeviceOptions = ''
+          Option "AllowEmptyInitialConfiguration"
+        '';
+      };
+    })
+  ];
+
+  services.xserver = mkMerge [
+    {
+      # Set the right DPI. xdpyinfo says the screen is 508×285 mm but
+      # it actually is 344×193 mm.
+      monitorSection = ''
+        DisplaySize 344 193
+      '';
+    }
+
+    # To support intel-virtual-output when using Bumblebee.
+    (mkIf config.hardware.bumblebee.enable {
+      deviceSection = ''Option "VirtualHeads" "1"'';
+      videoDrivers = [ "intel" ];
+    })
+  ];
+
+  services.throttled.enable = lib.mkDefault true;
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/x1/6th-gen/QHD/default.nix b/modules/nixos-hardware/lenovo/thinkpad/x1/6th-gen/QHD/default.nix
new file mode 100644
index 000000000000..0b8555fbfb08
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/x1/6th-gen/QHD/default.nix
@@ -0,0 +1,36 @@
+# X1 6th generation with a QHD (2560x1440px) display
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ../.
+  ];
+
+  # Fix font sizes in X
+  services.xserver.dpi = 210;
+  fonts.fontconfig.dpi = 210;
+
+  # Fix sizes of GTK/GNOME ui elements
+  environment.variables = {
+    GDK_SCALE = lib.mkDefault "2";
+    GDK_DPI_SCALE= lib.mkDefault "0.5";
+  };
+  # Enable readable font on console. The example configuration that
+  # follows is taliored towards western languages. To see how to
+  # configure the font download the source tarball from
+  # http://terminus-font.sourceforge.net/ and read the README file on
+  # the root dir
+
+  # i18n = {
+  #   # this means ISO8859-1 or ISO8859-15 or Windows-1252 codepages
+  #   # (ter-1), 16x32 px (32), normal font weight (n)
+  #   consoleFont = "ter-132n";
+  #   consoleKeyMap = "us";
+  #   defaultLocale = "en_US.UTF-8";
+  #   consolePackages = [ pkgs.terminus_font ];
+  # };
+
+  # Early configure the console to make the font readable from the
+  # start
+  # boot.earlyVconsoleSetup = true;
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/x1/6th-gen/default.nix b/modules/nixos-hardware/lenovo/thinkpad/x1/6th-gen/default.nix
new file mode 100644
index 000000000000..00446b8c278b
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/x1/6th-gen/default.nix
@@ -0,0 +1,20 @@
+# A good source of information about how to fix the issues still
+# standing with kernel 4.6.11 is the following wiki page:
+# https://wiki.archlinux.org/index.php/Lenovo_ThinkPad_X1_Carbon_(Gen_6). The
+# TrackPoint and TouchPad issues there seem to have been fixed already.
+#
+# Enable the lower-power S3 suspend state by upgrading the BIOS to version >= 1.30,
+# then manually selecting Linux in the power management section.
+{ config, pkgs, lib, ... }:
+{
+  imports = [
+    ../.
+    ../../../../common/pc/laptop/acpi_call.nix
+  ];
+
+  # New ThinkPads have a different TrackPoint manufacturer/name.
+  # See also https://certification.ubuntu.com/catalog/component/input/5313/input%3ATPPS/2ElanTrackPoint/
+  hardware.trackpoint.device = "TPPS/2 Elan TrackPoint";
+
+  services.throttled.enable = lib.mkDefault true;
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/x1/default.nix b/modules/nixos-hardware/lenovo/thinkpad/x1/default.nix
new file mode 100644
index 000000000000..870d9a857742
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/x1/default.nix
@@ -0,0 +1,6 @@
+{
+  imports = [
+    ../.
+    ../../../common/cpu/intel
+  ];
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/x140e/default.nix b/modules/nixos-hardware/lenovo/thinkpad/x140e/default.nix
new file mode 100644
index 000000000000..c03410e936e2
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/x140e/default.nix
@@ -0,0 +1,14 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ../.
+    ../../../common/cpu/amd
+  ];
+
+  boot.extraModprobeConfig = lib.mkDefault ''
+    options snd_hda_intel enable=0,1
+  '';
+
+  services.xserver.videoDrivers = [ "ati" ];
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/x220/default.nix b/modules/nixos-hardware/lenovo/thinkpad/x220/default.nix
new file mode 100644
index 000000000000..3cf18e397469
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/x220/default.nix
@@ -0,0 +1,10 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ../.
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop/hdd # TODO: reverse compat
+    ../tp-smapi.nix
+  ];
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/x230/default.nix b/modules/nixos-hardware/lenovo/thinkpad/x230/default.nix
new file mode 100644
index 000000000000..58b09f4c5d5f
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/x230/default.nix
@@ -0,0 +1,19 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ../.
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop/acpi_call.nix
+  ];
+
+  boot = {
+    kernelModules = [
+      "tpm-rng"
+    ];
+  };
+
+  services.xserver.deviceSection = lib.mkDefault ''
+    Option "TearFree" "true"
+  '';
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/x250/default.nix b/modules/nixos-hardware/lenovo/thinkpad/x250/default.nix
new file mode 100644
index 000000000000..55ae2096342e
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/x250/default.nix
@@ -0,0 +1,7 @@
+{
+  imports = [
+    ../.
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop/acpi_call.nix
+  ];
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/x260/default.nix b/modules/nixos-hardware/lenovo/thinkpad/x260/default.nix
new file mode 100644
index 000000000000..d64a53075163
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/x260/default.nix
@@ -0,0 +1,12 @@
+{
+  imports = [
+    ../.
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop/acpi_call.nix
+  ];
+
+  # https://wiki.archlinux.org/index.php/TLP#Btrfs
+  services.tlp.extraConfig = ''
+    SATA_LINKPWR_ON_BAT=med_power_with_dipm
+  '';
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/x270/default.nix b/modules/nixos-hardware/lenovo/thinkpad/x270/default.nix
new file mode 100644
index 000000000000..870d9a857742
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/x270/default.nix
@@ -0,0 +1,6 @@
+{
+  imports = [
+    ../.
+    ../../../common/cpu/intel
+  ];
+}
diff --git a/modules/nixos-hardware/lenovo/thinkpad/x280/default.nix b/modules/nixos-hardware/lenovo/thinkpad/x280/default.nix
new file mode 100644
index 000000000000..a9423e547321
--- /dev/null
+++ b/modules/nixos-hardware/lenovo/thinkpad/x280/default.nix
@@ -0,0 +1,12 @@
+{ config, pkgs, lib, ... }:
+
+{
+  imports = [
+    ../.
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop/acpi_call.nix
+    ../../../common/pc/laptop/ssd/default.nix
+  ];
+
+  services.throttled.enable = lib.mkDefault true;
+}
diff --git a/modules/nixos-hardware/microsoft/hyper-v/README.md b/modules/nixos-hardware/microsoft/hyper-v/README.md
new file mode 100644
index 000000000000..f396214e09fd
--- /dev/null
+++ b/modules/nixos-hardware/microsoft/hyper-v/README.md
@@ -0,0 +1,63 @@
+This is a setup for installing NixOS in Hyper-V as a guest.
+
+I don't have details handy anymore on the detailed steps I had to do on the Windows host
+(there's no NixWindows yet, unfortunately...), so you'll have to try googling that yourself, e.g. something like
+"linux on hyper-v" or "ubuntu on hyper-v". (You're welcome to send PRs with improvements of this guide.)
+Below, I'm providing only the info with what to do on the NixOS side of things.
+
+## Installation ##
+
+I basically followed the [guide for NixOS on VirtualBox](https://nixos.org/wiki/Installing_NixOS_in_a_VirtualBox_guest).
+However, some additional changes in `/etc/nixos/configuration.nix` were required to really make it work
+(I don't include them as a .nix file, as they must be done **before `nixos-install`**, and I'm not sure how to proceed
+with cloning the nixos-hardware repo at this stage):
+
+    # REQUIRED - see: https://github.com/nixos/nixpkgs/issues/9899
+    boot.initrd.kernelModules = ["hv_vmbus" "hv_storvsc"];
+
+    # RECOMMENDED
+    # - use 800x600 resolution for text console, to make it easy to fit on screen
+    boot.kernelParams = ["video=hyperv_fb:800x600"];  # https://askubuntu.com/a/399960
+    # - avoid a problem with `nix-env -i` running out of memory
+    boot.kernel.sysctl."vm.overcommit_memory" = "1"; # https://github.com/NixOS/nix/issues/421
+
+    # UNKNOWN - not sure if below are needed; were suggested for VirtualBox and I used them
+    boot.loader.grub.device = "/dev/sda";
+    boot.initrd.checkJournalingFS = false;
+
+## Shared folder ##
+
+To share a folder between Windows host and Linux/NixOS guest, the typical solution seems to be to make a folder "shared"
+on Windows, then access it via Samba from NixOS.
+On the Windows host, I had to make an additional virtual switch in Hyper-V Manager, with mode "internal".
+Then in properties of the virtual network card on Windows host (attached to the virtual switch), I
+changed the IP to a fixed 10.0.0.100 (mask 255.255.255.240). I also added a special purpose user on the host, with some
+long randomly generated password, to act as Samba credentials for NixOS.
+To test that it works, I used the following commands:
+
+    $ nix-env -iA nixos.samba
+    $ smbclient -L //10.0.0.100 -U shares-guest%ReplaceWithSomeLongRandomlyGeneratedPassword
+    Domain=[DESKTOP-ABCD123] OS=[Windows 10 Pro 14393] Server=[Windows 10 Pro 6.3]
+    
+    	Sharename       Type      Comment
+    	---------       ----      -------
+    	ADMIN$          Disk      Administracja zdalna
+    	C$              Disk      Domyślny udział
+    	IPC$            IPC       Zdalne wywołanie IPC 
+    	shared-space    Disk      
+    Connection to 10.0.0.100 failed (Error NT_STATUS_RESOURCE_NAME_NOT_FOUND)
+    NetBIOS over TCP disabled -- no workgroup available
+
+    $ nix-env -e samba
+
+Then I added the following lines in `/etc/nixos/configuration.nix`:
+
+    # Client for shared folder on Windows Hyper-V host
+    # Based on: nixpkgs.git/nixos/tests/samba.nix
+    fileSystems."/vm-share" = {
+      fsType = "cifs";
+      device = "//10.0.0.100/shared-space";
+      options = [ "username=shares-guest" "password=ReplaceWithSomeLongRandomlyGeneratedPassword" ];
+    };
+    networking.interfaces.eth1.ip4 = [{address="10.0.0.101"; prefixLength=28;}];
+
diff --git a/modules/nixos-hardware/microsoft/surface-pro/3/README.wiki b/modules/nixos-hardware/microsoft/surface-pro/3/README.wiki
new file mode 100644
index 000000000000..9ffdc559e183
--- /dev/null
+++ b/modules/nixos-hardware/microsoft/surface-pro/3/README.wiki
@@ -0,0 +1,41 @@
+Work in progress. This is the setup for a dual-boot windows/NixOS. 
+
+== Installation ==
+
+Current requirements:
+* USB stick
+* Latest unstable minimal installation iso, I used [[https://nixos.org/releases/nixos/unstable/nixos-15.05pre61665.4c01e6d/nixos-minimal-15.05pre61665.4c01e6d-x86_64-linux.iso the 15.06-pre one]]
+* (maybe) USB keyboard and therefore a USB hub
+* WiFi SSID and password
+
+In the current installation iso, the kernel doesn't support the MS Type cover yet, so you need a USB keyboard for the initial install. Also note that at one point the keyboard wasn't working, and I couldn't see it in lsusb output. After a cold boot it worked fine.
+
+=== Steps ===
+
+Preparation:
+* Copy the ISO to the USB stick as explained [[https://nixos.org/nixos/manual/sec-installation.html#sec-booting-from-usb in the manual]]. Note that if you just dd the image onto the USB stick, it will boot without changes
+* With Windows Disk Management, shrink the windows disk so you have room for your installation. Use the free space to create a new partition without a filesystem or drive letter.
+* Check out [[https://github.com/cransom/surface-pro-3-nixos this expression and set of patches]] and add it to support newer versions of the Type Cover as well as enable the touch screen.
+
+* Reboot the Surface and furiously tap Esc until you end up in the BIOS screen. There, turn off secure boot (don't delete the keys), and change the boot order to USB + SSD. Save.
+* You should now be in the NixOS installer
+* Attach the USB stick and keyboard if the Type Cover doesn't work. The NixOS manual is at alt+F8
+
+Installation:
+* Using gdisk, print your partition table to find the empty partition you created. Find the EFI partition number (1 for me) and your new partition number (5 for me).
+* Create your filesystem of choice on the partition, I picked btrfs: `mkfs.btrfs -L root /dev/disk/by-id/xxx-5` (xxx is the samsung ssd). Mount it on /mnt.
+** With btrfs it's wise to put the nix store in its own subvolume: `btrfs subvol create /mnt/nix; btrfs subvol create /mnt/nix/store`
+* We'll be using Gummiboot, which needs to access the EFI partition. `mkdir /mnt/boot; mount /dev/disk/by-id/xxx-1`
+* Initialize your hardware configuration: `nixos-generate-config --root /mnt`
+* Create your wifi credentials: `wpa_passphrase SSID PASS > /etc/wpa_supplicant.conf; cp /etc/wpa_supplicant.conf /mnt/etc`. Check that ifconfig gets an ip address.
+* Edit /mnt/etc/configuration.nix, making sure that you have at least:
+   <nowiki>networking.wireless.enable = true;  # Enables wireless support via wpa_supplicant.
+
+  boot.kernelPackages = pkgs.linuxPackages_latest;
+  # be able to use the type cover in the initrd
+  boot.kernelModules = [ "hid-microsoft" ];</nowiki>
+* Install: `nixos-install`
+
+If this completes successfully, you should be able to reboot, remove the USB stick, and NixOS should come up.
+
+'''Note''': If using the hid-multitouch patch, you can boot windows as well as other profiles of NixOS without using a usb keyboard.
diff --git a/modules/nixos-hardware/microsoft/surface-pro/3/default.nix b/modules/nixos-hardware/microsoft/surface-pro/3/default.nix
new file mode 100644
index 000000000000..a09c60b1f205
--- /dev/null
+++ b/modules/nixos-hardware/microsoft/surface-pro/3/default.nix
@@ -0,0 +1,9 @@
+{ lib, ... }:
+
+{
+  # to use the type cover in the initrd
+  boot.kernelModules = [ "hid-microsoft" ];
+
+  # TODO: reverse compat
+  networking.wireless.enable = lib.mkDefault true;
+}
diff --git a/modules/nixos-hardware/pcengines/apu/default.nix b/modules/nixos-hardware/pcengines/apu/default.nix
new file mode 100644
index 000000000000..521887fe600b
--- /dev/null
+++ b/modules/nixos-hardware/pcengines/apu/default.nix
@@ -0,0 +1,8 @@
+{
+  boot.kernelParams = [ "console=ttyS0,115200n8" ];
+  boot.loader.grub.extraConfig = "
+    serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1
+    terminal_input serial
+    terminal_output serial
+  ";
+}
diff --git a/modules/nixos-hardware/purism/librem/13v3/README.md b/modules/nixos-hardware/purism/librem/13v3/README.md
new file mode 100644
index 000000000000..b4bae0b2f886
--- /dev/null
+++ b/modules/nixos-hardware/purism/librem/13v3/README.md
@@ -0,0 +1,87 @@
+This config is for [Librem 13v3](https://puri.sm/products/librem-13/) and [15v3](https://puri.sm/products/librem-15/) Laptops from Purism.
+
+
+Librem comes with Coreboot + SeaBIOS payload. That means EFI boot is not
+possible. Use `fdisk` to partition hard drive, and GRUB as a bootloader:
+
+```nix
+{
+  boot.loader.grub = {
+    enable = true;
+    device = "/dev/sda";
+    version = 2;
+  };
+}
+```
+
+## Adding a PureOS partition to the GRUB menu
+
+I first assume that `boot.loader.grub.useOSProber = true;` should be sufficient.
+However GRUB was not able to identify the disks correctly and it took me several
+reinstallation till setting `boot.loader.grub.fsIdentifier= "provided";` and using
+boot.loader.grub.extraEntries allowed me to dual boot NixOS and PureOS.
+
+Be aware that each time the PureOS updates the /boot/grub/grub.cfg you will be unable
+to boot into NixOS unless you patch grub.cfg manually again.
+
+Therefore: If you want to be able to boot into your old PureOS distribution
+add the following lines, assuming that you have a separate boot partition
+Adapt linux version and the UUID to your disk!!
+
+
+```nix
+{
+  boot.loader.grub.useOSProber = false;
+  boot.loader.grub.fsIdentifier= "provided";
+  boot.loader.grub.extraEntries = ''
+  menuentry "PureOS with linux 4.19.0-5-amd64 on /dev/sdb2 " {
+      insmod gzio
+      if [ x$grub_platform = xxen ]; then insmod xzio; insmod lzopio; fi
+      insmod part_msdos
+      insmod ext2
+      set root='hd0,msdos1'
+      if [ x$feature_platform_search_hint = xy ]; then
+        search --no-floppy --fs-uuid --set=root --hint-bios=hd0,msdos1 --hint-efi=hd0,msdos1 --hint-baremetal=ahci0,msdos1  ef7a4dcf-8cc4-4870-b860-3ed64906f9b9
+      else
+        search --no-floppy --fs-uuid --set=root ef7a4dcf-8cc4-4870-b860-3ed64906f9b9
+      fi
+      linux   /vmlinuz-4.19.0-5-amd64 root=UUID=43899f26-04f2-4ccb-b52a-c9441f1a1a6d ro  quiet splash resume=UUID=923317f8-d8bb-4e1f-bca3-f36a556de609 # $vt_handoff
+      initrd  /initrd.img-4.19.0-5-amd64
+  };
+}
+```
+
+## Automatically lock the desktop when removing the librem key
+
+The [instructions](https://docs.puri.sm/Librem_Key/Getting_Started/User_Manual.html#automatically-lock-the-desktop-when-removing-the-librem-key) to lock the screen after unplugging the [Librem Key](https://puri.sm/products/librem-key/#overview) don't work under NixOS.
+
+This snippet works on my Librem 15v3 laptop running KDE without wayland and is using the xlock from the package xlockmore.
+
+```nix
+{ pkgs, services , systemd, ... }:
+let
+  libremScreenSaver = pkgs.writeScriptBin "libremScreenSaver" ''
+  #!${pkgs.bash}/bin/bash
+  user=`ps aux | egrep "start_kdeinit|gdm-(wayland|x)-session"| head -n 1 | awk '{print $1}'`
+  if [ -n "$user" ]; then
+    sudo -u $user DISPLAY=:0 xlock 2>&1 | logger lockScreen for user $user
+  else
+    logger libremScreenSaver failed to find a user. Not running KDE?
+  fi
+  '';
+
+in {
+  services.udev.path = [ pkgs.procps pkgs.logger pkgs.gawk pkgs.xlockmore ];
+  services.udev.extraRules = ''
+    ACTION=="remove", ENV{PRODUCT}=="316d/4c4b/101" RUN+="${pkgs.systemd}/bin/systemctl --no-block start lockScreen@%k.service"
+  '';
+  systemd.services."lockScreen@" = {
+    bindsTo = [ "dev-%i.device"] ;
+    path = [ pkgs.procps pkgs.logger pkgs.sudo pkgs.xlockmore pkgs.gawk ] ;
+    serviceConfig = {
+        Type = "oneshot"; # was simple
+        ExecStart = "${libremScreenSaver}/bin/libremScreenSaver %I";
+    };
+  };
+}
+```
diff --git a/modules/nixos-hardware/purism/librem/13v3/default.nix b/modules/nixos-hardware/purism/librem/13v3/default.nix
new file mode 100644
index 000000000000..89ae07e4e693
--- /dev/null
+++ b/modules/nixos-hardware/purism/librem/13v3/default.nix
@@ -0,0 +1,13 @@
+{
+  imports = [
+    ../../../common/cpu/intel
+    ../../../common/pc/laptop
+  ];
+
+  # https://github.com/systemd/systemd/pull/9318
+  services.udev.extraHwdb = ''
+    # Purism Librem 13 V3
+    evdev:atkbd:dmi:bvn*:bvr*:bd*:svnPurism*:pn*Librem13v3*:pvr*
+     KEYBOARD_KEY_56=backslash
+  '';
+}
diff --git a/modules/nixos-hardware/purism/librem/15v3 b/modules/nixos-hardware/purism/librem/15v3
new file mode 120000
index 000000000000..feb0d06fafda
--- /dev/null
+++ b/modules/nixos-hardware/purism/librem/15v3
@@ -0,0 +1 @@
+13v3/
\ No newline at end of file
diff --git a/modules/nixos-hardware/raspberry-pi/2/README.org b/modules/nixos-hardware/raspberry-pi/2/README.org
new file mode 100644
index 000000000000..842919b1acf4
--- /dev/null
+++ b/modules/nixos-hardware/raspberry-pi/2/README.org
@@ -0,0 +1,23 @@
+** Status
+
+The code in master as of June 2015 should be able to prepare a bootable NixOS
+for Raspberry PI 2.
+
+There are still some drawbacks:
+
+NixOS does not provide a /boot/config.txt (the FAT32 partition). Making NixOS
+work in the Raspberry PI 2 is mainly the result of the recent work of ambro718,
+Dezgeg and viric (#nixos@irc.freenode.net).
+
+** Download
+
+If you want to test, you can flash this 4GB SD image (DOS partition table +
+fat32 + ext4 rootfs):
+
+magnet:?xt=urn:btih:0def3f6acb3bceddb22cb24098f58e40e2853ec2&dn=rpi2-nixos-4b09501f2-img.xz&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80
+
+Then you should be able to nixos-rebuild any configuration.nix changes.
+
+The image is the result of a "nixos-install" alone. No root password has been
+set, and it does not include a nixpkgs checkout or channel. In fact I (viric)
+created the FS into a NBD, not a real SD, to create this image.
diff --git a/modules/nixos-hardware/raspberry-pi/2/default.nix b/modules/nixos-hardware/raspberry-pi/2/default.nix
new file mode 100644
index 000000000000..d10bbd2424d9
--- /dev/null
+++ b/modules/nixos-hardware/raspberry-pi/2/default.nix
@@ -0,0 +1,32 @@
+{ lib, pkgs, ...}:
+
+{
+  boot = {
+    consoleLogLevel = lib.mkDefault 7;
+    extraTTYs = [ "ttyAMA0" ];
+    kernelPackages = lib.mkDefault pkgs.linuxPackages_rpi2;
+    kernelParams = [
+      "dwc_otg.lpm_enable=0"
+      "console=ttyAMA0,115200"
+      "rootwait"
+      "elevator=deadline"
+    ];
+    loader = {
+      grub.enable = lib.mkDefault false;
+      generationsDir.enable = lib.mkDefault false;
+      raspberryPi = {
+        enable = lib.mkDefault true;
+	version = lib.mkDefault 2;
+      };
+    };
+  };
+
+  nix.buildCores = 4;
+
+  nixpkgs.config.platform = lib.systems.platforms.raspberrypi2;
+
+  # cpufrequtils doesn't build on ARM
+  powerManagement.enable = lib.mkDefault false;
+
+  services.openssh.enable = lib.mkDefault true;
+}
diff --git a/modules/nixos-hardware/samsung/np900x3c/README.wiki b/modules/nixos-hardware/samsung/np900x3c/README.wiki
new file mode 100644
index 000000000000..e9fc58b8613b
--- /dev/null
+++ b/modules/nixos-hardware/samsung/np900x3c/README.wiki
@@ -0,0 +1,94 @@
+= Overview =
+
+Most of the features seem to be working with Linux >= 3.9
+
+== Hardware ==
+
+* CPU Intel(R) Core(TM) i5-3317U CPU @ 1.70GHz
+* RAM 4 GB
+* HDD 128GB SSD
+* Screen 13.3-Inch Screen
+* Graphics Intel HD Graphics 4000, Ivy bridge
+
+= Configuration =
+
+Full configurations is in my
+[https://raw.github.com/grwlf/nixpkgs/local/machines/samsung-np900x3c-v2.nix nixpkgs branch]. Note, it requires several local packages.
+
+=== Touchpad ===
+
+Touchpad is detected as 'ETPS/2 Elantech Touchpad'. xf86-input-synaptics handles
+it well. Corresponding config lines:
+
+<pre>
+  services.xserver = {
+    synaptics = {
+      enable = true;
+      accelFactor = "0.05";
+      maxSpeed = "10";
+      twoFingerScroll = true;
+      additionalOptions =
+        ''
+        MatchProduct "ETPS"
+        Option "FingerLow"                 "3"
+        Option "FingerHigh"                "5"
+        Option "FingerPress"               "30"
+        Option "MaxTapTime"                "100"
+        Option "MaxDoubleTapTime"          "150"
+        Option "FastTaps"                  "1"
+        Option "VertTwoFingerScroll"       "1"
+        Option "HorizTwoFingerScroll"      "1"
+        Option "TrackstickSpeed"           "0"
+        Option "LTCornerButton"            "3"
+        Option "LBCornerButton"            "2"
+        Option "CoastingFriction"          "20"
+        '';
+      };
+  };
+</pre>
+
+=== Wireless ===
+
+System requires iwlwifi-6000g2b-6.ucode in order to work. I've extracted the
+file from some debian package and placed it into /root/firmware. Corresponding
+config settings:
+
+<pre>
+  hardware.firmware = [ "/root/firmware" ];
+</pre>
+
+= Problems =
+There are some. See
+
+* [http://ubuntuforums.org/showthread.php?t=1737086 Ubuntu thread]
+* [http://bugzilla.kernel.org/show_bug.cgi?id=44161 Kernel.org bug]
+* [http://jablonskis.org/2012/linux-and-samsung-series-laptop-9-fn-keys/ jablonskis.org]
+
+=== BIOS problems ===
+I had to disable SSD boot completely in order to boot from USB. Just changing boot priority didn't help.
+
+Fix: Update BIOS up to recent version
+
+=== Battery ===
+Battery charging/discharging indicator doesn't work good.
+
+Fix: Update BIOS up to recent version
+
+=== Lid ===
+Acpi thinks lid is always open
+<pre>
+[ierton@greyblade:~]$ cat /proc/acpi/button/lid/LID0/state 
+state:      open
+</pre>
+
+Related [https://bugzilla.kernel.org/show_bug.cgi?id=44161 Kernel bug #44161]
+
+=== Multimedia keys ===
+* rfkill/fanless don't work
+* volume up/down don't work
+* brightness up/down work, but release is broken
+* touchpad disable works
+
+Related discussion on
+[http://jablonskis.org/2012/linux-and-samsung-series-laptop-9-fn-keys/ jablonskis.org]
+
diff --git a/modules/nixos-hardware/samsung/np900x3c/default.nix b/modules/nixos-hardware/samsung/np900x3c/default.nix
new file mode 100644
index 000000000000..ced3d7013dd8
--- /dev/null
+++ b/modules/nixos-hardware/samsung/np900x3c/default.nix
@@ -0,0 +1,7 @@
+# TODO: use ../../common/pc/laptop
+
+{ lib, ... }:
+
+{
+  services.xserver.synaptics.enable = lib.mkDefault true;
+}
diff --git a/modules/nixos-hardware/supermicro/a1sri-2758f/default.nix b/modules/nixos-hardware/supermicro/a1sri-2758f/default.nix
new file mode 100644
index 000000000000..3ffdc0c6e6d2
--- /dev/null
+++ b/modules/nixos-hardware/supermicro/a1sri-2758f/default.nix
@@ -0,0 +1,9 @@
+# http://www.supermicro.com/products/motherboard/Atom/X10/A1SRi-2758F.cfm
+# This board contains a TPM header, but you must supply your own module.
+
+{ pkgs, ... }:
+
+{
+  boot.kernelModules = [ "ipmi_devintf" "ipmi_si" ];
+  environment.systemPackages = [ pkgs.ipmitool ];
+}
diff --git a/modules/nixos-hardware/supermicro/x10sll-f/default.nix b/modules/nixos-hardware/supermicro/x10sll-f/default.nix
new file mode 100644
index 000000000000..10c6cc016035
--- /dev/null
+++ b/modules/nixos-hardware/supermicro/x10sll-f/default.nix
@@ -0,0 +1,12 @@
+{ config, pkgs, ... }:
+
+{
+  environment.systemPackages = with pkgs; [ ipmitool ];
+
+  boot.kernelModules = [ "jc42" "ipmi_devintf" "ipmi_si" "tpm_rng" ];
+
+  # services.cron.systemCronJobs = [
+  #   # Reset 5-minute watchdog timer every minute
+  #   "* * * * * ${pkgs.ipmitool}/bin/ipmitool raw 0x30 0x97 1 5"
+  # ];
+}
diff --git a/modules/nixos-hardware/tests/build-profile.nix b/modules/nixos-hardware/tests/build-profile.nix
new file mode 100644
index 000000000000..c4509d36ad33
--- /dev/null
+++ b/modules/nixos-hardware/tests/build-profile.nix
@@ -0,0 +1,19 @@
+{ profile }:
+
+let
+  shim = {
+    boot.loader.systemd-boot.enable = true;
+
+    fileSystems."/" = {
+      device = "/dev/disk/by-uuid/00000000-0000-0000-0000-000000000000";
+      fsType = "btrfs";
+    };
+
+    nixpkgs.config = {
+      allowBroken = true;
+      allowUnfree = true;
+    };
+  };
+in (import <nixpkgs/nixos> {
+  configuration.imports = [ profile shim ];
+}).system
diff --git a/modules/nixos-hardware/tests/run.py b/modules/nixos-hardware/tests/run.py
new file mode 100755
index 000000000000..653337d19190
--- /dev/null
+++ b/modules/nixos-hardware/tests/run.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env nix-shell
+#!nix-shell -p nix -p python3 -i python
+
+import argparse
+import multiprocessing
+import re
+import subprocess
+import sys
+from pathlib import Path
+from typing import List, Tuple
+
+TEST_ROOT = Path(__file__).resolve().parent
+ROOT = TEST_ROOT.parent
+
+GREEN = "\033[92m"
+RED = "\033[91m"
+RESET = "\033[0m"
+
+
+def parse_readme() -> List[str]:
+    profiles = set()
+    with open(ROOT.joinpath("README.md")) as f:
+        for line in f:
+            results = re.findall(r"<nixos-hardware/[^>]+>", line)
+            profiles.update(results)
+    return list(profiles)
+
+
+def build_profile(profile: str) -> Tuple[str, subprocess.CompletedProcess]:
+    # Hard-code this for now until we have enough other architectures to care about this.
+    system = "x86_64-linux"
+    if "raspberry-pi/2" in profile:
+        system = "armv7l-linux"
+
+    cmd = [
+        "nix-build",
+        "-I",
+        f"nixos-hardware={ROOT}",
+        "--dry-run",
+        "--show-trace",
+        "build-profile.nix",
+        "--system",
+        system,
+        "--arg",
+        "profile",
+        profile,
+    ]
+    res = subprocess.run(
+        cmd, cwd=TEST_ROOT, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
+    )
+    return (profile, res)
+
+
+def parse_args() -> argparse.Namespace:
+    parser = argparse.ArgumentParser(description="Run hardware tests")
+    parser.add_argument(
+        "--jobs",
+        type=int,
+        default=multiprocessing.cpu_count(),
+        help="Number of parallel evaluations."
+        "If set to 1 it disable multi processing (suitable for debugging)",
+    )
+    parser.add_argument("profiles", nargs="*")
+    return parser.parse_args()
+
+
+def main() -> None:
+    args = parse_args()
+    if len(args.profiles) == 0:
+        profiles = parse_readme()
+    else:
+        profiles = args.profiles
+
+    failed_profiles = []
+
+    def eval_finished(args: Tuple[str, subprocess.CompletedProcess]) -> None:
+        profile, res = args
+        if res.returncode == 0:
+            print(f"{GREEN}OK {profile}{RESET}")
+        else:
+            print(f"{RED}FAIL {profile}:{RESET}", file=sys.stderr)
+            if res.stdout != "":
+                print(f"{RED}{res.stdout.rstrip()}{RESET}", file=sys.stderr)
+            print(f"{RED}{res.stderr.rstrip()}{RESET}", file=sys.stderr)
+            failed_profiles.append(profile)
+
+    if len(profiles) == 0 or args.jobs == 1:
+        for profile in profiles:
+            eval_finished(build_profile(profile))
+    else:
+        pool = multiprocessing.Pool(processes=args.jobs)
+        for r in pool.imap(build_profile, profiles):
+            eval_finished(r)
+    if len(failed_profiles) > 0:
+        print(f"\n{RED}The following {len(failed_profiles)} test(s) failed:{RESET}")
+        for profile in failed_profiles:
+            print(f"{sys.argv[0]} '{profile}'")
+        sys.exit(1)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/modules/nixos-hardware/toshiba/swanky/README.md b/modules/nixos-hardware/toshiba/swanky/README.md
new file mode 100644
index 000000000000..ab4c072c6b09
--- /dev/null
+++ b/modules/nixos-hardware/toshiba/swanky/README.md
@@ -0,0 +1,66 @@
+# Toshiba Chromebook 2 `swanky`
+
+There might be a way to install NixOS without hardware modifications (via
+enabled developer mode and SeaBIOS boot on Ctrl+L), however I'd highly
+recommend replacing Google's Coreboot payload with Tianocore: that allows for
+proper virtualization, suspend, removes annoying developer mode screen, and
+generally just works much better.
+
+These instructions carry some risk of bricking your device, since you'll be
+reflashing BIOS. Risk is rather low, but please for the love of god make a BIOS
+backup and store it someplace safe. That's the only way to reinstall ChromeOS
+back after this procedure (BIOS image has licensing info), and it's much easier
+to use a known good state to unbrick the laptop if things go wrong.
+
+If you ever get unlucky, you can unbrick your device using Raspberry Pi,
+some cables and a SOIC clip, see:
+http://sicarul.com/how-to-un-brick-your-toshiba-chromebook-2-gandof-without-invoking-any-demons/
+
+## Enable developer mode
+
+This will wipe all user data and settings from the laptop.
+
+Power off, then hold ESC + Refresh (F3) and abruptly press power button. You
+should see "Chrome OS is missing or damaged" message. Press Ctrl+D. Press enter
+at the next screen, then press Ctrl+D again. Wait until the laptop boots into
+ChromeOS, then power it off.
+
+## Disable hardware-backed BIOS write protection
+
+Follow the first part of the guide at:
+https://github.com/brendenyule/NativeToshibaCB2Guide/wiki/Remove-Write-Protect
+
+Ignore SeaBIOS section. I also used some ductape over #5 to make sure that
+metallic motherboard shield would not re-enable write protection.
+
+## Flash Coreboot + Tianocore BIOS
+
+Prepare a FAT32-formatted flash drive for BIOS backup in advance.
+
+Go through ChromeOS installation dialogues until you have network access and
+are able to log into a guest session. Open Chrome, press Ctrl+Alt+T to open
+`crosh`, type in `shell` to get a real shell. Then, run:
+
+```
+$ cd ~
+$ curl -LO https://mrchromebox.tech/firmware-util.sh
+$ sudo bash firmware-util.sh
+```
+
+Choose "Install/Update Full ROM Firmware" option and follow instructions.
+Do not skip BIOS backup!
+
+Documentation: https://mrchromebox.tech/#fwscript
+
+## Enable hardware-backed BIOS write protection
+
+This is a cool security feature, so after flashing Coreboot + Tianocore BIOS
+and making sure new BIOS works, consider re-enabling BIOS protection. Just put
+in the missing screw #5.
+
+## Install NixOS
+
+`dd` an image on a flash drive, partition the drive, etc. On some later models,
+you can swap SSD with any other 2242 M.2 SATA SSD, but on `swanky`, you have
+to live with what you have (16GB eMMC). I recommend `256MB` for EFI partition,
+and the rest for `/`.
diff --git a/modules/nixos-hardware/toshiba/swanky/default.nix b/modules/nixos-hardware/toshiba/swanky/default.nix
new file mode 100644
index 000000000000..8361df66e53f
--- /dev/null
+++ b/modules/nixos-hardware/toshiba/swanky/default.nix
@@ -0,0 +1,27 @@
+{ lib, pkgs, ... }:
+
+let
+  ucm = pkgs.fetchzip {
+    url = "https://github.com/plbossart/UCM/archive/2050ca78a4d1a853d1ba050b591f42e6f97adfc0.tar.gz";
+    sha256 = "1rs4mpz3b965nmz0yhy6j4ga3fdz320qnpkd7d61nvpv9c3i6zwj";
+  };
+in
+
+{
+  imports = [
+    ../../common/cpu/intel
+    ../../common/pc/laptop
+  ];
+
+  # Required for screen brightness control:
+  boot.kernelParams = [ "acpi_backlight=vendor" ];
+
+  # Sound requires a custom UCM config:
+  system.replaceRuntimeDependencies = [{
+    original = pkgs.alsaLib;
+
+    replacement = pkgs.alsaLib.overrideAttrs (super: {
+      postFixup = "cp -r ${ucm}/chtmax98090 $out/share/alsa/ucm";
+    });
+  }];
+}
diff --git a/modules/nixos-hardware/tuxedo/infinitybook/v4/default.nix b/modules/nixos-hardware/tuxedo/infinitybook/v4/default.nix
new file mode 100644
index 000000000000..36450bf52680
--- /dev/null
+++ b/modules/nixos-hardware/tuxedo/infinitybook/v4/default.nix
@@ -0,0 +1,5 @@
+{
+  boot.kernelParams = [
+    "i8042.reset"
+  ];
+}
diff --git a/modules/server/default.nix b/modules/server/default.nix
new file mode 100644
index 000000000000..c7eb390d01ca
--- /dev/null
+++ b/modules/server/default.nix
@@ -0,0 +1,14 @@
+{ pkgs, ... }:
+
+{
+  imports = [ ../nix ../ssh ../users ];
+
+  services.openssh.enable = true;
+  security.sudo.wheelNeedsPassword = false;
+
+  networking.firewall.logRefusedConnections = true;
+
+  i18n.defaultLocale = "C.utf8";
+
+  environment.systemPackages = with pkgs; [ htop ncdu ];
+}
diff --git a/modules/server/dns/default.nix b/modules/server/dns/default.nix
new file mode 100644
index 000000000000..6274d6b6b348
--- /dev/null
+++ b/modules/server/dns/default.nix
@@ -0,0 +1,13 @@
+{ pkgs, ... }:
+
+{
+  networking.nameservers = [ "127.0.0.1" ];
+
+  services.unbound.enable = true;
+
+  systemd.services.doh-proxy = {
+    after = [ "networking.target" ];
+    wantedBy = [ "multi-user.target" ];
+    script = "exec ${pkgs.doh-proxy}/bin/doh-httpproxy --port 4448";
+  };
+}
diff --git a/modules/server/ftp/default.nix b/modules/server/ftp/default.nix
new file mode 100644
index 000000000000..78f7c794ce1f
--- /dev/null
+++ b/modules/server/ftp/default.nix
@@ -0,0 +1,47 @@
+{ pkgs, lib, config, ... }:
+
+with lib;
+
+let
+  cfg = config.ftp;
+in
+
+{
+  options = {
+    ftp = {
+      files = mkOption {
+        default = {};
+        type = with types; attrsOf path;
+        description = ''
+          Files to serve on https://ftp.qyliss.net/
+        '';
+        example = literalExample ''
+          {
+            "foo/bar.txt" = pkgs.writeText "bar.txt" ''''
+              Hello, world!
+            '''';
+          }
+        '';
+      };
+    };
+  };
+
+  config = {
+    services.nginx.virtualHosts."ftp.qyliss.net" = {
+      forceSSL = true;
+      enableACME = true;
+
+      root = pkgs.runCommandNoCC "ftp.qyliss.net" {} ''
+        mkdir $out
+        ${concatStrings (mapAttrsToList (httpPath: diskPath: ''
+          mkdir -p "$out/$(dirname ${escapeShellArg httpPath})"
+          ln -s ${escapeShellArg diskPath} $out/${escapeShellArg httpPath}
+        '') cfg.files)}
+      '';
+
+      extraConfig = ''
+        autoindex on;
+      '';
+    };
+  };
+}
diff --git a/modules/server/irc/bitlbee/default.nix b/modules/server/irc/bitlbee/default.nix
new file mode 100644
index 000000000000..c0aaaa0b4f17
--- /dev/null
+++ b/modules/server/irc/bitlbee/default.nix
@@ -0,0 +1,6 @@
+{ ... }:
+
+{
+  services.bitlbee.enable = true;
+  services.bitlbee.portNumber = 6262;
+}
diff --git a/modules/server/irc/default.nix b/modules/server/irc/default.nix
new file mode 100644
index 000000000000..f031773efacd
--- /dev/null
+++ b/modules/server/irc/default.nix
@@ -0,0 +1,5 @@
+{ ... }:
+
+{
+  imports = [ ./bitlbee ./znc ];
+}
diff --git a/modules/server/irc/znc/default.nix b/modules/server/irc/znc/default.nix
new file mode 100644
index 000000000000..76cce01180a8
--- /dev/null
+++ b/modules/server/irc/znc/default.nix
@@ -0,0 +1,7 @@
+{ pkgs, ... }:
+
+{
+  services.znc.enable = true;
+  services.znc.useLegacyConfig = false;
+  services.znc.modulePackages = with pkgs; [ zncModules.playback ];
+}
diff --git a/modules/server/nginx/default.nix b/modules/server/nginx/default.nix
new file mode 100644
index 000000000000..2344197bcff6
--- /dev/null
+++ b/modules/server/nginx/default.nix
@@ -0,0 +1,21 @@
+{ pkgs, ... }:
+
+{
+  services.fcgiwrap.enable = true;
+
+  services.nginx.enable = true;
+  services.nginx.package = pkgs.nginxMainline;
+
+  services.nginx.recommendedOptimisation = true;
+  services.nginx.recommendedTlsSettings = true;
+  services.nginx.recommendedGzipSettings = true;
+  services.nginx.recommendedProxySettings = true;
+
+  services.nginx.commonHttpConfig = ''
+    log_format privacy '[$time_local] $request_method '
+                       '$scheme://$host$request_uri $status $body_bytes_sent';
+
+    # systemd catches syslog, and access_log doesn't support stdout/stderr.
+    access_log syslog:server=unix:/dev/log privacy;
+  '';
+}
diff --git a/modules/server/tor/default.nix b/modules/server/tor/default.nix
new file mode 100644
index 000000000000..efd534bc4464
--- /dev/null
+++ b/modules/server/tor/default.nix
@@ -0,0 +1,23 @@
+{ lib, config, ... }:
+
+{
+  networking.firewall.allowedTCPPorts =
+    let
+      port = config.services.tor.relay.port;
+    in
+      lib.optional (lib.isInt port && port > 0) port;
+
+  services.tor.enable = true;
+
+  services.tor.relay.accountingMax =
+    lib.mkDefault (throw "Set tor accountingMax!!");
+
+  services.tor.relay.accountingStart =
+    lib.mkDefault (throw "Set tor accountingStart!!");
+
+  services.tor.relay.enable = true;
+  services.tor.relay.contactInfo = "hi@alyssa.is";
+  services.tor.relay.nickname = lib.mkDefault config.networking.hostName;
+  services.tor.relay.port = 143;
+  services.tor.relay.role = "relay";
+}
diff --git a/modules/server/xmpp/default.nix b/modules/server/xmpp/default.nix
new file mode 100644
index 000000000000..9be174cc7edd
--- /dev/null
+++ b/modules/server/xmpp/default.nix
@@ -0,0 +1,23 @@
+{ pkgs, ... }:
+
+{
+  networking.firewall.allowedTCPPorts = [ 5222 5269 ];
+
+  services.prosody.enable = true;
+  services.prosody.modules.http_files = true;
+  services.prosody.modules.mam = true;
+  services.prosody.s2sSecureAuth = true;
+  services.prosody.package = pkgs.prosody.override {
+    withCommunityModules = [ "http_upload" "smacks" "csi" "cloud_notify" ];
+  };
+  services.prosody.ssl.key = "/var/lib/acme/qyliss.net/key.pem";
+  services.prosody.ssl.cert = "/var/lib/acme/qyliss.net/fullchain.pem";
+  services.prosody.virtualHosts."qyliss.net" = {
+    domain = "qyliss.net";
+    enabled = true;
+    ssl.key = "/var/lib/acme/qyliss.net/key.pem";
+    ssl.cert = "/var/lib/acme/qyliss.net/fullchain.pem";
+  };
+
+  users.users.prosody.extraGroups = [ "tls" ];
+}
diff --git a/modules/shell/cargo/default.nix b/modules/shell/cargo/default.nix
new file mode 100644
index 000000000000..246571c33e60
--- /dev/null
+++ b/modules/shell/cargo/default.nix
@@ -0,0 +1,9 @@
+{ ... }:
+
+{
+  environment.extraInit = ''
+    export CARGO_HOME="$HOME/state/cargo"
+  '';
+
+  home.qyliss.dirs."state/cargo" = {};
+}
diff --git a/modules/shell/default.nix b/modules/shell/default.nix
new file mode 100644
index 000000000000..a75c23853819
--- /dev/null
+++ b/modules/shell/default.nix
@@ -0,0 +1,40 @@
+{ pkgs, ... }:
+
+{
+  imports = [
+    ./cargo ./direnv ./git ./kakoune ./less ./lynx ./pass ./tmux ./zsh
+  ];
+
+  environment.systemPackages = with pkgs; [
+    (aspellWithDicts (dicts: with dicts; [ en en-computers en-science eo ]))
+    coreutils-prefixed
+    curl
+    execline
+    file
+    ffsend
+    fzf
+    gnused
+    gotop
+    htop
+    httpie
+    jq
+    moreutils
+    ncdu
+    neovim
+    nmap
+    openssh
+    pv
+    ranger
+    silver-searcher
+    traceroute
+    tree
+    units
+    unixtools.watch
+    wget
+    whois
+  ] ++ lib.optional stdenv.isDarwin pinentry_mac;
+
+  environment.variables.CLICOLOR = "1";
+  environment.variables.EDITOR = "kak";
+  environment.variables.EMAIL = "hi@alyssa.is";
+}
diff --git a/modules/shell/direnv/default.nix b/modules/shell/direnv/default.nix
new file mode 100644
index 000000000000..951d79870b49
--- /dev/null
+++ b/modules/shell/direnv/default.nix
@@ -0,0 +1,7 @@
+{ pkgs, ... }:
+
+{
+  environment.systemPackages = with pkgs; [ direnv ];
+
+  home.qyliss.dirs."state/direnv" = {};
+}
diff --git a/modules/shell/git/attributes b/modules/shell/git/attributes
new file mode 100644
index 000000000000..57ea092a232a
--- /dev/null
+++ b/modules/shell/git/attributes
@@ -0,0 +1,2 @@
+*.json diff=json
+*.json.mozlz4 diff=mozlz4
diff --git a/modules/shell/git/config.in b/modules/shell/git/config.in
new file mode 100644
index 000000000000..2f682170c614
--- /dev/null
+++ b/modules/shell/git/config.in
@@ -0,0 +1,50 @@
+[alias]
+    ignore = "!gi() { curl -L -s https://www.gitignore.io/api/$@ ;}; gi"
+    unbranch = "!git branch --sort -HEAD --merged | sed 1d | xargs git branch -d"
+[branch]
+    autosetuprebase = always
+[color "branch"]
+    current = yellow reverse
+    local = yellow
+    remote = green
+[color "diff"]
+    frag = magenta bold
+    meta = yellow bold
+    new = green bold
+    old = red bold
+[color "status"]
+    added = yellow
+    changed = green
+    untracked = cyan
+[commit]
+    gpgsign = true
+    verbose = true
+[core]
+    attributesfile = @attributesfile@
+    excludesfile = @ignorefile@
+[credential]
+    helper = osxkeychain
+[diff]
+    compactionHeuristic = true
+    renameLimit = 0
+    wsErrorHighlight = all
+[diff "mozlz4"]
+    textconv = @mozlz4_textconv@
+[filter "lfs"]
+    clean = git-lfs clean -- %f
+    required = true
+    smudge = git-lfs smudge -- %f
+[format]
+    pretty = format:%Cred%h %C(magenta)%G? %Cgreen(%ar)%Creset -%C(bold red)%d%Creset %s %C(bold blue)<%aN>%Creset
+[grep]
+    lineNumber = true
+    patternType = perl
+[help]
+    autocorrect = 1
+[hub]
+    protocol = ssh
+[merge]
+    conflictstyle = diff3
+    tool = opendiff
+[push]
+    default = upstream
diff --git a/modules/shell/git/default.nix b/modules/shell/git/default.nix
new file mode 100644
index 000000000000..94a89b316275
--- /dev/null
+++ b/modules/shell/git/default.nix
@@ -0,0 +1,32 @@
+{ pkgs, ... }:
+
+let
+  mozlz4_textconv = pkgs.substituteAll {
+    src = diff/mozlz4.in;
+    isExecutable = true;
+
+    inherit (pkgs) execline mozlz4a;
+  };
+
+  config = pkgs.substituteAll {
+    src = ./config.in;
+
+    inherit mozlz4_textconv;
+    python = pkgs.python3;
+    attributesfile = ./attributes;
+    ignorefile = ./ignore;
+    preferLocalBuild = true;
+  };
+
+in
+{
+  imports = [ ../../xdg ];
+
+  environment.systemPackages = with pkgs; with gitAndTools; [
+    (git.override { svnSupport = true; sendEmailSupport = true; })
+    git-absorb
+    git-remote-hg
+  ];
+
+  users.users.qyliss.xdg.config.paths."git/config" = config;
+}
diff --git a/modules/shell/git/diff/mozlz4.in b/modules/shell/git/diff/mozlz4.in
new file mode 100644
index 000000000000..4377e69526c7
--- /dev/null
+++ b/modules/shell/git/diff/mozlz4.in
@@ -0,0 +1,4 @@
+#! @execline@/bin/execlineb -S1
+
+pipeline { @mozlz4a@/bin/mozlz4a -d $1 /dev/stdout }
+@python@/bin/python -m json.tool
diff --git a/modules/shell/git/ignore b/modules/shell/git/ignore
new file mode 100644
index 000000000000..bea5e703b81b
--- /dev/null
+++ b/modules/shell/git/ignore
@@ -0,0 +1,27 @@
+# macOS
+.DS_Store
+
+# Kakoune
+*.kak.*
+
+# Sublime Text
+sublime-project
+sublime-workspace
+
+# IntelliJ IDEA
+.idea
+*.iml
+
+# Bundler
+gem_graph.png
+
+# Direnv
+.envrc
+
+# ctags
+/tags
+
+# Emacs
+*~
+\#*\#
+.\#*
\ No newline at end of file
diff --git a/modules/shell/kakoune/default.nix b/modules/shell/kakoune/default.nix
new file mode 100644
index 000000000000..8831d628f2b8
--- /dev/null
+++ b/modules/shell/kakoune/default.nix
@@ -0,0 +1,7 @@
+{ pkgs, ... }:
+
+{
+  environment.systemPackages = with pkgs; [ kakoune ];
+
+  users.users.qyliss.xdg.config.paths."kak/kakrc" = pkgs.copyPathToStore ./kakrc;
+}
diff --git a/modules/shell/kakoune/kakrc b/modules/shell/kakoune/kakrc
new file mode 100644
index 000000000000..9b670c7eb824
--- /dev/null
+++ b/modules/shell/kakoune/kakrc
@@ -0,0 +1,86 @@
+# Highlight any files whose names start with "zsh" as sh
+hook global BufCreate (.*/)?\.?zsh.* %{
+  set-option buffer filetype sh
+}
+
+# Highlight files ending in .conf as ini
+# (Will probably be close enough)
+hook global BufCreate .*\.conf %{
+  set-option buffer filetype ini
+}
+
+# Highlight files ending in .html.erb as html
+hook global BufCreate .*\.html.erb %{
+  set-option buffer filetype html
+}
+
+# Strip trailing whitespace on save
+hook global BufWritePre .*(?<!\.diff)$ %{
+  try %{execute-keys -draft %{%s\h+$<ret>d}}
+  echo
+}
+
+hook global WinSetOption filetype=mail %{
+  set-option buffer autowrap_column 72
+  autowrap-enable
+}
+
+define-command -docstring "import from the system clipboard" clipboard-import %{
+  set-register dquote %sh{pbpaste}
+  echo -markup "{Information}imported system clipboard to register """
+}
+
+define-command -docstring "export to the system clipboard" clipboard-export %{
+  nop %sh{ printf "%s" "$kak_main_reg_dquote" | pbcopy }
+  echo -markup "{Information}exported register "" to system clipboard"
+}
+
+define-command -docstring "open a file using fzf" open %{
+  edit %sh{git ls-files -oc --exclude-standard | choose}
+}
+alias global o open
+
+define-command -params 1.. -docstring "change file modes" chmod %{
+  nop %sh{ chmod $@ "$kak_buffile" }
+}
+
+define-command mkdirp %{
+  nop %sh{mkdir -p "$(dirname "$kak_buffile")"}
+}
+
+define-command mkdirpw %{
+  mkdirp
+  write
+}
+
+map global normal <a-Y> :clipboard-import<ret>
+map global normal <a-y> :clipboard-export<ret>
+
+map global normal / /(?i)
+
+# Allow indenting from any position in the line, with the correct indentation.
+map global insert <tab> '<a-;><a-gt>'
+map global insert <s-tab> '<a-;><lt>'
+
+map -docstring "Remove Symbol hashrockets" global user \
+  <gt> 's:\S+<space>+=<gt><ret>;<a-?><space>+<ret>c:<esc>h<a-/>:<ret>d'
+
+map -docstring "Convert single quotes to double quotes" global user \
+  %{'} %{s'<ret>r"}
+
+map -docstring "Toggle line comments" global user \
+  %{#} %{:comment-line<ret>}
+
+declare-user-mode bookmarks
+map -docstring "Edit bookmarked file" global user b \
+  %{:enter-user-mode bookmarks<ret>}
+
+map -docstring "wiki" global bookmarks w %{:edit-wiki-index<ret>}
+
+set-option global indentwidth 2
+set-option global ui_options ncurses_assistant=none
+set-option global scrolloff 5,5
+set-option global autoreload yes
+
+add-highlighter global/ number-lines -hlcursor -separator "│"
+add-highlighter global/ show-matching
diff --git a/modules/shell/less/default.nix b/modules/shell/less/default.nix
new file mode 100644
index 000000000000..bf6637ee3a7e
--- /dev/null
+++ b/modules/shell/less/default.nix
@@ -0,0 +1,7 @@
+{ pkgs, ... }:
+
+{
+  environment.systemPackages = with pkgs; [ less ];
+
+  environment.variables.LESS = "cRS"; # use whole terminal, color, don't wrap
+}
diff --git a/modules/shell/lynx/default.nix b/modules/shell/lynx/default.nix
new file mode 100644
index 000000000000..0e1262b07a7d
--- /dev/null
+++ b/modules/shell/lynx/default.nix
@@ -0,0 +1,16 @@
+{ pkgs, ... }:
+
+{
+  environment.extraInit = ''
+    export LYNX_CFG="/etc/xdg/nixos/per-user/$USER/lynx/lynx.cfg"
+    export LYNX_LSS="/etc/xdg/nixos/per-user/$USER/lynx/lynx.lss"
+  '';
+
+  environment.systemPackages = with pkgs; [ lynx ];
+
+  users.users.qyliss.xdg.config.paths."lynx/lynx.cfg" =
+    pkgs.copyPathToStore ./lynx.cfg;
+
+  users.users.qyliss.xdg.config.paths."lynx/lynx.lss" =
+    pkgs.copyPathToStore ./lynx.lss;
+}
diff --git a/modules/shell/lynx/lynx.cfg b/modules/shell/lynx/lynx.cfg
new file mode 100644
index 000000000000..3359b95ce9af
--- /dev/null
+++ b/modules/shell/lynx/lynx.cfg
@@ -0,0 +1,17 @@
+STARTFILE:https://search.weho.st/
+DEFAULT_INDEX_FILE:https://search.weho.st/
+SAVE_SPACE:/tmp
+URL_DOMAIN_PREFIXES:
+URL_DOMAIN_SUFFIXES:
+SHOW_CURSOR:TRUE
+UNDERLINE_LINKS:TRUE
+SOURCE_CACHE:MEMORY
+FORCE_SSL_COOKIES_SECURE:TRUE
+SYSTEM_MAIL:neomutt
+VI_KEYS_ALWAYS_ON:TRUE
+DEFAULT_KEYPAD_MODE:LINKS_AND_FIELDS_ARE_NUMBERED
+MAKE_LINKS_FOR_ALL_IMAGES:TRUE
+XLOADIMAGE_COMMAND:imv %s &
+EXTERNAL:mailto:neomutt %s:TRUE:TRUE
+TEXTFIELDS_NEED_ACTIVATION:TRUE
+CHARACTER_SET:utf-8
diff --git a/modules/shell/lynx/lynx.lss b/modules/shell/lynx/lynx.lss
new file mode 100644
index 000000000000..cf0d3265e00c
--- /dev/null
+++ b/modules/shell/lynx/lynx.lss
@@ -0,0 +1,105 @@
+# Normal type styles correspond to HTML tags.
+#
+# The next line (beginning with "em") means:  use bold if mono, otherwise
+# brightblue on <defaultbackground>
+em:		bold:			brightblue
+strong:		bold:			brightred
+b:		bold:			red
+i:		bold:			brightblue
+a:		bold:			green
+img:		dim:			brown
+fig:		normal:			gray
+caption:	reverse:		brown
+hr:		normal:			yellow
+blockquote:	normal:			brightblue
+ul:		normal:			brown
+address:	normal:			magenta
+title:		normal:			magenta
+tt:		dim:			brightmagenta:	black
+h1:		bold:			yellow:		blue
+label:		normal:			magenta
+q:		normal:			yellow:		magenta
+small:		dim:			default
+big:		bold:			yellow
+sup:		bold:			yellow
+sub:		dim:			gray
+li:		normal:			magenta
+code:		normal:			cyan
+cite:		normal:			cyan
+
+table:		normal:			brightcyan
+tr:		bold:			brown
+td:		normal:			default
+br:		normal:			default
+
+# Special styles - not corresponding directly to HTML tags
+#	alert	- status bar, when message begins "Alert".
+#	alink	- active link
+#	normal	- default attributes
+#	status	- status bar
+#	whereis	- whereis search target
+#
+#normal:normal:default:blue
+alink:		reverse:		yellow:		black
+status:		reverse:		yellow:		blue
+alert:		bold:			yellow:		red
+whereis:	reverse+underline:	magenta:	cyan
+# currently not used
+#value:normal:green
+
+menu.bg:	normal:			black:		lightgray
+menu.frame:	normal:			black:		lightgray
+menu.entry:	normal:			lightgray:	black
+menu.n:		normal:			red:		gray
+menu.active:	normal:			yellow:		black
+menu.sb:	normal:			brightred:	lightgray
+
+forwbackw.arrow:reverse
+hot.paste:	normal:			brightred:	gray
+
+# Styles with classes - <ul class=red> etc.
+ul.red:		underline:		brightred
+ul.blue:	bold:			brightblue
+li.red:		reverse:		red:		yellow
+li.blue:	bold:			blue
+strong.a:	bold:			black:		red
+em.a:		reverse:		black:		blue
+strong.b:	bold:			white:		red
+em.b:		reverse:		white:		blue
+strong.debug:	reverse:		green
+font.letter:	normal:			white:		blue
+input.submit:	normal:			cyan
+tr.baone:	bold:			yellow
+tr.batwo:	bold:			green
+tr.bathree:	bold:			red
+#
+# Special handling for link.
+link:		normal:			white
+link.green:	bold:			brightgreen
+link.red:	bold:			black:		red
+link.blue:	bold:			white:		blue
+link.toc:	bold:			black:		white
+# Special cases for link - the rel or title is appended after the class.
+# <link rel=next class=red href="1">
+link.red.next:	bold:			red
+link.red.prev:	bold:			yellow:		red
+link.blue.prev:	bold:			yellow:		blue
+link.blue.next:	bold:			blue
+link.green.toc:	bold:			white:		green
+#
+# Define styles that will be used when syntax highlighting is requested
+# (commandline option -prettysrc).
+span.htmlsrc_comment:normal:		white
+span.htmlsrc_tag:normal:		white
+#If you don't like that the tag name and attribute name are displayed
+#in different colors, comment the following line.
+span.htmlsrc_attrib:normal:		cyan
+span.htmlsrc_attrval:normal:		magenta
+span.htmlsrc_abracket:normal:		white
+span.htmlsrc_entity:normal:		white
+##span.htmlsrc_href:
+##span.htmlsrc_entire:
+span.htmlsrc_badseq:normal:		red
+span.htmlsrc_badtag:normal:		red
+span.htmlsrc_badattr:normal:		red
+span.htmlsrc_sgmlspecial:normal:	yellow
diff --git a/modules/shell/pass/default.nix b/modules/shell/pass/default.nix
new file mode 100644
index 000000000000..69312f2b9345
--- /dev/null
+++ b/modules/shell/pass/default.nix
@@ -0,0 +1,11 @@
+{ pkgs, ... }:
+
+{
+  environment.systemPackages = with pkgs; [ pass ];
+
+  environment.extraInit = ''
+    export PASSWORD_STORE_DIR="$HOME/state/pass"
+  '';
+
+  home.qyliss.dirs."state/pass" = {};
+}
diff --git a/modules/shell/tmux/config.nix b/modules/shell/tmux/config.nix
new file mode 100644
index 000000000000..d62a472bc1d8
--- /dev/null
+++ b/modules/shell/tmux/config.nix
@@ -0,0 +1,90 @@
+{ stdenv, writeText, writeShellScriptBin
+, coreutils, extract_url, reattach-to-user-namespace, ruby, tmux, zsh }:
+
+''
+set -gw activity-action none
+set -g  allow-rename off
+set -g  base-index 1
+${if stdenv.isDarwin then ''
+  set -g default-command "${reattach-to-user-namespace}/bin/reattach-to-user-namespace -l ${zsh}/bin/zsh"
+'' else ''
+  set -g default-command "${zsh}/bin/zsh -l"
+''}
+set -gs default-terminal screen-256color
+set -s  escape-time 0
+set -gw mode-keys vi
+set -gw monitor-activity on
+set -g  mouse on
+set -gw pane-active-border-style fg=default,bright
+set -gw pane-base-index 1
+set -gw pane-border-format ' #{pane_current_command} '
+set -gw pane-border-status top
+set -g  prefix C-a
+set -g  renumber-windows on
+set -g  status-left ""
+set -g  status-position top
+set -g  status-right ""
+set -ga status-right " #{?client_prefix, #[bright]#{prefix}#[nobright] ,}"
+# set -ga status-right ' #(cd #{pane_current_path} && zsh -c "print -P %%~") '
+# set -ga status-right '#(head="$(git -C #{pane_current_path} rev-parse --abbrev-ref HEAD 2>/dev/null)" && echo " $head ")'
+# set -ga status-right ' #(pmset -g batt | grep -o \\d*%%) '
+set -ga status-right " %d-%b-%y %H:%M"
+set -g  status-right-length 200
+set -g  status-style bg=default,fg=default
+set -gw window-status-style fg=default
+set -gw window-status-activity-style fg=default,underscore
+set -gw window-status-bell-style fg=brightcyan
+set -gw window-status-current-format '#W'
+set -gw window-status-current-style bright
+set -gw window-status-format '#W'
+set -gw window-status-separator '  '
+
+bind -n   F1  select-window -t :1
+bind -n   F2  select-window -t :2
+bind -n   F3  select-window -t :3
+bind -n   F4  select-window -t :4
+bind -n   F5  select-window -t :5
+bind -n   F6  select-window -t :6
+bind -n   F7  select-window -t :7
+bind -n   F8  select-window -t :8
+bind -n   F9  select-window -t :9
+bind -n   F10 select-window -t :10
+bind -n   F11 select-window -t :11
+bind -n   F12 select-window -t :12
+bind -n S-F1  select-window -t :13
+bind -n S-F2  select-window -t :14
+bind -n S-F3  select-window -t :15
+bind -n S-F4  select-window -t :16
+bind -n S-F5  select-window -t :17
+bind -n S-F6  select-window -t :18
+bind -n S-F7  select-window -t :19
+bind -n S-F8  select-window -t :20
+
+bind -n C-l send-keys C-l \; run-shell "sleep 0.3" \; clear-history
+bind    C-l send-keys -R \; clear-history
+
+bind '"' split-window -v -c '#{pane_current_path}'
+bind  %  split-window -h -c '#{pane_current_path}'
+
+bind C-a send-prefix
+
+unbind C-b
+
+set-hook -g after-select-pane "refresh-client -S"
+set-hook -g alert-activity "refresh-client -S"
+
+bind u run ${writeShellScriptBin "tmux-url" ''
+set -ue
+buffer="$(mktemp -t tmux-buffer)"
+open=${writeText "tmux-open-url.rb" ''
+#!${ruby}/bin/ruby
+require "cgi"
+exec "/usr/bin/open", CGI.unescape(ARGV[0]), *ARGV[1..-1]
+''}
+${tmux}/bin/tmux capture-pane -J
+${tmux}/bin/tmux save-buffer "$buffer"
+${tmux}/bin/tmux split-window -l 10 -f "${coreutils}/bin/tac $buffer | BROWSER=\"$open\"i ${extract_url}/bin/extract_url"
+''}/bin/tmux-url
+
+# bind u run "~/.config/tmux/url.sh"
+''
diff --git a/modules/shell/tmux/default.nix b/modules/shell/tmux/default.nix
new file mode 100644
index 000000000000..78bc447932eb
--- /dev/null
+++ b/modules/shell/tmux/default.nix
@@ -0,0 +1,8 @@
+{ pkgs, ... }:
+
+{
+  users.users.qyliss.xdg.config.paths."tmux.conf" =
+    with pkgs; writeText "tmux.conf" (callPackage ./config.nix { });
+
+  environment.systemPackages = with pkgs; [ tmux ];
+}
diff --git a/modules/shell/zsh/default.nix b/modules/shell/zsh/default.nix
new file mode 100644
index 000000000000..f784842b0ef7
--- /dev/null
+++ b/modules/shell/zsh/default.nix
@@ -0,0 +1,24 @@
+{ pkgs, config, ... }:
+
+{
+  environment.etc.zshrc.text = ''
+    unsetopt GLOBAL_RCS
+
+    if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]; then
+        . ${config.system.build.setEnvironment}
+    fi
+  '';
+
+  environment.extraInit = ''
+    export ZDOTDIR="/etc/xdg/nixos/per-user/$USER/zsh"
+  '';
+
+  home.qyliss.dirs."state/zsh" = {};
+
+  users.users.qyliss.shell = pkgs.zsh;
+
+  users.users.qyliss.xdg.config.paths."zsh/.zshrc" =
+    with pkgs; writeText "zshrc" (callPackage ./zshrc.nix {});
+
+  environment.systemPackages = with pkgs; [ zsh nix-zsh-completions ];
+}
diff --git a/modules/shell/zsh/zshrc.nix b/modules/shell/zsh/zshrc.nix
new file mode 100644
index 000000000000..31fefcfdc27d
--- /dev/null
+++ b/modules/shell/zsh/zshrc.nix
@@ -0,0 +1,206 @@
+{ stdenv, lib
+, any-nix-shell
+, coreutils
+, zsh-autosuggestions
+, zsh-history-substring-search
+, zsh-syntax-highlighting
+}:
+
+let
+  options = {
+    # Completion
+    always_to_end = true;
+    auto_name_dirs = true;
+    complete_in_word = true;
+    list_packed = true;
+
+    # Expansion and Globbing
+    bad_pattern = true;
+    brace_ccl = true;
+    extended_glob = true;
+    equals = true;
+    glob_star_short = true;
+    magic_equal_subst = true;
+    rc_expand_param = true;
+    rematch_pcre = true;
+
+    # Input/Output
+    correct = true;
+    mail_warning = true;
+    rc_quotes = true;
+    rm_star_wait = true;
+    short_loops = true;
+
+    # History
+    share_history = true;
+    bang_hist = true;
+    hist_allow_clobber = true;
+    hist_beep = true;
+    hist_expire_dups_first = true;
+    hist_ignore_dups = true;
+    hist_ignore_space = true;
+    hist_no_store = true;
+    hist_reduce_blanks = true;
+
+    # Prompting
+    prompt_sp = true;
+
+    # Scripts and Functions
+    c_bases = true;
+    c_precedences = true;
+    octal_zeroes = true;
+  };
+
+  enabledOptions = lib.attrNames (lib.filterAttrs (_: lib.id) options);
+  disabledOptions = lib.attrNames (lib.filterAttrs (_: v: !v) options);
+
+in ''
+
+. ${../../../nixpkgs/nixos/modules/programs/zsh/zinputrc}
+
+# Disable silver searcher numbers when piped or redirected.
+ag() {
+    if ! [ -t 1 ]; then
+        command ag --no-numbers "$@"
+    else
+        command ag "$@"
+    fi
+}
+
+fzf_ctrl_t() {
+    if git rev-parse >/dev/null 2>&1; then
+        git ls-files -co --exclude-standard
+    else
+        find . -type f | sed 's/^\.\///g'
+    fi
+}
+
+HISTFILE=~/state/zsh/history
+HISTSIZE=10000
+REPORTTIME=5
+SAVEHIST=9000
+ZSH_HIGHLIGHT_HIGHLIGHTERS=(main brackets)
+
+${lib.optionalString (enabledOptions == [])
+    "setopt ${lib.concatStringsSep " " enabledOptions}"}
+
+${lib.optionalString (disabledOptions == [])
+    "unsetopt ${lib.concatStringsSep " " enabledOptions}"}
+
+autoload -Uz colors && colors
+
+zstyle ':completion::complete:*' use-cache on
+zstyle ':completion::complete:*' cache-path "$XDG_CACHE_DIR/zsh/zcompcache"
+zstyle ':completion:*' auto-description '%d'
+zstyle ':completion:*' completer _expand _complete _ignored _match _correct \
+                                             _approximate _prefix
+zstyle ':completion:*' expand suffix
+zstyle ':completion:*' group-name '''
+zstyle ':completion:*' insert-unambiguous true
+zstyle ':completion:*' list-colors '''
+zstyle ':completion:*' list-suffixes true
+zstyle ':completion:*' matcher-list \
+    'm:{[:lower:]}={[:upper:]}' 'm:{[:lower:][:upper:]}={[:upper:][:lower:]}' \
+    'r:|[._-/]=* r:|=*' 'l:|=* r:|=*'
+zstyle ':completion:*' menu select=1
+zstyle ':completion:*' original false
+zstyle ':completion:*' preserve-prefix '//[^/]##/'
+zstyle ':completion:*' select-prompt \
+    %SScrolling active: current selection at %p%s
+zstyle ':completion:*' squeeze-slashes true
+zstyle ':completion:*' use-compctl true
+zstyle ':completion:*' verbose true
+
+autoload -U url-quote-magic
+zle -N self-insert url-quote-magic
+
+autoload -Uz compinit
+compinit -d "$XDG_CACHE_HOME/zsh/zcompdump"
+
+eval "$(${any-nix-shell}/bin/any-nix-shell zsh --info-right)"
+source ${zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
+source ${zsh-history-substring-search}/share/zsh-history-substring-search/zsh-history-substring-search.zsh
+source ${zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh
+
+zmodload zsh/complist
+bindkey -M menuselect "^[[Z" reverse-menu-complete
+
+zmodload zsh/terminfo
+bindkey "^[[A" history-substring-search-up
+bindkey "^[[B" history-substring-search-down
+
+bindkey "^[[3~" delete-char
+bindkey "^[[F" end-of-line
+bindkey "^[[H" beginning-of-line
+bindkey "^[[1~" beginning-of-line
+bindkey "^[[4~" end-of-line
+bindkey "^[^[[C" forward-word
+bindkey "^[^[[D" backward-word
+
+if [ -n "$TMUX" ]; then
+    tmux-page-up() { tmux copy-mode -ue }
+    tmux-page-down() { tmux send-keys -X page-down 2>/dev/null }
+    zle -N tmux-page-up
+    zle -N tmux-page-down
+    bindkey "^[[5~" tmux-page-up
+    bindkey "^[[6~" tmux-page-down
+fi
+
+autoload -U edit-command-line
+zle -N edit-command-line
+bindkey "^x^e" edit-command-line
+
+if [[ -v TMUX ]]; then
+    _tmux_hook() {
+        local exit_status="$?"
+        tmux set-environment "last_exit_status_$(tmux display-message -p '#D')" "$exit_status"
+        tmux refresh-client -S
+    }
+
+    typeset -ag precmd_functions;
+    if [[ -z $${precmd_functions[(r)_tmux_hook]} ]]; then
+        precmd_functions+=_tmux_hook;
+    fi
+fi
+
+${lib.optionalString stdenv.isDarwin ''
+    # Load SSH passphrases from the Keychain.
+    # Use an explicit path because upstream OpenSSH
+    # doesn't have the keychain functionality.
+    (/usr/bin/ssh-add -A &) 2> /dev/null
+''}
+
+if command -v fzf-share >/dev/null; then
+    source "$(fzf-share)/key-bindings.zsh"
+fi
+
+# Aliases
+alias beep='printf "\a"'
+alias ls=${lib.escapeShellArg (if stdenv.isDarwin then
+                                 "/bin/ls -AFh"
+                               else
+                                 "${coreutils}/bin/ls -AF --si --color=auto")}
+alias tree='tree -aRF -I .git'
+alias vim=nvim
+
+for command in cargo curl dig find git mutt ri wget; do
+    alias "$command=noglob $command"
+done
+
+# Prompt
+nl=$'\n'
+PS1="%F{yellow}%1(j.&%j .)%f%# "
+
+preexec() {
+    show_exit=1
+}
+
+precmd() {
+    ex="$?"
+    if [[ -n "$show_exit" && "$ex" -ne 0 ]]
+    then echo -e "\r\e[33m[exit $ex]\e[0m"
+    fi
+    unset show_exit
+}
+
+''
diff --git a/modules/ssh/default.nix b/modules/ssh/default.nix
new file mode 100644
index 000000000000..b3c29dd3666c
--- /dev/null
+++ b/modules/ssh/default.nix
@@ -0,0 +1,62 @@
+{ config, pkgs, lib, ... }:
+
+let
+  inherit (lib) concatStringsSep;
+
+  mkDefault = lib.mkOverride ((lib.mkDefault null).priority - 1);
+
+  # SSL added and removed here ;-)
+  bannedAlgorithms = [
+    "ecdsa-sha2-nistp256-cert-v01@openssh.com"
+    "ecdsa-sha2-nistp384-cert-v01@openssh.com"
+    "ecdsa-sha2-nistp521-cert-v01@openssh.com"
+    "ecdsa-sha2-nistp256"
+    "ecdsa-sha2-nistp384"
+    "ecdsa-sha2-nistp521"
+  ];
+in
+
+{
+  programs.mosh.enable = mkDefault config.services.openssh.enable;
+
+  programs.ssh.extraConfig = ''
+    CASignatureAlgorithms -${concatStringsSep "," bannedAlgorithms}
+    HostKeyAlgorithms -${concatStringsSep "," bannedAlgorithms}
+
+    Host uhura spock
+      HostName %h.edef.eu
+
+    Host hyperion
+      HostName %h.kookie.space
+
+    Host atuin
+      HostName %h.qyliss.net
+
+    Host github gitlab
+      HostName %h.com
+
+    Match host github.com,gitlab.com
+      User git
+  '';
+
+  services.openssh.authorizedKeysFiles = [ "${./keys}/%u.keys" ];
+  services.openssh.strictModes = false;
+
+  users.users.root.openssh.authorizedKeys.keyFiles = [ ./keys/qyliss.keys ];
+
+  programs.ssh.knownHosts = {
+    "github.com" = {
+      publicKeyFile = ./keys/github.keys;
+    };
+
+    "gitlab.com" = {
+      publicKeyFile = ./keys/gitlab.keys;
+    };
+
+    "edef" = {
+      certAuthority = true;
+      hostNames = [ "edef.eu" "*.edef.eu" ];
+      publicKeyFile = ./keys/edef.keys;
+    };
+  };
+}
diff --git a/modules/ssh/keys/edef.keys b/modules/ssh/keys/edef.keys
new file mode 100644
index 000000000000..f0eda71d5f2c
--- /dev/null
+++ b/modules/ssh/keys/edef.keys
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCvb/7ojfcbKvHIyjnrNUOOgzy44tCkgXY9HLuyFta1jQOE9pFIK19B4dR9bOglPKf145CCL0mSFJNNqmNwwavU2uRn+TQrW+U1dQAk8Gt+gh3O49YE854hwwyMU+xD6bIuUdfxPr+r5al/Ov5Km28ZMlHOs3FoAP0hInK+eAibioxL5rVJOtgicrOVCkGoXEgnuG+LRbOYTwzdClhRUxiPjK8alCbcJQ53AeZHO4G6w9wTr+W5ILCfvW4OmUXCX01sKzaBiQuuFCF6M/H4LlnsPWLMra2twXxkOIhZblwC+lncps9lQaUgiD4koZeOCORvHW00G0L39ilFbbnVcL6Itp/m8RRWm/xRxS4RMnsdV/AhvpRLrhL3lfQ7E2oCeSM36v1S9rdg6a47zcnpL+ahG76Gz39Y7KmVRQciNx7ezbwxj3Q5lZtFykgdfGIAN+bT8ijXMO6m68g60i9Bz4IoMZGkiJGqMYLTxMQ+oRgR3Ro5lbj7E11YBHyeimoBYXYGHMkiuxopQZ7lIj3plxIzhmUlXJBA4jMw9KGHdYaLhaicIYhvQmCTAjrkt2HvxEe6lU8iws2Qv+pB6tAGundN36RVVWAckeQPZ4ZsgDP8V2FfibZ1nsrQ+zBKqaslYMAHs01Cf0Hm0PnCqagf230xaobu0iooNuXx44QKoDnB+w== openpgp:0x803010E7
diff --git a/modules/ssh/keys/github.keys b/modules/ssh/keys/github.keys
new file mode 100644
index 000000000000..66dd9944184d
--- /dev/null
+++ b/modules/ssh/keys/github.keys
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
\ No newline at end of file
diff --git a/modules/ssh/keys/gitlab.keys b/modules/ssh/keys/gitlab.keys
new file mode 100644
index 000000000000..91a781168a11
--- /dev/null
+++ b/modules/ssh/keys/gitlab.keys
@@ -0,0 +1,2 @@
+gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf
+gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9
diff --git a/modules/ssh/keys/qyliss.keys b/modules/ssh/keys/qyliss.keys
new file mode 100644
index 000000000000..68956b69b7ba
--- /dev/null
+++ b/modules/ssh/keys/qyliss.keys
@@ -0,0 +1,4 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDLw1dxoV+AAHEHvpKBteVYKCcaAYvdy8696tssKcp2UBazTcVLhNRlpkSv+3JJrf4dvpr6m2wG9HR1/Pmlq76yKSbPX5FBYI6MccZhowpu9Mtx95UlBZxof/YhMdtNZ7VHztJySLuDdt7YAL1LGLaiGAY6ADGQ7KrAcu45AhwQGonnVc1rZrBifqBCQI0N5rs3VJdP+v+BSZHrZutgTgnHGMpJj/YAQPZ3LVDc597KOmlv8Z732RzS6iu9oIT8UO/9gDUDxR9n5l5FM342A8ACD/mqVSUr1bwT7zQ9RwB9vio5yZJljDKF/1OCEW0++njKzWrr4DkcCDS7I8ba/0w+O2OUd+Zy6FEwU1+kJINp1R0Gui6w3jlf1yycNn03t651I5Fs1xMuCtyjyC44xzI3Y4xxoVlY0CYrTvKQnrC2tBjZ+VZiBt24sjQQb8uZdFAhbvm75IxdFmLbRD0JZjkstLWFImkizd2LuCiblNXTMxaPjkAdx4zQ1xwyHrYQADKlxzcW2wfS/MEVMyBHY58aHYNQbrvnjk72sRhoS7ipGbPRGRkhQAhlxKhTQU+iFRtJHX0c9cYZa+RP0AdxH2i0Dg3+I3R6NkbVO4vLxSPcfWvt4+aQn3fL4NqMYffm1WT7JT8VEX0BiXSVnVgc9j8QxHgeBQxVO5+uUPyZtrO4Lw==
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDgBBzMNio/v3M0QibrJ+im/j/JvNKtni0uSXDNNMX/5gaoZbCSj6am/kAlmo37MJAm9WW7m1eDg/rDOmyit2PZm3WPgdrhyhQqbHa48gsNWjLCLuG/drRLwmRd6NKqaGS2eJQ6AZa/wX90mqZEh2lUnVSLItUVWPpvGNGGO3i20HCA0q+kfIQRmIL9lACPX8/WNN8LJ7mRljLAgiNbXKKQOtdE8DvHgNX/Pe+7zl7q+w+Fk1t8dWdj4UPv+aLYQV6k7zP7j9zeyVmtUAzxM8wCeUuEwUuEV3x5HiwRGYsS+A2k8Tp+IJbDuOrE75MwoxDFz+3fa+eOiUzqHruXvc9rz2b8LTADTL54IbJh7nezU5Fer5Bwik7iKTuoKVJygkpNsCQsQAbPD1XqwdAii+EBUU34sI3O8QW1XHYQ6fZd+hKj5K7CG1pqThnITUHNA1nUzN/8TWujtdhRTBE7NEtu5ekvpf7QLbAVUfKjwYl9hCrudoPGLICzspxxq+0aSieP7idhlf0M2wXkdTV3lNEnpip9YDU9wGPAf5u55HnxZb3lWZNeYyigEspuMnjGXpvwwAFSRoG7tce+Q0LDhUTuAfwME+0UB5g6vhGbvkTCsvoJTQujj5+I4Z0Ghb2aJBW65VXzFp1gxmice7oNyHhVulL0/VXk+XZ4QCLn7Dz79Q==
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDO11Pr7jKaRZ2It1yB312SKFN8mCV7aVYdry16LNwtnA6EDgFxyshG4Zmhl9asxQ9wa1lT3tdKB6ArA+VKxXMZB0zm15jYSLKpHQxMT7T3SqtTluJQpJD9zRtWeHbW/e1mtgn3tPYTHERB4HVGKIeGk97eOR2YOdXPHOIWhOXpogDtUlyt1bmWl0gyRHbWhViLeReHYhsu0KbZlo+ntN9aN7lPVkDfa7gUARv6IeGE5hAYHPRWmQ3VJCDaQnzsTtesLPFiNmV6Pq7qtWbHVNOG9XQLXJhD/305+yDZ2y/+KuBEQCroiWF8fPY/8gutfkZ0ZLjdGbXl38j5v+yRjreh+wjcN5MYWCWM18hMdutpoMd9D7PXaZz90V2vS+mRC81t3zXKrAy3Ke+LQBmlWSWxmKWdDoOTGOHjyPuCC/q+In7Q8hetB9/b9WUXTwEaaE3lUsa7y5JHAekNmdSoN3WD10nGYVUMvRRPGAlyqZTQdvxhn+6Pyu2piwIv/TMmC1CwiHr+fLbHxXQF745sOBQNmrdfiOzqDsKleybNB6i0AdDm5UZcYRcMLuxmryxN8O8qNUdMjMGoCeFcGwAIieqM+0xkPiByKr8ky2yV2lwOaZ4jrp/3j5GsGoQlvNKIPdCA/GQFad6vuqvhlbWcbdfiNpawrppLcJBsGB2NVjGbNQ==
+
diff --git a/modules/users/default.nix b/modules/users/default.nix
new file mode 100644
index 000000000000..a7d28a6032ef
--- /dev/null
+++ b/modules/users/default.nix
@@ -0,0 +1,32 @@
+{ pkgs, lib, ... }:
+
+let
+  # These defaults should override the NixOS defaults,
+  # but still themselves be overridable at the default priority.
+  mkDefault = lib.mkOverride ((lib.mkDefault null).priority - 1);
+
+in {
+  imports = [ ../home ];
+
+  users.mutableUsers = false;
+  users.groups.qyliss = {};
+  users.users.qyliss = {
+    createHome = true;
+    description = "Alyssa Ross";
+    home = mkDefault "/home";
+    uid = mkDefault 1000;
+    subUidRanges = [ { count = 65536; startUid = 100000; } ];
+    subGidRanges = [ { count = 65536; startGid = 100000; } ];
+    group = "qyliss";
+    extraGroups = [ "wheel" ];
+    shell = lib.mkDefault pkgs.bash;
+  };
+
+  home.qyliss = {
+    permissions = "0555";
+    group = "qyliss";
+  };
+  home.qyliss.dirs.state = {
+    permissions = "0500";
+  };
+}
diff --git a/modules/workstation/default.nix b/modules/workstation/default.nix
new file mode 100644
index 000000000000..3f4e3f1c8ce7
--- /dev/null
+++ b/modules/workstation/default.nix
@@ -0,0 +1,16 @@
+{ lib, pkgs, ... }:
+
+{
+  imports = [
+    ../nix ../shell ../users ../ssh
+    ./documentation ./windowing ./fonts ./hardware ./locale
+    ./dict ./dino ./emacs ./gnupg ./lorri ./mail ./podman ./weechat
+  ];
+
+  environment.systemPackages = with pkgs; [ mosh mpv youtube-dl ];
+
+  services.mingetty.autologinUser = "qyliss";
+  services.mingetty.loginOptions = "-- \u";
+  services.locate.enable = true;
+
+}
diff --git a/modules/workstation/dict/default.nix b/modules/workstation/dict/default.nix
new file mode 100644
index 000000000000..c21e20e947a4
--- /dev/null
+++ b/modules/workstation/dict/default.nix
@@ -0,0 +1,6 @@
+{ pkgs, ... }:
+
+{
+  services.dictd.enable = true;
+  services.dictd.DBs = with pkgs.dictdDBs; [ epo2eng ];
+}
diff --git a/modules/workstation/dino/default.nix b/modules/workstation/dino/default.nix
new file mode 100644
index 000000000000..1c483d4fc473
--- /dev/null
+++ b/modules/workstation/dino/default.nix
@@ -0,0 +1,7 @@
+{ pkgs, ... }:
+
+{
+  environment.systemPackages = with pkgs; [ dino ];
+
+  home.qyliss.dirs."state/dino" = {};
+}
diff --git a/modules/workstation/documentation/default.nix b/modules/workstation/documentation/default.nix
new file mode 100644
index 000000000000..ecaad504dda4
--- /dev/null
+++ b/modules/workstation/documentation/default.nix
@@ -0,0 +1,6 @@
+{ pkgs, ... }:
+
+{
+  environment.systemPackages = with pkgs;
+    [ glibcInfo gnumake.info man-pages (lowPrio mandoc) ];
+}
diff --git a/modules/workstation/emacs/default.el b/modules/workstation/emacs/default.el
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/modules/workstation/emacs/default.el
diff --git a/modules/workstation/emacs/default.nix b/modules/workstation/emacs/default.nix
new file mode 100644
index 000000000000..a0432bd25ba5
--- /dev/null
+++ b/modules/workstation/emacs/default.nix
@@ -0,0 +1,14 @@
+{ pkgs, ... }:
+
+{
+  environment.systemPackages = with pkgs; [
+    (emacsWithPackages (epkgs: [
+      (runCommandNoCC "default.el" {} ''
+        mkdir -p $out/share/emacs/site-lisp
+        cp ${./default.el} $out/share/emacs/site-lisp/default.el
+      '')
+    ] ++ (with epkgs; [
+      magit
+    ])))
+  ];
+}
diff --git a/modules/workstation/fonts/default.nix b/modules/workstation/fonts/default.nix
new file mode 100644
index 000000000000..ae590859897f
--- /dev/null
+++ b/modules/workstation/fonts/default.nix
@@ -0,0 +1,7 @@
+{ pkgs, ... }:
+
+{
+  fonts.fonts = with pkgs; [ iosevka twemoji-color-font noto-fonts-cjk ];
+  fonts.fontconfig.allowBitmaps = false;
+  fonts.fontconfig.defaultFonts.monospace = [ "Iosevka" ];
+}
diff --git a/modules/workstation/gnupg/default.nix b/modules/workstation/gnupg/default.nix
new file mode 100644
index 000000000000..a93a31411d08
--- /dev/null
+++ b/modules/workstation/gnupg/default.nix
@@ -0,0 +1,30 @@
+{ pkgs, ... }:
+
+{
+  home.qyliss.dirs."state/gnupg".activationScripts.config =
+    let
+      pinentry = if pkgs.stdenv.isDarwin then
+        "/Applications/pinentry-mac.app/Contents/MacOS/pinentry-mac"
+      else
+        "${pkgs.pinentry.qt}/bin/pinentry";          
+      
+      gpg-agent-conf = pkgs.writeText "gpg-agent.conf" ''
+        pinentry-program ${pinentry}
+      '';
+    in ''
+      ln -sf ${./dirmngr.conf} dirmngr.conf
+      ln -sf ${./gpg.conf} gpg.conf
+      ln -sf ${gpg-agent-conf} gpg-agent.conf
+    '';
+
+  environment.systemPackages = with pkgs; [ gnupg ];
+
+  environment.extraInit = ''
+    export GNUPGHOME="$HOME/state/gnupg"
+    export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
+  '';
+
+  programs.sway.extraConfig = ''
+    exec gpg-connect-agent /bye
+  '';
+}
diff --git a/modules/workstation/gnupg/dirmngr.conf b/modules/workstation/gnupg/dirmngr.conf
new file mode 100644
index 000000000000..14114144cc9d
--- /dev/null
+++ b/modules/workstation/gnupg/dirmngr.conf
@@ -0,0 +1 @@
+keyserver hkps://keys.openpgp.org
diff --git a/modules/workstation/gnupg/gpg.conf b/modules/workstation/gnupg/gpg.conf
new file mode 100644
index 000000000000..ab999bfbc32a
--- /dev/null
+++ b/modules/workstation/gnupg/gpg.conf
@@ -0,0 +1,3 @@
+ask-cert-level
+auto-key-retrieve
+trust-model tofu+pgp
diff --git a/modules/workstation/hardware/default.nix b/modules/workstation/hardware/default.nix
new file mode 100644
index 000000000000..3c33ee2b4a1b
--- /dev/null
+++ b/modules/workstation/hardware/default.nix
@@ -0,0 +1,9 @@
+{ pkgs, ... }:
+
+{
+  imports = [ ./keyboard ./yubikey ];
+
+  environment.systemPackages = with pkgs; [ ddrescue ];
+
+  sound.enable = true;
+}
diff --git a/modules/workstation/hardware/keyboard/MAPPINGS b/modules/workstation/hardware/keyboard/MAPPINGS
new file mode 100644
index 000000000000..60ded39c8cf1
--- /dev/null
+++ b/modules/workstation/hardware/keyboard/MAPPINGS
@@ -0,0 +1,7 @@
+Key remappings are spread across several different places, because they
+have to be done differently depending on the remapping.
+
+Here is an overview of remapped keys:
+
+Caps Lock: Escape if pressed, Ctrl if held
+Tab: Super-L if pressed, Tab if held
diff --git a/modules/workstation/hardware/keyboard/default.nix b/modules/workstation/hardware/keyboard/default.nix
new file mode 100644
index 000000000000..092854d535b4
--- /dev/null
+++ b/modules/workstation/hardware/keyboard/default.nix
@@ -0,0 +1,22 @@
+{ pkgs, config, ... }:
+
+let
+  xcfg = config.services.xserver;
+
+in
+{
+  console.useXkbConfig = true;
+  services.xserver.layout = "dvorak";
+  services.xserver.xkbOptions = "ctrl:nocaps,compose:menu";
+
+  environment.variables.XKB_DEFAULT_LAYOUT = xcfg.layout;
+  environment.variables.XKB_DEFAULT_OPTIONS = xcfg.xkbOptions;
+
+  services.evscript.enable = true;
+  services.evscript.script = ./events.dyon;
+
+  boot.postBootCommands = ''
+    # Remap tab to left super
+    /run/current-system/sw/bin/setkeycodes 0f 125
+  '';
+}
diff --git a/modules/workstation/hardware/keyboard/events.dyon b/modules/workstation/hardware/keyboard/events.dyon
new file mode 100644
index 000000000000..96cc15450e46
--- /dev/null
+++ b/modules/workstation/hardware/keyboard/events.dyon
@@ -0,0 +1,14 @@
+//! [events]
+//! keys = ['ESC', 'TAB']
+fn main() ~ evdevs, uinput {
+    should_esc := false
+    should_tab := false
+    loop {
+        evts := next_events(evdevs)
+        for i {
+            evt := evts[i]
+            xcape(mut should_esc, evt, KEY_CAPSLOCK(), [KEY_ESC()])
+            xcape(mut should_tab, evt, KEY_LEFTMETA(), [KEY_TAB()])
+        }
+    }
+}
diff --git a/modules/workstation/hardware/yubikey/default.nix b/modules/workstation/hardware/yubikey/default.nix
new file mode 100644
index 000000000000..d047246bb20d
--- /dev/null
+++ b/modules/workstation/hardware/yubikey/default.nix
@@ -0,0 +1,13 @@
+{ pkgs, ... }:
+
+{
+  services.udev.packages = with pkgs; [ yubikey-personalization ];
+
+  security.pam.services.sudo.u2fAuth = true;
+  security.sudo.extraConfig = ''
+    Defaults timestamp_timeout=0
+  '';
+
+  security.pam.u2f.cue = true;
+  security.pam.u2f.authFile = pkgs.copyPathToStore ./u2f_keys;
+}
diff --git a/modules/workstation/hardware/yubikey/u2f_keys b/modules/workstation/hardware/yubikey/u2f_keys
new file mode 100644
index 000000000000..c5601f5f0703
--- /dev/null
+++ b/modules/workstation/hardware/yubikey/u2f_keys
@@ -0,0 +1 @@
+qyliss:HIWnn91xwo-f14WjdDSdiX2Rs9NdJr2QF4_5_gYUpTkennbpR8AOXHmfEzj5llyLyb-_WEaVUQU59ieCamq9SA,044196971044cff2724ea9ab4624ef860fde32337acc6c3a323899f36a50bf87d06f535c146111f96925455f8c07addff769dfd502c216a9683c70898bb521ada5
diff --git a/modules/workstation/locale/default.nix b/modules/workstation/locale/default.nix
new file mode 100644
index 000000000000..8028bd36e271
--- /dev/null
+++ b/modules/workstation/locale/default.nix
@@ -0,0 +1,6 @@
+{ ... }:
+
+{
+  i18n.defaultLocale = "eo.utf8";
+  environment.sessionVariables.LC_CTYPE = "en_GB.utf8";
+}
diff --git a/modules/workstation/lorri/default.nix b/modules/workstation/lorri/default.nix
new file mode 100644
index 000000000000..4008934dab01
--- /dev/null
+++ b/modules/workstation/lorri/default.nix
@@ -0,0 +1,10 @@
+{ ... }:
+
+{
+  home.qyliss.dirs."state/lorri" = {};
+
+  services.lorri.enable = true;
+
+  # FIXME: systemd should have this set globally.
+  systemd.user.services.lorri.environment.XDG_CACHE_HOME = "/home/state/cache";
+}
diff --git a/modules/workstation/mail/default.nix b/modules/workstation/mail/default.nix
new file mode 100644
index 000000000000..c53128520812
--- /dev/null
+++ b/modules/workstation/mail/default.nix
@@ -0,0 +1,47 @@
+{ pkgs, config, ... }:
+
+let
+  maildir = "${config.users.users.qyliss.home}/mail";
+  mbsyncrc = pkgs.substituteAll { inherit maildir; src = ./mbsyncrc.in; };
+
+in
+
+{
+  imports = [ ./mutt ./notmuch ./postfix ./rss2email ];
+
+  environment.systemPackages = with pkgs; [ isync ];
+
+  systemd.services.mail = {
+    path = with pkgs; [ coreutils findutils isync notmuch sudo ];
+    serviceConfig.Type = "oneshot";
+    after = [ "network-online.target" ];
+    script = "sudo -u qyliss-mail mbsync -a -V -c ${mbsyncrc}";
+    postStart = ''
+      find "${maildir}" \! -name .mbsyncstate* \
+                        \( \( \! -user qyliss -o \! -group qyliss \) , \
+                           -type f \! -perm 660 -exec chmod 0660 '{}' \; , \
+                           -type d \! -perm 770 -exec chmod 0770 '{}' \; \)
+      sudo -u qyliss \
+          env NOTMUCH_CONFIG=/etc/xdg/nixos/per-user/qyliss/notmuch/config \
+          notmuch new
+    '';
+  };
+
+  systemd.timers.mail = {
+    timerConfig.OnCalendar = "*:0/5";
+    timerConfig.Persistent = true;
+    after = [ "network-online.target" ];
+    wantedBy = [ "timers.target" ];
+  };
+
+  users.users.qyliss-mail = {
+    home = "/var/home/qyliss-mail";
+    group = "qyliss";
+    createHome = true;
+  };
+
+  home.qyliss.dirs.mail = {
+    group = "qyliss";
+    permissions = "0770";
+  };
+}
diff --git a/modules/workstation/mail/mbsyncrc.in b/modules/workstation/mail/mbsyncrc.in
new file mode 100644
index 000000000000..987646dd9e66
--- /dev/null
+++ b/modules/workstation/mail/mbsyncrc.in
@@ -0,0 +1,22 @@
+Create Both
+
+MaildirStore local
+  Path @maildir@/
+  Inbox @maildir@/INBOX
+  Subfolders Verbatim
+
+IMAPAccount fastmail
+  Host imap.fastmail.com
+  User alyssa@fastmail.com
+  PassCmd "cat ~/imappass"
+  SSLType IMAPS
+  SSLVersions TLSv1.2
+
+IMAPStore fastmail-remote
+  Account fastmail
+
+Channel fastmail
+  Master :fastmail-remote:
+  Slave :local:
+  Patterns *
+  SyncState *
diff --git a/modules/workstation/mail/mutt/default.nix b/modules/workstation/mail/mutt/default.nix
new file mode 100644
index 000000000000..00ca6e86f4fc
--- /dev/null
+++ b/modules/workstation/mail/mutt/default.nix
@@ -0,0 +1,9 @@
+{ pkgs, ... }:
+
+{
+  environment.systemPackages = with pkgs; [ neomutt ];
+
+  users.users.qyliss.xdg.config.paths."mutt/muttrc" = pkgs.copyPathToStore ./muttrc;
+
+  home.qyliss.dirs."state/mutt/header_cache" = {};
+}
diff --git a/modules/workstation/mail/mutt/muttrc b/modules/workstation/mail/mutt/muttrc
new file mode 100644
index 000000000000..ed10e60507f8
--- /dev/null
+++ b/modules/workstation/mail/mutt/muttrc
@@ -0,0 +1,56 @@
+color index red default ~P
+
+unignore List-Id:
+ignore User-Agent:
+
+set beep = no
+set beep_new = yes
+set edit_headers = yes
+set fast_reply = yes
+set folder = ~/mail
+set header_cache = ~/state/mutt/header_cache
+set help = no
+set mark_old = no
+set mime_forward = ask-no
+set pager = "less -+S"
+set quit = ask-yes
+set sort = threads
+set sort_browser = new
+set user_agent = no
+
+set newsrc = $XDG_DATA_HOME/mutt/newsrc
+
+unset prompt_after
+
+set spoolfile = +INBOX
+
+# set record = "=[Gmail]/Sent Mail"
+# set postponed = "=[Gmail]/Drafts"
+mailboxes `cd ~/mail; find . -name cur -print0 | sed -z -e 's|^\./||' -e 's|/cur$||' -e 's/\\/\\\\/' -e 's/"/\\"/g' -e 's/^/"=/' -e 's/$/"/' | xargs -0`
+set record = "=Sent"
+set trash = "=Archive"
+set postponed = "=Drafts"
+set sendmail = "msmtp"
+
+set pgp_use_gpg_agent = yes
+set crypt_autosign = yes
+set crypt_opportunistic_encrypt = yes
+set postpone_encrypt = yes
+
+# Required for postpone_encrypt to work
+set pgp_default_key = 757356D779BBB888773E415E736CCDF9EF51BD97
+
+set pgp_decode_command       = "gpg --status-fd=2 %?p?--pinentry-mode loopback --passphrase-fd 0? --no-verbose --quiet --batch --output - %f"
+set pgp_verify_command       = "gpg --status-fd=2 --no-verbose --quiet --batch --output - --verify %s %f"
+set pgp_decrypt_command      = "gpg --status-fd=2 %?p?--pinentry-mode loopback --passphrase-fd 0? --no-verbose --quiet --batch --output - --decrypt %f"
+set pgp_sign_command         = "gpg %?p?--pinentry-mode loopback --passphrase-fd 0? --no-verbose --batch --quiet --output - --armor --textmode %?a?--local-user %a? --detach-sign %f"
+set pgp_clearsign_command    = "gpg %?p?--pinentry-mode loopback --passphrase-fd 0? --no-verbose --batch --quiet --output - --armor --textmode %?a?--local-user %a? --clearsign %f"
+set pgp_encrypt_only_command = "pgpewrap gpg --trust-model always --batch --quiet --no-verbose --output - --textmode --armor --encrypt -- --recipient %r -- %f"
+set pgp_encrypt_sign_command = "pgpewrap gpg %?p?--pinentry-mode loopback --passphrase-fd 0? --trust-model always --batch --quiet --no-verbose --textmode --output - %?a?--local-user %a? --armor --sign --encrypt -- --recipient %r -- %f"
+set pgp_import_command       = "gpg --no-verbose --import %f"
+set pgp_export_command       = "gpg --no-verbose --armor --export %r"
+set pgp_verify_key_command   = "gpg --verbose --batch --fingerprint --check-sigs %r"
+set pgp_list_pubring_command = "gpg --no-verbose --batch --quiet --with-colons --with-fingerprint --with-fingerprint --list-keys %r"
+set pgp_list_secring_command = "gpg --no-verbose --batch --quiet --with-colons --with-fingerprint --with-fingerprint --list-secret-keys %r"
+set pgp_good_sign            = "^\\[GNUPG:\\] GOODSIG
+set pgp_decryption_okay      = "^\\[GNUPG:\\] DECRYPTION_OKAY"
diff --git a/modules/workstation/mail/notmuch/config b/modules/workstation/mail/notmuch/config
new file mode 100644
index 000000000000..64c056fa3528
--- /dev/null
+++ b/modules/workstation/mail/notmuch/config
@@ -0,0 +1,15 @@
+[database]
+path=/home/mail
+
+[user]
+other_email=alyssa.ross@freeagent.com;
+
+[new]
+tags=unread;inbox;
+ignore=.uidvalidity;.mbsyncstate;.mbsyncstate.new;.mbsyncstate.journal;
+
+[search]
+exclude_tags=
+
+[maildir]
+synchronize_flags=true
\ No newline at end of file
diff --git a/modules/workstation/mail/notmuch/default.nix b/modules/workstation/mail/notmuch/default.nix
new file mode 100644
index 000000000000..46aa17374a0e
--- /dev/null
+++ b/modules/workstation/mail/notmuch/default.nix
@@ -0,0 +1,12 @@
+{ pkgs, ... }:
+
+{
+  environment.extraInit = ''
+    export NOTMUCH_CONFIG="/etc/xdg/nixos/per-user/$USER/notmuch/config"
+  '';
+
+  environment.systemPackages = with pkgs; [ notmuch ];
+
+  users.users.qyliss.xdg.config.paths."notmuch/config" =
+    pkgs.copyPathToStore ./config;
+}
diff --git a/modules/workstation/mail/postfix/default.nix b/modules/workstation/mail/postfix/default.nix
new file mode 100644
index 000000000000..0da8936cf1b3
--- /dev/null
+++ b/modules/workstation/mail/postfix/default.nix
@@ -0,0 +1,32 @@
+{ pkgs, lib, config, ... }:
+
+{
+  services.postfix.enable = true;
+
+  services.postfix.hostname = with lib; with config.networking;
+    concatStringsSep "." (filter (x: x != null) [ hostName domain ]);
+
+  services.postfix.relayHost = "smtp.fastmail.com";
+  services.postfix.relayPort = 465;
+
+  services.postfix.recipientDelimiter = "+";
+  services.postfix.config.home_mailbox = "mail/INBOX/";
+  services.postfix.virtual = ''
+    hi@alyssa.is qyliss
+  '';
+
+  # NixOS links /var/lib/postfix/conf to /etc/postfix, but
+  # postfix.service deletes /var/lib/postfix in an ExecStartPre, so we
+  # can't keep files there without adding them to the store.
+  #
+  # Work around this with a layer of symlink indirection.
+  services.postfix.mapFiles.sasl_passwd = pkgs.runCommand "sasl_passwd" {} ''
+    ln -s /var/lib/postfix/sasl_passwd $out
+  '';
+  services.postfix.config.smtp_sasl_password_maps = "hash:/etc/postfix/sasl_passwd";
+
+  services.postfix.config.smtp_sasl_auth_enable = true;
+  services.postfix.config.smtp_sasl_tls_security_options = "noanonymous";
+  services.postfix.config.smtp_tls_security_level = "encrypt";
+  services.postfix.config.smtp_tls_wrappermode = true;
+}
diff --git a/modules/workstation/mail/rss2email/default.nix b/modules/workstation/mail/rss2email/default.nix
new file mode 100644
index 000000000000..2e754679fdb2
--- /dev/null
+++ b/modules/workstation/mail/rss2email/default.nix
@@ -0,0 +1,16 @@
+{ config, ... }:
+
+{
+  services.rss2email.enable = true;
+  services.rss2email.to = "hi+rss2email@alyssa.is";
+  services.rss2email.config.date-header = true;
+  services.rss2email.config.from =
+    "rss2email@${config.services.postfix.hostname}";
+
+  services.rss2email.feeds = {
+    fading-memories = { url = "https://valdyas.org/fading/feed/"; };
+    flak = { url = "https://flak.tedunangst.com/rss"; };
+    wandering-thoughts =
+      { url = "https://utcc.utoronto.ca/~cks/space/blog/?atom"; };
+  };
+}
diff --git a/modules/workstation/networking/default.nix b/modules/workstation/networking/default.nix
new file mode 100644
index 000000000000..3176e8d061c0
--- /dev/null
+++ b/modules/workstation/networking/default.nix
@@ -0,0 +1,46 @@
+{ pkgs, config, ... }:
+
+{
+  services.resolved.enable = true;
+
+  networking.domain = "qyliss.net";
+  networking.hosts = with config.networking;
+    { "127.0.1.1" = [ "${hostName}.${domain}" ]; };
+
+  networking.networkmanager.enable = true;
+
+  users.users.qyliss.extraGroups = [ "networkmanager" ];
+
+  # Plausible MAC randomization
+  networking.networkmanager.ethernet.macAddress = "random";
+  networking.networkmanager.wifi.macAddress = "random";
+  networking.networkmanager.extraConfig = ''
+    [connection-extra]
+    ethernet.generate-mac-address-mask=FE:FF:FF:00:00:00
+    wifi.generate-mac-address-mask=FE:FF:FF:00:00:00
+  '';
+
+  networking.nameservers = [ "::1" ];
+
+  networking.networkmanager.dispatcherScripts = [
+    {
+      source = pkgs.writeText "doh-stub" ''
+        if [ "$2" = up ]
+        then systemctl restart doh-stub.service
+        fi
+      '';
+      type = "basic";
+    }
+  ];
+
+  systemd.services.doh-stub = {
+    script = ''
+      exec ${pkgs.doh-proxy}/bin/doh-stub \
+          --level INFO \
+          --domain qyliss.net \
+          --remote-address 85.119.82.108
+    '';
+  };
+
+  programs.mtr.enable = true;
+}
diff --git a/modules/workstation/physical/default.nix b/modules/workstation/physical/default.nix
new file mode 100644
index 000000000000..173dbbbb517f
--- /dev/null
+++ b/modules/workstation/physical/default.nix
@@ -0,0 +1,7 @@
+{ ... }:
+
+{
+  imports = [ ../. ../networking ];
+
+  programs.swayidle.enable = true;
+}
diff --git a/modules/workstation/podman/default.nix b/modules/workstation/podman/default.nix
new file mode 100644
index 000000000000..aefad1f9d97f
--- /dev/null
+++ b/modules/workstation/podman/default.nix
@@ -0,0 +1,47 @@
+{ pkgs, ... }:
+
+{
+  environment.etc."containers/libpod.conf".text = ''
+    runtime_path = ["${pkgs.runc}/bin/runc"]
+    conmon_path = ["${pkgs.conmon}/bin/conmon"]
+  '';
+
+  environment.etc."containers/policy.json".text = builtins.toJSON {
+    # Not insecure when I'm manually pulling images on a workstation.
+    default = [ { type = "insecureAcceptAnything"; } ];
+  };
+
+  environment.etc."containers/registries.conf".text = ''
+    [registries.search]
+    registries = ['docker.io']
+  '';
+
+  environment.systemPackages = with pkgs;
+    let
+      podman-bin = writeShellScriptBin "podman" ''
+        HOME="$XDG_CONFIG_HOME/podman"
+        exec ${podman}/bin/podman "$@"
+      '';
+    in
+      [ podman-bin podman.man runc conmon slirp4netns ];
+
+  users.users.qyliss.xdg.config.paths."podman/.config/containers/libpod.conf" =
+    pkgs.writeText "libpod.conf" ''
+      runtime_path = ["${pkgs.runc}/bin/runc"]
+      conmon_path = ["${pkgs.conmon}/bin/conmon"]
+    '';
+
+  users.users.qyliss.xdg.config.paths."podman/.config/containers/storage.conf" =
+    pkgs.writeText "storage.conf" ''
+      [storage]
+      driver = "overlay"
+      runroot = "/tmp/1000"
+      graphroot = "/home/state/podman/containers/storage"
+
+      [storage.options]
+      mount_program = "${pkgs.fuse-overlayfs}/bin/fuse-overlayfs"
+    '';
+
+  home.qyliss.dirs."state/containers" = {};
+  home.qyliss.dirs."state/podman" = {};
+}
diff --git a/modules/workstation/weechat/default.nix b/modules/workstation/weechat/default.nix
new file mode 100644
index 000000000000..4298122a4e0b
--- /dev/null
+++ b/modules/workstation/weechat/default.nix
@@ -0,0 +1,121 @@
+{ pkgs, lib, ... }:
+
+with lib;
+
+let
+  networks = [ "freenode" "hackint" "indymedia" "oftc" ];
+
+  toWeeChat = value:
+    /**/ if value == true  then "on"
+    else if value == false then "off"
+    else if isList value   then concatStringsSep "," value
+    else toString value;
+
+  sec = [ "znc.username" "znc.password" "slack.api_tokens" ];
+
+  cfgin = {
+    alias.cmd.B = "buffer";
+    irc.look.buffer_switch_autojoin = false;
+    irc.look.display_join_message = false;
+    irc.look.server_buffer = "independent";
+    irc.look.temporary_servers = true;
+
+    irc.server_default = {
+      addresses = "znc.qyliss.net/6697";
+      autoconnect = true;
+      capabilities = [ "account-notify" "away-notify" "cap-notify" "multi-prefix" "server-time" "znc.in/server-time-iso" "znc.in/self-message" "znc.in/playback" ];
+      nicks = "qyliss";
+      password = "\\\${sec.data.znc.password}";
+      ssl = true;
+      username = "\\\${sec.data.znc.username}";
+    };
+
+    irc.server = genAttrs networks (s:
+      { username = "'\\\${sec.data.znc.username}/${s}'"; });
+    plugins.var.python.zncplayback.servers = networks;
+
+    logger.color.backlog_end = "*default";
+    logger.look.backlog = 200;
+
+    plugins.var.python.slack.slack_api_token = "\\\${sec.data.slack.api_tokens}";
+    plugins.var.python.slack.auto_open_threads = true;
+    plugins.var.python.slack.background_load_all_history = true;
+    plugins.var.python.slack.short_buffer_names = true;
+    plugins.var.python.slack.show_reaction_nicks = true;
+    plugins.var.python.vimode.no_warn = true;
+
+    script.look.sort = "p,n";
+
+    weechat.bar.buflist.hidden = true;
+    weechat.bar.fset.items = "fset";
+    weechat.bar.input.items = "[mode_indicator]+ [input_prompt]+(away),[input_search],[input_paste],input_text,[vi_buffer]";
+    weechat.bar.status.color_bg = 53;
+    weechat.bar.status.items = "[otr],[buffer_plugin],buffer_name+(buffer_modes)+{buffer_nicklist_count}+buffer_zoom+buffer_filter,scroll,[lag],[hotlist],completion";
+    weechat.bar.title.color_bg = 53;
+
+    weechat.color.chat_nick_colors = [ "cyan" "magenta" "green" "brown" "lightblue" "lightcyan" "lightmagenta" "lightgreen" "blue" ];
+    weechat.color.chat_nick_self = "default";
+    weechat.color.status_count_other = "white";
+    weechat.color.status_data_other = "white";
+    weechat.color.status_nicklist_count = "white";
+    weechat.color.status_time = "white";
+
+    weechat.completion.default_template = "%(nicks)|%(irc_channels)|%(emoji)";
+    weechat.completion.nick_completer = ":";
+
+    weechat.look.buffer_notify_default = "message";
+    weechat.look.highlight = [ "spectrum" "crosvm" "qyliss" "alyssa*" "*dntmissher" "*@alyssa" ];
+    weechat.look.hotlist_names_count = 10;
+    weechat.look.hotlist_names_level = 14;
+    weechat.look.mouse = true;
+    weechat.look.prefix_align_max = 12;
+    weechat.look.save_config_on_exit = false;
+    weechat.look.window_title = "WeeChat \\\${info:version}";
+  };
+
+  commands =
+    map (d: ''/set sec.data.${d} test'') sec ++ [ "/save" ] ++
+    map (n: "/server add ${n} ${cfgin.irc.server_default.addresses}") networks ++
+    [ "/ignore add osmbot-test oftc #osm-gb" ] ++
+    mapAttrsToList (name: value: "/set ${name} ${toWeeChat value}")
+                   (flattenAttrs cfgin);
+
+  flattenAttrs' = sep: attrs:
+    listToAttrs (concatLists (flip mapAttrsToList attrs (k: v:
+      if isAttrs v then mapAttrsToList (k': nameValuePair "${k}${sep}${k'}") (flattenAttrs' sep v)
+                 else [ (nameValuePair k v) ])));
+
+  flattenAttrs = flattenAttrs' ".";
+
+  cfg = pkgs.runCommand "weechat-config" {} ''
+    ${pkgs.weechat}/bin/weechat-headless -d $out \
+        ${concatMapStringsSep " " (cmd: "-r ${escapeShellArg cmd}") commands} \
+        -r /save \
+        -r /exit
+  '';
+
+in
+
+{
+  environment.extraInit = ''
+    export WEECHAT_HOME="$HOME/state/weechat"
+  '';
+
+  environment.systemPackages = with pkgs; [
+    (weechat.override {
+      configure = { availablePlugins, ... }: {
+        scripts = with weechatScripts;
+          [ colorize_nicks go wee-slack zncplayback ];
+      };
+    })
+  ];
+
+  home.qyliss.dirs."state/weechat".activationScripts.config = ''
+    for file in ${cfg}/*.conf
+    do
+        if [ "$file" != ${cfg}/sec.conf ]
+        then ln -sf $file $(basename $file)
+        fi
+    done
+  '';
+}
diff --git a/modules/workstation/windowing/alacritty/config.yml b/modules/workstation/windowing/alacritty/config.yml
new file mode 100644
index 000000000000..86aa7ed5622c
--- /dev/null
+++ b/modules/workstation/windowing/alacritty/config.yml
@@ -0,0 +1,340 @@
+# Any items in the `env` entry below will be added as
+# environment variables. Some entries may override variables
+# set by alacritty itself.
+#env:
+  # TERM variable
+  #
+  # This value is used to set the `$TERM` environment variable for
+  # each instance of Alacritty. If it is not present, alacritty will
+  # check the local terminfo database and use 'alacritty' if it is
+  # available, otherwise 'xterm-256color' is used.
+  #TERM: xterm-256color
+
+window:
+  # If both are `0`, this setting is ignored.
+  dimensions:
+    columns: 80
+    lines: 24
+
+  padding:
+    x: 2
+    y: 2
+
+  dynamic_padding: true
+  decorations: none
+  startup_mode: Windowed
+
+scrolling:
+  history: 0 # disabled
+
+font:
+  normal:
+    family: monospace
+    #style: Regular
+
+  bold:
+    family: monospace
+    #style: Bold
+
+  italic:
+    family: monospace
+    #style: Italic
+
+  offset:
+    x: 0
+    y: 0
+
+  glyph_offset:
+    x: 0
+    y: 0
+
+draw_bold_text_with_bright_colors: false
+
+colors:
+  primary:
+    background: '0x000000'
+    foreground: '0xffffff'
+    # dim_foreground: auto
+    # bright_foreground: normal foreground color
+
+  cursor:
+    text:   '0x161616'
+    cursor: '0xfcdc07'
+
+  normal:
+    black:   '0x2e3436'
+    red:     '0xb40000'
+    green:   '0x307000'
+    yellow:  '0xce5c00'
+    blue:    '0x3465a4'
+    magenta: '0x75507b'
+    cyan:    '0x06989a'
+    white:   '0xd3d7cf'
+
+  bright:
+    black:   '0x555753'
+    red:     '0xcc0000'
+    green:   '0x4e9a06'
+    yellow:  '0xedd400'
+    blue:    '0x729fcf'
+    magenta: '0xad7fa8'
+    cyan:    '0x34e2e2'
+    white:   '0xeeeeec'
+
+  # dim: default
+  # indexed_colors: default
+
+# Values for `animation`:
+#   - Ease
+#   - EaseOut
+#   - EaseOutSine
+#   - EaseOutQuad
+#   - EaseOutCubic
+#   - EaseOutQuart
+#   - EaseOutQuint
+#   - EaseOutExpo
+#   - EaseOutCirc
+#   - Linear
+#
+# Specifying a `duration` of `0` will disable the visual bell.
+visual_bell:
+  animation: EaseOutExpo
+  duration: 1
+
+background_opacity: 0.8
+
+# Available fields:
+#   - mouse
+#   - action
+#   - mods (optional)
+#
+# Values for `mouse`:
+#   - Middle
+#   - Left
+#   - Right
+#   - Numeric identifier such as `5`
+#
+# All available `mods` and `action` values are documented in the key binding
+# section.
+mouse_bindings:
+  - { mouse: Middle, action: PasteSelection }
+
+mouse:
+  double_click: { threshold: 300 }
+  triple_click: { threshold: 300 }
+  hide_when_typing: false
+
+  url:
+    launcher: None
+
+selection:
+  semantic_escape_chars: ",│`|:\"' ()[]{}<>"
+
+  # When set to `true`, selected text will be copied to both the primary and
+  # the selection clipboard. Otherwise, it will only be copied to the selection
+  # clipboard.
+  save_to_clipboard: false
+
+dynamic_title: true
+
+cursor:
+  style: Block
+  unfocused_hollow: true
+
+live_config_reload: true
+
+# Shell
+#
+# You can set `shell.program` to the path of your favorite shell, e.g. `/bin/fish`.
+# Entries in `shell.args` are passed unmodified as arguments to the shell.
+#shell:
+#  program: /bin/bash
+#  args:
+#    - --login
+
+# Key bindings
+#
+# Key bindings are specified as a list of objects. Each binding will specify
+# a key and modifiers required to trigger it, terminal modes where the binding
+# is applicable, and what should be done when the key binding fires. It can
+# either send a byte sequnce to the running application (`chars`), execute
+# a predefined action (`action`) or fork and execute a specified command plus
+# arguments (`command`).
+#
+# Example:
+#   `- { key: V, mods: Command, action: Paste }`
+#
+# Available fields:
+#   - key
+#   - mods (optional)
+#   - chars | action | command (exactly one required)
+#   - mode (optional)
+#
+# Values for `key`:
+#   - `A` -> `Z`
+#   - `F1` -> `F12`
+#   - `Key1` -> `Key0`
+#
+#   A full list with available key codes can be found here:
+#   https://docs.rs/glutin/*/glutin/enum.VirtualKeyCode.html#variants
+#
+#   Instead of using the name of the keys, the `key` field also supports using
+#   the scancode of the desired key. Scancodes have to be specified as a
+#   decimal number.
+#   This command will allow you to display the hex scancodes for certain keys:
+#     `showkey --scancodes`
+#
+# Values for `mods`:
+#   - Command
+#   - Control
+#   - Shift
+#   - Alt
+#
+#   Multiple `mods` can be combined using `|` like this: `mods: Control|Shift`.
+#   Whitespace and capitalization is relevant and must match the example.
+#
+# Values for `chars`:
+#   The `chars` field writes the specified string to the terminal. This makes
+#   it possible to pass escape sequences.
+#   To find escape codes for bindings like `PageUp` ("\x1b[5~"), you can run
+#   the command `showkey -a` outside of tmux.
+#   Note that applications use terminfo to map escape sequences back to
+#   keys. It is therefore required to update the terminfo when
+#   changing an escape sequence.
+#
+# Values for `action`:
+#   - Paste
+#   - PasteSelection
+#   - Copy
+#   - IncreaseFontSize
+#   - DecreaseFontSize
+#   - ResetFontSize
+#   - ScrollPageUp
+#   - ScrollPageDown
+#   - ScrollToTop
+#   - ScrollToBottom
+#   - ClearHistory
+#   - Hide
+#   - Quit
+#   - ClearLogNotice
+#
+# Values for `command`:
+#   The `command` field must be a map containing a `program` string and
+#   an `args` array of command line parameter strings.
+#
+#   Example:
+#       `command: { program: "alacritty", args: ["-e", "vttest"] }`
+#
+# Values for `mode`:
+#   - ~AppCursor
+#   - AppCursor
+#   - ~AppKeypad
+#   - AppKeypad
+key_bindings:
+  - { key: V,        mods: Control|Shift,    action: Paste               }
+  - { key: C,        mods: Control|Shift,    action: Copy                }
+  - { key: Paste,                   action: Paste                        }
+  - { key: Copy,                    action: Copy                         }
+  - { key: Q,        mods: Command, action: Quit                         }
+  - { key: W,        mods: Command, action: Quit                         }
+  - { key: Insert,   mods: Shift,   action: PasteSelection               }
+  - { key: Key0,     mods: Control, action: ResetFontSize                }
+  - { key: Equals,   mods: Control, action: IncreaseFontSize             }
+  - { key: Subtract, mods: Control, action: DecreaseFontSize             }
+  - { key: L,        mods: Control, action: ClearLogNotice               }
+  - { key: L,        mods: Control, chars: "\x0c"                        }
+  - { key: Home,                    chars: "\x1bOH",   mode: AppCursor   }
+  - { key: Home,                    chars: "\x1b[H",   mode: ~AppCursor  }
+  - { key: End,                     chars: "\x1bOF",   mode: AppCursor   }
+  - { key: End,                     chars: "\x1b[F",   mode: ~AppCursor  }
+  - { key: PageUp,   mods: Shift,   chars: "\x1b[5;2~"                   }
+  - { key: PageUp,   mods: Control, chars: "\x1b[5;5~"                   }
+  - { key: PageUp,                  chars: "\x1b[5~"                     }
+  - { key: PageDown, mods: Shift,   chars: "\x1b[6;2~"                   }
+  - { key: PageDown, mods: Control, chars: "\x1b[6;5~"                   }
+  - { key: PageDown,                chars: "\x1b[6~"                     }
+  - { key: Tab,      mods: Shift,   chars: "\x1b[Z"                      }
+  - { key: Back,                    chars: "\x7f"                        }
+  - { key: Back,     mods: Alt,     chars: "\x1b\x7f"                    }
+  - { key: Insert,                  chars: "\x1b[2~"                     }
+  - { key: Delete,                  chars: "\x1b[3~"                     }
+  - { key: Left,     mods: Shift,   chars: "\x1b[1;2D"                   }
+  - { key: Left,     mods: Control, chars: "\x1b[1;5D"                   }
+  - { key: Left,     mods: Alt,     chars: "\x1b[1;3D"                   }
+  - { key: Left,                    chars: "\x1b[D",   mode: ~AppCursor  }
+  - { key: Left,                    chars: "\x1bOD",   mode: AppCursor   }
+  - { key: Right,    mods: Shift,   chars: "\x1b[1;2C"                   }
+  - { key: Right,    mods: Control, chars: "\x1b[1;5C"                   }
+  - { key: Right,    mods: Alt,     chars: "\x1b[1;3C"                   }
+  - { key: Right,                   chars: "\x1b[C",   mode: ~AppCursor  }
+  - { key: Right,                   chars: "\x1bOC",   mode: AppCursor   }
+  - { key: Up,       mods: Shift,   chars: "\x1b[1;2A"                   }
+  - { key: Up,       mods: Control, chars: "\x1b[1;5A"                   }
+  - { key: Up,       mods: Alt,     chars: "\x1b[1;3A"                   }
+  - { key: Up,                      chars: "\x1b[A",   mode: ~AppCursor  }
+  - { key: Up,                      chars: "\x1bOA",   mode: AppCursor   }
+  - { key: Down,     mods: Shift,   chars: "\x1b[1;2B"                   }
+  - { key: Down,     mods: Control, chars: "\x1b[1;5B"                   }
+  - { key: Down,     mods: Alt,     chars: "\x1b[1;3B"                   }
+  - { key: Down,                    chars: "\x1b[B",   mode: ~AppCursor  }
+  - { key: Down,                    chars: "\x1bOB",   mode: AppCursor   }
+  - { key: F1,                      chars: "\x1bOP"                      }
+  - { key: F2,                      chars: "\x1bOQ"                      }
+  - { key: F3,                      chars: "\x1bOR"                      }
+  - { key: F4,                      chars: "\x1bOS"                      }
+  - { key: F5,                      chars: "\x1b[15~"                    }
+  - { key: F6,                      chars: "\x1b[17~"                    }
+  - { key: F7,                      chars: "\x1b[18~"                    }
+  - { key: F8,                      chars: "\x1b[19~"                    }
+  - { key: F9,                      chars: "\x1b[20~"                    }
+  - { key: F10,                     chars: "\x1b[21~"                    }
+  - { key: F11,                     chars: "\x1b[23~"                    }
+  - { key: F12,                     chars: "\x1b[24~"                    }
+  - { key: F1,       mods: Shift,   chars: "\x1b[1;2P"                   }
+  - { key: F2,       mods: Shift,   chars: "\x1b[1;2Q"                   }
+  - { key: F3,       mods: Shift,   chars: "\x1b[1;2R"                   }
+  - { key: F4,       mods: Shift,   chars: "\x1b[1;2S"                   }
+  - { key: F5,       mods: Shift,   chars: "\x1b[15;2~"                  }
+  - { key: F6,       mods: Shift,   chars: "\x1b[17;2~"                  }
+  - { key: F7,       mods: Shift,   chars: "\x1b[18;2~"                  }
+  - { key: F8,       mods: Shift,   chars: "\x1b[19;2~"                  }
+  - { key: F9,       mods: Shift,   chars: "\x1b[20;2~"                  }
+  - { key: F10,      mods: Shift,   chars: "\x1b[21;2~"                  }
+  - { key: F11,      mods: Shift,   chars: "\x1b[23;2~"                  }
+  - { key: F12,      mods: Shift,   chars: "\x1b[24;2~"                  }
+  - { key: F1,       mods: Control, chars: "\x1b[1;5P"                   }
+  - { key: F2,       mods: Control, chars: "\x1b[1;5Q"                   }
+  - { key: F3,       mods: Control, chars: "\x1b[1;5R"                   }
+  - { key: F4,       mods: Control, chars: "\x1b[1;5S"                   }
+  - { key: F5,       mods: Control, chars: "\x1b[15;5~"                  }
+  - { key: F6,       mods: Control, chars: "\x1b[17;5~"                  }
+  - { key: F7,       mods: Control, chars: "\x1b[18;5~"                  }
+  - { key: F8,       mods: Control, chars: "\x1b[19;5~"                  }
+  - { key: F9,       mods: Control, chars: "\x1b[20;5~"                  }
+  - { key: F10,      mods: Control, chars: "\x1b[21;5~"                  }
+  - { key: F11,      mods: Control, chars: "\x1b[23;5~"                  }
+  - { key: F12,      mods: Control, chars: "\x1b[24;5~"                  }
+  - { key: F1,       mods: Alt,     chars: "\x1b[1;6P"                   }
+  - { key: F2,       mods: Alt,     chars: "\x1b[1;6Q"                   }
+  - { key: F3,       mods: Alt,     chars: "\x1b[1;6R"                   }
+  - { key: F4,       mods: Alt,     chars: "\x1b[1;6S"                   }
+  - { key: F5,       mods: Alt,     chars: "\x1b[15;6~"                  }
+  - { key: F6,       mods: Alt,     chars: "\x1b[17;6~"                  }
+  - { key: F7,       mods: Alt,     chars: "\x1b[18;6~"                  }
+  - { key: F8,       mods: Alt,     chars: "\x1b[19;6~"                  }
+  - { key: F9,       mods: Alt,     chars: "\x1b[20;6~"                  }
+  - { key: F10,      mods: Alt,     chars: "\x1b[21;6~"                  }
+  - { key: F11,      mods: Alt,     chars: "\x1b[23;6~"                  }
+  - { key: F12,      mods: Alt,     chars: "\x1b[24;6~"                  }
+  - { key: F1,       mods: Super,   chars: "\x1b[1;3P"                   }
+  - { key: F2,       mods: Super,   chars: "\x1b[1;3Q"                   }
+  - { key: F3,       mods: Super,   chars: "\x1b[1;3R"                   }
+  - { key: F4,       mods: Super,   chars: "\x1b[1;3S"                   }
+  - { key: F5,       mods: Super,   chars: "\x1b[15;3~"                  }
+  - { key: F6,       mods: Super,   chars: "\x1b[17;3~"                  }
+  - { key: F7,       mods: Super,   chars: "\x1b[18;3~"                  }
+  - { key: F8,       mods: Super,   chars: "\x1b[19;3~"                  }
+  - { key: F9,       mods: Super,   chars: "\x1b[20;3~"                  }
+  - { key: F10,      mods: Super,   chars: "\x1b[21;3~"                  }
+  - { key: F11,      mods: Super,   chars: "\x1b[23;3~"                  }
+  - { key: F12,      mods: Super,   chars: "\x1b[24;3~"                  }
diff --git a/modules/workstation/windowing/alacritty/default.nix b/modules/workstation/windowing/alacritty/default.nix
new file mode 100644
index 000000000000..e8d086f54d8c
--- /dev/null
+++ b/modules/workstation/windowing/alacritty/default.nix
@@ -0,0 +1,11 @@
+{ pkgs, ... }:
+
+{
+  imports = [ ../../../xdg ];
+
+  environment.systemPackages = with pkgs;
+    lib.optional (!stdenv.isDarwin) alacritty;
+
+  users.users.qyliss.xdg.config.paths."alacritty/alacritty.yml" =
+    pkgs.copyPathToStore ./config.yml;
+}
diff --git a/modules/workstation/windowing/default.nix b/modules/workstation/windowing/default.nix
new file mode 100644
index 000000000000..8387f1981ac4
--- /dev/null
+++ b/modules/workstation/windowing/default.nix
@@ -0,0 +1,7 @@
+{ pkgs, ... }:
+
+{
+  imports = [ ./alacritty ./firefox ./gnome-mines ./sway ];
+
+  environment.systemPackages = with pkgs; [ imv wf-recorder ];
+}
diff --git a/modules/workstation/windowing/firefox/default.nix b/modules/workstation/windowing/firefox/default.nix
new file mode 100644
index 000000000000..877f0ef0892c
--- /dev/null
+++ b/modules/workstation/windowing/firefox/default.nix
@@ -0,0 +1,13 @@
+{ pkgs, ... }:
+
+{
+  home.qyliss.dirs.state.activationScripts.profile = ''
+    install -o qyliss -g users -d mozilla{,/firefox{,/default}}
+    ln -sf ${./profiles.ini} mozilla/firefox/profiles.ini
+    ln -sf ${./user.js} mozilla/firefox/default/user.js
+  '';
+
+  environment.systemPackages = with pkgs; [ firefox-wayland ];
+
+  environment.variables.BROWSER = "firefox";
+}
diff --git a/modules/workstation/windowing/firefox/profiles.ini b/modules/workstation/windowing/firefox/profiles.ini
new file mode 100644
index 000000000000..becf53354e76
--- /dev/null
+++ b/modules/workstation/windowing/firefox/profiles.ini
@@ -0,0 +1,8 @@
+[General]
+StartWithLastProfile=1
+
+[Profile0]
+Name=default
+IsRelative=1
+Path=default
+Default=1
diff --git a/modules/workstation/windowing/firefox/user.js b/modules/workstation/windowing/firefox/user.js
new file mode 100644
index 000000000000..8b137891791f
--- /dev/null
+++ b/modules/workstation/windowing/firefox/user.js
@@ -0,0 +1 @@
+
diff --git a/modules/workstation/windowing/gnome-mines/default.nix b/modules/workstation/windowing/gnome-mines/default.nix
new file mode 100644
index 000000000000..a2376676d007
--- /dev/null
+++ b/modules/workstation/windowing/gnome-mines/default.nix
@@ -0,0 +1,7 @@
+{ pkgs, ... }:
+
+{
+  environment.systemPackages = with pkgs; [ gnome3.gnome-mines ];
+
+  home.qyliss.dirs."state/gnome-mines" = {};
+}
diff --git a/modules/workstation/windowing/sway/choose_workspace.sh.in b/modules/workstation/windowing/sway/choose_workspace.sh.in
new file mode 100644
index 000000000000..963746e0c810
--- /dev/null
+++ b/modules/workstation/windowing/sway/choose_workspace.sh.in
@@ -0,0 +1,17 @@
+#! @shell@ -ue
+swaymsg -t get_workspaces |
+    @jq@/bin/jq -r \
+        '(to_entries | map(select(.value.focused)) | .[0].key), .[].name' |
+    (
+        read index
+        exec @bemenu@/bin/bemenu \
+            -p workspace \
+            -I "$index" \
+            -H 24 \
+            --fn 'monospace 10' \
+            --nf '#777777' \
+            --hb '#285577' \
+            --hf '#ffffff' \
+            --tf '#777777' \
+            --ff '#ffffff'
+    )
diff --git a/modules/workstation/windowing/sway/config.in b/modules/workstation/windowing/sway/config.in
new file mode 100644
index 000000000000..6770080462af
--- /dev/null
+++ b/modules/workstation/windowing/sway/config.in
@@ -0,0 +1,110 @@
+set $mod Mod4
+set $left h
+set $down j
+set $up k
+set $right l
+
+default_border pixel
+default_floating_border normal
+
+client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a00
+client.unfocused        #333333 #222222 #888888 #292d2e #22222200
+
+for_window [app_id="float"] floating enable
+for_window [class="Tor Browser"] floating enable
+for_window [class="XDvi"] floating enable
+for_window [instance="xdvi"] floating enable
+for_window [class="XDvi" instance="xdvi"] floating disable
+
+# Stop the Firefox sharing indicator (that appears when on an
+# audio/video call) tiling, or appearing in the center of the display,
+# or being focused.
+no_focus [title="Firefox - Sharing Indicator"]
+for_window [title="Firefox - Sharing Indicator"] {
+    floating enable
+    sticky enable
+
+    # I'd really like this to be at the top, horizontally centered.
+    # Or maybe at the bottom right.  But there's not really a way to
+    # do that in sway configuration, as far as I can tell.  I think
+    # I'd need to exec a program that would measure the display size
+    # and compute the coordinates or something.  That sounds horrible
+    # and fragile, so top left it is.
+    move position 0 0
+}
+
+input * natural_scroll enabled
+output * bg @wallpaper@ fill
+
+bindsym $mod+Return exec alacritty
+bindsym $mod+backslash exec firefox
+bindsym $mod+Shift+q kill
+bindsym $mod+d exec swaymsg exec "$(choosebin --tiebreak=begin,length,index)"
+
+# Drag floating windows by holding down $mod and left mouse button.
+# Resize them with right mouse button + $mod.
+# Despite the name, also works for non-floating windows.
+# Change normal to inverse to use left mouse button for resizing and right
+# mouse button for dragging.
+floating_modifier $mod normal
+
+# reload the configuration file
+bindsym $mod+Shift+c reload
+
+# exit sway (logs you out of your Wayland session)
+bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'
+
+bindsym $mod+$left focus left
+bindsym $mod+$down focus down
+bindsym $mod+$up focus up
+bindsym $mod+$right focus right
+
+bindsym $mod+Shift+$left move left
+bindsym $mod+Shift+$down move down
+bindsym $mod+Shift+$up move up
+bindsym $mod+Shift+$right move right
+
+bindsym $mod+g exec swaymsg workspace "$(@choose_workspace@)"
+bindsym $mod+Shift+g exec swaymsg move container to workspace "$(@choose_workspace@)"
+
+bindsym $mod+b splith
+bindsym $mod+v splitv
+
+bindsym $mod+s layout stacking
+bindsym $mod+w layout tabbed
+bindsym $mod+e layout toggle split
+
+bindsym $mod+f fullscreen
+
+bindsym $mod+Shift+space floating toggle
+bindsym $mod+space focus mode_toggle
+
+bindsym $mod+a focus parent
+
+bindsym $mod+Shift+minus move scratchpad
+bindsym $mod+minus scratchpad show
+
+mode "resize" {
+    bindsym $left resize shrink width 10px
+    bindsym $down resize grow height 10px
+    bindsym $up resize shrink height 10px
+    bindsym $right resize grow width 10px
+
+    bindsym Return mode "default"
+    bindsym Escape mode "default"
+}
+bindsym $mod+r mode "resize"
+
+bar {
+    position top
+
+    status_command @status_command@
+
+    colors {
+        statusline #ffffff
+        background #00000077
+        inactive_workspace #33333377 #00000077 #FFFFFF77
+    }
+}
+
+@extraConfig@
diff --git a/modules/workstation/windowing/sway/default.nix b/modules/workstation/windowing/sway/default.nix
new file mode 100644
index 000000000000..5901639d1baa
--- /dev/null
+++ b/modules/workstation/windowing/sway/default.nix
@@ -0,0 +1,52 @@
+{ pkgs, lib, config, ... }:
+
+{
+  imports = [ ./swayidle ];
+
+  options = {
+    programs.sway.extraConfig = with lib; mkOption {
+      type = types.lines;
+      description = "Lines to append to sway's config file";
+      default = "";
+    };
+  };
+
+  config = let
+    cfg = config.programs.sway;
+
+    wallpaper = pkgs.fetchurl {
+      url = https://mir-s3-cdn-cf.behance.net/project_modules/2800_opt_1/36731876964505.5c793fa788b5d.jpg;
+      sha256 = "1c6camdipng8ws41sgpcxzrxb96crgip3wirqjgf2ajn60qg3v64";
+
+      meta = with lib; {
+        homepage = https://www.behance.net/gallery/76964505/IQOO-style-frame-and-scene-design;
+      };
+    };
+
+    configFile = pkgs.substituteAll {
+      src = ./config.in;
+      inherit choose_workspace status_command wallpaper;
+      inherit (cfg) extraConfig;
+    };
+
+    status_command = pkgs.runCommandCC "status" {} ''
+      c++ -std=c++17 -o $out ${./status.cpp}
+    '';
+
+    choose_workspace = pkgs.substituteAll {
+      src = ./choose_workspace.sh.in;
+      isExecutable = true;
+      inherit (pkgs) bemenu jq;
+    };
+
+  in
+
+  {
+    environment.systemPackages = with pkgs; [ bemenu choose ];
+
+    programs.sway.enable = true;
+    programs.swayidle.enable = true;
+
+    users.users.qyliss.xdg.config.paths."sway/config" = configFile;
+  };
+}
diff --git a/modules/workstation/windowing/sway/status.cpp b/modules/workstation/windowing/sway/status.cpp
new file mode 100644
index 000000000000..71acb38eeb70
--- /dev/null
+++ b/modules/workstation/windowing/sway/status.cpp
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// Copyright 2020 Alyssa Ross
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+#include <chrono>
+#include <fcntl.h>
+#include <filesystem>
+#include <iostream>
+#include <thread>
+#include <unistd.h>
+
+namespace fs = std::filesystem;
+
+using fs::directory_iterator;
+using std::chrono::seconds;
+using std::generic_category;
+using std::put_time;
+using std::stoi;
+using std::string;
+using std::stringstream;
+using std::system_error;
+using std::this_thread::sleep_for;
+using std::time;
+
+enum BatteryStatus {
+	Unknown,
+	Charging,
+	Discharging,
+	NotCharging,
+	Full,
+};
+
+class Battery {
+public:
+	Battery(string name);
+
+	string name() { return m_name; }
+	fs::path path();
+	BatteryStatus status();
+	int capacity();
+
+private:
+	string m_name;
+	string attr(string name);
+};
+
+Battery::Battery(string name)
+{
+	m_name = name;
+}
+
+fs::path Battery::path()
+{
+	static fs::path base = "/sys/class/power_supply";
+	return base / name();
+}
+
+BatteryStatus Battery::status()
+{
+	auto status = attr("status");
+
+	if (status == "Charging")
+		return Charging;
+	if (status == "Discharging")
+		return Discharging;
+	if (status == "Not charging")
+		return NotCharging;
+	if (status == "Full")
+		return Full;
+
+	return Unknown;
+}
+
+int Battery::capacity()
+{
+	return stoi(attr("capacity"));
+}
+
+string Battery::attr(string name)
+{
+	// Use read() to make sure this is done in a single read syscall,
+	// because sysfs doesn't like multiple reads.
+	int fd = open((path() / name).c_str(), O_RDONLY);
+	if (fd == -1)
+		throw system_error(errno, generic_category());
+	char buf[13];
+	int len = read(fd, &buf, 13);
+	if (len == -1)
+		throw system_error(errno, generic_category());
+	close(fd);
+	string capacity (buf, len);
+	if (capacity.back() == '\n')
+		capacity.pop_back();
+	return capacity;
+}
+
+int main(void)
+{
+	while (true) {
+		// Buffer output so it can be done all at once in a single write(),
+		// because of <https://github.com/swaywm/sway/issues/3857>.
+		stringstream out;
+
+		for (const auto& entry : directory_iterator("/sys/class/power_supply")) {
+			auto name = entry.path().filename().string();
+			if (name.find("BAT") != 0)
+				continue;
+
+			Battery battery (name);
+			BatteryStatus status;
+			int capacity;
+
+			try {
+				status = battery.status();
+				capacity = battery.capacity();
+			} catch (const system_error& ex) {
+				if (ex.code().value() == ENOENT)
+					continue;
+
+				throw ex;
+			}
+
+			switch (status) {
+			case Charging:
+				out << "↑";
+				break;
+			case Discharging:
+				out << "↓";
+				break;
+			default:
+				out << " ";
+			}
+
+			out << capacity << "%  ";
+		}
+
+		auto t = time(nullptr);
+		out << put_time(localtime(&t), "%F %T") << "\n";
+
+		auto buf = out.str();
+		if (write(STDOUT_FILENO, buf.c_str(), buf.length()) == -1)
+			perror("write");
+
+		sleep_for(seconds(1));
+	}
+}
diff --git a/modules/workstation/windowing/sway/swayidle/default.nix b/modules/workstation/windowing/sway/swayidle/default.nix
new file mode 100644
index 000000000000..8e5f264b5038
--- /dev/null
+++ b/modules/workstation/windowing/sway/swayidle/default.nix
@@ -0,0 +1,23 @@
+{ lib, config, ... }:
+
+let
+  cfg = config.programs.swayidle;
+in
+
+with lib;
+
+{
+  options = {
+    programs.swayidle.enable = mkEnableOption "swayidle";
+  };
+
+  config = mkIf cfg.enable {
+    programs.sway.extraConfig = ''
+      exec swayidle \
+          timeout 300 'swaylock -c 000000' \
+          timeout 600 'swaymsg "output * dpms off"' \
+          resume 'swaymsg "output * dpms on"' \
+          before-sleep 'swaylock -c 000000'
+    '';
+  };
+}
diff --git a/modules/xdg/default.nix b/modules/xdg/default.nix
new file mode 100644
index 000000000000..bf7783cd3986
--- /dev/null
+++ b/modules/xdg/default.nix
@@ -0,0 +1,44 @@
+{ lib, pkgs, config, ... }:
+
+let
+  inherit (lib) filterAttrs mapAttrsToList mkOption;
+  inherit (lib.types) loaOf attrsOf path submodule;
+  inherit (pkgs) linkFarm;
+
+  userOptions = {
+    options.xdg.config.paths = mkOption {
+      default = {};
+      type = attrsOf path;
+    };
+  };
+
+in
+  {
+    options = {
+      users.users = mkOption {
+        type = loaOf (submodule userOptions);
+      };
+    };
+
+    config = {
+      environment.etc."xdg/nixos/per-user".source =
+        linkFarm "xdg-config-users"
+                  (mapAttrsToList (user: { xdg, ... }: {
+                    name = user;
+                    path = linkFarm "${user}-xdg-config-home"
+                                    (mapAttrsToList
+                                      (name: path: { inherit name path; })
+                                      xdg.config.paths);
+                  }) (filterAttrs (_: { xdg, ... }: xdg.config.paths != {})
+                                  config.users.users));
+
+      environment.extraInit = ''
+        etc_xdg_config_home=/run/current-system/etc/xdg/nixos/per-user/$USER
+        if [ -d "$etc_xdg_config_home" ]; then
+            export XDG_CONFIG_HOME="''${XDG_CONFIG_HOME-$etc_xdg_config_home}"
+        fi
+        export XDG_DATA_HOME="''${XDG_DATA_HOME-$HOME/state}"
+        export XDG_CACHE_HOME="''${XDG_CACHE_HOME-$HOME/state/cache}"
+      '';
+    };
+  }