What happens when your AI assistant can read your .env file?
In the previous guide, we configured OpenClaw as a systemd service to keep it running persistently on our remote VM. However, a critical security issue emerged: the API credentials stored in the .env file were accessible to any process running as our user — including AI assistants with file access. This follow-up documents the solution.
After setting up OpenClaw as described in the original guide, a significant security concern came to light. The API keys and Telegram bot tokens stored in ~/.openclaw/.env were accessible to any process running under our user account (demo_user).
This became particularly relevant when using AI coding assistants that have access to the filesystem. Even though the assistant was designed to help with development, it could potentially read the .env file and extract credentials — essentially handing over the keys to the kingdom.
The root cause was simple: the systemd service ran as the same user who owned the credentials, with no isolation between the two.
Several attempts were made to secure the credentials within the systemd framework:
EnvironmentFile in the systemd unit. While the file could be protected with restrictive permissions, any process running as the same user could still read it.openssl to encrypt the credentials and decrypt them at service startup. This required a password — but systemd services don't have access to interactive terminals.The core issue: systemd is built for automated, unattended service management — not for interactive password entry. There is no clean way to require a password at service startup within the systemd paradigm.
The solution combines encryption with manual operation:
opensslThis approach means OpenClaw only runs when we're actively using it — and the credentials remain encrypted on disk at all other times.
sudo systemctl stop openclaw
sudo systemctl disable openclaw
sudo rm -f /etc/systemd/system/openclaw.service
sudo systemctl daemon-reload
First, create a directory for the secrets:
mkdir -p ~/.openclaw/secrets
chmod 700 ~/.openclaw/secrets
Create a temporary file with your credentials:
cat > ~/.openclaw/secrets/creds.txt << 'EOF'
DEEPSEEK_API_KEY=sk-your-actual-api-key-here
TELEGRAM_BOT_TOKEN=your-telegram-bot-token-here
EOF
Encrypt the file (you'll be prompted for a password):
openssl enc -aes-256-cbc -salt -pbkdf2 -in ~/.openclaw/secrets/creds.txt -out ~/.openclaw/secrets/creds.enc
Securely remove the plaintext file:
shred -u ~/.openclaw/secrets/creds.txt
chmod 600 ~/.openclaw/secrets/creds.enc
cat > ~/.openclaw/secrets/decrypt.sh << 'EOF'
#!/bin/bash
read -sp "Enter decryption password: " PASSWORD
echo "$PASSWORD" | openssl enc -aes-256-cbc -d -salt -pbkdf2 -pass stdin -in ~/.openclaw/secrets/creds.enc 2>/dev/null
EOF
chmod 700 ~/.openclaw/secrets/decrypt.sh
Create a script that handles starting, stopping, and checking the status of OpenClaw:
cat > ~/openclawctl << 'EOF'
#!/bin/bash
OPENCLAW_DIR="$HOME/.openclaw"
PID_FILE="$OPENCLAW_DIR/openclaw.pid"
LOG_FILE="$OPENCLAW_DIR/openclaw.log"
DECRYPT_SCRIPT="$OPENCLAW_DIR/secrets/decrypt.sh"
mkdir -p "$OPENCLAW_DIR"
case "$1" in
start)
if [ -f "$PID_FILE" ]; then
PID=$(cat "$PID_FILE")
if kill -0 "$PID" 2>/dev/null; then
echo "OpenClaw is already running (PID: $PID)"
exit 1
else
rm -f "$PID_FILE"
fi
fi
echo "Starting OpenClaw..."
DECRYPTED=$(bash "$DECRYPT_SCRIPT")
if [ $? -ne 0 ] || [ -z "$DECRYPTED" ]; then
echo "Failed to decrypt credentials"
exit 1
fi
eval "$(echo "$DECRYPTED" | grep -v "^#" | grep -v "^$")"
if [ -z "$DEEPSEEK_API_KEY" ] && [ -z "$TELEGRAM_BOT_TOKEN" ]; then
echo "Failed to load credentials"
exit 1
fi
nohup env DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" TELEGRAM_BOT_TOKEN="$TELEGRAM_BOT_TOKEN" /usr/bin/npx openclaw gateway >> "$LOG_FILE" 2>&1 &
echo $! > "$PID_FILE"
sleep 3
if kill -0 $(cat "$PID_FILE") 2>/dev/null; then
echo "OpenClaw started (PID: $(cat "$PID_FILE"))"
else
echo "Failed to start OpenClaw - check $LOG_FILE"
rm -f "$PID_FILE"
exit 1
fi
;;
stop)
if [ ! -f "$PID_FILE" ]; then
echo "OpenClaw is not running"
exit 1
fi
PID=$(cat "$PID_FILE")
kill "$PID" 2>/dev/null
for i in 1 2 3 4 5; do
if ! kill -0 "$PID" 2>/dev/null; then
break
fi
sleep 1
done
if kill -0 "$PID" 2>/dev/null; then
echo "Force killing OpenClaw..."
kill -9 "$PID" 2>/dev/null
fi
rm -f "$PID_FILE"
echo "OpenClaw stopped"
;;
status)
if [ ! -f "$PID_FILE" ]; then
echo "OpenClaw is not running"
exit 1
fi
PID=$(cat "$PID_FILE")
if kill -0 "$PID" 2>/dev/null; then
echo "OpenClaw is running (PID: $PID)"
else
echo "OpenClaw is not running (stale PID file)"
rm -f "$PID_FILE"
fi
;;
logs)
if [ -f "$LOG_FILE" ]; then
tail -50 "$LOG_FILE"
else
echo "No log file found"
fi
;;
*)
echo "Usage: $0 {start|stop|status|logs}"
exit 1
;;
esac
EOF
chmod +x ~/openclawctl
shred -u ~/.openclaw/.env
Now instead of using systemctl, use the openclawctl script:
# Start OpenClaw (will prompt for password)
~/openclawctl start
# Check status
~/openclawctl status
# View logs
~/openclawctl logs
# Stop OpenClaw
~/openclawctl stop
The password prompt appears in the terminal, and OpenClaw runs in the background using nohup — meaning it persists even after the SSH session closes.
This approach significantly improves security:
However, there are trade-offs:
The transition from systemd service to manual operation with encrypted credentials addresses the original security concern. While less convenient than automatic startup, this approach ensures that API keys and bot tokens remain protected even from processes running as the same user.
The key insight is that the convenience of automatic startup directly conflicted with the security requirement of keeping credentials encrypted. By accepting manual operation, we achieve both security and peace of mind.