Initial commit

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ellie 2026-02-15 14:57:00 -08:00
commit b8402a9049
82 changed files with 1345 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
docs/

5
.sops.yaml Normal file
View file

@ -0,0 +1,5 @@
creation_rules:
- path_regex: .*_vps\.yaml$
age: age1856wmagg3dz4j07alwqnn6d75655t6wcs8glklyjyezhu5p875fq9sez4p
- path_regex: .*\.yaml$
age: age126v48dgev6pu3uhe7dtpdhax2yes2ff9u42ke2k2h97e90z8d4psedau7u

81
common.nix Normal file
View file

@ -0,0 +1,81 @@
{ lib, ... }:
{
nix.gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 7d";
};
time.timeZone = "America/Vancouver";
i18n.defaultLocale = "en_US.UTF-8";
nix.settings.experimental-features = "nix-command flakes";
nix.settings.trusted-public-keys = lib.mkAfter [
"local.yesod.ellie:3cAK/At9uVQq5kNrZco1cuthpgoPy7JfEvd+sBs80fk="
];
networking.useDHCP = true;
services.openssh = {
enable = true;
settings = {
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
PermitRootLogin = "no";
AllowUsers = [ "ellie" ];
};
};
services.fail2ban = {
enable = true;
maxretry = 5;
bantime = "1h";
bantime-increment.enable = true;
jails = {
sshd.settings = {
enabled = true;
maxretry = 3;
};
nginx-botsearch.settings = {
enabled = true;
filter = "nginx-botsearch";
backend = "systemd";
maxretry = 2;
};
nginx-http-auth.settings = {
enabled = true;
filter = "nginx-http-auth";
backend = "systemd";
};
nginx-bad-request.settings = {
enabled = true;
filter = "nginx-bad-request";
backend = "systemd";
};
};
};
users.mutableUsers = true;
security.sudo.wheelNeedsPassword = true;
users.users.ellie = {
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOv7i4ChPUm+DmELG6uRx0co4quhQ+h7QB4fgcxcC3qx contact@elliehigh.com"
];
isNormalUser = true;
description = "Ellie";
extraGroups = [ "wheel" ];
initialPassword = "install";
};
system.autoUpgrade = {
enable = true;
allowReboot = true;
dates = "04:00";
};
system.stateVersion = "25.05";
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 780 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 535 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 507 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 805 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 B

View file

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Service Directory</title>
<link rel="stylesheet" href="style.css" />
<link rel="icon" href="images/reference-software.png" />
</head>
<body>
<header>
<h1>service directory</h1>
</header>
<main>
<article id="services">
<service>
<a href="http://192.168.1.66:3000">
<img src="images/internet.png" alt="invidious" />
<name>invidious</name>
</a>
</service>
<service>
<a href="http://192.168.1.66:9696">
<img src="images/torrent.png" alt="prowlarr" />
<name>prowlarr</name>
</a>
</service>
<service>
<a href="http://192.168.1.66:8989">
<img src="images/downloads.png" alt="sonarr" />
<name>sonarr</name>
</a>
</service>
<service>
<a href="http://192.168.1.66:7878">
<img src="images/downloads.png" alt="radarr" />
<name>radarr</name>
</a>
</service>
<service>
<a href="http://192.168.1.66:8096">
<img src="images/speaker.png" alt="jellyfin" />
<name>jellyfin</name>
</a>
</service>
</article>
</main>
</body>
</html>

View file

@ -0,0 +1,60 @@
body {
background: url("images/roesbudtrelbg.gif") repeat;
}
header {
display: flex;
justify-content: center;
align-items: center;
margin: 0 auto 0 auto;
}
header h1::before {
content: url("images/angel01.gif");
display: inline-block;
vertical-align: middle;
margin-right: .05em;
}
header h1::after {
content: url("images/angel02.gif");
display: inline-block;
vertical-align: middle;
margin-left: .05em;
}
service {
display: flex;
flex-direction: column;
align-items: center;
align-content: center;
margin: 1em;
& a {
display: contents;
}
& img {
width: 64px;
height: 64px;
image-rendering: pixelated;
transform-origin: top left;
}
}
#services {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
article {
background-color: white;
border: 1px dashed #fed1c9;
max-width: 30rem;
}
main {
display: flex;
flex-direction: column;
align-items: center;
}

251
flake.lock generated Normal file
View file

@ -0,0 +1,251 @@
{
"nodes": {
"advisory-db": {
"flake": false,
"locked": {
"lastModified": 1766324728,
"narHash": "sha256-9C+WyE5U3y5w4WQXxmb0ylRyMMsPyzxielWXSHrcDpE=",
"owner": "rustsec",
"repo": "advisory-db",
"rev": "c88b88c62bda077be8aa621d4e89d8701e39cb5d",
"type": "github"
},
"original": {
"owner": "rustsec",
"repo": "advisory-db",
"type": "github"
}
},
"continuwuity": {
"inputs": {
"advisory-db": "advisory-db",
"crane": "crane",
"fenix": "fenix",
"flake-compat": "flake-compat",
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs",
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1771194746,
"narHash": "sha256-U/BvGW5p405bbsxd8IdMHbSUNVXsLpgPy0ieQwEh3mE=",
"ref": "refs/heads/main",
"rev": "4e1dac32a5c0bf71021027bee35eca53892b069f",
"revCount": 6085,
"type": "git",
"url": "https://forgejo.ellis.link/continuwuation/continuwuity"
},
"original": {
"type": "git",
"url": "https://forgejo.ellis.link/continuwuation/continuwuity"
}
},
"crane": {
"locked": {
"lastModified": 1766194365,
"narHash": "sha256-4AFsUZ0kl6MXSm4BaQgItD0VGlEKR3iq7gIaL7TjBvc=",
"owner": "ipetkov",
"repo": "crane",
"rev": "7d8ec2c71771937ab99790b45e6d9b93d15d9379",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"disko": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1769524058,
"narHash": "sha256-zygdD6X1PcVNR2PsyK4ptzrVEiAdbMqLos7utrMDEWE=",
"owner": "nix-community",
"repo": "disko",
"rev": "71a3fc97d80881e91710fe721f1158d3b96ae14d",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "disko",
"type": "github"
}
},
"fenix": {
"inputs": {
"nixpkgs": [
"continuwuity",
"nixpkgs"
],
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1766299592,
"narHash": "sha256-7u+q5hexu2eAxL2VjhskHvaUKg+GexmelIR2ve9Nbb4=",
"owner": "nix-community",
"repo": "fenix",
"rev": "381579dee168d5ced412e2990e9637ecc7cf1c5d",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1765121682,
"narHash": "sha256-4VBOP18BFeiPkyhy9o4ssBNQEvfvv1kXkasAYd0+rrA=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "65f23138d8d09a92e30f1e5c87611b23ef451bf3",
"type": "github"
},
"original": {
"owner": "edolstra",
"ref": "master",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1765835352,
"narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "a34fae9c08a15ad73f295041fec82323541400a9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1766070988,
"narHash": "sha256-G/WVghka6c4bAzMhTwT2vjLccg/awmHkdKSd2JrycLc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c6245e83d836d0433170a16eb185cefe0572f8b8",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1765674936,
"narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1771008912,
"narHash": "sha256-gf2AmWVTs8lEq7z/3ZAsgnZDhWIckkb+ZnAo5RzSxJg=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "a82ccc39b39b621151d6732718e3e250109076fa",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"continuwuity": "continuwuity",
"disko": "disko",
"nixpkgs": "nixpkgs_2",
"sops-nix": "sops-nix"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1766253897,
"narHash": "sha256-ChK07B1aOlJ4QzWXpJo+y8IGAxp1V9yQ2YloJ+RgHRw=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "765b7bdb432b3740f2d564afccfae831d5a972e4",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
"sops-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1771166946,
"narHash": "sha256-UFc4lfGBr+wJmwgDGJDn1cVD6DTr0/8TdronNUiyXlU=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "2d0cf89b4404529778bc82de7e42b5754e0fe4fa",
"type": "github"
},
"original": {
"owner": "Mic92",
"repo": "sops-nix",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"continuwuity",
"nixpkgs"
]
},
"locked": {
"lastModified": 1766000401,
"narHash": "sha256-+cqN4PJz9y0JQXfAK5J1drd0U05D5fcAGhzhfVrDlsI=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "42d96e75aa56a3f70cab7e7dc4a32868db28e8fd",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

90
flake.nix Normal file
View file

@ -0,0 +1,90 @@
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
disko = {
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
};
sops-nix = {
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
continuwuity = {
url = "git+https://forgejo.ellis.link/continuwuation/continuwuity";
};
};
outputs =
{
nixpkgs,
disko,
sops-nix,
continuwuity,
...
}:
let
system = "x86_64-linux";
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
};
in
{
nixosConfigurations.home-server = nixpkgs.lib.nixosSystem {
inherit system;
specialArgs = { inherit continuwuity; };
modules = [
disko.nixosModules.disko
sops-nix.nixosModules.sops
(
{ ... }:
{
nixpkgs = {
config.allowUnfree = true;
system = "x86_64-linux";
};
}
)
./hosts/homeserver/hardware-configuration.nix
./common.nix
./hosts/homeserver/configuration.nix
./hosts/homeserver/setup.nix
./services/website.nix
./services/wireguard-inner.nix
./services/matrix.nix
./services/ergo.nix
./services/akkoma.nix
];
};
nixosConfigurations.vps = nixpkgs.lib.nixosSystem {
inherit system;
modules = [
disko.nixosModules.disko
sops-nix.nixosModules.sops
(
{ ... }:
{
nixpkgs = {
config.allowUnfree = true;
system = "x86_64-linux";
};
}
)
./hosts/vps/hardware-configuration.nix
./common.nix
./hosts/vps/configuration.nix
./hosts/vps/disko-config.nix
./services/coturn.nix
./services/wireguard-outer.nix
];
};
formatter.${system} = pkgs.nixfmt-tree;
};
}

View file

@ -0,0 +1,70 @@
{
modulesPath,
lib,
pkgs,
config,
...
}:
{
boot.loader.systemd-boot = {
enable = true;
configurationLimit = 10;
};
boot.loader.efi.canTouchEfiVariables = true;
networking.hostName = "ellie-server";
networking.useNetworkd = true;
systemd.network.enable = true;
hardware.graphics = {
enable = true;
};
hardware.nvidia = {
package = config.boot.kernelPackages.nvidiaPackages.stable;
modesetting.enable = true;
open = false;
};
services.resolved.enable = true;
services.openssh = {
openFirewall = false;
};
networking.nftables.enable = true;
networking.firewall = {
enable = true;
allowPing = true;
checkReversePath = true;
rejectPackets = true;
allowedTCPPorts = [ ];
interfaces."enp34s0".allowedTCPPorts = [ ];
extraInputRules = ''
ip saddr 192.168.1.0/24 tcp dport {22, 8096, 8920, 3000, 8282, 9696, 8989, 7878, 80} accept
tcp dport {22, 8096, 8920, 3000, 8282, 9696, 8989, 7878, 80} drop
ip saddr 192.168.1.0/24 udp dport 5353 accept
udp dport 5353 drop
'';
};
users.groups.media = { };
#users.users.radarr.extraGroups = [ "media" ];
#users.users.sonarr.extraGroups = [ "media" ];
#users.users.jellyfin.extraGroups = [ "media" ];
sops.age.keyFile = "/home/ellie/.config/sops/age/keys.txt";
environment.systemPackages = with pkgs; [
git
vim
dropbear
age
sops
];
system.stateVersion = "25.05";
}

View file

@ -0,0 +1,25 @@
{
config,
lib,
pkgs,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [
"xhci-pci"
"ahci"
"usbhid"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View file

@ -0,0 +1,91 @@
{ ... }:
{
boot.initrd.network = {
enable = true;
ssh = {
enable = true;
authorizedKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOv7i4ChPUm+DmELG6uRx0co4quhQ+h7QB4fgcxcC3qx contact@elliehigh.com"
];
shell = "/bin/cryptsetup-askpass";
hostKeys = [ "/etc/initrd-ssh/ssh_host_ed25519_dropbear" ];
};
};
boot.initrd.preLVMCommands = ''
iptables -A INPUT -s 192.168.1.0/24 -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP
'';
fileSystems."/media" = {
device = "/dev/mapper/crypted";
fsType = "btrfs";
options = [
"subvol=/media"
"compress=zstd"
"noatime"
];
};
disko.devices.disk.main = {
type = "disk";
device = "/dev/sda";
content = {
"type" = "gpt";
partitions = {
ESP = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted";
settings = {
allowDiscards = true;
};
content = {
type = "btrfs";
# extraArgs = [ "-f" ];
subvolumes = {
"/root" = {
mountpoint = "/";
mountOptions = [
"compress=zstd"
"noatime"
];
};
"/home" = {
mountpoint = "/home";
mountOptions = [
"compress=zstd"
"noatime"
];
};
"/jellyfin" = {
mountpoint = "/jellyfin";
mountOptions = [
"compress=zstd"
"noatime"
];
};
"/swap" = {
mountpoint = "/.swapvol";
swap.swapfile.size = "36G";
};
};
};
};
};
};
};
};
}

