{ pkgs, ... }: # Shared base for all hosts: user, SSH hardening, nix settings, packages. { # ---- User ---- users.users.darman = { isNormalUser = true; description = "darman"; extraGroups = [ "wheel" "networkmanager" ]; shell = pkgs.zsh; openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILD5K6AQ0wYYHbNGzC4PyunUQsXbaD0iu1eaadLtv+Xp darman@terra" ]; }; security.sudo = { enable = true; wheelNeedsPassword = true; extraConfig = '' Defaults timestamp_timeout=20 ''; }; # ---- SSH (key-only) ---- services.openssh = { enable = true; settings = { PasswordAuthentication = false; PermitRootLogin = "no"; }; }; # ---- Nix ---- nix.settings = { experimental-features = [ "nix-command" "flakes" ]; # trust wheel so `nixos-rebuild --target-host darman@…` can push closures. trusted-users = [ "root" "@wheel" ]; # authentik (services/identity/authentik.nix) comes from authentik-nix, # which cache.nixos.org doesn't carry — without this it's ~400 local # derivations (npm, rust, python). NOTE: the closure is built on whatever # machine runs ./scripts/deploy, so the LAPTOP needs these two lines too, # in /etc/nix/nix.custom.conf (Determinate Nix rewrites nix.conf). extra-substituters = [ "https://nix-community.cachix.org" ]; extra-trusted-public-keys = [ "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" ]; }; nix.gc = { automatic = true; dates = "weekly"; options = "--delete-older-than 30d"; }; environment.systemPackages = with pkgs; [ git btop tmux curl wget zsh-powerlevel10k lsd jq ]; # ---- home-manager (user-level config for darman, all hosts) ---- # Requires home-manager.nixosModules.home-manager in the host's own # `modules` list (flake.nix) — this only sets values for options that # module declares, it doesn't import it, so every nixosSystem using # common.nix needs that line too (mirrors terra's original setup). home-manager.useGlobalPkgs = true; home-manager.useUserPackages = true; # Protects activation if a plain (non-symlink) ~/.zshrc etc. already # exists from before home-manager managed it — e.g. a host where the # zsh-newuser-install wizard's option (0) was used to silence itself. home-manager.backupFileExtension = "hm-bak"; home-manager.users.darman.imports = [ ./home/common.nix ]; # ---- zsh / oh-my-zsh / powerlevel10k ---- programs.zsh = { enable = true; ohMyZsh = { enable = true; theme = "robbyrussell"; # prompt itself replaced by p10k below }; shellAliases = { ls = "lsd"; }; interactiveShellInit = '' source ${pkgs.zsh-powerlevel10k}/share/zsh-powerlevel10k/powerlevel10k.zsh-theme source ${./dotfiles/p10k.zsh} ''; }; # ---- Boot generations ---- # Cap every host at 5 generations so none of them can quietly repeat # jupiter's 34-generations-on-a-29G-eMMC incident. Both loader options are # set unconditionally since only one is ever enabled per host (systemd-boot # everywhere except mercury's generic-extlinux-compatible RPi image) — the # other one is simply inert. boot.loader.systemd-boot.configurationLimit = 5; boot.loader.generic-extlinux-compatible.configurationLimit = 5; # Stock journald defaults to ~10% of the filesystem (up to 4G) before it # rotates — no scheduled vacuum, just a ceiling it grows into. On jupiter's # 29G eMMC that's ~2.9G it could silently accumulate. Cap it well below that # everywhere instead of only noticing when a disk fills up again. services.journald.extraConfig = '' SystemMaxUse=200M ''; # ---- Locale / firewall base ---- time.timeZone = "Europe/Berlin"; i18n.defaultLocale = "en_US.UTF-8"; console.keyMap = "de"; # Firewall on, ssh always allowed. Service modules add their own ports # (samba via openFirewall, caddy 80/443, tailscale trusts tailscale0). networking.firewall.enable = true; networking.firewall.allowedTCPPorts = [ 22 ]; }