initial commit

This commit is contained in:
2025-05-12 23:25:39 -03:00
parent bf178e3caa
commit dc6f6894e6
95 changed files with 3922 additions and 0 deletions

1
.envrc Normal file
View File

@@ -0,0 +1 @@
use flake

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.pre-commit-config.yaml
.direnv
tmp
result
latest.iso

2
FUNDING.yaml Normal file
View File

@@ -0,0 +1,2 @@
github: emergentmind
ko_fi: unmovedcentre

9
LICENSE Normal file
View File

@@ -0,0 +1,9 @@
MIT License
Copyright (c) 2024 Téo Adams
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

BIN
docs/nixos-ascendancy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

5
docs/secretsmgmt.md Normal file
View File

@@ -0,0 +1,5 @@
# Nix-Config and Nix-Secrets Secrets Management
[README](../README.md) > Nix-Config Secrets Management
This content has been updated and moved to my website [NixOS Secrets Management](https://unmovedcentre.com/posts/secrets-management/). There is also an accompanying [video series](https://youtu.be/6EMNHDOY-wo).

332
flake.lock generated Normal file
View File

@@ -0,0 +1,332 @@
{
"nodes": {
"disko": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1746729224,
"narHash": "sha256-9R4sOLAK1w3Bq54H3XOJogdc7a6C2bLLmatOQ+5pf5w=",
"owner": "nix-community",
"repo": "disko",
"rev": "85555d27ded84604ad6657ecca255a03fd878607",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "disko",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"hardware": {
"locked": {
"lastModified": 1747083103,
"narHash": "sha256-dMx20S2molwqJxbmMB4pGjNfgp5H1IOHNa1Eby6xL+0=",
"owner": "nixos",
"repo": "nixos-hardware",
"rev": "d1d68fe8b00248caaa5b3bbe4984c12b47e0867d",
"type": "github"
},
"original": {
"owner": "nixos",
"repo": "nixos-hardware",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1747020534,
"narHash": "sha256-D/6rkiC6w2p+4SwRiVKrWIeYzun8FBg7NlMKMwQMxO0=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "b4bbdc6fde16fc2051fcde232f6e288cd22007ca",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "release-24.11",
"repo": "home-manager",
"type": "github"
}
},
"nix-darwin": {
"inputs": {
"nixpkgs": [
"nixpkgs-darwin"
]
},
"locked": {
"lastModified": 1747069642,
"narHash": "sha256-a4TdGi/Ju8P3r5OIecNfM3LH3kccMY0dIo+EwiyphmM=",
"owner": "lnl7",
"repo": "nix-darwin",
"rev": "d642c9856003ed37ce34dab618abf37e3ade1061",
"type": "github"
},
"original": {
"owner": "lnl7",
"repo": "nix-darwin",
"type": "github"
}
},
"nix4vscode": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"rust-overlay": "rust-overlay",
"systems": "systems"
},
"locked": {
"lastModified": 1747097775,
"narHash": "sha256-WYW1IpCnWSkTIO0oVv1r6rVP6SCZixmIZ3ftlm8Ern8=",
"owner": "nix-community",
"repo": "nix4vscode",
"rev": "c566a8cdd1e24591ed6644b918c4e474c0b49f8a",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nix4vscode",
"type": "github"
}
},
"nixos-hardware": {
"locked": {
"lastModified": 1747083103,
"narHash": "sha256-dMx20S2molwqJxbmMB4pGjNfgp5H1IOHNa1Eby6xL+0=",
"owner": "NixOS",
"repo": "nixos-hardware",
"rev": "d1d68fe8b00248caaa5b3bbe4984c12b47e0867d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "master",
"repo": "nixos-hardware",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1736320768,
"narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4bc9c909d9ac828a039f288cf872d16d38185db8",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-darwin": {
"locked": {
"lastModified": 1747037616,
"narHash": "sha256-LRoT0AiI9kTK1pP8j0Va4geuE1YTtRwQuW/vLC3aaBY=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "40909cce0f2c3346ded03302b86228d8b292b9ce",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-24.11-darwin",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1746957726,
"narHash": "sha256-k9ut1LSfHCr0AW82ttEQzXVCqmyWVA5+SHJkS5ID/Jo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a39ed32a651fdee6842ec930761e31d1f242cb94",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1746904237,
"narHash": "sha256-3e+AVBczosP5dCLQmMoMEogM57gmZ2qrVSrmq9aResQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d89fc19e405cb2d55ce7cc114356846a0ee5e956",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1746957726,
"narHash": "sha256-k9ut1LSfHCr0AW82ttEQzXVCqmyWVA5+SHJkS5ID/Jo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a39ed32a651fdee6842ec930761e31d1f242cb94",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.11",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1746537231,
"narHash": "sha256-Wb2xeSyOsCoTCTj7LOoD6cdKLEROyFAArnYoS+noCWo=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "fa466640195d38ec97cf0493d6d6882bc4d14969",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"disko": "disko",
"hardware": "hardware",
"home-manager": "home-manager",
"nix-darwin": "nix-darwin",
"nix4vscode": "nix4vscode",
"nixos-hardware": "nixos-hardware",
"nixpkgs": "nixpkgs_2",
"nixpkgs-darwin": "nixpkgs-darwin",
"nixpkgs-stable": "nixpkgs-stable",
"nixpkgs-unstable": "nixpkgs-unstable",
"pre-commit-hooks": "pre-commit-hooks",
"zen-browser": "zen-browser"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1742178793,
"narHash": "sha256-S2onMdoDS4tIYd3/Jc5oFEZBr2dJOgPrh9KzSO/bfDw=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "954582a766a50ebef5695a9616c93b5386418c08",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"systems": {
"flake": false,
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"zen-browser": {
"inputs": {
"home-manager": [
"home-manager"
],
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1747071553,
"narHash": "sha256-EMIzJ+F2DTuOSPD608HaAra9cah87Emz8GjYNGtxpLo=",
"owner": "0xc000022070",
"repo": "zen-browser-flake",
"rev": "50065c8bee3f5c20d29bce19037447b2c2006c48",
"type": "github"
},
"original": {
"owner": "0xc000022070",
"repo": "zen-browser-flake",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

182
flake.nix Normal file
View File

@@ -0,0 +1,182 @@
{
description = "EmergentMind's Nix-Config Starter";
outputs = {
self,
nixpkgs,
...
} @ inputs: let
inherit (self) outputs;
inherit (nixpkgs) lib;
#
# ========= Architectures =========
#
# NOTE(starter): Comment or uncomment architectures below as required by your hosts.
forAllSystems = nixpkgs.lib.genAttrs [
"x86_64-linux"
"aarch64-darwin"
];
#
# ========= Host Config Functions =========
#
# Handle a given host config based on whether its underlying system is nixos or darwin
mkHost = host: isDarwin: {
${host} = let
func =
if isDarwin
then inputs.nix-darwin.lib.darwinSystem
else lib.nixosSystem;
systemFunc = func;
in
systemFunc {
specialArgs = {
inherit
inputs
outputs
isDarwin
;
# ========== Extend lib with lib.custom ==========
# This approach allows lib.custom to propagate into hm
# see: https://github.com/nix-community/home-manager/pull/3454
lib = nixpkgs.lib.extend (self: super: {custom = import ./lib {inherit (nixpkgs) lib;};});
};
modules = [
./hosts/${
if isDarwin
then "darwin"
else "nixos"
}/${host}
];
};
};
# Invoke mkHost for each host config that is declared for either nixos or darwin
mkHostConfigs = hosts: isDarwin: lib.foldl (acc: set: acc // set) {} (lib.map (host: mkHost host isDarwin) hosts);
# Return the hosts declared in the given directory
readHosts = folder: lib.attrNames (builtins.readDir ./hosts/${folder});
in {
#
# ========= Overlays =========
#
# Custom modifications/overrides to upstream packages.
overlays = import ./overlays {inherit inputs;};
#
# ========= Host Configurations =========
#
# Building configurations is available through `just rebuild` or `nixos-rebuild --flake .#hostname`
# NOTE(starter): Only uncomment darwinConfigurations if you actually have a host module configured in `./hosts/darwin`
nixosConfigurations = mkHostConfigs (readHosts "nixos") false;
#darwinConfigurations = mkHostConfigs (readHosts "darwin") true;
#
# ========= Packages =========
#
# Add custom packages to be shared or upstreamed.
packages = forAllSystems (
system: let
pkgs = import nixpkgs {
inherit system;
overlays = [self.overlays.default];
};
in
lib.packagesFromDirectoryRecursive {
callPackage = lib.callPackageWith pkgs;
directory = ./pkgs/common;
}
);
#
# ========= Formatting =========
#
# Nix formatter available through 'nix fmt' https://nix-community.github.io/nixpkgs-fmt
formatter = forAllSystems (system: nixpkgs.legacyPackages.${system}.nixfmt-rfc-style);
#
# ========= DevShell =========
#
# Custom shell for bootstrapping on new hosts, modifying nix-config, and secrets management
devShells = forAllSystems (
system:
import ./shell.nix {
pkgs = nixpkgs.legacyPackages.${system};
}
);
};
inputs = {
#
# ========= Official NixOS, Nix-Darwin, and HM Package Sources =========
#
# NOTE(starter): As with typical flake-based configs, you'll need to update the nixOS, hm,
# and darwin version numbers below when new releases are available.
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
# The next two inputs are for pinning nixpkgs to stable vs unstable regardless of what the above is set to.
# This is particularly useful when an upcoming stable release is in beta because you can effectively
# keep 'nixpkgs-stable' set to stable for critical packages while setting 'nixpkgs' to the beta branch to
# get a jump start on deprecation changes.
# See also 'stable-packages' and 'unstable-packages' overlays at 'overlays/default.nix"
nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-24.11";
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
hardware.url = "github:nixos/nixos-hardware";
home-manager = {
url = "github:nix-community/home-manager/release-24.11";
inputs.nixpkgs.follows = "nixpkgs";
};
nixpkgs-darwin.url = "github:nixos/nixpkgs/nixpkgs-24.11-darwin";
nix-darwin = {
url = "github:lnl7/nix-darwin";
inputs.nixpkgs.follows = "nixpkgs-darwin";
};
#
# ========= Utilities =========
#
disko = {
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
};
nixos-hardware = {
url = "github:NixOS/nixos-hardware/master";
};
nix4vscode = {
url = "github:nix-community/nix4vscode";
inputs.nixpkgs.follows = "nixpkgs";
};
pre-commit-hooks = {
url = "github:cachix/git-hooks.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
#
# ========= Extra Applications =========
#
zen-browser = {
url = "github:0xc000022070/zen-browser-flake";
inputs = {
home-manager.follows = "home-manager";
nixpkgs.follows = "nixpkgs";
};
};
#
# ========= Personal Repositories =========
#
# Private secrets repo. See ./docs/secretsmgmt.md
# Authenticates via ssh and use shallow clone
# FIXME(starter): The url below points to the 'simple' branch of the public, nix-secrets-reference repository which is inherently INSECURE!
# Replace the url with your personal, private nix-secrets repo.
/*
nix-secrets = {
url = "git+ssh://git@github.com/emergentmind/nix-secrets-reference.git?ref=simple&shallow=1";
inputs = { };
};
*/
};
}

View File

@@ -0,0 +1,15 @@
{ pkgs, ... }:
{
imports = [
#################### Required Configs ####################
common/core # required
#################### Host-specific Optional Configs ####################
];
home.packages = builtins.attrValues {
inherit (pkgs)
vlc
;
};
}

View File

@@ -0,0 +1,9 @@
{
programs.brave = {
enable = true;
commandLineArgs = [
"--no-default-browser-check"
"--restore-last-session"
];
};
}

View File

@@ -0,0 +1,47 @@
{
config,
lib,
pkgs,
...
}:
{
imports = lib.flatten [
(lib.custom.scanPaths ./.)
(map lib.custom.relativeToRoot [
"modules/home"
])
];
home = {
username = lib.mkDefault "exampleSecondUser";
homeDirectory = lib.mkDefault "/home/${config.home.username}";
stateVersion = lib.mkDefault "24.11";
sessionPath = [ "$HOME/.local/bin" ];
};
home.packages = builtins.attrValues {
inherit (pkgs)
# Packages that don't have custom configs go here
nix-tree
;
};
nix = {
package = lib.mkDefault pkgs.nix;
settings = {
experimental-features = [
"nix-command"
"flakes"
];
warn-dirty = false;
};
};
programs = {
home-manager.enable = true;
};
# Nicely reload system units when changing configs
systemd.user.startServices = "sd-switch";
}

View File

@@ -0,0 +1,11 @@
{ pkgs, ... }:
{
gtk = {
enable = true;
iconTheme = {
name = "elementary-Xfce-dark";
package = pkgs.elementary-xfce-icon-theme;
};
};
}

View File

@@ -0,0 +1,19 @@
{...}: {
imports = [
#
# ========== Required Configs ==========
#
common/core
#
# ========== Host-specific Optional Configs ==========
#
# FIXME(starter): add or remove any optional config directories or files ehre
common/optional/browsers
common/optional/desktops
common/optional/comms
common/optional/media
common/optional/coding
common/optional/games
];
}

View File

@@ -0,0 +1,12 @@
# FIXME(starter): customize your bash preferences here
{
programs.bash = {
enable = true;
enableCompletion = true;
shellAliases = {
};
initExtra = ''
'';
};
}

View File

@@ -0,0 +1,10 @@
# Core home functionality that will only work on Darwin
{ config, ... }:
{
home.sessionPath = [ "/opt/homebrew/bin" ];
home = {
username = config.hostSpec.username;
homeDirectory = config.hostSpec.home;
};
}

View File

@@ -0,0 +1,79 @@
#FIXME: Move attrs that will only work on linux to nixos.nix
{
config,
lib,
pkgs,
hostSpec,
...
}: let
platform =
if hostSpec.isDarwin
then "darwin"
else "nixos";
in {
imports = lib.flatten [
(map lib.custom.relativeToRoot [
"modules/common/host-spec.nix"
"modules/home"
])
./${platform}.nix
# FIXME(starter): add/edit as desired
./fish.nix
./bash.nix
./direnv.nix
./fonts.nix
./kitty.nix
./git.nix
./ssh.nix
];
inherit hostSpec;
services.ssh-agent.enable = true;
home = {
username = lib.mkDefault config.hostSpec.username;
homeDirectory = lib.mkDefault config.hostSpec.home;
stateVersion = lib.mkDefault "24.11";
sessionPath = [
"$HOME/.local/bin"
];
sessionVariables = {
FLAKE = "$HOME/src/nix/nix-config";
SHELL = "bash";
};
};
home.packages = builtins.attrValues {
inherit
(pkgs)
# FIXME(starter): add/edit as desired
# Packages that don't have custom configs go here
curl
pciutils
pfetch # system info
pre-commit # git hooks
p7zip # compression & encryption
usbutils
unzip # zip extraction
unrar # rar extraction
;
};
nix = {
package = lib.mkDefault pkgs.nix;
settings = {
experimental-features = [
"nix-command"
"flakes"
];
warn-dirty = false;
};
};
programs.home-manager.enable = true;
# Nicely reload system units when changing configs
systemd.user.startServices = "sd-switch";
}

View File

@@ -0,0 +1,8 @@
{
programs.direnv = {
enable = true;
enableBashIntegration = true;
enableZshIntegration = true;
nix-direnv.enable = true; # better than native direnv nix functionality - https://github.com/nix-community/nix-direnv
};
}

View File

@@ -0,0 +1,25 @@
{pkgs, ...}: {
home.packages = builtins.attrValues {
inherit
(pkgs)
grc
;
};
programs.fish = {
enable = true;
interactiveShellInit = ''
set fish_greeting # Disable greeting
'';
plugins = [
{
name = "grc";
src = pkgs.fishPlugins.grc.src;
}
{
name = "autopair";
src = pkgs.fishPlugins.autopair.src;
}
];
};
}

View File

@@ -0,0 +1,7 @@
{ pkgs, ... }:
{
fonts.fontconfig.enable = true;
home.packages = [
pkgs.noto-fonts
];
}

View File

@@ -0,0 +1,25 @@
# git is core no matter what but additional settings may could be added made in optional/foo eg: development.nix
{
pkgs,
...
}:
{
programs.git = {
enable = true;
package = pkgs.gitAndTools.gitFull;
ignores = [
".csvignore"
# nix
"*.drv"
"result"
# python
"*.py?"
"__pycache__/"
".venv/"
# direnv
".direnv"
];
};
}

View File

@@ -0,0 +1,5 @@
{
programs.kitty = {
enable = true;
};
}

View File

@@ -0,0 +1,7 @@
# Core home functionality that will only work on Linux
{
...
}:
{
}

View File

@@ -0,0 +1,9 @@
{ pkgs, ... }:
{
home.packages = [ pkgs.screen ];
home.file.".screenrc".text = ''
startup_message off
defbce on
setenv TERM xterm-256color
'';
}

View File

@@ -0,0 +1,24 @@
# FIXME(starter): adjust to you security requirements
{
config,
...
}:
{
programs.ssh =
{
enable = true;
controlMaster = "auto";
controlPath = "${config.home.homeDirectory}/.ssh/sockets/S.%r@%h:%p";
controlPersist = "20m";
# Avoids infinite hang if control socket connection interrupted. ex: vpn goes down/up
serverAliveCountMax = 3;
serverAliveInterval = 5; # 3 * 5s
hashKnownHosts = true;
addKeysToAgent = "yes";
};
home.file = {
".ssh/config.d/.keep".text = "# Managed by Home Manager";
".ssh/sockets/.keep".text = "# Managed by Home Manager";
};
}

View File

@@ -0,0 +1,6 @@
{pkgs, ...}: {
programs.chromium = {
enable = true;
package = pkgs.ungoogled-chromium;
};
}

View File

@@ -0,0 +1,8 @@
# FIXME(starter): add/edit any browser modules here
{
imports = [
./chromium.nix
./firefox.nix
./zen.nix
];
}

View File

@@ -0,0 +1,5 @@
{...}: {
programs.librewolf = {
enable = true;
};
}

View File

@@ -0,0 +1,24 @@
{inputs, ...}: {
# home.nix
imports = [
inputs.zen-browser.homeModules.beta
# or inputs.zen-browser.homeModules.twilight
# or inputs.zen-browser.homeModules.twilight-official
];
programs.zen-browser = {
enable = true;
policies = {
AutofillAddressEnabled = true;
AutofillCreditCardEnabled = false;
DisableAppUpdate = true;
DisableFeedbackCommands = true;
DisableFirefoxStudies = true;
DisablePocket = true; # save webs for later reading
DisableTelemetry = true;
DontCheckDefaultBrowser = true;
NoDefaultBookmarks = true;
OfferToSaveLogins = false;
};
};
}

View File

@@ -0,0 +1,13 @@
{pkgs, ...}: {
imports = [
./vscode
./zed.nix
];
home.packages = builtins.attrValues {
inherit
(pkgs)
hoppscotch
;
};
}

View File

@@ -0,0 +1,69 @@
{pkgs, ...}: {
imports = [
./javascript.nix
./latex.nix
./markdown.nix
./nix.nix
./rust.nix
];
programs.vscode = {
enable = true;
package = pkgs.vscodium;
enableUpdateCheck = false; # Disable VSCode self-update and let Home Manager to manage VSCode versions instead.
enableExtensionUpdateCheck = false; # Disable extensions auto-update and let nix4vscode manage updates and extensions
# Extensions
extensions = pkgs.nix4vscode.forVscode [
# General extensions
## Code Completion
"continue.continue"
#rooveterinaryinc.roo-cline
## Development Environment
"ms-toolsai.jupyter"
"ms-vscode-remote.remote-containers"
## Error Checking
"usernamehw.errorlens"
## Export and Visualisation
"ibm.output-colorizer"
"nobuhito.printcode"
"pnp.polacode"
## Git
"lamartire.git-indicators"
"mhutchie.git-graph"
## Miscelaneous
"britesnow.vscode-toggle-quotes"
"mrmlnc.vscode-duplicate"
"qcz.text-power-tools"
# Language extensions
## CSV
"mechatroner.rainbow-csv"
## Golang
"golang.go"
## Python
"ms-python.python"
## SVG
"jock.svg"
];
# Settings
userSettings = {
"editor.linkedEditing" = true;
"editor.inlineSuggest.enabled" = true;
"continue.enableTabAutocomplete" = true;
"window.menuBarVisibility" = "toggle";
#"github.copilot.editor.enableAutoCompletions" = true;
"redhat.telemetry.enabled" = false;
};
};
}

View File

@@ -0,0 +1,56 @@
{pkgs, ...}: {
programs.vscode = {
extensions = pkgs.nix4vscode.forVscode [
# General
"christian-kohler.npm-intellisense"
"dbaeumer.vscode-eslint"
"denoland.vscode-deno"
"esbenp.prettier-vscode"
"liamhammett.inline-parameters"
"yatki.vscode-surround"
# Astro
"astro-build.astro-vscode"
# CSS
"bradlc.vscode-tailwindcss"
"pranaygp.vscode-css-peek"
"stylelint.vscode-stylelint"
"zignd.html-css-class-completion"
# ServiceNow
"arnoudkooicom.sn-scriptsync"
# Svelte
"svelte.svelte-vscode"
# Tauri
"tauri-apps.tauri-vscode"
# Testing
"ms-playwright.playwright"
];
userSettings = {
"[javascript]"."editor.defaultFormatter" = "esbenp.prettier-vscode";
"[typescript]"."editor.defaultFormatter" = "esbenp.prettier-vscode";
"[typescriptreact]"."editor.defaultFormatter" = "esbenp.prettier-vscode";
"[json]"."editor.defaultFormatter" = "esbenp.prettier-vscode";
"[jsonc]"."editor.defaultFormatter" = "esbenp.prettier-vscode";
"[html]"."editor.defaultFormatter" = "esbenp.prettier-vscode";
"[scss]"."editor.defaultFormatter" = "esbenp.prettier-vscode";
"[css]"."editor.defaultFormatter" = "esbenp.prettier-vscode";
"[astro]"."editor.defaultFormatter" = "astro-build.astro-vscode";
"svelte.enable-ts-plugin" = true;
"playwright.reuseBrowser" = true;
};
};
home.packages = with pkgs; [
deno
pnpm
bun
nodejs
];
}

View File

@@ -0,0 +1,28 @@
{pkgs, ...}: {
programs.vscode = {
extensions = pkgs.nix4vscode.forVscode [
# General
"james-yu.latex-workshop"
];
userSettings = {
"latex-workshop.latex.recipe.default" = "tectonic";
"latex-workshop.latex.autoBuild.run" = "onSave";
"latex-workshop.latex.outDir" = "%WORKSPACE_FOLDER%/build/index";
"latex-workshop.view.pdf.viewer" = "tab";
"latex-workshop.latex.recipes" = [
{
"name" = "tectonic";
"tools" = ["tectonic"];
}
];
"latex-workshop.latex.tools" = [
{
"name" = "tectonic";
"command" = "tectonic";
"args" = ["-X" "build" "--keep-intermediates" "--keep-logs"];
"env" = {};
}
];
};
};
}

View File

@@ -0,0 +1,15 @@
{pkgs, ...}: {
programs.vscode = {
extensions = pkgs.nix4vscode.forVscode [
# General
"bpruitt-goddard.mermaid-markdown-syntax-highlighting"
"davidanson.vscode-markdownlint"
"yzhang.markdown-all-in-one"
];
userSettings = {
"[markdown]" = {
"editor.defaultFormatter" = "esbenp.prettier-vscode";
};
};
};
}

View File

@@ -0,0 +1,18 @@
{pkgs, ...}: {
home.packages = with pkgs; [
nil
alejandra
deadnix
];
programs.vscode = {
extensions = pkgs.nix4vscode.forVscode [
"jnoortheen.nix-ide"
"kamadorueda.alejandra"
];
userSettings = {
"nix.enableLanguageServer" = true;
"nix.serverPath" = "nil";
};
};
}

View File

@@ -0,0 +1,19 @@
{pkgs, ...}: {
programs.vscode = {
extensions =
pkgs.nix4vscode.forVscode
[
# General
"serayuzgur.crates"
"tamasfe.even-better-toml"
"rust-lang.rust-analyzer"
];
userSettings = {
};
};
home.packages = with pkgs; [
cargo
rustc
];
}

View File

@@ -0,0 +1,5 @@
{pkgs, ...}: {
programs.zed-editor = {
enable = true;
};
}

View File

@@ -0,0 +1,13 @@
# FIXME(starter): add/edit any optional, communications related pkgs here
{pkgs, ...}: {
#imports = [ ./foo.nix ];
home.packages = builtins.attrValues {
inherit
(pkgs)
teams-for-linux
signal-desktop
discord
;
};
}

View File

@@ -0,0 +1,14 @@
{ pkgs, ... }:
{
imports = [
# Packages with custom configs go here
########## Utilities ##########
#./fonts.nix
./gtk.nix
];
home.packages = [
pkgs.pavucontrol # gui for pulseaudio server and volume controls
pkgs.galculator # gtk based calculator
];
}

View File

@@ -0,0 +1,14 @@
{
pkgs,
...
}:
{
gtk = {
enable = true;
iconTheme = {
name = "elementary-Xfce-dark";
package = pkgs.elementary-xfce-icon-theme;
};
};
}

View File

@@ -0,0 +1,7 @@
{ pkgs, ... }:
{
home.packages = [ pkgs.playerctl ];
services.playerctld = {
enable = true;
};
}

View File

@@ -0,0 +1,7 @@
# FIXME(starter): add/edit any browser modules here
{
imports = [
./steam.nix
./minecraft.nix
];
}

View File

@@ -0,0 +1,3 @@
{pkgs, ...}: {
home.packages = [pkgs.prismlauncher];
}

View File

@@ -0,0 +1,3 @@
{pkgs, ...}: {
home.packages = [pkgs.steam];
}

View File

@@ -0,0 +1,11 @@
# FIXME(starter): add/edit any optional, media related pkgs here
{ pkgs, ... }:
{
#imports = [ ./foo.nix ];
home.packages = builtins.attrValues {
inherit (pkgs)
vlc
;
};
}

View File

@@ -0,0 +1,20 @@
{pkgs, ...}: {
imports = [
./obsidian.nix
];
# FIXME(starter): add/edit any optional, communications related pkgs here
#imports = [ ./foo.nix ];
home.packages = builtins.attrValues {
inherit
(pkgs)
blender
inkscape
libreoffice-qt
hunspell
hunspellDicts
;
};
}

View File

@@ -0,0 +1,8 @@
{pkgs, ...}: {
home.packages = builtins.attrValues {
inherit
(pkgs)
obsidian
;
};
}

6
home/panotaka/iso.nix Normal file
View File

@@ -0,0 +1,6 @@
{ ... }:
{
imports = [
common/core
];
}

View File

@@ -0,0 +1,16 @@
{ pkgs, ... }:
{
# TODO add ttf-font-awesome or font-awesome for waybar
fontProfiles = {
enable = true;
monospace = {
family = "FiraCode Nerd Font";
package = pkgs.nerdfonts.override { fonts = [ "FiraCode" ]; };
};
regular = {
family = "Fira Sans";
package = pkgs.fira;
};
};
}

View File

@@ -0,0 +1,3 @@
# Core functionality for every nix-darwin host
# NOTE(starter): Declare any darwin-specific, core configurations here.
{ }

View File

@@ -0,0 +1,103 @@
# FIXME(starter): modify this file and the other .nix files in `nix-config/hosts/common/core/` to declare
# settings that will occur across all hosts
# IMPORTANT: This is used by NixOS and nix-darwin so options must exist in both!
{
inputs,
outputs,
config,
lib,
pkgs,
isDarwin,
...
}:
let
platform = if isDarwin then "darwin" else "nixos";
platformModules = "${platform}Modules";
in
{
imports = lib.flatten [
inputs.home-manager.${platformModules}.home-manager
(map lib.custom.relativeToRoot [
"modules/common"
"modules/hosts/common"
"modules/hosts/${platform}"
"hosts/common/core/${platform}.nix"
#"hosts/common/core/sops.nix" # Core because it's used for backups, mail
"hosts/common/core/ssh.nix"
#"hosts/common/core/services" # uncomment this line if you add any modules to services directory
"hosts/common/users/primary"
"hosts/common/users/primary/${platform}.nix"
])
];
#
# ========== Core Host Specifications ==========
#
# FIXME(starter): modify the hostSpec options below to define values that are common across all hosts
# such as the username and handle of the primary user (see also `nix-config/hosts/common/users/primary`)
hostSpec = {
username = "panotaka";
handle = "panotaka";
# FIXME(starter): modify the attribute sets hostSpec will inherit from your nix-secrets.
# If you're not using nix-secrets then remove the following six lines below.
};
networking.hostName = config.hostSpec.hostName;
# System-wide packages, in case we log in as root
environment.systemPackages = [ pkgs.openssh ];
# Force home-manager to use global packages
home-manager.useGlobalPkgs = true;
# If there is a conflict file that is backed up, use this extension
home-manager.backupFileExtension = "bk";
#
# ========== Overlays ==========
#
nixpkgs = {
overlays = [
outputs.overlays.default
];
config = {
allowUnfree = true;
};
};
#
# ========== Nix Nix Nix ==========
#
nix = {
# This will add each flake input as a registry
# To make nix3 commands consistent with your flake
registry = lib.mapAttrs (_: value: { flake = value; }) inputs;
# This will add your inputs to the system's legacy channels
# Making legacy nix commands consistent as well, awesome!
nixPath = lib.mapAttrsToList (key: value: "${key}=${value.to.path}") config.nix.registry;
settings = {
# See https://jackson.dev/post/nix-reasonable-defaults/
connect-timeout = 5;
log-lines = 25;
min-free = 128000000; # 128MB
max-free = 1000000000; # 1GB
trusted-users = [ "@wheel" ];
# Deduplicate and optimize nix store
auto-optimise-store = true;
warn-dirty = false;
allow-import-from-derivation = true;
experimental-features = [
"nix-command"
"flakes"
];
};
};
}

View File

@@ -0,0 +1,36 @@
# Core functionality for every nixos host
{ config, lib, ... }:
{
# Database for aiding terminal-based programs
environment.enableAllTerminfo = true;
# Enable firmware with a license allowing redistribution
hardware.enableRedistributableFirmware = true;
# This should be handled by config.security.pam.sshAgentAuth.enable
security.sudo.extraConfig = ''
Defaults lecture = never # rollback results in sudo lectures after each reboot, it's somewhat useless anyway
Defaults pwfeedback # password input feedback - makes typed password visible as asterisks
Defaults timestamp_timeout=120 # only ask for password every 2h
# Keep SSH_AUTH_SOCK so that pam_ssh_agent_auth.so can do its magic.
Defaults env_keep+=SSH_AUTH_SOCK
'';
#
# ========== Nix Helper ==========
#
# Provide better build output and will also handle garbage collection in place of standard nix gc (garbace collection)
# FIXME(starter): customize garbage collection rules as desired.
programs.nh = {
enable = true;
clean.enable = true;
clean.extraArgs = "--keep-since 20d --keep 20";
flake = "/home/user/${config.hostSpec.home}/nix-config";
};
#
# ========== Localization ==========
#
# FIXME(starter): customize localization values as desired.
i18n.defaultLocale = lib.mkDefault "en_US.UTF-8";
time.timeZone = lib.mkDefault "America/Edmonton";
}

View File

@@ -0,0 +1,9 @@
# Add your core services to the same directory as this default.nix file.
# They will automatically be imported below.
{
lib,
...
}:
{
imports = (lib.custom.scanPaths ./.);
}

11
hosts/common/core/ssh.nix Normal file
View File

@@ -0,0 +1,11 @@
{
lib,
pkgs,
...
}:
{
programs.ssh = lib.optionalAttrs pkgs.stdenv.isLinux {
startAgent = true;
enableAskPassword = true;
};
}

View File

@@ -0,0 +1,65 @@
# `...` is needed because dikso passes diskoFile
{
lib,
disk ? "/dev/vda",
withSwap ? false,
swapSize,
...
}:
{
disko.devices = {
disk = {
disk0 = {
type = "disk";
device = disk;
content = {
type = "gpt";
partitions = {
ESP = {
priority = 1;
name = "ESP";
start = "1M";
end = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "defaults" ];
};
};
root = {
size = "100%";
content = {
type = "btrfs";
extraArgs = [ "-f" ]; # Override existing partition
# Subvolumes must set a mountpoint in order to be mounted,
# unless their parent is mounted
subvolumes = {
"@root" = {
mountpoint = "/";
mountOptions = [
"compress=zstd"
"noatime"
];
};
"@nix" = {
mountpoint = "/nix";
mountOptions = [
"compress=zstd"
"noatime"
];
};
"@swap" = lib.mkIf withSwap {
mountpoint = "/.swapvol";
swap.swapfile.size = "${swapSize}G";
};
};
};
};
};
};
};
};
};
}

View File

@@ -0,0 +1,71 @@
# `...` is needed because dikso passes diskoFile
{
lib,
disk ? "/dev/vda",
withSwap ? false,
swapSize,
...
}:
{
disko.devices = {
disk = {
main = {
type = "disk";
device = disk;
content = {
type = "gpt";
partitions = {
ESP = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted";
# disable settings.keyFile if you want to use interactive password entry
#passwordFile = "/tmp/secret.key"; # Interactive
settings = {
allowDiscards = true;
};
content = {
type = "btrfs";
extraArgs = [ "-f" ]; # Override existing partition
# Subvolumes must set a mountpoint in order to be mounted,
# unless their parent is mounted
subvolumes = {
"@root" = {
mountpoint = "/";
mountOptions = [
"compress=zstd"
"noatime"
];
};
"@nix" = {
mountpoint = "/nix";
mountOptions = [
"compress=zstd"
"noatime"
];
};
"@swap" = lib.mkIf withSwap {
mountpoint = "/.swapvol";
swap.swapfile.size = "${swapSize}G";
};
};
};
};
};
};
};
};
};
};
}

View File

@@ -0,0 +1,21 @@
# NOTE(starter): configure your audio needs as required.
{ pkgs, ... }:
{
hardware.pulseaudio.enable = false;
security.rtkit.enable = true;
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
wireplumber.enable = true;
jack.enable = true;
};
environment.systemPackages = builtins.attrValues {
inherit (pkgs)
playerctl # cli utility and lib for controlling media players
# pamixer # cli pulseaudio sound mixer
;
};
}

View File

@@ -0,0 +1,4 @@
# NOTE(starter): This is just a basic enabling of the XFCE windows manager for simplicity
{
services.desktopManager.plasma6.enable = true;
}

View File

@@ -0,0 +1,13 @@
# NOTE: This module is required for nixos-installer
{ config, ... }:
{
# Set a temp password for use by minimal builds like installer and iso
users.users.${config.hostSpec.username} = {
isNormalUser = true;
# This is a hashed version of the plain-text password "nixos" for use in the ISO. Even though,
# the password is known, we use `hashedPassword` here instead of `password` to mitigate
# occurrences of the latter not being used during build.
password = "password";
extraGroups = [ "wheel" ];
};
}

View File

@@ -0,0 +1,10 @@
# NOTE(starter): This is just a basic enabling of the XFCE windows manager for simplicity
{
services.displayManager = {
sddm.enable = true;
sddm.wayland = {
enable = true;
compositor = "kwin";
};
};
}

View File

@@ -0,0 +1,36 @@
{
config,
...
}:
let
# FIXME(starter): if you are not defining ports in your "soft" nix-secrets, you can
# replace the following line with: sshPort = 22;
# and substitute 22 with a custom port number if needed.
sshPort = 22;
in
{
services.openssh = {
enable = true;
ports = [ sshPort ];
settings = {
# Harden
PasswordAuthentication = false;
PermitRootLogin = "no";
# Automatically remove stale sockets
StreamLocalBindUnlink = "yes";
# Allow forwarding ports to everywhere
GatewayPorts = "clientspecified";
};
hostKeys = [
{
path = "/etc/ssh/ssh_host_ed25519_key";
type = "ed25519";
}
];
};
networking.firewall.allowedTCPPorts = [ sshPort ];
}

View File

@@ -0,0 +1,5 @@
# NOTE(starter): This is just a basic enabling of the XFCE windows manager for simplicity
{
services.xserver.desktopManager.xfce.enable = true;
services.xserver.desktopManager.xfce.enableScreensaver = false;
}

View File

@@ -0,0 +1,67 @@
# FIXME(starter): this is an example of how a secondary user called "exampleSecondUser" can be declared.
# NOTE that this file's parent directory matches the username!
# Modify the directory name and all instances of `exampleSecondUser` in this file to a real username to
# make use of this file. You'll also need to import this file in the relevant `nix-config/hosts/[platform]/[hostname]/default.nix`
# host file for the user to be created on the host.
# NOTE that this file also assumes you will be declaring the user's password via sops.
# .
# If you have no need for secondary users, simple delete this file and its parent directory, and ensure that
# your `nix-confitg/hosts/[platform]/[hostname]/default.nix` files do not import this file.
#
# Basic user for viewing exampleSecondUser
#
{
inputs,
lib,
pkgs,
config,
...
}:
let
hostSpec = config.hostSpec;
secretsSubPath = "passwords/exampleSecondUser";
in
{
# Decrypt passwords/exampleSecondUser to /run/secrets-for-users/ so it can be used to create the user
sops.secrets.${secretsSubPath}.neededForUsers = true;
users.mutableUsers = false; # Required for password to be set via sops during system activation!
users.users.exampleSecondUser = {
isNormalUser = true;
hashedPasswordFile = config.sops.secrets.${secretsSubPath}.path;
shell = pkgs.zsh; # default shell
extraGroups = [
"audio"
"video"
];
packages = [ pkgs.home-manager ];
};
}
# Import this user's personal/home configurations
// lib.optionalAttrs (inputs ? "home-manager") {
home-manager = {
extraSpecialArgs = {
inherit pkgs inputs;
hostSpec = config.hostSpec;
};
users.exampleSecondUser.imports = lib.flatten (
lib.optional (!hostSpec.isMinimal) [
(
{ config, ... }:
import (lib.custom.relativeToRoot "home/exampleSecondUser/${hostSpec.hostName}.nix") {
inherit
pkgs
inputs
config
lib
hostSpec
;
}
)
]
);
};
}

View File

@@ -0,0 +1,7 @@
# User config applicable only to darwin
{ config, ... }:
{
users.users.${config.hostSpec.username} = {
home = "/Users/${config.hostSpec.username}";
};
}

View File

@@ -0,0 +1,68 @@
# NOTE(starter): this is the primary user across all hosts. The username for primary is defined in hostSpec,
# and is declared in `nix-config/common/core/default.nix`
# User config applicable to both nixos and darwin
{
inputs,
pkgs,
config,
lib,
...
}:
let
hostSpec = config.hostSpec;
pubKeys = lib.filesystem.listFilesRecursive ./keys;
in
{
users.users.${hostSpec.username} = {
name = hostSpec.username;
shell = pkgs.bash; # default shell
# These get placed into /etc/ssh/authorized_keys.d/<name> on nixos
openssh.authorizedKeys.keys = lib.lists.forEach pubKeys (key: builtins.readFile key);
};
# Create ssh sockets directory for controlpaths when homemanager not loaded (i.e. isMinimal)
systemd.tmpfiles.rules =
let
user = config.users.users.${hostSpec.username}.name;
group = config.users.users.${hostSpec.username}.group;
in
# you must set the rule for .ssh separately first, otherwise it will be automatically created as root:root and .ssh/sockects will fail
[
"d /home/${hostSpec.username}/.ssh 0750 ${user} ${group} -"
"d /home/${hostSpec.username}/.ssh/sockets 0750 ${user} ${group} -"
];
# No matter what environment we are in we want these tools
programs.zsh.enable = true;
environment.systemPackages = [
pkgs.just
pkgs.rsync
];
}
# Import the user's personal/home configurations, unless the environment is minimal
// lib.optionalAttrs (inputs ? "home-manager") {
home-manager = {
extraSpecialArgs = {
inherit pkgs inputs;
hostSpec = config.hostSpec;
};
users.${hostSpec.username}.imports = lib.flatten (
lib.optional (!hostSpec.isMinimal) [
(
{ config, ... }:
import (lib.custom.relativeToRoot "home/${hostSpec.username}/${hostSpec.hostName}.nix") {
inherit
pkgs
inputs
config
lib
hostSpec
;
}
)
]
);
};
}

View File

@@ -0,0 +1 @@
Add your ssh public keys to this directory. E.g. id_foo.pub

View File

@@ -0,0 +1,44 @@
# User config applicable only to nixos
{
config,
lib,
pkgs,
...
}:
let
hostSpec = config.hostSpec;
ifTheyExist = groups: builtins.filter (group: builtins.hasAttr group config.users.groups) groups;
in
{
users.mutableUsers = false; # Only allow declarative credentials; Required for password to be set via sops during system activation!
users.users.${hostSpec.username} = {
home = "/home/${hostSpec.username}";
isNormalUser = true;
password = "password";
extraGroups = lib.flatten [
"wheel"
(ifTheyExist [
"audio"
"video"
"docker"
"git"
"networkmanager"
"scanner" # for print/scan"
"lp" # for print/scan"
])
];
};
# No matter what environment we are in we want these tools for root, and the user(s)
programs.git.enable = true;
# root's ssh key are mainly used for remote deployment, borg, and some other specific ops
users.users.root = {
shell = pkgs.bash;
hashedPasswordFile = config.users.users.${hostSpec.username}.hashedPasswordFile;
hashedPassword = config.users.users.${hostSpec.username}.hashedPassword; # This comes from hosts/common/optional/minimal.nix and gets overridden if sops is working
openssh.authorizedKeys.keys = config.users.users.${hostSpec.username}.openssh.authorizedKeys.keys; # root's ssh keys are mainly used for remote deployment.
};
}

View File

@@ -0,0 +1,73 @@
{
inputs,
lib,
...
}:
let
in
{
imports = [
#
# ========== Hardware ==========
#
./hardware-configuration.nix
#inputs.hardware.nixosModules.common-cpu-amd
#inputs.hardware.nixosModules.common-cpu-intel
#inputs.hardware.nixosModules.common-gpu-nvidia
#inputs.hardware.nixosModules.common-gpu-intel
#inputs.hardware.nixosModules.common-pc-ssd
#
# ========== Disk Layout ==========
#
#inputs.disko.nixosModules.disko
#
# ========== Misc Inputs ==========
#
(map lib.custom.relativeToRoot [
#
# ========== Required Configs ==========
#
"hosts/common/core"
#
# ========== Non-Primary Users to Create ==========
#
#
# ========== Optional Configs ==========
#
])
];
#
# ========== Host Specification ==========
#
hostSpec = {
hostName = "foo";
scaling = lib.mkForce "1";
};
networking = {
networkmanager.enable = true;
enableIPv6 = false;
};
boot.loader = {
systemd-boot = {
enable = true;
};
efi.canTouchEfiVariables = true;
timeout = 3;
};
boot.initrd = {
systemd.enable = true;
};
# https://wiki.nixos.org/wiki/FAQ/When_do_I_update_stateVersion
system.stateVersion = "24.11";
}

View File

@@ -0,0 +1,107 @@
# This is an example nixos hosts module.
# They will automatically be imported below.
#############################################################
#
# Bellerophon - Example Desktop
#
###############################################################
{
inputs,
lib,
...
}:
{
imports = lib.flatten [
#
# ========== Hardware ==========
#
inputs.nixos-hardware.nixosModules.common-cpu-intel
inputs.nixos-hardware.nixosModules.common-hidpi
inputs.nixos-hardware.nixosModules.common-pc-laptop
inputs.nixos-hardware.nixosModules.common-pc-laptop-ssd
./hardware-configuration.nix
#
# ========== Disk Layout ==========
#
inputs.disko.nixosModules.disko
# FIXME(starter): modify with the disko spec file you want to use.
(lib.custom.relativeToRoot "hosts/common/disks/luks-btrfs-disk.nix")
# FIXME(starter): modify the options below to inform disko of the host's disk path and swap requirements.
# IMPORTANT: nix-config-starter assumes a single disk per host. If you require more disks, you
# must modify or create new dikso specs.
{
_module.args = {
disk = "/dev/nvme0n1";
withSwap = true;
swapSize = "64";
};
}
(map lib.custom.relativeToRoot [
#
# ========== Required Configs ==========
#
"hosts/common/core"
#
# ========== Non-Primary Users to Create ==========
#
# FIXME(starter): the primary user, defined in `nix-config/hosts/common/users`, is added by default, via
# `hosts/common/core` above.
# To create additional users, specify the path to their config file, as shown in the commented line below, and create/modify
# the specified file as required. See `nix-config/hosts/common/users/exampleSecondUser` for more info.
#"hosts/common/users/exampleSecondUser"
#
# ========== Optional Configs ==========
#
# FIXME(starter): add or remove any optional host-level configuration files the host will use
# The following are for example sake only and are not necessarily required.
"hosts/common/optional/services/openssh.nix" # allow remote SSH access
"hosts/common/optional/audio.nix" # pipewire and cli controls
"hosts/common/optional/kde.nix"
"hosts/common/optional/sddm.nix"
])
];
#
# ========== Host Specification ==========
#
# FIXME(starter): declare any host-specific hostSpec options. Note that hostSpec options pertaining to
# more than one host can be declared in `nix-config/hosts/common/core/` see the default.nix file there
# for examples.
hostSpec = {
hostName = "Bellerophon";
};
networking = {
networkmanager.enable = true;
enableIPv6 = false;
};
boot.loader = {
systemd-boot = {
enable = true;
# When using plymouth, initrd can expand by a lot each time, so limit how many we keep around
configurationLimit = lib.mkDefault 10;
};
efi.canTouchEfiVariables = true;
timeout = 3;
};
boot.initrd = {
systemd.enable = true;
};
hardware.graphics = {
enable = true;
};
# https://wiki.nixos.org/wiki/FAQ/When_do_I_update_stateVersion
system.stateVersion = "24.11";
}

View File

@@ -0,0 +1,49 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
pkgs,
modulesPath,
...
}: {
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = ["xhci_pci" "thunderbolt" "vmd" "nvme" "usbhid"];
boot.initrd.kernelModules = [];
boot.kernelPackages = pkgs.linuxPackages_6_12;
boot.kernelModules = ["kvm-intel"];
boot.kernelPatches = [
/*
{
name = "zenbook-asus-wmi";
patch = ./zenbook-asus-wmi.patch;
}
*/
];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
#networking.useDHCP = lib.mkDefault true;
# networking.interfaces.docker0.useDHCP = lib.mkDefault true;
# networking.interfaces.wlo1.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
environment.systemPackages = with pkgs; [
sof-firmware
#rotation stuff
pkgs.gnome-monitor-config
pkgs.usbutils
pkgs.inotify-tools
pkgs.kdePackages.libkscreen
];
}

