From c923126effb701a2f6cd8259d763301bca309bf9 Mon Sep 17 00:00:00 2001 From: Katharina Heidenreich Date: Sat, 4 Apr 2026 17:22:37 +0200 Subject: [PATCH 01/10] feat: add redirect --- config/endpoints/vikunja.nix | 11 +++++++++++ intermediate/nginx.nix | 19 +++++++++++++++++++ validation/endpoints.nix | 25 +++++++++++++++++++++++-- 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/config/endpoints/vikunja.nix b/config/endpoints/vikunja.nix index 0212f71..ec801ba 100644 --- a/config/endpoints/vikunja.nix +++ b/config/endpoints/vikunja.nix @@ -16,4 +16,15 @@ in proxyWebsockets = true; }; } + { + type = "redirect"; + domain = "wekan.${net.devices.remote_proxy.domain}"; + endpoint = "/"; + force_ssl = true; + port = 443; + content = { + target = "https://vikunja.${net.devices.remote_proxy.domain}/"; + status = 301; + }; + } ] \ No newline at end of file diff --git a/intermediate/nginx.nix b/intermediate/nginx.nix index e44f4da..3b17093 100644 --- a/intermediate/nginx.nix +++ b/intermediate/nginx.nix @@ -205,6 +205,25 @@ let proxyWebsockets = route.content.proxyWebsockets; }; } + else if route.type == "redirect" then + let + redirectTarget = + if builtins.isString route.content then + route.content + else + route.content.target; + redirectStatus = + if builtins.isAttrs route.content && route.content ? status then + route.content.status + else + 301; + in + { + name = "= ${route.endpoint}"; + value = { + return = "${toString redirectStatus} ${redirectTarget}"; + }; + } else if route.type == "inline" then let inlineBody = diff --git a/validation/endpoints.nix b/validation/endpoints.nix index cf60b0f..50c7c7d 100644 --- a/validation/endpoints.nix +++ b/validation/endpoints.nix @@ -6,6 +6,7 @@ let allowedWebContentKeys = [ "type" "files" ]; allowedWebFileKeys = [ "path" "filePath" "contentType" "status" ]; allowedInlineContentKeys = [ "body" "contentType" "status" ]; + allowedRedirectContentKeys = [ "target" "status" ]; ensureNoUnknownKeys = context: obj: allowedKeys: let @@ -38,10 +39,10 @@ let else throw "Endpoint at index ${toString index} must define type as a string."; _type = - if lib.elem typeValue [ "proxy" "web" "inline" ] then + if lib.elem typeValue [ "proxy" "web" "inline" "redirect" ] then null else - throw "Endpoint at index ${toString index} type must be 'proxy', 'web', or 'inline'."; + throw "Endpoint at index ${toString index} type must be 'proxy', 'web', 'inline', or 'redirect'."; _domain = if endpoint ? domain && builtins.isString endpoint.domain && endpoint.domain != "" then null @@ -131,6 +132,26 @@ let ) filesValue; in null + else if typeValue == "redirect" then + if builtins.isString contentValue then + null + else if builtins.isAttrs contentValue then + let + ____ = ensureNoUnknownKeys "Redirect content at endpoint index ${toString index}" contentValue allowedRedirectContentKeys; + _____ = + if contentValue ? target && builtins.isString contentValue.target && contentValue.target != "" then + null + else + throw "Redirect endpoint at index ${toString index} must define content.target as a non-empty string when content is an attrset."; + ______ = + if !(contentValue ? status) || builtins.isInt contentValue.status then + null + else + throw "Redirect endpoint at index ${toString index} content.status must be an int when provided."; + in + null + else + throw "Redirect endpoint at index ${toString index} must define content as a string or an attrset." else if builtins.isString contentValue then null From 2c1316a0a6598dd10f8108b099e8eec8cddfadd5 Mon Sep 17 00:00:00 2001 From: Katharina Heidenreich Date: Sat, 4 Apr 2026 18:11:22 +0200 Subject: [PATCH 02/10] feat: add memory limit --- config/services.nix | 7 ++--- services/continuwuity.nix | 15 ++++----- validation/service/continuwuity.nix | 49 ++++++++++++++++++++++++----- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/config/services.nix b/config/services.nix index d037e6b..9fcdb35 100644 --- a/config/services.nix +++ b/config/services.nix @@ -5,6 +5,9 @@ in rec { continuwuity = { port = 6167; + server_name = "nudelerde.de"; + trusted_servers = [ "matrix.org" ]; + memory_max = "512M"; }; qbittorrent = { @@ -25,10 +28,6 @@ rec { ]; }; - matrix = { - trusted_servers = [ "matrix.org" ]; - }; - vikunja = { port = 8081; }; diff --git a/services/continuwuity.nix b/services/continuwuity.nix index 2b07d7b..19ab9f6 100644 --- a/services/continuwuity.nix +++ b/services/continuwuity.nix @@ -1,17 +1,13 @@ -{ config, pkgs, ... }: +{ config, pkgs, lib, ... }: let - net = import ../config/network.nix; serv = import ../config/services.nix; serviceValidation = import ../validation/service/continuwuity.nix; - serverName = - if net ? devices && builtins.isAttrs net.devices && net.devices ? remote_proxy && net.devices.remote_proxy ? domain && builtins.isString net.devices.remote_proxy.domain then - net.devices.remote_proxy.domain - else - throw "config/network.nix must define devices.remote_proxy.domain as string for continuwuity."; + serverName = serviceValidation.getServerName serv; trustedServers = serviceValidation.getTrustedServers serv; + memoryMax = serviceValidation.getMemoryMax serv; in { services.matrix-continuwuity = { @@ -27,4 +23,9 @@ in }; }; }; + + systemd.services.matrix-continuwuity.serviceConfig = + lib.optionalAttrs (memoryMax != null) { + MemoryMax = memoryMax; + }; } diff --git a/validation/service/continuwuity.nix b/validation/service/continuwuity.nix index 4e9dc64..d428002 100644 --- a/validation/service/continuwuity.nix +++ b/validation/service/continuwuity.nix @@ -2,19 +2,54 @@ let common = import ./common.nix; in rec { + getServerName = serviceData: + let + continuwuity = + if serviceData ? continuwuity then + common.ensureAttrset "config/services.nix continuwuity" serviceData.continuwuity + else + throw "config/services.nix must define continuwuity attrset."; + + serverName = + if continuwuity ? server_name then + common.ensureString "config/services.nix continuwuity.server_name" continuwuity.server_name + else + throw "config/services.nix continuwuity.server_name must exist."; + in + if serverName != "" then + serverName + else + throw "config/services.nix continuwuity.server_name must be a non-empty string."; + getTrustedServers = serviceData: let - matrix = - if serviceData ? matrix then - common.ensureAttrset "config/services.nix matrix" serviceData.matrix + continuwuity = + if serviceData ? continuwuity then + common.ensureAttrset "config/services.nix continuwuity" serviceData.continuwuity else - throw "config/services.nix must define matrix attrset."; + throw "config/services.nix must define continuwuity attrset."; trustedServers = - if matrix ? trusted_servers then - common.ensureList "config/services.nix matrix.trusted_servers" matrix.trusted_servers + if continuwuity ? trusted_servers then + common.ensureList "config/services.nix continuwuity.trusted_servers" continuwuity.trusted_servers else - throw "config/services.nix matrix.trusted_servers must exist."; + throw "config/services.nix continuwuity.trusted_servers must exist."; in trustedServers; + + getMemoryMax = serviceData: + let + continuwuity = + if serviceData ? continuwuity then + common.ensureAttrset "config/services.nix continuwuity" serviceData.continuwuity + else + throw "config/services.nix must define continuwuity attrset."; + in + if !(continuwuity ? memory_max) then + null + else + let + value = common.ensureString "config/services.nix continuwuity.memory_max" continuwuity.memory_max; + in + if value != "" then value else throw "config/services.nix continuwuity.memory_max must be a non-empty string when provided."; } \ No newline at end of file From 338570920844afc0c30a51a6477aad5e2ec095b6 Mon Sep 17 00:00:00 2001 From: Katharina Heidenreich Date: Sat, 4 Apr 2026 19:45:40 +0200 Subject: [PATCH 03/10] feat: add port offset handling for auto SSH devices --- config/network.nix | 1 + intermediate/remote.nix | 26 ++++++++++++++++++-------- validation/auto_ssh.nix | 16 +++++++++++++++- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/config/network.nix b/config/network.nix index 74688d2..5a7d46c 100644 --- a/config/network.nix +++ b/config/network.nix @@ -36,6 +36,7 @@ rec { sshUser = "root"; key = secrets.byName.autossh_remote_proxy_key.path; known_hosts = secrets.byName.autossh_remote_proxy_known_hosts.path; + portOffset = 10000; }; }; }; diff --git a/intermediate/remote.nix b/intermediate/remote.nix index 4cbd677..251cfb8 100644 --- a/intermediate/remote.nix +++ b/intermediate/remote.nix @@ -19,22 +19,31 @@ let getRemotePortMap = device: autoSshValidation.getRemotePortMap device; - resolveRemotePort = remotePortMap: endpoint: + getPortOffset = device: + autoSshValidation.getPortOffset device; + + ensurePortRange = context: port: + if builtins.isInt port && port >= 1 && port <= 65535 then + port + else + throw "${context} must be in range 1..65535."; + + resolveRemotePort = remotePortMap: portOffset: endpoint: let localPort = if endpoint ? port then - endpoint.port + ensurePortRange "Endpoint port '${toString endpoint.port}'" endpoint.port else throw "Endpoint is missing required field: port."; overrides = lib.filter (entry: entry.localPort == localPort) remotePortMap; in if overrides != [] then - (builtins.head overrides).remotePort + ensurePortRange "remotePortMap remotePort '${toString (builtins.head overrides).remotePort}'" (builtins.head overrides).remotePort else - localPort; + ensurePortRange "Computed remote port (local ${toString localPort} + offset ${toString portOffset})" (localPort + portOffset); - mapEndpointToForward = remotePortMap: endpoint: { - remote = resolveRemotePort remotePortMap endpoint; + mapEndpointToForward = remotePortMap: portOffset: endpoint: { + remote = resolveRemotePort remotePortMap portOffset endpoint; localAddress = "localhost"; localPort = endpoint.port; }; @@ -50,6 +59,7 @@ let else throw "Auto SSH device is missing required field: domain."; remotePortMap = getRemotePortMap device; + portOffset = getPortOffset device; matchedEndpoints = lib.filter (endpoint: if endpoint.force_ssl && endpoint.port == 80 then @@ -61,8 +71,8 @@ let ) endpoints; forwards = lib.concatMap (endpoint: let - baseForward = mapEndpointToForward remotePortMap endpoint; - httpRedirectForward = mapEndpointToForward remotePortMap (endpointForHttpRedirect endpoint); + baseForward = mapEndpointToForward remotePortMap portOffset endpoint; + httpRedirectForward = mapEndpointToForward remotePortMap portOffset (endpointForHttpRedirect endpoint); in if endpoint.force_ssl then [ baseForward httpRedirectForward ] diff --git a/validation/auto_ssh.nix b/validation/auto_ssh.nix index 0035ce3..14070ee 100644 --- a/validation/auto_ssh.nix +++ b/validation/auto_ssh.nix @@ -53,9 +53,23 @@ let else device.auto_ssh.remotePortMap; + getPortOffset = device: + if !(device ? auto_ssh) then + 0 + else if !builtins.isAttrs device.auto_ssh then + throw "Device auto_ssh must be an attrset when present." + else if !(device.auto_ssh ? portOffset) then + 0 + else if !builtins.isInt device.auto_ssh.portOffset then + throw "Device auto_ssh.portOffset must be an int." + else if device.auto_ssh.portOffset < 0 then + throw "Device auto_ssh.portOffset must be >= 0." + else + device.auto_ssh.portOffset; + isSafeName = name: builtins.match "^[a-z_][a-z0-9_-]*$" name != null; in { - inherit getDevices getAutoSshDevices getAutoSshDomains getAutoSshConfig getRemotePortMap isSafeName; + inherit getDevices getAutoSshDevices getAutoSshDomains getAutoSshConfig getRemotePortMap getPortOffset isSafeName; } From 884af65ed848119696ce64ece964bcacfebe072f Mon Sep 17 00:00:00 2001 From: Katharina Heidenreich Date: Sat, 4 Apr 2026 21:46:26 +0200 Subject: [PATCH 04/10] feat: update autossh config --- config/network.nix | 2 +- secrets/autossh/remote_proxy_known_hosts | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/config/network.nix b/config/network.nix index 5a7d46c..96d1c50 100644 --- a/config/network.nix +++ b/config/network.nix @@ -33,7 +33,7 @@ rec { auto_ssh = { enable = true; sshPort = 22; - sshUser = "root"; + sshUser = "autossh-incoming"; key = secrets.byName.autossh_remote_proxy_key.path; known_hosts = secrets.byName.autossh_remote_proxy_known_hosts.path; portOffset = 10000; diff --git a/secrets/autossh/remote_proxy_known_hosts b/secrets/autossh/remote_proxy_known_hosts index d27b3bc..101fc4e 100644 --- a/secrets/autossh/remote_proxy_known_hosts +++ b/secrets/autossh/remote_proxy_known_hosts @@ -1,5 +1,5 @@ { - "data": "ENC[AES256_GCM,data:1ZzjJFqw28vXu6cw9efESy11132ntsaZwBmNqupqWju+y2iT2qq1wE+meGVH/e59YMC0ptNpsm+RbcfvC81vFvTFu46FUXPw2fXPb5UQmyDZxN7q+AM8SuOCqSy6,iv:FguIXmdsNUvhoqME97e5OIY+LMy5uuNd+d295U4lTuE=,tag:QlKokl5sOAtgwu06pnuRgA==,type:str]", + "data": "ENC[AES256_GCM,data:2O2/4FIDmmuwG0eRGmPUAm8Ji5lIly4dGT/ll+HZDvlC/sS6cbb3SEyplpVvqjMJNZACZtI9U87WJqKtoj9778Asxu0CFkwrsZK5bOf7XM27ZIN44/KWzbO6xezdcQ==,iv:XdgphFl56RNyjIIr9JQTUbh36j7UB8yjT0jgtCpMXDk=,tag:6OklZR86/gudejOFd9UneQ==,type:str]", "sops": { "age": [ { @@ -11,8 +11,9 @@ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiSVhGUHVJNVZVRXhxZzhx\ncEVSNWZBQW8vQXlYdnFUQTg4Q2VlY0lZckRnCkRKNnF5R3ZlUElWN0FOL1hrNHAr\nT2JPaDJ3bzVhZzZONHBkRHBueW16eVEKLS0tIEFxU2pWMVRqaC9BeGRsaWppT1lO\nMzI4UmkybmROL3dKK0xEaWpCM1VPQzQKWsLEb0Z7fbTVF8WQ8a1Lom8Bh7FQ2cPB\nIwIqWRM1L0oXNHyBoFkmHO434DZ8SXlyUYegBwrbVZGedRQFLQ4ibg==\n-----END AGE ENCRYPTED FILE-----\n" } ], - "lastmodified": "2026-04-04T09:12:02Z", - "mac": "ENC[AES256_GCM,data:C6jOqg6lFy8A2UTIfZXjtNB/F+0UZJV8/F1sOm4aGgR5B4e2JT+3vL/A62m6pvrj1zSuoD1dH47y3OcpmO/FmsnYcCTdWW2MPEOA63NkHJMScaLJry8JDHpColG8WYmJfGb9QwLJ1O64vpUNXNscmUFs7PomBgJnMCmTyo3twKk=,iv:wiAJw8Vk70b/gnfcgPlT3exsnnp87mGoIXMtzqd5m88=,tag:oT9TPTNSDX9y0cJJCP/JkQ==,type:str]", + "lastmodified": "2026-04-04T19:45:45Z", + "mac": "ENC[AES256_GCM,data:EEAh4MsdWJa6GvI2CZ0dS19ERRNtYucvEVCENZnc4Yqyz1vyQbNYuBZKi15D/XL5A8WN0jweBFtJV8bNwbuwW1gJZW8x10uhGJ1WpZrT+/vlFQbEzmEvvjkiO46R4kkQ+yhzkmth+85S0m/nz6p3lgIzcTRBfHIMe9V+kVb2eZI=,iv:u3E9Ybjl9a91Z8ic1fjCUqLqedAVS1e6ZZ9PNE7Em7c=,tag:XOJJBCQBxShz4bC/2t/VXg==,type:str]", + "unencrypted_suffix": "_unencrypted", "version": "3.12.1" } } From 5df4cca133fb291d5c2efdadce1f232e11cef58e Mon Sep 17 00:00:00 2001 From: Katharina Heidenreich Date: Sun, 5 Apr 2026 14:52:37 +0200 Subject: [PATCH 05/10] feat: advertise livekit --- config/endpoints/matrix.nix | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/config/endpoints/matrix.nix b/config/endpoints/matrix.nix index fa6ffc6..7135226 100644 --- a/config/endpoints/matrix.nix +++ b/config/endpoints/matrix.nix @@ -50,7 +50,19 @@ in content = { contentType = "application/json"; status = 200; - body = ''{"m.homeserver":{"base_url":"https://${net.devices.remote_proxy.domain}"}}''; + body = '' + { + "m.homeserver": { + "base_url": "https://${net.devices.remote_proxy.domain}" + }, + "org.matrix.msc4143.rtc_foci": [ + { + "type": "livekit", + "livekit_service_url": "https://livekit.${net.devices.remote_proxy.domain}/livekit/jwt" + } + ] + } + ''; }; } ] \ No newline at end of file From 84bb31214cc26a1d94695fa89eb21e18d55daca0 Mon Sep 17 00:00:00 2001 From: Katharina Heidenreich Date: Sun, 5 Apr 2026 15:58:15 +0200 Subject: [PATCH 06/10] feat: add livekit to continuwuity --- config/services.nix | 1 + services/continuwuity.nix | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/config/services.nix b/config/services.nix index 9fcdb35..0ce971b 100644 --- a/config/services.nix +++ b/config/services.nix @@ -8,6 +8,7 @@ rec { server_name = "nudelerde.de"; trusted_servers = [ "matrix.org" ]; memory_max = "512M"; + livekit_url = "https://livekit.nudelerde.de/livekit/jwt"; }; qbittorrent = { diff --git a/services/continuwuity.nix b/services/continuwuity.nix index 19ab9f6..4da8dfc 100644 --- a/services/continuwuity.nix +++ b/services/continuwuity.nix @@ -20,6 +20,11 @@ in allow_federation = true; max_request_size = 20 * 1024 * 1024; # 20 MiB trusted_servers = trustedServers; + matrix_rtc = { + foci = [ + { type = "livekit"; livekit_service_url = serv.continuwuity.livekit_url; } + ]; + }; }; }; }; From 1a3d1f1fdd57eae55e5f7710698889ad1ed6a23b Mon Sep 17 00:00:00 2001 From: Katharina Heidenreich Date: Sun, 5 Apr 2026 16:43:09 +0200 Subject: [PATCH 07/10] feat: override continuwuity package --- config/endpoints/matrix.nix | 2 +- config/services.nix | 6 ++++++ configuration.nix | 8 ++++++++ services/continuwuity.nix | 25 ++++++++++++++++++++++++- 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/config/endpoints/matrix.nix b/config/endpoints/matrix.nix index 7135226..7c81689 100644 --- a/config/endpoints/matrix.nix +++ b/config/endpoints/matrix.nix @@ -58,7 +58,7 @@ in "org.matrix.msc4143.rtc_foci": [ { "type": "livekit", - "livekit_service_url": "https://livekit.${net.devices.remote_proxy.domain}/livekit/jwt" + "livekit_service_url": "https://livekit.${net.devices.remote_proxy.domain}" } ] } diff --git a/config/services.nix b/config/services.nix index 0ce971b..ae8027c 100644 --- a/config/services.nix +++ b/config/services.nix @@ -9,6 +9,12 @@ rec { trusted_servers = [ "matrix.org" ]; memory_max = "512M"; livekit_url = "https://livekit.nudelerde.de/livekit/jwt"; + + package = { + version = "0.5.6"; + sourceHash = "sha256-p6dL1wL9n+1ivUItdlZuLxTneDBjCHEdNr0ukau2rHI="; + cargoHash = "sha256-lLbnFA2WS96er84G2e9bGrYhhqe2zL3Npn1SXB3De2w="; + }; }; qbittorrent = { diff --git a/configuration.nix b/configuration.nix index adc645f..e312cc9 100644 --- a/configuration.nix +++ b/configuration.nix @@ -9,6 +9,7 @@ timeZone = "Europe/Berlin"; defaultLocale = "en_US.UTF-8"; + storageConfig = import ./config/storage.nix; storageModel = import ./intermediate/storage.nix; in { imports = [ @@ -21,6 +22,13 @@ in { fileSystems = storageModel.fileSystems; + swapDevices = [ + { + device = "${storageConfig.ssd.path}/swapfile"; + size = 8192; + } + ]; + networking.hostName = "raspberry"; environment.systemPackages = with pkgs; [ diff --git a/services/continuwuity.nix b/services/continuwuity.nix index 4da8dfc..3509ab8 100644 --- a/services/continuwuity.nix +++ b/services/continuwuity.nix @@ -3,15 +3,38 @@ let serv = import ../config/services.nix; serviceValidation = import ../validation/service/continuwuity.nix; + continuwuity = serv.continuwuity; serverName = serviceValidation.getServerName serv; trustedServers = serviceValidation.getTrustedServers serv; memoryMax = serviceValidation.getMemoryMax serv; + + package = if continuwuity ? package then + pkgs.matrix-continuwuity.overrideAttrs (old: rec { + version = continuwuity.package.version; + pname = old.pname or "matrix-continuwuity"; + + src = pkgs.fetchFromGitea { + domain = "forgejo.ellis.link"; + owner = "continuwuation"; + repo = "continuwuity"; + tag = "v${version}"; + hash = continuwuity.package.sourceHash; + }; + + cargoDeps = pkgs.rustPlatform.fetchCargoVendor { + inherit src; + hash = continuwuity.package.cargoHash; + }; + }) + else + pkgs.matrix-continuwuity; in { services.matrix-continuwuity = { enable = true; + package = package; settings = { global = { server_name = serverName; @@ -22,7 +45,7 @@ in trusted_servers = trustedServers; matrix_rtc = { foci = [ - { type = "livekit"; livekit_service_url = serv.continuwuity.livekit_url; } + { type = "livekit"; livekit_service_url = continuwuity.livekit_url; } ]; }; }; From 20f8049d646fec5f60938061105862919ba5cca6 Mon Sep 17 00:00:00 2001 From: Katharina Heidenreich Date: Mon, 6 Apr 2026 13:22:22 +0200 Subject: [PATCH 08/10] fix: matrix rtc mapping --- config/endpoints/matrix.nix | 37 ++++++++++++++++++++++++------------- config/services.nix | 2 +- intermediate/nginx.nix | 9 +++++++++ validation/endpoints.nix | 16 +++++++++++++++- 4 files changed, 49 insertions(+), 15 deletions(-) diff --git a/config/endpoints/matrix.nix b/config/endpoints/matrix.nix index 7c81689..8ea90ed 100644 --- a/config/endpoints/matrix.nix +++ b/config/endpoints/matrix.nix @@ -29,6 +29,19 @@ in proxyWebsockets = true; }; } + # { + # type = "proxy"; + # domain = net.devices.remote_proxy.domain; + # endpoint = "/.well-known/matrix/"; + # force_ssl = true; + # port = 443; + # content = { + # type = "service"; + # ip = net.devices.pi.ip; + # port = services.continuwuity.port; + # proxyWebsockets = true; + # }; + # } { type = "inline"; domain = net.devices.remote_proxy.domain; @@ -37,6 +50,11 @@ in port = 443; content = { contentType = "application/json"; + headers = { + Access-Control-Allow-Origin = "*"; + Access-Control-Allow-Methods = "GET, POST, PUT, DELETE, OPTIONS"; + Access-Control-Allow-Headers = "X-Requested-With, Content-Type, Authorization"; + }; status = 200; body = ''{"m.server":"${net.devices.remote_proxy.domain}:443"}''; }; @@ -49,20 +67,13 @@ in port = 443; content = { contentType = "application/json"; + headers = { + Access-Control-Allow-Origin = "*"; + Access-Control-Allow-Methods = "GET, POST, PUT, DELETE, OPTIONS"; + Access-Control-Allow-Headers = "X-Requested-With, Content-Type, Authorization"; + }; status = 200; - body = '' - { - "m.homeserver": { - "base_url": "https://${net.devices.remote_proxy.domain}" - }, - "org.matrix.msc4143.rtc_foci": [ - { - "type": "livekit", - "livekit_service_url": "https://livekit.${net.devices.remote_proxy.domain}" - } - ] - } - ''; + body = ''{"m.homeserver": {"base_url": "https://${net.devices.remote_proxy.domain}"},"org.matrix.msc3575.proxy":{"url":"https://nudelerde.de/"},"org.matrix.msc4143.rtc_foci": [{"type": "livekit","livekit_service_url": "https://livekit.${net.devices.remote_proxy.domain}"}]}''; }; } ] \ No newline at end of file diff --git a/config/services.nix b/config/services.nix index ae8027c..c0ab036 100644 --- a/config/services.nix +++ b/config/services.nix @@ -8,7 +8,7 @@ rec { server_name = "nudelerde.de"; trusted_servers = [ "matrix.org" ]; memory_max = "512M"; - livekit_url = "https://livekit.nudelerde.de/livekit/jwt"; + livekit_url = "https://livekit.nudelerde.de"; package = { version = "0.5.6"; diff --git a/intermediate/nginx.nix b/intermediate/nginx.nix index 3b17093..85a8f6f 100644 --- a/intermediate/nginx.nix +++ b/intermediate/nginx.nix @@ -241,6 +241,14 @@ let route.content.contentType else "text/plain; charset=utf-8"; + inlineHeaders = + if builtins.isAttrs route.content && route.content ? headers then + route.content.headers + else + {}; + inlineHeaderLines = lib.concatStringsSep "\n" ( + lib.mapAttrsToList (name: value: " add_header ${name} ${builtins.toJSON value} always;") inlineHeaders + ); in { name = "= ${route.endpoint}"; @@ -248,6 +256,7 @@ let return = "${toString inlineStatus} ${builtins.toJSON inlineBody}"; extraConfig = '' default_type ${inlineContentType}; +${lib.optionalString (inlineHeaderLines != "") inlineHeaderLines} ''; }; } diff --git a/validation/endpoints.nix b/validation/endpoints.nix index 50c7c7d..410eba1 100644 --- a/validation/endpoints.nix +++ b/validation/endpoints.nix @@ -5,7 +5,7 @@ let allowedProxyContentKeys = [ "type" "ip" "port" "proxyWebsockets" ]; allowedWebContentKeys = [ "type" "files" ]; allowedWebFileKeys = [ "path" "filePath" "contentType" "status" ]; - allowedInlineContentKeys = [ "body" "contentType" "status" ]; + allowedInlineContentKeys = [ "body" "contentType" "headers" "status" ]; allowedRedirectContentKeys = [ "target" "status" ]; ensureNoUnknownKeys = context: obj: allowedKeys: @@ -168,6 +168,20 @@ let null else throw "Inline endpoint at index ${toString index} content.contentType must be a non-empty string when provided."; + ________ = + if contentValue ? headers then + if builtins.isAttrs contentValue.headers then + let + headerValues = builtins.attrValues contentValue.headers; + in + if lib.all (value: builtins.isString value && value != "") headerValues then + null + else + throw "Inline endpoint at index ${toString index} content.headers values must be non-empty strings." + else + throw "Inline endpoint at index ${toString index} content.headers must be an attrset when provided." + else + null; _______ = if !(contentValue ? status) || builtins.isInt contentValue.status then null From 7eaceeac27c8e2f8ef7f20bc4b2cfd74c644c233 Mon Sep 17 00:00:00 2001 From: Katharina Heidenreich Date: Wed, 8 Apr 2026 20:07:51 +0200 Subject: [PATCH 09/10] feat: add tuserver --- config/network.nix | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/config/network.nix b/config/network.nix index 96d1c50..0bad294 100644 --- a/config/network.nix +++ b/config/network.nix @@ -39,6 +39,14 @@ rec { portOffset = 10000; }; }; + "tuserver" = { + type = "local"; + ip = "192.168.2.102"; + reservation = { + hw_address = "00:23:24:f9:43:e6"; + hostname = "tuserver"; + }; + }; }; dhcp = { From 059bac76d19d7708dda28d9e519e0f0314f7143f Mon Sep 17 00:00:00 2001 From: Katharina Heidenreich Date: Wed, 8 Apr 2026 20:39:31 +0200 Subject: [PATCH 10/10] feat: add git --- config/endpoints.nix | 3 ++- config/endpoints/git.nix | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 config/endpoints/git.nix diff --git a/config/endpoints.nix b/config/endpoints.nix index 082f336..3160f56 100644 --- a/config/endpoints.nix +++ b/config/endpoints.nix @@ -3,5 +3,6 @@ let web = import ./endpoints/web.nix; matrix = import ./endpoints/matrix.nix; vikunja = import ./endpoints/vikunja.nix; + git = import ./endpoints/git.nix; in -local ++ web ++ matrix ++ vikunja ++ [] \ No newline at end of file +local ++ web ++ matrix ++ vikunja ++ git ++ [] \ No newline at end of file diff --git a/config/endpoints/git.nix b/config/endpoints/git.nix new file mode 100644 index 0000000..6202d25 --- /dev/null +++ b/config/endpoints/git.nix @@ -0,0 +1,18 @@ +let + net = import ../network.nix; +in +[ + { + type = "proxy"; + domain = "git.${net.devices.remote_proxy.domain}"; + endpoint = "/"; + force_ssl = true; + port = 443; + content = { + type = "service"; + ip = net.devices.tuserver.ip; + port = 3000; + proxyWebsockets = false; + }; + } +] \ No newline at end of file