Compare commits

...

11 commits

Author SHA1 Message Date
042820fb2a pruned 2026-04-14 02:19:14 -04:00
9ad55ac79a updated secret 2026-04-14 01:46:53 -04:00
d8be05169c api key 2026-04-14 00:55:20 -04:00
573f5ec95d mcp 2026-04-14 00:51:25 -04:00
46adf8e9f0 seperate routing for llama & stack 2026-04-14 00:41:39 -04:00
3feb5ddc6b fix perms 2026-04-13 23:58:49 -04:00
54b2a18d66 timeout 2026-04-13 23:57:20 -04:00
cb5b10493f llama-stack 2026-04-13 23:28:14 -04:00
c41a6ff637 added llama-stack 2026-04-13 23:12:50 -04:00
07586a80ee added llama api key 2026-04-13 22:45:02 -04:00
27f765fe22 removed 2026-04-13 21:45:22 -04:00
8 changed files with 96 additions and 52 deletions

View file

@ -1,9 +0,0 @@
{
"permissions": {
"allow": [
"WebSearch",
"WebFetch(domain:forgejo.org)",
"Bash(ssh:*)"
]
}
}

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
*.qcow2
result
.direnv
.claude

View file

@ -12,13 +12,19 @@ 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$
- path_regex: secrets/system/searxng\.yaml$ # searxng token
key_groups:
- age:
- *server

25
secrets/system/llama.yaml Normal file
View file

@ -0,0 +1,25 @@
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

View file

@ -5,6 +5,10 @@ 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
@ -19,6 +23,11 @@ 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 = ''

View file

@ -22,7 +22,6 @@ in
'';
};
searxng.enable = mkEnableOption "Publicly exposed SearXNG endpoint with secret path via sops";
};
config = mkIf cfg.enable {
@ -92,17 +91,6 @@ 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;
@ -116,32 +104,49 @@ 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};
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};
'';
};
virtualHosts."chat.${domain}" = {
in {
useACMEHost = domain;
forceSSL = true;
locations."/" = {
proxyPass = "http://192.168.0.23:3080";
proxyWebsockets = true;
extraConfig = privateAccessRules;
};
};
virtualHosts."ai.${domain}" = {
useACMEHost = domain;
forceSSL = true;
# Web UI + llama.cpp API (browser, /v1/* calls from the UI)
# Auth handled by llama.cpp itself (--api-key flag)
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}" = {

View file

@ -9,20 +9,28 @@
# Camera RTSP credentials (used by frigate/go2rtc)
sops.secrets = let
cameras = { sopsFile = ../../../secrets/system/cameras.yaml; };
searxng = { sopsFile = ../../../secrets/system/searxng.yaml; };
llama = { sopsFile = ../../../secrets/system/llama.yaml; };
in {
"RTSP_USER" = cameras;
"RTSP_PASS" = cameras;
"SEARXNG_TOKEN" = searxng;
"LLAMA_API_KEY" = llama // { owner = config.user.name; };
};
sops.templates."nginx-searxng-location.conf" = {
# API key auth for ai.ramos.codes — nginx validates Bearer token against sops secret
sops.templates."nginx-ai-auth.conf" = {
content = ''
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";
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"}';
}
'';
owner = "nginx";
@ -31,7 +39,6 @@
modules.system = {
nginx = {
enable = true;
searxng.enable = true;
};
sandpack.enable = true;
forgejo.enable = true;

View file

@ -30,9 +30,9 @@ in
];
programs = {
#bash = {
# initExtra = import ./config/penpot.nix;
#};
bash = {
initExtra = "export LLAMA_API_KEY=$(cat /run/secrets/LLAMA_API_KEY)";
};
direnv = {
enable = true;
enableBashIntegration = true;