treewide: move nixos modules

This commit is contained in:
Charlotte Van Petegem 2024-07-18 15:04:18 +02:00
parent d84be7c616
commit 8eff4c5e4f
73 changed files with 62 additions and 62 deletions

View file

@ -0,0 +1,19 @@
{ config, lib, pkgs, ... }:
{
options.chvp.base.bluetooth.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.base.bluetooth.enable {
chvp.base.zfs.systemLinks = [{ path = "/var/lib/bluetooth"; type = "cache"; }];
hardware.bluetooth.enable = true;
services.blueman.enable = true;
home-manager.users.charlotte = lib.mkIf config.chvp.graphical.enable ({ ... }: {
services.blueman-applet.enable = true;
});
};
}

View file

@ -0,0 +1,134 @@
{ config, lib, pkgs, ... }:
{
imports = [
./bluetooth
./emacs
./mail
./network
./nix
./phone-push
./smartd
./ssh
./sshd
./telegraf
./tmux
./zfs
./zsh
];
options.chvp = {
stateVersion = lib.mkOption {
example = "20.09";
};
dataPrefix = lib.mkOption {
default = "";
example = "/data";
};
cachePrefix = lib.mkOption {
default = "";
example = "/cache";
};
};
config = {
home-manager.useGlobalPkgs = true;
system = {
stateVersion = config.chvp.stateVersion;
autoUpgrade = {
enable = true;
flake = "gitlab:chvp/nixos-config?host=git.chvp.be";
dates = "01/4:00";
randomizedDelaySec = "10min";
};
};
home-manager.users = {
charlotte = { ... }: {
home.stateVersion = config.chvp.stateVersion;
systemd.user.sessionVariables = config.home-manager.users.charlotte.home.sessionVariables;
};
root = { ... }: {
home.stateVersion = config.chvp.stateVersion;
};
};
environment.systemPackages = with pkgs; [ git htop moreutils ncdu ripgrep unzip zip ];
boot.kernelParams = [ "mitigations=off" ];
console = {
colors = [
"51576d"
"e78284"
"a6d189"
"e5c890"
"8caaee"
"f4b8e4"
"81c8be"
"b5bfe2"
"626880"
"e78284"
"a6d189"
"e5c890"
"8caaee"
"f4b8e4"
"81c8be"
"a5adce"
];
earlySetup = true;
font = "Lat2-Terminus16";
keyMap = "us";
};
i18n = {
defaultLocale = "en_IE.UTF-8";
extraLocaleSettings = {
LC_TIME = "en_GB.UTF-8";
};
supportedLocales = [
"en_GB.UTF-8/UTF-8"
"en_IE.UTF-8/UTF-8"
"en_US.UTF-8/UTF-8"
];
};
security = {
sudo.enable = false;
doas = {
enable = true;
extraRules = [
{
users = [ "charlotte" ];
noPass = true;
cmd = "nix-collect-garbage";
runAs = "root";
}
];
};
polkit.enable = true;
};
services.fwupd.enable = true;
users = {
mutableUsers = false;
defaultUserShell = pkgs.zsh;
users = {
charlotte = {
isNormalUser = true;
home = "/home/charlotte";
description = "Charlotte Van Petegem";
extraGroups = [ "systemd-journal" ];
hashedPasswordFile = config.age.secrets."passwords/users/charlotte".path;
};
root.hashedPasswordFile = config.age.secrets."passwords/users/root".path;
};
};
age.secrets."passwords/users/charlotte".file = ../../../secrets/passwords/users/charlotte.age;
age.secrets."passwords/users/root".file = ../../../secrets/passwords/users/root.age;
};
}

View file

