diff --git a/LICENSE.org b/LICENSE.org new file mode 100644 index 0000000..523f707 --- /dev/null +++ b/LICENSE.org @@ -0,0 +1,17 @@ +* Licensing +The source code you can find in various programming languages in this +repository including, but not limited to, Javascript and CSS source +code is under the [[https://www.gnu.org/licenses/agpl-3.0.html][AGPL-3.0]] licence. + +The creative work contained in [[https://labs.phundrak.com/phundrak/langue-phundrak-com][the main code repository]], on my [[https://github.com/Phundrak/langue-phundrak-fr/][Github +mirror]], and on my website [[https://langue.phundrak.com][langue.phundrak.com]] is dual-licenced +between the [[https://www.gnu.org/licenses/#FDL][GNU Free Documentation License]] ([[file:fdl-1.3.md][legal code]]) for the text +and the /Creative Commons Attribution-NonCommercial-ShareAlike 4.0 +International/ ([[https://creativecommons.org/licenses/by-nc-sa/4.0/][CC BY-NC-SA 4.0]], [[https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode][legal code]]) license. + +Copies of all the mentionned licenses can be found in this code +repository. + +If you wish to obtain a special license that is incompatible with the +current ones, please contact me at [[mailto:lucien@phundrak.com][lucien@phundrak.com]] so we can +discuss it. diff --git a/flake.lock b/flake.lock index f6140e0..e8aa16c 100644 --- a/flake.lock +++ b/flake.lock @@ -35,11 +35,11 @@ ] }, "locked": { - "lastModified": 1787985728, - "narHash": "sha256-qKYq/NmpxLKn2ZcxNUGx7PgVlmxW833xKZ3Hngvvnro=", + "lastModified": 1789186140, + "narHash": "sha256-CEKrGzjO7Zk+BC0bsIySyu7q0vdy68V5MMq8YNKPpsk=", "owner": "caelestia-dots", "repo": "cli", - "rev": "56f404c61dff30d698961780aefcf48bd4dd7b86", + "rev": "6b84ee5090ec50f36aa1a53990d57ab0013925a0", "type": "github" }, "original": { @@ -58,15 +58,16 @@ "quickshell": "quickshell" }, "locked": { - "lastModified": 1788247772, - "narHash": "sha256-KJtxaK9Ws9DimBKdrZFs3iHjUHF/V1WviJx5A2zk86c=", + "lastModified": 1789736975, + "narHash": "sha256-PVYroXR/tuQdsWjp2XAqOhgY6dV2kN5nyzH4zha6ZPs=", "owner": "caelestia-dots", "repo": "shell", - "rev": "065dac2957a1f31bd2d2f029cabb96f69921f72b", + "rev": "d999d4878ee4cec134168e60d913714566a8cfa6", "type": "github" }, "original": { "owner": "caelestia-dots", + "ref": "stable", "repo": "shell", "type": "github" } diff --git a/flake.nix b/flake.nix index b543ae4..6147dd3 100644 --- a/flake.nix +++ b/flake.nix @@ -1,7 +1,7 @@ # DO-NOT-EDIT. This file was auto-generated using github:denful/flake-file. # Use `nix run .#write-flake` to regenerate it. { - description = "NixOS and Home Manager configuration of phundrak"; + description = "NixOS and Home Manager configuration of P'undrak"; outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } (inputs.import-tree ./modules); @@ -25,7 +25,7 @@ inputs = { caelestia-shell = { - url = "github:caelestia-dots/shell"; + url = "github:caelestia-dots/shell?ref=stable"; inputs.nixpkgs.follows = "nixpkgs"; }; flake-file.url = "github:vic/flake-file"; diff --git a/modules/boot/hardened.nix b/modules/boot/hardened.nix index 0a939b6..1606f2d 100644 --- a/modules/boot/hardened.nix +++ b/modules/boot/hardened.nix @@ -1,44 +1,26 @@ { flake.modules.nixos.hardened = { - lib, - config, - ... - }: - with lib; let - cfg = config.mySystem.boot.kernel; - in { - options.mySystem.boot.kernel.hardened = mkEnableOption "Enables hardened Linux kernel"; - - config.boot = { - kernelModules = lists.optional cfg.hardened "tcp_bbr"; - kernel.sysctl = mkIf cfg.hardened { - "kernel.sysrq" = 0; # Disable magic SysRq key - # Ignore ICMP broadcasts to avoid participating in Smurf attacks - "net.ipv4.icmp_echo_ignore_broadcasts" = 1; - # Ignore bad ICMP errors - "net.ipv4.icmp_ignore_bogus_error_responses" = 1; - # SYN flood protection - "net.ipv4.tcp_syncookies" = 1; - # Do not accept ICMP redirects (prevent MITM attacks) - "net.ipv4.conf.all.accept_redirects" = 0; - "net.ipv4.conf.default_accept_redirects" = 0; - "net.ipv4.conf.all.secure_redirects" = 0; - "net.ipv4.conf.default.secure_redirects" = 0; - "net.ipv6.conf.all.accept_redirects" = 0; - "net.ipv6.conf.default.accept_redirects" = 0; - # Do not send ICMP redirects (we are not a router) - "net.ipv4.conf.all.send_redirects" = 0; - # Do not accept IP source route packets (we are not a router) - "net.ipv4.conf.all.accept_source_route" = 0; - "net.ipv6.conf.all.accept_source_route" = 0; - # Protect against tcp time-wait assassination hazards - "net.ipv4.tcp_rfc1337" = 1; - # Latency reduction - "net.ipv4.tcp_fastopen" = 3; - # Bufferfloat mitigations - "net.ipv4.tcp_congestion_control" = "bbr"; - "net.core.default_qdisc" = "cake"; - }; + boot = { + kernelModules = ["tcp_bbr"]; + kernel.sysctl = { + "kernel.sysrq" = 0; + "net.ipv4.icmp_echo_ignore_broadcasts" = 1; + "net.ipv4.icmp_ignore_bogus_error_responses" = 1; + "net.ipv4.conf.all.accept_redirects" = 0; + "net.ipv4.conf.default_accept_redirects" = 0; + "net.ipv4.conf.all.secure_redirects" = 0; + "net.ipv4.conf.default.secure_redirects" = 0; + "net.ipv6.conf.all.accept_redirects" = 0; + "net.ipv6.conf.default.accept_redirects" = 0; + "net.ipv4.conf.all.send_redirects" = 0; + "net.ipv4.conf.all.accept_source_route" = 0; + "net.ipv6.conf.all.accept_source_route" = 0; + "net.ipv4.tcp_syncookies" = 1; + "net.ipv4.tcp_rfc1337" = 1; + "net.ipv4.tcp_congestion_control" = "bbr"; + "net.core.default_qdisc" = "cake"; + "net.ipv4.tcp_fastopen" = 3; }; }; + }; } diff --git a/modules/boot/hardened.org b/modules/boot/hardened.org new file mode 100644 index 0000000..2920440 --- /dev/null +++ b/modules/boot/hardened.org @@ -0,0 +1,116 @@ +#+title: Kernel Hardening +#+setupfile: ../headers + +* Kernel Hardening +Some of my machines are exposed to the Internet, and therefore get +their kernel hardened. First, let me declare the Nix file’s skeleton, +with the =nixos.hardened= module. + +#+begin_src nix :tangle yes +{ + flake.modules.nixos.hardened = { + boot = { + <> + <> + }; + }; +} +#+end_src + +** Kernel Modules +The very first thing to do is to load the =tcp_bbr= kernel module. It +increases the connection speed of the system with a better congestion +control. It is particularly interesting for my servers, as they may +have to deal with high traffic if a crawler ever decides to explore +all webpages offered by some websites I host. See [[https://www.cyberciti.biz/cloud-computing/increase-your-linux-server-internet-speed-with-tcp-bbr-congestion-control/][this article]] by +Nixcraft for more details. +#+name: kernel-modules +#+begin_src nix +kernelModules = ["tcp_bbr"]; +#+end_src + +** Kernel Options +Next are a series of kernel options. +#+name: kernel-options +#+begin_src nix +kernel.sysctl = { + <> + <> + <> + <> + <> + <> + <> + <> + <> +}; +#+end_src + +First, we’ll disable the magic SysRq key. Not that I expect anyone to +have physical access to my servers, but it is a really powerful tool +that I’d rather have off. +#+name: sysrq-key +#+begin_src nix +"kernel.sysrq" = 0; +#+end_src + +Next, we’ll ignore ICMP broadcasts to avoid participating in Smurf +attacks, and we’ll also ignore ICMP errors. +#+name: icmp +#+begin_src nix +"net.ipv4.icmp_echo_ignore_broadcasts" = 1; +"net.ipv4.icmp_ignore_bogus_error_responses" = 1; +#+end_src + +Speaking of ICMP, we won’t accept ICMP redirects to prevent some MITM +attacks. +#+name: icmp-no-accept-redirects +#+begin_src nix +"net.ipv4.conf.all.accept_redirects" = 0; +"net.ipv4.conf.default_accept_redirects" = 0; +"net.ipv4.conf.all.secure_redirects" = 0; +"net.ipv4.conf.default.secure_redirects" = 0; +"net.ipv6.conf.all.accept_redirects" = 0; +"net.ipv6.conf.default.accept_redirects" = 0; +#+end_src + +And we won’t send ICMP redirects (we’re not a router). +#+name: icmp-no-send-redirects +#+begin_src nix +"net.ipv4.conf.all.send_redirects" = 0; +#+end_src + +We’re stil not a router, so we’ll refuse IP source route packets, both +on IPV4 and IPV6. +#+name: ip-source-route-packets +#+begin_src nix +"net.ipv4.conf.all.accept_source_route" = 0; +"net.ipv6.conf.all.accept_source_route" = 0; +#+end_src + +Now, let’s get some SYN flood protection. +#+name: syn +#+begin_src nix +"net.ipv4.tcp_syncookies" = 1; +#+end_src + +And protection against TCP time-wait assassination hazards. +#+name: tcp-time-wait +#+begin_src nix +"net.ipv4.tcp_rfc1337" = 1; +#+end_src + +We will also mitigate bufferfloat, including with BBR (hey, we enabled that above!) +#+name: bufferfloat +#+begin_src nix +"net.ipv4.tcp_congestion_control" = "bbr"; +"net.core.default_qdisc" = "cake"; +#+end_src + +And lastly, we’ll reduce latency on IPV4. +#+name: latency +#+begin_src nix +"net.ipv4.tcp_fastopen" = 3; +#+end_src + +And we should be good to go! diff --git a/modules/boot/kernel.nix b/modules/boot/kernel.nix index 2b60312..4ab5130 100644 --- a/modules/boot/kernel.nix +++ b/modules/boot/kernel.nix @@ -1,6 +1,4 @@ -{config, ...}: let - flakeModules = config.flake.modules; -in { +{ flake.modules.nixos.kernel = { pkgs, config, @@ -10,8 +8,6 @@ in { with lib; let cfg = config.mySystem.boot.kernel; in { - imports = [flakeModules.nixos.amdgpu]; - options.mySystem.boot.kernel = { package = mkOption { type = types.raw; @@ -35,13 +31,8 @@ in { ''; }; }; - config.boot = { - initrd.kernelModules = lib.lists.singleton ( - if config.mySystem.hardware.amdgpu.enable - then "amdgpu" - else "i915" - ); + initrd.kernelModules = ["i915"]; extraModprobeConfig = strings.concatLines ([cfg.extraModprobeConfig] diff --git a/modules/boot/kernel.org b/modules/boot/kernel.org new file mode 100644 index 0000000..b1f96c2 --- /dev/null +++ b/modules/boot/kernel.org @@ -0,0 +1,136 @@ +#+title: Kernel Configuration +#+setupfile: ../headers + +* Kernel Configuration +This module centralises everything related to the kernel: which +package to boot, which extra modules to load, which KVM module the +CPU needs, and a couple of modprobe quirks for specific devices. +Here’s the skeleton of the =nixos.kernel= module. +#+begin_src nix :tangle yes +{ + flake.modules.nixos.kernel = { + pkgs, + config, + lib, + ... + }: + with lib; let + cfg = config.mySystem.boot.kernel; + in { + <> + <> + }; +} +#+end_src + +** Declaring the Options +#+name: options +#+begin_src nix +options.mySystem.boot.kernel = { + <> + <> + <> + <> + <> +}; +#+end_src + +*** Kernel Package +=package= picks which kernel to boot. I default to the Zen kernel for +its desktop-tuned scheduler, but a host can override it with a +hardened or hardware-specific kernel instead. +#+name: opt-package +#+begin_src nix +package = mkOption { + type = types.raw; + default = pkgs.linuxPackages_zen; +}; +#+end_src + +*** Extra Kernel Modules +=modules= lists any extra kernel modules a host needs beyond the ones +this module already adds on its own. +#+name: opt-modules +#+begin_src nix +modules = mkOption { + type = types.listOf types.str; + default = []; +}; +#+end_src + +*** CPU Vendor +=cpuVendor= tells the module which KVM module to load, since Intel and +AMD CPUs each need their own. +#+name: opt-cpu-vendor +#+begin_src nix +cpuVendor = mkOption { + description = "Intel or AMD?"; + type = types.enum ["intel" "amd"]; + default = "amd"; +}; +#+end_src + +*** Virtual Webcam +=v4l2loopback.enable= turns on a virtual video device. I feed it a +video source, and OBS Studio picks it up as if it were a webcam. +#+name: opt-v4l2loopback +#+begin_src nix +v4l2loopback.enable = mkEnableOption "Enables v4l2loopback kernel module"; +#+end_src + +*** Extra Modprobe Configuration +=extraModprobeConfig= lets a host inject raw =modprobe.d= lines. The +example below fixes a USB sound card that otherwise misconfigures +itself on boot. +#+name: opt-extra-modprobe +#+begin_src nix +extraModprobeConfig = mkOption { + type = types.lines; + default = ""; + example = '' + options snd_usb_audio vid=0x1235 pid=0x8212 device_setup=1 + ''; +}; +#+end_src + +** Wiring It All Up +#+name: config +#+begin_src nix +config.boot = { + <> + <> + <> +}; +#+end_src + +*** Choosing the Initrd Driver +Most of my machines have Intel graphics, so this module loads =i915= +early, in the initrd, to keep the Plymouth splash screen from flashing +or glitching before the proper driver takes over. Machines with an AMD +GPU instead load =amdgpu= early; the [[file:../hardware/amdgpu.org][AMD GPU module]] handles that +itself, so this module doesn’t need to know or care which GPU a host +actually has. +#+name: initrd-driver +#+begin_src nix +initrd.kernelModules = ["i915"]; +#+end_src + +*** Assembling Modprobe Configuration +This concatenates whatever a host set through =cfg.extraModprobeConfig= +with the v4l2loopback quirk line whenever =v4l2loopback.enable= is on. +#+name: extra-modprobe-lines +#+begin_src nix +extraModprobeConfig = + strings.concatLines + ([cfg.extraModprobeConfig] + ++ lists.optional cfg.v4l2loopback.enable ''options v4l2loopback exclusive_caps=1 devices=1 video_nr=0 card_label="OBS Studio"''); +#+end_src + +*** Kernel Package and Extra Modules +Finally, =kernelPackages= and =kernelModules= wire the chosen package +and modules through, appending the vendor-specific KVM module. +#+name: kernel-packages-and-modules +#+begin_src nix +kernelPackages = cfg.package; +kernelModules = cfg.modules ++ ["kvm-${cfg.cpuVendor}"]; +#+end_src diff --git a/modules/boot/loader.nix b/modules/boot/loader.nix index eb42974..481f093 100644 --- a/modules/boot/loader.nix +++ b/modules/boot/loader.nix @@ -7,39 +7,25 @@ with lib; let cfg = config.mySystem.boot; in { - options.mySystem.boot = { - systemd-boot = mkOption { - type = types.bool; - default = !cfg.grub.enable; - description = "Does the system use systemd-boot?"; - }; - grub = { - enable = mkEnableOption "Does the system use GRUB? (Disables systemd-boot)"; - device = mkOption { - type = types.path; - description = "The GRUB device"; - default = ""; - }; - }; - zfs = { - enable = mkEnableOption "Enables ZFS"; - pools = mkOption { - type = types.listOf types.str; - default = []; - }; + options.mySystem.boot.systemd-boot = mkOption { + type = types.bool; + default = !cfg.grub.enable; + description = "Does the system use systemd-boot?"; + }; + options.mySystem.boot.grub = { + enable = mkEnableOption "Does the system use GRUB? (Disables systemd-boot)"; + device = mkOption { + type = types.path; + description = "The GRUB device"; }; }; - config.boot = { - loader = { - systemd-boot.enable = cfg.systemd-boot; - efi.canTouchEfiVariables = cfg.systemd-boot; - grub = mkIf cfg.grub.enable { - inherit (cfg.grub) enable device; - }; - }; - supportedFilesystems = mkIf cfg.zfs.enable ["zfs"]; - zfs.extraPools = mkIf cfg.zfs.enable cfg.zfs.pools; + config.boot.loader = { + systemd-boot.enable = cfg.systemd-boot; + efi.canTouchEfiVariables = cfg.systemd-boot; + }; + config.boot.loader.grub = mkIf cfg.grub.enable { + inherit (cfg.grub) enable device; }; }; } diff --git a/modules/boot/loader.org b/modules/boot/loader.org new file mode 100644 index 0000000..af0b92f --- /dev/null +++ b/modules/boot/loader.org @@ -0,0 +1,73 @@ +#+title: Bootloaders and Filesystems +#+setupfile: ../headers + +* Bootloaders and Filesystems +This page sets up the bootloader of my machines. Whilst I generally +prefer to use [[https://systemd.io/BOOT/][systemd-boot]], some of my VPS use GRUB. All that gets +exposed with my module =nixos.loader=. + +#+begin_src nix :tangle yes +{ + flake.modules.nixos.loader = { + lib, + config, + ... + }: + with lib; let + cfg = config.mySystem.boot; + in { + <> + <> + + <> + <> + }; +} +#+end_src + +By default, I want to use systemd-boot on my machines, but I need it +to be disabled whenever I use something else. For now, this “something +else” is only GRUB, but I’m not excluding using something else on yet +another machine such as [[https://www.rodsbooks.com/refind/][rEFInd]]. To ensure a single source of truth +regarding whether systemd-boot is to be used, I have an option for +that. +#+name: systemd-boot-options +#+begin_src nix +options.mySystem.boot.systemd-boot = mkOption { + type = types.bool; + default = !cfg.grub.enable; + description = "Does the system use systemd-boot?"; +}; +#+end_src + +I can now set some options depending on that, such as whether to +enable systemd-boot itself (duh), and whether the installation process +can touch my EFI variables. +#+name: systemd-boot-config +#+begin_src nix +config.boot.loader = { + systemd-boot.enable = cfg.systemd-boot; + efi.canTouchEfiVariables = cfg.systemd-boot; +}; +#+end_src + +But, I have a VPS that requires me to use GRUB. For this, I also have +an option to enable it. +#+name: grub-options +#+begin_src nix +options.mySystem.boot.grub = { + enable = mkEnableOption "Does the system use GRUB? (Disables systemd-boot)"; + device = mkOption { + type = types.path; + description = "The GRUB device"; + }; +}; +#+end_src + +I can no pass these options to the boot configuration of my machine. +#+name: grub-config +#+begin_src nix +config.boot.loader.grub = mkIf cfg.grub.enable { + inherit (cfg.grub) enable device; +}; +#+end_src diff --git a/modules/boot/plymouth.nix b/modules/boot/plymouth.nix index 21333e0..83789db 100644 --- a/modules/boot/plymouth.nix +++ b/modules/boot/plymouth.nix @@ -1,35 +1,25 @@ { - flake.modules.nixos.plymouth = { - pkgs, - lib, - config, - ... - }: - with lib; let - cfg = config.mySystem.boot.plymouth; - in { - options.mySystem.boot.plymouth.enable = mkEnableOption "Enables Plymouth at system boot"; - config.boot = mkIf cfg.enable { - plymouth = { - inherit (cfg) enable; - theme = "circle_hud"; - themePackages = with pkgs; [ - (adi1090x-plymouth-themes.override { - selected_themes = ["circle_hud"]; - }) - ]; - }; - consoleLogLevel = 3; - initrd.verbose = false; - kernelParams = [ - "quiet" - "splash" - "boot.shell_on_fail" - "udev.log_priority=3" - "rd.systemd.show_status=auto" + flake.modules.nixos.plymouth = {pkgs, ...}: { + boot = { + plymouth = { + enable = true; + theme = "circle_hud"; + themePackages = with pkgs; [ + (adi1090x-plymouth-themes.override { + selected_themes = ["circle_hud"]; + }) ]; - # Loader appears only if a key is pressed - loader.timeout = 0; }; + kernelParams = [ + "quiet" + "splash" + "boot.shell_on_fail" + "udev.log_level=3" + "rd.systemd.show_status=auto" + ]; + consoleLogLevel = 3; + initrd.verbose = false; + loader.timeout = 0; }; + }; } diff --git a/modules/boot/plymouth.org b/modules/boot/plymouth.org new file mode 100644 index 0000000..7b44047 --- /dev/null +++ b/modules/boot/plymouth.org @@ -0,0 +1,99 @@ +#+title: Plymouth +#+setupfile: ../headers + +* Plymouth +Plymouth is a utility which shows an animation at startup or when +powering off a device, instead of showing only terminal things. My +Plymouth settings are set in the =nixos.plymouth= module. + +#+begin_src nix :tangle yes +{ + flake.modules.nixos.plymouth = {pkgs, ...}: { + boot = { + <> + <> + <> + <> + }; + }; +} +#+end_src + +** Quieting Down the Console +First, I need to set a few kernel parameters to make displaying +Plymouth possible: +#+name: kernel-parameters-list +- =quiet= :: silences the logs in the terminal +- =splash= :: show the splash screen (in our case, Plymouth) +- =boot.shell_on_fail= :: sets =allowShell=1=, which permits the =fail()= + handler to drop an interactive rescue shell if stage-1 boot fails +- =udev.log_level=3= :: sets udev’s log level to =err= +- =rd.systemd.show_status=auto= :: suppress systemd status messages in + initrd unless boot is significantly delayed + +#+name: kernel-params +#+begin_src emacs-lisp :var params=kernel-parameters-list :cache yes +(mapconcat (lambda (param) + (format "\"%s\"" + (s-chop-suffix "=" (s-chop-prefix "=" (car (s-split " ::" param)))))) + params + "\n") +#+end_src + +#+RESULTS[7a824c095f2934792b4a3749e56091a8603aaacf]: kernel-params +: "quiet" +: "splash" +: "boot.shell_on_fail" +: "udev.log_level=3" +: "rd.systemd.show_status=auto" + +This translates into: +#+name: kernel-parameters +#+begin_src nix :noweb yes +kernelParams = [ + <> +]; +#+end_src + +To further decrease the verbosity of the booting screen, we can +deactivate initrd’s verbosity and lower the console’s log level to +=err=. +#+name: quiet-console +#+begin_src nix +consoleLogLevel = 3; +initrd.verbose = false; +#+end_src + +And we’ll show Plymouth immediately, skipping the bootloader’s menu. +We can hold a key to force displaying the menu, though. +#+name: no-menu +#+begin_src nix +loader.timeout = 0; +#+end_src + + +** Configuring Plymouth +Now, we can configure Plymouth proper. +#+name: plymouth +#+begin_src nix +plymouth = { + enable = true; + <> +}; +#+end_src + +The only significant configuration I want to change in Plymouth is the +animation. I really like how it looks. You can find a preview of it in +[[https://github.com/adi1090x/plymouth-themes#previews][adi1090x's repository]], under the first pack. For this, I need to set +a theme package and the theme name. +#+name: plymouth-theme +#+begin_src nix +theme = "circle_hud"; +themePackages = with pkgs; [ + (adi1090x-plymouth-themes.override { + selected_themes = ["circle_hud"]; + }) +]; +#+end_src + +And we’re done! diff --git a/modules/boot/zfs.nix b/modules/boot/zfs.nix new file mode 100644 index 0000000..3623791 --- /dev/null +++ b/modules/boot/zfs.nix @@ -0,0 +1,21 @@ +{ + flake.modules.nixos.zfs = { + lib, + config, + ... + }: + with lib; let + cfg = config.mySystem.boot; + in { + options.mySystem.boot.zfs = { + pools = mkOption { + type = types.listOf types.str; + default = []; + }; + forceImportRoot = mkEnableOption "Force-import the ZFS root pool at boot, even if it looks already imported elsewhere"; + }; + config.boot.supportedFilesystems = ["zfs"]; + config.boot.zfs.extraPools = cfg.zfs.pools; + config.boot.zfs.forceImportRoot = cfg.zfs.forceImportRoot; + }; +} diff --git a/modules/boot/zfs.org b/modules/boot/zfs.org new file mode 100644 index 0000000..92477b3 --- /dev/null +++ b/modules/boot/zfs.org @@ -0,0 +1,62 @@ +#+title: ZFS Support +#+setupfile: ../headers + +* ZFS Support +Enabling ZFS is quite straightforward on my machines. In my module +=nixos.zfs=, I expose two options: which ZFS pools to import, and +whether to force-import the root pool. But first, here’s the skeleton +of my module. + +#+begin_src nix :tangle yes +{ + flake.modules.nixos.zfs = { + lib, + config, + ... + }: + with lib; let + cfg = config.mySystem.boot; + in { + <> + <> + <> + <> + }; +} +#+end_src + +The main option is the list of pools, which I pass directly to the +standard NixOS configuration. +#+name: options +#+begin_src nix +options.mySystem.boot.zfs = { + pools = mkOption { + type = types.listOf types.str; + default = []; + }; + forceImportRoot = mkEnableOption "Force-import the ZFS root pool at boot, even if it looks already imported elsewhere"; +}; +#+end_src + +Now, I can enable ZFS on the system importing the current module. +#+name: enable +#+begin_src nix +config.boot.supportedFilesystems = ["zfs"]; +#+end_src + +And lastly, passing the list of pools to the standard NixOS option is +quite simple. +#+name: pools +#+begin_src nix +config.boot.zfs.extraPools = cfg.zfs.pools; +#+end_src + +Force-importing the root pool can paper over a stale or mismatched +host ID, but it also risks mounting a pool that’s already imported +elsewhere and corrupting it, so NixOS is moving to disable it by +default. I’d rather keep that safety and only turn it back on for the +rare host (or the rare boot) that actually needs it. +#+name: force-import-root +#+begin_src nix +config.boot.zfs.forceImportRoot = cfg.zfs.forceImportRoot; +#+end_src diff --git a/modules/default.nix b/modules/default.nix index cb041bf..507b72e 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -25,7 +25,6 @@ inputs.nixpkgsUnstable.follows = "nixpkgsPinetab2Kernel"; }; }; - systems = ["x86_64-linux" "aarch64-linux"]; flake.lib = { @@ -37,7 +36,6 @@ ]; }; }; - mkHome = system: userName: hostName: { "${userName}@${hostName}" = inputs.home-manager.lib.homeManagerConfiguration { pkgs = inputs.nixpkgs.legacyPackages.${system}; @@ -48,7 +46,6 @@ modules = [config.flake.modules.homeManager."${userName}-${hostName}"]; }; }; - mkPinetab = buildPlatform: variantModule: { pinetab2 = inputs.nixpkgs.lib.nixosSystem { system = "aarch64-linux"; @@ -72,24 +69,22 @@ }; flake.nixosConfigurations = lib.mkMerge [ - (config.flake.lib.mkNixos "x86_64-linux" "elcafe") - (config.flake.lib.mkNixos "x86_64-linux" "gampo") (config.flake.lib.mkNixos "x86_64-linux" "marpa") - (config.flake.lib.mkNixos "x86_64-linux" "NaroMk3") + (config.flake.lib.mkNixos "x86_64-linux" "gampo") (config.flake.lib.mkNixos "x86_64-linux" "tilo") + (config.flake.lib.mkNixos "x86_64-linux" "elcafe") + (config.flake.lib.mkNixos "x86_64-linux" "NaroMk3") (config.flake.lib.mkPinetab "x86_64-linux" config.flake.modules.nixos.pinetab2-gnome) ]; - flake.homeConfigurations = lib.mkMerge [ - (config.flake.lib.mkHome "x86_64-linux" "phundrak" "elcafe") - (config.flake.lib.mkHome "x86_64-linux" "phundrak" "gampo") (config.flake.lib.mkHome "x86_64-linux" "phundrak" "marpa") - (config.flake.lib.mkHome "x86_64-linux" "phundrak" "NaroMk3") - (config.flake.lib.mkHome "x86_64-linux" "phundrak" "pinetab2") + (config.flake.lib.mkHome "x86_64-linux" "phundrak" "gampo") (config.flake.lib.mkHome "x86_64-linux" "phundrak" "tilo") + (config.flake.lib.mkHome "x86_64-linux" "phundrak" "elcafe") (config.flake.lib.mkHome "x86_64-linux" "creug" "elcafe") + (config.flake.lib.mkHome "x86_64-linux" "phundrak" "NaroMk3") + (config.flake.lib.mkHome "aarch64-linux" "phundrak" "pinetab2") ]; - perSystem = { pkgs, system, diff --git a/modules/desktop/xserver.nix b/modules/desktop/xserver.nix index 1211c15..8510421 100644 --- a/modules/desktop/xserver.nix +++ b/modules/desktop/xserver.nix @@ -15,6 +15,12 @@ example = "kde"; description = "Which DE to enable"; }; + videoDrivers = mkOption { + type = types.listOf types.str; + default = []; + example = ["amdgpu"]; + description = "Extra X11 video drivers to load"; + }; }; config.services = mkIf cfg.enable { displayManager = { @@ -36,7 +42,7 @@ xserver = { inherit (cfg) enable; - videoDrivers = lists.optional config.mySystem.hardware.amdgpu.enable "amdgpu"; + videoDrivers = cfg.videoDrivers; xkb = { layout = "fr"; variant = "bepo_afnor"; diff --git a/modules/dev/qemu.nix b/modules/dev/qemu.nix index 0a7465a..758bf7a 100644 --- a/modules/dev/qemu.nix +++ b/modules/dev/qemu.nix @@ -8,15 +8,12 @@ with lib; let cfg = config.mySystem.dev.qemu; in { - options.mySystem.dev.qemu = { - enable = mkEnableOption "Enable QEMU"; - users = mkOption { - type = types.listOf types.str; - default = ["phundrak"]; - example = ["user1" "user2"]; - }; + options.mySystem.dev.qemu.users = mkOption { + type = types.listOf types.str; + default = ["phundrak"]; + example = ["user1" "user2"]; }; - config = mkIf cfg.enable { + config = { programs.virt-manager.enable = true; users.groups.libvirtd.members = cfg.users; virtualisation = { diff --git a/modules/dev/qemu.org b/modules/dev/qemu.org new file mode 100644 index 0000000..53c5b6a --- /dev/null +++ b/modules/dev/qemu.org @@ -0,0 +1,99 @@ +#+title: QEMU +#+setupfile: ../headers + +* QEMU +On machines where I run virtual machines, I want =virt-manager=, +=libvirtd=, and a few supporting pieces set up together. Here’s the +skeleton of the =nixos.qemu= module. +#+begin_src nix :tangle yes +{ + flake.modules.nixos.qemu = { + config, + lib, + pkgs, + ... + }: + with lib; let + cfg = config.mySystem.dev.qemu; + in { + <> + config = { + <> + <> + <> + <> + <> + <> + }; + }; +} +#+end_src + +** Declaring the Options +=users= lists which users get added to the =libvirtd= group, so they can +manage VMs without needing root. By default, I add myself to this +group. +#+name: options +#+begin_src nix +options.mySystem.dev.qemu.users = mkOption { + type = types.listOf types.str; + default = ["phundrak"]; + example = ["user1" "user2"]; +}; +#+end_src + +This option is then passed onto the standard NixOS option. +#+name: libvirtd-group +#+begin_src nix +users.groups.libvirtd.members = cfg.users; +#+end_src + +** virt-manager +This pulls in the GUI I actually use to create and manage VMs. +#+name: virt-manager +#+begin_src nix +programs.virt-manager.enable = true; +#+end_src + +** Virtualisation Backend +=libvirtd= is the daemon that actually runs and manages the VMs. SPICE +USB redirection lets me pass a USB device straight through to a guest +without unplugging it from the host first. +#+name: virtualisation +#+begin_src nix +virtualisation = { + libvirtd.enable = true; + spiceUSBRedirection.enable = true; +}; +#+end_src + +** Extra Packages +Besides =qemu= itself, =quickemu= lets me spin up a VM from a template in +one command, and =swtpm= provides the software TPM that guests like +Windows 11 insist on. +#+name: packages +#+begin_src nix +environment.systemPackages = with pkgs; [ + qemu + quickemu + swtpm +]; +#+end_src + +** Firmware Lookup Path +=virt-manager= and friends expect to find UEFI firmware images under +=/var/lib/qemu/firmware=, so I symlink QEMU’s own copy there instead of +making every tool aware of the Nix store path. +#+name: firmware-tmpfiles +#+begin_src nix +systemd.tmpfiles.rules = ["L+ /var/lib/qemu/firmware - - - - ${pkgs.qemu}/share/qemu/firmware"]; +#+end_src + +** ARM Emulation +This lets me run (and build) =aarch64-linux= binaries transparently +through QEMU’s user-mode emulation, which comes in handy when working +on the PineTab2 without needing actual ARM hardware on hand. +#+name: binfmt +#+begin_src nix +boot.binfmt.emulatedSystems = ["aarch64-linux"]; +#+end_src diff --git a/modules/hardware/amdgpu.nix b/modules/hardware/amdgpu.nix index a479d0a..c4bf121 100644 --- a/modules/hardware/amdgpu.nix +++ b/modules/hardware/amdgpu.nix @@ -1,64 +1,50 @@ { - flake.modules.nixos.amdgpu = { - pkgs, - lib, - config, - ... - }: - with lib; let - cfg = config.mySystem.hardware.amdgpu; - in { - options.mySystem.hardware.amdgpu.enable = mkEnableOption "Enables an AMD GPU configuration"; - config = mkIf cfg.enable { - hardware = { - graphics = { - enable = true; - enable32Bit = true; - extraPackages = with pkgs; [ - mesa # Mesa drivers for AMD GPUs - rocmPackages.clr # common language runtime for ROCm - rocmPackages.clr.icd # ROCm ICD for OpenCL - rocmPackages.rocblas # ROCm BLAS library - rocmPackages.hipblas # - rocmPackages.rpp # High-performance computer vision library - nvtopPackages.amd # GPU utilization monitoring - ]; - }; - amdgpu = { - initrd.enable = true; - opencl.enable = true; - }; - }; - environment.systemPackages = with pkgs; [ - clinfo - amdgpu_top - nvtopPackages.amd - ]; - systemd = { - packages = with pkgs; [lact]; - services.lactd.wantedBy = ["multi-user.target"]; - tmpfiles.rules = let - rocmEnv = pkgs.symlinkJoin { - name = "rocm-combined"; - paths = with pkgs.rocmPackages; [ - clr - clr.icd - rocblas - hipblas - rpp - ]; - }; - in [ - "L+ /opt/rocm - - - - ${rocmEnv}" + flake.modules.nixos.amdgpu = {pkgs, ...}: { + hardware.graphics = { + enable = true; + enable32Bit = true; + }; + hardware.amdgpu = { + initrd.enable = true; + opencl.enable = true; + }; + hardware.graphics.extraPackages = with pkgs; [ + mesa + rocmPackages.clr + rocmPackages.clr.icd + rocmPackages.rocblas + rocmPackages.hipblas + rocmPackages.rpp + nvtopPackages.amd + ]; + environment.systemPackages = with pkgs; [ + clinfo + amdgpu_top + nvtopPackages.amd + ]; + systemd = { + packages = with pkgs; [lact]; + services.lactd.wantedBy = ["multi-user.target"]; + tmpfiles.rules = let + rocmEnv = pkgs.symlinkJoin { + name = "rocm-combined"; + paths = with pkgs.rocmPackages; [ + clr + clr.icd + rocblas + hipblas + rpp ]; }; - environment.variables = { - ROCM_PATH = "/opt/rocm"; # Set ROCm path - HIP_VISIBLE_DEVICES = "1"; # Use only the eGPU (ID 1) - ROCM_VISIBLE_DEVICES = "1"; # Optional: ROCm equivalent for visibility - # LD_LIBRARY_PATH = "/opt/rocm/lib"; # Add ROCm libraries - HSA_OVERRIDE_GFX_VERSION = "10.3.0"; # Set GFX version override - }; - }; + in [ + "L+ /opt/rocm - - - - ${rocmEnv}" + ]; }; + environment.variables = { + ROCM_PATH = "/opt/rocm"; + HIP_VISIBLE_DEVICES = "1"; + ROCM_VISIBLE_DEVICES = "1"; + HSA_OVERRIDE_GFX_VERSION = "10.3.0"; + }; + }; } diff --git a/modules/hardware/amdgpu.org b/modules/hardware/amdgpu.org new file mode 100644 index 0000000..aa224ad --- /dev/null +++ b/modules/hardware/amdgpu.org @@ -0,0 +1,147 @@ +#+title: AMD GPU support +#+setupfile: ../headers +#+property: header-args:emacs-lisp :lexical t :exports none :tangle no + +* AMD GPU support +Only one of my machines has an AMD GPU, but it does require some +tweaking. First, here’s the skeleton of the =nixos.amdgpu= module. +#+begin_src nix :tangle yes +{ + flake.modules.nixos.amdgpu = {pkgs, ...}: { + <> + <> + <> + <> + <> + <> + }; +} +#+end_src + +** Enable the Hardware +The first thing is to enable graphics in NixOS. We’ll enable 32-bit +support while we’re at it. +#+name: enable-graphics +#+begin_src nix +hardware.graphics = { + enable = true; + enable32Bit = true; +}; +#+end_src + +We can now enable the hardware proper, including initrd and OpenCL +support for the GPU. =initrd.enable= already makes NixOS load =amdgpu= as +early as stage 1 on its own, which is why the kernel module (see +[[file:../boot/kernel.org][Kernel Configuration]]) doesn’t need to pick between =amdgpu= and =i915= +itself: it can simply default to =i915= and let this module handle its +own early loading. +#+name: enable-hardware +#+begin_src nix +hardware.amdgpu = { + initrd.enable = true; + opencl.enable = true; +}; +#+end_src + +Some software expect some packages to be installed by default, such as +rocblas or Hipblas. Here are these packages. +#+name: amd-packages +| Package Name | Description | +|----------------------+--------------------------------------------------------------------| +| =mesa= | Mesa drivers for AMD GPUs | +| =rocmPackages.clr= | Common Language Runtime for ROCm | +| =rocmPackages.clr.icd= | ROCm ICD for OpenCL | +| =rocmPackages.rocblas= | ROCm BLAS library | +| =rocmPackages.hipblas= | HIP BLAS marshalling library (CUDA-compatible interface to rocBLAS) | +| =rocmPackages.rpp= | High-performance computer vision library | +| =nvtopPackages.amd= | Just for me, GPU utilisation monitoring | + +#+name: extra-hardware-packages +#+begin_src emacs-lisp :var packages=amd-packages :cache yes +(mapconcat (lambda (package) (s-chop-prefix "=" (s-chop-suffix "=" package))) + (mapcar #'car packages) + "\n") +#+end_src + +#+RESULTS[28ba71596b4f184338e46c76c0ba1aa41899bba0]: extra-hardware-packages +: mesa +: rocmPackages.clr +: rocmPackages.clr.icd +: rocmPackages.rocblas +: rocmPackages.hipblas +: rocmPackages.rpp +: nvtopPackages.amd + +#+name: hardware-extra-packages +#+begin_src nix +hardware.graphics.extraPackages = with pkgs; [ + <> +]; +#+end_src + +** Diagnostics and Monitoring +Beyond what’s needed by the driver stack itself, I also want a couple +of tools available on the command line to check on the GPU: =clinfo= to +inspect the available OpenCL platforms and devices, =amdgpu_top= for a +=btop=-like view of the AMD GPU’s activity, and =nvtop= (packaged for AMD +under =nvtopPackages.amd=) for a more familiar process-oriented monitor. +#+name: system-packages +#+begin_src nix +environment.systemPackages = with pkgs; [ + clinfo + amdgpu_top + nvtopPackages.amd +]; +#+end_src + +** LACT, the GPU Control Daemon +[[https://github.com/ilya-zlobintsev/LACT][LACT]] (Linux AMDGPU Control Application) lets me tweak fan curves, +power limits and clocks on the card, through a daemon (=lactd=) and a +GUI that talks to it. The package ships both a systemd service +definition and a udev rule, so all that’s left for me to do is install +the package and make sure the daemon is started. + +I’m also consolidating the ROCm libraries under =/opt/rocm= via a +=tmpfiles= rule. Some tools out there still expect to find ROCm +installed at that standard filesystem location rather than resolved +through the Nix store, so I build a combined derivation of the ROCm +packages I need and symlink it into place. +#+name: lact +#+begin_src nix +systemd = { + packages = with pkgs; [lact]; + services.lactd.wantedBy = ["multi-user.target"]; + tmpfiles.rules = let + rocmEnv = pkgs.symlinkJoin { + name = "rocm-combined"; + paths = with pkgs.rocmPackages; [ + clr + clr.icd + rocblas + hipblas + rpp + ]; + }; + in [ + "L+ /opt/rocm - - - - ${rocmEnv}" + ]; +}; +#+end_src + +** Environment Variables +Finally, a handful of environment variables to point tools at that +=/opt/rocm= prefix and to steer ROCm/HIP towards the right GPU. This +machine exposes the AMD card as device ID =1= (the other device being an +iGPU), so I pin both =HIP_VISIBLE_DEVICES= and =ROCM_VISIBLE_DEVICES= to +it. The card also isn’t officially supported by ROCm, hence the +=HSA_OVERRIDE_GFX_VERSION= override, which tells ROCm to treat it as the +closest supported architecture instead of refusing to run. +#+name: environment-variables +#+begin_src nix +environment.variables = { + ROCM_PATH = "/opt/rocm"; + HIP_VISIBLE_DEVICES = "1"; + ROCM_VISIBLE_DEVICES = "1"; + HSA_OVERRIDE_GFX_VERSION = "10.3.0"; +}; +#+end_src diff --git a/modules/hardware/bluetooth.nix b/modules/hardware/bluetooth.nix index f490d3b..7b654ac 100644 --- a/modules/hardware/bluetooth.nix +++ b/modules/hardware/bluetooth.nix @@ -1,16 +1,6 @@ { flake.modules.nixos.bluetooth = { - lib, - config, - ... - }: - with lib; let - cfg = config.mySystem.hardware.bluetooth; - in { - options.mySystem.hardware.bluetooth.enable = mkEnableOption "Enable bluetooth"; - config = mkIf cfg.enable { - hardware.bluetooth.enable = cfg.enable; - services.blueman.enable = cfg.enable; - }; - }; + hardware.bluetooth.enable = true; + services.blueman.enable = true; + }; } diff --git a/modules/hardware/bluetooth.org b/modules/hardware/bluetooth.org new file mode 100644 index 0000000..c289295 --- /dev/null +++ b/modules/hardware/bluetooth.org @@ -0,0 +1,24 @@ +#+title: Bluetooth +#+setupfile: ../headers + +* Bluetooth +Not all of my machines have Bluetooth hardware worth turning on, so +this is a plain toggle rather than something applied unconditionally. +Here’s the skeleton of the =nixos.bluetooth= module. +#+begin_src nix :tangle yes +{ + flake.modules.nixos.bluetooth = { + <> + }; +} +#+end_src + +** Enabling Bluetooth +Turning the option on enables Bluetooth support at the kernel level +and installs =blueman=, a tray applet I use to pair and manage devices +without digging through a terminal. +#+name: config +#+begin_src nix +hardware.bluetooth.enable = true; +services.blueman.enable = true; +#+end_src diff --git a/modules/hardware/fingerprint.nix b/modules/hardware/fingerprint.nix index 2512f63..cba5c6f 100644 --- a/modules/hardware/fingerprint.nix +++ b/modules/hardware/fingerprint.nix @@ -1,15 +1,5 @@ { flake.modules.nixos.fingerprint = { - lib, - config, - ... - }: - with lib; let - cfg = config.mySystem.hardware.fingerprint; - in { - options.mySystem.hardware.fingerprint.enable = mkEnableOption "Enable fingerprint reader"; - config = mkIf cfg.enable { - hardware.facter.detected.fingerprint.enable = cfg.enable; - }; - }; + hardware.facter.detected.fingerprint.enable = true; + }; } diff --git a/modules/hardware/fingerprint.org b/modules/hardware/fingerprint.org new file mode 100644 index 0000000..c8f9d97 --- /dev/null +++ b/modules/hardware/fingerprint.org @@ -0,0 +1,22 @@ +#+title: Fingerprint Reader +#+setupfile: ../headers + +* Fingerprint Reader +My ThinkPad x220 has a fingerprint reader, so this is a plain toggle +rather than something applied unconditionally. Here’s the skeleton of +the =nixos.fingerprint= module. +#+begin_src nix :tangle yes +{ + flake.modules.nixos.fingerprint = { + <> + }; +} +#+end_src + +** Enabling the Reader +Rather than picking a driver myself, I let [[https://github.com/numtide/nixos-facter-modules][nixos-facter]] detect the +reader and wire up the matching driver automatically. +#+name: config +#+begin_src nix +hardware.facter.detected.fingerprint.enable = true; +#+end_src diff --git a/modules/hardware/firmware.org b/modules/hardware/firmware.org new file mode 100644 index 0000000..5707817 --- /dev/null +++ b/modules/hardware/firmware.org @@ -0,0 +1,21 @@ +#+title: Firmware +#+setupfile: ../headers + +* Firmware +A small module to pull in the full firmware blob set, for machines +where I’d rather not chase down which specific firmware package a +piece of hardware needs. Here’s the =nixos.firmware= module. +#+begin_src nix :tangle yes +{ + flake.modules.nixos.firmware = {lib, ...}: { + <> + }; +} +#+end_src + +=mkDefault= keeps this overridable, in case a host needs to turn it back +off or pin a narrower set of firmware packages itself. +#+name: enable-all-firmware +#+begin_src nix +hardware.enableAllFirmware = lib.mkDefault true; +#+end_src diff --git a/modules/hardware/input/corne.nix b/modules/hardware/input/corne.nix index ab3c644..8faa105 100644 --- a/modules/hardware/input/corne.nix +++ b/modules/hardware/input/corne.nix @@ -1,17 +1,9 @@ { flake.modules.nixos.corne = { - lib, - config, - ... - }: - with lib; let - cfg = config.mySystem.hardware.input.corne; - in { - options.mySystem.hardware.input.corne.allowHidAccess = mkEnableOption "Enable HID access to the corne keyboard"; - config.services.udev = mkIf cfg.allowHidAccess { - extraRules = '' - KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{serial}=="*vial:f64c2b3c*", MODE="0660", GROUP="users", TAG+="uaccess", TAG+="udev-acl" - ''; - }; + services.udev = { + extraRules = '' + KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{serial}=="*vial:f64c2b3c*", MODE="0660", GROUP="users", TAG+="uaccess", TAG+="udev-acl" + ''; }; + }; } diff --git a/modules/hardware/input/corne.org b/modules/hardware/input/corne.org new file mode 100644 index 0000000..349820a --- /dev/null +++ b/modules/hardware/input/corne.org @@ -0,0 +1,32 @@ +#+title: Corne Keyboard +#+setupfile: ../../headers + +* Corne Keyboard +I use a Corne (=crkbd=), a small split ergonomic keyboard, flashed with +[[https://get.vial.today/][Vial]], a QMK-based firmware that lets me remap keys live from a GUI +instead of having to reflash the keyboard every time I want to tweak +my layout. Here’s the skeleton of the =nixos.corne= module. +#+begin_src nix :tangle yes +{ + flake.modules.nixos.corne = { + <> + }; +} +#+end_src + +** Granting HID Access +By default, the =hidraw= device nodes created for the keyboard are only +accessible to root, which means Vial can’t talk to it without running +as root too. Instead, I add a =udev= rule matching my keyboard’s Vial +identifier (the part after =vial:= is the keyboard’s unique unlock ID, +burned into its firmware) and grant the =users= group read/write access +to it, tagging it for =uaccess= / =udev-acl= so it’s also picked up by the +logged-in session’s ACLs. +#+name: udev-rule +#+begin_src nix +services.udev = { + extraRules = '' + KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{serial}=="*vial:f64c2b3c*", MODE="0660", GROUP="users", TAG+="uaccess", TAG+="udev-acl" + ''; +}; +#+end_src diff --git a/modules/hardware/input/disable-ibm-trackpoint.nix b/modules/hardware/input/disable-ibm-trackpoint.nix new file mode 100644 index 0000000..785f5ad --- /dev/null +++ b/modules/hardware/input/disable-ibm-trackpoint.nix @@ -0,0 +1,7 @@ +{ + flake.modules.nixos.disable-ibm-trackpoint = { + services.udev.extraRules = '' + ATTRS{name}=="*TPPS/2 IBM TrackPoint", ENV{ID_INPUT}="", ENV{ID_INPUT_MOUSE}="", ENV{ID_INPUT_POINTINGSTICK}="" + ''; + }; +} diff --git a/modules/hardware/input/disable-ibm-trackpoint.org b/modules/hardware/input/disable-ibm-trackpoint.org new file mode 100644 index 0000000..3cacc3d --- /dev/null +++ b/modules/hardware/input/disable-ibm-trackpoint.org @@ -0,0 +1,30 @@ +#+title: Disable the IBM TrackPoint +#+setupfile: ../../headers + +* Disable the IBM TrackPoint +My ThinkPads come with IBM’s TrackPoint, the little red nub sitting +between the =G=, =H= and =B= keys. I don’t want it active, though, it has a +drift and I cannot fix it unless I change my keyboard. Thus, this +module exposes a way to turn it off. Here’s the skeleton of the +=nixos.disable-ibm-trackpoint= module. +#+begin_src nix :tangle yes +{ + flake.modules.nixos.disable-ibm-trackpoint = { + <> + }; +} +#+end_src + +** Disabling the TrackPoint +The TrackPoint shows up to the input stack as a regular pointer +device, so to disable it I can’t simply blacklist a driver — libinput +and friends would still pick it up through =evdev=. Instead, I match it +by its device name and clear the =ID_INPUT=, =ID_INPUT_MOUSE= and +=ID_INPUT_POINTINGSTICK= udev properties on it, which tells the input +stack to simply ignore the device as a pointing device. +#+name: udev-rule +#+begin_src nix +services.udev.extraRules = '' + ATTRS{name}=="*TPPS/2 IBM TrackPoint", ENV{ID_INPUT}="", ENV{ID_INPUT_MOUSE}="", ENV{ID_INPUT_POINTINGSTICK}="" +''; +#+end_src diff --git a/modules/hardware/input/ibm-trackpoint.nix b/modules/hardware/input/ibm-trackpoint.nix deleted file mode 100644 index 172b18c..0000000 --- a/modules/hardware/input/ibm-trackpoint.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ - flake.modules.nixos.ibm-trackpoint = { - lib, - config, - ... - }: - with lib; let - cfg = config.mySystem.hardware.input.ibmTrackpoint; - in { - options.mySystem.hardware.input.ibmTrackpoint.disable = mkEnableOption "Disable IBM’s trackpoint on ThinkPad"; - config.services.udev = mkIf cfg.disable { - extraRules = '' - ATTRS{name}=="*TPPS/2 IBM TrackPoint", ENV{ID_INPUT}="", ENV{ID_INPUT_MOUSE}="", ENV{ID_INPUT_POINTINGSTICK}="" - ''; - }; - }; -} diff --git a/modules/hardware/input/opentablet.nix b/modules/hardware/input/opentablet.nix index 652735b..4d4b015 100644 --- a/modules/hardware/input/opentablet.nix +++ b/modules/hardware/input/opentablet.nix @@ -1,19 +1,9 @@ { flake.modules.nixos.opentablet = { - lib, - config, - ... - }: - with lib; let - cfg = config.mySystem.hardware.input.opentablet; - in { - options.mySystem.hardware.input.opentablet.enable = mkEnableOption "Enables OpenTablet drivers"; - config = mkIf cfg.enable { - hardware.opentabletdriver = { - inherit (cfg) enable; - daemon.enable = true; - }; - boot.kernelModules = ["wacom"]; - }; + hardware.opentabletdriver = { + enable = true; + daemon.enable = true; }; + boot.kernelModules = ["wacom"]; + }; } diff --git a/modules/hardware/input/opentablet.org b/modules/hardware/input/opentablet.org new file mode 100644 index 0000000..4a7b337 --- /dev/null +++ b/modules/hardware/input/opentablet.org @@ -0,0 +1,30 @@ +#+title: OpenTabletDriver +#+setupfile: ../../headers + +* OpenTabletDriver +Some of my machines are hooked up to a graphics tablet, a Wacom +Bamboo, driven by [[https://opentabletdriver.net/][OpenTabletDriver]]. I remember buying it for 15€, what +a deal that was! Anyway, since most of my hosts don’t have a tablet +attached, this is exposed as a regular toggle rather than enabled +unconditionally. Here’s the skeleton of the =nixos.opentablet= module. +#+begin_src nix :tangle yes +{ + flake.modules.nixos.opentablet = { + <> + }; +} +#+end_src + +** Enabling the Driver +Enabling OpenTabletDriver proper is just a matter of turning on the +module and its daemon. I also need to explicitly load the =wacom= +kernel module, since my tablet (like most of them) identifies itself +as a Wacom device at the hardware level regardless of brand. +#+name: config +#+begin_src nix +hardware.opentabletdriver = { + enable = true; + daemon.enable = true; +}; +boot.kernelModules = ["wacom"]; +#+end_src diff --git a/modules/hardware/input/trackball.org b/modules/hardware/input/trackball.org new file mode 100644 index 0000000..9bc13d9 --- /dev/null +++ b/modules/hardware/input/trackball.org @@ -0,0 +1,22 @@ +#+title: Trackball +#+setupfile: ../../headers + +* Trackball +I use a trackball on some of my machines, and I middle-click by +pressing the left and right buttons together rather than through a +dedicated button. That’s the only downside of the two trackballs I +own; otherwise, I can’t recommend one enough. Here’s the +=nixos.trackball= module enabling this behaviour. +#+begin_src nix :tangle yes +{ + flake.modules.nixos.trackball = { + <> + }; +} +#+end_src + +Enabling middle-click emulation is simple: +#+name: middle-emulation +#+begin_src nix +services.libinput.mouse.middleEmulation = true; +#+end_src diff --git a/modules/hardware/pinetab2.nix b/modules/hardware/pinetab2.nix index 556bf46..a36c9e7 100644 --- a/modules/hardware/pinetab2.nix +++ b/modules/hardware/pinetab2.nix @@ -1,14 +1,10 @@ { - flake.modules.nixos.pinetab2 = {lib, ...}: - with lib; { - options.mySystem.hardware.pinetab2.enable = mkEnableOption "Activate support for the PineTab2"; - config = { - boot.kernelParams = ["console=tty0" "console=ttyS2,1500000n8" "rootwait" "root=LABEL=NIXOS_SD" "rw"]; - hardware.sensor.iio.enable = true; - services.avahi = { - enable = true; - openFirewall = true; - }; - }; + flake.modules.nixos.pinetab2 = { + boot.kernelParams = ["console=tty0" "console=ttyS2,1500000n8" "rootwait" "root=LABEL=NIXOS_SD" "rw"]; + hardware.sensor.iio.enable = true; + services.avahi = { + enable = true; + openFirewall = true; }; + }; } diff --git a/modules/hardware/pinetab2.org b/modules/hardware/pinetab2.org new file mode 100644 index 0000000..aa231e0 --- /dev/null +++ b/modules/hardware/pinetab2.org @@ -0,0 +1,48 @@ +#+title: PineTab2 +#+setupfile: ../headers + +* PineTab2 +A few tweaks are specific to my PineTab2 tablet: getting a serial +console working at boot, exposing its sensors, and making it easy to +find on whatever network it’s connected to. Here’s the skeleton of the +=nixos.pinetab2= module. +#+begin_src nix :tangle yes +{ + flake.modules.nixos.pinetab2 = { + <> + <> + <> + }; +} +#+end_src + +** Serial Console +These kernel parameters get me a working serial console on the +tablet’s UART at boot, and point the kernel at the SD card’s root +filesystem by label rather than by a device path that can shift +around. +#+name: kernel-params +#+begin_src nix +boot.kernelParams = ["console=tty0" "console=ttyS2,1500000n8" "rootwait" "root=LABEL=NIXOS_SD" "rw"]; +#+end_src + +** Sensors +Turning on the IIO (Industrial I/O) subsystem exposes the tablet’s +accelerometer and ambient light sensor, which is what lets things like +auto-rotate work. +#+name: sensors +#+begin_src nix +hardware.sensor.iio.enable = true; +#+end_src + +** Finding It on the Network +The tablet moves between networks a lot, so Avahi lets me reach it by +its =.local= hostname over mDNS instead of having to look up whatever IP +it was handed. +#+name: avahi +#+begin_src nix +services.avahi = { + enable = true; + openFirewall = true; +}; +#+end_src diff --git a/modules/hardware/sound.nix b/modules/hardware/sound.nix index 0fd8ff8..01891f8 100644 --- a/modules/hardware/sound.nix +++ b/modules/hardware/sound.nix @@ -9,7 +9,6 @@ cfg = config.mySystem.hardware.sound; in { options.mySystem.hardware.sound = { - enable = mkEnableOption "Whether to enable sounds with Pipewire"; noisetorch = mkEnableOption "Whether to activate noisetorch support"; scarlett.enable = mkEnableOption "Activate support for Scarlett sound card"; alsa = mkOption { @@ -35,7 +34,7 @@ config = { environment.systemPackages = mkIf cfg.scarlett.enable [pkgs.alsa-scarlett-gui]; services = { - pipewire = mkIf cfg.enable { + pipewire = { enable = true; alsa = mkIf cfg.alsa { enable = mkDefault true; @@ -45,9 +44,7 @@ }; pulseaudio.enable = false; }; - programs.noisetorch = mkIf cfg.enable { - enable = cfg.noisetorch; - }; + programs.noisetorch.enable = cfg.noisetorch; }; }; } diff --git a/modules/hardware/sound.org b/modules/hardware/sound.org new file mode 100644 index 0000000..b31a81e --- /dev/null +++ b/modules/hardware/sound.org @@ -0,0 +1,118 @@ +#+title: Sound +#+setupfile: ../headers + +* Sound +I use Pipewire for audio on my desktop machines, with a couple of +compatibility layers and bits of hardware-specific support switched on +as needed. Here’s the skeleton of the =nixos.sound= module. +#+begin_src nix :tangle yes +{ + flake.modules.nixos.sound = { + lib, + config, + pkgs, + ... + }: + with lib; let + cfg = config.mySystem.hardware.sound; + in { + options.mySystem.hardware.sound = { + <> + <> + <> + <> + <> + }; + + config = { + <> + <> + <> + }; + }; +} +#+end_src + +** Noise Suppression +=noisetorch= toggles [[https://github.com/noisetorch/NoiseTorch][NoiseTorch]], a real-time microphone noise +suppression tool I use for calls, so I have an option for that. +#+name: opt-noisetorch +#+begin_src nix +noisetorch = mkEnableOption "Whether to activate noisetorch support"; +#+end_src + +Passing it to NixOS is quite simple. +#+name: noisetorch-config +#+begin_src nix +programs.noisetorch.enable = cfg.noisetorch; +#+end_src + +** Scarlett Sound Card +=scarlett.enable= is for the machine =marpa= with a Focusrite Scarlett 2i2 +audio interface plugged in; it only pulls in that card’s control GUI. +#+name: opt-scarlett +#+begin_src nix +scarlett.enable = mkEnableOption "Activate support for Scarlett sound card"; +#+end_src + +It is installed quite easily in enabled. +#+name: scarlett-package +#+begin_src nix +environment.systemPackages = mkIf cfg.scarlett.enable [pkgs.alsa-scarlett-gui]; +#+end_src + +** Enabling Pipewire +Enabling Pipewire also means explicitly turning PulseAudio off, since +the two can’t run as the system’s sound server at the same time. +#+name: pipewire-config +#+begin_src nix +services = { + pipewire = { + enable = true; + alsa = mkIf cfg.alsa { + enable = mkDefault true; + support32Bit = mkDefault true; + }; + jack.enable = mkDefault cfg.jack; + }; + pulseaudio.enable = false; +}; +#+end_src + +** ALSA Compatibility +=alsa= enables Pipewire’s ALSA compatibility layer, on by default since +most of my audio software still expects ALSA. +#+name: opt-alsa +#+begin_src nix +alsa = mkOption { + type = types.bool; + example = true; + default = true; + description = "Whether to enable ALSA support with Pipewire"; +}; +#+end_src + +** JACK Compatibility +=jack= enables Pipewire’s JACK compatibility layer, off by default since +only my production machine needs it. +#+name: opt-jack +#+begin_src nix +jack = mkOption { + type = types.bool; + example = true; + default = false; + description = "Whether to enable JACK support with Pipewire"; +}; +#+end_src + +** Base Package +=package= picks which PulseAudio package to build things on top of. +#+name: opt-package +#+begin_src nix +package = mkOption { + type = types.package; + example = pkgs.pulseaudio; + default = pkgs.pulseaudioFull; + description = "Which base package to use for PulseAudio"; +}; +#+end_src diff --git a/modules/headers b/modules/headers new file mode 100644 index 0000000..6398e47 --- /dev/null +++ b/modules/headers @@ -0,0 +1,22 @@ +# -*- mode: org -*- +#+AUTHOR: Lucien Cartier-Tilet +#+EMAIL: lucien@phundrak.com +#+CREATOR: Lucien Cartier-Tilet +#+LANGUAGE: en + +# ### ORG OPTIONS ############################################################## + +#+options: H:4 broken_links:mark email:t ^:{} tex:dvisvgm toc:nil +#+KEYWORDS: dotfiles, linux, emacs, configuration, phundrak, drakpa +#+startup: content align hideblocks +#+property: header-args:emacs-lisp :noweb yes :exports none :eval yes :cache yes +#+property: header-args:nix :exports code :tangle no :noweb no-export +#+property: header-args:dot :dir img :exports results :eval yes :cache yes :class gentree + +# ### MACROS ################################################################### +#+macro: phon @@html:/$1/@@ +#+macro: newline @@html:
@@ +#+macro: latex-html @@latex:$1@@@@html:$2@@ +#+macro: v @@html:$1@@ +#+macro: begin-largetable @@html:
@@ +#+macro: end-largetable @@html:
@@ diff --git a/modules/home/desktop/caelestia.nix b/modules/home/desktop/caelestia.nix index 7e7cce5..b6807a4 100644 --- a/modules/home/desktop/caelestia.nix +++ b/modules/home/desktop/caelestia.nix @@ -1,6 +1,6 @@ {inputs, ...}: { flake-file.inputs.caelestia-shell = { - url = "github:caelestia-dots/shell"; + url = "github:caelestia-dots/shell?ref=stable"; inputs.nixpkgs.follows = "nixpkgs"; }; diff --git a/modules/home/phundrak/phundrak-packages.nix b/modules/home/phundrak/phundrak-packages.nix index c9c2c84..7cb3476 100644 --- a/modules/home/phundrak/phundrak-packages.nix +++ b/modules/home/phundrak/phundrak-packages.nix @@ -62,6 +62,7 @@ openmw openttd-jgrpp moonlight-qt + vintagestory # Gnome stuff gnomeExtensions.tray-icons-reloaded diff --git a/modules/hosts/NaroMk3/default.nix b/modules/hosts/NaroMk3/default.nix index 501e758..6cfad0a 100644 --- a/modules/hosts/NaroMk3/default.nix +++ b/modules/hosts/NaroMk3/default.nix @@ -27,10 +27,7 @@ in { mySystem = { boot = { - kernel = { - hardened = true; - cpuVendor = "amd"; - }; + kernel.cpuVendor = "amd"; grub = { enable = true; device = "/dev/sdb"; diff --git a/modules/hosts/elcafe/default.nix b/modules/hosts/elcafe/default.nix index 1f8f3bc..858ba2c 100644 --- a/modules/hosts/elcafe/default.nix +++ b/modules/hosts/elcafe/default.nix @@ -9,6 +9,7 @@ in { m.kernel m.hardened m.loader + m.zfs m.docker m.endlessh m.ssh @@ -22,18 +23,12 @@ in { mySystem = { boot = { - kernel = { - hardened = true; - cpuVendor = "intel"; - }; + kernel.cpuVendor = "intel"; grub = { enable = true; device = "/dev/sdh"; }; - zfs = { - enable = true; - pools = ["tank"]; - }; + zfs.pools = ["tank"]; }; dev.docker = { enable = true; diff --git a/modules/hosts/gampo/default.nix b/modules/hosts/gampo/default.nix index 69546dd..85ab49e 100644 --- a/modules/hosts/gampo/default.nix +++ b/modules/hosts/gampo/default.nix @@ -19,7 +19,7 @@ in { m.bluetooth m.fingerprint m.corne - m.ibm-trackpoint + m.disable-ibm-trackpoint m.opentablet m.sound m.i18n-input @@ -32,7 +32,6 @@ in { mySystem = { boot = { - plymouth.enable = true; kernel = { cpuVendor = "intel"; package = pkgs.linuxPackages; @@ -51,29 +50,14 @@ in { podman.enable = true; autoprune.enable = true; }; - hardware = { - bluetooth.enable = true; - fingerprint.enable = true; - input = { - corne.allowHidAccess = true; - ibmTrackpoint.disable = true; - opentablet.enable = true; - }; - sound.enable = true; - }; - i18n.input.enable = true; misc.keymap = "fr-bepo"; networking = { hostname = "gampo"; id = "0630b33f"; }; - packages = { - appimage.enable = true; - flatpak.enable = true; - nix = { - gc.automatic = true; - nix-ld.enable = true; - }; + packages.nix = { + gc.automatic = true; + nix-ld.enable = true; }; services = { fwupd.enable = true; diff --git a/modules/hosts/marpa/default.nix b/modules/hosts/marpa/default.nix index 7c53dc7..44282f4 100644 --- a/modules/hosts/marpa/default.nix +++ b/modules/hosts/marpa/default.nix @@ -17,6 +17,7 @@ in { # m.niri m.waydroid m.xserver + m.amdgpu m.docker m.qemu m.bluetooth @@ -72,7 +73,6 @@ in { mySystem = { boot = { - plymouth.enable = true; kernel = { cpuVendor = "amd"; v4l2loopback.enable = true; @@ -84,11 +84,11 @@ in { }; desktop = { hyprland.enable = true; - niri.enable = true; waydroid.enable = true; xserver = { enable = true; de = "gnome"; + videoDrivers = ["amdgpu"]; }; }; dev = { @@ -97,23 +97,14 @@ in { podman.enable = true; autoprune.enable = true; }; - qemu.enable = true; }; hardware = { - amdgpu.enable = true; - bluetooth.enable = true; - input = { - corne.allowHidAccess = true; - opentablet.enable = true; - }; sound = { - enable = true; noisetorch = true; jack = true; scarlett.enable = true; }; }; - i18n.input.enable = true; misc.keymap = "fr-bepo"; networking = { hostname = "marpa"; @@ -126,11 +117,7 @@ in { } ]; }; - packages = { - appimage.enable = true; - flatpak.enable = true; - nix.nix-ld.enable = true; - }; + packages.nix.nix-ld.enable = true; services = { fwupd.enable = true; harmonia = { diff --git a/modules/hosts/pinetab2/default.nix b/modules/hosts/pinetab2/default.nix index f2b13fe..1de1c07 100644 --- a/modules/hosts/pinetab2/default.nix +++ b/modules/hosts/pinetab2/default.nix @@ -9,7 +9,6 @@ in { m.niri m.waydroid m.xserver - m.amdgpu m.docker m.bluetooth m.opentablet @@ -38,26 +37,15 @@ in { podman.enable = true; autoprune.enable = true; }; - hardware = { - bluetooth.enable = true; - input.opentablet.enable = true; - pinetab2.enable = true; - sound.enable = true; - }; - i18n.input.enable = true; misc.keymap = "fr-bepo"; networking = { hostname = "pinetab2"; id = "99a11b15"; wifi.disablePowersave = true; }; - packages = { - appimage.enable = true; - flatpak.enable = true; - nix = { - gc.automatic = true; - nix-ld.enable = true; - }; + packages.nix = { + gc.automatic = true; + nix-ld.enable = true; }; services.ssh.enable = true; users = { diff --git a/modules/hosts/tilo/default.nix b/modules/hosts/tilo/default.nix index 4bfe530..8157b22 100644 --- a/modules/hosts/tilo/default.nix +++ b/modules/hosts/tilo/default.nix @@ -8,6 +8,7 @@ in { m.kernel m.hardened m.loader + m.zfs m.docker m.calibre m.endlessh @@ -18,14 +19,8 @@ in { mySystem = { boot = { - kernel = { - hardened = true; - cpuVendor = "amd"; - }; - zfs = { - enable = true; - pools = ["tank"]; - }; + kernel.cpuVendor = "amd"; + zfs.pools = ["tank"]; }; dev.docker.enable = true; misc.keymap = "fr-bepo"; diff --git a/modules/i18n/input.nix b/modules/i18n/input.nix index bc7b684..e625479 100644 --- a/modules/i18n/input.nix +++ b/modules/i18n/input.nix @@ -1,27 +1,17 @@ { - flake.modules.nixos.i18n-input = { - lib, - config, - pkgs, - ... - }: - with lib; let - cfg = config.mySystem.i18n.input; - in { - options.mySystem.i18n.input.enable = mkEnableOption "Enable i18n input with fcitx5"; - - config.i18n.inputMethod = mkIf cfg.enable { - enable = true; - type = "fcitx5"; - fcitx5.addons = with pkgs; [ - fcitx5-gtk - fcitx5-mozc-ut # Japanese input support - fcitx5-nord - fcitx5-table-other # X-SAMPA to IPA support - qt6Packages.fcitx5-chinese-addons # allow to load table addons - qt6Packages.fcitx5-configtool - qt6Packages.fcitx5-with-addons - ]; - }; + flake.modules.nixos.i18n-input = {pkgs, ...}: { + i18n.inputMethod = { + enable = true; + type = "fcitx5"; + fcitx5.addons = with pkgs; [ + fcitx5-gtk + fcitx5-mozc-ut + fcitx5-nord + fcitx5-table-other + qt6Packages.fcitx5-chinese-addons + qt6Packages.fcitx5-configtool + qt6Packages.fcitx5-with-addons + ]; }; + }; } diff --git a/modules/i18n/input.org b/modules/i18n/input.org new file mode 100644 index 0000000..6bbd8df --- /dev/null +++ b/modules/i18n/input.org @@ -0,0 +1,52 @@ +#+title: Input Methods +#+setupfile: ../headers + +* Input Methods +I type in more than one script, so I need a proper input method +framework rather than relying on whatever the desktop environment +ships with. Plus, the default /AFNOR bépo/ layout is currently buggy, +with some dead keys not working properly, such as =AltGr+s=, but it +isn’t with fcitx5. Here’s the =nixos.i18n-input= module. +#+begin_src nix :tangle yes +{ + flake.modules.nixos.i18n-input = {pkgs, ...}: { + i18n.inputMethod = { + <> + <> + }; + }; +} +#+end_src + +** Enabling fcitx5 +[[https://fcitx-im.org/wiki/Fcitx_5][fcitx5]] is the input method framework I’ve settled on; it covers +everything from CJK input to custom table-based methods. +#+name: enable-fcitx5 +#+begin_src nix +enable = true; +type = "fcitx5"; +#+end_src + +** Addons +=fcitx5-gtk= gets fcitx5 working inside GTK applications, and the Qt +equivalents (=fcitx5-configtool=, =fcitx5-with-addons=) do the same for Qt +ones whilst also giving me a GUI to configure it all. =fcitx5-mozc-ut= +adds Japanese input through Mozc, and +=qt6Packages.fcitx5-chinese-addons= adds the table addons Chinese input +methods need. I don’t actually need this package for Chinese itself, +but it is somehow necessary for the X-SAMPA input method, which I get +from =fcitx5-table-other=. I have it to type IPA on the fly when working +on my constructed languages. Lastly, =fcitx5-nord= just reskins the UI +to match the rest of my setup. +#+name: fcitx5-addons +#+begin_src nix +fcitx5.addons = with pkgs; [ + fcitx5-gtk + fcitx5-mozc-ut + fcitx5-nord + fcitx5-table-other + qt6Packages.fcitx5-chinese-addons + qt6Packages.fcitx5-configtool + qt6Packages.fcitx5-with-addons +]; +#+end_src diff --git a/modules/i18n/locale.org b/modules/i18n/locale.org new file mode 100644 index 0000000..a927bd3 --- /dev/null +++ b/modules/i18n/locale.org @@ -0,0 +1,45 @@ +#+title: Locale +#+setupfile: ../headers + +* Locale +I want my system messages in English, but everything else about how +dates, money and paper sizes are formatted to reflect where I actually +live. Here’s the =nixos.locale= module. +#+begin_src nix :tangle yes +{ + flake.modules.nixos.locale = { + i18n = { + <> + <> + }; + }; +} +#+end_src + +** Default Locale +=en_DK.UTF-8= is a well-known trick: English messages and collation, but +ISO 8601 dates and sane metric units, instead of =en_US='s nonsense +MM/DD/YYYY and imperial units. +#+name: default-locale +#+begin_src nix +defaultLocale = "en_DK.UTF-8"; +#+end_src + +** Regional Settings +Since I live in France, I want addresses, money, paper size, phone +numbers and the like to follow French conventions instead of whatever +=en_DK= would otherwise pick. +#+name: extra-locale-settings +#+begin_src nix +extraLocaleSettings = { + LC_ADDRESS = "fr_FR.UTF-8"; + LC_IDENTIFICATION = "fr_FR.UTF-8"; + LC_MEASUREMENT = "fr_FR.UTF-8"; + LC_MONETARY = "fr_FR.UTF-8"; + LC_NAME = "fr_FR.UTF-8"; + LC_NUMERIC = "fr_FR.UTF-8"; + LC_PAPER = "fr_FR.UTF-8"; + LC_TELEPHONE = "fr_FR.UTF-8"; + LC_TIME = "fr_FR.UTF-8"; +}; +#+end_src diff --git a/modules/index.org b/modules/index.org new file mode 100644 index 0000000..c9ed816 --- /dev/null +++ b/modules/index.org @@ -0,0 +1,342 @@ +#+title: P’undrak’s Litterate Nix Config +#+setupfile: headers +#+property: header-args:emacs-lisp :lexical t :exports none :tangle no + +* Index +Hi, I’m P’undrak (pronounced /PUN-drak/, or more exactly {{{phon(pʰynɖak̚)}}}), +also known as Lucien Cartier-Tilet. If you want to know more about me, +you can head to my [[https://phundrak.com/en][main website]]. + +This website documents my entire Linux configuration, managed through +NixOS. Most of my programs are configured through Nix, following the +[[https://github.com/Doc-Steve/dendritic-design-with-flake-parts][dendritic pattern]]. + +To give you a tl;dr, this basically means that each aspect of my +configuration (be it a concept or an application) tries to only have a +single source of truth. But as you can see with my Emacs +configuration, some aspects can be quite extensive and require several +pages to be properly organised. + +** License +See [[https://labs.phundrak.com/phundrak/dotfiles/src/branch/master/LICENSE.org][the repository’s license file]]. + +** Note on What a Literate Configuration Is +As implied by the title of this website, my configuration is a +litterate one. This means that every page of this website comes from +an Emacs org-mode file, and the code you see as code blocks are the +actual source of my configuration. + +Org-mode offers to “tangle” these code blocks into full files, which +can then be used as any other source file. If you visit this website’s +repository, you will find the source org files alongside their +resulting Nix file if any is generated by said file. For instance, +this very page generates my =modules/default.nix= file, as we’ll see +below. + +This also implies heavy usage of the [[https://orgmode.org/manual/Noweb-Reference-Syntax.html][noweb]] syntax. If you encounter +some code that looks ~<>~, org-mode will replace this snippet +with another code snippet declared elsewhere in my configuration. If +you see some code that looks ~<>~, some generating code +will run and replace this piece of text with the text generated. A +quick example: +#+begin_src elisp +(defun hello () + <> + <>) +#+end_src + +Will instead appear as +#+begin_src emacs-lisp :noweb yes +(defun hello () + <> + <>) +#+end_src + +This is because I have the block of code below named +~generate-docstring~ which generates an output, which replaces its noweb +tag. You can recognize noweb snippets generating code with the +parenthesis. Often, such blocks aren’t visible in my HTML exports, but +you can still see them if you open the actual org source file. +#+name: generate-docstring +#+begin_src emacs-lisp +(concat "\"" + "Print \\\"Hello World!\\\" in the minibuffer." + "\"") +#+end_src + +On the other hand, noweb snippets without parenthesis simply replace +the snippet with the equivalent named code block. For instance the one +below is named ~print-hello~ and is placed as-is in the target source +block. +#+name: print-hello +#+begin_src emacs-lisp +(message "Hello World!") +#+end_src + +** Root Nix Configuration +As this configuration uses [[https://flake.parts/][flake-parts]] and [[https://flake-file.denful.dev/][flake-file]], I need a +=modules/default.nix= which defines how to manage my config. + +For the record, I use [[https://github.com/nix-community/nh][nh]] to compile my configuration and switch to +newer generations of my OS and home. This allows me to simply run =nh +os switch= to upgrade my system, or =nh home switch= to upgrade my +[[https://nix-community.github.io/home-manager/][home-manager]] configuration. This, therefore, requires having [[https://nixos.wiki/wiki/flakes][flakes]] +outputs in the form of =nixosConfigurations.marpa= (with =marpa= being the +hostname of a machine) and =homeConfigurations.phundrak= or +=homeConfigurations.phundrak@marpa= (with =phundrak= being the username of +one of the users of a machine). + +But first things first, let’s declare the closure that will +encapsulate the rest of the file: +#+begin_src nix :tangle default.nix +{ + inputs, + lib, + config, + ... +}: { + <> + + <> + + config = { + <> + <> + + flake.lib = { + <> + <> + <> + }; + + <> + <> + }; +} +#+end_src + +To get our configuration to work, we need to import the modules from +flake-parts and flake-file. +#+name: modules-imports +#+begin_src nix +imports = [ + inputs.flake-parts.flakeModules.modules + inputs.flake-file.flakeModules.default +]; +#+end_src + +Now, we can declare an option to make =homeConfigurations= available as +an output for our flakes. +#+name: options-home +#+begin_src nix +options.flake.homeConfigurations = lib.mkOption { + type = lib.types.lazyAttrsOf lib.types.raw; + default = {}; +}; +#+end_src + +*** Setting Things Up +We need to declare a few inputs for our flake, thanks to +flake-file. The first one is =flake-utils=, which allows me to easily +declare my development shell for this repository both for my x86-64 +machines and my aarch64 tablet, a PineTab 2. Then, speaking of the +PineTab, I need some specific nixpkgs input, to handle a bug in the +compilation of the kernel, as well as the flake =rockchip= to support +said kernel. +#+name: flake-inputs +#+begin_src nix +flake-file.inputs = { + flake-utils.url = "github:numtide/flake-utils"; + nixpkgsPinetab2Kernel.url = "github:nixos/nixpkgs/e73de5be04e0eff4190a1432b946d469c794e7b4"; + rockchip = { + url = "github:raboof/nixos-rockchip/pinetab-linux-7.0"; + inputs.utils.follows = "flake-utils"; + inputs.nixpkgsStable.follows = "nixpkgsStable"; + inputs.nixpkgsUnstable.follows = "nixpkgsPinetab2Kernel"; + }; +}; +#+end_src + +As I said, I support two architectures for my development shell, so +let’s declare them. +#+name: dev-arch +#+begin_src nix +systems = ["x86_64-linux" "aarch64-linux"]; +#+end_src + +Now, we can declare some functions for our flake. These three +functions’ role is to create the output of each machine and user as +required. First, let’s create the function to get a machine’s +configuration based on its name. +#+name: lib-mkNixos +#+begin_src nix +mkNixos = system: name: { + ${name} = inputs.nixpkgs.lib.nixosSystem { + modules = [ + config.flake.modules.nixos.${name} + {nixpkgs.hostPlatform = lib.mkDefault system;} + ]; + }; +}; +#+end_src + +What this does is basically declare a Nix system named after the +host’s name, created with its module (located in =modules/hosts/=) and +the related nixpkgs with the appropriate architecture. For now, the +only architecture used with this function is x86-64, but I’m not +excluding the possibility to have other hosts using an ARM CPU. + +=mkHome= is somewhat similar: it declares a home-manager module, +importing the module related to the user and the machine it will be +deployed on. +#+name: lib-mkHome +#+begin_src nix +mkHome = system: userName: hostName: { + "${userName}@${hostName}" = inputs.home-manager.lib.homeManagerConfiguration { + pkgs = inputs.nixpkgs.legacyPackages.${system}; + extraSpecialArgs = { + inherit inputs; + bunBaseline = config.flake.packages.${system}.bun-baseline; + }; + modules = [config.flake.modules.homeManager."${userName}-${hostName}"]; + }; +}; +#+end_src + +You may notice the declaration of =bunBaseline=. This is because of my +ThinkPad x220; the default binary distributed for Bun uses CPU +instructions that are more recent than this laptop’s CPU, which +results in fatal errors trying to run it. Therefore, =bunBaseline= is +here to either compile Bun on my ThinkPad with the correct instruction +set, or simply use a prepackaged Bun if the host supports it. + +Lastly, I have a function dedicated to building NixOS on my PineTab 2. +#+name: lib-mkPinetab +#+begin_src nix +mkPinetab = buildPlatform: variantModule: { + pinetab2 = inputs.nixpkgs.lib.nixosSystem { + system = "aarch64-linux"; + modules = [ + inputs.rockchip.nixosModules.sdImageRockchip + inputs.rockchip.nixosModules.dtOverlayPCIeFix + inputs.rockchip.nixosModules.noZFS + config.flake.modules.nixos.pinetab2-base + variantModule + { + rockchip.uBoot = inputs.rockchip.packages.${buildPlatform}.uBootPineTab2; + boot.kernelPackages = + inputs.rockchip.legacyPackages.${buildPlatform}.kernel_linux_7_0_pinetab_unstable; + hardware.firmware = [inputs.rockchip.packages.aarch64-linux.bes2600]; + nixpkgs.config.allowUnfreePredicate = pkg: + builtins.elem (inputs.nixpkgs.lib.getName pkg) ["bes2600-firmware"]; + } + ]; + }; +}; +#+end_src + +I won’t go into too much details here, but it mostly imports the +modules required to run NixOS on the Pinetab as well as explicitly +import the bes2600 firmware module to make Bluetooth and Wi-Fi +available on the tablet. + +*** Declaring the Actual Outputs +With all that being said, we can now actually declare our +configurations. You can see below the table of hosts I have, with +their CPU architecture, and which users are present on the system. + +#+name: table-hosts +| Host | Architecture | Users | Comment | +|----------+---------------+-----------------+------------------| +| marpa | x86_64-linux | phundrak | Main workstation | +| gampo | x86_64-linux | phundrak | Thinkpad x220 | +| tilo | x86_64-linux | phundrak | Home Server | +| elcafe | x86_64-linux | phundrak, creug | Server | +| NaroMk3 | x86_64-linux | phundrak | Cloud Server | +| pinetab2 | aarch64-linux | phundrak | PineTab 2 tablet | + +#+name: make-hosts +#+begin_src emacs-lisp :exports none :var hosts=table-hosts :cache yes +(mapconcat + (lambda (line) + (let ((hostname (car line)) + (arch (nth 1 line))) + (format "(config.flake.lib.mkNixos \"%s\" \"%s\")" + arch + hostname))) + (-filter (lambda (host) (not (string= "pinetab2" (car host)))) + hosts) + "\n") +#+end_src + +#+RESULTS[f5f8b3a89622e7526a83cbf722c4b7c77ff7bd86]: make-hosts +: (config.flake.lib.mkNixos "x86_64-linux" "marpa") +: (config.flake.lib.mkNixos "x86_64-linux" "gampo") +: (config.flake.lib.mkNixos "x86_64-linux" "tilo") +: (config.flake.lib.mkNixos "x86_64-linux" "elcafe") +: (config.flake.lib.mkNixos "x86_64-linux" "NaroMk3") + +#+name: make-home +#+begin_src emacs-lisp :exports none :var hosts=table-hosts :cache yes +(require 's) + +(mapconcat + (lambda (line) + (let ((hostname (car line)) + (arch (nth 1 line)) + (users (mapcar #'s-trim (s-split "," (nth 2 line) t)))) + (mapconcat (lambda (user) + (format "(config.flake.lib.mkHome \"%s\" \"%s\" \"%s\")" + arch user hostname)) + users + "\n"))) + hosts + "\n") +#+end_src + +#+RESULTS[301fccb07591fffc8d7bdfd7d2958602150aa688]: make-home +: (config.flake.lib.mkHome "x86_64-linux" "phundrak" "marpa") +: (config.flake.lib.mkHome "x86_64-linux" "phundrak" "gampo") +: (config.flake.lib.mkHome "x86_64-linux" "phundrak" "tilo") +: (config.flake.lib.mkHome "x86_64-linux" "phundrak" "elcafe") +: (config.flake.lib.mkHome "x86_64-linux" "creug" "elcafe") +: (config.flake.lib.mkHome "x86_64-linux" "phundrak" "NaroMk3") +: (config.flake.lib.mkHome "aarch64-linux" "phundrak" "pinetab2") + +This translates into this Nix code. +#+name: configs-decl +#+begin_src nix :noweb yes +flake.nixosConfigurations = lib.mkMerge [ + <> + (config.flake.lib.mkPinetab "x86_64-linux" config.flake.modules.nixos.pinetab2-gnome) +]; +flake.homeConfigurations = lib.mkMerge [ + <> +]; +#+end_src + +*** Development Shell +Lastly, here is the declaration of my development shell. It really is +only useful if I’m on a new machine or a machine that is not quite up +to date and misses some packages I rely on to work on my dotfiles. +Namely, these are =nh= (which I mentioned above), Jujutsu, =jj-cz= (a +Commitizen alternative for Jujutsu I’m working on), and Git itself as +a fallback. +#+name: devshell +#+begin_src nix +perSystem = { + pkgs, + system, + ... +}: { + formatter = pkgs.alejandra; + devShells.default = pkgs.mkShell { + buildInputs = [ + pkgs.nh + pkgs.jujutsu + pkgs.git + inputs.jj-cz.packages.${system}.default + ]; + }; +}; +#+end_src diff --git a/modules/inputs.nix b/modules/inputs.nix index 622d875..1761653 100644 --- a/modules/inputs.nix +++ b/modules/inputs.nix @@ -11,5 +11,5 @@ }; }; flake-file.outputs = "dendritic"; - flake-file.description = "NixOS and Home Manager configuration of phundrak"; + flake-file.description = "NixOS and Home Manager configuration of P'undrak"; } diff --git a/modules/inputs.org b/modules/inputs.org new file mode 100644 index 0000000..e27426e --- /dev/null +++ b/modules/inputs.org @@ -0,0 +1,82 @@ +#+title: Flake File Setup +#+setupfile: headers + +* Flake File Setup +This configuration uses [[https://flake-file.denful.dev/][flake-file]]. This means my =flake.nix= file is +modular, as it allows me to define my inputs where I need them, and +not necessarily all at the same place. This can be a bit unusual for +people who are new to it, but trust me, it’s well worth it. + +I’ll simply set up the outputs, a single one named =dendritic=, and the +description here, as nothing is too complicated. +#+begin_src nix :tangle yes +{ + <> + flake-file.outputs = "dendritic"; + flake-file.description = "NixOS and Home Manager configuration of P'undrak"; +} +#+end_src + +** Inputs +Some flake imputs are required globally, as they are used by default +by this configuration and some hosts. + +#+name: inputs +#+begin_src nix +flake-file.inputs = { + <> + <> + <> + <> + <> +}; +#+end_src + +First, we get to nixpkgs, which +is quite necessary to get NixOS up and running: it gives access to +Nix’s packages. I also have a =nixpkgsStable= input for the kernel of my +PineTab 2 tablet. Otherwise, I use Nix unstable. + +#+name: inputs-nixpkgs +#+begin_src nix +nixpkgsStable.url = "nixpkgs/nixos-25.11"; +nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; +#+end_src + +Next, I need some inputs for my dendritic config. =flake-parts= is the +heart of it: it’s a framework for writing flake modules that can +easily be put together as a single module defining an entire system, +such as a home configuration or a host configuration. +#+name: inputs-flake-parts +#+begin_src nix +flake-parts.url = "github:hercules-ci/flake-parts"; +#+end_src + +Next, we have flake-file, which permits the modularisation of my +=flake.nix= file itself, as mentioned abve. +#+name: inputs-flake-file +#+begin_src nix +flake-file.url = "github:vic/flake-file"; +#+end_src + +Next, we have =import-tree=, which automatically imports my Nix files, +making all modules globally known. +#+name: inputs-import-tree +#+begin_src nix +import-tree.url = "github:vic/import-tree"; +#+end_src + +And last but not least, =home-manager=. This allows me to manage the +programs of me as the user of my machine and not necessarily the +machine itself, as well as their configuration. As such, I can upgrade +a machine’s system without touching my environment, and the inverse is +true. However, it’s important to keep them in sync when it comes to +major upgrades of NixOS, so home-manager will follow the system’s +version. +#+name: inputs-home-manager +#+begin_src nix +home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; +}; +#+end_src diff --git a/modules/misc.nix b/modules/misc.nix index 3ecf0d4..10a1622 100644 --- a/modules/misc.nix +++ b/modules/misc.nix @@ -7,31 +7,27 @@ with lib; let cfg = config.mySystem.misc; in { - options.mySystem.misc = { - timezone = mkOption { - type = types.str; - default = "Europe/Paris"; - }; - keymap = mkOption { - type = types.str; - default = "fr"; - example = "fr-bepo"; - description = "Keymap to use in the TTY console"; - }; + options.mySystem.misc.timezone = mkOption { + type = types.str; + default = "Europe/Paris"; + }; + options.mySystem.misc.keymap = mkOption { + type = types.str; + default = "fr"; + example = "fr-bepo"; + description = "Keymap to use in the TTY console"; }; config = { - boot.tmp.cleanOnBoot = true; console.keyMap = cfg.keymap; time.timeZone = cfg.timezone; + services.envfs.enable = true; + services.orca.enable = false; + boot.tmp.cleanOnBoot = true; environment.pathsToLink = [ "/share/bash-completion" "/share/zsh" ]; - services = { - orca.enable = false; - envfs.enable = true; - }; }; }; } diff --git a/modules/misc.org b/modules/misc.org new file mode 100644 index 0000000..659f8f0 --- /dev/null +++ b/modules/misc.org @@ -0,0 +1,109 @@ +#+title: Misc NixOS Configurations +#+setupfile: headers + +* Misc NixOS Configurations + +This module hosts some misc configuration I am unsure where to put +elsewhere than here. Don’t expect this file to be coherent, but expect +it to be quite short. + +This module declares the aspect =nixos.misc=, which is used by all my +hosts. + +#+begin_src nix :tangle yes +{ + flake.modules.nixos.misc = { + lib, + config, + ... + }: + with lib; let + cfg = config.mySystem.misc; + in { + <> + <> + + config = { + <> + <> + <> + <> + <> + <> + }; + }; +} +#+end_src + +** System Timezone +First, let me declare the timezone. I’ll set it as an option, as not +all my machines live in the same place, but I’ll default to +Metropolitan France’s timezone. +#+name: timezone-option +#+begin_src nix +options.mySystem.misc.timezone = mkOption { + type = types.str; + default = "Europe/Paris"; +}; +#+end_src + +Now, I can set it for my system. +#+name: timezone-config +#+begin_src nix +time.timeZone = cfg.timezone; +#+end_src + +** TTY Keyboard Layout +Now, I can set the TTY’s keyboard config. Most of my machines use the +Bépo layout everywhere, but I do share one whose owner doesn’t use it, +so I’ll let this as an option to be set, defaulting to the default +French layout. +#+name: layout-option +#+begin_src nix +options.mySystem.misc.keymap = mkOption { + type = types.str; + default = "fr"; + example = "fr-bepo"; + description = "Keymap to use in the TTY console"; +}; +#+end_src + +Now, I can set it on my system. +#+name: layout-config +#+begin_src nix +console.keyMap = cfg.keymap; +#+end_src + +** Truly Miscelaneous Config +First, some scripts are written with the assumption that some +utilities are always in the same place, such as =/bin/bash=. It is, +however, not always the case with NixOS. Mic92’s [[https://github.com/Mic92/envfs][envfs]] solves that. +#+name: envfs +#+begin_src nix +services.envfs.enable = true; +#+end_src + +I have no idea why, but sometimes, [[https://orca.gnome.org/][Orca]] get activated without my +consent. So I hard-disable it here. +#+name: orca +#+begin_src nix +services.orca.enable = false; +#+end_src + +I was surprised to see =/tmp= still hold the same files after my first +reboot in NixOS, I always assumed it was cleared on every reboot on +every system. But I can enable this behaviour back. +#+name: clean-tmp +#+begin_src nix +boot.tmp.cleanOnBoot = true; +#+end_src + +Lastly, I want to make sure my shell completions work, so I’ll enable +this. +#+name: completion +#+begin_src nix +environment.pathsToLink = [ + "/share/bash-completion" + "/share/zsh" +]; +#+end_src diff --git a/modules/network/networking.nix b/modules/network/networking.nix index 81fc4aa..8c0e220 100644 --- a/modules/network/networking.nix +++ b/modules/network/networking.nix @@ -7,74 +7,70 @@ with lib; let cfg = config.mySystem.networking; in { - options.mySystem.networking = with types; { - hostname = mkOption { - type = str; - example = "gampo"; - }; - id = mkOption { - type = str; - example = "deadb33f"; - }; - domain = mkOption { - type = nullOr str; - example = "phundrak.com"; - default = null; - }; - hostFiles = mkOption { - type = listOf path; - example = [/path/to/hostFile]; - default = []; - }; - firewall = { - openPorts = mkOption { - type = listOf int; - example = [22 80 443]; - default = []; - }; - openPortRanges = mkOption { - type = listOf (attrsOf port); - default = []; - example = [ - { - from = 8080; - to = 8082; - } - ]; - description = '' - A range of TCP and UDP ports on which incoming connections are - accepted. - ''; - }; - extraCommands = mkOption { - type = nullOr lines; - example = "iptables -A INPUTS -p icmp -j ACCEPT"; - default = null; - }; - }; - wifi.disablePowersave = mkEnableOption '' - Disables powersave for Wifi. + options.mySystem.networking.hostname = mkOption { + type = types.str; + example = "gampo"; + }; + config.networking.hostName = cfg.hostname; + options.mySystem.networking.id = mkOption { + type = types.str; + example = "deadb33f"; + }; + config.networking.hostId = cfg.id; + options.mySystem.networking.domain = mkOption { + type = types.nullOr types.str; + example = "phundrak.com"; + default = null; + }; + config.networking.domain = cfg.domain; + options.mySystem.networking.hostFiles = mkOption { + type = types.listOf types.path; + example = [/path/to/hostFile]; + default = []; + }; + config.networking.hostFiles = cfg.hostFiles; + options.mySystem.networking.wifi.disablePowersave = mkEnableOption '' + Disables powersave for Wifi. - Used mainly for the PineTab2, as leaving WiFi powersave with the bes2600 can cause stability issues. + Used mainly for the PineTab2, as leaving WiFi powersave with the bes2600 can cause stability issues. + ''; + config.networking.networkmanager = { + enable = true; + wifi.powersave = !cfg.wifi.disablePowersave; + }; + options.mySystem.networking.firewall.openPorts = mkOption { + type = types.listOf types.int; + example = [22 80 443]; + default = []; + }; + config.networking.firewall = { + enable = true; + allowedTCPPorts = cfg.firewall.openPorts; + allowedUDPPorts = cfg.firewall.openPorts; + }; + options.mySystem.networking.firewall.openPortRanges = mkOption { + type = types.listOf (types.attrsOf types.port); + default = []; + example = [ + { + from = 8080; + to = 8082; + } + ]; + description = '' + A range of TCP and UDP ports on which incoming connections are + accepted. ''; }; - - config.networking = { - hostName = cfg.hostname; # Define your hostname. - hostId = cfg.id; - networkmanager = { - enable = true; - wifi.powersave = ! cfg.wifi.disablePowersave; - }; - inherit (cfg) hostFiles domain; - firewall = { - enable = true; - allowedTCPPorts = cfg.firewall.openPorts; - allowedUDPPorts = cfg.firewall.openPorts; - allowedTCPPortRanges = cfg.firewall.openPortRanges; - allowedUDPPortRanges = cfg.firewall.openPortRanges; - extraCommands = (mkIf (cfg.firewall.extraCommands != null)) cfg.firewall.extraCommands; - }; + config.networking.firewall = { + allowedTCPPortRanges = cfg.firewall.openPortRanges; + allowedUDPPortRanges = cfg.firewall.openPortRanges; }; + options.mySystem.networking.firewall.extraCommands = mkOption { + type = types.nullOr types.lines; + example = "iptables -A INPUTS -p icmp -j ACCEPT"; + default = null; + }; + config.networking.firewall.extraCommands = mkIf (cfg.firewall.extraCommands != null) cfg.firewall.extraCommands; }; } diff --git a/modules/network/networking.org b/modules/network/networking.org new file mode 100644 index 0000000..3043d90 --- /dev/null +++ b/modules/network/networking.org @@ -0,0 +1,151 @@ +#+title: Networking +#+setupfile: ../headers + +* Networking +This module centralises the basic networking identity of a host — +hostname, host ID, domain, NetworkManager and the firewall — behind a +single =mySystem.networking= namespace. Here’s the skeleton of the +=nixos.networking= module. +#+begin_src nix :tangle yes +{...}: { + flake.modules.nixos.networking = { + lib, + config, + ... + }: + with lib; let + cfg = config.mySystem.networking; + in { + <> + <> + <> + <> + <> + <> + <> + <> + }; +} +#+end_src + +** Hostname +#+name: hostname +#+begin_src nix +options.mySystem.networking.hostname = mkOption { + type = types.str; + example = "gampo"; +}; +config.networking.hostName = cfg.hostname; +#+end_src + +** Host ID +Every host needs a unique =hostId=; besides identifying the machine on +the network, ZFS also uses it to guard against accidentally importing +a pool that’s still active on another machine (see [[file:../boot/zfs.org][ZFS Support]]). +#+name: host-id +#+begin_src nix +options.mySystem.networking.id = mkOption { + type = types.str; + example = "deadb33f"; +}; +config.networking.hostId = cfg.id; +#+end_src + +** Domain +Not every host needs a domain name, so this defaults to =null=. +#+name: domain +#+begin_src nix +options.mySystem.networking.domain = mkOption { + type = types.nullOr types.str; + example = "phundrak.com"; + default = null; +}; +config.networking.domain = cfg.domain; +#+end_src + +** Extra Host Files +=hostFiles= lets a host point at extra files to merge into =/etc/hosts=, +on top of whatever NixOS generates itself. +#+name: host-files +#+begin_src nix +options.mySystem.networking.hostFiles = mkOption { + type = types.listOf types.path; + example = [/path/to/hostFile]; + default = []; +}; +config.networking.hostFiles = cfg.hostFiles; +#+end_src + +** NetworkManager and WiFi Powersave +NetworkManager is always enabled; the only thing a host can tune here +is WiFi powersave. I mainly need to turn it off on the PineTab2, since +leaving powersave on with its =bes2600= WiFi chip causes stability +issues. +#+name: wifi-powersave +#+begin_src nix +options.mySystem.networking.wifi.disablePowersave = mkEnableOption '' + Disables powersave for Wifi. + + Used mainly for the PineTab2, as leaving WiFi powersave with the bes2600 can cause stability issues. +''; +config.networking.networkmanager = { + enable = true; + wifi.powersave = !cfg.wifi.disablePowersave; +}; +#+end_src + +** Firewall: Ports +This also turns the firewall itself on; =openPorts= is just the plain +list of single ports to open, on both TCP and UDP. +#+name: firewall-ports +#+begin_src nix +options.mySystem.networking.firewall.openPorts = mkOption { + type = types.listOf types.int; + example = [22 80 443]; + default = []; +}; +config.networking.firewall = { + enable = true; + allowedTCPPorts = cfg.firewall.openPorts; + allowedUDPPorts = cfg.firewall.openPorts; +}; +#+end_src + +** Firewall: Port Ranges +=openPortRanges= does the same, but for a contiguous range of ports at +once, again on both TCP and UDP. +#+name: firewall-port-ranges +#+begin_src nix +options.mySystem.networking.firewall.openPortRanges = mkOption { + type = types.listOf (types.attrsOf types.port); + default = []; + example = [ + { + from = 8080; + to = 8082; + } + ]; + description = '' + A range of TCP and UDP ports on which incoming connections are + accepted. + ''; +}; +config.networking.firewall = { + allowedTCPPortRanges = cfg.firewall.openPortRanges; + allowedUDPPortRanges = cfg.firewall.openPortRanges; +}; +#+end_src + +** Firewall: Extra Commands +For anything the declarative options above can’t express, =extraCommands= +lets a host drop raw =iptables= commands straight into the firewall +setup. +#+name: firewall-extra-commands +#+begin_src nix +options.mySystem.networking.firewall.extraCommands = mkOption { + type = types.nullOr types.lines; + example = "iptables -A INPUTS -p icmp -j ACCEPT"; + default = null; +}; +config.networking.firewall.extraCommands = mkIf (cfg.firewall.extraCommands != null) cfg.firewall.extraCommands; +#+end_src diff --git a/modules/network/tailscale.nix b/modules/network/tailscale.nix index c2a8614..53bccb1 100644 --- a/modules/network/tailscale.nix +++ b/modules/network/tailscale.nix @@ -1,25 +1,12 @@ -{...}: { +{ flake.modules.nixos.tailscale = { - lib, - config, - ... - }: - with lib; let - cfg = config.mySystem.network.tailscale; - in { - options.mySystem.network.tailscale = { - enable = mkOption { - type = types.bool; - default = true; - }; - }; - config.services.tailscale = { - inherit (cfg) enable; - extraSetFlags = [ - "--accept-dns" - "--accept-routes" - "--ssh" - ]; - }; + services.tailscale = { + enable = true; + extraSetFlags = [ + "--accept-dns" + "--accept-routes" + "--ssh" + ]; }; + }; } diff --git a/modules/network/tailscale.org b/modules/network/tailscale.org new file mode 100644 index 0000000..d8bcb8e --- /dev/null +++ b/modules/network/tailscale.org @@ -0,0 +1,52 @@ +#+title: Tailscale +#+setupfile: ../headers + +* Tailscale +I use [[https://tailscale.com/][Tailscale]] as the mesh VPN tying all my machines together. Here’s +the =nixos.tailscale= module. +#+begin_src nix :tangle yes +{ + flake.modules.nixos.tailscale = { + services.tailscale = { + <> + <> + }; + }; +} +#+end_src + +** Enabling Tailscale +#+name: enable +#+begin_src nix +enable = true; +#+end_src + +** Extra Flags +I add some extra flags for Tailscale. + +#+name: flags +| Flag | Why | +|-----------------+---------------------------------------------------------| +| =--accept-dns= | Turns on MagicDNS | +| =--accept-routes= | Let this machine use subnets advertised by other nodes | +| =--ssh= | Turns on Tailscale’s own SSH server for easy SSH access | + +#+name: make-flags +#+begin_src emacs-lisp :exports none :var flags=flags :cache yes +(mapconcat (lambda (flag) + (replace-regexp-in-string "=" "\"" (car flag))) + flags + "\n") +#+end_src + +#+RESULTS[4969e8a817fa6617e136b57d47a43d6e21ce2a7b]: make-flags +: "--accept-dns" +: "--accept-routes" +: "--ssh" + +#+name: extra-flags +#+begin_src nix +extraSetFlags = [ + <> +]; +#+end_src diff --git a/modules/nix-config.nix b/modules/nix-config.nix index a2b7040..9a98784 100644 --- a/modules/nix-config.nix +++ b/modules/nix-config.nix @@ -1,5 +1,5 @@ -{...}: let - cacheSettings = { +let + cacheSettings = rec { substituters = [ "https://phundrak.cachix.org?priority=10" "https://nix-community.cachix.org?priority=20" @@ -10,28 +10,22 @@ "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" ]; + commonConf = { + extra-trusted-public-keys = trustedPublicKeys; + extra-substituters = substituters; + extra-experimental-features = ["nix-command" "flakes"]; + http-connections = 128; + }; }; in { - flake-file.nixConfig = { - extra-trusted-public-keys = cacheSettings.trustedPublicKeys; - extra-substituters = cacheSettings.substituters; - extra-experimental-features = ["nix-command" "flakes"]; - http-connections = 128; - }; - - flake.nixConfig = { - extra-trusted-public-keys = cacheSettings.trustedPublicKeys; - extra-substituters = cacheSettings.substituters; - extra-experimental-features = ["nix-command" "flakes"]; - http-connections = 128; - }; - + flake-file.nixConfig = cacheSettings.commonConf; + flake.nixConfig = cacheSettings.commonConf; flake.modules.nixos.nix-cache-settings = { nix.settings = { - substituters = cacheSettings.substituters; + inherit (cacheSettings) substituters; trusted-public-keys = cacheSettings.trustedPublicKeys; - http-connections = 128; experimental-features = ["nix-command" "flakes"]; + http-connections = 128; }; }; } diff --git a/modules/nix-config.org b/modules/nix-config.org new file mode 100644 index 0000000..264b58a --- /dev/null +++ b/modules/nix-config.org @@ -0,0 +1,81 @@ +#+title: Nix Configuration +#+setupfile: headers +#+property: header-args:emacs-lisp :lexical t :exports none :tangle no + +* Nix Configuration +Something that I want to enable everywhere is, first and foremost, the +support for Nix commands and Nix flakes. I also want to make sure I +can use some caches, also known as substituters, including one of mine +from Cachix, to avoid compiling stuff as much as possible. + +So first, here are my caches with their public key. + +#+name: substituters +| Substituter's URL | Public Key | +|----------------------------------------------+-------------------------------------------------------------------------| +| https://phundrak.cachix.org?priority=10 | =phundrak.cachix.org-1:osJAkYO0ioTOPqaQCIXMfIRz1/+YYlVFkup3R2KSexk== | +| https://nix-community.cachix.org?priority=20 | =nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs== | +| https://cache.nixos.org?priority=40 | =cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY== | + +#+name: substituters-urls +#+begin_src emacs-lisp :var subs=substituters :cache yes +(mapconcat (lambda (sub) (format "\"%s\"" (car sub))) subs "\n") +#+end_src + +#+RESULTS[99d05698d7e8ca90b34823f4dd4858224be8d007]: substituters-urls +: "https://phundrak.cachix.org?priority=10" +: "https://nix-community.cachix.org?priority=20" +: "https://cache.nixos.org?priority=40" + +#+name: substituters-keys +#+begin_src emacs-lisp :var subs=substituters :cache yes +(require 's) +(mapconcat (lambda (sub) (format "\"%s\"" + (s-chop-prefix "=" (s-chop-suffix "=" (cadr sub))))) + subs + "\n") +#+end_src + +#+RESULTS[6f5e709a7b1c1dd55e4187dfaf8be945138c68ec]: substituters-keys +: "phundrak.cachix.org-1:osJAkYO0ioTOPqaQCIXMfIRz1/+YYlVFkup3R2KSexk=" +: "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" +: "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" + +This results in the following substituters and trusted public keys. +#+name: config-vars +#+begin_src nix :noweb yes +substituters = [ + <> +]; +trustedPublicKeys = [ + <> +]; +#+end_src + +Now, we can declare the rest of the file. You’ll see, it repeats +itself a bit. + +#+begin_src nix :tangle yes +let + cacheSettings = rec { + <> + commonConf = { + extra-trusted-public-keys = trustedPublicKeys; + extra-substituters = substituters; + extra-experimental-features = ["nix-command" "flakes"]; + http-connections = 128; + }; + }; +in { + flake-file.nixConfig = cacheSettings.commonConf; + flake.nixConfig = cacheSettings.commonConf; + flake.modules.nixos.nix-cache-settings = { + nix.settings = { + inherit (cacheSettings) substituters; + trusted-public-keys = cacheSettings.trustedPublicKeys; + experimental-features = ["nix-command" "flakes"]; + http-connections = 128; + }; + }; +} +#+end_src diff --git a/modules/packages/appimage.nix b/modules/packages/appimage.nix index 2f583f7..2433e3f 100644 --- a/modules/packages/appimage.nix +++ b/modules/packages/appimage.nix @@ -1,16 +1,8 @@ { flake.modules.nixos.appimage = { - lib, - config, - ... - }: - with lib; let - cfg = config.mySystem.packages.appimage; - in { - options.mySystem.packages.appimage.enable = mkEnableOption "Enables AppImage support"; - config.programs.appimage = mkIf cfg.enable { - inherit (cfg) enable; - binfmt = true; - }; + programs.appimage = { + enable = true; + binfmt = true; }; + }; } diff --git a/modules/packages/appimage.org b/modules/packages/appimage.org new file mode 100644 index 0000000..03eead0 --- /dev/null +++ b/modules/packages/appimage.org @@ -0,0 +1,24 @@ +#+title: AppImage +#+setupfile: ../headers + +* AppImage +A small module to let AppImages run directly, without having to +extract or wrap them by hand first. Here’s the =nixos.appimage= module. +#+begin_src nix :tangle yes +{ + flake.modules.nixos.appimage = { + <> + }; +} +#+end_src + +=binfmt= registers AppImages with the kernel’s =binfmt_misc=, so running +one is as simple as executing it directly, the same as any other +binary. +#+name: enable-appimage +#+begin_src nix +programs.appimage = { + enable = true; + binfmt = true; +}; +#+end_src diff --git a/modules/packages/flatpak.nix b/modules/packages/flatpak.nix index 6691744..cfe61f8 100644 --- a/modules/packages/flatpak.nix +++ b/modules/packages/flatpak.nix @@ -8,11 +8,8 @@ with lib; let cfg = config.mySystem.packages.flatpak; in { - options.mySystem.packages.flatpak = { - enable = mkEnableOption "Enable Flatpak support"; - builder.enable = mkEnableOption "Enable Flatpak builder"; - }; - config = mkIf cfg.enable { + options.mySystem.packages.flatpak.builder.enable = mkEnableOption "Enable Flatpak builder"; + config = { environment.systemPackages = lists.optional cfg.builder.enable pkgs.flatpak-builder; services.flatpak.enable = true; };