My Nix Configuration

In order to edit the configuration without root permissions, it is convenient to symlink configuration.nix to my user's home directory, like this:

mkdir ~/etc
sudo mv /etc/nixos/ ~/etc/
sudo chown -R $(id -un):users ~/etc/nixos/
sudo ln -s ~/etc/nixos/ /etc/

The Configuration

# Edit this configuration file to define what should be installed on
# your system.  Help is available in the configuration.nix(5) man page
# and in the NixOS manual (accessible by running ‘nixos-help’).

{ config, pkgs, lib, ... }:

let
  home-manager = builtins.fetchTarball https://github.com/nix-community/home-manager/archive/release-26.05.tar.gz;

  # Given a list of attribute sets, union them into a single attribute
  # set, preferring keys earlier in the list.
  unionsAttrs = lib.foldAttrs lib.const {};
in
{
  imports =
    [ # Include the results of the hardware scan.
      ./hardware-configuration.nix
      (import "${home-manager}/nixos")
    ];

  # Bootloader.
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;

  networking.hostName = "xps"; # Define your hostname.
  networking.wireless.enable = true;  # Enables wireless support via wpa_supplicant.

  # Configure network proxy if necessary
  # networking.proxy.default = "http://user:password@proxy:port/";
  # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";

  # Enable networking
  networking.networkmanager.enable = true;

  # Enable bluetooth
  hardware.bluetooth = {
    enable = true;
    powerOnBoot = false;
  };

  # Set your time zone.
  time.timeZone = "America/Chicago";

  # Select internationalisation properties.
  i18n.defaultLocale = "en_US.UTF-8";

  i18n.extraLocaleSettings = {
    LC_ADDRESS = "en_US.UTF-8";
    LC_IDENTIFICATION = "en_US.UTF-8";
    LC_MEASUREMENT = "en_US.UTF-8";
    LC_MONETARY = "en_US.UTF-8";
    LC_NAME = "en_US.UTF-8";
    LC_NUMERIC = "en_US.UTF-8";
    LC_PAPER = "en_US.UTF-8";
    LC_TELEPHONE = "en_US.UTF-8";
    LC_TIME = "en_US.UTF-8";
  };

  # Add swap
  swapDevices = [{
    device = "/var/lib/swapfile";
    size = 16*1024; # 16 GiB
  }];

  # Define a user account. Don't forget to set a password with ‘passwd’.
  users.users."sam" = {
    isNormalUser = true;
    description = "Sam Cohen";
    extraGroups = [ "networkmanager" "wheel" "video" ];
    packages = with pkgs; [];
  };

  # Necessary for sway
  security.polkit.enable = true;

  # Keyring for wayland applications
  services.gnome.gnome-keyring.enable = true;
  security.pam.services = {
    greetd.enableGnomeKeyring = true;
    # Necessary for swaylock to work correctly.
    swaylock = {};
  };

  # Launches sway on login
  services.greetd = {
    enable = true;
    settings = {
      default_session = {
        command = "${pkgs.tuigreet}/bin/tuigreet --time --cmd sway";
        user = "greeter";
      };
    };
  };

  services.logind.settings.Login = {
    # don’t shutdown when power button is short-pressed. We want sway
    # to handle that key instead.
    HandlePowerKey = "ignore";
  };

  # CPU frequency scaling. This is meant to extend battery life.
  services.auto-cpufreq = {
    enable = true;
    settings = {
      battery = {
        governor = "powersave";
        turbo = "never";
      };
      charger = {
        governor = "performance";
        turbo = "auto";
      };
    };
  };
  # Tries to keep the CPU at a reasonable temperature by throttling
  # when necessary.
  services.thermald.enable = true;

  # A service which automatically mounts external storage devices.
  services.udisks2.enable = true;

  ## Printing ##########

  # NOTE: see http://localhost:631 to configure printers.
  services.printing.enable = true;

  # The below is for automatic discovery of network printers using
  # the IPP Everywhere protocol.
  services.avahi = {
    enable = true;
    nssmdns4 = true;
    openFirewall = true;
  };

  # Fonts
  fonts.packages = with pkgs; [
    hack-font
  ];

  home-manager.users."sam" = { config, pkgs, lib, ... }:
    let
      # Permits adjustment of display brightness
      brightnessctl = "${pkgs.brightnessctl}/bin/brightnessctl";
      # Permits adjustment of audio volume via pactl
      pactl = "${pkgs.pulseaudio}/bin/pactl";
      # Permits locking the screen
      swaylock = "${pkgs.swaylock}/bin/swaylock";
    in {
      home.packages = with pkgs; [
        # For keeping track of passwords:
        pass

        # For reading mail:
        mu

        # For syncing with mail servers:
        isync
        # For sending email:
        msmtp
        # For communicating with exchange servers. See below, usually
        # we will start this program as a systemd service.
        davmail

        # dokoff (locally defined)
        (callPackage /home/sam/Projects/dokoff/dokoff.nix { })

        # For taking screenshots
        grim
      ];

      programs.bash = {
        enable = true;
        enableCompletion = true;
        bashrcExtra = "eval \"$(direnv hook bash)\"";
      };

      programs.git = {
        enable = true;
        signing = {
          key = "E2FE54A52DF79398";
          format = "openpgp";
          signByDefault = true;
        };
        settings = {
          init.defaultBranch = "main";
          user.useConfigOnly = true;
          user.name = "Sam Cohen";
          user.email = "sbcohen2000@gmail.com";
          rebase.autoSquash = true;
        };
      };

      ## GPG ##########

      programs.gpg = {
        enable = true;
      };
      services.gpg-agent = {
        enable = true;
        pinentry.package = pkgs.pinentry-qt;
        defaultCacheTtl = 1800; # Only ask for a password half an hour after inactivity
      };

      ## Sway/GUI ##########

      programs.kitty = {
        enable = true;
        themeFile = "AtomOneLight";
        font.name = "Hack";
        font.size = 10;
        # Make the text render a lot thicker, more similar to emacs
        # text rendering:
        extraConfig = "text_composition_strategy 1.5 15";
      };

      programs.rofi = {
        enable = true;
        modes = [ "drun" ];
        theme = "dmenu";
      };

      # Client for udisks2
      services.udiskie = {
        enable = true;
      };

      services.emacs.enable = true;
      programs.emacs = {
        enable = true;
      };
      home.sessionVariables = {
        # Put the location of mu4e on the path so that emacs can find
        # it later.
        MU4E = "${pkgs.mu.mu4e}";
        EDITOR = "emacsclient";
      };

      wayland.windowManager.sway = {
        enable = true;
        wrapperFeatures.gtk = true; # Fixes common issues with gtk3 apps
        checkConfig = true;
        config = rec {
          terminal = "kitty";
          startup = [
            # Launch emacs on start
            { command = "emacsclient -c"; }
          ];
          modifier = "Mod4";
          keybindings = let
            workspaces = [1 2 3 4];

            # Setup two bindings for each workspace, one which moves
            # to the workspace, and one which moves the current
            # container to the workspace:
            bindings = lib.listToAttrs
              (lib.concatMap (n:
                let
                  ns = toString n;
                in [
                  { "name" = "${modifier}+${ns}";       value = "workspace number ${ns}"; }
                  { "name" = "${modifier}+Shift+${ns}"; value = "move container to workspace number ${ns}"; }
                ]) workspaces);
          in
            unionsAttrs [
              bindings
              {
                "${modifier}+Left" = "focus left";
                "${modifier}+Right" = "focus right";
                "${modifier}+Up" = "focus up";
                "${modifier}+Down" = "focus down";
                "${modifier}+Shift+Left" = "move left";
                "${modifier}+Shift+Right" = "move right";
                "${modifier}+Shift+Up" = "move up";
                "${modifier}+Shift+Down" = "move down";

                "${modifier}+r" = "mode resize";
                "${modifier}+q" = "kill";
                "${modifier}+a" = "focus parent";
                "${modifier}+f" = "fullscreen toggle";
                "${modifier}+space" = "layout toggle split";
                "${modifier}+Shift+space" = "floating toggle";

                # Brightness Controls
                "XF86MonBrightnessDown" = "exec ${brightnessctl} s 5%-";
                "XF86MonBrightnessUp" = "exec ${brightnessctl} s +5%";

                # Volume Controls
                "XF86AudioRaiseVolume" = "exec ${pactl} set-sink-volume @DEFAULT_SINK@ +5%";
                "XF86AudioLowerVolume" = "exec ${pactl} set-sink-volume @DEFAULT_SINK@ -5%";
                "XF86AudioMute" = "exec ${pactl} set-sink-mute @DEFAULT_SINK@ toggle";

                # Launching
                "${modifier}+d" = "exec rofi -show";
                "${modifier}+Return" = "exec kitty";
                "${modifier}+e" = "exec emacsclient -c";
                "${modifier}+b" = "exec firefox";

                # Locking
                "XF86PowerOff" = "exec ${swaylock}";
              }
            ];

          input = {
            "type:keyboard" = {
              xkb_options = "ctrl:nocaps";
              repeat_delay = "250";
              repeat_rate = "30";
            };
            "type:touchpad" = {
              tap = "enabled";
              # Prevent an odd feature where after tapping you can drag
              # the mouse to create a selection.
              drag = "disabled";
              drag_lock = "disabled";

              # Change scroll direction.
              natural_scroll = "enabled";

              # Disable trackpad while typing.
              dwt = "enabled";
            };
          };
        };
      };

      # Turns off the display, then suspends the computer after a period
      # of inactivity.
      services.swayidle = {
        enable = true;
        timeouts = [
          {
            timeout = 1 * 60;
            command = "${brightnessctl} -s s 5%"; # Set the brightness, saving the old value to a tmpfile.
            resumeCommand = "${brightnessctl} -r"; # Restore the previous brightness.
          }
          {
            timeout = 5 * 60;
            command = "${pkgs.systemd}/bin/systemctl suspend";
          }
        ];
        events = {
          # Restore the previously saved brightness.
          "after-resume" = "${brightnessctl} -r";
          "before-sleep" = "${swaylock}";
        };
      };

      # Locks the screen when power button is pressed.
      programs.swaylock = {
        enable = true;
      };

      # Displays notifications when called upon.
      services.mako = {
        enable = true;
        settings = {
          default-timeout = 5000; # 5 seconds.
        };
      };

      programs.firefox = {
        enable = true;
      };

      ## Mail ##########

      # We use a davmail server to proxy Exchange mail servers as
      # local SMTP/IMAP servers.
      systemd.user.services.davmail = {
        Unit = {
          Description = "Proxy Exchange servers through local SMTP/IMAP.";
        };
        Install = {
          WantedBy = [ "default.target" ];
        };
        Service = {
          ExecStart = "${pkgs.davmail}/bin/davmail ${config.home.homeDirectory}/.davmail.properties";
        };
      };

      # Restart user services when we perform a configuration switch.
      systemd.user.startServices = "sd-switch";

      home.stateVersion = "26.05";
    };

  # List packages installed in system profile. To search, run:
  # $ nix search wget
  environment.systemPackages = with pkgs; [
    libnotify          # Send notifications to the notification daemon.
    wl-clipboard       # Copy/Paste from the terminal (`wl-copy/paste`).
    direnv
    kdePackages.okular # A PDF viewer
    darktable          # Raw image editor
    gphoto2            # A command-line program to communicate with cameras

    ## In Service of Emacs ##########

    # Ghostscript is necessary to interpret PDF files in emacs'
    # document viewer.
    ghostscript
    ispell             # A spell-checking program.
    ripgrep            # A better grep.
  ];

  # This value determines the NixOS release from which the default
  # settings for stateful data, like file locations and database versions
  # on your system were taken. It‘s perfectly fine and recommended to leave
  # this value at the release version of the first install of this system.
  # Before changing this value read the documentation for this option
  # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
  system.stateVersion = "26.05"; # Did you read the comment?
}