feat: moar nix

This commit is contained in:
nullishamy 2025-06-18 21:41:53 +01:00
parent 7e3bf4d6f3
commit 2025eb74a4
Signed by: amy
SSH key fingerprint: SHA256:WmV0uk6WgAQvDJlM8Ld4mFPHZo02CLXXP5VkwQ5xtyk
23 changed files with 1408 additions and 15 deletions

140
config/nix01.nix Normal file
View file

@ -0,0 +1,140 @@
{ modulesPath, pkgs, config, lib, ... }:
{
imports = [
# Include the default lxd configuration.
"${modulesPath}/virtualisation/proxmox-lxc.nix"
# Include the container-specific autogenerated configuration.
./lxd.nix
./services/sharkey.nix
];
networking = {
dhcpcd.enable = false;
useDHCP = false;
useHostResolvConf = false;
firewall.enable = false;
nameservers = ["192.168.1.155" "1.1.1.1"];
};
environment.systemPackages = with pkgs; [
git
curl
vim
];
services.postgresql = {
enable = true;
enableTCPIP = true;
ensureDatabases = [ "authentik" "blog" "forgejo" "infisical" "sharkey" ];
ensureUsers = [
{
name = "authentik";
ensureDBOwnership = true;
}
{
name = "blog";
ensureDBOwnership = true;
}
{
name = "forgejo";
ensureDBOwnership = true;
}
{
name = "infisical";
ensureDBOwnership = true;
}
{
name = "sharkey";
ensureDBOwnership = true;
}
];
authentication = pkgs.lib.mkOverride 10 ''
# type database DBuser auth-method
local all all trust
# ipv4
host all all 127.0.0.1/32 trust
# ipv6
host all all ::1/128 trust
# LAN
host all all 192.168.0.0/16 trust
'';
};
services.calibre-server = {
enable = true;
auth = {
enable = true;
userDb = "/var/lib/calibre-server/.config/calibre/server-users.sqlite";
};
libraries = [
"/var/lib/calibre-server"
];
};
services.pgadmin = {
enable = true;
initialEmail = "hello@amyerskine.me";
initialPasswordFile = config.age.secrets."pgadmin.password".path;
};
services.nginx.enable = true;
services.nginx.virtualHosts."pg.nix01.cluster" = {
locations."/" = {
proxyPass = "http://127.0.0.1:5050";
proxyWebsockets = true;
};
};
services.nginx.virtualHosts."sharkey.nix01.cluster" = {
locations."/" = {
proxyPass = "http://127.0.0.1:3001";
proxyWebsockets = true;
};
};
services.nginx.virtualHosts."calibre.nix01.cluster" = {
locations."/" = {
proxyPass = "http://127.0.0.1:8080";
proxyWebsockets = true;
};
};
services.sharkey = {
enable = true;
domain = "fedi.amy.mov";
package = (pkgs.callPackage ./services/sharkey-pkg.nix {});
database = {
passwordFile = config.age.secrets."sharkey.dbpass".path;
};
redis = {
passwordFile = config.age.secrets."sharkey.redispass".path;
};
meilisearch = {
createLocally = false;
};
settings = {
id = "aidx";
port = 3001;
maxNoteLength = 8192;
maxFileSize = 1024 * 1024 * 1024;
proxyRemoteFiles = true;
# at the suggestion of Sharkey maintainers,
# this allows the server to run multiple workers
# and without this (and postgres tuning), the instance runs slowly
clusterLimit = 3;
signToActivityPubGet = true;
CheckActivityPubGetSigned = false;
};
};
system.stateVersion = "24.11"; # Did you read the comment?
}

90
config/nix02.nix Normal file
View file

