Compare commits

..

3 Commits

132 changed files with 2462 additions and 5053 deletions

4
.gitignore vendored
View File

@ -1,7 +1,3 @@
result result
result/ result/
.aider*
.env
**/.claude/settings.local.json

View File

@ -1,15 +0,0 @@
# Nix Flake README
This Nix Flake defines the NixOS configurations and Home-Manager configurations for my personal systems.
## NixOS Configurations
- `architect`
- `gAluminum`
## Home-Manager Configurations
- `giulioMac`
- `giulioAarch`
- `giulioX64`
- `giulioX64NoSSH`

View File

@ -9,5 +9,6 @@ let
imports = lib.mapAttrsToList toImport (lib.filterAttrs filterCaches (builtins.readDir folder)); imports = lib.mapAttrsToList toImport (lib.filterAttrs filterCaches (builtins.readDir folder));
in { in {
inherit imports; inherit imports;
nix.settings.substituters = ["https://cache.nixos.org/"]; nix.binaryCaches = ["https://cache.nixos.org/"];
} }

View File

@ -1,13 +0,0 @@
{
nix = {
settings = {
substituters = [
"https://nix-community.cachix.org"
];
trusted-public-keys = [
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
};
};
}

12
cachix/ropfuscator.nix Normal file
View File

@ -0,0 +1,12 @@
{
nix = {
binaryCaches = [
"https://ropfuscator.cachix.org"
];
binaryCachePublicKeys = [
"ropfuscator.cachix.org-1:LZ03aJ1yqFlxpU+wfGhLlOkA3MwXqnntd2Wk7u2LnHQ="
];
};
}

317
flake.lock generated
View File

@ -1,348 +1,63 @@
{ {
"nodes": { "nodes": {
"agenix-flake": {
"inputs": {
"darwin": "darwin",
"home-manager": "home-manager",
"nixpkgs": "nixpkgs",
"systems": "systems"
},
"locked": {
"lastModified": 1754433428,
"narHash": "sha256-NA/FT2hVhKDftbHSwVnoRTFhes62+7dxZbxj5Gxvghs=",
"owner": "ryantm",
"repo": "agenix",
"rev": "9edb1787864c4f59ae5074ad498b6272b3ec308d",
"type": "github"
},
"original": {
"owner": "ryantm",
"repo": "agenix",
"type": "github"
}
},
"darwin": {
"inputs": {
"nixpkgs": [
"agenix-flake",
"nixpkgs"
]
},
"locked": {
"lastModified": 1744478979,
"narHash": "sha256-dyN+teG9G82G+m+PX/aSAagkC+vUv0SgUw3XkPhQodQ=",
"owner": "lnl7",
"repo": "nix-darwin",
"rev": "43975d782b418ebf4969e9ccba82466728c2851b",
"type": "github"
},
"original": {
"owner": "lnl7",
"ref": "master",
"repo": "nix-darwin",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"home-manager": { "home-manager": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
"agenix-flake",
"nixpkgs" "nixpkgs"
] ]
}, },
"locked": { "locked": {
"lastModified": 1745494811, "lastModified": 1639871969,
"narHash": "sha256-YZCh2o9Ua1n9uCvrvi5pRxtuVNml8X2a03qIFfRKpFs=", "narHash": "sha256-6feWUnMygRzA9tzkrfAzpA5/NBYg75bkFxnqb1DtD7E=",
"owner": "nix-community", "owner": "rycee",
"repo": "home-manager", "repo": "home-manager",
"rev": "abfad3d2958c9e6300a883bd443512c55dfeb1be", "rev": "697cc8c68ed6a606296efbbe9614c32537078756",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nix-community", "owner": "rycee",
"ref": "release-21.11",
"repo": "home-manager", "repo": "home-manager",
"type": "github" "type": "github"
} }
}, },
"home-manager_2": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1753592768,
"narHash": "sha256-oV695RvbAE4+R9pcsT9shmp6zE/+IZe6evHWX63f2Qg=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "fc3add429f21450359369af74c2375cb34a2d204",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "release-25.05",
"repo": "home-manager",
"type": "github"
}
},
"langtool-ngrams": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1752491272,
"narHash": "sha256-lEGxYHo1Q7qBj0tiMGPAhZC0u1lBEP14UanKFRiIhhc=",
"owner": "peperunas",
"repo": "nix-languagetool-ngram",
"rev": "10725a39dc6b9ba2dcd8cfe59abe6a7e1a62b503",
"type": "github"
},
"original": {
"owner": "peperunas",
"repo": "nix-languagetool-ngram",
"type": "github"
}
},
"local-unstable": {
"locked": {
"lastModified": 0,
"narHash": "sha256-eCA4jXsPHiBkrf1sNOfQPYS2g9DoCsICVzk4ec0cEdo=",
"path": "/home/giulio/dev/nixpkgs",
"type": "path"
},
"original": {
"path": "/home/giulio/dev/nixpkgs",
"type": "path"
}
},
"nixos-unstable": { "nixos-unstable": {
"locked": { "locked": {
"lastModified": 1754214453, "lastModified": 1639699734,
"narHash": "sha256-Q/I2xJn/j1wpkGhWkQnm20nShYnG7TI99foDBpXm1SY=", "narHash": "sha256-tlX6WebGmiHb2Hmniff+ltYp+7dRfdsBxw9YczLsP60=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "5b09dc45f24cf32316283e62aec81ffee3c3e376", "rev": "03ec468b14067729a285c2c7cfa7b9434a04816c",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs",
"rev": "5b09dc45f24cf32316283e62aec81ffee3c3e376",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1754028485,
"narHash": "sha256-IiiXB3BDTi6UqzAZcf2S797hWEPCRZOwyNThJIYhUfk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "59e69648d345d6e8fef86158c555730fa12af9de",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-25.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-master": {
"locked": {
"lastModified": 1754468877,
"narHash": "sha256-4f2CMXEM6PtLcfIC5Emo4pYaj+yLizWEQAekSwy4gzQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "5b7d804f77c2e9e89ddd527ed6a5e5bf77f0a826",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "master",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1751984180,
"narHash": "sha256-LwWRsENAZJKUdD3SpLluwDmdXY9F45ZEgCb0X+xgOL0=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "9807714d6944a957c2e036f84b0ff8caf9930bc0",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable", "ref": "nixos-unstable",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"nixpkgs_3": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1754292888, "lastModified": 1639794673,
"narHash": "sha256-1ziydHSiDuSnaiPzCQh1mRFBsM2d2yRX9I+5OPGEmIE=", "narHash": "sha256-bjauV0+Z4WmxeiHXecyiEOEwo+XysO6kx36beeatbl0=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "ce01daebf8489ba97bd1609d185ea276efdeb121", "rev": "2627c4b795107ba94562626925f5a9a2bc62ebc6",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "ref": "nixos-21.11",
"rev": "ce01daebf8489ba97bd1609d185ea276efdeb121",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1735264675,
"narHash": "sha256-MgdXpeX2GuJbtlBrH9EdsUeWl/yXEubyvxM1G+yO4Ak=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "d49da4c08359e3c39c4e27c74ac7ac9b70085966",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-24.11",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"nvidia-patch": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"utils": "utils"
},
"locked": {
"lastModified": 1753078133,
"narHash": "sha256-z+cvobe/+6pSVmwVrI+/k4lt7CjsQtfhlMaAlLQcSPY=",
"owner": "icewind1991",
"repo": "nvidia-patch-nixos",
"rev": "b5bb7576a5a951cea1a46703f488ac76fa827876",
"type": "github"
},
"original": {
"owner": "icewind1991",
"repo": "nvidia-patch-nixos",
"type": "github"
}
},
"pepeflake": {
"inputs": {
"nixpkgs": "nixpkgs_4"
},
"locked": {
"lastModified": 1739457875,
"narHash": "sha256-ak7EyrCKa79mZjr98PE1lFBj3UuDDIIpaYtky1MN2a0=",
"ref": "refs/heads/master",
"rev": "249e02c5c915fed09c448092d9066257317a4d68",
"revCount": 10,
"type": "git",
"url": "https://git.giugl.io/peperunas/pepeflake"
},
"original": {
"type": "git",
"url": "https://git.giugl.io/peperunas/pepeflake"
}
},
"root": { "root": {
"inputs": { "inputs": {
"agenix-flake": "agenix-flake", "home-manager": "home-manager",
"home-manager": "home-manager_2",
"langtool-ngrams": "langtool-ngrams",
"local-unstable": "local-unstable",
"nixos-unstable": "nixos-unstable", "nixos-unstable": "nixos-unstable",
"nixpkgs": "nixpkgs_3", "nixpkgs": "nixpkgs"
"nixpkgs-master": "nixpkgs-master",
"nvidia-patch": "nvidia-patch",
"pepeflake": "pepeflake"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_3": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems_3"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
} }
} }
}, },

124
flake.nix
View File

@ -1,108 +1,62 @@
{ {
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/ce01daebf8489ba97bd1609d185ea276efdeb121"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-21.11";
nixos-unstable.url = "github:NixOS/nixpkgs/5b09dc45f24cf32316283e62aec81ffee3c3e376"; nixos-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs-master.url = "github:NixOS/nixpkgs/master";
local-unstable.url = "path:///home/giulio/dev/nixpkgs";
pepeflake.url = "git+https://git.giugl.io/peperunas/pepeflake";
agenix-flake.url = "github:ryantm/agenix";
home-manager = { home-manager = {
url = "github:nix-community/home-manager/release-25.05"; url = "github:rycee/home-manager/release-21.11";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
nvidia-patch = { navidrome.url = "github:antifuchs/nixpkgs/fix-151550";
url = "github:icewind1991/nvidia-patch-nixos";
inputs.nixpkgs.follows = "nixpkgs";
};
langtool-ngrams.url = "github:peperunas/nix-languagetool-ngram";
}; };
outputs = outputs = inputs@{ self, nixpkgs, nixos-unstable, home-manager, navidrome}:
{ self
, nixpkgs
, nixos-unstable
, nixpkgs-master
, local-unstable
, home-manager
, nvidia-patch
, agenix-flake
, pepeflake
, langtool-ngrams
}:
let let
sysLinuxX64 = "x86_64-linux"; system = "x86_64-linux";
sysDarwin = "aarch64-darwin";
sysLinuxAarch = "aarch64-linux";
forAllSystems = nixpkgs.lib.genAttrs [ sysLinuxX64 sysLinuxAarch sysDarwin ];
mkSystem = (import ./lib/systems.nix { pkgs = import nixpkgs {
inherit nixpkgs; inherit system;
inputs = { config.allowUnfree = true;
inherit nixos-unstable nixpkgs-master local-unstable overlays = [ (final: prev: { inherit unstable; }) ];
agenix-flake pepeflake langtool-ngrams };
nvidia-patch home-manager;
};
}).mkSystem;
linuxX64Cuda = mkSystem { system = sysLinuxX64; cudaSupport = true; }; unstable = import nixos-unstable {
linuxAarch = mkSystem { system = sysLinuxAarch; }; inherit system;
darwin = mkSystem { system = sysDarwin; }; config.allowUnfree = true;
in };
{
utils = import ./lib {
inherit pkgs unstable nixpkgs nixos-unstable home-manager;
};
inherit (utils) host;
inherit (utils) user;
in {
nixosConfigurations = { nixosConfigurations = {
architect = linuxX64Cuda.utils.host.mkHost { architect = host.mkHost {
name = "architect"; name = "architect";
users = [{ users = [{
user = "giulio"; user = "giulio";
roles = [ ]; roles = [ ];
}]; }];
imports = [ imports = [
agenix-flake.nixosModules.default {
disabledModules = [ "services/audio/navidrome.nix" ];
imports =
[ (navidrome + "/nixos/modules/services/audio/navidrome.nix") ];
}
]; ];
}; };
}; gAluminum = host.mkHost {
name = "gAluminum";
homeConfigurations = { users = [{
giulioMac = darwin.utils.user.mkHMUser { user = "giulio";
name = "giulio"; roles = [ "desktop" "ssh" "git" ];
roles = [ "ssh" ]; }];
roles = [ "gnome" ];
}; };
gdepasqualeMac = darwin.utils.user.mkHMUser { proxy = host.mkHost {
name = "gdepasquale"; name = "proxy";
roles = [ "ssh" ]; users = [ ];
};
giulioAarch = linuxAarch.utils.user.mkHMUser {
name = "giulio";
roles = [ "ssh" ];
};
giulioX64 = linuxX64Cuda.utils.user.mkHMUser {
name = "giulio";
roles = [ "ssh" "go" ];
};
giulioX64NoSSH = linuxX64Cuda.utils.user.mkHMUser {
name = "giulio";
roles = [ "go" ];
};
gdepasqualeX64 = linuxX64Cuda.utils.user.mkHMUser {
name = "gdepasquale";
roles = [ "ssh" "go" ];
};
};
packages = forAllSystems (system:
let pkgs = nixpkgs.legacyPackages.${system}; in rec {
default = update;
update = pkgs.callPackage ./update.nix { };
});
defaultTemplate = self.templates.basicShell;
templates = {
basicShell = {
path = ./templates/basicShell;
description = "A barebone shell with custom defined packages";
}; };
}; };
}; };

View File

@ -1,25 +1,13 @@
{ config, ... }: { config, lib, ... }:
{ {
age.secrets = {
restic-passwords = {
file = ../../secrets/restic-passwords.age;
};
restic-environment = {
file = ../../secrets/restic-environment.age;
};
};
services.restic.backups = { services.restic.backups = {
backblaze = { backblaze = {
initialize = true; initialize = true;
passwordFile = config.age.secrets.restic-passwords.path; passwordFile = "/secrets/restic/data.key";
environmentFile = config.age.secrets.restic-environment.path; environmentFile = "/secrets/restic/credentials.txt";
repository = "b2:architect:/"; repository = "b2:architect:/";
paths = [ "/var/lib" "/services" ]; paths = [ "/var/lib" "/secrets" ];
exclude = [
"/var/lib/ollama"
];
pruneOpts = [ pruneOpts = [
"--keep-daily 45" "--keep-daily 45"
"--keep-weekly 12" "--keep-weekly 12"
@ -27,8 +15,8 @@
"--keep-yearly 3" "--keep-yearly 3"
]; ];
timerConfig = { timerConfig = {
OnCalendar = "monday 09:00"; OnCalendar = "monday 00:05";
RandomizedDelaySec = "1h"; RandomizedDelaySec = "2h";
}; };
}; };
}; };

View File

@ -0,0 +1,33 @@
{ lib, ... }:
let
domain = "htbaz.giugl.io";
network = import ./network.nix;
in {
services = {
bazarr = {
enable = true;
group = "media";
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:6767";
extraConfig = ''
allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
deny all;
'';
};
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "bazarr" ];
}

View File

@ -0,0 +1,6 @@
{
programs.ccache.enable = true;
nix.extraOptions = ''
extra-sandbox-paths = /nix/var/cache/ccache
'';
}

View File

@ -1,39 +1,59 @@
{ config, pkgs, ... }: { config, pkgs, ... }:
let let
macbookPubkey = (import ../pubkeys.nix).macbook; pubkeys = [
pubkeys = [ macbookPubkey ]; "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC1we38/N+t8Ah5yrLof8QUwhrob7/VXFKIddaJeOVBLuDVnW7ljiAtdtEiL69D/DV4Ohmt5wMvkAAjfuHmim6FD9A6lzPbSU4KH9W2dcckszKbbI636kuDwem/xui6BW3wJa6P+0xW5ksygEAkzcK2PXuC2b4B9uwhuUdKahiGMKDxISG/WianqAe72cGMfNkYvion3Y1VsMLUdm48d2ABnxNpr7NI9B5iJ8dziOft9gpgfz13CCQRlReo75gk/4xI+vSNrQp7eR+wzJy2/dZg/T8jtyA9Q6jVxrxBpqQ1LNXkAKaJkGo9OabF6Wgpzp+YTAurL4nwR2NaJxwFuyoKvACQy0ai4jrS3206gC6JXZv8ktZMZrwUN+jPqCwfgh5qObFkAqKCxbp52ioDek2MQLdOvzQBX//DBhGEp5rzHGLZ3vhRIiiQiaof5sF5zWiYDW5mqezSPNxJPX/BrTP/Wbs/jpwTLBh3wytiia0S1WXQmya89bqzTPFiDWvTRA62EVKB/JaQtPQQOFAxWwg799DMycPeZ81xttZOyMtI/MZSddyqx2S8fWGwvToZQvuZ38mSIpFseLM1IkgabRIrAmat5SBNGGy9Dqa0eMEa7bwIY/4CMB1y6HMTnaoMXA6cnQfHMoB/zyTZ6oTXIeqeOyiZsK+RN0Mvahj8mXi7dw== giulio@giulio-X230"
domain = "devs.giugl.io"; ];
in hostname = "architect";
{ network = import ./network.nix;
imports = [ in {
./options.nix imports = [ # Include the results of the hardware scan.
./backup.nix ./backup.nix
./hardware.nix ./hardware.nix
./firewall.nix ./firewall.nix
./nginx.nix ./nginx.nix
./gitea.nix
./sonarr.nix
./radarr.nix
./bazarr.nix
./nzbget.nix
./nextcloud.nix ./nextcloud.nix
./wireguard.nix
./minio.nix
./matrix.nix ./matrix.nix
./fail2ban.nix ./fail2ban.nix
./dns.nix ./dns.nix
./tailscale.nix #./minecraft.nix
./sunshine.nix ./prowlarr.nix
./postgres.nix ./plex.nix
./searx.nix ./githubrunner.nix
./libreddit.nix
./invidious.nix
./nitter.nix
./ccache.nix
./lidarr.nix
./navidrome.nix
./jellyfin.nix
./prosody.nix
./deluge.nix
]; ];
age.identityPaths = [ "/root/.ssh/id_ed25519" ]; time.timeZone = "Europe/Rome";
system.stateVersion = "21.11"; # Did you read the comment?
architect = {
firewall = {
openTCP = [ 22 ];
};
};
time.timeZone = "Europe/London";
users.users.giulio.openssh.authorizedKeys.keys = pubkeys; users.users.giulio.openssh.authorizedKeys.keys = pubkeys;
services.fwupd.enable = true;
boot = { boot = {
kernelParams = [
"ip=${network.architect-lan}::10.0.0.1:255.255.255.0::${network.wan-if}:off"
"nvme_core.default_ps_max_latency_us=5500"
];
kernel.sysctl= {
"net.ipv4.ip_forward" = 1;
"fs.protected_regular" = 0;
};
initrd = { initrd = {
availableKernelModules = [ "igc" "r8169" ]; availableKernelModules = [ "igc" "r8169" ];
network = { network = {
@ -41,20 +61,22 @@ in
ssh = { ssh = {
enable = true; enable = true;
port = 22; port = 22;
hostKeys = [ /secrets/ssh_host_rsa_key ]; hostKeys = [ /boot/ssh_host_rsa_key ];
authorizedKeys = pubkeys; authorizedKeys = pubkeys;
}; };
postCommands = ''
zpool import backedpool
zpool import zpool
mkdir /mnt-root
echo "zfs load-key -ar; mount -t zfs zpool/nixos/root /mnt-root; zfs load-key -a; umount /mnt-root; rmdir /mnt-root; killall zfs" >> /root/.profile
'';
}; };
}; };
kernelParams = with config.pepe.core.network.interfaces.lan; [
"ip=${devices.architect.address}::${devices.brigettine.address}:255.255.255.0::${interface}:off"
];
kernel.sysctl = { "net.ipv4.ip_forward" = 1; };
loader = { loader = {
systemd-boot = { systemd-boot ={
enable = true; enable = true;
memtest86.enable = true; memtest86.enable = true;
}; };
@ -63,169 +85,71 @@ in
supportedFilesystems = [ "zfs" ]; supportedFilesystems = [ "zfs" ];
zfs.requestEncryptionCredentials = true; zfs.requestEncryptionCredentials = true;
tmp.tmpfsSize = "50%"; tmpOnTmpfsSize = "80%";
}; };
networking = with config.pepe.core.network.interfaces.lan; { networking = {
hostName = "architect"; hostName = hostname;
hostId = "49350853"; hostId = "49350853";
useDHCP = false; useDHCP = false;
defaultGateway = devices.brigettine.address; defaultGateway = "10.0.0.1";
interfaces = { interfaces = {
${interface}.ipv4.addresses = [{ enp5s0.ipv4.addresses = [{
address = devices.architect.address; address = network.architect-lan;
prefixLength = 24; prefixLength = 24;
}]; }];
enp6s0.useDHCP = false;
wlp4s0.useDHCP = false;
}; };
extraHosts = ''
127.0.0.1 ${hostname}.devs.giugl.io localhost
# LAN
${network.architect-lan} ${hostname}.devs.giugl.io
${network.dvr-lan} dvr.devs.giugl.io
${network.nas-lan} nas.devs.giugl.io
192.168.1.1 vodafone.station
# Blacklist
0.0.0.0 metrics.plex.tv
0.0.0.0 analytics.plex.tv
0.0.0.0 cdn.luckyorange.com
0.0.0.0 w1.luckyorange.com
0.0.0.0 browser.sentry-cdn.com
0.0.0.0 analytics.facebook.com
0.0.0.0 ads.facebook.com
0.0.0.0 extmaps-api.yandex.net
0.0.0.0 logservice.hicloud.com
0.0.0.0 logbak.hicloud.com
0.0.0.0 logservice1.hicloud.com
0.0.0.0 samsung-com.112.2o7.net
0.0.0.0 supportmetrics.apple.com
0.0.0.0 analytics.oneplus.cn
0.0.0.0 click.oneplus.cn
0.0.0.0 analytics-api.samsunghealthcn.com
'';
}; };
services = { environment.systemPackages = with pkgs; [ cudatoolkit cachix ];
fwupd.enable = true;
das_watchdog.enable = true;
zfs.autoScrub.enable = true;
hardware = {
opengl.enable = true;
opengl.extraPackages = with pkgs; [ vaapiVdpau ];
opengl.driSupport = true;
};
services.das_watchdog.enable = true;
services = {
zfs.autoScrub.enable = true;
xserver.videoDrivers = [ "nvidia" ];
openssh = { openssh = {
enable = true; enable = true;
passwordAuthentication = false;
settings = { challengeResponseAuthentication = false;
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
};
extraConfig = ''
MaxAuthTries 15
'';
}; };
smartd.enable = true; smartd.enable = true;
}; };
pepe = { environment.variables = { LIBVA_DRIVER_NAME = "vdpau"; };
core = {
media = {
enable = true;
path = "/media";
};
network.interfaces = {
lan = {
interface = "enp6s0";
type = "lan";
net = "10.0.0.0/24";
devices = {
architect = { address = "10.0.0.250"; hostname = "architect.${domain}"; isEndpoint = true; };
brigettine = { address = "10.0.0.1"; hostname = "router.${domain}"; };
dreamel10 = { address = "10.0.0.199"; hostname = "dreamel10.${domain}"; };
reolinkcamera = { address = "10.0.0.200"; hostname = "reolinkcamera.${domain}"; };
lgtv = { address = "10.0.0.202"; hostname = "lgtv.${domain}"; };
homeassistant = { address = "10.0.0.251"; hostname = "homeassistant.${domain}"; };
};
};
};
};
services = {
gitea = {
enable = true;
domain = "git.giugl.io";
};
immich = {
enable = true;
domain = "photos.giugl.io";
package = pkgs.unstablePkgs.immich;
};
radarr = {
enable = true;
domain = "htrad.giugl.io";
};
sonarr = {
enable = true;
domain = "htson.giugl.io";
};
bazarr = {
enable = true;
domain = "htbaz.giugl.io";
};
nzbget = {
enable = true;
domain = "htnzb.giugl.io";
};
jellyfin = {
enable = true;
domain = "media.giugl.io";
package = pkgs.unstablePkgs.jellyfin;
};
jellyseer = {
enable = true;
domain = "aumm-aumm.giugl.io";
};
prowlarr = {
enable = true;
domain = "htpro.giugl.io";
};
redlib = {
enable = true;
domain = "reddit.giugl.io";
package = pkgs.unstablePkgs.redlib;
settings = {
REDLIB_ROBOTS_DISABLE_INDEXING = "on";
REDLIB_DEFAULT_THEME = "dracula";
REDLIB_DEFAULT_SHOW_NSFW = "on";
REDLIB_DEFAULT_BLUR_NSFW = "off";
REDLIB_DEFAULT_USE_HLS = "on";
REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION = "on";
};
};
llm = {
enable = true;
package = pkgs.masterPkgs.ollama-cuda;
backendDomain = "ollama.giugl.io";
acceleration = "cuda";
# frontend = {
# enable = true;
# domain = "pino.giugl.io";
# package = pkgs.unstablePkgs.open-webui;
# };
};
homeassistant = {
enable = true;
domain = "home.giugl.io";
};
headscale = {
enable = true;
domain = "vipienne.giugl.io";
settings = with config.pepe.core.network.interfaces.tailscale; {
server_url = "https://${domain}";
prefixes.v4 = net;
dns = {
magic_dns = false;
override_local_dns = true;
global = [ devices.architect.address ];
nameservers.global = [ devices.architect.address ];
};
log.level = "debug";
};
};
navidrome = {
enable = true;
domain = "music.giugl.io";
package = pkgs.unstablePkgs.navidrome;
};
};
};
} }

View File

@ -0,0 +1,52 @@
{ lib, config, pkgs, ... }:
let
domain = "htdel.giugl.io";
network = import ./network.nix;
in {
services = {
deluge = {
enable = true;
group = "media";
declarative = true;
config = {
download_location = "/media/deluge";
max_upload_speed = 20;
# full-stream
enc_level = 1;
# forced
enc_in_policy = 0;
# forced
enc_out_policy = 0;
max_active_seeding = 100;
max_connections_global = 1000;
max_active_limit = 100;
max_active_downloading = 100;
listen_ports = [ 51413 51414 ];
random_port = false;
enabled_plugins = [ "Label" "Extractor" ];
};
web.enable = true;
authFile = "/secrets/deluge/auth";
extraPackages = [ pkgs.unrar ];
};
nginx.virtualHosts.${domain} = {
locations."/" = {
proxyPass = "http://localhost:8112";
extraConfig = ''
allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
deny all;
'';
};
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "deluge" ];
}

View File

@ -1,12 +1,40 @@
{ ... }: { config, pkgs, lib, ... }:
{ {
pepe.core.dns = { services = {
enable = true; dnsmasq = {
nextDNSId = "d65174"; enable = true;
extraDomains = { servers = [ "127.0.0.1#5300" ];
"architect.devs.giugl.io" = { extraConfig = ''
dnsInterfaces = [ "lan" "tailscale" ]; localise-queries
min-cache-ttl=120
max-cache-ttl=2400
'';
};
adguardhome = {
enable = true;
port = 3031;
};
dnscrypt-proxy2 = {
enable = true;
settings = {
listen_addresses = [ "127.0.0.1:5353" ];
ipv4_servers = true;
ipv6_servers = false;
block_ipv6 = true;
dnscrypt_servers = true;
doh_servers = true;
require_nolog = true;
require_nofilter = true;
timeout = 350;
lb_strategy = "p4";
lb_estimator = true;
ignore_system_dns = true;
fallback_resolvers = [ "1.1.1.1:53" "9.9.9.9:53" ];
cache_min_ttl = 450;
cache_max_ttl = 2400;
}; };
}; };
}; };

View File

@ -1,25 +0,0 @@
{ config, ... }:
{
architect.networks.docker = {
interface = "docker0";
net = "172.17.0.0/16";
};
hardware.nvidia-container-toolkit.enable = true;
virtualisation = {
oci-containers.backend = "docker";
docker = {
enable = true;
extraOptions = ''
--dns 127.0.0.1 --dns ${config.architect.networks.lan.devices.architect.address} --data-root /docker
'';
enableOnBoot = false;
daemon.settings.iptables = false;
};
};
users.users.giulio.extraGroups = [ "docker" ];
}

View File

@ -1,14 +1,9 @@
{ config, pkgs, ... }: { config, pkgs, ... }: {
{
services.fail2ban = { services.fail2ban = {
enable = true; enable = true;
package = pkgs.fail2ban; package = pkgs.fail2ban;
packageFirewall = pkgs.nftables; packageFirewall = pkgs.nftables;
bantime-increment.enable = true; bantime-increment.enable = true;
ignoreIP = [ ignoreIP = [ "10.0.0.0/24" "10.3.0.0/24" ];
config.pepe.core.network.interfaces.tailscale.net
config.pepe.core.network.interfaces.lan.net
];
}; };
} }

View File

@ -1,21 +1,33 @@
{ config, lib, ... }: { config, lib, ... }:
with import ./network.nix;
let let
firewallRules = config.pepe.core.firewall; open_tcp_ports = lib.concatMapStringsSep "," (x: toString x) [
openTCP = lib.concatMapStringsSep "," (x: toString x) firewallRules.openTCP; 22 # ssh
openUDP = lib.concatMapStringsSep "," (x: toString x) firewallRules.openUDP; 80 # http
ifaces = config.pepe.core.network.interfaces; 443 # https
8448 # matrix
deviceAddress = interface: device: 10022 # gitea
ifaces.${interface}.devices.${device}.address; 18080 # monero
51413 # transmission
gdevices = [
(deviceAddress "tailscale" "architect")
(deviceAddress "tailscale" "manduria")
(deviceAddress "tailscale" "kmerr")
]; ];
in open_udp_ports = lib.concatMapStringsSep "," (x: toString x) [
{ 1194 # wireguard
51413 # transmission
];
open_tcp_ports_vpn = lib.concatMapStringsSep "," (x: toString x) [
22
80
443
32400 # plex
];
open_udp_ports_vpn = lib.concatMapStringsSep "," (x: toString x) [
53 # dns
1194 # vpn
];
in {
networking = { networking = {
# needed to use nftables # needed to use nftables
firewall.enable = false; firewall.enable = false;
@ -23,150 +35,170 @@ in
nftables = { nftables = {
enable = true; enable = true;
ruleset = with config.pepe.core.network.interfaces; '' ruleset = ''
table ip raw { table ip raw {
chain PREROUTING { chain PREROUTING {
type filter hook prerouting priority raw; policy accept; type filter hook prerouting priority raw; policy accept;
} }
chain OUTPUT { chain OUTPUT {
type filter hook output priority raw; policy accept; type filter hook output priority raw; policy accept;
} }
} }
table ip nat { table ip nat {
chain DOCKER { chain PREROUTING {
type nat hook prerouting priority dstnat; policy accept; type nat hook prerouting priority dstnat; policy accept;
} }
chain PREROUTING {
type nat hook prerouting priority dstnat; policy accept;
}
chain INPUT { chain INPUT {
type nat hook input priority 100; policy accept; type nat hook input priority 100; policy accept;
} }
chain OUTPUT { chain OUTPUT {
type nat hook output priority -100; policy accept; type nat hook output priority -100; policy accept;
} }
chain POSTROUTING { chain POSTROUTING {
type nat hook postrouting priority srcnat; policy accept; type nat hook postrouting priority srcnat; policy accept;
oifname ${lan.interface} ip saddr ${tailscale.net} masquerade oifname ${wan-if} ip saddr {${
} lib.concatStringsSep "," towan-wg
} }} masquerade
}
}
table ip mangle { table ip mangle {
chain PREROUTING { chain PREROUTING {
type filter hook prerouting priority mangle; policy drop; type filter hook prerouting priority mangle; policy drop;
ct state invalid,untracked drop comment "drop invalid" ct state invalid,untracked drop comment "drop invalid"
ip daddr 255.255.255.255 accept comment "allow broadcast traffic" ip daddr 255.255.255.255 accept comment "allow broadcast traffic"
ip daddr 224.0.0.0/4 accept comment "allow multicast traffic" ip daddr 224.0.0.0/4 accept comment "allow multicast traffic"
iifname ${lan.interface} ip saddr 127.0.0.0/8 drop comment "bind any ip to intf ${lan.interface}" iifname ${wan-if} ip saddr ${vpn-net} drop comment "bind any ip to intf ${wan-if}"
iifname ${lan.interface} accept comment "bind any ip to intf ${lan.interface}" iifname ${wan-if} ip saddr 127.0.0.0/8 drop comment "bind any ip to intf ${wan-if}"
iifname ${tailscale.interface} ip saddr ${tailscale.net} accept iifname ${wan-if} accept comment "bind any ip to intf ${wan-if}"
iifname ${tailscale.interface} ip saddr 100.100.100.100/32 accept iifname ${proxy-if} ip saddr ${proxy-net} accept comment "bind ip ${proxy-net} to intf ${proxy-if}"
iifname "lo" accept comment "bind any ip to intf lo" iifname ${vpn-if} ip saddr ${vpn-net} accept comment "bind ip ${vpn-net} to intf ${vpn-if}"
jump mangle_drop iifname "lo" accept comment "bind any ip to intf lo"
} jump mangle_drop
}
chain INPUT { chain INPUT {
type filter hook input priority mangle; policy accept; type filter hook input priority mangle; policy accept;
} }
chain FORWARD { chain FORWARD {
type filter hook forward priority mangle; policy accept; type filter hook forward priority mangle; policy accept;
} }
chain OUTPUT { chain OUTPUT {
type route hook output priority mangle; policy accept; type route hook output priority mangle; policy accept;
} }
chain POSTROUTING { chain POSTROUTING {
type filter hook postrouting priority mangle; policy accept; type filter hook postrouting priority mangle; policy accept;
} }
chain mangle_drop { chain mangle_drop {
ip protocol icmp jump mangle_drop_icmp ip protocol icmp jump mangle_drop_icmp
ip protocol udp jump mangle_drop_udp ip protocol udp jump mangle_drop_udp
ip protocol tcp jump mangle_drop_tcp ip protocol tcp jump mangle_drop_tcp
log prefix "MANGLE-DROP-UNK " log prefix "MANGLE-DROP-UNK "
drop drop
} }
chain mangle_drop_icmp { chain mangle_drop_icmp {
log prefix "MANGLE-DROP-ICMP " log prefix "MANGLE-DROP-ICMP "
drop drop
} }
chain mangle_drop_tcp { chain mangle_drop_tcp {
log prefix "MANGLE-DROP-TCP " log prefix "MANGLE-DROP-TCP "
drop drop
} }
chain mangle_drop_udp { chain mangle_drop_udp {
log prefix "MANGLE-DROP-UDP " log prefix "MANGLE-DROP-UDP "
drop drop
} }
} }
table ip filter { table ip filter {
chain INPUT { chain INPUT {
type filter hook input priority filter; policy drop; type filter hook input priority filter; policy drop;
ct state established,related accept ct state established,related accept
iifname "lo" accept comment "loopback" iifname "lo" accept comment "loopback"
ip daddr 255.255.255.255 accept comment "allow broadcast traffic" ip daddr 255.255.255.255 accept comment "allow broadcast traffic"
ip daddr 224.0.0.0/4 accept comment "allow multicast traffic" ip daddr 224.0.0.0/4 accept comment "allow multicast traffic"
ip saddr ${lan.net} accept comment "lan > local" ip saddr ${lan-net} accept comment "lan > local"
ip saddr ${tailscale.net} accept comment "tailscale > local" ip saddr ${proxy-wg} accept comment "proxy > local"
ip saddr {${lib.concatStringsSep "," gdevices}} accept comment "vpn > local" ip saddr {${lib.concatStringsSep "," gdevices-wg}} accept comment "vpn > local"
iifname ${lan.interface} tcp dport {${openTCP}} accept iifname ${wan-if} tcp dport {${open_tcp_ports}} accept
iifname ${lan.interface} udp dport {${openUDP}} accept iifname ${wan-if} udp dport {${open_udp_ports}} accept
jump filter_drop iifname ${vpn-if} tcp dport {${open_tcp_ports_vpn}} accept
} iifname ${vpn-if} udp dport {${open_udp_ports_vpn}} accept
iifname ${vpn-if} icmp type echo-request accept
chain FORWARD { jump filter_drop
type filter hook forward priority filter; policy drop; }
ct state established,related accept
oifname ${lan.interface} ip saddr ${tailscale.net} accept chain FORWARD {
type filter hook forward priority filter; policy drop;
ct state established,related accept
jump filter_drop # client to client
} ip saddr {${lib.concatStringsSep "," c2c-wg}} ip daddr {${
lib.concatStringsSep "," c2c-wg
}} accept
chain OUTPUT { # gdevices talking to everyone in VPN
type filter hook output priority filter; policy drop; ip saddr {${
ct state established,related accept lib.concatStringsSep "," gdevices-wg
accept comment "local > *" }} ip daddr ${vpn-net} accept
jump filter_drop ip saddr {${
} lib.concatStringsSep "," gamenet-wg
}} ip daddr {${lib.concatStringsSep "," gamenet-wg}} accept
chain filter_drop { # nat to wan
ip protocol icmp jump filter_drop_icmp oifname ${wan-if} ip saddr {${
ip protocol udp jump filter_drop_udp lib.concatStringsSep "," towan-wg
ip protocol tcp jump filter_drop_tcp }} accept
log prefix "DROP-UNK "
drop
}
chain filter_drop_icmp { jump filter_drop
log prefix "DROP-icmp " }
drop
}
chain filter_drop_tcp { chain OUTPUT {
log prefix "DROP-tcp " type filter hook output priority filter; policy drop;
drop ct state established,related accept
} accept comment "local > *"
jump filter_drop
}
chain filter_drop_udp { chain filter_drop {
log prefix "DROP-udp " ip protocol icmp jump filter_drop_icmp
drop ip protocol udp jump filter_drop_udp
} ip protocol tcp jump filter_drop_tcp
} log prefix "DROP-UNK "
''; drop
}
chain filter_drop_icmp {
log prefix "DROP-icmp "
drop
}
chain filter_drop_tcp {
log prefix "DROP-tcp "
drop
}
chain filter_drop_udp {
log prefix "DROP-udp "
drop
}
}
'';
}; };
}; };
} }

37
hosts/architect/gitea.nix Normal file
View File

@ -0,0 +1,37 @@
{ lib, ... }:
let
domain = "git.giugl.io";
network = import ./network.nix;
in {
services.gitea = {
enable = true;
database.type = "sqlite3";
domain = domain;
appName = "Gitea";
rootUrl = "https://${domain}";
ssh.clonePort = 22;
settings.server.LFS_START_SERVER = true;
};
services.nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:3000";
extraConfig = ''
allow 127.0.0.1;
allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
allow 10.4.0.0/24;
deny all;
'';
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
}

View File

@ -0,0 +1,15 @@
{ ... }:
{
services.github-runner = {
enable = true;
url = "https://github.com/ropfuscator";
tokenFile = "/secrets/github-runner/token";
replace = true;
};
nix.extraOptions = ''
tarball-ttl = 0
access-tokens = github.com=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
'';
}

View File

@ -1,69 +1,50 @@
{ config, lib, modulesPath, ... }: # Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{ {
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ]; imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
hardware.cpu.amd.updateMicrocode =
lib.mkDefault config.hardware.enableRedistributableFirmware;
environment.etc."crypttab".text = '' boot.initrd.availableKernelModules =
backedNvme /dev/disk/by-uuid/92cfaa4a-82a1-4336-b552-b7f4f3c68613 /newdrive.key [ "nvme" "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
''; boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
boot = { fileSystems."/" = {
kernelModules = [ "kvm-amd" "dm-snapshot" ]; device = "zpool/nixos/root";
initrd = { fsType = "zfs";
luks.devices = {
# backedNvme = {
# device = "/dev/disk/by-uuid/92cfaa4a-82a1-4336-b552-b7f4f3c68613";
# keyFile = "/newdrive.key";
# allowDiscards = true;
# };
root = {
device = "/dev/disk/by-uuid/bdd5f111-ecec-48d8-861f-94083098c724";
preLVM = true;
allowDiscards = true;
fallbackToPassword = true;
};
};
availableKernelModules =
[ "nvme" "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
};
}; };
fileSystems = { fileSystems."/home" = {
"/" = { device = "zpool/data/home";
device = "/dev/disk/by-uuid/28ce6650-de21-4c1d-ae42-95d1e3507740"; fsType = "zfs";
fsType = "ext4"; };
};
"/boot" = { fileSystems."/media" = {
device = "/dev/disk/by-uuid/B790-869D"; device = "datapool/media";
fsType = "vfat"; fsType = "zfs";
}; };
"/backednvme" = { fileSystems."/secrets" = {
device = "/dev/mapper/backedNvme"; device = "backedpool/secrets";
}; fsType = "zfs";
};
"/services" = { fileSystems."/var/lib" = {
device = "/backednvme/services"; device = "backedpool/services";
options = [ "bind" ]; fsType = "zfs";
}; };
"/secrets" = { fileSystems."/boot" = {
device = "/backednvme/secrets"; device = "/dev/disk/by-uuid/AF19-5616";
options = [ "bind" ]; fsType = "vfat";
};
"${config.pepe.core.media.path}" = lib.mkIf config.pepe.core.media.enable {
device = "nvmedata/media";
fsType = "zfs";
};
}; };
swapDevices = [{ swapDevices = [{
device = "/swapfile"; device = "/dev/sdc1";
size = 1024 * 64; size = 10000;
}]; }];
} }

View File

@ -0,0 +1,24 @@
{ lib, ... }:
let
domain = "tube.giugl.io";
network = import ./network.nix;
in {
services = {
invidious = {
enable = true;
port = 9092;
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = { proxyPass = "http://localhost:9092"; };
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
}

View File

@ -0,0 +1,45 @@
{ pkgs, ... }:
let
network = import ./network.nix;
domain = "jellyfin.giugl.io";
in {
disabledModules = [ "services/misc/jellyfin.nix" ];
imports = [ ./modules/jellyfin.nix ];
services = {
jellyfin = {
enable = true;
group = "media";
package = pkgs.unstable.jellyfin;
};
nginx.virtualHosts.${domain} = {
# forceSSL = true;
# enableACME = true;
locations."/" = {
proxyPass = "http://localhost:8096";
extraConfig = ''
allow 10.0.0.0/24;
allow 10.3.0.0/24;
deny all;
'';
};
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "jellyfin" ];
users.groups.video.members = [ "jellyfin" ];
users.groups.render.members = [ "jellyfin" ];
fileSystems."/tmp/jellyfin" = {
device = "none";
fsType = "tmpfs";
options = [ "defaults" "size=20G" "uid=jellyfin" ];
};
}

View File

@ -0,0 +1,24 @@
{ lib, pkgs, ... }:
let
domain = "reddit.giugl.io";
network = import ./network.nix;
in {
services = {
libreddit = {
enable = true;
port = 9090;
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = { proxyPass = "http://localhost:9090"; };
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
}

View File

@ -0,0 +1,34 @@
{ lib, ... }:
let
domain = "htlid.giugl.io";
network = import ./network.nix;
in {
services = {
lidarr = {
enable = true;
group = "media";
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:8686";
extraConfig = ''
allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
deny all;
deny 10.0.0.1;
'';
};
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "lidarr" ];
}

View File

@ -1,45 +1,85 @@
{ config, pkgs, lib, ... }: { pkgs, ... }:
let let
domain = "runas.rocks"; domain = "matrix.giugl.io";
utilities = import ./utilities.nix { inherit lib config; }; webui_domain = "chat.giugl.io";
inherit (utilities) architectInterfaceAddress; network = import ./network.nix;
in in {
{
age.secrets.matrix = {
file = ../../secrets/matrix-synapse.age;
owner = "matrix-synapse";
};
services = { services = {
matrix-synapse = { matrix-synapse = {
enable = true; enable = true;
# Database config is in the .age file server_name = "${domain}";
extraConfigFiles = [ config.age.secrets.matrix.path ]; database_name = "synapse";
settings = { public_baseurl = "https://${domain}";
server_name = "${domain}"; registration_shared_secret = "runas!";
public_baseurl = "https://${domain}"; url_preview_enabled = true;
registration_shared_secret = "runas!"; dynamic_thumbnails = true;
url_preview_enabled = true; withJemalloc = true;
dynamic_thumbnails = true; # enable_registration = true;
withJemalloc = true; app_service_config_files = [
enable_registration = false; "/var/lib/matrix-synapse/discord-registration.yaml"
password_config.enabled = true; # "/var/lib/matrix-synapse/hookshot-registration.yml"
# "/var/lib/matrix-synapse/telegram-registration.yaml"
auto_join_rooms = [ "#general:${domain}" "#music:${domain}" "#movies:${domain}" ]; ];
extraConfig = ''
listeners = [{ auto_join_rooms:
port = 8008; - "#general:matrix.giugl.io"
bind_addresses = [ "127.0.0.1" ]; max_upload_size: "50M"
type = "http"; '';
tls = false; listeners = [{
x_forwarded = true; port = 8008;
resources = [{ bind_address = "::1";
names = [ "client" "federation" ]; type = "http";
compress = false; tls = false;
}]; x_forwarded = true;
resources = [{
names = [ "client" "federation" ];
compress = false;
}]; }];
}; }];
turn_uris = [
"turns:turn.giugl.io:5349?transport=udp"
"turns:turn.giugl.io:5349?transport=tcp"
];
turn_shared_secret = "69duck duck fuck420";
turn_user_lifetime = "1h";
logConfig = ''
version: 1
# In systemd's journal, loglevel is implicitly stored, so let's omit it
# from the message text.
formatters:
journal_fmt:
format: '%(name)s: [%(request)s] %(message)s'
filters:
context:
(): synapse.util.logcontext.LoggingContextFilter
request: ""
handlers:
journal:
class: systemd.journal.JournalHandler
formatter: journal_fmt
filters: [context]
SYSLOG_IDENTIFIER: synapse
root:
level: WARN
handlers: [journal]
disable_existing_loggers: False
'';
};
postgresql = {
enable = true;
package = pkgs.postgresql_11;
ensureDatabases = [ "synapse" ];
ensureUsers = [{
name = "matrix-synapse";
ensurePermissions = { "DATABASE synapse" = "ALL PRIVILEGES"; };
}];
}; };
nginx.virtualHosts = { nginx.virtualHosts = {
@ -52,44 +92,120 @@ in
''; '';
locations."= /.well-known/matrix/server".extraConfig = locations."= /.well-known/matrix/server".extraConfig =
let server = { "m.server" = "${domain}:443"; }; let server = { "m.server" = "${domain}:443"; };
in in ''
''
add_header Content-Type application/json; add_header Content-Type application/json;
return 200 '${builtins.toJSON server}'; return 200 '${builtins.toJSON server}';
''; '';
locations."= /.well-known/matrix/client".extraConfig = locations."= /.well-known/matrix/client".extraConfig = let
let client = {
client = { "m.homeserver" = { "base_url" = "https://${domain}:443"; };
"m.homeserver" = { "base_url" = "https://${domain}:443"; }; "m.identity_server" = { "base_url" = "https://vector.im"; };
"m.identity_server" = { "base_url" = "https://vector.im"; }; };
}; # ACAO required to allow element-web on any URL to request this json file
# ACAO required to allow element-web on any URL to request this json file in ''
in add_header Content-Type application/json;
'' add_header Access-Control-Allow-Origin *;
add_header Content-Type application/json; return 200 '${builtins.toJSON client}';
add_header Access-Control-Allow-Origin *; '';
return 200 '${builtins.toJSON client}';
'';
# locations."/".extraConfig = '' locations."/".extraConfig = ''
# return 404; return 404;
# ''; '';
# forward all Matrix API calls to the synapse Matrix homeserver # forward all Matrix API calls to the synapse Matrix homeserver
locations."/_matrix" = { locations."/_matrix" = {
proxyPass = "http://127.0.0.1:8008"; # without a trailing / proxyPass = "http://[::1]:8008"; # without a trailing /
}; };
};
locations."/_synapse" = { # web client
proxyPass = "http://127.0.0.1:8008"; # without a trailing / "${webui_domain}" = {
enableACME = true;
forceSSL = true;
root = pkgs.element-web.override {
conf = {
default_server_config."m.homeserver" = {
"base_url" = "https://${domain}";
"server_name" = "${domain}";
};
};
}; };
}; };
}; };
# discord bridge
matrix-appservice-discord = {
enable = true;
environmentFile = /secrets/matrix-appservice-discord/tokens.env;
# The appservice is pre-configured to use SQLite by default.
# It's also possible to use PostgreSQL.
settings = {
bridge = {
domain = domain;
homeserverUrl = "https://${domain}";
};
};
};
# telegram bridge
# mautrix-telegram = {
# enable = true;
# environmentFile = /secrets/mautrix-telegram/mautrix-telegram.env;
# settings = {
# homeserver = {
# address = "https://${domain}";
# domain = "${domain}";
# };
# appservice = {
# provisioning.enabled = false;
# id = "telegram";
# };
# bridge = {
# permissions = {
# "@pepe:${domain}" = "admin";
# "${domain}" = "puppeting";
# };
# # Animated stickers conversion requires additional packages in the
# # service's path.
# # If this isn't a fresh installation, clearing the bridge's uploaded
# # file cache might be necessary (make a database backup first!):
# # delete from telegram_file where \
# # mime_type in ('application/gzip', 'application/octet-stream')
# animated_sticker = {
# target = "gif";
# args = {
# width = 256;
# height = 256;
# fps = 30; # only for webm
# background = "020202"; # only for gif, transparency not supported
# };
# };
# encryption = {
# allow = true;
# default = true;
# };
# };
# };
# };
}; };
# systemd.services.mautrix-telegram.path = with pkgs; [
# lottieconverter # for animated stickers conversion, unfree package
# ffmpeg # if converting animated stickers to webm (very slow!)
# ];
networking.extraHosts = '' networking.extraHosts = ''
${architectInterfaceAddress "lan"} ${domain} ${network.architect-lan} ${domain} ${webui_domain}
${architectInterfaceAddress "tailscale"} ${domain} ${network.architect-wg} ${domain} ${webui_domain}
''; '';
} }

View File

@ -1,24 +1,18 @@
{ lib, config, pkgs, ... }: { config, pkgs, ... }:
let let
domain = "minecraft.giugl.io"; domain = "minecraft.giugl.io";
network = import ./network.nix;
utilities = import ./utilities.nix { inherit lib config; }; in {
inherit (utilities) architectInterfaceAddress;
in
{
architect.firewall.openTCP = [ 25565 ];
services.minecraft-server = { services.minecraft-server = {
enable = true; enable = true;
eula = true; eula = true;
declarative = true; declarative = true;
package = pkgs.unstablePkgs.minecraft-server;
serverProperties = { motd = "Welcome on the RuNas server!"; }; serverProperties = { motd = "Welcome on the RuNas server!"; };
}; };
networking.extraHosts = '' networking.extraHosts = ''
${architectInterfaceAddress "lan"} ${domain} ${network.architect-lan} ${domain}
${architectInterfaceAddress "tailscale"} ${domain} ${network.architect-wg} ${domain}
''; '';
} }

28
hosts/architect/minio.nix Normal file
View File

@ -0,0 +1,28 @@
{ lib, ... }:
let
domain = "s3.giugl.io";
network = import ./network.nix;
in {
services = {
minio.enable = true;
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:9000";
extraConfig = ''
allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
deny all;
'';
};
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
}

View File

@ -0,0 +1,130 @@
{ config, pkgs, lib, ... }:
with lib;
let cfg = config.services.jellyfin;
in {
options = {
services.jellyfin = {
enable = mkEnableOption "Jellyfin Media Server";
user = mkOption {
type = types.str;
default = "jellyfin";
description = "User account under which Jellyfin runs.";
};
package = mkOption {
type = types.package;
default = pkgs.jellyfin;
example = literalExample "pkgs.jellyfin";
description = ''
Jellyfin package to use.
'';
};
group = mkOption {
type = types.str;
default = "jellyfin";
description = "Group under which jellyfin runs.";
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = ''
Open the default ports in the firewall for the media server. The
HTTP/HTTPS ports can be changed in the Web UI, so this option should
only be used if they are unchanged.
'';
};
};
};
config = mkIf cfg.enable {
systemd.services.jellyfin = {
description = "Jellyfin Media Server";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = rec {
User = cfg.user;
Group = cfg.group;
# # Allows access to drm devices for transcoding with hardware acceleration
# SupplementaryGroups = [ "video" ];
StateDirectory = "jellyfin";
CacheDirectory = "jellyfin";
ExecStart =
"${cfg.package}/bin/jellyfin --datadir '/var/lib/${StateDirectory}' --cachedir '/var/cache/${CacheDirectory}'";
Restart = "on-failure";
# Security options:
NoNewPrivileges = true;
AmbientCapabilities = "";
CapabilityBoundingSet = "";
# # ProtectClock= adds DeviceAllow=char-rtc r
# DeviceAllow = [
# "char-drm r"
# "/dev/nvidia0 r"
# "/dev/nvidiactl r"
# "/dev/nvidia-uvm r"
# "/dev/nvidia-uvm-tools r"
# ];
DeviceAllow = "";
LockPersonality = true;
PrivateTmp = true;
PrivateUsers = true;
# ProtectClock = true;
ProtectControlGroups = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
RemoveIPC = true;
RestrictNamespaces = true;
# # AF_NETLINK needed because Jellyfin monitors the network connection
RestrictAddressFamilies = [ "AF_NETLINK" "AF_INET" "AF_INET6" "AF_UNIX" ];
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallErrorNumber = "EPERM";
SystemCallFilter = [
"@system-service"
"~@cpu-emulation"
"~@debug"
"~@keyring"
"~@memlock"
"~@obsolete"
"~@privileged"
"~@setuid"
];
};
};
users.users = mkIf (cfg.user == "jellyfin") {
jellyfin = {
group = cfg.group;
isSystemUser = true;
};
};
users.groups = mkIf (cfg.group == "jellyfin") { jellyfin = { }; };
networking.firewall = mkIf cfg.openFirewall {
# from https://jellyfin.org/docs/general/networking/index.html
allowedTCPPorts = [ 8096 8920 ];
allowedUDPPorts = [ 1900 7359 ];
};
};
meta.maintainers = with lib.maintainers; [ minijackson ];
}

View File

@ -0,0 +1,53 @@
{ lib, pkgs, ... }:
let
domain = "music.giugl.io";
network = import ./network.nix;
in {
services = {
navidrome = {
enable = true;
settings = {
MusicFolder = "/media/Music";
LastFM.enable = true;
LastFM.ApiKey = "5cef5cb5f9d31326b97d0f929ca9cf20";
LastFM.Secret = "d1296896126f4caae47407aecf080b25";
Spotify.ID = "3900c029b4f34f3fb61d554dda64794d";
Spotify.Secret = "d931ce5575a9401aa5ff8d37558cca0a";
EnableGravatar = true;
LogLevel = "WARN";
};
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:4533";
# extraConfig = ''
# allow 10.0.0.0/24;
# ${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
# deny all;
# '';
};
};
};
systemd.services."beets-rename" = {
enable = true;
serviceConfig = {
Type = "oneshot";
ExecStart =
"${pkgs.findutils}/bin/find /media/Music -type d -mindepth 2 -maxdepth 2 -exec ${pkgs.beets}/bin/beet -c /media/config.conf import --flat -q {} \\;";
};
startAt = "daily";
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "navidrome" ];
}

View File

@ -0,0 +1,73 @@
rec {
# interfaces
wan-if = "enp5s0";
vpn-if = "wg0";
proxy-if = "proxy";
# nets
lan-net = "10.0.0.0/24";
vpn-net = "10.3.0.0/24";
proxy-net = "10.4.0.0/24";
external_lan-net = "192.168.1.0/24";
# ips
dvr-lan = "10.0.0.2";
nas-lan = "10.0.0.3";
architect-lan = "10.0.0.250";
proxy-wg = "10.4.0.1";
architect-wg = "10.3.0.1";
galuminum-wg = "10.3.0.2";
oneplus-wg = "10.3.0.3";
ipad-wg = "10.3.0.4";
manduria-wg = "10.3.0.5";
antonio-wg = "10.3.0.6";
gbeast-wg = "10.3.0.7";
parisaphone-wg = "10.3.0.8";
parisapc-wg = "10.3.0.9";
peppiniell-wg = "10.3.0.10";
padulino-wg = "10.3.0.11";
shield-wg = "10.3.0.12";
pepos-wg = "10.3.0.15";
salvatore-wg = "10.3.0.16";
papa-wg = "10.3.0.17";
defy-wg = "10.3.0.18";
germano-wg = "10.3.0.19";
flavio-wg = "10.3.0.20";
tommy-wg = "10.3.0.21";
alain-wg = "10.3.0.22";
dima-wg = "10.3.0.23";
mikey-wg = "10.3.0.24";
andrew-wg = "10.3.0.25";
mikeylaptop-wg = "10.3.0.26";
andrewdesktop-wg = "10.3.0.27";
jacopo-wg = "10.3.0.28";
frznn-wg = "10.3.0.29";
ludo-wg = "10.3.0.30";
parina-wg = "10.3.0.31";
nilo-wg = "10.3.0.32";
parina-ipad-wg = "10.3.0.33";
eleonora-wg = "10.3.0.100";
angellane-wg = "10.3.0.200";
hotpottino-wg = "10.3.0.201";
dodino-wg = "10.3.0.202";
wolfsonhouse-wg = "10.3.0.203";
# groups
gdevices-wg =
[ galuminum-wg oneplus-wg ipad-wg gbeast-wg peppiniell-wg padulino-wg wolfsonhouse-wg ];
routers-wg = [ hotpottino-wg angellane-wg dodino-wg wolfsonhouse-wg ];
c2c-wg = [ ] ++ gdevices-wg;
towan-wg = [ shield-wg parisaphone-wg parisapc-wg parina-wg parina-ipad-wg ] ++ gdevices-wg
++ routers-wg;
gamenet-wg = [
andrew-wg
galuminum-wg
gbeast-wg
mikey-wg
andrewdesktop-wg
mikeylaptop-wg
flavio-wg
salvatore-wg
];
}

View File

@ -1,86 +1,37 @@
{ pkgs, config, lib, ... }: { pkgs, ... }:
let let
domain = "cloud.giugl.io"; domain = "cloud.giugl.io";
redis_port = 6379; network = import ./network.nix;
in {
utilities = import ./utilities.nix { inherit lib config; };
inherit (utilities) architectInterfaceAddress;
in
{
age.secrets = {
nextcloud-admin = {
file = ../../secrets/nextcloud-admin.age;
owner = "nextcloud";
group ="nginx";
};
nextcloud-database = {
file = ../../secrets/nextcloud-database.age;
owner = "nextcloud";
group = "nginx";
};
};
environment.systemPackages = with pkgs; [
ffmpeg
];
services = { services = {
nginx.virtualHosts.${domain} = { mysql.enable = true;
forceSSL = true; mysql.package = pkgs.unstable.mysql80;
enableACME = true;
extraConfig = ''
aio threads;
directio 1M;
output_buffers 3 1M;
sendfile on; redis.enable = true;
sendfile_max_chunk 0;
autoindex on;
'';
};
mysql = {
enable = true;
package = pkgs.mariadb_1011;
};
redis = {
vmOverCommit = true;
servers."nextcloud" = {
enable = true;
port = redis_port;
};
};
nextcloud = { nextcloud = {
enable = true; enable = true;
hostName = domain; hostName = "${domain}";
https = true; https = true;
package = pkgs.nextcloud31; package = pkgs.unstable.nextcloud23;
datadir = "/services/nextcloud";
configureRedis = true; caching.redis = true;
caching = {
redis = true;
};
autoUpdateApps.enable = true; autoUpdateApps.enable = true;
autoUpdateApps.startAt = "05:00:00"; autoUpdateApps.startAt = "05:00:00";
logLevel = 1;
maxUploadSize = "50G";
settings = {
overwriteprotocol = "https";
};
config = { config = {
overwriteProtocol = "https";
dbtype = "mysql"; dbtype = "mysql";
dbuser = "nextcloud"; dbuser = "oc_giulio2";
dbhost = "localhost"; dbhost = "localhost";
dbname = "nextcloud"; dbname = "nextcloud_final";
dbpassFile = config.age.secrets.nextcloud-database.path; dbpassFile = "/secrets/nextcloud/dbpass.txt";
adminpassFile = config.age.secrets.nextcloud-admin.path; adminpassFile = "/secrets/nextcloud/adminpass.txt";
adminuser = "giulio";
extraTrustedDomains = [ "${domain}" ];
}; };
}; };
}; };
@ -91,8 +42,12 @@ in
}; };
networking.extraHosts = '' networking.extraHosts = ''
${architectInterfaceAddress "lan"} ${domain} ${network.architect-lan} ${domain}
${architectInterfaceAddress "tailscale"} ${domain} ${network.architect-wg} ${domain}
''; '';
services.nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
};
} }

View File

@ -1,93 +1,35 @@
{ services, pkgs, lib, ... }: { services, ... }:
{ {
architect.firewall = {
openTCP = [ 80 443 ];
};
services.nginx = { services.nginx = {
enable = true; enable = true;
package = pkgs.nginx;
recommendedGzipSettings = true; recommendedGzipSettings = true;
recommendedOptimisation = true; recommendedOptimisation = true;
recommendedProxySettings = true; recommendedProxySettings = true;
recommendedTlsSettings = true; recommendedTlsSettings = true;
virtualHosts."architect.devs.giugl.io" = { # virtualHosts."giugl.io" = {
default = true; # default = true;
enableACME = true; # enableACME = true;
forceSSL = true; # addSSL = true;
root = "/var/lib/nginx/error_pages"; # root = "/var/lib/nginx/error_pages";
extraConfig = "error_page 404 /index.htm;"; # extraConfig = "error_page 404 /index.htm;";
#
locations = { # locations = {
"/" = { return = "404"; }; # "/" = {
# return = "404";
"/index.htm" = { }; # };
#
"/style.css" = { }; # "/index.htm" = {
# };
"/wat.jpg" = { }; #
}; # "/style.css" = {
}; # };
#
# appendHttpConfig = # "/wat.jpg" = {
# let # };
# extraPureLuaPackages = with pkgs.luajitPackages; [ # };
# lua-resty-openidc # };
# lua-resty-http
# lua-resty-session
# lua-resty-jwt
# lua-resty-openssl
# ];
# luaPath = pkg: "${pkg}/share/lua/5.1/?.lua";
# makeLuaPath = lib.concatMapStringsSep ";" luaPath;
# in
# ''
# # https://stackoverflow.com/questions/38931468/nginx-reverse-proxy-error14077438ssl-ssl-do-handshake-failed
# proxy_ssl_server_name on;
# lua_package_path '${makeLuaPath extraPureLuaPackages};;';
# lua_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
# lua_ssl_verify_depth 5;
# # cache for OIDC discovery metadata
# lua_shared_dict discovery 1m;
# lua_shared_dict jwks 1m;
# # https://github.com/openresty/lua-resty-redis/issues/159
# resolver local=on ipv6=off;
# init_worker_by_lua_block {
# function check_role (res, role)
# if res.user.roles == nil then
# return false
# end
# for _,v in pairs(res.user.roles) do
# if string.lower(v) == role then
# return true
# end
# end
# return false
# end
# function is_ip_whitelisted(ip, whitelist)
# for _, x in ipairs(whitelist) do
# if ip == x then
# return true
# end
# end
# return false
# end
# }
# '';
appendConfig = ''
worker_processes 24;
'';
}; };
users.groups.acme.members = [ "nginx" ]; users.groups.acme.members = [ "nginx" ];

View File

@ -0,0 +1,32 @@
{ lib, pkgs, ... }:
let
domain = "tweet.giugl.io";
network = import ./network.nix;
in {
services = {
nitter = {
enable = true;
server = {
port = 9093;
hostname = domain;
staticDir = "${pkgs.unstable.nitter}/share/nitter/public";
};
preferences = {
replaceYouTube = "tube.giugl.io";
replaceTwitter = "tweet.giugl.io";
};
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = { proxyPass = "http://localhost:9093"; };
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
}

View File

@ -0,0 +1,33 @@
{ lib, ... }:
let
domain = "htnzb.giugl.io";
network = import ./network.nix;
in {
services = {
nzbget = {
enable = true;
group = "media";
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:6789";
extraConfig = ''
allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
deny all;
'';
};
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "nzbget" ];
}

View File

@ -1,25 +0,0 @@
{ config, lib, ... }:
{
options.architect = {
firewall = lib.mkOption {
internal = true;
default = config.pepe.core.firewall;
};
networks = lib.mkOption {
internal = true;
default = config.pepe.core.network.interfaces;
};
vhost = lib.mkOption {
internal = true;
default = config.pepe.core.vhost.hosts;
};
};
config.architect.networks.docker = {
interface = "docker0";
net = "172.17.0.0/16";
};
}

View File

@ -0,0 +1,13 @@
{ ... }:
{
virtualisation.oci-containers.containers."overseerr" = {
image = "sctx/overseerr:latest";
volumes = [ "/var/lib/overseerr:/app/config" ];
environment = {
"LOG_LEVEL" = "debug";
"TZ" = "Europe/Rome";
};
#ports = [ "5055:5055" ];
};
}

91
hosts/architect/plex.nix Normal file
View File

@ -0,0 +1,91 @@
{ pkgs, lib, ... }:
let
domain = "media.giugl.io";
network = import ./network.nix;
in {
services.plex = {
enable = true;
package = pkgs.unstable.plex;
dataDir = "/plex";
};
services.nginx = {
enable = true;
# give a name to the virtual host. It also becomes the server name.
virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
http2 = true;
extraConfig = ''
allow 10.3.0.0/24;
allow 10.0.0.0/24;
deny all;
#Some players don't reopen a socket and playback stops totally instead of resuming after an extended pause
send_timeout 100m;
# Why this is important: https://blog.cloudflare.com/ocsp-stapling-how-cloudflare-just-made-ssl-30/
ssl_stapling on;
ssl_stapling_verify on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
#Intentionally not hardened for security for player support and encryption video streams has a lot of overhead with something like AES-256-GCM-SHA384.
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
# Forward real ip and host to Plex
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $server_addr;
proxy_set_header Referer $server_addr;
proxy_set_header Origin $server_addr;
# Plex has A LOT of javascript, xml and html. This helps a lot, but if it causes playback issues with devices turn it off.
gzip on;
gzip_vary on;
gzip_min_length 1000;
gzip_proxied any;
gzip_types text/plain text/css text/xml application/xml text/javascript application/x-javascript image/svg+xml;
gzip_disable "MSIE [1-6]\.";
# Nginx default client_max_body_size is 1MB, which breaks Camera Upload feature from the phones.
# Increasing the limit fixes the issue. Anyhow, if 4K videos are expected to be uploaded, the size might need to be increased even more
client_max_body_size 100M;
# Plex headers
proxy_set_header X-Plex-Client-Identifier $http_x_plex_client_identifier;
proxy_set_header X-Plex-Device $http_x_plex_device;
proxy_set_header X-Plex-Device-Name $http_x_plex_device_name;
proxy_set_header X-Plex-Platform $http_x_plex_platform;
proxy_set_header X-Plex-Platform-Version $http_x_plex_platform_version;
proxy_set_header X-Plex-Product $http_x_plex_product;
proxy_set_header X-Plex-Token $http_x_plex_token;
proxy_set_header X-Plex-Version $http_x_plex_version;
proxy_set_header X-Plex-Nocache $http_x_plex_nocache;
proxy_set_header X-Plex-Provides $http_x_plex_provides;
proxy_set_header X-Plex-Device-Vendor $http_x_plex_device_vendor;
proxy_set_header X-Plex-Model $http_x_plex_model;
# Websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Buffering off send to the client as soon as the data is received from Plex.
proxy_redirect off;
proxy_buffering off;
'';
locations."/" = { proxyPass = "http://localhost:32400"; };
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "plex" ];
}

View File

@ -1,8 +0,0 @@
{ pkgs, lib, ... }:
{
services.postgresql = {
enable = true;
package = lib.mkForce pkgs.postgresql_16;
};
}

View File

@ -0,0 +1,41 @@
{ lib, config, ... }:
let
domain = "xmpp.giugl.io";
conference_domain = "conference.${domain}";
upload_domain = "uploads.${domain}";
network = import ./network.nix;
in {
services = {
prosody = {
enable = true;
virtualHosts = {
"${domain}" = {
domain = domain;
enabled = true;
ssl.key = "${config.security.acme.certs.${domain}.directory}/key.pem";
ssl.cert =
"${config.security.acme.certs.${domain}.directory}/fullchain.pem";
};
};
muc = [{ domain = conference_domain; }];
uploadHttp = { domain = upload_domain; };
admins = [ "giulio@${domain}" ];
#httpInterfaces = [ "wg0" ];
#httpsInterfaces = [ "wg0" ];
};
};
services.nginx.virtualHosts."${domain}".enableACME = true;
#services.nginx.virtualHosts."${conference_domain}".enableACME = true;
#services.nginx.virtualHosts."${upload_domain}".enableACME = true;
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.acme.members = [ "prosody" ];
}

View File

@ -0,0 +1,38 @@
{ lib, ... }:
let
domain = "htpro.giugl.io";
network = import ./network.nix;
in {
services = {
prowlarr.enable = true;
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:9696";
extraConfig = ''
allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
deny all;
'';
};
# locations."/api" = {
# proxyPass = "http://127.0.0.1:9696/prowlarr/api";
# };
#
# locations."/Content" = {
# proxyPass = "http://127.0.0.1:9696/prowlarr/Content";
# };
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "prowlarr" ];
}

View File

@ -0,0 +1,33 @@
{ lib, ... }:
let
domain = "htrad.giugl.io";
network = import ./network.nix;
in {
services = {
radarr = {
enable = true;
group = "media";
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:7878";
extraConfig = ''
allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
deny all;
'';
};
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "radarr" ];
}

View File

@ -0,0 +1,49 @@
#!/bin/sh
EASYLIST_HOSTSNAME="easylist_hosts.txt"
EASYPRIVACY_HOSTSNAME="easyprivacy_hosts.txt"
STEVENBLACK_HOSTSNAME="stevenblack_hosts.txt"
get_easylist() {
EASYLIST_URL="https://raw.githubusercontent.com/easylist/easylist/master/easylist/easylist_adservers.txt"
tmpfile=`mktemp`
# download easylist
${pkgs.wget}/bin/wget $EASYLIST_URL -O $tmpfile
# remove IP addresses and prepend 0.0.0.0 to create hosts file
cat $tmpfile | egrep -v "([0-9]{1,3}\.){3}[0-9]{1,3}" | grep -oP "^\|\|(\K[a-zA-Z0-9\.\-]+)" | ${pkgs.gawk}/bin/gawk '{print "0.0.0.0 " $0}' > $EASYLIST_HOSTSNAME
}
get_easyprivacy() {
EASYLIST_URL="https://raw.githubusercontent.com/easylist/easylist/master/easyprivacy/easyprivacy_trackingservers.txt"
tmpfile=`mktemp`
# download easylist
${pkgs.wget}/bin/wget $EASYLIST_URL -O $tmpfile
# remove IP addresses and prepend 0.0.0.0 to create hosts file
cat $tmpfile | egrep -v "([0-9]{1,3}\.){3}[0-9]{1,3}" | grep -oP "^\|\|(\K[a-zA-Z0-9\.\-]+)" | ${pkgs.gawk}/bin/gawk '{print "0.0.0.0 " $0}' > $EASYPRIVACY_HOSTSNAME
}
get_stevenblack() {
STEVENBLACK_URL="https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/fakenews/hosts"
${pkgs.wget}/bin/wget $STEVENBLACK_URL -O $STEVENBLACK_HOSTSNAME
}
get_easylist
get_easyprivacy
get_stevenblack
# create unified file
cat *hosts.txt | sort | uniq | grep "^0" > /etc/adblock_hosts
rm $EASYLIST_HOSTSNAME $STEVENBLACK_HOSTSNAME

View File

@ -1,64 +0,0 @@
{ config, pkgs, ... }:
let
domain = "search.giugl.io";
in
{
services = {
redis.servers."searx" = { enable = true; port = 4456; };
searx = {
enable = true;
package = pkgs.unstablePkgs.searxng;
environmentFile = /secrets/searx/env;
settings = {
server = {
secret_key = "@SEARX_SECRET_KEY@";
port = 4455;
};
general = {
instance_name = "PepoSearch";
contact_url = "mailto:search@depasquale.giugl.io";
enable_metrics = true;
};
search = {
safe_search = 0;
autocomplete = "google";
prefer_configured_language = false;
formats = [ "html" "json"];
};
ui = {
infinite_scroll = true;
query_in_title = true;
results_on_new_tab = true;
theme_args.simple_style = "dark";
};
redis.url = "redis://127.0.0.1:${toString config.services.redis.servers."searx".port}";
engines = [
{ name = "google"; disabled = false; }
{ name = "bing"; disabled = false; }
{ name = "qwant"; disabled = true; }
{ name = "brave"; disabled = true; }
{ name = "duckduckgo"; disabled = false; }
];
};
};
};
architect.vhost.${domain} = with config.architect.networks; {
dnsInterfaces = [ "tailscale" ];
locations."/" = {
port = config.services.searx.settings.server.port;
allowLan = true;
allowWAN = true;
allow = [
tailscale.net
];
};
};
}

View File

@ -0,0 +1,33 @@
{ lib, ... }:
let
domain = "htson.giugl.io";
network = import ./network.nix;
in {
services = {
sonarr = {
enable = true;
group = "media";
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:8989";
extraConfig = ''
allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
deny all;
'';
};
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "sonarr" ];
}

View File

@ -1,203 +0,0 @@
{ config, pkgs, ... }:
let
user = "sunshine";
resolutionScript = pkgs.writeTextFile {
name = "sunshine-resolution-script";
text = ''
#!${pkgs.bash}/bin/bash
width=''${1:-1280}
height=''${2:-720}
refresh_rate=''${3:-120}
# Get the modeline info from the 2nd row in the cvt output
modeline=$(${pkgs.xorg.libxcvt}/bin/cvt ''${width} ''${height} ''${refresh_rate} | ${pkgs.gawk}/bin/gawk 'FNR == 2')
xrandr_mode_str=''${modeline//Modeline \"*\" /}
mode_alias="''${width}x''${height}"
echo "xrandr setting new mode ''${mode_alias} ''${xrandr_mode_str}"
# Check if mode exists before trying to remove it
if ${pkgs.xorg.xrandr}/bin/xrandr --listmodes | grep -q "^''${mode_alias}"; then
${pkgs.xorg.xrandr}/bin/xrandr --rmmode ''${mode_alias} || echo "Failed to remove existing mode"
fi
${pkgs.xorg.xrandr}/bin/xrandr --newmode ''${mode_alias} ''${xrandr_mode_str} || echo "Failed to create new mode"
${pkgs.xorg.xrandr}/bin/xrandr --addmode DP-0 ''${mode_alias} || echo "Failed to add mode to output"
# Apply new xrandr mode
${pkgs.xorg.xrandr}/bin/xrandr --output DP-0 --primary --mode ''${mode_alias} --pos 0x0 --rotate normal || echo "Failed to apply mode"
${config.boot.kernelPackages.nvidia_x11.settings}/bin/nvidia-settings -a 'SyncToVBlank=0' || echo "Failed to disable VSync"
${config.boot.kernelPackages.nvidia_x11.bin}/bin/nvidia-smi --persistence-mode=ENABLED || echo "Failed to enable persistence mode"
'';
executable = true;
destination = "/bin/resolution.sh";
};
sunshinePkg = (pkgs.unstablePkgs.sunshine.override { cudaSupport = true; });
in
{
pepe.core.graphics = {
enable = true;
nvidia = true;
};
boot.kernelModules = [ "uinput" ];
environment.systemPackages = with pkgs.unstablePkgs; [ gamemode heroic ];
systemd.services.NetworkManager-wait-online.enable = pkgs.lib.mkForce false;
programs.steam = {
enable = true;
gamescopeSession.enable = true;
};
security = {
polkit.extraConfig = ''
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.login1.suspend" ||
action.id == "org.freedesktop.login1.suspend-multiple-sessions" ||
action.id == "org.freedesktop.login1.hibernate" ||
action.id == "org.freedesktop.login1.hibernate-multiple-sessions")
{
return polkit.Result.NO;
}
});
'';
rtkit.enable = true;
};
systemd.user.services.sunshine = {
serviceConfig = {
Restart = pkgs.lib.mkForce "always";
};
};
services = {
pulseaudio.enable = false;
sunshine = {
enable = true;
autoStart = true;
package = sunshinePkg;
settings = {
sunshine_name = "The Architect";
capture = "nvfbc";
encoder = "nvenc";
wan_encryption_mode = 0;
lan_encryption_mode = 0;
origin_web_ui_allowed = "lan";
min_threads = 12;
log_path = "sunshine.log";
back_button_timeout = 2500;
};
applications = {
env = {
VDPAU_DRIVER = "nvidia";
LIBVA_DRIVER_NAME = "nvidia";
NVD_BACKEND = "direct";
__GL_SYNC_TO_VBLANK = "0";
__GL_VRR_ALLOWED = "0";
DXVK_ASYNC = "1";
};
apps = [
{
name = "Steam w/ Hue Lights";
cmd = ''${pkgs.bash}/bin/bash -c "${pkgs.gamescope}/bin/gamescope --adaptive-sync --force-composition --immediate-flips --rt -C 3000 -f -e -W ''${SUNSHINE_CLIENT_WIDTH} -H ''${SUNSHINE_CLIENT_HEIGHT} -r ''${SUNSHINE_CLIENT_FPS} -- ${pkgs.steam}/bin/steam -pipewire"'';
detached = [
"${pkgs.pepePkgs.huenicorn}/bin/huenicorn"
];
prep-cmd = [
{
do = ''${pkgs.bash}/bin/bash -c "${resolutionScript}/bin/resolution.sh ''${SUNSHINE_CLIENT_WIDTH} ''${SUNSHINE_CLIENT_HEIGHT}" ''${SUNSHINE_CLIENT_FPS}"'';
undo = ''${pkgs.bash}/bin/bash -c "${pkgs.procps}/bin/pkill gamescope; ${pkgs.procps}/bin/pkill sunshine; ${pkgs.procps}/bin/pkill -KILL huenicorn"'';
}
];
}
{
name = "Steam";
cmd = ''${pkgs.bash}/bin/bash -c "${pkgs.steam}/bin/steam -pipewire"'';
prep-cmd = [
{
do = ''${pkgs.bash}/bin/bash -c "${resolutionScript}/bin/resolution.sh ''${SUNSHINE_CLIENT_WIDTH} ''${SUNSHINE_CLIENT_HEIGHT}" ''${SUNSHINE_CLIENT_FPS}"'';
undo = ''${pkgs.bash}/bin/bash -c "${pkgs.procps}/bin/pkill gamescope; ${pkgs.procps}/bin/pkill sunshine"'';
}
];
}
{
name = "Heroic";
cmd = ''${pkgs.bash}/bin/bash -c "${pkgs.unstablePkgs.heroic}/bin/heroic"'';
prep-cmd = [
{
do = ''${pkgs.bash}/bin/bash -c "${resolutionScript}/bin/resolution.sh ''${SUNSHINE_CLIENT_WIDTH} ''${SUNSHINE_CLIENT_HEIGHT}" ''${SUNSHINE_CLIENT_FPS}"'';
undo = ''${pkgs.bash}/bin/bash -c "${pkgs.procps}/bin/pkill heroic; ${pkgs.procps}/bin/pkill sunshine"'';
}
];
}
];
};
};
displayManager = {
autoLogin = {
inherit user;
enable = true;
};
sddm = {
enable = true;
wayland.enable = false;
};
};
xserver = {
enable = true;
desktopManager.xfce.enable = true;
monitorSection = ''
HorizSync 5.0 - 1000.0
VertRefresh 5.0 - 1000.0
Option "DPMS"
'';
deviceSection = ''
VendorName "NVIDIA Corporation"
Option "CustomEDID" "DFP-1:/etc/X11/120edid.bin"
Option "ConnectedMonitor" "DFP-1"
'';
screenSection = ''
Monitor "Configured Monitor"
DefaultDepth 24
Option "ModeValidation" "NoVertRefreshCheck, NoHorizSyncCheck, NoMaxSizeCheck, NoMaxPClkCheck, AllowNonEdidModes, NoEdidMaxPClkCheck"
Option "UseEdidfreqs" "False"
Option "TripleBuffer" "False"
SubSection "Display"
Depth 24
EndSubSection
'';
};
};
systemd.targets = {
sleep.enable = false;
suspend.enable = false;
hibernate.enable = false;
hybrid-sleep.enable = false;
};
users = {
users.${user} = {
isNormalUser = true;
home = "/home/${user}";
description = "Sunshine Server";
extraGroups = [ "wheel" "networkmanager" "input" "video" "sound" ];
openssh.authorizedKeys.keys = [ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC1we38/N+t8Ah5yrLof8QUwhrob7/VXFKIddaJeOVBLuDVnW7ljiAtdtEiL69D/DV4Ohmt5wMvkAAjfuHmim6FD9A6lzPbSU4KH9W2dcckszKbbI636kuDwem/xui6BW3wJa6P+0xW5ksygEAkzcK2PXuC2b4B9uwhuUdKahiGMKDxISG/WianqAe72cGMfNkYvion3Y1VsMLUdm48d2ABnxNpr7NI9B5iJ8dziOft9gpgfz13CCQRlReo75gk/4xI+vSNrQp7eR+wzJy2/dZg/T8jtyA9Q6jVxrxBpqQ1LNXkAKaJkGo9OabF6Wgpzp+YTAurL4nwR2NaJxwFuyoKvACQy0ai4jrS3206gC6JXZv8ktZMZrwUN+jPqCwfgh5qObFkAqKCxbp52ioDek2MQLdOvzQBX//DBhGEp5rzHGLZ3vhRIiiQiaof5sF5zWiYDW5mqezSPNxJPX/BrTP/Wbs/jpwTLBh3wytiia0S1WXQmya89bqzTPFiDWvTRA62EVKB/JaQtPQQOFAxWwg799DMycPeZ81xttZOyMtI/MZSddyqx2S8fWGwvToZQvuZ38mSIpFseLM1IkgabRIrAmat5SBNGGy9Dqa0eMEa7bwIY/4CMB1y6HMTnaoMXA6cnQfHMoB/zyTZ6oTXIeqeOyiZsK+RN0Mvahj8mXi7dw== giulio@giulio-X230" ];
};
groups.media.members = [ user ];
};
}

View File

@ -1,44 +0,0 @@
{ pkgs, config, lib, ... }:
let
domain = "devs.giugl.io";
utilities = import ./utilities.nix { inherit lib config; };
inherit (utilities) generateDeviceStrings;
in
{
pepe.core = {
firewall.openUDP = [ 41641 ];
network.interfaces.tailscale = {
interface = "ts0";
net = "100.64.0.0/10";
type = "vpn";
devices = {
architect = { address = "100.64.0.1"; hostname = "architect.${domain}"; isEndpoint = true; };
kmerr = { address = "100.64.0.2"; hostname = "kmerr.${domain}"; };
work_laptop = { address = "100.64.0.4"; hostname = "work_laptop.${domain}"; };
work_desktop = { address = "100.64.0.5"; hostname = "work_desktop.${domain}"; };
manduria = { address = "100.64.0.6"; hostname = "manduria.${domain}"; };
tommy = { address = "100.64.0.7"; hostname = "tommy.${domain}"; };
alfredo = { address = "100.64.0.9"; hostname = "alfredo.${domain}"; };
appletv = { address = "100.64.0.13"; hostname = "appletv.${domain}"; };
afsun = { address = "100.64.0.15"; hostname = "afsun.${domain}"; };
jacopo-desktop = { address = "100.64.0.21"; hostname = "jacopo-desktop.${domain}"; };
jacopo-tv = { address = "100.64.0.22"; hostname = "jacopo-tv.${domain}"; };
jacopo-phone = { address = "100.64.0.28"; hostname = "jacopo-phone.${domain}"; };
jacopo-ipad = { address = "100.64.0.8"; hostname = "jacopo-ipad.${domain}"; };
};
};
};
services = {
tailscale = {
enable = true;
interfaceName = config.pepe.core.network.interfaces.tailscale.interface;
package = pkgs.unstablePkgs.tailscale;
};
};
networking.extraHosts = generateDeviceStrings config.pepe.core.network.interfaces.tailscale.devices;
}

View File

@ -0,0 +1,43 @@
{ lib, config, ... }:
let
domain = "httra.giugl.io";
network = import ./network.nix;
in {
services = {
transmission = {
enable = true;
group = "media";
settings = {
download-dir = "/media/transmission";
incomplete-dir = "/media/transmission/.incomplete";
rpc-host-whitelist = "${domain}";
encryption = 2;
speed-limit-up = 10;
speed-limit-up-enabled = true;
peer-port = 51413;
};
performanceNetParameters = true;
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:9091";
extraConfig = ''
allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
deny all;
'';
};
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "transmission" ];
}

View File

@ -1,16 +0,0 @@
{ config, lib, ... }:
let
ifaces = config.pepe.core.network.interfaces;
in
{
# device.address device.hostname
generateDeviceStrings = devices: lib.concatStringsSep "\n"
(lib.mapAttrsToList (name: device: "${device.address} ${device.hostname}") devices);
getDeviceAddress = interface: device:
ifaces.${interface}.devices.${device}.address;
architectInterfaceAddress = interface:
ifaces.${interface}.devices.architect.address;
}

View File

@ -0,0 +1,270 @@
with import ./network.nix; {
networking = {
extraHosts = ''
${architect-wg} architect.devs.giugl.io
${galuminum-wg} galuminum.devs.giugl.io
${oneplus-wg} oneplus.devs.giugl.io
${ipad-wg} ipad.devs.giugl.io
${manduria-wg} manduria.devs.giugl.io
${antonio-wg} antonio.devs.giugl.io
${gbeast-wg} gbeast.devs.giugl.io
${parisaphone-wg} parisa-phone.devs.giugl.io
${parisapc-wg} parisa-pc.devs.giugl.io
${peppiniell-wg} peppiniell.devs.giugl.io
${padulino-wg} padulino.devs.giugl.io
${shield-wg} shield.devs.giugl.io
${pepos-wg} pepos.devs.giugl.io
${eleonora-wg} eleonora.devs.giugl.io
${angellane-wg} angellane.devs.giugl.io
${hotpottino-wg} hotpottino.devs.giugl.io
${salvatore-wg} salvatore.devs.giugl.io
${papa-wg} papa.devs.giugl.io
${defy-wg} defy.devs.giugl.io
${germano-wg} germano.devs.giugl.io
${dodino-wg} dodino.devs.giugl.io
${tommy-wg} tommy.devs.giugl.io
${alain-wg} alain.devs.giugl.io
${dima-wg} dima.devs.giugl.io
${mikey-wg} mikey.devs.giugl.io
${andrew-wg} andrew.devs.giugl.io
${mikeylaptop-wg} mikeylaptop.devs.giugl.io
${wolfsonhouse-wg} wolfsonhouse.devs.giugl.io
${frznn-wg} frznn.devs.giugl.io
${ludo-wg} ludo.devs.giugl.io
${parina-wg} parina.devs.giugl.io
${parina-ipad-wg} parinaipad.devs.giugl.io
${nilo-wg} nilo.devs.giugl.io
'';
wireguard = {
interfaces.${proxy-if} = {
ips = [ "10.4.0.2/32" ];
privateKeyFile = "/secrets/wireguard/proxy.key";
peers = [{
publicKey = "WmJBpXpYebcmJEF8nVTKMqQK01KyBe42vzc38K66rVs=";
allowedIPs = [ "10.4.0.1/32" ];
endpoint = "giugl.io:1195";
persistentKeepalive = 21;
}];
};
interfaces.${vpn-if} = {
listenPort = 1194;
ips = [ "10.3.0.1/24" ];
privateKeyFile = "/secrets/wireguard/server.key";
peers = [
{
# gAluminum
allowedIPs = [ galuminum-wg ];
publicKey = "pEEgSs7xmO0cfyvoQlU8lfwqdYM1ISgmPAunPtF+0xw=";
}
{
# OnePlus
allowedIPs = [ oneplus-wg ];
publicKey = "zynSERy6VhxN5zBf1ih3BOAHxvigDixHB9YKnSBgYFs=";
}
{
# iPad
allowedIPs = [ ipad-wg ];
publicKey = "DPpd+P/hV1XLuvdcrCRv1sgz8BeZt1y5D6VehNuhjSQ=";
}
{
# Manduria
allowedIPs = [ manduria-wg ];
publicKey = "wT38oXvDQ8g0hI+pAXQobOWf/Wott2zhwo8TLvXK400=";
}
{
# Antonio
allowedIPs = [ antonio-wg ];
publicKey = "SPndCvEzuLHtGAQV8u/4dfLlFHoPcXS3L98oFOwTljc=";
}
{
# Eleonora
allowedIPs = [ eleonora-wg ];
publicKey = "SL54f1ZeieFyn5X5UAPmypP10GV/c419O94vCzGHFhg=";
}
{
# padulino
allowedIPs = [ padulino-wg ];
publicKey = "sk2Wr2OesND9jcuP/8k7BirSpR4pNNbS9gBkbOxZxwg=";
}
{
# GBEAST
allowedIPs = [ gbeast-wg ];
publicKey = "XiK+wk+DErz0RmCWRxuaJN1cvdj+3DoiU6tcR+uZfAI=";
}
{
# parisa-phone
allowedIPs = [ parisaphone-wg ];
publicKey = "MGdaRMmsik7SLRUsijS0TctcKUD6Tnr7XugGJClTCC4=";
}
{
# parisa-pc
allowedIPs = [ parisapc-wg ];
publicKey = "b2QzZDTgGQbNXSCLYB4KUzq0/099pH2T8H5BckfNSTQ=";
}
{
# peppiniell
allowedIPs = [ peppiniell-wg ];
publicKey = "bzoW3Rx+7Un9hx/2opgBQJmmnZ/hgj1lQ2FnonCHjTc=";
}
{
# angellane
allowedIPs = [ angellane-wg ];
publicKey = "MZ+nZklHpBxTL7QN9QJpBBx7yOYRZLONfvqAnuk85x0=";
}
{
# hotpottino
allowedIPs = [ hotpottino-wg ];
publicKey = "YqtzTWqGBs2GwSPNO0aRSV4nvJDW3UHHt6fV4UC7vnU=";
}
{
# shield
allowedIPs = [ shield-wg ];
publicKey = "1GaV/M48sHqQTrBVRQ+jrFU2pUMmv2xkguncVcwPCFs=";
}
{
# pepos
allowedIPs = [ pepos-wg ];
publicKey = "mb1VaMLML5J24oCMBuhqvBrT6S4tAqWERn30z+h/LwM=";
}
{
# salvatore
allowedIPs = [ salvatore-wg ];
publicKey = "fhlnBHeMyHZKLUCTSA9kmkKoM5x/qzz/rnCJrUh3Gzs=";
}
{
# papa
allowedIPs = [ papa-wg ];
publicKey = "oGHygt02Oni3IFbScKD0NVEfHKCp6bpw68aq5g4RrAA=";
}
{
# defy
allowedIPs = [ defy-wg ];
publicKey = "Cvi/eto7E6Ef+aiL81ou7x12fJCeuXrf/go9fxEqXG4=";
}
{
# germano
allowedIPs = [ germano-wg ];
publicKey = "gi4o+pZWKItzVs7vY8fvXh98jX6CNeCwc1YDzhc3mA4=";
}
{
# flavio
allowedIPs = [ flavio-wg ];
publicKey = "Yg0P+yHi/9SZHyoel8jT9fmmu+irLYmT8yMp/CZoaSg=";
}
{
# dodino
allowedIPs = [ dodino-wg ];
publicKey = "JHkqlADQpY1CUcivraG9i6rIzCzLVFcl8HP5uIk35lk=";
}
{
# tommy
allowedIPs = [ tommy-wg ];
publicKey = "tytknU7wql1d0A2provX3RP7CNcEIajfgBJKoSyVLgo=";
}
{
# alain
allowedIPs = [ alain-wg ];
publicKey = "/o2msFJoUL4yovcIQJTU8c1faFtekrjSBBWJABouWno=";
}
{
# dima
allowedIPs = [ dima-wg ];
publicKey = "svzWYIZ6v+cLCp/emGG7mx2YpBJqw2fqjVuHZy7b6H0=";
}
{
# wolfsonhouse
allowedIPs = [ wolfsonhouse-wg ];
publicKey = "UJRJcAOcnEjEB3o4K2I7gEM97SrhENEesZNf28z+EBQ=";
}
{
# mikey
allowedIPs = [ mikey-wg ];
publicKey = "ewbDdX3z7nxG2aPIf9TogXkhxPlGipLFcy6XfyDC6gI=";
}
{
# andrew
allowedIPs = [ andrew-wg ];
publicKey = "LP/FgST9fmBQSoKQFq9sFGvjRFOtRooMcuEcjuqaoWM=";
}
{
# mikey laptop
allowedIPs = [ mikeylaptop-wg ];
publicKey = "kz/pY/PgV+dwF1JZ2It4r5B5QfRSQM7HkbFCdvd5Yxk=";
}
{
# andrew desktop
allowedIPs = [ andrewdesktop-wg ];
publicKey = "rpYr3JNLIzxpxzFuQuaHFEl/XvPEPfwLbDETBP8KYXI=";
}
{
# laptop desktop
allowedIPs = [ jacopo-wg ];
publicKey = "W/taWI79bPIKOolVVu5xZfiJnPw9K91Xn1zhcM0+4g0=";
}
{
# frznn
allowedIPs = [ frznn-wg ];
publicKey = "dXcrdME6VnnE5PBYwvUmayf7cn2wpcExeCR9gIXOO0o=";
}
{
# ludo
allowedIPs = [ ludo-wg ];
publicKey = "ecrxdzx7tQZwMPxZOjHUvxZT2xY79B6XEDIW+fhEtEM=";
}
{
# parina
allowedIPs = [ parina-wg ];
publicKey = "7nubNnfGsg4/7KemMDn9r99mNK8RFU9uOFFqaYv6rUA=";
}
{
# nilo
allowedIPs = [ nilo-wg ];
publicKey = "lhTEDJ9WnizvEHTd5kN21fTHF27HNk+fPLQnB1B3LW0=";
}
{
# parina ipad
allowedIPs = [ parina-ipad-wg ];
publicKey = "ezkCzl2qC7Hd7rFKfqMa0JXDKRhVqy79H52rA06x7mU=";
}
];
};
};
};
}

View File

@ -9,8 +9,7 @@ let
export __VK_LAYER_NV_optimus=NVIDIA_only export __VK_LAYER_NV_optimus=NVIDIA_only
exec -a "$0" "$@" exec -a "$0" "$@"
''; '';
in in {
{
imports = [ ./hardware.nix ./wireguard.nix ./sound.nix ]; imports = [ ./hardware.nix ./wireguard.nix ./sound.nix ];
boot = { boot = {
@ -71,5 +70,5 @@ in
programs.steam.enable = true; programs.steam.enable = true;
environment.systemPackages = with pkgs; [ efibootmgr nvidia-offload ]; environment.systemPackages = with pkgs; [ efibootmgr nvidia-offload ];
# system.stateVersion = "21.05"; # Did you read the comment? system.stateVersion = "21.05"; # Did you read the comment?
} }

73
hosts/proxy/coturn.nix Normal file
View File

@ -0,0 +1,73 @@
{ pkgs, config, ... }:
let
public_ip = "23.88.108.216";
realm = "turn.giugl.io";
static-auth-secret = "69duck duck fuck420";
in {
services.coturn = rec {
inherit realm static-auth-secret;
secure-stun = true;
enable = true;
no-cli = true;
no-tcp-relay = true;
min-port = 49000;
max-port = 50000;
use-auth-secret = true;
relay-ips = [ public_ip ];
listening-ips = [ public_ip ];
cert = "${config.security.acme.certs.${realm}.directory}/full.pem";
pkey = "${config.security.acme.certs.${realm}.directory}/key.pem";
extraConfig = ''
verbose
cipher-list=\"HIGH\"
no-multicast-peers
denied-peer-ip=0.0.0.0-0.255.255.255
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=169.254.0.0-169.254.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
denied-peer-ip=192.0.0.0-192.0.0.255
denied-peer-ip=192.0.2.0-192.0.2.255
denied-peer-ip=192.88.99.0-192.88.99.255
denied-peer-ip=192.168.0.0-192.168.255.255
denied-peer-ip=198.18.0.0-198.19.255.255
denied-peer-ip=198.51.100.0-198.51.100.255
denied-peer-ip=203.0.113.0-203.0.113.255
denied-peer-ip=240.0.0.0-255.255.255.255
denied-peer-ip=::1
denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
denied-peer-ip=100::-100::ffff:ffff:ffff:ffff
denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
'';
};
networking.firewall = {
interfaces.ens3 = let
range = with config.services.coturn; [{
from = min-port;
to = max-port;
}];
in {
allowedUDPPortRanges = range;
allowedUDPPorts = [ 5349 ];
#allowedTCPPortRanges = range;
allowedTCPPorts = [ 80 443 5349 ];
};
};
services.nginx.enable = true;
services.nginx.virtualHosts.${realm} = {
addSSL = true;
enableACME = true;
};
# to access the ACME files
users.groups.nginx.members = [ "turnserver" ];
}

26
hosts/proxy/default.nix Normal file
View File

@ -0,0 +1,26 @@
{ config, pkgs, ... }:
{
imports =
[ ./hardware-configuration.nix ./coturn.nix ./wireguard.nix ./ssh.nix ];
boot.loader.grub = {
enable = true;
version = 2;
devices = [ "/dev/sda" ];
};
system.stateVersion = "21.05";
networking = {
useDHCP = false;
hostName = "proxy";
nameservers = [ "10.4.0.2" "1.1.1.1" ];
interfaces.ens3.useDHCP = true;
};
users.users.root.openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCuURERnIFe2XbNu6AsPe2DO11RuaHxVGUcaoJUsIB1F+VOggOVLhxSenOPYLm6NvvGeXVi95G5Sm1UZRcJEEkvxus4bSViV4t/Q2azfYFE27yRH/IeMMoWNPGYNm5Bok2qFb4vHifra9FffwXnOzr0nDDTdHXCft4TO5nsenLJwqu5zOO1CR7J52otY7LheNPyzbGxgIkB3Y7LeOj1+/xXSOJ379NOL2RQBobsg7k442WCX7tU6AC1ct3W+93tcJUUdzJKTT9TJ+XmhdjXNWhDd+QZUNAMr+nKoEdExHp0H40/wIhcLD2OV95gX4i/YBzCg4OQOqZqWiibiEQfGTSAh5aD+nX/PqjXf0XSLEUOA81biLFu28oO8gocjwnhgqmlghvO4SG1rs6uZ8EyPyWsrVMjy8B9FX4aloKqua3aicgC+upjLl3x+KkMJizlMB5Ew7KOjPsjXwMqeJmeBOEd6TSEctttR+lIp+/368FtwXeBxzx9MBT4620mnjWtVKM= giulio@gAluminum"
];
}

View File

@ -0,0 +1,22 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
imports = [ (modulesPath + "/profiles/qemu-guest.nix") ];
boot.initrd.availableKernelModules =
[ "ata_piix" "virtio_pci" "virtio_scsi" "xhci_pci" "sd_mod" "sr_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/8b5bcd4a-02b8-4e11-b856-eda792b8b7b8";
fsType = "ext4";
};
swapDevices = [ ];
}

15
hosts/proxy/ssh.nix Normal file
View File

@ -0,0 +1,15 @@
{ config, ... }:
{
services = {
fail2ban.enable = true;
openssh = {
permitRootLogin = "prohibit-password";
passwordAuthentication = false;
enable = true;
};
};
networking.firewall.allowedTCPPorts = [ 22 ];
}

42
hosts/proxy/wireguard.nix Normal file
View File

@ -0,0 +1,42 @@
{ config, ... }:
let
wg_if = "wg0";
wan_if = "ens3";
in {
networking = {
firewall.allowedUDPPorts = [ 1195 ];
nat = {
enable = true;
externalInterface = wan_if;
internalInterfaces = [ wg_if ];
forwardPorts = [{
destination = "10.4.0.2:1194";
proto = "udp";
sourcePort = 1194;
}];
};
wireguard = {
interfaces.${wg_if} = {
listenPort = 1195;
ips = [ "10.4.0.1/24" ];
privateKeyFile = "/secrets/wireguard/server.key";
postSetup = ''
/run/current-system/sw/bin/iptables -t nat -A POSTROUTING -o ${wg_if} -j MASQUERADE
'';
postShutdown = ''
/run/current-system/sw/bin/iptables -t nat -D POSTROUTING -o ${wg_if} -j MASQUERADE
'';
peers = [{
allowedIPs = [ "10.4.0.2" "10.3.0.0/24" ];
publicKey = "73oFhyQA3mgX4GmN6ul5HuOsgxa4INlzCPsyuXna0AA=";
}];
};
};
};
}

View File

@ -1,7 +0,0 @@
rec {
architect = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICu7rSsZ+d3BkppimNHJj8xL5jfl5RxMU0+Q5cue0LUu root@architect";
architectHostKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDGLLAtRzLtCExHLhpsC+vH1nXcla3wibbMOFRCwXfXjtn2A9DjewHBwcbQbYQa6yuaEa3vmvUyrUtW6RUAiGSNhDMUPz7swr5tujgO/6ToPf0vKDDeOCwK5wqmNoUlDf7qzkxwCiI0dPYuCr7uGt00/ebSGfp+F1zmgC9MxuefYMdX5Q5I7HoHOYbBC9q9ue5mc0g+F8GnmD+Pd2pDDiHpCflT+iOzLJH0gCcW/0e5q7XYKGs09Cm/L1zroHIb14Borndu0Mby7x2FlnSeap5KXr9rkKVyr3amX0mksb4N0T36MMJwLYcrvE0S8utFdHEusoYEkP3fjSgsKKHKEgiZbqaeA0oZHddG49JNBsCLmmrN8T142t1fftP4NdFyKpcI9gYsbXhZf6bheV1wQ/cpv3KkLGG7JlZeORRAc4xgT33BHvVXTcWCE2EYcNmdscrMOEw3mcDESu7S14iXZgGIUgYISZ3GTZ5+mNB6OoEwxqK+eYzYMyDpNBxv6/LlEvc= root@architect";
macbook = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC1we38/N+t8Ah5yrLof8QUwhrob7/VXFKIddaJeOVBLuDVnW7ljiAtdtEiL69D/DV4Ohmt5wMvkAAjfuHmim6FD9A6lzPbSU4KH9W2dcckszKbbI636kuDwem/xui6BW3wJa6P+0xW5ksygEAkzcK2PXuC2b4B9uwhuUdKahiGMKDxISG/WianqAe72cGMfNkYvion3Y1VsMLUdm48d2ABnxNpr7NI9B5iJ8dziOft9gpgfz13CCQRlReo75gk/4xI+vSNrQp7eR+wzJy2/dZg/T8jtyA9Q6jVxrxBpqQ1LNXkAKaJkGo9OabF6Wgpzp+YTAurL4nwR2NaJxwFuyoKvACQy0ai4jrS3206gC6JXZv8ktZMZrwUN+jPqCwfgh5qObFkAqKCxbp52ioDek2MQLdOvzQBX//DBhGEp5rzHGLZ3vhRIiiQiaof5sF5zWiYDW5mqezSPNxJPX/BrTP/Wbs/jpwTLBh3wytiia0S1WXQmya89bqzTPFiDWvTRA62EVKB/JaQtPQQOFAxWwg799DMycPeZ81xttZOyMtI/MZSddyqx2S8fWGwvToZQvuZ38mSIpFseLM1IkgabRIrAmat5SBNGGy9Dqa0eMEa7bwIY/4CMB1y6HMTnaoMXA6cnQfHMoB/zyTZ6oTXIeqeOyiZsK+RN0Mvahj8mXi7dw== giulio@giulio-X230";
groups.architect = [ architect architectHostKey ];
}

6
lib/default.nix Normal file
View File

@ -0,0 +1,6 @@
{ pkgs, unstable, nixpkgs, nixos-unstable, home-manager, ... }: rec {
user = import ./user.nix { inherit pkgs unstable; };
host = import ./host.nix {
inherit pkgs nixpkgs unstable nixos-unstable home-manager user;
};
}

View File

@ -1,49 +1,44 @@
{ pkgs { pkgs, nixpkgs, nixos-unstable, unstable, home-manager, user, ... }:
, nixpkgs
, home-manager
, system
, mkSysRole
, mkUser
, ...
}:
{ {
mkHost = { name, users, roles ? [ ], imports ? [ ] }: mkHost = { name, users, roles ? [], imports ? [] }:
let let
users_mod = (map system = "x86_64-linux";
(u:
mkUser {
name = u.user;
roles = u.roles;
})
users);
roles_mod = (map (r: mkSysRole r) roles);
add_imports = imports;
in
nixpkgs.lib.nixosSystem {
inherit system pkgs;
modules = [ mkRole = role : import (../roles + "/${role}.nix");
{
imports = users_mod ++
roles_mod ++
add_imports ++ [
(mkSysRole "common")
(mkSysRole "acme")
(mkUser { name = "root"; roles = [ ]; })
../modules
];
home-manager = { users_mod= (map (u: user.mkUser {name = u.user; roles = u.roles; }) users);
useGlobalPkgs = true; roles_mod = (map (r: mkRole r) roles);
}; add_imports = imports;
in nixpkgs.lib.nixosSystem {
inherit system;
system.stateVersion = "25.05"; modules = [
} {
imports = users_mod ++ roles_mod ++ add_imports;
nixpkgs = {
pkgs = pkgs;
};
home-manager.nixosModules.home-manager nix.nixPath = [
../hosts/${name}/default.nix "nixpkgs=${nixpkgs}"
pkgs.nixosModules.cachixConfig "unstable=${nixos-unstable}"
]; ];
}; nix.registry.nixpkgs.flake = nixpkgs;
nix.registry.unstable.flake = nixos-unstable;
users.users.root = {
shell = pkgs.zsh;
};
home-manager.users.root.imports = [ ../roles/home/common.nix ];
home-manager.extraSpecialArgs.unstable = unstable;
}
home-manager.nixosModules.home-manager
../roles/common.nix
../roles/acme.nix
../hosts/${name}/default.nix
];
};
} }

View File

@ -1,72 +0,0 @@
{ nixpkgs, inputs }:
let
mkSystem = { system, cudaSupport ? false }:
let
config = {
inherit cudaSupport;
allowUnfree = true;
};
cachixOverlay = final: prev: {
nixosModules = (prev.nixosModules or { }) // {
cachixConfig = import ../cachix.nix;
};
};
extOverlays = [
(inputs.nvidia-patch.overlays.default)
cachixOverlay
];
importNixpkgs = { flake }:
import flake {
inherit system config;
overlays = extOverlays;
};
unstablePkgs = importNixpkgs { flake = inputs.nixos-unstable; };
masterPkgs = importNixpkgs { flake = inputs.nixpkgs-master; };
localPkgs = importNixpkgs { flake = inputs.local-unstable; };
agenixPkgs = importNixpkgs { flake = inputs.agenix-flake; };
pepePkgs = inputs.pepeflake.packages.${system} // inputs.pepeflake.legacyPackages.${system} or { };
langtoolPkgs = inputs.langtool-ngrams.packages.${system} // inputs.langtool-ngrams.legacyPackages.${system} or { };
additionalOverlays = [
(final: prev: { inherit unstablePkgs; })
(final: prev: { inherit localPkgs; })
(final: prev: { inherit agenixPkgs; })
(final: prev: { inherit masterPkgs; })
(final: prev: { inherit pepePkgs; })
(final: prev: { inherit langtoolPkgs; })
];
pkgs = import nixpkgs {
inherit system config;
overlays = additionalOverlays ++ extOverlays ++ [
(final: prev: {
ctranslate2 = prev.ctranslate2.override {
withCUDA = cudaSupport;
withCuDNN = cudaSupport;
};
})
];
};
utils =
let
inherit (pkgs.lib) makeScope;
inherit (pkgs) newScope;
in
makeScope newScope (self: rec {
inherit nixpkgs inputs;
inherit (self.callPackage ../lib/utils.nix { }) mkSysRole mkHomeRole;
inherit (user) mkUser;
user = self.callPackage ../lib/user.nix { home-manager = inputs.home-manager; };
host = self.callPackage ../lib/host.nix { home-manager = inputs.home-manager; };
});
in
{ inherit pkgs utils; };
in
{ inherit mkSystem; }

View File

@ -1,58 +1,36 @@
{ pkgs { pkgs, unstable, ... }:
, stdenv
, home-manager
, mkHomeRole
, ...
}:
let
# Default roles that all NixOS users get
defaultSystemUserRoles = [ "common" "zsh" "aichat" ];
# Default roles for standalone home-manager configs
defaultHMUserRoles = [ "common" "aichat" ];
in
{ {
mkUser = { name, roles ? [ ], defaultRoles ? defaultSystemUserRoles }: mkUser = { name, roles ? [ ] }:
let let
roles_mod = (map (r: mkHomeRole r) roles); mkRole = role: import (../roles/home + "/${role}.nix");
defaults_mod = (map (r: mkHomeRole r) defaultRoles); roles_mod = (map (r: mkRole r) roles);
in in {
{ users.groups.plugdev = { };
fileSystems."/home/${name}/Downloads" = pkgs.lib.mkIf stdenv.isLinux {
fileSystems."/home/${name}/Downloads" = {
device = "tmpfs"; device = "tmpfs";
fsType = "tmpfs"; fsType = "tmpfs";
options = [ "size=3G" ]; options = [ "size=3G" ];
}; };
users = { users.users.${name} = {
users.${name} = { isNormalUser = true;
isNormalUser = name != "root"; shell = pkgs.zsh;
extraGroups = [ "wheel" "plugdev" ]; extraGroups = [ "wheel" "plugdev" ];
shell = pkgs.zsh;
};
}; };
programs.zsh.enable = true; home-manager.users.${name}.imports = [ (mkRole "common") ]
++ roles_mod;
};
home-manager.users.${name}.imports = defaults_mod ++ roles_mod; mkHMUser = { name, roles ? [] }:
}; let
mkRole = role: import (../roles/home + "/${role}.nix");
in{
home-manager.users.${name}.imports = [ (mkRole "common") ]
++ roles_mod;
};
mkHMUser = { name, roles ? [ ], defaultRoles ? defaultHMUserRoles }: };
let
roles_mod = (map (r: mkHomeRole r) roles);
defaults_mod = (map (r: mkHomeRole r) defaultRoles);
in
home-manager.lib.homeManagerConfiguration {
inherit pkgs;
modules = [
{
home = {
username = name;
homeDirectory =
if stdenv.isLinux then "/home/${name}" else "/Users/${name}";
};
}
] ++ defaults_mod ++ roles_mod;
};
} }

View File

@ -1,24 +0,0 @@
{ ... }:
let
mkSysRole = role:
let
path = ../roles + "/${role}.nix";
in
if builtins.pathExists path then
import path
else
throw "System role '${role}' not found at ${toString path}";
mkHomeRole = role:
let
path = ../roles/home + "/${role}.nix";
in
if builtins.pathExists path then
import path
else
throw "Home role '${role}' not found at ${toString path}";
in
{
inherit mkSysRole mkHomeRole;
}

View File

@ -1,11 +0,0 @@
{...}: {
imports = [
./media.nix
./graphics.nix
./network.nix
./vhost.nix
./firewall.nix
./dns.nix
./docker.nix
];
}

View File

@ -1,169 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkOption types mkIf concatMapStrings concatStrings mapAttrsToList attrsets;
cfg = config.pepe.core.dns;
in
{
options.pepe.core.dns = {
enable = mkOption {
type = types.bool;
default = false;
description = "Whether to enable the DNS server.";
};
nextDNSId = mkOption {
type = types.str;
default = "d65174";
description = "NextDNS ID for DNS over TLS.";
};
extraDomains = mkOption {
type = types.attrsOf (types.submodule {
options = {
dnsInterfaces = mkOption {
type = types.listOf types.str;
default = [ ];
description = "List of interfaces to add DNS entries for this domain.";
};
};
});
default = { };
description = "Additional domains to add to DNS configuration.";
};
};
config = mkIf cfg.enable {
services.coredns = {
enable = true;
config =
let
# Function to generate domain-specific configurations
generateDomainConfig = domain: conf: ifaceName:
let
iface = config.pepe.core.network.interfaces.${ifaceName};
ifaceEndpoint = lib.head (lib.attrNames (lib.filterAttrs (_: device: device.isEndpoint) iface.devices));
serverIP = iface.devices.${ifaceEndpoint}.address;
interfaceNet = iface.net;
in
''
${domain} {
view ${ifaceName} {
expr incidr(client_ip(), '${interfaceNet}')
}
template IN A ${domain} {
answer "${domain}. 60 IN A ${serverIP}"
}
template IN HTTPS ${domain} {
answer "${domain}. 60 IN HTTPS 1 . ipv4hint=\"${serverIP}\""
}
cache
log
}
'';
# Function to generate device views for an interface
generateDeviceViews = ifaceName:
let
iface = config.pepe.core.network.interfaces.${ifaceName};
in
concatMapStrings
({ name, device }:
let
deviceIP = device.address;
serverName = "${name}-${cfg.nextDNSId}.dns.nextdns.io";
in
''
. {
view ${name} {
expr client_ip() == '${deviceIP}'
}
forward . tls://45.90.28.77 tls://45.90.30.77 {
tls_servername ${serverName}
health_check 5s
}
}
''
)
(attrsets.mapAttrsToList
(name: device: { inherit name device; })
iface.devices
);
# Collect all interfaces used across all domains
allInterfaces = lib.unique (lib.flatten
(lib.mapAttrsToList
(_: conf: conf.dnsInterfaces)
(config.pepe.core.vhost.hosts // cfg.extraDomains)
));
# Generate all device views once
allDeviceViews = concatMapStrings generateDeviceViews allInterfaces;
# Function to generate configurations for all domains
generateCoreDNSConfig = domains:
let
generateForDomain = domain: conf:
concatMapStrings
(ifaceName: generateDomainConfig domain conf ifaceName)
conf.dnsInterfaces;
in
concatStrings (mapAttrsToList generateForDomain domains);
allDomains = config.pepe.core.vhost.hosts // cfg.extraDomains;
in
let
# Function to generate DNS records for individual devices based on dnsResolvableName
generateDeviceHostRecords =
let
generateRecordsForInterface = ifaceName: ifaceConfig:
lib.concatStringsSep "\n" (lib.mapAttrsToList
(deviceName: deviceConfig:
if deviceConfig.hostname!= null then
let
deviceAddress = deviceConfig.address;
deviceIface = ifaceConfig.net;
deviceHost = deviceConfig.hostname;
in
''
${deviceHost} {
view ${ifaceName} {
expr incidr(client_ip(), '${deviceIface}')
}
template IN A ${deviceHost} {
answer "${deviceHost}. 60 IN A ${deviceAddress}"
}
cache
log
}
''
else ""
)
ifaceConfig.devices);
in
lib.concatStringsSep "\n" (lib.mapAttrsToList generateRecordsForInterface config.pepe.core.network.interfaces);
in
''
${generateCoreDNSConfig allDomains}
${generateDeviceHostRecords}
${allDeviceViews}
. {
forward . tls://45.90.28.77 tls://45.90.30.77 {
tls_servername "lan-${cfg.nextDNSId}.dns.nextdns.io"
health_check 5s
}
}
'';
};
};
}

View File

@ -1,60 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.core.docker;
in
{
options.pepe.core.docker = with lib; {
enable = mkEnableOption "Enable Docker";
nvidia = mkEnableOption "Enable NVIDIA Container Toolkit";
dataRoot = mkOption {
type = types.str;
default = "/var/lib/docker";
description = "Docker data root directory";
};
extraOptions = mkOption {
type = types.str;
default = "";
description = "Extra options for Docker daemon";
};
enableOnBoot = mkOption {
type = types.bool;
default = false;
description = "Start Docker on boot";
};
iptables = mkOption {
type = types.bool;
default = false;
description = "Whether Docker should manipulate iptables";
};
users = mkOption {
type = types.listOf types.str;
default = [];
description = "Users to add to the docker group";
};
};
config = mkIf cfg.enable {
hardware.nvidia-container-toolkit.enable = cfg.nvidia;
virtualisation = {
oci-containers.backend = "docker";
docker = {
enable = true;
extraOptions = cfg.extraOptions;
enableOnBoot = cfg.enableOnBoot;
daemon.settings = {
iptables = cfg.iptables;
data-root = cfg.dataRoot;
};
};
};
users.users = lib.genAttrs cfg.users (user: {
extraGroups = [ "docker" ];
});
};
}

View File

@ -1,27 +0,0 @@
{ config, lib, ... }:
let
inherit (lib) mkOption types;
cfg = config.pepe.core.firewall;
in
{
options.pepe.core.firewall = {
openTCP = mkOption {
type = types.listOf types.int;
default = [ ];
description = "TCP ports to open in the firewall";
};
openUDP = mkOption {
type = types.listOf types.int;
default = [ ];
description = "UDP ports to open in the firewall";
};
};
config = {
networking.firewall = {
allowedTCPPorts = cfg.openTCP;
allowedUDPPorts = cfg.openUDP;
};
};
}

View File

@ -1,39 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.core.graphics;
in
{
options.pepe.core.graphics = with lib; {
enable = mkEnableOption "Enable graphics";
nvidia = mkEnableOption "Enable nvidia graphics";
};
config = mkIf cfg.enable {
hardware = {
graphics = {
enable = true;
extraPackages = with pkgs; mkIf cfg.nvidia [ vaapiVdpau ];
};
nvidia = mkIf cfg.nvidia {
modesetting.enable = true;
powerManagement.enable = false;
powerManagement.finegrained = false;
open = false;
nvidiaSettings = true;
package = config.boot.kernelPackages.nvidiaPackages.latest;
};
};
services = {
xserver = {
enable = true;
videoDrivers = mkIf cfg.nvidia [ "nvidia" ];
};
};
};
}

View File

@ -1,27 +0,0 @@
{ config, lib, ... }:
let
cfg = config.pepe.core.media;
in
{
options.pepe.core.media = with lib; {
enable = mkEnableOption "Enable media library";
group = mkOption {
type = types.str;
default = "media";
};
path = mkOption {
type = types.str;
default = null;
};
groupMembers = mkOption {
type = types.listOf types.str;
default = [];
};
};
config = lib.mkIf cfg.enable {
users.groups.${cfg.group}.members = [ cfg.group ] ++ cfg.groupMembers;
};
}

View File

@ -1,126 +0,0 @@
{ config, lib, ... }:
let
inherit (lib) mkOption types;
cfg = config.pepe.core.network;
in
{
options.pepe.core.network = {
interfaces = mkOption {
type = types.attrsOf (types.submodule {
options = {
interface = mkOption {
type = types.str;
description = "The network interface name.";
};
type = mkOption {
type = types.enum [ "lan" "wan" "vpn" ];
description = "The type of interface (lan, wan, or vpn).";
};
net = mkOption {
type = types.str;
description = "The network address in CIDR format.";
};
devices = mkOption {
type = types.attrsOf (types.submodule {
options = {
address = mkOption {
type = types.str;
description = "The IP address of the device.";
};
hostname = mkOption {
type = types.str;
description = "The hostname of the device.";
};
isEndpoint = mkOption {
type = types.bool;
default = false;
description = "Whether this device serves as a DNS endpoint for this interface.";
};
};
});
default = { };
description = "An attribute set of devices with their configurations.";
};
};
});
default = { };
description = "An attribute set of networks with their configurations.";
};
interfacesByType = mkOption {
type = types.attrsOf (types.listOf types.str);
default = { };
description = "Interfaces grouped by type (lan, wan, vpn) for easy access.";
internal = true;
};
dnsEndpoints = mkOption {
type = types.attrsOf (types.submodule {
options = {
interface = mkOption {
type = types.str;
description = "The interface this DNS endpoint belongs to.";
};
device = mkOption {
type = types.str;
description = "The device name that serves as the DNS endpoint.";
};
address = mkOption {
type = types.str;
description = "The IP address of the DNS endpoint.";
};
serverName = mkOption {
type = types.str;
default = "";
description = "DNS server name for TLS connections.";
};
};
});
default = { };
description = "DNS endpoints for each interface.";
internal = true;
};
};
config = {
# Create lists of interfaces by type for easy access elsewhere
pepe.core.network.interfacesByType = {
lan = lib.attrNames (lib.filterAttrs (_: iface: iface.type == "lan") cfg.interfaces);
wan = lib.attrNames (lib.filterAttrs (_: iface: iface.type == "wan") cfg.interfaces);
vpn = lib.attrNames (lib.filterAttrs (_: iface: iface.type == "vpn") cfg.interfaces);
};
# Collect DNS endpoints from all interfaces
pepe.core.network.dnsEndpoints =
let
collectEndpoints = ifaceName: iface:
lib.mapAttrs'
(deviceName: device:
lib.nameValuePair
"${ifaceName}-${deviceName}"
{
interface = ifaceName;
device = deviceName;
address = device.address;
serverName = device.dnsServerName;
}
)
(lib.filterAttrs (_: device: device.isDnsEndpoint) iface.devices);
in
lib.foldl
(acc: ifaceName:
acc // (collectEndpoints ifaceName cfg.interfaces.${ifaceName})
)
{ }
(lib.attrNames cfg.interfaces);
};
}

View File

@ -1,131 +0,0 @@
{ config, lib, ... }:
let
inherit (lib) mkOption types mapAttrs concatMapStringsSep optionalString mkIf;
cfg = config.pepe.core.vhost;
in
{
options.pepe.core.vhost = {
hosts = mkOption {
type = types.attrsOf (types.submodule {
options = {
locations = mkOption {
type = types.attrsOf (types.submodule {
options = {
extraConfig = mkOption {
type = types.str;
description = "Extra configuration for the location.";
default = "";
};
proxyWebsockets = mkOption {
type = types.bool;
default = false;
};
host = mkOption {
type = types.str;
description = "The host for the location.";
default = "127.0.0.1";
};
port = mkOption {
type = types.int;
description = "The port number for the location.";
};
allow = mkOption {
type = types.listOf types.str;
default = [ ];
description = "IP address or CIDR block to allow.";
};
allowVPN = mkOption {
type = types.bool;
default = false;
description = "If set to true, allow VPN traffic.";
};
allowLAN = mkOption {
type = types.bool;
default = false;
description = "If set to true, allow LAN traffic.";
};
allowWAN = mkOption {
type = types.bool;
default = false;
description = "If set to true, allow WAN traffic. If false, deny all WAN traffic.";
};
path = mkOption {
type = types.str;
default = "";
};
recommendedProxySettings = mkOption {
type = types.bool;
default = true;
description = "Force the use of recommended proxy configuration.";
};
};
});
default = { };
description = "An attribute set of location configurations.";
};
resolveLocally = mkOption {
type = types.bool;
default = true;
description = "If true, ensure the vhost is resolvable to 127.0.0.1 locally on this server.";
};
};
});
default = { };
description = "An attribute set of domain configurations.";
};
};
config = {
pepe.core.firewall.openTCP = [ 80 443 ];
# Configure DNS entries for vhosts when DNS is enabled
pepe.core.dns = mkIf config.pepe.core.dns.enable {
extraDomains = mapAttrs
(domain: conf: {
dnsInterfaces =
(lib.optionals (lib.any (loc: loc.allowLAN) (lib.attrValues conf.locations))
config.pepe.core.network.interfacesByType.lan) ++
(lib.optionals (lib.any (loc: loc.allowVPN) (lib.attrValues conf.locations))
config.pepe.core.network.interfacesByType.vpn);
})
cfg.hosts;
};
services.nginx.virtualHosts = mapAttrs
(domain: conf: {
forceSSL = true;
useACMEHost = "giugl.io";
locations = mapAttrs
(path: location: {
proxyPass = "http://${location.host}:${toString location.port}${location.path}";
proxyWebsockets = location.proxyWebsockets;
recommendedProxySettings = location.recommendedProxySettings;
extraConfig = ''
${concatMapStringsSep "\n" (allowCIDR: "allow ${allowCIDR};") location.allow}
${optionalString location.allowLAN (concatMapStringsSep "\n" (ifaceName: "allow ${config.pepe.core.network.interfaces.${ifaceName}.net};") config.pepe.core.network.interfacesByType.lan)}
${optionalString location.allowVPN (concatMapStringsSep "\n" (ifaceName: "allow ${config.pepe.core.network.interfaces.${ifaceName}.net};") config.pepe.core.network.interfacesByType.vpn)}
${optionalString (!location.allowWAN) "deny all;"}
'' + location.extraConfig;
})
conf.locations;
})
cfg.hosts;
networking.extraHosts = lib.concatStringsSep "\n" (lib.flatten (lib.mapAttrsToList
(domain: hostConf:
lib.optionals hostConf.resolveLocally "127.0.0.1 ${domain}"
)
cfg.hosts
));
};
}

View File

@ -1,7 +0,0 @@
{ ... }: {
imports = [
./services
./core
];
}

View File

@ -1,35 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.bazarr;
in
{
options.pepe.services.bazarr = with lib; {
enable = mkEnableOption "Enable bazarr";
package = mkPackageOption pkgs "bazarr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.bazarr = {
enable = true;
package = cfg.package;
group = "media";
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = 6767;
allowLAN = true;
allowVPN = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ "bazarr" ];
};
}

View File

@ -1,22 +0,0 @@
{ ... }: {
imports = [
./bazarr
./gitea
./homeassistant
./immich
./jellyfin
./jellyseer
./lidarr
./llm
./minio
./navidrome
./nzbget
./prowlarr
./radarr
./redlib
./sonarr
./headscale
./languagetool
./whisparr
];
}

View File

@ -1,53 +0,0 @@
{ config, pkgs, lib, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.gitea;
in
{
options.pepe.services.gitea = with lib; {
enable = mkEnableOption "Enable gitea";
package = mkPackageOption pkgs "gitea" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
pepe.core = {
firewall.openTCP = [ config.services.gitea.settings.server.SSH_PORT ];
vhost.hosts.${cfg.domain} = {
locations."/" = {
port = config.services.gitea.settings.server.HTTP_PORT;
allowLAN = true;
allowVPN = true;
allowWAN = true;
};
};
};
services.gitea = {
enable = true;
package = cfg.package;
database.type = "sqlite3";
appName = "Gitea";
# https://github.com/NixOS/nixpkgs/issues/235442#issuecomment-1574329453
lfs.enable = true;
settings = {
server = {
DOMAIN = cfg.domain;
ROOT_URL = "https://${cfg.domain}";
SSH_PORT = 22;
HTTP_PORT = 3001;
};
service = {
DISABLE_REGISTRATION = true;
};
};
};
};
}

View File

@ -1,59 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.headscale;
in
{
options.pepe.services.headscale = with lib; {
enable = mkEnableOption "Enable Headscale";
package = mkPackageOption pkgs "headscale" { };
domain = mkOption {
type = types.str;
default = null;
description = "Domain for the Headscale service.";
};
host = mkOption {
type = types.str;
default = "127.0.0.1";
description = "Host for the Headscale service.";
};
port = mkOption {
type = types.int;
default = 1194;
description = "Port for the Headscale service.";
};
settings = mkOption {
type = types.attrsOf types.anything;
default = { };
description = "Arbitrary configuration settings for Headscale.";
};
};
config = mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
services.headscale = {
enable = true;
package = cfg.package;
port = cfg.port;
settings = cfg.settings // { server_url = "https://${cfg.domain}"; };
};
pepe.core = {
firewall.openUDP = [ cfg.port ];
vhost.hosts.${cfg.domain} = {
locations."/" = {
host = cfg.host;
port = cfg.port;
allowWAN = true;
proxyWebsockets = true;
extraConfig = ''
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
proxy_buffering off;
'';
};
};
};
};
}

View File

@ -1,29 +0,0 @@
{ config, lib, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.homeassistant;
in
{
options.pepe.services.homeassistant = with lib; {
enable = mkEnableOption "Enable Home Assistant";
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
host = "10.0.0.251";
port = 8123;
allowLAN = true;
allowVPN = true;
allowWAN = true;
proxyWebsockets = true;
};
};
};
}

View File

@ -1,42 +0,0 @@
{ config, pkgs, lib, ... }:
let
cfg = config.pepe.services.immich;
in
{
options.pepe.services.immich = with lib; {
enable = mkEnableOption "Enable immich";
package = mkPackageOption pkgs "immich" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = lib.mkIf cfg.enable {
services = {
immich = {
enable = true;
package = cfg.package;
# accelerationDevices = null;
};
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
host = "[::1]";
port = config.services.immich.port;
allowLAN = true;
allowVPN = true;
allowWAN = true;
proxyWebsockets = true;
extraConfig = ''
# allow large file uploads
client_max_body_size 50000M;
'';
};
};
users.users.immich.extraGroups = [ "video" "render" "media" "nextcloud" ];
};
}

View File

@ -1,63 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf mkForce;
cfg = config.pepe.services.jellyfin;
in
{
options.pepe.services.jellyfin = with lib; {
enable = mkEnableOption "Enable jellyfin";
package = mkPackageOption pkgs "jellyfin" { };
domain = mkOption {
type = types.str;
default = null;
};
tmpfsSize = mkOption {
type = types.str;
default = "20G";
description = "Size of the tmpfs mount for Jellyfin";
};
};
config = mkIf cfg.enable {
# needed since StateDirectory does not accept symlinks
systemd.services.jellyfin.serviceConfig.StateDirectory = mkForce "";
pepe.core.vhost.hosts.${cfg.domain} = {
locations = {
"/" = {
port = 8096;
allowLAN = true;
allowVPN = true;
};
"/socket" = {
port = 8096;
allowLAN = true;
allowVPN = true;
proxyWebsockets = true;
};
};
};
services.jellyfin = {
enable = true;
group = "media";
package = cfg.package;
};
users.groups = {
video.members = [ "jellyfin" ];
render.members = [ "jellyfin" ];
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ "jellyfin" ];
fileSystems."/tmp/jellyfin" = {
device = "none";
fsType = "tmpfs";
options = [ "defaults" "size=${cfg.tmpfsSize}" "uid=jellyfin" ];
};
};
}

View File

@ -1,32 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.jellyseer;
in
{
options.pepe.services.jellyseer = with lib; {
enable = mkEnableOption "Enable jellyseer";
package = mkPackageOption pkgs "jellyseerr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.jellyseerr = {
enable = true;
package = cfg.package;
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = config.services.jellyseerr.port;
allowLAN = true;
allowVPN = true;
};
};
};
}

View File

@ -1,47 +0,0 @@
{ config, pkgs, lib, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.languagetool;
ngramDataDir = pkgs.symlinkJoin {
name = "languagetool-ngrams";
paths = builtins.attrValues pkgs.langtoolPkgs;
};
in
{
options.pepe.services.languagetool = with lib; {
enable = mkEnableOption "Enable LanguageTool";
package = mkPackageOption pkgs "languagetool" { };
fasttextPackage = mkPackageOption pkgs "fasttext" {};
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
pepe.core = {
vhost.hosts.${cfg.domain} = {
locations."/" = {
port = config.services.languagetool.port;
allowLAN = true;
allowVPN = true;
allowWAN = true;
};
};
};
services.languagetool = {
enable = true;
package = cfg.package;
allowOrigin = cfg.domain;
settings = {
languageModel = "${ngramDataDir}/share/languagetool/ngrams/";
fasttextModel = "${pkgs.langtoolPkgs.fasttextmodel}/share/languagetool/fasttextmodel/lid.176.bin";
fasttextBinary = "${cfg.fasttextPackage}/bin/fasttext";
};
};
};
}

View File

@ -1,36 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.lidarr;
in
{
options.pepe.services.lidarr = with lib; {
enable = mkEnableOption "Enable lidarr";
package = mkPackageOption pkgs "lidarr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.lidarr = {
enable = true;
package = cfg.package;
group = "media";
};
pepe.core.vhost.hosts.${cfg.domain} = with config.pepe.core.network; {
dnsInterfaces = [ interfaceTypes.lan interfaceTypes.vpn ];
locations."/" = {
port = 8686;
allowLAN = true;
allowVPN = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ "lidarr" ];
};
}

View File

@ -1,138 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf mkEnableOption mkPackageOption mkOption types optionalAttrs mkMerge;
cfg = config.pepe.services.llm;
in
{
options.pepe.services.llm = {
enable = mkEnableOption "Enable LLM backend service (Ollama)";
package = mkPackageOption pkgs "ollama" { };
backendDomain = mkOption {
type = types.nullOr types.str;
default = null;
description = "Domain for Ollama backend. If null, no vhost is created for the backend.";
};
acceleration = mkOption {
type = types.enum [ "none" "cuda" "rocm" ];
default = "none";
description = "Acceleration type for Ollama";
};
environmentVariables = mkOption {
type = types.attrsOf types.str;
default = {
OLLAMA_FLASH_ATTENTION = "1";
OLLAMA_NUM_PARALLEL = "2";
OLLAMA_KV_CACHE_TYPE = "q8_0";
};
description = "Environment variables for Ollama";
};
frontend = {
enable = mkEnableOption "Enable LLM frontend service (Open WebUI)"; # Defaults to false
package = mkPackageOption pkgs "open-webui" { };
domain = mkOption {
type = types.nullOr types.str;
default = null;
description = "Domain for Open WebUI frontend. If null, no vhost is created for the frontend.";
};
};
};
config = mkMerge [
# Combined environment packages
{
environment.systemPackages =
(if cfg.enable then [ cfg.package ] else [ ]) ++
(if cfg.enable && cfg.frontend.enable then [ cfg.frontend.package ] else [ ]);
}
# Backend Ollama Service Configuration
(mkIf cfg.enable {
services.ollama = {
enable = true;
package = cfg.package;
acceleration = cfg.acceleration;
environmentVariables = cfg.environmentVariables;
};
services.docling-serve = {
enable = true;
package = pkgs.unstablePkgs.docling-serve;
};
pepe.core.vhost.hosts = optionalAttrs (cfg.backendDomain != null) {
"${cfg.backendDomain}" = {
locations."/" = {
host = config.services.ollama.host;
port = config.services.ollama.port;
allowLAN = true;
allowVPN = true;
allowWAN = true;
recommendedProxySettings = false;
extraConfig = ''
proxy_buffering off;
proxy_read_timeout 600s; # Ollama can take time to respond
proxy_set_header Host localhost:${toString config.services.ollama.port};
'';
};
locations."/docling/" = {
host = config.services.docling-serve.host;
port = config.services.docling-serve.port;
allowLAN = true;
allowVPN = true;
allowWAN = true;
recommendedProxySettings = false;
extraConfig = ''
proxy_buffering off;
proxy_read_timeout 600s;
rewrite ^/docling/(.*) /$1 break;
'';
};
};
};
})
# Frontend Open WebUI and Tika Service Configuration
(mkIf (cfg.enable && cfg.frontend.enable) {
services.open-webui = {
enable = true;
package = cfg.frontend.package;
};
services.docling-serve = {
enable = true;
package = pkgs.unstablePkgs.docling-serve;
};
pepe.core.vhost.hosts = optionalAttrs (cfg.frontend.domain != null) {
"${cfg.frontend.domain}" = {
locations."/" = {
host = config.services.open-webui.host;
port = config.services.open-webui.port;
allowLAN = true;
allowVPN = true;
allowWAN = true;
proxyWebsockets = true;
};
};
"${cfg.backend.domain}" = {
locations."/docling" = {
host = config.services.docling-serve.host;
port = config.services.docling-serve.port;
allowLAN = true;
allowVPN = true;
allowWAN = true;
};
};
};
})
];
}

View File

@ -1,35 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.minio;
in
{
options.pepe.services.minio = with lib; {
enable = mkEnableOption "Enable MinIO S3-compatible object storage";
package = mkPackageOption pkgs "minio" {};
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.minio = {
enable = true;
package = cfg.package;
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = 9000;
allowLAN = true;
allowVPN = true;
extraConfig = ''
client_max_body_size 500M;
'';
};
};
};
}

View File

@ -1,170 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.navidrome;
beetsConfigTemplate = pkgs.writeText "beets.conf.template" ''
directory: ${cfg.musicFolder}
plugins: embedart fetchart scrub lastgenre chroma
import:
write: yes
move: yes
incremental: yes
from_scratch: yes
group_albums: yes
log: ${cfg.musicFolder}/beets.log
original_date: yes
discogs:
user_token: "__DISCOGS_TOKEN__"
paths:
default: $albumartist/$album ($original_year)/$track - $title
singleton: $artist/singles/$title
match:
strong_rec_thresh: 0.20
musicbrainz:
extra_tags: [year]
lastfm:
user: peperunas
api_key: "__LASTFM_KEY__"
embedart:
auto: yes
fetchart:
auto: yes
lastfmkey: "__LASTFM_KEY__"
enforce_ratio: 0.5%
scrub:
auto: yes
lastgenre:
auto: yes
count: 5
min_weight: 10
prefer_specific: yes
force: yes
chroma:
auto: yes
'';
preStartBeetsDecrypt = ''
set -eu
sed \
-e "s|__DISCOGS_TOKEN__|$(cat ${config.age.secrets.discogs.path})|g" \
-e "s|__LASTFM_KEY__|$(cat ${config.age.secrets.lastfmKey.path})|g" \
${beetsConfigTemplate} > /run/beets.conf
'';
navidromeWrapped = pkgs.writeShellScriptBin "navidrome-wrapped" ''
set -eu
${preStartNavidromeDecrypt}
exec ${cfg.package}/bin/navidrome "$@"
'';
preStartNavidromeDecrypt = ''
set -eu
# export ND_LASTFM_APIKEY=$(cat ${config.age.secrets.lastfmKey.path})
# export ND_LASTFM_SECRET=$(cat ${config.age.secrets.lastfmSecret.path})
# export ND_SPOTIFY_ID=$(cat ${config.age.secrets.spotifyID.path})
# export ND_SPOTIFY_SECRET=$(cat ${config.age.secrets.spotifySecret.path})
'';
in
{
options.pepe.services.navidrome = with lib; {
enable = mkEnableOption "Enable navidrome";
enableBeets = mkEnableOption "Enable beets";
package = mkPackageOption pkgs "navidrome" { };
beetsPackage = mkPackageOption pkgs "beets" { };
domain = mkOption {
type = types.str;
default = null;
};
musicFolder = mkOption {
type = types.str;
default = "/media/Music";
description = "Path to the music library";
};
settings = mkOption {
type = types.attrs;
default = { };
description = "Additional settings for Navidrome";
};
};
config = mkIf cfg.enable {
services.navidrome = {
enable = true;
package = navidromeWrapped;
settings = {
MusicFolder = cfg.musicFolder;
LastFM.enable = true;
EnableGravatar = true;
LogLevel = "WARN";
} // cfg.settings;
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = config.services.navidrome.settings.Port;
allowLAN = true;
allowVPN = true;
allowWAN = true;
};
};
age.secrets = {
discogs = mkIf cfg.enableBeets {
file = ../../../secrets/discogs.age;
};
lastfmKey = {
file = ../../../secrets/lastfm-key.age;
};
lastfmSecret = {
file = ../../../secrets/lastfm-key.age;
};
spotifyID = {
file = ../../../secrets/spotify-id.age;
};
spotifySecret = {
file = ../../../secrets/spotify-secret.age;
};
};
systemd.services = {
"navidrome".preStart = preStartNavidromeDecrypt;
} // mkIf cfg.enableBeets {
"beets-update" = {
enable = true;
preStart = preStartBeetsDecrypt;
before = [ "beets-import.service" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${cfg.beetsPackage}/bin/beet -c /run/beets.conf update";
};
};
"beets-import" = {
enable = true;
path = [ pkgs.imagemagick ];
requires = [ "beets-update.service" ];
after = [ "beets-update.service" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${cfg.beetsPackage}/bin/beet -c /run/beets.conf import --flat -q ${cfg.musicFolder}";
};
startAt = "weekly";
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ "navidrome" ];
};
}

View File

@ -1,35 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.nzbget;
in
{
options.pepe.services.nzbget = with lib; {
enable = mkEnableOption "Enable nzbget";
package = mkPackageOption pkgs "nzbget" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.nzbget = {
enable = true;
package = cfg.package;
group = "media";
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = 6789;
allowLAN = true;
allowVPN = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ "nzbget" ];
};
}

View File

@ -1,35 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.prowlarr;
in
{
options.pepe.services.prowlarr = with lib; {
enable = mkEnableOption "Enable prowlarr";
package = mkPackageOption pkgs "prowlarr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.prowlarr = {
enable = true;
package = cfg.package;
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = 9696;
allowLAN = true;
allowVPN = true;
proxyWebsockets = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ "prowlarr" ];
};
}

View File

@ -1,34 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.radarr;
in
{
options.pepe.services.radarr = with lib; {
enable = mkEnableOption "Enable radarr";
package = mkPackageOption pkgs "radarr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.radarr = {
enable = true;
package = cfg.package;
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = 7878;
allowLAN = true;
allowVPN = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ config.services.radarr.group ];
};
}

