summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--nixos/modules/services/networking/i2pd.nix604
-rw-r--r--pkgs/applications/networking/instant-messengers/nheko/default.nix63
-rw-r--r--pkgs/applications/networking/instant-messengers/nheko/external-deps.patch94
-rw-r--r--pkgs/applications/networking/instant-messengers/nheko/fetchurls.patch21
-rw-r--r--pkgs/data/misc/osinfo-db/default.nix4
-rw-r--r--pkgs/development/interpreters/racket/default.nix2
-rw-r--r--pkgs/development/libraries/mtxclient/default.nix31
-rw-r--r--pkgs/development/libraries/spdlog/default.nix60
-rw-r--r--pkgs/development/python-modules/cozy/default.nix4
-rw-r--r--pkgs/development/python-modules/flask-ldap-login/default.nix21
-rw-r--r--pkgs/development/tools/castxml/default.nix10
-rw-r--r--pkgs/games/dwarf-fortress/default.nix115
-rw-r--r--pkgs/games/dwarf-fortress/dfhack/default.nix62
-rw-r--r--pkgs/games/dwarf-fortress/dwarf-therapist/dwarf-therapist.in26
-rw-r--r--pkgs/games/dwarf-fortress/dwarf-therapist/wrapper.nix43
-rw-r--r--pkgs/games/dwarf-fortress/game.nix3
-rw-r--r--pkgs/games/dwarf-fortress/lazy-pack.nix22
-rw-r--r--pkgs/games/dwarf-fortress/themes/default.nix2
-rw-r--r--pkgs/games/dwarf-fortress/twbt/default.nix56
-rw-r--r--pkgs/games/dwarf-fortress/unfuck.nix57
-rw-r--r--pkgs/games/dwarf-fortress/wrapper/default.nix31
-rw-r--r--pkgs/top-level/all-packages.nix11
-rw-r--r--pkgs/top-level/emacs-packages.nix2
23 files changed, 845 insertions, 499 deletions
diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix
index 3afafaf3fed5..0e9b354cfcaf 100644
--- a/nixos/modules/services/networking/i2pd.nix
+++ b/nixos/modules/services/networking/i2pd.nix
@@ -8,6 +8,17 @@ let
 
   homeDir = "/var/lib/i2pd";
 
+  strOpt = k: v: k + " = " + v;
+  boolOpt = k: v: k + " = " + boolToString v;
+  intOpt = k: v: k + " = " + toString v;
+  lstOpt = k: xs: k + " = " + concatStringsSep "," xs;
+  optionalNullString = o: s: optional (! isNull s) (strOpt o s);
+  optionalNullBool = o: b: optional (! isNull b) (boolOpt o b);
+  optionalNullInt = o: i: optional (! isNull i) (intOpt o i);
+  optionalEmptyList = o: l: optional ([] != l) (lstOpt o l);
+
+  mkEnableTrueOption = name: mkEnableOption name // { default = true; };
+
   mkEndpointOpt = name: addr: port: {
     enable = mkEnableOption name;
     name = mkOption {
@@ -18,42 +29,54 @@ let
     address = mkOption {
       type = types.str;
       default = addr;
-      description = "Bind address for ${name} endpoint. Default: " + addr;
+      description = "Bind address for ${name} endpoint.";
     };
     port = mkOption {
       type = types.int;
       default = port;
-      description = "Bind port for ${name} endoint. Default: " + toString port;
+      description = "Bind port for ${name} endoint.";
     };
   };
 
-  mkKeyedEndpointOpt = name: addr: port: keyFile:
+  i2cpOpts = name: {
+    length = mkOption {
+      type = types.int;
+      description = "Guaranteed minimum hops for ${name} tunnels.";
+      default = 3;
+    };
+    quantity = mkOption {
+      type = types.int;
+      description = "Number of simultaneous ${name} tunnels.";
+      default = 5;
+    };
+  };
+
+  mkKeyedEndpointOpt = name: addr: port: keyloc:
     (mkEndpointOpt name addr port) // {
       keys = mkOption {
-        type = types.str;
-        default = "";
+        type = with types; nullOr str;
+        default = keyloc;
         description = ''
           File to persist ${lib.toUpper name} keys.
         '';
       };
-    };
-
-  commonTunOpts = let
-    i2cpOpts = {
-      length = mkOption {
-        type = types.int;
-        description = "Guaranteed minimum hops.";
-        default = 3;
+      inbound = i2cpOpts name;
+      outbound = i2cpOpts name;
+      latency.min = mkOption {
+        type = with types; nullOr int;
+        description = "Min latency for tunnels.";
+        default = null;
       };
-      quantity = mkOption {
-        type = types.int;
-        description = "Number of simultaneous tunnels.";
-        default = 5;
+      latency.max = mkOption {
+        type = with types; nullOr int;
+        description = "Max latency for tunnels.";
+        default = null;
       };
     };
-  in name: {
-    outbound = i2cpOpts;
-    inbound = i2cpOpts;
+
+  commonTunOpts = name: {
+    outbound = i2cpOpts name;
+    inbound = i2cpOpts name;
     crypto.tagsToSend = mkOption {
       type = types.int;
       description = "Number of ElGamal/AES tags to send.";
@@ -70,94 +93,142 @@ let
     };
   } // mkEndpointOpt name "127.0.0.1" 0;
 
-  i2pdConf = pkgs.writeText "i2pd.conf" ''
-    # DO NOT EDIT -- this file has been generated automatically.
-    loglevel = ${cfg.logLevel}
-
-    ipv4 = ${boolToString cfg.enableIPv4}
-    ipv6 = ${boolToString cfg.enableIPv6}
-    notransit = ${boolToString cfg.notransit}
-    floodfill = ${boolToString cfg.floodfill}
-    netid = ${toString cfg.netid}
-    ${if isNull cfg.bandwidth then "" else "bandwidth = ${toString cfg.bandwidth}" }
-    ${if isNull cfg.port then "" else "port = ${toString cfg.port}"}
-
-    [limits]
-    transittunnels = ${toString cfg.limits.transittunnels}
-
-    [upnp]
-    enabled = ${boolToString cfg.upnp.enable}
-    name = ${cfg.upnp.name}
-
-    [precomputation]
-    elgamal = ${boolToString cfg.precomputation.elgamal}
-
-    [reseed]
-    verify = ${boolToString cfg.reseed.verify}
-    file = ${cfg.reseed.file}
-    urls = ${builtins.concatStringsSep "," cfg.reseed.urls}
-
-    [addressbook]
-    defaulturl = ${cfg.addressbook.defaulturl}
-    subscriptions = ${builtins.concatStringsSep "," cfg.addressbook.subscriptions}
-
-    ${flip concatMapStrings
+  sec = name: "\n[" + name + "]";
+  notice = "# DO NOT EDIT -- this file has been generated automatically.";
+  i2pdConf = let
+    opts = [
+      notice
+      (strOpt "loglevel" cfg.logLevel)
+      (boolOpt "logclftime" cfg.logCLFTime)
+      (boolOpt "ipv4" cfg.enableIPv4)
+      (boolOpt "ipv6" cfg.enableIPv6)
+      (boolOpt "notransit" cfg.notransit)
+      (boolOpt "floodfill" cfg.floodfill)
+      (intOpt "netid" cfg.netid)
+    ] ++ (optionalNullInt "bandwidth" cfg.bandwidth)
+      ++ (optionalNullInt "port" cfg.port)
+      ++ (optionalNullString "family" cfg.family)
+      ++ (optionalNullString "datadir" cfg.dataDir)
+      ++ (optionalNullInt "share" cfg.share)
+      ++ (optionalNullBool "ssu" cfg.ssu)
+      ++ (optionalNullBool "ntcp" cfg.ntcp)
+      ++ (optionalNullString "ntcpproxy" cfg.ntcpProxy)
+      ++ (optionalNullString "ifname" cfg.ifname)
+      ++ (optionalNullString "ifname4" cfg.ifname4)
+      ++ (optionalNullString "ifname6" cfg.ifname6)
+      ++ [
+      (sec "limits")
+      (intOpt "transittunnels" cfg.limits.transittunnels)
+      (intOpt "coresize" cfg.limits.coreSize)
+      (intOpt "openfiles" cfg.limits.openFiles)
+      (intOpt "ntcphard" cfg.limits.ntcpHard)
+      (intOpt "ntcpsoft" cfg.limits.ntcpSoft)
+      (intOpt "ntcpthreads" cfg.limits.ntcpThreads)
+      (sec "upnp")
+      (boolOpt "enabled" cfg.upnp.enable)
+      (sec "precomputation")
+      (boolOpt "elgamal" cfg.precomputation.elgamal)
+      (sec "reseed")
+      (boolOpt "verify" cfg.reseed.verify)
+    ] ++ (optionalNullString "file" cfg.reseed.file)
+      ++ (optionalEmptyList "urls" cfg.reseed.urls)
+      ++ (optionalNullString "floodfill" cfg.reseed.floodfill)
+      ++ (optionalNullString "zipfile" cfg.reseed.zipfile)
+      ++ (optionalNullString "proxy" cfg.reseed.proxy)
+      ++ [
+      (sec "trust")
+      (boolOpt "enabled" cfg.trust.enable)
+      (boolOpt "hidden" cfg.trust.hidden)
+    ] ++ (optionalEmptyList "routers" cfg.trust.routers)
+      ++ (optionalNullString "family" cfg.trust.family)
+      ++ [
+      (sec "websockets")
+      (boolOpt "enabled" cfg.websocket.enable)
+      (strOpt "address" cfg.websocket.address)
+      (intOpt "port" cfg.websocket.port)
+      (sec "exploratory")
+      (intOpt "inbound.length" cfg.exploratory.inbound.length)
+      (intOpt "inbound.quantity" cfg.exploratory.inbound.quantity)
+      (intOpt "outbound.length" cfg.exploratory.outbound.length)
+      (intOpt "outbound.quantity" cfg.exploratory.outbound.quantity)
+      (sec "ntcp2")
+      (boolOpt "enabled" cfg.ntcp2.enable)
+      (boolOpt "published" cfg.ntcp2.published)
+      (intOpt "port" cfg.ntcp2.port)
+      (sec "addressbook")
+      (strOpt "defaulturl" cfg.addressbook.defaulturl)
+    ] ++ (optionalEmptyList "subscriptions" cfg.addressbook.subscriptions)
+      ++ (flip map
       (collect (proto: proto ? port && proto ? address && proto ? name) cfg.proto)
-      (proto: ''
-        [${proto.name}]
-        enabled = ${boolToString proto.enable}
-        address = ${proto.address}
-        port = ${toString proto.port}
-        ${if proto ? keys then "keys = ${proto.keys}" else ""}
-        ${if proto ? auth then "auth = ${boolToString proto.auth}" else ""}
-        ${if proto ? user then "user = ${proto.user}" else ""}
-        ${if proto ? pass then "pass = ${proto.pass}" else ""}
-        ${if proto ? outproxy then "outproxy = ${proto.outproxy}" else ""}
-        ${if proto ? outproxyPort then "outproxyport = ${toString proto.outproxyPort}" else ""}
-      '')
-    }
-  '';
-
-  i2pdTunnelConf = pkgs.writeText "i2pd-tunnels.conf" ''
-    # DO NOT EDIT -- this file has been generated automatically.
-    ${flip concatMapStrings
+      (proto: let protoOpts = [
+        (sec proto.name)
+        (boolOpt "enabled" proto.enable)
+        (strOpt "address" proto.address)
+        (intOpt "port" proto.port)
+        ] ++ (if proto ? keys then optionalNullString "keys" proto.keys else [])
+        ++ (if proto ? auth then optionalNullBool "auth" proto.auth else [])
+        ++ (if proto ? user then optionalNullString "user" proto.user else [])
+        ++ (if proto ? pass then optionalNullString "pass" proto.pass else [])
+        ++ (if proto ? strictHeaders then optionalNullBool "strictheaders" proto.strictHeaders else [])
+        ++ (if proto ? hostname then optionalNullString "hostname" proto.hostname else [])
+        ++ (if proto ? outproxy then optionalNullString "outproxy" proto.outproxy else [])
+        ++ (if proto ? outproxyPort then optionalNullInt "outproxyport" proto.outproxyPort else [])
+        ++ (if proto ? outproxyEnable then optionalNullBool "outproxy.enabled" proto.outproxyEnable else []);
+        in (concatStringsSep "\n" protoOpts)
+      ));
+  in
+    pkgs.writeText "i2pd.conf" (concatStringsSep "\n" opts);
+
+  tunnelConf = let opts = [
+    notice
+    (flip map
       (collect (tun: tun ? port && tun ? destination) cfg.outTunnels)
-      (tun: ''
-        [${tun.name}]
-        type = client
-        destination = ${tun.destination}
-        destinationport = ${toString tun.destinationPort}
-        keys = ${tun.keys}
-        address = ${tun.address}
-        port = ${toString tun.port}
-        inbound.length = ${toString tun.inbound.length}
-        outbound.length = ${toString tun.outbound.length}
-        inbound.quantity = ${toString tun.inbound.quantity}
-        outbound.quantity = ${toString tun.outbound.quantity}
-        crypto.tagsToSend = ${toString tun.crypto.tagsToSend}
-      '')
-    }
-    ${flip concatMapStrings
+      (tun: let outTunOpts = [
+        (sec tun.name)
+        "type = client"
+        (intOpt "port" tun.port)
+        (strOpt "destination" tun.destination)
+        ] ++ (if tun ? destinationPort then optionalNullInt "destinationport" tun.destinationPort else [])
+        ++ (if tun ? keys then
+            optionalNullString "keys" tun.keys else [])
+        ++ (if tun ? address then
+            optionalNullString "address" tun.address else [])
+        ++ (if tun ? inbound.length then
+            optionalNullInt "inbound.length" tun.inbound.length else [])
+        ++ (if tun ? inbound.quantity then
+            optionalNullInt "inbound.quantity" tun.inbound.quantity else [])
+        ++ (if tun ? outbound.length then
+            optionalNullInt "outbound.length" tun.outbound.length else [])
+        ++ (if tun ? outbound.quantity then
+            optionalNullInt "outbound.quantity" tun.outbound.quantity else [])
+        ++ (if tun ? crypto.tagsToSend then
+            optionalNullInt "crypto.tagstosend" tun.crypto.tagsToSend else []);
+        in concatStringsSep "\n" outTunOpts))
+    (flip map
       (collect (tun: tun ? port && tun ? address) cfg.inTunnels)
-      (tun: ''
-        [${tun.name}]
-        type = server
-        destination = ${tun.destination}
-        keys = ${tun.keys}
-        host = ${tun.address}
-        port = ${toString tun.port}
-        inport = ${toString tun.inPort}
-        accesslist = ${builtins.concatStringsSep "," tun.accessList}
-      '')
-    }
-  '';
+      (tun: let inTunOpts = [
+        (sec tun.name)
+        "type = server"
+        (intOpt "port" tun.port)
+        (strOpt "host" tun.address)
+      ] ++ (if tun ? destination then
+            optionalNullString "destination" tun.destination else [])
+        ++ (if tun ? keys then
+            optionalNullString "keys" tun.keys else [])
+        ++ (if tun ? inPort then
+            optionalNullInt "inport" tun.inPort else [])
+        ++ (if tun ? accessList then
+            optionalEmptyList "accesslist" tun.accessList else []);
+        in concatStringsSep "\n" inTunOpts))];
+    in pkgs.writeText "i2pd-tunnels.conf" opts;
 
   i2pdSh = pkgs.writeScriptBin "i2pd" ''
     #!/bin/sh
     exec ${pkgs.i2pd}/bin/i2pd \
       ${if isNull cfg.address then "" else "--host="+cfg.address} \
+      --service \
       --conf=${i2pdConf} \
-      --tunconf=${i2pdTunnelConf}
+      --tunconf=${tunnelConf}
   '';
 
 in
@@ -170,9 +241,7 @@ in
 
     services.i2pd = {
 
-      enable = mkOption {
-        type = types.bool;
-        default = false;
+      enable = mkEnableOption "I2Pd daemon" // {
         description = ''
           Enables I2Pd as a running service upon activation.
           Please read http://i2pd.readthedocs.io/en/latest/ for further
@@ -192,6 +261,8 @@ in
         '';
       };
 
+      logCLFTime = mkEnableOption "Full CLF-formatted date and time to log";
+
       address = mkOption {
         type = with types; nullOr str;
         default = null;
@@ -200,17 +271,72 @@ in
         '';
       };
 
-      notransit = mkOption {
-        type = types.bool;
-        default = false;
+      family = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Specify a family the router belongs to.
+        '';
+      };
+
+      dataDir = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Alternative path to storage of i2pd data (RI, keys, peer profiles, ...)
+        '';
+      };
+
+      share = mkOption {
+        type = types.int;
+        default = 100;
+        description = ''
+          Limit of transit traffic from max bandwidth in percents.
+        '';
+      };
+
+      ifname = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Network interface to bind to.
+        '';
+      };
+
+      ifname4 = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          IPv4 interface to bind to.
+        '';
+      };
+
+      ifname6 = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          IPv6 interface to bind to.
+        '';
+      };
+
+      ntcpProxy = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Proxy URL for NTCP transport.
+        '';
+      };
+
+      ntcp = mkEnableTrueOption "ntcp";
+      ssu = mkEnableTrueOption "ssu";
+
+      notransit = mkEnableOption "notransit" // {
         description = ''
           Tells the router to not accept transit tunnels during startup.
         '';
       };
 