@ -0,0 +1,90 @@
{ modulesPath, pkgs, unstable, config, ... }:
{
imports = [
# Include the default lxd configuration.
"${modulesPath}/virtualisation/proxmox-lxc.nix"
# Include the container-specific autogenerated configuration.
./lxd.nix
];
networking = {
dhcpcd.enable = false;
useDHCP = false;
useHostResolvConf = false;
firewall.enable = false;
nameservers = ["192.168.1.155" "1.1.1.1"];
};
environment.systemPackages = with pkgs; [
git
curl
vim
];
services.nginx = {
enable = true;
};
services.nginx.virtualHosts."forgejo.nix02.cluster" = {
locations."/" = {
proxyPass = "http://127.0.0.1:8312";
proxyWebsockets = true;
};
};
services.nginx.virtualHosts."forge.amy.mov" = {
locations."/" = {
proxyPass = "http://127.0.0.1:8312";
proxyWebsockets = true;
};
};
services.forgejo = {
enable = true;
package = unstable.forgejo;
settings = {
server = {
HTTP_PORT = 8312;
ROOT_URL = "https://forge.amy.mov";
};
};
database = {
createDatabase = false;
type = "postgres";
host = "nix01.cluster";
name = "forgejo";
user = "forgejo";
passwordFile = config.age.secrets."forgejo.dbpass".path;
};
};
services.authentik = {
enable = true;
environmentFile = config.age.secrets."authentik.env".path;
nginx = {
enable = true;
enableACME = false;
host = "auth.nix02.cluster";
};
createDatabase = false;
settings = {
postgresql = {
host = "nix01.cluster";
user = "authentik";
password = "authentik";
name = "authentik";
};
disable_startup_analytics = true;
avatars = "initials";
};
};
system.stateVersion = "24.11"; # Did you read the comment?
}

View file

@ -7,6 +7,8 @@
# Include the container-specific autogenerated configuration.
./lxd.nix
./services/opengist.nix
./services/kener.nix
#./services/upsnap.nix
];
networking = {
@ -22,6 +24,36 @@
vim
];
# services.upsnap = {
# enable = true;
# };
services.kener = {
enable = true;
};
# Would like to use my PG DB for this but the service just doesn't
# support DBs that are hosted outside of the Nix box
services.writefreely = {
enable = true;
host = "write.amy.mov";
database = {
type = "sqlite3";
};
admin = {
name = "amy";
};
settings = {
server = {
bind = "0.0.0.0";
port = 8123;
};
};
};
services.opengist = {
enable = true;
config = ./opengist.yml;

View file

@ -0,0 +1,45 @@
{ lib, buildNpmPackage, fetchFromGitHub }:
let
pname = "kener";
version = "3.2.12";
in
buildNpmPackage rec {
inherit pname version;
src = fetchFromGitHub {
owner = "rajnandan1";
repo = pname;
rev = "e6b5600a4726f719c2228d7d2da5a919e4bc15a3";
hash = "sha256-UBmt7SYZ2WukJvT1TOcwVr/L8RZVpVkLamCNV/xC8L4=";
};
npmDepsHash = "sha256-csB6qMJt3wBQyyWrK31F0FaRck3rt0JiH/lv77f+570=";
npmBuild = "npm run build";
# Copy src because the main runner (hosts the API etc) calls some stuff from it
installPhase = ''
mkdir $out
cp -R src/ $out
cp -R node_modules/ $out
cp -R build/ $out
cp -R migrations/ $out
cp -R seeds/ $out
cp -R static/ $out
cp -R knexfile.js $out
sed -i "s@./migrations@$out/migrations@g" $out/knexfile.js
sed -i "s@./seeds@$out/seeds@g" $out/knexfile.js
cp main.js $out
'';
meta = with lib; {
description = "Stunning status pages, batteries included!";
homepage = "https://kener.ing";
license = licenses.mit;
maintainers = with maintainers; [ nullishamy ];
mainProgram = "";
};
}

44
config/services/kener.nix Normal file
View file

@ -0,0 +1,44 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.kener;
src = builtins.fetchTarball {
url = "https://github.com/rajnandan1/kener/archive/refs/tags/3.2.12.tar.gz";
sha256 = "sha256:0a301jz8vqi2bd93k4lyabinshvadz084jfnkzrmxqrfr7w9gqbl";
};
in {
options = {
services.kener = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable Kener.
'';
};
};
};
config = mkIf cfg.enable {
systemd.services.kener =
let
kener = (pkgs.callPackage ./kener-pkg.nix {});
in {
path = [
pkgs.nodejs
pkgs.unixtools.ping
];
environment = {
"DATABASE_URL" = "sqlite:///opt/kener/kener.db";
"ORIGIN" = "https://amy.mov";
"KENER_SECRET_KEY" = "my-super-strong-key";
};
name = "kener.service";
enable = true;
script = "node ${kener}/main.js";
description = "Kener";
};
};
}