107
hosts/nixos/iso/default.nix Normal file
View File

@@ -0,0 +1,107 @@
#NOTE: This ISO is NOT minimal. We don't want a minimal environment when using the iso for recovery purposes.
{
inputs,
pkgs,
lib,
config,
...
}:
{
imports = lib.flatten [
# FIXME(starter): comment/uncomment the following two lines depending on if you want a cli-only, minimal iso, or a graphical iso that installs gnome
# If you are planning to make use of `nix-config/nixos-installer`, you will not require a graphical iso.
"${inputs.nixpkgs}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix"
#"${inputs.nixpkgs}/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix"
"${inputs.nixpkgs}/nixos/modules/installer/cd-dvd/channel.nix"
# This is overkill but I want my core home level utils if I need to use the iso environment for recovery purpose
inputs.home-manager.nixosModules.home-manager
(map lib.custom.relativeToRoot [
"modules/common/host-spec.nix"
# We want primary default so we get ssh authorized keys, zsh, and some basic tty tools. It also pulls in the hm spec for iso.
# Note that we are not pulling in "hosts/common/users/primary/nixos.nix" for the iso as it's not needed.
"hosts/common/users/primary/"
"hosts/common/optional/minimal-user.nix"
])
];
hostSpec = {
hostName = "iso";
# FIXME(starter): the username below will be available in additional the the standard `root` and `nixos` users from the nixos installation image.
username = "panotaka";
isProduction = lib.mkForce false;
# FIXME(starter): add your github username and github-noreply email address
handle = "panotaka";
#email.gitHub = "foo@users.noreply.github.com";
};
# root's ssh key are mainly used for remote deployment
users.extraUsers.root = {
inherit (config.users.users.${config.hostSpec.username}) hashedPassword;
openssh.authorizedKeys.keys =
config.users.users.${config.hostSpec.username}.openssh.authorizedKeys.keys;
};
environment.etc = {
isoBuildTime = {
#
text = lib.readFile (
"${pkgs.runCommand "timestamp" {
# builtins.currentTime requires --impure
env.when = builtins.currentTime;
} "echo -n `date -d @$when +%Y-%m-%d_%H-%M-%S` > $out"}"
);
};
};
# Add the build time to the prompt so it's easier to know the ISO age
programs.bash.promptInit = ''
export PS1="\\[\\033[01;32m\\]\\u@\\h-$(cat /etc/isoBuildTime)\\[\\033[00m\\]:\\[\\033[01;34m\\]\\w\\[\\033[00m\\]\\$ "
'';
# The default compression-level is (6) and takes too long on some machines (>30m). 3 takes <2m
isoImage.squashfsCompression = "zstd -Xcompression-level 3";
nixpkgs = {
hostPlatform = lib.mkDefault "x86_64-linux";
config.allowUnfree = true;
};
nix = {
settings.experimental-features = [
"nix-command"
"flakes"
];
extraOptions = "experimental-features = nix-command flakes";
};
services = {
qemuGuest.enable = true;
openssh = {
settings.PermitRootLogin = lib.mkForce "yes";
};
};
boot = {
kernelPackages = pkgs.linuxPackages_latest;
supportedFilesystems = lib.mkForce [
"btrfs"
"vfat"
];
};
networking = {
hostName = "iso";
};
systemd = {
services.sshd.wantedBy = lib.mkForce [ "multi-user.target" ];
# gnome power settings to not turn off screen
targets = {
sleep.enable = false;
suspend.enable = false;
hibernate.enable = false;
hybrid-sleep.enable = false;
};
};
}

