From 9e77ea8e6572729ba268c70515bef4b07dba6ad1 Mon Sep 17 00:00:00 2001 From: Mohammad Rafiq Date: Wed, 9 Jul 2025 03:42:33 +0800 Subject: [PATCH] feat(nix): add librechat module and enable for server WARNING: This commit includes new files and secrets modifications. --- flake.lock | 17 +++++ flake.nix | 2 + nix/lib/services.nix | 2 +- nix/manifest.nix | 4 ++ nix/modules/server/web-apps/librechat.nix | 87 +++++++++++++++++++++++ secrets/keys.yaml | 5 +- secrets/librechat.yaml | 20 ++++++ 7 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 nix/modules/server/web-apps/librechat.nix create mode 100644 secrets/librechat.yaml diff --git a/flake.lock b/flake.lock index 7567fb2..507ff62 100644 --- a/flake.lock +++ b/flake.lock @@ -471,6 +471,7 @@ "nixpkgs": "nixpkgs", "nur": "nur", "nvf": "nvf", + "rrvsh-nixpkgs": "rrvsh-nixpkgs", "sops-nix": "sops-nix", "stable-diffusion-webui-nix": "stable-diffusion-webui-nix", "stylix": "stylix", @@ -478,6 +479,22 @@ "text": "text" } }, + "rrvsh-nixpkgs": { + "locked": { + "lastModified": 1750146550, + "narHash": "sha256-vFNbONVWIdYBqlKZoJScDRjnQ/euDmVqgCL2ebnsu7U=", + "owner": "rrvsh", + "repo": "nixpkgs", + "rev": "d7fa95990fd890bbd17ca8361f5d4e4935512c75", + "type": "github" + }, + "original": { + "owner": "rrvsh", + "ref": "librechat-module", + "repo": "nixpkgs", + "type": "github" + } + }, "sops-nix": { "inputs": { "nixpkgs": [ diff --git a/flake.nix b/flake.nix index 7736b6b..1f706c3 100644 --- a/flake.nix +++ b/flake.nix @@ -15,6 +15,8 @@ systems.url = "github:nix-systems/default"; # nixos-unstable provides a binary cache for all packages. nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + # My fork for random shit + rrvsh-nixpkgs.url = "github:rrvsh/nixpkgs/librechat-module"; # home-manager manages our user packages and dotfiles home-manager = { url = "github:nix-community/home-manager"; diff --git a/nix/lib/services.nix b/nix/lib/services.nix index a836682..7ec6025 100644 --- a/nix/lib/services.nix +++ b/nix/lib/services.nix @@ -41,7 +41,7 @@ in server.ddns.domains = singleton (mkRootDomain cfg.domain); server.web-servers.nginx.proxies = singleton { source = cfg.domain; - target = "http://${config.hostname}:${toString cfg.port}"; + target = "http://${config.networking.hostName}:${toString cfg.port}"; }; }; diff --git a/nix/manifest.nix b/nix/manifest.nix index d207af5..9c5e4be 100644 --- a/nix/manifest.nix +++ b/nix/manifest.nix @@ -63,6 +63,10 @@ mysql.enable = true; postgresql.enable = true; }; + web-apps = { + librechat.enable = true; + librechat.domain = "chat.bwfiq.com"; + }; }; }; }; diff --git a/nix/modules/server/web-apps/librechat.nix b/nix/modules/server/web-apps/librechat.nix new file mode 100644 index 0000000..63d2efa --- /dev/null +++ b/nix/modules/server/web-apps/librechat.nix @@ -0,0 +1,87 @@ +{ + lib, + inputs, + config, + ... +}: +let + inherit (lib.lists) singleton; + inherit (config.flake.lib.options) mkStrOption; + inherit (config.flake.lib.services) mkWebApp; + inherit (config.flake.paths) secrets; +in +{ + flake.modules.nixos.default = + { config, ... }: + let + cfg = config.server.web-apps.librechat; + upstreamCfg = config.services.librechat; + in + mkWebApp { + inherit config; + name = "librechat"; + defaultPort = 3080; + persistDirs = singleton { + directory = upstreamCfg.dataDir; + inherit (upstreamCfg) user group; + }; + extraOptions.mongodbURI = mkStrOption "mongodb://${config.networking.hostName}:27017/LibreChat"; + extraConfig = { + sops.secrets = { + "librechat/creds_key".sopsFile = secrets + "/librechat.yaml"; + "librechat/creds_iv".sopsFile = secrets + "/librechat.yaml"; + "librechat/jwt_secret".sopsFile = secrets + "/librechat.yaml"; + "librechat/jwt_refresh_secret".sopsFile = secrets + "/librechat.yaml"; + "keys/gemini".sopsFile = secrets + "/keys.yaml"; + "keys/openrouter".sopsFile = secrets + "/keys.yaml"; + }; + services.librechat = { + enable = true; + openFirewall = true; + inherit (cfg) port; + env = { + HOST = "0.0.0.0"; + ALLOW_REGISTRATION = "true"; + NO_INDEX = "true"; + MONGO_URI = cfg.mongodbURI; + DOMAIN_CLIENT = cfg.domain; + DOMAIN_SERVER = cfg.domain; + ENDPOINTS = "anthropic,agents,google"; + }; + credentials = { + CREDS_KEY = config.sops.secrets."librechat/creds_key".path; + CREDS_IV = config.sops.secrets."librechat/creds_iv".path; + JWT_SECRET = config.sops.secrets."librechat/jwt_secret".path; + JWT_REFRESH_SECRET = config.sops.secrets."librechat/jwt_refresh_secret".path; + OPENROUTER_KEY = config.sops.secrets."keys/openrouter".path; + GOOGLE_KEY = config.sops.secrets."keys/gemini".path; + }; + settings = { + version = "1.1.4"; + cache = true; + endpoints.custom = [ + { + name = "OpenRouter"; + apiKey = "\${OPENROUTER_KEY}"; + baseURL = "https://openrouter.ai/api/v1"; + models.default = [ "meta-llama/llama-3-70b-instruct" ]; + models.fetch = true; + titleConvo = true; + titleModel = "current_model"; + modelDisplayLabel = "OpenRouter"; + } + ]; + interface = { + privacyPolicy = { + externalUrl = "https://librechat.ai/privacy-policy"; + openNewTab = true; + }; + }; + }; + }; + }; + } + // { + imports = singleton "${inputs.rrvsh-nixpkgs}/nixos/modules/services/web-apps/librechat.nix"; + }; +} diff --git a/secrets/keys.yaml b/secrets/keys.yaml index 7b3f094..5389445 100644 --- a/secrets/keys.yaml +++ b/secrets/keys.yaml @@ -1,5 +1,6 @@ keys: cloudflare: ENC[AES256_GCM,data:p2IISOuU/ShoifW5OFY/6Bi6PI0iIiQoBfnV512f2z84U9QS/KEhzA==,iv:5AkwtNAK8mD2DbvXCtTeNeIrpF/GIsSyOYxy8G4Jsqo=,tag:u2xJcRBR5WTMWdzupx4tbQ==,type:str] + gemini: ENC[AES256_GCM,data:GwXVBsQdLesgP6PUZJRrLO5u6jd6XYFv9vjNTsojOwaWlxkDeRos,iv:w6Uz6j/MfpgQdIRYqJCneWqTsA+JEsB/T3cySVY2k3c=,tag:JY+LDar1UzC6qLKLichKnQ==,type:str] sops: age: - recipient: age12l33pas8eptwjc7ewux3d8snyzfzwz0tn9qg5kw8le79fswmjgjqdjgyy6 @@ -11,7 +12,7 @@ sops: T3VyZXZnaGZaMVBnVko2Tlc2S3FpdDQKRiHCOtkHKugfquQfYkk4o9SMtZlo1CqZ 3i9+9Z516KS1+ERTklBUzZDBRZISY0c2nluO+tn71wnKAMIxetKryQ== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-07-08T18:05:51Z" - mac: ENC[AES256_GCM,data:YkQqkKw+OEKFtORp+WDwclazCk166JndSstoiJYbpdAKLxvEQLdScaGin7j0fIoeTCY1CytzAHAUt8iraZeHscIkPdz/3WGPOa23dt56ZEd+rK/OL+XIuOCsbz08+yVWgJWeeEeMZ8IinwA46kVVYI3+p4FxDBDXBvhl9x7+CG4=,iv:YHmM2mk2fKwefydYV29eEMaXlyHVKwyDZAHCnbnKb3Y=,tag:Ksb8N/1pnzDGpyGj8Z0fhg==,type:str] + lastmodified: "2025-07-08T19:37:17Z" + mac: ENC[AES256_GCM,data:Yel+wX5FjCntqxbvStDGs/Zzbgh6Ht2/7DmV4Xu8R/rVzjzGoO4DziEjpzaTLNxXLXbjfdOuucU9YPmdqSlf9FpjiB488Fjn7GMP53V74c7RiYZq3s+vpVIdnkEA52xgYqPY6sOJAoujjHJM3uRYgOM0XwkKONBfF5FrO6WdbvU=,iv:wg5U1V94oPUSnjCgMewW6L/dftOPWOgQl9qLF6o8Gps=,tag:SMqKnAKJBjCGHjPanjDk4A==,type:str] unencrypted_suffix: _unencrypted version: 3.10.2 diff --git a/secrets/librechat.yaml b/secrets/librechat.yaml new file mode 100644 index 0000000..d6668cd --- /dev/null +++ b/secrets/librechat.yaml @@ -0,0 +1,20 @@ +librechat: + creds_key: ENC[AES256_GCM,data:sELKgqif9ec6VV0Q9OVk8IbUAI5noPtUB1b1WrPvxjDzJODd9YoJHWiH+N0vwORje5LiuzqZ/0Kn/UMdPfy3qw==,iv:SFFW+P0vxy4s6TkaAyCNLLXLIBrdi8oMkm7Q/Vec/yk=,tag:ZNC0vMdyh+S204Qr0itvnw==,type:str] + creds_iv: ENC[AES256_GCM,data:h8RHcW7zt8CnKrYDGxlN/H9Wim4KpLaiFl2E2AK+YJY=,iv:xRctbyBFprN6Y1Lvk08EpzZNXa0owYCph+wqcOAR/Gw=,tag:ZdA0ibjyH1Y6DAd23mfJRQ==,type:str] + jwt_secret: ENC[AES256_GCM,data:mXMi0EenuU1EIZWUyLE3wkVTouJk2QPXIKV38sfwbKfjdc28GgdsaWtunaSpD4uYBrWCv1rXq5qj18ohlAKs/g==,iv:ZWZWgYzVQh+kRN4+EEBFdWc4aWGq5IDtlEVde9mzS7I=,tag:BmWQN9yI92RHJMy/pt8rRg==,type:str] + jwt_refresh_secret: ENC[AES256_GCM,data:iw+/E0wb2Ih1iQOaCCXBN5tj98Z2CdpaJMYOiuoTanjW7bvJXGfVObXKTBTtRs1P4TzCc4qK7mes5Sa6oajBpg==,iv:3mr3PYAjJ3bncATgfSwEyrIM2YioSfSu38NUfDmk6zs=,tag:RIYJ1YBaQVpwAmlo3CKg2Q==,type:str] +sops: + age: + - recipient: age12l33pas8eptwjc7ewux3d8snyzfzwz0tn9qg5kw8le79fswmjgjqdjgyy6 + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArcko2MmowTkpKaDJTdjZE + NVdzbklXZngxaHhrbkhGTXlCNDkzNml4dnlZCjRWOFZCSWRKenZzN0dhYXplVzh0 + OVdaUnRkS2dIYklFS2dwUXVxaElxNkkKLS0tICtxZDV0a2hIaUM2NFBwOGwxcklz + YWJyU0VKRXFxT29TSjR1KzE0ZHJGQncKrX5Sujd617WgFDYA5r63K4ZwoJpP9m8M + xexbGVHAeSyWNjOG7x5A9gYC1/dG3NY2l5xoITn0NKi68ZEfGD/J3w== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2025-07-08T19:37:50Z" + mac: ENC[AES256_GCM,data:LBkUPMR8D8+IVUugWzK4a51d0lkGJqnG5D9EkHC4aGXcuSpxpxkbUDXWsqK3u1FxxfCnR87ZhD+UGd3OV6Wvsl9/v968eC/3jxuZALnOgUGcTyUayo8qLq1J6HEFUDoUoH2tk/SF0Cn2r34fkcUd1NtRdQX+C0Zsc8Tk0zIRA8U=,iv:aUvg409sogxRBgYzNECW5eH7GsSAsYY9AHWmL0UD6PA=,tag:0pMoXeuF6DLCyIdDVsPmGA==,type:str] + unencrypted_suffix: _unencrypted + version: 3.10.2