Files
nixos/Droidnix/README.org
T
2026-03-24 08:15:04 +00:00

24 KiB
Raw Blame History

Droidnix: A Dendritic NixOS + Home Manager Configuration NixOS Configuration Structure

Introduction   intro

What is Droidnix

Droidnix is a modular, declarative NixOS + Home Manager configuration system. It allows users to choose between Hyprland and Mangowc as their window manager, with shared and WM-specific configurations managed via Emacs Org and Nix Flakes. The project is designed for reproducibility, maintainability, and cross-machine compatibility.

Installed components:

Core
Hyprland
Mangowc

Goals, project Structure, import hierarchy

This project uses a modular NixOS configuration with Hyprland and MangoWC support, designed for literate programming and cross-device reusability. The Droidnix repository is organized into two main parts:

  1. .assets/: Static, non-generated files (e.g., configs, scripts, themes).
  2. Generated folders (system, hyprland, mangowc): NixOS and Home Manager configurations, generated from Org files.

Root Level

  • flake.nix is the entry point and imports:

    • generated/assets/
    • generated/<host>/modules/
    • generated/hosts/

Generated Structure

The generated/ directory contains all generated configurations, divided into three main groups: system, hyprland, and mangowc.

First Setup

  1. Clone this repository.
  2. Run the setup script: ./setup_droid.
  3. Edit .assets/system/conf/base.conf to choose your window manager (wm = "hyprland" or wm = "mangowc").
  4. Tangle this Org file to generate Nix configurations: C-c C-v t in Emacs or use this: emacs README.org --batch -f org-babel-tangle && emacs --batch --eval "(setq org-html-htmlize-output-type nil)" README.org -f org-html-export-to-html
  5. Build and switch: sudo nixos-rebuild switch --flake .#<hostname>.

The Assets Folder   assets

The .assets/ folder contains all static files, such as configs, scripts, and themes. These files are not generated and can be edited directly.

The Actual Code   code

This section contains the Org blocks for tangling Nix code into the generated folders.

flake.nix

The Nix flake definition for Droidnix.

{
  description = "Droidnix: A dendritic NixOS + Home Manager configuration";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";

    home-manager = {
      url = "github:nix-community/home-manager";
      inputs.nixpkgs.follows = "nixpkgs";
    };

    import-tree.url = "github:vic/import-tree";

    stylix = {
      url = "github:nix-community/stylix";
      inputs.nixpkgs.follows = "nixpkgs";
    };

    zen-browser = {
      url = "github:youwen5/zen-browser-flake";
      inputs.nixpkgs.follows = "nixpkgs";
    };

    hyprland.url = "github:hyprwm/Hyprland";
  };

  outputs = { self, nixpkgs, home-manager, import-tree, stylix, hyprland, zen-browser, ... }:
    let
      system = "x86_64-linux";
      flakeRoot = self;
    in {
      nixosConfigurations = {
        traveldroid = nixpkgs.lib.nixosSystem {
          inherit system;

          modules = [
            ./generated/hosts/traveldroid/host.nix
          ];

          specialArgs = {
            inherit flakeRoot;
            inherit home-manager import-tree stylix hyprland zen-browser;
          };
        };
      };
    };
}

generated/hosts/traveldroid/host.nix

{ lib, config, pkgs, flakeRoot, import-tree, home-manager, ... }:

let
  hostname = "traveldroid";

  modulesPath = "${flakeRoot}/generated/modules/${hostname}";
  usersPath   = "${flakeRoot}/generated/users";

  hostModules = import-tree modulesPath;
  globalUsers = import-tree usersPath;

  allModules = hostModules.imports ++ globalUsers.imports;

in
{
  #################################
  # Core system config
  #################################

  networking.hostName = hostname;
  system.stateVersion = "26.05";

  #################################
  # Imports
  #################################

  imports =
    [
      ./boot.nix
      ./hardware-configuration.nix

      # REQUIRED for Home Manager
      home-manager.nixosModules.home-manager
    ]
    ++ allModules;

  #################################
  # Home Manager integration
  #################################

  home-manager.useGlobalPkgs = true;
  home-manager.useUserPackages = true;
}

