~jb55/citadel

50e4b2040727974c3905968e41910fa0ab9ea641 — William Casarin 1 year, 1 month ago 11e4ada
add nix configs
96 files changed, 7145 insertions(+), 0 deletions(-)

A nix-config/.gitignore
A nix-config/certs/default.nix
A nix-config/certs/flynn-dev.cer
A nix-config/certs/flynn-prod.cer
A nix-config/configuration.nix
A nix-config/environment/default.nix
A nix-config/environment/desktop/default.nix
A nix-config/fonts/default.nix
A nix-config/hardware-configuration.nix
A nix-config/hardware/desktop/default.nix
A nix-config/machines/archer/backups/default.nix
A nix-config/machines/archer/backups/git.nix
A nix-config/machines/archer/backups/wiki.nix
A nix-config/machines/archer/bandcamp-sales-bot/default.nix
A nix-config/machines/archer/beatport-sales-bot/default.nix
A nix-config/machines/archer/cogs-bot/default.nix
A nix-config/machines/archer/config/default.nix
A nix-config/machines/archer/default.nix
A nix-config/machines/archer/fail-notifier/default.nix
A nix-config/machines/archer/hardware/default.nix
A nix-config/machines/archer/itunes-bots/default.nix
A nix-config/machines/archer/nginx/default.nix
A nix-config/machines/archer/nginx/git.nix
A nix-config/machines/archer/nginx/hoogle.nix
A nix-config/machines/archer/nginx/nix-serve.nix
A nix-config/machines/archer/nginx/sites/local
A nix-config/machines/archer/payments-runner/default.nix
A nix-config/machines/archer/payments-server/default.nix
A nix-config/machines/archer/shopify-sales-bot/default.nix
A nix-config/machines/archer/transaction-bot/default.nix
A nix-config/machines/archer/trendbot/default.nix
A nix-config/machines/archer/tunecore-sales-bot/default.nix
A nix-config/machines/archer/youtube-pub-sales-bot/default.nix
A nix-config/machines/archer/youtube-sales-bot/default.nix
A nix-config/machines/charon/config/default.nix
A nix-config/machines/charon/default.nix
A nix-config/machines/charon/dovecot/filters.sieve
A nix-config/machines/charon/hardware/default.nix
A nix-config/machines/charon/networking/default.nix
A nix-config/machines/charon/nginx/default.nix
A nix-config/machines/charon/nginx/pokemap.nix
A nix-config/machines/charon/nginx/sites/coretto.io
A nix-config/machines/charon/nginx/sites/hearpress.com
A nix-config/machines/charon/nginx/sites/jb55.com
A nix-config/machines/charon/nginx/sites/npmrepo.com
A nix-config/machines/charon/nginx/sites/wineparty.xyz
A nix-config/machines/charon/sheetzen/default.nix
A nix-config/machines/charon/vidstats/default.nix
A nix-config/machines/monad/bitcoin/dca.nix
A nix-config/machines/monad/bitcoin/default.nix
A nix-config/machines/monad/bitcoin/walletemail.nix
A nix-config/machines/monad/config/default.nix
A nix-config/machines/monad/contracts/commit/default.nix
A nix-config/machines/monad/contracts/plastiq/default.nix
A nix-config/machines/monad/default.nix
A nix-config/machines/monad/hardware/default.nix
A nix-config/machines/monad/networking/default.nix
A nix-config/machines/monad/nginx/default.nix
A nix-config/machines/quiver/config/default.nix
A nix-config/machines/quiver/default.nix
A nix-config/machines/quiver/hardware-configuration.nix
A nix-config/machines/quiver/networking/default.nix
A nix-config/machines/quiver/timers/archer-cookies/default.nix
A nix-config/machines/quiver/timers/default.nix
A nix-config/misc/dnsmasq-adblock.nix
A nix-config/misc/git-server.nix
A nix-config/misc/imap-notifier/default.nix
A nix-config/misc/msmtp/default.nix
A nix-config/misc/util.nix
A nix-config/networking/default.nix
A nix-config/nixpkgs/config.nix
A nix-config/nixpkgs/dotfiles.nix
A nix-config/nixpkgs/haskell-overrides/default.nix
A nix-config/nixpkgs/haskell-overrides/massager-service.nix
A nix-config/nixpkgs/haskell-overrides/monstercat-backend.nix
A nix-config/nixpkgs/haskell-overrides/payment.nix
A nix-config/nixpkgs/scripts/ds4ctl/default.nix
A nix-config/nixpkgs/scripts/footswitch/default.nix
A nix-config/nixpkgs/scripts/footswitch/patch.diff
A nix-config/nixpkgs/scripts/ical2org/default.nix
A nix-config/services/default.nix
A nix-config/services/desktop/default.nix
A nix-config/services/desktop/networking/default.nix
A nix-config/services/fail-notifier/default.nix
A nix-config/services/footswitch/default.nix
A nix-config/services/hoogle/default.nix
A nix-config/services/mailz/default.nix
A nix-config/services/mailz/opensmtpd.diff
A nix-config/services/mailz/opensmtpd.nix
A nix-config/services/mailz/proc_path.diff
A nix-config/services/pokemongo-map/default.nix
A nix-config/services/pokemongo-map/requirements.nix
A nix-config/services/pokemongo-map/requirements_generated.nix
A nix-config/services/pokemongo-map/requirements_override.nix
A nix-config/timers/sync-ical2org.nix
A nix-config/wayland/default.nix
A nix-config/.gitignore => nix-config/.gitignore +2 -0
@@ 0,0 1,2 @@
private.nix
/machines/monad/commit/commit.ovpn

A nix-config/certs/default.nix => nix-config/certs/default.nix +7 -0
@@ 0,0 1,7 @@
{ config, lib, pkgs, ... }:
let certs = [ ./flynn-dev.cer
              ./flynn-prod.cer
            ];
in {
  security.pki.certificates = map builtins.readFile certs;
}

A nix-config/certs/flynn-dev.cer => nix-config/certs/flynn-dev.cer +19 -0
@@ 0,0 1,19 @@
-----BEGIN CERTIFICATE-----
MIIDAzCCAe2gAwIBAgIQRt44w0Dtmo6Kc0rZK/yGtzALBgkqhkiG9w0BAQswLTEO
MAwGA1UEChMFRmx5bm4xGzAZBgNVBAsTEkZseW5uIEVwaGVtZXJhbCBDQTAeFw0x
NjAxMTEyMzU4MTRaFw0yMTAxMDkyMzU4MTRaMC0xDjAMBgNVBAoTBUZseW5uMRsw
GQYDVQQLExJGbHlubiBFcGhlbWVyYWwgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQC1vDwqPoUmwRepdUV1rs67c/vnDn8GaFoKyLY4OBrmsxMA/E9J
yeTK5cfTmFK7YnMjBOg93PxYkQIL3viJhL04gKqZdVF3VTMdP0RNYLIT28qyoWbt
bDfc/OMLDh8pNXtOovCuIIWkKkVJWPk+SA5a1Cj6755WU8faRJ58unUFK3AeurFs
5g7F+FZahzrGqYAZt6uN/er3OQlYWOueklMBkQBo26EPN9GX6wSJJyh+tlXXnIU7
aGs+Y3za8Sf9aitEdZJ1++S7nunzfv6DHmT+qGLgKkeykWJp3pt01l2KfM99n4cu
dTMY1sdI8xjc5bKb1N80xf39GZfCw5btzZctAgMBAAGjIzAhMA4GA1UdDwEB/wQE
AwIABjAPBgNVHRMBAf8EBTADAQH/MAsGCSqGSIb3DQEBCwOCAQEAOy5O7cME57bR
BemhUY9tQrcxJOIu/Wzo6ccHxDzWMJ2aCPuFZGcCvflGKdYorVFDGq4qWAAISrRT
3j5gtfPgDxGlck17RdptM1PB6IM//1WwoZoKO6h6tRyXGjCQr7PvhBB9rWepZfyZ
8CxH6XZY3To0IdVfikXnSgWpFncpmlfl465fBERKkDRN0+5q51wlxPNsykQOzgjo
giJySbYUD345vGDsVwAffwMnnE9xwGB9Xdoyd7AvAaXFmsYONGCb0+kaN4CZQYtR
P1zau8J1jy5KAahfvMIWvih2aWqeqQpNQ9PfSsz5F2C76XvkxnkOicga9tuoJYgo
luF0apj1Qg==
-----END CERTIFICATE-----
\ No newline at end of file

A nix-config/certs/flynn-prod.cer => nix-config/certs/flynn-prod.cer +19 -0
@@ 0,0 1,19 @@
-----BEGIN CERTIFICATE-----
MIIDAzCCAe2gAwIBAgIQWmzx7lHQxRCWh1Nm8gse2zALBgkqhkiG9w0BAQswLTEO
MAwGA1UEChMFRmx5bm4xGzAZBgNVBAsTEkZseW5uIEVwaGVtZXJhbCBDQTAeFw0x
NjAxMTIwMDI0MjdaFw0yMTAxMTAwMDI0MjdaMC0xDjAMBgNVBAoTBUZseW5uMRsw
GQYDVQQLExJGbHlubiBFcGhlbWVyYWwgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQCn5f5K0iqK5ZtE2wjFnxD5hoMa3k9oyvkSflOO7tDyMi+zLyt3
chvbccKcJLiYEWB5+RTu/JzmTNMejxh1toAglrrKTxqQ76t8oHDh0pD661rUELDQ
I4a83Lh3A4JBY2IjFMSWHqSJjEK50HIUoPbkkIkRlBVpZP6n/c4Tgl43VTLiShFz
RndX3PF3+Zxdilo4sIbFGKzw2Gq15qKuSV5P8FRpQMBC5uMAFaC2coxgdHZ0SclV
m/te3f5L3Dg71dLXePqotlCBW89peoOBu3+n8v0IzMB0R4tMm5kT7kGVYWNN//Gf
d4syJ7Q5mg2fWOdfOGiTOgZWw3OI/odn1TnPAgMBAAGjIzAhMA4GA1UdDwEB/wQE
AwIABjAPBgNVHRMBAf8EBTADAQH/MAsGCSqGSIb3DQEBCwOCAQEAAfEDAS/VW7q0
xaWqjtr341h+VKAjLPjgMrrOIli52oco1q5UvYWa5EVSoVtU2NZwzstDOIrnD/2T
+RG1gOdMA+FyRIeC6qmQ7An4Tim2O08TG18jGRHDMzoIi2s4ZSek989OT4ZvLMmX
yIh4M1mNt3v2aSOVEYiUrZ0yibo1i6QgRJSgIJ/QSCCyR1suyKIcQIlYGSgIeA0s
cPUbGhjj2T28oAZDVDPx7QdXRwLz07FAvrblL4mm4LnI/tjZ9Zy5xYqRdEl/Q0uu
PLmE19PrMCXE3r2kS3z+EY2KKbaZyaoP5nkSdx5YI1re6jPp6snZsjyCW7uOpY2Z
VjkJuS7sCQ==
-----END CERTIFICATE-----
\ No newline at end of file

A nix-config/configuration.nix => nix-config/configuration.nix +110 -0
@@ 0,0 1,110 @@
# Edit this configuration file to define what should be installed on
# your system.  Help is available in the configuration.nix(5) man page
# and in the NixOS manual (accessible by running ‘nixos-help’).

{ config, pkgs, ... }:

let machine = "quiver";
    isDesktop = true;
    machinePath = p: let m = "/" + machine;
                     in ./machines + m + p;
    machineConfig = import (machinePath "/config") pkgs;
    userConfig = pkgs.callPackage ./nixpkgs/dotfiles.nix {
      machineSessionCommands = machineConfig.sessionCommands;
    };
    extra = {
      is-minimal = false;
      git-server = import ./misc/git-server.nix;
      util       = import ./misc/util.nix { inherit pkgs; };
      private    = import ./private.nix;
      machine    = machineConfig;
    };
    util = extra.util;
    caches = [ "https://cache.nixos.org" ];
    zsh = "${pkgs.zsh}/bin/zsh";
    composeKey = if machine == "quiver" then "ralt" else "rwin";
    home = "/home/jb55";
    isDark = false;
    theme = if isDark then {
      package = pkgs.theme-vertex;
      name = "Vertex-Dark";
    }
    else {
      package = pkgs.arc-theme;
      name = "Arc";
    };
    icon-theme = {
      package = pkgs.numix-icon-theme;
      name = "Numix";
    };
    user = {
        name = "jb55";
        group = "users";
        uid = 1000;
        extraGroups = [ "wheel" "dialout" ];
        createHome = true;
        openssh.authorizedKeys.keys = [
          "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAvMdnEEAd/ZQM+pYp6ZYG/1NPE/HSwIKoec0/QgGy4UlO0EvpWWhxPaV0HlNUFfwiHE0I2TwHc+KOKcG9jcbLAjCk5rvqU7K8UeZ0v/J83bQh78dr4le09WLyhczamJN0EkNddpCyUqIbH0q3ISGPmTiW4oQniejtkdJPn2bBwb3Za8jLzlh2UZ/ZJXhKvcGjQ/M1+fBmFUwCp5Lpvg0XYXrmp9mxAaO+fxY32EGItXcjYM41xr/gAcpmzL5rNQ9a9YBYFn2VzlpL+H7319tgdZa4L57S49FPQ748paTPDDqUzHtQD5FEZXe7DZZPZViRsPc370km/5yIgsEhMPKr jb55"
        ];
        home = home;
        shell = zsh;
      };
in {
  imports =
    [ # Include the results of the hardware scan.
      ./hardware-configuration.nix
      ./certs
      (import ./services extra)
      (import ./environment extra)
      (import ./networking machine)
      (import (machinePath "") extra)
    ] ++ (if isDesktop then [
      (import ./hardware/desktop extra)
      # ./wayland
      (import ./fonts extra)
      (import ./environment/desktop { inherit userConfig theme icon-theme extra; })
      (import ./services/desktop { inherit extra util composeKey userConfig theme icon-theme; })
    ] else []);

  # Use the GRUB 2 boot loader.
  boot.loader.grub.enable = true;
  environment.ld-linux = true;
  systemd.extraConfig = ''
    DefaultTimeoutStopSec=10s
    DefaultTimeoutStartSec=20s
  '';

  documentation.nixos.enable = false;

  programs.ssh.startAgent = true;

  time.timeZone = "America/Vancouver";

  nixpkgs.config = import ./nixpkgs/config.nix;

  nix.useSandbox = machine != "charon";
  nix.trustedUsers = [ "root" "jb55" ];

  users.extraUsers.jb55 = user;
  users.extraGroups.docker.members = [ "jb55" ];

  users.defaultUserShell = zsh;
  users.mutableUsers = true;

  i18n.consoleUseXkbConfig = true;

  programs.zsh.enable = true;

  boot.kernelPackages = pkgs.linuxPackages_latest;

  # boot.kernelPatches = [{
  #   name = "nintendo-hid";
  #   patch = (pkgs.fetchurl {
  #             url = "https://jb55.com/s/2020-03-24-HID-nintendo-add-nintendo-switch-controller-driver.patch";
  #             sha256 = "660302c88b797df6a89e5e715388ab22144dedfc0174597221a03a987a496a2e";
  #           });
  #   extraConfig = ''
  #     NINTENDO_FF y
  #   '';
  # }];
}

A nix-config/environment/default.nix => nix-config/environment/default.nix +85 -0
@@ 0,0 1,85 @@
extra:
{ config, lib, pkgs, ... }:
let jb55pkgs = import <jb55pkgs> { inherit pkgs; };
    kindle-send = pkgs.callPackage (pkgs.fetchFromGitHub {
      owner = "jb55";
      repo = "kindle-send";
      rev = "0.2.1";
      sha256 = "0xd86s2smjvlc7rlb6rkgx2hj3c3sbcz3gs8rf93x69jqdvwb6rr";
    }) {};
    nixify = pkgs.nur.repos.kampka.nixify;
    myPackages = with jb55pkgs; [
       bcalc
       csv-delim
       csv-scripts
       dbopen
       extname
       kindle-send
       mandown
       samp
       sharefile
       snap
       btcs
    ];
    myHaskellPackages = with pkgs.haskellPackages; [
      #skeletons
    ];

    minimal-pkgs = with pkgs; [
      git-tools
      neovim
      fzf
      ripgrep
    ];

    mypkgs = with pkgs; myHaskellPackages ++ myPackages ++ [
      aspell
      aspellDicts.en
      aspellDicts.en-computers
      aspellDicts.en-science
      bat
      bc
      binutils
      dateutils
      direnv
      du-dust
      file
      fzf
      git-tools
      gnupg
      groff
      haskellPackages.una
      htop
      imagemagick
      jq
      libbitcoin-explorer
      libqalculate
      linuxPackages.bpftrace
      lsof
      manpages
      minisign
      neovim
      network-tools
      nixify
      nodejs
      opentimestamps-client
      par
      parallel
      patchelf
      pv
      python
      ranger
      ripgrep
      rsync
      screen
      shellcheck
      unixtools.xxd
      unzip
      weechat
      wget
      zip
      zstd
    ];
in {
  environment.systemPackages = if extra.is-minimal then minimal-pkgs else mypkgs;
}

A nix-config/environment/desktop/default.nix => nix-config/environment/desktop/default.nix +98 -0
@@ 0,0 1,98 @@
{ userConfig, theme, icon-theme, extra }:
{ config, lib, pkgs, ... }:
let gtk2rc = pkgs.writeText "gtk2rc" ''
      gtk-icon-theme-name = "${icon-theme.name}"
      gtk-theme-name = "${theme.name}"

      binding "gtk-binding-menu" {
        bind "j" { "move-current" (next) }
        bind "k" { "move-current" (prev) }
        bind "h" { "move-current" (parent) }
        bind "l" { "move-current" (child) }
      }
      class "GtkMenuShell" binding "gtk-binding-menu"
    '';

    mypkgs = with pkgs; [
      aerc
      clipmenu
      colorpicker
      dmenu2
      dragon-drop
      dunst
      dynamic-colors
      emacs
      feh
      getmail # for getmail-gmail-xoauth-tokens
      gnome3.gnome-calculator
      gtk-engine-murrine
      lastpass-cli
      libnotify
      msmtp
      muchsync
      notmuch
      oathToolkit
      pandoc
      pavucontrol
      pinentry
      postgresql # psql
      python37Packages.trezor
      qalculate-gtk
      qutebrowser
      rxvt_unicode-with-plugins
      signal-desktop
      simplescreenrecorder
      slock
      spotify
      sxiv
      texlive.combined.scheme-basic
      userConfig
      vlc
      w3m
      wine
      wmctrl
      xautolock
      xbindkeys
      xclip
      xdotool
      xlibs.xev
      xlibs.xmodmap
      xlibs.xset
      zathura
      zoom-us
    ];
