├── .gitignore ├── .envrc ├── .git-blame-ignore-revs ├── assets ├── pp.png ├── bird.gif ├── screenshot.jpeg └── spotify-light.svg ├── secrets ├── grist │ ├── env.age │ └── oidc_client_secret_digest.age ├── immich │ └── env.age ├── restic │ ├── env.age │ └── password.age ├── traefik │ └── env.age ├── hass │ └── secrets.age ├── smtp_password.age ├── lldap │ ├── jwt_secret.age │ ├── server_key.age │ └── ldap_user_pass.age ├── authelia │ ├── jwt_secret.age │ ├── ldap_password.age │ ├── session_secret.age │ ├── oidc_hmac_secret.age │ ├── oidc_jwt_private_key.age │ └── storage_encryption_key.age ├── n8n │ └── encryption_key.age ├── nextcloud │ ├── secrets.age │ └── admin_password.age ├── fastmail │ └── app_password.age ├── obsidian-livesync │ └── env.age ├── dawarich │ ├── secret_key_base.age │ ├── oidc_client_secret_env.age │ └── oidc_client_secret_digest.age ├── obsidian-share-note │ └── env.age └── secrets.nix ├── profiles ├── pc │ ├── languagetool.nix │ ├── ssh.nix │ ├── nextcloud.nix │ ├── yazi.nix │ ├── email.nix │ └── base.nix ├── develop │ ├── docker.nix │ ├── go.nix │ ├── pgcli │ │ ├── default.nix │ │ └── config │ └── helix.nix ├── core │ ├── theme.nix │ ├── tailscale.nix │ ├── upgrade-diff.nix │ ├── nix.nix │ ├── users.nix │ ├── git.nix │ ├── kak │ │ ├── default.nix │ │ └── kakrc │ ├── helix │ │ └── default.nix │ ├── home.nix │ ├── base.nix │ ├── fish.nix │ └── tmux.nix ├── virt-manager.nix ├── graphical │ ├── spotify │ │ └── default.nix │ ├── wezterm │ │ ├── default.nix │ │ └── config.lua │ ├── zed │ │ ├── default.nix │ │ ├── user_keymap.nix │ │ └── kakoune_keymap.nix │ ├── zen │ │ ├── tridactylrc │ │ ├── default.nix │ │ └── tridactyl_style.css │ ├── style.nix │ ├── hyprland │ │ ├── windowrules.nix │ │ ├── keybindings.nix │ │ └── default.nix │ ├── base.nix │ ├── beeper.nix │ └── caelestia │ │ └── default.nix ├── audio.nix ├── hardware │ ├── logitech-g203.nix │ └── keychron.nix ├── laptop.nix ├── server.nix └── vars.nix ├── overlays ├── himalaya.nix └── material-symbols.nix ├── pkgs ├── fedit.sh ├── bbox.nix ├── dawarich-ha.nix ├── aiobbox.nix ├── dawarich-api.nix ├── default.nix ├── backlight.py ├── kakoune_plugins.nix └── soundcards.bash ├── hosts ├── carokann │ ├── pipewire.nix │ ├── default.nix │ └── shikane.nix ├── najdorf │ ├── netdata.nix │ ├── cockpit.nix │ ├── portainer.nix │ ├── nocodb.nix │ ├── immich.nix │ ├── calibre-web.nix │ ├── minecraft.nix │ ├── n8n.nix │ ├── obsidian-share-note.nix │ ├── obsidian-livesync.nix │ ├── goeland.nix │ ├── grist.nix │ ├── traefik.nix │ ├── lldap.nix │ ├── dawarich.nix │ ├── nextcloud.nix │ ├── default.nix │ ├── hass.nix │ └── authelia.nix ├── bootstrap.nix └── grunfeld │ └── default.nix ├── hm-modules ├── glow.nix └── beeper.nix ├── lib.nix ├── shell.nix ├── modules └── vars.nix ├── README.md └── flake.nix /.gitignore: -------------------------------------------------------------------------------- 1 | .direnv/ 2 | result 3 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | watch_file shell.nix flake.nix 2 | use flake 3 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | 66bdfc1dd7bdae1c9776005668491da4a65cc73c 2 | -------------------------------------------------------------------------------- /assets/pp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/assets/pp.png -------------------------------------------------------------------------------- /assets/bird.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/assets/bird.gif -------------------------------------------------------------------------------- /secrets/grist/env.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/grist/env.age -------------------------------------------------------------------------------- /assets/screenshot.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/assets/screenshot.jpeg -------------------------------------------------------------------------------- /secrets/immich/env.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/immich/env.age -------------------------------------------------------------------------------- /secrets/restic/env.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/restic/env.age -------------------------------------------------------------------------------- /secrets/traefik/env.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/traefik/env.age -------------------------------------------------------------------------------- /secrets/hass/secrets.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/hass/secrets.age -------------------------------------------------------------------------------- /secrets/smtp_password.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/smtp_password.age -------------------------------------------------------------------------------- /secrets/lldap/jwt_secret.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/lldap/jwt_secret.age -------------------------------------------------------------------------------- /secrets/lldap/server_key.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/lldap/server_key.age -------------------------------------------------------------------------------- /secrets/restic/password.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/restic/password.age -------------------------------------------------------------------------------- /secrets/authelia/jwt_secret.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/authelia/jwt_secret.age -------------------------------------------------------------------------------- /secrets/n8n/encryption_key.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/n8n/encryption_key.age -------------------------------------------------------------------------------- /secrets/nextcloud/secrets.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/nextcloud/secrets.age -------------------------------------------------------------------------------- /secrets/fastmail/app_password.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/fastmail/app_password.age -------------------------------------------------------------------------------- /secrets/lldap/ldap_user_pass.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/lldap/ldap_user_pass.age -------------------------------------------------------------------------------- /secrets/obsidian-livesync/env.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/obsidian-livesync/env.age -------------------------------------------------------------------------------- /secrets/authelia/ldap_password.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/authelia/ldap_password.age -------------------------------------------------------------------------------- /secrets/authelia/session_secret.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/authelia/session_secret.age -------------------------------------------------------------------------------- /secrets/dawarich/secret_key_base.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/dawarich/secret_key_base.age -------------------------------------------------------------------------------- /secrets/nextcloud/admin_password.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/nextcloud/admin_password.age -------------------------------------------------------------------------------- /secrets/obsidian-share-note/env.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/obsidian-share-note/env.age -------------------------------------------------------------------------------- /profiles/pc/languagetool.nix: -------------------------------------------------------------------------------- 1 | { 2 | services.languagetool = { 3 | enable = false; 4 | port = 61000; 5 | }; 6 | } 7 | -------------------------------------------------------------------------------- /secrets/authelia/oidc_hmac_secret.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/authelia/oidc_hmac_secret.age -------------------------------------------------------------------------------- /secrets/authelia/oidc_jwt_private_key.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/authelia/oidc_jwt_private_key.age -------------------------------------------------------------------------------- /secrets/authelia/storage_encryption_key.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/authelia/storage_encryption_key.age -------------------------------------------------------------------------------- /secrets/dawarich/oidc_client_secret_env.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/dawarich/oidc_client_secret_env.age -------------------------------------------------------------------------------- /secrets/grist/oidc_client_secret_digest.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/grist/oidc_client_secret_digest.age -------------------------------------------------------------------------------- /secrets/dawarich/oidc_client_secret_digest.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sweenu/nixfiles/HEAD/secrets/dawarich/oidc_client_secret_digest.age -------------------------------------------------------------------------------- /profiles/develop/docker.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | { 4 | home-manager.users."${config.vars.username}".home.packages = [ pkgs.docker-compose ]; 5 | } 6 | -------------------------------------------------------------------------------- /profiles/core/theme.nix: -------------------------------------------------------------------------------- 1 | { config, nix-colors, ... }: 2 | 3 | { 4 | home-manager.users."${config.vars.username}".colorScheme = nix-colors.colorSchemes.oceanicnext; 5 | } 6 | -------------------------------------------------------------------------------- /profiles/develop/go.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | { 4 | home-manager.users."${config.vars.username}".programs.go = { 5 | enable = true; 6 | env.GOPATH = "${config.vars.home}/.go"; 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /overlays/himalaya.nix: -------------------------------------------------------------------------------- 1 | final: prev: { 2 | himalaya = prev.himalaya.override { 3 | withFeatures = [ 4 | "notmuch" 5 | "imap" 6 | "maildir" 7 | "smtp" 8 | ]; 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /profiles/virt-manager.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | virtualisation.libvirtd.enable = true; 5 | virtualisation.spiceUSBRedirection.enable = true; 6 | environment.systemPackages = with pkgs; [ virt-manager ]; 7 | } 8 | -------------------------------------------------------------------------------- /profiles/pc/ssh.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | { 4 | home-manager.users."${config.vars.username}".programs.ssh = { 5 | extraConfig = '' 6 | AddKeysToAgent yes 7 | IdentitiesOnly yes 8 | ''; 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /profiles/pc/nextcloud.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | { 4 | home-manager.users."${config.vars.username}" = { 5 | services.nextcloud-client = { 6 | enable = true; 7 | startInBackground = true; 8 | }; 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /profiles/develop/pgcli/default.nix: -------------------------------------------------------------------------------- 1 | # TODO: make pgcli module 2 | { config, pkgs, ... }: 3 | 4 | { 5 | home-manager.users."${config.vars.username}" = { 6 | home.packages = [ pkgs.pgcli ]; 7 | xdg.configFile."pgcli/config".source = ./config; 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /profiles/graphical/spotify/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | lib, 4 | ... 5 | }: 6 | { 7 | environment.defaultPackages = [ 8 | pkgs.spotifywm 9 | ]; 10 | 11 | # For spotify connect 12 | networking.firewall.extraCommands = lib.openTCPPortForLAN 4070; 13 | } 14 | -------------------------------------------------------------------------------- /profiles/core/tailscale.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | { 4 | services.tailscale = { 5 | enable = true; 6 | openFirewall = true; 7 | }; 8 | 9 | networking.firewall = { 10 | trustedInterfaces = [ config.services.tailscale.interfaceName ]; 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /profiles/audio.nix: -------------------------------------------------------------------------------- 1 | { 2 | services.pipewire = { 3 | enable = true; 4 | alsa.enable = true; 5 | pulse.enable = true; 6 | wireplumber.extraConfig = { 7 | "11-bluetooth-policy" = { 8 | "wireplumber.settings" = { 9 | "bluetooth.autoswitch-to-headset-profile" = false; 10 | }; 11 | }; 12 | }; 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /pkgs/fedit.sh: -------------------------------------------------------------------------------- 1 | root=$(git rev-parse --show-toplevel 2> /dev/null || echo '.') 2 | kak_cmd=$(test -z "$1" && echo "kak" || echo "kak -c $1") 3 | 4 | file=$( 5 | fd --type file . --base-directory "$root" | 6 | sk --tiebreak=length,score,begin,end,index --preview-window=down:60% --preview "bat --style=numbers --color=always --line-range :500 $root/{}" 7 | ) 8 | 9 | test -z "$file" || $kak_cmd "$root"/"$file" 10 | -------------------------------------------------------------------------------- /overlays/material-symbols.nix: -------------------------------------------------------------------------------- 1 | final: prev: { 2 | material-symbols = prev.material-symbols.overrideAttrs (old: { 3 | version = "4.0.0-unstable-2025-09-19"; 4 | src = prev.fetchFromGitHub { 5 | owner = "google"; 6 | repo = "material-design-icons"; 7 | rev = "bb04090f930e272697f2a1f0d7b352d92dfeee43"; 8 | hash = "sha256-aFKG8U4OBqh2hoHYm1n/L4bK7wWPs6o0rYVhNC7QEpI="; 9 | sparseCheckout = [ "variablefont" ]; 10 | }; 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /pkgs/bbox.nix: -------------------------------------------------------------------------------- 1 | { 2 | aiobbox, 3 | fetchFromGitHub, 4 | buildHomeAssistantComponent, 5 | }: 6 | 7 | buildHomeAssistantComponent rec { 8 | owner = "sweenu"; 9 | domain = "bbox"; 10 | version = "0.1.0"; 11 | 12 | src = fetchFromGitHub { 13 | inherit owner; 14 | repo = "bbox-ha"; 15 | rev = "9713882882357f9da58946e05aee29ba6a05b966"; 16 | hash = "sha256-WC6FQoKeMYDf5qdWq6TuBg5udUstGa6ilbIgoJDTlDg="; 17 | }; 18 | 19 | dependencies = [ 20 | aiobbox 21 | ]; 22 | } 23 | -------------------------------------------------------------------------------- /pkgs/dawarich-ha.nix: -------------------------------------------------------------------------------- 1 | { 2 | dawarich-api, 3 | fetchFromGitHub, 4 | buildHomeAssistantComponent, 5 | }: 6 | 7 | buildHomeAssistantComponent rec { 8 | owner = "AlbinLind"; 9 | domain = "dawarich"; 10 | version = "0.8.5"; 11 | 12 | src = fetchFromGitHub { 13 | inherit owner; 14 | repo = "dawarich-home-assistant"; 15 | tag = version; 16 | hash = "sha256-T7f4z501jO2XWC4YQx7MEJSZY5ZAYXQb1vtYGcEVJU0"; 17 | }; 18 | 19 | dependencies = [ 20 | dawarich-api 21 | ]; 22 | } 23 | -------------------------------------------------------------------------------- /profiles/core/upgrade-diff.nix: -------------------------------------------------------------------------------- 1 | # https://github.com/nix-community/srvos/blob/b3af8aed091d85e180a861695f2d57b3b2d24ba1/nixos/common/upgrade-diff.nix 2 | { config, pkgs, ... }: 3 | 4 | { 5 | system.activationScripts.diff = { 6 | supportsDryActivation = true; 7 | text = '' 8 | if [[ -e /run/current-system ]]; then 9 | echo "--- diff to current-system" 10 | ${pkgs.nvd}/bin/nvd --color=always --nix-bin-dir=${config.nix.package}/bin diff --sort semver /run/current-system "$systemConfig" 11 | echo "---" 12 | fi 13 | ''; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /profiles/pc/yazi.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | { 3 | home-manager.users."${config.vars.username}" = { 4 | programs.yazi = { 5 | enable = true; 6 | }; 7 | programs.fish.functions.y = { 8 | body = '' 9 | set tmp (mktemp -t "yazi-cwd.XXXXXX") 10 | yazi $argv --cwd-file="$tmp" 11 | if read -z cwd < "$tmp"; and [ -n "$cwd" ]; and [ "$cwd" != "$PWD" ] 12 | builtin cd -- "$cwd" 13 | end 14 | rm -f -- "$tmp" 15 | ''; 16 | description = "Change the current directory through yazi"; 17 | }; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /profiles/graphical/wezterm/default.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | { 3 | home-manager.users."${config.vars.username}" = 4 | let 5 | tablineRepo = pkgs.fetchFromGitHub { 6 | owner = "michaelbrusegard"; 7 | repo = "tabline.wez"; 8 | rev = "v1.6.0"; 9 | sha256 = "sha256-1/lA0wjkvpIRauuhDhaV3gzCFSql+PH39/Kpwzrbk54="; 10 | }; 11 | in 12 | { 13 | xdg.configFile."wezterm/tabline".source = "${tablineRepo}/plugin"; 14 | 15 | programs.wezterm = { 16 | enable = true; 17 | extraConfig = builtins.readFile ./config.lua; 18 | }; 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /hosts/carokann/pipewire.nix: -------------------------------------------------------------------------------- 1 | { 2 | services.pipewire.wireplumber.extraConfig = { 3 | "5-built-in-speakers-rename" = { 4 | "monitor.alsa.rules" = [ 5 | { 6 | matches = [ { "node.name" = "alsa_output.pci-0000_c1_00.6.HiFi__Speaker__sink"; } ]; 7 | actions = { 8 | update-props = { 9 | "node.nick" = "Built-in Speakers"; 10 | }; 11 | }; 12 | } 13 | ]; 14 | }; 15 | # See https://github.com/NixOS/nixos-hardware/issues/1603 16 | "no-ucm" = { 17 | "monitor.alsa.properties" = { 18 | "alsa.use-ucm" = false; 19 | }; 20 | }; 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /profiles/core/nix.nix: -------------------------------------------------------------------------------- 1 | { 2 | nix.settings = { 3 | extra-experimental-features = "nix-command flakes"; 4 | extra-substituters = [ 5 | "https://nix-community.cachix.org" 6 | "https://sweenu.cachix.org" 7 | "https://deploy-rs.cachix.org" 8 | "https://cache.garnix.io" 9 | ]; 10 | extra-trusted-public-keys = [ 11 | "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" 12 | "sweenu.cachix.org-1:DvQl16NWBp41k5IlxTODTOrIThyGRj8/ekrXxEheBQ0=" 13 | "deploy-rs.cachix.org-1:xfNobmiwF/vzvK1gpfediPwpdIP0rpDV2rYqx40zdSI=" 14 | "cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g=" 15 | ]; 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /hosts/najdorf/netdata.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | { 4 | services.netdata = { 5 | enable = true; 6 | package = pkgs.netdata.override { 7 | withCloudUi = true; 8 | }; 9 | python = { 10 | recommendedPythonPackages = true; 11 | }; 12 | }; 13 | 14 | services.traefik.dynamicConfigOptions.http = rec { 15 | routers.to-netdata = { 16 | rule = "Host(`netdata.${config.vars.domainName}`)"; 17 | service = "netdata"; 18 | middlewares = [ "authelia" ]; 19 | }; 20 | services."${routers.to-netdata.service}".loadBalancer.servers = [ 21 | { 22 | url = "http://localhost:19999"; 23 | } 24 | ]; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /hosts/najdorf/cockpit.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | let 4 | fqdn = "cockpit.${config.vars.domainName}"; 5 | url = "https://${fqdn}"; 6 | in 7 | { 8 | services.cockpit = { 9 | enable = true; 10 | allowed-origins = [ url ]; 11 | port = 12431; 12 | }; 13 | 14 | services.traefik.dynamicConfigOptions.http = rec { 15 | routers.to-cockpit = { 16 | rule = "Host(`${fqdn}`)"; 17 | service = "cockpit"; 18 | middlewares = "authelia"; 19 | }; 20 | services."${routers.to-cockpit.service}".loadBalancer.servers = [ 21 | { 22 | url = "http://127.0.0.1:${builtins.toString config.services.cockpit.port}"; 23 | } 24 | ]; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /pkgs/aiobbox.nix: -------------------------------------------------------------------------------- 1 | { 2 | aiohttp, 3 | pydantic, 4 | buildPythonPackage, 5 | fetchPypi, 6 | uv-build, 7 | }: 8 | 9 | buildPythonPackage rec { 10 | pname = "aiobbox"; 11 | version = "0.3.1"; 12 | pyproject = true; 13 | 14 | src = fetchPypi { 15 | inherit pname version; 16 | hash = "sha256-/UyyhqvIcfMwrisbUF5AEiwzIUAfQxvmjxQL9ppsj9g="; 17 | }; 18 | 19 | postPatch = '' 20 | substituteInPlace pyproject.toml \ 21 | --replace-fail "uv_build>=0.8.19,<0.9.0" "uv_build" 22 | ''; 23 | 24 | propagatedBuildInputs = [ 25 | aiohttp 26 | pydantic 27 | ]; 28 | 29 | nativeBuildInputs = [ 30 | uv-build 31 | ]; 32 | 33 | pythonImportsCheck = [ "aiobbox" ]; 34 | } 35 | -------------------------------------------------------------------------------- /profiles/hardware/logitech-g203.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | let 4 | vendorId = "046d"; 5 | productId = "c092"; 6 | g203-white = pkgs.writeShellScriptBin "g203-white" '' 7 | ${pkgs.openrgb}/bin/openrgb --noautoconnect --device "Logitech G203 Lightsync" --color FFFFFF 8 | ''; 9 | in 10 | { 11 | systemd.services.g203-white = { 12 | description = "g203-white"; 13 | serviceConfig = { 14 | ExecStart = "${g203-white}/bin/g203-white"; 15 | Type = "oneshot"; 16 | }; 17 | }; 18 | 19 | services.udev.extraRules = '' 20 | ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="${vendorId}", ATTRS{idProduct}=="${productId}", TAG+="systemd", ENV{SYSTEMD_WANTS}="g203-white.service" 21 | ''; 22 | } 23 | -------------------------------------------------------------------------------- /hm-modules/glow.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | 8 | with lib; 9 | 10 | let 11 | cfg = config.programs.glow; 12 | ymlFormat = pkgs.formats.yaml { }; 13 | in 14 | { 15 | options.programs.glow = { 16 | enable = mkEnableOption "Render markdown on the CLI, with pizzazz!"; 17 | 18 | settings = mkOption { 19 | description = "Configuration of glow."; 20 | default = { }; 21 | type = ymlFormat.type; 22 | }; 23 | }; 24 | 25 | config = mkIf cfg.enable { 26 | home.packages = [ pkgs.glow ]; 27 | 28 | xdg.configFile."glow/glow.yml" = mkIf (cfg.settings != { }) { 29 | source = ymlFormat.generate "glow.yml" cfg.settings; 30 | }; 31 | }; 32 | 33 | meta.maintainers = [ maintainers.sweenu ]; 34 | } 35 | -------------------------------------------------------------------------------- /pkgs/dawarich-api.nix: -------------------------------------------------------------------------------- 1 | { 2 | buildPythonPackage, 3 | fetchFromGitHub, 4 | setuptools, 5 | wheel, 6 | pythonOlder, 7 | aiohttp, 8 | pydantic, 9 | }: 10 | 11 | buildPythonPackage rec { 12 | pname = "dawarich-api"; 13 | version = "0.4.1"; 14 | pyproject = true; 15 | 16 | disabled = pythonOlder "3.8"; 17 | 18 | src = fetchFromGitHub { 19 | owner = "AlbinLind"; 20 | repo = "dawarich-api"; 21 | tag = version; 22 | hash = "sha256-nR06P2QvC9/RxeX3CcehTDlyk7EwZ33ryKRG8sjfp3s="; 23 | }; 24 | 25 | nativeBuildInputs = [ 26 | setuptools 27 | wheel 28 | ]; 29 | 30 | propagatedBuildInputs = [ 31 | aiohttp 32 | pydantic 33 | ]; 34 | 35 | doCheck = false; 36 | 37 | pythonImportsCheck = [ 38 | "dawarich_api" 39 | ]; 40 | } 41 | -------------------------------------------------------------------------------- /lib.nix: -------------------------------------------------------------------------------- 1 | { 2 | hyprMoveWsToMonitor = 3 | ws: "hyprctl dispatch moveworkspacetomonitor ${builtins.toString ws} $SHIKANE_OUTPUT_NAME"; 4 | openTCPPortForLAN = 5 | port: 6 | "iptables -A nixos-fw -p tcp -s 192.168.1.0/24 --dport ${builtins.toString port} -j nixos-fw-accept"; 7 | 8 | openUDPPortForLAN = 9 | port: 10 | "iptables -A nixos-fw -p udp -s 192.168.1.0/24 --dport ${builtins.toString port} -j nixos-fw-accept"; 11 | 12 | openTCPPortRangeForLAN = 13 | from: to: 14 | "iptables -A nixos-fw -p tcp -s 192.168.1.0/24 --dport ${builtins.toString from}:${builtins.toString to} -j nixos-fw-accept"; 15 | 16 | openUDPPortRangeForLAN = 17 | from: to: 18 | "iptables -A nixos-fw -p udp -s 192.168.1.0/24 --dport ${builtins.toString from}:${builtins.toString to} -j nixos-fw-accept"; 19 | } 20 | -------------------------------------------------------------------------------- /assets/spotify-light.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /hosts/najdorf/portainer.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | let 3 | portainerDir = "/opt/portainer"; 4 | in 5 | { 6 | virtualisation.arion.projects.portainer.settings = { 7 | enableDefaultNetwork = false; 8 | networks.traefik.external = true; 9 | services.portainer.service = { 10 | image = "portainer/portainer-ce:latest"; 11 | container_name = "portainer"; 12 | networks = [ config.services.traefik.staticConfigOptions.providers.docker.network ]; 13 | volumes = [ 14 | "/var/run/docker.sock:/var/run/docker.sock" 15 | "${portainerDir}:/data" 16 | ]; 17 | labels = { 18 | "traefik.enable" = "true"; 19 | "traefik.http.routers.to-portainer.service" = "portainer"; 20 | "traefik.http.services.portainer.loadbalancer.server.port" = "9000"; 21 | }; 22 | }; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /hosts/najdorf/nocodb.nix: -------------------------------------------------------------------------------- 1 | { lib, ... }: 2 | 3 | let 4 | nocodbDir = "/opt/nocodb"; 5 | basePort = 8097; 6 | exposedPort = 12876; 7 | in 8 | { 9 | virtualisation.arion.projects.nocodb.settings = { 10 | services = { 11 | nocodb.service = { 12 | image = "nocodb/nocodb:0.264.8"; 13 | ports = [ "${builtins.toString exposedPort}:${builtins.toString basePort}" ]; 14 | container_name = "nocodb"; 15 | networks = [ "default" ]; 16 | volumes = [ "${nocodbDir}:/usr/app/data" ]; 17 | labels = { 18 | "traefik.enable" = "true"; 19 | "traefik.http.routers.to-nocodb.service" = "nocodb"; 20 | "traefik.http.routers.to-nocodb.middlewares" = "authelia@file"; 21 | "traefik.http.services.nocodb.loadbalancer.server.port" = builtins.toString basePort; 22 | }; 23 | }; 24 | }; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /profiles/core/users.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | { 4 | users = { 5 | mutableUsers = false; 6 | users = { 7 | root.hashedPassword = "$y$j9T$jMx/XkaWfgV1bsRBjgmvd1$rDi8pFUskXUjlyhJefMZDYp6VwFy.zACp3wVtI7g.IA"; 8 | "${config.vars.username}" = { 9 | shell = pkgs.fish; 10 | hashedPassword = "$y$j9T$ZjVM7P34OZjC8/o5LI5N7/$nFGR9tfv6TDeIrf7FZ.o5Kb.WJuewOB6THeNBIuY44/"; 11 | isNormalUser = true; 12 | extraGroups = [ 13 | "wheel" 14 | "dialout" 15 | ] 16 | ++ pkgs.lib.optional config.virtualisation.libvirtd.enable "libvirtd" 17 | ++ pkgs.lib.optional config.networking.networkmanager.enable "networkmanager" 18 | ++ pkgs.lib.optional config.security.tpm2.enable "tss" 19 | ++ pkgs.lib.optional config.hardware.i2c.enable config.hardware.i2c.group; 20 | }; 21 | }; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /hosts/bootstrap.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | config, 4 | pkgs, 5 | suites, 6 | ... 7 | }: 8 | { 9 | imports = suites.base; 10 | 11 | boot.loader.systemd-boot.enable = true; 12 | 13 | environment.defaultPackages = with pkgs; [ 14 | cryptsetup 15 | nvme-cli 16 | parted 17 | ]; 18 | 19 | networking.wireless.enable = false; 20 | 21 | services = { 22 | openssh = { 23 | enable = true; 24 | openFirewall = true; 25 | settings.PasswordAuthentication = false; 26 | }; 27 | }; 28 | 29 | users.users.root.openssh.authorizedKeys.keys = [ config.vars.sshPublicKey ]; 30 | 31 | # Required, but will be overridden in the resulting installer ISO. 32 | fileSystems."/" = { 33 | device = "/dev/disk/by-label/nixos"; 34 | }; 35 | 36 | home-manager.users."${config.vars.username}" = { 37 | home.file.nixfiles = { 38 | source = self; 39 | recursive = true; 40 | }; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | pkgs, 4 | inputs', 5 | }: 6 | 7 | pkgs.mkShell { 8 | packages = 9 | with pkgs; 10 | [ 11 | cachix 12 | git 13 | nix-output-monitor 14 | nvd 15 | ] 16 | ++ [ 17 | config.treefmt.build.wrapper 18 | inputs'.agenix.packages.default 19 | inputs'.deploy.packages.default 20 | inputs'.nixos-generators.packages.default 21 | inputs'.nixos-anywhere.packages.default 22 | inputs'.disko.packages.default 23 | ]; 24 | 25 | shellHook = '' 26 | echo "" 27 | echo "🚀 Welcome to nixfiles devshell" 28 | echo "" 29 | echo "Available tools:" 30 | echo " - agenix: Secret management" 31 | echo " - deploy-rs: Deployment tool" 32 | echo " - nixos-generate: Generate NixOS images" 33 | echo " - nixos-anywhere: Remote NixOS installation" 34 | echo " - disko: Declarative disk partitioning" 35 | echo "" 36 | ''; 37 | } 38 | -------------------------------------------------------------------------------- /hosts/grunfeld/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | config, 4 | suites, 5 | ... 6 | }: 7 | { 8 | imports = suites.server; 9 | 10 | fileSystems = { 11 | "/" = { 12 | device = "/dev/disk/by-label/NIXOS_SD"; 13 | fsType = "ext4"; 14 | }; 15 | }; 16 | 17 | environment.systemPackages = with pkgs; [ 18 | kakoune 19 | git 20 | curl 21 | bottom 22 | wol 23 | ]; 24 | 25 | services.journald.extraConfig = '' 26 | Storage = volatile 27 | RuntimeMaxFileSize = 10M; 28 | ''; 29 | 30 | services.avahi = { 31 | enable = true; 32 | nssmdns4 = true; 33 | publish = { 34 | enable = true; 35 | addresses = true; 36 | domain = true; 37 | userServices = true; 38 | }; 39 | }; 40 | 41 | programs.mosh.enable = true; 42 | 43 | hardware.enableRedistributableFirmware = true; 44 | 45 | time.timeZone = config.vars.timezone; 46 | 47 | system.stateVersion = "24.05"; 48 | } 49 | -------------------------------------------------------------------------------- /hosts/najdorf/immich.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | config, 4 | ... 5 | }: 6 | 7 | { 8 | age.secrets = { 9 | "immich/envFile".file = "${self}/secrets/immich/env.age"; 10 | }; 11 | 12 | services.immich = { 13 | enable = true; 14 | environment = { 15 | TZ = config.vars.timezone; 16 | }; 17 | machine-learning = { 18 | enable = true; 19 | }; 20 | secretsFile = config.age.secrets."immich/envFile".path; 21 | mediaLocation = "/opt/immich"; 22 | port = 2283; 23 | settings = null; 24 | database.enableVectors = false; 25 | }; 26 | 27 | services.traefik.dynamicConfigOptions.http = rec { 28 | routers.to-immich = { 29 | rule = "Host(`immich.${config.vars.domainName}`)"; 30 | service = "immich"; 31 | }; 32 | services."${routers.to-immich.service}".loadBalancer.servers = [ 33 | { 34 | url = "http://localhost:${builtins.toString config.services.immich.port}"; 35 | } 36 | ]; 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /pkgs/default.nix: -------------------------------------------------------------------------------- 1 | final: prev: with prev; { 2 | fedit = writeShellApplication { 3 | name = "fedit"; 4 | text = (builtins.readFile ./fedit.sh); 5 | runtimeInputs = [ 6 | fd 7 | skim 8 | bat 9 | git 10 | ]; 11 | }; 12 | 13 | kakounePlugins = kakounePlugins // lib.recurseIntoAttrs (callPackage ./kakoune_plugins.nix { }); 14 | 15 | soundcards = writers.writeBashBin "soundcards" { 16 | makeWrapperArgs = [ 17 | "--prefix" 18 | "PATH" 19 | ":" 20 | "${lib.makeBinPath [ 21 | pulseaudio 22 | wireplumber 23 | ]}" 24 | ]; 25 | } (builtins.readFile ./soundcards.bash); 26 | 27 | backlight = writers.writePython3Bin "backlight" { } (builtins.readFile ./backlight.py); 28 | 29 | aiobbox = pkgs.python3Packages.callPackage ./aiobbox.nix { }; 30 | bbox = callPackage ./bbox.nix { }; 31 | 32 | dawarich-api = pkgs.python3Packages.callPackage ./dawarich-api.nix { }; 33 | dawarich-ha = callPackage ./dawarich-ha.nix { }; 34 | } 35 | -------------------------------------------------------------------------------- /hosts/najdorf/calibre-web.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | let 4 | calibreDir = "/opt/calibre"; 5 | calibreDataDir = "${calibreDir}/data"; 6 | in 7 | { 8 | virtualisation.arion.projects.calibre-web.settings = { 9 | enableDefaultNetwork = false; 10 | networks.traefik.external = true; 11 | services.calibre.service = { 12 | image = "lscr.io/linuxserver/calibre-web:0.6.20"; 13 | container_name = "calibre-web"; 14 | volumes = [ 15 | "${calibreDir}/config:/config" 16 | "${calibreDataDir}:/books" 17 | ]; 18 | networks = [ config.services.traefik.staticConfigOptions.providers.docker.network ]; 19 | environment = { 20 | TZ = config.vars.timezone; 21 | PUID = "0"; 22 | PGID = "0"; 23 | }; 24 | labels = { 25 | "traefik.enable" = "true"; 26 | "traefik.http.routers.to-calibre.service" = "calibre"; 27 | "traefik.http.services.calibre.loadbalancer.server.port" = "8083"; 28 | }; 29 | }; 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /hosts/najdorf/minecraft.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | let 4 | modpack = pkgs.fetchPackwizModpack { 5 | url = "https://github.com/sweenu/Create-Co/raw/1c01cb29911aeaa1713c6881f00e957a91d4b2c4/pack.toml"; 6 | packHash = "sha256-C6rpzs+IdbJ5S9QgZvBjyHnJHw3HoMXH2HbmENsWEwI="; 7 | }; 8 | in 9 | { 10 | services.minecraft-servers = { 11 | enable = true; 12 | eula = true; 13 | openFirewall = true; 14 | 15 | servers.create-co = { 16 | enable = true; 17 | managementSystem = { 18 | tmux.enable = true; 19 | systemd-socket.enable = false; 20 | }; 21 | autoStart = true; 22 | 23 | package = pkgs.neoforgeServers.neoforge-1_21_1-21_1_216; 24 | 25 | jvmOpts = "-Xms4G -Xmx8G"; 26 | 27 | serverProperties = { 28 | motd = "Create & Co"; 29 | max-players = 10; 30 | online-mode = true; 31 | server-port = 25565; 32 | }; 33 | 34 | symlinks = { 35 | mods = "${modpack}/mods"; 36 | }; 37 | files = { 38 | config = "${modpack}/config"; 39 | }; 40 | }; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /profiles/hardware/keychron.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | let 4 | layoutName = "custom-us-keychron"; 5 | in 6 | { 7 | boot.extraModprobeConfig = '' 8 | # fix the F* keys on the Keychron K6 9 | options hid_apple fnmode=0 10 | ''; 11 | 12 | home-manager.users."${config.vars.username}" = { 13 | wayland.windowManager.hyprland.settings.device = 14 | let 15 | k6Conf = name: { 16 | name = name; 17 | kb_layout = layoutName; 18 | kb_options = "caps:escape"; 19 | repeat_rate = 30; 20 | repeat_delay = 200; 21 | }; 22 | in 23 | [ 24 | (k6Conf "keychron-keychron-k6") 25 | (k6Conf "keychron-keychron-k6-1") 26 | (k6Conf "keychron-keychron-k6-2") 27 | ]; 28 | 29 | home.file.".xkb/symbols/${layoutName}".text = '' 30 | default partial alphanumeric_keys 31 | xkb_symbols "custom-altgr-intl-keychron" { 32 | include "custom-us(custom-altgr-intl)" 33 | name[Group1]= "English (US, custom algr-intl-keychron)"; 34 | 35 | key { [ grave, asciitilde, dead_grave, dead_tilde ] }; 36 | }; 37 | ''; 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /profiles/laptop.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | { 4 | services.logind = { 5 | settings.Login = { 6 | HandlePowerKey = "lock"; 7 | LidSwitchIgnoreInhibited = "no"; 8 | HandleLidSwitch = "suspend-then-hibernate"; 9 | HandleLidSwitchExternalPower = "suspend-then-hibernate"; 10 | HandleLidSwitchDocked = "ignore"; 11 | }; 12 | }; 13 | services.upower.enable = true; 14 | services.power-profiles-daemon.enable = true; 15 | powerManagement.powertop.enable = true; 16 | systemd.sleep.extraConfig = "HibernateDelaySec=2h"; 17 | 18 | environment.defaultPackages = with pkgs; [ powertop ]; 19 | 20 | services.udev.extraRules = 21 | let 22 | activatePowerSaver = pkgs.writeShellScript "power-save" "${pkgs.power-profiles-daemon}/bin/powerprofilesctl set power-saver"; 23 | activatePerformance = pkgs.writeShellScript "performance" "${pkgs.power-profiles-daemon}/bin/powerprofilesctl set performance"; 24 | in 25 | '' 26 | SUBSYSTEM=="power_supply", ATTR{status}=="Discharging", RUN+="${activatePowerSaver}" 27 | SUBSYSTEM=="power_supply", ATTR{status}=="Charging", RUN+="${activatePerformance}" 28 | SUBSYSTEM=="power_supply", ATTR{status}=="Full", RUN+="${activatePerformance}" 29 | ''; 30 | } 31 | -------------------------------------------------------------------------------- /profiles/core/git.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | { 4 | home-manager.users."${config.vars.username}".programs = { 5 | git = { 6 | enable = true; 7 | ignores = [ 8 | "venv" 9 | ".env" 10 | ".envrc" 11 | ".direnv" 12 | "devenv.*" 13 | ".devenv.*" 14 | ".devenv/" 15 | ]; 16 | settings = { 17 | user = { 18 | email = config.vars.email; 19 | name = "sweenu"; 20 | signingKey = "${config.vars.sshPublicKey}"; 21 | }; 22 | pull.rebase = "true"; 23 | push.autoSetupRemote = "true"; 24 | branch.autosetuprebase = "always"; 25 | blame.ignoreRevsFile = ".git-blame-ignore-revs"; 26 | init.defaultBranch = "main"; 27 | gpg.format = "ssh"; 28 | commit.gpgSign = "true"; 29 | tag.gpgSign = "true"; 30 | alias = { 31 | ll = "log --graph --date='short' --color=always --pretty=format:'%Cgreen%h %Cred%<(15,trunc)%an %Cblue%cd %Creset%s'"; 32 | }; 33 | }; 34 | }; 35 | 36 | delta = { 37 | enable = true; 38 | enableGitIntegration = true; 39 | options = { 40 | dark = true; 41 | syntax-theme = "Monokai Extended"; 42 | navigate = "true"; 43 | }; 44 | }; 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /profiles/graphical/zed/default.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | { 4 | home-manager.users."${config.vars.username}".programs.zed-editor = { 5 | enable = true; 6 | extensions = [ 7 | "html" 8 | "nix" 9 | "qml" 10 | "git-firefly" 11 | "toml" 12 | "dockerfile" 13 | "sql" 14 | "csv" 15 | ]; 16 | userSettings = { 17 | helix_mode = true; 18 | buffer_font_size = 14; 19 | auto_signature_help = true; 20 | show_whitespaces = "trailing"; 21 | }; 22 | userTasks = [ ]; 23 | userKeymaps = (import ./kakoune_keymap.nix) ++ (import ./user_keymap.nix { mod = "ctrl"; }); 24 | extraPackages = with pkgs; [ 25 | # python 26 | ruff 27 | ty 28 | 29 | # yaml 30 | yaml-language-server 31 | 32 | # go 33 | gopls 34 | golangci-lint-langserver 35 | delve 36 | 37 | # rust 38 | rust-analyzer 39 | lldb 40 | 41 | # nix 42 | nil 43 | nixd 44 | 45 | # html, css, json 46 | vscode-langservers-extracted 47 | 48 | # yaml 49 | yaml-language-server 50 | 51 | # c 52 | clang-tools 53 | 54 | # bash 55 | nodePackages.bash-language-server 56 | 57 | # javascript 58 | nodePackages.typescript-language-server 59 | 60 | # markdown 61 | marksman 62 | 63 | # protobuf 64 | buf 65 | pb 66 | 67 | # qml 68 | qt6.qtdeclarative 69 | qt6.qtbase 70 | qt6.wrapQtAppsHook 71 | ]; 72 | }; 73 | } 74 | -------------------------------------------------------------------------------- /profiles/server.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | { 4 | imports = [ ./core/tailscale.nix ]; 5 | 6 | environment.variables.BROWSER = "echo"; 7 | 8 | nix = { 9 | gc = { 10 | automatic = true; 11 | options = "--delete-older-than 5d"; 12 | }; 13 | optimise.automatic = true; 14 | }; 15 | 16 | networking = { 17 | useDHCP = false; 18 | useNetworkd = true; 19 | networkmanager.enable = false; 20 | }; 21 | 22 | programs.mosh.enable = true; 23 | 24 | services = { 25 | openssh = { 26 | enable = true; 27 | openFirewall = false; 28 | settings.PasswordAuthentication = false; 29 | }; 30 | resolved = { 31 | enable = true; 32 | dnssec = "false"; # DNS resolution stops working after a while with `allow-downgrade` 33 | dnsovertls = "opportunistic"; 34 | fallbackDns = config.vars.dnsResolvers; 35 | }; 36 | tailscale = { 37 | useRoutingFeatures = "server"; 38 | extraUpFlags = [ 39 | "--ssh" 40 | "--advertise-exit-node" 41 | ]; 42 | }; 43 | }; 44 | 45 | users.users.root.openssh.authorizedKeys.keys = [ config.vars.sshPublicKey ]; 46 | 47 | # Server optimization 48 | documentation.enable = false; 49 | fonts.fontconfig.enable = false; 50 | environment.stub-ld.enable = false; 51 | 52 | # Taken from: https://github.com/nix-community/srvos/blob/b3af8aed091d85e180a861695f2d57b3b2d24ba1/nixos/server/default.nix#L89 53 | boot.kernel.sysctl = { 54 | "net.core.default_qdisc" = "fq"; 55 | "net.ipv4.tcp_congestion_control" = "bbr"; 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /profiles/core/kak/default.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | { 3 | home-manager.users."${config.vars.username}" = { 4 | home.packages = with pkgs; [ 5 | pkgs.nixfmt-rfc-style 6 | fedit 7 | ]; 8 | 9 | programs.kakoune = { 10 | enable = true; 11 | extraConfig = builtins.readFile ./kakrc; 12 | plugins = with pkgs.kakounePlugins; [ 13 | kak-fzf 14 | kak-auto-pairs 15 | kakoune-lsp 16 | kak-surround 17 | kak-sudo-write 18 | kak-find 19 | ]; 20 | }; 21 | 22 | xdg.configFile."kak-lsp/kak-lsp.toml".text = '' 23 | verbosity = 2 24 | 25 | [language.nix] 26 | filetypes = ["nix"] 27 | roots = ["flake.nix", "shell.nix", ".git", ".hg"] 28 | command = "${pkgs.nil}/bin/nil" 29 | 30 | [language.go] 31 | filetypes = ["go"] 32 | roots = ["Gopkg.toml", "go.mod", ".git", ".hg"] 33 | command = "${pkgs.gopls}/bin/gopls" 34 | settings_section = "gopls" 35 | 36 | [language.python] 37 | filetypes = ["python"] 38 | roots = ["pyproject.toml", "requirements.txt", "setup.py", ".git", ".hg"] 39 | command = "${pkgs.python3Packages.python-lsp-server}/bin/pylsp" 40 | settings_section = "_" 41 | [language.python.settings._] 42 | pylsp.configurationSources = ["flake8"] 43 | pylsp.plugins.flake8.enabled = true 44 | pylsp.plugins.mccabe.enabled = false 45 | pylsp.plugins.pycodestyle.enabled = false 46 | pylsp.plugins.pylint.enabled = false 47 | pylsp.plugins.yapf.enabled = false 48 | ''; 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /hosts/najdorf/n8n.nix: -------------------------------------------------------------------------------- 1 | { self, config, ... }: 2 | 3 | let 4 | fqdn = "n8n.${config.vars.domainName}"; 5 | url = "https://${fqdn}"; 6 | in 7 | { 8 | age.secrets.n8nEncryptionKey.file = "${self}/secrets/n8n/encryption_key.age"; 9 | 10 | services.n8n = { 11 | enable = true; 12 | environment = { 13 | WEBHOOK_URL = url; 14 | N8N_ENCRYPTION_KEY_FILE = "%d/encryption_key"; 15 | N8N_PERSONALIZATION_ENABLED = "false"; 16 | N8N_VERSION_NOTIFICATIONS_ENABLED = "false"; 17 | N8N_HIRING_BANNER_ENABLED = "false"; 18 | N8N_DIAGNOSTICS_ENABLED = "false"; 19 | }; 20 | }; 21 | 22 | systemd.services.n8n.serviceConfig.LoadCredential = [ 23 | "encryption_key:${config.age.secrets.n8nEncryptionKey.path}" 24 | ]; 25 | 26 | services.traefik.dynamicConfigOptions.http = rec { 27 | routers.to-n8n = { 28 | rule = "Host(`${fqdn}`)"; 29 | service = "n8n"; 30 | middlewares = "authelia"; 31 | }; 32 | services."${routers.to-n8n.service}".loadBalancer.servers = [ 33 | { 34 | url = "http://127.0.0.1:${builtins.toString config.services.n8n.environment.N8N_PORT}"; 35 | } 36 | ]; 37 | }; 38 | 39 | services.authelia.instances.main.settings.access_control.rules = [ 40 | { 41 | domain = fqdn; 42 | resources = [ "^/webhook/.*" ]; 43 | methods = [ "POST" ]; 44 | policy = "bypass"; 45 | } 46 | { 47 | domain = fqdn; 48 | resources = [ "^/form/.*" ]; 49 | methods = [ 50 | "GET" 51 | "POST" 52 | ]; 53 | policy = "bypass"; 54 | } 55 | ]; 56 | } 57 | -------------------------------------------------------------------------------- /hosts/najdorf/obsidian-share-note.nix: -------------------------------------------------------------------------------- 1 | { self, config, ... }: 2 | 3 | let 4 | fqdn = "notes.${config.vars.domainName}"; 5 | obsidianShareNoteDir = "/opt/obsidian-share-note"; 6 | in 7 | { 8 | 9 | age.secrets."obsidian-share-note/envFile".file = "${self}/secrets/obsidian-share-note/env.age"; 10 | 11 | virtualisation.arion.projects.obsidian-share-note.settings = { 12 | enableDefaultNetwork = false; 13 | networks.traefik.external = true; 14 | services.obsidian-share-note.service = { 15 | image = "ghcr.io/note-sx/server:latest"; 16 | container_name = "obsidian-share-note"; 17 | volumes = [ 18 | "${obsidianShareNoteDir}:/notesx/db:Z" 19 | "${obsidianShareNoteDir}:/notesx/userfiles:Z" 20 | ]; 21 | networks = [ config.services.traefik.staticConfigOptions.providers.docker.network ]; 22 | environment = { 23 | BASE_WEB_URL = "https://${fqdn}"; 24 | }; 25 | env_file = [ config.age.secrets."obsidian-share-note/envFile".path ]; 26 | healthcheck = { 27 | test = [ 28 | "CMD-SHELL" 29 | "(wget -qO - http://localhost:3000/v1/ping | grep -q ok) || exit 1" 30 | ]; 31 | interval = "30s"; 32 | timeout = "5s"; 33 | retries = 2; 34 | start_period = "10s"; 35 | }; 36 | labels = { 37 | "traefik.enable" = "true"; 38 | "traefik.http.routers.to-obsidian-share-note.rule" = "Host(`${fqdn}`)"; 39 | "traefik.http.routers.to-obsidian-share-note.service" = "obsidian-share-note"; 40 | "traefik.http.services.obsidian-share-note.loadbalancer.server.port" = "3000"; 41 | }; 42 | }; 43 | }; 44 | 45 | } 46 | -------------------------------------------------------------------------------- /profiles/vars.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | pkgs, 4 | lib, 5 | ... 6 | }: 7 | 8 | { 9 | vars = rec { 10 | email = "contact@sweenu.xyz"; 11 | username = "sweenu"; 12 | terminal = "wezterm"; 13 | terminalBin = "${pkgs.wezterm}/bin/wezterm"; 14 | defaultBrowser = "zen-twilight"; 15 | 16 | home = "/home/${username}"; 17 | configHome = (builtins.getAttr username config.home-manager.users).xdg.configHome; 18 | documentsFolder = "documents"; 19 | downloadFolder = "downloads"; 20 | musicFolder = "music"; 21 | picturesFolder = "pictures"; 22 | videosFolder = "videos"; 23 | repositoriesFolder = "repos"; 24 | screenshotFolder = "${picturesFolder}/screenshots"; 25 | screencastFolder = "${videosFolder}/screencasts"; 26 | wallpapersFolder = "${picturesFolder}/wallpapers"; 27 | defaultFont = { 28 | name = "Roboto"; 29 | package = pkgs.roboto; 30 | }; 31 | defaultMonoFont = { 32 | name = "DejaVuSansM Nerd Font"; 33 | package = pkgs.nerd-fonts.dejavu-sans-mono; 34 | }; 35 | 36 | sshPublicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGyqgAJe9NTMN895kztljIIPYIRExKOdDvB6zroete6Z sweenu@carokann"; 37 | 38 | timezone = "Europe/Paris"; 39 | 40 | dnsResolvers = [ 41 | "9.9.9.9" 42 | "1.1.1.1" 43 | ]; 44 | 45 | # najdorf 46 | defaultGateway = "192.168.1.254"; 47 | staticIPWithSubnet = "192.168.1.41/24"; 48 | staticIP = builtins.head (lib.splitString "/" staticIPWithSubnet); 49 | domainName = "sweenu.xyz"; 50 | smtp = { 51 | user = "contact@sweenu.xyz"; 52 | host = "smtp.fastmail.com"; 53 | port = 465; 54 | }; 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /profiles/core/helix/default.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | { 4 | home-manager.users."${config.vars.username}".programs.helix = rec { 5 | enable = false; 6 | 7 | settings = { 8 | theme = "material_oceanic_transparent"; 9 | editor = { 10 | cursor-shape.insert = "bar"; 11 | soft-wrap.enable = true; 12 | statusline = { 13 | mode = { 14 | normal = "normal"; 15 | insert = "insert"; 16 | select = "select"; 17 | }; 18 | }; 19 | }; 20 | keys = { 21 | normal = { 22 | "*" = [ 23 | "move_prev_word_start" 24 | "move_next_word_end" 25 | "search_selection" 26 | "search_next" 27 | ]; 28 | A-o = "add_newline_below"; 29 | A-O = "add_newline_above"; # TODO: not working 30 | y = [ 31 | "yank" 32 | "yank_main_selection_to_clipboard" 33 | ]; 34 | d = [ 35 | "yank_to_clipboard" 36 | "delete_selection" 37 | ]; 38 | }; 39 | }; 40 | }; 41 | 42 | themes.${settings.theme} = { 43 | inherits = "material_oceanic"; 44 | "ui.background" = { }; 45 | palette = 46 | let 47 | palette = config.home-manager.users."${config.vars.username}".colorScheme.palette; 48 | in 49 | { 50 | red = "#${palette.base08}"; 51 | orange = "#${palette.base09}"; 52 | yellow = "#${palette.base0A}"; 53 | green = "#${palette.base0B}"; 54 | cyan = "#${palette.base0C}"; 55 | blue = "#${palette.base0D}"; 56 | purple = "#${palette.base0E}"; 57 | gray = "#${palette.base0F}"; 58 | text = "#eeffff"; 59 | }; 60 | }; 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /pkgs/backlight.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from subprocess import run, PIPE 3 | 4 | BACKLIGHT_CMD = [ 5 | "caelestia-shell", 6 | "ipc", 7 | "--any-display", 8 | "call", 9 | "brightness" 10 | ] 11 | 12 | 13 | def _get_brightness() -> int: 14 | output = run(BACKLIGHT_CMD + ["get"], stdout=PIPE).stdout 15 | return float(output.decode("utf-8")) * 100 16 | 17 | 18 | def _increase_brightness(percentage: int) -> None: 19 | run(BACKLIGHT_CMD + ["set", f"+{str(percentage)}%"]) 20 | 21 | 22 | def _decrease_brightness(percentage: int) -> None: 23 | run(BACKLIGHT_CMD + ["set", f"{str(percentage)}%-"]) 24 | 25 | 26 | def _set_brightness(value: str) -> None: 27 | run(BACKLIGHT_CMD + ["set", value]) 28 | 29 | 30 | def change_backlight( 31 | action: str, 32 | limit: int = 10, 33 | small_step: int = 1, 34 | big_step: int = 5, 35 | ) -> None: 36 | """Increases or decreases brightness. 37 | 38 | Takes 'dec' or 'inc' as parameter for 'action'. 39 | Goes `small_step` percent at a time from 1 to `limit` percent then 40 | `big_step` percent at a time from `limit` to 100 percent. 41 | """ 42 | current_brightness = _get_brightness() 43 | 44 | if action == "inc": 45 | if current_brightness < limit: 46 | if current_brightness < 1: 47 | _set_brightness("1%") 48 | else: 49 | _increase_brightness(small_step) 50 | else: 51 | _increase_brightness(big_step) 52 | elif action == "dec": 53 | if current_brightness <= limit: 54 | if current_brightness > 1: 55 | _decrease_brightness(small_step) 56 | else: 57 | _set_brightness("0") 58 | else: 59 | _decrease_brightness(big_step) 60 | 61 | 62 | if __name__ == "__main__": 63 | change_backlight(sys.argv[1]) 64 | -------------------------------------------------------------------------------- /profiles/core/home.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | { 4 | home-manager.users."${config.vars.username}" = { 5 | home = { 6 | username = "${config.vars.username}"; 7 | homeDirectory = "${config.vars.home}"; 8 | stateVersion = config.system.stateVersion; 9 | sessionVariables = { 10 | EDITOR = "kak"; 11 | VISUAL = "kak"; 12 | PAGER = "less -R"; 13 | TERM = "${config.vars.terminal}"; 14 | BROWSER = "${config.vars.defaultBrowser}"; 15 | }; 16 | packages = with pkgs; [ 17 | neofetch 18 | rclone 19 | ]; 20 | }; 21 | programs = { 22 | bottom.enable = true; 23 | btop = { 24 | enable = true; 25 | settings = { 26 | theme_background = false; 27 | vim_keys = true; 28 | update_ms = 1000; 29 | proc_tree = true; 30 | }; 31 | }; 32 | broot = { 33 | enable = true; 34 | settings.modal = true; 35 | }; 36 | bat = { 37 | enable = true; 38 | config = { 39 | theme = "zenburn"; 40 | pager = "less"; 41 | }; 42 | }; 43 | home-manager.enable = true; 44 | skim = rec { 45 | enable = true; 46 | defaultCommand = "fd --type f --hidden --exclude '.git'"; 47 | defaultOptions = [ 48 | "--height 40%" 49 | "--inline-info" 50 | ]; 51 | changeDirWidgetCommand = "fd --type d --hidden --exclude '.git'"; 52 | fileWidgetCommand = defaultCommand; 53 | fileWidgetOptions = [ 54 | "--preview 'bat --color always {} 2> /dev/null | head -200; highlight -O ansi -l {} ^ /dev/null | head -200 || cat {} ^ /dev/null | head -200'" 55 | ]; 56 | historyWidgetOptions = [ "--tac" ]; 57 | }; 58 | zoxide = { 59 | enable = true; 60 | }; 61 | }; 62 | }; 63 | } 64 | -------------------------------------------------------------------------------- /hosts/najdorf/obsidian-livesync.nix: -------------------------------------------------------------------------------- 1 | { self, config, ... }: 2 | 3 | let 4 | obsidianLiveSyncDir = "/opt/obsidian-livesync"; 5 | in 6 | { 7 | age.secrets."obsidian-livesync/envFile".file = "${self}/secrets/obsidian-livesync/env.age"; 8 | 9 | virtualisation.arion.projects.obsidian-livesync.settings = { 10 | enableDefaultNetwork = false; 11 | networks.traefik.external = true; 12 | services.obsidian-livesync.service = { 13 | image = "couchdb:3"; 14 | container_name = "obsidian-livesync"; 15 | volumes = [ 16 | "${obsidianLiveSyncDir}/data:/opt/couchdb/data" 17 | "${obsidianLiveSyncDir}/etc:/opt/couchdb/etc/local.d" 18 | ]; 19 | networks = [ config.services.traefik.staticConfigOptions.providers.docker.network ]; 20 | env_file = [ config.age.secrets."obsidian-livesync/envFile".path ]; 21 | labels = { 22 | "traefik.enable" = "true"; 23 | "traefik.http.routers.to-obsidian-livesync.service" = "obsidian-livesync"; 24 | "traefik.http.services.obsidian-livesync.loadbalancer.server.port" = "5984"; 25 | "traefik.http.routers.obsidian-livesync.middlewares" = "obsidiancors"; 26 | "traefik.http.middlewares.obsidiancors.headers.accesscontrolallowmethods" = 27 | "GET,PUT,POST,HEAD,DELETE"; 28 | "traefik.http.middlewares.obsidiancors.headers.accesscontrolallowheaders" = 29 | "accept,authorization,content-type,origin,referer"; 30 | "traefik.http.middlewares.obsidiancors.headers.accesscontrolalloworiginlist" = 31 | "app://obsidian.md,capacitor://localhost,http://localhost"; 32 | "traefik.http.middlewares.obsidiancors.headers.accesscontrolmaxage" = "3600"; 33 | "traefik.http.middlewares.obsidiancors.headers.addvaryheader" = "true"; 34 | "traefik.http.middlewares.obsidiancors.headers.accessControlAllowCredentials" = "true"; 35 | }; 36 | }; 37 | }; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /profiles/graphical/zen/tridactylrc: -------------------------------------------------------------------------------- 1 | " Theme 2 | colorscheme zen 3 | 4 | " Open urls 5 | unbind t 6 | unbind T 7 | unbind w 8 | 9 | bind O fillcmdline tabopen 10 | bind W tabdetach 11 | bind ge current_url tabopen 12 | bind yt tabduplicate 13 | 14 | " Scrolling 15 | bind j scrollline 2 16 | bind k scrollline -2 17 | bind scrollline 2 18 | bind scrollline -2 19 | set smoothscroll true 20 | set scrollduration 10 21 | 22 | " Vim search mode 23 | bind / fillcmdline find 24 | bind ? fillcmdline find -? 25 | bind n findnext 1 26 | bind N findnext -1 27 | bind , nohlsearch 28 | 29 | " Misc 30 | set hintdelay 100 31 | set relatedopenpos last 32 | set tabopenpos last 33 | set modeindicatormodes normal false 34 | set editorcmd @terminalBin@ -e @editor@ 35 | set bmarkweight 10000000 "Makes the bookmarks appear first in :open 36 | set allowautofocus false 37 | " TODO: remove when https://github.com/tridactyl/tridactyl/commit/e1cce5f2b9c008da8880d14624ef9f20652ded43 is merged 38 | set completions.Tab.statusstylepretty true 39 | set completions.History.statusstylepretty true 40 | 41 | "Blacklist 42 | seturl grist.@domainName@ superignore true 43 | unbindurl calendar.notion.so ? 44 | unbindurl calendar.notion.so c 45 | unbindurl calendar.notion.so f 46 | unbindurl calendar.notion.so s 47 | unbindurl calendar.notion.so o 48 | unbindurl calendar.notion.so n 49 | unbindurl calendar.notion.so b 50 | unbindurl calendar.notion.so / 51 | unbindurl calendar.notion.so e 52 | unbindurl calendar.notion.so m 53 | unbindurl calendar.notion.so . 54 | unbindurl calendar.notion.so t 55 | unbindurl calendar.notion.so j 56 | unbindurl calendar.notion.so k 57 | unbindurl calendar.notion.so z 58 | unbindurl calendar.notion.so g 59 | 60 | 61 | " Git{Hub,Lab} git clone via SSH yank 62 | bind yg composite js "git clone " + document.location.href.replace(/https?:\/\//,"git@").replace("/",":").replace(/(.+?\/.+?\/).*/, "$1").replace(/\/?$/,".git") | clipboard yank 63 | -------------------------------------------------------------------------------- /pkgs/kakoune_plugins.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | kakouneUtils, 4 | fetchFromGitHub, 5 | }: 6 | 7 | let 8 | inherit (kakouneUtils) buildKakounePluginFrom2Nix; 9 | in 10 | { 11 | kak-surround = buildKakounePluginFrom2Nix { 12 | pname = "kak-surround"; 13 | version = "2018-09-17"; 14 | src = fetchFromGitHub { 15 | owner = "h-youhei"; 16 | repo = "kakoune-surround"; 17 | rev = "efe74c6f434d1e30eff70d4b0d737f55bf6c5022"; 18 | sha256 = "sha256-0PicMTkYRnhtrFAMWVgynE4HfoL9/EHZIu4rTSE+zSU="; 19 | }; 20 | meta.homepage = "https://github.com/h-youhei/kakoune-surround"; 21 | }; 22 | 23 | kak-sudo-write = buildKakounePluginFrom2Nix { 24 | pname = "kak-sudo-write"; 25 | version = "2021-08-16"; 26 | src = fetchFromGitHub { 27 | owner = "occivink"; 28 | repo = "kakoune-sudo-write"; 29 | rev = "ec0d6d26ceaadd93d6824630ba587b31e442214d"; 30 | sha256 = "sha256-O+yw8upyYnQThDoWKnFbjrjthPTCm6EaBUoJNqpUPLA="; 31 | }; 32 | meta.homepage = "https://github.com/occivink/kakoune-sudo-write"; 33 | }; 34 | 35 | kak-auto-pairs = buildKakounePluginFrom2Nix { 36 | pname = "kak-auto-pairs"; 37 | version = "2021-10-27"; 38 | src = fetchFromGitHub { 39 | owner = "alexherbo2"; 40 | repo = "auto-pairs.kak"; 41 | rev = "596872fb1bd6cf804ee984e005ec2e05ec6872c7"; 42 | sha256 = "sha256-5M0Omi+rSnXhm3WtU9tkBhhIcRCWaGTMOdbne7Z9Yvs="; 43 | }; 44 | meta.homepage = "https://github.com/alexherbo2/auto-pairs.kak"; 45 | }; 46 | 47 | kak-find = buildKakounePluginFrom2Nix { 48 | pname = "kak-find"; 49 | version = "2021-11-15"; 50 | src = fetchFromGitHub { 51 | owner = "occivink"; 52 | repo = "kakoune-find"; 53 | rev = "b424ad4edb62cb0f5ee15b26c6bdfca7797377fa"; 54 | sha256 = "sha256-tSDVIqqEsQnq8QG9/llvSgdxtR42WAA98Hv2NY5qN1g="; 55 | }; 56 | meta.homepage = "https://github.com/occivink/kakoune-find"; 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /modules/vars.nix: -------------------------------------------------------------------------------- 1 | { lib, ... }: 2 | 3 | with lib; 4 | with lib.types; 5 | { 6 | options.vars = { 7 | email = mkOption { type = str; }; 8 | username = mkOption { type = str; }; 9 | terminal = mkOption { type = str; }; 10 | terminalBin = mkOption { type = str; }; 11 | defaultBrowser = mkOption { type = str; }; 12 | 13 | home = mkOption { type = str; }; 14 | configHome = mkOption { type = str; }; 15 | documentsFolder = mkOption { type = str; }; 16 | downloadFolder = mkOption { type = str; }; 17 | musicFolder = mkOption { type = str; }; 18 | picturesFolder = mkOption { type = str; }; 19 | videosFolder = mkOption { type = str; }; 20 | repositoriesFolder = mkOption { type = str; }; 21 | screenshotFolder = mkOption { type = str; }; 22 | screencastFolder = mkOption { type = str; }; 23 | wallpapersFolder = mkOption { type = str; }; 24 | defaultFont = mkOption { 25 | type = submodule { 26 | options = { 27 | name = mkOption { type = str; }; 28 | package = mkOption { type = package; }; 29 | }; 30 | }; 31 | }; 32 | defaultMonoFont = mkOption { 33 | type = submodule { 34 | options = { 35 | name = mkOption { type = str; }; 36 | package = mkOption { type = package; }; 37 | }; 38 | }; 39 | }; 40 | 41 | sshPublicKey = mkOption { type = str; }; 42 | 43 | timezone = mkOption { type = str; }; 44 | 45 | dnsResolvers = mkOption { type = listOf str; }; 46 | 47 | # Server vars 48 | defaultGateway = mkOption { type = str; }; 49 | staticIPWithSubnet = mkOption { type = str; }; 50 | staticIP = mkOption { type = str; }; 51 | 52 | domainName = mkOption { type = str; }; 53 | smtp = mkOption { 54 | type = submodule { 55 | options = { 56 | user = mkOption { type = str; }; 57 | host = mkOption { type = str; }; 58 | port = mkOption { type = int; }; 59 | }; 60 | }; 61 | }; 62 | }; 63 | } 64 | -------------------------------------------------------------------------------- /profiles/develop/helix.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | { 4 | home-manager.users."${config.vars.username}".programs.helix = { 5 | languages = { 6 | language = [ 7 | { 8 | name = "nix"; 9 | formatter.command = "${pkgs.nixfmt-rfc-style}/bin/nixfmt"; 10 | auto-format = true; 11 | } 12 | { 13 | name = "python"; 14 | # formatter = { command = "${pkgs.ruff}/bin/ruff"; args = ["format"]; }; 15 | language-servers = [ 16 | "pylsp" 17 | "ruff" 18 | ]; 19 | auto-format = true; 20 | } 21 | ]; 22 | 23 | language-server = { 24 | ruff = { 25 | command = "${pkgs.ruff}/bin/ruff"; 26 | args = [ 27 | "server" 28 | "--preview" 29 | ]; 30 | }; 31 | pylsp = { 32 | command = "${pkgs.python311Packages.python-lsp-server}/bin/pylsp"; 33 | config = { 34 | pylsp = { 35 | plugins = { 36 | # TODO: find out why it doesn't work 37 | pylsp_mypy.enabled = true; 38 | }; 39 | }; 40 | }; 41 | }; 42 | }; 43 | }; 44 | 45 | extraPackages = with pkgs; [ 46 | # python 47 | python311Packages.jedi 48 | python311Packages.rope 49 | python311Packages.pylsp-rope 50 | python311Packages.pylsp-mypy 51 | 52 | # go 53 | gopls 54 | golangci-lint-langserver 55 | delve 56 | 57 | # rust 58 | rust-analyzer 59 | lldb 60 | 61 | # nix 62 | nil 63 | 64 | # html, css, json 65 | vscode-langservers-extracted 66 | 67 | # yaml 68 | yaml-language-server 69 | 70 | # C 71 | clang-tools 72 | 73 | # bash 74 | nodePackages.bash-language-server 75 | 76 | # javascript 77 | nodePackages.typescript-language-server 78 | 79 | # markdown 80 | marksman 81 | 82 | # protobuf 83 | buf 84 | pb 85 | ]; 86 | }; 87 | } 88 | -------------------------------------------------------------------------------- /secrets/secrets.nix: -------------------------------------------------------------------------------- 1 | let 2 | carokann = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGyqgAJe9NTMN895kztljIIPYIRExKOdDvB6zroete6Z sweenu@carokann"; 3 | najdorfHost = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINKpdd19cjYDViCu9mLhbMwf33ohOYXEEqg32MY9SP6s root@najdorf"; 4 | in 5 | { 6 | # najdorf 7 | "smtp_password.age".publicKeys = [ carokann najdorfHost ]; 8 | "restic/password.age".publicKeys = [ carokann najdorfHost ]; 9 | "restic/env.age".publicKeys = [ carokann najdorfHost ]; 10 | "traefik/env.age".publicKeys = [ carokann najdorfHost ]; 11 | "authelia/jwt_secret.age".publicKeys = [ carokann najdorfHost ]; 12 | "authelia/session_secret.age".publicKeys = [ carokann najdorfHost ]; 13 | "authelia/storage_encryption_key.age".publicKeys = [ carokann najdorfHost ]; 14 | "authelia/ldap_password.age".publicKeys = [ carokann najdorfHost ]; 15 | "authelia/oidc_hmac_secret.age".publicKeys = [ carokann najdorfHost ]; 16 | "authelia/oidc_jwt_private_key.age".publicKeys = [ carokann najdorfHost ]; 17 | "nextcloud/admin_password.age".publicKeys = [ carokann najdorfHost ]; 18 | "nextcloud/secrets.age".publicKeys = [ carokann najdorfHost ]; 19 | "n8n/encryption_key.age".publicKeys = [ carokann najdorfHost ]; 20 | "immich/env.age".publicKeys = [ carokann najdorfHost ]; 21 | "obsidian-livesync/env.age".publicKeys = [ carokann najdorfHost ]; 22 | "obsidian-share-note/env.age".publicKeys = [ carokann najdorfHost ]; 23 | "fastmail/app_password.age".publicKeys = [ carokann najdorfHost ]; 24 | "lldap/jwt_secret.age".publicKeys = [ carokann najdorfHost ]; 25 | "lldap/ldap_user_pass.age".publicKeys = [ carokann najdorfHost ]; 26 | "lldap/server_key.age".publicKeys = [ carokann najdorfHost ]; 27 | "grist/env.age".publicKeys = [ carokann najdorfHost ]; 28 | "grist/oidc_client_secret_digest.age".publicKeys = [ carokann najdorfHost ]; 29 | "hass/secrets.age".publicKeys = [ carokann najdorfHost ]; 30 | "dawarich/secret_key_base.age".publicKeys = [ carokann najdorfHost ]; 31 | "dawarich/oidc_client_secret_digest.age".publicKeys = [ carokann najdorfHost ]; 32 | "dawarich/oidc_client_secret_env.age".publicKeys = [ carokann najdorfHost ]; 33 | } 34 | -------------------------------------------------------------------------------- /profiles/pc/email.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | config, 4 | pkgs, 5 | ... 6 | }: 7 | 8 | { 9 | age.secrets.fastmailAppPassword = { 10 | file = "${self}/secrets/fastmail/app_password.age"; 11 | owner = config.vars.username; 12 | }; 13 | 14 | home-manager.users."${config.vars.username}" = 15 | let 16 | homeManagerConfig = config.home-manager.users."${config.vars.username}"; 17 | mailAccountName = "fastmail"; 18 | in 19 | { 20 | accounts.email.maildirBasePath = "${homeManagerConfig.xdg.dataHome}/mail"; 21 | accounts.email.accounts."${mailAccountName}" = { 22 | primary = true; 23 | flavor = "fastmail.com"; 24 | address = "contact@sweenu.xyz"; 25 | realName = "Sweenu"; 26 | passwordCommand = "${pkgs.coreutils}/bin/cat ${config.age.secrets.fastmailAppPassword.path}"; 27 | notmuch.enable = true; 28 | mbsync = { 29 | enable = true; 30 | create = "maildir"; 31 | }; 32 | himalaya = { 33 | enable = true; 34 | settings = { 35 | backend.type = "maildir"; 36 | backend.root-dir = homeManagerConfig.accounts.email.accounts."${mailAccountName}".maildir.absPath; 37 | }; 38 | }; 39 | }; 40 | 41 | programs = { 42 | himalaya = { 43 | enable = true; 44 | settings = { 45 | downloads-dir = "${config.vars.home}/${config.vars.downloadFolder}"; 46 | }; 47 | }; 48 | mbsync = { 49 | enable = true; 50 | }; 51 | notmuch.enable = true; 52 | }; 53 | 54 | services.mbsync = { 55 | enable = true; 56 | }; 57 | 58 | home.packages = with pkgs; [ 59 | (writeShellScriptBin "himalaya-tui" '' 60 | ${skim}/bin/sk -c '${himalaya}/bin/himalaya envelopes list -s 200' \ 61 | --preview "${himalaya}/bin/himalaya message read {1} -t html | \ 62 | ${w3m}/bin/w3m -T text/html" \ 63 | --bind 'space:execute(${himalaya}/bin/himalaya message reply {1})' \ 64 | --reverse --header-lines=2 --ansi 65 | '') 66 | ]; 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /profiles/graphical/zed/user_keymap.nix: -------------------------------------------------------------------------------- 1 | { mod }: 2 | [ 3 | { 4 | context = "Workspace"; 5 | bindings = { 6 | "${mod}-shift-r" = "workspace::Reload"; 7 | 8 | "${mod}-g n" = "pane::ActivateNextItem"; 9 | "${mod}-g p" = "pane::ActivatePreviousItem"; 10 | "${mod}-g x" = "pane::CloseActiveItem"; 11 | "${mod}-g X" = "pane::CloseAllItems"; 12 | 13 | "${mod}-1" = [ 14 | "pane::ActivateItem" 15 | 0 16 | ]; 17 | "${mod}-2" = [ 18 | "pane::ActivateItem" 19 | 1 20 | ]; 21 | "${mod}-3" = [ 22 | "pane::ActivateItem" 23 | 2 24 | ]; 25 | "${mod}-4" = [ 26 | "pane::ActivateItem" 27 | 3 28 | ]; 29 | "${mod}-5" = [ 30 | "pane::ActivateItem" 31 | 4 32 | ]; 33 | "${mod}-6" = [ 34 | "pane::ActivateItem" 35 | 5 36 | ]; 37 | "${mod}-7" = [ 38 | "pane::ActivateItem" 39 | 6 40 | ]; 41 | "${mod}-8" = [ 42 | "pane::ActivateItem" 43 | 7 44 | ]; 45 | "${mod}-9" = [ 46 | "pane::ActivateItem" 47 | 8 48 | ]; 49 | "${mod}-0" = [ 50 | "pane::ActivateItem" 51 | 9 52 | ]; 53 | "${mod}-'" = "pane::ActivateLastItem"; 54 | 55 | "${mod}-h" = "workspace::ActivatePaneLeft"; 56 | "${mod}-j" = "workspace::ActivatePaneDown"; 57 | "${mod}-k" = "workspace::ActivatePaneUp"; 58 | "${mod}-l" = "workspace::ActivatePaneRight"; 59 | 60 | "${mod}-shift-j" = "workspace::SwapPaneDown"; 61 | "${mod}-shift-k" = "workspace::SwapPaneUp"; 62 | "${mod}-shift-h" = "workspace::SwapPaneLeft"; 63 | "${mod}-shift-l" = "workspace::SwapPaneRight"; 64 | 65 | # Pane splits 66 | "alt-g v" = "pane::SplitDown"; 67 | 68 | # Pane management 69 | "alt-tab" = "workspace::ToggleZoom"; 70 | }; 71 | } 72 | { 73 | context = "Editor && vim_mode != insert"; 74 | bindings = { 75 | "${mod}-h" = "workspace::ActivatePaneLeft"; 76 | "${mod}-j" = "workspace::ActivatePaneDown"; 77 | "${mod}-k" = "workspace::ActivatePaneUp"; 78 | "${mod}-l" = "workspace::ActivatePaneRight"; 79 | }; 80 | } 81 | ] 82 | -------------------------------------------------------------------------------- /profiles/graphical/style.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | let 4 | theme = { 5 | name = "Adwaita"; 6 | package = pkgs.gnome-themes-extra; 7 | }; 8 | themeQt = { 9 | name = "Adwaita-dark"; 10 | package = pkgs.adwaita-qt; 11 | }; 12 | cursorTheme = { 13 | name = "capitaine-cursors-white"; 14 | size = 24; 15 | package = pkgs.capitaine-cursors; 16 | }; 17 | iconsTheme = { 18 | name = "Papirus-Dark"; 19 | package = pkgs.papirus-icon-theme; 20 | }; 21 | homeCfg = config.home-manager.users."${config.vars.username}"; 22 | in 23 | { 24 | environment.systemPackages = [ 25 | theme.package 26 | themeQt.package 27 | cursorTheme.package 28 | iconsTheme.package 29 | ]; 30 | 31 | fonts.packages = with pkgs; [ 32 | config.vars.defaultFont.package 33 | config.vars.defaultMonoFont.package 34 | font-awesome 35 | twitter-color-emoji 36 | material-symbols 37 | ]; 38 | 39 | home-manager.users."${config.vars.username}" = { 40 | home.pointerCursor = { 41 | enable = true; 42 | package = cursorTheme.package; 43 | name = cursorTheme.name; 44 | size = cursorTheme.size; 45 | gtk.enable = true; 46 | dotIcons.enable = true; 47 | hyprcursor.enable = homeCfg.wayland.windowManager.hyprland.enable; 48 | sway.enable = homeCfg.wayland.windowManager.sway.enable; 49 | x11.enable = false; 50 | }; 51 | 52 | gtk = { 53 | enable = true; 54 | colorScheme = "dark"; 55 | iconTheme = { 56 | package = iconsTheme.package; 57 | name = iconsTheme.name; 58 | }; 59 | theme = { 60 | package = theme.package; 61 | name = "${theme.name}"; 62 | }; 63 | font = { 64 | package = config.vars.defaultFont.package; 65 | name = config.vars.defaultFont.name; 66 | size = 12; 67 | }; 68 | gtk3 = { 69 | theme = { 70 | package = theme.package; 71 | name = "${theme.name}-dark"; 72 | }; 73 | }; 74 | }; 75 | 76 | qt = { 77 | enable = true; 78 | platformTheme.name = "gtk3"; # https://github.com/caelestia-dots/shell/issues/390 79 | style = { 80 | name = themeQt.name; 81 | package = themeQt.package; 82 | }; 83 | }; 84 | }; 85 | } 86 | -------------------------------------------------------------------------------- /hosts/najdorf/goeland.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | let 4 | feeds = { 5 | xe = "https://xeiaso.net/blog.json"; 6 | determinate-systems = "https://determinate.systems/posts?format=rss"; 7 | emersion = "https://emersion.fr/blog/atom.xml"; 8 | drew-devault = "https://drewdevault.com/blog/index.xml"; 9 | tailscale = "https://tailscale.com/blog/index.xml"; 10 | poettering = "https://0pointer.net/blog/index.atom"; 11 | notion = "https://rss.app/feeds/NsIZtd0vgm0BsqKL.xml"; 12 | starlwart = "https://stalwartlabs.medium.com/feed"; 13 | alternativeto = "https://feed.alternativeto.net/news/all/"; 14 | home-assistant = "https://www.home-assistant.io/atom.xml"; 15 | zed = "https://zed.dev/blog.rss"; 16 | }; 17 | in 18 | { 19 | users.users.goeland.extraGroups = [ "smtp" ]; 20 | 21 | services.goeland = { 22 | enable = true; 23 | schedule = "Mon, 00:00:00"; 24 | settings = { 25 | loglevel = "info"; 26 | email = { 27 | host = config.vars.smtp.host; 28 | port = config.vars.smtp.port; 29 | username = config.vars.smtp.user; 30 | password_file = config.age.secrets.smtpPassword.path; 31 | include-header = true; 32 | include-footer = true; 33 | include-title = true; 34 | include-content = false; 35 | include-toc = true; 36 | encryption = "ssl"; 37 | }; 38 | pipes.weekly-digest = { 39 | source = "all"; 40 | destination = "email"; 41 | email_to = [ config.vars.email ]; 42 | email_from = "goeland <${config.vars.email}>"; 43 | email_title = "Weekly digest"; 44 | }; 45 | sources = 46 | let 47 | lastWeekFilter = "lasthours(${toString (7 * 24)})"; 48 | defaultFilters = [ 49 | lastWeekFilter 50 | "includelink" 51 | "toc(title)" 52 | "first" 53 | ]; 54 | makeSource = name: url: { 55 | type = "feed"; 56 | filters = defaultFilters; 57 | url = url; 58 | }; 59 | in 60 | (builtins.mapAttrs makeSource feeds) 61 | // { 62 | all = { 63 | type = "merge"; 64 | filters = [ "digest" ]; 65 | sources = builtins.attrNames feeds; 66 | }; 67 | }; 68 | }; 69 | }; 70 | } 71 | -------------------------------------------------------------------------------- /hosts/najdorf/grist.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | config, 4 | ... 5 | }: 6 | 7 | let 8 | gristDir = "/opt/grist"; 9 | fqdn = "grist.${config.vars.domainName}"; 10 | gristUrl = "https://${fqdn}"; 11 | oidcClientId = "grist"; 12 | autheliaUrl = "https://authelia.${config.vars.domainName}"; 13 | in 14 | { 15 | age.secrets = { 16 | "grist/envFile".file = "${self}/secrets/grist/env.age"; 17 | "grist/oidcClientSecretDigest" = { 18 | file = "${self}/secrets/grist/oidc_client_secret_digest.age"; 19 | owner = config.services.authelia.instances.main.user; 20 | }; 21 | }; 22 | 23 | virtualisation.arion.projects.grist.settings = { 24 | networks.traefik.external = true; 25 | 26 | services = { 27 | grist.service = { 28 | image = "gristlabs/grist:1.6"; 29 | container_name = "grist"; 30 | networks = [ 31 | "default" 32 | config.services.traefik.staticConfigOptions.providers.docker.network 33 | ]; 34 | volumes = [ 35 | "${gristDir}:/persist" 36 | ]; 37 | environment = { 38 | APP_HOME_URL = gristUrl; 39 | GRIST_LOG_LEVEL = "warn"; 40 | GRIST_SINGLE_ORG = "grist"; 41 | GRIST_FORCE_LOGIN = "true"; 42 | GRIST_SANDBOX_FLAVOR = "gvisor"; 43 | # OIDC 44 | GRIST_OIDC_IDP_ISSUER = autheliaUrl; 45 | GRIST_OIDC_IDP_CLIENT_ID = oidcClientId; 46 | GRIST_OIDC_IDP_END_SESSION_ENDPOINT = "${autheliaUrl}/logout"; 47 | }; 48 | env_file = [ config.age.secrets."grist/envFile".path ]; # sets GRIST_DEFAULT_EMAIL, GRIST_SESSION_SECRET and GRIST_OIDC_IDP_CLIENT_SECRET 49 | labels = { 50 | "traefik.enable" = "true"; 51 | "traefik.http.routers.to-grist.service" = "grist"; 52 | "traefik.http.services.grist.loadbalancer.server.port" = "8484"; 53 | "traefik.http.routers.to-grist.middlewares" = "authelia@file"; 54 | }; 55 | }; 56 | }; 57 | }; 58 | 59 | services.authelia.instances.main.settings.identity_providers.oidc.clients = [ 60 | { 61 | client_id = oidcClientId; 62 | client_name = "Grist"; 63 | client_secret = ''{{ secret "${config.age.secrets."grist/oidcClientSecretDigest".path}" }}''; 64 | scopes = [ 65 | "openid" 66 | "groups" 67 | "email" 68 | "profile" 69 | ]; 70 | authorization_policy = "one_factor"; 71 | redirect_uris = [ "${gristUrl}/oauth2/callback" ]; 72 | pre_configured_consent_duration = "1y"; 73 | } 74 | ]; 75 | } 76 | -------------------------------------------------------------------------------- /profiles/graphical/hyprland/windowrules.nix: -------------------------------------------------------------------------------- 1 | [ 2 | "opaque, class:org\\.quickshell|imv|swappy" # They use native transparency or we want them opaque 3 | "center 1, floating:1, xwayland:0 # Center all floating windows (not xwayland cause popups)" 4 | 5 | # Float 6 | "float, class:guifetch" 7 | "float, class:wev" 8 | "float, class:org\\.gnome\\.FileRoller" 9 | "float, class:file-roller" 10 | "float, class:feh" 11 | "float, class:imv" 12 | "float, class:system-config-printer" 13 | "float, class:org\\.quickshell" 14 | "float, class:Zoom Workspace, title:menu window" 15 | "float, class:org\\.kde\\.kdeconnect\\.sms" 16 | 17 | # Float, resize and center 18 | "float, class:org\\.gnome\\.Settings" 19 | "size 70% 80%, class:org\\.gnome\\.Settings" 20 | "center 1, class:org\\.gnome\\.Settings" 21 | "float, class:com\\.saivert\\.pwvucontrol" 22 | "size 60% 70%, class:com\\.saivert\\.pwvucontrol" 23 | "center 1, class:com\\.saivert\\.pwvucontrol" 24 | "float, class:nwg-look" 25 | "size 50% 60%, class:nwg-look" 26 | "center 1, class:nwg-look" 27 | "float, class:signal" 28 | "size 773 471, class:signal" 29 | "center 1, class:signal" 30 | "float, class:com\\.nextcloud\\.desktopclient\\.nextcloud" 31 | "size 607 607, class:com\\.nextcloud\\.desktopclient\\.nextcloud" 32 | "center 1, class:com\\.nextcloud\\.desktopclient\\.nextcloud" 33 | "float, class:wdisplays" 34 | "size 1021 622, class:wdisplays" 35 | "center 1, class:wdisplays" 36 | "float, class:io\\.missioncenter\\.MissionCenter" 37 | "size 60% 60%, class:io\\.missioncenter\\.MissionCenter" 38 | "center 1, class:io\\.missioncenter\\.MissionCenter" 39 | 40 | # Dialogs 41 | "float, title:(Select|Open)( a)? (File|Folder)(s)?" 42 | "float, title:File (Operation|Upload)( Progress)?" 43 | "float, title:.* Properties" 44 | "float, title:Export Image as PNG" 45 | "float, title:GIMP Crash Debug" 46 | "float, title:Save As" 47 | "float, title:Library" 48 | 49 | # Picture in picture 50 | "move 100%-w-2% 100%-w-3%, title:Picture(-| )in(-| )[Pp]icture" 51 | "size <25% <25%, title:Picture(-| )in(-| )[Pp]icture" 52 | "keepaspectratio, title:Picture(-| )in(-| )[Pp]icture" 53 | "float, title:Picture(-| )in(-| )[Pp]icture" 54 | "pin, title:Picture(-| )in(-| )[Pp]icture" 55 | 56 | # xwayland popups 57 | "nodim, xwayland:1, title:win[0-9]+" 58 | "noshadow, xwayland:1, title:win[0-9]+" 59 | "rounding 10, xwayland:1, title:win[0-9]+" 60 | 61 | # Kill Firefox sharing indicator 62 | "suppressevent maximize fullscreen, title:Firefox.*Sharing Indicator" 63 | 64 | # App specific 65 | "workspace special:signal, class:Signal" 66 | ] 67 | -------------------------------------------------------------------------------- /profiles/graphical/base.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | pkgs, 4 | config, 5 | ... 6 | }: 7 | 8 | { 9 | boot.extraModulePackages = [ config.boot.kernelPackages.ddcci-driver ]; 10 | boot.kernelModules = [ "ddcci" ]; 11 | 12 | programs = { 13 | kdeconnect.enable = true; 14 | thunar.enable = true; 15 | zoom-us.enable = true; 16 | }; 17 | 18 | environment.defaultPackages = with pkgs; [ 19 | calibre 20 | imv 21 | libnotify 22 | obsidian 23 | pwvucontrol 24 | # TODO: need https://github.com/NixOS/nixpkgs/pull/454282 25 | # rpi-imager 26 | vlc 27 | xdg-utils 28 | ]; 29 | 30 | home-manager.users."${config.vars.username}" = { 31 | home.file = { 32 | ".face".source = "${self}/assets/pp.png"; 33 | "${config.vars.screenshotFolder}/.keep".source = builtins.toFile "keep" ""; 34 | "${config.vars.screencastFolder}/.keep".source = builtins.toFile "keep" ""; 35 | "${config.vars.wallpapersFolder}/.keep".source = builtins.toFile "keep" ""; 36 | }; 37 | 38 | services = { 39 | kdeconnect = { 40 | enable = true; 41 | indicator = true; 42 | }; 43 | }; 44 | 45 | xdg = { 46 | portal = { 47 | enable = true; 48 | xdgOpenUsePortal = true; 49 | extraPortals = with pkgs; [ 50 | xdg-desktop-portal-gtk 51 | ]; 52 | }; 53 | 54 | autostart = { 55 | enable = true; 56 | }; 57 | 58 | desktopEntries = { 59 | img = { 60 | name = "Image Viewer"; 61 | exec = "${pkgs.imv}/bin/imv %u"; 62 | categories = [ "Application" ]; 63 | }; 64 | pdf = { 65 | name = "PDF reader"; 66 | exec = "${pkgs.zathura}/bin/zathura %u"; 67 | categories = [ "Application" ]; 68 | }; 69 | text = { 70 | name = "Text editor"; 71 | exec = "${config.vars.terminalBin} -e kak %u"; 72 | categories = [ "Application" ]; 73 | }; 74 | }; 75 | 76 | mime.enable = true; 77 | mimeApps = { 78 | enable = true; 79 | defaultApplications = { 80 | "text/plain" = [ "text.desktop" ]; 81 | "application/postscript" = [ "pdf.desktop" ]; 82 | "application/pdf" = [ "pdf.desktop" ]; 83 | "image/apng" = [ "img.desktop" ]; 84 | "image/png" = [ "img.desktop" ]; 85 | "image/jpeg" = [ "img.desktop" ]; 86 | "image/gif" = [ "img.desktop" ]; 87 | "image/avif" = [ "img.desktop" ]; 88 | "image/svg+xml" = [ "img.desktop" ]; 89 | "image/webp" = [ "img.desktop" ]; 90 | }; 91 | }; 92 | }; 93 | }; 94 | } 95 | -------------------------------------------------------------------------------- /hosts/najdorf/traefik.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | config, 4 | ... 5 | }: 6 | 7 | { 8 | age.secrets = { 9 | traefikEnvFile.file = "${self}/secrets/traefik/env.age"; 10 | }; 11 | 12 | networking.firewall.allowedTCPPorts = [ 13 | 80 14 | 443 15 | ]; 16 | 17 | # HTTP/3 18 | networking.firewall.allowedUDPPorts = [ 19 | 443 20 | ]; 21 | 22 | services.traefik = { 23 | enable = true; 24 | dataDir = "/opt/traefik"; 25 | group = "docker"; 26 | environmentFiles = [ config.age.secrets.traefikEnvFile.path ]; 27 | staticConfigOptions = { 28 | global.checkNewVersion = false; 29 | providers = { 30 | docker = { 31 | network = "traefik"; 32 | exposedByDefault = false; 33 | defaultRule = "Host(`{{ index .Labels \"com.docker.compose.service\" }}.${config.vars.domainName}`)"; 34 | }; 35 | }; 36 | entryPoints = { 37 | web = { 38 | address = ":80"; 39 | http = { 40 | redirections = { 41 | entryPoint = { 42 | to = "websecure"; 43 | scheme = "https"; 44 | }; 45 | }; 46 | }; 47 | }; 48 | websecure = { 49 | address = ":443"; 50 | http3 = { 51 | advertisedPort = 443; 52 | }; 53 | http = { 54 | middlewares = [ "sts-header@file" ]; 55 | tls = { 56 | certResolver = "default"; 57 | domains = [ 58 | { 59 | main = config.vars.domainName; 60 | sans = [ "*.${config.vars.domainName}" ]; 61 | } 62 | ]; 63 | }; 64 | }; 65 | }; 66 | }; 67 | api = { 68 | dashboard = true; 69 | insecure = false; 70 | }; 71 | ping = { }; 72 | accessLog = { }; 73 | certificatesResolvers = { 74 | default = { 75 | acme = { 76 | email = config.vars.email; 77 | storage = "${config.services.traefik.dataDir}/acme.json"; 78 | dnsChallenge = { 79 | provider = "cloudflare"; 80 | }; 81 | }; 82 | }; 83 | }; 84 | }; 85 | dynamicConfigOptions = { 86 | http = { 87 | middlewares = { 88 | "sts-header" = { 89 | headers = { 90 | stsSeconds = 63072000; 91 | }; 92 | }; 93 | }; 94 | routers.dashboard = { 95 | rule = "Host(`traefik.${config.vars.domainName}`)"; 96 | service = "api@internal"; 97 | middlewares = [ "authelia" ]; 98 | }; 99 | }; 100 | }; 101 | }; 102 | } 103 | -------------------------------------------------------------------------------- /profiles/core/base.nix: -------------------------------------------------------------------------------- 1 | { pkgs, lib, ... }: 2 | 3 | { 4 | systemd.services.NetworkManager-wait-online.enable = false; 5 | boot.tmp.cleanOnBoot = true; 6 | 7 | environment = { 8 | # Using anything other than bash seems to not be recommended in Nix 9 | # binsh = ${pkgs.dash}/bin/dash; 10 | defaultPackages = with pkgs; [ 11 | bind 12 | binutils 13 | curl 14 | dua 15 | dust 16 | dnsutils 17 | entr 18 | ethtool 19 | fd 20 | file 21 | gdu 22 | git 23 | highlight 24 | inetutils 25 | iputils 26 | jq 27 | kakoune 28 | less 29 | lshw 30 | moreutils 31 | nmap 32 | nvd 33 | nix-tree 34 | pciutils 35 | psmisc 36 | procs 37 | ripgrep 38 | rsync 39 | skim 40 | speedtest-cli 41 | tmux 42 | gtrash 43 | unzip 44 | usbutils 45 | uutils-coreutils 46 | wget2 47 | whois 48 | yq 49 | zip 50 | ]; 51 | variables = { 52 | EDITOR = "kak"; 53 | VISUAL = "less"; 54 | }; 55 | }; 56 | 57 | hardware.enableAllFirmware = true; 58 | 59 | networking = { 60 | useDHCP = false; 61 | firewall.enable = true; 62 | }; 63 | 64 | nix = { 65 | package = pkgs.nixVersions.latest; 66 | gc.automatic = true; 67 | optimise.automatic = true; 68 | settings = { 69 | sandbox = true; 70 | trusted-users = [ 71 | "root" 72 | "@wheel" 73 | ]; 74 | auto-optimise-store = true; 75 | min-free = 536870912; 76 | keep-outputs = true; 77 | keep-derivations = true; 78 | fallback = true; 79 | warn-dirty = false; 80 | }; 81 | }; 82 | 83 | location.provider = "geoclue2"; 84 | 85 | programs = { 86 | fish.enable = true; 87 | mosh.enable = true; 88 | mtr.enable = true; 89 | }; 90 | 91 | security = { 92 | rtkit.enable = true; 93 | sudo.extraConfig = "Defaults timestamp_timeout=300"; 94 | }; 95 | 96 | services = { 97 | avahi = { 98 | nssmdns4 = true; 99 | publish = { 100 | enable = true; 101 | addresses = true; 102 | domain = true; 103 | userServices = true; 104 | }; 105 | }; 106 | earlyoom = { 107 | enable = true; 108 | enableNotifications = true; 109 | freeMemThreshold = 5; 110 | }; 111 | geoclue2.enable = true; 112 | locate = { 113 | enable = true; 114 | interval = "daily"; 115 | package = pkgs.plocate; 116 | }; 117 | resolved = { 118 | extraConfig = '' 119 | # No need when using Avahi 120 | MulticastDNS=no 121 | ''; 122 | }; 123 | }; 124 | 125 | systemd.settings.Manager = { 126 | DefaultLimitNOFILE = 1048576; 127 | }; 128 | 129 | system.stateVersion = lib.mkDefault "24.05"; 130 | } 131 | -------------------------------------------------------------------------------- /profiles/graphical/beeper.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | { 4 | home-manager.users."${config.vars.username}".programs.beeper = { 5 | enable = true; 6 | mergeWithExisting = true; 7 | overrideOnConflict = false; 8 | settings = { 9 | keybindings = { 10 | ARCHIVE_ALL_READ_THREADS = "disabled"; 11 | TOGGLE_THREAD_READ = "disabled"; 12 | TOGGLE_THREAD_MUTE = "disabled"; 13 | SEND_FILE = { 14 | keys = [ "alt+o" ]; 15 | }; 16 | CREATE_NEW_CHAT = { 17 | keys = [ "alt+n" ]; 18 | }; 19 | TOGGLE_THREAD_ARCHIVE = { 20 | keys = [ "alt+a" ]; 21 | }; 22 | TOGGLE_THREAD_PIN = { 23 | keys = [ "alt+p" ]; 24 | }; 25 | HISTORY_NEXT_THREAD = { 26 | keys = [ "alt+j" ]; 27 | disabled = true; 28 | }; 29 | HISTORY_PREV_THREAD = { 30 | keys = [ "alt+k" ]; 31 | disabled = true; 32 | }; 33 | OPEN_PROFILE = "disabled"; 34 | OPEN_REMIND_LATER_MENU = { 35 | keys = [ "alt+l" ]; 36 | }; 37 | SCROLL_TO_END_OF_CHAT = { 38 | keys = [ "alt+g" ]; 39 | }; 40 | SELECT_NEXT_THREAD = { 41 | keys = [ "alt+j" ]; 42 | }; 43 | SELECT_NEXT_UNREAD_THREAD = "disabled"; 44 | SELECT_PREV_THREAD = { 45 | keys = [ "alt+k" ]; 46 | }; 47 | SWITCH_FIRST_9_THREADS = { 48 | keys = [ "alt" ]; 49 | }; 50 | TOGGLE_THREAD_INFO = { 51 | keys = [ "alt+i" ]; 52 | }; 53 | MINIMIZE_TO_TRAY = "disabled"; 54 | QUIT_APP = "disabled"; 55 | RELOAD_APP = { 56 | keys = [ "alt+shift+r" ]; 57 | }; 58 | REPORT_A_PROBLEM = "disabled"; 59 | SEARCH = { 60 | keys = [ "alt+/" ]; 61 | }; 62 | SELECT_NEXT_ACCOUNT = "disabled"; 63 | SELECT_PREV_ACCOUNT = "disabled"; 64 | SEND_FEEDBACK = "disabled"; 65 | TOGGLE_PREFS_PANE = "disabled"; 66 | SWITCH_FIRST_9_ACCOUNTS = "disabled"; 67 | TOGGLE_SIDEBAR = { 68 | keys = [ "alt+s" ]; 69 | }; 70 | TOGGLE_FILTER_BAR = "disabled"; 71 | TOGGLE_HOTKEYS_MODAL = "disabled"; 72 | DOWNLOAD_ATTACHMENTS = { 73 | keys = [ "alt+d" ]; 74 | }; 75 | EDIT_MESSAGE = { 76 | keys = [ "alt+e" ]; 77 | }; 78 | QUOTE_AND_REPLY = { 79 | keys = [ "alt+r" ]; 80 | }; 81 | FORWARD_MESSAGES = { 82 | keys = [ "alt+f" ]; 83 | }; 84 | SHOW_AUDIO_BAR = { 85 | keys = [ "alt+shift+a" ]; 86 | }; 87 | SCHEDULE_MESSAGE = { 88 | keys = [ "alt+shift+l" ]; 89 | }; 90 | SHOW_GIF_PICKER = { 91 | keys = [ "alt+shift+g" ]; 92 | }; 93 | SHOW_TRANSCRIBE_BAR = { 94 | keys = [ "alt+shift+t" ]; 95 | }; 96 | }; 97 | }; 98 | }; 99 | } 100 | -------------------------------------------------------------------------------- /hosts/najdorf/lldap.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | config, 4 | lib, 5 | ... 6 | }: 7 | 8 | let 9 | dbName = "lldap"; 10 | dbUser = "lldap"; 11 | fqdn = "lldap.${config.vars.domainName}"; 12 | domainParts = lib.strings.splitString "\\." config.vars.domainName; 13 | dcParts = map (part: "dc=${part}") domainParts; 14 | baseDN = builtins.concatStringsSep "," dcParts; 15 | jwtSecretCredName = "jwt_secret"; 16 | ldapUserPassCredName = "ldap_user_pass"; 17 | smtpPasswordCredName = "smtp_password"; 18 | serverKeyCredName = "server_key"; 19 | in 20 | { 21 | age.secrets = { 22 | "lldap/jwtSecret".file = "${self}/secrets/lldap/jwt_secret.age"; 23 | "lldap/ldapUserPass".file = "${self}/secrets/lldap/ldap_user_pass.age"; 24 | "lldap/serverKey".file = "${self}/secrets/lldap/server_key.age"; 25 | }; 26 | 27 | # Until https://github.com/NixOS/nixpkgs/pull/447164 28 | systemd.services.lldap.after = [ "postgresql.target" ]; 29 | 30 | services.lldap = { 31 | enable = true; 32 | environment = { 33 | LLDAP_JWT_SECRET_FILE = "%d/${jwtSecretCredName}"; 34 | LLDAP_LDAP_USER_PASS_FILE = "%d/${ldapUserPassCredName}"; 35 | LLDAP_SMTP_OPTIONS__PASSWORD_FILE = "%d/${smtpPasswordCredName}"; 36 | LLDAP_KEY_FILE = "%d/${serverKeyCredName}"; 37 | }; 38 | silenceForceUserPassResetWarning = true; 39 | settings = { 40 | database_url = "postgresql:///${dbName}?host=/run/postgresql"; 41 | http_host = "127.0.0.1"; 42 | http_url = "https://${fqdn}"; 43 | ldap_host = "127.0.0.1"; 44 | ldap_base_dn = baseDN; 45 | ldap_user_dn = "admin"; 46 | ldap_user_email = config.vars.email; 47 | smtp_options = { 48 | server = config.vars.smtp.host; 49 | port = config.vars.smtp.port; 50 | user = config.vars.smtp.user; 51 | from = "LLDAP Admin <${config.vars.email}>"; 52 | reply_to = "Do not reply "; 53 | }; 54 | }; 55 | }; 56 | 57 | services.postgresql = { 58 | enable = true; 59 | ensureDatabases = [ dbName ]; 60 | ensureUsers = [ 61 | { 62 | name = dbUser; 63 | ensureDBOwnership = true; 64 | } 65 | ]; 66 | }; 67 | 68 | systemd.services.lldap.serviceConfig = { 69 | SupplementaryGroups = "smtp"; 70 | LoadCredential = [ 71 | "${jwtSecretCredName}:${config.age.secrets."lldap/jwtSecret".path}" 72 | "${ldapUserPassCredName}:${config.age.secrets."lldap/ldapUserPass".path}" 73 | "${serverKeyCredName}:${config.age.secrets."lldap/serverKey".path}" 74 | "${smtpPasswordCredName}:${config.age.secrets.smtpPassword.path}" 75 | ]; 76 | }; 77 | 78 | services.traefik.dynamicConfigOptions.http = rec { 79 | routers.to-lldap = { 80 | rule = "Host(`${fqdn}`)"; 81 | service = "lldap"; 82 | }; 83 | services."${routers.to-lldap.service}".loadBalancer.servers = [ 84 | { 85 | url = "http://${config.services.lldap.settings.http_host}:${builtins.toString config.services.lldap.settings.http_port}"; 86 | } 87 | ]; 88 | }; 89 | } 90 | -------------------------------------------------------------------------------- /hosts/carokann/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | suites, 4 | pkgs, 5 | ... 6 | }: 7 | 8 | let 9 | encryptedRoot = "cryptroot"; 10 | in 11 | { 12 | imports = suites.laptop ++ [ 13 | ./shikane.nix 14 | ./pipewire.nix 15 | ]; 16 | 17 | disko = { 18 | devices = { 19 | disk.main = { 20 | device = "/dev/nvme0n1"; 21 | type = "disk"; 22 | content = { 23 | type = "gpt"; 24 | partitions = { 25 | ESP = { 26 | type = "EF00"; 27 | size = "512M"; 28 | content = { 29 | type = "filesystem"; 30 | mountpoint = "/boot"; 31 | format = "vfat"; 32 | mountOptions = [ "umask=0077" ]; 33 | }; 34 | }; 35 | root = { 36 | size = "100%"; 37 | content = { 38 | type = "luks"; 39 | name = encryptedRoot; 40 | content = { 41 | type = "filesystem"; 42 | mountpoint = "/"; 43 | format = "ext4"; 44 | }; 45 | }; 46 | }; 47 | }; 48 | }; 49 | }; 50 | }; 51 | }; 52 | 53 | boot = { 54 | binfmt.emulatedSystems = [ "aarch64-linux" ]; 55 | initrd = { 56 | availableKernelModules = [ 57 | "xhci_pci" 58 | "thunderbolt" 59 | "nvme" 60 | ]; 61 | systemd = { 62 | enable = true; 63 | }; 64 | luks.devices.${encryptedRoot} = { 65 | allowDiscards = true; 66 | bypassWorkqueues = true; 67 | }; 68 | }; 69 | kernelModules = [ "kvm-amd" ]; 70 | kernelPackages = pkgs.linuxPackages_latest; 71 | loader = { 72 | systemd-boot = { 73 | enable = true; 74 | editor = false; 75 | configurationLimit = 5; 76 | }; 77 | efi.canTouchEfiVariables = true; 78 | }; 79 | }; 80 | 81 | swapDevices = [ 82 | { 83 | device = "/var/swapfile"; 84 | size = 1024 * 32; 85 | } 86 | ]; 87 | 88 | hardware = { 89 | bluetooth = { 90 | enable = true; 91 | powerOnBoot = false; 92 | }; 93 | sane.enable = true; 94 | graphics.enable = true; 95 | i2c.enable = true; 96 | }; 97 | 98 | powerManagement.cpuFreqGovernor = "powersave"; 99 | 100 | services = { 101 | fstrim.enable = true; 102 | fprintd.enable = true; 103 | fwupd.enable = true; 104 | tailscale.useRoutingFeatures = "client"; 105 | }; 106 | 107 | time.timeZone = config.vars.timezone; 108 | 109 | virtualisation.docker = { 110 | enable = true; 111 | enableOnBoot = false; 112 | }; 113 | 114 | environment.defaultPackages = with pkgs; [ 115 | framework-tool 116 | ]; 117 | 118 | age.identityPaths = [ "${config.vars.home}/.ssh/id_ed25519" ]; 119 | home-manager.users."${config.vars.username}" = { 120 | home.file.".ssh/id_ed25519.pub".text = config.vars.sshPublicKey; 121 | }; 122 | } 123 | -------------------------------------------------------------------------------- /profiles/pc/base.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | { 4 | boot.plymouth = { 5 | enable = true; 6 | }; 7 | 8 | documentation.dev.enable = true; 9 | 10 | environment.defaultPackages = with pkgs; [ 11 | bitwarden-cli 12 | bitwarden-desktop 13 | beeper 14 | brightnessctl 15 | comma 16 | devenv 17 | ethtool 18 | ffmpeg 19 | gitui 20 | gh 21 | gpg-tui 22 | nix-search-tv 23 | nix-output-monitor 24 | nix-prefetch-git 25 | nix-prefetch-github 26 | nixpkgs-review 27 | python3Packages.ptpython 28 | simple-scan 29 | tealdeer 30 | tmate 31 | wol 32 | you-get 33 | yt-dlp 34 | ]; 35 | 36 | services = { 37 | avahi.enable = true; 38 | getty = { 39 | extraArgs = [ "--skip-login" ]; 40 | loginOptions = "${config.vars.username}"; 41 | }; 42 | gnome = { 43 | gnome-keyring.enable = true; 44 | gcr-ssh-agent.enable = true; 45 | }; 46 | printing = { 47 | enable = true; 48 | drivers = with pkgs; [ 49 | gutenprint 50 | gutenprintBin 51 | hplip 52 | ]; 53 | }; 54 | resolved = { 55 | enable = true; 56 | dnssec = "allow-downgrade"; 57 | dnsovertls = "opportunistic"; 58 | fallbackDns = config.vars.dnsResolvers; 59 | }; 60 | }; 61 | 62 | security.polkit.enable = true; 63 | security.pam.services.login.fprintAuth = false; 64 | security.pam.services.login.enableGnomeKeyring = true; 65 | 66 | networking.networkmanager = { 67 | enable = true; 68 | dns = "systemd-resolved"; 69 | }; 70 | 71 | boot.enableContainers = true; 72 | 73 | programs = { 74 | gnupg.agent.enable = true; 75 | nix-ld = { 76 | enable = true; 77 | }; 78 | }; 79 | 80 | home-manager.users."${config.vars.username}" = { 81 | programs = { 82 | keychain = { 83 | enable = true; 84 | keys = [ "id_ed25519" ]; 85 | }; 86 | zathura = { 87 | enable = true; 88 | options = { 89 | selection-clipboard = "clipboard"; 90 | }; 91 | }; 92 | glow = { 93 | enable = true; 94 | settings = { 95 | style = "auto"; 96 | local = false; 97 | width = 100; 98 | pager = true; 99 | }; 100 | }; 101 | git.package = pkgs.gitFull; 102 | }; 103 | 104 | manual.json.enable = true; # To use with manix 105 | 106 | services.playerctld.enable = true; 107 | 108 | xdg.userDirs = { 109 | enable = true; 110 | createDirectories = true; 111 | documents = "${config.vars.home}/${config.vars.documentsFolder}"; 112 | download = "${config.vars.home}/${config.vars.downloadFolder}"; 113 | music = "${config.vars.home}/${config.vars.musicFolder}"; 114 | pictures = "${config.vars.home}/${config.vars.picturesFolder}"; 115 | templates = "${config.vars.home}/${config.vars.repositoriesFolder}"; 116 | videos = "${config.vars.home}/${config.vars.videosFolder}"; 117 | desktop = "${config.vars.home}"; 118 | publicShare = "${config.vars.home}"; 119 | }; 120 | }; 121 | } 122 | -------------------------------------------------------------------------------- /hm-modules/beeper.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | 8 | let 9 | inherit (lib) 10 | mkEnableOption 11 | mkIf 12 | mkOption 13 | mkPackageOption 14 | types 15 | ; 16 | 17 | cfg = config.programs.beeper; 18 | jsonFormat = pkgs.formats.json { }; 19 | json5 = pkgs.python3Packages.toPythonApplication pkgs.python3Packages.json5; 20 | 21 | impureConfigMerger = empty: jqOperation: path: staticSettings: '' 22 | mkdir -p $(dirname ${lib.escapeShellArg path}) 23 | if [ ! -e ${lib.escapeShellArg path} ]; then 24 | # No file? Create it 25 | echo ${lib.escapeShellArg empty} > ${lib.escapeShellArg path} 26 | fi 27 | dynamic="$(${lib.getExe json5} --as-json ${lib.escapeShellArg path} 2>/dev/null || echo ${lib.escapeShellArg empty})" 28 | static="$(cat ${lib.escapeShellArg staticSettings})" 29 | config="$(${lib.getExe pkgs.jq} -n ${lib.escapeShellArg jqOperation} --argjson dynamic "$dynamic" --argjson static "$static")" 30 | printf '%s\n' "$config" > ${lib.escapeShellArg path} 31 | unset config 32 | ''; 33 | 34 | in 35 | { 36 | options.programs.beeper = { 37 | enable = mkEnableOption "Beeper messaging client"; 38 | 39 | package = mkPackageOption pkgs "beeper" { nullable = true; }; 40 | 41 | settings = mkOption { 42 | type = jsonFormat.type; 43 | default = { }; 44 | description = "Beeper configuration as a Nix attribute set"; 45 | }; 46 | 47 | mergeWithExisting = mkOption { 48 | type = types.bool; 49 | default = true; 50 | description = "Whether to merge with existing config file if it exists"; 51 | }; 52 | 53 | overrideOnConflict = mkOption { 54 | type = types.bool; 55 | default = false; 56 | description = "Override existing config file instead of erroring on merge conflicts"; 57 | }; 58 | }; 59 | 60 | config = mkIf cfg.enable { 61 | home.packages = mkIf (cfg.package != null) [ cfg.package ]; 62 | 63 | home.activation = lib.mkIf (cfg.mergeWithExisting && cfg.settings != { }) { 64 | beeperConfigActivation = lib.hm.dag.entryAfter [ "linkGeneration" ] ( 65 | let 66 | jqOperation = 67 | if cfg.overrideOnConflict then 68 | "$dynamic * $static" 69 | else 70 | # Check for conflicts and error if any 71 | '' 72 | $dynamic as $d | $static as $s | 73 | if ($d | keys | map(select($s[.] != null and $s[.] != $d[.])) | length) > 0 then 74 | error("Beeper config merge conflicts detected. Set overrideOnConflict = true to override conflicts.") 75 | else 76 | $d * $s 77 | end 78 | ''; 79 | in 80 | impureConfigMerger "{}" jqOperation "${config.xdg.configHome}/BeeperTexts/config.json" ( 81 | jsonFormat.generate "beeper-settings" cfg.settings 82 | ) 83 | ); 84 | }; 85 | 86 | xdg.configFile = lib.mkIf (!cfg.mergeWithExisting && cfg.settings != { }) { 87 | "BeeperTexts/config.json".source = jsonFormat.generate "beeper-settings" cfg.settings; 88 | }; 89 | }; 90 | 91 | meta.maintainers = [ lib.hm.maintainers.sweenu ]; 92 | } 93 | -------------------------------------------------------------------------------- /profiles/graphical/zen/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs, 3 | config, 4 | pkgs, 5 | lib, 6 | ... 7 | }: 8 | 9 | { 10 | home-manager.users."${config.vars.username}" = { 11 | programs.zen-browser = { 12 | enable = true; 13 | nativeMessagingHosts = with pkgs; [ 14 | tridactyl-native 15 | ]; 16 | policies = 17 | let 18 | mkLockedAttrs = builtins.mapAttrs ( 19 | _: value: { 20 | Value = value; 21 | Status = "locked"; 22 | } 23 | ); 24 | in 25 | { 26 | AutofillAddressEnabled = false; 27 | AutofillCreditCardEnabled = false; 28 | DisableAppUpdate = true; 29 | DisableMasterPasswordCreation = true; 30 | DisableSetDesktopBackground = true; 31 | DisablePrivateBrowsing = true; 32 | DisplayBookmarksToolbar = "never"; 33 | DontCheckDefaultBrowser = true; 34 | DownloadDirectory = config.vars.downloadFolder; 35 | EnableTrackingProtection = { 36 | Value = true; 37 | Locked = true; 38 | Cryptomining = true; 39 | Fingerprinting = true; 40 | }; 41 | NoDefaultBookmarks = true; 42 | OfferToSaveLogins = false; 43 | PasswordManagerEnabled = false; 44 | SearchEngines.Default = "duckduckgo"; 45 | SkipTermsOfUse = true; 46 | Preferences = { 47 | "browser.aboutConfig.showWarning" = false; 48 | "browser.tabs.insertRelatedAfterCurrent" = false; 49 | "browser.urlbar.resultMenu.keyboardAccessible" = true; 50 | "media.videocontrols.picture-in-picture.enable-when-switching-tabs.enabled" = true; 51 | "zen.view.experimental-no-window-controls" = true; 52 | }; 53 | }; 54 | }; 55 | 56 | xdg.mimeApps = 57 | let 58 | associations = builtins.listToAttrs ( 59 | map 60 | (name: { 61 | inherit name; 62 | value = 63 | let 64 | zen-browser = inputs.zen-browser.packages.${pkgs.stdenv.hostPlatform.system}.twilight; 65 | in 66 | zen-browser.meta.desktopFileName; 67 | }) 68 | [ 69 | "application/x-extension-shtml" 70 | "application/x-extension-xhtml" 71 | "application/x-extension-html" 72 | "application/x-extension-xht" 73 | "application/x-extension-htm" 74 | "x-scheme-handler/unknown" 75 | "x-scheme-handler/mailto" 76 | "x-scheme-handler/chrome" 77 | "x-scheme-handler/about" 78 | "x-scheme-handler/https" 79 | "x-scheme-handler/http" 80 | "application/xhtml+xml" 81 | "application/json" 82 | "text/plain" 83 | "text/html" 84 | ] 85 | ); 86 | in 87 | lib.mkIf (lib.strings.hasPrefix "zen" config.vars.defaultBrowser) { 88 | associations.added = associations; 89 | defaultApplications = associations; 90 | }; 91 | 92 | home.sessionVariables = { 93 | MOZ_ENABLE_WAYLAND = "1"; 94 | MOZ_DBUS_REMOTE = "1"; 95 | }; 96 | 97 | # Tridactyl 98 | xdg.configFile."tridactyl/tridactylrc".source = pkgs.replaceVars ./tridactylrc { 99 | inherit (config.vars) terminalBin domainName; 100 | editor = config.environment.variables.EDITOR; 101 | }; 102 | xdg.configFile."tridactyl/themes/zen.css".source = ./tridactyl_style.css; 103 | }; 104 | } 105 | -------------------------------------------------------------------------------- /hosts/najdorf/dawarich.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | config, 4 | ... 5 | }: 6 | 7 | let 8 | fqdn = "dawarich.${config.vars.domainName}"; 9 | url = "https://${fqdn}"; 10 | autheliaUrl = "https://authelia.${config.vars.domainName}"; 11 | oidcClientId = "dawarich"; 12 | oidcRedirectUri = "${url}/users/auth/openid_connect/callback"; 13 | in 14 | 15 | { 16 | age.secrets = { 17 | "dawarich/secretKeyBaseFile".file = "${self}/secrets/dawarich/secret_key_base.age"; 18 | "dawarich/oidcClientSecretEnvFile".file = "${self}/secrets/dawarich/oidc_client_secret_env.age"; 19 | "dawarich/oidcClientSecretDigest" = { 20 | file = "${self}/secrets/dawarich/oidc_client_secret_digest.age"; 21 | owner = config.services.authelia.instances.main.user; 22 | }; 23 | smtpPassword.file = "${self}/secrets/smtp_password.age"; 24 | }; 25 | 26 | services.dawarich = { 27 | enable = true; 28 | configureNginx = false; 29 | localDomain = fqdn; 30 | secretKeyBaseFile = config.age.secrets."dawarich/secretKeyBaseFile".path; 31 | redis.createLocally = true; 32 | database.createLocally = true; 33 | smtp = { 34 | host = config.vars.smtp.host; 35 | port = config.vars.smtp.port; 36 | user = config.vars.smtp.user; 37 | passwordFile = config.age.secrets.smtpPassword.path; 38 | fromAddress = "Dawarich <${config.vars.email}>"; 39 | }; 40 | extraConfig = { 41 | OIDC_CLIENT_ID = oidcClientId; 42 | OIDC_ISSUER = autheliaUrl; 43 | OIDC_REDIRECT_URI = oidcRedirectUri; 44 | OIDC_PROVIDER_NAME = "Authelia"; 45 | OIDC_AUTO_REGISTER = "false"; 46 | ALLOW_EMAIL_PASSWORD_REGISTRATION = "false"; 47 | PHOTON_API_HOST = "127.0.0.1:2322"; 48 | PHOTON_API_USE_HTTPS = "false"; 49 | }; 50 | extraEnvFiles = [ 51 | config.age.secrets."dawarich/oidcClientSecretEnvFile".path 52 | ]; 53 | }; 54 | 55 | services.traefik.dynamicConfigOptions.http = rec { 56 | routers.to-dawarich = { 57 | rule = "Host(`${fqdn}`)"; 58 | service = "dawarich"; 59 | middlewares = [ "authelia" ]; 60 | }; 61 | services."${routers.to-dawarich.service}".loadBalancer.servers = [ 62 | { 63 | url = "http://localhost:${builtins.toString config.services.dawarich.webPort}"; 64 | } 65 | ]; 66 | }; 67 | 68 | services.authelia.instances.main.settings = { 69 | identity_providers.oidc.clients = [ 70 | { 71 | client_id = oidcClientId; 72 | client_name = "Dawarich"; 73 | client_secret = ''{{ secret "${config.age.secrets."dawarich/oidcClientSecretDigest".path}" }}''; 74 | scopes = [ 75 | "openid" 76 | "groups" 77 | "email" 78 | "profile" 79 | ]; 80 | authorization_policy = "one_factor"; 81 | redirect_uris = [ oidcRedirectUri ]; 82 | pre_configured_consent_duration = "1y"; 83 | } 84 | ]; 85 | 86 | access_control.rules = [ 87 | { 88 | domain = fqdn; 89 | resources = [ "^/api/.*" ]; 90 | policy = "bypass"; 91 | } 92 | ]; 93 | }; 94 | 95 | virtualisation.arion.projects.photon.settings = 96 | let 97 | volume = "photon_data"; 98 | in 99 | { 100 | docker-compose.volumes.${volume} = { }; 101 | services.photon.service = { 102 | image = "rtuszik/photon-docker:latest"; 103 | container_name = "photon"; 104 | network_mode = "host"; 105 | volumes = [ "${volume}:/photon/data" ]; 106 | restart = "unless-stopped"; 107 | environment = { 108 | UPDATE_INTERVAL = "720h"; 109 | PUID = "0"; 110 | PGID = "0"; 111 | REGION = "europe"; 112 | }; 113 | }; 114 | }; 115 | } 116 | -------------------------------------------------------------------------------- /hosts/carokann/shikane.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | shikaneMonitorSetup = pkgs.writeShellScriptBin "shikane-monitor-setup" '' 9 | MONITOR="$1" 10 | START="$2" 11 | END="$3" 12 | 13 | # Wait until the monitor is actually listed by Hyprland 14 | # giving up after 5 seconds to prevent infinite hanging 15 | MAX_RETRIES=50 16 | i=0 17 | while ! hyprctl monitors -j | ${lib.getExe pkgs.jq} -e ".[] | select(.name == \"$MONITOR\")" > /dev/null; do 18 | if [ $i -ge $MAX_RETRIES ]; then 19 | echo "Timeout waiting for monitor $MONITOR" 20 | exit 1 21 | fi 22 | sleep 0.1 23 | ((i++)) 24 | done 25 | 26 | # Small buffer after detection to allow layout to settle 27 | sleep 0.2 28 | 29 | # Apply the rule for future workspaces 30 | # This ensures any NEW workspaces in this range open on this monitor 31 | hyprctl keyword workspace "r[$START-$END],monitor:$MONITOR" 32 | 33 | # Force-move existing persistent workspaces 34 | for ((i=START; i<=END; i++)); do 35 | hyprctl dispatch moveworkspacetomonitor "$i" "$MONITOR" 36 | done 37 | ''; 38 | in 39 | { 40 | home-manager.users."${config.vars.username}".services.shikane = { 41 | enable = true; 42 | settings = { 43 | profile = 44 | let 45 | resWidth = 2256; 46 | resHeight = 1504; 47 | laptopOutput = { 48 | search = "n=eDP-1"; 49 | enable = true; 50 | mode = { 51 | width = resWidth; 52 | height = resHeight; 53 | refresh = 60; 54 | }; 55 | scale = 1.6; 56 | }; 57 | in 58 | [ 59 | { 60 | name = "laptop-only"; 61 | output = [ laptopOutput ]; 62 | } 63 | # Put a default external monitor to the right and assign workspaces u, i, o and p. 64 | { 65 | name = "external-output-default"; 66 | output = [ 67 | { 68 | search = "n/(DP-[1-9]|HDMI-[A-C]-[1-9])"; 69 | enable = true; 70 | position = { 71 | x = resWidth; 72 | y = 0; 73 | }; 74 | mode = "preferred"; 75 | exec = [ 76 | "${lib.getExe shikaneMonitorSetup} \"$SHIKANE_OUTPUT_NAME\" 5 8" 77 | ]; 78 | } 79 | (laptopOutput // { exec = [ "hyprctl keyword workspace r[1-4],monitor:$SHIKANE_OUTPUT_NAME" ]; }) 80 | ]; 81 | } 82 | # At home, put laptop monitor on the right and give the main monitor a, s, d and f 83 | { 84 | name = "home"; 85 | output = [ 86 | { 87 | search = [ 88 | "v=Dell Inc." 89 | "m=DELL U2424HE" 90 | "s=FF904X3" 91 | ]; 92 | enable = true; 93 | position = { 94 | x = 0; 95 | y = 0; 96 | }; 97 | mode = "1920x1080"; 98 | exec = [ 99 | "${lib.getExe shikaneMonitorSetup} \"$SHIKANE_OUTPUT_NAME\" 1 4" 100 | ]; 101 | } 102 | ( 103 | laptopOutput 104 | // { 105 | position = { 106 | x = 1920; 107 | y = 141; 108 | }; # Align bottom corners 109 | exec = [ 110 | "${lib.getExe shikaneMonitorSetup} \"$SHIKANE_OUTPUT_NAME\" 5 8" 111 | ]; 112 | } 113 | ) 114 | ]; 115 | } 116 | ]; 117 | }; 118 | }; 119 | } 120 | -------------------------------------------------------------------------------- /profiles/graphical/caelestia/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | config, 4 | pkgs, 5 | inputs, 6 | ... 7 | }: 8 | let 9 | wallpapersDir = "${config.vars.home}/${config.vars.picturesFolder}/wallpapers"; 10 | in 11 | { 12 | home-manager.users."${config.vars.username}" = { 13 | programs.caelestia = { 14 | enable = true; 15 | package = inputs.caelestia-shell.packages.${pkgs.stdenv.hostPlatform.system}.default.override { 16 | hyprland = inputs.hyprland.packages.${pkgs.stdenv.hostPlatform.system}.hyprland; 17 | }; 18 | settings = { 19 | general = { 20 | apps = { 21 | terminal = config.vars.terminalBin; 22 | audio = "${pkgs.pwvucontrol}/bin/pwvucontrol"; 23 | }; 24 | idle = { 25 | timeouts = [ 26 | { 27 | timeout = 300; # 5 minutes 28 | idleAction = "lock"; 29 | } 30 | ]; 31 | }; 32 | }; 33 | bar = { 34 | status = { 35 | showAudio = true; 36 | }; 37 | workspaces = rec { 38 | shown = 4; 39 | label = ""; 40 | showWindowsOnSpecialWorkspaces = false; 41 | occupiedLabel = ""; 42 | activeLabel = occupiedLabel; 43 | }; 44 | tray = { 45 | background = true; 46 | iconSubs = [ 47 | { 48 | id = "spotify-client"; 49 | image = "${self}/assets/spotify-light.svg"; 50 | } 51 | ]; 52 | }; 53 | clock.showIcon = false; 54 | entries = [ 55 | { 56 | id = "logo"; 57 | enabled = false; 58 | } 59 | { 60 | id = "workspaces"; 61 | enabled = true; 62 | } 63 | { 64 | id = "spacer"; 65 | enabled = true; 66 | } 67 | { 68 | id = "activeWindow"; 69 | enabled = true; 70 | } 71 | { 72 | id = "spacer"; 73 | enabled = true; 74 | } 75 | { 76 | id = "tray"; 77 | enabled = true; 78 | } 79 | { 80 | id = "clock"; 81 | enabled = true; 82 | } 83 | { 84 | id = "statusIcons"; 85 | enabled = true; 86 | } 87 | { 88 | id = "power"; 89 | enabled = false; 90 | } 91 | ]; 92 | }; 93 | background = { 94 | enabled = true; 95 | desktopClock.enabled = false; 96 | visualiser.enabled = false; 97 | }; 98 | services = { 99 | useFahrenheit = false; 100 | useTwelveHourClock = false; 101 | }; 102 | session = { 103 | vimKeybinds = true; 104 | }; 105 | launcher = { 106 | actionPrefix = "/"; 107 | vimKeybinds = true; 108 | }; 109 | paths = { 110 | wallpaperDir = wallpapersDir; 111 | sessionGif = "${self}/assets/bird.gif"; 112 | # mediaGif = "${self}/assets/.gif"; 113 | }; 114 | utilities = { 115 | enabled = true; 116 | }; 117 | }; 118 | cli.enable = true; 119 | }; 120 | 121 | wayland.windowManager.hyprland.settings.env = [ 122 | "CAELESTIA_WALLPAPERS_DIR, ${wallpapersDir}" 123 | "CAELESTIA_SCREENSHOTS_DIR, ${config.vars.home}/${config.vars.screenshotFolder}" 124 | "CAELESTIA_RECORDINGS_DIR, ${config.vars.home}/${config.vars.screencastFolder}" 125 | ]; 126 | 127 | # Use caelestia's dynamically generated theme in the terminal 128 | programs.fish.interactiveShellInit = '' 129 | cat ~/.local/state/caelestia/sequences.txt 2> /dev/null 130 | ''; 131 | }; 132 | } 133 | -------------------------------------------------------------------------------- /profiles/core/kak/kakrc: -------------------------------------------------------------------------------- 1 | # Options ──────────────────────────────────────────────────────────────────── 2 | set -add global ui_options terminal_assistant=none 3 | set global tabstop 4 4 | set global indentwidth 4 5 | set global scrolloff 3,5 6 | set global grepcmd "rg --column --with-filename" 7 | 8 | 9 | # Highlighters ──────────────────────────────────────────────────────────────── 10 | addhl global/ wrap 11 | addhl global/ number-lines 12 | addhl global/trailing-whitespaces regex "\h+$" 0:black,red 13 | 14 | 15 | # Mappings ──────────────────────────────────────────────────────────────────── 16 | map global user c ":comment-line" -docstring "Comment line" 17 | map global insert "" 18 | map global insert "" 19 | map global normal "*s" # select all occurences of the current selection in the file 20 | 21 | # scroll with and like in vim 22 | map global normal "vj" 23 | map global insert "vj" 24 | map global normal "vk" 25 | map global insert "vk" 26 | 27 | # Splits 28 | declare-user-mode windows 29 | map global windows h ':split-horizontal' -docstring "Open a new window horizontally" 30 | map global windows v ':split-vertical' -docstring "Open a new window vertically" 31 | map global normal ':enter-user-mode windows' 32 | 33 | 34 | # Plugins ──────────────────────────────────────────────────────────────────── 35 | ### kakoune-surround ### 36 | declare-user-mode surround 37 | map global surround s ':surround' -docstring "Surround selected text" 38 | map global surround c ':change-surround' -docstring "Change selected text's surroundings" 39 | map global surround d ':delete-surround' -docstring "Delete selected text's surroundings" 40 | map global surround t ':select-surrounding-tag' -docstring "Select selected text's surrounding tags" 41 | map global normal ^ ':enter-user-mode surround' 42 | 43 | ### kak-lsp ### 44 | eval %sh{kak-lsp --kakoune -s $kak_session} 45 | 46 | # Enable logs 47 | # eval %sh{echo ${kak_opt_lsp_cmd} >> /tmp/kak-lsp.log} 48 | # set global lsp_cmd "kak-lsp -s %val{session} -vvv --log /tmp/kak-lsp.log" 49 | 50 | set global lsp_diagnostic_line_error_sign "║" 51 | set global lsp_diagnostic_line_warning_sign "┊" 52 | set global lsp_auto_highlight_references true 53 | set global lsp_hover_anchor false 54 | 55 | map global user r ":lsp-rename-prompt" -docstring "LSP rename the selected symbol" 56 | map global goto p ": lsp-implementation" -docstring "implementation" 57 | 58 | hook global WinSetOption filetype=(rust|python|go|nix) %{ 59 | lsp-auto-hover-enable 60 | lsp-enable-window 61 | } 62 | hook global KakEnd .* lsp-exit 63 | 64 | ### auto-pairs ### 65 | hook global WinCreate .* %{ 66 | enable-auto-pairs 67 | } 68 | 69 | # Hooks ────────────────────────────────────────────────────────────────────── 70 | hook global WinSetOption filetype=(go|c|cpp|rust) %{ 71 | set global indentwidth 0 72 | } 73 | hook global WinSetOption filetype=(yaml|json|protobuf|nix) %{ 74 | set global indentwidth 2 75 | } 76 | hook global WinSetOption filetype=python %{ 77 | map global user b "\Obreakpoint()" -docstring "Insert a breakpoint on the line above" 78 | } 79 | hook global WinSetOption filetype=go %{ 80 | set-option window formatcmd "goimports" 81 | hook buffer BufWritePost .* format 82 | } 83 | hook global WinSetOption filetype=nix %{ 84 | set-option window formatcmd "nixfmt" 85 | hook buffer BufWritePost .* format 86 | } 87 | hook global ModuleLoaded tmux %{ 88 | alias global terminal tmux-terminal-vertical 89 | } 90 | 91 | # Auto-set clipboard 92 | hook global RegisterModified '"' %{ nop %sh{ 93 | if [[ -n "$DISPLAY" ]] || [[ -n "$WAYLAND_DISPLAY" ]]; then 94 | printf %s "$kak_main_reg_dquote" | wl-copy > /dev/null 2>&1 & 95 | elif [[ -n "$TMUX" ]]; then 96 | tmux set-buffer -- "$kak_main_reg_dquote" 97 | fi 98 | }} 99 | 100 | 101 | # Commands ──────────────────────────────────────────────────────────────────── 102 | define-command -docstring "split-vertical: split tmux vertically" \ 103 | split-vertical %{ 104 | tmux-terminal-vertical fedit %val{session} 105 | } 106 | 107 | define-command -docstring "split-horizontal: split tmux horizontally" \ 108 | split-horizontal %{ 109 | tmux-terminal-horizontal fedit %val{session} 110 | } 111 | 112 | # aliases 113 | alias global x write-quit 114 | alias global vs split-vertical 115 | alias global sp split-horizontal 116 | -------------------------------------------------------------------------------- /pkgs/soundcards.bash: -------------------------------------------------------------------------------- 1 | # Usage: 2 | # $ soundcards next # switch to the next available device 3 | # $ soundcards previous # switch to the previous available device 4 | 5 | function switch_sink { 6 | local sink=$2 7 | local node_id=$(get_node_id_from_sink "$1" "$sink") 8 | local node_nick=$(get_node_info "$1" "$node_id" "node.nick") 9 | if [[ -z "$node_nick" || "$node_nick" == "null" ]]; then 10 | node_nick=$(get_node_info "$1" "$node_id" "node.description") 11 | fi 12 | 13 | wpctl set-default "$node_id" 14 | # notify-send --icon=audio-volume-high "Sound output" "$node_nick" 15 | } 16 | 17 | function get_node_info { 18 | jq -r \ 19 | --argjson node_id $2 \ 20 | --arg field $3 \ 21 | '.[] | select(.type=="PipeWire:Interface:Node" and .id==$node_id).info.props[$field]' $1 22 | } 23 | 24 | function get_node_id_from_sink { 25 | jq -r \ 26 | --arg sink "$2" \ 27 | '.[] | select(.type=="PipeWire:Interface:Node" and .info.props."node.name"==$sink).id' $1 28 | } 29 | 30 | function list_sinks { 31 | jq -r '.[] | select(.type=="PipeWire:Interface:Node" and .info.props."media.class"=="Audio/Sink") 32 | | .info.props."node.name"' $1 | sort 33 | } 34 | 35 | function get_route_availability { 36 | jq -r \ 37 | --argjson device_id $2 \ 38 | --arg device_profile_description "$3" \ 39 | '.[] | select(.type=="PipeWire:Interface:Device" and .id==$device_id).info.params.EnumRoute[]? 40 | | select(.description==$device_profile_description).available // empty' $1 41 | } 42 | 43 | function is_sink_available { 44 | local pw_dump_file=$1 45 | local sink_name=$2 46 | 47 | local node_id=$(get_node_id_from_sink "$pw_dump_file" "$sink_name") 48 | local device_id=$(get_node_info "$pw_dump_file" "$node_id" "device.id") 49 | local device_profile_description=$(get_node_info "$pw_dump_file" "$node_id" "device.profile.description") 50 | 51 | # If we can't get device info, assume it's available (e.g., software sinks) 52 | if [[ -z "$device_id" || -z "$device_profile_description" || "$device_id" == "null" ]]; then 53 | echo "yes" 54 | return 55 | fi 56 | 57 | local availability=$(get_route_availability "$pw_dump_file" "$device_id" "$device_profile_description") 58 | if [[ "$availability" != "no" ]]; then 59 | echo "yes" # Default to available if not found 60 | else 61 | echo "$availability" 62 | fi 63 | } 64 | 65 | function list_available_sinks { 66 | local pw_dump_file=$1 67 | local all_sinks=($(list_sinks "$pw_dump_file")) 68 | 69 | for sink in "${all_sinks[@]}"; do 70 | if [[ "$(is_sink_available "$pw_dump_file" "$sink")" == "yes" ]]; then 71 | echo "$sink" 72 | fi 73 | done | sort 74 | } 75 | 76 | function get_default_sink { 77 | pw-dump | jq -r ' 78 | ( 79 | .[] | 80 | select(.type=="PipeWire:Interface:Metadata" and .props."metadata.name"=="default") | 81 | .metadata 82 | ) as $m | 83 | (first($m[] | select(.key=="default.audio.sink").value.name)) 84 | // 85 | (first($m[] | select(.key=="default.configured.audio.sink").value.name)) 86 | ' 87 | } 88 | 89 | function get_index_of_element { 90 | local element=$1 91 | shift 92 | local arr=("$@") 93 | 94 | for i in "${!arr[@]}"; do 95 | if [[ "${arr[$i]}" = "${element}" ]]; then 96 | echo $i 97 | break 98 | fi 99 | done 100 | } 101 | 102 | function main { 103 | local subcommand=$1 104 | local pw_dump_file=/tmp/pw_dump 105 | 106 | pw-dump -N > /tmp/pw_dump 107 | 108 | local sinks=( $(list_available_sinks $pw_dump_file) ) 109 | local default_sink=$(get_default_sink $pw_dump_file) 110 | local default_sink_index=$(get_index_of_element "${default_sink}" ${sinks[@]}) 111 | local nb_sinks=${#sinks[@]} 112 | 113 | if [[ $nb_sinks -eq 0 ]]; then 114 | notify-send --icon=audio-volume-high "Sound output" "No available audio devices found" 115 | rm /tmp/pw_dump 116 | exit 1 117 | fi 118 | 119 | if [ "$subcommand" = "next" ]; then 120 | local next_sink=${sinks[$(( ($default_sink_index + 1) % $nb_sinks ))]} 121 | switch_sink $pw_dump_file $next_sink 122 | elif [ "$subcommand" = "previous" ]; then 123 | local previous_sink=${sinks[$(( ($default_sink_index - 1 + $nb_sinks) % $nb_sinks ))]} 124 | switch_sink $pw_dump_file $previous_sink 125 | else 126 | echo "Usage: soundcards {next|previous}" 127 | rm /tmp/pw_dump 128 | exit 1 129 | fi 130 | 131 | rm /tmp/pw_dump 132 | } 133 | 134 | main "$@" 135 | -------------------------------------------------------------------------------- /profiles/graphical/hyprland/keybindings.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs }: 2 | let 3 | soundcards = "${pkgs.soundcards}/bin/soundcards"; 4 | wpctl = "${pkgs.wireplumber}/bin/wpctl"; 5 | backlight = "${pkgs.backlight}/bin/backlight"; 6 | mod = "SUPER"; 7 | in 8 | { 9 | # Keybindings 10 | bind = [ 11 | # Kill focused window 12 | "${mod}, X, killactive" 13 | 14 | # Reload configuration 15 | "${mod} SHIFT, R, exec, hyprctl reload" 16 | 17 | # Exit Hyprland 18 | "${mod} SHIFT, Q, exit" 19 | 20 | # Focus movement 21 | "${mod}, H, movefocus, l" 22 | "${mod}, J, movefocus, d" 23 | "${mod}, K, movefocus, u" 24 | "${mod}, L, movefocus, r" 25 | 26 | # Move windows 27 | "${mod} SHIFT, H, movewindow, l" 28 | "${mod} SHIFT, J, movewindow, d" 29 | "${mod} SHIFT, K, movewindow, u" 30 | "${mod} SHIFT, L, movewindow, r" 31 | 32 | # Workspace switching 33 | "${mod}, A, workspace, 1" 34 | "${mod}, S, workspace, 2" 35 | "${mod}, D, workspace, 3" 36 | "${mod}, F, workspace, 4" 37 | "${mod}, U, workspace, 5" 38 | "${mod}, I, workspace, 6" 39 | "${mod}, O, workspace, 7" 40 | "${mod}, P, workspace, 8" 41 | 42 | # Move to workspace 43 | "${mod} SHIFT, A, movetoworkspacesilent, 1" 44 | "${mod} SHIFT, S, movetoworkspacesilent, 2" 45 | "${mod} SHIFT, D, movetoworkspacesilent, 3" 46 | "${mod} SHIFT, F, movetoworkspacesilent, 4" 47 | "${mod} SHIFT, U, movetoworkspacesilent, 5" 48 | "${mod} SHIFT, I, movetoworkspacesilent, 6" 49 | "${mod} SHIFT, O, movetoworkspacesilent, 7" 50 | "${mod} SHIFT, P, movetoworkspacesilent, 8" 51 | 52 | "${mod} CTRL SHIFT, A, movetoworkspace, 1" 53 | "${mod} CTRL SHIFT, S, movetoworkspace, 2" 54 | "${mod} CTRL SHIFT, D, movetoworkspace, 3" 55 | "${mod} CTRL SHIFT, F, movetoworkspace, 4" 56 | "${mod} CTRL SHIFT, U, movetoworkspace, 5" 57 | "${mod} CTRL SHIFT, I, movetoworkspace, 6" 58 | "${mod} CTRL SHIFT, O, movetoworkspace, 7" 59 | "${mod} CTRL SHIFT, P, movetoworkspace, 8" 60 | 61 | # Layout controls 62 | "${mod}, V, layoutmsg, togglesplit" 63 | "${mod} SHIFT, V, layoutmsg, preselect d" 64 | "${mod}, TAB, fullscreen, 1" 65 | ", F11, fullscreen" 66 | 67 | # Apps 68 | "${mod}, Space, global, caelestia:launcher" 69 | "${mod}, Return, exec, app2unit -- ${config.vars.terminalBin}" 70 | "${mod}, B, exec, app2unit -- ${config.vars.defaultBrowser}" 71 | "${mod}, N, exec, app2unit -- obsidian" 72 | "${mod}, Z, exec, app2unit -- zeditor" 73 | "${mod} SHIFT, Escape, exec, app2unit -- ${pkgs.mission-center}/bin/missioncenter" 74 | 75 | # Notifications 76 | "${mod}, Comma, global, caelestia:clearNotifs" 77 | 78 | # Soundcards 79 | "${mod}, bracketleft, exec, ${soundcards} previous" 80 | "${mod}, bracketright, exec, ${soundcards} next" 81 | 82 | # Screen capture 83 | ", Print, exec, caelestia screenshot" # Fullscreen screenshot 84 | "${mod}, Print, global, caelestia:screenshot" 85 | "${mod} SHIFT, Print, global, caelestia:screenshotFreeze" 86 | 87 | # Turn off laptop screen 88 | ", F9, exec, hyprctl dispatch dpms toggle eDP-1" 89 | 90 | # Inhibit suspend 91 | ", F12, exec, caelestia shell idleInhibitor toggle" 92 | ", XF86AudioMedia, exec, caelestia shell idleInhibitor toggle" 93 | 94 | # Special workspace 95 | "${mod}, minus, togglespecialworkspace, communication" 96 | "${mod}, M, togglespecialworkspace, music" 97 | 98 | # Submaps 99 | "${mod}, W, submap, window" 100 | 101 | # Caelestia 102 | "${mod}, Escape, global, caelestia:session" 103 | "${mod}, Period, exec, caelestia emoji -p" 104 | ]; 105 | 106 | bindl = [ 107 | ", XF86AudioMute, exec, ${wpctl} set-mute @DEFAULT_SINK@ toggle" 108 | ", XF86AudioMicMute, exec, ${wpctl} set-mute @DEFAULT_SOURCE@ toggle" 109 | ", XF86AudioPlay, global, caelestia:mediaToggle" 110 | ", XF86AudioNext, global, caelestia:mediaNext" 111 | ", XF86AudioPrev, global, caelestia:mediaPrev" 112 | ", XF86AudioStop, global, caelestia:mediaStop" 113 | ]; 114 | 115 | bindle = [ 116 | # Volume 117 | ", XF86AudioRaiseVolume, exec, ${wpctl} set-volume @DEFAULT_SINK@ 5%+" 118 | ", XF86AudioLowerVolume, exec, ${wpctl} set-volume @DEFAULT_SINK@ 5%-" 119 | # Backlight 120 | ", XF86MonBrightnessUp, exec, ${backlight} inc" 121 | ", XF86MonBrightnessDown, exec, ${backlight} dec" 122 | ]; 123 | 124 | binde = [ 125 | # Resizing windows 126 | "${mod}, left, resizeactive, -10 0" 127 | "${mod}, right, resizeactive, 10 0" 128 | "${mod}, up, resizeactive, 0 -10" 129 | "${mod}, down, resizeactive, 0 10" 130 | ]; 131 | 132 | bindm = [ 133 | "${mod}, mouse:272, movewindow" 134 | "${mod}, mouse:273, resizewindow" 135 | ]; 136 | } 137 | -------------------------------------------------------------------------------- /profiles/core/fish.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | { 4 | home-manager.users."${config.vars.username}" = { 5 | home.packages = with pkgs; [ eza ]; 6 | programs.fish = { 7 | enable = true; 8 | interactiveShellInit = '' 9 | fish_vi_key_bindings 10 | set sponge_purge_only_on_exit true 11 | ''; 12 | shellAbbrs = rec { 13 | # nix 14 | n = "nix"; 15 | np = "nix profile"; 16 | ni = "${np} install"; 17 | nr = "${np} remove"; 18 | ns = "nix search --no-update-lock-file nixpkgs"; 19 | nf = "nix flake"; 20 | nepl = "nix repl ''"; 21 | nrb = ''nixos-rebuild --sudo --flake "$(pwd)#$(hostname)"''; 22 | nrbs = "nb $(hostname) && ${nrb} switch"; 23 | ndiff = "${pkgs.nvd}/bin/nvd diff --sort semver /nix/var/nix/profiles/(ls -r /nix/var/nix/profiles/ | grep -E 'system-' | sed -n '2 p') /nix/var/nix/profiles/system"; 24 | 25 | # sudo 26 | s = "sudo -E"; 27 | si = "sudo -i"; 28 | se = "sudoedit"; 29 | 30 | # git 31 | g = "git"; 32 | ga = "git add"; 33 | "ga." = "git add ."; 34 | gamend = "git commit --amend --no-edit"; 35 | gb = "git branch"; 36 | gc = "git commit"; 37 | gco = "git checkout"; 38 | gd = "git diff"; 39 | gds = "git diff --staged"; 40 | gp = "git push"; 41 | gpf = "git push --force-with-lease"; 42 | grc = "git rebase --continue"; 43 | gri = "git rebase --interactive --committer-date-is-author-date"; 44 | gra = "git rebase --abort"; 45 | grs = "git rebase --skip"; 46 | gs = "git status --short"; 47 | gS = "git status"; 48 | gst = "git stash"; 49 | gstl = "git stash list"; 50 | gstp = "git stash pop"; 51 | 52 | # systemd 53 | ctl = "sudo systemctl"; 54 | utl = "systemctl --user"; 55 | up = "sudo systemctl start"; 56 | dn = "sudo systemctl stop"; 57 | jtl = "journalctl"; 58 | 59 | # apps 60 | py = "ptpython"; 61 | kc = "kdeconnect-cli -n Mamène"; 62 | hs = "nmcli connection up hs"; 63 | }; 64 | functions = { 65 | fish_greeting = ""; 66 | ".." = "cd .."; 67 | "..." = "cd ../.."; 68 | "...." = "cd ../../.."; 69 | "....." = "cd ../../../.."; 70 | ls = "eza $argv"; 71 | la = "eza -laa $argv"; 72 | tree = "eza -T $argv"; 73 | wget = "wget2 $argv"; 74 | bwu = "set -Ux BW_SESSION (bw unlock --raw)"; 75 | genpass = "bw generate -ulns --length 16"; 76 | pyclean = "find . | grep -E '(__pycache__|\\.pyc|\\.pyo$)' | xargs rm -rf"; 77 | trash = "gtrash $argv"; 78 | tp = "trash put $argv"; 79 | rm = "echo 'Stop using rm, use trash put (or tp) instead'"; 80 | k = "kak $argv"; 81 | h = "hx $argv"; 82 | myip = "dig +short myip.opendns.com @208.67.222.222 2>&1"; 83 | nb = "${pkgs.nix-output-monitor}/bin/nom build .#nixosConfigurations.$argv.config.system.build.toplevel"; 84 | nm-wifi = { 85 | body = "nmcli -t -f SSID device wifi list | sk | xargs -o -I_ nmcli --ask dev wifi connect '_'"; 86 | description = "Fuzzy connect to a wifi"; 87 | }; 88 | nm-con = { 89 | body = "nmcli -t -f NAME con show | sk | xargs -o -I_ nmcli con up '_'"; 90 | description = "Fuzzy activate a connection"; 91 | }; 92 | activate-combined-output = "${pkgs.pulseaudio}/bin/pactl load-module module-combine-sink"; 93 | deactivate-combined-output = "${pkgs.pulseaudio}/bin/pactl unload-module module-combine-sink"; 94 | }; 95 | plugins = [ 96 | { 97 | name = "anicode"; 98 | src = pkgs.fetchFromGitHub { 99 | owner = "igalic"; 100 | repo = "anicode"; 101 | rev = "982709ba6619dd758e83c0e7126356fccabf2379"; 102 | sha256 = "Vu1gioUMbCa/AVTMQMkC+dskcUqXyHP6Tay/gsVu+Pc="; 103 | }; 104 | } 105 | { 106 | name = "done"; 107 | src = pkgs.fetchFromGitHub { 108 | owner = "franciscolourenco"; 109 | repo = "done"; 110 | rev = "1.16.5"; 111 | sha256 = "E0wveeDw1VzEH2kzn63q9hy1xkccfxQHBV2gVpu2IdQ="; 112 | }; 113 | } 114 | { 115 | name = "sponge"; 116 | src = pkgs.fetchFromGitHub { 117 | owner = "meaningful-ooo"; 118 | repo = "sponge"; 119 | rev = "1.1.0"; 120 | sha256 = "MdcZUDRtNJdiyo2l9o5ma7nAX84xEJbGFhAVhK+Zm1w="; 121 | }; 122 | } 123 | ]; 124 | }; 125 | 126 | programs = { 127 | starship = { 128 | enable = true; 129 | }; 130 | nix-index = { 131 | enable = true; 132 | }; 133 | direnv = { 134 | enable = true; 135 | nix-direnv.enable = true; 136 | }; 137 | }; 138 | 139 | home.sessionVariables = { 140 | DIRENV_LOG_FORMAT = ""; 141 | }; 142 | }; 143 | } 144 | -------------------------------------------------------------------------------- /profiles/core/tmux.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | { 4 | home-manager.users."${config.vars.username}".programs.tmux = { 5 | enable = true; 6 | aggressiveResize = true; 7 | baseIndex = 1; 8 | escapeTime = 0; 9 | historyLimit = 100000; 10 | keyMode = "vi"; 11 | prefix = "M-g"; 12 | secureSocket = true; 13 | sensibleOnTop = false; 14 | plugins = with pkgs; [ 15 | tmuxPlugins.yank 16 | tmuxPlugins.open 17 | tmuxPlugins.copycat 18 | ]; 19 | extraConfig = 20 | let 21 | mod = "C"; 22 | in 23 | '' 24 | ############ 25 | # BINDINGS # 26 | ############ 27 | bind -n ${mod}-h select-pane -L 28 | bind -n ${mod}-j select-pane -D 29 | bind -n ${mod}-k select-pane -U 30 | bind -n ${mod}-l select-pane -R 31 | bind p previous-window 32 | bind n next-window 33 | 34 | bind -T copy-mode-vi ${mod}-h select-pane -L 35 | bind -T copy-mode-vi ${mod}-j select-pane -D 36 | bind -T copy-mode-vi ${mod}-k select-pane -U 37 | bind -T copy-mode-vi ${mod}-l select-pane -R 38 | 39 | # reset scrollback buffer 40 | bind k clear-history 41 | 42 | # go to copy mode 43 | bind [ copy-mode 44 | 45 | # move pane to new window 46 | bind w break-pane 47 | 48 | # kill pane 49 | bind -n ${mod}-x kill-pane 50 | bind -n ${mod}-X kill-window 51 | 52 | # reload tmux conf 53 | bind -n M-r source-file ~/.config/tmux/tmux.conf \; display-message "Tmux conf reloaded" 54 | 55 | # resize panes 56 | bind -n -r M-Down resize-pane -D 2 57 | bind -n -r M-Up resize-pane -U 2 58 | bind -n -r M-Left resize-pane -L 2 59 | bind -n -r M-Right resize-pane -R 2 60 | 61 | # Vi copypaste mode 62 | bind -T copy-mode-vi v send -X begin-selection 63 | 64 | # Split horiziontal and vertical splits, instead of % and " 65 | # Also open them in the same directory 66 | bind v split-window -v -c '#{pane_current_path}' 67 | bind h split-window -h -c '#{pane_current_path}' 68 | 69 | # Open new window 70 | bind -n M-Enter new-window 71 | 72 | # Zoom on focused tab 73 | bind -n M-Tab resize-pane -Z 74 | 75 | # Detach client 76 | bind d detach-client 77 | 78 | # Client mode 79 | bind s choose-tree -s 80 | 81 | # Select windows 82 | bind -n M-1 select-window -t 1 83 | bind -n M-2 select-window -t 2 84 | bind -n M-3 select-window -t 3 85 | bind -n M-4 select-window -t 4 86 | bind -n M-5 select-window -t 5 87 | bind -n M-6 select-window -t 6 88 | bind -n M-7 select-window -t 7 89 | bind -n M-8 select-window -t 8 90 | bind -n M-9 select-window -t 9 91 | bind -n M-0 select-window -t 10 92 | bind -n "M-'" last-window 93 | 94 | ########### 95 | # SESSION # 96 | ########### 97 | # make inactive window's background dimmer 98 | set -g window-active-style bg=#161e22 99 | set -g window-style bg=terminal 100 | 101 | # renumber windows when a window is closed 102 | set -g renumber-windows on 103 | 104 | # disable running multiple commands pressing the prefix once 105 | set -g repeat-time 0 106 | 107 | # enable both visual message and bell on activity alerts 108 | set -g monitor-activity on 109 | set -g visual-activity on 110 | 111 | # tmux messages are displayed for 4 seconds 112 | set -g display-time 4000 113 | 114 | ########## 115 | # VISUAL # 116 | ########## 117 | # jellybeans theme created with tmuxline 118 | set -g status "on" 119 | set -g status-justify "left" 120 | set -g status-left-style "none" 121 | set -g status-right-style "none" 122 | set -g status-right-length "100" 123 | set -g status-left-length "100" 124 | setw -g window-status-activity-style "none" 125 | setw -g window-status-separator "" 126 | 127 | # colors 128 | set -g message-command-style "fg=colour253,bg=colour239" 129 | set -g pane-active-border-style "fg=colour103" 130 | set -g status-style "none,bg=colour236" 131 | set -g message-style "fg=colour253,bg=colour239" 132 | set -g pane-border-style "fg=colour239" 133 | set -g status-left "#[fg=colour236,bg=colour103] #S #[fg=colour103,bg=colour236,nobold,nounderscore,noitalics]" 134 | set -g status-right "#[fg=colour239,bg=colour236,nobold,nounderscore,noitalics]#[fg=colour248,bg=colour239] %Y-%m-%d  %H:%M #[fg=colour246,bg=colour239,nobold,nounderscore,noitalics]#[fg=colour236,bg=colour246] #h " 135 | setw -g window-status-style "none,fg=colour244,bg=colour236" 136 | setw -g window-status-format "#[fg=colour244,bg=colour236] #I #[fg=colour244,bg=colour236] #W " 137 | setw -g window-status-current-format "#[fg=colour236,bg=colour239,nobold,nounderscore,noitalics]#[fg=colour253,bg=colour239] #I #[fg=colour253,bg=colour239] #W #[fg=colour239,bg=colour236,nobold,nounderscore,noitalics]" 138 | ''; 139 | }; 140 | } 141 | -------------------------------------------------------------------------------- /profiles/graphical/wezterm/config.lua: -------------------------------------------------------------------------------- 1 | local act = wezterm.action 2 | local config = wezterm.config_builder() 3 | 4 | -- tabline config (need tabline.setup before apply_to_config, which I need before the rest) 5 | local tabline = require "tabline" 6 | tabline.setup({ 7 | options = { 8 | icons_enabled = false, 9 | theme = config.colors, 10 | section_separators = { 11 | left = wezterm.nerdfonts.ple_right_half_circle_thick, 12 | right = wezterm.nerdfonts.ple_left_half_circle_thick, 13 | }, 14 | component_separators = { 15 | left = wezterm.nerdfonts.ple_right_half_circle_thin, 16 | right = wezterm.nerdfonts.ple_left_half_circle_thin, 17 | }, 18 | tab_separators = { 19 | left = wezterm.nerdfonts.ple_right_half_circle_thick, 20 | right = wezterm.nerdfonts.ple_left_half_circle_thick, 21 | }, 22 | }, 23 | sections = { 24 | tabline_a = { 'mode' }, 25 | tabline_b = { }, 26 | tabline_c = { }, 27 | tab_active = { { 'cwd', padding = { left = 0, right = 1 } }, }, 28 | tab_inactive = { { 'cwd', padding = { left = 0, right = 1 } } }, 29 | tabline_x = { }, 30 | tabline_y = { 'battery', 'datetime' }, 31 | tabline_z = { }, 32 | }, 33 | }) 34 | 35 | tabline.apply_to_config(config) 36 | 37 | -- Wezterm config 38 | config.term = 'wezterm' 39 | config.window_background_opacity = 0.7 40 | config.window_content_alignment = { horizontal = 'Center', vertical = 'Center' } 41 | config.window_decorations = 'NONE' 42 | config.font_size = 10.0 43 | config.disable_default_key_bindings = true 44 | config.unzoom_on_switch_pane = false 45 | config.hide_tab_bar_if_only_one_tab = true 46 | config.tab_bar_at_bottom = true 47 | config.quick_select_remove_styling = true 48 | config.inactive_pane_hsb = { saturation = 0.9, brightness = 0.5 } 49 | 50 | -- Keys 51 | config.leader = { key = 'g', mods = 'ALT' } 52 | config.keys = { 53 | -- Copy/paste 54 | { key = 'c', mods = 'CTRL|SHIFT', action = act.CopyTo 'Clipboard' }, 55 | { key = 'v', mods = 'CTRL|SHIFT', action = act.PasteFrom 'Clipboard' }, 56 | 57 | -- Pane navigation 58 | { key = 'h', mods = 'CTRL', action = act.ActivatePaneDirection 'Left' }, 59 | { key = 'j', mods = 'CTRL', action = act.ActivatePaneDirection 'Down' }, 60 | { key = 'k', mods = 'CTRL', action = act.ActivatePaneDirection 'Up' }, 61 | { key = 'l', mods = 'CTRL', action = act.ActivatePaneDirection 'Right' }, 62 | 63 | -- Resize panes 64 | { key = 'LeftArrow', mods = 'CTRL', action = act.AdjustPaneSize { 'Left', 2 } }, 65 | { key = 'RightArrow', mods = 'CTRL', action = act.AdjustPaneSize { 'Right', 2 } }, 66 | { key = 'UpArrow', mods = 'CTRL', action = act.AdjustPaneSize { 'Up', 2 } }, 67 | { key = 'DownArrow', mods = 'CTRL', action = act.AdjustPaneSize { 'Down', 2 } }, 68 | 69 | -- Split panes 70 | { key = 'v', mods = 'LEADER', action = act.SplitVertical { domain = 'CurrentPaneDomain' } }, 71 | { key = 'h', mods = 'LEADER', action = act.SplitHorizontal { domain = 'CurrentPaneDomain' } }, 72 | 73 | { key = 'Enter', mods = 'ALT', action = act.SpawnTab 'CurrentPaneDomain' }, 74 | 75 | -- Zoom/unzoom focused pane 76 | { key = 'Tab', mods = 'ALT', action = act.TogglePaneZoomState }, 77 | 78 | -- Kill pane / kill tab 79 | -- { key = 'x', mods = 'CTRL', action = act.CloseCurrentPane { confirm = true } }, 80 | -- { key = 'x', mods = 'CTRL|SHIFT', action = act.CloseCurrentTab { confirm = true } }, 81 | 82 | -- Prev/Next tab 83 | { key = 'p', mods = 'ALT', action = act.ActivateTabRelative(-1) }, 84 | { key = 'n', mods = 'ALT', action = act.ActivateTabRelative(1) }, 85 | 86 | -- Select tabs by number 87 | { key = '1', mods = 'ALT', action = act.ActivateTab(0) }, 88 | { key = '2', mods = 'ALT', action = act.ActivateTab(1) }, 89 | { key = '3', mods = 'ALT', action = act.ActivateTab(2) }, 90 | { key = '4', mods = 'ALT', action = act.ActivateTab(3) }, 91 | { key = '5', mods = 'ALT', action = act.ActivateTab(4) }, 92 | { key = '6', mods = 'ALT', action = act.ActivateTab(5) }, 93 | { key = '7', mods = 'ALT', action = act.ActivateTab(6) }, 94 | { key = '8', mods = 'ALT', action = act.ActivateTab(7) }, 95 | { key = '9', mods = 'ALT', action = act.ActivateTab(8) }, 96 | { key = '0', mods = 'ALT', action = act.ActivateTab(9) }, 97 | 98 | -- Last tab 99 | { key = ",", mods = 'LEADER', action = act.ActivateLastTab }, 100 | 101 | -- Change font size 102 | { key = "=", mods = "CTRL", action = act.IncreaseFontSize }, 103 | { key = "-", mods = "CTRL", action = act.DecreaseFontSize }, 104 | { key = "0", mods = "CTRL", action = act.ResetFontSize }, 105 | 106 | -- Search 107 | { key = "/", mods = "ALT", action = act.Search {CaseSensitiveString=""} }, 108 | 109 | -- Copy and quick select modes 110 | { key = ' ', mods = 'CTRL|ALT', action = act.ActivateCopyMode }, 111 | { key = 'o', mods = 'CTRL|ALT', action = act.QuickSelect }, 112 | { 113 | key = 'O', 114 | mods = 'CTRL|ALT', 115 | action = wezterm.action.QuickSelectArgs { 116 | label = 'Open', 117 | skip_action_on_paste = true, 118 | action = wezterm.action_callback(function(window, pane) 119 | local selection = window:get_selection_text_for_pane(pane) 120 | wezterm.open_with(selection) 121 | end), 122 | }, 123 | }, 124 | 125 | -- Clear history/viewport 126 | { key = 'k', mods = 'CTRL|SHIFT', action = act.ClearScrollback 'ScrollbackAndViewport' }, 127 | 128 | -- Move current pane to a new tab 129 | { 130 | key = 'w', 131 | mods = 'CTRL|SHIFT', 132 | action = wezterm.action_callback(function(window, pane) 133 | pane:move_to_new_tab() 134 | end), 135 | }, 136 | } 137 | 138 | return config 139 | -------------------------------------------------------------------------------- /hosts/najdorf/nextcloud.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | config, 4 | pkgs, 5 | lib, 6 | ... 7 | }: 8 | 9 | let 10 | fqdn = "nextcloud.${config.vars.domainName}"; 11 | port = 9821; 12 | collaboraCfg = config.services.collabora-online; 13 | in 14 | { 15 | age.secrets = { 16 | nextcloudAdminPass.file = "${self}/secrets/nextcloud/admin_password.age"; 17 | nextcloudSecrets.file = "${self}/secrets/nextcloud/secrets.age"; 18 | }; 19 | 20 | services.nextcloud = { 21 | enable = true; 22 | package = pkgs.nextcloud32; 23 | hostName = fqdn; 24 | 25 | database.createLocally = true; 26 | config = { 27 | dbtype = "pgsql"; 28 | adminuser = "sweenu"; 29 | adminpassFile = config.age.secrets.nextcloudAdminPass.path; 30 | }; 31 | 32 | maxUploadSize = "16G"; 33 | https = true; 34 | 35 | notify_push.enable = true; 36 | notify_push.nextcloudUrl = "http://localhost:${builtins.toString port}"; 37 | 38 | autoUpdateApps.enable = false; 39 | autoUpdateApps.startAt = "05:00:00"; 40 | 41 | phpOptions = { 42 | "opcache.interned_strings_buffer" = "32"; 43 | }; 44 | 45 | secretFile = config.age.secrets.nextcloudSecrets.path; 46 | settings = { 47 | log_type = "file"; 48 | trusted_proxies = [ 49 | "127.0.0.1" 50 | "::1" 51 | "2001:861:3884:4fd0::/64" 52 | ]; 53 | overwriteprotocol = "https"; 54 | default_phone_region = "FR"; 55 | maintenance_window_start = 1; 56 | 57 | mail_smtpmode = "smtp"; 58 | mail_smtphost = config.vars.smtp.host; 59 | mail_smtpport = config.vars.smtp.port; 60 | mail_smtpname = config.vars.smtp.user; 61 | mail_smtpsecure = "ssl"; 62 | mail_from_address = config.vars.email; 63 | mail_domain = builtins.elemAt (lib.strings.splitString "@" config.vars.email) 1; 64 | }; 65 | 66 | appstoreEnable = true; 67 | extraAppsEnable = true; 68 | extraApps = with config.services.nextcloud.package.packages.apps; { 69 | inherit contacts; 70 | inherit forms; 71 | inherit polls; 72 | inherit richdocuments; 73 | inherit music; 74 | inherit previewgenerator; 75 | inherit gpoddersync; 76 | 77 | cardhook = pkgs.fetchFromGitHub { 78 | owner = "sweenu"; 79 | repo = "cardhook"; 80 | rev = "main"; 81 | sha256 = "sha256-eGxmJ2IURQRl71FzDSVg7JXSWNNBBLT+m48NpbCdZRg="; 82 | }; 83 | }; 84 | }; 85 | 86 | services.nginx = { 87 | enable = true; 88 | recommendedProxySettings = true; 89 | defaultListen = [ ]; 90 | virtualHosts.${fqdn} = { 91 | forceSSL = false; 92 | listen = [ 93 | { 94 | addr = "0.0.0.0"; 95 | port = port; 96 | } 97 | ]; 98 | extraConfig = '' 99 | set_real_ip_from 127.0.0.1; 100 | set_real_ip_from ::1; 101 | real_ip_header X-Forwarded-For; 102 | real_ip_recursive on; 103 | ''; 104 | }; 105 | }; 106 | 107 | services.collabora-online = { 108 | enable = true; 109 | port = 9980; 110 | settings = { 111 | server_name = "collabora.${config.vars.domainName}"; 112 | ssl = { 113 | enable = false; 114 | termination = true; 115 | }; 116 | net = { 117 | listen = "loopback"; 118 | post_allow.host = [ 119 | "127.0.0.1" 120 | "::1" 121 | ]; 122 | }; 123 | storage.wopi = { 124 | "@allow" = true; 125 | host = [ fqdn ]; 126 | alias_groups = { 127 | "@mode" = "groups"; 128 | group = [ 129 | { 130 | "@allow" = true; 131 | host = "https://${fqdn}"; 132 | alias = "http://localhost:${builtins.toString port}"; 133 | } 134 | ]; 135 | }; 136 | }; 137 | }; 138 | }; 139 | 140 | systemd.services.nextcloud-setup-collabora = 141 | let 142 | inherit (config.services.nextcloud) occ; 143 | wopi_url = "http://localhost:${toString collaboraCfg.port}"; 144 | public_wopi_url = "https://${collaboraCfg.settings.server_name}"; 145 | wopi_allowlist = "127.0.0.1,::1,2001:861:3884:4fd0:8ceb:7d56:bf25:5a17"; 146 | in 147 | { 148 | wantedBy = [ "multi-user.target" ]; 149 | after = [ 150 | "nextcloud-setup.service" 151 | "coolwsd.service" 152 | ]; 153 | requires = [ "coolwsd.service" ]; 154 | serviceConfig = { 155 | Type = "oneshot"; 156 | RemainAfterExit = true; 157 | }; 158 | script = '' 159 | ${lib.getExe occ} config:app:set richdocuments wopi_url --value "${wopi_url}" 160 | ${lib.getExe occ} config:app:set richdocuments public_wopi_url --value "${public_wopi_url}" 161 | ${lib.getExe occ} config:app:set richdocuments wopi_allowlist --value "${wopi_allowlist}" 162 | ''; 163 | }; 164 | 165 | services.traefik.dynamicConfigOptions.http = rec { 166 | routers.to-nextcloud = { 167 | rule = "Host(`${fqdn}`)"; 168 | service = "nextcloud"; 169 | }; 170 | routers.to-collabora = { 171 | rule = "Host(`${collaboraCfg.settings.server_name}`)"; 172 | service = "collabora"; 173 | }; 174 | services = { 175 | "${routers.to-nextcloud.service}".loadBalancer.servers = [ 176 | { 177 | url = "http://localhost:${builtins.toString port}"; 178 | } 179 | ]; 180 | "${routers.to-collabora.service}".loadBalancer.servers = [ 181 | { 182 | url = "http://localhost:${builtins.toString collaboraCfg.port}"; 183 | } 184 | ]; 185 | }; 186 | }; 187 | 188 | services.restic.backups.opt.paths = [ config.services.nextcloud.home ]; 189 | } 190 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NixOS config for my personal computer and servers 2 | 3 | ![screenshot](./assets/screenshot.jpeg) 4 | 5 | ## Presentation 6 | 7 | I have three [NixOS](https://nixos.org) machines: 8 | - _carokann_: personal computer ([Framework](https://frame.work) laptop). 9 | - _najdorf_: server where I deploy my self-hosted apps. 10 | - _grunfeld_: main RaspberryPi that serves as a [snapcast](https://github.com/badaix/snapcast) server and a local backup. 11 | 12 | The hosts communicate through [Tailscale](https://tailscale.com). 13 | 14 | This repo is structured with [flake-parts](https://flake.parts/) and [haumea](https://github.com/nix-community/haumea) for automatic module/profile discovery, preserving all the convenience features from the original [digga](https://github.com/divnix/digga) setup. 15 | 16 | ### Directory Structure 17 | ``` 18 | nixfiles/ 19 | ├── hosts/ → Host configurations 20 | ├── modules/ → Custom NixOS modules 21 | ├── hm-modules/ → Custom home-manager modules 22 | ├── profiles/ → Reusable configuration profiles 23 | ├── pkgs/ → Custom packages 24 | ├── lib/ → Custom lib functions 25 | ├── overlays/ → Additional overlays 26 | └── shell/ → Development shell 27 | ``` 28 | 29 | ### Software I use on my personal computer (carokann) 30 | 31 | - Wayland compositor: [hyprland](https://hypr.land) 32 | - Desktop shell: [Caelestia](https://github.com/caelestia-dots/shell) 33 | - Editor: [kakoune](https://github.com/mawww/kakoune) 34 | - Terminal: [wezterm](https://wezterm.org) 35 | - Shell: [fish](https://fishshell.com) 36 | - Browser: [zen](https://zen-browser.app/) 37 | 38 | ### Self-hosted apps on my server (najdorf) 39 | 40 | Here's the list of the main services deployed through their NixOS modules: 41 | - [Træfik](https://traefik.io/traefik) 42 | - [Authelia](https://www.authelia.com) 43 | - [LLDAP](https://github.com/lldap/lldap) 44 | - [Nextcloud](https://nextcloud.com) 45 | - [n8n](https://n8n.io/) 46 | - [Immich](https://immich.app) 47 | - [Home Assistant](https://www.home-assistant.io/) 48 | - [goeland](https://github.com/slurdge/goeland) 49 | - [Cockpit](https://cockpit-project.org/) 50 | - [Dawarich](https://dawarich.app/) 51 | 52 | I deploy some service as Docker containers through [Arion](https://github.com/hercules-ci/arion): 53 | - [Calibre-web](https://github.com/janeczku/calibre-web) 54 | - [Grist](https://www.getgrist.com/) 55 | - [Obsidian Livesync](https://github.com/vrtmrz/obsidian-livesync) 56 | - [Obsidian share-note](https://github.com/alangrainger/share-note) 57 | 58 | Important data is backed up with [Restic](https://restic.net). 59 | 60 | 61 | ## Bootstrap 62 | ### PC 63 | Create a bootstrap ISO for a personal computer run: 64 | ```bash 65 | $ nixos-generate --flake '.#bootstrap' --format iso 66 | ``` 67 | 68 | Then install NixOS: 69 | ```bash 70 | $ cd nixfiles 71 | $ sudo disko --mode destroy,format,mount -f '.#carokann' 72 | $ sudo mount /dev/mapper/cryptroot /mnt 73 | $ sudo mkdir /mnt/boot 74 | $ sudo mount /dev/nvme0n1p1 /mnt/boot 75 | # Generate the hardware config for reference, change what you need before install 76 | $ sudo nixos-generate-config --root /mnt --dir /home/sweenu 77 | $ sudo nixos-install --flake '.#carokann' --root /mnt 78 | 79 | # Enroll your fingerprint 80 | $ sudo fprintd-enroll 81 | # Enroll TPM2 for dm-crypt 82 | $ sudo systemd-cryptenroll --tpm2-device=auto /dev/nvme0n1p2 83 | ``` 84 | 85 | After logging in with tailscale and enabling SSH connections (`sudo tailscale set --ssh`), you can backup the important files: 86 | - ~/.ssh 87 | - ~/.local/share/fish/fish_history 88 | - /etc/NetworkManager/system-connections (replace interface names: `sed -i 's/wlp166s0f0/wlp192s0/' *`) 89 | - All documents from ~ that you want to keep 90 | 91 | ### Raspberry Pi 92 | Create a ready-to-boot SD card for a RaspberryPi, do the following: 93 | ```bash 94 | $ nixos-generate --flake '.#grunfeld' --format sd-aarch64 --system aarch64-linux 95 | $ unzstd -d {the output path from the command above} -o nixos-sd-image.img 96 | $ sudo dd if=nixos-sd-image.img of=/dev/sda bs=64K status=progress 97 | ``` 98 | 99 | ### Server 100 | Deploy the server config to a new machine: 101 | ```bash 102 | # Add an auth key file to the tailscale module for unattended login. 103 | # Then run: 104 | $ nixos-anywhere --copy-host-keys --flake '.#najdorf' root@ 105 | # Copy the old server's host key 106 | $ scp 'root@najdorf:/etc/ssh/ssh_host_*' root@najdorf-1:/etc/ssh/ 107 | # Stop all running services, then: 108 | $ ssh root@najdorf 'ssh-keyscan -H najdorf-1 >> ~/.ssh/known_hosts' 109 | $ ssh -f root@najdorf 'rsync -Aavz /opt/ root@najdorf-1:/opt > /home/sweenu/rsync.log 2>&1 &' 110 | # Transfer Postgres database 111 | $ ssh root@najdorf 'sudo -u postgres pg_dumpall > /root/pgdump_all.sql' 112 | $ scp root@najdorf:/root/pgdump_all.sql root@najdorf-1:/root/ 113 | $ ssh root@najdorf-1 'sudo -u postgres psql -f /root/pgdump_all.sql' 114 | # I made all Docker volumes bind mounts in /opt in order for this command to be enough for migrating everything important. 115 | # Uncomment services in hosts/najdorf/default.nix and comment the tailscale-login service line. 116 | # Remove najdorf from tailscale and change the tailscale name from najdorf-1 to najdorf. 117 | # Change DNS records to point to the new server (on Cloudflare, change the IP scope of the API token to the new IP). 118 | # Finally: 119 | $ deploy '.#najdorf' 120 | $ ssh root@najdorf docker network create traefik 121 | # All done! 122 | ``` 123 | 124 | sudo ssh-keygen -t ed25519 -N "" -f /etc/ssh/initrd_ssh_host_ed25519_key 125 | 126 | ## Acknowledgment: 127 | * Thanks to the [digga](https://digga.divnix.com) people for making my life easier when I first started to use NixOS. 128 | * Thanks to [soramanew](https://github.com/soramanew) for the amazing [caelestia shell](https://github.com/caelestia-dots/shell) and for the [hyprland config](https://github.com/caelestia-dots/caelestia/tree/e456e8abb90b94f2e6ae859f6e3b3ef2a5e27099/hypr) from which I took liberally. 129 | -------------------------------------------------------------------------------- /hosts/najdorf/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | config, 4 | suites, 5 | pkgs, 6 | lib, 7 | ... 8 | }: 9 | 10 | let 11 | resticRepository = "s3:s3.us-west-001.backblazeb2.com/sweenu-server-restic"; 12 | encryptedRoot = "cryptroot"; 13 | network = { 14 | matchConfig.Name = "en* eth*"; 15 | networkConfig = { 16 | DHCP = "no"; 17 | Address = config.vars.staticIPWithSubnet; 18 | Gateway = config.vars.defaultGateway; 19 | IPv6AcceptRA = true; 20 | DNSDefaultRoute = true; 21 | }; 22 | domains = [ 23 | "~lan" 24 | "~local" 25 | "~bytel.fr" # Bbox 26 | ]; 27 | dns = [ config.vars.defaultGateway ]; 28 | linkConfig.RequiredForOnline = "yes"; 29 | }; 30 | in 31 | { 32 | imports = [ 33 | # Services 34 | ./traefik.nix 35 | ./authelia.nix 36 | ./portainer.nix 37 | ./nextcloud.nix 38 | ./calibre-web.nix 39 | ./goeland.nix 40 | ./n8n.nix 41 | ./immich.nix 42 | ./obsidian-livesync.nix 43 | ./obsidian-share-note.nix 44 | ./lldap.nix 45 | ./nocodb.nix 46 | ./netdata.nix 47 | ./hass.nix 48 | ./cockpit.nix 49 | ./minecraft.nix 50 | ] 51 | ++ suites.base 52 | ++ suites.server; 53 | 54 | users.groups.smtp = { }; 55 | age.secrets = { 56 | smtpPassword = { 57 | file = "${self}/secrets/smtp_password.age"; 58 | group = "smtp"; 59 | mode = "440"; 60 | }; 61 | }; 62 | 63 | boot = { 64 | initrd = { 65 | availableKernelModules = [ 66 | "nvme" 67 | "xhci_pci" 68 | "thunderbolt" 69 | "usb_storage" 70 | "usbhid" 71 | "sd_mod" 72 | "r8152" # for framework eth adapter 73 | ]; 74 | luks.devices.${encryptedRoot} = { 75 | allowDiscards = true; 76 | }; 77 | # Allows decrypting the drive over SSH 78 | network = { 79 | enable = true; 80 | ssh = { 81 | enable = true; 82 | hostKeys = [ "/etc/ssh/initrd_ssh_host_ed25519_key" ]; 83 | }; 84 | }; 85 | systemd = { 86 | enable = true; 87 | users.root.shell = "/bin/systemd-tty-ask-password-agent"; 88 | network.networks."10-wired" = network; 89 | }; 90 | }; 91 | kernelPackages = pkgs.linuxPackages_6_17; 92 | kernelModules = [ "kvm-amd" ]; 93 | loader = { 94 | systemd-boot = { 95 | enable = true; 96 | editor = false; 97 | configurationLimit = 5; 98 | }; 99 | efi.canTouchEfiVariables = true; 100 | }; 101 | }; 102 | 103 | disko = { 104 | devices = { 105 | disk.main = { 106 | device = "/dev/nvme0n1"; 107 | type = "disk"; 108 | content = { 109 | type = "gpt"; 110 | partitions = { 111 | ESP = { 112 | type = "EF00"; 113 | size = "512M"; 114 | content = { 115 | type = "filesystem"; 116 | mountpoint = "/boot"; 117 | format = "vfat"; 118 | mountOptions = [ "umask=0077" ]; 119 | }; 120 | }; 121 | root = { 122 | size = "100%"; 123 | content = { 124 | type = "luks"; 125 | name = encryptedRoot; 126 | content = { 127 | type = "filesystem"; 128 | mountpoint = "/"; 129 | format = "ext4"; 130 | }; 131 | }; 132 | }; 133 | }; 134 | }; 135 | }; 136 | }; 137 | }; 138 | 139 | zramSwap.enable = true; 140 | 141 | environment.defaultPackages = with pkgs; [ 142 | framework-tool 143 | restic 144 | redu 145 | wol 146 | ]; 147 | 148 | users.users."${config.vars.username}".openssh.authorizedKeys.keys = [ config.vars.sshPublicKey ]; 149 | 150 | time.timeZone = config.vars.timezone; 151 | 152 | systemd.network.networks."10-wired" = network; 153 | 154 | networking.usePredictableInterfaceNames = false; 155 | 156 | virtualisation = { 157 | docker = { 158 | enable = true; 159 | daemon.settings.dns = config.vars.dnsResolvers; 160 | }; 161 | arion.backend = "docker"; 162 | }; 163 | 164 | services = { 165 | avahi.enable = true; 166 | journald.extraConfig = '' 167 | SystemMaxUse = 10G; 168 | ''; 169 | # PostgreSQL config and backups 170 | postgresql = { 171 | package = pkgs.postgresql_16; 172 | settings.log_timezone = config.time.timeZone; 173 | }; 174 | postgresqlBackup = { 175 | enable = config.services.postgresql.enable; 176 | startAt = "*-*-* 03:17:00"; 177 | }; 178 | fstrim.enable = true; 179 | fwupd.enable = true; 180 | }; 181 | 182 | # Restic backups 183 | age.secrets = { 184 | resticPassword.file = "${self}/secrets/restic/password.age"; 185 | resticEnv.file = "${self}/secrets/restic/env.age"; 186 | }; 187 | environment.sessionVariables = { 188 | RESTIC_PASSWORD_FILE = config.age.secrets.resticPassword.path; 189 | RESTIC_REPOSITORY = resticRepository; 190 | }; 191 | services.restic = { 192 | backups.opt = { 193 | initialize = true; 194 | repository = resticRepository; 195 | environmentFile = config.age.secrets.resticEnv.path; 196 | paths = [ 197 | "/opt" 198 | ] 199 | ++ ( 200 | if config.services.postgresqlBackup.enable then 201 | [ config.services.postgresqlBackup.location ] 202 | else 203 | [ ] 204 | ); 205 | pruneOpts = [ 206 | "--keep-last 36" 207 | "--keep-daily 14" 208 | "--keep-weekly 5" 209 | "--keep-monthly 12" 210 | "--keep-yearly 3" 211 | ]; 212 | timerConfig = { 213 | OnCalendar = "*-*-* *:00:00"; # every hour 214 | RandomizedDelaySec = "5m"; 215 | }; 216 | passwordFile = config.age.secrets.resticPassword.path; 217 | backupCleanupCommand = "${pkgs.curl}/bin/curl -m 10 --retry 5 https://hc-ping.com/3e004d53-809a-4386-bb45-a36fc919120a/$EXIT_STATUS"; 218 | exclude = [ "/opt/containerd" ]; 219 | }; 220 | }; 221 | } 222 | -------------------------------------------------------------------------------- /hosts/najdorf/hass.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | config, 4 | pkgs, 5 | inputs, 6 | lib, 7 | ... 8 | }: 9 | 10 | let 11 | fqdn = "hass.${config.vars.domainName}"; 12 | serverIP = config.vars.staticIP; 13 | serverIPv6 = "2001:861:3884:4fd0:8ceb:7d56:bf25:5a17"; 14 | hassPort = config.services.home-assistant.config.http.server_port; 15 | massWebPort = 8095; 16 | in 17 | { 18 | networking.firewall.extraCommands = '' 19 | # Thread 20 | ${lib.openTCPPortForLAN 8081} 21 | ${lib.openTCPPortForLAN 8082} 22 | # Music Assistant 23 | ${lib.openTCPPortForLAN massWebPort} # web interface 24 | ${lib.openTCPPortForLAN 8097} # web socket 25 | # Squeezlite 26 | ${lib.openTCPPortForLAN 3483} 27 | ${lib.openUDPPortForLAN 3483} 28 | ${lib.openTCPPortForLAN 9090} 29 | # Spotify Connect & more (ephemeral ports) 30 | ${lib.openTCPPortRangeForLAN 32768 65535} 31 | ${lib.openUDPPortRangeForLAN 32768 65535} 32 | ''; 33 | 34 | age.secrets = { 35 | hassSecretsYaml = { 36 | file = "${self}/secrets/hass/secrets.age"; 37 | path = "${config.services.home-assistant.configDir}/secrets.yaml"; 38 | owner = config.systemd.services.home-assistant.serviceConfig.User; 39 | group = config.systemd.services.home-assistant.serviceConfig.Group; 40 | mode = "600"; 41 | }; 42 | }; 43 | 44 | services = { 45 | home-assistant = { 46 | enable = true; 47 | extraComponents = [ 48 | "apple_tv" # looks for it for some reason 49 | "default_config" 50 | "esphome" 51 | "google" 52 | "google_translate" # TTS fallback for wyoming-piper 53 | "improv_ble" 54 | "isal" # fast compression 55 | "ipp" 56 | "matter" 57 | "met" 58 | "meteo_france" 59 | "music_assistant" 60 | "open_router" 61 | "otbr" 62 | "overkiz" # needed for somfy 63 | "shelly" 64 | "somfy" 65 | "spotify" 66 | "thread" 67 | "upnp" 68 | "wyoming" 69 | ]; 70 | customComponents = [ 71 | pkgs.bbox 72 | pkgs.dawarich-ha 73 | ]; 74 | config = { 75 | automation = "!include automations.yaml"; 76 | script = "!include scripts.yaml"; 77 | homeassistant = { 78 | name = "Home"; 79 | latitude = "!secret latitude"; 80 | longitude = "!secret longitude"; 81 | elevation = "!secret elevation"; 82 | unit_system = "metric"; 83 | temperature_unit = "C"; 84 | external_url = "https://${fqdn}"; 85 | internal_url = "http://${serverIP}:${builtins.toString hassPort}"; 86 | }; 87 | http = { 88 | use_x_forwarded_for = true; 89 | trusted_proxies = [ 90 | "127.0.0.1" 91 | serverIP 92 | "::1" 93 | serverIPv6 94 | ]; 95 | }; 96 | 97 | # Default config except `backup`, `cloud`, `go2rtc` 98 | assist_pipeline = { }; 99 | bluetooth = { }; 100 | config = { }; 101 | conversation = { }; 102 | dhcp = { }; 103 | energy = { }; 104 | history = { }; 105 | homeassistant_alerts = { }; 106 | image_upload = { }; 107 | logbook = { }; 108 | media_source = { }; 109 | mobile_app = { }; 110 | my = { }; 111 | ssdp = { }; 112 | stream = { }; 113 | sun = { }; 114 | usage_prediction = { }; 115 | usb = { }; 116 | webhook = { }; 117 | zeroconf = { }; 118 | }; 119 | configWritable = true; 120 | openFirewall = true; 121 | }; 122 | 123 | music-assistant = { 124 | enable = true; 125 | providers = [ 126 | "hass" 127 | "hass_players" 128 | "spotify" 129 | "spotify_connect" 130 | "squeezelite" 131 | ]; 132 | }; 133 | 134 | matter-server = { 135 | enable = true; 136 | }; 137 | 138 | openthread-border-router = { 139 | enable = true; 140 | package = inputs.otbr.legacyPackages.${pkgs.stdenv.hostPlatform.system}.openthread-border-router; 141 | backboneInterface = "eth0"; 142 | rest.listenAddress = "::"; 143 | web = { 144 | enable = true; 145 | listenAddress = "::"; 146 | }; 147 | radio = { 148 | device = "/dev/serial/by-id/usb-Nabu_Casa_Home_Assistant_Connect_ZBT-1_a47f43e6f769ef11bee8a976d9b539e6-if00-port0"; 149 | baudRate = 460800; 150 | flowControl = false; 151 | }; 152 | }; 153 | 154 | wyoming = { 155 | piper = { 156 | servers."main" = { 157 | enable = true; 158 | zeroconf.enable = false; 159 | voice = "en_GB-jenny_dioco-medium"; 160 | uri = "tcp://0.0.0.0:10200"; 161 | }; 162 | }; 163 | faster-whisper = { 164 | servers.main = { 165 | enable = true; 166 | model = "small"; 167 | language = "en"; 168 | uri = "tcp://0.0.0.0:10300"; 169 | }; 170 | }; 171 | }; 172 | }; 173 | 174 | services.traefik.dynamicConfigOptions.http = rec { 175 | routers.to-hass = { 176 | rule = "Host(`${fqdn}`)"; 177 | service = "hass"; 178 | }; 179 | services."${routers.to-hass.service}".loadBalancer.servers = [ 180 | { 181 | url = "http://127.0.0.1:${builtins.toString hassPort}"; 182 | } 183 | ]; 184 | routers.to-mass = { 185 | rule = routers.to-hass.rule + " && PathPrefix(`/mass`)"; 186 | service = "mass"; 187 | middlewares = [ 188 | "mass-strip" 189 | "mass-prefix-headers" 190 | "mass-allow" 191 | ]; 192 | }; 193 | services."${routers.to-mass.service}".loadBalancer.servers = [ 194 | { 195 | url = "http://127.0.0.1:${builtins.toString massWebPort}"; 196 | } 197 | ]; 198 | middlewares = { 199 | mass-strip.stripPrefix.prefixes = [ "/mass" ]; 200 | mass-prefix-headers.headers.customRequestHeaders = { 201 | "X-Forwarded-Prefix" = "/mass"; 202 | "X-Forwarded-Proto" = "https"; 203 | "X-Forwarded-Host" = fqdn; 204 | }; 205 | mass-allow.ipAllowList.sourceRange = [ 206 | "127.0.0.1/32" 207 | "192.168.0.0/16" 208 | "10.0.0.0/8" 209 | "172.16.0.0/12" 210 | "::1/128" 211 | "fc00::/7" 212 | "2001:861:3884:4fd0::/64" 213 | ]; 214 | }; 215 | }; 216 | 217 | services.restic.backups.opt.paths = [ config.services.home-assistant.configDir ]; 218 | } 219 | -------------------------------------------------------------------------------- /profiles/graphical/zed/kakoune_keymap.nix: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | context = "vim_mode == insert"; 4 | bindings = { 5 | "alt-;" = "vim::TemporaryNormal"; 6 | "ctrl-n" = "editor::ShowWordCompletions"; 7 | "ctrl-p" = "editor::ShowWordCompletions"; 8 | "ctrl-r" = "vim::PushRegister"; 9 | "ctrl-v" = [ 10 | "vim::PushLiteral" 11 | { } 12 | ]; 13 | "escape" = "vim::NormalBefore"; 14 | }; 15 | } 16 | { 17 | context = "(vim_mode == helix_normal || vim_mode == helix_select) && !menu"; 18 | bindings = { 19 | "%" = "editor::SelectAll"; 20 | "*" = [ 21 | "vim::MoveToNext" 22 | { partial_word = false; } 23 | ]; 24 | "," = "vim::HelixKeepNewestSelection"; 25 | "/" = "vim::Search"; 26 | ";" = "vim::HelixCollapseSelection"; 27 | "<" = "vim::Outdent"; 28 | "=" = "vim::AutoIndent"; 29 | ">" = "vim::Indent"; 30 | "`" = "vim::ConvertToLowerCase"; 31 | "a" = "vim::HelixAppend"; 32 | "alt-*" = [ 33 | "vim::MoveToNext" 34 | { partial_word = true; } 35 | ]; 36 | "alt-." = "vim::RepeatFind"; 37 | "alt-/" = [ 38 | "vim::Search" 39 | { backwards = true; } 40 | ]; 41 | "alt-;" = "vim::OtherEnd"; 42 | "alt-`" = "vim::ChangeCase"; 43 | "alt-b" = [ 44 | "vim::PreviousWordStart" 45 | { ignore_punctuation = true; } 46 | ]; 47 | "alt-c" = "vim::HelixSubstituteNoYank"; 48 | "alt-d" = "editor::Delete"; 49 | "alt-e" = [ 50 | "vim::NextWordEnd" 51 | { ignore_punctuation = true; } 52 | ]; 53 | "alt-f" = [ 54 | "vim::PushFindBackward" 55 | { 56 | after = false; 57 | multiline = true; 58 | } 59 | ]; 60 | "alt-h" = "vim::StartOfLine"; 61 | "alt-j" = "vim::JoinLines"; 62 | "alt-J" = "vim::JoinLines"; 63 | "alt-l" = "vim::EndOfLine"; 64 | "alt-s" = [ 65 | "editor::SplitSelectionIntoLines" 66 | { keep_selections = true; } 67 | ]; 68 | "alt-shift-c" = "vim::HelixDuplicateAbove"; 69 | "alt-shift-s" = "editor::SelectLine"; 70 | "alt-t" = [ 71 | "vim::PushFindBackward" 72 | { 73 | after = true; 74 | multiline = true; 75 | } 76 | ]; 77 | "alt-w" = [ 78 | "vim::NextWordStart" 79 | { ignore_punctuation = true; } 80 | ]; 81 | "alt-x" = "editor::SelectLine"; 82 | "b" = "vim::PreviousWordStart"; 83 | "c" = "vim::HelixSubstitute"; 84 | "ctrl-b" = "vim::PageUp"; 85 | "ctrl-d" = "vim::ScrollDown"; 86 | "ctrl-f" = "vim::PageDown"; 87 | "ctrl-u" = "vim::ScrollUp"; 88 | "d" = "vim::HelixDelete"; 89 | "down" = "vim::Down"; 90 | "e" = "vim::NextWordEnd"; 91 | "end" = "vim::EndOfLine"; 92 | "f" = [ 93 | "vim::PushFindForward" 94 | { 95 | before = false; 96 | multiline = true; 97 | } 98 | ]; 99 | "g ." = "vim::HelixGotoLastModification"; 100 | "g b" = "vim::WindowBottom"; 101 | "g c" = "vim::WindowMiddle"; 102 | "g e" = "vim::EndOfDocument"; 103 | "g f" = "editor::OpenSelectedFilename"; 104 | "g g" = "vim::StartOfDocument"; 105 | "g h" = "vim::StartOfLine"; 106 | "g i" = "vim::FirstNonWhitespace"; 107 | "g j" = "vim::EndOfDocument"; 108 | "g k" = "vim::StartOfDocument"; 109 | "g l" = "vim::EndOfLine"; 110 | "g n" = "pane::ActivateNextItem"; 111 | "g p" = "pane::ActivatePreviousItem"; 112 | "g t" = "vim::WindowTop"; 113 | "h" = "vim::WrappingLeft"; 114 | "home" = "vim::StartOfLine"; 115 | "i" = "vim::HelixInsert"; 116 | "j" = "vim::Down"; 117 | "k" = "vim::Up"; 118 | "l" = "vim::WrappingRight"; 119 | "left" = "vim::WrappingLeft"; 120 | "m" = "vim::PushHelixMatch"; 121 | # TODO: replace by this when on > 0.213.4 : "n" = "vim::HelixSelectNext"; 122 | "n" = "search::SelectNextMatch"; 123 | "o" = "vim::InsertLineBelow"; 124 | "p" = "vim::HelixPaste"; 125 | "pagedown" = "vim::PageDown"; 126 | "pageup" = "vim::PageUp"; 127 | "r" = "vim::PushReplace"; 128 | "right" = "vim::WrappingRight"; 129 | "s" = "vim::HelixSelectRegex"; 130 | "shift-a" = "vim::InsertEndOfLine"; 131 | "shift-c" = "vim::HelixDuplicateBelow"; 132 | "shift-h" = "pane::ActivatePreviousItem"; 133 | "shift-i" = "vim::InsertFirstNonWhitespace"; 134 | "shift-l" = "pane::ActivateNextItem"; 135 | "shift-m" = "vim::Matching"; 136 | # TODO: replace by this when on > 0.213.4 : "alt-n" = "vim::HelixSelectPrevious"; 137 | "alt-n" = "search::SelectPreviousMatch"; 138 | "shift-o" = "vim::InsertLineAbove"; 139 | "shift-p" = [ 140 | "vim::HelixPaste" 141 | { before = true; } 142 | ]; 143 | "shift-r" = "editor::Paste"; 144 | "shift-u" = "vim::Redo"; 145 | "space a" = "editor::ToggleCodeActions"; 146 | "space c" = "editor::ToggleComments"; 147 | "space d" = "editor::GoToDiagnostic"; 148 | "space f" = "file_finder::Toggle"; 149 | "space h" = "editor::SelectAllMatches"; 150 | "space k" = "editor::Hover"; 151 | "space p" = "editor::Paste"; 152 | "space r" = "editor::Rename"; 153 | "space s" = "outline::Toggle"; 154 | "space shift-s" = "project_symbols::Toggle"; 155 | "space w h" = "workspace::ActivatePaneLeft"; 156 | "space w j" = "workspace::ActivatePaneDown"; 157 | "space w k" = "workspace::ActivatePaneUp"; 158 | "space w l" = "workspace::ActivatePaneRight"; 159 | "space w q" = "pane::CloseActiveItem"; 160 | "space w s" = "pane::SplitRight"; 161 | "space w v" = "pane::SplitDown"; 162 | "space y" = "editor::Copy"; 163 | "t" = [ 164 | "vim::PushFindForward" 165 | { 166 | before = true; 167 | multiline = true; 168 | } 169 | ]; 170 | "u" = "vim::Undo"; 171 | "up" = "vim::Up"; 172 | "v b" = "editor::ScrollCursorBottom"; 173 | "v c" = "editor::ScrollCursorCenter"; 174 | "v h" = "vim::ColumnLeft"; 175 | "v j" = "vim::LineDown"; 176 | "v k" = "vim::LineUp"; 177 | "v l" = "vim::ColumnRight"; 178 | "v t" = "editor::ScrollCursorTop"; 179 | "v v" = "editor::ScrollCursorCenter"; 180 | "w" = "vim::NextWordStart"; 181 | "x" = "vim::HelixSelectLine"; 182 | "y" = "vim::HelixYank"; 183 | "~" = "vim::ConvertToUpperCase"; 184 | }; 185 | } 186 | { 187 | context = "VimControl && !menu"; 188 | bindings = { 189 | ":" = "command_palette::Toggle"; 190 | "q" = "vim::ReplayLastRecording"; 191 | "shift-q" = "vim::ToggleRecord"; 192 | }; 193 | } 194 | ] 195 | -------------------------------------------------------------------------------- /hosts/najdorf/authelia.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | config, 4 | lib, 5 | ... 6 | }: 7 | 8 | let 9 | instance = "main"; 10 | autheliaUser = config.services.authelia.instances."${instance}".user; 11 | dbUser = autheliaUser; 12 | dbName = dbUser; 13 | fqdn = "authelia.${config.vars.domainName}"; 14 | autheliaPort = 9091; 15 | lldapConfig = config.services.lldap.settings; 16 | in 17 | { 18 | age.secrets = { 19 | "authelia/jwtSecret" = { 20 | file = "${self}/secrets/authelia/jwt_secret.age"; 21 | owner = autheliaUser; 22 | }; 23 | "authelia/sessionSecret" = { 24 | file = "${self}/secrets/authelia/session_secret.age"; 25 | owner = autheliaUser; 26 | }; 27 | "authelia/storageEncryptionKey" = { 28 | file = "${self}/secrets/authelia/storage_encryption_key.age"; 29 | owner = autheliaUser; 30 | }; 31 | "authelia/ldapPassword" = { 32 | file = "${self}/secrets/authelia/ldap_password.age"; 33 | owner = autheliaUser; 34 | }; 35 | "authelia/oidcHmacSecret" = { 36 | file = "${self}/secrets/authelia/oidc_hmac_secret.age"; 37 | owner = autheliaUser; 38 | }; 39 | "authelia/oidcJwtPrivateKey" = { 40 | file = "${self}/secrets/authelia/oidc_jwt_private_key.age"; 41 | owner = autheliaUser; 42 | }; 43 | }; 44 | 45 | users.users."${autheliaUser}".extraGroups = [ "smtp" ]; 46 | 47 | systemd.services."authelia-${instance}".after = [ "lldap.service" ]; 48 | 49 | services.authelia.instances."${instance}" = { 50 | enable = true; 51 | secrets = { 52 | storageEncryptionKeyFile = config.age.secrets."authelia/storageEncryptionKey".path; 53 | jwtSecretFile = config.age.secrets."authelia/jwtSecret".path; 54 | sessionSecretFile = config.age.secrets."authelia/sessionSecret".path; 55 | oidcHmacSecretFile = config.age.secrets."authelia/oidcHmacSecret".path; 56 | oidcIssuerPrivateKeyFile = config.age.secrets."authelia/oidcJwtPrivateKey".path; 57 | }; 58 | environmentVariables = { 59 | AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE = config.age.secrets.smtpPassword.path; 60 | AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE = 61 | config.age.secrets."authelia/ldapPassword".path; 62 | }; 63 | settings = { 64 | theme = "auto"; 65 | server = { 66 | address = "tcp://:${builtins.toString autheliaPort}/"; 67 | buffers = { 68 | read = 16384; 69 | }; 70 | endpoints = { 71 | authz = { 72 | forward-auth = { 73 | implementation = "ForwardAuth"; 74 | }; 75 | basic-auth = { 76 | implementation = "ForwardAuth"; 77 | authn_strategies = [ 78 | { 79 | name = "HeaderAuthorization"; 80 | schemes = [ "Basic" ]; 81 | } 82 | ]; 83 | }; 84 | }; 85 | }; 86 | }; 87 | log.level = "warn"; 88 | storage.postgres = { 89 | address = "unix:///run/postgresql"; 90 | database = dbName; 91 | username = dbUser; 92 | }; 93 | totp = { 94 | issuer = "${fqdn}"; 95 | period = 30; 96 | skew = 1; 97 | }; 98 | authentication_backend = { 99 | password_reset.disable = false; 100 | refresh_interval = "1m"; 101 | ldap = { 102 | implementation = "lldap"; 103 | address = "ldap://${lldapConfig.ldap_host}:${builtins.toString lldapConfig.ldap_port}"; 104 | base_dn = lldapConfig.ldap_base_dn; 105 | user = "uid=authelia,ou=people,${lldapConfig.ldap_base_dn}"; 106 | users_filter = "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person))"; 107 | groups_filter = "(&(member={dn})(objectClass=groupOfNames))"; 108 | }; 109 | }; 110 | access_control = { 111 | default_policy = "deny"; 112 | rules = [ 113 | { 114 | domain = "*.${config.vars.domainName}"; 115 | subject = [ "user:sweenu" ]; 116 | policy = "two_factor"; 117 | } 118 | { 119 | domain_regex = "^(?P\\w+)\\.${lib.strings.escapeRegex config.vars.domainName}$"; 120 | policy = "one_factor"; 121 | } 122 | ]; 123 | }; 124 | session = { 125 | expiration = "1h"; 126 | inactivity = "5m"; 127 | remember_me = "3M"; 128 | cookies = [ 129 | { 130 | domain = config.vars.domainName; 131 | authelia_url = "https://${fqdn}"; 132 | default_redirection_url = "https://${fqdn}/"; 133 | } 134 | ]; 135 | }; 136 | regulation = { 137 | max_retries = 3; 138 | find_time = "2m"; 139 | ban_time = "10m"; 140 | }; 141 | notifier.smtp = { 142 | username = config.vars.smtp.user; 143 | address = "submissions://${config.vars.smtp.host}:${toString config.vars.smtp.port}"; 144 | sender = "Authelia <${config.vars.email}>"; 145 | identifier = config.vars.domainName; 146 | }; 147 | password_policy.standard = { 148 | enabled = true; 149 | min_length = 8; 150 | max_length = 0; 151 | }; 152 | ntp.address = "${builtins.head config.networking.timeServers}:123"; 153 | }; 154 | }; 155 | 156 | services.traefik.dynamicConfigOptions.http = rec { 157 | middlewares = 158 | let 159 | forwardAuth = endpoint: { 160 | address = "http://localhost:${builtins.toString autheliaPort}/api/authz/${endpoint}"; 161 | trustForwardHeader = true; 162 | authResponseHeaders = [ 163 | "Remote-User" 164 | "Remote-Groups" 165 | "Remote-Email" 166 | "Remote-Name" 167 | ]; 168 | }; 169 | in 170 | { 171 | authelia.forwardAuth = forwardAuth "forward-auth"; 172 | authelia-basic.forwardAuth = forwardAuth "basic-auth"; 173 | authelia-headers = { 174 | headers = { 175 | browserXssFilter = true; 176 | customFrameOptionsValue = "SAMEORIGIN"; 177 | customResponseHeaders = { 178 | "Cache-Control" = "no-store"; 179 | "Pragma" = "no-cache"; 180 | }; 181 | }; 182 | }; 183 | }; 184 | 185 | routers.to-authelia = { 186 | rule = "Host(`${fqdn}`)"; 187 | service = "authelia"; 188 | middlewares = [ "authelia-headers" ]; 189 | }; 190 | 191 | services."${routers.to-authelia.service}".loadBalancer.servers = [ 192 | { 193 | url = "http://localhost:${builtins.toString autheliaPort}"; 194 | } 195 | ]; 196 | }; 197 | 198 | services.postgresql = { 199 | enable = true; 200 | ensureDatabases = [ dbName ]; 201 | ensureUsers = [ 202 | { 203 | name = dbUser; 204 | ensureDBOwnership = true; 205 | } 206 | ]; 207 | }; 208 | } 209 | -------------------------------------------------------------------------------- /profiles/develop/pgcli/config: -------------------------------------------------------------------------------- 1 | [main] 2 | 3 | # Enables context sensitive auto-completion. If this is disabled the all 4 | # possible completions will be listed. 5 | smart_completion = True 6 | 7 | # Display the completions in several columns. (More completions will be 8 | # visible.) 9 | wider_completion_menu = False 10 | 11 | # Multi-line mode allows breaking up the sql statements into multiple lines. If 12 | # this is set to True, then the end of the statements must have a semi-colon. 13 | # If this is set to False then sql statements can't be split into multiple 14 | # lines. End of line (return) is considered as the end of the statement. 15 | multi_line = True 16 | 17 | # If multi_line_mode is set to "psql", in multi-line mode, [Enter] will execute 18 | # the current input if the input ends in a semicolon. 19 | # If multi_line_mode is set to "safe", in multi-line mode, [Enter] will always 20 | # insert a newline, and [Esc] [Enter] or [Alt]-[Enter] must be used to execute 21 | # a command. 22 | multi_line_mode = psql 23 | 24 | # Destructive warning mode will alert you before executing a sql statement 25 | # that may cause harm to the database such as "drop table", "drop database" 26 | # or "shutdown". 27 | destructive_warning = True 28 | 29 | # Enables expand mode, which is similar to `\x` in psql. 30 | expand = False 31 | 32 | # Enables auto expand mode, which is similar to `\x auto` in psql. 33 | auto_expand = False 34 | 35 | # If set to True, table suggestions will include a table alias 36 | generate_aliases = True 37 | 38 | # log_file location. 39 | # In Unix/Linux: ~/.config/pgcli/log 40 | # In Windows: %USERPROFILE%\AppData\Local\dbcli\pgcli\log 41 | # %USERPROFILE% is typically C:\Users\{username} 42 | log_file = default 43 | 44 | # keyword casing preference. Possible values "lower", "upper", "auto" 45 | keyword_casing = auto 46 | 47 | # casing_file location. 48 | # In Unix/Linux: ~/.config/pgcli/casing 49 | # In Windows: %USERPROFILE%\AppData\Local\dbcli\pgcli\casing 50 | # %USERPROFILE% is typically C:\Users\{username} 51 | casing_file = default 52 | 53 | # If generate_casing_file is set to True and there is no file in the above 54 | # location, one will be generated based on usage in SQL/PLPGSQL functions. 55 | generate_casing_file = False 56 | 57 | # Casing of column headers based on the casing_file described above 58 | case_column_headers = True 59 | 60 | # history_file location. 61 | # In Unix/Linux: ~/.config/pgcli/history 62 | # In Windows: %USERPROFILE%\AppData\Local\dbcli\pgcli\history 63 | # %USERPROFILE% is typically C:\Users\{username} 64 | history_file = default 65 | 66 | # Default log level. Possible values: "CRITICAL", "ERROR", "WARNING", "INFO" 67 | # and "DEBUG". "NONE" disables logging. 68 | log_level = ERROR 69 | 70 | # Order of columns when expanding * to column list 71 | # Possible values: "table_order" and "alphabetic" 72 | asterisk_column_order = table_order 73 | 74 | # Whether to qualify with table alias/name when suggesting columns 75 | # Possible values: "always", never" and "if_more_than_one_table" 76 | qualify_columns = if_more_than_one_table 77 | 78 | # When no schema is entered, only suggest objects in search_path 79 | search_path_filter = True 80 | 81 | # Default pager. 82 | # By default 'PAGER' environment variable is used 83 | # pager = less -SRXF 84 | 85 | # Timing of sql statments and table rendering. 86 | timing = True 87 | 88 | # Table format. Possible values: psql, plain, simple, grid, fancy_grid, pipe, 89 | # ascii, double, github, orgtbl, rst, mediawiki, html, latex, latex_booktabs, 90 | # textile, moinmoin, jira, vertical, tsv, csv. 91 | # Recommended: psql, fancy_grid and grid. 92 | table_format = double 93 | 94 | # Syntax Style. Possible values: manni, igor, xcode, vim, autumn, vs, rrt, 95 | # native, perldoc, borland, tango, emacs, friendly, monokai, paraiso-dark, 96 | # colorful, murphy, bw, pastie, paraiso-light, trac, default, fruity 97 | syntax_style = paraiso-dark 98 | 99 | # Keybindings: 100 | # When Vi mode is enabled you can use modal editing features offered by Vi in the REPL. 101 | # When Vi mode is disabled emacs keybindings such as Ctrl-A for home and Ctrl-E 102 | # for end are available in the REPL. 103 | vi = True 104 | 105 | # Error handling 106 | # When one of multiple SQL statements causes an error, choose to either 107 | # continue executing the remaining statements, or stopping 108 | # Possible values "STOP" or "RESUME" 109 | on_error = STOP 110 | 111 | # Set threshold for row limit. Use 0 to disable limiting. 112 | row_limit = 0 113 | 114 | # Skip intro on startup and goodbye on exit 115 | less_chatty = True 116 | 117 | # Postgres prompt 118 | # \t - Current date and time 119 | # \u - Username 120 | # \h - Short hostname of the server (up to first '.') 121 | # \H - Hostname of the server 122 | # \d - Database name 123 | # \p - Database port 124 | # \i - Postgres PID 125 | # \# - "@" sign if logged in as superuser, '>' in other case 126 | # \n - Newline 127 | # \dsn_alias - name of dsn alias if -D option is used (empty otherwise) 128 | prompt = "\d> " 129 | 130 | # Number of lines to reserve for the suggestion menu 131 | min_num_menu_lines = 4 132 | 133 | # Character used to left pad multi-line queries to match the prompt size. 134 | multiline_continuation_char = "" 135 | 136 | # The string used in place of a null value. 137 | null_string = 138 | 139 | # manage pager on startup 140 | enable_pager = True 141 | 142 | # Use keyring to automatically save and load password in a secure manner 143 | keyring = False 144 | 145 | [colors] 146 | completion-menu.completion.current = "bg:#ffffff #000000" 147 | completion-menu.completion = "bg:#008888 #ffffff" 148 | completion-menu.meta.completion.current = "bg:#44aaaa #000000" 149 | completion-menu.meta.completion = "bg:#448888 #ffffff" 150 | completion-menu.multi-column-meta = "bg:#aaffff #000000" 151 | scrollbar.arrow = "bg:#003333" 152 | scrollbar = "bg:#00aaaa" 153 | selected = "#ffffff bg:#6666aa" 154 | search = "#ffffff bg:#4444aa" 155 | search.current = "#ffffff bg:#44aa44" 156 | bottom-toolbar = "bg:#222222 #aaaaaa" 157 | bottom-toolbar.off = "bg:#222222 #888888" 158 | bottom-toolbar.on = "bg:#222222 #ffffff" 159 | search-toolbar = noinherit bold 160 | search-toolbar.text = nobold 161 | system-toolbar = noinherit bold 162 | arg-toolbar = noinherit bold 163 | arg-toolbar.text = nobold 164 | bottom-toolbar.transaction.valid = "bg:#222222 #00ff5f bold" 165 | bottom-toolbar.transaction.failed = "bg:#222222 #ff005f bold" 166 | 167 | # style classes for colored table output 168 | output.header = "#00ff5f bold" 169 | output.odd-row = "" 170 | output.even-row = "" 171 | 172 | [named queries] 173 | table_sizes = '''SELECT nspname, relname, pg_size_pretty(pg_relation_size(c.oid)) as "size" 174 | FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace 175 | WHERE nspname NOT IN ('pg_catalog','information_schema') 176 | ORDER BY pg_relation_size(c.oid) 177 | DESC LIMIT 30''' 178 | list_triggers = '''SELECT tgname FROM pg_trigger 179 | WHERE tgrelid = '$1'::regclass''' 180 | list_locks = '''SELECT pid, state, usename, query, query_start 181 | FROM pg_stat_activity 182 | WHERE pid IN ( 183 | SELECT pid FROM pg_locks l 184 | JOIN pg_class t ON l.relation = t.oid AND t.relkind = 'r' 185 | WHERE t.relname = '$1' 186 | )''' 187 | 188 | [data_formats] 189 | decimal = "" 190 | float = "" 191 | -------------------------------------------------------------------------------- /profiles/graphical/hyprland/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | pkgs, 4 | lib, 5 | ... 6 | }: 7 | 8 | let 9 | palette = config.home-manager.users."${config.vars.username}".colorScheme.palette; 10 | in 11 | { 12 | environment.defaultPackages = with pkgs; [ 13 | app2unit 14 | grim 15 | hyprpicker 16 | playerctl 17 | slurp 18 | swappy 19 | wdisplays 20 | wev 21 | wf-recorder 22 | wl-clipboard 23 | # Custom scripts 24 | soundcards 25 | backlight 26 | ]; 27 | 28 | programs.hyprland = { 29 | enable = true; 30 | withUWSM = true; 31 | xwayland.enable = false; 32 | }; 33 | 34 | home-manager.users."${config.vars.username}" = { 35 | services.hyprsunset = { 36 | enable = true; 37 | }; 38 | 39 | services.hyprpolkitagent = { 40 | enable = true; 41 | }; 42 | 43 | wayland.windowManager.hyprland = { 44 | enable = true; 45 | package = config.programs.hyprland.package; 46 | portalPackage = config.programs.hyprland.portalPackage; 47 | settings = { 48 | ecosystem = { 49 | no_update_news = true; 50 | no_donation_nag = true; 51 | }; 52 | misc = { 53 | disable_hyprland_logo = true; 54 | disable_splash_rendering = true; 55 | mouse_move_enables_dpms = true; 56 | key_press_enables_dpms = false; 57 | new_window_takes_over_fullscreen = 1; 58 | exit_window_retains_fullscreen = true; 59 | focus_on_activate = true; 60 | }; 61 | binds = { 62 | movefocus_cycles_fullscreen = true; 63 | allow_pin_fullscreen = true; # necessary for fullscreening Picture-in-Picture 64 | hide_special_on_workspace_change = true; 65 | }; 66 | xwayland = { 67 | enabled = config.programs.hyprland.xwayland.enable; 68 | force_zero_scaling = true; 69 | }; 70 | 71 | # Input configuration 72 | input = { 73 | kb_layout = "custom-us"; 74 | kb_options = "caps:escape"; 75 | repeat_rate = 30; 76 | repeat_delay = 200; 77 | touchpad = { 78 | natural_scroll = true; 79 | disable_while_typing = true; 80 | tap-to-click = true; 81 | }; 82 | }; 83 | 84 | # General settings 85 | general = { 86 | gaps_in = 10; 87 | gaps_out = 40; 88 | gaps_workspaces = 20; 89 | border_size = 1; 90 | "col.active_border" = "rgba(ffffffff)"; 91 | "col.inactive_border" = "rgba(595959aa)"; 92 | layout = "dwindle"; 93 | }; 94 | 95 | # Decoration settings 96 | decoration = { 97 | rounding = 10; 98 | blur = { 99 | enabled = true; 100 | xray = false; 101 | special = false; 102 | ignore_opacity = true; # Allows opacity blurring 103 | new_optimizations = true; 104 | popups = true; 105 | input_methods = true; 106 | size = 8; 107 | passes = 2; 108 | }; 109 | 110 | shadow = { 111 | enabled = true; 112 | range = 20; 113 | render_power = 3; 114 | }; 115 | }; 116 | 117 | # Animation settings 118 | animations.enabled = true; 119 | animation = [ 120 | "layersIn, 1, 5, emphasizedDecel, slide" 121 | "layersOut, 1, 4, emphasizedAccel, slide" 122 | "fadeLayers, 1, 5, standard" 123 | 124 | "windowsIn, 1, 5, emphasizedDecel" 125 | "windowsOut, 1, 3, emphasizedAccel" 126 | "windowsMove, 1, 6, standard" 127 | "workspaces, 1, 5, standard" 128 | 129 | "specialWorkspace, 1, 4, specialWorkSwitch, slidefadevert 15%" 130 | 131 | "fade, 1, 6, standard" 132 | "fadeDim, 1, 6, standard" 133 | "border, 1, 6, standard" 134 | ]; 135 | 136 | # Animation curves 137 | bezier = [ 138 | "specialWorkSwitch, 0.05, 0.7, 0.1, 1" 139 | "emphasizedAccel, 0.3, 0, 0.8, 0.15" 140 | "emphasizedDecel, 0.05, 0.7, 0.1, 1" 141 | "standard, 0.2, 0, 0, 1" 142 | ]; 143 | 144 | # Layout settings 145 | dwindle = { 146 | pseudotile = true; 147 | force_split = 2; 148 | preserve_split = true; 149 | }; 150 | 151 | master = { 152 | smart_resizing = false; 153 | }; 154 | 155 | workspace = [ 156 | "1, defaultName:a, persistent:true, default:true" 157 | "2, defaultName:s, persistent:true" 158 | "3, defaultName:d, persistent:true" 159 | "4, defaultName:f, persistent:true" 160 | "5, defaultName:u, persistent:true" 161 | "6, defaultName:i, persistent:true" 162 | "7, defaultName:o, persistent:true" 163 | "8, defaultName:p, persistent:true" 164 | "w[tv1]s[false], gapsout:20" 165 | "f[1]s[false], gapsout:20" 166 | "special:communication, on-created-empty:app2unit -- ${pkgs.beeper}/bin/beeper" 167 | "special:music, on-created-empty:app2unit -- spotify" 168 | ]; 169 | 170 | # Window rules 171 | windowrule = [ 172 | "bordercolor rgb(${palette.base0E}), fullscreen:1" 173 | ] 174 | ++ import ./windowrules.nix; 175 | 176 | # Layer rules 177 | layerrule = [ 178 | "animation fade, hyprpicker" # Colour picker out animation 179 | "animation fade, logout_dialog" # wlogout 180 | "animation fade, selection" # slurp 181 | "animation fade, wayfreeze" 182 | 183 | # Fuzzel 184 | "animation popin 80%, launcher" 185 | "blur, launcher" 186 | 187 | # Shell 188 | "noanim, caelestia-(border-exclusion|area-picker)" 189 | "animation fade, caelestia-(drawers|background)" 190 | 191 | "blur, caelestia-drawers" 192 | "ignorealpha 0.57, caelestia-drawers" 193 | ]; 194 | } 195 | // (import ./keybindings.nix { inherit config pkgs; }); 196 | 197 | # Submaps configuration 198 | extraConfig = '' 199 | # Window submap 200 | submap = window 201 | bind = , p, pin 202 | bind = , p, submap, reset 203 | bind = , f, togglefloating 204 | bind = , f, submap, reset 205 | bind = , Return, submap, reset 206 | bind = , Escape, submap, reset 207 | submap = reset 208 | ''; 209 | }; 210 | 211 | systemd.user.sessionVariables = { 212 | XDG_CURRENT_DESKTOP = "Hyprland"; 213 | XDG_SESSION_DESKTOP = "Hyprland"; 214 | XDG_SESSION_TYPE = "wayland"; 215 | 216 | QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; 217 | QT_AUTO_SCREEN_SCALE_FACTOR = "1"; 218 | 219 | GDK_BACKEND = "wayland,x11"; 220 | QT_QPA_PLATFORM = "wayland;xcb"; 221 | SDL_VIDEODRIVER = "wayland,x11"; 222 | CLUTTER_BACKEND = "wayland"; 223 | ELECTRON_OZONE_PLATFORM_HINT = "auto"; 224 | 225 | NIXOS_OZONE_WL = "1"; 226 | _JAVA_AWT_WM_NONREPARENTING = "1"; 227 | }; 228 | 229 | home.file.".xkb/symbols/custom-us".text = '' 230 | default partial alphanumeric_keys 231 | xkb_symbols "custom-altgr-intl" { 232 | include "us(basic)" 233 | include "level3(ralt_switch)" 234 | name[Group1]= "English (US, custom algr-intl)"; 235 | key { [ grave, asciitilde, egrave, dead_grave ] }; 236 | key { [ c, C, ccedilla, Ccedilla ] }; 237 | key { [ comma, less, dead_cedilla, guillemotleft ] }; 238 | key { [ period, greater, dead_abovedot, guillemotright ] }; 239 | key { [ a, A, agrave, Agrave ] }; 240 | key { [ s, S, scedilla, Scedilla ] }; 241 | key { [ g, G, gbreve, Gbreve ] }; 242 | key { [ apostrophe, quotedbl, dead_acute, dead_diaeresis ] }; 243 | key { [ e, E, eacute, Eacute ] }; 244 | key { [ u, U, ugrave, Ugrave ] }; 245 | key { [ i, I, idotless, Iabovedot ] }; 246 | key { [ o, O, odiaeresis, Odiaeresis ] }; 247 | key { [ bracketleft, braceleft, oe, OE ] }; 248 | key { [ bracketright, braceright, ae, AE ] }; 249 | key { [ 4, dollar, EuroSign ] }; 250 | key { [ 6, asciicircum, dead_circumflex ] }; 251 | }; 252 | ''; 253 | 254 | programs.fish.interactiveShellInit = lib.mkBefore '' 255 | if uwsm check may-start -q 256 | exec uwsm start hyprland-uwsm.desktop 257 | end 258 | ''; 259 | }; 260 | } 261 | -------------------------------------------------------------------------------- /profiles/graphical/zen/tridactyl_style.css: -------------------------------------------------------------------------------- 1 | :root { /* Theme inspired by the Zen browser's URL bar */ 2 | --tridactyl-font-family: "Roboto", sans-serif; 3 | --tridactyl-bg: #131211; 4 | --tridactyl-fg: #ffffff; 5 | 6 | --tridactyl-status-font-size: 12px; 7 | --tridactyl-status-font-bg: var(--tridactyl-cmdl-bg); 8 | --tridactyl-status-border: 1px solid rgba(255, 255, 255, 0.15); 9 | --tridactyl-status-border-radius: 8px; 10 | 11 | --tridactyl-header-font-size: 16px; 12 | 13 | --tridactyl-of-fg: #0d1117; 14 | --tridactyl-of-bg: #ffd662; 15 | 16 | --tridactyl-hintspan-fg: white; 17 | --tridactyl-hintspan-bg: #204e8a; 18 | 19 | --tridactyl-hint-active-fg: #333; 20 | --tridactyl-hint-active-bg: #006e51; 21 | --tridactyl-hint-active-outline: 1px solid #000; 22 | 23 | --tridactyl-hint-bg: rgba(13, 31, 54, 0.25); 24 | --tridactyl-hint-outline: 1px solid var(--tridactyl-hintspan-bg); 25 | 26 | --tridactyl-cmdl-font-size: 18px; 27 | --tridactyl-cmplt-option-height: 2.2em; 28 | 29 | --tridactyl-border-radius: 8px; 30 | 31 | /* Zen-inspired colors */ 32 | --zen-toolbar-element-bg: #131211; 33 | --zen-primary-color: #6cb6ff; 34 | --zen-colors-tertiary: #1e1d1c; 35 | --zen-element-separation: 8px; 36 | --zen-border-radius: 8px; 37 | } 38 | 39 | :root.TridactylOwnNamespace { 40 | scrollbar-width: thin; 41 | scrollbar-color: rgba(255, 255, 255, 0.3) rgba(255, 255, 255, 0.1); 42 | background: var(--tridactyl-bg) !important; 43 | } 44 | 45 | :root.TridactylOwnNamespace a { 46 | color: var(--zen-primary-color); 47 | } 48 | 49 | :root.TridactylOwnNamespace code { 50 | background-color: var(--zen-colors-tertiary); 51 | padding: 3px 7px; 52 | border-radius: 4px; 53 | } 54 | 55 | :root #command-line-holder { 56 | order: 1; 57 | } 58 | 59 | :root #command-line-holder { 60 | border-radius: var(--zen-border-radius) var(--zen-border-radius) 0 0; 61 | border: none; 62 | box-shadow: 0 0 40px rgba(0, 0, 0, 0.8); 63 | background: var(--zen-toolbar-element-bg) !important; 64 | backdrop-filter: blur(10px); 65 | } 66 | 67 | :root #tridactyl-input { 68 | border: none; 69 | } 70 | 71 | :root #tridactyl-colon::before { 72 | content: ""; 73 | } 74 | 75 | :root #tridactyl-input { 76 | width: 96%; 77 | padding: 1rem; 78 | background: var(--zen-toolbar-element-bg) !important; 79 | color: var(--tridactyl-fg); 80 | font-family: var(--tridactyl-font-family); 81 | font-size: 18px; 82 | } 83 | 84 | :root #completions table { 85 | font-weight: 350; 86 | table-layout: fixed; 87 | padding: 0.5rem; 88 | padding-top: 0; 89 | background: var(--zen-toolbar-element-bg) !important; 90 | font-size: 16px; 91 | } 92 | 93 | :root #completions > div { 94 | max-height: calc(12 * var(--tridactyl-cmplt-option-height)); 95 | min-height: calc(6 * var(--tridactyl-cmplt-option-height)); 96 | } 97 | 98 | :root #completions { 99 | border: none !important; 100 | font-family: var(--tridactyl-font-family); 101 | font-size: 16px; 102 | order: 2; 103 | border-radius: 0 0 var(--zen-border-radius) var(--zen-border-radius); 104 | background: color-mix( 105 | in srgb, 106 | var(--zen-toolbar-element-bg) 90%, 107 | transparent 10% 108 | ) !important; 109 | box-shadow: 0 0 40px rgba(0, 0, 0, 0.8); 110 | color: var(--tridactyl-fg) !important; 111 | position: relative; 112 | backdrop-filter: blur(10px); 113 | } 114 | 115 | :root #completions::before { 116 | content: ""; 117 | position: absolute; 118 | top: 0; 119 | left: 1rem; 120 | right: 1rem; 121 | height: 1px; 122 | background: rgba(255, 255, 255, 0.25); 123 | z-index: 1; 124 | margin-bottom: 0.5rem; 125 | } 126 | 127 | :root #completions > div { 128 | padding-top: 1rem; 129 | } 130 | 131 | :root #completions .HistoryCompletionSource table, 132 | :root #completions .BmarkCompletionSource table { 133 | width: 100%; 134 | border-spacing: 0; 135 | table-layout: fixed; 136 | background: transparent !important; 137 | } 138 | 139 | :root #completions .BufferCompletionSource table { 140 | width: unset; 141 | font-size: unset; 142 | border-spacing: unset; 143 | table-layout: unset; 144 | background: transparent !important; 145 | } 146 | 147 | :root #completions table tr .title { 148 | width: 50% !important; 149 | padding-right: 1rem !important; 150 | overflow: hidden; 151 | text-overflow: ellipsis; 152 | white-space: nowrap; 153 | box-sizing: border-box; 154 | font-weight: bold !important; 155 | } 156 | 157 | :root #completions tr .documentation { 158 | white-space: nowrap; 159 | overflow: hidden; 160 | text-overflow: ellipsis; 161 | width: 50% !important; 162 | padding-left: 1rem !important; 163 | box-sizing: border-box; 164 | } 165 | 166 | :root #completions .sectionHeader { 167 | display: none !important; 168 | } 169 | 170 | :root #completions .optionContainer { 171 | color: rgba(255, 255, 255, 0.6) !important; 172 | transition: background-color 0.15s ease; 173 | font-size: 16px; 174 | font-weight: 500; 175 | padding-left: 0.5rem !important; 176 | margin-bottom: 0.3rem !important; 177 | } 178 | 179 | :root #completions .optionContainer:hover { 180 | background-color: transparent !important; 181 | } 182 | 183 | /* Disable all hover effects on completion elements */ 184 | :root #completions tr:hover, 185 | :root #completions tr:hover td, 186 | :root #completions td:hover, 187 | :root #completions .option:hover, 188 | :root #completions .optionContainer:hover, 189 | :root #completions .optionContainer:hover *, 190 | :root #completions table:hover, 191 | :root #completions table tr:hover { 192 | background-color: transparent !important; 193 | color: rgba(255, 255, 255, 0.6) !important; 194 | } 195 | 196 | /* Specifically prevent text dimming on hover */ 197 | :root #completions .optionContainer:hover, 198 | :root #completions .optionContainer:hover .title, 199 | :root #completions .optionContainer:hover .documentation, 200 | :root #completions .optionContainer:hover .url, 201 | :root #completions tr:hover, 202 | :root #completions tr:hover td, 203 | :root #completions tr:hover .title, 204 | :root #completions tr:hover .documentation, 205 | :root #completions tr:hover .url { 206 | color: rgba(255, 255, 255, 0.6) !important; 207 | } 208 | 209 | :root #completions .optionContainer:hover *, 210 | :root #completions tr:hover *, 211 | :root #completions td:hover, 212 | :root #completions td:hover * { 213 | color: rgba(255, 255, 255, 0.6) !important; 214 | } 215 | 216 | /* Ensure hover doesn't override selection state */ 217 | :root #completions .optionContainer[data-selected]:hover, 218 | :root #completions .optionContainer.focused:hover, 219 | :root #completions .optionContainer.selected:hover, 220 | :root #completions tr.focused:hover, 221 | :root #completions tr[data-focused]:hover, 222 | :root #completions tr[selected]:hover { 223 | background-color: #3f4070 !important; 224 | color: #ffffff !important; 225 | } 226 | 227 | :root #completions .optionContainer[data-selected], 228 | :root #completions .optionContainer.focused { 229 | background-color: #3f4070 !important; 230 | color: #ffffff !important; 231 | } 232 | 233 | :root #completions .optionContainer.selected { 234 | background-color: #3f4070 !important; 235 | color: #ffffff !important; 236 | } 237 | 238 | :root #completions .url { 239 | color: rgba(255, 255, 255, 0.8) !important; 240 | } 241 | 242 | :root #completions .optionContainer[data-selected] .url, 243 | :root #completions .optionContainer.focused .url, 244 | :root #completions .optionContainer.selected .url { 245 | color: #ffffff !important; 246 | } 247 | 248 | /* More comprehensive selection styling */ 249 | :root #completions tr.focused, 250 | :root #completions tr[data-focused], 251 | :root #completions tr[selected], 252 | :root #completions .focused, 253 | :root #completions [data-focused] { 254 | background-color: #3f4070 !important; 255 | color: #ffffff !important; 256 | } 257 | 258 | :root #completions tr.focused td, 259 | :root #completions tr[data-focused] td, 260 | :root #completions tr[selected] td, 261 | :root #completions .focused td, 262 | :root #completions [data-focused] td { 263 | background-color: #3f4070 !important; 264 | color: #ffffff !important; 265 | } 266 | 267 | /* Override the default focused/selected colors */ 268 | :root #completions .option:focus, 269 | :root #completions .option[data-focused], 270 | :root #completions .option.focused { 271 | background-color: #3f4070 !important; 272 | } 273 | 274 | /* Ensure all elements have proper dark backgrounds */ 275 | :root #completions td { 276 | background: transparent !important; 277 | color: rgba(255, 255, 255, 0.6) !important; 278 | font-weight: 500; 279 | padding: 0.4rem 0.2rem !important; 280 | } 281 | 282 | :root #completions tr { 283 | background: transparent !important; 284 | margin-bottom: 0.3rem !important; 285 | } 286 | 287 | :root #completions .optionContainer td { 288 | background: transparent !important; 289 | border: none !important; 290 | padding: 0.4rem 0.2rem !important; 291 | } 292 | 293 | :root #cmdline_iframe { 294 | position: fixed !important; 295 | bottom: unset; 296 | top: 25% !important; 297 | left: 10% !important; 298 | z-index: 2147483647 !important; 299 | width: 80% !important; 300 | filter: drop-shadow(0px 0px 50px rgba(0, 0, 0, 0.9)) !important; 301 | color-scheme: only dark; 302 | background: rgba(19, 18, 17, 0.95) !important; 303 | border-radius: var(--zen-border-radius) !important; 304 | backdrop-filter: blur(15px) saturate(180%); 305 | } 306 | 307 | :root #completions * { 308 | background-color: transparent !important; 309 | } 310 | 311 | :root #completions, 312 | :root #completions table, 313 | :root #completions > div { 314 | background-color: var(--zen-toolbar-element-bg) !important; 315 | } 316 | 317 | :root #command-line-holder * { 318 | background-color: transparent !important; 319 | } 320 | 321 | :root .TridactylStatusIndicator { 322 | position: fixed !important; 323 | bottom: 10px !important; 324 | right: 10px !important; 325 | font-weight: 200 !important; 326 | padding: 8px 12px !important; 327 | background: rgba(15, 14, 13, 0.9) !important; 328 | border: none !important; 329 | border-radius: var(--zen-border-radius) !important; 330 | color: var(--tridactyl-fg) !important; 331 | box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6) !important; 332 | backdrop-filter: blur(10px); 333 | } 334 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "sweenu's hosts' setup"; 3 | 4 | inputs = { 5 | nixos.url = "github:nixos/nixpkgs/nixos-unstable"; 6 | nixos-hardware.url = "github:nixos/nixos-hardware/master"; 7 | 8 | flake-parts.url = "github:hercules-ci/flake-parts"; 9 | 10 | haumea = { 11 | url = "github:nix-community/haumea/v0.2.2"; 12 | inputs.nixpkgs.follows = "nixos"; 13 | }; 14 | 15 | home = { 16 | url = "github:nix-community/home-manager"; 17 | inputs.nixpkgs.follows = "nixos"; 18 | }; 19 | 20 | treefmt-nix.url = "github:numtide/treefmt-nix"; 21 | 22 | agenix = { 23 | url = "github:ryantm/agenix"; 24 | inputs.nixpkgs.follows = "nixos"; 25 | }; 26 | 27 | deploy = { 28 | url = "github:serokell/deploy-rs"; 29 | inputs.nixpkgs.follows = "nixos"; 30 | }; 31 | 32 | nixos-generators = { 33 | url = "github:nix-community/nixos-generators"; 34 | inputs.nixpkgs.follows = "nixos"; 35 | }; 36 | 37 | nixos-anywhere = { 38 | url = "github:nix-community/nixos-anywhere"; 39 | inputs = { 40 | nixpkgs.follows = "nixos"; 41 | disko.follows = "disko"; 42 | }; 43 | }; 44 | 45 | disko = { 46 | url = "github:nix-community/disko"; 47 | inputs.nixpkgs.follows = "nixos"; 48 | }; 49 | 50 | arion = { 51 | url = "github:hercules-ci/arion"; 52 | inputs.nixpkgs.follows = "nixos"; 53 | }; 54 | 55 | nix-colors = { 56 | url = "github:misterio77/nix-colors"; 57 | }; 58 | 59 | caelestia-shell = { 60 | url = "github:caelestia-dots/shell/pull/915/head"; 61 | inputs.nixpkgs.follows = "nixos"; 62 | }; 63 | 64 | zen-browser = { 65 | url = "github:0xc000022070/zen-browser-flake"; 66 | inputs.nixpkgs.follows = "nixos"; 67 | }; 68 | 69 | otbr.url = "github:NixOS/nixpkgs/pull/332296/head"; 70 | 71 | restic.url = "github:NixOS/nixpkgs/pull/446825/head"; 72 | 73 | hyprland.url = "github:hyprwm/Hyprland/v0.52.2"; 74 | 75 | dawarich.url = "github:NixOS/nixpkgs/pull/423867/head"; 76 | 77 | nix-minecraft = { 78 | url = "github:sweenu/nix-minecraft"; 79 | inputs.nixpkgs.follows = "nixos"; 80 | }; 81 | }; 82 | 83 | outputs = 84 | inputs@{ 85 | self, 86 | nixos, 87 | flake-parts, 88 | haumea, 89 | ... 90 | }: 91 | flake-parts.lib.mkFlake { inherit inputs; } { 92 | systems = [ 93 | "x86_64-linux" 94 | "aarch64-linux" 95 | ]; 96 | 97 | imports = [ 98 | inputs.treefmt-nix.flakeModule 99 | ]; 100 | 101 | perSystem = 102 | { 103 | config, 104 | self', 105 | inputs', 106 | pkgs, 107 | system, 108 | ... 109 | }: 110 | { 111 | _module.args.pkgs = import inputs.nixos { 112 | inherit system; 113 | config.allowUnfree = true; 114 | }; 115 | 116 | treefmt = { 117 | programs = { 118 | nixfmt = { 119 | enable = true; 120 | excludes = [ "secrets/secrets.nix" ]; 121 | }; 122 | }; 123 | }; 124 | 125 | devShells.default = import ./shell.nix { inherit config pkgs inputs'; }; 126 | }; 127 | 128 | flake = 129 | let 130 | inherit (nixos) lib; 131 | 132 | rakeLeaves = 133 | path: 134 | let 135 | loaded = haumea.lib.load { 136 | src = path; 137 | loader = haumea.lib.loaders.path; 138 | }; 139 | liftDefaults = 140 | tree: 141 | lib.mapAttrs ( 142 | name: value: 143 | if lib.isAttrs value then 144 | if value ? default && lib.isPath value.default then value.default else liftDefaults value 145 | else 146 | value 147 | ) tree; 148 | in 149 | liftDefaults loaded; 150 | 151 | profiles = rakeLeaves ./profiles; 152 | 153 | overlaysFromDir = 154 | path: 155 | let 156 | content = builtins.readDir path; 157 | overlayFiles = lib.filterAttrs ( 158 | name: type: type == "regular" && lib.hasSuffix ".nix" name && name != "default.nix" 159 | ) content; 160 | in 161 | map (name: import (path + "/${name}")) (builtins.attrNames overlayFiles); 162 | 163 | importModules = 164 | path: 165 | let 166 | entries = lib.filesystem.listFilesRecursive path; 167 | nixFiles = builtins.filter (path: lib.hasSuffix ".nix" path) entries; 168 | in 169 | map import nixFiles; 170 | 171 | importHosts = 172 | path: 173 | let 174 | hostDirs = builtins.readDir path; 175 | validHosts = lib.filterAttrs ( 176 | name: type: type == "directory" && builtins.pathExists (path + "/${name}/default.nix") 177 | ) hostDirs; 178 | in 179 | lib.mapAttrs (name: _: import (path + "/${name}")) validHosts; 180 | 181 | hosts = importHosts ./hosts; 182 | customModules = importModules ./modules; 183 | hmModules = importModules ./hm-modules; 184 | 185 | pkgsOverlay = import ./pkgs/default.nix; 186 | 187 | overlays = [ 188 | pkgsOverlay 189 | inputs.agenix.overlays.default 190 | inputs.deploy.overlays.default 191 | inputs.nix-minecraft.overlay 192 | (final: prev: { 193 | hyprland = inputs.hyprland.packages.${prev.stdenv.hostPlatform.system}.hyprland; 194 | xdg-desktop-portal-hyprland = 195 | inputs.hyprland.packages.${prev.stdenv.hostPlatform.system}.xdg-desktop-portal-hyprland; 196 | dawarich = inputs.dawarich.legacyPackages.${prev.stdenv.hostPlatform.system}.dawarich; 197 | }) 198 | ] 199 | ++ (overlaysFromDir ./overlays); 200 | 201 | extendedLib = lib.extend (final: prev: import ./lib.nix); 202 | 203 | commonModules = [ 204 | inputs.agenix.nixosModules.age 205 | inputs.home.nixosModules.home-manager 206 | inputs.disko.nixosModules.disko 207 | inputs.arion.nixosModules.arion 208 | inputs.nix-minecraft.nixosModules.minecraft-servers 209 | "${inputs.otbr}/nixos/modules/services/home-automation/openthread-border-router.nix" 210 | "${inputs.restic}/nixos/modules/services/backup/restic.nix" 211 | "${inputs.dawarich}/nixos/modules/services/web-apps/dawarich.nix" 212 | { 213 | disabledModules = [ 214 | "services/backup/restic.nix" 215 | ]; 216 | } 217 | { 218 | nixpkgs.overlays = overlays; 219 | nixpkgs.config.allowUnfree = true; 220 | } 221 | { 222 | home-manager.useGlobalPkgs = true; 223 | home-manager.useUserPackages = true; 224 | home-manager.sharedModules = hmModules ++ [ 225 | inputs.nix-colors.homeManagerModules.default 226 | inputs.caelestia-shell.homeManagerModules.default 227 | inputs.zen-browser.homeModules.twilight 228 | ]; 229 | } 230 | ] 231 | ++ customModules; 232 | 233 | suites = 234 | with builtins; 235 | let 236 | explodeAttrs = set: map (a: getAttr a set) (attrNames set); 237 | in 238 | rec { 239 | base = (explodeAttrs profiles.core) ++ [ profiles.vars ]; 240 | server = [ 241 | profiles.server 242 | profiles.vars 243 | ]; 244 | desktop = 245 | base 246 | ++ [ 247 | profiles.audio 248 | profiles.virt-manager 249 | ] 250 | ++ (explodeAttrs profiles.graphical) 251 | ++ (explodeAttrs profiles.pc) 252 | ++ (explodeAttrs profiles.hardware) 253 | ++ (explodeAttrs profiles.develop); 254 | laptop = desktop ++ [ profiles.laptop ]; 255 | }; 256 | 257 | mkHost = 258 | { 259 | hostname, 260 | system ? "x86_64-linux", 261 | extraModules ? [ ], 262 | }: 263 | extendedLib.nixosSystem { 264 | inherit system; 265 | specialArgs = { 266 | inherit 267 | self 268 | inputs 269 | suites 270 | profiles 271 | ; 272 | lib = extendedLib; 273 | nix-colors = inputs.nix-colors; 274 | }; 275 | modules = 276 | commonModules 277 | ++ [ 278 | { networking.hostName = hostname; } 279 | hosts.${hostname} 280 | ] 281 | ++ extraModules; 282 | }; 283 | 284 | in 285 | { 286 | nixosConfigurations = { 287 | carokann = mkHost { 288 | hostname = "carokann"; 289 | extraModules = [ inputs.nixos-hardware.nixosModules.framework-amd-ai-300-series ]; 290 | }; 291 | 292 | najdorf = mkHost { 293 | hostname = "najdorf"; 294 | }; 295 | 296 | grunfeld = mkHost { 297 | hostname = "grunfeld"; 298 | system = "aarch64-linux"; 299 | extraModules = [ inputs.nixos-hardware.nixosModules.raspberry-pi-3 ]; 300 | }; 301 | }; 302 | 303 | homeConfigurations = 304 | let 305 | mkHomeConfigs = 306 | name: config: 307 | let 308 | hmUsers = config.config.home-manager.users or { }; 309 | in 310 | lib.mapAttrs' (user: cfg: lib.nameValuePair "${user}@${name}" cfg.home) hmUsers; 311 | in 312 | lib.foldl' (acc: name: acc // (mkHomeConfigs name self.nixosConfigurations.${name})) { } ( 313 | builtins.attrNames self.nixosConfigurations 314 | ); 315 | 316 | deploy.nodes = { 317 | najdorf = { 318 | hostname = "najdorf"; 319 | remoteBuild = true; 320 | fastConnection = true; 321 | profilesOrder = [ 322 | "system" 323 | "sweenu" 324 | ]; 325 | profiles = { 326 | system = { 327 | sshUser = "root"; 328 | path = inputs.deploy.lib.x86_64-linux.activate.nixos self.nixosConfigurations.najdorf; 329 | user = "root"; 330 | }; 331 | sweenu = { 332 | user = "sweenu"; 333 | sshUser = "root"; 334 | path = 335 | inputs.deploy.lib.x86_64-linux.activate.home-manager 336 | self.homeConfigurations."sweenu@najdorf"; 337 | }; 338 | }; 339 | }; 340 | 341 | grunfeld = { 342 | hostname = "grunfeld"; 343 | profiles = { 344 | system = { 345 | sshUser = "root"; 346 | path = inputs.deploy.lib.aarch64-linux.activate.nixos self.nixosConfigurations.grunfeld; 347 | user = "root"; 348 | }; 349 | }; 350 | }; 351 | }; 352 | 353 | checks = builtins.mapAttrs ( 354 | system: deployLib: deployLib.deployChecks self.deploy 355 | ) inputs.deploy.lib; 356 | }; 357 | }; 358 | } 359 | --------------------------------------------------------------------------------