about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.emacs.d/lisp/init-completion.el74
-rw-r--r--.emacs.d/lisp/init-minibuffer.el32
-rw-r--r--.emacs.d/lisp/init-navigation.el3
-rw-r--r--.emacs.d/lisp/init-sql.el2
-rw-r--r--nix-conf/.sops.yaml1
-rw-r--r--nix-conf/home/includes/common.nix32
-rw-r--r--nix-conf/home/includes/dev-common.nix1
-rw-r--r--nix-conf/home/includes/linux-server.nix2
-rw-r--r--nix-conf/home/includes/zsh.nix140
-rw-r--r--nix-conf/home/otm.nix16
-rw-r--r--nix-conf/machines/edrahil/configuration.nix111
-rw-r--r--nix-conf/secrets/edrahil.yaml26
12 files changed, 322 insertions, 118 deletions
diff --git a/.emacs.d/lisp/init-completion.el b/.emacs.d/lisp/init-completion.el
index 27d4d19..eead296 100644
--- a/.emacs.d/lisp/init-completion.el
+++ b/.emacs.d/lisp/init-completion.el
@@ -55,21 +55,41 @@
 
 (use-package orderless
   :defer 2
-  :bind (:map minibuffer-local-map
-              ("C-l" . my/orderless-match-components-literally))
+  :bind
+  (:map minibuffer-local-map
+        ("C-l" . orderless-toggle-literal-matching))
+  (:map corfu-map
+        ("C-l" . orderless-toggle-literal-matching))
   :custom
   (orderless-component-separator 'orderless-escapable-split-on-space)
   (completion-styles '(orderless partial-completion basic))
   (completion-category-defaults nil)
-  (completion-category-overrides '((file (styles . (partial-completion orderless)))))
+  (completion-category-overrides '((file (styles partial-completion orderless))
+                                   (command (styles +orderless-with-strict-leading-initialism))
+                                   (variable (styles +orderless-with-strict-leading-initialism))
+                                   (symbol (styles +orderless-with-strict-leading-initialism))))
   (orderless-matching-styles '(orderless-literal orderless-regexp orderless-strict-initialism))
-  (orderless-style-dispatchers '(+orderless-dispatch))
+  (orderless-style-dispatchers (list #'+orderless-consult-dispatch
+                                     #'orderless-affix-dispatch))
   :config
-  (defun my/orderless-match-components-literally ()
-    "Components match literally for the rest of the session."
+  ;; Inspired by https://github.com/oantolin/orderless/blob/ac4aeb66f331f4c4a430d5556071e33177304c37/README.org#interactively-changing-the-configuration
+  (defun orderless-toggle-literal-matching ()
+    "Toggle matching components literally for the rest of the session."
     (interactive)
-    (setq-local orderless-matching-styles '(orderless-literal)
-                orderless-style-dispatchers nil))
+    (if orderless-style-dispatchers
+        (progn
+          (setq-local +saved-orderless-matching-styles orderless-matching-styles
+                      +saved-orderless-style-dispatchers orderless-style-dispatchers)
+          (setq-local orderless-matching-styles '(orderless-literal)
+                      orderless-style-dispatchers nil))
+      (setq-local orderless-matching-styles +saved-orderless-matching-styles
+                  orderless-style-dispatchers +saved-orderless-style-dispatchers))
+    (when vertico--input
+      (setq vertico--input t)
+      (vertico--update))
+    (when corfu--input
+      (setq corfu--input t)
+      (corfu--update)))
 
   (defun orderless-strict-initialism (component &optional leading)
     "Match a component as a strict initialism.
@@ -88,48 +108,36 @@ candidate, in that order, at the beginning of words, with
 no words in between, beginning with the first word."
     (orderless-strict-initialism component t))
 
-  ;; based on https://github.com/minad/consult/wiki#minads-orderless-configuration
-  (defvar +orderless-dispatch-alist
-    '((?% . char-fold-to-regexp)
-      (?! . orderless-without-literal)
-      (?` . orderless-strict-leading-initialism)
-      (?= . orderless-literal)
-      (?_ . orderless-prefix)
-      (?~ . orderless-flex)))
+  (setf (alist-get ?, orderless-affix-dispatch-alist) #'orderless-strict-leading-initialism)
+  (add-to-list 'orderless-affix-dispatch-alist '(?` . orderless-strict-initialism) t)
 
-  (defun +orderless--suffix-regexp ()
+  ;; Copied from https://github.com/minad/consult/wiki#minads-orderless-configuration
+  (defun +orderless--consult-suffix ()
+    "Regexp which matches the end of string with Consult tofu support."
     (if (and (boundp 'consult--tofu-char) (boundp 'consult--tofu-range))
         (format "[%c-%c]*$"
                 consult--tofu-char
                 (+ consult--tofu-char consult--tofu-range -1))
       "$"))
 
+  ;; Copied from https://github.com/minad/consult/wiki#minads-orderless-configuration
   ;; Recognizes the following patterns:
-  ;; * ~flex flex~
-  ;; * =literal literal=
-  ;; * _prefix prefix_
-  ;; * %char-fold char-fold%
-  ;; * `strict-leading-initialism strict-leading-initialism`
-  ;; * !without-literal without-literal!
   ;; * .ext (file extension)
   ;; * regexp$ (regexp matching at end)
-  (defun +orderless-dispatch (word _index _total)
+  (defun +orderless-consult-dispatch (word _index _total)
     (cond
      ;; Ensure that $ works with Consult commands, which add disambiguation suffixes
      ((string-suffix-p "$" word)
-      `(orderless-regexp . ,(concat (substring word 0 -1) (+orderless--suffix-regexp))))
+      `(orderless-regexp . ,(concat (substring word 0 -1) (+orderless--consult-suffix))))
      ;; File extensions
      ((and (or minibuffer-completing-file-name
                (derived-mode-p 'eshell-mode))
            (string-match-p "\\`\\.." word))
-      `(orderless-regexp . ,(concat "\\." (substring word 1) (+orderless--suffix-regexp))))
-     ;; Ignore single !
-     ((equal "!" word) `(orderless-literal . ""))
-     ;; Prefix and suffix
-     ((if-let (x (assq (aref word 0) +orderless-dispatch-alist))
-          (cons (cdr x) (substring word 1))
-        (when-let (x (assq (aref word (1- (length word))) +orderless-dispatch-alist))
-          (cons (cdr x) (substring word 0 -1))))))))
+      `(orderless-regexp . ,(concat "\\." (substring word 1) (+orderless--consult-suffix))))))
+
+  ;; Based on https://github.com/minad/consult/wiki#minads-orderless-configuration
+  (orderless-define-completion-style +orderless-with-strict-leading-initialism
+    (orderless-matching-styles '(orderless-literal orderless-regexp orderless-strict-leading-initialism))))
 
 ;; code completion - corfu
 (use-package corfu
diff --git a/.emacs.d/lisp/init-minibuffer.el b/.emacs.d/lisp/init-minibuffer.el
index 69fec18..c0b6ee3 100644
--- a/.emacs.d/lisp/init-minibuffer.el
+++ b/.emacs.d/lisp/init-minibuffer.el
@@ -227,7 +227,7 @@ DEFS is a plist associating completion categories to commands."
                ("G" . consult-git-grep)
                ("r" . consult-ripgrep)
                ("R" . consult-ripgrep) ;; can't use r in isearch-mode, so add R too
-               ("M-r" . consult-ripgrep-unrestricted)
+               ("u" . consult-ripgrep-unrestricted)
                ("*" . consult-ripgrep-thing-at-point)
                ("z" . consult-z-ripgrep)
                ("^" . consult-ripgrep-parent)
@@ -235,8 +235,8 @@ DEFS is a plist associating completion categories to commands."
                ("L" . consult-line-multi)
                ("m" . consult-multi-occur)
                ("k" . consult-keep-lines)
-               ("u" . consult-focus-lines)
-               ("e" . consult-isearch))
+               ("C-f" . consult-focus-lines)
+               ("e" . consult-isearch-history))
          (:map vertico-map
                ;; These are used for previewing with some consult commands (see consult-customize call below)
                ("C-S-p" . vertico-previous)
@@ -403,7 +403,7 @@ DEFS is a plist associating completion categories to commands."
     "Remove a # character from the beginning of the current line.
 
 Designed to be used for consult commands that automatically add a # at the beginning of the minibuffer.
-See `+become-consult-line'."
+See `+become' and the functions that call it (e.g. `+become-consult-line')."
     (interactive)
     (save-excursion
       (beginning-of-line)
@@ -481,17 +481,31 @@ See `+become-consult-line'."
   ;; See https://github.com/oantolin/embark/commit/47daded610b245caf01a97d74c940aff91fe14e2#r46010972
   :demand t
   :config
+  (defun +become (fn)
+    "Remove the leading # from the minibuffer, and call `FN'.
+Useful with embark-become, when changing from a command that uses # as a separator, to one that doesn't."
+    (interactive)
+    (setq unread-command-events (listify-key-sequence "\C-x\C-\M-x"))
+    (funcall-interactively fn))
+  (defun +become-consult-line ()
+    "A version of `consult-line', designed for use with `embark-become'.
+The leading # added by other consult commands is removed."
+    (interactive)
+    (+become #'consult-line))
   (defun +become-consult-line ()
-    "TODO."
+    "A version of `consult-imenu', designed for use with `embark-become'.
+The leading # added by other consult commands is removed."
     (interactive)
-    (progn
-      (setq unread-command-events (listify-key-sequence "\C-x\C-\M-x"))
-      (consult-line)))
+    (+become #'consult-imenu))
   :bind
   (:map embark-consult-async-search-map
         ("l" . +become-consult-line)
+        ("f" . consult-focus-lines)
+        ("i" . +become-consult-imenu)
         ("^" . consult-ripgrep-parent)
-        ("R" . consult-ripgrep-unrestricted))
+        ("u" . consult-ripgrep-unrestricted)
+        ("c" . consult-ripgrep-case-sensitive)
+        ("z" . consult-z-ripgrep))
   :hook
   (embark-collect-mode . consult-preview-at-point-mode))
 
diff --git a/.emacs.d/lisp/init-navigation.el b/.emacs.d/lisp/init-navigation.el
index 7d87b8a..0ad8d0d 100644
--- a/.emacs.d/lisp/init-navigation.el
+++ b/.emacs.d/lisp/init-navigation.el
@@ -72,7 +72,8 @@ Or remove all highlighted symbols in the current buffer (with`ARG')."
   :bind
   ("C-c o" . symbol-overlay-put-or-clear)
   ("M-N" . symbol-overlay-switch-forward)
-  ("M-P" . symbol-overlay-switch-backward))
+  ("M-P" . symbol-overlay-switch-backward)
+  (:map symbol-overlay-map ("o" . symbol-overlay-put-or-clear)))
 
 (use-package gumshoe
   :after perspective
diff --git a/.emacs.d/lisp/init-sql.el b/.emacs.d/lisp/init-sql.el
index d71e73d..ca304e1 100644
--- a/.emacs.d/lisp/init-sql.el
+++ b/.emacs.d/lisp/init-sql.el
@@ -46,7 +46,7 @@
   :hook
   (sql-mode . sqlup-mode)
   (sql-interactive-mode . sqlup-mode)
-  :bind ("C-c c u" . sqlup-capitalize-keywords-in-region))
+  :bind ("C-c c C-u" . sqlup-capitalize-keywords-in-region))
 
 (use-package sql-indent
   :commands sqlind-minor-mode)
diff --git a/nix-conf/.sops.yaml b/nix-conf/.sops.yaml
index 24125e8..c9f1394 100644
--- a/nix-conf/.sops.yaml
+++ b/nix-conf/.sops.yaml
@@ -7,6 +7,7 @@ creation_rules:
     key_groups:
     - age:
       - *server_edrahil
+      - *admin_djm
   - path_regex: secrets/djmuk2\.(json|yaml)$
     key_groups:
     - age:
diff --git a/nix-conf/home/includes/common.nix b/nix-conf/home/includes/common.nix
index 6a0c721..2dc3a8a 100644
--- a/nix-conf/home/includes/common.nix
+++ b/nix-conf/home/includes/common.nix
@@ -8,6 +8,20 @@ let
   hcr = pkgs.callPackage ./scripts/hm-changes-report.nix { inherit config pkgs; };
   scr = pkgs.callPackage ./scripts/system-changes-report.nix { inherit config pkgs; };
   unstable = import <unstable> { };
+
+  nix-search = (
+    pkgs.buildGoModule {
+      pname = "nix-search";
+      version = "0.3.1";
+      src = pkgs.fetchFromGitHub {
+        owner = "diamondburned";
+        repo = "nix-search";
+        rev = "e616ac1c82a616fa6e6d8c94839c5052eb8c808d";
+        hash = "sha256-h9yYOjL9i/m0r5NbqMcLMFNnwSKsIgfUr5qk+47pOtc=";
+      };
+      vendorHash = "sha256-bModWDH5Htl5rZthtk/UTw/PXT+LrgyBjsvE6hgIePY=";
+    }
+  );
 in
 {
   imports = [
@@ -70,6 +84,7 @@ in
     inetutils
     ispell
     isync
+    jd-diff-patch
     jq
     libqalculate
     lscolors
@@ -78,6 +93,7 @@ in
     nix-info
     nix-prefetch-git
     nix-prefetch-github
+    nix-search
     nixpkgs-review
     nvd
     pass
@@ -245,6 +261,22 @@ in
         identityFile = "~/.ssh/id_ed25519";
         identitiesOnly = true;
       };
+      "hb-backup" = {
+        hostname = "de1.hashbang.sh";
+        identityFile = "~/.ssh/hb_backup_key";
+        identitiesOnly = true;
+      };
+      "bs-backup" = {
+        hostname = "ssh.blinkenshell.org";
+        port = 2222;
+        identityFile = "~/.ssh/bs_backup_key";
+        identitiesOnly = true;
+      };
+      "tt-backup" = {
+        hostname = "tilde.team";
+        identityFile = "~/.ssh/tt_backup_key";
+        identitiesOnly = true;
+      };
     };
   };
 
diff --git a/nix-conf/home/includes/dev-common.nix b/nix-conf/home/includes/dev-common.nix
index 12f4893..92b286e 100644
--- a/nix-conf/home/includes/dev-common.nix
+++ b/nix-conf/home/includes/dev-common.nix
@@ -22,6 +22,7 @@ in
       gopass-jsonapi
       multimarkdown
       neovim
+      ripgrep-all
     ]
     ++ optionals (!stdenv.isDarwin) [
       ffmpeg
diff --git a/nix-conf/home/includes/linux-server.nix b/nix-conf/home/includes/linux-server.nix
index 427b167..9b0b353 100644
--- a/nix-conf/home/includes/linux-server.nix
+++ b/nix-conf/home/includes/linux-server.nix
@@ -45,7 +45,7 @@
       set -g status-interval 1
 
       set -g status-left "#[fg=yellow]#h#[default]"
-      set -g status-right "#[fg=blue]%a%d/%m#[fg=yellow]%H:%M:%S"
+      set -g status-right "#[fg=blue]%a%d/%m#[fg=yellow]#(TZ="Europe/London" date +%%H:%%M:%%S)"
     '';
   };
 
diff --git a/nix-conf/home/includes/zsh.nix b/nix-conf/home/includes/zsh.nix
index a76fea4..db17933 100644
--- a/nix-conf/home/includes/zsh.nix
+++ b/nix-conf/home/includes/zsh.nix
@@ -17,10 +17,17 @@ in
     changeDirWidgetCommand = "fd --type=d --hidden --strip-cwd-prefix --exclude .git --exclude node_modules"; # FZF_ALT_C_COMMAND
     changeDirWidgetOptions = [ "--preview 'eza --tree --color=always {} | head -200'" ]; # FZF_ALT_C_OPTS
     defaultCommand = "fd --hidden --strip-cwd-prefix --exclude .git --exclude node_modules"; # FZF_DEFAULT_COMMAND
-    defaultOptions = [ "--bind=ctrl-t:toggle-all" "--bind=ctrl-j:jump" ]; # FZF_DEFAULT_OPTS
-    fileWidgetCommand = config.programs.fzf.defaultCommand;  # FZF_CTRL_T_COMMAND
+    defaultOptions = [
+      "--bind=ctrl-t:toggle-all"
+      "--bind=ctrl-j:jump"
+    ]; # FZF_DEFAULT_OPTS
+    fileWidgetCommand = config.programs.fzf.defaultCommand; # FZF_CTRL_T_COMMAND
     fileWidgetOptions = [ "--preview '${show_file_or_dir_preview}'" ]; # FZF_CTRL_T_OPTS
-    historyWidgetOptions = [ "--preview 'echo {}'" "--preview-window down:3:hidden:wrap" "--bind 'ctrl-t:toggle-preview'" ]; # FZF_CTRL_R_OPTS
+    historyWidgetOptions = [
+      "--preview 'echo {}'"
+      "--preview-window down:3:hidden:wrap"
+      "--bind 'ctrl-t:toggle-preview'"
+    ]; # FZF_CTRL_R_OPTS
   };
   programs.zoxide = {
     enable = true;
@@ -80,6 +87,7 @@ in
       l = "eza";
       la = "eza -a";
       lg = "eza -G";
+      lga = "eza -aG";
       ll = "\\eza --icons --git --colour --long --group-directories-first --classify";
       lla = "ll -a";
       t = "eza --tree";
@@ -202,21 +210,29 @@ in
       # vip  # edits the first result from ea (roughly equivalent to vi `ea p 1`)
       # vip <n> # edits the nth result from ea (vi `ea p <n>`)
       # vip <n> foo # if the nth result from ea is a directory, edit foo in that directory (vi `ea p <n>`/foo)
+      # Will add +<line-number>, where the line number is available
       function _vip () {
-        CMD=(''${=1}) # zsh only, not portable; something like CMD=($(echo $1)) is more portable but is ugly
-        BASE_PATH=$(ea p ''${2:-1})
+        local cmd=(''${=1}) # zsh only, not portable; something like CMD=($(echo $1)) is more portable but is ugly
+        local idx=''${2:-1}
+        local base_path=$(ea p $idx)
+        local line=$(ea p $idx "{line}")
+        local ea_format="'{path}'"
 
-        if [ -z "$BASE_PATH" ]; then
+        if [ -z "$base_path" ]; then
           echo "No file path found for index $2"
           return 1
         fi
 
-        if [ $# -gt 2 -a ! -d "$BASE_PATH" ]; then
-          echo "$BASE_PATH is not a directory"
+        if [ $# -gt 2 -a ! -d "$base_path" ]; then
+          echo "$base_path is not a directory"
           return 2
         fi
 
-        $CMD "''${BASE_PATH}''${3}"
+        if [ $# -lt 3 -a $line -ne 1 ]; then
+          ea_format+=" +{line}"
+        fi
+
+        eval $(ea p $idx "$cmd ''${ea_format}$3")
       }
 
       function vip () {
@@ -311,59 +327,57 @@ in
       [[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
     '';
 
-    plugins =
-      with pkgs;
-      [
-        {
-          name = "zsh-autopair";
-          src = zsh-autopair;
-          file = "share/zsh/zsh-autopair/autopair.zsh";
-        }
-        {
-          name = "zsh-bd";
-          src = zsh-bd;
-          file = "share/zsh-bd/bd.zsh";
-        }
-        {
-          name = "zsh-fzf-tab";
-          src = zsh-fzf-tab;
-          file = "share/fzf-tab/fzf-tab.zsh";
-        }
-        {
-          name = "zsh-fast-syntax-highlighting";
-          src = zsh-fast-syntax-highlighting;
-          file = "share/zsh/site-functions/fast-syntax-highlighting.plugin.zsh";
-        }
-        {
-          name = "zsh-powerlevel10k";
-          src = zsh-powerlevel10k;
-          file = "share/zsh-powerlevel10k/powerlevel10k.zsh-theme";
-        }
-        {
-          name = "zsh-forgit";
-          src = zsh-forgit;
-          file = "share/zsh/zsh-forgit/forgit.plugin.zsh";
-        }
-        {
-          name = "zsh-edit";
-          src = zsh-edit;
-          file = "share/zsh/zsh-edit/zsh-edit.plugin.zsh";
-        }
-        {
-          name = "fzf-git.sh";
-          src = fzf-git-sh;
-          file = "share/fzf-git-sh/fzf-git.sh";
-        }
-        {
-          name = "per-directory-history";
-          src = fetchFromGitHub {
-            owner = "jimhester";
-            repo = "per-directory-history";
-            rev = "0687bbfd736da566472a6d67c2b45c501b73d405";
-            sha256 = "7Z0qaDhgopKt9BDKSqdziw9jsVgiLLafs30wPPbz+oo=";
-          };
-          file = "per-directory-history.zsh";
-        }
-      ];
+    plugins = with pkgs; [
+      {
+        name = "zsh-autopair";
+        src = zsh-autopair;
+        file = "share/zsh/zsh-autopair/autopair.zsh";
+      }
+      {
+        name = "zsh-bd";
+        src = zsh-bd;
+        file = "share/zsh-bd/bd.zsh";
+      }
+      {
+        name = "zsh-fzf-tab";
+        src = zsh-fzf-tab;
+        file = "share/fzf-tab/fzf-tab.zsh";
+      }
+      {
+        name = "zsh-fast-syntax-highlighting";
+        src = zsh-fast-syntax-highlighting;
+        file = "share/zsh/site-functions/fast-syntax-highlighting.plugin.zsh";
+      }
+      {
+        name = "zsh-powerlevel10k";
+        src = zsh-powerlevel10k;
+        file = "share/zsh-powerlevel10k/powerlevel10k.zsh-theme";
+      }
+      {
+        name = "zsh-forgit";
+        src = zsh-forgit;
+        file = "share/zsh/zsh-forgit/forgit.plugin.zsh";
+      }
+      {
+        name = "zsh-edit";
+        src = zsh-edit;
+        file = "share/zsh/zsh-edit/zsh-edit.plugin.zsh";
+      }
+      {
+        name = "fzf-git.sh";
+        src = fzf-git-sh;
+        file = "share/fzf-git-sh/fzf-git.sh";
+      }
+      {
+        name = "per-directory-history";
+        src = fetchFromGitHub {
+          owner = "jimhester";
+          repo = "per-directory-history";
+          rev = "0687bbfd736da566472a6d67c2b45c501b73d405";
+          sha256 = "7Z0qaDhgopKt9BDKSqdziw9jsVgiLLafs30wPPbz+oo=";
+        };
+        file = "per-directory-history.zsh";
+      }
+    ];
   };
 }
diff --git a/nix-conf/home/otm.nix b/nix-conf/home/otm.nix
index 7d7c667..67dab1e 100644
--- a/nix-conf/home/otm.nix
+++ b/nix-conf/home/otm.nix
@@ -103,13 +103,20 @@ let
     # and https://github.com/NixOS/nixpkgs/blob/4877ea239f4d02410c3516101faf35a81af0c30e/pkgs/development/compilers/openjdk/jre.nix#L32
     passthru.home = "${zscaler-jdk}"; # make sure JAVA_HOME is set
     installPhase =
+      # This is probably equivalent to
+      # $out/bin/keytool -import -noprompt -trustcacerts -alias zscalerrootca -keystore $out/lib/security/cacerts <<< "${zscaler-cert}"
+      # but follow the zscaler instructions just in case
       old.installPhase
       + ''
-        $out/bin/keytool -import -noprompt -trustcacerts -alias zscalerrootca -keystore $out/lib/security/cacerts <<< "${zscaler-cert}"
+        ${pkgs.openssl}/bin/openssl x509 -inform pem -outform der <<< "${zscaler-cert}" | $out/bin/keytool -import -noprompt -trustcacerts -alias zscalerrootca -keystore $out/lib/security/cacerts
       '';
   });
 
   zscaler-lein = pkgs.leiningen.override { jdk = zscaler-jdk; };
+
+  toggle = pkgs.writeShellScriptBin "remote-toggle" ''
+    ssh -nT pi "playerctl play-pause" 2>/dev/null
+  '';
 in
 {
   imports = [ ./includes/darwin.nix ];
@@ -143,7 +150,10 @@ in
     yarn_build = "aws codeartifact login --tool npm --repository otm-js --domain otm --domain-owner 103567893073 --region eu-west-1 --profile aws_otm_dev_developers && yarn && yarn build && notify";
   };
 
-  home.packages = with pkgs; [ zscaler-lein ];
+  home.packages = with pkgs; [
+    zscaler-lein
+    toggle
+  ];
 
   home.file = {
     "certs/zscaler-cert.pem".source = zscaler-cert-file;
@@ -151,6 +161,8 @@ in
     "certs/full-cert.pem".source = full-cert-file;
     "certs/internal-ca.pem".text = internal-cert;
     "certs/staging-internal-ca.pem".text = internal-staging-cert;
+
+    ".skhdrc".text = "play : remote-toggle";
   };
 
   sops.secrets = {
diff --git a/nix-conf/machines/edrahil/configuration.nix b/nix-conf/machines/edrahil/configuration.nix
index d78c2a8..05f43a2 100644
--- a/nix-conf/machines/edrahil/configuration.nix
+++ b/nix-conf/machines/edrahil/configuration.nix
@@ -3,6 +3,7 @@
   imports = [
     ./hardware-configuration.nix
     ./network-configuration.nix
+    <sops-nix/modules/sops>
   ];
 
   boot.tmp.cleanOnBoot = true;
@@ -17,6 +18,16 @@
     ];
   };
 
+  sops = {
+    defaultSopsFile = builtins.path {
+      path = /etc/nixos/secrets/edrahil.yaml;
+      name = "edrahil-secrets.yaml";
+    };
+    secrets.restic_password = {
+      owner = config.users.users.djm.name;
+    };
+  };
+
   services.openssh = {
     enable = true;
     ports = [ 2222 ];
@@ -44,6 +55,98 @@
     localuser = null;
   };
 
+  services.restic = {
+    backups = {
+      hb = {
+        paths = [ "${config.users.users.djm.home}" ];
+        repository = "sftp:djm@hb-backup:/home/djm/backup/edrahil";
+        initialize = true;
+        user = "djm";
+        environmentFile = "/etc/restic-environment";
+        passwordFile = config.sops.secrets.restic_password.path;
+        timerConfig = {
+          OnCalendar = "02:25";
+          RandomizedDelaySec = "20min";
+        };
+        exclude = [
+          "irclogs"
+          ".cache"
+          ".config"
+          ".directory_history"
+          ".local"
+          "BTS"
+          "nixpkgs"
+        ];
+        extraBackupArgs = [
+          "--compression=max"
+        ];
+        pruneOpts = [
+          "--keep-daily 5"
+          "--keep-weekly 2"
+          "--keep-monthly 3"
+        ];
+      };
+      bs = {
+        paths = [ "${config.users.users.djm.home}" ];
+        repository = "sftp:djm@bs-backup:/home/djm/backup/edrahil";
+        initialize = true;
+        user = "djm";
+        environmentFile = "/etc/restic-environment";
+        passwordFile = config.sops.secrets.restic_password.path;
+        timerConfig = {
+          OnCalendar = "03:15";
+          RandomizedDelaySec = "20min";
+        };
+        exclude = [
+          "irclogs"
+          ".cache"
+          ".config"
+          ".directory_history"
+          ".local"
+          "BTS"
+          "nixpkgs"
+        ];
+        extraBackupArgs = [
+          "--compression=max"
+        ];
+        pruneOpts = [
+          "--keep-daily 5"
+          "--keep-weekly 2"
+          "--keep-monthly 3"
+        ];
+      };
+      tt = {
+        paths = [ "${config.users.users.djm.home}" ];
+        repository = "sftp:djm@tt-backup:/home/djm/backup/edrahil";
+        initialize = true;
+        user = "djm";
+        environmentFile = "/etc/restic-environment";
+        passwordFile = config.sops.secrets.restic_password.path;
+        timerConfig = {
+          OnCalendar = "04:05";
+          RandomizedDelaySec = "20min";
+        };
+        exclude = [
+          "irclogs"
+          ".cache"
+          ".config"
+          ".directory_history"
+          ".local"
+          "BTS"
+          "nixpkgs"
+        ];
+        extraBackupArgs = [
+          "--compression=max"
+        ];
+        pruneOpts = [
+          "--keep-daily 5"
+          "--keep-weekly 2"
+          "--keep-monthly 3"
+        ];
+      };
+    };
+  };
+
   time.timeZone = "Europe/London";
 
   users.users.djm = {
@@ -79,6 +182,14 @@
 
   programs.vim.defaultEditor = true;
 
+  environment.etc = {
+    "restic-environment" = {
+      text = ''
+        RESTIC_COMPRESSION=max
+      '';
+    };
+  };
+
   environment.systemPackages = with pkgs; [
     #procmail
     git
diff --git a/nix-conf/secrets/edrahil.yaml b/nix-conf/secrets/edrahil.yaml
index 1b15022..1f4c31e 100644
--- a/nix-conf/secrets/edrahil.yaml
+++ b/nix-conf/secrets/edrahil.yaml
@@ -1,4 +1,5 @@
-ipv6_address: ENC[AES256_GCM,data:4oIZakw5l3axCBc4aMTj1kxpUNg=,iv:/wocTWNcxkgOQQF31XJ3/tMuUm9u+oDSwa2IqWkTMnI=,tag:vx8gYah7r3qSt/dbd4U/cA==,type:str]
+ipv6_address: ENC[AES256_GCM,data:CGQWUSuwmucIEwtlLK0FodXOWjM=,iv:ZLPiACwjOmes+FbezZKjjwUETujhTbT++4zCuoptpkY=,tag:VjMtetJhRDlJXdKAmJlOxQ==,type:str]
+restic_password: ENC[AES256_GCM,data:2sxeUDRdh9cPv0ACY9EIP7JcmPFo/w==,iv:bkA/FW82l5gSEOZPtVhSNoATmoJf07kC0FJLAcXFkZU=,tag:PbDY039oBas7CvK8RaFRkA==,type:str]
 sops:
     kms: []
     gcp_kms: []
@@ -8,14 +9,23 @@ sops:
         - recipient: age1tjfctwnwldmyxnu6qmeufgr9l79vyzmrs7fy58v3d0qj4x4nhqhq2gjmlp
           enc: |
             -----BEGIN AGE ENCRYPTED FILE-----
-            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpOExVL2dvUGloajRUM042
-            a2xQMTcrRDdmUWJmdkxMdnZleHYyZGordW5rCjBTQmRWdndYbjNsQnd2QXo5VVRs
-            aytvOVEvZUtBMm9lYlVNTjlaN3JvS00KLS0tIFBJVWlNQzB2ZXA4ZWRKdmRaSjYy
-            L2owQXVwRXRnWmhuVGk5QjVwQjdweEEKxksatVlA9RP4CqRCRAiXjLE4W3iZa1P6
-            pOtqoPB+QtcnJtEo5rOU+Bw7nlHVocy9oshwrgN+vNWoiCoQwAGUSw==
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvUGNnbm1Jemc5dVZYM01h
+            R0h4RlRpVnU1ZGxyL3V2TXdYS2JUSHFNTm1BCjJxdlFFbURjdXBaNjNUdldXNkJy
+            blZYRkhkZUgxR0lST2MxM3hENUhiQkEKLS0tIDhvYjhpRnpIVnVmV0VoTDFNOXIx
+            RlB1dXVsdEhETTNUdTRIbGxIMGNFSEkKqeafOyRg3F9dtENNnH5DhJzJU+AEEqrV
+            nfndOlVQe0G/e8SUzUYjVtD6V6Hj/x8OxN6FSOfZnNFNFHQgJ42jFg==
             -----END AGE ENCRYPTED FILE-----
-    lastmodified: "2024-08-30T08:39:18Z"
-    mac: ENC[AES256_GCM,data:jRAyoYXXG6AKugVUyqv6tDp3orSZn66zn7ypVh5wsmbQictV8jeY6lrN/0AZsKZyTDuOlguG1NYRm8WHdSndZtPyv18LAme4nnAcMkqBGFQ4Uo5kx1zNv/+fi6CzLNYwiok1UbJGtMdASqpSXKgMiuGaBct5OohXzzgvHE7npFY=,iv:PCYwNQpBnhvZNhnwUO4iMuO6/A09XvPrRucQX4Hzx+4=,tag:d8MhigIjNM49fnas7JeaTg==,type:str]
+        - recipient: age1w7kjp0qdgfyg9cyj5w4qc4fc9qz3w65xw2veazesfgdenqrd3ucqsc5ejv
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwSGIvTjdEZGdGWm9ldnow
+            RGxnZ3RhMmthQXFhOCtaNUk1UGhYSytQdkFnCmY3UUxWVFJKeDE5eG9GNktobndz
+            UjBLOFBNWmFXSmJ2akRDYitsZm9TcmMKLS0tIDZoTGFxSWpwWmFTNjN6b2Q2T2Y3
+            Qm1PWnAvZGcxWGZjcnE4QWJZaDE2cGMKOAfTIipI68eJnOnvpQyLCOyH1KAWd/d/
+            SLnJQ+rmh9onA/znahB7Pn3LQtfKLNBADVtwLIuPID0FcgUW7nlOiw==
+            -----END AGE ENCRYPTED FILE-----
+    lastmodified: "2024-09-17T13:43:53Z"
+    mac: ENC[AES256_GCM,data:lVMbjnDvwlw72CiixJkEXCO7a20DYi4zKA8JTf0kSVQR/xjr9WbLpyllNq9Ex+eca/X0yaHBYjyOnBBpgz1h5o4i5iq738VXOEqD9v5BMdOrVmmDNnVcTAXqmWZGE7/pGmkiKef/iXOyJT2vsrrYR0vhgrvo/0WXce1YLUA4NTs=,iv:Y1w/llSNDry+PWz4oA/0MBJ+Ra6ceC1ZHMKb+CPCvE0=,tag:r2RR6ZfGL9TYwHtV9auL3A==,type:str]
     pgp: []
     unencrypted_suffix: _unencrypted
     version: 3.8.1