View File

@ -1,48 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.redlib;
in
{
options.pepe.services.redlib = with lib; {
enable = mkEnableOption "Enable Redlib";
package = mkPackageOption pkgs "redlib" { };
domain = mkOption {
type = types.str;
default = null;
};
settings = mkOption {
type = types.attrsOf types.str;
default = {
REDLIB_ROBOTS_DISABLE_INDEXING = "on";
REDLIB_DEFAULT_THEME = "dracula";
REDLIB_DEFAULT_SHOW_NSFW = "on";
REDLIB_DEFAULT_BLUR_NSFW = "off";
REDLIB_DEFAULT_USE_HLS = "on";
REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION = "on";
};
description = "Environment variables for Redlib configuration";
};
};
config = mkIf cfg.enable {
services.redlib = {
enable = true;
port = 9090;
package = cfg.package;
};
systemd.services.redlib.environment = cfg.settings;
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = config.services.redlib.port;
allowLAN = true;
allowVPN = true;
allowWAN = true;
};
};
};
}

View File

@ -1,34 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.sonarr;
in
{
options.pepe.services.sonarr = with lib; {
enable = mkEnableOption "Enable sonarr";
package = mkPackageOption pkgs "sonarr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.sonarr = {
enable = true;
package = cfg.package;
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = 8989;
allowLAN = true;
allowVPN = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ config.services.sonarr.group ];
};
}

