Compare commits

..

1 commit

Author SHA1 Message Date
aef71f548a
WIP: authelia: Add SSO 2024-06-14 01:01:39 +02:00
243 changed files with 3801 additions and 7855 deletions

View file

@ -1,14 +0,0 @@
# Run this command to always ignore formatting commits in `git blame`
# git config blame.ignoreRevsFile .git-blame-ignore-revs
# Switch to nixfmt formatting
04f7a7ef1d38906163afc9cddfa8ce2b0ebf3b45
# Switch to nixpkgs-fmt formatting
fd138d45e6a2cad89fead6e9f246ba282070d6b7
# Switch to alejandra formatting
046a88905ddfa7f9edc3291c310dbb985dee34f9
# Apply wide linting
63b3cbe00be80ccb4b221aad64eb657ae5c96d70

1
.gitignore vendored
View file

@ -1,3 +1,2 @@
/result
*.qcow2
/gcroots/

View file

@ -1,5 +1,5 @@
keys:
- &tlater 8322CC1D351D8EFE63F3 D27A8CF496E6E265C6A3!
- &tlater 535B61015823443941C744DD12264F6BBDFABA89
- &server_tlaternet 8a3737d48f1035fe6c3a0a8fd6a1976ca74c7f3b
- &server_hetzner1 0af7641adb8aa843136cf6d047f71da3e5ad79f9
- &server_staging 2f5caa73e7ceea4fcc8d2881fde587e6737d2dbc

View file

@ -1,61 +0,0 @@
{
self,
nixpkgs,
deploy-rs,
system,
...
}:
let
pkgs = nixpkgs.legacyPackages.${system};
statix' = pkgs.statix.overrideAttrs (old: {
patches = old.patches ++ [
(pkgs.fetchpatch {
url = "https://github.com/oppiliappan/statix/commit/925dec39bb705acbbe77178b4d658fe1b752abbb.patch";
hash = "sha256-0wacO6wuYJ4ufN9PGucRVJucFdFFNF+NoHYIrLXsCWs=";
})
];
});
runNuCheck =
{
name,
packages,
check,
}:
pkgs.stdenvNoCC.mkDerivation {
inherit name;
src = nixpkgs.lib.cleanSourceWith {
src = self;
filter = nixpkgs.lib.cleanSourceFilter;
};
dontPatch = true;
dontConfigure = true;
dontBuild = true;
dontInstall = true;
dontFixup = true;
doCheck = true;
checkInputs = nixpkgs.lib.singleton pkgs.nushell ++ packages;
checkPhase = ''
nu ${check}
'';
};
in
nixpkgs.lib.recursiveUpdate {
lints = runNuCheck {
name = "lints";
packages = [
pkgs.deadnix
pkgs.nixfmt-rfc-style
pkgs.shellcheck
statix'
];
check = ./lints.nu;
};
} (deploy-rs.lib.${system}.deployChecks self.deploy)

View file