-      floodfill = mkOption {
-        type = types.bool;
-        default = false;
+      floodfill = mkEnableOption "floodfill" // {
         description = ''
           If the router is declared to be unreachable and needs introduction nodes.
         '';
@@ -241,131 +367,178 @@ in
         '';
       };
 
-      enableIPv4 = mkOption {
-        type = types.bool;
-        default = true;
+      enableIPv4 = mkEnableTrueOption "IPv4 connectivity";
+      enableIPv6 = mkEnableOption "IPv6 connectivity";
+      nat = mkEnableTrueOption "NAT bypass";
+
+      upnp.enable = mkEnableOption "UPnP service discovery";
+      upnp.name = mkOption {
+        type = types.str;
+        default = "I2Pd";
         description = ''
-          Enables IPv4 connectivity. Enabled by default.
+          Name i2pd appears in UPnP forwardings list.
         '';
       };
 
-      enableIPv6 = mkOption {
-        type = types.bool;
-        default = false;
+      precomputation.elgamal = mkEnableTrueOption "Precomputed ElGamal tables" // {
         description = ''
-          Enables IPv6 connectivity. Disabled by default.
+          Whenever to use precomputated tables for ElGamal.
+          <command>i2pd</command> defaults to <literal>false</literal>
+          to save 64M of memory (and looses some performance).
+
+          We default to <literal>true</literal> as that is what most
+          users want anyway.
         '';
       };
 
-      nat = mkOption {
-        type = types.bool;
-        default = true;
+      reseed.verify = mkEnableOption "SU3 signature verification";
+
+      reseed.file = mkOption {
+        type = with types; nullOr str;
+        default = null;
         description = ''
-          Assume router is NATed. Enabled by default.
+          Full path to SU3 file to reseed from.
         '';
       };
 
-      upnp = {
-        enable = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            Enables UPnP.
-          '';
-        };
+      reseed.urls = mkOption {
+        type = with types; listOf str;
+        default = [];
+        description = ''
+          Reseed URLs.
+        '';
+      };
 
-        name = mkOption {
-          type = types.str;
-          default = "I2Pd";
-          description = ''
-            Name i2pd appears in UPnP forwardings list.
-          '';
-        };
+      reseed.floodfill = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Path to router info of floodfill to reseed from.
+        '';
       };
 
-      precomputation.elgamal = mkOption {
-        type = types.bool;
-        default = true;
+      reseed.zipfile = mkOption {
+        type = with types; nullOr str;
+        default = null;
         description = ''
-          Whenever to use precomputated tables for ElGamal.
-          <command>i2pd</command> defaults to <literal>false</literal>
-          to save 64M of memory (and looses some performance).
+          Path to local .zip file to reseed from.
+        '';
+      };
 
-          We default to <literal>true</literal> as that is what most
-          users want anyway.
+      reseed.proxy = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          URL for reseed proxy, supports http/socks.
         '';
       };
 
-      reseed = {
-        verify = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            Request SU3 signature verification
-          '';
-        };
+     addressbook.defaulturl = mkOption {
+        type = types.str;
+        default = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt";
+        description = ''
+          AddressBook subscription URL for initial setup
+        '';
+      };
+     addressbook.subscriptions = mkOption {
+        type = with types; listOf str;
+        default = [
+          "http://inr.i2p/export/alive-hosts.txt"
+          "http://i2p-projekt.i2p/hosts.txt"
+          "http://stats.i2p/cgi-bin/newhosts.txt"
+        ];
+        description = ''
+          AddressBook subscription URLs
+        '';
+      };
 
-        file = mkOption {
-          type = types.str;
-          default = "";
-          description = ''
-            Full path to SU3 file to reseed from
-          '';
-        };
+      trust.enable = mkEnableOption "Explicit trust options";
 
-        urls = mkOption {
-          type = with types; listOf str;
-          default = [
-            "https://reseed.i2p-project.de/"
-            "https://i2p.mooo.com/netDb/"
-            "https://netdb.i2p2.no/"
-            "https://us.reseed.i2p2.no:444/"
-            "https://uk.reseed.i2p2.no:444/"
-            "https://i2p.manas.ca:8443/"
-          ];
-          description = ''
-            Reseed URLs
-          '';
-        };
+      trust.family = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Router Familiy to trust for first hops.
+        '';
       };
 
