Add RTL-SDR support; switch to Limine + SDDM; drop Hyprland-era leftovers

RTL-SDR Blog V4. The V4 swapped the R820T2 tuner for an R828D behind a
built-in HF upconverter, so it needs the rtlsdrblog fork of librtlsdr --
the stock Osmocom one reports no supported devices. pkgs.rtl-sdr on this
channel already is that fork (rtl-sdr-blog 1.3.5), so hardware.rtl-sdr's
default package is correct as-is. That also blacklists the DVB-T drivers
that otherwise grab the dongle on plug-in, and ships udev rules handing it
to plugdev at 0660 -- hence plugdev in the user's groups, or every SDR app
needs root. Apps: SDR++ and rtl_433; the rtl_* CLI tools come with the
driver.

Both SDR apps are overridden to drop hardware we do not own. SDR++ and
rtl_433 default to supporting every radio ever made, which pulled in UHD
(1.4 GB of USRP firmware), BladeRF and LimeSuite. gqrx was the original
choice but reaches UHD through gr-osmosdr's gr-uhd feature; SDR++ uses no
GNU Radio at all. Per-package overrides rather than a global overlay, so
nothing else is affected.

Bootloader: systemd-boot -> Limine, themed to match the Gruvbox icon
theme. Limine installs to its own EFI directory and adds its own NVRAM
entry, so systemd-boot stays on the ESP as a firmware-menu fallback.

Display manager: lightdm -> SDDM with wayland.enable. Budgie in this
channel ships only a Wayland session (labwc) and was already running as
one; lightdm just handles that badly. SDDM's greeter runs on weston, not
kwin, so no KDE compositor comes along.

Qt defaults over Budgie's GTK/MATE apps. The Budgie module force-enables
gnome-terminal and installs nemo, eom, pluma, atril, engrampa, mate-calc
and mate-system-monitor; environment.budgie.excludePackages drops all of
them in favour of konsole, dolphin, gwenview, kate, okular, ark, kcalc and
plasma-systemmonitor. default-apps.nix rewritten to match: 113
associations, video to vlc, audio to elisa, text/code to kate, and
/etc/xdg/xdg-terminals.list pointing at konsole. Fixes a real bug there --
inode/directory was mapped to gwenview, so folders opened in the image
viewer.

Hyprland-era leftovers removed. system-telemetry and weather-fetch fed
Quickshell panels that no longer exist; brightness-ctl and hotspot-start
lost their keybinds. jq went with weather-fetch. Also dropped
gnome-screenshot, playerctl and imv. vault-toggle and strip-metadata stay
-- they never depended on Hyprland.

Comments trimmed throughout: deleted anything restating the code, kept the
ones recording a why you cannot recover by reading it (systemd.gpt_auto,
disable_ertm, ptrace_scope, the stable ethernet MAC, the mimeapps.list
parser gotcha). system.nix went 225 -> 166 lines.

README rewritten -- it still described Hyprland, Quickshell and Kitty.
Screenshot dropped.
This commit is contained in:
Nexoniarz 2026-09-05 19:38:56 +02:00
commit 33bf94bd4c
14 changed files with 177 additions and 396 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

View file

@ -1,16 +1,29 @@
# nexoniarz-config-nixos
My personal NixOS system configuration and dotfiles tailored for my daily usage and needs.
![Desktop Screenshot](Assets/screenshot1.png)
My personal NixOS system configuration.
## Overview
- **OS:** NixOS
- **WM / Compositor:** Hyprland (Wayland)
- **Bar / Panels:** Quickshell
- **Terminal:** Kitty
- **OS:** NixOS 26.05 (unstable channel)
- **Desktop:** Budgie on Wayland (labwc), SDDM
- **Bootloader:** Limine
- **Terminal:** Konsole
- **Shell:** Zsh
- **Audio:** PipeWire
- **GPU:** NVIDIA (open kernel modules)
## Layout
| File | Contents |
|---|---|
| `configuration.nix` | Imports only |
| `modules/system.nix` | Boot, networking, locale, audio, hardening |
| `modules/drivers.nix` | GPU, tablet, I2C, RTL-SDR |
| `modules/desktop.nix` | Budgie, theming |
| `modules/users.nix` | Account, shell, Steam |
| `modules/apps.nix` | Installed packages |
| `modules/scripts.nix` | Custom scripts in `modules/scripts/` |
| `modules/default-apps.nix` | MIME associations |
---

View file

@ -1,17 +1,15 @@
# 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 # NVIDIA Open GPU kernel modules and graphics
./modules/desktop.nix # Budgie desktop environment
./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
./modules/default-apps.nix # Default app associations (terminal, image viewer, archives), LibreOffice
./hardware-configuration.nix
./modules/system.nix
./modules/drivers.nix
./modules/desktop.nix
./modules/users.nix
./modules/apps.nix
./modules/scripts.nix
./modules/default-apps.nix
];
# Do not change this value. Read documentation before updating.

View file

@ -1,18 +1,37 @@
{ config, pkgs, ... }:
let
# Both SDR++ and rtl_433 default to supporting every radio ever made, which
# drags in UHD (USRP firmware), BladeRF and LimeSuite — ~1.5 GB for hardware
# we don't own. These two overrides cut that down to the RTL-SDR paths.
soapysdrLean = pkgs.soapysdr.override {
extraPackages = with pkgs; [ soapyrtlsdr soapyremote ];
};
rtl433Lean = pkgs.rtl_433.override { soapysdr-with-plugins = soapysdrLean; };
# Leaves the RTL-SDR, rtl_tcp, file and network sources on.
sdrppLean = pkgs.sdrpp.override {
soapy_source = false;
bladerf_source = false;
limesdr_source = false;
plutosdr_source = false;
airspy_source = false;
airspyhf_source = false;
hermes_source = false;
rfspace_source = false;
dragonlabs_source = false;
spectran_http_source = false;
};
in
{
# 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; [
tumbler # Thumbnails (paired with services.tumbler.enable; also used by Nemo)
gnome-screenshot # Screenshot capture (X11)
imv # Image viewer
brightnessctl # Backlight brightness (laptop panels)
ddcutil # DDC/CI brightness (external monitors)
lm_sensors # sensors-detect helps ddcutil find I2C buses
networkmanager # nmcli, for the Wi-Fi hotspot toggle
xdg-user-dirs # Resolves localized folder names (Obrazy, not Pictures, on pl_PL)
playerctl # MPRIS media control, for the function-key media binds
tumbler
brightnessctl
ddcutil
lm_sensors # sensors-detect helps ddcutil find the I2C buses
networkmanager
xdg-user-dirs # Resolves localized folder names (Obrazy, not Pictures)
# Apps
blender
@ -25,36 +44,31 @@
filezilla
chromium
obs-studio
easyeffects # Real-time mic/output effects (pitch, EQ, ...) — auto-creates a
# selectable "Easy Effects Source" virtual mic once an Input effect
# is added, no manual PipeWire routing needed
qpwgraph # Patchbay GUI — wires EasyEffects'/Soundux's output into the
# standalone "Virtual Microphone Sink" (see system.nix)
# Soundux (soundboard) isn't packaged in this nixpkgs channel anymore —
# installed via Flatpak instead (io.github.Soundux).
sdrppLean
rtl433Lean
# Games
prismlauncher
pkgs.osu-lazer-bin
osu-lazer-bin
# Dev: compilers, runtimes, languages
# Dev
glibc
musl
cmake
gnumake # Provides the 'make' command
gnumake
gcc
python3
openjdk25
claude-code # AI Developer Assistant
claude-code
];
# Global command-line utilities available everywhere
environment.systemPackages = with pkgs; [
git
gh # GitHub CLI — was only installed ad-hoc before (login didn't survive)
gh
vim
fastfetch
gparted
efibootmgr
firejail
gnupg
lmstudio
@ -66,10 +80,11 @@
kdePackages.kate
kdePackages.qqc2-desktop-style
linphonePackages.linphone-desktop
kdePackages.kcalc # Calculator (nothing filled this role before)
kdePackages.okular # Document/PDF viewer (nothing filled this role before)
kdePackages.elisa # Audio player (vlc/ffmpeg cover video already; nothing dedicated for audio)
kdePackages.konsole # Terminal (replaces kitty)
kdePackages.dolphin # File manager (replaces Thunar, alongside Budgie's default Nemo)
kdePackages.kcalc
kdePackages.okular
kdePackages.elisa
kdePackages.konsole
kdePackages.dolphin
kdePackages.plasma-systemmonitor
];
}

View file

