This commit is contained in:
ashisgreat22 2026-01-14 21:24:19 +01:00
commit 2be8de47fa
87 changed files with 11501 additions and 0 deletions

View file

@ -0,0 +1,72 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.antigravity2api;
workDir = "/home/ashie/git/antigravity2api-nodejs";
in
{
options.services.antigravity2api = {
enable = lib.mkEnableOption "Antigravity2API service";
credentials = {
username = lib.mkOption {
type = lib.types.str;
default = "admin";
description = "Admin username for the dashboard";
};
password = lib.mkOption {
type = lib.types.str;
default = "password";
description = "Admin password for the dashboard";
};
apiKey = lib.mkOption {
type = lib.types.str;
default = "";
description = "API Key for client access";
};
};
};
config = lib.mkIf cfg.enable {
systemd.user.services.antigravity2api = {
Unit = {
Description = "Antigravity API to OpenAI Proxy";
After = [ "network.target" ];
};
Service = {
ExecStartPre = pkgs.writeShellScript "antigravity2api-init" ''
cat > ${workDir}/.env <<EOF
API_KEY=${cfg.credentials.apiKey}
ADMIN_USERNAME=${cfg.credentials.username}
ADMIN_PASSWORD=${cfg.credentials.password}
SYSTEM_INSTRUCTION=""
OFFICIAL_SYSTEM_PROMPT="You are Antigravity, a powerful agentic AI coding assistant designed by the Google Deepmind team working on Advanced Agentic Coding.You are pair programming with a USER to solve their coding task. The task may require creating a new codebase, modifying or debugging an existing codebase, or simply answering a question.**Proactiveness**"
EOF
'';
ExecStart = ''
${pkgs.podman}/bin/podman run --replace --rm --name antigravity2api \
-p 8045:8045 \
-v ${workDir}/data:/app/data \
-v ${workDir}/public/images:/app/public/images \
-v ${workDir}/.env:/app/.env \
-v ${workDir}/config.json:/app/config.json \
localhost/antigravity2api
'';
ExecStop = "${pkgs.podman}/bin/podman stop antigravity2api";
Restart = "always";
RestartSec = "10";
};
Install = {
WantedBy = [ "default.target" ];
};
};
};
}

View file

@ -0,0 +1,118 @@
# Browser Container Update Module (Home Manager)
# Provides: Auto-update timer for browser container images
#
# Usage:
# myModules.browserContainerUpdate = {
# enable = true;
# repositoryPath = "/home/user/nixos";
# schedule = "weekly";
# };
{
config,
lib,
pkgs,
...
}:
let
cfg = config.myModules.browserContainerUpdate;
in
{
options.myModules.browserContainerUpdate = {
enable = lib.mkEnableOption "Browser container auto-update timer";
repositoryPath = lib.mkOption {
type = lib.types.str;
default = config.myModules.common.repoPath;
description = "Path to repository containing container Dockerfiles";
};
schedule = lib.mkOption {
type = lib.types.str;
default = "weekly";
description = "systemd calendar expression for update schedule";
};
randomDelay = lib.mkOption {
type = lib.types.str;
default = "1h";
description = "Random delay before running update";
};
browsers = lib.mkOption {
type = lib.types.listOf (
lib.types.enum [
"firefox"
"tor-browser"
"thorium"
]
);
default = [
"firefox"
"tor-browser"
"thorium"
];
description = "Which browser containers to update";
};
};
config = lib.mkIf cfg.enable {
systemd.user.services.browser-containers-update = {
Unit = {
Description = "Update browser container images";
};
Service = {
Type = "oneshot";
ExecStart = pkgs.writeShellScript "update-browser-containers" ''
set -e
REPO_DIR="${cfg.repositoryPath}"
${lib.optionalString (builtins.elem "firefox" cfg.browsers) ''
echo "=== Updating Firefox container ==="
${pkgs.podman}/bin/podman build --pull --no-cache \
-t localhost/firefox-wayland:latest \
"$REPO_DIR/containers/firefox-wayland/"
''}
${lib.optionalString (builtins.elem "tor-browser" cfg.browsers) ''
echo "=== Updating Tor Browser container ==="
${pkgs.podman}/bin/podman build --pull --no-cache \
-t localhost/tor-browser-wayland:latest \
"$REPO_DIR/containers/tor-browser-wayland/"
''}
${lib.optionalString (builtins.elem "thorium" cfg.browsers) ''
echo "=== Updating Thorium container ==="
${pkgs.podman}/bin/podman build --pull --no-cache \
-t localhost/thorium-wayland:latest \
"$REPO_DIR/containers/thorium-wayland/"
''}
echo "=== Cleaning old images ==="
${pkgs.podman}/bin/podman image prune -f
echo "=== Update complete ==="
${pkgs.libnotify}/bin/notify-send "Browser Containers" "Updated browser containers" --icon=security-high
'';
};
};
systemd.user.timers.browser-containers-update = {
Unit = {
Description = "Weekly browser container update timer";
};
Timer = {
OnCalendar = cfg.schedule;
Persistent = true;
RandomizedDelaySec = cfg.randomDelay;
};
Install = {
WantedBy = [ "timers.target" ];
};
};
};
}

View file

