This repository has no description
3.2 kB
94 lines
1{
2 config,
3 lib,
4 pkgs,
5 self,
6 ...
7}:
8let
9 port = 8790;
10in
11{
12 # hermes-voice (tools/hermes-voice): live voice conversations with Hermes
13 # through OpenAI's gpt-live-1. Runs next to Hermes so its API key never
14 # leaves this host; reachable over retiolum and publicly as
15 # https://voice.lassul.us through neoprism (configs/hermes-proxy.nix).
16 #
17 # SECURITY: the page drives Hermes, which has terminal tools and code
18 # execution as lass (machines/coaxmetal/hermes.nix). The access token below
19 # is the gate on every API route; treat it like the hermes-api key.
20 systemd.services.hermes-voice = {
21 description = "hermes-voice: live voice conversations with Hermes";
22 wantedBy = [ "multi-user.target" ];
23 wants = [ "network-online.target" ];
24 after = [
25 "network-online.target"
26 "hermes-agent.service"
27 ];
28 serviceConfig = {
29 ExecStart = lib.escapeShellArgs [
30 (lib.getExe self.packages.${pkgs.stdenv.hostPlatform.system}.hermes-voice)
31 "--listen"
32 "[::]:${toString port}"
33 # The API server binds the retiolum address (hermes.nix), not
34 # loopback; traffic to the host's own address goes through lo.
35 "--hermes-url"
36 "http://${config.networking.hostName}.r:8642"
37 "--hermes-key-file"
38 "%d/hermes-key"
39 "--openai-key-file"
40 "%d/openai-key"
41 "--access-token-file"
42 "%d/access-token"
43 ];
44 LoadCredential = [
45 "hermes-key:${config.clan.core.vars.generators.hermes-api.files."api_key".path}"
46 "openai-key:${config.clan.core.vars.generators.hermes-voice-openai.files."api-key".path}"
47 "access-token:${config.clan.core.vars.generators.hermes-voice.files."access-token".path}"
48 ];
49 DynamicUser = true;
50 Restart = "on-failure";
51 CapabilityBoundingSet = "";
52 NoNewPrivileges = true;
53 PrivateDevices = true;
54 PrivateTmp = true;
55 ProtectHome = true;
56 ProtectSystem = "strict";
57 ProtectKernelTunables = true;
58 ProtectKernelModules = true;
59 ProtectControlGroups = true;
60 RestrictAddressFamilies = [
61 "AF_INET"
62 "AF_INET6"
63 ];
64 RestrictNamespaces = true;
65 LockPersonality = true;
66 SystemCallArchitectures = "native";
67 };
68 };
69
70 # Only retiolum: neoprism's proxy and devices on the mesh. Every other
71 # interface keeps the default DROP (configs/default.nix).
72 networking.firewall.interfaces.retiolum.allowedTCPPorts = [ port ];
73
74 # OpenAI project key with GPT-Live access; billed per second of open voice
75 # session. persist=true so the prompt value is stored once.
76 clan.core.vars.generators.hermes-voice-openai.prompts.api-key = {
77 description = "OpenAI API key for hermes-voice (gpt-live-1)";
78 type = "hidden";
79 persist = true;
80 };
81
82 # What the page sends as its bearer token. Read it out for the phone with
83 # clan vars get coaxmetal hermes-voice/access-token
84 clan.core.vars.generators.hermes-voice = {
85 files."access-token" = { };
86 runtimeInputs = [
87 pkgs.coreutils
88 pkgs.openssl
89 ];
90 script = ''
91 openssl rand -hex 32 | tr -d '\n' > "$out/access-token"
92 '';
93 };
94}