This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

superconfig / configs / hermes-proxy.nix
4.0 kB 102 lines
1{ lib, ... }: 2let 3 upstream = "http://coaxmetal.r:8642"; 4 5 # Agent turns run for minutes and /v1/chat/completions streams SSE chunks the 6 # whole time. nginx's default 60s proxy_read_timeout would cut a long turn off 7 # mid-answer, and its response buffering would hold the stream back until the 8 # turn ended, so the phone would sit silent and then dump everything at once. 9 # coaxmetal is a laptop — asleep or off the mesh most of the day — so connect 10 # fails fast, to report "unreachable" instead of hanging on the assistant 11 # button. 12 streamingProxy = '' 13 proxy_read_timeout 1h; 14 proxy_send_timeout 1h; 15 proxy_buffering off; 16 proxy_cache off; 17 proxy_connect_timeout 5s; 18 limit_req zone=hermes_api burst=20 nodelay; 19 ''; 20 21 proxied = { 22 recommendedProxySettings = true; 23 proxyPass = upstream; 24 extraConfig = streamingProxy; 25 }; 26 27 # Endpoints WakeHermesClaw actually calls, taken from its sources 28 # (backend/HermesApiServerClient.kt and friends): the chat/runs lanes plus the 29 # read-only lanes its settings screens populate from. Everything else — most 30 # of the adapter's route table — is unreachable from outside. 31 allowed = [ 32 "= /health" 33 "/v1/" 34 "= /api/config" 35 "= /api/available-models" 36 "= /api/model/options" 37 "= /api/skills" 38 "= /api/jobs" 39 "/api/jobs/" 40 "/api/profiles" 41 ]; 42in 43{ 44 # Public door to the Hermes API server on coaxmetal (machines/coaxmetal/hermes.nix). 45 # This is what the WakeHermesClaw app on massulus points at when it is not on 46 # retiolum: the assistant button posts to /v1/chat/completions here. 47 # 48 # SECURITY: behind this vhost sits an agent with terminal tools and, through 49 # the herdr wrapper, code execution as lass. The gateway's only check on this 50 # lane is the API_SERVER_KEY bearer token — there is no per-user allowlist like 51 # MATRIX_ALLOWED_USERS on the Matrix lane, and the app cannot do mTLS (it 52 # builds a bare OkHttpClient with no KeyManager), so a client certificate is 53 # not an option without patching it. That token is therefore a 54 # root-equivalent credential for that laptop: rotate it by regenerating the 55 # hermes-api var. The hardening below is damage control, not authentication. 56 services.nginx.virtualHosts."hermes.lassul.us" = { 57 enableACME = true; 58 forceSSL = true; 59 60 # Attachments: the app can send images with a prompt. 25M is generous for a 61 # phone photo and still bounds what an unauthenticated request can push 62 # into the proxy before hermes rejects it. 63 extraConfig = '' 64 client_max_body_size 25m; 65 ''; 66 67 # Unlisted paths never reach hermes. Notably /api/pty and /api/ws — the 68 # Hermes Desktop terminal lane — are only served with backend.mode 69 # "serve"/"dashboard", which this host does not enable; 404ing them here 70 # means enabling it later cannot quietly publish a PTY. 71 locations = lib.genAttrs allowed (_: proxied) // { 72 "/".extraConfig = '' 73 return 404; 74 ''; 75 }; 76 }; 77 78 # Public door to hermes-voice on coaxmetal (machines/coaxmetal/hermes-voice.nix): 79 # the voice page for the phone. hermes-voice gates every API route with its 80 # own access token, so nothing is filtered by path here. /delegate streams 81 # NDJSON for the length of a Hermes turn, so the streaming settings apply. 82 services.nginx.virtualHosts."voice.lassul.us" = { 83 enableACME = true; 84 forceSSL = true; 85 extraConfig = '' 86 client_max_body_size 256k; 87 ''; 88 locations."/" = { 89 recommendedProxySettings = true; 90 proxyPass = "http://coaxmetal.r:8790"; 91 extraConfig = streamingProxy; 92 }; 93 }; 94 95 # One phone driving one agent needs a trickle of requests; a turn is a single 96 # long-lived request, not a stream of them. This is sized to absorb the 97 # settings screens refreshing (hence the burst) while making credential 98 # stuffing against the bearer token pointless. 99 services.nginx.appendHttpConfig = '' 100 limit_req_zone $binary_remote_addr zone=hermes_api:1m rate=60r/m; 101 ''; 102}