This repository has no description
0

Configure Feed

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

superconfig / 2configs / omnigent.nix
6.4 kB 176 lines
1let 2 domain = "omni.lassul.us"; 3 port = 8771; 4 stateDir = "/var/lib/omnigent"; 5in 6{ 7 config, 8 lib, 9 pkgs, 10 self, 11 ... 12}: 13let 14 # Server-side agent catalog entry so a browser session can pick omp without 15 # any per-user config: the generic ACP harness drives `omp acp` (omp speaks 16 # the Agent Client Protocol natively). The command is unqualified on purpose 17 # — it is resolved on the *host* that runs the session, not here. 18 ompAgentConfig = pkgs.writeText "omp-config.yaml" '' 19 spec_version: 1 20 name: omp 21 description: Oh My Pi (omp) over ACP — runs on whichever machine hosts the session. 22 prompt: You are omp (Oh My Pi), a concise coding assistant. 23 24 executor: 25 type: omnigent 26 config: 27 harness: acp 28 acp_agent: 29 name: Oh My Pi 30 command: omp acp 31 # See tools/covibe/flake-module.nix: omp fails session/new outright 32 # when omnigent's MCP relay cannot start, and the relay's 33 # `python -I -m omnigent…` child cannot import omnigent under a 34 # nixpkgs-wrapped interpreter. 35 omnigent_mcp: false 36 37 os_env: 38 type: caller_process 39 cwd: "." 40 sandbox: 41 type: none 42 ''; 43 44 # `--agent` takes a bundle *directory* holding config.yaml. 45 ompAgent = pkgs.runCommand "omnigent-agent-omp" { } '' 46 mkdir -p $out 47 cp ${ompAgentConfig} $out/config.yaml 48 ''; 49 50 # One identity per line. Admins bypass every per-session ACL, which is what 51 # makes sharing manageable at all: a session's creator gets LEVEL_OWNER, and 52 # only owners/managers can grant others read (watch + diffs) or edit (steer). 53 admins = pkgs.writeText "omnigent-admins" '' 54 lass@lassul.us 55 ''; 56in 57{ 58 # omnigent: self-hosted agent meta-harness. The server owns sessions, 59 # transcripts, per-session ACLs (read / edit / manage / owner), the per-file 60 # diff view and the browser UI; the agents themselves run on whichever 61 # machine registers as a *host*, so no coding agent runs on neoprism unless 62 # someone points a host at it. `s covibe` in a project directory is the 63 # client: it runs omp locally and registers the session here. 64 # 65 # Browser auth is the local pocket-id IdP. pocket-id has no declarative 66 # client API, so register the client by hand at https://id.lassul.us: 67 # name: omnigent 68 # callback URL: https://omni.lassul.us/auth/callback 69 # logout URL: https://omni.lassul.us/ 70 # then paste its id + secret into the clan prompt below. 71 72 # Generated cookie-signing secret plus the prompted pocket-id client 73 # id/secret, materialised into one EnvironmentFile. 74 clan.core.vars.generators.omnigent = { 75 files."env" = { }; 76 prompts.oidc_client = { 77 description = '' 78 Register an OIDC client in pocket-id (https://id.lassul.us): 79 name: omnigent 80 callback URL: https://omni.lassul.us/auth/callback 81 Generate a client secret, then paste both lines: 82 OMNIGENT_OIDC_CLIENT_ID=... 83 OMNIGENT_OIDC_CLIENT_SECRET=... 84 ''; 85 type = "multiline"; 86 persist = true; 87 }; 88 runtimeInputs = [ pkgs.openssl ]; 89 script = '' 90 { 91 printf 'OMNIGENT_OIDC_COOKIE_SECRET=%s\n' "$(openssl rand -hex 32)" 92 cat "$prompts"/oidc_client 93 } > "$out/env" 94 ''; 95 }; 96 97 systemd.services.omnigent = { 98 description = "omnigent agent meta-harness (${domain})"; 99 wantedBy = [ "multi-user.target" ]; 100 after = [ 101 "network-online.target" 102 "nginx.service" 103 ]; 104 wants = [ "network-online.target" ]; 105 environment = { 106 HOME = stateDir; 107 OMNIGENT_AUTH_ENABLED = "1"; 108 OMNIGENT_AUTH_PROVIDER = "oidc"; 109 OMNIGENT_OIDC_ISSUER = "https://id.lassul.us"; 110 OMNIGENT_OIDC_REDIRECT_URI = "https://${domain}/auth/callback"; 111 OMNIGENT_OIDC_LOGOUT_REDIRECT_URI = "https://${domain}/"; 112 OMNIGENT_OIDC_ALLOWED_DOMAINS = "lassul.us"; 113 # pocket-id emits `email` *and* `email_verified` in the id_token when the 114 # `email` scope is requested (which omnigent does), but the value of 115 # `email_verified` is the users.email_verified column, whose default comes 116 # from the app-config key `emailsVerified` — shipped as false. omnigent 117 # otherwise hard-rejects the login with "Could not determine user email 118 # from IdP". The gate exists for IdPs where a user can self-assert an 119 # arbitrary address; here users are provisioned by hand in pocket-id 120 # (signups are disabled) and the domain allowlist above still applies, so 121 # the marker carries no information for us. 122 OMNIGENT_OIDC_SKIP_EMAIL_VERIFICATION = "1"; 123 OMNIGENT_ADMIN_LIST_PATH = "${admins}"; 124 # The wheel checks PyPI for updates on every start otherwise. 125 OMNIGENT_NO_UPDATE_CHECK = "1"; 126 # Off by default upstream; be explicit, it phones home otherwise. 127 OMNIGENT_TELEMETRY_ENABLED = "0"; 128 }; 129 serviceConfig = { 130 ExecStart = lib.concatStringsSep " " [ 131 (lib.getExe self.packages.${pkgs.system}.omnigent) 132 "server" 133 "--host 127.0.0.1" 134 "--port ${toString port}" 135 "--no-open" 136 "--database-uri sqlite:///${stateDir}/chat.db" 137 "--artifact-location ${stateDir}/artifacts" 138 "--agent ${ompAgent}" 139 ]; 140 EnvironmentFile = [ config.clan.core.vars.generators.omnigent.files."env".path ]; 141 Restart = "always"; 142 RestartSec = 5; 143 DynamicUser = true; 144 StateDirectory = "omnigent"; 145 WorkingDirectory = stateDir; 146 ProtectSystem = "strict"; 147 ProtectHome = true; 148 PrivateTmp = true; 149 NoNewPrivileges = true; 150 ProtectKernelTunables = true; 151 ProtectControlGroups = true; 152 RestrictAddressFamilies = [ 153 "AF_INET" 154 "AF_INET6" 155 "AF_UNIX" 156 ]; 157 }; 158 }; 159 160 services.nginx.virtualHosts.${domain} = { 161 enableACME = true; 162 forceSSL = true; 163 locations."/" = { 164 proxyPass = "http://127.0.0.1:${toString port}"; 165 # The session event stream is SSE (omnigent sets X-Accel-Buffering: no, 166 # which nginx honours) and the terminal/host/runner tunnels are 167 # WebSockets that idle between turns, so the default 60s read timeout 168 # would sever live sessions. 169 proxyWebsockets = true; 170 extraConfig = '' 171 proxy_read_timeout 1d; 172 proxy_send_timeout 1d; 173 ''; 174 }; 175 }; 176}