@ -2,27 +2,26 @@
{
environment.systemPackages = with pkgs; [
libreoffice-qt6 # Qt6-styled build — fits the Qt theming work in theme.nix
# better than the default GTK3 build would.
libreoffice-qt6
];
# System-level fallback defaults (XDG spec: only consulted for a
# mimetype the user's own ~/.config/mimeapps.list doesn't already
# claim), so a future "Open With" choice always wins over this without
# needing to touch it. Mimetype lists taken directly from Gwenview's
# and Ark's own .desktop files, not guessed.
# GIO picks the terminal from here for "Open Terminal" style actions.
environment.etc."xdg/xdg-terminals.list".text = ''
org.kde.konsole.desktop
'';
# System-level fallbacks. Per the XDG spec these are only consulted for a
# mimetype the user's own ~/.config/mimeapps.list doesn't already claim,
# so any "Open With" choice you make later still wins over this.
#
# One mimetype=desktopfile pair per line: the semicolon only separates
# multiple desktop-file candidates for the SAME key, it doesn't chain
# separate mimetype=value pairs on one line — mimeapps.list parsers
# (verified against xdg-utils' own xdg-mime script) match by scanning
# for a line that STARTS WITH "mimetype=", so cramming several pairs
# onto one semicolon-joined line silently hides every key after the
# first from ever being found.
# One mimetype=desktopfile pair per line. The semicolon only separates
# multiple candidates for the SAME key — it does not chain separate pairs,
# and parsers match by scanning for a line starting with "mimetype=", so
# cramming several onto one line silently hides all but the first.
environment.etc."xdg/mimeapps.list".text =
let
gwenviewMimes = [
"inode/directory" "image/avif" "image/gif" "image/heif" "image/jpeg"
"image/avif" "image/gif" "image/heif" "image/jpeg"
"image/jxl" "image/png" "image/bmp" "image/x-eps" "image/x-icns"
"image/x-ico" "image/x-portable-bitmap" "image/x-portable-graymap"
"image/x-portable-pixmap" "image/x-xbitmap" "image/x-xpixmap"
@ -42,19 +41,40 @@
"application/x-7z-compressed" "application/vnd.rar" "application/zip"
"application/x-java-archive" "application/x-arj"
];
# Taken from Okular's own .desktop file, same as the other two lists.
okularMimes = [
"application/pdf" "application/x-pdf" "image/vnd.djvu" "application/epub+zip"
"application/x-fictionbook+xml" "application/x-mobipocket-ebook"
"image/x-portable-document" "application/x-cbr" "application/x-cbz"
"application/x-cbt" "application/x-cb7" "application/postscript"
];
vlcMimes = [
"video/mp4" "video/x-matroska" "video/webm" "video/x-msvideo"
"video/quicktime" "video/mpeg" "video/x-ms-wmv" "video/x-flv"
"video/3gpp" "video/ogg" "video/x-theora+ogg" "video/x-ogm+ogg"
"application/x-matroska" "application/vnd.rn-realmedia"
];
elisaMimes = [
"audio/mpeg" "audio/flac" "audio/x-flac" "audio/ogg" "audio/x-vorbis+ogg"
"audio/opus" "audio/x-opus+ogg" "audio/mp4" "audio/aac" "audio/x-wav"
"audio/wav" "audio/x-ms-wma" "audio/x-aiff" "audio/x-musepack"
];
kateMimes = [
"text/plain" "text/markdown" "text/x-csrc" "text/x-c++src" "text/x-chdr"
"text/x-c++hdr" "text/x-python" "text/x-java" "text/x-shellscript"
"text/x-nix" "text/css" "text/html" "text/xml" "application/json"
"application/x-yaml" "application/toml" "application/javascript"
"application/x-shellscript" "text/x-log"
];
line = desktop: mime: "${mime}=${desktop}";
in
''
[Default Applications]
inode/directory=org.kde.dolphin.desktop
${lib.concatMapStringsSep "\n" (line "org.kde.gwenview.desktop") gwenviewMimes}
${lib.concatMapStringsSep "\n" (line "org.kde.ark.desktop") arkMimes}
${lib.concatMapStringsSep "\n" (line "org.kde.okular.desktop") okularMimes}
${lib.concatMapStringsSep "\n" (line "vlc.desktop") vlcMimes}
${lib.concatMapStringsSep "\n" (line "org.kde.elisa.desktop") elisaMimes}
${lib.concatMapStringsSep "\n" (line "org.kde.kate.desktop") kateMimes}
'';
}

View file

