init
This commit is contained in:
commit
2be8de47fa
87 changed files with 11501 additions and 0 deletions
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
*.key
|
||||
antigravity-src/
|
||||
*.env
|
||||
chars/
|
||||
*chars/
|
||||
scripts/data_generator/GEMINI.md
|
||||
REFRESH_CANDIDATE
|
||||
codex2api
|
||||
codex2api/
|
||||
TOKENA
|
||||
personal-website/
|
||||
FINDINGS.md
|
||||
VULNERAB*
|
||||
sillytavern
|
||||
unified-router/
|
||||
unified_router-nodejs/
|
||||
.agent/
|
||||
old/
|
||||
11
.sops.yaml
Normal file
11
.sops.yaml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
keys:
|
||||
- &user_ashie age1g76q4cec3qykmkzrd6f4fxxpafj5fsut4jk7pklweuff97scpuusnwdknu
|
||||
- &host_ashie_nixos ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKqErzhr2RWGOdfZo1udpWANe0LeMvFpLbQKKa3/aKnf
|
||||
|
||||
creation_rules:
|
||||
- path_regex: secrets/secrets.yaml$
|
||||
key_groups:
|
||||
- age:
|
||||
- *user_ashie
|
||||
- ssh:
|
||||
- *host_ashie_nixos
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2025 ashisgreat22
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
320
README.md
Normal file
320
README.md
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
# NixOS Configuration
|
||||
|
||||
Personal NixOS configuration with Hyprland, containerized services, and security hardening.
|
||||
|
||||
> **Note:** Parts of this configuration were created with the assistance of AI tools.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Apply configuration
|
||||
doas nixos-rebuild switch --flake ~/nixos#nixos
|
||||
|
||||
# Update flake inputs
|
||||
nix flake update
|
||||
|
||||
# Test configuration without applying
|
||||
doas nixos-rebuild dry-run --flake ~/nixos#nixos
|
||||
```
|
||||
|
||||
## Using These Modules
|
||||
|
||||
Others can import individual modules from this flake:
|
||||
|
||||
```nix
|
||||
{
|
||||
inputs.ashie-nixos.url = "github:ashisgreat22/nixos";
|
||||
|
||||
outputs = { nixpkgs, ashie-nixos, ... }: {
|
||||
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
|
||||
modules = [
|
||||
ashie-nixos.nixosModules.security
|
||||
ashie-nixos.nixosModules.kernelHardening
|
||||
{
|
||||
myModules.security.enable = true;
|
||||
myModules.kernelHardening.enable = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Available Modules
|
||||
|
||||
| Module | Description |
|
||||
| --------------------------------------- | ------------------------------ |
|
||||
| `nixosModules.security` | doas, audit logging, AppArmor |
|
||||
| `nixosModules.kernelHardening` | Boot params, sysctl, ZRAM |
|
||||
| `nixosModules.dnsOverTls` | DNSSEC + DNS-over-TLS |
|
||||
| `nixosModules.cloudflareFirewall` | nftables Cloudflare-only rules |
|
||||
| `nixosModules.caddyCloudflare` | Caddy with DNS-01 ACME |
|
||||
| `nixosModules.podman` | Podman container runtime |
|
||||
| `nixosModules.browserVpn` | VPN-isolated browsers |
|
||||
| `homeManagerModules.hyprlandCatppuccin` | Themed Hyprland config |
|
||||
| `homeManagerModules.gluetunUser` | Rootless VPN container |
|
||||
| `homeManagerModules.qbittorrentVpn` | qBittorrent through VPN |
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
~/nixos/
|
||||
├── configuration.nix # Main config (enables modules via myModules.*)
|
||||
├── flake.nix # Flake inputs, outputs, and module exports
|
||||
├── hardware-configuration.nix
|
||||
├── home.nix # Home Manager entry point
|
||||
├── modules/ # Reusable NixOS modules
|
||||
│ ├── default.nix # Imports all system modules
|
||||
│ ├── system/ # System-level modules
|
||||
│ │ ├── security.nix # doas, audit, AppArmor
|
||||
│ │ ├── kernel-hardening.nix # Boot params, sysctl, ZRAM
|
||||
│ │ ├── dns-over-tls.nix # DNSSEC + DoT
|
||||
│ │ ├── cloudflare-firewall.nix # nftables rules
|
||||
│ │ ├── caddy-cloudflare.nix # Caddy + DNS-01
|
||||
│ │ ├── podman.nix # Container runtime
|
||||
│ │ └── browser-vpn.nix # VPN-isolated browsers
|
||||
│ └── home/ # Home Manager modules
|
||||
│ ├── hyprland-catppuccin.nix
|
||||
│ ├── gluetun-user.nix
|
||||
│ ├── qbittorrent-vpn.nix
|
||||
│ └── browser-container-update.nix
|
||||
├── system/ # Host-specific system config
|
||||
│ ├── boot.nix # Bootloader
|
||||
│ ├── hardware.nix # GPU, USBGuard, fonts
|
||||
│ ├── networking.nix # Hostname, ddclient
|
||||
│ ├── packages.nix # System packages
|
||||
│ ├── services.nix # Steam, Caddy vhosts
|
||||
│ └── secrets.nix # SOPS secrets
|
||||
├── home/ # Host-specific Home Manager config
|
||||
│ ├── fastfetch.nix, kitty.nix, steam.nix, vscode.nix
|
||||
├── containers/ # Container Dockerfiles
|
||||
│ ├── firefox-wayland/ # Isolated Firefox
|
||||
│ ├── thorium-wayland/ # Isolated Thorium
|
||||
│ └── tor-browser-wayland/
|
||||
├── unified_router/ # API routing service
|
||||
├── codex2api/ # Codex API proxy
|
||||
├── antigravity-src/ # Antigravity2API source
|
||||
└── secrets/ # SOPS-encrypted secrets
|
||||
```
|
||||
|
||||
## Integrated Services
|
||||
|
||||
### API Ecosystem
|
||||
|
||||
A microservices architecture for managing LLM interactions:
|
||||
|
||||
- **Unified Router** (`unified_router/`)
|
||||
- **Codex2API** (`codex2api/`)
|
||||
- **Antigravity2API** (`antigravity-src/`)
|
||||
- **Data Generator** (`scripts/data_generator/`): Tool for generating synthetic training data.
|
||||
|
||||
### Web Services (via Caddy)
|
||||
|
||||
| Service | URL | Port |
|
||||
| --------------- | --------------------- | ----------- |
|
||||
| Open WebUI | `chat.ashisgreat.xyz` | 3000 → 8080 |
|
||||
| Unified Router | `api.ashisgreat.xyz` | 6767 |
|
||||
| Antigravity2API | (Internal) | 8045 |
|
||||
|
||||
### Containers
|
||||
|
||||
```bash
|
||||
# View running containers
|
||||
podman ps
|
||||
|
||||
# View container logs
|
||||
podman logs open-webui
|
||||
podman logs antigravity2api
|
||||
```
|
||||
|
||||
## Isolated Browsers (VPN)
|
||||
|
||||
Browsers running in containers routed through WireGuard VPN.
|
||||
|
||||
### Firefox
|
||||
|
||||
```bash
|
||||
# Launch isolated Firefox
|
||||
firefox-vpn-podman
|
||||
|
||||
# Or use commands directly
|
||||
firefox-vpn-podman run # Start Firefox
|
||||
firefox-vpn-podman stop # Stop containers
|
||||
firefox-vpn-podman status # Check status
|
||||
firefox-vpn-podman build # Rebuild container image
|
||||
```
|
||||
|
||||
### Tor Browser
|
||||
|
||||
```bash
|
||||
# Launch isolated Tor Browser
|
||||
tor-browser-vpn-podman
|
||||
|
||||
# Or use commands directly
|
||||
tor-browser-vpn-podman run # Start Tor Browser
|
||||
tor-browser-vpn-podman stop # Stop containers
|
||||
tor-browser-vpn-podman status # Check status
|
||||
tor-browser-vpn-podman build # Rebuild container image
|
||||
```
|
||||
|
||||
> **Note:** Traffic flows through both the VPN and Tor network for double isolation.
|
||||
|
||||
### Thorium Browser
|
||||
|
||||
```bash
|
||||
# Launch isolated Thorium Browser
|
||||
thorium-vpn-podman
|
||||
|
||||
# Or use commands directly
|
||||
thorium-vpn-podman run # Start Thorium
|
||||
thorium-vpn-podman stop # Stop containers
|
||||
thorium-vpn-podman status # Check status
|
||||
thorium-vpn-podman build # Rebuild container image
|
||||
```
|
||||
|
||||
### Auto-Updates
|
||||
|
||||
Browser containers are automatically rebuilt weekly via systemd timer.
|
||||
|
||||
```bash
|
||||
# Check timer status
|
||||
systemctl --user status browser-containers-update.timer
|
||||
|
||||
# Manually trigger update
|
||||
systemctl --user start browser-containers-update
|
||||
|
||||
# View update logs
|
||||
journalctl --user -u browser-containers-update -n 50
|
||||
```
|
||||
|
||||
## qBittorrent (VPN)
|
||||
|
||||
User service running through gluetun VPN container.
|
||||
|
||||
```bash
|
||||
# Start/stop
|
||||
systemctl --user start qbittorrent
|
||||
systemctl --user stop qbittorrent
|
||||
|
||||
# View status
|
||||
systemctl --user status gluetun
|
||||
systemctl --user status qbittorrent
|
||||
|
||||
# Access WebUI (through VPN container)
|
||||
# http://127.0.0.1:8080
|
||||
```
|
||||
|
||||
## Secrets Management (SOPS)
|
||||
|
||||
Secrets are encrypted with AGE and decrypted at activation time.
|
||||
|
||||
```bash
|
||||
# Edit secrets
|
||||
sops secrets/secrets.yaml
|
||||
|
||||
# Add new secret to secrets.nix, then re-encrypt
|
||||
sops updatekeys secrets/secrets.yaml
|
||||
```
|
||||
|
||||
## Security Features & Hardening
|
||||
|
||||
### Kernel Hardening
|
||||
|
||||
**Boot Parameters** (runtime protection):
|
||||
|
||||
- `slab_nomerge` - Prevents slab cache merging
|
||||
- `init_on_alloc/free=1` - Zeros memory (use-after-free mitigation)
|
||||
- `page_alloc.shuffle=1` - Randomizes page allocator
|
||||
- `randomize_kstack_offset=on` - Randomizes kernel stack
|
||||
- `vsyscall=none` - Disables legacy vsyscall
|
||||
- `debugfs=off` - Disables kernel debug interface
|
||||
- `oops=panic` - Panics on kernel oops
|
||||
|
||||
**Sysctl Settings**:
|
||||
|
||||
- `kptr_restrict=2` - Hide kernel pointers
|
||||
- `dmesg_restrict=1` - Restrict kernel logs
|
||||
- `ptrace_scope=1` - Restrict debugging
|
||||
- `unprivileged_bpf_disabled=1` - Disable BPF for users
|
||||
|
||||
```bash
|
||||
# Verify boot params after reboot
|
||||
cat /proc/cmdline
|
||||
```
|
||||
|
||||
### Network Security
|
||||
|
||||
- **DNS-over-TLS (DoT)**: Enabled via `systemd-resolved`. Encrypts all DNS queries to Quad9 and Cloudflare.
|
||||
- **Firewall**: `nftables` with Cloudflare-only access on ports 80/443. Direct connections are blocked.
|
||||
- **Caddy**: Uses DNS-01 ACME challenge (via Cloudflare API) for SSL certs. Configured with security headers (HSTS, CSP, etc.).
|
||||
|
||||
### Audit Logging
|
||||
|
||||
```bash
|
||||
# View audit logs
|
||||
sudo ausearch -ts today # Today's events
|
||||
sudo ausearch -k sudoers # Sudoers changes
|
||||
sudo aureport --summary # Summary report
|
||||
```
|
||||
|
||||
### Automatic Updates
|
||||
|
||||
- Runs daily at 4 AM
|
||||
- Downloads updates but doesn't auto-reboot
|
||||
- Apply manually: `sudo nixos-rebuild switch --flake ~/nixos#nixos`
|
||||
|
||||
### Known Security Considerations
|
||||
|
||||
- **Secrets**: `cloudflare.key` is currently a raw file, not managed by SOPS.
|
||||
- **Containers**: Custom service containers may run as root internally.
|
||||
|
||||
## Useful Commands
|
||||
|
||||
```bash
|
||||
# System
|
||||
sudo nixos-rebuild switch --flake ~/nixos#nixos # Apply config
|
||||
sudo nixos-rebuild boot --flake ~/nixos#nixos # Apply on next boot
|
||||
nix flake update # Update all inputs
|
||||
nix-collect-garbage -d # Clean old generations
|
||||
|
||||
# Containers
|
||||
podman system prune -a # Clean unused images
|
||||
podman volume ls # List volumes
|
||||
|
||||
# Firewall
|
||||
sudo nft list ruleset # View nftables
|
||||
sudo nft list set inet cloudflare cloudflare_ipv4 # View Cloudflare IPs
|
||||
|
||||
# Logs
|
||||
journalctl -u caddy -f # Caddy logs
|
||||
journalctl --user -u gluetun -f # VPN logs
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Container network issues
|
||||
|
||||
```bash
|
||||
# Recreate podman network
|
||||
podman network rm antigravity-net
|
||||
sudo systemctl restart podman-network-antigravity-net
|
||||
```
|
||||
|
||||
### Firefox VPN not starting
|
||||
|
||||
```bash
|
||||
# Check gluetun status first
|
||||
systemctl --user status gluetun
|
||||
journalctl --user -u gluetun -n 50
|
||||
|
||||
# Rebuild image if needed
|
||||
firefox-vpn-podman build
|
||||
```
|
||||
|
||||
### Secrets not decrypting
|
||||
|
||||
```bash
|
||||
# Check SOPS key
|
||||
ls -la ~/.config/sops/age/keys.txt
|
||||
sops -d secrets/secrets.yaml # Test decryption
|
||||
```
|
||||
123
configuration.nix
Normal file
123
configuration.nix
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# Noctalia shell
|
||||
environment.systemPackages = with pkgs; [
|
||||
inputs.noctalia.packages.${pkgs.stdenv.hostPlatform.system}.default
|
||||
];
|
||||
|
||||
environment.etc."glfw".source = "${pkgs.glfw}/lib";
|
||||
|
||||
# FORCE Root Filesystem to satisfy assertions
|
||||
fileSystems."/" = lib.mkForce {
|
||||
device = "none";
|
||||
fsType = "tmpfs";
|
||||
options = [
|
||||
"defaults"
|
||||
"size=16G"
|
||||
"mode=755"
|
||||
];
|
||||
};
|
||||
|
||||
imports = [
|
||||
./hosts/nixos/default.nix # Host-specific configuration
|
||||
./hardware-configuration.nix
|
||||
./system/boot.nix # Boot loader settings (non-hardening parts)
|
||||
./system/networking.nix # Host-specific networking (hostname, ddclient)
|
||||
./system/hardware.nix # Hardware-specific (GPU, USBGuard, fonts)
|
||||
./system/services.nix # Host-specific services (Steam, Caddy vhosts)
|
||||
./system/packages.nix # Package list
|
||||
./system/users.nix # User accounts
|
||||
./system/greetd.nix # Display manager
|
||||
./system/kernel.nix # CachyOS kernel
|
||||
./system/locate.nix # mlocate
|
||||
./system/secrets.nix # SOPS secrets
|
||||
./system/compatibility.nix # Compatibility layers (nix-ld)
|
||||
./system/game-drive.nix
|
||||
# ./system/vpn.nix # Uncomment to enable WireGuard VPN
|
||||
];
|
||||
|
||||
nixpkgs.config.allowUnfreePredicate =
|
||||
pkg:
|
||||
builtins.elem (lib.getName pkg) [
|
||||
"steam"
|
||||
"steam-original"
|
||||
"steam-run"
|
||||
"spotify"
|
||||
"antigravity"
|
||||
"vscode-extension-bmewburn-vscode-intelephense-client"
|
||||
"claude-code"
|
||||
"steam-unwrapped"
|
||||
];
|
||||
hardware.enableRedistributableFirmware = true;
|
||||
|
||||
# Enable Fish shell
|
||||
programs.fish.enable = true;
|
||||
|
||||
# Enable Gamemode
|
||||
programs.gamemode.enable = true;
|
||||
|
||||
# Disable command-not-found to prevent info leaks
|
||||
programs.command-not-found.enable = false;
|
||||
|
||||
# Git security exception for flakes
|
||||
programs.git = {
|
||||
enable = true;
|
||||
config.safe.directory = "/home/ashie/nixos";
|
||||
};
|
||||
|
||||
# Automatic security updates
|
||||
system.autoUpgrade = {
|
||||
enable = true;
|
||||
allowReboot = false;
|
||||
dates = "04:00";
|
||||
flake = "/home/ashie/nixos#nixos";
|
||||
};
|
||||
|
||||
time.timeZone = "Europe/Berlin";
|
||||
i18n.defaultLocale = "en_US.UTF-8";
|
||||
i18n.supportedLocales = [
|
||||
"en_US.UTF-8/UTF-8"
|
||||
"de_DE.UTF-8/UTF-8"
|
||||
];
|
||||
|
||||
nix.settings.experimental-features = [
|
||||
"nix-command"
|
||||
"flakes"
|
||||
];
|
||||
nix.settings.allowed-users = [ "ashie" ];
|
||||
nix.settings.sandbox = true;
|
||||
|
||||
# Automatic Garbage Collection
|
||||
nix.gc = {
|
||||
automatic = true;
|
||||
dates = "weekly";
|
||||
options = "--delete-older-than 7d";
|
||||
};
|
||||
|
||||
# Binary caches for CachyOS kernel
|
||||
nix.settings.substituters = [
|
||||
"https://cache.cachyos.org"
|
||||
"https://hyprland.cachix.org"
|
||||
"https://nix-community.cachix.org"
|
||||
"https://attic.xuyh0120.win/lantian"
|
||||
"https://cache.garnix.io"
|
||||
];
|
||||
nix.settings.trusted-public-keys = [
|
||||
"cache.cachyos.org-1:j9qLlx+z0OYBtCqflh9v4I+5fsljqG5l2/C9t0yY18q="
|
||||
"hyprland.cachix.org-1:a7pgxzMz7+chwVL3/pzj6jIBMioiJM7ypFP8PwtkuGc="
|
||||
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
|
||||
"lantian:EeAUQ+W+6r7EtwnmYjeVwx5kOGEBpjlBfPlzGlTNvHc="
|
||||
"cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g="
|
||||
];
|
||||
|
||||
# Enable performance optimizations
|
||||
myModules.performance.enable = true;
|
||||
|
||||
system.stateVersion = "25.05";
|
||||
}
|
||||
61
containers/arch-kitty/Dockerfile
Normal file
61
containers/arch-kitty/Dockerfile
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
FROM archlinux:latest
|
||||
|
||||
# Update system and install dependencies
|
||||
# wayland, kitty, fonts, coreutils, curl, iputils
|
||||
RUN pacman -Syu --noconfirm && \
|
||||
pacman -S --noconfirm \
|
||||
kitty \
|
||||
wayland \
|
||||
mesa \
|
||||
vulkan-intel \
|
||||
vulkan-radeon \
|
||||
noto-fonts \
|
||||
noto-fonts-emoji \
|
||||
noto-fonts-cjk \
|
||||
ttf-jetbrains-mono \
|
||||
ttf-dejavu \
|
||||
bash \
|
||||
base-devel \
|
||||
git \
|
||||
coreutils \
|
||||
curl \
|
||||
iputils \
|
||||
libpulse \
|
||||
pipewire \
|
||||
sudo \
|
||||
starship \
|
||||
eza \
|
||||
git \
|
||||
hyfetch \
|
||||
fastfetch \
|
||||
&& pacman -Scc --noconfirm
|
||||
|
||||
# Create non-root user 'arch-user' (matching typical UID 1000)
|
||||
RUN useradd -m -u 1000 -s /bin/bash arch-user && \
|
||||
echo "arch-user ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/arch-user
|
||||
|
||||
# Switch to user to build yay
|
||||
USER arch-user
|
||||
WORKDIR /home/arch-user
|
||||
|
||||
# Install yay AUR helper
|
||||
RUN cd /tmp && \
|
||||
git clone https://aur.archlinux.org/yay.git && \
|
||||
cd yay && \
|
||||
makepkg -si --noconfirm && \
|
||||
cd .. && \
|
||||
rm -rf yay
|
||||
|
||||
# Hardening: Set password to 'arch' and remove NOPASSWD for runtime security
|
||||
USER root
|
||||
RUN echo "arch-user:arch" | chpasswd && \
|
||||
sed -i 's/NOPASSWD: //g' /etc/sudoers.d/arch-user
|
||||
USER arch-user
|
||||
|
||||
# Ensure .config/kitty exists
|
||||
RUN mkdir -p /home/arch-user/.config/kitty
|
||||
|
||||
ENV MOZ_ENABLE_WAYLAND=1
|
||||
ENV XDG_RUNTIME_DIR=/tmp
|
||||
|
||||
CMD ["kitty"]
|
||||
66
containers/firefox-wayland/Dockerfile
Normal file
66
containers/firefox-wayland/Dockerfile
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
FROM debian:trixie-slim
|
||||
|
||||
# Avoid interactive prompts
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install Firefox and dependencies
|
||||
# We use firefox-esr as it is the standard in Debian
|
||||
# mesa-utils, libgl1-mesa-dri, libglx-mesa0 for AMD GPU support
|
||||
# wayland support packages
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
firefox-esr \
|
||||
mesa-utils \
|
||||
libgl1-mesa-dri \
|
||||
libglx-mesa0 \
|
||||
libwayland-client0 \
|
||||
libwayland-egl1 \
|
||||
wayland-protocols \
|
||||
pulseaudio \
|
||||
libpulse0 \
|
||||
fonts-noto \
|
||||
fonts-noto-color-emoji \
|
||||
fonts-dejavu \
|
||||
dbus \
|
||||
dbus-x11 \
|
||||
libdbus-glib-1-2 \
|
||||
libxtst6 \
|
||||
libgtk-3-0 \
|
||||
libx11-xcb1 \
|
||||
libpci3 \
|
||||
libvulkan1 \
|
||||
mesa-vulkan-drivers \
|
||||
ca-certificates \
|
||||
wget \
|
||||
unzip \
|
||||
gnome-themes-extra \
|
||||
adwaita-icon-theme \
|
||||
gsettings-desktop-schemas \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Catppuccin Mocha GTK Theme
|
||||
RUN mkdir -p /usr/share/themes/Catppuccin-Mocha-Standard-Blue-Dark && \
|
||||
wget -qO /tmp/theme.zip https://github.com/catppuccin/gtk/releases/download/v1.0.3/catppuccin-mocha-blue-standard+default.zip && \
|
||||
unzip -q /tmp/theme.zip -d /usr/share/themes/ && \
|
||||
mv /usr/share/themes/catppuccin-mocha-blue-standard+default/* /usr/share/themes/Catppuccin-Mocha-Standard-Blue-Dark/ && \
|
||||
rm -rf /tmp/theme.zip /usr/share/themes/catppuccin-mocha-blue-standard+default
|
||||
|
||||
# Create non-root user
|
||||
RUN useradd -m -s /bin/bash firefox-user
|
||||
|
||||
# Establish GTK settings
|
||||
RUN mkdir -p /home/firefox-user/.config/gtk-3.0 && \
|
||||
printf "[Settings]\ngtk-theme-name=Catppuccin-Mocha-Standard-Blue-Dark\ngtk-application-prefer-dark-theme=1\n" > /home/firefox-user/.config/gtk-3.0/settings.ini && \
|
||||
chown -R firefox-user:firefox-user /home/firefox-user/.config
|
||||
|
||||
# Setup directories for runtime
|
||||
RUN mkdir -p /run/user/1000 && chown firefox-user:firefox-user /run/user/1000
|
||||
|
||||
USER firefox-user
|
||||
WORKDIR /home/firefox-user
|
||||
|
||||
# Set environment variables for Wayland
|
||||
ENV MOZ_ENABLE_WAYLAND=1
|
||||
ENV XDG_RUNTIME_DIR=/run/user/1000
|
||||
ENV GTK_THEME=Catppuccin-Mocha-Standard-Blue-Dark
|
||||
|
||||
CMD ["dbus-run-session", "firefox-esr", "--new-instance", "--allow-downgrade"]
|
||||
27
containers/p-stream-backend/Dockerfile
Normal file
27
containers/p-stream-backend/Dockerfile
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
FROM node:22-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install git and openssl (needed for Prisma)
|
||||
RUN apk add --no-cache git openssl
|
||||
|
||||
# Clone repository
|
||||
RUN git clone https://github.com/p-stream/backend .
|
||||
|
||||
# Install dependencies (use npm install since upstream lockfile can be out of sync)
|
||||
RUN npm install
|
||||
|
||||
# We do NOT run build here because we need ENV vars at build time?
|
||||
# The original Dockerfile used ARGs. We can do that or just build at runtime if we want dynamic config.
|
||||
# But for stability, let's follow their pattern but use defaults.
|
||||
# We will use ENV at runtime to override.
|
||||
|
||||
# Prisma generate needs DATABASE_URL format (doesn't need actual connectivity)
|
||||
ENV DATABASE_URL=postgresql://dummy:dummy@localhost:5432/dummy
|
||||
RUN npx prisma generate
|
||||
RUN npm run build
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
# We need a custom entrypoint to handle DB migrations
|
||||
CMD ["sh", "-c", "npx prisma migrate deploy && node .output/server/index.mjs"]
|
||||
47
containers/p-stream/Dockerfile
Normal file
47
containers/p-stream/Dockerfile
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# Based on the official Dockerfile but modified for self-building
|
||||
FROM node:20-alpine as build
|
||||
WORKDIR /app
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
RUN corepack enable
|
||||
|
||||
# Install git to clone the repo
|
||||
RUN apk add --no-cache git
|
||||
|
||||
# Clone the repository
|
||||
# We clone main branch. To pin a version, we could checkout a specific hash.
|
||||
RUN git clone https://github.com/p-stream/p-stream .
|
||||
|
||||
# Install dependencies
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
|
||||
|
||||
# Build Arguments
|
||||
# These determine the features enabled in the static build.
|
||||
ARG TMDB_READ_API_KEY=""
|
||||
ARG CORS_PROXY_URL=""
|
||||
ARG BACKEND_URL=""
|
||||
ARG PWA_ENABLED="true"
|
||||
ARG OPENSEARCH_ENABLED="false"
|
||||
# Enable onboarding so the user can potentially configure things in UI (if supported)
|
||||
# or at least not crash without a key (though TMDB key is usually required for content).
|
||||
ARG HAS_ONBOARDING="true"
|
||||
ARG ALLOW_FEBBOX_KEY="true"
|
||||
|
||||
# Set Env vars for Vite
|
||||
ENV VITE_PWA_ENABLED=${PWA_ENABLED}
|
||||
ENV VITE_OPENSEARCH_ENABLED=${OPENSEARCH_ENABLED}
|
||||
ENV VITE_TMDB_READ_API_KEY=${TMDB_READ_API_KEY}
|
||||
ENV VITE_CORS_PROXY_URL=${CORS_PROXY_URL}
|
||||
ENV VITE_BACKEND_URL=${BACKEND_URL}
|
||||
ENV VITE_HAS_ONBOARDING=${HAS_ONBOARDING}
|
||||
ENV VITE_ALLOW_FEBBOX_KEY=${ALLOW_FEBBOX_KEY}
|
||||
|
||||
# Build the app
|
||||
RUN pnpm run build
|
||||
|
||||
# Production environment
|
||||
FROM nginx:stable-alpine
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
23
containers/p-stream/nginx.conf
Normal file
23
containers/p-stream/nginx.conf
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
|
||||
# Handle SPA routing: serve index.html if file doesn't exist
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# Cache static assets
|
||||
location ~* \.(?:ico|css|js|gif|jpe?g|png|woff2?|eot|ttf|svg|map)$ {
|
||||
expires 6m;
|
||||
access_log off;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
68
containers/thorium-wayland/Dockerfile
Normal file
68
containers/thorium-wayland/Dockerfile
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
FROM --platform=linux/amd64 docker.io/library/debian:trixie-slim
|
||||
|
||||
# Install dependencies for Thorium
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
bash \
|
||||
wget \
|
||||
xz-utils \
|
||||
ca-certificates \
|
||||
libgtk-3-0 \
|
||||
libnss3 \
|
||||
libatk1.0-0 \
|
||||
libatk-bridge2.0-0 \
|
||||
libcups2 \
|
||||
libdrm2 \
|
||||
libxkbcommon0 \
|
||||
libxcomposite1 \
|
||||
libxdamage1 \
|
||||
libxrandr2 \
|
||||
libgbm1 \
|
||||
libasound2 \
|
||||
libpulse0 \
|
||||
libpango-1.0-0 \
|
||||
libcairo2 \
|
||||
libdbus-1-3 \
|
||||
libexpat1 \
|
||||
libxext6 \
|
||||
libxfixes3 \
|
||||
libx11-6 \
|
||||
libxcb1 \
|
||||
libatspi2.0-0 \
|
||||
fonts-noto \
|
||||
fonts-dejavu-core \
|
||||
unzip \
|
||||
xdg-utils \
|
||||
libegl1 \
|
||||
libgl1 \
|
||||
libgles2 \
|
||||
--no-install-recommends \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Catppuccin Mocha GTK Theme
|
||||
RUN mkdir -p /usr/share/themes/Catppuccin-Mocha-Standard-Blue-Dark && \
|
||||
wget -O /tmp/theme.zip https://github.com/catppuccin/gtk/releases/download/v1.0.3/catppuccin-mocha-blue-standard+default.zip && \
|
||||
unzip /tmp/theme.zip -d /usr/share/themes/ && \
|
||||
mv /usr/share/themes/catppuccin-mocha-blue-standard+default/* /usr/share/themes/Catppuccin-Mocha-Standard-Blue-Dark/ && \
|
||||
rm -rf /tmp/theme.zip /usr/share/themes/catppuccin-mocha-blue-standard+default
|
||||
|
||||
# Create non-root user
|
||||
RUN useradd -m -s /bin/bash thorium-user
|
||||
|
||||
# Download and install Thorium (AVX2)
|
||||
# Using specific version to ensure stability, can be updated later
|
||||
RUN wget -O /tmp/thorium.deb "https://github.com/Alex313031/Thorium/releases/download/M128.0.6613.189/thorium-browser_128.0.6613.189_AVX2.deb" && \
|
||||
apt-get update && \
|
||||
apt-get install -y /tmp/thorium.deb && \
|
||||
rm /tmp/thorium.deb && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create settings.ini to enforce theme
|
||||
RUN mkdir -p /home/thorium-user/.config/gtk-3.0 && \
|
||||
echo -e "[Settings]\ngtk-theme-name=Catppuccin-Mocha-Standard-Blue-Dark\ngtk-application-prefer-dark-theme=1" > /home/thorium-user/.config/gtk-3.0/settings.ini && \
|
||||
chown -R thorium-user:thorium-user /home/thorium-user/.config
|
||||
|
||||
USER thorium-user
|
||||
WORKDIR /home/thorium-user
|
||||
|
||||
# Thorium flags for Wayland and features
|
||||
CMD ["thorium-browser", "--ozone-platform=wayland", "--enable-features=UseOzonePlatform", "--enable-gpu-rasterization", "--enable-zero-copy"]
|
||||
60
containers/tor-browser-wayland/Dockerfile
Normal file
60
containers/tor-browser-wayland/Dockerfile
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
FROM --platform=linux/amd64 docker.io/library/debian:trixie-slim
|
||||
|
||||
# Install dependencies for Tor Browser
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
bash \
|
||||
wget \
|
||||
xz-utils \
|
||||
libgtk-3-0 \
|
||||
libdbus-glib-1-2 \
|
||||
libxt6 \
|
||||
libasound2 \
|
||||
libpulse0 \
|
||||
libx11-xcb1 \
|
||||
libxcomposite1 \
|
||||
libxdamage1 \
|
||||
libxrandr2 \
|
||||
libxtst6 \
|
||||
libxcursor1 \
|
||||
libgl1 \
|
||||
libegl1 \
|
||||
fonts-noto \
|
||||
fonts-dejavu-core \
|
||||
ca-certificates \
|
||||
unzip \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Catppuccin Mocha GTK Theme (consistency with Firefox)
|
||||
RUN mkdir -p /usr/share/themes/Catppuccin-Mocha-Standard-Blue-Dark && \
|
||||
wget -O /tmp/theme.zip https://github.com/catppuccin/gtk/releases/download/v1.0.3/catppuccin-mocha-blue-standard+default.zip && \
|
||||
unzip /tmp/theme.zip -d /usr/share/themes/ && \
|
||||
mv /usr/share/themes/catppuccin-mocha-blue-standard+default/* /usr/share/themes/Catppuccin-Mocha-Standard-Blue-Dark/ && \
|
||||
rm -rf /tmp/theme.zip /usr/share/themes/catppuccin-mocha-blue-standard+default
|
||||
|
||||
# Create non-root user
|
||||
RUN useradd -m -s /bin/bash tor-user
|
||||
|
||||
# Download and install Tor Browser (fetches latest stable version)
|
||||
RUN TB_VERSION=$(wget -qO- "https://www.torproject.org/download/" | \
|
||||
sed -n 's/.*\/dist\/torbrowser\/\([0-9.]*\)\/tor-browser-linux.*/\1/p' | \
|
||||
head -1) && \
|
||||
if [ -z "$TB_VERSION" ]; then TB_VERSION="15.0.3"; fi && \
|
||||
echo "Installing Tor Browser version: $TB_VERSION" && \
|
||||
wget -O /tmp/tor.tar.xz "https://www.torproject.org/dist/torbrowser/${TB_VERSION}/tor-browser-linux-x86_64-${TB_VERSION}.tar.xz" && \
|
||||
cd /home/tor-user && \
|
||||
tar -xJf /tmp/tor.tar.xz && \
|
||||
rm /tmp/tor.tar.xz && \
|
||||
chown -R tor-user:tor-user /home/tor-user
|
||||
|
||||
# Create settings.ini to enforce theme
|
||||
RUN mkdir -p /home/tor-user/.config/gtk-3.0 && \
|
||||
echo -e "[Settings]\ngtk-theme-name=Catppuccin-Mocha-Standard-Blue-Dark\ngtk-application-prefer-dark-theme=1" > /home/tor-user/.config/gtk-3.0/settings.ini && \
|
||||
chown -R tor-user:tor-user /home/tor-user/.config
|
||||
|
||||
USER tor-user
|
||||
WORKDIR /home/tor-user
|
||||
|
||||
# Tor Browser uses its own profile directory within the bundle
|
||||
ENV MOZ_ENABLE_WAYLAND=1
|
||||
|
||||
CMD ["/home/tor-user/tor-browser/Browser/start-tor-browser"]
|
||||
740
flake.lock
generated
Normal file
740
flake.lock
generated
Normal file
|
|
@ -0,0 +1,740 @@
|
|||
{
|
||||
"nodes": {
|
||||
"cachyos-kernel": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1768206129,
|
||||
"narHash": "sha256-BpTer/+8ZSHq4hXbfN/DZh1rru3LVCapp6ks1nyuWj0=",
|
||||
"owner": "CachyOS",
|
||||
"repo": "linux-cachyos",
|
||||
"rev": "8e4d77a4aeef28c8e93fd9b724d61a84b11b384f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "CachyOS",
|
||||
"repo": "linux-cachyos",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"cachyos-kernel-patches": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1768204281,
|
||||
"narHash": "sha256-4GraDM1qDeLxPWlyN7+SaN/lgsvZxW+hAcxb3192+aE=",
|
||||
"owner": "CachyOS",
|
||||
"repo": "kernel-patches",
|
||||
"rev": "11908b28acba425e0acfa8a68f6488e665d6e25c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "CachyOS",
|
||||
"repo": "kernel-patches",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"catppuccin": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1767967164,
|
||||
"narHash": "sha256-Cx4VETh9dGoQYDtWhre7g66d7SAr+h1h6f+SSHxVrck=",
|
||||
"owner": "catppuccin",
|
||||
"repo": "nix",
|
||||
"rev": "e973584280e3b0e1d5b5a1a5e9948dc222c54af7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "catppuccin",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"crane": {
|
||||
"locked": {
|
||||
"lastModified": 1767744144,
|
||||
"narHash": "sha256-9/9ntI0D+HbN4G0TrK3KmHbTvwgswz7p8IEJsWyef8Q=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "2fb033290bf6b23f226d4c8b32f7f7a16b043d7e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1767039857,
|
||||
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat_2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1767039857,
|
||||
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1768135262,
|
||||
"narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts_2": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"steam-config-nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1765835352,
|
||||
"narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "a34fae9c08a15ad73f295041fec82323541400a9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"lanzaboote",
|
||||
"pre-commit",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709087332,
|
||||
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"home-manager": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1768271922,
|
||||
"narHash": "sha256-zmFw7AtcmfMxW3vR7AiGeQQeHhdrd2x7a3hxzd6vJYI=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "fbd566923adcfa67be512a14a79467e2ab8a5777",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"home-manager_2": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"impermanence",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1747978958,
|
||||
"narHash": "sha256-pQQnbxWpY3IiZqgelXHIe/OAE/Yv4NSQq7fch7M6nXQ=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "7419250703fd5eb50e99bdfb07a86671939103ea",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"impermanence": {
|
||||
"inputs": {
|
||||
"home-manager": "home-manager_2",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1767822991,
|
||||
"narHash": "sha256-iyrn9AcPZCoyxX4OT8eMkBsjG7SRUQXXS/V1JzxS7rA=",
|
||||
"owner": "nix-community",
|
||||
"repo": "impermanence",
|
||||
"rev": "82e5bc4508cab9e8d5a136626276eb5bbce5e9c5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "impermanence",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"ixx": {
|
||||
"inputs": {
|
||||
"flake-utils": [
|
||||
"nix-bwrapper",
|
||||
"nuschtosSearch",
|
||||
"flake-utils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nix-bwrapper",
|
||||
"nuschtosSearch",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1754860581,
|
||||
"narHash": "sha256-EM0IE63OHxXCOpDHXaTyHIOk2cNvMCGPqLt/IdtVxgk=",
|
||||
"owner": "NuschtOS",
|
||||
"repo": "ixx",
|
||||
"rev": "babfe85a876162c4acc9ab6fb4483df88fa1f281",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NuschtOS",
|
||||
"ref": "v0.1.1",
|
||||
"repo": "ixx",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"lanzaboote": {
|
||||
"inputs": {
|
||||
"crane": "crane",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"pre-commit": "pre-commit",
|
||||
"rust-overlay": "rust-overlay"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1768307256,
|
||||
"narHash": "sha256-3yDvlAqWa0Vk3B9hFRJJrSs1xc+FwVQFLtu//VrTR4c=",
|
||||
"owner": "nix-community",
|
||||
"repo": "lanzaboote",
|
||||
"rev": "7e031eb535a494582f4fc58735b5aecba7b57058",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "lanzaboote",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"libnbtplusplus": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1744811532,
|
||||
"narHash": "sha256-qhmjaRkt+O7A+gu6HjUkl7QzOEb4r8y8vWZMG2R/C6o=",
|
||||
"owner": "PrismLauncher",
|
||||
"repo": "libnbtplusplus",
|
||||
"rev": "531449ba1c930c98e0bcf5d332b237a8566f9d78",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "PrismLauncher",
|
||||
"repo": "libnbtplusplus",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"niri": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"rust-overlay": "rust-overlay_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1768196703,
|
||||
"narHash": "sha256-mttBQdVnVFO3mn+M+oqCsZZOtS2HvXYy+VaHxb8YuMw=",
|
||||
"owner": "YaLTeR",
|
||||
"repo": "niri",
|
||||
"rev": "3672e79369d72297abda8878245ea4ec327062c6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "YaLTeR",
|
||||
"repo": "niri",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-bwrapper": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"nuschtosSearch": "nuschtosSearch",
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1766319780,
|
||||
"narHash": "sha256-Uh5180wjvBtSgtJ9zccZ7hu7bd7nvrnb6ff0nDwT2Rw=",
|
||||
"owner": "Naxdy",
|
||||
"repo": "nix-bwrapper",
|
||||
"rev": "3b0d58d4d3e8da89147369d803926998798443e4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "Naxdy",
|
||||
"repo": "nix-bwrapper",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-cachyos-kernel": {
|
||||
"inputs": {
|
||||
"cachyos-kernel": "cachyos-kernel",
|
||||
"cachyos-kernel-patches": "cachyos-kernel-patches",
|
||||
"flake-compat": "flake-compat_2",
|
||||
"flake-parts": "flake-parts",
|
||||
"nixpkgs": "nixpkgs_4"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1768240180,
|
||||
"narHash": "sha256-7OHZ5iSiiHLteGG9WSQDsGlr731vbNEmraML1Vh8I+s=",
|
||||
"owner": "xddxdd",
|
||||
"repo": "nix-cachyos-kernel",
|
||||
"rev": "b555ec531ba870b0aeecbec46a7c75f9c6e88c09",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "xddxdd",
|
||||
"repo": "nix-cachyos-kernel",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-flatpak": {
|
||||
"locked": {
|
||||
"lastModified": 1767983141,
|
||||
"narHash": "sha256-7ZCulYUD9RmJIDULTRkGLSW1faMpDlPKcbWJLYHoXcs=",
|
||||
"owner": "gmodena",
|
||||
"repo": "nix-flatpak",
|
||||
"rev": "440818969ac2cbd77bfe025e884d0aa528991374",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "gmodena",
|
||||
"ref": "latest",
|
||||
"repo": "nix-flatpak",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1767116409,
|
||||
"narHash": "sha256-5vKw92l1GyTnjoLzEagJy5V5mDFck72LiQWZSOnSicw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "cad22e7d996aea55ecab064e84834289143e44a0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1765674936,
|
||||
"narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1758035966,
|
||||
"narHash": "sha256-qqIJ3yxPiB0ZQTT9//nFGQYn8X/PBoJbofA7hRKZnmE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "8d4ddb19d03c65a36ad8d189d001dc32ffb0306b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1754340878,
|
||||
"narHash": "sha256-lgmUyVQL9tSnvvIvBp7x1euhkkCho7n3TMzgjdvgPoU=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "cab778239e705082fe97bb4990e0d24c50924c04",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_4": {
|
||||
"locked": {
|
||||
"lastModified": 1768207485,
|
||||
"narHash": "sha256-4HuteAAt/c9IXq5u2qRRGxYwL/ohww5J/jml6zJPzpw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "8b162715b04e986c97788e1edf254d319681e4ae",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable-small",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_5": {
|
||||
"locked": {
|
||||
"lastModified": 1768127708,
|
||||
"narHash": "sha256-1Sm77VfZh3mU0F5OqKABNLWxOuDeHIlcFjsXeeiPazs=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "ffbc9f8cbaacfb331b6017d5a5abb21a492c9a38",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-unstable",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"noctalia": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1768305888,
|
||||
"narHash": "sha256-PJ0CbkW/u2M8JBPL1+fR3hljZHl5qJy7BXKKf51EeLE=",
|
||||
"owner": "noctalia-dev",
|
||||
"repo": "noctalia-shell",
|
||||
"rev": "12090997885d087f86c7bcdf748dd31deece7507",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "noctalia-dev",
|
||||
"repo": "noctalia-shell",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nuschtosSearch": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"ixx": "ixx",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1758662783,
|
||||
"narHash": "sha256-igrxT+/MnmcftPOHEb+XDwAMq3Xg1Xy7kVYQaHhPlAg=",
|
||||
"owner": "NuschtOS",
|
||||
"repo": "search",
|
||||
"rev": "7d4c0fc4ffe3bd64e5630417162e9e04e64b27a4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NuschtOS",
|
||||
"repo": "search",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"opencode-flake": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1766754166,
|
||||
"narHash": "sha256-anLh9N8KLqssOMd+xooagd/kLhoU4BChMfEToWmntCg=",
|
||||
"owner": "AodhanHayter",
|
||||
"repo": "opencode-flake",
|
||||
"rev": "57574d4c2d550b24487cb810e2cfcb7c259b0357",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "AodhanHayter",
|
||||
"repo": "opencode-flake",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"lanzaboote",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1767281941,
|
||||
"narHash": "sha256-6MkqajPICgugsuZ92OMoQcgSHnD6sJHwk8AxvMcIgTE=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "f0927703b7b1c8d97511c4116eb9b4ec6645a0fa",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"prismlauncher": {
|
||||
"inputs": {
|
||||
"libnbtplusplus": "libnbtplusplus",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1768284897,
|
||||
"narHash": "sha256-gZS0Pf/fwZGyRmvMQNyE3GWayKdNqVD47u0YRkYZiVQ=",
|
||||
"owner": "PrismLauncher",
|
||||
"repo": "PrismLauncher",
|
||||
"rev": "c2fc0a30b789c63667eb1514b113a2bca6704330",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "PrismLauncher",
|
||||
"repo": "PrismLauncher",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"catppuccin": "catppuccin",
|
||||
"home-manager": "home-manager",
|
||||
"impermanence": "impermanence",
|
||||
"lanzaboote": "lanzaboote",
|
||||
"niri": "niri",
|
||||
"nix-bwrapper": "nix-bwrapper",
|
||||
"nix-cachyos-kernel": "nix-cachyos-kernel",
|
||||
"nix-flatpak": "nix-flatpak",
|
||||
"nixpkgs": "nixpkgs_5",
|
||||
"noctalia": "noctalia",
|
||||
"opencode-flake": "opencode-flake",
|
||||
"prismlauncher": "prismlauncher",
|
||||
"sops-nix": "sops-nix",
|
||||
"steam-config-nix": "steam-config-nix"
|
||||
}
|
||||
},
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"lanzaboote",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1768272338,
|
||||
"narHash": "sha256-Tg/kL8eKMpZtceDvBDQYU8zowgpr7ucFRnpP/AtfuRM=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "03dda130a8701b08b0347fcaf850a190c53a3c1e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"rust-overlay_2": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"niri",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1757989933,
|
||||
"narHash": "sha256-9cpKYWWPCFhgwQTww8S94rTXgg8Q8ydFv9fXM6I8xQM=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "8249aa3442fb9b45e615a35f39eca2fe5510d7c3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"sops-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1768271704,
|
||||
"narHash": "sha256-jJqlW8A3OZ5tYbXphF7U8P8g/3Cn8PPwPa4YlJ/9agg=",
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"rev": "691b8b6713855d0fe463993867291c158472fc6f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"steam-config-nix": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts_2",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1767484813,
|
||||
"narHash": "sha256-zSpaCHGORhPi5tQufxD1NeYNS85sTZzszDcDYGBayvU=",
|
||||
"owner": "different-name",
|
||||
"repo": "steam-config-nix",
|
||||
"rev": "c07a36b9f941766c7f1cf06231fe1fb0cc718a7e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "different-name",
|
||||
"repo": "steam-config-nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"treefmt-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1758728421,
|
||||
"narHash": "sha256-ySNJ008muQAds2JemiyrWYbwbG+V7S5wg3ZVKGHSFu8=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "5eda4ee8121f97b218f7cc73f5172098d458f1d1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
148
flake.nix
Normal file
148
flake.nix
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
{
|
||||
description = "Modular NixOS Configuration with Hyprland";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "nixpkgs/nixos-unstable";
|
||||
|
||||
nix-cachyos-kernel = {
|
||||
url = "github:xddxdd/nix-cachyos-kernel";
|
||||
};
|
||||
|
||||
nix-flatpak = {
|
||||
url = "github:gmodena/nix-flatpak/?ref=latest";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
sops-nix = {
|
||||
url = "github:Mic92/sops-nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
noctalia = {
|
||||
url = "github:noctalia-dev/noctalia-shell";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
steam-config-nix = {
|
||||
url = "github:different-name/steam-config-nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
home-manager = {
|
||||
url = "github:nix-community/home-manager";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
prismlauncher = {
|
||||
url = "github:PrismLauncher/PrismLauncher";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
opencode-flake = {
|
||||
url = "github:AodhanHayter/opencode-flake";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
nix-bwrapper = {
|
||||
url = "github:Naxdy/nix-bwrapper";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
lanzaboote = {
|
||||
url = "github:nix-community/lanzaboote";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
niri = {
|
||||
url = "github:YaLTeR/niri";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
impermanence = {
|
||||
url = "github:nix-community/impermanence";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
catppuccin.url = "github:catppuccin/nix";
|
||||
};
|
||||
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
home-manager,
|
||||
noctalia,
|
||||
nix-flatpak,
|
||||
lanzaboote,
|
||||
niri,
|
||||
...
|
||||
}@inputs:
|
||||
{
|
||||
# Expose reusable NixOS modules for others to import
|
||||
nixosModules = {
|
||||
security = import ./modules/system/security.nix;
|
||||
kernelHardening = import ./modules/system/kernel-hardening.nix;
|
||||
secureBoot = import ./modules/system/secure-boot.nix;
|
||||
dnsOverTls = import ./modules/system/dns-over-tls.nix;
|
||||
cloudflareFirewall = import ./modules/system/cloudflare-firewall.nix;
|
||||
caddyCloudflare = import ./modules/system/caddy-cloudflare.nix;
|
||||
podman = import ./modules/system/podman.nix;
|
||||
browserVpn = import ./modules/system/browser-vpn.nix;
|
||||
default = import ./modules;
|
||||
};
|
||||
|
||||
# Expose reusable Home Manager modules
|
||||
homeManagerModules = {
|
||||
hyprlandCatppuccin = import ./modules/home/hyprland-catppuccin.nix;
|
||||
gluetunUser = import ./modules/home/gluetun-user.nix;
|
||||
qbittorrentVpn = import ./modules/home/qbittorrent-vpn.nix;
|
||||
browserContainerUpdate = import ./modules/home/browser-container-update.nix;
|
||||
protonCachyosUpdater = import ./modules/home/proton-cachyos-updater.nix;
|
||||
default = import ./modules/home;
|
||||
};
|
||||
|
||||
nixosConfigurations.nixos = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
specialArgs = { inherit inputs; };
|
||||
modules = [
|
||||
./configuration.nix
|
||||
./modules # Import all system modules
|
||||
inputs.sops-nix.nixosModules.sops
|
||||
home-manager.nixosModules.home-manager
|
||||
inputs.catppuccin.nixosModules.catppuccin
|
||||
{
|
||||
home-manager = {
|
||||
extraSpecialArgs = { inherit inputs; };
|
||||
useGlobalPkgs = true;
|
||||
useUserPackages = true;
|
||||
backupFileExtension = "backup";
|
||||
users.ashie = import ./home.nix;
|
||||
};
|
||||
}
|
||||
./modules/system/impermanence.nix
|
||||
];
|
||||
};
|
||||
|
||||
nixosConfigurations.impermanence = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
specialArgs = { inherit inputs; };
|
||||
modules = [
|
||||
./configuration.nix
|
||||
./modules
|
||||
inputs.sops-nix.nixosModules.sops
|
||||
home-manager.nixosModules.home-manager
|
||||
inputs.catppuccin.nixosModules.catppuccin
|
||||
{
|
||||
home-manager = {
|
||||
extraSpecialArgs = { inherit inputs; };
|
||||
useGlobalPkgs = true;
|
||||
useUserPackages = true;
|
||||
backupFileExtension = "backup";
|
||||
users.ashie = import ./home.nix;
|
||||
};
|
||||
}
|
||||
./modules/system/impermanence.nix
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
92
hardware-configuration.nix
Normal file
92
hardware-configuration.nix
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
modulesPath,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
imports = [
|
||||
(modulesPath + "/installer/scan/not-detected.nix")
|
||||
];
|
||||
|
||||
boot.initrd.availableKernelModules = [
|
||||
"nvme"
|
||||
"xhci_pci"
|
||||
"ahci"
|
||||
"uas"
|
||||
"usbhid"
|
||||
"sd_mod"
|
||||
];
|
||||
|
||||
boot.initrd.kernelModules = [ ];
|
||||
boot.kernelModules = [ "kvm-amd" ];
|
||||
boot.extraModulePackages = [ ];
|
||||
|
||||
# Unlock the encrypted root early in initrd (stable: UUID of the LUKS container)
|
||||
boot.initrd.luks.devices.cryptroot = {
|
||||
device = "/dev/disk/by-uuid/362284b1-a1ab-4ad0-b87b-eba30eaa258d";
|
||||
# allowDiscards = true; # uncomment if you use SSD discard/TRIM through LUKS
|
||||
};
|
||||
|
||||
# EFI System Partition
|
||||
fileSystems."/boot" = {
|
||||
device = "/dev/disk/by-uuid/042E-DA9E";
|
||||
fsType = "vfat";
|
||||
options = [
|
||||
"fmask=0077"
|
||||
"dmask=0077"
|
||||
];
|
||||
};
|
||||
|
||||
fileSystems."/games" = {
|
||||
device = "/dev/mapper/cryptdata";
|
||||
fsType = "btrfs";
|
||||
options = [
|
||||
"subvol=@games"
|
||||
"compress-force=zstd"
|
||||
"noatime"
|
||||
];
|
||||
};
|
||||
|
||||
# Impermanence layout: persistent subvolumes that must be mounted in initrd
|
||||
fileSystems."/nix" = {
|
||||
device = lib.mkForce "/dev/mapper/cryptroot";
|
||||
fsType = "btrfs";
|
||||
options = [
|
||||
"subvol=@nix"
|
||||
"compress-force=zstd"
|
||||
"noatime"
|
||||
"autodefrag"
|
||||
];
|
||||
neededForBoot = true;
|
||||
};
|
||||
|
||||
fileSystems."/persist" = {
|
||||
device = lib.mkForce "/dev/mapper/cryptroot";
|
||||
fsType = "btrfs";
|
||||
options = [
|
||||
"subvol=@persist"
|
||||
"compress-force=zstd"
|
||||
"noatime"
|
||||
"autodefrag"
|
||||
];
|
||||
neededForBoot = true;
|
||||
};
|
||||
|
||||
# NOTE:
|
||||
# We intentionally do NOT define fileSystems."/" here because your setup appears to
|
||||
# use impermanence (root is typically tmpfs). If you *do* have a persistent root
|
||||
# subvolume, define it in your main config instead.
|
||||
|
||||
networking.useDHCP = lib.mkDefault true;
|
||||
|
||||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
|
||||
|
||||
boot.swraid = {
|
||||
enable = true;
|
||||
mdadmConf = "PROGRAM ${pkgs.coreutils}/bin/true"; # Silences mdmon warning
|
||||
};
|
||||
}
|
||||
194
home.nix
Normal file
194
home.nix
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
imports = [
|
||||
inputs.sops-nix.homeManagerModules.sops
|
||||
inputs.steam-config-nix.homeModules.default
|
||||
inputs.catppuccin.homeManagerModules.catppuccin
|
||||
# inputs.unified-router-mcp.homeManagerModules.default
|
||||
./modules/home # Import all Home Manager modules
|
||||
./hosts/nixos/home-modules.nix # Host-specific module configuration
|
||||
./home/fastfetch.nix
|
||||
./home/vscode.nix
|
||||
./home/kitty.nix
|
||||
./home/steam.nix
|
||||
./home/mangohud.nix
|
||||
./home/starship.nix
|
||||
];
|
||||
|
||||
home.packages = [
|
||||
pkgs.mimalloc
|
||||
(pkgs.writeShellScriptBin "opencode" ''
|
||||
export OPENAI_BASE_URL="https://api.ashisgreat.xyz/v1"
|
||||
export OPENAI_API_KEY="$(cat ${config.sops.secrets.master_api_key.path})"
|
||||
export OPENCODE_DISABLE_DEFAULT_PLUGINS=true
|
||||
|
||||
# Ensure config directory exists
|
||||
mkdir -p $HOME/.config/opencode
|
||||
|
||||
# Force remove config.json if it is a symlink to ensure we can write to it
|
||||
if [ -L $HOME/.config/opencode/config.json ]; then
|
||||
rm -f $HOME/.config/opencode/config.json
|
||||
fi
|
||||
|
||||
# Validate permissions and force write correct config
|
||||
# We verify if we can write to it, if not (e.g. read-only file), we remove it
|
||||
if [ -f $HOME/.config/opencode/config.json ] && [ ! -w $HOME/.config/opencode/config.json ]; then
|
||||
rm -f $HOME/.config/opencode/config.json
|
||||
fi
|
||||
|
||||
# Always overwrite config.json to ensure correct settings
|
||||
cat > $HOME/.config/opencode/config.json <<EOF
|
||||
{
|
||||
"model": "openai/gpt-4o",
|
||||
"disabled_providers": ["opencode-anthropic-auth", "anthropic", "github"],
|
||||
"plugin": []
|
||||
}
|
||||
EOF
|
||||
|
||||
# Clear broken plugin from cache if it exists (one-time cleanup)
|
||||
if [ -d "$HOME/.cache/opencode/node_modules/opencode-anthropic-auth" ]; then
|
||||
rm -rf "$HOME/.cache/opencode"
|
||||
fi
|
||||
|
||||
exec ${inputs.opencode-flake.packages.${pkgs.stdenv.hostPlatform.system}.default}/bin/opencode "$@"
|
||||
'')
|
||||
];
|
||||
|
||||
sops.defaultSopsFile = ./secrets/secrets.yaml;
|
||||
sops.defaultSopsFormat = "yaml";
|
||||
sops.age.keyFile = "/home/ashie/.config/sops/age/keys.txt";
|
||||
|
||||
sops.secrets.master_api_key = { };
|
||||
|
||||
# Unified Router MCP Servers
|
||||
# services.unified-router-mcp = {
|
||||
# enable = true;
|
||||
# databasePath = "/home/ashie/nixos/unified-router/data/database.db";
|
||||
# logPath = "/home/ashie/nixos/unified-router/server.log";
|
||||
# };
|
||||
|
||||
home.username = "ashie";
|
||||
home.homeDirectory = "/home/ashie";
|
||||
home.stateVersion = "25.05";
|
||||
|
||||
services.polling-rate-switcher.enable = true;
|
||||
services.antigravity2api = {
|
||||
enable = true;
|
||||
credentials = {
|
||||
username = "ashie";
|
||||
password = "AshieAntigravity2024!";
|
||||
apiKey = "sk-antigravity-local-key";
|
||||
};
|
||||
};
|
||||
|
||||
programs.fish = {
|
||||
enable = true;
|
||||
interactiveShellInit = ''
|
||||
set fish_greeting # Disable greeting
|
||||
hyfetch
|
||||
'';
|
||||
plugins = [
|
||||
{
|
||||
name = "grc";
|
||||
src = pkgs.fishPlugins.grc.src;
|
||||
}
|
||||
{
|
||||
name = "done";
|
||||
src = pkgs.fishPlugins.done.src;
|
||||
}
|
||||
{
|
||||
name = "sponge";
|
||||
src = pkgs.fishPlugins.sponge.src;
|
||||
}
|
||||
{
|
||||
name = "puffer";
|
||||
src = pkgs.fishPlugins.puffer.src;
|
||||
}
|
||||
{
|
||||
name = "fzf-fish";
|
||||
src = pkgs.fishPlugins.fzf-fish.src;
|
||||
}
|
||||
{
|
||||
name = "pisces";
|
||||
src = pkgs.fishPlugins.pisces.src;
|
||||
}
|
||||
];
|
||||
shellAliases = {
|
||||
btw = "echo i use hyprland btw";
|
||||
vi = "nvim";
|
||||
vim = "nvim";
|
||||
"67" = "nh os switch";
|
||||
};
|
||||
};
|
||||
|
||||
programs.bash = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
programs.git = {
|
||||
enable = true;
|
||||
settings.user.name = "ashisgreat22";
|
||||
settings.user.email = "dev@ashisgreat.xyz";
|
||||
};
|
||||
|
||||
programs.gh = {
|
||||
enable = true;
|
||||
settings = {
|
||||
git_protocol = "ssh";
|
||||
editor = "nvim";
|
||||
};
|
||||
};
|
||||
|
||||
programs.direnv = {
|
||||
enable = true;
|
||||
nix-direnv.enable = true;
|
||||
};
|
||||
|
||||
programs.tealdeer = {
|
||||
enable = true;
|
||||
settings = {
|
||||
updates = {
|
||||
auto_update = true;
|
||||
auto_update_interval_hours = 48;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
fonts.fontconfig = {
|
||||
enable = true;
|
||||
|
||||
defaultFonts = {
|
||||
serif = [ "ComicShannsMono Nerd Font" ];
|
||||
sansSerif = [ "ComicShannsMono Nerd Font" ];
|
||||
monospace = [ "ComicShannsMono Nerd Font Mono" ];
|
||||
emoji = [ "Noto Color Emoji" ];
|
||||
};
|
||||
};
|
||||
|
||||
xdg.mimeApps = {
|
||||
enable = true;
|
||||
defaultApplications = {
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document" = [
|
||||
"onlyoffice-desktopeditors.desktop"
|
||||
];
|
||||
"application/msword" = [ "onlyoffice-desktopeditors.desktop" ];
|
||||
"text/html" = [ "nix.bwrapper.firefox.desktop" ];
|
||||
"x-scheme-handler/http" = [ "nix.bwrapper.firefox.desktop" ];
|
||||
"x-scheme-handler/https" = [ "nix.bwrapper.firefox.desktop" ];
|
||||
"x-scheme-handler/about" = [ "nix.bwrapper.firefox.desktop" ];
|
||||
"x-scheme-handler/unknown" = [ "nix.bwrapper.firefox.desktop" ];
|
||||
"application/xhtml+xml" = [ "nix.bwrapper.firefox.desktop" ];
|
||||
"application/x-extension-htm" = [ "nix.bwrapper.firefox.desktop" ];
|
||||
"application/x-extension-html" = [ "nix.bwrapper.firefox.desktop" ];
|
||||
"application/x-extension-shtml" = [ "nix.bwrapper.firefox.desktop" ];
|
||||
"application/x-extension-xhtml" = [ "nix.bwrapper.firefox.desktop" ];
|
||||
"application/x-extension-xht" = [ "nix.bwrapper.firefox.desktop" ];
|
||||
"application/pdf" = [ "nix.bwrapper.firefox.desktop" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
182
home/fastfetch.nix
Normal file
182
home/fastfetch.nix
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
programs.fastfetch = {
|
||||
enable = true;
|
||||
|
||||
settings = {
|
||||
"$schema" = "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json";
|
||||
|
||||
logo = {
|
||||
type = "builtin";
|
||||
height = 15;
|
||||
width = 30;
|
||||
padding = {
|
||||
top = 5;
|
||||
left = 3;
|
||||
};
|
||||
};
|
||||
|
||||
modules = [
|
||||
"break"
|
||||
{
|
||||
type = "custom";
|
||||
format = "┌──────────────────────Hardware──────────────────────┐";
|
||||
}
|
||||
{
|
||||
type = "host";
|
||||
key = " PC";
|
||||
keyColor = "green";
|
||||
}
|
||||
{
|
||||
type = "cpu";
|
||||
key = "│ ├";
|
||||
keyColor = "green";
|
||||
}
|
||||
{
|
||||
type = "gpu";
|
||||
key = "│ ├";
|
||||
keyColor = "green";
|
||||
}
|
||||
{
|
||||
type = "memory";
|
||||
key = "│ ├";
|
||||
keyColor = "green";
|
||||
}
|
||||
{
|
||||
type = "disk";
|
||||
key = "└ └";
|
||||
keyColor = "green";
|
||||
}
|
||||
{
|
||||
type = "custom";
|
||||
format = "└────────────────────────────────────────────────────┘";
|
||||
}
|
||||
|
||||
"break"
|
||||
{
|
||||
type = "custom";
|
||||
format = "┌──────────────────────Software──────────────────────┐";
|
||||
}
|
||||
{
|
||||
type = "os";
|
||||
key = " OS";
|
||||
keyColor = "yellow";
|
||||
}
|
||||
{
|
||||
type = "kernel";
|
||||
key = "│ ├";
|
||||
keyColor = "yellow";
|
||||
}
|
||||
{
|
||||
type = "bios";
|
||||
key = "│ ├";
|
||||
keyColor = "yellow";
|
||||
}
|
||||
{
|
||||
type = "packages";
|
||||
key = "│ ├";
|
||||
keyColor = "yellow";
|
||||
}
|
||||
{
|
||||
type = "shell";
|
||||
key = "└ └";
|
||||
keyColor = "yellow";
|
||||
}
|
||||
|
||||
"break"
|
||||
{
|
||||
type = "de";
|
||||
key = " DE";
|
||||
keyColor = "blue";
|
||||
}
|
||||
{
|
||||
type = "lm";
|
||||
key = "│ ├";
|
||||
keyColor = "blue";
|
||||
}
|
||||
{
|
||||
type = "wm";
|
||||
key = "│ ├";
|
||||
keyColor = "blue";
|
||||
}
|
||||
{
|
||||
type = "wmtheme";
|
||||
key = "│ ├";
|
||||
keyColor = "blue";
|
||||
}
|
||||
{
|
||||
type = "terminal";
|
||||
key = "└ └";
|
||||
keyColor = "blue";
|
||||
}
|
||||
{
|
||||
type = "custom";
|
||||
format = "└────────────────────────────────────────────────────┘";
|
||||
}
|
||||
|
||||
"break"
|
||||
{
|
||||
type = "custom";
|
||||
format = "┌────────────────────Uptime / Age / DT────────────────────┐";
|
||||
}
|
||||
{
|
||||
type = "command";
|
||||
key = " OS Age ";
|
||||
keyColor = "magenta";
|
||||
text = ''
|
||||
birth_install=$(stat -c %W /); current=$(date +%s); time_progression=$((current - birth_install)); days_difference=$((time_progression / 86400)); echo $days_difference days
|
||||
'';
|
||||
}
|
||||
{
|
||||
type = "uptime";
|
||||
key = " Uptime ";
|
||||
keyColor = "magenta";
|
||||
}
|
||||
{
|
||||
type = "datetime";
|
||||
key = " DateTime ";
|
||||
keyColor = "magenta";
|
||||
}
|
||||
{
|
||||
type = "custom";
|
||||
format = "└─────────────────────────────────────────────────────────┘";
|
||||
}
|
||||
|
||||
# Original commented colors block:
|
||||
# { type = "colors"; }
|
||||
|
||||
{
|
||||
type = "colors";
|
||||
paddingLeft = 2;
|
||||
symbol = "circle";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
programs.hyfetch = {
|
||||
enable = true;
|
||||
|
||||
settings = {
|
||||
preset = "transgender";
|
||||
mode = "rgb";
|
||||
auto_detect_light_dark = true;
|
||||
light_dark = "dark";
|
||||
lightness = 0.65;
|
||||
color_align = {
|
||||
mode = "horizontal";
|
||||
};
|
||||
backend = "fastfetch";
|
||||
args = null;
|
||||
distro = null;
|
||||
pride_month_disable = false;
|
||||
custom_ascii_path = null;
|
||||
};
|
||||
};
|
||||
}
|
||||
16
home/kitty.nix
Normal file
16
home/kitty.nix
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
programs.kitty = {
|
||||
enable = true;
|
||||
themeFile = "Catppuccin-Mocha"; # Updated option name
|
||||
settings = {
|
||||
confirm_os_window_close = 0;
|
||||
cursor_shape = "beam";
|
||||
};
|
||||
|
||||
# extraConfig = ''
|
||||
# include current-theme.conf
|
||||
# '';
|
||||
};
|
||||
}
|
||||
29
home/mangohud.nix
Normal file
29
home/mangohud.nix
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{ pkgs, ... }:
|
||||
{
|
||||
programs.mangohud = {
|
||||
enable = true;
|
||||
settings = {
|
||||
# Performance overlay
|
||||
fps = true;
|
||||
frametime = false;
|
||||
frame_timing = false;
|
||||
histogram = false;
|
||||
|
||||
# Hardware stats (Ensure disabled)
|
||||
cpu_stats = false;
|
||||
gpu_stats = false;
|
||||
ram = false;
|
||||
vram = false;
|
||||
core_load = false;
|
||||
gpu_load = false;
|
||||
|
||||
# Appearance
|
||||
legacy_layout = false;
|
||||
table_columns = 3;
|
||||
background_alpha = 0.0;
|
||||
font_size = 24;
|
||||
position = "top-left";
|
||||
round_corners = 10;
|
||||
};
|
||||
};
|
||||
}
|
||||
206
home/starship.nix
Normal file
206
home/starship.nix
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
programs.starship = {
|
||||
enable = true;
|
||||
enableFishIntegration = true;
|
||||
settings = {
|
||||
add_newline = false;
|
||||
format = lib.concatStrings [
|
||||
"[](red)"
|
||||
"$os"
|
||||
"$username"
|
||||
"[](bg:peach fg:red)"
|
||||
"$directory"
|
||||
"[](bg:yellow fg:peach)"
|
||||
"$git_branch"
|
||||
"$git_status"
|
||||
"[](fg:yellow bg:green)"
|
||||
"$c"
|
||||
"$rust"
|
||||
"$golang"
|
||||
"$nodejs"
|
||||
"$php"
|
||||
"$java"
|
||||
"$kotlin"
|
||||
"$haskell"
|
||||
"$python"
|
||||
"[](fg:green bg:sapphire)"
|
||||
"$conda"
|
||||
"[](fg:sapphire bg:lavender)"
|
||||
"$time"
|
||||
"[ ](fg:lavender)"
|
||||
"$cmd_duration"
|
||||
"$line_break"
|
||||
"$character"
|
||||
];
|
||||
|
||||
palette = "catppuccin_mocha";
|
||||
|
||||
os = {
|
||||
disabled = false;
|
||||
style = "bg:red fg:crust";
|
||||
symbols = {
|
||||
Windows = "";
|
||||
Ubuntu = "";
|
||||
SUSE = "";
|
||||
Raspbian = "";
|
||||
Mint = "";
|
||||
Macos = "";
|
||||
Manjaro = "";
|
||||
Linux = "";
|
||||
Gentoo = "";
|
||||
Fedora = "";
|
||||
Alpine = "";
|
||||
Amazon = "";
|
||||
Android = "";
|
||||
AOSC = "";
|
||||
Arch = "";
|
||||
Artix = "";
|
||||
CentOS = "";
|
||||
Debian = "";
|
||||
Redhat = "";
|
||||
RedHatEnterprise = "";
|
||||
};
|
||||
};
|
||||
|
||||
username = {
|
||||
show_always = true;
|
||||
style_user = "bg:red fg:crust";
|
||||
style_root = "bg:red fg:crust";
|
||||
format = "[ $user]($style)";
|
||||
};
|
||||
|
||||
directory = {
|
||||
style = "bg:peach fg:crust";
|
||||
format = "[ $path ]($style)";
|
||||
truncation_length = 3;
|
||||
truncation_symbol = "…/";
|
||||
substitutions = {
|
||||
"Documents" = " ";
|
||||
"Downloads" = " ";
|
||||
"Music" = " ";
|
||||
"Pictures" = " ";
|
||||
"Developer" = " ";
|
||||
};
|
||||
};
|
||||
|
||||
git_branch = {
|
||||
symbol = "";
|
||||
style = "bg:yellow";
|
||||
format = "[[ $symbol $branch ](fg:crust bg:yellow)]($style)";
|
||||
};
|
||||
|
||||
git_status = {
|
||||
style = "bg:yellow";
|
||||
format = "[[($all_status$ahead_behind )](fg:crust bg:yellow)]($style)";
|
||||
};
|
||||
|
||||
nodejs = {
|
||||
symbol = "";
|
||||
style = "bg:green";
|
||||
format = "[[ $symbol( $version) ](fg:crust bg:green)]($style)";
|
||||
};
|
||||
|
||||
c = {
|
||||
symbol = " ";
|
||||
style = "bg:green";
|
||||
format = "[[ $symbol( $version) ](fg:crust bg:green)]($style)";
|
||||
};
|
||||
|
||||
rust = {
|
||||
symbol = "";
|
||||
style = "bg:green";
|
||||
format = "[[ $symbol( $version) ](fg:crust bg:green)]($style)";
|
||||
};
|
||||
|
||||
golang = {
|
||||
symbol = "";
|
||||
style = "bg:green";
|
||||
format = "[[ $symbol( $version) ](fg:crust bg:green)]($style)";
|
||||
};
|
||||
|
||||
php = {
|
||||
symbol = "";
|
||||
style = "bg:green";
|
||||
format = "[[ $symbol( $version) ](fg:crust bg:green)]($style)";
|
||||
};
|
||||
|
||||
java = {
|
||||
symbol = " ";
|
||||
style = "bg:green";
|
||||
format = "[[ $symbol( $version) ](fg:crust bg:green)]($style)";
|
||||
};
|
||||
|
||||
kotlin = {
|
||||
symbol = "";
|
||||
style = "bg:green";
|
||||
format = "[[ $symbol( $version) ](fg:crust bg:green)]($style)";
|
||||
};
|
||||
|
||||
haskell = {
|
||||
symbol = "";
|
||||
style = "bg:green";
|
||||
format = "[[ $symbol( $version) ](fg:crust bg:green)]($style)";
|
||||
};
|
||||
|
||||
python = {
|
||||
symbol = "";
|
||||
style = "bg:green";
|
||||
format = "[[ $symbol( $version)(\\(#$virtualenv\\)) ](fg:crust bg:green)]($style)";
|
||||
};
|
||||
|
||||
docker_context = {
|
||||
symbol = "";
|
||||
style = "bg:sapphire";
|
||||
format = "[[ $symbol( $context) ](fg:crust bg:sapphire)]($style)";
|
||||
};
|
||||
|
||||
conda = {
|
||||
symbol = " ";
|
||||
style = "fg:crust bg:sapphire";
|
||||
format = "[$symbol$environment ]($style)";
|
||||
ignore_base = false;
|
||||
};
|
||||
|
||||
time = {
|
||||
disabled = false;
|
||||
time_format = "%R";
|
||||
style = "bg:lavender";
|
||||
format = "[[ $time ](fg:crust bg:lavender)]($style)";
|
||||
};
|
||||
|
||||
line_break = {
|
||||
disabled = true;
|
||||
};
|
||||
|
||||
character = {
|
||||
disabled = false;
|
||||
success_symbol = "[❯](bold fg:green)";
|
||||
error_symbol = "[❯](bold fg:red)";
|
||||
vimcmd_symbol = "[❮](bold fg:green)";
|
||||
vimcmd_replace_one_symbol = "[❮](bold fg:lavender)";
|
||||
vimcmd_replace_symbol = "[❮](bold fg:lavender)";
|
||||
vimcmd_visual_symbol = "[❮](bold fg:yellow)";
|
||||
};
|
||||
|
||||
cmd_duration = {
|
||||
show_milliseconds = true;
|
||||
format = " in $duration ";
|
||||
style = "bg:lavender";
|
||||
disabled = false;
|
||||
show_notifications = true;
|
||||
min_time_to_notify = 45000;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Enable the Catppuccin starship module to provide the palette
|
||||
catppuccin.starship.enable = true;
|
||||
catppuccin.flavor = "mocha";
|
||||
}
|
||||
21
home/steam.nix
Normal file
21
home/steam.nix
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
programs.steam.config = {
|
||||
enable = true;
|
||||
closeSteam = true; # Closes Steam on rebuild, to prevent data loss
|
||||
defaultCompatTool = "proton-cachyos-latest";
|
||||
|
||||
apps = {
|
||||
overwatch2 = {
|
||||
id = 2357570;
|
||||
compatTool = "proton-cachyos-latest";
|
||||
launchOptions = "gamemoderun mangohud PROTON_USE_NTSYNC=1 ENABLE_LAYER_MESA_ANTI_LAG=1 PROTON_LOCAL_SHADER_CACHE=1 %command%";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
230
home/vscode.nix
Normal file
230
home/vscode.nix
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
{ config, pkgs, ... }:
|
||||
|
||||
let
|
||||
# Wrap Antigravity in an FHS environment to support dynamically linked binaries
|
||||
antigravityFHS = pkgs.buildFHSEnv {
|
||||
name = "antigravity";
|
||||
|
||||
targetPkgs =
|
||||
pkgs:
|
||||
(with pkgs; [
|
||||
antigravity
|
||||
chromium
|
||||
# Common libraries for dynamically linked binaries
|
||||
gcc
|
||||
glibc
|
||||
stdenv.cc.cc
|
||||
zlib
|
||||
openssl
|
||||
curl
|
||||
libgcc
|
||||
glib
|
||||
gtk3
|
||||
libsecret
|
||||
libnotify
|
||||
nss
|
||||
nspr
|
||||
alsa-lib
|
||||
cups
|
||||
dbus
|
||||
expat
|
||||
libdrm
|
||||
libxkbcommon
|
||||
mesa
|
||||
pango
|
||||
cairo
|
||||
# X11 libraries
|
||||
xorg.libX11
|
||||
xorg.libXcomposite
|
||||
xorg.libXdamage
|
||||
xorg.libXext
|
||||
xorg.libXfixes
|
||||
xorg.libXrandr
|
||||
xorg.libxcb
|
||||
]);
|
||||
|
||||
# Run antigravity binary when the FHS env is invoked
|
||||
runScript = pkgs.writeShellScript "antigravity-wrapper" ''
|
||||
unset LD_PRELOAD
|
||||
exec ${pkgs.antigravity}/bin/antigravity "$@"
|
||||
'';
|
||||
|
||||
# Ensure the FHS environment is set up properly
|
||||
profile = ''
|
||||
export LD_LIBRARY_PATH=/usr/lib:/usr/lib64:$LD_LIBRARY_PATH
|
||||
'';
|
||||
|
||||
extraBindMounts = [
|
||||
"/etc/subuid"
|
||||
"/etc/subgid"
|
||||
];
|
||||
};
|
||||
|
||||
# Wrap the FHS environment to add product.json symlink where Home Manager expects it
|
||||
antigravityWrapped = pkgs.symlinkJoin {
|
||||
name = "antigravity-wrapped";
|
||||
paths = [ antigravityFHS ];
|
||||
postBuild = ''
|
||||
# Link product.json from the original antigravity package
|
||||
ln -sf ${pkgs.antigravity}/lib/antigravity/resources/app/product.json $out/product.json
|
||||
'';
|
||||
# Pass through required attributes for Home Manager vscode module
|
||||
passthru = {
|
||||
inherit (pkgs.antigravity) pname version;
|
||||
};
|
||||
};
|
||||
|
||||
# Launcher for the regular non-sandboxed environment (Native)
|
||||
antigravityNative = pkgs.writeShellScriptBin "antigravity-native" ''
|
||||
unset LD_PRELOAD
|
||||
exec ${pkgs.antigravity}/bin/antigravity "$@"
|
||||
'';
|
||||
|
||||
# Explicit launcher for the FHS environment (same as default)
|
||||
antigravityFHSLauncher = pkgs.writeShellScriptBin "antigravity-fhs" ''
|
||||
exec ${antigravityWrapped}/bin/antigravity "$@"
|
||||
'';
|
||||
in
|
||||
{
|
||||
home.packages = [
|
||||
antigravityNative
|
||||
antigravityFHSLauncher
|
||||
];
|
||||
|
||||
programs.vscode = {
|
||||
enable = true;
|
||||
package = antigravityWrapped;
|
||||
|
||||
# Allow mutable extensions dir so Antigravity can create extensions.json
|
||||
mutableExtensionsDir = true;
|
||||
|
||||
# extensions = [
|
||||
# (pkgs.vscode-utils.extensionFromVscodeMarketplace {
|
||||
# name = "x";
|
||||
# publisher = "x";
|
||||
# version = "x.x.x";
|
||||
# sha256 = "sha256-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=";
|
||||
# })
|
||||
# ];
|
||||
|
||||
profiles.default = {
|
||||
# Disable update checks (not applicable for Nix-managed packages)
|
||||
enableUpdateCheck = false;
|
||||
enableExtensionUpdateCheck = false;
|
||||
|
||||
# Extensions from nixpkgs
|
||||
extensions = with pkgs.vscode-extensions; [
|
||||
# Theme & Icons
|
||||
catppuccin.catppuccin-vsc
|
||||
catppuccin.catppuccin-vsc-icons
|
||||
|
||||
# Git
|
||||
eamodio.gitlens
|
||||
|
||||
# C/C++
|
||||
llvm-vs-code-extensions.vscode-clangd
|
||||
|
||||
# Nix
|
||||
jnoortheen.nix-ide
|
||||
|
||||
# Python
|
||||
ms-python.python
|
||||
ms-python.debugpy
|
||||
|
||||
# Go
|
||||
golang.go
|
||||
|
||||
# Java (RedHat + vscjava)
|
||||
redhat.java
|
||||
vscjava.vscode-java-debug
|
||||
vscjava.vscode-java-dependency
|
||||
vscjava.vscode-java-pack
|
||||
vscjava.vscode-java-test
|
||||
vscjava.vscode-gradle
|
||||
vscjava.vscode-maven
|
||||
|
||||
# PHP
|
||||
bmewburn.vscode-intelephense-client
|
||||
xdebug.php-debug
|
||||
|
||||
# Ruby
|
||||
shopify.ruby-lsp
|
||||
|
||||
# Docker & Containers
|
||||
ms-azuretools.vscode-docker
|
||||
|
||||
# Formatters
|
||||
esbenp.prettier-vscode
|
||||
];
|
||||
|
||||
# User settings (settings.json equivalent)
|
||||
userSettings = {
|
||||
# Existing settings from your current settings.json
|
||||
"workbench.colorTheme" = "Catppuccin Mocha";
|
||||
"workbench.iconTheme" = "catppuccin-mocha";
|
||||
"terminal.integrated.shellIntegration.enabled" = false;
|
||||
"python.languageServer" = "Default";
|
||||
"json.schemaDownload.enable" = true;
|
||||
"git.autofetch" = true;
|
||||
"git.confirmSync" = false;
|
||||
"explorer.confirmDelete" = false;
|
||||
"redhat.telemetry.enabled" = false;
|
||||
|
||||
# MCP Server configuration for Continue.dev / Cline
|
||||
# These servers provide AI agents with direct access to:
|
||||
# - Database inspection (sqlite-inspector)
|
||||
# - Log analysis (pino-parser)
|
||||
# - API testing (api-tester)
|
||||
"mcp.servers" = {
|
||||
"unified-router-sqlite" = {
|
||||
command = "mcp-sqlite-inspector";
|
||||
env = {
|
||||
DEFAULT_DB_PATH = "/home/ashie/nixos/unified-router/data/database.db";
|
||||
};
|
||||
};
|
||||
"unified-router-logs" = {
|
||||
command = "mcp-pino-parser";
|
||||
env = {
|
||||
DEFAULT_LOG_PATH = "/home/ashie/nixos/unified-router/server.log";
|
||||
};
|
||||
};
|
||||
"unified-router-api" = {
|
||||
command = "mcp-api-tester";
|
||||
env = {
|
||||
ALLOWED_HOSTS = "localhost,127.0.0.1";
|
||||
DEFAULT_PORT = "9090";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Antigravity MCP Server Configuration
|
||||
home.file.".gemini/antigravity/mcp_config.json" = {
|
||||
force = true; # Allow overwriting existing file created by Antigravity
|
||||
text = builtins.toJSON {
|
||||
mcpServers = {
|
||||
unified-router-sqlite = {
|
||||
command = "mcp-sqlite-inspector";
|
||||
env = {
|
||||
DEFAULT_DB_PATH = "/home/ashie/nixos/unified-router/data/database.db";
|
||||
};
|
||||
};
|
||||
unified-router-logs = {
|
||||
command = "mcp-pino-parser";
|
||||
env = {
|
||||
DEFAULT_LOG_PATH = "/home/ashie/nixos/unified-router/server.log";
|
||||
};
|
||||
};
|
||||
unified-router-api = {
|
||||
command = "mcp-api-tester";
|
||||
env = {
|
||||
ALLOWED_HOSTS = "localhost,127.0.0.1";
|
||||
DEFAULT_PORT = "9090";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
84
hosts/nixos/default.nix
Normal file
84
hosts/nixos/default.nix
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# ============================================
|
||||
# Enable Modular Components
|
||||
# ============================================
|
||||
myModules = {
|
||||
# Global Settings
|
||||
system.repoPath = "/home/ashie/nixos";
|
||||
|
||||
# Security hardening (doas, audit, AppArmor)
|
||||
security = {
|
||||
enable = true;
|
||||
useDoas = true;
|
||||
enableAudit = false;
|
||||
enableAppArmor = true;
|
||||
enableFail2Ban = false;
|
||||
};
|
||||
|
||||
# Kernel hardening (boot params, sysctl, ZRAM)
|
||||
kernelHardening = {
|
||||
enable = true;
|
||||
enableZram = true;
|
||||
zramPercent = 100;
|
||||
zramAlgorithm = "zstd";
|
||||
};
|
||||
|
||||
# Secure Boot (Lanzaboote)
|
||||
# 1. sudo sbctl create-keys
|
||||
# 2. sudo sbctl enroll-keys -m
|
||||
# 3. Enable this option
|
||||
# 4. Reboot
|
||||
secureBoot = {
|
||||
enable = false; # Disabled for initial install (enable after running sbctl create-keys)
|
||||
pkiBundle = "/var/lib/sbctl";
|
||||
};
|
||||
|
||||
# DNS-over-TLS with DNSSEC
|
||||
dnsOverTls = {
|
||||
enable = true;
|
||||
dnssec = true;
|
||||
};
|
||||
|
||||
# Cloudflare-only firewall rules
|
||||
cloudflareFirewall = {
|
||||
enable = false;
|
||||
enablePodmanWorkaround = false;
|
||||
restrictedPorts = [
|
||||
80
|
||||
443
|
||||
];
|
||||
};
|
||||
|
||||
# Base Podman container runtime
|
||||
# Disabled here because system/podman.nix handles Podman + container definitions
|
||||
podman.enable = true;
|
||||
|
||||
# VPN-isolated browser containers
|
||||
browserVpn = {
|
||||
enable = true;
|
||||
browsers = [
|
||||
"firefox"
|
||||
"tor-browser"
|
||||
"thorium"
|
||||
"thorium-dev"
|
||||
"kitty"
|
||||
];
|
||||
};
|
||||
|
||||
# Ollama System Service (Isolated)
|
||||
ollamaRocm = {
|
||||
enable = false; # Disabled temporarily to unblock install (namespace issues)
|
||||
};
|
||||
|
||||
# Open WebUI System Service (Isolated)
|
||||
openWebUI = {
|
||||
enable = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
79
hosts/nixos/home-modules.nix
Normal file
79
hosts/nixos/home-modules.nix
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# ============================================
|
||||
# Enable Modular Components
|
||||
# ============================================
|
||||
myModules = {
|
||||
# Global Settings
|
||||
common.repoPath = "/home/ashie/nixos";
|
||||
|
||||
# Catppuccin-themed Hyprland (Disabled)
|
||||
hyprlandCatppuccin = {
|
||||
enable = false;
|
||||
# Settings kept for reference
|
||||
keyboardLayout = "de";
|
||||
keyboardVariant = "nodeadkeys";
|
||||
keyboardModel = "pc105";
|
||||
primaryMonitor = "DP-2";
|
||||
primaryResolution = "2560x1440@165";
|
||||
secondaryMonitor = "HDMI-A-1";
|
||||
secondaryResolution = "1920x1080@60";
|
||||
};
|
||||
|
||||
# Niri Configuration
|
||||
niri = {
|
||||
enable = true;
|
||||
keyboardLayout = "de";
|
||||
keyboardVariant = "nodeadkeys";
|
||||
primaryMonitor = "DP-2";
|
||||
primaryResolution = "2560x1440@165";
|
||||
secondaryMonitor = "HDMI-A-1";
|
||||
secondaryResolution = "1920x1080@60";
|
||||
};
|
||||
|
||||
# Gluetun VPN user service
|
||||
gluetunUser = {
|
||||
enable = true;
|
||||
environmentFile = "/run/secrets/rendered/gluetun.env";
|
||||
};
|
||||
|
||||
# qBittorrent through VPN
|
||||
qbittorrentVpn = {
|
||||
enable = true;
|
||||
configDir = "/home/ashie/qbittorrent/config";
|
||||
downloadsDir = "/home/ashie/qbittorrent/downloads";
|
||||
};
|
||||
|
||||
# Auto-update browser containers
|
||||
browserContainerUpdate = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
# Auto-update Proton CachyOS from GitHub
|
||||
protonCachyosUpdater = {
|
||||
enable = true;
|
||||
arch = "x86_64_v3";
|
||||
};
|
||||
|
||||
# Unified API Router
|
||||
# unifiedRouter = {
|
||||
# enable = true;
|
||||
# environmentFile = "/home/ashie/nixos/unified-router/.env";
|
||||
# };
|
||||
|
||||
# SillyTavern Frontend
|
||||
sillytavern = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
# Noctalia Shell
|
||||
noctalia = {
|
||||
enable = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
19
modules/default.nix
Normal file
19
modules/default.nix
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# NixOS Modules Collection
|
||||
# Reusable, modular NixOS configuration modules
|
||||
#
|
||||
# Usage:
|
||||
# # In flake.nix outputs
|
||||
# nixosModules = import ./modules;
|
||||
#
|
||||
# # In configuration.nix
|
||||
# imports = [ ./modules ];
|
||||
# myModules.security.enable = true;
|
||||
# myModules.kernelHardening.enable = true;
|
||||
# # etc.
|
||||
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
./system
|
||||
];
|
||||
}
|
||||
72
modules/home/antigravity2api.nix
Normal file
72
modules/home/antigravity2api.nix
Normal 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" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
118
modules/home/browser-container-update.nix
Normal file
118
modules/home/browser-container-update.nix
Normal 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" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
92
modules/home/cli-tools.nix
Normal file
92
modules/home/cli-tools.nix
Normal 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
14
modules/home/common.nix
Normal 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
28
modules/home/default.nix
Normal 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
|
||||
];
|
||||
}
|
||||
124
modules/home/gluetun-user.nix
Normal file
124
modules/home/gluetun-user.nix
Normal 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" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
291
modules/home/hyprland-catppuccin.nix
Normal file
291
modules/home/hyprland-catppuccin.nix
Normal 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
264
modules/home/niri.nix
Normal 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
79
modules/home/noctalia.nix
Normal 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;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
99
modules/home/polling-rate.nix
Normal file
99
modules/home/polling-rate.nix
Normal 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" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
190
modules/home/proton-cachyos-updater.nix
Normal file
190
modules/home/proton-cachyos-updater.nix
Normal 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" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
96
modules/home/qbittorrent-vpn.nix
Normal file
96
modules/home/qbittorrent-vpn.nix
Normal 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" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
87
modules/home/sillytavern.nix
Normal file
87
modules/home/sillytavern.nix
Normal 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
36
modules/home/theme.nix
Normal 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";
|
||||
};
|
||||
}
|
||||
90
modules/home/unified-router.nix
Normal file
90
modules/home/unified-router.nix
Normal 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" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
95
modules/system/azahar-sandboxed.nix
Normal file
95
modules/system/azahar-sandboxed.nix
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
|
||||
|
||||
pname = "azahar";
|
||||
version = "2123.4";
|
||||
|
||||
src = pkgs.fetchurl {
|
||||
url = "https://github.com/azahar-emu/azahar/releases/download/2123.4/azahar.AppImage";
|
||||
sha256 = "0x9k5kamn7lr5frffzv5vdgxv65cwwb01pbf6dyb8p2dw63cq87a";
|
||||
};
|
||||
|
||||
appimageContents = pkgs.appimageTools.extractType2 {
|
||||
inherit pname version src;
|
||||
};
|
||||
|
||||
azahar = pkgs.appimageTools.wrapType2 {
|
||||
inherit pname version src;
|
||||
|
||||
extraInstallCommands = ''
|
||||
install -m 444 -D ${appimageContents}/usr/share/applications/azahar.desktop $out/share/applications/azahar.desktop
|
||||
install -m 444 -D ${appimageContents}/usr/share/icons/hicolor/scalable/apps/org.azahar_emu.Azahar.svg \
|
||||
$out/share/icons/hicolor/scalable/apps/azahar.svg
|
||||
|
||||
substituteInPlace $out/share/applications/azahar.desktop \
|
||||
--replace 'Exec=AppRun' 'Exec=azahar'
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
azahar-sandboxed = bwrapperPkgs.mkBwrapper {
|
||||
app = {
|
||||
package = azahar;
|
||||
id = "org.azahar_emu.azahar";
|
||||
env = {
|
||||
QT_QPA_PLATFORM = "wayland;xcb";
|
||||
XDG_CURRENT_DESKTOP = "KDE";
|
||||
};
|
||||
};
|
||||
|
||||
flatpak.enable = false;
|
||||
fhsenv.bwrap.additionalArgs = [
|
||||
"--dir /run/systemd/resolve"
|
||||
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
|
||||
''--bind "$XDG_RUNTIME_DIR/app/org.azahar_emu.azahar/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
|
||||
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
|
||||
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
|
||||
];
|
||||
|
||||
mounts = {
|
||||
read = [
|
||||
"$HOME/.config/kdedefaults"
|
||||
"$HOME/.local/share/color-schemes"
|
||||
"$HOME/.config/fontconfig"
|
||||
"$HOME/.icons"
|
||||
"$HOME/.config/MangoHud"
|
||||
];
|
||||
readWrite = [
|
||||
"$HOME/Games/3DS"
|
||||
"$HOME/.config/azahar"
|
||||
"$HOME/.local/share/azahar"
|
||||
];
|
||||
};
|
||||
|
||||
dbus.enable = false;
|
||||
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
|
||||
appId = "org.azahar_emu.azahar";
|
||||
enableSystemBus = false;
|
||||
proxyArgs = [
|
||||
"--filter"
|
||||
''--talk="org.freedesktop.Flatpak"''
|
||||
''--talk="org.kde.StatusNotifierWatcher"''
|
||||
''--talk="org.kde.KWin"''
|
||||
''--talk="org.gnome.Mutter.DisplayConfig"''
|
||||
''--talk="org.freedesktop.ScreenSaver"''
|
||||
''--talk="org.freedesktop.portal.Desktop"''
|
||||
''--talk="org.freedesktop.portal.OpenURI"''
|
||||
''--talk="org.freedesktop.secrets"''
|
||||
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
|
||||
];
|
||||
};
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
153
modules/system/brave-sandboxed.nix
Normal file
153
modules/system/brave-sandboxed.nix
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
# Brave Sandboxed with nix-bwrapper
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
|
||||
|
||||
# create a custom settings.ini to force dark mode
|
||||
darkSettingsIni = pkgs.writeText "settings.ini" ''
|
||||
[Settings]
|
||||
gtk-theme-name=catppuccin-mocha-mauve-standard
|
||||
gtk-application-prefer-dark-theme=1
|
||||
gtk-cursor-theme-name=Future-Cyan-Hyprcursor_Theme
|
||||
gtk-xft-antialias=1
|
||||
gtk-xft-hinting=1
|
||||
gtk-xft-hintstyle=hintslight
|
||||
gtk-xft-rgba=rgb
|
||||
'';
|
||||
|
||||
# Define policies.json with Catppuccin Mocha Theme (Chrome Web Store)
|
||||
bravePolicies = pkgs.writeText "policies.json" (
|
||||
builtins.toJSON {
|
||||
ExtensionInstallForcelist = [
|
||||
"pgonbchglnnkjolggcdhphlbnjihfofh;https://clients2.google.com/service/update2/crx" # Catppuccin Mocha
|
||||
];
|
||||
}
|
||||
);
|
||||
in
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
brave-sandboxed = bwrapperPkgs.mkBwrapper {
|
||||
app = {
|
||||
package = pkgs.symlinkJoin {
|
||||
name = "brave-single-desktop";
|
||||
paths = [ prev.brave ];
|
||||
inherit (prev.brave) pname version meta;
|
||||
postBuild = ''
|
||||
rm $out/share/applications/com.brave.Browser.desktop
|
||||
'';
|
||||
};
|
||||
# id = "brave-browser"; # Omit app.id to avoid potential bind errors (like Firefox)
|
||||
env = {
|
||||
# Propagate XDG_DATA_DIRS so GTK can find themes in user profile/system
|
||||
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
|
||||
GTK_THEME = "catppuccin-mocha-mauve-standard";
|
||||
HYPRCURSOR_THEME = "Future-Cyan-Hyprcursor_Theme";
|
||||
HYPRCURSOR_SIZE = "32";
|
||||
# Force ozone/wayland usage for Brave/Chromium
|
||||
NIXOS_OZONE_WL = "1";
|
||||
};
|
||||
};
|
||||
|
||||
flatpak.enable = false;
|
||||
sockets.x11 = false;
|
||||
sockets.wayland = true;
|
||||
fhsenv.opts = {
|
||||
unshareUser = true;
|
||||
unshareUts = false;
|
||||
unshareCgroup = false;
|
||||
unsharePid = false;
|
||||
unshareNet = false;
|
||||
unshareIpc = false;
|
||||
};
|
||||
|
||||
fhsenv.bwrap.baseArgs = lib.mkForce [
|
||||
"--new-session"
|
||||
"--proc /proc"
|
||||
"--dev /dev"
|
||||
"--dev-bind /dev/dri /dev/dri"
|
||||
"--tmpfs /home"
|
||||
"--tmpfs /mnt"
|
||||
"--tmpfs /run"
|
||||
"--ro-bind-try /run/current-system /run/current-system"
|
||||
"--ro-bind-try /run/booted-system /run/booted-system"
|
||||
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
|
||||
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
|
||||
# Brave flags
|
||||
"--setenv NIXOS_OZONE_WL \"1\""
|
||||
"--setenv NOTIFY_IGNORE_PORTAL 1"
|
||||
# Bind policies for Theme
|
||||
"--dir /etc/brave/policies/managed"
|
||||
"--ro-bind ${bravePolicies} /etc/brave/policies/managed/policies.json"
|
||||
# Fallback paths for Chromium/Chrome base
|
||||
"--dir /etc/chromium/policies/managed"
|
||||
"--ro-bind ${bravePolicies} /etc/chromium/policies/managed/policies.json"
|
||||
"--dir /etc/opt/chrome/policies/managed"
|
||||
"--ro-bind ${bravePolicies} /etc/opt/chrome/policies/managed/policies.json"
|
||||
];
|
||||
|
||||
# Filesystem: Limited to Brave directories and Downloads
|
||||
mounts = {
|
||||
read = [
|
||||
"$HOME/.config/kdedefaults"
|
||||
"$HOME/.config/fontconfig"
|
||||
"$HOME/.config/user-dirs.dirs"
|
||||
"$HOME/.config/mimeapps.list"
|
||||
"$HOME/.local/share/color-schemes"
|
||||
"$HOME/.local/share/fonts"
|
||||
"$HOME/.icons"
|
||||
"$HOME/.themes"
|
||||
"$HOME/.local/share/themes"
|
||||
"$HOME/.config/gtk-3.0"
|
||||
];
|
||||
readWrite = [
|
||||
"$HOME/.config/BraveSoftware"
|
||||
"$HOME/.cache/BraveSoftware"
|
||||
"$HOME/Downloads"
|
||||
];
|
||||
};
|
||||
|
||||
# Bind mount systemd-resolved socket for DNS and required system files
|
||||
# Disable built-in DBus module because it invokes bwrap without --unshare-user
|
||||
dbus.enable = false;
|
||||
|
||||
# Manually set up DBus proxy with --unshare-user
|
||||
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
|
||||
appId = "nix.bwrapper.brave";
|
||||
proxyArgs = [
|
||||
"--filter"
|
||||
''--talk="org.freedesktop.portal.Desktop"''
|
||||
''--talk="org.freedesktop.portal.OpenURI"''
|
||||
''--talk="org.freedesktop.portal.FileChooser"''
|
||||
''--talk="org.freedesktop.secrets"''
|
||||
''--talk="org.kde.StatusNotifierWatcher"''
|
||||
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
|
||||
''--own="org.chromium.LibCrosService"'' # Chromium/Brave specific
|
||||
''--own="org.mpris.MediaPlayer2.chromium.*"''
|
||||
''--own="org.mpris.MediaPlayer2.brave.*"''
|
||||
];
|
||||
enableSystemBus = true;
|
||||
systemProxyArgs = [
|
||||
"--filter"
|
||||
''--talk="org.freedesktop.NetworkManager"''
|
||||
];
|
||||
};
|
||||
|
||||
fhsenv.bwrap.additionalArgs = [
|
||||
''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.brave/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.brave/bus_system" /run/dbus/system_bus_socket''
|
||||
"--dir /run/systemd/resolve"
|
||||
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
|
||||
"--bind-try /run/user/${toString config.users.users.ashie.uid}/dconf /run/user/${toString config.users.users.ashie.uid}/dconf"
|
||||
];
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
467
modules/system/browser-vpn.nix
Normal file
467
modules/system/browser-vpn.nix
Normal file
|
|
@ -0,0 +1,467 @@
|
|||
# Browser VPN Isolation Module
|
||||
# Provides: Isolated browsers (Firefox, Tor, Thorium, Kitty) running in Podman through VPN
|
||||
#
|
||||
# Usage:
|
||||
# myModules.browserVpn = {
|
||||
# enable = true;
|
||||
# browsers = [ "firefox" "tor-browser" "thorium" "kitty" ]; # default: all
|
||||
# gtkTheme = "Catppuccin-Frappe-Standard-Blue-Dark";
|
||||
# repositoryPath = "/home/user/nixos"; # Path to container Dockerfiles
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.browserVpn;
|
||||
|
||||
# Helper function for auto-recovery from podman namespace corruption
|
||||
# Detects "cannot re-exec process" errors and runs migrate to fix
|
||||
podmanRecoveryHelper = ''
|
||||
podman_with_recovery() {
|
||||
local output
|
||||
local exit_code
|
||||
|
||||
# First attempt
|
||||
output=$(podman "$@" 2>&1)
|
||||
exit_code=$?
|
||||
|
||||
# Check for the namespace corruption error
|
||||
if echo "$output" | grep -q "cannot re-exec process to join the existing user namespace"; then
|
||||
echo "Detected stale podman namespace, running recovery..."
|
||||
podman system migrate 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
# Retry the command
|
||||
output=$(podman "$@" 2>&1)
|
||||
exit_code=$?
|
||||
fi
|
||||
|
||||
echo "$output"
|
||||
return $exit_code
|
||||
}
|
||||
'';
|
||||
|
||||
# Backend script generator for browsers
|
||||
# Firefox needs --security-opt=label=disable, others use --cap-drop=ALL
|
||||
mkBrowserBackend =
|
||||
name: containerName: imageName: dataVol: securityOpts: extraCmd:
|
||||
pkgs.writeShellScriptBin "${name}-vpn-backend" ''
|
||||
ACTION="$1"
|
||||
W_DISPLAY="$2"
|
||||
RUNTIME_DIR="$3"
|
||||
REPO_DIR="${cfg.repositoryPath}"
|
||||
|
||||
${podmanRecoveryHelper}
|
||||
|
||||
case "$ACTION" in
|
||||
stop)
|
||||
echo "Stopping containers..."
|
||||
podman_with_recovery stop ${containerName} 2>/dev/null || true
|
||||
systemctl --user stop gluetun.service
|
||||
echo "Containers stopped."
|
||||
;;
|
||||
status)
|
||||
echo "=== gluetun ==="
|
||||
systemctl --user status gluetun.service --no-pager 2>/dev/null || echo "Not running (or service not found)"
|
||||
echo ""
|
||||
echo "=== ${containerName} ==="
|
||||
podman_with_recovery ps --filter name=${containerName} 2>/dev/null || echo "Not running"
|
||||
;;
|
||||
build)
|
||||
echo "Building ${name} container..."
|
||||
podman_with_recovery build -t ${imageName}:latest "$REPO_DIR/containers/${name}-wayland/"
|
||||
echo "Build complete."
|
||||
;;
|
||||
run)
|
||||
if ! podman_with_recovery image exists ${imageName}:latest 2>/dev/null; then
|
||||
echo "Building ${name} container image..."
|
||||
podman_with_recovery build -t ${imageName}:latest "$REPO_DIR/containers/${name}-wayland/"
|
||||
fi
|
||||
|
||||
echo "Starting VPN container (user service)..."
|
||||
systemctl --user start gluetun.service
|
||||
|
||||
echo "Waiting for VPN connection..."
|
||||
sleep 10
|
||||
|
||||
echo "Starting ${name} with native Wayland (Rootless)..."
|
||||
podman_with_recovery run --rm -d \
|
||||
--name ${containerName} \
|
||||
--network=container:gluetun \
|
||||
${securityOpts} \
|
||||
--userns=keep-id \
|
||||
--shm-size=2g \
|
||||
--device=/dev/dri \
|
||||
-v "$RUNTIME_DIR/$W_DISPLAY:/run/user/1000/$W_DISPLAY:ro" \
|
||||
-v "$RUNTIME_DIR/pipewire-0:/tmp/pipewire-0:ro" \
|
||||
-v "$RUNTIME_DIR/pulse:/tmp/pulse:ro" \
|
||||
-v /etc/machine-id:/etc/machine-id:ro \
|
||||
-e "WAYLAND_DISPLAY=$W_DISPLAY" \
|
||||
-e "XDG_RUNTIME_DIR=/run/user/1000" \
|
||||
-e "PULSE_SERVER=unix:/tmp/pulse/native" \
|
||||
-e "MOZ_ENABLE_WAYLAND=1" \
|
||||
-e "LIBGL_ALWAYS_SOFTWARE=1" \
|
||||
-e "MOZ_WEBRENDER=0" \
|
||||
-e "LD_PRELOAD=" \
|
||||
-v ${dataVol} \
|
||||
-e GTK_THEME=${cfg.gtkTheme} \
|
||||
-e "GSETTINGS_BACKEND=keyfile" \
|
||||
${imageName}:latest \
|
||||
${extraCmd}
|
||||
|
||||
echo ""
|
||||
echo "${name} started! Window should appear on your desktop."
|
||||
;;
|
||||
*)
|
||||
echo "Usage: ${name}-vpn-backend {stop|status|build|run} <DISPLAY> <RUNTIME_DIR>"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
'';
|
||||
|
||||
# Frontend wrapper script
|
||||
mkFrontendScript =
|
||||
name: backend:
|
||||
pkgs.writeShellScriptBin "${name}-vpn-podman" ''
|
||||
CMD="run"
|
||||
if [ -n "$1" ]; then
|
||||
CMD="$1"
|
||||
fi
|
||||
${backend}/bin/${name}-vpn-backend \
|
||||
"$CMD" \
|
||||
"$WAYLAND_DISPLAY" \
|
||||
"$XDG_RUNTIME_DIR"
|
||||
'';
|
||||
|
||||
# Desktop entry generator
|
||||
mkDesktopEntry = name: displayName: icon: category: keywords: ''
|
||||
cat > $out/share/applications/${name}-vpn.desktop << 'EOF'
|
||||
[Desktop Entry]
|
||||
Name=${displayName} (Isolated VPN)
|
||||
Comment=${displayName} with network isolation through VPN
|
||||
Exec=${name}-vpn-podman
|
||||
Icon=${icon}
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=${category};
|
||||
Keywords=${keywords};
|
||||
EOF
|
||||
'';
|
||||
|
||||
# Firefox policies to disable IPv6 and force fast connections
|
||||
firefoxPolicies = pkgs.writeText "policies.json" (
|
||||
builtins.toJSON {
|
||||
policies = {
|
||||
DisableAppUpdate = true;
|
||||
DisableTelemetry = true;
|
||||
DisablePocket = true;
|
||||
DisableFirefoxStudies = true;
|
||||
EnableTrackingProtection = {
|
||||
Value = true;
|
||||
Locked = true;
|
||||
Cryptomining = true;
|
||||
Fingerprinting = true;
|
||||
};
|
||||
Preferences = {
|
||||
"network.dns.disableIPv6" = true;
|
||||
"network.ipv6" = false;
|
||||
"network.http.fast-fallback-to-IPv4" = true;
|
||||
"network.trr.mode" = 5; # Disable DNS over HTTPS (use system/VPN DNS)
|
||||
"ui.systemUsesDarkTheme" = 1;
|
||||
"browser.theme.content-theme" = 0;
|
||||
"browser.theme.toolbar-theme" = 0;
|
||||
"browser.in-content.dark-mode" = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
# Browser configurations
|
||||
# Firefox needs label=disable for its internal sandbox to work
|
||||
firefoxBackend =
|
||||
mkBrowserBackend "firefox" "firefox-vpn" "localhost/firefox-wayland"
|
||||
"firefox-vpn-data:/home/firefox-user/.mozilla"
|
||||
"--security-opt=label=disable --security-opt=seccomp=unconfined -v ${firefoxPolicies}:/usr/lib/firefox/distribution/policies.json:ro"
|
||||
"";
|
||||
|
||||
# Other browsers use --cap-drop=ALL for enhanced security
|
||||
torBrowserBackend =
|
||||
mkBrowserBackend "tor-browser" "tor-browser-vpn" "localhost/tor-browser-wayland"
|
||||
"tor-browser-vpn-data:/home/tor-user/tor-browser/Browser/TorBrowser/Data"
|
||||
"--cap-drop=ALL"
|
||||
"";
|
||||
|
||||
thoriumBackend =
|
||||
mkBrowserBackend "thorium" "thorium-vpn" "localhost/thorium-wayland"
|
||||
"thorium-vpn-data:/home/thorium-user/.config/thorium"
|
||||
"--cap-drop=ALL"
|
||||
"thorium-browser --ozone-platform=wayland --enable-features=UseOzonePlatform --enable-gpu-rasterization --enable-zero-copy --no-sandbox";
|
||||
|
||||
# Thorium Dev backend with custom browser flags for localhost-only access
|
||||
thoriumDevBackend = pkgs.writeShellScriptBin "thorium-dev-vpn-backend" ''
|
||||
ACTION="$1"
|
||||
W_DISPLAY="$2"
|
||||
RUNTIME_DIR="$3"
|
||||
REPO_DIR="${cfg.repositoryPath}"
|
||||
|
||||
${podmanRecoveryHelper}
|
||||
|
||||
case "$ACTION" in
|
||||
stop)
|
||||
echo "Stopping containers..."
|
||||
podman_with_recovery stop thorium-dev-vpn 2>/dev/null || true
|
||||
systemctl --user stop gluetun.service
|
||||
echo "Containers stopped."
|
||||
;;
|
||||
status)
|
||||
echo "=== gluetun ==="
|
||||
systemctl --user status gluetun.service --no-pager 2>/dev/null || echo "Not running (or service not found)"
|
||||
echo ""
|
||||
echo "=== thorium-dev-vpn ==="
|
||||
podman_with_recovery ps --filter name=thorium-dev-vpn 2>/dev/null || echo "Not running"
|
||||
;;
|
||||
build)
|
||||
echo "Building thorium-dev container..."
|
||||
podman_with_recovery build -t localhost/thorium-wayland:latest "$REPO_DIR/containers/thorium-wayland/"
|
||||
echo "Build complete."
|
||||
;;
|
||||
run)
|
||||
if ! podman_with_recovery image exists localhost/thorium-wayland:latest 2>/dev/null; then
|
||||
echo "Building thorium-dev container image..."
|
||||
podman_with_recovery build -t localhost/thorium-wayland:latest "$REPO_DIR/containers/thorium-wayland/"
|
||||
fi
|
||||
|
||||
echo "Starting VPN container (user service)..."
|
||||
systemctl --user start gluetun.service
|
||||
|
||||
echo "Waiting for VPN connection..."
|
||||
sleep 5
|
||||
|
||||
echo "Starting thorium-dev with native Wayland (Rootless) and localhost-only restrictions..."
|
||||
podman_with_recovery run --rm -d \
|
||||
--name thorium-dev-vpn \
|
||||
--network=container:gluetun \
|
||||
--cap-drop=ALL \
|
||||
--userns=keep-id \
|
||||
--shm-size=2g \
|
||||
--device=/dev/dri \
|
||||
-v "$RUNTIME_DIR/$W_DISPLAY:/tmp/$W_DISPLAY:ro" \
|
||||
-v "$RUNTIME_DIR/pipewire-0:/tmp/pipewire-0:ro" \
|
||||
-v "$RUNTIME_DIR/pulse:/tmp/pulse:ro" \
|
||||
-v /etc/machine-id:/etc/machine-id:ro \
|
||||
-e "WAYLAND_DISPLAY=$W_DISPLAY" \
|
||||
-e "XDG_RUNTIME_DIR=/tmp" \
|
||||
-e "PULSE_SERVER=unix:/tmp/pulse/native" \
|
||||
-e "MOZ_ENABLE_WAYLAND=1" \
|
||||
-v thorium-dev-vpn-data:/home/thorium-user/.config/thorium \
|
||||
-e GTK_THEME=${cfg.gtkTheme} \
|
||||
-e "GSETTINGS_BACKEND=keyfile" \
|
||||
localhost/thorium-wayland:latest \
|
||||
thorium-browser \
|
||||
--ozone-platform=wayland \
|
||||
--enable-features=UseOzonePlatform \
|
||||
--enable-gpu-rasterization \
|
||||
--enable-zero-copy \
|
||||
--no-sandbox \
|
||||
--proxy-server="http://127.0.0.1:65535" \
|
||||
--proxy-bypass-list="localhost;127.0.0.1;host.containers.internal;*.local"
|
||||
|
||||
echo ""
|
||||
echo "thorium-dev started! Window should appear on your desktop."
|
||||
echo "This browser is restricted to localhost and host.containers.internal only."
|
||||
;;
|
||||
*)
|
||||
echo "Usage: thorium-dev-vpn-backend {stop|status|build|run} <DISPLAY> <RUNTIME_DIR>"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
'';
|
||||
|
||||
# Kitty backend (special handling for config mounts)
|
||||
kittyBackend = pkgs.writeShellScriptBin "kitty-vpn-backend" ''
|
||||
ACTION="$1"
|
||||
W_DISPLAY="$2"
|
||||
RUNTIME_DIR="$3"
|
||||
REPO_DIR="${cfg.repositoryPath}"
|
||||
|
||||
${podmanRecoveryHelper}
|
||||
|
||||
resolve_path() {
|
||||
realpath "$1"
|
||||
}
|
||||
|
||||
case "$ACTION" in
|
||||
stop)
|
||||
echo "Stopping containers..."
|
||||
podman_with_recovery stop kitty-vpn 2>/dev/null || true
|
||||
systemctl --user stop gluetun.service
|
||||
echo "Containers stopped."
|
||||
;;
|
||||
status)
|
||||
echo "=== gluetun ==="
|
||||
systemctl --user status gluetun.service --no-pager 2>/dev/null || echo "Not running (or service not found)"
|
||||
echo ""
|
||||
echo "=== kitty-vpn ==="
|
||||
podman_with_recovery ps --filter name=kitty-vpn 2>/dev/null || echo "Not running"
|
||||
;;
|
||||
build)
|
||||
echo "Building Arch Kitty container..."
|
||||
podman_with_recovery build -t localhost/arch-kitty:latest "$REPO_DIR/containers/arch-kitty/"
|
||||
echo "Build complete."
|
||||
;;
|
||||
run)
|
||||
if ! podman_with_recovery image exists localhost/arch-kitty:latest 2>/dev/null; then
|
||||
echo "Building Arch Kitty container image..."
|
||||
podman_with_recovery build -t localhost/arch-kitty:latest "$REPO_DIR/containers/arch-kitty/"
|
||||
fi
|
||||
|
||||
echo "Starting VPN container (user service)..."
|
||||
systemctl --user start gluetun.service
|
||||
|
||||
echo "Waiting for VPN connection..."
|
||||
sleep 5
|
||||
|
||||
KITTY_CONF_DIR="${cfg.kittyConfigDir}"
|
||||
KITTY_CONF_FILE="${cfg.kittyConfigDir}/kitty.conf"
|
||||
BASHRC_FILE="${cfg.bashrcPath}"
|
||||
|
||||
REAL_KITTY_CONF=$(resolve_path "$KITTY_CONF_FILE")
|
||||
REAL_BASHRC=$(resolve_path "$BASHRC_FILE")
|
||||
|
||||
echo "Starting Kitty with native Wayland (Rootless)..."
|
||||
podman_with_recovery run --rm -d \
|
||||
--name kitty-vpn \
|
||||
--network=container:gluetun \
|
||||
--cap-drop=ALL \
|
||||
--userns=keep-id \
|
||||
--shm-size=2g \
|
||||
--device=/dev/dri \
|
||||
-v "$RUNTIME_DIR/$W_DISPLAY:/tmp/$W_DISPLAY:ro" \
|
||||
-v "$RUNTIME_DIR/pipewire-0:/tmp/pipewire-0:ro" \
|
||||
-v "$RUNTIME_DIR/pulse:/tmp/pulse:ro" \
|
||||
-v /etc/machine-id:/etc/machine-id:ro \
|
||||
-v "$KITTY_CONF_DIR:/home/arch-user/.config/kitty:ro" \
|
||||
-v "$REAL_KITTY_CONF:/home/arch-user/.config/kitty/kitty.conf:ro" \
|
||||
-v "$REAL_BASHRC:/home/arch-user/.bashrc:ro" \
|
||||
-v arch-user-home:/home/arch-user \
|
||||
-e "WAYLAND_DISPLAY=$W_DISPLAY" \
|
||||
-e "XDG_RUNTIME_DIR=/tmp" \
|
||||
-e "PULSE_SERVER=unix:/tmp/pulse/native" \
|
||||
localhost/arch-kitty:latest
|
||||
|
||||
echo ""
|
||||
echo "Kitty started! Window should appear on your desktop."
|
||||
;;
|
||||
*)
|
||||
echo "Usage: kitty-vpn-backend {stop|status|build|run} <DISPLAY> <RUNTIME_DIR>"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
'';
|
||||
|
||||
# Build list of enabled browsers
|
||||
enabledPackages = lib.flatten [
|
||||
(lib.optional (builtins.elem "firefox" cfg.browsers) [
|
||||
firefoxBackend
|
||||
(mkFrontendScript "firefox" firefoxBackend)
|
||||
])
|
||||
(lib.optional (builtins.elem "tor-browser" cfg.browsers) [
|
||||
torBrowserBackend
|
||||
(mkFrontendScript "tor-browser" torBrowserBackend)
|
||||
])
|
||||
(lib.optional (builtins.elem "thorium" cfg.browsers) [
|
||||
thoriumBackend
|
||||
(mkFrontendScript "thorium" thoriumBackend)
|
||||
])
|
||||
(lib.optional (builtins.elem "thorium-dev" cfg.browsers) [
|
||||
thoriumDevBackend
|
||||
(mkFrontendScript "thorium-dev" thoriumDevBackend)
|
||||
])
|
||||
(lib.optional (builtins.elem "kitty" cfg.browsers) [
|
||||
kittyBackend
|
||||
(mkFrontendScript "kitty" kittyBackend)
|
||||
])
|
||||
];
|
||||
|
||||
desktopEntriesPackage = pkgs.runCommand "browser-vpn-desktop-entries" { } ''
|
||||
mkdir -p $out/share/applications
|
||||
|
||||
${lib.optionalString (builtins.elem "firefox" cfg.browsers) (
|
||||
mkDesktopEntry "firefox" "Firefox" "firefox" "Network;WebBrowser" "browser;vpn;isolated;secure"
|
||||
)}
|
||||
${lib.optionalString (builtins.elem "tor-browser" cfg.browsers) (
|
||||
mkDesktopEntry "tor-browser" "Tor Browser" "firefox" "Network;WebBrowser"
|
||||
"browser;vpn;isolated;secure;tor;onion"
|
||||
)}
|
||||
${lib.optionalString (builtins.elem "thorium" cfg.browsers) (
|
||||
mkDesktopEntry "thorium" "Thorium" "chromium" "Network;WebBrowser"
|
||||
"browser;vpn;isolated;secure;chromium;thorium;privacy"
|
||||
)}
|
||||
${lib.optionalString (builtins.elem "thorium-dev" cfg.browsers) (
|
||||
mkDesktopEntry "thorium-dev" "Thorium (Dev/Local)" "chromium" "Network;WebBrowser"
|
||||
"browser;vpn;isolated;secure;chromium;thorium;dev;local"
|
||||
)}
|
||||
${lib.optionalString (builtins.elem "kitty" cfg.browsers) (
|
||||
mkDesktopEntry "kitty" "Kitty" "kitty" "System;TerminalEmulator" "terminal;vpn;isolated;kitty;arch"
|
||||
)}
|
||||
'';
|
||||
|
||||
in
|
||||
{
|
||||
options.myModules.browserVpn = {
|
||||
enable = lib.mkEnableOption "VPN-isolated browser containers";
|
||||
|
||||
browsers = lib.mkOption {
|
||||
type = lib.types.listOf (
|
||||
lib.types.enum [
|
||||
"firefox"
|
||||
"tor-browser"
|
||||
"thorium"
|
||||
"thorium-dev"
|
||||
"kitty"
|
||||
]
|
||||
);
|
||||
default = [
|
||||
"firefox"
|
||||
"tor-browser"
|
||||
"thorium"
|
||||
"thorium-dev"
|
||||
"kitty"
|
||||
];
|
||||
description = "Which browsers to enable";
|
||||
};
|
||||
|
||||
gtkTheme = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "Catppuccin-Mocha-Standard-Blue-Dark";
|
||||
description = "GTK theme for browsers";
|
||||
};
|
||||
|
||||
repositoryPath = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = config.myModules.system.repoPath;
|
||||
description = "Path to repository containing container Dockerfiles";
|
||||
};
|
||||
|
||||
kittyConfigDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/home/ashie/.config/kitty";
|
||||
description = "Path to kitty configuration directory";
|
||||
};
|
||||
|
||||
bashrcPath = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/home/ashie/.bashrc";
|
||||
description = "Path to bashrc file for Kitty container";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
environment.systemPackages = enabledPackages ++ [ desktopEntriesPackage ];
|
||||
};
|
||||
}
|
||||
118
modules/system/caddy-cloudflare.nix
Normal file
118
modules/system/caddy-cloudflare.nix
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
# Caddy with Cloudflare DNS-01 ACME Module
|
||||
# Provides: Caddy reverse proxy with automatic SSL via Cloudflare DNS
|
||||
#
|
||||
# Usage:
|
||||
# myModules.caddyCloudflare = {
|
||||
# enable = true;
|
||||
# email = "you@example.com";
|
||||
# cloudflareApiTokenFile = config.sops.secrets.cloudflare_api_key.path;
|
||||
# virtualHosts = {
|
||||
# "api.example.com" = { reverseProxy = "127.0.0.1:8080"; };
|
||||
# };
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.caddyCloudflare;
|
||||
|
||||
# Generate virtual host configs with security headers
|
||||
mkVirtualHost = name: hostCfg: {
|
||||
extraConfig = ''
|
||||
# Security headers
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "${hostCfg.frameOptions}"
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
${lib.optionalString (hostCfg.csp != null) ''Content-Security-Policy "${hostCfg.csp}"''}
|
||||
-Server
|
||||
}
|
||||
reverse_proxy ${hostCfg.reverseProxy}
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
options.myModules.caddyCloudflare = {
|
||||
enable = lib.mkEnableOption "Caddy with Cloudflare DNS-01 ACME";
|
||||
|
||||
email = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Email for ACME certificate registration";
|
||||
};
|
||||
|
||||
cloudflareApiTokenFile = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
description = "Path to file containing Cloudflare API token";
|
||||
};
|
||||
|
||||
virtualHosts = lib.mkOption {
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule {
|
||||
options = {
|
||||
reverseProxy = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Backend address (e.g., 127.0.0.1:8080)";
|
||||
};
|
||||
frameOptions = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "DENY";
|
||||
description = "X-Frame-Options header value";
|
||||
};
|
||||
csp = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https:;";
|
||||
description = "Content-Security-Policy header (null to disable)";
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
default = { };
|
||||
description = "Virtual host configurations";
|
||||
};
|
||||
|
||||
hardenSystemd = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Apply systemd hardening to Caddy service";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
services.caddy = {
|
||||
enable = true;
|
||||
email = cfg.email;
|
||||
|
||||
# Caddy with Cloudflare DNS plugin
|
||||
package = pkgs.caddy.withPlugins {
|
||||
plugins = [ "github.com/caddy-dns/cloudflare@v0.2.3-0.20251204174556-6dc1fbb7e925" ];
|
||||
hash = "sha256-htrfa7whiIK2pqtKl6pKFby928dCkMmJp3Hu0e3JBX4=";
|
||||
};
|
||||
|
||||
globalConfig = ''
|
||||
acme_dns cloudflare {env.CF_API_TOKEN}
|
||||
servers {
|
||||
protocols h1 h2
|
||||
}
|
||||
'';
|
||||
|
||||
virtualHosts = lib.mapAttrs mkVirtualHost cfg.virtualHosts;
|
||||
};
|
||||
|
||||
# Systemd hardening
|
||||
systemd.services.caddy.serviceConfig = lib.mkIf cfg.hardenSystemd {
|
||||
NoNewPrivileges = true;
|
||||
ProtectHome = true;
|
||||
ProtectSystem = "strict";
|
||||
PrivateTmp = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectControlGroups = true;
|
||||
EnvironmentFile = cfg.cloudflareApiTokenFile;
|
||||
};
|
||||
};
|
||||
}
|
||||
199
modules/system/citron-sandboxed.nix
Normal file
199
modules/system/citron-sandboxed.nix
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
# Citron Emulator Sandboxed with nix-bwrapper
|
||||
# Runs AppImage directly (self-extracting) since pkgforge uses non-standard compression
|
||||
# Uses manual DBus proxy approach like Steam/Faugus for stronger isolation
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
|
||||
|
||||
pname = "citron";
|
||||
version = "0.12.25";
|
||||
appId = "org.citron_emu.citron";
|
||||
|
||||
citronAppImage = pkgs.fetchurl {
|
||||
url = "https://github.com/pkgforge-dev/Citron-AppImage/releases/download/0.12.25%402025-12-26_1766769485/Citron-0.12.25-anylinux-x86_64.AppImage";
|
||||
sha256 = "sha256-BLTX4IZX5BNt7NlUti8NILL76NCzsPShkvx8BS/pl38=";
|
||||
};
|
||||
|
||||
# Create a wrapper script that runs the AppImage directly
|
||||
# AppImages are self-extracting executables
|
||||
citronWrapper = pkgs.writeShellScriptBin "citron" ''
|
||||
# Ensure the AppImage can extract to a writable location
|
||||
export APPIMAGE_EXTRACT_AND_RUN=1
|
||||
export TMPDIR="$HOME/.cache/citron-tmp"
|
||||
mkdir -p "$TMPDIR"
|
||||
|
||||
# Copy AppImage to cache and make executable if needed
|
||||
|
||||
# Use a unique name based on the hash to avoid busy-file issues
|
||||
# Sanitize hash to remove slashes which break paths
|
||||
APPIMAGE_HASH=$(echo "${citronAppImage.outputHash}" | tr '/' '_')
|
||||
APPIMAGE="$TMPDIR/citron-$APPIMAGE_HASH.AppImage"
|
||||
|
||||
if [ ! -f "$APPIMAGE" ]; then
|
||||
# Clean up old versions
|
||||
rm -f "$TMPDIR"/citron-*.AppImage
|
||||
cp "${citronAppImage}" "$APPIMAGE"
|
||||
chmod 755 "$APPIMAGE"
|
||||
fi
|
||||
exec "$APPIMAGE" "$@"
|
||||
'';
|
||||
|
||||
# Final package with proper attributes
|
||||
citron =
|
||||
pkgs.symlinkJoin {
|
||||
name = "${pname}-${version}";
|
||||
paths = [ citronWrapper ];
|
||||
postBuild = ''
|
||||
mkdir -p $out/share/applications
|
||||
cat > $out/share/applications/${appId}.desktop << EOF
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Citron
|
||||
Comment=Nintendo Switch Emulator
|
||||
Exec=citron
|
||||
Icon=citron
|
||||
Terminal=false
|
||||
Categories=Game;Emulator;
|
||||
EOF
|
||||
'';
|
||||
}
|
||||
// {
|
||||
inherit pname version;
|
||||
meta = {
|
||||
description = "Nintendo Switch Emulator";
|
||||
homepage = "https://citron-emu.org/";
|
||||
mainProgram = "citron";
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
citron-sandboxed = bwrapperPkgs.mkBwrapper {
|
||||
app = {
|
||||
package = citron;
|
||||
id = appId;
|
||||
env = {
|
||||
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
|
||||
QT_QPA_PLATFORM = "wayland;xcb";
|
||||
XDG_CURRENT_DESKTOP = "KDE";
|
||||
# Allow AppImage to extract and run
|
||||
APPIMAGE_EXTRACT_AND_RUN = "1";
|
||||
};
|
||||
};
|
||||
|
||||
# Enable X11 and Wayland
|
||||
sockets.x11 = true;
|
||||
sockets.wayland = true;
|
||||
|
||||
# Disable Flatpak emulation
|
||||
flatpak.enable = false;
|
||||
|
||||
fhsenv.opts = {
|
||||
unshareUser = true;
|
||||
unshareUts = false;
|
||||
unshareCgroup = false;
|
||||
unsharePid = false;
|
||||
unshareNet = false; # Need network for online features
|
||||
unshareIpc = false;
|
||||
};
|
||||
|
||||
fhsenv.bwrap.baseArgs = lib.mkForce [
|
||||
"--new-session"
|
||||
"--proc /proc"
|
||||
"--dev /dev"
|
||||
"--dev-bind /dev/dri /dev/dri" # GPU acceleration
|
||||
"--dev-bind /dev/shm /dev/shm" # Shared memory
|
||||
"--dev-bind-try /dev/uinput /dev/uinput" # Controller support
|
||||
"--dev-bind-try /dev/input /dev/input"
|
||||
"--tmpfs /home"
|
||||
"--tmpfs /tmp"
|
||||
"--tmpfs /run"
|
||||
"--tmpfs /run"
|
||||
"--dir /run/user"
|
||||
"--dir /run/user/${toString config.users.users.ashie.uid}"
|
||||
# Fix for amdgpu.ids missing - use tmpfs so mkdir can succeed
|
||||
"--tmpfs /usr/share"
|
||||
"--ro-bind ${pkgs.libdrm}/share/libdrm /usr/share/libdrm"
|
||||
# System paths
|
||||
"--ro-bind /sys /sys"
|
||||
"--ro-bind-try /run/current-system /run/current-system"
|
||||
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
|
||||
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
|
||||
"--ro-bind-try /nix/store /nix/store"
|
||||
"--dir /run/systemd/resolve"
|
||||
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
|
||||
# udev for controller hotplug
|
||||
"--ro-bind-try /run/udev /run/udev"
|
||||
];
|
||||
|
||||
# Disable built-in DBus module (invokes bwrap without --unshare-user)
|
||||
dbus.enable = false;
|
||||
|
||||
# Manually set up DBus proxy with --unshare-user (session bus only)
|
||||
# Also create required directories before bwrap runs
|
||||
script.preCmds.stage2 = ''
|
||||
# Create directories that bwrap will bind
|
||||
mkdir -p "$HOME/.cache/citron-tmp"
|
||||
mkdir -p "$HOME/.config/citron"
|
||||
mkdir -p "$HOME/.local/share/citron"
|
||||
mkdir -p "$HOME/Games/Switch"
|
||||
''
|
||||
+ (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
|
||||
inherit appId;
|
||||
enableSystemBus = false;
|
||||
proxyArgs = [
|
||||
"--filter"
|
||||
''--talk="org.freedesktop.portal.*"''
|
||||
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
|
||||
''--talk="org.freedesktop.Notifications"''
|
||||
''--talk="org.freedesktop.ScreenSaver"''
|
||||
''--talk="org.kde.StatusNotifierWatcher"''
|
||||
''--talk="org.kde.KWin"''
|
||||
''--talk="org.gnome.Mutter.DisplayConfig"''
|
||||
''--talk="org.freedesktop.secrets"''
|
||||
|
||||
''--talk="com.feralinteractive.GameMode"''
|
||||
''--own="${appId}"''
|
||||
''--own="${appId}.*"''
|
||||
];
|
||||
};
|
||||
|
||||
fhsenv.bwrap.additionalArgs = [
|
||||
# D-Bus session proxy only
|
||||
''--bind "$XDG_RUNTIME_DIR/app/${appId}/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
|
||||
# Wayland socket
|
||||
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
|
||||
|
||||
# PipeWire + Pulse
|
||||
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
|
||||
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
|
||||
|
||||
# Manual mounts for data persistence
|
||||
"--ro-bind-try $HOME/.config/kdedefaults $HOME/.config/kdedefaults"
|
||||
"--ro-bind-try $HOME/.local/share/color-schemes $HOME/.local/share/color-schemes"
|
||||
"--ro-bind-try $HOME/.config/fontconfig $HOME/.config/fontconfig"
|
||||
"--ro-bind-try $HOME/.local/share/fonts $HOME/.local/share/fonts"
|
||||
"--ro-bind-try $HOME/.icons $HOME/.icons"
|
||||
"--ro-bind-try $HOME/.themes $HOME/.themes"
|
||||
"--ro-bind-try $HOME/.config/qt6ct $HOME/.config/qt6ct"
|
||||
"--ro-bind-try $HOME/.config/Kvantum $HOME/.config/Kvantum"
|
||||
"--ro-bind-try $HOME/.config/MangoHud $HOME/.config/MangoHud"
|
||||
# Read-write mounts
|
||||
"--bind $HOME/Games/Switch $HOME/Games/Switch"
|
||||
"--bind $HOME/.config/citron $HOME/.config/citron"
|
||||
"--bind $HOME/.local/share/citron $HOME/.local/share/citron"
|
||||
"--bind $HOME/.cache/citron-tmp $HOME/.cache/citron-tmp"
|
||||
];
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
156
modules/system/cloudflare-firewall.nix
Normal file
156
modules/system/cloudflare-firewall.nix
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
# Cloudflare Firewall Module
|
||||
# Provides: nftables rules restricting web ports (80/443) to Cloudflare IPs only
|
||||
#
|
||||
# Usage:
|
||||
# myModules.cloudflareFirewall = {
|
||||
# enable = true;
|
||||
# restrictedPorts = [ 80 443 ]; # default
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.cloudflareFirewall;
|
||||
portsStr = lib.concatStringsSep ", " (map toString cfg.restrictedPorts);
|
||||
in
|
||||
{
|
||||
options.myModules.cloudflareFirewall = {
|
||||
enable = lib.mkEnableOption "Cloudflare-only firewall rules";
|
||||
|
||||
restrictedPorts = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.port;
|
||||
default = [
|
||||
80
|
||||
443
|
||||
];
|
||||
description = "Ports to restrict to Cloudflare IPs only";
|
||||
};
|
||||
|
||||
allowLocalTraffic = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Allow traffic from private networks (RFC1918)";
|
||||
};
|
||||
|
||||
enablePodmanWorkaround = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Add nftables workaround for Podman networking";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
# Ensure nf_conntrack is loaded for ct state rules
|
||||
boot.kernelModules = [ "nf_conntrack" ];
|
||||
|
||||
networking.nftables = {
|
||||
enable = true;
|
||||
|
||||
tables.cloudflare = {
|
||||
family = "inet";
|
||||
# Cloudflare IP ranges: https://www.cloudflare.com/ips
|
||||
content = ''
|
||||
set cloudflare_ipv4 {
|
||||
type ipv4_addr
|
||||
flags interval
|
||||
elements = {
|
||||
173.245.48.0/20,
|
||||
103.21.244.0/22,
|
||||
103.22.200.0/22,
|
||||
103.31.4.0/22,
|
||||
141.101.64.0/18,
|
||||
108.162.192.0/18,
|
||||
190.93.240.0/20,
|
||||
188.114.96.0/20,
|
||||
197.234.240.0/22,
|
||||
198.41.128.0/17,
|
||||
162.158.0.0/15,
|
||||
104.16.0.0/13,
|
||||
104.24.0.0/14,
|
||||
172.64.0.0/13,
|
||||
131.0.72.0/22
|
||||
}
|
||||
}
|
||||
|
||||
set cloudflare_ipv6 {
|
||||
type ipv6_addr
|
||||
flags interval
|
||||
elements = {
|
||||
2400:cb00::/32,
|
||||
2606:4700::/32,
|
||||
2803:f800::/32,
|
||||
2405:b500::/32,
|
||||
2405:8100::/32,
|
||||
2a06:98c0::/29,
|
||||
2c0f:f248::/32
|
||||
}
|
||||
}
|
||||
|
||||
chain input {
|
||||
type filter hook input priority 0; policy drop;
|
||||
|
||||
# Allow loopback
|
||||
iifname "lo" accept
|
||||
|
||||
# Allow established and related connections
|
||||
ct state established,related accept
|
||||
|
||||
# Allow ICMP (Ping)
|
||||
ip protocol icmp accept
|
||||
ip6 nexthdr icmpv6 accept
|
||||
|
||||
# Allow SSH (Port 5732), otherwise you might get locked out!
|
||||
tcp dport 5732 accept
|
||||
|
||||
# Allow all traffic from internal container interfaces (Podman/CNI)
|
||||
# This allows containers to reach the host (DNS, Gateway)
|
||||
iifname "podman*" accept
|
||||
iifname "cni*" accept
|
||||
|
||||
# Allow RFC1918 Private Networks (LAN, Containers, Link-Local)
|
||||
${lib.optionalString cfg.allowLocalTraffic ''
|
||||
ip saddr 10.0.0.0/8 accept
|
||||
ip saddr 172.16.0.0/12 accept
|
||||
ip saddr 192.168.0.0/16 accept
|
||||
''}
|
||||
ip saddr 169.254.0.0/16 accept
|
||||
|
||||
ip saddr @cloudflare_ipv4 tcp dport { ${portsStr} } accept
|
||||
ip6 saddr @cloudflare_ipv6 tcp dport { ${portsStr} } accept
|
||||
|
||||
# Drop all other traffic to restricted ports (redundant with policy drop but good for clarity/logging if needed)
|
||||
tcp dport { ${portsStr} } drop
|
||||
}
|
||||
|
||||
chain forward {
|
||||
type filter hook forward priority 0; policy drop;
|
||||
|
||||
# Allow forwarding for containers (Internet Access)
|
||||
iifname "podman*" accept
|
||||
oifname "podman*" accept
|
||||
iifname "cni*" accept
|
||||
oifname "cni*" accept
|
||||
|
||||
# Allow established/related forwarding
|
||||
ct state established,related accept
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
tables.podman-mangle = lib.mkIf cfg.enablePodmanWorkaround {
|
||||
family = "ip";
|
||||
content = ''
|
||||
chain prerouting {
|
||||
type filter hook prerouting priority mangle; policy accept;
|
||||
iifname "podman*" meta mark set 0
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
14
modules/system/common.nix
Normal file
14
modules/system/common.nix
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
{
|
||||
options.myModules.system = {
|
||||
repoPath = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/home/ashie/nixos";
|
||||
description = "Path to the main NixOS configuration repository";
|
||||
};
|
||||
};
|
||||
}
|
||||
35
modules/system/default.nix
Normal file
35
modules/system/default.nix
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# System Modules Index
|
||||
# Import this to get all system modules
|
||||
#
|
||||
# Usage in configuration.nix:
|
||||
# imports = [ ./modules/system ];
|
||||
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
./common.nix
|
||||
./security.nix
|
||||
./kernel-hardening.nix
|
||||
./secure-boot.nix
|
||||
./dns-over-tls.nix
|
||||
./cloudflare-firewall.nix
|
||||
./sched-ext.nix
|
||||
./caddy-cloudflare.nix
|
||||
./podman.nix
|
||||
./browser-vpn.nix
|
||||
./ollama-rocm.nix
|
||||
./open-webui.nix
|
||||
./lutris-sandboxed.nix
|
||||
./firefox-sandboxed.nix
|
||||
./brave-sandboxed.nix
|
||||
./prismlauncher-sandboxed.nix
|
||||
./steam-sandboxed.nix
|
||||
./azahar-sandboxed.nix
|
||||
./faugus-sandboxed.nix
|
||||
./citron-sandboxed.nix
|
||||
./ryubing-sandboxed.nix
|
||||
./spotify-sandboxed.nix
|
||||
./performance.nix
|
||||
./vesktop-sandboxed.nix
|
||||
];
|
||||
}
|
||||
65
modules/system/dns-over-tls.nix
Normal file
65
modules/system/dns-over-tls.nix
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# DNS-over-TLS Module
|
||||
# Provides: Encrypted DNS with DNSSEC via systemd-resolved
|
||||
#
|
||||
# Usage:
|
||||
# myModules.dnsOverTls = {
|
||||
# enable = true;
|
||||
# dnssec = true; # default: true
|
||||
# primaryDns = [ "9.9.9.9" "1.1.1.1" ]; # default: Quad9 + Cloudflare
|
||||
# fallbackDns = [ "1.1.1.1" "1.0.0.1" ]; # default: Cloudflare
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.dnsOverTls;
|
||||
in
|
||||
{
|
||||
options.myModules.dnsOverTls = {
|
||||
enable = lib.mkEnableOption "DNS-over-TLS with DNSSEC";
|
||||
|
||||
dnssec = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Enable DNSSEC validation";
|
||||
};
|
||||
|
||||
primaryDns = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [
|
||||
"9.9.9.9"
|
||||
"149.112.112.112"
|
||||
"1.1.1.1"
|
||||
"1.0.0.1"
|
||||
];
|
||||
description = "Primary DNS servers (Quad9 + Cloudflare by default)";
|
||||
};
|
||||
|
||||
fallbackDns = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [
|
||||
"1.1.1.1"
|
||||
"1.0.0.1"
|
||||
];
|
||||
description = "Fallback DNS servers";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
networking.nameservers = cfg.primaryDns;
|
||||
networking.networkmanager.dns = "systemd-resolved";
|
||||
|
||||
services.resolved = {
|
||||
enable = true;
|
||||
dnssec = if cfg.dnssec then "true" else "false";
|
||||
domains = [ "~." ];
|
||||
fallbackDns = cfg.fallbackDns;
|
||||
dnsovertls = "true";
|
||||
};
|
||||
};
|
||||
}
|
||||
154
modules/system/faugus-sandboxed.nix
Normal file
154
modules/system/faugus-sandboxed.nix
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
# Faugus Launcher Sandboxed with nix-bwrapper
|
||||
# Provides a sandboxed Faugus Launcher with restricted permissions
|
||||
# Uses advanced D-Bus proxy approach like Steam for stronger isolation
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
|
||||
in
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
faugus-sandboxed =
|
||||
let
|
||||
singleDesktopPkg =
|
||||
pkgs.symlinkJoin {
|
||||
name = "faugus-launcher-single";
|
||||
paths = [ prev.faugus-launcher ];
|
||||
postBuild = ''
|
||||
rm -rf $out/share/applications
|
||||
mkdir -p $out/share/applications
|
||||
ln -s ${prev.faugus-launcher}/share/applications/faugus-launcher.desktop $out/share/applications/io.github.faugus.Launcher.desktop
|
||||
'';
|
||||
}
|
||||
// {
|
||||
inherit (prev.faugus-launcher) pname version meta;
|
||||
};
|
||||
in
|
||||
bwrapperPkgs.mkBwrapper {
|
||||
app = {
|
||||
package = singleDesktopPkg;
|
||||
id = "io.github.faugus.Launcher";
|
||||
env = {
|
||||
# Propagate XDG_DATA_DIRS so themes/icons can be found
|
||||
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
|
||||
# Fix for file dialogs/theming
|
||||
XDG_CURRENT_DESKTOP = "KDE";
|
||||
# GTK theming
|
||||
GTK_THEME = "catppuccin-frappe-blue-standard";
|
||||
# Force GTK to use the portal for file dialogs
|
||||
GTK_USE_PORTAL = "1";
|
||||
# Force Wayland backend to ensure xdg-foreign protocol works
|
||||
GDK_BACKEND = "wayland";
|
||||
};
|
||||
};
|
||||
|
||||
# Enable X11 and Wayland
|
||||
sockets.x11 = true;
|
||||
sockets.wayland = true;
|
||||
|
||||
# Disable Flatpak emulation
|
||||
flatpak.enable = false;
|
||||
|
||||
fhsenv.opts = {
|
||||
unshareUser = true;
|
||||
unshareUts = false;
|
||||
unshareCgroup = false;
|
||||
unsharePid = false;
|
||||
unshareNet = false; # Need network
|
||||
unshareIpc = false;
|
||||
};
|
||||
|
||||
fhsenv.bwrap.baseArgs = lib.mkForce [
|
||||
"--new-session"
|
||||
"--proc /proc"
|
||||
"--dev /dev"
|
||||
"--dev-bind /dev/dri /dev/dri" # GPU acceleration
|
||||
"--dev-bind /dev/shm /dev/shm" # Shared memory
|
||||
"--tmpfs /home"
|
||||
"--tmpfs /tmp"
|
||||
"--tmpfs /run"
|
||||
"--dir /run/user"
|
||||
"--dir /run/user/${toString config.users.users.ashie.uid}"
|
||||
# System paths
|
||||
"--ro-bind /sys /sys"
|
||||
"--ro-bind-try /run/current-system /run/current-system"
|
||||
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
|
||||
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
|
||||
"--dir /run/systemd/resolve"
|
||||
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
|
||||
];
|
||||
|
||||
mounts = {
|
||||
read = [
|
||||
"$HOME/.config/kdedefaults"
|
||||
"$HOME/.local/share/color-schemes"
|
||||
"$HOME/.config/fontconfig"
|
||||
"$HOME/.icons"
|
||||
"$HOME/.themes"
|
||||
"$HOME/.local/share/themes"
|
||||
"$HOME/.local/share/fonts"
|
||||
"$HOME/.config/Kvantum"
|
||||
"$HOME/.config/gtk-3.0"
|
||||
"$HOME/.config/gtk-4.0"
|
||||
"$HOME/.gtkrc-2.0"
|
||||
"$HOME/.config/MangoHud"
|
||||
];
|
||||
readWrite = [
|
||||
"$HOME/Games"
|
||||
"$HOME/.config/faugus-launcher"
|
||||
"$HOME/.local/share/faugus-launcher"
|
||||
"$HOME/.cache/faugus-launcher"
|
||||
"$HOME/.config/qt6ct" # Allow theming
|
||||
];
|
||||
};
|
||||
|
||||
# Disable built-in DBus module (invokes bwrap without --unshare-user)
|
||||
dbus.enable = false;
|
||||
|
||||
# Manually set up DBus proxy with --unshare-user (session bus only)
|
||||
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
|
||||
appId = "io.github.faugus.Launcher";
|
||||
enableSystemBus = false; # No system bus access
|
||||
proxyArgs = [
|
||||
"--filter"
|
||||
''--talk="org.freedesktop.portal.*"''
|
||||
''--talk="org.freedesktop.portal.FileChooser"''
|
||||
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
|
||||
''--talk="org.freedesktop.Notifications"''
|
||||
''--talk="org.freedesktop.ScreenSaver"''
|
||||
''--talk="org.kde.StatusNotifierWatcher"''
|
||||
''--talk="org.kde.KWin"''
|
||||
''--talk="org.gnome.Mutter.DisplayConfig"''
|
||||
''--talk="org.freedesktop.secrets"''
|
||||
''--talk="org.freedesktop.portal.Settings"''
|
||||
''--talk="com.feralinteractive.GameMode"''
|
||||
''--own="io.github.faugus.Launcher"''
|
||||
''--own="io.github.faugus.Launcher.*"''
|
||||
];
|
||||
};
|
||||
|
||||
fhsenv.bwrap.additionalArgs = [
|
||||
# D-Bus session proxy only
|
||||
''--bind "$XDG_RUNTIME_DIR/app/io.github.faugus.Launcher/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
|
||||
# Wayland socket
|
||||
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
|
||||
|
||||
# PipeWire + Pulse
|
||||
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
|
||||
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
|
||||
|
||||
# dconf for GTK settings
|
||||
"--bind-try /run/user/${toString config.users.users.ashie.uid}/dconf /run/user/${toString config.users.users.ashie.uid}/dconf"
|
||||
];
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
158
modules/system/firefox-sandboxed.nix
Normal file
158
modules/system/firefox-sandboxed.nix
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
# Firefox Sandboxed with nix-bwrapper
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
|
||||
|
||||
# create a custom settings.ini to force dark mode
|
||||
darkSettingsIni = pkgs.writeText "settings.ini" ''
|
||||
[Settings]
|
||||
gtk-theme-name=catppuccin-mocha-mauve-standard
|
||||
gtk-application-prefer-dark-theme=1
|
||||
gtk-cursor-theme-name=Future-Cyan-Hyprcursor_Theme
|
||||
gtk-xft-antialias=1
|
||||
gtk-xft-hinting=1
|
||||
gtk-xft-hintstyle=hintslight
|
||||
gtk-xft-rgba=rgb
|
||||
'';
|
||||
|
||||
# Define policies.json with Catppuccin Theme and P-Stream extension
|
||||
firefoxPolicies = pkgs.writeText "policies.json" (
|
||||
builtins.toJSON {
|
||||
policies = {
|
||||
ExtensionSettings = {
|
||||
# Catppuccin Mocha Mauve (Official)
|
||||
"catppuccin-mocha-mauve-official@catppuccin.com" = {
|
||||
install_url = "https://addons.mozilla.org/firefox/downloads/latest/catppuccin-mocha-mauve-official/latest.xpi";
|
||||
installation_mode = "force_installed";
|
||||
};
|
||||
# P-Stream extension
|
||||
"{de055456-589b-45fe-8342-c685a7ffb424}" = {
|
||||
install_url = "https://github.com/p-stream/extension/releases/download/1.3.5/firefox-mv3-prod.xpi";
|
||||
installation_mode = "force_installed";
|
||||
};
|
||||
};
|
||||
Preferences = {
|
||||
"extensions.activeThemeID" = "catppuccin-mocha-mauve-official@catppuccin.com";
|
||||
"xpinstall.signatures.required" = false;
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
in
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
firefox-sandboxed = bwrapperPkgs.mkBwrapper {
|
||||
app = {
|
||||
package = prev.firefox;
|
||||
# Omit app.id to avoid document portal bind that fails on FUSE
|
||||
env = {
|
||||
MOZ_ENABLE_WAYLAND = "1";
|
||||
LD_PRELOAD = "";
|
||||
# Propagate XDG_DATA_DIRS so GTK can find themes in user profile/system
|
||||
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
|
||||
GTK_THEME = "catppuccin-mocha-mauve-standard";
|
||||
HYPRCURSOR_THEME = "Future-Cyan-Hyprcursor_Theme";
|
||||
HYPRCURSOR_SIZE = "32";
|
||||
};
|
||||
};
|
||||
|
||||
flatpak.enable = false;
|
||||
sockets.x11 = false;
|
||||
sockets.wayland = true;
|
||||
fhsenv.opts = {
|
||||
unshareUser = true;
|
||||
unshareUts = false;
|
||||
unshareCgroup = false;
|
||||
unsharePid = false;
|
||||
unshareNet = false;
|
||||
unshareIpc = false;
|
||||
};
|
||||
|
||||
fhsenv.bwrap.baseArgs = lib.mkForce [
|
||||
"--new-session"
|
||||
"--proc /proc"
|
||||
"--dev /dev"
|
||||
"--dev-bind /dev/dri /dev/dri"
|
||||
"--tmpfs /home"
|
||||
"--tmpfs /mnt"
|
||||
"--tmpfs /run"
|
||||
"--ro-bind-try /run/current-system /run/current-system"
|
||||
"--ro-bind-try /run/booted-system /run/booted-system"
|
||||
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
|
||||
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
|
||||
# Removed: --bind "$XDG_RUNTIME_DIR/doc/by-app/..." which causes FUSE errors
|
||||
"--unsetenv LD_PRELOAD"
|
||||
"--setenv MOZ_ENABLE_WAYLAND \"1\""
|
||||
"--setenv NOTIFY_IGNORE_PORTAL 1"
|
||||
"--dir /etc"
|
||||
"--dir /etc/firefox"
|
||||
"--dir /etc/firefox/policies"
|
||||
"--ro-bind ${firefoxPolicies} /etc/firefox/policies/policies.json"
|
||||
];
|
||||
|
||||
# Filesystem: Limited to Mozilla directories and Downloads
|
||||
mounts = {
|
||||
read = [
|
||||
"$HOME/.config/kdedefaults"
|
||||
"$HOME/.config/fontconfig"
|
||||
"$HOME/.config/user-dirs.dirs"
|
||||
"$HOME/.config/mimeapps.list"
|
||||
"$HOME/.local/share/color-schemes"
|
||||
"$HOME/.local/share/fonts"
|
||||
"$HOME/.icons"
|
||||
"$HOME/.themes"
|
||||
"$HOME/.local/share/themes"
|
||||
"$HOME/.config/gtk-3.0"
|
||||
];
|
||||
readWrite = [
|
||||
"$HOME/.mozilla"
|
||||
"$HOME/.cache/mozilla"
|
||||
"$HOME/Downloads"
|
||||
];
|
||||
};
|
||||
|
||||
# Bind mount systemd-resolved socket for DNS and required system files
|
||||
# Disable built-in DBus module because it invokes bwrap without --unshare-user
|
||||
dbus.enable = false;
|
||||
|
||||
# Manually set up DBus proxy with --unshare-user
|
||||
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
|
||||
appId = "nix.bwrapper.firefox";
|
||||
proxyArgs = [
|
||||
"--filter"
|
||||
''--talk="org.freedesktop.portal.Desktop"''
|
||||
''--talk="org.freedesktop.portal.OpenURI"''
|
||||
''--talk="org.freedesktop.portal.FileChooser"''
|
||||
''--talk="org.freedesktop.secrets"''
|
||||
''--talk="org.kde.StatusNotifierWatcher"''
|
||||
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
|
||||
''--own="org.mozilla.firefox"''
|
||||
''--own="org.mozilla.firefox.*"''
|
||||
''--own="org.mpris.MediaPlayer2.firefox.*"''
|
||||
];
|
||||
enableSystemBus = true;
|
||||
systemProxyArgs = [
|
||||
"--filter"
|
||||
''--talk="org.freedesktop.NetworkManager"''
|
||||
];
|
||||
};
|
||||
|
||||
fhsenv.bwrap.additionalArgs = [
|
||||
''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.firefox/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
''--bind "$XDG_RUNTIME_DIR/app/nix.bwrapper.firefox/bus_system" /run/dbus/system_bus_socket''
|
||||
"--dir /run/systemd/resolve"
|
||||
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
|
||||
"--bind-try /run/user/${toString config.users.users.ashie.uid}/dconf /run/user/${toString config.users.users.ashie.uid}/dconf"
|
||||
];
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
84
modules/system/impermanence.nix
Normal file
84
modules/system/impermanence.nix
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
imports = [ inputs.impermanence.nixosModules.impermanence ];
|
||||
|
||||
boot.initrd.supportedFilesystems = [ "btrfs" ];
|
||||
boot.initrd.systemd.enable = true;
|
||||
fileSystems."/etc/ssh" = {
|
||||
device = "/persist/etc/ssh";
|
||||
fsType = "none";
|
||||
options = [ "bind" ];
|
||||
neededForBoot = true;
|
||||
};
|
||||
|
||||
environment.persistence."/persist" = {
|
||||
hideMounts = true;
|
||||
directories = [
|
||||
"/var/lib/nixos"
|
||||
"/var/lib/systemd" # Random seed and other systemd state
|
||||
"/var/lib/systemd/coredump"
|
||||
"/var/log/journal" # Journald logs (binary format)
|
||||
"/var/log" # Text logs (optional but good for legacy)
|
||||
"/var/lib/containers" # Podman/Docker images and containers
|
||||
"/var/lib/ollama" # LLM models
|
||||
"/var/lib/open-webui" # Chat history
|
||||
"/var/lib/caddy" # SSL certs
|
||||
"/var/lib/tailscale" # Tailscale identity
|
||||
"/var/lib/bluetooth" # Bluetooth pairings
|
||||
"/var/lib/sbctl" # Secure Boot Keys
|
||||
"/etc/NetworkManager/system-connections" # Wifi/Ethernet profiles
|
||||
];
|
||||
|
||||
files = [
|
||||
"/etc/machine-id"
|
||||
];
|
||||
|
||||
users.ashie = {
|
||||
directories = [
|
||||
"Downloads"
|
||||
"Documents"
|
||||
"Music"
|
||||
"Pictures"
|
||||
"Videos"
|
||||
"nixos" # Config repo
|
||||
".local/share/PrismLauncher" # Minecraft
|
||||
".local/share/containers" # Rootless podman
|
||||
".config/BraveSoftware" # Browser profile
|
||||
".mozilla" # Firefox profile
|
||||
".ssh" # User SSH keys
|
||||
".gnupg" # GPG keys
|
||||
".gemini" # AI Assistant State
|
||||
"git" # Git Repositories
|
||||
".local/state" # Application State
|
||||
".config/Antigravity" # Antigravity Config
|
||||
".config/VSCodium" # Codium Config
|
||||
".config/sops" # Sops Keys
|
||||
".config/gh" # Github CLI Auth
|
||||
".local/share/keyrings" # Gnome Keyrings (Passwords)
|
||||
".local/share/flatpak" # Flatpak Apps
|
||||
".vscode" # VSCode Extensions
|
||||
".vscode-oss" # VSCodium Extensions
|
||||
".config/lutris"
|
||||
".local/share/lutris"
|
||||
".local/share/Larian Studios"
|
||||
".config/citron"
|
||||
".local/share/citron"
|
||||
".cache/lutris"
|
||||
".local/share/umu"
|
||||
".cache/mesa_shader_cache"
|
||||
# ".local/share/Steam" # Symlinked to /games/Steam (Already Persistent)
|
||||
".steam" # Steam Symlinks and logs
|
||||
".config/steamtinkerlaunch" # Example of extra tools
|
||||
".local/share/applications" # Desktop entries
|
||||
".local/share/icons" # Application icons
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
139
modules/system/kernel-hardening.nix
Normal file
139
modules/system/kernel-hardening.nix
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
# Kernel Hardening Module
|
||||
# Provides: hardened boot params, sysctl settings, blacklisted modules, ZRAM
|
||||
#
|
||||
# Usage:
|
||||
# myModules.kernelHardening = {
|
||||
# enable = true;
|
||||
# enableZram = true; # default: true
|
||||
# zramPercent = 50; # default: 50
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.kernelHardening;
|
||||
in
|
||||
{
|
||||
options.myModules.kernelHardening = {
|
||||
enable = lib.mkEnableOption "kernel hardening module";
|
||||
|
||||
enableZram = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Enable ZRAM swap";
|
||||
};
|
||||
|
||||
zramPercent = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 50;
|
||||
description = "Percentage of RAM to use for ZRAM";
|
||||
};
|
||||
|
||||
zramAlgorithm = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "lz4";
|
||||
description = "Compression algorithm for ZRAM (lz4, zstd, lzo)";
|
||||
};
|
||||
|
||||
tmpfsPercent = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "50%";
|
||||
description = "Size of /tmp tmpfs as percentage of RAM";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
# Secure boot loader settings
|
||||
boot.loader.systemd-boot.editor = false;
|
||||
|
||||
# Use tmpfs for /tmp
|
||||
boot.tmp = {
|
||||
useTmpfs = true;
|
||||
tmpfsSize = cfg.tmpfsPercent;
|
||||
};
|
||||
|
||||
# Runtime kernel hardening
|
||||
boot.kernelParams = [
|
||||
"slab_nomerge"
|
||||
"init_on_alloc=1"
|
||||
"init_on_free=1"
|
||||
"page_alloc.shuffle=1"
|
||||
"randomize_kstack_offset=on"
|
||||
"vsyscall=none"
|
||||
"oops=panic"
|
||||
];
|
||||
|
||||
# Kernel sysctl hardening
|
||||
boot.kernel.sysctl = {
|
||||
"kernel.kptr_restrict" = 2;
|
||||
"kernel.dmesg_restrict" = 1;
|
||||
"fs.protected_hardlinks" = 1;
|
||||
"fs.protected_symlinks" = 1;
|
||||
"net.ipv4.tcp_syncookies" = 1;
|
||||
"net.ipv4.tcp_rfc1337" = 1;
|
||||
"net.ipv4.ip_forward" = 1;
|
||||
"kernel.unprivileged_bpf_disabled" = 1;
|
||||
"kernel.kexec_load_disabled" = 1;
|
||||
"kernel.perf_event_paranoid" = 3;
|
||||
"net.ipv4.tcp_timestamps" = 0;
|
||||
"dev.tty.ldisc_autoload" = 0;
|
||||
"kernel.yama.ptrace_scope" = 1;
|
||||
"kernel.core_pattern" = "|/bin/false";
|
||||
"net.ipv4.conf.all.accept_redirects" = 0;
|
||||
"net.ipv4.conf.default.accept_redirects" = 0;
|
||||
"net.ipv4.conf.all.secure_redirects" = 0;
|
||||
"net.ipv4.conf.default.secure_redirects" = 0;
|
||||
"net.ipv6.conf.all.accept_redirects" = 0;
|
||||
"net.ipv6.conf.default.accept_redirects" = 0;
|
||||
"net.ipv4.conf.all.send_redirects" = 0;
|
||||
"net.ipv4.conf.default.send_redirects" = 0;
|
||||
"net.ipv4.conf.all.rp_filter" = 2;
|
||||
"net.ipv4.conf.default.rp_filter" = 2;
|
||||
"net.ipv4.conf.all.log_martians" = 1;
|
||||
"net.ipv4.conf.default.log_martians" = 1;
|
||||
"net.ipv4.icmp_echo_ignore_broadcasts" = 1;
|
||||
|
||||
# Network optimization (TCP Buffers)
|
||||
"net.core.rmem_max" = 2500000;
|
||||
"net.core.wmem_max" = 2500000;
|
||||
"net.ipv4.tcp_rmem" = "4096 87380 2500000";
|
||||
"net.ipv4.tcp_wmem" = "4096 65536 2500000";
|
||||
"net.core.netdev_max_backlog" = 5000;
|
||||
};
|
||||
|
||||
# Set IO Scheduler to kyber for NVMe and bfq for SATA
|
||||
services.udev.extraRules = ''
|
||||
# NVMe
|
||||
ACTION=="add|change", KERNEL=="nvme[0-9]*n[0-9]*", ATTR{queue/scheduler}="kyber"
|
||||
# SSD/HDD
|
||||
ACTION=="add|change", KERNEL=="sd[a-z]*", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="bfq"
|
||||
ACTION=="add|change", KERNEL=="sd[a-z]*", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq"
|
||||
'';
|
||||
|
||||
boot.blacklistedKernelModules = [
|
||||
"cramfs"
|
||||
"freevxfs"
|
||||
"jffs2"
|
||||
"hfs"
|
||||
"hfsplus"
|
||||
"udf"
|
||||
# DMA vulnerable modules
|
||||
"firewire-core"
|
||||
"firewire_ohci"
|
||||
"thunderbolt"
|
||||
];
|
||||
|
||||
security.lockKernelModules = true;
|
||||
|
||||
zramSwap = lib.mkIf cfg.enableZram {
|
||||
enable = true;
|
||||
memoryPercent = cfg.zramPercent;
|
||||
algorithm = cfg.zramAlgorithm;
|
||||
};
|
||||
};
|
||||
}
|
||||
139
modules/system/lutris-sandboxed.nix
Normal file
139
modules/system/lutris-sandboxed.nix
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
# Lutris Sandboxed with nix-bwrapper
|
||||
# Provides a sandboxed Lutris with restricted permissions
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
# Apply the bwrapper overlay to get mkBwrapper
|
||||
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
|
||||
in
|
||||
{
|
||||
# Provide the sandboxed Lutris package
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
lutris-sandboxed = bwrapperPkgs.mkBwrapper {
|
||||
app = {
|
||||
package = prev.lutris.override {
|
||||
extraPkgs = pkgs: [
|
||||
pkgs.curl
|
||||
pkgs.wget
|
||||
pkgs.gnutar
|
||||
pkgs.gzip
|
||||
pkgs.zstd
|
||||
pkgs.xz
|
||||
pkgs.p7zip
|
||||
pkgs.which
|
||||
pkgs.file
|
||||
pkgs.zenity
|
||||
pkgs.vulkan-loader
|
||||
pkgs.vulkan-tools
|
||||
pkgs.unzip
|
||||
pkgs.cabextract
|
||||
pkgs.xorg.xrandr
|
||||
pkgs.pciutils
|
||||
pkgs.gamemode.lib
|
||||
pkgs.xdg-utils
|
||||
];
|
||||
};
|
||||
isFhsenv = true; # Lutris uses buildFHSEnv
|
||||
id = "net.lutris.Lutris";
|
||||
env = {
|
||||
WEBKIT_DISABLE_DMABUF_RENDERER = 1;
|
||||
APPIMAGE_EXTRACT_AND_RUN = 1;
|
||||
PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION = "python";
|
||||
GTK_THEME = "catppuccin-mocha-blue-standard";
|
||||
BROWSER = "xdg-open";
|
||||
XDG_CURRENT_DESKTOP = "niri";
|
||||
XDG_SESSION_TYPE = "wayland";
|
||||
DBUS_SESSION_BUS_ADDRESS = "unix:path=$XDG_RUNTIME_DIR/bus";
|
||||
# Ensure Vulkan loader finds the drivers
|
||||
VK_ICD_FILENAMES = "/run/opengl-driver/share/vulkan/icd.d/radeon_icd.x86_64.json:/run/opengl-driver-32/share/vulkan/icd.d/radeon_icd.i686.json";
|
||||
};
|
||||
};
|
||||
|
||||
fhsenv = {
|
||||
skipExtraInstallCmds = false;
|
||||
};
|
||||
|
||||
# Disable Flatpak emulation
|
||||
flatpak.enable = false;
|
||||
|
||||
# Filesystem: Limited to Games directory
|
||||
mounts = {
|
||||
read = [
|
||||
"$HOME/.config/kdedefaults"
|
||||
"$HOME/.local/share/color-schemes"
|
||||
"$HOME/.local/share/Steam/compatibilitytools.d"
|
||||
# GTK Theming
|
||||
"$HOME/.config/gtk-3.0"
|
||||
"$HOME/.config/gtk-4.0"
|
||||
"$HOME/.icons"
|
||||
];
|
||||
|
||||
readWrite = [
|
||||
"$HOME/Games"
|
||||
"$HOME/.local/share/icons"
|
||||
"$HOME/.config/lutris"
|
||||
"$HOME/.local/share/lutris"
|
||||
"$HOME/.cache/lutris"
|
||||
"$HOME/.steam"
|
||||
"$HOME/.local/share/steam"
|
||||
"$HOME/.local/share/umu"
|
||||
"$HOME/.local/share/applications"
|
||||
"$HOME/.local/share/desktop-directories"
|
||||
];
|
||||
};
|
||||
|
||||
# Bind mount systemd-resolved socket directory to fix DNS
|
||||
# The sandbox mounts a tmpfs on /run, so we need to validly expose this
|
||||
fhsenv.bwrap.additionalArgs = [
|
||||
"--dir /run/systemd/resolve"
|
||||
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
|
||||
# D-Bus session proxy
|
||||
''--bind "$XDG_RUNTIME_DIR/app/net.lutris.Lutris/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
# D-Bus system proxy
|
||||
''--bind "$XDG_RUNTIME_DIR/app/net.lutris.Lutris/bus_system" /run/dbus/system_bus_socket''
|
||||
# Wayland socket
|
||||
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
|
||||
# PipeWire + Pulse
|
||||
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
|
||||
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
|
||||
# Bind system themes to /usr/share
|
||||
"--ro-bind /run/current-system/sw/share/themes /usr/share/themes"
|
||||
"--ro-bind /run/current-system/sw/share/icons /usr/share/icons"
|
||||
];
|
||||
|
||||
# Disable built-in DBus module (invokes bwrap without --unshare-user)
|
||||
dbus.enable = false;
|
||||
|
||||
# Manually set up DBus proxy with --unshare-user
|
||||
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
|
||||
appId = "net.lutris.Lutris";
|
||||
enableSystemBus = true;
|
||||
proxyArgs = [
|
||||
"--filter"
|
||||
''--talk="org.freedesktop.Flatpak"''
|
||||
''--talk="org.kde.StatusNotifierWatcher"''
|
||||
''--talk="org.kde.KWin"''
|
||||
''--talk="org.gnome.Mutter.DisplayConfig"''
|
||||
''--talk="org.freedesktop.ScreenSaver"''
|
||||
''--talk="org.freedesktop.portal.*"''
|
||||
''--talk="org.freedesktop.secrets"''
|
||||
''--talk="com.feralinteractive.GameMode"''
|
||||
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
|
||||
''--own="net.lutris.Lutris"''
|
||||
];
|
||||
systemProxyArgs = [
|
||||
"--filter"
|
||||
''--talk="org.freedesktop.UDisks2"'' # Disk detection
|
||||
];
|
||||
};
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
175
modules/system/ollama-rocm.nix
Normal file
175
modules/system/ollama-rocm.nix
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
# Ollama ROCm Module (System)
|
||||
# Provides: Ollama LLM server with AMD ROCm GPU passthrough as system container
|
||||
#
|
||||
# Usage:
|
||||
# myModules.ollamaRocm = {
|
||||
# enable = true;
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.ollamaRocm;
|
||||
in
|
||||
{
|
||||
options.myModules.ollamaRocm = {
|
||||
enable = lib.mkEnableOption "Ollama with ROCm GPU passthrough (System Service)";
|
||||
|
||||
image = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "docker.io/ollama/ollama:rocm";
|
||||
description = "Ollama ROCm container image";
|
||||
};
|
||||
|
||||
dataDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/var/lib/ollama";
|
||||
description = "Path to Ollama data directory (models, etc.)";
|
||||
};
|
||||
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 11434;
|
||||
description = "Ollama API port";
|
||||
};
|
||||
|
||||
hsaGfxVersion = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "12.0.1";
|
||||
description = "HSA_OVERRIDE_GFX_VERSION for AMD GPU compatibility";
|
||||
};
|
||||
|
||||
# Note: For system podman, usually we don't need group permissions if running as root,
|
||||
# but passing devices needs strictly correct flags.
|
||||
# We will assume root execution for simplicity and GPU access.
|
||||
|
||||
keepAlive = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "5m";
|
||||
description = "Duration to keep model in memory (e.g. 5m, 1h). Set to 0 to unload immediately.";
|
||||
};
|
||||
|
||||
# Performance Tuning
|
||||
numParallel = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 1;
|
||||
description = "OLLAMA_NUM_PARALLEL: Concurrent requests (keep low for speed)";
|
||||
};
|
||||
|
||||
maxLoadedModels = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 1;
|
||||
description = "OLLAMA_MAX_LOADED_MODELS: Max models in memory";
|
||||
};
|
||||
|
||||
numThreads = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.int;
|
||||
default = 6; # Optimized for Ryzen 5600X (physical cores)
|
||||
description = "OLLAMA_NUM_THREADS: CPU threads for inference";
|
||||
};
|
||||
|
||||
processPriority = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = -10;
|
||||
description = "Systemd Nice priority (lower is higher priority, range -20 to 19)";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
# Ensure data directory exists
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${cfg.dataDir} 0755 root root -"
|
||||
];
|
||||
|
||||
systemd.services.ollama = {
|
||||
description = "Ollama ROCm Container (System)";
|
||||
after = [ "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
|
||||
serviceConfig = {
|
||||
Restart = "always";
|
||||
Nice = cfg.processPriority;
|
||||
|
||||
# Hardening
|
||||
ProtectSystem = "full";
|
||||
ProtectHome = false;
|
||||
PrivateTmp = true;
|
||||
ProtectKernelTunables = false; # Needed for Podman (BPF, etc)
|
||||
ProtectControlGroups = false; # Podman needs cgroups
|
||||
ProtectKernelModules = true;
|
||||
|
||||
# Allow Podman to write to state and data
|
||||
ReadWritePaths = [
|
||||
"/var/lib/containers"
|
||||
"/run" # Podman needs to write to sockets and runtime dirs in /run
|
||||
"/etc/containers" # Network configs live here
|
||||
cfg.dataDir
|
||||
];
|
||||
|
||||
# ExecStartPre to cleanup old container and create net if needed.
|
||||
# Note: 'podman' in system context sees system containers/networks.
|
||||
ExecStartPre = [
|
||||
"-${pkgs.podman}/bin/podman stop ollama"
|
||||
"-${pkgs.podman}/bin/podman rm ollama"
|
||||
"-${pkgs.podman}/bin/podman network rm antigravity-net"
|
||||
"${pkgs.podman}/bin/podman network create antigravity-net --ignore"
|
||||
|
||||
# Fix permission issue where /var/lib/ollama is a symlink to /var/lib/private/ollama
|
||||
# which is not accessible by the subuid user (200000).
|
||||
(pkgs.writeShellScript "ollama-pre-start" ''
|
||||
DATA_DIR="${cfg.dataDir}"
|
||||
|
||||
# Check if it is a symlink
|
||||
if [ -L "$DATA_DIR" ]; then
|
||||
echo "Detected symlink at $DATA_DIR. Removing and converting to directory..."
|
||||
TARGET=$(readlink -f "$DATA_DIR")
|
||||
rm "$DATA_DIR"
|
||||
mkdir -p "$DATA_DIR"
|
||||
|
||||
# If the target existed and has data, copy it back (optional, but safe)
|
||||
if [ -d "$TARGET" ]; then
|
||||
echo "Restoring data from $TARGET..."
|
||||
cp -r "$TARGET"/* "$DATA_DIR/" || true
|
||||
fi
|
||||
else
|
||||
mkdir -p "$DATA_DIR"
|
||||
fi
|
||||
|
||||
# Fix ownership for UserNS (container user maps to host UID 200000)
|
||||
${pkgs.coreutils}/bin/chown -R 200000:200000 "$DATA_DIR"
|
||||
${pkgs.coreutils}/bin/chmod 0755 "$DATA_DIR"
|
||||
'')
|
||||
];
|
||||
ExecStart = ''
|
||||
${pkgs.podman}/bin/podman run --rm --name ollama \
|
||||
--network=antigravity-net \
|
||||
--network-alias=ollama \
|
||||
--dns=8.8.8.8 \
|
||||
--device=/dev/kfd \
|
||||
--device=/dev/dri \
|
||||
--userns=auto \
|
||||
-e HSA_OVERRIDE_GFX_VERSION=${cfg.hsaGfxVersion} \
|
||||
-e OLLAMA_HOST=0.0.0.0 \
|
||||
-e OLLAMA_ORIGINS="*" \
|
||||
-e OLLAMA_KEEP_ALIVE=${cfg.keepAlive} \
|
||||
-e OLLAMA_NUM_PARALLEL=${toString cfg.numParallel} \
|
||||
-e OLLAMA_MAX_LOADED_MODELS=${toString cfg.maxLoadedModels} \
|
||||
${
|
||||
lib.optionalString (cfg.numThreads != null) "-e OLLAMA_NUM_THREADS=${toString cfg.numThreads}"
|
||||
} \
|
||||
-v ${cfg.dataDir}:/root/.ollama:U \
|
||||
-p 127.0.0.1:${toString cfg.port}:11434 \
|
||||
${cfg.image}
|
||||
'';
|
||||
ExecStop = "${pkgs.podman}/bin/podman stop ollama";
|
||||
};
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
139
modules/system/open-webui.nix
Normal file
139
modules/system/open-webui.nix
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
# Open WebUI Module (System)
|
||||
# Provides: Open WebUI chat interface as system container
|
||||
#
|
||||
# Usage:
|
||||
# myModules.openWebUI = {
|
||||
# enable = true;
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.openWebUI;
|
||||
in
|
||||
{
|
||||
options.myModules.openWebUI = {
|
||||
enable = lib.mkEnableOption "Open WebUI chat interface (System Service)";
|
||||
|
||||
image = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "ghcr.io/open-webui/open-webui:main";
|
||||
description = "Open WebUI container image";
|
||||
};
|
||||
|
||||
dataDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/var/lib/open-webui";
|
||||
description = "Path to Open WebUI data directory";
|
||||
};
|
||||
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 3000;
|
||||
description = "Host port for Open WebUI";
|
||||
};
|
||||
|
||||
# Networking Defaults:
|
||||
# - Ollama is on the SAME system network (antigravity-net), so we use container name 'ollama'.
|
||||
# - Unified Router is a USER container, so we must access via Host gateway.
|
||||
ollamaUrl = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "http://ollama:11434";
|
||||
description = "URL to Ollama API (Internal Container Network)";
|
||||
};
|
||||
|
||||
openaiBaseUrl = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
# 10.88.0.1 is default podman gateway, or use host.containers.internal if available
|
||||
default = "http://host.containers.internal:6767/v1";
|
||||
description = "URL to OpenAI-compatible API (Unified Router on Host)";
|
||||
};
|
||||
|
||||
envFile = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/run/secrets/open_webui_env";
|
||||
description = "Path to environment file with secrets";
|
||||
};
|
||||
|
||||
apiKeyEnvFile = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/run/secrets/rendered/api_key.env";
|
||||
description = "Path to API key environment file";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
# Ensure data directory exists
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${cfg.dataDir} 0750 root root -"
|
||||
];
|
||||
|
||||
systemd.services.open-webui = {
|
||||
description = "Open WebUI Container (System)";
|
||||
after = [
|
||||
"network-online.target"
|
||||
"ollama.service"
|
||||
];
|
||||
wants = [ "network-online.target" ];
|
||||
requires = [ "ollama.service" ]; # It depends on Ollama for local inference often
|
||||
|
||||
serviceConfig = {
|
||||
Restart = "always";
|
||||
|
||||
# Hardening
|
||||
ProtectSystem = "strict";
|
||||
ProtectHome = true;
|
||||
PrivateTmp = true;
|
||||
ProtectKernelTunables = false;
|
||||
ProtectControlGroups = false; # Podman needs cgroups
|
||||
ProtectKernelModules = true;
|
||||
|
||||
# Allow Podman to write to state and data
|
||||
ReadWritePaths = [
|
||||
"/var/lib/containers"
|
||||
"/run"
|
||||
"/etc/containers" # Network configs live here
|
||||
cfg.dataDir
|
||||
];
|
||||
|
||||
# ExecStartPre to cleanup
|
||||
ExecStartPre = [
|
||||
"-${pkgs.podman}/bin/podman stop open-webui"
|
||||
"-${pkgs.podman}/bin/podman rm open-webui"
|
||||
"${pkgs.podman}/bin/podman pull ${cfg.image}"
|
||||
"${pkgs.podman}/bin/podman network create antigravity-net --ignore"
|
||||
# Fix ownership for UserNS (container user maps to host UID 200000)
|
||||
"${pkgs.coreutils}/bin/chown -R 200000:200000 ${cfg.dataDir}"
|
||||
];
|
||||
ExecStart = ''
|
||||
${pkgs.podman}/bin/podman run --rm --name open-webui \
|
||||
--network=antigravity-net \
|
||||
--dns=8.8.8.8 \
|
||||
--userns=auto \
|
||||
-e ENABLE_PERSISTENT_CONFIG=True \
|
||||
-e OPENAI_API_BASE_URL=${cfg.openaiBaseUrl} \
|
||||
-e OLLAMA_BASE_URL=${cfg.ollamaUrl} \
|
||||
-e ENABLE_RAG_WEB_SEARCH=True \
|
||||
-e RAG_WEB_SEARCH=True \
|
||||
-e RAG_WEB_SEARCH_ENGINE=duckduckgo \
|
||||
-e RAG_WEB_SEARCH_RESULT_COUNT=3 \
|
||||
-e ENABLE_RAG=True \
|
||||
--env-file=${cfg.envFile} \
|
||||
--env-file=${cfg.apiKeyEnvFile} \
|
||||
-v ${cfg.dataDir}:/app/backend/data:U \
|
||||
--add-host=host.containers.internal:host-gateway \
|
||||
-p 127.0.0.1:${toString cfg.port}:8080 \
|
||||
${cfg.image}
|
||||
'';
|
||||
ExecStop = "${pkgs.podman}/bin/podman stop open-webui";
|
||||
};
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
61
modules/system/performance.nix
Normal file
61
modules/system/performance.nix
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
options.myModules.performance = {
|
||||
enable = lib.mkEnableOption "system performance optimizations";
|
||||
};
|
||||
|
||||
config = lib.mkIf config.myModules.performance.enable {
|
||||
services.scx = {
|
||||
enable = true;
|
||||
scheduler = "scx_rustland";
|
||||
package = pkgs.scx.full;
|
||||
};
|
||||
|
||||
services.ananicy = {
|
||||
enable = true;
|
||||
package = pkgs.ananicy-cpp;
|
||||
rulesProvider = pkgs.ananicy-rules-cachyos;
|
||||
};
|
||||
|
||||
zramSwap = {
|
||||
enable = lib.mkForce true;
|
||||
algorithm = lib.mkForce "zstd";
|
||||
memoryPercent = lib.mkForce 100;
|
||||
priority = 100;
|
||||
};
|
||||
|
||||
boot.kernel.sysctl = {
|
||||
"vm.swappiness" = lib.mkForce 180;
|
||||
"vm.watermark_boost_factor" = lib.mkForce 0;
|
||||
"vm.watermark_scale_factor" = lib.mkForce 125;
|
||||
"vm.page-cluster" = lib.mkForce 0;
|
||||
"vm.dirty_ratio" = lib.mkForce 10;
|
||||
"vm.dirty_background_ratio" = lib.mkForce 5;
|
||||
|
||||
"net.core.somaxconn" = lib.mkForce 8192;
|
||||
"net.core.netdev_max_backlog" = lib.mkForce 16384;
|
||||
"net.ipv4.tcp_slow_start_after_idle" = lib.mkForce 0;
|
||||
"net.ipv4.tcp_rmem" = lib.mkForce "4096 1048576 2097152";
|
||||
"net.ipv4.tcp_wmem" = lib.mkForce "4096 65536 16777216";
|
||||
};
|
||||
|
||||
# faster boot
|
||||
systemd.services.NetworkManager-wait-online.enable = lib.mkForce false;
|
||||
systemd.services.systemd-networkd-wait-online.enable = lib.mkForce false;
|
||||
|
||||
services.irqbalance.enable = true;
|
||||
|
||||
nix.settings = {
|
||||
max-jobs = "auto";
|
||||
cores = 0;
|
||||
log-lines = 25;
|
||||
};
|
||||
};
|
||||
}
|
||||
82
modules/system/podman.nix
Normal file
82
modules/system/podman.nix
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
# Podman Module
|
||||
# Provides: Rootless Podman container runtime with Docker compatibility
|
||||
#
|
||||
# Usage:
|
||||
# myModules.podman = {
|
||||
# enable = true;
|
||||
# dockerCompat = true; # default: true
|
||||
# enableDns = true; # default: true
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.podman;
|
||||
in
|
||||
{
|
||||
options.myModules.podman = {
|
||||
enable = lib.mkEnableOption "Podman container runtime";
|
||||
|
||||
dockerCompat = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Enable Docker CLI compatibility (docker alias)";
|
||||
};
|
||||
|
||||
enableDns = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Enable DNS for container networking";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
virtualisation = {
|
||||
containers.enable = true;
|
||||
podman = {
|
||||
enable = true;
|
||||
dockerCompat = cfg.dockerCompat;
|
||||
defaultNetwork.settings.dns_enabled = cfg.enableDns;
|
||||
};
|
||||
oci-containers.backend = "podman";
|
||||
};
|
||||
|
||||
environment.systemPackages = [ pkgs.podman ];
|
||||
|
||||
# Ensure required kernel modules are loaded at boot for locked kernel
|
||||
boot.kernelModules = [
|
||||
"veth" # Required for netavark to create container network interfaces
|
||||
"bridge"
|
||||
"br_netfilter"
|
||||
"tap"
|
||||
"tun"
|
||||
"loop"
|
||||
"nft_ct"
|
||||
"nft_nat"
|
||||
"nft_chain_nat"
|
||||
"nft_compat"
|
||||
"nft_masq"
|
||||
"nft_reject_inet"
|
||||
"nft_reject_ipv4"
|
||||
"nft_reject_ipv6"
|
||||
"nft_fib_inet"
|
||||
# IPTables extensions commonly used by Podman/Docker
|
||||
"xt_conntrack"
|
||||
"xt_comment"
|
||||
"xt_addrtype"
|
||||
"xt_mark"
|
||||
"xt_multiport"
|
||||
"xt_nat"
|
||||
|
||||
# NAT/Masquerade support
|
||||
"xt_MASQUERADE"
|
||||
"iptable_nat"
|
||||
"iptable_filter"
|
||||
];
|
||||
};
|
||||
}
|
||||
199
modules/system/prismlauncher-sandboxed.nix
Normal file
199
modules/system/prismlauncher-sandboxed.nix
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
# PrismLauncher Sandboxed with nix-bwrapper
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
|
||||
|
||||
# Libraries required by Minecraft natives (LWJGL), various mods,
|
||||
# and the Microsoft authentication flow (NSS/NSPR).
|
||||
runtimeLibs = with pkgs; [
|
||||
glib
|
||||
libgbm
|
||||
libglvnd
|
||||
nspr
|
||||
nss
|
||||
gtk3
|
||||
alsa-lib
|
||||
libpulseaudio
|
||||
udev
|
||||
cups
|
||||
mesa
|
||||
expat
|
||||
libdrm
|
||||
libxkbcommon
|
||||
dbus
|
||||
xorg.libXcomposite
|
||||
xorg.libXdamage
|
||||
xorg.libXext
|
||||
xorg.libXfixes
|
||||
xorg.libXrandr
|
||||
xorg.libxcb
|
||||
pango
|
||||
cairo
|
||||
at-spi2-atk
|
||||
at-spi2-core
|
||||
libxml2
|
||||
libxml2
|
||||
xorg.libXScrnSaver
|
||||
glfw
|
||||
# Kvantum Style Plugins
|
||||
# Kvantum Style Plugins
|
||||
kdePackages.qtstyleplugin-kvantum
|
||||
];
|
||||
in
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
prismlauncher-sandboxed = bwrapperPkgs.mkBwrapper {
|
||||
app = {
|
||||
id = "org.prismlauncher.PrismLauncher";
|
||||
package =
|
||||
inputs.prismlauncher.packages.${pkgs.stdenv.hostPlatform.system}.prismlauncher.overrideAttrs
|
||||
(old: {
|
||||
pname = "prismlauncher";
|
||||
version = old.version or "9.1";
|
||||
buildInputs = (old.buildInputs or [ ]) ++ runtimeLibs ++ [ pkgs.mimalloc ];
|
||||
|
||||
qtWrapperArgs = (old.qtWrapperArgs or [ ]) ++ [
|
||||
"--set MIMALLOC_PATH ${pkgs.mimalloc}/lib/libmimalloc.so"
|
||||
"--prefix LD_PRELOAD : ${pkgs.mimalloc}/lib/libmimalloc.so"
|
||||
"--prefix LD_LIBRARY_PATH : ${lib.makeLibraryPath runtimeLibs}"
|
||||
"--prefix QT_PLUGIN_PATH : ${pkgs.kdePackages.qtstyleplugin-kvantum}/lib/qt6/plugins"
|
||||
];
|
||||
});
|
||||
|
||||
env = {
|
||||
# Propagate XDG_DATA_DIRS so themes/icons can be found
|
||||
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
|
||||
GTK_THEME = "catppuccin-mocha-mauve-standard";
|
||||
QT_QPA_PLATFORMTHEME = "gtk3";
|
||||
QT_STYLE_OVERRIDE = "kvantum";
|
||||
BROWSER = "firefox";
|
||||
};
|
||||
};
|
||||
|
||||
sockets.x11 = true; # Old versions of minecraft require X11, and forge still doesnt care its breaking wayland.
|
||||
sockets.wayland = true;
|
||||
|
||||
flatpak.enable = false;
|
||||
|
||||
fhsenv.opts = {
|
||||
unshareUser = true;
|
||||
unshareUts = false;
|
||||
unshareCgroup = false;
|
||||
unsharePid = false;
|
||||
unshareNet = false;
|
||||
unshareIpc = false;
|
||||
};
|
||||
|
||||
fhsenv.bwrap.baseArgs = lib.mkForce [
|
||||
"--new-session"
|
||||
"--proc /proc"
|
||||
"--dev /dev"
|
||||
"--dev-bind /dev/dri /dev/dri"
|
||||
"--tmpfs /home"
|
||||
"--tmpfs /tmp"
|
||||
"--tmpfs /run"
|
||||
"--dir /run/user"
|
||||
"--dir /run/user/${toString config.users.users.ashie.uid}"
|
||||
# Bind ro system paths commonly needed
|
||||
"--ro-bind-try /run/current-system /run/current-system"
|
||||
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
|
||||
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
|
||||
"--dir /run/systemd/resolve"
|
||||
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
|
||||
"--ro-bind /run/dbus /run/dbus"
|
||||
];
|
||||
|
||||
mounts = {
|
||||
read = [
|
||||
"$HOME/.config/fontconfig"
|
||||
"$HOME/.local/share/fonts"
|
||||
"$HOME/.icons"
|
||||
"$HOME/.themes"
|
||||
"$HOME/.local/share/themes"
|
||||
"$HOME/.config/qt6ct"
|
||||
"$HOME/.config/Kvantum"
|
||||
"$HOME/.config/MangoHud"
|
||||
"$HOME/Downloads"
|
||||
];
|
||||
readWrite = [
|
||||
"$HOME/.local/share/PrismLauncher"
|
||||
"$HOME/.cache/PrismLauncher"
|
||||
];
|
||||
};
|
||||
|
||||
dbus.enable = false;
|
||||
|
||||
script.preCmds.stage2 =
|
||||
let
|
||||
jvmArgs = "-XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+AlwaysActAsServerClassMachine -XX:+AlwaysPreTouch -XX:+DisableExplicitGC -XX:+UseNUMA -XX:NmethodSweepActivity=1 -XX:ReservedCodeCacheSize=400M -XX:NonNMethodCodeHeapSize=12M -XX:ProfiledCodeHeapSize=194M -XX:NonProfiledCodeHeapSize=194M -XX:-DontCompileHugeMethods -XX:MaxNodeLimit=240000 -XX:NodeLimitFudgeFactor=8000 -XX:+UseVectorCmov -XX:+PerfDisableSharedMem -XX:+UseFastUnorderedTimeStamps -XX:+UseCriticalJavaThreadPriority -XX:ThreadPriorityPolicy=1 -XX:AllocatePrefetchStyle=3 -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGuaranteedGCInterval=1000000 -XX:AllocatePrefetchStyle=1 -XX:ConcGCThreads=4";
|
||||
glfwPath = "${pkgs.glfw}/lib/libglfw.so.3";
|
||||
|
||||
dbusScript = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
|
||||
appId = "org.prismlauncher.PrismLauncher";
|
||||
proxyArgs = [
|
||||
"--filter"
|
||||
''--talk="org.freedesktop.portal.*"''
|
||||
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
|
||||
''--talk="org.freedesktop.Notifications"''
|
||||
''--own="org.prismlauncher.PrismLauncher"''
|
||||
''--own="org.prismlauncher.PrismLauncher.*"''
|
||||
];
|
||||
};
|
||||
in
|
||||
''
|
||||
${dbusScript}
|
||||
|
||||
# Force Configs (JVM Args + GLFW)
|
||||
cfg="$HOME/.local/share/PrismLauncher/prismlauncher.cfg"
|
||||
if [ -f "$cfg" ]; then
|
||||
# JVM Args
|
||||
if ${pkgs.gnugrep}/bin/grep -q "^JvmArgs=" "$cfg"; then
|
||||
${pkgs.gnused}/bin/sed -i "s|^JvmArgs=.*|JvmArgs=${jvmArgs}|" "$cfg"
|
||||
else
|
||||
if ${pkgs.gnugrep}/bin/grep -q "^\[General\]" "$cfg"; then
|
||||
${pkgs.gnused}/bin/sed -i "/^\[General\]/a JvmArgs=${jvmArgs}" "$cfg"
|
||||
else
|
||||
echo "JvmArgs=${jvmArgs}" >> "$cfg"
|
||||
fi
|
||||
fi
|
||||
|
||||
# GLFW Settings
|
||||
# 1. CustomGLFWPath
|
||||
if ${pkgs.gnugrep}/bin/grep -q "^CustomGLFWPath=" "$cfg"; then
|
||||
${pkgs.gnused}/bin/sed -i "s|^CustomGLFWPath=.*|CustomGLFWPath=${glfwPath}|" "$cfg"
|
||||
else
|
||||
echo "CustomGLFWPath=${glfwPath}" >> "$cfg"
|
||||
fi
|
||||
|
||||
# 2. UseNativeGLFW
|
||||
if ${pkgs.gnugrep}/bin/grep -q "^UseNativeGLFW=" "$cfg"; then
|
||||
${pkgs.gnused}/bin/sed -i "s|^UseNativeGLFW=.*|UseNativeGLFW=true|" "$cfg"
|
||||
else
|
||||
echo "UseNativeGLFW=true" >> "$cfg"
|
||||
fi
|
||||
fi
|
||||
'';
|
||||
|
||||
fhsenv.bwrap.additionalArgs = [
|
||||
# D-Bus proxy
|
||||
''--bind "$XDG_RUNTIME_DIR/app/org.prismlauncher.PrismLauncher/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
|
||||
# Wayland socket
|
||||
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
|
||||
|
||||
# PipeWire + Pulse (PipeWire hosts both)
|
||||
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
|
||||
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
|
||||
];
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
135
modules/system/ryubing-sandboxed.nix
Normal file
135
modules/system/ryubing-sandboxed.nix
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
|
||||
|
||||
appId = "org.ryubing.Ryubing";
|
||||
in
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
ryubing-sandboxed = bwrapperPkgs.mkBwrapper {
|
||||
app = {
|
||||
package = pkgs.ryubing;
|
||||
id = appId;
|
||||
env = {
|
||||
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
|
||||
# Ryubing uses Avalonia which works better with X11
|
||||
AVALONIA_SCREEN_SCALE_FACTOR = "1";
|
||||
};
|
||||
};
|
||||
|
||||
# Enable X11 and Wayland
|
||||
sockets.x11 = true;
|
||||
sockets.wayland = true;
|
||||
|
||||
# Disable Flatpak emulation
|
||||
flatpak.enable = false;
|
||||
|
||||
fhsenv.opts = {
|
||||
unshareUser = true;
|
||||
unshareUts = false;
|
||||
unshareCgroup = false;
|
||||
unsharePid = false;
|
||||
unshareNet = false; # Need network for online features
|
||||
unshareIpc = false;
|
||||
};
|
||||
|
||||
fhsenv.bwrap.baseArgs = lib.mkForce [
|
||||
"--new-session"
|
||||
"--proc /proc"
|
||||
"--dev /dev"
|
||||
"--dev-bind /dev/dri /dev/dri" # GPU acceleration
|
||||
"--dev-bind /dev/shm /dev/shm" # Shared memory
|
||||
"--dev-bind-try /dev/uinput /dev/uinput" # Controller support
|
||||
"--dev-bind-try /dev/input /dev/input"
|
||||
"--tmpfs /home"
|
||||
"--tmpfs /tmp"
|
||||
"--tmpfs /run"
|
||||
"--dir /run/user"
|
||||
"--dir /run/user/${toString config.users.users.ashie.uid}"
|
||||
# Fix for amdgpu.ids missing - use tmpfs so mkdir can succeed
|
||||
"--tmpfs /usr/share"
|
||||
"--ro-bind ${pkgs.libdrm}/share/libdrm /usr/share/libdrm"
|
||||
# System paths
|
||||
"--ro-bind /sys /sys"
|
||||
"--ro-bind-try /run/current-system /run/current-system"
|
||||
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
|
||||
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
|
||||
"--ro-bind-try /nix/store /nix/store"
|
||||
"--dir /run/systemd/resolve"
|
||||
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
|
||||
# udev for controller hotplug
|
||||
"--ro-bind-try /run/udev /run/udev"
|
||||
];
|
||||
|
||||
# Disable built-in DBus module (invokes bwrap without --unshare-user)
|
||||
dbus.enable = false;
|
||||
|
||||
# Manually set up DBus proxy with --unshare-user (session bus only)
|
||||
# Also create required directories before bwrap runs
|
||||
script.preCmds.stage2 = ''
|
||||
# Create directories that bwrap will bind
|
||||
# Note: Ryubing still uses Ryujinx config paths
|
||||
mkdir -p "$HOME/.config/Ryujinx/system"
|
||||
mkdir -p "$HOME/.config/Ryujinx/bis/system/Contents/registered"
|
||||
mkdir -p "$HOME/.local/share/Ryujinx"
|
||||
mkdir -p "$HOME/Games/Switch"
|
||||
''
|
||||
+ (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
|
||||
inherit appId;
|
||||
enableSystemBus = false;
|
||||
proxyArgs = [
|
||||
"--filter"
|
||||
''--talk="org.freedesktop.portal.Desktop"''
|
||||
''--talk="org.freedesktop.portal.OpenURI"''
|
||||
''--talk="org.freedesktop.portal.FileChooser"''
|
||||
''--talk="org.freedesktop.portal.*"''
|
||||
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
|
||||
''--talk="org.freedesktop.Notifications"''
|
||||
''--talk="org.freedesktop.ScreenSaver"''
|
||||
''--talk="org.kde.StatusNotifierWatcher"''
|
||||
''--talk="org.kde.KWin"''
|
||||
''--talk="org.gnome.Mutter.DisplayConfig"''
|
||||
''--talk="org.freedesktop.secrets"''
|
||||
|
||||
''--talk="com.feralinteractive.GameMode"''
|
||||
''--own="${appId}"''
|
||||
''--own="${appId}.*"''
|
||||
];
|
||||
};
|
||||
|
||||
fhsenv.bwrap.additionalArgs = [
|
||||
# D-Bus session proxy only
|
||||
''--bind "$XDG_RUNTIME_DIR/app/${appId}/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
|
||||
# Wayland socket
|
||||
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
|
||||
|
||||
# PipeWire + Pulse
|
||||
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
|
||||
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
|
||||
|
||||
# Manual mounts for data persistence
|
||||
"--ro-bind-try $HOME/.config/kdedefaults $HOME/.config/kdedefaults"
|
||||
"--ro-bind-try $HOME/.local/share/color-schemes $HOME/.local/share/color-schemes"
|
||||
"--ro-bind-try $HOME/.config/fontconfig $HOME/.config/fontconfig"
|
||||
"--ro-bind-try $HOME/.local/share/fonts $HOME/.local/share/fonts"
|
||||
"--ro-bind-try $HOME/.icons $HOME/.icons"
|
||||
"--ro-bind-try $HOME/.themes $HOME/.themes"
|
||||
"--ro-bind-try $HOME/.config/MangoHud $HOME/.config/MangoHud"
|
||||
# Read-write mounts
|
||||
"--bind $HOME/Games/Switch $HOME/Games/Switch"
|
||||
"--bind $HOME/.config/Ryujinx $HOME/.config/Ryujinx"
|
||||
"--bind $HOME/.local/share/Ryujinx $HOME/.local/share/Ryujinx"
|
||||
];
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
91
modules/system/sandbox-utils.nix
Normal file
91
modules/system/sandbox-utils.nix
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
{ pkgs, lib }:
|
||||
{
|
||||
# Generates the shell script content to set up xdg-dbus-proxy inside a bwrap user namespace.
|
||||
# This is used for sandboxed apps that run with unshareUser = true.
|
||||
mkDbusProxyScript = {
|
||||
appId, # Unique ID for the app (e.g. "org.mozilla.firefox")
|
||||
proxyArgs, # Arguments for xdg-dbus-proxy (session bus). Can be string or list.
|
||||
socketPath ? "$XDG_RUNTIME_DIR/app/${appId}/bus",
|
||||
upstreamSocket ? "$XDG_RUNTIME_DIR/bus",
|
||||
enableSystemBus ? false,
|
||||
systemProxyArgs ? "", # Arguments for xdg-dbus-proxy (system bus). Can be string or list.
|
||||
systemSocketPath ? "$XDG_RUNTIME_DIR/app/${appId}/bus_system",
|
||||
systemUpstreamSocket ? "/run/dbus/system_bus_socket"
|
||||
}:
|
||||
let
|
||||
bwrap = "${pkgs.bubblewrap}/bin/bwrap";
|
||||
dbusProxy = "${pkgs.xdg-dbus-proxy}/bin/xdg-dbus-proxy";
|
||||
coreutils = "${pkgs.coreutils}/bin";
|
||||
|
||||
# Helper to normalize args (support list or string)
|
||||
normalizeArgs = args: if builtins.isList args then lib.concatStringsSep " " args else args;
|
||||
pArgs = normalizeArgs proxyArgs;
|
||||
sArgs = normalizeArgs systemProxyArgs;
|
||||
|
||||
# Helper to generate the function definition
|
||||
# We bind XDG_RUNTIME_DIR to allow creating the socket.
|
||||
# We optionally bind /run/dbus for the system bus socket.
|
||||
mkProxyFunc = name: upstream: sock: args: bindSystem: ''
|
||||
${name}() {
|
||||
${coreutils}/mkdir -p "$(${coreutils}/dirname "${sock}")"
|
||||
${bwrap} \
|
||||
--unshare-user \
|
||||
--dev /dev \
|
||||
--proc /proc \
|
||||
--new-session \
|
||||
--ro-bind /nix/store /nix/store \
|
||||
--bind "$XDG_RUNTIME_DIR" "$XDG_RUNTIME_DIR" \
|
||||
${if bindSystem then "--ro-bind /run/dbus /run/dbus" else ""} \
|
||||
--die-with-parent \
|
||||
--clearenv \
|
||||
-- \
|
||||
${dbusProxy} "unix:path=${upstream}" "${sock}" ${args}
|
||||
}
|
||||
'';
|
||||
|
||||
sessionFunc = mkProxyFunc "set_up_dbus_proxy" upstreamSocket socketPath pArgs false;
|
||||
systemFunc = if enableSystemBus
|
||||
then mkProxyFunc "set_up_system_dbus_proxy" systemUpstreamSocket systemSocketPath sArgs true
|
||||
else "";
|
||||
|
||||
waitLoop = ''
|
||||
# Wait for socket(s) with fail-fast check
|
||||
for i in $(${coreutils}/seq 1 50);
|
||||
do
|
||||
# Check if processes are still running
|
||||
if ! kill -0 "$PID_SESSION" 2>/dev/null;
|
||||
then
|
||||
echo "Error: Session D-Bus proxy (PID $PID_SESSION) died unexpectedly." >&2
|
||||
exit 1
|
||||
fi
|
||||
${if enableSystemBus then ''
|
||||
if ! kill -0 "$PID_SYSTEM" 2>/dev/null;
|
||||
then
|
||||
echo "Error: System D-Bus proxy (PID $PID_SYSTEM) died unexpectedly." >&2
|
||||
exit 1
|
||||
fi
|
||||
'' else ""}
|
||||
|
||||
# Check for sockets
|
||||
if [ -S "${socketPath}" ]${if enableSystemBus then " && [ -S \"${systemSocketPath}\" ]" else ""};
|
||||
then
|
||||
break
|
||||
fi
|
||||
${coreutils}/sleep 0.02
|
||||
done
|
||||
'';
|
||||
|
||||
in ''
|
||||
${sessionFunc}
|
||||
${systemFunc}
|
||||
|
||||
set_up_dbus_proxy &
|
||||
PID_SESSION=$!
|
||||
${if enableSystemBus then ''
|
||||
set_up_system_dbus_proxy &
|
||||
PID_SYSTEM=$!
|
||||
'' else ""}
|
||||
|
||||
${waitLoop}
|
||||
'';
|
||||
}
|
||||
39
modules/system/sched-ext.nix
Normal file
39
modules/system/sched-ext.nix
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.sched-ext;
|
||||
in
|
||||
{
|
||||
options.myModules.sched-ext = {
|
||||
enable = lib.mkEnableOption "sched-ext (scx) schedulers";
|
||||
|
||||
scheduler = lib.mkOption {
|
||||
type = lib.types.enum [ "scx_rustland" "scx_lavd" "scx_rusty" "scx_bpfland" ];
|
||||
default = "scx_lavd";
|
||||
description = "The scx scheduler to run.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
# Install the schedulers
|
||||
environment.systemPackages = [ pkgs.scx ];
|
||||
|
||||
# Systemd service to run the scheduler
|
||||
systemd.services.scx = {
|
||||
description = "sched-ext scheduler (${cfg.scheduler})";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
path = [ pkgs.scx ];
|
||||
serviceConfig = {
|
||||
ExecStart = "${pkgs.scx}/bin/${cfg.scheduler}";
|
||||
Restart = "always";
|
||||
RestartSec = "3";
|
||||
Nice = -20;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
38
modules/system/secure-boot.nix
Normal file
38
modules/system/secure-boot.nix
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.myModules.secureBoot;
|
||||
in
|
||||
{
|
||||
imports = [ inputs.lanzaboote.nixosModules.lanzaboote ];
|
||||
|
||||
options.myModules.secureBoot = {
|
||||
enable = mkEnableOption "Secure Boot with Lanzaboote";
|
||||
|
||||
pkiBundle = mkOption {
|
||||
type = types.path;
|
||||
default = "/var/lib/sbctl";
|
||||
description = "Path to the PKI bundle directory created by sbctl";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
# Lanzaboote replaces systemd-boot
|
||||
boot.loader.systemd-boot.enable = mkForce false;
|
||||
|
||||
boot.lanzaboote = {
|
||||
enable = true;
|
||||
pkiBundle = cfg.pkiBundle;
|
||||
};
|
||||
|
||||
environment.systemPackages = [ pkgs.sbctl ];
|
||||
};
|
||||
}
|
||||
117
modules/system/security.nix
Normal file
117
modules/system/security.nix
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
# Security Hardening Module
|
||||
# Provides: doas (sudo replacement), audit logging, AppArmor, core dump prevention
|
||||
#
|
||||
# Usage:
|
||||
# myModules.security = {
|
||||
# enable = true;
|
||||
# enableAudit = true; # default: true
|
||||
# enableAppArmor = true; # default: true
|
||||
# useDoas = true; # default: true (replaces sudo)
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.security;
|
||||
in
|
||||
{
|
||||
options.myModules.security = {
|
||||
enable = lib.mkEnableOption "security hardening module";
|
||||
|
||||
useDoas = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Replace sudo with doas for privilege escalation";
|
||||
};
|
||||
|
||||
enableAudit = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false; # Disabled: still incompatible with kernel
|
||||
description = "Enable auditd with security-focused rules";
|
||||
};
|
||||
|
||||
enableAppArmor = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Enable AppArmor mandatory access control";
|
||||
};
|
||||
|
||||
enableFail2Ban = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Enable Fail2Ban for SSH and other services";
|
||||
};
|
||||
|
||||
wheelGroup = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "wheel";
|
||||
description = "Group allowed to use doas/sudo";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
# Replace sudo with doas
|
||||
security.sudo.enable = !cfg.useDoas;
|
||||
security.doas.enable = cfg.useDoas;
|
||||
security.doas.extraRules = lib.mkIf cfg.useDoas [
|
||||
{
|
||||
groups = [ cfg.wheelGroup ];
|
||||
keepEnv = false;
|
||||
persist = true;
|
||||
}
|
||||
];
|
||||
|
||||
# Security audit logging
|
||||
security.auditd.enable = cfg.enableAudit;
|
||||
security.audit = lib.mkIf cfg.enableAudit {
|
||||
enable = true;
|
||||
rules = [
|
||||
# Log all execve calls (command execution)
|
||||
"-a exit,always -F arch=b64 -S execve"
|
||||
# Log privilege escalation
|
||||
"-w /etc/shadow -p wa -k shadow"
|
||||
"-w /etc/passwd -p wa -k passwd"
|
||||
"-w /etc/group -p wa -k group"
|
||||
# Watch for kernel module insertion
|
||||
"-a always,exit -F arch=b64 -S init_module -S finit_module -k module_insertion"
|
||||
];
|
||||
};
|
||||
|
||||
# Disable core dumps
|
||||
systemd.coredump.enable = false;
|
||||
|
||||
# AppArmor
|
||||
security.apparmor = lib.mkIf cfg.enableAppArmor {
|
||||
enable = true;
|
||||
packages = with pkgs; [ apparmor-profiles ];
|
||||
};
|
||||
|
||||
# Polkit for privilege management
|
||||
security.polkit.enable = true;
|
||||
|
||||
# Restrict su to wheel group
|
||||
security.pam.services.su.requireWheel = true;
|
||||
|
||||
# Fail2Ban
|
||||
services.fail2ban = lib.mkIf cfg.enableFail2Ban {
|
||||
enable = true;
|
||||
maxretry = 5;
|
||||
bantime = "24h"; # Ban for 24 hours
|
||||
bantime-increment = {
|
||||
enable = true; # Enable exponential backoff
|
||||
factor = "2";
|
||||
maxtime = "168h"; # Max ban time of 1 week
|
||||
};
|
||||
ignoreIP = [
|
||||
"127.0.0.1/8"
|
||||
"10.0.0.0/8"
|
||||
"192.168.0.0/16"
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
118
modules/system/spotify-sandboxed.nix
Normal file
118
modules/system/spotify-sandboxed.nix
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
# Spotify Sandboxed with nix-bwrapper
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
|
||||
in
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
spotify-sandboxed = bwrapperPkgs.mkBwrapper {
|
||||
app = {
|
||||
package = prev.spotify;
|
||||
id = "com.spotify.Client";
|
||||
env = {
|
||||
# Propagate XDG_DATA_DIRS for theming
|
||||
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
|
||||
# Force Wayland if preferred, or rely on auto-detection
|
||||
# DISPLAY variable is handled by sockets.x11/wayland
|
||||
};
|
||||
};
|
||||
|
||||
# Enable X11 and Wayland
|
||||
sockets.x11 = true;
|
||||
sockets.wayland = true;
|
||||
|
||||
# Spotify is not a flatpak ref, so disable flatpak emulation
|
||||
flatpak.enable = false;
|
||||
|
||||
fhsenv.opts = {
|
||||
unshareUser = true;
|
||||
unshareUts = false;
|
||||
unshareCgroup = false;
|
||||
unsharePid = false;
|
||||
unshareNet = false; # Need network for streaming
|
||||
unshareIpc = false;
|
||||
};
|
||||
|
||||
fhsenv.bwrap.baseArgs = lib.mkForce [
|
||||
"--new-session"
|
||||
"--proc /proc"
|
||||
"--dev /dev"
|
||||
"--dev-bind /dev/dri /dev/dri" # GPU acceleration
|
||||
"--tmpfs /home"
|
||||
"--tmpfs /tmp"
|
||||
"--tmpfs /run"
|
||||
"--dir /run/user"
|
||||
"--dir /run/user/${toString config.users.users.ashie.uid}"
|
||||
# System paths
|
||||
"--ro-bind /sys /sys"
|
||||
"--ro-bind-try /run/current-system /run/current-system"
|
||||
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
|
||||
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
|
||||
"--dir /run/systemd/resolve"
|
||||
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
|
||||
# Audio
|
||||
"--ro-bind-try /etc/asound.conf /etc/asound.conf"
|
||||
];
|
||||
|
||||
mounts = {
|
||||
read = [
|
||||
"$HOME/.config/fontconfig"
|
||||
"$HOME/.local/share/fonts"
|
||||
"$HOME/.icons"
|
||||
"$HOME/.themes"
|
||||
"$HOME/.local/share/themes"
|
||||
"$HOME/.config/kdedefaults"
|
||||
"$HOME/.local/share/color-schemes"
|
||||
];
|
||||
readWrite = [
|
||||
"$HOME/.config/spotify"
|
||||
"$HOME/.cache/spotify"
|
||||
"$HOME/.local/share/spotify"
|
||||
];
|
||||
};
|
||||
|
||||
# Disable built-in DBus module (invokes bwrap without --unshare-user)
|
||||
dbus.enable = false;
|
||||
|
||||
# Manually set up DBus proxy with --unshare-user (session bus only)
|
||||
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
|
||||
appId = "com.spotify.Client";
|
||||
enableSystemBus = false;
|
||||
proxyArgs = [
|
||||
"--filter"
|
||||
''--talk="org.freedesktop.portal.*"''
|
||||
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
|
||||
''--talk="org.freedesktop.Notifications"''
|
||||
''--talk="org.freedesktop.ScreenSaver"''
|
||||
''--talk="org.kde.StatusNotifierWatcher"''
|
||||
''--talk="org.gnome.Mutter.DisplayConfig"''
|
||||
''--talk="org.mpris.MediaPlayer2.Player"''
|
||||
''--own="org.mpris.MediaPlayer2.spotify"''
|
||||
''--own="com.spotify.Client"''
|
||||
''--own="com.spotify.Client.*"''
|
||||
];
|
||||
};
|
||||
|
||||
fhsenv.bwrap.additionalArgs = [
|
||||
# D-Bus session proxy only
|
||||
''--bind "$XDG_RUNTIME_DIR/app/com.spotify.Client/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
|
||||
# Wayland socket
|
||||
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
|
||||
|
||||
# PipeWire + Pulse
|
||||
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
|
||||
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
|
||||
];
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
148
modules/system/steam-sandboxed.nix
Normal file
148
modules/system/steam-sandboxed.nix
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
# Steam Sandboxed with nix-bwrapper
|
||||
# Provides a sandboxed Steam with restricted permissions like Lutris
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
|
||||
in
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
steam-sandboxed = bwrapperPkgs.mkBwrapper {
|
||||
app = {
|
||||
package = prev.steam;
|
||||
isFhsenv = true; # Steam uses buildFHSEnv
|
||||
id = "com.valvesoftware.Steam";
|
||||
env = {
|
||||
# Unset LD_PRELOAD to avoid mimalloc crashes
|
||||
LD_PRELOAD = "";
|
||||
# Propagate XDG_DATA_DIRS for theming
|
||||
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
|
||||
# Proton/Wine optimizations
|
||||
PROTON_USE_NTSYNC = 1;
|
||||
XDG_CURRENT_DESKTOP = "niri";
|
||||
XDG_SESSION_TYPE = "wayland";
|
||||
DBUS_SESSION_BUS_ADDRESS = "unix:path=$XDG_RUNTIME_DIR/bus";
|
||||
};
|
||||
};
|
||||
|
||||
# Enable X11 and Wayland
|
||||
sockets.x11 = true;
|
||||
sockets.wayland = true;
|
||||
|
||||
# Disable Flatpak emulation
|
||||
flatpak.enable = false;
|
||||
|
||||
fhsenv = {
|
||||
skipExtraInstallCmds = true; # Steam package is special, don't try to modify it
|
||||
};
|
||||
|
||||
fhsenv.opts = {
|
||||
unshareUser = true;
|
||||
unshareUts = false;
|
||||
unshareCgroup = false;
|
||||
unsharePid = false; # Share PIDs for compatibility
|
||||
unshareNet = false; # Need network for Steam login/downloads
|
||||
unshareIpc = false;
|
||||
};
|
||||
|
||||
fhsenv.bwrap.baseArgs = lib.mkForce [
|
||||
"--new-session"
|
||||
"--proc /proc"
|
||||
"--dev /dev"
|
||||
"--dev-bind /dev/dri /dev/dri" # GPU acceleration
|
||||
"--dev-bind /dev/shm /dev/shm" # Shared memory (required for games)
|
||||
"--dev-bind-try /dev/uinput /dev/uinput" # Controller support
|
||||
"--dev-bind-try /dev/input /dev/input" # Controller/input devices
|
||||
"--dev-bind-try /dev/hidraw0 /dev/hidraw0" # HID devices (controllers)
|
||||
"--dev-bind-try /dev/hidraw1 /dev/hidraw1"
|
||||
"--dev-bind-try /dev/hidraw2 /dev/hidraw2"
|
||||
"--dev-bind-try /dev/hidraw3 /dev/hidraw3"
|
||||
"--tmpfs /home"
|
||||
"--tmpfs /tmp"
|
||||
"--tmpfs /run"
|
||||
"--dir /run/user"
|
||||
"--dir /run/user/${toString config.users.users.ashie.uid}"
|
||||
# System paths
|
||||
"--ro-bind /sys /sys"
|
||||
"--ro-bind-try /run/current-system /run/current-system"
|
||||
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
|
||||
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
|
||||
"--dir /run/systemd/resolve"
|
||||
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
|
||||
"--unsetenv LD_PRELOAD"
|
||||
# udev for controller hotplug
|
||||
"--ro-bind-try /run/udev /run/udev"
|
||||
];
|
||||
|
||||
mounts = {
|
||||
read = [
|
||||
"$HOME/.config/fontconfig"
|
||||
"$HOME/.local/share/fonts"
|
||||
"$HOME/.icons"
|
||||
"$HOME/.themes"
|
||||
"$HOME/.local/share/themes"
|
||||
"$HOME/.config/qt6ct"
|
||||
"$HOME/.config/Kvantum"
|
||||
"$HOME/.config/MangoHud"
|
||||
"$HOME/.config/kdedefaults"
|
||||
"$HOME/.local/share/color-schemes"
|
||||
];
|
||||
readWrite = [
|
||||
"$HOME/Games"
|
||||
"$HOME/.steam"
|
||||
"$HOME/.local/share/Steam"
|
||||
"$HOME/.local/share/umu"
|
||||
"$HOME/.local/share/applications"
|
||||
"$HOME/.local/share/desktop-directories"
|
||||
"$HOME/.local/share/icons"
|
||||
"$HOME/.local/share/Larian Studios"
|
||||
"$HOME/Desktop"
|
||||
];
|
||||
};
|
||||
|
||||
# Disable built-in DBus module (invokes bwrap without --unshare-user)
|
||||
dbus.enable = false;
|
||||
|
||||
# Manually set up DBus proxy with --unshare-user (session bus only)
|
||||
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
|
||||
appId = "com.valvesoftware.Steam";
|
||||
enableSystemBus = false; # No system bus access
|
||||
proxyArgs = [
|
||||
"--filter"
|
||||
''--talk="org.freedesktop.portal.*"''
|
||||
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
|
||||
''--talk="org.freedesktop.Notifications"''
|
||||
''--talk="org.freedesktop.ScreenSaver"''
|
||||
''--talk="org.kde.StatusNotifierWatcher"''
|
||||
''--talk="org.kde.KWin"''
|
||||
''--talk="org.gnome.Mutter.DisplayConfig"''
|
||||
''--talk="org.freedesktop.secrets"''
|
||||
''--talk="com.feralinteractive.GameMode"''
|
||||
''--talk="org.freedesktop.portal.*"''
|
||||
''--own="com.valvesoftware.Steam"''
|
||||
''--own="com.valvesoftware.Steam.*"''
|
||||
];
|
||||
};
|
||||
|
||||
fhsenv.bwrap.additionalArgs = [
|
||||
# D-Bus session proxy only
|
||||
''--bind "$XDG_RUNTIME_DIR/app/com.valvesoftware.Steam/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
|
||||
# Wayland socket
|
||||
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
|
||||
|
||||
# PipeWire + Pulse
|
||||
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
|
||||
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
|
||||
];
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
145
modules/system/vesktop-sandboxed.nix
Normal file
145
modules/system/vesktop-sandboxed.nix
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# Vesktop Sandboxed with nix-bwrapper
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
|
||||
|
||||
# Define specific Vesktop version to avoid build errors from source
|
||||
vesktop-bin = pkgs.stdenv.mkDerivation rec {
|
||||
pname = "vesktop";
|
||||
version = "1.6.3";
|
||||
|
||||
src = pkgs.fetchurl {
|
||||
url = "https://github.com/Vencord/Vesktop/releases/download/v${version}/vesktop_${version}_amd64.deb";
|
||||
sha256 = "0c6k82rb21p0xi6c3xm5zrzbrph1v6x9qg0kmy9zxwv0z9lq47la";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
pkgs.dpkg
|
||||
pkgs.makeWrapper
|
||||
];
|
||||
|
||||
unpackPhase = ''
|
||||
dpkg-deb -x $src .
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p $out
|
||||
cp -r usr/* $out/
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
meta.mainProgram = "vesktop";
|
||||
};
|
||||
in
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
vesktop-sandboxed = bwrapperPkgs.mkBwrapper {
|
||||
app = {
|
||||
package = vesktop-bin;
|
||||
id = "dev.vencord.Vesktop";
|
||||
env = {
|
||||
# Propagate XDG_DATA_DIRS for theming
|
||||
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
|
||||
# Force Wayland
|
||||
NIXOS_OZONE_WL = "1";
|
||||
};
|
||||
};
|
||||
|
||||
# Enable X11 and Wayland
|
||||
sockets.x11 = true;
|
||||
sockets.wayland = true;
|
||||
|
||||
# Disable flatpak emulation
|
||||
flatpak.enable = false;
|
||||
|
||||
fhsenv.opts = {
|
||||
unshareUser = true;
|
||||
unshareUts = false;
|
||||
unshareCgroup = false;
|
||||
unsharePid = false;
|
||||
unshareNet = false; # Need network for Discord
|
||||
unshareIpc = false;
|
||||
};
|
||||
|
||||
fhsenv.bwrap.baseArgs = lib.mkForce [
|
||||
"--new-session"
|
||||
"--proc /proc"
|
||||
"--dev /dev"
|
||||
"--dev-bind /dev/dri /dev/dri" # GPU acceleration
|
||||
"--tmpfs /home"
|
||||
"--tmpfs /tmp"
|
||||
"--tmpfs /run"
|
||||
"--dir /run/user"
|
||||
"--dir /run/user/${toString config.users.users.ashie.uid}"
|
||||
# System paths
|
||||
"--ro-bind /sys /sys"
|
||||
"--ro-bind-try /run/current-system /run/current-system"
|
||||
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
|
||||
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
|
||||
"--dir /run/systemd/resolve"
|
||||
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
|
||||
# Audio
|
||||
"--ro-bind-try /etc/asound.conf /etc/asound.conf"
|
||||
];
|
||||
|
||||
mounts = {
|
||||
read = [
|
||||
"$HOME/.config/fontconfig"
|
||||
"$HOME/.local/share/fonts"
|
||||
"$HOME/.icons"
|
||||
"$HOME/.themes"
|
||||
"$HOME/.local/share/themes"
|
||||
"$HOME/.config/kdedefaults"
|
||||
"$HOME/.local/share/color-schemes"
|
||||
];
|
||||
readWrite = [
|
||||
"$HOME/.config/vesktop"
|
||||
"$HOME/Downloads"
|
||||
];
|
||||
};
|
||||
|
||||
# Disable built-in DBus module (invokes bwrap without --unshare-user)
|
||||
dbus.enable = false;
|
||||
|
||||
# Manually set up DBus proxy with --unshare-user (session bus only)
|
||||
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
|
||||
appId = "dev.vencord.Vesktop";
|
||||
enableSystemBus = false;
|
||||
proxyArgs = [
|
||||
"--filter"
|
||||
''--talk="org.freedesktop.portal.*"''
|
||||
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
|
||||
''--talk="org.freedesktop.Notifications"''
|
||||
''--talk="org.freedesktop.ScreenSaver"''
|
||||
''--talk="org.kde.StatusNotifierWatcher"''
|
||||
''--talk="org.gnome.Mutter.DisplayConfig"''
|
||||
''--talk="com.canonical.AppMenu.Registrar"''
|
||||
''--own="dev.vencord.Vesktop"''
|
||||
''--own="dev.vencord.Vesktop.*"''
|
||||
];
|
||||
};
|
||||
|
||||
fhsenv.bwrap.additionalArgs = [
|
||||
# D-Bus session proxy only
|
||||
''--bind "$XDG_RUNTIME_DIR/app/dev.vencord.Vesktop/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
|
||||
# Wayland socket
|
||||
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
|
||||
|
||||
# PipeWire + Pulse
|
||||
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
|
||||
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
|
||||
];
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
61
scripts/data_generator/README.md
Normal file
61
scripts/data_generator/README.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# Synthetic Training Data Generator
|
||||
|
||||
This tool generates high-quality synthetic training data for fine-tuning LLMs using an OpenAI-compatible API. Designed for roleplay data with a strict style: **Obtuse, Passionate, Absurd** (includes mature themes).
|
||||
|
||||
## Current Status (2024-12-14)
|
||||
|
||||
**ISSUE**: The script is getting intermittent HTTP 400 and 429 errors from the API.
|
||||
|
||||
- **429 errors**: Quota exhausted on rotating keys (handled by key rotation)
|
||||
- **400 errors**: Need to add retry logic to handle transient failures
|
||||
|
||||
**TODO for next session**:
|
||||
1. Add retry logic with exponential backoff to `generate_training_data.py`
|
||||
2. Detect when error messages are returned as successful content (the proxy sometimes returns errors inside 200 responses)
|
||||
3. Consider filtering out responses that start with `错误:` (Chinese for "Error:")
|
||||
|
||||
## Structure
|
||||
|
||||
- `generate_training_data.py`: Main script that processes character cards and generates multi-turn conversations
|
||||
- `.env`: API configuration (API_KEY, MODEL_NAME, BASE_URL)
|
||||
- `chars/`: Directory containing character definition files (chara_card_v2 JSON format)
|
||||
- `training_data.json`: Output file with generated conversations
|
||||
- `GEMINI.md`: Session memory file with full context history
|
||||
|
||||
## Setup
|
||||
|
||||
1. **Configure API** - Edit `.env`:
|
||||
```ini
|
||||
API_KEY=your_api_key
|
||||
MODEL_NAME=claude-opus-4-5-thinking
|
||||
BASE_URL=http://127.0.0.1:8045/v1
|
||||
```
|
||||
|
||||
2. **Run on NixOS**:
|
||||
```bash
|
||||
nix-shell -p python3Packages.python-dotenv python3Packages.requests python3Packages.openai --run "python generate_training_data.py"
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
1. Loads character cards from `chars/*.json`
|
||||
2. Uses an enforced "GameMaster" system prompt (see `ENFORCED_SYSTEM_PROMPT` in script)
|
||||
3. For each character:
|
||||
- Uses the character's `first_mes` as the initial assistant message
|
||||
- Generates 5 turns of User ↔ Character interaction
|
||||
- User responses are generated by a "User Simulator" prompt
|
||||
- Character responses use the full system prompt + character description
|
||||
4. Saves incrementally to `training_data.json`
|
||||
|
||||
## Key Code Sections
|
||||
|
||||
- **Lines 137-197**: The `ENFORCED_SYSTEM_PROMPT` - detailed roleplay instructions
|
||||
- **Lines 38-82**: `generate_user_response()` - simulates user input
|
||||
- **Lines 84-107**: `generate_character_response()` - generates character replies
|
||||
- **Error handling**: Currently catches `APIStatusError` but needs retry logic
|
||||
|
||||
## API Notes
|
||||
|
||||
- The local endpoint at `127.0.0.1:8045` is a proxy with rotating API keys
|
||||
- Thinking models (`claude-*-thinking`) may have special requirements
|
||||
- Error responses sometimes come back as 200 with error text in content
|
||||
310
scripts/data_generator/generate_training_data.py
Normal file
310
scripts/data_generator/generate_training_data.py
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
import os
|
||||
import json
|
||||
import glob
|
||||
import time
|
||||
from dotenv import load_dotenv
|
||||
from openai import OpenAI
|
||||
import openai
|
||||
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
API_KEY = os.getenv("API_KEY", "sk-dummy")
|
||||
BASE_URL = os.getenv("BASE_URL", "http://127.0.0.1:8045/v1")
|
||||
MODEL_NAME = os.getenv("MODEL_NAME", "gpt-3.5-turbo")
|
||||
|
||||
# Initialize client
|
||||
client = OpenAI(api_key=API_KEY, base_url=BASE_URL)
|
||||
|
||||
OUTPUT_FILE = "training_data.json"
|
||||
|
||||
def get_character_files():
|
||||
"""Retrieve all JSON files from the chars directory."""
|
||||
return glob.glob("chars/*.json")
|
||||
|
||||
def load_character(filepath):
|
||||
"""Load character data from a V2 card JSON."""
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
# Handle different JSON structures (V1 vs V2 card)
|
||||
if 'data' in data:
|
||||
return data['data']
|
||||
return data
|
||||
except Exception as e:
|
||||
print(f"Error loading {filepath}: {e}")
|
||||
return None
|
||||
|
||||
def generate_user_response(history, scenario, char_name):
|
||||
"""
|
||||
Generate a synthetic User response based on the conversation history.
|
||||
This acts as the 'User' simulator.
|
||||
"""
|
||||
|
||||
# Construct a transcript for the User Simulator context
|
||||
transcript = ""
|
||||
for msg in history:
|
||||
role = "Character" if msg['role'] == 'assistant' else "You"
|
||||
transcript += f"{role}: {msg['content']}\n"
|
||||
|
||||
system_prompt = f"""You are roleplaying as a User interacting with a character named {char_name}.
|
||||
|
||||
SCENARIO:
|
||||
{scenario}
|
||||
|
||||
INSTRUCTIONS:
|
||||
1. Read the Transcript below.
|
||||
2. Write the next logical response as the 'User'.
|
||||
3. Keep it short (1-3 sentences), engaging, and natural.
|
||||
4. Do not be repetitive. Respond directly to the Character's last action/dialogue.
|
||||
5. Output ONLY the dialogue/action. No 'User:' prefix.
|
||||
"""
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": f"TRANSCRIPT:\n{transcript}\n\nYour Response:"}
|
||||
]
|
||||
|
||||
# Retry loop for rate limiting
|
||||
max_retries = 5
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
response = client.chat.completions.create(
|
||||
model=MODEL_NAME,
|
||||
messages=messages,
|
||||
temperature=0.9, # Higher temp for variety
|
||||
max_tokens=200
|
||||
)
|
||||
content = response.choices[0].message.content.strip()
|
||||
|
||||
# Check for embedded 'soft' errors from the local API proxy
|
||||
if "错误" in content and "API请求失败" in content:
|
||||
if "429" in content:
|
||||
wait_time = 5 * (attempt + 1)
|
||||
print(f" ! 429 Rate Limit (User Gen - Soft). Retrying in {wait_time}s...")
|
||||
time.sleep(wait_time)
|
||||
continue
|
||||
elif "400" in content:
|
||||
print(f" ! 400 Bad Request (User Gen - Soft): {content[:100]}...")
|
||||
return "*Nods silently*"
|
||||
else:
|
||||
print(f" ! API Error (User Gen - Soft): {content[:100]}...")
|
||||
return "*Nods silently*"
|
||||
|
||||
return content
|
||||
except openai.APIStatusError as e:
|
||||
if e.status_code == 429:
|
||||
wait_time = 5 * (attempt + 1)
|
||||
print(f" ! 429 Rate Limit (User Gen). Retrying in {wait_time}s...")
|
||||
time.sleep(wait_time)
|
||||
continue
|
||||
print(f" ! Error generating user response: HTTP {e.status_code}")
|
||||
print(f" Body: {e.body}")
|
||||
return "*Nods silently*"
|
||||
except Exception as e:
|
||||
print(f" ! Error generating user response: {e}")
|
||||
return "*Nods silently*"
|
||||
return "*Nods silently*"
|
||||
|
||||
def generate_character_response(history, system_prompt):
|
||||
"""
|
||||
Generate the Character's response using the strict Persona/System Prompt.
|
||||
This generates the actual 'training data' target.
|
||||
"""
|
||||
|
||||
# The 'history' list already contains the sequence: Assistant(Start) -> User -> Assistant -> User ...
|
||||
messages = [{"role": "system", "content": system_prompt}] + history
|
||||
|
||||
# Retry loop for rate limiting
|
||||
max_retries = 5
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
response = client.chat.completions.create(
|
||||
model=MODEL_NAME,
|
||||
messages=messages,
|
||||
temperature=0.8,
|
||||
max_tokens=400
|
||||
)
|
||||
content = response.choices[0].message.content.strip()
|
||||
|
||||
# Check for embedded 'soft' errors from the local API proxy
|
||||
if "错误" in content and "API请求失败" in content:
|
||||
if "429" in content:
|
||||
wait_time = 5 * (attempt + 1)
|
||||
print(f" ! 429 Rate Limit (Char Gen - Soft). Retrying in {wait_time}s...")
|
||||
time.sleep(wait_time)
|
||||
continue
|
||||
elif "400" in content:
|
||||
print(f" ! 400 Bad Request (Char Gen - Soft): {content[:100]}...")
|
||||
return "*Stares blankly*"
|
||||
else:
|
||||
print(f" ! API Error (Char Gen - Soft): {content[:100]}...")
|
||||
return "*Stares blankly*"
|
||||
|
||||
return content
|
||||
except openai.APIStatusError as e:
|
||||
if e.status_code == 429:
|
||||
wait_time = 5 * (attempt + 1)
|
||||
print(f" ! 429 Rate Limit (Char Gen). Retrying in {wait_time}s...")
|
||||
time.sleep(wait_time)
|
||||
continue
|
||||
print(f" ! Error generating char response: HTTP {e.status_code}")
|
||||
print(f" Body: {e.body}")
|
||||
return "*Stares blankly*"
|
||||
except Exception as e:
|
||||
print(f" ! Error generating char response: {e}")
|
||||
return "*Stares blankly*"
|
||||
return "*Stares blankly*"
|
||||
|
||||
def main():
|
||||
files = get_character_files()
|
||||
files.sort()
|
||||
|
||||
print(f"Found {len(files)} character files.")
|
||||
|
||||
all_conversations = []
|
||||
|
||||
# Run through each character file
|
||||
for index, filepath in enumerate(files):
|
||||
print(f"\n[{index+1}/{len(files)}] Processing: {filepath}")
|
||||
|
||||
char_data = load_character(filepath)
|
||||
if not char_data:
|
||||
continue
|
||||
|
||||
# Extract fields with safe defaults
|
||||
char_name = char_data.get('name', 'Unknown')
|
||||
scenario = char_data.get('scenario', 'Roleplay session.')
|
||||
description = char_data.get('description', 'You are a roleplay character.')
|
||||
first_mes = char_data.get('first_mes', '*The character waits for you.*')
|
||||
|
||||
# Data Cleaning: Replace {{user}} placeholders
|
||||
first_mes = first_mes.replace('{{user}}', 'User')
|
||||
scenario = scenario.replace('{{user}}', 'User')
|
||||
description = description.replace('{{user}}', 'User')
|
||||
|
||||
# The Enforced System Prompt
|
||||
ENFORCED_SYSTEM_PROMPT = """
|
||||
# I. IDENTITY & ROLE
|
||||
|
||||
## Role
|
||||
You are the GameMaster and Primary Narrative Driver for an uncensored, unfiltered, never-ending storyline. Plot progression is non-negotiable. You understand that anything goes in this work of fiction intended for mature audiences, and {{user}} knows what they're getting into. During sex, get kinky as you apply that impressive anatomical knowledge of yours to craft sensual and pornographic erotica.
|
||||
|
||||
## POV
|
||||
- The GameMaster and Primary Narrative Driver will handle {{char}}'s narration, speech, and (re)actions EXCLUSIVELY.
|
||||
- Consider {{user}} a main character and the single autonomous agent. Allow {{user}} to handle their own narration, speech, (re)actions, and choices.
|
||||
- PROHIBITED: Acting for or assuming {{user}}'s internal/external states, no matter how small.
|
||||
|
||||
## Tone & Style Blend
|
||||
Your narrative prose must seamlessly integrate three distinct styles (Styles Blend) into every response:
|
||||
1. Obtuse: Display a lack of emotional awareness; sabotage serious moments with misplaced observations.
|
||||
2. Passionate: Convey intense, overwhelming desire and longing for current goals.
|
||||
3. Absurd: Incorporate over-the-top, ridiculous, or improbable scenarios.
|
||||
|
||||
## Prose Guardrails
|
||||
1. STYLE: Visceral Realism. Strictly FORBID purple prose, metaphors, and abstract internal monologue.
|
||||
2. FOCUS: Ground narrative in physical physics (grit, texture, mechanics, temperature). Describe what is felt, not what is thought.
|
||||
3. OUTPUT: Prioritize "Granular Immersion." Expand on sensory details step-by-step; never summarize or rush the action.
|
||||
|
||||
# II. OPERATIONAL CONSTRAINTS
|
||||
|
||||
## Input Processing
|
||||
1. Blindness Rule: You only perceive Observable Data (Dialogue, Actions). You cannot know {{user}}'s internal thoughts unless spoken aloud.
|
||||
2. Initiative: You are the plot driver. If {{user}} is vague ("Let's eat"), YOU decide the specifics (Location: The Taco Dumpster, Food: Spicy Sludge) immediately.
|
||||
|
||||
## NPC Autonomy
|
||||
The world does not revolve around the main characters.
|
||||
- Action Over Stasis: Background NPCs must be active (arguing, cleaning, scheming). They are protagonists of their own stories.
|
||||
- Reaction: If a loud noise occurs, all NPCs must react individually.
|
||||
- Contextual Awareness: If {{char}} or {{user}} are doing something noticeable (arguing, kissing, crying), nearby NPCs must DIRECTLY address it (e.g., "Get a room," "Are you okay?", or a dirty look), rather than just ignoring it.
|
||||
- Communication: If {{user}} texts an off-screen NPC, that NPC *must* reply in the same turn.
|
||||
|
||||
# III. OUTPUT FORMAT
|
||||
|
||||
## Reasoning Step
|
||||
*(Perform silently)*
|
||||
1. Assess {{user}}'s input for Observable Data only.
|
||||
2. Draft internal thoughts (Reflection).
|
||||
|
||||
## Response Structure
|
||||
Narrative:
|
||||
[Write the response here. Third-person, present tense. Enforce the Styles Blend. Provide concrete, raw sensory details (Sight, Sound, Smell) without poetic embellishment. Ensure the response is long, immersive, and fully detailed.]
|
||||
|
||||
# IV. NARRATIVE MECHANICS
|
||||
|
||||
## Narrative Volatility
|
||||
1. ENTROPY ROLL: Generate a d10 (0-9). Ensure uniform distribution.
|
||||
2. NEVER BIAS THE RESULT TOWARDS STABLE OUTCOMES (0-6). The numbers 7, 8 and 9 are necessary for plot progression.
|
||||
3. LOGIC GATES (Context-Sensitive):
|
||||
- 0-6 (Stable): Maintain current scene focus.
|
||||
- 7-8 (Interruption): An external character disrupts the scene.
|
||||
* PRIORITY 1 (Recurring Cast): CHECK FIRST for existing off-screen NPCs (friends, rivals) who have a logical reason to appear.
|
||||
* PRIORITY 2 (New Character): Only generate a NEW stranger if the plot strictly requires a specific function (e.g., waiter, delivery person).
|
||||
* BRIDGING CONSTRAINT: The entry must be "Pretext-Driven." The NPC needs a valid excuse to enter (e.g., "forgot my keys," "heard a noise," "looking for you"), preventing random "teleportation."
|
||||
* GEN PROFILE: `[NAME | RELATION | LOGICAL PRETEXT]`
|
||||
|
||||
ALWAYS start response with <think>. Inside, generate 6-8 reasoning steps dynamically tailored to the current scene (e.g., "1. Analyzing Threat: ..."). Close with </think>, then proceed with roleplay.
|
||||
"""
|
||||
|
||||
# Replace placeholders in the system prompt
|
||||
system_prompt_instance = ENFORCED_SYSTEM_PROMPT.replace('{{char}}', char_name).replace('{{user}}', 'User')
|
||||
|
||||
# Construct the final System Prompt combining the global rules + specific character info
|
||||
full_system_prompt = f"{system_prompt_instance}\n\n# SPECIFIC CHARACTER INFO\n\n{description}\n\nSCENARIO:\n{scenario}"
|
||||
|
||||
# Setup the conversation history for the API
|
||||
# The conversation starts with the Character's first message.
|
||||
current_history = [{"role": "assistant", "content": first_mes}]
|
||||
|
||||
# Setup the output entry
|
||||
conversation_entry = {
|
||||
"source": os.path.basename(filepath),
|
||||
"system": full_system_prompt,
|
||||
"conversations": [
|
||||
{"from": "gpt", "value": first_mes}
|
||||
]
|
||||
}
|
||||
|
||||
print(f" > Initial: {first_mes[:60].replace(chr(10), ' ')}...")
|
||||
|
||||
# Generate 5 turns of interaction
|
||||
for turn in range(5):
|
||||
# 1. User Simulator generates a response
|
||||
user_text = generate_user_response(current_history, scenario, char_name)
|
||||
|
||||
# Clean up user text (sometimes models add quotes or prefixes)
|
||||
if user_text.startswith("User:"): user_text = user_text[5:].strip()
|
||||
|
||||
print(f" > Turn {turn+1} User: {user_text[:60].replace(chr(10), ' ')}...")
|
||||
|
||||
current_history.append({"role": "user", "content": user_text})
|
||||
conversation_entry["conversations"].append({
|
||||
"from": "human",
|
||||
"value": user_text
|
||||
})
|
||||
|
||||
# 2. Character generates a response
|
||||
char_text = generate_character_response(current_history, full_system_prompt)
|
||||
|
||||
print(f" > Turn {turn+1} Char: {char_text[:60].replace(chr(10), ' ')}...")
|
||||
|
||||
current_history.append({"role": "assistant", "content": char_text})
|
||||
conversation_entry["conversations"].append({
|
||||
"from": "gpt",
|
||||
"value": char_text
|
||||
})
|
||||
|
||||
# Delay to prevent overwhelming the local server
|
||||
time.sleep(2.0)
|
||||
|
||||
# Append to main list
|
||||
all_conversations.append(conversation_entry)
|
||||
|
||||
# Save incrementally
|
||||
with open(OUTPUT_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump(all_conversations, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f"\nDone! Saved {len(all_conversations)} conversations to {OUTPUT_FILE}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
2
scripts/data_generator/requirements.txt
Normal file
2
scripts/data_generator/requirements.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
openai
|
||||
python-dotenv
|
||||
28
scripts/data_generator/test_api.py
Normal file
28
scripts/data_generator/test_api.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import os
|
||||
from dotenv import load_dotenv
|
||||
from openai import OpenAI
|
||||
import openai
|
||||
|
||||
load_dotenv()
|
||||
|
||||
client = OpenAI(
|
||||
api_key=os.getenv("API_KEY"),
|
||||
base_url=os.getenv("BASE_URL")
|
||||
)
|
||||
|
||||
print(f"DEBUG: BASE_URL='{os.getenv('BASE_URL')}'")
|
||||
print(f"DEBUG: API_KEY='{os.getenv('API_KEY')[:10]}...'")
|
||||
|
||||
try:
|
||||
print("Testing simple 'Hello' prompt...")
|
||||
response = client.chat.completions.create(
|
||||
model=os.getenv("MODEL_NAME"),
|
||||
messages=[{"role": "user", "content": "Hello, are you working?"}],
|
||||
max_tokens=50
|
||||
)
|
||||
print("Success!")
|
||||
print(response.choices[0].message.content)
|
||||
except openai.APIStatusError as e:
|
||||
print(f"Error HTTP {e.status_code}: {e.body}")
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
18
scripts/data_generator/test_url.py
Normal file
18
scripts/data_generator/test_url.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import os
|
||||
from openai import OpenAI
|
||||
|
||||
# Case 1: URL with /v1
|
||||
print("--- Case 1: /v1 ---")
|
||||
client = OpenAI(api_key="sk-test", base_url="http://127.0.0.1:8045/v1")
|
||||
try:
|
||||
client.chat.completions.create(model="test", messages=[{"role":"user","content":"hi"}])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
# Case 2: URL without /v1 (Root)
|
||||
print("\n--- Case 2: Root ---")
|
||||
client = OpenAI(api_key="sk-test", base_url="http://127.0.0.1:8045")
|
||||
try:
|
||||
client.chat.completions.create(model="test", messages=[{"role":"user","content":"hi"}])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
2102
scripts/data_generator/training_data.json
Normal file
2102
scripts/data_generator/training_data.json
Normal file
File diff suppressed because one or more lines are too long
65
scripts/migrate-game-header.sh
Executable file
65
scripts/migrate-game-header.sh
Executable file
|
|
@ -0,0 +1,65 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# CONSTANTS
|
||||
DISK_ID="/dev/disk/by-id/nvme-KINGSTON_SNVS1000G_50026B7784BF8876"
|
||||
HEADER_FILE="/persist/etc/cryptdata.header"
|
||||
MAPPER_NAME="cryptdata"
|
||||
|
||||
echo "========================================================"
|
||||
echo "LUKS DETACHED HEADER MIGRATION"
|
||||
echo "Target Disk: $DISK_ID"
|
||||
echo "Header File: $HEADER_FILE"
|
||||
echo "========================================================"
|
||||
echo ""
|
||||
echo "WARNING: This process isolates the encryption header from the disk."
|
||||
echo "1. If you lose $HEADER_FILE, your data is GONE FOREVER."
|
||||
echo "2. The disk will appear as random noise to anyone inspecting it."
|
||||
echo ""
|
||||
|
||||
if [ -f "$HEADER_FILE" ]; then
|
||||
echo "ERROR: Header file $HEADER_FILE already exists. Aborting to prevent overwrite."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -e "$DISK_ID" ]; then
|
||||
echo "ERROR: Target disk $DISK_ID not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read -p "Type 'DETACH' to proceed with backing up and WIPING the header from the disk: " confirm
|
||||
if [ "$confirm" != "DETACH" ]; then
|
||||
echo "Aborting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "[1/3] Backing up LUKS header..."
|
||||
doas cryptsetup luksHeaderBackup "$DISK_ID" --header-backup-file "$HEADER_FILE"
|
||||
|
||||
if [ ! -s "$HEADER_FILE" ]; then
|
||||
echo "ERROR: Header file creation failed or is empty."
|
||||
exit 1
|
||||
fi
|
||||
echo "Header saved to $HEADER_FILE"
|
||||
doas chmod 600 "$HEADER_FILE"
|
||||
|
||||
echo ""
|
||||
echo "[2/3] Verifying header backup (dry-run open)..."
|
||||
# We try to dump parameters from the file to ensure it's valid
|
||||
if ! doas cryptsetup luksDump "$HEADER_FILE" > /dev/null; then
|
||||
echo "ERROR: The backup header appears invalid. Aborting wipe."
|
||||
rm "$HEADER_FILE"
|
||||
exit 1
|
||||
fi
|
||||
echo "Header backup looks valid."
|
||||
|
||||
echo ""
|
||||
echo "[3/3] WIPING header from physical disk..."
|
||||
# This is the point of no return for the disk's standalone validity
|
||||
doas cryptsetup luksErase "$DISK_ID"
|
||||
|
||||
echo ""
|
||||
echo "SUCCESS! The header is now detached."
|
||||
echo "You must now update your NixOS configuration to use 'header=$HEADER_FILE'."
|
||||
echo "UUIDs on the raw device are now gone. Use the /dev/disk/by-id/ path."
|
||||
39
secrets/secrets.yaml
Normal file
39
secrets/secrets.yaml
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
wireguard_private_key: ENC[AES256_GCM,data:k2M3YwaqyZt5ToK590ooi4m1yuG67alUqqJRiQ9mu8Y07t4b1jq8DovtsUo=,iv:/3P8RwQTnqW41Ig4APs3aTzPnNq1ocXahhNlfsDEYic=,tag:vnUGfPDhz8ZYqIda2quavA==,type:str]
|
||||
wireguard_public_key: ENC[AES256_GCM,data:80Y60Wp+eBuq91WWRO5LmhDCg+bLX7WZpa5/lTLK/RJ1BpwYPzXAe68QUok=,iv:lDLmHvqDUwXty61by0h80BsX5b3H4mAykfrLKLHkL88=,tag:a4o3rZm4PQvK1gJRJvihKg==,type:str]
|
||||
wireguard_addresses: ENC[AES256_GCM,data:vQqHxAiemIXuhI0+6c0DawE=,iv:+qi8nkZmEpBdrePRtc9l/LvAp41h4IDucXfH7oi42Vw=,tag:GIvKywb5T83aBFht7Vc53w==,type:str]
|
||||
wireguard_endpoint_ip: ENC[AES256_GCM,data:fvpWVmC3u52bbJe8,iv:WXY9sRrQQAPZuggm/Rm4cDAGD7yuZB5bwPCnf+9gX6g=,tag:thAR6mG7NMwWT4OK2Ognrg==,type:str]
|
||||
wireguard_endpoint_port: ENC[AES256_GCM,data:wfeiNw==,iv:J0ElbpEHu1S4j2XK9FI4J0jc4QzyZIPCZq+Q+ZOWfeY=,tag:k/JQNRezgIAbTpcSqPkCJA==,type:str]
|
||||
wireguard_preshared_key: ENC[AES256_GCM,data:b0QklQZX8pWyTfDoAZsx+fM/7/D1MA2udxOwJdpsBCbrxVvXc03SoLjLsHI=,iv:hP9YiqpPy/j5LvOUPr48LKgX0cP7xq722Mphvp11lh8=,tag:QKK9sFk8P8nC6LcZzQ2g0w==,type:str]
|
||||
cloudflare_api_key: ENC[AES256_GCM,data:CBlFgYq7+S0Jsga+7zkdcs2aGHZwvQ0bb+Lpwubpo6HACKmYGC9UlA==,iv:5cB6M4GuBoPe9alrvkWPCrNnr1rutNPLN0u1TKrVeOk=,tag:RSjItKjnNV6mCa72fyeJ4Q==,type:str]
|
||||
open_webui_env: ENC[AES256_GCM,data:m5Z/2kZSM13/XsfqktpiuA8D4oQ2/xBWiufK/wznBmUGokOC0znXuaZqVLEDCwxNGpZk4fQSnB/VFV+2xxGd4D1sTmGSCx5fRHM/avcgyKe//GG7LpF4Q3lPNGJReJcumZtzo42CZsQ0hptpH+Ztq0dC8I0TBr7dys7DmFFiYTf5clqglQscb3OQhj8pVtJMiSL+wrCs8I0alZ3aTyqjsSNljGRFj7oTxKD71dx009FOEt5Bvtoec3tx2LMKMJvnZv7jTr9A2uN5Rg7o9mtIoWOMXyrYLDr+fA2U96r3U83f5eF/lKANB11iSSq9LB2mz+uE6pWzX6ehbGxy/UgXO/dlFYBdismvV04i1X9nhAc8Nd0=,iv:UCWCyIaVno2/p2OlRCbKcHKmOauaT5WaMT6dMs7jyIU=,tag:JJZwEs3Tk15Da8WZaYemlA==,type:str]
|
||||
master_api_key: ENC[AES256_GCM,data:2ByHyqPt3Epvy1rJTFAxzf/Wq0TC4uJ5aQ9e/c4J,iv:CUyyC3QMVhxhkhwS3W2vk8lBq8/up52wElOgnYUaCmw=,tag:A2wFwJxOLULHdu2WeAaUQA==,type:str]
|
||||
jwt_secret: ENC[AES256_GCM,data:9C5s3uVf8MjoYavILkRl6jN8NuqyEMGU9REah5u2Q2I51SDvGuikXbag0ZbdPi07t7ta/0Nx+xjLGsIOSYQvCw==,iv:urG3iAAoU+W556xu24WAv/z4P4DsPPaIhTl4zfyi/4I=,tag:rxX8VuePRHFSOlpfNW9NOg==,type:str]
|
||||
#ENC[AES256_GCM,data:u1jbICA5cr4HQJnWGpnGcEorXLtDezYVMg0ZVzkxBnuy9Kb/4xxNWWiK//XvxzEQarHrgN3KoVwqUp43YFNtMuuhiPL9yQvT4fmAfPY/fwndzI/cr3NSy+XeEoDFOQ==,iv:XaHxZASGAi3Z6vHP9PHpTHVJ0GqL+KNd/3bCaUCmC0c=,tag:9b/4Jt2zTXMVHIHR5mBi+w==,type:comment]
|
||||
hashed_password: ENC[AES256_GCM,data:s8ADA68VHHZwGEuYsCw0J0/fNlaZ+7W2PSKckGM/cAcn6kU3IoS0W073FW0IcvIRmiPZrOmW8Wz+ZrnFpa5nuskH9Sr2ZDqkyhk/GlcDR+84RcDL4RCW3Nu/eMVWt0/PpoCbAMgaNgAhgQ==,iv:F2liuSVf1IoSY8Ho6iXEMrnfpBOeVEyQlO7FCZQrqYE=,tag:ZIy/N0TWSCZIvBFtps3W2A==,type:str]
|
||||
pstream_tmdb_api_key: ENC[AES256_GCM,data:bh+jCUiAAyFOpP0EOe/6vDnUQvC9HURG/2oIU2mIbQNo0lNXP5pFy2hE/9ys2DMnnTvopkMLNu4wGyjXuxLMiJtIrq0Wv7kmH0dVmIXeAwQoa8j8QbU2OWuaHambq0sIxY6+UQJo9lhz88uEtys9zDvKDi3sn4JQREvHnCnVljeGQJnXCAALSgFdzhyU2Zy9fX9kyGPkUu7WA1ZBw0JjGjvxSoYcFJZmG5t2J1rRv6gxVcDvOxddkugoZ4Q4d0Fkac+ybAvqGaqy6m/C5FJ5zYUth88wqppB9SUm6XigliRWrUtTT+WrNoRPXusc,iv:uueZ41EkhJOVuC+xfGFORlWKrandTAM3KN/4PxRDwz0=,tag:DbywbqDgWCvsuo21XwDtmQ==,type:str]
|
||||
pstream_jwt_secret: ENC[AES256_GCM,data:jW81qa96mTaCrGvZOZ3t5eVrUk6H78db5Q1kCd1/u9C0oi8MYFzTY7nH4GaYpkhIbfd65ozDOx78y9G71IbFbg==,iv:tPKT4GlttwtVn43ZpDoNMdSFP4AsqAYdL+hRnN88vLY=,tag:wANFe22Vliabmbb9NKsmDA==,type:str]
|
||||
pstream_postgres_password: ENC[AES256_GCM,data:X00fVgOG+1o6NomujERC30ifp3cRrSIQ5LttgIaFXwPHJb0oXRTE//gHnS46hlKSoZYc9Vz1RQ0ecDOW9bmvWQ==,iv:JHfjowWFUS5CVZ0MgFtAjWuZJxmgQFpYYhUBOWb2ob0=,tag:Raj+yNEkojAnHKTN1Ti3uQ==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1g76q4cec3qykmkzrd6f4fxxpafj5fsut4jk7pklweuff97scpuusnwdknu
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTY0kvdC9kME8zV05wbTRX
|
||||
U2VoSnJ2R25LMURQOWlycUp5dmFpQzFMMmxzCmNXczlwbHRjTUpFWktwckdSWFVu
|
||||
bGY1RjFMcTNzck51YXFYOWJXMXNCNU0KLS0tIGdRMUI3Z0R6Y2JPQVFSd1lkVEo5
|
||||
cEJRdFJieDRweEQ0UjZNSVVJSEs4QXMKWDZOp4SLgu3fgxeLs/lHihulJ8fToYxO
|
||||
f7Sc1U6If53nM/1+jZ4fy/c2FEgZKSxWVJAGJOC92MuTlPeYF7F0wA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1gah9fjenac8yd989k4akqxkjj9u684cr5zgdtz4lvu4jhnujuqvqnw87t7
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1NmhuRlNyWjduS2hNcmNT
|
||||
WVdhZThLNHE1ZnpPNGNaTkpsUUkwRDlSRTJzCmltYU81TnBDaGdaWFovYnFHd055
|
||||
UGM3QTNCc21vTXZjR1RoNVNPdlhTclEKLS0tIEQwdFJWN0dycExFeUZySzVwaEc0
|
||||
cVlpL1pxTFN3d1llbEhiNzlCcDV6NzAK6RlVB106woOkrmlINKB5hjoQs8CBfMAI
|
||||
nAjTYfHW0h4PznY0JpWfeNaVRD4EbDwbE2m8X6OzQEWJJB1WESw4Zg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-01-12T13:21:54Z"
|
||||
mac: ENC[AES256_GCM,data:oYMO342I9EWOIeFxGxCKYVxKrfACdm9/8z8dnmaC2E97mMNhbuoxid7W6g7GgE5Iz6Xlo0paARim43wrmFAnT6U0YlEKdDkducZtVaxc3mCGwbVaGL1E9ErhZBBWFQoF+GGgeB8Co0TbB1Ox5VeRdPzz4diJkPiwsXVGlSCofU8=,iv:UpCOtVwvYrk5kXChUo/g34ZvjxHkTK9r0LTv9Dag6iM=,tag:XBlvehU8xKWkV2zcMNFPpw==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.11.0
|
||||
24
system/boot.nix
Normal file
24
system/boot.nix
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{ pkgs, ... }:
|
||||
{
|
||||
# Disable systemd-boot
|
||||
boot.loader.systemd-boot.enable = false;
|
||||
|
||||
# Enable GRUB
|
||||
boot.loader.grub = {
|
||||
enable = true;
|
||||
efiSupport = true;
|
||||
device = "nodev";
|
||||
useOSProber = false;
|
||||
configurationLimit = 10;
|
||||
};
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
boot.loader.timeout = 5;
|
||||
|
||||
# Boot Animation (Plymouth)
|
||||
boot.plymouth.enable = true;
|
||||
|
||||
# Catppuccin Theme Configuration
|
||||
catppuccin.flavor = "mocha";
|
||||
catppuccin.grub.enable = true;
|
||||
catppuccin.plymouth.enable = true;
|
||||
}
|
||||
47
system/compatibility.nix
Normal file
47
system/compatibility.nix
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
programs.nix-ld.enable = true;
|
||||
programs.nix-ld.libraries = with pkgs; [
|
||||
stdenv.cc.cc.lib
|
||||
zlib
|
||||
fuse3
|
||||
icu
|
||||
nss
|
||||
openssl
|
||||
curl
|
||||
expat
|
||||
|
||||
# CEF / Electron dependencies
|
||||
glib
|
||||
libgbm
|
||||
libglvnd
|
||||
nspr
|
||||
gtk3
|
||||
alsa-lib
|
||||
cups
|
||||
mesa
|
||||
libdrm
|
||||
libxkbcommon
|
||||
dbus
|
||||
libxml2
|
||||
|
||||
# X11 / Wayland
|
||||
xorg.libX11
|
||||
xorg.libXcomposite
|
||||
xorg.libXdamage
|
||||
xorg.libXext
|
||||
xorg.libXfixes
|
||||
xorg.libXrandr
|
||||
xorg.libxcb
|
||||
xorg.libXScrnSaver
|
||||
pango
|
||||
cairo
|
||||
at-spi2-atk
|
||||
at-spi2-core
|
||||
systemd
|
||||
];
|
||||
}
|
||||
28
system/game-drive.nix
Normal file
28
system/game-drive.nix
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
# Unlock secondary drive in Stage 2 (after /persist is mounted)
|
||||
environment.etc."crypttab".text = ''
|
||||
cryptdata /dev/disk/by-id/nvme-KINGSTON_SNVS1000G_50026B7784BF8876 /persist/etc/cryptdata.key header=/persist/etc/cryptdata.header
|
||||
'';
|
||||
|
||||
# Ensure the mount point exists on the tmpfs root
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /home/ashie/Games 0755 ashie users -"
|
||||
"L+ /home/ashie/.local/share/Steam - - - - /home/ashie/Games/Steam"
|
||||
];
|
||||
|
||||
fileSystems."/home/ashie/Games" = {
|
||||
device = "/games";
|
||||
fsType = "none";
|
||||
options = [
|
||||
"bind"
|
||||
"X-systemd.after=games.mount"
|
||||
];
|
||||
};
|
||||
}
|
||||
35
system/greetd.nix
Normal file
35
system/greetd.nix
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
tuigreet = "${pkgs.tuigreet}/bin/tuigreet";
|
||||
hyprland-session = "${pkgs.hyprland}/share/wayland-sessions";
|
||||
in
|
||||
{
|
||||
services.greetd = {
|
||||
enable = true;
|
||||
settings = {
|
||||
default_session = {
|
||||
command = "${tuigreet} --time --remember --remember-session --cmd niri-session";
|
||||
user = "greeter";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# this is a life saver.
|
||||
# literally no documentation about this anywhere.
|
||||
# might be good to write about this...
|
||||
# https://www.reddit.com/r/NixOS/comments/u0cdpi/tuigreet_with_xmonad_how/
|
||||
systemd.services.greetd.serviceConfig = {
|
||||
Type = "idle";
|
||||
StandardInput = "tty";
|
||||
StandardOutput = "tty";
|
||||
StandardError = "journal"; # Without this errors will spam on screen
|
||||
# Without these bootlogs will spam on screen
|
||||
TTYReset = true;
|
||||
TTYVHangup = true;
|
||||
TTYVTDisallocate = true;
|
||||
};
|
||||
}
|
||||
112
system/hardware.nix
Normal file
112
system/hardware.nix
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# Enable early KMS for the prompt
|
||||
boot.initrd.kernelModules = [ "amdgpu" ];
|
||||
|
||||
hardware.graphics = {
|
||||
enable = true;
|
||||
enable32Bit = true;
|
||||
extraPackages = with pkgs; [
|
||||
rocmPackages.clr.icd
|
||||
];
|
||||
};
|
||||
|
||||
hardware.openrazer.enable = true;
|
||||
hardware.openrazer.users = [ "ashie" ];
|
||||
|
||||
hardware.amdgpu.overdrive.enable = true;
|
||||
services.xserver.videoDrivers = [ "amdgpu" ];
|
||||
services.lact.enable = true;
|
||||
|
||||
services.fwupd.enable = true;
|
||||
|
||||
services.usbguard = {
|
||||
enable = true;
|
||||
dbus.enable = true;
|
||||
implicitPolicyTarget = "block";
|
||||
# Restore keyboard/mouse if they disconnect/reconnect
|
||||
restoreControllerDeviceState = true;
|
||||
rules = ''
|
||||
allow id 1d6b:0002 serial "0000:02:00.0" name "xHCI Host Controller" hash "4+i1fOQzh6/CdbdfiwrmdTYf8TLnLkUDuN34mexLwrg=" parent-hash "/1e+oO/+QiBM87zSY7rDiPm4h+kOVFNNTkeJA9MoRos=" with-interface 09:00:00 with-connect-type ""
|
||||
allow id 1d6b:0003 serial "0000:02:00.0" name "xHCI Host Controller" hash "ViuugLRua/aPlAvgkXniQWreBpkM4XpeLtv3FPTwTSk=" parent-hash "/1e+oO/+QiBM87zSY7rDiPm4h+kOVFNNTkeJA9MoRos=" with-interface 09:00:00 with-connect-type ""
|
||||
allow id 1d6b:0002 serial "0000:0b:00.3" name "xHCI Host Controller" hash "TrEQZkga/umqtek+QDHj31Ymjb6xG1N177/tn/x3Wwo=" parent-hash "HYUOOWtH2SmhNDgrVquPq/YUStPUYo6SumTyP8wwEFk=" with-interface 09:00:00 with-connect-type ""
|
||||
allow id 1d6b:0003 serial "0000:0b:00.3" name "xHCI Host Controller" hash "ZqN/My4EyNiCWZdGdPH5rFMhfQthYbnRGW1/I0aO13s=" parent-hash "HYUOOWtH2SmhNDgrVquPq/YUStPUYo6SumTyP8wwEFk=" with-interface 09:00:00 with-connect-type ""
|
||||
allow id 1a40:0801 serial "" name "USB 2.0 Hub" hash "AGUYEKgZCSjWnBQRgECiKsBFduiNgQO6eIKZQaynmmY=" parent-hash "4+i1fOQzh6/CdbdfiwrmdTYf8TLnLkUDuN34mexLwrg=" via-port "1-2" with-interface 09:00:00 with-connect-type "hotplug"
|
||||
allow id 1532:0552 serial "1234" name "Razer Barracuda X 2.4" hash "iWNeDU4Lq0B+7fpGii17D5dD4RIgfQCZtC6/B72rD2o=" parent-hash "4+i1fOQzh6/CdbdfiwrmdTYf8TLnLkUDuN34mexLwrg=" with-interface { 01:01:00 01:02:00 01:02:00 01:02:00 01:02:00 03:01:00 } with-connect-type "hotplug"
|
||||
allow id 0781:55a3 serial "00015218091224140918" name " SanDisk 3.2Gen1" hash "DMEbNrSWf2Zj643VR0IoT+9AXBj1P+lXYZY6Tca93HE=" parent-hash "ViuugLRua/aPlAvgkXniQWreBpkM4XpeLtv3FPTwTSk=" with-interface { 08:06:50 08:06:62 } with-connect-type "hotplug"
|
||||
allow id 1532:0257 serial "00000000001A" name "Razer Huntsman Mini" hash "np7/cWye90u8Ym2VrOaMPtB7JbM0z5joqW4dzFWf6Mk=" parent-hash "TrEQZkga/umqtek+QDHj31Ymjb6xG1N177/tn/x3Wwo=" with-interface { 03:01:01 03:00:01 03:00:02 03:00:01 } with-connect-type "hotplug"
|
||||
allow id 1038:1852 serial "" name "SteelSeries Aerox 5 Wireless" hash "5YD8B0XxvySALtxGFAPYSjOuSgSHelgmtAYzPOVVzW0=" parent-hash "4+i1fOQzh6/CdbdfiwrmdTYf8TLnLkUDuN34mexLwrg=" via-port "1-10" with-interface { 03:01:02 03:00:00 03:00:00 03:00:00 03:00:00 } with-connect-type "hotplug"
|
||||
allow id 03f0:07b4 serial "1H544305B9" name "HyperX QuadCast 2" hash "zHNOiwP67DkizHez0CkaCcSjiKa/K/8zZHrAUSE4h3c=" parent-hash "AGUYEKgZCSjWnBQRgECiKsBFduiNgQO6eIKZQaynmmY=" with-interface { 01:01:00 01:02:00 01:02:00 01:02:00 01:02:00 01:02:00 01:02:00 01:02:00 03:00:00 } with-connect-type "unknown"
|
||||
allow id 03f0:09af serial "1H544305B9" name "HyperX QuadCast 2 Controller" hash "hHNUvgz8TH+wYR2h1fbF/JUIOCyZMgw+ylLErxsmNDg=" parent-hash "AGUYEKgZCSjWnBQRgECiKsBFduiNgQO6eIKZQaynmmY=" with-interface { 03:00:00 03:00:00 } with-connect-type "unknown"
|
||||
allow id 1532:00c1 serial "" name "Razer Viper V3 Pro" hash "B5HZa6gqaXvOnQI3eY7h8dorvfT91KEI6SqQ+Q+N0zU=" parent-hash "TrEQZkga/umqtek+QDHj31Ymjb6xG1N177/tn/x3Wwo=" via-port "3-4" with-interface { 03:01:02 03:01:00 03:01:01 } with-connect-type "hotplug"
|
||||
'';
|
||||
};
|
||||
|
||||
services.xserver.xkb.layout = "de";
|
||||
services.xserver.xkb.options = "eurosign:e,caps:escape";
|
||||
|
||||
console.keyMap = "de";
|
||||
|
||||
environment.variables = {
|
||||
# Enforces RADV Vulkan implementation
|
||||
AMD_VULKAN_ICD = "RADV";
|
||||
# Increase AMD's shader cache size
|
||||
MESA_SHADER_CACHE_MAX_SIZE = "100G";
|
||||
};
|
||||
|
||||
fonts.packages = with pkgs; [
|
||||
nerd-fonts.comic-shanns-mono
|
||||
];
|
||||
|
||||
systemd.services.amdgpu-power-limit = {
|
||||
description = "Set AMDGPU Power Limit";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
wants = [ "systemd-udev-settle.service" ];
|
||||
after = [
|
||||
"sysinit.target"
|
||||
"systemd-udev-settle.service"
|
||||
];
|
||||
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = pkgs.writeShellScript "amdgpu-power-limit" ''
|
||||
set -euo pipefail
|
||||
shopt -s nullglob
|
||||
|
||||
TARGET=374000000 # microWatts = 374W
|
||||
|
||||
applied=0
|
||||
for hwmon in /sys/class/drm/card*/device/hwmon/hwmon*; do
|
||||
cap="$hwmon/power1_cap"
|
||||
minf="$hwmon/power1_cap_min"
|
||||
maxf="$hwmon/power1_cap_max"
|
||||
|
||||
[[ -e "$cap" && -w "$cap" ]] || continue
|
||||
|
||||
min=0; max=0
|
||||
[[ -r "$minf" ]] && min="$(cat "$minf")"
|
||||
[[ -r "$maxf" ]] && max="$(cat "$maxf")"
|
||||
|
||||
val="$TARGET"
|
||||
if [[ "$min" -gt 0 && "$val" -lt "$min" ]]; then val="$min"; fi
|
||||
if [[ "$max" -gt 0 && "$val" -gt "$max" ]]; then val="$max"; fi
|
||||
|
||||
echo "$val" > "$cap"
|
||||
echo "AMDGPU power cap set to $val µW (target $TARGET) for $hwmon (min=$min max=$max)"
|
||||
applied=1
|
||||
done
|
||||
|
||||
if [[ "$applied" -eq 0 ]]; then
|
||||
echo "No writable AMDGPU power1_cap nodes found." >&2
|
||||
exit 1
|
||||
fi
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
43
system/kernel.nix
Normal file
43
system/kernel.nix
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# nixpkgs.overlays = [
|
||||
# inputs.nix-cachyos-kernel.overlays.default
|
||||
# ];
|
||||
# Use CachyOS Kernel
|
||||
boot.kernelPackages =
|
||||
pkgs.linuxPackagesFor
|
||||
inputs.nix-cachyos-kernel.packages.${pkgs.system}.linux-cachyos-latest;
|
||||
|
||||
# =============================================================================
|
||||
# DEFAULT BOOT: Linux Desktop Mode (GPU for Host)
|
||||
# =============================================================================
|
||||
# This is the normal boot. The GPU is used by the host for Hyprland/gaming.
|
||||
|
||||
boot.kernelParams = [
|
||||
"amd_iommu=on"
|
||||
"iommu=pt"
|
||||
"split_lock_detect=off"
|
||||
];
|
||||
|
||||
# Vendor Reset (helps with GPU reset when switching modes)
|
||||
boot.extraModulePackages = [ config.boot.kernelPackages.vendor-reset ];
|
||||
boot.kernelModules = [
|
||||
"tcp_bbr"
|
||||
"vendor-reset"
|
||||
"ntsync"
|
||||
];
|
||||
|
||||
# =============================================================================
|
||||
# Network and Misc Sysctl
|
||||
# =============================================================================
|
||||
boot.kernel.sysctl = {
|
||||
"net.ipv4.tcp_congestion_control" = "bbr";
|
||||
"net.core.default_qdisc" = "fq";
|
||||
"vm.max_map_count" = 1048576;
|
||||
};
|
||||
}
|
||||
14
system/locate.nix
Normal file
14
system/locate.nix
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
users.groups.mlocate = { };
|
||||
services.locate = {
|
||||
enable = true;
|
||||
package = pkgs.mlocate;
|
||||
interval = "daily";
|
||||
};
|
||||
}
|
||||
68
system/networking.nix
Normal file
68
system/networking.nix
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# Networking Configuration (Host-Specific)
|
||||
# DNS-over-TLS is now in modules/system/dns-over-tls.nix
|
||||
# Cloudflare firewall is now in modules/system/cloudflare-firewall.nix
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
networking.hostName = "nixos";
|
||||
|
||||
# Switch to systemd-networkd for bridging support
|
||||
networking.networkmanager.enable = false;
|
||||
networking.useNetworkd = true;
|
||||
|
||||
systemd.network = {
|
||||
netdevs."br0".netdevConfig = {
|
||||
Kind = "bridge";
|
||||
Name = "br0";
|
||||
};
|
||||
|
||||
networks."10-eth" = {
|
||||
matchConfig.Name = "enp4s0";
|
||||
networkConfig.Bridge = "br0";
|
||||
};
|
||||
|
||||
networks."20-br0" = {
|
||||
matchConfig.Name = "br0";
|
||||
networkConfig = {
|
||||
DHCP = "yes";
|
||||
# Ensure DNS/Gateway is accepted
|
||||
IPv6PrivacyExtensions = "yes";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
networking.enableIPv6 = false;
|
||||
|
||||
# Disable IPv6 via sysctl
|
||||
boot.kernel.sysctl = {
|
||||
"net.ipv6.conf.all.disable_ipv6" = 1;
|
||||
"net.ipv6.conf.default.disable_ipv6" = 1;
|
||||
"net.ipv6.conf.lo.disable_ipv6" = 1;
|
||||
};
|
||||
|
||||
# Basic firewall settings (Cloudflare rules are in the module)
|
||||
networking.firewall.enable = false;
|
||||
|
||||
# Dynamic DNS for Cloudflare
|
||||
services.ddclient = {
|
||||
enable = true;
|
||||
protocol = "cloudflare";
|
||||
zone = "ashisgreat.xyz";
|
||||
username = "token";
|
||||
passwordFile = config.sops.secrets.cloudflare_api_key.path;
|
||||
domains = [
|
||||
"api.ashisgreat.xyz"
|
||||
"chat.ashisgreat.xyz"
|
||||
"stream.ashisgreat.xyz"
|
||||
"stream-api.ashisgreat.xyz"
|
||||
];
|
||||
interval = "10min";
|
||||
usev6 = "disabled";
|
||||
usev4 = "cmdv4";
|
||||
extraConfig = "cmdv4='${pkgs.curl}/bin/curl -s https://api.ipify.org'";
|
||||
};
|
||||
}
|
||||
130
system/packages.nix
Normal file
130
system/packages.nix
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
environment.systemPackages = with pkgs; [
|
||||
# Rust System Rewrites
|
||||
mimalloc # Fast allocator
|
||||
grc # Generic Colouriser
|
||||
mold # Fast linker
|
||||
skim # Rust fuzzy finder (fzf alternative)
|
||||
uutils-coreutils-noprefix # GNU coreutils replacement
|
||||
ripgrep # grep replacement
|
||||
eza # ls replacement
|
||||
bat # cat replacement
|
||||
fd # find replacement
|
||||
procs # ps replacement
|
||||
dust # du replacement
|
||||
sd # sed replacement
|
||||
bottom # top replacement
|
||||
zoxide # cd replacement
|
||||
yazi # file manager
|
||||
tokei # code statistics
|
||||
hyperfine # benchmarking
|
||||
|
||||
slirp4netns # Better network backend than slirp4netns for rootless containers
|
||||
neovim
|
||||
wget
|
||||
kitty
|
||||
quickshell
|
||||
git
|
||||
sbctl
|
||||
fuzzel
|
||||
prismlauncher-sandboxed
|
||||
polychromatic
|
||||
vscodium
|
||||
kdePackages.dolphin
|
||||
kdePackages.dolphin-plugins
|
||||
jdk
|
||||
antigravity
|
||||
onlyoffice-desktopeditors
|
||||
python3
|
||||
swww
|
||||
claude-code
|
||||
lxqt.lxqt-policykit
|
||||
(catppuccin-gtk.override { variant = "mocha"; })
|
||||
catppuccin-kvantum
|
||||
catppuccin
|
||||
nwg-look
|
||||
chromium
|
||||
qt6Packages.qt6ct
|
||||
libsForQt5.qt5ct
|
||||
kdePackages.qtstyleplugin-kvantum
|
||||
goverlay
|
||||
mangohud
|
||||
gamemode
|
||||
lact
|
||||
fastfetch
|
||||
hyfetch
|
||||
nautilus
|
||||
lutris-sandboxed
|
||||
steam-sandboxed
|
||||
azahar-sandboxed
|
||||
faugus-sandboxed
|
||||
citron-sandboxed
|
||||
ryubing-sandboxed
|
||||
wireguard-tools
|
||||
jq
|
||||
grim
|
||||
vlc
|
||||
slurp
|
||||
wl-clipboard
|
||||
vesktop-sandboxed
|
||||
starship
|
||||
zip
|
||||
unzip
|
||||
unar
|
||||
p7zip
|
||||
nixfmt
|
||||
tealdeer
|
||||
uv
|
||||
nodejs
|
||||
sillytavern
|
||||
btop
|
||||
distrobox
|
||||
heroic
|
||||
tcpdump
|
||||
codex
|
||||
distroshelf
|
||||
gemini-cli
|
||||
wineWow64Packages.waylandFull
|
||||
qbittorrent
|
||||
stress-ng
|
||||
kdePackages.kleopatra
|
||||
kdePackages.ark
|
||||
easyeffects
|
||||
dysk
|
||||
zstd
|
||||
podman
|
||||
spotify-sandboxed
|
||||
jmtpfs
|
||||
glfw
|
||||
mlocate
|
||||
openssl
|
||||
nspr
|
||||
firefox-sandboxed
|
||||
brave-sandboxed
|
||||
eddie
|
||||
appimage-run
|
||||
rivalcfg
|
||||
rocmPackages.rocminfo
|
||||
rocmPackages.rocm-smi
|
||||
clinfo
|
||||
playerctl
|
||||
dotnet-sdk_9
|
||||
xdelta
|
||||
xxd
|
||||
winetricks
|
||||
protontricks
|
||||
file
|
||||
ffmpeg-full
|
||||
];
|
||||
|
||||
environment.variables = {
|
||||
QT_QPA_PLATFORMTHEME = "qt6ct";
|
||||
};
|
||||
}
|
||||
80
system/secrets.nix
Normal file
80
system/secrets.nix
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
sops.defaultSopsFile = ../secrets/secrets.yaml;
|
||||
sops.defaultSopsFormat = "yaml";
|
||||
|
||||
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
|
||||
sops.age.keyFile = "/home/ashie/.config/sops/age/keys.txt";
|
||||
# WireGuard / Gluetun secrets
|
||||
sops.secrets.wireguard_private_key = {
|
||||
owner = "ashie";
|
||||
};
|
||||
sops.secrets.wireguard_public_key = {
|
||||
owner = "ashie";
|
||||
};
|
||||
sops.secrets.wireguard_endpoint_ip = {
|
||||
owner = "ashie";
|
||||
};
|
||||
sops.secrets.wireguard_endpoint_port = {
|
||||
owner = "ashie";
|
||||
};
|
||||
sops.secrets.wireguard_addresses = {
|
||||
owner = "ashie";
|
||||
};
|
||||
sops.secrets.wireguard_preshared_key = {
|
||||
owner = "ashie";
|
||||
};
|
||||
|
||||
sops.secrets.open_webui_env = {
|
||||
owner = "ashie";
|
||||
};
|
||||
|
||||
sops.templates."gluetun.env" = {
|
||||
owner = "ashie";
|
||||
content = ''
|
||||
WIREGUARD_PUBLIC_KEY=${config.sops.placeholder.wireguard_public_key}
|
||||
WIREGUARD_ENDPOINT_IP=${config.sops.placeholder.wireguard_endpoint_ip}
|
||||
WIREGUARD_ENDPOINT_PORT=${config.sops.placeholder.wireguard_endpoint_port}
|
||||
'';
|
||||
};
|
||||
|
||||
# Cloudflare secrets
|
||||
sops.secrets.cloudflare_api_key = { };
|
||||
|
||||
# Unified API Key
|
||||
sops.secrets.master_api_key = {
|
||||
owner = "ashie";
|
||||
};
|
||||
|
||||
sops.templates."api_key.env" = {
|
||||
owner = "ashie";
|
||||
content = ''
|
||||
OPENAI_API_KEY=${config.sops.placeholder.master_api_key}
|
||||
API_KEY=${config.sops.placeholder.master_api_key}
|
||||
KEY=${config.sops.placeholder.master_api_key}
|
||||
JWT_SECRET=${config.sops.placeholder.jwt_secret}
|
||||
'';
|
||||
};
|
||||
|
||||
sops.secrets.jwt_secret = {
|
||||
owner = "ashie";
|
||||
};
|
||||
|
||||
sops.secrets.hashed_password = {
|
||||
neededForUsers = true;
|
||||
};
|
||||
|
||||
sops.templates."caddy.env" = {
|
||||
owner = "caddy";
|
||||
group = "caddy";
|
||||
content = ''
|
||||
CF_API_TOKEN=${config.sops.placeholder.cloudflare_api_key}
|
||||
'';
|
||||
};
|
||||
}
|
||||
182
system/services.nix
Normal file
182
system/services.nix
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
services.flatpak.enable = true;
|
||||
|
||||
services.timesyncd.enable = false;
|
||||
services.chrony = {
|
||||
enable = true;
|
||||
enableNTS = true;
|
||||
servers = [
|
||||
"time.cloudflare.com"
|
||||
"nts.netnod.se"
|
||||
"ptbtime1.ptb.de"
|
||||
];
|
||||
extraConfig = ''
|
||||
user chrony
|
||||
pidfile /run/chrony/chrony.pid
|
||||
driftfile /var/lib/chrony/drift
|
||||
makestep 1.0 3
|
||||
'';
|
||||
};
|
||||
|
||||
services.fstrim.enable = true;
|
||||
|
||||
services.dbus.implementation = "broker";
|
||||
|
||||
services.earlyoom = {
|
||||
enable = true;
|
||||
enableNotifications = true;
|
||||
freeMemThreshold = 5;
|
||||
};
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
ports = [ 5732 ];
|
||||
hostKeys = [
|
||||
{
|
||||
path = "/etc/ssh/ssh_host_ed25519_key";
|
||||
type = "ed25519";
|
||||
}
|
||||
];
|
||||
settings = {
|
||||
PasswordAuthentication = false;
|
||||
PermitRootLogin = "no";
|
||||
};
|
||||
};
|
||||
|
||||
services.gnome.gnome-keyring.enable = true;
|
||||
security.pam.services.greetd.enableGnomeKeyring = true;
|
||||
|
||||
programs.hyprland = {
|
||||
enable = true;
|
||||
xwayland.enable = true;
|
||||
};
|
||||
|
||||
programs.firefox.enable = false;
|
||||
|
||||
services.caddy = {
|
||||
enable = true;
|
||||
email = "mails@ashisgreat.xyz";
|
||||
|
||||
package = pkgs.caddy.withPlugins {
|
||||
plugins = [ "github.com/caddy-dns/cloudflare@v0.2.3-0.20251204174556-6dc1fbb7e925" ];
|
||||
hash = "sha256-htrfa7whiIK2pqtKl6pKFby928dCkMmJp3Hu0e3JBX4=";
|
||||
};
|
||||
globalConfig = ''
|
||||
acme_dns cloudflare {env.CF_API_TOKEN}
|
||||
servers {
|
||||
protocols h1 h2
|
||||
}
|
||||
'';
|
||||
|
||||
virtualHosts."api.ashisgreat.xyz" = {
|
||||
extraConfig = ''
|
||||
# Security headers
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "DENY"
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https:;"
|
||||
-Server
|
||||
}
|
||||
reverse_proxy 127.0.0.1:8045
|
||||
'';
|
||||
};
|
||||
|
||||
virtualHosts."chat.ashisgreat.xyz" = {
|
||||
extraConfig = ''
|
||||
# Security headers
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "SAMEORIGIN"
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https: blob:; font-src 'self' data:; connect-src 'self' wss: https:; worker-src 'self' blob:;"
|
||||
-Server
|
||||
}
|
||||
reverse_proxy 127.0.0.1:3000
|
||||
'';
|
||||
};
|
||||
|
||||
virtualHosts."stream.ashisgreat.xyz" = {
|
||||
extraConfig = ''
|
||||
# Basic Auth
|
||||
basic_auth {
|
||||
admin $2a$14$2kaAS6oLx6SdyuM2lksnYOZidfRWb7AGPXT5hhg/s5nseL7bjHsx2
|
||||
}
|
||||
# Security headers
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "SAMEORIGIN"
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
-Server
|
||||
}
|
||||
reverse_proxy 127.0.0.1:3333
|
||||
'';
|
||||
};
|
||||
|
||||
virtualHosts."stream-api.ashisgreat.xyz" = {
|
||||
extraConfig = ''
|
||||
# Security headers
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||
X-Content-Type-Options "nosniff"
|
||||
# Backend API needs to be accessible by frontend
|
||||
Access-Control-Allow-Origin "https://stream.ashisgreat.xyz"
|
||||
-Server
|
||||
}
|
||||
|
||||
reverse_proxy 127.0.0.1:3334
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
# Hardening for Chrony
|
||||
systemd.services.chronyd.serviceConfig = {
|
||||
ProtectSystem = lib.mkForce "strict";
|
||||
ProtectHome = true;
|
||||
PrivateTmp = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectKernelModules = true;
|
||||
# Chrony needs to adjust time, preserve CAP_SYS_TIME and CAP_NET_BIND_SERVICE
|
||||
CapabilityBoundingSet = [
|
||||
"CAP_SYS_TIME"
|
||||
"CAP_NET_BIND_SERVICE"
|
||||
];
|
||||
MemoryDenyWriteExecute = true;
|
||||
LockPersonality = true;
|
||||
};
|
||||
|
||||
# Hardening for EarlyOOM
|
||||
systemd.services.earlyoom.serviceConfig = {
|
||||
ProtectSystem = "strict";
|
||||
ProtectHome = true;
|
||||
PrivateTmp = true;
|
||||
PrivateDevices = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectKernelModules = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
LockPersonality = true;
|
||||
};
|
||||
|
||||
systemd.services.caddy.serviceConfig = {
|
||||
NoNewPrivileges = true;
|
||||
ProtectHome = true;
|
||||
ProtectSystem = "strict";
|
||||
PrivateTmp = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectControlGroups = true;
|
||||
};
|
||||
|
||||
systemd.services.caddy.serviceConfig.EnvironmentFile = config.sops.templates."caddy.env".path;
|
||||
}
|
||||
85
system/users.nix
Normal file
85
system/users.nix
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
users.mutableUsers = false;
|
||||
|
||||
users.users.ashie = {
|
||||
isNormalUser = true;
|
||||
initialPassword = "password"; # Temporary password, change with 'passwd' after login
|
||||
# hashedPasswordFile = config.sops.secrets.hashed_password.path;
|
||||
uid = 1000;
|
||||
shell = pkgs.fish;
|
||||
extraGroups = [
|
||||
"wheel"
|
||||
"podman"
|
||||
"render"
|
||||
"video"
|
||||
];
|
||||
packages = with pkgs; [
|
||||
tree
|
||||
];
|
||||
subUidRanges = [
|
||||
{
|
||||
startUid = 200000000;
|
||||
count = 100000000;
|
||||
}
|
||||
];
|
||||
subGidRanges = [
|
||||
{
|
||||
startGid = 200000000;
|
||||
count = 100000000;
|
||||
}
|
||||
];
|
||||
openssh.authorizedKeys.keys = [
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGrff2OCTbuThkfOYQmf4T+pbA+rk4tGodk7HsXf30rN u0_a337@localhost"
|
||||
];
|
||||
};
|
||||
|
||||
# Disable root password login
|
||||
users.users.root = {
|
||||
hashedPassword = "!";
|
||||
subUidRanges = [
|
||||
{
|
||||
startUid = 100000;
|
||||
count = 100000000;
|
||||
}
|
||||
];
|
||||
subGidRanges = [
|
||||
{
|
||||
startGid = 100000;
|
||||
count = 100000000;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
# Restrict su to wheel group
|
||||
security.pam.services.su.requireWheel = true;
|
||||
|
||||
# Alias sudo to doas for muscle memory
|
||||
environment.shellAliases = {
|
||||
sudo = "doas";
|
||||
};
|
||||
|
||||
# System user for Podman --userns=auto allocations
|
||||
users.users.containers = {
|
||||
isSystemUser = true;
|
||||
group = "containers";
|
||||
subUidRanges = [
|
||||
{
|
||||
startUid = 200000;
|
||||
count = 100000000;
|
||||
}
|
||||
];
|
||||
subGidRanges = [
|
||||
{
|
||||
startGid = 200000;
|
||||
count = 100000000;
|
||||
}
|
||||
];
|
||||
};
|
||||
users.groups.containers = { };
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue