diff --git a/modules/home/terminal/opencode/default.nix b/modules/home/terminal/opencode/default.nix index 1fa74cc..c30f4f5 100644 --- a/modules/home/terminal/opencode/default.nix +++ b/modules/home/terminal/opencode/default.nix @@ -2,8 +2,11 @@ lib, config, pkgs, + osConfig, ... -}: { +}: let + a2a = osConfig.mods.server.auth2api; +in { options.mods.opencode.enable = lib.mkEnableOption "enables opencode"; # imports = [ @@ -21,6 +24,44 @@ settings = { "plugin" = ["opencode-gemini-auth" "opencode-anthropic-oauth"]; + "provider" = { + "auth2api" = { + "npm" = "@ai-sdk/openai-compatible"; + "name" = "auth2api (local)"; + "options" = { + "baseURL" = "http://${a2a.host}:${toString a2a.port}/v1"; + }; + "models" = { + "sonnet" = { + "name" = "Claude Sonnet 4 (auth2api)"; + "reasoning" = true; + }; + "claude-sonnet-4-6" = { + "name" = "Claude Sonnet 4.6 (auth2api)"; + "reasoning" = true; + }; + "opus" = { + "name" = "Claude Opus 4 (auth2api)"; + "reasoning" = true; + }; + "claude-opus-4-6" = { + "name" = "Claude Opus 4.6 (auth2api)"; + "reasoning" = true; + }; + "claude-opus-4-7" = { + "name" = "Claude Opus 4.7 (auth2api)"; + "reasoning" = true; + }; + "haiku" = { + "name" = "Claude Haiku 4.5 (auth2api)"; + }; + "claude-haiku-4-5-20251001" = { + "name" = "Claude Haiku 4.5 20251001 (auth2api)"; + }; + }; + }; + }; + "mode" = { "build" = { "prompt" = "You are Claude Code, Anthropic's official CLI for Claude."; diff --git a/modules/nixos/server/auth2api.nix b/modules/nixos/server/auth2api.nix index 4cc50b9..58c5048 100644 --- a/modules/nixos/server/auth2api.nix +++ b/modules/nixos/server/auth2api.nix @@ -19,7 +19,7 @@ let postBuild = '' wrapProgram $out/bin/auth2api \ --add-flags "--config=${cfg.stateDir}/config.yaml" \ - --run "umask 027" + --run "umask 007" ''; }; diff --git a/modules/nixos/server/hermes.nix b/modules/nixos/server/hermes.nix index 902251e..6283cca 100644 --- a/modules/nixos/server/hermes.nix +++ b/modules/nixos/server/hermes.nix @@ -71,6 +71,35 @@ with lib; { }; config = mkIf cfg.enable { + # Write the nix-store overlay script into the stateDir so it's available + # at /data/nix-store-overlay inside the container. Run it once with: + # docker exec hermes-agent sudo /data/nix-store-overlay + # This mounts a writable overlay over the read-only /nix/store so the Nix + # daemon can build new derivations without touching the host store. + # Upper/work dirs live on /data (persistent volume) so builds survive restarts. + system.activationScripts.hermes-nix-overlay-script = { + text = '' + install -m 0755 -o root -g root /dev/stdin \ + '${cfg.stateDir}/nix-store-overlay' << 'OVERLAY_EOF' +#!/bin/sh +set -e +UPPER=/data/nix-store-upper +WORK=/data/nix-store-work +if ! touch /nix/store/.rw-test 2>/dev/null; then + mkdir -p "$UPPER" "$WORK" + mount -t overlay overlay \ + -o lowerdir=/nix/store,upperdir="$UPPER",workdir="$WORK" \ + /nix/store + echo "nix-store overlay mounted (upper=$UPPER)" +else + rm -f /nix/store/.rw-test + echo "nix-store already writable, skipping overlay" +fi +OVERLAY_EOF + ''; + deps = [ "users" "groups" ]; + }; + # Hermes needs auth2api running to have a backend to call. assertions = [ { @@ -130,6 +159,12 @@ with lib; { enable = true; backend = "docker"; inherit (cfg.container) hostUsers extraVolumes; + + # SYS_ADMIN is required for the overlay mount over /nix/store. + # The nix-store-overlay script (written to stateDir by tmpfiles) mounts + # a writable overlay so the Nix daemon can build inside the container + # without touching the host store. + extraOptions = [ "--cap-add=SYS_ADMIN" ]; }; }; };