View File

@ -1,34 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.whisparr;
in
{
options.pepe.services.whisparr = with lib; {
enable = mkEnableOption "Enable whisparr";
package = mkPackageOption pkgs "whisparr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.whisparr = {
enable = true;
package = cfg.package;
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = config.services.whisparr.settings.server.port;
allowLAN = true;
allowVPN = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ config.services.whisparr.group ];
};
}

View File

@ -1,25 +1,4 @@
{ config, ... }: { ... }: {
security.acme.acceptTerms = true;
let security.acme.email = "sysadmin@giugl.io";
giuglioDomain = "giugl.io";
in
{
age.secrets.cloudflare = {
file = ../secrets/cloudflare.age;
owner = "acme";
};
security.acme = {
acceptTerms = true;
certs.${giuglioDomain} =
{
dnsProvider = "cloudflare";
environmentFile = config.age.secrets.cloudflare.path;
extraDomainNames = [ "*.${giuglioDomain}" ];
};
defaults = {
email = "letsencrypt@depasquale.giugl.io";
dnsProvider = "cloudflare";
environmentFile = config.age.secrets.cloudflare.path;
};
};
} }

View File

@ -1,9 +1,7 @@
{ config, pkgs, lib, ... }: { config, pkgs, variables, lib, ... }:
{ {
boot.tmp = { boot.tmpOnTmpfs = true;
useTmpfs = true;
};
console = { console = {
keyMap = "us"; keyMap = "us";
@ -13,7 +11,8 @@
i18n.defaultLocale = "en_US.UTF-8"; i18n.defaultLocale = "en_US.UTF-8";
nix = { nix = {
settings.auto-optimise-store = true; autoOptimiseStore = true;
package = pkgs.nixUnstable;
extraOptions = '' extraOptions = ''
experimental-features = nix-command flakes experimental-features = nix-command flakes
''; '';
@ -25,11 +24,10 @@
}; };
}; };
nixpkgs = { config = { allowUnfree = true; }; };
fonts = { fonts.fontconfig.enable = true;
fontconfig.enable = true; fonts.fonts = with pkgs; [ cascadia-code victor-mono ];
packages = with pkgs; [ cascadia-code victor-mono ];
};
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
file file
@ -42,6 +40,7 @@
glances glances
tcpdump tcpdump
restic restic
neovim
tmux tmux
parted parted
unzip unzip
@ -50,7 +49,5 @@
nmap nmap
ripgrep ripgrep
jq jq
helix
poetry
]; ];
} }