generated/hosts/traveldroid/hardware-configuration.nix

  1. Boot into NixOS Live ISO or your installed system.
  2. Open a terminal.
  3. Run: <code>sudo nixos-generate-config root /mnt</code> (Omit root /mnt if already running NixOS.)
{
  hostname,
  pkgs,
  lib,
  modulesPath,
  user,
  config,
  ...
}:
{
  imports = [
    # (modulesPath + "/installer/scan/not-detected.nix")
    #../../hardware/hardware.nix
  ];

  boot.initrd.availableKernelModules = [
    "xhci_pci"
    "nvme"
    "usb_storage"
    "sd_mod"
    "rtsx_usb_sdmmc"
  ];
  boot.initrd.kernelModules = [ ];
  boot.kernelModules = [ "kvm-intel" ];
  boot.extraModulePackages = [ ];

  fileSystems."/" = {
    device = "/dev/disk/by-uuid/69433a14-fbaf-401b-af85-cd1bbf02b4e2";
    fsType = "ext4";
  };

  fileSystems."/boot" = {
    device = "/dev/disk/by-uuid/811D-0676";
    fsType = "vfat";
    options = [
      "fmask=0077"
      "dmask=0077"
    ];
  };

  swapDevices = [
    { device = "/dev/disk/by-uuid/b6c557c2-7682-460b-a5e7-8f6f2f429a3a"; }
  ];

  nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
  hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

generated/hosts/traveldroid/boot.nix

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

let
  hostname = config.networking.hostName or "traveldroid";

  # Build GRUB theme from your assets
  grubTheme = pkgs.stdenv.mkDerivation {
    name = "droidnix-grub-theme";

    src = "${flakeRoot}/assets/traveldroid/theming/boot";

    installPhase = ''
      mkdir -p $out

      # GRUB expects this exact name
      cp theming.txt $out/theme.txt

      # Assets
      cp background.png $out/
    '';
  };
in
{
  #################################
  # Bootloader (UEFI + GRUB)
  #################################
  boot.loader = {
    efi = {
      canTouchEfiVariables = true;
      efiSysMountPoint = "/boot";
    };

    grub = {
      enable = true;
      efiSupport = true;
      device = "nodev";
      useOSProber = true;

      # Keep multiple generations (fixes menu issue)
      configurationLimit = 10;

      # ✅ Fully declarative theme
      theme = grubTheme;
    };

    timeout = 5;
  };

  #################################
  # Kernel / initrd
  #################################
  boot.kernelPackages = pkgs.linuxPackages_latest;

  boot.kernelParams = [
    "quiet"
    "splash"
    "udev.log_level=3"
    "rd.systemd.show_status=false"
  ];

  boot.consoleLogLevel = 0;

  boot.initrd.systemd.enable = true;

  boot.initrd.availableKernelModules = [
    "xhci_pci"
    "nvme"
    "usb_storage"
    "sd_mod"
    "rtsx_usb_sdmmc"
  ];

  boot.kernelModules = [ "kvm-intel" ];

  #################################
  # Plymouth
  #################################
  boot.plymouth = {
    enable = true;
    theme = "rings";
    themePackages = [
      (pkgs.adi1090x-plymouth-themes.override {
        selected_themes = [ "rings" ];
      })
    ];
  };

  #################################
  # CPU / platform
  #################################
  hardware.cpu.intel.updateMicrocode =
    lib.mkDefault config.hardware.enableRedistributableFirmware;

  nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}

Following are the imported modules

generated/traveldroid/modules/apps

generated/modules/traveldroid/apps/packages.nix

This installs a list of apps

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

let
  #################################
  # Read package list from config file
  #################################
  packagesConfPath = "${flakeRoot}/assets/traveldroid/conf/packages.conf";
  raw = builtins.readFile packagesConfPath;

  rawLines = lib.splitString "\n" raw;

  # Guard against splitting into characters accidentally
  _guard = assert !(builtins.stringLength raw > 1 && builtins.length rawLines == builtins.stringLength raw); true;

  # Clean each line: remove CRs, comments, trim whitespace
  cleanLine = line:
    let
      noCR = lib.replaceStrings [ "\r" ] [ "" ] line;
      noInlineComment = lib.head (lib.splitString "#" noCR);
    in
      lib.strings.trim noInlineComment;

  # Filter out empty lines
  entries = builtins.filter (l: l != "") (map cleanLine rawLines);

  # Resolve attribute paths in pkgs
  resolvePkg = name:
    let
      parts = lib.splitString "." name;
      found = lib.attrByPath parts null pkgs;
    in
      if found == null then
        (throw ''
          packages.nix: package not found in pkgs
          Token          : ${builtins.toJSON name}
          packages.conf  : ${packagesConfPath}
          Hint           : check the attribute name on search.nixos.org/packages
        '')
      else
        found;

  # Final system-wide package list
  packages = builtins.seq _guard (map resolvePkg entries);

in {
  #################################
  # Allow unfree packages globally
  #################################
  nixpkgs.config = { allowUnfree = true; };

  #################################
  # System packages
  #################################
  environment.systemPackages = packages;
}

generated/modules/traveldroid/apps/kitty.nix

This file sets up Kitty terminal

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

let
  #################################
  # Determine default username
  #################################
  username   = config.defaultUser or "henrov";
  moduleName = "kitty";

  #################################
  # Paths to assets
  #################################
  assetPath    = ../../../assets/traveldroid/conf/${moduleName};
  programFiles = builtins.readDir assetPath;

  # Convert asset files into a nix attribute set
  files = lib.genAttrs (builtins.attrNames programFiles) (name: {
    source = "${assetPath}/${name}";
  });

in
{
  #################################
  # System-wide packages
  #################################
  environment.systemPackages = [
    pkgs.kitty
  ];

  #################################
  # Home Manager user configuration
  #################################
  _module.args.hmUsers = {
    ${username} = {

      # Enable Kitty through Home Manager
      programs.kitty.enable = true;

      # Extra user-specific config snippet
      programs.kitty.extraConfig = ''
        # Include the Catppuccin-Mocha theme
        include themes/Catppuccin-Mocha.conf
      '';

      # Map all asset files into ~/.config/kitty/
      home.file = lib.mkMerge (
        map (name: { ".config/${moduleName}/${name}" = { source = files.${name}.source; }; })
            (builtins.attrNames files)
      );
    };
  };
}

generated/modules/traveldroid/apps/starship.nix

This file sets up starship prompt

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

let
  # Default username fallback
  username = config.defaultUser or "henrov";

  # Path to the starship config in assets
  starshipConfSrc = "${flakeRoot}/assets/traveldroid/conf/starship.toml";
in
{
  #################################
  # Enable Starship system-wide
  #################################
  environment.systemPackages = [ pkgs.starship ];

  #################################
  # Home Manager user configuration
  #################################
  _module.args.hmUsers = {
    ${username} = {
      programs.starship = {
        enable = true;
      };

      # Copy the starship.toml from assets to ~/.config/starship.toml
      home.file = {
        ".config/starship.toml" = { source = starshipConfSrc; };
      };
    };
  };
}

generated/modules/traveldroid/desktop

generated/modules/traveldroid/desktop/fonts.nix

This file installs and configures fonts

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

{
    fonts.packages = with pkgs; [
      nerd-fonts.iosevka
      nerd-fonts.fira-code
    ];
}

generated/modules/traveldroid/desktop/hyprland.nix

Setting up Hyprland

{ lib, config, pkgs, flakeRoot, home-manager, inputs, ... }:

let
  username = config.defaultUser or "henrov";
  assetPath = "${flakeRoot}/assets/traveldroid/conf/hypr/";

  # Read all files in the asset directory
  assetFiles = builtins.attrNames (builtins.readDir assetPath);

  # Convert files to Home Manager xdg config entries
  hyprFiles = lib.genAttrs assetFiles (f: {
    # Destination path in home directory
    name = ".config/hypr/${f}";
    # Source file path
    value = { source = "${assetPath}/${f}"; };
  });

  # Determine Hyprland package
  hyprlandPkg =
    pkgs.hyprland or
    pkgs.hyprland-git or
    inputs.hyprland.packages.${pkgs.system}.default;
in
{
  environment.systemPackages = [ hyprlandPkg ];

  _module.args.hmUsers = {
    ${username} = {
      home.packages = [ hyprlandPkg ];

      # Merge all files in the asset folder into ~/.config/hypr/
      home.file = lib.mkMerge hyprFiles;

      # Optional: Hyprland settings
      settings.general."col.active_border" = "0xff97cbcd 0xff89b4fa";
    };
  };
}

generated/modules/traveldroid/desktop/stylix.nix

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

let
  # Determine the default username from host config
  username = config.defaultUser or "henrov";

  moduleName = "stylix";
  assetPath  = "${flakeRoot}/assets/traveldroid/conf/";

  # Read all files in the asset directory
  programFiles = builtins.readDir assetPath;

  files = lib.genAttrs (builtins.attrNames programFiles) (name: {
    source = "${assetPath}/${name}";
  });

  # Optional stylix.conf
  stylixConfFile = "${assetPath}/stylix.conf";
  stylixConf =
    if builtins.pathExists stylixConfFile
    then builtins.readFile stylixConfFile
    else "";

  # Cursor defaults
  cursorName = "phinger-cursors-light";
  cursorSize = 24;
in
{
  ############################
  # System-level packages
  ############################
  environment.systemPackages = [
    pkgs.feh
    pkgs.st
  ];

  ############################
  # Home Manager user config
  ############################
  _module.args.hmUsers = {
    ${username} = {

      # Map all config files into ~/.config/stylix/
      xdg.configFile =
        lib.mapAttrs' (name: value: {
          name = "${moduleName}/${name}";
          value = { inherit (value) source; };
        }) files;

      # Environment variables for the session
      home.sessionVariables = {
        STYLIX_CONF      = "$HOME/.config/stylix/stylix.conf";

        XCURSOR_THEME    = cursorName;
        XCURSOR_SIZE     = toString cursorSize;
        HYPRCURSOR_THEME = cursorName;
        HYPRCURSOR_SIZE  = toString cursorSize;
      };
    };
  };
}

generated/modules/traveldroid/desktop/waybar.nix

This file installs and configures waybar

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

let
  username = config.defaultUser or "henrov";

  moduleName = "waybar";
  assetPath  = "${flakeRoot}/assets/traveldroid/conf/${moduleName}";
in
{
  #################################
  # System packages
  #################################
  environment.systemPackages = [
    pkgs.waybar
  ];

  #################################
  # Home Manager integration
  #################################
  _module.args.hmUsers = {
    ${username} = {

      # Install Waybar for the user as well (optional but recommended)
      home.packages = [ pkgs.waybar ];

      # Place config files into ~/.config/waybar/
      xdg.configFile = {
        "${moduleName}/config".source = "${assetPath}/config";
        "${moduleName}/style.css".source = "${assetPath}/style.css";
      };
    };
  };
}

generated/modules/traveldroid/desktop/wayland.nix

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

{
  #################################
  # Core Wayland packages
  #################################
  environment.systemPackages = with pkgs; [
    wayland
    wl-clipboard      # optional but commonly used for copy/paste
  ];

  #################################
  # Optional: enable graphics stack
  #################################
  hardware.graphics.enable = true;

  #################################
  # Optional session variables for Wayland
  #################################
  environment.sessionVariables = {
    # Forces some apps to use Wayland
    NIXOS_OZONE_WL = "1";
  };
}

