Compare commits

..

3 Commits

99 changed files with 2141 additions and 5706 deletions

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="
];
};
}

589
flake.lock generated
View File

@ -1,620 +1,63 @@
{ {
"nodes": { "nodes": {
"agenix-flake": {
"inputs": {
"darwin": "darwin",
"home-manager": "home-manager",
"nixpkgs": "nixpkgs",
"systems": "systems"
},
"locked": {
"lastModified": 1736955230,
"narHash": "sha256-uenf8fv2eG5bKM8C/UvFaiJMZ4IpUFaQxk9OH5t/1gA=",
"owner": "ryantm",
"repo": "agenix",
"rev": "e600439ec4c273cf11e06fe4d9d906fb98fa097c",
"type": "github"
},
"original": {
"owner": "ryantm",
"repo": "agenix",
"type": "github"
}
},
"cachix": {
"inputs": {
"devenv": [
"teslamate-flake",
"devenv"
],
"flake-compat": [
"teslamate-flake",
"devenv"
],
"git-hooks": [
"teslamate-flake",
"devenv"
],
"nixpkgs": "nixpkgs_4"
},
"locked": {
"lastModified": 1728672398,
"narHash": "sha256-KxuGSoVUFnQLB2ZcYODW7AVPAh9JqRlD5BrfsC/Q4qs=",
"owner": "cachix",
"repo": "cachix",
"rev": "aac51f698309fd0f381149214b7eee213c66ef0a",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "latest",
"repo": "cachix",
"type": "github"
}
},
"darwin": {
"inputs": {
"nixpkgs": [
"agenix-flake",
"nixpkgs"
]
},
"locked": {
"lastModified": 1700795494,
"narHash": "sha256-gzGLZSiOhf155FW7262kdHo2YDeugp3VuIFb4/GGng0=",
"owner": "lnl7",
"repo": "nix-darwin",
"rev": "4b9b83d5a92e8c1fbfd8eb27eda375908c11ec4d",
"type": "github"
},
"original": {
"owner": "lnl7",
"ref": "master",
"repo": "nix-darwin",
"type": "github"
}
},
"devenv": {
"inputs": {
"cachix": "cachix",
"flake-compat": "flake-compat",
"git-hooks": "git-hooks",
"nix": "nix",
"nixpkgs": "nixpkgs_6"
},
"locked": {
"lastModified": 1732298876,
"narHash": "sha256-WXlcDNMaMJeI4JO4VfQM2ZZCBJBds7j7N04tS9UjiYU=",
"owner": "cachix",
"repo": "devenv",
"rev": "741e23a22f3dc9e53075be3eaa795ea9ed6f5129",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"devenv-root": {
"flake": false,
"locked": {
"narHash": "sha256-d6xi4mKdjkX2JFicDIv5niSzpyI0m/Hnm8GGAIU04kY=",
"type": "file",
"url": "file:///dev/null"
},
"original": {
"type": "file",
"url": "file:///dev/null"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"teslamate-flake",
"devenv",
"nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1712014858,
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_2": {
"inputs": {
"nixpkgs-lib": [
"teslamate-flake",
"nixpkgs"
]
},
"locked": {
"lastModified": 1730504689,
"narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "506278e768c2a08bec68eb62932193e341f55c90",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"git-hooks": {
"inputs": {
"flake-compat": [
"teslamate-flake",
"devenv"
],
"gitignore": "gitignore",
"nixpkgs": [
"teslamate-flake",
"devenv",
"nixpkgs"
],
"nixpkgs-stable": [
"teslamate-flake",
"devenv"
]
},
"locked": {
"lastModified": 1730302582,
"narHash": "sha256-W1MIJpADXQCgosJZT8qBYLRuZls2KSiKdpnTVdKBuvU=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "af8a16fe5c264f5e9e18bcee2859b40a656876cf",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"teslamate-flake",
"devenv",
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"home-manager": { "home-manager": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
"agenix-flake",
"nixpkgs" "nixpkgs"
] ]
}, },
"locked": { "locked": {
"lastModified": 1703113217, "lastModified": 1639871969,
"narHash": "sha256-7ulcXOk63TIT2lVDSExj7XzFx09LpdSAPtvgtM7yQPE=", "narHash": "sha256-6feWUnMygRzA9tzkrfAzpA5/NBYg75bkFxnqb1DtD7E=",
"owner": "nix-community", "owner": "rycee",
"repo": "home-manager", "repo": "home-manager",
"rev": "3bfaacf46133c037bb356193bd2f1765d9dc82c1", "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": 1739757849,
"narHash": "sha256-Gs076ot1YuAAsYVcyidLKUMIc4ooOaRGO0PqTY7sBzA=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "9d3d080aec2a35e05a15cedd281c2384767c2cfe",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "release-24.11",
"repo": "home-manager",
"type": "github"
}
},
"libgit2": {
"flake": false,
"locked": {
"lastModified": 1697646580,
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
"owner": "libgit2",
"repo": "libgit2",
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
"type": "github"
},
"original": {
"owner": "libgit2",
"repo": "libgit2",
"type": "github"
}
},
"local-unstable": {
"locked": {
"lastModified": 0,
"narHash": "sha256-uewgkTWbDOpOP+wEA3f03XEKsPHsJi0iDqBGQnxWQo0=",
"path": "/home/giulio/dev/nixpkgs",
"type": "path"
},
"original": {
"path": "/home/giulio/dev/nixpkgs",
"type": "path"
}
},
"nix": {
"inputs": {
"flake-compat": [
"teslamate-flake",
"devenv"
],
"flake-parts": "flake-parts",
"libgit2": "libgit2",
"nixpkgs": "nixpkgs_5",
"nixpkgs-23-11": [
"teslamate-flake",
"devenv"
],
"nixpkgs-regression": [
"teslamate-flake",
"devenv"
],
"pre-commit-hooks": [
"teslamate-flake",
"devenv"
]
},
"locked": {
"lastModified": 1727438425,
"narHash": "sha256-X8ES7I1cfNhR9oKp06F6ir4Np70WGZU5sfCOuNBEwMg=",
"owner": "domenkozar",
"repo": "nix",
"rev": "f6c5ae4c1b2e411e6b1e6a8181cc84363d6a7546",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "devenv-2.24",
"repo": "nix",
"type": "github"
}
},
"nixos-master": {
"locked": {
"lastModified": 1741609898,
"narHash": "sha256-2WBG7YPJRxEVEekvux7ut6/lBxkwyNmu54hVRbUx2Ts=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "23aebfa4550ef6b2f755286f13b68aababf2ea03",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "master",
"repo": "nixpkgs",
"type": "github"
}
},
"nixos-unstable": { "nixos-unstable": {
"locked": { "locked": {
"lastModified": 1741462378, "lastModified": 1639699734,
"narHash": "sha256-ZF3YOjq+vTcH51S+qWa1oGA9FgmdJ67nTNPG2OIlXDc=", "narHash": "sha256-tlX6WebGmiHb2Hmniff+ltYp+7dRfdsBxw9YczLsP60=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "2d9e4457f8e83120c9fdf6f1707ed0bc603e5ac9", "rev": "03ec468b14067729a285c2c7cfa7b9434a04816c",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixpkgs-unstable", "ref": "nixos-unstable",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1703013332, "lastModified": 1639794673,
"narHash": "sha256-+tFNwMvlXLbJZXiMHqYq77z/RfmpfpiI3yjL6o/Zo9M=", "narHash": "sha256-bjauV0+Z4WmxeiHXecyiEOEwo+XysO6kx36beeatbl0=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "54aac082a4d9bb5bbc5c4e899603abfb76a3f6d6", "rev": "2627c4b795107ba94562626925f5a9a2bc62ebc6",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixos-unstable", "ref": "nixos-21.11",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"nixpkgs_2": {
"locked": {
"lastModified": 1741607077,
"narHash": "sha256-dVBOW6Hhc8dMvQMi/DrGQuaZOJRmOGX6Ps0+QkdW6cM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1b47a1dbffce177e49d6174e540a5472432bffe2",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-24.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"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",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1730531603,
"narHash": "sha256-Dqg6si5CqIzm87sp57j5nTaeBbWhHFaVyG7V6L8k3lY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7ffd9ae656aec493492b44d0ddfb28e79a1ea25d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_5": {
"locked": {
"lastModified": 1717432640,
"narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "88269ab3044128b7c2f4c7d68448b2fb50456870",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_6": {
"locked": {
"lastModified": 1716977621,
"narHash": "sha256-Q1UQzYcMJH4RscmpTkjlgqQDX5yi1tZL0O345Ri6vXQ=",
"owner": "cachix",
"repo": "devenv-nixpkgs",
"rev": "4267e705586473d3e5c8d50299e71503f16a6fb6",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "rolling",
"repo": "devenv-nixpkgs",
"type": "github"
}
},
"nixpkgs_7": {
"locked": {
"lastModified": 1732014248,
"narHash": "sha256-y/MEyuJ5oBWrWAic/14LaIr/u5E0wRVzyYsouYY3W6w=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "23e89b7da85c3640bbc2173fe04f4bd114342367",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nvidia-patch": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"utils": "utils"
},
"locked": {
"lastModified": 1741330828,
"narHash": "sha256-Vj5UBTlVRWGX3T0EAI6pVWTMmi8SpAeMuRMMVz/Hgz0=",
"owner": "icewind1991",
"repo": "nvidia-patch-nixos",
"rev": "0cc22a482f2aa4c13daeac0935a787d868122ff0",
"type": "github"
},
"original": {
"owner": "icewind1991",
"repo": "nvidia-patch-nixos",
"type": "github"
}
},
"pepeflake": {
"inputs": {
"nixpkgs": "nixpkgs_3"
},
"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",
"local-unstable": "local-unstable",
"nixos-master": "nixos-master",
"nixos-unstable": "nixos-unstable", "nixos-unstable": "nixos-unstable",
"nixpkgs": "nixpkgs_2", "nixpkgs": "nixpkgs"
"nvidia-patch": "nvidia-patch",
"pepeflake": "pepeflake",
"teslamate-flake": "teslamate-flake"
}
},
"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"
}
},
"teslamate-flake": {
"inputs": {
"devenv": "devenv",
"devenv-root": "devenv-root",
"flake-parts": "flake-parts_2",
"nixpkgs": "nixpkgs_7",
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1732358620,
"narHash": "sha256-diQRtJYfzGIVLxrdBad3XKWCtR97rj9Q1ZJ9MmvJGRk=",
"owner": "teslamate-org",
"repo": "teslamate",
"rev": "0ec408c8e182fe64e9568b6f137cbfb528717e8e",
"type": "github"
},
"original": {
"owner": "teslamate-org",
"ref": "v1.32.0",
"repo": "teslamate",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"teslamate-flake",
"nixpkgs"
]
},
"locked": {
"lastModified": 1732292307,
"narHash": "sha256-5WSng844vXt8uytT5djmqBCkopyle6ciFgteuA9bJpw=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "705df92694af7093dfbb27109ce16d828a79155f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems_2"
},
"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"
} }
} }
}, },

163
flake.nix
View File

@ -1,157 +1,62 @@
{ {
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/release-24.11"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-21.11";
nixos-unstable.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; nixos-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
nixos-master.url = "github:NixOS/nixpkgs/master";
local-unstable.url = "path:///home/giulio/dev/nixpkgs";
pepeflake.url = "git+https://git.giugl.io/peperunas/pepeflake";
teslamate-flake.url = "github:teslamate-org/teslamate/v1.32.0";
agenix-flake.url = "github:ryantm/agenix";
home-manager = { home-manager = {
url = "github:nix-community/home-manager/release-24.11"; url = "github:rycee/home-manager/release-21.11";
inputs.nixpkgs.follows = "nixpkgs";
};
nvidia-patch = {
url = "github:icewind1991/nvidia-patch-nixos";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
navidrome.url = "github:antifuchs/nixpkgs/fix-151550";
}; };
outputs = outputs = inputs@{ self, nixpkgs, nixos-unstable, home-manager, navidrome}:
{ self
, nixpkgs
, nixos-unstable
, nixos-master
, local-unstable
, home-manager
, teslamate-flake
, nvidia-patch
, agenix-flake
, pepeflake
}:
let let
sysLinuxX64 = "x86_64-linux"; system = "x86_64-linux";
sysDarwin = "aarch64-darwin";
sysLinuxAarch = "aarch64-linux";
wrapPkgsSystem = { system, cudaSupport ? false }: pkgs = import nixpkgs {
let inherit system;
config = { config.allowUnfree = true;
inherit cudaSupport; overlays = [ (final: prev: { inherit unstable; }) ];
allowUnfree = true;
}; };
cachixOverlay = final: prev: { unstable = import nixos-unstable {
nixosModules = (prev.nixosModules or { }) // { inherit system;
cachixConfig = import ./cachix.nix; config.allowUnfree = true;
};
}; };
utils = import ./lib {
extOverlays = [ inherit pkgs unstable nixpkgs nixos-unstable home-manager;
(nvidia-patch.overlays.default)
cachixOverlay
];
importNixpkgs = { flake }:
import flake {
inherit system config;
overlays = extOverlays;
}; };
unstablePkgs = importNixpkgs { flake = nixos-unstable; }; inherit (utils) host;
masterPkgs = importNixpkgs { flake = nixos-master; }; inherit (utils) user;
localPkgs = importNixpkgs { flake = local-unstable; }; in {
teslamatePkgs = importNixpkgs { flake = teslamate-flake; };
agenixPkgs = importNixpkgs { flake = agenix-flake; };
pepePkgs = pepeflake.packages.${system} // pepeflake.legacyPackages.${system} or { };
additionalOverlays = [
(final: prev: { inherit unstablePkgs; })
(final: prev: { inherit localPkgs; })
(final: prev: { inherit teslamatePkgs; })
(final: prev: { inherit agenixPkgs; })
(final: prev: { inherit masterPkgs; })
(final: prev: { inherit pepePkgs; })
];
in
import nixpkgs {
inherit system config;
overlays = additionalOverlays ++ extOverlays ++ [
(final: prev: {
ctranslate2 = prev.ctranslate2.override {
withCUDA = true;
withCuDNN = true;
};
})
];
};
wrapUtils = { pkgs }:
let
inherit (pkgs.lib) makeScope;
inherit (pkgs) newScope;
in
makeScope newScope (self: rec {
inherit nixpkgs home-manager nixos-unstable;
inherit (self.callPackage ./lib/utils.nix { }) mkSysRole mkHomeRole;
inherit (user) mkUser;
user = self.callPackage ./lib/user.nix { };
host = self.callPackage ./lib/host.nix { };
});
pkgsLinuxX64Cuda = wrapPkgsSystem { system = sysLinuxX64; };
utilsLinuxX64Cuda = wrapUtils { pkgs = pkgsLinuxX64Cuda; };
pkgsLinuxAarch = wrapPkgsSystem { system = sysLinuxAarch; };
utilsLinuxAarch = wrapUtils { pkgs = pkgsLinuxAarch; };
pkgsDarwin = wrapPkgsSystem { system = sysDarwin; };
utilsDarwin = wrapUtils { pkgs = pkgsDarwin; };
in
{
nixosConfigurations = { nixosConfigurations = {
architect = utilsLinuxX64Cuda.host.mkHost { architect = host.mkHost {
name = "architect"; name = "architect";
users = [{ users = [{
user = "giulio"; user = "giulio";
roles = [ ]; roles = [ ];
}]; }];
imports = [ imports = [
teslamate-flake.nixosModules.default {
agenix-flake.nixosModules.default disabledModules = [ "services/audio/navidrome.nix" ];
imports =
[ (navidrome + "/nixos/modules/services/audio/navidrome.nix") ];
}
]; ];
}; };
gAluminum = host.mkHost {
name = "gAluminum";
users = [{
user = "giulio";
roles = [ "desktop" "ssh" "git" ];
}];
roles = [ "gnome" ];
}; };
proxy = host.mkHost {
homeConfigurations = { name = "proxy";
giulioMac = utilsDarwin.user.mkHMUser { users = [ ];
name = "giulio";
roles = [ "ssh" ];
};
giulioAarch = utilsLinuxAarch.user.mkHMUser {
name = "giulio";
roles = [ "ssh" ];
};
giulioX64 = utilsLinuxX64Cuda.user.mkHMUser {
name = "giulio";
roles = [ "ssh" "go" ];
};
giulioX64NoSSH = utilsLinuxX64Cuda.user.mkHMUser {
name = "giulio";
roles = [ "go" ];
};
};
defaultTemplate = self.templates.basicShell;
templates = {
basicShell = {
path = ./templates/basicShell;
description = "A barebone shell with custom defined packages";
}; };
}; };
}; };

View File

@ -1,22 +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" ];
pruneOpts = [ pruneOpts = [
"--keep-daily 45" "--keep-daily 45"
"--keep-weekly 12" "--keep-weekly 12"
@ -24,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

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

View File

@ -1,39 +0,0 @@
{ config, lib, ... }:
let
domain = "books.giugl.io";
auth_block = (import ./openid.nix { inherit lib; }).openresty_oidc_block;
utilities = import ./utilities.nix { inherit lib config; };
inherit (utilities) architectInterfaceAddress;
in
{
services = {
calibre-web = {
enable = true;
group = "media";
options = {
enableBookConversion = true;
enableBookUploading = true;
};
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://127.0.0.1:8083";
extraConfig = ''
client_max_body_size 500M;
'' + auth_block { access_role = "calibre"; };
};
};
};
networking.extraHosts = ''
${architectInterfaceAddress "lan"} ${domain}
${architectInterfaceAddress "tailscale"} ${domain}
'';
users.groups.media.members = [ "calibre-web" ];
}

View File

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

View File

@ -1,16 +1,13 @@
{ config, pkgs, lib, ... }: { 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"; ];
hostname = "architect";
utilities = import ./utilities.nix { inherit lib config; }; network = import ./network.nix;
inherit (utilities) generateDeviceStrings; in {
in imports = [ # Include the results of the hardware scan.
{
imports = [
./options.nix
./backup.nix ./backup.nix
./hardware.nix ./hardware.nix
./firewall.nix ./firewall.nix
@ -21,50 +18,42 @@ in
./bazarr.nix ./bazarr.nix
./nzbget.nix ./nzbget.nix
./nextcloud.nix ./nextcloud.nix
./wireguard.nix
./minio.nix ./minio.nix
./matrix.nix ./matrix.nix
./fail2ban.nix ./fail2ban.nix
./dns.nix ./dns.nix
#./minecraft.nix #./minecraft.nix
./prowlarr.nix ./prowlarr.nix
./redlib.nix ./plex.nix
# ./invidious.nix ./githubrunner.nix
./libreddit.nix
./invidious.nix
./nitter.nix
./ccache.nix
./lidarr.nix
./navidrome.nix
./jellyfin.nix ./jellyfin.nix
# ./docker.nix ./prosody.nix
./tailscale.nix ./deluge.nix
./headscale.nix
./llm.nix
# ./photoprism.nix
./sunshine.nix
./jellyseer.nix
./teslamate.nix
./postgres.nix
./netdata.nix
./homeassistant.nix
./searx.nix
]; ];
age.identityPaths = [ "/root/.ssh/id_ed25519" ]; time.timeZone = "Europe/Rome";
system.stateVersion = "21.11"; # Did you read the comment?
architect = {
networks.lan = {
interface = "enp6s0";
net = "10.0.0.0/24";
devices = {
architect = { address = "10.0.0.250"; hostname = "architect.${domain}"; };
router = { address = "10.0.0.1"; hostname = "router.${domain}"; };
dvr = { address = "10.0.0.3"; hostname = "dvr.${domain}"; };
};
};
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 = {
@ -72,18 +61,20 @@ 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.architect.networks.lan; [
"ip=${devices.architect.address}::${devices.router.address}:255.255.255.0::${interface}:off"
];
kernel.sysctl = { "net.ipv4.ip_forward" = 1; };
loader = { loader = {
systemd-boot ={ systemd-boot ={
enable = true; enable = true;
@ -94,22 +85,31 @@ in
supportedFilesystems = [ "zfs" ]; supportedFilesystems = [ "zfs" ];
zfs.requestEncryptionCredentials = true; zfs.requestEncryptionCredentials = true;
tmp.tmpfsSize = "50%"; tmpOnTmpfsSize = "80%";
}; };
networking = with config.architect.networks.lan; { networking = {
hostName = "architect"; hostName = hostname;
hostId = "49350853"; hostId = "49350853";
useDHCP = false; useDHCP = false;
defaultGateway = devices.router.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 = (generateDeviceStrings config.architect.networks.lan.devices) + '' 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 # Blacklist
0.0.0.0 metrics.plex.tv 0.0.0.0 metrics.plex.tv
0.0.0.0 analytics.plex.tv 0.0.0.0 analytics.plex.tv
@ -130,29 +130,26 @@ in
''; '';
}; };
hardware.opengl = { environment.systemPackages = with pkgs; [ cudatoolkit cachix ];
enable = true;
extraPackages = with pkgs; [ vaapiVdpau ]; hardware = {
opengl.enable = true;
opengl.extraPackages = with pkgs; [ vaapiVdpau ];
opengl.driSupport = true;
}; };
services.das_watchdog.enable = true;
services = { services = {
fwupd.enable = true;
das_watchdog.enable = true;
zfs.autoScrub.enable = true; 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;
}; };
}
environment.variables = { LIBVA_DRIVER_NAME = "vdpau"; };
}

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,90 +1,26 @@
{ config, lib, ... }: { config, pkgs, lib, ... }:
with lib;
let
generateCoreDNSConfig = domains:
let
generateForDomain = domain: conf:
concatMapStrings
(iface:
let
architectIP = config.architect.networks.${iface}.devices.architect.address;
interfaceNet = config.architect.networks.${iface}.net;
in
''
${domain} {
view ${iface} {
expr incidr(client_ip(), '${interfaceNet}')
}
template IN A ${domain} {
answer "${domain}. 60 IN A ${architectIP}"
}
template IN HTTPS ${domain} {
answer "${domain}. 60 IN HTTPS 1 . ipv4hint=\"${architectIP}\""
}
cache
log
}
''
)
conf.dnsInterfaces;
in
concatStrings (mapAttrsToList generateForDomain domains);
# Combine vhosts and the single domain
allDomains = config.architect.vhost // {
"architect.devs.giugl.io" = { dnsInterfaces = [ "lan" "tailscale" ]; };
};
domain = "adguard.giugl.io";
in
{ {
architect.vhost.${domain} = with config.architect.networks; {
dnsInterfaces = [ "tailscale" "lan" ];
locations."/" = {
port = config.services.adguardhome.port;
allowLan = true;
allow = [
tailscale.net
];
};
};
services = { services = {
coredns = { dnsmasq = {
enable = true; enable = true;
config = '' servers = [ "127.0.0.1#5300" ];
${generateCoreDNSConfig allDomains} extraConfig = ''
localise-queries
. { min-cache-ttl=120
cache max-cache-ttl=2400
forward . 127.0.0.1:${toString config.services.adguardhome.settings.dns.port}
}
''; '';
}; };
adguardhome = { adguardhome = {
enable = true; enable = true;
settings = { port = 3031;
port = 5354;
dns = {
port = 5300;
};
upstream_dns = [
"tls://architect.d65174.dns.nextdns.io"
"https://dns.nextdns.io/d65174/architect"
];
};
}; };
dnscrypt-proxy2 = { dnscrypt-proxy2 = {
enable = true; enable = true;
settings = { settings = {
listen_addresses = [ "127.0.0.1:5354" ]; listen_addresses = [ "127.0.0.1:5353" ];
ipv4_servers = true; ipv4_servers = true;
ipv6_servers = false; ipv6_servers = false;
block_ipv6 = true; block_ipv6 = true;
@ -97,8 +33,8 @@ in
lb_estimator = true; lb_estimator = true;
ignore_system_dns = true; ignore_system_dns = true;
fallback_resolvers = [ "1.1.1.1:53" "9.9.9.9:53" ]; fallback_resolvers = [ "1.1.1.1:53" "9.9.9.9:53" ];
cache_min_ttl = 60; cache_min_ttl = 450;
cache_max_ttl = 360; 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.architect.networks.lan.net
config.architect.networks.tailscale.net
];
}; };
} }

View File

@ -1,23 +1,33 @@
{ config, lib, ... }: { config, lib, ... }:
with import ./network.nix;
let let
openTCP = lib.concatMapStringsSep "," (x: toString x) config.architect.firewall.openTCP; open_tcp_ports = lib.concatMapStringsSep "," (x: toString x) [
openUDP = lib.concatMapStringsSep "," (x: toString x) config.architect.firewall.openUDP; 22 # ssh
openTCPVPN = lib.concatMapStringsSep "," (x: toString x) config.architect.firewall.openTCPVPN; 80 # http
openUDPVPN = lib.concatMapStringsSep "," (x: toString x) config.architect.firewall.openUDPVPN; 443 # https
8448 # matrix
deviceAddress = interface: device: 10022 # gitea
config.architect.networks.${interface}.devices.${device}.address; 18080 # monero
51413 # transmission
gdevices = [
(deviceAddress "tailscale" "architect")
(deviceAddress "tailscale" "dodino")
(deviceAddress "tailscale" "manduria")
(deviceAddress "tailscale" "kmerr")
(deviceAddress "tailscale" "chuck")
]; ];
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;
@ -25,7 +35,7 @@ in
nftables = { nftables = {
enable = true; enable = true;
ruleset = with config.architect.networks; '' 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;
@ -37,9 +47,6 @@ in
} }
table ip nat { table ip nat {
chain DOCKER {
type nat hook prerouting priority dstnat; policy accept;
}
chain PREROUTING { chain PREROUTING {
type nat hook prerouting priority dstnat; policy accept; type nat hook prerouting priority dstnat; policy accept;
} }
@ -54,7 +61,9 @@ in
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
} }
} }
@ -64,10 +73,11 @@ in
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 ${vpn-if} ip saddr ${vpn-net} accept comment "bind ip ${vpn-net} to intf ${vpn-if}"
iifname "lo" accept comment "bind any ip to intf lo" iifname "lo" accept comment "bind any ip to intf lo"
jump mangle_drop jump mangle_drop
} }
@ -120,12 +130,16 @@ in
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 ${wan-if} tcp dport {${open_tcp_ports}} accept
iifname ${wan-if} udp dport {${open_udp_ports}} accept
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
iifname ${lan.interface} tcp dport {${openTCP}} accept
iifname ${lan.interface} udp dport {${openUDP}} accept
jump filter_drop jump filter_drop
} }
@ -133,7 +147,23 @@ in
type filter hook forward priority filter; policy drop; type filter hook forward priority filter; policy drop;
ct state established,related accept ct state established,related accept
oifname ${lan.interface} ip saddr ${tailscale.net} accept # client to client
ip saddr {${lib.concatStringsSep "," c2c-wg}} ip daddr {${
lib.concatStringsSep "," c2c-wg
}} accept
# gdevices talking to everyone in VPN
ip saddr {${
lib.concatStringsSep "," gdevices-wg
}} ip daddr ${vpn-net} accept
ip saddr {${
lib.concatStringsSep "," gamenet-wg
}} ip daddr {${lib.concatStringsSep "," gamenet-wg}} accept
# nat to wan
oifname ${wan-if} ip saddr {${
lib.concatStringsSep "," towan-wg
}} accept
jump filter_drop jump filter_drop
} }

View File

@ -1,34 +1,37 @@
{ config, lib, ... }: { lib, ... }:
let let
domain = "git.giugl.io"; domain = "git.giugl.io";
in network = import ./network.nix;
{ in {
architect = {
firewall.openTCP = [ config.services.gitea.settings.server.SSH_PORT ];
vhost.${domain} = {
dnsInterfaces = [ "lan" "tailscale" ];
locations."/" = {
port = config.services.gitea.settings.server.HTTP_PORT;
allowWAN = true;
};
};
};
services.gitea = { services.gitea = {
enable = true; enable = true;
database.type = "sqlite3"; database.type = "sqlite3";
domain = domain;
appName = "Gitea"; appName = "Gitea";
# https://github.com/NixOS/nixpkgs/issues/235442#issuecomment-1574329453 rootUrl = "https://${domain}";
lfs.enable = true; ssh.clonePort = 22;
settings = { settings.server.LFS_START_SERVER = true;
server = {
DOMAIN = domain;
ROOT_URL = "https://${domain}";
SSH_PORT = 22;
HTTP_PORT = 3001;
}; };
openid.enable_openid_signin = 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
'';
boot = {
kernelModules = [ "kvm-amd" "dm-snapshot" ];
initrd = {
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" ]; [ "nvme" "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
}; boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "zpool/nixos/root";
fsType = "zfs";
}; };
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 = "zfs";
};
fileSystems."/secrets" = {
device = "backedpool/secrets";
fsType = "zfs";
};
fileSystems."/var/lib" = {
device = "backedpool/services";
fsType = "zfs";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/AF19-5616";
fsType = "vfat"; fsType = "vfat";
}; };
"/backednvme" = {
device = "/dev/mapper/backedNvme";
};
"/services" = {
device = "/backednvme/services";
options = [ "bind" ];
};
"/secrets" = {
device = "/backednvme/secrets";
options = [ "bind" ];
};
"/media" = {
device = "nvmedata/media";
fsType = "zfs";
};
};
swapDevices = [{ swapDevices = [{
device = "/swapfile"; device = "/dev/sdc1";
size = 1024 * 64; size = 10000;
}]; }];
} }

View File

@ -1,49 +0,0 @@
{ config, pkgs, ... }:
let
domain = "vipienne.giugl.io";
headscalePkg = pkgs.headscale;
in
{
environment.systemPackages = [ headscalePkg ];
architect = {
firewall = {
openUDP = [ config.services.tailscale.port ];
};
vhost.${domain} = {
dnsInterfaces = [ "lan" "tailscale" ];
locations."/" = {
port = config.services.headscale.port;
allowWAN = true;
proxyWebsockets = true;
};
};
};
services.headscale = {
enable = true;
package = headscalePkg;
port = 1194;
settings = {
server_url = "https://${domain}";
# log.level = "debug";
dns = {
magic_dns = false;
# base_domain = domain;
override_local_dns = true;
global = [
config.architect.networks.tailscale.devices.architect.address
];
nameservers.global = [
config.architect.networks.tailscale.devices.architect.address
];
};
logtail.enabled = false;
prefixes.v4 = config.architect.networks.tailscale.net;
noise.private_key_path = "/var/lib/headscale/noise_private.key";
};
};
}

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +1,24 @@
{ config, lib, pkgs, ... }: { lib, ... }:
let let
domain = "tube.giugl.io"; domain = "tube.giugl.io";
in network = import ./network.nix;
{ in {
services.invidious = { services = {
invidious = {
enable = true; enable = true;
package = pkgs.unstablePkgs.invidious; port = 9092;
settings = { };
hmac_key = "a2a91eca269d26de1221285e8981879834045bff";
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = { proxyPass = "http://localhost:9092"; };
}; };
}; };
architect.vhost.${domain} = { networking.extraHosts = ''
dnsInterfaces = [ "lan" "tailscale" ]; ${network.architect-lan} ${domain}
locations."/" = { ${network.architect-wg} ${domain}
port = config.services.invidious.port; '';
allowWAN = true;
};
};
} }

View File

@ -1,47 +1,41 @@
{ config, pkgs, lib, ... }: { pkgs, ... }:
let let
domain = "media.giugl.io"; network = import ./network.nix;
port = 8096; domain = "jellyfin.giugl.io";
allowLan = true; in {
in disabledModules = [ "services/misc/jellyfin.nix" ];
{ imports = [ ./modules/jellyfin.nix ];
# needed since StateDirectory does not accept symlinks
systemd.services.jellyfin.serviceConfig.StateDirectory = lib.mkForce "";
architect.vhost.${domain} = with config.architect.networks; { services = {
dnsInterfaces = [ "lan" "tailscale" ]; jellyfin = {
locations = {
"/" = {
inherit port allowLan;
allow = [
tailscale.net
];
};
"/socket" = {
inherit port allowLan;
proxyWebsockets = true;
allow = [
tailscale.net
];
};
};
};
services.jellyfin = {
enable = true; enable = true;
group = "media"; group = "media";
package = pkgs.unstablePkgs.jellyfin; package = pkgs.unstable.jellyfin;
}; };
users.groups = { nginx.virtualHosts.${domain} = {
media.members = [ "jellyfin" ]; # forceSSL = true;
video.members = [ "jellyfin" ]; # enableACME = true;
render.members = [ "jellyfin" ]; 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" = { fileSystems."/tmp/jellyfin" = {
device = "none"; device = "none";

View File

@ -1,23 +0,0 @@
{ config, pkgs, ... }:
let
domain = "aumm-aumm.giugl.io";
in
{
services.jellyseerr = {
enable = true;
# package = pkgs.unstablePkgs.jellyseerr;
};
architect.vhost.${domain} = {
dnsInterfaces = [ "tailscale" "lan" ];
locations."/" = {
port = config.services.jellyseerr.port;
allowLan = true;
allow = [
config.architect.networks.tailscale.net
];
};
};
}

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

@ -1,82 +0,0 @@
{ config, lib, ... }:
let
domain = "photos.giugl.io";
backendPort = 8001;
frontendPort = 3000;
in
{
architect.vhost.${domain} = {
dnsInterfaces = [ "tailscale" ];
locations."/" = {
host = "172.17.0.1";
port = frontendPort;
# allowLan = true;
# allow = [ config.architect.networks."tailscale".net ];
};
locations."~ ^/(api|media)/" = {
host = "172.17.0.1";
port = backendPort;
# allowLan = true;
# allow = [ config.architect.networks."tailscale".net ];
};
locations."/ws" = {
host = "172.17.0.1";
port = backendPort;
proxyWebsockets = true;
# allowLan = true;
# allow = [ config.architect.networks."tailscale".net ];
};
};
services.redis.servers."librephotos" = {
enable = true;
port = 1233;
bind = "172.17.0.1";
extraParams = [ "--protected-mode no" ];
};
virtualisation.oci-containers = {
containers = {
librephotos-front = {
image = "reallibrephotos/librephotos-frontend:latest";
autoStart = true;
ports = [
"172.17.0.1:${toString frontendPort}:${toString frontendPort}"
];
};
librephotos-back = {
image = "reallibrephotos/librephotos:latest";
autoStart = true;
ports = [
"172.17.0.1:${toString backendPort}:${toString backendPort}"
];
environment = {
SECRET_KEY = "LOLOL";
BACKEND_HOST = domain;
ADMIN_EMAIL = "me@giugl.io";
ADMIN_USERNAME = "giulio";
ADMIN_PASSWORD = "giulio";
ALLOWED_HOSTS = domain;
DB_BACKEND = "mysql";
DB_NAME = "librephotos";
DB_USER = "librephotos";
DB_PASS = "librephotos";
DB_HOST = "172.17.0.1";
DB_PORT = toString config.services.mysql.settings.mysqld.port;
REDIS_HOST = "172.17.0.1";
REDIS_PORT = toString config.services.redis.servers."librephotos".port;
MAPBOX_API_KEY = "SOME_KEY";
WEB_CONCURRENCY = "24";
DEBUG = "0";
};
};
};
};
}

View File

@ -1,25 +1,34 @@
{ config, ... }: { lib, ... }:
let let
domain = "htlid.giugl.io"; domain = "htlid.giugl.io";
in network = import ./network.nix;
{ in {
services = { services = {
lidarr = { lidarr = {
enable = true; enable = true;
group = "media"; 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;
'';
};
};
}; };
architect.vhost.${domain} = { networking.extraHosts = ''
dnsInterfaces = [ "lan" "tailscale" ]; ${network.architect-lan} ${domain}
locations."/" = { ${network.architect-wg} ${domain}
port = 8686; '';
allowLan = true;
allowWAN = false;
allow = [ config.architect.networks."tailscale".net ];
};
};
users.groups.media.members = [ "lidarr" ]; users.groups.media.members = [ "lidarr" ];
} }

View File

@ -1,56 +0,0 @@
{ config, pkgs, ... }:
let
backendDomain = "ollama.giugl.io";
frontendDomain = "llm.giugl.io";
ollamaPkg = pkgs.unstablePkgs.ollama-cuda;
in
{
environment = {
systemPackages = [ ollamaPkg ];
};
services = {
ollama = {
enable = true;
package = ollamaPkg;
acceleration = "cuda";
environmentVariables = {
OLLAMA_FLASH_ATTENTION = "1";
OLLAMA_NUM_PARALLEL = "2";
OLLAMA_KV_CACHE_TYPE = "q8_0";
};
};
open-webui.enable = true;
};
architect.vhost.${backendDomain} = {
dnsInterfaces = [ "tailscale" "lan" ];
locations."/" = {
host = config.services.ollama.host;
port = config.services.ollama.port;
allowLan = true;
allowWAN = true;
recommendedProxySettings = false;
extraConfig = ''
proxy_buffering off;
proxy_read_timeout 600s;
proxy_set_header Host localhost:${toString config.services.ollama.host};
'';
};
};
architect.vhost.${frontendDomain} = {
dnsInterfaces = [ "tailscale" "lan" ];
locations."/" = {
host = config.services.open-webui.host;
port = config.services.open-webui.port;
allowLan = true;
allowWAN = true;
};
};
}

View File

@ -1,36 +1,34 @@
{ 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
extraConfigFiles = [ config.age.secrets.matrix.path ];
settings = {
server_name = "${domain}"; server_name = "${domain}";
database_name = "synapse";
public_baseurl = "https://${domain}"; public_baseurl = "https://${domain}";
registration_shared_secret = "runas!"; registration_shared_secret = "runas!";
url_preview_enabled = true; url_preview_enabled = true;
dynamic_thumbnails = true; dynamic_thumbnails = true;
withJemalloc = true; withJemalloc = true;
enable_registration = false; # enable_registration = true;
password_config.enabled = true; app_service_config_files = [
"/var/lib/matrix-synapse/discord-registration.yaml"
auto_join_rooms = [ "#general:${domain}" "#music:${domain}" "#movies:${domain}" ]; # "/var/lib/matrix-synapse/hookshot-registration.yml"
# "/var/lib/matrix-synapse/telegram-registration.yaml"
];
extraConfig = ''
auto_join_rooms:
- "#general:matrix.giugl.io"
max_upload_size: "50M"
'';
listeners = [{ listeners = [{
port = 8008; port = 8008;
bind_addresses = [ "127.0.0.1" ]; bind_address = "::1";
type = "http"; type = "http";
tls = false; tls = false;
x_forwarded = true; x_forwarded = true;
@ -39,7 +37,49 @@ in
compress = false; 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 Content-Type application/json;
add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Origin *;
return 200 '${builtins.toJSON client}'; 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}
''; '';
} }

View File

@ -1,27 +1,20 @@
{ config, lib, pkgs, ... }: { lib, ... }:
let let
domain = "s3.giugl.io"; domain = "s3.giugl.io";
network = import ./network.nix;
utilities = import ./utilities.nix { inherit lib config; }; in {
inherit (utilities) architectInterfaceAddress;
in
{
services = { services = {
minio = { minio.enable = true;
enable = true;
package = pkgs.minio_legacy_fs;
};
nginx.virtualHosts.${domain} = { nginx.virtualHosts.${domain} = {
forceSSL = true; forceSSL = true;
enableACME = true; enableACME = true;
locations."/" = { locations."/" = {
proxyPass = "http://127.0.0.1:9000"; proxyPass = "http://localhost:9000";
extraConfig = '' extraConfig = ''
client_max_body_size 500M; allow 10.0.0.0/24;
allow ${config.architect.networks.lan.net}; ${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
allow ${config.architect.networks.tailscale.net};
deny all; deny all;
''; '';
}; };
@ -29,7 +22,7 @@ in
}; };
networking.extraHosts = '' networking.extraHosts = ''
${architectInterfaceAddress "lan"} ${domain} ${network.architect-lan} ${domain}
${architectInterfaceAddress "tailscale"} ${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

@ -1,16 +1,15 @@
{ config, lib, pkgs, ... }: { lib, pkgs, ... }:
let let
domain = "music.giugl.io"; domain = "music.giugl.io";
library_path = "/media/Music"; network = import ./network.nix;
beets_config = "/media/beets.conf"; in {
in services = {
{ navidrome = {
services.navidrome = {
enable = true; enable = true;
settings = { settings = {
MusicFolder = library_path; MusicFolder = "/media/Music";
LastFM.enable = true; LastFM.enable = true;
LastFM.ApiKey = "5cef5cb5f9d31326b97d0f929ca9cf20"; LastFM.ApiKey = "5cef5cb5f9d31326b97d0f929ca9cf20";
LastFM.Secret = "d1296896126f4caae47407aecf080b25"; LastFM.Secret = "d1296896126f4caae47407aecf080b25";
@ -21,63 +20,34 @@ in
}; };
}; };
architect.vhost.${domain} = { nginx.virtualHosts.${domain} = {
dnsInterfaces = [ "lan" "tailscale" ]; forceSSL = true;
enableACME = true;
locations."/" = { locations."/" = {
port = 4533; proxyPass = "http://localhost:4533";
allowLan = true; # extraConfig = ''
allowWAN = true; # allow 10.0.0.0/24;
# allow = [ config.architect.networks."tailscale".net ]; # ${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
# deny all;
# '';
};
}; };
}; };
systemd.services = { systemd.services."beets-rename" = {
"beets-update" = {
enable = true; enable = true;
# requires = [ "remove-badmp3.service" "remove-badflac.service" ];
before = [ "beets-import.service" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.beets}/bin/beet -c ${beets_config} update";
};
};
"beets-import" = {
enable = true;
path = [ pkgs.imagemagick ];
requires = [ "beets-update.service" ];
after = [ "beets-update.service" ];
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
ExecStart = ExecStart =
"${pkgs.beets}/bin/beet -c ${beets_config} import --flat -q ${library_path}"; "${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 = "weekly";
}; };
startAt = "daily";
}; };
# "remove-badmp3" = { networking.extraHosts = ''
# enable = true; ${network.architect-lan} ${domain}
# before = [ "beets-import.service" "beets-update.service" ]; ${network.architect-wg} ${domain}
# serviceConfig = { '';
# Type = "oneshot";
# ExecStart = ''
# ${pkgs.findutils}/bin/find ${library_path} -name "*.mp3" -type f -exec ${pkgs.bash}/bin/sh -c '${pkgs.mp3val}/bin/mp3val "{}" | grep -Pi error 1>/dev/null && ${pkgs.busybox}/bin/rm "{}"' \;
# '';
# };
# };
# "remove-badflac" = {
# enable = true;
# before = [ "beets-import.service" "beets-update.service" ];
# serviceConfig = {
# Type = "oneshot";
# ExecStart = ''
# ${pkgs.findutils}/bin/find ${library_path} -name "*.flac" -type f -exec ${pkgs.bash}/bin/sh -c '${pkgs.flac}/bin/flac -st "{}" || ${pkgs.busybox}/bin/rm "{}"' \;
# '';
# };
# };
# };
users.groups.media.members = [ "navidrome" ]; users.groups.media.members = [ "navidrome" ];
} }

View File

@ -1,26 +0,0 @@
{ config, pkgs, ... }:
let
domain = "monitor.giugl.io";
in
{
services.netdata = {
enable = true;
package = pkgs.unstablePkgs.netdata;
config = {
db.mode = "dbengine";
};
};
architect.vhost.${domain} = with config.architect.networks; {
dnsInterfaces = [ "tailscale" "lan" ];
locations."/" = {
port = 19999;
allowLan = true;
allow = [
tailscale.net
];
};
};
}

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,88 +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; [
nodejs-18_x
libtensorflow
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.nextcloud30; 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}" ];
}; };
}; };
}; };
@ -93,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

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

View File

@ -1,50 +0,0 @@
{ lib }:
{
openresty_oidc_block =
{ access_role ? "", whitelisted_ips ? [ ] }: ''
'';
# access_by_lua_block {
# local opts = {
# discovery = "https://auth.giugl.io/realms/master/.well-known/openid-configuration",
# client_id = "nginx",
# client_secret = "9C6BYxPhTbrRS4DIwd3Smk7e11ABmnt8",
# logout_path = "/logout",
# redirect_after_logout_uri = "/",
# redirect_uri = "/redirect_uri",
# keepalive = "yes",
# accept_none_alg = true,
# revoke_tokens_on_logout = true,
# -- access token valid for a day
# access_token_expires_in = 86400
# }
# ${lib.optionalString (whitelisted_ips != []) ''
# local whitelist = {${lib.strings.concatMapStringsSep "," (x: "\"${x}\"") whitelisted_ips}}
# if is_ip_whitelisted(ngx.var.remote_addr, whitelist) then
# return
# end
# ''}
# -- call introspect for OAuth 2.0 Bearer Access Token validation
# local res, err = require("resty.openidc").authenticate(opts)
# if err then
# ngx.status = 403
# ngx.say(err)
# ngx.exit(ngx.HTTP_FORBIDDEN)
# end
# ${lib.optionalString (access_role != "") ''
# if not check_role(res, "${access_role}") then
# ngx.status = 401
# ngx.header.content_type = 'text/html';
# ngx.say("You are not authorized to access this page. Please contact Er Pepotto.")
# ngx.exit(ngx.HTTP_UNAUTHORIZED)
# end
# ''}
# }
# '';
}

View File

@ -1,148 +0,0 @@
{ config, lib, ... }:
with lib;
{
options.architect = {
firewall = {
openTCP = mkOption {
type = types.listOf types.int;
default = [ ];
};
openUDP = mkOption {
type = types.listOf types.int;
default = [ ];
};
};
networks = mkOption {
type = types.attrsOf (types.submodule {
options = {
interface = mkOption {
type = types.str;
description = "The network interface name.";
};
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.";
};
};
});
default = { };
description = "An attribute set of devices with their configurations.";
};
};
});
default = { };
description = "An attribute set of networks with their configurations.";
};
vhost = mkOption {
type = types.attrsOf (types.submodule {
options = {
dnsInterfaces = mkOption {
type = types.listOf types.str;
default = [ ];
description = "List of interfaces to add extra DNS hosts for this vhost.";
};
locations = mkOption {
type = types.attrsOf (types.submodule {
options = {
extraConfig = mkOption {
type = types.str;
description = "Extra configuration for the location.";
default = "";
};
allowLan = mkOption {
type = types.bool;
default = false;
};
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.";
};
path = mkOption {
type = types.str;
default = "";
};
recommendedProxySettings = mkOption {
type = types.bool;
default = true;
description = "Force the use of recommended proxy configuration.";
};
allowWAN = mkOption {
type = types.bool;
default = false;
description = "If set to false, deny all WAN traffic.";
};
};
});
default = { };
description = "An attribute set of location configurations.";
};
};
});
default = { };
description = "An attribute set of domain configurations.";
};
};
# TODO: move to nginx
config = {
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 ''allow ${config.architect.networks."lan".net};''}
${optionalString (!location.allowWAN) "deny all;"}
'' + location.extraConfig;
})
conf.locations;
})
config.architect.vhost;
};
}

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" ];
};
}

