❄️ Dotfiles for our NixOS system configuration.

Compare changes

Choose any two refs to compare.

+16
flake.lock
···
"type": "github"
}
},
"easy-hosts": {
"locked": {
"lastModified": 1755470564,
···
"root": {
"inputs": {
"catppuccin": "catppuccin",
"easy-hosts": "easy-hosts",
"flake-parts": "flake-parts",
"home-manager": "home-manager",
···
"type": "github"
}
},
+
"darwin-login-items": {
+
"locked": {
+
"lastModified": 1763528199,
+
"narHash": "sha256-8LQ5Wp3AJUp71Elax1R9lNkuEbO2Mrnpq+o8qpbhQyc=",
+
"owner": "uncenter",
+
"repo": "nix-darwin-login-items",
+
"rev": "ab75c315893ca206ddf9529e6e3aac6cb01b2f1a",
+
"type": "github"
+
},
+
"original": {
+
"owner": "uncenter",
+
"repo": "nix-darwin-login-items",
+
"type": "github"
+
}
+
},
"easy-hosts": {
"locked": {
"lastModified": 1755470564,
···
"root": {
"inputs": {
"catppuccin": "catppuccin",
+
"darwin-login-items": "darwin-login-items",
"easy-hosts": "easy-hosts",
"flake-parts": "flake-parts",
"home-manager": "home-manager",
+3 -5
flake.nix
···
url = "git+https://tangled.org/@tangled.org/core";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
-
inputs@{
-
flake-parts,
-
nixos-wsl,
-
...
-
}:
flake-parts.lib.mkFlake { inherit inputs; } {
imports = [
inputs.easy-hosts.flakeModule
···
url = "git+https://tangled.org/@tangled.org/core";
inputs.nixpkgs.follows = "nixpkgs";
};
+
+
darwin-login-items.url = "github:uncenter/nix-darwin-login-items";
};
outputs =
+
inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
imports = [
inputs.easy-hosts.flakeModule
+20
home/chloe/activation.nix
···
···
+
# macOS Tahoe (26.x) no longer supports symlinks in the Launchpad.
+
# So, we're forced to copy applications instead of linking them.
+
# This is computationally slower, but we're left with no choice.
+
+
{
+
pkgs,
+
lib,
+
...
+
}:
+
+
{
+
config = lib.mkIf pkgs.stdenv.hostPlatform.isDarwin {
+
targets.darwin.copyApps = {
+
enable = true;
+
enableChecks = true;
+
};
+
+
targets.darwin.linkApps.enable = false;
+
};
+
}
+28
home/chloe/autostart.nix
···
···
+
{
+
lib,
+
osConfig,
+
pkgs,
+
...
+
}:
+
+
{
+
config = lib.mkIf osConfig.settings.profiles.graphical.enable {
+
xdg.configFile = {
+
# .desktop files for autostart only work on Linux with XDG
+
"autostart/1password.desktop" = lib.mkIf pkgs.stdenv.hostPlatform.isLinux {
+
text = ''
+
[Desktop Entry]
+
Name=1Password
+
Exec=1password --silent %U
+
Terminal=false
+
Type=Application
+
Icon=1password
+
StartupWMClass=1Password
+
Comment=Password manager and secure wallet
+
MimeType=x-scheme-handler/onepassword;
+
Categories=Office;
+
'';
+
};
+
};
+
};
+
}
+3
home/chloe/default.nix
···
{
imports = [
./catppuccin.nix
./docs.nix
./files.nix
./packages
./programs
];
xdg.enable = true;
···
{
imports = [
+
./activation.nix
+
./autostart.nix
./catppuccin.nix
./docs.nix
./files.nix
./packages
./programs
+
./scripts.nix
];
xdg.enable = true;
-23
home/chloe/packages/autostart.nix
···
-
{ lib, osConfig, pkgs, ... }:
-
-
{
-
config = lib.mkIf osConfig.settings.profiles.graphical.enable {
-
xdg.configFile = {
-
# .desktop files for autostart only work on Linux with XDG
-
"autostart/1password.desktop" = lib.mkIf pkgs.stdenv.isLinux {
-
text = ''
-
[Desktop Entry]
-
Name=1Password
-
Exec=1password --silent %U
-
Terminal=false
-
Type=Application
-
Icon=1password
-
StartupWMClass=1Password
-
Comment=Password manager and secure wallet
-
MimeType=x-scheme-handler/onepassword;
-
Categories=Office;
-
'';
-
};
-
};
-
};
-
}
···
+14
home/chloe/packages/base.nix
···
···
+
{ pkgs }:
+
+
with pkgs;
+
[
+
# dev tools
+
nodejs
+
deno
+
cloudflared
+
corepack_latest
+
bun
+
+
# other
+
_1password-cli
+
]
+15
home/chloe/packages/darwin.nix
···
···
+
{
+
pkgs,
+
lib,
+
osConfig,
+
}:
+
+
let
+
packages = with pkgs; [
+
# tools
+
shottr
+
];
+
in
+
lib.optionals (
+
osConfig.settings.profiles.graphical.enable && pkgs.stdenv.hostPlatform.isDarwin
+
) packages
+7 -10
home/chloe/packages/default.nix
···
-
{
pkgs,
lib,
-
osConfig,
...
}:
let
-
defaultPackages = import ./list/default.nix { inherit pkgs; };
-
guiPackages = import ./list/gui.nix { inherit pkgs lib osConfig; };
in
{
-
imports = [
-
./autostart.nix
-
./scripts.nix
-
];
-
config = {
-
home.packages = defaultPackages ++ guiPackages;
};
}
···
+
{
pkgs,
lib,
+
osConfig,
...
}:
let
+
basePackages = import ./base.nix { inherit pkgs; };
+
darwinPackages = import ./darwin.nix { inherit pkgs lib osConfig; };
+
linuxPackages = import ./linux.nix { inherit pkgs lib osConfig; };
+
universalPackages = import ./universal.nix { inherit pkgs lib osConfig; };
in
{
config = {
+
home.packages = basePackages ++ darwinPackages ++ linuxPackages ++ universalPackages;
};
}
+32
home/chloe/packages/linux.nix
···
···
+
{
+
pkgs,
+
lib,
+
osConfig,
+
}:
+
+
let
+
packages = with pkgs; [
+
# messengers
+
telegram-desktop
+
vesktop
+
+
# dev tools
+
httpie-desktop
+
+
# mail
+
thunderbird
+
+
# games
+
xivlauncher
+
+
# messengers
+
discord
+
+
# other GUI apps
+
obs-studio
+
_1password-gui
+
];
+
in
+
lib.optionals (
+
osConfig.settings.profiles.graphical.enable && pkgs.stdenv.hostPlatform.isLinux
+
) packages
-19
home/chloe/packages/list/default.nix
···
-
{ pkgs }:
-
-
with pkgs; [
-
# dev tools
-
nodejs
-
deno
-
cloudflared
-
corepack_latest
-
bun
-
-
# fonts
-
iosevka
-
inter
-
atkinson-hyperlegible
-
nerd-fonts.jetbrains-mono
-
-
# other
-
_1password-cli
-
]
···
-31
home/chloe/packages/list/gui.nix
···
-
{ pkgs, lib, osConfig }:
-
-
let
-
# Common GUI packages available on all platforms
-
commonPackages = with pkgs; [
-
# cloud
-
owncloud-client
-
-
# messengers
-
telegram-desktop
-
vesktop
-
-
# notes
-
obsidian
-
-
# dev tools
-
zed-editor
-
httpie-desktop
-
-
# mail
-
thunderbird
-
-
# games
-
prismlauncher
-
xivlauncher
-
-
# other GUI apps
-
obs-studio
-
_1password-gui
-
];
-
in lib.optionals osConfig.settings.profiles.graphical.enable commonPackages
···
-15
home/chloe/packages/scripts.nix
···
-
{ pkgs, ... }:
-
-
{
-
home.packages = with pkgs; [
-
# Convert nix hash to SRI format and fetch from URL
-
(writeShellScriptBin "shash" ''
-
nix hash to-sri --type sha256 $(nix-prefetch-url ''$1)
-
'')
-
-
# Create a Python virtual environment with --copies flag
-
(writeShellScriptBin "create-venv" ''
-
nix run nixpkgs#python3 -- -m venv .venv --copies
-
'')
-
];
-
}
···
+25
home/chloe/packages/universal.nix
···
···
+
{
+
pkgs,
+
lib,
+
osConfig,
+
}:
+
+
let
+
packages = with pkgs; [
+
# dev tools
+
zed-editor
+
+
# fonts
+
iosevka
+
inter
+
atkinson-hyperlegible
+
nerd-fonts.jetbrains-mono
+
+
# games
+
prismlauncher
+
+
# notes
+
obsidian
+
];
+
in
+
lib.optionals osConfig.settings.profiles.graphical.enable packages
+12 -8
home/chloe/programs/cli/zsh.nix
···
-
{ lib, pkgs, osConfig, config, ... }:
{
programs.zsh = {
···
'';
envExtra = ''
-
${lib.optionalString pkgs.stdenv.isLinux ''
-
export PRISMA_SCHEMA_ENGINE_BINARY="${pkgs.prisma-engines}/bin/schema-engine"
-
export PRISMA_QUERY_ENGINE_BINARY="${pkgs.prisma-engines}/bin/query-engine"
-
export PRISMA_QUERY_ENGINE_LIBRARY="${pkgs.prisma-engines}/lib/libquery_engine.node"
-
export PRISMA_INTROSPECTION_ENGINE_BINARY="${pkgs.prisma-engines}/bin/introspection-engine"
-
export PRISMA_FMT_BINARY="${pkgs.prisma-engines}/bin/prisma-fmt"
-
''}
'';
shellAliases = lib.mkMerge [
···
+
{
+
lib,
+
pkgs,
+
osConfig,
+
config,
+
...
+
}:
{
programs.zsh = {
···
'';
envExtra = ''
+
export PRISMA_SCHEMA_ENGINE_BINARY="${pkgs.prisma-engines}/bin/schema-engine"
+
export PRISMA_QUERY_ENGINE_BINARY="${pkgs.prisma-engines}/bin/query-engine"
+
export PRISMA_QUERY_ENGINE_LIBRARY="${pkgs.prisma-engines}/lib/libquery_engine.node"
+
export PRISMA_INTROSPECTION_ENGINE_BINARY="${pkgs.prisma-engines}/bin/introspection-engine"
+
export PRISMA_FMT_BINARY="${pkgs.prisma-engines}/bin/prisma-fmt"
'';
shellAliases = lib.mkMerge [
+15
home/chloe/scripts.nix
···
···
+
{ pkgs, ... }:
+
+
{
+
home.packages = with pkgs; [
+
# Convert nix hash to SRI format and fetch from URL
+
(writeShellScriptBin "shash" ''
+
nix hash to-sri --type sha256 $(nix-prefetch-url ''$1)
+
'')
+
+
# Create a Python virtual environment with --copies flag
+
(writeShellScriptBin "create-venv" ''
+
nix run nixpkgs#python3 -- -m venv .venv --copies
+
'')
+
];
+
}
+6
hosts/juniper/default.nix
···
{
system.stateVersion = 6; # Initial nix-darwin version
}
···
{
+
settings = {
+
profiles = {
+
graphical.enable = true;
+
laptop.enable = true;
+
};
+
};
system.stateVersion = 6; # Initial nix-darwin version
}
+7 -1
modules/base/default.nix
···
{
imports = [
../shared
-
./nix.nix
./packages.nix
./zsh.nix
];
}
···
{
imports = [
../shared
+
./nix.nix
./packages.nix
./zsh.nix
+
];
+
+
age.identityPaths = [
+
"/home/chloe/.ssh/id_ed25519.age"
+
"/home/chloe/.ssh/id_ed25519.auth"
+
"/home/chloe/.ssh/id_ed25519.sign"
];
}
+1
modules/base/packages.nix
···
jq
nixfmt
just
];
}
···
jq
nixfmt
just
+
ragenix
];
}
+1
modules/darwin/default.nix
···
./packages.nix
./preferences
./security
./users.nix
];
}
···
./packages.nix
./preferences
./security
+
./startup.nix
./users.nix
];
}
+2
modules/darwin/extras.nix
···
{
imports = [
inputs.home-manager.darwinModules.home-manager
];
}
···
{
imports = [
inputs.home-manager.darwinModules.home-manager
+
inputs.ragenix.darwinModules.default
+
inputs.darwin-login-items.darwinModules.default
];
}
+8 -1
modules/darwin/homebrew.nix
···
# Casks (GUI applications)
casks = [
"1password"
"maccy"
"microsoft-teams"
"music-presence"
-
"prismlauncher"
];
# Mac App Store apps (requires mas-cli)
···
# Casks (GUI applications)
casks = [
"1password"
+
"bruno"
+
"crossover"
+
"discord"
"maccy"
+
"microsoft-edge"
"microsoft-teams"
+
"mos"
"music-presence"
+
"osu"
+
"signal"
+
"steam"
];
# Mac App Store apps (requires mas-cli)
+19
modules/darwin/startup.nix
···
···
+
{
+
config,
+
pkgs,
+
lib,
+
...
+
}:
+
+
{
+
environment.loginItems = {
+
enable = true;
+
items = [
+
"/Applications/1Password.app"
+
"/Applications/Mos.app"
+
"/Applications/Maccy.app"
+
"/Users/chloe/Applications/Home Manager Apps/Shottr.app"
+
"/Applications/Tailscale.app"
+
];
+
};
+
}
-6
modules/nixos/extras.nix
···
inputs.ragenix.nixosModules.default
inputs.tangled.nixosModules.knot
];
-
-
age.identityPaths = [
-
"/home/chloe/.ssh/id_ed25519.age"
-
"/home/chloe/.ssh/id_ed25519.auth"
-
"/home/chloe/.ssh/id_ed25519.sign"
-
];
}
···
inputs.ragenix.nixosModules.default
inputs.tangled.nixosModules.knot
];
}
-1
modules/nixos/packages.nix
···
{
environment.systemPackages = with pkgs; [
gparted
-
ragenix
];
}
···
{
environment.systemPackages = with pkgs; [
gparted
];
}
+14
secrets/abuseipdb.age
···
···
+
-----BEGIN AGE ENCRYPTED FILE-----
+
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGN2U2dKQSA5eHJO
+
L0V2dE1tem9DSy92ZnBweG55YjRmVjdrM3FPN24vZUE0K1E2Smx3ClFiQXpiUCt6
+
N0JBTm0yZWI5Q3lYTjVlWXYzTkZBTFpLY0dqSE5INmlITDgKLT4gc3NoLWVkMjU1
+
MTkgMUNUOTd3IFYxRXhpWS9tQnl0SEc5RGo2Ymhyb3Jqa0dVN0twcEplK3lLR0tl
+
SkJBeWsKMFYya3FtRTg0Um9mRnNWbElMWkdMNEE5RlExTkNQaUpGRUEzK2pOdzc0
+
RQotPiBzc2gtZWQyNTUxOSBlUDNUdFEgVDRrNUNSWXl5bWlXWFEvdmpVZWtsRTJr
+
WmU3OWhkTFlVeUlpTW1vekxHbwpxOFJPQTFsN2RQUmY0YzRUVlBBR0JiL0FzbHZy
+
b2pqQzNJd1ZSSElYVHFrCi0+ICctZ3JlYXNlICFofCAlKytiRHFHClZvUQotLS0g
+
Mi9YUUNMK0pxc1dOSmt1VWxjcG8yUTl4VmQxT3RKQm9JZUoxK1ZyUUZ0Ywr+XbIf
+
30MbeXvOVwJSQMLcc/m9044pc1UXNcO6aLrbShVeVMs95zg18luAiE0V4ML7Ldrs
+
LTduxwbz/k3Tr+H4AKTCocXsilVmGhqaEZ6ST0aEjMHvuXFiXSHu8upnBybYrDfy
+
pp+sJnTp4/SfL9y+WA==
+
-----END AGE ENCRYPTED FILE-----
+14 -13
secrets/caddy.age
···
-----BEGIN AGE ENCRYPTED FILE-----
-
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGN2U2dKQSA3OTRw
-
N0pLbkIxZ2lvVEkyb0o4RGdZVVEzQ0VtSlp5MVJPMWNPd0xoc0JrCmRRMWR5N2hs
-
dFRwUVQ3RCtIb2hKT2ZvVlU4UUFBM0dkWmZ0U0lCUERZMUEKLT4gc3NoLWVkMjU1
-
MTkgMUNUOTd3IERweUM3ekJqTXBNTVYxU3p1OVhwZGxGdDl5bGNLRHBzbW12OHdN
-
NlpMRmsKendPWjFTZFFmMnpSWWNsa0NDM2kvTTNKYk42M01uS1BpazJzSGErUnE5
-
MAotPiBzc2gtZWQyNTUxOSBlUDNUdFEgb3prY01iWjBlS1pLSnZnTFJsOXM1NkVL
-
S3hJZTJ5V21lNFBDN21RTFN5VQpCa1lVNStyVWl5TS9Lcm9ZZHArV3ExWmViVzNv
-
cm9zNUdzN2ZjcVFkTjFrCi0+IDF3VVg2LWdyZWFzZSAvLUNTTzp0NSAyRUFrdSBp
-
UH0xIEgKWDJxU2tkQ20wZ29aQzlEWlh4RTQzaHBFV0tUb1hNUVg3aENOT0NxY0Z4
-
eEtpd3Nwb3BBYmU0a0JKZFBFOXFJYgpKMVNPcTIxNnhUelRmNmFqVGNsQVV6NTRO
-
d2cKLS0tIDYrQ2tWRWhHcTFGUnVNRU9kaWJCRkRSTXB5YTJvVnpwZ2ttLzhubGg1
-
N2sKd+UAtI0xQxss7MdlaHSRhAK6sOhBJ84PmnjDYDBhf4Xg2ON/EmD5AmUfcmcD
-
yEfwQSp1/vvUCtW1CXzEUswCRUzzhufms8GSLC7ehRAbhImhIK0lCKU=
-----END AGE ENCRYPTED FILE-----
···
-----BEGIN AGE ENCRYPTED FILE-----
+
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGN2U2dKQSA0THNq
+
UjBzRllITWxyM0tWckVhU0p3SE4zNWhYdmVuUUVyREtLOGxwbFRJCkh3QXFOUW9p
+
ZWxSUS9LTzJsMFdyK05jOXVBVzI2VUptTFFManZTQnRMUmsKLT4gc3NoLWVkMjU1
+
MTkgMUNUOTd3IEdKUHhlMG1hakFJcThMdjVWMk5WZ3RzYUZnbE5RdXJycmk3TW9H
+
TDF5bEUKTTIrbVh6UTZHYnhmV21sUFpSOGYwT3FCT1RacjRscUVabFpYWmh4U3Ft
+
cwotPiBzc2gtZWQyNTUxOSBlUDNUdFEgTXhVUGFrcExTV05qb2ZuWDkzd0hYSCtl
+
NjZLaWxFdi9ibmtUcDVzWE5DSQpSVTVvUlpBN2REZ2ZSMkRkWWlNWkFaekxROVVt
+
bEc4NXBkdVpWTkF0OENVCi0+IEw5e0xlLWdyZWFzZSBUIHdHZHk5IgpwVERQMzZG
+
ODgwUExoa082aFp2K0NvYmhtT1lvd2MzMlEycGc4YjlwVGNPcy9nS0FQV3lZTkpr
+
SkkzdXlWdllBCnBGODRBRU9vQnB4THdBQzZycGdMS05mN3R4VWZvaUpnbWZ5M3Zu
+
blN1cys1VGc4R09TdHdHaDhEQVF3Ci0tLSBYWmNaYkZiSzVQUXp0OU14WmY4cGNR
+
Mk1VZ1NxOWV3RUxVb0taRWRxZTFrCvVGqFlzVEsdUx95OQNCI1lmXdAXY5SxwJ6q
+
PI7Cj+zq+5BjUYqwPnu2KcMMKEtmBReSyL791bRn16PSzIS3yn2DzmIM72oea9hd
+
WJCud87qCxejIF3LgczKUqpTILzXtM7jzR2CNhoBSc9VDDA3U+ku4jOnxfj6
-----END AGE ENCRYPTED FILE-----
+1
secrets/secrets.nix
···
];
in
{
"bluesky-pds.age".publicKeys = keys;
"caddy.age".publicKeys = keys;
"destiny-labeler.age".publicKeys = keys;
···
];
in
{
+
"abuseipdb.age".publicKeys = keys;
"bluesky-pds.age".publicKeys = keys;
"caddy.age".publicKeys = keys;
"destiny-labeler.age".publicKeys = keys;
+53 -15
services/adguardhome/default.nix
···
{
services.adguardhome = {
enable = true;
host = "127.0.0.1";
port = 3000;
settings = {
dns = {
-
port = 53;
-
port_tls = 853;
-
port_https = 8443;
-
port_quic = 784;
};
-
};
-
};
-
settings.firewall = {
-
allowedTCPPorts = [
-
8443
-
3000
-
];
-
allowedUDPPorts = [
-
53
-
784
-
];
};
}
···
{
services.adguardhome = {
enable = true;
+
+
# We'll be using Tailscale Services to serve AdGuard Home as a subdomain in our tailnet.
host = "127.0.0.1";
port = 3000;
+
settings = {
+
# AdGuard Home keeps insisting on forcing the Russian language on me for some reason.
+
language = "en";
+
+
# Use the auto theme so it matches the user's system theme.
+
theme = "auto";
+
dns = {
+
# Since we're not exposing AdGuard Home to the public internet, we can disable rate limiting.
+
ratelimit = 0;
+
+
bootstrap_dns = [
+
"9.9.9.9" # Quad9
+
"1.1.1.1" # Cloudflare
+
];
+
+
upstream_dns = [
+
"https://dns10.quad9.net/dns-query"
+
"https://doh.mullvad.net/dns-query"
+
"https://security.cloudflare-dns.com/dns-query"
+
+
# Resolve DNS queries for 100.* IPs with Tailscale's Magic DNS.
+
"[/100.in-addr.arpa/]100.100.100.100"
+
];
+
+
allowed_clients = [
+
"100.64.0.0/10" # Tailscale IP range
+
"127.0.0.1/24" # localhost
+
];
};
+
clients.persistent = [
+
{
+
name = "macOS";
+
ids = [ "100.118.131.42" ];
+
}
+
{
+
name = "Android";
+
ids = [ "100.113.214.72" ];
+
}
+
{
+
name = "UpCloud";
+
ids = [ "100.96.154.98" ];
+
}
+
{
+
name = "Windows";
+
ids = [ "100.124.27.32" ];
+
}
+
{
+
name = "Windows Subsystem for Linux";
+
ids = [ "100.127.166.121" ];
+
}
+
];
+
};
};
}
+45 -2
services/bluesky-pds/default.nix
···
serverAliases = [ "*.pds.sappho.systems" ];
extraConfig = ''
import common
-
import tls_cloudflare
-
reverse_proxy http://127.0.0.1:3333
'';
};
}
···
serverAliases = [ "*.pds.sappho.systems" ];
extraConfig = ''
import common
+
import tls_bunny
+
+
handle / {
+
respond <<EOF
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⡀⢀⠀⢀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⢧⢳⣾⣂⣾⡁⣴⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⠓⣬⠾⠋⠉⢸⣷⣋⣤⡦
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢯⡞⠃⠀⠀⠀⡼⠊⣠⡏⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣶⣄⣀⣀⡴⣡⡴⠃⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⣽⠧⢴⡀⢸⡛⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡿⠻⠛⠋⠉⠁⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠾⡇⢸⠉⠀⠀⠹⣼⣽⡚⠉⠹⡄⠀⠀⠀⠀⠀⠀⠀⠀⡴⠛⠙⢳⡀⠀⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡘⢿⣆⠀⠀⠀⣿⠉⠀⠀⢀⣧⠀⢀⣤⠤⢤⣄⠀⢸⠉⠀⠀⠈⣧⢸⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠾⠉⠙⠻⢗⠀⣦⣏⣤⠀⣠⠞⠀⠀⣞⠋⠀⠀⠈⠻⡼⡀⣄⡀⢀⡯⠟⠧⢤⡀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢯⣀⠀⠀⠲⣷⡿⣿⠋⠉⠁⠀⠀⠀⠹⣄⠀⠀⣠⣤⠘⠧⣽⣨⣞⡁⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠁⠀⢸⣷⠀⠀⠀⠀⠀⠀⠈⣓⣦⣜⠉⣿⣶⠧⣟⣙⣀⣀⣠⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⠀⠀⠀⠀⠀⠀⡞⠉⠀⠾⠞⡵⢜⣇⡀⠙⢦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡤⠤⠤⣄⠀⣼⣿⡠⠴⠶⠦⣄⠀⣷⣀⠀⢀⣠⡷⡀⠉⠁⠀⢸⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⡋⠀⠀⠀⠈⢷⣿⠋⠀⠀⠀⠀⢸⡆⠀⠈⠉⣩⣿⠣⠙⠲⠤⠶⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⢻⠀⠀⠀⠀⠀⠀⡇⣠⣴⣾⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣄⠀⠀⠀⢠⣦⣌⡄⢠⣦⠀⠀⣾⠹⠋⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⠤⠙⠢⢄⡀⠀⢳⡄⢃⡼⠉⣀⠜⠋⠉⠒⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⢠⡯⠀⠀⠀⠀⠀⣠⡉⠙⣹⣿⠶⣮⣁⡀⠀⠀⠀⠀⢻⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠳⣄⠀⠀⠀⠘⠛⠓⣛⠇⢸⡷⡀⠻⠟⠀⠀⠀⠀⢸⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣹⠒⠶⢶⠒⠉⠀⠀⠈⠉⢫⠳⢄⣀⣀⣀⡤⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⡾⠋⠀⠀⢰⠀⠀⠀⠀⠀⠀⢸⣇⠀⠈⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⣠⣴⣾⠿⠛⠁⠀⠀⠀⠀⠈⠳⣄⣀⣀⣀⡤⠞⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⣴⣿⡿⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⣾⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⢸⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠈⢧⡃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
+
Welcome to Sapphic Angels' AT Protocol Personal Data Server (PDS).
+
+
Website: https://sapphic.moe
+
PDS: https://github.com/bluesky-social/pds
+
ATProto: https://atproto.com
+
+
🌸 🐇
+
EOF 200
+
}
+
+
handle {
+
reverse_proxy http://127.0.0.1:3333
+
}
'';
};
}
+12 -5
services/caddy/default.nix
···
services.caddy = {
enable = true;
package = pkgs.caddy.withPlugins {
-
plugins = [ "github.com/caddy-dns/cloudflare@v0.2.1" ];
-
hash = "sha256-iRzpN9awuEFsc7hqKzOMNiCFFEv833xhd4LM+VFQedI=";
};
environmentFile = config.age.secrets.caddy.path;
globalConfig = ''
email chloe@sapphic.moe
'';
extraConfig = ''
-
(tls_cloudflare) {
tls {
-
dns cloudflare {env.CF_API_TOKEN}
-
resolvers 8.8.8.8 1.1.1.1
}
}
(common) {
encode zstd gzip
}
'';
};
···
services.caddy = {
enable = true;
package = pkgs.caddy.withPlugins {
+
plugins = [
+
"github.com/caddy-dns/bunny@v1.2.0"
+
];
+
hash = "sha256-bwffi5sWq07DVoPQGgEIN1jnvQKL6c4tFfR9AT9ThD4=";
};
environmentFile = config.age.secrets.caddy.path;
globalConfig = ''
+
debug
email chloe@sapphic.moe
'';
extraConfig = ''
+
(tls_bunny) {
tls {
+
dns bunny {env.BUNNY_API_KEY}
+
resolvers 9.9.9.9 149.112.112.112
}
}
(common) {
encode zstd gzip
}
+
'';
+
logFormat = ''
+
level debug
+
format json
'';
};
+1
services/default.nix
···
./bluesky-pds
./caddy
./destiny-labeler
./glance
./knot
./lanyard
···
./bluesky-pds
./caddy
./destiny-labeler
+
./fail2ban
./glance
./knot
./lanyard
+1 -1
services/destiny-labeler/default.nix
···
services.caddy.virtualHosts."labeler.sappho.systems" = {
extraConfig = ''
import common
-
import tls_cloudflare
reverse_proxy http://127.0.0.1:4002
'';
};
···
services.caddy.virtualHosts."labeler.sappho.systems" = {
extraConfig = ''
import common
+
import tls_bunny
reverse_proxy http://127.0.0.1:4002
'';
};
+123
services/fail2ban/default.nix
···
···
+
{ pkgs, ... }:
+
+
{
+
age.secrets.abuseipdb = {
+
file = ../../secrets/abuseipdb.age;
+
mode = "600";
+
};
+
+
services.fail2ban = {
+
enable = true;
+
+
# Include curl for making HTTP requests to AbuseIPDB
+
extraPackages = [ pkgs.curl ];
+
+
# Globally applicable settings for all jails
+
daemonSettings = {
+
Definition = {
+
# How long to keep an IP in the ban list (in seconds)
+
# 1 day = 86400 seconds
+
bantime = "86400";
+
+
# How far back to look for failures (in seconds)
+
# 1 hour = 3600 seconds
+
findtime = "3600";
+
+
# Number of failures before banning
+
maxretry = 3;
+
+
# Allow fail2ban to write to syslog
+
logtarget = "SYSLOG";
+
};
+
};
+
+
# Ignore local networks and trusted services
+
ignoreIP = [
+
"127.0.0.1/8"
+
"::1"
+
"100.64.0.0/10" # Tailscale IP range
+
];
+
+
# Jails for protecting various services
+
jails = {
+
# SSH protection - monitor failed login attempts using systemd journal
+
sshd.settings = {
+
enabled = true;
+
port = "ssh";
+
filter = "sshd";
+
backend = "systemd";
+
maxretry = 5;
+
findtime = "3600";
+
bantime = "86400";
+
action = "iptables-multiport[name=SSH, port='ssh'] abuseipdb-agenix[abuseipdb_category='18,22']";
+
};
+
+
# Caddy HTTP/HTTPS protection - monitor for repeated 4xx/5xx errors
+
caddy-http.settings = {
+
enabled = true;
+
port = "http,https";
+
filter = "caddy-http";
+
logpath = "/var/log/caddy/access-*.log";
+
backend = "auto";
+
maxretry = 10;
+
findtime = "600";
+
bantime = "3600";
+
action = "iptables-multiport[name=Caddy, port='http,https'] abuseipdb-agenix[abuseipdb_category='21']";
+
};
+
+
# Rate-based protection - ban on excessive requests
+
caddy-ratelimit.settings = {
+
enabled = true;
+
port = "http,https";
+
filter = "caddy-ratelimit";
+
logpath = "/var/log/caddy/access-*.log";
+
backend = "auto";
+
maxretry = 50;
+
findtime = "60";
+
bantime = "1800";
+
action = "iptables-multiport[name=Caddy-RateLimit, port='http,https'] abuseipdb-agenix[abuseipdb_category='21']";
+
};
+
};
+
};
+
+
# Custom filters for Fail2Ban
+
environment.etc = {
+
# Caddy HTTP error monitoring filter
+
"fail2ban/filter.d/caddy-http.conf".text = ''
+
[Definition]
+
failregex = ^<HOST> -.*" (?:400|401|403|404|405|429|500|502|503|504) .*$
+
ignoreregex =
+
'';
+
+
# Caddy rate limiting filter - detects repeated requests within short timeframe
+
"fail2ban/filter.d/caddy-ratelimit.conf".text = ''
+
[Definition]
+
failregex = ^<HOST> -.*" \d{3} .*$
+
ignoreregex =
+
'';
+
+
# Custom abuseipdb action that reads API key from file
+
"fail2ban/action.d/abuseipdb-agenix.conf".text = ''
+
[Definition]
+
# Report IP to AbuseIPDB, reading API key from Agenix secret file
+
# Uses double quotes to allow shell expansion of $(cat /run/agenix/abuseipdb)
+
# Sleep 12 seconds to respect AbuseIPDB rate limit (~5 requests per minute)
+
# Note: Don't use <matches> - fail2ban's wrapper causes issues with multiline content
+
# Don't use --fail so 429 rate limit errors don't mark action as failed
+
actionban = sleep 12; curl 'https://api.abuseipdb.com/api/v2/report' -H 'Accept: application/json' -H "Key: $(cat /run/agenix/abuseipdb)" --data-urlencode 'ip=<ip>' --data 'categories=<abuseipdb_category>' > /dev/null 2>&1
+
+
actionstart =
+
actionstop =
+
actioncheck =
+
actionunban =
+
+
[Init]
+
abuseipdb_category = 18
+
'';
+
};
+
+
# Ensure the log directory exists
+
systemd.tmpfiles.rules = [
+
"d /var/log/fail2ban 0755 root root -"
+
];
+
}
+1 -2
services/glance/default.nix
···
services.glance = {
enable = true;
-
openFirewall = true;
environmentFile = config.age.secrets.glance.path;
settings = import ./settings.nix;
};
···
services.caddy.virtualHosts."home.sappho.systems" = {
extraConfig = ''
import common
-
import tls_cloudflare
reverse_proxy http://localhost:4040
'';
};
···
services.glance = {
enable = true;
+
openFirewall = false;
environmentFile = config.age.secrets.glance.path;
settings = import ./settings.nix;
};
···
services.caddy.virtualHosts."home.sappho.systems" = {
extraConfig = ''
import common
reverse_proxy http://localhost:4040
'';
};
+31 -3
services/knot/default.nix
···
services.tangled-knot = {
enable = true;
motd = ''
-
🌸 welcome to the tangled knot server 🌸
-
hosted by sapphic angels
'';
server = {
hostname = "knot.sappho.systems";
···
services.caddy.virtualHosts."knot.sappho.systems" = {
extraConfig = ''
import common
-
import tls_cloudflare
reverse_proxy http://127.0.0.1:5555
'';
};
}
···
services.tangled-knot = {
enable = true;
motd = ''
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⡀⢀⠀⢀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⢧⢳⣾⣂⣾⡁⣴⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⠓⣬⠾⠋⠉⢸⣷⣋⣤⡦
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢯⡞⠃⠀⠀⠀⡼⠊⣠⡏⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣶⣄⣀⣀⡴⣡⡴⠃⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⣽⠧⢴⡀⢸⡛⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡿⠻⠛⠋⠉⠁⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠾⡇⢸⠉⠀⠀⠹⣼⣽⡚⠉⠹⡄⠀⠀⠀⠀⠀⠀⠀⠀⡴⠛⠙⢳⡀⠀⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡘⢿⣆⠀⠀⠀⣿⠉⠀⠀⢀⣧⠀⢀⣤⠤⢤⣄⠀⢸⠉⠀⠀⠈⣧⢸⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠾⠉⠙⠻⢗⠀⣦⣏⣤⠀⣠⠞⠀⠀⣞⠋⠀⠀⠈⠻⡼⡀⣄⡀⢀⡯⠟⠧⢤⡀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢯⣀⠀⠀⠲⣷⡿⣿⠋⠉⠁⠀⠀⠀⠹⣄⠀⠀⣠⣤⠘⠧⣽⣨⣞⡁⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠁⠀⢸⣷⠀⠀⠀⠀⠀⠀⠈⣓⣦⣜⠉⣿⣶⠧⣟⣙⣀⣀⣠⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⠀⠀⠀⠀⠀⠀⡞⠉⠀⠾⠞⡵⢜⣇⡀⠙⢦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡤⠤⠤⣄⠀⣼⣿⡠⠴⠶⠦⣄⠀⣷⣀⠀⢀⣠⡷⡀⠉⠁⠀⢸⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⡋⠀⠀⠀⠈⢷⣿⠋⠀⠀⠀⠀⢸⡆⠀⠈⠉⣩⣿⠣⠙⠲⠤⠶⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⢻⠀⠀⠀⠀⠀⠀⡇⣠⣴⣾⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣄⠀⠀⠀⢠⣦⣌⡄⢠⣦⠀⠀⣾⠹⠋⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⠤⠙⠢⢄⡀⠀⢳⡄⢃⡼⠉⣀⠜⠋⠉⠒⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⢠⡯⠀⠀⠀⠀⠀⣠⡉⠙⣹⣿⠶⣮⣁⡀⠀⠀⠀⠀⢻⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠳⣄⠀⠀⠀⠘⠛⠓⣛⠇⢸⡷⡀⠻⠟⠀⠀⠀⠀⢸⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣹⠒⠶⢶⠒⠉⠀⠀⠈⠉⢫⠳⢄⣀⣀⣀⡤⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⡾⠋⠀⠀⢰⠀⠀⠀⠀⠀⠀⢸⣇⠀⠈⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⣠⣴⣾⠿⠛⠁⠀⠀⠀⠀⠈⠳⣄⣀⣀⣀⡤⠞⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⣴⣿⡿⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⣾⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⢸⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠈⢧⡃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
Welcome to Sapphic Angels' Knot server for tangled.org.
'';
server = {
hostname = "knot.sappho.systems";
···
services.caddy.virtualHosts."knot.sappho.systems" = {
extraConfig = ''
import common
+
import tls_bunny
reverse_proxy http://127.0.0.1:5555
'';
};
+
+
settings.firewall.allowedTCPPorts = [ 22 ];
}
+1 -1
services/lanyard/default.nix
···
services.caddy.virtualHosts."lanyard.sappho.systems" = {
extraConfig = ''
import common
-
import tls_cloudflare
reverse_proxy http://127.0.0.1:4001
'';
};
···
services.caddy.virtualHosts."lanyard.sappho.systems" = {
extraConfig = ''
import common
+
import tls_bunny
reverse_proxy http://127.0.0.1:4001
'';
};
+2 -4
services/ntfy/default.nix
···
services.caddy.virtualHosts."notify.sappho.systems" = {
extraConfig = ''
import common
-
import tls_cloudflare
reverse_proxy http://127.0.0.1:7070
'';
};
-
-
# Firewall
-
settings.firewall.allowedTCPPorts = [ 7070 ];
}
···
services.caddy.virtualHosts."notify.sappho.systems" = {
extraConfig = ''
import common
+
import tls_bunny
+
reverse_proxy http://127.0.0.1:7070
'';
};
}
+2 -2
services/outline/default.nix
···
services.caddy.virtualHosts."wiki.sappho.systems" = {
extraConfig = ''
import common
-
import tls_cloudflare
reverse_proxy http://localhost:3300
'';
};
···
services.caddy.virtualHosts."minio.sappho.systems" = {
extraConfig = ''
import common
-
import tls_cloudflare
reverse_proxy http://localhost:9000
'';
};
···
services.caddy.virtualHosts."wiki.sappho.systems" = {
extraConfig = ''
import common
+
import tls_bunny
reverse_proxy http://localhost:3300
'';
};
···
services.caddy.virtualHosts."minio.sappho.systems" = {
extraConfig = ''
import common
+
import tls_bunny
reverse_proxy http://localhost:9000
'';
};