-      addressbook = {
-       defaulturl = mkOption {
-          type = types.str;
-          default = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt";
-          description = ''
-            AddressBook subscription URL for initial setup
-          '';
-        };
-       subscriptions = mkOption {
-          type = with types; listOf str;
-          default = [
-            "http://inr.i2p/export/alive-hosts.txt"
-            "http://i2p-projekt.i2p/hosts.txt"
-            "http://stats.i2p/cgi-bin/newhosts.txt"
-          ];
-          description = ''
-            AddressBook subscription URLs
-          '';
-        };
+      trust.routers = mkOption {
+        type = with types; listOf str;
+        default = [];
+        description = ''
+          Only connect to the listed routers.
+        '';
+      };
+
+      trust.hidden = mkEnableOption "Router concealment.";
+
+      websocket = mkEndpointOpt "websockets" "127.0.0.1" 7666;
+
+      exploratory.inbound = i2cpOpts "exploratory";
+      exploratory.outbound = i2cpOpts "exploratory";
+
+      ntcp2.enable = mkEnableTrueOption "NTCP2.";
+      ntcp2.published = mkEnableOption "NTCP2 publication.";
+      ntcp2.port = mkOption {
+        type = types.int;
+        default = 0;
+        description = ''
+          Port to listen for incoming NTCP2 connections (0=auto).
+        '';
       };
 
       limits.transittunnels = mkOption {
         type = types.int;
         default = 2500;
         description = ''
-          Maximum number of active transit sessions
+          Maximum number of active transit sessions.
+        '';
+      };
+
+      limits.coreSize = mkOption {
+        type = types.int;
+        default = 0;
+        description = ''
+          Maximum size of corefile in Kb (0 - use system limit).
+        '';
+      };
+
+      limits.openFiles = mkOption {
+        type = types.int;
+        default = 0;
+        description = ''
+          Maximum number of open files (0 - use system default).
+        '';
+      };
+
+      limits.ntcpHard = mkOption {
+        type = types.int;
+        default = 0;
+        description = ''
+          Maximum number of active transit sessions.
+        '';
+      };
+
+      limits.ntcpSoft = mkOption {
+        type = types.int;
+        default = 0;
+        description = ''
+          Threshold to start probabalistic backoff with ntcp sessions (default: use system limit).
+        '';
+      };
+
+      limits.ntcpThreads = mkOption {
+        type = types.int;
+        default = 1;
+        description = ''
+          Maximum number of threads used by NTCP DH worker.
         '';
       };
 
       proto.http = (mkEndpointOpt "http" "127.0.0.1" 7070) // {
-        auth = mkOption {
-          type = types.bool;
-          default = false;
-          description = ''
-            Enable authentication for webconsole.
-          '';
-        };
+
+        auth = mkEnableOption "Webconsole authentication";
+
         user = mkOption {
           type = types.str;
           default = "i2pd";
@@ -373,6 +546,7 @@ in
             Username for webconsole access
           '';
         };
+
         pass = mkOption {
           type = types.str;
           default = "i2pd";
@@ -380,11 +554,35 @@ in
             Password for webconsole access.
           '';
         };
+
+        strictHeaders = mkOption {
+          type = with types; nullOr bool;
+          default = null;
+          description = ''
+            Enable strict host checking on WebUI.
+          '';
+        };
+
+        hostname = mkOption {
+          type = with types; nullOr str;
+          default = null;
+          description = ''
+            Expected hostname for WebUI.
+          '';
+        };
       };
 
-      proto.httpProxy = mkKeyedEndpointOpt "httpproxy" "127.0.0.1" 4444 "";
-      proto.socksProxy = (mkKeyedEndpointOpt "socksproxy" "127.0.0.1" 4447 "")
+      proto.httpProxy = (mkKeyedEndpointOpt "httpproxy" "127.0.0.1" 4444 "httpproxy-keys.dat")
+      // {
+        outproxy = mkOption {
+          type = with types; nullOr str;
+          default = null;
+          description = "Upstream outproxy bind address.";
+        };
+      };
+      proto.socksProxy = (mkKeyedEndpointOpt "socksproxy" "127.0.0.1" 4447 "socksproxy-keys.dat")
       // {