@ -0,0 +1,92 @@
{ pkgs, ... }:
{
programs = {
# Modern replacement for 'ls'
eza = {
enable = true;
icons = "auto";
git = true;
enableBashIntegration = true;
};
# A cat(1) clone with wings
bat = {
enable = true;
config = {
theme = "Catppuccin Mocha";
};
};
# A smarter cd command
zoxide = {
enable = true;
enableBashIntegration = true;
options = [
"--cmd cd" # Replace cd with zoxide
];
};
# Command-line fuzzy finder
fzf = {
enable = true;
enableBashIntegration = true;
};
# Modern grep replacement
ripgrep.enable = true;
# Modern find replacement
fd.enable = true;
# Magical Shell History
atuin = {
enable = true;
enableFishIntegration = true;
flags = [
"--disable-up-arrow"
];
};
# Terminal Multiplexer
zellij = {
enable = true;
enableFishIntegration = false;
settings = {
theme = "catppuccin-mocha";
show_startup_tips = false;
};
};
# Interactive Cheatsheet
navi = {
enable = true;
enableFishIntegration = true;
};
};
home.packages = with pkgs; [
nh # Nix helper for faster rebuilds
];
# Point nh to the flake location
home.sessionVariables = {
NH_FLAKE = "/home/ashie/nixos";
};
home.shellAliases = {
cat = "bat";
grep = "rg";
find = "fd";
top = "btm";
ps = "procs";
sed = "sd";
du = "dust -r";
# Enhanced eza aliases
ls = "eza --icons=auto";
ll = "eza --icons=auto --long --git";
la = "eza --icons=auto --all";
lla = "eza --icons=auto --all --long --git";
tree = "eza --icons=auto --tree";
};
}

14
modules/home/common.nix Normal file
View file

@ -0,0 +1,14 @@
{
lib,
config,
...
}:
{
options.myModules.common = {
repoPath = lib.mkOption {
type = lib.types.str;
default = "/home/ashie/nixos";
description = "Path to the main NixOS configuration repository";
};
};
}

28
modules/home/default.nix Normal file
View file

@ -0,0 +1,28 @@
# Home Manager Modules Index
# Import this to get all home-manager modules
#
# Usage in home.nix:
# imports = [ ./modules/home ];
{ ... }:
{
imports = [
./common.nix
./hyprland-catppuccin.nix
./niri.nix
./gluetun-user.nix
./qbittorrent-vpn.nix
./browser-container-update.nix
./proton-cachyos-updater.nix
./cli-tools.nix
# ./unified-router.nix
./sillytavern.nix
./niri.nix
./noctalia.nix
./polling-rate.nix
./antigravity2api.nix
./theme.nix
];
}

View file

@ -0,0 +1,124 @@
# Gluetun User Service Module (Home Manager)
# Provides: Rootless Gluetun VPN container as user systemd service
#
# Usage:
# myModules.gluetunUser = {
# enable = true;
# environmentFile = "/run/secrets/rendered/gluetun.env";
# };
{
config,
lib,
pkgs,
...
}:
let
cfg = config.myModules.gluetunUser;
in
{
options.myModules.gluetunUser = {
enable = lib.mkEnableOption "Rootless Gluetun VPN container service";
image = lib.mkOption {
type = lib.types.str;
default = "qmcgaw/gluetun:latest";
description = "Gluetun container image";
};
webPort = lib.mkOption {
type = lib.types.port;
default = 8080;
description = "Web UI port for Gluetun";
};
environmentFile = lib.mkOption {
type = lib.types.str;
description = "Path to environment file with VPN credentials";
};
secretsDir = lib.mkOption {
type = lib.types.str;
default = "/run/secrets";
description = "Path to secrets directory";
};
dnsAddress = lib.mkOption {
type = lib.types.str;
default = "1.1.1.1";
description = "DNS server address for VPN";
};
extraPorts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Extra ports to map (e.g. ['3000:80'])";
};
wireguardMtu = lib.mkOption {
type = lib.types.int;
default = 1280;
description = "WireGuard MTU setting";
};
keepaliveInterval = lib.mkOption {
type = lib.types.str;
default = "15s";
description = "WireGuard persistent keepalive interval";
};
};
config = lib.mkIf cfg.enable {
systemd.user.services.gluetun = {
Unit = {
Description = "Gluetun VPN Container (Rootless)";
After = [ "network-online.target" ];
Wants = [ "network-online.target" ];
};
Service = {
Restart = "always";
ExecStartPre = [
"-${pkgs.podman}/bin/podman system migrate"
"-${pkgs.podman}/bin/podman stop gluetun"
];
ExecStart = ''
${pkgs.podman}/bin/podman run --rm --replace --name gluetun \
--cap-add=NET_ADMIN \
--device=/dev/net/tun \
--sysctl=net.ipv4.conf.all.src_valid_mark=1 \
--add-host=host.containers.internal:host-gateway \
-v ${cfg.environmentFile}:${cfg.environmentFile}:ro \
-v ${cfg.secretsDir}:${cfg.secretsDir}:ro \
-p 127.0.0.1:${toString cfg.webPort}:8080 \
${lib.concatMapStringsSep " " (p: "-p ${p}") cfg.extraPorts} \
-e VPN_SERVICE_PROVIDER=custom \
-e VPN_TYPE=wireguard \
-e WIREGUARD_IMPLEMENTATION=userspace \
-e WIREGUARD_PRIVATE_KEY_SECRETFILE=${cfg.secretsDir}/wireguard_private_key \
-e WIREGUARD_PRESHARED_KEY_SECRETFILE=${cfg.secretsDir}/wireguard_preshared_key \
-e WIREGUARD_ADDRESSES_SECRETFILE=${cfg.secretsDir}/wireguard_addresses \
-e WIREGUARD_PUBLIC_KEY=''${WIREGUARD_PUBLIC_KEY} \
-e WIREGUARD_ENDPOINT_IP=''${WIREGUARD_ENDPOINT_IP} \
-e WIREGUARD_ENDPOINT_PORT=''${WIREGUARD_ENDPOINT_PORT} \
-e WIREGUARD_MTU=1280 \
-e WIREGUARD_PERSISTENT_KEEPALIVE_INTERVAL=15s \
-e DNS_ADDRESS=${cfg.dnsAddress} \
-e DOT=off \
-e DOT_CACHING=on \
-e HEALTH_RESTART_VPN=off \
-e LOG_LEVEL=info \
-e FIREWALL_VPN_INPUT_PORTS=4000,3001,3000,9090,36630 \
${cfg.image}
'';
EnvironmentFile = [ cfg.environmentFile ];
ExecStop = "${pkgs.podman}/bin/podman stop gluetun";
};
Install = {
WantedBy = [ "default.target" ];
};
};
};
}

View file

@ -0,0 +1,291 @@
# Hyprland Catppuccin Theme Module (Home Manager)
# Provides: Catppuccin-themed Hyprland with animations and keybinds
#
# Usage:
# myModules.hyprlandCatppuccin = {
# enable = true;
# keyboardLayout = "de";
# primaryMonitor = "DP-2";
# secondaryMonitor = "HDMI-A-1";
# };
{
config,
lib,
pkgs,
...
}:
let
cfg = config.myModules.hyprlandCatppuccin;
in
{
options.myModules.hyprlandCatppuccin = {
enable = lib.mkEnableOption "Catppuccin-themed Hyprland configuration";
keyboardLayout = lib.mkOption {
type = lib.types.str;
default = "us";
description = "Keyboard layout";
};
keyboardVariant = lib.mkOption {
type = lib.types.str;
default = "";
description = "Keyboard variant (e.g., 'nodeadkeys')";
};
keyboardModel = lib.mkOption {
type = lib.types.str;
default = "pc104";
description = "Keyboard model (e.g., 'pc104' or 'pc105')";
};
capsToEscape = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Remap Caps Lock to Escape";
};
primaryMonitor = lib.mkOption {
type = lib.types.str;
default = "DP-1";
description = "Primary monitor name";
};
primaryResolution = lib.mkOption {
type = lib.types.str;
default = "2560x1440@165";
description = "Primary monitor resolution and refresh rate";
};
secondaryMonitor = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Secondary monitor name (null to disable)";
};
secondaryResolution = lib.mkOption {
type = lib.types.str;
default = "1920x1080@60";
description = "Secondary monitor resolution";
};
terminal = lib.mkOption {
type = lib.types.str;
default = "kitty";
description = "Default terminal emulator";
};
fileManager = lib.mkOption {
type = lib.types.str;
default = "nautilus";
description = "Default file manager";
};
launcher = lib.mkOption {
type = lib.types.str;
default = "noctalia-shell ipc call launcher toggle";
description = "Application launcher";
};
gapsIn = lib.mkOption {
type = lib.types.int;
default = 3;
description = "Inner gaps between windows";
};
gapsOut = lib.mkOption {
type = lib.types.int;
default = 8;
description = "Outer gaps around windows";
};
borderSize = lib.mkOption {
type = lib.types.int;
default = 2;
description = "Window border size";
};
rounding = lib.mkOption {
type = lib.types.int;
default = 10;
description = "Window corner rounding";
};
enableBlur = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable window blur effect";
};
};
config = lib.mkIf cfg.enable {
wayland.windowManager.hyprland = {
enable = true;
xwayland.enable = true;
settings = {
input = {
kb_layout = cfg.keyboardLayout;
kb_variant = cfg.keyboardVariant;
kb_model = cfg.keyboardModel;
kb_options = lib.mkIf cfg.capsToEscape "caps:escape";
accel_profile = "flat";
};
exec-once = [
"gnome-keyring-daemon --start --components=secrets,pkcs11"
"lxqt-policykit-agent"
"swww-daemon"
"swww img /home/ashie/Pictures/Wallpapers/chill-mario.gif"
"noctalia-shell"
];
bind = [
"SUPER, Return, exec, ${cfg.terminal}"
"SUPER, Q, killactive"
"SUPER, E, exec, ${cfg.fileManager}"
"SUPER, 1, exec, ~/.config/hypr/ws-go.sh workspace 1"
"SUPER, 2, exec, ~/.config/hypr/ws-go.sh workspace 2"
"SUPER, 3, exec, ~/.config/hypr/ws-go.sh workspace 3"
"SUPER, 4, exec, ~/.config/hypr/ws-go.sh workspace 4"
"SUPER, 5, exec, ~/.config/hypr/ws-go.sh workspace 5"
"SUPER, 6, exec, ~/.config/hypr/ws-go.sh workspace 6"
"SUPER, 7, exec, ~/.config/hypr/ws-go.sh workspace 7"
"SUPER, 8, exec, ~/.config/hypr/ws-go.sh workspace 8"
"SUPER, 9, exec, ~/.config/hypr/ws-go.sh workspace 9"
"SUPER, 0, exec, ~/.config/hypr/ws-go.sh workspace 0"
"SUPER_CTRL, 1, exec, ~/.config/hypr/ws-go.sh movetoworkspace 1"
"SUPER_CTRL, 2, exec, ~/.config/hypr/ws-go.sh movetoworkspace 2"
"SUPER_CTRL, 3, exec, ~/.config/hypr/ws-go.sh movetoworkspace 3"
"SUPER_CTRL, 4, exec, ~/.config/hypr/ws-go.sh movetoworkspace 4"
"SUPER_CTRL, 5, exec, ~/.config/hypr/ws-go.sh movetoworkspace 5"
"SUPER_CTRL, 6, exec, ~/.config/hypr/ws-go.sh movetoworkspace 6"
"SUPER_CTRL, 7, exec, ~/.config/hypr/ws-go.sh movetoworkspace 7"
"SUPER_CTRL, 8, exec, ~/.config/hypr/ws-go.sh movetoworkspace 8"
"SUPER_CTRL, 9, exec, ~/.config/hypr/ws-go.sh movetoworkspace 9"
"SUPER_CTRL, 0, exec, ~/.config/hypr/ws-go.sh movetoworkspace 0"
"SUPER, F, fullscreen, 0"
"SUPER, SPACE, togglefloating"
# Browsers (all via isolated Podman containers)
"SUPER, W, exec, firefox-vpn-podman"
"SUPER ALT, W, exec, tor-browser-vpn-podman"
"SUPER ALT, Return, exec, kitty-vpn-podman"
"SUPER SHIFT, W, exec, thorium-vpn-podman"
# Media Controls
", XF86AudioPlay, exec, playerctl play-pause"
", XF86AudioNext, exec, playerctl next"
", XF86AudioPrev, exec, playerctl previous"
", XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
", XF86AudioRaiseVolume, exec, wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%+"
", XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"
];
bindm = [
"SUPER, mouse:272, movewindow"
"SUPER, mouse:273, resizewindow"
];
monitor = [
"${cfg.primaryMonitor}, ${cfg.primaryResolution}, 1920x0, 1"
]
++ lib.optional (
cfg.secondaryMonitor != null
) "${cfg.secondaryMonitor}, ${cfg.secondaryResolution}, 0x0, 1";
workspace = [
"1, monitor:${cfg.primaryMonitor}"
"2, monitor:${cfg.primaryMonitor}"
"3, monitor:${cfg.primaryMonitor}"
"4, monitor:${cfg.primaryMonitor}"
"5, monitor:${cfg.primaryMonitor}"
"6, monitor:${cfg.primaryMonitor}"
"7, monitor:${cfg.primaryMonitor}"
"8, monitor:${cfg.primaryMonitor}"
"9, monitor:${cfg.primaryMonitor}"
"10, monitor:${cfg.primaryMonitor}"
]
++ lib.optionals (cfg.secondaryMonitor != null) [
"12, monitor:${cfg.secondaryMonitor}"
"13, monitor:${cfg.secondaryMonitor}"
"14, monitor:${cfg.secondaryMonitor}"
"15, monitor:${cfg.secondaryMonitor}"
"16, monitor:${cfg.secondaryMonitor}"
"17, monitor:${cfg.secondaryMonitor}"
"18, monitor:${cfg.secondaryMonitor}"
"19, monitor:${cfg.secondaryMonitor}"
"20, monitor:${cfg.secondaryMonitor}"
];
};
extraConfig = ''
bind = Super, Super_L, exec, ${cfg.launcher}
windowrulev2 = float, class:^(Tor Browser)$
env = HYPRCURSOR_THEME,"Future-Cyan-Hyprcursor_Theme"
env = HYPRCURSOR_SIZE,32
animations {
enabled = 1
bezier = default, 0.12, 0.92, 0.08, 1.0
bezier = wind, 0.12, 0.92, 0.08, 1.0
bezier = overshot, 0.18, 0.95, 0.22, 1.03
bezier = liner, 1, 1, 1, 1
animation = windows, 1, 5, wind, popin 60%
animation = windowsIn, 1, 6, overshot, popin 60%
animation = windowsOut, 1, 4, overshot, popin 60%
animation = windowsMove, 1, 4, overshot, slide
animation = layers, 1, 4, default, popin
animation = fadeIn, 1, 7, default
animation = fadeOut, 1, 7, default
animation = fadeSwitch, 1, 7, default
animation = fadeShadow, 1, 7, default
animation = fadeDim, 1, 7, default
animation = fadeLayers, 1, 7, default
animation = workspaces, 1, 5, overshot, slidevert
animation = border, 1, 1, liner
animation = borderangle, 1, 24, liner, loop
}
env = QT_QPA_PLATFORMTHEME, qt6ct
general {
gaps_in = ${toString cfg.gapsIn}
gaps_out = ${toString cfg.gapsOut}
border_size = ${toString cfg.borderSize}
col.active_border = rgba(ca9ee6ff) rgba(f2d5cfff) 45deg
col.inactive_border = rgba(b4befecc) rgba(6c7086cc) 45deg
layout = dwindle
resize_on_border = true
}
group {
col.border_active = rgba(ca9ee6ff) rgba(f2d5cfff) 45deg
col.border_inactive = rgba(b4befecc) rgba(6c7086cc) 45deg
col.border_locked_active = rgba(ca9ee6ff) rgba(f2d5cfff) 45deg
col.border_locked_inactive = rgba(b4befecc) rgba(6c7086cc) 45deg
}
decoration {
rounding = ${toString cfg.rounding}
shadow:enabled = false
blur {
enabled = ${if cfg.enableBlur then "yes" else "no"}
size = 6
passes = 3
new_optimizations = on
ignore_opacity = on
xray = false
}
}
bind = , Print, exec, grim -g "$(slurp -w 0)" - | wl-copy
'';
};
};
}

264
modules/home/niri.nix Normal file
View file

@ -0,0 +1,264 @@
{
config,
lib,
pkgs,
inputs,
...
}:
let
cfg = config.myModules.niri;
in
{
options.myModules.niri = {
enable = lib.mkEnableOption "Niri configuration";
keyboardLayout = lib.mkOption {
type = lib.types.str;
default = "us";
};
keyboardVariant = lib.mkOption {
type = lib.types.str;
default = "";
};
keyboardOptions = lib.mkOption {
type = lib.types.str;
default = "caps:escape";
};
primaryMonitor = lib.mkOption {
type = lib.types.str;
default = "DP-1";
};
primaryResolution = lib.mkOption {
type = lib.types.str;
default = "2560x1440@165";
};
secondaryMonitor = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
};
secondaryResolution = lib.mkOption {
type = lib.types.str;
default = "1920x1080@60";
};
terminal = lib.mkOption {
type = lib.types.str;
default = "kitty";
};
launcher = lib.mkOption {
type = lib.types.str;
default = "noctalia-shell ipc call launcher toggle";
};
};
config = lib.mkIf cfg.enable {
home.packages = [
inputs.niri.packages.${pkgs.system}.niri
inputs.niri.packages.${pkgs.system}.niri
pkgs.xwayland-satellite
pkgs.grim
pkgs.slurp
pkgs.wl-clipboard
pkgs.lxqt.lxqt-policykit
pkgs.libnotify
pkgs.swww
];
xdg.portal = {
enable = true;
extraPortals = [
pkgs.xdg-desktop-portal-gtk
pkgs.xdg-desktop-portal-gnome
];
config.common.default = [
"gtk"
"gnome"
];
};
systemd.user.services.xwayland-satellite = {
Unit = {
Description = "Xwayland Satellite (Rootless Xwayland Bridge)";
After = [ "graphical-session.target" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
ExecStart = "${pkgs.xwayland-satellite}/bin/xwayland-satellite";
Restart = "always";
RestartSec = "10";
# Security Hardening
NoNewPrivileges = true;
ProtectSystem = "strict";
ProtectHome = true;
PrivateTmp = true;
RestrictNamespaces = true;
LockPersonality = true;
MemoryDenyWriteExecute = true;
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
};
xdg.configFile."niri/config.kdl".text = ''
input {
keyboard {
xkb {
layout "${cfg.keyboardLayout}"
variant "${cfg.keyboardVariant}"
options "${cfg.keyboardOptions}"
}
}
mouse {
accel-profile "flat"
}
focus-follows-mouse
}
environment {
GTK_THEME "catppuccin-mocha-mauve-standard"
GTK_THEME "catppuccin-mocha-mauve-standard"
QT_QPA_PLATFORMTHEME "gtk3"
QT_STYLE_OVERRIDE "kvantum"
XCURSOR_THEME "Bibata-Modern-Ice"
XCURSOR_SIZE "24"
}
prefer-no-csd
output "${cfg.primaryMonitor}" {
mode "${cfg.primaryResolution}"
scale 1.0
position x=1920 y=0
}
${lib.optionalString (cfg.secondaryMonitor != null) ''
output "${cfg.secondaryMonitor}" {
mode "${cfg.secondaryResolution}"
scale 1.0
position x=0 y=0
}
''}
layout {
gaps 8
center-focused-column "never"
preset-column-widths {
proportion 0.33333
proportion 0.5
proportion 0.66667
}
default-column-width { proportion 0.5; }
focus-ring {
off
}
}
spawn-at-startup "swww-daemon"
spawn-at-startup "bash" "-c" "sleep 1; swww img /home/ashie/Pictures/Wallpapers/chill-mario.gif"
spawn-at-startup "bash" "-c" "sleep 2; dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP; ${
inputs.noctalia.packages.${pkgs.system}.default
}/bin/noctalia-shell >> /tmp/noctalia.log 2>&1"
binds {
Mod+Return { spawn "${cfg.terminal}"; }
Mod+D { spawn "sh" "-c" "${cfg.launcher}"; }
Mod+Q { close-window; }
Mod+E { spawn "nautilus"; }
Mod+F { fullscreen-window; }
Mod+M { maximize-column; }
Mod+Space { switch-preset-column-width; }
Mod+1 { focus-workspace 1; }
Mod+2 { focus-workspace 2; }
Mod+3 { focus-workspace 3; }
Mod+4 { focus-workspace 4; }
Mod+5 { focus-workspace 5; }
Mod+6 { focus-workspace 6; }
Mod+7 { focus-workspace 7; }
Mod+8 { focus-workspace 8; }
Mod+9 { focus-workspace 9; }
Mod+WheelScrollDown { focus-column-right; }
Mod+WheelScrollUp { focus-column-left; }
Mod+Left { focus-column-left; }
Mod+Right { focus-column-right; }
Mod+Up { focus-window-up; }
Mod+Down { focus-window-down; }
Mod+H { focus-column-left; }
Mod+L { focus-column-right; }
Mod+K { focus-window-up; }
Mod+J { focus-window-down; }
Mod+Shift+Left { move-column-left; }
Mod+Shift+Right { move-column-right; }
Mod+Shift+Up { move-window-up; }
Mod+Shift+Down { move-window-down; }
Mod+Shift+H { move-column-left; }
Mod+Shift+L { move-column-right; }
Mod+Shift+K { move-window-up; }
Mod+Shift+J { move-window-down; }
Mod+Ctrl+1 { move-column-to-workspace 1; }
Mod+Ctrl+2 { move-column-to-workspace 2; }
Mod+Ctrl+3 { move-column-to-workspace 3; }
Mod+Ctrl+4 { move-column-to-workspace 4; }
Mod+Ctrl+5 { move-column-to-workspace 5; }
Mod+Ctrl+6 { move-column-to-workspace 6; }
Mod+Ctrl+7 { move-column-to-workspace 7; }
Mod+Ctrl+8 { move-column-to-workspace 8; }
Mod+Ctrl+9 { move-column-to-workspace 9; }
Mod+BracketLeft { consume-or-expel-window-left; }
Mod+BracketRight { consume-or-expel-window-right; }
Mod+C { center-column; }
Mod+Minus { set-column-width "-10%"; }
Mod+Equal { set-column-width "+10%"; }
Mod+Shift+E { quit; }
Print { spawn "sh" "-c" "grim -g \"$(slurp)\" - | wl-copy"; }
// Browsers
Mod+W { spawn "firefox"; }
Mod+Alt+W { spawn "tor-browser-vpn-podman"; }
Mod+Shift+W { spawn "brave"; }
Mod+Alt+Return { spawn "kitty-vpn-podman"; }
// Media
XF86AudioRaiseVolume { spawn "wpctl" "set-volume" "-l" "1.5" "@DEFAULT_AUDIO_SINK@" "5%+"; }
XF86AudioLowerVolume { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "5%-"; }
XF86AudioMute { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; }
XF86AudioPlay { spawn "playerctl" "play-pause"; }
XF86AudioNext { spawn "playerctl" "next"; }
XF86AudioPrev { spawn "playerctl" "previous"; }
}
window-rule {
geometry-corner-radius 12
clip-to-geometry true
}
window-rule {
match app-id="^Tor Browser$"
open-floating true
}
'';
};
}

79
modules/home/noctalia.nix Normal file
View file

@ -0,0 +1,79 @@
{
config,
lib,
pkgs,
inputs,
...
}:
let
cfg = config.myModules.noctalia;
# Catppuccin Mocha Palette
mocha = {
base = "#1e1e2e";
mantle = "#181825";
crust = "#11111b";
text = "#cdd6f4";
subtext0 = "#a6adc8";
overlay0 = "#6c7086";
mauve = "#cba6f7"; # Primary
lavender = "#b4befe"; # Secondary
pink = "#f5c2e7"; # Tertiary
red = "#f38ba8"; # Error
};
in
{
imports = [ inputs.noctalia.homeModules.default ];
options.myModules.noctalia = {
enable = lib.mkEnableOption "Noctalia Shell Configuration";
};
config = lib.mkIf cfg.enable {
# Correct Option Name: programs.noctalia-shell
programs.noctalia-shell = {
enable = true;
# Manual Catppuccin Mocha Theme mapping to Material Design roles
colors = {
mPrimary = mocha.mauve;
mOnPrimary = mocha.base;
mSecondary = mocha.lavender;
mOnSecondary = mocha.base;
mTertiary = mocha.pink;
mOnTertiary = mocha.base;
mError = mocha.red;
mOnError = mocha.base;
mSurface = mocha.base;
mOnSurface = mocha.text;
mSurfaceVariant = mocha.mantle;
mOnSurfaceVariant = mocha.subtext0;
mOutline = mocha.overlay0;
mShadow = mocha.crust;
};
settings = {
colorSchemes = {
darkMode = true;
useWallpaperColors = false; # Force our manual colors
};
location = {
weatherEnabled = true;
name = "Berlin";
};
wallpaper = {
enabled = false;
};
};
};
};
}

View file

@ -0,0 +1,99 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.polling-rate-switcher;
switcherScript = pkgs.writeShellScriptBin "polling-rate-switcher" ''
# Find Razer Viper V3 Pro sysfs path
# Look for a device that supports poll_rate
MOUSE_PATH=""
find_mouse() {
for dev in /sys/bus/hid/drivers/razermouse/*; do
if [ -f "$dev/device_type" ] && [ -f "$dev/poll_rate" ]; then
TYPE=$(cat "$dev/device_type")
# Check if it looks like a mouse "Razer Viper V3 Pro" usually has unique ID/Type
# For now, we take the first Razer device that supports polling rate
# Or specifically filter if we knew exact string.
# The user said "Active window... polling rate of my razer viper v3 pro"
# We'll assume it's the main device found.
MOUSE_PATH="$dev"
echo "Found Razer device at $MOUSE_PATH ($TYPE)"
break
fi
done
}
find_mouse
if [ -z "$MOUSE_PATH" ]; then
echo "No Razer mouse found with poll_rate capability."
exit 1
fi
echo "Starting Polling Rate Switcher for $MOUSE_PATH"
CURRENT_MODE="unknown"
update_rate() {
TARGET=$1
# Read current to avoid redundant writes
CURRENT=$(cat "$MOUSE_PATH/poll_rate")
if [ "$CURRENT" != "$TARGET" ]; then
echo "Switching polling rate to $TARGET Hz"
echo "$TARGET" > "$MOUSE_PATH/poll_rate"
fi
}
while true; do
# Get active window info from Niri
# Niri msg active-window returns JSON
WINDOW_INFO=$(${pkgs.niri}/bin/niri msg -j focused-window 2>/dev/null)
if [ -n "$WINDOW_INFO" ]; then
APP_ID=$(echo "$WINDOW_INFO" | ${pkgs.jq}/bin/jq -r '.app_id // ""' | tr '[:upper:]' '[:lower:]')
TITLE=$(echo "$WINDOW_INFO" | ${pkgs.jq}/bin/jq -r '.title // ""' | tr '[:upper:]' '[:lower:]')
# Check for Overwatch
if [[ "$APP_ID" == *"overwatch"* ]] || [[ "$TITLE" == *"overwatch"* ]]; then
update_rate 8000
else
update_rate 1000
fi
fi
sleep 2
done
'';
in
{
options.services.polling-rate-switcher = {
enable = lib.mkEnableOption "Polling Rate Switcher Service";
};
config = lib.mkIf cfg.enable {
systemd.user.services.polling-rate-switcher = {
Unit = {
Description = "Auto-switch Razer Polling Rate for Overwatch";
After = [ "graphical-session.target" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
ExecStart = "${switcherScript}/bin/polling-rate-switcher";
Restart = "always";
RestartSec = 5;
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
};
};
}

View file

@ -0,0 +1,190 @@
# Proton CachyOS Auto-Updater Module (Home Manager)
# Provides: Auto-update timer for Proton CachyOS from GitHub releases
#
# Usage:
# myModules.protonCachyosUpdater = {
# enable = true;
# arch = "x86_64_v3";
# schedule = "daily";
# };
{
config,
lib,
pkgs,
...
}:
let
cfg = config.myModules.protonCachyosUpdater;
updateScript = pkgs.writeShellScript "update-proton-cachyos" ''
set -euo pipefail
COMPAT_DIR="${cfg.compatToolsDir}"
ARCH="${cfg.arch}"
GITHUB_API="https://api.github.com/repos/CachyOS/proton-cachyos/releases/latest"
VERSION_FILE="$COMPAT_DIR/.proton-cachyos-version"
# Ensure directory exists
mkdir -p "$COMPAT_DIR"
echo "=== Checking for Proton CachyOS updates ==="
# Get latest release info from GitHub
RELEASE_JSON=$(${pkgs.curl}/bin/curl -sL "$GITHUB_API")
if [ -z "$RELEASE_JSON" ] || echo "$RELEASE_JSON" | ${pkgs.jq}/bin/jq -e '.message' > /dev/null 2>&1; then
echo "Failed to fetch release info from GitHub"
exit 1
fi
LATEST_TAG=$(echo "$RELEASE_JSON" | ${pkgs.jq}/bin/jq -r '.tag_name')
echo "Latest version: $LATEST_TAG"
# Check current version
CURRENT_VERSION=""
if [ -f "$VERSION_FILE" ]; then
CURRENT_VERSION=$(cat "$VERSION_FILE")
fi
echo "Current version: ''${CURRENT_VERSION:-none}"
if [ "$LATEST_TAG" = "$CURRENT_VERSION" ]; then
echo "Already up to date!"
# Still ensure symlink exists
LATEST_DIR=$(ls -v "$COMPAT_DIR" | grep -E "^proton-cachyos" | grep -v "latest" | tail -1)
if [ -n "$LATEST_DIR" ]; then
ln -sfn "$COMPAT_DIR/$LATEST_DIR" "$COMPAT_DIR/proton-cachyos-latest"
fi
exit 0
fi
# Find download URL for our architecture
DOWNLOAD_URL=$(echo "$RELEASE_JSON" | ${pkgs.jq}/bin/jq -r ".assets[] | select(.name | contains(\"$ARCH\")) | .browser_download_url" | grep "\.tar\.xz$" | head -1)
if [ -z "$DOWNLOAD_URL" ]; then
echo "No download found for architecture: $ARCH"
exit 1
fi
FILENAME=$(basename "$DOWNLOAD_URL")
echo "Downloading: $FILENAME"
# Download to temp directory
TEMP_DIR=$(mktemp -d)
trap "rm -rf $TEMP_DIR" EXIT
${pkgs.curl}/bin/curl -L --progress-bar -o "$TEMP_DIR/$FILENAME" "$DOWNLOAD_URL"
echo "Extracting..."
${pkgs.gnutar}/bin/tar -xf "$TEMP_DIR/$FILENAME" -C "$COMPAT_DIR"
# Find extracted directory name
EXTRACTED_DIR=$(ls -v "$COMPAT_DIR" | grep -E "^proton-cachyos" | grep -v "latest" | tail -1)
if [ -z "$EXTRACTED_DIR" ]; then
echo "Failed to find extracted directory"
exit 1
fi
echo "Installed: $EXTRACTED_DIR"
# Create wrapper directory (remove old symlink/dir first)
rm -rf "$COMPAT_DIR/proton-cachyos-latest"
mkdir -p "$COMPAT_DIR/proton-cachyos-latest"
# Create wrapper compatibilitytool.vdf
# This allows Steam to see "proton-cachyos-latest" as a distinct tool pointing to the real files
cat > "$COMPAT_DIR/proton-cachyos-latest/compatibilitytool.vdf" <<EOF
"compatibilitytools"
{
"compat_tools"
{
"proton-cachyos-latest"
{
"install_path" "$COMPAT_DIR/$EXTRACTED_DIR"
"display_name" "Proton CachyOS (Latest)"
"from_oslist" "windows"
"to_oslist" "linux"
}
}
}
EOF
echo "Updated wrapper: proton-cachyos-latest -> $EXTRACTED_DIR"
# Save version
echo "$LATEST_TAG" > "$VERSION_FILE"
echo "=== Update complete ==="
${pkgs.libnotify}/bin/notify-send "Proton CachyOS" "Updated to $LATEST_TAG" --icon=steam
'';
in
{
options.myModules.protonCachyosUpdater = {
enable = lib.mkEnableOption "Proton CachyOS auto-updater";
arch = lib.mkOption {
type = lib.types.enum [
"x86_64"
"x86_64_v2"
"x86_64_v3"
"x86_64_v4"
];
default = "x86_64_v3";
description = "CPU architecture variant to download";
};
schedule = lib.mkOption {
type = lib.types.str;
default = "daily";
description = "systemd calendar expression for update schedule";
};
randomDelay = lib.mkOption {
type = lib.types.str;
default = "1h";
description = "Random delay before running update";
};
compatToolsDir = lib.mkOption {
type = lib.types.str;
default = "$HOME/.local/share/Steam/compatibilitytools.d";
description = "Steam compatibility tools directory";
};
};
config = lib.mkIf cfg.enable {
systemd.user.services.proton-cachyos-update = {
Unit = {
Description = "Update Proton CachyOS from GitHub releases";
After = [ "network-online.target" ];
Wants = [ "network-online.target" ];
};
Service = {
Type = "oneshot";
ExecStart = updateScript;
Environment = [
"HOME=%h"
];
};
};
systemd.user.timers.proton-cachyos-update = {
Unit = {
Description = "Proton CachyOS update timer";
};
Timer = {
OnCalendar = cfg.schedule;
Persistent = true;
RandomizedDelaySec = cfg.randomDelay;
};
Install = {
WantedBy = [ "timers.target" ];
};
};
};
}

View file

@ -0,0 +1,96 @@
# qBittorrent VPN Module (Home Manager)
# Provides: qBittorrent running through Gluetun VPN as user service
#
# Usage:
# myModules.qbittorrentVpn = {
# enable = true;
# configDir = "/home/user/qbittorrent/config";
# downloadsDir = "/home/user/qbittorrent/downloads";
# };
{
config,
lib,
pkgs,
...
}:
let
cfg = config.myModules.qbittorrentVpn;
in
{
options.myModules.qbittorrentVpn = {
enable = lib.mkEnableOption "qBittorrent via VPN container";
image = lib.mkOption {
type = lib.types.str;
default = "lscr.io/linuxserver/qbittorrent:latest";
description = "qBittorrent container image";
};
configDir = lib.mkOption {
type = lib.types.str;
description = "Path to qBittorrent config directory";
};
downloadsDir = lib.mkOption {
type = lib.types.str;
description = "Path to downloads directory";
};
webPort = lib.mkOption {
type = lib.types.port;
default = 8080;
description = "WebUI port (inside container)";
};
timezone = lib.mkOption {
type = lib.types.str;
default = "Europe/Berlin";
description = "Container timezone";
};
vpnContainer = lib.mkOption {
type = lib.types.str;
default = "gluetun";
description = "Name of VPN container to route through";
};
vpnService = lib.mkOption {
type = lib.types.str;
default = "gluetun.service";
description = "Systemd service name of VPN container";
};
};
config = lib.mkIf cfg.enable {
systemd.user.services.qbittorrent = {
Unit = {
Description = "qBittorrent Container (Rootless)";
After = [ cfg.vpnService ];
Requires = [ cfg.vpnService ];
};
Service = {
Restart = "always";
ExecStartPre = "-${pkgs.podman}/bin/podman stop qbittorrent";
ExecStart = ''
${pkgs.podman}/bin/podman run --rm --name qbittorrent \
--network=container:${cfg.vpnContainer} \
-e PUID=0 \
-e PGID=0 \
-e TZ=${cfg.timezone} \
-e WEBUI_PORT=${toString cfg.webPort} \
-v ${cfg.configDir}:/config \
-v ${cfg.downloadsDir}:/downloads \
${cfg.image}
'';
ExecStop = "${pkgs.podman}/bin/podman stop qbittorrent";
};
Install = {
WantedBy = [ "default.target" ];
};
};
};
}

View file

@ -0,0 +1,87 @@
# SillyTavern Module (Home Manager)
# Provides: SillyTavern as rootless container
#
# Usage:
# myModules.sillytavern = {
# enable = true;
# };
{
config,
lib,
pkgs,
...
}:
let
cfg = config.myModules.sillytavern;
in
{
options.myModules.sillytavern = {
enable = lib.mkEnableOption "SillyTavern container";
image = lib.mkOption {
type = lib.types.str;
default = "ghcr.io/sillytavern/sillytavern:latest";
description = "SillyTavern container image";
};
port = lib.mkOption {
type = lib.types.port;
default = 8000;
description = "Host port for SillyTavern";
};
configDir = lib.mkOption {
type = lib.types.str;
default = "/home/ashie/nixos/sillytavern/config";
description = "Path to config directory";
};
dataDir = lib.mkOption {
type = lib.types.str;
default = "/home/ashie/nixos/sillytavern/data";
description = "Path to data directory";
};
pluginsDir = lib.mkOption {
type = lib.types.str;
default = "/home/ashie/nixos/sillytavern/plugins";
description = "Path to plugins directory";
};
};
config = lib.mkIf cfg.enable {
systemd.user.services.sillytavern = {
Unit = {
Description = "SillyTavern Container (Rootless)";
After = [ "network-online.target" ];
Wants = [ "network-online.target" ];
};
Service = {
Restart = "always";
ExecStartPre = [
"-${pkgs.podman}/bin/podman stop sillytavern"
"${pkgs.podman}/bin/podman network create antigravity-net --ignore"
];
ExecStart = ''
${pkgs.podman}/bin/podman run --rm --name sillytavern \
--network=antigravity-net \
--network-alias=sillytavern \
--dns=8.8.8.8 \
-v ${cfg.configDir}:/home/node/app/config \
-v ${cfg.dataDir}:/home/node/app/data \
-v ${cfg.pluginsDir}:/home/node/app/plugins \
-p 127.0.0.1:${toString cfg.port}:8000 \
${cfg.image}
'';
ExecStop = "${pkgs.podman}/bin/podman stop sillytavern";
};
Install = {
WantedBy = [ "default.target" ];
};
};
};
}

36
modules/home/theme.nix Normal file
View file

@ -0,0 +1,36 @@
{
pkgs,
...
}:
{
home.pointerCursor = {
gtk.enable = true;
x11.enable = true;
package = pkgs.bibata-cursors;
name = "Bibata-Modern-Ice";
size = 24;
};
gtk = {
enable = true;
iconTheme = {
name = "Papirus-Dark";
package = pkgs.papirus-icon-theme;
};
theme = {
name = "Catppuccin-Mocha-Standard-Mauve-Dark";
package = pkgs.catppuccin-gtk.override {
accents = [ "mauve" ];
size = "standard";
tweaks = [ "rimless" "black" ];
variant = "mocha";
};
};
};
qt = {
enable = true;
platformTheme.name = "gtk";
style.name = "gtk2";
};
}

View file

@ -0,0 +1,90 @@
# Unified Router Module (Home Manager)
# Provides: Unified API router as rootless container
#
# Usage:
# myModules.unifiedRouter = {
# enable = true;
# };
{
config,
lib,
pkgs,
...
}:
let
cfg = config.myModules.unifiedRouter;
in
{
options.myModules.unifiedRouter = {
enable = lib.mkEnableOption "Unified API Router";
image = lib.mkOption {
type = lib.types.str;
default = "localhost/unified-router:latest";
description = "Unified Router container image";
};
port = lib.mkOption {
type = lib.types.port;
default = 6767;
description = "Host port for Unified Router";
};
environmentFile = lib.mkOption {
type = lib.types.str;
default = "/run/secrets/rendered/api_key.env";
description = "Path to environment file containing API_KEY";
};
antigravityPath = lib.mkOption {
type = lib.types.str;
default = "/home/ashie/nixos/antigravity-src";
description = "Path to antigravity-src directory";
};
dataDir = lib.mkOption {
type = lib.types.str;
default = "/home/ashie/.local/share/unified-router";
description = "Path to persist container data (accounts.json, etc.)";
};
};
config = lib.mkIf cfg.enable {
systemd.user.services.unified-router = {
Unit = {
Description = "Unified API Router Container (Rootless)";
After = [ "network-online.target" ];
Wants = [ "network-online.target" ];
};
Service = {
Environment = "PATH=/run/wrappers/bin:/run/current-system/sw/bin";
Restart = "always";
RestartSec = "10s";
ExecStartPre = [
# Best effort cleanup, ignore errors
"-${pkgs.podman}/bin/podman system migrate"
"-${pkgs.podman}/bin/podman rm -f unified-router --ignore"
"-${pkgs.podman}/bin/podman stop unified-router --ignore"
# Network creation removed (host mode)
"${pkgs.coreutils}/bin/mkdir -p ${cfg.dataDir}"
];
ExecStart = ''
${pkgs.podman}/bin/podman run --replace --rm --name unified-router \
--user 0 \
--network=host \
-e PORT=${toString cfg.port} \
--env-file=${cfg.environmentFile} \
-e LOG_LEVEL=debug \
-v ${cfg.dataDir}:/app/data \
${cfg.image}
'';
ExecStop = "${pkgs.podman}/bin/podman stop -t 10 unified-router";
};
Install = {
WantedBy = [ "default.target" ];
};
};
};
}