From 99b47d37d2b28d4cb37fd118dcbd7cef1682da34 Mon Sep 17 00:00:00 2001 From: Sayali Kandarkar Date: Sun, 5 Apr 2026 03:59:02 -0700 Subject: [PATCH] fix(onboard): bake messaging channels into openclaw.json at build time In non-root mode (OpenShell no-new-privileges), the entrypoint cannot patch the immutable openclaw.json to add messaging channel config. This caused "Channel is required (no configured channels detected)" when the agent tried to send Discord messages. Bake channel entries with placeholder tokens into openclaw.json at image build time via NEMOCLAW_MESSAGING_CHANNELS_B64. The L7 proxy rewrites placeholders with real secrets at egress, so no runtime config patching is needed. Co-Authored-By: Claude Opus 4.6 (1M context) --- Dockerfile | 11 ++++++++++- bin/lib/onboard.js | 17 +++++++++++++++++ scripts/nemoclaw-start.sh | 8 +++++--- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index adef7f1d4..2c5430a6d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,6 +55,10 @@ ARG NEMOCLAW_INFERENCE_BASE_URL=https://inference.local/v1 ARG NEMOCLAW_INFERENCE_API=openai-completions ARG NEMOCLAW_INFERENCE_COMPAT_B64=e30= ARG NEMOCLAW_WEB_CONFIG_B64=e30= +# Base64-encoded JSON list of messaging channel names to pre-configure +# (e.g. ["discord","telegram"]). Channels are added with placeholder tokens +# so the L7 proxy can rewrite them at egress. Default: empty list. +ARG NEMOCLAW_MESSAGING_CHANNELS_B64=W10= # Set to "1" to disable device-pairing auth (development/headless only). # Default: "0" (device auth enabled — secure by default). ARG NEMOCLAW_DISABLE_DEVICE_AUTH=0 @@ -73,6 +77,7 @@ ENV NEMOCLAW_MODEL=${NEMOCLAW_MODEL} \ NEMOCLAW_INFERENCE_API=${NEMOCLAW_INFERENCE_API} \ NEMOCLAW_INFERENCE_COMPAT_B64=${NEMOCLAW_INFERENCE_COMPAT_B64} \ NEMOCLAW_WEB_CONFIG_B64=${NEMOCLAW_WEB_CONFIG_B64} \ + NEMOCLAW_MESSAGING_CHANNELS_B64=${NEMOCLAW_MESSAGING_CHANNELS_B64} \ NEMOCLAW_DISABLE_DEVICE_AUTH=${NEMOCLAW_DISABLE_DEVICE_AUTH} WORKDIR /sandbox @@ -94,6 +99,10 @@ inference_base_url = os.environ['NEMOCLAW_INFERENCE_BASE_URL']; \ inference_api = os.environ['NEMOCLAW_INFERENCE_API']; \ inference_compat = json.loads(base64.b64decode(os.environ['NEMOCLAW_INFERENCE_COMPAT_B64']).decode('utf-8')); \ web_config = json.loads(base64.b64decode(os.environ.get('NEMOCLAW_WEB_CONFIG_B64', 'e30=') or 'e30=').decode('utf-8')); \ +msg_channels = json.loads(base64.b64decode(os.environ.get('NEMOCLAW_MESSAGING_CHANNELS_B64', 'W10=') or 'W10=').decode('utf-8')); \ +_token_keys = {'discord': 'token', 'telegram': 'botToken', 'slack': 'botToken'}; \ +_env_keys = {'discord': 'DISCORD_BOT_TOKEN', 'telegram': 'TELEGRAM_BOT_TOKEN', 'slack': 'SLACK_BOT_TOKEN'}; \ +_ch_cfg = {ch: {'accounts': {'main': {_token_keys[ch]: f'openshell:resolve:env:{_env_keys[ch]}', 'enabled': True}}} for ch in msg_channels if ch in _token_keys}; \ parsed = urlparse(chat_ui_url); \ chat_origin = f'{parsed.scheme}://{parsed.netloc}' if parsed.scheme and parsed.netloc else 'http://127.0.0.1:18789'; \ origins = ['http://127.0.0.1:18789']; \ @@ -111,7 +120,7 @@ providers = { \ config = { \ 'agents': {'defaults': {'model': {'primary': primary_model_ref}}}, \ 'models': {'mode': 'merge', 'providers': providers}, \ - 'channels': {'defaults': {'configWrites': False}}, \ + 'channels': dict({'defaults': {'configWrites': False}}, **_ch_cfg), \ 'gateway': { \ 'mode': 'local', \ 'controlUi': { \ diff --git a/bin/lib/onboard.js b/bin/lib/onboard.js index 78a3e7fc1..e9581d652 100644 --- a/bin/lib/onboard.js +++ b/bin/lib/onboard.js @@ -1233,6 +1233,7 @@ function patchStagedDockerfile( provider = null, preferredInferenceApi = null, webSearchConfig = null, + messagingChannels = [], ) { const { providerKey, primaryModelRef, inferenceBaseUrl, inferenceApi, inferenceCompat } = getSandboxInferenceConfig(model, provider, preferredInferenceApi); @@ -1276,6 +1277,12 @@ function patchStagedDockerfile( /^ARG NEMOCLAW_DISABLE_DEVICE_AUTH=.*$/m, `ARG NEMOCLAW_DISABLE_DEVICE_AUTH=1`, ); + if (messagingChannels.length > 0) { + dockerfile = dockerfile.replace( + /^ARG NEMOCLAW_MESSAGING_CHANNELS_B64=.*$/m, + `ARG NEMOCLAW_MESSAGING_CHANNELS_B64=${encodeDockerJsonArg(messagingChannels)}`, + ); + } fs.writeFileSync(dockerfilePath, dockerfile); } @@ -2598,6 +2605,15 @@ async function createSandbox( ); process.exit(1); } + const activeMessagingChannels = messagingTokenDefs + .filter(({ token }) => !!token) + .map(({ envKey }) => { + if (envKey === "DISCORD_BOT_TOKEN") return "discord"; + if (envKey === "SLACK_BOT_TOKEN") return "slack"; + if (envKey === "TELEGRAM_BOT_TOKEN") return "telegram"; + return null; + }) + .filter(Boolean); patchStagedDockerfile( stagedDockerfile, model, @@ -2606,6 +2622,7 @@ async function createSandbox( provider, preferredInferenceApi, webSearchConfig, + activeMessagingChannels, ); // Only pass non-sensitive env vars to the sandbox. Credentials flow through // OpenShell providers — the gateway injects them as placeholders and the L7 diff --git a/scripts/nemoclaw-start.sh b/scripts/nemoclaw-start.sh index cd039e5c6..5d27352be 100755 --- a/scripts/nemoclaw-start.sh +++ b/scripts/nemoclaw-start.sh @@ -145,12 +145,14 @@ configure_messaging_channels() { # Real tokens are never visible inside the sandbox. # # Requires root: openclaw.json is owned by root with chmod 444. - # Non-root mode cannot patch the config — channels are unavailable. + # Non-root mode relies on channels being pre-baked into openclaw.json + # at build time via NEMOCLAW_MESSAGING_CHANNELS_B64. [ -n "${TELEGRAM_BOT_TOKEN:-}" ] || [ -n "${DISCORD_BOT_TOKEN:-}" ] || [ -n "${SLACK_BOT_TOKEN:-}" ] || return 0 if [ "$(id -u)" -ne 0 ]; then - echo "[channels] Messaging tokens detected but running as non-root — skipping openclaw.json patch" >&2 - echo "[channels] Channels still work via L7 proxy token rewriting (no config patch needed)" >&2 + echo "[channels] Messaging tokens detected (non-root mode)" >&2 + echo "[channels] Channel entries should be baked into openclaw.json at build time" >&2 + echo "[channels] (NEMOCLAW_MESSAGING_CHANNELS_B64). L7 proxy rewrites placeholder tokens at egress." >&2 return 0 fi