Hello! Welcome to my literate file that describes (and generates) the configuration for all my computers.
For people who are confused, this file has a bunch of source blocks that are then put into individual files using emacs+org’s tangle mode.
Once the files are generated, they are converted into an immutable system configuration using nixos.
This repository consists of configuration for two machines
bigbox
– my wfh computerlaptop
– my travel laptop + office computer
To generate the actual nix files, you need to open this file in emacs and then execute M-x org-babel-tangle
.
Or run the following from the command line
emacs README.org --batch -f org-babel-tangle
Once the nix files are ready, you can deploy using
nixos-rebuild switch --flake .#<machine>
Other files in this repo are :-
flake.lock
so as to keep my versions intact. More on that later.assets/*
contains images like the wallpaper that cannot be part of this.secrets/secrets.yaml
contains encrypted keys and is edited usingsops
.
The other files in this repository are generated. They ideally shouldn’t be edited directly.
- Emacs is the text editor that I use. Some people might take offense at me calling it a text editor.
- Org mode is an inbuilt plugin for emacs that helps with managing org files like this one. Org files are similar to markdown but with superpowers.
- Tangle is an org mode option that lets us export snippets to other files. In this case, the configuration snippets you see are written to individual files.
- Anything that appears in
<<code-id>>
is like a variable that gets filled in later. You will see them in the snippets below where they are filled in by other snippets later in the file.
- Anything that appears in
- Nix is a bespoke programming language, used mainly to configure environments and dependencies.
- Nixos is a linux distro where you define your operating system and other things using nix.
The expectation is that the output of evaluating the nix files is a “configuration” that can be applied.
This way your operating system is always defined by configuration files. You almost never install anything.
Make a change to the configuration, reapply and repeat. You need vim? Add it to the config, and rebuild.
YourNixCode(Input) -> System Configuration
I use nix flakes which means that the entry point for the nix evaluation is a file called
flake.nix
which has two parts (among other things){ inputs: # describes the function input, consisting mainly of package sources outputs: # what the function outputs, a nixos configuration in our case }
Nix flakes is still behind an
experimental
flag, but it is considered the standard by most of the community. Flakes allow us to pin the input package versions using aflake.lock
file. This prevents unwanted and surprise updates when rebuilding without changing the configuration.
Window Manager | Hyprland |
Bar | Waybar |
Application Launcher | Tofi |
Notification Daemon | Dunst |
Terminal Emulator | Alacritty |
Shell | zsh + ohmyzsh |
Text Editor | Emacs |
File Manager | Thunar |
Fonts | Iosevka |
Colors | Nord |
Icons | Nordzy |
Lock Screen | Swaylock |
Wallpapers | Nordic + Hyprpaper |
I have a bunch of constant strings that I would rather put in a file. Thats what user.nix
is.
The values are imported at the beginning and are available to almost all the functions being called to configure the system.
{
system = "x86_64-linux";
username = "nambiar";
gitUser = "gamedolphin";
gitEmail = "[email protected]"; # github email
stateVersion = "25.05";
locale = "sv_SE.UTF-8";
}
The inputs for my system’s configuration are very simple
- nixpkgs - the main nix repository of packages. Its huge and growing. Pinned to the unstable release channel. Sometimes pinned to a specific commit because unstable broke something and the fix hasn’t made it into the release yet.
- home-manager - a nix module that helps keep track of user specific dotfiles and configurations as part of my nix config.
- emacs-overlay - this has more configuration options and generally a newer emacs available provided by the community.
{
description = "Sandeep's nixos configuration";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
emacs-overlay.url = "github:nix-community/emacs-overlay";
sops-nix.url = "github:Mic92/sops-nix";
};
<<flake-outputs>>
}
Now that the inputs are ready, the outputs define what the system will actually look like. I also define the machines that this configuration specifies early on. Finally, I iterate over the machines
list and pull files from /.machines/${name}
subdirectory. This allows me to have configuration that has machine specific configuration limited to those files while also keeping a modular reusable base.
We also add a devshell that makes editing this repository easier in emacs.
outputs = {
nixpkgs,
home-manager,
emacs-overlay,
sops-nix,
...
}:
let
user = import ./user.nix;
lib = nixpkgs.lib;
machines = [
"bigbox"
"laptopdell"
];
pkgs = import nixpkgs {
inherit (user) system;
};
in
{
nixosConfigurations = builtins.listToAttrs (
builtins.map (machine: {
name = machine;
value = lib.nixosSystem {
modules = [
<<flake-emacs-module>>
<<flake-config-module>>
<<flake-home-module>>
sops-nix.nixosModules.sops # sops
];
specialArgs = {
hostname = machine;
inherit user;
};
};
}) machines
);
devShells.${user.system}.default = pkgs.mkShell {
buildInputs = with pkgs; [
nil # nix lsp server
nixfmt-rfc-style # nix formatter
sops # used to edit secrets
];
};
};
Lets look at the individual modules
- Emacs
The first is the emacs overlay so that it uses the nix-community emacs overlay from the inputs instead of the nixpkgs one.
Overlays are a special nix way to override existing packages within a repository.
({ ... }: { nixpkgs.overlays = [ emacs-overlay.overlays.default ]; })
- Then the machine specific configuration
./machines/${machine}/configuration.nix
- And finally the home-manager module.
This can be initialized and managed on its own but I’d rather use the
nixos-rebuild
command to build everything instead of managing userland dotfiles separately.home-manager.nixosModules.home-manager { home-manager.useGlobalPkgs = true; home-manager.useUserPackages = true; home-manager.extraSpecialArgs = { inherit user; }; <<flake-home-backup>> <<flake-home-config>> }
- Home-Manager will not overwrite existing configuration files and that is good in most cases, but when everything is declarative like it is here, I’d rather that home-manager create a
.backup
and replace the file.home-manager.backupFileExtension = "backup";
- Finally I pull in the machine specific home configuration.
home-manager.users.${user.username} = { imports = [./machines/${machine}/home.nix]; };
- Home-Manager will not overwrite existing configuration files and that is good in most cases, but when everything is declarative like it is here, I’d rather that home-manager create a
Editing this file will be much nicer if we have the dev environment configured. That is done in the devshells section. But to auto load this dev shell, we need a .envrc file. This tells direnv to load the devshell in the flake.
use flake
The individual machines subdirectory is configured as follows :-
+--machine
| +--configuration.nix
| +--home.nix
| +--hardware-configuration.nix
- configuration.nix has the system configuration.
- home.nix has the user level configuration.
- hardware-configuration.nix has the unique hardware configuration.
nixos-rebuild switch --flake .#bigbox
looks for the bigbox configuration (in /machines/bigbox
) and returns the configuration that is specific to that computer.
This is my main computer and is also the base configuration (modified by the laptop).
So here, the configuration.nix
just directly imports the other files.
{ user, ... } :
{
imports =
[
./hardware-configuration.nix
../../hardware/nvidia.nix
../../configuration
];
<<bigbox-secrets>>
}
- Note about imports
imports = []
in a nix file will pull in the function/object from the list of files provided. This imported object (or function result) is just trivially merged into a common object.
Lets look at hardware-configuration.nix
for bigbox.
{
hostname,
pkgs,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
../../hardware/hardware.nix
];
boot.initrd.availableKernelModules = [
"xhci_pci"
"ahci"
"usbhid"
"usb_storage"
"sd_mod"
];
fileSystems."/" = {
device = "/dev/disk/by-uuid/911d76da-1990-4e83-aefb-fa116e9cd0ee";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/BAD3-5A84";
fsType = "vfat";
};
fileSystems."/home" = {
device = "/dev/disk/by-uuid/18818348-1ee4-4fa5-9984-e4e01b9fa304";
fsType = "ext4";
};
swapDevices = [ ];
<<bigbox-scanner>>
networking.hostName = hostname; # this machine is available as "bigbox" on the network
}
Most of this file is copied over from the first installation of nixos where it auto-detects partitions along with existing hardware and adds them to this file.
I also need the machine to detect the epson scanner available on the network.
hardware.sane.enable = true;
hardware.sane.extraBackends = [
pkgs.sane-airscan
pkgs.utsushi
];
services.udev.packages = [ pkgs.utsushi ]; # scanner backend for epson
Finally, you’ll notice the ../../hardware/hardware.nix
import at the top.
We can take a look at that next and see the common hardware options I have for all my machines.
I’ll let the code comments explain the file here.
{ pkgs, lib, user, config, ...} :
{
# allow automatic ip assignment when connecting to a network
networking.useDHCP = lib.mkDefault true;
# let wifi info be NOT declarative, allowing user to configure wifi.
networking.wireless.userControlled.enable = true;
nixpkgs.hostPlatform = lib.mkDefault user.system; # x86_64-linux
powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; # enable power saving on the cpu
# update cpu microcode with firmware that allows redistribution
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
hardware = {
# always enable bluetooth
bluetooth.enable = true;
# always enable graphics drivers and enable a bunch of layers for it (including vulkan validation)
graphics = {
enable = true;
extraPackages = with pkgs; [
intel-media-driver
vaapiIntel # video acceleration on intel inbuilt graphics
vulkan-validation-layers # helps catch and debug vulkan crashes
];
};
};
hardware.enableAllFirmware = true; # enable all firmware regardless of license
}
I have nvidia graphics on both machines and configuring it is almost always painful. But this is the base I’ve settled on for now. All Nvidia related configuration goes here.
{ config, pkgs, ... }:
{
boot.kernelParams = [
"nvidia-drm.modeset=1" # nvidia needs "Direct Rendering Manager" kernel mode to work with wayland.
"nvidia-drm.fbdev=1" # it also needs kernel frame buffer as well. Dont ask why.
];
hardware.graphics.extraPackages = with pkgs; [
vaapiVdpau # video acceleration on nvidia graphics
libvdpau-va-gl # vdpau driver with opengl backend
];
hardware.nvidia = {
modesetting.enable = true; # lets the driver set kernel mod settings autoamagically
powerManagement.enable = true; # allows the gpu to be switched of when not needed.
powerManagement.finegrained = false; # even finegrained, per app
open = false; # use proprietary drivers
nvidiaSettings = true; # install the nvidia-settings app
# latest greatest driver always
package = config.boot.kernelPackages.nvidiaPackages.latest;
};
# add vulkan headers and tools to the system
environment.systemPackages = with pkgs; [
vulkan-headers
vulkan-loader
vulkan-tools
];
# this enables the nvidia gpu. even for wayland.
services.xserver.videoDrivers = [ "nvidia" ];
}
This section describes the main system configuration for the computers that I have. Nix will look for a default.nix
file if you give it a path to a folder to import. And default.nix
looks as follows :-
{ pkgs, user, ... } :
{
imports = [
./boot.nix
./login.nix
./cli.nix
./files.nix
./locale.nix
./nix-settings.nix
./networking.nix
./hyprland.nix
./services.nix
./audio.nix
./steam.nix
./sops.nix
./wireguard.nix
];
<<config-system-packages>>
<<config-user>>
<<config-programs>>
<<config-fonts>>
system.stateVersion = user.stateVersion;
}
Whoa. Thats a lot of imports. Lets go through them one by one.
These are global nix settings that configure the settings for the actual tool.
{ pkgs, user, ... } :
{
nix.settings = {
# enable flakes
experimental-features = ["nix-command" "flakes"];
# add a cache that speed up new applications by downloading binaries
# from the trusted cache instead of compiling from source
substituters = [ "https://nix-community.cachix.org" ];
# trust the cache public key
trusted-public-keys = [
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
};
# allow proprietary software on this machine. I'm not a purist.
nixpkgs.config.allowUnfree = true;
# I work with games and unity. And Unity 2021 needs this to work.
# But nix wont allow an insecure package unless explicitly declared as allowed.
nixpkgs.config.permittedInsecurePackages = [
"openssl-1.1.1w"
];
# this declares how often old configurations are cleared up.
# i cleanup anything older than a week, every week.
nix.gc = {
automatic = true;
options = "--delete-older-than 7d";
dates = "weekly";
};
programs = {
# command line utility that makes applying changes easy and pretty
nh = {
enable = true;
flake = "/home/${user.username}/system";
};
};
}
This file has most of the settings the control how the computer boots up.
{ pkgs, ... } :
{
boot = {
initrd = {
verbose = false; # its a lot of logs. dont need it, unless we do.
kernelModules = [ ]; # no kernel modules on boot
};
extraModulePackages = [ ]; # no extra packages on boot either
kernelPackages = pkgs.linuxPackages_latest; # latest greatest linux kernel
kernelParams = [ "silent" ]; # quiet those logs
consoleLogLevel = 0; # quiten more logs
plymouth.enable = true; # graphical boot animation instead
supportedFilesystems = [ "ntfs" ]; # should see the ntfs (windows)
loader = {
systemd-boot.enable = true; # systemd-boot
efi.canTouchEfiVariables = true; # allow editing efi to edit the boot loader
grub = {
# this keeps old versions of my applied configurations
# 50 is high, but i have disk space
configurationLimit = 50;
};
timeout = 5; # grub timeout to make a selection
};
};
}
Here we control what the login screen would look like. I’m using greetd’s tuigreet for the ui. Doesn’t match the rest of the aesthetic of the system (with hyprland), but I like its simplicity.
{ pkgs, ... } :
{
environment.systemPackages = with pkgs; [
greetd.tuigreet
];
services.greetd = {
enable = true;
settings = {
default_session = {
command = pkgs.lib.mkForce "${pkgs.greetd.tuigreet}/bin/tuigreet --time --time-format '%I:%M %p | %a • %h | %F'";
};
};
};
}
This is the initial system level configuration for the terminal that I use on this machine. Its just zsh.
{ pkgs, user, ... }:
{
console.useXkbConfig = true;
users.users.${user.username}.shell = pkgs.zsh;
environment.shells = with pkgs; [ zsh ];
programs.zsh.enable = true;
}
I use Thunar as the file explorer. Also setup a few plugins for Thunar in this config. Along with that, a few other utilities like zip and enabling services to automount usb drives.
{ pkgs, ... } :
{
environment.systemPackages = with pkgs; [
zip
unzip
p7zip
usbutils
udiskie
];
programs.thunar = {
enable = true;
plugins = with pkgs.xfce; [
thunar-archive-plugin
thunar-media-tags-plugin
];
};
services = {
gvfs.enable = true; # Mount, trash, and other functionalities
tumbler.enable = true; # Thumbnail support for images
udisks2.enable = true; # Auto mount usb drives
};
programs.file-roller.enable = true; # thunar zip support
}
I live in Sweden and would like all my locale and timezone settings to match. Except my default locale.
{ user, ... } :
let
locale = user.locale;
in
{
# Set your time zone.
time.timeZone = "Europe/Stockholm";
# Select internationalisation properties.
i18n.defaultLocale = "en_US.UTF-8";
i18n.extraLocaleSettings = {
LC_ADDRESS = locale;
LC_IDENTIFICATION = locale;
LC_MEASUREMENT = locale;
LC_MONETARY = locale;
LC_NAME = locale;
LC_NUMERIC = locale;
LC_PAPER = locale;
LC_TELEPHONE = locale;
LC_TIME = locale;
};
}
Not much to see here. I want networking to be enabled. I want firewall as well.
{
networking = {
networkmanager.enable = true;
firewall.enable = true;
};
}
This is a big one because the DE needs so much configuration. This section mostly installs Hyprland. The configuration is done in the home manager section.
{ pkgs, ... }:
{
nix.settings = {
# add the hyprland cache so that we dont build hyprland from source
substituters = [ "https://hyprland.cachix.org" ];
trusted-public-keys = [
"hyprland.cachix.org-1:a7pgxzMz7+chwVL3/pzj6jIBMioiJM7ypFP8PwtkuGc="
];
};
# these extra portals allow for things like screen sharing
xdg = {
portal = {
enable = true;
extraPortals = [
pkgs.xdg-desktop-portal-wlr
pkgs.xdg-desktop-portal-gtk
];
};
};
environment.systemPackages = with pkgs; [
hyprland # the actual package
tofi # launcher
uwsm # wayland session manager
hyprland-qtutils # needed by hyprland
lxqt.lxqt-policykit # to interact with polkit and show auth dialog
];
# we use uwsm to manage launching hyprland
# uswm will add hyprland to the login sessions with greetd.
programs = {
uwsm.enable = true;
uwsm.waylandCompositors = {
hyprland = {
prettyName = "Hyprland";
comment = "Hyprland compositor managed by UWSM";
binPath = "/run/current-system/sw/bin/Hyprland";
};
};
hyprland = {
withUWSM = true;
enable = true;
xwayland.enable = true;
};
};
# this is mainly for the lock screen
# lock.png is provided elsewhere
services.xserver = {
enable = true;
desktopManager = {
xterm.enable = false;
};
displayManager = {
lightdm.background = ./lock.png;
};
};
# this is a lot of env vars.
# and this requires some cleanup
# but hyprland moves fast and some of these are probably outdated already
environment.sessionVariables = {
LIBVA_DRIVER_NAME="nvidia";
XDG_SESSION_TYPE="wayland";
XDG_CURRENT_DESKTOP="Hyprland";
XDG_SESSION_DESKTOP="Hyprland";
GBM_BACKEND="nvidia-drm";
__GLX_VENDOR_LIBRARY_NAME="nvidia";
NVD_BACKEND="direct";
NIXOS_OZONE_WL="1";
GTK_THEME="Nordic";
XCURSOR_THEME="Nordzy-cursors";
XCURSOR_SIZE="24";
HYPRCURSOR_THEME="Nordzy-cursors";
HYPRCURSOR_SIZE="24";
};
# allow swaylock (lockscreen) to lock user session
security.pam.services.swaylock = { };
security.polkit.enable = true;
security.pam.services.gdm.enableGnomeKeyring = true;
}
These are some of the services that I enable at the system level. Explanation in the comments.
{
services = {
blueman.enable = true; # bluetooth manager
fwupd.enable = true; # firmware updating service
fstrim.enable = true; # ssd maintenance service
thermald.enable = true; # thermal regulation service
printing.enable = true; # printing services, cups
gnome.gnome-keyring.enable = true; # keyring
flatpak.enable = true; # allow installing things from flatpaks
# printer discovery
avahi = {
enable = true;
nssmdns4 = true;
openFirewall = true;
};
};
}
This is still a work in progress, but it almost works. I enable all the audio related services, hoping everything is going to be okay.
{ pkgs, ...}:
{
environment.systemPackages = with pkgs; [
pamixer
];
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
jack.enable = true;
};
# pipewire needs realtime scheduling access
security.rtkit.enable = true;
}
Finally, I have steam installed and it requires a few extra things as well.
{ pkgs, ... } :
{
environment.systemPackages = with pkgs; [
steam-run # also used for random executables that expect fhs
];
programs.steam = {
enable = true;
# Open ports in the firewall for Steam Remote Play
remotePlay.openFirewall = true;
# Open ports in the firewall for Source Dedicated Server
dedicatedServer.openFirewall = true;
};
}
We use sops to manage secrets on this machine.
{ user, ...} :
{
sops.defaultSopsFile = ../secrets/secrets.yaml;
sops.defaultSopsFormat = "yaml";
sops.age.keyFile = "/home/${user.username}/.config/sops/age/keys.txt";
sops.secrets.claude_key = { # anthropic claude api key, used in emacs
owner = "${user.username}";
};
}
Sops requires a public .sops.yaml
that dictates how the secrets are encrypted.
This contains the public key and path to the secrets file.
keys:
- &primary age1yq35g6mmlem0rhr47u6ewh8dctlwp9hj0s0ac60e4hrw9hjzlqms6crf7n
creation_rules:
- path_regex: secrets/secrets.yaml$
key_groups:
- age:
- *primary
Use this to connect to work vpn over wireguard.
{ user, ...} :
{
sops.secrets.wireguard_coherence = {
owner = "${user.username}";
};
networking.wg-quick.interfaces.wg0.configFile = "/run/secrets/wireguard_coherence";
}
I can then start/stop thi service using systemctl.
systemctl start wg-quick-wg0.service
systemctl stop wg-quick-wg0.service
environment.systemPackages = with pkgs; [
wget # fetch utility
curl # more fetch utility
binutils # executable utilities, like ld
dmidecode # tool for dumping system info
libnotify # notification daemon
python3 # nice to have this ready for quick things
cacert # certificate authority
remmina # remote desktop app
];
programs = {
nix-ld.enable = true; # helps with linking troubles with dynamic libraries
appimage.enable = true; # allow appimage installations
dconf.enable = true; # to save user settings
gnupg.agent = {
# pgp client
enable = true;
enableSSHSupport = true;
};
firefox.enable = true; # browser
wireshark.enable = true; # vpn
};
Nothing much to see here. I love iosevka, and I use it everywhere.
fonts.packages = with pkgs; [
iosevka
nerd-fonts.iosevka
];
This creates the user profile that I login with. Initially created during install.
users.users.${user.username} = {
isNormalUser = true;
description = "Sandeep Nambiar";
extraGroups = [
"networkmanager" # allow editing network connections
"wheel" # can do sudo
"scanner" # access to the network scanner
"lp" # access to the printer
];
};
I use home-manager to manage my user level dotfiles and configurations. Most of the “theme” of the system is decided here. I also use it to install programs that are okay with being installed at the user level instead of the system.
{ pkgs, user, ... } :
{
imports = [
./waybar.nix
./wallpaper.nix
./lock.nix
./unity.nix
./hyprland.nix
./tofi.nix
./theme.nix
./terminal.nix
./dev.nix
./emacs
];
<<home-user>>
<<home-packages>>
programs.home-manager.enable = true;
}
Oof! Again with all the imports! We can go through them one at a time!
Mostly styling and enabling modules in the top bar.
{
programs.waybar = {
enable = true;
settings = {
mainBar = {
layer = "top";
height = 24;
modules-left = [ "hyprland/workspaces" ];
modules-center = [ "hyprland/window" ];
modules-right = [
"idle_inhibitor"
"pulseaudio"
"network"
"cpu"
"memory"
"temperature"
"clock"
"tray"
"battery"
];
"idle_inhibitor" = {
format = "{icon}";
format-icons = {
activated = "";
deactivated = "";
};
};
"network".format = "{ipaddr}";
"cpu".tooltip = false;
"temperature".tooltip = false;
"tray".spacing = 10;
};
};
style = ''
* {
font-family: Iosevka Nerd Font, Roboto, Helvetica, Arial, sans-serif;
font-size: 13px;
}
window#waybar {
background-color: #4c566a;
color: #ffffff;
transition-property: background-color;
border-bottom: 0px solid rgba(0, 0, 0, 0);
transition-duration: .5s;
}
#workspaces button {
padding: 0 5px;
background-color: transparent;
border: none;
border-radius: 0;
}
#workspaces button:hover {
background: #2e3440;
color: white;
}
#workspaces button.active {
background-color: #5e81ac;
box-shadow: inset 0 -3px #ffffff;
}
#clock,
#idle_inhibitor
#battery,
#cpu,
#memory,
#temperature,
#network,
#pulseaudio,
#tray {
padding: 0 10px;
background-color: transparent;
}
#window,
#workspaces {
margin: 0 4px;
}
.modules-left > widget:first-child > #workspaces {
margin-left: 0;
}
.modules-right > widget:last-child > #workspaces {
margin-right: 0;
}
#battery.charging, #battery.plugged {
color: #ffffff;
background-color: #26A65B;
}
#network.disconnected {
background-color: #f53c3c;
}
#temperature.critical {
background-color: #eb4d4b;
}
'';
};
}
I use hyprpaper for setting the wallpaper. The image is copied into the home folder when applying this configuration and the tool picks it from there.
{ pkgs, ... }:
{
home.packages = with pkgs; [
hyprpaper
];
services.hyprpaper.enable = true;
services.hyprpaper.settings = {
ipc = "on";
splash = false;
splash_offset = 2.0;
preload = [ "~/.background-image.png" ];
wallpaper = [ ",~/.background-image.png" ];
};
home.file = {
background = {
source = ../assets/background.png;
target = ".background-image.png";
};
};
}
The lock screen configured using swaylock. I use swayidle to detect idle time and use wlogout to show a logout menu. They are configured below.
{ pkgs, ... } :
{
home.packages = with pkgs; [
swaylock
swayidle
wlogout
];
programs.swaylock.enable = true;
home.file = {
lock = {
source = ../assets/lock.png;
target = ".lock.png";
};
};
programs.swaylock.settings = {
image = "~/.lock.png";
ignore-empty-password = true;
show-failed-attempts = true;
font = "Iosevka";
};
services = {
# swayidle to lock using swaylock after 180 seconds
# suspend after 10 minutes
# lock before suspend
swayidle = {
enable = true;
timeouts = [
{
timeout = 185;
command = "swaylock";
}
{
timeout = 600;
command = "systemctl suspend";
}
];
events = [
{
event = "before-sleep";
command = "swaylock";
}
];
};
};
programs.wlogout = {
enable = true;
layout = [
{
"label" = "lock";
"action" = "swaylock";
"text" = "Lock";
"keybind" = "l";
}
{
"label" = "shutdown";
"action" = "systemctl poweroff";
"text" = "Shutdown";
"keybind" = "s";
}
{
"label" = "suspend";
"action" = "swaylock & sleep 1 ; systemctl suspend";
"text" = "Suspend";
"keybind" = "u";
}
{
"label" = "reboot";
"action" = "systemctl reboot";
"text" = "Reboot";
"keybind" = "r";
}
{
"label" = "hibernate";
"action" = "systemctl hibernate";
"text" = "Hibernate";
"keybind" = "h";
}
{
"label" = "reboot";
"action" = "systemctl reboot";
"text" = "Reboot";
"keybind" = "r";
}
];
};
}
I work with the Unity Game Engine and I have the unity hub installed globally, instead of in a project flake. Unity 2021 depends on openssl 1.1.1 (deprecated) so we override the existing package with a version that also includes it. The HDRP renderer also needs vulkan libraries, but we installed that globally.
{ pkgs, ... }:
{
home.packages = with pkgs; [
(pkgs.unityhub.override {
extraPkgs = pkgs: with pkgs; [
openssl_1_1
];
})
];
}
This configures the desktop environment along with the peripherals. The comments should explain whats happening.
{
# required for the default Hyprland config
programs.kitty.enable = true;
# enable Hyprland
wayland.windowManager.hyprland.enable = true;
# we start hyprland using uwsm
wayland.windowManager.hyprland.systemd.enable = false;
# hyprland.conf
wayland.windowManager.hyprland.settings = {
exec-once = [
# read in env vars
"dbus-update-activation-environment --systemd --all"
# start the wallpaper service
"systemctl --user enable --now hyprpaper.service"
# start waybar
"uwsm app -- waybar"
# start the policykit agent
"uwsm app -- lxqt-policykit-agent"
];
input = {
follow_mouse = "1";
touchpad = {
natural_scroll = "no";
};
sensitivity = 0; # -1.0 - 1.0, 0 means no modification.
kb_layout = "us";
# very emacs specific, i use caps as an extra ctrl key
kb_options = "ctrl:nocaps";
};
cursor = {
no_hardware_cursors = "true";
};
general = {
gaps_in = 2;
gaps_out = 4;
border_size = 2;
"col.active_border" = "rgba(33ccffee) rgba(00ff99ee) 45deg";
"col.inactive_border" = "rgba(595959aa)";
layout = "dwindle";
};
decoration = {
rounding = 5;
};
animations = {
enabled = "yes";
bezier = "myBezier, 0.05, 0.9, 0.1, 1.05";
animation = [
"windows, 1, 7, myBezier"
"windowsOut, 1, 7, default, popin 80%"
"border, 1, 10, default"
"borderangle, 1, 8, default"
"fade, 1, 7, default"
"workspaces, 1, 6, default"
];
};
dwindle = {
pseudotile = "yes";
preserve_split = "yes";
};
gestures = {
workspace_swipe = "off";
};
misc = {
disable_hyprland_logo = true;
focus_on_activate = true;
};
"$mainMod" = "SUPER";
bind = [
"$mainMod, Return, exec, uwsm app -- alacritty"
"$mainMod_SHIFT, Q, killactive"
"$mainMod_SHIFT, M, exit"
"$mainMod, F, exec, uwsm app -- thunar"
"$mainMod_SHIFT, Space, togglefloating"
# use tofi to show exec menu
"$mainMod, Space, exec, uwsm app -- tofi-run | xargs hyprctl dispatch -- exec"
"$mainMod, P, pseudo"
"$mainMod, J, togglesplit"
"$mainMod_SHIFT, left, movewindow, l"
"$mainMod_SHIFT, right, movewindow, r"
"$mainMod_SHIFT, up, movewindow, u"
"$mainMod_SHIFT, down, movewindow, d"
"$mainMod, left, movefocus, l"
"$mainMod, right, movefocus, r"
"$mainMod, up, movefocus, u"
"$mainMod, down, movefocus, d"
"$mainMod, mouse_down, workspace, e+1"
"$mainMod, mouse_up, workspace, e-1"
"$mainMod_SHIFT, S, exec, wlogout"
] ++ (
# workspaces
# binds $mod + [shift +] {1..9} to [move to] workspace {1..9}
builtins.concatLists (builtins.genList (i:
let ws = i + 1;
in [
"$mainMod, ${toString ws}, workspace, ${toString ws}"
"$mainMod SHIFT, ${toString ws}, movetoworkspace, ${toString ws}"
]
)
9)
);
bindm = [
"$mainMod, mouse:272, movewindow"
"$mainMod, mouse:273, resizewindow"
];
};
}
This is how I launch applications. It is bound to Win+Space in the hyprland config above. Configuration is mostly for visual things here.
{
programs.tofi.enable = true;
programs.tofi.settings = {
font = "Iosevka";
font-size = 16;
selection-color = "#a3be8c";
prompt-text = ":";
width = 600;
height = 480;
background-color = "#2e3440";
outline-width = 0;
outline-color = "#080800";
border-width = 1;
border-color = "#8fbcbb";
corner-radius = 10;
padding-top = 4;
padding-bottom = 4;
padding-left = 4;
padding-right = 4;
matching-algorithm = "fuzzy";
};
}
I use the Nord palette almost everywhere. I’ve heard that stylix is good for this, but for now, its manual. You’ll notice the color values in multiple places outside this as well.
{ pkgs, ...}:
{
home.packages = with pkgs; [
nordzy-icon-theme
nordzy-cursor-theme
nordic
];
gtk = {
enable = true;
iconTheme = {
name = "Nordzy-icon";
package = pkgs.nordzy-icon-theme;
};
theme = {
name = "Nordic";
package = pkgs.nordic;
};
cursorTheme = {
name = "Nordzy-cursors";
package = pkgs.nordzy-cursor-theme;
};
};
}
Alacritty is my terminal program. The snippet below configures how it looks. Mostly nord palette things.
{
programs = {
alacritty = {
enable = true;
settings = {
font.normal.family = "Iosevka Nerd Font";
font.size = 12;
terminal.shell.program = "zsh";
window = {
padding.x = 4;
padding.y = 4;
};
colors = {
primary = {
background = "#1f222d";
foreground = "#d8dee9";
dim_foreground = "#a5abb6";
};
cursor = {
text = "#2e3440";
cursor = "#d8dee9";
};
normal = {
black = "#3b4252";
red = "#bf616a";
green = "#a3be8c";
yellow = "#ebcb8b";
blue = "#81a1c1";
magenta = "#b48ead";
cyan = "#88c0d0";
white = "#e5e9f0";
};
};
};
};
};
}
All the miscellaneous dev tools on this computer. Git config is from ~user.nix~.
{ user, ... }:
{
programs = {
vscode.enable = true; # yes, sometimes i like to dabble
vim.enable = true; # and this one too
ripgrep.enable = true; # fast text search across projects
htop.enable = true; # task manager
# this is mainly for integration with nix flakes in individual projects
direnv = {
enable = true;
enableZshIntegration = true;
nix-direnv.enable = true;
};
# zsh everywhere with oh-my-zsh
zsh = {
enable = true;
oh-my-zsh = {
enable = true;
plugins = [ "git" ];
theme = "robbyrussell";
};
};
# git with lfs
git = {
lfs.enable = true;
enable = true;
userName = "${user.gitUser}";
userEmail = "${user.gitEmail}";
};
};
}
Some repeated info from the configuration.
home.username = "${user.username}";
home.homeDirectory = pkgs.lib.mkDefault "/home/${user.username}";
home.stateVersion = user.stateVersion;
A bunch of programs that I use.
home.packages = with pkgs; [
audacity # audio recording
zoom-us # meetings
handbrake # video transcoding
sway-contrib.grimshot # screenshot tool
xdg-utils # utils, for screensharing
vlc # media player
discord # other chat
slack # work chat
pavucontrol # audio control
everdo # gtd tool
spotify # music player
simple-scan # scanner software
];
services.dunst.enable = true; # notifications daemon
programs.obs-studio.enable = true; # screen recording tool
I practically live inside emacs. The configuration for it is a mix between init.el and the nix configuration. Nix allows me to install emacs packages as part of the configuration which is most of the following file. I install the nix community provided emacs overlay that lets me have the latest emacs with pgtk ui (for wayland). Comments describe the emacs package and what it does.
{ pkgs, ... }:
{
programs.emacs = {
enable = true;
# install with tree sitter enabled
package = (pkgs.emacs-pgtk.override { withTreeSitter = true; });
extraPackages = epkgs: [
# also install all tree sitter grammars
epkgs.manualPackages.treesit-grammars.with-all-grammars
pkgs.nodejs # some emacs deps need node installed
pkgs.omnisharp-roslyn # c# lsp
epkgs.nerd-icons # nerd fonts support
epkgs.doom-modeline # model line
epkgs.doom-themes # for doom-nord theme
epkgs.diminish # hides modes from modeline
epkgs.eldoc # doc support
epkgs.whitespace-cleanup-mode # cleanup accidental whitespace
epkgs.pulsar # pulses the cursor when jumping about
epkgs.which-key # help porcelain
epkgs.expreg # expand region
epkgs.vundo # undo tree
epkgs.puni # structured editing
epkgs.avy # jumping utility
epkgs.consult # emacs right click
epkgs.vertico # minibuffer completion
epkgs.marginalia # annotations for completions
epkgs.crux # utilities
epkgs.magit # git porcelain
epkgs.nerd-icons-corfu # nerd icons for completion
epkgs.corfu # completion
epkgs.cape # completion extensions
epkgs.orderless # search paradigm
epkgs.yasnippet # snippets support
epkgs.yasnippet-snippets # commonly used snippets
epkgs.projectile # project management
epkgs.rg # ripgrep
epkgs.exec-path-from-shell # load env and path
epkgs.flycheck # error integration
epkgs.lsp-mode # lsp
epkgs.lsp-ui # ui for lsp
epkgs.rust-mode # rust mode (when rust-ts doesn't cut it)
epkgs.nix-mode # nix lang
epkgs.eat # better shell
epkgs.hcl-mode # hashicorp file mode
epkgs.shell-pop # quick shell popup
epkgs.f # string + file utilities
epkgs.gptel # llm chat (mainly claude)
epkgs.copilot # emacs copilot plugin
epkgs.envrc # support for loading .envrc
epkgs.nixpkgs-fmt # format nix files
];
};
home.sessionVariables = {
EDITOR = "emacs";
};
home.file = {
emacs-init = {
source = ./early-init.el;
target = ".emacs.d/early-init.el";
};
emacs = {
source = ./init.el;
target = ".emacs.d/init.el";
};
};
}
There are some emacs settings that can be configured before the gui shows up. And some of them help increase performance and let the gui show up that much faster. These are listed here.
;;; package --- early init -*- lexical-binding: t -*-
;;; Commentary:
;;; Prevents white flash and better Emacs defaults
;;; Code:
(set-language-environment "UTF-8")
(setq-default
default-frame-alist
'((background-color . "#3F3F3F")
(bottom-divider-width . 1) ; Thin horizontal window divider
(foreground-color . "#DCDCCC") ; Default foreground color
(fullscreen . maximized) ; Maximize the window by default
(horizontal-scroll-bars . nil) ; No horizontal scroll-bars
(left-fringe . 8) ; Thin left fringe
(menu-bar-lines . 0) ; No menu bar
(right-divider-width . 1) ; Thin vertical window divider
(right-fringe . 8) ; Thin right fringe
(tool-bar-lines . 0) ; No tool bar
(undecorated . t) ; Remove extraneous X decorations
(vertical-scroll-bars . nil)) ; No vertical scroll-bars
user-full-name "Sandeep Nambiar" ; ME!
;; memory configuration
;; Higher garbage collection threshold, prevents frequent gc locks
gc-cons-threshold most-positive-fixnum
;; Ignore warnings for (obsolete) elisp compilations
byte-compile-warnings '(not obsolete)
;; And other log types completely
warning-suppress-log-types '((comp) (bytecomp))
;; Large files are okay in the new millenium.
large-file-warning-threshold 100000000
;; Read more based on system pipe capacity
read-process-output-max (max (* 10240 10240) read-process-output-max)
;; scroll configuration
scroll-margin 0 ; Lets scroll to the end of the margin
scroll-conservatively 100000 ; Never recenter the window
scroll-preserve-screen-position 1 ; Scrolling back and forth
;; frame config
;; Improve emacs startup time by not resizing to adjust for custom settings
frame-inhibit-implied-resize t
;; Dont resize based on character height / width but to exact pixels
frame-resize-pixelwise t
;; backups & files
backup-directory-alist '(("." . "~/.backups/")) ; Don't clutter
backup-by-copying t ; Don't clobber symlinks
create-lockfiles nil ; Don't have temp files
delete-old-versions t ; Cleanup automatically
kept-new-versions 6 ; Update every few times
kept-old-versions 2 ; And cleanup even more
version-control t ; Version them backups
delete-by-moving-to-trash t ; Dont delete, send to trash instead
;; startup
inhibit-startup-screen t ; I have already done the tutorial. Twice
inhibit-startup-message t ; I know I am ready
inhibit-startup-echo-area-message t ; Yep, still know it
initial-scratch-message nil ; I know it is the scratch buffer!
;; tabs
tab-width 4 ; Always tab 4 spaces.
indent-tabs-mode nil ; Never use actual tabs.
;; rendering
cursor-in-non-selected-windows nil ; dont render cursors other windows
;; packages
use-package-always-defer t
;; custom
custom-file (concat user-emacs-directory "custom.el")
load-prefer-newer t
default-input-method nil
)
;;; early-init.el ends here
Now starts the main emacs configuration.
;;; package --- Summary - My minimal Emacs init file -*- lexical-binding: t -*-
;;; Commentary:
;;; Simple Emacs setup I carry everywhere
;;; Code:
(load custom-file 'noerror) ;; no error on missing custom file
(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(add-to-list 'package-archives '("nongnu" . "https://elpa.nongnu.org/nongnu/") t)
(package-initialize)
(use-package emacs
:init
(global-auto-revert-mode t) ;; revert automatically on external file changes
(savehist-mode) ;; save minibuffer history
;; base visual
(menu-bar-mode -1) ;; no menu bar
(toggle-scroll-bar -1) ;; no scroll bar
(tool-bar-mode -1) ;; no tool bar either
(global-hl-line-mode +1) ;; always highlight current line
(blink-cursor-mode -1) ;; stop blinking
(global-display-line-numbers-mode 1) ;; always show line numbers
(column-number-mode t) ;; column number in the mode line
(size-indication-mode t) ;; file size in the mode line
(pixel-scroll-precision-mode) ;; smooth mouse scroll
(fset 'yes-or-no-p 'y-or-n-p) ;; y/n is good enough
(electric-pair-mode) ;; i mean ... parens should auto create
(recentf-mode) ;; keep track of recently opened files
;; font of the century
(set-frame-font "Iosevka Nerd Font 12" nil t)
:bind
(("C-<wheel-up>" . pixel-scroll-precision) ; dont zoom in please, just scroll
("C-<wheel-down>" . pixel-scroll-precision) ; dont zoom in either, just scroll
("C-x k" . kill-current-buffer)) ; kill the buffer, dont ask
)
(use-package nerd-icons
:custom
;; disable bright icon colors
(nerd-icons-color-icons nil))
(use-package doom-modeline
:custom
(inhibit-compacting-font-caches t) ;; speed
(doom-modeline-buffer-file-name-style 'relative-from-project)
(doom-modeline-major-mode-icon nil) ;; distracting icons, no thank you
(doom-modeline-buffer-encoding nil) ;; everything is utf-8 anyway
(doom-modeline-buffer-state-icon nil) ;; the filename already shows me
(doom-modeline-lsp nil) ;; lsp state is too distracting, too often
:hook (after-init . doom-modeline-mode))
(use-package doom-themes
:commands doom-themes-visual-bell-config
:custom
(doom-themes-enable-bold t)
(doom-themes-enable-italic t)
:init
(load-theme 'doom-nord t)
(doom-themes-visual-bell-config))
(use-package diminish :demand t) ;; declutter the modeline
(use-package eldoc :diminish eldoc-mode) ;; docs for everything
(use-package whitespace-cleanup-mode
:commands global-whitespace-cleanup-mode
:custom
(whitespace-cleanup-mode-only-if-initially-clean nil)
:hook
(after-init . global-whitespace-cleanup-mode))
(use-package pulsar
:commands pulsar-global-mode pulsar-recenter-top pulsar-reveal-entry
:init
(defface pulsar-nord
'((default :extend t)
(((class color) (min-colors 88) (background light))
:background "#2e3440")
(((class color) (min-colors 88) (background dark))
:background "#81a1c1")
(t :inverse-video t))
"Alternative nord face for `pulsar-face'."
:group 'pulsar-faces)
:custom
(pulsar-face 'pulsar-nord)
:hook
(after-init . pulsar-global-mode))
(use-package which-key
:commands which-key-mode
:diminish which-key-mode
:hook
(after-init . which-key-mode))
(use-package expreg
:bind ("M-m" . expreg-expand))
(use-package vundo) ;; undo tree
;; better structured editing
(use-package puni
:commands puni-global-mode
:hook
(after-init . puni-global-mode))
(use-package avy
:bind
("M-i" . avy-goto-char-2)
:custom
(avy-background t))
(use-package consult
:bind
("C-x b" . consult-buffer) ;; orig. switch-to-buffer
("M-y" . consult-yank-pop) ;; orig. yank-pop
("M-g M-g" . consult-goto-line) ;; orig. goto-line
:custom
(consult-narrow-key "<"))
(use-package vertico
:commands vertico-mode
:custom
(read-file-name-completion-ignore-case t)
(read-buffer-completion-ignore-case t)
(completion-ignore-case t)
(enable-recursive-minibuffers t)
:init
(vertico-mode)
:config
(setq minibuffer-prompt-properties
'(read-only t cursor-intangible t face minibuffer-prompt))
:hook
(minibuffer-setup-hook . cursor-intangible-mode))
(use-package marginalia
:commands marginalia-mode
:hook (after-init . marginalia-mode))
(use-package crux
:bind
("C-c M-e" . crux-find-user-init-file)
("C-c C-w" . crux-transpose-windows)
("C-a" . crux-move-beginning-of-line))
(use-package magit
:bind (("C-M-g" . magit-status)))
(use-package nerd-icons-corfu
:commands nerd-icons-corfu-formatter
:defines corfu-margin-formatters)
(use-package corfu
:commands global-corfu-mode
:custom
(corfu-cycle t)
(corfu-auto t)
(corfu-auto-delay 1)
(corfu-auto-prefix 3)
(corfu-separator ?_)
:hook
(after-init . global-corfu-mode)
:config
(add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))
(use-package cape)
(use-package orderless
:custom
(completion-styles '(orderless partial-completion basic))
(completion-category-defaults nil)
(completion-category-overrides nil))
(use-package yasnippet
:commands yas-global-mode
:diminish yas-minor-mode
:hook
(after-init . yas-global-mode))
(use-package yasnippet-snippets :after yasnippet)
(use-package projectile
:commands projectile-mode
:diminish projectile-mode
:custom
(projectile-globally-ignored-directories (append '("node_modules")))
:bind-keymap ("C-c p" . projectile-command-map)
:config
(projectile-mode +1))
(use-package exec-path-from-shell
:commands exec-path-from-shell-initialize
:custom
(exec-path-from-shell-arguments nil)
:hook
(after-init . exec-path-from-shell-initialize))
(use-package flycheck
:commands global-flycheck-mode
:diminish
:hook
(after-init . global-flycheck-mode))
(use-package lsp-mode
:commands (lsp lsp-deferred lsp-format-buffer
lsp-organize-imports
orderless-dispatch-flex-first
cape-capf-buster lsp-completion-at-point)
:defines lsp-file-watch-ignored-directories
:diminish lsp-lens-mode
:bind-keymap
("C-c l" . lsp-command-map)
:custom
(lsp-lens-enable nil)
(lsp-idle-delay 0.500)
(lsp-modeline-code-actions-enable t)
(lsp-modeline-diagnostics-enable t)
(lsp-csharp-omnisharp-roslyn-binary-path "OmniSharp")
(lsp-completion-provider :none) ;; we use Corfu!
(lsp-eldoc-render-all t)
:init
(defun orderless-dispatch-flex-first (_pattern index _total)
(and (eq index 0) 'orderless-flex))
;; Configure the first word as flex filtered.
(add-hook 'orderless-style-dispatchers #'orderless-dispatch-flex-first nil 'local)
(defun lsp-mode-setup-completion ()
(setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults))
'(orderless)))
;; Optionally configure the cape-capf-buster.
(setq-local completion-at-point-functions
(list (cape-capf-buster #'lsp-completion-at-point)))
:config
(add-to-list 'lsp-file-watch-ignored-directories "[/\\\\]\\Temp\\'")
(add-to-list 'lsp-file-watch-ignored-directories "[/\\\\]\\Logs\\'")
(defun lsp-cleanup ()
(lsp-format-buffer)
(lsp-organize-imports)
(whitespace-cleanup))
:hook
(lsp-completion-mode . lsp-mode-setup-completion)
(lsp-mode . lsp-enable-which-key-integration)
(before-save . lsp-cleanup)
(prog-mode . lsp-deferred))
(use-package lsp-ui :commands lsp-ui-mode
:custom
(lsp-ui-doc-enable t)
(lsp-ui-sideline-diagnostic-max-lines 4)
(lsp-ui-doc-show-with-mouse nil)
(lsp-ui-doc-position 'bottom)
(lsp-ui-doc-show-with-cursor t)
(lsp-eldoc-enable-hover nil)
)
(use-package nixpkgs-fmt
:custom
(nixpkgs-fmt-command "nixfmt"))
(use-package rust-mode
:custom
(rust-mode-treesitter-derive t)
(lsp-rust-analyzer-cargo-watch-command "clippy")
(lsp-rust-analyzer-exclude-dirs ["Temp/**"]))
(use-package typescript-ts-mode
:custom
(lsp-javascript-preferences-import-module-specifier :relative)
(typescript-indent-level 2)
(typescript-ts-mode-indent-offset 2))
(use-package eat)
(use-package hcl-mode)
(use-package jinja2-mode)
(use-package f :demand t)
(use-package envrc
:commands envrc-global-mode
:hook
(after-init . envrc-global-mode))
(use-package nix-mode
:hook (nix-mode . lsp-deferred))
(use-package shell-pop
:custom
(shell-pop-universal-key "M-o"))
(use-package copilot
:defines copilot-completion-map
:bind
(:map copilot-completion-map
("<tab>" . copilot-accept-completion)
("M-n" . copilot-next-completion)
("M-p" . copilot-previous-completion)
("C-g" . copilot-clear-overlay)))
(use-package gptel
:commands gptel-make-anthropic f-read-text
:config
(gptel-make-anthropic "Claude"
:stream t :key (f-read-text "/run/secrets/claude_key")))
(provide 'init)
;;; init.el ends here
Only a few more things left. Specifically the machine level extra settings.
We already setup bigbox’s configuration.nix and hardware-configuration.nix files. Finally, the ssh keys that identifies this machine is exported using sops.
sops.secrets."ssh/bigbox/private" = { # ssh private key
owner = "${user.username}";
mode = "600";
path = "/home/${user.username}/.ssh/id_ed25519";
};
sops.secrets."ssh/bigbox/public" = { # ssh public key
owner = "${user.username}";
mode = "644";
path = "/home/${user.username}/.ssh/id_ed25519.pub";
};
sops.secrets."ssh/wavefunk/private" = { # ssh private key
owner = "${user.username}";
mode = "600";
path = "/home/${user.username}/.ssh/wavefunk";
};
sops.secrets."ssh/wavefunk/public" = { # ssh public key
owner = "${user.username}";
mode = "644";
path = "/home/${user.username}/.ssh/wavefunk.pub";
};
sops.secrets."ssh/wavefunk_dev/private" = { # ssh private key
owner = "${user.username}";
mode = "600";
path = "/home/${user.username}/.ssh/wavefunk_dev";
};
sops.secrets."ssh/wavefunk_dev/public" = { # ssh public key
owner = "${user.username}";
mode = "644";
path = "/home/${user.username}/.ssh/wavefunk_dev.pub";
};
# the key is in /home so we need to make sure it boots
fileSystems."/home".neededForBoot = true;
We include my monitor setup at home along with default applications at launch. This is added to the home configuration.
{
imports = [
../../home
];
wayland.windowManager.hyprland.settings = {
monitor = [
"HDMI-A-1, 2560x1440@60, 0x0, 1, vrr, 2" # main monitor
"DP-2, 3840x2160, 2560x0, 1.5, transform, 1" # side monitor on the right, vertical
];
workspace = [
"1,monitor:HDMI-A-1"
"2,monitor:HDMI-A-1"
"5,monitor:DP-2"
];
exec-once = [
"[workspace 1 silent] firefox"
"[workspace 2 silent] emacs"
"[workspace 5 silent] slack"
];
};
}
The configuration for the laptop does not change much. Most changes are because the hardware is different.
The only system level change is that the monitor is hidpi. So GDK_SCALE=2 helps with proper scaling of gdk applications.
{
imports =
[
./hardware-configuration.nix
../../hardware/nvidia.nix
../../configuration
];
environment.sessionVariables = {
GDK_SCALE = 2;
};
}
This is the most different. Mostly taken from hardware-configuration.nix setup at first install. As you might notice, there are a lot more kernel modules. This device has thunderbolt for example.
{
hostname,
pkgs,
lib,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
../../hardware/hardware.nix
];
boot.initrd.availableKernelModules = [
"vmd" # intel volume management
"xhci_pci" # usb wake up (usb host controller)
"thunderbolt" # :/
"nvme" # support for the nvme disk in here
"usb_storage" # :/
"sd_mod" # hard drive controller
"rtsx_pci_sdmmc" # realtek card reader
];
boot.kernelParams = lib.mkDefault [
"acpi_rev_override" # prevents nvidia disable
"nvidia.NVreg_PreserveVideoMemoryAllocations=1" # allow for hibernation
];
boot.kernelModules = [ "kvm-intel" ];
boot.loader.grub.efiSupport = true;
boot.loader.grub.useOSProber = true; # detect windows since thats on a partition here
boot.loader.grub.devices = [ "/dev/nvme0n1" ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/b6bd668c-9a55-4b6d-b700-ecae6c08518d";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/5A9D-1E16";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
swapDevices = [
{ device = "/dev/disk/by-uuid/ab21b211-151b-46f0-ab8f-f0788228cb70"; }
];
# enable fingerprinting services
services.fprintd = {
enable = true;
package = pkgs.fprintd-tod;
tod = {
enable = true;
driver = pkgs.libfprint-2-tod1-goodix;
};
};
networking.hostName = hostname;
}
Just like the bigbox machine’s home configuration, this is mostly about configuring the screen and workspaces.
{
imports = [
../../home
];
wayland.windowManager.hyprland.settings = {
monitor = [
",highres,auto,2,bitdepth,10"
];
xwayland = {
force_zero_scaling = true;
};
exec-once = [
"[workspace 1 silent] firefox"
"[workspace 2 silent] emacs"
"[workspace 5 silent] slack"
];
};
}
This script adds a DO NOT MODIFY
header to all the generated nix files.
(progn
(defun add-tangle-headers ()
(message "running in %s" (buffer-file-name))
(when (string= (file-name-extension (buffer-file-name)) "nix")
(goto-char (point-min))
(insert "# WARNING : This file was generated by README.org\n# DO NOT MODIFY THIS FILE!\n# Any changes made here will be overwritten.\n")
(save-buffer))
(save-buffer))
(add-hook 'org-babel-post-tangle-hook 'add-tangle-headers))