Files
nixos-haus/current
2025-10-02 15:13:47 +03:00

1882 lines
53 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
192.168.0.131 news.bigbox.local
192.168.0.131 gitea.bigbox.local
192.168.0.131 media.bigbox.local
192.168.0.131 torrent.bigbox.local
192.168.0.131 kiwix.bigbox.local
192.168.0.131 syncthing.bigbox.local
192.168.0.131 speed.bigbox.local
192.168.0.131 ai.bigbox.local
error:
… while calling anonymous lambda
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/attrsets.nix:1696:14:
1695| zipAttrsWith (
1696| n: values:
| ^
1697| let
… while calling the 'head' builtin
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/attrsets.nix:1701:13:
1700| if length values == 1 || pred here (elemAt values 1) (head values) then
1701| head values
| ^
1702| else
… while calling anonymous lambda
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/attrsets.nix:1193:17:
1192| mapAttrs (
1193| name: value:
| ^
1194| if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value
… from call site
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/attrsets.nix:1194:85:
1193| name: value:
1194| if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value
| ^
1195| );
… while calling anonymous lambda
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/modules.nix:275:71:
274| # For definitions that have an associated option
275| declaredConfig = mapAttrsRecursiveCond (v: !isOption v) (_: v: v.value) options;
| ^
276|
… while evaluating the attribute 'value'
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/modules.nix:1118:7:
1117| // {
1118| value = addErrorContext "while evaluating the option `${showOption loc}':" value;
| ^
1119| inherit (res.defsFinal') highestPrio;
… while evaluating the option `system.build.toplevel':
… while evaluating the attribute 'mergedValue'
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/modules.nix:1166:5:
1165| # Type-check the remaining definitions, and merge them. Or throw if no definitions.
1166| mergedValue =
| ^
1167| if isDefined then
… while evaluating a branch condition
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/modules.nix:1167:7:
1166| mergedValue =
1167| if isDefined then
| ^
1168| if type.merge ? v2 then
… while evaluating the attribute 'values'
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/modules.nix:1160:9:
1159| {
1160| values = defsSorted;
| ^
1161| inherit (defsFiltered) highestPrio;
… while evaluating a branch condition
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/modules.nix:1154:11:
1153| # Avoid sorting if we don't have to.
1154| if any (def: def.value._type or "" == "order") defsFiltered.values then
| ^
1155| sortProperties defsFiltered.values
… while calling the 'any' builtin
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/modules.nix:1154:14:
1153| # Avoid sorting if we don't have to.
1154| if any (def: def.value._type or "" == "order") defsFiltered.values then
| ^
1155| sortProperties defsFiltered.values
… while evaluating the attribute 'values'
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/modules.nix:1331:7:
1330| {
1331| values = concatMap (def: if getPrio def == highestPrio then [ (strip def) ] else [ ]) defs;
| ^
1332| inherit highestPrio;
… while calling the 'concatMap' builtin
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/modules.nix:1331:16:
1330| {
1331| values = concatMap (def: if getPrio def == highestPrio then [ (strip def) ] else [ ]) defs;
| ^
1332| inherit highestPrio;
… while calling the 'concatMap' builtin
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/modules.nix:1134:26:
1133| # Process mkMerge and mkIf properties.
1134| defsNormalized = concatMap (
| ^
1135| m:
… while calling anonymous lambda
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/modules.nix:1135:11:
1134| defsNormalized = concatMap (
1135| m:
| ^
1136| map (
… while calling the 'map' builtin
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/modules.nix:1136:11:
1135| m:
1136| map (
| ^
1137| value:
… while evaluating definitions from `/nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/nixos/modules/system/activation/top-level.nix':
… from call site
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/modules.nix:1145:80:
1144| }
1145| ) (addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
| ^
1146| ) defs;
… while calling 'dischargeProperties'
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/modules.nix:1282:5:
1281| dischargeProperties =
1282| def:
| ^
1283| if def._type or "" == "merge" then
… while evaluating a branch condition
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/modules.nix:1283:5:
1282| def:
1283| if def._type or "" == "merge" then
| ^
1284| concatMap dischargeProperties def.contents
… while evaluating the attribute 'value'
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/modules.nix:805:21:
804| inherit (module) file;
805| inherit value;
| ^
806| }) module.config
… from call site
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/nixos/modules/system/activation/top-level.nix:83:26:
82| # Handle assertions and warnings
83| baseSystemAssertWarn = lib.asserts.checkAssertWarn config.assertions config.warnings baseSystem;
| ^
84|
… while calling 'checkAssertWarn'
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/asserts.nix:193:27:
192| checkAssertWarn =
193| assertions: warnings: val:
| ^
194| let
… while calling the 'throw' builtin
at /nix/store/jc3mfa7ybzh32bkvdj5xiib1vkx8jy6x-source/lib/asserts.nix:198:7:
197| if failedAssertions != [ ] then
198| throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}"
| ^
199| else
error:
Failed assertions:
- Exactly one of users.users.user.isSystemUser and users.users.user.isNormalUser must be set.
- users.users.user.group is unset. This used to default to
nogroup, but this is unsafe. For example you can create a group
for this user with:
users.users.user.group = "user";
users.groups.user = {};
Command 'nix --extra-experimental-features 'nix-command flakes' build --print-out-paths '/home/boxuser/haus#nixosConfigurations."box".config.system.build.toplevel' --show-trace --no-link' returned non-zero exit status 1.
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1759036355,
"narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}
{
description = "My modular NixOS configuration";
inputs = {
# nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
# unstable.url = "github:NixOS/nixpkgs/nixos-unstable"
# Add other flake inputs if needed
};
outputs = {
self,
nixpkgs,
...
} @ inputs: {
nixosConfigurations = {
puter = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [./hosts/puter/default.nix];
specialArgs = {inherit inputs;};
};
box = nixpkgs.lib.nixosSystem {
system = "aarch64-linux";
modules = [./hosts/box/default.nix];
specialArgs = {inherit inputs;};
};
};
};
}
hosts := "puter box"
_check-host host:
if ! echo "{{hosts}}" | grep -qw "{{host}}"; then \
echo "Unknown host:{{host}} \n Must be one of: {{hosts}}"; exit 1;\
fi
update-config: host: _check-host
alejandra .
nix flake update
git add .
git commit
sudo nixos-rebuild switch --upgrade --flake ~/haus#{{host}}
git push
update: host: _check-host
nix flake update
git add .
git commit -m "nix flake update"
sudo nixos-rebuild switch --upgrade --flake ~/haus#{{host}}
git push
update-and-reboot: host: _check-host
nix flake update
git add .
git commit -m "nix flake update"
sudo nixos-rebuild boot --upgrade --flake ~/haus#{{host}}
git push
update-config-and-reboot: host: _check-host
alejandra .
nix flake update
git add .
git commit
sudo nixos-rebuild boot --upgrade --flake ~/haus#{{host}}
git push
upgrade: host: _check-host
sudo nixos-rebuild switch --upgrade --flake ~/haus#{{host}}
clean:
sudo nix-env --delete-generations old
sudo nix-collect-garbage --delete-older-than 30d
sudo nix store optimise
sudo rm -rf /nix/var/nix/downloads/*
sudo journalctl --vacuum-time=30d
# Flakified NixOS config for the whole haus
A better version of the [initial config](http://192.168.0.131:3000/kenny/nixos-cfg) but with both common and specific configs so that we can _easily grow a coherent park_.
# Achieved so far
- [X] Automated puter workflow with justfile
- [X] Modulable set-up with apps and services
- [ ] Box config
- [ ] Adapt justfile for different machines
- [ ] BigBox config
{
config,
pkgs,
...
}: {
environment.systemPackages = with pkgs; [
libraspberrypi
raspberrypi-eeprom
docker-compose
];
}
{config, lib, pkgs, modulesPath, ...}:
{
services.adguardhome = {
enable = true;
settings = {
http = {
# You can select any ip and port, just make sure to open firewalls where needed
address = "192.168.0.101:3003";
};
dns = {
upstream_dns = [
# Example config with quad9
"9.9.9.11"
"149.112.112.11"
# Uncomment the following to use a local DNS service (e.g. Unbound)
# Additionally replace the address & port as needed
# "127.0.0.1:5335"
];
};
filtering = {
protection_enabled = true;
filtering_enabled = true;
parental_enabled = false; # Parental control-based DNS requests filtering.
safe_search = {
enabled = false; # Enforcing "Safe search" option for search engines, when possible.
};
};
# The following notation uses map
# to not have to manually create {enabled = true; url = "";} for every filter
# This is, however, fully optional
filters = map(url: { enabled = true; url = url; }) [
"https://adguardteam.github.io/HostlistsRegistry/assets/filter_9.txt" # The Big List of Hacked Malware Web Sites
"https://adguardteam.github.io/HostlistsRegistry/assets/filter_11.txt" # malicious url blocklist
];
};
};
}
{config, lib, pkgs, modulesPath, ...}:
{
}
{config, lib, pkgs, modulesPath, ...}:
{
services.chrony = {
enable = true;
enableNTS = true;
servers = ["nts.teambelgium.net" "ptbtime1.ptb.de" "paris.time.system76.com"];
};
}
# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page, on
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
{ config, lib, pkgs, ... }:
{
#imports =
# [ # Include the results of the hardware scan.
# <nixos-hardware/raspberry-pi/4>
# ./hardware-configuration.nix
# ];
imports = [./dns.nix ./radicale.nix ./glance.nix];
boot.blacklistedKernelModules = [
"bluetooth"
"btbcm"
"hci_uart"
"hci_bcm"
];
boot = {
kernelPackages = pkgs.linuxKernel.packages.linux_rpi4;
initrd.availableKernelModules = [ "xhci_pci" "usbhid" "usb_storage" ];
loader = {
grub.enable = false;
generic-extlinux-compatible.enable = true;
};
};
fileSystems = {
"/" = {
device = "/dev/disk/by-label/NIXOS_SD";
fsType = "ext4";
options = [ "noatime" ];
};
};
services.chrony = {
enable = true;
enableNTS = true;
servers = ["nts.teambelgium.net" "ptbtime1.ptb.de" "paris.time.system76.com"];
};
hardware.enableRedistributableFirmware = true;
networking.networkmanager = {
enable = true;
dns = "none";
};
networking.nameservers = [ "192.168.0.101" "9.9.9.11" "149.112.112.11" ];
networking.hostName = "box"; # Define your hostname.
# Pick only one of the below networking options.
# networking.wireless = {
# enable = true;
# networks.Ligma.psk = "rox+theo.ZNG6";
# interfaces = [ "wlan0" ];
# };
# networking.networkmanager.enable = true; # Easiest to use and most distros use this by default.
# networking.networkmanager.wifi.powersave = false;
services.openssh.enable = true;
# Set your time zone.
time.timeZone = "Europe/Bucharest";
# Configure network proxy if necessary
# networking.proxy.default = "http://user:password@proxy:port/";
# networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
# Select internationalisation properties.
# i18n.defaultLocale = "en_US.UTF-8";
# console = {
# font = "Lat2-Terminus16";
# keyMap = "us";
# useXkbConfig = true; # use xkb.options in tty.
# };
# Enable the X11 windowing system.
# services.xserver.enable = true;
# Configure keymap in X11
# services.xserver.xkb.layout = "us";
# services.xserver.xkb.options = "eurosign:e,caps:escape";
# Enable CUPS to print documents.
# services.printing.enable = true;
# Enable sound.
# services.pulseaudio.enable = true;
# OR
# services.pipewire = {
# enable = true;
# pulse.enable = true;
# };
# Enable touchpad support (enabled default in most desktopManager).
# services.libinput.enable = true;
# Define a user account. Don't forget to set a password with passwd.
users.users.boxuser = {
isNormalUser = true;
extraGroups = [ "wheel" "docker" "networkmanager" ]; # Enable sudo for the user.
password = "boxuser";
packages = with pkgs; [
tree
];
};
security.sudo = {
enable = true;
wheelNeedsPassword = false;
};
# programs.firefox.enable = true;
# List packages installed in system profile.
# You can use https://search.nixos.org/ to find more packages (and options).
environment.systemPackages = with pkgs; [
vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
wget
libraspberrypi
raspberrypi-eeprom
htop
docker-compose
git
];
# Some programs need SUID wrappers, can be configured further or are
# started in user sessions.
# programs.mtr.enable = true;
# programs.gnupg.agent = {
# enable = true;
# enableSSHSupport = true;
# };
# List services that you want to enable:
# Enable the OpenSSH daemon.
# services.openssh.enable = true;
# Open ports in the firewall.
# networking.firewall.allowedTCPPorts = [ 80 5232 ];
# networking.firewall.allowedUDPPorts = [ 5232 ];
# Or disable the firewall altogether.
networking.firewall.enable = false;
# Copy the NixOS configuration file and link it from the resulting system
# (/run/current-system/configuration.nix). This is useful in case you
# accidentally delete configuration.nix.
# system.copySystemConfiguration = true;
# This option defines the first version of NixOS you have installed on this particular machine,
# and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions.
#
# Most users should NEVER change this value after the initial install, for any reason,
# even if you've upgraded your system to a new NixOS release.
#
# This value does NOT affect the Nixpkgs version your packages and OS are pulled from,
# so changing it will NOT upgrade your system - see https://nixos.org/manual/nixos/stable/#sec-upgrading for how
# to actually do that.
#
# This value being lower than the current NixOS release does NOT mean your system is
# out of date, out of support, or vulnerable.
#
# Do NOT change this value unless you have manually inspected all the changes it would make to your configuration,
# and migrated your data accordingly.
#
# For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
system.stateVersion = "25.05"; # Did you read the comment?
}
{config, lib, pkgs, modulesPath, ...}:
{
services.crab-hole = {
enable = true;
settings = {
api = {
port = 8080;
listen = "192.168.0.101";
# optional (default = false)
show_doc = true; # OpenAPI doc loads content from third party websites
# optional
admin_key = "admin";
};
};
settings = {
blocklist = {
include_subdomains = true;
lists = [
#"https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/fakenews-gambling-porn/hosts"
"https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"
"https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt"
"https://energized.pro/nsfw/hosts.txt"
"https://energized.pro/antipopads-re/hosts.txt"
];
};
downstream = [
{
protocol = "udp";
listen = "192.168.0.101";
port = 53;
}
];
upstream = {
validate = true;
name_servers = [
{
socket_addr = "9.9.9.11:853";
protocol = "tls";
tls_dns_name = "tls://dns11.quad9.net";
trust_nx_responses = false;
}
];
};
};
};
}
{config, lib, pkgs, modulesPath, ...}:
{
services.pihole-web = {
enable = true;
ports = [ 8085 ];
};
services.pihole-ftl = {
enable = true;
#openFirewallDNS = true;
#openFirewallDHCP = true;
queryLogDeleter.enable = true;
lists = [
{
url = "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts";
# Alternatively, use the file from nixpkgs. Note its contents won't be
# automatically updated by Pi-hole, as it would with an online URL.
# url = "file://${pkgs.stevenblack-blocklist}/hosts";
description = "Steven Black's unified adlist";
}
{
url = "https://codeberg.org/hagezi/mirror2/raw/branch/main/dns-blocklists/adblock/ultimate.txt";
description = "Hagezi Ultimate";
}
{
url = "https://codeberg.org/hagezi/mirror2/raw/branch/main/dns-blocklists/adblock/nsfw.txt";
description = "Hazegi NSFW";
}
];
settings = {
files.macvendor = lib.mkForce "/var/lib/pihole/macvendor.db";
dns = {
domainNeeded = true;
expandHosts = true;
interface = "end0";
listeningMode = "BIND";
domain = "lan";
upstreams = [ "9.9.9.11" ];
hosts = [
"*.bigbox.lan 192.168.0.131"
"bigbox.lan 192.168.0.131"
"inv.bigbox.lan 192.168.0.131"
"media.bigbox.lan 192.168.0.131"
"type.bigbox.lan 192.168.0.131"
"bookmarks.bigbox.lan 192.168.0.131"
"bin.bigbox.lan 192.168.0.131"
"ai.bigbox.lan 192.168.0.131"
"speed.bigbox.lan 192.168.0.131"
"syncthing.bigbox.lan 192.168.0.131"
"kiwix.bigbox.lan 192.168.0.131"
"torrent.bigbox.lan 192.168.0.131"
"gitea.bigbox.lan 192.168.0.131"
"news.bigbox.lan 192.168.0.131"
];
};
dhcp = {
active = false;
router = "192.168.0.1";
start = "192.168.0.2";
end = "192.168.0.99";
netmask = "255.255.255.0";
leaseTime = "1d";
#ipv6 = true;
multiDNS = true;
rapidCommit = true;
hosts = [
# Static address for the current host
"d8:3a:dd:9a:c1:99,192.168.0.101,box,infinite"
"80:ce:62:ed:ba:2b,192.168.0.131,bigbox,infinite"
"60:30:d4:6b:89:10,192.168.0.100,puter,infinite"
];
};
misc.dnsmasq_lines = [
# This DHCP server is the only one on the network
#"dhcp-authoritative"
# Source: https://data.iana.org/root-anchors/root-anchors.xml
"trust-anchor=.,38696,8,2,683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A4C0FB2B16"
];
};
};
}
{
config,
lib,
pkgs,
modulesPath,
...
}:
{
services.glance = {
enable = true;
settings = {
server.port = 8080;
server.host = "192.168.0.101";
pages = [
{
name = "HomeLAN";
#hide-desktop-navigation = true;
columns = [
{
size = "small";
widgets = [
{
type = "search";
search-engine = "startpage";
new-tab = true;
autofocus = true;
placeholder = "Startpage | @in, @media, @pkg, @git";
bangs = [
{
title = "Invidious";
shortcut = "@in";
url = "http://192.168.0.131:4000/search?q={QUERY}";
}
{
title = "NixOS Packages";
shortcut = "@pkg";
url = "https://search.nixos.org/packages?query={QUERY}";
}
{
title = "Jellyfin";
shortcut = "@media";
url = "http://192.168.0.131:8096/web/#/search.html?query={QUERY}";
}
{
title = "Github";
shortcut = "@git";
url = "https://github.com/search?q={QUERY}&type=repositories";
}
];
}
{
type = "monitor";
cache = "1m";
style = "compact";
title = "Health";
sites = [
{
title = "Gitea";
url = "http://192.168.0.131:3010/user/login";
}
{
title = "Jellyfin";
url = "http://192.168.0.131:8096/web/#/home.html";
}
{
title = "Invidious";
url = "http://192.168.0.131:4000";
}
{
title = "Transmission";
url = "http://192.168.0.131:9091";
}
{
title = "OpenWebUI";
url = "http://192.168.0.131:3005/auth?redirect=%2F";
}
{
title = "FreshRSS";
url = "http://192.168.0.131:8011";
}
{
title = "Karakeep";
url = "http://192.168.0.131:5000";
}
{
title = "Syncthing";
url = "http://192.168.0.131:8384";
}
{
title = "Radicale";
url = "http://192.168.0.101:5232";
}
{
title = "NetData";
url = "http://192.168.0.131:19999/v3";
}
{
title = "Keybr";
url = "http://192.168.0.131:3000";
}
{
title = "Speedtest Tracker";
url = "http://192.168.0.131:8765/";
}
{
title = "PiHole";
url = "http://192.168.0.101:8085";
}
{
title = "Dozzle";
url = "http://192.168.0.131:8009";
}
{
title = "TP-Link";
url = "http://192.168.0.1";
}
];
}
{
type = "server-stats";
name = "Box";
}
];
}
{
size = "full";
widgets = [
{
type = "group";
widgets = [
{
type = "hacker-news";
limit = 40;
collapse-after = 5;
}
{
type = "lobsters";
sort-by = "hot";
#tags = [ "c" "rust" "networking" ];
tags = [ "rust" "networking" "c" "culture" "law" "cryptography" "hardware" "science" "linux" "windows" "nix" "android" "privacy" "security" "virtualization" "editors" "systemd" "vim" ];
}
];
}
{
type = "group";
widgets = [
{
type = "iframe";
source = "http://192.168.0.131:19999/v3";
height = 800;
}
];
}
];
}
{
size = "small";
widgets = [
{
type = "to-do";
}
{
type = "weather";
location = "Bucharest, Romania";
units = "metric";
hour-format = "24h";
}
{
type = "twitch-channels";
channels = [
"theprimeagen"
"tsoding"
"euuhhh"
];
}
];
}
];
}
];
};
};
}
# 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 = [ "xhci_pci" "usbhid" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-uuid/44444444-4444-4444-8888-888888888888";
fsType = "ext4";
};
swapDevices = [ ];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.end0.useDHCP = lib.mkDefault true;
# networking.interfaces.wlan0.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux";
}
clean:
sudo nix-env --delete-generations old
sudo nix-collect-garbage --delete-older-than 30d
sudo nix store optimise
sudo rm -rf /nix/var/nix/downloads/*
sudo journalctl --vacuum-time=30d
{config, lib, pkgs, modulesPath, ...}:
{
services.radicale = {
enable = true;
settings.server.hosts = [ "192.168.0.101:5232" ];
settings.auth.type = "htpasswd";
#settings.auth.htpasswd_filename = "/home/boxuser/radicale/config/users";
settings.auth.htpasswd_encryption = "plain";
#settings.storage.filesystem_folder = "/home/boxuser/radicale/data/collections";
};
}
{
config,
pkgs,
lib,
...
}: {
imports = [
./dns.nix
./radicale.nix
./glance.nix
./packages.nix
./hardware-configuration.nix
../../modules/raspberrypi.nix
../../modules/common/networking.nix
../../modules/common/shell.nix
./user.nix
];
networking.hostName = "box";
networking.firewall.enable = false;
# This will be overridden by system/default.nix
system.stateVersion = "25.05";
}
{
config,
lib,
pkgs,
modulesPath,
...
}: {
services.pihole-web = {
enable = true;
ports = [8085];
};
services.pihole-ftl = {
enable = true;
#openFirewallDNS = true;
#openFirewallDHCP = true;
queryLogDeleter.enable = true;
lists = [
{
url = "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts";
# Alternatively, use the file from nixpkgs. Note its contents won't be
# automatically updated by Pi-hole, as it would with an online URL.
# url = "file://${pkgs.stevenblack-blocklist}/hosts";
description = "Steven Black's unified adlist";
}
{
url = "https://codeberg.org/hagezi/mirror2/raw/branch/main/dns-blocklists/adblock/ultimate.txt";
description = "Hagezi Ultimate";
}
{
url = "https://codeberg.org/hagezi/mirror2/raw/branch/main/dns-blocklists/adblock/nsfw.txt";
description = "Hazegi NSFW";
}
];
settings = {
files.macvendor = lib.mkForce "/var/lib/pihole/macvendor.db";
dns = {
domainNeeded = true;
expandHosts = true;
interface = "end0";
listeningMode = "BIND";
domain = "lan";
upstreams = ["9.9.9.11"];
hosts = [
"*.bigbox.lan 192.168.0.131"
"bigbox.lan 192.168.0.131"
"inv.bigbox.lan 192.168.0.131"
"media.bigbox.lan 192.168.0.131"
"type.bigbox.lan 192.168.0.131"
"bookmarks.bigbox.lan 192.168.0.131"
"bin.bigbox.lan 192.168.0.131"
"ai.bigbox.lan 192.168.0.131"
"speed.bigbox.lan 192.168.0.131"
"syncthing.bigbox.lan 192.168.0.131"
"kiwix.bigbox.lan 192.168.0.131"
"torrent.bigbox.lan 192.168.0.131"
"gitea.bigbox.lan 192.168.0.131"
"news.bigbox.lan 192.168.0.131"
];
};
dhcp = {
active = false;
router = "192.168.0.1";
start = "192.168.0.2";
end = "192.168.0.99";
netmask = "255.255.255.0";
leaseTime = "1d";
#ipv6 = true;
multiDNS = true;
rapidCommit = true;
hosts = [
# Static address for the current host
"d8:3a:dd:9a:c1:99,192.168.0.101,box,infinite"
"80:ce:62:ed:ba:2b,192.168.0.131,bigbox,infinite"
"60:30:d4:6b:89:10,192.168.0.100,puter,infinite"
];
};
misc.dnsmasq_lines = [
# This DHCP server is the only one on the network
#"dhcp-authoritative"
# Source: https://data.iana.org/root-anchors/root-anchors.xml
"trust-anchor=.,38696,8,2,683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A4C0FB2B16"
];
};
};
}
{
config,
pkgs,
...
}: {
}
{
config,
lib,
pkgs,
modulesPath,
...
}: {
services.glance = {
enable = true;
settings = {
server.port = 8080;
server.host = "192.168.0.101";
pages = [
{
name = "HomeLAN";
#hide-desktop-navigation = true;
columns = [
{
size = "small";
widgets = [
{
type = "search";
search-engine = "startpage";
new-tab = true;
autofocus = true;
placeholder = "Startpage | @in, @media, @pkg, @git";
bangs = [
{
title = "Invidious";
shortcut = "@in";
url = "http://192.168.0.131:4000/search?q={QUERY}";
}
{
title = "NixOS Packages";
shortcut = "@pkg";
url = "https://search.nixos.org/packages?query={QUERY}";
}
{
title = "Jellyfin";
shortcut = "@media";
url = "http://192.168.0.131:8096/web/#/search.html?query={QUERY}";
}
{
title = "Github";
shortcut = "@git";
url = "https://github.com/search?q={QUERY}&type=repositories";
}
];
}
{
type = "monitor";
cache = "1m";
style = "compact";
title = "Health";
sites = [
{
title = "Gitea";
url = "http://192.168.0.131:3010/user/login";
}
{
title = "Jellyfin";
url = "http://192.168.0.131:8096/web/#/home.html";
}
{
title = "Invidious";
url = "http://192.168.0.131:4000";
}
{
title = "Transmission";
url = "http://192.168.0.131:9091";
}
{
title = "OpenWebUI";
url = "http://192.168.0.131:3005/auth?redirect=%2F";
}
{
title = "FreshRSS";
url = "http://192.168.0.131:8011";
}
{
title = "Karakeep";
url = "http://192.168.0.131:5000";
}
{
title = "Syncthing";
url = "http://192.168.0.131:8384";
}
{
title = "Radicale";
url = "http://192.168.0.101:5232";
}
{
title = "NetData";
url = "http://192.168.0.131:19999/v3";
}
{
title = "Keybr";
url = "http://192.168.0.131:3000";
}
{
title = "Speedtest Tracker";
url = "http://192.168.0.131:8765/";
}
{
title = "PiHole";
url = "http://192.168.0.101:8085";
}
{
title = "Dozzle";
url = "http://192.168.0.131:8009";
}
{
title = "TP-Link";
url = "http://192.168.0.1";
}
];
}
{
type = "server-stats";
name = "Box";
}
];
}
{
size = "full";
widgets = [
{
type = "group";
widgets = [
{
type = "hacker-news";
limit = 40;
collapse-after = 5;
}
{
type = "lobsters";
sort-by = "hot";
#tags = [ "c" "rust" "networking" ];
tags = ["rust" "networking" "c" "culture" "law" "cryptography" "hardware" "science" "linux" "windows" "nix" "android" "privacy" "security" "virtualization" "editors" "systemd" "vim"];
}
];
}
{
type = "group";
widgets = [
{
type = "iframe";
source = "http://192.168.0.131:19999/v3";
height = 800;
}
];
}
];
}
{
size = "small";
widgets = [
{
type = "to-do";
}
{
type = "weather";
location = "Bucharest, Romania";
units = "metric";
hour-format = "24h";
}
{
type = "twitch-channels";
channels = [
"theprimeagen"
"tsoding"
"euuhhh"
];
}
];
}
];
}
];
};
};
}
{
config,
lib,
pkgs,
...
}: {
boot.blacklistedKernelModules = [
"bluetooth"
"btbcm"
"hci_uart"
"hci_bcm"
];
boot = {
kernelPackages = pkgs.linuxKernel.packages.linux_rpi4;
initrd.availableKernelModules = ["xhci_pci" "usbhid" "usb_storage"];
loader = {
grub.enable = false;
generic-extlinux-compatible.enable = true;
};
};
fileSystems = {
"/" = {
device = "/dev/disk/by-label/NIXOS_SD";
fsType = "ext4";
options = ["noatime"];
};
};
hardware.enableRedistributableFirmware = true;
}
{
config,
pkgs,
...
}: {
imports = [
../../modules/userapps/utils.nix
];
services.openssh.enable = true;
security.sudo = {
enable = true;
wheelNeedsPassword = false;
};
}
{
config,
lib,
pkgs,
modulesPath,
...
}: {
services.radicale = {
enable = true;
settings.server.hosts = ["192.168.0.101:5232"];
settings.auth.type = "htpasswd";
#settings.auth.htpasswd_filename = "/home/boxuser/radicale/config/users";
settings.auth.htpasswd_encryption = "plain";
#settings.storage.filesystem_folder = "/home/boxuser/radicale/data/collections";
};
}
{ config, pkgs, lib, ...}:
{
users.users.boxuser = {
isNormalUser = true;
group = "boxuser";
extraGroups = ["wheel" "docker" "networkmanager"]; # Enable sudo for the user.
hashedPassword = "$6$Gk6L21XBSf.YbfU1$eadMLbwvAgudTjPOLCsZfRNxfGptARnAazhs0xz/GcNEYGQS/GjLov/jJsHnPIKBNIPQJEG4XhZ3K097bfi1c1";
packages = with pkgs; [
fastfetch
];
};
users.groups.boxuser = {};
}
{
config,
pkgs,
...
}: {
imports = [
./hardware-configuration.nix
#./syncthing.nix
./packages.nix
./de/default.nix
../../modules/userapps/kondo_timer.nix
../../modules/common/nix.nix
../../modules/common/networking.nix
../../modules/common/shell.nix
../../modules/common/security.nix
#../../modules/common/wine.nix
../../modules/common/libvirt.nix
];
networking.hostName = "puter";
# This will be overridden by system/default.nix
system.stateVersion = "24.05";
security.pki.certificates = ["/home/user/.config/bigbox.local.crt"];
}
# 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 = ["xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod"];
boot.initrd.kernelModules = [];
boot.kernelModules = ["kvm-intel" "wl"];
boot.extraModulePackages = [config.boot.kernelPackages.broadcom_sta];
fileSystems."/" = {
device = "/dev/disk/by-uuid/19a0aa4c-8d49-4545-9ed6-cd4a952f9353";
fsType = "ext4";
};
boot.initrd.luks.devices."luks-08992355-1404-44dc-9bb4-fd32f1d0b62d".device = "/dev/disk/by-uuid/08992355-1404-44dc-9bb4-fd32f1d0b62d";
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/F54E-7011";
fsType = "vfat";
options = ["fmask=0022" "dmask=0022"];
};
swapDevices = [{device = "/dev/disk/by-uuid/c9b7b195-d072-4574-8e39-a5e4f90ca84d";}];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.ens9.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode =
lib.mkDefault config.hardware.enableRedistributableFirmware;
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.initrd.luks.devices."luks-d27fc6c5-7edd-4c18-ab70-3ca02a28abcc".device = "/dev/disk/by-uuid/d27fc6c5-7edd-4c18-ab70-3ca02a28abcc";
nixpkgs.config.packageOverrides = pkgs: {
intel-vaapi-driver =
pkgs.intel-vaapi-driver.override {enableHybridCodec = true;};
};
hardware.opengl = {
enable = true;
extraPackages = with pkgs; [
intel-media-driver
intel-vaapi-driver
libvdpau-va-gl
];
};
environment.sessionVariables = {LIBVA_DRIVER_NAME = "i965";};
hardware.facetimehd = {
enable = true;
withCalibration = true;
};
#services.logind = {
# lidSwitch = "ignore";
# lidSwitchDocked = "ignore";
# lidSwitchExternalPower = "ignore";
# extraConfig = ''
# HandlePowerKey=ignore
# HandleSuspendKey=ignore
# '';
# This goes in the extraConfig section IdleAction=ignore
#};
environment.systemPackages = with pkgs; [mbpfan thermald];
boot.blacklistedKernelModules = ["b43" "ssb" "brcmfmac" "brcmsmac" "bcma"];
nixpkgs.config.allowInsecurePredicate = pkg:
builtins.elem (lib.getName pkg) [
"broadcom-sta" # aka “wl”
"python"
"qtwebengine"
];
powerManagement = {
enable = true;
cpuFreqGovernor = "performance";
};
}
{
config,
pkgs,
...
}: {
nixpkgs.config.permittedInsecurePackages = ["qtwebengine-5.15.19"];
nixpkgs.config = {chromium = {enableWideVine = true;};};
imports = [
../../modules/userapps/utils.nix
#../../modules/common/steam.nix
../../modules/userapps/devutils.nix
../../modules/userapps/internet.nix
../../modules/userapps/media.nix
../../modules/userapps/productivity.nix
];
users.users.user = {
isNormalUser = true;
description = "user";
extraGroups = ["networkmanager" "wheel" "video" "libvirtd"];
};
}
{
config,
pkgs,
...
}: {
services.syncthing = {
enable = true;
openDefaultPorts = true;
dataDir = "/home/user/Documents/syncthing/";
configDir = "/home/user/.config/syncthing";
user = "user";
# Optional: GUI credentials (can be set in the browser instead if you don't want plaintext credentials in your configuration.nix file)
# or the password hash can be generated with "syncthing generate --config <path> --gui-password=<password>"
settings.gui = {
user = "user";
password = "user";
};
settings = {
devices = {
"phone" = {
id = "N3RII7R-4YOKJ46-HY6NSTD-TPE7SW4-N66CWVQ-J2JVW7E-NARRMBF-JG27YQ7";
};
"bigbox" = {
id = "RFFXZJI-HJTEDCU-M7MU3SW-M7DJK4U-MESRKYA-OGHYOSG-KNZO7JB-6LQ5VAE";
};
};
folders = {
"brain_zero" = {
# Name of folder in Syncthing, also the folder ID
path = "/home/user/brain_zero/"; # Which folder to add to Syncthing
devices = ["phone"]; # Which devices to share the folder with
ignores = [".obsidian/workspace-mobile.json" ".obsidian/workspace.json"];
};
"brain_zero_readonly" = {
# Name of folder in Syncthing, also the folder ID
path = "/home/user/brain_zero/"; # Which folder to add to Syncthing
type = "sendonly";
devices = ["bigbox"]; # Which devices to share the folder with
};
"puter_home" = {
path = "/home/user/";
type = "sendonly";
devices = ["bigbox"];
ignores = [".*" "!.ssh/"];
};
};
};
};
}
{
config,
pkgs,
...
}: {
# Enable with systemctl --user enable --now docker
virtualisation.docker = {
enable = true;
rootless = {
enable = true;
setSocketVariable = true;
};
};
}
{
config,
pkgs,
...
}: {
virtualisation.libvirtd = {
enable = true;
qemu = {
package = pkgs.qemu_kvm;
runAsRoot = true;
swtpm.enable = true;
ovmf = {
enable = true;
packages = [
(pkgs.OVMF.override {
secureBoot = true;
tpmSupport = true;
}).fd
];
};
};
};
environment.systemPackages = with pkgs; [virt-manager qemu_kvm quickemu];
}
{
config,
pkgs,
...
}: {
networking.networkmanager.enable = true;
networking.networkmanager.dns = "none";
networking.nameservers = ["192.168.0.101" "9.9.9.11" "149.112.112.11"];
time.timeZone = "Europe/Bucharest";
i18n.defaultLocale = "en_US.UTF-8";
i18n.extraLocaleSettings = {
LC_ADDRESS = "ro_RO.UTF-8";
LC_IDENTIFICATION = "ro_RO.UTF-8";
LC_MEASUREMENT = "ro_RO.UTF-8";
LC_MONETARY = "ro_RO.UTF-8";
LC_NAME = "ro_RO.UTF-8";
LC_NUMERIC = "ro_RO.UTF-8";
LC_PAPER = "ro_RO.UTF-8";
LC_TELEPHONE = "ro_RO.UTF-8";
LC_TIME = "ro_RO.UTF-8";
};
environment.systemPackages = with pkgs; [torsocks];
# 9050,9063,8118(HTTP)
services.tor = {
enable = true;
client.enable = true;
openFirewall = true;
settings = {ControlPort = 9051;};
};
services.chrony = {
enable = true;
enableNTS = true;
servers = ["nts.teambelgium.net" "ptbtime1.ptb.de" "paris.time.system76.com"];
};
}
{
config,
pkgs,
...
}: {
nixpkgs.config.allowUnfree = true;
nix.settings.experimental-features = ["nix-command" "flakes"];
nix.extraOptions = ''
trusted-users = root user
'';
}
{
config,
pkgs,
...
}: {
environment.systemPackages = with pkgs; [
lynis
];
}
{
config,
pkgs,
lib,
...
}: {
environment.variables.EDITOR = "nvim";
users.users.user.shell = pkgs.bash;
environment.shells = with pkgs; [bash];
programs.bash = {
interactiveShellInit = ''
if [[ $(${pkgs.procps}/bin/ps --no-header --pid=$PPID --format=comm) != "fish" && -z ''${BASH_EXECUTION_STRING} ]]
then
shopt -q login_shell && LOGIN_OPTION='--login' || LOGIN_OPTION=""
exec ${pkgs.fish}/bin/fish $LOGIN_OPTION
fi
'';
};
programs.fish.enable = true;
environment.systemPackages = with pkgs; [starship];
programs.starship = {
enable = true;
settings = {
add_newline = true;
command_timeout = 1300;
scan_timeout = 50;
format = ''
$all$nix_shell$nodejs$lua$golang$rust$php$git_branch$git_commit$git_state$git_status
$username$hostname$directory'';
character = {
success_symbol = "[](bold green) ";
error_symbol = "[✗](bold red) ";
};
};
};
#programs.zsh = {
# enable = true;
# enableCompletion = true;
# autosuggestions.enable = true;
# syntaxHighlighting.enable = true;
#
# shellAliases = {
# update = "sudo nixos-rebuild switch";
# upgrade = "sudo nixos-rebuild switch --upgrade";
# proxy-grabber =
# "if [ $# -eq 0 ]; then torsocks curl -sL https://cdn.jsdelivr.net/gh/proxifly/free-proxy-list@main/proxies/all/data.txt else countrycode=$(echo $1 | tr '[:lower:]' '[:upper:]') torsocks curl -sL https://cdn.jsdelivr.net/gh/proxifly/free-proxy-list@main/proxies/countries/$countrycode/data.txt fi";
# usbdiff =
# "lsblk > diff1 && echo 'SLEEPING 5...' && sleep 5 && lsblk > diff2 && diff diff1 diff2 && rm diff1 diff2";
# };
#
# oh-my-zsh = {
# enable = true;
# plugins = [ "git" ];
# theme = "clean";
# };
#};
}
{
config,
pkgs,
...
}: {
environment.systemPackages = with pkgs; [
# support both 32-bit and 64-bit applications
wineWowPackages.stable
lutris
];
}
{
config,
pkgs,
...
}: {
programs.neovim = {defaultEditor = true;};
environment.systemPackages = with pkgs; [
# Dev stuff, most of it might be replaced by devenvs later on
ghostty
tealdeer
vscodium
gcc
python
alejandra
devenv
nodejs
mermaid-cli
rustup
delta
just
kondo
ciscoPacketTracer8
# stuff needed by neovim
vimPlugins.LazyVim
texliveSmall # for pdflatex
fzf
lazygit
tree-sitter
neovim
mcfly
luajitPackages.luarocks-nix
tectonic
];
}
{
config,
pkgs,
...
}: {
programs.neovim = {defaultEditor = true;};
environment.systemPackages = with pkgs; [
];
}
{
config,
pkgs,
...
}: {
programs.neovim = {defaultEditor = true;};
environment.systemPackages = with pkgs; [
# Chatting
signal-desktop
element-desktop
# web browsers
ungoogled-chromium
tor-browser-bundle-bin
librewolf
transmission_4-gtk
];
}
{
config,
pkgs,
...
}: {
systemd.timers."kondo" = {
wantedBy = ["timers.target"];
timerConfig = {
#OnBootSec = "5m";
#OnUnitActiveSec = "5m";
Unit = "kondo.service";
OnCalendar = "weekly";
Persistent = true;
};
};
systemd.services."kondo" = {
script = ''
kondo --all --ignored-dirs dev/current
'';
serviceConfig = {
Type = "oneshot";
User = "root";
};
};
}
{
config,
pkgs,
...
}: {
programs.neovim = {defaultEditor = true;};
environment.systemPackages = with pkgs; [
# multimedia
vlc
mpv
yt-dlp
freetube
jellyfin-media-player
feishin
];
}
{
config,
pkgs,
...
}: {
programs.neovim = {defaultEditor = true;};
environment.systemPackages = with pkgs; [
# Productivity apps
obsidian
onlyoffice-desktopeditors
keepassxc
evolution
anki-bin
pomodoro-gtk
#ticker
];
}
{
config,
pkgs,
...
}: {
programs.steam = {
enable = true;
# remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play
# dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server
# localNetworkGameTransfers.openFirewall = true; # Open ports in the firewall for Steam Local Network Game Transfers
};
}
{
config,
pkgs,
...
}: {
programs.neovim = {defaultEditor = true;};
environment.systemPackages = with pkgs; [
parallel-disk-usage
pciutils
btop
nload
htop
nethogs
uutils-coreutils-noprefix
neovim
git
curl
bat
eza
dua
ripgrep
dig
fd
proxychains-ng
unzip
wget
];
}
{
config,
pkgs,
...
}: {
imports = [./gnome.nix ./sway.nix];
services.xserver = {
enable = true;
displayManager.gdm.enable = true;
layout = "us";
xkbVariant = "";
};
# Enable sound with pipewire.
hardware.pulseaudio.enable = false;
security.rtkit.enable = true;
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
# If you want to use JACK applications, uncomment this
#jack.enable = true;
# use the example session manager (no others are packaged yet so this is en>
# no need to redefine it in your config for now)
#media-session.enable = true;
};
}
{
config,
pkgs,
...
}: {
services.xserver.desktopManager.gnome.enable = true;
programs.kdeconnect.enable = true;
environment.systemPackages = with pkgs; [
gnome-tweaks
# GNOME-specific packages
bustle
collision
commit
dialect
fretboard
#gaphor
hieroglyphic
junction
keypunch
letterpress
gnome-obfuscate
shortwave
solanum
wike
commit
wl-clipboard
gnomeExtensions.gsconnect
gnomeExtensions.astra-monitor
lm_sensors
iotop
gtop
wirelesstools
];
}
{
config,
pkgs,
...
}: {
programs.sway = {
enable = true;
wrapperFeatures.gtk = true;
};
environment.systemPackages = with pkgs; [
mako # Notification daemon for Sway
playerctl
pavucontrol
# Other Sway-specific packages
];
programs.light.enable = true; # For brightness control
}