diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..8463192 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,9 @@ +{ + "permissions": { + "allow": [ + "WebSearch", + "WebFetch(domain:forgejo.org)", + "Bash(ssh:*)" + ] + } +} diff --git a/.gitignore b/.gitignore index fad0876..619d00a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ *.qcow2 result .direnv -.claude diff --git a/.sops.yaml b/.sops.yaml index 6fa100a..fdad7d3 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -12,19 +12,13 @@ creation_rules: key_groups: - age: - *desktop - # Shared secrets (desktop + server) - - path_regex: secrets/system/llama\.yaml$ # llama.cpp API key - key_groups: - - age: - - *desktop - - *server # Server secrets (cameras) - path_regex: secrets/system/cameras\.yaml$ # RTSP Feed key_groups: - age: - *server # Server secrets (searxng) - - path_regex: secrets/system/searxng\.yaml$ # searxng token + - path_regex: secrets/system/searxng\.yaml$ key_groups: - age: - *server diff --git a/secrets/system/llama.yaml b/secrets/system/llama.yaml deleted file mode 100644 index 6df19c1..0000000 --- a/secrets/system/llama.yaml +++ /dev/null @@ -1,25 +0,0 @@ -LLAMA_API_KEY: ENC[AES256_GCM,data:ZVDpwGAxnHbHxt+JW3mYGyyBU5JfFAbjc/byq6Ok9wTlpQZBx969Z0wV74F5pR4axmpdGs7XlZDh1rJaQTn7lg==,iv:oAG9G25x+1FRkRNBRzLW2UJmbSxgx5Cu64Qo/6VzAyw=,tag:nkO/SdzjjLxH4fkgIdwUYQ==,type:str] -sops: - age: - - recipient: age17ejyzyk52unr6eyaa9rpunxpmf7u9726v6sx7me3ww3mdu5xzgjqsgj9gl - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzUmV6Q2dCMWU3TUFkZ0I0 - dHA3dXd2U0RSRzNtL3YvdG8rYWdnOTZoTkMwCkNnYnVlVmMyRDNnS1FmWktlNU9N - UW1OMlJYODVzSHNIZWZMRkpPY05Ed3cKLS0tIDg0b0VkT0NrS3NIWE9EdWtWYXc1 - NjNESHpYbVptcnVRYWFKb3RlYkJ6OWMK3JsRXPDvJdKv2UyYIH8kr/WKbXgUDXbc - fYOD0Huo73BA0vr8PlrsF4STVgJr/arKCMdI1C0bDdcwjExKnR1tIw== - -----END AGE ENCRYPTED FILE----- - - recipient: age198jg29ryg3c0qj3yg6y9ha4ce2ue4hjdaa9kalf49fxju74dhchsquvjzp - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFTGNKOWczaityaXowWi9I - dmh0MjJoelV3bVlzeGpLZmVTVzJjckwwQUFzCk81ZHlTcm5oWHRQNklreUR4bWNS - OVdQelQ4YXkzeWZqOWZoNWlOVkZpWUkKLS0tIDZKQUU3LzV0UUhnRHVHQkFadkxm - djRyUEYyZ2srMlVxR0JtQlFqSWV1QWcKMIF9Sq4TUUmpVZAukjTjFbIrMxcE3+el - QSrHIm1HXLXwCKLDQ2N6b8Q9iUo/XMV0wsD3TLxdnUfegpQpfsDhag== - -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-04-14T05:45:37Z" - mac: ENC[AES256_GCM,data:G+o6OhNF5AFBDKQEU3f1MZ+GOkxQj/m7NNk4Ti8PxPPOHdByoCrauvgB78SdQf5ubcfupElcNB0yF5QsG3/m7eGaSA+8J0cDL6jB3NEE5EUbW1Fuzzg2Ez1JnFu4BstkLiDRD/TribXMNFAjykmNrHt4zee6fhU3H0MOn7+Acok=,iv:IqBLSBq1kOMRHQn1IvU8OgmWGn6EFJcef/rNr38txmY=,tag:/mSWgbPbhUNoIm3x+6zyRA==,type:str] - unencrypted_suffix: _unencrypted - version: 3.12.1 diff --git a/system/machines/desktop/system.nix b/system/machines/desktop/system.nix index e981ab8..7a12793 100644 --- a/system/machines/desktop/system.nix +++ b/system/machines/desktop/system.nix @@ -5,10 +5,6 @@ let (user: user.modules.user.security.gpg.enable or false) (lib.attrValues config.home-manager.users); - devEnabled = lib.any - (user: user.modules.user.utils.dev.enable or false) - (lib.attrValues config.home-manager.users); - sysModules = config.modules.system; in @@ -23,11 +19,6 @@ in "WIFI_HOME_PSK" = wifi; "WIFI_CAMS_SSID" = wifi; "WIFI_CAMS_PSK" = wifi; - } // lib.optionalAttrs devEnabled { - "LLAMA_API_KEY" = { - sopsFile = ../../../secrets/system/llama.yaml; - owner = config.user.name; - }; }; sops.templates."wifi-env".content = '' diff --git a/system/machines/server/modules/nginx/default.nix b/system/machines/server/modules/nginx/default.nix index 3f4b0f2..e423815 100644 --- a/system/machines/server/modules/nginx/default.nix +++ b/system/machines/server/modules/nginx/default.nix @@ -22,6 +22,7 @@ in ''; }; + searxng.enable = mkEnableOption "Publicly exposed SearXNG endpoint with secret path via sops"; }; config = mkIf cfg.enable { @@ -91,6 +92,17 @@ in }; }; + virtualHosts."test.${domain}" = { + useACMEHost = domain; + forceSSL = true; + locations."/" = { + return = "200 'nginx is working'"; + extraConfig = '' + add_header Content-Type text/plain; + ''; + }; + }; + virtualHosts."wg.${domain}" = { useACMEHost = domain; forceSSL = true; @@ -104,49 +116,32 @@ in }; }; - virtualHosts."ai.${domain}" = let - apiKeyAuth = '' - set $api_key ""; - if ($http_authorization ~* "^Bearer (.+)$") { - set $api_key $1; - } - if ($api_key = "") { - return 401 '{"error": "Missing Authorization header"}'; - } - include ${config.sops.templates."nginx-ai-auth.conf".path}; - ''; - in { + virtualHosts."searxng.${domain}" = mkIf cfg.searxng.enable { useACMEHost = domain; forceSSL = true; + locations."/".return = "404"; + extraConfig = '' + include ${config.sops.templates."nginx-searxng-location.conf".path}; + ''; + }; - # Web UI + llama.cpp API (browser, /v1/* calls from the UI) - # Auth handled by llama.cpp itself (--api-key flag) + virtualHosts."chat.${domain}" = { + useACMEHost = domain; + forceSSL = true; + locations."/" = { + proxyPass = "http://192.168.0.23:3080"; + proxyWebsockets = true; + extraConfig = privateAccessRules; + }; + }; + + virtualHosts."ai.${domain}" = { + useACMEHost = domain; + forceSSL = true; locations."/" = { proxyPass = "http://192.168.0.23:8000"; proxyWebsockets = true; }; - - # Llama Stack API (opencode, programmatic clients) - # Clients use baseURL: https://ai.ramos.codes/stack/v1 - locations."/stack/v1/" = { - proxyPass = "http://192.168.0.23:8321/v1/"; - proxyWebsockets = true; - extraConfig = apiKeyAuth + '' - proxy_read_timeout 300s; - proxy_send_timeout 300s; - ''; - }; - - # MCP servers (namespaced, for llama.cpp web UI + direct access) - locations."/mcp/web_search/" = { - proxyPass = "http://192.168.0.23:8002/"; - proxyWebsockets = true; - extraConfig = '' - include ${config.sops.templates."nginx-mcp-auth.conf".path}; - proxy_read_timeout 300s; - proxy_send_timeout 300s; - ''; - }; }; virtualHosts."comfy.${domain}" = { diff --git a/system/machines/server/system.nix b/system/machines/server/system.nix index 43b75f6..b8674dd 100644 --- a/system/machines/server/system.nix +++ b/system/machines/server/system.nix @@ -9,28 +9,20 @@ # Camera RTSP credentials (used by frigate/go2rtc) sops.secrets = let cameras = { sopsFile = ../../../secrets/system/cameras.yaml; }; - llama = { sopsFile = ../../../secrets/system/llama.yaml; }; + searxng = { sopsFile = ../../../secrets/system/searxng.yaml; }; in { "RTSP_USER" = cameras; "RTSP_PASS" = cameras; - "LLAMA_API_KEY" = llama // { owner = config.user.name; }; + "SEARXNG_TOKEN" = searxng; }; - # API key auth for ai.ramos.codes — nginx validates Bearer token against sops secret - sops.templates."nginx-ai-auth.conf" = { + sops.templates."nginx-searxng-location.conf" = { content = '' - if ($api_key != "${config.sops.placeholder."LLAMA_API_KEY"}") { - return 401 '{"error": "Invalid API key"}'; - } - ''; - owner = "nginx"; - }; - - # MCP endpoint auth — validates X-API-Key header - sops.templates."nginx-mcp-auth.conf" = { - content = '' - if ($http_x_api_key != "${config.sops.placeholder."LLAMA_API_KEY"}") { - return 401 '{"error": "Unauthorized"}'; + location /${config.sops.placeholder."SEARXNG_TOKEN"}/ { + proxy_pass http://192.168.0.23:8080/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; } ''; owner = "nginx"; @@ -39,6 +31,7 @@ modules.system = { nginx = { enable = true; + searxng.enable = true; }; sandpack.enable = true; forgejo.enable = true; diff --git a/user/modules/utils/dev/default.nix b/user/modules/utils/dev/default.nix index 991524b..89c4809 100644 --- a/user/modules/utils/dev/default.nix +++ b/user/modules/utils/dev/default.nix @@ -30,9 +30,9 @@ in ]; programs = { - bash = { - initExtra = "export LLAMA_API_KEY=$(cat /run/secrets/LLAMA_API_KEY)"; - }; + #bash = { + # initExtra = import ./config/penpot.nix; + #}; direnv = { enable = true; enableBashIntegration = true;