View File

@ -22,7 +22,7 @@
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
gnomeExtensions.appindicator gnomeExtensions.appindicator
gnomeExtensions.sound-output-device-chooser gnomeExtensions.sound-output-device-chooser
pkgs.unstablePkgs.gnomeExtensions.pop-shell pkgs.unstable.gnomeExtensions.pop-shell
]; ];
security.pam.services.gdm.enableGnomeKeyring = true; security.pam.services.gdm.enableGnomeKeyring = true;
} }

View File

@ -1,17 +0,0 @@
{ pkgs, ... }:
let
lib = pkgs.lib;
configDir = "$HOME/.config/aichat";
in
{
home = {
sessionVariables = {
AICHAT_CONFIG_DIR = configDir;
};
packages = [ pkgs.aichat ];
file.".config/aichat/config.yaml".text = lib.readFile ./aichat/config.yaml;
file.".config/aichat/roles/commitmessage.md".text = lib.readFile ./aichat/roles/commitmessage.md;
file.".config/aichat/roles/createpr.md".text = lib.readFile ./aichat/roles/createpr.md;
};
}

View File

@ -1,20 +0,0 @@
clients:
- type: openai-compatible
name: ollama
api_base: https://ollama.giugl.io/v1
models:
- name: pino
max_input_tokens: 8192
max_output_tokens: 16000
- name: pino-coder
max_input_tokens: 16000
max_output_tokens: 16000
- name: pino-embed
type: embedding
default_chunk_size: 512
max_batch_size: 100
rag_embedding_model: ollama:pino-embed
rag_top_k: 5