View file

@ -0,0 +1,7 @@
{ ... }:
{
boot.loader.grub.enable = true;
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
}

View file

@ -0,0 +1,39 @@
{ ... }:
{
disko.devices = {
disk = {
main = {
type = "disk";
device = "/dev/sda";
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02";
priority = 1;
};
ESP = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}

View file

@ -0,0 +1,8 @@
{ modulesPath, lib, ... }:
{
imports = [ (modulesPath + "/profiles/qemu-guest.nix") ];
networking.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}

103
services/akkoma.nix Normal file
View file

@ -0,0 +1,103 @@
{ pkgs, ... }:
let
inherit ((pkgs.formats.elixirConf { }).lib) mkRaw;
mangane = pkgs.stdenv.mkDerivation rec {
pname = "mangane";
version = "1.19.4";
src = pkgs.fetchzip {
url = "https://github.com/BDX-town/Mangane/releases/download/${version}/static.zip";
hash = "sha256-Z+3rhqAlPX1sO0Lswky0rMe9pZmdFGWrckHDJnaCyQU=";
stripRoot = false;
};
installPhase = ''
runHook preInstall
mkdir -p $out
cp -r dist/* $out/
runHook postInstall
'';
};
in
{
networking.firewall.interfaces.wg0.allowedTCPPorts = [ 4000 ];
services.postgresql = {
enable = true;
};
services.akkoma = {
enable = true;
frontends = {
primary = {
package = mangane;
name = "mangane";
ref = "stable";
};
admin = {
package = pkgs.akkoma-admin-fe;
name = "admin-fe";
ref = "stable";
};
};
config = {
":pleroma" = {
"Pleroma.Web.Endpoint" = {
http = {
ip = "10.10.0.2";
port = 4000;
};
url = {
host = "akkoma.ellie.town";
scheme = "https";
port = 443;
};
};
"Pleroma.Upload" = {
base_url = "https://media.ellie.town";
filters = map mkRaw [
"Pleroma.Upload.Filter.Exiftool.StripMetadata"
];
};
":instance" = {
name = "ellie town";
email = "wizzeh@protonmail.com";
notify_email = "akkoma@ellie.town";
description = "ellie's akkoma instance";
registrations_open = false;
invites_enabled = false;
account_approval_required = false;
federating = true;
allow_relay = true;
languages = ["en"];
public = false;
safe_dm_mentions = true;
healthcheck = true;
limit = 5000;
};
":configurable_from_database" = true;
":http_security" = {
sts = true;
referrer_policy = "same-origin";
};
":mrf" = {
policies = map mkRaw [
"Pleroma.Web.ActivityPub.MRF.SimplePolicy"
"Pleroma.Web.ActivityPub.MRF.HellthreadPolicy"
"Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy"
];
};
":mrf_hellthread" = {
delist_threshold = 10;
reject_threshold = 20;
};
};
};
};
}

72
services/coturn.nix Normal file
View file

@ -0,0 +1,72 @@
{ config, pkgs, ... }:
{
sops.secrets."coturn/auth_secret_vps" = {
sopsFile = ./secrets/coturn_vps.yaml;
mode = "0400";
owner = "turnserver";
group = "turnserver";
};
security.acme = {
acceptTerms = true;
defaults.email = "wizzeh@protonmail.com";
certs."turn.ellie.town" = {
webroot = "/var/lib/acme/acme-challenges";
};
certs."ellie.town" = { };
};
networking.firewall = {
allowedUDPPorts = [
3478
5349
];
allowedTCPPorts = [
3478
5349
80
443
];
allowedUDPPortRanges = [
{
from = 49152;
to = 65535;
}
];
};
services.coturn = {
enable = true;
realm = "turn.ellie.town";
use-auth-secret = true;
static-auth-secret-file = config.sops.secrets."coturn/auth_secret_vps".path;
cert = "/var/lib/acme/turn.ellie.town/fullchain.pem";
pkey = "/var/lib/acme/turn.ellie.town/key.pem";
listening-ips = [
"0.0.0.0"
"::"
];
listening-port = 3478;
tls-listening-port = 5349;
lt-cred-mech = true;
no-tcp-relay = true;
min-port = 49152;
max-port = 65535;
};
services.nginx = {
enable = true;
virtualHosts."turn.ellie.town" = {
locations."/.well-known/acme-challenge/" = {
root = "/var/lib/acme/acme-challenges";
};
};
};
}

113
services/ergo.nix Normal file
View file

@ -0,0 +1,113 @@
{ lib, pkgs, config, ... }:
let
settingsFormat = pkgs.formats.yaml { };
ergoConfigFile = settingsFormat.generate "ircd.yaml" config.services.ergochat.settings;
in
{
sops.secrets."ergo/oper_password_hash" = {
sopsFile = ./secrets/ergo.yaml;
mode = "0400";
};
networking.firewall.interfaces.wg0.allowedTCPPorts = [ 6667 8097 ];
services.mysql = {
enable = true;
package = pkgs.mariadb;
ensureDatabases = [ "ergochat" ];
ensureUsers = [
{
name = "ergochat";
ensurePermissions = {
"ergochat.*" = "ALL PRIVILEGES";
};
}
];
};
systemd.services.ergochat = {
after = [ "mysql.service" ];
requires = [ "mysql.service" ];
serviceConfig = {
SupplementaryGroups = [ "mysql" ];
RuntimeDirectory = "ergochat";
ExecStartPre = [
# Run as root (+) to read our SOPS secret, then write the patched config
"+${pkgs.writeShellScript "ergochat-inject-secrets" ''
OPER_HASH=$(cat ${config.sops.secrets."ergo/oper_password_hash".path})
sed "s|__OPER_PASSWORD_PLACEHOLDER__|$OPER_HASH|" ${ergoConfigFile} > /run/ergochat/ircd.yaml
chown ergochat:ergochat /run/ergochat/ircd.yaml
chmod 0400 /run/ergochat/ircd.yaml
''}"
];
ExecStart = lib.mkForce "${pkgs.ergochat}/bin/ergo run --conf /run/ergochat/ircd.yaml";
};
};
services.ergochat = {
enable = true;
settings = {
network.name = "ellie.town";
server = {
name = "irc.ellie.town";
enforce-utf8 = true;
listeners = lib.mkForce {
"10.10.0.2:6667" = { };
"10.10.0.2:8097" = { websocket = true; };
};
};
accounts.registration.enabled = false;
accounts.nick-reservation.force-nick-equals-account = true;
accounts.nick-reservation.method = "strict";
datastore.mysql = {
enabled = true;
socket-path = "/run/mysqld/mysqld.sock";
user = "ergochat";
password = "";
history-database = "ergochat";
};
history.persistent = {
enabled = true;
unregistered-channels = false;
registered-channels = "opt-out";
direct-messages = "opt-out";
};
history.restrictions.expire-time = "0";
oper-classes = {
server-admin = {
title = "Server Admin";
capabilities = [
"rehash"
"accreg"
"chanreg"
"kill"
"ban"
"nofakelag"
"relaymsg"
"sajoin"
"samode"
"snomasks"
"history"
"defcon"
"massmessage"
];
};
};
opers = {
ellie = {
class = "server-admin";
whois-line = "is a server administrator";
password = "__OPER_PASSWORD_PLACEHOLDER__";
};
};
};
};
}

33
services/matrix.nix Normal file
View file

@ -0,0 +1,33 @@
{ config, continuwuity, ... }:
{
sops.secrets."coturn/auth_secret_home" = {
sopsFile = ./secrets/coturn_home.yaml;
mode = "0400";
owner = "continuwuity";
group = "continuwuity";
};
networking.firewall.interfaces.wg0.allowedTCPPorts = [ 8008 ];
services.matrix-continuwuity = {
enable = true;
package = continuwuity.packages.x86_64-linux.default;
settings.global = {
server_name = "ellie.town";
new_user_displayname_suffix = "";
allow_registration = true;
address = [ "10.10.0.2" ];
port = [ 8008 ];
turn_uris = [ "turns:turn.ellie.town" ];
turn_secret_file = config.sops.secrets."coturn/auth_secret_home".path;
well_known = {
client = "https://matrix.ellie.town";
server = "matrix.ellie.town:443";
};
};
};
}

View file

@ -0,0 +1,17 @@
coturn:
auth_secret_home: ENC[AES256_GCM,data:ezWNA0NfHYBNq+pfwMBAl5I+g79Bx277NfLEi9irp+ey3EoZKwbRnfJ6gwExskUV8hiUWiCqj/Anbbd7XerF5w==,iv:neGLTFE8vdW6eALsOgrm5/nxqtx/+V1PY7xPG94BQN8=,tag:MXRiLPChtNvMKId/HX1fMA==,type:str]
sops:
age:
- recipient: age126v48dgev6pu3uhe7dtpdhax2yes2ff9u42ke2k2h97e90z8d4psedau7u
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArQnREeGtpTmx4WGtETThz
Nkk1bG9DUkJIWndhVzRkdGxFSUV0NmY2cW5nClg4L0g0T3NpT3U3YStOa3pPajVo
ZTNtTlk4WlpOc1Fra3I1RzRHWmRhbDgKLS0tIElBR1RyMjJxdjAyRm9rZFRDYzVq
V2RqU3VCTnY5UXIvMWRnTnV4MnpCeVUKQSqKoD0CpBYNklgUN400zXwzKCHKExMU
p83UFI1k/89VZCEJm/GJ+QrMl1fRj9zgNxakK4tFghEfNu4tkAub5A==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-09-01T00:22:01Z"
mac: ENC[AES256_GCM,data:3jh/mzPw2Q3Bsc1WYG9T/6VjxwpbP+meUkTZSqMbcIVgob22mAc0VWtRlAjK2183huqro5Od7O9+bOYUQ4s/TnaC8f79vi4aPAjgrFuAn0lCZLlF76d+1jMdBNzlXxdXfL5bkbBEYFnSzzkC+oRcRRW9iU83Nx1nJ4iZ6i1BZKU=,iv:mAmdvfBSXWmL+aZU+nn6IGEEKj1qByLZgeO7KGMcdWk=,tag:Ex3vSlAJdkh85xX1RP3fgg==,type:str]
unencrypted_suffix: _unencrypted
version: 3.10.2

View file

@ -0,0 +1,17 @@
coturn:
auth_secret_vps: ENC[AES256_GCM,data:/kDgDOJ0FwmhcqDRVi8xGNrOK+4n38cPYr3KuKHXZkd7HdCUZSMY3f62BUZdAnIXaASFSQkmWEIjqT2LSCBz6Q==,iv:bRWwG6Yn319W/lxi9k1zSSJRBRNcRFxnpMs0EGXeKIc=,tag:+mPHNJTcYm07RWgSHdwEjA==,type:str]
sops:
age:
- recipient: age1856wmagg3dz4j07alwqnn6d75655t6wcs8glklyjyezhu5p875fq9sez4p
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxc05wa21aWmN1dGZWdWZO
VWg4WDJRMjZJYVZMZUxQSCtMdXRYdjloWmlBCmowdk1VY0pXbTluMHdQYlpwbG82
bmZQZjRvZ1FKVklrc2xRcjBhSHNaeFkKLS0tIFA0VWdCdmxiTzU2R08reTA2WXNm
dUFXU0dLNFlvYXc5cWtKSGhveTErZ1EKGUZDVHYTiufhp3P2IKcBv0QUM9KremOr
vfG+83HK3LkydiOBUtYEuh/2Xv86dJ9Xs/be+HMbUwETNlzAHcyhNA==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-09-01T00:22:26Z"
mac: ENC[AES256_GCM,data:QA7OTsClu+3GXbqxmfN53cUN778ZPfjX9ve+XYF1tjpyVcfSnLB9m0Dnb2xAI9AnZSRCPwqZq0sIAiUWzbVZ6TdgEMU+hEQq8immUg+ZchbKUkfQORgflcJtaRQI2ms6h7oHGPJLuSV3deUBqeRNx1Q31DpnsUcy94Qd/6JIHUg=,iv:33Kf+wt0gwf9wmyPQT2JyrmWPJ+u3jbexEJQ5j0paxk=,tag:g5ycy+E3I97XWecbnXMTEw==,type:str]
unencrypted_suffix: _unencrypted
version: 3.10.2

View file

@ -0,0 +1,17 @@
ergo:
oper_password_hash: ENC[AES256_GCM,data:9INtvkk0bbaaNw+uHqy7/ms9bEQ/AwQoP1WYKdJSS7HJDs5Ki0MhzgFFq2fCf6ovJdDs/cbwDSIjIJXZ,iv:NKUmdz6o5zbKWbNOpiqtM3OaU/tmMpYmgEQlqtNDIsY=,tag:n822gtnHaH/ffL3BvBIX6w==,type:str]
sops:
age:
- recipient: age126v48dgev6pu3uhe7dtpdhax2yes2ff9u42ke2k2h97e90z8d4psedau7u
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwT0RyeVlwYjRtbnRmOHZO
WE8wRXpYS2pQNlhUSUJoS0ZOb0NHajlDZ2tNCkNobnNWTDVCd2xWblNENUNiOWRM
c2svWEdkbUtKSkVOV0MvK2l1MjB1Y0EKLS0tIDhNaFBLZ3JnY2JBY0pjc0RSd1M5
KzYxaXVacEh2MmdNbnhPMEtkSUJ0UHcKfLGMEsdGDcmAnXWK7aGSMr6ZjsQQiT8l
PjTGUyVAWBVGaQEaV33VT7y4rrns+8PmEzfMKl2y12iP6R7Jm9AntA==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-02-10T23:31:43Z"
mac: ENC[AES256_GCM,data:l2C3HPIn5NiidEgS38vsxxkJRtij6mTiqoSQGYaaLc7tKAbJnppLwO7/Ps/3ZwcLVe+Se7/5sEel2u0aXO9nbhs4e0T83x0+wOOW4464tdN66gn3mGcNslWS7LzMl4JbL57Qx6WC9OW1xg3lw67FcPte/j7KWO3xGqG6K5Bqrjo=,iv:nE8ez8udhu0IVg7CGM2/pPxPn4gehWwxbXXwPc8TI0E=,tag:bScTdwfH6edsQOTEgyl6fg==,type:str]
unencrypted_suffix: _unencrypted
version: 3.11.0

View file

@ -0,0 +1,17 @@
wireguard:
private_key: ENC[AES256_GCM,data:2UFwy9hqFIZ61pminFni+w0uJJlKL9vJz4Kh81Z+NtH8K3sgOy5ZVsfH6+0=,iv:CPq9xVzSxo9A7LfzEpyTAthqRQ26aeOOEEOt1izIA9E=,tag:X5HdQD7TnTdmGc227HrJfQ==,type:str]
sops:
age:
- recipient: age126v48dgev6pu3uhe7dtpdhax2yes2ff9u42ke2k2h97e90z8d4psedau7u
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhOW1HbmUrYmdwQ21GQ3FP
UWM0cEFqcDlnQ2U3WldWRDhIOVpMM2tQSlJZCmp0dldJd3pxd0luRitmWk1hVk5w
SWZNZVdrRnV0MEtYSWtYb2xramlOczAKLS0tIDBCTVJ5clozUzZ4dDBXU3dxVG5n
bm9EeXFQNFZQT0tvT05PeXB3bGNxLzQKDXcd3unCZ0SgBrYFehz38ppHFF8T5QqH
TcZMPWHthvGt0OrY6uis/aTBZOBSv/k4sAocTN/e+vR0v9kHMcSw3A==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-02-10T23:31:35Z"
mac: ENC[AES256_GCM,data:XkhjxTIQJBZgSVyeQcx9J3BLux/4+JPnDjZyaL4utZWWP+mkzNmD4UNU/6YpWahouix/ftq9bT2ISV/m5b9+ps9eMHjTLGGjnPB0O4gJx0sEYqY1lOgW51NnqXcogvQ6JEMkO03C/0zyXpn+GEf2Yq0sEnh2XGLTsoojGgS+CTY=,iv:rZPBJnJgOG9l1yWZ2rdzweBda6vwa06P1kkGOlDBVPY=,tag:1QjaoljyCF92YMQB4ysYpQ==,type:str]
unencrypted_suffix: _unencrypted
version: 3.11.0

View file

@ -0,0 +1,17 @@
wireguard:
private_key: ENC[AES256_GCM,data:SY/y0Lo2c3luFgTGMzkmFUuDk/JXD0exrZpIglfvdRto0lci0B7k/iVQv8Y=,iv:2lUzaKj0eEZVbRORd9BuPLK5thX8kxEszfsknBNSP2E=,tag:yABpH9Opuwu5MmtygmMpwA==,type:str]
sops:
age:
- recipient: age1856wmagg3dz4j07alwqnn6d75655t6wcs8glklyjyezhu5p875fq9sez4p
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZYW1LR09adjllcy9OMzVm
T2lGdjlRWU53KyttaElPeGtQY1ZZRWRpOGlRCkp6ckdkSnAwazQwM3NQV1JVOHJp
OTZRWGYyWnovanljbG5FRGI3ZllQckEKLS0tIFR2ZW14QmxDcElWSXAzSzNXS1BS
eW9jQVdEUFpsR1hDRXR0dDhrVFo4VjAKiOPabz4+pb5lwxh0gAhr35cavG9aS/qa
xUndQM6xGfZQwgoOt5LX1Xlo5HBJifTpdUPvMxgPKwHbUyTQY5lKNQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-02-10T23:31:40Z"
mac: ENC[AES256_GCM,data:Z/qrV4V4ZaIhzrIC/SDnCdRFnQEbaYn/zZ/PVhcVkA8qd5WHLHLZMRjOTLPstMo8ll88ua00e6KShlZJLsi1/bSG4WKxTz2Yv/TkGRTD4KE25LmqFLe/70YM1f7SC/QvmlYecVCCWpwXB6rRprt38X2Fzez5AeReZdkdo/IuH1w=,iv:38GOJdQw/9vJQPKHkK/U1HBixqQfo8m0l4HwYOKkm4s=,tag:9Ci8yglwFLQz1RXoXCCN6A==,type:str]
unencrypted_suffix: _unencrypted
version: 3.11.0

31
services/website.nix Normal file
View file

@ -0,0 +1,31 @@
{ pkgs, ... }:
let
site = pkgs.copyPathToStore ../data/service-directory;
in
{
services.avahi = {
enable = true;
nssmdns4 = true;
nssmdns6 = true;
publish = {
enable = true;
addresses = true;
domain = true;
workstation = true;
};
};
services.nginx = {
enable = true;
virtualHosts."service-directory" = {
root = site;
locations."/" = {
index = "index.html";
};
};
};
}

View file

@ -0,0 +1,24 @@
{ config, ... }:
{
sops.secrets."wireguard/private_key" = {
sopsFile = ./secrets/wireguard_home.yaml;
mode = "0400";
};
networking.firewall.allowedUDPPorts = [ 51820 ];
networking.wireguard.interfaces."wg0" = {
ips = [ "10.10.0.2/24" ];
privateKeyFile = config.sops.secrets."wireguard/private_key".path;
listenPort = 51820;
peers = [
{
publicKey = "9itF3RfEP/DhK1C1288njiCQg0AMjjvRsWDYGyNj0ns=";
endpoint = "23.88.105.213:51820";
allowedIPs = [ "10.10.0.1/32" ];
persistentKeepalive = 25;
}
];
};
}

View file

@ -0,0 +1,108 @@
{ lib, pkgs, config, ... }:
{
sops.secrets."wireguard/private_key" = {
sopsFile = ./secrets/wireguard_vps.yaml;
mode = "0400";
};
networking.firewall.allowedTCPPorts = [
80
443
6697
];
networking.firewall.allowedUDPPorts = [ 51820 ];
networking.wireguard.interfaces."wg0" = {
ips = [ "10.10.0.1/24" ];
listenPort = 51820;
privateKeyFile = config.sops.secrets."wireguard/private_key".path;
peers = [
{
publicKey = "s2plHABMTF83iqrCHlQ+o5ieJSAfudx3upm3v77y1DI=";
allowedIPs = [ "10.10.0.2/32" ];
}
];
};
services.nginx = {
enable = true;
recommendedGzipSettings = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
defaultListen = [
{ addr = "0.0.0.0"; }
{ addr = "[::]"; }
];
virtualHosts."matrix.ellie.town" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://10.10.0.2:8008";
};
};
# virtualHosts."akkoma.ellie.town" = {
# enableACME = true;
# forceSSL = true;
# locations."/" = {
# proxyPass = "http://10.10.0.2:4000";
# proxyWebsockets = true;
# extraConfig = ''
# client_max_body_size 16m;
# '';
# };
# };
# virtualHosts."media.ellie.town" = {
# enableACME = true;
# forceSSL = true;
# locations."/" = {
# proxyPass = "http://10.10.0.2:4000";
# extraConfig = ''
# client_max_body_size 16m;
# '';
# };
# };
virtualHosts."irc.ellie.town" = {
enableACME = true;
forceSSL = true;
};
streamConfig = ''
upstream ergo {
server 10.10.0.2:6667;
}
server {
listen 6697 ssl;
ssl_certificate /var/lib/acme/irc.ellie.town/fullchain.pem;
ssl_certificate_key /var/lib/acme/irc.ellie.town/key.pem;
proxy_pass ergo;
}
'';
virtualHosts."ellie.town" = {
enableACME = true;
forceSSL = true;
locations."= /.well-known/matrix/server".extraConfig = ''
default_type application/json;
add_header Access-Control-Allow-Origin *;
return 200 '{"m.server":"matrix.ellie.town:443"}';'';
locations."= /.well-known/matrix/client".extraConfig = ''
default_type application/json;
add_header Access-Control-Allow-Origin *;
return 200 '{"m.homeserver":{"base_url":"https://matrix.ellie.town"}}';'';
};
};
security.acme = {
acceptTerms = true;
defaults.email = "wizzeh@protonmail.com";
};
}