diff --git a/config/openclaw/openclaw.json b/config/openclaw/openclaw.json deleted file mode 100644 index 5f736b1..0000000 --- a/config/openclaw/openclaw.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "gateway": { - "mode": "local", - "bind": "loopback", - "port": 2470, - "trustedProxies": ["127.0.0.1", "::1"], - "controlUi": { - "allowedOrigins": ["https://netty.harivan.sh"] - }, - "auth": { - "mode": "token", - "token": "${OPENCLAW_GATEWAY_TOKEN}" - } - }, - "channels": { - "telegram": { - "botToken": "${TELEGRAM_BOT_TOKEN}", - "dmPolicy": "pairing" - } - }, - "agents": { - "defaults": { - "workspace": "~/.openclaw/workspace", - "skipBootstrap": true, - "model": { - "primary": "anthropic/claude-sonnet-4-6" - }, - "sandbox": { - "mode": "non-main" - } - } - }, - "tools": { - "profile": "coding", - "fs": { - "workspaceOnly": true - }, - "loopDetection": { - "enabled": true - }, - "deny": ["sessions_send", "sessions_spawn"] - } -} diff --git a/flake.lock b/flake.lock index 9c0d3a4..43c32fc 100644 --- a/flake.lock +++ b/flake.lock @@ -133,6 +133,24 @@ "type": "github" } }, + "flake-utils_3": { + "inputs": { + "systems": "systems_3" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, "googleworkspace-cli": { "inputs": { "flake-utils": "flake-utils_2", @@ -174,6 +192,27 @@ "type": "github" } }, + "home-manager_2": { + "inputs": { + "nixpkgs": [ + "openClaw", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1767909183, + "narHash": "sha256-u/bcU0xePi5bgNoRsiqSIwaGBwDilKKFTz3g0hqOBAo=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "cd6e96d56ed4b2a779ac73a1227e0bb1519b3509", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, "neovim-nightly": { "inputs": { "flake-parts": "flake-parts_2", @@ -251,6 +290,24 @@ "type": "github" } }, + "nix-steipete-tools": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1773561580, + "narHash": "sha256-wT0bKTp45YnMkc4yXQvk943Zz/rksYiIjEXGdWzxnic=", + "owner": "openclaw", + "repo": "nix-steipete-tools", + "rev": "cd4c429ff3b3aaef9f92e59812cf2baf5704b86f", + "type": "github" + }, + "original": { + "owner": "openclaw", + "repo": "nix-steipete-tools", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1774701658, @@ -283,6 +340,22 @@ } }, "nixpkgs_2": { + "locked": { + "lastModified": 1767364772, + "narHash": "sha256-fFUnEYMla8b7UKjijLnMe+oVFOz6HjijGGNS1l7dYaQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "16c7794d0a28b5a37904d55bcca36003b9109aaa", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { "locked": { "lastModified": 1767640445, "narHash": "sha256-UWYqmD7JFBEDBHWYcqE6s6c77pWdcU/i+bwD6XxMb8A=", @@ -298,9 +371,32 @@ "type": "github" } }, + "openClaw": { + "inputs": { + "flake-utils": "flake-utils_3", + "home-manager": "home-manager_2", + "nix-steipete-tools": "nix-steipete-tools", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773851886, + "narHash": "sha256-+3ygZuf5K8mtSGMMEZ/h+vxGvXCu1CmiB+531KMagH8=", + "owner": "openclaw", + "repo": "nix-openclaw", + "rev": "64d410666821866c565e048a4d07d6cf5d8e494e", + "type": "github" + }, + "original": { + "owner": "openclaw", + "repo": "nix-openclaw", + "type": "github" + } + }, "openspec": { "inputs": { - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs_3" }, "locked": { "lastModified": 1772182342, @@ -327,6 +423,7 @@ "nix-darwin": "nix-darwin", "nix-homebrew": "nix-homebrew", "nixpkgs": "nixpkgs", + "openClaw": "openClaw", "openspec": "openspec" } }, @@ -359,6 +456,21 @@ "repo": "default", "type": "github" } + }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index f2e2fc2..56253cf 100644 --- a/flake.nix +++ b/flake.nix @@ -25,6 +25,11 @@ inputs.nixpkgs.follows = "nixpkgs"; }; + openClaw = { + url = "github:openclaw/nix-openclaw"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + openspec = { url = "github:Fission-AI/OpenSpec"; }; diff --git a/home/openclaw.nix b/home/openclaw.nix index d3ea4f7..aad815e 100644 --- a/home/openclaw.nix +++ b/home/openclaw.nix @@ -1,36 +1,35 @@ { config, - lib, + inputs, pkgs, hostConfig, + lib, ... }: let - openClawStateDir = "${config.home.homeDirectory}/.openclaw"; - openClawWorkspaceDir = "${openClawStateDir}/workspace"; openClawVersion = "2026.4.2"; + npmDir = "${config.xdg.dataHome}/npm"; in lib.mkIf hostConfig.isLinux { + home.packages = [ + inputs.openClaw.packages.${pkgs.stdenv.hostPlatform.system}.default + ]; + home.activation.installOpenClaw = lib.hm.dag.entryAfter [ "writeBoundary" ] '' - export PATH="${ - lib.makeBinPath [ - pkgs.nodejs_22 - pkgs.coreutils - ] - }:$PATH" + export PATH="${lib.makeBinPath [ pkgs.nodejs_22 pkgs.coreutils ]}:$PATH" export NPM_CONFIG_USERCONFIG="${config.xdg.configHome}/npm/npmrc" export XDG_DATA_HOME="${config.xdg.dataHome}" export XDG_CACHE_HOME="${config.xdg.cacheHome}" + OPENCLAW_DIR="${npmDir}/lib/node_modules/openclaw" INSTALLED=$(npm ls -g openclaw --depth=0 --json 2>/dev/null | ${pkgs.jq}/bin/jq -r '.dependencies.openclaw.version // empty') - if [ "$INSTALLED" != "${openClawVersion}" ]; then - npm install -g "openclaw@${openClawVersion}" 2>/dev/null || true + HEALTHY=true + [ "$INSTALLED" != "${openClawVersion}" ] && HEALTHY=false + [ ! -d "$OPENCLAW_DIR/node_modules/grammy" ] && HEALTHY=false + if [ "$HEALTHY" = false ]; then + npm install -g "openclaw@${openClawVersion}" --force 2>/dev/null || true fi ''; - home.activation.syncOpenClawState = lib.hm.dag.entryAfter [ "writeBoundary" ] '' - install -d -m 700 "${openClawStateDir}" "${openClawWorkspaceDir}" - install -m 600 ${../config/openclaw/openclaw.json} "${openClawStateDir}/openclaw.json" - install -m 644 ${../config/openclaw/SOUL.md} "${openClawWorkspaceDir}/SOUL.md" - ''; + home.file.".openclaw/workspace/SOUL.md".source = ../config/openclaw/SOUL.md; } diff --git a/hosts/netty/openclaw-gateway.nix b/hosts/netty/openclaw-gateway.nix index 46b27bd..eb9b79d 100644 --- a/hosts/netty/openclaw-gateway.nix +++ b/hosts/netty/openclaw-gateway.nix @@ -1,61 +1,68 @@ { + inputs, pkgs, username, ... }: let homeDir = "/home/${username}"; - openClawStateDir = "${homeDir}/.openclaw"; - openClawConfigPath = "${openClawStateDir}/openclaw.json"; - openClawEnvFile = "${openClawStateDir}/.env"; - openClawBin = "${homeDir}/.local/share/npm/bin/openclaw"; - openClawCheck = pkgs.writeShellScript "openclaw-gateway-check" '' - [ -x "${openClawBin}" ] && [ -f "${openClawConfigPath}" ] && [ -s "${openClawEnvFile}" ] - ''; + stateDir = "${homeDir}/.openclaw"; + runtimeConfig = "${stateDir}/openclaw.json"; in { - systemd.tmpfiles.rules = [ - "d ${openClawStateDir} 0700 ${username} users -" - "d ${openClawStateDir}/workspace 0700 ${username} users -" - "z ${openClawEnvFile} 0600 ${username} users -" - "z ${openClawConfigPath} 0600 ${username} users -" - ]; - - systemd.services.openclaw-gateway = { - description = "OpenClaw Gateway"; - after = [ "network-online.target" ]; - wants = [ "network-online.target" ]; - wantedBy = [ "multi-user.target" ]; - path = with pkgs; [ - nodejs_22 + services.openclaw-gateway = { + enable = true; + package = inputs.openClaw.packages.${pkgs.stdenv.hostPlatform.system}.default; + port = 2470; + user = username; + group = "users"; + createUser = false; + stateDir = stateDir; + environmentFiles = [ "${stateDir}/.env" ]; + environment = { + OPENCLAW_NIX_MODE = "1"; + OPENCLAW_CONFIG_PATH = runtimeConfig; + }; + execStart = "${homeDir}/.local/share/npm/bin/openclaw gateway --port 2470"; + execStartPre = [ + "+${pkgs.coreutils}/bin/install -m 600 -o ${username} -g users /etc/openclaw/openclaw.json ${runtimeConfig}" + ]; + servicePath = with pkgs; [ + pkgs.nodejs_22 git - coreutils - findutils - gnugrep - gawk docker ]; - environment = { - HOME = homeDir; - NODE_NO_WARNINGS = "1"; - OPENCLAW_NIX_MODE = "1"; - OPENCLAW_STATE_DIR = openClawStateDir; - OPENCLAW_CONFIG_PATH = openClawConfigPath; - NPM_CONFIG_USERCONFIG = "${homeDir}/.config/npm/npmrc"; - XDG_CACHE_HOME = "${homeDir}/.cache"; - XDG_CONFIG_HOME = "${homeDir}/.config"; - XDG_DATA_HOME = "${homeDir}/.local/share"; - }; - serviceConfig = { - Type = "simple"; - User = username; - Group = "users"; - WorkingDirectory = openClawStateDir; - ExecCondition = openClawCheck; - EnvironmentFile = "-${openClawEnvFile}"; - ExecStart = "${openClawBin} gateway run"; - Restart = "always"; - RestartSec = 5; + config = { + gateway = { + mode = "local"; + bind = "loopback"; + port = 2470; + trustedProxies = [ "127.0.0.1" "::1" ]; + controlUi.allowedOrigins = [ "https://netty.harivan.sh" ]; + auth = { + mode = "token"; + token = "\${OPENCLAW_GATEWAY_TOKEN}"; + }; + }; + channels.telegram = { + botToken = "\${TELEGRAM_BOT_TOKEN}"; + dmPolicy = "pairing"; + }; + agents.defaults = { + workspace = "~/.openclaw/workspace"; + skipBootstrap = false; + model = { + primary = "anthropic/claude-opus-4-6"; + fallbacks = [ "anthropic/claude-sonnet-4-6" ]; + }; + sandbox.mode = "non-main"; + }; + tools = { + profile = "coding"; + fs.workspaceOnly = true; + loopDetection.enabled = true; + deny = [ "sessions_send" "sessions_spawn" ]; + }; }; }; } diff --git a/modules/hosts/netty.nix b/modules/hosts/netty.nix index 850d7c3..347ba36 100644 --- a/modules/hosts/netty.nix +++ b/modules/hosts/netty.nix @@ -15,6 +15,7 @@ in specialArgs = mkSpecialArgs host; modules = [ inputs.disko.nixosModules.disko + inputs.openClaw.nixosModules.openclaw-gateway ../../hosts/${host.name}/configuration.nix inputs.home-manager.nixosModules.home-manager (mkHomeManagerModule host)