The original PocketClaw start script passed API keys directly as command-line arguments to the proot process. This worked, but had a serious security problem: on any Unix system, the command line of every running process is world-readable. Any process or user can inspect it via ps -eo args or by reading /proc/[pid]/cmdline.
On the Moto E2, this means any Termux plugin, any app with shell access, or anyone connected via ADB could see the full Kimi API key, Telegram bot token, OpenAI key, and Moonshot key in plain text. The ps output looked like this:
proot --rootfs=/data/data/com.termux/files/usr/var/lib/proot-distro/installed-rootfs/ubuntu \
/bin/bash -c "export TELEGRAM_BOT_TOKEN='8360xxxxx:AAHxxxxxxx' \
MOONSHOT_API_KEY='sk-xxxxxxxxxxxxxxxx' \
OPENAI_API_KEY='sk-xxxxxxxxxxxxxxxx' && ..."Every key, fully visible. On a shared system or a compromised device, this is a credential leak waiting to happen.
The fix: store keys in a file inside the proot rootfs, source it at runtime, and never pass secrets on the command line.
Create the env file:
# Inside proot (or write from Termux to the rootfs path):
cat > /root/.openclaw/env << 'EOF'
MOONSHOT_API_KEY=sk-xxxxxxxxxxxxxxxx
TELEGRAM_BOT_TOKEN=8360xxxxx:AAHxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx
OPENCLAW_MODEL_PROVIDER=kimi
EOF
# Lock down permissions — readable only by owner:
chmod 600 /root/.openclaw/envUpdate the start script to source the file instead of passing keys inline:
# Before (INSECURE — keys visible in ps):
proot --rootfs=$ROOTFS /bin/bash -c "
export TELEGRAM_BOT_TOKEN='8360...' MOONSHOT_API_KEY='sk-...' && \
node dist/cli.js gateway run --port 9000
"
# After (SECURE — keys loaded from file):
proot --rootfs=$ROOTFS /bin/bash -c "
. /root/.openclaw/env && \
export MOONSHOT_API_KEY TELEGRAM_BOT_TOKEN OPENAI_API_KEY && \
node dist/cli.js gateway run --port 9000
"After the native migration (no more proot), the same pattern is used directly:
# In $PREFIX/bin/start-openclaw (native stack):
. $PREFIX/var/lib/proot-distro/installed-rootfs/ubuntu/root/.openclaw/env
export MOONSHOT_API_KEY TELEGRAM_BOT_TOKEN OPENAI_API_KEY OPENCLAW_MODEL_PROVIDER
exec $PREFIX/bin/node22-icu $PREFIX/lib/node_modules/openclaw/dist/cli.js gateway run --port 9000# Check that ps no longer shows any keys:
ps -eo args | grep -i "api_key\|bot_token\|sk-"
# Expected: no output (or only the grep command itself)
# Verify the env file has correct permissions:
ls -la /root/.openclaw/env
# Expected: -rw------- 1 root root ... env
# Verify keys are loaded in the Node.js process:
# (from inside the gateway, via the dashboard API)
curl -s http://localhost:9000/api/status | grep -o '"provider":"[^"]*"'
# Expected: "provider":"kimi" (proves OPENCLAW_MODEL_PROVIDER loaded)
# Double-check /proc/[pid]/cmdline is clean:
cat /proc/$(pgrep -f openclaw-gateway)/cmdline | tr '\0' ' '
# Expected: node path with no API keys visibleexport in the shell are still visible in /proc/[pid]/environ, but this file is readable only by the process owner (or root). This is significantly more secure than /proc/[pid]/cmdline which is world-readableexport VAR separately from the sourcing. The pattern . /root/.openclaw/env && export VAR1 VAR2 sources the file (setting shell variables) and then exports them to the environment. Without the explicit export, child processes (Node.js) cannot see the variables\r character. This causes API authentication to fail with cryptic "invalid key" errors. Always ensure the file uses Unix line endings: tr -d '\r' < env > env.clean && mv env.clean env| Aspect | Before | After |
|---|---|---|
| Keys in ps output | 4 keys visible | 0 keys visible |
| Keys in /proc/cmdline | World-readable | Not present |
| Keys in /proc/environ | N/A (inline) | Owner-read only |
| File permissions | N/A | chmod 600 |
| Key rotation | Edit start script | Edit env file |
A simple fix with outsized security impact. The env file pattern became the standard for all secret management in PocketClaw, later extended with the dashboard KEYS page (Hack #50) for browser-based key management. The principle holds universally: never pass secrets as command-line arguments, on any system, in any language.