@ -1,37 +0,0 @@
#!/usr/bin/env nu
let nix_files = ls **/*.nix | where name !~ "hardware-configuration.nix|_sources" | get name
let linters = [
([nixfmt --check --strict] ++ $nix_files)
([deadnix --fail] ++ $nix_files)
([statix check] ++ $nix_files)
]
mkdir $env.out
def run-linter [linterArgs: list<string>] {
print $'Running ($linterArgs.0)...'
let exit_code = try {
^$linterArgs.0 ...($linterArgs | skip 1)
$env.LAST_EXIT_CODE
} catch {|e| $e.exit_code}
[$linterArgs.0, $exit_code]
}
let results = $linters | each {|linter| run-linter $linter}
print 'Linter results:'
let success = $results | each {|result|
match $result.1 {
0 => {print $'(ansi green)($result.0)(ansi reset)'}
_ => {print $'(ansi red)($result.0)(ansi reset)'}
}
$result.1
} | math sum
exit $success

View file

@ -1,10 +1,11 @@
{
config,
pkgs,
lib,
modulesPath,
flake-inputs,
...
}:
{
}: {
imports = [
flake-inputs.disko.nixosModules.disko
flake-inputs.sops-nix.nixosModules.sops
@ -13,41 +14,49 @@
"${modulesPath}/profiles/minimal.nix"
(import ../modules)
./services/afvalcalendar.nix
./services/auth.nix
./services/backups.nix
./services/battery-manager.nix
./services/conduit
./services/crowdsec.nix
./services/conduit.nix
./services/fail2ban.nix
./services/foundryvtt.nix
./services/gitea.nix
./services/immich.nix
./services/metrics
./services/minecraft.nix
./services/nextcloud.nix
./services/webserver.nix
./services/wireguard.nix
# ./services/starbound.nix -- Not currently used
./services/starbound.nix
./services/postgres.nix
./nginx.nix
./sops.nix
];
nixpkgs.overlays = [ (_: prev: { local = import ../pkgs { pkgs = prev; }; }) ];
nixpkgs.overlays = [
(final: prev: {
local = import ../pkgs {
pkgs = prev;
lib = prev.lib;
};
})
];
nix = {
package = pkgs.nixFlakes;
extraOptions = ''
experimental-features = nix-command flakes
'';
# Enable remote builds from tlater
settings.trusted-users = [ "@wheel" ];
settings.trusted-users = ["@wheel"];
};
nixpkgs.config.allowUnfreePredicate = pkg:
builtins.elem (lib.getName pkg) ["steam-original" "steam-runtime" "steam-run" "steamcmd"];
# Optimization for minecraft servers, see:
# https://bugs.mojang.com/browse/MC-183518
boot.kernelParams = [
"highres=off"
"nohz=off"
];
boot.kernelParams = ["highres=off" "nohz=off"];
networking = {
usePredictableInterfaceNames = false;
@ -64,6 +73,8 @@
8448
# starbound
21025
# Minecraft
25565
config.services.coturn.listening-port
config.services.coturn.tls-listening-port
@ -72,6 +83,9 @@
];
allowedUDPPorts = [
# More minecraft
25565
config.services.coturn.listening-port
config.services.coturn.tls-listening-port
config.services.coturn.alt-listening-port
@ -93,14 +107,15 @@
users.users.tlater = {
isNormalUser = true;
extraGroups = [ "wheel" ];
openssh.authorizedKeys.keyFiles = [ ../keys/tlater.pub ];
extraGroups = ["wheel"];
openssh.authorizedKeys.keyFiles = [../keys/tlater.pub];
};
services = {
openssh = {
enable = true;
ports = [ 2222 ];
allowSFTP = false;
ports = [2222];
startWhenNeeded = true;
settings = {
@ -117,16 +132,16 @@
sudo.execWheelOnly = true;
pam = {
rssh = {
sshAgentAuth = {
enable = true;
settings.auth_key_file = "/etc/ssh/authorized_keys.d/$ruser";
authorizedKeysFiles = ["/etc/ssh/authorized_keys.d/%u"];
};
services.sudo.rssh = true;
services.sudo.sshAgentAuth = true;
};
};
# Remove some unneeded packages
environment.defaultPackages = [ ];
environment.defaultPackages = [];
system.stateVersion = "20.09";
}

View file

@ -8,13 +8,10 @@
# disables it by default.
#
# TODO(tlater): See if would be useful for anything?
boot.kernelParams = [ "nosgx" ];
boot.kernelParams = ["nosgx"];
networking.hostName = "hetzner-1";
services = {
btrfs.autoScrub.enable = true;
nginx.domain = "tlater.net";
};
services.nginx.domain = "tlater.net";
systemd.network.networks."eth0" = {
matchConfig.MACAddress = "90:1b:0e:c1:8c:62";
@ -22,11 +19,15 @@
addresses = [
# IPv4
{
Address = "116.202.158.55/32";
Peer = "116.202.158.1/32"; # Gateway
addressConfig = {
Address = "116.202.158.55/32";
Peer = "116.202.158.1/32"; # Gateway
};
}
# IPv6
{ Address = "2a01:4f8:10b:3c85::2/64"; }
{
addressConfig.Address = "2a01:4f8:10b:3c85::2/64";
}
];
networkConfig = {

View file

@ -1,106 +1,82 @@
{
disko.devices.disk =
let
bootPartition = {
size = "1M";
type = "EF02";
disko.devices.disk = let
bootPartition = {
size = "1M";
type = "EF02";
};
swapPartition = {
# 8G is apparently recommended for this much RAM, but we set up
# 4G on both disks for mirroring purposes.
#
# That'll still be 8G during normal operation, and it's probably
# not too bad to have slightly less swap if a disk dies.
size = "4G";
content = {
type = "swap";
randomEncryption = true;
};
};
swapPartition = {
# 8G is apparently recommended for this much RAM, but we set up
# 4G on both disks for mirroring purposes.
#
# That'll still be 8G during normal operation, and it's probably
# not too bad to have slightly less swap if a disk dies.
size = "4G";
content = {
type = "swap";
randomEncryption = true;
};
};
mountOptions = ["compress=zstd" "noatime"];
in {
sda = {
type = "disk";
device = "/dev/sda";
content = {
type = "gpt";
partitions = {
boot = bootPartition;
swap = swapPartition;
mountOptions = [
"compress=zstd"
"noatime"
];
in
{
sda = {
type = "disk";
device = "/dev/sda";
content = {
type = "gpt";
partitions = {
boot = bootPartition;
swap = swapPartition;
disk1 = {
size = "100%";
# Empty partition to combine in RAID0 with the other disk
};
disk1 = {
size = "100%";
# Empty partition to combine in RAID0 with the other disk
};
};
};
};
sdb = {
type = "disk";
device = "/dev/sdb";
content = {
type = "gpt";
partitions = {
boot = bootPartition;
swap = swapPartition;
sdb = {
type = "disk";
device = "/dev/sdb";
content = {
type = "gpt";
partitions = {
boot = bootPartition;
swap = swapPartition;
disk2 = {
size = "100%";
content = {
type = "btrfs";
# Hack to get multi-device btrfs going
# See https://github.com/nix-community/disko/issues/99
extraArgs = [
"-d"
"raid1"
"-m"
"raid1"
"--runtime-features"
"quota"
"/dev/sda3"
];
subvolumes = {
"/volume" = { };
"/volume/root" = {
inherit mountOptions;
mountpoint = "/";
};
"/volume/home" = {
inherit mountOptions;
mountpoint = "/home";
};
"/volume/var" = {
inherit mountOptions;
mountpoint = "/var";
};
"/volume/var/lib/private/matrix-conduit" = {
mountOptions = [
# Explicitly don't compress here, since
# conduwuit's database does compression by
# itself, and relies on being able to read the
# raw file data from disk (which is impossible
# if btrfs compresses it)
"noatime"
];
mountpoint = "/var/lib/private/matrix-conduit";
};
"/volume/nix-store" = {
inherit mountOptions;
mountpoint = "/nix";
};
"/snapshots" = { };
disk2 = {
size = "100%";
content = {
type = "btrfs";
# Hack to get multi-device btrfs going
# See https://github.com/nix-community/disko/issues/99
extraArgs = ["-d" "raid1" "-m" "raid1" "--runtime-features" "quota" "/dev/sda3"];
subvolumes = {
"/volume" = {};
"/volume/root" = {
inherit mountOptions;
mountpoint = "/";
};
"/volume/home" = {
inherit mountOptions;
mountpoint = "/home";
};
"/volume/var" = {
inherit mountOptions;
mountpoint = "/var";
};
"/volume/nix-store" = {
inherit mountOptions;
mountpoint = "/nix";
};
"/snapshots" = {};
};
};
};
};
};
};
};
}

View file

@ -1,35 +1,19 @@
{ lib, ... }:
{
{lib, ...}: {
users.users.tlater.password = "insecure";
# Disable graphical tty so -curses works
boot.kernelParams = [ "nomodeset" ];
boot.kernelParams = ["nomodeset"];
networking.hostName = "testvm";
services = {
# Sets the base domain for nginx to a local domain so that we can
# easily test locally with the VM.
nginx.domain = "dev.local";
# Don't run this
batteryManager.enable = lib.mkForce false;
openssh.hostKeys = lib.mkForce [
{
type = "rsa";
bits = 4096;
path = "/etc/staging.key";
}
];
};
# Sets the base domain for nginx to a local domain so that we can
# easily test locally with the VM.
services.nginx.domain = "dev.local";
# Use the staging secrets
sops.defaultSopsFile = lib.mkOverride 99 ../../keys/staging.yaml;
systemd.network.networks."10-eth0" = {
matchConfig.Name = "eth0";
gateway = [ "192.168.9.1" ];
networkConfig = {
Address = "192.168.9.2/24";
};
@ -43,6 +27,14 @@
source = ../../keys/hosts/staging.key;
};
services.openssh.hostKeys = lib.mkForce [
{
type = "rsa";
bits = 4096;
path = "/etc/staging.key";
}
];
virtualisation.vmVariant = {
virtualisation = {
memorySize = 3941;

View file

@ -1,31 +1,33 @@
{ config, lib, ... }:
{
services = {
nginx = {
enable = true;
recommendedTlsSettings = true;
recommendedOptimisation = true;
recommendedGzipSettings = true;
recommendedProxySettings = true;
clientMaxBodySize = "10G";
config,
lib,
...
}: {
services.nginx = {
enable = true;
recommendedTlsSettings = true;
recommendedOptimisation = true;
recommendedGzipSettings = true;
recommendedProxySettings = true;
clientMaxBodySize = "10G";
statusPage = true; # For metrics, should be accessible only from localhost
statusPage = true; # For metrics, should be accessible only from localhost
commonHttpConfig = ''
log_format upstream_time '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
'';
};
commonHttpConfig = ''
log_format upstream_time '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
'';
};
logrotate.settings = {
services.logrotate.settings =
{
# Override the default, just keep fewer logs
nginx.rotate = 6;
}
// lib.mapAttrs' (
virtualHost: _:
// lib.mapAttrs' (virtualHost: _:
lib.nameValuePair "/var/log/nginx/${virtualHost}/access.log" {
frequency = "daily";
rotate = 2;
@ -33,45 +35,33 @@
delaycompress = true;
su = "${config.services.nginx.user} ${config.services.nginx.group}";
postrotate = "[ ! -f /var/run/nginx/nginx.pid ] || kill -USR1 `cat /var/run/nginx/nginx.pid`";
}
) config.services.nginx.virtualHosts;
})
config.services.nginx.virtualHosts;
backups.acme = {
user = "acme";
paths = lib.mapAttrsToList (
virtualHost: _: "/var/lib/acme/${virtualHost}"
) config.services.nginx.virtualHosts;
};
};
systemd.tmpfiles.rules = lib.mapAttrsToList (
virtualHost: _:
#
"d /var/log/nginx/${virtualHost} 0750 ${config.services.nginx.user} ${config.services.nginx.group}"
) config.services.nginx.virtualHosts;
systemd.tmpfiles.rules =
lib.mapAttrsToList (
virtualHost: _:
#
"d /var/log/nginx/${virtualHost} 0750 ${config.services.nginx.user} ${config.services.nginx.group}"
)
config.services.nginx.virtualHosts;
security.acme = {
defaults.email = "tm@tlater.net";
acceptTerms = true;
certs."tlater.net" = {
extraDomainNames = [
"*.tlater.net"
"tlater.com"
"*.tlater.com"
];
dnsProvider = "porkbun";
group = "ssl-cert";
credentialFiles = {
PORKBUN_API_KEY_FILE = config.sops.secrets."porkbun/api-key".path;
PORKBUN_SECRET_API_KEY_FILE = config.sops.secrets."porkbun/secret-api-key".path;
};
extraDomainNames = ["*.tlater.net"];
dnsProvider = "hetzner";
group = "nginx";
credentialFiles."HETZNER_API_KEY_FILE" = config.sops.secrets."hetzner-api".path;
};
};
users.groups.ssl-cert = { };
systemd.services.nginx.serviceConfig.SupplementaryGroups = [
config.security.acme.certs."tlater.net".group
];
services.backups.acme = {
user = "acme";
paths =
lib.mapAttrsToList (virtualHost: _: "/var/lib/acme/${virtualHost}")
config.services.nginx.virtualHosts;
};
}

View file

@ -0,0 +1,67 @@
{
pkgs,
config,
...
}: {
systemd.services.afvalcalendar = {
description = "Enschede afvalcalendar -> ical converter";
wantedBy = ["multi-user.target"];
after = ["network.target"];
script = ''
${pkgs.local.afvalcalendar}/bin/afvalcalendar > /srv/afvalcalendar/afvalcalendar.ical
'';
startAt = "daily";
serviceConfig = {
DynamicUser = true;
ProtectHome = true; # Override the default (read-only)
PrivateDevices = true;
PrivateIPC = true;
PrivateUsers = true;
ProtectHostname = true;
ProtectClock = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectKernelLogs = true;
ProtectControlGroups = true;
RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"];
RestrictNamespaces = true;
LockPersonality = true;
MemoryDenyWriteExecute = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallFilter = ["@system-service" "~@privileged @resources @setuid @keyring"];
Umask = 0002;
SupplementaryGroups = "afvalcalendar-hosting";
ReadWritePaths = "/srv/afvalcalendar";
};
};
services.nginx.virtualHosts."afvalcalendar.${config.services.nginx.domain}" = {
forceSSL = true;
useACMEHost = "tlater.net";
enableHSTS = true;
root = "/srv/afvalcalendar";
};
users.groups.afvalcalendar-hosting = {};
systemd.tmpfiles.settings."10-afvalcalendar" = {
"/srv/afvalcalendar".d = {
user = "nginx";
group = "afvalcalendar-hosting";
mode = "0775";
};
"/srv/afvalcalendar/afvalcalendar.ical".f = {
user = "nginx";
group = "afvalcalendar-hosting";
mode = "0775";
};
};
}

View file

@ -0,0 +1,95 @@
{
pkgs,
config,
...
}: let
user = config.services.authelia.instances.main.user;
domain = "auth.${config.services.nginx.domain}";
in {
services.authelia.instances.main = {
enable = true;
settings = {
theme = "auto";
access_control.default_policy = "one_factor";
authentication_backend = {
password_reset.disable = true;
file.path = "/var/lib/authelia-main/users.yml";
};
notifier.filesystem.filename = "/var/lib/authelia-main/notification.txt";
session = {
domain = config.services.nginx.domain;
redis.host = config.services.redis.servers.authelia.unixSocket;
};
storage.postgres = {
host = "/run/postgresql";
port = 5432;
database = user;
username = user;
password = "unnecessary";
};
};
secrets = {
storageEncryptionKeyFile = config.sops.secrets."authelia/storageEncryptionKey".path; # Database
sessionSecretFile = config.sops.secrets."authelia/sessionSecret".path; # Redis
jwtSecretFile = config.sops.secrets."authelia/jwtSecret".path;
};
};
systemd.services.authelia-main.after = ["postgresql.service"];
services.nginx = {
# TODO(tlater): Possibly remove on next authelia release
additionalModules = with pkgs.nginxModules; [
develkit
set-misc
];
virtualHosts."${domain}" = {
forceSSL = true;
enableACME = true;
enableHSTS = true;
locations = {
"/" = {
proxyPass = "http://127.0.0.1:9091";
recommendedProxySettings = false;
enableAutheliaProxy = true;
};
"/api/verify" = {
proxyPass = "http://127.0.0.1:9091";
recommendedProxySettings = false;
};
};
};
};
services.redis.servers.authelia = {
inherit user;
enable = true;
};
sops.secrets = {
"authelia/storageEncryptionKey" = {
owner = user;
group = user;
};
"authelia/sessionSecret" = {
owner = user;
group = user;
};
"authelia/jwtSecret" = {
owner = user;
group = user;
};
};
}

View file

@ -3,33 +3,27 @@
pkgs,
lib,
...
}:
let
}: let
inherit (lib) types optional singleton;
mkShutdownScript =
service:
mkShutdownScript = service:
pkgs.writeShellScript "backup-${service}-shutdown" ''
if systemctl is-active --quiet '${service}'; then
touch '/tmp/${service}-was-active'
systemctl stop '${service}'
fi
'';
mkRestartScript =
service:
mkRestartScript = service:
pkgs.writeShellScript "backup-${service}-restart" ''
if [ -f '/tmp/${service}-was-active' ]; then
rm '/tmp/${service}-was-active'
systemctl start '${service}'
fi
'';
writeScript =
name: packages: text:
lib.getExe (
pkgs.writeShellApplication {
inherit name text;
runtimeInputs = packages;
}
);
writeScript = name: packages: text:
lib.getExe (pkgs.writeShellApplication {
inherit name text;
runtimeInputs = packages;
});
# *NOT* a TOML file, for some reason quotes are interpreted
# *literally
@ -48,213 +42,201 @@ let
RESTIC_REPOSITORY = "rclone:storagebox:backups";
RCLONE_CONFIG = rcloneConfig;
};
in
{
in {
options = {
services.backups = lib.mkOption {
description = lib.mdDoc ''
Configure restic backups with a specific tag.
'';
type = types.attrsOf (
types.submodule (
{ name, ... }:
{
options = {
user = lib.mkOption {
type = types.str;
description = ''
The user as which to run the backup.
'';
};
paths = lib.mkOption {
type = types.listOf types.str;
description = ''
The paths to back up.
'';
};
tag = lib.mkOption {
type = types.str;
description = ''
The restic tag to mark the backup with.
'';
default = name;
};
preparation = {
packages = lib.mkOption {
type = types.listOf types.package;
default = [ ];
description = ''
The list of packages to make available in the
preparation script.
'';
};
text = lib.mkOption {
type = types.nullOr types.str;
default = null;
description = ''
The preparation script to run before the backup.
This should include things like database dumps and
enabling maintenance modes. If a service needs to be
shut down for backups, use `pauseServices` instead.
'';
};
};
cleanup = {
packages = lib.mkOption {
type = types.listOf types.package;
default = [ ];
description = ''
The list of packages to make available in the
cleanup script.
'';
};
text = lib.mkOption {
type = types.nullOr types.str;
default = null;
description = ''
The cleanup script to run after the backup.
This should do things like cleaning up database dumps
and disabling maintenance modes.
'';
};
};
pauseServices = lib.mkOption {
type = types.listOf types.str;
default = [ ];
description = ''
The systemd services that need to be shut down before
the backup can run. Services will be restarted after the
backup is complete.
This is intended to be used for services that do not
support hot backups.
'';
};
type = types.attrsOf (types.submodule ({
config,
name,
...
}: {
options = {
user = lib.mkOption {
type = types.str;
description = ''
The user as which to run the backup.
'';
};
paths = lib.mkOption {
type = types.listOf types.str;
description = ''
The paths to back up.
'';
};
tag = lib.mkOption {
type = types.str;
description = ''
The restic tag to mark the backup with.
'';
default = name;
};
preparation = {
packages = lib.mkOption {
type = types.listOf types.package;
default = [];
description = ''
The list of packages to make available in the
preparation script.
'';
};
}
)
);
text = lib.mkOption {
type = types.nullOr types.str;
default = null;
description = ''
The preparation script to run before the backup.
This should include things like database dumps and
enabling maintenance modes. If a service needs to be
shut down for backups, use `pauseServices` instead.
'';
};
};
cleanup = {
packages = lib.mkOption {
type = types.listOf types.package;
default = [];
description = ''
The list of packages to make available in the
cleanup script.
'';
};
text = lib.mkOption {
type = types.nullOr types.str;
default = null;
description = ''
The cleanup script to run after the backup.
This should do things like cleaning up database dumps
and disabling maintenance modes.
'';
};
};
pauseServices = lib.mkOption {
type = types.listOf types.str;
default = [];
description = ''
The systemd services that need to be shut down before
the backup can run. Services will be restarted after the
backup is complete.
This is intended to be used for services that do not
support hot backups.
'';
};
};
}));
};
};
config = lib.mkIf (config.services.backups != { }) {
systemd.services = {
restic-prune = {
# Doesn't hurt to finish the ongoing prune
restartIfChanged = false;
config = lib.mkIf (config.services.backups != {}) {
systemd.services =
{
restic-prune = {
# Doesn't hurt to finish the ongoing prune
restartIfChanged = false;
environment = resticEnv;
environment = resticEnv;
path = with pkgs; [
openssh
rclone
restic
];
script = ''
# TODO(tlater): In an append-only setup, we should be
# careful with this; an attacker could delete backups by
# simply appending ad infinitum:
# https://restic.readthedocs.io/en/stable/060_forget.html#security-considerations-in-append-only-mode
restic forget --keep-last 3 --prune
restic check
'';
serviceConfig = {
DynamicUser = true;
Group = "backup";
CacheDirectory = "restic-prune";
CacheDirectoryMode = "0700";
};
};
}
// lib.mapAttrs' (
name: backup:
lib.nameValuePair "backup-${name}" {
# Don't want to restart mid-backup
restartIfChanged = false;
environment = resticEnv // {
RESTIC_CACHE_DIR = "%C/backup-${name}";
};
path = with pkgs; [
coreutils
openssh
rclone
restic
];
# TODO(tlater): If I ever add more than one repo, service
# shutdown/restarting will potentially break if multiple
# backups for the same service overlap. A more clever
# sentinel file with reference counts would probably solve
# this.
serviceConfig = {
User = backup.user;
Group = "backup";
RuntimeDirectory = "backup-${name}";
CacheDirectory = "backup-${name}";
CacheDirectoryMode = "0700";
PrivateTmp = true;
ExecStart = [
(lib.concatStringsSep " " (
[
"${pkgs.restic}/bin/restic"
"backup"
"--tag"
name
]
++ backup.paths
))
path = with pkgs; [
openssh
rclone
restic
];
ExecStartPre =
map (service: "+${mkShutdownScript service}") backup.pauseServices
++ singleton (
writeScript "backup-${name}-repo-init" [ ] ''
script = ''
# TODO(tlater): In an append-only setup, we should be
# careful with this; an attacker could delete backups by
# simply appending ad infinitum:
# https://restic.readthedocs.io/en/stable/060_forget.html#security-considerations-in-append-only-mode
restic forget --keep-last 3 --prune
restic check
'';
serviceConfig = {
DynamicUser = true;
Group = "backup";
CacheDirectory = "restic-prune";
CacheDirectoryMode = "0700";
};
};
}
// lib.mapAttrs' (name: backup:
lib.nameValuePair "backup-${name}" {
# Don't want to restart mid-backup
restartIfChanged = false;
environment =
resticEnv
// {
RESTIC_CACHE_DIR = "%C/backup-${name}";
};
path = with pkgs; [
coreutils
openssh
rclone
restic
];
# TODO(tlater): If I ever add more than one repo, service
# shutdown/restarting will potentially break if multiple
# backups for the same service overlap. A more clever
# sentinel file with reference counts would probably solve
# this.
serviceConfig = {
User = backup.user;
Group = "backup";
RuntimeDirectory = "backup-${name}";
CacheDirectory = "backup-${name}";
CacheDirectoryMode = "0700";
PrivateTmp = true;
ExecStart = [
(lib.concatStringsSep " " (["${pkgs.restic}/bin/restic" "backup" "--tag" name] ++ backup.paths))
];
ExecStartPre =
map (service: "+${mkShutdownScript service}") backup.pauseServices
++ singleton (writeScript "backup-${name}-repo-init" [] ''
restic snapshots || restic init
''
)
++ optional (backup.preparation.text != null) (
writeScript "backup-${name}-prepare" backup.preparation.packages backup.preparation.text
);
'')
++ optional (backup.preparation.text != null)
(writeScript "backup-${name}-prepare" backup.preparation.packages backup.preparation.text);
# TODO(tlater): Add repo pruning/checking
ExecStopPost =
map (service: "+${mkRestartScript service}") backup.pauseServices
++ optional (backup.cleanup.text != null) (
writeScript "backup-${name}-cleanup" backup.cleanup.packages backup.cleanup.text
);
# TODO(tlater): Add repo pruning/checking
ExecStopPost =
map (service: "+${mkRestartScript service}") backup.pauseServices
++ optional (backup.cleanup.text != null)
(writeScript "backup-${name}-cleanup" backup.cleanup.packages backup.cleanup.text);
};
})
config.services.backups;
systemd.timers =
{
restic-prune = {
wantedBy = ["timers.target"];
timerConfig.OnCalendar = "Thursday 03:00:00 UTC";
# Don't make this persistent, in case the server was offline
# for a while. This job cannot run at the same time as any
# of the backup jobs.
};
}
) config.services.backups;
systemd.timers = {
restic-prune = {
wantedBy = [ "timers.target" ];
timerConfig.OnCalendar = "Thursday 03:00:00 UTC";
# Don't make this persistent, in case the server was offline
# for a while. This job cannot run at the same time as any
# of the backup jobs.
};
}
// lib.mapAttrs' (
name: _:
lib.nameValuePair "backup-${name}" {
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "Wednesday 02:30:00 UTC";
RandomizedDelaySec = "1h";
FixedRandomDelay = true;
Persistent = true;
};
}
) config.services.backups;
// lib.mapAttrs' (name: backup:
lib.nameValuePair "backup-${name}" {
wantedBy = ["timers.target"];
timerConfig = {
OnCalendar = "Wednesday 02:30:00 UTC";
RandomizedDelaySec = "1h";
FixedRandomDelay = true;
Persistent = true;
};
})
config.services.backups;
users = {
# This user is only used to own the ssh key, because apparently
@ -263,7 +245,7 @@ in
group = "backup";
isSystemUser = true;
};
groups.backup = { };
groups.backup = {};
};
};
}

View file

@ -1,16 +1,17 @@
{ config, flake-inputs, ... }:
{
imports = [ flake-inputs.sonnenshift.nixosModules.default ];
config,
flake-inputs,
...
}: {
imports = [
flake-inputs.sonnenshift.nixosModules.default
];
services.batteryManager = {
enable = true;
battery = "3ca39300-c523-4315-b9a3-d030f85a9373";
emailFile = "${config.sops.secrets."battery-manager/email".path}";
passwordFile = "${config.sops.secrets."battery-manager/password".path}";
settings = {
battery_id = "3ca39300-c523-4315-b9a3-d030f85a9373";
log_level = "DEBUG";
};
};
}

View file

@ -0,0 +1,254 @@
{
pkgs,
config,
lib,
...
}: let
inherit (lib.strings) concatMapStringsSep;
cfg = config.services.matrix-conduit;
domain = "matrix.${config.services.nginx.domain}";
turn-realm = "turn.${config.services.nginx.domain}";
in {
services.matrix-conduit = {
enable = true;
settings.global = {
address = "127.0.0.1";
server_name = domain;
database_backend = "rocksdb";
turn_uris = let
address = "${config.services.coturn.realm}:${toString config.services.coturn.listening-port}";
tls-address = "${config.services.coturn.realm}:${toString config.services.coturn.tls-listening-port}";
in [
"turn:${address}?transport=udp"
"turn:${address}?transport=tcp"
"turns:${tls-address}?transport=udp"
"turns:${tls-address}?transport=tcp"
];
};
};
systemd.services.heisenbridge = let
replaceSecretBin = "${pkgs.replace-secret}/bin/replace-secret";
registrationFile = builtins.toFile "heisenbridge-registration.yaml" (builtins.toJSON {
id = "heisenbridge";
url = "http://127.0.0.1:9898";
as_token = "@AS_TOKEN@";
hs_token = "@HS_TOKEN@";
rate_limited = false;
sender_localpart = "heisenbridge";
namespaces = {
users = [
{
regex = "@irc_.*";
exclusive = true;
}
{
regex = "@heisenbridge:.*";
exclusive = true;
}
];
aliases = [];
rooms = [];
};
});
# TODO(tlater): Starting with systemd 253 it will become possible
# to do the credential setup as part of ExecStartPre/preStart
# instead.
#
# This will also make it possible to actually set caps on the
# heisenbridge process using systemd, so that we can run the
# identd process.
execScript = pkgs.writeShellScript "heisenbridge" ''
cp ${registrationFile} "$RUNTIME_DIRECTORY/heisenbridge-registration.yaml"
chmod 600 $RUNTIME_DIRECTORY/heisenbridge-registration.yaml
${replaceSecretBin} '@AS_TOKEN@' "$CREDENTIALS_DIRECTORY/heisenbridge_as-token" "$RUNTIME_DIRECTORY/heisenbridge-registration.yaml"
${replaceSecretBin} '@HS_TOKEN@' "$CREDENTIALS_DIRECTORY/heisenbridge_hs-token" "$RUNTIME_DIRECTORY/heisenbridge-registration.yaml"
chmod 400 $RUNTIME_DIRECTORY/heisenbridge-registration.yaml
${pkgs.heisenbridge}/bin/heisenbridge \
--config $RUNTIME_DIRECTORY/heisenbridge-registration.yaml \
--owner @tlater:matrix.tlater.net \
'http://localhost:${toString cfg.settings.global.port}'
'';
in {
description = "Matrix<->IRC bridge";
wantedBy = ["multi-user.target"];
after = ["conduit.service"];
serviceConfig = {
Type = "simple";
LoadCredential = "heisenbridge:/run/secrets/heisenbridge";
ExecStart = execScript;
DynamicUser = true;
RuntimeDirectory = "heisenbridge";
RuntimeDirectoryMode = "0700";
RestrictNamespaces = true;
PrivateUsers = true;
ProtectHostname = true;
ProtectClock = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectKernelLogs = true;
ProtectControlGroups = true;
RestrictAddressFamilies = ["AF_INET AF_INET6"];
LockPersonality = true;
RestrictRealtime = true;
ProtectProc = "invisible";
ProcSubset = "pid";
UMask = 0077;
# For the identd port
# CapabilityBoundingSet = ["CAP_NET_BIND_SERVICE"];
# AmbientCapabilities = ["CAP_NET_BIND_SERVICE"];
};
};
# Pass in the TURN secret via EnvironmentFile, not supported by
# upstream module currently.
#
# See also https://gitlab.com/famedly/conduit/-/issues/314
systemd.services.conduit.serviceConfig.EnvironmentFile = config.sops.secrets."turn/env".path;
services.coturn = {
enable = true;
no-cli = true;
use-auth-secret = true;
static-auth-secret-file = config.sops.secrets."turn/secret".path;
realm = turn-realm;
relay-ips = [
"116.202.158.55"
];
# SSL config
#
# TODO(tlater): Switch to letsencrypt once google fix:
# https://github.com/vector-im/element-android/issues/1533
pkey = config.sops.secrets."turn/ssl-key".path;
cert = config.sops.secrets."turn/ssl-cert".path;
# Based on suggestions from
# https://github.com/matrix-org/synapse/blob/develop/docs/turn-howto.md
# and
# https://www.foxypossibilities.com/2018/05/19/setting-up-a-turn-sever-for-matrix-on-nixos/
no-tcp-relay = true;
secure-stun = true;
extraConfig = ''
# Deny various local IP ranges, see
# https://www.rtcsec.com/article/cve-2020-26262-bypass-of-coturns-access-control-protection/
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
# *Allow* any IP addresses that we explicitly set as relay IPs
${concatMapStringsSep "\n" (ip: "allowed-peer-ip=${ip}") config.services.coturn.relay-ips}
# Various other security settings
no-tlsv1
no-tlsv1_1
# Monitoring
prometheus
'';
};
services.nginx.virtualHosts."${domain}" = {
useACMEHost = "tlater.net";
listen = [
{
addr = "0.0.0.0";
port = 80;
}
{
addr = "[::0]";
port = 80;
}
{
addr = "0.0.0.0";
port = 443;
ssl = true;
}
{
addr = "[::0]";
port = 443;
ssl = true;
}
{
addr = "0.0.0.0";
port = 8448;
ssl = true;
}
{
addr = "[::0]";
port = 8448;
ssl = true;
}
];
forceSSL = true;
enableHSTS = true;
extraConfig = ''
merge_slashes off;
'';
locations = {
"/_matrix" = {
proxyPass = "http://${cfg.settings.global.address}:${toString cfg.settings.global.port}";
# Recommended by conduit
extraConfig = ''
proxy_buffering off;
'';
};
# Add Element X support
# TODO(tlater): Remove when no longer required: https://github.com/vector-im/element-x-android/issues/1085
"=/.well-known/matrix/client" = {
alias = pkgs.writeText "well-known-matrix-client" (builtins.toJSON {
"m.homeserver".base_url = "https://${domain}";
"org.matrix.msc3575.proxy".url = "https://${domain}";
});
extraConfig = ''
default_type application/json;
add_header Access-Control-Allow-Origin "*";
'';
};
};
};
services.backups.conduit = {
user = "root";
paths = [
"/var/lib/private/matrix-conduit/"
];
# Other services store their data in conduit, so no other services
# need to be shut down currently.
pauseServices = ["conduit.service"];
};
}

View file

@ -1,182 +0,0 @@
{
pkgs,
config,
lib,
...
}:
let
inherit (lib.strings) concatMapStringsSep;
cfg = config.services.matrix-conduit;
domain = "matrix.${config.services.nginx.domain}";
turn-realm = "turn.${config.services.nginx.domain}";
in
{
imports = [
./heisenbridge.nix
./matrix-hookshot.nix
];
services = {
matrix-conduit = {
enable = true;
package = pkgs.matrix-continuwuity;
settings.global = {
address = "127.0.0.1";
server_name = domain;
new_user_displayname_suffix = "🦆";
allow_check_for_updates = true;
# Set up delegation: https://docs.conduit.rs/delegation.html#automatic-recommended
# This is primarily to make sliding sync work
well_known = {
client = "https://${domain}";
server = "${domain}:443";
};
turn_uris =
let
address = "${config.services.coturn.realm}:${toString config.services.coturn.listening-port}";
tls-address = "${config.services.coturn.realm}:${toString config.services.coturn.tls-listening-port}";
in
[
"turn:${address}?transport=udp"
"turn:${address}?transport=tcp"
"turns:${tls-address}?transport=udp"
"turns:${tls-address}?transport=tcp"
];
};
};
coturn = {
enable = true;
no-cli = true;
use-auth-secret = true;
static-auth-secret-file = config.sops.secrets."turn/secret".path;
realm = turn-realm;
relay-ips = [ "116.202.158.55" ];
# SSL config
pkey = "${config.security.acme.certs."tlater.net".directory}/key.pem";
cert = "${config.security.acme.certs."tlater.net".directory}/fullchain.pem";
# Based on suggestions from
# https://github.com/matrix-org/synapse/blob/develop/docs/turn-howto.md
# and
# https://www.foxypossibilities.com/2018/05/19/setting-up-a-turn-sever-for-matrix-on-nixos/
no-tcp-relay = true;
secure-stun = true;
extraConfig = ''
# Deny various local IP ranges, see
# https://www.rtcsec.com/article/cve-2020-26262-bypass-of-coturns-access-control-protection/
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
# *Allow* any IP addresses that we explicitly set as relay IPs
${concatMapStringsSep "\n" (ip: "allowed-peer-ip=${ip}") config.services.coturn.relay-ips}
# Various other security settings
no-tlsv1
no-tlsv1_1
# Monitoring
prometheus
'';
};
nginx.virtualHosts."${domain}" = {
useACMEHost = "tlater.net";
listen = [
{
addr = "0.0.0.0";
port = 80;
}
{
addr = "[::0]";
port = 80;
}
{
addr = "0.0.0.0";
port = 443;
ssl = true;
}
{
addr = "[::0]";
port = 443;
ssl = true;
}
{
addr = "0.0.0.0";
port = 8448;
ssl = true;
}
{
addr = "[::0]";
port = 8448;
ssl = true;
}
];
forceSSL = true;
enableHSTS = true;
extraConfig = ''
merge_slashes off;
'';
locations = {
"/_matrix" = {
proxyPass = "http://${cfg.settings.global.address}:${toString cfg.settings.global.port}";
# Recommended by conduit
extraConfig = ''
proxy_buffering off;
'';
};
"/.well-known/matrix" = {
proxyPass = "http://${cfg.settings.global.address}:${toString cfg.settings.global.port}";
};
};
};
backups.conduit = {
user = "root";
paths = [ "/var/lib/private/matrix-conduit/" ];
# Other services store their data in conduit, so no other services
# need to be shut down currently.
pauseServices = [ "conduit.service" ];
};
};
systemd.services.conduit.serviceConfig = {
ExecStart = lib.mkForce "${config.services.matrix-conduit.package}/bin/conduwuit";
# Pass in the TURN secret via EnvironmentFile, not supported by
# upstream module currently.
#
# See also https://gitlab.com/famedly/conduit/-/issues/314
EnvironmentFile = config.sops.secrets."turn/env".path;
};
systemd.services.coturn.serviceConfig.SupplementaryGroups = [
config.security.acme.certs."tlater.net".group
];
}

View file

@ -1,78 +0,0 @@
{
pkgs,
lib,
config,
...
}:
let
conduitCfg = config.services.matrix-conduit;
matrixLib = pkgs.callPackage ./lib.nix { };
in
{
systemd.services.heisenbridge =
let
registration = matrixLib.writeRegistrationScript {
id = "heisenbridge";
url = "http://127.0.0.1:9898";
sender_localpart = "heisenbridge";
namespaces = {
users = [
{
regex = "@irc_.*";
exclusive = true;
}
{
regex = "@heisenbridge:.*";
exclusive = true;
}
];
aliases = [ ];
rooms = [ ];
};
};
in
{
description = "Matrix<->IRC bridge";
wantedBy = [ "multi-user.target" ];
after = [ "conduit.service" ];
serviceConfig = {
Type = "exec";
LoadCredential = "heisenbridge:/run/secrets/heisenbridge";
inherit (registration) ExecStartPre;
ExecStart = lib.concatStringsSep " " [
"${lib.getExe pkgs.heisenbridge}"
"--config \${RUNTIME_DIRECTORY}/heisenbridge-registration.yaml"
"--owner @tlater:matrix.tlater.net"
"http://localhost:${toString conduitCfg.settings.global.port}"
];
DynamicUser = true;
RuntimeDirectory = "heisenbridge";
RuntimeDirectoryMode = "0700";
RestrictNamespaces = true;
PrivateUsers = true;
ProtectHostname = true;
ProtectClock = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectKernelLogs = true;
ProtectControlGroups = true;
RestrictAddressFamilies = [ "AF_INET AF_INET6" ];
LockPersonality = true;
RestrictRealtime = true;
ProtectProc = "invisible";
ProcSubset = "pid";
UMask = 77;
# For the identd port
# CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
# AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
};
};
}

View file

@ -1,67 +0,0 @@
{
lib,
writeShellScript,
formats,
replace-secret,
}:
let
replaceSecretBin = "${lib.getExe replace-secret}";
in
{
# Write a script that will set up the service's registration.yaml
# with secrets from systemd credentials.
#
# The credentials should be named `${id}_as-token` and
# `${id}_hs-token`.
#
# This registration file needs to be manually added to conduit by
# messaging the admin with the yaml file.
#
# TODO(tlater): Conduwuit seems to support a CLI interface for this,
# may want to migrate to that sometime.
writeRegistrationScript =
{
id, # Must be unique among all registered appservices/bots
url, # The URL on which the service listens
sender_localpart,
rate_limited ? false,
namespaces ? {
aliases = [ ];
rooms = [ ];
users = [ ];
},
extraSettings ? { },
# The location to place the file; assumes systemd runtime dir
runtimeRegistration ? "$RUNTIME_DIRECTORY/${id}-registration.yaml",
}:
let
registrationFile = (formats.yaml { }).generate "${id}-registration.yaml" (
{
inherit
id
url
sender_localpart
rate_limited
namespaces
;
as_token = "@AS_TOKEN@";
hs_token = "@HS_TOKEN@";
}
// extraSettings
);
in
{
inherit runtimeRegistration;
ExecStartPre = writeShellScript "${id}-registration-setup.sh" ''
cp -f ${registrationFile} "${runtimeRegistration}"
chmod 600 "${runtimeRegistration}"
# Write actual secrets into config
${replaceSecretBin} '@AS_TOKEN@' "$CREDENTIALS_DIRECTORY/${id}_as-token" "${runtimeRegistration}"
${replaceSecretBin} '@HS_TOKEN@' "$CREDENTIALS_DIRECTORY/${id}_hs-token" "${runtimeRegistration}"
chmod 400 "${runtimeRegistration}"
'';
};
}

View file

@ -1,166 +0,0 @@
{
pkgs,
lib,
config,
...
}:
let
matrixLib = pkgs.callPackage ./lib.nix { };
cfg = config.services.matrix-hookshot;
conduitCfg = config.services.matrix-conduit;
domain = conduitCfg.settings.global.server_name;
registration = matrixLib.writeRegistrationScript {
id = "matrix-hookshot";
url = "http://127.0.0.1:9993";
sender_localpart = "hookshot";
namespaces = {
aliases = [ ];
rooms = [ ];
users = [
{
regex = "@${cfg.settings.generic.userIdPrefix}.*:${domain}";
exclusive = true;
}
];
};
# Encryption support
# TODO(tlater): Enable when
# https://github.com/matrix-org/matrix-hookshot/issues/1060 is
# fixed
# extraSettings = {
# "de.sorunome.msc2409.push_ephemeral" = true;
# push_ephemeral = true;
# "org.matrix.msc3202" = true;
# };
runtimeRegistration = "${cfg.registrationFile}";
};
in
{
# users = {
# users.matrix-hookshot = {
# home = "/run/matrix-hookshot";
# group = "matrix-hookshot";
# isSystemUser = true;
# };
# groups.matrix-hookshot = { };
# };
systemd.services.matrix-hookshot = {
serviceConfig = {
Type = lib.mkForce "exec";
LoadCredential = "matrix-hookshot:/run/secrets/matrix-hookshot";
inherit (registration) ExecStartPre;
# Some library in matrix-hookshot wants a home directory
Environment = [ "HOME=/run/matrix-hookshot" ];
# User = "matrix-hookshot";
DynamicUser = true;
StateDirectory = "matrix-hookshot";
RuntimeDirectory = "matrix-hookshot";
RuntimeDirectoryMode = "0700";
RestrictNamespaces = true;
PrivateUsers = true;
ProtectHostname = true;
ProtectClock = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectKernelLogs = true;
ProtectControlGroups = true;
RestrictAddressFamilies = [
# "AF_UNIX"
"AF_INET"
"AF_INET6"
];
LockPersonality = true;
RestrictRealtime = true;
ProtectProc = "invisible";
ProcSubset = "pid";
UMask = 77;
};
};
# services.redis.servers.matrix-hookshot = {
# enable = true;
# user = "matrix-hookshot";
# };
services.matrix-hookshot = {
enable = true;
serviceDependencies = [ "conduit.service" ];
registrationFile = "/run/matrix-hookshot/registration.yaml";
settings = {
bridge = {
inherit domain;
url = "http://localhost:${toString conduitCfg.settings.global.port}";
mediaUrl = conduitCfg.settings.global.well_known.client;
port = 9993;
bindAddress = "127.0.0.1";
};
bot.displayname = "Hookshot";
# cache.redisUri = "redis://${config.services.redis.servers.matrix-hookshot.unixSocket}";
generic = {
enabled = true;
outbound = false;
# Only allow webhooks from localhost for the moment
urlPrefix = "http://127.0.0.1:9000/webhook";
userIdPrefix = "_webhooks_";
allowJsTransformationFunctions = true;
};
# TODO(tlater): Enable when
# https://github.com/matrix-org/matrix-hookshot/issues/1060 is
# fixed
# encryption.storagePath = "/var/lib/matrix-hookshot/cryptostore";
permissions = [
{
actor = "matrix.tlater.net";
services = [
{
service = "*";
level = "notifications";
}
];
}
{
actor = "@tlater:matrix.tlater.net";
services = [
{
service = "*";
level = "admin";
}
];
}
];
listeners = [
{
port = 9000;
resources = [ "webhooks" ];
}
{
port = 9001;
resources = [ "metrics" ];
}
];
metrics.enabled = true;
};
};
}

View file

@ -1,83 +0,0 @@
{
pkgs,
config,
lib,
...
}:
{
security.crowdsec = {
enable = true;
parserWhitelist = [ "10.45.249.2" ];
extraGroups = [
"systemd-journal"
"nginx"
];
acquisitions = [
{
source = "journalctl";
labels.type = "syslog";
journalctl_filter = [ "SYSLOG_IDENTIFIER=Nextcloud" ];
}
{
source = "journalctl";
labels.type = "syslog";
journalctl_filter = [ "SYSLOG_IDENTIFIER=sshd-session" ];
}
{
labels.type = "nginx";
filenames = [
"/var/log/nginx/*.log"
]
++ lib.mapAttrsToList (
vHost: _: "/var/log/nginx/${vHost}/access.log"
) config.services.nginx.virtualHosts;
}
];
remediationComponents.firewallBouncer = {
enable = true;
settings.prometheus = {
enabled = true;
listen_addr = "127.0.0.1";
listen_port = "60601";
};
};
};
# Add whitelists for matrix
systemd.tmpfiles.settings."10-matrix" =
let
stateDir = config.security.crowdsec.stateDirectory;
in
{
"${stateDir}/config/postoverflows".d = {
user = "crowdsec";
group = "crowdsec";
mode = "0700";
};
"${stateDir}/config/postoverflows/s01-whitelist".d = {
user = "crowdsec";
group = "crowdsec";
mode = "0700";
};
"${stateDir}/config/postoverflows/s01-whitelist/matrix-whitelist.yaml"."L+".argument =
((pkgs.formats.yaml { }).generate "crowdsec-matrix-whitelist.yaml" {
name = "tetsumaki/matrix";
description = "custom matrix whitelist";
whitelist = {
reason = "whitelist false positive for matrix";
expression = [
"evt.Overflow.Alert.Events[0].GetMeta('target_fqdn') == '${config.services.matrix-conduit.settings.global.server_name}'"
"evt.Overflow.Alert.GetScenario() in ['crowdsecurity/http-probing', 'crowdsecurity/http-crawl-non_statics']"
];
};
}).outPath;
};
}

View file

@ -0,0 +1,42 @@
{pkgs, ...}: {
services.fail2ban = {
enable = true;
extraPackages = [pkgs.ipset];
banaction = "iptables-ipset-proto6-allports";
bantime-increment.enable = true;
jails = {
nginx-botsearch = ''
enabled = true
logpath = /var/log/nginx/access.log
'';
};
ignoreIP = [
"127.0.0.0/8"
"10.0.0.0/8"
"172.16.0.0/12"
"192.168.0.0/16"
];
};
# Allow metrics services to connect to the socket as well
users.groups.fail2ban = {};
systemd.services.fail2ban.serviceConfig = {
ExecStartPost =
"+"
+ (pkgs.writeShellScript "fail2ban-post-start" ''
while ! [ -S /var/run/fail2ban/fail2ban.sock ]; do
sleep 1
done
while ! ${pkgs.netcat}/bin/nc -zU /var/run/fail2ban/fail2ban.sock; do
sleep 1
done
${pkgs.coreutils}/bin/chown root:fail2ban /var/run/fail2ban /var/run/fail2ban/fail2ban.sock
${pkgs.coreutils}/bin/chmod 660 /var/run/fail2ban/fail2ban.sock
${pkgs.coreutils}/bin/chmod 710 /var/run/fail2ban
'');
};
}

View file

@ -2,48 +2,42 @@
lib,
config,
flake-inputs,
pkgs,
...
}:
let
}: let
domain = "foundryvtt.${config.services.nginx.domain}";
in
{
imports = [ flake-inputs.foundryvtt.nixosModules.foundryvtt ];
in {
imports = [flake-inputs.foundryvtt.nixosModules.foundryvtt];
services = {
foundryvtt = {
enable = true;
hostName = domain;
minifyStaticFiles = true;
proxySSL = true;
proxyPort = 443;
package = flake-inputs.foundryvtt.packages.${pkgs.system}.foundryvtt_13;
};
nginx.virtualHosts."${domain}" =
let
inherit (config.services.foundryvtt) port;
in
{
forceSSL = true;
useACMEHost = "tlater.net";
enableHSTS = true;
locations."/" = {
proxyWebsockets = true;
proxyPass = "http://localhost:${toString port}";
};
};
backups.foundryvtt = {
user = "foundryvtt";
paths = [ config.services.foundryvtt.dataDir ];
pauseServices = [ "foundryvtt.service" ];
};
services.foundryvtt = {
enable = true;
hostName = domain;
minifyStaticFiles = true;
proxySSL = true;
proxyPort = 443;
};
# Want to start it manually when I need it, not have it constantly
# running
systemd.services.foundryvtt.wantedBy = lib.mkForce [ ];
systemd.services.foundryvtt.wantedBy = lib.mkForce [];
services.nginx.virtualHosts."${domain}" = let
inherit (config.services.foundryvtt) port;
in {
forceSSL = true;
useACMEHost = "tlater.net";
enableHSTS = true;
locations."/" = {
proxyWebsockets = true;
proxyPass = "http://localhost:${toString port}";
};
};
services.backups.foundryvtt = {
user = "foundryvtt";
paths = [
config.services.foundryvtt.dataDir
];
pauseServices = ["foundryvtt.service"];
};
}

View file

@ -3,81 +3,93 @@
config,
lib,
...
}:
let
}: let
domain = "gitea.${config.services.nginx.domain}";
in
{
services = {
forgejo = {
enable = true;
database.type = "postgres";
in {
services.forgejo = {
enable = true;
database.type = "postgres";
settings = {
server = {
DOMAIN = domain;
HTTP_ADDR = "127.0.0.1";
ROOT_URL = "https://${domain}/";
SSH_PORT = 2222;
};
metrics = {
ENABLED = true;
TOKEN = "#metricstoken#";
};
service.DISABLE_REGISTRATION = true;
session.COOKIE_SECURE = true;
};
};
# Set up SSL
nginx.virtualHosts."${domain}" =
let
httpAddress = config.services.forgejo.settings.server.HTTP_ADDR;
httpPort = config.services.forgejo.settings.server.HTTP_PORT;
in
{
forceSSL = true;
useACMEHost = "tlater.net";
enableHSTS = true;
locations."/".proxyPass = "http://${httpAddress}:${toString httpPort}";
locations."/metrics" = {
extraConfig = ''
access_log off;
allow 127.0.0.1;
${lib.optionalString config.networking.enableIPv6 "allow ::1;"}
deny all;
'';
};
settings = {
server = {
DOMAIN = domain;
HTTP_ADDR = "127.0.0.1";
ROOT_URL = "https://${domain}/";
SSH_PORT = 2222;
};
backups.forgejo = {
user = "forgejo";
paths = [
"/var/lib/forgejo/forgejo-db.sql"
"/var/lib/forgejo/repositories/"
"/var/lib/forgejo/data/"
"/var/lib/forgejo/custom/"
# Conf is backed up via nix
];
preparation = {
packages = [ config.services.postgresql.package ];
text = "pg_dump ${config.services.forgejo.database.name} --file=/var/lib/forgejo/forgejo-db.sql";
metrics = {
ENABLED = true;
TOKEN = "#metricstoken#";
};
cleanup = {
packages = [ pkgs.coreutils ];
text = "rm /var/lib/forgejo/forgejo-db.sql";
};
pauseServices = [ "forgejo.service" ];
service.DISABLE_REGISTRATION = true;
session.COOKIE_SECURE = true;
};
};
systemd.services.forgejo.serviceConfig.ExecStartPre =
let
replaceSecretBin = "${pkgs.replace-secret}/bin/replace-secret";
secretPath = config.sops.secrets."forgejo/metrics-token".path;
runConfig = "${config.services.forgejo.customDir}/conf/app.ini";
in
[ "+${replaceSecretBin} '#metricstoken#' '${secretPath}' '${runConfig}'" ];
systemd.services.forgejo.serviceConfig.ExecStartPre = let
replaceSecretBin = "${pkgs.replace-secret}/bin/replace-secret";
secretPath = config.sops.secrets."forgejo/metrics-token".path;
runConfig = "${config.services.forgejo.customDir}/conf/app.ini";
in [
"+${replaceSecretBin} '#metricstoken#' '${secretPath}' '${runConfig}'"
];
# Set up SSL
services.nginx.virtualHosts."${domain}" = let
httpAddress = config.services.forgejo.settings.server.HTTP_ADDR;
httpPort = config.services.forgejo.settings.server.HTTP_PORT;
in {
forceSSL = true;
useACMEHost = "tlater.net";
enableHSTS = true;
locations."/".proxyPass = "http://${httpAddress}:${toString httpPort}";
locations."/metrics" = {
extraConfig = ''
access_log off;
allow 127.0.0.1;
${lib.optionalString config.networking.enableIPv6 "allow ::1;"}
deny all;
'';
};
};
# Block repeated failed login attempts
#
# TODO(tlater): Update this - we switched to forgejo, who knows what
# the new matches are.
# environment.etc = {
# "fail2ban/filter.d/gitea.conf".text = ''
# [Definition]
# failregex = .*(Failed authentication attempt|invalid credentials|Attempted access of unknown user).* from <HOST>
# journalmatch = _SYSTEMD_UNIT=forgejo.service + _COMM=forgejo + SYSLOG_IDENTIFIER=forgejo
# '';
# };
# services.fail2ban.jails = {
# gitea = ''
# enabled = true
# '';
# };
services.backups.forgejo = {
user = "forgejo";
paths = [
"/var/lib/forgejo/forgejo-db.sql"
"/var/lib/forgejo/repositories/"
"/var/lib/forgejo/data/"
"/var/lib/forgejo/custom/"
# Conf is backed up via nix
];
preparation = {
packages = [config.services.postgresql.package];
text = "pg_dump ${config.services.forgejo.database.name} --file=/var/lib/forgejo/forgejo-db.sql";
};
cleanup = {
packages = [pkgs.coreutils];
text = "rm /var/lib/forgejo/forgejo-db.sql";
};
pauseServices = ["forgejo.service"];
};
}

View file

@ -1,67 +0,0 @@
{
pkgs,
config,
lib,
...
}:
let
hostName = "immich.${config.services.nginx.domain}";
in
{
services = {
immich = {
enable = true;
settings.server.externalDomain = "https://${hostName}";
environment.IMMICH_TELEMETRY_INCLUDE = "all";
};
nginx.virtualHosts.${hostName} =
let
local = "http://${config.services.immich.host}:${toString config.services.immich.port}";
in
{
forceSSL = true;
useACMEHost = "tlater.net";
enableHSTS = true;
locations."/" = {
proxyPass = local;
proxyWebsockets = true;
};
locations."/metrics" = {
extraConfig = ''
access_log off;
allow 127.0.0.1;
${lib.optionalString config.networking.enableIPv6 "allow ::1;"}
deny all;
'';
};
};
backups.immich =
let
db-dump = "${config.services.immich.mediaLocation}/immich-db.sql";
in
{
user = "immich";
paths = [ config.services.immich.mediaLocation ];
preparation = {
packages = [ config.services.postgresql.package ];
text = ''
pg_dump ${config.services.immich.database.name} --clean --if-exists --file=${db-dump}
'';
};
cleanup = {
packages = [ pkgs.coreutils ];
text = "rm ${db-dump}";
};
pauseServices = [
"immich-server.service"
"immich-machine-learning.service"
];
};
};
}

View file

@ -5,6 +5,5 @@
./exporters.nix
./grafana.nix
./victoriametrics.nix
./victorialogs.nix
];
}

View file

@ -3,49 +3,23 @@
pkgs,
lib,
...
}:
let
yaml = pkgs.formats.yaml { };
in
{
}: let
yaml = pkgs.formats.yaml {};
in {
services.prometheus = {
exporters = {
blackbox = {
enable = true;
listenAddress = "127.0.0.1";
configFile = yaml.generate "blackbox.yaml" {
modules = {
http_2xx = {
prober = "http";
timeout = "5s";
http.preferred_ip_protocol = "ip4";
};
turn_server = {
prober = "tcp";
timeout = "5s";
tcp = {
preferred_ip_protocol = "ip4";
source_ip_address = "116.202.158.55";
tls = true;
};
};
};
};
};
# Periodically check domain registration status
domain = {
enable = true;
listenAddress = "127.0.0.1";
extraFlags =
let
conf.domains = [
"tlater.net"
"tlater.com"
];
in
[ "--config=${yaml.generate "domains.yml" conf}" ];
extraFlags = let
conf.domains = [
"tlater.net"
"tlater.com"
];
in [
"--config=${yaml.generate "domains.yml" conf}"
];
};
# System statistics
@ -74,29 +48,54 @@ in
listenAddress = "127.0.0.1";
group = "nginx";
settings.namespaces = lib.mapAttrsToList (name: _: {
inherit name;
metrics_override.prefix = "nginxlog";
namespace_label = "vhost";
settings.namespaces =
lib.mapAttrsToList (name: virtualHost: {
inherit name;
metrics_override.prefix = "nginxlog";
namespace_label = "vhost";
format = lib.concatStringsSep " " [
"$remote_addr - $remote_user [$time_local]"
''"$request" $status $body_bytes_sent''
''"$http_referer" "$http_user_agent"''
''rt=$request_time uct="$upstream_connect_time"''
''uht="$upstream_header_time" urt="$upstream_response_time"''
];
format = lib.concatStringsSep " " [
"$remote_addr - $remote_user [$time_local]"
''"$request" $status $body_bytes_sent''
''"$http_referer" "$http_user_agent"''
''rt=$request_time uct="$upstream_connect_time"''
''uht="$upstream_header_time" urt="$upstream_response_time"''
];
source.files = [ "/var/log/nginx/${name}/access.log" ];
}) config.services.nginx.virtualHosts;
source.files = [
"/var/log/nginx/${name}/access.log"
];
})
config.services.nginx.virtualHosts;
};
};
extraExporters = {
fail2ban = let
cfg = config.services.prometheus.extraExporters.fail2ban;
in {
port = 9191;
serviceOpts = {
after = ["fail2ban.service"];
requires = ["fail2ban.service"];
serviceConfig = {
Group = "fail2ban";
RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"];
ExecStart = lib.concatStringsSep " " [
"${pkgs.local.prometheus-fail2ban-exporter}/bin/fail2ban-prometheus-exporter"
"--collector.f2b.socket=/var/run/fail2ban/fail2ban.sock"
"--web.listen-address='${cfg.listenAddress}:${toString cfg.port}'"
"--collector.f2b.exit-on-socket-connection-error=true"
];
};
};
};
};
# TODO(tlater):
# - wireguard (?)
# - postgres (?)
# - blackbox (?) (curl to see if http and similar is up)
# - ssl_exporter (?)
};
services.dbus.implementation = "broker";
}

View file

@ -1,15 +1,10 @@
{ pkgs, config, ... }:
let
{config, ...}: let
domain = "metrics.${config.services.nginx.domain}";
in
{
in {
services.grafana = {
enable = true;
settings = {
server = {
http_port = 3001; # Default overlaps with gitea
root_url = "https://metrics.tlater.net";
};
server.http_port = 3001; # Default overlaps with gitea
security = {
admin_user = "tlater";
@ -28,11 +23,6 @@ in
};
};
declarativePlugins = [
pkgs.grafanaPlugins.victoriametrics-metrics-datasource
pkgs.grafanaPlugins.victoriametrics-logs-datasource
];
provision = {
enable = true;
@ -40,16 +30,7 @@ in
{
name = "Victoriametrics - tlater.net";
url = "http://localhost:8428";
type = "victoriametrics-metrics-datasource";
access = "proxy";
isDefault = true;
}
{
name = "Victorialogs - tlater.net";
url = "http://${config.services.victorialogs.bindAddress}";
type = "victoriametrics-logs-datasource";
access = "proxy";
type = "prometheus";
}
];
};
@ -59,12 +40,7 @@ in
forceSSL = true;
useACMEHost = "tlater.net";
enableHSTS = true;
locations = {
"/".proxyPass = "http://localhost:${toString config.services.grafana.settings.server.http_port}";
"/api/live" = {
proxyWebsockets = true;
proxyPass = "http://localhost:${toString config.services.grafana.settings.server.http_port}";
};
};
enableAuthorization = true;
locations."/".proxyPass = "http://localhost:${toString config.services.grafana.settings.server.http_port}";
};
}

View file

@ -3,234 +3,202 @@
config,
lib,
...
}:
let
}: let
inherit (lib) types mkOption mkDefault;
yaml = pkgs.formats.yaml { };
in
{
yaml = pkgs.formats.yaml {};
in {
options = {
services.prometheus = {
extraExporters = mkOption {
default = { };
type = types.attrsOf (
types.submodule {
options = {
port = mkOption {
type = types.int;
description = "The port on which this exporter listens.";
};
listenAddress = mkOption {
type = types.str;
default = "127.0.0.1";
description = "Address to listen on.";
};
serviceOpts = mkOption {
type = types.attrs;
description = "An attrset to be merged with the exporter's systemd service.";
};
type = types.attrsOf (types.submodule {
options = {
port = mkOption {
type = types.int;
description = "The port on which this exporter listens.";
};
}
);
listenAddress = mkOption {
type = types.str;
default = "127.0.0.1";
description = "Address to listen on.";
};
serviceOpts = mkOption {
type = types.attrs;
description = "An attrset to be merged with the exporter's systemd service.";
};
};
});
};
};
services.victoriametrics.scrapeConfigs = mkOption {
type = types.attrsOf (
types.submodule (
{ name, ... }:
{
options = {
job_name = mkOption {
type = types.str;
default = name;
type = types.attrsOf (types.submodule ({
name,
self,
...
}: {
options = {
job_name = mkOption {
type = types.str;
default = name;
};
extraSettings = mkOption {
type = types.anything;
description = ''
Other settings to set for this scrape config.
'';
default = {};
};
targets = mkOption {
type = types.listOf types.str;
description = lib.mdDoc ''
Addresses scrape targets for this config listen on.
Shortcut for `static_configs = lib.singleton {targets = [<targets>];}`
'';
default = [];
};
static_configs = mkOption {
default = [];
type = types.listOf (types.submodule {
options = {
targets = mkOption {
type = types.listOf types.str;
description = lib.mdDoc ''
The addresses scrape targets for this config listen on.
Must in `listenAddress:port` format.
'';
};
labels = mkOption {
type = types.attrsOf types.str;
description = lib.mdDoc ''
Labels to apply to all targets defined for this static config.
'';
default = {};
};
};
extraSettings = mkOption {
inherit (pkgs.formats.yaml { }) type;
description = ''
Other settings to set for this scrape config.
'';
default = { };
};
targets = mkOption {
type = types.listOf types.str;
description = lib.mdDoc ''
Addresses scrape targets for this config listen on.
Shortcut for `static_configs = lib.singleton {targets = [<targets>];}`
'';
default = [ ];
};
static_configs = mkOption {
default = [ ];
type = types.listOf (
types.submodule {
options = {
targets = mkOption {
type = types.listOf types.str;
description = lib.mdDoc ''
The addresses scrape targets for this config listen on.
Must in `listenAddress:port` format.
'';
};
labels = mkOption {
type = types.attrsOf types.str;
description = lib.mdDoc ''
Labels to apply to all targets defined for this static config.
'';
default = { };
};
};
}
);
};
};
}
)
);
});
};
};
}));
};
};
config = {
systemd.services = lib.mkMerge [
(lib.mapAttrs' (
name: exporter:
lib.nameValuePair "prometheus-${name}-exporter" (
lib.mkMerge [
{
# Shamelessly copied from upstream because the upstream
# module is an intractable mess
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
Restart = mkDefault "always";
PrivateTmp = mkDefault true;
WorkingDirectory = mkDefault /tmp;
DynamicUser = mkDefault true;
# Hardening
CapabilityBoundingSet = mkDefault [ "" ];
DeviceAllow = [ "" ];
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateDevices = mkDefault true;
ProtectClock = mkDefault true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectSystem = mkDefault "strict";
RemoveIPC = true;
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
UMask = "0077";
};
}
exporter.serviceOpts
]
)
) config.services.prometheus.extraExporters)
(lib.mapAttrs' (name: exporter:
lib.nameValuePair "prometheus-${name}-exporter" (lib.mkMerge [
{
# Shamelessly copied from upstream because the upstream
# module is an intractable mess
wantedBy = ["multi-user.target"];
after = ["network.target"];
serviceConfig.Restart = mkDefault "always";
serviceConfig.PrivateTmp = mkDefault true;
serviceConfig.WorkingDirectory = mkDefault /tmp;
serviceConfig.DynamicUser = mkDefault true;
# Hardening
serviceConfig.CapabilityBoundingSet = mkDefault [""];
serviceConfig.DeviceAllow = [""];
serviceConfig.LockPersonality = true;
serviceConfig.MemoryDenyWriteExecute = true;
serviceConfig.NoNewPrivileges = true;
serviceConfig.PrivateDevices = mkDefault true;
serviceConfig.ProtectClock = mkDefault true;
serviceConfig.ProtectControlGroups = true;
serviceConfig.ProtectHome = true;
serviceConfig.ProtectHostname = true;
serviceConfig.ProtectKernelLogs = true;
serviceConfig.ProtectKernelModules = true;
serviceConfig.ProtectKernelTunables = true;
serviceConfig.ProtectSystem = mkDefault "strict";
serviceConfig.RemoveIPC = true;
serviceConfig.RestrictAddressFamilies = ["AF_INET" "AF_INET6"];
serviceConfig.RestrictNamespaces = true;
serviceConfig.RestrictRealtime = true;
serviceConfig.RestrictSUIDSGID = true;
serviceConfig.SystemCallArchitectures = "native";
serviceConfig.UMask = "0077";
}
exporter.serviceOpts
]))
config.services.prometheus.extraExporters)
{
vmagent-scrape-exporters =
let
inherit (config.services.victoriametrics) listenAddress;
vmAddr = (lib.optionalString (lib.hasPrefix ":" listenAddress) "127.0.0.1") + listenAddress;
promscrape = yaml.generate "prometheus.yml" {
scrape_configs = lib.mapAttrsToList (
_: scrape:
lib.recursiveUpdate {
inherit (scrape) job_name;
static_configs =
scrape.static_configs ++ lib.optional (scrape.targets != [ ]) { inherit (scrape) targets; };
} scrape.extraSettings
) config.services.victoriametrics.scrapeConfigs;
};
in
{
enable = true;
path = [ pkgs.victoriametrics ];
wantedBy = [ "multi-user.target" ];
after = [
"network.target"
"victoriametrics.service"
];
serviceConfig = {
ExecStart = [
(lib.concatStringsSep " " [
"${pkgs.victoriametrics}/bin/vmagent"
"-promscrape.config=${promscrape}"
"-remoteWrite.url=http://${vmAddr}/api/v1/write"
"-remoteWrite.tmpDataPath=%t/vmagent"
])
];
SupplementaryGroups = "metrics";
DynamicUser = true;
RuntimeDirectory = "vmagent";
CapabilityBoundingSet = [ "" ];
DeviceAllow = [ "" ];
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateDevices = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectSystem = "strict";
RemoveIPC = true;
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
UMask = "0077";
};
vmagent-scrape-exporters = let
listenAddress = config.services.victoriametrics.listenAddress;
vmAddr = (lib.optionalString (lib.hasPrefix ":" listenAddress) "127.0.0.1") + listenAddress;
promscrape = yaml.generate "prometheus.yml" {
scrape_configs = lib.mapAttrsToList (_: scrape:
lib.recursiveUpdate {
inherit (scrape) job_name;
static_configs =
scrape.static_configs
++ lib.optional (scrape.targets != []) {targets = scrape.targets;};
}
scrape.extraSettings)
config.services.victoriametrics.scrapeConfigs;
};
in {
enable = true;
path = [pkgs.victoriametrics];
wantedBy = ["multi-user.target"];
after = ["network.target" "victoriametrics.service"];
serviceConfig = {
ExecStart = [
(lib.concatStringsSep " " [
"${pkgs.victoriametrics}/bin/vmagent"
"-promscrape.config=${promscrape}"
"-remoteWrite.url=http://${vmAddr}/api/v1/write"
"-remoteWrite.tmpDataPath=%t/vmagent"
])
];
SupplementaryGroups = "metrics";
DynamicUser = true;
RuntimeDirectory = "vmagent";
CapabilityBoundingSet = [""];
DeviceAllow = [""];
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateDevices = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectSystem = "strict";
RemoveIPC = true;
RestrictAddressFamilies = ["AF_INET" "AF_INET6"];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
UMask = "0077";
};
};
}
];
users.groups.metrics = { };
users.groups.metrics = {};
services.victoriametrics.scrapeConfigs =
let
allExporters = lib.mapAttrs (_: exporter: { inherit (exporter) listenAddress port; }) (
(lib.filterAttrs (
name: exporter:
# A bunch of deprecated exporters that need to be ignored
!(builtins.elem name [
"blackbox"
"minio"
"tor"
"unifi-poller"
])
&& builtins.isAttrs exporter
&& exporter.enable
) config.services.prometheus.exporters)
// config.services.prometheus.extraExporters
);
in
services.victoriametrics.scrapeConfigs = let
allExporters =
lib.mapAttrs (name: exporter: {
inherit (exporter) listenAddress port;
}) ((lib.filterAttrs (_: exporter: builtins.isAttrs exporter && exporter.enable)
config.services.prometheus.exporters)
// config.services.prometheus.extraExporters);
in
lib.mapAttrs (_: exporter: {
targets = [ "${exporter.listenAddress}:${toString exporter.port}" ];
}) allExporters;
targets = ["${exporter.listenAddress}:${toString exporter.port}"];
})
allExporters;
};
}

View file

@ -1,30 +0,0 @@
{ config, lib, ... }:
let
cfg = config.services.victorialogs;
in
{
options.services.victorialogs.bindAddress = lib.mkOption {
readOnly = true;
type = lib.types.str;
description = ''
Final address on which victorialogs listens.
'';
};
config = {
services.victorialogs = {
enable = true;
bindAddress =
(lib.optionalString (lib.hasPrefix ":" cfg.listenAddress) "127.0.0.1") + cfg.listenAddress;
};
services.journald.upload = {
enable = true;
settings.Upload = {
URL = "http://${cfg.bindAddress}/insert/journald";
NetworkTimeoutSec = "20s";
};
};
systemd.services."systemd-journal-upload".after = [ "victorialogs.service" ];
};
}

View file

@ -1,99 +1,16 @@
{ config, lib, ... }:
let
blackbox_host = config.services.prometheus.exporters.blackbox.listenAddress;
blackbox_port = config.services.prometheus.exporters.blackbox.port;
in
{
{config, ...}: {
config.services.victoriametrics = {
enable = true;
extraOptions = [ "-storage.minFreeDiskSpaceBytes=5GB" ];
extraOptions = [
"-storage.minFreeDiskSpaceBytes=5GB"
];
scrapeConfigs = {
forgejo = {
targets = [ "127.0.0.1:${toString config.services.forgejo.settings.server.HTTP_PORT}" ];
targets = ["127.0.0.1:${toString config.services.forgejo.settings.server.HTTP_PORT}"];
extraSettings.authorization.credentials_file = config.sops.secrets."forgejo/metrics-token".path;
};
blackbox = {
static_configs = lib.singleton {
targets = lib.mapAttrsToList (vHost: _: "https://${vHost}") config.services.nginx.virtualHosts;
};
extraSettings = {
metrics_path = "/probe";
params.module = [ "http_2xx" ];
relabel_configs = [
{
source_labels = [ "__address__" ];
target_label = "__param_target";
}
{
source_labels = [ "__param_target" ];
target_label = "instance";
}
{
target_label = "__address__";
replacement = "${blackbox_host}:${toString blackbox_port}";
}
];
};
};
blackbox_turn = {
targets = [ "turn.tlater.net:${toString config.services.coturn.tls-listening-port}" ];
extraSettings = {
metrics_path = "/probe";
params.module = [ "turn_server" ];
relabel_configs = [
{
source_labels = [ "__address__" ];
target_label = "__param_target";
}
{
source_labels = [ "__param_target" ];
target_label = "instance";
}
{
target_label = "__address__";
replacement = "${blackbox_host}:${toString blackbox_port}";
}
];
};
};
blackbox_exporter.targets = [ "${blackbox_host}:${toString blackbox_port}" ];
coturn.targets = [ "127.0.0.1:9641" ];
crowdsec.targets =
let
address = config.security.crowdsec.settings.prometheus.listen_addr;
port = config.security.crowdsec.settings.prometheus.listen_port;
in
[ "${address}:${toString port}" ];
csFirewallBouncer.targets =
let
address =
config.security.crowdsec.remediationComponents.firewallBouncer.settings.prometheus.listen_addr;
port =
config.security.crowdsec.remediationComponents.firewallBouncer.settings.prometheus.listen_port;
in
[ "${address}:${toString port}" ];
immich.targets = [
"127.0.0.1:8081"
"127.0.0.1:8082"
];
# Configured in the hookshot listeners, but it's hard to filter
# the correct values out of that config.
matrixHookshot.targets = [ "127.0.0.1:9001" ];
victorialogs.targets = [ config.services.victorialogs.bindAddress ];
coturn.targets = ["127.0.0.1:9641"];
};
};
}

View file

@ -1,96 +0,0 @@
{
pkgs,
lib,
config,
...
}:
let
java = pkgs.jdk21_headless;
in
{
services.minecraft-server = {
enable = true;
eula = true;
# jvmOpts are set using a file for forge
# jvmOpts = "-Xmx8G -Xms8G";
openFirewall = true;
declarative = true;
whitelist = {
tlater = "140d177a-966f-41b8-a4c0-e305babd291b";
romino25 = "59cd1648-14a4-4bcf-8f5a-2e1bde678f2c";
lasi25 = "0ab6e3d1-544a-47e7-8538-2e6c248e49a4";
};
serverProperties = {
allow-flight = true;
difficulty = "hard";
motd = "tlater.net";
spawn-protection = 1;
white-list = true;
enable-query = true;
enable-status = true;
# Allows the server to write chunks without hogging the main
# thread...
sync-chunk-writes = false;
# Disables chat reporting, because we don't need any of that
# drama on a lil' friends-only server.
enforce-secure-profile = false;
};
package = pkgs.writeShellApplication {
name = "minecraft-server";
runtimeInputs = [ java ];
text = ''
exec /var/lib/minecraft/run.sh $@
'';
};
};
systemd.services.minecraft-server = {
path = [ java ];
# Since we read from our own HTTP server, we need to wait for it
# to be up
after = [ "nginx.service" ];
# Don't auto-start the server
wantedBy = lib.mkForce [ ];
serviceConfig = {
# Use packwiz to install mods
ExecStartPre = [
"${java}/bin/java -jar ${config.services.minecraft-server.dataDir}/packwiz-installer-bootstrap.jar -g -s server 'https://minecraft.${config.services.nginx.domain}/cobblemon-pack/pack.toml'"
];
# Forge requires some bonus JVM options, which they include in a
# little `run.sh` script
ExecStart = lib.mkForce "${config.services.minecraft-server.dataDir}/run.sh --nogui";
};
};
systemd.tmpfiles.settings."10-minecraft" = {
"/srv/minecraft".d = {
user = "nginx";
group = "minecraft";
mode = "0775";
};
"/srv/minecraft/cobblemon-pack"."L+" = {
user = "nginx";
group = "minecraft";
mode = "0775";
argument = "${../../pkgs/minecraftmon}";
};
};
services.nginx.virtualHosts."minecraft.${config.services.nginx.domain}" = {
forceSSL = true;
useACMEHost = "tlater.net";
enableHSTS = true;
root = "/srv/minecraft";
};
}

View file

@ -1,103 +1,105 @@
{
pkgs,
config,
lib,
...
}:
let
nextcloud = pkgs.nextcloud31;
}: let
# Update pending on rewrite of nextcloud news, though there is an
# alpha to switch to if it becomes necessary:
# https://github.com/nextcloud/news/issues/2610
nextcloud = pkgs.nextcloud27;
hostName = "nextcloud.${config.services.nginx.domain}";
in
{
services = {
nextcloud = {
inherit hostName;
in {
services.nextcloud = {
inherit hostName;
package = nextcloud;
phpPackage = lib.mkForce (
pkgs.php.override {
packageOverrides = _: prev: {
extensions = prev.extensions // {
pgsql = prev.extensions.pgsql.overrideAttrs (_: {
configureFlags = [ "--with-pgsql=${lib.getDev config.services.postgresql.package.pg_config}" ];
});
pdo_pgsql = prev.extensions.pdo_pgsql.overrideAttrs (_: {
configureFlags = [ "--with-pdo-pgsql=${lib.getDev config.services.postgresql.package.pg_config}" ];
});
};
};
}
);
enable = true;
maxUploadSize = "2G";
https = true;
package = nextcloud;
enable = true;
maxUploadSize = "2G";
https = true;
configureRedis = true;
configureRedis = true;
config = {
dbtype = "pgsql";
dbhost = "/run/postgresql";
config = {
dbtype = "pgsql";
dbhost = "/run/postgresql";
adminuser = "tlater";
adminpassFile = config.sops.secrets."nextcloud/tlater".path;
};
settings = {
default_phone_region = "AT";
overwriteprotocol = "https";
};
phpOptions = {
"opcache.interned_strings_buffer" = "16";
};
extraApps = {
inherit (config.services.nextcloud.package.packages.apps)
calendar
contacts
cookbook
news
;
};
adminuser = "tlater";
adminpassFile = config.sops.secrets."nextcloud/tlater".path;
};
# Set up SSL
nginx.virtualHosts."${hostName}" = {
forceSSL = true;
useACMEHost = "tlater.net";
# The upstream module already adds HSTS
settings = {
default_phone_region = "AT";
overwriteprotocol = "https";
};
backups.nextcloud = {
user = "nextcloud";
paths = [
"/var/lib/nextcloud/nextcloud-db.sql"
"/var/lib/nextcloud/data/"
"/var/lib/nextcloud/config/config.php"
];
preparation = {
packages = [
config.services.postgresql.package
config.services.nextcloud.occ
];
text = ''
nextcloud-occ maintenance:mode --on
pg_dump ${config.services.nextcloud.config.dbname} --file=/var/lib/nextcloud/nextcloud-db.sql
'';
};
cleanup = {
packages = [
pkgs.coreutils
config.services.nextcloud.occ
];
text = ''
rm /var/lib/nextcloud/nextcloud-db.sql
nextcloud-occ maintenance:mode --off
'';
};
phpOptions = {
"opcache.interned_strings_buffer" = "16";
};
extraApps = {
inherit (pkgs.local) bookmarks calendar contacts cookbook news notes;
};
};
# Ensure that this service doesn't start before postgres is ready
systemd.services.nextcloud-setup.after = [ "postgresql.service" ];
systemd.services.nextcloud-setup.after = ["postgresql.service"];
# Set up SSL
services.nginx.virtualHosts."${hostName}" = {
forceSSL = true;
useACMEHost = "tlater.net";
# The upstream module already adds HSTS
};
# Block repeated failed login attempts
environment.etc = {
"fail2ban/filter.d/nextcloud.conf".text = ''
[Definition]
_groupsre = (?:(?:,?\s*"\w+":(?:"[^"]+"|\w+))*)
failregex = \{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Login failed:
\{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Trusted domain error.
datepattern = ,?\s*"time"\s*:\s*"%%Y-%%m-%%d[T ]%%H:%%M:%%S(%%z)?"
journalmatch = SYSLOG_IDENTIFIER=Nextcloud
'';
};
services.fail2ban.jails = {
nextcloud = ''
enabled = true
# Nextcloud does some throttling already, so we need to set
# these to something bigger.
findtime = 43200
bantime = 86400
'';
};
services.backups.nextcloud = {
user = "nextcloud";
paths = [
"/var/lib/nextcloud/nextcloud-db.sql"
"/var/lib/nextcloud/data/"
"/var/lib/nextcloud/config/config.php"
];
preparation = {
packages = [
config.services.postgresql.package
config.services.nextcloud.occ
];
text = ''
nextcloud-occ maintenance:mode --on
pg_dump ${config.services.nextcloud.config.dbname} --file=/var/lib/nextcloud/nextcloud-db.sql
'';
};
cleanup = {
packages = [
pkgs.coreutils
config.services.nextcloud.occ
];
text = ''
rm /var/lib/nextcloud/nextcloud-db.sql
nextcloud-occ maintenance:mode --off
'';
};
};
}

View file

@ -1,5 +1,8 @@
{ pkgs, ... }:
{
config,
pkgs,
...
}: {
services.postgresql = {
package = pkgs.postgresql_14;
enable = true;
@ -25,11 +28,16 @@
name = "nextcloud";
ensureDBOwnership = true;
}
{
name = config.services.authelia.instances.main.user;
ensureDBOwnership = true;
}
];
ensureDatabases = [
"grafana"
"nextcloud"
config.services.authelia.instances.main.user
];
};
}

View file

@ -1,14 +1,16 @@
{ pkgs, lib, ... }:
let
inherit (lib) concatStringsSep;
in
{
pkgs,
lib,
...
}: let
inherit (lib) concatStringsSep;
in {
# Sadly, steam-run requires some X libs
environment.noXlibs = false;
systemd.services.starbound = {
description = "Starbound";
after = [ "network.target" ];
after = ["network.target"];
serviceConfig = {
ExecStart = "${pkgs.local.starbound}/bin/launch-starbound ${./configs/starbound.json}";
@ -65,7 +67,7 @@ in
# Game servers shouldn't use cgroups themselves either
ProtectControlGroups = true;
# Most game servers will never need other socket types
RestrictAddressFamilies = [ "AF_UNIX AF_INET AF_INET6" ];
RestrictAddressFamilies = ["AF_UNIX AF_INET AF_INET6"];
# Also a no-brainer, no game server should ever need this
LockPersonality = true;
# Some game servers will probably try to set this, but they
@ -111,7 +113,9 @@ in
services.backups.starbound = {
user = "root";
paths = [ "/var/lib/private/starbound/storage/universe/" ];
pauseServices = [ "starbound.service" ];
paths = [
"/var/lib/private/starbound/storage/universe/"
];
pauseServices = ["starbound.service"];
};
}

View file

@ -1,8 +1,6 @@
{ config, ... }:
let
inherit (config.services.nginx) domain;
in
{
{config, ...}: let
domain = config.services.nginx.domain;
in {
services.tlaternet-webserver = {
enable = true;
listen = {
@ -12,17 +10,15 @@ in
};
# Set up SSL
services.nginx.virtualHosts."${domain}" =
let
inherit (config.services.tlaternet-webserver.listen) addr port;
in
{
serverAliases = [ "www.${domain}" ];
services.nginx.virtualHosts."${domain}" = let
inherit (config.services.tlaternet-webserver.listen) addr port;
in {
serverAliases = ["www.${domain}"];
forceSSL = true;
useACMEHost = "tlater.net";
enableHSTS = true;
forceSSL = true;
useACMEHost = "tlater.net";
enableHSTS = true;
locations."/".proxyPass = "http://${addr}:${toString port}";
};
locations."/".proxyPass = "http://${addr}:${toString port}";
};
}

View file

@ -1,5 +1,4 @@
{ config, ... }:
{
{config, ...}: {
# iptables needs to permit forwarding from wg0 to wg0
networking.firewall.extraCommands = ''
iptables -A FORWARD -i wg0 -o wg0 -j ACCEPT
@ -24,10 +23,20 @@
};
wireguardPeers = [
# yui
{
AllowedIPs = [ "10.45.249.2/32" ];
PublicKey = "WbNuxp7tTWTVve/nyiwC1stfaJS0wORvBxiK9IFTpio=";
# yui
wireguardPeerConfig = {
AllowedIPs = ["10.45.249.2/32"];
PublicKey = "5mlnqEVJWks5OqgeFA2bLIrvST9TlCE81Btl+j4myz0=";
};
}
{
# yuanyuan
wireguardPeerConfig = {
AllowedIPs = ["10.45.249.10/32"];
PublicKey = "0UsFE2atz/O5P3OKQ8UHyyyGQNJbp1MeIWUJLuoerwE=";
};
}
];
};
@ -38,23 +47,23 @@
matchConfig.Name = "wg0";
networkConfig = {
Description = "VLAN";
Address = [
"10.45.249.1/32"
# TODO(tlater): Add IPv6 whenever that becomes relevant
];
IPv4Forwarding = "yes";
IPForward = "yes";
IPv4ProxyARP = "yes";
};
routes = [
{
Source = "10.45.249.0/24";
Destination = "10.45.249.0/24";
Gateway = "10.45.249.1";
GatewayOnLink = "no";
routeConfig = {
Source = "10.45.249.0/24";
Destination = "10.45.249.0/24";
Gateway = "10.45.249.1";
GatewayOnLink = "no";
};
}
];

View file

@ -3,9 +3,15 @@
defaultSopsFile = ../keys/production.yaml;
secrets = {
"battery-manager/email" = { };
"battery-manager/email" = {
owner = "battery-manager";
group = "battery-manager";
};
"battery-manager/password" = { };
"battery-manager/password" = {
owner = "battery-manager";
group = "battery-manager";
};
# Gitea
"forgejo/metrics-token" = {
@ -25,12 +31,12 @@
};
# Heisenbridge
"heisenbridge/as-token" = { };
"heisenbridge/hs-token" = { };
"heisenbridge/as-token" = {};
"heisenbridge/hs-token" = {};
# Matrix-hookshot
"matrix-hookshot/as-token" = { };
"matrix-hookshot/hs-token" = { };
"hetzner-api" = {
owner = "acme";
};
# Nextcloud
"nextcloud/tlater" = {
@ -38,14 +44,6 @@
group = "nextcloud";
};
# Porkbub/ACME
"porkbun/api-key" = {
owner = "acme";
};
"porkbun/secret-api-key" = {
owner = "acme";
};
# Restic
"restic/local-backups" = {
owner = "root";
@ -64,10 +62,10 @@
};
# Steam
"steam/tlater" = { };
"steam/tlater" = {};
# Turn
"turn/env" = { };
"turn/env" = {};
"turn/secret" = {
owner = "turnserver";
};

976
flake.lock generated

File diff suppressed because it is too large Load diff

215
flake.nix
View file

@ -2,7 +2,8 @@
description = "tlater.net host configuration";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05-small";
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05";
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
disko = {
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
@ -12,6 +13,10 @@
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
nvfetcher = {
url = "github:berberman/nvfetcher";
inputs.nixpkgs.follows = "nixpkgs";
};
tlaternet-webserver = {
url = "git+https://gitea.tlater.net/tlaternet/tlaternet.git";
inputs.nixpkgs.follows = "nixpkgs";
@ -22,119 +27,131 @@
};
sonnenshift = {
url = "git+ssh://git@github.com/sonnenshift/battery-manager";
url = "git+ssh://git@github.com/sonnenshift/battery-manager?ref=tlater/implement-nix-module";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
{
self,
nixpkgs,
sops-nix,
deploy-rs,
...
}@inputs:
let
system = "x86_64-linux";
vm = nixpkgs.lib.nixosSystem {
outputs = {
self,
nixpkgs,
sops-nix,
nvfetcher,
deploy-rs,
...
} @ inputs: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in {
##################
# Configurations #
##################
nixosConfigurations = {
# The actual system definition
hetzner-1 = nixpkgs.lib.nixosSystem {
inherit system;
specialArgs.flake-inputs = inputs;
modules = [
./configuration
./configuration/hardware-specific/vm.nix
./configuration/hardware-specific/hetzner
];
};
in
{
##################
# Configurations #
##################
nixosConfigurations = {
# The actual system definition
hetzner-1 = nixpkgs.lib.nixosSystem {
inherit system;
specialArgs.flake-inputs = inputs;
};
modules = [
./configuration
./configuration/hardware-specific/hetzner
];
};
};
############################
# Deployment configuration #
############################
deploy.nodes = {
hetzner-1 = {
hostname = "116.202.158.55";
############################
# Deployment configuration #
############################
deploy.nodes = {
hetzner-1 = {
hostname = "116.202.158.55";
profiles.system = {
user = "root";
path = deploy-rs.lib.${system}.activate.nixos self.nixosConfigurations.hetzner-1;
};
sshUser = "tlater";
sshOpts = [
"-p"
"2222"
"-o"
"ForwardAgent=yes"
];
};
};
#########
# Tests #
#########
checks.${system} = import ./checks (inputs // { inherit system; });
###########################
# Garbage collection root #
###########################
packages.${system} = {
default = vm.config.system.build.vm;
}
// import ./pkgs { pkgs = nixpkgs.legacyPackages.${system}; };
###################
# Utility scripts #
###################
apps.${system} = {
default = self.apps.${system}.run-vm;
run-vm = {
type = "app";
program =
(nixpkgs.legacyPackages.${system}.writeShellScript "" ''
${vm.config.system.build.vm.outPath}/bin/run-testvm-vm
'').outPath;
};
};
###########################
# Development environment #
###########################
devShells.${system} = {
default = nixpkgs.legacyPackages.${system}.mkShell {
sopsPGPKeyDirs = [
"./keys/hosts/"
"./keys/users/"
];
packages = nixpkgs.lib.attrValues {
inherit (sops-nix.packages.${system}) sops-import-keys-hook sops-init-gpg-key;
inherit (deploy-rs.packages.${system}) default;
};
profiles.system = {
user = "root";
path = deploy-rs.lib.${system}.activate.nixos self.nixosConfigurations.hetzner-1;
};
minecraft = nixpkgs.legacyPackages.${system}.mkShell {
packages = nixpkgs.lib.attrValues { inherit (nixpkgs.legacyPackages.${system}) packwiz; };
};
sshUser = "tlater";
sshOpts = ["-p" "2222" "-o" "ForwardAgent=yes"];
};
};
#########
# Tests #
#########
checks = builtins.mapAttrs (system: deployLib: deployLib.deployChecks self.deploy) deploy-rs.lib;
###################
# Utility scripts #
###################
apps.${system} = {
default = self.apps.${system}.run-vm;
run-vm = {
type = "app";
program = let
vm = nixpkgs.lib.nixosSystem {
inherit system;
specialArgs.flake-inputs = inputs;
modules = [
./configuration
./configuration/hardware-specific/vm.nix
];
};
in
(pkgs.writeShellScript "" ''
${vm.config.system.build.vm.outPath}/bin/run-testvm-vm
'')
.outPath;
};
update-pkgs = {
type = "app";
program = let
nvfetcher-bin = "${nvfetcher.packages.${system}.default}/bin/nvfetcher";
in
(pkgs.writeShellScript "update-pkgs" ''
cd "$(git rev-parse --show-toplevel)/pkgs"
${nvfetcher-bin} -o _sources_pkgs -c nvfetcher.toml
'')
.outPath;
};
update-nextcloud-apps = {
type = "app";
program = let
nvfetcher-bin = "${nvfetcher.packages.${system}.default}/bin/nvfetcher";
in
(pkgs.writeShellScript "update-nextcloud-apps" ''
cd "$(git rev-parse --show-toplevel)/pkgs"
${nvfetcher-bin} -o _sources_nextcloud -c nextcloud-apps.toml
'')
.outPath;
};
};
###########################
# Development environment #
###########################
devShells.${system}.default = nixpkgs.legacyPackages.${system}.mkShell {
sopsPGPKeyDirs = ["./keys/hosts/" "./keys/users/"];
nativeBuildInputs = [
sops-nix.packages.${system}.sops-import-keys-hook
];
packages = with pkgs; [
sops-nix.packages.${system}.sops-init-gpg-key
deploy-rs.packages.${system}.default
cargo
clippy
rustc
rustfmt
rust-analyzer
pkg-config
openssl
];
};
};
}

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,7 @@
porkbun:
api-key: ENC[AES256_GCM,data:A5J1sqwq6hs=,iv:77Mar3IX7mq7z7x6s9sSeGNVYc1Wv78HptJElEC7z3Q=,tag:eM/EF9TxKu+zcbJ1SYXiuA==,type:str]
secret-api-key: ENC[AES256_GCM,data:8Xv+jWYaWMI=,iv:li4tdY0pch5lksftMmfMVS729caAwfaacoztaQ49az0=,tag:KhfElBGzVH4ByFPfuQsdhw==,type:str]
authelia:
storageEncryptionKey: ENC[AES256_GCM,data:8X6Zvq7ct1MkMH+lc5kTTHXOo6rGyhSzc3aWxGRA5tnBet+TGcENo0RYTYmactsPGVpTIUGGplaG7B7dqRPhkdDHhbCCZCm2nLaYjpVJ241DrpUNKHn8lvg/bMxUQ/Dvw76ByYuWN6bREr3XRaBztBSPzld8zTSYx71I0CKY7vk=,iv:cJSwfuVWO39qqKCGt2Mvw7pN8+hD6kRH9v4c/u4hLuk=,tag:YhdlXuX2ETxjb443RI8MsA==,type:str]
sessionSecret: ENC[AES256_GCM,data:dnoWmc4HND62w3jMXL+akncAEb61c/I70DgRytx55Wxcn4rMiswp6zCkRdsP4CkouTQ1lyAcQrubp5I8M9Kyow/KBMYz9dPkr4+2xJ9w0SEmAVhyPe2DFvYos3x0Uvx5S0B3o1mXoXqbg78e4w5yEIbALiJT8VPGrWK8Cl4nVPo=,iv:FHDXUW2DWUmEZzWUYkYduogdVOtvMlRH4/fVg05cZaI=,tag:u282WQnHpBsZGYJH7mFFKA==,type:str]
jwtSecret: ENC[AES256_GCM,data:0M3AyoMp+orrljl5NsxmthzrHMmu0REcz7+9fpFKbwwqV6KqlpgGddjYZIsTpHEWEq9zhZ2YWLJkMxKdDgROVHUFZGKut28JPSAjjY+1V0wxNBnfSCnxEv5BUw2+cCxcpCwYQyNfRK6SotTt8aqpxvda4oRXpzxV6SW7ogDjc6E=,iv:D57SynZkW2JuFyX6bpZYkxpR2KtkOmKaySg1Bxim0r8=,tag:JCPGZaumdHrtgcH16A7b+g==,type:str]
battery-manager:
email: ENC[AES256_GCM,data:LM/EGzWHfVQ=,iv:jFaoUQuUfuGoOyj/GFpdI8TerH/c8D9fjvio+IEt2Tc=,tag:IWLiN011JEnHRLIXWQgfmA==,type:str]
password: ENC[AES256_GCM,data:SUxjqS7SJHM=,iv:LvdKk88S+nSImh6/ZezbFGLCUBu1Lpdu+neF2xyHdBg=,tag:rcMyZuW4FVNbcbz00wQKBg==,type:str]
@ -16,9 +17,6 @@ steam:
heisenbridge:
as-token: ENC[AES256_GCM,data:tXbOeo7nv8I=,iv:wJAKcOXX9nGIw4n38ThOoj29u7dUWhsxSQG/p79JlEw=,tag:rTVaGS2UuWcea1uBa8YX2g==,type:str]
hs-token: ENC[AES256_GCM,data:VBwvwomv0Xg=,iv:q6INtJ+rg+QiXj8uBdBzQYQZUBBXp+9odxDHwvu8Jxc=,tag:XKhm8nxygAkKaiVPJ2Fcdg==,type:str]
matrix-hookshot:
as-token: ENC[AES256_GCM,data:uSUOo4f2KqA=,iv:Xb9G8Ecv6m59m51kDw2bOfq3SMJt4g9/6/EdH74R+KM=,tag:K9MSfO2c2Y4rlf0eYrmTnw==,type:str]
hs-token: ENC[AES256_GCM,data:0KsyA06InL4=,iv:zAR0Y1fk8SyodcSLBHlQ8I+BAmttz9Hkd8Q3OREFqs4=,tag:t1Et8N/3seq95DeGoUd7Sw==,type:str]
wireguard:
server-key: ENC[AES256_GCM,data:FvY897XdKoa/mckE8JQLCkklsnYD6Wz1wpsu5t3uhEnW3iarnDQxF9msuYU=,iv:jqGXfekM+Vs+J9b5nlZ5Skd1ZKHajoUo2Dc4tMYPm1w=,tag:EehikjI/FCU8wqtpvJRamQ==,type:str]
restic:
@ -32,39 +30,48 @@ turn:
#ENC[AES256_GCM,data:bxhKzU5Tzezl749CDu8e8kxa7ahGuZFaPa9K3kxuD+4sg5Hi3apgDlC0n8oK0DeiK4Ks7+9Cyw==,iv:T/zVJUpNAv1rR0a9+6SDTG08ws2A1hFBs5Ia3TpT0uk=,tag:uGXb1VryM+lIJ8r0I5durA==,type:comment]
ssl-cert: ENC[AES256_GCM,data:xHUr14CjKslgbGh/n5jYSOuCw9JRxS6YXE4fxS+aJzFcNeSeGNqoipPeuJupZGBnQP/FCqohiHY=,iv:/OEsVqRshGL9NIvntMC42EPZSNL0u6EfhtUBqgV7qog=,tag:4pxtNjuvy/ibm6nDtKdSkw==,type:str]
sops:
lastmodified: "2025-02-07T17:43:24Z"
mac: ENC[AES256_GCM,data:akmD/bfgeTyFzW1quvM16cdj0fC6+CbJ8WyX9173H11yKGxvE1USQYcErpl1SHOx9Jk8LVb7f+MsUm2fjQF1MEq6xaWI74jem12lZ9CGXFaTL7e87JvfbK7pV+aKpxSBBNFyJgbYm30ibdUwxwKmNVfPb1e0HT9qwenvoV7RobM=,iv:mKqOW0ULXL711uczUbRf9NPo6uPTQoS/IbR46S+JID4=,tag:vE6NYzYLbQHDImov1XGTcg==,type:str]
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age: []
lastmodified: "2024-04-12T01:00:31Z"
mac: ENC[AES256_GCM,data:fVnMwfvGi7vtP1Fg4NLrhGvLF2PcIgZPOcwk4Ssm4iw5iSj0K1npOX3pd5BWzyszqchfYYRHY99GllAump0bZmprVAld9rf70B2HZIVvowBPuUXfc9Cz/5q0z+s8bQ5vCdElW1Bh7h8W/POePdc8cFGAyBS4i1ZVNheIDOHdDjI=,iv:Bi6rekXOx3/dwwPRryF3CoAoQi3D06ABysRF1oBeG5A=,tag:0TCra+AkhBDczj4uvAzKMw==,type:str]
pgp:
- created_at: "2025-10-03T21:38:26Z"
enc: |-
- created_at: "2023-12-29T15:25:27Z"
enc: |
-----BEGIN PGP MESSAGE-----
hF4DjPSW5uJlxqMSAQdAf6HxNOE9bH2dQOF2E5HnAJT6b8cZwEJc0TgNoVpeB08w
bqksAeuTLWp2MI05J2L9lGSgjpNRERacNmwacx3ZYEIq0iuUhiL41bJXcJjgH4j1
0lYBGmTQ3NBGyFucFEvmuZW2X8ZlgTldszehheiuWIzvlUmgFQHue2TlPAaNY8of
ekOj13blDxtmz4bnxvcGjZlWOOBDeYz2ams7UI+FnG+zc5o4GqRywA==
=5y2E
hQEMA7x7stsXx45CAQf/RWxP6z7xjV5TqiA6lFhtygjrH9x3y1DUWG9aUb/dO+xH
zDbGMYqGe9RPlgi5sWPstdKXvCgs+AKNj93qJYMwEtaasJOinYXCGeAQmzg90+pt
bS6SoBHhGIxAvvLKKPtYx0V50I2reYR+32ux9bcrnzwIsV0P7/SSp1Cl8H+sotB8
yf+0ULXcpC+SYECmZqzR9qQ3S+3I6/+QS+QgWj4NsyF+apxnE9oQDcBLdYP4aKgR
JHERA9HYfDTKoS137pFHxgINqHkFRY6lhoZdz1yDzOjiPxd8YVfPdKyf022Rg+cX
J/Q2P+OhNZEG3gapNATp6wH3niovA89KwZKSmbTZOdJeAZ6NV6TiUP+TgGg5+CmV
pSLaGel2NZRnFVNdDFi0dsOwhHv3FpKhIpALJh08/jsmAAslfE7vVlcEnaoUJPTS
3v86AACUC5D/gUxmFrrED1qoxbELCmZ17xTwjQzxwg==
=KzdF
-----END PGP MESSAGE-----
fp: 8322CC1D351D8EFE63F3D27A8CF496E6E265C6A3!
- created_at: "2025-10-03T21:38:26Z"
enc: |-
fp: 535B61015823443941C744DD12264F6BBDFABA89
- created_at: "2023-12-29T15:25:27Z"
enc: |
-----BEGIN PGP MESSAGE-----
hQIMA/3lh+ZzfS28ARAAiuuzmYY/KBYSKjRWBnpbG5myhJlddDP1lplkd3S8vk9G
S5tTJRqfSEOUPPMiUW7v0KP9+DkSfE1RnF0JyUNKo8GmMerw3fo6hLsDPa35rse8
QgAmO1o0h2Yd8oUe+c3BYtTTnF2hyMhxv3SxSdxcBN//rggNuIAo+K5M67RKDidV
5q2en9rclWYezI/OT6UPtoDwdff8LeHbF5skkfOpQkACStGCIioQB7fnDaLc5DwG
ExaWWNTHolLjMMc6cuv7LGQNUS0h83Li5X8FM5op7BJdxH6p8UxsmGlzKhhuR5ON
kyljM3ICaS1QJc1OuD/RruYTitbVGCQfZT8/zfgcTJA6PGMNsbDG6oxXoLWjgAwO
ImQ7MmidcN52npzumQDm7Tv4yyQKyv4tgOSQSLL87++uLUZE0KLr490f7n/wOEAE
augOkNiid5icvPOQ8sD8kUTX27vhGPG2nuRfzVWt3EObwak1fde5GmWZSR2famK5
nvKhLthsp5a+Iz1f3mK4zdfxJSneOrRdTrCP35CVIW/BY9F+Hs6PaBeVVh37/6HE
DuDYiWlPpeADt2JV4M0Ri1W4QK6wIqZi9AFH3uesoavR5ONj/Eb6K6chYy2ZGepN
qJgMLuoBa+ZlyC1ju96Qt1TKmtzDKnPcygNQi2OyS0mEYMJrcNrcVTWRffh3VLfS
VgGjmzLSDkyVFthF0D2SQvUp0DzXEr+dZw3NhFgMy+icSOZi6+FHkfCWjJcNiJ10
7bbburzdjQC50fJfg8pNEpbI6WCfXrfj2Gj8zMLIpXOfvnVJ/nxa
=mYN1
hQIMA/3lh+ZzfS28ARAAm729dMouF7juUeHAb+aHMoyZVKsXapxnxebkjE/LSIbz
IEZwegTNrtxQJLclV4Km2gUaBTcE4vLJCpB7YxZvk7JV9OdVKi97o9PcXUXbz9ej
/WomnEvFyyxTZGTiHU+L4kNudl8UAKhTt3P4fR3PLpTily75Kn53tzLFJuCO8fAY
I/YwQAzayxhPcxk3FuPsD/ONiG7mW8n2ZwfwgOkKXwnrlJv7DreKJRYzu/EeuvX/
d4oz+k+xofniOeZmQjZllzR7/++MBg/e1U9VocN1EAWpWHP5taLiThfnVSGDhlQM
+4WT5ezH6EuUQlAyQNpDaCincBvCHInhrNlUPOpW51nHMb0y3n4x2hMtZA0JbYEu
mkWTYDe65cHjImHXQk9oO2/v4oIyq7ywHX7g2hqVbbiLHZqqTaGfV8lP30+r6/UQ
29iAdWac1hY5HDzwbqpY6b38i60j4bkiS83xqrGYBy037bCFk1oHJqwxp5P7vrzr
rTv5NBr95BlwF+s8xPEPZneaEu7N3UnhhSzDWp1jgsCxN9b/XHarchNt70xEt2VS
xpgs9GEXhsJcbrFNPYqTkFb8vjLFI+poGPTfadW17j4Pp5ftIBRNdKvDG0ni/AIp
K98R/nvaHEFuX31SkL8ZUIRqhJm3JVqilFxLAJrqGuSN3jA6wKrimUYpK+t+64jS
WAEN9jHYFQDTVHix3g15S5YTGh5ROyqxouDhvSDFTmGtbm5W/HYgnkZmh53TgVeJ
Rph/O9QptculzTN+nEqshBhbjhl/uDsLsjLYo/O1AyCwTUSd3OKn6uU=
=zThh
-----END PGP MESSAGE-----
fp: 2f5caa73e7ceea4fcc8d2881fde587e6737d2dbc
unencrypted_suffix: _unencrypted
version: 3.9.2
version: 3.8.1

View file

@ -1 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMMst2rs9WuvWnRTOuQElDMx0/cf4n9x9lC1+8clT0LZ openpgp:0xDD46BD5E
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC1QMrJrjjWOjQf9wF5JC52EHtDh03grRHdS0PHlnc9kLDXYw8n41KEN2vE2skVTKBVufQhuckCAlXd6BpisW1YoyhaLs9Bcp6Un63VH3YvRvXzKtwXJHEv/5SnDrGqxo5UF6xTVTxC9ux0dnSSizAeQiremr9Zuzz4B25NJCiZ2lVZUOi8iG1m4082owuwZWIW6qrFWi3gUGtjz32EM3eUX75BE1ZcDap7BJj2GdYsMHO2iiHBZwQVOXd7Fvk/23f9IeQ95D2RMgeQun8OIF+7X5zaWzyzq1Tky4pijaSsUebnFk9Cxm6KRg+yXgmIjpskskCY6kj82+wqFjPRIgxf cardno:000606358963

View file

@ -1,44 +1,72 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mDMEaN+Y2hYJKwYBBAHaRw8BAQdAxpAAfVB+wwIcxEqLhZXGOLQcGzN8tIvnL4RM
ovzDyOmI0QQfFgoAgwWCaN+Y2gWJBaSPvQMLCQcJEJFYJf4CDDsERxQAAAAAAB4A
IHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZ8pYda8oUsLzLqj1avZv2DOc
sT3a3jJNKz4NsRpp2/xUAxUKCAKbAQIeCRYhBBypFiLIAlMIN83xvZFYJf4CDDsE
AADVSwD+P3Jy6qBwbgPwgZzf5+cikSnMLwMuEUdI+jY6ICFrB1UA/1uwLVdjveRY
OwqhrPbuc00DFlAUDTXVhudNAwrIo1UFtA88dG1AdGxhdGVyLm5ldD6I0QQTFgoA
gwWCaN+Y2gWJBaSPvQMLCQcJEJFYJf4CDDsERxQAAAAAAB4AIHNhbHRAbm90YXRp
b25zLnNlcXVvaWEtcGdwLm9yZ7YE8O8QdVSn74dRU3+tbzkaA5vOxcG8hPiHxC92
pwUQAxUKCAKbAQIeCRYhBBypFiLIAlMIN83xvZFYJf4CDDsEAAB6fAD+J8JUTiGP
8YsMdL/EQpnrYT6rnn15ubnEFYgq8uoLXREA/0xanMgtFfjlLBl1KJKATcWW1N3Y
UIwVc5fSMQlMMhcCtBRUcmlzdGFuIERhbmnDq2wgTWFhdIjUBBMWCgCGBYJo35ja
BYkFpI+9AwsJBwkQkVgl/gIMOwRHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2Vx
dW9pYS1wZ3Aub3Jn0BVq6ZTyEVzQbva4FzWzthlLI0RLKFehK5KrzYxHtUADFQoI
ApkBApsBAh4JFiEEHKkWIsgCUwg3zfG9kVgl/gIMOwQAADBZAPsFyfzAPbvpTHIM
Iy3NCjnTIEkKyUB0/Bv05XpSTJEIXAD+K6Xt7rXexehL9cU/uHbv79kkalY4dfxo
HIIMDBiFQg+4MwRo35jaFgkrBgEEAdpHDwEBB0BMgkmV8/WIQUCDNFyZ8vy6NRlf
bLUkRoeXs1goAMl1mIkBhQQYFgoBNwWCaN+Y2gWJBaSPvQkQkVgl/gIMOwRHFAAA
AAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnYPaWFUQUEGfbaDyY
ySYmH15HQu+/s1WIYTghFMsDfIICmwK+oAQZFgoAbwWCaN+Y2gkQAuk1AGzy6OdH
FAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnC4a3260kzH8Q
IQldNaxtdHhLScfZJNVvS2oA4ekTGasWIQSCgqelFkdq3xOTIn4C6TUAbPLo5wAA
dvMA/1MQKUrQDmqCTrZY7UB0teiInkZKZRmP3JC3yhVVKCuqAP9ESMmGyJZ6CMxZ
jmJJCfBkXVuTD0On6qFJA8q5TN46CBYhBBypFiLIAlMIN83xvZFYJf4CDDsEAAD1
UAEAmNy3+9KMYoiZee8q3HPjggDLxUxgPqGilGTux2UuIsIBAMtpBoN0RRAw2wau
bG0KsuS7uByzgSvVI3fIBPXW60QAuDMEaN+Y2hYJKwYBBAHaRw8BAQdAwyy3auz1
a69adFM65ASUMzHT9x/if3H2ULX7xyVPQtmJAYUEGBYKATcFgmjfmNoFiQWkj70J
EJFYJf4CDDsERxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9y
Z+z6/cvmpd8w4gdbRaiLwucfwxEXr6gHp3BSXh+nKmHtApsgvqAEGRYKAG8Fgmjf
mNoJEARvgyjdRr1eRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdw
Lm9yZ0OtJQh/BjruiyemJkYA8G/Y4dpegcQxcsLerz1YoE1nFiEE3HwYRWAJ0IIM
K3u3BG+DKN1GvV4AABCTAP9wfTED3ymUQUi5ZuRqN7ZOvSjY1rNg/hAP3XFrf1dG
BAD/ZGdj6daBbenCNO2J3kXHdRSX0rFsZd63cGD4KksGfwwWIQQcqRYiyAJTCDfN
8b2RWCX+Agw7BAAARfIBALiWY/wyxxdQe1DQ8seufBPmIWQO06pKknMP4WRPxmlj
AQDm/MS7m+XqALr0M2LL6AxSaSW5hXROgq85ppTMhtQCBrg4BGjfmNoSCisGAQQB
l1UBBQEBB0CLw491EDOnLPf2eKA5nwSp/ZMxjrUgkA/i4IFtcDNxbQMBCAeIxgQY
FgoAeAWCaN+Y2gWJBaSPvQkQkVgl/gIMOwRHFAAAAAAAHgAgc2FsdEBub3RhdGlv
bnMuc2VxdW9pYS1wZ3Aub3Jn3mOPxHpLk/tMD65hWa3xbhfZR1Oq4GFM28/HWoKL
t9wCmwwWIQQcqRYiyAJTCDfN8b2RWCX+Agw7BAAAKBIBAJ61F1bGwQyLlTmYK6Ga
THrdlscsaDkPy4EcFa4czg88AQD+FcRvkydZj6ClgTbFvV2iAI5yjOBccBrp2OK1
M4MECg==
=gDMv
mQINBF4nGvQBEAC7BhX6nCHHt/lmxnnu+z/GPJ9QPcNkXZuGtf9+GCBjUfkNpeuf
4vbyX88jlqhBHYkLKqFeWitNH1bwJtk+0gslPLnXUcYDgKNu+JrWiJd+ygJptrDf
Kh8wLRVoyKeSVURVMclAkHA328qgEPPQuIrPX6ILa2+xey3gOAW1jKm1teldQ6IB
dt1OgzaglVWhKENWQt5VamkN+ABnBymalg2Z5nbgE3LO/QW1bnAXM1wpRUcK+v99
b2KpoPVn44ZjWGXD3jLJAPxZcvanpT2RDaYhPLTrr8nRf2+i8BzA7BUHmzVZFF4h
8J9N3da/+LINGCFVqHgMT8N7/ZZgXg7PwW+k8s2juhjyv5ycgzJlE2E679LoXJeJ
jDmZdP/kFqa2FCQjfM4wpflB7EfmdNIbEIJ82kXaZOKG/cGjbEsQ8oZtGBV+Y0aH
FMU/1LD0M+n+if7Ydw7RsMzgOnr4DEXYLtNtaOgebc/rZRu7Wkix+gvAYSTwV+ph
eDSnFQZui1QXJ1gnzO6Hi4Xe4ChPwUrcIIAoJ07INWruF6nXo8h9dtpPOtMsIZ02
Ena7OwfaCuKRf0hwNYERyZN+Lzc105BzUv0d9rsA6qlv4qlaG01Lz+2kdb1zhk7E
8FYksFrdnSRwd1qYm4KKGJO/dKJat1sJI4ldK2rn2/O5Hrm9O9RaATT1QQARAQAB
tDpUcmlzdGFuIERhbmnDq2wgTWFhdCAod29yaykgPHRyaXN0YW4ubWFhdEBjb2Rl
dGhpbmsuY28udWs+iQJMBBMBCgA2FiEEU1thAVgjRDlBx0TdEiZPa736uokFAl4n
HXICGwEECwkIBwQVCgkIBRYCAwEAAh4BAheAAAoJEBImT2u9+rqJy0oP/1rl/R4n
oqJbVa4BCkUZI3wyVomibyodWed/nAFRT9BzapVWqrhYsZuOtv7Xy5/qzFHrxMOW
9aaJ3Rz7lI72uy4o86AqwRdMmSsmgFDj+HEk98eUoZed3ijtAltR83xYzlpQuJ3+
o+O+Vs+sUUAI57FZ/7RyEPzcxk/+9yU1aSGWGwjDP4tWR+Fe69kqCoCUskydU4IM
Ss3LbNzEypVGG/GOa/rUlJ3n/XvG0HjjGm1g4tnelkE06zFDIbglhPKJNuLbeH1j
RDUdj1iX81B5nFDr+ARr4zqS5PJFgyiQDxU7fDx55mTRuqQ8M/X8eYeK7PXn8w5U
2DQgh1AEgfQrLbPdZ2lwf/aINHf5wRSGvxBKIAxXpsMt98RUfd+L//aPrl5Hwi6H
ZKe0Pbkn/GwmKZq3iqd5uaN6vLqybl9cHHaQ58MPCb0guYAJD0higgrw1tgNMhX1
RGAutfQCbgJhkzqgYQ2Ystd9ky+mf4L+d5Ct+ks9J/WkkjQ61A8WbtqR34iytE+P
clR9Z+p1kgeF0mzN29ZYYrIxVFhxD7abBBZfzNLJPEmRiU7EstH0sqVtuD8G1Uec
/u/v7NTECtUu1zq5BK09mV8ZkeqTkQKCx1iRyfD8JSFYYb3sGyYtCIaOvLnnQf93
mhbzdlXAyHLshjzrHoF2TixCIpskMWx1zM/5tC1UcmlzdGFuIERhbmnDq2wgTWFh
dCAodGxhdGVyKSA8dG1AdGxhdGVyLm5ldD6JAkwEEwEKADYWIQRTW2EBWCNEOUHH
RN0SJk9rvfq6iQUCXica9AIbAQQLCQgHBBUKCQgFFgIDAQACHgECF4AACgkQEiZP
a736uon/aA/6A0tn+otsfGfei9QxDgM255R1U76PdSVb2bEl4/IdSq4uw8CtM69b
CesCoajj/qgXzEFhpijQ9pXZDpMDXk64EZtcO5T96RNXA8Oh5CkpnVXv2MOrFXVL
Zc05EOh6qxMCRACliii7CyGExAbRU/TdhL86dWc06CAiMt0/vzYgHpy69jpomHsP
OeWk8TghHsMHPHOpfk4sS8gYlvsKV7QDpW5sNPmbm/nCFr+PoR7R+BKnklhdX9HN
d/Ha6KSXpEjzqIoI/X3TtYtTyhNdwZ0rbKPtQYqMi08dnr4INjED5kZvrVFr16s1
nFp8CghrnnUq9t1hzFAnYTWX736/CU/0ZrANRVsaKdJ3tCey2OT+IdprYZlriFvO
ZO9AD5BV/V/e+QFi7LUZEEmsK3AVFERmRxP+597ZyK4ozjc0s8aLI2q9O8GROGxR
GHloGMH9Mv5T/gfBfyFtoMU3poYOHCfwvtt9sr5IuiaexsGKTSTqRUWhq9moiGb8
mQxQnZwIoRuQkmcV1JlfuS2inSpnd/Ar7M+eFUjV/qjW4Pg6LUH5WQoZbTT/3TUS
9qS1v10wLJNMIniYGZslh71yb937i6BsAKLzVP6VTsZgO4+8eoEpL46l1C/h/sTW
gdWwG/cB8c3qgcYRTtn9t93TRofnxxr8pSVihe2TAzk6I1EKcf2DzymJAjMEEAEK
AB0WIQTfYF3xdR5NoZx+auO0Z9GZCjri0AUCXiccgwAKCRC0Z9GZCjri0Mc3D/9X
/OLjPBrwR2rnv7qGB8jhg304RskvYx/kzcSadp4JQhF8zD6Lzb+F/NRzaN09E9RD
jsnF595UiOqQ9NUY1Ku0+1HicJHKg7chK11tQWQyjYZKyCc/WxoOye+G7LGjLLl0
MpJ2uO/fgD5asF6ufXU0XDVPUGUBilM2NiEFuVRK51ZOmP7hrQYjMD+TSz3PfvT5
xAyggGmDOswQGMYCRj2S/hIbTADkSVwG61OiPHWAKxIPaIK+MBJm04KM7bnZmTly
4j7ZA9oj2MikMe2z5M99EYIIDauVy5N9R0qzaOcUCFZXDaoZpPfq1fwk5Aj9tG4S
/FdlMZeYeJNHkk7ZNaZ0vdQf4P7lib6gv9V1XePB4WANoG1KRVSq8eVYQvlxlFhR
EJjiuahoT59KhX1aC4tEmBo4yC0LwQ+G2Vw2DZMZHDo8IqP/wrAPGblyOlo09vr+
Hqd7oyDSxiYFekttvLmS3wtD+Fz3X7xZ3NOooqemH1pKd4XYTTX2RNryEx+pIcMh
Emkmyo9D8P/FgwB7qTj5ANOhEVY4zYWmYKDck8AlDe1VY/yQ8mo2cJWYKo1qdpI3
mrdHBkDhNANwBfKBUu4zxrapBSrZvIYJd8YJjqvBX/EsqrQHb8Sf71HrZt8UILrQ
WBhTC0/BETEay2BDgV8tP+9l3/lJyJZzRB8u6x4UtrkBDQReJx0UAQgAk+y5c4DR
oWSiJvZMQ+w+Hz7hv4s8Q/xxvjVPmp921oYRk+qrMS963Qc9Zstn9RSI8ALZFksD
gKDPZ+DAgF1FLkv9HqoggcE8iUU+WiYCVkxj4M67nmuOeUf/vIja4fxs/t0vJXkP
U9/Hn2d5dyRyseZEYbxPdJvQGk6GJ1OO+h6hLos9GNrFB64SQug9j57YY2R5u+fU
RwE/xg2YDb4xGH8sAyZkKRNFnfQ67cL1TdfcbKs9jrI2U6KFYbdWVbRgeWEQSlLS
fIzQuUG73iHuo8FD2CKsag5smI9l4iV3W1UHEscc5soeDzLA2lH66sb/yl858bEJ
KDci3k+wE3RQEwARAQABiQI7BBgBCgAmAhsMFiEEU1thAVgjRDlBx0TdEiZPa736
uokFAmASBaMFCQPMHA8ACgkQEiZPa736uonUAw/4xh//cHEJ2UBgiei//8vBYR7E
PB62NUmFXDphoAHB1xRMlFh3ljsU25hzXfTR1SyEvuYN9f7zmmW3ZmH0rV8xn0zb
BCAORGmFm6auYV5x89Ika/ecoFAew8eeZbKuzT/ZWH9OEmGXoRP0eFAxDpOlEg85
n+ErkRxnvc3VxUYt1swPhZ9Om/bZ26XzznJ11FztmYht6VXcB9jrpVwMjk5rAAAF
LuK7Uiw9yQMaW8z7lcKQvAdiQ6j1TmGogIT3XAhVJkBNcMyb5qz+mylupMe69hs3
L8I3PPMZJhT7ymll09KURChaGR8H3dohS2b/wLNdWoqMAyXqXWHDrZ83Uor/wzGh
TQ6FHz0z8GMoiUgoU9GEQVu4vy2mjpR4vnHZ0pXP469rYdxQDkrfyuQSvbpYi9br
ayllJQG8qoHXI92wugslD2CIeI14h8C14ZkOymI4uZCv0kR3mIxV9WVAanJyHVto
HrYiHVt5TzJMqY0Eu3NPvr9W/B4x0srFOmM9MBivbTo4S3KDZEfRpqC5QCdw79qP
spm35kqWIEpM+O4gc+zE4EHUbddu/68yXNaqvWRODg8mo8flFTZ5PvpIb/qNkPOG
GDgPiiIae4ga6KNOS1STroHf63ort4G0zuQPzQg1N9ll4lo62OqDmW+25nzHC7yB
PhCB2Dz76iQ5nDY4MQ==
=R7Pm
-----END PGP PUBLIC KEY BLOCK-----

View file

@ -1,383 +0,0 @@
{
pkgs,
lib,
config,
...
}:
let
cfg = config.security.crowdsec;
settingsFormat = pkgs.formats.yaml { };
hub = pkgs.fetchFromGitHub {
owner = "crowdsecurity";
repo = "hub";
rev = "7a3b4753f4577257c0cbeb8f8f90c7f17d2ae008";
hash = "sha256-HB4jHyhiO8gjBkLmpo6bDbwhfm5m5nAtNlKhDkZjt2I=";
};
cscli = pkgs.writeShellScriptBin "cscli" ''
export PATH="$PATH:${cfg.package}/bin/"
sudo=exec
if [ "$USER" != "crowdsec" ]; then
sudo='exec /run/wrappers/bin/sudo -u crowdsec'
fi
$sudo ${cfg.package}/bin/cscli "$@"
'';
acquisitions = ''
---
${lib.concatMapStringsSep "\n---\n" builtins.toJSON cfg.acquisitions}
---
'';
in
{
imports = [ ./remediations ];
options.security.crowdsec =
let
inherit (lib.types)
nullOr
listOf
package
path
str
;
in
{
enable = lib.mkEnableOption "crowdsec";
package = lib.mkOption {
type = package;
default = pkgs.crowdsec;
};
stateDirectory = lib.mkOption {
type = path;
readOnly = true;
description = ''
The state directory of the crowdsec instance. Cannot be
changed, but is exposed for downstream use.
'';
};
settings = lib.mkOption {
inherit (settingsFormat) type;
default = { };
description = ''
The crowdsec configuration. Refer to
<https://docs.crowdsec.net/docs/next/configuration/crowdsec_configuration/>
for details on supported values.
'';
};
parserWhitelist = lib.mkOption {
type = listOf str;
default = [ ];
description = ''
Set of IP addresses to add to a parser-based whitelist.
Addresses can be specified either as plain IP addresses or
in CIDR notation.
'';
};
acquisitions = lib.mkOption {
type = listOf settingsFormat.type;
default = [ ];
description = ''
Log acquisitions.
'';
};
extraGroups = lib.mkOption {
type = listOf str;
default = [ ];
description = ''
Additional groups to make the service part of.
Required to permit reading from various log sources.
'';
};
hubConfigurations = {
collections = lib.mkOption {
type = listOf str;
description = ''
List of pre-made crowdsec collections to install.
'';
};
scenarios = lib.mkOption {
type = listOf str;
description = ''
List of pre-made crowdsec scenarios to install.
'';
};
parsers = lib.mkOption {
type = listOf str;
description = ''
List of pre-made crowdsec parsers to install.
'';
};
postoverflows = lib.mkOption {
type = listOf str;
description = ''
List of pre-made crowdsec postoverflows to install.
'';
};
appsecConfigs = lib.mkOption {
type = listOf str;
description = ''
List of pre-made crowdsec appsec configurations to install.
'';
};
appsecRules = lib.mkOption {
type = listOf str;
description = ''
List of pre-made crowdsec appsec rules to install.
'';
};
};
centralApiCredentials = lib.mkOption {
type = nullOr path;
default = null;
description = ''
The API key to access crowdsec's central API - this is
required to access any of the shared blocklists.
Use of this feature is optional, entering no API key (the
default) turns all sharing or receiving of blocked IPs off.
Note that adding the API key by itself does not enable
sharing of blocked IPs with the central API. This limits the
types of blocklists this instance can access.
To also turn sharing blocked IPs on, set
`api.server.online_client.sharing = true;`.
'';
};
ctiApiKey = lib.mkOption {
type = nullOr path;
default = null;
description = ''
The API key for crowdsec's CTI offering.
'';
};
};
config = lib.mkIf cfg.enable {
# Set up default settings; anything that *shouldn't* be changed is
# set to the default priority so that users need to use
# `lib.mkForce`.
security.crowdsec = {
stateDirectory = "/var/lib/crowdsec";
settings = {
common = {
daemonize = true;
# The default logs to files, which isn't the preferred way
# on NixOS
log_media = "stdout";
};
config_paths = {
config_dir = "${cfg.stateDirectory}/config/";
data_dir = "${cfg.stateDirectory}/data/";
# This "config" file is intended to be written to using the
# cscli tool, so you can temporarily make it so rules don't
# do anything but log what they *would* do for
# experimentation.
simulation_path = "${cfg.stateDirectory}/config/simulation.yaml";
pattern_dir = lib.mkDefault "${cfg.package}/share/crowdsec/config/patterns";
hub_dir = hub;
index_path = "${hub}/.index.json";
# Integrations aren't supported for now
notification_dir = lib.mkDefault "/var/empty/";
plugin_dir = lib.mkDefault "/var/empty/";
};
crowdsec_service.acquisition_path =
# Using an if/else here because `mkMerge` does not work in
# YAML-type options
if cfg.acquisitions == [ ] then
"${cfg.package}/share/crowdsec/config/acquis.yaml"
else
pkgs.writeText "acquis.yaml" acquisitions;
cscli = {
prometheus_uri = lib.mkDefault "127.0.0.1:6060";
};
db_config = {
type = lib.mkDefault "sqlite";
db_path = lib.mkDefault "${cfg.stateDirectory}/data/crowdsec.db";
use_wal = lib.mkDefault true;
flush = {
max_items = lib.mkDefault 5000;
max_age = lib.mkDefault "7d";
};
};
api = {
cti = {
enabled = cfg.ctiApiKey != null;
key = cfg.ctiApiKey;
};
client.credentials_path = "${cfg.stateDirectory}/local_credentials.yaml";
server = {
listen_uri = lib.mkDefault "127.0.0.1:8080";
profiles_path = lib.mkDefault "${cfg.package}/share/crowdsec/config/profiles.yaml";
console_path = lib.mkDefault "${cfg.package}/share/crowdsec/config/console.yaml";
online_client = {
# By default, we don't let crowdsec phone home, since
# this is usually within NixOS users' concerns.
sharing = lib.mkDefault false;
credentials_path = cfg.centralApiCredentials;
};
};
};
# We enable prometheus by default, since cscli relies on it
# for metrics
prometheus = {
enabled = lib.mkDefault true;
level = lib.mkDefault "full";
listen_addr = lib.mkDefault "127.0.0.1";
listen_port = lib.mkDefault 6060;
};
};
};
systemd.packages = [ cfg.package ];
environment = {
systemPackages = [
# To add completions; sadly need to hand-roll this since
# neither `symlinkJoin` nor `buildEnv` have collision
# handling.
(pkgs.runCommandNoCCLocal "cscli" { } ''
mkdir -p $out
ln -s ${cscli}/bin $out/bin
ln -s ${cfg.package}/share $out/share
'')
];
etc."crowdsec/config.yaml".source = settingsFormat.generate "crowdsec-settings.yaml" cfg.settings;
};
systemd = {
tmpfiles.settings."10-crowdsec" = {
"${cfg.stateDirectory}".d = {
user = "crowdsec";
group = "crowdsec";
mode = "0700";
};
# This must be created for the setup service to work
"${cfg.stateDirectory}/config".d = {
user = "crowdsec";
group = "crowdsec";
mode = "0700";
};
"${cfg.stateDirectory}/config/parsers".d = lib.mkIf (cfg.parserWhitelist != [ ]) {
user = "crowdsec";
group = "crowdsec";
mode = "0700";
};
"${cfg.stateDirectory}/config/parsers/s02-enrich".d = lib.mkIf (cfg.parserWhitelist != [ ]) {
user = "crowdsec";
group = "crowdsec";
mode = "0700";
};
"${cfg.stateDirectory}/config/parsers/s02-enrich/nixos-whitelist.yaml" =
lib.mkIf (cfg.parserWhitelist != [ ])
{
"L+".argument =
(settingsFormat.generate "crowdsec-nixos-whitelist.yaml" {
name = "nixos/parser-whitelist";
description = "Parser whitelist generated by the crowdsec NixOS module";
whitelist = {
reason = "Filtered by NixOS whitelist";
ip = lib.lists.filter (ip: !(lib.hasInfix "/" ip)) cfg.parserWhitelist;
cidr = lib.lists.filter (ip: lib.hasInfix "/" ip) cfg.parserWhitelist;
};
}).outPath;
};
};
services = {
crowdsec-setup = {
# TODO(tlater): Depend on tmpfiles path for
# /var/lib/crowdsec/config
description = "Crowdsec database and config preparation";
script = ''
if [ ! -e '${cfg.settings.config_paths.simulation_path}' ]; then
cp '${cfg.package}/share/crowdsec/config/simulation.yaml' '${cfg.settings.config_paths.simulation_path}'
fi
if [ ! -e '${cfg.settings.api.client.credentials_path}' ]; then
${cfg.package}/bin/cscli machines add --auto --file '${cfg.settings.api.client.credentials_path}'
fi
'';
serviceConfig = {
User = "crowdsec";
Group = "crowdsec";
StateDirectory = "crowdsec";
Type = "oneshot";
RemainAfterExit = true;
};
};
# Note that the service basics are already defined upstream
crowdsec = {
enable = true;
after = [ "crowdsec-setup.service" ];
bindsTo = [ "crowdsec-setup.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
User = "crowdsec";
Group = "crowdsec";
SupplementaryGroups = cfg.extraGroups;
StateDirectory = "crowdsec";
};
};
};
};
users = {
users.crowdsec = {
isSystemUser = true;
home = cfg.stateDirectory;
group = "crowdsec";
};
groups = {
crowdsec = { };
};
};
};
}

View file

@ -1,87 +0,0 @@
{
flake-inputs,
pkgs,
lib,
config,
...
}:
let
inherit (flake-inputs.self.packages.${pkgs.system}) crowdsec-firewall-bouncer;
crowdsecCfg = config.security.crowdsec;
cfg = crowdsecCfg.remediationComponents.firewallBouncer;
settingsFormat = pkgs.formats.yaml { };
in
{
options.security.crowdsec.remediationComponents.firewallBouncer = {
enable = lib.mkEnableOption "cs-firewall-bouncer";
settings = lib.mkOption {
inherit (settingsFormat) type;
default = { };
description = ''
The bouncer configuration. Refer to
<https://docs.crowdsec.net/u/bouncers/firewall/> for details
on supported values.
'';
};
};
config = lib.mkIf cfg.enable {
security.crowdsec.remediationComponents.firewallBouncer.settings = {
mode = lib.mkDefault "${if config.networking.nftables.enable then "nftables" else "iptables"}";
log_mode = "stdout";
iptables_chains = [ "nixos-fw" ];
# Don't let users easily override this; unfortunately we need to
# set up this key through substitution at runtime.
api_key = lib.mkForce "\${API_KEY}";
api_url = lib.mkDefault "http://${crowdsecCfg.settings.api.server.listen_uri}";
};
systemd = {
packages = [ crowdsec-firewall-bouncer ];
services = {
crowdsec-firewall-bouncer-setup = {
description = "Crowdsec firewall bouncer config preparation";
script = ''
if [ ! -e '${crowdsecCfg.stateDirectory}/firewall_bouncer_credentials.yaml' ]; then
${crowdsecCfg.package}/bin/cscli -oraw bouncers add "cs-firewall-bouncer-$(${pkgs.coreutils}/bin/date +%s)" > \
${crowdsecCfg.stateDirectory}/firewall_bouncer_credentials.yaml
fi
# Stdout redirection is deliberately used to forcibly
# overwrite the file if it exists
API_KEY="$(<${crowdsecCfg.stateDirectory}/firewall_bouncer_credentials.yaml)" \
${lib.getExe pkgs.envsubst} \
-i ${settingsFormat.generate "crowdsec-firewall-bouncer.yaml" cfg.settings} \
> /var/lib/crowdsec/config/crowdsec-firewall-bouncer.yaml
'';
serviceConfig = {
User = "crowdsec";
Group = "crowdsec";
Type = "oneshot";
RemainAfterExit = true;
};
};
crowdsec-firewall-bouncer = {
enable = true;
after = [ "crowdsec-firewall-bouncer-setup.service" ];
bindsTo = [ "crowdsec-firewall-bouncer-setup.service" ];
requiredBy = [ "crowdsec.service" ];
path =
lib.optionals (cfg.settings.mode == "ipset" || cfg.settings.mode == "iptables") [ pkgs.ipset ]
++ lib.optional (cfg.settings.mode == "iptables") pkgs.iptables
++ lib.optional (cfg.settings.mode == "nftables") pkgs.nftables;
};
};
};
};
}

View file

@ -1 +0,0 @@
{ imports = [ ./cs-firewall-bouncer.nix ]; }

View file

@ -1,6 +1,5 @@
{
imports = [
./crowdsec
./nginxExtensions.nix
];
}

View file

@ -3,57 +3,156 @@
pkgs,
lib,
...
}:
{
}: {
options = {
services.nginx.domain = lib.mkOption {
type = lib.types.str;
description = "The base domain name to append to virtual domain names";
};
services.nginx.virtualHosts =
let
extraVirtualHostOptions =
{ name, config, ... }:
{
options = {
enableHSTS = lib.mkEnableOption "Enable HSTS";
services.nginx.virtualHosts = let
autheliaDomain = "auth.${config.services.nginx.domain}";
extraLocationOptions = {config, ...}: {
options = {
enableAutheliaProxy = lib.mkEnableOption "Enable recommended authelia proxy settings";
enableAuthorization = lib.mkEnableOption "Enable authorization via authelia";
};
addAccessLog = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Add special logging to `/var/log/nginx/''${serverName}`
'';
};
};
config = {
recommendedProxySettings = lib.mkIf config.enableAutheliaProxy false;
config = {
extraConfig = lib.concatStringsSep "\n" [
(lib.optionalString config.enableHSTS ''
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
'')
(lib.optionalString config.addAccessLog ''
access_log /var/log/nginx/${name}/access.log upstream_time;
'')
];
extraConfig = lib.concatStringsSep "\n" [
(lib.optionalString config.enableAutheliaProxy ''
proxy_set_header Host $host;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-URI $request_uri;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Connection "";
client_body_buffer_size 128k;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
proxy_redirect http:// $scheme://;
proxy_http_version 1.1;
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
proxy_buffers 64 256k;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
send_timeout 5m;
proxy_read_timeout 360;
proxy_send_timeout 360;
proxy_connect_timeout 360;
'')
(lib.optionalString config.enableAuthorization ''
auth_request /authelia;
set_escape_uri $target_url $scheme://$http_host$request_uri;
auth_request_set $user $upstream_http_remote_user;
auth_request_set $groups $upstream_http_remote_groups;
auth_request_set $name $upstream_http_remote_name;
auth_request_set $email $upstream_http_remote_email;
proxy_set_header Remote-User $user;
proxy_set_header Remote-Groups $groups;
proxy_set_header Remote-Email $email;
proxy_set_header Remote-Name $name;
error_page 401 =302 https://${autheliaDomain}/?rd=$target_url;
'')
];
};
};
extraVirtualHostOptions = {
name,
config,
...
}: {
options = {
enableAuthorization = lib.mkEnableOption "Enable authorization via authelia";
enableHSTS = lib.mkEnableOption "Enable HSTS";
addAccessLog = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Add special logging to `/var/log/nginx/''${serverName}`
'';
};
locations = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule extraLocationOptions);
};
};
config = {
extraConfig = lib.concatStringsSep "\n" [
(lib.optionalString config.enableHSTS ''
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
'')
(lib.optionalString config.addAccessLog ''
access_log /var/log/nginx/${name}/access.log upstream_time;
'')
];
locations = lib.mkIf config.enableAuthorization {
"/".enableAuthorization = true;
"/authelia" = {
proxyPass = "http://127.0.0.1:9091/api/verify";
recommendedProxySettings = false;
extraConfig = ''
internal;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Original-Method $request_method;
proxy_set_header X-Forwarded-Method $request_method;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Content-Length "";
proxy_set_header Connection "";
proxy_pass_request_body off;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
proxy_redirect http:// $scheme://;
proxy_http_version 1.1;
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
proxy_buffers 4 32k;
client_body_buffer_size 128k;
send_timeout 5m;
proxy_read_timeout 240;
proxy_send_timeout 240;
proxy_connect_timeout 240;
'';
};
};
in
lib.mkOption { type = lib.types.attrsOf (lib.types.submodule extraVirtualHostOptions); };
};
};
in
lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule extraVirtualHostOptions);
};
};
config = {
# Don't attempt to run acme if the domain name is not tlater.net
systemd.services =
let
confirm = ''[[ "tlater.net" = ${config.services.nginx.domain} ]]'';
in
lib.mapAttrs' (
cert: _:
systemd.services = let
confirm = ''[[ "tlater.net" = ${config.services.nginx.domain} ]]'';
in
lib.mapAttrs' (cert: _:
lib.nameValuePair "acme-${cert}" {
serviceConfig.ExecCondition = ''${pkgs.runtimeShell} -c '${confirm}' '';
}
) config.security.acme.certs;
})
config.security.acme.certs;
};
}

View file

@ -0,0 +1,86 @@
{
"bookmarks": {
"cargoLocks": null,
"date": null,
"extract": null,
"name": "bookmarks",
"passthru": null,
"pinned": false,
"src": {
"sha256": "sha256-JXNQNnWXoii71QhtKktuEBEIqzmONVetULBhpSjM9xo=",
"type": "tarball",
"url": "https://github.com/nextcloud/bookmarks/releases/download/v13.1.3/bookmarks-13.1.3.tar.gz"
},
"version": "13.1.3"
},
"calendar": {
"cargoLocks": null,
"date": null,
"extract": null,
"name": "calendar",
"passthru": null,
"pinned": false,
"src": {
"sha256": "sha256-hZfjWAMi/0qs5xMMgOlcoSXG6kcZ2aeDaez+NqSZFKI=",
"type": "tarball",
"url": "https://github.com/nextcloud-releases/calendar/releases/download/v4.6.7/calendar-v4.6.7.tar.gz"
},
"version": "v4.6.7"
},
"contacts": {
"cargoLocks": null,
"date": null,
"extract": null,
"name": "contacts",
"passthru": null,
"pinned": false,
"src": {
"sha256": "sha256-HCEjiAqn6sTNXKW6O5X6Ta9Ll4ehvzmGZUj1c0ue2Xc=",
"type": "tarball",
"url": "https://github.com/nextcloud-releases/contacts/releases/download/v5.5.3/contacts-v5.5.3.tar.gz"
},
"version": "v5.5.3"
},
"cookbook": {
"cargoLocks": null,
"date": null,
"extract": null,
"name": "cookbook",
"passthru": null,
"pinned": false,
"src": {
"sha256": "sha256-TE/w8SgyIPaGl5wZUAsG234nxoPj25QoRPF3zjbMoRk=",
"type": "tarball",
"url": "https://github.com/christianlupus-nextcloud/cookbook-releases/releases/download/v0.10.5/Cookbook-0.10.5.tar.gz"
},
"version": "0.10.5"
},
"news": {
"cargoLocks": null,
"date": null,
"extract": null,
"name": "news",
"passthru": null,
"pinned": false,
"src": {
"sha256": "sha256-cfJkKRNSz15L4E3w1tnEb+t4MrVwVzb8lb6vCOA4cK4=",
"type": "tarball",
"url": "https://github.com/nextcloud/news/releases/download/24.0.0/news.tar.gz"
},
"version": "24.0.0"
},
"notes": {
"cargoLocks": null,
"date": null,
"extract": null,
"name": "notes",
"passthru": null,
"pinned": false,
"src": {
"sha256": "sha256-ydpxatwuZUz7XIgK8FMklZlxNQklpsP8Uqpxvt3iK0k=",
"type": "tarball",
"url": "https://github.com/nextcloud/notes/releases/download/v4.10.0/notes.tar.gz"
},
"version": "v4.10.0"
}
}

View file

@ -0,0 +1,52 @@
# This file was generated by nvfetcher, please do not modify it manually.
{ fetchgit, fetchurl, fetchFromGitHub, dockerTools }:
{
bookmarks = {
pname = "bookmarks";
version = "13.1.3";
src = fetchTarball {
url = "https://github.com/nextcloud/bookmarks/releases/download/v13.1.3/bookmarks-13.1.3.tar.gz";
sha256 = "sha256-JXNQNnWXoii71QhtKktuEBEIqzmONVetULBhpSjM9xo=";
};
};
calendar = {
pname = "calendar";
version = "v4.6.7";
src = fetchTarball {
url = "https://github.com/nextcloud-releases/calendar/releases/download/v4.6.7/calendar-v4.6.7.tar.gz";
sha256 = "sha256-hZfjWAMi/0qs5xMMgOlcoSXG6kcZ2aeDaez+NqSZFKI=";
};
};
contacts = {
pname = "contacts";
version = "v5.5.3";
src = fetchTarball {
url = "https://github.com/nextcloud-releases/contacts/releases/download/v5.5.3/contacts-v5.5.3.tar.gz";
sha256 = "sha256-HCEjiAqn6sTNXKW6O5X6Ta9Ll4ehvzmGZUj1c0ue2Xc=";
};
};
cookbook = {
pname = "cookbook";
version = "0.10.5";
src = fetchTarball {
url = "https://github.com/christianlupus-nextcloud/cookbook-releases/releases/download/v0.10.5/Cookbook-0.10.5.tar.gz";
sha256 = "sha256-TE/w8SgyIPaGl5wZUAsG234nxoPj25QoRPF3zjbMoRk=";
};
};
news = {
pname = "news";
version = "24.0.0";
src = fetchTarball {
url = "https://github.com/nextcloud/news/releases/download/24.0.0/news.tar.gz";
sha256 = "sha256-cfJkKRNSz15L4E3w1tnEb+t4MrVwVzb8lb6vCOA4cK4=";
};
};
notes = {
pname = "notes";
version = "v4.10.0";
src = fetchTarball {
url = "https://github.com/nextcloud/notes/releases/download/v4.10.0/notes.tar.gz";
sha256 = "sha256-ydpxatwuZUz7XIgK8FMklZlxNQklpsP8Uqpxvt3iK0k=";
};
};
}

View file

@ -0,0 +1,21 @@
{
"prometheus-fail2ban-exporter": {
"cargoLocks": null,
"date": null,
"extract": null,
"name": "prometheus-fail2ban-exporter",
"passthru": null,
"pinned": false,
"src": {
"deepClone": false,
"fetchSubmodules": false,
"leaveDotGit": false,
"name": null,
"rev": "v0.10.1",
"sha256": "sha256-zGEhDy3uXIbvx4agSA8Mx7bRtiZZtoDZGbNbHc9L+yI=",
"type": "git",
"url": "https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter"
},
"version": "v0.10.1"
}
}

View file

@ -0,0 +1,16 @@
# This file was generated by nvfetcher, please do not modify it manually.
{ fetchgit, fetchurl, fetchFromGitHub, dockerTools }:
{
prometheus-fail2ban-exporter = {
pname = "prometheus-fail2ban-exporter";
version = "v0.10.1";
src = fetchgit {
url = "https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter";
rev = "v0.10.1";
fetchSubmodules = false;
deepClone = false;
leaveDotGit = false;
sha256 = "sha256-zGEhDy3uXIbvx4agSA8Mx7bRtiZZtoDZGbNbHc9L+yI=";
};
};
}

1430
pkgs/afvalcalendar/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,16 @@
[package]
name = "afvalcalendar"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
icalendar = "0.16.0"
serde = { version = "1.0.195", features = ["derive"] }
tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread"] }
reqwest = { version = "0.11", features = ["cookies", "json"] }
chrono = { version = "0.4.34", features = ["serde"] }
serde_json = "1.0.114"
serde_repr = "0.1.18"
hostname = "0.3.1"

View file

@ -0,0 +1,20 @@
{
pkgs,
rustPlatform,
...
}:
rustPlatform.buildRustPackage {
pname = "afvalcalendar";
version = "0.1.0";
src = ./.;
nativeBuildInputs = with pkgs; [
pkg-config
];
buildInputs = with pkgs; [
openssl
];
cargoHash = "sha256-JXx6aUKdKbUTBCwlBw5i1hZy8ofCfSrhLCwFzqdA8cI=";
}

View file

@ -0,0 +1,43 @@
use chrono::{Duration, NaiveDate};
use icalendar::{Alarm, Calendar, Component, Event, EventLike, Property};
use crate::trash::TrashType;
pub(crate) fn calendar_from_pickup_dates(dates: Vec<(TrashType, NaiveDate)>) -> Calendar {
let mut ical = Calendar::new();
ical.name("Twente Milieu Afvalkalender");
let events = dates.iter().map(|date| {
let description = match date.0 {
TrashType::Grey => "Restafval wordt opgehaald",
TrashType::Green => "GFT wordt opgehaald",
TrashType::Paper => "Papier wordt opgehaald",
TrashType::Packages => "Verpakkingen worden opgehaald",
};
let color = Property::new(
"COLOR",
match date.0 {
TrashType::Grey => "darkgray",
TrashType::Green => "darkgreen",
TrashType::Paper => "royalblue",
TrashType::Packages => "darkorange",
},
);
let reminder = Alarm::display(description, -Duration::hours(5));
Event::new()
.all_day(date.1)
.summary(description)
.append_property(color)
.alarm(reminder)
.done()
});
for event in events {
ical.push(event);
}
ical.done()
}

View file

@ -0,0 +1,15 @@
mod calendar;
mod trash;
#[tokio::main]
async fn main() {
match trash::get_pickup_dates().await {
Ok(dates) => {
let calendar = calendar::calendar_from_pickup_dates(dates);
calendar.print().unwrap();
}
Err(error) => {
eprintln!("{}", error);
}
}
}

View file

@ -0,0 +1,59 @@
use chrono::{Months, NaiveDate, NaiveDateTime, Utc};
use serde::Deserialize;
use serde_repr::Deserialize_repr;
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct CalendarAPIDatum {
pickup_dates: Vec<NaiveDateTime>,
pickup_type: TrashType,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct CalendarAPIResponse {
data_list: Vec<CalendarAPIDatum>,
}
#[derive(Copy, Clone, Deserialize_repr, Debug)]
#[repr(u8)]
pub(crate) enum TrashType {
Grey = 0,
Green = 1,
Paper = 2,
Packages = 10,
}
pub(crate) async fn get_pickup_dates() -> Result<Vec<(TrashType, NaiveDate)>, reqwest::Error> {
let today = Utc::now().date_naive();
let next_month = (today + Months::new(1)).to_string();
let today = today.to_string();
let client = reqwest::Client::new();
let params = [
("companyCode", "8d97bb56-5afd-4cbc-a651-b4f7314264b4"),
("uniqueAddressID", "1300002485"),
("startDate", &today),
("endDate", &next_month),
];
let calendar = client
.post("https://twentemilieuapi.ximmio.com/api/GetCalendar")
.form(&params)
.send()
.await?
.json::<CalendarAPIResponse>()
.await?;
Ok(calendar
.data_list
.iter()
.flat_map(|datum| {
datum
.pickup_dates
.iter()
.map(|date| (datum.pickup_type, NaiveDate::from(*date)))
})
.collect::<Vec<(TrashType, NaiveDate)>>())
}

View file

@ -0,0 +1,4 @@
POST https://twentemilieuapi.ximmio.com/api/GetCalendar
Content-Type: application/x-www-form-urlencoded
companyCode=8d97bb56-5afd-4cbc-a651-b4f7314264b4&uniqueAddressID=1300002485&startDate=2024-02-01&endDate=2024-02-29

View file

@ -1,5 +1,22 @@
{ pkgs }:
pkgs.lib.packagesFromDirectoryRecursive {
{
pkgs,
lib,
}: let
inherit (builtins) fromJSON mapAttrs readFile;
inherit (pkgs) callPackage;
directory = ./packages;
}
in
{
starbound = callPackage ./starbound {};
prometheus-fail2ban-exporter = callPackage ./prometheus/fail2ban-exporter.nix {
sources = pkgs.callPackage ./_sources_pkgs/generated.nix {};
};
afvalcalendar = callPackage ./afvalcalendar {};
}
// (
# Add nextcloud apps
let
mkNextcloudApp = pkgs.callPackage ./mkNextcloudApp.nix {};
sources = fromJSON (readFile ./_sources_nextcloud/generated.json);
in
mapAttrs (_: source: mkNextcloudApp source) sources
)

View file

@ -1,3 +0,0 @@
.direnv
.envrc
pack.zip

View file

@ -1,811 +0,0 @@
[Etc]
#ScreenShake(on/off)
"ScreenShake(on/off)" = true
#Forced viewpoint change when hit by a grab attack
"setThirdPerson(on/off)" = true
#Forced viewpoint change when hit by a grab attack
"setFirstPerson(on/off)" = true
["bosses Common settings"]
#custombossbar(on/off)
"custombossbar(on/off)" = true
#BossMusic(on/off)
"BossMusic(on/off)" = true
#BossMusicVolume(denominator)
# Default: 1
# Range: 1 ~ 1000000
BossMusicVolume = 1
#If the boss leaves the summoned location and there is no target, it returns to the summoned location. When set to 0, it does not return
# Default: 20
# Range: 0 ~ 200
ReturnHome = 20
[Weapon]
#Armor Infinity Durability(on/off)
"Armor Infinity Durability(on/off)" = true
#Bulwark of the Flame's Cooldown
# Default: 80
# Range: 0 ~ 1000000
BulwarkOfTheFlameCooldown = 80
#Gauntlet of Bulwark's Cooldown
# Default: 80
# Range: 0 ~ 1000000
GauntletOfBulwarkCooldown = 80
#Infernal Forge's Cooldown
# Default: 80
# Range: 0 ~ 1000000
InfernalForgeCooldown = 80
#Void Forge's Cooldown
# Default: 120
# Range: 0 ~ 1000000
VoidForgeCooldown = 120
#The Incinerator's Cooldown
# Default: 400
# Range: 0 ~ 1000000
TheIncineratorCooldown = 400
#Wither Assault Shoulder Weapon's Missile Cooldown
# Default: 40
# Range: 0 ~ 1000000
WASWMissileCooldown = 40
#WASW's Wither Missile's Damage
# Default: 16.0
# Range: 0.0 ~ 1000000.0
"WASW's WitherMissiledamage" = 16.0
#Wither Assault Shoulder Weapon's Howitzer Cooldown
# Default: 100
# Range: 0 ~ 1000000
WASWHowitzerCooldown = 100
#Void Assault Shoulder Weapon's Cooldown
# Default: 120
# Range: 0 ~ 1000000
VASWCooldown = 120
#Void Core's Cooldown
# Default: 160
# Range: 0 ~ 1000000
VoidCoreCooldown = 160
#Sandstorm's cooldown
# Default: 300
# Range: 0 ~ 1000000
Sandstormcooldown = 300
#Soul Render's Timer
# Default: 100
# Range: 0 ~ 1000000
SoulRenderCooldown = 100
#Gauntlet of Maelstrom's Timer
# Default: 180
# Range: 0 ~ 1000000
gauntletofMaelstromCooldown = 180
#The Immolator's Timer
# Default: 300
# Range: 0 ~ 1000000
immolatorCooldown = 300
#Storm Bringer's LightningStorm Damage
# Default: 6.0
# Range: 0.0 ~ 1000000.0
"Ceraunus 's Lightning Storm Damage" = 6.0
#Ceraunus's Wave Damage
# Default: 6.0
# Range: 0.0 ~ 1000000.0
"Ceraunus's Wave Damage'" = 6.0
#Ceraunus's Cooldown
# Default: 150
# Range: 0 ~ 1000000
"Ceraunus Cooldown" = 150
#Astrape's LightningStorm Damage
# Default: 11.0
# Range: 0.0 ~ 1000000.0
"Astrape's Lightning Spear Damage" = 11.0
#Astrape's Wave Damage
# Default: 2.0
# Range: 0.0 ~ 1000000.0
"Astrape's Area Damage'" = 2.0
#Astrape's Cooldown
# Default: 80
# Range: 0 ~ 1000000
"Astrape Cooldown" = 80
[Block]
#Cursed Tombstone Summon cooldown Minute
# Default: 1
# Range: 1 ~ 300
"Cursed Tombstone Summon cooldown Minute" = 1
["Entity damage"]
#Void Rune's Damage
# Default: 7.0
# Range: 0.0 ~ 1000000.0
Voidrunedamage = 7.0
#Ashen Breath's Damage
# Default: 4.0
# Range: 0.0 ~ 1000000.0
Ashenbreathdamage = 4.0
#Death Laser's Damage
# Default: 5.0
# Range: 0.0 ~ 1000000.0
DeathLaserdamage = 5.0
#Death Laser's Hp Damage
# Default: 5.0
# Range: 0.0 ~ 100.0
DeathLaserHpdamage = 5.0
#Player's Laser's Damage
# Default: 7.0
# Range: 0.0 ~ 1000000.0
Laserdamage = 7.0
#Blazing Bone's Damage
# Default: 5.0
# Range: 0.0 ~ 1000000.0
BlazingBonedamage = 5.0
#Lionfish Spike's Damage
# Default: 4.0
# Range: 0.0 ~ 1000000.0
LionfishSpikedamage = 4.0
#Wither Howizter's Damage
# Default: 8.0
# Range: 0.0 ~ 1000000.0
WitherHowizterdamage = 8.0
#Dimensional Rift's Damage
# Default: 10.0
# Range: 0.0 ~ 1000000.0
DimensionalRiftdamage = 10.0
#Wither Homing Missile's Damage
# Default: 3.0
# Range: 0.0 ~ 1000000.0
WitherHomingMissiledamage = 3.0
#Abyss Blast's Damage
# Default: 10.0
# Range: 0.0 ~ 1000000.0
AbyssBlastdamage = 10.0
#Abyss Blast's Hp Damage
# Default: 0.1
# Range: 0.0 ~ 1.0
AbyssBlastHpdamage = 0.1
#Abyss Orb's Damage
# Default: 4.0
# Range: 0.0 ~ 1000000.0
AbyssOrbdamage = 4.0
#Lava bomb's Radius
# Default: 2
# Range: 1 ~ 7
Lavabombradius = 2
#Amethyst Cluster's Damage
# Default: 12.0
# Range: 0.0 ~ 1000000.0
"Amethyst Cluster Damage" = 12.0
#Sandstorm's Damage
# Default: 5.0
# Range: 0.0 ~ 1000000.0
"Sandstorm Damage" = 5.0
#Ancient Desert Stele's Damage
# Default: 18.0
# Range: 0.0 ~ 1000000.0
"Ancient Desert Stele Damage" = 18.0
#Player's Phantom Arrow's Damage
# Default: 8.0
# Range: 0.0 ~ 1000000.0
"Phantom Arrow Damage" = 8.0
#Phantom Halberd's Damage
# Default: 12.0
# Range: 0.0 ~ 1000000.0
"Phantom Halberd Damage" = 12.0
#Cursed Sandstorm's Damage
# Default: 7.5
# Range: 0.0 ~ 1000000.0
"Cursed Sandstorm Damage" = 7.5
#Flame jet's Damage
# Default: 7.0
# Range: 0.0 ~ 1000000.0
"Flame Jet Damage" = 7.0
#Flare Bomb's Damage
# Default: 7.0
# Range: 0.0 ~ 1000000.0
"Flare Bomb Damage" = 7.0
["Ender Guardian"]
#EnderGuardian's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
EnderGuardianHealthMultiplier = 1.0
#EnderGuardian's Damage Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
EnderGuardianDamageMultiplier = 1.0
#EnderGuardian's DamageCap
# Default: 22.0
# Range: 0.0 ~ 1000000.0
EnderGuardianDamageCap = 22.0
#EnderGuardian's DamageTime
# Default: 30
# Range: 0 ~ 100
EnderGuardianDamageTime = 30
#EnderGuardian's Healing with out target
# Default: 25.0
# Range: 0.0 ~ 1000000.0
EnderGuardianNatureHealing = 25.0
#Ender guardian's block breaking ignore the MobGriefing
EnderguardianBlockBreaking = true
#Guardian's Immune to Long distance attack range.
# Default: 12.0
# Range: 1.0 ~ 1000000.0
"Guardian's prevent attacks from far away Range" = 12.0
#Guardian's gravity Punch Hp Damage
# Default: 0.05
# Range: 0.0 ~ 1.0
"Guardian's gravity Punch Hp Damage" = 0.05
#Guardian's Teleport attack Hp Damage
# Default: 0.05
# Range: 0.0 ~ 1.0
"Guardian's Teleport attack Hp Damage" = 0.05
#Guardian's Punch Hp Damage
# Default: 0.06
# Range: 0.0 ~ 1.0
"Guardian's knockback Hp Damage" = 0.06
#Guardian's Uppercut Hp Damage
# Default: 0.1
# Range: 0.0 ~ 1.0
"Guardian's Uppercut Hp Damage" = 0.1
#Guardian's RocketPunch Hp Damage
# Default: 0.1
# Range: 0.0 ~ 1.0
"Guardian's RocketPunch Hp Damage" = 0.1
#Guardian's etc area attack Hp Damage
# Default: 0.08
# Range: 0.0 ~ 1.0
"Guardian's area attack Hp Damage" = 0.08
#EnderGuardianBlockBreaking radius
# Default: 15
# Range: 0 ~ 20
"EnderGuardianBlockBreaking X" = 15
#EnderGuardianBlockBreaking radius
# Default: 2
# Range: 0 ~ 10
"EnderGuardianBlockBreaking Y" = 2
#EnderGuardianBlockBreaking radius
# Default: 15
# Range: 0 ~ 20
"EnderGuardianBlockBreaking Z" = 15
["Netherite Monstrosity"]
#Monstrosity's Lavabomb magazine.
# Default: 3
# Range: 1 ~ 1000000
LavabombMagazine = 3
#Monstrosity's Lavabomb amount
# Default: 3
# Range: 1 ~ 1000000
Lavabombamount = 3
#Lava Bomb of Monstrosity's Duration
# Default: 350
# Range: 1 ~ 10000
LavaBombDuration = 350
#Lava Bomb of Monstrosity's additional random duration size
# Default: 150
# Range: 1 ~ 10000
LavaBombRandomDuration = 150
#Monstrosity's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
MonstrosityHealthMultiplier = 1.0
#Monstrosity's Damage Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
MonstrosityDamageMultiplier = 1.0
#Monstrosity's Healing Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
MonstrosityHealingMultiplier = 1.0
# Monstrosity's Healing with out target
# Default: 25.0
# Range: 0.0 ~ 1000000.0
MonstrosityNatureHealing = 25.0
#Monstrosity's DamageCap
# Default: 22.0
# Range: 0.0 ~ 1000000.0
MonstrosityDamageCap = 22.0
#Monstrosity's DamageTime
# Default: 10
# Range: 0 ~ 100
MonstrosityDamageTime = 10
#Monstrosity's bodyBlocking verdict
NetheritemonstrosityBodyBloking = true
#Monstrosity's attack Hp Damage
# Default: 0.08
# Range: 0.0 ~ 1.0
"Monstrosity's attack Hp Damage" = 0.08
#Monstrosity's Immune to Long distance attack range.
# Default: 18.0
# Range: 1.0 ~ 1000000.0
"Monstrosity's prevent attacks from far away Range" = 18.0
#Monstrosity's block breaking ignore the MobGriefing
monstrosityBlockBreaking = true
["Ender Golem"]
#Ender Golem's block breaking ignore the MobGriefing
EndergolemBlockBreaking = false
#Endergolem's Immune to Long distance attack range.
# Default: 6.0
# Range: 1.0 ~ 1000000.0
"Endergolem's prevent attacks from far away Range" = 6.0
#Golem's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
GolemHealthMultiplier = 1.0
#Golem's Damage Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
GolemDamageMultiplier = 1.0
[Ignis]
#Ignis's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
IgnisHealthMultiplier = 1.0
#Ignis's Damage Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
IgnisDamageMultiplier = 1.0
#Ignis's Healing with out target
# Default: 25.0
# Range: 0.0 ~ 1000000.0
IgnisNatureHealing = 25.0
#Ignis's Healing Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
IgnisHealingMultiplier = 1.0
#Ignis's Immune to Long distance attack range.
# Default: 15.0
# Range: 1.0 ~ 1000000.0
"Ignis's prevent attacks from far away Range" = 15.0
#Ignis's DamageCap
# Default: 20.0
# Range: 0.0 ~ 1000000.0
IgnisDamageCap = 20.0
#Ignis's DamageTime
# Default: 15
# Range: 0 ~ 100
IgnisDamageTime = 15
#Ignis's cracked block breaking ignore the MobGriefing
IgnisBlockBreaking = true
[revenant]
#Revenant's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
RevenantHealthMultiplier = 1.0
#Revenant's Damage Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
RevenantDamageMultiplier = 1.0
["The Prowler"]
#The Prowler's Immune to Long distance attack range.
# Default: 16.0
# Range: 1.0 ~ 1000000.0
"The Prowler's prevent attacks from far away Range" = 16.0
#Prowler's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
ProwlerHealthMultiplier = 1.0
#Prowler's Damage Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
ProwlerDamageMultiplier = 1.0
["The Harbinger"]
#Harbinger's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
HarbingerHealthMultiplier = 1.0
#Harbinger's Damage Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
HarbingerDamageMultiplier = 1.0
#Harbinger's Healing Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
HarbingerHealingMultiplier = 1.0
#Harbinger's Wither Missile's Damage
# Default: 8.0
# Range: 0.0 ~ 1000000.0
"Harbinger's WitherMissiledamage" = 8.0
#Harbinger's laser's Damage
# Default: 5.0
# Range: 0.0 ~ 1000000.0
"Harbinger's laser damage" = 5.0
#Harbinger's Immune to Long distance attack range.
# Default: 35.0
# Range: 1.0 ~ 1000000.0
"The Harbinger's prevent attacks from far away Range" = 35.0
#Harbinger's DamageCap
# Default: 22.0
# Range: 0.0 ~ 1000000.0
"The Harbinger DamageCap" = 22.0
#Harbinger's DamageTime
# Default: 12
# Range: 0 ~ 100
"The Harbinger DamageTime" = 12
#Harbinger's lasers can light a fire in MobGriefing
"The Harbinger Light A Fire" = true
#The Harbinger's charge attack Hp Damage
# Default: 0.06
# Range: 0.0 ~ 1.0
"The Harbinger's charge attack Hp Damage" = 0.06
["The Leviathan"]
#Leviathan's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
LeviathanHealthMultiplier = 1.0
#Leviathan's Damage Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
LeviathanDamageMultiplier = 1.0
#Leviathan's Healing with out target
# Default: 25.0
# Range: 0.0 ~ 1000000.0
LeviathanNatureHealing = 25.0
#Leviathan's Immune to Long distance attack range.
# Default: 38.0
# Range: 1.0 ~ 1000000.0
"Leviathan's prevent attacks from far away Range" = 38.0
#Leviathan's Bite Hp Damage
# Default: 0.1
# Range: 0.0 ~ 1.0
"Leviathan's Bite Hp Damage" = 0.1
#Leviathan's Rush Hp Damage
# Default: 0.05
# Range: 0.0 ~ 1.0
"Leviathan's Rush Hp Damage" = 0.05
#Leviathan's TailSwing Hp Damage
# Default: 0.08
# Range: 0.0 ~ 1.0
"Leviathan's TailSwing Hp Damage" = 0.08
#Leviathan's Tentacle Hp Damage
# Default: 0.03
# Range: 0.0 ~ 1.0
"Leviathan's Tentacle Hp Damage" = 0.03
#Leviathan's DamageCap
# Default: 20.0
# Range: 0.0 ~ 1000000.0
LeviathanDamageCap = 20.0
#Leviathan's DamageTime
# Default: 15
# Range: 0 ~ 100
"Leviathan DamageTime" = 15
#Leviathan's block breaking ignore the MobGriefing
LeviathanBlockBreaking = true
#Leviathan Immune Out of Water
LeviathanImmuneOutofWater = true
["The Baby Leviathan"]
#BabyLeviathan's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
BabyLeviathanHealthMultiplier = 1.0
#BabyLeviathan's Damage Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
BabyLeviathanDamageMultiplier = 1.0
["Modern Remnant"]
#Modern Remnant's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
ModernRemnantHealthMultiplier = 1.0
#Modern Remnant's Damage Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
ModernRemnantDamageMultiplier = 1.0
["Netherite Ministrosity"]
#Ministrosity's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
MinistrosityHealthMultiplier = 1.0
["Amethyst Crab"]
#Amethyst Crab's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
AmethystCrabHealthMultiplier = 1.0
#Amethyst Crab's EarthQuake Damage
# Default: 5.0
# Range: 0.0 ~ 1000000.0
AmethystCrabEarthQuakeDamage = 5.0
#Amethyst Crab's Damage Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
AmethystCrabDamageMultiplier = 1.0
["Ancient Remnant"]
#Ancient Remnant's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
AncientRemnantHealthMultiplier = 1.0
#Ancient Remnant's Damage Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
AncientRemnantDamageMultiplier = 1.0
#AncientRemnant's Healing with out target
# Default: 25.0
# Range: 0.0 ~ 1000000.0
AncientRemnantNatureHealing = 25.0
#Ancient Remnant's Immune to Long distance attack range.
# Default: 14.0
# Range: 1.0 ~ 1000000.0
"Ancient Remnant's prevent attacks from far away Range" = 14.0
#Ancient Remnant's DamageCap
# Default: 21.0
# Range: 0.0 ~ 1000000.0
AncientRemnantCap = 21.0
#Ancient Remnant's DamageTime
# Default: 12
# Range: 0 ~ 100
"Ancient Remnant DamageTime" = 12
#Ancient Remnant's block breaking ignore the MobGriefing
AncientRemnantBlockBreaking = true
#Remnant's Charge Hp Damage
# Default: 0.1
# Range: 0.0 ~ 1.0
"Remnant's Charge Hp Damage" = 0.1
#Remnant's Hp Damage
# Default: 0.05
# Range: 0.0 ~ 1.0
"Remnant's Normal attack Hp Damage" = 0.05
#Remnant's Stomp Hp Damage
# Default: 0.03
# Range: 0.0 ~ 1.0
"Remnant's Stomp Hp Damage" = 0.03
#Remnant's EarthQuake Damage
# Default: 11.0
# Range: 0.0 ~ 1000000.0
"Remnant's EarthQuakeDamage" = 11.0
[Koboleton]
#Cause Koboleton to Drop Item In Hand Percent
# Default: 5.0
# Range: 0.0 ~ 100.0
CauseKoboletontoDropItemInHandPercent = 5.0
[Kobolediator]
#Kobolediator's block breaking ignore the MobGriefing
KobolediatorBlockBreaking = false
#Kobolediator's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
KobolediatorHealthMultiplier = 1.0
#Kobolediator's Damage Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
KobolediatorDamageMultiplier = 1.0
[Wadjet]
#Wadjet's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
WadjetHealthMultiplier = 1.0
#Wadjet's Damage Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
WadjetDamageMultiplier = 1.0
[Aptrgangr]
#Aptrgangr's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
AptrgangrHealthMultiplier = 1.0
#Aptrgangr's Damage Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
AptrgangrDamageMultiplier = 1.0
#Axe Blade's Damage
# Default: 8.0
# Range: 0.0 ~ 1000000.0
AptrgangrAxeBladeDamage = 8.0
[Clawdian]
#Clawdian's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
ClawdianHealthMultiplier = 1.0
#Clawdian's Damage Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
ClawdianDamageMultiplier = 1.0
[Scylla]
#Scylla's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
ScyllaHealthMultiplier = 1.0
#Scylla's Damage Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
ScyllaDamageMultiplier = 1.0
#Scylla's Immune to Long distance attack range.
# Default: 10.0
# Range: 1.0 ~ 1000000.0
"Scylla's prevent attacks from far away Range" = 10.0
#Scylla's Spear Damage
# Default: 14.0
# Range: 0.0 ~ 1000000.0
"Scylla's Spear Damage" = 14.0
#Scylla's Lightning Storm
# Default: 10.0
# Range: 0.0 ~ 1000000.0
"Scylla's Lightning Storm" = 10.0
#Scylla's Lightning Area
# Default: 4.0
# Range: 0.0 ~ 1000000.0
"Scylla's Lightning Area" = 4.0
#Scylla's Storm Serpent
# Default: 16.0
# Range: 0.0 ~ 1000000.0
"Scylla's Snake Damage" = 16.0
#Scylla's Anchor Damage
# Default: 16.0
# Range: 0.0 ~ 1000000.0
"Scylla's Anchor Damage" = 16.0
#Scylla's Healing with out target
# Default: 25.0
# Range: 0.0 ~ 1000000.0
"Scylla NatureHealing" = 25.0
#Scylla's DamageCap
# Default: 22.0
# Range: 0.0 ~ 1000000.0
"Scylla DamageCap" = 22.0
#Scylla's DamageTime
# Default: 25
# Range: 0 ~ 100
"Scylla DamageTime" = 25
#Scylla's HP Damage
# Default: 0.05
# Range: 0.0 ~ 1000000.0
"Scylla's HP Damage" = 0.05
#Scylla's Spin HP Damage
# Default: 0.07
# Range: 0.0 ~ 1000000.0
"Scylla's Spin Hp Damage" = 0.07
#Scylla's Lightning Storm HP Damage
# Default: 0.04
# Range: 0.0 ~ 1000000.0
"Scylla's Lightning Storm HP Damage" = 0.04
[Maledictus]
#Maledictus's Health Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
MaledictusHealthMultiplier = 1.0
#Maledictus's Damage Multiplier
# Default: 1.0
# Range: 0.0 ~ 1000000.0
MaledictusDamageMultiplier = 1.0
#Maledictus's Immune to Long distance attack range.
# Default: 12.0
# Range: 1.0 ~ 1000000.0
"Maledictus's prevent attacks from far away Range" = 12.0
#Maledictus's Healing with out target
# Default: 25.0
# Range: 0.0 ~ 1000000.0
MaledictusNatureHealing = 25.0
#Maledictus's Phantom Halberd Damage
# Default: 10.0
# Range: 0.0 ~ 1000000.0
"Maledictus' Phantom Halberd Damage'" = 10.0
#Maledictus's DamageCap
# Default: 20.0
# Range: 0.0 ~ 1000000.0
MaledictusDamageCap = 20.0
#Maledictus's DamageTime
# Default: 30
# Range: 0 ~ 100
"Maledictus DamageTime" = 30
#Maledictus's melee Hp Damage
# Default: 0.05
# Range: 0.0 ~ 1.0
"Maledictus's melee Hp Damage" = 0.05
#Maledictus's Shock wave Hp Damage
# Default: 0.03
# Range: 0.0 ~ 1.0
"Maledictus's Shock Wave Hp Damage" = 0.03
#Maledictus's AOE Hp Damage
# Default: 0.15
# Range: 0.0 ~ 1.0
"Maledictus's AOE Hp Damage" = 0.15
#Maledictus's flying Smash Hp Damage
# Default: 0.1
# Range: 0.0 ~ 1.0
"Maledictus's Flying Smash Hp Damage" = 0.1
#Maledictus's Jump Smash Hp Damage
# Default: 0.08
# Range: 0.0 ~ 1.0
"Maledictus's Jump Smash Hp Damage" = 0.08
#Maledictus's Phantom Arrow's Damage
# Default: 5.0
# Range: 0.0 ~ 1000000.0
"Maledictus's Phantom Arrow Damage" = 5.0
#Maledictus's cracked block breaking ignore the MobGriefing
MaledictusBlockBreaking = true
[spawning]
#Spawn Weight, added to a pool of other mobs for each biome. Higher number = higher chance of spawning. 0 = disable spawn
# Default: 2
# Range: 0 ~ 1000
DeeplingSpawnWeight = 0
#Random roll chance to enable mob spawning. Higher number = lower chance of spawning
# Default: 30
# Range: > 0
DeeplingSpawnRolls = 30
#Spawn Weight, added to a pool of other mobs for each biome. Higher number = higher chance of spawning. 0 = disable spawn
# Default: 1
# Range: 0 ~ 1000
DeeplingBruteSpawnWeight = 0
#Random roll chance to enable mob spawning. Higher number = lower chance of spawning
# Default: 50
# Range: > 0
DeeplingBruteSpawnRolls = 50
#Spawn Weight, added to a pool of other mobs for each biome. Higher number = higher chance of spawning. 0 = disable spawn
# Default: 2
# Range: 0 ~ 1000
DeeplingAnglerSpawnWeight = 0
#Random roll chance to enable mob spawning. Higher number = lower chance of spawning
# Default: 30
# Range: > 0
DeeplingAnglerSpawnRolls = 30
#Spawn Weight, added to a pool of other mobs for each biome. Higher number = higher chance of spawning. 0 = disable spawn
# Default: 1
# Range: 0 ~ 1000
DeeplingPriestSpawnWeight = 0
#Random roll chance to enable mob spawning. Higher number = lower chance of spawning
# Default: 70
# Range: > 0
DeeplingPriestSpawnRolls = 70
#Spawn Weight, added to a pool of other mobs for each biome. Higher number = higher chance of spawning. 0 = disable spawn
# Default: 1
# Range: 0 ~ 1000
DeeplingWarlockSpawnWeight = 0
#Random roll chance to enable mob spawning. Higher number = lower chance of spawning
# Default: 70
# Range: > 0
DeeplingWarlockSpawnRolls = 70
#Spawn Weight, added to a pool of other mobs for each biome. Higher number = higher chance of spawning. 0 = disable spawn
# Default: 1
# Range: 0 ~ 1000
CoralgolemSpawnWeight = 1
#Random roll chance to enable mob spawning. Higher number = lower chance of spawning
# Default: 70
# Range: > 0
CoralgolemSpawnRolls = 70
#Spawn Weight, added to a pool of other mobs for each biome. Higher number = higher chance of spawning. 0 = disable spawn
# Default: 1
# Range: 0 ~ 1000
AmethystCrabSpawnWeight = 1
#Random roll chance to enable mob spawning. Higher number = lower chance of spawning
# Default: 20
# Range: > 0
AmethystCrabSpawnRolls = 20
#Spawn Weight, added to a pool of other mobs for each biome. Higher number = higher chance of spawning. 0 = disable spawn
# Default: 15
# Range: 0 ~ 1000
KoboletonSpawnWeight = 15
#Random roll chance to enable mob spawning. Higher number = lower chance of spawning
# Default: 1
# Range: > 0
KoboletonSpawnRolls = 1
#Spawn Weight, added to a pool of other mobs for each biome. Higher number = higher chance of spawning. 0 = disable spawn
# Default: 5
# Range: 0 ~ 1000
IgnitedBerserkerSpawnWeight = 5
#Random roll chance to enable mob spawning. Higher number = lower chance of spawning
# Default: 2
# Range: > 0
IgnitedBerserkerSpawnRolls = 2
["World Generation"]
#Defines the area in which the structure check for height variances (1 means 9 chunks will be checked (center + area around it)) - 0 disables this check
# Default: 2
# Range: 0 ~ 5
cursedPyramidCheckRange = 2
#Allowed height variance for the check - if the variance is lower than this value the structure will not spawn (has no effect if the are check is disabled)
# Default: 2
# Range: 0 ~ 32
cursedPyramidHeightVariance = 2

View file

@ -1,249 +0,0 @@
#Settings for Inventory HUD
[inventoryhud]
#Inventory HUD mini mode
invMini = true
#Inventory HUD vertical mode
invVert = false
#Inventory HUD alpha
# Default: 0
# Range: 0 ~ 100
invAlpha = 0
#Toggle on by default
byDefault = false
#Animate recently picked up items
animatedInv = false
#Hide background if inventory is empty
hideBackground = false
#Show Inventory HUD when debug menu is open
invWithDebug = true
#Settings for ArmorStatus HUD
[armorhud]
#Is Armor Damage HUD enabled
ArmorDamage = true
#Hide if durability is above this (in percentage):
# Default: 100
# Range: 0 ~ 100
armAbove = 100
#Show/Hide armor
showArmor = true
#Show/Hide armor
showMain = true
#Show/Hide armor
showOff = true
#Show/Hide armor
showArrows = true
#Show/Hide armor
showInv = true
#Armor HUD durability view (PERCENTAGE, DAMAGE, DAMAGE LEFT)
#Allowed Values: PERCENTAGE, DAMAGE, DAMAGE_LEFT, OFF
armView = "PERCENTAGE"
#Show item durability bar
armBars = false
#Move all items at once or each one
moveAll = true
#Show/Hide empty slot icon
showEmpty = true
#Show overall count of items in main/off hand
showCount = false
#ArmorHUD scale in persentage from 50 to 150
# Default: 100
# Range: 50 ~ 150
armScale = 100
#Show Armor HUD when debug menu is open
armWithDebug = true
#Show arrows count when no weapon in your hands
showArrowsWithoutWeapon = true
#Settings for Potions HUD
[potionshud]
#Is Potions HUD enabled
Potions = true
#Potion HUD alpha
# Default: 100
# Range: 0 ~ 100
potAlpha = 100
#Potion HUD gap
# Default: 0
# Range: -5 ~ 5
potGap = 0
#Potion HUD mini mode
potMini = false
#Potion HUD horizontal mode
potHor = false
#Full bar duration
# Default: 300
# Range: > 1
barDuration = 300
#Show hidden effects
showHiddenEffects = true
#Disable icons for effects in this list
effectsBlackList = []
#Show Potion HUD when debug menu is open
potWithDebug = true
#Show all levels of effects
potionLevels = false
#DONT TOUCH THESE FIELDS!
[positions]
#Inventory HUD vertical align
#Allowed Values: TOP, CENTER, BOTTOM
invValign = "BOTTOM"
#Inventory HUD horizontal align
#Allowed Values: LEFT, MIDDLE, RIGHT
invHalign = "MIDDLE"
#Inventory HUD position (X)
# Default: 0
# Range: -9999 ~ 9999
xPos = 0
#Inventory HUD position (Y)
# Default: 150
# Range: -9999 ~ 9999
yPos = 150
#Armor HUD vertical align
#Allowed Values: TOP, CENTER, BOTTOM
armValign = "BOTTOM"
#Armor HUD horizontal align
#Allowed Values: LEFT, MIDDLE, RIGHT
armHalign = "MIDDLE"
#Armor HUD position (X)
# Default: 0
# Range: -9999 ~ 9999
xArmPos = 0
#Armor HUD position (Y)
# Default: 70
# Range: -9999 ~ 9999
yArmPos = 70
#Potion HUD vertical align
#Allowed Values: TOP, CENTER, BOTTOM
potValign = "TOP"
#Potion HUD horizontal align
#Allowed Values: LEFT, MIDDLE, RIGHT
potHalign = "LEFT"
#Potion HUD position (X)
# Default: 20
# Range: -9999 ~ 9999
xPotionPos = 20
#Potion HUD position (Y)
# Default: 20
# Range: -9999 ~ 9999
yPotionPos = 20
#Helmet position (X)
# Default: 103
# Range: -9999 ~ 9999
helmPosX = 103
#Helmet position (Y)
# Default: 54
# Range: -9999 ~ 9999
helmPosY = 54
#Chestplate position (X)
# Default: 103
# Range: -9999 ~ 9999
chestPosX = 103
#Chestplate position (Y)
# Default: 37
# Range: -9999 ~ 9999
chestPosY = 37
#Leggings position (X)
# Default: -103
# Range: -9999 ~ 9999
legPosX = -103
#Leggings position (Y)
# Default: 54
# Range: -9999 ~ 9999
legPosY = 54
#Boots position (X)
# Default: -103
# Range: -9999 ~ 9999
bootPosX = -103
#Boots position (Y)
# Default: 37
# Range: -9999 ~ 9999
bootPosY = 37
#MainHand position (X)
# Default: 103
# Range: -9999 ~ 9999
mainPosX = 103
#MainHand position (Y)
# Default: 71
# Range: -9999 ~ 9999
mainPosY = 71
#OffHand position (X)
# Default: -103
# Range: -9999 ~ 9999
offPosX = -103
#OffHand position (Y)
# Default: 71
# Range: -9999 ~ 9999
offPosY = 71
#Arrows position (X)
# Default: 103
# Range: -9999 ~ 9999
arrPosX = 103
#Arrows position (Y)
# Default: 20
# Range: -9999 ~ 9999
arrPosY = 20
#InvIcon position (X)
# Default: -103
# Range: -9999 ~ 9999
invPosX = -103
#InvIcon position (Y)
# Default: 20
# Range: -9999 ~ 9999
invPosY = 20
#Helmet horizontal align
#Allowed Values: LEFT, MIDDLE, RIGHT
helmHal = "MIDDLE"
#Helmet vertical align
#Allowed Values: TOP, CENTER, BOTTOM
helmVal = "BOTTOM"
#Chestplate horizontal align
#Allowed Values: LEFT, MIDDLE, RIGHT
chestHal = "MIDDLE"
#Chestplate vertical align
#Allowed Values: TOP, CENTER, BOTTOM
chestVal = "BOTTOM"
#Leggings horizontal align
#Allowed Values: LEFT, MIDDLE, RIGHT
legHal = "MIDDLE"
#Leggings vertical align
#Allowed Values: TOP, CENTER, BOTTOM
legVal = "BOTTOM"
#Boots horizontal align
#Allowed Values: LEFT, MIDDLE, RIGHT
bootHal = "MIDDLE"
#Boots vertical align
#Allowed Values: TOP, CENTER, BOTTOM
bootVal = "BOTTOM"
#MainHand horizontal align
#Allowed Values: LEFT, MIDDLE, RIGHT
mainHal = "MIDDLE"
#MainHand vertical align
#Allowed Values: TOP, CENTER, BOTTOM
mainVal = "BOTTOM"
#OffHand horizontal align
#Allowed Values: LEFT, MIDDLE, RIGHT
offHal = "MIDDLE"
#OffHand vertical align
#Allowed Values: TOP, CENTER, BOTTOM
offVal = "BOTTOM"
#Arrows horizontal align
#Allowed Values: LEFT, MIDDLE, RIGHT
arrHal = "MIDDLE"
#Arrows vertical align
#Allowed Values: TOP, CENTER, BOTTOM
arrVal = "BOTTOM"
#InvIcon horizontal align
#Allowed Values: LEFT, MIDDLE, RIGHT
invHal = "MIDDLE"
#InvIcon vertical align
#Allowed Values: TOP, CENTER, BOTTOM
invVal = "BOTTOM"
#Notification settings
[notification]
#Last notified mod version
lastNotifiedVersion = "3.4.28"
#Notify more than once
keepNotifying = true

View file

@ -1,9 +0,0 @@
#This file stores configuration options for Iris, such as the currently active shaderpack
#Thu Jul 31 02:25:56 HKT 2025
allowUnknownShaders=false
colorSpace=SRGB
disableUpdateMessage=false
enableDebugOptions=false
enableShaders=true
maxShadowRenderDistance=32
shaderPack=ComplementaryReimagined_r5.5.1 + Colorwheel_0.2.4.zip

View file

@ -1,66 +0,0 @@
{
"animation_settings": {
"animation": true,
"water": true,
"lava": true,
"fire": true,
"portal": true,
"block_animations": true,
"sculk_sensor": true
},
"particle_settings": {
"particles": true,
"rain_splash": true,
"block_break": true,
"block_breaking": true,
"other": {
"subtle_effects:item_rarity": false,
"subtle_effects:frosty_breath": false
}
},
"detail_settings": {
"sky": true,
"sun": true,
"moon": true,
"stars": true,
"rain_snow": true,
"biome_colors": true,
"sky_colors": true
},
"render_settings": {
"fog_distance": 0,
"fog_start": 100,
"multi_dimension_fog_control": false,
"dimensionFogDistance": {},
"light_updates": true,
"item_frame": true,
"armor_stand": true,
"painting": true,
"piston": true,
"beacon_beam": true,
"limit_beacon_beam_height": false,
"enchanting_table_book": true,
"item_frame_name_tag": true,
"player_name_tag": true
},
"extra_settings": {
"overlay_corner": "TOP_LEFT",
"text_contrast": "NONE",
"show_fps": false,
"show_f_p_s_extended": true,
"show_coords": false,
"reduce_resolution_on_mac": false,
"use_adaptive_sync": false,
"cloud_height": 192,
"cloud_distance": 100,
"toasts": true,
"advancement_toast": true,
"recipe_toast": true,
"system_toast": true,
"tutorial_toast": true,
"instant_sneak": false,
"prevent_shaders": false,
"steady_debug_hud": true,
"steady_debug_hud_refresh_interval": 1
}
}

View file

@ -1,18 +0,0 @@
[sodiumdynamiclights]
#Lighting mode
#Allowed Values: OFF, SLOW, FAST, REALTIME
mode = "OFF"
#Enable entities light source.
entities = false
#Enable first-person player light source.
self = true
#Enable block entities light source.
block_entities = true
#Enables water-sensitive light sources check. This means that water-sensitive items will not light up when submerged in water.
water_sensitive_check = true
#TNT lighting mode. May be off, simple or fancy.
#Allowed Values: OFF, SIMPLE, FANCY
tnt = "SIMPLE"
#Creeper lighting mode. May be off, simple or fancy.
#Allowed Values: OFF, SIMPLE, FANCY
creeper = "OFF"

View file

@ -1,135 +0,0 @@
[embeddiumextras.general]
#Set Fullscreen mode
#Borderless let you change between screens more faster and move your mouse across monitors
#Allowed Values: WINDOWED, BORDERLESS, FULLSCREEN
fullscreen = "WINDOWED"
#Configure FPS Display mode
#Complete mode gives you min FPS count and average count
#Allowed Values: OFF, SIMPLE, ADVANCED
fpsDisplay = "ADVANCED"
#Configure FPS Display gravity
#Places counter on specified corner of your screen
#Allowed Values: LEFT, CENTER, RIGHT
fpsDisplayGravity = "LEFT"
#Shows GPU and memory usage onto FPS display
#Allowed Values: OFF, ON, GPU, RAM
fpsDisplaySystem = "ON"
#Configure FPS Display margin
#Give some space between corner and text
# Default: 12
# Range: 0 ~ 48
fpsDisplayMargin = 12
#Toggle FPS Display shadow
#In case sometimes you can't see the text
fpsDisplayShadow = false
[embeddiumextras.quality]
#Toggle fog feature
#Fog was a vanilla feature, toggling off may increases performance
fog = true
#Raise clouds
#Modify clouds height perfect for a adaptative world experience
# Default: 192
# Range: 0 ~ 512
cloudsHeight = 192
#Chunks fade in speed
#This option doesn't affect performance, just changes speed
#Allowed Values: OFF, FAST, SLOW
chunkFadeSpeed = "SLOW"
[embeddiumextras.quality.darkness]
#Configure Darkness Mode
#Each config changes what is considered 'true darkness'
#Allowed Values: PITCH_BLACK, TOTAL_DARKNESS, DARK, DIM, OFF
mode = "OFF"
#Toggle Darkness on Overworld dimension
enableOnOverworld = true
#Toggle Darkness on Nether dimension
enableOnNether = false
#Configure fog brightness on nether when darkness is enabled
# Default: 0.5
# Range: 0.0 ~ 1.0
netherFogBright = 0.5
#Toggle Darkness on End dimension
enableOnEnd = false
#Configure fog brightness on nether when darkness is enabled
# Default: 0.5
# Range: 0.0 ~ 1.0
endFogBright = 0.5
#Toggle Darkness default mode for modded dimensions
valueByDefault = false
#List of all dimensions to use True Darkness
#This option overrides 'valueByDefault' state
dimensionWhitelist = []
#Toggle darkness when dimension has no SkyLight
enableOnNoSkyLight = false
#Disables all bright sources of darkness like moon or fog
#Only affects darkness effect
enableBlockLightOnly = false
#Toggles if moon phases affects darkness in the overworld
affectedByMoonPhase = true
#Configure max moon brightness level with darkness
# Default: 0.25
# Range: 0.0 ~ 1.0
fullMoonBright = 0.25
#Configure min moon brightness level with darkness
# Default: 0.0
# Range: 0.0 ~ 1.0
newMoonBright = 0.0
[embeddiumextras.performance]
#Toggles JREI item rendering until searching
#Increases performance a little bit and cleans your screen when you don't want to use it
hideJREI = false
#Toggles Minecraft Fonts shadows
#Depending of the case may increase performance
#Gives a flat style text
fontShadows = true
[embeddiumextras.performance.distanceCulling.tileEntities]
#Toggles distance culling for Block Entities
#Maybe you use another mod for that :(
enable = true
#Configure horizontal max distance before cull Block entities
#Value is squared, default was 64^2 (or 64x64)
# Default: 4096
# Range: > 0
cullingMaxDistanceX = 4096
#Configure vertical max distance before cull Block entities
#Value is raw
# Default: 32
# Range: 0 ~ 512
cullingMaxDistanceY = 32
#List of all Block Entities to be ignored by distance culling
#Uses ResourceLocation to identify it
#Example 1: "minecraft:chest" - Ignores chests only
#Example 2: "ae2:*" - ignores all Block entities from Applied Energetics 2
whitelist = ["waterframes:*"]
[embeddiumextras.performance.distanceCulling.entities]
#Toggles distance culling for entities
#Maybe you use another mod for that :(
enable = true
#Configure horizontal max distance before cull entities
#Value is squared, default was 64^2 (or 64x64)
# Default: 4096
# Range: > 0
cullingMaxDistanceX = 4096
#Configure vertical max distance before cull entities
#Value is raw
# Default: 32
# Range: 0 ~ 512
cullingMaxDistanceY = 32
#List of all Entities to be ignored by distance culling
#Uses ResourceLocation to identify it
#Example 1: "minecraft:bat" - Ignores bats only
#Example 2: "alexsmobs:*" - ignores all entities for alexmobs mod
whitelist = ["minecraft:ghast", "minecraft:ender_dragon", "iceandfire:*", "create:*"]
[embeddiumextras.others]
#Configure if borderless fullscreen option should be attached to F11 or replace vanilla fullscreen
#Allowed Values: ATTACH, REPLACE, OFF
borderlessAttachModeOnF11 = "OFF"
#Toggles fast language reload
#Embeddedt points it maybe cause troubles to JEI, so ¿why not add it as a toggleable option?
fastLanguageReload = true

View file

@ -1,50 +0,0 @@
key_iris.keybind.reload:key.keyboard.unknown:
key_iris.keybind.toggleShaders:key.keyboard.unknown:
key_iris.keybind.shaderPackSelection:key.keyboard.unknown:
key_key.corpse.death_history:key.keyboard.unknown:
key_key.curios.open.desc:key.keyboard.unknown:
key_key.cobblemon.hideparty:key.keyboard.unknown:
key_key.cobblemon.throwpartypokemon:key.keyboard.z
key_accessories.key.open_accessories_screen:key.keyboard.unknown:
key_key.jei.toggleOverlay:key.keyboard.unknown:
key_key.jei.focusSearch:key.keyboard.unknown:
key_key.jei.bookmark:key.keyboard.unknown:
key_key.jei.showRecipe:key.keyboard.unknown:
key_key.jei.showRecipe2:key.keyboard.unknown:
key_key.jei.showUses:key.keyboard.unknown:
key_key.jei.showUses2:key.keyboard.unknown:
key_key.jei.transferRecipeBookmark:key.keyboard.unknown:
key_key.jei.maxTransferRecipeBookmark:key.keyboard.unknown:
key_key.jei.clearSearchBar:key.keyboard.unknown:
key_key.jei.previousSearch:key.keyboard.unknown:
key_key.jei.nextSearch:key.keyboard.unknown:
key_key.jei.cheatOneItem:key.keyboard.unknown:
key_key.jei.cheatOneItem2:key.keyboard.unknown:
key_key.jei.cheatItemStack:key.keyboard.unknown:
key_key.jei.cheatItemStack2:key.keyboard.unknown:
key_key.jei.toggleCheatModeConfigButton:key.keyboard.unknown:
key_key.jei.toggleHideIngredient:key.keyboard.unknown:
key_key.jei.toggleWildcardHideIngredient:key.keyboard.unknown:
key_key.jei.recipeBack:key.keyboard.unknown:
key_key.jei.previousRecipePage:key.keyboard.unknown:
key_key.jei.nextRecipePage:key.keyboard.unknown:
key_key.jei.previousCategory:key.keyboard.unknown:
key_key.jei.nextCategory:key.keyboard.unknown:
key_key.jei.closeRecipeGui:key.keyboard.unknown:
key_key.aether.open_accessories.desc:key.keyboard.unknown:
key_key.aether.invisibility_toggle.desc:key.keyboard.unknown:
key_key.deep_aether.stratus_dash_ability.desc:key.keyboard.unknown:
key_key.deep_aether.toggle_skyjade_transparency:key.keyboard.unknown:
key_key.cataclysm.ability:key.keyboard.unknown:
key_key.cataclysm.helmet_ability:key.keyboard.unknown:
key_key.cataclysm.chestplate_ability:key.keyboard.unknown:
key_key.cataclysm.boots_ability:key.keyboard.unknown:
key_key.fightorflight.startbattle:key.keyboard.unknown:
key_key.journeymap.minimap_type:key.keyboard.unknown:
key_key.journeymap.minimap_preset:key.keyboard.unknown:
key_key.journeymap.create_waypoint:key.keyboard.unknown:
key_key.journeymap.toggle_entity_names:key.keyboard.unknown:
key_key.irons_spellbooks.spell_wheel:key.keyboard.v
key_key.irons_spellbooks.spellbook_cast:key.keyboard.r
key_key.inventoryhud.toggle:key.keyboard.unknown:
key_key.inventoryhud.openconfig:key.keyboard.unknown:

View file

@ -1 +0,0 @@
guiScale:2

View file

@ -1,842 +0,0 @@
hash-format = "sha256"
[[files]]
file = "config/defaultoptions/extra/config/cataclysm.toml"
hash = "9a76bf60248db9056912d34a2f5c303882f9e4bd4c1b9ffe61fef99b8eb0de38"
[[files]]
file = "config/defaultoptions/extra/config/inventoryhud-client.toml"
hash = "61d89f6c1a9c1398ff109bc4f13ec30b68f3e71408b005ec096ebab163c05a53"
[[files]]
file = "config/defaultoptions/extra/config/iris.properties"
hash = "7c386f2921f9c1a94c984114ff8df28d7c127b5b986ce344615e4ac4cb02c4eb"
[[files]]
file = "config/defaultoptions/extra/config/sodium-extra-options.json"
hash = "92a62325cccecccf843d04e1be02f036fa1a3506a21290ebf040fbb78b46fc1f"
[[files]]
file = "config/defaultoptions/extra/config/sodiumdynamiclights-client.toml"
hash = "33516556e1f7761835817a2ad13a42c3563010cf4df15296b2f685e95d0ddcb1"
[[files]]
file = "config/defaultoptions/extra/config/sodiumextras-client.toml"
hash = "8b493ad01cbdb351da8f51d98ad791b9ae9519e69d353acf82a2f893094853ef"
[[files]]
file = "config/defaultoptions/keybindings.txt"
hash = "64852eef84ab3f853973c3ad6affb00999638d0fc49515b9daa42981e3b9f5b2"
[[files]]
file = "config/defaultoptions/options.txt"
hash = "624a8e4974cae64389b28e8046c3cc18a94e609a02df63f5397ec2ca0c0f7ee8"
[[files]]
file = "config/defaultoptions/servers.dat"
hash = "0e7bfc4d1ae85b5e1bb57914cb3d07abf75a4ef5158763095f5208891f43bfe7"
[[files]]
file = "mods/aether-emissivity.pw.toml"
hash = "d245f988ce58a69b2734cbd656afc8c0ba510a7ddcc7b779c0f05de3275c52d3"
metafile = true
[[files]]
file = "mods/aether-enhanced-extinguishing.pw.toml"
hash = "3ab0e784d15c52edda001697336bc3d7bceb1d016c89b166c77b10f6fb689737"
metafile = true
[[files]]
file = "mods/aether.pw.toml"
hash = "5e62bb12eb55e4575d1ca5e3570b5cc18473138973c80ac7093e61e459339db1"
metafile = true
[[files]]
file = "mods/ambientsounds.pw.toml"
hash = "f63a7e300c9556f4bcb76b7afd3aecc4d19d5323d24c48709083f5dafcbde09f"
metafile = true
[[files]]
file = "mods/appleskin.pw.toml"
hash = "3213c881d019cd8f952f738c10d5a950ee52ff0084750b81f656a58a4a78515d"
metafile = true
[[files]]
file = "mods/architectury-api.pw.toml"
hash = "ea336d25c3e21ef168aec20e1ea470c96949ad29632646ee8e8ab0d74b4bbfc9"
metafile = true
[[files]]
file = "mods/armourers-workshop.pw.toml"
hash = "735660605aff58b7a921c328a62195c3600d23b9e7709784c3863bd618adc19a"
metafile = true
[[files]]
file = "mods/attributefix.pw.toml"
hash = "586341de7078015cea16ffaf8e2e198a16c43b306d70bc439a15ea0298f9d3cb"
metafile = true
[[files]]
file = "mods/bad-wither-no-cookie.pw.toml"
hash = "574807cedb57ba6360060d6bf4a359cea9d00bf8700caee866d48ab949005b89"
metafile = true
[[files]]
file = "mods/badoptimizations.pw.toml"
hash = "c92c73551dc3ba9f116e0cef1e90bb553bf59f34530c7c281af28ac624a3a9d6"
metafile = true
[[files]]
file = "mods/balm.pw.toml"
hash = "c51f5bc71fff2b0df7420b1c3df9f68cadb2572f6d8df239734db83c186b6715"
metafile = true
[[files]]
file = "mods/barbeques-delight-forge.pw.toml"
hash = "ae63e4f67427755b231839f860c21db3737d8aae799e7d2677e756271294238c"
metafile = true
[[files]]
file = "mods/berry-good.pw.toml"
hash = "781a0931f5ab4cc333717c2d6a56ce311474d2c0bd27681c4126aa8f24e97aa8"
metafile = true
[[files]]
file = "mods/better-advancements.pw.toml"
hash = "93c4acd165a291c6d1e69c27b05b2747afc926b38a414712263fbc95f42da323"
metafile = true
[[files]]
file = "mods/better-animations-collection.pw.toml"
hash = "fbb8968dd31ab07131a8885ddf4f70999fad54cdd68d448dbb09dd56b2bf7919"
metafile = true
[[files]]
file = "mods/better-third-person.pw.toml"
hash = "e1f1588b18947699ddc9b4f7c5b5406c5559155b8cb52dcd0f203c07c31cd0b8"
metafile = true
[[files]]
file = "mods/betterf3.pw.toml"
hash = "c9aca350959a8936b054c7164fb55d789ab1e0d38cff2af48b78f70af2bc0c6d"
metafile = true
[[files]]
file = "mods/blueprint.pw.toml"
hash = "02c2891e2548adf5c59d0ef237ed01ffd89632b4908290269b513c4958ca9043"
metafile = true
[[files]]
file = "mods/bookshelf-lib.pw.toml"
hash = "293ec22e4e8342a9bd0f338505a14e3794b44cd7ca1e5278a86f41f99c69a3af"
metafile = true
[[files]]
file = "mods/cant-sleep-clowns-will-eat-me.pw.toml"
hash = "c9b95c629249ab90b4baeb88f501c51592b52f3edddb20ebf6eed96631a54839"
metafile = true
[[files]]
file = "mods/cloth-config.pw.toml"
hash = "75181b55e0407d1fca1e2f346c6501f919f5109545990ddfb66db9cd1b771f8a"
metafile = true
[[files]]
file = "mods/clumps.pw.toml"
hash = "2acaff560bbda5741538528a2967355ec86a8f7564adba0b2316989c66892374"
metafile = true
[[files]]
file = "mods/cobblemon-additions.pw.toml"
hash = "d94f943034bca0270c72a3e56a9ef4864d81e2300c5e64418ee2b2619b7d979b"
metafile = true
[[files]]
file = "mods/cobblemon-counter.pw.toml"
hash = "46e0f70fdcc4d47e3750b8b62a9d69c2accc3b08aafc25891ba1617c3c586714"
metafile = true
[[files]]
file = "mods/cobblemon-fight-or-flight-reborn.pw.toml"
hash = "c6c63f3818b49bf0de7d5000e43ef6a381faf7d46198b13771ab6080c751f56c"
metafile = true
[[files]]
file = "mods/cobblemon-integrations.pw.toml"
hash = "d21c41437169bb49b9b92bc97a2f05daa45bcd56744f590a2a858840a5f56044"
metafile = true
[[files]]
file = "mods/cobblemon-pokenav.pw.toml"
hash = "0d1d81244b3898bfeced61aaba79e063ced9778f0c70aaddd0bd2442131066b5"
metafile = true
[[files]]
file = "mods/cobblemon-unchained.pw.toml"
hash = "98f1c04523e6d97b0d91a40614aff3d15a9e6e3c3107876e1584fdcfcb014d67"
metafile = true
[[files]]
file = "mods/cobblemon.pw.toml"
hash = "e3cb1eb238192071af93f764e3ff2c493f4f437802bb56273f664bafdea13f1d"
metafile = true
[[files]]
file = "mods/colorwheel-patcher.pw.toml"
hash = "2d8c419acad3bab8eb54c6d5461f99ab76d924b753a9c811e7e94d624fe32f61"
metafile = true
[[files]]
file = "mods/colorwheel.pw.toml"
hash = "9da820e4bd91294a900ec2c2859fa8ae261a573922df3a15ca64cb838c76df9f"
metafile = true
[[files]]
file = "mods/comforts.pw.toml"
hash = "5e0a875282f4abed66a2cccf7929488ebb5e88447d2bb3369e8ddc728fc66fc0"
metafile = true
[[files]]
file = "mods/connector.pw.toml"
hash = "9f920c5f1df2577cf374285e4aad4f2982a30ea2a3da1cf7ddaf25e04b3a2e31"
metafile = true
[[files]]
file = "mods/controlling.pw.toml"
hash = "65c8086d2ec3d39acc7bd36c7a0306ccc43b602b38fa07d13291bc478ef7dfff"
metafile = true
[[files]]
file = "mods/corn-delight.pw.toml"
hash = "a7216dad68fe8cabfbdcced66ad94e1da20d23fe531e03022f877ffead92df50"
metafile = true
[[files]]
file = "mods/corpse.pw.toml"
hash = "d0c4db41717ede48a6f9c47ce2cd30a4336b7520279cac56649f96939cd1e7cb"
metafile = true
[[files]]
file = "mods/crabbers-delight.pw.toml"
hash = "cd66e48a19b8447ef66c6148bbcd5ecb363f4a7b472dba385b4b790feae0ad0f"
metafile = true
[[files]]
file = "mods/create-dragons-plus.pw.toml"
hash = "8e88d68a9dee5ca107ea3fd5d5394e52ae4e4f03decfc33974d6a958ccb200b5"
metafile = true
[[files]]
file = "mods/create-enchantment-industry.pw.toml"
hash = "0177b98d7552743df5bd62e45d2efe012d8d416c53f94735a0d4074468f5a2ca"
metafile = true
[[files]]
file = "mods/create.pw.toml"
hash = "7f25446123f66f57e7cc8e148155a3da39d85b31963c663b5b9be77fc1bacbe6"
metafile = true
[[files]]
file = "mods/creativecore.pw.toml"
hash = "6028668d5c1c5e49797eb97bb597d818be33a67a939d0cc356a702460b504675"
metafile = true
[[files]]
file = "mods/curios.pw.toml"
hash = "36debf7653e95a855b4fedacc19027cf2e01960fc0b6d55ef2ffbffbcb455690"
metafile = true
[[files]]
file = "mods/cut-through.pw.toml"
hash = "4c16ee75d3b04443e96d854eec79344293963e9f4beaaf8e7215023e8ef53b88"
metafile = true
[[files]]
file = "mods/deep-aether.pw.toml"
hash = "5e0f65ac9e6e260e213f3d9a6657cafdd35f509965b09404a8eb9ab55c3ffbec"
metafile = true
[[files]]
file = "mods/default-options.pw.toml"
hash = "8950c7377844b642f7e6547b821691f395a6d3f90f8cf9a6d740e390792a2de3"
metafile = true
[[files]]
file = "mods/diagonal-fences.pw.toml"
hash = "632743d54d410c8b78fb92c94d8f3bb6a7a3faed45171f91e554877f94f82163"
metafile = true
[[files]]
file = "mods/diagonal-walls.pw.toml"
hash = "bbc15c019fd260bbb9058b14a8e03232a55df77608d9aca466d051007d62abd2"
metafile = true
[[files]]
file = "mods/diagonal-windows.pw.toml"
hash = "218dcd780f0251a15f78b097206ea22a519d57b974b39e2fdd43dce91ac07133"
metafile = true
[[files]]
file = "mods/distanthorizons.pw.toml"
hash = "34fc0845727b08bc39e5b56444e0695b55f6a5471380f2d1d40c5548105da28e"
metafile = true
[[files]]
file = "mods/distinguished-potions.pw.toml"
hash = "fa1ff0b4d2c6fc9c26b74ba4b06d30f1e6d77763c6e2823b42fb2c08f5b72f13"
metafile = true
[[files]]
file = "mods/dynamic-fps.pw.toml"
hash = "975f1280c712d2e86dd4ecddf89c516afbf3f1fb684499d3e5c71fc666c4888c"
metafile = true
[[files]]
file = "mods/edivadlib.pw.toml"
hash = "e2ee2481b63a012bdfb8c9f588788dd2f116ff48298014bb3e00d08a224fe14f"
metafile = true
[[files]]
file = "mods/emi-create-schematics.pw.toml"
hash = "8a9bf4dfd8d29112e64dbffd71a84aba5279f6c85ed31233f026c84ebc4ef688"
metafile = true
[[files]]
file = "mods/emi-enchanting.pw.toml"
hash = "ff029ee6fc50f2696e96cdbcc696f42e75b483c9d7b83f2b1447dcf5c865e664"
metafile = true
[[files]]
file = "mods/emi-loot.pw.toml"
hash = "bbf03bec2bd4927ace16eef106c45b0f3ec53c48d5f43119520123ad98f3fdf1"
metafile = true
[[files]]
file = "mods/emi-ores.pw.toml"
hash = "0b921f73d5b1ad6615740956567d6cb06950f5520f094dbb7a045bb4bd5ba309"
metafile = true
[[files]]
file = "mods/emi-professions-(emip).pw.toml"
hash = "0477d572817d9de3a5569109a8d07a2e519af9ccf0e8236cae621369afbc6e4c"
metafile = true
[[files]]
file = "mods/emi.pw.toml"
hash = "d3c9b48d7caa2ed964e5d1a4dab1da5655c63f0a7e4a59231c4369be7ffc30e1"
metafile = true
[[files]]
file = "mods/emiffect.pw.toml"
hash = "34342d4a5a9a4853c653cf768309d5b42f651250dc832bc239e746c736c70411"
metafile = true
[[files]]
file = "mods/enchantment-descriptions.pw.toml"
hash = "0b380e24e46e328c362bda7cff892c5b0e471789ca8c429fd2a588eaf63b8712"
metafile = true
[[files]]
file = "mods/ends-delight.pw.toml"
hash = "c31e8aea1f4ddc50d79c6dfa46dec8cbbf1a6108ff74db140baa0395ea4c5766"
metafile = true
[[files]]
file = "mods/entityculling.pw.toml"
hash = "65026277699cc4b73c3d2c57ab7aad5173f161c0d6ff95e184911826387dbdec"
metafile = true
[[files]]
file = "mods/every-compat.pw.toml"
hash = "6235e7170c7322559d2b0e76a97c891dbab60e79c15a9d4cb3a4805b5d23fbda"
metafile = true
[[files]]
file = "mods/extra-mod-integrations.pw.toml"
hash = "6f59e18621f30c6dc0ffa7f34324b423f3e3a7a8c251560c6a1dd835b6bce87a"
metafile = true
[[files]]
file = "mods/extrastorage.pw.toml"
hash = "6569836247170fbd7c8bc9397cf07961f8f67f7e73b404bfe479172da2f84279"
metafile = true
[[files]]
file = "mods/farmers-delight.pw.toml"
hash = "c4027b8071cc6dfc7f0d49e702a5e25b1e36841e50acba1e699f308be156b8f2"
metafile = true
[[files]]
file = "mods/forgified-fabric-api.pw.toml"
hash = "5b34f5aff5000de94ed9e132315bad7288a72a9cbb8f902df2799a579433b7af"
metafile = true
[[files]]
file = "mods/friends-and-foes-beekeeper-hut-forge.pw.toml"
hash = "75c642b17b484c0d7c4ec9863295c24364ef69785937d15a80de568efafd585a"
metafile = true
[[files]]
file = "mods/friends-and-foes-flowery-mooblooms-forge.pw.toml"
hash = "1cdeed5adabcbac7ca3d06ead6392d43ae400557f71275c5449ec2505008cd40"
metafile = true
[[files]]
file = "mods/friends-and-foes-forge.pw.toml"
hash = "1f1c592232159b04b7009d2413eebb2b18b5a92f7d0168cd4dde2f446bf45653"
metafile = true
[[files]]
file = "mods/fruits-delight.pw.toml"
hash = "70eaf1f619cfa0c05d2b74a3b74f3a8f28466e13feb23aaed538efcb85178a99"
metafile = true
[[files]]
file = "mods/fzzy-config.pw.toml"
hash = "e00b467d81fc5f35bf7432bc3b5d371281b0719eeae4377854a5f8ca33968190"
metafile = true
[[files]]
file = "mods/geckolib.pw.toml"
hash = "9e80e6f2a680b052f2a66ade20a5abbd98a0abbebde6f30472d6ae6b993e9464"
metafile = true
[[files]]
file = "mods/hardcore-revival.pw.toml"
hash = "2c0d0941d6ade8fb28d43b2ba211678aa643d229f0df0c2112442de1e5073a7b"
metafile = true
[[files]]
file = "mods/immersiveengineering.pw.toml"
hash = "91875c996a9958aeea794843fb449915218625f52aaf0234ccd1faffa887e277"
metafile = true
[[files]]
file = "mods/immersivemusicmod.pw.toml"
hash = "9b105ded2255d63b2b4d8b3bb24c14e467617d56019dc9abafe64cbb0c0d3cfa"
metafile = true
[[files]]
file = "mods/inventory-hud-forge.pw.toml"
hash = "bacbda049bf918a3a048f017a23e3197a91d31ab905e25f7a811d69d55344ca6"
metafile = true
[[files]]
file = "mods/iris.pw.toml"
hash = "f67aa60c765593a8c70f78296a460349c6aef5f4a16d173eae02e8803b545ad4"
metafile = true
[[files]]
file = "mods/irons-spells-n-spellbooks.pw.toml"
hash = "cf1bb72bbbb1ad55af745b6ccb8baf8ff5d6914dc11e41e4752212ca3eb17440"
metafile = true
[[files]]
file = "mods/jade.pw.toml"
hash = "e7d3f7e0472ba9a3ef946fa5defcb157e4126636e603c2306d4ad6065936d5e2"
metafile = true
[[files]]
file = "mods/jei.pw.toml"
hash = "8d3e669a813af2e685efafeca797f95ad8a99c344078e72d2ce0ee1273369bd8"
metafile = true
[[files]]
file = "mods/journey-pac.pw.toml"
hash = "c1a7d513c59eeb49bd2f377236357a280cc2e1d206c3eb3985a3a4615ed97aec"
metafile = true
[[files]]
file = "mods/journeymap-integration.pw.toml"
hash = "f161cd39e3619182a8606ec812634d74713d32ca30c3585bdf7d40b7bb573919"
metafile = true
[[files]]
file = "mods/journeymap.pw.toml"
hash = "cc903c56f854019dc420ab9a088e1a3a328e7839291aafae038f37005e59988e"
metafile = true
[[files]]
file = "mods/kiwi.pw.toml"
hash = "a5e837dd0b40e4c730252cd8b571a28bb8baab22ab8c277d2293b39ab403c201"
metafile = true
[[files]]
file = "mods/kotlin-for-forge.pw.toml"
hash = "297eb75ce1aedc28f183a8891fde9c67318953e6c8adf3d7531a17b211f9349d"
metafile = true
[[files]]
file = "mods/l_enders-cataclysm.pw.toml"
hash = "b07bbd76636d50d623dc1c41d27ca42f5f7ba196e3e2a9c846add0ee10c26531"
metafile = true
[[files]]
file = "mods/leave-my-bars-alone.pw.toml"
hash = "6fc46d72336be11582b933177410f6e0e1e89c5464e19a1885c5ce80738b01c5"
metafile = true
[[files]]
file = "mods/leaves-be-gone.pw.toml"
hash = "6917553073df67745da1d535cc6107839a76b54081b1b6f368a38c723f5fd8c7"
metafile = true
[[files]]
file = "mods/lionfish-api.pw.toml"
hash = "b821efc793dd7308be6788095bea26a2a0479f9a3ddc3b0d6a6a2af351153fc0"
metafile = true
[[files]]
file = "mods/lithostitched.pw.toml"
hash = "c18ca4f4035e67182ea949ee2d6cd2c31409306ffc404c0c14bc5c1c212747a8"
metafile = true
[[files]]
file = "mods/magnum-torch.pw.toml"
hash = "f0c97c0212ddda5c012ba451a123d4b39e89f402cc70d38d31dc9098736ec07f"
metafile = true
[[files]]
file = "mods/mmlib.pw.toml"
hash = "59f033615895cd019f9fb168fa93590422c5050274630a8faec46460979a5156"
metafile = true
[[files]]
file = "mods/modelfix.pw.toml"
hash = "6f0dcbcffd4400c6cef07f1eeb48cdc25bac0fa01270936feab4c549aff4738e"
metafile = true
[[files]]
file = "mods/moderately-enough-effect-descriptions-(meed)-jeed-addon.pw.toml"
hash = "67cf0829e2773cb44edda983e169b8197deb8e87b1cc8e1669c6ad5e4c760880"
metafile = true
[[files]]
file = "mods/modernfix.pw.toml"
hash = "a9593968393bae08257ba9aae887ad60fc49991f676b637006b2bcd89907f09a"
metafile = true
[[files]]
file = "mods/moonlight.pw.toml"
hash = "2219a1b4f53f8a96bf1e9827aa695e84ad9d53623f7fe40b88ccd260c732cc98"
metafile = true
[[files]]
file = "mods/more-concrete.pw.toml"
hash = "af08c145608a13f68a624394b68611753f7d7d6f439538e2c328eea60be5a8e1"
metafile = true
[[files]]
file = "mods/mouse-tweaks.pw.toml"
hash = "6bc414cc0f2d4c078fe83f384d786343f701f371d657c5279b5b0df1595804bd"
metafile = true
[[files]]
file = "mods/no-chat-reports.pw.toml"
hash = "5da18814472be5dc93a21886983646da3a7236413800075ac75229be82d114fa"
metafile = true
[[files]]
file = "mods/noisium.pw.toml"
hash = "b3e831b3e4d6ddbf23806b0fec6087eca264af1ebc04e747c89024972aaa8607"
metafile = true
[[files]]
file = "mods/notenoughrecipebook.pw.toml"
hash = "8c36ea5d11344480d76f8463f452c4ecc8ee59781667589ea2fd7b69f6ee86e8"
metafile = true
[[files]]
file = "mods/nullscape.pw.toml"
hash = "71dba581a68de19790dd6888e515d8b693debfc915a18ffb82af473c395f4dd3"
metafile = true
[[files]]
file = "mods/oceans-delight.pw.toml"
hash = "152f6cfe8ee891ba80faa800dd3df3058be14882f9f8d07cb38eb6a6f96ece4b"
metafile = true
[[files]]
file = "mods/octo-lib.pw.toml"
hash = "94828636fa5e8b258a60049250c84ba8c5a0310a98bc1530fc02c351a06aab8b"
metafile = true
[[files]]
file = "mods/ominous-phantoms.pw.toml"
hash = "086a276f1ab191871a11914448792bcdacc29de00770978b55ebe7d40eb6f4c9"
metafile = true
[[files]]
file = "mods/open-loader.pw.toml"
hash = "904c4adb3f8087a2c5381efd5d210183484934540372a5c839ab4790325e5cff"
metafile = true
[[files]]
file = "mods/open-parties-and-claims.pw.toml"
hash = "28d546c3ea37927bd45975200aa74c1e8fb8bd82d2a29e611abe577389818702"
metafile = true
[[files]]
file = "mods/overflowing-bars.pw.toml"
hash = "cb40fc0e1e0a3338d57b34a5ba2612ae9a9f0e32619695c4da637a98b4e52cf3"
metafile = true
[[files]]
file = "mods/owo-lib.pw.toml"
hash = "46831c04077a06df823bcd0f254833a90a9a9ccc78f5069f4b4c27fa301ac0cf"
metafile = true
[[files]]
file = "mods/pick-up-notifier.pw.toml"
hash = "a1b19740a94f43405c4a9d303e9313911b3d12e48958699bfeaed3dafc57a24f"
metafile = true
[[files]]
file = "mods/platter.pw.toml"
hash = "f82f7a5186b43d1e40773b8576a711f4d93bf09f4e3dcacc811e896f8271ad9c"
metafile = true
[[files]]
file = "mods/playeranimator.pw.toml"
hash = "c83705becd548879473132552de960f9d5975fa0edaeeb2d4665a4e35457423e"
metafile = true
[[files]]
file = "mods/prickle.pw.toml"
hash = "a69a8ba0d9e52361b1853cf59b2cf2a38b668b1d508a565f315f2fa30407f546"
metafile = true
[[files]]
file = "mods/puzzles-lib.pw.toml"
hash = "e91052ce0d9999e7a1a62ebbe9fe5f347a7cd0b0bfd3b4395ae33e5efa164773"
metafile = true
[[files]]
file = "mods/reeses-sodium-options.pw.toml"
hash = "09306b955060349986ccf6a6d2a67f1e019fa172a7dc690dd6007f81c966309a"
metafile = true
[[files]]
file = "mods/refined-storage-emi-integration.pw.toml"
hash = "4a5af515250fbde913593346d51b2c4ac8f660e71ba21f65f49a02209bb65509"
metafile = true
[[files]]
file = "mods/refined-storage.pw.toml"
hash = "dedc0295cba84c8241eeaa078838f333631dd6c67d693b3a0b8dd93cdfdae33d"
metafile = true
[[files]]
file = "mods/resource-pack-overrides.pw.toml"
hash = "b8101dca20d054277d99e75b43dd8db44dea64a761d2ab69fdda9f6d59d9e6f1"
metafile = true
[[files]]
file = "mods/resourceful-lib.pw.toml"
hash = "c0b066684294c33972d562e0cb08ea17b2d31bcf86cd1d6c63b8e968f0eac7a2"
metafile = true
[[files]]
file = "mods/respawning-animals.pw.toml"
hash = "c24f3a995c06808a047abf9b8b97fb1e09defcbab121755cb23f7316a6b0bb4c"
metafile = true
[[files]]
file = "mods/rrls.pw.toml"
hash = "b78044fd79ce21750d05dc61b6aede33c4b87cdc16a26f5aff472d4f73ab623c"
metafile = true
[[files]]
file = "mods/searchables.pw.toml"
hash = "ce9ddada6ffd55a512149cc474c6a453281a997a5fdf1b7b4a2cc73f875e704a"
metafile = true
[[files]]
file = "mods/silent-gear.pw.toml"
hash = "3ef17cc38cbe19ee924f20f3bfebb7c52bb2bb0db5e578e32473870bd1de67fa"
metafile = true
[[files]]
file = "mods/silent-lib.pw.toml"
hash = "0270a241e1d1c3c8e1ecda59aeaa5004771a57d46a4a470566cb7dd6d7e89c85"
metafile = true
[[files]]
file = "mods/simpletms-tms-and-trs-for-cobblemon.pw.toml"
hash = "f8ba20a06c8391dedc25741ab2817e1af2bfb9a182eee248471dae4b41eb7d77"
metafile = true
[[files]]
file = "mods/snow-real-magic.pw.toml"
hash = "a1deef8071fb83a13df63c289576813b82f0687eebda9b48b7667620d5d45103"
metafile = true
[[files]]
file = "mods/snow-under-trees.pw.toml"
hash = "82aedef3c457ffe51e27a4153aaf73ef7c073b87059a021495af487e41ed2e99"
metafile = true
[[files]]
file = "mods/sodium-dynamic-lights.pw.toml"
hash = "c1e526d3d17cb3563e596d45c7567ed1fe60c31bdc3457f7a747ef8150bd3c1b"
metafile = true
[[files]]
file = "mods/sodium-extra.pw.toml"
hash = "a1fb713df668ed21dda0602e99cb23b991c8faff841e130d7aae46258c3d6a45"
metafile = true
[[files]]
file = "mods/sodium-extras.pw.toml"
hash = "02b29315d2ec0d055d0ebba1f36879da5bcacc823328a668c7e7340fb2cb03d4"
metafile = true
[[files]]
file = "mods/sodium-options-api.pw.toml"
hash = "d193173f0f5dfb4a921d44e29e8d5cee656dd3a40872939069715bb482b66fc9"
metafile = true
[[files]]
file = "mods/sodium.pw.toml"
hash = "0281ed9713f3141bdac363056a791845510e14761ea65b4b2813cf96d664b20a"
metafile = true
[[files]]
file = "mods/sophisticated-backpacks.pw.toml"
hash = "b298b954e05956871a5f724569c226d97cc57059d8f587e5a768f3aa781de014"
metafile = true
[[files]]
file = "mods/sophisticated-core.pw.toml"
hash = "02c8e010cf0edca6b868cbf16dcc6e70bddea0d6d5451b1ff7fcc10056972adc"
metafile = true
[[files]]
file = "mods/sophisticated-storage-create-integration.pw.toml"
hash = "27ff0f0f6845e5ca640a1d0eaa70ec9af869e5794c18abf65546a23c9ceac765"
metafile = true
[[files]]
file = "mods/sophisticated-storage.pw.toml"
hash = "f21fbfc70bc381e1beb3d8e123ec7a9fe5290e70bc0b91fe53e26e2244c71ef0"
metafile = true
[[files]]
file = "mods/spice-of-life-onion.pw.toml"
hash = "bce75bb35eb4719687f61b217bb57d83ef0e63040c291354c1b2369f3c508680"
metafile = true
[[files]]
file = "mods/storage-delight.pw.toml"
hash = "0ccdb1ac3f7fffacc28ffa235bdf7e37105c088095f96b6e3fc9748d64328b94"
metafile = true
[[files]]
file = "mods/stylish-effects.pw.toml"
hash = "07208867dff9eb56eed1f370186187dab4ac0c1086d57ce5fd9ce07ea93a5320"
metafile = true
[[files]]
file = "mods/subtle-effects.pw.toml"
hash = "dc70a1f2d451deb0718c82d08bbdef00bb7b86fb4ecde86a2b63c87a27a6d7a0"
metafile = true
[[files]]
file = "mods/tax-free-levels.pw.toml"
hash = "dbfe27c8d868f5d934b882b8d26f3772fad78d97da191937d051a4f5c81a0b3b"
metafile = true
[[files]]
file = "mods/tectonic.pw.toml"
hash = "3601406f8001d9e5a1564e0bed50a4b92f75d435af37415e44900ae74273bb0e"
metafile = true
[[files]]
file = "mods/terralith.pw.toml"
hash = "da7ee99189ad35aac094919fe33cac647d98533b23c228f10d4811f08989e2b8"
metafile = true
[[files]]
file = "mods/tidal-towns.pw.toml"
hash = "0ccb1c696758d7935d02dd5160987cab8d3594ab24f89deb01c28f9e596094e7"
metafile = true
[[files]]
file = "mods/wall-jump-txf.pw.toml"
hash = "4ba37fc038fb52396b3557d6249d96c45acd8feec0355589032ea625d0a9edd6"
metafile = true
[[files]]
file = "mods/yacl.pw.toml"
hash = "aa9070a0e763f00f0d0b325e9bb980e53faabe9854bae25f2e542a7b81b7492e"
metafile = true
[[files]]
file = "mods/yeetus-experimentus.pw.toml"
hash = "5e25dccab496945a5f19bb82383e0ebf98452ffc14cd25c7e0259cc44312281a"
metafile = true
[[files]]
file = "mods/yungs-api.pw.toml"
hash = "d9c0c9209bac0b693af2a7c1e47ffb28d366bad19c91df31dc6d345fa75250a1"
metafile = true
[[files]]
file = "mods/yungs-better-desert-temples.pw.toml"
hash = "c2cb28859e7bcc0eb4ce59c5792f90fa5f8ab8865f5867ba26a5a345ec60b498"
metafile = true
[[files]]
file = "mods/yungs-better-dungeons.pw.toml"
hash = "1496ffa30020676979e65921b91e583a2fdc77e50001e2ed55f99edf1a0ae880"
metafile = true
[[files]]
file = "mods/yungs-better-end-island.pw.toml"
hash = "03d964cba45be30fbcbef52f89f3953185737f55c8934d12e199b67194c2b368"
metafile = true
[[files]]
file = "mods/yungs-better-jungle-temples.pw.toml"
hash = "c2dc09f214e7c1cecca6cf434776c2616f6d36bd27c06a6b39f985da0453a4f5"
metafile = true
[[files]]
file = "mods/yungs-better-mineshafts.pw.toml"
hash = "e839608ebc974e9e7819adc7bf29132ea86fc41b46af540fe0fa39b2007b2439"
metafile = true
[[files]]
file = "mods/yungs-better-nether-fortresses.pw.toml"
hash = "50c1731148d91a4ef05cfc47f759ca8fb3dad254e15ce4ecc5732d6fbfa68637"
metafile = true
[[files]]
file = "mods/yungs-better-ocean-monuments.pw.toml"
hash = "a2d7176026abf4a68a663b375811a603c508f36062cc7fb5b45416e1462682a8"
metafile = true
[[files]]
file = "mods/yungs-better-strongholds.pw.toml"
hash = "03084966c05070b5c4b17cd89234f3c0d813940a2d76b405a6699deac10a0ab8"
metafile = true
[[files]]
file = "mods/yungs-better-witch-huts.pw.toml"
hash = "9854b699d5a067c46ac99b1dedeeb529bff3fc18dd079e29b438c4cbf9725f68"
metafile = true
[[files]]
file = "mods/yungs-bridges.pw.toml"
hash = "c5e5f5aed37edd8073ec736e179b6159530ff497f4fe4c4dfaf954c810910433"
metafile = true
[[files]]
file = "mods/yungs-extras.pw.toml"
hash = "cfb7ce4c2b00cedd2e61f7d345b824d6bcc1471021668d2319ee2e4079108639"
metafile = true
[[files]]
file = "shaderpacks/complementary-reimagined.pw.toml"
hash = "3385e33cb8e2cbf402562dcb68669cc2905fec01b4be9fe27a516010c5aaff87"
metafile = true

View file

@ -1,13 +0,0 @@
name = "Aether Addon: Emissivity"
filename = "aether_emissivity-1.21.1-1.0.1-neoforge.jar"
side = "client"
[download]
url = "https://cdn.modrinth.com/data/sKHHynnb/versions/NgXUuFDh/aether_emissivity-1.21.1-1.0.1-neoforge.jar"
hash-format = "sha512"
hash = "51d68890596958d9dba32afce0f27b8d03fcfbaafec654c8604e847beef681adb895b85a349380997d448449c86d255825b0061290daf0db5fe890bc31016689"
[update]
[update.modrinth]
mod-id = "sKHHynnb"
version = "NgXUuFDh"

View file

@ -1,13 +0,0 @@
name = "Aether Addon: Enhanced Extinguishing"
filename = "aether_enhanced_extinguishing-1.21.1-1.0.0-neoforge.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/FDrEl7QY/versions/6O6CJTuj/aether_enhanced_extinguishing-1.21.1-1.0.0-neoforge.jar"
hash-format = "sha512"
hash = "006a921f1c52bd8aa0c195cda646ab6787c28bbc70ebed66d7d444838f464ba8be479c74b801deeb9aaf3ddf7350ea238fc73228af275a7112783d91c064d4b0"
[update]
[update.modrinth]
mod-id = "FDrEl7QY"
version = "6O6CJTuj"

View file

@ -1,13 +0,0 @@
name = "The Aether"
filename = "aether-1.21.1-1.5.8-neoforge.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/YhmgMVyu/versions/IOyjRzWX/aether-1.21.1-1.5.8-neoforge.jar"
hash-format = "sha512"
hash = "3343a8d87f49ae46eeae1aa657bcec8a6a6451110ffee18bc7d6e6b5df5a85c01a534c1bc01ef7f62648abc8a8ee408ac741891cd066804fa3fe6761b9532f7e"
[update]
[update.modrinth]
mod-id = "YhmgMVyu"
version = "IOyjRzWX"

View file

@ -1,13 +0,0 @@
name = "AmbientSounds"
filename = "AmbientSounds_NEOFORGE_v6.1.12_mc1.21.1.jar"
side = "client"
[download]
url = "https://cdn.modrinth.com/data/fM515JnW/versions/YY79wyx3/AmbientSounds_NEOFORGE_v6.1.12_mc1.21.1.jar"
hash-format = "sha512"
hash = "9af4fdae4d520924299baea52a56c2bb3ce6997d356ba7e74d5a48cf42ee6353583037fa3cbc36172c97cff806b321bbb8d9b8d4065465cfbf8d883b67c80340"
[update]
[update.modrinth]
mod-id = "fM515JnW"
version = "YY79wyx3"

View file

@ -1,13 +0,0 @@
name = "AppleSkin"
filename = "appleskin-neoforge-mc1.21-3.0.7.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/EsAfCjCV/versions/kztxpjAA/appleskin-neoforge-mc1.21-3.0.7.jar"
hash-format = "sha512"
hash = "03a94fe4143250b8e80abe97770918ba0af1265110bb73c25444674f9cdf86164464332a913cbac29af82d8ed02dce6ef19bbb62fced92620817a7ec1e761b71"
[update]
[update.modrinth]
mod-id = "EsAfCjCV"
version = "kztxpjAA"

View file

@ -1,13 +0,0 @@
name = "Architectury API"
filename = "architectury-13.0.8-neoforge.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/lhGA9TYQ/versions/ZxYGwlk0/architectury-13.0.8-neoforge.jar"
hash-format = "sha512"
hash = "65e3664953385d880320dd6bb818bcb96d361c07c53e2a7f65e64c6a47720ee26b233224ae9cad465ef0b2bbaefdaf30fb0175a983cecd91de058817d6fcf57e"
[update]
[update.modrinth]
mod-id = "lhGA9TYQ"
version = "ZxYGwlk0"

View file

@ -1,13 +0,0 @@
name = "Armourer's Workshop"
filename = "armourersworkshop-forge-1.21.1-3.2.3-beta.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/y4JF3gXL/versions/gWKk3ffs/armourersworkshop-forge-1.21.1-3.2.3-beta.jar"
hash-format = "sha512"
hash = "d36aa65096c3293db2b6f4056123f737823bd55eeaf86984e9bfd83d3ab33e68f0e38770962913530d4113ceebac6ebb53676c739975aac8a56f646eccd7fd0c"
[update]
[update.modrinth]
mod-id = "y4JF3gXL"
version = "gWKk3ffs"

View file

@ -1,13 +0,0 @@
name = "AttributeFix"
filename = "attributefix-neoforge-1.21.1-21.1.2.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/lOOpEntO/versions/a0oKmnPU/attributefix-neoforge-1.21.1-21.1.2.jar"
hash-format = "sha512"
hash = "bbfe003ed29a2bb0b4dd0a61d64b411748c9f5cb48ccbfd954dd2d813c452c540638749b6f19e7472e44ae1e966a3629b135190ffba3099cfdf45355618c0b97"
[update]
[update.modrinth]
mod-id = "lOOpEntO"
version = "a0oKmnPU"

View file

@ -1,13 +0,0 @@
name = "Bad Wither No Cookie - Reloaded"
filename = "bwncr-neoforge-1.21.1-3.20.3.jar"
side = "client"
[download]
url = "https://cdn.modrinth.com/data/lL2MtE37/versions/Vg54emzq/bwncr-neoforge-1.21.1-3.20.3.jar"
hash-format = "sha512"
hash = "db8419504e6ede1553b2e2d060fe4937c53898154c66475b726381155233a55daa0f52a530562dfc84202692e52614f8c585e89b1f8621f6bf799ce6d97c91ab"
[update]
[update.modrinth]
mod-id = "lL2MtE37"
version = "Vg54emzq"

View file

@ -1,13 +0,0 @@
name = "BadOptimizations"
filename = "BadOptimizations-2.3.0-1.21.1.jar"
side = "client"
[download]
url = "https://cdn.modrinth.com/data/g96Z4WVZ/versions/AQrpXs3f/BadOptimizations-2.3.0-1.21.1.jar"
hash-format = "sha512"
hash = "a9d78ab8d84d0ba3ebfd77eb3c4f585b9c3f0cad8e59b0ecef8c3ba107067342d7549f275c8fe45a5d4f35f7d848a15c184ad6e369044e659d17ce08afe91a77"
[update]
[update.modrinth]
mod-id = "g96Z4WVZ"
version = "AQrpXs3f"

View file

@ -1,13 +0,0 @@
name = "Balm"
filename = "balm-neoforge-1.21.1-21.0.49.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/MBAkmtvl/versions/EBGKvOX4/balm-neoforge-1.21.1-21.0.49.jar"
hash-format = "sha512"
hash = "d2b24857515d035b971ec2617d89b490c750bf0763f42c0eb2bc2b3172abc52dd16309bc460a55e4c57023843c5027b91913dcefcddf72cce17393b18537cf5c"
[update]
[update.modrinth]
mod-id = "MBAkmtvl"
version = "EBGKvOX4"

View file

@ -1,13 +0,0 @@
name = "Barbeque's Delight [Forge/NeoForge]"
filename = "barbequesdelight-1.2.2.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/rtu7uERF/versions/eS5ZBlEc/barbequesdelight-1.2.2.jar"
hash-format = "sha512"
hash = "5d0dfcf4892526043b0098ffff57beed2a21f06b7705ee95c470ec41f7f74bdde9111838675af2b51d4531d54cda54378fdb3fe654aef87ec705478555e13171"
[update]
[update.modrinth]
mod-id = "rtu7uERF"
version = "eS5ZBlEc"

View file

@ -1,13 +0,0 @@
name = "Berry Good"
filename = "berry_good-1.21.1-8.0.0.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/2WZWaKCl/versions/dJvvZvQk/berry_good-1.21.1-8.0.0.jar"
hash-format = "sha512"
hash = "0fea9a289d39e0d722a323d5777bede66d4850655377c923cd4cae2f864b0e994fc87b0e75df7bd50683f40f08f509a77f3107c9956169326a0c184d7f092970"
[update]
[update.modrinth]
mod-id = "2WZWaKCl"
version = "dJvvZvQk"

View file

@ -1,13 +0,0 @@
name = "Better Advancements"
filename = "BetterAdvancements-NeoForge-1.21.1-0.4.3.21.jar"
side = "client"
[download]
url = "https://cdn.modrinth.com/data/Q2OqKxDG/versions/FjTYILOi/BetterAdvancements-NeoForge-1.21.1-0.4.3.21.jar"
hash-format = "sha512"
hash = "b3deafd146dc3ea6e879fbc92f00de5797319a1aa620fb3b39be64c19271dab2efa07f824cda7139f8c22f2524ea413949357f5469bb6de584d1386de069c6b7"
[update]
[update.modrinth]
mod-id = "Q2OqKxDG"
version = "FjTYILOi"

View file

@ -1,13 +0,0 @@
name = "Better Animations Collection"
filename = "BetterAnimationsCollection-v21.1.0-1.21.1-NeoForge.jar"
side = "client"
[download]
url = "https://cdn.modrinth.com/data/OoOVj3J3/versions/tlFY9zBb/BetterAnimationsCollection-v21.1.0-1.21.1-NeoForge.jar"
hash-format = "sha512"
hash = "bd99f6040b076ff878e4aae3832e6c494fbaba5d0cb0b26d80c1874c7bd59c11e725705aaeb777d9f1bb821b516e15866600c665a4fba1eafa7a75cbf66307bf"
[update]
[update.modrinth]
mod-id = "OoOVj3J3"
version = "tlFY9zBb"

View file

@ -1,13 +0,0 @@
name = "Better Third Person"
filename = "BetterThirdPerson-neoforge-1.9.0.jar"
side = "client"
[download]
url = "https://cdn.modrinth.com/data/G1s2WpNo/versions/aG5y4JUQ/BetterThirdPerson-neoforge-1.9.0.jar"
hash-format = "sha512"
hash = "053acb813940aac69578810558056942966f2f0a24006a3a5de545c82888f8190b3bdc27a2401d0f70200dc4c927c0f84a45bf17821fb94fb9b886d633644619"
[update]
[update.modrinth]
mod-id = "G1s2WpNo"
version = "aG5y4JUQ"

View file

@ -1,13 +0,0 @@
name = "BetterF3"
filename = "BetterF3-11.0.3-NeoForge-1.21.1.jar"
side = "client"
[download]
url = "https://cdn.modrinth.com/data/8shC1gFX/versions/maXNB1dn/BetterF3-11.0.3-NeoForge-1.21.1.jar"
hash-format = "sha512"
hash = "59c36a882669399dce2110db42df05e8fe935a2e1194c4bac49abffc3129b2e19373dfe3c4a3d2f8b22f21b1d66ad8cbd653944ce0a71ae05d3d65528d1b7514"
[update]
[update.modrinth]
mod-id = "8shC1gFX"
version = "maXNB1dn"

View file

@ -1,13 +0,0 @@
name = "Blueprint"
filename = "blueprint-1.21.1-8.0.5.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/VsM5EDoI/versions/l2AucG5l/blueprint-1.21.1-8.0.5.jar"
hash-format = "sha512"
hash = "6a0801d078b233e4e909c9f9644236def1b29dd135bdf64a98c2688edebc0eef266a7b419911035726c565eee616090047e59601ece77033e38108a3e1bfd525"
[update]
[update.modrinth]
mod-id = "VsM5EDoI"
version = "l2AucG5l"

View file

@ -1,13 +0,0 @@
name = "Bookshelf"
filename = "bookshelf-neoforge-1.21.1-21.1.67.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/uy4Cnpcm/versions/9LiRySL2/bookshelf-neoforge-1.21.1-21.1.67.jar"
hash-format = "sha512"
hash = "98225af4ba0a8e10e30219b9a2bef39d0aa49425f5f09b6b2c7f627992458e39e30b686d03a8b8c4563eae44505ce94a7d21363700d77b21c1c4771409c34c63"
[update]
[update.modrinth]
mod-id = "uy4Cnpcm"
version = "9LiRySL2"

View file

@ -1,13 +0,0 @@
name = "Can't Sleep Clowns Will Eat Me"
filename = "cant_sleep_clowns_will_eat_me-1.21-2.0.0.0.jar"
side = "both"
[download]
hash-format = "sha1"
hash = "da6f8415bb3be1f1855b1bb2c617db27683d3dab"
mode = "metadata:curseforge"
[update]
[update.curseforge]
file-id = 5530236
project-id = 430957

View file

@ -1,13 +0,0 @@
name = "Cloth Config API"
filename = "cloth-config-15.0.140-neoforge.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/9s6osm5g/versions/izKINKFg/cloth-config-15.0.140-neoforge.jar"
hash-format = "sha512"
hash = "aaf9b010955b8cd294e5a92f069985b18729fd5e2cf22d351f1dff9680f15488688803ec41e77e941cbde130ceb535014ca4c868047d80ab69c2d508e216654d"
[update]
[update.modrinth]
mod-id = "9s6osm5g"
version = "izKINKFg"

View file

@ -1,13 +0,0 @@
name = "Clumps"
filename = "Clumps-neoforge-1.21.1-19.0.0.1.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/Wnxd13zP/versions/jo7lDoK4/Clumps-neoforge-1.21.1-19.0.0.1.jar"
hash-format = "sha512"
hash = "314d8d8e640d73041f27e0f3f2cad7aad8b4c77dbd7fb31700ef7760362261f77085eed5289555c725d99c3f47a114e7290cd608f39c9f0f12ef74958463bdcc"
[update]
[update.modrinth]
mod-id = "Wnxd13zP"
version = "jo7lDoK4"

View file

@ -1,13 +0,0 @@
name = "Cobblemon additions"
filename = "BCA-Datapack-3.8_CE_norm_M1.21.1_C1.6.1.zip"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/W2pr9jyL/versions/J1wqmyOI/BCA-Datapack-3.8_CE_norm_M1.21.1_C1.6.1.zip"
hash-format = "sha512"
hash = "65d97aeb98572b264badf9c19b7994853472910dc2b9da2c962525e6f635deae812b9bf7a7e1886fcdf5e72764271fa0543d9066bb43f71a60028507e6e4e35c"
[update]
[update.modrinth]
mod-id = "W2pr9jyL"
version = "J1wqmyOI"

View file

@ -1,13 +0,0 @@
name = "Cobblemon Counter"
filename = "cobblemon-counter-1.6-neoforge-1.5.1.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/rj8uLYP4/versions/4NXvB2jm/cobblemon-counter-1.6-neoforge-1.5.1.jar"
hash-format = "sha512"
hash = "ff49a9e9939ef3c84e941c4070ca923dfc439f5ffed0aad3530db5fc5b70b1c4be5751310cc61e7d61853e25ff2c8a039de5e5cf38827e24aadfd659e0d1b511"
[update]
[update.modrinth]
mod-id = "rj8uLYP4"
version = "4NXvB2jm"

View file

@ -1,13 +0,0 @@
name = "Cobblemon Fight or Flight Reborn"
filename = "fightorflight-neoforge-0.8.2.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/cTdIg5HZ/versions/9JSwVvPR/fightorflight-neoforge-0.8.2.jar"
hash-format = "sha512"
hash = "3024acdc80eb3e07a914d46ecfc85c9addf2775c6469a5f4d88922d9d0f2fe6599aa8b6c4137bad10bc38dc88c0351eb5d7173bdcbf29a5b238610a3fbf2f80c"
[update]
[update.modrinth]
mod-id = "cTdIg5HZ"
version = "9JSwVvPR"

View file

@ -1,13 +0,0 @@
name = "Cobblemon Integrations"
filename = "cobblemonintegrations-neoforge-1.21.1-1.1.3.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/NPCfuUI4/versions/slzdgomG/cobblemonintegrations-neoforge-1.21.1-1.1.3.jar"
hash-format = "sha512"
hash = "74a30fdd644638d2fc95bccb8b4c20cb6e5dd33309ccca8c155e30e8e77e8bf3670f6a79a86c4f9404b04bc3ce7b78428cf69a3fb15ba46e4a71a4ddd2f1f471"
[update]
[update.modrinth]
mod-id = "NPCfuUI4"
version = "slzdgomG"

View file

@ -1,13 +0,0 @@
name = "Cobblemon Pokenav"
filename = "cobblenav-neoforge-2.2.3.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/bI8Nt3uA/versions/O4yCDqfr/cobblenav-neoforge-2.2.3.jar"
hash-format = "sha512"
hash = "3d6d97246a4255c172e13542d551de91d4047934711405cf7717d55bce83dd476fb146318c793592c28331d01fd8887e3e67dda4071d6c34ad8576393401ed12"
[update]
[update.modrinth]
mod-id = "bI8Nt3uA"
version = "O4yCDqfr"

View file

@ -1,13 +0,0 @@
name = "Cobblemon Unchained"
filename = "cobblemon-unchained-1.6-neoforge-1.3.0.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/wh0wnzrT/versions/CtDdOnR5/cobblemon-unchained-1.6-neoforge-1.3.0.jar"
hash-format = "sha512"
hash = "765ea6cbac30b36ff2270bfe018e03f7c1e3def5aa148ecadeb77d22ae99da7e06dd02b415c2d227b4cc224dedd5cbd239e2a9cf616dea9dfb89142f216f1baa"
[update]
[update.modrinth]
mod-id = "wh0wnzrT"
version = "CtDdOnR5"

View file

@ -1,13 +0,0 @@
name = "Cobblemon"
filename = "Cobblemon-neoforge-1.6.1+1.21.1.jar"
side = "both"
[download]
url = "https://cdn.modrinth.com/data/MdwFAVRL/versions/eLcb8xod/Cobblemon-neoforge-1.6.1%2B1.21.1.jar"
hash-format = "sha512"
hash = "9d10fb7aec60c775ea050002b7f8f4390a5bed823a91e68ec088160ddf024860c7a7fb8aef3069b7619009a04335466702150eac6e1502f1c1051d88cb5b3897"
[update]
[update.modrinth]
mod-id = "MdwFAVRL"
version = "eLcb8xod"

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