@ -1,38 +1,50 @@
{ config, pkgs, ... }:
{
# --- Budgie: X11 desktop environment, lightdm login manager ---
# Budgie ships only a Wayland session here (labwc); xserver stays on for
# Xwayland and for the xkb layout below.
services.xserver.enable = true;
services.xserver.displayManager.lightdm.enable = true;
services.desktopManager.budgie.enable = true;
# SDDM replaces lightdm: Qt, and it actually launches Wayland sessions.
# Its own greeter runs on weston, not kwin, so no KDE compositor tags along.
services.displayManager.sddm = {
enable = true;
wayland.enable = true;
};
security.polkit.enable = true;
# Keyboard layout for console/X11/lightdm
services.xserver.xkb = {
layout = "pl";
variant = "";
};
# Thumbnails + trash/volume mount integration for Dolphin and Budgie's
# default Nemo file manager alike.
services.tumbler.enable = true;
services.gvfs.enable = true;
# Drop the xterm fallback terminal xserver.nix installs unconditionally —
# Konsole is the real terminal here.
# xserver.nix installs xterm unconditionally; Konsole is the terminal here.
services.xserver.excludePackages = [ pkgs.xterm ];
# GTK/Qt themselves stay at their stock defaults (no forced dark mode, no
# custom CSS) — only the icon and cursor theme are picked explicitly, via
# Budgie's own GSettings-override hook rather than a custom script.
# (extraGSettingsOverridePackages is for packages that ship their own
# gsettings-schema overrides, e.g. plugins — not theme packages, which
# just need to be on the system path for their icons/cursors to resolve.)
# Budgie's module installs GTK/MATE apps that duplicate the Qt ones in
# apps.nix. Excluding gnome-terminal also flips programs.gnome-terminal.enable
# off — the module gates that on this same list.
environment.budgie.excludePackages = with pkgs; [
nemo # -> dolphin
gnome-terminal # -> konsole
eom # -> gwenview
pluma # -> kate
atril # -> okular
engrampa # -> ark
mate-calc # -> kcalc
mate-system-monitor # -> plasma-systemmonitor
];
environment.systemPackages = [
pkgs.gruvbox-plus-icons
pkgs.bibata-cursors
];
services.desktopManager.budgie.extraGSettingsOverrides = ''
[org.gnome.desktop.interface]
icon-theme='Gruvbox-Plus-Dark'

View file

@ -1,43 +1,34 @@
{ 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" ];
boot.extraModprobeConfig = ''
# 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.
# DualSense/PS4 pads pair over Bluetooth then drop instantly; broken
# ERTM in many adapters. Disabling it is the standard fix.
options bluetooth disable_ertm=1
'';
# I2C bus, for DDC/CI monitor control (brightness) via ddcutil — this
# machine has no laptop backlight, only external DisplayPort monitors.
# Blog V4 needs the rtlsdrblog fork of librtlsdr, which is what
# pkgs.rtl-sdr already is here. Also blacklists the DVB-T drivers.
hardware.rtl-sdr.enable = true;
# For ddcutil — no laptop backlight here, only external DisplayPort.
hardware.i2c.enable = true;
# Load NVIDIA driver for X11 and Wayland
services.xserver.videoDrivers = [ "nvidia" ];
hardware.nvidia = {
# Required settings
modesetting.enable = true;
# Enable NVIDIA Open GPU Kernel Modules (Turing architecture or newer)
open = true;
# Enable the NVIDIA settings menu
nvidiaSettings = true;
# Use the stable driver package matching current kernel
package = config.boot.kernelPackages.nvidiaPackages.stable;
};
}

View file

@ -1,25 +1,14 @@
{ 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
];
}

View file

@ -1,56 +0,0 @@
#!/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

View file

@ -1,33 +0,0 @@
#!/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 <ssid> <password> <wpa2|wpa3> <auto|bg|a>
set -euo pipefail
ssid="${1:?usage: hotspot-start <ssid> <password> <wpa2|wpa3> <auto|bg|a>}"
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

View file

@ -1,62 +0,0 @@
#!/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"

View file

@ -1,32 +0,0 @@
#!/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&current=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"

View file