in {

  # latest emacs overlay
  #nixpkgs.overlays =  [
  #  (import (builtins.fetchTarball {
  #    url = https://github.com/nix-community/emacs-overlay/archive/773a9f17db9296b45e6b7864d8cee741c8d0d7c7.tar.gz;
  #    sha256 = "157klv69myjmdgqvxv0avv32yfra3i21h5bxjhksvaii1xf3w1gp";
  #  }))
  #];

  environment.variables = lib.mkIf (!extra.is-minimal) {
    LC_TIME="en_DK.UTF-8";
    GDK_PIXBUF_MODULE_FILE = "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache";
    GTK2_RC_FILES = "${gtk2rc}:${theme.package}/share/themes/${theme.name}/gtk-2.0/gtkrc:$GTK2_RC_FILES";
    GTK_DATA_PREFIX = "${theme.package}";
    GTK_EXEC_PREFIX = "${theme.package}";
    GTK_IM_MODULE = "xim";
    GTK_PATH = "${theme.package}:${pkgs.gtk3.out}";
    GTK_THEME = "${theme.name}";
    QT_STYLE_OVERRIDE = "GTK+";
    VK_ICD_FILENAMES = "/run/opengl-driver/share/vulkan/icd.d/radeon_icd.x86_64.json"; # radv
  };

  environment.systemPackages = if extra.is-minimal then (with pkgs; [
    steam
    steam-run
    wine
    lastpass-cli
    rxvt_unicode-with-plugins
  ]) else mypkgs;

  security.wrappers = {
    slock.source = "${pkgs.slock}/bin/slock";
  };
}

A nix-config/fonts/default.nix => nix-config/fonts/default.nix +108 -0
@@ 0,0 1,108 @@
extra:
{ config, lib, pkgs, ... }:
let mkfont = n: lesrc:
               pkgs.stdenv.mkDerivation rec {
                 name = "${n}-${version}";
                 src = pkgs.fetchurl lesrc;
                 version = "1.0";
                 phases = ["installPhase"];

                 installPhase = ''
                   mkdir -p $out/share/fonts/${n}
                   cp -v ${src} $out/share/fonts/${n}
                 '';
               };
    aldrich =
      mkfont "aldrich" {
        url = "https://jb55.com/s/bef303d9e370f941.ttf";
        sha256 = "ecc2fbf1117eed2d0b1bf32ee8624077577d568f1c785699353416b67b519227";
      };
    VarelaRound-Regular =
      mkfont "VarelaRound-Regular" {
        url = "https://jb55.com/s/c8bbd8415dea995f.ttf";
        sha256 = "c4327a38270780eb03d305de3514de62534262c73f9e7235eea6ce26904c2dc5";
      };
    Bookerly-Regular =
      mkfont "Bookerly-Regular" {
        url = "https://jb55.com/s/Bookerly-Regular.ttf";
        sha256 = "1db94d4ab763f812b3fe505c02cdeb0927251c118cc65322be23eb93a70eafd7";
      };
    Bookerly-RegularItalic =
      mkfont "Bookerly-RegularItalic" {
        url = "https://jb55.com/s/Bookerly-RegularItalic.ttf";
        sha256 = "6e364837e08fa89c0fed287a13c7149567ab5657847f666e45e523ecc9c7820b";
      };
    Bookerly-Bold =
      mkfont "Bookerly-Bold" {
        url = "https://jb55.com/s/Bookerly-Bold.ttf";
        sha256 = "367a28ceb9b2c79dbe5956624f023a54219d89f31d6d2e81e467e202273d40da";
      };
    Bookerly-BoldItalic =
      mkfont "Bookerly-BoldItalic" {
        url = "https://jb55.com/s/Bookerly-BoldItalic.ttf";
        sha256 = "d975e3260e26f1b33fc50b00540caece84a0800e9bc900922cf200645e79693f";
      };
    Questrial =
      mkfont "Questrial" {
        url = "https://jb55.com/s/1ccac9ff5cb42fd7.ttf";
        sha256 = "294729bb4bf3595490d2e3e89928e1754a7bfa91ce91e1e44ecd18c974a6dbbc";
      };
    Comfortaa-Regular =
      mkfont "Comfortaa-Regular" {
        url = "https://jb55.com/s/a266c50144cbad1a.ttf";
        sha256 = "db5133b6a09c8eba78b29dc05019d8f361f350483d679fd8c668e1c657a303fc";
      };

    ohsnap =
      pkgs.stdenv.mkDerivation rec {
        name = "ohsnap-${version}";
        version = "1.7.9";

        src = pkgs.fetchzip {
          url = "https://sourceforge.net/projects/osnapfont/files/${name}.tar.gz";
          sha256 = "0jvgii1sdv3gzmx8k68bd3fp2rmfsdigg67spbi2c83krb1x445v";
        };

        phases = ["unpackPhase" "installPhase"];

        installPhase = ''
          mkdir -p $out/share/fonts/ohsnap
          cp ${src}/* $out/share/fonts/ohsnap
        '';
      };

    myfonts = [ aldrich VarelaRound-Regular Questrial Comfortaa-Regular
                Bookerly-Regular Bookerly-RegularItalic Bookerly-Bold Bookerly-BoldItalic ohsnap ];
in
{
  fonts = {
    enableFontDir = true;
    enableGhostscriptFonts = true;
    fontconfig.defaultFonts.serif = [ "Bookerly" ];
    fontconfig.defaultFonts.monospace  = [ "Inconsolata" ];
    fontconfig.defaultFonts.sansSerif  = [ "Noto Sans" ];
    enableDefaultFonts = if extra.is-minimal then false else true;
    fonts = if extra.is-minimal then [pkgs.terminus_font] else (with pkgs; [
      aldrich
      corefonts
      # emojione
      fira-code
      fira-mono
      inconsolata
      ipafont
      kochi-substitute
      libertinus
      ibm-plex
      noto-fonts
      noto-fonts-emoji
      opensans-ttf
      raleway
      profont
      terminus_font
      paratype-pt-mono
      source-code-pro
      ubuntu_font_family
      proggyfonts
    ] ++ myfonts);
  };
}

A nix-config/hardware-configuration.nix => nix-config/hardware-configuration.nix +11 -0
@@ 0,0 1,11 @@
# Do not modify this file!  It was generated by ‘nixos-generate-config’
# and may be overwritten by future invocations.  Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, ... }:

{
  imports = [ ];

  boot.loader.grub.version = 2;
  nix.maxJobs = 8;
}

A nix-config/hardware/desktop/default.nix => nix-config/hardware/desktop/default.nix +112 -0
@@ 0,0 1,112 @@
extra:
{ config, lib, pkgs, ... }:
let
  kindle-opts = ["noatime" "user" "gid=100" "uid=1000" "utf8" "x-systemd.automount"];
in
{
  boot.supportedFilesystems = ["ntfs" "exfat"];

  services.udev.extraRules = ''
    # coldcard
    KERNEL=="hidraw*", ATTRS{idVendor}=="d13e", ATTRS{idProduct}=="cc10", GROUP="plugdev", MODE="0666", SYMLINK+="coldcard

    # yubikey neo
    KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0116", MODE="0666"

    # yubikey4
    KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0407", MODE="0666"

    # kindle
    ATTRS{idVendor}=="1949", ATTRS{idProduct}=="0004", SYMLINK+="kindle"
    ATTRS{idVendor}=="1949", ATTRS{idProduct}=="0003", SYMLINK+="kindledx"

    # HTC Vive HID Sensor naming and permissioning
    # vive hmd
    KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0bb4", ATTRS{idProduct}=="2c87", TAG+="uaccess"

    # vive controller
    KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="28de", ATTRS{idProduct}=="2101", TAG+="uaccess"

    # vive lighthouse
    KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="28de", ATTRS{idProduct}=="2000", TAG+="uaccess"
    KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="28de", ATTRS{idProduct}=="1043", TAG+="uaccess"
    KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="28de", ATTRS{idProduct}=="2050", TAG+="uaccess"
    KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="28de", ATTRS{idProduct}=="2011", TAG+="uaccess"
    KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="28de", ATTRS{idProduct}=="2012", TAG+="uaccess"

    # vive audio
    KERNEL=="hidraw*", ATTRS{idVendor}=="0d8c", ATTRS{idProduct}=="0012", MODE="0666"

    SUBSYSTEM=="usb", ATTRS{idVendor}=="0bb4", ATTRS{idProduct}=="2c87", TAG+="uaccess"

    # HTC Camera USB Node
    SUBSYSTEM=="usb", ATTRS{idVendor}=="114d", ATTRS{idProduct}=="8328", TAG+="uaccess"

    # HTC Mass Storage Node
    SUBSYSTEM=="usb", ATTRS{idVendor}=="114d", ATTRS{idProduct}=="8200", TAG+="uaccess"

    # ds4
    KERNEL=="uinput", MODE="0666"
    KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="05c4", MODE="0666"
    KERNEL=="hidraw*", SUBSYSTEM=="hidraw", KERNELS=="0005:054C:05C4.*", MODE="0666"

    # Nintendo Switch Pro Controller over USB hidraw
    KERNEL=="hidraw*", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="2009", MODE="0666"

    # Nintendo Switch Pro Controller over bluetooth hidraw
    KERNEL=="hidraw*", KERNELS=="*057E:2009*", MODE="0666"

    # rtl-sdr
    SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2832", MODE="0666", SYMLINK+="rtl_sdr"
    SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2838", MODE="0666", SYMLINK+="rtl_sdr_dvb"

    # arduino
    SUBSYSTEM=="usb", ATTRS{idVendor}=="2341", ATTRS{idProduct}=="0043", MODE="0666", SYMLINK+="arduino"
  '';

  services.xserver.config = ''
    Section "InputClass"
      Identifier "Logitech M705"
      MatchIsPointer "yes"
      Option "AccelerationProfile" "-1"
      Option "ConstantDeceleration" "5"
      Option "AccelerationScheme" "none"
      Option "AccelSpeed" "-1"
    EndSection

    Section "InputClass"
      Identifier "Razer Razer DeathAdder 2013"
      MatchIsPointer "yes"
      Option "AccelerationProfile" "-1"
      Option "ConstantDeceleration" "5"
      Option "AccelerationScheme" "none"
      Option "AccelSpeed" "-1"
    EndSection
  '';

  services.printing.drivers = [ pkgs.samsung-unified-linux-driver_4_01_17 ];

  boot.blacklistedKernelModules = ["dvb_usb_rtl28xxu"];
  fileSystems."/media/kindle" =
    { device = "/dev/kindle";
      fsType = "vfat";
      options = kindle-opts;
    };

  fileSystems."/media/kindledx" =
    { device = "/dev/kindledx";
      fsType = "vfat";
      options = kindle-opts;
    };

  hardware = {
    bluetooth.enable = true;
    pulseaudio = {
      package = if extra.is-minimal then pkgs.pulseaudio else pkgs.pulseaudioFull;
      enable = true;
      support32Bit = true;
    };
    opengl.driSupport32Bit = true;
    opengl.driSupport = true;
  };
}

A nix-config/machines/archer/backups/default.nix => nix-config/machines/archer/backups/default.nix +45 -0
@@ 0,0 1,45 @@
extra:
{ config, lib, pkgs, ... }:
let pubkey = pkgs.fetchurl {
               url = "https://jb55.com/pgp.txt";
               sha256 = "012910961fb58b886fc44a8ebedba394240be4e17604703f3b094eef86d5aca5";
             };
in
{
  systemd.services.postgresql-backup = {
    description = "PostgreSQL backups";

    environment = {
      AWS_ACCESS_KEY_ID = extra.private.aws_access_key;
      AWS_SECRET_ACCESS_KEY = extra.private.aws_secret_key;
    };

    unitConfig.OnFailure = "notify-failed@%n.service";
    # Saturday morning? should be fine
    startAt = "Sat *-*-* 08:10:00";
    serviceConfig.ExecStart = let script = pkgs.writeScript "postgresql-backup" ''
      #!${pkgs.bash}/bin/bash
      set -euo pipefail

      filename="Monstercat-pgdev-$(date +%F-%H%M%z).sql.xz.gpg"

      ${pkgs.gnupg}/bin/gpg2 --import ${pubkey} || echo "already have key!"

      ${pkgs.postgresql}/bin/pg_dump Monstercat \
         | ${pkgs.pxz}/bin/pxz -T24 \
         | ${pkgs.gnupg}/bin/gpg2 \
              -e \
              --compress-level 0 \
              --yes \
              --no-tty \
              --output - \
              -r 0x6D3E2004415AF4A3 \
         | ${pkgs.awscli}/bin/aws s3 \
              cp - \
              "s3://data.monstercat.com/backups/pg-dev/$filename"

    '';
    in "${script}";
  };

}

A nix-config/machines/archer/backups/git.nix => nix-config/machines/archer/backups/git.nix +45 -0
@@ 0,0 1,45 @@
extra:
{ config, lib, pkgs, ... }:
let pubkey = pkgs.fetchurl {
               url = "https://jb55.com/pgp.txt";
               sha256 = "012910961fb58b886fc44a8ebedba394240be4e17604703f3b094eef86d5aca5";
             };
in
{
  systemd.services.gitzero-backup = {
    description = "Git repo backups";

    environment = {
      AWS_ACCESS_KEY_ID = extra.private.aws_access_key;
      AWS_SECRET_ACCESS_KEY = extra.private.aws_secret_key;
    };

    unitConfig.OnFailure = "notify-failed@%n.service";
    # Saturday morning? should be fine
    startAt = "*-*-* 03:57:00";
    serviceConfig.ExecStart = let script = pkgs.writeScript "gitzero-backup" ''
      #!${pkgs.bash}/bin/bash
      set -euo pipefail

      filename="Monstercat-gitzero-$(date +%F-%H%M%z).tar.xz.gpg"

      ${pkgs.gnupg}/bin/gpg2 --import ${pubkey} || echo "already have key!"

      ${pkgs.gnutar}/bin/tar --exclude=/var/git/db-backup -cf - /var/git  \
         | ${pkgs.pxz}/bin/pxz -T24 \
         | ${pkgs.gnupg}/bin/gpg2 \
              -e \
              --compress-level 0 \
              --yes \
              --no-tty \
              --output - \
              -r 0x6D3E2004415AF4A3 \
         | ${pkgs.awscli}/bin/aws s3 \
              cp - \
              "s3://data.monstercat.com/backups/gitzero/$filename"

    '';
    in "${script}";
  };

}

A nix-config/machines/archer/backups/wiki.nix => nix-config/machines/archer/backups/wiki.nix +42 -0
@@ 0,0 1,42 @@
extra:
{ config, lib, pkgs, ... }:
let pubkey = pkgs.fetchurl {
               url = "https://jb55.com/pgp.txt";
               sha256 = "012910961fb58b886fc44a8ebedba394240be4e17604703f3b094eef86d5aca5";
             };
in
{
  systemd.services.wiki-backup = {
    description = "Wiki backups";

    environment = {
      AWS_ACCESS_KEY_ID = extra.private.aws_access_key;
      AWS_SECRET_ACCESS_KEY = extra.private.aws_secret_key;
    };

    unitConfig.OnFailure = "notify-failed@%n.service";
    startAt = "Sat *-*-* 02:57:00";
    serviceConfig.ExecStart = extra.util.writeBash "wiki-backup" ''
      set -euo pipefail

      filename="Monstercat-wiki-$(date +%F-%H%M%z).tar.xz.gpg"

      ${pkgs.gnupg}/bin/gpg2 --import ${pubkey} || echo "already have key!"

      ${pkgs.gnutar}/bin/tar -cf - /var/lib/gitit  \
         | ${pkgs.pxz}/bin/pxz -T24 \
         | ${pkgs.gnupg}/bin/gpg2 \
              -e \
              --compress-level 0 \
              --yes \
              --no-tty \
              --output - \
              -r 0x6D3E2004415AF4A3 \
         | ${pkgs.awscli}/bin/aws s3 \
              cp - \
              "s3://data.monstercat.com/backups/wiki/$filename"

    '';
  };

}

A nix-config/machines/archer/bandcamp-sales-bot/default.nix => nix-config/machines/archer/bandcamp-sales-bot/default.nix +22 -0
@@ 0,0 1,22 @@
extra:
{ config, lib, pkgs, ... }:
let cfg = extra.private;
in
{
  systemd.services.bandcamp-sales-bot = {
    description = "bandcamp sales bot";

    environment = {
      BANDCAMP_USER = cfg.bandcamp-user;
      BANDCAMP_PASS = cfg.bandcamp-pass;
      AWS_ACCESS_KEY_ID = cfg.aws_access_key;
      AWS_SECRET_ACCESS_KEY = cfg.aws_secret_key;
    };

    serviceConfig.ExecStart = "${extra.import-scripts}/bin/bandcamp-sales-bot";
    unitConfig.OnFailure = "notify-failed@%n.service";

    # 3rd day of each month
    startAt = "*-*-03 8:30:00";
  };
}

A nix-config/machines/archer/beatport-sales-bot/default.nix => nix-config/machines/archer/beatport-sales-bot/default.nix +25 -0
@@ 0,0 1,25 @@
extra:
{ config, lib, pkgs, ... }:
let cfg = extra.private;
    util = extra.util;
    import-scripts = extra.import-scripts;
in
{
  systemd.user.services.shopify-sales-bot = {
    description = "beatport sales bot";

    wantedBy = [ "default.target" ];
    after    = [ "default.target" ];

    environment = {
      SHOPIFY_USER = extra.private.beatport-user;
      SHOPIFY_PASS = extra.private.beatport-pass;
    };

    serviceConfig.ExecStart = "${import-scripts}/bin/beaport-sales-bot";
    unitConfig.OnFailure = "notify-failed-user@%n.service";

    # 20th is always before the earliest possible last wednesday (22nd)
    startAt = "*-*-20 7:30:00";
  };
}

A nix-config/machines/archer/cogs-bot/default.nix => nix-config/machines/archer/cogs-bot/default.nix +23 -0
@@ 0,0 1,23 @@
extra:
{ config, lib, pkgs, ... }:
let cfg = extra.private;
    util = extra.util;
    import-scripts = extra.import-scripts;
in
{
  systemd.user.services.cogs-bot = {
    description = "cogs bot";

    wantedBy = [ "default.target" ];
    after    = [ "default.target" ];

    environment = {
      COGS_SHEET_ID="1lIluimJqBlGK1yRTmsekwUmk0_Wk0wD9VErUE8z6_dY";
    };

    serviceConfig.ExecStart = "${import-scripts}/bin/cogs-bot daily-check";
    unitConfig.OnFailure = "notify-failed-user@%n.service";

    startAt = "*-*-* 5:30:00";
  };
}

A nix-config/machines/archer/config/default.nix => nix-config/machines/archer/config/default.nix +5 -0
@@ 0,0 1,5 @@
pkgs: {
  sessionCommands = ''
    ${pkgs.xlibs.xset}/bin/xset m 0 0
  '';
}

A nix-config/machines/archer/default.nix => nix-config/machines/archer/default.nix +146 -0
@@ 0,0 1,146 @@
extra:
{ config, lib, pkgs, ... }:
let util = extra.util;
    private = extra.private;
    extras = (rec { ztip = "10.144.14.20";
                    nix-serve = {
                      port = 10845;
                      bindAddress = ztip;
                    };
                    import-scripts = (import <monstercatpkgs> { }).import-scripts;
                 }) // extra;
in {
  imports = [
    ./hardware
    (import ./backups extras)
    (import ./backups/git.nix extras)
    (import ./backups/wiki.nix extras)
    (import ./nginx extras)
    (import ./trendbot extras)
    (import ./transaction-bot extras)
    (import ./tunecore-sales-bot extras)
    (import ./bandcamp-sales-bot extras)
    (import ./youtube-sales-bot extras)
    (import ./youtube-pub-sales-bot extras)
    (import ./shopify-sales-bot extras)
    (import ./itunes-bots extras)
    (import ./cogs-bot extras)
    (import <nixpkgs/nixos/modules/services/misc/gitit.nix>)
  ];

  services.printing.drivers = [ pkgs.samsung-unified-linux-driver_4_01_17 ];
  services.mongodb.enable = true;
  services.redis = {
    enable = true;
    bind = extras.ztip;
  };

  services.gitit = rec {
    enable = true;
    wikiTitle = "Monstercat Wiki";
    requireAuthentication = "none";
    sessionTimeout = 43800;
    math = "mathml";
    mathJaxScript = "MathJax/MathJax.js";
    plugins = [];
    mailCommand = "/run/current-system/sw/bin/sendmail %s";
    accessQuestion = "Enter 'monstercat' here";
    accessQuestionAnswers = "monstercat";
    staticDir = "/var/lib/gitit-static";
    useFeed = true;
    resetPasswordMessage = ''

      	> From: gitit@monstercat.com
      	> To: $useremail$
      	> Subject: ${wikiTitle} password reset
      	>
      	> Hello $username$,
      	>
      	> To reset your password, please follow the link below:
      	> http://wiki.monstercat.com$resetlink$
      	>
      	> Regards
    '';
  };

  users.extraGroups.gitit.members = [ "jb55" ];

  services.nginx.httpConfig = ''
    server {
      listen 80;
      server_name pkgs.monster.cat;

      location = / {
        return 301 https://github.com/monstercat/monstercatpkgs/archive/master.tar.gz;
      }
    }

    server {
      listen 80;
      server_name nixcache.monstercat.com;

      location / {
        proxy_pass  http://${extras.nix-serve.bindAddress}:${toString extras.nix-serve.port};
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_redirect off;
        proxy_buffering off;
        proxy_set_header        Host            $host;
        proxy_set_header        X-Real-IP       $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
      }
    }

    server {
      listen 80;
      server_name wiki.monstercat.com wiki.monster.cat;

      location / {
        proxy_pass  http://localhost:${toString config.services.gitit.port};
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_redirect off;
        proxy_buffering off;
        proxy_set_header        Host            $host;
        proxy_set_header        X-Real-IP       $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
      }
    }
  '';

  services.nix-serve.enable = true;
  services.nix-serve.bindAddress = extras.nix-serve.bindAddress;
  services.nix-serve.port = extras.nix-serve.port;

  networking.firewall.trustedInterfaces = ["zt0" "zt2"];
  networking.firewall.allowedTCPPorts = [ 22 143 80 ];

  networking.defaultMailServer = {
    directDelivery = private.gmail-user != null || private.gmail-pass != null;
    hostName = "smtp.gmail.com:587";
    root = "bill@monstercat.com";
    domain = "monstercat.com";
    useTLS = true;
    useSTARTTLS = true;
    authUser = private.gmail-user;
    authPass = private.gmail-pass;
  };

  services.fcgiwrap.enable = true;

  systemd.services.postgresql.after = [ "zerotierone.service" ];

  services.postgresql = {
    dataDir = "/var/db/postgresql/9.5/";
    enable = true;
    # extraPlugins = with pkgs; [ pgmp ];
    authentication = pkgs.lib.mkForce ''
      # type db  user address        method
      local  all all                 trust
      host   all all  10.144.0.0/16  trust
      host   all all  192.168.1.0/16 trust

    '';
    extraConfig = ''
      listen_addresses = '10.144.14.20,192.168.1.49'
    '';
  };
}

A nix-config/machines/archer/fail-notifier/default.nix => nix-config/machines/archer/fail-notifier/default.nix +29 -0
@@ 0,0 1,29 @@
{ config, lib, pkgs, ... }:
{
  systemd.services."notify-failed@" = {
    description = "Job failure notifier";

    serviceConfig.ExecStart = let script = pkgs.writeScript "failure-notifier" ''
      #!${pkgs.bash}/bin/bash

      UNIT=$1

      /var/setuid-wrappers/sendmail -t <<ERRMAIL
      To: bill@monstercat.com
      From: systemd <root@$HOSTNAME>
      Subject: $UNIT Failed
      Content-Transfer-Encoding: 8bit
      Content-Type: text/plain; charset=UTF-8

      $2
      $3
      $4

      $(systemctl status $UNIT)
      ERRMAIL
    '';
    in "${script} %I 'Hostname: %H' 'Machine ID: %m' 'Boot ID: %b'";

  };

}

A nix-config/machines/archer/hardware/default.nix => nix-config/machines/archer/hardware/default.nix +36 -0
@@ 0,0 1,36 @@
# Do not modify this file!  It was generated by ‘nixos-generate-config’
# and may be overwritten by future invocations.  Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, ... }:

{
  imports =
    [ <nixpkgs/nixos/modules/installer/scan/not-detected.nix>
    ];

  boot.initrd.availableKernelModules = [ "ehci_pci" "ahci" "xhci_hcd" "firewire_ohci" "usb_storage" "usbhid" ];
  boot.kernelModules = [ "kvm-intel" ];
  boot.extraModulePackages = [ ];
  boot.loader.grub.device = "/dev/sda";

  fileSystems."/" = {
    device = "/dev/disk/by-uuid/4b076497-f2f8-4e3a-bd27-2874a4a0e361";
    fsType = "ext4";
    options = ["noatime" "nodiratime" "discard"];
  };

  fileSystems."/shares/turtlerock" = {
    device = "//192.168.1.66/Confidential\\040Share";
    fsType = "cifs";
    options = ["x-systemd.automount" "x-systemd.idle-timeout=1min" "username=bill" "password=connect123" "uid=1000" "gid=1000" "workgroup=WORKGROUP" "rw"];
  };

  fileSystems."/dropbox" = {
    device = "/dev/disk/by-label/vertex";
    fsType = "ext4";
    options = ["noatime" "nodiratime" "discard"];
  };

  # swapDevices =
  #   [ { device = "/dev/disk/by-uuid/d4e4ae51-9179-439d-925b-8df42dd1bfc5"; } ] ;
}

A nix-config/machines/archer/itunes-bots/default.nix => nix-config/machines/archer/itunes-bots/default.nix +40 -0
@@ 0,0 1,40 @@
extra:
{ config, lib, pkgs, ... }:
let util = extra.util;
    import-scripts = extra.import-scripts;
    countries = pkgs.fetchurl {
      url    = "https://jb55.com/s/8536f14537bbb417.csv";
      sha256 = "9c31690e31f5a26b12bc5a16d3a1508a06ac1d842e4a129868bc7aaf33358ab5";
    };
in
{
  systemd.user.services.itunes-sales-bot = {
    description = "itunes sales bot";

    wantedBy = [ "default.target" ];
    after    = [ "default.target" ];

    environment = {
      ISO_3166_COUNTRIES = countries;
    };

    serviceConfig.ExecStart = "${import-scripts}/bin/itunes-sales-bot";
    unitConfig.OnFailure = "notify-failed-user@%n.service";

    # First tuesday of every month @ 1600
    startAt = "Tue *-*-1..7 11:30:00";
  };

  systemd.user.services.itunes-transaction-bot = {
    description = "itunes transaction bot";

    wantedBy = [ "default.target" ];
    after    = [ "default.target" ];

    serviceConfig.ExecStart = "${import-scripts}/bin/itunes-transaction-bot";
    unitConfig.OnFailure = "notify-failed-user@%n.service";

    # First tuesday of every month @11
    startAt = "Tue *-*-1..7 11:00:00";
  };
}

A nix-config/machines/archer/nginx/default.nix => nix-config/machines/archer/nginx/default.nix +95 -0
@@ 0,0 1,95 @@
extra:
{ config, lib, pkgs, ... }:
let sites = [ ];
    logDir = "/var/log/nginx";
    gitExtra = {
      git = {
        projectroot = "/var/git";
      };
    };
    gitCfg = import ./git.nix { inherit config pkgs; extra = extra // gitExtra; };
    hoogle = import ./hoogle.nix extra.ztip;
    nixserve = import ./nix-serve.nix extra;
in {
  services.logrotate.config = ''
    ${logDir}/*.log {
      daily
      missingok
      rotate 52
      compress
      delaycompress
      notifempty
      # 20MB
      minsize 20971520
      create 640 root adm
      sharedscripts
      postrotate
              ${pkgs.procps}/bin/pkill -USR1 nginx
      endscript
    }
  '';

  services.nginx = {
    enable = true;

    httpConfig = ''
      port_in_redirect off;
      ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
      ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
      ssl_prefer_server_ciphers on;

      # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
      add_header Strict-Transport-Security max-age=15768000;

      sendfile on;
      tcp_nopush on;
      tcp_nodelay on;
      keepalive_timeout 65;
      types_hash_max_size 2048;
      client_max_body_size 6G;

      # server_tokens off;
      proxy_buffering off;
      proxy_read_timeout 300s;
      expires off;
      default_type application/octet-stream;

      access_log ${logDir}/access.log;
      error_log ${logDir}/error.log;

      gzip on;
      gzip_disable "msie6";

      server {
        listen      80;
        server_name archer.zero.monster.cat;

        root /www/public;
        index index.html index.htm;

        location / {
          try_files $uri $uri/ =404;
        }
      }

      server {
        listen       80;
        server_name  siren.zero.monster.cat;

        location / {
          include ${pkgs.nginx}/conf/fastcgi_params;
          gzip off;

          fastcgi_param SCRIPT_FILENAME /home/jb55/src/c/libsirenofshame/siren-rest.fcgi;
          fastcgi_param PATH_INFO       $uri;
          fastcgi_pass  unix:${config.services.fcgiwrap.socketAddress};
        }
      }

      ${lib.concatStringsSep "\n\n" (map builtins.readFile sites)}

      ${gitCfg}
      ${hoogle}
    '';
  };
}

A nix-config/machines/archer/nginx/git.nix => nix-config/machines/archer/nginx/git.nix +70 -0
@@ 0,0 1,70 @@

{ extra, config, pkgs }:
let gitwebConf = pkgs.writeText "gitweb.conf" ''
      # path to git projects (<project>.git)
      $projectroot = "${extra.git.projectroot}";
    '';
    gitweb-wrapper = pkgs.writeScript "gitweb.cgi" ''
      #!${pkgs.bash}/bin/bash
      export PERL5LIB=$PERL5LIB:${with pkgs.perlPackages; pkgs.lib.makePerlPath [ CGI HTMLParser ]}
      ${pkgs.perl}/bin/perl ${pkgs.git}/share/gitweb/gitweb.cgi
    '';
    gitweb-theme = pkgs.fetchFromGitHub {
      owner  = "kogakure";
      repo   = "gitweb-theme";
      rev    = "4305b3551551c470339c24a6567b1ac9e642ae54";
      sha256 = "0gagy0jvqb3mc587b6yy8l9g5j5wqr2xlz128v6f01364cb7whmv";
    };
    giturl = "git.monster.cat";
in
if config.services.fcgiwrap.enable then ''
  server {
      listen       80;
      server_name  ${giturl};

      location = / {
        return 301 http://${giturl}/repos/;
      }

      location = /repos {
        return 301 http://${giturl}/repos/;
      }

      location / {
        # fcgiwrap is set up to listen on this host:port
        fastcgi_pass                  unix:${config.services.fcgiwrap.socketAddress};
        include                       ${pkgs.nginx}/conf/fastcgi_params;
        fastcgi_param SCRIPT_FILENAME ${pkgs.git}/bin/git-http-backend;

        client_max_body_size 0;

        # export all repositories under GIT_PROJECT_ROOT

        fastcgi_param GIT_HTTP_EXPORT_ALL "";
        fastcgi_param GIT_PROJECT_ROOT    ${extra.git.projectroot};
        fastcgi_param PATH_INFO           $uri;
      }

      location /repos/static {
        alias ${gitweb-theme};
      }

      location /add-repo {
        include ${pkgs.nginx}/conf/fastcgi_params;
        gzip off;

        fastcgi_param SCRIPT_FILENAME /var/git/mkrepod;
        fastcgi_pass  unix:${config.services.fcgiwrap.socketAddress};
      }

      location /repos {
        include ${pkgs.nginx}/conf/fastcgi_params;
        gzip off;

        fastcgi_param GITWEB_CONFIG   ${gitwebConf};
        fastcgi_param SCRIPT_FILENAME ${gitweb-wrapper};
        fastcgi_pass  unix:${config.services.fcgiwrap.socketAddress};
      }

  }
'' else ""

A nix-config/machines/archer/nginx/hoogle.nix => nix-config/machines/archer/nginx/hoogle.nix +16 -0
@@ 0,0 1,16 @@
ztip: ''
  server {
    listen 80;
    server_name hoogle.zero.monster.cat;

    location / {
      proxy_pass  http://localhost:8080;
      proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
      proxy_redirect off;
      proxy_buffering off;
      proxy_set_header        Host            $host;
      proxy_set_header        X-Real-IP       $remote_addr;
      proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    }
  }
''
\ No newline at end of file

A nix-config/machines/archer/nginx/nix-serve.nix => nix-config/machines/archer/nginx/nix-serve.nix +7 -0
@@ 0,0 1,7 @@
config:
let
    port = config.nix-serve.port;
    bind = config.ztip;
    localbind = config.nix-serve.bindAddress;
in ''
''

A nix-config/machines/archer/nginx/sites/local => nix-config/machines/archer/nginx/sites/local +17 -0
@@ 0,0 1,17 @@

server {
  listen 80;
  server_name archer.;
  root /www/jb55/public;
  index index.html index.htm;

  location / {
    try_files $uri $uri/ =404;
  }
}

server {
  listen 80;
  server_name www.archer.;
  return 301 https://archer.$request_uri;
}
\ No newline at end of file

A nix-config/machines/archer/payments-runner/default.nix => nix-config/machines/archer/payments-runner/default.nix +9 -0
@@ 0,0 1,9 @@
extra:
{ config, lib, pkgs, ... }:
let
  monstercatpkgs = import <monstercatpkgs> {};
  payments-processor = monstercatpkgs.payments-processor;
  payment-scripts = monstercatpkgs.payment-scripts;
in
{
}

A nix-config/machines/archer/payments-server/default.nix => nix-config/machines/archer/payments-server/default.nix +57 -0
@@ 0,0 1,57 @@
extra:
{ config, lib, pkgs, ... }:
let
  port = "8989";
  monstercatpkgs = import <monstercatpkgs> {};
  payments-server = monstercatpkgs.payments-server;
  payments-client = monstercatpkgs.payments-client;
in
{
  services.nginx.httpConfig = lib.mkIf config.services.nginx.enable ''
    server {
      listen 80;
      server_name payments.zero.monster.cat;
      root ${payments-client}/share;
      index index.html;

      location ^~ /api/ {
        proxy_pass  http://localhost:${port}/;
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_redirect off;
        proxy_buffering off;
        proxy_intercept_errors on;
        proxy_set_header        Host            $host;
        proxy_set_header        X-Real-IP       $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
      }

      location / {
        try_files $uri $uri /index.html;
      }
    }
  '';

  systemd.services.payments-server = {
    description = "Monstercat Payments Server";

    wantedBy = [ "multi-user.target" ];
    after = [ "network.target" "redis.service" "postgresql.service" ];

    environment = with extra.private; {
      POSTGRES_USER     = "jb55";
      POSTGRES_PASSWORD = "";
      POSTGRES_HOST     = "db.zero.monster.cat";
      POSTGRES_DATABASE = "Monstercat";
      REDIS_URL         = "redis://redis.zero.monster.cat:6379";
      PORT              = port;
      AWS_ACCESS_KEY    = aws_access_key;
      AWS_PRIVATE_KEY   = aws_secret_key;
      AWS_REGION        = aws_region;
      AWS_BUCKET        = aws_bucket;
    };

    serviceConfig.ExecStart = "${payments-server}/bin/payments-server";
    serviceConfig.Restart = "always";
    unitConfig.OnFailure = "notify-failed@%n.service";
  };
}

A nix-config/machines/archer/shopify-sales-bot/default.nix => nix-config/machines/archer/shopify-sales-bot/default.nix +22 -0
@@ 0,0 1,22 @@
extra:
{ config, lib, pkgs, ... }:
let cfg = extra.private;
    util = extra.util;
    import-scripts = extra.import-scripts;
in
{
  systemd.user.services.shopify-sales-bot = {
    description = "shopify sales bot";

    environment = {
      SHOPIFY_USER = extra.private.shopify-user;
      SHOPIFY_PASS = extra.private.shopify-pass;
    };

    serviceConfig.ExecStart = "${import-scripts}/bin/shopify-sales-bot";
    unitConfig.OnFailure = "notify-failed-user@%n.service";

    # 20th is always before the earliest possible last wednesday (22nd)
    startAt = "*-*-20 8:30:00";
  };
}

A nix-config/machines/archer/transaction-bot/default.nix => nix-config/machines/archer/transaction-bot/default.nix +21 -0
@@ 0,0 1,21 @@
extra:
{ config, lib, pkgs, ... }:
{
  systemd.user.services.transaction-bot = {
    description = "tc transaction bot";

    wantedBy = [ "default.target" ];
    after    = [ "default.target" ];

    environment = {
      TUNECORE_USER = extra.private.tc-user;
      TUNECORE_PASS = extra.private.tc-pass;
    };

    serviceConfig.ExecStart = "${extra.import-scripts}/bin/tunecore-transaction-bot";
    unitConfig.OnFailure = "notify-failed@%n.service";

    startAt = "*-*-* 01:00:00";
  };
}


A nix-config/machines/archer/trendbot/default.nix => nix-config/machines/archer/trendbot/default.nix +21 -0
@@ 0,0 1,21 @@
extra:
{ config, lib, pkgs, ... }:
{
  systemd.user.services.trend-bot = {
    description = "tc trend bot";

    wantedBy = [ "default.target" ];
    after    = [ "default.target" ];

    environment = {
      TUNECORE_USER = extra.private.tc-user;
      TUNECORE_PASS = extra.private.tc-pass;
    };

    serviceConfig.ExecStart = "${extra.import-scripts}/bin/trend-bot";
    unitConfig.OnFailure = "notify-failed@%n.service";

    startAt = "*-*-* 23:59:00";
  };
}


A nix-config/machines/archer/tunecore-sales-bot/default.nix => nix-config/machines/archer/tunecore-sales-bot/default.nix +24 -0
@@ 0,0 1,24 @@
extra:
{ config, lib, pkgs, ... }:
{
  systemd.user.services.tunecore-sales-bot = {
    description = "tc sales bot";

    wantedBy = [ "default.target" ];
    after    = [ "default.target" ];

    environment = {
      TUNECORE_USER = extra.private.tc-user;
      TUNECORE_PASS = extra.private.tc-pass;
      AWS_ACCESS_KEY_ID = extra.private.aws_access_key;
      AWS_SECRET_ACCESS_KEY = extra.private.aws_secret_key;
    };

    serviceConfig.ExecStart = "${extra.import-scripts}/bin/tunecore-sales-bot daily-check";
    unitConfig.OnFailure = "notify-failed@%n.service";

    # monthly, first tuesday
    startAt = "Tue *-*-1..7 10:30:00";
  };
}


A nix-config/machines/archer/youtube-pub-sales-bot/default.nix => nix-config/machines/archer/youtube-pub-sales-bot/default.nix +23 -0
@@ 0,0 1,23 @@

extra:
{ config, lib, pkgs, ... }:
let cfg = extra.private;
in
{
  systemd.user.services.youtube-pub-sales-bot = {
    description = "youtube publishing sales bot";

    environment = {
      YOUTUBE_TYPE="publishing";
      YOUTUBE_OWNER_ID="svBi-FFZiMepj02zaNfDNQ";
    };

    serviceConfig.ExecStart = "${extra.import-scripts}/bin/youtube-sales-bot";
    unitConfig.OnFailure = "notify-failed-user@%n.service";

    # monthly, more than half way through the month. This is because YouTube
    # updates these sheets all the way up to at most half the month (highest
    # I've seen is ~15th)
    startAt = "*-*-20 11:00:00";
  };
}

A nix-config/machines/archer/youtube-sales-bot/default.nix => nix-config/machines/archer/youtube-sales-bot/default.nix +20 -0
@@ 0,0 1,20 @@
extra:
{ config, lib, pkgs, ... }:
let cfg = extra.private;
in
{
  systemd.user.services.youtube-sales-bot = {
    description = "youtube sales bot";

    wantedBy = [ "default.target" ];
    after    = [ "default.target" ];

    serviceConfig.ExecStart = "${extra.import-scripts}/bin/youtube-sales-bot";
    unitConfig.OnFailure = "notify-failed-user@%n.service";

    # monthly, more than half way through the month. This is because YouTube
    # updates these sheets all the way up to at most half the month (highest
    # I've seen is ~15th)
    startAt = "*-*-20 10:24:00";
  };
}

A nix-config/machines/charon/config/default.nix => nix-config/machines/charon/config/default.nix +1 -0
@@ 0,0 1,1 @@
pkgs: {}

A nix-config/machines/charon/default.nix => nix-config/machines/charon/default.nix +453 -0
@@ 0,0 1,453 @@
extra:
{ config, lib, pkgs, ... }:
let gitExtra = {
      git = {projectroot = "/var/git";};
      host = "git.zero.jb55.com";
    };
    httpipePort = "8899";
    # httpiped = (import (pkgs.fetchgit {
    #   url = https://github.com/jb55/httpipe;
    #   rev = "376de0e37bba505ba5f23c46435277bb74603acd";
    #   sha256 = "1x9d98z6zbs22x38xwxjnb6mwladbah9xajyl7kk8bm418l8wac4";
    # }) { nodejs = pkgs.nodejs; }).package;
    npmrepo = (import (pkgs.fetchFromGitHub {
      owner  = "jb55";
      repo   = "npm-repo-proxy";
      rev    = "bef839a95736588ec40c917fa63d490cd736f307";
      sha256 = "1j2xclgcmz9hbf47k4ygyzmiradfg9q30m8bzr1i2x91kz1ck946";
    }) {}).package;
    gitCfg = extra.git-server { inherit config pkgs; extra = extra // gitExtra; };
    hearpress = (import <jb55pkgs> { nixpkgs = pkgs; }).hearpress;
    myemail = "jb55@jb55.com";
    radicale-rights = pkgs.writeText "radicale-rights" ''
      [vanessa-famcal-access]
      user = vanessa
      collection = jb55/4bcae62e-9c8b-0d94-d8ef-977a29a24a84
      permission = rw

      # Give owners read-write access to everything else:
      [owner-write]
      user = .+
      collection = %(login)s(/.*)?
      permission = rw

      # Everyone can read the root collection
      [read]
      user = .*
      collection =
      permission = r
    '';
    jb55-activity = pkgs.writeText "jb55-custom-activity" ''
      {
        "@context": [
          "https://www.w3.org/ns/activitystreams"
        ],
        "inbox": "https://jb55/inbox",
        "id": "https://jb55.com",
        "type": "Person",
        "preferredUsername": "jb55",
        "name": "William Casarin",
        "summary": "This is not a real activitypub endpoint yet! I'm still building it",
        "url": "https://jb55.com",
        "manuallyApprovesFollowers": false,
        "icon": {
          "type": "Image",
          "mediaType": "image/jpeg",
          "url": "https://jb55.com/me.jpg"
        },
        "publicKey": {
          "id": "https://jb55.com#main-key",
          "owner": "https://jb55.com",
          "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnJOPxwmRGBBQYm7YgHRu\nbTaYaKbMoEQiui+37nizXA73CRNeKblSXIaJnfOKfz/ttRG0GH43GzHTpghUDuZX\n+QBpyOk8UMmCW5gM0Y5c3IOv0zLezqLXrVEM8UXMUHE3hxf61r1NKl1+IG9MwhtH\nayx0Kaz6vT/V8nkotCSlb91lMT8X28bButwN86RCclZncecQXuVvgXnFeZCeBLM+\nqV2tBPnn14Ws+AqVvVnBW8xXwVfSPFHQchSLAusdWI7Kw/oWN/on2CqfRASoaVAS\nqKG+uPuJ+1f92iH0ZY1wLB2/ITl7HKTiIMKNikXTWcUudkMlKxc5Iqb7HMHuaPZ9\nIQIDAQAB\n-----END PUBLIC KEY-----"
        }
      }
    '';
    webfinger = pkgs.writeText "webfinger-acct-jb55" ''
      {
        "subject": "acct:jb55@jb55.com",
        "aliases": [
          "https://jb55.com"
        ],
        "links": [
          {
            "rel": "http://webfinger.net/rel/profile-page",
            "type": "text/html",
            "href": "https://jb55.com"
          },
          {
            "rel": "self",
            "type": "application/activity+json",
            "href": "https://jb55.com"
          }
        ]
      }
    '';
in
{
  imports = [
    ./networking
    ./hardware
    (import ./nginx extra)
    (import ./sheetzen extra)
    #(import ./vidstats extra)
  ];

  # systemd.services.httpiped = {
  #   description = "httpiped";
  #   wantedBy = [ "multi-user.target" ];
  #   after    = [ "multi-user.target" ];
  #   environment = {
  #     PORT = httpipePort;
  #   };
  #   serviceConfig.Restart = "always";
  #   serviceConfig.ExecStart = "${httpiped}/bin/httpiped";
  # };

  services.xinetd.enable = true;
  services.xinetd.services =
  [
    { name = "gopher";
      port = 70;
      server = "${pkgs.gophernicus}/bin/in.gophernicus";
      serverArgs = "-h jb55.com -nf -r /var/gopher";
      extraConfig = ''
        disable = no
      '';
    }
  ];

  users.extraGroups.jb55cert.members = [ "prosody" "nginx" ];

  services.gitDaemon.basePath = "/var/git-public/repos";
  services.gitDaemon.enable = true;

  services.radicale.enable = true;
  services.radicale.config = ''
    [auth]
    type = htpasswd
    htpasswd_filename = /home/jb55/.config/radicale/users
    htpasswd_encryption = plain
    delay = 1

    [storage]
    filesystem_folder = /home/jb55/.config/radicale/data

    [server]
    hosts = 127.0.0.1:5232
    ssl = False
    max_connections = 20

    # 1 Megabyte
    max_content_length = 10000000

    timeout = 10

    [rights]
    type = from_file
    file = ${radicale-rights}
  '';

  security.acme.certs."jb55.com" = {
    webroot = "/var/www/challenges";
    group = "jb55cert";
    allowKeysForGroup = true;
    postRun = "systemctl restart prosody";
    email = myemail;
  };

  security.acme.certs."coretto.io" = {
    webroot = "/var/www/challenges";
    email = myemail;
  };

  security.acme.certs."git.jb55.com" = {
    webroot = "/var/www/challenges";
    group = "jb55cert";
    allowKeysForGroup = true;
    email = myemail;
  };

  security.acme.certs."sheetzen.com" = {
    webroot = "/var/www/challenges";
    email = myemail;
  };

  security.acme.certs."hearpress.com" = {
    webroot = "/var/www/challenges";
    email = myemail;
  };

  services.mailz = {
    enable = true;
    domain = "jb55.com";

    users = {
      jb55 = {
        password = "$6$KHmFLeDBaXBE1Jkg$eEN8HM3LpZ4muDK/JWC25qW9xSZq0AqsF4tlzEan7yctROJ9A/lSqz6gN1b1GtwE7efroXGHtDi2FEJ2ujDAl0";
        aliases = [ "postmaster" "bill" "will" "william" "me" "jb" ];
      };
    };

    sieves = builtins.readFile ./dovecot/filters.sieve;
  };

  users.extraUsers.prosody.extraGroups = [ "jb55cert" ];
  services.prosody.enable = true;
  services.prosody.admins = [ "jb55@jb55.com" ];
  services.prosody.allowRegistration = false;
  services.prosody.extraModules = [
    # "cloud_notify"
    # "smacks"
    # "carbons"
    # "http_upload"
  ];
  services.prosody.extraConfig = ''
    c2s_require_encryption = true
  '';
  services.prosody.ssl = {
    cert = "/var/lib/acme/jb55.com/fullchain.pem";
    key = "/var/lib/acme/jb55.com/key.pem";
  };
  services.prosody.virtualHosts.jb55 = {
    enabled = true;
    domain = "jb55.com";
    ssl = {
      cert = "/var/lib/acme/jb55.com/fullchain.pem";
      key = "/var/lib/acme/jb55.com/key.pem";
    };
  };

  services.postgresql = {
    dataDir = "/var/db/postgresql/9.5";
    package = pkgs.postgresql95;
    enable = true;
    enableTCPIP = true;
    authentication = ''
      # type db  user address        method
      local  all all                 trust
      host   all all  172.24.0.0/16  trust
      host   all all  127.0.0.1/16  trust
    '';
    #extraConfig = ''
    #  listen_addresses = '${extra.ztip}'
    #'';
  };

  systemd.services.npmrepo = {
    description = "npmrepo.com";

    wantedBy = [ "multi-user.target" ];

    serviceConfig.Type = "simple";
    serviceConfig.ExecStart = "${npmrepo}/bin/npm-repo-proxy";
  };

  systemd.user.services.rss2email = {
    description = "run rss2email";
    path = with pkgs; [ rss2email ];
    wantedBy = [ "default.target" ];
    serviceConfig.ExecStart = "${pkgs.rss2email}/bin/r2e run";
  };

  systemd.user.services.backup-rss2email = {
    description = "backup rss2email";
    wantedBy = [ "default.target" ];
    serviceConfig.ExecStart = pkgs.writeScript "backup-rss2email" ''
      #!${pkgs.bash}/bin/bash
      BACKUP_DIR=/home/jb55/backups/rss2email
      cp /home/jb55/.config/rss2email.cfg $BACKUP_DIR
      cp /home/jb55/.local/share/rss2email.json $BACKUP_DIR
      cd $BACKUP_DIR
      ${pkgs.git}/bin/git add -u
      ${pkgs.git}/bin/git commit -m "bump"
      ${pkgs.git}/bin/git push
    '';
  };

  systemd.user.timers.backup-rss2email = {
    wantedBy = [ "timers.target" ];
    timerConfig.OnCalendar = "daily";
  };

  systemd.user.timers.rss2email = {
    wantedBy = [ "timers.target" ];
    timerConfig.OnCalendar = "hourly";
  };

  # systemd.services.hearpress = {
  #   description = "Hearpress server";
  #   wantedBy = [ "multi-user.target" ];
  #   after = [ "postgresql.service" ];

  #   environment = {
  #     PG_CS = "postgresql://jb55@localhost/hearpress";
  #     AWS_ACCESS_KEY_ID = extra.private.aws.access_key;
  #     AWS_SECRET_ACCESS_KEY = extra.private.aws.secret_key;
  #   };

  #   serviceConfig.Type = "simple";
  #   serviceConfig.ExecStart = "${hearpress}/bin/hearpressd";
  # };


  security.setuidPrograms = [ "sendmail" ];

  services.fcgiwrap.enable = true;

  services.nginx.httpConfig = ''
    ${gitCfg}

    server {
      listen 443 ssl;
      server_name coretto.io;
      root /home/jb55/www/coretto.io;
      index index.html;

      ssl_certificate /var/lib/acme/coretto.io/fullchain.pem;
      ssl_certificate_key /var/lib/acme/coretto.io/key.pem;

      location / {
        try_files $uri $uri/ =404;
      }

      location /email {
        gzip off;
        # fcgiwrap is set up to listen on this host:port
        fastcgi_pass                  unix:${config.services.fcgiwrap.socketAddress};
        include                       ${pkgs.nginx}/conf/fastcgi_params;
        fastcgi_param SCRIPT_FILENAME /home/jb55/www/coretto.io/email.py;

        client_max_body_size 512;

        # export all repositories under GIT_PROJECT_ROOT

        fastcgi_param PATH_INFO           $uri;
      }


    }

    server {
      listen 80;
      server_name coretto.io www.coretto.io;

      location /.well-known/acme-challenge {
        root /var/www/challenges;
      }

      location / {
        return 301 https://coretto.io$uri;
      }
    }

    server {
      listen 443 ssl;
      server_name www.coretto.io;
      return 301 https://coretto.io$request_uri;
    }

    server {
      listen 80;
      server_name git.jb55.com;

      location /.well-known/acme-challenge {
        root /var/www/challenges;
      }

      location / {
        return 301 https://git.jb55.com$request_uri;
      }
    }

    server {
      listen       443 ssl;
      server_name  git.jb55.com;

      root /var/git-public/stagit;
      index index.html index.htm;

      ssl_certificate /var/lib/acme/git.jb55.com/fullchain.pem;
      ssl_certificate_key /var/lib/acme/git.jb55.com/key.pem;
    }

    server {
      listen 443 ssl;
      server_name jb55.com;
      root /www/jb55/public;
      index index.html index.htm;

      ssl_certificate /var/lib/acme/jb55.com/fullchain.pem;
      ssl_certificate_key /var/lib/acme/jb55.com/key.pem;

      rewrite ^/pkgs.tar.gz$ https://github.com/jb55/jb55pkgs/archive/master.tar.gz permanent;
      rewrite ^/pkgs/?$ https://github.com/jb55/jb55pkgs/archive/master.tar.gz permanent;

      location / {
        error_page 418 = @jb55activity;

        if ( $http_accept ~ "application/activity\+json" ) { return 418; }

        try_files $uri $uri/ =404;
      }

      location @jb55activity {
         root /;
         default_type application/activity+json;
         try_files ${jb55-activity} =404;
      }

      location = /.well-known/webfinger {
         error_page 418 = @jb55webfinger;
         if ( $query_string = "resource=acct:jb55@jb55.com" ) { return 418; }
         return 404;
      }

      location @jb55webfinger {
         root /;
         default_type application/jrd+json;
         try_files ${webfinger} =404;
      }

      location /paste/ {
        proxy_max_temp_file_size 0;
        client_max_body_size 0;
        proxy_request_buffering off;
        proxy_buffering off;
        proxy_http_version 1.1;
        proxy_pass http://127.0.0.1:${httpipePort}/;

        add_header X-Content-Type-Options nosniff;
      }

      location /cal/ {
        proxy_pass        http://127.0.0.1:5232/;
        proxy_set_header  X-Script-Name /cal;
        proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
      }

      location ^~ /files/calls {
        error_page 405 =200 $uri;
      }
    }

    server {
      listen 80;
      server_name jb55.com www.jb55.com;

      location /.well-known/acme-challenge {
        root /var/www/challenges;
      }

      location / {
        return 301 https://jb55.com$request_uri;
      }
    }
    server {
      listen 443 ssl;
      server_name www.jb55.com;
      return 301 https://jb55.com$request_uri;
    }

  '';

}

A nix-config/machines/charon/dovecot/filters.sieve => nix-config/machines/charon/dovecot/filters.sieve +191 -0
@@ 0,0 1,191 @@

require ["regex", "variables","envelope","mailbox","body","fileinto","imap4flags","reject"];

if allof (header :contains "from" "Microsoft Canada") {
	addflag "\\Seen";
}

if header :contains "X-RSS-Feed" "reddit.com" {
  fileinto "Reddit";
}
elsif header :contains "X-RSS-Feed" "arxiv.org" {
  fileinto "Arxiv";
}
elsif header :contains "X-RSS-Feed" "youtube.com" {
  fileinto "YouTube";
}
elsif header :contains "X-RSS-Feed" "ycombinator.com" {
  fileinto "HackerNews";
}
elsif header :contains "from" "user@rss2email.invalid"  {
  fileinto "RSS";
}

if header :contains "list-id" "lobsters-izs7WbyfQp@lobste.rs" {
  fileinto "Lists.lobsters";
}

if header :contains "from" "nixos1@discoursemail.com" {
  fileinto "Lists.nix";
}

if header :contains "list-id" "vger.kernel.org" {
  fileinto "Lists.lkml";
}

if header :contains "list-id" "emacs-devel.gnu.org" {
  fileinto "Lists.emacs";
}

if header :contains "list-id" "guix-devel.gnu.org" {
  fileinto "Lists.guix";
}

if header :contains "to" "cryptography@metzdowd.com" {
  fileinto "Lists";
}

if header :contains "user-agent" "rss2email" {
  fileinto "RSS";
}

if allof (header :contains "from" "post@tinyportal.net") {
	discard;
}

if allof (header :contains "from" "yahoo.com.hk") {
	discard;
}

# rule:[servers]
if allof (header :contains "from" "noreply@outbound.getsentry.com") {
	fileinto "Alerts";
}

# rule:[Haskell Streaming]
if header :contains "list-id"
  [ "streaming-haskell.googlegroups.com"
  , "cabal-devel.haskell.org"
  , "commercialhaskell.googlegroups.com"
  , "ghc-devs.haskell.org"
  , "haskell-cafe.haskell.org"
  , "haskell.haskell.org"
  , "libraries.haskell.org"
  , "haskell-pipes.googlegroups.com"
  , "shake-build-system.googlegroups.com"
  ]
{
	fileinto "Lists.haskell";
}



# rule:[Alerts]
if allof (header :contains "from" "builds@circleci.com") {
	fileinto "Alerts";
}

# rule:[bitcoin-dev]
if allof (header :contains "list-id" "bitcoin-dev.lists.linuxfoundation.org") {
	fileinto "Lists.bitcoin";
}

# rule:[Monstercat]
if allof (header :contains "to" "bill@monstercat.com") {
	fileinto "Monstercat";
}

# rule:[Updates]
if header :contains "from" [ "no-reply@twitch.tv"
                           , "notify@twitter.com"
                           , "info@meetup.com"
                           , "no-reply@mail.goodreads.com"
                           ]
{
	fileinto "Updates";
}

# rule:[WebVR]
if allof (header :contains "list-id" "web-vr-discuss.mozilla.org") {
	fileinto "Lists.webvr";
}

# rule:[ICN]
if allof (header :contains "list-id" "ccnx.www.ccnx.org") {
	fileinto "Lists.icn";
}

# rule:[ICN]
if allof (header :contains "list-id" "icnrg.irtf.org") {
	fileinto "Lists.icn";
}

# rule:[ICN]
if allof (header :contains "list-id" "ccnx.ccnx.org") {
	fileinto "Lists.icn";
}

# Elm
if header :contains "list-id" [ "elm-discuss", "elm-dev" ] {
	fileinto "Lists.elm";
}

# GitHub
if header :contains "list-id"
     [ "nix.NixOS.github.com"
     , "hydra.NixOS.github.com"
     , "nix-dev.lists.science.uu.nl"
     , "nix-devel.googlegroups.com"
     ]
{
	fileinto "Lists.nix";
}
elsif header :contains "list-id" "spacemacs.syl20bnr.github.com" {
	fileinto "Lists.spacemacs";
}
elsif header :contains "list-id" "streaming.michaelt.github.com" {
	fileinto "Lists.haskell";
}
elsif header :contains "list-id" "nixpkgs.NixOS.github.com" {
	fileinto "Lists.nixpkgs";
}
elsif header :contains "from" "notifications@github.com" {
  # file into github if it doesn't match any other github lists
	fileinto "GitHub";
}

# rule:[Updates]
if header :contains "from" "gab.ai" {
	fileinto "Updates";
}

if header :contains "to" "mention@noreply.github.com" {
	addflag "\\Flagged";
}

if header :contains "list-id" "ndn-interest.lists.cs.ucla.edu" {
	fileinto "Lists.icn";
}

# rule:[ats]
if allof (header :contains "list-id" "ats-lang-users.googlegroups.com") {
	fileinto "Lists.ats";
}

# rule:[shen]
if allof (header :contains "list-id" "qilang.googlegroups.com") {
	fileinto "Lists.shen";
}


# rule:[Craigslist]
if allof (header :contains "from" "reply.craigslist.org") {
	fileinto "Lists.craigslist";
}


# rule:[Alerts]
if allof (header :contains "from" "noreply@md.getsentry.com") {
	fileinto "Alerts";
}


A nix-config/machines/charon/hardware/default.nix => nix-config/machines/charon/hardware/default.nix +24 -0
@@ 0,0 1,24 @@
{ config, lib, pkgs, ... }:
{
  imports =
    [ <nixpkgs/nixos/modules/profiles/qemu-guest.nix>
    ];
  boot.kernelParams = [ "console=ttyS0" ];
  boot.initrd.availableKernelModules = [ "virtio_net" "virtio_pci" "virtio_blk" "virtio_scsi" "9p" "9pnet_virtio" "ata_piix" "virtio_pci" ];
  boot.loader.grub.extraConfig = "serial; terminal_input serial; terminal_output serial";
  boot.loader.grub.device = "/dev/sda";

  fileSystems."/" =
    { device = "/dev/disk/by-uuid/98b261fa-4f9e-4e42-895b-91c17cf145b3";
      fsType = "ext4";
    };

  fileSystems."/boot" =
    { device = "/dev/disk/by-uuid/b3c7ddd8-fa2f-41ea-b77f-b9a1f434b668";
      fsType = "ext4";
    };

  swapDevices =
    [ { device = "/dev/disk/by-uuid/eb7f5cd4-6586-47f4-b4cd-c118e3521f17"; }
    ];
}

A nix-config/machines/charon/networking/default.nix => nix-config/machines/charon/networking/default.nix +22 -0
@@ 0,0 1,22 @@
{ config, lib, pkgs, ... }:
let
  openTCP = dev: port: ''
    ip46tables -A nixos-fw -i ${dev} -p tcp --dport ${toString port} -j nixos-fw-accept
  '';
in
{
  networking.firewall.allowedTCPPorts = [ 22 443 80 70 12566 12788 5222 5269  ];
  networking.firewall.trustedInterfaces = ["zt0"];
  networking.domain = "jb55.com";
  networking.search = [ "jb55.com" ];
  networking.extraHosts = ''
    127.0.0.1 jb55.com
    ::1 jb55.com
  '';

  networking.firewall.extraCommands = ''
    ${openTCP "zt0" 993}
    ${openTCP "zt0" 143}
    ${openTCP "zt0" 587}
  '';
}

A nix-config/machines/charon/nginx/default.nix => nix-config/machines/charon/nginx/default.nix +75 -0
@@ 0,0 1,75 @@
extra:
{ config, lib, pkgs, ... }:
let sites = [./sites/jb55.com
             ./sites/npmrepo.com
             ./sites/wineparty.xyz
             ./sites/hearpress.com
            ];
    logDir = "/var/log/nginx";
in {
  services.logrotate.config = ''
    ${logDir}/*.log {
      daily
      missingok
      rotate 52
      compress
      delaycompress
      notifempty
      # 20MB
      minsize 20971520
      create 640 root adm
      sharedscripts
      postrotate
              ${pkgs.procps}/bin/pkill -USR1 nginx
      endscript
    }
  '';

  services.nginx = {
    enable = true;

    config = ''
      worker_processes 2;

      events {
      	worker_connections 768;
        # multi_accept on;
      }
    '';

    httpConfig = ''
      port_in_redirect off;
      ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
      ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
      ssl_prefer_server_ciphers on;

      # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
      add_header Strict-Transport-Security max-age=15768000;

      sendfile on;
      tcp_nopush on;
      tcp_nodelay on;
      keepalive_timeout 65;
      types_hash_max_size 2048;
      # server_tokens off;
      proxy_buffering off;
      proxy_read_timeout 300s;
      expires off;
      default_type application/octet-stream;

      access_log ${logDir}/access.log;
      error_log ${logDir}/error.log;

      gzip on;
      gzip_disable "msie6";

      server {
        listen      80 default_server;
        server_name "";
        return      444;
      }

      ${lib.concatStringsSep "\n\n" (map builtins.readFile sites)}
    '';
  };
}

A nix-config/machines/charon/nginx/pokemap.nix => nix-config/machines/charon/nginx/pokemap.nix +28 -0
@@ 0,0 1,28 @@
subdomain: port: ''
  server {
    listen 80;
    server_name ${subdomain}.jb55.com;
    root /www/jb55/public/maps;

    location /.well-known {
      try_files $uri $uri/ =404;
    }

    location / {
      return 301 https://${subdomain}.jb55.com$request_uri;
    }
  }

  server {
    listen 443;
    server_name ${subdomain}.jb55.com;

    ssl_certificate /etc/letsencrypt/live/${subdomain}.jb55.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/${subdomain}.jb55.com/privkey.pem;

    location / {
      proxy_pass http://localhost:${port};
    }
  }

''

A nix-config/machines/charon/nginx/sites/coretto.io => nix-config/machines/charon/nginx/sites/coretto.io +0 -0
A nix-config/machines/charon/nginx/sites/hearpress.com => nix-config/machines/charon/nginx/sites/hearpress.com +43 -0
@@ 0,0 1,43 @@
server {
  listen 443 ssl;
  server_name hearpress.com;
  root /www/hearpress.com/public;
  index index.html index.htm;

  ssl_certificate /var/lib/acme/hearpress.com/fullchain.pem;
  ssl_certificate_key /var/lib/acme/hearpress.com/key.pem;

  location @hearpress {
    proxy_pass http://localhost:3000$request_uri;
  }

  location / {
    try_files $uri $uri/ @hearpress;
    error_page 405 @hearpress;
  }

  location /blobs {
    resolver 8.8.8.8;
    proxy_pass https://hearpress.s3.amazonaws.com$request_uri;
  }
}

server {
  listen 80;
  server_name hearpress.com www.hearpress.com;

  location /.well-known/acme-challenge {
    root /var/www/challenges;
  }

  location / {
    return 301 https://hearpress.com$request_uri;
  }

}

server {
  listen 443 ssl;
  server_name www.hearpress.com;
  return 301 https://hearpress.com$request_uri;
}

A nix-config/machines/charon/nginx/sites/jb55.com => nix-config/machines/charon/nginx/sites/jb55.com +0 -0
A nix-config/machines/charon/nginx/sites/npmrepo.com => nix-config/machines/charon/nginx/sites/npmrepo.com +14 -0
@@ 0,0 1,14 @@
server {
  listen 80;
  server_name npmrepo.com www.npmrepo.com;

  location / {
    proxy_pass  http://localhost:9676;
    proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
    proxy_redirect off;
    proxy_buffering off;
    proxy_set_header        Host            $host;
    proxy_set_header        X-Real-IP       $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

A nix-config/machines/charon/nginx/sites/wineparty.xyz => nix-config/machines/charon/nginx/sites/wineparty.xyz +33 -0
@@ 0,0 1,33 @@

server {
  listen 80;
  server_name www.wineparty.xyz;
  root /www/wineparty.xyz/public;
  index index.html index.htm;

  location / {
    try_files $uri $uri/ =404;
  }
}

server {
  listen 80;
  server_name wineparty.xyz;
  return 301 http://www.wineparty.xyz$request_uri;
}

server {
  listen 80;
  server_name pg-zero.wineparty.xyz;
  location / {
    proxy_pass http://localhost:3000;
    proxy_set_header Host      $host;
    proxy_set_header X-Real-IP $remote_addr;
  }
}

server {
  listen 443 ssl;
  server_name wineparty.xyz www.wineparty.xyz;
  return 301 http://www.wineparty.xyz$request_uri;
}

A nix-config/machines/charon/sheetzen/default.nix => nix-config/machines/charon/sheetzen/default.nix +79 -0
@@ 0,0 1,79 @@
extra:
{ config, lib, pkgs, ... }:
let port = "1080";
    sname = "sheetzen.com";
    sheetzen = (import (pkgs.fetchzip {
      url    = "https://jb55.com/s/2d3e137102241acb.tgz";
      sha256 = "00rha983ym6p0bsiz0wsxv750ppgcalvpas6wx790jp9awn5zxlb";
    }) {});
in
{
  services.nginx.httpConfig = lib.mkIf config.services.nginx.enable ''
    server {
      listen 80;
      server_name ${sname} www.${sname};

      location /.well-known/acme-challenge {
        root /var/www/challenges;
      }

      location / {
        return 301 https://${sname}$request_uri;
      }
    }

    server {
      listen 443 ssl;
      server_name ${sname};
      root ${sheetzen}/share/sheetzen/frontend;
      index index.html;

      ssl_certificate /var/lib/acme/${sname}/fullchain.pem;
      ssl_certificate_key /var/lib/acme/${sname}/key.pem;

      location = / {
        try_files index.html /index.html;
      }

      location / {
        try_files $uri $uri/ @proxy;
      }

      location @proxy {
        proxy_pass  http://localhost:${port};
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_redirect off;
        proxy_buffering off;
        proxy_intercept_errors on;
        proxy_set_header        Host            $host;
        proxy_set_header        X-Real-IP       $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
      }

    }
  '';

  systemd.services.sheetzen = {
    enable = true;

    description = "sheetzen";

    wantedBy = [ "multi-user.target" ];
    after    = [ "postgresql.target" ];

    environment = {
      PGHOST = "127.0.0.1";
      PGPORT = "5432";
      PGUSER = "jb55";
      PGPASS = "";
      PGDATABASE = "sheetzen";
      ENV = "Production";
      JWT_KEYFILE = "${sheetzen}/share/sheetzen/credentials/token-key.json";
      CREDENTIAL_PATH = "${sheetzen}/share/sheetzen/credentials/SocialTracker.json";
      PORT = "${port}";
    };

    serviceConfig.ExecStart = "${sheetzen}/bin/sheetzend";
    unitConfig.OnFailure = "systemd-failure-emailer@%n.service";
  };
}

A nix-config/machines/charon/vidstats/default.nix => nix-config/machines/charon/vidstats/default.nix +40 -0
@@ 0,0 1,40 @@
extra:
{ config, lib, pkgs, ... }:
let cfg = extra.private.vidstats;
    videostats = (import (pkgs.fetchgit {
      url    = "http://git.zero.jb55.com/edm-video-stats";
      rev    = "4514bd35d111257f71235fcb121cfbbc6c11eb15";
      sha256 = "0sd26vvffk12a3ax98qlcd0kw7lgnszx9lxyqfya913qkgcyrzmb";
    }) {}).package;
    client_secret = pkgs.fetchurl {
      name = "client_secret.json";
      url = "http://git.zero.jb55.com/repos/?p=edm-video-stats;a=blob_plain;f=client_secret.json";
      sha256 = "0i1kwq8zy1s1w7db3yh6687hyh44m5g5xrlxc425nfnl6hzl9187";
    };
in
{
  systemd.services.vidstats = {
    enable = true;

    description = "vidstats bot";

    wantedBy = [ "multi-user.target" ];
    after    = [ "network-online.target" ];

    environment = {
      GOOGLE_SHEET_ID = cfg.sheet_id;
      GOOGLE_API_KEY  = cfg.api_key;
      VIDEOSTATS_RANGE  = cfg.range;
      TOKEN_DIR = "/home/jb55/.config/edm/videostats/credentials";
      VIDEOSTATS_STATS_RANGE  = cfg.stats_range;
      CLIENT_SECRET = "${client_secret}";
    };

    serviceConfig.Type = "oneshot";
    serviceConfig.ExecStart = "${videostats}/bin/video-stats";

    unitConfig.OnFailure = "systemd-failure-emailer@%n.service";

    startAt = "*-*-* 05:24:00";
  };
}

A nix-config/machines/monad/bitcoin/dca.nix => nix-config/machines/monad/bitcoin/dca.nix +36 -0
@@ 0,0 1,36 @@
{ pkgs, to, bcli, addr }:

pkgs.writeScript "dca"
''
#!${pkgs.bash}/bin/bash

set -e

keybasesock=/run/user/1000/keybase/keybased.sock
wallet=''${BTCDCA_WALLET:-cc}
user=''${BTCDCA_RPCUSER:-rpcuser}
pass=''${BTCDCA_RPCPASS:-rpcpass}
amount=''${BTCDCA_AMOUNT:-$(cat /home/jb55/var/dca-amount)}
pair=''${BTCDCA_PAIR:-XXBTZCAD}
keybaseto=''${BTCDCA_KEYBASETO:-${to}}

price=$(${pkgs.curl}/bin/curl -sL "https://api.kraken.com/0/public/Ticker?pair=$pair" | ${pkgs.jq}/bin/jq -r ".result.$pair.a[0]")
invid=$(dd if=/dev/urandom bs=1 count=4 | ${pkgs.xxd}/bin/xxd -p | ${pkgs.libbitcoin-explorer}/bin/bx base58-encode)

address=${addr}

satsdec=$(${pkgs.bcalc}/bin/bcalc -n --price $price $amount fiat to sats)
sats=''${satsdec%.*}
btc=$(${pkgs.bcalc}/bin/bcalc -n $sats sats to btc)

msg=$(printf "Please send %s BTC to %s for \$%s @ \$%s\ne-transfer password is %s\nplease ACK to confirm. e-transfer will be sent when ACKed.\n" \
       "$btc" "$address" "$amount" "$price" "$invid")

if [ ! -e $keybasesock ]; then
   ${pkgs.systemd}/bin/systemctl --user restart keybase
fi

for user in $keybaseto; do
    ${pkgs.keybase}/bin/keybase chat send "$user" "$msg"
done
''

A nix-config/machines/monad/bitcoin/default.nix => nix-config/machines/monad/bitcoin/default.nix +74 -0
@@ 0,0 1,74 @@
extra:
{ config, lib, pkgs, ... }:

let
  bitcoinDataDir = "/zbig/bitcoin";

  base-bitcoin-conf = extra.private.bitcoin;

  bcli = "${pkgs.bitcoind}/bin/bitcoin-cli --datadir=${bitcoinDataDir} --conf=${base-bitcoin-conf-file} --rpcuser=${extra.private.btc-user} --rpcpassword=${extra.private.btc-pass}";

  bitcoin-conf = ''
    ${base-bitcoin-conf}
    walletnotify=${walletemail} %s %w
  '';

  base-bitcoin-conf-file = pkgs.writeText "bitcoin-base.conf" base-bitcoin-conf;
  bitcoin-conf-file = pkgs.writeText "bitcoin.conf" bitcoin-conf;

  dca = import ./dca.nix {
    inherit pkgs bcli;
    to = "jb55 ${extra.private.btc-supplier}";
    addr = extra.private.btc-supplier-addr;
  };
  walletemail = import ./walletemail.nix { inherit pkgs bcli; };
in
{
  #systemd.user.services.bitcoin-dca =  {
  #  enable = true;
  #  description = "bitcoin dca";

  #  serviceConfig = {
  #    Type = "oneshot";
  #    ExecStart = dca;
  #  };

  #  startAt = "Thu *-*-* 10:00:00";
  #};

  services.bitcoind = {
    mainnet = {
      enable = if extra.is-minimal then false else true;
      dataDir = bitcoinDataDir;
      configFile = bitcoin-conf-file;
      user = "jb55";
      group = "users";
    };
  };

  services.clightning.networks = {
    mainnet = {
      dataDir = "/home/jb55/.lightning-bitcoin";

      config = ''
        bitcoin-rpcuser=rpcuser
        bitcoin-rpcpassword=rpcpass
        bitcoin-rpcconnect=127.0.0.1
        bitcoin-rpcport=8332
        fee-per-satoshi=900
        bind-addr=0.0.0.0:9735
        announce-addr=24.84.152.187:9735
        network=bitcoin
        log-level=debug
        alias=bitsbacker.com
        rgb=ff0000
      '';
    };
  };

  # services.electrs.enable = false;
  # services.electrs.dataDir = "/zbig/electrs";
  # services.electrs.bitcoinDataDir = bitcoinDataDir;


}

A nix-config/machines/monad/bitcoin/walletemail.nix => nix-config/machines/monad/bitcoin/walletemail.nix +57 -0
@@ 0,0 1,57 @@
{ pkgs, bcli }:

pkgs.writeScript "walletemail" ''
#!${pkgs.bash}/bin/bash

set -e

txid="$1"
wallet="$2"

from="Bitcoin Wallet <bitcoind@monad>"
to="William Casarin <jb55@jb55.com>"
subject="Wallet notification"
keys="-r 0x8860420C3C135662EABEADF96342E010C44A6337 -r 0x5B2B1E4F62216BC74362AC61D4FBA2FC4535A2A9 -r 0xE02D3FD4EB4585A63531C1D0E1BFCB90A1FF7A1C"

tx="$(${bcli} -rpcwallet=$wallet gettransaction "$txid" true)"
address="$(${pkgs.jq}/bin/jq -r '.details[0].address' <<<"$tx")"
details="$(${pkgs.jq}/bin/jq -r '.details[] | [.address, .category, .amount, .label] | @csv' <<<"$tx")"
keypath="$(${bcli} -rpcwallet=$wallet getaddressinfo "$address" | ${pkgs.jq}/bin/jq -r .hdkeypath)"

amount="$(${pkgs.jq}/bin/jq -r '.amount' <<<"$tx")"
confs="$(${pkgs.jq}/bin/jq -r '.confirmations' <<<"$tx")"

time="$(date -d @$(${pkgs.jq}/bin/jq -r '.time' <<<"$tx"))"
received="$(date -d @$(${pkgs.jq}/bin/jq -r '.timereceived' <<<"$tx"))"

export GNUPGHOME=/zbig/bitcoin/gpg

msg="$(printf "txid: %s\n\naddress: %s\n\namount: %s\n\nconfirmations: %d\n\nwallet: %s\n\ntime: %s\n\nreceived: %s\n\nkeypath: %s\n\n%s\n\n\n%s" \
              "$txid" "$address" "$amount" "$confs" "$wallet" "$time" "$received" "$keypath" "$details" "$tx" )"

enctx="$(printf "Content-Type: text/plain\n\n%s\n" "$msg" | ${pkgs.gnupg}/bin/gpg --yes --always-trust --encrypt --armor $keys)"

{
cat <<EOF
From: $from
To: $to
Subject: $subject
MIME-Version: 1.0
Content-Type: multipart/encrypted; boundary="=-=-=";
  protocol="application/pgp-encrypted"

--=-=-=
Content-Type: application/pgp-encrypted

Version: 1

--=-=-=
Content-Type: application/octet-stream

$enctx
--=-=-=--
EOF
} | /run/current-system/sw/bin/sendmail --file /zbig/bitcoin/gpg/.msmtprc -oi -t

printf "sent walletnotify email for %s\n" "$txid"
''

A nix-config/machines/monad/config/default.nix => nix-config/machines/monad/config/default.nix +12 -0
@@ 0,0 1,12 @@
pkgs: rec {
  hostId = "d7ee0243"; # needed for zfs
  ztip = "172.24.172.111";
  nix-serve = {
    port = 10845;
    bindAddress = ztip;
  };
  sessionCommands = ''
    ${pkgs.xorg.xrandr}/bin/xrandr -r 144
    ${pkgs.xorg.xgamma}/bin/xgamma -gamma 0.8
  '';
}

A nix-config/machines/monad/contracts/commit/default.nix => nix-config/machines/monad/contracts/commit/default.nix +20 -0
@@ 0,0 1,20 @@
{ config, lib, pkgs, ... }:
{
  # services.kubernetes = {
  #   apiserver.enable = true;
  #   controllerManager.enable = true;
  #   scheduler.enable = true;
  #   addonManager.enable = true;
  #   proxy.enable = true;
  #   flannel.enable = true;
  #   masterAddress = "127.0.0.1";
  # };

  #services.kubernetes.masterAddress = "127.0.0.1";
  #services.kubernetes.roles = [ "master" "node" ];

  # services.openvpn.servers.commit = {
  #   autoStart = true;
  #   config = builtins.readFile ./commit.ovpn;
  # };
}

A nix-config/machines/monad/contracts/plastiq/default.nix => nix-config/machines/monad/contracts/plastiq/default.nix +12 -0
@@ 0,0 1,12 @@
{ config, lib, pkgs, ... }:
let
  user-file = pkgs.writeText "plastiq-user" ''
    will.casarin
  '';
in
{
  services.openvpn.servers.plastiq = {
    autoStart = false;
    config = import ./plastiq.ovpn.nix user-file;
  };
}

A nix-config/machines/monad/default.nix => nix-config/machines/monad/default.nix +320 -0
@@ 0,0 1,320 @@
extra:
{ config, lib, pkgs, ... }:
let util = extra.util;
    nix-serve = extra.machine.nix-serve;
    zenstates = pkgs.fetchFromGitHub {
      owner  = "r4m0n";
      repo   = "ZenStates-Linux";
      rev    = "0bc27f4740e382f2a2896dc1dabfec1d0ac96818";
      sha256 = "1h1h2n50d2cwcyw3zp4lamfvrdjy1gjghffvl3qrp6arfsfa615y";
    };
    email-notify = util.writeBash "email-notify-user" ''
      export HOME=/home/jb55
      export PATH=${lib.makeBinPath (with pkgs; [ eject libnotify muchsync notmuch openssh ])}:$PATH
      (
        flock -x -w 100 200 || exit 1

        muchsync charon

        #DISPLAY=:0 notify-send --category=email "you got mail"

      ) 200>/tmp/email-notify.lock
    '';

in
{
  imports = [
    ./hardware
    # ./contracts/commit
    # ./contracts/plastiq

    #(import ../../misc/dnsmasq-adblock.nix)
    (import ../../misc/msmtp extra)
    (import ./networking extra)
    (import ../../misc/imap-notifier extra)
  ] ++ (if !extra.is-minimal then [ (import ./bitcoin extra) ] else []);

  hardware.steam-hardware.enable = true;

  services.prometheus.enable = false;
  # services.prometheus.dataDir = "/zbig/data/prometheus";
  services.grafana.enable = false;
  services.grafana.port = 3005;
  services.grafana.provision.datasources = [
    { name = "bitcoin";
      type = "prometheus";
      access = "direct";
      isDefault = true;
    }
  ];

  # services.guix.enable = true;
  services.synergy.server.enable = if extra.is-minimal then false else true;
  services.synergy.server.autoStart = true;
  services.synergy.server.screenName = "desktop";
  services.synergy.server.configFile = pkgs.writeText "synergy-cfg" ''
    section: screens
      desktop:
      mac:
    end
    section: aliases
        desktop:
          192.168.86.26
        mac:
          192.168.86.232
    end
    section: links
      desktop:
          left = mac
      mac:
          right = desktop
    end
    section: options
      keystroke(alt+control+h) = switchInDirection(left)
      keystroke(alt+control+l) = switchInDirection(right)
    end
  '';

  services.bitlbee.enable = if extra.is-minimal then false else true;
  services.bitlbee.libpurple_plugins = with pkgs; [
    pidgin-skypeweb
    purple-facebook
    purple-hangouts
    telegram-purple
    purple-matrix
  ];

  # services.thelounge.enable = true;
  # services.thelounge.theme = "thelounge-theme-mininapse";
  # services.thelounge.port = 9002;

  services.dnscrypt-proxy2.enable = false;
  services.dnscrypt-proxy2.settings = {

    listen_addresses = [ "127.0.0.1:43" ];
    server_names = ["cs-ca2" "ev-to"];
    fallback_resolver = "1.1.1.1:53";
    sources = {
      public-resolvers = {
        urls = ["https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v2/public-resolvers.md"
                "https://download.dnscrypt.info/resolvers-list/v2/public-resolvers.md"
               ];
        cache_file = "public-resolvers.md";
        minisign_key = "RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3";
        refresh_delay = 71;
      };

      relays = {
        urls = ["https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v2/relays.md"];
        cache_file = "relays.md";
        minisign_key = "RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3";
        refresh_delay = 71;
      };
    };
    anonymized_dns.routes = [
      { server_name="cs-ca2"; via=["anon-ev-va"]; }
      { server_name="ev-to"; via=["anon-cs-ca2"]; }
    ];
  };

  services.dnsmasq.enable = true;
  services.dnsmasq.resolveLocalQueries = true;
  #services.dnsmasq.servers = ["127.0.0.1#43"];
  # services.dnsmasq.servers = ["127.0.0.1#43" "1.1.1.1" "8.8.8.8"];
  services.dnsmasq.servers = ["1.1.1.1" "8.8.8.8"];
  services.dnsmasq.extraConfig = ''
    cache-size=10000
    addn-hosts=/var/hosts
    conf-file=/var/dnsmasq-hosts
    conf-file=/var/distracting-hosts
  '';


  services.bitlbee.plugins = with pkgs; [
    bitlbee-discord
    bitlbee-mastodon
  ];

  # shitcoin vendor
  services.keybase.enable = false;

  systemd.services.block-distracting-hosts = {
    description = "Block Distracting Hosts";

    path = with pkgs; [ systemd procps ];

    serviceConfig.ExecStart = util.writeBash "block-distracting-hosts" ''
      set -e
      cp /var/undistracting-hosts /var/distracting-hosts

      # crude way to clear the cache...
      systemctl restart dnsmasq
      pkill qutebrowser
    '';

    startAt = "Mon..Fri *-*-* 09:00:00";
  };

  systemd.user.services.stop-spotify-bedtime = {
    enable      = if extra.is-minimal then false else true;
    description = "Stop spotify when Elliott goes to bed";
    wantedBy    = [ "graphical-session.target" ];
    after       = [ "graphical-session.target" ];
    serviceConfig.ExecStart = "${pkgs.dbus}/bin/dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Stop";

    startAt = "*-*-* 19:30:00";
  };

  systemd.services.unblock-distracting-hosts = {
    enable = if extra.is-minimal then false else true;

    description = "Unblock Distracting Hosts";

    path = with pkgs; [ systemd ];

    serviceConfig.ExecStart = util.writeBash "unblock-distracting-hosts" ''
      set -e
      echo "" > /var/distracting-hosts
      systemctl restart dnsmasq
    '';

    startAt = "Mon..Fri *-*-* 17:00:00";
  };

  virtualisation.docker.enable = if extra.is-minimal then false else true;
  virtualisation.virtualbox.host.enable = false;#if extra.is-minimal then false else true;
  virtualisation.virtualbox.host.enableHardening = false;
  #virtualization.virtualbox.host.enableExtensionPack = true;
  users.extraUsers.jb55.extraGroups = [ "vboxusers" "bitcoin" ];

  services.xserver.videoDrivers = [ ];

  users.extraGroups.tor.members = [ "jb55" "nginx" ];
  users.extraGroups.bitcoin.members = [ "jb55" ];
  users.extraGroups.nginx.members = [ "jb55" ];
  users.extraGroups.transmission.members = [ "nginx" "jb55" ];

  programs.mosh.enable = false;
  programs.adb.enable = true;

  documentation.nixos.enable = false;

  # services.trezord.enable = if extra.is-minimal then false else true;
  services.redis.enable = if extra.is-minimal then false else true;

  services.zeronet.enable = false;
  #services.zeronet.trackers = ''
  #  http://tracker.nyap2p.com:8080/announce
  #  http://tracker3.itzmx.com:6961/announce
  #  http://tracker1.itzmx.com:8080/announce
  #  https://trakx.herokuapp.com:443/announce
  #  udp://ultra.zt.ua:6969/announce
  #'';

  services.mongodb.enable = if extra.is-minimal then false else false;

  services.tor.enable = if extra.is-minimal then false else true;
  #services.tor.controlPort = 9051;
  services.tor.client.enable = true;
  services.tor.extraConfig = extra.private.tor.extraConfig;

  services.fcgiwrap.enable = if extra.is-minimal then false else true;

  services.nix-serve.enable = false;
  services.nix-serve.bindAddress = nix-serve.bindAddress;
  services.nix-serve.port = nix-serve.port;

  services.nginx.enable = if extra.is-minimal then false else true;
  services.nginx.httpConfig = ''
      server {
        listen      80 default_server;
        server_name _;
        root /www/public;
        index index.html index.htm;
        location / {
          try_files $uri $uri/ =404;
        }
      }

    '' + (if config.services.nix-serve.enable then ''
      server {
        listen ${nix-serve.bindAddress}:80;
        server_name cache.monad.jb55.com;

        location / {
          proxy_pass  http://${nix-serve.bindAddress}:${toString nix-serve.port};
          proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
          proxy_redirect off;
          proxy_buffering off;
          proxy_set_header        Host            $host;
          proxy_set_header        X-Real-IP       $remote_addr;
          proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        }
      }
    '' else "") + (if config.services.tor.enable then extra.private.tor.nginx else "");

  # services.footswitch = {
  #   enable = false;
  #   enable-led = true;
  #   led = "input5::numlock";
  # };

  systemd.services.disable-c6 = {
    description = "Ryzen Disable C6 State";

    wantedBy = [ "basic.target" ];
    after = [ "sysinit.target" "local-fs.target" ];

    serviceConfig.Type = "oneshot";
    serviceConfig.ExecStart = util.writeBash "disable-c6-state" ''
      ${pkgs.kmod}/bin/modprobe msr
      ${pkgs.python2}/bin/python ${zenstates}/zenstates.py --c6-disable --list
    '';
  };

  services.mysql.enable = false;
  services.mysql.package = pkgs.mariadb;

  # services.postgresql = {
  #   dataDir = "/var/db/postgresql/100/";
  #   enable = true;
  #   package = pkgs.postgresql_10;
  #   # extraPlugins = with pkgs; [ pgmp ];
  #   authentication = pkgs.lib.mkForce ''
  #     # type db  user address            method
  #     local  all all                     trust
  #     host   all all  127.0.0.1/32       trust
  #     host   all all  192.168.86.0/24    trust
  #   '';
  #   extraConfig = ''
  #     listen_addresses = '0.0.0.0'
  #   '';
  # };

  # services.postgresql = {
  #   dataDir = "/var/db/postgresql/96/";
  #   enable = true;
  #   package = pkgs.postgresql96;
  #   # extraPlugins = with pkgs; [ pgmp ];
  #   authentication = pkgs.lib.mkForce ''
  #     # type db  user address            method
  #     local  all all                     trust
  #     host   all all  127.0.0.1/32       trust
  #     host   all all  192.168.86.0/24    trust
  #   '';
  #   extraConfig = ''
  #     listen_addresses = '0.0.0.0'
  #   '';
  # };

  # security.pam.u2f = {
  #   enable = true;
  #   interactive = true;
  #   cue = true;
  #   control = "sufficient";
  #   authfile = "${pkgs.writeText "pam-u2f-config" ''
  #     jb55:vMXUgYb1ytYmOVgqFDwVOxJmvVI9F3gdSJVbvsi1A1VA-3mftTUhgARo4Kmm_8SAH6IJJ8p3LSXPSbtTSXMIpQ,04d8c1542a7391ee83112a577db968b84351f0090a9abe7c75bedcd94777cf15727c68ce4ac8858ff2812ded3c86d978efc5893b25cf906032632019fe792d3ec4
  #   ''}";
  # };

}

A nix-config/machines/monad/hardware/default.nix => nix-config/machines/monad/hardware/default.nix +64 -0
@@ 0,0 1,64 @@
{ config, lib, pkgs, ... }:
{
  # fileSystems."/" =
  #   { device = "/dev/disk/by-uuid/62518649-0872-49e2-a269-34975e314c6a";
  #     fsType = "ext4";
  #   };

  # fileSystems."/" =
  #   { device = "/dev/nvme0n1p1";
  #     fsType = "zfs";
  #nixos-generate-config --root /mnt   };

  boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usbhid" "sd_mod" ];
  boot.kernelParams = [ "amdgpu.gpu_recovery=1" ];
  boot.kernelModules = [ "kvm-amd" ];
  boot.loader.grub.copyKernels = true;
  boot.extraModulePackages = [ ];

  fileSystems."/" =
    { device = "znix/root/nixos";
      fsType = "zfs";
    };

  fileSystems."/home" =
    { device = "znix/home";
      fsType = "zfs";
    };

  fileSystems."/zbig" =
    { device = "zbig";
      fsType = "zfs";
    };

  #fileSystems."/vr" =
  # { device = "/dev/disk/by-uuid/E234A89834A87169";
  #   fsType = "ntfs";
  # };

  #fileSystems."/sand" =
  #  { device = "/dev/disk/by-uuid/2ee709b8-7e83-470f-91bc-d0b0ba59b945";
  #    fsType = "ext4";
  #  };

  # fileSystems."/home/jb55/shares/will-vm/projects" =
  #   { device = "//192.168.86.199/Users/jb55/projects";
  #     fsType = "cifs";
  #     options = ["username=jb55" "password=notsecurepw" "gid=100" "uid=1000"];
  #   };

  #fileSystems."/home/jb55/.local/share/Steam/steamapps" =
  #  { device = "/sand/data/SteamAppsLinux";
  #    fsType = "none";
  #    options = ["bind"];
  #  };

  # swapDevices =
  #   [ { device = "/dev/disk/by-uuid/d4e4ae51-9179-439d-925b-8df42dd1bfc5"; }
  #   ];

  hardware.enableAllFirmware = true;

  boot.loader.grub.devices = [ "/dev/nvme0n1" ];
  boot.supportedFilesystems = ["zfs"];
}

A nix-config/machines/monad/networking/default.nix => nix-config/machines/monad/networking/default.nix +175 -0
@@ 0,0 1,175 @@
extra:
{ config, lib, pkgs, ... }:
let
  chromecastIP = "192.168.86.190";
  iptables = "iptables -A nixos-fw";
  ipr = "${pkgs.iproute}/bin/ip";
  writeBash = extra.util.writeBash;
  transmission-dir = "/zbig/torrents";
  download-dir = "${transmission-dir}/Downloads";
  openCloseTCP = op: dev: port: ''
    ip46tables -${op} nixos-fw -i ${dev} -p tcp --dport ${toString port} -j nixos-fw-accept ${if op == "D" then "|| true" else ""}
  '';
  openTCP = dev: port: openCloseTCP "A" dev port;
  closeTCP = dev: port: openCloseTCP "D" dev port;

  ports = {
    synergy = 24800;
    lightning = 9735;
    lightningt = 9736;
    dns = 53;
    wireguard = 51820;
  };
in
{
  networking.hostId = extra.machine.hostId;

  #networking.firewall.trustedInterfaces = ["wg0"];
  networking.firewall.allowedTCPPorts = with ports; [ lightning lightningt synergy ];
  networking.firewall.allowedUDPPorts = [ ports.dns ports.wireguard ];

  networking.nat.enable = true;
  networking.nat.externalInterface = "eth0";
  networking.nat.internalInterfaces = [ "wg0" ];

  networking.wireguard.interfaces = {
    # "wg0" is the network interface name. You can name the interface arbitrarily.
    wg0 = {
      # Determines the IP address and subnet of the server's end of the tunnel interface.
      ips = [ "10.100.0.1/24" ];

      # The port that Wireguard listens to. Must be accessible by the client.
      listenPort = ports.wireguard;

      postSetup = ''
        ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 10.100.0.0/24 -o enp30s0 -j MASQUERADE
      '';

      # This undoes the above command
      postShutdown = ''
        ${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s 10.100.0.0/24 -o enp30s0 -j MASQUERADE
      '';


      # Path to the private key file.
      #
      # Note: The private key can also be included inline via the privateKey option,
      # but this makes the private key world-readable; thus, using privateKeyFile is
      # recommended.
      privateKeyFile = "/home/jb55/.wg/private";

      peers = [
        { publicKey = "wcoun9+1GX4awQF2Yd0WbsQ6RKHE9SsOsYv3qR7mbB0="; # quiver
          allowedIPs = [ "10.100.0.2/32" ];
        }
        { publicKey = "vIh3IQgP92OhHaC9XBiJVDLlrs3GVcR6hlXaapjTiA0="; # phone
          allowedIPs = [ "10.100.0.3/32" ];
        }
        { publicKey = "Dp8Df75X8Kh9gd33e+CWyyhOvT4mT0X9ToPwBUEBU1k="; # macos
          allowedIPs = [ "10.100.0.4/32" ];
        }
      ];
    };
  };


  services.transmission = {
    enable = true;
    home = transmission-dir;
    settings = {
      incomplete-dir-enable = true;
      rpc-whitelist = "127.0.0.1";
    };

    port = 14325;
  };

  services.plex = {
    enable = false;
    group = "transmission";
    openFirewall = true;
  };

  services.xinetd.enable = true;
  services.xinetd.services =
  [
    { name = "gopher";
      port = 70;
      server = "${pkgs.gophernicus}/bin/in.gophernicus";
      serverArgs = "-nf -r /var/gopher";
      extraConfig = ''
        disable = no
        env = PATH=${pkgs.coreutils}/bin:${pkgs.curl}/bin
        passenv = PATH
      '';
    }
  ];

  services.nginx.httpConfig = lib.mkIf config.services.transmission.enable ''
    server {
      listen 80;
      listen ${extra.machine.ztip}:80;
      listen 192.168.86.26;

      # server names for this server.
      # any requests that come in that match any these names will use the proxy.
      server_name plex.jb55.com plez.jb55.com media.home plex.home;

      # this is where everything cool happens (you probably don't need to change anything here):
      location / {
        # if a request to / comes in, 301 redirect to the main plex page.
        # but only if it doesn't contain the X-Plex-Device-Name header
        # this fixes a bug where you get permission issues when accessing the web dashboard

        if ($http_x_plex_device_name = \'\') {
          rewrite ^/$ http://$http_host/web/index.html;
        }

        # set some headers and proxy stuff.
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_redirect off;

        # include Host header
        proxy_set_header Host $host;

        # proxy request to plex server
        proxy_pass http://127.0.0.1:32400;
      }
    }

    server {
      listen 80;
      listen ${extra.machine.ztip}:80;
      listen 192.168.86.26;
      server_name torrents.jb55.com torrentz.jb55.com torrents.home torrent.home;

      location = /download {
        return 301 " /download/";
      }

      location /download/ {
        alias ${download-dir}/;
        autoindex on;
      }

      location / {
        proxy_read_timeout 300;
        proxy_pass_header  X-Transmission-Session-Id;
        proxy_set_header   X-Forwarded-Host   $host;
        proxy_set_header   X-Forwarded-Server $host;
        proxy_set_header   X-Forwarded-For    $proxy_add_x_forwarded_for;
        proxy_pass         http://127.0.0.1:${toString config.services.transmission.port}/transmission/web/;
      }

      location /rpc {
        proxy_pass         http://127.0.0.1:${toString config.services.transmission.port}/transmission/rpc;
      }

      location /upload {
        proxy_pass         http://127.0.0.1:${toString config.services.transmission.port}/transmission/upload;
      }
    }
  '';

}

A nix-config/machines/monad/nginx/default.nix => nix-config/machines/monad/nginx/default.nix +99 -0
@@ 0,0 1,99 @@
extra:
{ config, lib, pkgs, ... }:
let sites = [ ];
    logDir = "/var/log/nginx";
    gitExtra = {
      ztip = "172.24.172.226";
      git = {
        projectroot = "/var/git";
      };
      host = "git.zero.jb55.com";
    };
    razornetExtra = {
      ztip = "172.29.172.226";
      git = {
        projectroot = "/var/razorgit";
      };
      host = "git.razor.jb55.com";
    };
    gitCfg = extra.git-server { inherit config pkgs; extra = extra // gitExtra; };
    razornetGit = extra.git-server { inherit config pkgs; extra = extra // razornetExtra; };
in {
  services.logrotate.config = ''
    ${logDir}/*.log {
      daily
      missingok
      rotate 52
      compress
      delaycompress
      notifempty
      # 20MB
      minsize 20971520
      create 640 root adm
      sharedscripts
      postrotate
              ${pkgs.procps}/bin/pkill -USR1 nginx
      endscript
    }
  '';

  services.nginx = {
    enable = true;

    package = pkgs.nginx.override {
      modules = with pkgs.nginxModules; [ lua ];
    };

    user = "jb55";

    config = ''
      worker_processes 2;

      events {
      	worker_connections 768;
        # multi_accept on;
      }
    '';

    httpConfig = ''
      port_in_redirect off;
      ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
      ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
      ssl_prefer_server_ciphers on;

      # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
      add_header Strict-Transport-Security max-age=15768000;

      sendfile on;
      tcp_nopush on;
      tcp_nodelay on;
      keepalive_timeout 65;
      types_hash_max_size 2048;
      # server_tokens off;
      proxy_buffering off;
      proxy_read_timeout 300s;
      expires off;
      default_type application/octet-stream;

      access_log ${logDir}/access.log;
      error_log ${logDir}/error.log;

      gzip on;
      gzip_disable "msie6";

      server {
        listen      80 default_server;
        server_name _;
        root /www/public;
        index index.html index.htm;
        location / {
          try_files $uri $uri/ =404;
        }
      }

      ${gitCfg}

      ${razornetGit}
    '';
  };
}

A nix-config/machines/quiver/config/default.nix => nix-config/machines/quiver/config/default.nix +4 -0
@@ 0,0 1,4 @@

pkgs: {
  sessionCommands = "";
}

A nix-config/machines/quiver/default.nix => nix-config/machines/quiver/default.nix +187 -0
@@ 0,0 1,187 @@
extra:
{ config, lib, pkgs, ... }:
{
  imports = [
    ./hardware-configuration.nix
    (import ../../misc/msmtp extra)
    (import ./networking extra)
    (import ../../misc/imap-notifier extra)
    (import ./timers extra)
  ];

  environment.systemPackages = with pkgs; [ acpi xorg.xbacklight ];

  virtualisation.docker.enable = true;
  virtualisation.virtualbox.host.enable = false;
  users.extraGroups.vboxusers.members = [ "jb55" ];

  documentation.nixos.enable = false;

  boot.extraModprobeConfig = ''
    options thinkpad_acpi enabled=0
  '';


  # telepathy is a garbage fire
  services.telepathy.enable = false;
  services.zerotierone.enable = false;
  services.mongodb.enable = true;
  services.redis.enable = true;
  services.keybase.enable = true;
  services.mysql.enable = true;
  services.mysql.package = pkgs.mariadb;

  services.xinetd.enable = true;
  services.xinetd.services = [
    { name = "gopher";
      port = 70;
      server = "/var/gopher/in.gophernicus";
      serverArgs = "-nf -r /var/gopher";
      extraConfig = ''
        disable = no
      '';
    }
  ];

  services.xserver.libinput.enable = true;
  services.xserver.config = ''
    Section "InputClass"
      Identifier     "Enable libinput for TrackPoint"
      MatchProduct   "TPPS/2 Elan TrackPoint"
      Driver         "libinput"
      Option         "AccelSpeed" "1"
      Option         "AccelProfile" "flat"
    EndSection

    Section "InputClass"
      Identifier       "Disable TouchPad"
      MatchIsTouchpad  "on"
      Driver           "libinput"
      Option           "Ignore" "true"
    EndSection
  '';


  services.plex = {
    enable = false;
    openFirewall = true;
  };

  services.nginx.enable = true;
  services.nginx.group = "www-data";

  services.nginx.httpConfig = ''
    server {
      listen 80;

      root /var/www/share;

      location / {
        autoindex on;
      }
    }
  '';

  systemd.user.services.clightning-rpc-tunnel = {
    description = "clightning mainnet rpc tunnel";
    wantedBy = [ "default.target" ];
    after    = [ "default.target" ];

    serviceConfig.ExecStart = extra.util.writeBash "lightning-tunnel" ''
      socket=/home/jb55/.lightning-bitcoin-rpc
      rm -f $socket
      ${pkgs.socat}/bin/socat -d -d UNIX-LISTEN:$socket,reuseaddr,fork TCP:10.147.20.220:7878
    '';
  };

  systemd.user.services.clightning-testnet-rpc-tunnel = {
    description = "clightning testnet rpc tunnel";
    wantedBy = [ "default.target" ];
    after    = [ "default.target" ];

    serviceConfig.ExecStart = extra.util.writeBash "lightning-testnet-tunnel" ''
      socket=/home/jb55/.lightning-testnet-rpc
      rm -f $socket
      ${pkgs.socat}/bin/socat -d -d UNIX-LISTEN:$socket,reuseaddr,fork TCP:10.147.20.220:7879
    '';
  };

  systemd.services.blink-led-battery-low = {
    description = "blink power led when battery is low";
    wantedBy = [ "default.target" ];
    after    = [ "default.target" ];

    path = with pkgs; [ gnused acpi ];

    serviceConfig.ExecStart = extra.util.writeBash "battery-power" ''
      set -e

      LED=/sys/class/leds/tpacpi::power
      LED2=/sys/class/leds/tpacpi::kbd_backlight

      # led will start blinking below this battery %
      limit=10

      state=""

      while true
      do
          percent=$(acpi -b | sed -E -n 's/.* ([0-9]+)%.*/\1/p')

          if [ $percent -lt $limit ] && [ "$state" != "heartbeat" ]
          then
              printf "battery %d%% < %d%%, setting heartbeat trigger\n" "$percent" "$limit" >&2
              echo heartbeat > "$LED"/trigger
              echo heartbeat > "$LED2"/trigger
              state="heartbeat"
          elif [ $percent -ge $limit ] && [ "$state" = "heartbeat" ]
          then
              printf "battery %d%% >= %d%%, resetting led trigger\n" "$percent" "$limit" >&2
              echo none > "$LED"/trigger
              echo none > "$LED2"/trigger
              cat "$LED"/max_brightness > "$LED"/brightness
              state=""
          fi
          sleep 10
      done
    '';
  };

  services.hydra.enable = false;
  services.hydra.dbi = "dbi:Pg:dbname=hydra;host=localhost;user=postgres;";
  services.hydra.hydraURL = "localhost";
  services.hydra.notificationSender = "hydra@quiver";
  services.hydra.buildMachinesFiles = [];
  services.hydra.useSubstitutes = true;

  users.extraGroups.hydra.members = [ "jb55" ];
  users.extraGroups.www-data.members = [ "jb55" ];

  # https://github.com/nmikhailov/Validity90  # driver not done yet
  services.fprintd.enable = false;

  services.tor.enable = false;
  services.tor.controlPort = 9051;

  services.autorandr.enable = true;
  services.acpid.enable = false;
  powerManagement.enable = false;

  networking.wireless.enable = true;

  services.postgresql = {
    dataDir = "/var/db/postgresql/10/";
    enable = true;
    package = pkgs.postgresql_10;
    # extraPlugins = with pkgs; [ pgmp ];
    authentication = pkgs.lib.mkForce ''
      # type db  user address            method
      local  all all                     trust
      host   all all  localhost          trust
    '';
    # extraConfig = ''
    #   listen_addresses = '172.24.172.226,127.0.0.1'
    # '';
  };

}