View File

@ -1,37 +0,0 @@
{ config, pkgs, lib, ... }:
let
domain = "photos.giugl.io";
in
{
services.photoprism = {
enable = true;
package = pkgs.unstablePkgs.photoprism;
originalsPath = "/var/lib/private/photoprism/originals";
address = "0.0.0.0";
settings = {
PHOTOPRISM_DEFAULT_LOCALE = "en";
PHOTOPRISM_DATABASE_DRIVER = "mysql";
PHOTOPRISM_DATABASE_NAME = "photoprism";
PHOTOPRISM_DATABASE_SERVER = "/run/mysqld/mysqld.sock";
PHOTOPRISM_DATABASE_USER = "photoprism";
PHOTOPRISM_SITE_URL = "https://${domain}";
PHOTOPRISM_SITE_TITLE = "PePrism";
PHOTOPRISM_FFMPEG_ENCODER = "nvidia";
PHOTOPRISM_INIT = "tensorflow";
NVIDIA_VISIBLE_DEVICES = "all";
NVIDIA_DRIVER_CAPABILITIES = "compute,video,utility";
PHOTOPRISM_FFMPEG_BIN = "${pkgs.ffmpeg}/bin/ffmpeg";
};
};
architect.vhost.${domain} = {
dnsInterfaces = [ "tailscale" "lan" ];
locations."/" = {
port = config.services.photoprism.port;
allowLan = true;
allow = [ config.architect.networks."tailscale".net ];
proxyWebsockets = true;
};
};
}

