diff --git a/krebs/3modules/git.nix b/krebs/3modules/git.nix
index d2d73ba3d..0f5e3172e 100644
--- a/krebs/3modules/git.nix
+++ b/krebs/3modules/git.nix
@@ -13,7 +13,7 @@ let
   out = {
     options.krebs.git = api;
     config = with lib; mkIf cfg.enable (mkMerge [
-      (mkIf cfg.cgit cgit-imp)
+      (mkIf cfg.cgit.enable cgit-imp)
       git-imp
     ]);
   };
@@ -22,10 +22,33 @@ let
     enable = mkEnableOption "krebs.git";
 
     cgit = mkOption {
-      type = types.bool;
-      default = true;
+      type = types.submodule {
+        options = {
+          enable = mkEnableOption "krebs.git.cgit" // { default = true; };
+          fcgiwrap = {
+            group = mkOption {
+              type = types.group;
+              default = {
+                name = "fcgiwrap";
+              };
+            };
+            user = mkOption {
+              type = types.user;
+              default = {
+                name = "fcgiwrap";
+                home = toString pkgs.empty;
+              };
+            };
+          };
+          settings = mkOption {
+            apply = flip removeAttrs ["_module"];
+            default = {};
+            type = subtypes.cgit-settings;
+          };
+        };
+      };
+      default = {};
       description = ''
-          Enable cgit.
           Cgit is an attempt to create a fast web interface for the git version
           control system, using a built in cache to decrease pressure on the
           git server.
@@ -40,7 +63,11 @@ let
       description = "Directory used to store repositories.";
     };
     etcDir = mkOption {
-      type = types.str;
+      type = mkOptionType {
+        name = "${types.absolute-pathname.name} starting with `/etc/'";
+        check = x: types.absolute-pathname.check x && hasPrefix "/etc/" x;
+        merge = mergeOneOption;
+      };
       default = "/etc/git";
     };
     repos = mkOption {
@@ -63,22 +90,6 @@ let
         Repositories.
       '';
     };
-    root-desc = mkOption {
-      type = types.nullOr types.str;
-      default = null;
-      description = ''
-        Text printed below the heading on the repository index page.
-        Default value: "a fast webinterface for the git dscm".
-      '';
-    };
-    root-title = mkOption {
-      type = types.nullOr types.str;
-      default = null;
-      description = ''
-        Text printed as heading on the repository index page.
-        Default value: "Git Repository Browser".
-      '';
-    };
     rules = mkOption {
       type = types.listOf subtypes.rule;
       default = [];
@@ -95,12 +106,117 @@ let
         access and permission rules for git repositories.
       '';
     };
