This repository has no description
0

Configure Feed

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

hermes-proxy: public hermes.lassul.us in front of the api server

Lets the phone reach the agent when it is off retiolum. Long read timeout
and no buffering because /v1/chat/completions streams SSE for the whole
turn; fast connect timeout because coaxmetal is a laptop.

The bearer token is the only authn on this lane and the app cannot do
mTLS, so the location allowlist and limit_req are damage control: only the
endpoints the app calls are reachable, and /api/pty stays 404 even if
backend.mode is enabled later.

+76 -25
+74 -25
2configs/hermes-proxy.nix
··· 1 + { lib, ... }: 2 + let 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 + ]; 42 + in 1 43 { 2 44 # Public door to the Hermes API server on coaxmetal (machines/coaxmetal/hermes.nix). 3 - # This is what the WakeHermesClaw app on massulus points at when it is not 4 - # on retiolum: the assistant button posts to /v1/chat/completions here. 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. 5 47 # 6 48 # SECURITY: behind this vhost sits an agent with terminal tools and, through 7 - # the herdr wrapper, code execution as lass. The gateway's only check on 8 - # this lane is the API_SERVER_KEY bearer token — there is no per-user 9 - # allowlist like MATRIX_ALLOWED_USERS on the Matrix lane. So the token is a 10 - # root-equivalent credential for that laptop; rotate it by regenerating the 11 - # hermes-api var, and expect to see internet background noise in the access 12 - # log (unauthenticated requests get 401 from hermes itself). 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. 13 56 services.nginx.virtualHosts."hermes.lassul.us" = { 14 57 enableACME = true; 15 58 forceSSL = true; 16 - locations."/" = { 17 - recommendedProxySettings = true; 18 - proxyPass = "http://coaxmetal.r:8642"; 19 - extraConfig = '' 20 - # Agent turns run for minutes and /v1/chat/completions streams SSE 21 - # chunks the whole time. nginx's default 60s proxy_read_timeout would 22 - # cut a long turn off mid-answer, and its response buffering would 23 - # hold the stream back until the turn ended, so the phone would sit 24 - # silent and then dump everything at once. 25 - proxy_read_timeout 1h; 26 - proxy_send_timeout 1h; 27 - proxy_buffering off; 28 - proxy_cache off; 29 - # coaxmetal is a laptop: asleep or off the mesh most of the day. Fail 30 - # fast on connect so the app reports "unreachable" instead of hanging 31 - # on the assistant button. 32 - proxy_connect_timeout 5s; 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; 33 74 ''; 34 75 }; 35 76 }; 77 + 78 + # One phone driving one agent needs a trickle of requests; a turn is a single 79 + # long-lived request, not a stream of them. This is sized to absorb the 80 + # settings screens refreshing (hence the burst) while making credential 81 + # stuffing against the bearer token pointless. 82 + services.nginx.appendHttpConfig = '' 83 + limit_req_zone $binary_remote_addr zone=hermes_api:1m rate=60r/m; 84 + ''; 36 85 }
+1
2configs/dns/lassul.us.zone
··· 50 50 cdio IN CNAME neoprism 51 51 c IN CNAME neoprism 52 52 hass IN CNAME neoprism 53 + hermes IN CNAME neoprism 53 54 vault IN CNAME neoprism 54 55 cal IN CNAME neoprism 55 56 kanban IN CNAME neoprism
+1
machines/neoprism/config.nix
··· 31 31 ../../2configs/services/pad 32 32 33 33 ../../2configs/hass/proxy.nix 34 + ../../2configs/hermes-proxy.nix 34 35 35 36 # sync-containers 36 37 ../../2configs/consul.nix