commit dda5d14d634144a5aa47a232195b225c8deb1304 Author: ahmza Date: Wed Aug 5 18:57:23 2026 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f8011a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.session diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..10bde67 --- /dev/null +++ b/flake.lock @@ -0,0 +1,80 @@ +{ + "nodes": { + "flake-file": { + "locked": { + "lastModified": 1785193256, + "narHash": "sha256-KsEoDoDbXp1PJqp2s7U64rfxZ6J5TS9utLNM4r6tEzs=", + "owner": "vic", + "repo": "flake-file", + "rev": "cddd6489b9ef8d20b50a57851763c1931390dc2b", + "type": "github" + }, + "original": { + "owner": "vic", + "repo": "flake-file", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1782949081, + "narHash": "sha256-vp6Y/Grm98ESt6ceOkWiHWyZRDV3J1RID4w+6NWK9yA=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "17c9d6cdfc60c64f4ee8d306f9bc0b4ccb51481e", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "import-tree": { + "locked": { + "lastModified": 1784254960, + "narHash": "sha256-iI88R3wHz8wTKQb5orvpc51L/Xr64AJyxid/0MKa/b8=", + "owner": "vic", + "repo": "import-tree", + "rev": "4ebb10ae17d5f1ad366e7aef5b92cb8eecf24f69", + "type": "github" + }, + "original": { + "owner": "vic", + "repo": "import-tree", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1785734586, + "narHash": "sha256-ODZkEK9Gy50yg6h98u7KkitZ3oc/uuTFK00bh1CRdNA=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "531670d871c0e29724a02f3cbcac170adc65b58c", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-26.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-file": "flake-file", + "flake-parts": "flake-parts", + "import-tree": "import-tree", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..356d3d5 --- /dev/null +++ b/flake.nix @@ -0,0 +1,15 @@ +# DO-NOT-EDIT. This file was auto-generated using github:vic/flake-file. +# Use `nix run .#write-flake` to regenerate it. +{ + outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } (inputs.import-tree ./modules); + + inputs = { + flake-file.url = "github:vic/flake-file"; + flake-parts = { + url = "github:hercules-ci/flake-parts"; + inputs.nixpkgs-lib.follows = "nixpkgs"; + }; + import-tree.url = "github:vic/import-tree"; + nixpkgs.url = "github:nixos/nixpkgs/nixos-26.05"; + }; +} diff --git a/modules/attrs/bottles/default.nix b/modules/attrs/bottles/default.nix new file mode 100644 index 0000000..649c7f3 --- /dev/null +++ b/modules/attrs/bottles/default.nix @@ -0,0 +1,19 @@ +{ + self, + moduleWithSystem, + ... +}: { + flake.nixosModules.bottles = moduleWithSystem ({pkgs, ...}: { + environment.systemPackages = with pkgs; [ + (bottles.override { + removeWarningPopup = true; + }) + wineWow64Packages.stable + winetricks + wine + wine64 + freetype + ]; + }); +} + diff --git a/modules/attrs/development/default.nix b/modules/attrs/development/default.nix new file mode 100644 index 0000000..e92c771 --- /dev/null +++ b/modules/attrs/development/default.nix @@ -0,0 +1,23 @@ +{ + self, + moduleWithSystem, + ... +}: { + flake.nixosModules.development = moduleWithSystem ({pkgs, ...}: let + modules = with self.nixosModules; [ + git + nvim + opencode-mcp + bun + ssh-agent + ]; + in { + imports = modules; + environment.systemPackages = with pkgs; [ + opencode + devenv + jellyfin-tui + ]; + }); +} + diff --git a/modules/attrs/gaming/default.nix b/modules/attrs/gaming/default.nix new file mode 100644 index 0000000..12ee4e9 --- /dev/null +++ b/modules/attrs/gaming/default.nix @@ -0,0 +1,14 @@ +{ + self, + moduleWithSystem, + ... +}: { + flake.nixosModules.gaming = moduleWithSystem ({...}: let + modules = with self.nixosModules; [ + steam + ]; + in { + imports = modules; + }); +} + diff --git a/modules/dendritic.nix b/modules/dendritic.nix new file mode 100644 index 0000000..68befe8 --- /dev/null +++ b/modules/dendritic.nix @@ -0,0 +1,16 @@ +{ inputs, ... }: +{ + imports = [ + inputs.flake-file.flakeModules.dendritic + ]; + + flake-file.inputs = { + flake-file.url = "github:vic/flake-file"; + flake-parts = { + url = "github:hercules-ci/flake-parts"; + inputs.nixpkgs-lib.follows = "nixpkgs"; + }; + import-tree.url = "github:vic/import-tree"; + nixpkgs.url = "github:nixos/nixpkgs/nixos-26.05"; + }; +} diff --git a/modules/features/bun/default.nix b/modules/features/bun/default.nix new file mode 100644 index 0000000..5b36b26 --- /dev/null +++ b/modules/features/bun/default.nix @@ -0,0 +1,8 @@ +{...}: { + # Bun JavaScript runtime / package manager / bundler. + flake.nixosModules.bun = {pkgs, ...}: { + environment.systemPackages = with pkgs; [ + bun + ]; + }; +} diff --git a/modules/features/discord/default.nix b/modules/features/discord/default.nix new file mode 100644 index 0000000..9210a25 --- /dev/null +++ b/modules/features/discord/default.nix @@ -0,0 +1,8 @@ +{...}: { + # Discord. Launched as `discord` by the hypr autostart (workspace 8). + flake.nixosModules.discord = {pkgs, ...}: { + environment.systemPackages = with pkgs; [ + discord + ]; + }; +} diff --git a/modules/features/feishin/default.nix b/modules/features/feishin/default.nix new file mode 100644 index 0000000..05eade8 --- /dev/null +++ b/modules/features/feishin/default.nix @@ -0,0 +1,12 @@ +{...}: { + # Feishin music client (Jellyfin/Navidrome/Subsonic). + # Note: hypr autostart currently launches this via `flatpak run + # org.jeffvli.feishin` on workspace 9. To use this native package instead, + # change that line to `uwsm app feishin`. The window class is `feishin`, + # which already matches the window-rule in window-rules.lua. + flake.nixosModules.feishin = {pkgs, ...}: { + environment.systemPackages = with pkgs; [ + feishin + ]; + }; +} diff --git a/modules/features/fuzzel/default.nix b/modules/features/fuzzel/default.nix new file mode 100644 index 0000000..dcd7e0b --- /dev/null +++ b/modules/features/fuzzel/default.nix @@ -0,0 +1,9 @@ +{...}: { + # Package only. Config is managed via stow (dotfiles: no dedicated fuzzel + # package yet; fuzzel uses its defaults / ~/.config/fuzzel if present). + flake.nixosModules.fuzzel = {pkgs, ...}: { + environment.systemPackages = with pkgs; [ + fuzzel + ]; + }; +} diff --git a/modules/features/ghostty/default.nix b/modules/features/ghostty/default.nix new file mode 100644 index 0000000..a12222e --- /dev/null +++ b/modules/features/ghostty/default.nix @@ -0,0 +1,9 @@ +{...}: { + # Package only. Config is managed via stow (dotfiles: ghostty package). + flake.nixosModules.ghostty = {pkgs, ...}: { + environment.systemPackages = with pkgs; [ + ghostty + ]; + ahmza.stow.packages = ["ghostty"]; + }; +} diff --git a/modules/features/git/default.nix b/modules/features/git/default.nix new file mode 100644 index 0000000..2802daf --- /dev/null +++ b/modules/features/git/default.nix @@ -0,0 +1,14 @@ +{...}: { + # git user config (~/.config/git or ~/.gitconfig) is managed via stow. + # We only install git + related tooling and enable the gpg agent. + flake.nixosModules.git = {pkgs, ...}: { + environment.systemPackages = with pkgs; [ + git + git-secret + ]; + programs.gnupg.agent = { + enable = true; + pinentryPackage = pkgs.pinentry-all; + }; + }; +} diff --git a/modules/features/hyprland/default.nix b/modules/features/hyprland/default.nix new file mode 100644 index 0000000..8b38c4b --- /dev/null +++ b/modules/features/hyprland/default.nix @@ -0,0 +1,58 @@ +{self, ...}: { + # Hyprland desktop. The compositor config itself (hyprland.lua + ahmza/*.lua, + # hyprlock, hypridle, hyprsunset) is managed via stow (dotfiles: hypr package). + # Here we enable Hyprland and install the runtime tools that config + the + # helper scripts in the `bin` package expect on PATH. + flake.nixosModules.hyprland = {pkgs, ...}: let + modules = with self.nixosModules; [ + audio + systemTheme + fuzzel + ghostty + waybar + ]; + in { + imports = modules; + + programs.hyprland = { + enable = true; + withUWSM = true; + }; + + # hypr = compositor config, dunst = notifications, wal = pywal templates, + # bin = helper scripts (swp/launch-waybar/passmenu) referenced by keybinds. + ahmza.stow.packages = ["hypr" "dunst" "wal" "bin"]; + + services.displayManager.defaultSession = "hyprland-uwsm"; + services.xserver.enable = true; + security.polkit.enable = true; + + environment.systemPackages = with pkgs; [ + # hypr* ecosystem (keybinds / hypridle / autostart) + hyprlock + hypridle + hyprsunset + hyprpicker + # launcher / file manager / browser (vars.lua: Picker/FileManager/Browser) + kdePackages.dolphin + firefox + # wallpaper daemon used by the `swp` script + autostart (awww-daemon) + awww + # scripting deps for bin/ helpers (swp uses jq + hyprctl) + jq + # display / applets referenced by autostart + xorg.xrandr + networkmanagerapplet + blueman + udiskie + brightnessctl + # clipboard + wl-clipboard + cliphist + # theming + gsettings-desktop-schemas + dunst + pywal16 + ]; + }; +} diff --git a/modules/features/lazygit/default.nix b/modules/features/lazygit/default.nix new file mode 100644 index 0000000..7050f30 --- /dev/null +++ b/modules/features/lazygit/default.nix @@ -0,0 +1,8 @@ +{...}: { + # lazygit config is managed via stow. Package only. + flake.nixosModules.lazygit = {pkgs, ...}: { + environment.systemPackages = with pkgs; [ + lazygit + ]; + }; +} diff --git a/modules/features/nvim/default.nix b/modules/features/nvim/default.nix new file mode 100644 index 0000000..360525b --- /dev/null +++ b/modules/features/nvim/default.nix @@ -0,0 +1,15 @@ +{...}: { + flake.nixosModules.nvim = {pkgs, ...}: { + programs.neovim = { + enable = true; + defaultEditor = true; + viAlias = true; + vimAlias = true; + }; + + ahmza.stow.packages = ["nvim"]; + environment.systemPackages = with pkgs; [ + # TODO: re-add your custom neovim config/distribution here later. + ]; + }; +} diff --git a/modules/features/obsidian/default.nix b/modules/features/obsidian/default.nix new file mode 100644 index 0000000..ddc5554 --- /dev/null +++ b/modules/features/obsidian/default.nix @@ -0,0 +1,11 @@ +{...}: { + # Obsidian notes app. + # Note: hypr autostart currently launches this via `flatpak run + # mc.obsidian.Obsidian` on workspace 4. To use this native package instead, + # change that line to `uwsm app obsidian`. + flake.nixosModules.obsidian = {pkgs, ...}: { + environment.systemPackages = with pkgs; [ + obsidian + ]; + }; +} diff --git a/modules/features/opencode-mcp/default.nix b/modules/features/opencode-mcp/default.nix new file mode 100644 index 0000000..4eecbb5 --- /dev/null +++ b/modules/features/opencode-mcp/default.nix @@ -0,0 +1,44 @@ +{...}: { + # opencode MCP servers, installed declaratively instead of via the dotfiles + # `install` script (which downloads prebuilt binaries into ~/.local/bin). + # - gitea-mcp : packaged in nixpkgs as gitea-mcp-server (binary: gitea-mcp) + # - engram : not in nixpkgs, built here from source with buildGoModule + # Both are referenced by bare command name in opencode.jsonc, so they resolve + # from PATH. + flake.nixosModules.opencode-mcp = {pkgs, ...}: let + engram = pkgs.buildGoModule rec { + pname = "engram"; + version = "1.20.0"; + + src = pkgs.fetchFromGitHub { + owner = "Gentleman-Programming"; + repo = "engram"; + rev = "v${version}"; + hash = "sha256-qdKAll7N0HtJRbZYilzatVCUz1Tr+pqM217Y8O+Csjs="; + }; + + vendorHash = "sha256-O+pC4x4DKNUWr7Sx9iZOjK6a64wrQA4/lnjvkNLBX64="; + + subPackages = ["cmd/engram"]; + + # Inject the version (upstream sets main.version via goreleaser ldflags). + ldflags = ["-s" "-w" "-X" "main.version=${version}"]; + + # Upstream tests bind network ports (autosync), which fails in the Nix + # build sandbox. The binary itself builds fine. + doCheck = false; + + meta = with pkgs.lib; { + description = "Persistent memory MCP server for coding agents"; + homepage = "https://github.com/Gentleman-Programming/engram"; + license = licenses.mit; + mainProgram = "engram"; + }; + }; + in { + environment.systemPackages = [ + pkgs.gitea-mcp-server # provides the `gitea-mcp` binary + engram + ]; + }; +} diff --git a/modules/features/quickshell/default.nix b/modules/features/quickshell/default.nix new file mode 100644 index 0000000..e61f840 --- /dev/null +++ b/modules/features/quickshell/default.nix @@ -0,0 +1,9 @@ +{...}: { + # TODO: quickshell config (shell.qml etc.) previously came from a dedicated flake input + # and was built via the wrappers flake. Re-add your quickshell config later. + flake.nixosModules.quickshell = {pkgs, ...}: { + environment.systemPackages = with pkgs; [ + quickshell + ]; + }; +} diff --git a/modules/features/sddm/autologin.nix b/modules/features/sddm/autologin.nix new file mode 100644 index 0000000..fd5e637 --- /dev/null +++ b/modules/features/sddm/autologin.nix @@ -0,0 +1,9 @@ +{...}: { + flake.nixosModules.sddm-autologin = {...}: { + services.displayManager = { + autoLogin.enable = true; + autoLogin.user = "ahmza"; + }; + }; +} + diff --git a/modules/features/sddm/default.nix b/modules/features/sddm/default.nix new file mode 100644 index 0000000..7f4f06f --- /dev/null +++ b/modules/features/sddm/default.nix @@ -0,0 +1,26 @@ +{ + self, + inputs, + ... +}: { + flake.nixosModules.sddm = { + pkgs, + lib, + ... + }: { + environment.systemPackages = [ + (pkgs.catppuccin-sddm.override { + flavor = "mocha"; + font = "JetBrainsMono Nerd Font"; + fontSize = "11"; + background = null; + }) + ]; + services.displayManager.sddm = { + enable = true; + theme = "catppuccin-mocha-mauve"; + package = pkgs.kdePackages.sddm; + }; + }; +} + diff --git a/modules/features/ssh-agent/default.nix b/modules/features/ssh-agent/default.nix new file mode 100644 index 0000000..9c9d9fe --- /dev/null +++ b/modules/features/ssh-agent/default.nix @@ -0,0 +1,17 @@ +{...}: { + # System-managed ssh-agent. Enabling startAgent runs an ssh-agent per user + # session and exports SSH_AUTH_SOCK automatically, so the dotfiles zsh + # `ssh-agent` bootstrap finds a reachable agent (its Linux fallback hardcodes + # /usr/bin/ssh-agent, which does not exist on NixOS). + flake.nixosModules.ssh-agent = {pkgs, ...}: { + programs.ssh = { + startAgent = true; + # ed25519-sk / passphrase prompts route through this pinentry. + enableAskPassword = true; + }; + + environment.systemPackages = with pkgs; [ + openssh # provides ssh-add / ssh-agent on PATH + ]; + }; +} diff --git a/modules/features/steam/default.nix b/modules/features/steam/default.nix new file mode 100644 index 0000000..fbcacac --- /dev/null +++ b/modules/features/steam/default.nix @@ -0,0 +1,12 @@ +{...}: { + # TODO: previously injected SLSsteam via LD_AUDIT from the sls-steam flake. Now stock. + flake.nixosModules.steam = {pkgs, ...}: { + programs.steam = { + enable = true; + protontricks.enable = true; + extraCompatPackages = with pkgs; [ + proton-ge-bin + ]; + }; + }; +} diff --git a/modules/features/waybar/default.nix b/modules/features/waybar/default.nix new file mode 100644 index 0000000..0d30984 --- /dev/null +++ b/modules/features/waybar/default.nix @@ -0,0 +1,14 @@ +{...}: { + # Package only. Config is managed via stow (dotfiles: waybar package). + flake.nixosModules.waybar = {pkgs, ...}: { + environment.systemPackages = with pkgs; [ + waybar + # waybar config on-click / modules deps: + mpc # mpd module: "mpc toggle/random" + # nm-connection-editor comes from networkmanagerapplet (hyprland module); + # pavucontrol comes from the audio module. + inotify-tools + ]; + ahmza.stow.packages = ["waybar"]; + }; +} diff --git a/modules/features/weylus/default.nix b/modules/features/weylus/default.nix new file mode 100644 index 0000000..74c5a6b --- /dev/null +++ b/modules/features/weylus/default.nix @@ -0,0 +1,9 @@ +{...}: { + flake.nixosModules.weylus = {pkgs, ...}: { + programs.weylus = { + enable = true; + package = pkgs.weylus; + users = ["ahmza"]; + }; + }; +} diff --git a/modules/features/zsh/default.nix b/modules/features/zsh/default.nix new file mode 100644 index 0000000..3ad5f69 --- /dev/null +++ b/modules/features/zsh/default.nix @@ -0,0 +1,24 @@ +{...}: { + # zsh runtime config (.zshrc, plugins, aliases, prompt) is managed via stow + # (dotfiles: zsh + shell + starship packages). We only enable zsh at the system + # level and install the tools the dotfiles expect on PATH. + flake.nixosModules.zsh = {pkgs, ...}: { + programs.zsh.enable = true; + users.defaultUserShell = pkgs.zsh; + + ahmza.stow.packages = ["zsh" "shell" "starship"]; + + environment.systemPackages = with pkgs; [ + starship + fzf + ripgrep + fd + lsd + lolcat + zoxide + bat + lazygit + carapace + ]; + }; +} diff --git a/modules/hosts/t480/default.nix b/modules/hosts/t480/default.nix new file mode 100644 index 0000000..4a4e954 --- /dev/null +++ b/modules/hosts/t480/default.nix @@ -0,0 +1,23 @@ +{ + self, + inputs, + ... +}: { + flake.nixosConfigurations.t480 = inputs.nixpkgs.lib.nixosSystem { + modules = with self.nixosModules; [ + desktop + t480Configuration + # CPU microcode + integrated Intel GPU. The T480 also has an NVIDIA MX150; + # to enable it in PRIME offload mode, swap gpuIntel for gpuNvidiaPrime + # (which includes gpuIntel). Left on Intel-only by default to save power. + cpuIntel + gpuIntel + development + feishin + obsidian + discord + gaming + ]; + }; +} + diff --git a/modules/hosts/t480/t480Configuration.nix b/modules/hosts/t480/t480Configuration.nix new file mode 100644 index 0000000..a01c0d9 --- /dev/null +++ b/modules/hosts/t480/t480Configuration.nix @@ -0,0 +1,12 @@ +{ + self, + inputs, + ... +}: { + flake.nixosModules.t480Configuration = {...}: { + networking = { + hostName = "t480"; + }; + }; +} + diff --git a/modules/parts.nix b/modules/parts.nix new file mode 100644 index 0000000..657e3ca --- /dev/null +++ b/modules/parts.nix @@ -0,0 +1,10 @@ +{ + config = { + systems = [ + "x86_64-linux" + "x86_64-darwin" + "aarch64-linux" + "aarch64-darwin" + ]; + }; +} diff --git a/modules/system/audio/default.nix b/modules/system/audio/default.nix new file mode 100644 index 0000000..75f313c --- /dev/null +++ b/modules/system/audio/default.nix @@ -0,0 +1,22 @@ +{...}: { + # PipeWire-based audio. wpctl (used by the Hyprland media keybinds) is provided + # by wireplumber, which PipeWire enables by default. + flake.nixosModules.audio = {pkgs, ...}: { + environment.systemPackages = with pkgs; [ + playerctl # media keys (hypr keybinds) + pavucontrol # GUI mixer (waybar pulseaudio on-click) + ]; + + # PipeWire replaces the PulseAudio server; don't run the standalone daemon. + services.pulseaudio.enable = false; + security.rtkit.enable = true; + + services.pipewire = { + enable = true; + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + jack.enable = true; + }; + }; +} diff --git a/modules/system/core/boot.nix b/modules/system/core/boot.nix new file mode 100644 index 0000000..8b008f1 --- /dev/null +++ b/modules/system/core/boot.nix @@ -0,0 +1,31 @@ +{...}: { + flake.nixosModules.bootloader = { + pkgs, + lib, + ... + }: { + boot = { + loader = { + timeout = 2; + efi = { + canTouchEfiVariables = true; + }; + grub = { + efiSupport = true; + device = "nodev"; + theme = pkgs.catppuccin-grub; + }; + }; + plymouth = { + enable = true; + theme = "catppuccin-mocha"; + themePackages = with pkgs; [ + (catppuccin-plymouth.override { + variant = "mocha"; + }) + ]; + }; + kernelPackages = pkgs.linuxPackages_latest; + }; + }; +} diff --git a/modules/system/core/default.nix b/modules/system/core/default.nix new file mode 100644 index 0000000..1da10d0 --- /dev/null +++ b/modules/system/core/default.nix @@ -0,0 +1,43 @@ +{ + self, + inputs, + ... +}: { + flake.nixosModules.core = { + pkgs, + lib, + ... + }: let + modules = with self.nixosModules; [ + user + bootloader + nix + hardware + locale + nix-ld + ]; + in { + imports = + [ + /etc/nixos/hardware-configuration.nix + ] + ++ modules; + services = { + openssh.enable = true; + avahi.enable = true; + }; + environment.systemPackages = with pkgs; [ + # neovim is provided by the nvim feature module (programs.neovim.enable). + unzip + p7zip-rar + usbutils + lsof + gvfs + libnotify + python313 + curlWithGnuTls + wget + ]; + system.stateVersion = "26.05"; + }; +} diff --git a/modules/system/core/hardware.nix b/modules/system/core/hardware.nix new file mode 100644 index 0000000..374b10b --- /dev/null +++ b/modules/system/core/hardware.nix @@ -0,0 +1,11 @@ +{...}: { + flake.nixosModules.hardware = {...}: { + hardware = { + bluetooth = { + enable = true; + powerOnBoot = true; + }; + xpadneo.enable = true; + }; + }; +} diff --git a/modules/system/core/locale.nix b/modules/system/core/locale.nix new file mode 100644 index 0000000..df67371 --- /dev/null +++ b/modules/system/core/locale.nix @@ -0,0 +1,32 @@ +{ + self, + inputs, + ... +}: { + flake.nixosModules.locale = { + pkgs, + lib, + ... + }: { + time.timeZone = "Europe/London"; + i18n = { + defaultLocale = "en_GB.UTF-8"; + extraLocaleSettings = { + LC_ADDRESS = "en_GB.UTF-8"; + LC_IDENTIFICATION = "en_GB.UTF-8"; + LC_MEASUREMENT = "en_GB.UTF-8"; + LC_MONETARY = "en_GB.UTF-8"; + LC_NAME = "en_GB.UTF-8"; + LC_NUMERIC = "en_GB.UTF-8"; + LC_PAPER = "en_GB.UTF-8"; + LC_TELEPHONE = "en_GB.UTF-8"; + LC_TIME = "en_GB.UTF-8"; + }; + }; + services.xserver.xkb = { + layout = "gb"; + variant = ""; + }; + console.keyMap = "uk"; + }; +} diff --git a/modules/system/core/nix-settings.nix b/modules/system/core/nix-settings.nix new file mode 100644 index 0000000..b6fec2c --- /dev/null +++ b/modules/system/core/nix-settings.nix @@ -0,0 +1,23 @@ +{inputs, ...}: { + flake.nixosModules.nix = {...}: { + nix = { + settings = { + cores = 6; + trusted-users = ["root" "ahmza"]; + download-buffer-size = 524288000; + experimental-features = [ + "nix-command" + "flakes" + ]; + }; + nixPath = ["nixpkgs=${inputs.nixpkgs}"]; + optimise.automatic = true; + gc = { + automatic = true; + dates = "daily"; + options = "--delete-older-than 5d"; + }; + }; + nixpkgs.config.allowUnfree = true; + }; +} diff --git a/modules/system/core/user.nix b/modules/system/core/user.nix new file mode 100644 index 0000000..6c0d4e7 --- /dev/null +++ b/modules/system/core/user.nix @@ -0,0 +1,30 @@ +{self, ...}: { + flake.nixosModules.user = { + pkgs, + ... + }: let + modules = with self.nixosModules; [ + zsh + stow + ]; + in { + imports = modules; + + ahmza.stow = { + enable = true; + user = "ahmza"; + # repoPath defaults to /home/ahmza/dot + }; + + users.users.ahmza = { + isNormalUser = true; + initialPassword = "qwer"; + shell = pkgs.zsh; + description = "ahmza"; + extraGroups = [ + "networkmanager" + "wheel" + ]; + }; + }; +} diff --git a/modules/system/desktop/default.nix b/modules/system/desktop/default.nix new file mode 100644 index 0000000..aefbdee --- /dev/null +++ b/modules/system/desktop/default.nix @@ -0,0 +1,20 @@ +{ + self, + moduleWithSystem, + ... +}: { + flake.nixosModules.desktop = moduleWithSystem ({pkgs, ...}: let + modules = with self.nixosModules; [ + core + hyprland + sddm + network + ]; + in { + imports = modules; + environment.systemPackages = with pkgs; [ + mpv + ]; + }); +} + diff --git a/modules/system/hardware/cpu/amd.nix b/modules/system/hardware/cpu/amd.nix new file mode 100644 index 0000000..db1bc36 --- /dev/null +++ b/modules/system/hardware/cpu/amd.nix @@ -0,0 +1,7 @@ +{...}: { + # AMD CPU: microcode updates (applied via initrd at boot). + flake.nixosModules.cpuAmd = {...}: { + hardware.cpu.amd.updateMicrocode = true; + hardware.enableRedistributableFirmware = true; + }; +} diff --git a/modules/system/hardware/cpu/intel.nix b/modules/system/hardware/cpu/intel.nix new file mode 100644 index 0000000..4f0e631 --- /dev/null +++ b/modules/system/hardware/cpu/intel.nix @@ -0,0 +1,7 @@ +{...}: { + # Intel CPU: microcode updates (applied via initrd at boot). + flake.nixosModules.cpuIntel = {...}: { + hardware.cpu.intel.updateMicrocode = true; + hardware.enableRedistributableFirmware = true; + }; +} diff --git a/modules/system/hardware/gpu/amd.nix b/modules/system/hardware/gpu/amd.nix new file mode 100644 index 0000000..52de0b5 --- /dev/null +++ b/modules/system/hardware/gpu/amd.nix @@ -0,0 +1,20 @@ +{...}: { + # AMD GPU (mesa + RADV/ROCm). + flake.nixosModules.gpuAmd = {pkgs, ...}: { + environment.systemPackages = with pkgs; [ + mesa + vulkan-tools + rocmPackages.rocm-smi + rocmPackages.rocminfo + ]; + hardware.graphics = { + enable = true; + enable32Bit = true; + extraPackages = with pkgs; [ + libva-vdpau-driver + libvdpau-va-gl + rocmPackages.clr.icd + ]; + }; + }; +} diff --git a/modules/system/hardware/gpu/intel.nix b/modules/system/hardware/gpu/intel.nix new file mode 100644 index 0000000..635272e --- /dev/null +++ b/modules/system/hardware/gpu/intel.nix @@ -0,0 +1,18 @@ +{...}: { + # Intel integrated GPU (mesa + VA-API hardware video acceleration). + flake.nixosModules.gpuIntel = {pkgs, ...}: { + environment.systemPackages = with pkgs; [ + mesa + vulkan-tools + ]; + hardware.graphics = { + enable = true; + enable32Bit = true; + extraPackages = with pkgs; [ + intel-media-driver # modern iHD driver (Broadwell+) + intel-vaapi-driver # legacy i965 driver + libva-vdpau-driver + ]; + }; + }; +} diff --git a/modules/system/hardware/gpu/nvidia-prime.nix b/modules/system/hardware/gpu/nvidia-prime.nix new file mode 100644 index 0000000..41bb4a9 --- /dev/null +++ b/modules/system/hardware/gpu/nvidia-prime.nix @@ -0,0 +1,41 @@ +{self, ...}: { + # Hybrid Intel + NVIDIA (PRIME) for laptops like the T480 (Intel UHD 620 + + # NVIDIA MX150). Uses "offload" mode: the dGPU stays powered down by default + # (minimal extra battery drain) and is only used for apps launched with + # `nvidia-offload `. It also pulls in the Intel GPU stack (gpuIntel). + # + # Bus IDs below are specific to the T480 (from `lspci`): + # Intel 00:02.0 -> PCI:0:2:0 + # NVIDIA 01:00.0 -> PCI:1:0:0 + # Adjust for other machines. + flake.nixosModules.gpuNvidiaPrime = { + config, + pkgs, + ... + }: { + imports = [self.nixosModules.gpuIntel]; + + services.xserver.videoDrivers = ["nvidia"]; + + hardware.nvidia = { + modesetting.enable = true; + # MX150 is Pascal (pre-Turing); the proprietary modules are the safe choice. + open = false; + nvidiaSettings = true; + powerManagement.enable = true; + + prime = { + # Offload keeps the NVIDIA GPU asleep until explicitly used. This gives + # the `nvidia-offload` helper wrapper. Swap to sync mode if you want the + # dGPU always driving the display (higher power draw). + offload = { + enable = true; + enableOffloadCmd = true; # provides `nvidia-offload` wrapper + }; + + intelBusId = "PCI:0:2:0"; + nvidiaBusId = "PCI:1:0:0"; + }; + }; + }; +} diff --git a/modules/system/hardware/gpu/nvidia.nix b/modules/system/hardware/gpu/nvidia.nix new file mode 100644 index 0000000..c01461f --- /dev/null +++ b/modules/system/hardware/gpu/nvidia.nix @@ -0,0 +1,31 @@ +{...}: { + # Dedicated NVIDIA GPU (single discrete card, no hybrid/PRIME). + # For Intel+NVIDIA laptops (e.g. the T480), use gpuNvidiaPrime instead. + flake.nixosModules.gpuNvidia = { + config, + pkgs, + ... + }: { + services.xserver.videoDrivers = ["nvidia"]; + + hardware.graphics = { + enable = true; + enable32Bit = true; + }; + + hardware.nvidia = { + modesetting.enable = true; + # Proprietary kernel modules. Set to true only for Turing+ (RTX 20xx and + # newer) where the open modules are well supported. + open = false; + nvidiaSettings = true; + # Fixes suspend/resume corruption on many setups. + powerManagement.enable = true; + package = config.boot.kernelPackages.nvidiaPackages.stable; + }; + + environment.systemPackages = with pkgs; [ + vulkan-tools + ]; + }; +} diff --git a/modules/system/network/default.nix b/modules/system/network/default.nix new file mode 100644 index 0000000..fec7978 --- /dev/null +++ b/modules/system/network/default.nix @@ -0,0 +1,13 @@ +{...}: { + flake.nixosModules.network = {...}: { + networking = { + networkmanager.enable = true; + # Firewall intentionally disabled (trusted LAN; local services like + # weylus / remote play need open ports). + firewall.enable = false; + }; + + # Don't block boot waiting for a network connection. + systemd.services.NetworkManager-wait-online.enable = false; + }; +} diff --git a/modules/system/nix-ld/default.nix b/modules/system/nix-ld/default.nix new file mode 100644 index 0000000..35338d8 --- /dev/null +++ b/modules/system/nix-ld/default.nix @@ -0,0 +1,16 @@ +{moduleWithSystem, ...}: { + flake.nixosModules.nix-ld = moduleWithSystem ({pkgs, ...}: { + programs = { + nix-ld = { + enable = true; + libraries = with pkgs; [ + util-linux + stdenv.cc.cc + zlib + libusb1 + ]; + }; + }; + }); +} + diff --git a/modules/system/stow/default.nix b/modules/system/stow/default.nix new file mode 100644 index 0000000..2c692a6 --- /dev/null +++ b/modules/system/stow/default.nix @@ -0,0 +1,115 @@ +{...}: { + flake.nixosModules.stow = { + config, + pkgs, + lib, + ... + }: let + cfg = config.ahmza.stow; + + stowScript = pkgs.writeShellApplication { + name = "ahmza-stow"; + runtimeInputs = [pkgs.stow pkgs.coreutils pkgs.findutils pkgs.gnused]; + text = '' + set -euo pipefail + + REPO="${cfg.repoPath}" + TARGET="$HOME" + + if [ ! -d "$REPO" ]; then + echo "ahmza-stow: dotfiles repo not found at $REPO; skipping." >&2 + echo " (clone it with: git clone ${cfg.repoUrl} $REPO)" >&2 + exit 0 + fi + + packages=(${lib.escapeShellArgs cfg.packages}) + if [ ''${#packages[@]} -eq 0 ]; then + echo "ahmza-stow: no packages configured; nothing to do." + exit 0 + fi + + cd "$REPO" + + # Back up any existing non-symlink targets that would block stowing $pkg. + # Mirrors the dotfiles repo's own install script: list the files/links the + # package provides (relative to the package dir) and back up only real + # (non-symlink) targets. Skipping symlinks means we never write through an + # already-stowed folded directory symlink into the repo itself. + # Backups go to .pre-stow.bak. + backup_conflicts() { + local pkg="$1" rel target + while IFS= read -r rel; do + target="$HOME/$rel" + if [ -e "$target" ] && [ ! -L "$target" ]; then + echo "ahmza-stow: backing up existing $target -> $target.pre-stow.bak" + mv "$target" "$target.pre-stow.bak" + fi + done < <(cd "$REPO/$pkg" && find . \( -type f -o -type l \) | sed 's|^\./||') + } + + for pkg in "''${packages[@]}"; do + if [ ! -d "$pkg" ]; then + echo "ahmza-stow: package '$pkg' not present in repo; skipping." >&2 + continue + fi + + echo "ahmza-stow: stow -R $pkg" + # Try to stow first; only back up (and retry) if there's a real conflict. + if ! stow -R --target="$TARGET" "$pkg" 2>/dev/null; then + echo "ahmza-stow: conflicts detected for '$pkg'; backing up and retrying." + backup_conflicts "$pkg" + if ! stow -R --target="$TARGET" "$pkg"; then + echo "ahmza-stow: failed to stow '$pkg' — resolve manually." >&2 + fi + fi + done + + echo "ahmza-stow: done." + ''; + }; + in { + options.ahmza.stow = { + enable = lib.mkEnableOption "automatic GNU Stow of dotfiles on login"; + + user = lib.mkOption { + type = lib.types.str; + description = "User whose dotfiles should be stowed."; + }; + + repoPath = lib.mkOption { + type = lib.types.str; + default = "/home/${cfg.user}/dot"; + defaultText = "/home/\${config.ahmza.stow.user}/dot"; + description = "Absolute path to the stow dotfiles repository."; + }; + + repoUrl = lib.mkOption { + type = lib.types.str; + default = "https://github.com/ahmzaa/dot.git"; + description = "URL of the dotfiles repo (used only in help messages)."; + }; + + packages = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = []; + description = '' + Stow package names (top-level dirs in the dotfiles repo) to link. + Feature modules append to this via lib.mkAfter / list concatenation. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = [pkgs.stow stowScript]; + + systemd.user.services.ahmza-stow = { + description = "Stow dotfiles for ${cfg.user}"; + wantedBy = ["default.target"]; + serviceConfig = { + Type = "oneshot"; + ExecStart = lib.getExe stowScript; + }; + }; + }; + }; +} diff --git a/modules/system/systemTheme/cursor.nix b/modules/system/systemTheme/cursor.nix new file mode 100644 index 0000000..cfd0f22 --- /dev/null +++ b/modules/system/systemTheme/cursor.nix @@ -0,0 +1,25 @@ +{...}: { + flake.nixosModules.bibataCursors = {pkgs, ...}: { + environment.systemPackages = with pkgs; [ + bibata-cursors + ]; + + environment.variables = { + XCURSOR_THEME = "Bibata-Modern-Ice"; + XCURSOR_SIZE = "20"; + }; + + programs.dconf.profiles.user.databases = [ + { + lockAll = false; + settings = { + "org/gnome/desktop/interface" = { + cursor-theme = "Bibata-Modern-Ice"; + font-name = "JetBrainsMono Nerd Font 11"; + }; + }; + } + ]; + }; +} + diff --git a/modules/system/systemTheme/default.nix b/modules/system/systemTheme/default.nix new file mode 100644 index 0000000..2ddc250 --- /dev/null +++ b/modules/system/systemTheme/default.nix @@ -0,0 +1,21 @@ +{ + self, + inputs, + ... +}: { + flake.nixosModules.systemTheme = { + config, + pkgs, + lib, + ... + }: let + modules = with self.nixosModules; [ + bibataCursors + catppuccinGtk + fonts + ]; + in { + imports = modules; + }; +} + diff --git a/modules/system/systemTheme/fonts.nix b/modules/system/systemTheme/fonts.nix new file mode 100644 index 0000000..d18e150 --- /dev/null +++ b/modules/system/systemTheme/fonts.nix @@ -0,0 +1,23 @@ +{...}: { + # System-wide fonts. Standardized on JetBrainsMono Nerd Font everywhere + # (terminal, sddm, gtk/dconf, waybar, etc.). + flake.nixosModules.fonts = {pkgs, ...}: { + fonts = { + packages = with pkgs; [ + nerd-fonts.jetbrains-mono + # Fallbacks for emoji / CJK / general coverage. + noto-fonts + noto-fonts-color-emoji + ]; + + enableDefaultPackages = true; + + fontconfig.defaultFonts = { + monospace = ["JetBrainsMono Nerd Font"]; + sansSerif = ["JetBrainsMono Nerd Font"]; + serif = ["JetBrainsMono Nerd Font"]; + emoji = ["Noto Color Emoji"]; + }; + }; + }; +} diff --git a/modules/system/systemTheme/gtk.nix b/modules/system/systemTheme/gtk.nix new file mode 100644 index 0000000..53b2768 --- /dev/null +++ b/modules/system/systemTheme/gtk.nix @@ -0,0 +1,44 @@ +{ + self, + inputs, + ... +}: { + flake.nixosModules.catppuccinGtk = { + pkgs, + lib, + ... + }: let + in { + environment.systemPackages = with pkgs; [ + (catppuccin-gtk.override { + variant = "mocha"; + accents = ["mauve"]; + tweaks = ["rimless"]; + size = "compact"; + }) + (catppuccin-papirus-folders.override { + flavor = "mocha"; + accent = "mauve"; + }) + ]; + programs = { + xfconf.enable = true; + dconf = { + enable = true; + profiles.user.databases = [ + { + lockAll = false; + settings = { + "org/gnome/desktop/interface" = { + gtk-theme = "catppuccin-mocha-mauve-compact+rimless"; + icon-theme = "Papirus"; + color-scheme = "prefer-dark"; + }; + }; + } + ]; + }; + }; + }; +} + diff --git a/result b/result new file mode 120000 index 0000000..90987da --- /dev/null +++ b/result @@ -0,0 +1 @@ +/nix/store/dgd4pa9cl1n266zsbf8xi277p304zxcw-nixos-system-t480-26.05.20260803.531670d \ No newline at end of file