View file

@ -0,0 +1,141 @@
{
lib,
stdenv,
fetchFromGitLab,
bash,
makeWrapper,
jemalloc,
ffmpeg-headless,
python3,
pkg-config,
glib,
vips,
pnpm_9,
nodejs,
pixman,
pango,
cairo,
}:
stdenv.mkDerivation (finalAttrs: {
pname = "sharkey";
version = "2025.4.2";
src = fetchFromGitLab {
domain = "activitypub.software";
owner = "TransFem-org";
repo = "Sharkey";
rev = finalAttrs.version;
fetchSubmodules = true;
hash = "sha256-gCZY9d/YLNQRGVFqsK7//UDiS19Jtqa7adGliIdE+4c=";
};
pnpmDeps = pnpm_9.fetchDeps {
inherit (finalAttrs) src pname;
hash = "sha256-2bt/sHKGNIjKfOvZ6DCXvdJcKoOJX/ueWdLULlYK3YU=";
};
nativeBuildInputs = [
pnpm_9.configHook
nodejs
makeWrapper
python3
pkg-config
];
buildInputs = [
glib
vips
pixman
pango
cairo
];
# This environment variable is required for `node-gyp`, which is used by some native dependencies we build below.
# Without it, `node-gyp` won't know where the source code for node.js is, and will fail to download it instead.
npm_config_nodedir = nodejs;
# Sharkey depends on some packages with native code that needs to be built.
# These aren't built by default, so we need to run their build scripts manually.
#
# The tricky thing is that not all of them required for Sharkey to "successfully" build.
# They will trick you, make you think that Sharkey works, and successfully run your databse migrations.
# And then, when your instance tries to run, it will crash with an error like:
#
# Error [ERR_INTERNAL_ASSERTION]: This is caused by either a bug in Node.js or incorrect usage of Node.js internals.
# Please open an issue with this stack trace at https://github.com/nodejs/node/issues
#
# If you see that error, IT IS LYING TO YOU. It means Sharkey added a new dependency that required native code to be built.
# Figure out what is the new dependency. You can ask in their discord, and they'll probably tell you.
# And then, build it in the `buildPhase` below.
buildPhase = ''
runHook preBuild
(
cd node_modules/.pnpm/node_modules/v-code-diff
pnpm run postinstall
)
(
cd node_modules/.pnpm/node_modules/re2
pnpm run rebuild
)
(
cd node_modules/.pnpm/node_modules/sharp
pnpm run install
)
(
cd node_modules/.pnpm/node_modules/canvas
pnpm run install
)
pnpm build
runHook postBuild
'';
installPhase = ''
runHook preInstall
mkdir -p $out/Sharkey
ln -s /var/lib/sharkey $out/Sharkey/files
ln -s /run/sharkey $out/Sharkey/.config
cp -r * $out/Sharkey
makeWrapper ${lib.getExe pnpm_9} $out/bin/sharkey \
--chdir $out/Sharkey \
--prefix PATH : ${
lib.makeBinPath [
bash
pnpm_9
nodejs
]
} \
--prefix LD_LIBRARY_PATH : ${
lib.makeLibraryPath [
jemalloc
ffmpeg-headless
stdenv.cc.cc.lib
]
}
runHook postInstall
'';
passthru = {
inherit (finalAttrs) pnpmDeps;
};
meta = {
description = "🌎 A Sharkish microblogging platform 🚀";
homepage = "https://joinsharkey.org";
license = lib.licenses.gpl3Only;
maintainers = with lib.maintainers; [ sodiboo ];
platforms = [
"x86_64-linux"
"aarch64-linux"
];
mainProgram = "sharkey";
};
})

246
config/services/sharkey.nix Normal file
View file