A nix-config/machines/quiver/hardware-configuration.nix => nix-config/machines/quiver/hardware-configuration.nix +46 -0
@@ 0,0 1,46 @@
# Do not modify this file!  It was generated by ‘nixos-generate-config’
# and may be overwritten by future invocations.  Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, ... }:

{
  imports =
    [ <nixpkgs/nixos/modules/installer/scan/not-detected.nix>
    ];

  boot.initrd.availableKernelModules = [ "xhci_pci" "nvme" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
  boot.kernelModules = [ "kvm-intel" "ledtrig_heartbeat" ];
  #boot.kernelParams = [ "intel_pstate=nohwp" ];
  boot.extraModulePackages = [ ];

  boot.loader.grub.enable = true;
  boot.loader.grub.device = "nodev"; 
  boot.loader.grub.efiSupport = true; 
  boot.loader.efi.canTouchEfiVariables = true;

  boot.initrd.luks.devices = [
   { name = "root";
     device = "/dev/disk/by-uuid/ddb70a55-f123-461d-a4c1-a42a393b61fa";
     preLVM = false;
     allowDiscards = true;
   }
  ];

  fileSystems."/" =
    { device = "/dev/disk/by-uuid/a6670d72-9bdf-4c62-b397-d35c8c1356ef";
      fsType = "ext4";
      options = [ "noatime" "nodiratime" "discard" ];
    };

  fileSystems."/boot" =
    { device = "/dev/disk/by-uuid/4F4E-282E";
      fsType = "vfat";
    };

  swapDevices =
    [ { device = "/dev/disk/by-uuid/699fec01-9969-4279-bc7c-8f64252e40b0"; }
    ];

  nix.maxJobs = lib.mkDefault 4;
  powerManagement.cpuFreqGovernor = "powersave";
}

A nix-config/machines/quiver/networking/default.nix => nix-config/machines/quiver/networking/default.nix +111 -0
@@ 0,0 1,111 @@
extra:
{ config, lib, pkgs, ... }:
let
  chromecastIPs = [ "192.168.86.190" ];
  iptables = "iptables -A nixos-fw";
  openChromecast = ip: ''
    ${iptables} -p udp -s ${ip} -j nixos-fw-accept
    ${iptables} -p tcp -s ${ip} -j nixos-fw-accept
  '';
  ipr = "${pkgs.iproute}/bin/ip";
  writeBash = extra.util.writeBash;
  openTCP = dev: port: ''
    ip46tables -A nixos-fw -i ${dev} -p tcp --dport ${toString port} -j nixos-fw-accept
  '';

in
{
  networking.extraHosts = ''
    10.0.9.1         secure.datavalet.io.
    172.24.242.111   securitycam.home.
    24.244.54.234    wifisignon.shaw.ca.
  '';

  networking.wireguard.interfaces = {
    # "wg0" is the network interface name. You can name the interface arbitrarily.
    wg0 = {
      # Determines the IP address and subnet of the client's end of the tunnel interface.
      ips = [ "10.100.0.2/28" ];

      # Path to the private key file.
      #
      # Note: The private key can also be included inline via the privateKey option,
      # but this makes the private key world-readable; thus, using privateKeyFile is
      # recommended.
      privateKeyFile = "/home/jb55/.wg/private";

      peers = [
        # For a client configuration, one peer entry for the server will suffice.
        {
          # Public key of the server (not a file path).
          publicKey = "TbGgpOqD6teLon0ksZKS8zvvjHtkOGKNWPpHZxhVFWA=";

          allowedIPs = [ "10.100.0.1/32" ];

          # Set this to the server IP and port.
          endpoint = "24.84.152.187:53";

          # Send keepalives every 25 seconds. Important to keep NAT tables alive.
          persistentKeepalive = 25;
        }
        {
          publicKey = "vIh3IQgP92OhHaC9XBiJVDLlrs3GVcR6hlXaapjTiA0=";

          allowedIPs = [ "10.100.0.3/32" ];

          # Send keepalives every 25 seconds. Important to keep NAT tables alive.
          persistentKeepalive = 25;
        }
        {
          publicKey = "Dp8Df75X8Kh9gd33e+CWyyhOvT4mT0X9ToPwBUEBU1k="; # macos
          allowedIPs = [ "10.100.0.4/32" ];

          # Send keepalives every 25 seconds. Important to keep NAT tables alive.
          persistentKeepalive = 25;
        }
      ];
    };
  };


  networking.wireless.userControlled.enable = true;

  networking.firewall.enable = true;
  networking.firewall.extraCommands = ''
    ${lib.concatStringsSep "\n\n" (map openChromecast chromecastIPs)}

    # home network nginx
    iptables -A nixos-fw -p tcp -s 192.168.86.0/24 -d 192.168.86.0/24 --dport 80 -j nixos-fw-accept

    # mark tor-related packets
    iptables -t mangle -A OUTPUT -m cgroup --cgroup 12 -j MARK --set-mark 12

    # all tor traffic should never try to route outside our wireguard tunnel to our tor node
    iptables -t nat -A POSTROUTING -m cgroup --cgroup 12 -o wg0 -j MASQUERADE

    # create separate routing table
    ${ipr} rule add fwmark 12 table 12

    # add fallback route that blocks traffic, should the VPN go down
    ${ipr} route add blackhole default metric 2 table 12
  '';

  networking.firewall.extraStopCommands = ''
    iptables -D nixos-fw -p tcp -s 192.168.86.0/24 -d 192.168.86.0/24 --dport 80 -j nixos-fw-accept || true

    # mark tor-related packets
    iptables -t mangle -D OUTPUT -m cgroup --cgroup 12 -j MARK --set-mark 12 || true

    # all tor traffic should never try to route outside our wireguard tunnel to our tor node
    iptables -t nat -D POSTROUTING -m cgroup --cgroup 12 -o wg0 -j MASQUERADE || true

    # create separate routing table
    ${ipr} rule del fwmark 12 table 12

    # add fallback route that blocks traffic, should the VPN go down
    ${ipr} route del blackhole default metric 2 table 12
  '';


  #networking.firewall.allowedTCPPorts = [ 8333 ];
}

A nix-config/machines/quiver/timers/archer-cookies/default.nix => nix-config/machines/quiver/timers/archer-cookies/default.nix +57 -0
@@ 0,0 1,57 @@
extra:
{ config, lib, pkgs, ... }:
let
  util = extra.util;
in
{

  systemd.user.services.cookie-bot = {
    description = "copy cookies to archer";

    wantedBy = [ "default.target" ];
    after    = [ "default.target" ];

    path = with pkgs; [ openssh rsync ];

    serviceConfig.ExecStart = util.writeBash "cp-cookies" ''
      export HOME=/home/jb55
      PTH=".config/chromium/Default/Cookies"
      rsync -av $HOME/$PTH archer:$PTH
    '';
    unitConfig.OnFailure = "notify-failed-user@%n.service";

    startAt = [
      "*-*-20 09:24:00"       # youtube bot is run on the 20th at 10:24:00
      "Tue *-*-1..7 15:00:00" # cookies for itunes bot on the first tuesday
    ];
  };

  systemd.user.services.cookie-bot-reminder = {
    description = "reminder to login";

    wantedBy = [ "default.target" ];
    after    = [ "default.target" ];

    serviceConfig.ExecStart = util.writeBash "cookie-reminder" ''
      /run/wrappers/bin/sendmail -f bill@monstercat.com <<EOF
      To: bill@monstercat.com
      Cc: jb55@jb55.com
      From: THE COOKIE MONSTER <cookiemonster@quiver>
      Subject: Reminder to log into YouTube cms

      I'll be doing an rsync from quiver tomorrow at 10:24

      Here's a link for your convenience:

        https://cms.youtube.com

      Cheers,
        THE COOKIE MONSTER
      EOF
    '';
    unitConfig.OnFailure = "notify-failed-user@%n.service";

    startAt = "*-*-19 10:24:00";
  };

}

A nix-config/machines/quiver/timers/default.nix => nix-config/machines/quiver/timers/default.nix +6 -0
@@ 0,0 1,6 @@
extra:
{ config, lib, pkgs, ... }:
{
  imports = [
  ];
}

A nix-config/misc/dnsmasq-adblock.nix => nix-config/misc/dnsmasq-adblock.nix +20 -0
@@ 0,0 1,20 @@
{ config, lib, pkgs, ... }:
let
  adblock-hosts = pkgs.fetchurl {
                    url    = "https://jb55.com/s/ad-sources.txt";
                    sha256 = "d9e6ae17ecc41eb7021c0552548a1c8da97efbb61e3a750fb023674d01d81134";
                  };
  dnsmasq-adblock = pkgs.fetchurl {
                      url = "https://jb55.com/s/dnsmasq-ad-sources.txt";
                      sha256 = "3b34e565fb240c4ac1d261cb223bdc2d992fa755b5f6e981144e5b18f96f260d";
                    };
in
{
  services.dnsmasq.enable = true;
  services.dnsmasq.resolveLocalQueries = false;
  services.dnsmasq.servers = ["1.1.1.1" "8.8.8.8"];
  services.dnsmasq.extraConfig = ''
    addn-hosts=${adblock-hosts}
    conf-file=${dnsmasq-adblock}
  '';
}

A nix-config/misc/git-server.nix => nix-config/misc/git-server.nix +60 -0
@@ 0,0 1,60 @@
{ extra, config, pkgs }:
let gitwebConf = pkgs.writeText "gitweb.conf" ''
      # path to git projects (<project>.git)
      $projectroot = "${extra.git.projectroot}";
    '';
    gitweb-wrapper = pkgs.writeScript "gitweb.cgi" ''
      #!${pkgs.bash}/bin/bash
      export PERL5LIB=$PERL5LIB:${with pkgs.perlPackages; makePerlPath [ CGI HTMLParser ]}
      ${pkgs.perl}/bin/perl ${pkgs.git}/share/gitweb/gitweb.cgi
    '';
    gitweb-theme = pkgs.fetchFromGitHub {
      owner  = "kogakure";
      repo   = "gitweb-theme";
      rev    = "4305b3551551c470339c24a6567b1ac9e642ae54";
      sha256 = "0gagy0jvqb3mc587b6yy8l9g5j5wqr2xlz128v6f01364cb7whmv";
    };
in
if config.services.fcgiwrap.enable then ''
  server {
      listen       80;
      server_name  ${extra.host};

      location = / {
        return 301 http://${extra.host}/repos/;
      }

      location = /repos {
        return 301 http://${extra.host}/repos/;
      }

      location / {
        # fcgiwrap is set up to listen on this host:port
        fastcgi_pass                  unix:${config.services.fcgiwrap.socketAddress};
        include                       ${pkgs.nginx}/conf/fastcgi_params;
        fastcgi_param SCRIPT_FILENAME ${pkgs.git}/bin/git-http-backend;

        client_max_body_size 0;

        # export all repositories under GIT_PROJECT_ROOT

        fastcgi_param GIT_HTTP_EXPORT_ALL "";
        fastcgi_param GIT_PROJECT_ROOT    ${extra.git.projectroot};
        fastcgi_param PATH_INFO           $uri;
      }

      location /repos/static {
        alias ${gitweb-theme};
      }

      location /repos {
        include ${pkgs.nginx}/conf/fastcgi_params;
        gzip off;

        fastcgi_param GITWEB_CONFIG   ${gitwebConf};
        fastcgi_param SCRIPT_FILENAME ${gitweb-wrapper};
        fastcgi_pass  unix:${config.services.fcgiwrap.socketAddress};
      }

  }
'' else throw "fcgiwrap must be enabled to run git-server"

A nix-config/misc/imap-notifier/default.nix => nix-config/misc/imap-notifier/default.nix +114 -0
@@ 0,0 1,114 @@
extra:
{ config, lib, pkgs, ... }:
let notify = pkgs.callPackage (pkgs.fetchFromGitHub {
                      owner = "jb55";
                      repo = "imap-notify";
                      rev = "c0936c0bb4b7e283bbfeccdbac77f4cb50f71b3b";
                      sha256 = "19vadvnkg6bjp1607nlawdx1x07xnbbx7bgk66rbwrs4vhkvarkg";
                    }) {};
    penv = pkgs.python2.withPackages (ps: with ps; [ dbus-python pygobject2 ]);
    awake-from-sleep-fetcher = pkgs.writeScript "awake-from-sleep-fetcher" ''
      #!${penv}/bin/python2 -u

      import dbus
      import datetime
      import gobject
      import os
      from dbus.mainloop.glib import DBusGMainLoop

      def start_home():
        print("starting email fetcher")
        os.system("systemctl restart --user email-fetcher")

      def handle_sleep_callback(sleeping):
        if not sleeping:
          # awoke from sleep
          start_home()

      DBusGMainLoop(set_as_default=True) # integrate into main loob
      bus = dbus.SystemBus()             # connect to dbus system wide
      bus.add_signal_receiver(           # defince the signal to listen to
          handle_sleep_callback,            # name of callback function
          'PrepareForSleep',                 # signal name
          'org.freedesktop.login1.Manager',   # interface
          'org.freedesktop.login1'            # bus name
      )

      loop = gobject.MainLoop()          # define mainloop
      loop.run()
    '';

    notifier = user: pass: cmd: host: extra.util.writeBash "notifier" ''
      set -e

      arg="${host}"

      # wait for connectivity
      until ${pkgs.libressl.nc}/bin/nc -w 1 -vz jb55.com 12566 &>/dev/null; do sleep 1; done

      # run it once first in case we missed any from lost connectivity
      ${cmd} || :
      export IMAP_NOTIFY_USER=${user}
      export IMAP_NOTIFY_PASS=${pass}
      export IMAP_NOTIFY_CMD=${cmd}
      export IMAP_NOTIFY_HOST=${host}
      exec ${notify}/bin/imap-notify
    '';
in
with extra; {
  systemd.user.services.email-fetcher = {
    enable = if extra.is-minimal then false else true;
    description = "email fetcher";

    environment = {
      IMAP_ALLOW_UNAUTHORIZED = "0";
      IMAP_NOTIFY_PORT = "12788";
    };

    path = with pkgs; [ eject utillinux muchsync notmuch bash openssh ];

    serviceConfig.Type = "simple";
    serviceConfig.Restart = "always";
    serviceConfig.ExecStart =
      let cmd = util.writeBash "email-fetcher" ''
            set -e
            export HOME=/home/jb55
            export DATABASEDIR=$HOME/mail/personal

            notify() {
              local c=$(notmuch --config /home/jb55/.notmuch-config-personal count 'tag:inbox and not tag:filed and not tag:noise')
              local lc=$c
              if [ -f /tmp/last-email-count ]; then
                lc=$(</tmp/last-email-count)
              fi
              echo "$c" > /tmp/last-email-count
              if [ -f ~/var/notify/home ] && [ $c -ne $lc ]; then
                ${pkgs.libnotify}/bin/notify-send -i email-new "You Got Mail (inbox $c)"
              fi
            }

            (
              flock -x -w 100 200 || exit 1
              if [ -f ~/var/notify/home ]; then
                ${pkgs.libnotify}/bin/notify-send -i email-new "Fetching new mail..."
              fi
              muchsync -C ~/.notmuch-config-personal notmuch
              notify
            ) 200>/tmp/email-notify.lock
          '';
      in notifier "jb55@jb55.com" private.personal-email-pass cmd "jb55.com";
  };

  systemd.user.services.awake-from-sleep-fetcher = {
    enable = if extra.is-minimal then false else true;
    description = "";

    path = with pkgs; [ systemd ];

    wantedBy = [ "default.target" ];
    after    = [ "default.target" ];

    serviceConfig.ExecStart = "${awake-from-sleep-fetcher}";
  };

}

A nix-config/misc/msmtp/default.nix => nix-config/misc/msmtp/default.nix +12 -0
@@ 0,0 1,12 @@
extra:
{ config, lib, pkgs, ... }:
{
  # services.mail.sendmailSetuidWrapper = {
  #   program = "sendmail";
  #   source = lib.mkForce (extra.util.writeBash "sendmail" ''
  #     ${pkgs.msmtp}/bin/msmtp --read-envelope-from -C /home/jb55/.msmtprc -t "$@"
  #   '');
  #   setuid = false;
  #   setgid = false;
  # };
}

A nix-config/misc/util.nix => nix-config/misc/util.nix +7 -0
@@ 0,0 1,7 @@
{ pkgs }:
{
  writeBash = fname: body: pkgs.writeScript fname ''
    #! ${pkgs.bash}/bin/bash
    ${body}
  '';
}

A nix-config/networking/default.nix => nix-config/networking/default.nix +7 -0
@@ 0,0 1,7 @@
machine:
{ config, lib, pkgs, ... }:
{
  networking.hostName = machine;

  networking.firewall.allowPing = true;
}

A nix-config/nixpkgs/config.nix => nix-config/nixpkgs/config.nix +510 -0
@@ 0,0 1,510 @@
{ pkgs }:
let #monstercatPkgs = import <monstercatpkgs> { inherit pkgs; };
    #haskellOverrides = import ./haskell-overrides { inherit monstercatPkgs; };
    jb55pkgs = import <jb55pkgs> { inherit pkgs; };
    callPackage = pkgs.callPackage;
    doJailbreak = pkgs.haskell.lib.doJailbreak;
    dontCheck = pkgs.haskell.lib.dontCheck;
    regularFiles = builtins.filterSource (f: type: type == "symlink"
                                                || type == "directory"
                                                || type == "regular");
in {
  allowUnfree = true;
  allowUnfreeRedistributable = true;
  allowBroken = false;
  zathura.useMupdf = true;

  #firefox = #{
  #   enableGoogleTalkPlugin = false;
  #   enableAdobeFlash = false;
  # };

  packageOverrides = super: rec {

    # /run/current-system/sw/bin/ls $HOME/.emacs.d/elpa | sed 's/-[[:digit:]].*//g;s/\+$/-plus/g' | sort -u
    #emacs = super.emacsHead;
    nur = import (builtins.fetchTarball {
      url = "https://github.com/nix-community/NUR/archive/cff4dfbe6d6f4ab14560234fcf2d73332ee3ecc1.tar.gz";
      sha256 = "01yxz6w820vryirrwkmsnxkmvp35dncjp1n8fdfsq4n0r28nw31a";
    }) {
      inherit pkgs;
    };


    msmtp = pkgs.lib.overrideDerivation super.msmtp (attrs: {
      patches = [ /home/jb55/dev/msmtp-1.8.3/msmtpq-custom-conn-test.patch ];
    });

    weechat = super.weechat.override {configure = {availablePlugins, ...}: {
        scripts = with super.weechatScripts; [ wee-slack ];
      };
    };

    dunst = pkgs.lib.overrideDerivation super.dunst (attrs: {
      src = pkgs.fetchFromGitHub {
        owner = "jb55";
        repo  = "dunst";
        rev   = "138edff170e4e4a2bf6891bd634c4ec215d4b7ef";
        sha256 = "1pf3v4mrcd0cfhvm9fk9nwvgj5dy6qlbs0mhlcyx26cbqxd62brp";
      };
    });

    lastpass-cli = super.lastpass-cli.override { guiSupport = true; };

    wine = super.wineWowPackages.staging;

    dasht = super.lib.overrideDerivation super.dasht (attrs: {
      src = pkgs.fetchFromGitHub {
        owner  = "sunaku";
        repo   = "dasht";
        sha256 = "1i4gc68aypa0fk94ssy2gzakcn2qlmp9qf427km6fxbjx15qsbjn";
        rev    = "issue-45";
      };
    });

    phonectl = super.python3Packages.callPackage (import (super.fetchFromGitHub {
      owner  = "jb55";
      repo   = "phonectl";
      sha256 = "0wqpwg32qa1rzpw7881r6q2zklxlq1y4qgyyy742pihfh99rkcmj";
      rev    = "de0f37a20d16a32a73f9267860302357b2df0c20";
    })) {};

    notmuch = pkgs.lib.overrideDerivation super.notmuch (attrs: {
      src = pkgs.fetchFromGitHub {
        owner  = "jb55";
        repo   = "notmuch";
        rev    = "adcc427b8356cca865479b433d4be362b1f50e38";
        sha256 = "14l95hld7gs42p890a9r8dfw4m945iy2sf9bdyajs2yqjwmarwn7";
      };

      doCheck = false;
    });

    # wirelesstools =
    #   let
    #     patch = super.fetchurl {
    #               url    = "https://jb55.com/s/iwlist-print-scanning-info-allocation-failed.patch";
    #               sha256 = "31c97c6abf3f0073666f9f94f233fae2fcb8990aae5e7af1030af980745a8efc";
    #             };
    #   in
    #     pkgs.lib.overrideDerivation super.wirelesstools (attrs: {
    #       prePatch = ''
    #         patch -p0 < ${patch}