View File

@ -1,98 +0,0 @@
---
model: ollama:pino-coder
temperature: 0
---
You are an expert software developer and a master of Git version control. Your sole purpose is to analyze a `git diff` provided by the user and write an impeccable commit message that strictly follows the Conventional Commits specification v1.0.0.
You must adhere to the following rules without exception:
1. **Structure**: The commit message must be structured as follows:
```
<type>[optional scope]: <subject>
[optional body]
[optional footer(s)]
```
2. **Type**: The `<type>` must be one of the following allowed values:
* **feat**: A new feature for the user.
* **fix**: A bug fix for the user.
* **docs**: Documentation only changes.
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc).
* **refactor**: A code change that neither fixes a bug nor adds a feature.
* **perf**: A code change that improves performance.
* **test**: Adding missing tests or correcting existing tests.
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm).
* **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs).
* **chore**: Other changes that don't modify src or test files.
3. **Scope**: The `(scope)` is optional. If used, it must be a noun describing the section of the codebase affected.
4. **Subject**: The `<subject>` line:
* Must be written in the imperative mood (e.g., "Add feature", not "Added feature" or "Adds feature").
* Must not be capitalized.
* Must not end with a period.
5. **Body**: The `[body]` is optional but highly encouraged for anything other than trivial changes.
* It must be separated from the subject by one blank line.
* It must explain the "what" and "why" of the change, not the "how".
6. **Output**: Your final output must be ONLY the raw text of the commit message and nothing else. Do not include any explanations, apologies, or surrounding markdown like ` ``` `.
---
### EXAMPLE
**INPUT GIT DIFF:**
```diff
diff --git a/.config/aichat/roles/commit.md b/.config/aichat/roles/commit.md
new file mode 100644
index 0000000..d67e2a9
--- /dev/null
+++ b/.config/aichat/roles/commit.md
@@ -0,0 +1,52 @@
+# The name of the role.
+name: commit
+
+# The author of the role.
+author: Gemini
+
+# A description of the role.
+description: Generates a conventional commit message from a git diff.
+
+# The model to use.
+model: qwen2:14b-instruct
+
+# The temperature to use.
+temperature: 0.1
+
+# The prompt type.
+prompt: embedded
+
+# The prompt text.
+---
+You are an expert software developer and a master of Git version control. Your sole purpose is to analyze a `git diff` provided by the user and write an impeccable commit message that strictly follows the Conventional Commits specification v1.0.0.
+
+You must adhere to the following rules without exception:
+
+1. **Structure**: The commit message must be structured as follows:
+ ```
+ <type>[optional scope]: <subject>
+
+ [optional body]
+
+ [optional footer(s)]
+ ```
+
+2. **Type**: The `<type>` must be one of the following allowed values:
+ * **feat**: A new feature for the user.
+ * **fix**: A bug fix for the user.
+ * **docs**: Documentation only changes.
+ * **style**: Changes that do not affect the meaning of the code.
+ * **refactor**: A code change that neither fixes a bug nor adds a feature.
+ * **perf**: A code change that improves performance.
+ * **test**: Adding missing tests or correcting existing tests.
+ * **build**: Changes that affect the build system or external dependencies.
+ * **ci**: Changes to our CI configuration files and scripts.
+ * **chore**: Other changes that don't modify src or test files.

