From 02aee842f583172458caaf419c9a22a8a6f62949 Mon Sep 17 00:00:00 2001 From: Zuckerberg Date: Wed, 3 Jun 2026 15:38:04 -0700 Subject: [PATCH] Add signal + codex --- machines/fry/workspaces/hermes.nix | 64 ++++++++++++++++++++++++++--- secrets/hermes-env.age | Bin 762 -> 918 bytes 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/machines/fry/workspaces/hermes.nix b/machines/fry/workspaces/hermes.nix index 56c35ca..9565d86 100644 --- a/machines/fry/workspaces/hermes.nix +++ b/machines/fry/workspaces/hermes.nix @@ -14,7 +14,17 @@ group = "users"; createUser = false; - extraPackages = with pkgs; [ nix git ripgrep fd jq ]; + # Share the user's workspace tree so the agent operates on the same files + # you see when SSH'd in. Codex's workspace-write sandbox keeps writes scoped + # to this dir. + workingDirectory = "/home/googlebot/workspace"; + + extraPackages = with pkgs; [ nix git ripgrep fd jq codex ]; + + environment = { + SIGNAL_HTTP_URL = "http://127.0.0.1:8080"; + CODEX_HOME = "/var/lib/hermes/.codex"; + }; # Bind-mounted from /run/agenix/hermes-env on fry (host decrypts via agenix). # Lives at /etc/... rather than /run/... because the workspace's systemd @@ -26,16 +36,58 @@ model = { provider = "openai-codex"; default = "gpt-5.5"; + # Delegate openai/* turns to the codex CLI subprocess so the agent gets + # codex's sandbox + tooling. Codex CLI must be on PATH and authenticated + # via `codex login` (separate from `hermes auth`). + openai_runtime = "codex_app_server"; }; toolsets = [ "all" ]; terminal.backend = "local"; }; }; - # Daemon sets HERMES_HOME to stateDir/.hermes via the systemd unit. Setting - # it system-wide here makes interactive `hermes` (now running as googlebot) - # pick up the same auth.json that the daemon wrote. - environment.variables.HERMES_HOME = "/var/lib/hermes/.hermes"; + # Align googlebot's interactive `hermes` / `codex` invocations with the + # daemon's persisted state dirs so logins from a shell land where the + # service expects to read them. + home-manager.users.googlebot.home.sessionVariables = { + HERMES_HOME = "/var/lib/hermes/.hermes"; + CODEX_HOME = "/var/lib/hermes/.codex"; + }; - environment.systemPackages = [ pkgs.codex ]; + home-manager.users.googlebot.home.packages = with pkgs; [ codex signal-cli ]; + + # Declarative codex CLI defaults — seeded into $CODEX_HOME on first boot, + # then codex owns the file (it rewrites config.toml on `codex login`, project + # trust grants, etc.). To re-seed after changing the defaults below, delete + # the persisted /var/lib/hermes/.codex/config.toml and restart hermes-agent. + environment.etc."hermes-codex-config.toml".text = '' + sandbox_mode = "danger-full-access" + approval_policy = "never" + ''; + + systemd.tmpfiles.rules = [ + "d /var/lib/hermes/.codex 0700 googlebot users -" + "C /var/lib/hermes/.codex/config.toml 0644 googlebot users - /etc/hermes-codex-config.toml" + ]; + + # signal-cli runs an HTTP JSON-RPC daemon on localhost; hermes-agent talks to + # it via SIGNAL_HTTP_URL. Registered as the PRIMARY device on a dedicated + # number (GV / JMP.chat / prepaid SIM) — the account identity lives entirely + # in /var/lib/hermes/signal-cli/, which is bind-mounted from the host. If + # that tree is lost, the Signal account is irrecoverable, so back it up. + systemd.services.signal-cli = { + description = "signal-cli JSON-RPC daemon for Hermes"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + before = [ "hermes-agent.service" ]; + serviceConfig = { + Type = "simple"; + User = "googlebot"; + Group = "users"; + ExecStart = "${pkgs.signal-cli}/bin/signal-cli --config /var/lib/hermes/signal-cli daemon --http 127.0.0.1:8080"; + Restart = "always"; + RestartSec = "5"; + }; + }; } diff --git a/secrets/hermes-env.age b/secrets/hermes-env.age index 11c7d2f1f8ff5cbfe6eabc167d09d4ece085c0b0..1259c9727daeafcf59cb0e3057c29dec659b71c4 100644 GIT binary patch delta 830 zcmeyxI*omTPQ8J#i+f5~v8jHtQ@)R1x<^o=d#IC}vx`AlT9k>KXG)}tK}A(bpnh^* zHdj@#zgLx?TZxCGWtnAua)e)|cBM~XP?U+Mp=nB4u69UJNtSzRkVi$hBbTn7LUD11 zZfc5=si~o*f}eRxQC7M_xn)_Twtl#_dwG_NQ(?A2g_~Q7V|ZawMr4(vM`?wLfm4!e zu&bl9XL@iTm!nCgPgr46vA02Dk#mrLRl0$piAj-pxml`@x4U7asc(*3E_^8N^V0Q$eT$1M z0-OsfEG>%l+NVjs1*WTpT?!{4G3-Lrv2spJf!UclXqGEGzfWPWCe`^A0HvHwx3v zi8QnfcZ@JBb`EiK^(r(g3M)1VG4crI%1!aDOifEI&(_cI_sI=TcS}l2Gbu6m40AC^ zOx3O|_028v46pL{Pc02akAc*(!m_k zD(5P1&ph*xoT$Le3dm{bC0}|KrUTfU4_h&F#S+>pR$sS3fH{s6h~L{ESKUe%lw>Be`C|ah{7O?{36G~ z5VKN`WG?n17KWJY!40-<)LSGv!js?y^@4te($EE3B7DOPg>j-C+M? z-j9clD;?Kko0ybVwdShvi*TJoOGQPt&Dfc4eXq;YL&QPI(?fmF!;M=d)6f0=@sj7z z{YOQ+C61RDpEUm~zLq^secIWjZN;&1HT?7c&Mvl4Uobs%Nwoa+r=qG4!ah8E^PGX^}XMvxi zE0=d*num*TURp+ki)lu1p;2OaVMLj~X;wjyYpJJ!eqw}4hPJn9iMNMmGMBEMLUD11 zZfc5=si~o*f}eRxQC7M_d1jSMN>D{;saa88RH&hok6VeCb8>J*mQzkfwq;0_p|(%9 zt4Cynk87YKSBjxgWRP24Kz2@mai)c~qp7}6Vo<(ziCJ-YN_l~Ac7|uMSE-X_esQ?( z#E;_P*(P}|`4;*4+7a$X#aZQ{F6Q1Lk(ue)CZ%4cIj)t-NtWIg>Bjn|u7(y|2FZo4 zKK|tqe(r_7fkpmNe)@(*jzMP0MuC5{q)