about summary refs log tree commit diff
path: root/users/tazjin
diff options
context:
space:
mode:
Diffstat (limited to 'users/tazjin')
-rw-r--r--users/tazjin/dotfiles/config.fish40
-rw-r--r--users/tazjin/dotfiles/msmtprc16
-rw-r--r--users/tazjin/dotfiles/notmuch-config21
-rw-r--r--users/tazjin/dotfiles/offlineimaprc39
-rw-r--r--users/tazjin/nixos/README.md20
-rw-r--r--users/tazjin/nixos/camden/default.nix464
-rw-r--r--users/tazjin/nixos/default.nix46
-rw-r--r--users/tazjin/nixos/frog/default.nix254
-rw-r--r--users/tazjin/nixos/nugget/default.nix280
9 files changed, 1180 insertions, 0 deletions
diff --git a/users/tazjin/dotfiles/config.fish b/users/tazjin/dotfiles/config.fish
new file mode 100644
index 0000000000..de2c99ae60
--- /dev/null
+++ b/users/tazjin/dotfiles/config.fish
@@ -0,0 +1,40 @@
+# Configure classic prompt
+set fish_color_user --bold blue
+set fish_color_cwd --bold white
+
+# Enable colour hints in VCS prompt:
+set __fish_git_prompt_showcolorhints yes
+set __fish_git_prompt_color_prefix purple
+set __fish_git_prompt_color_suffix purple
+
+# Fish configuration
+set fish_greeting ""
+set PATH $HOME/.local/bin $HOME/.cargo/bin $PATH
+
+# Editor configuration
+set -gx EDITOR "emacsclient"
+set -gx ALTERNATE_EDITOR "emacs -q -nw"
+set -gx VISUAL "emacsclient"
+
+# Miscellaneous
+eval (direnv hook fish)
+
+# Useful command aliases
+alias gpr 'git pull --rebase'
+alias gco 'git checkout'
+alias gf 'git fetch'
+alias gap 'git add -p'
+alias pbcopy 'xclip -selection clipboard'
+alias edit 'emacsclient -n'
+alias servedir 'nix-shell -p haskellPackages.wai-app-static --run warp'
+
+# Old habits die hard (also ls is just easier to type):
+alias ls 'exa'
+
+# Fix up nix-env & friends for Nix 2.0
+export NIX_REMOTE=daemon
+
+# Fix display of fish in emacs' term-mode:
+function fish_title
+  true
+end
diff --git a/users/tazjin/dotfiles/msmtprc b/users/tazjin/dotfiles/msmtprc
new file mode 100644
index 0000000000..624b6a77fc
--- /dev/null
+++ b/users/tazjin/dotfiles/msmtprc
@@ -0,0 +1,16 @@
+defaults
+
+port 587
+tls on
+tls_trust_file /etc/ssl/certs/ca-certificates.crt
+
+# Runbox mail
+account runbox
+from mail@tazj.in
+host mail.runbox.com
+auth on
+user mail@tazj.in
+passwordeval pass show general/runbox-tazjin
+
+# Use Runbox as default
+account default : runbox
diff --git a/users/tazjin/dotfiles/notmuch-config b/users/tazjin/dotfiles/notmuch-config
new file mode 100644
index 0000000000..a490774e63
--- /dev/null
+++ b/users/tazjin/dotfiles/notmuch-config
@@ -0,0 +1,21 @@
+# .notmuch-config - Configuration file for the notmuch mail system
+#
+# For more information about notmuch, see https://notmuchmail.org
+
+[database]
+path=/home/vincent/mail
+
+[user]
+name=Vincent Ambo
+primary_email=mail@tazj.in
+other_email=tazjin@gmail.com;
+
+[new]
+tags=unread;inbox;
+ignore=
+
+[search]
+exclude_tags=deleted;spam;draft;
+
+[maildir]
+synchronize_flags=true
diff --git a/users/tazjin/dotfiles/offlineimaprc b/users/tazjin/dotfiles/offlineimaprc
new file mode 100644
index 0000000000..78315447e4
--- /dev/null
+++ b/users/tazjin/dotfiles/offlineimaprc
@@ -0,0 +1,39 @@
+[general]
+accounts = tazjin, gmail
+
+[DEFAULT]
+ssl = yes
+sslcacertfile = /etc/ssl/certs/ca-certificates.crt
+
+# Private GMail account (old):
+[Account gmail]
+maxage = 90
+localrepository = gmail-local
+remoterepository = gmail-remote
+synclabels = yes
+
+[Repository gmail-local]
+type = GmailMaildir
+localfolders = ~/mail/gmail
+
+[Repository gmail-remote]
+type = Gmail
+remoteuser = tazjin@gmail.com
+remotepassfile = ~/.config/mail/gmail-pass
+folderfilter = lambda folder: folder == 'INBOX'
+
+# Main private account:
+[Account tazjin]
+localrepository = tazjin-local
+remoterepository = tazjin-remote
+
+[Repository tazjin-local]
+type = Maildir
+localfolders = ~/mail/tazjin
+
+[Repository tazjin-remote]
+type = IMAP
+remotehost = mail.runbox.com
+remoteuser = mail@tazj.in
+remotepassfile = ~/.config/mail/tazjin-pass
+auth_mechanisms = LOGIN
diff --git a/users/tazjin/nixos/README.md b/users/tazjin/nixos/README.md
new file mode 100644
index 0000000000..fc90cb4b43
--- /dev/null
+++ b/users/tazjin/nixos/README.md
@@ -0,0 +1,20 @@
+NixOS configuration
+===================
+
+My NixOS configuration! It configures most of the packages I require
+on my systems, sets up Emacs the way I need and does a bunch of other
+interesting things.
+
+System configuration lives in folders for each machine and a custom
+fixed point evaluation (similar to standard NixOS module
+configuration) is used to combine configuration together.
+
+Building `ops.nixos.rebuilder` yields a script that will automatically
+build and activate the newest configuration based on the current
+hostname.
+
+## Configured hosts:
+
+* `frog` - weapon of mass computation at home
+* `nugget` - desktop computer at home
+* ~~`urdhva` - T470s~~ (currently with edef)
diff --git a/users/tazjin/nixos/camden/default.nix b/users/tazjin/nixos/camden/default.nix
new file mode 100644
index 0000000000..9bdebac5f5
--- /dev/null
+++ b/users/tazjin/nixos/camden/default.nix
@@ -0,0 +1,464 @@
+# This file configures camden.tazj.in, my homeserver.
+{ depot, pkgs, lib, ... }:
+
+config: let
+  nixpkgs = import depot.third_party.nixpkgsSrc {
+    config.allowUnfree = true;
+  };
+
+  nginxRedirect = { from, to, acmeHost }: {
+    serverName = from;
+    useACMEHost = acmeHost;
+    forceSSL = true;
+
+    extraConfig = "return 301 https://${to}$request_uri;";
+  };
+in lib.fix(self: {
+  imports = [
+    "${depot.depotPath}/ops/nixos/depot.nix"
+    "${depot.depotPath}/ops/nixos/hound.nix"
+    "${depot.depotPath}/ops/nixos/monorepo-gerrit.nix"
+    "${depot.depotPath}/ops/nixos/smtprelay.nix"
+    "${depot.depotPath}/ops/nixos/tvl-slapd/default.nix"
+    "${pkgs.nixpkgsSrc}/nixos/modules/services/web-apps/gerrit.nix"
+  ];
+  depot = depot;
+
+  # camden is intended to boot unattended, despite having an encrypted
+  # root partition.
+  #
+  # The below configuration uses an externally connected USB drive
+  # that contains a LUKS key file to unlock the disk automatically at
+  # boot.
+  #
+  # TODO(tazjin): Configure LUKS unlocking via SSH instead.
+  boot = {
+    initrd = {
+      availableKernelModules = [
+        "ahci" "xhci_pci" "usbhid" "usb_storage" "sd_mod" "sdhci_pci"
+        "rtsx_usb_sdmmc" "r8169"
+      ];
+
+      kernelModules = [ "dm-snapshot" ];
+
+      luks.devices.camden-crypt = {
+        fallbackToPassword = true;
+        device = "/dev/disk/by-label/camden-crypt";
+        keyFile = "/dev/sdb";
+        keyFileSize = 4096;
+      };
+    };
+
+    loader = {
+      systemd-boot.enable = true;
+      efi.canTouchEfiVariables = true;
+    };
+
+    cleanTmpDir = true;
+  };
+
+  fileSystems = {
+    "/" = {
+      device = "/dev/disk/by-label/camden-root";
+      fsType = "ext4";
+    };
+
+    "/home" = {
+      device = "/dev/disk/by-label/camden-home";
+      fsType = "ext4";
+    };
+
+    "/boot" = {
+      device = "/dev/disk/by-label/BOOT";
+      fsType = "vfat";
+    };
+  };
+
+  nix = {
+    maxJobs = lib.mkDefault 4;
+
+    nixPath = [
+      "depot=/home/tazjin/depot"
+      "nixpkgs=${depot.third_party.nixpkgsSrc}"
+    ];
+
+    trustedUsers = [ "root" "tazjin" ];
+
+    binaryCaches = [
+      "https://tazjin.cachix.org"
+    ];
+
+    binaryCachePublicKeys = [
+      "tazjin.cachix.org-1:IZkgLeqfOr1kAZjypItHMg1NoBjm4zX9Zzep8oRSh7U="
+    ];
+  };
+  nixpkgs.pkgs = nixpkgs;
+
+  powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
+
+  networking = {
+    hostName = "camden";
+    interfaces.enp1s0.useDHCP = true;
+    interfaces.enp1s0.ipv6.addresses = [
+      {
+        address = "2a01:4b00:821a:ce02::5";
+        prefixLength = 64;
+      }
+    ];
+
+    firewall.enable = false;
+  };
+
+  time.timeZone = "UTC";
+
+  # System-wide application setup
+  programs.fish.enable = true;
+  programs.mosh.enable = true;
+
+  environment.systemPackages =
+    # programs from the depot
+    (with depot; [
+      fun.idual.script
+      fun.idual.setAlarm
+      third_party.pounce
+    ]) ++
+
+    # programs from nixpkgs
+    (with nixpkgs; [
+      bat
+      curl
+      direnv
+      emacs26-nox
+      gnupg
+      git
+      htop
+      jq
+      pass
+      pciutils
+      ripgrep
+    ]);
+
+  users = {
+    # Set up my own user for logging in and doing things ...
+    users.tazjin = {
+      isNormalUser = true;
+      uid = 1000;
+      extraGroups = [ "git" "wheel" ];
+      shell = nixpkgs.fish;
+    };
+
+    # Set up a user & group for general git shenanigans
+    groups.git = {};
+    users.git = {
+      group = "git";
+      isNormalUser = false;
+    };
+  };
+
+  # Services setup
+  services.openssh.enable = true;
+  services.haveged.enable = true;
+
+  # Join Tailscale into home network
+  services.tailscale.enable = true;
+
+  # Allow sudo-ing via the forwarded SSH agent.
+  security.pam.enableSSHAgentAuth = true;
+
+  # Run cgit for the depot. The onion here is nginx(thttpd(cgit)).
+  systemd.services.cgit = {
+    wantedBy = [ "multi-user.target" ];
+    script = "${depot.web.cgit-taz}/bin/cgit-launch";
+
+    serviceConfig = {
+      Restart = "on-failure";
+      User = "git";
+      Group = "git";
+    };
+  };
+
+  # Run honk as the ActivityPub server, using all the fancy systemd
+  # magic.
+  systemd.services.honk = {
+    wantedBy = [ "multi-user.target" ];
+    script = lib.concatStringsSep " " [
+      "${depot.third_party.honk}/bin/honk"
+      "-datadir /var/lib/honk"
+      "-viewdir ${depot.third_party.honk.src}"
+    ];
+
+    serviceConfig = {
+      Restart = "always";
+      DynamicUser = true;
+      StateDirectory = "honk";
+      WorkingDirectory = "/var/lib/honk";
+    };
+  };
+
+  # NixOS 20.03 broke nginx and I can't be bothered to debug it
+  # anymore, all solution attempts have failed, so here's a
+  # brute-force fix.
+  systemd.services.fix-nginx = {
+    script = "${nixpkgs.coreutils}/bin/chown -R nginx: /var/spool/nginx /var/cache/nginx";
+
+    serviceConfig = {
+      User = "root";
+      Type = "oneshot";
+    };
+  };
+
+  systemd.timers.fix-nginx = {
+    wantedBy = [ "multi-user.target" ];
+    timerConfig = {
+      OnCalendar = "minutely";
+    };
+  };
+
+  # Provision a TLS certificate outside of nginx to avoid
+  # nixpkgs#38144
+  security.acme = {
+    acceptTerms = true;
+    email = "mail@tazj.in";
+
+    certs."tazj.in" = {
+      user = "nginx";
+      group = "nginx";
+      webroot = "/var/lib/acme/acme-challenge";
+      extraDomains = {
+        "cs.tazj.in" = null;
+        "git.tazj.in" = null;
+        "www.tazj.in" = null;
+
+        # Local domains (for this machine only)
+        "camden.tazj.in" = null;
+      };
+      postRun = "systemctl reload nginx";
+    };
+
+    certs."tvl.fyi" = {
+      user = "nginx";
+      group = "nginx";
+      webroot = "/var/lib/acme/acme-challenge";
+      postRun = "systemctl reload nginx";
+      extraDomains = {
+        "cl.tvl.fyi" = null;
+        "code.tvl.fyi" = null;
+        "cs.tvl.fyi" = null;
+      };
+    };
+  };
+
+  # Forward logs to Google Cloud Platform
+  services.journaldriver = {
+    enable                 = true;
+    logStream              = "home";
+    googleCloudProject     = "tazjins-infrastructure";
+    applicationCredentials = "/etc/gcp/key.json";
+  };
+
+  # Serve a code search (hound) instance
+  services.depot.hound = {
+    enable = true;
+    title = "tazjin's depot";
+    repos.depot = {
+      url = "file:///var/lib/gerrit/git/depot.git";
+      vcs = "git";
+      url-pattern = {
+        base-url = "https://code.tvl.fyi/tree/{path}{anchor}";
+        anchor = "#n{line}";
+      };
+    };
+    repos.nixpkgs = {
+      url = "file:///var/git/nixpkgs";
+      vcs = "git";
+      url-pattern = {
+        base-url = "https://github.com/NixOS/nixpkgs/blob/${pkgs.nixpkgsCommit}/{path}{anchor}";
+        anchor = "#L{line}";
+      };
+    };
+  };
+
+  # Start a local SMTP relay to Gmail (used by gerrit)
+  services.depot.smtprelay = {
+    enable = true;
+    args = {
+      listen = ":2525";
+      remote_host = "smtp.gmail.com:587";
+      remote_auth = "plain";
+      remote_user = "tvlbot@tazj.in";
+    };
+  };
+
+  # serve my website(s)
+  services.nginx = {
+    enable = true;
+    enableReload = true;
+    package = with nixpkgs; nginx.override {
+      modules = [ nginxModules.rtmp ];
+    };
+
+    recommendedTlsSettings = true;
+    recommendedGzipSettings = true;
+    recommendedProxySettings = true;
+
+
+    appendConfig = ''
+      rtmp_auto_push on;
+      rtmp {
+        server {
+          listen 1935;
+          chunk_size 4000;
+
+          application tvl {
+            live on;
+
+            allow publish 88.98.195.213;
+            allow publish 10.0.1.0/24;
+            deny publish all;
+
+            allow play all;
+          }
+        }
+      }
+    '';
+
+    commonHttpConfig = ''
+      log_format json_combined escape=json
+      '{'
+          '"remote_addr":"$remote_addr",'
+          '"method":"$request_method",'
+          '"uri":"$request_uri",'
+          '"status":$status,'
+          '"request_size":$request_length,'
+          '"response_size":$body_bytes_sent,'
+          '"response_time":$request_time,'
+          '"referrer":"$http_referer",'
+          '"user_agent":"$http_user_agent"'
+      '}';
+
+      access_log syslog:server=unix:/dev/log,nohostname json_combined;
+    '';
+
+    virtualHosts.homepage = {
+      serverName = "tazj.in";
+      serverAliases = [ "camden.tazj.in" ];
+      default = true;
+      useACMEHost = "tazj.in";
+      root = depot.web.homepage;
+      forceSSL = true;
+
+      extraConfig = ''
+        ${depot.web.blog.oldRedirects}
+
+        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
+
+        location ~* \.(webp|woff2)$ {
+          add_header Cache-Control "public, max-age=31536000";
+        }
+
+        location /blog/ {
+          alias ${depot.web.blog.rendered}/;
+
+          if ($request_uri ~ ^/(.*)\.html$) {
+            return 302 /$1;
+          }
+
+          try_files $uri $uri.html $uri/ =404;
+        }
+
+        location /blobs/ {
+          alias /var/www/blobs/;
+        }
+      '';
+    };
+
+    virtualHosts.tvl = {
+      serverName = "tvl.fyi";
+      useACMEHost = "tvl.fyi";
+      root = depot.web.tvl;
+      forceSSL = true;
+
+      extraConfig = ''
+        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
+
+        rewrite ^/builds/?$ https://builds.sr.ht/~tazjin/depot last;
+        rewrite ^/meet/?$ https://meet.google.com/mng-biyw-xbb last;
+
+        rewrite ^/monorepo-doc/?$ https://docs.google.com/document/d/1nnyByXcH0F6GOmEezNOUa2RFelpeRpDToBLYD_CtjWE/edit?usp=sharing last;
+
+        rewrite ^/irc/?$ ircs://chat.freenode.net:6697/##tvl last;
+
+        location ~* \.(webp|woff2)$ {
+          add_header Cache-Control "public, max-age=31536000";
+        }
+      '';
+    };
+
+    virtualHosts.cgit = {
+      serverName = "code.tvl.fyi";
+      useACMEHost = "tvl.fyi";
+      forceSSL = true;
+
+      extraConfig = ''
+        # Static assets must always hit the root.
+        location ~ ^/(favicon\.ico|cgit\.(css|png))$ {
+           proxy_pass http://localhost:2448;
+        }
+
+        # Everything else hits the depot directly.
+        location / {
+            proxy_pass http://localhost:2448/cgit.cgi/depot/;
+        }
+      '';
+    };
+
+    virtualHosts.hound = {
+      serverName = "cs.tvl.fyi";
+      useACMEHost = "tvl.fyi";
+      forceSSL = true;
+
+      extraConfig = ''
+        location / {
+          proxy_pass http://localhost:6080;
+        }
+      '';
+    };
+
+    virtualHosts.gerrit = {
+      serverName = "cl.tvl.fyi";
+      useACMEHost = "tvl.fyi";
+      forceSSL = true;
+
+      extraConfig = ''
+        location / {
+          proxy_pass http://localhost:4778;
+          proxy_set_header  X-Forwarded-For $remote_addr;
+          proxy_set_header  Host $host;
+        }
+      '';
+    };
+
+    virtualHosts.cgit-old = nginxRedirect {
+      from = "git.tazj.in";
+      to = "code.tvl.fyi";
+      acmeHost = "tazj.in";
+    };
+
+    virtualHosts.cs-old = nginxRedirect {
+      from = "cs.tazj.in";
+      to = "cs.tvl.fyi";
+      acmeHost = "tazj.in";
+    };
+  };
+
+  # Timer units that can be started with systemd-run to set my alarm.
+  systemd.user.services.light-alarm = {
+    script = "${depot.fun.idual.script}/bin/idualctl wakey";
+    postStart = "${pkgs.systemd}/bin/systemctl --user stop light-alarm.timer";
+    serviceConfig = {
+      Type = "oneshot";
+    };
+  };
+
+  system.stateVersion = "19.09";
+})
diff --git a/users/tazjin/nixos/default.nix b/users/tazjin/nixos/default.nix
new file mode 100644
index 0000000000..4f8923af79
--- /dev/null
+++ b/users/tazjin/nixos/default.nix
@@ -0,0 +1,46 @@
+# TODO(tazjin): Generalise this and move to //ops/nixos
+{ depot, lib, ... }:
+
+let
+  inherit (builtins) foldl';
+
+  systemFor = configs: (depot.third_party.nixos {
+    configuration = lib.fix(config:
+      foldl' lib.recursiveUpdate {} (map (c: c config) configs)
+    );
+  }).system;
+
+  caseFor = hostname: ''
+    ${hostname})
+      echo "Rebuilding NixOS for //users/tazjin/nixos/${hostname}"
+      system=$(nix-build -E '(import <depot> {}).users.tazjin.nixos.${hostname}System' --no-out-link)
+      ;;
+  '';
+
+  rebuilder = depot.third_party.writeShellScriptBin "rebuilder" ''
+    set -ue
+    if [[ $EUID -ne 0 ]]; then
+      echo "Oh no! Only root is allowed to rebuild the system!" >&2
+      exit 1
+    fi
+
+    case $HOSTNAME in
+    ${caseFor "nugget"}
+    ${caseFor "camden"}
+    ${caseFor "frog"}
+    *)
+      echo "$HOSTNAME is not a known NixOS host!" >&2
+      exit 1
+      ;;
+    esac
+
+    nix-env -p /nix/var/nix/profiles/system --set $system
+    $system/bin/switch-to-configuration switch
+  '';
+in {
+  inherit rebuilder;
+
+  nuggetSystem = systemFor [ depot.users.tazjin.nixos.nugget ];
+  camdenSystem = systemFor [ depot.users.tazjin.nixos.camden ];
+  frogSystem = systemFor [ depot.users.tazjin.nixos.frog ];
+}
diff --git a/users/tazjin/nixos/frog/default.nix b/users/tazjin/nixos/frog/default.nix
new file mode 100644
index 0000000000..72b1c9ed36
--- /dev/null
+++ b/users/tazjin/nixos/frog/default.nix
@@ -0,0 +1,254 @@
+{ depot, lib, ... }:
+
+config: let
+  nixpkgs = import depot.third_party.nixpkgsSrc {
+    config.allowUnfree = true;
+  };
+
+  lieer = depot.third_party.lieer {};
+
+  # add google-c-style here because other machines get it from, eh,
+  # elsewhere.
+  frogEmacs = (depot.tools.emacs.overrideEmacs(epkgs: epkgs ++ [
+    depot.third_party.emacsPackages.google-c-style
+  ]));
+in depot.lib.fix(self: {
+  # TODO(tazjin): v4l2loopback
+
+  boot = {
+    tmpOnTmpfs = true;
+    kernelModules = [ "kvm-amd" ];
+
+    loader = {
+      systemd-boot.enable = true;
+      efi.canTouchEfiVariables = true;
+    };
+
+    initrd = {
+      luks.devices.frog-crypt.device = "/dev/disk/by-label/frog-crypt";
+      availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usb_storage" "usbhid" "sd_mod" ];
+      kernelModules = [ "dm-snapshot" ];
+    };
+
+    kernelPackages = nixpkgs.linuxPackages_latest;
+    kernel.sysctl = {
+      "kernel.perf_event_paranoid" = 1;
+    };
+  };
+
+  hardware = {
+    cpu.amd.updateMicrocode = true;
+    enableRedistributableFirmware = true;
+    pulseaudio.enable = true;
+    u2f.enable = true;
+    opengl = {
+      enable = true;
+      driSupport = true;
+    };
+  };
+
+  nix = {
+    maxJobs = 48;
+    nixPath = [
+      "depot=/depot"
+      "nixpkgs=${depot.third_party.nixpkgsSrc}"
+    ];
+  };
+
+  nixpkgs.pkgs = nixpkgs;
+
+  networking = {
+    hostName = "frog";
+    useDHCP = false;
+    interfaces.enp67s0.useDHCP = true;
+
+    # Don't use ISP's DNS servers:
+    nameservers = [
+      "8.8.8.8"
+      "8.8.4.4"
+    ];
+
+    firewall.enable = false;
+  };
+
+  # Generate an immutable /etc/resolv.conf from the nameserver settings
+  # above (otherwise DHCP overwrites it):
+  environment.etc."resolv.conf" = with lib; {
+    source = depot.third_party.writeText "resolv.conf" ''
+      ${concatStringsSep "\n" (map (ns: "nameserver ${ns}") self.networking.nameservers)}
+      options edns0
+    '';
+  };
+
+  time.timeZone = "Europe/London";
+
+  fileSystems = {
+    "/".device = "/dev/disk/by-label/frog-root";
+    "/boot".device = "/dev/disk/by-label/BOOT";
+    "/home".device = "/dev/disk/by-label/frog-home";
+  };
+
+  # Configure user account
+  users.extraUsers.tazjin = {
+    extraGroups = [ "wheel" "audio" ];
+    isNormalUser = true;
+    uid = 1000;
+    shell = nixpkgs.fish;
+  };
+
+  security.sudo = {
+    enable = true;
+    extraConfig = "wheel ALL=(ALL:ALL) SETENV: ALL";
+  };
+
+  fonts = {
+    fonts = with nixpkgs; [
+      corefonts
+      dejavu_fonts
+      jetbrains-mono
+      noto-fonts-cjk
+      noto-fonts-emoji
+    ];
+
+    fontconfig = {
+      hinting.enable = true;
+      subpixel.lcdfilter = "light";
+
+      defaultFonts = {
+        monospace = [ "JetBrains Mono" ];
+      };
+    };
+  };
+
+  # Configure location (Vauxhall, London) for services that need it.
+  location = {
+    latitude = 51.4819109;
+    longitude = -0.1252998;
+  };
+
+  programs.fish.enable = true;
+  programs.ssh.startAgent = true;
+
+  services.redshift.enable = true;
+  services.openssh.enable = true;
+  services.fstrim.enable = true;
+
+  # Required for Yubikey usage as smartcard
+  services.pcscd.enable = true;
+  services.udev.packages = [
+    nixpkgs.yubikey-personalization
+  ];
+
+  services.xserver = {
+    enable = true;
+    layout = "us";
+    xkbOptions = "caps:super";
+    exportConfiguration = true;
+    displayManager = {
+      # Give EXWM permission to control the session.
+      sessionCommands = "${nixpkgs.xorg.xhost}/bin/xhost +SI:localuser:$USER";
+
+      lightdm.enable = true;
+      lightdm.greeters.gtk.clock-format = "%H·%M"; # TODO(tazjin): TZ?
+    };
+
+    windowManager.session = lib.singleton {
+      name = "exwm";
+      start = "${frogEmacs}/bin/tazjins-emacs";
+    };
+  };
+
+  # Do not restart the display manager automatically
+  systemd.services.display-manager.restartIfChanged = lib.mkForce false;
+
+  # clangd needs more than ~2GB in the runtime directory to start up
+  services.logind.extraConfig = ''
+    RuntimeDirectorySize=16G
+  '';
+
+  # Configure email setup
+  systemd.user.services.lieer-tazjin = {
+    description = "Synchronise mail@tazj.in via lieer";
+    script = "${lieer}/bin/gmi sync";
+
+    serviceConfig = {
+      WorkingDirectory = "%h/mail/account.tazjin";
+      Type = "oneshot";
+    };
+  };
+
+  systemd.user.timers.lieer-tazjin = {
+    wantedBy = [ "timers.target" ];
+
+    timerConfig = {
+      OnActiveSec = "1";
+      OnUnitActiveSec = "180";
+    };
+  };
+
+  environment.systemPackages =
+    # programs from the depot
+    (with depot; [
+      fun.idual.script
+      lieer
+      frogEmacs
+      ops.kontemplate
+      third_party.ffmpeg
+      third_party.git
+    ]) ++
+
+    # programs from nixpkgs
+    (with nixpkgs; [
+      age
+      bat
+      chromium
+      clang-manpages
+      clang-tools
+      clang_10
+      curl
+      direnv
+      dnsutils
+      emacs26 # mostly for emacsclient
+      exa
+      fd
+      gnupg
+      go
+      google-chrome
+      google-cloud-sdk
+      htop
+      hyperfine
+      i3lock
+      imagemagick
+      jq
+      kubectl
+      linuxPackages.perf
+      miller
+      msmtp
+      nix-prefetch-github
+      notmuch
+      openssh
+      openssl
+      pass
+      pavucontrol
+      pinentry
+      pinentry-emacs
+      pwgen
+      ripgrep
+      rr
+      rustup
+      scrot
+      spotify
+      steam
+      tokei
+      tree
+      unzip
+      vlc
+      xclip
+      yubico-piv-tool
+      yubikey-personalization
+      zoxide
+    ]);
+
+  # ... and other nonsense.
+  system.stateVersion = "20.03";
+})
diff --git a/users/tazjin/nixos/nugget/default.nix b/users/tazjin/nixos/nugget/default.nix
new file mode 100644
index 0000000000..7c9530072d
--- /dev/null
+++ b/users/tazjin/nixos/nugget/default.nix
@@ -0,0 +1,280 @@
+# This file configures nugget, my home desktop machine.
+{ depot, lib, ... }:
+
+config: let
+  nixpkgs = import depot.third_party.stableNixpkgsSrc {
+    config.allowUnfree = true;
+  };
+
+  unstable = import depot.third_party.nixpkgsSrc {};
+  lieer = (depot.third_party.lieer {});
+
+  # google-c-style is installed only on nugget because other
+  # machines get it from, eh, elsewhere.
+  nuggetEmacs = (depot.tools.emacs.overrideEmacs(epkgs: epkgs ++ [
+    depot.third_party.emacsPackages.google-c-style
+  ]));
+in depot.lib.fix(self: {
+  imports = [
+    ../modules/v4l2loopback.nix
+  ];
+
+  hardware = {
+    pulseaudio.enable = true;
+    cpu.intel.updateMicrocode = true;
+    u2f.enable = true;
+  };
+
+  boot = {
+    cleanTmpDir = true;
+    kernelModules = [ "kvm-intel" ];
+
+    loader = {
+      timeout = 3;
+      systemd-boot.enable = true;
+      efi.canTouchEfiVariables = false;
+    };
+
+    initrd = {
+      luks.devices.nugget-crypt.device = "/dev/disk/by-label/nugget-crypt";
+      availableKernelModules = [ "xhci_pci" "ehci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" ];
+      kernelModules = [ "dm-snapshot" ];
+    };
+
+    kernel.sysctl = {
+      "kernel.perf_event_paranoid" = 1;
+    };
+  };
+
+  nix = {
+    package = depot.third_party.nix;
+    nixPath = [
+      "depot=/home/tazjin/depot"
+      "nixpkgs=${depot.third_party.nixpkgsSrc}"
+    ];
+  };
+
+  nixpkgs.pkgs = nixpkgs;
+
+  networking = {
+    hostName = "nugget";
+    useDHCP = false;
+    interfaces.eno1.useDHCP = true;
+    interfaces.wlp7s0.useDHCP = true;
+
+    # Don't use ISP's DNS servers:
+    nameservers = [
+      "8.8.8.8"
+      "8.8.4.4"
+    ];
+
+    # Open Chromecast-related ports & servedir
+    firewall.enable = false;
+    firewall.allowedTCPPorts = [ 4242 5556 5558 ];
+
+    # Connect to the WiFi to let the Chromecast work.
+    wireless.enable = true;
+    wireless.networks = {
+      "How do I computer?" = {
+        psk = "washyourface";
+      };
+    };
+  };
+
+  # Generate an immutable /etc/resolv.conf from the nameserver settings
+  # above (otherwise DHCP overwrites it):
+  environment.etc."resolv.conf" = with lib; {
+    source = depot.third_party.writeText "resolv.conf" ''
+      ${concatStringsSep "\n" (map (ns: "nameserver ${ns}") self.networking.nameservers)}
+      options edns0
+    '';
+  };
+
+  time.timeZone = "Europe/London";
+
+  environment.systemPackages =
+    # programs from the depot
+    (with depot; [
+      fun.idual.script
+      lieer
+      nuggetEmacs
+      ops.kontemplate
+      third_party.ffmpeg
+      third_party.git
+    ]) ++
+
+    # programs from nixpkgs
+    (with nixpkgs; [
+      age
+      bat
+      cachix
+      chromium
+      clang-manpages
+      clang-tools
+      clang_10
+      curl
+      direnv
+      dnsutils
+      exa
+      fd
+      gnupg
+      go
+      google-chrome
+      google-cloud-sdk
+      guile
+      htop
+      hyperfine
+      i3lock
+      imagemagick
+      jq
+      keybase-gui
+      kubectl
+      linuxPackages.perf
+      meson
+      miller
+      msmtp
+      nix-prefetch-github
+      notmuch
+      openssh
+      openssl
+      pass
+      pavucontrol
+      pinentry
+      pinentry-emacs
+      pwgen
+      ripgrep
+      rr
+      rustup
+      sbcl
+      scrot
+      spotify
+      steam
+      tokei
+      tree
+      unzip
+      vlc
+      xclip
+      yubico-piv-tool
+      yubikey-personalization
+    ]) ++
+
+    # programs from unstable nixpkgs
+    (with unstable; [
+      zoxide
+    ]);
+
+    fileSystems = {
+      "/".device = "/dev/disk/by-label/nugget-root";
+      "/boot".device = "/dev/disk/by-label/EFI";
+      "/home".device = "/dev/disk/by-label/nugget-home";
+    };
+
+    # Configure user account
+    users.extraUsers.tazjin = {
+      extraGroups = [ "wheel" "audio" ];
+      isNormalUser = true;
+      uid = 1000;
+      shell = nixpkgs.fish;
+    };
+
+    security.sudo = {
+      enable = true;
+      extraConfig = "wheel ALL=(ALL:ALL) SETENV: ALL";
+    };
+
+    fonts = {
+      fonts = with nixpkgs; [
+        corefonts
+        dejavu_fonts
+        jetbrains-mono
+        noto-fonts-cjk
+        noto-fonts-emoji
+      ];
+
+      fontconfig = {
+        hinting.enable = true;
+        subpixel.lcdfilter = "light";
+
+        defaultFonts = {
+          monospace = [ "JetBrains Mono" ];
+        };
+      };
+    };
+
+    # Configure location (Vauxhall, London) for services that need it.
+    location = {
+      latitude = 51.4819109;
+      longitude = -0.1252998;
+    };
+
+    programs.fish.enable = true;
+    programs.ssh.startAgent = true;
+
+    services.redshift.enable = true;
+    services.openssh.enable = true;
+    services.keybase.enable = true;
+
+    # Required for Yubikey usage as smartcard
+    services.pcscd.enable = true;
+    services.udev.packages = [
+      nixpkgs.yubikey-personalization
+    ];
+
+    services.xserver = {
+      enable = true;
+      layout = "us";
+      xkbOptions = "caps:super";
+      exportConfiguration = true;
+      videoDrivers = [ "nvidia" ];
+
+      displayManager = {
+        # Give EXWM permission to control the session.
+        sessionCommands = "${nixpkgs.xorg.xhost}/bin/xhost +SI:localuser:$USER";
+
+        lightdm.enable = true;
+        lightdm.greeters.gtk.clock-format = "%H·%M";
+      };
+
+      windowManager.session = lib.singleton {
+        name = "exwm";
+        start = "${nuggetEmacs}/bin/tazjins-emacs";
+      };
+    };
+
+    # Do not restart the display manager automatically
+    systemd.services.display-manager.restartIfChanged = lib.mkForce false;
+
+    # Configure email setup
+    systemd.user.services.lieer-tazjin = {
+      description = "Synchronise mail@tazj.in via lieer";
+      script = "${lieer}/bin/gmi sync";
+
+      serviceConfig = {
+        WorkingDirectory = "%h/mail/account.tazjin";
+        Type = "oneshot";
+      };
+    };
+
+    systemd.user.timers.lieer-tazjin = {
+      wantedBy = [ "timers.target" ];
+
+      timerConfig = {
+        OnActiveSec = "1";
+        OnUnitActiveSec = "180";
+      };
+    };
+
+    # Use Tailscale \o/
+    services.tailscale.enable = true;
+
+    # nugget has an SSD
+    services.fstrim.enable = true;
+
+    # clangd needs more than ~2GB in the runtime directory to start up
+    services.logind.extraConfig = ''
+      RuntimeDirectorySize=4G
+    '';
+
+    # ... and other nonsense.
+    system.stateVersion = "19.09";
+})