@ -0,0 +1,246 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.sharkey;
createDB = cfg.database.host == "127.0.0.1" && cfg.database.createLocally;
createRedis = cfg.redis.host == "127.0.0.1" && cfg.redis.createLocally;
createMeili = cfg.meilisearch.host == "127.0.0.1" && cfg.meilisearch.createLocally;
createMeiliKey = cfg.meilisearch.key == lib.fakeSha256;
settingsFormat = pkgs.formats.yaml { };
configFile = settingsFormat.generate "sharkey-config.yml" cfg.settings;
in
{
options = {
services.sharkey = with lib; {
enable = mkEnableOption "sharkey";
domain = mkOption {
type = lib.types.str;
example = "fedi.amy.mov";
};
package = lib.mkOption {
type = lib.types.package;
defaultText = lib.literalExpression "pkgs.sharkey";
description = "Sharkey package to use.";
};
database = {
createLocally = mkOption {
type = lib.types.bool;
default = true;
};
host = mkOption {
type = lib.types.str;
default = "127.0.0.1";
};
port = mkOption {
type = lib.types.port;
default = 5432;
};
name = mkOption {
type = lib.types.str;
default = "sharkey";
};
passwordFile = mkOption {
description = ''
Path to a file containing the password for the database user.
This file must be readable by the `sharkey` user.
If creating a database locally, it must also be readable by the `postgres` user.
'';
type = lib.types.path;
example = "/run/secrets/sharkey-db-password";
};
};
redis = {
createLocally = mkOption {
type = lib.types.bool;
default = true;
};
host = mkOption {
type = lib.types.str;
default = "127.0.0.1";
};
port = mkOption {
type = lib.types.port;
default = 6379;
};
passwordFile = mkOption {
description = ''
Path to a file containing the password for the redis server.
This file must be readable by the `sharkey` user.
'';
type = lib.types.path;
example = "/run/secrets/sharkey-redis-password";
};
};
meilisearch = {
createLocally = mkOption {
type = lib.types.bool;
default = true;
};
host = mkOption {
type = lib.types.str;
default = "127.0.0.1";
};
port = mkOption {
type = lib.types.port;
default = 7700;
};
index = mkOption {
type = lib.types.str;
default = replaceStrings [ "." ] [ "_" ] cfg.domain;
};
key = mkOption {
type = lib.types.str;
default = "$MEILI_MASTER_KEY";
};
};
settings = mkOption {
type = settingsFormat.type;
default = { };
description = ''
Configuration for Sharkey, see
<link xlink:href="https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/.config/example.yml"/>
for supported settings.
'';
};
};
};
config = lib.mkIf cfg.enable {
documentation.enable = false;
assertions = [
{
assertion = createMeiliKey -> createMeili;
message = "services.sharkey.meilisearch.key is required to be set when connecting to a remote meilisearch instance";
}
];
services.sharkey.settings = {
url = "https://${cfg.domain}/";
db.host = cfg.database.host;
db.port = cfg.database.port;
db.db = cfg.database.name;
db.user = cfg.database.name;
db.pass = "$SHARKEY_DB_PASSWORD";
redis.host = cfg.redis.host;
redis.port = cfg.redis.port;
redis.pass = "$SHARKEY_REDIS_PASSWORD";
meilisearch.host = cfg.meilisearch.host;
meilisearch.port = cfg.meilisearch.port;
meilisearch.apiKey = cfg.meilisearch.key;
meilisearch.index = cfg.meilisearch.index;
meilisearch.ssl = !createMeili;
meilisearch.scope = "global";
};
environment.etc."sharkey.yml".source = configFile;
systemd.services.sharkey = {
after =
[ "network-online.target" ]
++ lib.optionals createDB [ "postgresql.service" ]
++ lib.optionals createRedis [ "redis-sharkey.service" ]
++ lib.optionals createMeili [ "meilisearch.service" ];
wantedBy = [ "multi-user.target" ];
preStart = ''
SHARKEY_DB_PASSWORD="$(cat ${lib.escapeShellArg cfg.database.passwordFile})" \
SHARKEY_REDIS_PASSWORD="$(cat ${lib.escapeShellArg cfg.redis.passwordFile})" \
${pkgs.envsubst}/bin/envsubst -i "${configFile}" > $MISSKEY_CONFIG_YML
'';
environment.MISSKEY_CONFIG_YML = "/run/sharkey/config.yml";
environment.NODE_ENV = "production";
serviceConfig = {
EnvironmentFile = lib.mkIf (
config.services.meilisearch.masterKeyEnvironmentFile != null
) config.services.meilisearch.masterKeyEnvironmentFile;
Type = "simple";
User = "sharkey";
StateDirectory = "sharkey";
StateDirectoryMode = "0700";
RuntimeDirectory = "sharkey";
RuntimeDirectoryMode = "0700";
ExecStart = "${cfg.package}/bin/sharkey migrateandstart";
TimeoutSec = 60;
Restart = "always";
StandardOutput = "journal";
StandardError = "journal";
SyslogIdentifier = "sharkey";
};
};
services.postgresql = lib.mkIf createDB {
enable = true;
settings.port = cfg.database.port;
ensureUsers = [
{
name = cfg.database.name;
ensureDBOwnership = true;
}
];
ensureDatabases = [ cfg.database.name ];
};
services.redis = lib.mkIf createRedis {
servers.sharkey = {
enable = true;
user = "sharkey";
bind = "127.0.0.1";
port = cfg.redis.port;
requirePassFile = cfg.redis.passwordFile;
};
};
systemd.services.postgresql.postStart = lib.mkIf createDB ''
$PSQL -tAc "ALTER ROLE ${cfg.database.name} WITH ENCRYPTED PASSWORD '$(printf "%s" $(cat ${cfg.database.passwordFile} | tr -d "\n"))';"
'';
services.meilisearch = lib.mkIf createMeili {
enable = true;
listenAddress = "127.0.0.1";
listenPort = cfg.meilisearch.port;
environment = "production";
};
users.users.sharkey = {
group = "sharkey";
isSystemUser = true;
home = "/run/sharkey";
packages = [ cfg.package ];
};
users.groups.sharkey = { };
};
meta.maintainers = with lib.maintainers; [ sodiboo ];
}