+
+    user = mkOption {
+      type = types.user;
+      default = {
+        name = "git";
+        home = toString pkgs.empty;
+      };
+    };
   };
 
   # TODO put into krebs/4lib/types.nix?
   subtypes = {
-    repo = types.submodule ({
+    cgit-settings = types.submodule {
+      # A setting's value of `null` means cgit's default should be used.
       options = {
+        cache-root = mkOption {
+          type = types.absolute-pathname;
+          default = "/tmp/cgit";
+        };
+        cache-size = mkOption {
+          type = types.uint;
+          default = 1000;
+        };
+        css = mkOption {
+          type = types.absolute-pathname;
+          default = "/static/cgit.css";
+        };
+        enable-commit-graph = mkOption {
+          type = types.bool;
+          default = true;
+        };
+        enable-index-links = mkOption {
+          type = types.bool;
+          default = true;
+        };
+        enable-index-owner = mkOption {
+          type = types.bool;
+          default = false;
+        };
+        enable-log-filecount = mkOption {
+          type = types.bool;
+          default = true;
+        };
+        enable-log-linecount = mkOption {
+          type = types.bool;
+          default = true;
+        };
+        enable-remote-branches = mkOption {
+          type = types.bool;
+          default = true;
+        };
+        logo = mkOption {
+          type = types.absolute-pathname;
+          default = "/static/cgit.png";
+        };
+        max-stats = mkOption {
+          type =
+            types.nullOr (types.enum ["week" "month" "quarter" "year"]);
+          default = "year";
+        };
+        robots = mkOption {
+          type = types.nullOr (types.listOf types.str);
+          default = ["nofollow" "noindex"];
+        };
+        root-desc = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+        };
+        root-title = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+        };
+        virtual-root = mkOption {
+          type = types.nullOr types.absolute-pathname;
+          default = "/";
+        };
+      };
+    };
+    repo = types.submodule ({ config, ... }: {
+      options = {
+        cgit = {
+          desc = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            description = ''
+              Repository description.
+            '';
+          };
+          path = mkOption {
+            type = types.str;
+            default = "${cfg.dataDir}/${config.name}";
+            description = ''
+              An absolute path to the repository directory. For non-bare
+              repositories this is the .git-directory.
+            '';
+          };
+          section = mkOption {
+            type = types.nullOr types.str;
+            default = null;
+            description = ''
+              Repository section.
+            '';
+          };
+          url = mkOption {
+            type = types.str;
+            default = config.name;
+            description = ''
+              The relative url used to access the repository.
+            '';
+          };
+        };
         collaborators = mkOption {
           type = types.listOf types.user;
           default = [];
@@ -112,20 +228,6 @@ let
             an example.
           '';
         };
-        desc = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          description = ''
-            Repository description.
-          '';
-        };
-        section = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          description = ''
-            Repository section.
-          '';
-        };
         name = mkOption {
           type = types.str;
           description = ''
@@ -216,90 +318,80 @@ let
     system.activationScripts.git-init = "${init-script}";
 
     # TODO maybe put all scripts here and then use PATH?
-    environment.etc."${etc-base}".source =
+    environment.etc.${removePrefix "/etc/" cfg.etcDir}.source =
       scriptFarm "git-ssh-authorizers" {
         authorize-command = makeAuthorizeScript (map (rule: [
-          (map getName (ensureList rule.user))
-          (map getName (ensureList rule.repo))
+          (map getName (toList rule.user))
+          (map getName (toList rule.repo))
           (map getName rule.perm.allow-commands)
         ]) cfg.rules);
 
         authorize-push = makeAuthorizeScript (map (rule: [
-          (map getName (ensureList rule.user))
-          (map getName (ensureList rule.repo))
-          (ensureList rule.perm.allow-receive-ref)
+          (map getName (toList rule.user))
+          (map getName (toList rule.repo))
+          (toList rule.perm.allow-receive-ref)
           (map getName rule.perm.allow-receive-modes)
         ]) (filter (rule: rule.perm.allow-receive-ref != null) cfg.rules));
       };
 
-    # TODO cfg.user
-    users.users.git = rec {
+    users.users.${cfg.user.name} = {
+      inherit (cfg.user) home name uid;
       description = "Git repository hosting user";
-      name = "git";
       shell = "/bin/sh";
       openssh.authorizedKeys.keys =
         mapAttrsToList (_: makeAuthorizedKey git-ssh-command)
                        (filterAttrs (_: user: isString user.pubkey)
                                     config.krebs.users);
-      uid = genid name;
     };
   };
 
   cgit-imp = {
-    users.extraUsers = lib.singleton {
-      inherit (fcgitwrap-user) group name uid;
-      home = toString (pkgs.runCommand "empty" {} "mkdir -p $out");
-    };
-
-    users.extraGroups = lib.singleton {
-      inherit (fcgitwrap-group) gid name;
+    users = {
+      groups.${cfg.cgit.fcgiwrap.group.name} = {
+        inherit (cfg.cgit.fcgiwrap.group) name gid;
+      };
+      users.${cfg.cgit.fcgiwrap.user.name} = {
+        inherit (cfg.cgit.fcgiwrap.user) home name uid;
+        group = cfg.cgit.fcgiwrap.group.name;
+      };
     };
 
     services.fcgiwrap = {
       enable = true;
-      user = fcgitwrap-user.name;
-      group = fcgitwrap-user.group;
+      user = cfg.cgit.fcgiwrap.user.name;
+      group = cfg.cgit.fcgiwrap.group.name;
       # socketAddress = "/run/fcgiwrap.sock" (default)
       # socketType = "unix" (default)
     };
 
-    environment.etc."cgitrc".text = ''
-      css=/static/cgit.css
-      logo=/static/cgit.png
+    environment.etc."cgitrc".text = let
+      repo-to-cgitrc = _: repo:
+        optionals (isPublicRepo repo) (concatLists [
+          [""] # empty line
+          [(kv-to-cgitrc "repo.url" repo.cgit.url)]
+          (mapAttrsToList kv-to-cgitrc
+            (mapAttrs' (k: nameValuePair "repo.${k}")
+              (removeAttrs repo.cgit ["url"])))
+        ]);
 
-      # if you do not want that webcrawler (like google) index your site
-      robots=noindex, nofollow
-
-      virtual-root=/
-
-      # TODO make this nicer (and/or somewhere else)
-      cache-root=/tmp/cgit
-
-      cache-size=1000
-      enable-commit-graph=1
-      enable-index-links=1
-      enable-index-owner=0
-      enable-log-filecount=1
-      enable-log-linecount=1
-      enable-remote-branches=1
-
-      ${optionalString (cfg.root-title != null) "root-title=${cfg.root-title}"}
-      ${optionalString (cfg.root-desc != null) "root-desc=${cfg.root-desc}"}
-
-      snapshots=0
-      max-stats=year
-
-      ${concatMapStringsSep "\n" (repo: ''
-        repo.url=${repo.name}
-        repo.path=${cfg.dataDir}/${repo.name}
-        ${optionalString (repo.section != null) "repo.section=${repo.section}"}
-        ${optionalString (repo.desc != null) "repo.desc=${repo.desc}"}
-      '') (filter isPublicRepo (attrValues cfg.repos))}
-    '';
+      kv-to-cgitrc = k: v: getAttr (typeOf v) {
+        bool = kv-to-cgitrc k (if v then 1 else 0);
+        null = []; # This will be removed by `flatten`.
+        list = "${k}=${concatStringsSep ", " v}";
+        int = "${k}=${toString v}";
+        string = "${k}=${v}";
+      };
+    in
+      concatStringsSep "\n"
+        (flatten (
+          mapAttrsToList kv-to-cgitrc cfg.cgit.settings
+          ++
+          mapAttrsToList repo-to-cgitrc cfg.repos
+        ));
 
     system.activationScripts.cgit = ''
-      mkdir -m 0700 -p /tmp/cgit
-      chown ${toString fcgitwrap-user.uid}:${toString fcgitwrap-group.gid} /tmp/cgit
+      mkdir -m 0700 -p ${cfg.cgit.settings.cache-root}
+      chown ${toString cfg.cgit.fcgiwrap.user.uid}:${toString cfg.cgit.fcgiwrap.group.gid} ${cfg.cgit.settings.cache-root}
     '';
 
     krebs.nginx = {
@@ -307,6 +399,7 @@ let
       servers.cgit = {
         server-names = [
           "cgit.${config.networking.hostName}"
+          "cgit.${config.networking.hostName}.r"
           "cgit.${config.networking.hostName}.retiolum"
         ];
         locations = [
@@ -327,21 +420,6 @@ let
     };
   };
 
-  fcgitwrap-user = rec {
-    name = "fcgiwrap";
-    uid = genid name;
-    group = "fcgiwrap";
-  };
-
-  fcgitwrap-group = {
-    name = fcgitwrap-user.name;
-    gid = fcgitwrap-user.uid;
-  };
-
-
-  ensureList = x:
-    if typeOf x == "list" then x else [x];
-
   getName = x: x.name;
 
   isPublicRepo = getAttr "public"; # TODO this is also in ./cgit.nix
@@ -366,7 +444,7 @@ let
   makeAuthorizeScript =
     let
       # TODO escape
-      to-pattern = x: concatStringsSep "|" (ensureList x);
+      to-pattern = x: concatStringsSep "|" (toList x);
       go = i: ps:
         if ps == []
           then "exit 0"
@@ -567,9 +645,5 @@ let
     '';
   };
 
-  etc-base =
-    assert (hasPrefix "/etc/" cfg.etcDir);
-    removePrefix "/etc/" cfg.etcDir;
-
 in
 out
diff --git a/krebs/4lib/default.nix b/krebs/4lib/default.nix
index 585bd313f..bfe8c581c 100644
--- a/krebs/4lib/default.nix
+++ b/krebs/4lib/default.nix
@@ -41,8 +41,6 @@ let out = rec {
     mapAttrs (name: _: path + "/${name}")
              (filterAttrs (_: eq "directory") (readDir path));
 
-  getAttrDef = name: set: set.${name} or set.default or null;
-  mapAttrValues = f: mapAttrs (_: f);
   setAttr = name: value: set: set // { ${name} = value; };
 
   optionalTrace = c: msg: x: if c then trace msg x else x;
diff --git a/krebs/4lib/types.nix b/krebs/4lib/types.nix
index 66191d0b3..678ae7a60 100644
--- a/krebs/4lib/types.nix
+++ b/krebs/4lib/types.nix
@@ -154,6 +154,12 @@ types // rec {
     merge = mergeOneOption;
   };
 
+  uint = mkOptionType {
+    name = "unsigned integer";
+    check = x: isInt x && x >= 0;
+    merge = mergeOneOption;
+  };
+
   secret-file = submodule ({ config, ... }: {
     options = {
       path = mkOption { type = str; };
@@ -199,8 +205,9 @@ types // rec {
         description = ''
           Set of user's PGP public keys.
 
-          Modules supporting PGP may use well-known key names to define option
-          defaults, e.g. using `getAttrDef well-known-name pubkeys`.
+          Modules supporting PGP may use well-known key names to define
+          default values for options, in which case the well-known name
+          should be documented in the respective option's description.
         '';
       };
       pubkey = mkOption {
@@ -318,10 +325,7 @@ types // rec {
   # POSIX.1‐2013, 3.278 Portable Filename Character Set
   filename = mkOptionType {
     name = "POSIX filename";
-    check = let
-      filename-chars = stringToCharacters
-        "-.0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
-    in s: all (flip elem filename-chars) (stringToCharacters s);
+    check = x: match "([0-9A-Za-z._])[0-9A-Za-z._-]*" x != null;
     merge = mergeOneOption;
   };
 
@@ -330,7 +334,8 @@ types // rec {
   # TODO two slashes
   absolute-pathname = mkOptionType {
     name = "POSIX absolute pathname";
-    check = s: pathname.check s && substring 0 1 s == "/";
+    check = s: s == "/" || (pathname.check s && substring 0 1 s == "/");
+    merge = mergeOneOption;
   };
 
   # POSIX.1‐2013, 3.267 Pathname
@@ -338,11 +343,13 @@ types // rec {
   pathname = mkOptionType {
     name = "POSIX pathname";
     check = s: isString s && all filename.check (splitString "/" s);
+    merge = mergeOneOption;
   };
 
   # POSIX.1-2013, 3.431 User Name
   username = mkOptionType {
     name = "POSIX username";
-    check = s: filename.check s && substring 0 1 s != "-";
+    check = filename.check;
+    merge = mergeOneOption;
   };
 }
diff --git a/krebs/5pkgs/builders.nix b/krebs/5pkgs/builders.nix
index f60bbc9d0..8ba0ab5a7 100644
--- a/krebs/5pkgs/builders.nix
+++ b/krebs/5pkgs/builders.nix
@@ -81,6 +81,26 @@ rec {
     mv "$textPath" $out
   '';
 
+  writeFiles = name: specs0:
+  let
+    specs = mapAttrsToList (path: spec0: {
+      path = assert types.pathname.check path; path;
+      var = "file_${hashString "sha1" path}";
+      text = spec0.text;
+    }) specs0;
+
+    filevars = genAttrs' specs (spec: nameValuePair spec.var spec.text);
+
+    env = filevars // { passAsFile = attrNames filevars; };
+  in
+    pkgs.runCommand name env /* sh */ ''
+      set -efu
+      PATH=${makeBinPath [pkgs.coreutils]}
+      ${concatMapStrings (spec: /* sh */ ''
+        install -D ''$${spec.var}Path $out${spec.path}
+      '') specs}
+    '';
+
   writeHaskell =
     k:
     let
diff --git a/krebs/5pkgs/cgit/default.nix b/krebs/5pkgs/cgit/default.nix
deleted file mode 100644
index 3180a5bd3..000000000
--- a/krebs/5pkgs/cgit/default.nix
+++ /dev/null
@@ -1,64 +0,0 @@
-{ stdenv, fetchurl, openssl, zlib, asciidoc, libxml2, libxslt
-, docbook_xml_xslt, pkgconfig, luajit
-, gzip, bzip2, xz
-}:
-
-stdenv.mkDerivation rec {
-  name = "cgit-${version}";
-  version = "0.12";
-
-  src = fetchurl {
-    url = "http://git.zx2c4.com/cgit/snapshot/${name}.tar.xz";
-    sha256 = "1dx54hgfyabmg9nm5qp6d01f54nlbqbbdwhwl0llb9imjf237qif";
-  };
-
-  # cgit is tightly coupled with git and needs a git source tree to build.
-  # IMPORTANT: Remember to check which git version cgit needs on every version
-  # bump (look in the Makefile).
-  # NOTE: as of 0.10.1, the git version is compatible from 1.9.0 to
-  # 1.9.2 (see the repository history)
-  gitSrc = fetchurl {
-    url    = "mirror://kernel/software/scm/git/git-2.7.2.tar.xz";
-    sha256 = "086ga30ksijfxad085ply83ddf955d2b8qxph5sw6c9hab77j15j";
-  };
-
-  buildInputs = [
-    openssl zlib asciidoc libxml2 libxslt docbook_xml_xslt pkgconfig luajit
-  ];
-
-  postPatch = ''
-    sed -e 's|"gzip"|"${gzip}/bin/gzip"|' \
-        -e 's|"bzip2"|"${bzip2}/bin/bzip2"|' \
-        -e 's|"xz"|"${xz}/bin/xz"|' \
-        -i ui-snapshot.c
-  '';
-
-  # Give cgit a git source tree and pass configuration parameters (as make
-  # variables).
-  preBuild = ''
-    mkdir -p git
-    tar --strip-components=1 -xf "$gitSrc" -C git
-
-    makeFlagsArray+=(prefix="$out" CGIT_SCRIPT_PATH="$out/cgit/")
-  '';
-
-  # Install manpage.
-  postInstall = ''
-    # xmllint fails:
-    #make install-man
-
-    # bypassing xmllint works:
-    a2x --no-xmllint -f manpage cgitrc.5.txt
-    mkdir -p "$out/share/man/man5"
-    cp cgitrc.5 "$out/share/man/man5"
-  '';
-
-  meta = {
-    homepage = http://git.zx2c4.com/cgit/about/;
-    repositories.git = git://git.zx2c4.com/cgit;
-    description = "Web frontend for git repositories";
-    license = stdenv.lib.licenses.gpl2;
-    platforms = stdenv.lib.platforms.linux;
-    maintainers = with stdenv.lib.maintainers; [ bjornfor ];
-  };
-}
diff --git a/krebs/5pkgs/default.nix b/krebs/5pkgs/default.nix
index 53fc4de44..f2bbaf7f4 100644
--- a/krebs/5pkgs/default.nix
+++ b/krebs/5pkgs/default.nix
@@ -20,6 +20,8 @@ with config.krebs.lib;
               (filterAttrs (_: dir.has-default-nix)
                            (subdirsOf ./.))
   // {
+    empty = pkgs.runCommand "empty-1.0.0" {} "mkdir $out";
+
     haskellPackages = pkgs.haskellPackages.override {
       overrides = self: super:
         mapAttrs (name: path: self.callPackage path {})
diff --git a/lass/2configs/git.nix b/lass/2configs/git.nix
index aac3f6e02..c0affe981 100644
--- a/lass/2configs/git.nix
+++ b/lass/2configs/git.nix
@@ -7,8 +7,12 @@ let
   out = {
     krebs.git = {
       enable = true;
-      root-title = "public repositories at ${config.krebs.build.host.name}";
-      root-desc = "keep calm and engage";
+      cgit = {
+        settings = {
+          root-title = "public repositories at ${config.krebs.build.host.name}";
+          root-desc = "keep calm and engage";
+        };
+      };
       repos = mapAttrs (_: s: removeAttrs s ["collaborators"]) repos;
       rules = rules;
     };
@@ -27,7 +31,7 @@ let
   public-repos = mapAttrs make-public-repo {
     painload = {};
     stockholm = {
-      desc = "take all the computers hostage, they'll love you!";
+      cgit.desc = "take all the computers hostage, they'll love you!";
     };
     wai-middleware-time = {};
     web-routes-wai-custom = {};
@@ -52,8 +56,8 @@ let
     import <secrets/repos.nix> { inherit config lib pkgs; }
   );
 
-  make-public-repo = name: { desc ? null, ... }: {
-    inherit name desc;
+  make-public-repo = name: { cgit ? {}, ... }: {
+    inherit cgit name;
     public = true;
     hooks = {
       post-receive = pkgs.git-hooks.irc-announce {
@@ -66,13 +70,13 @@ let
     };
   };
 
-  make-public-repo-silent = name: { desc ? null, ... }: {
-    inherit name desc;
+  make-public-repo-silent = name: { cgit ? {}, ... }: {
+    inherit cgit name;
     public = true;
   };
 
-  make-restricted-repo = name: { collaborators ? [], desc ? null, ... }: {
-    inherit name collaborators desc;
+  make-restricted-repo = name: { collaborators ? [], ... }: {
+    inherit collaborators name;
     public = false;
   };
 
diff --git a/makefu/2configs/git/brain-retiolum.nix b/makefu/2configs/git/brain-retiolum.nix
index 80e4c87cf..ae54c6dbf 100644
--- a/makefu/2configs/git/brain-retiolum.nix
+++ b/makefu/2configs/git/brain-retiolum.nix
@@ -7,9 +7,7 @@ let
   rules = concatMap krebs-rules (attrValues krebs-repos) ++ concatMap priv-rules (attrValues priv-repos);
 
   krebs-repos = mapAttrs make-krebs-repo {
-    brain = {
-      desc = "braiiiins";
-    };
+    brain = { };
   };
 
   priv-repos = mapAttrs make-priv-repo {
@@ -18,13 +16,13 @@ let
   };
 
   # TODO move users to separate module
-  make-priv-repo = name: { desc ? null, ... }: {
-    inherit name desc;
+  make-priv-repo = name: { ... }: {
+    inherit name;
     public = false;
   };
 
-  make-krebs-repo = with git; name: { desc ? null, ... }: {
-    inherit name desc;
+  make-krebs-repo = with git; name: { ... }: {
+    inherit name;
     public = false;
     hooks = {
       post-receive = pkgs.git-hooks.irc-announce {
@@ -63,7 +61,7 @@ in {
   imports = [ ];
   krebs.git = {
     enable = true;
-    cgit = false;
+    cgit.enable = false;
     inherit repos rules;
   };
 }
diff --git a/makefu/2configs/git/cgit-retiolum.nix b/makefu/2configs/git/cgit-retiolum.nix
index 44d759488..fe2c850f7 100644
--- a/makefu/2configs/git/cgit-retiolum.nix
+++ b/makefu/2configs/git/cgit-retiolum.nix
@@ -10,17 +10,17 @@ let
 
   krebs-repos = mapAttrs make-krebs-repo {
     stockholm = {
-      desc = "Make all the systems into 1systems!";
+      cgit.desc = "Make all the systems into 1systems!";
     };
     tinc_graphs = {
-      desc = "Tinc Advanced Graph Generation";
+      cgit.desc = "Tinc Advanced Graph Generation";
     };
     stockholm-init = {
-      desc = "Build new Stockholm hosts";
+      cgit.desc = "Build new Stockholm hosts";
     };
     cac-api = { };
     init-stockholm = {
-      desc = "Init stuff for stockholm";
+      cgit.desc = "Init stuff for stockholm";
     };
   };
 
@@ -32,19 +32,19 @@ let
     connector = { };
     minikrebs = { };
     mattermost = {
-      desc = "Mattermost Docker files";
+      cgit.desc = "Mattermost Docker files";
     };
   };
 
 
   # TODO move users to separate module
-  make-priv-repo = name: { desc ? null, ... }: {
-    inherit name desc;
+  make-priv-repo = name: { ... }: {
+    inherit name;
     public = false;
   };
 
-  make-krebs-repo = with git; name: { desc ? null, ... }: {
-    inherit name desc;
+  make-krebs-repo = with git; name: { cgit ? {}, ... }: {
+    inherit cgit name;
     public = true;
     hooks = {
       post-receive = pkgs.git-hooks.irc-announce {
@@ -88,8 +88,12 @@ let
 in {
   krebs.git = {
     enable = true;
-    root-title = "public repositories";
-    root-desc = "keep on krebsing";
+    cgit = {
+      settings = {
+        root-title = "public repositories";
+        root-desc = "keep on krebsing";
+      };
+    };
     inherit repos rules;
   };
 }
diff --git a/miefda/2configs/git.nix b/miefda/2configs/git.nix
index 052cc4ab2..51679d2a5 100644
--- a/miefda/2configs/git.nix
+++ b/miefda/2configs/git.nix
@@ -7,8 +7,12 @@ let
   out = {
     krebs.git = {
       enable = true;
-      root-title = "public repositories at ${config.krebs.build.host.name}";
-      root-desc = "keep calm and engage";
+      cgit = {
+        settings = {
+          root-title = "public repositories at ${config.krebs.build.host.name}";
+          root-desc = "keep calm and engage";
+        };
+      };
       repos = mapAttrs (_: s: removeAttrs s ["collaborators"]) repos;
       rules = rules;
     };
@@ -27,7 +31,7 @@ let
   public-repos = mapAttrs make-public-repo {
     painload = {};
     stockholm = {
-      desc = "take all the computers hostage, they'll love you!";
+      cgit.desc = "take all the computers hostage, they'll love you!";
     };
     #wai-middleware-time = {};
     #web-routes-wai-custom = {};
@@ -46,8 +50,8 @@ let
     import <secrets/repos.nix> { inherit config lib pkgs; }
   );
 
-  make-public-repo = name: { desc ? null, ... }: {
-    inherit name desc;
+  make-public-repo = name: { cgit ? {}, ... }: {
+    inherit cgit name;
     public = true;
     hooks = {
       post-receive = pkgs.git-hooks.irc-announce {
@@ -60,8 +64,8 @@ let
     };
   };
 
-  make-restricted-repo = name: { collaborators ? [], desc ? null, ... }: {
-    inherit name collaborators desc;
+  make-restricted-repo = name: { collaborators ? [], ... }: {
+    inherit collaborators name;
     public = false;
   };
 
diff --git a/mv/2configs/git.nix b/mv/2configs/git.nix
index 933649f20..aee448cb6 100644
--- a/mv/2configs/git.nix
+++ b/mv/2configs/git.nix
@@ -7,8 +7,12 @@ let
   out = {
     krebs.git = {
       enable = true;
-      root-title = "public repositories at ${config.krebs.build.host.name}";
-      root-desc = "Hmhmh, im Moment nicht.";
+      cgit = {
+        settings = {
+          root-title = "public repositories at ${config.krebs.build.host.name}";
+          root-desc = "Hmhmh, im Moment nicht.";
+        };
+      };
       repos = mapAttrs (_: s: removeAttrs s ["collaborators"]) repos;
       rules = rules;
     };
@@ -22,8 +26,8 @@ let
     stockholm = {};
   };
 
-  make-public-repo = name: { desc ? null, section ? null, ... }: {
-    inherit name desc section;
+  make-public-repo = name: { cgit ? {}, ... }: {
+    inherit cgit name;
     public = true;
     hooks = {
       post-receive = pkgs.git-hooks.irc-announce {
diff --git a/shared/2configs/cgit-mirror.nix b/shared/2configs/cgit-mirror.nix
index b984535c9..d9241a2b5 100644
--- a/shared/2configs/cgit-mirror.nix
+++ b/shared/2configs/cgit-mirror.nix
@@ -11,7 +11,7 @@ let
   stockholm-mirror = {
     public = true;
     name = "stockholm-mirror";
-    desc = "mirror for all stockholm branches";
+    cgit.desc = "mirror for all stockholm branches";
     hooks = {
       post-receive = pkgs.git-hooks.irc-announce {
         nick = config.networking.hostName;
@@ -33,8 +33,12 @@ in {
   krebs.users.wolf-repo-sync = wolf-repo-sync;
   krebs.git = {
     enable = true;
-    root-title = "Shared Repos";
-    root-desc = "keep on krebsing";
+    cgit = {
+      settings = {
+        root-title = "Shared Repos";
+        root-desc = "keep on krebsing";
+      };
+    };
     inherit rules;
     repos.stockholm-mirror = stockholm-mirror;
   };
diff --git a/tv/2configs/default.nix b/tv/2configs/default.nix
index df5d5da29..741955eee 100644
--- a/tv/2configs/default.nix
+++ b/tv/2configs/default.nix
@@ -14,7 +14,7 @@ with config.krebs.lib;
       stockholm = "/home/tv/stockholm";
       nixpkgs = {
         url = https://github.com/NixOS/nixpkgs;
-        rev = "40c586b7ce2c559374df435f46d673baf711c543";
+        rev = "87fe38fd0e19ca83fc3ea338f8e0e7b12971d204";
       };
     } // optionalAttrs config.krebs.build.host.secure {
       secrets-master = "/home/tv/secrets/master";
diff --git a/tv/2configs/git.nix b/tv/2configs/git.nix
index 2e5fc301b..9bcf8f31f 100644
--- a/tv/2configs/git.nix
+++ b/tv/2configs/git.nix
@@ -7,8 +7,12 @@ let
   out = {
     krebs.git = {
       enable = true;
-      root-title = "repositories at ${config.krebs.build.host.name}";
-      root-desc = "mostly krebs";
+      cgit = {
+        settings = {
+          root-title = "repositories at ${config.krebs.build.host.name}";
+          root-desc = "mostly krebs";
+        };
+      };
       repos = repos;
       rules = rules;
     };
@@ -21,9 +25,9 @@ let
   rules = concatMap make-rules (attrValues repos);
 
   public-repos = mapAttrs make-public-repo ({
-  } // mapAttrValues (setAttr "section" "1. miscellaneous") {
+  } // mapAttrs (_: recursiveUpdate { cgit.section = "1. miscellaneous"; }) {
     cac-api = {
-      desc = "CloudAtCost API command line interface";
+      cgit.desc = "CloudAtCost API command line interface";
     };
     get = {};
     hack = {};
@@ -35,13 +39,13 @@ let
     push = {};
     regfish = {};
     soundcloud = {
-      desc = "SoundCloud command line interface";
+      cgit.desc = "SoundCloud command line interface";
     };
     stockholm = {
-      desc = "NixOS configuration";
+      cgit.desc = "NixOS configuration";
     };
     with-tmpdir = {};
-  } // mapAttrValues (setAttr "section" "2. Haskell libraries") {
+  } // mapAttrs (_: recursiveUpdate { cgit.section = "2. Haskell libraries"; }) {
     blessings = {};
     mime = {};
     quipper = {};
@@ -50,7 +54,7 @@ let
     web-routes-wai-custom = {};
     xintmap = {};
     xmonad-stockholm = {};
-  } // mapAttrValues (setAttr "section" "3. museum") {
+  } // mapAttrs (_: recursiveUpdate { cgit.section = "3. museum"; }) {
     cgserver = {};
     crude-mail-setup = {};
     dot-xmonad = {};
@@ -68,8 +72,8 @@ let
     import <secrets/repos.nix> { inherit config lib pkgs; }
   );
 
-  make-public-repo = name: { desc ? null, section ? null, ... }: {
-    inherit name desc section;
+  make-public-repo = name: { cgit ? {}, ... }: {
+    inherit cgit name;
     public = true;
     hooks = optionalAttrs (config.krebs.build.host.name == "cd") {
       post-receive = pkgs.git-hooks.irc-announce {
@@ -82,8 +86,8 @@ let
     };
   };
 
-  make-restricted-repo = name: { collaborators ? [], desc ? null, ... }: {
-    inherit name collaborators desc;
+  make-restricted-repo = name: { collaborators ? [], ... }: {
+    inherit collaborators name;
     public = false;
   };
 
diff --git a/tv/2configs/vim.nix b/tv/2configs/vim.nix
index 10e5aab52..0d87d97af 100644
--- a/tv/2configs/vim.nix
+++ b/tv/2configs/vim.nix
@@ -23,6 +23,84 @@ let
         sha256 = "0z47zq9rqh06ny0q8lpcdsraf3lyzn9xvb59nywnarf3nxrk6hx0";
       };
     })
+    ((rtp: rtp // { inherit rtp; }) (pkgs.writeTextFile (let
+      name = "hack";
+    in {
+      name = "vim-color-${name}-1.0.2";
+      destination = "/colors/${name}.vim";
+      text = /* vim */ ''
+        set background=dark
+        hi clear
+        if exists("syntax_on")
+          syntax clear
+        endif
+
+        let colors_name = ${toJSON name}
+
+        hi Normal       ctermbg=235
+        hi Comment      ctermfg=242
+        hi Constant     ctermfg=255
+        hi Identifier   ctermfg=253
+        hi Function     ctermfg=253
+        hi Statement    ctermfg=253
+        hi PreProc      ctermfg=251
+        hi Type         ctermfg=251
+        hi Delimiter    ctermfg=251
+        hi Special      ctermfg=255
+
+        hi Garbage      ctermbg=088
+        hi TabStop      ctermbg=016
+        hi Todo         ctermfg=174 ctermbg=NONE
+
+        hi NixCode      ctermfg=040
+        hi NixData      ctermfg=046
+        hi NixQuote     ctermfg=071
+
+        hi diffNewFile  ctermfg=207
+        hi diffFile     ctermfg=207
+        hi diffLine     ctermfg=207
+        hi diffSubname  ctermfg=207
+        hi diffAdded    ctermfg=010
+        hi diffRemoved  ctermfg=009
+      '';
+    })))
+    ((rtp: rtp // { inherit rtp; }) (pkgs.writeTextFile (let
+      name = "vim";
+    in {
+      name = "vim-syntax-${name}-1.0.0";
+      destination = "/syntax/${name}.vim";
+      text = /* vim */ ''
+        ${concatMapStringsSep "\n" (s: /* vim */ ''
+          syn keyword vimColor${s} ${s}
+            \ containedin=ALLBUT,vimComment,vimLineComment
+          hi vimColor${s} ctermfg=${s}
+        '') (map (i: lpad 3 "0" (toString i)) (range 0 255))}
+      '';
+    })))
+    ((rtp: rtp // { inherit rtp; }) (pkgs.writeTextFile (let
+      name = "showsyntax";
+    in {
+      name = "vim-plugin-${name}-1.0.0";
+      destination = "/plugin/${name}.vim";
+      text = /* vim */ ''
+        if exists('g:loaded_showsyntax')
+          finish
+        endif
+        let g:loaded_showsyntax = 0
+
+        fu! ShowSyntax()
+          let id = synID(line("."), col("."), 1)
+          let name = synIDattr(id, "name")
+          let transName = synIDattr(synIDtrans(id),"name")
+          if name != transName
+            let name .= " (" . transName . ")"
+          endif
+          echo "Syntax: " . name
+        endfu
+
+        command! -n=0 -bar ShowSyntax :call ShowSyntax()
+      '';
+    })))
   ];
 
   dirs = {
@@ -79,47 +157,16 @@ let
     filetype plugin indent on
 
     set t_Co=256
-    colorscheme industry
+    colorscheme hack
     syntax on
 
-    au Syntax * syn match Tabstop containedin=ALL /\t\+/
-            \ | hi Tabstop ctermbg=16
-            \ | syn match TrailingSpace containedin=ALL /\s\+$/
-            \ | hi TrailingSpace ctermbg=88
-            \ | hi Normal ctermfg=White
+    au Syntax * syn match Garbage containedin=ALL /\s\+$/
+            \ | syn match TabStop containedin=ALL /\t\+/
+            \ | syn keyword Todo containedin=ALL TODO
 
-    au BufRead,BufNewFile *.hs so ${pkgs.writeText "hs.vim" ''
-      syn region String start=+\[[[:alnum:]]*|+ end=+|]+
-    ''}
+    au BufRead,BufNewFile *.hs so ${hs.vim}
 
-    au BufRead,BufNewFile *.nix so ${pkgs.writeText "nix.vim" ''
-      setf nix
-      set isk=@,48-57,_,192-255,-,'
-
-      " Ref <nix/src/libexpr/lexer.l>
-      syn match INT   /\<[0-9]\+\>/
-      syn match PATH  /[a-zA-Z0-9\.\_\-\+]*\(\/[a-zA-Z0-9\.\_\-\+]\+\)\+/
-      syn match HPATH /\~\(\/[a-zA-Z0-9\.\_\-\+]\+\)\+/
-      syn match SPATH /<[a-zA-Z0-9\.\_\-\+]\+\(\/[a-zA-Z0-9\.\_\-\+]\+\)*>/
-      syn match URI   /[a-zA-Z][a-zA-Z0-9\+\-\.]*:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']\+/
-      hi link INT Constant
-      hi link PATH Constant
-      hi link HPATH Constant
-      hi link SPATH Constant
-      hi link URI Constant
-
-      syn match String /"\([^\\"]\|\\.\)*"/
-      syn match Comment /\(^\|\s\)#.*/
-
-      " Haskell comments
-      syn region Comment start=/\(^\|\s\){-#/ end=/#-}/
-      syn match Comment /\(^\|\s\)--.*/
-
-      " Vim comments
-      syn match Comment /\(^\|\s\)"[^"]*$/
-
-      let b:current_syntax = "nix"
-    ''}
+    au BufRead,BufNewFile *.nix so ${nix.vim}
 
     au BufRead,BufNewFile /dev/shm/* set nobackup nowritebackup noswapfile
 
@@ -152,5 +199,130 @@ let
     noremap <esc>[d <nop> | noremap! <esc>[d <nop>
     vnoremap u <nop>
   '';
+
+  hs.vim = pkgs.writeText "hs.vim" ''
+    syn region String start=+\[[[:alnum:]]*|+ end=+|]+
+
+    hi link ConId Identifier
+    hi link VarId Identifier
+    hi link hsDelimiter Delimiter
+  '';
+
+  nix.vim = pkgs.writeText "nix.vim" ''
+    setf nix
+
+    syn match NixCode /./
+
+    " Ref <nix/src/libexpr/lexer.l>
+    syn match NixINT   /\<[0-9]\+\>/
+    syn match NixPATH  /[a-zA-Z0-9\.\_\-\+]*\(\/[a-zA-Z0-9\.\_\-\+]\+\)\+/
+    syn match NixHPATH /\~\(\/[a-zA-Z0-9\.\_\-\+]\+\)\+/
+    syn match NixSPATH /<[a-zA-Z0-9\.\_\-\+]\+\(\/[a-zA-Z0-9\.\_\-\+]\+\)*>/
+    syn match NixURI   /[a-zA-Z][a-zA-Z0-9\+\-\.]*:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']\+/
+    syn region NixSTRING
+      \ matchgroup=NixSTRING
+      \ start='"'
+      \ skip='\\"'
+      \ end='"'
+    syn region NixIND_STRING
+      \ matchgroup=NixIND_STRING
+      \ start="'''"
+      \ skip="'''\('\|[$]\|\\[nrt]\)"
+      \ end="'''"
+
+    syn cluster NixStrings contains=NixSTRING,NixIND_STRING
+
+    syn match NixCommentMatch /\(^\|\s\)#.*/
+    syn region NixCommentRegion start="/\*" end="\*/"
+
+    hi link NixCode Statement
+    hi link NixData Constant
+    hi link NixComment Comment
+
+    hi link NixCommentMatch NixComment
+    hi link NixCommentRegion NixComment
+    hi link NixINT NixData
+    hi link NixPATH NixData
+    hi link NixHPATH NixData
+    hi link NixSPATH NixData
+    hi link NixURI NixData
+    hi link NixSTRING NixData
+    hi link NixIND_STRING NixData
+
+    hi link NixEnter NixCode
+    hi link NixExit NixData
+    hi link NixQuote NixData
+    hi link NixQuote2 NixQuote
+    hi link NixQuote3 NixQuote
+
+    syn cluster NixSubLangs contains=NONE
+
+    ${concatStringsSep "\n" (mapAttrsToList (lang: { extraStart ? null }: let
+      startAlts = filter isString [
+        ''/\* ${lang} \*/''
+        extraStart
+      ];
+      sigil = ''\(${concatStringsSep ''\|'' startAlts}\)[ \t\r\n]*'';
+    in /* vim */ ''
+      syn include @${lang}Syntax syntax/${lang}.vim
+      unlet b:current_syntax
+
+      syn region ${lang}Block_NixSTRING
+        \ matchgroup=NixExit
+        \ extend
+        \ start='${replaceStrings ["'"] ["\\'"] sigil}"'
+        \ skip='\\"'
+        \ end='"'
+        \ contains=@${lang}Syntax
+
+      syn region ${lang}Block_NixIND_STRING
+        \ matchgroup=NixExit
+        \ extend
+        \ start="${replaceStrings ["\""] ["\\\""] sigil}'''"
+        \ skip="'''\('\|[$]\|\\[nrt]\)"
+        \ end="'''"
+        \ contains=@${lang}Syntax
+
+      syn cluster NixSubLangs
+        \ add=@${lang}Syntax,${lang}Block_NixSTRING,${lang}Block_NixIND_STRING
+
+      hi link ${lang}Block_NixSTRING Statement
+      hi link ${lang}Block_NixIND_STRING Statement
+    '') {
+      c = {};
+      cabal = {};
+      haskell = {};
+      sh.extraStart = ''write\(Ba\|Da\)sh[^ \t\r\n]*[ \t\r\n]*"[^"]*"'';
+      vim.extraStart =
+        ''write[^ \t\r\n]*[ \t\r\n]*"\(\([^"]*\.\)\?vimrc\|[^"]*\.vim\)"'';
+    })}
+
+    " Clear syntax that interferes with NixBlock.
+    " TODO redefine NixBlock so syntax don't have to be cleared
+    syn clear shOperator shSetList shVarAssign
+
+    syn region NixBlock
+      \ matchgroup=NixEnter
+      \ start="[$]{"
+      \ end="}"
+      \ contains=TOP
+      \ containedin=@NixSubLangs,@NixStrings
+
+    syn region NixBlockHack
+      \ matchgroup=NixEnter
+      \ start="{"
+      \ end="}"
+      \ contains=TOP
+
+    syn match NixQuote  "'''[$]"he=e-1  contained containedin=@NixSubLangs
+    syn match NixQuote2 "''''"he=s+1    contained containedin=@NixSubLangs
+    syn match NixQuote3 "'''\\[nrt]"    contained containedin=@NixSubLangs
+
+    syn sync fromstart
+
+    let b:current_syntax = "nix"
+
+    set isk=@,48-57,_,192-255,-,'
+  '';
 in
 out
diff --git a/tv/5pkgs/q/default.nix b/tv/5pkgs/q/default.nix
index 1d08a5447..eeb600300 100644
--- a/tv/5pkgs/q/default.nix
+++ b/tv/5pkgs/q/default.nix
@@ -13,10 +13,10 @@ let
         | ${pkgs.gawk}/bin/awk '{printf "%-23s\n", $0}' \
         | ${pkgs.gnused}/bin/sed '
               # colorize header
-              1,2s/.*/&/
+              1,2s/.*/&/
 
               # colorize week number
-              s/^[ 1-9][0-9]/&/
+              s/^[ 1-9][0-9]/&/
             '
     }'';
   in ''