Compare commits

..

No commits in common. "master" and "refactor" have entirely different histories.

70 changed files with 682 additions and 3214 deletions

13
.gitignore vendored
View file

@ -1,13 +1,4 @@
# Nix build outputs result/
result result
result-*
.direnv/ .direnv/
oci-trantor/ .pre-commit-config.yaml
tailscale-tailnet/
cloudflare-baduhaidev
# Personal notes and temporary files
todo.md
notes.md
scratch/
tmp/

View file

@ -1,48 +0,0 @@
{ inputs, self, ... }:
{
flake.deploy = {
remoteBuild = true;
nodes = {
alexandria = {
hostname = "alexandria";
profiles.system = {
sshUser = "user";
path = inputs.deploy-rs.lib.x86_64-linux.activate.nixos self.nixosConfigurations.alexandria;
user = "root";
};
};
trantor = {
hostname = "trantor";
profiles.system = {
sshUser = "user";
path = inputs.deploy-rs.lib.aarch64-linux.activate.nixos self.nixosConfigurations.trantor;
user = "root";
};
};
io = {
hostname = "io";
profiles = {
system = {
sshUser = "user";
path = inputs.deploy-rs.lib.x86_64-linux.activate.nixos self.nixosConfigurations.io;
user = "root";
remoteBuild = false;
};
user = {
sshUser = "user";
path = inputs.deploy-rs.lib.x86_64-linux.activate.home-manager self.homeConfigurations."user@io";
user = "user";
remoteBuild = false;
};
};
};
};
};
perSystem =
{ system, ... }:
{
checks = inputs.deploy-rs.lib.${system}.deployChecks self.deploy;
};
}

View file

@ -1,13 +1,11 @@
{ inputs, ... }: { ... }:
{ {
perSystem = perSystem =
{ pkgs, system, ... }: { pkgs, ... }:
{ {
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
packages = with pkgs; [ packages = with pkgs; [
inputs.agenix.packages.${system}.default
deploy-rs
nil nil
nixfmt-rfc-style nixfmt-rfc-style
]; ];

1132
flake.lock generated

File diff suppressed because it is too large Load diff

View file

@ -2,57 +2,40 @@
description = "My nix hosts"; description = "My nix hosts";
inputs = { inputs = {
flake-parts.url = "github:hercules-ci/flake-parts";
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-25.05"; nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-25.05";
flake-parts.url = "github:hercules-ci/flake-parts";
home-manager = { home-manager = {
url = "github:nix-community/home-manager/master"; url = "github:nix-community/home-manager/master";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
home-manager-stable = {
url = "github:nix-community/home-manager/release-25.05";
inputs.nixpkgs.follows = "nixpkgs-stable";
};
agenix = { agenix = {
url = "github:ryantm/agenix"; url = "github:ryantm/agenix";
inputs.nixpkgs.follows = "nixpkgs-stable"; inputs.nixpkgs.follows = "nixpkgs-stable";
}; };
disko.url = "github:nix-community/disko"; disko = {
url = "github:nix-community/disko?ref=v1.11.0";
noctalia = { inputs.nixpkgs.follows = "nixpkgs-stable";
url = "github:noctalia-dev/noctalia-shell";
inputs.nixpkgs.follows = "nixpkgs";
}; };
stylix.url = "github:danth/stylix"; dms = {
url = "github:AvengeMedia/DankMaterialShell";
inputs.nixpkgs.follows = "nixpkgs";
};
nixos-cli.url = "github:nix-community/nixos-cli"; nixos-cli.url = "github:nix-community/nixos-cli";
nix-flatpak.url = "github:gmodena/nix-flatpak/main"; nix-flatpak.url = "github:gmodena/nix-flatpak/main";
zen-browser.url = "github:0xc000022070/zen-browser-flake";
impermanence.url = "github:nix-community/impermanence"; impermanence.url = "github:nix-community/impermanence";
deploy-rs.url = "github:serokell/deploy-rs";
niri-flake.url = "github:sodiboo/niri-flake";
niri.url = "github:baduhai/niri/auto-center-when-space-available";
nix-index-database = {
url = "github:nix-community/nix-index-database";
inputs.nixpkgs.follows = "nixpkgs";
};
terranix = {
url = "github:terranix/terranix";
inputs.nixpkgs.follows = "nixpkgs";
};
nix-ai-tools.url = "github:numtide/nix-ai-tools";
vicinae.url = "github:vicinaehq/vicinae";
}; };
outputs = outputs =
@ -64,14 +47,11 @@
]; ];
imports = [ imports = [
./deploy.nix
./devShells.nix ./devShells.nix
./homeConfigurations.nix ./homeConfigurations.nix
./nixosConfigurations.nix ./nixosConfigurations.nix
./nixosModules.nix
./overlays.nix ./overlays.nix
./packages.nix ./packages.nix
./terranixConfigurations.nix
]; ];
}; };
} }

View file

@ -1,41 +1,33 @@
{ inputs, ... }: { inputs, ... }:
let let
lib = inputs.nixpkgs.lib; lib = inputs.nixpkgs.lib;
utils = import ./utils.nix { inherit inputs lib; }; utils = import ./utils.nix { inherit inputs lib; };
inherit (utils) mkHome; inherit (utils) mkUser;
in in
{ {
flake.homeConfigurations = { flake.homeConfigurations = {
"user@rotterdam" = mkHome { "user@rotterdam" = mkUser {
username = "user"; username = "user";
hostname = "rotterdam";
tags = [ tags = [
"desktop"
"btop" "btop"
"comma" "desktop"
"direnv" "direnv"
"gaming" "gaming"
"helix" "helix"
"obs-studio" "obs-studio"
"starship" "starship"
"stylix"
"tmux" "tmux"
]; ];
}; };
"user@io" = mkHome { "user@io" = mkUser {
username = "user"; username = "user";
hostname = "io";
tags = [ tags = [
"desktop"
"btop" "btop"
"comma" "desktop"
"direnv" "direnv"
"helix" "helix"
"starship" "starship"
"stylix"
"tmux" "tmux"
]; ];
}; };

View file

@ -0,0 +1,11 @@
{ ... }:
{
networking.firewall = {
allowedTCPPorts = [
80
443
];
allowedUDPPorts = [ ];
};
}

View file

@ -0,0 +1,35 @@
{
config,
lib,
inputs,
...
}:
let
utils = import ../../utils.nix { inherit inputs lib; };
inherit (utils) mkNginxVHosts;
in
{
services.forgejo = {
enable = true;
repositoryRoot = "/data/forgejo";
settings = {
session.COOKIE_SECURE = true;
server = {
PROTOCOL = "http+unix";
DOMAIN = "git.baduhai.dev";
ROOT_URL = "https://git.baduhai.dev";
OFFLINE_MODE = true; # disable use of CDNs
SSH_DOMAIN = "baduhai.dev";
};
log.LEVEL = "Warn";
mailer.ENABLED = false;
actions.ENABLED = false;
};
};
services.nginx.virtualHosts = mkNginxVHosts {
acmeHost = "baduhai.dev";
domains."git.baduhai.dev".locations."/".proxyPass =
"http://unix:${config.services.forgejo.settings.server.HTTP_ADDR}:/";
};
}

View file

@ -10,6 +10,7 @@ in
}; };
services.nginx.virtualHosts = mkNginxVHosts { services.nginx.virtualHosts = mkNginxVHosts {
acmeHost = "baduhai.dev";
domains."jellyfin.baduhai.dev".locations."/".proxyPass = "http://127.0.0.1:8096/"; domains."jellyfin.baduhai.dev".locations."/".proxyPass = "http://127.0.0.1:8096/";
}; };
} }

View file

@ -1,83 +0,0 @@
{
config,
lib,
inputs,
pkgs,
...
}:
let
utils = import ../../utils.nix { inherit inputs lib; };
inherit (utils) mkNginxVHosts;
kanidmCertDir = "/var/lib/kanidm/certs";
in
{
services.kanidm = {
enableServer = true;
enableClient = true;
package = pkgs.kanidm;
serverSettings = {
domain = "auth.baduhai.dev";
origin = "https://auth.baduhai.dev";
bindaddress = "127.0.0.1:8443";
ldapbindaddress = "127.0.0.1:636";
trust_x_forward_for = true;
# Use self-signed certificates for internal TLS
tls_chain = "${kanidmCertDir}/cert.pem";
tls_key = "${kanidmCertDir}/key.pem";
};
clientSettings = {
uri = "https://auth.baduhai.dev";
};
};
services.nginx.virtualHosts = mkNginxVHosts {
domains."auth.baduhai.dev" = {
locations."/" = {
proxyPass = "https://127.0.0.1:8443";
extraConfig = ''
proxy_ssl_verify off;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
'';
};
};
};
networking.firewall.allowedTCPPorts = [ 636 ];
# Generate self-signed certificates for kanidm's internal TLS
systemd.services.kanidm-generate-certs = {
description = "Generate self-signed TLS certificates for Kanidm";
wantedBy = [ "multi-user.target" ];
before = [ "kanidm.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
mkdir -p ${kanidmCertDir}
if [ ! -f ${kanidmCertDir}/key.pem ]; then
${pkgs.openssl}/bin/openssl req -x509 -newkey rsa:4096 \
-keyout ${kanidmCertDir}/key.pem \
-out ${kanidmCertDir}/cert.pem \
-days 3650 -nodes \
-subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
chown -R kanidm:kanidm ${kanidmCertDir}
chmod 600 ${kanidmCertDir}/key.pem
chmod 644 ${kanidmCertDir}/cert.pem
fi
'';
};
# Ensure certificate generation runs before kanidm starts
systemd.services.kanidm = {
after = [ "kanidm-generate-certs.service" ];
wants = [ "kanidm-generate-certs.service" ];
};
}

View file

@ -0,0 +1,22 @@
{ lib, inputs, ... }:
let
utils = import ../../utils.nix { inherit inputs lib; };
inherit (utils) mkNginxVHosts;
in
{
virtualisation.oci-containers.containers."librespeed" = {
image = "lscr.io/linuxserver/librespeed:latest";
environment = {
TZ = "America/Bahia";
};
extraOptions = [
"--pull=newer"
"--label=io.containers.autoupdate=registry"
];
};
services.nginx.virtualHosts = mkNginxVHosts {
acmeHost = "baduhai.dev";
domains."speedtest.baduhai.dev".locations."/".proxyPass = "http://librespeed:80/";
};
}

View file

@ -1,97 +0,0 @@
{
lib,
config,
pkgs,
inputs,
...
}:
let
utils = import ../../utils.nix { inherit inputs lib; };
inherit (utils) mkNginxVHosts;
in
{
services = {
nextcloud = {
enable = true;
package = pkgs.nextcloud32;
datadir = "/data/nextcloud";
hostName = "cloud.baduhai.dev";
configureRedis = true;
https = true;
secretFile = config.age.secrets."nextcloud-secrets.json".path;
database.createLocally = true;
maxUploadSize = "16G";
extraApps = {
inherit (config.services.nextcloud.package.packages.apps)
calendar
contacts
notes
tasks
user_oidc
;
};
extraAppsEnable = true;
caching = {
apcu = true;
redis = true;
};
settings = {
trusted_proxies = [ "127.0.0.1" ];
default_phone_region = "BR";
maintenance_window_start = "4";
allow_local_remote_servers = true;
enabledPreviewProviders = [
"OC\\Preview\\BMP"
"OC\\Preview\\EMF"
"OC\\Preview\\Font"
"OC\\Preview\\GIF"
"OC\\Preview\\HEIC"
"OC\\Preview\\Illustrator"
"OC\\Preview\\JPEG"
"OC\\Preview\\Krita"
"OC\\Preview\\MarkDown"
"OC\\Preview\\Movie"
"OC\\Preview\\MP3"
"OC\\Preview\\MSOffice2003"
"OC\\Preview\\MSOffice2007"
"OC\\Preview\\MSOfficeDoc"
"OC\\Preview\\OpenDocument"
"OC\\Preview\\PDF"
"OC\\Preview\\Photoshop"
"OC\\Preview\\PNG"
"OC\\Preview\\Postscript"
"OC\\Preview\\SVG"
"OC\\Preview\\TIFF"
"OC\\Preview\\TXT"
"OC\\Preview\\XBitmap"
];
};
config = {
dbtype = "pgsql";
adminpassFile = config.age.secrets.nextcloud-adminpass.path;
};
phpOptions = {
"opcache.interned_strings_buffer" = "16";
};
};
nginx.virtualHosts = mkNginxVHosts {
domains."cloud.baduhai.dev" = { };
};
};
age.secrets = {
"nextcloud-secrets.json" = {
file = ../../secrets/nextcloud-secrets.json.age;
owner = "nextcloud";
group = "nextcloud";
};
nextcloud-adminpass = {
file = ../../secrets/nextcloud-adminpass.age;
owner = "nextcloud";
group = "nextcloud";
};
};
}

View file

@ -4,20 +4,10 @@
inputs, inputs,
... ...
}: }:
let let
utils = import ../../utils.nix { inherit inputs lib; }; utils = import ../../utils.nix { inherit inputs lib; };
inherit (utils) mkNginxVHosts services; inherit (utils) mkNginxVHosts;
# Get all unique domains from shared services that have LAN IPs (served by this host)
localDomains = lib.unique (map (s: s.domain) (lib.filter (s: s.host == "alexandria") services));
# Generate ACME cert configs for all local domains
acmeCerts = lib.genAttrs localDomains (domain: {
group = "nginx";
});
in in
{ {
security.acme = { security.acme = {
acceptTerms = true; acceptTerms = true;
@ -27,7 +17,15 @@ in
dnsProvider = "cloudflare"; dnsProvider = "cloudflare";
credentialsFile = config.age.secrets.cloudflare.path; credentialsFile = config.age.secrets.cloudflare.path;
}; };
certs = acmeCerts; certs."baduhai.dev" = {
extraDomainNames = [ "*.baduhai.dev" ];
};
};
age.secrets.cloudflare = {
file = ../../secrets/cloudflare.age;
owner = "nginx";
group = "nginx";
}; };
services.nginx = { services.nginx = {
@ -36,24 +34,11 @@ in
recommendedOptimisation = true; recommendedOptimisation = true;
recommendedProxySettings = true; recommendedProxySettings = true;
recommendedTlsSettings = true; recommendedTlsSettings = true;
virtualHosts = { virtualHosts = mkNginxVHosts {
"_" = { acmeHost = "baduhai.dev";
default = true; domains."_".locations."/".return = "444";
locations."/".return = "444";
};
}; };
}; };
users.users.nginx.extraGroups = [ "acme" ]; users.users.nginx.extraGroups = [ "acme" ];
networking.firewall.allowedTCPPorts = [
80
443
];
age.secrets.cloudflare = {
file = ../../secrets/cloudflare.age;
owner = "nginx";
group = "nginx";
};
} }