View file

@ -0,0 +1,43 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.upsnap;
src = pkgs.fetchzip {
url = "https://github.com/seriousm4x/UpSnap/releases/download/5.0.4/UpSnap_5.0.4_linux_amd64.zip";
sha256 = "sha256:1qlav9if6f2c50rzakyilxgzmq2c5bzcs6lx1w7sffxhl440nxhs";
};
in {
options = {
services.upsnap = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable UpSnap.
'';
};
bind = mkOption {
type = types.str;
default = "0.0.0.0:8090";
description = ''
The bind address/port
'';
};
};
};
config = mkIf cfg.enable {
systemd.services.upsnap = {
environment = {
"HOME" = "/opt/upsnap";
};
name = "upsnap.service";
enable = true;
script = "${src} serve --http ${cfg.bind}";
description = "UpSnap";
};
};
}

94
config/tf.nix Normal file
View file

@ -0,0 +1,94 @@
{ lib, ... }:
let
# Set to false to boostrap the containers
addNetworking = true;
pmHost = "https://192.168.1.100";
creds = {
ctPassword = "password";
};
sshKeys = ''
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDTbclOyOwIAPgVE/v5lIuf0P+Tq/Qkw3+GFa4YuRaCC amy@nixon
'';
templates = {
nixos = "nas-main:vztmpl/nixos-system-x86_64-linux.tar.xz";
};
in {
terraform = {
required_providers = {
proxmox = {
source = "telmate/proxmox";
version = "3.0.2-rc01";
};
};
};
provider.proxmox = {
pm_api_url = "${pmHost}:8006/api2/json";
pm_tls_insecure = true;
pm_user = "root@pam";
pm_password = "";
};
resource.proxmox_lxc = {
nix01 = {
target_node = "strawberry";
hostname = "nix01";
ostemplate = templates.nixos;
password = creds.ctPassword;
unprivileged = true;
swap = 1024;
ostype = "nixos";
cmode = "console";
rootfs = {
storage = "local-lvm";
size = "16G";
};
network = lib.mkIf addNetworking {
name = "eth0";
bridge = "vmbr0";
ip = "192.168.1.220/32";
gw = "192.168.1.1";
firewall = false;
};
features = {
nesting = true;
};
};
nix02 = {
target_node = "strawberry";
hostname = "nix02";
ostemplate = templates.nixos;
password = creds.ctPassword;
unprivileged = true;
swap = 1024;
ostype = "nixos";
cmode = "console";
rootfs = {
storage = "local-lvm";
size = "16G";
};
network = lib.mkIf addNetworking {
name = "eth0";
bridge = "vmbr0";
ip = "192.168.1.221/32";
gw = "192.168.1.1";
firewall = false;
};
features = {
nesting = true;
};
};
};
}