From cc0cd00a55b37ff5035f89b694e686b771cf1efc Mon Sep 17 00:00:00 2001 From: Shun Kakinoki Date: Mon, 2 Feb 2026 09:12:50 +0900 Subject: [PATCH 1/9] refactor: remove legacy secrets directory from hydrate script --- config/openclaw/hydrate.sh | 9 ++++----- dotagents | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/config/openclaw/hydrate.sh b/config/openclaw/hydrate.sh index 62b230499..35a91fe39 100644 --- a/config/openclaw/hydrate.sh +++ b/config/openclaw/hydrate.sh @@ -7,7 +7,6 @@ STATE_DIR="${OPENCLAW_STATE_DIR:-${HOME}/.openclaw}" CONFIG="${OPENCLAW_CONFIG_PATH:-${STATE_DIR}/openclaw.json}" TEMPLATE="@template@" SECRETS_DIR="${HOME}/.config/openclaw" -LEGACY_SECRETS_DIR="${HOME}/.config/clawdbot" ENV_FILE="${HOME}/dotfiles/.env" # Source .env if it exists @@ -30,10 +29,10 @@ read_secret() { } # Load secrets from files or environment -CLIPROXY_API_KEY="${OPENCLAW_CLIPROXY_API_KEY:-${CLIPROXY_API_KEY:-$(read_secret "${SECRETS_DIR}/cliproxy-key" "${LEGACY_SECRETS_DIR}/cliproxy-key")}}" -TELEGRAM_TOKEN="${OPENCLAW_TELEGRAM_TOKEN:-${TELEGRAM_TOKEN:-$(read_secret "${SECRETS_DIR}/telegram-token" "${LEGACY_SECRETS_DIR}/telegram-token")}}" -GATEWAY_TOKEN="${OPENCLAW_GATEWAY_TOKEN:-${GATEWAY_TOKEN:-$(read_secret "${SECRETS_DIR}/gateway-token" "${LEGACY_SECRETS_DIR}/gateway-token")}}" -ANTHROPIC_API_KEY="${OPENCLAW_ANTHROPIC_API_KEY:-${ANTHROPIC_API_KEY:-$(read_secret "${SECRETS_DIR}/anthropic-key" "${LEGACY_SECRETS_DIR}/anthropic-key")}}" +CLIPROXY_API_KEY="${OPENCLAW_CLIPROXY_API_KEY:-${CLIPROXY_API_KEY:-$(read_secret "${SECRETS_DIR}/cliproxy-key")}}" +TELEGRAM_TOKEN="${OPENCLAW_TELEGRAM_TOKEN:-${TELEGRAM_TOKEN:-$(read_secret "${SECRETS_DIR}/telegram-token")}}" +GATEWAY_TOKEN="${OPENCLAW_GATEWAY_TOKEN:-${GATEWAY_TOKEN:-$(read_secret "${SECRETS_DIR}/gateway-token")}}" +ANTHROPIC_API_KEY="${OPENCLAW_ANTHROPIC_API_KEY:-${ANTHROPIC_API_KEY:-$(read_secret "${SECRETS_DIR}/anthropic-key")}}" # Chromium path (injected by nix) CHROMIUM_PATH="@chromium@/bin/chromium" diff --git a/dotagents b/dotagents index 565bbe58d..2e69e55b2 160000 --- a/dotagents +++ b/dotagents @@ -1 +1 @@ -Subproject commit 565bbe58ded57d48484c1e81c9731085761e66b8 +Subproject commit 2e69e55b2d88be86aff8afb0c2a1a999f416e185 From f512a425505417b25d58e74de6025a9fd8784ea8 Mon Sep 17 00:00:00 2001 From: Shun Kakinoki Date: Mon, 2 Feb 2026 09:18:42 +0900 Subject: [PATCH 2/9] feat: enhance OpenClaw hydration script for improved mode handling and config generation --- config/openclaw/hydrate.sh | 68 +++++++++++++++-------- home-manager/modules/openclaw/default.nix | 29 +++++++--- 2 files changed, 67 insertions(+), 30 deletions(-) diff --git a/config/openclaw/hydrate.sh b/config/openclaw/hydrate.sh index 35a91fe39..e77a2dc89 100644 --- a/config/openclaw/hydrate.sh +++ b/config/openclaw/hydrate.sh @@ -1,8 +1,10 @@ #!/usr/bin/env bash -# OpenClaw gateway start script with runtime secret injection +# OpenClaw config hydration with runtime secret injection +# Mode is set by nix: "gateway" for Kyber, "client" for macOS # shellcheck source=/dev/null set -euo pipefail +MODE="@mode@" STATE_DIR="${OPENCLAW_STATE_DIR:-${HOME}/.openclaw}" CONFIG="${OPENCLAW_CONFIG_PATH:-${STATE_DIR}/openclaw.json}" TEMPLATE="@template@" @@ -28,33 +30,53 @@ read_secret() { echo "" } -# Load secrets from files or environment -CLIPROXY_API_KEY="${OPENCLAW_CLIPROXY_API_KEY:-${CLIPROXY_API_KEY:-$(read_secret "${SECRETS_DIR}/cliproxy-key")}}" -TELEGRAM_TOKEN="${OPENCLAW_TELEGRAM_TOKEN:-${TELEGRAM_TOKEN:-$(read_secret "${SECRETS_DIR}/telegram-token")}}" +# Load gateway token (required for both modes) GATEWAY_TOKEN="${OPENCLAW_GATEWAY_TOKEN:-${GATEWAY_TOKEN:-$(read_secret "${SECRETS_DIR}/gateway-token")}}" -ANTHROPIC_API_KEY="${OPENCLAW_ANTHROPIC_API_KEY:-${ANTHROPIC_API_KEY:-$(read_secret "${SECRETS_DIR}/anthropic-key")}}" -# Chromium path (injected by nix) -CHROMIUM_PATH="@chromium@/bin/chromium" +if [ -z "${GATEWAY_TOKEN}" ]; then + echo "Warning: OPENCLAW_GATEWAY_TOKEN not set, skipping OpenClaw hydration" >&2 + exit 0 +fi -# Create state directory if needed mkdir -p "$STATE_DIR" -# Generate config from template with secret substitution -@sed@ \ - -e "s|__CLIPROXY_API_KEY__|${CLIPROXY_API_KEY}|g" \ - -e "s|__TELEGRAM_TOKEN__|${TELEGRAM_TOKEN}|g" \ - -e "s|__GATEWAY_TOKEN__|${GATEWAY_TOKEN}|g" \ - -e "s|__CHROMIUM_PATH__|${CHROMIUM_PATH}|g" \ - -e "s|__HOME__|${HOME}|g" \ - "$TEMPLATE" >"$CONFIG" +if [ "$MODE" = "gateway" ]; then + # Gateway mode (Kyber): hydrate full template and start gateway + CLIPROXY_API_KEY="${OPENCLAW_CLIPROXY_API_KEY:-${CLIPROXY_API_KEY:-$(read_secret "${SECRETS_DIR}/cliproxy-key")}}" + TELEGRAM_TOKEN="${OPENCLAW_TELEGRAM_TOKEN:-${TELEGRAM_TOKEN:-$(read_secret "${SECRETS_DIR}/telegram-token")}}" + ANTHROPIC_API_KEY="${OPENCLAW_ANTHROPIC_API_KEY:-${ANTHROPIC_API_KEY:-$(read_secret "${SECRETS_DIR}/anthropic-key")}}" + CHROMIUM_PATH="@chromium@/bin/chromium" -echo "Generated openclaw config at $CONFIG" >&2 + @sed@ \ + -e "s|__CLIPROXY_API_KEY__|${CLIPROXY_API_KEY}|g" \ + -e "s|__TELEGRAM_TOKEN__|${TELEGRAM_TOKEN}|g" \ + -e "s|__GATEWAY_TOKEN__|${GATEWAY_TOKEN}|g" \ + -e "s|__CHROMIUM_PATH__|${CHROMIUM_PATH}|g" \ + -e "s|__HOME__|${HOME}|g" \ + "$TEMPLATE" >"$CONFIG" -# Export Anthropic API key for OpenClaw -if [ -n "$ANTHROPIC_API_KEY" ]; then - export ANTHROPIC_API_KEY -fi + echo "Generated openclaw gateway config at $CONFIG" >&2 + + if [ -n "$ANTHROPIC_API_KEY" ]; then + export ANTHROPIC_API_KEY + fi + + exec @openclaw@/bin/openclaw gateway --port 18789 "$@" -# Start OpenClaw gateway -exec @openclaw@/bin/openclaw gateway --port 18789 "$@" +else + # Client mode (macOS): generate remote config with gateway token + cat >"$CONFIG" <&2 +fi diff --git a/home-manager/modules/openclaw/default.nix b/home-manager/modules/openclaw/default.nix index c0d0823ba..7b37c6a8d 100644 --- a/home-manager/modules/openclaw/default.nix +++ b/home-manager/modules/openclaw/default.nix @@ -1,22 +1,37 @@ { config, lib, + pkgs, inputs, ... }: let inherit (inputs) host; homeDir = config.home.homeDirectory; + + mode = if host.isKyber then "gateway" else "client"; + + hydrateScript = pkgs.replaceVars ../../config/openclaw/hydrate.sh ({ + sed = "${pkgs.gnused}/bin/sed"; + template = ../../config/openclaw/openclaw.template.json; + inherit mode; + } // (if host.isKyber then { + chromium = pkgs.chromium; + openclaw = "${homeDir}/.bun"; + } else { + # Client mode: gateway-only placeholders are unused but must be substituted + chromium = "/unused"; + openclaw = "/unused"; + })); in -# Only enable on kyber (gateway host) -lib.mkIf (host.isKyber) { - # Ensure OpenClaw directories exist - # Note: Node symlink is managed by fnm module - home.activation.openclawSetup = config.lib.dag.entryAfter [ "writeBoundary" ] '' - mkdir -p /tmp/openclaw +{ + # Hydrate OpenClaw config from .env secrets + # Gateway mode on Kyber, client mode everywhere else + home.activation.hydrateOpenclawConfig = config.lib.dag.entryAfter [ "writeBoundary" ] '' mkdir -p ${homeDir}/.openclaw + ${pkgs.bash}/bin/bash ${hydrateScript} || true ''; - +} // lib.optionalAttrs (host.isKyber) { # Systemd service for OpenClaw gateway systemd.user.services.openclaw-gateway = { Unit = { From d2e2a5e759fddc5620a51d214ea1847f553e5e36 Mon Sep 17 00:00:00 2001 From: Shun Kakinoki Date: Mon, 2 Feb 2026 09:24:12 +0900 Subject: [PATCH 3/9] refactor: remove legacy secrets directory fallback from hydrate spec --- config/default.nix | 1 + config/openclaw/default.nix | 33 +++++++++++++++++++++++ home-manager/modules/openclaw/default.nix | 28 +++++-------------- spec/openclaw_hydrate_spec.sh | 5 ---- 4 files changed, 40 insertions(+), 27 deletions(-) create mode 100644 config/openclaw/default.nix diff --git a/config/default.nix b/config/default.nix index 18708a6f5..093ad5335 100644 --- a/config/default.nix +++ b/config/default.nix @@ -16,6 +16,7 @@ ./k3s ./karabiner ./llm + ./openclaw ./opencode ./pi ./serena diff --git a/config/openclaw/default.nix b/config/openclaw/default.nix new file mode 100644 index 000000000..fea48f17b --- /dev/null +++ b/config/openclaw/default.nix @@ -0,0 +1,33 @@ +{ + config, + lib, + pkgs, + inputs, + ... +}: +let + inherit (inputs) host; + homeDir = config.home.homeDirectory; + + mode = if host.isKyber then "gateway" else "client"; + + hydrateScript = pkgs.replaceVars ./hydrate.sh ({ + sed = "${pkgs.gnused}/bin/sed"; + template = ./openclaw.template.json; + inherit mode; + } // (if host.isKyber then { + chromium = pkgs.chromium; + openclaw = "${homeDir}/.bun"; + } else { + chromium = "/unused"; + openclaw = "/unused"; + })); +in +{ + # Hydrate OpenClaw config from .env secrets + # Gateway mode on Kyber, client mode everywhere else + home.activation.hydrateOpenclawConfig = config.lib.dag.entryAfter [ "writeBoundary" ] '' + mkdir -p ${homeDir}/.openclaw + ${pkgs.bash}/bin/bash ${hydrateScript} || true + ''; +} diff --git a/home-manager/modules/openclaw/default.nix b/home-manager/modules/openclaw/default.nix index 7b37c6a8d..e7c52c1bf 100644 --- a/home-manager/modules/openclaw/default.nix +++ b/home-manager/modules/openclaw/default.nix @@ -1,37 +1,21 @@ { config, lib, - pkgs, inputs, ... }: let inherit (inputs) host; homeDir = config.home.homeDirectory; - - mode = if host.isKyber then "gateway" else "client"; - - hydrateScript = pkgs.replaceVars ../../config/openclaw/hydrate.sh ({ - sed = "${pkgs.gnused}/bin/sed"; - template = ../../config/openclaw/openclaw.template.json; - inherit mode; - } // (if host.isKyber then { - chromium = pkgs.chromium; - openclaw = "${homeDir}/.bun"; - } else { - # Client mode: gateway-only placeholders are unused but must be substituted - chromium = "/unused"; - openclaw = "/unused"; - })); in -{ - # Hydrate OpenClaw config from .env secrets - # Gateway mode on Kyber, client mode everywhere else - home.activation.hydrateOpenclawConfig = config.lib.dag.entryAfter [ "writeBoundary" ] '' +# Only enable on kyber (gateway host) +lib.mkIf (host.isKyber) { + # Ensure OpenClaw directories exist + home.activation.openclawSetup = config.lib.dag.entryAfter [ "writeBoundary" ] '' + mkdir -p /tmp/openclaw mkdir -p ${homeDir}/.openclaw - ${pkgs.bash}/bin/bash ${hydrateScript} || true ''; -} // lib.optionalAttrs (host.isKyber) { + # Systemd service for OpenClaw gateway systemd.user.services.openclaw-gateway = { Unit = { diff --git a/spec/openclaw_hydrate_spec.sh b/spec/openclaw_hydrate_spec.sh index 0d0bd371b..429ef7f70 100644 --- a/spec/openclaw_hydrate_spec.sh +++ b/spec/openclaw_hydrate_spec.sh @@ -27,11 +27,6 @@ When run bash -c "grep 'SECRETS_DIR=' '$SCRIPT'" The output should include '.config/openclaw' End -It 'keeps legacy ~/.config/clawdbot fallback' -When run bash -c "grep 'LEGACY_SECRETS_DIR=' '$SCRIPT'" -The output should include '.config/clawdbot' -End - It 'reads from dotfiles .env file' When run bash -c "grep 'ENV_FILE=' '$SCRIPT'" The output should include 'dotfiles/.env' From 3f39994369743683de21fa021c2a839cfc2bc621 Mon Sep 17 00:00:00 2001 From: Shun Kakinoki Date: Mon, 2 Feb 2026 09:27:02 +0900 Subject: [PATCH 4/9] fix: rename password field to token in client config generation --- config/openclaw/hydrate.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/openclaw/hydrate.sh b/config/openclaw/hydrate.sh index e77a2dc89..b666eb1ee 100644 --- a/config/openclaw/hydrate.sh +++ b/config/openclaw/hydrate.sh @@ -72,7 +72,7 @@ else "remote": { "transport": "direct", "url": "wss://kyber.tail950b36.ts.net", - "password": "${GATEWAY_TOKEN}" + "token": "${GATEWAY_TOKEN}" } } } From bc69aff298a659f1ec2cf86b996bb1991d300228 Mon Sep 17 00:00:00 2001 From: Shun Kakinoki Date: Tue, 3 Feb 2026 13:45:55 +0900 Subject: [PATCH 5/9] feat: replace home.file symlink with activation script for codex config --- config/codex/default.nix | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/config/codex/default.nix b/config/codex/default.nix index 1f93cf834..da6b08e1e 100644 --- a/config/codex/default.nix +++ b/config/codex/default.nix @@ -1,7 +1,9 @@ -{ config, ... }: +{ config, lib, ... }: { - home.file.".codex/config.toml" = { - source = ./config.toml; - force = true; - }; + # Use activation script instead of home.file symlink + # Codex CLI uses atomic writes that break symlinks, so we force-copy on each switch + home.activation.codexConfig = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + $DRY_RUN_CMD cp -f ${./config.toml} ~/.codex/config.toml + $DRY_RUN_CMD chmod 600 ~/.codex/config.toml + ''; } From 23d10a574ffb81f07e639bccdd22b3ae6a1c65c2 Mon Sep 17 00:00:00 2001 From: Shun Kakinoki Date: Tue, 3 Feb 2026 13:58:09 +0900 Subject: [PATCH 6/9] fix: add suppress_unstable_features_warning to config.toml --- config/codex/config.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/codex/config.toml b/config/codex/config.toml index 7d6f158ae..6b436a083 100644 --- a/config/codex/config.toml +++ b/config/codex/config.toml @@ -5,6 +5,7 @@ notify = [ "notify", ] +suppress_unstable_features_warning = true web_search = "cached" [features] From 8a8c4ed168d282d0072e7f35e2dfa79aff8a4999 Mon Sep 17 00:00:00 2001 From: Shun Kakinoki Date: Tue, 3 Feb 2026 13:59:08 +0900 Subject: [PATCH 7/9] feat: add Codex app to persistent dock applications --- nix-darwin/config/dock.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nix-darwin/config/dock.nix b/nix-darwin/config/dock.nix index 10f71d3fc..0c0492e2b 100644 --- a/nix-darwin/config/dock.nix +++ b/nix-darwin/config/dock.nix @@ -17,6 +17,7 @@ "/System/Applications/Notes.app" "/System/Applications/Mail.app" "/Applications/ChatGPT.app" + "/Applications/Codex.app" "/Applications/Claude.app" "/Applications/OpenClaw.app" "/Applications/Google Chrome.app" From 52deedde2712fafc53014bc3d660f5469734086c Mon Sep 17 00:00:00 2001 From: Shun Kakinoki Date: Tue, 3 Feb 2026 13:59:20 +0900 Subject: [PATCH 8/9] fix: remove duplicate entry for Codex app in persistent dock applications --- nix-darwin/config/dock.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix-darwin/config/dock.nix b/nix-darwin/config/dock.nix index 0c0492e2b..395973c55 100644 --- a/nix-darwin/config/dock.nix +++ b/nix-darwin/config/dock.nix @@ -17,7 +17,6 @@ "/System/Applications/Notes.app" "/System/Applications/Mail.app" "/Applications/ChatGPT.app" - "/Applications/Codex.app" "/Applications/Claude.app" "/Applications/OpenClaw.app" "/Applications/Google Chrome.app" @@ -31,6 +30,7 @@ "/Applications/Ghostty.app" "/Applications/Linear.app" "/Applications/Tailscale.app" + "/Applications/Codex.app" "/Applications/Cursor.app" "/Applications/Visual Studio Code.app" "/Applications/Visual Studio Code - Insiders.app" From db0c880ca69a112130dffbb29a6ff7dcf10924a8 Mon Sep 17 00:00:00 2001 From: Shun Kakinoki Date: Tue, 3 Feb 2026 14:55:36 +0900 Subject: [PATCH 9/9] chore: update dotagents submodule to latest --- dotagents | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotagents b/dotagents index 2e69e55b2..25b1492f4 160000 --- a/dotagents +++ b/dotagents @@ -1 +1 @@ -Subproject commit 2e69e55b2d88be86aff8afb0c2a1a999f416e185 +Subproject commit 25b1492f463c3ea56609315e786f2530771a71ca