summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig20
-rw-r--r--.envrc1
-rw-r--r--.gitignore4
-rw-r--r--.shellcheckrc2
-rw-r--r--LICENSE14
-rw-r--r--configurations/default.nix139
-rw-r--r--configurations/manwe/default.nix171
-rw-r--r--configurations/manwe/mailserver.nix96
-rw-r--r--configurations/manwe/webserver.nix21
-rw-r--r--configurations/melian/default.nix288
-rw-r--r--configurations/test-headful/default.nix27
-rw-r--r--configurations/test-headless/default.nix22
-rw-r--r--configurations/varda/default.nix95
-rw-r--r--configurations/yavanna/default.nix101
-rw-r--r--flake.lock421
-rw-r--r--flake.nix242
-rw-r--r--lib/default.nix20
-rw-r--r--lib/dns.nix14
-rw-r--r--lib/my.nix202
-rw-r--r--modules/nixfiles/acme.nix32
-rw-r--r--modules/nixfiles/alacritty.nix119
-rw-r--r--modules/nixfiles/alertmanager.nix81
-rw-r--r--modules/nixfiles/aria2.nix48
-rw-r--r--modules/nixfiles/aspell.nix23
-rw-r--r--modules/nixfiles/bat.nix39
-rw-r--r--modules/nixfiles/beets.nix73
-rw-r--r--modules/nixfiles/bluetooth.nix34
-rw-r--r--modules/nixfiles/broot.nix18
-rw-r--r--modules/nixfiles/chromium.nix42
-rw-r--r--modules/nixfiles/common/console.nix6
-rw-r--r--modules/nixfiles/common/default.nix19
-rw-r--r--modules/nixfiles/common/documentation.nix48
-rw-r--r--modules/nixfiles/common/environment.nix7
-rw-r--r--modules/nixfiles/common/home-manager.nix23
-rw-r--r--modules/nixfiles/common/kernel.nix32
-rw-r--r--modules/nixfiles/common/locale.nix18
-rw-r--r--modules/nixfiles/common/networking.nix66
-rw-r--r--modules/nixfiles/common/nix.nix173
-rw-r--r--modules/nixfiles/common/secrets.nix47
-rw-r--r--modules/nixfiles/common/security.nix19
-rw-r--r--modules/nixfiles/common/services.nix9
-rw-r--r--modules/nixfiles/common/shell/default.nix125
-rw-r--r--modules/nixfiles/common/shell/functions.bash73
-rw-r--r--modules/nixfiles/common/systemd.nix22
-rw-r--r--modules/nixfiles/common/tmp.nix18
-rw-r--r--modules/nixfiles/common/users.nix21
-rw-r--r--modules/nixfiles/common/xdg.nix86
-rw-r--r--modules/nixfiles/curl.nix38
-rw-r--r--modules/nixfiles/default.nix80
-rw-r--r--modules/nixfiles/direnv.nix24
-rw-r--r--modules/nixfiles/docker.nix34
-rw-r--r--modules/nixfiles/dwm.nix159
-rw-r--r--modules/nixfiles/emacs.nix69
-rw-r--r--modules/nixfiles/endlessh-go.nix57
-rw-r--r--modules/nixfiles/endlessh.nix45
-rw-r--r--modules/nixfiles/fail2ban.nix32
-rw-r--r--modules/nixfiles/firefox/default.nix52
-rw-r--r--modules/nixfiles/firefox/profile.nix479
-rw-r--r--modules/nixfiles/firefox/userChrome.css156
-rw-r--r--modules/nixfiles/firefox/userContent.css210
-rw-r--r--modules/nixfiles/flatpak.nix13
-rw-r--r--modules/nixfiles/fonts.nix106
-rw-r--r--modules/nixfiles/games/default.nix47
-rw-r--r--modules/nixfiles/games/gamemode.nix13
-rw-r--r--modules/nixfiles/games/gog.nix19
-rw-r--r--modules/nixfiles/games/lutris.nix37
-rw-r--r--modules/nixfiles/games/mangohud.nix13
-rw-r--r--modules/nixfiles/games/minecraft.nix15
-rw-r--r--modules/nixfiles/games/steam-run.nix59
-rw-r--r--modules/nixfiles/games/steam.nix21
-rw-r--r--modules/nixfiles/git.nix125
-rw-r--r--modules/nixfiles/gnome.nix65
-rw-r--r--modules/nixfiles/gnupg.nix91
-rw-r--r--modules/nixfiles/gotify.nix66
-rw-r--r--modules/nixfiles/grafana.nix84
-rw-r--r--modules/nixfiles/htop.nix57
-rw-r--r--modules/nixfiles/hydra.nix56
-rw-r--r--modules/nixfiles/ipfs.nix168
-rw-r--r--modules/nixfiles/kde.nix27
-rw-r--r--modules/nixfiles/libvirtd.nix40
-rw-r--r--modules/nixfiles/lidarr.nix27
-rw-r--r--modules/nixfiles/loki.nix113
-rw-r--r--modules/nixfiles/lxc.nix16
-rw-r--r--modules/nixfiles/matrix/default.nix1
-rw-r--r--modules/nixfiles/matrix/dendrite.nix148
-rw-r--r--modules/nixfiles/matrix/element.nix59
-rw-r--r--modules/nixfiles/matrix/synapse.nix90
-rw-r--r--modules/nixfiles/monitoring.nix114
-rw-r--r--modules/nixfiles/mpd.nix212
-rw-r--r--modules/nixfiles/mpv.nix133
-rw-r--r--modules/nixfiles/nextcloud.nix133
-rw-r--r--modules/nixfiles/nginx.nix90
-rw-r--r--modules/nixfiles/nmap.nix42
-rw-r--r--modules/nixfiles/node-exporter.nix35
-rw-r--r--modules/nixfiles/nsd.nix157
-rw-r--r--modules/nixfiles/openssh.nix52
-rw-r--r--modules/nixfiles/password-store.nix33
-rw-r--r--modules/nixfiles/podman.nix35
-rw-r--r--modules/nixfiles/postgresql.nix53
-rw-r--r--modules/nixfiles/profiles/common.nix103
-rw-r--r--modules/nixfiles/profiles/default.nix5
-rw-r--r--modules/nixfiles/profiles/dev/common.nix290
-rw-r--r--modules/nixfiles/profiles/dev/containers/default.nix73
-rw-r--r--modules/nixfiles/profiles/dev/default.nix1
-rw-r--r--modules/nixfiles/profiles/dev/ghci.conf35
-rw-r--r--modules/nixfiles/profiles/dev/pystartup.py127
-rw-r--r--modules/nixfiles/profiles/dev/sql/default.nix94
-rw-r--r--modules/nixfiles/profiles/headful.nix69
-rw-r--r--modules/nixfiles/profiles/headless.nix67
-rw-r--r--modules/nixfiles/prometheus.nix60
-rw-r--r--modules/nixfiles/promtail.nix53
-rw-r--r--modules/nixfiles/psd.nix60
-rw-r--r--modules/nixfiles/qutebrowser.nix547
-rw-r--r--modules/nixfiles/radarr.nix27
-rw-r--r--modules/nixfiles/radicale.nix48
-rw-r--r--modules/nixfiles/rss-bridge.nix33
-rw-r--r--modules/nixfiles/rtorrent.nix246
-rw-r--r--modules/nixfiles/searx.nix81
-rw-r--r--modules/nixfiles/shadowsocks.nix119
-rw-r--r--modules/nixfiles/soju.nix83
-rw-r--r--modules/nixfiles/sonarr.nix27
-rw-r--r--modules/nixfiles/sound.nix21
-rw-r--r--modules/nixfiles/subversion.nix53
-rw-r--r--modules/nixfiles/syncthing.nix161
-rw-r--r--modules/nixfiles/throttled.nix117
-rw-r--r--modules/nixfiles/tmux.nix60
-rw-r--r--modules/nixfiles/unbound.nix208
-rw-r--r--modules/nixfiles/vaultwarden.nix117
-rw-r--r--modules/nixfiles/vim/default.nix56
-rw-r--r--modules/nixfiles/vim/rc.vim252
-rw-r--r--modules/nixfiles/vscode.nix172
-rw-r--r--modules/nixfiles/wget.nix36
-rw-r--r--modules/nixfiles/wireguard.nix208
-rw-r--r--modules/nixfiles/x11.nix110
-rw-r--r--modules/nixfiles/xmonad.nix28
-rw-r--r--modules/nixfiles/zathura.nix123
-rw-r--r--packages/bruh.nix17
-rw-r--r--packages/mpv-autosub.nix28
-rw-r--r--packages/myip.nix12
-rw-r--r--packages/nixfiles.nix82
-rw-r--r--packages/throttled.nix36
-rw-r--r--packages/ultimmc.nix63
-rw-r--r--pyproject.toml3
-rw-r--r--readme.org42
-rw-r--r--tox.ini2
145 files changed, 11576 insertions, 0 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..29b9040
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,20 @@
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+insert_final_newline = true
+max_line_length = 80
+trim_trailing_whitespace = true
+
+[*.nix]
+indent_size = 2
+indent_style = space
+
+[*.{yaml,yml}]
+indent_size = 2
+indent_style = space
+
+[*.{toml,tml}]
+indent_size = 4
+indent_style = space
diff --git a/.envrc b/.envrc
new file mode 100644
index 0000000..3550a30
--- /dev/null
+++ b/.envrc
@@ -0,0 +1 @@
+use flake
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a0e2b39
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*.qcow2
+.direnv/
+.pre-commit-config.yaml
+/result*
diff --git a/.shellcheckrc b/.shellcheckrc
new file mode 100644
index 0000000..53af2a0
--- /dev/null
+++ b/.shellcheckrc
@@ -0,0 +1,2 @@
+shell=bash
+disable=SC2164
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..5a8e332
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,14 @@
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ Version 2, December 2004
+
+ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
+
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
+
diff --git a/configurations/default.nix b/configurations/default.nix
new file mode 100644
index 0000000..e0741dc
--- /dev/null
+++ b/configurations/default.nix
@@ -0,0 +1,139 @@
+{
+ inputs,
+ lib,
+}:
+with lib; {
+ # TODO Make a generic wrapper for all hosts.
+ melian = let
+ system = "x86_64-linux";
+ in
+ nixosSystem {
+ inherit system;
+
+ modules =
+ attrValues inputs.self.nixosModules
+ ++ [./melian]
+ ++ (with inputs;
+ (with nixos-hardware.nixosModules; [
+ common-pc-laptop-ssd
+ lenovo-thinkpad-t480
+ ])
+ ++ [nixpkgs.nixosModules.notDetected]);
+
+ specialArgs = rec {
+ inherit inputs lib;
+ this = my.configurations.melian;
+ };
+ };
+
+ manwe = let
+ system = "x86_64-linux";
+ in
+ nixosSystem {
+ inherit system;
+
+ modules =
+ attrValues inputs.self.nixosModules
+ ++ [./manwe]
+ ++ (with inputs;
+ (with nixos-hardware.nixosModules; [common-cpu-amd common-pc-ssd])
+ ++ [
+ nixpkgs.nixosModules.notDetected
+ "${nixpkgs}/nixos/modules/profiles/qemu-guest.nix"
+ ]);
+
+ specialArgs = rec {
+ inherit inputs lib;
+ this = my.configurations.manwe;
+ };
+ };
+
+ varda = let
+ system = "x86_64-linux";
+ in
+ nixosSystem {
+ inherit system;
+
+ modules =
+ attrValues inputs.self.nixosModules
+ ++ [./varda]
+ ++ (with inputs;
+ (with nixos-hardware.nixosModules; [common-cpu-amd common-pc-ssd])
+ ++ [
+ nixpkgs.nixosModules.notDetected
+ "${nixpkgs}/nixos/modules/profiles/qemu-guest.nix"
+ ]);
+
+ specialArgs = rec {
+ inherit inputs lib;
+ this = my.configurations.varda;
+ };
+ };
+
+ yavanna = let
+ system = "x86_64-linux";
+ in
+ nixosSystem {
+ inherit system;
+
+ modules =
+ attrValues inputs.self.nixosModules
+ ++ [./yavanna]
+ ++ (with inputs;
+ (with nixos-hardware.nixosModules; [common-cpu-intel common-pc-hdd])
+ ++ [nixpkgs.nixosModules.notDetected]);
+
+ specialArgs = rec {
+ inherit inputs lib;
+ this = my.configurations.yavanna;
+ };
+ };
+
+ test-headless = let
+ system = "x86_64-linux";
+ in
+ nixosSystem {
+ inherit system;
+
+ modules =
+ attrValues inputs.self.nixosModules
+ ++ [./test-headless]
+ ++ (with inputs; [
+ nixpkgs.nixosModules.notDetected
+ "${nixpkgs}/nixos/modules/profiles/qemu-guest.nix"
+ ]);
+
+ specialArgs = rec {
+ inherit inputs lib;
+ this = {
+ hostname = "test-headless";
+ isHeadless = true;
+ isHeadful = false;
+ };
+ };
+ };
+
+ test-headful = let
+ system = "x86_64-linux";
+ in
+ nixosSystem {
+ inherit system;
+
+ modules =
+ attrValues inputs.self.nixosModules
+ ++ [./test-headful]
+ ++ (with inputs; [
+ nixpkgs.nixosModules.notDetected
+ "${nixpkgs}/nixos/modules/profiles/qemu-guest.nix"
+ ]);
+
+ specialArgs = rec {
+ inherit inputs lib;
+ this = {
+ hostname = "test-headful";
+ isHeadful = true;
+ isHeadless = false;
+ };
+ };
+ };
+}
diff --git a/configurations/manwe/default.nix b/configurations/manwe/default.nix
new file mode 100644
index 0000000..50acacd
--- /dev/null
+++ b/configurations/manwe/default.nix
@@ -0,0 +1,171 @@
+{
+ config,
+ inputs,
+ lib,
+ this,
+ ...
+}:
+with lib; {
+ imports = [
+ ./mailserver.nix
+ ./webserver.nix
+ ];
+
+ secrets.wireguard-private-key-manwe.file = "${inputs.self}/secrets/wireguard-private-key-manwe";
+
+ nixfiles.modules = {
+ nsd = {
+ enable = true;
+ fqdn = "ns1.${config.networking.domain}";
+ };
+ unbound.enable = true;
+
+ wireguard = {
+ privateKeyFile = config.secrets.wireguard-private-key-manwe.path;
+ server.enable = true;
+ };
+
+ acme.enable = true;
+
+ monitoring.enable = true;
+
+ gotify.enable = true;
+ matrix.dendrite = {
+ enable = true;
+ domain = my.domain.azahi;
+ };
+ soju = {
+ enable = true;
+ domain = my.domain.azahi;
+ };
+ };
+
+ services.prometheus = {
+ # TODO Make this generic.
+ scrapeConfigs = with my.configurations;
+ with config.services.prometheus.exporters; [
+ {
+ job_name = "endlessh-go";
+ static_configs = [
+ {
+ targets = with config.nixfiles.modules.endlessh-go; [
+ "${manwe.hostname}:${toString prometheusPort}"
+ "${varda.hostname}:${toString prometheusPort}"
+ "${yavanna.hostname}:${toString prometheusPort}"
+ ];
+ }
+ ];
+ }
+ {
+ job_name = "nginx";
+ static_configs = [
+ {
+ targets = with nginx; [
+ "${manwe.hostname}:${toString port}"
+ "${varda.hostname}:${toString port}"
+ "${yavanna.hostname}:${toString port}"
+ ];
+ }
+ ];
+ }
+ {
+ job_name = "node";
+ static_configs = [
+ {
+ targets = with node; [
+ "${manwe.hostname}:${toString port}"
+ "${varda.hostname}:${toString port}"
+ "${yavanna.hostname}:${toString port}"
+ ];
+ }
+ ];
+ }
+ {
+ job_name = "postgres";
+ static_configs = [
+ {
+ targets = with postgres; [
+ "${manwe.hostname}:${toString port}"
+ "${varda.hostname}:${toString port}"
+ ];
+ }
+ ];
+ }
+ {
+ job_name = "unbound";
+ static_configs = [
+ {
+ targets = with unbound; ["${manwe.hostname}:${toString port}"];
+ }
+ ];
+ }
+ # {
+ # job_name = "wireguard";
+ # static_configs = [{
+ # targets = with wireguard; [
+ # "${manwe.hostname}:${toString port}"
+ # ];
+ # }];
+ # }
+ ];
+ };
+
+ networking = let
+ interface = "eth0";
+ in {
+ interfaces.${interface} = {
+ ipv4.addresses = [
+ {
+ inherit (this.ipv4) address;
+ prefixLength = 22;
+ }
+ ];
+
+ ipv6.addresses = [
+ {
+ inherit (this.ipv6) address;
+ prefixLength = 64;
+ }
+ ];
+ };
+
+ defaultGateway = {
+ inherit interface;
+ address = this.ipv4.gateway;
+ };
+
+ defaultGateway6 = {
+ inherit interface;
+ address = this.ipv6.gateway;
+ };
+
+ nat.externalInterface = interface;
+ };
+
+ boot = {
+ loader.grub = {
+ enable = true;
+ device = "/dev/sda";
+ };
+
+ initrd.availableKernelModules = ["ata_piix" "uhci_hcd" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod"];
+ };
+
+ fileSystems = {
+ "/boot" = {
+ device = "/dev/sda1";
+ fsType = "xfs";
+ options = ["noatime"];
+ };
+
+ "/" = {
+ device = "/dev/sda2";
+ fsType = "xfs";
+ options = ["noatime"];
+ };
+ };
+
+ swapDevices = [{device = "/dev/sda3";}];
+
+ system.stateVersion = "22.05";
+}
diff --git a/configurations/manwe/mailserver.nix b/configurations/manwe/mailserver.nix
new file mode 100644
index 0000000..60a917b
--- /dev/null
+++ b/configurations/manwe/mailserver.nix
@@ -0,0 +1,96 @@
+{
+ config,
+ inputs,
+ lib,
+ ...
+}:
+with lib; {
+ imports = [inputs.simple-nixos-mailserver.nixosModule];
+
+ config = {
+ secrets = {
+ dkim-key-azahi-cc = {
+ file = "${inputs.self}/secrets/dkim-key-azahi-cc";
+ path = "/var/dkim/${my.domain.azahi}.${config.mailserver.dkimSelector}.key";
+ owner = "opendkim";
+ group = "opendkim";
+ };
+ dkim-key-rohan-net = {
+ file = "${inputs.self}/secrets/dkim-key-rohan-net";
+ path = "/var/dkim/${my.domain.rohan}.${config.mailserver.dkimSelector}.key";
+ owner = "opendkim";
+ group = "opendkim";
+ };
+ dkim-key-gondor-net = {
+ file = "${inputs.self}/secrets/dkim-key-gondor-net";
+ path = "/var/dkim/${my.domain.gondor}.${config.mailserver.dkimSelector}.key";
+ owner = "opendkim";
+ group = "opendkim";
+ };
+ dkim-key-shire-me = {
+ file = "${inputs.self}/secrets/dkim-key-shire-me";
+ path = "/var/dkim/${my.domain.shire}.${config.mailserver.dkimSelector}.key";
+ owner = "opendkim";
+ group = "opendkim";
+ };
+ };
+
+ nixfiles.modules.acme.enable = true;
+
+ mailserver = let
+ cert = config.certs.${my.domain.shire};
+ in {
+ enable = true;
+
+ fqdn = config.networking.domain;
+ domains = with my.domain; [azahi gondor rohan shire];
+
+ localDnsResolver = false;
+
+ certificateScheme = 1;
+ certificateFile = "${cert.directory}/fullchain.pem";
+ keyFile = "${cert.directory}/key.pem";
+
+ lmtpSaveToDetailMailbox = "no";
+
+ loginAccounts = with my.domain; {
+ "azahi@${shire}" = {
+ hashedPassword = "[REDACTED]";
+ aliases = [
+ "@${azahi}"
+ "@${rohan}"
+ "@${gondor}"
+ "abuse@${shire}"
+ "admin@${shire}"
+ "ceo@${shire}"
+ "postmaster@${shire}"
+ ];
+ };
+ "samwise@${shire}" = {
+ hashedPassword = "[REDACTED]";
+ aliases = ["chad@${shire}"];
+ quota = "1G";
+ };
+ "pippin@${shire}" = {
+ hashedPassword = "[REDACTED]";
+ quota = "1G";
+ };
+ "meriadoc@${shire}" = {
+ hashedPassword = "[REDACTED]";
+ quota = "1G";
+ };
+ };
+ };
+
+ services.fail2ban.jails = {
+ dovecot = ''
+ enabled = true
+ mode = aggressive
+ '';
+ postfix = ''
+ enabled = true
+ mode = aggressive
+ '';
+ };
+ };
+}
diff --git a/configurations/manwe/webserver.nix b/configurations/manwe/webserver.nix
new file mode 100644
index 0000000..fd23432
--- /dev/null
+++ b/configurations/manwe/webserver.nix
@@ -0,0 +1,21 @@
+{
+ inputs,
+ lib,
+ ...
+}:
+with lib; {
+ config.nixfiles.modules.nginx.virtualHosts = with my.domain;
+ {
+ ${shire}.locations."/".return = "301 https://www.youtube.com/watch?v=dQw4w9WgXcQ";
+ ${azahi} = {
+ serverAliases = ["frodo.${gondor}" "frodo.${rohan}"];
+ locations."/".root = inputs.azahi-cc;
+ };
+ }
+ // (let
+ frodo = "301 https://frodo.";
+ in {
+ ${gondor}.locations."/".return = concatStrings [frodo gondor];
+ ${rohan}.locations."/".return = concatStrings [frodo rohan];
+ });
+}
diff --git a/configurations/melian/default.nix b/configurations/melian/default.nix
new file mode 100644
index 0000000..2f3da98
--- /dev/null
+++ b/configurations/melian/default.nix
@@ -0,0 +1,288 @@
+{
+ config,
+ inputs,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; {
+ secrets = {
+ wireguard-private-key-melian.file = "${inputs.self}/secrets/wireguard-private-key-melian";
+
+ syncthing-cert-melian = with config.services.syncthing; {
+ file = "${inputs.self}/secrets/syncthing-cert-melian";
+ owner = user;
+ inherit group;
+ };
+
+ syncthing-key-melian = with config.services.syncthing; {
+ file = "${inputs.self}/secrets/syncthing-key-melian";
+ owner = user;
+ inherit group;
+ };
+ };
+
+ nixfiles.modules = {
+ profiles.dev = {
+ containers.enable = true;
+ sql.enable = true;
+ };
+
+ games = {
+ lutris.enable = true;
+ steam.enable = true;
+ };
+
+ wireguard = {
+ privateKeyFile = config.secrets.wireguard-private-key-melian.path;
+ client.enable = true;
+ };
+
+ syncthing = with config.secrets; {
+ enable = true;
+ key = syncthing-key-melian.path;
+ cert = syncthing-cert-melian.path;
+ };
+ # ipfs.enable = true; # High CPU usage is really anoying.
+
+ beets.enable = true;
+ bluetooth.enable = true;
+ chromium.enable = true;
+ libvirtd.enable = true;
+ mpd.enable = true;
+ qutebrowser.enable = true;
+
+ throttled.enable = true;
+ };
+
+ hm = {
+ home.packages = with pkgs; [
+ (aspellWithDicts (p: with p; [en ru]))
+ calibre
+ iaito
+ kotatogram-desktop
+ nheko
+ tor-browser
+ ];
+
+ accounts.email = {
+ maildirBasePath = "${config.my.home}/mail";
+
+ # TODO Move to my.nix.
+ accounts = let
+ base = {
+ mbsync = {
+ enable = true;
+ create = "both";
+ expunge = "both";
+ patterns = ["*"];
+ };
+ msmtp.enable = true;
+ mu.enable = true;
+ };
+
+ pass = path: "${pkgs.pass}/bin/pass show ${path}";
+ in {
+ shire =
+ base
+ // {
+ address = my.email;
+ gpg = {
+ inherit (my.pgp) key;
+ signByDefault = true;
+ encryptByDefault = false;
+ };
+
+ primary = true;
+
+ imap.host = "shire.me";
+ smtp.host = "shire.me";
+ userName = "azahi@shire.me";
+ passwordCommand = pass "email/shire.me/azahi";
+ };
+
+ yahoo =
+ base
+ // {
+ address = "a.gondor@yahoo.com";
+
+ imap.host = "imap.yahoo.com";
+ smtp.host = "smtp.yahoo.com";
+ userName = "a.gondor@yahoo.com";
+ passwordCommand = pass "email/yahoo.com/a.gondor";
+ };
+ };
+ };
+
+ programs = {
+ mbsync.enable = true;
+ msmtp.enable = true;
+ mu.enable = true;
+
+ ssh.matchBlocks = let
+ mkBlock = {
+ hostname,
+ port ? 22022,
+ user ? "azahi",
+ }: {
+ inherit hostname port user;
+ identityFile = "${config.my.home}/.ssh/id_ed25519";
+ };
+ mkInternalHostname = hostname: "${hostname}.${my.domain.shire}";
+ in
+ with my.configurations; {
+ # TODO Wrap this.
+ manwe = mkBlock {hostname = mkInternalHostname "manwe";};
+ manwe-init = mkBlock {hostname = manwe.ipv4.address;};
+ varda = mkBlock {hostname = mkInternalHostname "varda";};
+ varda-init = mkBlock {hostname = varda.ipv4.address;};
+ yavanna = mkBlock {hostname = mkInternalHostname "yavanna";};
+ yavanna-init = mkBlock {hostname = yavanna.ipv4.address;};
+ };
+ };
+ };
+
+ networking = {
+ interfaces = {
+ eth0.useDHCP = true;
+ wlan0.useDHCP = true;
+ };
+
+ wireless = {
+ enable = false;
+ iwd.enable = true;
+ userControlled.enable = true;
+ allowAuxiliaryImperativeNetworks = true;
+ };
+ };
+
+ fileSystems = {
+ "/" = {
+ device = "/dev/disk/by-uuid/bb8b09dc-cc67-47e5-8280-532b17a9e62a";
+ fsType = "xfs";
+ options = ["noatime"];
+ };
+
+ "/boot" = {
+ device = "/dev/disk/by-uuid/1083-C8A0";
+ fsType = "vfat";
+ };
+ };
+
+ # Hibernation is not enabled because I don't use it.
+ swapDevices = [
+ {
+ device = "/swapfile";
+ size = 8 * 1024;
+ }
+ ];
+
+ boot = {
+ kernelPackages = mkForce pkgs.linuxPackages_xanmod_latest;
+
+ # https://www.phoronix.com/scan.php?page=article&item=spectre-meltdown-2&num=11
+ kernelParams = ["mitigations=off"];
+
+ # Just to make Lutris/Wine logs happy.
+ kernel.sysctl."dev.i915.perf_stream_paranoid" = 0;
+
+ extraModprobeConfig = ''
+ options iwlwifi 11n_disable=1
+ '';
+
+ initrd = {
+ kernelModules = [];
+ availableKernelModules = ["ahci" "nvme" "sd_mod" "usb_storage" "usbhid" "xhci_pci"];
+
+ luks.devices."root" = {
+ device = "/dev/disk/by-uuid/c1b46f24-eec0-47d2-a142-75ddfd7bb218";
+ allowDiscards = true;
+ bypassWorkqueues = true;
+ };
+ };
+ kernelModules = ["kvm-intel"];
+
+ loader = {
+ efi.canTouchEfiVariables = true;
+
+ systemd-boot = {
+ enable = true;
+ configurationLimit = 10;
+ };
+ };
+ };
+
+ hardware = {
+ trackpoint = {
+ enable = true;
+ speed = 500;
+ sensitivity = 250;
+ };
+
+ opengl.extraPackages = with pkgs; [
+ intel-media-driver
+ libvdpau-va-gl
+ vaapiIntel
+ vaapiVdpau
+ ];
+ };
+
+ powerManagement = let
+ modprobe = "${pkgs.kmod}/bin/modprobe";
+ in {
+ enable = true;
+
+ # This fixes an issue with not being able to suspend or wake up from suspend
+ # due to a kernel bug[1].
+ #
+ # [1]: https://bbs.archlinux.org/viewtopic.php?id=270964
+ # [1]: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/522998
+ # [1]: https://bugs.launchpad.net/ubuntu/+source/pm-utils/+bug/562484/comments/3
+ # [1]: https://gist.github.com/ioggstream/8f380d398aef989ac455b93b92d42048
+ powerDownCommands = "${modprobe} -r xhci_pci";
+ powerUpCommands = "${modprobe} xhci_pci";
+ };
+
+ services = {
+ tlp = {
+ enable = false;
+ settings = {
+ START_CHARGE_THRESH_BAT0 = 75;
+ STOP_CHARGE_THRESH_BAT0 = 80;
+ RESTORE_THRESHOLDS_ON_BAT = 1;
+ };
+ };
+
+ throttled.enable = mkForce false;
+
+ thinkfan = {
+ enable = true;
+
+ # Old-style configuration should be nullified because it does not support
+ # hwmon search and appends to valid configuration resulting in conflicts
+ # and crash. I probably should make a PR to remove what was introduced[1]
+ # before.
+ #
+ # [1]: https://github.com/NixOS/nixpkgs/commit/02b872310d6a6503639f5a71a14f00441f961bc9
+ sensors = mkForce null;
+ fans = mkForce null;
+ levels = mkForce null;
+
+ settings = {
+ sensors = [
+ {
+ hwmon = "/sys/class/hwmon";
+ name = "coretemp";
+ indices = [1];
+ }
+ ];
+ fans = [{tpacpi = "/proc/acpi/ibm/fan";}];
+ levels = [["level auto" 0 50] ["level disengaged" 50 32767]];
+ };
+ };
+
+ xserver.videoDrivers = ["intel" "modesetting"];
+ };
+
+ system.stateVersion = "22.05";
+}
diff --git a/configurations/test-headful/default.nix b/configurations/test-headful/default.nix
new file mode 100644
index 0000000..fb3c276
--- /dev/null
+++ b/configurations/test-headful/default.nix
@@ -0,0 +1,27 @@
+{
+ lib,
+ modulesPath,
+ ...
+}:
+with lib; {
+ imports = ["${modulesPath}/profiles/qemu-guest.nix"];
+
+ nixfiles.modules = {
+ dwm.enable = true;
+ kde.enable = false;
+ xmonad.enable = false;
+ };
+
+ boot.loader.systemd-boot.enable = true;
+ fileSystems."/".device = "/dev/null";
+
+ documentation = {
+ enable = mkForce false;
+ man = {
+ enable = mkForce false;
+ man-db.enable = mkForce false;
+ };
+ };
+
+ system.stateVersion = "22.05";
+}
diff --git a/configurations/test-headless/default.nix b/configurations/test-headless/default.nix
new file mode 100644
index 0000000..c085e63
--- /dev/null
+++ b/configurations/test-headless/default.nix
@@ -0,0 +1,22 @@
+{
+ lib,
+ modulesPath,
+ ...
+}:
+with lib; {
+ imports = ["${modulesPath}/profiles/qemu-guest.nix"];
+
+ # This will allow `nix flake check` tests to pass.
+ nixfiles.modules = {
+ endlessh-go.enable = mkForce false;
+ node-exporter.enable = mkForce false;
+ promtail.enable = mkForce false;
+ };
+
+ boot.loader.systemd-boot.enable = true;
+ fileSystems."/".device = "/dev/null";
+
+ documentation.enable = mkForce false;
+
+ system.stateVersion = "22.05";
+}
diff --git a/configurations/varda/default.nix b/configurations/varda/default.nix
new file mode 100644
index 0000000..b9d84f7
--- /dev/null
+++ b/configurations/varda/default.nix
@@ -0,0 +1,95 @@
+{
+ config,
+ inputs,
+ lib,
+ this,
+ ...
+}:
+with lib; {
+ secrets.wireguard-private-key-varda.file = "${inputs.self}/secrets/wireguard-private-key-varda";
+
+ nixfiles.modules = {
+ wireguard = {
+ privateKeyFile = config.secrets.wireguard-private-key-varda.path;
+ client.enable = true;
+ };
+
+ acme.enable = true;
+
+ rss-bridge.enable = true;
+ radicale.enable = true;
+ };
+
+ networking = let
+ interface = "eth0";
+ in {
+ interfaces.${interface} = {
+ ipv4.addresses = [
+ {
+ inherit (this.ipv4) address;
+ prefixLength = 22;
+ }
+ ];
+
+ ipv6.addresses = [
+ {
+ inherit (this.ipv6) address;
+ prefixLength = 64;
+ }
+ ];
+ };
+
+ defaultGateway = {
+ inherit interface;
+ address = this.ipv4.gateway;
+ };
+
+ defaultGateway6 = {
+ inherit interface;
+ address = this.ipv6.gateway;
+ };
+ };
+
+ boot = {
+ loader.grub = {
+ enable = true;
+ device = "/dev/sda";
+ };
+
+ initrd = {
+ luks.devices.nixos = {
+ device = "/dev/sda2";
+ allowDiscards = true;
+ bypassWorkqueues = true;
+ };
+
+ network = {
+ enable = true;
+ ssh = {
+ enable = true;
+ port = head config.services.openssh.ports;
+ hostKeys = map (k: k.path) config.services.openssh.hostKeys;
+ authorizedKeys = config.my.openssh.authorizedKeys.keys;
+ };
+ };
+
+ availableKernelModules = ["ata_piix" "sd_mod" "sr_mod" "uhci_hcd" "virtio_pci" "virtio_scsi"];
+ };
+ };
+
+ fileSystems = {
+ "/boot" = {
+ device = "LABEL=boot";
+ fsType = "xfs";
+ options = ["noatime"];
+ };
+
+ "/" = {
+ device = "LABEL=nixos";
+ fsType = "xfs";
+ options = ["noatime"];
+ };
+ };
+
+ system.stateVersion = "22.05";
+}
diff --git a/configurations/yavanna/default.nix b/configurations/yavanna/default.nix
new file mode 100644
index 0000000..b740da3
--- /dev/null
+++ b/configurations/yavanna/default.nix
@@ -0,0 +1,101 @@
+{
+ config,
+ inputs,
+ lib,
+ this,
+ ...
+}:
+with lib; {
+ secrets = {
+ wireguard-private-key-yavanna.file = "${inputs.self}/secrets/wireguard-private-key-yavanna";
+
+ syncthing-cert-yavanna = with config.services.syncthing; {
+ file = "${inputs.self}/secrets/syncthing-cert-yavanna";
+ owner = user;
+ inherit group;
+ };
+
+ syncthing-key-yavanna = with config.services.syncthing; {
+ file = "${inputs.self}/secrets/syncthing-key-yavanna";
+ owner = user;
+ inherit group;
+ };
+ };
+
+ nixfiles.modules = {
+ wireguard = {
+ privateKeyFile = config.secrets.wireguard-private-key-yavanna.path;
+ client.enable = true;
+ };
+
+ syncthing = with config.secrets; {
+ enable = true;
+ key = syncthing-key-yavanna.path;
+ cert = syncthing-cert-yavanna.path;
+ };
+
+ acme.enable = true;
+
+ rtorrent = {
+ enable = true;
+ flood.enable = true;
+ };
+ };
+
+ networking = let
+ interface = "eth0";
+ in {
+ interfaces.${interface} = {
+ ipv4.addresses = [
+ {
+ inherit (this.ipv4) address;
+ prefixLength = 24;
+ }
+ ];
+
+ ipv6.addresses = [
+ {
+ inherit (this.ipv6) address;
+ prefixLength = 128;
+ }
+ ];
+ };
+
+ defaultGateway = {
+ inherit interface;
+ address = this.ipv4.gateway;
+ };
+
+ defaultGateway6 = {
+ inherit interface;
+ address = this.ipv6.gateway;
+ };
+ };
+
+ boot = {
+ loader.grub = {
+ enable = true;
+ device = "/dev/sda";
+ };
+
+ initrd = {
+ availableKernelModules = ["uhci_hcd" "ahci"];
+ kernelModules = ["nvme"];
+ };
+ };
+
+ fileSystems."/" = {
+ device = "/dev/sda1";
+ fsType = "ext4";
+ options = ["noatime"];
+ };
+
+ swapDevices = [
+ {
+ device = "/swapfile";
+ size = 4 * 1024;
+ }
+ ];
+
+ system.stateVersion = "22.05";
+}
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..f7141a2
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,421 @@
+{
+ "nodes": {
+ "agenix": {
+ "inputs": {
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1652712410,
+ "narHash": "sha256-hMJ2TqLt0DleEnQFGUHK9sV2aAzJPU8pZeiZoqRozbE=",
+ "owner": "ryantm",
+ "repo": "agenix",
+ "rev": "7e5e58b98c3dcbf497543ff6f22591552ebfe65b",
+ "type": "github"
+ },
+ "original": {
+ "owner": "ryantm",
+ "ref": "main",
+ "repo": "agenix",
+ "type": "github"
+ }
+ },
+ "alertmanager-gotify": {
+ "inputs": {
+ "flake-utils": [
+ "flake-utils"
+ ],
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1638488371,
+ "narHash": "sha256-WTzMc8W0+U7ifcaiwejFjnDNoBoK+CcaV6VIBLK3BrI=",
+ "ref": "refs/heads/master",
+ "rev": "b752ae3ca5974ab5a2d19a59c2e2960faeff699c",
+ "revCount": 35,
+ "type": "git",
+ "url": "https://git.mbosch.me/ma27/alertmanager-gotify"
+ },
+ "original": {
+ "type": "git",
+ "url": "https://git.mbosch.me/ma27/alertmanager-gotify"
+ }
+ },
+ "azahi-cc": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1656772709,
+ "narHash": "sha256-WY46rBmxd5oZUYOkSoLHDKcIUzm/gqoP0VsTZgJ+S7w=",
+ "owner": "azahi",
+ "repo": "azahi.cc",
+ "rev": "84169cc00825ff294498f380e0ec28b0bd088c27",
+ "type": "gitlab"
+ },
+ "original": {
+ "owner": "azahi",
+ "ref": "master",
+ "repo": "azahi.cc",
+ "type": "gitlab"
+ }
+ },
+ "blobs": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1604995301,
+ "narHash": "sha256-wcLzgLec6SGJA8fx1OEN1yV/Py5b+U5iyYpksUY/yLw=",
+ "owner": "simple-nixos-mailserver",
+ "repo": "blobs",
+ "rev": "2cccdf1ca48316f2cfd1c9a0017e8de5a7156265",
+ "type": "gitlab"
+ },
+ "original": {
+ "owner": "simple-nixos-mailserver",
+ "repo": "blobs",
+ "type": "gitlab"
+ }
+ },
+ "dns-nix": {
+ "inputs": {
+ "flake-utils": [
+ "flake-utils"
+ ],
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1635273082,
+ "narHash": "sha256-EHiDP2jEa7Ai5ZwIf5uld9RVFcV77+2SUxjQXwJsJa0=",
+ "owner": "kirelagin",
+ "repo": "dns.nix",
+ "rev": "c7b9645da9c0ddce4f9de4ef27ec01bb8108039a",
+ "type": "github"
+ },
+ "original": {
+ "owner": "kirelagin",
+ "ref": "master",
+ "repo": "dns.nix",
+ "type": "github"
+ }
+ },
+ "emacs-overlay": {
+ "inputs": {
+ "flake-utils": [
+ "flake-utils"
+ ],
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1659379767,
+ "narHash": "sha256-cfcutZL9YBqx2uTRfeLpic6baU/nwLlsp/hMnL/boDA=",
+ "owner": "nix-community",
+ "repo": "emacs-overlay",
+ "rev": "e911c43b99c7b9c94ee408c38b0c6e2c6a01132e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "ref": "master",
+ "repo": "emacs-overlay",
+ "type": "github"
+ }
+ },
+ "flake-compat": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1641205782,
+ "narHash": "sha256-4jY7RCWUoZ9cKD8co0/4tFARpWB+57+r1bLLvXNJliY=",
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "rev": "b7547d3eed6f32d06102ead8991ec52ab0a4f1a7",
+ "type": "github"
+ },
+ "original": {
+ "owner": "edolstra",
+ "ref": "master",
+ "repo": "flake-compat",
+ "type": "github"
+ }
+ },
+ "flake-registry": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1657791189,
+ "narHash": "sha256-Z2ckMWqybpJjIdGIPuHcYzTL7DPTbFKpBZJJgGfFUog=",
+ "owner": "NixOS",
+ "repo": "flake-registry",
+ "rev": "7a481cef7e86d6ceb4aec244d9bb085619360ba1",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "master",
+ "repo": "flake-registry",
+ "type": "github"
+ }
+ },
+ "flake-utils": {
+ "locked": {
+ "lastModified": 1656928814,
+ "narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "ref": "master",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "home-manager": {
+ "inputs": {
+ "nixpkgs": [
+ "nixpkgs"
+ ],
+ "utils": [
+ "flake-utils"
+ ]
+ },
+ "locked": {
+ "lastModified": 1659398318,
+ "narHash": "sha256-5wovS14I/DNXwfiMP402Ut2kxI58CO1wD943fboWMDw=",
+ "owner": "nix-community",
+ "repo": "home-manager",
+ "rev": "77648a07e459adff69b2c4033a77b2cababb5843",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "ref": "master",
+ "repo": "home-manager",
+ "type": "github"
+ }
+ },
+ "nixos-hardware": {
+ "locked": {
+ "lastModified": 1659356074,
+ "narHash": "sha256-UwV6hZZEtchvtiTCCD/ODEv1226eam8kEgEyQb7xB0E=",
+ "owner": "NixOS",
+ "repo": "nixos-hardware",
+ "rev": "ea3efc80f8ab83cb73aec39f4e76fe87afb15a08",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "master",
+ "repo": "nixos-hardware",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1659254610,
+ "narHash": "sha256-ee5W5MLWZ3kdx5hwOUs6trOJit+GeTDfG+Lg3rANKoc=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "67f49b2a3854e8b5e3f9df4422225daa0985f451",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixpkgs-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs-master": {
+ "locked": {
+ "lastModified": 1659395920,
+ "narHash": "sha256-Am1N2FK8KJWpEg5Opt7xefw5YDRYmalWF8keybhx3pc=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "ec9cb32147fbd46a4082cb119d274a0990caa390",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "master",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs-stable": {
+ "locked": {
+ "lastModified": 1659392573,
+ "narHash": "sha256-YHeOH+cypoB3ZCz7Dnh8/Nolrl2ZMSLsth2U3h+WqJ4=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "760d3360f77eca32cc0cdfea403e89285d82a048",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "release-22.05",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nmap-vulners": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1632035522,
+ "narHash": "sha256-Y3THM8cNVZdl4GVLI0d4c/EgAyBZeNLSlDsUMHD9UYk=",
+ "owner": "vulnersCom",
+ "repo": "nmap-vulners",
+ "rev": "bbf53dd085f8d810921ee00ccf85bdb329d59514",
+ "type": "github"
+ },
+ "original": {
+ "owner": "vulnersCom",
+ "ref": "master",
+ "repo": "nmap-vulners",
+ "type": "github"
+ }
+ },
+ "nmap-vulscan": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1646989909,
+ "narHash": "sha256-hV9gCEQwoA2l7eg2pKFmZeUdPkzeF9tnWTswgw0P4A8=",
+ "owner": "scipag",
+ "repo": "vulscan",
+ "rev": "64bd0049894431d81517801c42b8585f9064a043",
+ "type": "github"
+ },
+ "original": {
+ "owner": "scipag",
+ "ref": "master",
+ "repo": "vulscan",
+ "type": "github"
+ }
+ },
+ "nur": {
+ "locked": {
+ "lastModified": 1659378417,
+ "narHash": "sha256-76cfWXqX2Yh8+BZYIM7ZyI7o/SfNTdz49bgT4UjGnhI=",
+ "owner": "nix-community",
+ "repo": "NUR",
+ "rev": "b7b48e67de148d87c3a0b798977a45eacbaac3d0",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "ref": "master",
+ "repo": "NUR",
+ "type": "github"
+ }
+ },
+ "pre-commit-hooks": {
+ "inputs": {
+ "flake-utils": [
+ "flake-utils"
+ ],
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1658611562,
+ "narHash": "sha256-jktQ3mRrFAiFzzmVxQXh+8IxZOEE4hfr7St3ncXeVy4=",
+ "owner": "cachix",
+ "repo": "pre-commit-hooks.nix",
+ "rev": "f436e6dbc10bb3500775785072a40eefe057b18e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "cachix",
+ "ref": "master",
+ "repo": "pre-commit-hooks.nix",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "agenix": "agenix",
+ "alertmanager-gotify": "alertmanager-gotify",
+ "azahi-cc": "azahi-cc",
+ "dns-nix": "dns-nix",
+ "emacs-overlay": "emacs-overlay",
+ "flake-registry": "flake-registry",
+ "flake-utils": "flake-utils",
+ "home-manager": "home-manager",
+ "nixos-hardware": "nixos-hardware",
+ "nixpkgs": "nixpkgs",
+ "nixpkgs-master": "nixpkgs-master",
+ "nixpkgs-stable": "nixpkgs-stable",
+ "nmap-vulners": "nmap-vulners",
+ "nmap-vulscan": "nmap-vulscan",
+ "nur": "nur",
+ "pre-commit-hooks": "pre-commit-hooks",
+ "simple-nixos-mailserver": "simple-nixos-mailserver",
+ "xmonad-ng": "xmonad-ng"
+ }
+ },
+ "simple-nixos-mailserver": {
+ "inputs": {
+ "blobs": "blobs",
+ "nixpkgs": [
+ "nixpkgs"
+ ],
+ "nixpkgs-22_05": [
+ "nixpkgs-stable"
+ ],
+ "utils": [
+ "flake-utils"
+ ]
+ },
+ "locked": {
+ "lastModified": 1655930346,
+ "narHash": "sha256-ht56HHOzEhjeIgAv5ZNFjSVX/in1YlUs0HG9c1EUXTM=",
+ "owner": "simple-nixos-mailserver",
+ "repo": "nixos-mailserver",
+ "rev": "f535d8123c4761b2ed8138f3d202ea710a334a1d",
+ "type": "gitlab"
+ },
+ "original": {
+ "owner": "simple-nixos-mailserver",
+ "ref": "master",
+ "repo": "nixos-mailserver",
+ "type": "gitlab"
+ }
+ },
+ "xmonad-ng": {
+ "inputs": {
+ "flake-compat": "flake-compat",
+ "flake-utils": [
+ "flake-utils"
+ ],
+ "nixpkgs": [
+ "nixpkgs"
+ ],
+ "pre-commit-hooks": [
+ "pre-commit-hooks"
+ ]
+ },
+ "locked": {
+ "lastModified": 1644967454,
+ "narHash": "sha256-LYdzh5e5nITWbW7lFb9r5xva5k5YQ9rcFIsORgV7zZE=",
+ "owner": "azahi",
+ "repo": "xmonad-ng",
+ "rev": "0a150971e7cc05f518e290ad6d4d21b9311e6147",
+ "type": "gitlab"
+ },
+ "original": {
+ "owner": "azahi",
+ "ref": "master",
+ "repo": "xmonad-ng",
+ "type": "gitlab"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..5f1ad07
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,242 @@
+{
+ description = "nixfiles";
+
+ # `flake.lock` generation is not computable[1], thus it is impossible to add
+ # helper functions to manage inputs.
+ #
+ # [1]: https://github.com/NixOS/nix/issues/5373
+ inputs = {
+ nixpkgs = {
+ type = "github";
+ owner = "NixOS";
+ repo = "nixpkgs";
+ ref = "nixpkgs-unstable";
+ };
+
+ nixpkgs-master = {
+ type = "github";
+ owner = "NixOS";
+ repo = "nixpkgs";
+ ref = "master";
+ };
+
+ nixpkgs-stable = {
+ type = "github";
+ owner = "NixOS";
+ repo = "nixpkgs";
+ ref = "release-22.05";
+ };
+
+ nixos-hardware = {
+ type = "github";
+ owner = "NixOS";
+ repo = "nixos-hardware";
+ ref = "master";
+ };
+
+ nur = {
+ type = "github";
+ owner = "nix-community";
+ repo = "NUR";
+ ref = "master";
+ };
+
+ home-manager = {
+ type = "github";
+ owner = "nix-community";
+ repo = "home-manager";
+ ref = "master";
+ inputs = {
+ nixpkgs.follows = "nixpkgs";
+ utils.follows = "flake-utils";
+ };
+ };
+
+ emacs-overlay = {
+ type = "github";
+ owner = "nix-community";
+ repo = "emacs-overlay";
+ ref = "master";
+ inputs = {
+ flake-utils.follows = "flake-utils";
+ nixpkgs.follows = "nixpkgs";
+ };
+ };
+
+ dns-nix = {
+ type = "github";
+ owner = "kirelagin";
+ repo = "dns.nix";
+ ref = "master";
+ inputs = {
+ flake-utils.follows = "flake-utils";
+ nixpkgs.follows = "nixpkgs";
+ };
+ };
+
+ simple-nixos-mailserver = {
+ type = "gitlab";
+ owner = "simple-nixos-mailserver";
+ repo = "nixos-mailserver";
+ ref = "master";
+ inputs = {
+ nixpkgs-22_05.follows = "nixpkgs-stable";
+ nixpkgs.follows = "nixpkgs";
+ utils.follows = "flake-utils";
+ };
+ };
+
+ alertmanager-gotify = {
+ type = "git";
+ url = "https://git.mbosch.me/ma27/alertmanager-gotify";
+ inputs = {
+ flake-utils.follows = "flake-utils";
+ nixpkgs.follows = "nixpkgs";
+ };
+ };
+
+ flake-utils = {
+ type = "github";
+ owner = "numtide";
+ repo = "flake-utils";
+ ref = "master";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
+
+ flake-registry = {
+ type = "github";
+ owner = "NixOS";
+ repo = "flake-registry";
+ ref = "master";
+ flake = false;
+ };
+
+ agenix = {
+ type = "github";
+ owner = "ryantm";
+ repo = "agenix";
+ ref = "main";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
+
+ pre-commit-hooks = {
+ type = "github";
+ owner = "cachix";
+ repo = "pre-commit-hooks.nix";
+ ref = "master";
+ inputs = {
+ flake-utils.follows = "flake-utils";
+ nixpkgs.follows = "nixpkgs";
+ };
+ };
+
+ xmonad-ng = {
+ type = "gitlab";
+ owner = "azahi";
+ repo = "xmonad-ng";
+ ref = "master";
+ # type = "path";
+ # path = "/home/azahi/src/xmonad-ng";
+ inputs = {
+ flake-utils.follows = "flake-utils";
+ nixpkgs.follows = "nixpkgs";
+ pre-commit-hooks.follows = "pre-commit-hooks";
+ };
+ };
+
+ azahi-cc = {
+ type = "gitlab";
+ owner = "azahi";
+ repo = "azahi.cc";
+ ref = "master";
+ flake = false;
+ };
+
+ nmap-vulners = {
+ type = "github";
+ owner = "vulnersCom";
+ repo = "nmap-vulners";
+ ref = "master";
+ flake = false;
+ };
+
+ nmap-vulscan = {
+ type = "github";
+ owner = "scipag";
+ repo = "vulscan";
+ ref = "master";
+ flake = false;
+ };
+ };
+
+ outputs = inputs:
+ with inputs; let
+ lib = nixpkgs.lib.extend (import ./lib);
+ in
+ flake-utils.lib.eachSystem ["x86_64-linux"]
+ (system: let
+ pkgs = import nixpkgs {
+ inherit system;
+ overlays = [self.overlays.default];
+ };
+ in {
+ packages.default = pkgs.nixfiles.override {
+ nixfilesSrc = ".";
+ };
+
+ apps.${system}.default = {
+ type = "app";
+ package = "${self.packages.${system}.nixfiles}/bin/nixfiles";
+ };
+
+ devShells.default = pkgs.mkShell {
+ inherit (self.checks.${system}.preCommit) shellHook;
+ packages = with pkgs; [
+ pyright
+ python310
+ rnix-lsp
+ ];
+ };
+
+ formatter = pkgs.alejandra;
+
+ checks.preCommit = pre-commit-hooks.lib.${system}.run {
+ src = ./.;
+ hooks = {
+ alejandra.enable = true;
+ black.enable = true;
+ flake8 = {
+ enable = true;
+ entry = "${pkgs.python310Packages.flake8}/bin/flake8";
+ types = ["file" "python"];
+ };
+ isort.enable = true;
+ prettier = {
+ enable = true;
+ types = [];
+ files = "\\.css";
+ };
+ shellcheck.enable = true;
+ shfmt.enable = true;
+ statix.enable = true;
+ };
+ };
+ })
+ // {
+ inherit lib;
+
+ nixosModules.nixfiles = import ./modules/nixfiles;
+
+ nixosConfigurations =
+ import ./configurations {inherit inputs lib;};
+
+ overlays.default = final: _: {
+ UltimMC = final.libsForQt5.callPackage ./packages/ultimmc.nix {};
+ bruh = final.callPackage ./packages/bruh.nix {};
+ mpv-autosub = final.callPackage ./packages/mpv-autosub.nix {};
+ myip = final.callPackage ./packages/myip.nix {};
+ nixfiles = final.callPackage ./packages/nixfiles.nix {};
+ throttled = final.callPackage ./packages/throttled.nix {};
+ };
+ };
+}
diff --git a/lib/default.nix b/lib/default.nix
new file mode 100644
index 0000000..7a8dfbf
--- /dev/null
+++ b/lib/default.nix
@@ -0,0 +1,20 @@
+lib: _: rec {
+ my = import ./my.nix lib;
+
+ dns = import ./dns.nix lib;
+
+ isEven = number:
+ assert (builtins.isInt number) || (builtins.isFloat number);
+ builtins.div number 2 == 0;
+
+ isOdd = !isEven;
+
+ pow = base: exponent:
+ assert (builtins.isInt base) && (builtins.isInt exponent);
+ assert exponent > 0;
+ builtins.foldl' (x: _: x * base) 1 (builtins.genList _ exponent);
+
+ comcat = lib.concatStringsSep ",";
+
+ mkTcpMem = min: ini: max: (lib.concatMapStrings (x: toString x + " ") [(pow 2 min) (pow 2 ini) (pow 2 max)]);
+}
diff --git a/lib/dns.nix b/lib/dns.nix
new file mode 100644
index 0000000..e486f1c
--- /dev/null
+++ b/lib/dns.nix
@@ -0,0 +1,14 @@
+_: {
+ const = {
+ quad9 = {
+ default = ["9.9.9.9" "149.112.112.112" "2620:fe::fe" "2620:fe::9"];
+ unsecured = ["9.9.9.10" "149.112.112.10" "2620:fe::10" "2620:fe::fe:10"];
+ ecs = ["9.9.9.11" "149.112.112.11" "2620:fe::11" "2620:fe::fe:11"];
+ };
+ cloudflare = {
+ default = ["1.1.1.1" "1.0.0.1" "2606:4700:4700::1111" "2606:4700:4700::1001"];
+ blocking = ["1.1.1.2" "1.0.0.2" "2606:4700:4700::1112" "2606:4700:4700::1002"];
+ blockingPlus = ["1.1.1.3" "1.0.0.3" "2606:4700:4700::1113" "2606:4700:4700::1003"];
+ };
+ };
+}
diff --git a/lib/my.nix b/lib/my.nix
new file mode 100644
index 0000000..f7c4141
--- /dev/null
+++ b/lib/my.nix
@@ -0,0 +1,202 @@
+lib:
+with lib;
+ (evalModules {
+ modules = [
+ {
+ options.configurations = mkOption {
+ description = "My configurations.";
+ type = with types;
+ attrsOf (submodule ({name, ...}: {
+ freeformType = attrs;
+ options = let
+ mkConfigurationTypeOption = type:
+ mkOption {
+ description = "Whether the machine is a ${type} one.";
+ type = bool;
+ default = false;
+ };
+ in {
+ hostname = mkOption {
+ description = "The machine's hostname";
+ type = str;
+ default = name;
+ readOnly = true;
+ };
+
+ isHeadless = mkConfigurationTypeOption "headless";
+ isHeadful = mkConfigurationTypeOption "headful";
+ isOther = mkConfigurationTypeOption "other";
+
+ ipv4 = {
+ address = mkOption {
+ description = "The machine's public IPv4 addresses.";
+ type = nullOr str;
+ default = null;
+ };
+ gateway = mkOption {
+ description = "The machine's IPv4 gateway.";
+ type = nullOr str;
+ default = null;
+ };
+ };
+ ipv6 = {
+ address = mkOption {
+ description = "The machine's public IPv6 addresses.";
+ type = nullOr str;
+ default = null;
+ };
+ gateway = mkOption {
+ description = "The machine's IPv6 gateway.";
+ type = nullOr str;
+ default = null;
+ };
+ };
+
+ wireguard = {
+ ipv4.address = mkOption {
+ description = "The machine's internal IPv4 addresses.";
+ type = nullOr str;
+ default = null;
+ };
+ ipv6.address = mkOption {
+ description = "The machine's internal IPv4 addresses.";
+ type = nullOr str;
+ default = null;
+ };
+ publicKey = mkOption {
+ description = "The machine's public key.";
+ type = nullOr str;
+ default = null;
+ };
+ };
+
+ domains = mkOption {
+ description = "External domains that resovle to this address.";
+ type = listOf str;
+ default = [];
+ };
+
+ syncthing.id = mkOption {
+ description = "Syncthing ID.";
+ type = nullOr str;
+ default = null;
+ };
+ };
+ }));
+
+ default = {};
+ };
+
+ config = {
+ _module.freeformType = types.attrs;
+
+ fullname = "Azat Bahawi";
+ username = "azahi";
+ domain = {
+ azahi = "azahi.cc";
+ rohan = "rohan.net";
+ gondor = "gondor.net";
+ shire = "shire.me";
+ };
+ email = "frodo@${my.domain.gondor}";
+ pgp = {
+ key = "[REDACTED]";
+ fingerprint = "[REDACTED]";
+ grip = "[REDACTED]";
+ };
+ ssh = rec {
+ type = "ed25519";
+ id = my.email;
+ key = "ssh-${type} [REDACTED] ${id}";
+ };
+ hashedPassword = "[REDACTED]";
+
+ configurations = {
+ manwe = {
+ isHeadless = true;
+ ipv4 = {
+ address = "[IPv4]";
+ gateway = "[IPv4]";
+ };
+ ipv6 = {
+ address = "[IPv6]";
+ gateway = "[IPv6]";
+ };
+ wireguard = {
+ ipv4.address = "10.69.0.1";
+ ipv6.address = "fd69::0:1";
+ publicKey = "[REDACTED]";
+ };
+ domains = with my.domain; [
+ "frodo.${rohan}"
+ "frodo.${gondor}"
+ "gotify.${shire}"
+ "monitoring.${shire}"
+ azahi
+ rohan
+ gondor
+ shire
+ ];
+ };
+ varda = {
+ isHeadless = true;
+ ipv4 = {
+ address = "[IPv4]";
+ gateway = "[IPv4]";
+ };
+ ipv6 = {
+ address = "[IPv6]";
+ gateway = "[IPv6]";
+ };
+ wireguard = {
+ ipv4.address = "10.69.1.1";
+ ipv6.address = "fd69::1:1";
+ publicKey = "[REDACTED]";
+ };
+ domains = with my.domain; [
+ "radicale.${shire}"
+ "rss-bridge.${shire}"
+ ];
+ };
+ yavanna = {
+ isHeadless = true;
+ ipv4 = {
+ address = "[IPv4]";
+ gateway = "[IPv4]";
+ };
+ ipv6 = {
+ address = "[IPv6]";
+ gateway = "[IPv6]";
+ };
+ wireguard = {
+ ipv4.address = "10.69.1.2";
+ ipv6.address = "fd69::1:2";
+ publicKey = "[REDACTED]";
+ };
+ domains = with my.domain; ["flood.${shire}"];
+ syncthing.id = "[Syncthing ID]";
+ };
+ melian = {
+ isHeadful = true;
+ wireguard = {
+ ipv4.address = "10.69.4.1";
+ ipv6.address = "fd69::4:1";
+ publicKey = "[REDACTED]";
+ };
+ syncthing.id = "[Syncthing ID]";
+ };
+ gothmog = {
+ isOther = true;
+ wireguard = {
+ ipv4.address = "10.69.5.1";
+ ipv6.address = "fd69::5:1";
+ publicKey = "[REDACTED]";
+ };
+ syncthing.id = "[Syncthing ID]";
+ };
+ };
+ };
+ }
+ ];
+ })
+ .config
diff --git a/modules/nixfiles/acme.nix b/modules/nixfiles/acme.nix
new file mode 100644
index 0000000..196a6a5
--- /dev/null
+++ b/modules/nixfiles/acme.nix
@@ -0,0 +1,32 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.acme;
+in {
+ imports = [
+ (mkAliasOptionModule ["certs"] ["security" "acme" "certs"])
+ ];
+
+ options.nixfiles.modules.acme = {
+ enable = mkEnableOption "Whether to enable ACME.";
+
+ email = mkOption {
+ description = "Email for notifications.";
+ type = with types; str;
+ default = "admin+acme@${my.domain.shire}";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ security.acme = {
+ acceptTerms = true;
+ defaults = {
+ inherit (cfg) email;
+ validMinDays = 60;
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/alacritty.nix b/modules/nixfiles/alacritty.nix
new file mode 100644
index 0000000..8b3e646
--- /dev/null
+++ b/modules/nixfiles/alacritty.nix
@@ -0,0 +1,119 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.alacritty;
+in {
+ options.nixfiles.modules.alacritty.enable =
+ mkEnableOption "Whether to enable Alacritty terminal emulator.";
+
+ config = mkIf cfg.enable {
+ hm.programs.alacritty = {
+ enable = true;
+ settings = with config.nixfiles.modules; {
+ window = {
+ padding = with config.fontScheme.monospaceFont; {
+ x = size;
+ y = size;
+ };
+ dynamic_padding = false;
+ decorations =
+ if (kde.enable || gnome.enable)
+ then "full"
+ else "none";
+ };
+ font = with config.fontScheme.monospaceFont; {
+ normal = {
+ inherit family;
+ style = "Regular";
+ };
+ bold = {
+ inherit family;
+ style = "Bold";
+ };
+ italic = {
+ inherit family;
+ style = "Italic";
+ };
+ bold_italic = {
+ inherit family;
+ style = "Bold Italic";
+ };
+ inherit size;
+ };
+ colors = with profiles.common.colourScheme; {
+ primary = {inherit background foreground;};
+ cursor = {
+ text = "CellBackground";
+ cursor = "CellForeground";
+ };
+ vi_mode_cursor = {
+ text = "CellBackground";
+ cursor = "CellForeground";
+ };
+ search = {
+ matches = {
+ foreground = white;
+ background = red;
+ };
+ focused_match = {
+ foreground = red;
+ background = black;
+ };
+ bar = {
+ foreground = black;
+ background = white;
+ };
+ };
+ hints = {
+ start = {
+ foreground = black;
+ background = yellow;
+ };
+ end = {
+ foreground = yellow;
+ background = black;
+ };
+ line_indicator = {
+ foreground = null;
+ background = null;
+ };
+ selection = {
+ text = "CellBackground";
+ background = "CellForeground";
+ };
+ normal = {
+ inherit black red green yellow blue magenta cyan white;
+ };
+ bright = {
+ inherit
+ brightBlack
+ brightRed
+ brightGreen
+ brightYellow
+ brightBlue
+ brightMagenta
+ brightCyan
+ brightWhite
+ ;
+ };
+ };
+ };
+ bell = {
+ duration = 0;
+ command = null; # TODO notify-send?
+ };
+ cursor = {
+ style = {
+ shape = "Block";
+ blinking = "Off";
+ };
+ vi_mode_style = "Block";
+ };
+ selection.save_to_clipboard = true; # TODO autocutsel?
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/alertmanager.nix b/modules/nixfiles/alertmanager.nix
new file mode 100644
index 0000000..e067cd1
--- /dev/null
+++ b/modules/nixfiles/alertmanager.nix
@@ -0,0 +1,81 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.alertmanager;
+in {
+ options.nixfiles.modules.alertmanager = {
+ enable = mkEnableOption "Whether to enable Alertmanager.";
+
+ port = mkOption {
+ description = "Port.";
+ type = with types; port;
+ default = 30112;
+ };
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; nullOr str;
+ default = config.nixfiles.modules.monitoring.domain;
+ };
+
+ path = mkOption {
+ description = "Path.";
+ type = with types; str;
+ default = "/alertmanager";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ nixfiles.modules.nginx = with cfg; {
+ enable = true;
+ virtualHosts.${cfg.domain}.locations.${path} = {
+ proxyPass = "http://127.0.0.1:${toString port}${path}";
+ extraConfig = ''
+ if ($internal != 1) {
+ return 403;
+ }
+ '';
+ };
+ };
+
+ services = let
+ acme = config.nixfiles.modules.acme.enable;
+ in {
+ prometheus.alertmanager = {
+ enable = true;
+
+ listenAddress = "127.0.0.1";
+ inherit (cfg) port;
+
+ extraFlags = [
+ "--web.external-url=http${
+ optionalString acme "s"
+ }://${cfg.domain}${cfg.path}"
+ ];
+
+ # TODO Make an option.
+ configuration = {
+ global = {
+ smtp_from = "alertmanager@${my.domain.shire}";
+ smtp_smarthost = "${my.domain.shire}:584";
+ };
+
+ route = {
+ receiver = my.username;
+ group_by = ["alertname"];
+ };
+
+ receivers = [
+ {
+ name = my.username;
+ email_configs = [{to = "${my.username}+alert@${my.domain.shire}";}];
+ }
+ ];
+ };
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/aria2.nix b/modules/nixfiles/aria2.nix
new file mode 100644
index 0000000..87f2109
--- /dev/null
+++ b/modules/nixfiles/aria2.nix
@@ -0,0 +1,48 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.aria2;
+in {
+ options.nixfiles.modules.aria2.enable =
+ mkEnableOption "Whether to enable aria2.";
+
+ config = mkIf cfg.enable {
+ hm = {
+ programs.aria2 = {
+ enable = true;
+
+ settings = {
+ bt-max-peers = 128;
+ bt-save-metadata = true;
+ continue = true;
+ enable-dht = true;
+ enable-peer-exchange = true;
+ enable-rpc = false;
+ follow-torrent = true;
+ log-level = "info";
+ max-connection-per-server = 16;
+ max-overall-upload-limit = "1K";
+ max-tries = 5;
+ max-upload-limit = "1K";
+ seed-ratio = 0.1;
+ seed-time = 0.1;
+ stream-piece-selector = "default";
+ timeout = 60;
+ };
+ };
+
+ xdg.desktopEntries.aria2c = {
+ name = "aria2";
+ genericName = "Download Manager";
+ exec = "${pkgs.aria2}/bin/aria2c";
+ terminal = true;
+ categories = ["Application" "Network"];
+ mimeType = ["application/x-bittorrent" "x-scheme-handler/magnet"];
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/aspell.nix b/modules/nixfiles/aspell.nix
new file mode 100644
index 0000000..7669eb9
--- /dev/null
+++ b/modules/nixfiles/aspell.nix
@@ -0,0 +1,23 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.aspell;
+in {
+ options.nixfiles.modules.aspell.enable =
+ mkEnableOption "Whether to enable GNU Aspell.";
+
+ config = mkIf cfg.enable {
+ hm.home = {
+ file.".aspell.conf".text = ''
+ personal /dev/null
+ repl /dev/null
+ '';
+
+ packages = with pkgs; [(aspellWithDicts (p: with p; [en ru]))];
+ };
+ };
+}
diff --git a/modules/nixfiles/bat.nix b/modules/nixfiles/bat.nix
new file mode 100644
index 0000000..d4bb1d6
--- /dev/null
+++ b/modules/nixfiles/bat.nix
@@ -0,0 +1,39 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.bat;
+in {
+ options.nixfiles.modules.bat.enable = mkEnableOption "Whether to enable Bat.";
+
+ config = mkIf cfg.enable {
+ hm.programs = {
+ bat = {
+ enable = true;
+ config = {
+ style = "plain";
+ tabs = "4";
+ theme = "base16";
+ wrap = "never";
+ };
+ };
+
+ bash = {
+ shellAliases = let
+ bat = "${pkgs.bat}/bin/bat";
+ in {
+ bay = "${bat} --language=yaml --tabs 2";
+ baj = "${bat} --language=json --tabs 2";
+ };
+
+ initExtra = mkAfter ''
+ _complete_alias bay _bat bat
+ _complete_alias baj _bat bat
+ '';
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/beets.nix b/modules/nixfiles/beets.nix
new file mode 100644
index 0000000..de4cd73
--- /dev/null
+++ b/modules/nixfiles/beets.nix
@@ -0,0 +1,73 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.beets;
+in {
+ options.nixfiles.modules.beets.enable =
+ mkEnableOption "Whether to enable beets.";
+
+ config = mkIf cfg.enable {
+ hm = let
+ beetsdir = "${config.hm.xdg.dataHome}/beets";
+ in {
+ home.sessionVariables.BEETSDIR = beetsdir;
+
+ programs = {
+ beets = {
+ enable = true;
+
+ settings = {
+ library = "${beetsdir}/library.db";
+ directory = config.userDirs.music;
+ plugins = "badfiles edit fetchart info mbsync scrub";
+ original_date = true;
+ import = {
+ write = true;
+ copy = true;
+ move = false;
+ bell = true;
+ from_scratch = true;
+ };
+ match = {
+ preferred = {
+ countries = [
+ "JP"
+ "KR"
+ "TW"
+ "HK"
+ "CN"
+ "RU"
+ "NL"
+ "DE"
+ "AT"
+ "GB|UK"
+ "CA"
+ "AU"
+ "NZ"
+ "US"
+ ];
+ original_year = true;
+ };
+ };
+ edit = {
+ albumfields = "album artist albumartist";
+ itemfields = "track title album artist albumartist day month year genre";
+ };
+ fetchart = {
+ auto = true;
+ cautious = true;
+ cover_names = "cover Cover folder Folder art Art album Album front Front";
+ sources = "filesystem coverart itunes amazon albumart wikipedia";
+ };
+ scrub.auto = true;
+ };
+ };
+
+ bash.shellAliases.beet = "${config.hm.programs.beets.package}/bin/beet --config ${config.dirs.config}/beets/config.yaml";
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/bluetooth.nix b/modules/nixfiles/bluetooth.nix
new file mode 100644
index 0000000..04e6b8d
--- /dev/null
+++ b/modules/nixfiles/bluetooth.nix
@@ -0,0 +1,34 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.bluetooth;
+in {
+ options.nixfiles.modules.bluetooth.enable =
+ mkEnableOption "Whether to enable Bluetooth support.";
+
+ config = mkIf cfg.enable {
+ hardware.bluetooth = {
+ enable = true;
+ package = pkgs.bluezFull;
+ settings.General.FastConnectable = true;
+ };
+
+ environment = {
+ etc."bluetooth/input.conf".text = generators.toINI {} {
+ General = {
+ IdleTimeout = 15;
+ UserspaceHID = true;
+ };
+ };
+
+ systemPackages = with pkgs;
+ with config.nixfiles.modules;
+ optional gnome.enable gnome.gnome-bluetooth
+ ++ optional kde.enable plasma5Packages.bluedevil;
+ };
+ };
+}
diff --git a/modules/nixfiles/broot.nix b/modules/nixfiles/broot.nix
new file mode 100644
index 0000000..19cd1d6
--- /dev/null
+++ b/modules/nixfiles/broot.nix
@@ -0,0 +1,18 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.broot;
+in {
+ options.nixfiles.modules.broot.enable =
+ mkEnableOption "Whether to enable broot.";
+
+ config = mkIf cfg.enable {
+ hm.programs.broot = {
+ enable = true;
+ modal = true;
+ };
+ };
+}
diff --git a/modules/nixfiles/chromium.nix b/modules/nixfiles/chromium.nix
new file mode 100644
index 0000000..1ec761c
--- /dev/null
+++ b/modules/nixfiles/chromium.nix
@@ -0,0 +1,42 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.chromium;
+in {
+ options.nixfiles.modules.chromium.enable =
+ mkEnableOption "Whether to enable Chromium.";
+
+ config = mkIf cfg.enable {
+ hm = {
+ # home.sessionVariables.BROWSER = mkOverride 300 "chromium";
+
+ programs.chromium = {
+ enable = true;
+
+ package = pkgs.chromium;
+
+ extensions =
+ [
+ {id = "cjpalhdlnbpafiamejdnhcphjbkeiagm";} # UBlock Origin
+ {id = "clngdbkpkpeebahjckkjfobafhncgmne";} # Stylus
+ {id = "cnojnbdhbhnkbcieeekonklommdnndci";} # Search By Image
+ {id = "doojmbjmlfjjnbmnoijecmcbfeoakpjm";} # NoScript
+ {id = "eimadpbcbfnmbkopoojfekhnkhdbieeh";} # Dark Reader
+ {id = "jinjaccalgkegednnccohejagnlnfdag";} # Violentmonkey
+ {id = "nibjojkomfdiaoajekhjakgkdhaomnch";} # IPFS Companion
+ {id = "nngceckbapebfimnlniiiahkandclblb";} # Bitwarden
+ {id = "pmcmeagblkinmogikoikkdjiligflglb";} # Privacy Redirect
+ ]
+ ++ optional config.nixfiles.modules.kde.enable {
+ id = "cimiefiiaegbelhefglklhhakcgmhkai"; # KDE Plasma Integration
+ };
+ };
+ };
+
+ services.psd.enable = true;
+ };
+}
diff --git a/modules/nixfiles/common/console.nix b/modules/nixfiles/common/console.nix
new file mode 100644
index 0000000..3c73695
--- /dev/null
+++ b/modules/nixfiles/common/console.nix
@@ -0,0 +1,6 @@
+{config, ...}: {
+ console = {
+ earlySetup = true;
+ useXkbConfig = config.services.xserver.enable;
+ };
+}
diff --git a/modules/nixfiles/common/default.nix b/modules/nixfiles/common/default.nix
new file mode 100644
index 0000000..d73ac53
--- /dev/null
+++ b/modules/nixfiles/common/default.nix
@@ -0,0 +1,19 @@
+_: {
+ imports = [
+ ./documentation.nix
+ ./environment.nix
+ ./home-manager.nix
+ ./kernel.nix
+ ./locale.nix
+ ./networking.nix
+ ./nix.nix
+ ./secrets.nix
+ ./security.nix
+ ./services.nix
+ ./shell
+ ./systemd.nix
+ ./tmp.nix
+ ./users.nix
+ ./xdg.nix
+ ];
+}
diff --git a/modules/nixfiles/common/documentation.nix b/modules/nixfiles/common/documentation.nix
new file mode 100644
index 0000000..344d59d
--- /dev/null
+++ b/modules/nixfiles/common/documentation.nix
@@ -0,0 +1,48 @@
+{
+ config,
+ lib,
+ pkgs,
+ this,
+ ...
+}:
+with lib; {
+ config = mkMerge [
+ (mkIf this.isHeadful {
+ documentation = {
+ enable = true;
+
+ dev.enable = true;
+ doc.enable = false;
+ info.enable = false;
+ nixos.enable = true;
+
+ man = {
+ enable = true;
+ generateCaches = true;
+ man-db = {
+ enable = true;
+ manualPages =
+ (pkgs.buildEnv {
+ name = "man-paths";
+ paths = with config;
+ environment.systemPackages ++ hm.home.packages;
+ pathsToLink = ["/share/man"];
+ extraOutputsToInstall = ["man"];
+ ignoreCollisions = true;
+ })
+ .overrideAttrs (_: _: {__contentAddressed = true;});
+ };
+ };
+ };
+
+ environment.sessionVariables = {
+ MANOPT = "--no-hyphenation";
+ MANPAGER = "${pkgs.less}/bin/less -+F";
+ };
+ })
+ (mkIf this.isHeadless {
+ hm.manual.manpages.enable = false;
+ documentation.enable = false;
+ })
+ ];
+}
diff --git a/modules/nixfiles/common/environment.nix b/modules/nixfiles/common/environment.nix
new file mode 100644
index 0000000..9998441
--- /dev/null
+++ b/modules/nixfiles/common/environment.nix
@@ -0,0 +1,7 @@
+{lib, ...}:
+with lib; {
+ environment = {
+ localBinInPath = true;
+ defaultPackages = mkForce [];
+ };
+}
diff --git a/modules/nixfiles/common/home-manager.nix b/modules/nixfiles/common/home-manager.nix
new file mode 100644
index 0000000..e4c5d3b
--- /dev/null
+++ b/modules/nixfiles/common/home-manager.nix
@@ -0,0 +1,23 @@
+{
+ config,
+ inputs,
+ lib,
+ ...
+}:
+with lib; {
+ imports = [
+ inputs.home-manager.nixosModules.home-manager
+ (mkAliasOptionModule ["hm"] ["home-manager" "users" my.username])
+ ];
+
+ hm.home = {inherit (config.system) stateVersion;};
+
+ home-manager = {
+ backupFileExtension = "bak";
+ useUserPackages = true;
+ useGlobalPkgs = true;
+ verbose = true;
+ };
+
+ system.extraDependencies = [inputs.home-manager];
+}
diff --git a/modules/nixfiles/common/kernel.nix b/modules/nixfiles/common/kernel.nix
new file mode 100644
index 0000000..f6c096b
--- /dev/null
+++ b/modules/nixfiles/common/kernel.nix
@@ -0,0 +1,32 @@
+{lib, ...}:
+with lib; {
+ boot = {
+ kernelParams = ["hibernate=no"];
+
+ kernel.sysctl = {
+ "fs.file-max" = pow 2 17;
+ "fs.inotify.max_user_watches" = pow 2 19;
+ "fs.suid_dumpable" = 0;
+ "kernel.core_uses_pid" = 1;
+ "kernel.exec-shield" = 1;
+ "kernel.kptr_restrict" = 1;
+ "kernel.maps_protect" = 1;
+ "kernel.msgmax" = pow 2 16;
+ "kernel.msgmnb" = pow 2 16;
+ "kernel.pid_max" = pow 2 16;
+ "kernel.randomize_va_space" = 2;
+ "kernel.shmall" = pow 2 28;
+ "kernel.shmmax" = pow 2 28;
+ "kernel.sysrq" = 0;
+ "vm.dirty_background_bytes" = pow 2 22;
+ "vm.dirty_background_ratio" = 5;
+ "vm.dirty_bytes" = pow 2 22;
+ "vm.dirty_ratio" = 30;
+ "vm.min_free_kbytes" = pow 2 16;
+ "vm.mmap_min_addr" = pow 2 12;
+ "vm.overcommit_memory" = mkDefault 0;
+ "vm.overcommit_ratio" = mkDefault 50;
+ "vm.vfs_cache_pressure" = 50;
+ };
+ };
+}
diff --git a/modules/nixfiles/common/locale.nix b/modules/nixfiles/common/locale.nix
new file mode 100644
index 0000000..34a738b
--- /dev/null
+++ b/modules/nixfiles/common/locale.nix
@@ -0,0 +1,18 @@
+{lib, ...}:
+with lib; {
+ i18n.defaultLocale = mkDefault "en_GB.UTF-8";
+
+ time.timeZone = mkDefault "Europe/Moscow";
+
+ # TODO Fcitx or UIM as a Japanese IME.
+ services.xserver = {
+ layout = comcat ["us" "ru"];
+ xkbVariant = comcat ["" "phonetic"];
+ xkbOptions = comcat [
+ "terminate:ctrl_alt_bksp"
+ "caps:escape"
+ "compose:menu"
+ "grp:win_space_toggle"
+ ];
+ };
+}
diff --git a/modules/nixfiles/common/networking.nix b/modules/nixfiles/common/networking.nix
new file mode 100644
index 0000000..0ff7e3d
--- /dev/null
+++ b/modules/nixfiles/common/networking.nix
@@ -0,0 +1,66 @@
+{
+ config,
+ lib,
+ pkgs,
+ this,
+ ...
+}:
+with lib; {
+ hm.home.file.".digrc".text = ''
+ +answer
+ +multiline
+ +recurse
+ '';
+
+ networking = {
+ hostName = this.hostname;
+ hostId = substring 0 8 (builtins.hashString "md5" this.hostname);
+ domain = my.domain.shire;
+
+ usePredictableInterfaceNames = false;
+
+ useDHCP = false;
+
+ nameservers = dns.const.quad9.default;
+
+ hosts = {
+ "127.0.0.2" = mkForce [];
+ "::1" = mkForce [];
+ };
+
+ firewall = {
+ enable = true;
+
+ logRefusedConnections = false;
+ logRefusedPackets = false;
+
+ rejectPackets = false;
+
+ allowPing = config.nixfiles.modules.profiles.headless.enable;
+ };
+ };
+
+ environment = {
+ systemPackages = with pkgs; [dnsutils ldns myip rsync];
+
+ shellAliases = listToAttrs (map
+ ({
+ name,
+ value,
+ }:
+ nameValuePair name "${pkgs.iproute2}/bin/${value}") [
+ {
+ name = "bridge";
+ value = "bridge -color=always";
+ }
+ {
+ name = "ip";
+ value = "ip -color=always";
+ }
+ {
+ name = "tc";
+ value = "tc -color=always";
+ }
+ ]);
+ };
+}
diff --git a/modules/nixfiles/common/nix.nix b/modules/nixfiles/common/nix.nix
new file mode 100644
index 0000000..cc050f8
--- /dev/null
+++ b/modules/nixfiles/common/nix.nix
@@ -0,0 +1,173 @@
+{
+ config,
+ inputs,
+ lib,
+ pkgs,
+ this,
+ ...
+}:
+with lib; let
+ nixfilesSrc = "${config.my.home}/src/nixfiles";
+in {
+ _module.args = let
+ importNixpkgs = nixpkgs:
+ import nixpkgs {
+ inherit (config.nixpkgs) localSystem crossSystem config;
+ };
+ in rec {
+ pkgsMaster = importNixpkgs inputs.nixpkgs-master;
+ pkgsStable = importNixpkgs inputs.nixpkgs-stable;
+ pkgsRev = rev: sha256:
+ importNixpkgs (pkgs.fetchFromGitHub {
+ owner = "NixOS";
+ repo = "nixpkgs";
+ inherit rev sha256;
+ });
+ pkgsPR = pr: pkgsRev "refs/pull/${toString pr}/head";
+ pkgsLocal = importNixpkgs "${config.my.home}/src/nixpkgs";
+ };
+
+ nix = let
+ filteredInputs = filterAttrs (n: _: n != "self") inputs;
+ in {
+ # https://github.com/NixOS/nix/blob/master/src/libutil/experimental-features.cc
+ extraOptions = ''
+ extra-experimental-features = ca-derivations
+ extra-experimental-features = flakes
+ extra-experimental-features = nix-command
+ extra-experimental-features = recursive-nix
+ flake-registry = ${inputs.flake-registry}/flake-registry.json
+ keep-derivations = true
+ keep-outputs = true
+ warn-dirty = false
+ '';
+
+ nixPath =
+ mapAttrsToList (n: v: "${n}=${v}") filteredInputs
+ ++ ["nixfiles=${nixfilesSrc}"];
+
+ registry =
+ mapAttrs (_: flake: {inherit flake;}) filteredInputs
+ // {
+ nixfiles.flake = inputs.self;
+ };
+
+ settings = {
+ trusted-users = ["root" "@wheel"];
+
+ substituters = [
+ "https://azahi.cachix.org"
+ "https://cachix.cachix.org"
+ "https://mic92.cachix.org"
+ "https://nix-community.cachix.org"
+ "https://pre-commit-hooks.cachix.org"
+ ];
+ trusted-public-keys = [
+ "azahi.cachix.org-1:2bayb+iWYMAVw3ZdEpVg+NPOHCXncw7WMQ0ElX1GO3s="
+ "cachix.cachix.org-1:eWNHQldwUO7G2VkjpnjDbWwy4KQ/HNxht7H4SSoMckM="
+ "mic92.cachix.org-1:gi8IhgiT3CYZnJsaW7fxznzTkMUOn1RY4GmXdT/nXYQ="
+ "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
+ "pre-commit-hooks.cachix.org-1:Pkk3Panw5AW24TOv6kz3PvLhlH8puAsJTBbOPmBo7Rc="
+ ];
+ };
+ };
+
+ nixpkgs = {
+ overlays = with inputs; [
+ self.overlays.default
+ (_: super:
+ {
+ nix-bash-completions =
+ super.nix-bash-completions.overrideAttrs
+ (_: _: {
+ postPatch = ''
+ substituteInPlace _nix --replace 'nix nixos-option' 'nixos-option'
+ '';
+ });
+ nix-index = super.nix-index.override {nix = config.nix.package;};
+ logcli = super.grafana-loki.overrideAttrs (_: _: {
+ subPackages = ["cmd/logcli"];
+ preFixup = "";
+ doCheck = false;
+ });
+ helm = super.kubernetes-helm-wrapped.override {
+ plugins = with super.kubernetes-helmPlugins; [
+ helm-diff
+ helm-secrets
+ ];
+ };
+ pgcli = super.pgcli.overrideAttrs (_: _: {
+ # https://github.com/NixOS/nixpkgs/pull/184533
+ postPatch = ''
+ substituteInPlace setup.py \
+ --replace "pgspecial>=1.13.1,<2.0.0" "pgspecial>=1.13.1"
+ '';
+ });
+ }
+ // (with super; let
+ np = nodePackages;
+ in {
+ dockerfile-language-server = np.dockerfile-language-server-nodejs;
+ editorconfig = editorconfig-core-c;
+ inherit (np) bash-language-server;
+ inherit (np) vim-language-server;
+ inherit (np) yaml-language-server;
+ json-language-server = np.vscode-json-languageserver;
+ k3d = kube3d;
+ lua-language-server = sumneko-lua-language-server;
+ nix-language-server = rnix-lsp;
+ telepresence = telepresence2;
+ tor-browser = tor-browser-bundle-bin;
+ }))
+ agenix.overlay
+ emacs-overlay.overlay
+ nur.overlay
+ xmonad-ng.overlay
+ ];
+
+ config.allowUnfree = true;
+ };
+
+ environment = {
+ sessionVariables.NIX_SHELL_PRESERVE_PROMPT = "1";
+
+ etc = {
+ nixpkgs.source = inputs.nixpkgs;
+
+ gc-roots.text =
+ concatMapStrings (x: x + "\n")
+ (with inputs; [nixpkgs nixpkgs-master nixpkgs-stable]);
+ };
+
+ systemPackages = with pkgs;
+ optionals config.profile.headful [
+ (pkgs.nixfiles.override {
+ nix = config.nix.package;
+ inherit nixfilesSrc;
+ })
+ nix-top
+ nix-tree
+ ];
+ };
+
+ hm.home.file.".nix-defexpr/default.nix".text =
+ optionalString this.isHeadful
+ (
+ let
+ hostname = strings.escapeNixIdentifier this.hostname;
+ in ''
+ let
+ self = builtins.getFlake "nixfiles";
+ configurations = self.nixosConfigurations;
+ local = configurations.${hostname};
+ in rec {
+ inherit self;
+ inherit (self) inputs lib;
+ inherit (lib) my;
+ this = my.configurations.${hostname};
+ inherit (local) config;
+ inherit (local.config.system.build) toplevel vm vmWithBootLoader manual;
+ } // configurations // local._module.args
+ ''
+ );
+}
diff --git a/modules/nixfiles/common/secrets.nix b/modules/nixfiles/common/secrets.nix
new file mode 100644
index 0000000..9e59716
--- /dev/null
+++ b/modules/nixfiles/common/secrets.nix
@@ -0,0 +1,47 @@
+{
+ config,
+ inputs,
+ lib,
+ pkgs,
+ this,
+ ...
+}:
+with lib; {
+ imports = [
+ inputs.agenix.nixosModule
+ (mkAliasOptionModule ["secrets"] ["age" "secrets"])
+ ];
+
+ config = {
+ age = {
+ identityPaths =
+ if this.isHeadful
+ then ["${config.my.home}/.ssh/id_${my.ssh.type}"]
+ else
+ map (attr: attr.path) (filter (attr: attr.type == my.ssh.type)
+ config.services.openssh.hostKeys);
+
+ # This can be used to auto-add all secrets, thus eleminating the need to
+ # specify path to each envrypted file. The drawback is that this will
+ # expose *all* secrets to all machines and try to decrypt them all even on
+ # machines where the secret will not be used.
+ #
+ # secrets =
+ # let
+ # secretsSourceDir = "${inputs.self}/age";
+ # in
+ # mapAttrs'
+ # (name: _:
+ # nameValuePair name {
+ # file = "${secretsSourceDir}/${name}";
+ # owner = mkDefault my.username;
+ # group = mkDefault config.my.group;
+ # })
+ # (builtins.readDir secretsSourceDir);
+ };
+
+ environment.systemPackages = with pkgs; [agenix];
+
+ system.extraDependencies = [inputs.agenix];
+ };
+}
diff --git a/modules/nixfiles/common/security.nix b/modules/nixfiles/common/security.nix
new file mode 100644
index 0000000..d47edc9
--- /dev/null
+++ b/modules/nixfiles/common/security.nix
@@ -0,0 +1,19 @@
+_: {
+ security = {
+ sudo = {
+ enable = true;
+ execWheelOnly = true;
+ wheelNeedsPassword = false;
+ extraConfig = ''
+ Defaults env_keep+="SSH_CONNECTION SSH_CLIENT SSH_TTY"
+ '';
+ };
+
+ polkit.extraConfig = ''
+ polkit.addRule(function (action, subject) {
+ if (subject.isInGroup('wheel'))
+ return polkit.Result.YES;
+ });
+ '';
+ };
+}
diff --git a/modules/nixfiles/common/services.nix b/modules/nixfiles/common/services.nix
new file mode 100644
index 0000000..376c87d
--- /dev/null
+++ b/modules/nixfiles/common/services.nix
@@ -0,0 +1,9 @@
+_: {
+ services = {
+ earlyoom.enable = true;
+ haveged.enable = true;
+ irqbalance.enable = true;
+ };
+
+ hardware.ksm.enable = true;
+}
diff --git a/modules/nixfiles/common/shell/default.nix b/modules/nixfiles/common/shell/default.nix
new file mode 100644
index 0000000..7174443
--- /dev/null
+++ b/modules/nixfiles/common/shell/default.nix
@@ -0,0 +1,125 @@
+{
+ lib,
+ pkgs,
+ this,
+ ...
+}:
+with lib; {
+ hm = {
+ programs = {
+ bash = {
+ enable = true;
+
+ shellOptions = [
+ "autocd"
+ "cdspell"
+ "checkjobs"
+ "checkwinsize"
+ "dirspell"
+ "extglob"
+ "globstar"
+ "histappend"
+ "histreedit"
+ "histverify"
+ ];
+
+ profileExtra = ''
+ export _PROFILE_SOURCED=1
+ '';
+
+ initExtra = ''
+ set -o notify
+
+ ${readFile ./functions.bash}
+
+ GRC_ALIASES=true
+ source ${pkgs.grc}/etc/profile.d/grc.sh
+
+ if [ -z $_PROFILE_SOURCED ] && [ -f $HOME/.profile ]; then
+ source $HOME/.profile
+ fi
+ '';
+
+ historyControl = ["ignoredups" "ignorespace"];
+ };
+
+ command-not-found.enable = false;
+
+ dircolors.enable = true;
+ };
+
+ home.packages = with pkgs; [grc];
+ };
+
+ programs.command-not-found.enable = false;
+
+ environment = {
+ shellAliases =
+ listToAttrs
+ (map
+ ({
+ name,
+ value,
+ }:
+ nameValuePair name (with pkgs; let
+ pkg =
+ if this.isHeadful
+ then
+ (coreutils.overrideAttrs (_: super: {
+ patches =
+ super.patches
+ ++ [
+ (fetchpatch {
+ url = "https://raw.githubusercontent.com/jarun/advcpmv/master/advcpmv-0.9-9.1.patch";
+ sha256 = "sha256-d+SRT/R4xmfHLAdOr7m4R3WFiW64P5ZH6iqDvErYCyg=";
+ })
+ ];
+ }))
+ else coreutils;
+ in "${pkg}/bin/coreutils --coreutils-prog=${value}"))
+ (
+ let
+ mkAlias = {
+ name ? head command,
+ command,
+ }: {
+ inherit name;
+ value = concatStringsSep " " command;
+ };
+
+ progressBar = optionalString this.isHeadful "--progress-bar";
+ in [
+ (mkAlias {
+ command = ["cp" "--interactive" "--recursive" progressBar];
+ })
+ (mkAlias {command = ["mv" "--interactive" progressBar];})
+ (mkAlias {command = ["rm" "--interactive=once"];})
+ (mkAlias {command = ["ln" "--interactive"];})
+ (mkAlias {command = ["mkdir" "--parents"];})
+ (mkAlias {command = ["rmdir" "--parents"];})
+ (mkAlias {
+ name = "lower";
+ command = ["tr" "'[:upper:]'" "'[:lower:]'"];
+ })
+ (mkAlias {
+ name = "upper";
+ command = ["tr" "'[:lower:]'" "'[:upper:]'"];
+ })
+ (mkAlias {
+ name = "disk";
+ command = [
+ "df"
+ "--human-readable"
+ "--exclude-type=tmpfs"
+ "--exclude-type=devtmpfs"
+ "2>/dev/null"
+ ];
+ })
+ ]
+ ))
+ // genAttrs ["grep" "egrep" "fgrep"]
+ (name: "${pkgs.gnugrep}/bin/${name} --color=always");
+
+ systemPackages = with pkgs; [bash-completion bc gawk hr moreutils pv];
+ };
+}
diff --git a/modules/nixfiles/common/shell/functions.bash b/modules/nixfiles/common/shell/functions.bash
new file mode 100644
index 0000000..c18104f
--- /dev/null
+++ b/modules/nixfiles/common/shell/functions.bash
@@ -0,0 +1,73 @@
+_complete_alias() {
+ local alias_name=$1
+ local base_function=$2
+ local function_name=_alias_$alias_name
+ shift 2
+ eval "$function_name() {
+ COMP_WORDS=( ${*@Q} \"\${COMP_WORDS[@]:1}\" )
+ (( COMP_CWORD += $# - 1 ))
+ _completion_loader $1
+ $base_function
+ }"
+ complete -F "$function_name" "$alias_name"
+}
+
+function where() {
+ local s
+ s="$(type -P "$1")"
+ realpath "$s"
+}
+_complete_alias where _complete complete
+
+function what() {
+ local s
+ s="$(where "$1")"
+ printf "%s\n" "${s%/*/*}"
+}
+_complete_alias what _complete complete
+
+function cat() {
+ if (($# == 1)) && [[ -d $1 ]]; then
+ ll "$1"
+ else
+ command cat "$@"
+ fi
+}
+
+function cd() {
+ builtin cd "$@" &&
+ if ((${#FUNCNAME[@]} == 1)); then
+ ls
+ fi
+}
+
+function mkcd() {
+ mkdir -p "$1" && builtin cd "$1"
+}
+
+function mvcd() {
+ mv -i -- "$PWD" "$1" && builtin cd .
+}
+
+function bak() {
+ local f
+ for f; do
+ cp -ai -- "$f" "$f.bak"
+ done
+}
+
+function ubak() {
+ local f
+ for f; do
+ [[ $f == *.bak ]] || f="$f.bak"
+ mv -i -- "$f" "${f%.bak}"
+ done
+}
+
+function dec2hex() {
+ printf "0x%X\n" "$1"
+}
+
+function hex2dec() {
+ printf "%d\n" "0x$1"
+}
diff --git a/modules/nixfiles/common/systemd.nix b/modules/nixfiles/common/systemd.nix
new file mode 100644
index 0000000..5c7282d
--- /dev/null
+++ b/modules/nixfiles/common/systemd.nix
@@ -0,0 +1,22 @@
+{pkgs, ...}: {
+ hm.systemd.user.startServices = "sd-switch";
+
+ services.journald.extraConfig = ''
+ SystemMaxUse=5G
+ '';
+
+ systemd = let
+ extraConfig = ''
+ DefaultTimeoutStartSec=30s
+ DefaultTimeoutStopSec=15s
+ '';
+ in {
+ inherit extraConfig;
+ user = {inherit extraConfig;};
+ };
+
+ environment.sessionVariables = {
+ SYSTEMD_PAGER = "${pkgs.less}/bin/less";
+ SYSTEMD_LESS = "FRSXMK";
+ };
+}
diff --git a/modules/nixfiles/common/tmp.nix b/modules/nixfiles/common/tmp.nix
new file mode 100644
index 0000000..3fbf253
--- /dev/null
+++ b/modules/nixfiles/common/tmp.nix
@@ -0,0 +1,18 @@
+_: {
+ systemd.mounts = [
+ {
+ what = "tmpfs";
+ where = "/tmp";
+ type = "tmpfs";
+ mountConfig.Options = [
+ "huge=within_size"
+ "mode=1777"
+ "noatime"
+ "nodev"
+ "nosuid"
+ "rw"
+ "size=25%"
+ ];
+ }
+ ];
+}
diff --git a/modules/nixfiles/common/users.nix b/modules/nixfiles/common/users.nix
new file mode 100644
index 0000000..0878db6
--- /dev/null
+++ b/modules/nixfiles/common/users.nix
@@ -0,0 +1,21 @@
+{lib, ...}:
+with lib; {
+ imports = [(mkAliasOptionModule ["my"] ["users" "users" my.username])];
+
+ users = {
+ mutableUsers = false;
+
+ users = {
+ root.hashedPassword = "[REDACTED]";
+
+ ${my.username} = {
+ isNormalUser = true;
+ uid = 1000;
+ description = my.fullname;
+ inherit (my) hashedPassword;
+ openssh.authorizedKeys.keys = [my.ssh.key];
+ extraGroups = ["wheel"];
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/common/xdg.nix b/modules/nixfiles/common/xdg.nix
new file mode 100644
index 0000000..c05f5e3
--- /dev/null
+++ b/modules/nixfiles/common/xdg.nix
@@ -0,0 +1,86 @@
+{
+ config,
+ lib,
+ this,
+ ...
+}:
+with lib; {
+ imports = let
+ withBase = s: ["home-manager" "users" my.username "xdg" s];
+ in [
+ (mkAliasOptionModule ["dirs" "cache"] (withBase "cacheHome"))
+ (mkAliasOptionModule ["dirs" "config"] (withBase "configHome"))
+ (mkAliasOptionModule ["dirs" "data"] (withBase "dataHome"))
+ (mkAliasOptionModule ["dirs" "state"] (withBase "stateHome"))
+ (mkAliasOptionModule ["userDirs"] (withBase "userDirs"))
+ ];
+
+ hm = {
+ xdg = mkMerge [
+ {
+ enable = true;
+
+ userDirs = let
+ inherit (config.my) home;
+ tmp = home + "/tmp";
+ in {
+ enable = true;
+
+ createDirectories = this.isHeadful;
+
+ desktop = tmp;
+ documents = "${home}/doc";
+ download = tmp;
+ music = tmp;
+ pictures = tmp;
+ videos = tmp;
+ templates = tmp;
+ publicShare = "${home}/share";
+ };
+ }
+ (mkIf this.isHeadful {
+ mimeApps = let
+ images = [
+ "image/bmp"
+ "image/gif"
+ "image/jpeg"
+ "image/jpg"
+ "image/png"
+ "image/svg+xml"
+ "image/tiff"
+ "image/webp"
+ ];
+ media = [
+ "audio/aac"
+ "audio/flac"
+ "audio/mp3"
+ "audio/ogg"
+ "audio/wav"
+ "audio/webm"
+ "video/mkv"
+ "video/mp4"
+ "video/ogg"
+ "video/webm"
+ "video/x-matroska"
+ ];
+ in {
+ enable = true;
+
+ defaultApplications =
+ mkMerge
+ (mapAttrsToList (n: ms: genAttrs ms (_: ["${n}.desktop"])) {
+ aria2 = ["application/x-bittorrent" "x-scheme-handler/magnet"];
+ emacsclient = ["x-scheme-handler/mailto"];
+ firefox = [
+ "text/html"
+ "x-scheme-handler/http"
+ "x-scheme-handler/https"
+ ];
+ gwenview = images;
+ mpv = media;
+ });
+ };
+ })
+ ];
+ };
+}
diff --git a/modules/nixfiles/curl.nix b/modules/nixfiles/curl.nix
new file mode 100644
index 0000000..e7bee31
--- /dev/null
+++ b/modules/nixfiles/curl.nix
@@ -0,0 +1,38 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.curl;
+in {
+ options.nixfiles.modules.curl.enable =
+ mkEnableOption "Wether to enable cURL.";
+
+ config = mkIf cfg.enable {
+ hm.home.file.".curlrc".text = ''
+ connect-timeout = 60
+ progress-bar
+ referer = ";auto"
+ remote-time
+ show-error
+ '';
+
+ environment.systemPackages = with pkgs; [
+ curl
+ (writeShellScriptBin "0x0" ''
+ url="https://0x0.st"
+ form="file=@"
+
+ if [ -t 0 ] && [ -n "$1" ]; then
+ form="$form$1"
+ else
+ form="$form-"
+ fi
+
+ ${curl}/bin/curl --form "$form" "$url"
+ '')
+ ];
+ };
+}
diff --git a/modules/nixfiles/default.nix b/modules/nixfiles/default.nix
new file mode 100644
index 0000000..8d26e7f
--- /dev/null
+++ b/modules/nixfiles/default.nix
@@ -0,0 +1,80 @@
+{...}: {
+ imports = [
+ ./acme.nix
+ ./alacritty.nix
+ ./alertmanager.nix
+ ./aria2.nix
+ ./aspell.nix
+ ./bat.nix
+ ./beets.nix
+ ./bluetooth.nix
+ ./broot.nix
+ ./chromium.nix
+ ./common
+ ./curl.nix
+ ./direnv.nix
+ ./docker.nix
+ ./dwm.nix
+ ./emacs.nix
+ ./endlessh-go.nix
+ ./endlessh.nix
+ ./fail2ban.nix
+ ./firefox
+ ./flatpak.nix
+ ./fonts.nix
+ ./games
+ ./git.nix
+ ./gnome.nix
+ ./gnupg.nix
+ ./gotify.nix
+ ./grafana.nix
+ ./htop.nix
+ ./hydra.nix
+ ./ipfs.nix
+ ./kde.nix
+ ./libvirtd.nix
+ ./lidarr.nix
+ ./loki.nix
+ ./lxc.nix
+ ./matrix
+ ./monitoring.nix
+ ./mpd.nix
+ ./mpv.nix
+ ./nextcloud.nix
+ ./nginx.nix
+ ./nmap.nix
+ ./node-exporter.nix
+ ./nsd.nix
+ ./openssh.nix
+ ./password-store.nix
+ ./podman.nix
+ ./postgresql.nix
+ ./profiles
+ ./prometheus.nix
+ ./promtail.nix
+ ./psd.nix
+ ./qutebrowser.nix
+ ./radarr.nix
+ ./radicale.nix
+ ./rss-bridge.nix
+ ./rtorrent.nix
+ ./searx.nix
+ ./shadowsocks.nix
+ ./soju.nix
+ ./sonarr.nix
+ ./sound.nix
+ ./subversion.nix
+ ./syncthing.nix
+ ./throttled.nix
+ ./tmux.nix
+ ./unbound.nix
+ ./vaultwarden.nix
+ ./vim
+ ./vscode.nix
+ ./wget.nix
+ ./wireguard.nix
+ ./x11.nix
+ ./xmonad.nix
+ ./zathura.nix
+ ];
+}
diff --git a/modules/nixfiles/direnv.nix b/modules/nixfiles/direnv.nix
new file mode 100644
index 0000000..aea7dd8
--- /dev/null
+++ b/modules/nixfiles/direnv.nix
@@ -0,0 +1,24 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.direnv;
+in {
+ options.nixfiles.modules.direnv.enable =
+ mkEnableOption "Whether to enable direnv and Lorri.";
+
+ config = mkIf cfg.enable {
+ hm = {
+ programs.direnv = {
+ enable = true;
+ nix-direnv.enable = true;
+ };
+
+ home.sessionVariables.DIRENV_LOG_FORMAT = "";
+ };
+
+ services.lorri.enable = true;
+ };
+}
diff --git a/modules/nixfiles/docker.nix b/modules/nixfiles/docker.nix
new file mode 100644
index 0000000..d2e53d6
--- /dev/null
+++ b/modules/nixfiles/docker.nix
@@ -0,0 +1,34 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.docker;
+in {
+ options.nixfiles.modules.docker.enable =
+ mkEnableOption "Whether to enable Docker.";
+
+ config = mkIf cfg.enable {
+ secrets.containers-auth = {
+ file = "${inputs.self}/secrets/containers-auth";
+ path = "${config.my.home}/.docker/config.json";
+ owner = my.username;
+ };
+
+ virtualisation.docker.enable = true;
+
+ environment.systemPackages = with pkgs; [docker-compose];
+
+ my.extraGroups = ["docker"];
+
+ hm.programs.bash = {
+ shellAliases.d = "${pkgs.docker}/bin/docker";
+
+ initExtra = mkAfter ''
+ _complete_alias d _docker docker
+ '';
+ };
+ };
+}
diff --git a/modules/nixfiles/dwm.nix b/modules/nixfiles/dwm.nix
new file mode 100644
index 0000000..86d15fb
--- /dev/null
+++ b/modules/nixfiles/dwm.nix
@@ -0,0 +1,159 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.dwm;
+in {
+ options.nixfiles.modules.dwm.enable = mkEnableOption "Whether to enable dwm.";
+
+ config = mkIf cfg.enable {
+ nixfiles.modules.x11.enable = true;
+
+ hm.xsession = {
+ enable = true;
+
+ windowManager.command = let
+ pkg = pkgs.dwm.override {
+ conf = let
+ font = with config.fontScheme.monospaceFont; "${family}:size=${toString size}";
+ colour = config.colourScheme;
+ in ''
+ static const unsigned int borderpx = 1;
+ static const unsigned int snap = 32;
+ static const int showbar = 1;
+ static const int topbar = 1;
+
+ static const char *fonts[] = {
+ "${font}"
+ };
+
+ static const char *colors[][3] = {
+ [SchemeNorm] = {
+ "${colour.white}",
+ "${colour.black}",
+ "${colour.black}",
+ },
+ [SchemeSel] = {
+ "${colour.black}",
+ "${colour.white}",
+ "${colour.white}",
+ },
+ };
+
+ static const char *tags[] = {
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9"
+ };
+
+ static const Rule rules[] = {
+ { "Emacs", NULL, NULL, 1 << 0, 0, -1 },
+ };
+
+ static const float mfact = 0.666;
+ static const int nmaster = 1;
+ static const int resizehints = 0;
+ static const int lockfullscreen = 1;
+
+ static const Layout layouts[] = {
+ { "[]=", tile },
+ { "><>", NULL },
+ { "[M]", monocle },
+ };
+
+ #define MODKEY Mod4Mask
+ #define TAGKEYS(KEY,TAG) \
+ { MODKEY, KEY, view, { .ui = 1 << TAG } }, \
+ { MODKEY|ControlMask, KEY, toggleview, { .ui = 1 << TAG } }, \
+ { MODKEY|ShiftMask, KEY, tag, { .ui = 1 << TAG } }, \
+ { MODKEY|ControlMask|ShiftMask, KEY, toggletag, { .ui = 1 << TAG } },
+
+ static char dmenumon[2] = "0";
+ static const char *dmenucmd[] = {
+ "${pkgs.dmenu}/bin/dmenu_run",
+ "-m", dmenumon,
+ "-fn", "${font}",
+ "-nb", "${colour.black}",
+ "-nf", "${colour.white}",
+ "-sb", "${colour.white}",
+ "-sf", "${colour.black}",
+ NULL,
+ };
+ static const char *termcmd[] = {
+ "${pkgs.alacritty}/bin/alacritty",
+ NULL,
+ };
+
+ static const Key keys[] = {
+ { MODKEY, XK_x, spawn, {.v = dmenucmd } },
+ { MODKEY, XK_Return, spawn, {.v = termcmd } },
+ { MODKEY, XK_b, togglebar, {0} },
+ { MODKEY, XK_j, focusstack, {.i = +1 } },
+ { MODKEY, XK_k, focusstack, {.i = -1 } },
+ { MODKEY|ShiftMask, XK_k, incnmaster, {.i = +1 } },
+ { MODKEY|ShiftMask, XK_j, incnmaster, {.i = -1 } },
+ { MODKEY, XK_comma, setmfact, {.f = -0.05} },
+ { MODKEY, XK_period, setmfact, {.f = +0.05} },
+ { MODKEY, XK_p, zoom, {0} },
+ { MODKEY, XK_Tab, view, {0} },
+ { MODKEY, XK_d, killclient, {0} },
+ { MODKEY, XK_t, setlayout, {.v = &layouts[0]} },
+ { MODKEY, XK_m, setlayout, {.v = &layouts[1]} },
+ { MODKEY, XK_f, setlayout, {.v = &layouts[2]} },
+ { MODKEY, XK_o, togglefloating, {0} },
+ { MODKEY, XK_0, view, {.ui = ~0 } },
+ { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } },
+ { MODKEY, XK_h, focusmon, {.i = -1 } },
+ { MODKEY, XK_l, focusmon, {.i = +1 } },
+ { MODKEY|ShiftMask, XK_h, tagmon, {.i = -1 } },
+ { MODKEY|ShiftMask, XK_l, tagmon, {.i = +1 } },
+ TAGKEYS( XK_1, 0)
+ TAGKEYS( XK_2, 1)
+ TAGKEYS( XK_3, 2)
+ TAGKEYS( XK_4, 3)
+ TAGKEYS( XK_5, 4)
+ TAGKEYS( XK_6, 5)
+ TAGKEYS( XK_7, 6)
+ TAGKEYS( XK_8, 7)
+ TAGKEYS( XK_9, 8)
+ { MODKEY|ShiftMask, XK_q, quit, {0} },
+ };
+
+ static const Button buttons[] = {
+ { ClkLtSymbol, 0, Button1, setlayout, {0} },
+ { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
+ { ClkWinTitle, 0, Button2, zoom, {0} },
+ { ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
+ { ClkClientWin, MODKEY, Button1, movemouse, {0} },
+ { ClkClientWin, MODKEY, Button2, togglefloating, {0} },
+ { ClkClientWin, MODKEY, Button3, resizemouse, {0} },
+ { ClkTagBar, 0, Button1, view, {0} },
+ { ClkTagBar, 0, Button3, toggleview, {0} },
+ { ClkTagBar, MODKEY, Button1, tag, {0} },
+ { ClkTagBar, MODKEY, Button3, toggletag, {0} },
+ };
+ '';
+ };
+ in "${pkg}/bin/dwm";
+ };
+
+ hm.services.dwm-status = {
+ enable = true;
+ # package = pkgs.dwm-status.override {
+ # enableAlsaUtils = false;
+ # };
+ order = ["audio" "backlight" "battery" "cpu_load" "network" "time"];
+ };
+
+ services.xserver.displayManager.startx.enable = true;
+ };
+}
diff --git a/modules/nixfiles/emacs.nix b/modules/nixfiles/emacs.nix
new file mode 100644
index 0000000..230b965
--- /dev/null
+++ b/modules/nixfiles/emacs.nix
@@ -0,0 +1,69 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.emacs;
+in {
+ options.nixfiles.modules.emacs.enable =
+ mkEnableOption "Whether to enable the GNU Emacs.";
+
+ # TODO Nixify.
+ config = mkIf cfg.enable {
+ hm = {
+ home = {
+ packages = with pkgs; [
+ cmigemo # :lang japanese
+ gcc # :lang (org +roam2)
+ gnuplot # :lang (org +gnuplot)
+ gnutls # :app irc
+ graphviz # :lang (org +roam2)
+ grip # :lang (markdown +grip)
+ maim # :lang (org +dragndrop)
+ pandoc # :lang org markdown latex
+ plantuml # :lang plantuml
+ pre-commit # :tools magit
+ sqlite # :lang (org +roam2)
+ texlive.combined.scheme-full # :lang org tex
+ xclip # :os (tty +osc)
+ ];
+
+ activation = {
+ symlinkMigemoFiles = ''
+ target="${config.dirs.data}/migemo"
+ [[ -L "$target" ]] && rm "$target"
+ ln -s ${pkgs.cmigemo}/share/migemo "$target"
+ '';
+ symlinkSkkFiles = ''
+ target="${config.dirs.data}/skk"
+ [[ -L "$target" ]] && rm "$target"
+ ln -s ${pkgs.skk-dicts}/share "$target"
+ '';
+ symlinkPlantumlFiles = ''
+ target="${config.dirs.data}/plantuml"
+ [[ -L "$target" ]] && rm "$target"
+ ln -s ${pkgs.plantuml}/lib "$target"
+ '';
+ };
+ };
+
+ programs.emacs = {
+ enable = true;
+ package = pkgs.emacs28.override {nativeComp = true;};
+ extraPackages = p:
+ with p; [
+ vterm # :term vterm
+ ];
+ };
+
+ services.emacs = {
+ enable = true;
+ client.enable = true;
+ };
+ };
+
+ fonts.fonts = with pkgs; [emacs-all-the-icons-fonts];
+ };
+}
diff --git a/modules/nixfiles/endlessh-go.nix b/modules/nixfiles/endlessh-go.nix
new file mode 100644
index 0000000..8ab71ee
--- /dev/null
+++ b/modules/nixfiles/endlessh-go.nix
@@ -0,0 +1,57 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.endlessh-go;
+in {
+ options.nixfiles.modules.endlessh-go = {
+ enable = mkEnableOption "Whether to enable endlessh-go.";
+
+ prometheusPort = mkOption {
+ description = "Prometheus port.";
+ type = with types; port;
+ default = 9119;
+ };
+ };
+
+ config = let
+ port = 22;
+ in
+ mkIf cfg.enable {
+ assertions = [
+ {
+ assertion = !(any (x: x == port) config.services.openssh.ports);
+ message = "Port ${toString port} is already occupied by OpenSSH";
+ }
+ ];
+
+ systemd.services.endlessh-go = {
+ description = "Endlessh SSH Tarpit";
+ requires = ["network-online.target"];
+ serviceConfig = {
+ Restart = "always";
+ ExecStart = concatStringsSep " " [
+ "${pkgs.endlessh-go}/bin/endlessh-go"
+ "-conn_type=tcp4"
+ "-host=0.0.0.0"
+ "-port=${toString port}"
+ "-enable_prometheus"
+ "-prometheus_port=${toString cfg.prometheusPort}"
+ "-geoip_supplier=ip-api"
+ "-logtostderr"
+ "-v=1"
+ ];
+ KillSignal = "SIGTERM";
+ AmbientCapabilities = "CAP_NET_BIND_SERVICE";
+ DynamicUser = true;
+ StateDirectory = "endlessh-go";
+ };
+ wantedBy = ["multi-user.target"];
+ };
+
+ networking.firewall.allowedTCPPorts = [port];
+ };
+}
diff --git a/modules/nixfiles/endlessh.nix b/modules/nixfiles/endlessh.nix
new file mode 100644
index 0000000..2871683
--- /dev/null
+++ b/modules/nixfiles/endlessh.nix
@@ -0,0 +1,45 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.endlessh;
+in {
+ options.nixfiles.modules.endlessh.enable =
+ mkEnableOption "Whether to enable endlessh.";
+
+ config = let
+ port = 22;
+ in
+ mkIf cfg.enable {
+ assertions = [
+ {
+ assertion = !(any (x: x == port) config.services.openssh.ports);
+ message = "Port ${toString port} is already occupied by OpenSSH";
+ }
+ ];
+
+ systemd.services.endlessh = {
+ description = "Endlessh SSH Tarpit";
+ requires = ["network-online.target"];
+ serviceConfig = {
+ Restart = "always";
+ ExecStart = concatStringsSep " " [
+ "${pkgs.endlessh}/bin/endlessh"
+ "-v"
+ "-4"
+ "-p ${toString port}"
+ ];
+ KillSignal = "SIGTERM";
+ AmbientCapabilities = "CAP_NET_BIND_SERVICE";
+ DynamicUser = true;
+ StateDirectory = "endlessh";
+ };
+ wantedBy = ["multi-user.target"];
+ };
+
+ networking.firewall.allowedTCPPorts = [port];
+ };
+}
diff --git a/modules/nixfiles/fail2ban.nix b/modules/nixfiles/fail2ban.nix
new file mode 100644
index 0000000..56670e7
--- /dev/null
+++ b/modules/nixfiles/fail2ban.nix
@@ -0,0 +1,32 @@
+{
+ config,
+ lib,
+ this,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.fail2ban;
+in {
+ options.nixfiles.modules.fail2ban.enable =
+ mkEnableOption "Whether to enable fail2ban service.";
+
+ config = mkIf cfg.enable {
+ services.fail2ban = {
+ enable = true;
+
+ bantime-increment = {
+ enable = true;
+ maxtime = "24h";
+ rndtime = "8m";
+ };
+
+ ignoreIP =
+ optionals (hasAttr "wireguard" this)
+ (with config.nixfiles.modules.wireguard; [ipv4.subnet ipv6.subnet]);
+
+ jails.DEFAULT = ''
+ blocktype = DROP
+ '';
+ };
+ };
+}
diff --git a/modules/nixfiles/firefox/default.nix b/modules/nixfiles/firefox/default.nix
new file mode 100644
index 0000000..d7afdae
--- /dev/null
+++ b/modules/nixfiles/firefox/default.nix
@@ -0,0 +1,52 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.firefox;
+in {
+ options.nixfiles.modules.firefox.enable = mkEnableOption "";
+
+ config = mkIf cfg.enable {
+ hm = {
+ home = {
+ # sessionVariables.BROWSER = mkOverride 200 "firefox";
+
+ packages = with pkgs; [
+ (writeShellScriptBin "firefox-vanilla" ''
+ ${config.hm.programs.firefox.package}/bin/firefox -p vanilla $@
+ '')
+ profile-cleaner
+ ];
+ };
+
+ programs.firefox = {
+ enable = true;
+
+ package = pkgs.firefox.override {
+ cfg = with config.nixfiles.modules; {
+ enablePlasmaBrowserIntegration = kde.enable;
+ enableGnomeExtensions = gnome.enable;
+ };
+ };
+
+ profiles.default = (import ./profile.nix) config.nixfiles.modules lib;
+
+ extensions = with pkgs.nur.repos.rycee.firefox-addons;
+ [
+ bitwarden
+ darkreader
+ ipfs-companion
+ noscript
+ privacy-redirect
+ stylus
+ ublock-origin
+ violentmonkey
+ ]
+ ++ optional config.nixfiles.modules.kde.enable plasma-integration;
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/firefox/profile.nix b/modules/nixfiles/firefox/profile.nix
new file mode 100644
index 0000000..3382b85
--- /dev/null
+++ b/modules/nixfiles/firefox/profile.nix
@@ -0,0 +1,479 @@
+modules: lib:
+with lib; let
+ mkCssWithRoot = css:
+ mkMerge [
+ (with modules.profiles.common.colourScheme; ''
+ :root {
+ --black: ${black};
+ --red: ${red};
+ --green: ${green};
+ --yellow: ${yellow};
+ --blue: ${blue};
+ --magenta: ${magenta};
+ --cyan: ${cyan};
+ --white: ${white};
+ --bright-black: ${brightBlack};
+ --bright-red: ${brightRed};
+ --bright-green: ${brightGreen};
+ --bright-yellow: ${brightYellow};
+ --bright-blue: ${brightBlue};
+ --bright-magenta: ${brightMagenta};
+ --bright-cyan: ${brightCyan};
+ --bright-white: ${brightWhite};
+ --background: ${background};
+ --foreground: ${foreground};
+ '')
+ (with modules.fonts.fontScheme; ''
+ --sans-serif-font-family: "${sansSerifFont.family}", "${sansSerifFontFallback.family}", sans-serif;
+ --sans-serif-font-size: ${toString sansSerifFont.size};
+ --serif-font-family: "${serifFont.family}", "${serifFontFallback.family}", serif;
+ --serif-font-size: ${toString serifFont.size};
+ --monospace-font-family: "${monospaceFont.family}", "${monospaceFontFallback.family}", monospace;
+ --monospace-font-size: ${toString monospaceFont.size};
+ }
+ '')
+ (builtins.readFile css)
+ ];
+in {
+ id = 0;
+
+ isDefault = true;
+
+ userChrome = mkCssWithRoot ./userChrome.css;
+
+ userContent = mkCssWithRoot ./userContent.css;
+
+ settings = {
+ # Updates
+ #
+ "app.update.auto" = false;
+ "browser.search.update" = false;
+ "extensions.update.enabled" = false;
+ "extensions.update.autoUpdateDefault" = false;
+ #
+ "extensions.getAddons.cache.enabled" = false;
+ "extensions.getAddons.showPane" = false;
+
+ # Telemetry
+ #
+ # https://bugzilla.mozilla.org/1195552
+ "datareporting.healthreport.service.enabled" = false;
+ "datareporting.healthreport.uploadEnabled" = false;
+ "datareporting.policy.dataSubmissionEnabled" = false;
+ # https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/telemetry/internals/preferences.html
+ # https://medium.com/georg-fritzsche/data-preference-changes-in-firefox-58-2d5df9c428b5
+ "toolkit.telemetry.unified" = false;
+ "toolkit.telemetry.server" = "data:,";
+ "toolkit.telemetry.archive.enabled" = false;
+ "toolkit.telemetry.newProfilePing.enabled" = false;
+ "toolkit.telemetry.shutdownPingSender.enabled" = false;
+ "toolkit.telemetry.firstShutdownPing.enabled" = false;
+ "toolkit.telemetry.updatePing.enabled" = false;
+ "toolkit.telemetry.bhrPing.enabled" = false; # Background Hang Reporter
+ # https://blog.mozilla.org/data/2018/08/20/effectively-measuring-search-in-firefox
+ "toolkit.telemetry.coverage.opt-out" = true;
+ "toolkit.coverage.opt-out" = true;
+ "toolkit.coverage.endpoint.base" = "";
+
+ # Studies
+ #
+ # https://support.mozilla.org/en-US/kb/shield/
+ "app.shield.optoutstudies.enabled" = false;
+ # https://mozilla.github.io/normandy/
+ "app.normandy.enabled" = false;
+ "app.normandy.api_url" = "";
+
+ # Crash reports
+ #
+ "browser.tabs.crashReporting.sendReport" = false;
+ "breakpad.reportURL" = "";
+
+ # Captive Portal detection
+ #
+ # https://www.eff.org/deeplinks/2017/08/how-captive-portals-interfere-wireless-security-and-privacy
+ "captivedetect.canonicalURL" = "";
+ "network.captive-portal-service.enabled" = false;
+ # https://bugzilla.mozilla.org/1460537
+ "network.connectivity-service.enabled" = false;
+
+ # Safe browsing
+ #
+ # https://feeding.cloud.geek.nz/posts/how-safe-browsing-works-in-firefox/
+ # https://wiki.mozilla.org/Security/Safe_Browsing
+ # https://support.mozilla.org/kb/how-does-phishing-and-malware-protection-work
+ "browser.safebrowsing.malware.enabled" = false;
+ "browser.safebrowsing.passwords.enabled" = false;
+ "browser.safebrowsing.phishing.enabled" = false;
+ "browser.safebrowsing.downloads.enabled" = false;
+ "browser.safebrowsing.downloads.remote.enabled" = false;
+ "browser.safebrowsing.downloads.remote.url" = "";
+ "browser.safebrowsing.downloads.remote.block_potentially_unwanted" = false;
+ "browser.safebrowsing.downloads.remote.block_uncommon" = false;
+
+ # Implicit outbound
+ #
+ # https://developer.mozilla.org/docs/Web/HTTP/Link_prefetching_FAQ
+ "network.prefetch-next" = false;
+ # https://developer.mozilla.org/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control
+ "network.dns.disablePrefetch" = true;
+ "network.dns.disablePrefetchFromHTTPS" = true;
+ #
+ "network.predictor.enabled" = false;
+ "network.predictor.enable-prefetch" = false;
+ # https://news.slashdot.org/story/15/08/14/2321202/how-to-quash-firefoxs-silent-requests
+ "network.http.speculative-parallel-limit" = 0;
+ # https://www.bleepingcomputer.com/news/software/major-browsers-to-prevent-disabling-of-click-tracking-privacy-risk/
+ "browser.send_pings" = false;
+ # https://trac.torproject.org/projects/tor/wiki/doc/TorifyHOWTO/WebBrowsers
+ "network.proxy.socks_remote_dns" = true;
+ # https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/26424
+ "network.file.disable_unc_paths" = true;
+
+ # Proxy
+ #
+ # https://blog.mozilla.org/security/2021/10/25/securing-the-proxy-api-for-firefox-add-ons/
+ "network.proxy.failover_direct" = false;
+ # https://bugzilla.mozilla.org/buglist.cgi?bug_id=1732792,1733994,1733481
+ "network.proxy.allow_bypass" = false;
+
+ # DNS-over-HTTPS (DoH)
+ #
+ # https://hacks.mozilla.org/2018/05/a-cartoon-intro-to-dns-over-https/
+ # https://wiki.mozilla.org/Security/DOH-resolver-policy
+ # https://blog.mozilla.org/mozilla/news/firefox-by-default-dns-over-https-rollout-in-canada/
+ # https://www.eff.org/deeplinks/2020/12/dns-doh-and-odoh-oh-my-year-review-2020 ***/
+ "network.trr.mode" = 5;
+
+ # Location bar & search
+ #
+ "browser.fixup.alternate.enabled" = false;
+ #
+ "browser.urlbar.trimURLs" = false;
+ #
+ "browser.search.suggest.enabled" = false;
+ "browser.urlbar.suggest.searches" = false;
+ # https://bugzilla.mozilla.org/1348275
+ "browser.urlbar.speculativeConnect.enabled" = false;
+ # https://bugzilla.mozilla.org/1642623
+ "browser.urlbar.dnsResolveSingleWordsAfterSearch" = 0;
+ # https://blog.mozilla.org/data/2021/09/15/data-and-firefox-suggest/
+ "browser.urlbar.suggest.quicksuggest.nonsponsored" = false;
+ "browser.urlbar.suggest.quicksuggest.sponsored" = false;
+
+ # Search & form history
+ # https://blog.mindedsecurity.com/2011/10/autocompleteagain.html
+ # https://bugzilla.mozilla.org/381681
+ "browser.formfill.enable" = false;
+ # https://wiki.mozilla.org/Firefox/Features/Form_Autofill
+ "extensions.formautofill.available" = "off";
+ "extensions.formautofill.addresses.enabled" = false;
+ "extensions.formautofill.creditCards.available" = false;
+ "extensions.formautofill.creditCards.enabled" = false;
+ "extensions.formautofill.heuristics.enabled" = false;
+
+ # Passwords
+ #
+ "signon.rememberSignons" = false;
+
+ # Disk avoidance
+ #
+ # Disable caching to disk.
+ # This is a possible performance hit. Could be removed in favor of PSD?
+ "browser.cache.disk.enable" = false;
+ #
+ "browser.privatebrowsing.forceMediaMemoryCache" = true;
+ "media.memory_cache_max_size" = 65536;
+ # Extra session data
+ "browser.sessionstore.privacy_level" = 2;
+ # Reduce writes
+ "browser.sessionstore.interval" = 30000;
+
+ # SSL/TLS
+ #
+ # Block connections (SSL_ERROR_UNSAFE_NEGOTIATION) to servers that don't support RFC 5746
+ # https://wiki.mozilla.org/Security:Renegotiation
+ # https://datatracker.ietf.org/doc/html/rfc5746
+ # https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-3555
+ # https://www.ssllabs.com/ssl-pulse
+ "security.ssl.require_safe_negotiation" = true;
+ # Disable TLS1.3 0-RTT
+ # https://github.com/tlswg/tls13-spec/issues/1001
+ # https://www.rfc-editor.org/rfc/rfc9001.html#name-replay-attacks-with-0-rtt
+ # https://blog.cloudflare.com/tls-1-3-overview-and-q-and-a/
+ "security.tls.enable_0rtt_data" = false;
+
+ # OCSP (Online Certificate Status Protocol)
+ #
+ # https://en.wikipedia.org/wiki/Ocsp
+ # https://scotthelme.co.uk/revocation-is-broken/
+ # https://blog.mozilla.org/security/2013/07/29/ocsp-stapling-in-firefox/
+ # https://www.imperialviolet.org/2014/04/19/revchecking.html
+ # "security.OCSP.require" = true;
+
+ # HPKP (HTTP Public Key Pinning)
+ #
+ # https://blog.mozilla.org/security/2016/10/18/phasing-out-sha-1-on-the-public-web/
+ "security.pki.sha1_enforcement_level" = 1;
+ # https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/16206
+ "security.cert_pinning.enforcement_level" = 2;
+ # https://bugzilla.mozilla.org/buglist.cgi?bug_id=1429800,1670985
+ # https://blog.mozilla.org/security/tag/crlite/
+ "security.remote_settings.crlite_filters.enabled" = true;
+ "security.pki.crlite_mode" = 2;
+
+ # Mixed content
+ #
+ "security.mixed_content.block_display_content" = true;
+ # Force HTTPS-only mode
+ # Exceptions are managed on the settings page
+ "dom.security.https_only_mode" = true;
+ "dom.security.https_only_mode_pbm" = true;
+ # https://bugzilla.mozilla.org/buglist.cgi?bug_id=1642387,1660945
+ "dom.security.https_only_mode_send_http_background_request" = false;
+
+ # UI
+ #
+ # Dispaly warning on the padlock for "broken security"
+ # https://wiki.mozilla.org/Security:Renegotiation
+ # https://bugzilla.mozilla.org/1353705
+ "security.ssl.treat_unsafe_negotiation_as_broken" = true;
+ # https://github.com/pyllyukko/user.js/issues/210
+ "browser.ssl_override_behavior" = 1;
+ # Display advanced information on Insecure Connection warning pages
+ "browser.xul.error_pages.expert_bad_cert" = true;
+ # Display "Not Secure" text on HTTP sites
+ "security.insecure_connection_text.enabled" = true;
+ #
+ "browser.privatebrowsing.infoEnabled" = false;
+ "browser.privatebrowsing.infoTitleEnabled" = false;
+ "browser.privatebrowsing.promoEnabled" = false;
+ "browser.privatebrowsing.promoTitleEnabled" = false;
+ "browser.privatebrowsing.vpnpromourl" = "";
+
+ # UX
+ #
+ "browser.urlbar.decodeURLsOnCopy" = true;
+ #
+ "browser.tabs.closeWindowWithLastTab" = true;
+ #
+ "general.autoScroll" = true;
+ "general.smoothScroll" = true;
+
+ # Headers & referers
+ #
+ # "network.http.referer.XOriginPolicy" = 2;
+ # "network.http.referer.XOriginTrimmingPolicy" = 2;
+
+ # Containers
+ #
+ # https://wiki.mozilla.org/Security/Contextual_Identity_Project/Containers
+ # https://addons.mozilla.org/firefox/addon/temporary-containers/
+ # https://medium.com/@stoically/enhance-your-privacy-in-firefox-with-temporary-containers-33925cd6cd21
+ # https://github.com/stoically/temporary-containers/wiki
+ "privacy.userContext.enabled" = true;
+ "privacy.userContext.ui.enabled" = true;
+
+ # WebRTC
+ #
+ # Disable WebRTC
+ # https://browserleaks.com/webrtc
+ # https://groups.google.com/g/discuss-webrtc/c/6stQXi72BEU/m/2FwZd24UAQAJ
+ # https://datatracker.ietf.org/doc/html/draft-ietf-mmusic-mdns-ice-candidates#section-3.1.1
+ # "media.peerconnection.enabled" = false;
+
+ # DRM
+ #
+ # https://www.eff.org/deeplinks/2017/10/drms-dead-canary-how-we-just-lost-web-what-we-learned-it-and-what-we-need-do-next
+ "media.eme.enabled" = false;
+ # https://bugzilla.mozilla.org/show_bug.cgi?id=1451762#c55
+ "browser.eme.ui.enabled" = false;
+
+ # GMP (Gecko Media Plugins)
+ #
+ "media.gmp-provider.enabled" = false;
+
+ # Disable autoplay of HTML5 media
+ "media.autoplay.enabled" = false;
+ "media.autoplay.default" = 5;
+ "media.autoplay.blocking_policy" = 2;
+
+ # DOM (Document Object Model)
+ #
+ # Disable "Confirm you want to leave" dialog on page close
+ # https://developer.mozilla.org/docs/Web/Events/beforeunload
+ "dom.disable_beforeunload" = true;
+ # Prevent scripts from moving and resizing open windows
+ "dom.disable_window_move_resize" = true;
+ # Block popup windows
+ "dom.disable_open_during_load" = true;
+ # Limit events that can cause a popup
+ "dom.popup_allowed_events" = "click dblclick mousedown pointerdown";
+
+ # PDFjs
+ #
+ # "pdfjs.disabled" = true;
+ "pdfjs.enableScripting" = false;
+
+ # Developer Tools
+ #
+ "devtools.accessibility.enabled" = false;
+ "devtools.application.enabled" = false;
+ "devtools.chrome.enabled" = true;
+ "devtools.dom.enabled" = true;
+ "devtools.enabled" = true;
+ "devtools.inspector.enabled" = true;
+ "devtools.jsonview.enabled" = true;
+ "devtools.memory.enabled" = true;
+ "devtools.netmonitor.enabled" = true;
+ "devtools.performance.enabled" = false;
+ "devtools.storage.enabled" = true;
+ "devtools.styleeditor.enabled" = true;
+ # This will prevent working with XUL
+ # https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/16222
+ "devtools.debugger.enabled" = false;
+ "devtools.debugger.remote-enabled" = false;
+
+ # Downloads
+ #
+ "browser.download.useDownloadDir" = false;
+ "browser.download.alwaysOpenPanel" = false;
+ "browser.download.manager.addToRecentDocs" = false;
+ "browser.download.autohideButton" = false;
+
+ # Extensions
+ #
+ # https://mike.kaply.com/2012/02/21/understanding-add-on-scopes/
+ # https://archive.is/DYjAM
+ "extensions.enabledScopes" = 5;
+ "extensions.autoDisableScopes" = 15;
+ # https://bugzilla.mozilla.org/buglist.cgi?bug_id=1659530,1681331
+ "extensions.postDownloadThirdPartyPrompt" = false;
+ # Disable recommendations in about:addons panes
+ "extensions.htmlaboutaddons.recommendations.enabled" = false;
+ # Mozilla's trash
+ "extensions.pocket.enabled" = false;
+ "extensions.screenshots.disabled" = true;
+ "idenitity.fxaccounts.enabled" = false;
+ "reader.parse-on-load.enabled" = false;
+
+ # ETP (Encahnced Tracking Protection)
+ #
+ # https://blog.mozilla.org/security/2021/02/23/total-cookie-protection/
+ "browser.contentblocking.category" = "strict";
+ "privacy.partition.serviceWorkers" = true;
+
+ # Sanitize
+ #
+ "privacy.sanitize.sanitizeOnShutdown" = true;
+ "privacy.sanitize.timeSpan" = 0;
+ "privacy.clearOnShutdown.cache" = true;
+ "privacy.clearOnShutdown.cookies" = false;
+ "privacy.clearOnShutdown.downloads" = true;
+ "privacy.clearOnShutdown.formdata" = true;
+ "privacy.clearOnShutdown.history" = false;
+ "privacy.clearOnShutdown.offlineApps" = false;
+ "privacy.clearOnShutdown.sessions" = false;
+ "privacy.clearOnShutdown.siteSettings" = false;
+ "privacy.cpd.cache" = true;
+ "privacy.cpd.cookies" = false;
+ "privacy.cpd.downloads" = true;
+ "privacy.cpd.formdata" = true;
+ "privacy.cpd.history" = false;
+ "privacy.cpd.offlineApps" = false;
+ "privacy.cpd.passwords" = false;
+ "privacy.cpd.sessions" = false;
+ "privacy.cpd.siteSettings" = false;
+
+ # Fingerprinting
+ #
+ # https://bugzilla.mozilla.org/418986
+ # "privacy.resistFingerprinting" = true;
+ # https://bugzilla.mozilla.org/1448423
+ # "browser.startup.blankWindow" = false;
+
+ # Rice
+ #
+ "toolkit.legacyUserProfileCustomizations.stylesheets" = true;
+ #
+ "browser.startup.homepage" = "about:blank"; # TODO Custom?
+ "browser.startup.homepage_welcome_url" = "";
+ "browser.startup.homepage_welcome_url.additional" = "";
+ #
+ "browser.toolbars.bookmarks.visibility" = "never";
+ #
+ "browser.tabs.inTitlebar" = 1;
+ #
+ "browser.newtabpage.enabled" = false;
+ "browser.newtabpage.enhanced" = false;
+ "browser.newtabpage.activity-stream.default.sites" = "";
+ "browser.newtabpage.activity-stream.asrouter.disable-captive-portal-vpn-promo" =
+ true;
+ "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons" = false;
+ "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features" =
+ false;
+ "browser.newtabpage.activity-stream.discoverystream.enabled" = false;
+ "browser.newtabpage.activity-stream.feeds.discoverystreamfeed" = false;
+ "browser.newtabpage.activity-stream.feeds.places" = false;
+ "browser.newtabpage.activity-stream.feeds.sections" = false;
+ "browser.newtabpage.activity-stream.feeds.snippets" = false;
+ "browser.newtabpage.activity-stream.feeds.telemetry" = false;
+ "browser.newtabpage.activity-stream.feeds.topsites" = false;
+ "browser.newtabpage.activity-stream.showSearch" = false;
+ "browser.newtabpage.activity-stream.showSponsored" = false;
+ "browser.newtabpage.activity-stream.showSponsoredTopSites" = false;
+ "browser.newtabpage.activity-stream.telemetry" = false;
+ #
+ "layout.css.color-mix.enabled" = true;
+ #
+ "svg.context-properties.content.enabled" = true;
+
+ # Annoyances
+ #
+ "browser.aboutConfig.showWarning" = false;
+ "browser.disableResetPrompt" = true;
+ "browser.newtabpage.introShown" = true;
+ "browser.onboarding.enabled" = false;
+ "browser.shell.checkDefaultBrowser" = false;
+ "browser.tabs.warnOnClose" = false;
+ "browser.tabs.warnOnCloseOtherTabs" = false;
+ "browser.tabs.warnOnOpen" = false;
+ "browser.tabs.warnOnQuitShortcut" = false;
+ "full-screen-api.warning.delay" = 0;
+ "full-screen-api.warning.timeout" = 0;
+ "permissions.default.shortcuts" = 2;
+ "security.dialog_enable_delay" = 0;
+
+ # Geo
+ #
+ # Disable OS provider
+ "geo.provider.use_gpsd" = false;
+ # Use the Mozilla provider. Defaults to Google otherwise
+ "geo.provider.network.url" = "https://location.services.mozilla.com/v1/geolocate?key=%MOZILLA_API_KEY%";
+
+ # Unsorted
+ #
+ "accessibility.force_disabled" = 1;
+ "beacon.enabled" = false;
+ "browser.helperApps.deleteTempFileOnExit" = true;
+ "browser.pagethumbnails.capturing_disabled" = true;
+ "browser.region.network.url" = "";
+ "browser.region.update.enabled" = false;
+ "browser.selfsupport.url" = "";
+ "browser.uitour.enabled" = false;
+ "browser.uitour.url" = "";
+ "intl.accept_languages" = "en-US, en";
+ "layout.spellcheckDefault" = 2;
+ "middlemouse.contentLoadURL" = false;
+ "permissions.delegation.enabled" = false;
+ "permissions.manager.defaultsUrl" = "";
+ "webchannel.allowObject.urlWhitelist" = "";
+ "network.manage-offline-status" = false;
+ "xpinstall.signatures.required" = false;
+
+ # Toolbar
+ #
+ "browser.uiCustomization.state" = ''
+ {"placements":{"widget-overflow-fixed-list":["ublock0_raymondhill_net-browser-action","_73a6fe31-595d-460b-a920-fcc0f8843232_-browser-action","_446900e4-71c2-419f-a6a7-df9c091e268b_-browser-action","_2e5ff8c8-32fe-46d0-9fc8-6b8986621f3c_-browser-action","_b7f9d2cd-d772-4302-8c3f-eb941af36f76_-browser-action","ipfs-firefox-addon_lidel_org-browser-action","addon_darkreader_org-browser-action","_7a7a4a92-a2a0-41d1-9fd7-1e92480d612d_-browser-action","_aecec67f-0d10-4fa7-b7c7-609a2db280cf_-browser-action"],"nav-bar":["back-button","forward-button","urlbar-container","save-to-pocket-button"],"toolbar-menubar":["menubar-items"],"TabsToolbar":["tabbrowser-tabs","new-tab-button","alltabs-button"],"PersonalToolbar":["personal-bookmarks"]},"seen":["addon_darkreader_org-browser-action","ipfs-firefox-addon_lidel_org-browser-action","plasma-browser-integration_kde_org-browser-action","ublock0_raymondhill_net-browser-action","_2e5ff8c8-32fe-46d0-9fc8-6b8986621f3c_-browser-action","_446900e4-71c2-419f-a6a7-df9c091e268b_-browser-action","_73a6fe31-595d-460b-a920-fcc0f8843232_-browser-action","_7a7a4a92-a2a0-41d1-9fd7-1e92480d612d_-browser-action","_aecec67f-0d10-4fa7-b7c7-609a2db280cf_-browser-action","_b7f9d2cd-d772-4302-8c3f-eb941af36f76_-browser-action","developer-button"],"dirtyAreaCache":["nav-bar","widget-overflow-fixed-list","toolbar-menubar","TabsToolbar","PersonalToolbar"],"currentVersion":17,"newElementCount":6}
+ '';
+ };
+}
diff --git a/modules/nixfiles/firefox/userChrome.css b/modules/nixfiles/firefox/userChrome.css
new file mode 100644
index 0000000..4d83391
--- /dev/null
+++ b/modules/nixfiles/firefox/userChrome.css
@@ -0,0 +1,156 @@
+@-moz-document url(chrome://browser/content/browser.xul), url(chrome://browser/content/browser.xhtml)
+{
+ :root {
+ --toolbarbutton-border-radius: 0 !important;
+ --tab-border-radius: 0 !important;
+ --tab-block-margin: 0 !important;
+ --arrowpanel-border-radius: 0 !important;
+ }
+
+ #PersonalToolbar toolbarbutton:not(:hover),
+ #bookmarks-toolbar-button:not(:hover) {
+ filter: grayscale(1) !important;
+ }
+
+ .titlebar-spacer {
+ display: none !important;
+ }
+
+ .tabbrowser-tab::after,
+ .tabbrowser-tab::before {
+ border: none !important;
+ }
+
+ #urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity,
+ #urlbar[pageproxystate="valid"] > #identity-box.chromeUI,
+ #urlbar[pageproxystate="valid"] > #identity-box.extensionPage,
+ #urlbar-display-box {
+ border: none !important;
+ }
+
+ .tab-close-button {
+ display: none !important;
+ }
+
+ #tabbrowser-tabs:not([movingtab])
+ > #tabbrowser-arrowscrollbox
+ > .tabbrowser-tab
+ > .tab-stack
+ > .tab-background[multiselected="true"],
+ #tabbrowser-tabs:not([movingtab])
+ > #tabbrowser-arrowscrollbox
+ > .tabbrowser-tab
+ > .tab-stack
+ > .tab-background[selected="true"] {
+ background-image: none !important;
+ }
+
+ #nav-bar:not([tabs-hidden="true"]) {
+ box-shadow: none;
+ }
+
+ #tabbrowser-tabs[haspinnedtabs]:not([positionpinnedtabs])
+ > #tabbrowser-arrowscrollbox
+ > .tabbrowser-tab[first-visible-unpinned-tab] {
+ margin-inline-start: 0 !important;
+ }
+
+ .tab-background {
+ border-right: 0px solid var(--black) !important;
+ margin-left: -4px !important;
+ }
+
+ .tabbrowser-tab:is([visuallyselected="true"], [multiselected])
+ > .tab-stack
+ > .tab-background {
+ box-shadow: none !important;
+ }
+
+ .tabbrowser-tab[last-visible-tab="true"] {
+ padding-inline-end: 0 !important;
+ }
+
+ #tabs-newtab-button {
+ padding-left: 0 !important;
+ }
+
+ #urlbar-input-container {
+ border: 3px solid var(--black) !important;
+ }
+
+ #urlbar[focused="true"] > #urlbar-background {
+ box-shadow: none !important;
+ }
+
+ #navigator-toolbox {
+ border: none !important;
+ }
+
+ .bookmark-item .toolbarbutton-icon {
+ display: none;
+ }
+
+ toolbarbutton.bookmark-item:not(.subviewbutton) {
+ min-width: 1.6em;
+ }
+
+ #back-button,
+ #forward-button,
+ #context-bookmarklink,
+ #context-inspect-a11y,
+ #context-navigation,
+ #context-openlinkinusercontext-menu,
+ #context-pocket,
+ #context-print-selection,
+ #context-savelink,
+ #context-savelinktopocket,
+ #context-savepage,
+ #context-searchselect,
+ #context-selectall,
+ #context-sendimage,
+ #context-sendlinktodevice,
+ #context-sendlinktodevice,
+ #context-sendpagetodevice,
+ #context-viewsource,
+ #context_bookmarkTab,
+ #context_closeTabOptions,
+ #context_moveTabOptions,
+ #context_reopenInContainer,
+ #context_selectAllTabs,
+ #context_sendTabToDevice {
+ display: none !important;
+ }
+
+ .identity-color-blue {
+ --identity-tab-color: var(--blue) !important;
+ --identity-icon-color: var(--blue) !important;
+ }
+ .identity-color-turquoise {
+ --identity-tab-color: var(--cyan) !important;
+ --identity-icon-color: var(--cyan) !important;
+ }
+ .identity-color-green {
+ --identity-tab-color: var(--green) !important;
+ --identity-icon-color: var(--green) !important;
+ }
+ .identity-color-yellow {
+ --identity-tab-color: var(--yellow) !important;
+ --identity-icon-color: var(--yellow) !important;
+ }
+ .identity-color-orange {
+ --identity-tab-color: var(--brightRed) !important;
+ --identity-icon-color: var(--brightRed) !important;
+ }
+ .identity-color-red {
+ --identity-tab-color: var(--red) !important;
+ --identity-icon-color: var(--red) !important;
+ }
+ .identity-color-pink {
+ --identity-tab-color: var(--brightMagenta) !important;
+ --identity-icon-color: var(--brightMagenta) !important;
+ }
+ .identity-color-purple {
+ --identity-tab-color: var(--magenta) !important;
+ --identity-icon-color: var(--magenta) !important;
+ }
+}
diff --git a/modules/nixfiles/firefox/userContent.css b/modules/nixfiles/firefox/userContent.css
new file mode 100644
index 0000000..8426dc3
--- /dev/null
+++ b/modules/nixfiles/firefox/userContent.css
@@ -0,0 +1,210 @@
+@-moz-document url(about:blank), url(about:home), url(about:newtab), url(about:privatebrowsing) {
+ html,
+ body {
+ background: var(--background) !important;
+ }
+
+ body {
+ display: none !important;
+ }
+}
+
+@-moz-document media-document(all) {
+ body {
+ background-image: none !important;
+ background-color: var(--background) !important;
+ }
+}
+
+@-moz-document url-prefix(https://gitlab.com/)
+{
+ code {
+ font-family: var(--monospace-font-family) !important;
+ font-size: var(--monospace-font-size) !important;
+ }
+}
+
+@-moz-document url-prefix(https://github.com/), url-prefix(https://gist.github.com/)
+{
+ .blob-num,
+ .blob-code-inner {
+ font-family: var(--monospace-font-family) !important;
+ font-size: var(--monospace-font-size) !important;
+ }
+}
+
+@-moz-document regexp("https:\/\/.*\.stackexchange\.com\/.*"), url-prefix(https://askubuntu.com/), url-prefix(https://serverfault.com/), url-prefix(https://stackoverflow.com/), url-prefix(https://superuser.com/)
+{
+ #footer,
+ #left-sidebar,
+ #noscript-warning,
+ #notify-container,
+ #post-form,
+ #sidebar,
+ .ai-start,
+ .bottom-notice,
+ .comment-user,
+ .d-flex.g4,
+ .d-flex.s-btn-group,
+ .js-add-link.comments-link,
+ .js-dismissable-hero,
+ .js-post-issue,
+ .js-show-link.comments-link,
+ .post-taglist,
+ .s-notice,
+ .s-topbar,
+ .site-header,
+ a.ws-nowrap {
+ display: none !important;
+ }
+
+ #question-header .question-hyperlink {
+ font-family: var(--sans-serif-font-family);
+ }
+
+ #mainbar {
+ width: 100% !important;
+ }
+
+ #content {
+ margin-top: -50px !important;
+ border-width: 0 0 0 0 !important;
+ }
+}
+
+@-moz-document regexp("https:\/\/habr\.com\/(ru|en)\/(article|company\/.*\/blog|post)\/.*") {
+ .Vue-Toastification__container,
+ .tm-article-presenter__meta,
+ .tm-article-snippet__labels,
+ .tm-article-sticky-panel,
+ .tm-base-layout__header,
+ .tm-block.tm-block_spacing-bottom,
+ .tm-block_spacing-around,
+ .tm-comment-footer,
+ .tm-comment__header,
+ .tm-footer,
+ .tm-footer-menu,
+ .tm-header,
+ .tm-notice,
+ .tm-page__header,
+ .tm-page__sidebar,
+ .tm-user-info,
+ vue-portal-target {
+ display: none !important;
+ }
+
+ * {
+ transition: none !important;
+ }
+
+ body {
+ font-family: var(--sans-serif-font-family) !important;
+ font-size: var(--sans-serif-font-size) !important;
+ }
+
+ #app {
+ height: auto !important;
+ }
+
+ .tm-article-presenter__footer,
+ .tm-page__main_has-sidebar {
+ margin-left: auto !important;
+ margin-right: auto !important;
+ max-width: 100% !important;
+ }
+
+ .tm-page {
+ padding: 0 !important;
+ }
+
+ .tm-comment__buttons {
+ margin: 0 !important;
+ }
+
+ code {
+ font-family: var(--monospace-font-family) !important;
+ font-size: var(--monospace-font-size) !important;
+ }
+}
+
+@-moz-document url-prefix(https://jisho.org)
+{
+ header,
+ footer {
+ display: none !important;
+ }
+}
+
+@-moz-document url-prefix(https://search.nixos.org)
+{
+ .search-sidebar,
+ footer.container {
+ display: none !important;
+ }
+}
+
+@-moz-document url-prefix(https://gog.com)
+{
+ .galaxy-section-wrapper {
+ display: none !important;
+ }
+}
+
+@-moz-document url-prefix(https://steamdb.info)
+{
+ #steamdb-extension-protip {
+ display: none !important;
+ }
+}
+
+@-moz-document regexp("https:\/\/\.*\.hh\.ru/.*") {
+ .HH-Supernova-Footer,
+ .index-dashboard-applicant__banners,
+ .notification-manager,
+ .resume-sidebar-background,
+ .supernova-overlay {
+ display: none !important;
+ }
+}
+
+@-moz-document url-prefix(https://utaten.com/lyric)
+{
+ :root {
+ --kana-font-size: 20px;
+ --furigana-font-size: 14px;
+ }
+
+ body {
+ font-family: var(--sans-serif-font-family) !important;
+ font-size: var(--kana-font-size) !important;
+ }
+
+ .rb {
+ font-size: var(--kana-font-size) !important;
+ }
+
+ .rt {
+ font-size: var(--furigana-font-size) !important;
+ }
+
+ #footer__area,
+ #reviews,
+ #sidebar,
+ .btn_sbs,
+ .gaugeWrap,
+ .lyricData,
+ .movie_contents,
+ .newLyricWorkFooter,
+ .path::before,
+ .recommendAdTag,
+ .shareArea,
+ .sideBySideBanner,
+ .topBanner,
+ header {
+ display: none !important;
+ }
+
+ #contents {
+ width: 100% !important;
+ }
+}
diff --git a/modules/nixfiles/flatpak.nix b/modules/nixfiles/flatpak.nix
new file mode 100644
index 0000000..0dec763
--- /dev/null
+++ b/modules/nixfiles/flatpak.nix
@@ -0,0 +1,13 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.flatpak;
+in {
+ options.nixfiles.modules.flatpak.enable =
+ mkEnableOption "Whether to enable Flatpak.";
+
+ config = mkIf cfg.enable {services.flatpak.enable = true;};
+}
diff --git a/modules/nixfiles/fonts.nix b/modules/nixfiles/fonts.nix
new file mode 100644
index 0000000..29e2acb
--- /dev/null
+++ b/modules/nixfiles/fonts.nix
@@ -0,0 +1,106 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.fonts;
+in {
+ imports = [
+ (mkAliasOptionModule ["fontScheme"] [
+ "nixfiles"
+ "modules"
+ "fonts"
+ "fontScheme"
+ ])
+ ];
+
+ options.nixfiles.modules.fonts = {
+ enable = mkEnableOption "Whether to enable fonts and their configurations.";
+
+ fontScheme = let
+ mkFont = {
+ family,
+ style,
+ size,
+ }: {
+ family = mkOption {
+ description = "Family of the font.";
+ type = types.str;
+ default = family;
+ };
+ style = mkOption {
+ description = "Style of the font.";
+ type = types.str;
+ default = style;
+ };
+ size = mkOption {
+ description = "Size of the font.";
+ type = types.int;
+ default = size;
+ };
+ };
+ in {
+ serifFont = mkFont {
+ family = "Iosevka Etoile";
+ style = "Regular";
+ size = 16;
+ };
+
+ serifFontFallback = mkFont {
+ family = "Sarasa Gothic J";
+ style = "Regular";
+ size = 16;
+ };
+
+ sansSerifFont = mkFont {
+ family = "Iosevka Aile";
+ style = "Regular";
+ size = 16;
+ };
+
+ sansSerifFontFallback = mkFont {
+ family = "Sarasa Gothic J";
+ style = "Regular";
+ size = 16;
+ };
+
+ monospaceFont = mkFont {
+ family = "Iosevka";
+ style = "Regular";
+ size = 16;
+ };
+
+ monospaceFontFallback = mkFont {
+ family = "Sarasa Mono J";
+ style = "Regular";
+ size = 16;
+ };
+ };
+ };
+
+ config = mkMerge [
+ (mkIf cfg.enable {
+ hm.fonts.fontconfig.enable = true;
+
+ fonts = {
+ fonts = with pkgs; [iosevka sarasa-gothic];
+
+ fontconfig = {
+ enable = true;
+
+ defaultFonts = {
+ monospace = ["Iosevka" "Sarasa Mono" "DejaVu Sans Mono" "Noto Sans Mono"];
+ sansSerif = ["Iosevka Aile" "Sarasa Gothic" "DejaVu Sans" "Noto Sans"];
+ serif = ["Iosevka Etoile" "Sarasa Gothic" "DejaVu Serif" "Noto Serif"];
+ };
+ };
+ };
+ })
+ (mkIf (!cfg.enable) {
+ hm.fonts.fontconfig.enable = mkForce false;
+ fonts.fontconfig.enable = mkForce false;
+ })
+ ];
+}
diff --git a/modules/nixfiles/games/default.nix b/modules/nixfiles/games/default.nix
new file mode 100644
index 0000000..532fc57
--- /dev/null
+++ b/modules/nixfiles/games/default.nix
@@ -0,0 +1,47 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.games;
+in {
+ imports = [
+ ./gamemode.nix
+ ./gog.nix
+ ./lutris.nix
+ ./mangohud.nix
+ ./minecraft.nix
+ ./steam-run.nix
+ ./steam.nix
+ ];
+
+ options.nixfiles.modules.games.enable32BitSupport =
+ mkEnableOption "Whether to enable support for games.";
+
+ config = mkIf cfg.enable32BitSupport {
+ services = {
+ jack.alsa.support32Bit = config.services.jack.alsa.enable;
+
+ pipewire.alsa.support32Bit = config.services.pipewire.alsa.enable;
+
+ xserver.inputClassSections = [
+ ''
+ Identifier "ds-touchpad"
+ Driver "libinput"
+ MatchProduct "Wireless Controller Touchpad"
+ Option "Ignore" "true"
+ ''
+ ];
+ };
+
+ hardware = {
+ opengl = mkIf config.hardware.opengl.enable {
+ extraPackages32 = config.hardware.opengl.extraPackages;
+ driSupport32Bit = config.hardware.opengl.driSupport;
+ };
+
+ pulseaudio.support32Bit = config.hardware.pulseaudio.enable;
+ };
+ };
+}
diff --git a/modules/nixfiles/games/gamemode.nix b/modules/nixfiles/games/gamemode.nix
new file mode 100644
index 0000000..a74b651
--- /dev/null
+++ b/modules/nixfiles/games/gamemode.nix
@@ -0,0 +1,13 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.games.gamemode;
+in {
+ options.nixfiles.modules.games.gamemode.enable =
+ mkEnableOption "Whether to enable GameMode.";
+
+ config = mkIf cfg.enable {programs.gamemode.enable = true;};
+}
diff --git a/modules/nixfiles/games/gog.nix b/modules/nixfiles/games/gog.nix
new file mode 100644
index 0000000..f1188a2
--- /dev/null
+++ b/modules/nixfiles/games/gog.nix
@@ -0,0 +1,19 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.games.gog;
+in {
+ options.nixfiles.modules.games.gog.enable =
+ mkEnableOption
+ "Whether to enable GOG clients and the ability to run GOG games.";
+
+ config = mkIf cfg.enable {
+ nixfiles.modules.games.steam-run.enable = true;
+
+ hm.home.packages = with pkgs; [lgogdownloader];
+ };
+}
diff --git a/modules/nixfiles/games/lutris.nix b/modules/nixfiles/games/lutris.nix
new file mode 100644
index 0000000..ec1eaa2
--- /dev/null
+++ b/modules/nixfiles/games/lutris.nix
@@ -0,0 +1,37 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.games.lutris;
+in {
+ options.nixfiles.modules.games.lutris.enable =
+ mkEnableOption "Whether to enable Lutris.";
+
+ config = mkIf cfg.enable {
+ nixfiles.modules.games = {
+ steam-run.enable = true;
+ gamemode.enable = true;
+ };
+
+ hm.home.packages = with pkgs; [
+ (lutris.override {
+ lutris-unwrapped = lutris-unwrapped.override {
+ wine = buildFHSUserEnv {
+ # We don't really need Wine because Lutris downloads required
+ # runtime files for us. This feature is more robust because you can
+ # juggle different versions without manually rebuilding anything
+ # because nixpkgs cache was pruned.
+ name = "empty";
+ };
+ };
+ steamSupport = false;
+ extraPkgs = _: [
+ driversi686Linux.mesa # Battle.net
+ ];
+ })
+ ];
+ };
+}
diff --git a/modules/nixfiles/games/mangohud.nix b/modules/nixfiles/games/mangohud.nix
new file mode 100644
index 0000000..21d5fd1
--- /dev/null
+++ b/modules/nixfiles/games/mangohud.nix
@@ -0,0 +1,13 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.games.mangohud;
+in {
+ options.nixfiles.modules.games.mangohud.enable =
+ mkEnableOption "Whether to enable MangoHud.";
+
+ config = mkIf cfg.enable {hm.programs.mangohud.enable = true;};
+}
diff --git a/modules/nixfiles/games/minecraft.nix b/modules/nixfiles/games/minecraft.nix
new file mode 100644
index 0000000..70a98ce
--- /dev/null
+++ b/modules/nixfiles/games/minecraft.nix
@@ -0,0 +1,15 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.games.minecraft;
+in {
+ options.nixfiles.modules.games.minecraft.enable =
+ mkEnableOption "Whether to enable Minecraft.";
+
+ config =
+ mkIf cfg.enable {hm.home.packages = with pkgs; [jre nixfiles.UltimMC];};
+}
diff --git a/modules/nixfiles/games/steam-run.nix b/modules/nixfiles/games/steam-run.nix
new file mode 100644
index 0000000..983cc77
--- /dev/null
+++ b/modules/nixfiles/games/steam-run.nix
@@ -0,0 +1,59 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.games.steam-run;
+in {
+ options.nixfiles.modules.games.steam-run = {
+ enable = mkEnableOption "Whether to enable native Steam runtime.";
+
+ quirks = {
+ mountandblade = mkEnableOption ''Fix "Mount & Blade: Warband" issues.'';
+ };
+ };
+
+ config = mkIf cfg.enable {
+ nixfiles.modules = {
+ games = {
+ enable32BitSupport = true;
+ gamemode.enable = true;
+ };
+ };
+
+ hm.home.packages = with pkgs; [
+ (steam.override {
+ extraLibraries = _:
+ with cfg.quirks;
+ optionals mountandblade [
+ (glew.overrideAttrs (_: super: let
+ opname = super.pname;
+ in rec {
+ pname = "${opname}-mountandblade";
+ inherit (super) version;
+ src = fetchurl {
+ url = "mirror://sourceforge/${opname}/${opname}-${version}.tgz";
+ sha256 = "sha256-BN6R5+Z2MDm8EZQAlc2cf4gLq6ghlqd2X3J6wFqZPJU=";
+ };
+ }))
+ (fmodex.overrideAttrs (_: super: let
+ opname = super.pname;
+ in rec {
+ pname = "${opname}-mountandblade";
+ inherit (super) version;
+ installPhase = let
+ libPath =
+ makeLibraryPath [alsa-lib libpulseaudio stdenv.cc.cc];
+ in ''
+ install -Dm755 api/lib/libfmodex64-${version}.so $out/lib/libfmodex64.so
+ patchelf --set-rpath ${libPath} $out/lib/libfmodex64.so
+ '';
+ }))
+ ];
+ })
+ .run
+ ];
+ };
+}
diff --git a/modules/nixfiles/games/steam.nix b/modules/nixfiles/games/steam.nix
new file mode 100644
index 0000000..c1d471e
--- /dev/null
+++ b/modules/nixfiles/games/steam.nix
@@ -0,0 +1,21 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.games.steam;
+in {
+ options.nixfiles.modules.games.steam.enable =
+ mkEnableOption "Whether to enable Steam runtime.";
+
+ config = mkIf cfg.enable {
+ nixfiles.modules.games = {
+ enable32BitSupport = true;
+ gamemode.enable = true;
+ };
+
+ hm.home.packages = with pkgs; [steam];
+ };
+}
diff --git a/modules/nixfiles/git.nix b/modules/nixfiles/git.nix
new file mode 100644
index 0000000..9008c2a
--- /dev/null
+++ b/modules/nixfiles/git.nix
@@ -0,0 +1,125 @@
+{
+ config,
+ lib,
+ inputs,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.git;
+in {
+ options.nixfiles.modules.git.enable =
+ mkEnableOption "Whether to enable Git version control system.";
+
+ config = mkIf cfg.enable {
+ secrets = {
+ glab-cli-config = {
+ file = "${inputs.self}/secrets/glab-cli-config";
+ path = "${config.dirs.config}/glab-cli/config.yml";
+ owner = my.username;
+ };
+ gh-hosts = {
+ file = "${inputs.self}/secrets/gh-hosts";
+ path = "${config.dirs.config}/gh/hosts.yml";
+ owner = my.username;
+ };
+ hut = {
+ file = "${inputs.self}/secrets/hut";
+ path = "${config.dirs.config}/hut/config";
+ owner = my.username;
+ };
+ };
+
+ hm = {
+ home.packages = with pkgs; [glab hut];
+
+ programs = {
+ git = {
+ enable = true;
+
+ package = pkgs.git.override {
+ sendEmailSupport = true;
+ withSsh = true;
+ };
+
+ userName = my.fullname;
+ userEmail = my.email;
+ signing = {
+ inherit (my.pgp) key;
+ signByDefault = true;
+ };
+
+ extraConfig =
+ {
+ advice.detachedHead = false;
+ color.ui = true;
+ core.whitespace = "trailing-space";
+ diff = {
+ mnemonicPrefix = true;
+ renames = "copies";
+ submodule = "log";
+ };
+ init.defaultBranch = "master";
+ status.submoduleSummary = true;
+ github.user = my.username;
+ gitlab.user = my.username;
+ }
+ // mapAttrs'
+ (n: v: nameValuePair ''url "git@${v}:"'' {insteadOf = "${n}:";}) {
+ "bitbucket" = "bitbucket.com";
+ "codeberg" = "codeberg.org";
+ "github" = "github.com";
+ "gitlab" = "gitlab.com";
+ "sourcehut" = "git.sr.ht";
+ };
+
+ aliases = let
+ git = "${config.hm.programs.git.package}/bin/git";
+ curl = "${pkgs.curl}/bin/curl";
+ in {
+ fuck = "!${git} reset --hard && ${git} clean -fdx";
+ gud = ''commit -m "git gud"'';
+ wtc = "!${curl} -sq whatthecommit.com/index.txt | ${git} commit -F -";
+ };
+
+ # All helper tool/editor generated files should go here. This must be
+ # kept relatively clean and void of any tooling/project-specific
+ # residual files.
+ ignores = [
+ "*~"
+ ".ccls-cache/"
+ ".clangd/"
+ ".dir-locals.el"
+ ".gdb_history"
+ ".netrwhist"
+ "[._]*.s[a-v][a-z]"
+ "[._]*.sw[a-p]"
+ "[._]s[a-rt-v][a-z]"
+ "[._]ss[a-gi-z]"
+ "[._]sw[a-p]"
+ "\\#*\\#"
+ "compile_commands.json"
+ "cscope.*"
+ "vgcore.*"
+ ];
+ };
+
+ gh = {
+ enable = true;
+ settings.git_protocol = "ssh";
+ };
+
+ bash = {
+ shellAliases.gl = "${pkgs.glab}/bin/glab";
+ initExtra = mkAfter "_complete_alias gl __start_glab glab";
+ };
+ };
+
+ xdg.configFile."glab-cli/aliases.yml".text = generators.toYAML {} {
+ ci = "pipeline ci";
+ co = "mr checkout";
+ li = "ci lint";
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/gnome.nix b/modules/nixfiles/gnome.nix
new file mode 100644
index 0000000..2d0f6f6
--- /dev/null
+++ b/modules/nixfiles/gnome.nix
@@ -0,0 +1,65 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.gnome;
+in {
+ options.nixfiles.modules.gnome.enable =
+ mkEnableOption "Whether to enable the GNOME desktop environment.";
+
+ config = mkIf cfg.enable {
+ nixfiles.modules = {
+ gnupg.pinentry = mkForce "gnome";
+ sound.enable = true;
+ x11.enable = true;
+ };
+
+ services = {
+ xserver = {
+ enable = true;
+ desktopManager.gnome.enable = true;
+ displayManager.gdm = {
+ enable = true;
+ wayland = false;
+ };
+ };
+
+ gnome = {
+ core-os-services.enable = true;
+ core-shell.enable = true;
+ core-utilities.enable = false;
+ core-developer-tools.enable = false;
+ games.enable = false;
+
+ chrome-gnome-shell.enable = false;
+ gnome-initial-setup.enable = false;
+ gnome-online-accounts.enable = false;
+ gnome-remote-desktop.enable = false;
+ gnome-settings-daemon.enable = true;
+ gnome-user-share.enable = false;
+ rygel.enable = false;
+ tracker-miners.enable = false;
+ tracker.enable = false;
+ };
+
+ dleyna-renderer.enable = false;
+ dleyna-server.enable = false;
+ };
+
+ environment = {
+ gnome = {
+ excludePackages = with pkgs.gnome; [
+ geary
+ gnome-disk-utility
+ seahorse
+ sushi
+ ];
+ };
+
+ systemPackages = with pkgs; [pinentry-gnome];
+ };
+ };
+}
diff --git a/modules/nixfiles/gnupg.nix b/modules/nixfiles/gnupg.nix
new file mode 100644
index 0000000..16b8264
--- /dev/null
+++ b/modules/nixfiles/gnupg.nix
@@ -0,0 +1,91 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.gnupg;
+in {
+ options.nixfiles.modules.gnupg = {
+ enable = mkEnableOption "Whether to enable GnuPG.";
+
+ pinentry = mkOption {
+ description = "Name of a pinentry implementation.";
+ type = types.str;
+ default = with config.nixfiles.modules;
+ if kde.enable
+ then "qt"
+ else if gnome.enable
+ then "gnome"
+ else "curses";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ hm = {
+ programs.gpg = {
+ enable = true;
+
+ settings =
+ {
+ display-charset = "utf-8";
+ enable-progress-filter = true;
+ fixed-list-mode = true;
+ keyid-format = "0xlong";
+ no-comments = true;
+ no-emit-version = true;
+ no-greeting = true;
+ with-fingerprint = true;
+ throw-keyids = false;
+
+ use-agent = true;
+
+ armor = true;
+
+ no-random-seed-file = true;
+
+ list-options = "show-uid-validity";
+ verify-options = "show-uid-validity";
+ }
+ // (let
+ cipherAlgos = ["AES256" "AES192" "AES"];
+ compressionAlgos = ["ZLIB" "BZIP2" "ZIP" "Uncompressed"];
+ digestAlgos = ["SHA512" "SHA384" "SHA256" "SHA224"];
+
+ cs = concatStringsSep " ";
+ in {
+ default-preference-list =
+ cs (digestAlgos ++ cipherAlgos ++ compressionAlgos);
+
+ personal-cipher-preferences = cs cipherAlgos;
+ personal-compress-preferences = cs compressionAlgos;
+ personal-digest-preferences = cs digestAlgos;
+
+ s2k-cipher-algo = head cipherAlgos;
+ s2k-digest-algo = head digestAlgos;
+
+ digest-algo = head digestAlgos;
+ cert-digest-algo = head digestAlgos;
+ });
+ };
+
+ services.gpg-agent = {
+ enable = true;
+
+ enableSshSupport = true;
+ enableScDaemon = false;
+
+ defaultCacheTtl = 999999;
+ defaultCacheTtlSsh = 999999;
+ maxCacheTtl = 999999;
+ maxCacheTtlSsh = 999999;
+
+ grabKeyboardAndMouse = true;
+
+ sshKeys = [my.pgp.grip];
+
+ pinentryFlavor = cfg.pinentry;
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/gotify.nix b/modules/nixfiles/gotify.nix
new file mode 100644
index 0000000..b2bf3ae
--- /dev/null
+++ b/modules/nixfiles/gotify.nix
@@ -0,0 +1,66 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.gotify;
+in {
+ options.nixfiles.modules.gotify = {
+ enable = mkEnableOption "Whether to enable Gotify.";
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; str;
+ default = "gotify.${config.networking.domain}";
+ };
+ };
+
+ config = let
+ db = "gotify";
+ in
+ mkIf cfg.enable {
+ nixfiles.modules = {
+ nginx = {
+ enable = true;
+ virtualHosts.${cfg.domain} = {
+ locations."/" = {
+ proxyPass = "http://127.0.0.1:${toString config.services.gotify.port}";
+ proxyWebsockets = true;
+ };
+ };
+ };
+ postgresql.enable = true;
+ };
+
+ services = {
+ gotify = {
+ enable = true;
+ port = 7665;
+ };
+
+ postgresql = {
+ ensureDatabases = [db];
+ ensureUsers = [
+ {
+ name = db;
+ ensurePermissions."DATABASE \"${db}\"" = "ALL PRIVILEGES";
+ }
+ ];
+ };
+ };
+
+ systemd.services.gotify-server = {
+ after = ["network-online.target" "postgresql.service"];
+ environment = {
+ GOTIFY_DATABASE_DIALECT = "postgres";
+ GOTIFY_DATABASE_CONNECTION = concatStringsSep " " [
+ "host=/run/postgresql"
+ "user=${db}"
+ "dbname=${db}"
+ "sslmode=disable"
+ ];
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/grafana.nix b/modules/nixfiles/grafana.nix
new file mode 100644
index 0000000..4340f04
--- /dev/null
+++ b/modules/nixfiles/grafana.nix
@@ -0,0 +1,84 @@
+{
+ config,
+ inputs,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.grafana;
+in {
+ options.nixfiles.modules.grafana = {
+ enable = mkEnableOption "Whether to enable Grafana.";
+
+ port = mkOption {
+ description = "Port.";
+ type = with types; port;
+ default = 30101;
+ };
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; nullOr str;
+ default = null;
+ };
+ };
+
+ config = mkIf cfg.enable {
+ secrets = {
+ grafana-admin-password = {
+ file = "${inputs.self}/secrets/grafana-admin-password";
+ owner = "grafana";
+ group = "grafana";
+ };
+ grafana-key = {
+ file = "${inputs.self}/secrets/grafana-key";
+ owner = "grafana";
+ group = "grafana";
+ };
+ };
+
+ nixfiles.modules = {
+ nginx = {
+ enable = true;
+ virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://127.0.0.1:${toString cfg.port}";
+ };
+ postgresql.enable = true;
+ };
+
+ services = let
+ db = "grafana";
+ in {
+ grafana = {
+ enable = true;
+
+ inherit (cfg) domain port;
+ protocol = "http";
+ addr = "127.0.0.1";
+
+ analytics.reporting.enable = false;
+
+ database = {
+ type = "postgres";
+ host = "/run/postgresql";
+ name = db;
+ user = db;
+ };
+
+ security = with config.secrets; {
+ secretKeyFile = grafana-key.path;
+ adminPasswordFile = grafana-admin-password.path;
+ };
+ };
+
+ postgresql = {
+ ensureDatabases = [db];
+ ensureUsers = [
+ {
+ name = db;
+ ensurePermissions."DATABASE \"${db}\"" = "ALL PRIVILEGES";
+ }
+ ];
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/htop.nix b/modules/nixfiles/htop.nix
new file mode 100644
index 0000000..9cb3e60
--- /dev/null
+++ b/modules/nixfiles/htop.nix
@@ -0,0 +1,57 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.htop;
+in {
+ options.nixfiles.modules.htop.enable =
+ mkEnableOption "Whether to enable htop.";
+
+ config = mkIf cfg.enable {
+ hm.programs.htop = {
+ enable = true;
+
+ settings = with config.hm.lib.htop; {
+ fields = with fields; [
+ PID
+ USER
+ PRIORITY
+ NICE
+ M_SIZE
+ M_RESIDENT
+ M_SHARE
+ STATE
+ PERCENT_CPU
+ PERCENT_MEM
+ TIME
+ COMM
+ ];
+ account_guest_in_cpu_meter = 1;
+ detailed_cpu_time = 0;
+ enable_mouse = 0;
+ find_comm_in_cmdline = 1;
+ header_margin = 1;
+ hide_function_bar = 1;
+ hide_kernel_threads = 1;
+ hide_userland_threads = 1;
+ highlight_base_name = 1;
+ highlight_changes = 0;
+ highlight_changes_delay_secs = 1;
+ highlight_deleted_exe = 1;
+ highlight_megabytes = 1;
+ highlight_threads = 1;
+ shadow_other_users = 1;
+ show_cpu_frequency = 1;
+ show_cpu_usage = 1;
+ show_program_path = 0;
+ show_thread_names = 0;
+ strip_exe_from_cmdline = 1;
+ tree_view = 1;
+ tree_view_always_by_pid = 1;
+ update_process_names = 1;
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/hydra.nix b/modules/nixfiles/hydra.nix
new file mode 100644
index 0000000..4161eb3
--- /dev/null
+++ b/modules/nixfiles/hydra.nix
@@ -0,0 +1,56 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.hydra;
+in {
+ options.nixfiles.modules.hydra = {
+ enable = mkEnableOption "Whether to enable Nginx.";
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; str;
+ default = "hydra.${config.networking.domain}";
+ };
+
+ port = mkOption {
+ description = "Port.";
+ type = with types; port;
+ default = 7754;
+ };
+ };
+
+ config = mkIf cfg.enable {
+ nixfiles.modules = {
+ nginx = {
+ enable = true;
+ virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://127.0.0.1:${toString cfg.port}";
+ };
+ postgresql.enable = true;
+ };
+
+ services = let
+ db = "hydra";
+ in {
+ hydra = {
+ enable = true;
+ listenHost = "127.0.0.1";
+ inherit (cfg) port;
+ dbi = "dbi:Pg:dbname=${db};user=${db}";
+ hydraURL = cfg.domain;
+ };
+
+ postgresql = {
+ ensureDatabases = [db];
+ ensureUsers = [
+ {
+ name = db;
+ ensurePermissions."DATABASE \"${db}\"" = "ALL PRIVILEGES";
+ }
+ ];
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/ipfs.nix b/modules/nixfiles/ipfs.nix
new file mode 100644
index 0000000..501a07c
--- /dev/null
+++ b/modules/nixfiles/ipfs.nix
@@ -0,0 +1,168 @@
+{
+ config,
+ lib,
+ this,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.ipfs;
+
+ swarmDefaultPort = 4001;
+ apiDefaultPort = 5001;
+ gatewayDefaultPort = 6001;
+in {
+ options.nixfiles.modules.ipfs = {
+ enable = mkEnableOption "Whether to enable IPFS daemon.";
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; str;
+ default = "ipfs.${config.networking.fqdn}";
+ };
+
+ swarmPort = mkOption {
+ description = "Swarm port.";
+ type = with types; port;
+ default =
+ if this.isHeadless
+ then swarmDefaultPort + 990
+ else swarmDefaultPort;
+ };
+
+ apiPort = mkOption {
+ description = "API port.";
+ type = with types; port;
+ default =
+ if this.isHeadless
+ then apiDefaultPort + 990
+ else apiDefaultPort;
+ };
+
+ gatewayPort = mkOption {
+ description = "Gateway port.";
+ type = with types; port;
+ default =
+ if this.isHeadless
+ then gatewayDefaultPort + 990
+ else gatewayDefaultPort;
+ };
+ };
+
+ config = mkIf cfg.enable (mkMerge [
+ {
+ services.ipfs = {
+ enable = true;
+
+ user = my.username;
+ inherit (config.my) group;
+
+ dataDir = "${config.my.home}/.ipfs";
+
+ swarmAddress = let
+ port = toString cfg.swarmPort;
+ in
+ if this.isHeadless
+ then [
+ "/ip4/127.0.0.1/tcp/${port}"
+ "/ip4/127.0.0.1/udp/${port}/quic"
+ ]
+ else [
+ "/ip4/0.0.0.0/tcp/${port}"
+ "/ip6/::/tcp/${port}"
+ "/ip4/0.0.0.0/udp/${port}/quic"
+ "/ip6/::/udp/${port}/quic"
+ ];
+ apiAddress = "/ip4/127.0.0.1/tcp/${toString cfg.apiPort}";
+ gatewayAddress = "/ip4/127.0.0.1/tcp/${toString cfg.gatewayPort}";
+
+ autoMigrate = true;
+ autoMount = true;
+ emptyRepo = true;
+ enableGC = true;
+
+ extraConfig = mkMerge [
+ (let
+ filterAddresses =
+ [
+ "/ip4/100.64.0.0/ipcidr/10"
+ "/ip4/169.254.0.0/ipcidr/16"
+ "/ip4/172.16.0.0/ipcidr/12"
+ "/ip4/192.0.0.0/ipcidr/24"
+ "/ip4/192.0.2.0/ipcidr/24"
+ "/ip4/192.168.0.0/ipcidr/16"
+ "/ip4/198.18.0.0/ipcidr/15"
+ "/ip4/198.51.100.0/ipcidr/24"
+ "/ip4/203.0.113.0/ipcidr/24"
+ "/ip4/240.0.0.0/ipcidr/4"
+ "/ip6/100::/ipcidr/64"
+ "/ip6/2001:2::/ipcidr/48"
+ "/ip6/2001:db8::/ipcidr/32"
+ "/ip6/fe80::/ipcidr/10"
+ ]
+ ++ optionals (!hasAttr "wireguard" this) [
+ "/ip4/10.0.0.0/ipcidr/8"
+ "/ip6/fc00::/ipcidr/7"
+ ];
+ in {
+ Addresses = with config.services.ipfs; {
+ # https://github.com/NixOS/nixpkgs/pull/165259
+ # I think this shit broke inheritance... Gotta test more and make
+ # a PR I guess.
+ API = apiAddress;
+ Gateway = gatewayAddress;
+ Swarm = swarmAddress;
+
+ NoAnnounce = filterAddresses;
+ };
+ Swarm.AddrFilters = filterAddresses;
+ API.HTTPHeaders.Access-Control-Allow-Methods = ["GET" "POST" "PUT"];
+ })
+ (mkIf this.isHeadful {
+ API.HTTPHeaders.Access-Control-Allow-Origin = ["*"];
+ })
+ (mkIf this.isHeadless {
+ API.HTTPHeaders.Access-Control-Allow-Origin = ["https://${cfg.domain}" "https://api.${cfg.domain}"];
+ })
+ ];
+ };
+
+ networking.firewall = rec {
+ allowedTCPPorts = [swarmDefaultPort];
+ allowedUDPPorts = allowedTCPPorts;
+ };
+ }
+ (mkIf this.isHeadless {
+ nixfiles.modules.nginx = {
+ enable = true;
+ virtualHosts = {
+ ${cfg.domain}.locations."/".proxyPass = "http://127.0.0.1:${toString cfg.gatewayPort}";
+ "swarm.${cfg.domain}" = {
+ serverName = cfg.domain;
+ listen = [
+ {
+ addr = "0.0.0.0";
+ port = swarmDefaultPort;
+ }
+ {
+ addr = "[::0]";
+ port = swarmDefaultPort;
+ }
+ ];
+ locations."/".proxyPass = "http://127.0.0.1:${toString cfg.swarmPort}";
+ };
+ "api.${cfg.domain}" = {
+ # TODO Redirect "/" to "/webui" but keep other endpoints.
+ locations."/" = {
+ proxyPass = "http://127.0.0.1:${toString cfg.apiPort}";
+ extraConfig = ''
+ if ($internal != 1) {
+ return 403;
+ }
+ '';
+ };
+ };
+ };
+ };
+ })
+ ]);
+}
diff --git a/modules/nixfiles/kde.nix b/modules/nixfiles/kde.nix
new file mode 100644
index 0000000..571cb6d
--- /dev/null
+++ b/modules/nixfiles/kde.nix
@@ -0,0 +1,27 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.kde;
+in {
+ options.nixfiles.modules.kde.enable =
+ mkEnableOption "Whether to enable KDE Plasma 5.";
+
+ config = mkIf cfg.enable {
+ nixfiles.modules = {
+ gnupg.pinentry = mkForce "qt";
+ sound.enable = true;
+ x11.enable = true;
+ };
+
+ services.xserver = {
+ desktopManager.plasma5.enable = true;
+ displayManager.sddm.enable = true;
+ };
+
+ environment.systemPackages = with pkgs; [pinentry-qt];
+ };
+}
diff --git a/modules/nixfiles/libvirtd.nix b/modules/nixfiles/libvirtd.nix
new file mode 100644
index 0000000..5ce37f0
--- /dev/null
+++ b/modules/nixfiles/libvirtd.nix
@@ -0,0 +1,40 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.libvirtd;
+in {
+ options.nixfiles.modules.libvirtd.enable =
+ mkEnableOption "Wether to enable libvirtd.";
+
+ config = mkIf cfg.enable {
+ virtualisation.libvirtd = {
+ enable = true;
+
+ onBoot = "ignore";
+ onShutdown = "shutdown";
+
+ qemu = {
+ package = pkgs.qemu_kvm;
+ runAsRoot = false;
+
+ ovmf = {
+ enable = true;
+ packages = [pkgs.OVMFFull.fd];
+ };
+
+ swtpm = {
+ enable = false;
+ package = pkgs.swtpm-tpm2;
+ };
+ };
+ };
+
+ environment.systemPackages = with pkgs; [virt-manager qemu-utils];
+
+ my.extraGroups = ["libvirtd"];
+ };
+}
diff --git a/modules/nixfiles/lidarr.nix b/modules/nixfiles/lidarr.nix
new file mode 100644
index 0000000..54d1fe5
--- /dev/null
+++ b/modules/nixfiles/lidarr.nix
@@ -0,0 +1,27 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.lidarr;
+in {
+ options.nixfiles.modules.lidarr = {
+ enable = mkEnableOption "Whether to enable Lidarr.";
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; str;
+ default = "lidarr.${config.networking.fqdn}";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ nixfiles.modules.nginx = {
+ enable = true;
+ virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://127.0.0.1:8686";
+ };
+
+ services.lidarr.enable = true;
+ };
+}
diff --git a/modules/nixfiles/loki.nix b/modules/nixfiles/loki.nix
new file mode 100644
index 0000000..27217bd
--- /dev/null
+++ b/modules/nixfiles/loki.nix
@@ -0,0 +1,113 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.loki;
+in {
+ options.nixfiles.modules.loki = {
+ # TODO Figure out why this shit refuses to work with my configuraiton.
+ enable = mkEnableOption "Whether to enable Loki.";
+
+ port = mkOption {
+ description = "Port.";
+ type = with types; port;
+ default = 30171;
+ };
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; str;
+ default = config.nixfiles.modules.monitoring.domain;
+ };
+
+ path = mkOption {
+ description = "Path.";
+ type = with types; str;
+ default = "/loki";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ nixfiles.modules.nginx = with cfg; {
+ enable = true;
+ virtualHosts.${domain}.locations.${path} = {
+ proxyPass = "http://127.0.0.1:${toString port}";
+ extraConfig = ''
+ if ($internal != 1) {
+ return 403;
+ }
+ '';
+ };
+ };
+
+ services.loki = {
+ enable = true;
+
+ configuration = rec {
+ auth_enabled = false;
+
+ server = rec {
+ http_listen_address = "127.0.0.1";
+ http_listen_port = cfg.port;
+ http_path_prefix = cfg.path;
+
+ grpc_listen_address = "127.0.0.1";
+ grpc_listen_port = http_listen_port + 1;
+
+ log_level = "warn";
+ };
+
+ common = rec {
+ path_prefix = "/var/lib/loki";
+ storage.filesystem = {
+ chunks_directory = "${path_prefix}/chunker";
+ rules_directory = "${path_prefix}/ruler";
+ };
+ replication_factor = 1;
+ instance_interface_names = ["lo"];
+ ring = {
+ instance_addr = "127.0.0.1";
+ kvstore.store = "inmemory";
+ };
+ };
+
+ ruler = {
+ rule_path = "${common.path_prefix}/ruler";
+ storage = {
+ type = "local";
+ local.directory =
+ pkgs.writeTextDir "ruler/ruler.yml"
+ (generators.toJSON {} {groups = [{name = "default";}];});
+ };
+ };
+
+ schema_config.configs = [
+ {
+ from = "2020-01-01";
+ store = "boltdb-shipper";
+ object_store = "filesystem";
+ schema = "v11";
+ index = {
+ prefix = "index_";
+ period = "24h";
+ };
+ chunks = {
+ prefix = "chunks_";
+ period = "24h";
+ };
+ }
+ ];
+
+ analytics.reporting_enabled = false;
+ };
+ };
+
+ systemd.tmpfiles.rules = [
+ "d /var/lib/loki 0700 loki loki - -"
+ "d /var/lib/loki/ruler 0700 loki loki - -"
+ ];
+ };
+}
diff --git a/modules/nixfiles/lxc.nix b/modules/nixfiles/lxc.nix
new file mode 100644
index 0000000..ea4d8a6
--- /dev/null
+++ b/modules/nixfiles/lxc.nix
@@ -0,0 +1,16 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.lxc;
+in {
+ options.nixfiles.modules.lxc.enable =
+ mkEnableOption "Whether to enable LXC/LXD.";
+
+ config = mkIf cfg.enable {
+ virtualisation.lxd.enable = true;
+ my.extraGroups = "lxd";
+ };
+}
diff --git a/modules/nixfiles/matrix/default.nix b/modules/nixfiles/matrix/default.nix
new file mode 100644
index 0000000..bd221c4
--- /dev/null
+++ b/modules/nixfiles/matrix/default.nix
@@ -0,0 +1 @@
+_: {imports = [./dendrite.nix ./element.nix ./synapse.nix];}
diff --git a/modules/nixfiles/matrix/dendrite.nix b/modules/nixfiles/matrix/dendrite.nix
new file mode 100644
index 0000000..4e40e97
--- /dev/null
+++ b/modules/nixfiles/matrix/dendrite.nix
@@ -0,0 +1,148 @@
+{
+ config,
+ lib,
+ inputs,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.matrix.dendrite;
+in {
+ options.nixfiles.modules.matrix.dendrite = {
+ enable = mkEnableOption "Whether to enable Dendrite Matrix server.";
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; str;
+ default = config.networking.domain;
+ };
+ };
+
+ config = mkIf cfg.enable {
+ secrets.dendrite-private-key = {
+ file = "${inputs.self}/secrets/dendrite-private-key";
+ mode = "0444"; # User is dynamic.
+ };
+
+ nixfiles.modules = {
+ nginx = {
+ enable = true;
+ virtualHosts.${cfg.domain}.locations = {
+ "/_matrix".proxyPass = "http://127.0.0.1:${toString config.services.dendrite.httpPort}";
+ "= /.well-known/matrix/server" = {
+ extraConfig = ''
+ add_header Content-Type application/json;
+ '';
+ return = "200 '${
+ generators.toJSON {} {"m.server" = "${cfg.domain}:443";}
+ }'";
+ };
+ "= /.well-known/matrix/client" = {
+ extraConfig = ''
+ add_header Content-Type application/json;
+ add_header Access-Control-Allow-Origin *;
+ '';
+ return = "200 '${
+ generators.toJSON {} {
+ "m.homeserver".base_url = "https://${cfg.domain}";
+ }
+ }'";
+ };
+ };
+ };
+ postgresql.enable = true;
+ };
+
+ services = let
+ name = "dendrite";
+ prefix = "${name}-";
+ databaseNames = [
+ "account"
+ "appservice"
+ "device"
+ "federation"
+ "key"
+ "media"
+ "mscs"
+ "room"
+ "sync"
+ ];
+ databaseList = forEach databaseNames (x: concatStrings [prefix x]);
+ databaseAttr = genAttrs databaseNames (x: concatStrings [prefix x]);
+
+ mkDatabaseConnection = database: "postgres://${name}@/${database}?sslmode=disable";
+ in {
+ dendrite = {
+ enable = true;
+ httpPort = 8008;
+ settings = {
+ global = {
+ server_name = cfg.domain;
+
+ private_key = config.secrets.dendrite-private-key.path;
+
+ disable_federation = false;
+ };
+
+ app_service_api.database.connection_string =
+ mkDatabaseConnection databaseAttr.appservice;
+
+ client_api = {
+ registration_disabled = true;
+ turn = {}; # TODO
+ };
+
+ federation_api.database.connection_string =
+ mkDatabaseConnection databaseAttr.federation;
+
+ key_server.database.connection_string =
+ mkDatabaseConnection databaseAttr.key;
+
+ media_api = {
+ database.connection_string =
+ mkDatabaseConnection databaseAttr.media;
+ base_path = "./media";
+ };
+
+ mscs.database.connection_string =
+ mkDatabaseConnection databaseAttr.mscs;
+
+ room_server.database.connection_string =
+ mkDatabaseConnection databaseAttr.room;
+
+ sync_api.database.connection_string =
+ mkDatabaseConnection databaseAttr.sync;
+
+ user_api = {
+ account_database.connection_string =
+ mkDatabaseConnection databaseAttr.account;
+ device_database.connection_string =
+ mkDatabaseConnection databaseAttr.device;
+ };
+
+ metrics.enabled = false; # TODO
+ };
+ };
+
+ postgresql = {
+ ensureDatabases = databaseList;
+ ensureUsers =
+ map (x: {
+ inherit name;
+ ensurePermissions."DATABASE \"${x}\"" = "ALL PRIVILEGES";
+ })
+ databaseList;
+ };
+ };
+
+ systemd.services.dendrite.serviceConfig.ExecStart =
+ mkForce
+ (concatStringsSep " " [
+ "${pkgs.dendrite}/bin/dendrite-monolith-server"
+ "--config /run/dendrite/dendrite.yaml"
+ "--http-bind-address 127.0.0.1:${
+ toString config.services.dendrite.httpPort
+ }"
+ ]);
+ };
+}
diff --git a/modules/nixfiles/matrix/element.nix b/modules/nixfiles/matrix/element.nix
new file mode 100644
index 0000000..79c9f97
--- /dev/null
+++ b/modules/nixfiles/matrix/element.nix
@@ -0,0 +1,59 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.matrix.element;
+in {
+ options.nixfiles.modules.matrix.element = {
+ enable = mkEnableOption "Whether to enable Element Matrix web interface.";
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; nullOr str;
+ default = "element.${config.networking.domain}";
+ };
+
+ homeserver = mkOption {
+ description = "Default Matrix homeserver.";
+ type = with types; str;
+ default = my.domain.azahi;
+ };
+ };
+
+ config = mkIf cfg.enable {
+ assertions = [
+ {
+ assertion = with config.nixfiles.modules.matrix;
+ (synapse.enable || dendrite.enable) && !(!synapse.enable && !dendrite.enable);
+ message = "Synapse or Dendrite must be enabled";
+ }
+ ];
+
+ nixfiles.modules.nginx = with cfg; {
+ enable = true;
+ virtualHosts.${domain}.locations."/".root = pkgs.element-web.override {
+ conf = {
+ default_server_config."m.homeserver" = {
+ base_url = "https://${homeserver}";
+ server_name = homeserver;
+ };
+ disable_custom_urls = true;
+ disable_guests = true;
+ disable_login_language_selector = true;
+ disable_3pid_login = true;
+ brand = homeserver;
+ branding.authFooterLinks = [
+ {
+ text = "Hosted on NixOS";
+ url = "https://nixos.org";
+ }
+ ];
+ default_theme = "dark";
+ };
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/matrix/synapse.nix b/modules/nixfiles/matrix/synapse.nix
new file mode 100644
index 0000000..cde5f8e
--- /dev/null
+++ b/modules/nixfiles/matrix/synapse.nix
@@ -0,0 +1,90 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.matrix.synapse;
+in {
+ options.nixfiles.modules.matrix.synapse = {
+ enable = mkEnableOption "Whether to enable Synapse Matrix server.";
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; str;
+ default = config.networking.domain;
+ };
+ };
+
+ config = let
+ bind_address = "127.0.0.1";
+ port = 8448;
+ in
+ mkIf cfg.enable {
+ nixfiles.modules = {
+ nginx = {
+ enable = true;
+ virtualHosts.${cfg.domain}.locations = {
+ "~ ^(/_matrix|/_synapse/client)".proxyPass = "http://${bind_address}:${toString port}";
+ "= /.well-known/matrix/server" = {
+ extraConfig = ''
+ add_header Content-Type application/json;
+ '';
+ return = "200 '${
+ generators.toJSON {} {"m.server" = "${cfg.domain}:443";}
+ }'";
+ };
+ "= /.well-known/matrix/client" = {
+ extraConfig = ''
+ add_header Content-Type application/json;
+ add_header Access-Control-Allow-Origin *;
+ '';
+ return = "200 '${
+ generators.toJSON {} {
+ "m.homeserver".base_url = "https://${cfg.domain}";
+ }
+ }'";
+ };
+ };
+ };
+ postgresql.enable = true;
+ };
+
+ services = let
+ db = "synapse";
+ in {
+ matrix-synapse = {
+ enable = true;
+ server_name = config.networking.domain;
+
+ database_type = "psycopg2";
+ database_name = db;
+ database_user = db;
+
+ listeners = [
+ {
+ inherit bind_address port;
+ type = "http";
+ tls = false;
+ x_forwarded = true;
+ resources = [
+ {
+ names = ["client" "federation"];
+ compress = false;
+ }
+ ];
+ }
+ ];
+ };
+
+ postgresql.initialScript = pkgs.writeText "init-matrix-synapse.sql" ''
+ CREATE USER "${db}";
+ CREATE DATABASE "${db}" WITH OWNER "${db}"
+ TEMPLATE template0
+ LC_COLLATE = "C"
+ LC_CTYPE = "C";
+ '';
+ };
+ };
+}
diff --git a/modules/nixfiles/monitoring.nix b/modules/nixfiles/monitoring.nix
new file mode 100644
index 0000000..6db74d4
--- /dev/null
+++ b/modules/nixfiles/monitoring.nix
@@ -0,0 +1,114 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.monitoring;
+in {
+ options.nixfiles.modules.monitoring = {
+ enable = mkEnableOption ''
+ Whether to enable custom monitoring stack.
+
+ Currently this configures and enables Grafana, Loki, Prometheus and
+ Alertmanager.
+ '';
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; nullOr str;
+ default = "monitoring.${config.networking.domain}";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ nixfiles.modules = {
+ grafana = {
+ enable = true;
+ inherit (cfg) domain;
+ };
+ loki = {
+ enable = true;
+ inherit (cfg) domain;
+ };
+ prometheus = {
+ enable = true;
+ inherit (cfg) domain;
+ };
+ alertmanager = {
+ enable = true;
+ inherit (cfg) domain;
+ };
+ };
+
+ services = {
+ grafana.provision = {
+ enable = true;
+ datasources = with config.nixfiles.modules; [
+ {
+ name = "Prometheus";
+ type = "prometheus";
+ access = "proxy";
+ url = with prometheus; "https://${domain}${path}";
+ isDefault = true;
+ }
+ {
+ name = "Loki";
+ type = "loki";
+ access = "proxy";
+ url = with loki; "https://${domain}${path}";
+ }
+ ];
+ # TODO Move dashboards to this repository.
+ dashboards = [
+ {
+ name = "system";
+ options.path = pkgs.fetchurl {
+ url = "https://gist.githubusercontent.com/azahi/b8951223e6850d88159b0c34749a20aa/raw/00c6928374b5d231dc3afe617165550868b8c233/System-1645616603173.json";
+ sha256 = "sha256-vGlCOHT5Rp1K88Z8lkLGkvTDeFpgi967CSOb/797vwY=";
+ };
+ }
+ {
+ name = "endlessh-go";
+ options.path = pkgs.fetchurl {
+ url = "https://gist.githubusercontent.com/azahi/fd917e917fb53deacf3c6d7366bae2b2/raw/796530bd899e9328e423d18a462faf3c7d01c823/endlessh.json";
+ sha256 = "sha256-/wJDiFlrHEa1f82pDFtG/T2GRKGlAur0dlQ8eeVJ3m4=";
+ };
+ }
+ {
+ name = "unbound";
+ options.path = pkgs.fetchurl {
+ url = "https://gist.githubusercontent.com/azahi/73fc27e22acc9a01898b2d3e21150aac/raw/37d03bc51253583d9265d6f2189242003092261e/Unbound-1645616052811.json";
+ sha256 = "sha256-PXDCCa5qOg/Hj9CBId0OVUYmPlOjS05NxhDQ6zWxGp0=";
+ };
+ }
+ {
+ name = "nginx";
+ options.path = pkgs.fetchurl {
+ url = "https://raw.githubusercontent.com/nginxinc/nginx-prometheus-exporter/452953738bfcb8aac02ce6e6ac32f53479cad2aa/grafana/dashboard.json";
+ sha256 = "sha256-HRDWsSQeLzybaJ1g5xZqtmhf8JPtGhv/6TD1aFsgJIw=";
+ };
+ }
+ {
+ name = "postgersql";
+ options.path = pkgs.fetchurl {
+ url = "https://raw.githubusercontent.com/lstn/misc-grafana-dashboards/051873e103119ff0edf9886dcdc0c6c0a3ee6702/dashboards/postgresql-database.json";
+ sha256 = "sha256-awlj3BM3GaBBG/zJvxsVBRzoM8i6aY5dV34++DZFQHs=";
+ };
+ }
+ ];
+ };
+
+ loki.configuration.ruler.alertmanager_url = with config.nixfiles.modules.alertmanager; "https://${domain}${path}";
+
+ prometheus.alertmanagers = [
+ {
+ scheme = "https";
+ path_prefix = config.nixfiles.modules.alertmanager.path;
+ static_configs = [{targets = [cfg.domain];}];
+ }
+ ];
+ };
+ };
+}
diff --git a/modules/nixfiles/mpd.nix b/modules/nixfiles/mpd.nix
new file mode 100644
index 0000000..b2962ab
--- /dev/null
+++ b/modules/nixfiles/mpd.nix
@@ -0,0 +1,212 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.mpd;
+in {
+ options.nixfiles.modules.mpd.enable =
+ mkEnableOption "Wether to enable MPD and its clients.";
+
+ config = mkIf cfg.enable {
+ hm = {
+ home.packages = with pkgs; [mpc_cli];
+
+ services.mpd = {
+ enable = true;
+
+ extraConfig = ''
+ restore_paused "yes"
+
+ auto_update "no"
+
+ replaygain "album"
+ replaygain_preamp "0"
+ replaygain_limit "yes"
+
+ volume_normalization "no"
+
+ zeroconf_enabled "no"
+
+ audio_output {
+ type "pipewire"
+ name "piepwire"
+ }
+ '';
+ };
+
+ programs.ncmpcpp = {
+ enable = true;
+
+ settings = rec {
+ ncmpcpp_directory = "${config.hm.xdg.dataHome}/ncmpcpp";
+ lyrics_directory = "${ncmpcpp_directory}/lyrics";
+
+ playlist_disable_highlight_delay = 1;
+ message_delay_time = 1;
+
+ song_window_title_format = "{%a - }{%t}|{%f}";
+ song_list_format = "{$6%t}|{$2%f}$1 $R{$8%b}$1 {$5%a}";
+ song_columns_list_format = "(20)[red]{a} (30)[cyan]{b} (50)[blue]{t|f}";
+ song_status_format = "{{$8%a$9{ $b-$/b $6%b$9 {(%y)} } - } '{%t}}|{%f}'";
+ song_library_format = "{%n - }{%t}|{%f}";
+
+ now_playing_prefix = "$b";
+ now_playing_suffix = "$/b";
+
+ selected_item_prefix = "$0";
+ selected_item_suffix = "$9";
+ modified_item_prefix = "$3> $9";
+
+ browser_playlist_prefix = "$2playlist$9 ";
+ browser_sort_format = "{%a - }{%t}|{%f} {(%l)}";
+
+ playlist_show_mpd_host = false;
+ playlist_show_remaining_time = false;
+ playlist_shorten_total_times = false;
+ playlist_separate_albums = false;
+
+ playlist_display_mode = "classic";
+ browser_display_mode = "classic";
+ search_engine_display_mode = "classic";
+ playlist_editor_display_mode = "classic";
+
+ incremental_seeking = true;
+ seek_time = 1;
+
+ volume_change_step = 5;
+
+ autocenter_mode = true;
+ centered_cursor = true;
+
+ progressbar_look = "=*-";
+
+ default_place_to_search_in = "database";
+ search_engine_default_search_mode = 1;
+ data_fetching_delay = false;
+ media_library_primary_tag = "album_artist";
+ browser_sort_mode = "name";
+ default_find_mode = "wrapped";
+ default_tag_editor_pattern = "%n - %t";
+ empty_tag_marker = "<blank>";
+ tags_separator = " | ";
+ tag_editor_extended_numeration = true;
+ media_library_sort_by_mtime = false;
+ regular_expressions = "none";
+ block_search_constraints_change_if_items_found = true;
+
+ ignore_leading_the = true;
+
+ enable_window_title = false;
+
+ header_visibility = false;
+ statusbar_visibility = false;
+ titles_visibility = false;
+
+ display_volume_level = false;
+ display_bitrate = false;
+ display_remaining_time = false;
+
+ cyclic_scrolling = true;
+ lines_scrolled = 1;
+
+ follow_now_playing_lyrics = false;
+ fetch_lyrics_for_current_song_in_background = false;
+ store_lyrics_in_song_dir = false;
+
+ generate_win32_compatible_filenames = false;
+ allow_for_physical_item_deletion = false;
+ show_hidden_files_in_local_browser = false;
+
+ screen_switcher_mode = "playlist, browser";
+ startup_screen = "playlist";
+ startup_slave_screen = "";
+ startup_slave_screen_focus = false;
+ locked_screen_width_part = 50;
+ ask_for_locked_screen_width_part = true;
+
+ jump_to_now_playing_song_at_start = false;
+
+ ask_before_clearing_playlists = false;
+
+ clock_display_seconds = false;
+
+ mouse_support = false;
+
+ external_editor = "${config.programs.vim.package}/bin/vim";
+ use_console_editor = true;
+
+ colors_enabled = true;
+ discard_colors_if_item_is_selected = true;
+
+ empty_tag_color = "cyan";
+ header_window_color = "cyan";
+ volume_color = "cyan";
+ state_line_color = "cyan";
+ state_flags_color = "green";
+ main_window_color = "blue";
+ color1 = "cyan";
+ color2 = "red";
+ progressbar_color = "cyan";
+ progressbar_elapsed_color = "white";
+ statusbar_color = "yellow";
+ window_border_color = "green";
+ active_window_border = "red";
+ };
+
+ bindings = [
+ {
+ key = "j";
+ command = "scroll_down";
+ }
+ {
+ key = "k";
+ command = "scroll_up";
+ }
+ {
+ key = "J";
+ command = ["select_item" "scroll_down"];
+ }
+ {
+ key = "K";
+ command = ["select_item" "scroll_up"];
+ }
+ {
+ key = "h";
+ command = ["previous_column" "master_screen"];
+ }
+ {
+ key = "l";
+ command = ["next_column" "slave_screen"];
+ }
+ {
+ key = "g";
+ command = "move_home";
+ }
+ {
+ key = "G";
+ command = "move_end";
+ }
+ {
+ key = "d";
+ command = [
+ "delete_playlist_items"
+ "delete_browser_items"
+ "delete_stored_playlist"
+ ];
+ }
+ {
+ key = "L";
+ command = "show_lyrics";
+ }
+ {
+ key = "H";
+ command = "toggle_lyrics_fetcher";
+ }
+ ];
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/mpv.nix b/modules/nixfiles/mpv.nix
new file mode 100644
index 0000000..68628c5
--- /dev/null
+++ b/modules/nixfiles/mpv.nix
@@ -0,0 +1,133 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.mpv;
+in {
+ options.nixfiles.modules.mpv.enable = mkEnableOption "Whether to enable MPV.";
+
+ config = mkIf cfg.enable {
+ hm.programs = {
+ mpv = {
+ enable = true;
+
+ package = with pkgs;
+ mpv-with-scripts.override {
+ scripts = with mpvScripts; [autoload mpv-autosub sponsorblock];
+ };
+
+ bindings = {
+ "RIGHT" = "seek 10";
+ "LEFT" = "seek -10";
+ "UP" = "seek 60";
+ "DOWN" = "seek -60";
+
+ "Shift+RIGHT" = "no-osd seek 1 exact";
+ "Shift+LEFT" = "no-osd seek -1 exact";
+ "Shift+UP" = "no-osd seek 5 exact";
+ "Shift+DOWN" = "no-osd seek -5 exact";
+
+ "Alt+k" = "add sub-scale +0.1";
+ "Alt+j" = "add sub-scale -0.1";
+
+ "B" = ''cycle-values background "#000000" "#ffffff"'';
+ };
+
+ profiles = {
+ "protocol.http".force-window = "immediate";
+ "protocol.https".profile = "protocol.http";
+ };
+
+ config = let
+ lang = comcat [
+ "Japanese"
+ "japanese"
+ "jp"
+ "jpn"
+ "jaJP"
+ "ja-JP"
+ "English"
+ "english"
+ "en"
+ "eng"
+ "enUS"
+ "en-US"
+ "Russian"
+ "russian"
+ "ru"
+ "rus"
+ "ruRU"
+ "ru-RU"
+ ];
+ in {
+ audio-display = "no";
+ autofit-larger = "100%x95%";
+ cursor-autohide = 1000;
+ force-seekable = "no";
+ fullscreen = true;
+ load-unsafe-playlists = true;
+ msg-color = true;
+ msg-module = true;
+ prefetch-playlist = true;
+ save-position-on-quit = false;
+ screenshot-format = "png";
+ screenshot-template = "%F [%p]";
+ stop-screensaver = true;
+ term-osd-bar = true;
+ use-filedir-conf = true;
+
+ osd-bar-align-y = 0;
+ osd-bar-h = 2;
+ osd-bar-w = 60;
+ osd-border-color = "#FF262626";
+ osd-border-size = 2.5;
+ osd-color = "#FFFFFFFF";
+ osd-duration = 2500;
+ osd-font-size = 40;
+ osd-fractions = true;
+ osd-level = 1;
+ osd-shadow-color = "#33000000";
+
+ osc = false;
+
+ sub-auto = "fuzzy";
+ sub-file-paths-append = "srt";
+ sub-ass-force-margins = true;
+ sub-ass-force-style = "kerning=yes";
+ sub-fix-timing = true;
+ sub-use-margins = true;
+ sub-font-size = 40;
+ sub-color = "#FFFFFFFF";
+ sub-border-color = "#FF262626";
+ sub-border-size = 2.5;
+ sub-shadow-offset = 1;
+ sub-shadow-color = "#33000000";
+ sub-spacing = 0.5;
+ blend-subtitles = true;
+
+ audio-file-auto = "fuzzy";
+ volume = 100;
+ volume-max = 200;
+
+ alang = lang;
+ slang = lang;
+
+ ytdl = true;
+ ytdl-raw-options = ''sub-lang="${lang}",write-sub='';
+ ytdl-format = "(bestvideo[height<=?1080][fps<=?60][protocol!=http_dash_segments])+(bestaudio[acodec=opus]/bestaudio)/best";
+ };
+ };
+
+ bash = {
+ shellAliases.cam = "${config.hm.programs.mpv.package}/bin/mpv av://v4l2:/dev/video0";
+
+ initExtra = mkAfter ''
+ _complete_alias cam _mpv mpv
+ '';
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/nextcloud.nix b/modules/nixfiles/nextcloud.nix
new file mode 100644
index 0000000..d63f824
--- /dev/null
+++ b/modules/nixfiles/nextcloud.nix
@@ -0,0 +1,133 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.nextcloud;
+in {
+ options.nixfiles.modules.nextcloud = {
+ enable = mkEnableOption "Whether to enable Nextcloud.";
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; str;
+ default = "nextcloud.${config.networking.domain}";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ nixfiles.modules = {
+ nginx = {
+ enable = true;
+ virtualHosts.${cfg.domain} = {};
+ };
+ postgresql.enable = true;
+ };
+
+ services = let
+ db = "nextcloud";
+ in {
+ nextcloud = mkMerge [
+ {
+ enable = true;
+ package = pkgs.nextcloud23;
+
+ hostName = cfg.domain;
+
+ appstoreEnable = false;
+
+ config = {
+ adminpassFile = null; # This needs to be set as secret.
+
+ dbtype = "pgsql";
+ dbhost = "/run/postgresql";
+ dbuser = db;
+ dbname = db;
+
+ defaultPhoneRegion = "RU";
+ };
+
+ extraApps = let
+ mkNextcloudApp = {
+ name,
+ version,
+ sha256,
+ }:
+ pkgs.fetchNextcloudApp {
+ inherit name version sha256;
+ url = "https://github.com/nextcloud/${name}/archive/refs/tags/v${version}.tar.gz";
+ };
+ in {
+ contacts = mkNextcloudApp {
+ name = "contacts";
+ version = "4.0.1";
+ sha256 = "sha256-dXKsG8KmlUojeY5dUn/XsMD3KaSh4QcZFOGDdcqlSvE=";
+ };
+ calendar = mkNextcloudApp {
+ name = "calendar";
+ version = "3.0.5";
+ sha256 = "sha256-aKUKm7fWJQxOWwma56Tv+GGIo+p0n30Nhoyt4XoxsjI=";
+ };
+ files_rightclick = mkNextcloudApp {
+ name = "files_rightclick";
+ version = "23.0.1";
+ sha256 = "sha256-VYODzkvvGrtpyRoug/8UPKhAgfCx1ltP1JdGPiB/lts=";
+ };
+ unsplash = mkNextcloudApp {
+ name = "unsplash";
+ version = "1.2.4";
+ sha256 = "sha256-KGSkBOrNu0nK0YvAPYaxEL/kZNoJQD1oBV2aUBxh6cI=";
+ };
+ previewgenerator = mkNextcloudApp {
+ name = "previewgenerator";
+ version = "3.4.1";
+ sha256 = "sha256-IUdj0xWt5zHxQoiMv1bYyYTzekuOFrsRIe530QOwC/w=";
+ };
+ bruteforcesettings = mkNextcloudApp {
+ name = "bruteforcesettings";
+ version = "2.3.0";
+ sha256 = "sha256-J7ujmiPaw8GI7vDfVPXEum2XAMWvahciP8C6iXgckdE=";
+ };
+ };
+ }
+ (mkIf config.nixfiles.modules.acme.enable {
+ https = true;
+ config.overwriteProtocol = "https";
+ })
+ ];
+
+ postgresql = {
+ ensureDatabases = [db];
+ ensureUsers = [
+ {
+ name = db;
+ ensurePermissions."DATABASE \"${db}\"" = "ALL PRIVILEGES";
+ }
+ ];
+ };
+ };
+
+ systemd = {
+ services = {
+ nextcloud-setup.after = ["network-online.target" "postgresql.service"];
+
+ nextcloud-preview-generate-cron.serviceConfig = {
+ Type = "oneshot";
+ User = "nextcloud";
+ ExecStart = "${config.services.nextcloud.occ}/bin/nextcloud-occ preview:pre-generate";
+ };
+ };
+
+ timers.nextcloud-preview-generate = {
+ wantedBy = ["timers.target"];
+ timerConfig = {
+ OnBootSec = "15m";
+ OnUnitActiveSec = "15m";
+ Unit = "nextcloud-preview-generate-cron.service";
+ };
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/nginx.nix b/modules/nixfiles/nginx.nix
new file mode 100644
index 0000000..35f5098
--- /dev/null
+++ b/modules/nixfiles/nginx.nix
@@ -0,0 +1,90 @@
+{
+ config,
+ lib,
+ pkgs,
+ this,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.nginx;
+in {
+ options.nixfiles.modules.nginx = {
+ enable = mkEnableOption "Whether to enable Nginx.";
+
+ virtualHosts = mkOption {
+ description = "Attrset of virtual hosts.";
+ # Not sure how to "inherit" the type from the original Nixpkgs option.
+ # Just make sure it's compatible with service.nginx.virtualHosts, ok?
+ type = with types; anything;
+ default = null;
+ };
+ };
+
+ config = mkIf cfg.enable {
+ services = {
+ nginx = {
+ enable = true;
+ enableReload = true;
+
+ package = pkgs.nginxMainline;
+
+ statusPage = true;
+
+ recommendedGzipSettings = true;
+ recommendedOptimisation = true;
+ recommendedProxySettings = true;
+ recommendedTlsSettings = true;
+
+ commonHttpConfig = concatStrings [
+ ''
+ add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive";
+ ''
+ (optionalString (hasAttr "wireguard" this)
+ (with config.nixfiles.modules.wireguard; ''
+ geo $internal {
+ default 0;
+ 127.0.0.1/32 1;
+ ${ipv4.subnet} 1;
+ ${ipv6.subnet} 1;
+ }
+ ''))
+ ];
+
+ virtualHosts =
+ {
+ default = {
+ default = true;
+ rejectSSL = true;
+ locations."/".return = "444";
+ };
+ }
+ // (mkIf (cfg.virtualHosts != null) (mapAttrs (_: attr:
+ mkMerge [
+ attr
+ (mkIf config.nixfiles.modules.acme.enable {
+ enableACME = true;
+ forceSSL = true;
+ })
+ ])
+ cfg.virtualHosts));
+ };
+
+ fail2ban.jails = {
+ nginx-http-auth = ''
+ enabled = true
+ '';
+ nginx-botsearch = ''
+ enabled = true
+ '';
+ };
+
+ prometheus.exporters.nginx = {
+ enable = true;
+ listenAddress = mkDefault this.wireguard.ipv4.address;
+ port = mkDefault 9113;
+ };
+ };
+
+ networking.firewall.allowedTCPPorts = [80 443];
+ };
+}
diff --git a/modules/nixfiles/nmap.nix b/modules/nixfiles/nmap.nix
new file mode 100644
index 0000000..945cd59
--- /dev/null
+++ b/modules/nixfiles/nmap.nix
@@ -0,0 +1,42 @@
+{
+ config,
+ lib,
+ pkgs,
+ inputs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.nmap;
+in {
+ options.nixfiles.modules.nmap.enable =
+ mkEnableOption "Whether to enable Nmap.";
+
+ config = mkIf cfg.enable {
+ hm.home = {
+ file = {
+ ".nmap/scripts/vulners/vulners.nse".source = "${inputs.nmap-vulners}/vulners.nse";
+ ".nmap/scripts/vulscan/vulscan.nse".source = "${inputs.nmap-vulscan}/vulscan.nse";
+ };
+ packages = with pkgs; [nmap nmap-formatter];
+ activation.regenerateNmapScriptDatabase = with pkgs; ''
+ # declare -a vulscandbs=(
+ # "cve"
+ # "exploitdb"
+ # "openvas"
+ # "osvdb"
+ # "scipvuldb"
+ # "securityfocus"
+ # "securitytracker"
+ # "xforce"
+ # )
+ # for i in "''${vulscandbs[@]}"; do
+ # ${curl}/bin/curl \
+ # -o "$HOME/.nmap/scripts/vulscan/$i.csv" \
+ # "https://www.computec.ch/projekte/vulscan/download/$i.csv"
+ # done
+
+ ${nmap}/bin/nmap --script-updatedb
+ '';
+ };
+ };
+}
diff --git a/modules/nixfiles/node-exporter.nix b/modules/nixfiles/node-exporter.nix
new file mode 100644
index 0000000..794c309
--- /dev/null
+++ b/modules/nixfiles/node-exporter.nix
@@ -0,0 +1,35 @@
+{
+ config,
+ lib,
+ this,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.node-exporter;
+in {
+ options.nixfiles.modules.node-exporter.enable =
+ mkEnableOption "Whether to enable Prometheus Node Exporter.";
+
+ config = mkIf cfg.enable {
+ services.prometheus.exporters.node = {
+ enable = true;
+ listenAddress = mkDefault this.wireguard.ipv4.address;
+ port = 9100;
+ enabledCollectors = [
+ "buddyinfo"
+ "ethtool"
+ "interrupts"
+ "ksmd"
+ "lnstat"
+ "logind"
+ "mountstats"
+ "network_route"
+ "processes"
+ "qdisc"
+ "systemd"
+ "tcpstat"
+ "zoneinfo"
+ ];
+ };
+ };
+}
diff --git a/modules/nixfiles/nsd.nix b/modules/nixfiles/nsd.nix
new file mode 100644
index 0000000..f328b5c
--- /dev/null
+++ b/modules/nixfiles/nsd.nix
@@ -0,0 +1,157 @@
+{
+ config,
+ inputs,
+ lib,
+ this,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.nsd;
+in {
+ options.nixfiles.modules.nsd = {
+ enable = mkEnableOption "Whether to enable NSD.";
+
+ fqdn = mkOption {
+ description = "FQDN of this nameserver.";
+ type = with types; str;
+ default = "ns.${config.networking.domain}";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ services = {
+ nsd = {
+ enable = true;
+ interfaces = with this; [ipv4.address ipv6.address];
+ ipTransparent = true;
+ ratelimit.enable = true;
+
+ # TODO DNSSEC.
+ zones = let
+ dns = inputs.dns-nix.lib;
+ in
+ with dns.combinators; let
+ ips = hostname:
+ with my.configurations.${hostname}; {
+ A = [(a ipv4.address)];
+ AAAA = [(aaaa ipv6.address)];
+ };
+
+ # TODO Try moving DKIM keys somewhere in "my" or secrets maybe?
+ mkEmailEntries = {
+ domain ? my.domain.shire,
+ dkimKey ? null,
+ }: {
+ MX = [(mx.mx 10 "${domain}.")];
+ TXT = [(spf.strict ["a" "mx"])];
+ DMARC = [
+ {
+ p = "quarantine";
+ sp = "quarantine";
+ rua = ["mailto:admin+rua@${domain}"];
+ ruf = ["mailto:admin+ruf@${domain}"];
+ }
+ ];
+ DKIM = optional (dkimKey != null) {
+ selector = "mail";
+ p = dkimKey;
+ };
+ };
+
+ mkZone = {
+ domain,
+ sldIps ? (ips "manwe"),
+ extra ? {},
+ }: {
+ ${domain}.data = dns.toString domain ({
+ TTL = 60 * 60;
+
+ SOA = {
+ nameServer = "${cfg.fqdn}.";
+ adminEmail = "admin+dns@${my.domain.shire}";
+ serial = 2022081122;
+ };
+
+ NS = with my.domain; ["ns1.${shire}" "ns2.${shire}"];
+
+ CAA = letsEncrypt "admin+caa@${my.domain.shire}";
+ }
+ // sldIps
+ // extra);
+ };
+ in
+ mkMerge [
+ (mkZone {
+ domain = my.domain.shire;
+ extra =
+ (mkEmailEntries {
+ dkimKey = "[DKIM]";
+ })
+ // {
+ subdomains = rec {
+ manwe = ips "manwe";
+ "*.manwe" = manwe;
+ varda = ips "varda";
+ "*.varda" = varda;
+ yavanna = ips "yavanna";
+ "*.yavanna" = yavanna;
+
+ ns1 = manwe;
+ # ns2 = varda;
+
+ flood = yavanna;
+ gotify = manwe;
+ monitoring = manwe;
+ radicale = varda;
+ rss-bridge = varda;
+ vaultwarden = varda;
+ };
+ };
+ })
+ (mkZone {
+ domain = my.domain.azahi;
+ extra =
+ (mkEmailEntries {
+ dkimKey = "[DKIM]";
+ })
+ // {
+ subdomains = {
+ github.CNAME = ["github.com/${my.username}"];
+ gitlab.CNAME = ["gitlab.com/${my.username}"];
+ };
+ };
+ })
+ (mkZone {
+ domain = my.domain.gondor;
+ extra =
+ (mkEmailEntries {
+ dkimKey = "[DKIM]";
+ })
+ // {
+ subdomains.frodo = ips "manwe";
+ };
+ })
+ (mkZone {
+ domain = my.domain.rohan;
+ extra =
+ (mkEmailEntries {
+ dkimKey = "[DKIM]";
+ })
+ // {
+ subdomains.frodo = ips "manwe";
+ };
+ })
+ ];
+ };
+
+ fail2ban.jails.nsd = ''
+ enabled = true
+ '';
+ };
+
+ networking.firewall = rec {
+ allowedTCPPorts = [53];
+ allowedUDPPorts = allowedTCPPorts;
+ };
+ };
+}
diff --git a/modules/nixfiles/openssh.nix b/modules/nixfiles/openssh.nix
new file mode 100644
index 0000000..2f1559e
--- /dev/null
+++ b/modules/nixfiles/openssh.nix
@@ -0,0 +1,52 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.openssh;
+in {
+ options.nixfiles.modules.openssh = {
+ client.enable = mkEnableOption "Whether to enable OpenSSH client.";
+ server.enable = mkEnableOption "Whether to enable OpenSSH server.";
+ };
+
+ config = mkMerge [
+ (mkIf cfg.client.enable {
+ hm = {
+ home.packages = with pkgs; [mosh sshfs];
+
+ programs.ssh = {
+ enable = true;
+ controlMaster = "auto";
+ controlPersist = "24H";
+ hashKnownHosts = true;
+ serverAliveCountMax = 30;
+ serverAliveInterval = 60;
+ };
+ };
+ })
+ (mkIf cfg.server.enable {
+ programs.mosh.enable = true;
+
+ services = let
+ port = 22022;
+ in {
+ openssh = {
+ enable = true;
+ ports = [port];
+ logLevel = "VERBOSE";
+ permitRootLogin = "no";
+ passwordAuthentication = false;
+ };
+
+ fail2ban.jails.sshd = ''
+ enabled = true
+ mode = aggressive
+ port = ${toString port}
+ '';
+ };
+ })
+ ];
+}
diff --git a/modules/nixfiles/password-store.nix b/modules/nixfiles/password-store.nix
new file mode 100644
index 0000000..19e2f2a
--- /dev/null
+++ b/modules/nixfiles/password-store.nix
@@ -0,0 +1,33 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.password-store;
+in {
+ options.nixfiles.modules.password-store.enable =
+ mkEnableOption "Whether to enable pass.";
+
+ config = mkIf cfg.enable {
+ hm.programs = {
+ password-store = {
+ enable = true;
+
+ package = pkgs.pass.withExtensions (p: with p; [pass-otp]);
+
+ settings.PASSWORD_STORE_DIR = "${config.my.home}/.password-store";
+ };
+
+ # A dirty little hack to make completions for "otp" to work.
+ bash.initExtra = let
+ completions = "${config.hm.programs.password-store.package}/share/bash-completion/completions";
+ in
+ mkAfter ''
+ source ${completions}/pass-otp
+ source ${completions}/pass
+ '';
+ };
+ };
+}
diff --git a/modules/nixfiles/podman.nix b/modules/nixfiles/podman.nix
new file mode 100644
index 0000000..6c8b7e5
--- /dev/null
+++ b/modules/nixfiles/podman.nix
@@ -0,0 +1,35 @@
+{
+ config,
+ lib,
+ inputs,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.podman;
+in {
+ options.nixfiles.modules.podman.enable =
+ mkEnableOption "Whether to enable Podman.";
+
+ config = mkIf cfg.enable {
+ secrets.containers-auth = {
+ file = "${inputs.self}/secrets/containers-auth";
+ path = "${config.dirs.config}/containers/auth.json";
+ owner = my.username;
+ };
+
+ virtualisation.podman.enable = true;
+
+ environment.systemPackages = with pkgs; [podman-compose];
+
+ my.extraGroups = ["podman"];
+
+ hm.programs.bash = {
+ shellAliases.p = "${pkgs.podman}/bin/podman";
+
+ initExtra = mkAfter ''
+ _complete_alias p __start_podman podman
+ '';
+ };
+ };
+}
diff --git a/modules/nixfiles/postgresql.nix b/modules/nixfiles/postgresql.nix
new file mode 100644
index 0000000..23623af
--- /dev/null
+++ b/modules/nixfiles/postgresql.nix
@@ -0,0 +1,53 @@
+{
+ config,
+ lib,
+ pkgs,
+ this,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.postgresql;
+in {
+ options.nixfiles.modules.postgresql.enable =
+ mkEnableOption "Whether to enable PostgeSQL.";
+
+ config = mkIf cfg.enable {
+ hm = {
+ home.sessionVariables.PSQLRC = config.hm.xdg.configFile."psqlrc".target;
+
+ xdg.configFile."psqlrc".text = ''
+ \set QUIET 1
+
+ \timing
+ \x auto
+ \pset null '[NULL]'
+ \set PROMPT1 '%[%033[1m%]%M %n@%/%R%[%033[0m%]% λ '
+ \set PROMPT2 ' … > '
+ \set VERBOSITY verbose
+ \set HISTCONTROL ignoredups
+ \set HISTFILE /dev/null
+
+ \unset QUIET
+ '';
+ };
+
+ services = {
+ postgresql = {
+ enable = true;
+ package = pkgs.postgresql_14;
+ # TODO Test if this is still required.
+ authentication = ''
+ local all all trust
+ host all all 127.0.0.1/32 trust
+ host all all ::1/128 trust
+ '';
+ };
+
+ prometheus.exporters.postgres = {
+ enable = true;
+ listenAddress = mkDefault this.wireguard.ipv4.address;
+ port = mkDefault 9187;
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/profiles/common.nix b/modules/nixfiles/profiles/common.nix
new file mode 100644
index 0000000..dd287dc
--- /dev/null
+++ b/modules/nixfiles/profiles/common.nix
@@ -0,0 +1,103 @@
+{
+ config,
+ lib,
+ pkgs,
+ this,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.profiles.common;
+in {
+ imports = [
+ (mkAliasOptionModule ["colourScheme"] [
+ "nixfiles"
+ "modules"
+ "profiles"
+ "common"
+ "colourScheme"
+ ])
+ ];
+
+ options.nixfiles.modules.profiles.common = {
+ enable = mkEnableOption "The most common profiles of all profiles.";
+
+ colourScheme = let
+ mkColour = default:
+ mkOption {
+ type = types.str;
+ inherit default;
+ description = "Color in a standard hexademical notation.";
+ example = "#000000";
+ };
+ in rec {
+ black = mkColour "#161719";
+ red = mkColour "#cc6666";
+ green = mkColour "#b5bd68";
+ yellow = mkColour "#f0c674";
+ blue = mkColour "#81a2be";
+ magenta = mkColour "#b294bb";
+ cyan = mkColour "#8abeb7";
+ white = mkColour "#c5c8c6";
+
+ brightBlack = mkColour "#969896";
+ brightRed = mkColour "#cc6666";
+ brightGreen = mkColour "#b5bd68";
+ brightYellow = mkColour "#f0c674";
+ brightBlue = mkColour "#81a2be";
+ brightMagenta = mkColour "#b294bb";
+ brightCyan = mkColour "#8abeb7";
+ brightWhite = mkColour "#ffffff";
+
+ background = black;
+ foreground = white;
+ };
+ };
+
+ config = mkIf cfg.enable {
+ assertions = [
+ {
+ assertion = !(with this; isHeadless && isHeadful);
+ message = ''
+ The configuration cannot be both "headful" and "headless" at the same
+ time.
+ '';
+ }
+ ];
+
+ profile = with this; {
+ headless = isHeadless;
+ headful = isHeadful;
+ };
+
+ nixfiles.modules = {
+ htop.enable = true;
+ tmux.enable = true;
+ vim.enable = true;
+ };
+
+ hm.home.language = {
+ collate = "C";
+ messages = "C";
+ };
+
+ programs.less = {
+ enable = true;
+ envVariables.LESSHISTFILE = "-";
+ };
+
+ environment.systemPackages = with pkgs; [
+ cryptsetup
+ ddrescue
+ file
+ git
+ gnupg
+ lshw
+ lsof
+ pciutils
+ psmisc
+ tree
+ usbutils
+ util-linux
+ ];
+ };
+}
diff --git a/modules/nixfiles/profiles/default.nix b/modules/nixfiles/profiles/default.nix
new file mode 100644
index 0000000..3df88f8
--- /dev/null
+++ b/modules/nixfiles/profiles/default.nix
@@ -0,0 +1,5 @@
+{lib, ...}: {
+ imports = [./common.nix ./dev ./headful.nix ./headless.nix];
+
+ config.nixfiles.modules.profiles.common.enable = lib.mkDefault true;
+}
diff --git a/modules/nixfiles/profiles/dev/common.nix b/modules/nixfiles/profiles/dev/common.nix
new file mode 100644
index 0000000..d6c44ea
--- /dev/null
+++ b/modules/nixfiles/profiles/dev/common.nix
@@ -0,0 +1,290 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.profiles.dev.common;
+in {
+ options.nixfiles.modules.profiles.dev.common.enable = mkEnableOption "";
+
+ config = mkIf cfg.enable {
+ nixfiles.modules = {
+ bat.enable = true;
+ curl.enable = true;
+ direnv.enable = true;
+ git.enable = true;
+ gnupg.enable = true;
+ nmap.enable = true;
+ wget.enable = true;
+ };
+
+ hm.home = {
+ file = {
+ ".editorconfig".text = ''
+ root = true
+
+ [*]
+ charset = utf-8
+ end_of_line = lf
+ indent_size = 4
+ indent_style = space
+ insert_final_newline = true
+ max_line_length = 80
+ trim_trailing_whitespace = true
+
+ [*.nix]
+ indent_size = 2
+ indent_style = space
+
+ [*.{S,s,asm}]
+ indent_size = 4
+ indent_style = tab
+
+ [*.{C,H,c,c++,cc,cpp,cxx,h,h++,hh,hpp,hxx}]
+ indent_size = 4
+ indent_style = tab
+
+ [*.{bash,sh}]
+ indent_size = 4
+ indent_style = tab
+
+ [*.{cl,clj,el,l,lisp,lsp,rkt,scm,ss}]
+ indent_size = unset
+ indent_style = unset
+
+ [*.go]
+ indent_size = 4
+ indent_style = tab
+
+ [*.{py,pyx}]
+ indent_size = 4
+ indent_style = space
+
+ [*.{hs,lhs}]
+ indent_size = 2
+ indent_style = space
+
+ [*.{html,xhtml,xml}]
+ indent_size = 4
+ indent_style = tab
+
+ [*.json]
+ indent_size = 2
+ indent_style = space
+
+ [*.{yaml,yml}]
+ indent_size = 2
+ indent_style = space
+
+ [*.{toml,tml}]
+ indent_size = 4
+ indent_style = space
+
+ [*.{py,pyx}]
+ indent_size = 4
+ indent_style = space
+ max_line_length = 72
+
+ [*.zig]
+ indent_size = 4
+ indent_style = tab
+
+ [configure.ac]
+ indent_size = 4
+ indent_style = tab
+
+ [{Makefile*,*.mk}]
+ indent_size = 4
+ indent_style = tab
+
+ [{CMakeLists.txt,*.cmake}]
+ indent_size = 8
+ indent_style = tab
+
+ [*.tex]
+ indent_size = 4
+ indent_style = tab
+
+ [*.{md,adoc,rtf,txt}]
+ indent_size = 4
+ indent_style = tab
+ '';
+
+ ".ghc/ghci.conf".source = ./ghci.conf;
+
+ ".stack/config.yaml".text = generators.toYAML {} {
+ templates.params = rec {
+ author-name = my.fullname;
+ author-email = my.email;
+ copyright = "Copyright (c) ${author-name} <${author-email}>";
+ github-username = my.username;
+ };
+ };
+
+ ".clang-format".text = generators.toYAML {} {
+ AccessModifierOffset = -4;
+ AlignAfterOpenBracket = "Align";
+ AlignConsecutiveAssignments = "Consecutive";
+ AlignConsecutiveBitFields = "Consecutive";
+ AlignConsecutiveDeclarations = "Consecutive";
+ AlignConsecutiveMacros = "Consecutive";
+ AlignEscapedNewlines = "Right";
+ AlignOperands = "Align";
+ AlignTrailingComments = false;
+ AllowAllArgumentsOnNextLine = false;
+ AllowAllConstructorInitializersOnNextLine = true;
+ AllowAllParametersOfDeclarationOnNextLine = true;
+ AllowShortBlocksOnASingleLine = "Never";
+ AllowShortCaseLabelsOnASingleLine = false;
+ AllowShortEnumsOnASingleLine = false;
+ AllowShortFunctionsOnASingleLine = "None";
+ AllowShortIfStatementsOnASingleLine = "Never";
+ AllowShortLambdasOnASingleLine = "Inline";
+ AllowShortLoopsOnASingleLine = false;
+ AlwaysBreakAfterDefinitionReturnType = "All";
+ AlwaysBreakAfterReturnType = "AllDefinitions";
+ AlwaysBreakBeforeMultilineStrings = false;
+ AlwaysBreakTemplateDeclarations = "Yes";
+ BinPackArguments = false;
+ BinPackParameters = false;
+ BreakBeforeBinaryOperators = "None";
+ BreakBeforeBraces = "Allman";
+ BreakBeforeTernaryOperators = true;
+ BreakConstructorInitializers = "BeforeComma";
+ BreakInheritanceList = "BeforeComma";
+ BreakStringLiterals = true;
+ ColumnLimit = 80;
+ CommentPragmas = "^ IWYU pragma:";
+ CompactNamespaces = false;
+ ConstructorInitializerAllOnOneLineOrOnePerLine = false;
+ ConstructorInitializerIndentWidth = 4;
+ ContinuationIndentWidth = 4;
+ Cpp11BracedListStyle = true;
+ DeriveLineEnding = false;
+ DerivePointerAlignment = false;
+ DisableFormat = false;
+ ExperimentalAutoDetectBinPacking = false;
+ FixNamespaceComments = true;
+ IncludeBlocks = "Regroup";
+ IndentCaseBlocks = false;
+ IndentCaseLabels = false;
+ IndentExternBlock = "NoIndent";
+ IndentGotoLabels = false;
+ IndentPPDirectives = "None";
+ IndentWidth = 4;
+ IndentWrappedFunctionNames = false;
+ KeepEmptyLinesAtTheStartOfBlocks = false;
+ Language = "Cpp";
+ MaxEmptyLinesToKeep = 1;
+ NamespaceIndentation = "None";
+ PointerAlignment = "Left";
+ ReflowComments = false;
+ SortIncludes = "CaseSensitive";
+ SortUsingDeclarations = true;
+ SpaceAfterCStyleCast = false;
+ SpaceAfterLogicalNot = false;
+ SpaceAfterTemplateKeyword = true;
+ SpaceBeforeAssignmentOperators = true;
+ SpaceBeforeCpp11BracedList = false;
+ SpaceBeforeCtorInitializerColon = true;
+ SpaceBeforeInheritanceColon = true;
+ SpaceBeforeParens = "ControlStatements";
+ SpaceBeforeRangeBasedForLoopColon = true;
+ SpaceInEmptyParentheses = false;
+ SpacesBeforeTrailingComments = 1;
+ SpacesInAngles = false;
+ SpacesInCStyleCastParentheses = false;
+ SpacesInContainerLiterals = false;
+ SpacesInParentheses = false;
+ SpacesInSquareBrackets = false;
+ Standard = "Latest";
+ TabWidth = 4;
+ UseTab = "Always";
+ };
+
+ ".gdbinit".text = ''
+ set confirm off
+ set verbose off
+ set editing off
+
+ set history expansion on
+
+ set height 0
+ set width 0
+
+ handle SIGALRM nostop print nopass
+ handle SIGBUS stop print nopass
+ handle SIGPIPE nostop print nopass
+ handle SIGSEGV stop print nopass
+
+ set print address on
+ set print elements 0
+ set print object on
+ set print pretty on
+ set print repeats 0
+ set print static-members on
+ set print vtbl on
+
+ set output-radix 10
+
+ set demangle-style gnu-v3
+
+ set disassembly-flavor intel
+
+ alias iv=info variables
+
+ alias da=disassemble
+
+ define fs
+ finish
+ step
+ end
+
+ define btc
+ backtrace
+ continue
+ end
+ '';
+ };
+
+ sessionVariables = {
+ CARGO_HOME = "${config.dirs.data}/cargo";
+ GOPATH = "${config.dirs.data}/go";
+ PYTHONSTARTUP = ./pystartup.py;
+ };
+
+ # TODO Probably should scrap most of these in favor of per-project
+ # shell.nix or flake.nix.
+ packages = with pkgs; [
+ alejandra
+ bash-language-server
+ cloc
+ dockerfile-language-server
+ editorconfig
+ fd
+ fzf
+ htmlq
+ jc
+ jq
+ json-language-server
+ logcli
+ nix-language-server
+ nixfmt
+ nixpkgs-fmt
+ ripgrep
+ ripgrep-all
+ shellcheck
+ shfmt
+ treefmt
+ wrk
+ yaml-language-server
+ yamllint
+ yq
+ ];
+ };
+
+ my.extraGroups = ["kvm"];
+ };
+}
diff --git a/modules/nixfiles/profiles/dev/containers/default.nix b/modules/nixfiles/profiles/dev/containers/default.nix
new file mode 100644
index 0000000..d0e7ed7
--- /dev/null
+++ b/modules/nixfiles/profiles/dev/containers/default.nix
@@ -0,0 +1,73 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.profiles.dev.containers;
+in {
+ options.nixfiles.modules.profiles.dev.containers.enable =
+ mkEnableOption "Wether to enable tools for working with Kubernetes.";
+
+ config = mkIf cfg.enable {
+ nixfiles.modules = {
+ profiles.dev.common.enable = true;
+ podman.enable = true;
+ };
+
+ hm = {
+ home = {
+ sessionVariables = {
+ MINIKUBE_IN_STYLE = "false";
+ WERF_DEV = "true";
+ WERF_INSECURE_REGISTRY = "true";
+ WERF_LOG_DEBUG = "true";
+ WERF_LOG_PRETTY = "false";
+ WERF_LOG_VERBOSE = "true";
+ WERF_SYNCHRONIZATION = ":local";
+ };
+
+ file.".minikube/config/config.json".text = generators.toJSON {} {
+ config.Rootless = true;
+ driver = "podman";
+ container-runtime = "cri-o";
+ };
+
+ packages = with pkgs; [
+ buildah
+ chart-testing
+ cmctl
+ helm
+ kubectl
+ kubectx
+ kubescape
+ kubespy
+ minikube
+ skaffold
+ skopeo
+ stern
+ telepresence
+ werf
+ ];
+ };
+
+ programs.bash = {
+ shellAliases = with pkgs; {
+ b = "${buildah}/bin/buildah";
+ h = "${helm}/bin/helm";
+ k = "${kubectl}/bin/kubectl";
+ kns = "${kubectx}/bin/kubens";
+ ktx = "${kubectx}/bin/kubectx";
+ };
+ initExtra = mkAfter ''
+ _complete_alias b _buildah buildah
+ _complete_alias h __start_helm helm
+ _complete_alias k __start_kubectl kubectl
+ _complete_alias kns _kube_namespaces kubens
+ _complete_alias ktx _kube_contexts kubectx
+ '';
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/profiles/dev/default.nix b/modules/nixfiles/profiles/dev/default.nix
new file mode 100644
index 0000000..1e0bd01
--- /dev/null
+++ b/modules/nixfiles/profiles/dev/default.nix
@@ -0,0 +1 @@
+_: {imports = [./common.nix ./containers ./sql];}
diff --git a/modules/nixfiles/profiles/dev/ghci.conf b/modules/nixfiles/profiles/dev/ghci.conf
new file mode 100644
index 0000000..d672167
--- /dev/null
+++ b/modules/nixfiles/profiles/dev/ghci.conf
@@ -0,0 +1,35 @@
+:set -XBinaryLiterals
+:set -XFlexibleContexts
+:set -XNoMonomorphismRestriction
+
+:seti -XConstraintKinds
+:seti -XDataKinds
+:seti -XDeriveFunctor
+:seti -XFlexibleInstances
+:seti -XFunctionalDependencies
+:seti -XGADTs
+:seti -XLambdaCase
+:seti -XMagicHash
+:seti -XMultiParamTypeClasses
+:seti -XMultiWayIf
+:seti -XOverloadedLabels
+:seti -XPackageImports
+:seti -XPolyKinds
+:seti -XRankNTypes
+:seti -XScopedTypeVariables
+:seti -XStandaloneDeriving
+:seti -XTupleSections
+:seti -XTypeFamilies
+:seti -XTypeOperators
+:seti -XUndecidableInstances
+
+:set +c
+:set +m
+:set +r
+:set +s
+:set +t
+
+:set prompt "\ESC[1;34m>\ESC[m\STX "
+:set prompt-cont "\ESC[1;94m|\ESC[m\STX "
+
+:def hoogle \x -> pure (":!hoogle --color --count=10 \"" ++ x ++ "\"")
diff --git a/modules/nixfiles/profiles/dev/pystartup.py b/modules/nixfiles/profiles/dev/pystartup.py
new file mode 100644
index 0000000..1a78b55
--- /dev/null
+++ b/modules/nixfiles/profiles/dev/pystartup.py
@@ -0,0 +1,127 @@
+import atexit
+import os
+import readline
+import rlcompleter
+import sys
+from code import InteractiveConsole
+from tempfile import mkstemp
+
+readline.parse_and_bind("tab: complete")
+
+
+class TermColors(dict):
+ color_templates = (
+ ("Normal", "0"),
+ ("Black", "0;30"),
+ ("Red", "0;31"),
+ ("Green", "0;32"),
+ ("Brown", "0;33"),
+ ("Blue", "0;34"),
+ ("Purple", "0;35"),
+ ("Cyan", "0;36"),
+ ("LightGray", "0;37"),
+ ("DarkGray", "1;30"),
+ ("LightRed", "1;31"),
+ ("LightGreen", "1;32"),
+ ("Yellow", "1;33"),
+ ("LightBlue", "1;34"),
+ ("LightPurple", "1;35"),
+ ("LightCyan", "1;36"),
+ ("White", "1;37"),
+ )
+ color_base = "\001\033[%sm\002"
+
+ def __init__(self):
+ self.update(
+ dict([(k, self.color_base % v) for k, v in self.color_templates])
+ )
+
+
+class Completer(object):
+ def save_history(self):
+ import readline
+
+ readline.write_history_file(self.python_histfile)
+
+ def __init__(self):
+ self.python_dir = os.path.expanduser(
+ "%s/python" % os.environ["XDG_DATA_HOME"]
+ )
+
+ if not os.path.exists(self.python_dir):
+ os.mkdir(self.python_dir)
+
+ self.python_histfile = os.path.expanduser(
+ "%s/history" % self.python_dir
+ )
+
+ if os.path.exists(self.python_histfile):
+ readline.read_history_file(self.python_histfile)
+
+ readline.set_history_length(1000)
+ atexit.register(self.save_history)
+
+
+def DisplayHook(value):
+ if value is not None:
+ try:
+ import __builtin__
+
+ __builtin__._ = value
+ except ImportError:
+ __builtins__._ = value
+
+ import pprint
+
+ pprint.pprint(value)
+ del pprint
+
+
+class EditableBufferInteractiveConsole(InteractiveConsole):
+ def __init__(self, *args, **kwargs):
+ self.last_buffer = []
+ InteractiveConsole.__init__(self, *args, **kwargs)
+
+ def runsource(self, source, *args):
+ self.last_buffer = [source.encode("utf-8")]
+ return InteractiveConsole.runsource(self, source, *args)
+
+ def raw_input(self, *args):
+ line = InteractiveConsole.raw_input(self, *args)
+
+ if line == EDIT_CMD:
+ tmp_fd, tmp_file = mkstemp(".py")
+
+ os.write(tmp_fd, b"\n".join(self.last_buffer))
+ os.close(tmp_fd)
+
+ os.system("%s %s" % (EDITOR, tmp_file))
+
+ line = open(tmp_file).read()
+
+ os.unlink(tmp_file)
+ tmp_file = ""
+
+ lines = line.split("\n")
+
+ for i in range(len(lines) - 1):
+ self.push(lines[i])
+
+ line = lines[-1]
+ return line
+
+
+TC = TermColors()
+ps1 = "%sλ%s %s>%s "
+sys.ps1 = ps1 % (TC["Blue"], TC["Normal"], TC["White"], TC["Normal"])
+ps2 = " %s…%s %s>%s "
+sys.ps2 = ps2 % (TC["Blue"], TC["Normal"], TC["White"], TC["Normal"])
+sys.displayhook = DisplayHook
+
+C = Completer()
+EDITOR = os.environ.get("EDITOR", "vim")
+EDIT_CMD = ":e"
+C = EditableBufferInteractiveConsole(locals=locals())
+C.interact(banner="")
+
+sys.exit()
diff --git a/modules/nixfiles/profiles/dev/sql/default.nix b/modules/nixfiles/profiles/dev/sql/default.nix
new file mode 100644
index 0000000..deb3738
--- /dev/null
+++ b/modules/nixfiles/profiles/dev/sql/default.nix
@@ -0,0 +1,94 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.profiles.dev.sql;
+in {
+ options.nixfiles.modules.profiles.dev.sql.enable =
+ mkEnableOption "Whether to enable SQL database management tools.";
+
+ config = mkIf cfg.enable {
+ hm = {
+ home.packages = with pkgs; [pgcli litecli];
+
+ xdg = let
+ mainSection = {
+ destructive_warning = "True";
+ enable_pager = "True";
+ keyword_casing = "auto";
+ less_chatty = "True";
+ log_file = "/dev/null";
+ log_level = "CRITICAL";
+ multi_line = "False";
+ syntax_style = "default";
+ table_format = "fancy_grid";
+ };
+
+ colorsSection = with config.nixfiles.modules.profiles.common.colourScheme; {
+ "arg-toolbar" = "noinherit bold";
+ "arg-toolbar.text" = "nobold";
+ "bottom-toolbar" = "bg:${black} ${white}";
+ "bottom-toolbar.off" = "bg:${black} ${brightBlack}";
+ "bottom-toolbar.on" = "bg:${black} ${brightWhite}";
+ "bottom-toolbar.transaction.failed" = "bg:${black} ${red} bold";
+ "bottom-toolbar.transaction.valid" = "bg:${black} ${green} bold";
+ "completion-menu.completion" = "bg:${black} ${white}";
+ "completion-menu.completion.current" = "bg:${white} ${black}";
+ "completion-menu.meta.completion" = "bg:${black} ${yellow}";
+ "completion-menu.meta.completion.current" = "bg:${yellow} ${black}";
+ "completion-menu.multi-column-meta" = "bg:${yellow} ${black}";
+ "scrollbar" = "bg:${black}";
+ "scrollbar.arrow" = "bg:${black}";
+ "search" = "bg:${magenta} ${brightWhite}";
+ "search-toolbar" = "noinherit bold";
+ "search-toolbar.text" = "nobold";
+ "search.current" = "bg:${green} ${brightWhite}";
+ "selected" = "bg:${blue} ${brightWhite}";
+ "system-toolbar" = "noinherit bold";
+ };
+
+ mkCliConfig = {
+ name,
+ custom,
+ }: {
+ "${name}/config" = {
+ text = generators.toINI {} {
+ main = mainSection // custom;
+ colors = mapAttrs (_: v: "'${v}'") colorsSection;
+ };
+ };
+ };
+ in {
+ configFile = mkMerge (map mkCliConfig [
+ {
+ name = "pgcli";
+ custom = {
+ auto_expand = "True";
+ casing_file = "/dev/null";
+ expand = "True";
+ history_file = "/dev/null";
+ keyring = "False";
+ multi_line_mode = "psql";
+ on_error = "STOP";
+ prompt = "'\\u@\\h:\\d> '";
+ vi = "True";
+ };
+ }
+ {
+ name = "litecli";
+ custom = {
+ audit_log = "/dev/null";
+ key_bindings = "vi";
+ prompt = "'\\d> '";
+ prompt_continuation = "'-> '";
+ auto_vertical_output = "True";
+ };
+ }
+ ]);
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/profiles/headful.nix b/modules/nixfiles/profiles/headful.nix
new file mode 100644
index 0000000..fa2f0d7
--- /dev/null
+++ b/modules/nixfiles/profiles/headful.nix
@@ -0,0 +1,69 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.profiles.headful;
+in {
+ imports = [
+ (mkAliasOptionModule ["profile" "headful"] [
+ "nixfiles"
+ "modules"
+ "profiles"
+ "headful"
+ "enable"
+ ])
+ ];
+
+ options.nixfiles.modules.profiles.headful.enable =
+ mkEnableOption "Wether to enable headful profile.";
+
+ config = mkIf cfg.enable {
+ nixfiles.modules = {
+ alacritty.enable = true;
+ aria2.enable = true;
+ aspell.enable = true;
+ emacs.enable = true;
+ firefox.enable = true;
+ mpv.enable = true;
+ openssh.client.enable = true;
+ password-store.enable = true;
+ sound.enable = true;
+ x11.enable = true;
+
+ dwm.enable = mkDefault false;
+ kde.enable = mkDefault true;
+ xmonad.enable = mkDefault false;
+ };
+
+ hm.home.packages = with pkgs; [convmv dos2unix];
+
+ hardware.opengl = {
+ enable = true;
+ driSupport = true;
+ };
+
+ programs = {
+ iftop.enable = true;
+ mtr.enable = true;
+ traceroute.enable = true;
+
+ bash.shellAliases.open = "${pkgs.xdg-utils}/bin/xdg-open";
+ };
+
+ services.upower.enable = true;
+
+ environment.systemPackages = with pkgs; [
+ arping
+ ethtool
+ inetutils
+ nethogs
+ socat
+ tcpdump
+ ];
+
+ my.extraGroups = ["audio" "video" "input"];
+ };
+}
diff --git a/modules/nixfiles/profiles/headless.nix b/modules/nixfiles/profiles/headless.nix
new file mode 100644
index 0000000..9737344
--- /dev/null
+++ b/modules/nixfiles/profiles/headless.nix
@@ -0,0 +1,67 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.profiles.headless;
+in {
+ imports = [
+ (mkAliasOptionModule ["profile" "headless"] [
+ "nixfiles"
+ "modules"
+ "profiles"
+ "headless"
+ "enable"
+ ])
+ ];
+
+ options.nixfiles.modules.profiles.headless.enable =
+ mkEnableOption "Whether to enable headless profile.";
+
+ config = mkIf cfg.enable {
+ nixfiles.modules = {
+ openssh.server.enable = true;
+ endlessh-go.enable = true;
+
+ fail2ban.enable = true;
+
+ node-exporter.enable = true;
+ promtail.enable = true;
+ };
+
+ hm.home.file = {
+ ".hushlogin".text = "";
+ ".bash_history".source =
+ config.hm.lib.file.mkOutOfStoreSymlink "/dev/null";
+ };
+
+ boot.kernelPackages = pkgs.linuxPackages_5_15_hardened;
+
+ nix = {
+ gc = {
+ automatic = true;
+ dates = "weekly";
+ options = "--delete-older-than 30d";
+ };
+
+ optimise = {
+ automatic = true;
+ dates = ["weekly"];
+ };
+ };
+
+ i18n = {
+ # TODO Convert everything including PostgreSQL databases to the "en_US"
+ # locale or probably even the "C" one.
+ defaultLocale = mkForce "C";
+ supportedLocales = mkForce ["en_US.UTF-8/UTF-8" "en_GB.UTF-8/UTF-8"];
+ };
+ security.polkit.enable = false;
+ services.udisks2.enable = false;
+ xdg.sounds.enable = false;
+
+ environment.systemPackages = with pkgs; [alacritty.terminfo];
+ };
+}
diff --git a/modules/nixfiles/prometheus.nix b/modules/nixfiles/prometheus.nix
new file mode 100644
index 0000000..b67dd2e
--- /dev/null
+++ b/modules/nixfiles/prometheus.nix
@@ -0,0 +1,60 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.prometheus;
+in {
+ options.nixfiles.modules.prometheus = {
+ enable = mkEnableOption "Whether to enable Prometheus.";
+
+ port = mkOption {
+ description = "Port.";
+ type = with types; port;
+ default = 30111;
+ };
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; str;
+ default = config.nixfiles.modules.monitoring.domain;
+ };
+
+ path = mkOption {
+ description = "Path.";
+ type = with types; str;
+ default = "/prometheus";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ nixfiles.modules.nginx = with cfg; {
+ enable = true;
+ virtualHosts.${domain}.locations.${path} = {
+ proxyPass = with cfg; "http://127.0.0.1:${toString port}";
+ extraConfig = ''
+ if ($internal != 1) {
+ return 403;
+ }
+ '';
+ };
+ };
+
+ services.prometheus = with cfg; {
+ enable = true;
+
+ listenAddress = "127.0.0.1";
+ inherit port;
+
+ extraFlags = [
+ "--web.external-url=http${
+ optionalString config.nixfiles.modules.acme.enable "s"
+ }://${domain}${path}"
+ "--storage.tsdb.retention.size=50GB"
+ "--storage.tsdb.retention.time=1y"
+ "--storage.tsdb.wal-compression"
+ ];
+ };
+ };
+}
diff --git a/modules/nixfiles/promtail.nix b/modules/nixfiles/promtail.nix
new file mode 100644
index 0000000..ba4e635
--- /dev/null
+++ b/modules/nixfiles/promtail.nix
@@ -0,0 +1,53 @@
+{
+ config,
+ lib,
+ this,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.promtail;
+in {
+ options.nixfiles.modules.promtail = {
+ enable = mkEnableOption "Whether to enable Promtail.";
+
+ loki = {
+ url = mkOption {
+ description = "Address of a listening Loki service.";
+ type = with types; str;
+ default = with config.nixfiles.modules.loki; "https://${domain}${path}";
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ services.promtail = {
+ enable = true;
+
+ configuration = {
+ server = rec {
+ http_listen_address = this.wireguard.ipv4.address;
+ http_listen_port = 30181;
+
+ grpc_listen_address = this.wireguard.ipv4.address;
+ grpc_listen_port = http_listen_port + 1;
+
+ log_level = "warn";
+ };
+
+ clients = [{url = "${cfg.loki.url}/loki/api/v1/push";}];
+
+ positions.filename = "/tmp/positions.yaml";
+
+ scrape_configs = [
+ {
+ job_name = "journal";
+ journal = {
+ max_age = "24h";
+ labels.job = "systemd-journal";
+ };
+ }
+ ];
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/psd.nix b/modules/nixfiles/psd.nix
new file mode 100644
index 0000000..290c067
--- /dev/null
+++ b/modules/nixfiles/psd.nix
@@ -0,0 +1,60 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.psd;
+in {
+ options.nixfiles.modules.psd.enable =
+ mkEnableOption "Whether to enable Profile Sync Daemon.";
+
+ config = mkIf cfg.enable {
+ hm.home = {
+ file."${config.hm.xdg.configHome}/psd/psd.conf".text = ''
+ USE_OVERLAYFS="yes"
+ '';
+
+ packages = with pkgs; [profile-sync-daemon];
+ };
+
+ systemd.user = {
+ services = {
+ psd = {
+ unitConfig = {
+ Description = "Profile-sync-daemon";
+ Wants = ["psd-resync.service"];
+ RequiresMountsFor = "/home/";
+ After = ["local-fs.target"];
+ };
+ serviceConfig = {
+ RemainAfterExit = true;
+ ExecStart = "${pkgs.profile-sync-daemon}/bin/profile-sync-daemon startup";
+ ExecStop = "${pkgs.profile-sync-daemon}/bin/profile-sync-daemon unsync";
+ };
+ wantedBy = ["graphical.target"];
+ };
+
+ psd-resync = {
+ unitConfig = {
+ Description = "Profile-sync-daemon resync";
+ After = ["psd.service"];
+ Wants = ["psd-resync.timer"];
+ BindsTo = ["psd.service"];
+ };
+ serviceConfig.ExecStart = "${pkgs.profile-sync-daemon}/bin/profile-sync-daemon resync";
+ wantedBy = ["graphical.target"];
+ };
+ };
+
+ timers.psd-resync = {
+ unitConfig = {
+ Description = "Profile-sync-daemon resync timer";
+ BindsTo = ["psd.service"];
+ };
+ timerConfig.OnUnitActiveSec = "1h";
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/qutebrowser.nix b/modules/nixfiles/qutebrowser.nix
new file mode 100644
index 0000000..5af7e1f
--- /dev/null
+++ b/modules/nixfiles/qutebrowser.nix
@@ -0,0 +1,547 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.qutebrowser;
+in {
+ options.nixfiles.modules.qutebrowser.enable =
+ mkEnableOption "Whether to enable qutebrowser.";
+
+ config = mkIf cfg.enable {
+ hm = {
+ # home.sessionVariables.BROWSER = mkOverride 400 "qutebrowser";
+
+ programs.qutebrowser = with config.nixfiles.modules; {
+ enable = true;
+
+ package = pkgs.qutebrowser.override {
+ withMediaPlayback = false;
+ withPdfReader = false;
+ };
+
+ keyBindings.normal = mkIf mpv.enable {
+ "z" = let
+ mpv = "${config.hm.programs.mpv.package}/bin/mpv";
+ in "hint links spawn --detach ${mpv} {hint-url}";
+ };
+
+ searchEngines = rec {
+ aliexpress = "https://www.aliexpress.com/wholesale?SearchText={}";
+ ansible = "https://galaxy.ansible.com/search?keywords={}";
+ arch = "https://wiki.archlinux.org/?search={}";
+ crates = "https://crates.io/search?q={}";
+ crawl = "http://crawl.chaosforge.org/index.php?search={}";
+ discogs = "https://www.discogs.com/search/?q={}";
+ dockerdocs = "https://docs.docker.com/search/?q={}";
+ dockerhub = "https://hub.docker.com/search?q={}";
+ doublegis = "https://2gis.ru/search/{}";
+ duckduckgo = "https://duckduckgo.com/?q={}'";
+ dwarffortress = "https://dwarffortresswiki.org/index.php?search={}";
+ ebay = "https://www.ebay.com/sch/i.html?_nkw={}";
+ ecosia = "https://www.ecosia.org/search?q={}";
+ factorio = "https://wiki.factorio.com/index.php?search={}";
+ genius = "https://genius.com/search?q={}";
+ github = "https://github.com/search?q={}";
+ godocs = "https://godocs.io/?q={}";
+ gogdb = "https://www.gogdb.org/products?search={}";
+ google = "https://www.google.com/search?q={}";
+ google-images = "https://www.google.com/search?q={}&tbm=isch";
+ gopkgs = "https://pkg.go.dev/search?q={}";
+ habr = "https://habr.com/ru/search/?q={}";
+ hackage = "https://hackage.haskell.org/packages/search?terms={}";
+ hackernews = "https://hn.algolia.com/?q={}";
+ headhunter = "https://hh.ru/search/vacancy?st=searchVacancy&text={}";
+ hoogle = "https://hoogle.haskell.org/?hoogle={}";
+ jisho = "https://jisho.org/search/{}";
+ kotobank = "https://kotobank.jp/gs/?q={}";
+ kubernetes = "https://kubernetes.io/search/?q={}";
+ lastfm = "https://www.last.fm/search?q={}";
+ lobsters = "https://lobste.rs/search?q=test{}";
+ mdn = "https://developer.mozilla.org/en-US/search?q={}";
+ melpa = "https://melpa.org/#/?q={}";
+ moddb = "https://www.moddb.com/search?q={}";
+ musicbrainz = "https://musicbrainz.org/search?query={}";
+ nix-issues = "https://github.com/NixOS/nix/issues?q={}";
+ nix-prs = "https://github.com/NixOS/nix/pulls?q={}";
+ nixos-flakes = "https://search.nixos.org/flakes?query={}";
+ nixos-options = "https://search.nixos.org/options?query={}";
+ nixos-packages = "https://search.nixos.org/packages?query={}";
+ nixos-wiki = "https://nixos.wiki/index.php?search={}";
+ nixpkgs-issues = "https://github.com/NixOS/nixpkgs/issues?q={}";
+ nixpkgs-prs = "https://github.com/NixOS/nixpkgs/pulls?q={}";
+ openstreetmap = "https://www.openstreetmap.org/search?query={}";
+ ozon = "https://www.ozon.ru/search/?text={}";
+ protondb = "https://www.protondb.com/search?q={}";
+ pypi = "https://pypi.org/search/?q={}";
+ pythondocs = "https://docs.python.org/3/search.html?q={}";
+ rateyourmusic = "https://rateyourmusic.com/search?searchterm={}";
+ riichi = "https://riichi.wiki/index.php?search={}";
+ rustdoc = "https://doc.rust-lang.org/std/?search={}";
+ searx = "https://searx.tiekoetter.com/search?q={}";
+ slashdot = "https://slashdot.org/index2.pl?fhfilter={}";
+ sourcehut = "https://sr.ht/projects?search={}";
+ steam = "https://store.steampowered.com/search/?term={}";
+ steamdb = "https://steamdb.info/search/?a=app&q={}";
+ ubuntu = "https://wiki.ubuntu.com/Home?action=fullsearch&value={}";
+ wikipedia-en = "https://en.wikipedia.org/w/index.php?search={}";
+ wikipedia-ru = "https://ru.wikipedia.org/w/index.php?search={}";
+ wikipedia-ja = "https://ja.wikipedia.org/w/index.php?search={}";
+ wolphramalpha = "https://www.wolframalpha.com/input/?i={}";
+ yahoo = "https://yahoo.com/search/?text={}";
+ yahoo-images = "https://yahoo.com/images/search?text={}";
+ yahoo-market = "https://market.yahoo.com/search?text={}";
+ youtube = "https://yewtu.be/search?q={}";
+
+ aw = arch;
+ d = duckduckgo;
+ do = dockerhub;
+ docker = dockerhub;
+ dod = dockerdocs;
+ g = google;
+ gh = github;
+ h = hoogle;
+ k = kubernetes;
+ mb = musicbrainz;
+ n = nixos-options;
+ nw = nixos-wiki;
+ py = pypi;
+ pyd = pythondocs;
+ rym = rateyourmusic;
+ s = searx;
+ sh = sourcehut;
+ sr = sourcehut;
+ w = wikipedia-en;
+ wen = wikipedia-en;
+ wja = wikipedia-ja;
+ wru = wikipedia-ru;
+ y = yahoo;
+ yt = youtube;
+ };
+
+ settings = {
+ changelog_after_upgrade = "never";
+
+ content = {
+ autoplay = false;
+ cookies.accept = "all";
+ default_encoding = "utf-8";
+ desktop_capture = "ask";
+ dns_prefetch = false;
+ geolocation = false;
+ headers.do_not_track = true;
+ javascript.enabled = true;
+ prefers_reduced_motion = true;
+ webgl = true;
+
+ blocking = {
+ enabled = true;
+ method = "adblock";
+ adblock.lists = [
+ "https://easylist.to/easylist/easylist.txt"
+ "https://easylist.to/easylist/easyprivacy.txt"
+ "https://easylist.to/easylist/fanboy-social.txt"
+ "https://secure.fanboy.co.nz/fanboy-annoyance.txt"
+ "https://secure.fanboy.co.nz/fanboy-cookiemonster.txt"
+ ];
+ };
+ };
+
+ completion = {
+ height = "50%";
+ show = "auto";
+ shrink = true;
+ timestamp_format = "%y-%m-%d";
+ min_chars = 3;
+ open_categories = ["bookmarks" "quickmarks" "history"];
+
+ scrollbar = {
+ width = 0;
+ padding = 0;
+ };
+ };
+
+ downloads = {
+ location = {
+ directory = config.userDirs.download;
+ prompt = true;
+ };
+ remove_finished = 0;
+ };
+
+ editor.command = [
+ (
+ if alacritty.enable
+ then "${pkgs.alacritty}/bin/alacritty"
+ else "${pkgs.xterm}/bin/xterm"
+ )
+ "-e"
+ "${config.programs.vim.package}/bin/vim"
+ "-f"
+ "{}"
+ ];
+
+ hints = {
+ auto_follow = "unique-match";
+ auto_follow_timeout = 0;
+ border = "0px";
+ min_chars = 1;
+ scatter = false;
+ uppercase = false;
+ };
+
+ hints.radius = 0;
+ keyhint.radius = 0;
+ prompt.radius = 0;
+
+ scrolling = {
+ bar = "never";
+ smooth = false;
+ };
+
+ spellcheck.languages = ["en-GB" "en-US" "ru-RU"];
+
+ statusbar.position = "bottom";
+
+ tabs = {
+ position = "top";
+
+ title = {
+ alignment = "left";
+ format = "{audio}{index} : {current_title}";
+ format_pinned = "{audio}{index}";
+ };
+
+ min_width = -1;
+ max_width = -1;
+
+ indicator.width = 0;
+
+ pinned = {
+ shrink = true;
+ frozen = false;
+ };
+
+ close_mouse_button = "middle";
+ mousewheel_switching = false;
+
+ background = true;
+ select_on_remove = "next";
+ new_position = {
+ related = "next";
+ unrelated = "last";
+ };
+
+ favicons = {
+ show = "pinned";
+ scale = 0.75;
+ };
+ };
+
+ url = rec {
+ default_page = "about:blank";
+ start_pages = [default_page];
+ };
+
+ window = {
+ hide_decoration = false; # TODO Test in a WM.
+ title_format = "{perc}{current_title}{title_sep}qutebrowser";
+ };
+
+ colors = with config.colourScheme; {
+ completion = rec {
+ fg = white;
+ match.fg = red;
+ odd.bg = black;
+ even.bg = odd.bg;
+ category = {
+ fg = white;
+ bg = black;
+ border = {
+ top = black;
+ bottom = black;
+ };
+ };
+ item.selected = {
+ fg = black;
+ bg = white;
+ border = {
+ top = white;
+ bottom = white;
+ };
+ };
+ scrollbar = {
+ fg = white;
+ bg = black;
+ };
+ };
+ contextmenu = {
+ menu = {
+ fg = white;
+ bg = black;
+ };
+ selected = {
+ fg = black;
+ bg = white;
+ };
+ disabled = {
+ fg = brightBlack;
+ bg = black;
+ };
+ };
+ downloads = {
+ bar.bg = black;
+ start = {
+ fg = green;
+ bg = black;
+ };
+ stop = {
+ fg = yellow;
+ bg = black;
+ };
+ error = {
+ fg = red;
+ bg = black;
+ };
+ system = {
+ fg = "none";
+ bg = "none";
+ };
+ };
+ hints = {
+ fg = white;
+ match.fg = red;
+ bg = black;
+ };
+ keyhint = {
+ fg = white;
+ suffix.fg = red;
+ bg = black;
+ };
+ messages = {
+ error = rec {
+ bg = black;
+ fg = red;
+ border = bg;
+ };
+ info = rec {
+ fg = blue;
+ bg = black;
+ border = bg;
+ };
+ warning = rec {
+ fg = yellow;
+ bg = black;
+ border = bg;
+ };
+ };
+ prompts = rec {
+ fg = white;
+ bg = black;
+ selected = {
+ fg = black;
+ bg = white;
+ };
+ border = bg;
+ };
+ statusbar = {
+ normal = {
+ bg = black;
+ fg = white;
+ };
+ command = {
+ bg = black;
+ fg = white;
+ };
+ insert = {
+ bg = green;
+ fg = black;
+ };
+ passthrough = {
+ bg = blue;
+ fg = black;
+ };
+ private = {
+ bg = magenta;
+ fg = black;
+ };
+ url = {
+ fg = blue;
+ hover.fg = brightBlue;
+ success = {
+ http.fg = brightGreen;
+ https.fg = brightGreen;
+ };
+ warn.fg = brightYellow;
+ error.fg = brightRed;
+ };
+ };
+ tabs = rec {
+ bar.bg = black;
+ even = {
+ bg = black;
+ fg = white;
+ };
+ odd = with even; {inherit bg fg;};
+ selected = rec {
+ even = {
+ bg = white;
+ fg = black;
+ };
+ odd = with even; {inherit bg fg;};
+ };
+ pinned = rec {
+ even = {
+ bg = brightBlack;
+ fg = brightWhite;
+ };
+ odd = with even; {inherit bg fg;};
+ };
+ indicator = {
+ start = green;
+ stop = yellow;
+ error = red;
+ system = "none";
+ };
+ };
+ webpage = {
+ bg = "white";
+ darkmode = {
+ enabled = false;
+ algorithm = "lightness-cielab";
+ contrast = 0.0;
+ grayscale = {
+ all = false;
+ images = 0.0;
+ };
+ policy = {
+ images = "smart";
+ page = "smart";
+ };
+ threshold = {
+ background = 0;
+ text = 256;
+ };
+ };
+ preferred_color_scheme = "auto";
+ };
+ };
+
+ fonts =
+ (with config.fontScheme.monospaceFont; {
+ default_family = family;
+ default_size = (toString size) + "pt";
+ })
+ // {
+ web = with config.fontScheme; {
+ family = rec {
+ standard = sans_serif;
+ fixed = monospaceFont.family;
+ serif = serifFont.family;
+ sans_serif = sansSerifFont.family;
+ cursive = null;
+ fantasy = null;
+ };
+ size = rec {
+ default = sansSerifFont.size;
+ default_fixed = monospaceFont.size;
+ minimum = 0;
+ minimum_logical = default / 2;
+ };
+ };
+ }
+ // (listToAttrs
+ (map (name: nameValuePair name "default_size default_family") [
+ "completion.category"
+ "completion.entry"
+ "contextmenu"
+ "debug_console"
+ "downloads"
+ "hints"
+ "keyhint"
+ "messages.error"
+ "messages.info"
+ "messages.warning"
+ "prompts"
+ "statusbar"
+ ]));
+
+ qt = mkIf kde.enable {
+ force_platform = null;
+ force_platformtheme = "KDE";
+ };
+ };
+
+ extraConfig =
+ (let
+ mkPaddingDictionary = {
+ name,
+ bottom,
+ left,
+ right,
+ top,
+ }: let
+ n = "c.${name}.padding";
+ b = "'bottom': ${toString bottom}";
+ l = "'left': ${toString left}";
+ r = "'right': ${toString right}";
+ t = "'top': ${toString top}";
+ in "${n} = {${b}, ${l}, ${r}, ${t}}";
+
+ final = map mkPaddingDictionary [
+ {
+ name = "hints";
+ bottom = 3;
+ left = 3;
+ right = 3;
+ top = 3;
+ }
+ {
+ name = "statusbar";
+ bottom = 1;
+ left = 0;
+ right = 3;
+ top = 1;
+ }
+ {
+ name = "tabs";
+ bottom = 1;
+ left = 6;
+ right = 6;
+ top = 1;
+ }
+ ];
+ in
+ concatStringsSep "\n" final + "\n")
+ + (let
+ allowSetting = setting: url: "config.set('content.${setting}', True, '${url}')";
+
+ allowMediaCaptureSetting = url: [
+ (allowSetting "desktop_capture" url)
+ (allowSetting "media.audio_video_capture" url)
+ ];
+ allowedMediaCapture = flatten (map allowMediaCaptureSetting [
+ "https://discord.com"
+ "https://meet.google.com"
+ "https://web.skype.com"
+ ]);
+
+ allowNotificationsSetting = allowSetting "notifications.enabled";
+ allowedNotifications = map allowNotificationsSetting [
+ "https://discord.com"
+ "https://web.skype.com"
+ "https://web.telegram.org"
+ "https://web.whatsapp.com"
+ ];
+
+ final = allowedMediaCapture ++ allowedNotifications;
+ in
+ concatStringsSep "\n" final + "\n");
+ };
+ };
+
+ services.psd.enable = true;
+ };
+}
diff --git a/modules/nixfiles/radarr.nix b/modules/nixfiles/radarr.nix
new file mode 100644
index 0000000..f57efc9
--- /dev/null
+++ b/modules/nixfiles/radarr.nix
@@ -0,0 +1,27 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.radarr;
+in {
+ options.nixfiles.modules.radarr = {
+ enable = mkEnableOption "Whether to enable Radarr.";
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; str;
+ default = "radarr.${config.networking.fqdn}";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ nixfiles.modules.nginx = {
+ enable = true;
+ virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://127.0.0.1:7878";
+ };
+
+ services.radarr.enable = true;
+ };
+}
diff --git a/modules/nixfiles/radicale.nix b/modules/nixfiles/radicale.nix
new file mode 100644
index 0000000..8286be1
--- /dev/null
+++ b/modules/nixfiles/radicale.nix
@@ -0,0 +1,48 @@
+{
+ config,
+ inputs,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.radicale;
+in {
+ options.nixfiles.modules.radicale = {
+ enable = mkEnableOption "Whether to enable Radicale.";
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; str;
+ default = "radicale.${config.networking.domain}";
+ };
+ };
+
+ config = let
+ port = 5232;
+ in
+ mkIf cfg.enable {
+ secrets.radicale-htpasswd = {
+ file = "${inputs.self}/secrets/radicale-htpasswd";
+ owner = "radicale";
+ group = "radicale";
+ };
+
+ nixfiles.modules.nginx = {
+ enable = true;
+ virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://127.0.0.1:${toString port}";
+ };
+
+ services.radicale = {
+ enable = true;
+ settings = {
+ server.hosts = ["127.0.0.1:${toString port}"];
+ web.type = "none";
+ auth = {
+ type = "htpasswd";
+ htpasswd_filename = config.secrets.radicale-htpasswd.path;
+ htpasswd_encryption = "bcrypt";
+ };
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/rss-bridge.nix b/modules/nixfiles/rss-bridge.nix
new file mode 100644
index 0000000..fe3a638
--- /dev/null
+++ b/modules/nixfiles/rss-bridge.nix
@@ -0,0 +1,33 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.rss-bridge;
+in {
+ options.nixfiles.modules.rss-bridge = {
+ enable = mkEnableOption "Whether to enable rss-bridge.";
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; str;
+ default = "rss-bridge.${config.networking.domain}";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ nixfiles.modules.nginx = {
+ enable = true;
+ virtualHosts.${cfg.domain} = {};
+ };
+
+ services = {
+ rss-bridge = {
+ enable = true;
+ virtualHost = cfg.domain;
+ whitelist = ["LWNprev" "Phoronix"]; # TODO Expand.
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/rtorrent.nix b/modules/nixfiles/rtorrent.nix
new file mode 100644
index 0000000..2f1708c
--- /dev/null
+++ b/modules/nixfiles/rtorrent.nix
@@ -0,0 +1,246 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.rtorrent;
+in {
+ options.nixfiles.modules.rtorrent = {
+ enable = mkEnableOption "Whether to enable rTorrent.";
+
+ flood = {
+ enable = mkEnableOption "Whether to enable Flood.";
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; str;
+ default = "flood.${config.networking.domain}";
+ };
+ };
+ };
+
+ config = let
+ user = "rtorrent";
+ group = "rtorrent";
+ baseDir = "/var/lib/rtorrent";
+ rpcSocket = "/run/rtorrent/rpc.socket";
+ in
+ mkIf cfg.enable (mkMerge [
+ (let
+ port = 50000;
+ in {
+ systemd = {
+ services.rtorrent = {
+ description = "rTorrent";
+ after = ["network.target" "local-fs.target"];
+ serviceConfig = let
+ leechDir = "${baseDir}/leech";
+ seedDir = "${baseDir}/seed";
+ sessionDir = "${baseDir}/session";
+ logDir = "${baseDir}/log";
+ in {
+ User = user;
+ Group = group;
+ Type = "simple";
+ KillMode = "process";
+ KillSignal = "SIGHUP";
+ ExecStartPre = concatStringsSep " " [
+ "${pkgs.coreutils-full}/bin/mkdir -p"
+ leechDir
+ seedDir
+ sessionDir
+ logDir
+ ];
+ ExecStart = let
+ configFile = let
+ moveCompleted = let
+ pkg = pkgs.writeShellApplication {
+ name = "move-completed";
+ runtimeInputs = with pkgs; [
+ coreutils-full
+ gnused
+ findutils
+ ];
+ text = ''
+ set -x
+
+ leech_path="$1"
+ seed_path="$2"
+ # seed_path="$(echo "$2" | sed 's@+@ @g;s@%@\\x@g' | xargs -0 printf '%b')"
+
+ mkdir -p "$seed_path"
+ mv -u "$leech_path" "$seed_path"
+ '';
+ };
+ in "${pkg}/bin/move-completed";
+ in
+ pkgs.writeText "rtorrent.rc" ''
+ method.insert = cfg.leech, private|const|string, (cat, "${leechDir}")
+ method.insert = cfg.seed, private|const|string, (cat, "${seedDir}")
+ method.insert = cfg.session, private|const|string, (cat, "${sessionDir}")
+ method.insert = cfg.log, private|const|string, (cat, "${logDir}")
+ method.insert = cfg.rpcsocket, private|const|string, (cat, "${rpcSocket}")
+
+ directory.default.set = (cat, (cfg.leech))
+ session.path.set = (cat, (cfg.session))
+
+ network.port_range.set = ${toString port}-${toString port}
+ network.port_random.set = no
+
+ dht.mode.set = disable
+ protocol.pex.set = no
+
+ trackers.use_udp.set = no
+
+ protocol.encryption.set = allow_incoming,try_outgoing,enable_retry
+
+ pieces.memory.max.set = 2048M
+ pieces.preload.type.set = 2
+
+ network.max_open_files.set = 1024
+ network.max_open_sockets.set = 1024
+
+ network.http.max_open.set = 128
+
+ throttle.global_down.max_rate.set_kb = 0
+ throttle.global_up.max_rate.set_kb = 0
+
+ encoding.add = UTF-8
+ system.umask.set = 0027
+ system.cwd.set = (directory.default)
+
+ network.scgi.open_local = (cat, (cfg.rpcsocket))
+ schedule = scgi_group, 0, 0, "\
+ execute.nothrow=chown, \":${group}\", (cfg.rpcsocket)\
+ "
+ schedule = scgi_permission, 0, 0, "\
+ execute.nothrow=chmod, \"g+w,o=\", (cfg.rpcsocket)\
+ "
+
+ method.insert = d.move_completed, simple, "\
+ d.directory.set=$argument.1=;\
+ execute=${moveCompleted}, $argument.0=, $argument.1=;\
+ d.save_full_session=\
+ "
+ method.insert = d.leech_path, simple, "\
+ if=(d.is_multi_file),\
+ (cat, (d.directory), /),\
+ (cat, (d.directory), /, (d.name))\
+ "
+ method.insert = d.seed_path, simple, "\
+ cat=$cfg.seed=, /, $d.custom1=\
+ "
+ method.set_key = event.download.finished, move_complete, "\
+ d.move_completed=$d.leech_path=, $d.seed_path=\
+ "
+
+ log.open_file = "log", (cat, (cfg.log), "/", "default.log")
+ log.add_output = "debug", "log"
+ log.execute = (cat, (cfg.log), "/", "execute.log")
+ '';
+ in
+ concatStringsSep " " [
+ "${pkgs.rtorrent}/bin/rtorrent"
+ "-n"
+ "-o system.daemon.set=true"
+ "-o import=${configFile}"
+ ];
+ Restart = "on-failure";
+ RestartSec = 3;
+ RuntimeDirectory = "rtorrent";
+ RuntimeDirectoryMode = 755;
+ };
+ wantedBy = ["multi-user.target"];
+ };
+
+ tmpfiles.rules = ["d '${baseDir}' 0750 ${user} ${group} -"];
+ };
+
+ users = {
+ users.${user} = {
+ inherit group;
+ shell = pkgs.bashInteractive;
+ home = baseDir;
+ description = "rTorrent";
+ isSystemUser = true;
+ };
+ groups.${group} = {};
+ };
+ my.extraGroups = [group];
+
+ networking.firewall.allowedTCPPorts = [port];
+
+ boot.kernel.sysctl = {
+ "net.core.rmem_max" = mkOverride 500 (pow 2 24);
+ "net.core.wmem_max" = mkOverride 500 (pow 2 24);
+ "net.ipv4.tcp_fin_timeout" = mkOverride 500 30;
+ "net.ipv4.tcp_rmem" = mkOverride 500 (mkTcpMem 12 23 24);
+ "net.ipv4.tcp_slow_start_after_idle" = 0;
+ "net.ipv4.tcp_tw_recycle" = mkOverride 500 1;
+ "net.ipv4.tcp_tw_reuse" = mkOverride 500 1;
+ "net.ipv4.tcp_wmem" = mkOverride 500 (mkTcpMem 12 23 24);
+ };
+ })
+ (let
+ port = 50001;
+ pkg = pkgs.nodePackages.flood;
+ in
+ mkIf cfg.flood.enable {
+ nixfiles.modules.nginx = {
+ enable = true;
+ virtualHosts.${cfg.flood.domain} = {
+ root = "${pkg}/lib/node_modules/flood/dist/assets";
+ locations = {
+ "/" = {
+ tryFiles = "$uri /index.html";
+ extraConfig = ''
+ if ($internal != 1) {
+ return 403;
+ }
+ '';
+ };
+ "/api" = {
+ proxyPass = "http://127.0.0.1:${toString port}";
+ extraConfig = ''
+ proxy_buffering off;
+ proxy_cache off;
+
+ if ($internal != 1) {
+ return 403;
+ }
+ '';
+ };
+ };
+ };
+ };
+
+ systemd.services.flood = {
+ description = "Flood";
+ after = ["network.target" "rtorrent.service"];
+ path = with pkgs; [mediainfo];
+ serviceConfig = {
+ User = user;
+ Group = group;
+ Type = "simple";
+ KillMode = "process";
+ ExecStart = concatStringsSep " " [
+ "${pkg}/bin/flood"
+ "--allowedpath=${baseDir}"
+ "--baseuri=/"
+ "--rundir=${baseDir}/flood"
+ "--host=127.0.0.1"
+ "--port=${toString port}"
+ "--rtsocket=${rpcSocket}"
+ "--ssl=false"
+ "--auth=none"
+ ];
+ Restart = "on-failure";
+ RestartSec = 3;
+ };
+ wantedBy = ["multi-user.target"];
+ };
+ })
+ ]);
+}
diff --git a/modules/nixfiles/searx.nix b/modules/nixfiles/searx.nix
new file mode 100644
index 0000000..a5bb005
--- /dev/null
+++ b/modules/nixfiles/searx.nix
@@ -0,0 +1,81 @@
+{
+ config,
+ inputs,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.searx;
+in {
+ options.nixfiles.modules.searx = {
+ enable = mkEnableOption "Whether to enable SearX.";
+
+ port = mkOption {
+ description = "Port.";
+ type = with types; port;
+ default = 61001;
+ };
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; nullOr str;
+ default = "searx.${config.networking.domain}";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ secrets.searx-environment = {
+ file = "${inputs.self}/secrets/searx-environment";
+ owner = "searx";
+ group = "searx";
+ };
+
+ nixfiles.modules.nginx = {
+ enable = true;
+ virtualHosts.${cfg.domain}.locations."/" = {
+ proxyPass = "http://127.0.0.1:${toString cfg.port}";
+ extraConfig = ''
+ if ($internal != 1) {
+ return 403;
+ }
+ '';
+ };
+ };
+
+ services = {
+ searx = {
+ enable = true;
+
+ settings = {
+ general = {
+ instance_name = cfg.domain;
+ contact_url = "mailto:admin+searx@${config.networking.domain}";
+ git_url = false;
+ git_branch = false;
+ docs_url = false;
+ wiki_url = false;
+ twitter_url = false;
+ };
+ server = {
+ bind_address = "127.0.0.1";
+ inherit (cfg) port;
+ secret_key = "@SECRET_KEY@";
+ base_url = false;
+ image_proxy = false;
+ default_http_headers = {
+ Referrer-Policy = "no-referrer";
+ X-Content-Type-Options = "nosniff";
+ X-Download-Options = "noopen";
+ X-Robots-Tag = "noindex, nofollow, nosnippet, noarchive";
+ };
+ };
+ search = {
+ safe_search = 0;
+ autocomplete = "";
+ };
+ };
+ environmentFile = config.secrets.searx-environment.path;
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/shadowsocks.nix b/modules/nixfiles/shadowsocks.nix
new file mode 100644
index 0000000..c496caa
--- /dev/null
+++ b/modules/nixfiles/shadowsocks.nix
@@ -0,0 +1,119 @@
+{
+ config,
+ inputs,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.shadowsocks;
+in {
+ options.nixfiles.modules.shadowsocks = {
+ enable = mkEnableOption "Whether to enable Shadowsocks.";
+
+ port = mkOption {
+ type = with types; port;
+ default = 8388;
+ description = "Port.";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ secrets.shadowsocks-password = {
+ file = "${inputs.self}/secrets/shadowsocks-password";
+ mode = "0444"; # User is dynamic.
+ };
+
+ services = {
+ shadowsocks = {
+ enable = true;
+ passwordFile = config.secrets.shadowsocks-password.path;
+ localAddress = ["0.0.0.0"];
+ mode = "tcp_only";
+ };
+
+ fail2ban.jails.shadowsocks-libev = ''
+ enabled = true
+ filter = shadowsocks-libev
+ port = ${toString cfg.port}
+ '';
+ };
+
+ systemd.services.shadowsocks-libev.path = with pkgs;
+ mkForce [
+ (writeShellApplication {
+ name = "ss-server";
+ runtimeInputs = [shadowsocks-libev];
+ text = let
+ # https://github.com/shadowsocks/shadowsocks-libev/blob/master/acl/server_block_local.acl
+ aclFile = writeText "outbound_block_list.acl" ''
+ [outbound_block_list]
+ 0.0.0.0/8
+ 10.0.0.0/8
+ 100.64.0.0/10
+ 127.0.0.0/8
+ 169.254.0.0/16
+ 172.16.0.0/12
+ 192.0.0.0/24
+ 192.0.2.0/24
+ 192.88.99.0/24
+ 192.168.0.0/16
+ 198.18.0.0/15
+ 198.51.100.0/24
+ 203.0.113.0/24
+ 224.0.0.0/4
+ 240.0.0.0/4
+ 255.255.255.255/32
+ ::1/128
+ ::ffff:127.0.0.1/104
+ fc00::/7
+ fe80::/10
+ '';
+ in ''
+ ss-server --acl ${aclFile} "$@"
+ '';
+ })
+ coreutils-full
+ jq
+ ];
+
+ environment.etc = mkIf config.nixfiles.modules.fail2ban.enable {
+ "fail2ban/filter.d/shadowsocks-libev.conf".text = ''
+ [Definition]
+ failregex = ^.*failed to handshake with <ADDR>: authentication error$
+ ignoreregex =
+ journalmatch = _SYSTEMD_UNIT=shadowsocks-libev.service
+ '';
+ };
+
+ networking.firewall = {
+ allowedTCPPorts = [cfg.port];
+ extraCommands = ''
+ iptables -A nixos-fw -p tcp --syn --dport ${
+ toString cfg.port
+ } -m connlimit --connlimit-above 32 -j nixos-fw-refuse
+ '';
+ };
+
+ boot.kernel.sysctl = {
+ "net.core.rmem_max" = mkOverride 100 (pow 2 26);
+ "net.core.wmem_max" = mkOverride 100 (pow 2 26);
+ "net.core.netdev_max_backlog" = pow 2 18;
+ "net.core.somaxconn" = pow 2 12;
+ "net.ipv4.tcp_syncookies" = 1;
+ "net.ipv4.tcp_tw_reuse" = mkOverride 100 1;
+ "net.ipv4.tcp_tw_recycle" = mkOverride 100 0;
+ "net.ipv4.tcp_fin_timeout" = mkOverride 100 30;
+ "net.ipv4.tcp_keepalive_time" = 60 * 20;
+ "net.ipv4.ip_local_port_range" = "10000 65000";
+ "net.ipv4.tcp_max_syn_backlog" = pow 2 13;
+ "net.ipv4.tcp_max_tw_buckets" = pow 2 12;
+ "net.ipv4.tcp_fastopen" = 3;
+ "net.ipv4.tcp_mem" = mkOverride 100 (mkTcpMem 15 16 17);
+ "net.ipv4.tcp_rmem" = mkOverride 100 (mkTcpMem 12 16 26);
+ "net.ipv4.tcp_wmem" = mkOverride 100 (mkTcpMem 12 16 26);
+ "net.ipv4.tcp_mtu_probing" = 1;
+ "net.ipv4.tcp_congestion_control" = "hybla";
+ };
+ };
+}
diff --git a/modules/nixfiles/soju.nix b/modules/nixfiles/soju.nix
new file mode 100644
index 0000000..b586cbf
--- /dev/null
+++ b/modules/nixfiles/soju.nix
@@ -0,0 +1,83 @@
+{
+ config,
+ lib,
+ pkgs,
+ this,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.soju;
+in {
+ options.nixfiles.modules.soju = {
+ enable = mkEnableOption "Whether to enable soju.";
+
+ protocol = mkOption {
+ description = "Port.";
+ type = with types; enum ["ircs" "irc+insecure"];
+ default = "irc+insecure";
+ };
+
+ address = mkOption {
+ description = "Address.";
+ type = with types; str;
+ default = this.wireguard.ipv4.address;
+ };
+
+ port = mkOption {
+ description = "Port.";
+ type = with types; port;
+ default = 6667;
+ };
+
+ domain = mkOption {
+ description = "Domain.";
+ type = with types; str;
+ default = config.networking.fqdn;
+ };
+ };
+
+ config = let
+ db = "soju";
+ in
+ mkIf cfg.enable {
+ services.postgresql = {
+ ensureDatabases = [db];
+ ensureUsers = [
+ {
+ name = db;
+ ensurePermissions."DATABASE \"${db}\"" = "ALL PRIVILEGES";
+ }
+ ];
+ };
+
+ systemd.services.soju = {
+ description = "soju IRC bouncer";
+ wantedBy = ["multi-user.target"];
+ after = ["network-online.target" "postgresql.service"];
+ serviceConfig = {
+ Restart = "always";
+ ExecStart = let
+ configFile = pkgs.writeText "soju.conf" ''
+ listen ${cfg.protocol}://${cfg.address}:${toString cfg.port}
+ db postgres "${
+ concatStringsSep " " [
+ "host=/run/postgresql"
+ "user=${db}"
+ "dbname=${db}"
+ "sslmode=disable"
+ ]
+ }"
+ hostname ${cfg.domain}
+ title ${cfg.domain}
+ '';
+ in
+ concatStringsSep " " [
+ "${pkgs.soju}/bin/soju"
+ "-config ${configFile}"
+ ];
+ DynamicUser = true;
+ StateDirectory = "soju";
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/sonarr.nix b/modules/nixfiles/sonarr.nix
new file mode 100644
index 0000000..a2003f0
--- /dev/null
+++ b/modules/nixfiles/sonarr.nix
@@ -0,0 +1,27 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.sonarr;
+in {
+ options.nixfiles.modules.sonarr = {
+ enable = mkEnableOption "Whether to enable Sonarr.";
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; str;
+ default = "sonarr.${config.networking.fqdn}";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ nixfiles.modules.nginx = {
+ enable = true;
+ virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://127.0.0.1:8989";
+ };
+
+ services.sonarr.enable = true;
+ };
+}
diff --git a/modules/nixfiles/sound.nix b/modules/nixfiles/sound.nix
new file mode 100644
index 0000000..b352f3e
--- /dev/null
+++ b/modules/nixfiles/sound.nix
@@ -0,0 +1,21 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.sound;
+in {
+ options.nixfiles.modules.sound.enable =
+ mkEnableOption "Whether to enable sound support.";
+
+ config = mkIf cfg.enable {
+ services.pipewire = {
+ enable = true;
+
+ alsa.enable = false;
+ jack.enable = false;
+ pulse.enable = true;
+ };
+ };
+}
diff --git a/modules/nixfiles/subversion.nix b/modules/nixfiles/subversion.nix
new file mode 100644
index 0000000..1cfdca6
--- /dev/null
+++ b/modules/nixfiles/subversion.nix
@@ -0,0 +1,53 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.subversion;
+in {
+ options.nixfiles.modules.subversion.enable =
+ mkEnableOption "Whether to enable Subversion version control system.";
+
+ config = mkIf cfg.enable {
+ nixfiles.modules.gnupg.enable = true;
+
+ hm.home = {
+ file = {
+ ".subversion/config".text = generators.toINI {} {
+ auth = {
+ password-stores = "gpg-agent";
+ ssl-client-cert-file-prompt = "no";
+ store-passwords = "yes";
+ store-auth-creds = "yes";
+ };
+ helpers = {
+ editor-cmd = "${config.programs.vim.package}/bin/vim";
+ diff-cmd = "${pkgs.colordiff}/bin/colordiff";
+ };
+ miscellany = {
+ global-ignores = with config.hm.programs.git;
+ optionalString (ignores != []) (concatStringsSep " " ignores);
+ diff-ignore-content-type = "no";
+ };
+ working-copy = {
+ exclusive-locking-clients = "svn";
+ exclusive-locking = true;
+ busy-timeout = 10000;
+ };
+ };
+
+ ".subversion/servers".text = generators.toINI {} {
+ global = {
+ store-auth-creds = "yes";
+ store-passwords = "yes";
+ store-plaintext-passwords = "yes";
+ };
+ };
+ };
+
+ packages = with pkgs; [(subversionClient.override {saslSupport = true;})];
+ };
+ };
+}
diff --git a/modules/nixfiles/syncthing.nix b/modules/nixfiles/syncthing.nix
new file mode 100644
index 0000000..5a973cc
--- /dev/null
+++ b/modules/nixfiles/syncthing.nix
@@ -0,0 +1,161 @@
+{
+ config,
+ lib,
+ pkgs,
+ this,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.syncthing;
+in {
+ options.nixfiles.modules.syncthing = {
+ enable = mkEnableOption "Whether to enable Syncthing.";
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; str;
+ default = "syncthing.${config.networking.fqdn}";
+ };
+
+ # TODO Make this simpler.
+ cert = mkOption {
+ description = "Path to the cert file.";
+ type = with types; nullOr string;
+ default = null;
+ };
+
+ # TODO Make this simpler.
+ key = mkOption {
+ description = "Path to the key file.";
+ type = with types; nullOr string;
+ default = null;
+ };
+ };
+
+ config = mkIf cfg.enable (mkMerge [
+ {
+ assertions = [
+ {
+ assertion = cfg.cert != null;
+ message = "Cert file needs to be specified.";
+ }
+ {
+ assertion = cfg.key != null;
+ message = "Key file needs to be specified.";
+ }
+ ];
+
+ services.syncthing = {
+ enable = true;
+
+ user = my.username;
+ inherit (config.my) group;
+
+ dataDir = config.my.home;
+
+ guiAddress = "127.0.0.1:8384";
+
+ inherit (cfg) key cert;
+
+ overrideDevices = true;
+ devices = mapAttrs (name: attr:
+ mkIf (attr.syncthing.id != null && hasAttr "wireguard" attr) {
+ inherit (attr.syncthing) id;
+ addresses = ["tcp://${name}.${config.networking.domain}:22000"];
+ introducer = this.isHeadless;
+ })
+ my.configurations;
+
+ overrideFolders = true;
+ folders = let
+ filterDevices = f:
+ attrNames (filterAttrs (_: attr:
+ (attr.hostname != this.hostname)
+ && (attr.syncthing.id != null)
+ && f attr)
+ my.configurations);
+ all = filterDevices (_: true);
+ notHeadless = filterDevices (attr: !attr.isHeadless);
+ notOther = filterDevices (attr: !attr.isOther);
+
+ simple = {
+ type = "simple";
+ params.keep = "5";
+ };
+ trashcan = {
+ type = "trashcan";
+ params.cleanoutDays = "30";
+ };
+ void = {
+ type = "external";
+ params.versionPath = with pkgs;
+ writeShellScriptBin "backup" ''
+ ${coreutils-full}/bin/rm -rf $1/$2
+ '';
+ };
+ in
+ with config.hm.xdg.userDirs; {
+ share = {
+ path = publicShare;
+ devices = notHeadless;
+ versioning = void;
+ };
+ pass = {
+ path =
+ config.hm.programs.password-store.settings.PASSWORD_STORE_DIR;
+ devices = all;
+ versioning = trashcan;
+ };
+ org = {
+ path = "${documents}/org";
+ devices = all;
+ versioning = simple;
+ };
+ roam = {
+ path = "${documents}/roam";
+ devices = notOther;
+ versioning = simple;
+ };
+ elfeed = {
+ path = "${config.my.home}/.elfeed";
+ devices = notOther;
+ versioning = trashcan;
+ };
+ vidya = {
+ path = "${documents}/vidya";
+ devices = notOther;
+ versioning = void;
+ };
+ };
+
+ extraOptions = {
+ gui = {
+ insecureAdminAccess = true;
+ insecureSkipHostcheck = this.isHeadless;
+ };
+ options = {
+ # Only local discovery is used over VPN.
+ globalAnnounceEnabled = false;
+ relaysEnabled = false;
+ urAccepted = -1;
+ };
+ };
+ };
+
+ systemd.services.syncthing.environment.STNODEFAULTFOLDER = "yes";
+ }
+ (mkIf this.isHeadless {
+ nixfiles.modules.nginx = {
+ enable = true;
+ virtualHosts.${cfg.domain}.locations."/" = {
+ proxyPass = "http://${config.services.syncthing.guiAddress}";
+ extraConfig = ''
+ if ($internal != 1) {
+ return 403;
+ }
+ '';
+ };
+ };
+ })
+ ]);
+}
diff --git a/modules/nixfiles/throttled.nix b/modules/nixfiles/throttled.nix
new file mode 100644
index 0000000..b7c7d8a
--- /dev/null
+++ b/modules/nixfiles/throttled.nix
@@ -0,0 +1,117 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.throttled;
+in {
+ options.nixfiles.modules.throttled.enable =
+ mkEnableOption "Whether to enable Throttled.";
+
+ config = mkIf cfg.enable {
+ environment.etc."throttled.conf".text = ''
+ [GENERAL]
+ # Enable or disable the script execution
+ Enabled: True
+ # SYSFS path for checking if the system is running on AC power
+ Sysfs_Power_Path: /sys/class/power_supply/AC*/online
+ # Auto reload config on changes
+ Autoreload: True
+
+ ## Settings to apply while connected to Battery power
+ [BATTERY]
+ # Update the registers every this many seconds
+ Update_Rate_s: 30
+ # Max package power for time window #1
+ PL1_Tdp_W: 29
+ # Time window #1 duration
+ PL1_Duration_s: 28
+ # Max package power for time window #2
+ PL2_Tdp_W: 44
+ # Time window #2 duration
+ PL2_Duration_S: 0.002
+ # Max allowed temperature before throttling
+ Trip_Temp_C: 85
+ # Set cTDP to normal=0, down=1 or up=2 (EXPERIMENTAL)
+ cTDP: 0
+ # Disable BDPROCHOT (EXPERIMENTAL)
+ Disable_BDPROCHOT: False
+
+ ## Settings to apply while connected to AC power
+ [AC]
+ # Update the registers every this many seconds
+ Update_Rate_s: 5
+ # Max package power for time window #1
+ PL1_Tdp_W: 44
+ # Time window #1 duration
+ PL1_Duration_s: 28
+ # Max package power for time window #2
+ PL2_Tdp_W: 44
+ # Time window #2 duration
+ PL2_Duration_S: 0.002
+ # Max allowed temperature before throttling
+ Trip_Temp_C: 95
+ # Set HWP energy performance hints to 'performance' on high load (EXPERIMENTAL)
+ # Uncomment only if you really want to use it
+ # HWP_Mode: False
+ # Set cTDP to normal=0, down=1 or up=2 (EXPERIMENTAL)
+ cTDP: 0
+ # Disable BDPROCHOT (EXPERIMENTAL)
+ Disable_BDPROCHOT: False
+
+ # All voltage values are expressed in mV and *MUST* be negative (i.e. undervolt)!
+ [UNDERVOLT.BATTERY]
+ # CPU core voltage offset (mV)
+ CORE: 0
+ # Integrated GPU voltage offset (mV)
+ GPU: 0
+ # CPU cache voltage offset (mV)
+ CACHE: 0
+ # System Agent voltage offset (mV)
+ UNCORE: 0
+ # Analog I/O voltage offset (mV)
+ ANALOGIO: 0
+
+ # All voltage values are expressed in mV and *MUST* be negative (i.e. undervolt)!
+ [UNDERVOLT.AC]
+ # CPU core voltage offset (mV)
+ CORE: 0
+ # Integrated GPU voltage offset (mV)
+ GPU: 0
+ # CPU cache voltage offset (mV)
+ CACHE: 0
+ # System Agent voltage offset (mV)
+ UNCORE: 0
+ # Analog I/O voltage offset (mV)
+ ANALOGIO: 0
+
+ # [ICCMAX.AC]
+ # # CPU core max current (A)
+ # CORE:
+ # # Integrated GPU max current (A)
+ # GPU:
+ # # CPU cache max current (A)
+ # CACHE:
+
+ # [ICCMAX.BATTERY]
+ # # CPU core max current (A)
+ # CORE:
+ # # Integrated GPU max current (A)
+ # GPU:
+ # # CPU cache max current (A)
+ # CACHE:
+ '';
+
+ systemd.services.throttled = {
+ description = "Stop Intel throttling";
+ serviceConfig = {
+ Type = "simple";
+ ExecStart = "${pkgs.throttled}/opt/throttled/throttled.py";
+ };
+ environment.PYTHONUNBUFFERED = "1";
+ wantedBy = ["multi-user.target"];
+ };
+ };
+}
diff --git a/modules/nixfiles/tmux.nix b/modules/nixfiles/tmux.nix
new file mode 100644
index 0000000..c90b0fc
--- /dev/null
+++ b/modules/nixfiles/tmux.nix
@@ -0,0 +1,60 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.tmux;
+in {
+ options.nixfiles.modules.tmux.enable =
+ mkEnableOption "Whether to enable tmux.";
+
+ config = mkIf cfg.enable {
+ hm.programs.tmux = {
+ enable = true;
+
+ aggressiveResize = true;
+ baseIndex = 1;
+ clock24 = true;
+ disableConfirmationPrompt = true;
+ escapeTime = 0;
+ historyLimit = 50000;
+ newSession = true;
+ resizeAmount = 10;
+ terminal = "screen-256color";
+
+ extraConfig = ''
+ set -g set-titles on
+
+ set -g status-left ""
+ set -g status-right ""
+
+ set -g detach-on-destroy off
+
+ set -g status-keys emacs
+ set -g mode-keys vi
+
+ bind h select-pane -L
+ bind j select-pane -D
+ bind k select-pane -U
+ bind l select-pane -R
+
+ bind -r H resize-pane -L 10
+ bind -r J resize-pane -D 10
+ bind -r K resize-pane -U 10
+ bind -r L resize-pane -R 10
+
+ bind < swap-pane -D
+ bind > swap-pane -U
+
+ bind , swap-window -t -1
+ bind . swap-window -t +1
+
+ bind Tab last-window
+
+ bind _ split-window -v
+ bind | split-window -h
+ '';
+ };
+ };
+}
diff --git a/modules/nixfiles/unbound.nix b/modules/nixfiles/unbound.nix
new file mode 100644
index 0000000..af983a8
--- /dev/null
+++ b/modules/nixfiles/unbound.nix
@@ -0,0 +1,208 @@
+{
+ config,
+ lib,
+ pkgs,
+ this,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.unbound;
+in {
+ options.nixfiles.modules.unbound = {
+ enable = mkEnableOption ''
+ Whether to enable Unbound.
+
+ This service is configured to be used in conjunction with a Wireguard
+ server located on the same host to enable internal name resolution with a
+ forwarding fallback via Quad9.
+
+ Features included: Internal name resolution, Quad9 as a recursive
+ resolver, DNSSEC validation, Redis cache and adblocking via pgl.yoyo.org
+ lists.
+ '';
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; str;
+ default = config.networking.domain;
+ };
+ };
+
+ config = let
+ adblock-conf = "${config.services.unbound.stateDir}/adblock.conf";
+ in
+ mkIf cfg.enable {
+ services = {
+ unbound = {
+ enable = true;
+
+ package = pkgs.unbound-with-systemd.override {
+ withRedis = true;
+ withTFO = true;
+ };
+
+ settings = {
+ server = {
+ interface = with this.wireguard; [
+ "127.0.0.1"
+ "::1"
+ ipv4.address
+ ipv6.address
+ ];
+
+ local-zone =
+ concatLists
+ (mapAttrsToList (h: _: [''"${h}.${cfg.domain}" redirect''])
+ my.configurations);
+ local-data = concatLists (mapAttrsToList (hostname: let
+ domain = "${hostname}.${cfg.domain}";
+ in
+ attr: (optionals (hasAttr "wireguard" attr) (with attr.wireguard;
+ [
+ ''"${domain} 604800 IN A ${ipv4.address}"''
+ ''"${domain} 604800 IN AAAA ${ipv6.address}"''
+ ''"${domain}. A ${ipv4.address}"''
+ ''"${domain}. AAAA ${ipv6.address}"''
+ ]
+ ++ concatMap (domain: [
+ ''"${domain}. A ${ipv4.address}"''
+ ''"${domain}. AAAA ${ipv6.address}"''
+ ])
+ attr.domains)))
+ my.configurations);
+ local-data-ptr = concatLists (mapAttrsToList (hostname: let
+ domain = "${hostname}.${cfg.domain}";
+ in
+ attr: (optionals (hasAttr "wireguard" attr) (with attr.wireguard;
+ [
+ ''"${ipv4.address} ${domain}"''
+ ''"${ipv6.address} ${domain}"''
+ ]
+ ++ concatMap (domain: [
+ ''"${ipv4.address} ${domain}"''
+ ''"${ipv6.address} ${domain}"''
+ ])
+ attr.domains)))
+ my.configurations);
+
+ access-control = with config.nixfiles.modules.wireguard; [
+ "0.0.0.0/0 refuse"
+ "::/0 refuse"
+ "127.0.0.0/8 allow"
+ "::1/128 allow"
+ "${ipv4.subnet} allow"
+ "${ipv6.subnet} allow"
+ ];
+
+ private-domain = cfg.domain;
+ private-address = with config.nixfiles.modules.wireguard; [
+ ipv4.subnet
+ ipv6.subnet
+ ];
+
+ domain-insecure = cfg.domain;
+
+ prefetch = true;
+ prefetch-key = true;
+
+ hide-identity = true;
+ hide-version = true;
+
+ extended-statistics = true;
+
+ include = ''"${adblock-conf}"'';
+ };
+
+ forward-zone = [
+ {
+ name = ".";
+ forward-tls-upstream = true;
+ forward-addr = let
+ mkDnsOverTls = ips: auth:
+ map (ip: concatStrings [ip "@" auth]) ips;
+ in
+ mkDnsOverTls dns.const.quad9.default "853#dns.quad9.net";
+ }
+ ];
+
+ cachedb = with config.services.redis.servers.unbound; {
+ backend = "redis";
+ redis-server-host = bind;
+ redis-server-port = port;
+ };
+ };
+
+ localControlSocketPath = "/run/unbound/unbound.socket";
+ };
+
+ redis = {
+ servers.unbound = {
+ enable = true;
+ bind = "127.0.0.1";
+ port = 6379;
+ };
+ vmOverCommit = mkForce true;
+ };
+
+ prometheus.exporters = {
+ unbound = {
+ enable = true;
+ listenAddress = mkDefault this.wireguard.ipv4.address;
+ port = 9167;
+ fetchType = "uds";
+ controlInterface = config.services.unbound.localControlSocketPath;
+ inherit (config.services.unbound) group user;
+ };
+
+ redis = {
+ enable = true;
+ listenAddress = mkDefault this.wireguard.ipv4.address;
+ port = mkDefault 9121;
+ extraFlags = with config.services.redis.servers.unbound; [
+ "--redis.addr=redis://${bind}:${toString port}"
+ "--redis.user=${user}"
+ ];
+ };
+ };
+ };
+
+ systemd = {
+ services = {
+ unbound.after = ["unbound-adblock-update.service"];
+
+ unbound-adblock-update = {
+ serviceConfig = with config.services.unbound; {
+ Type = "oneshot";
+ User = user;
+ Group = group;
+ ExecStart = let
+ pkg = with pkgs;
+ writeShellApplication {
+ name = "unbound-adblock-update";
+ runtimeInputs = [curl package];
+ text = ''
+ curl \
+ "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=unbound&showintro=0&mimetype=plaintext" \
+ >${adblock-conf}
+
+ if [[ -f "${localControlSocketPath}" ]]; then
+ unbound-control reload
+ fi
+ '';
+ };
+ in "${pkg}/bin/unbound-adblock-update";
+ };
+ };
+ };
+
+ timers.unbound-adblock-update = {
+ requires = ["network-online.target"];
+ timerConfig = {
+ OnUnitActiveSec = "1d";
+ Unit = "unbound-adblock-update.service";
+ };
+ wantedBy = ["timers.target"];
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/vaultwarden.nix b/modules/nixfiles/vaultwarden.nix
new file mode 100644
index 0000000..4f5bcb4
--- /dev/null
+++ b/modules/nixfiles/vaultwarden.nix
@@ -0,0 +1,117 @@
+{
+ config,
+ inputs,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.vaultwarden;
+in {
+ options.nixfiles.modules.vaultwarden = {
+ enable = mkEnableOption "Whether to enable Vaultwarden.";
+
+ domain = mkOption {
+ description = "Domain name sans protocol scheme.";
+ type = with types; str;
+ default = "vaultwarden.${config.networking.domain}";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ secrets.vaultwarden-environment = {
+ file = "${inputs.self}/secrets/vaultwarden-environment";
+ owner = "vaultwarden";
+ group = "vaultwarden";
+ };
+
+ nixfiles.modules = {
+ nginx = {
+ enable = true;
+ virtualHosts.${cfg.domain} = with config.services.vaultwarden.config; {
+ locations."/" = {
+ proxyPass = "http://[${ROCKET_ADDRESS}]:${toString ROCKET_PORT}";
+ proxyWebsockets = true;
+ };
+ locations."/notifications/hub" = {
+ proxyPass = "http://[${WEBSOCKET_ADDRESS}]:${toString WEBSOCKET_PORT}";
+ proxyWebsockets = true;
+ };
+ locations."/notifications/hub/negotiate" = {
+ proxyPass = "http://[${ROCKET_ADDRESS}]:${toString ROCKET_PORT}";
+ proxyWebsockets = true;
+ };
+ };
+ };
+ postgresql.enable = true;
+ };
+
+ services = let
+ db = "vaultwarden";
+ in {
+ vaultwarden = {
+ enable = true;
+ config = {
+ TZ = config.time.timeZone;
+
+ WEB_VAULT_ENABLED = true;
+
+ DOMAIN = optionalString (cfg.domain != null) "http://${cfg.domain}";
+
+ SIGNUPS_ALLOWED = false;
+ INVITATIONS_ALLOWED = true;
+
+ ROCKET_ADDRESS = "::1";
+ ROCKET_PORT = 8812;
+
+ WEBSOCKET_ENABLED = true;
+ WEBSOCKET_ADDRESS = "::1";
+ WEBSOCKET_PORT = 8813;
+
+ LOG_LEVEL = "error";
+
+ DATABASE_URL = "postgresql://${db}@/${db}";
+ };
+ dbBackend = "postgresql";
+ environmentFile = config.secrets.vaultwarden-environment.path;
+ };
+
+ postgresql = {
+ ensureDatabases = [db];
+ ensureUsers = [
+ {
+ name = db;
+ ensurePermissions."DATABASE \"${db}\"" = "ALL PRIVILEGES";
+ }
+ ];
+ };
+
+ fail2ban.jails = mkIf config.nixfiles.modules.fail2ban.enable {
+ vaultwarden = ''
+ enabled = true
+ filter = vaultwarden
+ port = http,https
+ '';
+ vaultwarden-admin = ''
+ enabled = true
+ filter = vaultwarden-admin
+ port = http,https
+ '';
+ };
+ };
+
+ environment.etc = mkIf config.nixfiles.modules.fail2ban.enable {
+ "fail2ban/filter.d/vaultwarden.conf".text = ''
+ [Definition]
+ failregex = ^.*Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$
+ ignoreregex =
+ journalmatch = _SYSTEMD_UNIT=vaultwarden.service
+ '';
+ "fail2ban/filter.d/vaultwarden-admin.conf".text = ''
+ [Definition]
+ failregex = ^.*Invalid admin token\. IP: <ADDR>.*$
+ ignoreregex =
+ journalmatch = _SYSTEMD_UNIT=vaultwarden.service
+ '';
+ };
+ };
+}
diff --git a/modules/nixfiles/vim/default.nix b/modules/nixfiles/vim/default.nix
new file mode 100644
index 0000000..cef4423
--- /dev/null
+++ b/modules/nixfiles/vim/default.nix
@@ -0,0 +1,56 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.vim;
+in {
+ options.nixfiles.modules.vim.enable =
+ mkEnableOption "Whether to enable Vim text editor.";
+
+ config = mkIf cfg.enable {
+ programs.vim.package = with pkgs;
+ ((vim_configurable.override {features = "normal";}
+ // listToAttrs
+ (map (name: nameValuePair name false) [
+ "cscopeSupport"
+ "darwinSupport"
+ "guiSupport"
+ "luaSupport"
+ "multibyteSupport"
+ "netbeansSupport"
+ "nlsSupport"
+ "perlSupport"
+ "pythonSupport"
+ "rubySupport"
+ "tclSupport"
+ "ximSupport"
+ ]))
+ .overrideAttrs (_: super: {
+ configureFlags =
+ super.configureFlags
+ ++ ["--enable-gpm=no" "--enable-gui=no"];
+ }))
+ .customize {
+ name = "vim";
+ vimrcConfig = {
+ customRC = readFile ./rc.vim;
+ packages.myVimPackage.start = with vimPlugins; [
+ editorconfig-vim
+ vim-eunuch
+ vim-nix
+ vim-sleuth
+ vim-surround
+ vim-unimpaired
+ ];
+ };
+ };
+
+ environment = {
+ systemPackages = [config.programs.vim.package];
+ variables.EDITOR = mkOverride 100 "vim";
+ };
+ };
+}
diff --git a/modules/nixfiles/vim/rc.vim b/modules/nixfiles/vim/rc.vim
new file mode 100644
index 0000000..c53b2d2
--- /dev/null
+++ b/modules/nixfiles/vim/rc.vim
@@ -0,0 +1,252 @@
+set nocompatible
+
+let $VIMFILES = expand('<sfile>:p:h')
+
+let g:skip_defaults_vim = 1
+
+let g:netrw_dirhistmax = 0
+
+set autoread
+set backspace=indent,eol,start
+set clipboard=unnamed,unnamedplus
+set diffopt+=iwhite
+set hidden
+set history=256
+set lazyredraw
+set mouse=
+set path+=**
+set tabpagemax=50
+set viminfo=
+
+set cmdheight=1
+set display+=lastline
+set fillchars=vert:\ "
+set laststatus=2
+set modeline
+set noshowmode
+set ruler
+set shortmess+=I
+set textwidth=0
+set title
+
+set sessionoptions-=options
+set viewoptions-=options
+
+set noerrorbells
+set novisualbell
+
+set splitbelow
+set splitright
+
+set complete=
+set complete+=.
+set complete+=b
+set complete+=t
+set completeopt=
+set completeopt+=menu
+set completeopt+=longest
+
+set gdefault
+set hlsearch
+set incsearch
+set iskeyword+=-
+set magic
+
+set foldmethod=marker
+set nofoldenable
+
+set shortmess=
+set shortmess+=I
+set shortmess+=T
+set shortmess+=a
+set shortmess+=c
+set shortmess+=t
+
+set nolist
+set nowrap
+
+set scrolloff=10
+set sidescrolloff=10
+
+set number
+if v:version >= 700
+ set numberwidth=3
+endif
+
+set wildignorecase
+set wildmenu
+set wildignore=
+
+set nobackup
+set noswapfile
+set noundofile
+set nowritebackup
+
+set smartcase
+set ignorecase
+
+set autoindent
+set breakindent
+set smartindent
+
+set expandtab
+set shiftround
+set shiftwidth=4
+set smarttab
+set softtabstop=4
+set tabstop=4
+
+autocmd BufEnter *.* :set colorcolumn=
+
+if &t_Co == 8 && $TERM !~# '^Eterm'
+ set t_Co=16
+endif
+
+if &listchars ==# 'eol:$'
+ set listchars=tab:>\ ,trail:-,extends:>,precedes:<,nbsp:+
+endif
+
+if v:version > 703 || v:version == 703 && has("patch541")
+ set formatoptions+=j
+endif
+
+if has('path_extra')
+ setglobal tags-=./tags tags-=./tags; tags^=./tags;
+endif
+
+if !has('nvim') && &ttimeoutlen == -1
+ set ttimeout
+ set ttimeoutlen=100
+endif
+
+try
+ set encoding=utf-8
+ scriptencoding utf-8
+catch
+endtry
+
+try
+ if &fileencodings !~? "utf-8"
+ let g:added_fenc_utf8 = 1
+ set fileencodings+=utf-8
+ endif
+catch
+endtry
+
+if has('autocmd')
+ filetype plugin indent on
+
+ if exists("+omnifunc")
+ autocmd Filetype *
+ \ if &omnifunc == "" |
+ \ setlocal omnifunc=syntaxcomplete#Complete |
+ \ endif
+ endif
+
+ autocmd BufEnter * set noreadonly
+endif
+
+if has('syntax') && !exists('g:syntax_on')
+ syntax enable
+endif
+
+if !exists('g:loaded_matchit') && findfile('plugin/matchit.vim', &rtp) ==# ''
+ runtime! macros/matchit.vim
+endif
+
+nnoremap <SPACE> <nop>
+let mapleader=" "
+
+nnoremap :W :w
+nnoremap :W! :w!
+nnoremap :Q :q
+nnoremap :Q! :q!
+
+nnoremap <Expr> j v:count ? 'j' : 'gj'
+nnoremap <Expr> k v:count ? 'k' : 'gk'
+
+nnoremap J gt
+nnoremap K gT
+
+nnoremap <C-a> ^h
+vnoremap <C-a> ^h
+nnoremap H ^h
+vnoremap H ^h
+
+nnoremap <C-e> $
+vnoremap <C-e> $
+nnoremap L $
+vnoremap L $
+
+nnoremap N Nzzzv
+nnoremap n nzzzv
+
+inoremap <C-u> <C-g>u<C-u>
+inoremap <C-w> <C-g>u<C-w>
+
+vnoremap < <gv
+vnoremap > >gv
+vnoremap <Tab> >gv
+vnoremap <S-Tab> <gv
+nnoremap <Tab> >>_
+nnoremap <S-Tab> <<_
+
+nnoremap ]b :<C-u>bnext<CR>
+nnoremap [b :<C-u>bprevious<CR>
+
+nnoremap <C-h> <C-w>h
+nnoremap <C-j> <C-w>j
+nnoremap <C-k> <C-w>k
+nnoremap <C-l> <C-w>l
+
+nnoremap * /\<<C-r>=expand('<cword>')<CR>\><CR>
+nnoremap # ?\<<C-r>=expand('<cword>')<CR>\><CR>
+
+nnoremap <C-L> :<C-u>nohlsearch<C-r>=has('diff')?'<Bar>diffupdate':''<CR><CR><C-l>
+
+cnoremap ;/ <C-r>=expand('%:p:h').'/'<CR>
+cnoremap ;; <C-r>=expand('%:t')<CR>
+cnoremap ;. <C-r>=expand('%:p:r')<CR>
+
+nnoremap <Leader>. :<C-u>lcd %:p:h<CR>
+
+nnoremap Q @q
+
+nnoremap <Leader>c ^v$h
+nnoremap <Leader>v ggVG
+
+nnoremap <Leader>y "+y
+nnoremap <Leader>Y "+Y
+
+nnoremap <Leader>p "+p
+nnoremap <Leader>P "+P
+
+inoremap <C-v> <C-c>"+pi
+cnoremap <C-v> <C-r>+
+
+nmap <Leader>w :<C-u>w!<CR>
+nmap <Leader>wq :<C-u>wq!<CR>
+
+nnoremap ZX :<C-u>qa!<CR>
+
+nnoremap <Leader>q :<C-u>q<CR>
+
+command WS w !sudo tee "%" >/dev/null
+
+function! SwitchCase()
+ normal! ~
+ if strlen(getline('.')) != virtcol('.')
+ normal! h
+ endif
+endfunction
+nnoremap ~ :<C-u>call SwitchCase()<CR>
+
+function! s:GM()
+ execute 'normal! ^'
+ let first_col = virtcol('.')
+ execute 'normal! g_'
+ let last_col = virtcol('.')
+ execute 'normal! ' . (first_col + last_col) / 2 . '|'
+endfunction
+nnoremap gm :<C-u>call <SID>GM()<CR>
+onoremap gm :<C-u>call <SID>GM()<CR>
diff --git a/modules/nixfiles/vscode.nix b/modules/nixfiles/vscode.nix
new file mode 100644
index 0000000..b41d037
--- /dev/null
+++ b/modules/nixfiles/vscode.nix
@@ -0,0 +1,172 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.vscode;
+in {
+ options.nixfiles.modules.vscode = {
+ enable = mkEnableOption "Whether to enable VSCode.";
+
+ package = with pkgs;
+ mkOption {
+ type = types.enum [vscodium vscode vscode-fhs];
+ default = vscodium;
+ description = "Which package to use as a VSCode implementation.";
+ };
+
+ vim.enable = mkOption {
+ type = types.bool;
+ default = true;
+ description = "Whether to enable Vim emulation.";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ hm.programs.vscode = with config.nixfiles;
+ with modules;
+ with profiles; {
+ enable = true;
+
+ inherit (cfg) package;
+
+ extensions = with pkgs;
+ with vscode-extensions;
+ [editorconfig.editorconfig file-icons.file-icons redhat.vscode-yaml]
+ ++ optional cfg.vim.enable vscodevim.vim
+ ++ vscode-utils.extensionsFromVscodeMarketplace [
+ {
+ name = "vscode-xml";
+ publisher = "redhat";
+ version = "0.20.0";
+ sha256 = "sha256-GKBrf9s8n7Wv14RSfwyDma1dM0fGMvRkU/7v2DAcB9A=";
+ }
+ ];
+
+ userSettings = let
+ font = config.fontScheme.monospaceFont;
+ fontFamily = font.family;
+ fontSize = font.size;
+ in {
+ editor =
+ {
+ inherit fontFamily fontSize;
+ inlayHints = {inherit fontFamily fontSize;};
+ codeLens = false;
+ cursorStyle = "block";
+ detectIndentation = true;
+ minimap.enabled = false;
+ renderWhitespace = "trailing";
+ rulers = [80 120];
+ smoothScrolling = false;
+ tabCompletion = true;
+ }
+ // (let
+ surround = 10;
+ in {
+ cursorSurroundingLines = surround;
+ scrollBeyondLastColumn = surround;
+ });
+
+ keyboard.dispatch = "keyCode";
+
+ diffEditor.codeLens = false;
+
+ files = {
+ autoSave = "off";
+ enableTrash = false;
+ };
+
+ workbench = {
+ activityBar.visible = false;
+ editor.highlightModifiedTabs = true;
+ enableExperiments = false;
+ settings.enableNaturalLanguageSearch = false;
+ startupEditor = "none";
+ tips.enabled = false;
+ tree.indent = 4;
+ welcomePage = {
+ walkthroughs.openOnInstall = false;
+ preferReducedMotion = true;
+ };
+ };
+
+ debug.console = {inherit fontFamily fontSize;};
+
+ scm = {
+ inputFontFamily = fontFamily;
+ inputFontSize = fontSize;
+ };
+
+ extensions = {
+ autoCheckUpdates = false;
+ autoUpdate = false;
+ ignoreRecommendations = true;
+ };
+
+ terminal = {
+ external.linuxExec =
+ if alacritty.enable
+ then "${pkgs.alacritty}/bin/alacritty"
+ else "${pkgs.xterm}/bin/xterm}";
+
+ integrated = {
+ inherit fontFamily fontSize;
+ enableBell = true;
+ };
+ };
+
+ update = {
+ mode = "none";
+ showReleaseNotes = false;
+ };
+
+ telemetry = {
+ enableCrashReporter = false;
+ enableTelemetry = false;
+ };
+
+ security.workspace.trust.enabled = false;
+
+ git.allowForcePush = true;
+
+ vim = let
+ applyInputMethod = {
+ "ibus" = let
+ bin = "${pkgs.ibus}/bin/ibus";
+ in {
+ enable = true;
+ defaultIM = "xkb:us::eng";
+ obtainIMCmd = "${bin} engine";
+ switchIMCmd = "${bin} engine {im}";
+ };
+ "fcitx" = let
+ bin = "${pkgs.fcitx}/bin/fcitx-remote";
+ in {
+ enable = true;
+ defaultIM = "1";
+ obtainIMCmd = bin;
+ switchIMCmd = "${bin} -t {im}";
+ };
+ };
+ in
+ mkIf cfg.vim.enable rec {
+ easymotion = true;
+ easymotionMarkerFontFamily = fontFamily;
+ easymotionMarkerFontSize = fontSize;
+
+ leader = " ";
+
+ useSystemClipboard = true;
+
+ autoSwitchInputMethod = let
+ inputMethod = config.i18n.inputMethod.enabled;
+ in
+ mkIf (inputMethod != null) applyInputMethod.${inputMethod};
+ };
+ };
+ };
+ };
+}
diff --git a/modules/nixfiles/wget.nix b/modules/nixfiles/wget.nix
new file mode 100644
index 0000000..9e4db27
--- /dev/null
+++ b/modules/nixfiles/wget.nix
@@ -0,0 +1,36 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.wget;
+in {
+ options.nixfiles.modules.wget.enable =
+ mkEnableOption "Whether to enable wget.";
+
+ config = mkIf cfg.enable {
+ hm = {
+ home.file.".wgetrc".text = ''
+ adjust_extension = on
+ dirstruct = off
+ follow_ftp = on
+ passive_ftp = off
+ progress = bar
+ quota = inf
+ reclevel = 5
+ recursive = off
+ robots = off
+ timestamping = off
+ tries = 5
+ wait = 0
+ waitretry = 10
+ '';
+
+ programs.bash.shellAliases.wget = "${pkgs.wget}/bin/wget --hsts-file=${config.hm.xdg.cacheHome}/wget-hsts";
+ };
+
+ environment.systemPackages = with pkgs; [wget];
+ };
+}
diff --git a/modules/nixfiles/wireguard.nix b/modules/nixfiles/wireguard.nix
new file mode 100644
index 0000000..1da3e74
--- /dev/null
+++ b/modules/nixfiles/wireguard.nix
@@ -0,0 +1,208 @@
+{
+ config,
+ lib,
+ pkgs,
+ this,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.wireguard;
+in {
+ options.nixfiles.modules.wireguard = {
+ # TODO Make this simpler.
+ privateKeyFile = mkOption {
+ description = "Path to the private key file.";
+ type = with types; nullOr string;
+ default = null;
+ };
+
+ client = {
+ enable = mkEnableOption "Whether to enable WireGuard client.";
+
+ enableTrafficRouting = mkOption {
+ description = "Whether to enable traffic routing through the sever.";
+ type = with types; bool;
+ default = !this.isHeadless;
+ };
+ };
+
+ server = {
+ enable = mkEnableOption ''
+ Whether to enable WireGuard server. This module requires
+ <option>networking.nat.externalInterface</option> to be set.
+ '';
+
+ ipv4.address = mkOption {
+ description = "IPv4 address to bind to.";
+ type = with types; str;
+ default = my.configurations.manwe.wireguard.ipv4.address;
+ };
+
+ ipv6.address = mkOption {
+ description = "IPv4 address to bind to.";
+ type = with types; str;
+ default = my.configurations.manwe.wireguard.ipv6.address;
+ };
+
+ address = mkOption {
+ description = "Endpoint address to use";
+ type = with types; str;
+ default = my.configurations.manwe.ipv4.address;
+ };
+
+ port = mkOption {
+ description = "Endpoint port to use.";
+ type = with types; int;
+ default = 6969;
+ };
+
+ publicKey = mkOption {
+ description = "Server's public key.";
+ type = with types; str;
+ default = my.configurations.manwe.wireguard.publicKey;
+ };
+
+ peers = mkOption {
+ description = "List of peers.";
+ type = with types; listOf attrs;
+ default = mapAttrsToList (_: attr:
+ with attr; {
+ inherit (wireguard) publicKey;
+ allowedIPs = with wireguard; [
+ "${ipv4.address}/32"
+ "${ipv6.address}/128"
+ ];
+ }) (filterAttrs (_: attr:
+ attr.hostname != this.hostname && hasAttr "wireguard" attr)
+ my.configurations);
+ };
+ };
+
+ interface = mkOption {
+ description = "Name of the interface to use WireGuard with.";
+ type = with types; str;
+ default = "wg69";
+ };
+
+ ipv4.subnet = mkOption {
+ description = "CIDR notation for the IPv4 subnet to use over WireGuard.";
+ type = with types; str;
+ default = "10.69.0.0/16";
+ };
+
+ ipv6.subnet = mkOption {
+ description = "CIDR notation for the IPv6 subnet to use over WireGuard.";
+ type = with types; str;
+ default = "fd69::/16";
+ };
+ };
+
+ config =
+ {
+ assertions = [
+ {
+ assertion = cfg.privateKeyFile != null;
+ message = "Key file must be specified.";
+ }
+ {
+ assertion = config.security.sudo.enable;
+ message = "Sudo is not enabled.";
+ }
+ {
+ assertion = any (x: x == "wheel") config.my.extraGroups;
+ message = ''User is not in the "wheel" group.'';
+ }
+ ];
+ }
+ // mkMerge [
+ (mkIf (cfg.client.enable || cfg.server.enable) {
+ networking.firewall.trustedInterfaces = [cfg.interface];
+ })
+ (mkIf cfg.client.enable {
+ networking.wg-quick.interfaces.${cfg.interface} = mkMerge [
+ (with this.wireguard; {
+ inherit (cfg) privateKeyFile;
+ address = ["${ipv4.address}/16" "${ipv6.address}/16"];
+ })
+ (with cfg.server; {
+ peers = [
+ {
+ inherit publicKey;
+ endpoint = "${address}:${toString port}";
+ allowedIPs =
+ if cfg.client.enableTrafficRouting
+ then [
+ "0.0.0.0/0"
+ "::/0"
+ ]
+ else [
+ cfg.ipv4.subnet
+ cfg.ipv6.subnet
+ ];
+ persistentKeepalive = 25;
+ }
+ ];
+ dns = [
+ ipv4.address
+ ipv6.address
+ ]; # This assumes that the host has Unbound running.
+ })
+ ];
+
+ environment.systemPackages = with pkgs; [
+ (writeShellApplication {
+ name = "wg-toggle";
+ runtimeInputs = [iproute2 jq wireguard-tools];
+ text = ''
+ ip46() {
+ sudo ip -4 "$@"
+ sudo ip -6 "$@"
+ }
+
+ fwmark=$(sudo wg show ${cfg.interface} fwmark) || exit
+ if ip -j rule list lookup "$fwmark" | jq -e 'length > 0' >/dev/null; then
+ ip46 rule del lookup main suppress_prefixlength 0
+ ip46 rule del lookup "$fwmark"
+ else
+ ip46 rule add not fwmark "$fwmark" lookup "$fwmark"
+ ip46 rule add lookup main suppress_prefixlength 0
+ fi
+ '';
+ })
+ ];
+ })
+ (mkIf cfg.server.enable {
+ networking = {
+ wireguard = {
+ enable = true;
+ interfaces.${cfg.interface} = with cfg.server; {
+ inherit (cfg) privateKeyFile;
+ ips = ["${ipv4.address}/16" "${ipv6.address}/16"];
+ listenPort = port;
+ inherit peers;
+ allowedIPsAsRoutes = false;
+ };
+ };
+
+ nat = {
+ enable = true;
+ enableIPv6 = true;
+
+ internalInterfaces = [cfg.interface];
+ internalIPs = [cfg.ipv4.subnet];
+ internalIPv6s = [cfg.ipv6.subnet];
+ };
+
+ firewall.allowedUDPPorts = [cfg.server.port];
+ };
+
+ # # TODO Dashboard for this.
+ # services.prometheus.exporters.wireguard = {
+ # enable = true;
+ # listenAddress = mkDefault this.wireguard.ipv4.address;
+ # withRemoteIp = true;
+ # port = 9586;
+ # };
+ })
+ ];
+}
diff --git a/modules/nixfiles/x11.nix b/modules/nixfiles/x11.nix
new file mode 100644
index 0000000..ff0feaf
--- /dev/null
+++ b/modules/nixfiles/x11.nix
@@ -0,0 +1,110 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.x11;
+in {
+ options.nixfiles.modules.x11.enable = mkEnableOption "Whether to enable X11.";
+
+ config = mkIf cfg.enable {
+ nixfiles.modules.fonts.enable = true;
+
+ hm = {
+ home = {
+ packages = with pkgs;
+ [xclip xdotool]
+ ++ (with xorg; [
+ xdpyinfo
+ xdriinfo
+ xev
+ xfontsel
+ xkill
+ xlsatoms
+ xlsclients
+ xlsfonts
+ xprop
+ xrandr
+ xwininfo
+ ]);
+
+ sessionVariables = with config.dirs; {
+ XCOMPOSEFILE = "${cache}/XComposeFile";
+ XCOMPOSECACHE = "${cache}/XComposeCache";
+ };
+ };
+
+ xsession.scriptPath = ".xinitrc";
+
+ xresources.properties = with config.nixfiles.modules; let
+ font = with config.fontScheme.monospaceFont; "${family}:style=${style}:size=${toString size}";
+ in
+ {
+ "*.font" = font;
+
+ "Xft.antialias" = 1;
+ "Xft.autohint" = 0;
+ "Xft.dpi" = 96;
+ "Xft.hinting" = 1;
+ "Xft.hintstyle" = "hintslight";
+ "Xft.lcdfilter" = "lcddefault";
+ "Xft.rgba" = "rgb";
+ }
+ // (with profiles.common.colourScheme; {
+ "*.color0" = black;
+ "*.color8" = brightBlack;
+ "*.color1" = red;
+ "*.color9" = brightRed;
+ "*.color2" = green;
+ "*.color10" = brightGreen;
+ "*.color3" = yellow;
+ "*.color11" = brightYellow;
+ "*.color4" = blue;
+ "*.color12" = brightBlue;
+ "*.color5" = magenta;
+ "*.color13" = brightMagenta;
+ "*.color6" = cyan;
+ "*.color14" = brightCyan;
+ "*.color7" = white;
+ "*.color15" = brightWhite;
+
+ "*.background" = background;
+ "*.foreground" = foreground;
+ });
+ };
+
+ services.xserver = {
+ enable = true;
+
+ tty = mkDefault 1;
+
+ autoRepeatDelay = 200;
+ autoRepeatInterval = 25;
+
+ libinput.enable = true;
+
+ monitorSection = ''
+ Option "DPMS" "false"
+ '';
+
+ serverFlagsSection = ''
+ Option "BlankTime" "0"
+ Option "OffTime" "0"
+ Option "StandbyTime" "0"
+ Option "SuspendTime" "0"
+ '';
+
+ inputClassSections = [
+ ''
+ Identifier "Mouse"
+ MatchIsPointer "yes"
+ Option "AccelerationNumerator" "2"
+ Option "AccelerationDenominator" "1"
+ Option "AccelerationThreshold" "4"
+ ''
+ ];
+ };
+ };
+}
diff --git a/modules/nixfiles/xmonad.nix b/modules/nixfiles/xmonad.nix
new file mode 100644
index 0000000..566ec53
--- /dev/null
+++ b/modules/nixfiles/xmonad.nix
@@ -0,0 +1,28 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.xmonad;
+in {
+ options.nixfiles.modules.xmonad.enable =
+ mkEnableOption "Whether to enable XMonad.";
+
+ config = mkIf cfg.enable {
+ nixfiles.modules.x11.enable = true;
+
+ hm = {
+ xsession = {
+ enable = true;
+
+ scriptPath = ".xinitrc";
+
+ windowManager.command = "${pkgs.xmonad-ng}/bin/xmonad-ng";
+ };
+ };
+
+ services.xserver.displayManager.startx.enable = true;
+ };
+}
diff --git a/modules/nixfiles/zathura.nix b/modules/nixfiles/zathura.nix
new file mode 100644
index 0000000..d4e11e6
--- /dev/null
+++ b/modules/nixfiles/zathura.nix
@@ -0,0 +1,123 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.nixfiles.modules.zathura;
+in {
+ options.nixfiles.modules.zathura.enable =
+ mkEnableOption "Whether to enable zathura PDF reader.";
+
+ config = mkIf cfg.enable {
+ hm.programs.zathura = with config.nixfiles.modules; {
+ enable = true;
+
+ options =
+ (with profiles.common.colourScheme; {
+ default-fg = white;
+ default-bg = black;
+
+ statusbar-fg = black;
+ statusbar-bg = white;
+
+ inputbar-fg = black;
+ inputbar-bg = brightGreen;
+
+ notification-fg = black;
+ notification-bg = brightBlue;
+
+ notification-warning-fg = black;
+ notification-warning-bg = brightYellow;
+
+ notification-error-fg = black;
+ notification-error-bg = brightRed;
+
+ highlight-color = brightYellow;
+ highlight-active-color = yellow;
+
+ completion-fg = brightWhite;
+ completion-bg = brightBlack;
+
+ completion-highlight-fg = black;
+ completion-highlight-bg = brightRed;
+
+ completion-group-fg = black;
+ completion-group-bg = brightRed;
+
+ recolor-darkcolor = black;
+ recolor-lightcolor = white;
+ })
+ // {
+ recolor = true;
+ recolor-keephue = false;
+ recolor-reverse-video = false;
+
+ highlight-transparency = "0.3";
+
+ font = fonts.fontScheme.monospaceFont.family;
+
+ n-completion-items = 10;
+
+ guioptions = "";
+
+ statusbar-basename = true;
+ statusbar-home-tilde = true;
+
+ statusbar-h-padding = 0;
+ statusbar-v-padding = 0;
+
+ window-height = 800;
+ window-width = 600;
+
+ window-icon = "";
+
+ abort-clear-search = true;
+
+ incremental-search = true;
+
+ adjust-open = "best-fit";
+
+ advance-pages-per-row = false;
+
+ database = "sqlite";
+
+ dbus-service = false;
+
+ page-padding = 0;
+
+ pages-per-row = 1;
+
+ render-loading = false;
+
+ show-directories = true;
+ show-hidden = true;
+ show-recent = 10;
+
+ link-zoom = true;
+ link-hadjust = true;
+
+ window-title-basename = true;
+ window-title-home-tilde = true;
+ window-title-page = true;
+
+ zoom-center = false;
+ zoom-max = 1000;
+ zoom-min = 10;
+ zoom-step = 10;
+
+ scroll-hstep = -1;
+ scroll-step = 40;
+ scroll-full-overlap = 0;
+ scroll-wrap = true;
+ scroll-page-aware = false;
+
+ selection-clipboard =
+ if (kde.enable || gnome.enable)
+ then "clipboard"
+ else "primary";
+ selection-notification = false;
+ };
+ };
+ };
+}
diff --git a/packages/bruh.nix b/packages/bruh.nix
new file mode 100644
index 0000000..a555fcc
--- /dev/null
+++ b/packages/bruh.nix
@@ -0,0 +1,17 @@
+{
+ fetchFromGitHub,
+ stdenv,
+}:
+stdenv.mkDerivation rec {
+ pname = "bruh";
+ version = "2.1";
+
+ src = fetchFromGitHub {
+ owner = "kejpies";
+ repo = pname;
+ rev = version;
+ hash = "sha256-Uw6Qes0IZkkfBchFnvnX9l1ZG5T5pyExmV7yUJLPOJ0=";
+ };
+
+ makeFlags = ["PREFIX=$(out)"];
+}
diff --git a/packages/mpv-autosub.nix b/packages/mpv-autosub.nix
new file mode 100644
index 0000000..cebab76
--- /dev/null
+++ b/packages/mpv-autosub.nix
@@ -0,0 +1,28 @@
+{
+ fetchFromGitHub,
+ python3Packages,
+ stdenvNoCC,
+}:
+stdenvNoCC.mkDerivation rec {
+ pname = "mpv-autosub";
+ version = "unstable-";
+
+ src = fetchFromGitHub {
+ owner = "davidde";
+ repo = pname;
+ rev = "35115355bd339681f97d067538356c29e5b14afa";
+ hash = "sha256-BKT/Tzwl5ZA4fbdc/cxz0+CYc1zyY/KOXc58x5GYow0=";
+ };
+
+ postPatch = ''
+ substituteInPlace autosub.lua \
+ --replace '/home/david/.local/bin/subliminal' \
+ '${python3Packages.subliminal}/bin/subliminal'
+ '';
+
+ installPhase = ''
+ install -Dm644 autosub.lua $out/share/mpv/scripts/autosub.lua
+ '';
+
+ passthru.scriptName = "autosub.lua";
+}
diff --git a/packages/myip.nix b/packages/myip.nix
new file mode 100644
index 0000000..a830139
--- /dev/null
+++ b/packages/myip.nix
@@ -0,0 +1,12 @@
+{
+ dnsutils,
+ writeShellApplication,
+}:
+writeShellApplication {
+ name = "myip";
+ runtimeInputs = [dnsutils];
+ text = ''
+ dig -4 +short @resolver1.opendns.com myip.opendns.com A
+ dig -6 +short @resolver1.opendns.com myip.opendns.com AAAA
+ '';
+}
diff --git a/packages/nixfiles.nix b/packages/nixfiles.nix
new file mode 100644
index 0000000..90a5865
--- /dev/null
+++ b/packages/nixfiles.nix
@@ -0,0 +1,82 @@
+{
+ git,
+ jq,
+ lib,
+ nix,
+ nixfilesSrc ? "$HOME/src/nixfiles",
+ openssh,
+ writeShellApplication,
+}:
+writeShellApplication {
+ name = "nixfiles";
+ runtimeInputs = [git jq nix openssh];
+ text = ''
+ nixfiles=${lib.escapeShellArg nixfilesSrc}
+ cmd="$1"
+ shift
+ case $cmd in
+ update)
+ if (( $# )); then
+ args=()
+ for input do args+=(--update-input "$input"); done
+ exec nix flake lock "$nixfiles" "''${args[@]}"
+ else
+ exec nix flake update -v "$nixfiles"
+ fi
+ ;;
+ rev)
+ nix flake metadata --json nixfiles |
+ if (( $# )); then
+ jq -r --arg input "$1" '.locks.nodes[$input].locked.rev'
+ else
+ jq -r '.revision // "dirty"'
+ fi
+ ;;
+ repl|eval|build)
+ args=()
+ for arg do case $arg in
+ -w|--wip) args+=(--override-flake config "$nixfiles")
+ ;;
+ *) args+=("$arg")
+ esac done
+ set -- "''${args[@]}"
+ ;;&
+ repl)
+ exec nix repl ~/.nix-defexpr "$@"
+ ;;
+ eval)
+ exec nix eval -f ~/.nix-defexpr --json "$@" | jq -r .
+ ;;
+ build)
+ exec nix build -f ~/.nix-defexpr --json "$@" | jq -r .
+ ;;
+ home)
+ attr="nixosConfigurations.$HOSTNAME.config.hm.home.activationPackage"
+ export VERBOSE=1
+ exec nix shell --verbose "$nixfiles#$attr" "$@" --command home-manager-generation
+ ;;
+ specialise)
+ name=$1
+ shift
+ exec sudo /run/current-system/specialisation/"$name"/bin/switch-to-configuration switch
+ ;;
+ revert)
+ exec sudo /nix/var/nix/profiles/system/bin/switch-to-configuration switch
+ ;;
+ test-headless)
+ exec nixos-rebuild build-vm --verbose --print-build-logs --flake "$nixfiles#test-headless" "$@"
+ ;;
+ test-headful)
+ exec nixos-rebuild build-vm --verbose --print-build-logs --flake "$nixfiles#test-headful" "$@"
+ ;;
+ @*)
+ host="''${cmd#@}"
+ hostname="$(ssh -q "$host" 'echo "$HOSTNAME"')"
+ exec nixos-rebuild -v --flake "$nixfiles#$hostname" --target-host "$host" --use-remote-sudo "$@"
+ ;;
+ *)
+ exec nixos-rebuild -v --flake "$nixfiles" --use-remote-sudo "$cmd" "$@"
+ ;;
+ esac
+ '';
+}
diff --git a/packages/throttled.nix b/packages/throttled.nix
new file mode 100644
index 0000000..ff896cf
--- /dev/null
+++ b/packages/throttled.nix
@@ -0,0 +1,36 @@
+{
+ fetchFromGitHub,
+ kmod,
+ python3,
+ stdenv,
+}:
+stdenv.mkDerivation rec {
+ pname = "throttled";
+ version = "unstable-2022-06-30";
+
+ src = fetchFromGitHub {
+ owner = "erpalma";
+ repo = pname;
+ rev = "ab9641a93a409bdc7fca6889ff23ca5a685e5a77";
+ hash = "sha256-Zd+rTsVDkw9mq6k2OkMy+HxyYsl3ADy1fEM/1e/jxAs=";
+ };
+
+ nativeBuildInputs = with python3.pkgs; [wrapPython];
+
+ pythonPath =
+ (with python3.pkgs; [configparser dbus-python pygobject3])
+ ++ [kmod];
+
+ installPhase = ''
+ runHook preInstall
+
+ install -Dm755 -t $out/opt/throttled throttled.py
+ install -Dm644 -t $out/opt/throttled mmio.py
+
+ runHook postInstall
+ '';
+
+ postFixup = ''
+ wrapPythonProgramsIn $out/opt/throttled "$out $pythonPath"
+ '';
+}
diff --git a/packages/ultimmc.nix b/packages/ultimmc.nix
new file mode 100644
index 0000000..e90d5bd
--- /dev/null
+++ b/packages/ultimmc.nix
@@ -0,0 +1,63 @@
+{
+ cmake,
+ fetchFromGitHub,
+ jdk,
+ makeDesktopItem,
+ qtbase,
+ stdenv,
+ wrapQtAppsHook,
+ zlib,
+}:
+stdenv.mkDerivation rec {
+ pname = "UltimMC";
+ version = "unstable-2022-06-09";
+
+ src = fetchFromGitHub {
+ owner = "UltimMC";
+ repo = "Launcher";
+ rev = "b7c9b27ed6fb047da7fab5e4cee99b298b9e9811";
+ hash = "sha256-xmkflvtOSLMTuzAKovgOLjgpbR8j53bkGV3QZRsQItw=";
+ fetchSubmodules = true;
+ };
+
+ nativeBuildInputs = [cmake jdk wrapQtAppsHook];
+
+ buildInputs = [qtbase zlib];
+
+ postPatch = ''
+ substituteInPlace CMakeLists.txt \
+ --replace 'LIBRARY_DEST_DIR "bin"' 'LIBRARY_DEST_DIR "lib"' \
+ --replace 'JARS_DEST_DIR "bin/jars"' 'JARS_DEST_DIR "share/${pname}/jar"'
+
+ substituteInPlace launcher/Application.cpp \
+ --replace '"jars"' '"../share/${pname}/jar"';
+ '';
+
+ cmakeFlags = ["-DCMAKE_BUILD_TYPE=Release"];
+
+ desktopItem = makeDesktopItem {
+ name = pname;
+ exec = pname;
+ icon = pname;
+ desktopName = pname;
+ genericName = "Custom Minecraft launcher";
+ categories = ["Game"];
+ };
+
+ dontWrapQtApps = true;
+
+ postInstall = ''
+ rm $out/${pname}
+
+ wrapProgram $out/bin/${pname} \
+ --add-flags "--dir \''${XDG_DATA_HOME:-~/.local/share}/${pname}" \
+ "''${qtWrapperArgs[@]}"
+
+ install -Dm644 \
+ ${desktopItem}/share/applications/${pname}.desktop \
+ $out/share/applications/${pname}.desktop
+ install -Dm644 \
+ $src/notsecrets/logo.svg \
+ $out/share/icons/hicolor/scalable/apps/${pname}.svg
+ '';
+}
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..0f8a3bb
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,3 @@
+[tool.black]
+line-length = 79
+target-version = ['py310']
diff --git a/readme.org b/readme.org
new file mode 100644
index 0000000..a2c097a
--- /dev/null
+++ b/readme.org
@@ -0,0 +1,42 @@
+#+options: ':t *:t -:t ::t <:t H:3 \n:nil ^:t arch:headline author:t
+#+options: broken-links:nil c:nil creator:nil d:(not "LOGBOOK") date:t e:t
+#+options: email:nil f:t inline:t num:nil p:nil pri:nil prop:nil stat:t tags:t
+#+options: tasks:t tex:t timestamp:t title:t toc:t todo:t |:t
+#+title: nixfiles
+#+date: <2022-03-08 Tue>
+#+author: Azat Bahawi
+#+email: azat@bahawi.net
+#+language: en
+#+select_tags: export
+#+exclude_tags: noexport
+#+creator: Emacs 27.2 (Org mode 9.5)
+
+An IaC recipe for my digital infrastructure and configurations. An evolution of
+the "dotfiles" thingy I had going for several years.
+
+If you stumbled on this repository on GitHub, GitLab and such, the version you
+are currently looking at is a /"stripped"/ down rendition of *nixfiles* where IP
+addresses, domain names, secrets and other sensitive information was removed or
+replaced with gibberish. This is done so that you can get a general
+understanding of how stuff is made without me spilling the beans too much...
+
+If you are looking to get into Nix and NixOS, I /highly/ suggest to take this
+only as a reference and not just mindlessly copy-paste everything.
+
+* Inspiration and Credits
+
+Big thanks to everyone involved with Nix/NixOS/Nixpkgs and everything around
+these! Hopefully, the Nix ecosystem will get a lot more traction and the
+long-awaited industry-wide adoption.
+
+[[https://c.tenor.com/zJvexdmTjA4AAAAC/im-doing-my-part-serious.gif]]
+
+Special thanks for these fine people from whom I stole a lot of ideas for this
+project:
+- [[https://github.com/Mic92/dotfiles][Mic92]]
+- [[https://github.com/codygman/hci][codygman]]
+- [[https://github.com/davidtwco/veritas][davidtwco]]
+- [[https://github.com/grahamc/nixos-config][grahamc]]
+- [[https://github.com/gytis-ivaskevicius/nixfiles][gytis-ivaskevicius]]
+- [[https://github.com/hlissner/dotfiles][hlissner]]
+- [[https://github.com/ncfavier/config][ncfavier]]
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..3f15fc5
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,2 @@
+[flake8]
+ignore = F401