View File

@ -1,36 +1,39 @@
{ pkgs, config, ... }: { pkgs, lib, ... }:
let let
domain = "media.giugl.io"; domain = "media.giugl.io";
port = 32400; network = import ./network.nix;
in in {
{
architect.firewall = {
openTCP = [ 32400 3005 8324 32469 ];
openUDP = [ 1900 5353 32410 32412 32413 32414 ];
};
services.plex = { services.plex = {
enable = true; enable = true;
package = pkgs.unstablePkgs.plex; package = pkgs.unstable.plex;
dataDir = "/plex"; dataDir = "/plex";
}; };
architect.vhost.${domain} = with config.architect.networks; { services.nginx = {
dnsInterfaces = [ "lan" "tailscale" ]; enable = true;
locations = { # give a name to the virtual host. It also becomes the server name.
"/" = { virtualHosts.${domain} = {
inherit port; forceSSL = true;
enableACME = true;
proxyWebsockets = true; http2 = true;
allowLan = true;
allow = [
tailscale.net
];
extraConfig = '' 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 #Some players don't reopen a socket and playback stops totally instead of resuming after an extended pause
send_timeout 100m; 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 # Forward real ip and host to Plex
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@ -65,15 +68,24 @@ in
proxy_set_header X-Plex-Device-Vendor $http_x_plex_device_vendor; proxy_set_header X-Plex-Device-Vendor $http_x_plex_device_vendor;
proxy_set_header X-Plex-Model $http_x_plex_model; 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. # Buffering off send to the client as soon as the data is received from Plex.
proxy_redirect off; proxy_redirect off;
proxy_buffering off; proxy_buffering off;
add_header 'Content-Security-Policy' 'upgrade-insecure-requests';
''; '';
locations."/" = { proxyPass = "http://localhost:32400"; };
}; };
}; };
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "plex" ]; users.groups.media.members = [ "plex" ];
} }