View file

@ -1,58 +0,0 @@
{ inputs, lib, ... }:
let
utils = import ../../utils.nix { inherit inputs lib; };
in
{
services.unbound = {
enable = true;
enableRootTrustAnchor = true;
settings = {
server = {
interface = [
"0.0.0.0"
"::"
];
access-control = [
"127.0.0.0/8 allow"
"192.168.0.0/16 allow"
"::1/128 allow"
];
num-threads = 2;
msg-cache-size = "50m";
rrset-cache-size = "100m";
cache-min-ttl = 300;
cache-max-ttl = 86400;
prefetch = true;
prefetch-key = true;
hide-identity = true;
hide-version = true;
so-rcvbuf = "1m";
so-sndbuf = "1m";
# LAN-only DNS records
local-zone = ''"baduhai.dev." transparent'';
local-data = map (e: ''"${e.domain}. IN A ${e.lanIP}"'')
(lib.filter (e: e ? lanIP) utils.services);
};
forward-zone = [
{
name = ".";
forward-addr = [
"1.1.1.1@853#cloudflare-dns.com"
"1.0.0.1@853#cloudflare-dns.com"
];
forward-tls-upstream = true;
}
];
};
};
networking.firewall = {
allowedTCPPorts = [ 53 ];
allowedUDPPorts = [ 53 ];
};
}

View file

@ -14,13 +14,13 @@ in
config = { config = {
DOMAIN = "https://pass.baduhai.dev"; DOMAIN = "https://pass.baduhai.dev";
SIGNUPS_ALLOWED = false; SIGNUPS_ALLOWED = false;
ROCKET_ADDRESS = "127.0.0.1"; ROCKET_ADDRESS = "/run/vaultwarden/vaultwarden.sock";
ROCKET_PORT = 58222;
}; };
}; };
services.nginx.virtualHosts = mkNginxVHosts { services.nginx.virtualHosts = mkNginxVHosts {
acmeHost = "baduhai.dev";
domains."pass.baduhai.dev".locations."/".proxyPass = domains."pass.baduhai.dev".locations."/".proxyPass =
"http://${config.services.vaultwarden.config.ROCKET_ADDRESS}:${toString config.services.vaultwarden.config.ROCKET_PORT}/"; "http://unix:${config.services.vaultwarden.config.ROCKET_ADDRESS}:/";
}; };
} }

View file

@ -3,71 +3,75 @@
{ {
imports = [ inputs.disko.nixosModules.default ]; imports = [ inputs.disko.nixosModules.default ];
disko.devices.disk.main = { disko.devices = {
type = "disk"; disk = {
device = "/dev/disk/by-id/mmc-hDEaP3_0x1041b689"; main = {
content = { type = "disk";
type = "gpt"; device = "/dev/disk/by-id/mmc-hDEaP3_0x1041b689";
partitions = { content = {
ESP = { type = "gpt";
priority = 1; partitions = {
name = "ESP"; ESP = {
start = "1MiB"; priority = 1;
end = "1GiB"; name = "ESP";
type = "EF00"; start = "1MiB";
content = { end = "1GiB";
type = "filesystem"; type = "EF00";
format = "vfat"; content = {
mountpoint = "/boot/efi"; type = "filesystem";
mountOptions = [ format = "vfat";
"noatime" mountpoint = "/boot/efi";
"fmask=0077" mountOptions = [
"dmask=0077" "noatime"
]; "fmask=0077"
}; "dmask=0077"
}; ];
cryptroot = { };
priority = 2; };
name = "root"; cryptroot = {
size = "100%"; priority = 2;
content = { name = "root";
type = "luks"; size = "100%";
name = "cryptroot"; content = {
content = { type = "luks";
type = "btrfs"; name = "cryptroot";
extraArgs = [ "-f" ]; content = {
subvolumes = { type = "btrfs";
"@root" = { extraArgs = [ "-f" ];
mountpoint = "/"; subvolumes = {
mountOptions = [ "@root" = {
"noatime" mountpoint = "/";
"compress=zstd" mountOptions = [
"subvol=@root" "noatime"
]; "compress=zstd"
}; "subvol=@root"
"@home" = { ];
mountpoint = "/home"; };
mountOptions = [ "@home" = {
"noatime" mountpoint = "/home";
"compress=zstd" mountOptions = [
"subvol=@home" "noatime"
]; "compress=zstd"
}; "subvol=@home"
"@nix" = { ];
mountpoint = "/nix"; };
mountOptions = [ "@nix" = {
"noatime" mountpoint = "/nix";
"compress=zstd" mountOptions = [
"subvol=@nix" "noatime"
]; "compress=zstd"
}; "subvol=@nix"
"@persistent" = { ];
mountpoint = "/persistent"; };
mountOptions = [ "@persistent" = {
"noatime" mountpoint = "/persistent";
"compress=zstd" mountOptions = [
"subvol=@persistent" "noatime"
]; "compress=zstd"
"subvol=@persistent"
];
};
};
}; };
}; };
}; };

View file

@ -2,7 +2,6 @@
config, config,
lib, lib,
modulesPath, modulesPath,
inputs,
... ...
}: }:

View file

@ -48,8 +48,6 @@
}; };
}; };
}; };
upower.enable = true;
power-profiles-daemon.enable = true;
}; };
# TODO: remove once gmodena/nix-flatpak/issues/45 fixed # TODO: remove once gmodena/nix-flatpak/issues/45 fixed

View file

@ -1,10 +0,0 @@
{ inputs, pkgs, ... }:
{
environment.systemPackages = with inputs.nix-ai-tools.packages.${pkgs.system}; [
claude-desktop
claude-code
claudebox
opencode
];
}

View file

@ -7,7 +7,6 @@
defaultLocale = "en_US.UTF-8"; defaultLocale = "en_US.UTF-8";
extraLocaleSettings = { extraLocaleSettings = {
LC_ADDRESS = "pt_BR.utf8"; LC_ADDRESS = "pt_BR.utf8";
LC_COLLATE = "pt_BR.utf8";
LC_IDENTIFICATION = "pt_BR.utf8"; LC_IDENTIFICATION = "pt_BR.utf8";
LC_MEASUREMENT = "pt_BR.utf8"; LC_MEASUREMENT = "pt_BR.utf8";
LC_MONETARY = "pt_BR.utf8"; LC_MONETARY = "pt_BR.utf8";

View file

@ -26,11 +26,13 @@
buildDocs = false; buildDocs = false;
}; };
services.nixos-cli = { services = {
enable = true; nixos-cli = {
config = { enable = true;
use_nvd = true; config = {
ignore_dirty_tree = true; use_nvd = true;
ignore_dirty_tree = true;
};
}; };
}; };

View file

@ -4,8 +4,5 @@
services.openssh = { services.openssh = {
enable = true; enable = true;
settings.PermitRootLogin = "no"; settings.PermitRootLogin = "no";
extraConfig = ''
PrintLastLog no
'';
}; };
} }

View file

@ -7,8 +7,6 @@
git git
### System Utilities ### ### System Utilities ###
btop btop
fastfetch
helix
nixos-firewall-tool nixos-firewall-tool
nvd nvd
sysz sysz
@ -19,22 +17,13 @@
shellAliases = { shellAliases = {
cat = "${lib.getExe pkgs.bat} --paging=never --style=plain"; cat = "${lib.getExe pkgs.bat} --paging=never --style=plain";
ls = "${lib.getExe pkgs.eza} --icons --group-directories-first"; ls = "${lib.getExe pkgs.eza} --icons --group-directories-first";
neofetch = "${lib.getExe pkgs.fastfetch}";
tree = "ls --tree"; tree = "ls --tree";
}; };
}; };
programs = { programs = {
command-not-found.enable = false; command-not-found.enable = false;
fish = { fish.enable = true;
enable = true;
interactiveShellInit = ''
set fish_greeting
if set -q SSH_CONNECTION
export TERM=xterm-256color
clear
fastfetch
end
'';
};
}; };
} }

View file

@ -10,9 +10,8 @@
"wheel" "wheel"
]; ];
openssh.authorizedKeys.keys = [ openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICQPkAyy+Du9Omc2WtnUF2TV8jFAF4H6mJi2D4IZ1nzg user@himalia"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO3Y0PVpGfJHonqDS7qoCFhqzUvqGq9I9sax+F9e/5cs user@io"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA1v3+q3EaruiiStWjubEJWvtejam/r41uoOpCdwJtLL user@rotterdam" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA1v3+q3EaruiiStWjubEJWvtejam/r41uoOpCdwJtLL user@rotterdam"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO3Y0PVpGfJHonqDS7qoCFhqzUvqGq9I9sax+F9e/5cs user@io"
]; ];
hashedPassword = "$6$Pj7v/CpstyuWQQV0$cNujVDhfMBdwlGVEnnd8t71.kZPixbo0u25cd.874iaqLTH4V5fa1f98V5zGapjQCz5JyZmsR94xi00sUrntT0"; hashedPassword = "$6$Pj7v/CpstyuWQQV0$cNujVDhfMBdwlGVEnnd8t71.kZPixbo0u25cd.874iaqLTH4V5fa1f98V5zGapjQCz5JyZmsR94xi00sUrntT0";
}; };
@ -21,4 +20,10 @@
hashedPassword = "!"; hashedPassword = "!";
}; };
}; };
programs.fish = {
enable = true;
interactiveShellInit = ''
set fish_greeting
'';
};
} }

View file

