summary refs log tree commit diff stats
path: root/system
diff options
context:
space:
mode:
Diffstat (limited to 'system')
-rw-r--r--system/.gitignore5
-rw-r--r--system/README.org13
-rw-r--r--system/cachix.nix14
-rw-r--r--system/marvin.nix38
-rw-r--r--system/modules/base.nix13
-rw-r--r--system/modules/configuration/berlin.nix8
-rw-r--r--system/modules/configuration/british-english.nix4
-rw-r--r--system/modules/configuration/england.nix8
-rw-r--r--system/modules/configuration/nix.nix38
-rw-r--r--system/modules/configuration/user.nix11
-rw-r--r--system/modules/development/base.nix32
-rw-r--r--system/modules/development/javascript.nix50
-rw-r--r--system/modules/development/kubernetes.nix21
-rw-r--r--system/modules/development/lisp.nix13
-rw-r--r--system/modules/gaming.nix67
-rw-r--r--system/modules/hardware/adb.nix9
-rw-r--r--system/modules/hardware/audio.nix20
-rw-r--r--system/modules/hardware/bare-metal.nix29
-rw-r--r--system/modules/hardware/connman.nix14
-rw-r--r--system/modules/hardware/grub2.nix13
-rw-r--r--system/modules/hardware/hidpi.nix9
-rw-r--r--system/modules/hardware/intel-gpu.nix15
-rw-r--r--system/modules/hardware/keyboardio-model01.nix13
-rw-r--r--system/modules/hardware/laptop.nix87
-rw-r--r--system/modules/hardware/mouse.nix12
-rw-r--r--system/modules/hardware/network-manager.nix12
-rw-r--r--system/modules/hardware/nitrokey.nix13
-rw-r--r--system/modules/hardware/nvidia-gpu.nix5
-rw-r--r--system/modules/hardware/printing.nix19
-rw-r--r--system/modules/hardware/qwerty.nix16
-rw-r--r--system/modules/hardware/synaptics.nix27
-rw-r--r--system/modules/hardware/systemd-boot.nix10
-rw-r--r--system/modules/hardware/thinkpad.nix26
-rw-r--r--system/modules/hardware/trackball.nix13
-rw-r--r--system/modules/hardware/trezor.nix13
-rw-r--r--system/modules/machines/t470s.nix59
-rw-r--r--system/modules/machines/x250.nix33
-rw-r--r--system/modules/programs/accounting.nix10
-rw-r--r--system/modules/programs/barrier.nix7
-rw-r--r--system/modules/programs/dotfiles.nix14
-rw-r--r--system/modules/programs/gnome.nix24
-rw-r--r--system/modules/programs/gnupg.nix10
-rw-r--r--system/modules/programs/i3.nix21
-rw-r--r--system/modules/programs/infrastructure.nix8
-rw-r--r--system/modules/programs/kde.nix20
-rw-r--r--system/modules/programs/keybase.nix11
-rw-r--r--system/modules/programs/passwords.nix9
-rw-r--r--system/modules/programs/shell.nix34
-rw-r--r--system/modules/programs/tor.nix22
-rw-r--r--system/modules/programs/window-manager.nix47
-rw-r--r--system/modules/programs/wine.nix6
-rw-r--r--system/modules/satoshipay.nix96
-rw-r--r--system/modules/services/syncthing.nix11
-rw-r--r--system/modules/services/xserver.nix78
-rw-r--r--system/modules/services/zeroconf.nix16
-rw-r--r--system/modules/user-interface.nix144
-rw-r--r--system/packages/emacs.nix127
-rw-r--r--system/packages/node2nix/Makefile5
-rw-r--r--system/packages/node2nix/default.nix17
-rw-r--r--system/packages/node2nix/node-env.nix540
-rw-r--r--system/packages/node2nix/node-packages.json8
-rw-r--r--system/packages/node2nix/node-packages.nix1199
-rw-r--r--system/prefect.nix50
-rw-r--r--system/satoshipad.nix69
-rw-r--r--system/trillian.nix133
65 files changed, 3538 insertions, 0 deletions
diff --git a/system/.gitignore b/system/.gitignore
new file mode 100644
index 00000000..1ba8e950
--- /dev/null
+++ b/system/.gitignore
@@ -0,0 +1,5 @@
+/private/
+/hardware-configuration.nix
+/configuration.nix
+/darwin-configuration.nix
+/cachix
diff --git a/system/README.org b/system/README.org
new file mode 100644
index 00000000..cc07d58d
--- /dev/null
+++ b/system/README.org
@@ -0,0 +1,13 @@
+* nixos-configuration
+
+This repository is for my [[https://nixos.org/][NixOS]] configuration.
+
+** Setup
+
+1. Clone or symlink to =/etc/nixos=
+2. If necessary, re-create relevant files in =./private=
+3. Run =nixos-generate-config= if on a fresh machine
+4. Remove =configuration.nix=
+5. Link a machine configuration to =configuration.nix=
+6. Apply configuration with =nixos-rebuild= or =nixos-install=
+
diff --git a/system/cachix.nix b/system/cachix.nix
new file mode 100644
index 00000000..cce90a32
--- /dev/null
+++ b/system/cachix.nix
@@ -0,0 +1,14 @@
+
+# WARN: this file will get overwritten by $ cachix use <name>
+{ pkgs, lib, ... }:
+
+let
+  folder = ./cachix;
+  toImport = name: value: folder + ("/" + name);
+  filterCaches = key: value: value == "regular" && lib.hasSuffix ".nix" key;
+  imports = lib.mapAttrsToList toImport (lib.filterAttrs filterCaches (builtins.readDir folder));
+in {
+  inherit imports;
+  nix.binaryCaches = ["https://cache.nixos.org/"];
+}
+    
\ No newline at end of file
diff --git a/system/marvin.nix b/system/marvin.nix
new file mode 100644
index 00000000..0a7aceb5
--- /dev/null
+++ b/system/marvin.nix
@@ -0,0 +1,38 @@
+{ config, pkgs, ... }:
+
+{ imports = [
+    ./hardware-configuration.nix
+
+    ./modules/base.nix
+    ./modules/configuration/berlin.nix
+    ./modules/configuration/british-english.nix
+    ./modules/configuration/nix.nix
+    ./modules/configuration/user.nix
+    ./modules/development/base.nix
+    ./modules/development/javascript.nix
+    ./modules/development/lisp.nix
+    ./modules/hardware/adb.nix
+    ./modules/hardware/audio.nix
+    ./modules/hardware/qwerty.nix
+    ./modules/hardware/network-manager.nix
+    ./modules/hardware/nitrokey.nix
+    ./modules/hardware/systemd-boot.nix
+    ./modules/hardware/trackball.nix
+    ./modules/hardware/trezor.nix
+    ./modules/machines/x250.nix
+    ./modules/programs/accounting.nix
+    ./modules/programs/dotfiles.nix
+    ./modules/programs/i3.nix
+    ./modules/programs/infrastructure.nix
+    ./modules/programs/passwords.nix
+    ./modules/programs/shell.nix
+    ./modules/services/syncthing.nix
+  ];
+
+  networking = {
+    hostName = "marvin";
+    domain = "alanpearce.eu";
+  };
+
+  system.stateVersion = "18.09";
+}
diff --git a/system/modules/base.nix b/system/modules/base.nix
new file mode 100644
index 00000000..9e94a5a2
--- /dev/null
+++ b/system/modules/base.nix
@@ -0,0 +1,13 @@
+{ config, pkgs, ... }:
+
+{ boot.loader.timeout = 1;
+
+  environment.systemPackages = with pkgs; [
+    nix-index
+  ];
+
+  networking.extraHosts = ''
+    127.0.0.1 ${config.networking.hostName}
+    ::1 ${config.networking.hostName}
+  '';
+}
diff --git a/system/modules/configuration/berlin.nix b/system/modules/configuration/berlin.nix
new file mode 100644
index 00000000..246510c4
--- /dev/null
+++ b/system/modules/configuration/berlin.nix
@@ -0,0 +1,8 @@
+{ config, pkgs, ... }:
+
+{ time.timeZone = "Europe/Berlin";
+  services.redshift = {
+    latitude = "52.586";
+    longitude = "13.300";
+  };
+}
diff --git a/system/modules/configuration/british-english.nix b/system/modules/configuration/british-english.nix
new file mode 100644
index 00000000..3ff93678
--- /dev/null
+++ b/system/modules/configuration/british-english.nix
@@ -0,0 +1,4 @@
+{ config, pkgs, ... }:
+
+{ i18n.defaultLocale = "en_GB.UTF-8";
+}
diff --git a/system/modules/configuration/england.nix b/system/modules/configuration/england.nix
new file mode 100644
index 00000000..b2f799cd
--- /dev/null
+++ b/system/modules/configuration/england.nix
@@ -0,0 +1,8 @@
+{ config, pkgs, ... }:
+
+{ time.timeZone = "Europe/London";
+  services.redshift = {
+    latitude = "52.2394";
+    longitude = "-0.9416";
+  };
+}
diff --git a/system/modules/configuration/nix.nix b/system/modules/configuration/nix.nix
new file mode 100644
index 00000000..9aa83435
--- /dev/null
+++ b/system/modules/configuration/nix.nix
@@ -0,0 +1,38 @@
+{ config, pkgs, ... }:
+
+{ nix = {
+    buildCores = 0;
+
+    daemonNiceLevel = 19;
+    daemonIONiceLevel = 7;
+
+    autoOptimiseStore = true;
+    gc = {
+      automatic = true;
+      options = "--delete-older-than 14d";
+    };
+  };
+
+  nixpkgs.config = {
+    packageOverrides = pkgs: {
+      unstable = import <nixos-unstable> {
+        config = config.nixpkgs.config;
+      };
+    };
+  };
+
+  environment.systemPackages = with pkgs; [
+    cachix
+  ];
+
+  system.autoUpgrade = {
+    enable = true;
+    flags = [ "--max-jobs" "1" ];
+  };
+  systemd.services.nixos-upgrade = {
+    script = pkgs.lib.mkForce ''
+      ${pkgs.nix}/bin/nix-channel --update nixos-unstable
+      ${config.system.build.nixos-rebuild}/bin/nixos-rebuild boot --no-build-output --upgrade ${toString config.system.autoUpgrade.flags}
+    '';
+  };
+}
diff --git a/system/modules/configuration/user.nix b/system/modules/configuration/user.nix
new file mode 100644
index 00000000..e7362bce
--- /dev/null
+++ b/system/modules/configuration/user.nix
@@ -0,0 +1,11 @@
+{ config, pkgs, ... }:
+
+{ users.extraUsers.alan = {
+    description = "Alan Pearce";
+    isNormalUser = true;
+    extraGroups = [ "audio" "wheel" "lp" "adbusers" "docker" "nitrokey" "dialout" "networkmanager" ];
+    shell = "/run/current-system/sw/bin/zsh";
+    home = "/home/alan";
+    uid = 1000;
+  };
+}
diff --git a/system/modules/development/base.nix b/system/modules/development/base.nix
new file mode 100644
index 00000000..2beed5b3
--- /dev/null
+++ b/system/modules/development/base.nix
@@ -0,0 +1,32 @@
+{ config, pkgs, ... }:
+
+{ environment.systemPackages = with pkgs; [
+    gitAndTools.gitFull
+    gitAndTools.git-extras
+    git-lfs
+
+    checkbashisms
+    editorconfig-core-c
+    go
+
+    wrk
+
+    ag
+    (ripgrep.override { withPCRE2 = true; })
+
+    httpie
+    jq
+
+    discount
+  ] ++ (
+    if !stdenv.isDarwin
+    then [
+      whois
+      ldns
+      httping
+      http-prompt
+      firefox-devedition-bin
+    ] else [
+    ]
+  );
+}
diff --git a/system/modules/development/javascript.nix b/system/modules/development/javascript.nix
new file mode 100644
index 00000000..1bdd4a4c
--- /dev/null
+++ b/system/modules/development/javascript.nix
@@ -0,0 +1,50 @@
+{ config, pkgs, ... }:
+
+let
+  node = pkgs.unstable.nodejs-10_x;
+  npmPackages = pkgs.unstable.nodePackages_10_x;
+  node2nixPackages = import ../../packages/node2nix/default.nix {
+    pkgs = pkgs.unstable;
+    nodejs = node;
+  };
+in
+{ environment.systemPackages = (with pkgs.unstable; [
+    node
+  ] ++ (
+    if stdenv.isDarwin
+    then
+    [
+    ]
+    else
+    [
+    # npm install may use any of these
+    binutils
+    gcc
+    gnumake
+    python2
+    ]
+  )) ++ (with npmPackages; [
+    node-gyp
+    node-gyp-build
+    node-pre-gyp
+
+    tern
+    node2nix
+    nodemon
+    javascript-typescript-langserver
+    typescript-language-server
+    vscode-css-languageserver-bin
+    vscode-html-languageserver-bin
+    csslint
+    eslint_d
+    prettier
+    typescript
+
+    node2nixPackages.bunyan
+    node2nixPackages.pino-pretty
+    node2nixPackages."pnpm-3.6.2"
+    node2nixPackages.prettier_d
+    node2nixPackages.dockerfile-language-server-nodejs
+    node2nixPackages.yaml-language-server
+  ]);
+}
diff --git a/system/modules/development/kubernetes.nix b/system/modules/development/kubernetes.nix
new file mode 100644
index 00000000..692e46d6
--- /dev/null
+++ b/system/modules/development/kubernetes.nix
@@ -0,0 +1,21 @@
+{ config, pkgs, ... }:
+
+{
+  networking.bridges = {
+    cbr0.interfaces = [];
+  };
+  networking.interfaces = {
+    cbr0 = {
+      ipv4.addresses = [
+      { address = "10.10.0.1";
+        prefixLength = 24;
+      }
+      ];
+    };
+  };
+  services.kubernetes.roles = ["master" "node"];
+  services.kubernetes.kubelet.extraOpts = "--fail-swap-on=false";
+  virtualisation.docker.extraOptions = ''
+    --iptables=false --ip-masq=false -b cbr0
+  '';
+}
diff --git a/system/modules/development/lisp.nix b/system/modules/development/lisp.nix
new file mode 100644
index 00000000..dc521feb
--- /dev/null
+++ b/system/modules/development/lisp.nix
@@ -0,0 +1,13 @@
+{ config, pkgs, ... }:
+
+{ environment.systemPackages = with pkgs; [
+    ccl
+    sbcl
+    lispPackages.quicklisp
+    asdf
+    cl-launch
+
+    dust
+    pixie
+  ];
+}
diff --git a/system/modules/gaming.nix b/system/modules/gaming.nix
new file mode 100644
index 00000000..44b937a8
--- /dev/null
+++ b/system/modules/gaming.nix
@@ -0,0 +1,67 @@
+{ config, pkgs, ... }:
+
+{ environment.systemPackages = with pkgs; [
+    unstable.steam
+    (
+      unstable.winePackages.unstable.override {
+        pngSupport = true;
+        jpegSupport = true;
+        tiffSupport = true;
+        gettextSupport = true;
+        fontconfigSupport = true;
+        alsaSupport = true;
+        gtkSupport = true;
+        openglSupport = true;
+        tlsSupport = true;
+        gstreamerSupport = true;
+        cupsSupport = true;
+        colorManagementSupport = true;
+        dbusSupport = true;
+        mpg123Support = true;
+        openalSupport = true;
+        openclSupport = true;
+        cairoSupport = true;
+        odbcSupport = true;
+        netapiSupport = true;
+        cursesSupport = true;
+        vaSupport = true;
+        pcapSupport = true;
+        v4lSupport = true;
+        saneSupport = true;
+        gsmSupport = true;
+        gphoto2Support = true;
+        ldapSupport = true;
+        pulseaudioSupport = true;
+        udevSupport = true;
+        xineramaSupport = true;
+        xmlSupport = true;
+        vulkanSupport = true;
+        sdlSupport = true;
+      }
+    )
+    unstable.lutris
+  ];
+  hardware.steam-hardware.enable = true;
+  hardware.opengl.driSupport32Bit = true;
+  hardware.pulseaudio.support32Bit = true;
+
+  systemd = {
+    extraConfig = ''
+      DefaultLimitNOFILE=524288
+    '';
+    user.extraConfig = ''
+      DefaultLimitNOFILE=524288
+    '';
+  };
+
+  networking.firewall = {
+    allowedUDPPorts = [
+      27031
+      27036
+    ];
+    allowedTCPPorts = [
+      27036
+      27037
+    ];
+  };
+}
diff --git a/system/modules/hardware/adb.nix b/system/modules/hardware/adb.nix
new file mode 100644
index 00000000..8b511f55
--- /dev/null
+++ b/system/modules/hardware/adb.nix
@@ -0,0 +1,9 @@
+{ config, pkgs, ... }:
+
+{ programs.adb.enable = true;
+  users.groups.adbusers = {};
+
+  services.udev = {
+   packages = [ pkgs.android-udev-rules ];
+  };
+}
diff --git a/system/modules/hardware/audio.nix b/system/modules/hardware/audio.nix
new file mode 100644
index 00000000..ed956919
--- /dev/null
+++ b/system/modules/hardware/audio.nix
@@ -0,0 +1,20 @@
+{ config, pkgs, ... }:
+
+{ hardware.pulseaudio = {
+    enable = true;
+    support32Bit = true;
+    daemon.config = {
+      flat-volumes = "no";
+    };
+    package = if config.hardware.bluetooth.enable
+      then pkgs.pulseaudioFull
+      else pkgs.pulseaudio;
+  };
+
+  sound.enable = true;
+
+  environment.systemPackages = with pkgs; [
+    pamixer
+    pavucontrol
+  ];
+}
diff --git a/system/modules/hardware/bare-metal.nix b/system/modules/hardware/bare-metal.nix
new file mode 100644
index 00000000..0a61790a
--- /dev/null
+++ b/system/modules/hardware/bare-metal.nix
@@ -0,0 +1,29 @@
+{ config, pkgs, ... }:
+
+{ environment.systemPackages = with pkgs; [
+    fuse_exfat
+    cryptsetup
+    dmidecode
+    hdparm
+    pciutils
+    usbutils
+  ];
+
+  hardware.cpu.intel.updateMicrocode = true;
+
+  boot.kernel.sysctl = {
+    "net.ipv4.tcp_allowed_congestion_control" = "illinois reno lp";
+    "net.ipv4.tcp_congestion_control" = "illinois";
+  };
+
+  zramSwap = {
+    enable = true;
+    algorithm = "zstd";
+  };
+  boot.tmpOnTmpfs = true;
+
+  boot.kernelModules = [ "bfq" ];
+
+  fileSystems."/".options = [ "noatime" "nodiratime" ];
+  fileSystems."/home".options = [ "noatime" "nodiratime" ];
+}
diff --git a/system/modules/hardware/connman.nix b/system/modules/hardware/connman.nix
new file mode 100644
index 00000000..0361f9cb
--- /dev/null
+++ b/system/modules/hardware/connman.nix
@@ -0,0 +1,14 @@
+{ config, pkgs, ... }:
+
+{ networking.connman = {
+    enable = true;
+    enableVPN = false;
+  };
+  networking.wireless.enable = true;
+
+  environment.systemPackages = with pkgs; [
+    cmst
+    connman-notify
+    connman_dmenu
+  ];
+}
diff --git a/system/modules/hardware/grub2.nix b/system/modules/hardware/grub2.nix
new file mode 100644
index 00000000..70e86e71
--- /dev/null
+++ b/system/modules/hardware/grub2.nix
@@ -0,0 +1,13 @@
+{ config, pkgs, ... }:
+
+{ boot.loader = {
+    grub = {
+      enable = true;
+      splashImage = null;
+      version = 2;
+      device = "nodev";
+      efiSupport = true;
+    };
+    efi.canTouchEfiVariables = true;
+  };
+}
diff --git a/system/modules/hardware/hidpi.nix b/system/modules/hardware/hidpi.nix
new file mode 100644
index 00000000..1f4644c5
--- /dev/null
+++ b/system/modules/hardware/hidpi.nix
@@ -0,0 +1,9 @@
+{ config, pkgs, ... }:
+
+{ i18n = {
+    consoleFont = "ter-v24b";
+    consolePackages = with pkgs; [
+      terminus_font
+    ];
+  };
+}
diff --git a/system/modules/hardware/intel-gpu.nix b/system/modules/hardware/intel-gpu.nix
new file mode 100644
index 00000000..fc6b6fa3
--- /dev/null
+++ b/system/modules/hardware/intel-gpu.nix
@@ -0,0 +1,15 @@
+{ config, pkgs, ... }:
+
+{ hardware.opengl.extraPackages = with pkgs; [
+    vaapiIntel
+    vaapiVdpau
+    libvdpau-va-gl
+  ];
+
+  services.xserver.videoDrivers = [ "intel" "modesetting" ];
+
+  boot.earlyVconsoleSetup = true;
+  boot.initrd.kernelModules = [
+    "i915"
+  ];
+}
diff --git a/system/modules/hardware/keyboardio-model01.nix b/system/modules/hardware/keyboardio-model01.nix
new file mode 100644
index 00000000..7a624f56
--- /dev/null
+++ b/system/modules/hardware/keyboardio-model01.nix
@@ -0,0 +1,13 @@
+{ config, pkgs, ... }:
+
+{ services.udev.extraRules = ''
+    SUBSYSTEMS=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="2300", SYMLINK+="model01", ENV{ID_MM_DEVICE_IGNORE}:="1", ENV{ID_MM_CANDIDATE}:="0"
+    SUBSYSTEMS=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="2301", SYMLINK+="model01", ENV{ID_MM_DEVICE_IGNORE}:="1", ENV{ID_MM_CANDIDATE}:="0"
+  '';
+
+  environment.systemPackages = with pkgs; [
+    arduino_core
+  ];
+
+  environment.variables.ARDUINO_PATH = "${pkgs.arduino_core}/share/arduino";
+}
diff --git a/system/modules/hardware/laptop.nix b/system/modules/hardware/laptop.nix
new file mode 100644
index 00000000..17e27b63
--- /dev/null
+++ b/system/modules/hardware/laptop.nix
@@ -0,0 +1,87 @@
+{ config, pkgs, lib, ... }:
+
+{ boot.kernelModules = [ "coretemp" ];
+  boot.extraModulePackages = with config.boot.kernelPackages; [
+    x86_energy_perf_policy
+  ];
+
+  hardware = {
+    bluetooth = {
+      enable = true;
+      powerOnBoot = false;
+      package = pkgs.bluezFull;
+    };
+    pulseaudio = {
+      extraModules = with pkgs; [
+        pulseaudio-modules-bt
+      ];
+    };
+  };
+  systemd.services.bluetooth.wantedBy = lib.mkForce [];
+  systemd.timers.bluetooth = {
+    description = "Delayed startup of Bluetooth";
+    wantedBy = [ "timers.target" ];
+    timerConfig = {
+      OnActiveSec = "1 min";
+    };
+  };
+
+  environment.systemPackages = with pkgs; [
+    blueman
+    bluez-tools
+
+    powerstat
+    powertop
+
+    arandr
+    autorandr
+    disper
+  ];
+
+  programs.light.enable = true;
+
+  services.autorandr.enable = true;
+
+  services.logind.extraConfig = ''
+    IdleAction=suspend
+    IdleActionSec=600
+  '';
+
+  services.acpid = {
+    enable = true;
+    lidEventCommands = ''
+      ${pkgs.autorandr}/bin/autorandr --batch --change
+    '';
+  };
+
+  services.tlp = {
+    enable = true;
+    extraConfig = ''
+      CPU_SCALING_GOVERNOR_ON_BAT=powersave
+      ENERGY_PERF_POLICY_ON_BAT="balance_power"
+
+      SOUND_POWER_SAVE_ON_AC=60
+      DEVICES_TO_DISABLE_ON_BAT_NOT_IN_USE="bluetooth wwan"
+    '';
+  };
+
+  services.xserver = {
+    libinput = {
+      enable = lib.mkDefault true;
+      naturalScrolling = true;
+      disableWhileTyping = true;
+    };
+    displayManager.sessionCommands = ''
+      ${pkgs.autorandr}/bin/autorandr --change --force
+      ${pkgs.blueman}/bin/blueman-applet &
+    '';
+  };
+
+  systemd.services.nixos-upgrade.unitConfig.ConditionACPower = true;
+  systemd.services.nix-gc.unitConfig.ConditionACPower = true;
+  systemd.services.docker-prune.unitConfig.ConditionACPower = true;
+
+  imports = [
+    ../user-interface.nix
+  ];
+}
diff --git a/system/modules/hardware/mouse.nix b/system/modules/hardware/mouse.nix
new file mode 100644
index 00000000..b30d4124
--- /dev/null
+++ b/system/modules/hardware/mouse.nix
@@ -0,0 +1,12 @@
+{ config, pkgs, ... }:
+
+{ services.xserver.config = ''
+    Section "InputClass"
+        Identifier "Mouse (No Acceleration)"
+        MatchIsPointer "yes"
+        MatchIsTouchpad "no"
+        Option "AccelerationProfile" "-1"
+        Option "AccelerationScheme" "none"
+    EndSection
+  '';
+}
diff --git a/system/modules/hardware/network-manager.nix b/system/modules/hardware/network-manager.nix
new file mode 100644
index 00000000..f28548a1
--- /dev/null
+++ b/system/modules/hardware/network-manager.nix
@@ -0,0 +1,12 @@
+{ config, pkgs, ... }:
+
+{ networking.networkmanager = {
+    enable = true;
+    dns = "unbound";
+  };
+
+  environment.systemPackages = with pkgs; [
+    networkmanagerapplet
+    networkmanager_dmenu
+  ];
+}
diff --git a/system/modules/hardware/nitrokey.nix b/system/modules/hardware/nitrokey.nix
new file mode 100644
index 00000000..a77ce00c
--- /dev/null
+++ b/system/modules/hardware/nitrokey.nix
@@ -0,0 +1,13 @@
+{ config, pkgs, lib, ... }:
+
+{
+  hardware.nitrokey = {
+    enable = true;
+  };
+
+  services.pcscd.enable = true;
+
+  environment.systemPackages = with pkgs; [
+    nitrokey-app
+  ];
+}
diff --git a/system/modules/hardware/nvidia-gpu.nix b/system/modules/hardware/nvidia-gpu.nix
new file mode 100644
index 00000000..9fc34169
--- /dev/null
+++ b/system/modules/hardware/nvidia-gpu.nix
@@ -0,0 +1,5 @@
+{ config, pkgs, ... }:
+
+{ services.xserver.videoDrivers = [ "nvidia" ];
+  nixpkgs.config.allowUnfree = true;
+}
diff --git a/system/modules/hardware/printing.nix b/system/modules/hardware/printing.nix
new file mode 100644
index 00000000..96d3a959
--- /dev/null
+++ b/system/modules/hardware/printing.nix
@@ -0,0 +1,19 @@
+{ config, pkgs, lib, ... }:
+
+{ services.printing.enable = true;
+  systemd.services.cups.wantedBy = lib.mkForce [];
+  systemd.sockets.cups.wantedBy = [ "sockets.target" ];
+  systemd.services.cups-browsed.wantedBy = lib.mkForce [];
+
+  systemd.timers.cups-browsed = {
+    description = "Delayed startup of CUPS Remote Printer Discovery";
+    wantedBy = [ "timers.target" ];
+    timerConfig = {
+      OnActiveSec = "2 min";
+    };
+  };
+
+  imports = [
+    ../services/zeroconf.nix
+  ];
+}
diff --git a/system/modules/hardware/qwerty.nix b/system/modules/hardware/qwerty.nix
new file mode 100644
index 00000000..d220cb27
--- /dev/null
+++ b/system/modules/hardware/qwerty.nix
@@ -0,0 +1,16 @@
+{ config, pkgs, ... }:
+
+{ services.kmscon = {
+    extraConfig = ''
+      xkb-model=pc104
+      xkb-layout=us
+      xkb-variant=intl-unicode
+      xkb-options="altwin:prtsc_rwin"
+    '';
+  };
+  services.xserver = {
+    layout = "us";
+    xkbVariant = "intl-unicode";
+    xkbOptions = "altwin:prtsc_rwin";
+  };
+}
diff --git a/system/modules/hardware/synaptics.nix b/system/modules/hardware/synaptics.nix
new file mode 100644
index 00000000..9f075cce
--- /dev/null
+++ b/system/modules/hardware/synaptics.nix
@@ -0,0 +1,27 @@
+{ config, pkgs, ... }:
+
+{ services.xserver = {
+    libinput.enable = false;
+    synaptics = {
+      enable = true;
+
+      accelFactor = "0.04";
+
+      minSpeed = "0.3";
+      maxSpeed = "0.6";
+
+      palmDetect = true;
+      palmMinWidth = 5;
+      palmMinZ = 20;
+
+      twoFingerScroll = true;
+      vertTwoFingerScroll = true;
+      horizTwoFingerScroll = true;
+      additionalOptions = ''
+        Option "RBCornerButton" "3"
+        Option "VertScrollDelta" "-111"
+        Option "HorizScrollDelta" "-111"
+      '';
+    };
+  };
+}
diff --git a/system/modules/hardware/systemd-boot.nix b/system/modules/hardware/systemd-boot.nix
new file mode 100644
index 00000000..80e79fdc
--- /dev/null
+++ b/system/modules/hardware/systemd-boot.nix
@@ -0,0 +1,10 @@
+{ config, pkgs, ... }:
+
+{ boot.loader.systemd-boot = {
+    enable = true;
+    editor = false; # Don't allow modification
+  };
+  boot.loader.efi.canTouchEfiVariables = true;
+  boot.vesa = true;
+  boot.earlyVconsoleSetup = true;
+}
diff --git a/system/modules/hardware/thinkpad.nix b/system/modules/hardware/thinkpad.nix
new file mode 100644
index 00000000..903e819b
--- /dev/null
+++ b/system/modules/hardware/thinkpad.nix
@@ -0,0 +1,26 @@
+{ config, pkgs, ... }:
+
+{ boot.kernelModules = [ ];
+  boot.blacklistedKernelModules = [ "thinkpad_ec" ];
+  boot.extraModulePackages = with config.boot.kernelPackages; [
+    acpi_call
+  ];
+
+  hardware.trackpoint = {
+    enable = true;
+    emulateWheel = true;
+  };
+
+  services.thinkfan = {
+    enable = true;
+  };
+
+  services.tlp = {
+    enable = true;
+  };
+
+  imports = [
+    ./bare-metal.nix
+    ./laptop.nix
+  ];
+}
diff --git a/system/modules/hardware/trackball.nix b/system/modules/hardware/trackball.nix
new file mode 100644
index 00000000..9aa5abc0
--- /dev/null
+++ b/system/modules/hardware/trackball.nix
@@ -0,0 +1,13 @@
+{ config, pkgs, ... }:
+
+{ services.xserver.config = ''
+    Section "InputClass"
+        Identifier "Trackball (No Acceleration)"
+        MatchIsPointer "yes"
+        MatchIsTouchpad "no"
+        MatchProduct "Trackball"
+        Option "AccelerationProfile" "-1"
+        Option "AccelerationScheme" "none"
+    EndSection
+  '';
+}
diff --git a/system/modules/hardware/trezor.nix b/system/modules/hardware/trezor.nix
new file mode 100644
index 00000000..33cc6f25
--- /dev/null
+++ b/system/modules/hardware/trezor.nix
@@ -0,0 +1,13 @@
+{ config, lib, pkgs, ... }:
+
+{ services.trezord.enable = true;
+  environment.systemPackages = with pkgs; [
+    gnupg
+    pinentry
+    (python3.withPackages(ps: with ps; [ trezor_agent wheel ]))
+  ];
+  programs.gnupg.agent = {
+    enable = lib.mkForce false;
+    enableSSHSupport = lib.mkForce false;
+  };
+}
diff --git a/system/modules/machines/t470s.nix b/system/modules/machines/t470s.nix
new file mode 100644
index 00000000..4ee6c2ac
--- /dev/null
+++ b/system/modules/machines/t470s.nix
@@ -0,0 +1,59 @@
+{ config, pkgs, ... }:
+
+{ hardware.usbWwan.enable = false; # unused
+  systemd.services.ModemManager.enable = false;
+
+  hardware.firmware = with pkgs; [
+    firmwareLinuxNonfree
+  ];
+
+  services.thinkfan.sensors = ''
+    hwmon /sys/devices/platform/coretemp.0/hwmon/hwmon0/temp3_input
+    hwmon /sys/devices/platform/coretemp.0/hwmon/hwmon0/temp1_input
+    hwmon /sys/devices/platform/coretemp.0/hwmon/hwmon0/temp2_input
+  '';
+  services.thinkfan.levels = ''
+    (0,     0,      48)
+    (1,     45,     52)
+    (2,     50,     57)
+    (3,     55,     63)
+    (6,     60,     65)
+    (7,     60,     85)
+    (127,   80,     32767)
+  '';
+
+  boot.kernelParams = [
+    "i915.enable_guc=2"
+    "i915.enable_psr=1"
+  ];
+
+  boot.postBootCommands = ''
+    echo bfq > /sys/block/nvme0n1/queue/scheduler
+  '';
+
+  hardware.pulseaudio.extraConfig = ''
+    load-module module-alsa-sink device=hw:0,7
+  '';
+
+  services.tlp.extraConfig = ''
+    DISK_DEVICES="nvme0n1"
+    DISK_IOSCHED="keep"
+  '';
+
+  services.xserver = {
+    dpi = 109;
+    monitorSection = ''
+      DisplaySize 310 176
+    '';
+  };
+
+  environment.systemPackages = with pkgs; [
+    nvme-cli
+  ];
+
+  imports = [
+    ../hardware/intel-gpu.nix
+    ../hardware/hidpi.nix
+    ../hardware/thinkpad.nix
+  ];
+}
diff --git a/system/modules/machines/x250.nix b/system/modules/machines/x250.nix
new file mode 100644
index 00000000..0e8ac04e
--- /dev/null
+++ b/system/modules/machines/x250.nix
@@ -0,0 +1,33 @@
+{ config, pkgs, ... }:
+
+{ boot.extraModulePackages = with config.boot.kernelPackages; [
+    acpi_call
+  ];
+
+  hardware.firmware = with pkgs; [
+    firmwareLinuxNonfree
+  ];
+
+  services.tlp.extraConfig = ''
+    # Newer Thinkpads have a battery firmware
+    # it conflicts with TLP if stop thresholds are set
+    START_CHARGE_THRESH_BAT0=70
+    # STOP_CHARGE_THRESH_BAT0=80
+    START_CHARGE_THRESH_BAT1=70
+    # STOP_CHARGE_THRESH_BAT1=80
+
+    DISK_APM_LEVEL_ON_AC="254 254"
+    DISK_APM_LEVEL_ON_BAT="128 128"
+
+    # One or both of these lines stops disk corruption
+    # when re-attaching to AC whilst on.
+    SATA_LINKPWR_ON_BAT=medium_power
+    SATA_LINKPWR_BLACKLIST="host1"
+  '';
+
+  imports = [
+    ../hardware/synaptics.nix
+    ../hardware/intel-gpu.nix
+    ../hardware/thinkpad.nix
+  ];
+}
diff --git a/system/modules/programs/accounting.nix b/system/modules/programs/accounting.nix
new file mode 100644
index 00000000..e98c3e7e
--- /dev/null
+++ b/system/modules/programs/accounting.nix
@@ -0,0 +1,10 @@
+{ config, pkgs, ... }:
+
+{ environment.systemPackages = with pkgs; [
+    ledger
+    bean-add
+    beancount
+    fava
+    reckon
+  ];
+}
diff --git a/system/modules/programs/barrier.nix b/system/modules/programs/barrier.nix
new file mode 100644
index 00000000..9a73620d
--- /dev/null
+++ b/system/modules/programs/barrier.nix
@@ -0,0 +1,7 @@
+{ config, pkgs, ... }: {
+  environment.systemPackages = with pkgs; [
+    barrier
+  ];
+
+  networking.firewall.allowedTCPPorts = [ 24800 ];
+}
diff --git a/system/modules/programs/dotfiles.nix b/system/modules/programs/dotfiles.nix
new file mode 100644
index 00000000..2e3f44aa
--- /dev/null
+++ b/system/modules/programs/dotfiles.nix
@@ -0,0 +1,14 @@
+{ config, pkgs, ... }:
+
+{ environment.systemPackages = with pkgs; [
+    stow
+    fzf
+    ghq
+  ] ++ (
+    if stdenv.isDarwin
+    then [
+    ] else [
+      git
+    ]
+  );
+}
diff --git a/system/modules/programs/gnome.nix b/system/modules/programs/gnome.nix
new file mode 100644
index 00000000..bd785453
--- /dev/null
+++ b/system/modules/programs/gnome.nix
@@ -0,0 +1,24 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+{ services = {
+    gnome3 = {
+      gnome-documents.enable = false;
+      gnome-user-share.enable = false;
+      gnome-online-accounts.enable = false;
+      seahorse.enable = false;
+      tracker.enable = false;
+    };
+    telepathy.enable = false;
+
+    xserver = {
+      desktopManager.gnome3 = {
+        enable = true;
+        extraGSettingsOverrides = ''
+          [org.gnome.desktop.input-sources]
+          sources=[('xkb','${config.services.xserver.layout + (optionalString (config.services.xserver.xkbVariant != "") ("+" + config.services.xserver.xkbVariant))}')]
+        '';
+      };
+    };
+  };
+}
diff --git a/system/modules/programs/gnupg.nix b/system/modules/programs/gnupg.nix
new file mode 100644
index 00000000..663bcb3c
--- /dev/null
+++ b/system/modules/programs/gnupg.nix
@@ -0,0 +1,10 @@
+{ config, pkgs, lib, ... }:
+
+{
+  environment.systemPackages = with pkgs; [
+    gnupg
+    pinentry
+    (python3.withPackages(ps: with ps; [ trezor_agent wheel ]))
+  ];
+  environment.variables.GNUPGHOME = "$HOME/.gnupg/trezor/";
+}
diff --git a/system/modules/programs/i3.nix b/system/modules/programs/i3.nix
new file mode 100644
index 00000000..017f393e
--- /dev/null
+++ b/system/modules/programs/i3.nix
@@ -0,0 +1,21 @@
+{ config, pkgs, ... }:
+
+{ services.xserver.windowManager = {
+    default = "i3";
+    i3 = {
+      enable = true;
+      extraSessionCommands = ''
+        ${pkgs.dunst}/bin/dunst &
+        ${pkgs.sxhkd}/bin/sxhkd &
+      '';
+    };
+  };
+
+  environment.systemPackages = with pkgs; [
+    i3status
+  ];
+
+  imports = [
+    ./window-manager.nix
+  ];
+}
diff --git a/system/modules/programs/infrastructure.nix b/system/modules/programs/infrastructure.nix
new file mode 100644
index 00000000..5e68bc8a
--- /dev/null
+++ b/system/modules/programs/infrastructure.nix
@@ -0,0 +1,8 @@
+{ config, pkgs, ... }:
+
+{ environment.systemPackages = with pkgs; [
+    hugo
+
+    nixops
+  ];
+}
diff --git a/system/modules/programs/kde.nix b/system/modules/programs/kde.nix
new file mode 100644
index 00000000..652eb2a6
--- /dev/null
+++ b/system/modules/programs/kde.nix
@@ -0,0 +1,20 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+{ services = {
+    xserver = {
+      desktopManager = {
+        plasma5.enable = true;
+      };
+      displayManager = {
+        sddm.enable = true;
+      };
+    };
+
+    physlock.enable = lib.mkForce false;
+  };
+
+  environment.systemPackages = with pkgs; [
+    kde-gtk-config
+  ];
+}
diff --git a/system/modules/programs/keybase.nix b/system/modules/programs/keybase.nix
new file mode 100644
index 00000000..39a16b27
--- /dev/null
+++ b/system/modules/programs/keybase.nix
@@ -0,0 +1,11 @@
+{ config, pkgs, lib, ... }:
+
+{
+  services.keybase.enable = true;
+  services.kbfs.enable = true;
+  environment.variables.NIX_SKIP_KEYBASE_CHECKS = "1";
+
+  environment.systemPackages = with pkgs; [
+    keybase-gui
+  ];
+}
diff --git a/system/modules/programs/passwords.nix b/system/modules/programs/passwords.nix
new file mode 100644
index 00000000..094ad29e
--- /dev/null
+++ b/system/modules/programs/passwords.nix
@@ -0,0 +1,9 @@
+{ config, pkgs, ... }:
+
+{ environment.systemPackages = with pkgs.unstable; [
+    keepassx-community
+    rofi-pass
+    pass
+    pwgen
+  ];
+}
diff --git a/system/modules/programs/shell.nix b/system/modules/programs/shell.nix
new file mode 100644
index 00000000..cf36a115
--- /dev/null
+++ b/system/modules/programs/shell.nix
@@ -0,0 +1,34 @@
+{ config, pkgs, ... }:
+
+{ programs.zsh = {
+    enable = true;
+    promptInit = "";
+  };
+  programs.bash.enableCompletion = true;
+  programs.vim = pkgs.lib.attrsets.optionalAttrs pkgs.stdenv.isDarwin {
+    enable = true;
+  };
+
+  environment.systemPackages = with pkgs; [
+    pv
+    fd
+    unstable.sd
+    entr
+    file
+    htop
+    lsof
+    iftop
+    nmap
+    moreutils
+    mtr
+    tree
+    zip
+    telnet
+  ] ++ (
+  if !stdenv.isDarwin
+  then [
+    vim
+    unar
+  ] else [
+  ]);
+}
diff --git a/system/modules/programs/tor.nix b/system/modules/programs/tor.nix
new file mode 100644
index 00000000..5524aede
--- /dev/null
+++ b/system/modules/programs/tor.nix
@@ -0,0 +1,22 @@
+{ config, pkgs, lib, ... }:
+
+{
+  services.tor = {
+    enable = true;
+    client = {
+      enable = true;
+      socksListenAddress = "9050 IPv6Traffic";
+    };
+    torsocks = {
+      enable = true;
+    };
+  };
+  systemd.services.tor.wantedBy = lib.mkForce [];
+  systemd.timers.tor = {
+    description = "Delayed startup of Tor";
+    wantedBy = [ "timers.target" ];
+    timerConfig = {
+      OnActiveSec = "1 min";
+    };
+  };
+}
diff --git a/system/modules/programs/window-manager.nix b/system/modules/programs/window-manager.nix
new file mode 100644
index 00000000..ee6877d8
--- /dev/null
+++ b/system/modules/programs/window-manager.nix
@@ -0,0 +1,47 @@
+{ config, pkgs, ... }:
+
+{ services.xserver = {
+   desktopManager.xterm.enable = false;
+
+   displayManager = {
+     lightdm = {
+       enable = true;
+       greeters.mini = {
+         enable = true;
+         user = "alan";
+       };
+     };
+     sessionCommands = ''
+       ${pkgs.xorg.xrdb}/bin/xrdb -merge $HOME/.xresources/main
+       ${pkgs.xorg.xsetroot}/bin/xsetroot -cursor_name left_ptr -solid '#4d4d4c'
+     '';
+   };
+    xautolock = {
+      enable = true;
+      locker = "${pkgs.i3lock}/bin/i3lock -n";
+      enableNotifier = true;
+      notifier = "${pkgs.libnotify}/bin/notify-send \"Locking in 10 seconds\"";
+      time = 5;
+    };
+  };
+
+  services.xserver.displayManager.setupCommands = ''
+    ${pkgs.redshift}/bin/redshift \
+      -l ${toString config.services.redshift.latitude}:${toString config.services.redshift.longitude} \
+      -t ${toString config.services.redshift.temperature.day}:${toString config.services.redshift.temperature.night} \
+      -b 1:1 \
+      -o \
+      -r \
+  '';
+
+  environment.systemPackages = with pkgs; [
+    dmenu
+    dunst
+    libnotify # for notify-send
+    rofi
+    sxhkd
+    maim
+
+    perlPackages.FileMimeInfo # xdg-utils uses this when no DE
+  ];
+}
diff --git a/system/modules/programs/wine.nix b/system/modules/programs/wine.nix
new file mode 100644
index 00000000..98dd60a2
--- /dev/null
+++ b/system/modules/programs/wine.nix
@@ -0,0 +1,6 @@
+{ config, pkgs, ... }:
+
+{ environment.systemPackages = with pkgs; [
+    wineStable
+  ];
+}
diff --git a/system/modules/satoshipay.nix b/system/modules/satoshipay.nix
new file mode 100644
index 00000000..4b648740
--- /dev/null
+++ b/system/modules/satoshipay.nix
@@ -0,0 +1,96 @@
+{ config, pkgs, fetchurl, lib, ... }:
+
+{ virtualisation = {
+  docker = let
+    daemonConfig = {
+      ipv6 = true;
+      fixed-cidr-v6 = "fd69:2074:9fcd:b0fd::/64";
+      features = {
+        buildkit = true;
+      };
+    };
+    in {
+      enable = true;
+      enableOnBoot = false;
+      liveRestore = false;
+
+      extraOptions = "--config-file=${pkgs.writeText "daemon.json" (builtins.toJSON daemonConfig)}";
+
+      autoPrune = {
+        enable = true;
+      };
+    };
+  };
+
+  nixpkgs.config.allowUnfree = true;
+
+  environment.variables = {
+    KUBECTX_IGNORE_FZF = "1";
+  };
+  environment.systemPackages = with pkgs; [
+    caddy
+    openssl
+    mongodb-tools
+    pgadmin
+    pgcli
+    s3cmd
+    sops
+    unstable.mkcert
+    unstable.google-cloud-sdk
+    unstable.docker_compose
+    unstable.kubernetes
+    unstable.kubectx
+    unstable.redis-desktop-manager
+    unstable.kubernetes-helm
+    unstable.helmfile
+    unstable.robo3t
+    unstable.slack
+  ];
+
+  services.mongodb = {
+    enable = true;
+    replSetName = "rs0";
+    dbpath = "/tmp/mongodb";
+  };
+  systemd.services.mongodb.wantedBy = lib.mkForce  [];
+  systemd.timers.mongodb = {
+    description = "Delayed startup of MongoDB";
+    wantedBy = [ "timers.target" ];
+    timerConfig = {
+      OnActiveSec = "1 min";
+    };
+  };
+  systemd.services.mongodb-init = {
+    description = "Init mongodb replicaset";
+    requires = [ "mongodb.service" ];
+    script = "${pkgs.mongodb}/bin/mongo --eval 'rs.initiate()'";
+  };
+  systemd.timers.mongodb-init = {
+    wantedBy = [ "timers.target" ];
+    timerConfig = {
+      OnActiveSec = "2 min";
+    };
+  };
+
+  services.redis = {
+    enable = true;
+  };
+  systemd.services.redis.wantedBy = lib.mkForce [];
+  systemd.timers.redis = {
+    description = "Delayed startup of Redis";
+    wantedBy = [ "timers.target" ];
+    timerConfig = {
+      OnActiveSec = "1 min";
+    };
+  };
+
+
+  services.printing.drivers = with pkgs; [
+  ];
+
+  networking.domain = "satoshipay.io";
+
+  networking.extraHosts = ''
+    127.0.0.1 blogger.local wallet.satoshipay.local api.satoshipay.local ws.satoshipay.local
+  '';
+}
diff --git a/system/modules/services/syncthing.nix b/system/modules/services/syncthing.nix
new file mode 100644
index 00000000..b6a12861
--- /dev/null
+++ b/system/modules/services/syncthing.nix
@@ -0,0 +1,11 @@
+{ config, pkgs, ... }:
+
+{ services.syncthing = {
+    enable = true;
+    user = "alan";
+    group = "users";
+    openDefaultPorts = true;
+    systemService = true;
+    dataDir = "/home/alan/.config/syncthing";
+  };
+}
diff --git a/system/modules/services/xserver.nix b/system/modules/services/xserver.nix
new file mode 100644
index 00000000..80ed6a3a
--- /dev/null
+++ b/system/modules/services/xserver.nix
@@ -0,0 +1,78 @@
+{ config, pkgs, ... }:
+
+{ services.xserver = {
+    enable = true;
+    enableCtrlAltBackspace = true;
+    exportConfiguration = true;
+  };
+
+  environment.systemPackages = with pkgs; [
+    xorg.xmodmap
+    xorg.xinit
+    xorg.xev
+    xorg.xdpyinfo
+    xclip
+    xfontsel
+
+    vanilla-dmz
+    capitaine-cursors
+    bibata-cursors
+
+    arc-theme
+    hicolor_icon_theme
+    paper-gtk-theme
+    paper-icon-theme
+
+    arc-icon-theme
+    tango-icon-theme
+
+    gtk-engine-murrine
+    gtk_engines
+  ];
+
+  fonts = {
+    enableFontDir = true;
+    enableDefaultFonts = false;
+    fontconfig = {
+      defaultFonts = {
+        monospace = [ "Liberation Mono" ];
+        sansSerif = [ "Liberation Sans" ];
+        serif = [ "Liberation Serif" ];
+      };
+      penultimate = {
+        enable = true;
+      };
+      ultimate = {
+        enable = false;
+        preset = "osx";
+      };
+    };
+    fonts = with pkgs; [
+      dina-font
+      envypn-font
+      profont
+      proggyfonts
+      terminus_font
+
+      fantasque-sans-mono
+      emacs-all-the-icons-fonts
+      fira
+      fira-code
+      fira-mono
+      go-font
+      font-awesome_5
+      ibm-plex
+      liberation_ttf
+      mononoki
+      roboto
+      roboto-mono
+      roboto-slab
+      source-code-pro
+      source-sans-pro
+      source-serif-pro
+      xorg.fontmiscmisc
+      xorg.fontcursormisc
+      xorg.fontbhlucidatypewriter100dpi
+    ];
+  };
+}
diff --git a/system/modules/services/zeroconf.nix b/system/modules/services/zeroconf.nix
new file mode 100644
index 00000000..0b428c54
--- /dev/null
+++ b/system/modules/services/zeroconf.nix
@@ -0,0 +1,16 @@
+{ config, pkgs, lib, ... }:
+
+{ services.avahi = {
+    enable = true;
+    nssmdns = true;
+    ipv6 = true;
+  };
+  systemd.services.avahi-daemon.wantedBy = lib.mkForce [];
+  systemd.timers.avahi-daemon = {
+    description = "Delayed startup of Avahi";
+    wantedBy = [ "timers.target" ];
+    timerConfig = {
+      OnActiveSec = "1 min";
+    };
+  };
+}
diff --git a/system/modules/user-interface.nix b/system/modules/user-interface.nix
new file mode 100644
index 00000000..7c98d291
--- /dev/null
+++ b/system/modules/user-interface.nix
@@ -0,0 +1,144 @@
+{ config, pkgs, lib, makeDesktopItem, ... }:
+
+let
+  emacsPackage = import ../packages/emacs.nix {
+    pkgs = pkgs.unstable;
+    emacs = pkgs.unstable.emacs.override {
+      withGTK3 = false;
+    };
+  };
+  editorScript = pkgs.writeScriptBin "edit" ''
+    #!${pkgs.runtimeShell}
+    if [ -z "$1" ]; then
+      exec ${emacsPackage}/bin/emacsclient --create-frame --alternate-editor ${emacsPackage}/bin/emacs
+    else
+      exec ${emacsPackage}/bin/emacsclient --alternate-editor ${emacsPackage}/bin/emacs "$@"
+    fi
+  '';
+  desktopApplicationFile = makeDesktopItem {
+    name = "emacsclient.desktop";
+    destination = "/share/applications/emacsclient.desktop";
+    text = ''
+      [Desktop Entry]
+      Name=Emacsclient
+      GenericName=Text Editor
+      Comment=Edit text
+      MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
+      Exec=${editorScript}/bin/edit %F
+      Icon=emacs
+      Type=Application
+      Terminal=false
+      Categories=Development;TextEditor;
+      StartupWMClass=Emacs
+      Keywords=Text;Editor;
+    '';
+  };
+in
+{ documentation.info.enable = true;
+  nixpkgs.config.firefox.enableOfficialBranding = true;
+
+  nixpkgs.config.packageOverrides = pkgs: {
+    myEmacs = emacsPackage;
+  };
+  environment.systemPackages = with pkgs; [
+    aria2
+    firefox
+    pcmanfm
+
+    epdfview
+    geeqie
+
+    cmus
+
+    fish # for emacs-fish-completion
+    myEmacs
+    editorScript
+
+    unstable.xst # st, but with support for XResources
+
+    lxappearance
+    lxrandr
+    lxtask
+
+    python3Packages.keyring
+    isync
+    msmtp
+    html2text
+
+    weechat
+
+    unstable.pass-otp
+
+    mpv
+
+    mosh
+    aspell
+    aspellDicts.en
+
+    cifs-utils
+    hexchat
+    signal-desktop
+    wire-desktop
+
+    trash-cli
+  ] ++ (if !stdenv.isDarwin
+  then [
+    unstable.mu
+  ]
+  else []);
+
+  nixpkgs.config.allowUnfree = true;
+
+  services.compton = {
+    enable = true;
+  };
+
+  services.devmon.enable = true;
+
+  environment.sessionVariables.TERMINAL = "st";
+
+  systemd.user.services.trash-clean = {
+    path = with pkgs; [ trash-cli ];
+    description = "Remove old files from FreeDesktop.org trash";
+
+    serviceConfig = {
+      Type = "oneshot";
+    };
+    script = "trash-empty 30";
+  };
+  systemd.user.timers.trash-clean = {
+    wantedBy = [ "default.target" ];
+    timerConfig = {
+      OnCalendar = "weekly";
+      Persistent = true;
+    };
+  };
+
+  environment.variables = {
+    # This is required so that GTK applications launched from Emacs
+    # get properly themed:
+    GTK_DATA_PREFIX = "${config.system.path}";
+    EDITOR = lib.mkOverride 900 "${editorScript}/bin/edit";
+  };
+
+  services.redshift = {
+    enable = true;
+    temperature = {
+      day = 6500;
+      night = 3600;
+    };
+  };
+
+  programs.ssh.startAgent = true;
+
+  programs.dconf.enable = true;
+  services.gnome3 = {
+    gnome-keyring.enable = true;
+    seahorse.enable = true;
+    at-spi2-core.enable = true;
+  };
+
+  imports = [
+    ./services/xserver.nix
+  ];
+}
diff --git a/system/packages/emacs.nix b/system/packages/emacs.nix
new file mode 100644
index 00000000..e0df78b0
--- /dev/null
+++ b/system/packages/emacs.nix
@@ -0,0 +1,127 @@
+{
+  pkgs ? import <nixpkgs-unstable> {},
+  emacs ? pkgs.emacs
+}:
+
+let
+  myEmacs = pkgs.lib.overrideDerivation (emacs) (attrs: {
+    postInstall = attrs.postInstall + ''
+      rm $out/share/applications/emacs.desktop
+    '';
+  });
+  stdenv = pkgs.stdenv;
+
+  emacsWithPackages = (pkgs.emacsPackagesNgGen myEmacs).emacsWithPackages;
+in
+  emacsWithPackages (epkgs: (with epkgs; [
+    ace-link
+    all-the-icons
+    almost-mono-themes
+    add-node-modules-path
+    ag
+    all-the-icons
+    amx
+    auto-async-byte-compile
+    avy
+    basic-theme
+    bash-completion
+    caddyfile-mode
+    company
+    company-web
+    company-tabnine
+    counsel
+    counsel-projectile
+    crux
+    docker-compose-mode
+    dockerfile-mode
+    editorconfig
+    eink-theme
+    eldoc-box
+    emmet-mode
+    esh-autosuggest
+    esh-buf-stack
+    esh-help
+    eshell-fringe-status
+    eshell-toggle
+    eshell-up
+    evil
+    evil-collection
+    evil-commentary
+    evil-magit
+    evil-mu4e
+    evil-org
+    evil-quickscope
+    evil-space
+    evil-surround
+    eyebrowse
+    feature-mode
+    fish-completion
+    flycheck
+    flymake-diagnostic-at-point
+    general
+    git-gutter-fringe
+    git-messenger
+    git-timemachine
+    gitattributes-mode
+    gitconfig-mode
+    gitignore-mode
+    gitlab-ci-mode
+    gitlab-ci-mode-flycheck
+    goto-chg
+    haskell-mode
+    helpful
+    ivy-hydra
+    jinja2-mode
+    js2-mode
+    json-mode
+    k8s-mode
+    # kubernetes
+    # kubernetes-evil
+    ledger-mode
+    lsp-mode
+    lsp-ui
+    lsp-haskell
+    lsp-treemacs
+    magit
+    markdown-mode
+    minions
+    monotropic-theme
+    moody
+    nginx-mode
+    nix-mode
+    nix-update
+    org-journal
+    paredit
+    php-mode
+    posframe
+    prettier-js
+    projectile
+    projectile-ripgrep
+    quickrun
+    relative-buffers
+    restclient
+    ripgrep
+    rjsx-mode
+    scss-mode
+    spacemacs-theme
+    swiper
+    toml-mode
+    typescript-mode
+    undo-tree
+    use-package
+    web-mode
+    wgrep-ag
+    ws-butler
+    which-key
+    yaml-mode
+  ] ++ (if stdenv.isDarwin then [
+    exec-path-from-shell
+    ns-auto-titlebar
+  ] else [
+  ])) ++ (with epkgs.elpaPackages; [
+    rainbow-mode
+  ]) ++ (if !stdenv.isDarwin then [
+    # From main packages set
+
+    pkgs.unstable.mu
+  ] else []))
diff --git a/system/packages/node2nix/Makefile b/system/packages/node2nix/Makefile
new file mode 100644
index 00000000..d829eeb5
--- /dev/null
+++ b/system/packages/node2nix/Makefile
@@ -0,0 +1,5 @@
+default.nix: node-packages.json
+	node2nix --nodejs-10 --input $<
+
+install: default.nix
+	nix-env -f default.nix -i
diff --git a/system/packages/node2nix/default.nix b/system/packages/node2nix/default.nix
new file mode 100644
index 00000000..fa0a1482
--- /dev/null
+++ b/system/packages/node2nix/default.nix
@@ -0,0 +1,17 @@
+# This file has been generated by node2nix 1.7.0. Do not edit!
+
+{pkgs ? import <nixpkgs> {
+    inherit system;
+  }, system ? builtins.currentSystem, nodejs ? pkgs."nodejs-10_x"}:
+
+let
+  nodeEnv = import ./node-env.nix {
+    inherit (pkgs) stdenv python2 utillinux runCommand writeTextFile;
+    inherit nodejs;
+    libtool = if pkgs.stdenv.isDarwin then pkgs.darwin.cctools else null;
+  };
+in
+import ./node-packages.nix {
+  inherit (pkgs) fetchurl fetchgit;
+  inherit nodeEnv;
+}
\ No newline at end of file
diff --git a/system/packages/node2nix/node-env.nix b/system/packages/node2nix/node-env.nix
new file mode 100644
index 00000000..670556bf
--- /dev/null
+++ b/system/packages/node2nix/node-env.nix
@@ -0,0 +1,540 @@
+# This file originates from node2nix
+
+{stdenv, nodejs, python2, utillinux, libtool, runCommand, writeTextFile}:
+
+let
+  python = if nodejs ? python then nodejs.python else python2;
+
+  # Create a tar wrapper that filters all the 'Ignoring unknown extended header keyword' noise
+  tarWrapper = runCommand "tarWrapper" {} ''
+    mkdir -p $out/bin
+
+    cat > $out/bin/tar <<EOF
+    #! ${stdenv.shell} -e
+    $(type -p tar) "\$@" --warning=no-unknown-keyword --delay-directory-restore
+    EOF
+
+    chmod +x $out/bin/tar
+  '';
+
+  # Function that generates a TGZ file from a NPM project
+  buildNodeSourceDist =
+    { name, version, src, ... }:
+
+    stdenv.mkDerivation {
+      name = "node-tarball-${name}-${version}";
+      inherit src;
+      buildInputs = [ nodejs ];
+      buildPhase = ''
+        export HOME=$TMPDIR
+        tgzFile=$(npm pack | tail -n 1) # Hooks to the pack command will add output (https://docs.npmjs.com/misc/scripts)
+      '';
+      installPhase = ''
+        mkdir -p $out/tarballs
+        mv $tgzFile $out/tarballs
+        mkdir -p $out/nix-support
+        echo "file source-dist $out/tarballs/$tgzFile" >> $out/nix-support/hydra-build-products
+      '';
+    };
+
+  includeDependencies = {dependencies}:
+    stdenv.lib.optionalString (dependencies != [])
+      (stdenv.lib.concatMapStrings (dependency:
+        ''
+          # Bundle the dependencies of the package
+          mkdir -p node_modules
+          cd node_modules
+
+          # Only include dependencies if they don't exist. They may also be bundled in the package.
+          if [ ! -e "${dependency.name}" ]
+          then
+              ${composePackage dependency}
+          fi
+
+          cd ..
+        ''
+      ) dependencies);
+
+  # Recursively composes the dependencies of a package
+  composePackage = { name, packageName, src, dependencies ? [], ... }@args:
+    ''
+      DIR=$(pwd)
+      cd $TMPDIR
+
+      unpackFile ${src}
+
+      # Make the base dir in which the target dependency resides first
+      mkdir -p "$(dirname "$DIR/${packageName}")"
+
+      if [ -f "${src}" ]
+      then
+          # Figure out what directory has been unpacked
+          packageDir="$(find . -maxdepth 1 -type d | tail -1)"
+
+          # Restore write permissions to make building work
+          find "$packageDir" -type d -exec chmod u+x {} \;
+          chmod -R u+w "$packageDir"
+
+          # Move the extracted tarball into the output folder
+          mv "$packageDir" "$DIR/${packageName}"
+      elif [ -d "${src}" ]
+      then
+          # Get a stripped name (without hash) of the source directory.
+          # On old nixpkgs it's already set internally.
+          if [ -z "$strippedName" ]
+          then
+              strippedName="$(stripHash ${src})"
+          fi
+
+          # Restore write permissions to make building work
+          chmod -R u+w "$strippedName"
+
+          # Move the extracted directory into the output folder
+          mv "$strippedName" "$DIR/${packageName}"
+      fi
+
+      # Unset the stripped name to not confuse the next unpack step
+      unset strippedName
+
+      # Include the dependencies of the package
+      cd "$DIR/${packageName}"
+      ${includeDependencies { inherit dependencies; }}
+      cd ..
+      ${stdenv.lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
+    '';
+
+  pinpointDependencies = {dependencies, production}:
+    let
+      pinpointDependenciesFromPackageJSON = writeTextFile {
+        name = "pinpointDependencies.js";
+        text = ''
+          var fs = require('fs');
+          var path = require('path');
+
+          function resolveDependencyVersion(location, name) {
+              if(location == process.env['NIX_STORE']) {
+                  return null;
+              } else {
+                  var dependencyPackageJSON = path.join(location, "node_modules", name, "package.json");
+
+                  if(fs.existsSync(dependencyPackageJSON)) {
+                      var dependencyPackageObj = JSON.parse(fs.readFileSync(dependencyPackageJSON));
+
+                      if(dependencyPackageObj.name == name) {
+                          return dependencyPackageObj.version;
+                      }
+                  } else {
+                      return resolveDependencyVersion(path.resolve(location, ".."), name);
+                  }
+              }
+          }
+
+          function replaceDependencies(dependencies) {
+              if(typeof dependencies == "object" && dependencies !== null) {
+                  for(var dependency in dependencies) {
+                      var resolvedVersion = resolveDependencyVersion(process.cwd(), dependency);
+
+                      if(resolvedVersion === null) {
+                          process.stderr.write("WARNING: cannot pinpoint dependency: "+dependency+", context: "+process.cwd()+"\n");
+                      } else {
+                          dependencies[dependency] = resolvedVersion;
+                      }
+                  }
+              }
+          }
+
+          /* Read the package.json configuration */
+          var packageObj = JSON.parse(fs.readFileSync('./package.json'));
+
+          /* Pinpoint all dependencies */
+          replaceDependencies(packageObj.dependencies);
+          if(process.argv[2] == "development") {
+              replaceDependencies(packageObj.devDependencies);
+          }
+          replaceDependencies(packageObj.optionalDependencies);
+
+          /* Write the fixed package.json file */
+          fs.writeFileSync("package.json", JSON.stringify(packageObj, null, 2));
+        '';
+      };
+    in
+    ''
+      node ${pinpointDependenciesFromPackageJSON} ${if production then "production" else "development"}
+
+      ${stdenv.lib.optionalString (dependencies != [])
+        ''
+          if [ -d node_modules ]
+          then
+              cd node_modules
+              ${stdenv.lib.concatMapStrings (dependency: pinpointDependenciesOfPackage dependency) dependencies}
+              cd ..
+          fi
+        ''}
+    '';
+
+  # Recursively traverses all dependencies of a package and pinpoints all
+  # dependencies in the package.json file to the versions that are actually
+  # being used.
+
+  pinpointDependenciesOfPackage = { packageName, dependencies ? [], production ? true, ... }@args:
+    ''
+      if [ -d "${packageName}" ]
+      then
+          cd "${packageName}"
+          ${pinpointDependencies { inherit dependencies production; }}
+          cd ..
+          ${stdenv.lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
+      fi
+    '';
+
+  # Extract the Node.js source code which is used to compile packages with
+  # native bindings
+  nodeSources = runCommand "node-sources" {} ''
+    tar --no-same-owner --no-same-permissions -xf ${nodejs.src}
+    mv node-* $out
+  '';
+
+  # Script that adds _integrity fields to all package.json files to prevent NPM from consulting the cache (that is empty)
+  addIntegrityFieldsScript = writeTextFile {
+    name = "addintegrityfields.js";
+    text = ''
+      var fs = require('fs');
+      var path = require('path');
+
+      function augmentDependencies(baseDir, dependencies) {
+          for(var dependencyName in dependencies) {
+              var dependency = dependencies[dependencyName];
+
+              // Open package.json and augment metadata fields
+              var packageJSONDir = path.join(baseDir, "node_modules", dependencyName);
+              var packageJSONPath = path.join(packageJSONDir, "package.json");
+
+              if(fs.existsSync(packageJSONPath)) { // Only augment packages that exist. Sometimes we may have production installs in which development dependencies can be ignored
+                  console.log("Adding metadata fields to: "+packageJSONPath);
+                  var packageObj = JSON.parse(fs.readFileSync(packageJSONPath));
+
+                  if(dependency.integrity) {
+                      packageObj["_integrity"] = dependency.integrity;
+                  } else {
+                      packageObj["_integrity"] = "sha1-000000000000000000000000000="; // When no _integrity string has been provided (e.g. by Git dependencies), add a dummy one. It does not seem to harm and it bypasses downloads.
+                  }
+
+                  if(dependency.resolved) {
+                      packageObj["_resolved"] = dependency.resolved; // Adopt the resolved property if one has been provided
+                  } else {
+                      packageObj["_resolved"] = dependency.version; // Set the resolved version to the version identifier. This prevents NPM from cloning Git repositories.
+                  }
+
+                  if(dependency.from !== undefined) { // Adopt from property if one has been provided
+                      packageObj["_from"] = dependency.from;
+                  }
+
+                  fs.writeFileSync(packageJSONPath, JSON.stringify(packageObj, null, 2));
+              }
+
+              // Augment transitive dependencies
+              if(dependency.dependencies !== undefined) {
+                  augmentDependencies(packageJSONDir, dependency.dependencies);
+              }
+          }
+      }
+
+      if(fs.existsSync("./package-lock.json")) {
+          var packageLock = JSON.parse(fs.readFileSync("./package-lock.json"));
+
+          if(packageLock.lockfileVersion !== 1) {
+             process.stderr.write("Sorry, I only understand lock file version 1!\n");
+             process.exit(1);
+          }
+
+          if(packageLock.dependencies !== undefined) {
+              augmentDependencies(".", packageLock.dependencies);
+          }
+      }
+    '';
+  };
+
+  # Reconstructs a package-lock file from the node_modules/ folder structure and package.json files with dummy sha1 hashes
+  reconstructPackageLock = writeTextFile {
+    name = "addintegrityfields.js";
+    text = ''
+      var fs = require('fs');
+      var path = require('path');
+
+      var packageObj = JSON.parse(fs.readFileSync("package.json"));
+
+      var lockObj = {
+          name: packageObj.name,
+          version: packageObj.version,
+          lockfileVersion: 1,
+          requires: true,
+          dependencies: {}
+      };
+
+      function augmentPackageJSON(filePath, dependencies) {
+          var packageJSON = path.join(filePath, "package.json");
+          if(fs.existsSync(packageJSON)) {
+              var packageObj = JSON.parse(fs.readFileSync(packageJSON));
+              dependencies[packageObj.name] = {
+                  version: packageObj.version,
+                  integrity: "sha1-000000000000000000000000000=",
+                  dependencies: {}
+              };
+              processDependencies(path.join(filePath, "node_modules"), dependencies[packageObj.name].dependencies);
+          }
+      }
+
+      function processDependencies(dir, dependencies) {
+          if(fs.existsSync(dir)) {
+              var files = fs.readdirSync(dir);
+
+              files.forEach(function(entry) {
+                  var filePath = path.join(dir, entry);
+                  var stats = fs.statSync(filePath);
+
+                  if(stats.isDirectory()) {
+                      if(entry.substr(0, 1) == "@") {
+                          // When we encounter a namespace folder, augment all packages belonging to the scope
+                          var pkgFiles = fs.readdirSync(filePath);
+
+                          pkgFiles.forEach(function(entry) {
+                              if(stats.isDirectory()) {
+                                  var pkgFilePath = path.join(filePath, entry);
+                                  augmentPackageJSON(pkgFilePath, dependencies);
+                              }
+                          });
+                      } else {
+                          augmentPackageJSON(filePath, dependencies);
+                      }
+                  }
+              });
+          }
+      }
+
+      processDependencies("node_modules", lockObj.dependencies);
+
+      fs.writeFileSync("package-lock.json", JSON.stringify(lockObj, null, 2));
+    '';
+  };
+
+  prepareAndInvokeNPM = {packageName, bypassCache, reconstructLock, npmFlags, production}:
+    let
+      forceOfflineFlag = if bypassCache then "--offline" else "--registry http://www.example.com";
+    in
+    ''
+        # Pinpoint the versions of all dependencies to the ones that are actually being used
+        echo "pinpointing versions of dependencies..."
+        source $pinpointDependenciesScriptPath
+
+        # Patch the shebangs of the bundled modules to prevent them from
+        # calling executables outside the Nix store as much as possible
+        patchShebangs .
+
+        # Deploy the Node.js package by running npm install. Since the
+        # dependencies have been provided already by ourselves, it should not
+        # attempt to install them again, which is good, because we want to make
+        # it Nix's responsibility. If it needs to install any dependencies
+        # anyway (e.g. because the dependency parameters are
+        # incomplete/incorrect), it fails.
+        #
+        # The other responsibilities of NPM are kept -- version checks, build
+        # steps, postprocessing etc.
+
+        export HOME=$TMPDIR
+        cd "${packageName}"
+        runHook preRebuild
+
+        ${stdenv.lib.optionalString bypassCache ''
+          ${stdenv.lib.optionalString reconstructLock ''
+            if [ -f package-lock.json ]
+            then
+                echo "WARNING: Reconstruct lock option enabled, but a lock file already exists!"
+                echo "This will most likely result in version mismatches! We will remove the lock file and regenerate it!"
+                rm package-lock.json
+            else
+                echo "No package-lock.json file found, reconstructing..."
+            fi
+
+            node ${reconstructPackageLock}
+          ''}
+
+          node ${addIntegrityFieldsScript}
+        ''}
+
+        npm ${forceOfflineFlag} --nodedir=${nodeSources} ${npmFlags} ${stdenv.lib.optionalString production "--production"} rebuild
+
+        if [ "$dontNpmInstall" != "1" ]
+        then
+            # NPM tries to download packages even when they already exist if npm-shrinkwrap is used.
+            rm -f npm-shrinkwrap.json
+
+            npm ${forceOfflineFlag} --nodedir=${nodeSources} ${npmFlags} ${stdenv.lib.optionalString production "--production"} install
+        fi
+    '';
+
+  # Builds and composes an NPM package including all its dependencies
+  buildNodePackage =
+    { name
+    , packageName
+    , version
+    , dependencies ? []
+    , buildInputs ? []
+    , production ? true
+    , npmFlags ? ""
+    , dontNpmInstall ? false
+    , bypassCache ? false
+    , reconstructLock ? false
+    , preRebuild ? ""
+    , dontStrip ? true
+    , unpackPhase ? "true"
+    , buildPhase ? "true"
+    , ... }@args:
+
+    let
+      extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" "dontStrip" "dontNpmInstall" "preRebuild" "unpackPhase" "buildPhase" ];
+    in
+    stdenv.mkDerivation ({
+      name = "node_${name}-${version}";
+      buildInputs = [ tarWrapper python nodejs ]
+        ++ stdenv.lib.optional (stdenv.isLinux) utillinux
+        ++ stdenv.lib.optional (stdenv.isDarwin) libtool
+        ++ buildInputs;
+
+      inherit dontStrip; # Stripping may fail a build for some package deployments
+      inherit dontNpmInstall preRebuild unpackPhase buildPhase;
+
+      compositionScript = composePackage args;
+      pinpointDependenciesScript = pinpointDependenciesOfPackage args;
+
+      passAsFile = [ "compositionScript" "pinpointDependenciesScript" ];
+
+      installPhase = ''
+        # Create and enter a root node_modules/ folder
+        mkdir -p $out/lib/node_modules
+        cd $out/lib/node_modules
+
+        # Compose the package and all its dependencies
+        source $compositionScriptPath
+
+        ${prepareAndInvokeNPM { inherit packageName bypassCache reconstructLock npmFlags production; }}
+
+        # Create symlink to the deployed executable folder, if applicable
+        if [ -d "$out/lib/node_modules/.bin" ]
+        then
+            ln -s $out/lib/node_modules/.bin $out/bin
+        fi
+
+        # Create symlinks to the deployed manual page folders, if applicable
+        if [ -d "$out/lib/node_modules/${packageName}/man" ]
+        then
+            mkdir -p $out/share
+            for dir in "$out/lib/node_modules/${packageName}/man/"*
+            do
+                mkdir -p $out/share/man/$(basename "$dir")
+                for page in "$dir"/*
+                do
+                    ln -s $page $out/share/man/$(basename "$dir")
+                done
+            done
+        fi
+
+        # Run post install hook, if provided
+        runHook postInstall
+      '';
+    } // extraArgs);
+
+  # Builds a development shell
+  buildNodeShell =
+    { name
+    , packageName
+    , version
+    , src
+    , dependencies ? []
+    , buildInputs ? []
+    , production ? true
+    , npmFlags ? ""
+    , dontNpmInstall ? false
+    , bypassCache ? false
+    , reconstructLock ? false
+    , dontStrip ? true
+    , unpackPhase ? "true"
+    , buildPhase ? "true"
+    , ... }@args:
+
+    let
+      extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" ];
+
+      nodeDependencies = stdenv.mkDerivation ({
+        name = "node-dependencies-${name}-${version}";
+
+        buildInputs = [ tarWrapper python nodejs ]
+          ++ stdenv.lib.optional (stdenv.isLinux) utillinux
+          ++ stdenv.lib.optional (stdenv.isDarwin) libtool
+          ++ buildInputs;
+
+        inherit dontStrip; # Stripping may fail a build for some package deployments
+        inherit dontNpmInstall unpackPhase buildPhase;
+
+        includeScript = includeDependencies { inherit dependencies; };
+        pinpointDependenciesScript = pinpointDependenciesOfPackage args;
+
+        passAsFile = [ "includeScript" "pinpointDependenciesScript" ];
+
+        installPhase = ''
+          mkdir -p $out/${packageName}
+          cd $out/${packageName}
+
+          source $includeScriptPath
+
+          # Create fake package.json to make the npm commands work properly
+          cp ${src}/package.json .
+          chmod 644 package.json
+          ${stdenv.lib.optionalString bypassCache ''
+            if [ -f ${src}/package-lock.json ]
+            then
+                cp ${src}/package-lock.json .
+            fi
+          ''}
+
+          # Go to the parent folder to make sure that all packages are pinpointed
+          cd ..
+          ${stdenv.lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
+
+          ${prepareAndInvokeNPM { inherit packageName bypassCache reconstructLock npmFlags production; }}
+
+          # Expose the executables that were installed
+          cd ..
+          ${stdenv.lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
+
+          mv ${packageName} lib
+          ln -s $out/lib/node_modules/.bin $out/bin
+        '';
+      } // extraArgs);
+    in
+    stdenv.mkDerivation {
+      name = "node-shell-${name}-${version}";
+
+      buildInputs = [ python nodejs ] ++ stdenv.lib.optional (stdenv.isLinux) utillinux ++ buildInputs;
+      buildCommand = ''
+        mkdir -p $out/bin
+        cat > $out/bin/shell <<EOF
+        #! ${stdenv.shell} -e
+        $shellHook
+        exec ${stdenv.shell}
+        EOF
+        chmod +x $out/bin/shell
+      '';
+
+      # Provide the dependencies in a development shell through the NODE_PATH environment variable
+      inherit nodeDependencies;
+      shellHook = stdenv.lib.optionalString (dependencies != []) ''
+        export NODE_PATH=$nodeDependencies/lib/node_modules
+        export PATH="$nodeDependencies/bin:$PATH"
+      '';
+    };
+in
+{
+  buildNodeSourceDist = stdenv.lib.makeOverridable buildNodeSourceDist;
+  buildNodePackage = stdenv.lib.makeOverridable buildNodePackage;
+  buildNodeShell = stdenv.lib.makeOverridable buildNodeShell;
+}
diff --git a/system/packages/node2nix/node-packages.json b/system/packages/node2nix/node-packages.json
new file mode 100644
index 00000000..3f6e020b
--- /dev/null
+++ b/system/packages/node2nix/node-packages.json
@@ -0,0 +1,8 @@
+[
+  "prettier_d",
+  "dockerfile-language-server-nodejs",
+  "yaml-language-server",
+  { "pnpm": "3.6.2" },
+  "bunyan",
+  "pino-pretty"
+]
diff --git a/system/packages/node2nix/node-packages.nix b/system/packages/node2nix/node-packages.nix
new file mode 100644
index 00000000..ae1ee1bd
--- /dev/null
+++ b/system/packages/node2nix/node-packages.nix
@@ -0,0 +1,1199 @@
+# This file has been generated by node2nix 1.7.0. Do not edit!
+
+{nodeEnv, fetchurl, fetchgit, globalBuildInputs ? []}:
+
+let
+  sources = {
+    "@hapi/bourne-1.3.2" = {
+      name = "_at_hapi_slash_bourne";
+      packageName = "@hapi/bourne";
+      version = "1.3.2";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/@hapi/bourne/-/bourne-1.3.2.tgz";
+        sha512 = "1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==";
+      };
+    };
+    "agent-base-4.3.0" = {
+      name = "agent-base";
+      packageName = "agent-base";
+      version = "4.3.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz";
+        sha512 = "salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==";
+      };
+    };
+    "ansi-styles-3.2.1" = {
+      name = "ansi-styles";
+      packageName = "ansi-styles";
+      version = "3.2.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz";
+        sha512 = "VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==";
+      };
+    };
+    "argparse-1.0.10" = {
+      name = "argparse";
+      packageName = "argparse";
+      version = "1.0.10";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz";
+        sha512 = "o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==";
+      };
+    };
+    "args-5.0.1" = {
+      name = "args";
+      packageName = "args";
+      version = "5.0.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/args/-/args-5.0.1.tgz";
+        sha512 = "1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==";
+      };
+    };
+    "balanced-match-1.0.0" = {
+      name = "balanced-match";
+      packageName = "balanced-match";
+      version = "1.0.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz";
+        sha1 = "89b4d199ab2bee49de164ea02b89ce462d71b767";
+      };
+    };
+    "bluebird-3.5.5" = {
+      name = "bluebird";
+      packageName = "bluebird";
+      version = "3.5.5";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz";
+        sha512 = "5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==";
+      };
+    };
+    "brace-expansion-1.1.11" = {
+      name = "brace-expansion";
+      packageName = "brace-expansion";
+      version = "1.1.11";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz";
+        sha512 = "iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==";
+      };
+    };
+    "camelcase-4.1.0" = {
+      name = "camelcase";
+      packageName = "camelcase";
+      version = "4.1.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz";
+        sha1 = "d545635be1e33c542649c69173e5de6acfae34dd";
+      };
+    };
+    "camelcase-5.0.0" = {
+      name = "camelcase";
+      packageName = "camelcase";
+      version = "5.0.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz";
+        sha512 = "faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==";
+      };
+    };
+    "camelcase-keys-4.2.0" = {
+      name = "camelcase-keys";
+      packageName = "camelcase-keys";
+      version = "4.2.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz";
+        sha1 = "a2aa5fb1af688758259c32c141426d78923b9b77";
+      };
+    };
+    "chalk-2.4.2" = {
+      name = "chalk";
+      packageName = "chalk";
+      version = "2.4.2";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz";
+        sha512 = "Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==";
+      };
+    };
+    "color-convert-1.9.3" = {
+      name = "color-convert";
+      packageName = "color-convert";
+      version = "1.9.3";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz";
+        sha512 = "QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==";
+      };
+    };
+    "color-name-1.1.3" = {
+      name = "color-name";
+      packageName = "color-name";
+      version = "1.1.3";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz";
+        sha1 = "a7d0558bd89c42f795dd42328f740831ca53bc25";
+      };
+    };
+    "commander-2.0.0" = {
+      name = "commander";
+      packageName = "commander";
+      version = "2.0.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz";
+        sha1 = "d1b86f901f8b64bd941bdeadaf924530393be928";
+      };
+    };
+    "commander-2.20.1" = {
+      name = "commander";
+      packageName = "commander";
+      version = "2.20.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/commander/-/commander-2.20.1.tgz";
+        sha512 = "cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg==";
+      };
+    };
+    "concat-map-0.0.1" = {
+      name = "concat-map";
+      packageName = "concat-map";
+      version = "0.0.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz";
+        sha1 = "d8a96bd77fd68df7793a73036a3ba0d5405d477b";
+      };
+    };
+    "config-attendant-0.1.2" = {
+      name = "config-attendant";
+      packageName = "config-attendant";
+      version = "0.1.2";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/config-attendant/-/config-attendant-0.1.2.tgz";
+        sha512 = "FJ18CFusrB7R0g0FSSMSbMeZiGwqUaznXzmy/wEcpQ8lVf3b+p/D3l5JJu0iDsTmAVOADPP84UM848q0fZ8Nng==";
+      };
+    };
+    "dateformat-3.0.3" = {
+      name = "dateformat";
+      packageName = "dateformat";
+      version = "3.0.3";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz";
+        sha512 = "jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==";
+      };
+    };
+    "debug-3.1.0" = {
+      name = "debug";
+      packageName = "debug";
+      version = "3.1.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz";
+        sha512 = "OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==";
+      };
+    };
+    "deep-extend-0.5.1" = {
+      name = "deep-extend";
+      packageName = "deep-extend";
+      version = "0.5.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz";
+        sha512 = "N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==";
+      };
+    };
+    "deep-is-0.1.3" = {
+      name = "deep-is";
+      packageName = "deep-is";
+      version = "0.1.3";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz";
+        sha1 = "b369d6fb5dbc13eecf524f91b070feedc357cf34";
+      };
+    };
+    "dockerfile-ast-0.0.12" = {
+      name = "dockerfile-ast";
+      packageName = "dockerfile-ast";
+      version = "0.0.12";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/dockerfile-ast/-/dockerfile-ast-0.0.12.tgz";
+        sha512 = "cIV8oXkAxpIuN5XgG0TGg07nLDgrj4olkfrdT77OTA3VypscsYHBUg/FjHxW9K3oA+CyH4Th/qtoMgTVpzSobw==";
+      };
+    };
+    "dockerfile-ast-0.0.16" = {
+      name = "dockerfile-ast";
+      packageName = "dockerfile-ast";
+      version = "0.0.16";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/dockerfile-ast/-/dockerfile-ast-0.0.16.tgz";
+        sha512 = "+HZToHjjiLPl46TqBrok5dMrg5oCkZFPSROMQjRmvin0zG4FxK0DJXTpV/CUPYY2zpmEvVza55XLwSHFx/xZMw==";
+      };
+    };
+    "dockerfile-language-service-0.0.8" = {
+      name = "dockerfile-language-service";
+      packageName = "dockerfile-language-service";
+      version = "0.0.8";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/dockerfile-language-service/-/dockerfile-language-service-0.0.8.tgz";
+        sha512 = "peko1rQtZ81e3QK3VLZgkCKkCMnuoSqZxCQuudyEbtYqqAe6jJ4r0bwOnWD9hyH/FMg7jMvI5uLsoo7/dHLNYw==";
+      };
+    };
+    "dockerfile-utils-0.0.11" = {
+      name = "dockerfile-utils";
+      packageName = "dockerfile-utils";
+      version = "0.0.11";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/dockerfile-utils/-/dockerfile-utils-0.0.11.tgz";
+        sha512 = "LNdPIgcl58343dF4KNCHvFzUScUhgLI9BRAR+Vln6D1tVBGvv1k5/qHuxWRCAM2uyFbj73QVkKMScXPhY7TqfQ==";
+      };
+    };
+    "dockerfile-utils-0.0.13" = {
+      name = "dockerfile-utils";
+      packageName = "dockerfile-utils";
+      version = "0.0.13";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/dockerfile-utils/-/dockerfile-utils-0.0.13.tgz";
+        sha512 = "+MAmhEnQ16B7+3C2UDWpmIS1D8EiKvpl4LDRjrMv94bOusaeRcLagRR0AvgV6NWT+oiRxDMLDyay6yjm6LESsw==";
+      };
+    };
+    "dtrace-provider-0.8.8" = {
+      name = "dtrace-provider";
+      packageName = "dtrace-provider";
+      version = "0.8.8";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz";
+        sha512 = "b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==";
+      };
+    };
+    "editorconfig-0.14.2" = {
+      name = "editorconfig";
+      packageName = "editorconfig";
+      version = "0.14.2";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/editorconfig/-/editorconfig-0.14.2.tgz";
+        sha512 = "tghjvKwo1gakrhFiZWlbo5ILWAfnuOu1JFztW0li+vzbnInN0CMZuF4F0T/Pnn9UWpT7Mr1aFTWdHVuxiR9K9A==";
+      };
+    };
+    "end-of-stream-1.4.4" = {
+      name = "end-of-stream";
+      packageName = "end-of-stream";
+      version = "1.4.4";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz";
+        sha512 = "+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==";
+      };
+    };
+    "es6-promise-4.2.8" = {
+      name = "es6-promise";
+      packageName = "es6-promise";
+      version = "4.2.8";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz";
+        sha512 = "HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==";
+      };
+    };
+    "es6-promisify-5.0.0" = {
+      name = "es6-promisify";
+      packageName = "es6-promisify";
+      version = "5.0.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz";
+        sha1 = "5109d62f3e56ea967c4b63505aef08291c8a5203";
+      };
+    };
+    "escape-string-regexp-1.0.5" = {
+      name = "escape-string-regexp";
+      packageName = "escape-string-regexp";
+      version = "1.0.5";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz";
+        sha1 = "1b61c0562190a8dff6ae3bb2cf0200ca130b86d4";
+      };
+    };
+    "esprima-4.0.1" = {
+      name = "esprima";
+      packageName = "esprima";
+      version = "4.0.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz";
+        sha512 = "eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==";
+      };
+    };
+    "fast-levenshtein-2.0.6" = {
+      name = "fast-levenshtein";
+      packageName = "fast-levenshtein";
+      version = "2.0.6";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz";
+        sha1 = "3d8a5c66883a16a30ca8643e851f19baa7797917";
+      };
+    };
+    "fast-safe-stringify-2.0.7" = {
+      name = "fast-safe-stringify";
+      packageName = "fast-safe-stringify";
+      version = "2.0.7";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz";
+        sha512 = "Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==";
+      };
+    };
+    "glob-6.0.4" = {
+      name = "glob";
+      packageName = "glob";
+      version = "6.0.4";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz";
+        sha1 = "0f08860f6a155127b2fadd4f9ce24b1aab6e4d22";
+      };
+    };
+    "has-flag-2.0.0" = {
+      name = "has-flag";
+      packageName = "has-flag";
+      version = "2.0.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz";
+        sha1 = "e8207af1cc7b30d446cc70b734b5e8be18f88d51";
+      };
+    };
+    "has-flag-3.0.0" = {
+      name = "has-flag";
+      packageName = "has-flag";
+      version = "3.0.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz";
+        sha1 = "b5d454dc2199ae225699f3467e5a07f3b955bafd";
+      };
+    };
+    "http-proxy-agent-2.1.0" = {
+      name = "http-proxy-agent";
+      packageName = "http-proxy-agent";
+      version = "2.1.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz";
+        sha512 = "qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==";
+      };
+    };
+    "https-proxy-agent-2.2.2" = {
+      name = "https-proxy-agent";
+      packageName = "https-proxy-agent";
+      version = "2.2.2";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz";
+        sha512 = "c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==";
+      };
+    };
+    "inflight-1.0.6" = {
+      name = "inflight";
+      packageName = "inflight";
+      version = "1.0.6";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz";
+        sha1 = "49bd6331d7d02d0c09bc910a1075ba8165b56df9";
+      };
+    };
+    "inherits-2.0.4" = {
+      name = "inherits";
+      packageName = "inherits";
+      version = "2.0.4";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz";
+        sha512 = "k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==";
+      };
+    };
+    "ini-1.3.5" = {
+      name = "ini";
+      packageName = "ini";
+      version = "1.3.5";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz";
+        sha512 = "RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==";
+      };
+    };
+    "jmespath-0.15.0" = {
+      name = "jmespath";
+      packageName = "jmespath";
+      version = "0.15.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz";
+        sha1 = "a3f222a9aae9f966f5d27c796510e28091764217";
+      };
+    };
+    "js-yaml-3.13.1" = {
+      name = "js-yaml";
+      packageName = "js-yaml";
+      version = "3.13.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz";
+        sha512 = "YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==";
+      };
+    };
+    "json-align-0.1.0" = {
+      name = "json-align";
+      packageName = "json-align";
+      version = "0.1.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/json-align/-/json-align-0.1.0.tgz";
+        sha1 = "be7879795320dd53e7af6f836e5c2b35c9f1eb50";
+      };
+    };
+    "json-stable-stringify-1.0.1" = {
+      name = "json-stable-stringify";
+      packageName = "json-stable-stringify";
+      version = "1.0.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz";
+        sha1 = "9a759d39c5f2ff503fd5300646ed445f88c4f9af";
+      };
+    };
+    "jsonc-parser-2.1.1" = {
+      name = "jsonc-parser";
+      packageName = "jsonc-parser";
+      version = "2.1.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.1.1.tgz";
+        sha512 = "VC0CjnWJylKB1iov4u76/W/5Ef0ydDkjtYWxoZ9t3HdWlSnZQwZL5MgFikaB/EtQ4RmMEw3tmQzuYnZA2/Ja1g==";
+      };
+    };
+    "jsonify-0.0.0" = {
+      name = "jsonify";
+      packageName = "jsonify";
+      version = "0.0.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz";
+        sha1 = "2c74b6ee41d93ca51b7b5aaee8f503631d252a73";
+      };
+    };
+    "leven-2.1.0" = {
+      name = "leven";
+      packageName = "leven";
+      version = "2.1.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz";
+        sha1 = "c2e7a9f772094dee9d34202ae8acce4687875580";
+      };
+    };
+    "levn-0.3.0" = {
+      name = "levn";
+      packageName = "levn";
+      version = "0.3.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz";
+        sha1 = "3b09924edf9f083c0490fdd4c0bc4421e04764ee";
+      };
+    };
+    "lru-cache-3.2.0" = {
+      name = "lru-cache";
+      packageName = "lru-cache";
+      version = "3.2.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/lru-cache/-/lru-cache-3.2.0.tgz";
+        sha1 = "71789b3b7f5399bec8565dda38aa30d2a097efee";
+      };
+    };
+    "map-obj-2.0.0" = {
+      name = "map-obj";
+      packageName = "map-obj";
+      version = "2.0.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz";
+        sha1 = "a65cd29087a92598b8791257a523e021222ac1f9";
+      };
+    };
+    "minimatch-3.0.4" = {
+      name = "minimatch";
+      packageName = "minimatch";
+      version = "3.0.4";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz";
+        sha512 = "yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==";
+      };
+    };
+    "minimist-0.0.8" = {
+      name = "minimist";
+      packageName = "minimist";
+      version = "0.0.8";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz";
+        sha1 = "857fcabfc3397d2625b8228262e86aa7a011b05d";
+      };
+    };
+    "minimist-1.2.0" = {
+      name = "minimist";
+      packageName = "minimist";
+      version = "1.2.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz";
+        sha1 = "a35008b20f41383eec1fb914f4cd5df79a264284";
+      };
+    };
+    "mkdirp-0.5.1" = {
+      name = "mkdirp";
+      packageName = "mkdirp";
+      version = "0.5.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz";
+        sha1 = "30057438eac6cf7f8c4767f38648d6697d75c903";
+      };
+    };
+    "moment-2.24.0" = {
+      name = "moment";
+      packageName = "moment";
+      version = "2.24.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz";
+        sha512 = "bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==";
+      };
+    };
+    "mri-1.1.4" = {
+      name = "mri";
+      packageName = "mri";
+      version = "1.1.4";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz";
+        sha512 = "6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==";
+      };
+    };
+    "ms-2.0.0" = {
+      name = "ms";
+      packageName = "ms";
+      version = "2.0.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz";
+        sha1 = "5608aeadfc00be6c2901df5f9861788de0d597c8";
+      };
+    };
+    "mv-2.1.1" = {
+      name = "mv";
+      packageName = "mv";
+      version = "2.1.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz";
+        sha1 = "ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2";
+      };
+    };
+    "nan-2.14.0" = {
+      name = "nan";
+      packageName = "nan";
+      version = "2.14.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz";
+        sha512 = "INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==";
+      };
+    };
+    "ncp-2.0.0" = {
+      name = "ncp";
+      packageName = "ncp";
+      version = "2.0.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz";
+        sha1 = "195a21d6c46e361d2fb1281ba38b91e9df7bdbb3";
+      };
+    };
+    "once-1.4.0" = {
+      name = "once";
+      packageName = "once";
+      version = "1.4.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/once/-/once-1.4.0.tgz";
+        sha1 = "583b1aa775961d4b113ac17d9c50baef9dd76bd1";
+      };
+    };
+    "optionator-0.8.2" = {
+      name = "optionator";
+      packageName = "optionator";
+      version = "0.8.2";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz";
+        sha1 = "364c5e409d3f4d6301d6c0b4c05bba50180aeb64";
+      };
+    };
+    "path-is-absolute-1.0.1" = {
+      name = "path-is-absolute";
+      packageName = "path-is-absolute";
+      version = "1.0.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz";
+        sha1 = "174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f";
+      };
+    };
+    "path-parse-1.0.6" = {
+      name = "path-parse";
+      packageName = "path-parse";
+      version = "1.0.6";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz";
+        sha512 = "GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==";
+      };
+    };
+    "prelude-ls-1.1.2" = {
+      name = "prelude-ls";
+      packageName = "prelude-ls";
+      version = "1.1.2";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz";
+        sha1 = "21932a549f5e52ffd9a827f570e04be62a97da54";
+      };
+    };
+    "prettier-1.18.2" = {
+      name = "prettier";
+      packageName = "prettier";
+      version = "1.18.2";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz";
+        sha512 = "OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==";
+      };
+    };
+    "pseudomap-1.0.2" = {
+      name = "pseudomap";
+      packageName = "pseudomap";
+      version = "1.0.2";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz";
+        sha1 = "f052a28da70e618917ef0a8ac34c1ae5a68286b3";
+      };
+    };
+    "pump-3.0.0" = {
+      name = "pump";
+      packageName = "pump";
+      version = "3.0.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz";
+        sha512 = "LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==";
+      };
+    };
+    "quick-lru-1.1.0" = {
+      name = "quick-lru";
+      packageName = "quick-lru";
+      version = "1.1.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz";
+        sha1 = "4360b17c61136ad38078397ff11416e186dcfbb8";
+      };
+    };
+    "readable-stream-3.4.0" = {
+      name = "readable-stream";
+      packageName = "readable-stream";
+      version = "3.4.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz";
+        sha512 = "jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==";
+      };
+    };
+    "request-light-0.2.4" = {
+      name = "request-light";
+      packageName = "request-light";
+      version = "0.2.4";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/request-light/-/request-light-0.2.4.tgz";
+        sha512 = "pM9Fq5jRnSb+82V7M97rp8FE9/YNeP2L9eckB4Szd7lyeclSIx02aIpPO/6e4m6Dy31+FBN/zkFMTd2HkNO3ow==";
+      };
+    };
+    "resolve-1.12.0" = {
+      name = "resolve";
+      packageName = "resolve";
+      version = "1.12.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz";
+        sha512 = "B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==";
+      };
+    };
+    "rimraf-2.4.5" = {
+      name = "rimraf";
+      packageName = "rimraf";
+      version = "2.4.5";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz";
+        sha1 = "ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da";
+      };
+    };
+    "safe-buffer-5.2.0" = {
+      name = "safe-buffer";
+      packageName = "safe-buffer";
+      version = "5.2.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz";
+        sha512 = "fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==";
+      };
+    };
+    "safe-json-stringify-1.2.0" = {
+      name = "safe-json-stringify";
+      packageName = "safe-json-stringify";
+      version = "1.2.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz";
+        sha512 = "gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==";
+      };
+    };
+    "semver-5.7.1" = {
+      name = "semver";
+      packageName = "semver";
+      version = "5.7.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz";
+        sha512 = "sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==";
+      };
+    };
+    "sigmund-1.0.1" = {
+      name = "sigmund";
+      packageName = "sigmund";
+      version = "1.0.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz";
+        sha1 = "3ff21f198cad2175f9f3b781853fd94d0d19b590";
+      };
+    };
+    "split2-3.1.1" = {
+      name = "split2";
+      packageName = "split2";
+      version = "3.1.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/split2/-/split2-3.1.1.tgz";
+        sha512 = "emNzr1s7ruq4N+1993yht631/JH+jaj0NYBosuKmLcq+JkGQ9MmTw1RB1fGaTCzUuseRIClrlSLHRNYGwWQ58Q==";
+      };
+    };
+    "sprintf-js-1.0.3" = {
+      name = "sprintf-js";
+      packageName = "sprintf-js";
+      version = "1.0.3";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz";
+        sha1 = "04e6926f662895354f3dd015203633b857297e2c";
+      };
+    };
+    "string_decoder-1.3.0" = {
+      name = "string_decoder";
+      packageName = "string_decoder";
+      version = "1.3.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz";
+        sha512 = "hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==";
+      };
+    };
+    "strip-json-comments-2.0.1" = {
+      name = "strip-json-comments";
+      packageName = "strip-json-comments";
+      version = "2.0.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz";
+        sha1 = "3c531942e908c2697c0ec344858c286c7ca0a60a";
+      };
+    };
+    "supports-color-4.5.0" = {
+      name = "supports-color";
+      packageName = "supports-color";
+      version = "4.5.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz";
+        sha1 = "be7a0de484dec5c5cddf8b3d59125044912f635b";
+      };
+    };
+    "supports-color-5.5.0" = {
+      name = "supports-color";
+      packageName = "supports-color";
+      version = "5.5.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz";
+        sha512 = "QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==";
+      };
+    };
+    "type-check-0.3.2" = {
+      name = "type-check";
+      packageName = "type-check";
+      version = "0.3.2";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz";
+        sha1 = "5884cab512cf1d355e3fb784f30804b2b520db72";
+      };
+    };
+    "unquote-1.1.1" = {
+      name = "unquote";
+      packageName = "unquote";
+      version = "1.1.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz";
+        sha1 = "8fded7324ec6e88a0ff8b905e7c098cdc086d544";
+      };
+    };
+    "util-deprecate-1.0.2" = {
+      name = "util-deprecate";
+      packageName = "util-deprecate";
+      version = "1.0.2";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz";
+        sha1 = "450d4dc9fa70de732762fbd2d4a28981419a0ccf";
+      };
+    };
+    "vscode-json-languageservice-3.3.4" = {
+      name = "vscode-json-languageservice";
+      packageName = "vscode-json-languageservice";
+      version = "3.3.4";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-3.3.4.tgz";
+        sha512 = "/nuI4uDBfxyVyeGtBdYwP/tIaXYKOoymUOSozYKLzsmrDmu555gZpzc11LrARa96z92wSaa5hfjTtNMAoM2mxw==";
+      };
+    };
+    "vscode-jsonrpc-4.0.0" = {
+      name = "vscode-jsonrpc";
+      packageName = "vscode-jsonrpc";
+      version = "4.0.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz";
+        sha512 = "perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg==";
+      };
+    };
+    "vscode-languageserver-5.2.1" = {
+      name = "vscode-languageserver";
+      packageName = "vscode-languageserver";
+      version = "5.2.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-5.2.1.tgz";
+        sha512 = "GuayqdKZqAwwaCUjDvMTAVRPJOp/SLON3mJ07eGsx/Iq9HjRymhKWztX41rISqDKhHVVyFM+IywICyZDla6U3A==";
+      };
+    };
+    "vscode-languageserver-protocol-3.14.1" = {
+      name = "vscode-languageserver-protocol";
+      packageName = "vscode-languageserver-protocol";
+      version = "3.14.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz";
+        sha512 = "IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g==";
+      };
+    };
+    "vscode-languageserver-types-3.14.0" = {
+      name = "vscode-languageserver-types";
+      packageName = "vscode-languageserver-types";
+      version = "3.14.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz";
+        sha512 = "lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A==";
+      };
+    };
+    "vscode-languageserver-types-3.15.0-next.5" = {
+      name = "vscode-languageserver-types";
+      packageName = "vscode-languageserver-types";
+      version = "3.15.0-next.5";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.5.tgz";
+        sha512 = "7hrELhTeWieUgex3+6692KjCkcmO/+V/bFItM5MHGcBotzwmjEuXjapLLYTYhIspuJ1ibRSik5MhX5YwLpsPiw==";
+      };
+    };
+    "vscode-languageserver-types-3.6.0" = {
+      name = "vscode-languageserver-types";
+      packageName = "vscode-languageserver-types";
+      version = "3.6.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.6.0.tgz";
+        sha512 = "GSgQtGmtza4PoNH0+iHWylWg/1sw2DODezqYWRxbN910dPchI3CQaSJN76csKcQGv55wsWgX82T6n74q8mFSpw==";
+      };
+    };
+    "vscode-nls-4.1.1" = {
+      name = "vscode-nls";
+      packageName = "vscode-nls";
+      version = "4.1.1";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.1.tgz";
+        sha512 = "4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A==";
+      };
+    };
+    "vscode-uri-1.0.8" = {
+      name = "vscode-uri";
+      packageName = "vscode-uri";
+      version = "1.0.8";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.8.tgz";
+        sha512 = "obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ==";
+      };
+    };
+    "vscode-uri-2.0.3" = {
+      name = "vscode-uri";
+      packageName = "vscode-uri";
+      version = "2.0.3";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.0.3.tgz";
+        sha512 = "4D3DI3F4uRy09WNtDGD93H9q034OHImxiIcSq664Hq1Y1AScehlP3qqZyTkX/RWxeu0MRMHGkrxYqm2qlDF/aw==";
+      };
+    };
+    "wordwrap-1.0.0" = {
+      name = "wordwrap";
+      packageName = "wordwrap";
+      version = "1.0.0";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz";
+        sha1 = "27584810891456a4171c8d0226441ade90cbcaeb";
+      };
+    };
+    "wrappy-1.0.2" = {
+      name = "wrappy";
+      packageName = "wrappy";
+      version = "1.0.2";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz";
+        sha1 = "b5243d8f3ec1aa35f1364605bc0d1036e30ab69f";
+      };
+    };
+    "yaml-ast-parser-custom-tags-0.0.43" = {
+      name = "yaml-ast-parser-custom-tags";
+      packageName = "yaml-ast-parser-custom-tags";
+      version = "0.0.43";
+      src = fetchurl {
+        url = "https://registry.npmjs.org/yaml-ast-parser-custom-tags/-/yaml-ast-parser-custom-tags-0.0.43.tgz";
+        sha512 = "R5063FF/JSAN6qXCmylwjt9PcDH6M0ExEme/nJBzLspc6FJDmHHIqM7xh2WfEmsTJqClF79A9VkXjkAqmZw9SQ==";
+      };
+    };
+  };
+in
+{
+  prettier_d = nodeEnv.buildNodePackage {
+    name = "prettier_d";
+    packageName = "prettier_d";
+    version = "5.7.4";
+    src = fetchurl {
+      url = "https://registry.npmjs.org/prettier_d/-/prettier_d-5.7.4.tgz";
+      sha512 = "gip1vAQw8T2/l3ur84h2Q7RelHAegaBF8V+D70Jc0/FqpfRTKQrqPJTRQg8fxHZuBVCpY3tcsI5So8h/ltCfsA==";
+    };
+    dependencies = [
+      sources."ansi-styles-3.2.1"
+      sources."bluebird-3.5.5"
+      sources."camelcase-4.1.0"
+      sources."camelcase-keys-4.2.0"
+      (sources."chalk-2.4.2" // {
+        dependencies = [
+          sources."supports-color-5.5.0"
+        ];
+      })
+      sources."color-convert-1.9.3"
+      sources."color-name-1.1.3"
+      sources."commander-2.20.1"
+      sources."config-attendant-0.1.2"
+      sources."deep-extend-0.5.1"
+      sources."deep-is-0.1.3"
+      sources."editorconfig-0.14.2"
+      sources."escape-string-regexp-1.0.5"
+      sources."fast-levenshtein-2.0.6"
+      sources."has-flag-3.0.0"
+      sources."ini-1.3.5"
+      (sources."json-align-0.1.0" // {
+        dependencies = [
+          sources."commander-2.0.0"
+        ];
+      })
+      sources."json-stable-stringify-1.0.1"
+      sources."jsonify-0.0.0"
+      sources."levn-0.3.0"
+      sources."lru-cache-3.2.0"
+      sources."map-obj-2.0.0"
+      sources."minimist-1.2.0"
+      sources."optionator-0.8.2"
+      sources."path-parse-1.0.6"
+      sources."prelude-ls-1.1.2"
+      sources."prettier-1.18.2"
+      sources."pseudomap-1.0.2"
+      sources."quick-lru-1.1.0"
+      sources."resolve-1.12.0"
+      sources."semver-5.7.1"
+      sources."sigmund-1.0.1"
+      sources."strip-json-comments-2.0.1"
+      (sources."supports-color-4.5.0" // {
+        dependencies = [
+          sources."has-flag-2.0.0"
+        ];
+      })
+      sources."type-check-0.3.2"
+      sources."unquote-1.1.1"
+      sources."wordwrap-1.0.0"
+    ];
+    buildInputs = globalBuildInputs;
+    meta = {
+      description = "Makes prettier the fastest formatter on the planet";
+      homepage = https://github.com/josephfrazier/prettier_d;
+      license = "MIT";
+    };
+    production = true;
+    bypassCache = true;
+    reconstructLock = true;
+  };
+  dockerfile-language-server-nodejs = nodeEnv.buildNodePackage {
+    name = "dockerfile-language-server-nodejs";
+    packageName = "dockerfile-language-server-nodejs";
+    version = "0.0.21";
+    src = fetchurl {
+      url = "https://registry.npmjs.org/dockerfile-language-server-nodejs/-/dockerfile-language-server-nodejs-0.0.21.tgz";
+      sha512 = "lZ7VFAlS4vTm5MvxmwpREcYMARB3RQaGX0OZdcY8oSytsu4i5mMGVa6mi9/pZ9soqcUC08uxEA8EcqIeL3lyAA==";
+    };
+    dependencies = [
+      sources."dockerfile-ast-0.0.16"
+      (sources."dockerfile-language-service-0.0.8" // {
+        dependencies = [
+          (sources."dockerfile-utils-0.0.13" // {
+            dependencies = [
+              sources."vscode-languageserver-types-3.6.0"
+            ];
+          })
+        ];
+      })
+      (sources."dockerfile-utils-0.0.11" // {
+        dependencies = [
+          sources."dockerfile-ast-0.0.12"
+          sources."vscode-languageserver-types-3.6.0"
+        ];
+      })
+      sources."vscode-jsonrpc-4.0.0"
+      sources."vscode-languageserver-5.2.1"
+      sources."vscode-languageserver-protocol-3.14.1"
+      sources."vscode-languageserver-types-3.14.0"
+      sources."vscode-uri-1.0.8"
+    ];
+    buildInputs = globalBuildInputs;
+    meta = {
+      description = "A language server for Dockerfiles powered by NodeJS, TypeScript, and VSCode technologies.";
+      homepage = "https://github.com/rcjsuen/dockerfile-language-server-nodejs#readme";
+      license = "MIT";
+    };
+    production = true;
+    bypassCache = true;
+    reconstructLock = true;
+  };
+  yaml-language-server = nodeEnv.buildNodePackage {
+    name = "yaml-language-server";
+    packageName = "yaml-language-server";
+    version = "0.5.8";
+    src = fetchurl {
+      url = "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-0.5.8.tgz";
+      sha512 = "IDPHRXJJz3W7MIksFGwQ9GweY+n064MSsiC3LpoxURdafNQ+6OcBAuVwfJdItRTGSB96l2soQB5UfjiqVBeggQ==";
+    };
+    dependencies = [
+      sources."agent-base-4.3.0"
+      sources."argparse-1.0.10"
+      sources."debug-3.1.0"
+      sources."es6-promise-4.2.8"
+      sources."es6-promisify-5.0.0"
+      sources."esprima-4.0.1"
+      sources."http-proxy-agent-2.1.0"
+      sources."https-proxy-agent-2.2.2"
+      sources."js-yaml-3.13.1"
+      sources."jsonc-parser-2.1.1"
+      sources."ms-2.0.0"
+      sources."prettier-1.18.2"
+      sources."request-light-0.2.4"
+      sources."sprintf-js-1.0.3"
+      (sources."vscode-json-languageservice-3.3.4" // {
+        dependencies = [
+          sources."vscode-languageserver-types-3.15.0-next.5"
+        ];
+      })
+      sources."vscode-jsonrpc-4.0.0"
+      (sources."vscode-languageserver-5.2.1" // {
+        dependencies = [
+          sources."vscode-uri-1.0.8"
+        ];
+      })
+      sources."vscode-languageserver-protocol-3.14.1"
+      sources."vscode-languageserver-types-3.14.0"
+      sources."vscode-nls-4.1.1"
+      sources."vscode-uri-2.0.3"
+      sources."yaml-ast-parser-custom-tags-0.0.43"
+    ];
+    buildInputs = globalBuildInputs;
+    meta = {
+      description = "YAML language server";
+      homepage = "https://github.com/redhat-developer/yaml-language-server#readme";
+      license = "MIT";
+    };
+    production = true;
+    bypassCache = true;
+    reconstructLock = true;
+  };
+  "pnpm-3.6.2" = nodeEnv.buildNodePackage {
+    name = "pnpm";
+    packageName = "pnpm";
+    version = "3.6.2";
+    src = fetchurl {
+      url = "https://registry.npmjs.org/pnpm/-/pnpm-3.6.2.tgz";
+      sha512 = "/UW87zxOU3p60deTeo94GX/ZR6ph1tGDDZmCnIFvo2gMsAaWAM/nePuAdT/RZ3cbSJfG3+/HXxsYOxetAjC/Sg==";
+    };
+    buildInputs = globalBuildInputs;
+    meta = {
+      description = "Fast, disk space efficient package manager";
+      homepage = https://pnpm.js.org/;
+      license = "MIT";
+    };
+    production = true;
+    bypassCache = true;
+    reconstructLock = true;
+  };
+  bunyan = nodeEnv.buildNodePackage {
+    name = "bunyan";
+    packageName = "bunyan";
+    version = "1.8.12";
+    src = fetchurl {
+      url = "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz";
+      sha1 = "f150f0f6748abdd72aeae84f04403be2ef113797";
+    };
+    dependencies = [
+      sources."balanced-match-1.0.0"
+      sources."brace-expansion-1.1.11"
+      sources."concat-map-0.0.1"
+      sources."dtrace-provider-0.8.8"
+      sources."glob-6.0.4"
+      sources."inflight-1.0.6"
+      sources."inherits-2.0.4"
+      sources."minimatch-3.0.4"
+      sources."minimist-0.0.8"
+      sources."mkdirp-0.5.1"
+      sources."moment-2.24.0"
+      sources."mv-2.1.1"
+      sources."nan-2.14.0"
+      sources."ncp-2.0.0"
+      sources."once-1.4.0"
+      sources."path-is-absolute-1.0.1"
+      sources."rimraf-2.4.5"
+      sources."safe-json-stringify-1.2.0"
+      sources."wrappy-1.0.2"
+    ];
+    buildInputs = globalBuildInputs;
+    meta = {
+      description = "a JSON logging library for node.js services";
+      homepage = "https://github.com/trentm/node-bunyan#readme";
+      license = "MIT";
+    };
+    production = true;
+    bypassCache = true;
+    reconstructLock = true;
+  };
+  pino-pretty = nodeEnv.buildNodePackage {
+    name = "pino-pretty";
+    packageName = "pino-pretty";
+    version = "3.2.1";
+    src = fetchurl {
+      url = "https://registry.npmjs.org/pino-pretty/-/pino-pretty-3.2.1.tgz";
+      sha512 = "PGdcRYw7HCF7ovMhrnepOUmEVh5+tATydRrBICEbP37oRasXV+lo2HA9gg8b7cE7LG6G1OZGVXTZ7MLd946k1Q==";
+    };
+    dependencies = [
+      sources."@hapi/bourne-1.3.2"
+      sources."ansi-styles-3.2.1"
+      sources."args-5.0.1"
+      sources."camelcase-5.0.0"
+      sources."chalk-2.4.2"
+      sources."color-convert-1.9.3"
+      sources."color-name-1.1.3"
+      sources."dateformat-3.0.3"
+      sources."end-of-stream-1.4.4"
+      sources."escape-string-regexp-1.0.5"
+      sources."fast-safe-stringify-2.0.7"
+      sources."has-flag-3.0.0"
+      sources."inherits-2.0.4"
+      sources."jmespath-0.15.0"
+      sources."leven-2.1.0"
+      sources."mri-1.1.4"
+      sources."once-1.4.0"
+      sources."pump-3.0.0"
+      sources."readable-stream-3.4.0"
+      sources."safe-buffer-5.2.0"
+      sources."split2-3.1.1"
+      sources."string_decoder-1.3.0"
+      sources."supports-color-5.5.0"
+      sources."util-deprecate-1.0.2"
+      sources."wrappy-1.0.2"
+    ];
+    buildInputs = globalBuildInputs;
+    meta = {
+      description = "Prettifier for Pino log lines";
+      homepage = "https://github.com/pinojs/pino-pretty#readme";
+      license = "MIT";
+    };
+    production = true;
+    bypassCache = true;
+    reconstructLock = true;
+  };
+}
\ No newline at end of file
diff --git a/system/prefect.nix b/system/prefect.nix
new file mode 100644
index 00000000..b7a6ac32
--- /dev/null
+++ b/system/prefect.nix
@@ -0,0 +1,50 @@
+{ config, pkgs, ... }:
+
+{ imports = [
+    ./hardware-configuration.nix
+
+    ./modules/satoshipay.nix
+    ./modules/base.nix
+    ./modules/configuration/berlin.nix
+    ./modules/configuration/british-english.nix
+    ./modules/configuration/nix.nix
+    ./modules/configuration/user.nix
+    ./modules/development/base.nix
+    ./modules/development/javascript.nix
+    ./modules/development/lisp.nix
+    ./modules/hardware/audio.nix
+    ./modules/hardware/bare-metal.nix
+    ./modules/hardware/mouse.nix
+    ./modules/hardware/systemd-boot.nix
+    ./modules/hardware/nvidia-gpu.nix
+    ./modules/hardware/trezor.nix
+    ./modules/services/zeroconf.nix
+    ./modules/user-interface.nix
+    ./modules/programs/accounting.nix
+    ./modules/programs/dotfiles.nix
+    ./modules/programs/gnupg.nix
+    ./modules/programs/keybase.nix
+    ./modules/programs/i3.nix
+    ./modules/programs/infrastructure.nix
+    ./modules/programs/passwords.nix
+    ./modules/programs/shell.nix
+    ./modules/gaming.nix
+    ./modules/programs/tor.nix
+    ./modules/programs/barrier.nix
+    ./modules/services/syncthing.nix
+  ];
+  boot.loader.efi.canTouchEfiVariables = pkgs.lib.mkForce false;
+  services.xserver.screenSection = ''
+    Option "MetaModes" "2560x1440_120"
+  '';
+
+  services.xserver.xautolock.enable = pkgs.lib.mkForce false;
+  powerManagement.enable = false;
+  # powerManagement.cpuFreqGovernor = "performance";
+  networking = {
+    hostName = "prefect";
+    domain = "alanpearce.eu";
+  };
+
+  system.stateVersion = "19.03";
+}
diff --git a/system/satoshipad.nix b/system/satoshipad.nix
new file mode 100644
index 00000000..86123e1e
--- /dev/null
+++ b/system/satoshipad.nix
@@ -0,0 +1,69 @@
+{ config, pkgs, ... }:
+
+{
+  imports = [
+    ./hardware-configuration.nix
+    ./private/default.nix
+
+    ./modules/base.nix
+    ./modules/configuration/berlin.nix
+    ./modules/configuration/british-english.nix
+    ./modules/configuration/nix.nix
+    ./modules/configuration/user.nix
+    ./modules/development/base.nix
+    ./modules/development/javascript.nix
+    ./modules/hardware/audio.nix
+    ./modules/hardware/systemd-boot.nix
+    ./modules/hardware/keyboardio-model01.nix
+    ./modules/hardware/network-manager.nix
+    ./modules/hardware/qwerty.nix
+    ./modules/hardware/trackball.nix
+    ./modules/hardware/trezor.nix
+    ./modules/machines/t470s.nix
+    ./modules/programs/dotfiles.nix
+    ./modules/programs/gnupg.nix
+    ./modules/programs/keybase.nix
+    ./modules/programs/tor.nix
+    ./modules/programs/i3.nix
+    ./modules/programs/infrastructure.nix
+    ./modules/programs/passwords.nix
+    ./modules/programs/shell.nix
+    ./modules/satoshipay.nix
+  ];
+
+  boot.initrd.luks.devices = [
+    {
+      name = "root";
+      allowDiscards = true;
+      device = "/dev/disk/by-uuid/bb7f9a24-8963-4d00-9258-118050b35748";
+      preLVM = true;
+    }
+  ];
+
+  networking.hostName = "satoshipad";
+
+  nix.gc.dates = "12:30";
+  system.autoUpgrade.dates = "13:24";
+  virtualisation.docker.autoPrune.dates = "Mon, 13:00";
+
+  system.stateVersion = "18.03";
+
+  systemd.user.services.mbsync = {
+    description = "Mailbox synchronisation";
+    path = with pkgs; [ python3Packages.keyring ];
+    serviceConfig = {
+      Type = "oneshot";
+      ExecStart = "${pkgs.isync}/bin/mbsync -Va";
+      ExecStartPost = "${pkgs.myEmacs}/bin/emacsclient -e (mu4e-update-index)";
+    };
+  };
+
+  systemd.user.timers.mbsync = {
+    description = "Mailbox synchronisation timer";
+    wantedBy = [ "default.target" ];
+    timerConfig = {
+      OnBootSec = "2m";
+      OnUnitActiveSec = "10m";
+    };
+  };
+}
diff --git a/system/trillian.nix b/system/trillian.nix
new file mode 100644
index 00000000..1f4ce67e
--- /dev/null
+++ b/system/trillian.nix
@@ -0,0 +1,133 @@
+{ config, pkgs, ... }:
+
+let emacsPackage = import ./packages/emacs.nix {
+  pkgs = pkgs.unstable;
+  emacs = pkgs.unstable.emacs;
+};
+in
+{
+  imports = [
+    ./private/default.nix
+
+    ./modules/development/base.nix
+    ./modules/development/javascript.nix
+    ./modules/programs/accounting.nix
+    ./modules/programs/dotfiles.nix
+    ./modules/programs/shell.nix
+  ];
+
+  nixpkgs.config = {
+    allowUnfree = true;
+    packageOverrides = pkgs: {
+      unstable = import <nixpkgs-unstable> {
+        config = config.nixpkgs.config;
+      };
+    };
+  };
+
+  networking = {
+    hostName = "trillian";
+    knownNetworkServices = [ "Wi-Fi" "USB 10/100/1000 LAN" ];
+    dns = [
+      "::1"
+      "127.0.0.1"
+    ];
+  };
+
+  # List packages installed in system profile. To search by name, run:
+  # $ nix-env -qaP | grep  wget
+  environment.systemPackages = with pkgs;
+    [
+      mosh
+      emacsPackage
+      aspell
+      aspellDicts.en
+      darwin-zsh-completions
+    ];
+
+  # Use a custom configuration.nix location.
+  # $ darwin-rebuild switch -I darwin-config=$HOME/.config/nixpkgs/darwin/configuration.nix
+  # environment.darwinConfig = "$HOME/.config/nixpkgs/darwin/configuration.nix";
+
+  # Auto upgrade nix package and the daemon service.
+  services.nix-daemon.enable = true;
+  services.nix-daemon.enableSocketListener = true;
+  # nix.package = pkgs.nix;
+
+  services.activate-system.enable = true;
+
+  # programs.nix-index.enable = true;
+
+  programs.vim = {
+    enable = true;
+    enableSensible = true;
+  };
+
+  programs.zsh.enable = true;
+  programs.zsh.enableBashCompletion = true;
+
+  environment.variables.LANG = "en_GB.UTF-8";
+
+  programs.gnupg = {
+    agent = {
+      enable = true;
+      enableSSHSupport = false;
+    };
+  };
+
+  # launchd.user.agents.emacs-daemon = {
+  #   command = "${emacsPackage}/bin/emacs --daemon";
+  #   serviceConfig = {
+  #     KeepAlive = true;
+  #   };
+  # };
+  #
+  nix.gc = {
+    automatic = true;
+    options = "--max-freed $((25 * 1024**3 - 1024 * $(df -P -k /nix/store | tail -n 1 | awk '{ print $4 }')))";
+  };
+  nix.daemonNiceLevel = 10;
+  nix.daemonIONice = true;
+
+  nixpkgs.overlays = [
+    (self: super: {
+      darwin-zsh-completions = super.runCommandNoCC "darwin-zsh-completions-0.0.0"
+        { preferLocalBuild = true; }
+        ''
+          mkdir -p $out/share/zsh/site-functions
+          cat <<-'EOF' > $out/share/zsh/site-functions/_darwin-rebuild
+          #compdef darwin-rebuild
+          #autoload
+          _nix-common-options
+          local -a _1st_arguments
+          _1st_arguments=(
+            'switch:Build, activate, and update the current generation'\
+            'build:Build without activating or updating the current generation'\
+            'check:Build and run the activation sanity checks'\
+            'changelog:Show most recent entries in the changelog'\
+          )
+          _arguments \
+            '--list-generations[Print a list of all generations in the active profile]'\
+            '--rollback[Roll back to the previous configuration]'\
+            {--switch-generation,-G}'[Activate specified generation]'\
+            '(--profile-name -p)'{--profile-name,-p}'[Profile to use to track current and previous system configurations]:Profile:_nix_profiles'\
+            '1:: :->subcmds' && return 0
+          case $state in
+            subcmds)
+              _describe -t commands 'darwin-rebuild subcommands' _1st_arguments
+            ;;
+          esac
+          EOF
+        '';
+      })
+  ];
+  # Used for backwards compatibility, please read the changelog before changing.
+  # $ darwin-rebuild changelog
+  system.stateVersion = 4;
+
+  # You should generally set this to the total number of logical cores in your system.
+  # $ sysctl -n hw.ncpu
+  nix.maxJobs = 4;
+  nix.buildCores = 2;
+}
+# vim: sw=2 sts=2 expandtab autoindent smarttab