├── .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 | 
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 |
--------------------------------------------------------------------------------