generated/modules/traveldroid/desktop/xdg.nix

This sets the XDG implementation

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

let
  #################################
  # Default username fallback
  #################################
  username = config.defaultUser or "henrov";

  #################################
  # Determine XDG portal package
  #################################
  xdgPortalHyprlandPkg =
    pkgs.xdg-desktop-portal-hyprland or
    inputs.xdgPortalHyprland.packages.${pkgs.system}.default;
in
{
  #################################
  # System-wide packages
  #################################
  environment.systemPackages = [
    xdgPortalHyprlandPkg
  ];

  #################################
  # Home Manager user config
  #################################
  _module.args.hmUsers = {
    ${username} = {
      home.packages = [
        xdgPortalHyprlandPkg
      ];

      # Enable XDG portal integration for Hyprland
      xdg.portal = {
        enable = true;
        extraPortals = [ xdgPortalHyprlandPkg ];
        config.hyprland = {
          "org.freedesktop.impl.portal.Screencast" = [ "hyprland" ];
        };
      };
    };
  };
}

generated/modules/traveldroid/system

generated/modules/traveldroid/system/networking.nix

This sets the networking.

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

{
  #################################
  # Networking core
  #################################
  networking = {
    # Let DHCP be default unless overridden elsewhere
    useDHCP = lib.mkDefault true;

    # Hostname comes from host.nix, do NOT redefine here

    #################################
    # NetworkManager (primary stack)
    #################################
    networkmanager = {
      enable = true;

      # Use iwd backend for WiFi
      wifi.backend = "iwd";
    };

    #################################
    # iwd (WiFi daemon)
    #################################
    wireless.iwd = {
      enable = true;
      # Allow user control via NM / CLI
      settings.General.EnableNetworkConfiguration = true;
    };

    #################################
    # Firewall
    #################################
    firewall = {
      enable = true;

      # KDE Connect support
      allowedTCPPortRanges = [
        { from = 1714; to = 1764; }
      ];

      allowedUDPPortRanges = [
        { from = 1714; to = 1764; }
      ];
    };
  };

  #################################
  # System packages
  #################################
  environment.systemPackages = [
    pkgs.networkmanager
  ];
}