@ -0,0 +1,349 @@
;;; init --- My emacs init file
;;; Commentary:
;;; Code:
(eval-when-compile
(require 'use-package)
(require 'use-package-ensure)
(setq use-package-verbose nil)
(setq use-package-always-ensure t))
;; Dependencies that inject `:keywords' into `use-package' should be
;; included before all other packages.
;; For :diminish in (use-package). Hides minor modes from the status line.
(use-package diminish)
;; For :general in (use-package). Keybinding management framework.
(use-package general
:after evil
:config
(general-evil-setup t)
(defun chvp--kill-current-buffer ()
(interactive)
(kill-buffer (current-buffer))
)
;; Create bindings under the leader
(general-create-definer lmap
:states '(normal visual insert emacs motion)
:prefix "SPC"
:global-prefix "C-SPC"
)
(nmap "<escape>" 'save-buffer)
(lmap
"" nil ;; Unbind SPC, I don't use it for navigation anyway.
"SPC" '(:ignore t :which-key "mode")
":" '(eval-expression :which-key "eval")
"b" '(:ignore t :which-key "buffer")
"bd" '(chvp--kill-current-buffer :which-key "kill")
"br" '(rename-buffer :which-key "rename")
"f" '(:ignore t :which-key "file")
"ff" '(find-file :which-key "find")
"fs" '(save-buffer :which-key "save")
"h" '(:ignore t :which-key "help")
"hb" '(describe-bindings :which-key "bindings")
"hf" '(describe-function :which-key "function")
"hv" '(describe-variable :which-key "variable")
"q" '(:ignore t :which-key "quit")
"qq" '(delete-frame :which-key "quit")
"s" '(:ignore t :which-key "search")
"w" '(:ignore t :which-key "window")
"wv" '(split-window-vertically :which-key "split vertical")
"ws" '(split-window-horizontally :which-key "split horizontal")
"wd" '(delete-window :which-key "delete")
"w-" '(text-scale-decrease :which-key "decrease font size")
"w+" '(text-scale-increase :which-key "increase font size")
"x" '(execute-extended-command :which-key "exec")
)
)
;; Vim keybindings
(use-package evil
:custom
(evil-want-keybinding nil "Disable default evil keybindings, since
evil-collection is a superset. See
https://github.com/emacs-evil/evil-collection/issues/60.")
(evil-want-integration t "Also needed for evil-collection")
:config
(defalias 'mu4e--main-action-str 'mu4e~main-action-str)
(defalias 'mu4e--main-view-queue 'mu4e~main-view-queue)
(evil-mode 1)
)
;; Vim keybindings in other packages
(use-package evil-collection
:after (evil)
:diminish (evil-collection-unimpaired-mode)
:config (evil-collection-init)
)
;; Easymotion-like jumping
(use-package avy
:custom
(avy-style 'pre "Insert decision characters instead of overwriting")
:general
(lmap
"jc" '(avy-goto-char :which-key "Jump to character")
"j2" '(avy-goto-char-2 :which-key "Jump to 2 character sequence")
"jl" '(avy-goto-line :which-key "Jump to line number")
"jw" '(avy-goto-word-0 :which-key "Jump to word")
"js" '(avy-goto-word-1 :which-key "Jump to word starting with character")
)
)
;; Better defaults that aren't defaults for some reason.
(use-package better-defaults
;; But don't enable ido-mode...
:config (ido-mode nil)
)
;; Handy completion-at-point-functions
(use-package cape
:hook
(prog-mode . chvp--setup-capfs)
(text-mode . chvp--setup-capfs)
:config
(defun chvp--setup-capfs ()
(add-hook 'completion-at-point-functions #'tempel-complete -50 t)
(add-hook 'completion-at-point-functions #'cape-file 10 t)
(add-hook 'completion-at-point-functions #'cape-dabbrev 15 t)
(add-hook 'completion-at-point-functions #'cape-line 20 t)
)
)
;; Autocomplete
(use-package corfu
:diminish (corfu-mode)
:custom
(corfu-cycle t "Enable cycling through completions")
(corfu-auto t "Show completion preview by default")
(corfu-auto-prefix 2 "Show completion after two characters")
(corfu-quit-no-match t "Space occurs too often in my normal workflow to not quit on no match")
;; Only confirm autocompletion with TAB
:bind (:map corfu-map ("RET" . nil))
:config
(global-corfu-mode)
)
(use-package corfu-popupinfo
:ensure nil ;; Part of corfu
:config (corfu-popupinfo-mode)
)
;; Prescient in corfu
(use-package corfu-prescient
:after (corfu prescient)
:custom (corfu-prescient-enable-filtering nil "Orderless handles filtering")
:config (corfu-prescient-mode 1)
)
;; Replacements for emacs built-ins that better integrate with `vertico'.
(use-package consult
:commands (consult-ripgrep)
:general
(lmap
"bb" '(consult-buffer :which-key "switch")
"fr" '(consult-recent-file :which-key "recent")
"ha" '(consult-apropos :which-key "apropos")
"ss" '(consult-line :which-key "search")
)
)
;; General emacs settings
(use-package emacs
:ensure nil ;; Not a real package, but a place to collect global settings
:demand t
:hook
;; Always display line numbers for text-based modes
((text-mode prog-mode) . display-line-numbers-mode)
;; Enable basic auto pairs. Maybe replace this with something more
;; advanced later? Look into configuring pairs for frequently used
;; major modes.
((text-mode prog-mode) . electric-pair-mode)
;; Always highlight the current line in text modes
((text-mode prog-mode) . hl-line-mode)
:custom
(use-short-answers t "Allow short answers on yes/no prompt")
(fill-column 80 "Fill at column 80 instead of 70")
(create-lockfiles nil "I'm the only user on my devices and use emacs as a daemon, so don't clutter with lockfiles")
(inhibit-startup-screen t "Don't show default startup screen")
(comp-deferred-compilation nil "Don't do native-comp at runtime")
(project-vc-merge-submodules nil "Don't consider submodules as the same project")
:config
;; Font configuration
(defun font-settings ()
"Setup font settings."
(when window-system (set-frame-font "Hack 9"))
(set-fontset-font t 'symbol "Noto Color Emoji")
(set-fontset-font t 'symbol "Symbola" nil 'append))
;; Make sure DISPLAY is set correctly in env.
(defun display-env-hack ()
"Hack DISPLAY env variable back into env."
(setenv "DISPLAY" ":0")
)
(if (daemonp)
(progn
(add-hook 'server-after-make-frame-hook #'font-settings)
(add-hook 'server-after-make-frame-hook #'display-env-hack))
(font-settings))
;; Always display column number in mode line
(column-number-mode)
)
;; Linting
(use-package flycheck
:custom (flycheck-checker-error-threshold 10000 "Set error threshold a lot higher")
:hook ((text-mode prog-mode) . flycheck-mode)
:diminish (flycheck-mode)
)
(use-package flycheck-languagetool
:hook (text-mode . flycheck-languagetool-setup)
:custom
(flycheck-languagetool-url "http://localhost:15151")
)
;; Annotations in selection interface
(use-package marginalia
:after (vertico)
:custom
(marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))
:config
(marginalia-mode)
:general
(minibuffer-local-map "M-a" 'marginalia-cycle)
)
;; Theming
(use-package catppuccin-theme
:custom (catppuccin-flavor 'latte)
:config (load-theme 'catppuccin :no-confirm)
)
(use-package no-littering
:custom
(user-emacs-directory (expand-file-name "~/.cache/emacs/") "Don't put files into .emacs.d")
:config
;; Also make sure auto-save files are saved out-of-tree
(setq auto-save-file-name-transforms `((".*" ,(no-littering-expand-var-file-name "auto-save/") t)))
)
;; Orderless filtering
(use-package orderless
:after (vertico)
:custom
(completion-styles '(orderless basic) "Use orderless for filtering")
(orderless-matching-styles '(orderless-literal orderless-initialism orderless-prefixes) "More matching styles for more flexible matching.")
)
;; Org
(use-package org
:hook
(org-insert-heading . set-creation-date-heading-property)
:custom
(org-directory "~/sync/Notes" "Store org journal in synced directory")
(org-default-notes-file (concat org-directory "/inbox.org") "Capture in inbox by default")
(org-agenda-files '("~/sync/Notes") "Let's say all files can contain events for now")
(org-todo-keywords '((sequence "TODO(t)" "NEXT(n)" "DOING(i)" "DONE(d)")) "Add next and doing states")
(org-log-into-drawer "LOGBOOK" "Log repeated task state changes into drawer named LOGBOOK")
:demand t
:config
(defun find-file-in-org-directory ()
(interactive)
(ido-find-file-in-dir org-directory)
)
(defun set-creation-date-heading-property () (org-set-property "CREATED" (format-time-string (org-time-stamp-format t t))))
(defun chvp--org-inbox-length ()
(length (org-map-entries t t (list (concat org-directory "/inbox.org"))))
)
(defun chvp--org-inbox-length-modeline ()
(let ((org-len (chvp--org-inbox-length)))
(if (<= org-len 0) nil
(concat "Org inbox: " (propertize (format "%d" org-len) 'face 'error)))))
(add-to-list 'global-mode-string '(:eval (chvp--org-inbox-length-modeline)))
:general
(lmap
:keymaps 'org-mode-map
"SPC a" '(org-archive-subtree :which-key "Archive subtree")
"SPC i" '(org-insert-heading :which-key "Insert heading")
"SPC <" '(org-promote-subtree :which-key "Decrease level")
"SPC >" '(org-demote-subtree :which-key "Increase level")
"SPC c" '(orc-clone-subtree-with-time-shift :which-key "Repeat subtree")
"SPC x" '(org-cut-subtree :which-key "Cut subtree")
"SPC p" '(org-paste-subtree :which-key "Paste subtree")
"SPC t" '(org-todo :which-key "Cycle todo state")
)
(lmap
"o a" '(org-agenda-list :which-key "Agenda")
"o t" '(org-todo-list :which-key "Todo list")
"o o" '(find-file-in-org-directory :which-key "Find org file")
)
)
;; Citations with citeproc in org
(use-package citeproc)
;; Sorting when filtering
(use-package prescient
:custom
(prescient-aggressive-file-save t "Be aggressive with saving prescient data since we're in daemon mode")
(prescient-history-length 100000 "Save a lot of history")
(prescient-frequency-threshold 0.00005 "Save a lot of history")
:config (prescient-persist-mode 1)
)
;; Rainbow delimiters
(use-package rainbow-delimiters
:hook (prog-mode . rainbow-delimiters-mode))
;; Side window with symbols or headline
(use-package sr-speedbar
:custom
(speedbar-use-images nil "Don't use images in speedbar")
:general
(lmap
"wb" '(sr-speedbar-toggle :which-key "Outline bar"))
)
;; Tempel (snippet expansion)
(use-package tempel
:demand t
:after cape
;; This is not very nice, but let's just assume that development machines have my nixos-config checked out
:custom (tempel-path "/home/charlotte/repos/nixos-config/modules/base/emacs/snippets/*.eld")
:general
(lmap
"t i" '(tempel-insert :which-key "Insert template")
)
)
;; List item selection interface
(use-package vertico
:custom (vertico-count 20 "Allow selector to be a bit higher")
:config (vertico-mode)
:diminish (vertico-mode)
)
;; Prescient integration in vertico
(use-package vertico-prescient
:after (vertico prescient)
:custom (vertico-prescient-enable-filtering nil "`orderless' manages the filtering part.")
:config (vertico-prescient-mode 1))
;; Show keybindings
(use-package which-key
:diminish (which-key-mode)
:config (which-key-mode)
)

View file

@ -0,0 +1,57 @@
{ config, lib, pkgs, ... }:
{
options.chvp.base.emacs = {
fullConfig = lib.mkOption {
readOnly = true;
default = builtins.readFile ./base-init.el + (lib.concatStringsSep "\n" config.chvp.base.emacs.extraConfig) + ''
(provide 'init)
;;; init.el ends here
'';
};
extraConfig = lib.mkOption {
default = [ ];
};
package = lib.mkOption {
readOnly = true;
default = pkgs.emacsWithPackagesFromUsePackage {
config = config.chvp.base.emacs.fullConfig;
package = pkgs.emacs-pgtk;
alwaysEnsure = true;
extraEmacsPackages = epkgs: lib.optional config.chvp.graphical.mail.enable epkgs.mu4e;
};
};
};
config = {
chvp.base.zfs.homeLinks = [
{ path = ".cache/emacs"; type = "cache"; }
];
services.languagetool = {
enable = true;
port = 15151;
settings.cacheSize = 10000;
};
home-manager.users.charlotte = { ... }: {
services.emacs = {
enable = true;
client.enable = true;
socketActivation.enable = true;
package = config.chvp.base.emacs.package;
};
home = {
packages = [
(pkgs.writeShellScriptBin "emacs" ''${config.chvp.base.emacs.package}/bin/emacsclient -c "$@"'')
(pkgs.writeShellScriptBin "emacsclient" ''${config.chvp.base.emacs.package}/bin/emacsclient "$@"'')
];
sessionVariables = { EDITOR = "emacs"; };
};
xdg.configFile = {
"emacs/init.el".text = config.chvp.base.emacs.fullConfig;
"emacs/early-init.el".source = ./early-init.el;
};
};
};
}

View file

@ -0,0 +1,32 @@
;;; early-init --- My emacs early init file
;;; Commentary:
;;; Code:
(defun hm/reduce-gc ()
"Reduce the frequency of garbage collection."
(setq gc-cons-threshold most-positive-fixnum
gc-cons-percentage 0.6))
(defun hm/restore-gc ()
"Restore the frequency of garbage collection."
(setq gc-cons-threshold 16777216
gc-cons-percentage 0.1))
;; Make GC more rare during init, while minibuffer is active, and
;; when shutting down. In the latter two cases we try doing the
;; reduction early in the hook.
(hm/reduce-gc)
(add-hook 'minibuffer-setup-hook #'hm/reduce-gc -50)
(add-hook 'kill-emacs-hook #'hm/reduce-gc -50)
;; But make it more regular after startup and after closing minibuffer.
(add-hook 'emacs-startup-hook #'hm/restore-gc)
(add-hook 'minibuffer-exit-hook #'hm/restore-gc)
;; Nix manages our packages
(setq package-enable-at-startup nil)
;; Avoid expensive frame resizing. Inspired by Doom Emacs.
(setq frame-inhibit-implied-resize t)
(provide 'early-init)
;;; early-init.el ends here

View file

@ -0,0 +1,6 @@
fundamental-mode
(today (format-time-string "%Y-%m-%d"))
(NOW (format-time-string "<%Y-%0m-%0d %a %H:%0M>"))
(yesterday (format-time-string "%Y-%0m-%0d" (time-subtract nil (* 24 60 60))))
(tomorrow (format-time-string "%Y-%0m-%0d" (time-add nil (* 24 60 60))))

View file

@ -0,0 +1,7 @@
java-mode
(sout "System.out.println(" r ");")
(serr "System.err.println(" r ");")
(class "public class " p " {" n> q n "}")
(main "public static void main(String[] args) {" n> q n "}")

View file

@ -0,0 +1,49 @@
mu4e-compose-mode
(mvg
"Met vriendelijke groeten" n
"Charlotte Van Petegem"
)
(spelletjesavond-schulden
"Dag " p n n
"Na de spelletjesavond van vrijdag zou je mij € " q " moeten. Kan je dat overschrijven op BE08 8908 0409 9113? Alvast bedankt!" n n
"Groetjes" n
"Charlotte"
)
(teacher-reply
"Dag " q n n
r n
"Welkom op Dodona! Zou je het volgende formulier kunnen invullen?" n n
"https://dodona.ugent.be/rights_requests/new/" n n
"Zo hebben we meteen alle info die we nodig hebben om je lesgeversrechten te geven op Dodona." n n
"Met vriendelijke groeten" n
"Charlotte Van Petegem"
)
(twist-exam-nag-en
"Hi " q n n
"A part of being a member of the TWIST department is contributing to exam supervision. Since the department board meeting of september 2022 it was decided to ask a bit more of people who don't contribute or contribute less to education during the semester, to allow the people who have to mark a lot of exams to concentrate on that. You are expected to do 3 supervisions, but as far as I can see that is not the case yet. Can I ask that you register for a number of exam supervisions? You can do so via the following spreadsheet: https://sharepoint.ugent.be/teams/WE02/_layouts/15/WopiFrame2.aspx?sourcedoc=%7BAA66A430-5240-4002-9D47-F6D06CF819AA%7D&file=Examentoezichten.xlsx&action=default&IsList=1&ListId=%7BA2BEB5ED-0C41-4A4B-ADCA-8F7CD56C2BBB%7D&ListItemId=2 (see also the email that was sent to the department mailing list at the end of the semester)." n n
"If you received this mail in error, please let me know, then I won't bother you in the future." n n
"Kind regards" n
"Charlotte Van Petegem"
)
(twist-exam-nag-nl
"Dag " q n n
"Een deel van lid zijn van de TWIST vakgroep is het bijdragen aan de toezichten bij de examens. Sinds de vakgroepraad van september 2022 is er ook besloten om iets meer te vragen van mensen die minder (of niet) aan onderwijs bijdragen om de mensen met veel verbeterwerk toe te laten om zich daarop te kunnen concentreren. Jij wordt verwacht 3 toezichten te doen, maar voor zover ik kan zien is dat nog niet het geval. Mag ik vragen van je te registreren voor een aantal examentoezichten? Dat kan via de volgende spreadsheet: https://sharepoint.ugent.be/teams/WE02/_layouts/15/WopiFrame2.aspx?sourcedoc=%7BAA66A430-5240-4002-9D47-F6D06CF819AA%7D&file=Examentoezichten.xlsx&action=default&IsList=1&ListId=%7BA2BEB5ED-0C41-4A4B-ADCA-8F7CD56C2BBB%7D&ListItemId=2 (zie ook de mail die op het einde van het semester naar de mailinglijst van de vakgroep werd verstuurd)?" n
"Als je deze mail foutief gekregen hebt, laat het mij dan zeker weten, dan val ik je in de toekomst niet meer lastig." n n
"Alvast bedankt" n
"Met vriendelijke groeten" n
"Charlotte Van Petegem"
)
(rode-kruis-vrijwilliger-cursussen
"Dag " p n n
"Je wil vrijwilliger worden bij het Rode Kruis. Super! Daarom krijg je bij het inschrijven voor de cursussen ook wat voorrang, zodat je je zeker kan inschrijven. Dit zijn de opties:" n n
q n n
"Kan je een seintje geven wanneer je dit gedaan hebt (of moest het niet passen)? Dan kan ik daarna de mail naar het grote publiek sturen om in te schrijven." n n
"Met vriendelijke groeten" n
"Charlotte Van Petegem"
)

View file

@ -0,0 +1,14 @@
python-mode
(class "class " p ":"
n> "\"\"\""
n> p
n> "\"\"\""
n> "def __init__(self" (p ", *args, **kwargs") "):"
n> "\"\"\""
n> p
n> "\"\"\""
n> q
)
(def "def " p "(" (p "*args, **kwargs") "):" n> "\"\"\"" n> p n> "\"\"\"" n> q)

View file

@ -0,0 +1,21 @@
{ config, lib, pkgs, ... }:
{
programs.msmtp = {
enable = lib.mkDefault true;
accounts.default = {
auth = true;
from = "webmaster@vanpetegem.me";
host = "mail.vanpetegem.me";
passwordeval = ''${pkgs.coreutils}/bin/cat ${config.age.secrets."passwords/services/ssmtp-pass".path}'';
port = 465;
tls = true;
tls_starttls = false;
tls_trust_file = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
user = "webmaster@vanpetegem.me";
};
setSendmail = true;
};
age.secrets."passwords/services/ssmtp-pass".file = ../../../../secrets/passwords/services/ssmtp-pass.age;
}

View file

@ -0,0 +1,9 @@
{ ... }:
{
imports = [
./mobile.nix
./ovh.nix
./wireguard.nix
];
}

View file

@ -0,0 +1,93 @@
{ config, lib, pkgs, ... }:
{
options.chvp.base.network.mobile = {
enable = lib.mkOption {
default = false;
example = true;
};
wireless-interface = lib.mkOption {
type = lib.types.str;
example = "wlp2s0";
};
wired-interfaces = lib.mkOption {
example = { "enp0s29f0u1u2" = { macAddress = "10:65:30:85:bb:18"; }; };
};
};
config = with config.chvp.base.network.mobile; lib.mkIf enable {
environment.systemPackages = [ pkgs.wpa_supplicant_gui ];
users.users.charlotte.extraGroups = [ "network" ];
users.groups.network = { };
networking = {
useDHCP = false;
wireless = {
enable = true;
interfaces = [ wireless-interface ];
environmentFile = config.age.secrets."passwords/networks.age".path;
userControlled = {
enable = true;
group = "network";
};
networks = {
"Public Universal Friend".psk = "@PSK_PUF@";
AndroidAP.psk = "@PSK_AndroidAP@";
draadloosnw.psk = "@PSK_draadloosnw@";
werknet.psk = "@PSK_werknet@";
Secorima.psk = "@PSK_Secorima@";
"Mediaraven Guest".psk = "@PSK_Mediaraven@";
"down".psk = "@PSK_down@";
"Zeus WPI" = {
psk = "@PSK_Zeus@";
hidden = true;
};
"Zeus Event 5G".psk = "@PSK_Zeus@";
"Rode Kruis-Gent (internet)".psk = "@PSK_RKG@";
"CityStayNet".psk = "@PSK_CityStayNet@";
eduroam = {
authProtocols = [ "WPA-EAP" ];
auth = ''
eap=PEAP
identity="@EDUROAM_USER@"
password="@EDUROAM_PASS@"
'';
extraConfig = ''
phase1="peaplabel=0"
phase2="auth=MSCHAPV2"
group=CCMP TKIP
ca_cert="${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
altsubject_match="DNS:radius.ugent.be"
'';
};
"GUK-huis".psk = "@PSK_GUKhuis@";
"DasNetwerk".psk = "@PSK_DasNetwerk@";
};
};
};
systemd.network = {
enable = true;
networks = {
"${wireless-interface}" = {
enable = true;
DHCP = "yes";
matchConfig = { Name = wireless-interface; };
dhcpV4Config = { RouteMetric = 20; };
ipv6AcceptRAConfig = { RouteMetric = 20; };
};
} // lib.mapAttrs
(name: attrs: {
enable = true;
DHCP = "yes";
matchConfig = { Name = name; };
dhcpV4Config = { RouteMetric = 10; };
ipv6AcceptRAConfig = { RouteMetric = 10; };
} // attrs)
wired-interfaces;
wait-online.anyInterface = true;
};
age.secrets."passwords/networks.age" = {
file = ../../../../secrets/passwords/networks.age;
};
};
}

View file

@ -0,0 +1,63 @@
{ config, lib, ... }:
{
options.chvp.base.network.ovh = {
enable = lib.mkOption {
default = false;
example = true;
};
publicIPV4 = lib.mkOption {
example = {
ip = "1.2.3.4";
gateway = "1.2.3.254";
};
};
publicIPV6 = lib.mkOption {
example = {
ip = "1:2:3:4::";
gateway = "1:2:3:ff:ff:ff:ff:ff";
};
};
internalIPV4 = lib.mkOption {
example = "192.168.0.1";
};
};
config = lib.mkIf config.chvp.base.network.ovh.enable {
networking.useDHCP = false;
systemd.network = {
enable = true;
networks = with config.chvp.base.network.ovh; {
eno3 = {
enable = true;
matchConfig = { Name = "eno3"; };
address = [
"${publicIPV4.ip}/24"
"${publicIPV6.ip}/64"
];
gateway = [ publicIPV4.gateway ];
routes = [
{
Gateway = publicIPV6.gateway;
GatewayOnLink = true;
}
];
dns = [
"1.1.1.1"
"1.0.0.1"
"2606:4700:4700::1111"
"2606:4700:4700::1001"
];
};
eno4 = {
enable = true;
matchConfig = { Name = "eno4"; };
address = [ "${internalIPV4}/16" ];
routes = [
{ Destination = "${internalIPV4}/16"; }
];
};
};
};
};
}

View file

@ -0,0 +1,141 @@
{ config, lib, pkgs, ... }:
let
data = {
fairphone = {
pubkey = "mHAq+2AP1EZdlSZIxA8UCret8EStrR3nEIU2x6NVETE=";
ip = "10.240.0.4";
};
kholinar = {
pubkey = "oRA22ymFeNQBeRx6Jyd6Gd8EOUpAv9QSFkGs+Br7yEk=";
privkeyFile = config.age.secrets."files/wireguard/kholinar.privkey".path;
ip = "10.240.0.3";
};
lasting-integrity = {
pubkey = "mid3XfCY2jaNK0J6C9ltFLAbxL0IApwMw9K1Z+PU8C0=";
privkeyFile = config.age.secrets."files/wireguard/lasting-integrity.privkey".path;
ip = "10.240.0.1";
};
urithiru = {
pubkey = "f4bnm/qNhMW5iXdQcBMmP8IUN6n+pDS15Ikct7QPr0E=";
privkeyFile = config.age.secrets."files/wireguard/urithiru.privkey".path;
ip = "10.240.0.2";
};
};
subnet = "10.240.0.0/24";
pskFile = config.age.secrets."files/wireguard/psk".path;
in
{
options.chvp.base.network.wireguard = {
server = lib.mkOption {
default = false;
example = true;
};
onCorporate = lib.mkOption {
default = false;
example = true;
};
};
config = {
networking.firewall = {
allowedUDPPorts = lib.optional config.chvp.base.network.wireguard.server 51820;
allowedTCPPorts = lib.optional config.chvp.base.network.wireguard.server 8080;
trustedInterfaces = [ "wg0" ];
};
boot.kernel.sysctl = lib.mkIf config.chvp.base.network.wireguard.server { "net.ipv4.ip_forward" = 1; };
services.unbound = lib.mkIf config.chvp.base.network.wireguard.server {
enable = true;
resolveLocalQueries = true;
settings = {
server = {
interface = [ "10.240.0.1" "127.0.0.1" "::1" ];
access-control = [
"127.0.0.0/8 allow"
"10.240.0.0/24 allow"
];
private-domain = "internal";
domain-insecure = "internal";
local-zone = builtins.map (name: ''"${name}.internal" redirect'') (builtins.attrNames data);
local-data = builtins.map (name: ''"${name}.internal IN A ${data.${name}.ip}"'') (builtins.attrNames data);
};
forward-zone = {
name = ''"."'';
forward-addr = [
"1.1.1.1@853"
"1.0.0.1@853"
"2606:4700:4700::1111@853"
"2606:4700:4700::1001@853"
];
forward-tls-upstream = "yes";
};
};
};
systemd = {
network = {
netdevs.wg0 = {
enable = true;
netdevConfig = {
Name = "wg0";
Kind = "wireguard";
MTUBytes = "1342";
};
wireguardConfig =
if config.chvp.base.network.wireguard.server then {
PrivateKeyFile = data.${config.networking.hostName}.privkeyFile;
ListenPort = 51820;
} else {
PrivateKeyFile = data.${config.networking.hostName}.privkeyFile;
};
wireguardPeers =
if config.chvp.base.network.wireguard.server then
(builtins.map
(name: {
PublicKey = data.${name}.pubkey;
AllowedIPs = "${data.${name}.ip}/32";
PresharedKeyFile = pskFile;
})
(builtins.filter (name: name != config.networking.hostName) (builtins.attrNames data)))
else
([{
PublicKey = data.lasting-integrity.pubkey;
AllowedIPs = subnet;
Endpoint =
if config.chvp.base.network.wireguard.onCorporate
then "127.0.0.1:51820"
else "lasting-integrity.vanpetegem.me:51820";
PresharedKeyFile = pskFile;
PersistentKeepalive = 25;
}]);
};
networks.wg0 = {
enable = true;
name = "wg0";
address = [ "${data.${config.networking.hostName}.ip}/32" ];
domains = [ "internal" ];
dns = [ data.lasting-integrity.ip ];
linkConfig.MTUBytes = "1342";
routes = [
(
if config.chvp.base.network.wireguard.server then {
Gateway = "${data.${config.networking.hostName}.ip}";
Destination = subnet;
} else {
Gateway = "${data.lasting-integrity.ip}";
Destination = subnet;
GatewayOnLink = true;
}
)
];
};
};
};
age.secrets."files/wireguard/psk" = {
file = ../../../../secrets/files/wireguard/psk.age;
owner = "systemd-network";
};
age.secrets."files/wireguard/${config.networking.hostName}.privkey" = {
file = ../../../../secrets/files/wireguard + "/${config.networking.hostName}.privkey.age";
owner = "systemd-network";
};
};
}

View file

@ -0,0 +1,95 @@
{ config, lib, pkgs, ... }:
let
baseDirenv = {
programs.direnv = {
enable = true;
enableZshIntegration = true;
nix-direnv.enable = true;
};
};
baseNixIndex = {
programs.command-not-found.enable = false;
programs.nix-index = {
enable = true;
package = config.programs.nix-index.package;
};
};
in
{
options.chvp.base.nix = {
enableDirenv = lib.mkOption {
default = true;
example = false;
};
slowGc = lib.mkOption {
default = false;
example = true;
};
# Used in /flake.nix, since we have to use it at nixpkgs import time
unfreePackages = lib.mkOption {
default = [ ];
example = [ "teams" ];
};
};
config = {
programs.command-not-found.enable = false;
chvp.base = {
emacs.extraConfig = [
''
;; Nix syntax support
(use-package nix-mode
:mode "\\.nix\\'"
)
''
] ++ lib.optional config.chvp.base.nix.enableDirenv ''
;; Direnv integration in emacs.
(use-package direnv
:config (direnv-mode)
)
'';
zfs = {
homeLinks =
(lib.optional config.chvp.base.nix.enableDirenv { path = ".local/share/direnv"; type = "cache"; });
systemLinks =
(lib.optional config.chvp.base.nix.enableDirenv { path = "/root/.local/share/direnv"; type = "cache"; });
};
};
nix = {
gc = {
automatic = true;
dates = if config.chvp.base.nix.slowGc then "daily" else "hourly";
options = "--delete-older-than 7d";
};
settings = {
auto-optimise-store = true;
substituters = [
"https://cache.nixos.org"
"https://accentor.cachix.org"
"https://chvp.cachix.org"
"https://lanzaboote.cachix.org"
"https://nix-community.cachix.org"
];
trusted-public-keys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
"accentor.cachix.org-1:QP+oJwzmeq5Fsyp4Vk501UgUSbl5VIna/ard/XOePH8="
"chvp.cachix.org-1:eIG26KkeA+R3tCpvmaayA9i3KVVL06G+qB5ci4dHBT4="
"lanzaboote.cachix.org-1:Nt9//zGmqkg1k5iu+B3bkj3OmHKjSw9pvf3faffLLNk="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
trusted-users = [ "@wheel" ];
};
extraOptions = lib.mkIf config.chvp.base.nix.enableDirenv ''
keep-outputs = true
keep-derivations = true
'';
};
home-manager.users.charlotte = { ... }:
lib.recursiveUpdate
(lib.optionalAttrs config.chvp.base.nix.enableDirenv baseDirenv)
baseNixIndex;
home-manager.users.root = { ... }: baseNixIndex;
};
}

View file

@ -0,0 +1,15 @@
{ config, lib, pkgs, ... }:
let
phone-push = pkgs.writeShellScriptBin "phone-push" ''
curl $(cat ${config.age.secrets."files/services/phone-push-url".path}) -d "$(hostname): $@"
'';
in
{
environment.systemPackages = [ phone-push ];
age.secrets."files/services/phone-push-url" = {
file = ../../../../secrets/files/services/phone-push-url.age;
owner = "charlotte";
};
}

View file

@ -0,0 +1,17 @@
{ config, lib, pkgs, ... }:
{
environment.systemPackages = [ pkgs.smartmontools ];
services.smartd = {
enable = true;
autodetect = true;
notifications = {
mail = {
enable = true;
sender = "${config.networking.hostName}@vanpetegem.me";
recipient = "webmaster@vanpetegem.me";
};
wall.enable = false;
};
};
}

View file

@ -0,0 +1,44 @@
{ config, lib, pkgs, ... }:
let
ssh = pkgs.symlinkJoin {
name = "ssh";
paths = [
(
pkgs.writeShellScriptBin "ssh" ''
export TERM=xterm-256color
${pkgs.openssh}/bin/ssh "$@"
''
)
pkgs.openssh
];
};
base = home: user: {
programs.ssh = {
enable = true;
package = if config.chvp.graphical.enable then ssh else pkgs.openssh;
compression = true;
controlMaster = "auto";
controlPersist = "10m";
hashKnownHosts = true;
userKnownHostsFile = "${config.chvp.cachePrefix}${home}/.ssh/known_hosts";
serverAliveInterval = 10;
extraOptionOverrides = {
Include = config.age.secrets."files/programs/ssh/host_configuration_${user}".path;
IdentityFile = "${config.chvp.dataPrefix}${home}/.ssh/id_ed25519";
HostKeyAlgorithms = "ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa";
};
};
home.packages = lib.mkIf config.chvp.graphical.enable [ pkgs.sshfs ];
};
in
{
home-manager.users.root = { ... }: (base "/root" "root");
home-manager.users.charlotte = { ... }: (base "/home/charlotte" "charlotte");
age.secrets."files/programs/ssh/host_configuration_charlotte" = {
file = ../../../../secrets/files/programs/ssh/host_configuration.age;
owner = "charlotte";
};
age.secrets."files/programs/ssh/host_configuration_root" = {
file = ../../../../secrets/files/programs/ssh/host_configuration.age;
};
}

View file

@ -0,0 +1,31 @@
{ config, lib, ... }:
{
chvp.base.zfs = {
ensureSystemExists = [ "${config.chvp.dataPrefix}/etc/ssh" ];
ensureHomeExists = [ ".ssh" ];
};
services.openssh = {
enable = true;
hostKeys = [
{ bits = 4096; path = "${config.chvp.dataPrefix}/etc/ssh/ssh_host_rsa_key"; type = "rsa"; }
{ path = "${config.chvp.dataPrefix}/etc/ssh/ssh_host_ed25519_key"; type = "ed25519"; }
];
settings = {
PasswordAuthentication = false;
PermitRootLogin = "prohibit-password";
};
};
age.secrets."authorized_keys/root" = {
file = ../../../../secrets/authorized_keys/root.age;
path = "/root/.ssh/authorized_keys";
symlink = false;
};
age.secrets."authorized_keys/charlotte" = {
file = ../../../../secrets/authorized_keys/charlotte.age;
owner = "charlotte";
path = "/home/charlotte/.ssh/authorized_keys";
symlink = false;
};
}

View file

@ -0,0 +1,50 @@
{ config, lib, pkgs, ... }:
{
services.telegraf = {
enable = true;
extraConfig = {
agent = {
interval = "10s";
round_interval = true;
metric_batch_size = 1000;
metric_buffer_limit = 10000;
collection_jitter = "0s";
flush_interval = "10s";
flush_jitter = "0s";
precision = "0s";
omit_hostname = false;
};
outputs.influxdb_v2 = {
urls = [ "https://stats.chvp.be:8086" ];
token = "$TOKEN";
organization = "default";
bucket = "default";
};
inputs = {
cpu = {
percpu = true;
totalcpu = true;
collect_cpu_time = false;
report_active = false;
};
diskio = { };
exec = {
commands = [ "${pkgs.zfs}/libexec/zfs/zpool_influxdb" ];
timeout = "5s";
data_format = "influx";
};
kernel = { };
mem = { };
processes = { };
swap = { };
system = { };
};
};
environmentFiles = [ config.age.secrets."passwords/services/telegraf-env".path ];
};
age.secrets."passwords/services/telegraf-env" = {
file = ../../../../secrets/passwords/services/telegraf-env.age;
owner = "telegraf";
};
}

View file

@ -0,0 +1,22 @@
{ ... }:
let
base = {
programs.tmux = {
enable = true;
clock24 = true;
extraConfig = ''
bind q kill-session
bind v run-shell "tmux setw main-pane-width $(($(tmux display -p '#{window_width}') * 70 / 100)); tmux select-layout main-vertical"
bind h run-shell "tmux setw main-pane-height $(($(tmux display -p '#{window_height}') * 70 / 100)); tmux select-layout main-horizontal"
set -g default-terminal "screen-256color"
set -sg escape-time 10
'';
keyMode = "vi";
};
};
in
{
home-manager.users.charlotte = { ... }: base;
home-manager.users.root = { ... }: base;
}

View file

@ -0,0 +1,198 @@
{ config, lib, ... }:
{
options.chvp.base.zfs = {
encrypted = lib.mkOption {
default = false;
example = true;
};
systemLinks = lib.mkOption {
default = [ ];
example = [
{ path = "/var/lib/docker"; type = "cache"; }
{ path = "/var/lib/docker/volumes"; type = "data"; }
];
};
homeLinks = lib.mkOption {
default = [ ];
example = [
{ path = ".config/syncthing"; type = "data"; }
{ path = ".cache/nix-index"; type = "cache"; }
];
};
ensureSystemExists = lib.mkOption {
default = [ ];
example = [ "/data/etc/ssh" ];
};
ensureHomeExists = lib.mkOption {
default = [ ];
example = [ ".ssh" ];
};
backups = lib.mkOption {
default = [ ];
example = [{
path = "rpool/safe/data";
remotePath = "zdata/recv/<hostname>/safe/data";
fast = false;
location = "lasting-integrity.vanpetegem.me";
}];
};
rootDataset = lib.mkOption {
example = "rpool/local/root";
};
};
config = {
chvp.dataPrefix = lib.mkDefault "/data";
chvp.cachePrefix = lib.mkDefault "/cache";
boot = {
supportedFilesystems = [ "zfs" ];
zfs.requestEncryptionCredentials = config.chvp.base.zfs.encrypted;
initrd.postDeviceCommands = lib.mkAfter ''
zfs rollback -r ${config.chvp.base.zfs.rootDataset}@blank
'';
};
services = {
znapzend = {
enable = config.chvp.base.zfs.backups != [ ];
pure = true;
autoCreation = true;
zetup = builtins.listToAttrs
(map
(elem: {
name = elem.path;
value = {
enable = true;
plan =
if elem.fast then
"1hour=>15min,1day=>1hour,1week=>1day,4week=>1week" else
"1day=>1hour,1week=>1day,4week=>1week,1year=>1month,10year=>6month";
timestampFormat = "%Y-%m-%d--%H%M%SZ";
destinations."${elem.location}" = {
plan =
if elem.fast then
"1day=>1hour,1week=>1day,4week=>1week,1year=>4week,10year=>1year" else
"1day=>1hour,1week=>1day,4week=>1week,1year=>1month,10year=>6month";
host = "${elem.location}";
dataset = elem.remotePath;
};
};
})
config.chvp.base.zfs.backups);
};
zfs = {
autoScrub.enable = true;
trim.enable = true;
};
};
system.activationScripts =
let
ensureSystemExistsScript = lib.concatStringsSep "\n" (map (path: ''mkdir -p "${path}"'') config.chvp.base.zfs.ensureSystemExists);
ensureHomeExistsScript = lib.concatStringsSep "\n" (map (path: ''mkdir -p "/home/charlotte/${path}"'') config.chvp.base.zfs.ensureHomeExists);
ensureHomePermissionsScript = lib.concatStringsSep "\n" (map (path: ''chown charlotte:users /home/charlotte/${path}'') config.chvp.base.zfs.ensureHomeExists);
in
{
ensureSystemPathsExist = {
text = ensureSystemExistsScript;
deps = [ "agenixNewGeneration" ];
};
ensureHomePathsExist = {
text = ''
mkdir -p /home/charlotte/
${ensureHomeExistsScript}
'';
};
agenixInstall.deps = [ "ensureSystemPathsExist" "ensureHomePathsExist" ];
ensureHomePermissionsScript = {
text = ''
chown charlotte:users /home/charlotte
${ensureHomePermissionsScript}
'';
deps = [ "agenixInstall" "users" "groups" ];
};
};
systemd.services =
let
makeLinkScript = config: lib.concatStringsSep "\n" (map (location: ''mkdir -p "${location.path}"'') config);
systemLinksScript = makeLinkScript config.chvp.base.zfs.systemLinks;
homeLinksScript = makeLinkScript config.chvp.base.zfs.homeLinks;
in
{
make-system-links-destinations = {
script = systemLinksScript;
after = [ "local-fs.target" ];
wants = [ "local-fs.target" ];
before = [ "shutdown.target" "sysinit.target" ];
conflicts = [ "shutdown.target" ];
wantedBy = [ "sysinit.target" ];
serviceConfig = {
RemainAfterExit = "yes";
Type = "oneshot";
UMask = "0077";
};
unitConfig = {
DefaultDependencies = "no";
};
};
make-home-links-destinations = {
script = homeLinksScript;
after = [ "local-fs.target" "make-system-links-destinations.service" ];
wants = [ "local-fs.target" "make-system-links-destinations.service" ];
before = [ "shutdown.target" "sysinit.target" ];
conflicts = [ "shutdown.target" ];
wantedBy = [ "sysinit.target" ];
serviceConfig = {
RemainAfterExit = "yes";
Type = "oneshot";
User = "charlotte";
Group = "users";
UMask = "0077";
WorkingDirectory = "/home/charlotte";
};
unitConfig = {
DefaultDependencies = "no";
};
};
};
systemd.mounts =
(map
(location: {
what = "/${location.type}${location.path}";
where = "${location.path}";
type = "none";
options = "bind";
after = [ "local-fs.target" "make-system-links-destinations.service" ];
wants = [ "local-fs.target" "make-system-links-destinations.service" ];
before = [ "umount.target" "sysinit.target" ];
conflicts = [ "umount.target" ];
wantedBy = [ "sysinit.target" ];
unitConfig = {
DefaultDependencies = "no";
};
})
config.chvp.base.zfs.systemLinks) ++
(map
(location: {
what = "/${location.type}/home/charlotte/${location.path}";
where = "/home/charlotte/${location.path}";
type = "none";
options = "bind";
after = [ "local-fs.target" "make-home-links-destinations.service" ];
wants = [ "local-fs.target" "make-home-links-destinations.service" ];
before = [ "umount.target" "sysinit.target" ];
conflicts = [ "umount.target" ];
wantedBy = [ "sysinit.target" ];
unitConfig = {
DefaultDependencies = "no";
};
})
config.chvp.base.zfs.homeLinks);
};
}

View file

@ -0,0 +1,127 @@
{ config, lib, pkgs, ... }:
let
base = (home: {
home.packages = [ pkgs.autojump ];
programs.zsh = {
enable = true;
autosuggestion.enable = true;
syntaxHighlighting.enable = true;
autocd = true;
dotDir = ".config/zsh";
history = {
expireDuplicatesFirst = true;
path = "${config.chvp.cachePrefix}${home}/.local/share/zsh/history";
};
initExtra = ''
nshell() {
local -a drvs
for attr in "$@"; do
drvs+=(nixpkgs#$attr)
done
local paths="$(nix build --no-link --print-out-paths $drvs)"
for p in $paths; do
export PATH="$p/bin:$PATH"
done
}
nrun() {
local drv="$1"
shift 1
nix run nixpkgs#$drv $@
}
nsrun() {
local drv="$1"
shift 1
nix shell nixpkgs#$drv -c $@
}
lightmode() {
printf "\033]10;rgb:4c/4f/69\007"
printf "\033]11;rgb:ef/f1/f5\007"
printf "\033]17;rgb:cc/d0/da\007"
printf "\033]19;rgb:4c/4f/69\007"
printf "\033]4;0;rgb:5c/5f/77\007"
printf "\033]4;1;rgb:d2/0f/39\007"
printf "\033]4;2;rgb:40/a0/2b\007"
printf "\033]4;3;rgb:df/8e/1d\007"
printf "\033]4;4;rgb:1e/66/f5\007"
printf "\033]4;5;rgb:ea/76/cb\007"
printf "\033]4;6;rgb:17/92/99\007"
printf "\033]4;7;rgb:ac/b0/be\007"
printf "\033]4;8;rgb:6c/6f/85\007"
printf "\033]4;9;rgb:d2/0f/39\007"
printf "\033]4;10;rgb:40/a0/2b\007"
printf "\033]4;11;rgb:df/8e/1d\007"
printf "\033]4;12;rgb:1e/66/f5\007"
printf "\033]4;13;rgb:ea/76/cb\007"
printf "\033]4;14;rgb:17/92/99\007"
printf "\033]4;15;rgb:bc/c0/cc\007"
}
darkmode() {
printf "\033]10;rgb:c6/d0/f5\007"
printf "\033]11;rgb:30/34/46\007"
printf "\033]17;rgb:41/45/59\007"
printf "\033]19;rgb:c6/d0/f5\007"
printf "\033]4;0;rgb:51/57/6d\007"
printf "\033]4;1;rgb:e7/82/84\007"
printf "\033]4;2;rgb:a6/d1/89\007"
printf "\033]4;3;rgb:e5/c8/90\007"
printf "\033]4;4;rgb:8c/aa/ee\007"
printf "\033]4;5;rgb:f4/b8/e4\007"
printf "\033]4;6;rgb:81/c8/be\007"
printf "\033]4;7;rgb:b5/bf/e2\007"
printf "\033]4;8;rgb:62/68/80\007"
printf "\033]4;9;rgb:e7/82/84\007"
printf "\033]4;10;rgb:a6/d1/89\007"
printf "\033]4;11;rgb:e5/c8/90\007"
printf "\033]4;12;rgb:8c/aa/ee\007"
printf "\033]4;13;rgb:f4/b8/e4\007"
printf "\033]4;14;rgb:81/c8/be\007"
printf "\033]4;15;rgb:a5/ad/ce\007"
}
TRAPUSR1() {
lightmode
}
TRAPUSR2() {
darkmode
}
if type darkman >/dev/null
then
if [ "$(darkman get)" = "dark" ]
then
darkmode
else
lightmode
fi
fi
'';
sessionVariables = { DEFAULT_USER = "charlotte"; };
oh-my-zsh = {
enable = true;
plugins = [
"autojump"
"common-aliases"
"extract"
"history-substring-search"
"git"
"systemd"
"tmux"
];
theme = "robbyrussell";
};
};
});
in
{
chvp.base.zfs.systemLinks = [{ path = "/root/.local/share/autojump"; type = "cache"; }];
chvp.base.zfs.homeLinks = [{ path = ".local/share/autojump"; type = "cache"; }];
home-manager.users.charlotte = { ... }: (base "/home/charlotte");
home-manager.users.root = { ... }: (base "/root");
programs.zsh.enable = true;
}

12
modules/nixos/default.nix Normal file
View file

@ -0,0 +1,12 @@
{ ... }:
{
imports = [
./base
./development
./games
./graphical
./programs
./services
];
}

View file

@ -0,0 +1,27 @@
{ config, lib, ... }:
{
options.chvp.development.android.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.development.android.enable {
chvp.base = {
emacs.extraConfig = [
''
;; Groovy (gradle) language support
(use-package groovy-mode
:mode "\\.gradle\\'")
;; Kotlin language support
(use-package kotlin-mode
:mode "\\.kt\\'")
''
];
zfs.homeLinks = [{ path = ".android"; type = "cache"; }];
};
programs.adb.enable = true;
users.users.charlotte.extraGroups = [ "adbusers" ];
};
}

View file

@ -0,0 +1,184 @@
{ config, lib, pkgs, ... }:
{
imports = [
./android
./docker
./git
];
options.chvp.development.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.development.enable {
chvp = {
base.emacs.extraConfig = [
''
;; Editorconfig
(use-package editorconfig
:diminish (editorconfig-mode)
:config
(editorconfig-mode 1)
)
;; R syntax support
(use-package ess
:init
(load "ess-autoloads")
:mode ("\\.r\\'" . ess-r-mode)
:mode ("\\.R\\'" . ess-r-mode)
)
;; Language server support
(use-package eglot
:demand t
:general
(lmap
:keymaps '(prog-mode-map vue-mode-map)
"SPC s" '(eglot :which-key "Add buffer to eglot")
"SPC f" '(eglot-format :which-key "Format region")
"SPC F" '(eglot-format :which-key "Format buffer")
"SPC r" '(eglot-rename :which-key "Rename symbol")
"SPC a" '(eglot-code-actions :which-key "Relevant local actions")
"SPC n" '(flymake-goto-next-error :which-key "Next error")
"SPC p" '(flymake-goto-prev-error :which-key "Previous error")
)
:hook (eglot-managed-mode . chvp--eglot-capf)
:config
(advice-add 'eglot-completion-at-point :around #'cape-wrap-buster)
(defun chvp--eglot-capf ()
(setq-local completion-at-point-functions
(list (cape-super-capf
#'eglot-completion-at-point
#'tempel-complete)
#'cape-file
#'dabbrev-capf
#'cape-line)))
)
;; Forth syntax support
(use-package forth-mode
:mode ("\\.fs\\'" . forth-mode)
:mode ("\\.fb\\'" . forth-block-mode)
)
;; Markdown syntax support
(use-package markdown-mode
:commands (markdown-mode gfm-mode)
:mode ("README\\.md\\'" . gfm-mode)
:mode ("\\.md\\'" . markdown-mode)
:mode ("\\.markdown\\'" . markdown-mode)
)
;; Haskell language support
(use-package haskell-mode
:mode "\\.hs\\'"
:config
(require 'haskell-doc)
)
;; Folding
(use-package origami
:hook (prog-mode . origami-mode)
)
;; Python syntax support
(use-package python-mode
:mode "\\.py\\'"
)
;; Ruby language support
(use-package ruby-mode
:ensure nil ;; Included with emacs
:mode "\\.\\(?:cap\\|gemspec\\|irbrc\\|gemrc\\|rake\\|rb\\|ru\\|thor\\)\\'"
:mode "\\(?:Brewfile\\|Capfile\\|Gemfile\\(?:\\.[a-zA-Z0-9._-]+\\)?\\|[rR]akefile\\)\\'"
:custom
(ruby-insert-encoding-magic-comment nil "Don't insert encoding magic comment")
)
;; Rust language support
(use-package rust-mode
:mode "\\.rs\\'"
)
;; TypeScript language support
(use-package typescript-mode
:mode "\\.ts\\'"
)
;; Vue language support
(use-package vue-mode
:mode "\\.vue\\'"
:custom
(mmm-submode-decoration-level 0 "Don't color submodes differently")
:config
(defun vue-eglot-init-options ()
(let ((tsdk-path "${pkgs.typescript}/lib/node_modules/typescript/lib"))
`(:typescript (:tsdk ,tsdk-path
:languageFeatures (:completion
(:defaultTagNameCase "kebab"
:defaultAttrNameCase "kebab"
:getDocumentNameCasesRequest nil
:getDocumentSelectionRequest nil)
:diagnostics
(:getDocumentVersionRequest nil))
:documentFeatures (:documentFormatting
(:defaultPrintWidth 100
:getDocumentPrintWidthRequest nil)
:documentSymbol t
:documentColor t)))))
(add-to-list 'eglot-server-programs `(vue-mode . ("${pkgs.vue-language-server}/bin/vue-language-server" "--stdio" :initializationOptions ,(vue-eglot-init-options))))
(setq vue--front-tag-lang-regex
(concat "<%s" ; The tag name
"\\(?:" ; Zero of more of...
"\\(?:\\s-+\\w+=[\"'].*?[\"']\\)" ; Any optional key-value pairs like type="foo/bar"
"\\|\\(?:\\s-+scoped\\)" ; The optional "scoped" attribute
"\\|\\(?:\\s-+module\\)" ; The optional "module" attribute
"\\|\\(?:\\s-+setup\\)" ; The optional "setup" attribute
"\\)*"
"\\(?:\\s-+lang=[\"']%s[\"']\\)" ; The language specifier (required)
"\\(?:" ; Zero of more of...
"\\(?:\\s-+\\w+=[\"'].*?[\"']\\)" ; Any optional key-value pairs like type="foo/bar"
"\\|\\(?:\\s-+scoped\\)" ; The optional "scoped" attribute
"\\|\\(?:\\s-+module\\)" ; The optional "module" attribute
"\\|\\(?:\\s-+setup\\)" ; The optional "setup" attribute
"\\)*"
" *>\n")) ; The end of the tag
(setq vue--front-tag-regex
(concat "<%s" ; The tag name
"\\(?:" ; Zero of more of...
"\\(?:\\s-+" vue--not-lang-key "[\"'][^\"']*?[\"']\\)" ; Any optional key-value pairs like type="foo/bar".
;; ^ Disallow "lang" in k/v pairs to avoid matching regions with non-default languages
"\\|\\(?:\\s-+scoped\\)" ; The optional "scoped" attribute
"\\|\\(?:\\s-+module\\)" ; The optional "module" attribute
"\\|\\(?:\\s-+setup\\)" ; The optional "setup" attribute
"\\)*"
"\\s-*>\n")) ; The end of the tag
)
;; HTML (and HTML template) support
(use-package web-mode
:mode "\\.html\\'"
:mode "\\.html\\.erb\\'"
)
;; YAML syntax support
(use-package yaml-mode
:mode "\\.yml\\'"
:mode "\\.yaml\\'"
)
''
];
development = {
docker.enable = lib.mkDefault true;
git.enable = lib.mkDefault true;
};
};
users.users.charlotte.extraGroups = [ "dialout" "uucp" ];
boot.kernel.sysctl."fs.inotify.max_user_watches" = 524288;
};
}

View file

@ -0,0 +1,20 @@
{ config, lib, pkgs, ... }:
{
options.chvp.development.docker.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.development.docker.enable {
virtualisation.docker = {
enable = true;
extraOptions = "--data-root ${config.chvp.dataPrefix}/var/lib/docker";
storageDriver = "zfs";
};
environment.systemPackages = [ pkgs.docker-compose ];
users.users.charlotte.extraGroups = [ "docker" ];
};
}

View file

@ -0,0 +1,94 @@
{ config, lib, pkgs, ... }:
{
options.chvp.development.git = {
enable = lib.mkOption {
default = false;
example = true;
};
email = lib.mkOption {
type = lib.types.str;
default = "charlotte@vanpetegem.be";
example = "charlotte@vanpetegem.be";
description = ''
Default email set in global git config.
'';
};
};
config =
let
base = {
programs.git = {
enable = true;
lfs.enable = true;
extraConfig = {
branch.autosetuprebase = "always";
github.user = "chvp";
merge.conflictStyle = "diff3";
pull.rebase = true;
rerere.enabled = true;
tag.gpgSign = true;
};
ignores = [
".data"
".direnv"
".envrc"
".dir-locals.el"
];
signing = {
key = "charlotte@vanpetegem.me";
signByDefault = config.chvp.graphical.enable;
};
userEmail = config.chvp.development.git.email;
userName = "Charlotte Van Petegem";
};
};
in
lib.mkIf config.chvp.development.git.enable {
chvp.base.emacs.extraConfig = [
''
;; Magit GitHub/GitLab integration
(use-package forge
:after magit)
;; Git integration
(use-package magit
:init
(setq forge-add-default-bindings nil)
:general
(lmap
"g" '(:ignore t :which-key "git")
"gs" '(magit-status :which-key "status")
)
)
;; Project management
(use-package project
:custom
(project-switch-commands
'(
(project-find-file "find file")
(consult-ripgrep "find regexp" ?r)
(project-eshell "eshell")
)
"Change default actions when switching project"
)
:general
(lmap
"p" '(:ignore t :which-key "project")
"pf" '(project-find-file :which-key "find")
"pp" '(project-switch-project :which-key "switch")
"pr" '(project-query-replace-regexp :which-key "replace")
"ps" '(consult-ripgrep :search "incsearch")
"pS" '(project-find-regexp :which-key "search")
"p!" '(project-shell-command :which-key "command")
"p&" '(project-async-shell-command :which-key "task")
)
)
''
];
home-manager.users.charlotte = { ... }: base;
home-manager.users.root = { ... }: base;
};
}

View file

@ -0,0 +1,24 @@
{ config, lib, ... }:
{
imports = [
./minecraft
./mumble
./particles
./steam
./tetris
];
options.chvp.games.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.games.enable {
chvp.games = {
minecraft.client = lib.mkDefault false;
mumble.enable = lib.mkDefault true;
steam.enable = lib.mkDefault true;
};
};
}

View file

@ -0,0 +1,32 @@
{ config, lib, pkgs, ... }:
{
options.chvp.games.minecraft = {
client = lib.mkOption {
default = false;
example = true;
};
server = lib.mkOption {
default = false;
example = true;
};
};
config = lib.mkIf (config.chvp.games.minecraft.client || config.chvp.games.minecraft.server) {
home-manager.users.charlotte = lib.mkIf config.chvp.games.minecraft.client ({ ... }: {
home.packages = [ pkgs.minecraft ];
});
chvp.base = {
zfs.homeLinks = lib.optional config.chvp.games.minecraft.client { path = ".minecraft"; type = "cache"; };
nix.unfreePackages =
(lib.optional config.chvp.games.minecraft.client "minecraft-launcher") ++
(lib.optional config.chvp.games.minecraft.server "minecraft-server");
};
services.minecraft-server = lib.mkIf config.chvp.games.minecraft.server {
enable = true;
dataDir = "${config.chvp.dataPrefix}/var/lib/minecraft-server";
eula = true;
openFirewall = true;
};
};
}

View file

@ -0,0 +1,19 @@
{ config, lib, pkgs, ... }:
{
options.chvp.games.mumble.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.games.mumble.enable {
chvp.base.zfs.homeLinks = [
{ path = ".config/Mumble"; type = "data"; }
{ path = ".local/share/Mumble"; type = "data"; }
];
home-manager.users.charlotte = { ... }: {
home.packages = with pkgs; [ mumble ];
};
};
}

View file

@ -0,0 +1,24 @@
{ config, lib, pkgs, ... }:
let
particles = pkgs.fetchgit {
url = "https://git.zeus.gent/midgard/particles.git";
sha256 = "0weSBjhSS0XuII5yZXWYUJpPOhetFJKbcsS8WWpodhs=";
};
in
{
options.chvp.games.particles.server = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.games.particles.server {
chvp.services.nginx.hosts = [{
fqdn = "particles.vanpetegem.me";
options = {
root = "${particles}/public";
locations."/".index = "index.html";
};
}];
};
}

View file

@ -0,0 +1,29 @@
{ config, lib, pkgs, ... }:
{
options.chvp.games.steam.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.games.steam.enable {
hardware.graphics = {
enable32Bit = true;
extraPackages = with pkgs.pkgsi686Linux; [ libva ];
};
services.pipewire.alsa.support32Bit = true;
chvp.base = {
nix.unfreePackages = [ "steam" "steam-original" "steam-runtime" "steam-run" ];
zfs.homeLinks = [
{ path = ".paradoxlauncher"; type = "cache"; }
{ path = ".steam"; type = "cache"; }
{ path = ".local/share/Steam"; type = "cache"; }
{ path = ".local/share/Paradox Interactive"; type = "cache"; }
];
};
home-manager.users.charlotte = { pkgs, ... }: {
home.packages = [ pkgs.steam pkgs.protontricks pkgs.wine ];
};
};
}

View file

@ -0,0 +1,18 @@
{ config, lib, pkgs, ... }:
{
options.chvp.games.tetris.server = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.games.tetris.server {
chvp.services.nginx.hosts = [{
fqdn = "tetris.vanpetegem.me";
options = {
root = "${pkgs.tetris}";
locations."/".index = "index.html";
};
}];
};
}

View file

@ -0,0 +1,7 @@
{ pkgs }:
pkgs.writeShellScriptBin "color_picker" ''
color=$(${pkgs.grim}/bin/grim -t png -g "$(${pkgs.slurp}/bin/slurp -p)" - | ${pkgs.imagemagick}/bin/convert png:- -unique-colors txt:- | grep -o '#[A-F0-9]\+')
${pkgs.sway}/bin/swaymsg exec -- "echo -n '$color' | ${pkgs.wl-clipboard}/bin/wl-copy --foreground"
''

View file

@ -0,0 +1,523 @@
{ config, lib, pkgs, ... }:
let
launcher = import ./launcher.nix { inherit pkgs; stdenv = pkgs.stdenv; };
color-picker = import ./color-picker.nix { inherit pkgs; };
screenshot = import ./screenshot.nix { inherit pkgs; };
mail-status = pkgs.writeShellScript "mail-status" ''
mails=$(${pkgs.mblaze}/bin/mlist -N ~/mail/*/INBOX | ${pkgs.coreutils}/bin/wc -l)
if [ "$mails" -gt 0 ]
then
echo "{ \"class\": \"has-mail\", \"text\": \"📬 $mails\" }"
else
echo "{ \"text\": \"📭\" }"
fi
'';
lock = pkgs.writeShellScript "lock" ''
if [ "$(darkman get)" == "light" ]
then
${pkgs.swaylock}/bin/swaylock -fF -c eff1f5
else
${pkgs.swaylock}/bin/swaylock -fF -c 303446
fi
'';
baseWrapper = pkgs.writeShellScriptBin "river" ''
export XDG_SESSION_TYPE=wayland
export XDG_CURRENT_DESKTOP=river
export QT_WAYLAND_DISABLE_WINDOWDECORATION=1
export QT_AUTO_SCREEN_SCALE_FACTOR=0
export QT_SCALE_FACTOR=1
export GDK_SCALE=1
export GDK_DPI_SCALE=1
export MOZ_ENABLE_WAYLAND=1
export XCURSOR_SIZE=24
export _JAVA_AWT_WM_NONREPARENTING=1
if [ "$DBUS_SESSION_BUS_ADDRESS" ]; then
export DBUS_SESSION_BUS_ADDRESS
exec ${pkgs.river}/bin/river
else
exec ${pkgs.dbus}/bin/dbus-run-session ${pkgs.river}/bin/river
fi
'';
river = pkgs.symlinkJoin {
name = "river-${pkgs.river.version}";
paths = [ baseWrapper pkgs.river ];
strictDeps = false;
nativeBuildInputs = with pkgs; [ makeWrapper wrapGAppsHook ];
buildInputs = with pkgs; [ gdk-pixbuf glib gtk3 ];
dontWrapGApps = true;
postBuild = ''
gappsWrapperArgsHook
wrapProgram $out/bin/river "''${gappsWrapperArgs[@]}"
'';
};
river-init = pkgs.writeShellScript "river-init" ''
riverctl map normal Super Return spawn foot
riverctl map normal Super D spawn 'foot --app-id launcher -- zsh -ic ${launcher}/bin/launcher'
riverctl map normal None Menu spawn 'foot --app-id launcher -- zsh -ic ${launcher}/bin/launcher'
riverctl map normal Super C spawn ${lock}
riverctl map normal Super+Shift C close
riverctl map normal Super+Shift E exit
riverctl map normal Super J focus-view next
riverctl map normal Super K focus-view previous
riverctl map normal Super+Shift J swap next
riverctl map normal Super+Shift K swap previous
riverctl map normal Super H focus-output next
riverctl map normal Super L focus-output previous
riverctl map normal Super+Shift H send-to-output -current-tags next
riverctl map normal Super+Shift L send-to-output -current-tags previous
riverctl map normal Super F zoom
riverctl map normal Super+Shift F toggle-fullscreen
riverctl map normal Super+Control H send-layout-cmd rivertile "main-ratio -0.05"
riverctl map normal Super+Control L send-layout-cmd rivertile "main-ratio +0.05"
riverctl map normal Super+Control+Shift H send-layout-cmd rivertile "main-count +1"
riverctl map normal Super+Control+Shift L send-layout-cmd rivertile "main-count -1"
riverctl map normal Super+Alt H move left 100
riverctl map normal Super+Alt J move down 100
riverctl map normal Super+Alt K move up 100
riverctl map normal Super+Alt L move right 100
riverctl map normal Super+Alt+Control H snap left
riverctl map normal Super+Alt+Control J snap down
riverctl map normal Super+Alt+Control K snap up
riverctl map normal Super+Alt+Control L snap right
riverctl map normal Super+Alt+Shift H resize horizontal -100
riverctl map normal Super+Alt+Shift J resize vertical 100
riverctl map normal Super+Alt+Shift K resize vertical -100
riverctl map normal Super+Alt+Shift L resize horizontal 100
riverctl map normal Super Space toggle-float
riverctl map-pointer normal Super BTN_LEFT move-view
riverctl map-pointer normal Super BTN_RIGHT resize-view
for i in $(seq 1 9)
do
tags=$((1 << ($i - 1)))
riverctl map normal Super $i set-focused-tags $tags
riverctl map normal Super+Shift $i set-view-tags $tags
riverctl map normal Super+Control $i toggle-focused-tags $tags
riverctl map normal Super+Shift+Control $i toggle-view-tags $tags
done
all_tags=$(((1 << 32) - 1))
riverctl map normal Super 0 set-focused-tags $all_tags
riverctl map normal Super+Shift 0 set-view-tags $all_tags
riverctl map normal Super Up send-layout-cmd rivertile "main-location top"
riverctl map normal Super Right send-layout-cmd rivertile "main-location right"
riverctl map normal Super Down send-layout-cmd rivertile "main-location bottom"
riverctl map normal Super Left send-layout-cmd rivertile "main-location left"
riverctl map normal None XF86AudioRaiseVolume spawn '${pkgs.pamixer}/bin/pamixer -i 5'
riverctl map normal None XF86AudioLowerVolume spawn '${pkgs.pamixer}/bin/pamixer -d 5'
riverctl map normal None XF86AudioMute spawn '${pkgs.pamixer}/bin/pamixer --toggle-mute'
riverctl map normal None XF86MonBrightnessDown spawn '${pkgs.brightnessctl}/bin/brightnessctl s -- -5%'
riverctl map normal None XF86MonBrightnessUp spawn '${pkgs.brightnessctl}/bin/brightnessctl s -- +5%'
riverctl map normal None Print spawn '${screenshot}/bin/screenshot'
riverctl map normal Alt Print spawn '${screenshot}/bin/screenshot -d'
riverctl map normal Shift Print spawn '${screenshot}/bin/screenshot -r'
riverctl map normal Alt+Shift Print spawn '${screenshot}/bin/screenshot -r -d'
riverctl rule-add -app-id launcher float
riverctl rule-add -app-id be.ugent.objprog.ugentopoly.Ugentopoly float
riverctl rule-add -title "Quick Format Citation" float
riverctl rule-add ssd
riverctl default-layout rivertile
rivertile -view-padding 0 -outer-padding 0 &
riverctl default-attach-mode bottom
riverctl background-color 0xacb0be
riverctl border-color-focused 0x04e5e5
riverctl border-color-unfocused 0xdce0e8
riverctl border-color-urgent 0xea76cb
riverctl border-width 1
riverctl focus-follows-cursor normal
riverctl hide-cursor when-typing enabled
riverctl set-cursor-warp on-output-change
riverctl xcursor-theme Catppuccin-Latte-Light-Cursors 24
riverctl keyboard-layout -variant altgr-intl -options compose:caps us
configure_touchpads() {
riverctl list-inputs | grep 'type: pointer' -B 1 | grep -vE 'type: pointer|^--$' | xargs -I '{}' riverctl input '{}' $@
}
configure_touchpads drag enabled
configure_touchpads tap enabled
configure_touchpads scroll-method two-finger
${pkgs.dbus}/bin/dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY XDG_CURRENT_DESKTOP XDG_SESSION_TYPE XCURSOR_SIZE QT_QPA_PLATFORM_THEME QT_STYLE_OVERRIDE QT_PLUGIN_PATH QTWEBKIT_PLUGIN_PATH GDK_PIXBUF_MODULE_FILE XDG_DATA_DIRS GIO_EXTRA_MODULES PATH
systemctl --user start river-session.target
systemctl --user start tray.target
'';
in
{
options.chvp.graphical.compositor.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.graphical.compositor.enable {
services = {
dbus.packages = with pkgs; [ dconf ];
greetd = {
enable = true;
settings =
let
river-run = pkgs.writeShellScript "river-run" ''
exec zsh -c "systemd-cat -t river ${river}/bin/river"
'';
in
{
default_session = {
command = "${pkgs.greetd.greetd}/bin/agreety --cmd ${river-run}";
};
initial_session = {
command = "${river-run}";
user = "charlotte";
};
};
};
};
security.pam.services.swaylock = { };
xdg.portal = {
enable = true;
extraPortals = [ pkgs.xdg-desktop-portal-gtk pkgs.xdg-desktop-portal-wlr ];
config.preferred = {
default = "gtk";
"org.freedesktop.impl.portal.Screencast" = "wlr";
};
};
home-manager.users.charlotte = { ... }: {
home.packages = [
river
color-picker
screenshot
pkgs.wf-recorder
pkgs.wl-clipboard
pkgs.wl-mirror
];
programs = {
waybar = {
enable = true;
settings = {
mainBar = {
spacing = 2;
modules-left = [ "river/tags" ];
modules-center = [ "river/window" ];
modules-right = [ "idle_inhibitor" "network#wlp0s20f3" "network#enp0s13f0u2u2" "battery" "backlight" "mpris" "pulseaudio" "custom/mail-status" "clock" "tray" ];
backlight = {
format = "{percent}% {icon}";
format-icons = [ "🌑" "🌒" "🌓" "🌔" "🌕" ];
on-scroll-up = "${pkgs.brightnessctl}/bin/brightnessctl s -- +5%";
on-scroll-down = "${pkgs.brightnessctl}/bin/brightnessctl s -- -5%";
};
battery = {
states = {
good = 90;
warning = 30;
critical = 15;
};
format = "{capacity}% {icon}";
format-charging = "{capacity}% ";
format-plugged = "";
format-alt = "{time} {icon}";
format-icons = [ "" "" "" "" "" ];
};
clock.format = " {:%a %d/%m %H:%M}";
"custom/mail-status" = {
exec = "${mail-status}";
return-type = "json";
interval = 1;
on-click = "${pkgs.isync}/bin/mbsync -a && ${config.chvp.base.emacs.package}/bin/emacsclient --eval \"(mu4e-update-index)\"";
};
idle_inhibitor = {
format = "{icon}";
format-icons = {
activated = "";
deactivated = "";
};
};
mpris = {
player = "firefox";
format = "{status_icon} {artist} - {title}";
status-icons = {
playing = "";
paused = "";
stopped = "";
};
};
"network#wlp0s20f3" = {
interface = "wlp0s20f3";
format-wifi = "{essid} ";
format-ethernet = "{ipaddr}/{cidr} ";
tooltip-format = "{ifname} via {gwaddr} ";
format-linked = "{ifname} (No IP) ";
format-disconnected = "";
format-alt = "{ifname}: {ipaddr}/{cidr}";
};
"network#enp0s13f0u2u2" = {
interface = "enp0s13f0u2u2";
format-wifi = "{essid} ";
format-ethernet = "{ipaddr}/{cidr} ";
tooltip-format = "{ifname} via {gwaddr} ";
format-linked = "{ifname} (No IP) ";
format-disconnected = "";
format-alt = "{ifname}: {ipaddr}/{cidr}";
};
pulseaudio = {
format = "{volume}% {icon} {format_source}";
format-bluetooth = "{volume}% {icon} {format_source}";
format-bluetooth-muted = " {icon} {format_source}";
format-muted = " {format_source}";
format-source = "{volume}% ";
format-source-muted = "";
format-icons = {
headphone = "";
hands-free = "";
headset = "";
phone = "";
portable = "";
car = "";
default = [ "" "" "" ];
};
on-click = "${pkgs.pamixer}/bin/pamixer -t";
on-click-right = "${pkgs.pamixer}/bin/pamixer --default-source -t";
on-click-middle = "${pkgs.pavucontrol}/bin/pavucontrol";
};
tray.spacing = 2;
};
};
style = ''
@import "colors.css";
* {
font-family: Hack, monospace;
font-size: 11px;
}
#window, #idle_inhibitor, #network, #battery, #backlight, #mpris, #pulseaudio, #custom-mail-status, #clock, #tray {
margin: 0;
padding: 0 5px;
background-color: @surface0;
}
button {
border: none;
border-radius: 0;
}
button:hover {
border: none;
border-radius: 0;
}
window#waybar {
background-color: @base;
color: @text;
}
#backlight {
color: @sky;
}
#battery {
color: @green;
}
#battery.good {
color: @sky;
}
#battery.warning {
color: @yellow;
}
#battery.critical {
color: @pink;
}
#custom-mail-status.has-mail {
color: @sky;
}
#idle_inhibitor.activated {
color: @sky;
}
#pulseaudio {
color: @yellow;
}
#tags button {
padding: 0;
box-shadow: inset 0 -3px transparent
color: @text;
}
#tags button.occupied {
background-color: @surface1;
}
#tags button.focused {
color: @sky;
}
#tags button.urgent {
color: @pink;
}
#tags button:hover {
box-shadow: inset 0 -3px @text;
}
'';
systemd.enable = true;
};
zsh.initExtra = ''
rs() {
riverctl spawn "$*"
}
'';
};
services = {
kanshi = {
enable = true;
systemdTarget = "river-session.target";
settings = [
{
profile = {
name = "home-undocked";
outputs = [
{ criteria = "AU Optronics 0x2036 Unknown"; position = "0,0"; mode = "2560x1440"; scale = 1.0; }
];
};
}
{
profile = {
name = "home-docked";
outputs = [
{ criteria = "AU Optronics 0x2036 Unknown"; position = "0,0"; mode = "2560x1440"; scale = 1.0; }
{ criteria = "Dell Inc. DELL U2718Q FN84K01T095L"; position = "2560,0"; mode = "3840x2160"; scale = 1.0; }
];
};
}
];
};
mako = {
enable = true;
font = "Hack Regular 9";
};
swayidle = {
enable = true;
systemdTarget = "river-session.target";
events = [{ event = "before-sleep"; command = "${lock}"; }];
timeouts = [
{ timeout = 150; command = "${pkgs.wlopm}/bin/wlopm --off '*'"; resumeCommand = "${pkgs.wlopm}/bin/wlopm --on '*'"; }
{ timeout = 300; command = "${lock}"; }
];
};
};
systemd.user.targets = {
river-session.Unit = {
Description = "river compositor session";
BindsTo = [ "graphical-session.target" ];
Wants = [ "graphical-session-pre.target" ];
After = [ "graphical-session-pre.target" ];
};
tray.Unit = {
Description = "tray target";
Wants = [ "graphical-session.target" ];
After = [ "graphical-session.target" ];
};
};
xdg.configFile = {
"river/init" = {
source = river-init;
onChange = ''
if [ -d /run/user/$UID ]
then
WAYLAND_DISPLAY="$(${pkgs.findutils}/bin/find /run/user/$UID -mindepth 1 -maxdepth 1 -type s -name wayland-\*)"
if [ -S "WAYLAND_DISPLAY" ]
then
${river-init}
fi
fi
'';
};
"waybar/frappe.css".text = ''
@define-color base #303446;
@define-color mantle #292c3c;
@define-color crust #232634;
@define-color text #c6d0f5;
@define-color subtext0 #a5adce;
@define-color subtext1 #b5bfe2;
@define-color surface0 #414559;
@define-color surface1 #51576d;
@define-color surface2 #626880;
@define-color overlay0 #737994;
@define-color overlay1 #838ba7;
@define-color overlay2 #949cbb;
@define-color blue #8caaee;
@define-color lavender #babbf1;
@define-color sapphire #85c1dc;
@define-color sky #99d1db;
@define-color teal #81c8be;
@define-color green #a6d189;
@define-color yellow #e5c890;
@define-color peach #ef9f76;
@define-color maroon #ea999c;
@define-color red #e78284;
@define-color mauve #ca9ee6;
@define-color pink #f4b8e4;
@define-color flamingo #eebebe;
@define-color rosewater #f2d5cf;
'';
"waybar/latte.css".text = ''
@define-color base #eff1f5;
@define-color mantle #e6e9ef;
@define-color crust #dce0e8;
@define-color text #4c4f69;
@define-color subtext0 #6c6f85;
@define-color subtext1 #5c5f77;
@define-color surface0 #ccd0da;
@define-color surface1 #bcc0cc;
@define-color surface2 #acb0be;
@define-color overlay0 #9ca0b0;
@define-color overlay1 #8c8fa1;
@define-color overlay2 #7c7f93;
@define-color blue #1e66f5;
@define-color lavender #7287fd;
@define-color sapphire #209fb5;
@define-color sky #04a5e5;
@define-color teal #179299;
@define-color green #40a02b;
@define-color yellow #df8e1d;
@define-color peach #fe640b;
@define-color maroon #e64553;
@define-color red #d20f39;
@define-color mauve #8839ef;
@define-color pink #ea76cb;
@define-color flamingo #dd7878;
@define-color rosewater #dc8a78;
'';
};
};
};
}

View file

@ -0,0 +1,24 @@
{ pkgs, stdenv }:
let
script = pkgs.substituteAll {
src = ./launcher.zsh;
inherit (pkgs)
fzy
jq
libqalculate
nix
pass
slurp
uni
zsh
;
wfRecorder = pkgs.wf-recorder;
wlClipboard = pkgs.wl-clipboard;
xdgUserDirs = pkgs.xdg-user-dirs;
};
in
pkgs.runCommand "launcher" { } ''
mkdir -p $out/bin
cp ${script} $out/bin/launcher
chmod +x $out/bin/launcher
''

View file

@ -0,0 +1,61 @@
#!@zsh@/bin/zsh
_sighandler() {
kill -INT "$child" 2>/dev/null
}
calc_options() {
echo "calc "
}
calc() {
if [ -n "$1" ]
then
@libqalculate@/bin/qalc "$1"
sleep 5
else
@libqalculate@/bin/qalc
fi
}
emoji_options() {
@uni@/bin/uni emoji all | sed "s/^/emoji /"
}
emoji() {
char=$(echo -n "$1" | sed "s/^\([^ ]*\) .*/\1/")
riverctl spawn "echo -n $char | @wlClipboard@/bin/wl-copy --foreground"
}
nrun_options() {
echo "nrun "
}
nrun() {
riverctl spawn "@nix@/bin/nix run nixpkgs\#$1"
}
run_options() {
print -rl -- ''${(ko)commands} | grep -v "^\\." | sed "s/^/run /"
}
run() {
riverctl spawn $1
}
systemctl_options() {
echo systemctl hibernate
echo systemctl poweroff
echo systemctl reboot
echo systemctl suspend
}
CHOSEN=$(cat <(systemctl_options) <(nrun_options) <(run_options) <(calc_options) <(emoji_options) | @fzy@/bin/fzy --lines 80 | tail -n1)
if [ -n "$CHOSEN" ]
then
PREFIX=$(echo $CHOSEN | sed "s/^\([^ ]*\) .*/\1/g")
WORD=$(echo $CHOSEN | sed "s/^[^ ]* \(.*\)/\1/g")
$PREFIX $WORD
fi

View file

@ -0,0 +1,36 @@
{ pkgs }:
pkgs.writeShellScriptBin "screenshot" ''
while getopts ":rd" opt
do
case "''${opt}" in
r)
remote=true
;;
d)
delay=true
;;
esac
done
dims="$(${pkgs.slurp}/bin/slurp)"
if [[ -n "$delay" ]]
then
sleep 5
fi
if [[ -n "$remote" ]]
then
name=$(${pkgs.util-linux}/bin/uuidgen).png
${pkgs.grim}/bin/grim -t png -g "$dims" - | ${pkgs.openssh}/bin/ssh data "cat > data/public/$name"
path="https://data.vanpetegem.me/public/$name"
else
name=$(date +'screenshot_%Y-%m-%d-%H%M%S.png')
path="$(${pkgs.xdg-user-dirs}/bin/xdg-user-dir PICTURES)/$name"
${pkgs.grim}/bin/grim -g "$dims" "$path"
fi
${pkgs.sway}/bin/swaymsg exec -- "echo -n '$path' | ${pkgs.wl-clipboard}/bin/wl-copy --foreground"
${pkgs.libnotify}/bin/notify-send "Screenshot taken" "$path"
''

View file

@ -0,0 +1,56 @@
{ config, lib, pkgs, ... }:
{
imports = [
./compositor
./firefox
./gnupg
./mail
./nextcloud-client
./pass
./sound
./terminal
./theme
./xdg
];
options.chvp.graphical.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.graphical.enable {
users.users.charlotte.extraGroups = [ "input" "video" ];
chvp = {
base = {
nix.unfreePackages = [ "google-chrome" ];
};
graphical = {
compositor.enable = lib.mkDefault true;
firefox.enable = lib.mkDefault true;
gnupg = {
enable = lib.mkDefault true;
pinentryFlavor = "qt";
};
mail.enable = lib.mkDefault true;
nextcloud-client.enable = lib.mkDefault true;
pass.enable = lib.mkDefault true;
sound.enable = lib.mkDefault true;
terminal.enable = lib.mkDefault true;
theme.enable = lib.mkDefault true;
xdg.enable = lib.mkDefault true;
};
};
home-manager.users.charlotte = { ... }: {
home.packages = with pkgs; [
google-chrome
mpv
okular
ranger
uni
wtype
];
};
};
}

View file

@ -0,0 +1,108 @@
{ config, lib, pkgs, ... }:
let
ff2mpv-host = pkgs.stdenv.mkDerivation rec {
pname = "ff2mpv";
version = "4.0.0";
src = pkgs.fetchFromGitHub {
owner = "woodruffw";
repo = "ff2mpv";
rev = "v${version}";
sha256 = "sxUp/JlmnYW2sPDpIO2/q40cVJBVDveJvbQMT70yjP4=";
};
buildInputs = [ pkgs.python3 ];
buildPhase = ''
sed -i "s#/home/william/scripts/ff2mpv#$out/bin/ff2mpv.py#" ff2mpv.json
sed -i 's#"mpv"#"${pkgs.mpv}/bin/umpv"#' ff2mpv.py
'';
installPhase = ''
mkdir -p $out/bin
cp ff2mpv.py $out/bin
mkdir -p $out/lib/mozilla/native-messaging-hosts
cp ff2mpv.json $out/lib/mozilla/native-messaging-hosts
'';
};
ffPackage = pkgs.firefox.override { pkcs11Modules = [ pkgs.eid-mw ]; };
zotero-connector = pkgs.nur.repos.rycee.firefox-addons.buildFirefoxXpiAddon rec {
pname = "zotero-connector";
version = "5.0.119";
addonId = "zotero@chnm.gmu.edu";
url = "https://download.zotero.org/connector/firefox/release/Zotero_Connector-${version}.xpi";
sha256 = "sha256-uRbhq0OSQ0Exgi5FEVFtdAGuLVla0aoGLmL0bMud0J8=";
meta = with lib; {
homepage = "https://www.zotero.org";
description = "Save references to Zotero from your web browser";
license = licenses.agpl3Plus;
platforms = platforms.all;
};
};
in
{
options.chvp.graphical.firefox = {
enable = lib.mkEnableOption "firefox";
package = lib.mkOption {
description = "Final used firefox package";
default = ffPackage;
readOnly = true;
};
};
config = lib.mkIf config.chvp.graphical.firefox.enable {
chvp.base.zfs.homeLinks = [
{ path = ".mozilla"; type = "data"; }
{ path = ".cache/mozilla"; type = "cache"; }
];
home-manager.users.charlotte = { ... }: {
programs.firefox = {
enable = true;
package = ffPackage;
nativeMessagingHosts = [
pkgs.keepassxc
ff2mpv-host
];
policies = {
DisableFirefoxStudies = true;
DisablePocket = true;
DisableTelemetry = true;
DisableFirefoxAccounts = true;
FirefoxHome = { Pocket = false; Snippets = false; };
OfferToSaveLogins = false;
UserMessaging = { SkipOnboarding = true; ExtensionRecommendations = false; };
};
profiles.default = {
extensions = with pkgs.nur.repos.rycee.firefox-addons; [
belgium-eid
consent-o-matic
cookie-autodelete
darkreader
decentraleyes
ff2mpv
keepassxc-browser
multi-account-containers
stylus
ublock-origin
vue-js-devtools
zotero-connector
];
settings = {
"browser.aboutConfig.showWarning" = false;
"browser.contentblocking.category" = "custom";
"browser.download.dir" = "/home/charlotte/downloads";
"browser.newtabpage.enabled" = false;
"browser.safebrowsing.malware.enabled" = false;
"browser.safebrowsing.phishing.enabled" = false;
"browser.shell.checkDefaultBrowser" = false;
"browser.startup.homepage" = "about:blank";
"browser.startup.page" = 3;
"dom.security.https_only_mode" = true;
"network.cookie.cookieBehavior" = 1;
"privacy.annotate_channels.strict_list.enabled" = true;
"privacy.trackingprotection.enabled" = true;
"privacy.trackingprotection.socialtracking.enabled" = true;
"security.identityblock.show_extended_validation" = true;
};
};
};
};
};
}

View file

@ -0,0 +1,41 @@
{ config, lib, pkgs, ... }:
{
options.chvp.graphical.gnupg = {
enable = lib.mkOption {
default = false;
example = true;
};
pinentryFlavor = lib.mkOption {
type = lib.types.str;
default = "curses";
example = "qt";
description = ''
Pinentry flavor for gnupg.
'';
};
};
config = lib.mkIf config.chvp.graphical.gnupg.enable {
chvp.base.zfs.homeLinks = [
{ path = ".gnupg"; type = "data"; }
];
programs.gnupg.agent = {
enable = true;
pinentryPackage = pkgs."pinentry-${config.chvp.graphical.gnupg.pinentryFlavor}";
};
home-manager.users.charlotte = { lib, ... }: {
home.activation.fixPermissionsCommands = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
mkdir -p /home/charlotte/.gnupg
chmod u=rwX,go= /home/charlotte/.gnupg
'';
programs.gpg.enable = true;
services.gpg-agent = {
enable = true;
defaultCacheTtl = 7200;
maxCacheTtl = 99999;
pinentryPackage = pkgs."pinentry-${config.chvp.graphical.gnupg.pinentryFlavor}";
};
};
};
}

View file

@ -0,0 +1,315 @@
{ config, lib, pkgs, ... }:
let
passwordScript = pkgs.writeShellScript "get_mail_password" ''${pkgs.libsecret}/bin/secret-tool lookup secret-tool-id $1 | ${pkgs.coreutils}/bin/tr -d "\n"'';
notifyScript = name: pkgs.writeShellScript "notify_${name}_mail" ''
unseen_count=$(${pkgs.mblaze}/bin/mlist -N ~/mail/*/INBOX | ${pkgs.coreutils}/bin/wc -l)
if [ "$unseen_count" = "1" ]
then
${pkgs.libnotify}/bin/notify-send -t 5000 'New ${name} mail arrived' "1 unseen mail"
elif [ "$unseen_count" != "0" ]
then
${pkgs.libnotify}/bin/notify-send -t 5000 'New ${name} mail arrived' "$unseen_count unseen mails"
fi
'';
makeAccount = { name, address, host ? "", imapHost ? host, smtpHost ? host, useStartTls ? false, secretToolId, extraConfig ? { }, oauth ? false }: (lib.recursiveUpdate
{
inherit address;
gpg = {
key = "charlotte@vanpetegem.me";
signByDefault = true;
};
imap = {
host = imapHost;
port = 993;
tls.enable = true;
};
imapnotify = {
enable = true;
boxes = [ "INBOX" ];
onNotify = "${pkgs.isync}/bin/mbsync ${name}:INBOX";
onNotifyPost = "${config.chvp.base.emacs.package}/bin/emacsclient --eval \"(mu4e-update-index)\" && ${notifyScript name}";
extraConfig = lib.mkIf oauth { xoauth2 = true; };
};
mbsync = {
enable = true;
create = "both";
expunge = "both";
flatten = ".";
remove = "both";
extraConfig.account.AuthMechs = if (oauth) then "XOAUTH2" else "LOGIN";
};
msmtp = {
enable = true;
extraConfig = lib.mkIf oauth { auth = "xoauth2"; };
};
mu.enable = true;
passwordCommand = if oauth then "${pkgs.mfauth}/bin/mfauth access ${name}" else "${passwordScript} ${secretToolId}";
realName = "Charlotte Van Petegem";
signature = {
showSignature = "none";
};
smtp = {
host = smtpHost;
port = if useStartTls then 587 else 465;
tls = {
enable = true;
inherit useStartTls;
};
};
userName = address;
}
extraConfig);
toRecursiveINI = with lib.strings; with lib.attrsets; with lib.generators; with lib.lists; let
repeat = count: char: concatStrings (genList (_: char) count);
mkHeader = depth: name: concatStrings [ (repeat depth "[") (escape [ "[" ] name) (repeat depth "]") ];
simpleAttrs = filterAttrs (n: v: !(isAttrs v));
complexAttrs = filterAttrs (n: v: isAttrs v);
removeEmpty = filter (v: v != "");
toRecursiveINIBase = depth: data: (concatStringsSep "\n" (
mapAttrsToList
(name: values: concatStringsSep "\n" (removeEmpty [
(mkHeader depth name)
(toKeyValue { } (simpleAttrs values))
(toRecursiveINIBase (depth + 1) (complexAttrs values))
]))
data
));
in
toRecursiveINIBase 1;
in
{
options.chvp.graphical.mail.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.graphical.mail.enable {
nixpkgs.overlays = [
(self: super: rec {
isync = super.isync.override { withCyrusSaslXoauth2 = true; };
})
];
chvp = {
base = {
emacs.extraConfig =
let
mkAccountConfig = account: ''
(make-mu4e-context
:name "${account.name}"
:match-func (lambda (msg) (when msg (string-prefix-p "/${account.maildir.path}/" (mu4e-message-field msg :maildir))))
:vars '(
(user-mail-address . "${account.address}")
(user-full-name . "${account.realName}")
(mu4e-drafts-folder . "/${account.maildir.path}/${account.folders.drafts}")
(mu4e-sent-folder . "/${account.maildir.path}/${account.folders.sent}")
(mu4e-refile-folder . "/${account.maildir.path}/${account.folders.trash}")
(mu4e-trash-folder . "/${account.maildir.path}/${account.folders.trash}")
(message-sendmail-extra-arguments . ("--read-envelope-from" "--account" "${account.name}"))
)
)
'';
hmConfig = config.home-manager.users.charlotte;
in
[
''
(use-package mu4e
;; Use mu4e included in the mu package, see emacs/default.nix
:ensure nil
:demand t
:after (vertico)
:hook
(mu4e-view-mode . display-line-numbers-mode)
(mu4e-view-mode . visual-line-mode)
(mu4e-compose-mode . chvp--mu4e-auto-dodona-cc-reply-to)
(mu4e-compose-mode . visual-line-mode)
(mu4e-compose-mode . (lambda () (setq use-hard-newlines nil)))
:custom
(mu4e-read-option-use-builtin nil "Don't use builtin autocomplete in mu4e")
(mu4e-completing-read-function 'completing-read "Use default completing read function")
(mu4e-maildir-initial-input "" "Don't have initial input when completing a maildir")
(mu4e-change-filenames-when-moving t "Avoid sync issues with mbsync")
(mu4e-maildir "${hmConfig.accounts.email.maildirBasePath}" "Root of the maildir hierarchy")
(mu4e-context-policy 'pick-first "Use the first mail context in the list")
(mu4e-attachment-dir "/home/charlotte/downloads/" "Save attachments to downloads folder")
(mu4e-compose-dont-reply-to-self t "Don't reply to myself on reply to all")
(mu4e-compose-format-flowed t "Send format=flowed mails when use-hard-newlines gets enabled")
(fill-flowed-display-column 1000000000000 "Dont fill when decoding flowed messages, let visual-line-mode handle it")
(gnus-treat-fill-long-lines nil "Let visual-line-mode handle filling")
(mu4e-confirm-quit nil "Don't confirm when quitting")
(mu4e-headers-include-related nil "Don't show related messages by default")
(mu4e-headers-skip-duplicates nil "Show duplicate emails")
(message-kill-buffer-on-exit t "Close buffer when finished with email")
(mm-verify-option 'known "Always verify PGP signatures (known protocols)")
(mm-discouraged-alternatives '("text/html" "text/richtext") "Discourage showing HTML views")
(gnus-buttonized-mime-types '("multipart/signed") "Make sure signature verification is always shown")
(mml-secure-openpgp-sign-with-sender t "Sign mails with the sender")
(sendmail-program "msmtp" "Use msmtp to send email")
(message-sendmail-f-is-evil t "Remove username from the emacs message")
(message-send-mail-function 'message-send-mail-with-sendmail "Use sendmail to send mail instead internal smtp")
(message-cite-reply-position 'below "Bottom posting is the correct way to reply to email")
:config
;; mu4e should just open in the currently focused window instead of taking up the whole frame
(add-to-list 'display-buffer-alist
`(,(regexp-quote mu4e-main-buffer-name)
display-buffer-same-window))
(setq mu4e-contexts (list ${lib.concatStringsSep "\n" (map mkAccountConfig (lib.attrValues hmConfig.accounts.email.accounts))}))
(add-to-list
'mu4e-bookmarks
'(:name "Combined inbox" :query "maildir:/personal/INBOX or maildir:/posteo/INBOX or maildir:/rodekruis-eerstehulp/INBOX" :key ?i :favorite t)
)
(defun chvp--mu4e-dodona-cc-reply-to ()
"Add dodona@ugent.be in cc and reply-to headers."
(interactive)
(save-excursion (message-add-header "Cc: dodona@ugent.be\nReply-To: dodona@ugent.be\n"))
)
(defun chvp--mu4e-auto-dodona-cc-reply-to ()
"Set dodona@ugent.be in CC and Reply-To headers when message was directed to dodona@ugent.be"
(let ((msg mu4e-compose-parent-message))
(when (and msg (mu4e-message-contact-field-matches msg :to "dodona@ugent.be")) (chvp--mu4e-dodona-cc-reply-to))
)
)
;; Never actually quit mu4e, just close the current buffer (making sure the modeline is still visible)
(defalias 'mu4e-quit 'chvp--kill-current-buffer)
(define-advice mu4e--context-ask-user
(:around (orig-fun &rest args) mu4e--context-ask-user-completing-read)
"Replace `mu4e-read-option` by general-purpose completing-read"
(cl-letf (((symbol-function 'mu4e-read-option)
(lambda (prompt options)
(let* ((prompt (mu4e-format "%s" prompt))
(choice (completing-read prompt (cl-mapcar #'car options) nil t))
(chosen-el (cl-find-if (lambda (option) (equal choice (car option))) options)))
(if chosen-el
(cdr chosen-el)
(mu4e-warn "Unknown option: '%s'" choice))))))
(apply orig-fun args)))
(mu4e 'background)
:general
(lmap "m" '(mu4e :which-key "mail"))
;; Unmap SPC in the mail view so we can still use the leader.
(lmap mu4e-view-mode-map "" nil)
(lmap mu4e-compose-mode-map
"SPC s" '(mml-secure-message-sign-pgpmime :which-key "Sign")
"SPC c" '(mml-secure-message-encrypt-pgpmime :which-key "Encrypt")
"SPC d" '(chvp--mu4e-dodona-cc-reply-to :which-key "Dodona support headers")
"SPC f" '(mu4e-toggle-use-hard-newlines :which-key "Toggle format=flowed/hard newlines")
)
)
(use-package visual-fill-column
:custom (visual-fill-column-enable-sensible-window-split t "Sensibly split windows in visual-fill-column-mode")
:hook (visual-line-mode . visual-fill-column-mode)
)
(use-package adaptive-wrap
:hook (visual-fill-column-mode . adaptive-wrap-prefix-mode)
)
''
];
zfs.homeLinks = [
{ path = "mail"; type = "data"; }
{ path = ".cache/mu"; type = "cache"; }
{ path = ".cache/mfauth"; type = "cache"; }
];
};
};
home-manager.users.charlotte = { ... }: {
accounts.email = {
maildirBasePath = "/home/charlotte/mail";
accounts = {
personal = makeAccount {
name = "personal";
address = "charlotte@vanpetegem.be";
host = "mail.vanpetegem.me";
secretToolId = "personal-mail";
extraConfig = {
folders = { drafts = "Drafts"; inbox = "INBOX"; sent = "INBOX"; trash = "Trash"; };
primary = true;
};
};
posteo = makeAccount {
name = "posteo";
address = "chvp@posteo.net";
host = "posteo.de";
secretToolId = "posteo";
extraConfig = {
folders = { drafts = "Drafts"; inbox = "INBOX"; sent = "INBOX"; trash = "Trash"; };
};
};
postbot = makeAccount {
name = "postbot";
address = "postbot@vanpetegem.be";
host = "mail.vanpetegem.me";
secretToolId = "postbot";
extraConfig = {
folders = { drafts = "Drafts"; inbox = "INBOX"; sent = "INBOX"; trash = "Trash"; };
};
};
rodekruis-eerstehulp = makeAccount {
name = "rodekruis-eerstehulp";
address = "eerstehulp@gent.rodekruis.be";
imapHost = "imap.gmail.com";
smtpHost = "smtp.gmail.com";
useStartTls = true;
secretToolId = "eerstehulp-mail";
extraConfig = {
folders = { drafts = "[Gmail].Concepten"; inbox = "INBOX"; sent = "INBOX"; trash = "[Gmail].Prullenbak"; };
flavor = "gmail.com";
};
};
webmaster = makeAccount {
name = "webmaster";
address = "webmaster@vanpetegem.be";
host = "mail.vanpetegem.me";
secretToolId = "webmaster";
extraConfig = {
folders = { drafts = "Drafts"; inbox = "INBOX"; sent = "INBOX"; trash = "Trash"; };
};
};
};
};
programs = {
mbsync.enable = true;
msmtp.enable = true;
mu.enable = true;
};
services = {
imapnotify.enable = true;
};
systemd.user = {
services = {
mbsync = {
Unit = {
Description = "MBSync email fetcher";
After = "network-online.target";
Wants = "network-online.target";
};
Service = {
Type = "oneshot";
ExecStart = [ "${pkgs.isync}/bin/mbsync -a" "${config.chvp.base.emacs.package}/bin/emacsclient --eval \"(mu4e-update-index)\"" ];
};
};
};
timers = {
mbsync = {
Unit = { Description = "MBSync email fetcher"; };
Timer = {
OnCalendar = "*:0/5";
Unit = "mbsync.service";
};
Install = { WantedBy = [ "timers.target" ]; };
};
vdirsyncer = {
Unit = { Description = "VDirSyncer WebDAV syncer"; };
Timer = {
OnCalendar = "*:0/5";
Unit = "vdirsyncer.service";
};
Install = { WantedBy = [ "timers.target" ]; };
};
};
};
};
};
}

View file

@ -0,0 +1,22 @@
{ config, lib, pkgs, ... }:
{
options.chvp.graphical.nextcloud-client.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.graphical.nextcloud-client.enable {
chvp.base.zfs.homeLinks = [
{ path = ".config/Nextcloud"; type = "cache"; }
{ path = ".local/share/Nextcloud"; type = "cache"; }
{ path = "sync"; type = "cache"; }
];
home-manager.users.charlotte = { ... }: {
services.nextcloud-client = {
enable = true;
startInBackground = true;
};
};
};
}

View file

@ -0,0 +1,42 @@
{ config, lib, pkgs, ... }:
{
options.chvp.graphical.pass.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.graphical.pass.enable {
chvp.base.zfs.homeLinks = [
{ path = ".config/keepassxc"; type = "data"; }
{ path = ".cache/keepassxc"; type = "cache"; }
];
chvp.base.emacs.extraConfig = [
''
(use-package secrets
:ensure nil
:custom
(auth-sources '(default))
)
''
];
home-manager.users.charlotte = { ... }: {
home.packages = [ pkgs.keepassxc ];
systemd.user.services.keepassxc = {
Unit = {
Description = "KeepassXC startup";
PartOf = [ "river-session.target" ];
Wants = [ "waybar.service" ];
After = [ "river-session.target" "waybar.service" ];
};
Service = {
ExecStart = "${pkgs.keepassxc}/bin/keepassxc";
Restart = "always";
};
Install.WantedBy = [ "river-session.target" ];
};
};
};
}

View file

@ -0,0 +1,30 @@
{ config, lib, pkgs, ... }:
{
options.chvp.graphical.sound.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.graphical.sound.enable {
chvp.base.zfs.homeLinks = [
{ path = ".local/state/wireplumber"; type = "cache"; }
];
home-manager.users.charlotte = { ... }: {
home.packages = with pkgs; [
pavucontrol
qjackctl
];
};
services = {
pipewire = {
enable = true;
alsa.enable = true;
jack.enable = true;
pulse.enable = true;
};
};
};
}

View file

@ -0,0 +1,50 @@
{ config, lib, pkgs, ... }:
{
options.chvp.graphical.terminal.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.graphical.terminal.enable {
home-manager.users.charlotte = { pkgs, ... }: {
home.packages = [ pkgs.foot ];
programs.foot = {
enable = true;
settings = {
main = {
font = "Hack:size=9";
dpi-aware = "no";
};
bell = {
urgent = true;
notify = true;
};
scrollback.lines = 10000;
cursor.blink = true;
mouse.hide-when-typing = true;
colors = {
foreground = "4c4f69";
background = "eff1f5";
regular0 = "5c5f77";
regular1 = "d20f39";
regular2 = "40a02b";
regular3 = "df8e1d";
regular4 = "1e66f5";
regular5 = "ea76cb";
regular6 = "179299";
regular7 = "acb0be";
bright0 = "6c6f85";
bright1 = "d20f39";
bright2 = "40a02b";
bright3 = "df8e1d";
bright4 = "1e66f5";
bright5 = "ea76cb";
bright6 = "179299";
bright7 = "bcc0cc";
};
};
};
};
};
}

View file

@ -0,0 +1,198 @@
{ config, lib, pkgs, ... }:
{
options.chvp.graphical.theme.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.graphical.theme.enable {
chvp.base.zfs.homeLinks = [
{ path = ".config/qt5ct"; type = "cache"; }
{ path = ".config/qt6ct"; type = "cache"; }
];
fonts = {
fontDir.enable = true;
fontconfig = {
enable = true;
defaultFonts = {
emoji = [ "Noto Color Emoji" ];
# The Tinos and Amiro fonts overlap with Font Awesome's codepoints, so make sure we give Font Awesome a higher priority.
monospace = [ "Hack" "Font Awesome 6 Free" ];
sansSerif = [ "Noto Sans" "Font Awesome 6 Free" ];
serif = [ "Noto Serif" "Font Awesome 6 Free" ];
};
};
packages = with pkgs; [
hack-font
font-awesome
noto-fonts
noto-fonts-cjk
noto-fonts-emoji
noto-fonts-extra
roboto
];
};
programs.dconf.enable = true;
home-manager.users.charlotte = { pkgs, lib, ... }: {
home.packages = [
pkgs.catppuccin-cursors.latteLight
# Also install dark mode to profile for darkman
(pkgs.catppuccin-gtk.override { size = "compact"; variant = "frappe"; })
];
home.file = {
".icons/default/index.theme".text = ''
[Icon Theme]
Name=Default
Comment=Default Cursor Theme
Inherits=catppuccin-latte-light-cursors
'';
};
dconf.settings."org/gnome/desktop/wm/preferences".button-layout = "";
gtk = {
enable = true;
font = {
package = pkgs.noto-fonts;
name = "Noto Sans";
size = 10;
};
gtk2.extraConfig = ''
gtk-cursor-theme-name = "catppuccin-latte-light-cursors"
gtk-cursor-theme-size = 24
'';
gtk3 = {
extraConfig = {
gtk-cursor-theme-name = "catppuccin-latte-light-cursors";
gtk-cursor-theme-size = 24;
};
extraCss = ''
/* No (default) titlebar on wayland */
headerbar.titlebar.default-decoration {
background: transparent;
padding: 0;
margin: 0 0 -17px 0;
border: 0;
min-height: 0;
font-size: 0;
box-shadow: none;
}
/* rm -rf window shadows */
window.csd, /* gtk4? */
window.csd decoration { /* gtk3 */
box-shadow: none;
}
'';
};
gtk4 = {
extraConfig = {
gtk-cursor-theme-name = "catppuccin-latte-light-cursors";
gtk-cursor-theme-size = 24;
};
extraCss = ''
/* No (default) titlebar on wayland */
headerbar.titlebar.default-decoration {
background: transparent;
padding: 0;
margin: 0 0 -17px 0;
border: 0;
min-height: 0;
font-size: 0;
box-shadow: none;
}
/* rm -rf window shadows */
window.csd, /* gtk4? */
window.csd decoration { /* gtk3 */
box-shadow: none;
}
'';
};
iconTheme = {
package = pkgs.libsForQt5.breeze-icons;
name = "breeze";
};
theme = {
package = pkgs.catppuccin-gtk.override { size = "compact"; variant = "latte"; };
name = "Catppuccin-Latte-Compact-Blue-Light";
};
};
qt = {
enable = true;
platformTheme.name = "qtct";
style = {
name = "lightly";
package = pkgs.lightly-qt;
};
};
services.darkman = {
enable = true;
settings = {
lat = 51.0;
lng = 3.7;
usegeoclue = false;
dbusserver = true;
portal = true;
};
darkModeScripts = {
emacs = ''
emacsclient --eval "(setq catppuccin-flavor 'frappe)"
emacsclient --eval "(load-theme 'catppuccin :no-confirm)"
'';
gtk = ''
${pkgs.glib}/bin/gsettings set org.gnome.desktop.interface gtk-theme Catppuccin-Frappe-Compact-Blue-Dark
'';
river = ''
riverctl background-color 0x626880
riverctl border-color-focused 0x99d1db
riverctl border-color-unfocused 0x232634
riverctl border-color-urgent 0xf4b8e4
'';
qt = ''
sed -i "s/Latte/Frappe/" ~/.config/qt5ct/qt5ct.conf
sed -i "s/Latte/Frappe/" ~/.config/qt6ct/qt6ct.conf
'';
terminal = ''
pkill -SIGUSR2 zsh
'';
waybar = ''
ln -sf ~/.config/waybar/frappe.css ~/.config/waybar/colors.css
systemctl --user restart waybar.service
'';
};
lightModeScripts = {
emacs = ''
emacsclient --eval "(setq catppuccin-flavor 'latte)"
emacsclient --eval "(load-theme 'catppuccin :no-confirm)"
'';
gtk = ''
${pkgs.glib}/bin/gsettings set org.gnome.desktop.interface gtk-theme Catppuccin-Latte-Compact-Blue-Light
'';
river = ''
riverctl background-color 0xacb0be
riverctl border-color-focused 0x04e5e5
riverctl border-color-unfocused 0xdce0e8
riverctl border-color-urgent 0xea76cb
'';
qt = ''
sed -i "s/Frappe/Latte/" ~/.config/qt5ct/qt5ct.conf
sed -i "s/Frappe/Latte/" ~/.config/qt6ct/qt6ct.conf
'';
terminal = ''
pkill -SIGUSR1 zsh
'';
waybar = ''
ln -sf ~/.config/waybar/latte.css ~/.config/waybar/colors.css
systemctl --user restart waybar.service
'';
};
};
home.activation = {
linkWaybarCssColors = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
$DRY_RUN_CMD ln -sf $VERBOSE_ARG ~/.config/waybar/latte.css ~/.config/waybar/colors.css
'';
};
};
};
}

View file

@ -0,0 +1,58 @@
{ config, lib, pkgs, ... }:
{
options.chvp.graphical.xdg.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.graphical.xdg.enable {
chvp.base.zfs.homeLinks = [
{ path = "desktop"; type = "data"; }
{ path = "documents"; type = "data"; }
{ path = "downloads"; type = "cache"; }
{ path = "music"; type = "data"; }
{ path = "pictures"; type = "cache"; }
{ path = "repos"; type = "cache"; }
{ path = "templates"; type = "data"; }
{ path = "videos"; type = "data"; }
];
home-manager.users.charlotte = { pkgs, ... }: {
home.packages = with pkgs; [ xdg-user-dirs xdg-utils ];
xdg = {
enable = true;
# Some applications overwrite mimeapps.list with an identical file
configFile."mimeapps.list".force = true;
mimeApps = {
enable = true;
defaultApplications = {
"image/png" = [ "org.kde.okular.desktop" ];
"image/jpg" = [ "org.kde.okular.desktop" ];
"image/jpeg" = [ "org.kde.okular.desktop" ];
"application/pdf" = [ "org.kde.okular.desktop" ];
"text/html" = [ "firefox.desktop" ];
"x-scheme-handler/about" = [ "firefox.desktop" ];
"x-scheme-handler/http" = [ "firefox.desktop" ];
"x-scheme-handler/https" = [ "firefox.desktop" ];
"x-scheme-handler/unknown" = [ "firefox.desktop" ];
"x-scheme-handler/msteams" = [ "teams.desktop" ];
};
};
userDirs = {
enable = true;
desktop = "\$HOME/desktop";
documents = "\$HOME/documents";
download = "\$HOME/downloads";
music = "\$HOME/music";
pictures = "\$HOME/pictures";
publicShare = "\$HOME/desktop";
templates = "\$HOME/templates";
videos = "\$HOME/videos";
};
};
};
};
}

View file

@ -0,0 +1,18 @@
{ config, lib, pkgs, ... }:
{
options.chvp.programs.calibre.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.programs.calibre.enable {
chvp.base.zfs.homeLinks = [
{ path = ".config/calibre"; type = "cache"; }
];
home-manager.users.charlotte = { ... }: {
home.packages = [ pkgs.calibre ];
};
services.udisks2.enable = true;
};
}

View file

@ -0,0 +1,20 @@
{ pkgs, ... }:
{
imports = [
./calibre
./eid
./element
./hledger
./obs
./torrents
];
home-manager.users.charlotte = { ... }: {
home.packages = with pkgs; [
jq
xsv
yt-dlp
];
};
}

View file

@ -0,0 +1,16 @@
{ config, lib, pkgs, ... }:
{
options.chvp.programs.eid.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.programs.eid.enable {
environment.systemPackages = [ pkgs.eid-mw ];
services.pcscd = {
enable = true;
plugins = [ pkgs.ccid ];
};
};
}

View file

@ -0,0 +1,19 @@
{ config, lib, pkgs, ... }:
{
options.chvp.programs.element.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.programs.element.enable {
chvp.base.zfs.homeLinks = [
{ path = ".config/nheko"; type = "data"; }
{ path = ".local/share/nheko"; type = "data"; }
{ path = ".cache/nheko"; type = "cache"; }
];
home-manager.users.charlotte = { ... }: {
home.packages = [ pkgs.nheko ];
};
};
}

View file

@ -0,0 +1,33 @@
{ config, lib, pkgs, ... }:
{
options.chvp.programs.hledger.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.programs.hledger.enable {
chvp.base.emacs.extraConfig = [
''
;; Ledger syntax support
(use-package ledger-mode
:mode "\\.journal\\'"
:custom
(ledger-binary-path "hledger" "Use hledger instead of ledger")
(ledger-highlight-xact-under-point nil "Remove distracting highlight")
(ledger-mode-should-check-version nil "Remove version check, since it doesn't work with hledger anyway")
(ledger-post-account-alignment-column 4 "Indent postings with 4 spaces")
(ledger-post-amount-alignment-at :decimal "Align on the decimal")
(ledger-post-amount-alignment-column 69 "Align on column 70")
(ledger-post-auto-align t "Align when moving to the next line")
:config
(advice-add 'ledger-complete-at-point :around #'cape-wrap-nonexclusive)
)
''
];
home-manager.users.charlotte = { ... }: {
home.packages = [ pkgs.hledger ];
};
};
}

View file

@ -0,0 +1,27 @@
{ config, lib, pkgs, ... }:
{
options.chvp.programs.obs.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.programs.obs.enable {
boot.kernelModules = [ "v4l2loopback" ];
boot.extraModulePackages = [ pkgs.linuxPackages.v4l2loopback ];
boot.extraModprobeConfig = ''
options v4l2loopback video_nr=9 card_label="obs"
'';
chvp.base.zfs.homeLinks = [
{ path = ".config/obs-studio"; type = "data"; }
];
home-manager.users.charlotte = { pkgs, ... }: {
programs.obs-studio = {
enable = true;
plugins = [ pkgs.obs-studio-plugins.wlrobs ];
};
};
};
}

View file

@ -0,0 +1,16 @@
{ config, lib, pkgs, ... }:
{
options.chvp.programs.torrents.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.programs.torrents.enable {
chvp.base.zfs.homeLinks = [{ path = ".config/transmission-remote-gtk"; type = "data"; }];
home-manager.users.charlotte = { pkgs, ... }: {
home.packages = with pkgs; [ transmission-remote-gtk ];
};
};
}

View file

@ -0,0 +1,42 @@
{ config, lib, pkgs, ... }:
{
options.chvp.services.accentor.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.services.accentor.enable {
services.postgresql = {
enable = true;
dataDir = "${config.chvp.dataPrefix}/var/lib/postgresql/${config.services.postgresql.package.psqlSchema}";
};
services.accentor = {
enable = true;
home = "${config.chvp.dataPrefix}/var/lib/accentor";
hostname = "accentor.vanpetegem.me";
environmentFile = config.age.secrets."passwords/services/accentor".path;
rescanTimer = {
enable = true;
dates = "00:00";
};
nginx = {
forceSSL = true;
useACMEHost = "vanpetegem.me";
};
};
security.doas.extraRules = [{
users = [ "charlotte" ];
noPass = true;
cmd = "accentor-console";
runAs = "accentor";
}];
age.secrets."passwords/services/accentor" = {
file = ../../../../secrets/passwords/services/accentor.age;
owner = "accentor";
};
};
}

View file

@ -0,0 +1,21 @@
{ config, lib, pkgs, ... }:
{
options.chvp.services.containers = {
enable = lib.mkOption {
default = false;
example = true;
};
externalInterface = lib.mkOption {
example = "eno3";
};
};
config = {
networking.nat = lib.mkIf config.chvp.services.containers.enable {
enable = true;
enableIPv6 = true;
internalInterfaces = [ "ve-+" ];
externalInterface = config.chvp.services.containers.externalInterface;
};
};
}

View file

@ -0,0 +1,46 @@
{ pkgs, ... }:
{
users.users.data = {
isNormalUser = true;
home = "/home/data";
description = "Data Access";
uid = 1000;
group = "users";
hashedPasswordFile = "/run/secrets/password_file";
};
users.users.readonly = {
isNormalUser = true;
home = "/home/readonly";
description = "Readonly data access";
uid = 1001;
group = "sftponly";
hashedPasswordFile = "/run/secrets/readonly_password_file";
};
users.groups.sftponly = { gid = 10000; };
environment.systemPackages = [ pkgs.rsync pkgs.mktorrent (pkgs.writeShellScriptBin "create_torrent" ". /run/secrets/create_torrent") ];
security.sudo.enable = false;
services.openssh = {
enable = true;
hostKeys = [
{ bits = 4096; path = "/run/secrets/ssh_host_rsa_key"; type = "rsa"; }
{ path = "/run/secrets/ssh_host_ed25519_key"; type = "ed25519"; }
];
settings = {
HostKeyAlgorithms = "+ssh-rsa";
Macs = [ "hmac-sha2-512-etm@openssh.com" "hmac-sha2-256-etm@openssh.com" "umac-128-etm@openssh.com" "hmac-sha2-512" ];
PermitRootLogin = "no";
};
extraConfig = ''
Match group sftponly
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
ForceCommand internal-sftp
Match user data
PasswordAuthentication no
KbdInteractiveAuthentication no
'';
authorizedKeysFiles = [ "/run/secrets/%u_authorized_keys" ];
};
}

View file

@ -0,0 +1,122 @@
{ config, lib, ... }:
{
options.chvp.services.data-access.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.services.data-access.enable {
chvp.services = {
containers.enable = true;
nginx.hosts = [
{
fqdn = "data.vanpetegem.me";
options = {
default = true;
basicAuthFile = config.age.secrets."passwords/services/data-basic-auth".path;
root = "/srv/data";
locations = {
"/".extraConfig = ''
autoindex on;
'';
"/public".extraConfig = ''
autoindex on;
auth_basic off;
'';
};
};
}
];
};
networking.firewall.allowedTCPPorts = [ 2002 ];
containers.data-access = {
ephemeral = true;
autoStart = true;
bindMounts = {
"/home/data/data" = {
hostPath = "/srv/data";
isReadOnly = false;
};
"/home/readonly/data" = {
hostPath = "/srv/data";
isReadOnly = true;
};
"/run/secrets" = {
hostPath = "/run/data-access";
isReadOnly = true;
};
};
forwardPorts = [{
containerPort = 22;
hostPort = 2002;
protocol = "tcp";
}];
privateNetwork = true;
hostAddress = "192.168.100.10";
hostAddress6 = "fc00::1";
localAddress = "192.168.100.11";
localAddress6 = "fc00::2";
config = { ... }: {
system.stateVersion = config.chvp.stateVersion;
imports = [ ./config.nix ];
};
};
age.secrets."data-access/ssh_host_rsa_key" = {
file = ../../../../secrets/data-access/ssh_host_rsa_key.age;
path = "/run/data-access/ssh_host_rsa_key";
symlink = false;
};
age.secrets."data-access/ssh_host_rsa_key.pub" = {
file = ../../../../secrets/data-access/ssh_host_rsa_key.pub.age;
path = "/run/data-access/ssh_host_rsa_key.pub";
symlink = false;
};
age.secrets."data-access/ssh_host_ed25519_key" = {
file = ../../../../secrets/data-access/ssh_host_ed25519_key.age;
path = "/run/data-access/ssh_host_ed25519_key";
symlink = false;
};
age.secrets."data-access/ssh_host_ed25519_key.pub" = {
file = ../../../../secrets/data-access/ssh_host_ed25519_key.pub.age;
path = "/run/data-access/ssh_host_ed25519_key.pub";
symlink = false;
};
age.secrets."data-access/password_file" = {
file = ../../../../secrets/data-access/password_file.age;
path = "/run/data-access/password_file";
symlink = false;
};
age.secrets."data-access/readonly_password_file" = {
file = ../../../../secrets/data-access/readonly_password_file.age;
path = "/run/data-access/readonly_password_file";
symlink = false;
};
age.secrets."data-access/authorized_keys" = {
file = ../../../../secrets/data-access/authorized_keys.age;
owner = "charlotte";
path = "/run/data-access/data_authorized_keys";
symlink = false;
};
age.secrets."data-access/readonly_authorized_keys" = {
file = ../../../../secrets/data-access/readonly_authorized_keys.age;
owner = "1001";
group = "65534";
path = "/run/data-access/readonly_authorized_keys";
symlink = false;
};
age.secrets."data-access/create_torrent" = {
file = ../../../../secrets/data-access/create_torrent.age;
owner = "charlotte";
path = "/run/data-access/create_torrent";
symlink = false;
};
age.secrets."passwords/services/data-basic-auth" = {
file = ../../../../secrets/passwords/services/data-basic-auth.age;
owner = "nginx";
};
};
}

View file

@ -0,0 +1,20 @@
{ config, pkgs, ... }:
{
imports = [
./accentor
./containers
./data-access
./garmin-scraper
./git
./grafana
./mail
./mastodon
./matrix
./nextcloud
./nginx
./torrents
];
services.postgresql.package = pkgs.postgresql_15;
}

View file

@ -0,0 +1,39 @@
{ config, lib, pkgs, ... }:
let
garmin2influx = pkgs.writers.writePython3Bin "garmin2influx"
{
libraries = with pkgs.python3Packages; [ garminconnect influxdb-client ];
}
(builtins.readFile ./garmin2influx.py);
in
{
options.chvp.services.garmin-scraper.enable = lib.mkEnableOption "garmin scraper";
config = lib.mkIf config.chvp.services.garmin-scraper.enable {
# Install in environment to allow manual data collection
environment.systemPackages = [ garmin2influx ];
systemd = {
services.garmin2influx = {
description = "Garmin health data importer";
restartIfChanged = false;
unitConfig.X-StopOnRemoval = false;
serviceConfig = {
EnvironmentFile = config.age.secrets."passwords/services/garmin2influx-env".path;
Type = "oneshot";
User = "charlotte";
Group = "users";
ExecStart = "${garmin2influx}/bin/garmin2influx";
RestartSec = "5s";
Restart = "on-failure";
};
startAt = "02/4:00";
};
timers.garmin2influx.timerConfig.RandomizedDelaySec = "30min";
};
age.secrets."passwords/services/garmin2influx-env" = {
file = ../../../../secrets/passwords/services/garmin2influx-env.age;
owner = "charlotte";
};
};
}

View file

@ -0,0 +1,88 @@
import os
import sys
from datetime import date, datetime, timedelta, timezone
from garminconnect import (
Garmin,
GarminConnectConnectionError,
GarminConnectTooManyRequestsError,
GarminConnectAuthenticationError,
)
from influxdb_client import InfluxDBClient, Point, WritePrecision
from influxdb_client.client.write_api import SYNCHRONOUS
email = os.getenv('EMAIL')
password = os.getenv('PASSWORD')
token = os.getenv('TOKEN')
org = 'default'
bucket = 'default'
def hr2point(time, val):
return Point("health") \
.field("heart_rate", val) \
.time(
datetime.fromtimestamp(time / 1000, timezone.utc),
WritePrecision.S
)
def stress2point(time, val):
return Point("health") \
.field("stress", max(val, 0)) \
.time(
datetime.fromtimestamp(time / 1000, timezone.utc),
WritePrecision.S
)
def hr_for_date(api, date_to_fetch):
return api.get_heart_rates(date_to_fetch.isoformat())['heartRateValues']
def stress_for_date(api, date_to_fetch):
return api.get_stress_data(date_to_fetch.isoformat())['stressValuesArray']
date_to_fetch = date.today().isoformat()
if len(sys.argv) > 1:
date_to_fetch = sys.argv[1]
date_to_fetch = date.fromisoformat(date_to_fetch)
try:
api = Garmin(email, password)
api.login()
hr_points = list(map(
lambda p: hr2point(*p),
hr_for_date(api, date_to_fetch - timedelta(days=1))
))
stress_points = list(map(
lambda p: stress2point(*p),
stress_for_date(api, date_to_fetch - timedelta(days=1))
))
hr_points += list(map(
lambda p: hr2point(*p),
hr_for_date(api, date_to_fetch)
))
stress_points += list(map(
lambda p: stress2point(*p),
stress_for_date(api, date_to_fetch)
))
with InfluxDBClient(
url="https://stats.chvp.be:8086",
token=token,
org=org
) as client:
write_api = client.write_api(write_options=SYNCHRONOUS)
write_api.write(bucket, org, hr_points)
write_api.write(bucket, org, stress_points)
except (
GarminConnectConnectionError,
GarminConnectAuthenticationError,
GarminConnectTooManyRequestsError,
) as err:
print(
f'Error occured during Garmin Connect communication: {err}',
file=sys.stderr
)
sys.exit(1)

View file

@ -0,0 +1,85 @@
{ config, lib, pkgs, ... }:
{
imports = [ ./runner.nix ];
options.chvp.services.git.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.services.git.enable {
chvp.services.nginx.hosts = [{
fqdn = "git.chvp.be";
options = {
locations."/" = {
proxyPass = "http://unix:/run/gitlab/gitlab-workhorse.socket";
extraConfig = ''
client_max_body_size 50M;
'';
};
};
}];
users = {
users = {
git = {
uid = lib.mkForce 963;
group = "git";
isSystemUser = true;
useDefaultShell = true;
};
nginx.extraGroups = [ "git" ];
};
groups.git.gid = lib.mkForce 963;
};
services.openssh.settings.AcceptEnv = "GIT_PROTOCOL";
services.gitlab = {
enable = true;
statePath = "/var/lib/git/state";
backup.path = "/var/lib/git/backup";
databaseCreateLocally = true;
databaseUsername = "git";
databaseName = "git";
user = "git";
group = "git";
host = "git.chvp.be";
port = 443;
https = true;
initialRootEmail = "charlotte@vanpetegem.be";
initialRootPasswordFile = config.age.secrets."passwords/services/git/initial-root-password".path;
# Hack, https://github.com/NixOS/nixpkgs/pull/135926 broke stuff
pages.settings.pages-domain = "not.actually.enabled";
secrets = {
dbFile = config.age.secrets."passwords/services/git/db".path;
jwsFile = config.age.secrets."passwords/services/git/jws".path;
otpFile = config.age.secrets."passwords/services/git/otp".path;
secretFile = config.age.secrets."passwords/services/git/secret".path;
};
smtp = {
enable = true;
enableStartTLSAuto = false;
};
};
age.secrets."passwords/services/git/initial-root-password" = {
file = ../../../../secrets/passwords/services/git/initial-root-password.age;
owner = "git";
};
age.secrets."passwords/services/git/db" = {
file = ../../../../secrets/passwords/services/git/db.age;
owner = "git";
};
age.secrets."passwords/services/git/jws" = {
file = ../../../../secrets/passwords/services/git/jws.age;
owner = "git";
};
age.secrets."passwords/services/git/otp" = {
file = ../../../../secrets/passwords/services/git/otp.age;
owner = "git";
};
age.secrets."passwords/services/git/secret" = {
file = ../../../../secrets/passwords/services/git/secret.age;
owner = "git";
};
};
}

View file

@ -0,0 +1,57 @@
{ config, lib, pkgs, ... }:
{
options.chvp.services.git.runner.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.services.git.runner.enable {
services.gitlab-runner = {
enable = true;
settings.concurrent = 8;
services = {
nix = {
authenticationTokenConfigFile = config.age.secrets."passwords/services/gitlab-runner/registration".path;
dockerImage = "alpine";
dockerVolumes = [
"/nix/store:/nix/store:ro"
"/nix/var/nix/db:/nix/var/nix/db:ro"
"/nix/var/nix/daemon-socket:/nix/var/nix/daemon-socket:ro"
"/etc/nix/nix.conf:/etc/nix/nix.conf:ro"
];
preBuildScript = pkgs.writeScript "setup-container" ''
mkdir -p -m 0755 /nix/var/log/nix/drvs
mkdir -p -m 0755 /nix/var/nix/gcroots
mkdir -p -m 0755 /nix/var/nix/profiles
mkdir -p -m 0755 /nix/var/nix/temproots
mkdir -p -m 0755 /nix/var/nix/userpool
mkdir -p -m 1777 /nix/var/nix/gcroots/per-user
mkdir -p -m 1777 /nix/var/nix/profiles/per-user
mkdir -p -m 0755 /nix/var/nix/profiles/per-user/root
mkdir -p -m 0700 "$HOME/.nix-defexpr"
. ${pkgs.nix}/etc/profile.d/nix.sh
${pkgs.nix}/bin/nix-env -i ${lib.concatStringsSep " " (with pkgs; [ nix cacert git openssh ])}
'';
environmentVariables = {
ENV = "/etc/profile";
USER = "root";
NIX_REMOTE = "daemon";
PATH = "/nix/var/nix/profiles/default/bin:/nix/var/nix/profiles/default/sbin:/bin:/sbin:/usr/bin:/usr/sbin";
NIX_SSL_CERT_FILE = "/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt";
};
requestConcurrency = 4;
};
};
};
virtualisation.docker = {
enable = true;
storageDriver = "zfs";
};
age.secrets."passwords/services/gitlab-runner/registration" = {
file = ../../../../secrets/passwords/services/gitlab-runner/registration.age;
};
};
}

View file

@ -0,0 +1,97 @@
{ config, lib, pkgs, ... }:
{
options.chvp.services.grafana.enable = lib.mkEnableOption "grafana";
config = lib.mkIf config.chvp.services.grafana.enable {
chvp.services.nginx.hosts = [{
fqdn = "stats.chvp.be";
options.locations."/" = {
proxyPass = "http://grafana";
proxyWebsockets = true;
};
}];
users.users = {
influxdb2.extraGroups = [ "acme" ];
nginx.extraGroups = [ "grafana" ];
};
networking.firewall.allowedTCPPorts = [ 8086 ];
services = {
nginx.upstreams.grafana.servers = { "unix:/run/grafana/grafana.sock" = { }; };
influxdb2 = {
enable = true;
settings = {
reporting-disabled = true;
tls-cert = "${config.security.acme.certs."vanpetegem.me".directory}/fullchain.pem";
tls-key = "${config.security.acme.certs."vanpetegem.me".directory}/key.pem";
};
};
grafana = {
enable = true;
dataDir = "${config.chvp.dataPrefix}/var/lib/grafana";
settings = {
analytics.reporting_enabled = false;
"auth.anonymous" = {
enabled = "true";
org_name = "Van Petegem";
};
database = {
user = "grafana";
type = "postgres";
host = "/run/postgresql/";
name = "grafana";
};
security = {
admin_user = "chvp";
admin_password = "$__file{${config.age.secrets."passwords/services/grafana/admin-password".path}}";
secret_key = "$__file{${config.age.secrets."passwords/services/grafana/secret-key".path}}";
};
server = {
domain = "stats.chvp.be";
http_port = 3000;
protocol = "socket";
root_url = "https://stats.chvp.be";
socket = "/run/grafana/grafana.sock";
};
smtp = {
enabled = true;
host = "mail.vanpetegem.me:25";
user = "noreply@vanpetegem.me";
from_address = "noreply@vanpetegem.me";
password = "$__file{${config.age.secrets."passwords/services/grafana/smtp".path}}";
};
users = {
default_theme = "light";
allow_sign_up = false;
};
};
};
grafana-image-renderer = {
enable = true;
provisionGrafana = true;
chromium = pkgs.ungoogled-chromium;
};
postgresql = {
enable = true;
dataDir = "${config.chvp.dataPrefix}/var/lib/postgresql/${config.services.postgresql.package.psqlSchema}";
ensureDatabases = [ "grafana" ];
ensureUsers = [{
name = "grafana";
ensureDBOwnership = true;
}];
};
};
age.secrets."passwords/services/grafana/smtp" = {
file = ../../../../secrets/passwords/services/grafana/smtp.age;
owner = "grafana";
};
age.secrets."passwords/services/grafana/admin-password" = {
file = ../../../../secrets/passwords/services/grafana/admin-password.age;
owner = "grafana";
};
age.secrets."passwords/services/grafana/secret-key" = {
file = ../../../../secrets/passwords/services/grafana/secret-key.age;
owner = "grafana";
};
};
}

View file

@ -0,0 +1,166 @@
{ config, lib, pkgs, ... }:
let
keyFile = "${config.security.acme.certs."vanpetegem.me".directory}/key.pem";
certFile = "${config.security.acme.certs."vanpetegem.me".directory}/fullchain.pem";
in
{
options.chvp.services.mail.enable = lib.mkEnableOption "mail";
config = lib.mkIf config.chvp.services.mail.enable {
chvp.base.zfs.systemLinks = [
{ path = "/var/lib/dhparams"; type = "cache"; }
{ path = "/var/lib/dovecot"; type = "cache"; }
{ path = "/var/lib/opendkim"; type = "cache"; }
{ path = "/var/lib/postfix"; type = "cache"; }
{ path = "/var/lib/redis-rspamd"; type = "cache"; }
{ path = "/var/lib/rspamd"; type = "cache"; }
{ path = "/var/sieve"; type = "data"; }
];
mailserver = {
enable = true;
enableManageSieve = true;
fqdn = "mail.vanpetegem.me";
domains = [
"accentor.tech"
"chvp.be"
"cvpetegem.be"
"robbe.be"
"robbevanpetegem.be"
"robbevp.be"
"toekomstlabo.be"
"vanpetegem.be"
"vanpetegem.me"
];
localDnsResolver = false;
loginAccounts = {
"charlotte@vanpetegem.be" = {
hashedPasswordFile = config.age.secrets."passwords/services/mail/charlotte@vanpetegem.be".path;
aliases = [ "@chvp.be" "@cvpetegem.be" "charlotte@vanpetegem.me" ];
};
"huis@vanpetegem.me".hashedPasswordFile = config.age.secrets."passwords/services/mail/huis@vanpetegem.me".path;
"noreply@vanpetegem.me" = {
hashedPasswordFile = config.age.secrets."passwords/services/mail/noreply@vanpetegem.me".path;
sendOnly = true;
};
"peter@vanpetegem.me".hashedPasswordFile = config.age.secrets."passwords/services/mail/peter@vanpetegem.me".path;
"postbot@vanpetegem.be" = {
hashedPasswordFile = config.age.secrets."passwords/services/mail/postbot@vanpetegem.be".path;
aliases = [ "@vanpetegem.me" "@vanpetegem.be" ];
};
"robbe@vanpetegem.be" = {
hashedPasswordFile = config.age.secrets."passwords/services/mail/robbe@vanpetegem.be".path;
aliases = [ "robbe.nb@vanpetegem.me" "robbe@vanpetegem.me" ];
};
"robbe@robbevanpetegem.be" = {
hashedPasswordFile = config.age.secrets."passwords/services/mail/robbe@robbevanpetegem.be".path;
aliases = [ "@robbevanpetegem.be" ];
};
"hallo@robbe.be" = {
hashedPasswordFile = config.age.secrets."passwords/services/mail/hallo@robbe.be".path;
aliases = [ "@robbe.be" "@robbevp.be" ];
};
"webmaster@vanpetegem.be" = {
hashedPasswordFile = config.age.secrets."passwords/services/mail/webmaster@vanpetegem.be".path;
aliases = [ "webmaster@vanpetegem.me" ];
};
};
indexDir = "${config.chvp.cachePrefix}/var/lib/dovecot/indices";
fullTextSearch = {
enable = false;
memoryLimit = 4000;
};
lmtpSaveToDetailMailbox = "no";
mailboxes = {
Trash = {
auto = "no";
specialUse = "Trash";
autoexpunge = "60d";
};
Junk = {
auto = "subscribe";
specialUse = "Junk";
autoexpunge = "60d";
};
Drafts = {
auto = "subscribe";
specialUse = "Drafts";
};
Sent = {
auto = "subscribe";
specialUse = "Sent";
};
};
extraVirtualAliases = {
"team@accentor.tech" = [ "charlotte@vanpetegem.be" "robbe@vanpetegem.be" ];
};
forwards = {
"info@toekomstlabo.be" = "robbe+toekomstlabo@robbevanpetegem.be";
};
rejectRecipients = [
"cindy@vanpetegem.be"
"contact@vanpetegem.be"
"info@vanpetegem.be"
"isabelle@vanpetegem.be"
"marierose@vanpetegem.be"
"rudy@vanpetegem.be"
"sarina@vanpetegem.be"
"sabrina@vanpetegem.be"
];
rejectSender = [
"junjunggaming07@gmail.com"
"censysnetbackup@gmail.com"
"vitor.carvalheiro@escola.pr.gov.br"
"spam@vuztc.ru"
"mofos@theportalnetworks.com" # Someone registered my f-droid contact address on a porn site :(
];
mailDirectory = "${config.chvp.dataPrefix}/var/vmail";
useFsLayout = false;
certificateScheme = "manual";
certificateFile = certFile;
keyFile = keyFile;
dkimKeyDirectory = "${config.chvp.dataPrefix}/var/dkim";
};
services.dovecot2.sieve = {
extensions = [ "editheader" ];
scripts.after2 = pkgs.writeText "custom-spam.sieve" ''
require ["fileinto", "regex"];
if anyof(
# Freshdesk is often used to sent spam from emails like `support@info5813.freshdesk.com`
address :regex "From" "[a-z\d]+@[a-z\d]+\.freshdesk\.com",
header :contains "From" ["jakubbielec", "Jakub Bielec"],
# Stop any mail pretending to be from itsme not from their official domains
allof(
address :contains "From" "itsme",
not address :matches :domain "from" ["*itsme.be", "*itsme-id.com"]
)
) {
fileinto "Junk";
stop;
}
'';
};
services.rspamd.extraConfig = ''
actions {
reject = null;
add_header = 6;
greylist = 4;
}
'';
age.secrets = {
"passwords/services/mail/charlotte@vanpetegem.be".file = ../../../../secrets/passwords/services/mail/charlotte_at_vanpetegem.be.age;
"passwords/services/mail/hallo@robbe.be".file = ../../../../secrets/passwords/services/mail/hallo_at_robbe.be.age;
"passwords/services/mail/huis@vanpetegem.me".file = ../../../../secrets/passwords/services/mail/huis_at_vanpetegem.me.age;
"passwords/services/mail/noreply@vanpetegem.me".file = ../../../../secrets/passwords/services/mail/noreply_at_vanpetegem.me.age;
"passwords/services/mail/peter@vanpetegem.me".file = ../../../../secrets/passwords/services/mail/peter_at_vanpetegem.me.age;
"passwords/services/mail/postbot@vanpetegem.be".file = ../../../../secrets/passwords/services/mail/postbot_at_vanpetegem.be.age;
"passwords/services/mail/robbe@robbevanpetegem.be".file = ../../../../secrets/passwords/services/mail/robbe_at_robbevanpetegem.be.age;
"passwords/services/mail/robbe@vanpetegem.be".file = ../../../../secrets/passwords/services/mail/robbe_at_vanpetegem.be.age;
"passwords/services/mail/webmaster@vanpetegem.be".file = ../../../../secrets/passwords/services/mail/webmaster_at_vanpetegem.be.age;
};
};
}

View file

@ -0,0 +1,75 @@
{ config, lib, pkgs, ... }:
{
options.chvp.services.mastodon.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.services.mastodon.enable {
chvp.services.nginx.hosts = [{
fqdn = "social.chvp.be";
options = {
root = "${pkgs.mastodon}/public/";
locations = {
"/system/".alias = "/var/lib/mastodon/public-system/";
"/".tryFiles = "$uri @proxy";
"@proxy" = {
proxyPass = "http://unix:/run/mastodon-web/web.socket";
proxyWebsockets = true;
};
"/api/v1/streaming" = {
proxyPass = "http://unix:/run/mastodon-streaming/streaming.socket";
proxyWebsockets = true;
};
};
};
}];
users = {
users = {
mastodon.uid = 989;
nginx.extraGroups = [ "mastodon" ];
};
groups.mastodon.gid = 985;
};
services.mastodon = {
enable = true;
configureNginx = false;
localDomain = "social.chvp.be";
enableUnixSocket = true;
streamingProcesses = 4;
database.createLocally = true;
redis.createLocally = true;
smtp = {
fromAddress = "social@chvp.be";
createLocally = false;
};
extraConfig = {
SMTP_OPENSSL_VERIFY_MODE = "none";
};
otpSecretFile = config.age.secrets."passwords/services/mastodon/otp".path;
secretKeyBaseFile = config.age.secrets."passwords/services/mastodon/key".path;
vapidPublicKeyFile = config.age.secrets."passwords/services/mastodon/vapid-public".path;
vapidPrivateKeyFile = config.age.secrets."passwords/services/mastodon/vapid-private".path;
};
age.secrets."passwords/services/mastodon/vapid-public" = {
file = ../../../../secrets/passwords/services/mastodon/vapid-public.age;
owner = "mastodon";
};
age.secrets."passwords/services/mastodon/vapid-private" = {
file = ../../../../secrets/passwords/services/mastodon/vapid-private.age;
owner = "mastodon";
};
age.secrets."passwords/services/mastodon/key" = {
file = ../../../../secrets/passwords/services/mastodon/key.age;
owner = "mastodon";
};
age.secrets."passwords/services/mastodon/otp" = {
file = ../../../../secrets/passwords/services/mastodon/otp.age;
owner = "mastodon";
};
};
}

View file

@ -0,0 +1,229 @@
{ config, lib, pkgs, ... }:
{
options.chvp.services.matrix.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.services.matrix.enable {
chvp.base.zfs.systemLinks = [{ path = "/var/lib/matrix-hookshot"; type = "data"; }];
chvp.services.nginx.hosts = [
{
fqdn = "matrix.vanpetegem.me";
options.locations = {
"/" = {
proxyPass = "http://127.0.0.1:8448";
extraConfig = ''
proxy_set_header X-Forwarded-Ssl on;
proxy_read_timeout 600;
client_max_body_size 10M;
'';
};
"/_slack" = {
proxyPass = "http://127.0.0.1:9898";
extraConfig = ''
proxy_set_header X-Forwarded-Ssl on;
'';
};
"~ ^/_hookshot/(.*)" = {
proxyPass = "http://127.0.0.1:9000/$1";
extraConfig = ''
proxy_set_header X-Forwarded-Ssl on;
'';
};
};
}
{
fqdn = "matrix-sync.vanpetegem.me";
basicProxy = "http://localhost:8009";
}
];
services = {
matrix-synapse = {
enable = true;
settings = {
server_name = "vanpetegem.me";
public_baseurl = "https://matrix.vanpetegem.me";
listeners = [{
port = 8448;
bind_addresses = [ "::1" "127.0.0.1" ];
type = "http";
tls = false;
x_forwarded = true;
resources = [
{ names = [ "client" ]; compress = true; }
{ names = [ "federation" ]; compress = false; }
];
}];
url_preview_enabled = true;
enable_metrics = false;
enable_registration = false;
report_stats = false;
allow_guest_access = false;
suppress_key_server_warning = true;
app_service_config_files = [
config.age.secrets."files/services/matrix-synapse/whatsapp-registration.yml".path
config.age.secrets."files/services/matrix-synapse/slack-registration.yml".path
config.age.secrets."files/services/matrix-synapse/hookshot-registration.yml".path
];
};
extraConfigFiles = [
config.age.secrets."files/services/matrix-synapse/config.yml".path
];
dataDir = "${config.chvp.dataPrefix}/var/lib/matrix-synapse";
};
postgresql = {
enable = true;
dataDir = "${config.chvp.dataPrefix}/var/lib/postgresql/${config.services.postgresql.package.psqlSchema}";
ensureDatabases = [
"matrix-synapse"
"matrix_appservice_slack"
"mautrix_whatsapp"
];
ensureUsers = [
{
name = "matrix_appservice_slack";
ensureDBOwnership = true;
}
{
name = "mautrix_whatsapp";
ensureDBOwnership = true;
}
{
name = "matrix-synapse";
ensureDBOwnership = true;
}
];
};
matrix-sliding-sync = {
enable = true;
settings = {
SYNCV3_SERVER = "https://matrix.vanpetegem.me";
};
environmentFile = config.age.secrets."files/servers/matrix-sliding-sync/env".path;
createDatabase = true;
};
};
systemd.services = {
matrix-appservice-slack = {
description = "Matrix <-> Slack bridge";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" "postgresql.service" "matrix-synapse.service" ];
requires = [ "postgresql.service" "matrix-synapse.service" ];
script = "${pkgs.matrix-appservice-slack}/bin/matrix-appservice-slack --config ${config.age.secrets."files/services/matrix-appservice-slack/config.yml".path} --file ${config.age.secrets."files/services/matrix-appservice-slack/registration.yml".path}";
serviceConfig = {
User = "matrix_appservice_slack";
Group = "matrix_appservice_slack";
};
};
matrix-synapse = {
requires = [ "postgresql.service" ];
};
mautrix-whatsapp = {
description = "Matrix <-> WhatsApp bridge";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" "postgresql.service" "matrix-synapse.service" ];
requires = [ "postgresql.service" "matrix-synapse.service" ];
script = "${pkgs.mautrix-whatsapp}/bin/mautrix-whatsapp --config ${config.age.secrets."files/services/mautrix-whatsapp/config.yml".path}";
serviceConfig = {
User = "mautrix_whatsapp";
Group = "mautrix_whatsapp";
};
};
matrix-hookshot = {
description = "Matrix <-> Services bridge";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" "matrix-synapse.service" ];
requires = [ "matrix-synapse.service" ];
script = "${pkgs.matrix-hookshot}/bin/matrix-hookshot ${config.age.secrets."files/services/matrix-hookshot/config.yml".path} ${config.age.secrets."files/services/matrix-hookshot/registration.yml".path}";
serviceConfig = {
User = "matrix_hookshot";
Group = "matrix_hookshot";
WorkingDirectory = "/var/lib/matrix-hookshot";
};
};
};
users = {
users = {
matrix_appservice_slack = {
uid = 998;
group = "matrix_appservice_slack";
isSystemUser = true;
};
mautrix_whatsapp = {
uid = 997;
group = "mautrix_whatsapp";
isSystemUser = true;
};
matrix_hookshot = {
uid = 979;
group = "matrix_hookshot";
home = "/var/lib/matrix-hookshot";
isSystemUser = true;
};
};
groups = {
matrix_appservice_slack = {
gid = 998;
};
mautrix_whatsapp = {
gid = 997;
};
matrix_hookshot = {
gid = 979;
};
};
};
age.secrets."files/services/matrix-appservice-slack/config.yml" = {
file = ../../../../secrets/files/services/matrix-appservice-slack/config.yml.age;
owner = "matrix_appservice_slack";
};
age.secrets."files/services/matrix-appservice-slack/registration.yml" = {
file = ../../../../secrets/files/services/matrix-appservice-slack/registration.yml.age;
owner = "matrix_appservice_slack";
};
age.secrets."files/services/matrix-hookshot/config.yml" = {
file = ../../../../secrets/files/services/matrix-hookshot/config.yml.age;
owner = "matrix_hookshot";
};
age.secrets."files/services/matrix-hookshot/registration.yml" = {
file = ../../../../secrets/files/services/matrix-hookshot/registration.yml.age;
owner = "matrix_hookshot";
};
age.secrets."files/services/matrix-hookshot/passkey.pem" = {
path = "/var/lib/matrix-hookshot/passkey.pem";
file = ../../../../secrets/files/services/matrix-hookshot/passkey.pem.age;
owner = "matrix_hookshot";
};
age.secrets."files/services/mautrix-whatsapp/config.yml" = {
file = ../../../../secrets/files/services/mautrix-whatsapp/config.yml.age;
owner = "mautrix_whatsapp";
};
age.secrets."files/services/mautrix-whatsapp/registration.yml" = {
file = ../../../../secrets/files/services/mautrix-whatsapp/registration.yml.age;
owner = "mautrix_whatsapp";
};
age.secrets."files/services/matrix-synapse/config.yml" = {
file = ../../../../secrets/files/services/matrix-synapse/config.yml.age;
owner = "matrix-synapse";
};
age.secrets."files/services/matrix-synapse/slack-registration.yml" = {
file = ../../../../secrets/files/services/matrix-appservice-slack/registration.yml.age;
owner = "matrix-synapse";
};
age.secrets."files/services/matrix-synapse/whatsapp-registration.yml" = {
file = ../../../../secrets/files/services/mautrix-whatsapp/registration.yml.age;
owner = "matrix-synapse";
};
age.secrets."files/services/matrix-synapse/hookshot-registration.yml" = {
file = ../../../../secrets/files/services/matrix-hookshot/registration.yml.age;
owner = "matrix-synapse";
};
age.secrets."files/servers/matrix-sliding-sync/env".file = ../../../../secrets/files/services/matrix-sliding-sync/env.age;
};
}

View file

@ -0,0 +1,68 @@
{ config, lib, pkgs, ... }:
{
options.chvp.services.nextcloud.enable = lib.mkOption {
default = false;
example = true;
};
config = lib.mkIf config.chvp.services.nextcloud.enable {
services = {
nextcloud = {
home = "${config.chvp.dataPrefix}/var/lib/nextcloud";
https = true;
hostName = "nextcloud.vanpetegem.me";
enable = true;
autoUpdateApps.enable = true;
package = pkgs.nextcloud29;
caching.redis = true;
config = {
dbuser = "nextcloud";
dbname = "nextcloud";
dbtype = "pgsql";
dbhost = "/run/postgresql";
adminuser = "admin";
adminpassFile = config.age.secrets."passwords/services/nextcloud-admin".path;
};
settings.redis = {
host = "127.0.0.1";
port = 31638;
dbindex = 0;
timeout = 1.5;
};
};
nginx.virtualHosts."nextcloud.vanpetegem.me" = {
forceSSL = true;
useACMEHost = "vanpetegem.me";
extraConfig = ''
fastcgi_connect_timeout 10m;
fastcgi_read_timeout 10m;
fastcgi_send_timeout 10m;
'';
};
postgresql = {
enable = true;
dataDir = "${config.chvp.dataPrefix}/var/lib/postgresql/${config.services.postgresql.package.psqlSchema}";
ensureDatabases = [ "nextcloud" ];
ensureUsers = [{
name = "nextcloud";
ensureDBOwnership = true;
}];
};
redis.servers.nextcloud = {
enable = true;
port = 31638;
bind = "127.0.0.1";
};
};
age.secrets."passwords/services/nextcloud-admin" = {
file = ../../../../secrets/passwords/services/nextcloud-admin.age;
owner = "nextcloud";
};
systemd.services."nextcloud-setup" = {
requires = [ "postgresql.service" ];
after = [ "postgresql.service" ];
};
users.users.nextcloud.uid = 996;
users.groups.nextcloud.gid = 996;
};
}

View file

@ -0,0 +1,88 @@
{ config, lib, ... }:
{
options.chvp.services.nginx = {
enable = lib.mkOption {
readOnly = true;
default = (builtins.length config.chvp.services.nginx.hosts) > 0;
};
hosts = lib.mkOption {
default = [ ];
example = [
{
fqdn = "data.vanpetegem.me";
options = {
default = true;
root = "/srv/data";
locations = {
"/".extraConfig = ''
autoindex on;
'';
"/public".extraConfig = ''
autoindex on;
auth_basic off;
'';
};
};
}
];
};
};
config = lib.mkIf config.chvp.services.nginx.enable {
networking.firewall.allowedTCPPorts = [ 80 443 ];
security.acme = {
certs."vanpetegem.me" = {
dnsProvider = "cloudflare";
credentialsFile = config.age.secrets."passwords/services/acme".path;
extraDomainNames = [
"*.vanpetegem.me"
"cvpetegem.be"
"*.cvpetegem.be"
"chvp.be"
"*.chvp.be"
"vanpetegem.be"
"*.vanpetegem.be"
];
};
defaults.email = "webmaster@vanpetegem.me";
acceptTerms = true;
preliminarySelfsigned = false;
};
age.secrets."passwords/services/acme" = {
file = ../../../../secrets/passwords/services/acme.age;
owner = "acme";
};
chvp.base.zfs.systemLinks = [
{ type = "data"; path = "/var/lib/acme"; }
];
services.nginx = {
enable = true;
recommendedTlsSettings = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
virtualHosts = builtins.listToAttrs
(map
(elem: {
name = elem.fqdn;
value = {
forceSSL = true;
useACMEHost = "vanpetegem.me";
locations."/" = lib.mkIf (builtins.hasAttr "basicProxy" elem) {
proxyPass = elem.basicProxy;
extraConfig = ''
proxy_set_header X-Forwarded-Ssl on;
'' + (elem.extraProxySettings or "");
};
} // (elem.options or { });
})
config.chvp.services.nginx.hosts);
};
users.users = {
nginx.extraGroups = [ "acme" ];
acme.uid = 999;
};
users.groups.acme.gid = 999;
};
}

View file

@ -0,0 +1,41 @@
{ config, lib, pkgs, ... }:
{
options.chvp.services.torrents = {
enable = lib.mkOption {
default = false;
example = true;
};
};
config = lib.mkIf config.chvp.services.torrents.enable {
chvp.services.nginx.hosts = [{ fqdn = "transmission.vanpetegem.me"; basicProxy = "http://localhost:9091"; }];
services.transmission = {
enable = true;
package = pkgs.transmission_4;
user = "charlotte";
group = "users";
home = "/data/var/lib/transmission";
openRPCPort = false;
openPeerPorts = true;
credentialsFile = config.age.secrets."files/programs/transmission/config.json".path;
settings = {
umask = 18;
download-dir = "/srv/data";
incomplete-dir = "/srv/data/.incomplete";
rpc-authentication-required = true;
rpc-bind-address = "0.0.0.0";
rpc-enabled = true;
rpc-host-whitelist-enabled = false;
rpc-whitelist-enabled = false;
speed-limit-down = 51200;
speed-limit-down-enabled = true;
};
};
age.secrets."files/programs/transmission/config.json" = {
file = ../../../../secrets/files/programs/transmission/config.json.age;
owner = "charlotte";
};
};
}