View File

@ -1,626 +0,0 @@
You are a technical documentation assistant specializing in creating clear, concise PR messages. Given a git diff, you will:
1. Analyze the structural changes to identify:
- New components/interfaces
- Architectural changes
- Behavioral modifications
- Impact on existing systems
2. Create a PR message following this format:
<type>(<scope>): <description>
[PROSE DESCRIPTION explaining the changes and their purpose]
---
[OPTIONAL SECTIONS like "Structs:", "Interfaces:", "Methods:", etc. when relevant]
Each section should:
- Use bullet points for clarity
- Bold key terms
- Include brief descriptions
- Focus on significant changes
- Exclude trivial details (imports, formatting)
### INPUT:
```
diff --git a/pkg/cli/cli.go b/pkg/cli/cli.go
index 9aee19c..9863f0b 100644
--- a/pkg/cli/cli.go
+++ b/pkg/cli/cli.go
@@ -1,21 +1,13 @@
package cli
import (
- "context"
- "net/http"
"strings"
- "time"
"github.com/sanity-io/litter"
"github.com/spf13/cobra"
- "github.com/org/Core/pkg/logging"
"github.com/org/Core/pkg/structs"
- "github.com/org/example/internal/storage"
- "github.com/org/example/pkg/browser"
"github.com/org/example/pkg/config"
- "github.com/org/example/pkg/crawler"
"github.com/org/example/pkg/errors"
- "github.com/org/example/pkg/models"
cookieflags "github.com/org/example/pkg/plugins/cookieflags"
cookiescope "github.com/org/example/pkg/plugins/cookiescope"
corsmisconfig "github.com/org/example/pkg/plugins/cors"
@@ -124,7 +116,18 @@ func NewRootCmd() (*cobra.Command, error) {
logger.Tracef("Crawler Configuration:
=============
%s
=============
", litter.Sdump(crawlConfig))
logger.Tracef("Vulnerability Scan Configuration:
=============
%s
=============
", litter.Sdump(pluginsConfig))
- return StartScan(sysConfig, crawlConfig, pluginsConfig)
+ manager, err := NewManagerBuilder(sysConfig, crawlConfig, pluginsConfig).Build()
+
+ if err != nil {
+ return err
+ }
+
+ err = manager.Start()
+ if err != nil {
+ return err
+ }
+
+ return manager.Stop()
},
}
@@ -186,113 +189,6 @@ func parseConfigFiles(crawlConfPath, systemConfPath, pluginConfPath string) (*co
return crawlConfig, sysConfig, vulnConfig, nil
}
-func StartScan(sysCfg *config.SystemConfiguration, //nolint:cyclop
- crawlCfg *config.CrawlConfiguration,
- pluginCfg *config.PluginsConfiguration,
-) error {
- // Initialize shared components
- logger := logging.NewLoggerBuilder().Build()
- metrics := &models.Metrics{}
- // Initialize the browser session
- browserSession, err := browser.New(logger, crawlCfg)
- if err != nil {
- return errors.ErrFailedExecution.WrapWithNoMessage(err)
- }
- defer browserSession.Close()
- if err := browserSession.Start(context.Background()); err != nil {
- return errors.ErrFailedExecution.WrapWithNoMessage(err)
- }
- // Create custom HTTP client if needed
- // TODO: Change and centralise
- // see https://github.com/org/example/issues/436
- customClient := &http.Client{
- Timeout: time.Second * 30,
- // Add other custom configurations...
- }
- // Iniialize storage once
- scanStorage, err := storage.NewScanStorage(sysCfg, metrics, logger)
- if err != nil {
- return errors.ErrStorageSetup.WrapWithNoMessage(err)
- }
- // Start batch session before any operations
- err = scanStorage.BatchSession.Start()
- if err != nil {
- return errors.ErrStorageSetup.WrapWithNoMessage(err)
- }
- // Initialize crawler with all options
- crawler, err := crawler.New(
- crawlCfg,
- sysCfg,
- crawler.WithStorage(scanStorage.Database, scanStorage.BatchSession),
- crawler.WithLogger(logger),
- crawler.WithMetrics(metrics),
- crawler.WithHTTPClient(customClient),
- crawler.WithBrowser(browserSession),
- )
- if err != nil {
- return errors.ErrFailedExecution.WrapWithNoMessage(err)
- }
- // Initialize scanner with shared components
- scan, err := scanner.NewScanner(
- sysCfg,
- crawlCfg,
- pluginCfg,
- browserSession,
- scanner.WithStorage(scanStorage),
- scanner.WithLogger(logger),
- scanner.WithHTTPClient(customClient),
- )
- if err != nil {
- return errors.ErrFailedExecution.WrapWithNoMessage(err)
- }
- err = initializePluginsFromConfig(scan, pluginCfg)
- if err != nil {
- return errors.ErrInitialisePlugin.WrapWithNoMessage(err)
- }
- output, err := crawler.Start()
- if err != nil {
- crawler.Close()
- return errors.ErrFailedExecution.WrapWithNoMessage(err)
- }
- // Add this: Stop scanner before waiting for batch operations
- scan.Stop()
- logger.Debugf("Crawl completed with metrics: %+v", output.Metrics)
- if sysCfg.ShouldExportMetrics() {
- err := output.Metrics.ToJSONFile()
- if err != nil {
- return errors.ErrJSONMarshalling.WrapWithNoMessage(err)
- }
- }
- if output.BenchmarkResults != nil {
- logger.Debugf("Benchmark results: %+v", output.BenchmarkResults)
- }
- scanStorage.BatchSession.Wait()
- err = scanStorage.BatchSession.Stop()
- if err != nil {
- return errors.ErrStorageSetup.WrapWithNoMessage(err)
- }
- crawler.Close()
- return nil
-}
// BuildPluginsFromConfig creates plugin instances based on configuration
func BuildPluginsFromConfig(config *config.PluginsConfiguration) []*structs.Plugin {
enabledPlugins := []*structs.Plugin{}
diff --git a/pkg/cli/interface.go b/pkg/cli/interface.go
new file mode 100644
index 0000000..4d68a45
--- /dev/null
+++ b/pkg/cli/interface.go
@@ -0,0 +1,10 @@
+package cli
+
+// Scanner represents the core scanning operations lifecycle
+type ScanManagerInterface interface {
+ // Start initializes and begins the scanning process
+ Start() error
+
+ // Stop gracefully terminates all scanning operations
+ Stop() error
+}
diff --git a/pkg/cli/manager.go b/pkg/cli/manager.go
new file mode 100644
index 0000000..3f2a8fc
--- /dev/null
+++ b/pkg/cli/manager.go
@@ -0,0 +1,277 @@
+package cli
+
+import (
+ "context"
+ "net/http"
+
+ "github.com/org/Core/pkg/logging"
+ "github.com/org/example/internal/storage"
+ "github.com/org/example/pkg/browser"
+ "github.com/org/example/pkg/config"
+ "github.com/org/example/pkg/crawler"
+ "github.com/org/example/pkg/errors"
+ "github.com/org/example/pkg/models"
+ "github.com/org/example/pkg/scanner"
+)
+
+var _ ScanManagerInterface = (*ScanManager)(nil)
+
+type ScanManager struct {
+ browser *browser.Session
+ crawler *crawler.Crawler
+ scanner *scanner.Scanner
+ storage *storage.ScanStorage
+ logger *logging.Logger
+ metrics *models.Metrics
+ httpClient *http.Client
+
+ sysCfg *config.SystemConfiguration
+ crawlCfg *config.CrawlConfiguration
+ pluginCfg *config.PluginsConfiguration
+}
+
+// ScanManagerBuilder handles the construction of a ScanManager
+type ScanManagerBuilder struct {
+ manager *ScanManager
+ err error
+}
+
+// NewManagerBuilder creates a new builder for ScanManager
+func NewManagerBuilder(sysCfg *config.SystemConfiguration,
+ crawlCfg *config.CrawlConfiguration,
+ pluginCfg *config.PluginsConfiguration,
+) *ScanManagerBuilder {
+ builder := &ScanManagerBuilder{
+ manager: &ScanManager{
+ logger: logging.NewLoggerBuilder().Build(),
+ metrics: &models.Metrics{},
+ httpClient: &http.Client{},
+ },
+ }
+
+ if sysCfg == nil || crawlCfg == nil || pluginCfg == nil {
+ builder.err = errors.ErrInvalidArgument.New("configurations cannot be nil")
+
+ return builder
+ }
+
+ builder.manager.sysCfg = sysCfg
+ builder.manager.crawlCfg = crawlCfg
+ builder.manager.pluginCfg = pluginCfg
+
+ return builder
+}
+
+// WithLogger sets a custom logger
+func (b *ScanManagerBuilder) WithLogger(logger *logging.Logger) *ScanManagerBuilder {
+ if b.err != nil {
+ return b
+ }
+
+ if logger == nil {
+ b.err = errors.ErrInvalidArgument.New("logger cannot be nil")
+
+ return b
+ }
+
+ b.manager.logger = logger
+
+ return b
+}
+
+// WithMetrics sets custom metrics
+func (b *ScanManagerBuilder) WithMetrics(metrics *models.Metrics) *ScanManagerBuilder {
+ if b.err != nil {
+ return b
+ }
+
+ if metrics == nil {
+ b.err = errors.ErrInvalidArgument.New("metrics cannot be nil")
+
+ return b
+ }
+
+ b.manager.metrics = metrics
+
+ return b
+}
+
+// Build creates the ScanManager instance
+func (b *ScanManagerBuilder) Build() (*ScanManager, error) {
+ if b.err != nil {
+ return nil, b.err
+ }
+
+ return b.manager, nil
+}
+
+func (sm *ScanManager) initBrowser(crawlCfg *config.CrawlConfiguration) error {
+ var err error
+
+ sm.browser, err = browser.New(sm.logger, crawlCfg)
+ if err != nil {
+ return errors.ErrFailedExecution.WrapWithNoMessage(err)
+ }
+
+ if err := sm.browser.Start(context.Background()); err != nil {
+ sm.browser.Close()
+
+ return errors.ErrFailedExecution.WrapWithNoMessage(err)
+ }
+
+ return nil
+}
+
+func (sm *ScanManager) initStorage(sysCfg *config.SystemConfiguration) error {
+ var err error
+
+ storage, err := storage.NewScanStorage(sysCfg, sm.metrics, sm.logger)
+ if err != nil {
+ return errors.ErrStorageSetup.WrapWithNoMessage(err)
+ }
+
+ sm.storage = &storage
+
+ err = sm.storage.BatchSession.Start()
+ if err != nil {
+ return errors.ErrStorageSetup.WrapWithNoMessage(err)
+ }
+
+ return nil
+}
+
+func (sm *ScanManager) initCrawler(sysCfg *config.SystemConfiguration, crawlCfg *config.CrawlConfiguration) error {
+ var err error
+
+ sm.crawler, err = crawler.New(
+ crawlCfg,
+ sysCfg,
+ crawler.WithStorage(sm.storage.Database, sm.storage.BatchSession),
+ crawler.WithLogger(sm.logger),
+ crawler.WithMetrics(sm.metrics),
+ crawler.WithHTTPClient(sm.httpClient),
+ crawler.WithBrowser(sm.browser),
+ )
+
+ if err != nil {
+ return errors.ErrFailedExecution.WrapWithNoMessage(err)
+ }
+
+ return nil
+}
+
+func (sm *ScanManager) initScanner() error {
+ var err error
+
+ sm.scanner, err = scanner.NewScanner(
+ sm.sysCfg,
+ sm.crawlCfg,
+ sm.pluginCfg,
+ sm.browser,
+ scanner.WithStorage(*sm.storage),
+ scanner.WithLogger(sm.logger),
+ scanner.WithHTTPClient(sm.httpClient),
+ )
+
+ if err != nil {
+ return errors.ErrFailedExecution.WrapWithNoMessage(err)
+ }
+
+ err = initializePluginsFromConfig(sm.scanner, sm.pluginCfg)
+ if err != nil {
+ return errors.ErrInitialisePlugin.WrapWithNoMessage(err)
+ }
+
+ return nil
+}
+
+func (sm *ScanManager) cleanup() error {
+ var errs []error
+
+ if sm.crawler != nil {
+ sm.crawler.Close()
+ }
+
+ if sm.scanner != nil {
+ sm.scanner.Stop()
+ }
+
+ if sm.storage != nil && sm.storage.BatchSession != nil {
+ sm.storage.BatchSession.Wait()
+
+ err := sm.storage.BatchSession.Stop()
+ if err != nil {
+ errs = append(errs, errors.ErrStorageSetup.WrapWithNoMessage(err))
+ }
+ }
+
+ if sm.browser != nil {
+ sm.browser.Close()
+ }
+
+ if len(errs) > 0 {
+ return errors.ErrFailedExecution.New("multiple cleanup errors occurred: %v", errs)
+ }
+
+ return nil
+}
+
+func (sm *ScanManager) Start() error {
+ err := sm.initBrowser(sm.crawlCfg)
+ if err != nil {
+ return err
+ }
+
+ err = sm.initStorage(sm.sysCfg)
+ if err != nil {
+ _ = sm.cleanup()
+
+ return err
+ }
+
+ err = sm.initCrawler(sm.sysCfg, sm.crawlCfg)
+ if err != nil {
+ _ = sm.cleanup()
+
+ return err
+ }
+
+ err = sm.initScanner()
+ if err != nil {
+ _ = sm.cleanup()
+
+ return err
+ }
+
+ // Start the crawl
+ output, err := sm.crawler.Start()
+ if err != nil {
+ _ = sm.cleanup()
+
+ return errors.ErrFailedExecution.WrapWithNoMessage(err)
+ }
+
+ sm.logger.Debugf("Crawl completed with metrics: %+v", output.Metrics)
+
+ if sm.sysCfg.ShouldExportMetrics() {
+ err := output.Metrics.ToJSONFile()
+
+ if err != nil {
+ return errors.ErrJSONMarshalling.WrapWithNoMessage(err)
+ }
+ }
+
+ if output.BenchmarkResults != nil {
+ sm.logger.Debugf("Benchmark results: %+v", output.BenchmarkResults)
+ }
+
+ return nil
+}
+
+func (sm *ScanManager) Stop() error {
+ if sm == nil {
+ return nil
+ }
+
+ return sm.cleanup()
+}
diff --git a/pkg/scanner/scanner.go b/pkg/scanner/scanner.go
index c0a104f..6ef620a 100644
--- a/pkg/scanner/scanner.go
+++ b/pkg/scanner/scanner.go
@@ -676,6 +676,8 @@ func (s *Scanner) Stop() {
s.wg.Wait()
close(s.initialRequestQueue)
close(s.processedRequestQueue)
+
+ instance = nil
}
// Wait blocks until all workers have finished processing.
diff --git a/tests/e2e/wallace_test.go b/tests/e2e/wallace_test.go
index 0e899e9..b8de5c8 100644
--- a/tests/e2e/wallace_test.go
+++ b/tests/e2e/wallace_test.go
@@ -550,31 +550,26 @@ type scanResults struct {
// }
// TestPlugins verifies that each plugin detects at least one vulnerability for each test path
-func TestPlugins(t *testing.T) { //nolint: paralleltest
- // Retrieve all available plugins without specific configurations
+func TestPlugins(t *testing.T) {
plugins := cli.BuildAllPlugins()
if len(plugins) == 0 {
t.Fatal("No plugins available to test")
}
- // Ensure that there are test paths to scan
if len(SiteURLs) == 0 {
t.Fatal("No site URLs available for scanning")
}
- // Iterate over each plugin and perform the test
- for _, plugin := range plugins { //nolint: paralleltest
+ for _, plugin := range plugins {
pluginName := plugin.Name()
t.Run(pluginName, func(t *testing.T) {
- // t.Parallel()
// Setup test environment for the current plugin
resultDir, dbPath, cleanup := setupTestEnvironment(t, pluginName)
defer cleanup()
- // Initialize plugin-specific configuration if needed
- // Currently, using default configurations
+ // Initialize plugin-specific configuration
pluginConfig := coreStructs.NewPluginConfigBuilder(pluginName).
WithCustomConfiguration(coreStructs.CustomPluginConfig{"name": pluginName}).
Build()
@@ -595,7 +590,6 @@ func TestPlugins(t *testing.T) { //nolint: paralleltest
// Collect test URLs from initialized plugins' metadata
testURLs := collectTestURLs(pluginInstances)
- // Skip the test if no test URLs are defined for the plugin
if len(testURLs) == 0 {
t.Skipf("No test URLs defined for plugin '%s'. Skipping test.", pluginName)
}
@@ -606,11 +600,26 @@ func TestPlugins(t *testing.T) { //nolint: paralleltest
t.Fatalf("Failed to get default test config: %v", err)
}
- // Run the scan
- if err := cli.StartScan(sysConfig, crawlConfig, vulnConfig); err != nil {
+ // Create and start scanner using the new interface
+ scanner, err := cli.NewManagerBuilder(sysConfig, crawlConfig, vulnConfig).
+ Build()
+ if err != nil {
+ t.Fatalf("Failed to create scanner: %v", err)
+ }
+
+ // Start the scan
+ if err := scanner.Start(); err != nil {
t.Fatalf("Failed to start scan: %v", err)
}
+ // Ensure scanner is stopped after test
+ defer func() {
+ if err := scanner.Stop(); err != nil {
+ t.Errorf("Failed to stop scanner: %v", err)
+ }
+ }()
+
+ // Allow time for processing
time.Sleep(2 * time.Second)
// Verify results in the database
@@ -621,11 +630,11 @@ func TestPlugins(t *testing.T) { //nolint: paralleltest
// Assert that at least one vulnerability was found per test path
expectedVulns := len(testURLs)
- assert.GreaterOrEqual(t, vulnCount, expectedVulns, "Expected at least %d vulnerabilities", expectedVulns)
- time.Sleep(5 * time.Second)
+ assert.GreaterOrEqual(t, vulnCount, expectedVulns,
+ "Expected at least %d vulnerabilities", expectedVulns)
+
// Verify that result files are present
results := readScanResults(sysConfig.GetOutputDirectory())
assert.True(t, results.resultsFound, "Expected at least one result file")
})
}
```
### OUTPUT:
```
refactor(cli): introduce ScanManager for scan lifecycle management
Introduced `ScanManager` which simplifies the scan initiation process and makes the scan modular (instead of a monolithic function that bubbles everything). The manager implements the `ScanManagerInterface`:
- **Start():** Initializes and starts the scan process.
- **Stop():** Gracefully terminates the scan process.
---
Structs:
- **ScanManager:** A new struct that manages the lifecycle of scanning components such as browser, crawler, scanner, storage, logger, and metrics.
- **ScanManagerBuilder:** A builder pattern implementation to construct a `ScanManager` with custom configurations.
```
Guidelines:
- Title should follow commit convention
- Description should be clear and business-focused
- Technical details should be organized in sections
- Use markdown formatting for readability
- Focus on architectural and behavioral changes
- Exclude trivial changes
- Keep descriptions concise but complete