@ -1,31 +1,44 @@
{ config, lib, pkgs, ... }:
{
# Bootloader & Kernel
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.loader.systemd-boot.configurationLimit = 5;
boot.loader.limine = {
enable = true;
maxGenerations = 5;
# Gruvbox, to match the icon theme.
style = {
wallpapers = [ ]; # Solid background; a path here would be used instead
interface = {
branding = "NixOS";
brandingColor = "fabd2f";
helpColor = "928374";
helpColorBright = "d79921";
};
graphicalTerminal = {
foreground = "ebdbb2";
background = "00282828";
brightForeground = "fbf1c7";
palette = "282828;cc241d;98971a;d79921;458588;b16286;689d6a;a89984";
brightPalette = "928374;fb4934;b8bb26;fabd2f;83a598;d3869b;8ec07c;ebdbb2";
margin = 32;
};
};
};
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 = {
@ -47,10 +60,8 @@
options = "--delete-older-than 7d";
};
# Console keymap
console.keyMap = "pl2";
# Sound and Audio (Pipewire)
security.rtkit.enable = true;
services.pulseaudio.enable = false;
services.pipewire = {
@ -58,36 +69,6 @@
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
# Standalone "Virtual Microphone" device, fully independent of the real
# hardware mic — always present regardless of what's running, so apps
# like Discord keep defaulting to the real mic untouched, and only pick
# this up if explicitly selected. Feed anything into "Virtual
# Microphone Sink" (e.g. via qpwgraph/helvum, routing EasyEffects' or
# Soundux's output to it) and it comes out the other side as "Virtual
# Microphone", selectable like any other input device.
extraConfig.pipewire."99-virtual-microphone" = {
"context.modules" = [
{
name = "libpipewire-module-loopback";
args = {
"node.description" = "Virtual Microphone";
"capture.props" = {
"node.name" = "virtual_mic_sink";
"node.description" = "Virtual Microphone Sink";
"media.class" = "Audio/Sink";
"audio.position" = "FL,FR";
};
"playback.props" = {
"node.name" = "virtual_mic_source";
"node.description" = "Virtual Microphone";
"media.class" = "Audio/Source";
"audio.position" = "FL,FR";
};
};
}
];
};
};
hardware.bluetooth = {
@ -95,18 +76,10 @@
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;
Experimental = true; # Battery level of connected devices
FastConnectable = true; # Faster reconnects, more power draw
};
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;
};
};
@ -114,26 +87,22 @@
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-rofi; # Reuses the Rofi theme, no extra toolkit
pinentryPackage = pkgs.pinentry-rofi;
};
security.sudo.extraConfig = ''
Defaults pwfeedback
'';
# Printing services
services.printing.enable = true;
nixpkgs.config.allowUnfree = true;
@ -152,8 +121,7 @@
roboto
];
# --- Loader shim for running non-Nix-packaged binaries (e.g. the official
# Tor Browser bundle) that expect a standard FHS dynamic linker/libraries. ---
# Lets non-Nix binaries (e.g. the Tor Browser bundle) find an FHS linker.
programs.nix-ld.enable = true;
programs.nix-ld.libraries = with pkgs; [
glib gtk3 gdk-pixbuf pango cairo atk
@ -165,61 +133,34 @@
];
environment.systemPackages = [ pkgs.file ];
# --- Hardening: swap, kernel sysctls, DNS, MAC randomization ---
# --- Hardening ---
# 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.
# zram instead of disk swap: nothing sensitive is written to disk in
# plaintext. systemd's gpt-auto-generator would otherwise re-activate the
# old swap partition regardless of swapDevices, hence the kernel param.
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;
# Restrict ptrace to a process's own children — blocks a common local
# credential-scraping/process-injection vector between unrelated
# processes. Only bites ad-hoc `gdb -p <pid>`-style attaches to a
# process that isn't your debugger's own child.
# Only bites ad-hoc `gdb -p <pid>` on a non-child process.
"kernel.yama.ptrace_scope" = 1;
# Drop the kernel-uptime-derived TCP timestamp from outgoing packets
# (a passive fingerprinting/uptime-leak vector). Negligible throughput
# impact on a normal home connection.
"net.ipv4.tcp_timestamps" = 0;
};
# 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;
@ -230,26 +171,17 @@
};
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.
# Ethernet stays "stable" (pseudonymous but consistent) so static DHCP
# reservations on the router keep working; Wi-Fi is fully random.
networking.networkmanager.wifi.macAddress = "random";
networking.networkmanager.wifi.scanRandMacAddress = true;
networking.networkmanager.ethernet.macAddress = "stable";
# Disable NetworkManager's periodic external connectivity probe (normally
# pings a canary URL to detect captive portals) — one less routine
# external phone-home, no functional loss on a directly-connected desktop.
# Disable NetworkManager's periodic captive-portal probe.
networking.networkmanager.settings.connectivity = {
uri = "";
interval = 0;
};
# Budgie's module turns this on by default (for Budgie Control Center's
# Location Services panel) — nothing in this config needs location data.
services.geoclue2.enable = false;
}

View file

@ -1,27 +1,21 @@
{ config, pkgs, ... }:
{
# Primary user account configuration (Access and settings only)
users.users."nexoniarz" = {
isNormalUser = true;
description = "Nexoniarz";
extraGroups = [ "networkmanager" "wheel" "fuse" "i2c" "video" ];
extraGroups = [ "networkmanager" "wheel" "fuse" "i2c" "video" "plugdev" ];
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;
}