@ -1,63 +1,79 @@
{ {
config,
inputs, inputs,
lib, lib,
pkgs, pkgs,
... ...
}: }:
let
kwrite = pkgs.symlinkJoin {
name = "kwrite";
paths = [ pkgs.kdePackages.kate ];
postBuild = ''
rm -rf $out/bin/kate \
$out/bin/.kate-wrapped \
$out/share/applications/org.kde.kate.desktop \
$out/share/man \
$out/share/icons/hicolor/*/apps/kate.png \
$out/share/icons/hicolor/scalable/apps/kate.svg \
$out/share/appdata/org.kde.kate.appdata.xml
'';
};
in
{ {
imports = [ imports = [ inputs.nix-flatpak.nixosModules.nix-flatpak ];
inputs.niri-flake.nixosModules.niri
inputs.nix-flatpak.nixosModules.nix-flatpak
];
environment = { environment = {
sessionVariables = { sessionVariables = {
KDEHOME = "$XDG_CONFIG_HOME/kde4"; # Stops kde from placing a .kde4 folder in the home dir KDEHOME = "$XDG_CONFIG_HOME/kde4"; # Stops kde from placing a .kde4 folder in the home dir
NIXOS_OZONE_WL = "1"; # Forces chromium and most electron apps to run in wayland NIXOS_OZONE_WL = "1"; # Forces chromium and most electron apps to run in wayland
}; };
systemPackages = with pkgs; [ systemPackages =
### Web ### with pkgs;
bitwarden-desktop [
fragments ### Web ###
nextcloud-client bitwarden-desktop
tor-browser brave
vesktop tor-browser
inputs.zen-browser.packages."${system}".default qbittorrent
### Office & Productivity ### vesktop
aspell ### Office & Productivity ###
aspellDicts.de aspell
aspellDicts.en aspellDicts.de
aspellDicts.en-computers aspellDicts.en
aspellDicts.pt_BR aspellDicts.en-computers
libreoffice aspellDicts.pt_BR
onlyoffice-desktopeditors kwrite
papers libreoffice-qt
presenterm onlyoffice-desktopeditors
rnote rnote
### Graphics & Design ### ### Graphics & Design ###
gimp gimp
inkscape inkscape
plasticity plasticity
### System Utilities ### ### System Utilities ###
adwaita-icon-theme adwaita-icon-theme
ghostty colloid-gtk-theme
gnome-disk-utility junction
junction kara
libfido2 kde-rounded-corners
mission-center libfido2
nautilus mission-center
p7zip p7zip
rclone rclone
toggleaudiosink toggleaudiosink
unrar unrar
### Media ### ### Media ###
decibels mpv
loupe obs-studio
obs-studio qview
showtime ]
]; ++ (with pkgs.kdePackages; [
ark
dolphin
dolphin-plugins
kolourpaint
]);
}; };
services = { services = {
@ -73,13 +89,11 @@
enable = true; enable = true;
settings = { settings = {
default_session = { default_session = {
command = "${lib.getExe pkgs.tuigreet} --user-menu --time --remember --asterisks --cmd ${config.programs.niri.package}/bin/niri-session"; command = "${lib.getExe pkgs.greetd.tuigreet} --time --remember --asterisks --cmd ${lib.getExe pkgs.niri}";
user = "greeter"; user = "greeter";
}; };
}
// lib.optionalAttrs (config.networking.hostName == "io") {
initial_session = { initial_session = {
command = "${config.programs.niri.package}/bin/niri-session"; command = "${lib.getExe pkgs.niri}";
user = "user"; user = "user";
}; };
}; };
@ -87,6 +101,8 @@
flatpak = { flatpak = {
enable = true; enable = true;
packages = [ packages = [
### Internet Browsers & Communication ###
"app.zen_browser.zen"
### Graphics & Design ### ### Graphics & Design ###
"com.boxy_svg.BoxySVG" "com.boxy_svg.BoxySVG"
rec { rec {
@ -104,7 +120,6 @@
uninstallUnmanaged = true; uninstallUnmanaged = true;
update.auto.enable = true; update.auto.enable = true;
}; };
gvfs.enable = true;
}; };
security.rtkit.enable = true; # Needed for pipewire to acquire realtime priority security.rtkit.enable = true; # Needed for pipewire to acquire realtime priority
@ -118,23 +133,16 @@
}; };
programs = { programs = {
niri = { niri.enable = true;
enable = true;
package = inputs.niri.packages.${pkgs.system}.niri;
};
kdeconnect = {
enable = true;
package = pkgs.valent;
};
dconf.enable = true; dconf.enable = true;
kdeconnect.enable = true;
partition-manager.enable = true;
appimage = { appimage = {
enable = true; enable = true;
binfmt = true; binfmt = true;
}; };
}; };
niri-flake.cache.enable = false;
fonts = { fonts = {
fontDir.enable = true; fontDir.enable = true;
packages = with pkgs; [ packages = with pkgs; [
@ -146,18 +154,4 @@
roboto roboto
]; ];
}; };
xdg.portal = {
extraPortals = with pkgs; [
xdg-desktop-portal-gnome
xdg-desktop-portal-gtk
];
config = {
common.default = "*";
niri.default = [
"gtk"
"gnome"
];
};
};
} }

View file

@ -3,6 +3,7 @@
{ {
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
bat bat
claude-code
lazygit lazygit
fd fd
fzf fzf

View file

@ -1,41 +0,0 @@
{ config, inputs, ... }:
{
imports = [
inputs.impermanence.nixosModules.impermanence
inputs.self.nixosModules.ephemeral
];
ephemeral = {
enable = true;
rootDevice =
if config.networking.hostName == "trantor" then
"/dev/disk/by-id/scsi-360b207ed25d84372a95d1ecf842f8e20-part2"
else
"/dev/mapper/cryptroot";
rootSubvolume = "@root";
};
environment.persistence.main = {
persistentStoragePath = "/persistent";
files = [
"/etc/machine-id"
"/etc/ssh/ssh_host_ed25519_key"
"/etc/ssh/ssh_host_ed25519_key.pub"
"/etc/ssh/ssh_host_rsa_key"
"/etc/ssh/ssh_host_rsa_key.pub"
];
directories = [
"/etc/NetworkManager/system-connections"
"/etc/nixos"
"/var/lib/bluetooth"
"/var/lib/flatpak"
"/var/lib/lxd"
"/var/lib/nixos"
"/var/lib/systemd/coredump"
"/var/lib/systemd/timers"
"/var/lib/tailscale"
"/var/log"
];
};
}

View file

@ -0,0 +1,72 @@
{ inputs, ... }:
{
imports = [ inputs.impermanence.nixosModules.impermanence ];
boot.initrd.systemd.services.recreate-root = {
description = "Rolling over and creating new filesystem root";
requires = [ "initrd-root-device.target" ];
after = [
"local-fs-pre.target"
"initrd-root-device.target"
];
requiredBy = [ "initrd-root-fs.target" ];
before = [ "sysroot.mount" ];
unitConfig = {
AssertPathExists = "/etc/initrd-release";
DefaultDependencies = false;
};
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
mkdir /btrfs_tmp
mount /dev/mapper/cryptroot /btrfs_tmp
if [[ -e /btrfs_tmp/@root ]]; then
mkdir -p /btrfs_tmp/old_roots
timestamp=$(date --date="@$(stat -c %Y /btrfs_tmp/@root)" "+%Y-%m-%-d_%H:%M:%S")
mv /btrfs_tmp/@root "/btrfs_tmp/old_roots/$timestamp"
fi
delete_subvolume_recursively() {
IFS=$'\n'
for i in $(btrfs subvolume list -o "$1" | cut -f 9- -d ' '); do
delete_subvolume_recursively "/btrfs_tmp/$i"
done
btrfs subvolume delete "$1"
}
for i in $(find /btrfs_tmp/old_roots/ -maxdepth 1 -mtime +30); do
delete_subvolume_recursively "$i"
done
btrfs subvolume create /btrfs_tmp/@root
umount /btrfs_tmp
'';
};
environment.persistence.main = {
persistentStoragePath = "/persistent";
files = [
"/etc/machine-id"
"/etc/ssh/ssh_host_ed25519_key"
"/etc/ssh/ssh_host_ed25519_key.pub"
"/etc/ssh/ssh_host_rsa_key"
"/etc/ssh/ssh_host_rsa_key.pub"
];
directories = [
"/etc/NetworkManager/system-connections"
"/etc/nixos"
"/var/lib/bluetooth"
"/var/lib/flatpak"
"/var/lib/lxd"
"/var/lib/nixos"
"/var/lib/systemd/coredump"
"/var/lib/systemd/timers"
"/var/lib/tailscale"
"/var/log"
];
};
}

View file

@ -6,6 +6,7 @@
heroic heroic
mangohud mangohud
prismlauncher prismlauncher
protonup
steam-run steam-run
]; ];

View file

@ -1,6 +1,6 @@
{ {
boot = { boot = {
loader.efi.efiSysMountPoint = "/boot";
initrd.systemd.enable = true; initrd.systemd.enable = true;
loader.efi.efiSysMountPoint = "/boot/efi";
}; };
} }

View file

@ -3,57 +3,29 @@
{ {
imports = [ inputs.disko.nixosModules.default ]; imports = [ inputs.disko.nixosModules.default ];
disko.devices.disk.main = { disko.devices = {
type = "disk"; disk = {
device = "/dev/disk/by-id/scsi-360b207ed25d84372a95d1ecf842f8e20"; main = {
content = { type = "disk";
type = "gpt"; device = "/dev/disk/by-id/scsi-3605e4addb4c640319c8c03436205530b";
partitions = { content = {
ESP = { type = "gpt";
priority = 1; partitions = {
name = "ESP"; boot = {
start = "1MiB"; size = "512M";
end = "512MiB"; type = "EF00";
type = "EF00"; content = {
content = { type = "filesystem";
type = "filesystem"; format = "vfat";
format = "vfat"; mountpoint = "/boot";
mountpoint = "/boot/efi"; };
mountOptions = [ };
"noatime" root = {
"fmask=0077" size = "100%";
"dmask=0077" content = {
]; type = "filesystem";
}; format = "ext4";
};
root = {
priority = 2;
name = "root";
size = "100%";
content = {
type = "btrfs";
extraArgs = [ "-f" ];
subvolumes = {
"@root" = {
mountpoint = "/"; mountpoint = "/";
mountOptions = [
"noatime"
"compress=zstd"
];
};
"@nix" = {
mountpoint = "/nix";
mountOptions = [
"noatime"
"compress=zstd"
];
};
"@persistent" = {
mountpoint = "/persistent";
mountOptions = [
"noatime"
"compress=zstd"
];
}; };
}; };
}; };

View file

@ -1,23 +0,0 @@
{ config, pkgs, ... }:
{
services.fail2ban = {
enable = true;
maxretry = 5;
ignoreIP = [
"127.0.0.0/8"
"::1"
"10.0.0.0/8"
"172.16.0.0/12"
"192.168.0.0/16"
"100.64.0.0/10"
];
bantime = "1h";
bantime-increment = {
enable = true;
multipliers = "1 2 4 8 16 32 64";
maxtime = "10000h";
overalljails = true;
};
};
}

View file

@ -1,72 +0,0 @@
{
config,
lib,
inputs,
...
}:
let
utils = import ../../utils.nix { inherit inputs lib; };
inherit (utils) mkNginxVHosts;
in
{
services = {
forgejo = {
enable = true;
settings = {
session.COOKIE_SECURE = true;
server = {
PROTOCOL = "http+unix";
DOMAIN = "git.baduhai.dev";
ROOT_URL = "https://git.baduhai.dev";
OFFLINE_MODE = true; # disable use of CDNs
SSH_DOMAIN = "git.baduhai.dev";
};
log.LEVEL = "Warn";
mailer.ENABLED = false;
actions.ENABLED = false;
service.DISABLE_REGISTRATION = true;
oauth2_client = {
ENABLE_AUTO_REGISTRATION = true;
UPDATE_AVATAR = true;
ACCOUNT_LINKING = "login";
USERNAME = "preferred_username";
};
};
};
nginx.virtualHosts = mkNginxVHosts {
domains."git.baduhai.dev".locations."/".proxyPass =
"http://unix:${config.services.forgejo.settings.server.HTTP_ADDR}:/";
};
fail2ban.jails.forgejo = {
settings = {
enabled = true;
filter = "forgejo";
maxretry = 3;
findtime = "10m";
bantime = "1h";
};
};
};
environment = {
etc."fail2ban/filter.d/forgejo.conf".text = ''
[Definition]
failregex = .*(Failed authentication attempt|invalid credentials|Attempted access of unknown user).* from <HOST>
ignoreregex =
journalmatch = _SYSTEMD_UNIT=forgejo.service
'';
persistence.main.directories = [
{
directory = config.services.forgejo.stateDir;
inherit (config.services.forgejo) user group;
mode = "0700";
}
];
};
# Disable PrivateMounts to allow LoadCredential to work with bind-mounted directories
systemd.services.forgejo.serviceConfig.PrivateMounts = lib.mkForce false;
}

View file

@ -7,12 +7,19 @@
{ {
imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; imports = [ (modulesPath + "/profiles/qemu-guest.nix") ];
boot.initrd.availableKernelModules = [ boot = {
"xhci_pci" kernelModules = [ ];
"virtio_pci" extraModulePackages = [ ];
"virtio_scsi" initrd = {
"usbhid" availableKernelModules = [
]; "xhci_pci"
"virtio_pci"
"virtio_scsi"
"usbhid"
];
kernelModules = [ ];
};
};
networking.useDHCP = lib.mkDefault true; networking.useDHCP = lib.mkDefault true;

View file

@ -1,61 +0,0 @@
{
config,
lib,
inputs,
...
}:
let
utils = import ../../utils.nix { inherit inputs lib; };
inherit (utils) mkNginxVHosts services;
# Get all unique domains from shared services on trantor (host = "trantor")
localDomains = lib.unique (
map (s: s.domain) (lib.filter (s: s.host == "trantor") services)
);
# Generate ACME cert configs for all local domains
acmeCerts = lib.genAttrs localDomains (domain: {
group = "nginx";
});
in
{
security.acme = {
acceptTerms = true;
defaults = {
email = "baduhai@proton.me";
dnsResolver = "1.1.1.1:53";
dnsProvider = "cloudflare";
credentialsFile = config.age.secrets.cloudflare.path;
};
certs = acmeCerts;
};
services.nginx = {
enable = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
virtualHosts = {
"_" = {
default = true;
locations."/".return = "444";
};
};
};
users.users.nginx.extraGroups = [ "acme" ];
networking.firewall.allowedTCPPorts = [
80
443
];
age.secrets.cloudflare = {
file = ../../secrets/cloudflare.age;
owner = "nginx";
group = "nginx";
};
}

View file

@ -1,23 +0,0 @@
{ ... }:
{
services = {
openssh = {
settings = {
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
};
};
fail2ban.jails.sshd = {
settings = {
enabled = true;
port = "ssh";
filter = "sshd";
logpath = "/var/log/auth.log";
maxretry = 3;
findtime = "10m";
bantime = "1h";
};
};
};
}

View file

@ -1,58 +0,0 @@
{ inputs, lib, ... }:
let
utils = import ../../utils.nix { inherit inputs lib; };
in
{
services.unbound = {
enable = true;
enableRootTrustAnchor = true;
settings = {
server = {
interface = [
"0.0.0.0"
"::"
];
access-control = [
"127.0.0.0/8 allow"
"100.64.0.0/10 allow" # Tailscale CGNAT range
"::1/128 allow"
"fd7a:115c:a1e0::/48 allow" # Tailscale IPv6
];
num-threads = 2;
msg-cache-size = "50m";
rrset-cache-size = "100m";
cache-min-ttl = 300;
cache-max-ttl = 86400;
prefetch = true;
prefetch-key = true;
hide-identity = true;
hide-version = true;
so-rcvbuf = "1m";
so-sndbuf = "1m";
# Tailnet DNS records from shared services
local-zone = ''"baduhai.dev." transparent'';
local-data = map (e: ''"${e.domain}. IN A ${e.tailscaleIP}"'') utils.services;
};
forward-zone = [
{
name = ".";
forward-addr = [
"1.1.1.1@853#cloudflare-dns.com"
"1.0.0.1@853#cloudflare-dns.com"
];
forward-tls-upstream = true;
}
];
};
};
networking.firewall = {
allowedTCPPorts = [ 53 ];
allowedUDPPorts = [ 53 ];
};
}

View file

@ -1,84 +0,0 @@
{ lib, config, ... }:
let
cfg = config.ephemeral;
in
{
options.ephemeral = {
enable = lib.mkEnableOption "ephemeral root with automatic rollback";
rootDevice = lib.mkOption {
type = lib.types.str;
example = "/dev/mapper/cryptroot";
description = "Device path for the root btrfs filesystem";
};
rootSubvolume = lib.mkOption {
type = lib.types.str;
example = "@root";
description = "Name of the root btrfs subvolume";
};
oldRootRetentionDays = lib.mkOption {
type = lib.types.int;
default = 30;
description = "Number of days to keep old root snapshots before deletion";
};
};
config = lib.mkIf cfg.enable {
boot.initrd.systemd.services.recreate-root = {
description = "Rolling over and creating new filesystem root";
requires = [ "initrd-root-device.target" ];
after = [
"local-fs-pre.target"
"initrd-root-device.target"
];
requiredBy = [ "initrd-root-fs.target" ];
before = [ "sysroot.mount" ];
unitConfig = {
AssertPathExists = "/etc/initrd-release";
DefaultDependencies = false;
};
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
set -euo pipefail
mkdir /btrfs_tmp
if ! mount ${cfg.rootDevice} /btrfs_tmp; then
echo "ERROR: Failed to mount ${cfg.rootDevice}"
exit 1
fi
if [[ -e /btrfs_tmp/${cfg.rootSubvolume} ]]; then
mkdir -p /btrfs_tmp/old_roots
timestamp=$(date --date="@$(stat -c %Y /btrfs_tmp/${cfg.rootSubvolume})" "+%Y-%m-%-d_%H:%M:%S")
mv /btrfs_tmp/${cfg.rootSubvolume} "/btrfs_tmp/old_roots/$timestamp"
fi
delete_subvolume_recursively() {
IFS=$'\n'
for i in $(btrfs subvolume list -o "$1" | cut -f 9- -d ' '); do
delete_subvolume_recursively "/btrfs_tmp/$i"
done
btrfs subvolume delete "$1"
}
for i in $(find /btrfs_tmp/old_roots/ -maxdepth 1 -mtime +${toString cfg.oldRootRetentionDays}); do
delete_subvolume_recursively "$i"
done
if ! btrfs subvolume create /btrfs_tmp/${cfg.rootSubvolume}; then
echo "ERROR: Failed to create subvolume ${cfg.rootSubvolume}"
umount /btrfs_tmp
exit 1
fi
umount /btrfs_tmp
'';
};
};
}

View file

@ -10,10 +10,9 @@ in
hostname = "rotterdam"; hostname = "rotterdam";
tags = [ tags = [
"desktop" "desktop"
"ai"
"bluetooth" "bluetooth"
"dev" "dev"
"ephemeral" "ephermal"
"fwupd" "fwupd"
"gaming" "gaming"
"libvirtd" "libvirtd"
@ -26,10 +25,9 @@ in
hostname = "io"; hostname = "io";
tags = [ tags = [
"desktop" "desktop"
"ai"
"bluetooth" "bluetooth"
"dev" "dev"
"ephemeral" "ephermal"
"networkmanager" "networkmanager"
"podman" "podman"
]; ];
@ -38,8 +36,8 @@ in
alexandria = mkHost { alexandria = mkHost {
hostname = "alexandria"; hostname = "alexandria";
tags = [ tags = [
# "server" TODO: uncomment when 25.11 is out.
"fwupd" "fwupd"
"podman"
]; ];
}; };
@ -47,8 +45,6 @@ in
hostname = "trantor"; hostname = "trantor";
system = "aarch64-linux"; system = "aarch64-linux";
tags = [ tags = [
"server"
"ephemeral"
]; ];
}; };
}; };

View file

@ -1,7 +0,0 @@
{ ... }:
{
flake.nixosModules = {
ephemeral = import ./modules/ephemeral.nix;
};
}

View file

@ -3,11 +3,8 @@
{ {
flake.overlays = { flake.overlays = {
default = final: prev: { default = final: prev: {
base16-schemes = inputs.self.packages.${final.system}.base16-schemes;
fastfetch = inputs.self.packages.${final.system}.fastfetch;
hm-cli = inputs.self.packages.${final.system}.hm-cli;
kwrite = inputs.self.packages.${final.system}.kwrite;
toggleaudiosink = inputs.self.packages.${final.system}.toggleaudiosink; toggleaudiosink = inputs.self.packages.${final.system}.toggleaudiosink;
hm-cli = inputs.self.packages.${final.system}.hm-cli;
}; };
}; };
} }

View file

@ -5,11 +5,8 @@
{ pkgs, system, ... }: { pkgs, system, ... }:
{ {
packages = { packages = {
base16-schemes = pkgs.callPackage ./packages/base16-schemes.nix { };
fastfetch = pkgs.callPackage ./packages/fastfetch.nix { };
hm-cli = pkgs.callPackage ./packages/hm-cli.nix { };
kwrite = pkgs.callPackage ./packages/kwrite.nix { };
toggleaudiosink = pkgs.callPackage ./packages/toggleaudiosink.nix { }; toggleaudiosink = pkgs.callPackage ./packages/toggleaudiosink.nix { };
hm-cli = pkgs.callPackage ./packages/hm-cli.nix { };
}; };
}; };
} }

View file

@ -1,32 +0,0 @@
{
lib,
stdenv,
fetchFromGitHub,
}:
stdenv.mkDerivation (finalAttrs: {
pname = "base16-schemes";
version = "0-unstable-2025-06-04";
src = fetchFromGitHub {
owner = "tinted-theming";
repo = "schemes";
rev = "317a5e10c35825a6c905d912e480dfe8e71c7559";
hash = "sha256-d4km8W7w2zCUEmPAPUoLk1NlYrGODuVa3P7St+UrqkM=";
};
installPhase = ''
runHook preInstall
mkdir -p $out/share/themes/
install base16/*.yaml $out/share/themes/
runHook postInstall
'';
meta = {
description = "All the color schemes for use in base16 packages";
homepage = "https://github.com/tinted-theming/schemes";
maintainers = [ lib.maintainers.DamienCassou ];
license = lib.licenses.mit;
};
})

View file

@ -1,81 +0,0 @@
{
lib,
pkgs ? import <nixpkgs> { },
}:
let
fastfetch-logo = pkgs.fetchurl {
url = "https://discourse.nixos.org/uploads/default/original/3X/3/6/36954e6d6aa32c8b00f50ca43f142d898c1ff535.png";
hash = "sha256-aLHz8jSAFocrn+Pb4vRq0wtkYFJpBpZRevd+VoZC/PQ=";
};
fastfetch-config = pkgs.writeText "fastfetch-config.json" (
builtins.toJSON {
"$schema" = "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json";
modules = [
"title"
"separator"
{
type = "os";
keyWidth = 9;
}
{
type = "kernel";
keyWidth = 9;
}
{
type = "uptime";
keyWidth = 9;
}
{
type = "shell";
keyWidth = 9;
}
"break"
{
type = "cpu";
keyWidth = 11;
}
{
type = "memory";
keyWidth = 11;
}
{
type = "swap";
keyWidth = 11;
}
{
type = "disk";
folders = "/";
keyWidth = 11;
}
{
type = "command";
key = "Systemd";
keyWidth = 11;
text = "echo \"$(systemctl list-units --state=failed --no-legend | wc -l) failed units, $(systemctl list-jobs --no-legend | wc -l) queued jobs\"";
}
"break"
{
type = "command";
key = "Public IP";
keyWidth = 15;
text = "curl -s -4 ifconfig.me 2>/dev/null || echo 'N/A'";
}
{
type = "command";
key = "Tailscale IP";
keyWidth = 15;
text = "tailscale ip -4 2>/dev/null || echo 'N/A'";
}
{
type = "command";
key = "Local IP";
keyWidth = 15;
text = "ip -4 addr show scope global | grep inet | head -n1 | awk '{print $2}' | cut -d/ -f1";
}
];
}
);
in
pkgs.writeShellScriptBin "fastfetch" ''exec ${lib.getExe pkgs.fastfetch} --config ${fastfetch-config} --logo-type kitty --logo ${fastfetch-logo} --logo-padding-right 1 --logo-width 36 "$@" ''

View file

@ -23,9 +23,9 @@ pkgs.writeShellScriptBin "hm" ''
Environment Variables: Environment Variables:
HM_PATH Override default flake path (~/.config/home-manager) HM_PATH Override default flake path (~/.config/home-manager)
Currently set to "''${HM_PATH:-<not set>}" Currently set to "$(echo $HM_PATH)"
HM_USER Override default user output ("$(whoami)@$(hostname)") HM_USER Override default user output ("$(whoami)@$(hostname)")
Currently set to "''${HM_USER:-<not set>}" Currently set to "$(echo $HM_USER)"
EOF EOF
} }
@ -36,7 +36,7 @@ pkgs.writeShellScriptBin "hm" ''
case "$1" in case "$1" in
apply) apply)
"$HM" switch --flake "$FLAKE_PATH#$FLAKE_OUTPUT" -b bkp "$HM" switch --flake "$FLAKE_PATH#$FLAKE_OUTPUT"
;; ;;
generation) generation)
if [[ $# -lt 2 ]]; then if [[ $# -lt 2 ]]; then
@ -58,21 +58,17 @@ pkgs.writeShellScriptBin "hm" ''
"$HM" remove-generations "$@" "$HM" remove-generations "$@"
;; ;;
rollback) rollback)
PREV_GEN=$("$HM" generations | \ "$HM" generations | \
sed -n 's/^[[:space:]]*id \([0-9]\+\).*/\1/p' | \ sed -n 's/^[[:space:]]*id \([0-9]\+\).*/\1/p' | \
head -n 2 | tail -n 1) head -n 2 | tail -n 1 | \
if [[ -z "$PREV_GEN" ]]; then xargs -I {} "$HM" switch --flake "$FLAKE_PATH" --switch-generation {}
echo "Error: could not determine previous generation (possibly only one generation exists)"
exit 1
fi
"$HM" switch --flake "$FLAKE_PATH" --switch-generation "$PREV_GEN" -b bkp
;; ;;
switch) switch)
if [[ $# -ne 3 ]]; then if [[ $# -ne 3 ]]; then
echo "Error: switch requires exactly one generation ID" echo "Error: switch requires exactly one generation ID"
exit 1 exit 1
fi fi
"$HM" switch --flake "$FLAKE_PATH" --switch-generation "$3" -b bkp "$HM" switch --flake "$FLAKE_PATH" --switch-generation "$3"
;; ;;
cleanup) cleanup)
CURRENT_GEN=$("$HM" generations | sed -n 's/^.*id \([0-9]\+\) .* (current)$/\1/p') CURRENT_GEN=$("$HM" generations | sed -n 's/^.*id \([0-9]\+\) .* (current)$/\1/p')

View file

@ -1,15 +0,0 @@
{ pkgs }:
pkgs.symlinkJoin {
name = "kwrite";
paths = [ pkgs.kdePackages.kate ];
postBuild = ''
rm -rf $out/bin/kate \
$out/bin/.kate-wrapped \
$out/share/applications/org.kde.kate.desktop \
$out/share/man \
$out/share/icons/hicolor/*/apps/kate.png \
$out/share/icons/hicolor/scalable/apps/kate.svg \
$out/share/appdata/org.kde.kate.appdata.xml
'';
}

View file

@ -1,87 +1,8 @@
# Nix Configuration All my personal Nix and NixOS hosts, in a flake.
My personal Nix configuration for multiple NixOS hosts, home-manager users, miscellaneous resources... too many things to list. If I could put my life in a flake I would. |Host|Description|System Version|
|:---|:---:|---:|
## Hosts |alexandria|Personal server/NAS|NixOS 25.05|
|io|Mobile workstation|NixOS Unstable|
### Desktop Systems |rotterdam|Workstation|NixOS Unstable|
- **rotterdam** - Main desktop workstation (x86_64) |trantor|Oracle Cloud VPS|NixOS 25.05|
- Features: Desktop, AI tools, Bluetooth, Dev environment, Gaming, Virtualization (libvirtd), Podman
- Storage: Ephemeral root with LUKS encryption
- **io** - Laptop workstation (x86_64)
- Features: Desktop, AI tools, Bluetooth, Dev environment, Podman
- Storage: Ephemeral root with LUKS encryption
### Servers
- **alexandria** - Home server (x86_64)
- Hosts: Nextcloud, Vaultwarden, Jellyfin, Kanidm
- **trantor** - Cloud server (aarch64)
- Hosts: Forgejo
- Cloud provider: Oracle Cloud Infrastructure
- Storage: Ephemeral root with btrfs
## Home Manager Configurations
- **user@rotterdam** - Full desktop setup with gaming, OBS, and complete development environment
- **user@io** - Lightweight desktop setup
Both configurations include:
- btop, direnv, helix, starship, tmux
- Stylix theme management
- Fish shell with custom configurations
## Terranix Configurations
Infrastructure as code using Terranix (NixOS + Terraform/OpenTofu):
- **oci-trantor** - Oracle Cloud Infrastructure provisioning for Trantor server
- **cloudflare-baduhaidev** - DNS and CDN configuration for baduhai.dev domain
- **tailscale-tailnet** - Tailscale network ACL and device management
## Services
All services are accessible via custom domains under baduhai.dev:
- **Kanidm** (auth.baduhai.dev) - Identity and access management
- **Vaultwarden** (pass.baduhai.dev) - Password manager
- **Forgejo** (git.baduhai.dev) - Git forge (publicly accessible)
- **Nextcloud** (cloud.baduhai.dev) - File sync and collaboration
- **Jellyfin** (jellyfin.baduhai.dev) - Media server
Services are accessible via:
- LAN for alexandria-hosted services
- Tailscale VPN for all services
- Public internet for Forgejo only
## Notable Features
### Ephemeral Root
Rotterdam, io, and trantor use an ephemeral root filesystem that resets on every boot:
- Root filesystem is automatically rolled back using btrfs snapshots
- Old snapshots retained for 30 days
- Persistent data stored in dedicated subvolumes
- Implements truly stateless systems
### Custom DNS Architecture
- Unbound DNS servers on both alexandria and trantor
- Service routing based on visibility flags (public/LAN/Tailscale)
- Split-horizon DNS for optimal access paths
### Security
- LUKS full-disk encryption on desktop systems
- Fail2ban on public-facing servers
- agenix for secrets management
- Tailscale for secure remote access
### Desktop Environment
- Custom Niri window manager (Wayland compositor)
- Using forked version with auto-centering feature
- Stylix for consistent theming
### Development Setup
- Nix flakes for reproducible builds
- deploy-rs for automated deployments
- Podman for containerization
- Complete AI tooling integration

View file

@ -1,11 +1,15 @@
age-encryption.org/v1 age-encryption.org/v1
-> ssh-ed25519 Kfdnog IHXv4c5we36dCUsB1v8uEF23tIRlDQ/8WR1hX4GQ+Uc -> ssh-ed25519 Kfdnog HMpl/3mb59SsUvkDXXxO+odBNSc1dZS1nQtC8/BPlGI
Cwccw64BYBdSZUdkSqKESIU7E17cLNtiAZZ3Y1xV87A 4fPk0YtGxOoqXDfTN9kQlH0Pg2iaJXUZE5es6f317L4
-> ssh-ed25519 8YSAiw Ce3vdMG111ubjcFgd3+q2Qw2+7dsoUz7SiudtuLDr0Y -> ssh-ed25519 SP9f6A p1kh6UOFJ4xwulLY9IpbNZIJ7JSouR27j6HgK/XRegM
JUodwFsKfOTZXxFyRrEk/4gxJ4goPkwvYeThi893M0U rJCzN+RCdQgo/xCkAmcdN6GfXsoQhpmE1HuGwYs/2CI
-> ssh-ed25519 J6tVTA bExFuITTGXkTvhW25nushN7zT/PJGDoezsqu7fLKemI -> ssh-ed25519 8YSAiw cCbMOE3PMa3bzGGQSeQZuq074iwt4p4HLDu8uiHhWRY
4a90v0F4wgcZeqWBQ/EpqOZ9OCgT7qruwVvlGZeFmN8 U6wR/DuBdMKfbmQfUZ8XLdTBxNsUMR2lOueYucR5+bY
-> ssh-ed25519 Qt3Q+A j1oo46pNh1+yPEtxpgj+QPQPf5m82jL0DHGMacY8UFA -> ssh-ed25519 7cojTQ iLDzC79YtZcrqldzCyIFHrpsEapOXYD5AXuNoQ+3ulI
vy52Hl1WLTdKNA8+4p7A48Sg9+QkMXbECf/uxVMCLYk v2NiE3pA+J8Po+PqTTUU9XYKy37AIyj5KWdlh7FOPG8
--- 429vzgFnmFbEqDMwdvC0/EYDJlKU64YEGgE0AqPqlBs -> ssh-ed25519 J6tVTA Jw1pSpF1J2Ud46BDhdRCPErgUeim8uwWxiB1E3BiJB8
čď¬<E280BA>÷€b‰/!8Očô3Df®/ľŹ&kNQhuůr“t¤%&]ł˛ÎŐŇÖucÚjŮHĆ]Ż_łž¨ë5@D$<1C>>éN8Ϧ >Ť9:®CvĐѦ69W'X·]X^çŰĆ»$Ť§}|cš÷ă/ž ߸={ľuÉłs hGdGFi46iqKJN0QviG1xRNf2kwlls0rM5k3LkAiw8jY
-> ssh-ed25519 Kl5yTQ yXnKCsJNjTSQYiPuv6dAF6raB3EFcg2oag1cBVkdvwk
0hwjrZpclTrFWtr5JqAbwXImYzZwTJOJPkhNnlHmPeo
--- k97ZU6FfTWVqBwNcrF9QeEbnqnuQUQ9pR1qM/Sgjh7A
#Ž©L-qï¹hr­÷&§ý*Åáh¢¡NÂb®qÒܦHÅ G=@öâ¿uak”u™r^£BOoÔ}:Æ5ju€Å$%®¼éË".A1ÿäqŽP\Œ2CÏä RŽÜ¤¨¶_ Sû²‰=”(Æ%sÞ¨†ó§Çï£ß

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,35 +1,29 @@
let let
io-user = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO3Y0PVpGfJHonqDS7qoCFhqzUvqGq9I9sax+F9e/5cs user@io"; io-user = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO3Y0PVpGfJHonqDS7qoCFhqzUvqGq9I9sax+F9e/5cs";
io = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKCIrKJk5zWzWEHvLMPMK8T3PyeBjsCsqzxPN+OrXfhA root@io"; io-host = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKCIrKJk5zWzWEHvLMPMK8T3PyeBjsCsqzxPN+OrXfhA";
io = [
io-user
io-host
];
rotterdam-user = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA1v3+q3EaruiiStWjubEJWvtejam/r41uoOpCdwJtLL user@rotterdam"; rotterdam-user = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA1v3+q3EaruiiStWjubEJWvtejam/r41uoOpCdwJtLL";
rotterdam = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIjXcqQqlu03x2VVTdWOyxtKRszXAKX0AxTkGvF1oeJL root@rotterdam"; rotterdam-host = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIjXcqQqlu03x2VVTdWOyxtKRszXAKX0AxTkGvF1oeJL";
rotterdam = [
rotterdam-user
rotterdam-host
];
alexandria = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK95QueW+jp1ZmF299Xr3XkgHJ6dL7aZVsfWxqbOKVKA root@alexandria"; alexandria-host = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK95QueW+jp1ZmF299Xr3XkgHJ6dL7aZVsfWxqbOKVKA";
alexandria = [ alexandria-host ];
trantor = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIh/2u5pr/iPVeavlsor5hbTtsgUfP1JpzZVco2YQAo3 root@trantor"; trantor-host = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINkGuGLZPnYJbCGY4BhJ9uTupp6ruuR1NZ7FEYEaLPA7";
trantor = [ trantor-host ];
desktops = io ++ rotterdam;
servers = alexandria ++ trantor;
all-hosts = desktops ++ servers;
in in
{ {
"cloudflare.age".publicKeys = [ "cloudflare.age".publicKeys = all-hosts;
io-user "webdav.age".publicKeys = all-hosts;
rotterdam-user
alexandria
trantor
];
"nextcloud-adminpass.age".publicKeys = [
io-user
rotterdam-user
alexandria
];
"nextcloud-secrets.json.age".publicKeys = [
io-user
rotterdam-user
alexandria
];
"forgejo-root-password.age".publicKeys = [
io-user
rotterdam-user
trantor
];
} }

15
secrets/webdav.age Normal file
View file

@ -0,0 +1,15 @@
age-encryption.org/v1
-> ssh-ed25519 Kfdnog 9oKx6Oz/J/QJ0mmgoLX5AUx0sFdxnPVnjF42bElPSXA
BJ6h4lHGDsf1Npc4bwkvz5htGRT/x/b2bs9WFM2W/pc
-> ssh-ed25519 SP9f6A T5t4apynXLYN/4YEvaHRCI28rrKzet4r6LrbAye5VGk
BsXkZYBxG9zcfLYCd9H0+LW078oCDyYx9zG+DPfE7bA
-> ssh-ed25519 8YSAiw RY0YR30qyJPvhy7eTJLoj2JXpH9qHP43fJaHilJykXM
E5/P0Egz/LKwEhYLYd5Cnrat47gnYn93yDSeYgLi934
-> ssh-ed25519 7cojTQ qTCTw7CjilThFLmXYph4YhVBhnk1DpnFCGwgioo/XB0
N31nZ8nInQuddLD3b0bxI5Es/pTvTQD8nz0f/AZtNFg
-> ssh-ed25519 J6tVTA 7OawDsWwtVxu76ZgF0dFclMr19sBNdtu7H+Tr7Pd+SQ
hhVKcscIKIH1WChhRo/RYqUWy1rgs/EKnlHr9uY7QrQ
-> ssh-ed25519 Kl5yTQ +i2Q3uNHw1jAVH76NHy4QbjCc6sBBYjsbr7w4mLaHW4
JOJ02zU0+IxlbXMBsW4UrvzvLUbifdzABBNL+bc0bBs
--- W40oEFdBUKbi0teNTc6B1sX0ReHDvkIJcBm1dlROnk8
zÒ¼ÆCnçç®±àÈeÓ­ù7{ïÛé{Nðì§ä³6ê•azÁ"ÒÉE¯HB/¢BYbD

View file

@ -1,48 +0,0 @@
# Shared service definitions for cross-host configuration
# Used by:
# - alexandria: DNS server (LAN) + service hosting (vaultwarden, nextcloud, jellyfin, kanidm)
# - trantor: DNS server (Tailnet) + service hosting (forgejo)
{
services = [
{
name = "kanidm";
domain = "auth.baduhai.dev";
host = "alexandria";
lanIP = "192.168.15.142";
tailscaleIP = "100.76.19.50";
port = 8443;
}
{
name = "vaultwarden";
domain = "pass.baduhai.dev";
host = "alexandria";
lanIP = "192.168.15.142";
tailscaleIP = "100.76.19.50";
port = 8222;
}
{
name = "forgejo";
domain = "git.baduhai.dev";
host = "trantor";
public = true;
tailscaleIP = "100.108.5.90";
port = 3000;
}
{
name = "nextcloud";
domain = "cloud.baduhai.dev";
host = "alexandria";
lanIP = "192.168.15.142";
tailscaleIP = "100.76.19.50";
port = 443;
}
{
name = "jellyfin";
domain = "jellyfin.baduhai.dev";
host = "alexandria";
lanIP = "192.168.15.142";
tailscaleIP = "100.76.19.50";
port = 8096;
}
];
}

View file

@ -1,86 +0,0 @@
# Required environment variables:
# CLOUDFLARE_API_TOKEN - API token with "Edit zone DNS" permissions
# AWS_ACCESS_KEY_ID - Cloudflare R2 access key for state storage
# AWS_SECRET_ACCESS_KEY - Cloudflare R2 secret key for state storage
{ config, lib, ... }:
let
inherit (import ../../shared/services.nix) services;
# Helper to extract subdomain from full domain (e.g., "git.baduhai.dev" -> "git")
getSubdomain = domain: lib.head (lib.splitString "." domain);
# Generate DNS records for services
# Public services point to trantor's public IP
# Private services point to their tailscale IP
mkServiceRecords = lib.listToAttrs (
lib.imap0 (
i: svc:
let
subdomain = getSubdomain svc.domain;
targetIP =
if svc.public or false then
config.data.terraform_remote_state.trantor "outputs.instance_public_ip"
else
svc.tailscaleIP;
in
{
name = "service_${toString i}";
value = {
zone_id = config.variable.zone_id.default;
name = subdomain;
type = "A";
content = targetIP;
proxied = false;
ttl = 3600;
};
}
) services
);
in
{
terraform.required_providers.cloudflare = {
source = "cloudflare/cloudflare";
version = "~> 5.0";
};
terraform.backend.s3 = {
bucket = "terraform-state";
key = "cloudflare/baduhai.dev.tfstate";
region = "auto";
endpoint = "https://fcdf920bde00c3d013ee541f984da70e.r2.cloudflarestorage.com";
skip_credentials_validation = true;
skip_metadata_api_check = true;
skip_region_validation = true;
skip_requesting_account_id = true;
use_path_style = true;
};
variable = {
zone_id = {
default = "c63a8332fdddc4a8e5612ddc54557044";
type = "string";
};
};
data = {
terraform_remote_state.trantor = {
backend = "s3";
config = {
bucket = "terraform-state";
key = "oci/trantor.tfstate";
region = "auto";
endpoint = "https://fcdf920bde00c3d013ee541f984da70e.r2.cloudflarestorage.com";
skip_credentials_validation = true;
skip_metadata_api_check = true;
skip_region_validation = true;
skip_requesting_account_id = true;
use_path_style = true;
};
};
};
resource.cloudflare_dns_record = mkServiceRecords;
}

View file

@ -1,258 +0,0 @@
# Required environment variables:
# instead of OCI variables, ~/.oci/config may also be used
# OCI_TENANCY_OCID - Oracle tenancy OCID (or use TF_VAR_* to override variables)
# OCI_USER_OCID - Oracle user OCID
# OCI_FINGERPRINT - API key fingerprint
# OCI_PRIVATE_KEY_PATH - Path to OCI API private key
# AWS variables are required
# AWS_ACCESS_KEY_ID - Cloudflare R2 access key for state storage
# AWS_SECRET_ACCESS_KEY - Cloudflare R2 secret key for state storage
{ config, ... }:
{
terraform.required_providers.oci = {
source = "oracle/oci";
version = "~> 7.0";
};
provider.oci.region = "sa-saopaulo-1";
terraform.backend.s3 = {
bucket = "terraform-state";
key = "oci/trantor.tfstate";
region = "auto";
endpoint = "https://fcdf920bde00c3d013ee541f984da70e.r2.cloudflarestorage.com";
skip_credentials_validation = true;
skip_metadata_api_check = true;
skip_region_validation = true;
skip_requesting_account_id = true;
use_path_style = true;
};
variable = {
tenancy_ocid = {
default = "ocid1.tenancy.oc1..aaaaaaaap3vfdz4piygqza6e6zqunbcuso43ddqfo3ydmpmnomidyghh7rvq";
type = "string";
};
compartment_name = {
default = "trantor";
type = "string";
};
vcn_cidr = {
default = "10.0.0.0/24";
type = "string";
};
instance_name = {
default = "trantor";
type = "string";
};
ssh_public_keys = {
default = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICQPkAyy+Du9Omc2WtnUF2TV8jFAF4H6mJi2D4IZ1nzg user@himalia"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO3Y0PVpGfJHonqDS7qoCFhqzUvqGq9I9sax+F9e/5cs user@io"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA1v3+q3EaruiiStWjubEJWvtejam/r41uoOpCdwJtLL user@rotterdam"
];
type = "list(string)";
};
};
data = {
oci_identity_availability_domains.ads = {
compartment_id = config.variable.tenancy_ocid.default;
};
oci_core_images.ubuntu_arm = {
compartment_id = config.variable.tenancy_ocid.default;
operating_system = "Canonical Ubuntu";
operating_system_version = "24.04";
shape = "VM.Standard.A1.Flex";
sort_by = "TIMECREATED";
sort_order = "DESC";
};
};
resource = {
oci_identity_compartment.trantor = {
compartment_id = config.variable.tenancy_ocid.default;
description = "trantor infrastructure compartment";
name = config.variable.compartment_name.default;
};
oci_core_vcn.vcn = {
compartment_id = config.resource.oci_identity_compartment.trantor "id";
cidr_blocks = [ config.variable.vcn_cidr.default ];
display_name = "trantor-vcn";
dns_label = "trantor";
};
oci_core_internet_gateway.ig = {
compartment_id = config.resource.oci_identity_compartment.trantor "id";
vcn_id = config.resource.oci_core_vcn.vcn "id";
display_name = "trantor-ig";
enabled = true;
};
oci_core_route_table.rt = {
compartment_id = config.resource.oci_identity_compartment.trantor "id";
vcn_id = config.resource.oci_core_vcn.vcn "id";
display_name = "trantor-rt";
route_rules = [
{
network_entity_id = config.resource.oci_core_internet_gateway.ig "id";
destination = "0.0.0.0/0";
destination_type = "CIDR_BLOCK";
}
];
};
oci_core_security_list.sl = {
compartment_id = config.resource.oci_identity_compartment.trantor "id";
vcn_id = config.resource.oci_core_vcn.vcn "id";
display_name = "trantor-sl";
egress_security_rules = [
{
destination = "0.0.0.0/0";
protocol = "all";
stateless = false;
}
];
ingress_security_rules = [
{
protocol = "6"; # TCP
source = "0.0.0.0/0";
stateless = false;
tcp_options = {
min = 22;
max = 22;
};
}
{
protocol = "6"; # TCP
source = "0.0.0.0/0";
stateless = false;
tcp_options = {
min = 80;
max = 80;
};
}
{
protocol = "6"; # TCP
source = "0.0.0.0/0";
stateless = false;
tcp_options = {
min = 443;
max = 443;
};
}
{
protocol = "6"; # TCP
source = "0.0.0.0/0";
stateless = false;
tcp_options = {
min = 25565;
max = 25565;
};
}
{
protocol = "6"; # TCP
source = "0.0.0.0/0";
stateless = false;
tcp_options = {
min = 19132;
max = 19133;
};
}
{
protocol = "17"; # UDP
source = "0.0.0.0/0";
stateless = false;
udp_options = {
min = 19132;
max = 19133;
};
}
];
};
oci_core_subnet.subnet = {
compartment_id = config.resource.oci_identity_compartment.trantor "id";
vcn_id = config.resource.oci_core_vcn.vcn "id";
cidr_block = config.variable.vcn_cidr.default;
display_name = "trantor-subnet";
dns_label = "subnet";
route_table_id = config.resource.oci_core_route_table.rt "id";
security_list_ids = [ (config.resource.oci_core_security_list.sl "id") ];
prohibit_public_ip_on_vnic = false;
};
oci_core_instance.trantor = {
availability_domain = config.data.oci_identity_availability_domains.ads "availability_domains[0].name";
compartment_id = config.resource.oci_identity_compartment.trantor "id";
display_name = config.variable.instance_name.default;
shape = "VM.Standard.A1.Flex";
shape_config = {
ocpus = 2;
memory_in_gbs = 12;
};
source_details = {
source_type = "image";
source_id = config.data.oci_core_images.ubuntu_arm "images[0].id";
boot_volume_size_in_gbs = 100;
};
create_vnic_details = {
subnet_id = config.resource.oci_core_subnet.subnet "id";
display_name = "trantor-vnic";
assign_public_ip = true;
hostname_label = config.variable.instance_name.default;
};
metadata = {
ssh_authorized_keys = builtins.concatStringsSep "\n" config.variable.ssh_public_keys.default;
};
preserve_boot_volume = false;
};
oci_budget_budget.trantor_budget = {
compartment_id = config.variable.tenancy_ocid.default;
targets = [ (config.resource.oci_identity_compartment.trantor "id") ];
amount = 1;
reset_period = "MONTHLY";
display_name = "trantor-budget";
description = "Monthly budget for trantor compartment";
target_type = "COMPARTMENT";
};
oci_budget_alert_rule.daily_spend_alert = {
budget_id = config.resource.oci_budget_budget.trantor_budget "id";
type = "ACTUAL";
threshold = 5;
threshold_type = "PERCENTAGE";
display_name = "daily-spend-alert";
recipients = "baduhai@proton.me";
description = "Alert when daily spending exceeds $0.05";
message = "Daily spending has exceeded $0.05 in the trantor compartment";
};
};
output = {
compartment_id = {
value = config.resource.oci_identity_compartment.trantor "id";
};
instance_public_ip = {
value = config.resource.oci_core_instance.trantor "public_ip";
};
};
}

View file

@ -1,43 +0,0 @@
# Required environment variables:
# TAILSCALE_API_KEY - Tailscale API key with appropriate permissions
# TAILSCALE_TAILNET - Your tailnet name (e.g., "user@example.com" or "example.org.github")
# AWS_ACCESS_KEY_ID - Cloudflare R2 access key for state storage
# AWS_SECRET_ACCESS_KEY - Cloudflare R2 secret key for state storage
{ config, ... }:
{
terraform.required_providers.tailscale = {
source = "tailscale/tailscale";
version = "~> 0.17";
};
terraform.backend.s3 = {
bucket = "terraform-state";
key = "tailscale/tailnet.tfstate";
region = "auto";
endpoint = "https://fcdf920bde00c3d013ee541f984da70e.r2.cloudflarestorage.com";
skip_credentials_validation = true;
skip_metadata_api_check = true;
skip_region_validation = true;
skip_requesting_account_id = true;
use_path_style = true;
};
variable = {
trantor_tailscale_ip = {
default = "100.108.5.90";
type = "string";
};
};
resource = {
tailscale_dns_nameservers.global = {
nameservers = [
config.variable.trantor_tailscale_ip.default
"1.1.1.1"
"1.0.0.1"
];
};
};
}

View file

@ -1,27 +0,0 @@
{ inputs, ... }:
{
imports = [
inputs.terranix.flakeModule
];
perSystem =
{ pkgs, ... }:
{
terranix.terranixConfigurations = {
oci-trantor = {
modules = [ ./terranix/oci/trantor.nix ];
terraformWrapper.package = pkgs.opentofu;
};
cloudflare-baduhaidev = {
modules = [ ./terranix/cloudflare/baduhai.dev.nix ];
terraformWrapper.package = pkgs.opentofu;
};
tailscale-tailnet = {
modules = [ ./terranix/tailscale/tailnet.nix ];
terraformWrapper.package = pkgs.opentofu;
};
};
};
}

View file

@ -1,7 +0,0 @@
{ inputs, ... }:
{
imports = [ inputs.nix-index-database.homeModules.nix-index ];
programs.nix-index-database.comma.enable = true;
}

View file

@ -3,10 +3,7 @@
{ {
programs.fish = { programs.fish = {
enable = true; enable = true;
interactiveShellInit = '' interactiveShellInit = "${lib.getExe pkgs.nix-your-shell} fish | source";
set fish_greeting
${lib.getExe pkgs.nix-your-shell} fish | source
'';
loginShellInit = "${lib.getExe pkgs.nix-your-shell} fish | source"; loginShellInit = "${lib.getExe pkgs.nix-your-shell} fish | source";
plugins = [ plugins = [
{ {
@ -18,15 +15,24 @@
sha256 = "sha256-A8ydBX4LORk+nutjHurqNNWFmW6LIiBPQcxS3x4nbeQ="; sha256 = "sha256-A8ydBX4LORk+nutjHurqNNWFmW6LIiBPQcxS3x4nbeQ=";
}; };
} }
{
name = "sponge";
src = pkgs.fetchFromGitHub {
owner = "meaningful-ooo";
repo = "sponge";
rev = "384299545104d5256648cee9d8b117aaa9a6d7be";
sha256 = "sha256-MdcZUDRtNJdiyo2l9o5ma7nAX84xEJbGFhAVhK+Zm1w=";
};
}
{ {
name = "z"; name = "z";
src = pkgs.fetchFromGitHub { src = pkgs.fetchFromGitHub {
owner = "jethrokuan"; owner = "jethrokuan";
repo = "z"; repo = "z";
rev = "067e867debee59aee231e789fc4631f80fa5788e"; rev = "85f863f20f24faf675827fb00f3a4e15c7838d76";
sha256 = "sha256-emmjTsqt8bdI5qpx1bAzhVACkg0MNB/uffaRjjeuFxU="; sha256 = "sha256-+FUBM7CodtZrYKqU542fQD+ZDGrd2438trKM0tIESs0=";
}; };
} }
]; ];
}; };
} }

56
users/modules/desktop.nix Normal file
View file

@ -0,0 +1,56 @@
{ inputs, pkgs, ... }:
{
imports = [ inputs.dms.homeModules.dankMaterialShell.default ];
fonts.fontconfig.enable = true;
programs = {
dankMaterialShell = {
enable = true;
enableVPN = false;
};
rio = {
enable = true;
settings = {
theme = "catppuccin-mocha";
fonts = {
family = "FiraCode Nerd Font";
size = 16.0;
emoji.family = "Noto Color Emoji";
};
confirm-before-quit = false;
window = {
width = 1121;
height = 633;
};
};
};
password-store = {
enable = true;
package = pkgs.pass-wayland;
};
};
xdg.portal = {
enable = true;
xdgOpenUsePortal = true;
extraPortals = with pkgs; [
kdePackages.xdg-desktop-portal-kde
xdg-desktop-portal-gtk
xdg-desktop-portal-gnome
];
};
gtk = {
enable = true;
gtk3.extraConfig = {
gtk-decoration-layout = "appmenu:";
};
gtk4.extraConfig = {
gtk-decoration-layout = "appmenu:";
};
};
}

View file

@ -1,132 +0,0 @@
{
inputs,
pkgs,
...
}:
{
imports = [ inputs.vicinae.homeManagerModules.default ];
fonts.fontconfig.enable = true;
home.packages = with pkgs; [ xwayland-satellite ];
services.vicinae = {
enable = true;
autoStart = true;
};
programs = {
ghostty = {
enable = true;
settings = {
cursor-style = "block";
shell-integration-features = "no-cursor";
cursor-style-blink = false;
custom-shader = "${builtins.fetchurl {
url = "https://raw.githubusercontent.com/hackr-sh/ghostty-shaders/cb6eb4b0d1a3101c869c62e458b25a826f9dcde3/cursor_blaze.glsl";
sha256 = "sha256:0g2lgqjdrn3c51glry7x2z30y7ml0y61arl5ykmf4yj0p85s5f41";
}}";
bell-features = "";
gtk-titlebar-style = "tabs";
keybind = [ "shift+enter=text:\\x1b\\r" ];
};
};
password-store = {
enable = true;
package = pkgs.pass-wayland;
};
};
xdg = {
enable = true;
userDirs.enable = true;
mimeApps = {
enable = true;
defaultApplications = {
"text/html" = [
"re.sonny.Junction.desktop"
"zen-browser.desktop"
"torbrowser.desktop"
];
"x-scheme-handler/http" = [
"re.sonny.Junction.desktop"
"zen-browser.desktop"
"torbrowser.desktop"
];
"x-scheme-handler/https" = [
"re.sonny.Junction.desktop"
"zen-browser.desktop"
"torbrowser.desktop"
];
"x-scheme-handler/about" = [
"re.sonny.Junction.desktop"
"zen-browser.desktop"
"torbrowser.desktop"
];
"x-scheme-handler/unknown" = [
"re.sonny.Junction.desktop"
"zen-browser.desktop"
"torbrowser.desktop"
];
"image/jpeg" = "org.gnome.Loupe.desktop";
"image/png" = "org.gnome.Loupe.desktop";
"image/gif" = "org.gnome.Loupe.desktop";
"image/webp" = "org.gnome.Loupe.desktop";
"image/bmp" = "org.gnome.Loupe.desktop";
"image/svg+xml" = "org.gnome.Loupe.desktop";
"image/tiff" = "org.gnome.Loupe.desktop";
"video/mp4" = "io.bassi.Showtime.desktop";
"video/x-matroska" = "io.bassi.Showtime.desktop";
"video/webm" = "io.bassi.Showtime.desktop";
"video/mpeg" = "io.bassi.Showtime.desktop";
"video/x-msvideo" = "io.bassi.Showtime.desktop";
"video/quicktime" = "io.bassi.Showtime.desktop";
"video/x-flv" = "io.bassi.Showtime.desktop";
"audio/mpeg" = "io.bassi.Showtime.desktop";
"audio/flac" = "io.bassi.Showtime.desktop";
"audio/ogg" = "io.bassi.Showtime.desktop";
"audio/wav" = "io.bassi.Showtime.desktop";
"audio/mp4" = "io.bassi.Showtime.desktop";
"audio/x-opus+ogg" = "io.bassi.Showtime.desktop";
"application/pdf" = [
"org.gnome.Papers.desktop"
"zen-browser.desktop"
];
"text/plain" = "Helix.desktop";
"text/markdown" = "Helix.desktop";
"text/x-log" = "Helix.desktop";
"application/x-shellscript" = "Helix.desktop";
"application/vnd.openxmlformats-officedocument.wordprocessingml.document" =
"onlyoffice-desktopeditors.desktop"; # DOCX
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" =
"onlyoffice-desktopeditors.desktop"; # XLSX
"application/vnd.openxmlformats-officedocument.presentationml.presentation" =
"onlyoffice-desktopeditors.desktop"; # PPTX
"application/vnd.oasis.opendocument.text" = "onlyoffice-desktopeditors.desktop"; # ODT
"application/vnd.oasis.opendocument.spreadsheet" = "onlyoffice-desktopeditors.desktop"; # ODS
"application/vnd.oasis.opendocument.presentation" = "onlyoffice-desktopeditors.desktop"; # ODP
"application/msword" = "onlyoffice-desktopeditors.desktop"; # DOC
"application/vnd.ms-excel" = "onlyoffice-desktopeditors.desktop"; # XLS
"application/vnd.ms-powerpoint" = "onlyoffice-desktopeditors.desktop"; # PPT
"application/zip" = "org.gnome.FileRoller.desktop";
"application/x-tar" = "org.gnome.FileRoller.desktop";
"application/x-compressed-tar" = "org.gnome.FileRoller.desktop";
"application/x-bzip-compressed-tar" = "org.gnome.FileRoller.desktop";
"application/x-xz-compressed-tar" = "org.gnome.FileRoller.desktop";
"application/x-7z-compressed" = "org.gnome.FileRoller.desktop";
"application/x-rar" = "org.gnome.FileRoller.desktop";
"application/gzip" = "org.gnome.FileRoller.desktop";
"application/x-bzip" = "org.gnome.FileRoller.desktop";
"inode/directory" = "org.gnome.Nautilus.desktop";
};
};
};
# Set Ghostty as default terminal
home.sessionVariables = {
TERMINAL = "ghostty";
};
}

View file

@ -1,229 +0,0 @@
{
inputs,
lib,
pkgs,
hostname ? null,
...
}:
let
isRotterdam = hostname == "rotterdam";
in
{
imports = [ inputs.noctalia.homeModules.default ];
services.kanshi = {
enable = true;
settings = [
{
profile.name = "default";
profile.outputs = [
{
criteria = "*";
scale = 1.0;
}
];
}
];
};
home = {
packages = with pkgs; [
xwayland-satellite
inputs.noctalia.packages.${pkgs.system}.default
];
sessionVariables.QT_QPA_PLATFORMTHEME = "gtk3";
};
xdg.configFile."niri/config.kdl".text = ''
input {
keyboard {
xkb {
layout "us"
variant "altgr-intl"
}
}
touchpad {
tap
dwt
drag true
drag-lock
natural-scroll
accel-speed 0.2
accel-profile "flat"
scroll-method "two-finger"
middle-emulation
}
mouse {
natural-scroll
accel-speed 0.2
accel-profile "flat"
}
warp-mouse-to-focus mode="center-xy"
focus-follows-mouse
}
layout {
gaps 8
center-focused-column "never"
auto-center-when-space-available
preset-column-widths {
${
if isRotterdam then
''
proportion 0.33333
proportion 0.5
proportion 0.66667
''
else
''
proportion 0.5
proportion 1.0
''
}
}
default-column-width { proportion ${if isRotterdam then "0.33333" else "0.5"}; }
focus-ring {
off
}
border {
width 4
active-color "#ffc87f"
inactive-color "#505050"
urgent-color "#9b0000"
}
tab-indicator {
width 4
gap 4
place-within-column
}
}
overview {
zoom 0.65
}
spawn-at-startup "noctalia-shell" "-d"
layer-rule {
match namespace="^wallpaper$"
place-within-backdrop true
}
layer-rule {
match namespace="^quickshell-overview$"
place-within-backdrop true
}
hotkey-overlay {
skip-at-startup
}
prefer-no-csd
screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png"
animations {
slowdown 0.3
}
window-rule {
match app-id="zen"
default-column-width { proportion ${if isRotterdam then "0.5" else "1.0"}; }
}
window-rule {
geometry-corner-radius 12
clip-to-geometry true
}
config-notification {
disable-failed
}
binds {
Alt+Space repeat=false { spawn "vicinae" "toggle"; }
XF86AudioRaiseVolume allow-when-locked=true { spawn "noctalia-shell" "ipc" "call" "volume" "increase"; }
XF86AudioLowerVolume allow-when-locked=true { spawn "noctalia-shell" "ipc" "call" "volume" "decrease"; }
XF86AudioMute allow-when-locked=true { spawn "noctalia-shell" "ipc" "call" "volume" "muteOutput"; }
XF86MonBrightnessUp allow-when-locked=true { spawn "noctalia-shell" "ipc" "call" "brightness" "increase"; }
XF86MonBrightnessDown allow-when-locked=true { spawn "noctalia-shell" "ipc" "call" "brightness" "decrease"; }
XF86AudioPlay allow-when-locked=true { spawn "${lib.getExe pkgs.playerctl}" "play-pause"; }
XF86AudioStop allow-when-locked=true { spawn "${lib.getExe pkgs.playerctl}" "stop"; }
XF86AudioPrev allow-when-locked=true { spawn "${lib.getExe pkgs.playerctl}" "previous"; }
XF86AudioNext allow-when-locked=true { spawn "${lib.getExe pkgs.playerctl}" "next"; }
Mod+V repeat=false { spawn "vicinae" "vicinae://extensions/vicinae/clipboard/history"; }
Mod+Shift+L repeat=false { spawn "noctalia-shell" "ipc" "call" "lockScreen" "lock"; }
Mod+Return { spawn "ghostty"; }
Ctrl+Alt+Shift+A allow-when-locked=true { spawn "toggleaudiosink"; }
Mod+W repeat=false { toggle-overview; }
Mod+Q { close-window; }
Alt+Shift+Q { close-window;}
Mod+Shift+Q { close-window; }
Alt+F4 { close-window; }
Mod+Left { focus-column-left; }
Mod+Down { focus-window-or-workspace-down; }
Mod+Up { focus-window-or-workspace-up; }
Mod+Right { focus-column-right; }
Mod+H { focus-column-left; }
Mod+L { focus-column-right; }
Mod+J { focus-window-or-workspace-down; }
Mod+K { focus-window-or-workspace-up; }
Mod+Ctrl+Left { move-column-left; }
Mod+Ctrl+Down { move-window-down-or-to-workspace-down; }
Mod+Ctrl+Up { move-window-up-or-to-workspace-up; }
Mod+Ctrl+Right { move-column-right; }
Mod+Ctrl+H { move-column-left; }
Mod+Ctrl+J { move-window-down-or-to-workspace-down; }
Mod+Ctrl+K { move-window-up-or-to-workspace-up; }
Mod+Ctrl+L { move-column-right; }
Mod+Home { focus-column-first; }
Mod+End { focus-column-last; }
Mod+Ctrl+Home { move-column-to-first; }
Mod+Ctrl+End { move-column-to-last; }
Mod+Alt+Left { focus-monitor-left; }
Mod+Alt+Down { focus-monitor-down; }
Mod+Alt+Up { focus-monitor-up; }
Mod+Alt+Right { focus-monitor-right; }
Mod+Alt+H { focus-monitor-left; }
Mod+Alt+J { focus-monitor-down; }
Mod+Alt+K { focus-monitor-up; }
Mod+Alt+L { focus-monitor-right; }
Mod+Alt+Ctrl+Left { move-column-to-monitor-left; }
Mod+Alt+Ctrl+Down { move-column-to-monitor-down; }
Mod+Alt+Ctrl+Up { move-column-to-monitor-up; }
Mod+Alt+Ctrl+Right { move-column-to-monitor-right; }
Mod+Alt+Ctrl+H { move-column-to-monitor-left; }
Mod+Alt+Ctrl+J { move-column-to-monitor-down; }
Mod+Alt+Ctrl+K { move-column-to-monitor-up; }
Mod+Alt+Ctrl+L { move-column-to-monitor-right; }
Mod+Ctrl+U { move-workspace-down; }
Mod+Ctrl+I { move-workspace-up; }
Mod+WheelScrollDown cooldown-ms=150 { focus-workspace-down; }
Mod+WheelScrollUp cooldown-ms=150 { focus-workspace-up; }
Mod+Ctrl+WheelScrollDown cooldown-ms=150 { move-column-to-workspace-down; }
Mod+Ctrl+WheelScrollUp cooldown-ms=150 { move-column-to-workspace-up; }
Mod+Shift+WheelScrollDown { focus-column-right; }
Mod+Shift+WheelScrollUp { focus-column-left; }
Mod+Ctrl+Shift+WheelScrollDown { move-column-right; }
Mod+Ctrl+Shift+WheelScrollUp { move-column-left; }
Mod+BracketLeft { consume-or-expel-window-left; }
Mod+BracketRight { consume-or-expel-window-right; }
Mod+Comma { consume-window-into-column; }
Mod+Period { expel-window-from-column; }
Mod+R { switch-preset-column-width; }
Mod+F { maximize-column; }
Mod+Ctrl+F { fullscreen-window; }
Mod+C { center-visible-columns; }
Mod+Ctrl+C { center-column; }
Mod+Space { toggle-window-floating; }
Mod+Ctrl+Space { switch-focus-between-floating-and-tiling; }
Mod+T { toggle-column-tabbed-display; }
Print { screenshot-screen; }
Mod+Print { screenshot; }
Ctrl+Print { screenshot-window; }
Mod+Backspace allow-inhibiting=false { toggle-keyboard-shortcuts-inhibit; }
Mod+Alt+E { spawn "noctalia-shell" "ipc" "call" "sessionMenu" "toggle"; }
Ctrl+Alt+Delete { spawn "noctalia-shell" "ipc" "call" "sessionMenu" "toggle"; }
Mod+Ctrl+P { power-off-monitors; }
}
'';
}

View file

@ -8,12 +8,13 @@
programs.helix = { programs.helix = {
enable = true; enable = true;
settings = { settings = {
theme = "base16_transparent";
editor = { editor = {
file-picker.hidden = false; file-picker.hidden = false;
idle-timeout = 0; idle-timeout = 0;
line-number = "relative"; line-number = "relative";
cursor-shape = { cursor-shape = {
normal = "underline"; normal = "block";
insert = "bar"; insert = "bar";
select = "underline"; select = "underline";
}; };
@ -46,4 +47,4 @@
]; ];
}; };
}; };
} }

View file

@ -13,10 +13,10 @@
''; '';
right_format = "$cmd_duration$character"; right_format = "$cmd_duration$character";
hostname = { hostname = {
ssh_symbol = "󰖟 "; ssh_symbol = " ";
}; };
character = { character = {
error_symbol = "[](red)"; error_symbol = "[](red)";
success_symbol = "[󱐋](green)"; success_symbol = "[󱐋](green)";
}; };
cmd_duration = { cmd_duration = {
@ -25,7 +25,7 @@
min_time = 500; min_time = 500;
}; };
git_branch = { git_branch = {
symbol = " "; symbol = " ";
style = "purple"; style = "purple";
}; };
git_status.style = "red"; git_status.style = "red";
@ -37,4 +37,4 @@
}; };
}; };
}; };
} }

View file

@ -1,69 +0,0 @@
{
config,
inputs,
pkgs,
...
}:
{
imports = [
inputs.stylix.homeModules.stylix
inputs.zen-browser.homeModules.beta
];
stylix = {
enable = true;
polarity = "dark";
base16Scheme = "${pkgs.base16-schemes}/share/themes/tokyodark.yaml";
cursor = {
package = pkgs.kdePackages.breeze;
name = "breeze_cursors";
size = 24;
};
icons = {
enable = true;
package = pkgs.morewaita-icon-theme;
light = "MoreWaita";
dark = "MoreWaita";
};
opacity = {
applications = 1.0;
desktop = 0.8;
popups = config.stylix.opacity.desktop;
terminal = 1.0;
};
fonts = {
serif = {
package = pkgs.source-serif;
name = "Source Serif 4 Display";
};
sansSerif = {
package = pkgs.inter;
name = "Inter";
};
monospace = {
package = pkgs.nerd-fonts.fira-code;
name = "FiraCode Nerd Font";
};
emoji = {
package = pkgs.noto-fonts-color-emoji;
name = "Noto Color Emoji";
};
sizes = {
applications = 10;
desktop = config.stylix.fonts.sizes.applications;
popups = config.stylix.fonts.sizes.applications;
terminal = 12;
};
};
targets.zen-browser = {
enable = true;
profileNames = [ "william" ];
};
};
programs.zen-browser = {
enable = true;
profiles.william = { };
};
}

View file

@ -1,17 +1,10 @@
{ pkgs, ... }: { pkgs, ... }:
{ {
programs = { programs.git = {
git = { enable = true;
enable = true; diff-so-fancy.enable = true;
settings.user = { userName = "William";
name = "William"; userEmail = "baduhai@proton.me";
email = "baduhai@proton.me";
};
};
diff-so-fancy = {
enable = true;
enableGitIntegration = true;
};
}; };
} }

View file

@ -1,5 +1,4 @@
{ inputs, lib }: { inputs, lib }:
let let
inherit (inputs) inherit (inputs)
self self
@ -8,14 +7,8 @@ let
home-manager home-manager
agenix agenix
; ;
# Import shared service definitions
sharedServices = import ./shared/services.nix;
in in
{ {
# Re-export shared services for use in host configs
inherit (sharedServices) services;
# Tag-based host configuration system # Tag-based host configuration system
mkHost = mkHost =
{ {
@ -104,10 +97,9 @@ in
}; };
# Tag-based user configuration system # Tag-based user configuration system
mkHome = mkUser =
{ {
username, username,
hostname ? null,
homeDirectory ? "/home/${username}", homeDirectory ? "/home/${username}",
tags ? [ ], tags ? [ ],
extraModules ? [ ], extraModules ? [ ],
@ -171,7 +163,7 @@ in
home-manager.lib.homeManagerConfiguration { home-manager.lib.homeManagerConfiguration {
inherit pkgs; inherit pkgs;
extraSpecialArgs = { extraSpecialArgs = {
inherit inputs hostname; inherit inputs;
userTags = allTags; userTags = allTags;
}; };
modules = allModules ++ [ modules = allModules ++ [
@ -183,41 +175,16 @@ in
# Nginx virtual host utilities # Nginx virtual host utilities
mkNginxVHosts = mkNginxVHosts =
{ domains }: {
acmeHost,
domains,
}:
let let
# Extract domain name and apply it as useACMEHost commonVHostConfig = {
mkVHostConfig = domain: config: useACMEHost = acmeHost;
lib.recursiveUpdate { forceSSL = true;
useACMEHost = domain; kTLS = true;
forceSSL = true; };
kTLS = true;
} config;
in in
lib.mapAttrs mkVHostConfig domains; lib.mapAttrs (_: lib.recursiveUpdate commonVHostConfig) domains;
# Split DNS utilities for unbound
# Generates unbound view config from a list of DNS entries
mkSplitDNS =
entries:
let
# Generate local-data entries for all domains
tailscaleData = map (e: ''"${e.domain}. IN A ${e.tailscaleIP}"'') entries;
lanData = map (e: ''"${e.domain}. IN A ${e.lanIP}"'') entries;
in
[
# Single Tailscale view with all domains
{
name = "tailscale";
view-first = true;
local-zone = ''"baduhai.dev." transparent'';
local-data = tailscaleData;
}
# Single LAN view with all domains
{
name = "lan";
view-first = true;
local-zone = ''"baduhai.dev." transparent'';
local-data = lanData;
}
];
} }