diff --git a/default.nix b/default.nix
index c78464198..472d7597d 100644
--- a/default.nix
+++ b/default.nix
@@ -31,9 +31,13 @@ let stockholm = {
   kpath = lib.nspath "krebs";
   upath = lib.nspath current-user-name;
 
-  base-module = {
+  base-module = { config, ... }: {
     imports = map (f: f "3modules") [ kpath upath ];
 
+    krebs.current.enable = true;
+    krebs.current.host = config.krebs.hosts.${current-host-name};
+    krebs.current.user = config.krebs.users.${current-user-name};
+
     nixpkgs.config.packageOverrides = pkgs:
       let
         kpkgs = import (kpath "5pkgs") { inherit lib pkgs; };
diff --git a/krebs/3modules/current.nix b/krebs/3modules/current.nix
new file mode 100644
index 000000000..41941e289
--- /dev/null
+++ b/krebs/3modules/current.nix
@@ -0,0 +1,26 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.krebs.current;
+
+  out = {
+    options.krebs.current = api;
+    config = mkIf cfg.enable imp;
+  };
+
+  api = {
+    enable = mkEnableOption "krebs.current";
+    host = mkOption {
+      type = types.host;
+    };
+    user = mkOption {
+      type = types.user;
+    };
+  };
+
+  imp = {
+  };
+
+in out
diff --git a/krebs/3modules/default.nix b/krebs/3modules/default.nix
index e0c3b00f8..fd9d56ed2 100644
--- a/krebs/3modules/default.nix
+++ b/krebs/3modules/default.nix
@@ -8,6 +8,7 @@ let
     imports = [
       ./bepasty-server.nix
       ./build.nix
+      ./current.nix
       ./exim-retiolum.nix
       ./exim-smarthost.nix
       ./github-hosts-sync.nix
@@ -76,6 +77,7 @@ let
   imp = mkMerge [
     { krebs = import ./lass { inherit lib; }; }
     { krebs = import ./makefu { inherit lib; }; }
+    { krebs = import ./shared { inherit lib; }; }
     { krebs = import ./tv { inherit lib; }; }
     {
       krebs.dns.providers = {
diff --git a/krebs/3modules/shared/default.nix b/krebs/3modules/shared/default.nix
new file mode 100644
index 000000000..24dd7b782
--- /dev/null
+++ b/krebs/3modules/shared/default.nix
@@ -0,0 +1,42 @@
+{ lib, ... }:
+
+with lib;
+
+{
+  hosts = addNames {
+    wolf = {
+      #dc = "shack";
+      nets = {
+        #shack = {
+        #  addrs4 = [ TODO ];
+        #  aliases = ["wolf.shack"];
+        #};
+        retiolum = {
+          addrs4 = ["10.243.77.1"];
+          addrs6 = ["42:0:0:0:0:0:77:1"];
+          aliases = [
+            "wolf.retiolum"
+          ];
+          tinc.pubkey = ''
+            -----BEGIN RSA PUBLIC KEY-----
+            MIIBCgKCAQEAzpXyEATt8+ElxPq650/fkboEC9RvTWqN6UIAl/R4Zu+uDhAZ2ekb
+            HBjoSbRxu/0w2I37nwWUhEOemxGm4PXCgWrtO0jeRF4nVNYu3ZBppA3vuVALUWq7
+            apxRUEL9FdsWQlXGo4PVd20dGaDTi8M/Ggo755MStVTY0rRLluxyPq6VAa015sNg
+            4NOFuWm0NDn4e+qrahTCTiSjbCU8rWixm0GktV40kdg0QAiFbEcRhuXF1s9/yojk
+            7JT/nFg6LELjWUSSNZnioj5oSfVbThDRelIld9VaAKBAZZ5/zy6T2XSeDfoepytH
+            8aw6itEuTCy1M1DTiTG+12SPPw+ubG+NqQIDAQAB
+            -----END RSA PUBLIC KEY-----
+          '';
+        };
+      };
+      ssh.privkey.path = <secrets/ssh.id_ed25519>;
+      ssh.pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKYMXMWZIK0jjnZDM9INiYAKcwjXs2241vew54K8veCR";
+    };
+  };
+  users = addNames {
+    shared = {
+      mail = "spam@krebsco.de";
+      pubkey = "lol"; # TODO krebs.users.shared.pubkey should be unnecessary
+    };
+  };
+}
diff --git a/krebs/5pkgs/default.nix b/krebs/5pkgs/default.nix
index 0ec4b3ded..7df7b7d3c 100644
--- a/krebs/5pkgs/default.nix
+++ b/krebs/5pkgs/default.nix
@@ -54,4 +54,8 @@ subdirs // rec {
     gcc -O -Wall -o "$exe" $src
     strip --strip-unneeded "$exe"
   '';
+
+  writeNixFromCabal = name: path: pkgs.runCommand name {} ''
+    ${pkgs.cabal2nix}/bin/cabal2nix ${path} > $out
+  '';
 }
diff --git a/krebs/Zhosts/wolf b/krebs/Zhosts/wolf
new file mode 100644
index 000000000..ded8275bd
--- /dev/null
+++ b/krebs/Zhosts/wolf
@@ -0,0 +1,10 @@
+Subnet = 10.243.77.1/32
+Subnet = 42:0:0:0:0:0:77:1/128
+-----BEGIN RSA PUBLIC KEY-----
+MIIBCgKCAQEAzpXyEATt8+ElxPq650/fkboEC9RvTWqN6UIAl/R4Zu+uDhAZ2ekb
+HBjoSbRxu/0w2I37nwWUhEOemxGm4PXCgWrtO0jeRF4nVNYu3ZBppA3vuVALUWq7
+apxRUEL9FdsWQlXGo4PVd20dGaDTi8M/Ggo755MStVTY0rRLluxyPq6VAa015sNg
+4NOFuWm0NDn4e+qrahTCTiSjbCU8rWixm0GktV40kdg0QAiFbEcRhuXF1s9/yojk
+7JT/nFg6LELjWUSSNZnioj5oSfVbThDRelIld9VaAKBAZZ5/zy6T2XSeDfoepytH
+8aw6itEuTCy1M1DTiTG+12SPPw+ubG+NqQIDAQAB
+-----END RSA PUBLIC KEY-----
diff --git a/shared/1systems/wolf.nix b/shared/1systems/wolf.nix
new file mode 100644
index 000000000..aeaeee288
--- /dev/null
+++ b/shared/1systems/wolf.nix
@@ -0,0 +1,106 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  imports = [
+    <nixpkgs/nixos/modules/profiles/qemu-guest.nix>
+  ];
+
+  krebs.build.host = config.krebs.hosts.wolf;
+  # TODO rename shared user to "krebs"
+  krebs.build.user = config.krebs.users.shared;
+  krebs.build.target = "wolf";
+
+  krebs.enable = true;
+  krebs.retiolum = {
+    enable = true;
+    connectTo = [
+      # TODO remove connectTo cd, this was only used for bootstrapping
+      "cd"
+      "gum"
+      "pigstarter"
+    ];
+  };
+
+  krebs.build.source = {
+    git.nixpkgs = {
+      url = https://github.com/NixOS/nixpkgs;
+      rev = "e916273209560b302ab231606babf5ce1c481f08";
+    };
+    dir.secrets = {
+      host = config.krebs.current.host;
+      path = "${getEnv "HOME"}/secrets/krebs/wolf";
+    };
+    dir.stockholm = {
+      host = config.krebs.current.host;
+      path = "${getEnv "HOME"}/stockholm";
+    };
+  };
+
+  networking.hostName = config.krebs.build.host.name;
+
+  boot.kernel.sysctl = {
+    # Enable IPv6 Privacy Extensions
+    "net.ipv6.conf.all.use_tempaddr" = 2;
+    "net.ipv6.conf.default.use_tempaddr" = 2;
+  };
+
+  boot.initrd.availableKernelModules = [
+    "ata_piix" "uhci_hcd" "ehci_pci" "virtio_pci" "virtio_blk"
+  ];
+  boot.kernelModules = [ ];
+  boot.extraModulePackages = [ ];
+
+  boot.loader.grub.enable = true;
+  boot.loader.grub.version = 2;
+  boot.loader.grub.device = "/dev/vda";
+
+  fileSystems."/" = { device = "/dev/disk/by-label/nixos"; fsType = "ext4"; };
+
+  swapDevices = [
+    { device = "/dev/disk/by-label/swap"; }
+  ];
+
+  nix.maxJobs = 1;
+  nix.trustedBinaryCaches = [
+    "https://cache.nixos.org"
+    "http://cache.nixos.org"
+    "http://hydra.nixos.org"
+  ];
+  nix.useChroot = true;
+
+  nixpkgs.config.packageOverrides = pkgs: {
+    nano = pkgs.vim;
+  };
+
+  environment.systemPackages = with pkgs; [
+    git
+    rxvt_unicode.terminfo
+  ];
+
+  time.timeZone = "Europe/Berlin";
+
+  programs.ssh.startAgent = false;
+
+  services.openssh = {
+    enable = true;
+    hostKeys = [
+      { type = "ed25519"; path = "/etc/ssh/ssh_host_ed25519_key"; }
+    ];
+  };
+  services.cron.enable = false;
+  services.nscd.enable = false;
+  services.ntp.enable = false;
+
+  users.mutableUsers = false;
+  users.extraUsers.root.openssh.authorizedKeys.keys = [
+    # TODO
+    config.krebs.users.lass.pubkey
+    config.krebs.users.makefu.pubkey
+    config.krebs.users.tv.pubkey
+  ];
+
+  # The NixOS release to be compatible with for stateful data such as databases.
+  system.stateVersion = "15.09";
+}
diff --git a/shared/3modules/default.nix b/shared/3modules/default.nix
new file mode 100644
index 000000000..7fbdb77f4
--- /dev/null
+++ b/shared/3modules/default.nix
@@ -0,0 +1,5 @@
+# TODO don't require 3modules
+_:
+
+{
+}
diff --git a/shared/5pkgs/default.nix b/shared/5pkgs/default.nix
new file mode 100644
index 000000000..fdcfbb209
--- /dev/null
+++ b/shared/5pkgs/default.nix
@@ -0,0 +1,5 @@
+# TODO don't require 5pkgs
+_:
+
+{
+}
diff --git a/tv/1systems/wu.nix b/tv/1systems/wu.nix
index a5232c9e1..586ad1725 100644
--- a/tv/1systems/wu.nix
+++ b/tv/1systems/wu.nix
@@ -30,7 +30,6 @@ with lib;
     ../2configs/git.nix
     ../2configs/mail-client.nix
     ../2configs/xserver
-    ../2configs/test.nix
     {
       environment.systemPackages = with pkgs; [
 
diff --git a/tv/2configs/test.nix b/tv/2configs/test.nix
deleted file mode 100644
index f5f068d6f..000000000
--- a/tv/2configs/test.nix
+++ /dev/null
@@ -1,31 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-  out = {
-    environment.systemPackages = [
-      su-test
-    ];
-    security.sudo.extraConfig = ''
-      tv ALL=(test) NOPASSWD: ALL
-    '';
-    users.extraUsers.test = {
-      shell = "${test-shell}";
-    };
-  };
-
-  su-test = pkgs.execveBin "su-test" rec {
-    filename = "/var/setuid-wrappers/sudo";
-    argv = ["sudo" "-u" "test" "-i"];
-  };
-
-  test-shell = pkgs.execve "test-shell" rec {
-    filename = "${pkgs.bash}/bin/bash";
-    argv = ["sh" "--noprofile" "-l"];
-    envp.ENV = pkgs.writeText "test-env" ''
-      ${shell.cat "Hello, `$(j0w\nd0g!)`!\\o/\n"} >&2
-    '';
-  };
-
-in out
diff --git a/tv/2configs/xserver/default.nix b/tv/2configs/xserver/default.nix
index 5d3372609..c5cffbb30 100644
--- a/tv/2configs/xserver/default.nix
+++ b/tv/2configs/xserver/default.nix
@@ -44,11 +44,14 @@ let
     systemd.services.display-manager = mkForce {};
 
     services.xserver.enable = true;
+
     systemd.services.xmonad = {
       wantedBy = [ "multi-user.target" ];
       requires = [ "xserver.service" ];
+      environment = xmonad-environment;
       serviceConfig = {
-        ExecStart = "${xmonad}/bin/xmonad";
+        ExecStart = "${xmonad-start}/bin/xmonad";
+        ExecStop = "${xmonad-stop}/bin/xmonad-stop";
         User = user.name;
         WorkingDirectory = user.home;
       };
@@ -69,15 +72,30 @@ let
     };
   };
 
-  xmonad = let
-    pkg = pkgs.haskellPackages.callPackage src {};
-    src = pkgs.runCommand "xmonad-package" {} ''
-      ${pkgs.cabal2nix}/bin/cabal2nix ${./xmonad} > $out
-    '';
-  in pkgs.writeScriptBin "xmonad" ''
-    #! /bin/sh
+  xmonad-pkg = pkgs.haskellPackages.callPackage xmonad-src {};
+  xmonad-src = pkgs.writeNixFromCabal "xmonad.nix" ./xmonad;
+
+  xmonad-environment = {
+    DISPLAY = ":${toString config.services.xserver.display}";
+    XMONAD_STATE = "/tmp/xmonad.state";
+
+    # XXX JSON is close enough :)
+    XMONAD_WORKSPACES0_FILE = pkgs.writeText "xmonad.workspaces0" (toJSON [
+      "Dashboard" # we start here
+      "23"
+      "cr"
+      "ff"
+      "hack"
+      "im"
+      "mail"
+      "stockholm"
+      "za" "zj" "zs"
+    ]);
+  };
+
+  xmonad-start = pkgs.writeScriptBin "xmonad" ''
+    #! ${pkgs.bash}/bin/bash
     set -efu
-    export DISPLAY; DISPLAY=:${toString config.services.xserver.display}
     export PATH; PATH=${makeSearchPath "bin" [
       pkgs.rxvt_unicode
     ]}:/var/setuid-wrappers
@@ -93,7 +111,17 @@ let
     settle ${pkgs.xorg.xhost}/bin/xhost +LOCAL:
     settle ${pkgs.xorg.xrdb}/bin/xrdb -merge ${import ./Xresources.nix args}
     settle ${pkgs.xorg.xsetroot}/bin/xsetroot -solid '#1c1c1c'
-    exec ${pkg}/bin/xmonad
+    if test -e "$XMONAD_STATE"; then
+      IFS=''$'\n'
+      exec ${xmonad-pkg}/bin/xmonad --resume $(< "$XMONAD_STATE")
+    else
+      exec ${xmonad-pkg}/bin/xmonad
+    fi
+  '';
+
+  xmonad-stop = pkgs.writeScriptBin "xmonad-stop" ''
+    #! /bin/sh
+    exec ${xmonad-pkg}/bin/xmonad --shutdown
   '';
 
   xserver-environment = {
@@ -103,7 +131,7 @@ let
       [ "${pkgs.xorg.libX11}/lib" "${pkgs.xorg.libXext}/lib" ]
       ++ concatLists (catAttrs "libPath" config.services.xserver.drivers));
   };
-      
+
   xserver = pkgs.writeScriptBin "xserver" ''
     #! /bin/sh
     set -efu
diff --git a/tv/2configs/xserver/xmonad/.gitignore b/tv/2configs/xserver/xmonad/.gitignore
new file mode 100644
index 000000000..616204547
--- /dev/null
+++ b/tv/2configs/xserver/xmonad/.gitignore
@@ -0,0 +1 @@
+/shell.nix
diff --git a/tv/2configs/xserver/xmonad/Main.hs b/tv/2configs/xserver/xmonad/Main.hs
index cca2902a0..186a5e22c 100644
--- a/tv/2configs/xserver/xmonad/Main.hs
+++ b/tv/2configs/xserver/xmonad/Main.hs
@@ -1,9 +1,14 @@
 {-# LANGUAGE DeriveDataTypeable #-} -- for XS
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE ScopedTypeVariables #-}
 
 
 module Main where
 
+import Control.Exception
+import Text.Read (readEither)
 import XMonad
+import System.Environment (getArgs, getEnv)
 import XMonad.Prompt (defaultXPConfig)
 import XMonad.Actions.DynamicWorkspaces ( addWorkspacePrompt, renameWorkspace
                                         , removeEmptyWorkspace)
@@ -30,29 +35,26 @@ import XMonad.Layout.PerWorkspace (onWorkspace)
 import Util.Pager
 import Util.Rhombus
 import Util.Debunk
+import Util.Shutdown
 
 
---data MyState = MyState deriving Typeable
-
 myTerm :: String
 myTerm = "urxvtc"
 
 myRootTerm :: String
 myRootTerm = "urxvtc -name root-urxvt -e su -"
 
--- TODO execRootTerm = exec (shlex "urxvtc -e su -")
---    [ ("XENVIRONMENT", HOME ++ "/.Xdefaults/root-urxvt") ]
-
-
 myFont :: String
 myFont = "-schumacher-*-*-*-*-*-*-*-*-*-*-*-iso10646-*"
 
 main :: IO ()
-main = do
-    -- TODO exec (shlex "xrdb -merge" ++ [HOME ++ "/.Xresources"])
-    -- TODO exec (shlex "xsetroot -solid '#1c1c1c'")
-    --spawn "xrdb -merge \"$HOME/.Xresources\""
-    --spawn "xsetroot -solid '#1c1c1c'"
+main = getArgs >>= \case
+    ["--shutdown"] -> sendShutdownEvent
+    _ -> mainNoArgs
+
+mainNoArgs :: IO ()
+mainNoArgs = do
+    workspaces0 <- getWorkspaces0
     xmonad
         -- $ withUrgencyHookC dzenUrgencyHook { args = ["-bg", "magenta", "-fg", "magenta", "-h", "2"], duration = 500000 }
         --                   urgencyConfig { remindWhen = Every 1 }
@@ -63,16 +65,7 @@ main = do
             { terminal          = myTerm
             , modMask           = mod4Mask
             , keys              = myKeys
-            , workspaces        =
-                [ "Dashboard" -- we start here
-                , "23"
-                , "cr"
-                , "ff"
-                , "hack"
-                , "im"
-                , "mail"
-                , "zalora", "zjournal", "zskype"
-                ]
+            , workspaces        = workspaces0
             , layoutHook        = smartBorders $ myLayout
             -- , handleEventHook   = myHandleEventHooks <+> handleTimerEvent
             --, handleEventHook   = handleTimerEvent
@@ -80,6 +73,7 @@ main = do
             , startupHook       = spawn "echo emit XMonadStartup"
             , normalBorderColor  = "#1c1c1c"
             , focusedBorderColor = "#f000b0"
+            , handleEventHook = handleShutdownEvent
             }
   where
     myLayout =
@@ -87,39 +81,31 @@ main = do
         (FixedColumn 1 20 80 10 ||| Full)
 
 
+getWorkspaces0 :: IO [String]
+getWorkspaces0 =
+    try (getEnv "XMONAD_WORKSPACES0_FILE") >>= \case
+      Left e -> warn (displaySomeException e)
+      Right p -> try (readFile p) >>= \case
+        Left e -> warn (displaySomeException e)
+        Right x -> case readEither x of
+          Left e -> warn e
+          Right y -> return y
+  where
+    warn msg = putStrLn ("getWorkspaces0: " ++ msg) >> return []
+
+displaySomeException :: SomeException -> String
+displaySomeException = displayException
+
+
 spawnTermAt :: String -> X ()
 --spawnTermAt _ = floatNext True >> spawn myTerm
 --spawnTermAt "ff" = floatNext True >> spawn myTerm
 spawnTermAt _    = spawn myTerm
 
 
-
---jojo w = withDisplay $ \d -> liftIO $ do
---    wa <- getWindowAttributes d w
---    printToErrors (wa_width wa, wa_height wa, wa_x wa, wa_y wa)
-
-    --sh <- getWMNormalHints d w
-    --bw <- fmap (fi . wa_border_width) $ getWindowAttributes d w
-    --return $ applySizeHints bw sh
-
-
---data WindowDetails = WindowDetails
---    { wd_name :: Maybe String
---    , wd_rect :: Rectangle
---    } deriving (Show)
-
--- urxvtc
---  -title sets {,_NET_}WM_NAME but not WM_CLASS and {,_NET_}WM_ICON_NAME       res: title
---  -name sets all                                                              res: 
---mySpawn cmd = do
---    p <- xfork $ executeFile "/run/current-system/sw/bin/urxvtc" False [] Nothing
---    liftIO $ printToErrors $ (cmd, p)
-
-
 myKeys :: XConfig Layout -> Map (KeyMask, KeySym) (X ())
 myKeys conf = Map.fromList $
-    [ ((_4C , xK_Delete ), spawn "make -C $HOME/.xmonad reload")
-    , ((_4  , xK_Escape ), spawn "/var/setuid-wrappers/slock")
+    [ ((_4  , xK_Escape ), spawn "/var/setuid-wrappers/slock")
     , ((_4S , xK_c      ), kill)
 
     , ((_4  , xK_x      ), chooseAction spawnTermAt)
@@ -273,5 +259,3 @@ wGSConfig = defaultGSConfig
 allWorkspaceNames :: W.StackSet i l a sid sd -> X [i]
 allWorkspaceNames ws =
     return $ map W.tag (W.hidden ws) ++ [W.tag $ W.workspace $ W.current ws]
-
--- vim:set fdm=marker:
diff --git a/tv/2configs/xserver/xmonad/Util/Shutdown.hs b/tv/2configs/xserver/xmonad/Util/Shutdown.hs
new file mode 100644
index 000000000..c5a3edb80
--- /dev/null
+++ b/tv/2configs/xserver/xmonad/Util/Shutdown.hs
@@ -0,0 +1,53 @@
+{-# LANGUAGE LambdaCase #-}
+module Util.Shutdown
+    ( sendShutdownEvent
+    , handleShutdownEvent
+    , shutdown
+    )
+  where
+
+import Control.Monad
+import Data.Monoid
+import Data.Maybe (catMaybes)
+import qualified Data.Map as Map
+import System.Environment (getEnv)
+import System.Exit (exitSuccess)
+import XMonad
+import qualified XMonad.StackSet as W
+
+sendShutdownEvent :: IO ()
+sendShutdownEvent = do
+    dpy <- openDisplay ""
+    rw <- rootWindow dpy $ defaultScreen dpy
+    a <- internAtom dpy "XMONAD_SHUTDOWN" False
+    allocaXEvent $ \e -> do
+        setEventType e clientMessage
+        setClientMessageEvent e rw a 32 0 currentTime
+        sendEvent dpy rw False structureNotifyMask e
+    sync dpy False
+
+handleShutdownEvent :: Event -> X All
+handleShutdownEvent = \case
+  ClientMessageEvent { ev_message_type = mt } -> do
+    c <- (mt ==) <$> getAtom "XMONAD_SHUTDOWN"
+    when c shutdown
+    return (All c)
+  _ ->
+    return (All True)
+
+shutdown :: X ()
+shutdown = do
+  broadcastMessage ReleaseResources
+  io . flush =<< asks display
+  let wsData = show . W.mapLayout show . windowset
+      maybeShow (t, Right (PersistentExtension ext)) = Just (t, show ext)
+      maybeShow (t, Left str) = Just (t, str)
+      maybeShow _ = Nothing
+      extState =
+        return . show . catMaybes . map maybeShow . Map.toList . extensibleState
+  s <- gets (\s -> (wsData s : extState s))
+  _ <- io $ do
+    path <- getEnv "XMONAD_STATE"
+    writeFile path (concatMap (++"\n") s)
+    exitSuccess
+  return ()