❄️ Dotfiles for our NixOS system configuration.

Compare changes

Choose any two refs to compare.

+232 -25
flake.lock
···
]
},
"locked": {
-
"lastModified": 1761906071,
-
"narHash": "sha256-uq0X0KRzZkKGlkU1ARM+LWCAVvSYJZtTMpuzMO6LXRs=",
+
"lastModified": 1762198582,
+
"narHash": "sha256-P9giW/1Crn7ekQt4YIbONJ/hKFaHkTwyhz82FCjh+OM=",
"owner": "catppuccin",
"repo": "nix",
-
"rev": "fd1bc7b1af0a576edb13c166c4b1f7b5c7555a73",
+
"rev": "08716214674ca27914daa52e6fa809cc022b581e",
"type": "github"
},
"original": {
···
"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,
···
"flake-compat": {
"flake": false,
"locked": {
-
"lastModified": 1747046372,
-
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
+
"lastModified": 1761588595,
+
"narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=",
"owner": "edolstra",
"repo": "flake-compat",
-
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
+
"rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5",
"type": "github"
},
"original": {
···
"type": "github"
}
},
+
"flake-compat_4": {
+
"flake": false,
+
"locked": {
+
"lastModified": 1751685974,
+
"narHash": "sha256-NKw96t+BgHIYzHUjkTK95FqYRVKB8DHpVhefWSz/kTw=",
+
"rev": "549f2762aebeff29a2e5ece7a7dc0f955281a1d1",
+
"type": "tarball",
+
"url": "https://git.lix.systems/api/v1/repos/lix-project/flake-compat/archive/549f2762aebeff29a2e5ece7a7dc0f955281a1d1.tar.gz?rev=549f2762aebeff29a2e5ece7a7dc0f955281a1d1"
+
},
+
"original": {
+
"type": "tarball",
+
"url": "https://git.lix.systems/lix-project/flake-compat/archive/main.tar.gz"
+
}
+
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
-
"lastModified": 1760948891,
-
"narHash": "sha256-TmWcdiUUaWk8J4lpjzu4gCGxWY6/Ok7mOK4fIFfBuU4=",
+
"lastModified": 1762040540,
+
"narHash": "sha256-z5PlZ47j50VNF3R+IMS9LmzI5fYRGY/Z5O5tol1c9I4=",
"owner": "hercules-ci",
"repo": "flake-parts",
-
"rev": "864599284fc7c0ba6357ed89ed5e2cd5040f0c04",
+
"rev": "0010412d62a25d959151790968765a70c436598b",
"type": "github"
},
"original": {
···
"type": "github"
}
},
+
"flake-utils_3": {
+
"inputs": {
+
"systems": "systems_4"
+
},
+
"locked": {
+
"lastModified": 1694529238,
+
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
+
"owner": "numtide",
+
"repo": "flake-utils",
+
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
+
"type": "github"
+
},
+
"original": {
+
"owner": "numtide",
+
"repo": "flake-utils",
+
"type": "github"
+
}
+
},
+
"gomod2nix": {
+
"inputs": {
+
"flake-utils": "flake-utils_3",
+
"nixpkgs": [
+
"tangled",
+
"nixpkgs"
+
]
+
},
+
"locked": {
+
"lastModified": 1754078208,
+
"narHash": "sha256-YVoIFDCDpYuU3riaDEJ3xiGdPOtsx4sR5eTzHTytPV8=",
+
"owner": "nix-community",
+
"repo": "gomod2nix",
+
"rev": "7f963246a71626c7fc70b431a315c4388a0c95cf",
+
"type": "github"
+
},
+
"original": {
+
"owner": "nix-community",
+
"repo": "gomod2nix",
+
"type": "github"
+
}
+
},
"home-manager": {
"inputs": {
"nixpkgs": [
···
]
},
"locked": {
-
"lastModified": 1761878381,
-
"narHash": "sha256-lCRaipHgszaFZ1Cs8fdGJguVycCisBAf2HEFgip5+xU=",
+
"lastModified": 1762286824,
+
"narHash": "sha256-xwfiYyn210aNIE/JKc6qmU7qd0619JaYMOGuFKNm9oc=",
"owner": "nix-community",
"repo": "home-manager",
-
"rev": "4ac96eb21c101a3e5b77ba105febc5641a8959aa",
+
"rev": "1c75dd70229171f47ff10f4ed184101af7c7a392",
"type": "github"
},
"original": {
···
"type": "github"
}
},
+
"htmx-src": {
+
"flake": false,
+
"locked": {
+
"narHash": "sha256-nm6avZuEBg67SSyyZUhjpXVNstHHgUxrtBHqJgowU08=",
+
"type": "file",
+
"url": "https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js"
+
},
+
"original": {
+
"type": "file",
+
"url": "https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js"
+
}
+
},
+
"htmx-ws-src": {
+
"flake": false,
+
"locked": {
+
"narHash": "sha256-2fg6KyEJoO24q0fQqbz9RMaYNPQrMwpZh29tkSqdqGY=",
+
"type": "file",
+
"url": "https://cdn.jsdelivr.net/npm/htmx-ext-ws@2.0.2"
+
},
+
"original": {
+
"type": "file",
+
"url": "https://cdn.jsdelivr.net/npm/htmx-ext-ws@2.0.2"
+
}
+
},
+
"ibm-plex-mono-src": {
+
"flake": false,
+
"locked": {
+
"lastModified": 1731402384,
+
"narHash": "sha256-OwUmrPfEehLDz0fl2ChYLK8FQM2p0G1+EMrGsYEq+6g=",
+
"type": "tarball",
+
"url": "https://github.com/IBM/plex/releases/download/@ibm/plex-mono@1.1.0/ibm-plex-mono.zip"
+
},
+
"original": {
+
"type": "tarball",
+
"url": "https://github.com/IBM/plex/releases/download/@ibm/plex-mono@1.1.0/ibm-plex-mono.zip"
+
}
+
},
+
"indigo": {
+
"flake": false,
+
"locked": {
+
"lastModified": 1753693716,
+
"narHash": "sha256-DMIKnCJRODQXEHUxA+7mLzRALmnZhkkbHlFT2rCQYrE=",
+
"owner": "oppiliappan",
+
"repo": "indigo",
+
"rev": "5f170569da9360f57add450a278d73538092d8ca",
+
"type": "github"
+
},
+
"original": {
+
"owner": "oppiliappan",
+
"repo": "indigo",
+
"type": "github"
+
}
+
},
+
"inter-fonts-src": {
+
"flake": false,
+
"locked": {
+
"lastModified": 1731687360,
+
"narHash": "sha256-5vdKKvHAeZi6igrfpbOdhZlDX2/5+UvzlnCQV6DdqoQ=",
+
"type": "tarball",
+
"url": "https://github.com/rsms/inter/releases/download/v4.1/Inter-4.1.zip"
+
},
+
"original": {
+
"type": "tarball",
+
"url": "https://github.com/rsms/inter/releases/download/v4.1/Inter-4.1.zip"
+
}
+
},
+
"lucide-src": {
+
"flake": false,
+
"locked": {
+
"lastModified": 1754044466,
+
"narHash": "sha256-+exBR2OToB1iv7ZQI2S4B0lXA/QRvC9n6U99UxGpJGs=",
+
"type": "tarball",
+
"url": "https://github.com/lucide-icons/lucide/releases/download/0.536.0/lucide-icons-0.536.0.zip"
+
},
+
"original": {
+
"type": "tarball",
+
"url": "https://github.com/lucide-icons/lucide/releases/download/0.536.0/lucide-icons-0.536.0.zip"
+
}
+
},
"nil": {
"inputs": {
"nixpkgs": [
···
]
},
"locked": {
-
"lastModified": 1761339987,
-
"narHash": "sha256-IUaawVwItZKi64IA6kF6wQCLCzpXbk2R46dHn8sHkig=",
+
"lastModified": 1762186368,
+
"narHash": "sha256-dzLBZKccS0jMefj+WAYwsk7gKDluqavC7I4KfFwVh8k=",
"owner": "nix-darwin",
"repo": "nix-darwin",
-
"rev": "7cd9aac79ee2924a85c211d21fafd394b06a38de",
+
"rev": "69921864a70b58787abf5ba189095566c3f0ffd3",
"type": "github"
},
"original": {
···
]
},
"locked": {
-
"lastModified": 1761563673,
-
"narHash": "sha256-d+1TpVAmRjcNBfjZsh2yQSdwUfN7Xgz1blJ185g73+A=",
+
"lastModified": 1762251193,
+
"narHash": "sha256-CmSddz8e2kM+ITbYutluhKZyXXwI9Sg2lf7XXSvc8oY=",
"owner": "nix-community",
"repo": "NixOS-WSL",
-
"rev": "a518cf710e5ebb935518dc7ac98e07e7ee5014c3",
+
"rev": "e001844d4553aef268f97b32d3a825b6370eed91",
"type": "github"
},
"original": {
···
},
"nixpkgs": {
"locked": {
-
"lastModified": 1761672384,
-
"narHash": "sha256-o9KF3DJL7g7iYMZq9SWgfS1BFlNbsm6xplRjVlOCkXI=",
+
"lastModified": 1762111121,
+
"narHash": "sha256-4vhDuZ7OZaZmKKrnDpxLZZpGIJvAeMtK6FKLJYUtAdw=",
"owner": "nixos",
"repo": "nixpkgs",
-
"rev": "08dacfca559e1d7da38f3cf05f1f45ee9bfd213c",
+
"rev": "b3d51a0365f6695e7dd5cdf3e180604530ed33b4",
"type": "github"
},
"original": {
···
},
"nixpkgs-lib": {
"locked": {
-
"lastModified": 1754788789,
-
"narHash": "sha256-x2rJ+Ovzq0sCMpgfgGaaqgBSwY+LST+WbZ6TytnT9Rk=",
+
"lastModified": 1761765539,
+
"narHash": "sha256-b0yj6kfvO8ApcSE+QmA6mUfu8IYG6/uU28OFn4PaC8M=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
-
"rev": "a73b9c743612e4244d865a2fdee11865283c04e6",
+
"rev": "719359f4562934ae99f5443f20aa06c2ffff91fc",
"type": "github"
},
"original": {
···
"root": {
"inputs": {
"catppuccin": "catppuccin",
+
"darwin-login-items": "darwin-login-items",
"easy-hosts": "easy-hosts",
"flake-parts": "flake-parts",
"home-manager": "home-manager",
···
"nixos-wsl": "nixos-wsl",
"nixpkgs": "nixpkgs",
"ragenix": "ragenix",
-
"solaar": "solaar"
+
"solaar": "solaar",
+
"tangled": "tangled"
}
},
"rust-overlay": {
···
"type": "github"
}
},
+
"sqlite-lib-src": {
+
"flake": false,
+
"locked": {
+
"lastModified": 1706631843,
+
"narHash": "sha256-bJoMjirsBjm2Qk9KPiy3yV3+8b/POlYe76/FQbciHro=",
+
"type": "tarball",
+
"url": "https://sqlite.org/2024/sqlite-amalgamation-3450100.zip"
+
},
+
"original": {
+
"type": "tarball",
+
"url": "https://sqlite.org/2024/sqlite-amalgamation-3450100.zip"
+
}
+
},
"systems": {
"locked": {
"lastModified": 1681028828,
···
"owner": "nix-systems",
"repo": "default",
"type": "github"
+
}
+
},
+
"systems_4": {
+
"locked": {
+
"lastModified": 1681028828,
+
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+
"owner": "nix-systems",
+
"repo": "default",
+
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+
"type": "github"
+
},
+
"original": {
+
"owner": "nix-systems",
+
"repo": "default",
+
"type": "github"
+
}
+
},
+
"tangled": {
+
"inputs": {
+
"flake-compat": "flake-compat_4",
+
"gomod2nix": "gomod2nix",
+
"htmx-src": "htmx-src",
+
"htmx-ws-src": "htmx-ws-src",
+
"ibm-plex-mono-src": "ibm-plex-mono-src",
+
"indigo": "indigo",
+
"inter-fonts-src": "inter-fonts-src",
+
"lucide-src": "lucide-src",
+
"nixpkgs": [
+
"nixpkgs"
+
],
+
"sqlite-lib-src": "sqlite-lib-src"
+
},
+
"locked": {
+
"lastModified": 1762351433,
+
"narHash": "sha256-1PGiDLrx1hNGNoUCnmc1Np2t48r7nIxxiTO8JHvaTws=",
+
"ref": "refs/heads/master",
+
"rev": "dd1bcee8c99bf799130645f32c94d1693f812f0d",
+
"revCount": 1604,
+
"type": "git",
+
"url": "https://tangled.org/@tangled.org/core"
+
},
+
"original": {
+
"type": "git",
+
"url": "https://tangled.org/@tangled.org/core"
}
}
},
+8 -5
flake.nix
···
url = "github:Svenum/Solaar-Flake/main";
inputs.nixpkgs.follows = "nixpkgs";
};
+
+
tangled = {
+
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,
-
nixos-wsl,
-
...
-
}:
+
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 = [
+
./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,
+
osConfig,
...
}:
let
-
defaultPackages = import ./list/default.nix { inherit pkgs; };
-
guiPackages = import ./list/gui.nix { inherit pkgs lib osConfig; };
+
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
{
-
imports = [
-
./autostart.nix
-
./scripts.nix
-
];
-
config = {
-
home.packages = defaultPackages ++ guiPackages;
+
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
+17 -11
home/chloe/programs/cli/git.nix
···
let
signingKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICM6XP+CNc2CStEDe/W4LfkcRcG98obQiM2aqnydCRbX";
-
-
opSshSignPath =
+
+
opSshSignPath =
if (osConfig ? wsl) then
"/mnt/c/Users/Chloe/AppData/Local/1Password/app/8/op-ssh-sign-wsl"
else if pkgs.stdenv.hostPlatform.isDarwin then
"/Applications/1Password.app/Contents/MacOS/op-ssh-sign"
-
else
+
else
"${pkgs._1password-gui}/bin/op-ssh-sign";
in
{
home.file.".ssh/allowed_signers".text = "* ${signingKey}";
-
+
programs.git = {
enable = true;
-
userName = "Chloe";
-
userEmail = "chloe@sapphic.moe";
-
extraConfig = lib.mkMerge [
+
settings = lib.mkMerge [
{
-
user.signingkey = signingKey;
-
gpg.format = "ssh";
-
gpg.ssh.program = opSshSignPath;
-
gpg.ssh.allowedSignersFile = "~/.ssh/allowed_signers";
+
user = {
+
name = "Chloe A";
+
email = "chloe@sapphic.moe";
+
signingkey = signingKey;
+
};
+
+
gpg = {
+
format = "ssh";
+
ssh.program = opSshSignPath;
+
ssh.allowedSignersFile = "~/.ssh/allowed_signers";
+
};
+
commit.gpgsign = true;
}
+1 -7
home/chloe/programs/cli/ssh.nix
···
programs.ssh = {
enable = true;
enableDefaultConfig = false;
-
+
matchBlocks."*" = lib.mkMerge [
# {
# # Default configuration for all hosts
···
identityAgent = "\"~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock\"";
})
];
-
-
# Fallback TERM for Ghostty if remote does not support xterm-ghostty
-
extraConfig = ''
-
Host *
-
SetEnv TERM=xterm-256color
-
'';
};
}
+12 -8
home/chloe/programs/cli/zsh.nix
···
-
{ lib, pkgs, osConfig, config, ... }:
+
{
+
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"
-
''}
+
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 [
+9 -2
home/chloe/programs/gui/ghostty.nix
···
package = if pkgs.stdenv.hostPlatform.isLinux then pkgs.ghostty else pkgs.ghostty-bin;
settings = {
-
command = "/bin/zsh";
+
command = "${pkgs.zsh}/bin/zsh";
+
auto-update = "off";
font-family = "Iosevka";
font-size = 14;
···
confirm-close-surface = true;
shell-integration = "detect";
-
shell-integration-features = [ "cursor" "sudo" "title" "ssh-env" "ssh-terminfo" ];
+
shell-integration-features = [
+
"cursor"
+
"sudo"
+
"title"
+
"ssh-env"
+
"ssh-terminfo"
+
];
keybind = [
"super+n=new_window"
+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
···
{
+
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
+
];
+
+
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
+
ragenix
];
}
+1
modules/darwin/default.nix
···
./packages.nix
./preferences
./security
+
./startup.nix
./users.nix
];
}
+2
modules/darwin/extras.nix
···
{
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"
+
"bruno"
+
"crossover"
+
"discord"
"maccy"
+
"microsoft-edge"
"microsoft-teams"
+
"mos"
"music-presence"
-
"prismlauncher"
+
"osu"
+
"signal"
+
"steam"
];
# Mac App Store apps (requires mas-cli)
+16
modules/darwin/preferences/custom.nix
···
+
{
+
# Custom settings not supported directly by nix-darwin.
+
# TODO: Research other useful custom settings.
+
# https://github.com/yannbertrand/macos-defaults
+
+
system.defaults.CustomUserPreferences = {
+
# Lock the size of the dock.
+
"com.apple.dock".size-immutable = true;
+
+
# Prevent creation of .DS_Store files on network or USB volumes.
+
"com.apple.desktopservices" = {
+
DSDontWriteNetworkStores = true;
+
DSDontWriteUSBStores = true;
+
};
+
};
+
}
+1
modules/darwin/preferences/default.nix
···
{
imports = [
+
./custom.nix
./dock.nix
./finder.nix
./keyboard.nix
+2
modules/darwin/preferences/dock.nix
···
system.defaults.dock = {
orientation = "bottom";
show-recents = false;
+
magnification = true;
+
largesize = 72;
tilesize = 52;
minimize-to-application = true;
};
+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"
+
];
+
};
+
}
+1 -6
modules/nixos/extras.nix
···
imports = [
inputs.home-manager.nixosModules.home-manager
inputs.ragenix.nixosModules.default
-
];
-
-
age.identityPaths = [
-
"/home/chloe/.ssh/id_ed25519.age"
-
"/home/chloe/.ssh/id_ed25519.auth"
-
"/home/chloe/.ssh/id_ed25519.sign"
+
inputs.tangled.nixosModules.knot
];
}
-1
modules/nixos/packages.nix
···
{
environment.systemPackages = with pkgs; [
gparted
-
ragenix
];
}
+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=
+
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGN2U2dKQSA0THNq
+
UjBzRllITWxyM0tWckVhU0p3SE4zNWhYdmVuUUVyREtLOGxwbFRJCkh3QXFOUW9p
+
ZWxSUS9LTzJsMFdyK05jOXVBVzI2VUptTFFManZTQnRMUmsKLT4gc3NoLWVkMjU1
+
MTkgMUNUOTd3IEdKUHhlMG1hakFJcThMdjVWMk5WZ3RzYUZnbE5RdXJycmk3TW9H
+
TDF5bEUKTTIrbVh6UTZHYnhmV21sUFpSOGYwT3FCT1RacjRscUVabFpYWmh4U3Ft
+
cwotPiBzc2gtZWQyNTUxOSBlUDNUdFEgTXhVUGFrcExTV05qb2ZuWDkzd0hYSCtl
+
NjZLaWxFdi9ibmtUcDVzWE5DSQpSVTVvUlpBN2REZ2ZSMkRkWWlNWkFaekxROVVt
+
bEc4NXBkdVpWTkF0OENVCi0+IEw5e0xlLWdyZWFzZSBUIHdHZHk5IgpwVERQMzZG
+
ODgwUExoa082aFp2K0NvYmhtT1lvd2MzMlEycGc4YjlwVGNPcy9nS0FQV3lZTkpr
+
SkkzdXlWdllBCnBGODRBRU9vQnB4THdBQzZycGdMS05mN3R4VWZvaUpnbWZ5M3Zu
+
blN1cys1VGc4R09TdHdHaDhEQVF3Ci0tLSBYWmNaYkZiSzVQUXp0OU14WmY4cGNR
+
Mk1VZ1NxOWV3RUxVb0taRWRxZTFrCvVGqFlzVEsdUx95OQNCI1lmXdAXY5SxwJ6q
+
PI7Cj+zq+5BjUYqwPnu2KcMMKEtmBReSyL791bRn16PSzIS3yn2DzmIM72oea9hd
+
WJCud87qCxejIF3LgczKUqpTILzXtM7jzR2CNhoBSc9VDDA3U+ku4jOnxfj6
-----END AGE ENCRYPTED FILE-----
+14
secrets/destiny-labeler.age
···
+
-----BEGIN AGE ENCRYPTED FILE-----
+
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGN2U2dKQSBoNkJV
+
T04yWG9PbFdmRGc0bUhNaU4yTGl2S0lDZERlZ2FYbGNKSFdqMmkwCmdqVzlRYVhJ
+
NmN1VWYrbklIbFVvVUcxSHJnL09ydUVUV2tmaURVMUpKZ2sKLT4gc3NoLWVkMjU1
+
MTkgMUNUOTd3IFMvcVFvQ2M3UUpIZEdkRmtrSUtLaGZscHl0WGM2WFpjMlBPRlN1
+
K1p1MkkKYXg3d04weWdqRGY4RWJtdGZLUytvM1dQanBxVm4zUm91TXFDNjFhNDZk
+
dwotPiBzc2gtZWQyNTUxOSBlUDNUdFEgRUUxWVNLUDR3aVZUbENPai9LclhZTzZS
+
SDJjbDUyeXhqemdxWWlzdHZBSQpTMnhLWXp4MTE1eDVmUVoxYXNUZ2JrSWlFNGtr
+
NklWbmhCcWIxTzZEcEdFCi0+IDxsVypbQjJELWdyZWFzZSB5SnNuYGNjCjRDaXlC
+
cWdLd1EKLS0tIElObUx3SldVcTdtcVozc3A3Q0FROWZlV2JWckFESHBzQTVlNjlH
+
VEJ3YVUKWFcj2Hf3rGa7+aiJKUktkdOmVRF2c7VgKajt/EC3Z61jnMn9rMPWf9ZE
+
PlsfqBNOlLOiLfdEQnrwC3t2ajETUExrjUUhZ3n23rFT7SzB8giiDXEg9r6pV6Vk
+
aevt9JaLH123SxZURnkTJQ==
+
-----END AGE ENCRYPTED FILE-----
+2
secrets/secrets.nix
···
];
in
{
+
"abuseipdb.age".publicKeys = keys;
"bluesky-pds.age".publicKeys = keys;
"caddy.age".publicKeys = keys;
+
"destiny-labeler.age".publicKeys = keys;
"glance.age".publicKeys = keys;
"lanyard.age".publicKeys = keys;
"minio.age".publicKeys = keys;
+64
services/adguardhome/default.nix
···
+
{
+
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" ];
+
}
+
];
+
};
+
};
+
}
+52 -12
services/bluesky-pds/default.nix
···
-
{ config, ... }:
+
{ config, lib, ... }:
{
age.secrets.bluesky-pds = {
···
settings = {
PDS_HOSTNAME = "pds.sappho.systems";
PDS_PORT = 3333;
+
PDS_CRAWLERS = lib.concatStringsSep "," [
+
"https://bsky.network"
+
"https://relay.cerulea.blue"
+
"https://relay.upcloud.world"
+
"https://atproto.africa"
+
];
};
environmentFiles = [ config.age.secrets.bluesky-pds.path ];
};
services.caddy.virtualHosts."pds.sappho.systems" = {
-
listenAddresses = [ "::" ];
+
serverAliases = [ "*.pds.sappho.systems" ];
extraConfig = ''
import common
-
import tls_cloudflare
-
reverse_proxy http://127.0.0.1:3333
-
'';
-
};
+
import tls_bunny
-
services.caddy.virtualHosts."*.pds.sappho.systems" = {
-
listenAddresses = [ "::" ];
-
extraConfig = ''
-
import common
-
import tls_cloudflare
-
reverse_proxy http://127.0.0.1:3333
+
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=";
+
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_cloudflare) {
+
(tls_bunny) {
tls {
-
dns cloudflare {env.CF_API_TOKEN}
-
resolvers 8.8.8.8 1.1.1.1
+
dns bunny {env.BUNNY_API_KEY}
+
resolvers 9.9.9.9 149.112.112.112
}
}
(common) {
encode zstd gzip
}
+
'';
+
logFormat = ''
+
level debug
+
format json
'';
};
+6 -2
services/default.nix
···
{
imports = [
+
./adguardhome
+
./bluesky-pds
./caddy
+
./destiny-labeler
+
./fail2ban
./glance
-
./bluesky-pds
-
./outline
+
./knot
./lanyard
./ntfy
+
./outline
];
}
+48
services/destiny-labeler/default.nix
···
+
{ config, ... }:
+
+
{
+
systemd.tmpfiles.rules = [
+
"d /var/lib/destiny-labeler/data 0755 root root -"
+
"f /var/lib/destiny-labeler/data/cursor.txt 0644 root root -"
+
"f /var/lib/destiny-labeler/data/labels.db 0644 root root -"
+
"f /var/lib/destiny-labeler/data/labels.db-shm 0644 root root -"
+
"f /var/lib/destiny-labeler/data/labels.db-wal 0644 root root -"
+
];
+
+
age.secrets.destiny-labeler = {
+
file = ../../secrets/destiny-labeler.age;
+
mode = "600";
+
};
+
+
virtualisation.oci-containers.containers."destiny-labeler" = {
+
image = "ghcr.io/sapphosys/destiny-labeler:main";
+
pull = "always";
+
autoRemoveOnStop = false;
+
ports = [ "4002:4002" ];
+
environment = {
+
DID = "did:plc:zt2oycjggn5gwdtcgphdh4tn";
+
URL = "wss://jetstream1.us-east.bsky.network/subscribe";
+
PORT = "4002";
+
NODE_ENV = "production";
+
};
+
environmentFiles = [ config.age.secrets.destiny-labeler.path ];
+
volumes = [
+
"/var/lib/destiny-labeler/data/cursor.txt:/app/cursor.txt"
+
"/var/lib/destiny-labeler/data/labels.db:/app/labels.db"
+
"/var/lib/destiny-labeler/data/labels.db-shm:/app/labels.db-shm"
+
"/var/lib/destiny-labeler/data/labels.db-wal:/app/labels.db-wal"
+
];
+
extraOptions = [
+
"--restart=always"
+
"--network=host"
+
];
+
};
+
+
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 -3
services/glance/default.nix
···
services.glance = {
enable = true;
-
openFirewall = true;
+
openFirewall = false;
environmentFile = config.age.secrets.glance.path;
settings = import ./settings.nix;
};
services.caddy.virtualHosts."home.sappho.systems" = {
-
listenAddresses = [ "::" ];
extraConfig = ''
import common
-
import tls_cloudflare
reverse_proxy http://localhost:4040
'';
};
+50
services/knot/default.nix
···
+
{
+
services.tangled-knot = {
+
enable = true;
+
motd = ''
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⡀⢀⠀⢀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⢧⢳⣾⣂⣾⡁⣴⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⠓⣬⠾⠋⠉⢸⣷⣋⣤⡦
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢯⡞⠃⠀⠀⠀⡼⠊⣠⡏⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣶⣄⣀⣀⡴⣡⡴⠃⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⣽⠧⢴⡀⢸⡛⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡿⠻⠛⠋⠉⠁⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠾⡇⢸⠉⠀⠀⠹⣼⣽⡚⠉⠹⡄⠀⠀⠀⠀⠀⠀⠀⠀⡴⠛⠙⢳⡀⠀⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡘⢿⣆⠀⠀⠀⣿⠉⠀⠀⢀⣧⠀⢀⣤⠤⢤⣄⠀⢸⠉⠀⠀⠈⣧⢸⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠾⠉⠙⠻⢗⠀⣦⣏⣤⠀⣠⠞⠀⠀⣞⠋⠀⠀⠈⠻⡼⡀⣄⡀⢀⡯⠟⠧⢤⡀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢯⣀⠀⠀⠲⣷⡿⣿⠋⠉⠁⠀⠀⠀⠹⣄⠀⠀⣠⣤⠘⠧⣽⣨⣞⡁⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠁⠀⢸⣷⠀⠀⠀⠀⠀⠀⠈⣓⣦⣜⠉⣿⣶⠧⣟⣙⣀⣀⣠⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⠀⠀⠀⠀⠀⠀⡞⠉⠀⠾⠞⡵⢜⣇⡀⠙⢦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡤⠤⠤⣄⠀⣼⣿⡠⠴⠶⠦⣄⠀⣷⣀⠀⢀⣠⡷⡀⠉⠁⠀⢸⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⡋⠀⠀⠀⠈⢷⣿⠋⠀⠀⠀⠀⢸⡆⠀⠈⠉⣩⣿⠣⠙⠲⠤⠶⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⢻⠀⠀⠀⠀⠀⠀⡇⣠⣴⣾⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣄⠀⠀⠀⢠⣦⣌⡄⢠⣦⠀⠀⣾⠹⠋⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⠤⠙⠢⢄⡀⠀⢳⡄⢃⡼⠉⣀⠜⠋⠉⠒⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⢠⡯⠀⠀⠀⠀⠀⣠⡉⠙⣹⣿⠶⣮⣁⡀⠀⠀⠀⠀⢻⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠳⣄⠀⠀⠀⠘⠛⠓⣛⠇⢸⡷⡀⠻⠟⠀⠀⠀⠀⢸⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣹⠒⠶⢶⠒⠉⠀⠀⠈⠉⢫⠳⢄⣀⣀⣀⡤⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⡾⠋⠀⠀⢰⠀⠀⠀⠀⠀⠀⢸⣇⠀⠈⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⠀⠀⣠⣴⣾⠿⠛⠁⠀⠀⠀⠀⠈⠳⣄⣀⣀⣀⡤⠞⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠀⣴⣿⡿⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⣾⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⢸⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠈⢧⡃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
+
+
Welcome to Sapphic Angels' Knot server for tangled.org.
+
'';
+
server = {
+
hostname = "knot.sappho.systems";
+
owner = "did:plc:ucaezectmpny7l42baeyooxi";
+
};
+
};
+
+
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 -2
services/lanyard/default.nix
···
};
services.caddy.virtualHosts."lanyard.sappho.systems" = {
-
listenAddresses = [ "::" ];
extraConfig = ''
import common
-
import tls_cloudflare
+
import tls_bunny
reverse_proxy http://127.0.0.1:4001
'';
};
+2 -5
services/ntfy/default.nix
···
};
services.caddy.virtualHosts."notify.sappho.systems" = {
-
listenAddresses = [ "::" ];
extraConfig = ''
import common
-
import tls_cloudflare
+
import tls_bunny
+
reverse_proxy http://127.0.0.1:7070
'';
};
-
-
# Firewall
-
settings.firewall.allowedTCPPorts = [ 7070 ];
}
+3 -21
services/outline/default.nix
···
services.caddy.virtualHosts."wiki.sappho.systems" = {
extraConfig = ''
import common
-
import tls_cloudflare
+
import tls_bunny
reverse_proxy http://localhost:3300
'';
};
···
services.caddy.virtualHosts."minio.sappho.systems" = {
extraConfig = ''
import common
-
import tls_cloudflare
-
reverse_proxy http://localhost:9000 {
-
transport http {
-
versions 1.1
-
}
-
}
-
'';
-
};
-
-
services.caddy.virtualHosts."minio-admin.sappho.systems" = {
-
extraConfig = ''
-
import common
-
import tls_cloudflare
-
reverse_proxy http://localhost:9001 {
-
transport http {
-
versions 1.1
-
}
-
}
+
import tls_bunny
+
reverse_proxy http://localhost:9000
'';
};
-
-
settings.firewall.allowedTCPPorts = [ 3300 ];
}