Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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']; \
Expand All @@ -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': { \
Expand Down
17 changes: 17 additions & 0 deletions bin/lib/onboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -1233,6 +1233,7 @@ function patchStagedDockerfile(
provider = null,
preferredInferenceApi = null,
webSearchConfig = null,
messagingChannels = [],
) {
const { providerKey, primaryModelRef, inferenceBaseUrl, inferenceApi, inferenceCompat } =
getSandboxInferenceConfig(model, provider, preferredInferenceApi);
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down
8 changes: 5 additions & 3 deletions scripts/nemoclaw-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading