From 6bcd5e15adb887f9a9494669c65fc97e8873bb55 Mon Sep 17 00:00:00 2001 From: Nexoniarz Date: Mon, 31 Aug 2026 19:52:59 +0200 Subject: [PATCH] Switch desktop environment from Hyprland to Pantheon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Configures Pantheon (LightDM + gala) for daily use on this ASUS TUF Gaming A15 (FA507NU), and drops everything that only existed to serve the previous Hyprland/Quickshell setup: - desktop.nix: services.desktopManager.pantheon instead of programs.hyprland + greetd; comfy laptop touchpad defaults. - drivers.nix: NVIDIA PRIME render offload between the AMD Radeon 680M (drives the panel) and the RTX 4050 Max-Q (offload-only), using this machine's real PCI bus IDs; asusd/supergfxd for ASUS-specific laptop control. - apps.nix / scripts.nix: removed Hyprland/Quickshell-only packages and scripts (kitty, rofi, quickshell, mako, hyprlock, hyprpaper/mpvpaper, the Quickshell theme-picker scripts, cursor/wallpaper/display helpers) — Pantheon ships its own Files/Terminal/Screenshot/ notifications/theming natively. Kept the DE-agnostic scripts (vault toggle, metadata stripping, brightness control, Wi-Fi hotspot, telemetry, weather). - theme.nix and default-apps.nix deleted entirely — Pantheon owns GTK/icon theming itself, no more live dconf/CSS generation pipeline needed. - Deleted hyprland/ and dotfiles/ (kitty, mako, quickshell, rofi). Verified with `nixos-rebuild build`. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01RpiovBQL6k5MTDMN3Ln9nL --- README.md | 37 +++++ configuration.nix | 18 +++ hardware-configuration.nix | 45 ++++++ modules/apps.nix | 56 ++++++++ modules/desktop.nix | 38 ++++++ modules/drivers.nix | 79 +++++++++++ modules/scripts.nix | 25 ++++ modules/scripts/brightness-ctl.sh | 56 ++++++++ modules/scripts/hotspot-start.sh | 33 +++++ modules/scripts/strip-metadata.sh | 24 ++++ modules/scripts/system-telemetry.sh | 62 +++++++++ modules/scripts/vault-toggle.sh | 30 ++++ modules/scripts/weather-fetch.sh | 32 +++++ modules/system.nix | 204 ++++++++++++++++++++++++++++ modules/users.nix | 27 ++++ 15 files changed, 766 insertions(+) create mode 100644 README.md create mode 100644 configuration.nix create mode 100644 hardware-configuration.nix create mode 100644 modules/apps.nix create mode 100644 modules/desktop.nix create mode 100644 modules/drivers.nix create mode 100644 modules/scripts.nix create mode 100755 modules/scripts/brightness-ctl.sh create mode 100755 modules/scripts/hotspot-start.sh create mode 100755 modules/scripts/strip-metadata.sh create mode 100755 modules/scripts/system-telemetry.sh create mode 100755 modules/scripts/vault-toggle.sh create mode 100755 modules/scripts/weather-fetch.sh create mode 100644 modules/system.nix create mode 100644 modules/users.nix diff --git a/README.md b/README.md new file mode 100644 index 0000000..b714743 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# nexoniarz-config-nixos-laptop + +My personal NixOS system configuration tailored for daily use on an +**ASUS TUF Gaming A15 (FA507NU)**. + +## Overview + +- **OS:** NixOS +- **DE / Compositor:** Pantheon (gala, Wayland) +- **Display manager:** LightDM (elementary greeter) +- **Shell:** Zsh +- **CPU:** AMD Ryzen 5 7535HS +- **GPU:** AMD Radeon 680M (iGPU, drives the internal panel) + NVIDIA RTX + 4050 Max-Q (dGPU, PRIME render offload only — run anything through it with + `nvidia-offload `) +- **Laptop control:** `asusctl` (fan curves, keyboard backlight, battery + charge limit) + `supergfxctl` (dGPU power-state switching) + +## Layout + +- `configuration.nix` — top-level imports +- `hardware-configuration.nix` — generated by `nixos-generate-config`, don't + hand-edit +- `modules/system.nix` — bootloader, networking, time, sound, hardening, + nix-ld +- `modules/drivers.nix` — hybrid graphics (NVIDIA PRIME offload), ASUS + daemons, misc hardware (webcam loopback, tablet driver) +- `modules/desktop.nix` — Pantheon desktop environment +- `modules/users.nix` — user account, Steam, Firefox, Flatpak +- `modules/apps.nix` — all installed packages +- `modules/scripts.nix` + `modules/scripts/` — small custom CLI helpers + (encrypted vault toggle, metadata stripping, brightness control, Wi-Fi + hotspot, system telemetry, weather fetch) + +--- + +*Made by **Nexoniarz*** diff --git a/configuration.nix b/configuration.nix new file mode 100644 index 0000000..4f14d8d --- /dev/null +++ b/configuration.nix @@ -0,0 +1,18 @@ +# Main NixOS configuration file importing modular configurations. + +{ config, pkgs, ... }: + +{ + imports = [ + ./hardware-configuration.nix # Generated hardware scan results + ./modules/system.nix # Bootloader, networking, time, sound, hardening, nix-ld + ./modules/drivers.nix # AMD/NVIDIA hybrid graphics (PRIME) and ASUS laptop control + ./modules/desktop.nix # Pantheon desktop environment (LightDM + gala) + ./modules/users.nix # User accounts and global configurations + ./modules/apps.nix # All installed packages: apps, CLI tools, dev toolchains + ./modules/scripts.nix # Custom system scripts and helpers + ]; + + # Do not change this value. Read documentation before updating. + system.stateVersion = "26.05"; +} diff --git a/hardware-configuration.nix b/hardware-configuration.nix new file mode 100644 index 0000000..501ab14 --- /dev/null +++ b/hardware-configuration.nix @@ -0,0 +1,45 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "thunderbolt" "usb_storage" "sd_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-amd" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/disk/by-uuid/2e0afef3-e81a-45c1-a439-c17123eef182"; + fsType = "btrfs"; + }; + + fileSystems."/home" = + { device = "/dev/disk/by-uuid/2e0afef3-e81a-45c1-a439-c17123eef182"; + fsType = "btrfs"; + options = [ "subvol=home" ]; + }; + + fileSystems."/nix" = + { device = "/dev/disk/by-uuid/2e0afef3-e81a-45c1-a439-c17123eef182"; + fsType = "btrfs"; + options = [ "subvol=nix" ]; + }; + + fileSystems."/boot" = + { device = "/dev/disk/by-uuid/CA1D-BBF9"; + fsType = "vfat"; + options = [ "fmask=0077" "dmask=0077" ]; + }; + + swapDevices = + [ { device = "/dev/disk/by-uuid/f52f4ee6-889e-4c86-82bf-ba8396e07483"; } + ]; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/modules/apps.nix b/modules/apps.nix new file mode 100644 index 0000000..631d2aa --- /dev/null +++ b/modules/apps.nix @@ -0,0 +1,56 @@ +{ config, pkgs, ... }: + +{ + # Every installed package — GUI apps, CLI tools, and dev toolchains alike. + # If it's "install X", it goes here. + users.users."nexoniarz".packages = with pkgs; [ + # Hardware control (Pantheon itself ships Files/Terminal/Screenshot/ + # notifications/dock/launcher — see services.desktopManager.pantheon + # in modules/desktop.nix, nothing to list here for those) + brightnessctl # Backlight brightness (internal laptop panel) + ddcutil # DDC/CI brightness (external monitors) + lm_sensors # sensors-detect helps ddcutil find I2C buses; also feeds asusctl + networkmanager # nmcli, for the Wi-Fi hotspot script + + # Apps + blender + gimp + inkscape + vesktop + tor-browser + ffmpeg-full + vlc + filezilla + chromium + + # Games + prismlauncher + pkgs.osu-lazer-bin + + # Dev: compilers, runtimes, languages + glibc + musl + cmake + gnumake # Provides the 'make' command + gcc + python3 + openjdk25 + claude-code # AI Developer Assistant + ]; + + # Global command-line utilities available everywhere + environment.systemPackages = with pkgs; [ + git + gh # GitHub CLI — was only installed ad-hoc before (login didn't survive) + vim + fastfetch + gparted + firejail + gnupg + lmstudio + p7zip + unzip + unrar + libreoffice # GTK3 build — matches Pantheon's native GTK theming, unlike the Qt build + ]; +} diff --git a/modules/desktop.nix b/modules/desktop.nix new file mode 100644 index 0000000..32d30b5 --- /dev/null +++ b/modules/desktop.nix @@ -0,0 +1,38 @@ +{ config, pkgs, ... }: + +{ + # --- Pantheon: elementary OS's desktop environment (gala compositor, Wayland) --- + # Pulls in its own session (pantheon-wayland), top panel (Wingpanel), dock, + # app launcher, Files, Terminal, Screenshot, Photos, notifications, and + # GTK/icon theming — no separate compositor config or dotfile symlinking + # needed, unlike the previous Hyprland setup. + services.xserver.enable = true; + services.xserver.displayManager.lightdm.enable = true; + services.desktopManager.pantheon.enable = true; + + # Keyboard layout for LightDM/XWayland; Pantheon's own session picks this + # up too. + services.xserver.xkb = { + layout = "pl"; + variant = ""; + }; + + security.polkit.enable = true; + + # Native Wayland backend for Electron/Chromium-based apps + environment.sessionVariables.NIXOS_OZONE_WL = "1"; + + # Comfortable trackpad defaults for a laptop — tap-to-click and natural + # scrolling aren't on by default, and gala reads touchpad settings from + # GSettings at runtime (not services.libinput, which is X11-only), so + # they're set here as a dconf default instead. + programs.dconf.profiles.user.databases = [ + { + settings."org/gnome/desktop/peripherals/touchpad" = { + tap-to-click = true; + natural-scroll = true; + two-finger-scrolling-enabled = true; + }; + } + ]; +} diff --git a/modules/drivers.nix b/modules/drivers.nix new file mode 100644 index 0000000..0efa83e --- /dev/null +++ b/modules/drivers.nix @@ -0,0 +1,79 @@ +{ config, lib, pkgs, ... }: + +{ + # Enable graphics driver support and 32-bit acceleration (for Steam/gaming) + hardware.graphics = { + enable = true; + enable32Bit = true; + }; + + #OpenTabletDriver + hardware.opentabletdriver.enable = true; + hardware.uinput.enable = true; + boot.kernelModules = [ "uinput" "v4l2loopback" ]; + + # Virtual webcam device so ffmpeg (fed by the GoPro's stream) can appear + # as a normal camera to apps like Discord. exclusive_caps=1 is required + # for Chromium/WebRTC-based apps to recognize it as a real capture device. + boot.extraModulePackages = [ config.boot.kernelPackages.v4l2loopback ]; + boot.extraModprobeConfig = '' + options v4l2loopback devices=1 video_nr=10 card_label="GoPro Webcam" exclusive_caps=1 + # PS4/PS5 controllers (e.g. DualSense) connect over Bluetooth then + # immediately disconnect — a well-known Linux Bluetooth stack issue + # where many adapters' Enhanced Retransmission Mode implementation is + # broken. Disabling ERTM is the standard, widely-documented fix. + options bluetooth disable_ertm=1 + ''; + + # I2C bus, for DDC/CI monitor control (brightness) via ddcutil, when + # driving an external monitor over DisplayPort/HDMI. The internal panel + # uses the kernel backlight class instead (brightness-ctl picks whichever + # applies — see modules/scripts/brightness-ctl.sh). + hardware.i2c.enable = true; + + # --- Hybrid graphics: AMD Radeon 680M (iGPU, drives the internal panel) + # + NVIDIA RTX 4050 Max-Q (dGPU, offload-only) — ASUS TUF Gaming A15 + # FA507NU. Bus IDs come straight from `lspci` on this machine, not + # guessed: 01:00.0 (NVIDIA) -> PCI:1:0:0, 35:00.0 (AMD) -> PCI:53:0:0. + services.xserver.videoDrivers = [ "nvidia" ]; + + hardware.nvidia = { + # Required settings + modesetting.enable = true; + + # Enable NVIDIA Open GPU Kernel Modules (Turing architecture or newer — + # this RTX 4050 is Ada Lovelace, well within support) + open = true; + + # Enable the NVIDIA settings menu + nvidiaSettings = true; + + # Use the stable driver package matching current kernel + package = config.boot.kernelPackages.nvidiaPackages.stable; + + # Fixes suspend/resume hangs that are common with NVIDIA on laptops. + powerManagement.enable = true; + + # PRIME render offload: the AMD iGPU drives the display at all times + # (much better battery life than keeping the dGPU powered on), and the + # NVIDIA GPU is only spun up on demand for a specific process — run + # anything with `nvidia-offload ` (generated by + # enableOffloadCmd) to route it through the RTX 4050, e.g. games or + # Blender's Cycles renderer. + prime = { + offload = { + enable = true; + enableOffloadCmd = true; + }; + amdgpuBusId = "PCI:53:0:0"; + nvidiaBusId = "PCI:1:0:0"; + }; + }; + + # ASUS laptop control: keyboard backlight/fan curves/battery charge limit + # (asusctl) and dGPU power-state switching between Integrated/Hybrid modes + # (supergfxctl) — the two standard asus-linux.org daemons for ROG/TUF + # laptops, both packaged directly in nixpkgs. + services.asusd.enable = true; + services.supergfxd.enable = true; +} diff --git a/modules/scripts.nix b/modules/scripts.nix new file mode 100644 index 0000000..a6a61d8 --- /dev/null +++ b/modules/scripts.nix @@ -0,0 +1,25 @@ +{ config, pkgs, ... }: + +let + # Wrap external shell scripts into system executables + vaultToggle = pkgs.writeShellScriptBin "vault-toggle" (builtins.readFile ./scripts/vault-toggle.sh); + stripMetadata = pkgs.writeShellScriptBin "strip-metadata" (builtins.readFile ./scripts/strip-metadata.sh); + brightnessCtl = pkgs.writeShellScriptBin "brightness-ctl" (builtins.readFile ./scripts/brightness-ctl.sh); + hotspotStart = pkgs.writeShellScriptBin "hotspot-start" (builtins.readFile ./scripts/hotspot-start.sh); + systemTelemetry = pkgs.writeShellScriptBin "system-telemetry" (builtins.readFile ./scripts/system-telemetry.sh); + weatherFetch = pkgs.writeShellScriptBin "weather-fetch" (builtins.readFile ./scripts/weather-fetch.sh); +in +{ + # Install CLI dependencies required by the scripts + environment.systemPackages = with pkgs; [ + gocryptfs + exiftool + jq + vaultToggle + stripMetadata + brightnessCtl + hotspotStart + systemTelemetry + weatherFetch + ]; +} diff --git a/modules/scripts/brightness-ctl.sh b/modules/scripts/brightness-ctl.sh new file mode 100755 index 0000000..bc58dfa --- /dev/null +++ b/modules/scripts/brightness-ctl.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# Brightness control abstraction: prefers the kernel backlight class (laptop +# panels, via brightnessctl); falls back to DDC/CI over I2C (external +# monitors, via ddcutil) when no backlight device exists. Keeps the rest of +# the config (Quickshell's brightness popup) hardware-agnostic. +set -euo pipefail + +has_backlight() { + [ -n "$(ls -A /sys/class/backlight 2>/dev/null)" ] +} + +case "${1:-get}" in + get) + if has_backlight; then + brightnessctl -m | cut -d, -f4 | tr -d '%' + else + ddcutil getvcp 10 --brief 2>/dev/null | awk '{print $4}' + fi + ;; + set) + pct="${2:?usage: brightness-ctl set <0-100>}" + if has_backlight; then + brightnessctl set "${pct}%" > /dev/null + else + ddcutil setvcp 10 "$pct" > /dev/null + fi + ;; + up|down) + # brightnessctl takes a relative step directly, but ddcutil's + # setvcp only takes an absolute value — so the DDC/CI path (this + # machine's AOC 27G2G8 has no backlight device, so it always takes + # this path) has to read the current level and clamp the step + # itself. + step="${2:-5}" + if has_backlight; then + if [ "$1" = "up" ]; then + brightnessctl set "+${step}%" > /dev/null + else + brightnessctl set "${step}%-" > /dev/null + fi + else + cur=$(ddcutil getvcp 10 --brief 2>/dev/null | awk '{print $4}') + cur="${cur:-50}" + if [ "$1" = "up" ]; then + new=$(( cur + step > 100 ? 100 : cur + step )) + else + new=$(( cur - step < 0 ? 0 : cur - step )) + fi + ddcutil setvcp 10 "$new" > /dev/null + fi + ;; + *) + echo "usage: brightness-ctl [get|set <0-100>|up [step]|down [step]]" >&2 + exit 1 + ;; +esac diff --git a/modules/scripts/hotspot-start.sh b/modules/scripts/hotspot-start.sh new file mode 100755 index 0000000..5d78ba5 --- /dev/null +++ b/modules/scripts/hotspot-start.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# Starts a Wi-Fi hotspot with explicit security/band control — the simple +# `nmcli device wifi hotspot` one-liner doesn't expose those. +# hotspot-start +set -euo pipefail + +ssid="${1:?usage: hotspot-start }" +password="${2:?password required}" +security="${3:-wpa2}" +band="${4:-auto}" + +iface=$(nmcli -t -f DEVICE,TYPE device | awk -F: '$2=="wifi"{print $1; exit}') +if [ -z "$iface" ]; then + echo "No Wi-Fi device found" >&2 + exit 1 +fi + +# Idempotent: drop any previous Hotspot profile from an earlier run first. +nmcli connection delete Hotspot >/dev/null 2>&1 || true + +key_mgmt="wpa-psk" +[ "$security" = "wpa3" ] && key_mgmt="sae" + +nmcli connection add type wifi ifname "$iface" con-name Hotspot autoconnect no ssid "$ssid" > /dev/null +nmcli connection modify Hotspot 802-11-wireless.mode ap +if [ "$band" != "auto" ]; then + nmcli connection modify Hotspot 802-11-wireless.band "$band" +fi +nmcli connection modify Hotspot wifi-sec.key-mgmt "$key_mgmt" +nmcli connection modify Hotspot wifi-sec.psk "$password" +nmcli connection modify Hotspot ipv4.method shared + +nmcli connection up Hotspot diff --git a/modules/scripts/strip-metadata.sh b/modules/scripts/strip-metadata.sh new file mode 100755 index 0000000..2756091 --- /dev/null +++ b/modules/scripts/strip-metadata.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +if [ $# -eq 0 ]; then + echo "Usage: strip-metadata " + exit 1 +fi + +TARGET="$1" + +echo "[*] Stripping metadata from: $TARGET" + +# Remove all embedded metadata while keeping original filenames +if command -v exiftool &> /dev/null; then + exiftool -all= -overwrite_original -r "$TARGET" + echo "[+] Exif metadata removed." +else + echo "[!] Warning: exiftool is not installed." +fi + +# Reset timestamps (atime and mtime) to Epoch (1970-01-01 00:00:00) +echo "[*] Resetting timestamps..." +find "$TARGET" -exec touch -t 197001010000.00 {} + + +echo "[+] Operation completed successfully." \ No newline at end of file diff --git a/modules/scripts/system-telemetry.sh b/modules/scripts/system-telemetry.sh new file mode 100755 index 0000000..89c72a3 --- /dev/null +++ b/modules/scripts/system-telemetry.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# Single-shot system telemetry snapshot — CPU/RAM/GPU/VRAM/disk usage — as +# one JSON object, so the Quickshell System panel can poll this on a Timer +# instead of spawning half a dozen separate processes every tick. +set -euo pipefail + +# --- CPU --- +# /proc/stat's first line ("cpu ...") is cumulative jiffies since boot — +# computing instantaneous usage needs two samples across a short gap, so +# both reads happen inside this one invocation rather than needing +# cross-poll state kept on the QML side. +read_cpu() { + read -r _ user nice system idle iowait irq softirq steal _ < /proc/stat + echo "$((user + nice + system + irq + softirq + steal)) $((user + nice + system + idle + iowait + irq + softirq + steal))" +} +read -r cpu_busy1 cpu_total1 <<< "$(read_cpu)" +sleep 0.2 +read -r cpu_busy2 cpu_total2 <<< "$(read_cpu)" +cpu_busy_delta=$((cpu_busy2 - cpu_busy1)) +cpu_total_delta=$((cpu_total2 - cpu_total1)) +cpu_pct=0 +[ "$cpu_total_delta" -gt 0 ] && cpu_pct=$((100 * cpu_busy_delta / cpu_total_delta)) + +# --- RAM --- +mem_total_kb=$(awk '/^MemTotal:/{print $2}' /proc/meminfo) +mem_avail_kb=$(awk '/^MemAvailable:/{print $2}' /proc/meminfo) +mem_used_kb=$((mem_total_kb - mem_avail_kb)) + +# --- GPU / VRAM --- +# NVIDIA checked first via nvidia-smi (works regardless of which DRM card +# index the driver landed on); AMD falls back to the card0 sysfs counters +# directly — whichever's actually present decides which branch fires, so +# this works unmodified on either machine. +gpu_pct=-1 +vram_used_mb=-1 +vram_total_mb=-1 +if command -v nvidia-smi > /dev/null 2>&1 && nvidia-smi -L > /dev/null 2>&1; then + nv_out="$(nvidia-smi --query-gpu=utilization.gpu,memory.used,memory.total --format=csv,noheader,nounits -i 0 2>/dev/null | tr -d ' ')" + IFS=',' read -r gpu_pct vram_used_mb vram_total_mb <<< "$nv_out" +elif [ -e /sys/class/drm/card0/device/gpu_busy_percent ]; then + gpu_pct="$(cat /sys/class/drm/card0/device/gpu_busy_percent 2>/dev/null || echo -1)" + if [ -e /sys/class/drm/card0/device/mem_info_vram_used ]; then + vram_used_mb=$(($(cat /sys/class/drm/card0/device/mem_info_vram_used) / 1024 / 1024)) + vram_total_mb=$(($(cat /sys/class/drm/card0/device/mem_info_vram_total) / 1024 / 1024)) + fi +fi + +# --- Disks (root + home) --- +# Positional ($NF for mountpoint), not header-text matching — `df -h`'s +# header is locale-translated (verified live: "System plików"/"%uż." under +# this machine's pl_PL locale, not "Filesystem"/"Use%"), so matching on +# English header text would silently find nothing. +disks_json="$(df -h 2>/dev/null | awk ' + NR == 1 { next } + $NF == "/" || $NF == "/home" { + gsub(/%/, "", $5) + printf "%s{\"mount\":\"%s\",\"size\":\"%s\",\"used\":\"%s\",\"avail\":\"%s\",\"pct\":\"%s\"}", (n++ ? "," : ""), $NF, $2, $3, $4, $5 + } +')" + +printf '{"cpuPct":%s,"memUsedKb":%s,"memTotalKb":%s,"gpuPct":%s,"vramUsedMb":%s,"vramTotalMb":%s,"disks":[%s]}\n' \ + "$cpu_pct" "$mem_used_kb" "$mem_total_kb" "$gpu_pct" "$vram_used_mb" "$vram_total_mb" "$disks_json" diff --git a/modules/scripts/vault-toggle.sh b/modules/scripts/vault-toggle.sh new file mode 100755 index 0000000..fda6ad2 --- /dev/null +++ b/modules/scripts/vault-toggle.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +CIPHER_DIR="/run/media/nexoniarz/Lukas/.vault_cipher" +MOUNT_DIR="$HOME/Vault" + +mkdir -p "$CIPHER_DIR" "$MOUNT_DIR" + +if mountpoint -q "$MOUNT_DIR"; then + echo "[*] Synchronizing buffers and unmounting Vault..." + sync + + if fusermount -u "$MOUNT_DIR"; then + echo "[+] Vault unmounted successfully." + else + echo "[!] Failed to unmount Vault. Check if any application is using $MOUNT_DIR" + exit 1 + fi +else + echo "[*] Mounting Vault..." + if [ ! -f "$CIPHER_DIR/gocryptfs.conf" ]; then + echo "[!] Container not initialized. Initializing new gocryptfs vault..." + gocryptfs -init "$CIPHER_DIR" || exit 1 + fi + if gocryptfs -allow_other "$CIPHER_DIR" "$MOUNT_DIR"; then + echo "[+] Vault mounted successfully at $MOUNT_DIR" + else + echo "[!] Mount failed." + exit 1 + fi +fi diff --git a/modules/scripts/weather-fetch.sh b/modules/scripts/weather-fetch.sh new file mode 100755 index 0000000..5c83ec9 --- /dev/null +++ b/modules/scripts/weather-fetch.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# Fetches current weather + a short hourly forecast from Open-Meteo (free, +# no API key/account needed) for either the machine's IP-geolocated +# position (default, via ip-api.com — also free/keyless) or an explicitly +# named city, and prints one combined JSON object. +set -euo pipefail + +city="${1:-}" + +if [ -n "$city" ]; then + encoded="$(jq -sRr @uri <<< "$city")" + geo="$(curl -s "https://geocoding-api.open-meteo.com/v1/search?count=1&name=$encoded")" + lat="$(jq -r '.results[0].latitude // empty' <<< "$geo")" + lon="$(jq -r '.results[0].longitude // empty' <<< "$geo")" + place="$(jq -r '.results[0].name // empty' <<< "$geo")" + country="$(jq -r '.results[0].country // empty' <<< "$geo")" +else + ipgeo="$(curl -s "http://ip-api.com/json/")" + lat="$(jq -r '.lat // empty' <<< "$ipgeo")" + lon="$(jq -r '.lon // empty' <<< "$ipgeo")" + place="$(jq -r '.city // empty' <<< "$ipgeo")" + country="$(jq -r '.country // empty' <<< "$ipgeo")" +fi + +if [ -z "$lat" ] || [ -z "$lon" ]; then + echo '{"error":"location lookup failed"}' + exit 0 +fi + +weather="$(curl -s "https://api.open-meteo.com/v1/forecast?latitude=$lat&longitude=$lon¤t=temperature_2m,weather_code,wind_speed_10m,precipitation&hourly=temperature_2m,weather_code,precipitation_probability&timezone=auto&forecast_days=2")" + +jq --arg place "$place" --arg country "$country" '. + {place: $place, country: $country}' <<< "$weather" diff --git a/modules/system.nix b/modules/system.nix new file mode 100644 index 0000000..8118a20 --- /dev/null +++ b/modules/system.nix @@ -0,0 +1,204 @@ +{ config, lib, pkgs, ... }: + +{ + # Bootloader & Kernel + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + boot.loader.systemd-boot.configurationLimit = 5; + boot.kernelPackages = pkgs.linuxPackages; + boot.supportedFilesystems = [ "fuse" ]; + + # Networking + networking.hostName = "nixos"; + networking.networkmanager.enable = true; + + # Firewall Configuration + networking.firewall = { + enable = true; + + # Allow local ping diagnostic queries + allowPing = true; + + allowedTCPPortRanges = [ { from = 1714; to = 1764; } ]; + allowedUDPPortRanges = [ { from = 1714; to = 1764; } ]; + allowedTCPPorts = [ 80 443 ]; + # allowedUDPPorts = [ ]; + }; + + # Time zone and Internationalization + time.timeZone = "Europe/Warsaw"; + i18n.defaultLocale = "pl_PL.UTF-8"; + i18n.extraLocaleSettings = { + LC_ADDRESS = "pl_PL.UTF-8"; + LC_IDENTIFICATION = "pl_PL.UTF-8"; + LC_MEASUREMENT = "pl_PL.UTF-8"; + LC_MONETARY = "pl_PL.UTF-8"; + LC_NAME = "pl_PL.UTF-8"; + LC_NUMERIC = "pl_PL.UTF-8"; + LC_PAPER = "pl_PL.UTF-8"; + LC_TELEPHONE = "pl_PL.UTF-8"; + LC_TIME = "pl_PL.UTF-8"; + }; + + nix.settings.auto-optimise-store = true; + nix.gc = { + automatic = true; + dates = "weekly"; + options = "--delete-older-than 7d"; + }; + + # Console keymap + console.keyMap = "pl2"; + + # Sound and Audio (Pipewire) + security.rtkit.enable = true; + services.pulseaudio.enable = false; + services.pipewire = { + enable = true; + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + }; + + hardware.bluetooth = { + enable = true; + powerOnBoot = true; + settings = { + General = { + # Shows battery charge of connected devices on supported + # Bluetooth adapters. Defaults to 'false'. + Experimental = true; + # When enabled other devices can connect faster to us, however + # the tradeoff is increased power consumption. Defaults to + # 'false'. + FastConnectable = true; + }; + Policy = { + # Enable all controllers when they are found. This includes + # adapters present on start as well as adapters that are plugged + # in later on. Defaults to 'true'. + AutoEnable = true; + }; + }; + }; + + services.blueman.enable = true; + + #Firejail + programs.firejail = { + enable = true; + }; + + #FUSE shit + programs.fuse.userAllowOther = true; + + # Enable GnuPG Agent with GUI pinentry support + programs.gnupg.agent = { + enable = true; + enableSSHSupport = true; + pinentryPackage = pkgs.pinentry-gnome3; # Matches Pantheon's GTK/gnome-keyring stack + }; + + security.sudo.extraConfig = '' + Defaults pwfeedback + ''; + + # Printing services + services.printing.enable = true; + + nixpkgs.config.allowUnfree = true; + + fonts.packages = with pkgs; [ + corefonts + dejavu_fonts + noto-fonts + noto-fonts-cjk-sans + noto-fonts-color-emoji + nerd-fonts.fira-code + nerd-fonts.jetbrains-mono + nerd-fonts.hack + inter + liberation_ttf + roboto + ]; + + # --- Loader shim for running non-Nix-packaged binaries (e.g. the official + # Tor Browser bundle) that expect a standard FHS dynamic linker/libraries. --- + programs.nix-ld.enable = true; + programs.nix-ld.libraries = with pkgs; [ + glib gtk3 gdk-pixbuf pango cairo atk + dbus dbus-glib fontconfig freetype + libx11 libxext libxrender libxtst + libxi libxcomposite libxdamage libxfixes + libxrandr libxcursor libxcb libxkbcommon + alsa-lib mesa zlib stdenv.cc.cc + ]; + environment.systemPackages = [ pkgs.file ]; + + # --- Hardening: swap, kernel sysctls, DNS, MAC randomization --- + + # Swap: replace disk swap with compressed in-RAM swap. Nothing sensitive + # (decrypted vault contents, browser memory, GPG session data) ever gets + # written to disk in plaintext via swap. Also faster than disk swap under + # memory pressure. + zramSwap.enable = true; + swapDevices = lib.mkForce [ ]; + # systemd's gpt-auto-generator independently re-discovers and activates any + # GPT partition tagged as Linux swap, regardless of the swapDevices list + # above — this disables that auto-discovery so the old disk swap partition + # actually stays off. Doesn't affect /, /home, /nix, /boot: those are + # already explicitly declared in hardware-configuration.nix, not relying on + # gpt-auto. Takes effect on next reboot. + boot.kernelParams = [ "systemd.gpt_auto=0" ]; + + # Kernel/system hardening sysctls + boot.kernel.sysctl = { + # Hide kernel pointers from unprivileged users (blocks a common + # local info-leak used in exploit chains). + "kernel.kptr_restrict" = 2; + # Restrict dmesg access to root. + "kernel.dmesg_restrict" = 1; + # Reverse-path filtering: drop packets with spoofed source addresses. + "net.ipv4.conf.all.rp_filter" = 1; + "net.ipv4.conf.default.rp_filter" = 1; + # Don't accept/send ICMP redirects (blocks a classic local MITM trick). + "net.ipv4.conf.all.accept_redirects" = false; + "net.ipv4.conf.default.accept_redirects" = false; + "net.ipv6.conf.all.accept_redirects" = false; + "net.ipv6.conf.default.accept_redirects" = false; + "net.ipv4.conf.all.send_redirects" = false; + "net.ipv4.conf.default.send_redirects" = false; + }; + + # Crash dumps can contain decrypted memory contents, passwords, keys — + # never write them to disk. + systemd.coredump.enable = false; + + # Blocks kexec-based kernel image tampering. + security.protectKernelImage = true; + + # DNS-over-TLS system-wide (Tor Browser already routes its own DNS + # through Tor, so this only affects everything else). Cloudflare + Quad9 + # both support DoT; "allow-downgrade" avoids hard failures on domains + # with broken DNSSEC deployment. + networking.nameservers = [ "1.1.1.1#cloudflare-dns.com" "9.9.9.9#dns.quad9.net" ]; + services.resolved = { + enable = true; + settings.Resolve = { + DNSSEC = "allow-downgrade"; + DNSOverTLS = "true"; + }; + }; + networking.networkmanager.dns = "systemd-resolved"; + + # MAC address randomization. + # WiFi: fully random per connection (you're on ethernet now, but this + # protects you if you ever use WiFi). + # Ethernet: "stable" — a consistent pseudonymous MAC per connection + # profile rather than your real hardware MAC, so it won't break any + # static DHCP reservation/MAC filtering on your OpenWRT router, while + # still not exposing your real hardware identity. + networking.networkmanager.wifi.macAddress = "random"; + networking.networkmanager.wifi.scanRandMacAddress = true; + networking.networkmanager.ethernet.macAddress = "stable"; +} diff --git a/modules/users.nix b/modules/users.nix new file mode 100644 index 0000000..fe92cf7 --- /dev/null +++ b/modules/users.nix @@ -0,0 +1,27 @@ +{ config, pkgs, ... }: + +{ + # Primary user account configuration (Access and settings only) + users.users."nexoniarz" = { + isNormalUser = true; + description = "Nexoniarz"; + extraGroups = [ "networkmanager" "wheel" "fuse" "i2c" "video" ]; + shell = pkgs.zsh; + }; + + # Default shell (ZSH) + programs.zsh.enable = true; + + # Web browser configuration + programs.firefox.enable = true; + + # Enable Steam with optimized system configurations + programs.steam = { + enable = true; + remotePlay.openFirewall = true; + dedicatedServer.openFirewall = true; + }; + + # Enable Flatpak support + services.flatpak.enable = true; +}