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

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