This repository has no description
0

Configure Feed

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

superconfig / machines / coaxmetal / hermes-voice.nix
4.5 kB 127 lines
1{ 2 config, 3 lib, 4 pkgs, 5 self, 6 ... 7}: 8let 9 port = 8790; 10 # The gateway's webhook platform (hermes.nix), loopback only. 11 webhookPort = 8644; 12in 13{ 14 # hermes-voice (tools/hermes-voice): live voice conversations with Hermes 15 # through OpenAI's gpt-live-1. Runs next to Hermes so its API key never 16 # leaves this host; reachable over retiolum and publicly as 17 # https://assist.lassul.us through neoprism (configs/hermes-proxy.nix). 18 # 19 # SECURITY: the page drives Hermes, which has terminal tools and code 20 # execution as lass (machines/coaxmetal/hermes.nix). The access token below 21 # is the gate on every API route; treat it like the hermes-api key. 22 systemd.services.hermes-voice = { 23 description = "hermes-voice: live voice conversations with Hermes"; 24 wantedBy = [ "multi-user.target" ]; 25 wants = [ "network-online.target" ]; 26 after = [ 27 "network-online.target" 28 "hermes-agent.service" 29 ]; 30 serviceConfig = { 31 ExecStart = lib.escapeShellArgs [ 32 (lib.getExe self.packages.${pkgs.stdenv.hostPlatform.system}.hermes-voice) 33 "--listen" 34 "[::]:${toString port}" 35 # The API server binds the retiolum address (hermes.nix), not 36 # loopback; traffic to the host's own address goes through lo. 37 "--hermes-url" 38 "http://${config.networking.hostName}.r:8642" 39 "--hermes-key-file" 40 "%d/hermes-key" 41 "--openai-key-file" 42 "%d/openai-key" 43 "--access-token-file" 44 "%d/access-token" 45 "--matrix-webhook" 46 "http://127.0.0.1:${toString webhookPort}/webhooks/voice-approval" 47 ]; 48 LoadCredential = [ 49 "hermes-key:${config.clan.core.vars.generators.hermes-api.files."api_key".path}" 50 "openai-key:${config.clan.core.vars.generators.hermes-voice-openai.files."api-key".path}" 51 "access-token:${config.clan.core.vars.generators.hermes-voice.files."access-token".path}" 52 ]; 53 DynamicUser = true; 54 Restart = "on-failure"; 55 CapabilityBoundingSet = ""; 56 NoNewPrivileges = true; 57 PrivateDevices = true; 58 PrivateTmp = true; 59 ProtectHome = true; 60 ProtectSystem = "strict"; 61 ProtectKernelTunables = true; 62 ProtectKernelModules = true; 63 ProtectControlGroups = true; 64 RestrictAddressFamilies = [ 65 "AF_INET" 66 "AF_INET6" 67 ]; 68 RestrictNamespaces = true; 69 LockPersonality = true; 70 SystemCallArchitectures = "native"; 71 }; 72 }; 73 74 # Only retiolum: neoprism's proxy and devices on the mesh. Every other 75 # interface keeps the default DROP (configs/default.nix). 76 networking.firewall.interfaces.retiolum.allowedTCPPorts = [ port ]; 77 78 # hermes-voice's side in the gateway (tools/hermes-voice/hermes-plugin): the 79 # matrix_dm_send and end_call tools, on for every platform like any plugin 80 # toolset (end_call refuses outside voice sessions), and /assist, which 81 # answers approvals asked in the Matrix DM through hermes-voice 82 # (authenticated with API_SERVER_KEY). 83 services.hermes-agent.extraPlugins = [ 84 (pkgs.runCommand "hermes-voice" { } '' 85 cp -r ${../../tools/hermes-voice/hermes-plugin} $out 86 '') 87 ]; 88 services.hermes-agent.environment.HERMES_VOICE_URL = "http://localhost:${toString port}"; 89 90 # How hermes-voice asks for an approval when no call can hear it: a 91 # deliver_only route posts the text to the Matrix home room through the 92 # gateway, no agent turn. Signed with WEBHOOK_SECRET, which is the API 93 # server key (hermes-env in hermes.nix), so no secret is added. 94 services.hermes-agent.settings.platforms.webhook = { 95 enabled = true; 96 extra = { 97 host = "127.0.0.1"; 98 port = webhookPort; 99 routes.voice-approval = { 100 deliver = "matrix"; 101 deliver_only = true; 102 prompt = "{text}"; 103 }; 104 }; 105 }; 106 107 # OpenAI project key with GPT-Live access; billed per second of open voice 108 # session. persist=true so the prompt value is stored once. 109 clan.core.vars.generators.hermes-voice-openai.prompts.api-key = { 110 description = "OpenAI API key for hermes-voice (gpt-live-1)"; 111 type = "hidden"; 112 persist = true; 113 }; 114 115 # What the page sends as its bearer token. Read it out for the phone with 116 # clan vars get coaxmetal hermes-voice/access-token 117 clan.core.vars.generators.hermes-voice = { 118 files."access-token" = { }; 119 runtimeInputs = [ 120 pkgs.coreutils 121 pkgs.openssl 122 ]; 123 script = '' 124 openssl rand -hex 32 | tr -d '\n' > "$out/access-token" 125 ''; 126 }; 127}