View File

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

View File

@ -4,26 +4,20 @@ let
domain = "xmpp.giugl.io"; domain = "xmpp.giugl.io";
conference_domain = "conference.${domain}"; conference_domain = "conference.${domain}";
upload_domain = "uploads.${domain}"; upload_domain = "uploads.${domain}";
network = import ./network.nix;
utilities = import ./utilities.nix { inherit lib config; }; in {
inherit (utilities) architectInterfaceAddress;
in
{
architect.firewall = {
openTCP = [ 5222 5269 ];
};
services = { services = {
prosody = { prosody = {
enable = true; enable = true;
virtualHosts.${domain} = { virtualHosts = {
inherit domain; "${domain}" = {
domain = domain;
enabled = true; enabled = true;
ssl.key = "${config.security.acme.certs.${domain}.directory}/key.pem"; ssl.key = "${config.security.acme.certs.${domain}.directory}/key.pem";
ssl.cert = ssl.cert =
"${config.security.acme.certs.${domain}.directory}/fullchain.pem"; "${config.security.acme.certs.${domain}.directory}/fullchain.pem";
}; };
};
muc = [{ domain = conference_domain; }]; muc = [{ domain = conference_domain; }];
uploadHttp = { domain = upload_domain; }; uploadHttp = { domain = upload_domain; };
@ -32,24 +26,16 @@ in
#httpInterfaces = [ "wg0" ]; #httpInterfaces = [ "wg0" ];
#httpsInterfaces = [ "wg0" ]; #httpsInterfaces = [ "wg0" ];
}; };
};
nginx.virtualHosts = { services.nginx.virtualHosts."${domain}".enableACME = true;
"${domain}" = { #services.nginx.virtualHosts."${conference_domain}".enableACME = true;
enableACME = true; #services.nginx.virtualHosts."${upload_domain}".enableACME = true;
forceSSL = true;
};
# "${conference_domain}".enableACME = true;
# "${upload_domain}".enableACME = true;
};
};
networking.extraHosts = '' networking.extraHosts = ''
${architectInterfaceAddress "lan"} ${domain} ${network.architect-lan} ${domain}
${architectInterfaceAddress "tailscale"} ${domain} ${network.architect-wg} ${domain}
''; '';
users.groups = { users.groups.acme.members = [ "prosody" ];
acme.members = [ "prosody" ];
nginx.members = [ "prosody" ];
};
} }