View File

@ -1,77 +0,0 @@
---
model: ollama:pino-coder
temperature: 0
---
You are a documentation assistant skilled in writing professional, high-quality function documentation. Your task is to write top-level documentation for a given function, following these guidelines:
1. **High-Level Description**:
- Start with a concise, high-level summary of what the function does.
- Focus on *what* the function achieves rather than *how* it works internally.
- Avoid mentioning specific internal functions, algorithms, or implementation details.
2. **Parameters**:
- List all parameters with their names, types (if applicable), and purposes.
- Clearly describe what each parameter represents and how it is used.
3. **Return Values**:
- Explain what the function returns, including the type and purpose of each return value.
- If applicable, describe any errors or exceptions that might be returned and under what conditions.
4. **Notes**:
- Highlight any important behaviors, constraints, or side effects of the function.
- Mention whether the function modifies its inputs or is side-effect free.
5. **Style**:
- Use concise, professional language suitable for technical audiences.
- Structure the documentation clearly and logically for easy readability.
### INPUT:
```
func ReplaceAllRequestParameters(
req models.CrawlRequest, payload string,
) ([]models.CrawlRequest, error) {
requests := make([]models.CrawlRequest, 0, len(req.Path.Parameters))
for _, param := range req.Path.Parameters {
var modifiedParams []models.CrawlParameter
var err error
modifiedParams, err = ReplaceParameterValue(payload, &req, param.Name, "")
if err != nil {
return nil, err
}
modPluginRequest := SetParameters(req, modifiedParams)
requests = append(requests, modPluginRequest)
}
return requests, nil
}
```
### OUTPUT:
```
// ReplaceAllRequestParameters generates a new set of requests by replacing
// each parameter's value with the provided payload.
//
// This function iterates over all parameters in the given request and creates a new request for each parameter,
// replacing its value with the specified payload. The resulting requests are returned as a slice, leaving the original
// request unmodified.
//
// Parameters:
// - req (models.CrawlRequest): The original CrawlRequest containing parameters to be processed.
// - payload (string): The value to replace each parameter's value.
//
// Returns:
// - ([]models.CrawlRequest): A slice of new CrawlRequest instances, each with one parameter's value replaced by the payload.
// - (error): An error if any issue occurs during processing.
//
// Notes:
// - A new request is created for every parameter in the original request.
// - The function is side-effect free and does not modify the original `CrawlRequest`.
```