generated/modules/traveldroid/system/nix.nix

{ lib, config, ... }:

{
    nix.settings = {
      experimental-features = [ "nix-command" "flakes" ];
      download-buffer-size = 536870912; # 512 MB
      cores = 2;
      max-jobs = 1;
    };
}

generated/modules/traveldroid/system/login-tuigreet.nix

This sets up tuigreeter which is not fancy but imo fits the aesthetic I am aiming for

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

let
  username = config.defaultUser or "henrov";
in
{
  #################################
  # Install tuigreet
  #################################
  environment.systemPackages = [ pkgs.tuigreet ];

  #################################
  # Greetd (simple + reliable)
  #################################
  services.greetd = {
    enable = true;

    settings.default_session = {
      user = "greeter";

      command = ''
        ${pkgs.tuigreet}/bin/tuigreet \
          --remember \
          --time \
          --time-format "%I:%M %p | %a • %h | %F" \
          --cmd ${pkgs.hyprland}/bin/Hyprland
      '';
    };
  };
}

generated/users

generated/users/copy_2_home.nix

This copies stuff to the user home-folder

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

let
  username  = config.users.users.defaultUser or "henrov";
  homeDir   = "/home/${username}";
  assetPath = "${flakeRoot}/assets/copy_2_home";
in
{
  environment.systemPackages = [ pkgs.rsync ];

  systemd.services.copyAssets = {
    description = "Copy assets to ${username}'s home directory";
    wantedBy = [ "multi-user.target" ];

    # oneshot service runs once at boot
    serviceConfig.Type = "oneshot";

    # Always use /bin/sh -c for multi-line commands
    serviceConfig.ExecStart = ''
      /bin/sh -c '
        echo "Copying assets from ${assetPath} to ${homeDir} ..."

        if [ ! -d "${assetPath}" ]; then
          echo "ERROR: ${assetPath} does not exist"
          exit 1
        fi

        mkdir -p "${homeDir}"
        chown ${username}:${username} "${homeDir}"

        ${pkgs.rsync}/bin/rsync -a --no-owner --no-group "${assetPath}/" "${homeDir}/"

        echo "Done copying assets."
      '
    '';
  };
}

generated/users/henrov.nix

This is the default user, just search and replace henrov another name if you want to change

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

let
  username = "henrov";
in
{
  #################################
  # NixOS system user
  #################################
  users.users.${username} = {
    isNormalUser = true;
    home = "/home/${username}";
    hashedPassword = "$6$S7iShgBxB.77CwmP$i0njK.2r3OL5UEvgZbmwZ0rnpZ4QyJcv8p9uCmJ4AiVPSMXkQkIwMLzyAOnJ0q8.tPLIp/7EquEIZeK8qbmgw/";
    extraGroups = [ "wheel" "networkmanager" ];
  };

  #################################
  # Home Manager user definition
  #################################
  _module.args.hmUsers = {
    ${username} = {
      # Minimal required
      home.username      = username;
      home.homeDirectory = "/home/${username}";
      home.stateVersion  = "26.05";

      # Add user-specific packages here
      home.packages = [
      ];

      # Add user dotfiles, session variables, etc. here if needed
      home.file = {
        # Example:
        # ".bashrc" = { source = /path/to/bashrc; };
      };
    };
  };
}