+        outproxyEnable = mkEnableOption "SOCKS outproxy";
         outproxy = mkOption {
           type = types.str;
           default = "127.0.0.1";
@@ -408,8 +606,8 @@ in
           { name, ... }: {
             options = {
               destinationPort = mkOption {
-                type = types.int;
-                default = 0;
+                type = with types; nullOr int;
+                default = null;
                 description = "Connect to particular port at destination.";
               };
             } // commonTunOpts name;
diff --git a/pkgs/applications/networking/instant-messengers/nheko/default.nix b/pkgs/applications/networking/instant-messengers/nheko/default.nix
index cf9558b4b955..6716305df8b5 100644
--- a/pkgs/applications/networking/instant-messengers/nheko/default.nix
+++ b/pkgs/applications/networking/instant-messengers/nheko/default.nix
@@ -1,39 +1,9 @@
-{
-  lib, stdenv, fetchFromGitHub, fetchurl,
-  cmake, doxygen, lmdb, qt5, qtmacextras
+{ lib, stdenv, fetchFromGitHub, fetchurl
+, cmake, lmdb, qt5, qtmacextras, mtxclient
+, boost, spdlog, olm, pkgconfig
 }:
 
 let
-  json_hpp = fetchurl {
-    url = https://github.com/nlohmann/json/releases/download/v3.1.2/json.hpp;
-    sha256 = "fbdfec4b4cf63b3b565d09f87e6c3c183bdd45c5be1864d3fcb338f6f02c1733";
-  };
-
-  variant_hpp = fetchurl {
-    url = https://github.com/mpark/variant/releases/download/v1.3.0/variant.hpp;
-    sha256 = "1vjiz1x5l8ynqqyb5l9mlrzgps526v45hbmwjilv4brgyi5445fq";
-  };
-
-  matrix-structs = stdenv.mkDerivation rec {
-    name = "matrix-structs-git";
-
-    src = fetchFromGitHub {
-      owner = "mujx";
-      repo = "matrix-structs";
-      rev = "5e57c2385a79b6629d1998fec4a7c0baee23555e";
-      sha256 = "112b7gnvr04g1ak7fnc7ch7w2n825j4qkw0jb49xx06ag93nb6m6";
-    };
-
-    postUnpack = ''
-      cp ${json_hpp} "$sourceRoot/include/json.hpp"
-      cp ${variant_hpp} "$sourceRoot/include/variant.hpp"
-    '';
-
-    patches = [ ./fetchurls.patch ];
-
-    nativeBuildInputs = [ cmake doxygen ];
-  };
-
   tweeny = fetchFromGitHub {
     owner = "mobius3";
     repo = "tweeny";
@@ -50,19 +20,15 @@ let
 in
 stdenv.mkDerivation rec {
   name = "nheko-${version}";
-  version = "0.4.3";
+  version = "0.5.5";
 
   src = fetchFromGitHub {
     owner = "mujx";
     repo = "nheko";
     rev = "v${version}";
-    sha256 = "0qjia42nam3hj835k2jb5b6j6n56rdkb8rn67yqf45xdz8ypmbmv";
+    sha256 = "0k5gmfwmisfavliyz0nfsmwy317ps8a4r3l1d831giqp9pvqvi0i";
   };
 
-  # This patch is likely not strictly speaking needed, but will help detect when
-  # a dependency is updated, so that the fetches up there can be updated too
-  patches = [ ./external-deps.patch ];
-
   # If, on Darwin, you encounter the error
   #   error: must specify at least one argument for '...' parameter of variadic
   #   macro [-Werror,-Wgnu-zero-variadic-macro-arguments]
@@ -79,25 +45,30 @@ stdenv.mkDerivation rec {
   #  export CFLAGS=-Wno-error=gnu-zero-variadic-macro-arguments
   #'';
 
+  postPatch = ''
+    mkdir -p .deps/include/
+    ln -s ${tweeny}/include .deps/include/tweeny
+    ln -s ${spdlog} .deps/spdlog
+  '';
+
   cmakeFlags = [
-    "-DMATRIX_STRUCTS_LIBRARY=${matrix-structs}/lib/static/libmatrix_structs.a"
-    "-DMATRIX_STRUCTS_INCLUDE_DIR=${matrix-structs}/include/matrix_structs"
-    "-DTWEENY_INCLUDE_DIR=${tweeny}/include"
+    "-DTWEENY_INCLUDE_DIR=.deps/include"
     "-DLMDBXX_INCLUDE_DIR=${lmdbxx}"
   ];
 
-  nativeBuildInputs = [ cmake ];
+  nativeBuildInputs = [ cmake pkgconfig ];
 
   buildInputs = [
-    lmdb lmdbxx matrix-structs qt5.qtbase qt5.qtmultimedia qt5.qttools tweeny
+    mtxclient olm boost lmdb spdlog
+    qt5.qtbase qt5.qtmultimedia qt5.qttools
   ] ++ lib.optional stdenv.isDarwin qtmacextras;
 
   enableParallelBuilding = true;
 
   meta = with stdenv.lib; {
     description = "Desktop client for the Matrix protocol";
-    maintainers = with maintainers; [ ekleog ];
-    platforms = platforms.all;
+    maintainers = with maintainers; [ ekleog fpletz ];
+    platforms = platforms.unix;
     license = licenses.gpl3Plus;
   };
 }
diff --git a/pkgs/applications/networking/instant-messengers/nheko/external-deps.patch b/pkgs/applications/networking/instant-messengers/nheko/external-deps.patch
deleted file mode 100644
index fa388edfb75a..000000000000
--- a/pkgs/applications/networking/instant-messengers/nheko/external-deps.patch
+++ /dev/null
@@ -1,94 +0,0 @@
-diff --git a/cmake/LMDBXX.cmake b/cmake/LMDBXX.cmake
-index 3b9817d..e69de29 100644
---- a/cmake/LMDBXX.cmake
-+++ b/cmake/LMDBXX.cmake
-@@ -1,23 +0,0 @@
--include(ExternalProject)
--
--#
--# Build lmdbxx.
--#
--
--set(THIRD_PARTY_ROOT ${CMAKE_SOURCE_DIR}/.third-party)
--set(LMDBXX_ROOT ${THIRD_PARTY_ROOT}/lmdbxx)
--
--set(LMDBXX_INCLUDE_DIR ${LMDBXX_ROOT})
--
--ExternalProject_Add(
--  lmdbxx
--
--  GIT_REPOSITORY https://github.com/bendiken/lmdbxx
--  GIT_TAG 0b43ca87d8cfabba392dfe884eb1edb83874de02
--
--  BUILD_IN_SOURCE 1
--  SOURCE_DIR ${LMDBXX_ROOT}
--  CONFIGURE_COMMAND ""
--  BUILD_COMMAND ""
--  INSTALL_COMMAND ""
--)
-diff --git a/cmake/MatrixStructs.cmake b/cmake/MatrixStructs.cmake
-index cef00f6..e69de29 100644
---- a/cmake/MatrixStructs.cmake
-+++ b/cmake/MatrixStructs.cmake
-@@ -1,33 +0,0 @@
--include(ExternalProject)
--
--#
--# Build matrix-structs.
--#
--
--set(THIRD_PARTY_ROOT ${CMAKE_SOURCE_DIR}/.third-party)
--set(MATRIX_STRUCTS_ROOT ${THIRD_PARTY_ROOT}/matrix_structs)
--set(MATRIX_STRUCTS_INCLUDE_DIR ${MATRIX_STRUCTS_ROOT}/include)
--set(MATRIX_STRUCTS_LIBRARY matrix_structs)
--
--link_directories(${MATRIX_STRUCTS_ROOT})
--
--set(WINDOWS_FLAGS "")
--
--if(MSVC)
--    set(WINDOWS_FLAGS "-DCMAKE_GENERATOR_PLATFORM=x64")
--endif()
--
--ExternalProject_Add(
--  MatrixStructs
--
--  GIT_REPOSITORY https://github.com/mujx/matrix-structs
--  GIT_TAG 5e57c2385a79b6629d1998fec4a7c0baee23555e
--
--  BUILD_IN_SOURCE 1
--  SOURCE_DIR ${MATRIX_STRUCTS_ROOT}
--  CONFIGURE_COMMAND ${CMAKE_COMMAND}
--    -DCMAKE_BUILD_TYPE=Release ${MATRIX_STRUCTS_ROOT}
--    ${WINDOWS_FLAGS}
--  BUILD_COMMAND ${CMAKE_COMMAND} --build ${MATRIX_STRUCTS_ROOT} --config Release
--  INSTALL_COMMAND ""
--)
-diff --git a/cmake/Tweeny.cmake b/cmake/Tweeny.cmake
-index 537ac92..e69de29 100644
---- a/cmake/Tweeny.cmake
-+++ b/cmake/Tweeny.cmake
-@@ -1,23 +0,0 @@
--include(ExternalProject)
--
--#
--# Build tweeny
--#
--
--set(THIRD_PARTY_ROOT ${CMAKE_SOURCE_DIR}/.third-party)
--set(TWEENY_ROOT ${THIRD_PARTY_ROOT}/tweeny)
--
--set(TWEENY_INCLUDE_DIR ${TWEENY_ROOT}/include)
--
--ExternalProject_Add(
--  Tweeny
--
--  GIT_REPOSITORY https://github.com/mobius3/tweeny
--  GIT_TAG b94ce07cfb02a0eb8ac8aaf66137dabdaea857cf
--
--  BUILD_IN_SOURCE 1
--  SOURCE_DIR ${TWEENY_ROOT}
--  CONFIGURE_COMMAND ""
--  BUILD_COMMAND ""
--  INSTALL_COMMAND ""
--)
diff --git a/pkgs/applications/networking/instant-messengers/nheko/fetchurls.patch b/pkgs/applications/networking/instant-messengers/nheko/fetchurls.patch
deleted file mode 100644
index e2f72f600ed8..000000000000
--- a/pkgs/applications/networking/instant-messengers/nheko/fetchurls.patch
+++ /dev/null
@@ -1,21 +0,0 @@
-diff --git a/CMakeLists.txt b/CMakeLists.txt
-index 077ac37..c639d71 100644
---- a/CMakeLists.txt
-+++ b/CMakeLists.txt
-@@ -18,16 +18,6 @@ include(Doxygen)
- #
- include(CompilerFlags)
- 
--file(DOWNLOAD
--    "https://github.com/nlohmann/json/releases/download/v3.1.2/json.hpp"
--    ${PROJECT_SOURCE_DIR}/include/json.hpp
--    EXPECTED_HASH SHA256=fbdfec4b4cf63b3b565d09f87e6c3c183bdd45c5be1864d3fcb338f6f02c1733)
--
--file(DOWNLOAD
--    "https://github.com/mpark/variant/releases/download/v1.3.0/variant.hpp"
--    ${PROJECT_SOURCE_DIR}/include/variant.hpp
--    EXPECTED_MD5 "be0ce322cdd408e1b347b9f1d59ea67a")
--
- include_directories(include)
- 
- set(SRC
diff --git a/pkgs/data/misc/osinfo-db/default.nix b/pkgs/data/misc/osinfo-db/default.nix
index 9919fb57f7cc..93ee6d38c7c3 100644
--- a/pkgs/data/misc/osinfo-db/default.nix
+++ b/pkgs/data/misc/osinfo-db/default.nix
@@ -1,11 +1,11 @@
 { stdenv, fetchurl, osinfo-db-tools, intltool, libxml2 }:
 
 stdenv.mkDerivation rec {
-  name = "osinfo-db-20180531";
+  name = "osinfo-db-20180903";
 
   src = fetchurl {
     url = "https://releases.pagure.org/libosinfo/${name}.tar.xz";
-    sha256 = "0vw6hn7xdfj0q7wc3k9b0nvbghdp1b9dl63xz2v7frr55qv59m5x";
+    sha256 = "0xkxqyn2b03d4rd91f5rw3xar5vnv2n8l5pp8sm3hqm1wm5z5my9";
   };
 
   nativeBuildInputs = [ osinfo-db-tools intltool libxml2 ];
diff --git a/pkgs/development/interpreters/racket/default.nix b/pkgs/development/interpreters/racket/default.nix
index e8b6cc93c2c1..1f2a28cb8fb8 100644
--- a/pkgs/development/interpreters/racket/default.nix
+++ b/pkgs/development/interpreters/racket/default.nix
@@ -36,7 +36,7 @@ in
 
 stdenv.mkDerivation rec {
   name = "racket-${version}";
-  version = "7.0";
+  version = "7.0"; # always change at once with ./minimal.nix
 
   src = (stdenv.lib.makeOverridable ({ name, sha256 }:
     fetchurl rec {
diff --git a/pkgs/development/libraries/mtxclient/default.nix b/pkgs/development/libraries/mtxclient/default.nix
new file mode 100644
index 000000000000..465a70576356
--- /dev/null
+++ b/pkgs/development/libraries/mtxclient/default.nix
@@ -0,0 +1,31 @@
+{ stdenv, fetchFromGitHub, cmake, pkgconfig
+, boost, openssl, zlib, libsodium, olm, gtest, spdlog, nlohmann_json }:
+
+stdenv.mkDerivation rec {
+  name = "mtxclient-${version}";
+  version = "0.1.0";
+
+  src = fetchFromGitHub {
+    owner = "mujx";
+    repo = "mtxclient";
+    rev = "v${version}";
+    sha256 = "0i58y45diysayjzy5ick15356972z67dfxm0w41ay88nm42x1imp";
+  };
+
+  postPatch = ''
+    ln -s ${nlohmann_json}/include/nlohmann/json.hpp include/json.hpp
+  '';
+
+  cmakeFlags = [ "-DBUILD_LIB_TESTS=OFF" "-DBUILD_LIB_EXAMPLES=OFF" ];
+
+  nativeBuildInputs = [ cmake pkgconfig ];
+  buildInputs = [ boost openssl zlib libsodium olm ];
+
+  meta = with stdenv.lib; {
+    description = "Client API library for Matrix, built on top of Boost.Asio";
+    homepage = https://github.com/mujx/mtxclient;
+    license = licenses.mit;
+    maintainers = with maintainers; [ fpletz ];
+    platforms = platforms.unix;
+  };
+}
diff --git a/pkgs/development/libraries/spdlog/default.nix b/pkgs/development/libraries/spdlog/default.nix
index 1c9e67f87675..a96cd455f554 100644
--- a/pkgs/development/libraries/spdlog/default.nix
+++ b/pkgs/development/libraries/spdlog/default.nix
@@ -1,32 +1,46 @@
 { stdenv, fetchFromGitHub, cmake }:
 
-stdenv.mkDerivation rec {
-  name = "spdlog-${version}";
-  version = "0.14.0";
-
-  src = fetchFromGitHub {
-    owner  = "gabime";
-    repo   = "spdlog";
-    rev    = "v${version}";
-    sha256 = "13730429gwlabi432ilpnja3sfvy0nn2719vnhhmii34xcdyc57q";
-  };
+let
+  generic = { version, sha256 }:
+    stdenv.mkDerivation {
+      name = "spdlog-${version}";
+      inherit version;
+
+      src = fetchFromGitHub {
+        owner  = "gabime";
+        repo   = "spdlog";
+        rev    = "v${version}";
+        inherit sha256;
+      };
 
-  nativeBuildInputs = [ cmake ];
+      nativeBuildInputs = [ cmake ];
 
-  # cmakeFlags = [ "-DSPDLOG_BUILD_EXAMPLES=ON" ];
+      # cmakeFlags = [ "-DSPDLOG_BUILD_EXAMPLES=ON" ];
 
-  outputs = [ "out" "doc" ];
+      outputs = [ "out" "doc" ];
 
-  postInstall = ''
-    mkdir -p $out/share/doc/spdlog
-    cp -rv ../example $out/share/doc/spdlog
-  '';
+      postInstall = ''
+        mkdir -p $out/share/doc/spdlog
+        cp -rv ../example $out/share/doc/spdlog
+      '';
 
-  meta = with stdenv.lib; {
-    description    = "Very fast, header only, C++ logging library.";
-    homepage       = https://github.com/gabime/spdlog;
-    license        = licenses.mit;
-    maintainers    = with maintainers; [ obadz ];
-    platforms      = platforms.all;
+      meta = with stdenv.lib; {
+        description    = "Very fast, header only, C++ logging library.";
+        homepage       = https://github.com/gabime/spdlog;
+        license        = licenses.mit;
+        maintainers    = with maintainers; [ obadz ];
+        platforms      = platforms.all;
+      };
+    };
+in
+{
+  spdlog_1 = generic {
+    version = "1.1.0";
+    sha256 = "0yckz5w02v8193jhxihk9v4i8f6jafyg2a33amql0iclhk17da8f";
+  };
+
+  spdlog_0 = generic {
+    version = "0.14.0";
+    sha256 = "13730429gwlabi432ilpnja3sfvy0nn2719vnhhmii34xcdyc57q";
   };
 }
diff --git a/pkgs/development/python-modules/cozy/default.nix b/pkgs/development/python-modules/cozy/default.nix
index 0feca2773b37..7515891456e9 100644
--- a/pkgs/development/python-modules/cozy/default.nix
+++ b/pkgs/development/python-modules/cozy/default.nix
@@ -1,4 +1,4 @@
-{ buildPythonPackage, fetchFromGitHub, lib,
+{ buildPythonPackage, isPy3k, fetchFromGitHub, lib,
   z3, ply, python-igraph, oset, ordered-set, dictionaries }:
 
 buildPythonPackage {
@@ -29,6 +29,8 @@ buildPythonPackage {
     $out/bin/cozy --help
   '';
 
+  disabled = !isPy3k;
+
   meta = {
     description = "The collection synthesizer";
     homepage = https://cozy.uwplse.org/;
diff --git a/pkgs/development/python-modules/flask-ldap-login/default.nix b/pkgs/development/python-modules/flask-ldap-login/default.nix
index b95e694a232f..99b57dac816f 100644
--- a/pkgs/development/python-modules/flask-ldap-login/default.nix
+++ b/pkgs/development/python-modules/flask-ldap-login/default.nix
@@ -1,16 +1,27 @@
-{ stdenv, buildPythonPackage, fetchPypi
+{ stdenv, buildPythonPackage, isPy3k, fetchFromGitHub, fetchpatch
 , flask, flask_wtf, flask_testing, ldap
 , mock, nose }:
 
 buildPythonPackage rec {
   pname = "flask-ldap-login";
-  version = "0.3.0";
+  version = "0.3.4";
+  disabled = isPy3k;
 
-  src = fetchPypi {
-    inherit pname version;
-    sha256 = "085rik7q8xrp5g95346p6jcp9m2yr8kamwb2kbiw4q0b0fpnnlgq";
+  src = fetchFromGitHub {
+    owner = "ContinuumIO";
+    repo = "flask-ldap-login";
+    rev = version;
+    sha256 = "1l6zahqhwn5g9fmhlvjv80288b5h2fk5mssp7amdkw5ysk570wzp";
   };
 
+  patches = [
+    # Fix flask_wtf>=0.9.0 incompatibility. See https://github.com/ContinuumIO/flask-ldap-login/issues/41
+    (fetchpatch {
+      url = https://github.com/ContinuumIO/flask-ldap-login/commit/ed08c03c818dc63b97b01e2e7c56862eaa6daa43.patch;
+      sha256 = "19pkhbldk8jq6m10kdylvjf1c8m84fvvj04v5qda4cjyks15aq48";
+    })
+  ];
+
   checkInputs = [ nose mock flask_testing ];
   propagatedBuildInputs = [ flask flask_wtf ldap ];
 
diff --git a/pkgs/development/tools/castxml/default.nix b/pkgs/development/tools/castxml/default.nix
index 603b155ee4f9..aea94633bae3 100644
--- a/pkgs/development/tools/castxml/default.nix
+++ b/pkgs/development/tools/castxml/default.nix
@@ -17,6 +17,11 @@ stdenv.mkDerivation rec {
     sha256 = "1hjh8ihjyp1m2jb5yypp5c45bpbz8k004f4p1cjw4gc7pxhjacdj";
   };
 
+  cmakeFlags = [
+    "-DCLANG_RESOURCE_DIR=${llvmPackages.clang-unwrapped}"
+    "-DSPHINX_MAN=${if withMan then "ON" else "OFF"}"
+  ];
+
   buildInputs = [
     cmake
     llvmPackages.clang-unwrapped
@@ -25,11 +30,6 @@ stdenv.mkDerivation rec {
 
   propagatedbuildInputs = [ llvmPackages.libclang ];
 
-  preConfigure = ''
-    cmakeFlagsArray+=(
-     ${if withMan then "-DSPHINX_MAN=ON" else ""}
-  )'';
-
   # 97% tests passed, 96 tests failed out of 2866
   # mostly because it checks command line and nix append -isystem and all
   doCheck=false;
diff --git a/pkgs/games/dwarf-fortress/default.nix b/pkgs/games/dwarf-fortress/default.nix
index aa4ff210812b..88a6d72bc485 100644
--- a/pkgs/games/dwarf-fortress/default.nix
+++ b/pkgs/games/dwarf-fortress/default.nix
@@ -5,67 +5,112 @@
 # This directory menaces with spikes of Nix code. It is terrifying.
 #
 # If this is your first time here, you should probably install the dwarf-fortress-full package,
-# for instance with `environment.systempackages = [ pkgs.dwarf-fortress.dwarf-fortress-full ];`.
+# for instance with:
+#
+# environment.systemPackages = [ pkgs.dwarf-fortress-packages.dwarf-fortress-full ];
 #
 # You can adjust its settings by using override, or compile your own package by
-# using the other packages here. Take a look at lazy-pack.nix to get an idea of
-# how.
+# using the other packages here.
+#
+# For example, you can enable the FPS indicator, disable the intro, pick a
+# theme other than phoebus (the default for dwarf-fortress-full), _and_ use
+# an older version with something like:
+#
+# environment.systemPackages = [
+#   (pkgs.dwarf-fortress-packages.dwarf-fortress-full.override {
+#      dfVersion = "0.44.11";
+#      theme = "cla";
+#      enableIntro = false;
+#      enableFPS = true;
+#   })
+# ]
+#
+# Take a look at lazy-pack.nix to see all the other options.
 #
 # You will find the configuration files in ~/.local/share/df_linux/data/init. If
 # you un-symlink them and edit, then the scripts will avoid overwriting your
 # changes on later launches, but consider extending the wrapper with your
 # desired options instead.
-#
-# Although both dfhack and dwarf therapist are included in the lazy pack, you
-# can only use one at a time. DFHack does have therapist-like features, so this
-# may or may not be a problem.
+
+with lib;
 
 let
   callPackage = pkgs.newScope self;
 
-  df-games = lib.listToAttrs (map (dfVersion: {
-    name = "dwarf-fortress_${lib.replaceStrings ["."] ["_"] dfVersion}";
-    value = callPackage ./wrapper {
-      inherit (self) themes;
-      dwarf-fortress = callPackage ./game.nix { inherit dfVersion; };
-    };
-  }) (lib.attrNames self.df-hashes));
+  # The latest Dwarf Fortress version. Maintainers: when a new version comes
+  # out, ensure that (unfuck|dfhack|twbt) are all up to date before changing
+  # this.
+  latestVersion = "0.44.12";
 
-  self = rec {
-    df-hashes = builtins.fromJSON (builtins.readFile ./game.json);
-    dwarf-fortress = df-games.dwarf-fortress_0_44_12;
+  # Converts a version to a package name.
+  versionToName = version: "dwarf-fortress_${lib.replaceStrings ["."] ["_"] version}";
 
-    dwarf-fortress-full = callPackage ./lazy-pack.nix { };
+  # A map of names to each Dwarf Fortress package we know about.
+  df-games = lib.listToAttrs (map (dfVersion: {
+    name = versionToName dfVersion;
+    value =
+      let
+        # I can't believe this syntax works. Spikes of Nix code indeed...
+        dwarf-fortress = callPackage ./game.nix {
+          inherit dfVersion;
+          inherit dwarf-fortress-unfuck;
+        };
 
-    dfhack = callPackage ./dfhack {
-      inherit (pkgs.perlPackages) XMLLibXML XMLLibXSLT;
-      stdenv = gccStdenv;
-    };
+        # unfuck is linux-only right now, we will only use it there.
+        dwarf-fortress-unfuck = if stdenv.isLinux then callPackage ./unfuck.nix { inherit dfVersion; }
+                                else null;
 
-    soundSense = callPackage ./soundsense.nix { };
+        twbt = callPackage ./twbt { inherit dfVersion; };
 
-    # unfuck is linux-only right now, we will only use it there.
-    dwarf-fortress-unfuck = if stdenv.isLinux then callPackage ./unfuck.nix { }
-                            else null;
+        dfhack = callPackage ./dfhack {
+          inherit (pkgs.perlPackages) XMLLibXML XMLLibXSLT;
+          inherit dfVersion twbt;
+          stdenv = gccStdenv;
+        };
 
-    dwarf-therapist = callPackage ./dwarf-therapist/wrapper.nix {
-      inherit (dwarf-fortress) dwarf-fortress;
-      dwarf-therapist = pkgs.qt5.callPackage ./dwarf-therapist {
-        texlive = pkgs.texlive.combine {
-          inherit (pkgs.texlive) scheme-basic float caption wrapfig adjmulticol sidecap preprint enumitem;
+        dwarf-therapist = callPackage ./dwarf-therapist/wrapper.nix {
+          inherit dwarf-fortress;
+          dwarf-therapist = pkgs.qt5.callPackage ./dwarf-therapist {
+            texlive = pkgs.texlive.combine {
+              inherit (pkgs.texlive) scheme-basic float caption wrapfig adjmulticol sidecap preprint enumitem;
+            };
+          };
         };
+      in
+      callPackage ./wrapper {
+        inherit (self) themes;
+
+        dwarf-fortress = dwarf-fortress;
+        dwarf-fortress-unfuck = dwarf-fortress-unfuck;
+        twbt = twbt;
+        dfhack = dfhack;
+        dwarf-therapist = dwarf-therapist;
       };
+  }) (lib.attrNames self.df-hashes));
+
+  self = rec {
+    df-hashes = builtins.fromJSON (builtins.readFile ./game.json);
+
+    # Aliases for the latest Dwarf Fortress and the selected Therapist install
+    dwarf-fortress = getAttr (versionToName latestVersion) df-games;
+    dwarf-therapist = dwarf-fortress.dwarf-therapist;
+    dwarf-fortress-original = dwarf-fortress.dwarf-fortress;
+
+    dwarf-fortress-full = callPackage ./lazy-pack.nix {
+      inherit df-games versionToName latestVersion;
     };
+    
+    soundSense = callPackage ./soundsense.nix { };
 
     legends-browser = callPackage ./legends-browser {};
 
-    twbt = callPackage ./twbt {};
-    themes = recurseIntoAttrs (callPackage ./themes { });
+    themes = recurseIntoAttrs (callPackage ./themes {
+      stdenv = stdenvNoCC;
+    });
 
-    # aliases
+    # Theme aliases
     phoebus-theme = themes.phoebus;
     cla-theme = themes.cla;
-    dwarf-fortress-original = dwarf-fortress.dwarf-fortress;
   };
 
 in self // df-games
diff --git a/pkgs/games/dwarf-fortress/dfhack/default.nix b/pkgs/games/dwarf-fortress/dfhack/default.nix
index 4a8c84cf92dc..d65bdab84911 100644
--- a/pkgs/games/dwarf-fortress/dfhack/default.nix
+++ b/pkgs/games/dwarf-fortress/dfhack/default.nix
@@ -3,14 +3,62 @@
 , enableStoneSense ? false,  allegro5, libGLU_combined
 , enableTWBT ? true, twbt
 , SDL
+, dfVersion
 }:
 
+with lib;
+
 let
-  dfVersion = "0.44.12";
-  version = "${dfVersion}-r1";
+  dfhack-releases = {
+    "0.43.05" = {
+      dfHackRelease = "0.43.05-r3.1";
+      sha256 = "1ds366i0qcfbn62w9qv98lsqcrm38npzgvcr35hf6ihqa6nc6xrl";
+      xmlRev = "860a9041a75305609643d465123a4b598140dd7f";
+      prerelease = false;
+    };
+    "0.44.05" = {
+      dfHackRelease = "0.44.05-r2";
+      sha256 = "1cwifdhi48a976xc472nf6q2k0ibwqffil5a4llcymcxdbgxdcc9";
+      xmlRev = "2794f8a6d7405d4858bac486a0bb17b94740c142";
+      prerelease = false;
+    };
+    "0.44.09" = {
+      dfHackRelease = "0.44.09-r1";
+      sha256 = "1nkfaa43pisbyik5inj5q2hja2vza5lwidg5z02jyh136jm64hwk";
+      xmlRev = "3c0bf63674d5430deadaf7befaec42f0ec1e8bc5";
+      prerelease = false;
+    };
+    "0.44.10" = {
+      dfHackRelease = "0.44.10-r2";
+      sha256 = "19bxsghxzw3bilhr8sm4axz7p7z8lrvbdsd1vdjf5zbg04rs866i";
+      xmlRev = "321bd48b10c4c3f694cc801a7dee6be392c09b7b";
+      prerelease = false;
+    };
+    "0.44.11" = {
+      dfHackRelease = "0.44.11-beta2.1";
+      sha256 = "1jgwcqg9m1ybv3szgnklp6zfpiw5mswla464dlj2gfi5v82zqbv2";
+      xmlRev = "f27ebae6aa8fb12c46217adec5a812cd49a905c8";
+      prerelease = true;
+    };
+    "0.44.12" = {
+      dfHackRelease = "0.44.12-r1";
+      sha256 = "0j03lq6j6w378z6cvm7jspxc7hhrqm8jaszlq0mzfvap0k13fgyy";
+      xmlRev = "23500e4e9bd1885365d0a2ef1746c321c1dd5094";
+      prerelease = false;
+    };
+  };
+
+  release = if hasAttr dfVersion dfhack-releases
+            then getAttr dfVersion dfhack-releases
+            else throw "[DFHack] Unsupported Dwarf Fortress version: ${dfVersion}";
+
+  version = release.dfHackRelease;
+
+  warning = if release.prerelease then builtins.trace "[DFHack] Version ${version} is a prerelease. Careful!"
+                                  else null;
 
   # revision of library/xml submodule
-  xmlRev = "23500e4e9bd1885365d0a2ef1746c321c1dd5094";
+  xmlRev = release.xmlRev;
 
   arch =
     if stdenv.hostPlatform.system == "x86_64-linux" then "64"
@@ -21,6 +69,10 @@ let
     #! ${stdenv.shell}
     if [ "$*" = "describe --tags --long" ]; then
       echo "${version}-unknown"
+    elif [ "$*" = "describe --tags --abbrev=8 --long" ]; then
+      echo "${version}-unknown"
+    elif [ "$*" = "describe --tags --abbrev=8 --exact-match" ]; then
+      echo "${version}"
     elif [ "$*" = "rev-parse HEAD" ]; then
       if [ "$(dirname "$(pwd)")" = "xml" ]; then
         echo "${xmlRev}"
@@ -41,8 +93,8 @@ let
     src = fetchFromGitHub {
       owner = "DFHack";
       repo = "dfhack";
-      sha256 = "0j03lq6j6w378z6cvm7jspxc7hhrqm8jaszlq0mzfvap0k13fgyy";
-      rev = version;
+      rev = release.dfHackRelease;
+      sha256 = release.sha256;
       fetchSubmodules = true;
     };
 
diff --git a/pkgs/games/dwarf-fortress/dwarf-therapist/dwarf-therapist.in b/pkgs/games/dwarf-fortress/dwarf-therapist/dwarf-therapist.in
new file mode 100644
index 000000000000..77936c430e2b
--- /dev/null
+++ b/pkgs/games/dwarf-fortress/dwarf-therapist/dwarf-therapist.in
@@ -0,0 +1,26 @@
+#!@stdenv_shell@ -e
+
+[ -z "$DT_DIR" ] && DT_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/dwarftherapist"
+
+install_dir="@install@"
+therapist_dir="@therapist@"
+
+cat <<EOF >&2
+Using $DT_DIR as Dwarf Therapist overlay directory.
+EOF
+
+update_path() {
+  local path="$1"
+
+  mkdir -p "$DT_DIR/$(dirname "$path")"
+  if [ ! -e "$DT_DIR/$path" ] || [ -L "$DT_DIR/$path" ]; then
+    rm -f "$DT_DIR/$path"
+    ln -s "$install_dir/share/dwarftherapist/$path" "$DT_DIR/$path"
+  fi
+}
+
+cd "$install_dir/share/dwarftherapist"
+update_path memory_layouts
+
+QT_QPA_PLATFORM_PLUGIN_PATH="@qt_plugin_path@" \
+  exec "$therapist_dir/bin/dwarftherapist" "$@"
diff --git a/pkgs/games/dwarf-fortress/dwarf-therapist/wrapper.nix b/pkgs/games/dwarf-fortress/dwarf-therapist/wrapper.nix
index 322a21ec3ad9..071ab2af0c5c 100644
--- a/pkgs/games/dwarf-fortress/dwarf-therapist/wrapper.nix
+++ b/pkgs/games/dwarf-fortress/dwarf-therapist/wrapper.nix
@@ -1,12 +1,16 @@
-{ stdenv, symlinkJoin, dwarf-therapist, dwarf-fortress, makeWrapper }:
+{ pkgs, stdenv, symlinkJoin, lib, dwarf-therapist, dwarf-fortress, makeWrapper }:
 
 let
   platformSlug = if stdenv.targetPlatform.is32bit then
     "linux32" else "linux64";
   inifile = "linux/v0.${dwarf-fortress.baseVersion}.${dwarf-fortress.patchVersion}_${platformSlug}.ini";
 
-in symlinkJoin {
+in
+  
+stdenv.mkDerivation rec {
   name = "dwarf-therapist-${dwarf-therapist.version}";
+  
+  wrapper = ./dwarf-therapist.in;
 
   paths = [ dwarf-therapist ];
 
@@ -14,20 +18,33 @@ in symlinkJoin {
 
   passthru = { inherit dwarf-fortress dwarf-therapist; };
 
-  postBuild = ''
-    # DwarfTherapist assumes it's run in $out/share/dwarftherapist and
-    # therefore uses many relative paths.
-    wrapProgram $out/bin/dwarftherapist \
-      --run "cd $out/share/dwarftherapist"
+  buildCommand = ''
+    mkdir -p $out/bin
     ln -s $out/bin/dwarftherapist $out/bin/DwarfTherapist
+    substitute $wrapper $out/bin/dwarftherapist \
+      --subst-var-by stdenv_shell ${stdenv.shell} \
+      --subst-var-by install $out \
+      --subst-var-by therapist ${dwarf-therapist} \
+      --subst-var-by qt_plugin_path "${pkgs.qt5.qtbase}/lib/qt-${pkgs.qt5.qtbase.qtCompatVersion}/plugins/platforms"
 
+    chmod 755 $out/bin/dwarftherapist
+
+    # Fix up memory layouts
     rm -rf $out/share/dwarftherapist/memory_layouts/linux
     mkdir -p $out/share/dwarftherapist/memory_layouts/linux
-    origmd5=$(cat "${dwarf-fortress}/hash.md5.orig" | cut -c1-8)
-    patchedmd5=$(cat "${dwarf-fortress}/hash.md5" | cut -c1-8)
-    substitute \
-      ${dwarf-therapist}/share/dwarftherapist/memory_layouts/${inifile} \
-      $out/share/dwarftherapist/memory_layouts/${inifile} \
-      --replace "$origmd5" "$patchedmd5"
+    orig_md5=$(cat "${dwarf-fortress}/hash.md5.orig" | cut -c1-8)
+    patched_md5=$(cat "${dwarf-fortress}/hash.md5" | cut -c1-8)
+    input_file="${dwarf-therapist}/share/dwarftherapist/memory_layouts/${inifile}"
+    output_file="$out/share/dwarftherapist/memory_layouts/${inifile}"
+
+    echo "[Dwarf Therapist Wrapper] Fixing Dwarf Fortress MD5 prefix:"
+    echo "  Input:   $input_file"
+    echo "  Search:  $orig_md5"
+    echo "  Output:  $output_file"
+    echo "  Replace: $patched_md5"
+
+    substitute "$input_file" "$output_file" --replace "$orig_md5" "$patched_md5"
   '';
+
+  preferLocalBuild = true;
 }
diff --git a/pkgs/games/dwarf-fortress/game.nix b/pkgs/games/dwarf-fortress/game.nix
index 2547bb83f3f5..b5c80a0a56dc 100644
--- a/pkgs/games/dwarf-fortress/game.nix
+++ b/pkgs/games/dwarf-fortress/game.nix
@@ -42,9 +42,6 @@ let
 
 in
 
-assert dwarf-fortress-unfuck != null ->
-       dwarf-fortress-unfuck.dfVersion == dfVersion;
-
 stdenv.mkDerivation {
   name = "dwarf-fortress-${dfVersion}";
 
diff --git a/pkgs/games/dwarf-fortress/lazy-pack.nix b/pkgs/games/dwarf-fortress/lazy-pack.nix
index 3e0d3dcc6d73..3a81dcc9c931 100644
--- a/pkgs/games/dwarf-fortress/lazy-pack.nix
+++ b/pkgs/games/dwarf-fortress/lazy-pack.nix
@@ -1,13 +1,14 @@
-{ stdenvNoCC, lib, buildEnv
-, dwarf-fortress, themes
+{ stdenvNoCC, lib, buildEnv, callPackage
+, df-games, themes, latestVersion, versionToName
+, dfVersion ? latestVersion
   # This package should, at any given time, provide an opinionated "optimal"
   # DF experience. It's the equivalent of the Lazy Newbie Pack, that is, and
-  # should contain every utility available.
+  # should contain every utility available unless you disable them.
 , enableDFHack ? stdenvNoCC.isLinux
 , enableTWBT ? enableDFHack
 , enableSoundSense ? true
-, enableStoneSense ? false  # StoneSense is currently broken.
-, enableDwarfTherapist ? true, dwarf-therapist
+, enableStoneSense ? true
+, enableDwarfTherapist ? true
 , enableLegendsBrowser ? true, legends-browser
 , theme ? themes.phoebus
 # General config options:
@@ -16,6 +17,15 @@
 , enableFPS ? false
 }:
 
+with lib;
+
+let
+  dfGame = versionToName dfVersion;
+  dwarf-fortress = if hasAttr dfGame df-games
+                   then getAttr dfGame df-games
+                   else throw "Unknown Dwarf Fortress version: ${dfVersion}";
+  dwarf-therapist = dwarf-fortress.dwarf-therapist;
+in
 buildEnv {
   name = "dwarf-fortress-full";
   paths = [
@@ -28,7 +38,7 @@ buildEnv {
 
   meta = with stdenvNoCC.lib; {
     description = "An opinionated wrapper for Dwarf Fortress";
-    maintainers = with maintainers; [ Baughn ];
+    maintainers = with maintainers; [ Baughn numinit ];
     license = licenses.mit;
     platforms = platforms.all;
     homepage = https://github.com/NixOS/nixpkgs/;
diff --git a/pkgs/games/dwarf-fortress/themes/default.nix b/pkgs/games/dwarf-fortress/themes/default.nix
index 0b8eb23a7b9d..feb4782d7c32 100644
--- a/pkgs/games/dwarf-fortress/themes/default.nix
+++ b/pkgs/games/dwarf-fortress/themes/default.nix
@@ -1,4 +1,4 @@
-{lib, fetchFromGitHub}:
+{lib, fetchFromGitHub, ...}:
 
 with builtins;
 
diff --git a/pkgs/games/dwarf-fortress/twbt/default.nix b/pkgs/games/dwarf-fortress/twbt/default.nix
index d90812f5d05e..7c80c1012462 100644
--- a/pkgs/games/dwarf-fortress/twbt/default.nix
+++ b/pkgs/games/dwarf-fortress/twbt/default.nix
@@ -1,14 +1,59 @@
-{ stdenvNoCC, fetchurl, unzip }:
+{ stdenvNoCC, lib, fetchurl, unzip
+, dfVersion
+}:
 
+with lib;
+
+let
+  twbt-releases = {
+    "0.43.05" = {
+      twbtRelease = "6.22";
+      sha256 = "0di5d38f6jj9smsz0wjcs1zav4zba6hrk8cbn59kwpb1wamsh5c7";
+      prerelease = false;
+    };
+    "0.44.05" = {
+      twbtRelease = "6.35";
+      sha256 = "0qjkgl7dsqzsd7pdq8a5bihhi1wplfkv1id7sj6dp3swjpsfxp8g";
+      prerelease = false;
+    };
+    "0.44.09" = {
+      twbtRelease = "6.41";
+      sha256 = "0nsq15z05pbhqjvw2xqs1a9b1n2ma0aalhc3vh3mi4cd4k7lxh44";
+      prerelease = false;
+    };
+    "0.44.10" = {
+      twbtRelease = "6.49";
+      sha256 = "1qjkc7k33qhxj2g18njzasccjqsis5y8zrw5vl90h4rs3i8ld9xz";
+      prerelease = false;
+    };
+    "0.44.11" = {
+      twbtRelease = "6.51";
+      sha256 = "1yclqmarjd97ch054h425a12r8a5ailmflsd7b39cg4qhdr1nii5";
+      prerelease = true;
+    };
+    "0.44.12" = {
+      twbtRelease = "6.54";
+      sha256 = "10gfd6vv0vk4v1r5hjbz7vf1zqys06dsad695gysc7fbcik2dakh";
+      prerelease = false;
+    };
+  };
+
+  release = if hasAttr dfVersion twbt-releases
+            then getAttr dfVersion twbt-releases
+            else throw "[TWBT] Unsupported Dwarf Fortress version: ${dfVersion}";
+
+  warning = if release.prerelease then builtins.trace "[TWBT] Version ${version} is a prerelease. Careful!"
+                                  else null;
+
+in
 
 stdenvNoCC.mkDerivation rec {
   name = "twbt-${version}";
-  version = "6.54";
-  dfVersion = "0.44.12";
+  version = release.twbtRelease;
 
   src = fetchurl {
     url = "https://github.com/mifki/df-twbt/releases/download/v${version}/twbt-${version}-linux.zip";
-    sha256 = "10gfd6vv0vk4v1r5hjbz7vf1zqys06dsad695gysc7fbcik2dakh";
+    sha256 = release.sha256;
   };
 
   sourceRoot = ".";
@@ -24,10 +69,9 @@ stdenvNoCC.mkDerivation rec {
     cp -a *.png $art/data/art/
   '';
 
-  
   meta = with stdenvNoCC.lib; {
     description = "A plugin for Dwarf Fortress / DFHack that improves various aspects the game interface.";
-    maintainers = with maintainers; [ Baughn ];
+    maintainers = with maintainers; [ Baughn numinit ];
     license = licenses.mit;
     platforms = platforms.linux;
     homepage = https://github.com/mifki/df-twbt;
diff --git a/pkgs/games/dwarf-fortress/unfuck.nix b/pkgs/games/dwarf-fortress/unfuck.nix
index 0c5a81a52f0f..c4d01b3ff392 100644
--- a/pkgs/games/dwarf-fortress/unfuck.nix
+++ b/pkgs/games/dwarf-fortress/unfuck.nix
@@ -1,18 +1,52 @@
-{ stdenv, fetchFromGitHub, cmake
+{ stdenv, lib, fetchFromGitHub, cmake
 , libGL, libSM, SDL, SDL_image, SDL_ttf, glew, openalSoft
 , ncurses, glib, gtk2, libsndfile, zlib
+, dfVersion
 }:
 
-let dfVersion = "0.44.12"; in
+with lib;
+
+let
+  unfuck-releases = {
+    "0.43.05" = {
+      unfuckRelease = "0.43.05";
+      sha256 = "173dyrbxlzqvjf1j3n7vpns4gfjkpyvk9z16430xnmd5m6nda8p2";
+    };
+    "0.44.05" = {
+      unfuckRelease = "0.44.05";
+      sha256 = "00yj4l4gazxg4i6fj9rwri6vm17i6bviy2mpkx0z5c0mvsr7s14b";
+    };
+    "0.44.09" = {
+      unfuckRelease = "0.44.09";
+      sha256 = "138p0v8z2x47f0fk9k6g75ikw5wb3vxldwv5ggbkf4hhvlw6lvzm";
+    };
+    "0.44.10" = {
+      unfuckRelease = "0.44.10";
+      sha256 = "0vb19qx2ibc79j4bgbk9lskb883qfb0815zw1dfz9k7rqwal8mzj";
+    };
+    "0.44.11" = {
+      unfuckRelease = "0.44.11.1";
+      sha256 = "1kszkb1d1vll8p04ja41nangsaxb5lv4p3xh2jhmsmipfixw7nvz";
+    };
+    "0.44.12" = {
+      unfuckRelease = "0.44.12";
+      sha256 = "1kszkb1d1vll8p04ja41nangsaxb5lv4p3xh2jhmsmipfixw7nvz";
+    };
+  };
+
+  release = if hasAttr dfVersion unfuck-releases
+            then getAttr dfVersion unfuck-releases
+            else throw "[unfuck] Unknown Dwarf Fortress version: ${dfVersion}";
+in
 
 stdenv.mkDerivation {
-  name = "dwarf_fortress_unfuck-${dfVersion}";
+  name = "dwarf_fortress_unfuck-${release.unfuckRelease}";
 
   src = fetchFromGitHub {
     owner = "svenstaro";
     repo = "dwarf_fortress_unfuck";
-    rev = dfVersion;
-    sha256 = "1kszkb1d1vll8p04ja41nangsaxb5lv4p3xh2jhmsmipfixw7nvz";
+    rev = release.unfuckRelease;
+    sha256 = release.sha256;
   };
 
   cmakeFlags = [
@@ -20,23 +54,12 @@ stdenv.mkDerivation {
     "-DGTK2_GDKCONFIG_INCLUDE_DIR=${gtk2.out}/lib/gtk-2.0/include"
   ];
 
-  makeFlags = [
-    ''CFLAGS="-fkeep-inline-functions"''
-    ''CXXFLAGS="-fkeep-inline-functions"''
-  ];
-
   nativeBuildInputs = [ cmake ];
   buildInputs = [
     libSM SDL SDL_image SDL_ttf glew openalSoft
     ncurses gtk2 libsndfile zlib libGL
   ];
 
-  postPatch = ''
-    substituteInPlace CMakeLists.txt --replace \
-      'set(CMAKE_BUILD_TYPE Release)' \
-      'set(CMAKE_BUILD_TYPE Debug)'
-  '';
-
   # Don't strip unused symbols; dfhack hooks into some of them.
   dontStrip = true;
 
@@ -56,6 +79,6 @@ stdenv.mkDerivation {
     homepage = https://github.com/svenstaro/dwarf_fortress_unfuck;
     license = licenses.free;
     platforms = platforms.linux;
-    maintainers = with maintainers; [ abbradar ];
+    maintainers = with maintainers; [ abbradar numinit ];
   };
 }
diff --git a/pkgs/games/dwarf-fortress/wrapper/default.nix b/pkgs/games/dwarf-fortress/wrapper/default.nix
index 6efe004fa9e8..8d9f06ffe143 100644
--- a/pkgs/games/dwarf-fortress/wrapper/default.nix
+++ b/pkgs/games/dwarf-fortress/wrapper/default.nix
@@ -1,4 +1,6 @@
-{ stdenv, lib, buildEnv, dwarf-fortress, substituteAll
+{ stdenv, lib, buildEnv, substituteAll
+, dwarf-fortress, dwarf-fortress-unfuck
+, dwarf-therapist
 , enableDFHack ? false, dfhack
 , enableSoundSense ? false, soundSense, jdk
 , enableStoneSense ? false
@@ -36,18 +38,29 @@ let
 
     paths = themePkg ++ pkgs;
     pathsToLink = [ "/" "/hack" "/hack/scripts" ];
-    ignoreCollisions = true;
 
     postBuild = ''
       # De-symlink init.txt
       cp $out/data/init/init.txt init.txt
-      rm $out/data/init/init.txt
+      rm -f $out/data/init/init.txt
       mv init.txt $out/data/init/init.txt
     '' + lib.optionalString enableDFHack ''
+      # De-symlink symbols.xml
       rm $out/hack/symbols.xml
-      substitute ${dfhack_}/hack/symbols.xml $out/hack/symbols.xml \
-        --replace $(cat ${dwarf-fortress}/hash.md5.orig) \
-                  $(cat ${dwarf-fortress}/hash.md5)
+
+      # Patch the MD5
+      orig_md5=$(cat "${dwarf-fortress}/hash.md5.orig")
+      patched_md5=$(cat "${dwarf-fortress}/hash.md5")
+      input_file="${dfhack_}/hack/symbols.xml"
+      output_file="$out/hack/symbols.xml"
+
+      echo "[DFHack Wrapper] Fixing Dwarf Fortress MD5:"
+      echo "  Input:   $input_file"
+      echo "  Search:  $orig_md5"
+      echo "  Output:  $output_file"
+      echo "  Replace: $patched_md5"
+
+      substitute "$input_file" "$output_file" --replace "$orig_md5" "$patched_md5"
     '' + lib.optionalString enableTWBT ''
       substituteInPlace $out/data/init/init.txt \
         --replace '[PRINT_MODE:2D]' '[PRINT_MODE:TWBT]'
@@ -57,14 +70,14 @@ let
         --replace '[TRUETYPE:YES]' '[TRUETYPE:${unBool enableTruetype}]' \
         --replace '[FPS:NO]' '[FPS:${unBool enableFPS}]'
     '';
+
+    ignoreCollisions = true;
   };
 in
 
 stdenv.mkDerivation rec {
   name = "dwarf-fortress-${dwarf-fortress.dfVersion}";
 
-  compatible = lib.all (x: assert (x.dfVersion == dwarf-fortress.dfVersion); true) pkgs;
-
   dfInit = substituteAll {
     name = "dwarf-fortress-init";
     src = ./dwarf-fortress-init.in;
@@ -77,7 +90,7 @@ stdenv.mkDerivation rec {
   runDFHack = ./dfhack.in;
   runSoundSense = ./soundSense.in;
 
-  passthru = { inherit dwarf-fortress; };
+  passthru = { inherit dwarf-fortress dwarf-therapist; };
 
   buildCommand = ''
     mkdir -p $out/bin
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index cc71d5ec6892..182eb55557e9 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -2347,7 +2347,9 @@ with pkgs;
 
   enblend-enfuse = callPackage ../tools/graphics/enblend-enfuse { };
 
-  cryfs = callPackage ../tools/filesystems/cryfs { };
+  cryfs = callPackage ../tools/filesystems/cryfs {
+    spdlog = spdlog_0;
+  };
 
   encfs = callPackage ../tools/filesystems/encfs {
     tinyxml2 = tinyxml-2;
@@ -11244,6 +11246,8 @@ with pkgs;
 
   mtpfs = callPackage ../tools/filesystems/mtpfs { };
 
+  mtxclient = callPackage ../development/libraries/mtxclient { };
+
   mu = callPackage ../tools/networking/mu {
     texinfo = texinfo4;
   };
@@ -22215,7 +22219,10 @@ with pkgs;
 
   bullet = callPackage ../development/libraries/bullet {};
 
-  spdlog = callPackage ../development/libraries/spdlog { };
+  inherit (callPackages ../development/libraries/spdlog { })
+    spdlog_0 spdlog_1;
+
+  spdlog = spdlog_1;
 
   dart = callPackage ../development/interpreters/dart { };
   dart_stable = dart.override { version = "1.24.3"; };
diff --git a/pkgs/top-level/emacs-packages.nix b/pkgs/top-level/emacs-packages.nix
index 6cd80613526b..a83c3baaa4a0 100644
--- a/pkgs/top-level/emacs-packages.nix
+++ b/pkgs/top-level/emacs-packages.nix
@@ -178,7 +178,7 @@ let
       for file in elpy.el elpy-pkg.el; do
         substituteInPlace $file \
             --replace "company \"0.8.2\"" "company \"${company.version}\"" \
-            --replace "find-file-in-project \"3.3\"" "find-file-in-project \"${melpaPackages.find-file-in-project.version}\"" \
+            --replace "find-file-in-project \"3.3\"" "find-file-in-project \"${(melpaPackages self).find-file-in-project.version}\"" \
             --replace "highlight-indentation \"0.5.0\"" "highlight-indentation \"${highlight-indentation.version}\"" \
             --replace "pyvenv \"1.3\"" "pyvenv \"${pyvenv.version}\"" \
             --replace "yasnippet \"0.8.0\"" "yasnippet \"${yasnippet.version}\""