Start OpenClaw node host on ClawdBox boot

This commit is contained in:
Clawdbot
2026-06-07 14:30:07 +10:00
parent 18f559c4e6
commit 48e30b9d0e
8 changed files with 244 additions and 15 deletions

View File

@@ -35,7 +35,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
strace \ strace \
file \ file \
less \ less \
&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ && curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
&& apt-get install -y nodejs \ && apt-get install -y nodejs \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
@@ -99,6 +99,16 @@ RUN mkdir -p /data /data/.cache/pip /data/.cache/npm /data/.local && \
# Set working directory to the persistent volume # Set working directory to the persistent volume
WORKDIR /data WORKDIR /data
# Install startup and OpenClaw node helper scripts
COPY scripts/clawdbox-entrypoint /usr/local/bin/clawdbox-entrypoint
COPY scripts/start-clawdbox-node /usr/local/bin/start-clawdbox-node
COPY scripts/status-clawdbox-node /usr/local/bin/status-clawdbox-node
COPY scripts/stop-clawdbox-node /usr/local/bin/stop-clawdbox-node
RUN chmod +x /usr/local/bin/clawdbox-entrypoint \
/usr/local/bin/start-clawdbox-node \
/usr/local/bin/status-clawdbox-node \
/usr/local/bin/stop-clawdbox-node
# Expose SSH port (non-privileged) # Expose SSH port (non-privileged)
EXPOSE 2222 EXPOSE 2222
@@ -119,16 +129,5 @@ RUN echo 'export PS1="\[\e[32m\]\u@clawdbox\[\e[m\]:\[\e[34m\]\w\[\e[m\]\$ "' >>
echo 'export HISTSIZE=10000' >> /home/claw/.bashrc && \ echo 'export HISTSIZE=10000' >> /home/claw/.bashrc && \
echo 'export HISTFILESIZE=10000' >> /home/claw/.bashrc echo 'export HISTFILESIZE=10000' >> /home/claw/.bashrc
# Start SSH daemon # Start SSH in the foreground and opportunistically start the OpenClaw node host.
# The keys are generated if they don't exist on the persistent volume CMD ["/usr/local/bin/clawdbox-entrypoint"]
CMD ["/bin/bash", "-c", "\
mkdir -p /data/ssh && \
for keytype in rsa ecdsa ed25519; do \
if [ ! -f /data/ssh/ssh_host_${keytype}_key ]; then \
echo \"Generating persistent host ${keytype} key...\"; \
ssh-keygen -q -f /data/ssh/ssh_host_${keytype}_key -N '' -t ${keytype}; \
fi; \
done && \
chmod 600 /data/ssh/ssh_host_*_key && \
ln -sf /data/.gitconfig ~/.gitconfig && \
/usr/sbin/sshd -D -e -f /etc/ssh/sshd_config"]

View File

@@ -11,13 +11,14 @@ This container provides a stable, tool-rich environment for the AI agent to:
## Tools Included ## Tools Included
- **Core:** curl, wget, git, jq, yq, unzip, tar, vim/nano, tree, less - **Core:** curl, wget, git, jq, yq, unzip, tar, vim/nano, tree, less
- **Dev:** python3 (pip/venv), build-essential, nodejs (v20), npm - **Dev:** python3 (pip/venv), build-essential, nodejs (v22), npm
- **Python Libraries:** httpie, pyyaml, requests, black, ipython - **Python Libraries:** httpie, pyyaml, requests, black, ipython
- **Network:** ping, dnsutils, net-tools, openssh-server/client, sshpass - **Network:** ping, dnsutils, net-tools, openssh-server/client, sshpass
- **Media:** ffmpeg - **Media:** ffmpeg
- **Monitoring:** htop, tmux, ncdu, strace - **Monitoring:** htop, tmux, ncdu, strace
- **Kubernetes:** OpenShift CLI (oc) - **Kubernetes:** OpenShift CLI (oc)
- **Search:** ripgrep (fast grep alternative) - **Search:** ripgrep (fast grep alternative)
- **Agent node:** self-starting OpenClaw node host helper for `ClawdBox-CLI`
## Prerequisites ## Prerequisites
@@ -76,6 +77,14 @@ The `/data` volume preserves data across container restarts:
``` ```
/data/ /data/
├── ssh/ # SSH host keys (auto-generated on first run) ├── ssh/ # SSH host keys (auto-generated on first run)
├── agents/
│ └── clawd/ # Durable OpenClaw agent workspace and node host
│ ├── bin/ # start/status/stop helpers and openclaw symlink
│ ├── logs/ # node-host logs and pid file
│ ├── notes/ # operator notes
│ ├── src/ # source checkouts and experiments
│ ├── tmp/ # scratch files
│ └── tools/ # persistent tool installs
├── scripts/ # Helper scripts (from ConfigMap, read-only) ├── scripts/ # Helper scripts (from ConfigMap, read-only)
│ ├── disk-usage.sh │ ├── disk-usage.sh
│ ├── health-check.sh │ ├── health-check.sh
@@ -92,6 +101,38 @@ The `/data` volume preserves data across container restarts:
**Storage:** 10Gi PersistentVolumeClaim (ReadWriteOnce) **Storage:** 10Gi PersistentVolumeClaim (ReadWriteOnce)
## OpenClaw Node Host
On container startup, ClawdBox keeps SSH as the foreground service and
opportunistically starts an OpenClaw node host named `ClawdBox-CLI` in the
background.
The helper scripts are copied into `/data/agents/clawd/bin/` on each startup:
```bash
start-clawdbox-node
status-clawdbox-node
stop-clawdbox-node
```
The node host installs `openclaw@2026.6.1` under
`/data/agents/clawd/tools/openclaw-node` if it is missing or stale. It gets the
Gateway token from `OPENCLAW_GATEWAY_TOKEN` when set, otherwise from the live
`openclaw` deployment via the mounted kubeconfig.
Useful overrides:
```bash
OPENCLAW_VERSION=2026.6.1
OPENCLAW_GATEWAY_HOST=openclaw.apps.lab.apilab.us
OPENCLAW_GATEWAY_PORT=443
OPENCLAW_GATEWAY_TLS=true
CLAWDBOX_NODE_DISPLAY_NAME=ClawdBox-CLI
```
If node startup fails, the pod still starts and SSH remains available. Check
`/data/agents/clawd/logs/node-host.log` for details.
## Common Tasks ## Common Tasks
### Quick Operations (Using Makefile) ### Quick Operations (Using Makefile)

View File

@@ -41,6 +41,16 @@ data:
fi fi
echo "" echo ""
echo "=== OpenClaw Node Host ==="
if command -v status-clawdbox-node > /dev/null 2>&1; then
status-clawdbox-node
elif [ -x /data/agents/clawd/bin/status-clawdbox-node ]; then
/data/agents/clawd/bin/status-clawdbox-node
else
echo "status-clawdbox-node helper not found"
fi
echo ""
echo "=== Disk Space ===" echo "=== Disk Space ==="
df -h /data df -h /data
echo "" echo ""

View File

@@ -58,6 +58,16 @@ spec:
value: noninteractive value: noninteractive
- name: TZ - name: TZ
value: UTC value: UTC
- name: OPENCLAW_VERSION
value: "2026.6.1"
- name: OPENCLAW_GATEWAY_HOST
value: openclaw.apps.lab.apilab.us
- name: OPENCLAW_GATEWAY_PORT
value: "443"
- name: OPENCLAW_GATEWAY_TLS
value: "true"
- name: CLAWDBOX_NODE_DISPLAY_NAME
value: ClawdBox-CLI
startupProbe: startupProbe:
tcpSocket: tcpSocket:
port: ssh port: ssh

View File

@@ -0,0 +1,44 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT=/data/agents/clawd
mkdir -p /data/ssh /data/.cache/pip /data/.cache/npm /data/.local
mkdir -p "$ROOT/bin" "$ROOT/src" "$ROOT/tmp" "$ROOT/logs" "$ROOT/notes" "$ROOT/tools"
for keytype in rsa ecdsa ed25519; do
if [ ! -f "/data/ssh/ssh_host_${keytype}_key" ]; then
echo "Generating persistent host ${keytype} key..."
ssh-keygen -q -f "/data/ssh/ssh_host_${keytype}_key" -N '' -t "$keytype"
fi
done
chmod 600 /data/ssh/ssh_host_*_key
if [ -f /data/.gitconfig ]; then
ln -sf /data/.gitconfig "$HOME/.gitconfig"
fi
cat > "$ROOT/README.md" <<'EOF'
# ClawdBox Agent Workspace
Durable agent workspace for OpenClaw work that should survive pod restarts.
- `bin/` helper commands
- `src/` source checkouts and experiments
- `tmp/` scratch files
- `logs/` process logs
- `notes/` operator notes
- `tools/` persistent tool installs
EOF
for script in start-clawdbox-node status-clawdbox-node stop-clawdbox-node; do
cp "/usr/local/bin/${script}" "$ROOT/bin/${script}"
done
export PATH="$ROOT/bin:/usr/local/bin:$PATH"
start-clawdbox-node || {
echo "ClawdBox-CLI node host did not start; SSH will remain available." >&2
}
exec /usr/sbin/sshd -D -e -f /etc/ssh/sshd_config

View File

@@ -0,0 +1,84 @@
#!/usr/bin/env sh
set -eu
ROOT=${CLAWDBOX_AGENT_ROOT:-/data/agents/clawd}
LOG_DIR="$ROOT/logs"
PID_FILE="$LOG_DIR/node-host.pid"
LOG_FILE="$LOG_DIR/node-host.log"
OPENCLAW_DIR="$ROOT/tools/openclaw-node"
OPENCLAW_VERSION=${OPENCLAW_VERSION:-2026.6.1}
DISPLAY_NAME=${CLAWDBOX_NODE_DISPLAY_NAME:-ClawdBox-CLI}
GATEWAY_HOST=${OPENCLAW_GATEWAY_HOST:-openclaw.apps.lab.apilab.us}
GATEWAY_PORT=${OPENCLAW_GATEWAY_PORT:-443}
mkdir -p "$ROOT/bin" "$ROOT/tools" "$LOG_DIR" "$OPENCLAW_DIR"
log() {
printf '%s %s\n' "$(date -Iseconds)" "$*" | tee -a "$LOG_FILE"
}
if [ -f "$PID_FILE" ]; then
old=$(cat "$PID_FILE" 2>/dev/null || true)
case "$old" in
*[!0-9]*|"") ;;
*)
if kill -0 "$old" 2>/dev/null; then
echo "$DISPLAY_NAME already running: pid $old"
exit 0
fi
;;
esac
fi
if [ ! -f "$OPENCLAW_DIR/package.json" ]; then
cat > "$OPENCLAW_DIR/package.json" <<EOF
{
"name": "clawdbox-openclaw-node",
"private": true,
"dependencies": {
"openclaw": "$OPENCLAW_VERSION"
}
}
EOF
fi
installed_version=""
if [ -f "$OPENCLAW_DIR/node_modules/openclaw/package.json" ]; then
installed_version=$(node -p "require('$OPENCLAW_DIR/node_modules/openclaw/package.json').version" 2>/dev/null || true)
fi
if [ "$installed_version" != "$OPENCLAW_VERSION" ]; then
log "Installing openclaw@$OPENCLAW_VERSION under $OPENCLAW_DIR"
npm install --prefix "$OPENCLAW_DIR" "openclaw@$OPENCLAW_VERSION" --omit=dev >> "$LOG_FILE" 2>&1
fi
OPENCLAW_BIN="$OPENCLAW_DIR/node_modules/.bin/openclaw"
if [ ! -x "$OPENCLAW_BIN" ]; then
log "OpenClaw binary not found at $OPENCLAW_BIN"
exit 1
fi
ln -sf "$OPENCLAW_BIN" "$ROOT/bin/openclaw"
TOKEN=${OPENCLAW_GATEWAY_TOKEN:-}
if [ -z "$TOKEN" ]; then
TOKEN=$(oc get deployment openclaw -n openclaw -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="OPENCLAW_GATEWAY_TOKEN")].value}' 2>/dev/null || true)
fi
if [ -z "$TOKEN" ]; then
log "OPENCLAW_GATEWAY_TOKEN is unavailable; skipping node-host startup"
exit 1
fi
TLS_FLAG=""
if [ "${OPENCLAW_GATEWAY_TLS:-true}" = "true" ]; then
TLS_FLAG="--tls"
fi
log "Starting $DISPLAY_NAME against $GATEWAY_HOST:$GATEWAY_PORT"
OPENCLAW_GATEWAY_TOKEN="$TOKEN" nohup "$OPENCLAW_BIN" node run \
--display-name "$DISPLAY_NAME" \
--host "$GATEWAY_HOST" \
--port "$GATEWAY_PORT" \
$TLS_FLAG >> "$LOG_FILE" 2>&1 &
echo $! > "$PID_FILE"
sleep 1
echo "Started $DISPLAY_NAME node host: pid $(cat "$PID_FILE")"

View File

@@ -0,0 +1,18 @@
#!/usr/bin/env sh
set -eu
ROOT=${CLAWDBOX_AGENT_ROOT:-/data/agents/clawd}
PID_FILE="$ROOT/logs/node-host.pid"
LOG_FILE="$ROOT/logs/node-host.log"
if [ -f "$PID_FILE" ]; then
pid=$(cat "$PID_FILE" 2>/dev/null || true)
case "$pid" in
*[!0-9]*|"") echo "Invalid pid file: $pid" ;;
*) ps -p "$pid" -o pid,etime,cmd || true ;;
esac
else
echo "No pid file"
fi
sed -n '1,120p' "$LOG_FILE" 2>/dev/null || true

View File

@@ -0,0 +1,23 @@
#!/usr/bin/env sh
set -eu
ROOT=${CLAWDBOX_AGENT_ROOT:-/data/agents/clawd}
PID_FILE="$ROOT/logs/node-host.pid"
if [ ! -f "$PID_FILE" ]; then
echo "No pid file"
exit 0
fi
pid=$(cat "$PID_FILE" 2>/dev/null || true)
case "$pid" in
*[!0-9]*|"")
echo "Invalid pid file: $pid"
exit 1
;;
*)
kill "$pid" 2>/dev/null || true
rm -f "$PID_FILE"
echo "Stopped ClawdBox-CLI node host: pid $pid"
;;
esac