130
justfile Normal file
View File

@@ -0,0 +1,130 @@
SOPS_FILE := "../nix-secrets/.sops.yaml"
# Define path to helpers
export HELPERS_PATH := justfile_directory() + "/scripts/helpers.sh"
# default recipe to display help information
default:
@just --list
# Update commonly changing flakes and prep for a rebuild
rebuild-pre: update-nix-secrets
@git add --intent-to-add .
# Run post-rebuild checks, like if sops is running properly afterwards
rebuild-post: check-sops
# Run a flake check on the config and installer
check ARGS="":
NIXPKGS_ALLOW_UNFREE=1 REPO_PATH=$(pwd) nix flake check --impure --keep-going --show-trace {{ARGS}}
cd nixos-installer && NIXPKGS_ALLOW_UNFREE=1 REPO_PATH=$(pwd) nix flake check --impure --keep-going --show-trace {{ARGS}}
# Rebuild the system
rebuild: rebuild-pre && rebuild-post
# NOTE: Add --option eval-cache false if you end up caching a failure you cant get around
scripts/rebuild.sh
# Rebuild the system and run a flake check
rebuild-full: rebuild-pre && rebuild-post
scripts/rebuild.sh
just check
# Rebuild the system and run a flake check
rebuild-trace: rebuild-pre && rebuild-post
scripts/rebuild.sh trace
just check
# Update the flake
update:
nix flake update
# Update and then rebuild
rebuild-update: update rebuild
# Git diff there entire repo expcept for flake.lock
diff:
git diff ':!flake.lock'
# Generate a new age key
age-key:
nix-shell -p age --run "age-keygen"
# Check if sops-nix activated successfully
check-sops:
scripts/check-sops.sh
# Update nix-secrets flake
update-nix-secrets:
@(cd ../nix-secrets && git fetch && git rebase > /dev/null) || true
nix flake update nix-secrets --timeout 5
# Build an iso image for installing new systems and create a symlink for qemu usage
iso:
# If we dont remove this folder, libvirtd VM doesnt run with the new iso...
rm -rf result
nix build --impure .#nixosConfigurations.iso.config.system.build.isoImage && ln -sf result/iso/*.iso latest.iso
# Install the latest iso to a flash drive
iso-install DRIVE: iso
sudo dd if=$(eza --sort changed result/iso/*.iso | tail -n1) of={{DRIVE}} bs=4M status=progress oflag=sync
# Configure a drive password using disko
disko DRIVE PASSWORD:
echo "{{PASSWORD}}" > /tmp/disko-password
sudo nix --experimental-features "nix-command flakes" run github:nix-community/disko -- \
--mode disko \
disks/btrfs-luks-impermanence-disko.nix \
--arg disk '"{{DRIVE}}"' \
--arg password '"{{PASSWORD}}"'
rm /tmp/disko-password
# Copy all the config files to the remote host
sync USER HOST PATH:
rsync -av --filter=':- .gitignore' -e "ssh -l {{USER}} -oport=22" . {{USER}}@{{HOST}}:{{PATH}}/nix-config
# Run nixos-rebuild on the remote host
build-host HOST:
NIX_SSHOPTS="-p22" nixos-rebuild --target-host {{HOST}} --use-remote-sudo --show-trace --impure --flake .#"{{HOST}}" switch
# Called by the rekey recipe
sops-rekey:
cd ../nix-secrets && for file in $(ls sops/*.yaml); do \
sops updatekeys -y $file; \
done
# Update all keys in sops/*.yaml files in nix-secrets to match the creation rules keys
rekey: sops-rekey
cd ../nix-secrets && \
(pre-commit run --all-files || true) && \
git add -u && (git commit -nm "chore: rekey" || true) && git push
# Update an age key anchor or add a new one
sops-update-age-key FIELD KEYNAME KEY:
#!/usr/bin/env bash
source {{HELPERS_PATH}}
sops_update_age_key {{FIELD}} {{KEYNAME}} {{KEY}}
# Update an existing user age key anchor or add a new one
sops-update-user-age-key USER HOST KEY:
just sops-update-age-key users {{USER}}_{{HOST}} {{KEY}}
# Update an existing host age key anchor or add a new one
sops-update-host-age-key HOST KEY:
just sops-update-age-key hosts {{HOST}} {{KEY}}
# Automatically create creation rules entries for a <host>.yaml file for host-specific secrets
sops-add-host-creation-rules USER HOST:
#!/usr/bin/env bash
source {{HELPERS_PATH}}
sops_add_host_creation_rules "{{USER}}" "{{HOST}}"
# Automatically create creation rules entries for a shared.yaml file for shared secrets
sops-add-shared-creation-rules USER HOST:
#!/usr/bin/env bash
source {{HELPERS_PATH}}
sops_add_shared_creation_rules "{{USER}}" "{{HOST}}"
# Automatically add the host and user keys to creation rules for shared.yaml and <host>.yaml
sops-add-creation-rules USER HOST:
just sops-add-host-creation-rules {{USER}} {{HOST}} && \
just sops-add-shared-creation-rules {{USER}} {{HOST}}

21
lib/default.nix Normal file
View File

@@ -0,0 +1,21 @@
#NOTE(starter): custom functions added here are available via `lib.custom.foo` by passing `lib` into
# the expression parameters. The two functions below are used by `nix-config` and should not be modified.
{ lib, ... }:
{
# use path relative to the root of the project
relativeToRoot = lib.path.append ../.;
scanPaths =
path:
builtins.map (f: (path + "/${f}")) (
builtins.attrNames (
lib.attrsets.filterAttrs (
path: _type:
(_type == "directory") # include directories
|| (
(path != "default.nix") # ignore default.nix
&& (lib.strings.hasSuffix ".nix" path) # include .nix files
)
) (builtins.readDir path)
)
);
}

View File

@@ -0,0 +1,9 @@
# Add your reusable modules that are common across both home and hosts to this directory, in their own file (https://wiki.nixos.org/wiki/NixOS_modules).
# They will automatically be imported below but must be enabled elsewhere in the config, such as in common/core,
# common/optional, or common/hosts files for example.
# These are modules not specific to either nixos, darwin, or home-manger that you would share with others, not your personal configurations.
{ lib, ... }:
{
imports = lib.custom.scanPaths ./.;
}

View File

@@ -0,0 +1,153 @@
# Specifications For Differentiating Hosts
{
config,
pkgs,
lib,
...
}:
{
options.hostSpec = {
# Data variables that don't dictate configuration settings
username = lib.mkOption {
type = lib.types.str;
description = "The username of the host";
};
hostName = lib.mkOption {
type = lib.types.str;
description = "The hostname of the host";
};
email = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
description = "The email of the user";
};
work = lib.mkOption {
default = { };
type = lib.types.attrsOf lib.types.anything;
description = "An attribute set of work-related information if isWork is true";
};
networking = lib.mkOption {
default = { };
type = lib.types.attrsOf lib.types.anything;
description = "An attribute set of networking information";
};
wifi = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Used to indicate if a host has wifi";
};
domain = lib.mkOption {
type = lib.types.str;
description = "The domain of the host";
};
userFullName = lib.mkOption {
type = lib.types.str;
description = "The full name of the user";
};
handle = lib.mkOption {
type = lib.types.str;
description = "The handle of the user (eg: github user)";
};
home = lib.mkOption {
type = lib.types.str;
description = "The home directory of the user";
default =
let
user = config.hostSpec.username;
in
if pkgs.stdenv.isLinux then "/home/${user}" else "/Users/${user}";
};
persistFolder = lib.mkOption {
type = lib.types.str;
description = "The folder to persist data if impermenance is enabled";
default = "";
};
# Configuration Settings
isMinimal = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Used to indicate a minimal host";
};
isProduction = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Used to indicate a production host";
};
isServer = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Used to indicate a server host";
};
isWork = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Used to indicate a host that uses work resources";
};
# Sometimes we can't use pkgs.stdenv.isLinux due to infinite recursion
isDarwin = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Used to indicate a host that is darwin";
};
useYubikey = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Used to indicate if the host uses a yubikey";
};
voiceCoding = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Used to indicate a host that uses voice coding";
};
isAutoStyled = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Used to indicate a host that wants auto styling like stylix";
};
useNeovimTerminal = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Used to indicate a host that uses neovim for terminals";
};
useWindowManager = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Used to indicate a host that uses a window manager";
};
useAtticCache = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Used to indicate a host that uses LAN atticd for caching";
};
hdr = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Used to indicate a host that uses HDR";
};
scaling = lib.mkOption {
type = lib.types.str;
default = "1";
description = "Used to indicate what scaling to use. Floating point number";
};
};
config = {
assertions =
let
# We import these options to HM and NixOS, so need to not fail on HM
isImpermanent =
config ? "system" && config.system ? "impermanence" && config.system.impermanence.enable;
in
[
{
assertion =
!config.hostSpec.isWork || (config.hostSpec.isWork && !builtins.isNull config.hostSpec.work);
message = "isWork is true but no work attribute set is provided";
}
{
assertion = !isImpermanent || (isImpermanent && !("${config.hostSpec.persistFolder}" == ""));
message = "config.system.impermanence.enable is true but no persistFolder path is provided";
}
];
};
}

9
modules/home/default.nix Normal file
View File

@@ -0,0 +1,9 @@
# Add your reusable home-manager modules to this directory, in their own file (https://wiki.nixos.org/wiki/NixOS_modules).
# They will automatically be imported below but must be enabled elsewhere in the config, such as in common/core,
# common/optional, or common/hosts files for example.
# These are modules you would share with others, not your personal configurations.
{ lib, ... }:
{
imports = lib.custom.scanPaths ./.;
}

View File

@@ -0,0 +1,10 @@
# Add your reusable host-level modules that are common across both nixos and darwin to this directory, in
# their own file (https://wiki.nixos.org/wiki/NixOS_modules).
# They will automatically be imported below but must be enabled elsewhere in the config, such as in common/core,
# common/optional, or common/hosts files for example.
# These are modules you would share with others, not your personal configurations.
{ lib, ... }:
{
imports = lib.custom.scanPaths ./.;
}

View File

@@ -0,0 +1,9 @@
# Add your reusable nix-darwin modules to this directory, in their own file (https://wiki.nixos.org/wiki/NixOS_modules).
# They will automatically be imported below but must be enabled elsewhere in the config, such as in common/core,
# common/optional, or common/hosts files for example.
# These are modules you would share with others, not your personal configurations.
{ lib, ... }:
{
imports = lib.custom.scanPaths ./.;
}

View File

@@ -0,0 +1,9 @@
# Add your reusable NixOS modules to this directory, in their own file (https://wiki.nixos.org/wiki/NixOS_modules).
# They will automatically be imported below but must be enabled elsewhere in the config, such as in common/core,
# common/optional, or common/hosts files for example.
# These are modules you would share with others, not your personal configurations.
{ lib, ... }:
{
imports = lib.custom.scanPaths ./.;
}

312
nixos-installer/README.md Normal file
View File

@@ -0,0 +1,312 @@
# NixOS Installer for Nix-Config
This flake is separate from the main nix-config flake and prepares a Nix environment for bootstrapping a nix-config host on a new machine. Most of the process is automated with the [`nixos-bootstrap.sh`](../scripts/nixos-bootstraph.sh) script that is run on a "source" host to install NixOS on a "target" machine. There are a couple of small manual steps that are typical of any OS installation procedure, such defining information about the target host and adding host-specific secrets to the relevant sops secrets file. This document explains some of the reasoning behind the use of a separate flake and then provides installation steps. For a more indepth look at some of the concepts, reasoning, and automation process, see the blog post [Remotely Installing NixOS and nix-config with Secrets](https://unmovedcentre.com/posts/remote-install-nixos-config/) on my website. Note that the blog post was written during the first iteration of the bootstrap script and there have been significant enhancements to the code since that time. The general idea and flow still stand and may provide useful insight to understanding the script itself, for those who want to learn more about what it does.
- [Why an extra flake?](#why-an-extra-flake)
- [Assumptions](#assumptions)
- [Generating a custom NixOS ISO](#generating-a-custom-nixos-iso)
- [Requirements for Installing a New Host](#requirements-for-installing-a-new-host)
- [Requirements for installing an existing nix-config host on a new machine](requirements-for-installing-an-existing-nix-config-host-on-a-new-machine)
- [Installation Steps](#installation-steps)
- [Troubleshooting](#Troubleshooting)
## Why an extra flake?
The main flake, `nix-config/flake.nix`, takes longer to build, debug, and deploy because even the core modules are focused on a broad set of functional requirements. In contrast, this simplified flake is focused only on providing an environment with which to accomplish the following:
- Prepare the machine to successfully authenticate with our private nix-secrets repo _and_ decrypt the required secrets when the main flake is built.
- Adjust and verify the new host's `hardware-configuration.nix` and potentially modify it prior to building the main flake.
- We also have the option of testing new filesystem related features such as impermanence, Secure Boot, TPM2, Encryption, etc in a light weight environment prior to finalizing the main flake.
## Assumptions
The installer and instructions here assume that a _private_ nix-secrets repository is in use in conjunction with the nix-config _and_ the nix-secrets repo is structured to use shared secrets as well as host-specific secrets. Reference the _complex_ branch of the [nix-secrets-reference](https://github.com/EmergentMind/nix-secrets-reference) repository for an example of the expected structure as well as the article on [NixOS Secrets Management](https://unmovedcentre.com/posts/secrets-management/) to learn more.
For users new to Nix and NixOS it may be worth noting that because this script is installing NixOS, the usual [NixOS requirements](https://nixos.org/download/#nixos-iso) apply.
## Generating a custom NixOS ISO
We recommend using a custom ISO similar to what is defined in `nix-config/hosts/nixos/iso`. The official minimal NixOS iso has historical omitted some basic tty utilities that are expected by the installer scripts. The config for the ISO used in nix-config are similarly light-weight to [`nixos-installer/flake.nix`](flake.nix).
To generate the ISO, simply run `just iso` from the root of your `nix-config` directory. The resulting .iso file will be saved to `nix-config/result/iso/foo.iso`. A symlink to the file is also created at `nix-config/latest.iso`. The filename is time stamped for convenient reference when frequently trying out different ISOs in VMs. For example, `nixos-24.11.20250123.035f8c0-x86_64-linux.iso`.
If you are installing the host to a VM or remote infrastructure, configure the machine to boot into the .iso file.
If you are installing on a bare metal machine, write the .iso to a USB device. You can generate the iso and write it to a device in one command, using `just iso /path/to/usb/device`.
## Requirements for installing a new host
### Pre-installation steps:
1. Add `nix-config/hosts/nixos/[hostname]/` and `nix-config/home/[user]/[hostname].nix` files. You must declare the configuration settings for the target host as usual in your nix-config.
Be sure to specify the device name (e.g. sda, nvme0n1, vda, etc) you want to install to along with the desired `nix-config/hosts/common/disks` disko spec.
If needed, you can find the device name on the target machine itself by booting it into the iso environment and running `lsblk` to see a list of the devices. Virtual Machines often using a device called `vda`.
2. Add a `newConfig` entry for the target host in `nix-config/nixos-installer/flake.nix`, passing in the required arguments as noted in the file comments.
3. If you are planning to use the `backup` module on the target host, you _must_ temporarily disable it in the target host's config options until bootstrapping is complete. Failure to disable these two modules, will cause nix-config to look for the associated secrets in the new `[hostname].yaml` secrets file where they have not yet been added, causing sops-nix to fail to start during the build process. After rebuilding, we'll add the required keys to secrets and re-enable these modules.
For example:
```nix
# nix-config/hosts/nixos/guppy/default.nix
#--------------------
# ...
# The back module is enabled via a services option. Set it to false.
services.backup = {
enable = false;
# ...
};
#...
```
#### A note about secrets
There are different ways to set up secrets for a new target host. Some are more involved than others but they _all_ require some level of manual entry.
The installer script automates many of the required steps and therefore we will only describe the process of relying on that automation and making required manual entries near the end of the installation process.
In brief, the script will:
- create a host-specific age key pair
- create a host-specific user age key pair for the primary user
- create a `nix-secrets/sops/[hostname].yaml` secrets file with the user age private key (the host age private key is always derived from the host ssh key and therefore does not need to be stored in secrets)
- update the `.sops.yaml` file with:
- public age keys entries for both the host and user
- update the `creation_rules` for `shared.yaml` with the host and user age keys for the target host.
- create a new `creation_rules` entry for `[hostname].yaml` specifying that the secrets file can be encrypted and decrypted by the primary user and host of both the target host _AND_ the host from which the installation script is being executed. This is important because until the target host has been fully bootstrapped, its `[hostname].yaml` must be accessible by something.
For example, a host `ghost` running the installer script on target host `guppy` will result in the following sops `creation_rules` entry in `.sops.yaml`:
```yaml
- path_regex: guppy\.yaml$
key_groups:
- age:
- *ta_guppy
- *guppy
- *ta_ghost
- *ghost
```
As mentioned, the time for manual steps will be noted below.
## Requirements for installing an existing nix-config host on a new machine
Prior to installing an existing host config onto a new machine you likely only need to ensure that the `nix-config//hosts/nixos/[hostname]/default.nix`specific the correct disk device for disko.
Your existing config should already have a `hardware-configuration.nix` and a functioning compliment of sops secrets and sops creation rules. Therefore, many of the steps presented by the script can be safely skipped. The applicable steps will be noted below.
If you haven't already, add a `newConfig` entry for the target host in `nix-config/nixos-installer/flake.nix`, passing in the required arguments as noted in the file comments.
## Installation Steps
### 0. VM setup (optional)
This is only relevant if you are _not_ installing the target host on bare metal.
- Disk size: a decent _minimum_ disk size without swap is 25GB to accommodate for multiple generations on a testing machine.
If you are using swap, remember that the space will come from the main disk you allocated for the VM so be sure to allocate enough _additional_ main disk space to accommodate your swap size needs.
For example, if you need 50GB of storage for you machine and you also specified a swapsize of 8GB in nix-config, then allocate 48GB for the VMs disk size.
- You _must_ set up the hypervisor firmware to use UEFI instead of BIOS or the VM will fail to boot into the minimal-configuration.
When creating the VM using virtmanager, you must select "Customize configuration before install" during step 5 of 5, and then change BIOS to UEFI on the next screen.
- For the CD/DVD-ROM source path select the custom iso file.
- Ensure the boot order is sane for automated reboots. For example, on VirtManager, set `VirtIO Disk 1` ahead of `SATA CDROM`, ensure both are checked, and also check `Enable boot menu` so that you can easily override the boot order on reboot if need be.
NOTE: If you encounter installation problems during reboot into the minimal-configuration, refer to [Troubleshooting](#troubleshooting) as there are a couple of different causes.
### 1. Initial boot
Boot the target machine into the NixOS ISO environment.
If necessary, note the IP address of the machine by running `ip a`.
### 2. Run the bootstrap script
On the source machine where nix-config already resides, run the following command from the root of `nix-confg`.
```bash
./scripts/bootstrap-nixos.sh -n [hostname] -d [destination]
```
Replace `[hostname]` with the name of the target host you are installing.
Replace `[destination]` with the location of the target machine.
Be sure to specify `--impermanence` if necessary. Use `--debug` if something goes wrong...
This is an example of running the script from `nix-config` base folder installing on a VM (`guppy`) with the `--debug` flag enabled:
```bash
./scripts/bootstrap-nixos.sh -n guppy -d=192.168.122.29 --debug
```
The script will give you several yes/no or no/yes questions. The questions are fairly self explanatory but we'll go through them here and make some notes that will be valuable depending on whether you are bootstrapping a new or existing host.
1. "Run nixos-anywhere installation?" default yes - This initiates installation of the minimal-config environment.
1. "Manually set luks encryption passphrase?" default no - if you are using LUKS, say "y" and enter a temporary password when prompted. Disko will use for setting up LUKS and you can change it when installation is complete.
2. "Generate a new hardware config for this host? Yes if your nix-config doesn't have an entry for this host." default no - Say yes only for new hosts that don't have a premade `hardware-configuration.nix`
3. "Has your system restarted and are you ready to continue? (no exits)" - This is important. Nixosanywhere, will report the target host as available prior to it being fully rebooted. Wait until the target host prints a log in prompt before saying yes.
2. "Generate host (ssh-based) age key?" default yes - usually only needed for new hosts
3. "Generate user age key?" default yes - usually only needed for new hosts
4. "Add ssh host fingerprints for git{lab,hub}?" default yes - this will setup the full nix-config accessing nix-secrets as an input during the next steps
5. "Do you want to copy your full nix-config and nix-secrets to $target_hostname?" default yes - copies the source of both repos from the source machine to the target machine, faster than cloning from github/lab
1. "Do you want to rebuild immediately?" default yes - builds the full config
6. "Do you want to commit and push the generated hardware-configuration.nix for $target_hostname to nix-config?" default yes - This will _only_ be presented if you said yes to question 1.2 and will push the file to your repo with an appropriate commit message.
Note: these questions are largely in place to allow subsequent running of the script when errors are encountered without being required to start from the very beginning again. For example, if you get all the way to step 5.1 and there was a problem with your final config for the target host that causes a build failure, you can fix the issue on your source host, rerun the script, skip through all of the questions until 5, and then pick up where you left off.
On completion, the script should end with a "Success!" message.
Depending on your host, the following post-install steps may not be required.
### 3. Post install steps for LUKS (optional)
#### Change LUKS2's passphrase if you entered a temporary passphrase during bootstrap
```bash
# when entering /path/to/dev/ you must specify the partition (e.g. /dev/nvmeon1p2)
# test the old passphrase
sudo cryptsetup --verbose open --test-passphrase /path/to/dev/
# change the passphrase
sudo cryptsetup luksChangeKey /path/to/dev/
# test the new passphrase
sudo cryptsetup --verbose open --test-passphrase /path/to/dev/
```
#### Enroll yubikeys for touch-based unlock
Enable yubikey support:
NOTE: This requires LUKS2 (use cryptsetup luksDump /path/to/dev/ to check)
```bash
sudo systemd-cryptenroll --fido2-device=auto /path/to/dev/
```
You will need to do it for each yubikey you want to use.
#### Update the unlock passphrase for secondary drive unlock
If you passed the `--luks-secondary-drive-labels` arg when running the bootstrap script, it automatically created a `/luks-secondary-unlock.key` file for you using the passphrase you specified during bootstrap.
If you used a temporary passphrase during bootstrap, you can update the secondary unlock key by running the following command and following the prompts.
```bash
cryptsetup luksChangeKey /luks-secondary-unlock.key
```
#### If you forgot to use the `--luks-secondary-drive-labels` arg during bootstrap but need to set it up
From - https://wiki.nixos.org/wiki/Full_Disk_Encryption#Unlocking_secondary_drives :
1. Create a keyfile for your secondary drive(s), store it safely and add it as a LUKS key:
```bash
# dd bs=512 count=4 if=/dev/random of=/luks-secondary-unlock.key iflag=fullblock
# chmod 400 /luks-secondary-unlock.key
```
You can specify your own name for `luks-secondary-unlock.key`
2. For each secondary device, run the following command and specify the respective device:
```bash
# cryptsetup luksAddKey /path/to/dev /luks-secondary-unlock.key
```
3. Create /etc/crypttab in configuration.nix using the following option (replacing UUID-OF-SDB with the actual UUID of /dev/sdb):
To list the UUIDs of the devices use: `sudo lsblk -o +name,mountpoint,uuid`
You need the UUID of the partition that the volume exists on, not the uuid of the volume itself
```nix
{
environment.etc.crypttab.text = ''
volumename UUID=UUID-OF-SDB /mykeyfile.key
''
}
```
example:
```nix
{
environment.etc.crypttab.text = ''
cryptextra UUID=569e2951-1957-4387-8b51-f445741b02b6 /luks-secondary-unlock.key
''
}
```
With this approach, the secondary drive is unlocked just before the boot process completes, without the need to enter its password.
The secondary drive will be unlocked and made available under /dev/mapper/cryptstorage for mounting.
### 4. Enable `backup` module (optional)
Enable the backup module in the target host's config file. For example:
```nix
# nix-config/hosts/nixos/guppy/default.nix
#--------------------
# ...
services.backup = {
enable = true;
# ...
};
#...
```
You will, of course, need to declare additional backup options for the module to function correctly.
### 5. Rebuild (optional)
If you did any of the steps from 3 through 5, you will need to rebuild for the changes to take effect. Run `just rebuild` from the `nix-config` directory on the new host.
### 6. Everything else (optional)
Here you should have a fully working system, but here are some common tasks you may need to do for a "daily-driver" machine:
- Recover any backup files needed
- .mozilla
- syncthing
- Manually set syncthing username/password
- Run any commonly used apps and perform
- firefox and initiate sync
- protonvpn
- Re-link signal-desktop
- Login to spotify
## Troubleshooting
### Rebooting a VM into the minimal-config environment hangs indefinitely on "booting in to hard disk..."
There are two know causes for this issue:
1. The VM __must__ be created with the hypervisor firmware set to UEFI instead of BIOS. You will likely have to re-create the VM as this can't be changed after the fact.
2. The `hardware-configuration.nix` file may not have the required virtual I/O kernel module. Depending on the VM device type you will need to add either `virtio_pci` or `virtio_scsi` to the list of `availableKernelModules` in the host's `hardware-configuration.nix`
For example:
```nix
# nix-config/hosts/nixos/guppy/hardware-configuration.nix
# -------------------
# ...
boot.initrd.availableKernelModules = [
"ahci"
"xhci_pci"
"virtio_pci"
"sr_mod"
"virtio_blk"
];
# ...
```
### Activation script snippet 'setupSecrets' failed - /run/secrets/keys/age: is a directory
This issue may be encountere when running the bootstrap script to update a host that had been previously installed with an older variant of nix-secrets where the age keys for all hosts were stored as "keys: age: [hostname]: [keydata]" where as now, because we're using host-specific secrets, the structure is "keys: age: [keydata]".
The failure will occur will occur near the end of the build output and will not display as an error in red.
```bash
...
sops-install-secrets: Imported /etc/ssh/ssh_host_ed25519_key as age key with fingerprint age1ee6shkrhqg0n84n3ksjays6h5klxv2xmhn5uksq9qvsxd079cvdql7tyk8
/nix/store/chvwxir82c2mf99961qyf9hfqjq76g02-sops-install-secrets-0.0.1/bin/sops-install-secrets: cannot request units to restart: read /run/secrets/keys/age: is a directory
Activation script snippet 'setupSecrets' failed (1)
Failed to run activate script
...
```
To resolve the issue, run `sudo rm -r /run/secrets/keys/age` on the target host and then rebuild.

62
nixos-installer/flake.lock generated Normal file
View File

@@ -0,0 +1,62 @@
{
"nodes": {
"disko": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1732645828,
"narHash": "sha256-+4U2I2653JvPFxcux837ulwYS864QvEueIljUkwytsk=",
"owner": "nix-community",
"repo": "disko",
"rev": "869ba3a87486289a4197b52a6c9e7222edf00b3e",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "disko",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1732238832,
"narHash": "sha256-sQxuJm8rHY20xq6Ah+GwIUkF95tWjGRd1X8xF+Pkk38=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8edf06bea5bcbee082df1b7369ff973b91618b8d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1732350895,
"narHash": "sha256-GcOQbOgmwlsRhpLGSwZJwLbo3pu9ochMETuRSS1xpz4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0c582677378f2d9ffcb01490af2f2c678dcb29d3",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.11",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"disko": "disko",
"nixpkgs": "nixpkgs_2"
}
}
},
"root": "root",
"version": 7
}

62
nixos-installer/flake.nix Normal file
View File

@@ -0,0 +1,62 @@
{
description = "Minimal NixOS configuration for bootstrapping systems";
inputs = {
# FIXME(starter): adjust nixos version for the minimal environment as desired.
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
disko.url = "github:nix-community/disko"; # Declarative partitioning and formatting
};
outputs =
{
self,
nixpkgs,
...
}@inputs:
let
inherit (self) outputs;
minimalSpecialArgs = {
inherit inputs outputs;
lib = nixpkgs.lib.extend (self: super: { custom = import ../lib { inherit (nixpkgs) lib; }; });
};
newConfig =
name: disk: swapSize:
(
let
diskSpecPath =
../hosts/common/disks/btrfs-disk.nix;
in
nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = minimalSpecialArgs;
modules = [
inputs.disko.nixosModules.disko
diskSpecPath
{
_module.args = {
inherit disk;
withSwap = swapSize > 0;
swapSize = builtins.toString swapSize;
};
}
./minimal-configuration.nix
../hosts/nixos/${name}/hardware-configuration.nix
{ networking.hostName = name; }
];
}
);
in
{
nixosConfigurations = {
# This should mimic what is specified in the respective `nix-config/hosts/[platform]/[hostname]/default.nix`
# Add entries for each host you will be bootstrapping
# host = newConfig "name" disk" "swapSize"
# Swap size is in GiB
Bellerophon = newConfig "Bellerophon" "/dev/nvme0n1" 16;
};
};
}

View File

@@ -0,0 +1,85 @@
{
inputs,
config,
lib,
pkgs,
...
}:
{
imports = lib.flatten [
(map lib.custom.relativeToRoot [
"modules/common/host-spec.nix"
"hosts/common/core/ssh.nix"
"hosts/common/users/primary"
"hosts/common/users/primary/nixos.nix"
"hosts/common/optional/minimal-user.nix"
])
];
hostSpec = {
isMinimal = lib.mkForce true;
hostName = "installer";
# FIXME(starter): Add your primary username or whatever user you want to use for installation
username = "user";
};
fileSystems."/boot".options = [ "umask=0077" ]; # Removes permissions and security warnings.
boot.loader.efi.canTouchEfiVariables = true;
boot.loader.systemd-boot = {
enable = true;
# we use Git for version control, so we don't need to keep too many generations.
configurationLimit = lib.mkDefault 3;
# pick the highest resolution for systemd-boot's console.
consoleMode = lib.mkDefault "max";
};
boot.initrd = {
systemd.enable = true;
systemd.emergencyAccess = true; # Don't need to enter password in emergency mode
};
boot.kernelParams = [
"systemd.setenv=SYSTEMD_SULOGIN_FORCE=1"
"systemd.show_status=true"
#"systemd.log_level=debug"
"systemd.log_target=console"
"systemd.journald.forward_to_console=1"
];
environment.systemPackages = builtins.attrValues {
inherit (pkgs)
wget
curl
rsync
git
;
};
networking = {
networkmanager.enable = true;
};
services = {
qemuGuest.enable = true;
openssh = {
enable = true;
ports = [ 22 ];
settings.PermitRootLogin = "yes";
authorizedKeysFiles = lib.mkForce [ "/etc/ssh/authorized_keys.d/%u" ];
};
};
nix = {
# registry and nixPath shouldn't be required here because flakes but removal results in warning spam on build
registry = lib.mapAttrs (_: value: { flake = value; }) inputs;
nixPath = lib.mapAttrsToList (key: value: "${key}=${value.to.path}") config.nix.registry;
settings = {
experimental-features = [
"nix-command"
"flakes"
];
warn-dirty = false;
};
};
system.stateVersion = "24.11";
}

52
overlays/default.nix Normal file
View File

@@ -0,0 +1,52 @@
#
# This file defines overlays/custom modifications to upstream packages
#
{inputs, ...}: let
# Adds my custom packages
additions = final: prev: (prev.lib.packagesFromDirectoryRecursive {
callPackage = prev.lib.callPackageWith final;
directory = ../pkgs/common;
});
linuxModifications = final: prev: prev.lib.mkIf final.stdenv.isLinux {};
modifications = final: prev: {
# example = prev.example.overrideAttrs (oldAttrs: let ... in {
# ...
# });
# flameshot = prev.flameshot.overrideAttrs {
# cmakeFlags = [
# (prev.lib.cmakeBool "USE_WAYLAND_GRIM" true)
# (prev.lib.cmakeBool "USE_WAYLAND_CLIPBOARD" true)
# ];
# };
overlays = [
inputs.nix4vscode.overlays.forVscode
];
};
stable-packages = final: _prev: {
stable = import inputs.nixpkgs-stable {
inherit (final) system;
config.allowUnfree = true;
# overlays = [
# ];
};
};
unstable-packages = final: _prev: {
unstable = import inputs.nixpkgs-unstable {
inherit (final) system;
config.allowUnfree = true;
# overlays = [
# ];
};
};
in {
default = final: prev:
(additions final prev)
// (modifications final prev)
// (linuxModifications final prev)
// (stable-packages final prev)
// (unstable-packages final prev);
}

View File

@@ -0,0 +1,46 @@
# NOTE(starter): This is a "bonus" custom package that has been left here because the
# inclusion of custom pkgs in `nix-config/flake.nix` was never designed to work with an
# empty `nix-config/pkgs/common` directory.
# If nothing else, it can serve as an example of how to package something similar.
{
lib,
stdenv,
fetchgit,
}:
let
pname = "cd-gitroot";
install_path = "share/zsh/${pname}";
in
stdenv.mkDerivation {
inherit pname;
version = "66f6ba7549b9973eb57bfbc188e29d2f73bf31bb";
src = fetchgit {
url = "https://github.com/mollifier/cd-gitroot";
hash = "sha256-pLdF8wbkA9mPI5cg8VPYAW7i3cWNJX3+lfAZ5cZPUgE=";
};
strictDeps = true;
dontBuild = true;
buildInputs = [ ];
installPhase = ''
install -m755 -D cd-gitroot.plugin.zsh --target-directory $out/${install_path}/
install -m755 -D cd-gitroot --target-directory $out/${install_path}/
install -m755 -D _cd-gitroot --target-directory $out/share/zsh/site-functions/
'';
meta = {
homepage = "https://github.com/mollifier/cd-gitroot";
license = lib.licenses.mit;
longDescription = ''
zsh plugin to change directory to git repository root directory.
You can add the following to your `programs.zsh.plugins` list:
```nix
programs.zsh.plugins = [
{
name = "${pname}";
src = "''${pkgs.${pname}}/${install_path}";
}
];
```
'';
maintainers = [ lib.maintainers.fidgetingbits ];
};
}

312
scripts/bootstrap-nixos.sh Executable file
View File

@@ -0,0 +1,312 @@
#!/usr/bin/env bash
set -euo pipefail
# Helpers library
# shellcheck disable=SC1091
source "$(dirname "${BASH_SOURCE[0]}")/helpers.sh"
# User variables
target_hostname=""
target_destination=""
target_user=${BOOTSTRAP_USER-$(whoami)} # Set BOOTSTRAP_ defaults in your shell.nix
ssh_port=${BOOTSTRAP_SSH_PORT-22}
ssh_key=${BOOTSTRAP_SSH_KEY-}
persist_dir=""
luks_passphrase="passphrase"
luks_secondary_drive_labels=""
nix_src_path="src/nix/" # path relative to /home/${target_user} where nix-config and nix-secrets are written in the users home
git_root=$(git rev-parse --show-toplevel)
nix_secrets_dir=${NIX_SECRETS_DIR:-"${git_root}"/../nix-secrets}
# Create a temp directory for generated host keys
temp=$(mktemp -d)
# Cleanup temporary directory on exit
function cleanup() {
rm -rf "$temp"
}
trap cleanup exit
# Copy data to the target machine
function sync() {
# $1 = user, $2 = source, $3 = destination
rsync -av --filter=':- .gitignore' -e "ssh -oControlMaster=no -l $1 -oport=${ssh_port}" "$2" "$1@${target_destination}:${nix_src_path}"
}
# Usage function
function help_and_exit() {
echo
echo "Remotely installs NixOS on a target machine using this nix-config."
echo
echo "USAGE: $0 -n <target_hostname> -d <target_destination> -k <ssh_key> [OPTIONS]"
echo
echo "ARGS:"
echo " -n <target_hostname> specify target_hostname of the target host to deploy the nixos config on."
echo " -d <target_destination> specify ip or domain to the target host."
echo " -k <ssh_key> specify the full path to the ssh_key you'll use for remote access to the"
echo " target during install process."
echo " Example: -k /home/${target_user}/.ssh/my_ssh_key"
echo
echo "OPTIONS:"
echo " -u <target_user> specify target_user with sudo access. nix-config will be cloned to their home."
echo " Default='${target_user}'."
echo " --port <ssh_port> specify the ssh port to use for remote access. Default=${ssh_port}."
echo ' --luks-secondary-drive-labels <drives> specify the luks device names (as declared with "disko.devices.disk.*.content.luks.name" in host/common/disks/*.nix) separated by commas.'
echo ' Example: --luks-secondary-drive-labels "cryptprimary,cryptextra"'
echo " --impermanence Use this flag if the target machine has impermanence enabled. WARNING: Assumes /persist path."
echo " --debug Enable debug mode."
echo " -h | --help Print this help."
exit 0
}
# Handle command-line arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-n)
shift
target_hostname=$1
;;
-d)
shift
target_destination=$1
;;
-u)
shift
target_user=$1
;;
-k)
shift
ssh_key=$1
;;
--luks-secondary-drive-labels)
shift
luks_secondary_drive_labels=$1
;;
--port)
shift
ssh_port=$1
;;
--temp-override)
shift
temp=$1
;;
--impermanence)
persist_dir="/persist"
;;
--debug)
set -x
;;
-h | --help) help_and_exit ;;
*)
red "ERROR: Invalid option detected."
help_and_exit
;;
esac
shift
done
if [ -z "$target_hostname" ] || [ -z "$target_destination" ] || [ -z "$ssh_key" ]; then
red "ERROR: -n, -d, and -k are all required"
echo
help_and_exit
fi
# SSH commands
ssh_cmd="ssh \
-oControlPath=none \
-oport=${ssh_port} \
-oForwardAgent=yes \
-oStrictHostKeyChecking=no \
-oUserKnownHostsFile=/dev/null \
-i $ssh_key \
-t $target_user@$target_destination"
# shellcheck disable=SC2001
ssh_root_cmd=$(echo "$ssh_cmd" | sed "s|${target_user}@|root@|") # uses @ in the sed switch to avoid it triggering on the $ssh_key value
scp_cmd="scp -oControlPath=none -oport=${ssh_port} -oStrictHostKeyChecking=no -i $ssh_key"
git_root=$(git rev-parse --show-toplevel)
# Setup minimal environment for nixos-anywhere and run it
generated_hardware_config=0
function nixos_anywhere() {
# Clear the known keys, since they should be newly generated for the iso
green "Wiping known_hosts of $target_destination"
sed -i "/$target_hostname/d; /$target_destination/d" ~/.ssh/known_hosts
green "Installing NixOS on remote host $target_hostname at $target_destination"
###
# nixos-anywhere extra-files generation
###
green "Preparing a new ssh_host_ed25519_key pair for $target_hostname."
# Create the directory where sshd expects to find the host keys
install -d -m755 "$temp/$persist_dir/etc/ssh"
# Generate host ssh key pair without a passphrase
ssh-keygen -t ed25519 -f "$temp/$persist_dir/etc/ssh/ssh_host_ed25519_key" -C "$target_user"@"$target_hostname" -N ""
# Set the correct permissions so sshd will accept the key
chmod 600 "$temp/$persist_dir/etc/ssh/ssh_host_ed25519_key"
green "Adding ssh host fingerprint at $target_destination to ~/.ssh/known_hosts"
# This will fail if we already know the host, but that's fine
ssh-keyscan -p "$ssh_port" "$target_destination" | grep -v '^#' >>~/.ssh/known_hosts || true
###
# nixos-anywhere installation
###
cd nixos-installer
# when using luks, disko expects a passphrase on /tmp/disko-password, so we set it for now and will update the passphrase later
if no_or_yes "Manually set luks encryption passphrase? (Default: \"$luks_passphrase\")"; then
blue "Enter your luks encryption passphrase:"
read -rs luks_passphrase
$ssh_root_cmd "/bin/sh -c 'echo $luks_passphrase > /tmp/disko-password'"
else
green "Using '$luks_passphrase' as the luks encryption passphrase. Change after installation."
$ssh_root_cmd "/bin/sh -c 'echo $luks_passphrase > /tmp/disko-password'"
fi
# this will run if luks_secondary_drive_labels cli argument was set, regardless of whether the luks_passphrase is default or not
if [ -n "${luks_secondary_drive_labels}" ]; then
luks_setup_secondary_drive_decryption
fi
# If you are rebuilding a machine without any hardware changes, this is likely unneeded or even possibly disruptive
if no_or_yes "Generate a new hardware config for this host? Yes if your nix-config doesn't have an entry for this host."; then
green "Generating hardware-configuration.nix on $target_hostname and adding it to the local nix-config."
$ssh_root_cmd "nixos-generate-config --no-filesystems --root /mnt"
$scp_cmd root@"$target_destination":/mnt/etc/nixos/hardware-configuration.nix \
"${git_root}"/hosts/nixos/"$target_hostname"/hardware-configuration.nix
generated_hardware_config=1
fi
# --extra-files here picks up the ssh host key we generated earlier and puts it onto the target machine
SHELL=/bin/sh nix run github:nix-community/nixos-anywhere -- \
--ssh-port "$ssh_port" \
--post-kexec-ssh-port "$ssh_port" \
--extra-files "$temp" \
--flake .#"$target_hostname" \
root@"$target_destination"
if ! yes_or_no "Has your system restarted and are you ready to continue? (no exits)"; then
exit 0
fi
green "Adding $target_destination's ssh host fingerprint to ~/.ssh/known_hosts"
ssh-keyscan -p "$ssh_port" "$target_destination" | grep -v '^#' >>~/.ssh/known_hosts || true
if [ -n "$persist_dir" ]; then
$ssh_root_cmd "cp /etc/machine-id $persist_dir/etc/machine-id || true"
$ssh_root_cmd "cp -R /etc/ssh/ $persist_dir/etc/ssh/ || true"
fi
cd - >/dev/null
}
function sops_generate_host_age_key() {
green "Generating an age key based on the new ssh_host_ed25519_key"
# Get the SSH key
target_key=$(ssh-keyscan -p "$ssh_port" -t ssh-ed25519 "$target_destination" 2>&1 | grep ssh-ed25519 | cut -f2- -d" ") || {
red "Failed to get ssh key. Host down or maybe SSH port now changed?"
exit 1
}
host_age_key=$(echo "$target_key" | ssh-to-age)
if grep -qv '^age1' <<<"$host_age_key"; then
red "The result from generated age key does not match the expected format."
yellow "Result: $host_age_key"
yellow "Expected format: age10000000000000000000000000000000000000000000000000000000000"
exit 1
fi
green "Updating nix-secrets/.sops.yaml"
sops_update_age_key "hosts" "$target_hostname" "$host_age_key"
}
function luks_setup_secondary_drive_decryption() {
green "Generating /luks-secondary-unlock.key"
local key=${persist_dir}/luks-secondary-unlock.key
$ssh_root_cmd "dd bs=512 count=4 if=/dev/random of=$key iflag=fullblock && chmod 400 $key"
green "Cryptsetup luksAddKey will now be used to add /luks-secondary-unlock.key for the specified secondary drive names."
readarray -td, drivenames <<<"$luks_secondary_drive_labels"
for name in "${drivenames[@]}"; do
device_path=$($ssh_root_cmd -q "cryptsetup status \"$name\" | awk \'/device:/ {print \$2}\'")
$ssh_root_cmd "echo \"$luks_passphrase\" | cryptsetup luksAddKey $device_path /luks-secondary-unlock.key"
done
}
# Validate required options
# FIXME(bootstrap): The ssh key and destination aren't required if only rekeying, so could be moved into specific sections?
if [ -z "${target_hostname}" ] || [ -z "${target_destination}" ] || [ -z "${ssh_key}" ]; then
red "ERROR: -n, -d, and -k are all required"
echo
help_and_exit
fi
if yes_or_no "Run nixos-anywhere installation?"; then
nixos_anywhere
fi
updated_age_keys=0
if yes_or_no "Generate host (ssh-based) age key?"; then
sops_generate_host_age_key
updated_age_keys=1
fi
if yes_or_no "Generate user age key?"; then
# This may end up creating the host.yaml file, so add creation rules in advance
sops_setup_user_age_key "$target_user" "$target_hostname"
# We need to add the new file before we rekey later
cd "$nix_secrets_dir"
git add sops/"${target_hostname}".yaml
cd - >/dev/null
updated_age_keys=1
fi
if [[ $updated_age_keys == 1 ]]; then
# If the age generation commands added previously unseen keys (and associated anchors) we want to add those
# to some creation rules, namely <host>.yaml and shared.yaml
sops_add_creation_rules "${target_user}" "${target_hostname}"
# Since we may update the sops.yaml file twice above, only rekey once at the end
just rekey
green "Updating flake input to pick up new .sops.yaml"
nix flake update nix-secrets
fi
if yes_or_no "Do you want to copy your full nix-config and nix-secrets to $target_hostname?"; then
green "Adding ssh host fingerprint at $target_destination to ~/.ssh/known_hosts"
ssh-keyscan -p "$ssh_port" "$target_destination" 2>/dev/null | grep -v '^#' >>~/.ssh/known_hosts || true
green "Copying full nix-config to $target_hostname"
sync "$target_user" "${git_root}"/../nix-config
green "Copying full nix-secrets to $target_hostname"
sync "$target_user" "${nix_secrets_dir}"
# FIXME(bootstrap): Add some sort of key access from the target to download the config (if it's a cloud system)
if yes_or_no "Do you want to rebuild immediately?"; then
green "Rebuilding nix-config on $target_hostname"
$ssh_cmd "cd ${nix_src_path}nix-config && sudo nixos-rebuild --impure --show-trace --flake .#$target_hostname switch"
fi
else
echo
green "NixOS was successfully installed!"
echo "Post-install config build instructions:"
echo "To copy nix-config from this machine to the $target_hostname, run the following command"
echo "just sync $target_user $target_destination"
echo "To rebuild, sign into $target_hostname and run the following command"
echo "cd nix-config"
echo "sudo nixos-rebuild --show-trace --flake .#$target_hostname switch"
echo
fi
if [[ $generated_hardware_config == 1 ]]; then
if yes_or_no "Do you want to commit and push the generated hardware-configuration.nix for $target_hostname to nix-config?"; then
(pre-commit run --all-files 2>/dev/null || true) &&
git add "$git_root/hosts/$target_hostname/hardware-configuration.nix" &&
(git commit -m "feat: hardware-configuration.nix for $target_hostname" || true) &&
git push
fi
fi
green "Success!"
reen "Success!"

31
scripts/check-sops.sh Executable file
View File

@@ -0,0 +1,31 @@
#!/usr/bin/env bash
os=$(uname -s)
if [ "$os" == "Darwin" ]; then
sops_running=$(launchctl list | rg sops)
if [[ -z $sops_running ]]; then
echo "ERROR: sops-nix is not running"
exit 1
fi
else # If the sops-nix service wasn't started at all, we don't need to check if it failed
sops_running=$(journalctl --no-pager --no-hostname --since "10 minutes ago" | rg "Starting sops-nix activation")
if [ -z "$sops_running" ]; then
exit 0
fi
# Also this is HM specific atm, need a way to test the NixOS version too
sops_result=$(journalctl --no-pager --no-hostname --since "10 minutes ago" |
tac |
awk '!flag; /Starting sops-nix activation/{flag = 1};' |
tac |
rg sops)
# If we don't have "Finished sops-nix activation." in the logs, then we failed
if [[ ! $sops_result =~ "Finished sops-nix activation" ]]; then
echo "ERROR: sops-nix failed to activate"
echo "ERROR: $sops_result"
exit 1
fi
fi
exit 0

179
scripts/helpers.sh Normal file
View File

@@ -0,0 +1,179 @@
#!/usr/bin/env bash
set -eo pipefail
### UX helpers
function red() {
echo -e "\x1B[31m[!] $1 \x1B[0m"
if [ -n "${2-}" ]; then
echo -e "\x1B[31m[!] $($2) \x1B[0m"
fi
}
function green() {
echo -e "\x1B[32m[+] $1 \x1B[0m"
if [ -n "${2-}" ]; then
echo -e "\x1B[32m[+] $($2) \x1B[0m"
fi
}
function blue() {
echo -e "\x1B[34m[*] $1 \x1B[0m"
if [ -n "${2-}" ]; then
echo -e "\x1B[34m[*] $($2) \x1B[0m"
fi
}
function yellow() {
echo -e "\x1B[33m[*] $1 \x1B[0m"
if [ -n "${2-}" ]; then
echo -e "\x1B[33m[*] $($2) \x1B[0m"
fi
}
# Ask yes or no, with yes being the default
function yes_or_no() {
echo -en "\x1B[34m[?] $* [y/n] (default: y): \x1B[0m"
while true; do
read -rp "" yn
yn=${yn:-y}
case $yn in
[Yy]*) return 0 ;;
[Nn]*) return 1 ;;
esac
done
}
# Ask yes or no, with no being the default
function no_or_yes() {
echo -en "\x1B[34m[?] $* [y/n] (default: n): \x1B[0m"
while true; do
read -rp "" yn
yn=${yn:-n}
case $yn in
[Yy]*) return 0 ;;
[Nn]*) return 1 ;;
esac
done
}
### SOPS helpers
nix_secrets_dir=${NIX_SECRETS_DIR:-"$(dirname "${BASH_SOURCE[0]}")/../../nix-secrets"}
SOPS_FILE="${nix_secrets_dir}/.sops.yaml"
# Updates the .sops.yaml file with a new host or user age key.
function sops_update_age_key() {
field="$1"
keyname="$2"
key="$3"
if [ ! "$field" == "hosts" ] && [ ! "$field" == "users" ]; then
red "Invalid field passed to sops_update_age_key. Must be either 'hosts' or 'users'."
exit 1
fi
if [[ -n $(yq ".keys.${field}[] | select(anchor == \"$keyname\")" "${SOPS_FILE}") ]]; then
green "Updating existing ${keyname} key"
yq -i "(.keys.${field}[] | select(anchor == \"$keyname\")) = \"$key\"" "$SOPS_FILE"
else
green "Adding new ${keyname} key"
yq -i ".keys.$field += [\"$key\"] | .keys.${field}[-1] anchor = \"$keyname\"" "$SOPS_FILE"
fi
}
# Adds the user and host to the shared.yaml creation rules
function sops_add_shared_creation_rules() {
u="\"$1_$2\"" # quoted user_host for yaml
h="\"$2\"" # quoted hostname for yaml
shared_selector='.creation_rules[] | select(.path_regex == "shared\.yaml$")'
if [[ -n $(yq "$shared_selector" "${SOPS_FILE}") ]]; then
echo "BEFORE"
cat "${SOPS_FILE}"
if [[ -z $(yq "$shared_selector.key_groups[].age[] | select(alias == $h)" "${SOPS_FILE}") ]]; then
green "Adding $u and $h to shared.yaml rule"
# NOTE: Split on purpose to avoid weird file corruption
yq -i "($shared_selector).key_groups[].age += [$u, $h]" "$SOPS_FILE"
yq -i "($shared_selector).key_groups[].age[-2] alias = $u" "$SOPS_FILE"
yq -i "($shared_selector).key_groups[].age[-1] alias = $h" "$SOPS_FILE"
fi
else
red "shared.yaml rule not found"
fi
}
# Adds the user and host to the host.yaml creation rules
function sops_add_host_creation_rules() {
host="$2" # hostname for selector
h="\"$2\"" # quoted hostname for yaml
u="\"$1_$2\"" # quoted user_host for yaml
w="\"$(whoami)_$(hostname)\"" # quoted whoami_hostname for yaml
n="\"$(hostname)\"" # quoted hostname for yaml
host_selector=".creation_rules[] | select(.path_regex | contains(\"${host}\.yaml\"))"
if [[ -z $(yq "$host_selector" "${SOPS_FILE}") ]]; then
green "Adding new host file creation rule"
yq -i ".creation_rules += {\"path_regex\": \"${host}\\.yaml$\", \"key_groups\": [{\"age\": [$u, $h]}]}" "$SOPS_FILE"
# Add aliases one by one
yq -i "($host_selector).key_groups[].age[0] alias = $u" "$SOPS_FILE"
yq -i "($host_selector).key_groups[].age[1] alias = $h" "$SOPS_FILE"
yq -i "($host_selector).key_groups[].age[2] alias = $w" "$SOPS_FILE"
yq -i "($host_selector).key_groups[].age[3] alias = $n" "$SOPS_FILE"
fi
}
# Adds the user and host to the shared.yaml and host.yaml creation rules
function sops_add_creation_rules() {
user="$1"
host="$2"
sops_add_shared_creation_rules "$user" "$host"
sops_add_host_creation_rules "$user" "$host"
}
age_secret_key=""
# Generate a user age key, update the .sops.yaml entries, and return the key in age_secret_key
# args: user, hostname
function sops_generate_user_age_key() {
target_user="$1"
target_hostname="$2"
key_name="${target_user}_${target_hostname}"
green "Age key does not exist. Generating."
user_age_key=$(age-keygen)
readarray -t entries <<<"$user_age_key"
age_secret_key=${entries[2]}
public_key=$(echo "${entries[1]}" | rg key: | cut -f2 -d: | xargs)
green "Generated age key for ${key_name}"
# Place the anchors into .sops.yaml so other commands can reference them
sops_update_age_key "users" "$key_name" "$public_key"
sops_add_creation_rules "${target_user}" "${target_hostname}"
# "return" key so it can be used by caller
export age_secret_key
}
function sops_setup_user_age_key() {
target_user="$1"
target_hostname="$2"
secret_file="${nix_secrets_dir}/sops/${target_hostname}.yaml"
config="${nix_secrets_dir}/.sops.yaml"
# If the secret file doesn't exist, it means we're generating a new user key as well
if [ ! -f "$secret_file" ]; then
green "Host secret file does not exist. Creating $secret_file"
sops_generate_user_age_key "${target_user}" "${target_hostname}"
mkdir -p "$(dirname "$secret_file")"
echo "{}" >"$secret_file"
sops --config "$config" -e "$secret_file" >"$secret_file.enc"
mv "$secret_file.enc" "$secret_file"
fi
if ! sops --config "$config" -d --extract '["keys]["age"]' "$secret_file" >/dev/null 2>&1; then
if [ -z "$age_secret_key" ]; then
sops_generate_user_age_key "${target_user}" "${target_hostname}"
fi
# shellcheck disable=SC2116,SC2086
sops --config "$config" --set "$(echo '["keys"]["age"] "'$age_secret_key'"')" "$secret_file"
else
green "Age key already exists for ${target_hostname}"
fi
}

106
scripts/rebuild.sh Executable file
View File

@@ -0,0 +1,106 @@
#!/usr/bin/env bash
# shellcheck disable=SC2086
#
# This script is used to rebuild the system configuration for the current host.
#
# SC2086 is ignored because we purposefully pass some values as a set of arguments, so we want the splitting to happen
function red() {
echo -e "\x1B[31m[!] $1 \x1B[0m"
if [ -n "${2-}" ]; then
echo -e "\x1B[31m[!] $($2) \x1B[0m"
fi
}
function green() {
echo -e "\x1B[32m[+] $1 \x1B[0m"
if [ -n "${2-}" ]; then
echo -e "\x1B[32m[+] $($2) \x1B[0m"
fi
}
function yellow() {
echo -e "\x1B[33m[*] $1 \x1B[0m"
if [ -n "${2-}" ]; then
echo -e "\x1B[33m[*] $($2) \x1B[0m"
fi
}
switch_args="--show-trace --impure --flake "
if [[ -n $1 && $1 == "trace" ]]; then
switch_args="$switch_args --show-trace "
elif [[ -n $1 ]]; then
HOST=$1
else
HOST=$(hostname)
fi
switch_args="$switch_args .#$HOST switch"
os=$(uname -s)
if [ "$os" == "Darwin" ]; then
# FIXME: This might not have to be darwin specific
# FIXME: This will break if HM tries to create the file. We should use environment variables instead
mkdir -p ~/.config/nix || true
CONF=~/.config/nix/nix.conf
if [ ! -f $CONF ]; then
# Enable nix-command and flakes to bootstrap
cat <<-EOF >$CONF
experimental-features = nix-command flakes
EOF
fi
# Do some darwin pre-installation for bootstrapping
if ! which git &>/dev/null; then
echo "Installing xcode tools"
xcode-select --install
fi
# https://docs.brew.sh/Installation
if [ ! -e /opt/homebrew/bin/brew ]; then
echo "Installing rosetta"
# This is required for emulation of x86_64 binaries, so let's just
# assume if they didn't install brew yet, they need this
softwareupdate --install-rosetta --agree-to-license
echo "Installing homebrew"
export NONINTERACTIVE=1
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
fi
green "====== REBUILD ======"
# Test if there's no darwin-rebuild, then use nix build and then run it
if ! which darwin-rebuild &>/dev/null; then
nix build --show-trace .#darwinConfigurations."$HOST".system
./result/sw/bin/darwin-rebuild $switch_args
else
echo $switch_args
darwin-rebuild $switch_args
fi
else
green "====== REBUILD ======"
if command -v nh &>/dev/null; then
REPO_PATH=$(pwd)
export REPO_PATH
nh os switch . -- --impure --show-trace
else
sudo nixos-rebuild $switch_args
fi
fi
# shellcheck disable=SC2181
if [ $? -eq 0 ]; then
green "====== POST-REBUILD ======"
green "Rebuilt successfully"
# Check if there are any pending changes that would affect the build succeeding.
if git diff --exit-code >/dev/null && git diff --staged --exit-code >/dev/null; then
# Check if the current commit has a buildable tag
if git tag --points-at HEAD | grep -q buildable; then
yellow "Current commit is already tagged as buildable"
else
git tag buildable-"$(date +%Y%m%d%H%M%S)" -m ''
green "Tagged current commit as buildable"
fi
else
yellow "WARN: There are pending changes that would affect the build succeeding. Commit them before tagging"
fi
fi

9
scripts/system-install.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/usr/bin/env bash
if [ -e "$1" ]; then
HOST=$1
else
HOST=$(hostname)
fi
sudo nixos-rebuild --flake .#"$HOST" install

45
shell.nix Normal file
View File

@@ -0,0 +1,45 @@
# Shell for bootstrapping flake-enabled nix and other tooling
{
pkgs ?
# If pkgs is not defined, instantiate nixpkgs from locked commit
let
lock = (builtins.fromJSON (builtins.readFile ./flake.lock)).nodes.nixpkgs.locked;
nixpkgs = fetchTarball {
url = "https://github.com/nixos/nixpkgs/archive/${lock.rev}.tar.gz";
sha256 = lock.narHash;
};
in
import nixpkgs { overlays = [ ]; },
...
}:
{
default = pkgs.mkShell {
NIX_CONFIG = "extra-experimental-features = nix-command flakes";
BOOTSTRAP_USER = "panotaka";
BOOTSTRAP_SSH_PORT = "22";
BOOTSTRAP_SSH_KEY = "~/.ssh/id_manu";
nativeBuildInputs = builtins.attrValues {
inherit (pkgs)
# NOTE(starter): add any packages you want available in the shell when accessing the parent directory.
# These will be installed regardless of what was installed specific for the host or home configs
nix
home-manager
nil
alejandra
nh
git
just
pre-commit
deadnix
sops
yq-go # jq for yaml, used for build scripts
bats # for bash testing
age # for bootstrap script
ssh-to-age # for bootstrap script
;
};
};
}

View File

@@ -0,0 +1,3 @@
# created: 2025-02-07T09:27:05+08:00
# public key: age1uq2uymv63r4h5r47vkuhjz3hcz9rv48df8u5jt8zeejgt2wzme3qz3se8y
AGE-SECRET-KEY-1Z7AENV0K5VRCV87EDK2XYE4ZWJ3G39W7J3TEAWQSX2F46NGWL4FQ6QKGLE

11
tests/fixtures/nix-secrets/sops.yaml vendored Normal file
View File

@@ -0,0 +1,11 @@
keys:
users:
- &alice_testbox USER_KEY_1
hosts:
- &testbox age1v8v79wlsjnwvxaa6eulqx3zft0m5srj7etgk4v3rg80j42uzecxs26gaxz
creation_rules:
- path_regex: shared\.yaml$
key_groups:
- age:
- *alice_testbox
- *testbox

View File

@@ -0,0 +1,9 @@
setup() {
TEST_TEMP="$(mktemp -d)"
FIXTURES_DIR="$(dirname "$BATS_TEST_DIRNAME")/tests/fixtures"
export TEST_TEMP
export FIXTURES_DIR
}
teardown() {
rm -rf "$TEST_TEMP"
}

102
tests/sops.bats Normal file
View File

@@ -0,0 +1,102 @@
AGE_TEST_KEY_1="age1v8v79wlsjnwvxaa6eulqx3zft0m5srj7etgk4v3rg80j42uzecxs26gaxz"
AGE_TEST_KEY_2="age1zmplxr8x2h3tk4fd3zkleyspa7vtnyz5pyrj7zlf5vsl3fquhqvsp8n4k0"
AGE_TEST_KEY_3="age1zrjsjhsuwhqkdn2psjpukrsgjh5qls9023gructewn9skz4ya9gskncgmq"
AGE_TEST_KEY_4="age1e4zy6wcl0a8teaudtmsujkuupf56vkqdul0gljlssdqftrx3uphqqfx8p7"
# This key has a real associated private key in the fixtures
AGE_STATIC_HOST_KEY="age1uq2uymv63r4h5r47vkuhjz3hcz9rv48df8u5jt8zeejgt2wzme3qz3se8y"
setup_sops() {
load 'helpers/test_helper'
setup
mkdir -p "$TEST_TEMP"
cp -R "$FIXTURES_DIR"/nix-secrets/*.yaml "$TEST_TEMP"
mv "$TEST_TEMP/sops.yaml" "$TEST_TEMP/.sops.yaml"
NIX_SECRETS_DIR="$TEST_TEMP"
export NIX_SECRETS_DIR
# shellcheck disable=SC1091
source "$BATS_TEST_DIRNAME/../scripts/helpers.sh"
}
@test "add sops user anchor" {
setup_sops
sops_update_age_key users alice_testbox "${AGE_TEST_KEY_2}"
run grep -c "&alice_testbox" "$NIX_SECRETS_DIR"/.sops.yaml
[ "$status" -eq 0 ]
[ "$output" = "1" ]
run grep "${AGE_TEST_KEY_2}" "$NIX_SECRETS_DIR"/.sops.yaml
[ "$status" -eq 0 ]
teardown
}
@test "add sops host anchor" {
setup_sops
sops_update_age_key hosts testbox "${AGE_TEST_KEY_1}"
run grep -c "&testbox" "$NIX_SECRETS_DIR"/.sops.yaml
[ "$status" -eq 0 ]
[ "$output" = "1" ]
run grep "${AGE_TEST_KEY_1}" "$NIX_SECRETS_DIR"/.sops.yaml
[ "$status" -eq 0 ]
teardown
}
@test "update shared creation rules" {
setup_sops
sops_update_age_key users bob_deadbeef "${AGE_TEST_KEY_3}"
sops_update_age_key hosts deadbeef "${AGE_TEST_KEY_4}"
sops_add_shared_creation_rules bob deadbeef
yq '.creation_rules' "$NIX_SECRETS_DIR"/.sops.yaml >"$TEST_TEMP/creation_rules"
run grep "bob" "$TEST_TEMP/creation_rules"
[ "$status" -eq 0 ]
run grep "deadbeef" "$TEST_TEMP/creation_rules"
[ "$status" -eq 0 ]
teardown
}
@test "add host creation rules to sops" {
setup_sops
sops_update_age_key users bob_deadbeef "${AGE_TEST_KEY_1}"
sops_update_age_key hosts deadbeef "${AGE_TEST_KEY_2}"
sops_update_age_key users "$(whoami)_$(hostname)" "${AGE_STATIC_HOST_KEY}"
sops_update_age_key hosts "$(hostname)" "${AGE_STATIC_HOST_KEY}"
sops_add_host_creation_rules bob deadbeef
yq '.creation_rules' "$NIX_SECRETS_DIR"/.sops.yaml >"$TEST_TEMP/creation_rules"
run grep "bob" "$TEST_TEMP/creation_rules"
[ "$status" -eq 0 ]
run grep "deadbeef" "$TEST_TEMP/creation_rules"
[ "$status" -eq 0 ]
teardown
}
@test "add host.yaml file" {
setup_sops
sops_update_age_key users bob_deadbeef "${AGE_TEST_KEY_1}"
sops_update_age_key hosts deadbeef "${AGE_TEST_KEY_2}"
sops_update_age_key users "$(whoami)_$(hostname)" "${AGE_STATIC_HOST_KEY}"
sops_update_age_key hosts "$(hostname)" "${AGE_STATIC_HOST_KEY}"
sops_add_host_creation_rules bob deadbeef
# Create a new <host>.yaml file and verify it holds the correct entry
export SOPS_AGE_KEY_FILE="$BATS_TEST_DIRNAME/fixtures/nix-secrets/age_key.txt"
run sops_setup_user_age_key "deadbeef" "bob"
[ "$status" -eq 0 ]
teardown
}