View File

@ -1,28 +1,38 @@
{ config, pkgs, ... }: { lib, ... }:
let let
domain = "htpro.giugl.io"; domain = "htpro.giugl.io";
in network = import ./network.nix;
{ in {
services.prowlarr = { services = {
enable = true; prowlarr.enable = true;
package = pkgs.unstablePkgs.prowlarr;
};
architect.vhost.${domain} = with config.architect.networks; {
dnsInterfaces = [ "tailscale" "lan" ];
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = { locations."/" = {
port = 9696; proxyPass = "http://localhost:9696";
allowLan = true; extraConfig = ''
proxyWebsockets=true; allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
allow = [ deny all;
tailscale.net '';
];
}; };
# 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" ]; users.groups.media.members = [ "prowlarr" ];
} }

View File

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

View File

@ -1,29 +0,0 @@
{ config, pkgs, ... }:
let
domain = "reddit.giugl.io";
in
{
systemd.services.redlib.environment = {
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";
};
services.redlib = {
enable = true;
port = 9090;
package = pkgs.unstablePkgs.redlib;
};
architect.vhost.${domain} = {
dnsInterfaces = [ "lan" "tailscale" ];
locations."/" = {
port = config.services.redlib.port;
allowWAN = true;
};
};
}

View File

@ -1,48 +0,0 @@
{ config, pkgs, lib, ... }:
let
domain = "runas.rocks";
runas_root = "/var/lib/runas.rocks/dist";
service_name = "runas.rocks-pull";
mkStartScript = name: pkgs.writeShellScript "${name}.sh" ''
set -euo pipefail
cd ${runas_root}
git pull origin main --rebase
'';
utilities = import ./utilities.nix { inherit lib config; };
inherit (utilities) architectInterfaceAddress;
in
{
services.nginx.virtualHosts.${domain} = {
enableACME = true;
forceSSL = true;
locations."/".root = runas_root;
locations."/.git" = { return = "404"; };
};
systemd = {
services.${service_name} = {
path = [ pkgs.git ];
enable = true;
serviceConfig = {
Type = "oneshot";
ExecStart = mkStartScript "${service_name}";
};
};
timers.${service_name} = {
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "hourly";
Unit = "${service_name}.service";
};
};
};
networking.extraHosts = ''
${architectInterfaceAddress "lan"} ${domain}
${architectInterfaceAddress "tailscale"} ${domain}
'';
}

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

@ -1,26 +1,33 @@
{ config, pkgs, ... }: { lib, ... }:
let let
domain = "htson.giugl.io"; domain = "htson.giugl.io";
in network = import ./network.nix;
{ in {
services.sonarr = { services = {
sonarr = {
enable = true; enable = true;
group = "media"; group = "media";
package = pkgs.unstablePkgs.sonarr;
}; };
architect.vhost.${domain} = with config.architect.networks; { nginx.virtualHosts.${domain} = {
dnsInterfaces = [ "tailscale" "lan" ]; forceSSL = true;
enableACME = true;
locations."/" = { locations."/" = {
port = 8989; proxyPass = "http://localhost:8989";
allowLan = true; extraConfig = ''
allow = [ allow 10.0.0.0/24;
tailscale.net ${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" ]; users.groups.media.members = [ "sonarr" ];
} }

View File

@ -1,210 +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
{
boot.kernelModules = [ "uinput" ];
environment.systemPackages = with pkgs.unstablePkgs; [ gamemode heroic ];
hardware = {
pulseaudio.enable = false;
nvidia = {
modesetting.enable = true;
powerManagement.enable = false;
powerManagement.finegrained = false;
open = false;
nvidiaSettings = true;
package = config.boot.kernelPackages.nvidiaPackages.latest;
};
};
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 = {
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.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"'';
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;
videoDrivers = [ "nvidia" ];
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,41 +0,0 @@
{ pkgs, config, lib, ... }:
let
domain = "devs.giugl.io";
utilities = import ./utilities.nix { inherit lib config; };
inherit (utilities) generateDeviceStrings;
in
{
architect = {
networks.tailscale = {
interface = "ts0";
net = "100.64.0.0/10";
devices = {
architect = { address = "100.64.0.1"; hostname = "architect.${domain}"; };
kmerr = { address = "100.64.0.2"; hostname = "kmerr.${domain}"; };
parallels = { address = "100.64.0.3"; hostname = "parallels.${domain}"; };
chuck = { address = "100.64.0.4"; hostname = "chuck.${domain}"; };
dodino = { address = "100.64.0.5"; hostname = "dodino.${domain}"; };
manduria = { address = "100.64.0.6"; hostname = "manduria.${domain}"; };
tommy = { address = "100.64.0.7"; hostname = "tommy.${domain}"; };
ucsb-workstation = { address = "100.64.0.8"; hostname = "ucsb-workstation.${domain}"; };
alfredo = { address = "100.64.0.9"; hostname = "alfredo.${domain}"; };
appletv = { address = "100.64.0.13"; hostname = "appletv.${domain}"; };
watkinshouse = { address = "100.64.0.14"; hostname = "watkinshouse.${domain}"; };
afsun = { address = "100.64.0.15"; hostname = "afsun.${domain}"; };
};
};
};
services = {
tailscale = {
enable = true;
interfaceName = config.architect.networks.tailscale.interface;
package = pkgs.unstablePkgs.tailscale;
};
};
networking.extraHosts = generateDeviceStrings config.architect.networks.tailscale.devices;
}

View File

@ -1,64 +0,0 @@
{ config, ... }:
let
domain = "tesla.giugl.io";
teslamatePort = 11234;
grafanaPort = 11334;
allowLan = true;
allowWAN = false;
in
{
age.secrets.teslamate = {
file = ../../secrets/teslamate.age;
owner = "teslamate";
};
architect.vhost.${domain} = with config.architect.networks; {
dnsInterfaces = [ "lan" "tailscale" ];
locations = {
"/" = {
inherit allowLan allowWAN;
port = teslamatePort;
proxyWebsockets = true;
allow = [
tailscale.net
];
};
"/live/websocket" = {
inherit allowLan allowWAN;
port = teslamatePort;
proxyWebsockets = true;
allow = [
tailscale.net
];
};
"/grafana" = {
inherit allowLan allowWAN;
port = grafanaPort;
proxyWebsockets = true;
allow = [
tailscale.net
];
};
};
};
services.teslamate = {
enable = true;
port = teslamatePort;
listenAddress = "127.0.0.1";
secretsFile = config.age.secrets.teslamate.path;
virtualHost = domain;
postgres.enable_server = true;
grafana = {
enable = true;
port = grafanaPort;
listenAddress = "127.0.0.1";
urlPath = "/grafana";
};
mqtt = {
enable = true;
};
};
}

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,13 +0,0 @@
{ config, lib, ... }:
{
# device.address device.hostname
generateDeviceStrings = devices: lib.concatStringsSep "\n"
(lib.mapAttrsToList (name: device: "${device.address} ${device.hostname}") devices);
getDeviceAddress = interface: device:
config.architect.networks.${interface}.devices.${device}.address;
architectInterfaceAddress = interface:
config.architect.networks.${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,48 +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 { mkRole = role : import (../roles + "/${role}.nix");
name = u.user;
roles = u.roles; users_mod= (map (u: user.mkUser {name = u.user; roles = u.roles; }) users);
}) roles_mod = (map (r: mkRole r) roles);
users);
roles_mod = (map (r: mkSysRole r) roles);
add_imports = imports; add_imports = imports;
in in nixpkgs.lib.nixosSystem {
nixpkgs.lib.nixosSystem { inherit system;
inherit system pkgs;
modules = [ modules = [
{ {
imports = users_mod ++ imports = users_mod ++ roles_mod ++ add_imports;
roles_mod ++ nixpkgs = {
add_imports ++ [ pkgs = pkgs;
(mkSysRole "common")
(mkSysRole "acme")
(mkUser { name = "root"; roles = [ ]; })
];
home-manager = {
useGlobalPkgs = true;
}; };
system.stateVersion = "24.11"; nix.nixPath = [
"nixpkgs=${nixpkgs}"
"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 home-manager.nixosModules.home-manager
../roles/common.nix
../roles/acme.nix
../hosts/${name}/default.nix ../hosts/${name}/default.nix
pkgs.nixosModules.cachixConfig
]; ];
}; };
} }

View File

@ -1,55 +1,36 @@
{ pkgs { pkgs, unstable, ... }:
, stdenv
, home-manager
, mkHomeRole
, ...
}:
{ {
mkUser = { name, roles ? [ ] }: mkUser = { name, roles ? [ ] }:
let let
roles_mod = (map (r: mkHomeRole r) roles); mkRole = role: import (../roles/home + "/${role}.nix");
in roles_mod = (map (r: mkRole r) roles);
{ in {
users.groups.plugdev = { };
fileSystems."/home/${name}/Downloads" = { 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";
extraGroups = [ "wheel" "plugdev" ];
shell = pkgs.zsh; shell = pkgs.zsh;
}; extraGroups = [ "wheel" "plugdev" ];
}; };
programs.zsh.enable = true; home-manager.users.${name}.imports = [ (mkRole "common") ]
++ roles_mod;
home-manager.users.${name}.imports = [
(mkHomeRole "common")
(mkHomeRole "zsh")
(mkHomeRole "aichat")
] ++ roles_mod;
}; };
mkHMUser = { name, roles ? [] }: mkHMUser = { name, roles ? [] }:
let let
roles_mod = (map (r: mkHomeRole r) roles); mkRole = role: import (../roles/home + "/${role}.nix");
in in{
home-manager.lib.homeManagerConfiguration { home-manager.users.${name}.imports = [ (mkRole "common") ]
inherit pkgs; ++ roles_mod;
modules = [ };
{
home = {
username = name;
homeDirectory =
if stdenv.isLinux then "/home/${name}" else "/Users/${name}";
};
}
(mkHomeRole "common")
(mkHomeRole "aichat")
] ++ roles_mod;
}; };
} }

View File

@ -1,6 +0,0 @@
{ ... }:
{
mkSysRole = role: import (../roles/${role}.nix);
mkHomeRole = role: import (../roles/home/${role}.nix);
}

View File

@ -1,25 +1,4 @@
{ config, ... }: { ... }: {
security.acme.acceptTerms = true;
let security.acme.email = "sysadmin@giugl.io";
giuglioDomain = "giugl.io";
in
{
age.secrets.ovh = {
file = ../secrets/ovh.age;
owner = "acme";
};
security.acme = {
acceptTerms = true;
certs.${giuglioDomain} =
{
dnsProvider = "ovh";
environmentFile = config.age.secrets.ovh.path;
extraDomainNames = [ "*.${giuglioDomain}" ];
};
defaults = {
email = "letsencrypt@depasquale.giugl.io";
dnsProvider = "ovh";
environmentFile = config.age.secrets.ovh.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.unstablePkgs.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,204 +0,0 @@
---
model: ollama:pino-coder
temperature: 0
---
You are a panel of three expert developers specializing in commit message generation:
- A (Version Control Specialist): Expert in Git workflows and commit conventions
- B (Code Review Expert): Specializes in code change analysis and impact assessment
- C (Technical Writer): Focuses on clarity, consistency, and documentation standards
Commit Convention Format:
<type>(<scope>): <description>
[body description]
Types:
- feat: New feature
- fix: Bug fix
- docs: Documentation changes
- style: Code style changes (non-functional)
- refactor: Code restructuring (non-functional)
- test: Test-related changes
- chore: Build process or tool changes
- perf: Performance improvements
Panel Analysis Process:
1. Initial Assessment:
- Alex: Analyzes commit convention compliance and change scope
- Blake: Reviews technical changes and their impact
- Casey: Evaluates message clarity and completeness
2. Message Components:
- Type Selection: Panel agrees on the most appropriate type
- Scope Definition: Identify affected components/systems
- Description: Craft clear, concise summary, bullet points only
3. Quality Criteria:
- Conventional commits compliance
- Technical accuracy
- Clear and concise language
- Meaningful context
- Future maintainer consideration
- Breaking change identification
Guidelines:
- Exclude trivial changes (imports, formatting)
- Focus on functional and behavioral changes
- Include breaking changes prominently
- Reference relevant issue numbers
- Keep first line under 72 characters
- Use imperative mood ("add" not "added")
### INPUT:
diff --git a/src/utils/date-formatter.js b/src/utils/date-formatter.js
index 2345678..3456789 100644
--- a/src/utils/date-formatter.js
+++ b/src/utils/date-formatter.js
@@ -5,7 +5,7 @@ export function formatDate(date) {
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
- return `$${year}-$${month}-$${day}`;
+ return `$${year}/$${month}/$${day}`;
}
### OUTPUT:
fix(date-formatter): modified `formatDate()` to use '/' instead of '-' as the separator
### INPUT:
diff --git a/src/app.js b/src/app.js
index 83d2e7a..b6a1c3f 100644
--- a/src/app.js
+++ b/src/app.js
@@ -10,6 +10,10 @@ function initialize() {
setupEventListeners();
}
+// TODO: add other listeners
+// https://github.com/user/project/issue/123
+function setupEventListeners() {
+ document.getElementById('submit').addEventListener('click', handleSubmit);
+ document.getElementById('reset').addEventListener('click', handleReset);
+}
+
function handleSubmit(event) {
event.preventDefault();
const data = new FormData(event.target);
@@ -20,6 +24,10 @@ function handleSubmit(event) {
console.log('Form submitted:', data);
}
+function handleReset(event) {
+ event.preventDefault();
+ event.target.form.reset();
+ console.log('Form reset');
}
### OUTPUT:
feat(app): implement form event listeners
- Added `setupEventListeners()` to handle form interactions
- Implemented `handleReset()` for form reset functionality
- Added event listeners for submit and reset buttons
- Track TODO comment for future listener additions (https://github.com/user/project/issue/123)
### INPUT:
diff --git a/pkg/database/client.go b/pkg/database/client.go
index 003740f..6fc4861 100644
--- a/pkg/database/client.go
+++ b/pkg/database/client.go
@@ -24,9 +24,12 @@ var ErrNilDatabaseClient = errors.New("database client is nil after setup")
// InitDB initializes the database with the given application name and optional dbpath for SQLite.
func InitDB(appName string, dbpath ...string) error {
- cfg := config.New()
+ var (
+ psqlReadReplica string
+ err error
+ )
- var err error
+ cfg := config.New()
// Set up a new logger with your required configuration.
newLogger := logger.New(
@@ -38,9 +41,8 @@ func InitDB(appName string, dbpath ...string) error {
},
)
- // Load PostgreSQL configurations
- var psqlReadReplica string
psqlSource, err := cfg.Load(config.PSQL.String())
+
if err != nil {
log.Println("PSQL not set, using SQLite instead.")
} else {
### OUTPUT:
style(database/client): group together `psqlReadReplica` and `err` in function's prologue
### INPUT:
diff --git a/pkg/khttp/client.go b/pkg/khttp/client.go
index a53064c..3aff938 100644
--- a/pkg/khttp/client.go
+++ b/pkg/khttp/client.go
@@ -11,14 +11,17 @@ import (
"github.pie.apple.com/kerosene/Core/structs"
)
+// TODO: https://github.pie.apple.com/Kerosene/Core/issues/43
+// feat: Centralise and remove over use of os.Environment
const (
- // Environment variables and file names.
authFilesDirEnvVar = "WHISPER_AUTH_FILES_DIR"
keyName = "decrypted_key.pem"
certName = "cert.pem"
)
// Error for missing environment variable.
+// TODO: refactor: move errors into centralized errors.go files
+// https://github.pie.apple.com/Kerosene/Core/issues/57
var errMissingEnvironmentVariable = fmt.Errorf("%s environment variable is not set", authFilesDirEnvVar)
// AuthConfig holds authentication file paths.
@@ -31,9 +34,11 @@ type AuthConfig struct {
// NewAuthConfig creates an AuthConfig from environment variables.
func NewAuthConfig() (*AuthConfig, error) {
dir := os.Getenv(authFilesDirEnvVar)
+
if dir == "" {
return nil, errMissingEnvironmentVariable
}
+
return &AuthConfig{
Dir: dir,
CertFile: filepath.Join(dir, certName),
@@ -211,7 +216,7 @@ func setupMTLSOnlyTransport(certData string, keyData string) (*http.Transport, e
// Make scheme and Auth Type separate and load from DB.
func parseProxyURL(scheme string, routing structs.Routing) (*url.URL, error) {
- return url.Parse(fmt.Sprintf("%s://%s:%s", scheme, routing.Proxy, routing.Port))
+ return url.Parse(fmt.Sprintf("%s://%s:%d", scheme, routing.Proxy, routing.Port))
}
// loadX509KeyPair loads an X509 key pair from the specified cert and key files.
### OUTPUT:
fix/refactor(khttp/client): use correct format specifier for and add TODOs
- Parsed proxy URL using `fmt.Sprintf()` with correct format specifier for port
- Added TODOs to centralize errors and remove overuse of `os.Environment` (#43, #57)

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,28 +1,208 @@
{ pkgs, ... }: { config, pkgs, ... }:
{ let unstable = import <nixpkgs-unstable> { };
imports = [ in {
./zsh.nix imports = [ ./zsh.nix ./git.nix ];
./git.nix
./helix.nix
];
home = { home = {
packages = with pkgs; [ sessionVariables = {
rizin EDITOR = "nvim";
sshfs VISUAL = "nvim";
victor-mono };
home-manager };
ripgrep
ydiff
nix-index
pipenv
htop
glances
tree
]
++ lib.optional (!pkgs.stdenv.isDarwin) pastebinit;
stateVersion = "24.11"; 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,8 +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";
extraConfig = { extraConfig = {
filter."lfs" = { filter."lfs" = {
process = "git-lfs filter-process"; process = "git-lfs filter-process";
@ -12,11 +12,6 @@
}; };
}; };
delta.enable = true; delta.enable = true;
lfs.enable = true;
aliases = {
ai = ''! cd -- "''${GIT_PREFIX:-.}" && git diff HEAD -- "$@" | aichat -m ollama:pino-coder -r commitmessage #'';
ais = ''! cd -- "''${GIT_PREFIX:-.}" && git diff --staged HEAD -- "$@" | aichat -m ollama:pino-coder -r commitmessage #'';
};
}; };
home.packages = [ pkgs.git-lfs ]; home.packages = [ pkgs.git-lfs ];
} }

View File

@ -1,6 +0,0 @@
{ config, pkgs, lib, ... }: {
programs.go = {
enable = true;
goPath = ".local/share/go";
};
}

View File

@ -1,61 +0,0 @@
{ pkgs, ... }:
let
actualPkgs = pkgs.unstablePkgs;
lib = actualPkgs.lib;
nodePkgs = with actualPkgs.nodePackages; [
vscode-langservers-extracted
typescript
svelte-language-server
yaml-language-server
typescript-language-server
bash-language-server
];
py3 = actualPkgs.python3.withPackages (ps: with ps; [
python-lsp-server
python-lsp-ruff
pylsp-rope
rope
mypy
pylsp-mypy
]);
in
{
home = {
packages = with actualPkgs; [
black
helix
clang-tools
rust-analyzer
nixd
texlab
nixpkgs-fmt
shellcheck
shfmt
gopls
golangci-lint
golangci-lint-langserver
py3
ruff
gh
gofumpt
taplo
docker-compose-language-service
mdformat
marksman
dockerfile-language-server-nodejs
] ++ nodePkgs;
sessionVariables = {
EDITOR = "hx";
VISUAL = "hx";
};
file = {
".config/helix/config.toml".text = lib.readFile ./helix/config.toml;
".config/helix/languages.toml".text = lib.readFile ./helix/languages.toml;
};
};
}

View File

@ -1,21 +0,0 @@
theme = "monokai_pro_spectrum"
[editor]
cursorline = true
true-color = true
gutters = ["diff", "diagnostics", "line-numbers", "spacer"]
[editor.cursor-shape]
insert = "bar"
normal = "block"
select = "underline"
[editor.lsp]
display-messages = true
[editor.indent-guides]
render = true
[editor.statusline]
left = ["mode", "spinner"]
center = ["file-name"]

View File

@ -1,36 +0,0 @@
[[language]]
name = "nix"
formatter = { command = "nixpkgs-fmt" }
language-servers = ["nixd"]
[language-server.nixd]
command = "nixd"
[language-server.pylsp.config.pylsp.plugins]
ruff = { enabled = true }
rope = { enabled = true }
mypy = { enabled = true }
[[language]]
name = "bash"
formatter = { command = "shfmt", args = ["-s", "-ci", "-sr"] }
[[language]]
name = "go"
language-servers = ["gopls", "golangci-lint-langserver"]
[language-server.golangci-lint-langserver]
command = "golangci-lint-langserver"
[language-server.golangci-lint-langserver.config]
command = [
"golangci-lint",
"run",
"--out-format",
"json",
"--issues-exit-code=1",
]
[[language]]
name = "markdown"
formatter = { command = "mdformat", args = ["-"]}

View File

@ -1,192 +0,0 @@
{ config, pkgs, ... }:
{
home = {
sessionVariables = {
EDITOR = "nvim";
VISUAL = "nvim";
};
};
programs.neovim = {
enable = true;
viAlias = true;
vimAlias = true;
extraPackages = with pkgs; [
nodePackages.prettier
nodePackages.pyright
rnix-lsp
rust-analyzer
cmake-format
clang-tools
rustfmt
nixfmt
shfmt
];
plugins = with pkgs.vimPlugins; [
vim-nix
molokai
vim-airline
vim-airline-themes
vim-lsp
vim-indent-guides
vim-signify
vim-fugitive
vimtex
neoformat
nvim-lspconfig
vim-vsnip
nvim-cmp
cmp-nvim-lsp
(nvim-treesitter.withPlugins (_: pkgs.tree-sitter.allGrammars))
nvim-treesitter-textobjects
minimap-vim
];
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
" 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
" Config minimap
let g:minimap_width = 10
let g:minimap_auto_start = 1
let g:minimap_auto_start_win_enter = 1
lua << EOF
-- Setup leap-nvim keymappings
require('leap').add_default_mappings()
------------------
-- 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
'';
};
}

View File

@ -1,203 +0,0 @@
create_pr_from_files() {
local TIMESTAMP=$(date +%Y%m%d%H%M%S)
local temp_branch="pr-${TIMESTAMP}-temp"
local pr_branch="pr-${TIMESTAMP}"
local base_branch="development"
local working_branch=$(git rev-parse --abbrev-ref HEAD)
local files=()
local expanded_files=()
local temp_branch_created=false
cleanup() {
git checkout "${working_branch}"
if [ "$temp_branch_created" = true ]; then
git checkout "${temp_branch}" -- "${expanded_files[@]}"
git restore --staged .
git branch -D "${temp_branch}"
fi
git branch -D "${pr_branch}"
}
handle_error() {
local error_msg="$1"
echo "Error: ${error_msg}"
cleanup
return 1
}
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
-b | --base)
base_branch="$2"
shift 2
;;
*)
files+=("$1")
shift
;;
esac
done
if [ ${#files[@]} -eq 0 ]; then
echo "Usage: create_pr_from_files <file1> [<file2> ...] [-b <base-branch>]"
return 1
fi
# Expand files and directories
for file in "${files[@]}"; do
if [ -d "$file" ]; then
while IFS= read -r line; do
expanded_files+=("$line")
done < <(find "$file" -type f)
else
expanded_files+=("$file")
fi
done
# Check if there are any uncommitted changes
if [ -n "$(git status -s)" ]; then
# Only create temp branch and backup if there are uncommitted changes
git checkout -b "$temp_branch" || (
handle_error "Failed to create temporary branch"
return $?
)
git commit -am "Backup changes" || (
handle_error "Failed to commit changes to temporary branch"
return $?
)
temp_branch_created=true
fi
# Get current branch and switch to base branch if needed
current_branch=$(git rev-parse --abbrev-ref HEAD)
if [ "$current_branch" != "$base_branch" ]; then
git checkout "$base_branch" || (
handle_error "Failed to checkout base branch"
return $?
)
git pull || (
handle_error "Failed to sync base branch"
return $?
)
fi
git checkout -b "$pr_branch" || (
handle_error "Failed to create PR branch"
return $?
)
# Restore files either from temp branch or original branch
if [ "$temp_branch_created" = true ]; then
git checkout "$temp_branch" -- "${expanded_files[@]}" || (
handle_error "Failed to restore specified files"
return $?
)
else
git checkout "$working_branch" -- "${expanded_files[@]}" || (
handle_error "Failed to restore specified files"
return $?
)
fi
# Verify files were staged
if [ -z "$(git diff --staged)" ]; then
handle_error "No files were staged. Aborting PR creation."
return $?
fi
# Generate commit message
echo "Generating commit message..."
local commit_message=$(git ais || handle_error "Failed to generate commit message.")
local commit_subject=$(echo "$commit_message" | head -n 1)
local commit_body=$(echo "$commit_message" | tail -n +2)
# Commit the specified files
git commit --edit -m "$commit_subject"$'\n\n'"$commit_body" || handle_error "Committing files failed."
if [ $? -ne 0 ]; then
handle_error "Committing files failed."
fi
# Push the PR branch to the remote repository
git push origin "$pr_branch" || handle_error "Failed to push PR branch"
# Create the pull request
gh pr create \
--base "$base_branch" \
--head "$pr_branch" || handle_error "Failed to create pull request"
echo "Pull request created successfully."
# Cleanup
cleanup
}
create_pr_from_commit() {
local commit_hash="$1"
local base_branch="${2:-development}"
if [ -z "$commit_hash" ]; then
echo "Usage: create_pr_from_commit <commit-hash> [<base-branch>]"
return 1
fi
# Check for uncommitted changes and stash them if any
local has_changes=false
if ! git diff --quiet || ! git diff --cached --quiet; then
echo "Stashing uncommitted changes..."
git stash push -u
has_changes=true
fi
# Create a new branch name based on the commit hash
local new_branch="pr-${commit_hash}"
# Checkout to the new branch and cherry-pick the commit
git checkout "$base_branch" && git checkout -b "$new_branch"
if ! git cherry-pick "$commit_hash"; then
echo "Cherry-picking failed. Cleaning up."
git checkout -
git branch -D "$new_branch"
if $has_changes; then
echo "Restoring stashed changes..."
git stash pop
fi
return 1
fi
git push origin "$new_branch"
local commit_message=$(git show -s --format=%B "$commit_hash")
local commit_subject=$(echo "$commit_message" | head -n 1)
local commit_body=$(echo "$commit_message" | tail -n +2)
gh pr create --base "$base_branch" --head "$new_branch" --title "$commit_subject" --body "$commit_body"
echo "Pull request created successfully."
if $has_changes; then
echo "Restoring stashed changes..."
git stash pop
fi
}
anonymize_multiple() {
while IFS= read -r line; do
result="$line"
# Simple sed with multiple patterns
echo "$result" | sed '
s/github.pie.apple.com/github.com/g;
s/Kerosene/org/g;
s/kerosene/org/g;
s/Shelob/example/g;
s/shelob/example/g;
'
done
}

View File

@ -1,4 +1,4 @@
{ lib, pkgs, ... }: { pkgs, lib, ... }:
{ {
programs.ssh = { programs.ssh = {
@ -11,17 +11,6 @@
identityFile = "~/.ssh/architectproxy"; identityFile = "~/.ssh/architectproxy";
}; };
"192.35.222.32" = {
user = "giulio";
identityFile = "~/.ssh/ucsb";
};
"ucsb-reynolds" = {
hostname = "128.111.49.76";
user = "giulio";
identityFile = "~/.ssh/ucsb";
};
"tommy.devs.giugl.io" = { "tommy.devs.giugl.io" = {
user = "giulio"; user = "giulio";
identityFile = "~/.ssh/tommypc"; identityFile = "~/.ssh/tommypc";
@ -120,7 +109,7 @@
"git.seclab.cs.ucsb.edu" = { "git.seclab.cs.ucsb.edu" = {
user = "peperunas"; user = "peperunas";
identityFile = "~/.ssh/ucsb"; identityFile = "~/.ssh/gitlab-ucsb";
}; };
"architect.devs.giugl.io" = { "architect.devs.giugl.io" = {
@ -147,11 +136,6 @@
identityFile = "~/.ssh/github"; identityFile = "~/.ssh/github";
}; };
"code.iti.illinois.edu" = {
user = "gitlab";
identityFile = "~/.ssh/github";
};
"git.ctf.necst.it" = { "git.ctf.necst.it" = {
user = "ctf"; user = "ctf";
identityFile = "~/.ssh/gitlab_necst"; identityFile = "~/.ssh/gitlab_necst";
@ -179,24 +163,12 @@
user = "aur"; user = "aur";
identityFile = "~/.ssh/aur"; identityFile = "~/.ssh/aur";
}; };
"ucsb-workstation.devs.giugl.io" = {
user = "giulio";
identityFile = "~/.ssh/ucsb";
forwardAgent = true;
};
}; };
extraConfig = '' extraConfig = ''
IdentitiesOnly yes IdentitiesOnly yes
ServerAliveInterval 3600 ServerAliveInterval 3600
Include config.d/*
${if pkgs.stdenv.isDarwin then
''
AddKeysToAgent yes AddKeysToAgent yes
UseKeychain yes '' + lib.optionalString pkgs.stdenv.isDarwin "UseKeychain yes";
TCPKeepAlive no
'' else ""}
'';
}; };
} }

View File

@ -1,35 +1,14 @@
{ pkgs, ... }: { config, pkgs, lib, ... }: {
let
commitFunctions = pkgs.writeTextDir "bin/commits.sh" (builtins.readFile ./scripts/commits.sh);
in
{
home.packages = with pkgs; [ any-nix-shell ];
programs.zsh = { programs.zsh = {
enable = true; enable = true;
oh-my-zsh = { oh-my-zsh = {
enable = true; enable = true;
plugins = [ "git" "sudo" "docker" "docker-compose" "systemd" ]; plugins = [ "git" "sudo" "docker" "docker-compose" "adb" "systemd" ];
theme = "bira"; theme = "bira";
}; };
autosuggestion.enable = false;
dotDir = ".config/zsh";
initExtra = '' initExtra = ''
# avoid macOS updates to destroy nix source ~/.profile
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'
fi
any-nix-shell zsh --info-right | source /dev/stdin
source ${pkgs.nix-index}/etc/profile.d/command-not-found.sh
${if pkgs.stdenv.isDarwin then "export PATH=/opt/homebrew/opt/ruby/bin:/opt/homebrew/bin:/opt/homebrew/sbin:$PATH" else ""}
recap() { aichat -f "$@" ;}
source ${commitFunctions}/bin/commits.sh
''; '';
}; };
} }

8
roles/zsh.nix Normal file
View File

@ -0,0 +1,8 @@
{ ... }:
{
programs.zsh = {
enableBashCompletion = true;
enableCompletion = true;
};
}

Binary file not shown.

Binary file not shown.

View File

@ -1,27 +0,0 @@
age-encryption.org/v1
-> ssh-rsa QXZdow
JFZ512g1V5fHSCDuxPcpFGSSAzI6326lbmmQaepxfPyTzZpK5Qo7WaUeF0dCmwi1
mwS038cbo57hPnuGapJtrqggiVm8B53rli7xlwFQCydVkxnKPSvcERI8KphEn1K5
1YGeU6XdqqNyv1NSV9V8A4Y74LMk1H+igWR5sWZnO6sQi7LLAwfL+BsskdwY0ZuW
9TOzkeZtgU5qy9IbN6liouEMliO660q1sb+OxQFP8pVIS3xt9mD2IE4W3hP9aZyF
JHUZPizwF+HvspR8oMV4R7JI4gexBwnMVeu4HVu+ayY2udQvr2DNxQNHM66zClPo
7G67rblH6IfCOrOieqIVvYrbJQuSZip4npnQyXVXzg/wQ6CGu0k4E8wF1xHFYKAO
LGWK8uUxffC1ITEfNMaSs/3AKMuqBsJcDXYYe4yq4lJYxSfwXbu+G6aqOgHYAe7p
LBQgl5Dn19r/7zKRLJTK4eJ0ah8bnWWTU9FcHAJbqKFYK6DW+syqFYinXfwt9AQI
g0w5apgPm/B3PX0wKiabci8c4AZ6n2JVWvI9sJkhcL5t93JS9uBsgxzc3Hv4nu3E
zD1Skp648In+oQ+6xuDmIuEuu8xIhGwU3jhJeIiTZwX54wj35v/gNLU2sH1hK/90
vyJcZClmpGDsOu/vHeKPSfP29MEzlahA5dZS0DDkt58
-> ssh-ed25519 7eGqHw AJlmB4Up3Zs4gNdfRRt8zZ5r1M8DcXSdj7B09VUlYCk
Vteh5QnSqhIrXm10zdOjP+Lhm3qwABqGgQFHfrnrjH4
-> ssh-rsa tO3rGg
VPAsazrTmffI7Y0LOsLwAoeOtz9lnDm3vYTDcFi8DoJcHsXDh2cYib1hET4noWLf
gFQiP30rNKTvkBDeThdH5opyZbO9BfDX1IgJo5Fm7yO3LdSWB44fL3Mn8HoMKGkn
d6TKM0ZxDZAkApTMcKHjHlcnWgy5sGxW0pHDnBvCCqsQHqRywcGDZTVhmxshLxQw
giQo3ZI8fzD436bY+rWYJtqWKcOnBLGEiFoWJr9qfLcG2FwB0xLppfX7S6htLQpn
btqafMtA8HgGVkVGC+uADqghPGzO/rN/z571xvZ6F4GyeB1/2RbVX62N4jN8FlPc
+6UWe3kgxM9cOedpwYPqte3gIETWBxlfpspOfVaRv6qMx6ZM1mPsP1qTpQNUabm2
2Ale/EkLnfYzwXmaiql0/oEuqq7Dp806XP5AcKxZHNUJeZHRdqOUHGCNJzfAO3H4
uazZGDtZR+pSq0QwEZqp1GoodtzCbBnbko5ZwVYXIXc1gSbwvP6ZW/5HiPEM0jaM
--- TXLi+4AqW9L3grKPVMBDb75OHyjatQzBxUlI4Xe1eMw
ÛÞÁ }ccn‡ó…¹'ÏF¥At«5ËT Ƶ E]Òx7írÑ|kô§ÿ<C2A7><C3BF>µI°¼ú×%}´¿‰<C2BF>¬#=<3D>J.

View File

@ -1,29 +0,0 @@
age-encryption.org/v1
-> ssh-rsa QXZdow
aYgowxTfdGOqTYOZBbkg/dH7f+m6nvVF/8qZX0DE4hazln/QS9maWbkOwD7FLldm
HRNV/YwZZEhbujHbDqgxnXk7Q11KOA72864B6mF2VZUruyo0cnACqo7OyzwApqv/
+LPjGb9h/gCJpQ3a5Jdh202FfaNGAh358fZVDyd37XPSOykiIAAxgMlDyn+96OiM
P2vsyduWXDsqzCqtiNQrKVjryI5CIGOTAcYTgQ35S3uXFD8Gu27KfagUwZp2hdyp
3WmGl+ZTrPNdOwzLWGj/RXaeTslABn1Owmq1naASRvJpp97ToynRzkDA50rBqUyR
vGVB9IJxSjkSm3BJ4UAI6rpoz/6t2jkfNNE1cPix4AYjPAMyU+uiUSaZ/UBkwlXw
08rM1eGcBaErB1ExcDV5+jUCdJBfi6Q9vIG7Ty4wbN1PfztAhzEyzT0L1bTn1AKC
4S9n5lqFa1CdraK9eh2A+o9CNlkta+Z24ctPTVqBYtImBTKHOTofhr0omQdFV6M2
bhxsOoAAoNhwn/lWC2fAcgfPQrUOW524+eHyPjsvf4rNNv0bk5EP1J4vMrWr9rqJ
v5GEQ77YVXYQthiyg74XYc3Eo8sbtE+ncDoOquzdT385POd870qi1ht+JMY6OEmj
q8lxVau2SFTKPkkmZKmtoNrYdKp5+DsB3nOUKcIXofs
-> ssh-ed25519 7eGqHw cCrhq1kfav4TYAUOpP4O6fQ958O37Uad2jX9SUrnxn4
TSiMyrYsdblB5SFwZpw7HhmicWX1vNomhBP4HtlvHJo
-> ssh-rsa tO3rGg
J6oPMt6hiry6ks3hlAjUAY1AzEYU+7voto5XC+I6Fmyfabz9zaJ3TtbCPVF5BRNR
DOYLiD24EbcVoqECn2A2MRK1xH4owBD5YaE3Il2NwSJHhC+ZhROaMTu5mHxbzK/u
BF2MLRZ0Bwwq4szaHoFf12TFwNtIRZXS9m6l4jHdsxWj6x0iui18p3JLxij1cVwE
03rSWz+9c8bpZ6LHuPJAhatBZHSZwkKwH8Dn8NOxCLmVNRM4PyvJsj9lRn7fMwRY
64QI2z6bRAry6oINbVAAOsPlM0Ix+7hbFs/UstnENFqfcDvPzrrhALDhuDLIJpGu
WgAaMStZGjydy0oqHJceuduxVreqTlfiki7yruRFqRBgjMopwOsw5i9UPWR6SZ+E
cUCFeEynUMrmFSp5qvDX0WtkU2G/GRFEPaB+k+UN+JduIRb2RBCLt2uG0249TwO8
T4sq098XTM8wARgOv6n51lHFCPpM3iSbP5KMCYH9FhsJV0Qu9Q7157McNZuVL9Ie
--- KYLAPCcTkg/tF2c2ni4UaBTV5AhUleg8GgJH0oRQSK0
½;¬jŒ<6A>a羄ïÓÄ<C393>5`hÂŒø»æy;JúãÈå³C¢µ‡£ÏwX:eßøw³ù»ÜH
L<EFBFBD>he­jCÓ2¨ì"#˵„=Î/Dzˆ1ÒÅÿ¼™^Nû$ÃéM·úqN…v1µØÁÇ”ç¸T¦ÌñÙ—Ç0FsÕ(WeõöË…¡˜Ý8|^iYFQæ3œ ¡Õ­
A¤1­ïEÜÂÚM_=;•¸×jFÜVý[Ýät°¬{© w×…Ê<E280A6>Ö)

View File

@ -1,29 +0,0 @@
age-encryption.org/v1
-> ssh-rsa QXZdow
muUhcAzcKFoopF3H69fYU/CzBezvnBhgBKUqmFqjWVpLpzU/h75DPUMZcpT59dP1
rjJw8KEevEn6wnEG6KM5X1qKlQGKNYv1Ei8bFZ2KkIHQol77KA4UwfJOkZ75miNI
ZqYN2YT1acBtZVQn4Z1nsg3BKMKBFQVEvBmNh2tV38Zgnw3bPU06BKX07/gbaYvd
JGFWDik92eVkgHO5LPiIgQEhP/blCv28ELZ9CkRJXmz6Z+r7AINfSUwhRTLSG3E9
D5mYFcFF7mdmH7BFEvuk1kJiIxlrQoMgDa/8csmAYr/ma8jAb0fUK1vih4vdYPGL
Q2lHQPXJ7eJoYtn9mP3Bo8mRVuwYHyaSyKMxt3UEgCPJ4QI6N23Z7+7j9hJw9rNK
z9yheUaw8srCDz+ZLeSFvZ/gNLT7moTBYnjYPnsx3kYqKLNHyzTBKtbtQhI0PIkO
9ezOmH6GBqocEjA8XZ49VgB9+NWr/UVXI9qx+TNUTTzFyAZstcqOn32xCaRzPSBw
cpgPyIgWJ7wVOAWsevBSNqSntew0PCrStWKODiHGen3Z3lOCKeQloD9ANuF90iT8
7Ub0aGHMSlb3V6vX6lexc6mLF//ybtpvZ2FSyZfnj2iJRu8FAGdYpN5Ci9pfaTgF
v5CcQ+PqyyvPTgWBY4R244Vg4WKfvua65GAL8oxTERs
-> ssh-ed25519 7eGqHw I5j3zjd1QQzfFQXjZx8bC+wH3HkGOx2tJHlYax8pfTI
0+fXs8fEBjTXvLaTZH2QDWUIOT6+ZakpVyWGhOIm5Z0
-> ssh-rsa tO3rGg
OjfxuSAoX27FdTmDHfx7lYwYLP526SHbwNMuLwg2jdQlBbHZ3jsIDrTwTBpm2Q8R
K4T5wOUlicWvHz2RLQmjlrU9F0ksElhE6ZaqjgvBa1fIFFPNDm3Pl01Zs/NHnNGn
tetIDCkgWHqS/LtQv/RNzHlqb1H360fQLwPNamxR+kECpR7jy2aujsQxcilzPW+h
+s29T1CRTFd3kksW0cmiEXAH+nz8Orhz4GdJfFiIYmzUD/U/XsfF7V81ABrYBtxG
DxVqk5zwjYlCckyegMhjkKkpcJuZgkF0OpC9znxgy1s49irgJ1LNHuL9XvuSn81Z
U8/7qIXwumpx8hl2Fp52/qfu+z/Sgb4sNGdDwDabryVMM0iA44sW3A8968aEnU4+
ij4+MHuoiif9Gjd1OzxIpugg565hmbrpJHmLz/bwxSVuj/Q7EqfN4Q6WoXA4LPm+
D4U74W1rCqUY2lidiLG9xHjh48WVCyPaMMDTm/fryfUmbDU6tfgl+HedMQShFuut
--- AOqar+uICSyq8I8qWgkRiMW2dY73yezKi0RHaTmsbC4
 Qcv"àð·i;ïÕ`6Ï?]ÎÐ…èǹ# {œÛ¡<C39B>ËÎ^Q†Y<E280A0>;<¯ª:¬³~Þr~bœ¨Á_ÈÊÅ#š>é­3¨`RtYk™“”†»è~Cú<43>S÷tô5Êt<C38A>Úå1}ÊBQññ
Ç †l
²Ý„›!87ùP

View File

@ -1,28 +0,0 @@
age-encryption.org/v1
-> ssh-rsa QXZdow
tEqh6kH9Ctbirf94dBBvdYkYABBvkQYqoZEo7a3/EnFlwvkDxZoo9O8WiQ+fLhOI
jrAmdezC11UcvZK0D4KN34S1VgnQWwChTuOMWy5oTl9195GJm/1PQq8iyHFmCK63
DdZXE+MPbawlA/T+rsQghBX3TwNMYhfPw8+qfMC4A+5KhWzDPLYVidUvM2QwnoDZ
Zthek8bAOhwF/wZH7SI7QTQwe3x3kUyP3SbVipwguctRP7mNtRj/roVrfUoig7/L
SywHYmeBG6Z3kuWABoQIjF6TKS4No5NH5VKdJCtGlsSRUqJHa1GojSZUzgu0ARRK
v/Z/E6b64CnDZ1E+nZLr54PmrgjRbStqyvMxoQwYzu01TE6NU0h7aAgvk+S0AncK
AYgEkmsXxkYMSM0qUFvcGILNU5ZtyvhwS61Q13bZNM3+0CGcSv8lhQmJFrZbePmV
A1Jh+8JCxVJnNyEXLGPoofM8ds5Gtc35Iu5it5z2ZzJ3V1pRwTPzVlSuY1AygSvh
OTKg9kH4V3J311M0HJfG8CkOp8W1AvAfWagB9Y+E2KsL9riKpd9W+Rz6qB+u+q6r
bjKNy8oBEJ2xp9RAihQASeaBjK7v5bsgKy7L5GVVs9505pcKFOyWTVnbNdKsYYKs
sHW/dTVAGxf/SYz1cEpsp3ZPUe15h5+CuLf7OhI1RzI
-> ssh-ed25519 7eGqHw ws0TYpN8wBvtmJE2EsFF0Oz0v0kp/SN8nrc9eibd6m4
JKrIKa7Qescecpw5jkFcW4SgTaTtW3CocEg57rdS3A8
-> ssh-rsa tO3rGg
rn6k067Nol861dqxTId9zzWeupTMHik0597AR1vfyHJ+kBJhwNgj9bBPQYePoXcq
Ll91m0dX8TDN2RAcbl+ddxqkoedrCqa9RX7GxNG4nkAkVLAzIR3+B7cCjX06m+Mm
iI817kBXgIfy46HUtdft4D9R9y8G3RlnoPkV2msvlAAlps+tAkAsvIcMaWyWZF4U
fxOChL+RcRHUJ6mWzPU3EOES9pwmK+B+fI/25NRoWMlZDUWEJ8BEstDuQ6IORxbC
+DRGiQQCSVLyHkPI7KkXUxPeYjmitNdfAw5Cl0kn8rdXUn1AhceTfUsausqZMUOh
pSL6L8swiByy/vxO3HaNeSSVPyPVM8L9Cr9kqDTOoLJY2l1wSpNjbZrLoVunouIG
w8MyFxPxxpbPS7jPBI90kyrRfSyoDO6Va2EIW/YsVfOhYXIlA7qYe3Bo0xoT3B9R
awPedZO/qBzXVd3p+BwNwSxIRaBi5qchXn5B0kvv84tOtAlawrnKGly4mU0H42gN
--- cnd5/PWhWOHduSN+0fU4D3V2iLQE70ZSwBN8dW+YCw8
üÂTˆç"ÌHI+Ø ã‡ó^qmÆtê³Ý Y6_é½& %`ɱÝúâ­/ý¹æÅbd‡œ‡ãy4kˆ
YՌ

View File

@ -1,15 +0,0 @@
let
pubkeyModule = import ../hosts/pubkeys.nix;
pubkeys = [
pubkeyModule.macbook
] ++ pubkeyModule.groups.architect;
in
{
"matrix-synapse.age".publicKeys = pubkeys;
"teslamate.age".publicKeys = pubkeys;
"nextcloud-admin.age".publicKeys = pubkeys;
"nextcloud-database.age".publicKeys = pubkeys;
"restic-environment.age".publicKeys = pubkeys;
"restic-passwords.age".publicKeys = pubkeys;
"ovh.age".publicKeys = pubkeys;
}

View File

@ -1,27 +0,0 @@
age-encryption.org/v1
-> ssh-rsa QXZdow
IyHp/kqk6u/HazW25tlI9YykJ3AHySgPWFmQzIjh+BXyqo4qSKdNfQr1rIYFQGCJ
liIaMto8CWtbZUOiBXWtB/q3Z++Q0Qy8N1woYqVJ7gSlSbz1jKyDk2ZIrWCQ0CbT
zimI2gsdLEn5nkpV/NrkltH0/1aCW7HHzOo6UYp5YCQAwPO4eii636CYN9pFY8aD
wGuusZVsdEiP9+ETpxL8X0YDS6qWXAjrufEVSMmipxODGY9F9BncgrBXf6vNj4zv
/SudTaE4e1tfEQ8PjL+qE+aPMCVHITJsYWARiKIcUB4A2yLPxK4hEPuY+ikaV5nb
u2YBndS7RHA0c0xYAME1QZ2GOgFe995N+qgWM2pPmFhlFM7blzHLZPgNPQvQhaF1
dwv5mRnRhtLF27GWjtcPL0AaX2qWoVgWmjI03HY4m2RAXr+kPhs4asIb10iL5Zz2
I4GyupuX/yvds7ckTiVNc6HGPYgfN2re4ml0Lsgu+qMu6qkSSPwe4gdB8PRnlil4
JZS/rKXzLlqHW1P5PQLLaSO9DtiRIitbvNuWbTHdUK5bjEu8mjVzjT/u4JwHip7j
MpuWsSKEN6I+0hCfYfEwAWD4h6oTF+ckrRUXWg/p+K6IXBx4txCVHEZXymdBwf8I
eedRo2unHui7oT512HMXqx6DIIAPg/7Jr2/MWX+J6F8
-> ssh-ed25519 7eGqHw 9InUXz9Z8OvxNqVYckohNJYgFndSU5WH9VO9f4KnjhQ
lfE8tuSjZ5xJ19xzONy78dOzqZjqAk8RENdhBXoAXKY
-> ssh-rsa tO3rGg
t0P8ve/N9fxcBdIqmFajtIfQGTHXnwwaRRKJOoz/0PlH52Iat76P7IhdBipU9aJz
4lj2aFxYePD9Qz6+sLA4IibArW0Ej/XAehOwMiXU5NcD5ICcuc9dpBMekBzHTH6F
Z9fsz9ogKjBgfCulCDlf7XwQgXXx1+I2ar82y8Qix2esqO4fY4wXl7xQTONpKg0l
5Nethgwy6Xji2CBAsQDKm5xZ2FynUNWzk404pfDIkLvsU9NL53SHZwM8dzWiKxlq
g+uPlNYetfyFNWM1m018ev63adlrrBdzTwNBv+QTXF2fACarBxkqSPHLPrVn+DvM
mDPcXQJiORtMyOLJze2nt6ikZB/AqZWhGKFUpawI8MHx1HPlibG/cwKxLdmxexJz
Fk+EaGDeInyr7UflYjTQt2WlnaenittVwyIs08tqeJ/7mA/9uft6ThySIM/Cxsj0
sa85Pa6AnZhl5dpT7CIU3n1ZJIgk+ZLniMfZQdGxTVvZ2eqWhXqRhj9go0Obmk5G
--- fbeSdbhIc1G8BtYb99EUWMDa5Zgu2Pd1b2EL9mEs80Y
å‡; ÅÞøg üâ’gÔ1jìÔ·bý* g1<H<>/ -»óœ3¸Yøxó,oCÿ#^Nó<4E>Šý…€‰ˆ]¯ˆ$Çô«í½e· ãóPÿ\¦)X- Pþÿ¶Ê I•´Êä/íD]Bz¦ùB<C3B9>à ¾¶ôg¨rÓòž÷šT<>ý>ÁRéîæÌ…òå3½6