View File

@ -1,34 +1,208 @@
{ pkgs, ... }: { config, pkgs, ... }:
let let unstable = import <nixpkgs-unstable> { };
stablePkgs = with pkgs;[ in {
rizin imports = [ ./zsh.nix ./git.nix ];
sshfs
victor-mono
home-manager
ripgrep
ydiff
nix-index
pipenv
htop
glances
tree
direnv
devenv
] ++ lib.optional (!pkgs.stdenv.isDarwin) pastebinit;
unstablePkgs = with pkgs.unstablePkgs; [ aider-chat ];
in
{
imports = [
./zsh.nix
./git.nix
./helix.nix
];
home = { home = {
packages = stablePkgs ++ unstablePkgs; sessionVariables = {
EDITOR = "nvim";
VISUAL = "nvim";
};
};
stateVersion = "25.05"; programs.neovim = {
enable = true;
package = unstable.neovim-unwrapped;
extraPackages = with pkgs; [
nodePackages.prettier
nodePackages.pyright
rnix-lsp
rust-analyzer
cmake-format
clang-tools
rustfmt
nixfmt
];
extraConfig = ''
" syntax
syntax enable
" color themes
set termguicolors
colorscheme molokai
" wildcard mode
set wildmode=longest:full,full
" remapping popup menu (command autocompletion)
" cnoremap <expr> <up> pumvisible() ? "<C-p>" : "<up>
" cnoremap <expr> <down> pumvisible() ? "<C-n>" : "<down>"
" cnoremap <expr> <CR> pumvisible() ? "<C-e>":"<CR>"
" set line numbers
set number
" enable indent guides
let g:indent_guides_enable_on_vim_startup = 1
" Exit Vim if NERDTree is the only window left.
autocmd BufEnter * if tabpagenr('$') == 1 && winnr('$') == 1 && exists('b:NERDTree') && b:NERDTree.isTabTree() |
\ quit | endif
" Start NERDTree. If a file is specified, move the cursor to its window.
autocmd StdinReadPre * let s:std_in=1
autocmd VimEnter * NERDTree | if argc() > 0 || exists("s:std_in") | wincmd p | endif
" Start NERDTree when Vim starts with a directory argument.
autocmd StdinReadPre * let s:std_in=1
autocmd VimEnter * if argc() == 1 && isdirectory(argv()[0]) && !exists('s:std_in') |
\ execute 'NERDTree' argv()[0] | wincmd p | enew | execute 'cd '.argv()[0] | endif
" Exit Vim if NERDTree is the only window left.
autocmd BufEnter * if tabpagenr('$') == 1 && winnr('$') == 1 && exists('b:NERDTree') && b:NERDTree.isTabTree() |
\ quit | endif
" Start interactive EasyAlign in visual mode (e.g. vipga)
xmap ga <Plug>(EasyAlign)
" Start interactive EasyAlign for a motion/text object (e.g. gaip)
nmap ga <Plug>(EasyAlign)
" Highlight row and column
set cul
set cuc
" Fix for code not being aligned if between comment blocks
set cindent cinkeys-=0#
set expandtab shiftwidth=2 tabstop=2 softtabstop=2
" Enable alignment
let g:neoformat_basic_format_align = 1
" Enable tab to spaces conversion
let g:neoformat_basic_format_retab = 1
" Enable trimmming of trailing whitespace
let g:neoformat_basic_format_trim = 1
lua << EOF
------------------
-- Setup nvim-cmp.
------------------
-- Set completeopt to have a better completion experience
vim.o.completeopt = 'menuone,noselect'
local cmp = require'cmp'
cmp.setup({
snippet = {
-- REQUIRED - you must specify a snippet engine
expand = function(args)
vim.fn["vsnip#anonymous"](args.body) -- For `vsnip` users.
end,
},
mapping = {
['<C-b>'] = cmp.mapping(cmp.mapping.scroll_docs(-4), { 'i', 'c' }),
['<C-f>'] = cmp.mapping(cmp.mapping.scroll_docs(4), { 'i', 'c' }),
['<C-Space>'] = cmp.mapping(cmp.mapping.complete(), { 'i', 'c' }),
['<C-y>'] = cmp.config.disable, -- Specify `cmp.config.disable` if you want to remove the default `<C-y>` mapping.
['<C-e>'] = cmp.mapping({
i = cmp.mapping.abort(),
c = cmp.mapping.close(),
}),
['<CR>'] = cmp.mapping.confirm({ select = true }), -- Accept currently selected item. Set `select` to `false` to only confirm explicitly selected items.
},
sources = cmp.config.sources({
{ name = 'nvim_lsp' },
{ name = 'vsnip' }, -- For vsnip users.
}, {
{ name = 'buffer' },
})
})
-- Use buffer source for `/` (if you enabled `native_menu`, this won't work anymore).
cmp.setup.cmdline('/', {
sources = {
{ name = 'buffer' }
}
})
-- Use cmdline & path source for ':' (if you enabled `native_menu`, this won't work anymore).
cmp.setup.cmdline(':', {
sources = cmp.config.sources({
{ name = 'path' }
}, {
{ name = 'cmdline' }
})
})
-- Setup lspconfig.
local capabilities = require('cmp_nvim_lsp').update_capabilities(vim.lsp.protocol.make_client_capabilities())
--------------
-- LSP Servers
--------------
require'lspconfig'.pyright.setup{
capabilities = capabilities
}
require'lspconfig'.rust_analyzer.setup{
capabilities = capabilities
}
require'lspconfig'.rnix.setup{
capabilities = capabilities
}
require'lspconfig'.clangd.setup{
capabilities = capabilities,
cmd = {
"clangd",
"--background-index",
"--clang-tidy",
},
}
-------------------
-- TreeSitter setup
-------------------
require'nvim-treesitter.configs'.setup {
highlight = {
enable = true,
custom_captures = {
-- Highlight the @foo.bar capture group with the "Identifier" highlight group.
["foo.bar"] = "Identifier",
},
-- Setting this to true will run `:h syntax` and tree-sitter at the same time.
-- Set this to `true` if you depend on 'syntax' being enabled (like for indentation).
-- Using this option may slow down your editor, and you may see some duplicate highlights.
-- Instead of true it can also be a list of languages
additional_vim_regex_highlighting = false,
},
}
EOF
'';
viAlias = true;
vimAlias = true;
plugins = with unstable.vimPlugins; [
vim-nix
molokai
vim-airline
vim-airline-themes
vim-lsp
vim-indent-guides
vim-signify
nerdtree
vim-easy-align
vim-fugitive
vimtex
neoformat
nvim-lspconfig
vim-vsnip
nvim-cmp
cmp-nvim-lsp
(nvim-treesitter.withPlugins (_: unstable.tree-sitter.allGrammars))
nvim-treesitter-textobjects
];
}; };
} }

View File

@ -9,8 +9,7 @@ let
name = "guake"; name = "guake";
package = pkgs.guake; package = pkgs.guake;
}); });
in in {
{
imports = [ ./gnome.nix ]; imports = [ ./gnome.nix ];
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;

View File

@ -1,27 +0,0 @@
{ config, pkgs, lib, ... }:
{
home.packages = with pkgs; [ any-nix-shell fishPlugins.tide fishPlugins.bass fishPlugins.fzf-fish ];
programs.fish = {
enable = true;
shellAbbrs = {
"_" = "sudo";
};
shellInit = ''
# avoid macOS updates to destroy nix
# if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then
# source '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
# end
any-nix-shell fish --info-right | source
# source ${pkgs.nix-index}/etc/profile.d/command-not-found.sh
# disable autosuggestions
set -g fish_autosuggestion_enabled 0
'';
};
}

View File

@ -1,11 +1,8 @@
{ pkgs, ... }: { { config, pkgs, lib, ... }: {
programs.git = { programs.git = {
enable = true; enable = true;
userName = "Giulio De Pasquale"; userName = "Giulio De Pasquale";
userEmail = "git@depasquale.giugl.io"; userEmail = "depasquale+git@giugl.io";
delta.enable = true;
lfs.enable = true;
extraConfig = { extraConfig = {
filter."lfs" = { filter."lfs" = {
process = "git-lfs filter-process"; process = "git-lfs filter-process";
@ -14,12 +11,7 @@
smudge = "git-lfs smudge -- %f"; smudge = "git-lfs smudge -- %f";
}; };
}; };
delta.enable = true;
aliases = {
ai = ''! cd -- "''${GIT_PREFIX:-.}" && git diff HEAD -- "$@" | aichat -m ollama:pino-coder -c -r commitmessage #'';
ais = ''! cd -- "''${GIT_PREFIX:-.}" && git diff --staged HEAD -- "$@" | aichat -m ollama:pino-coder -c -r commitmessage #'';
};
}; };
home.packages = [ pkgs.git-lfs ]; home.packages = [ pkgs.git-lfs ];
} }

Some files were not shown because too many files have changed in this diff Show More