Skip to content
Open
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
73 changes: 73 additions & 0 deletions deployments/moltbot-cloud/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Clawdbot VM Image (Built from Source)
# Full Clawdbot gateway running in Hypercore microVMs
# Usage: hypercore spawn clawdbot/clawdbot-vm:latest

FROM node:22-bookworm AS builder

# Install pnpm
RUN npm install -g pnpm

# Copy source code
WORKDIR /build
COPY . .

# Install and build (including UI)
RUN pnpm install --frozen-lockfile
RUN pnpm build
RUN pnpm ui:build

# Create tarball for global install
RUN pnpm pack

# Runtime image - use node base for native module compatibility
FROM node:22-bookworm-slim

# Install base packages and build tools for native modules
RUN apt-get update && apt-get install -y \
curl \
git \
unzip \
ca-certificates \
sudo \
build-essential \
python3 \
&& rm -rf /var/lib/apt/lists/*

# Copy and install built clawdbot
COPY --from=builder /build/*.tgz /tmp/clawdbot.tgz
RUN npm install -g /tmp/clawdbot.tgz && rm /tmp/clawdbot.tgz

# Create clawdbot user
RUN useradd -m -s /bin/bash clawdbot && \
echo "clawdbot ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

# Switch to clawdbot user
USER clawdbot
WORKDIR /home/clawdbot
ENV HOME=/home/clawdbot

# Create config directory
RUN mkdir -p /home/clawdbot/.clawdbot

# Copy entrypoint script
# When building from clawdbot repo root with -f pointing to this Dockerfile,
# the entrypoint.sh should be copied to the repo root or specified path first.
# Example: cp hypercore/deployments/moltbot-cloud/entrypoint.sh ./entrypoint.sh
# docker build -f path/to/Dockerfile -t clawdbot-vm .
COPY --chown=clawdbot:clawdbot entrypoint.sh /opt/entrypoint.sh
RUN chmod +x /opt/entrypoint.sh

# Expose gateway port
EXPOSE 18789

# Health check - verify gateway is serving the Control UI
HEALTHCHECK --interval=10s --timeout=5s --start-period=30s --retries=3 \
CMD curl -sf http://localhost:18789/ | grep -q "clawdbot-app" || exit 1

# Default environment
ENV CLAWDBOT_PROVIDER=anthropic
ENV CLAWDBOT_MODEL=claude-sonnet-4-20250514
ENV CLAWDBOT_PORT=18789

# Start Clawdbot gateway via entrypoint
CMD ["/opt/entrypoint.sh"]
50 changes: 50 additions & 0 deletions deployments/moltbot-cloud/Dockerfile.npm
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Clawdbot VM Image (from npm)
# Uses published npm package instead of building from source
# Faster for testing/deployment

FROM node:22-bookworm-slim

# Install base packages, jq for JSON handling, and openssl for token generation
RUN apt-get update && apt-get install -y \
curl \
git \
unzip \
ca-certificates \
sudo \
jq \
openssl \
&& rm -rf /var/lib/apt/lists/*

# Install clawdbot from npm
RUN npm install -g clawdbot

# Create clawdbot user
RUN useradd -m -s /bin/bash clawdbot && \
echo "clawdbot ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

# Switch to clawdbot user
USER clawdbot
WORKDIR /home/clawdbot
ENV HOME=/home/clawdbot

# Create config directory
RUN mkdir -p /home/clawdbot/.clawdbot

# Copy entrypoint script
COPY --chown=clawdbot:clawdbot entrypoint.sh /opt/entrypoint.sh
RUN chmod +x /opt/entrypoint.sh

# Expose gateway port
EXPOSE 18789

# Health check - verify gateway is serving the Control UI
HEALTHCHECK --interval=10s --timeout=5s --start-period=30s --retries=3 \
CMD curl -sf http://localhost:18789/ | grep -q "clawdbot-app" || exit 1

# Default environment
ENV CLAWDBOT_PROVIDER=anthropic
ENV CLAWDBOT_MODEL=claude-sonnet-4-20250514
ENV CLAWDBOT_PORT=18789

# Start Clawdbot gateway via entrypoint
CMD ["/opt/entrypoint.sh"]
81 changes: 81 additions & 0 deletions deployments/moltbot-cloud/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Clawdbot Cloud Deployment for Hypercore

This directory contains Docker images and configuration for deploying Clawdbot on Hypercore microVMs.

## Images

### Dockerfile.npm (Recommended)
Builds from the published npm package. Fast and reliable.

```bash
cd deployments/moltbot-cloud
docker build -f Dockerfile.npm -t clawdbot-vm:npm .
```

### Dockerfile (From Source)
Builds from Clawdbot source code. Use this for custom builds.

**Build from Clawdbot source repo:**
```bash
# In the clawdbot source directory
cp /path/to/hypercore/deployments/moltbot-cloud/entrypoint.sh ./entrypoint.sh
docker build -f /path/to/hypercore/deployments/moltbot-cloud/Dockerfile -t clawdbot-vm:source .
```

## Running

### Docker (Standalone)
```bash
docker run -d \
--name clawdbot \
-p 18789:18789 \
-e ANTHROPIC_API_KEY="your-api-key" \
-e CLAWDBOT_GATEWAY_TOKEN="your-token" # optional, auto-generated if not set \
clawdbot-vm:npm
```

### Hypercore MicroVM
```bash
# Push to registry
docker tag clawdbot-vm:npm registry.your.domain/clawdbot:latest
docker push registry.your.domain/clawdbot:latest

# Deploy via Hypercore
hypercore cluster spawn \
--grpc-bind-addr "$NODE_IP:8000" \
--ports 443:18789 \
--image-ref registry.your.domain/clawdbot:latest
```

## Environment Variables

| Variable | Required | Description |
|----------|----------|-------------|
| `ANTHROPIC_API_KEY` | Yes | Your Anthropic API key |
| `CLAWDBOT_GATEWAY_TOKEN` | No | Gateway auth token (auto-generated if not set) |
| `CLAWDBOT_PORT` | No | Gateway port (default: 18789) |
| `CLAWDBOT_MODEL` | No | Default model (default: `anthropic/claude-sonnet-4-20250514`) |

## Health Check

The container includes a health check that verifies the gateway is serving the Control UI:
- Interval: 10s
- Start period: 30s
- Endpoint: `http://localhost:18789/`

## Connecting

Once running, connect to the gateway:
- **Control UI:** `http://your-host:18789/`
- **WebSocket:** `ws://your-host:18789/`

Pass the token (shown in container logs) via `connect.params.auth.token`.

## Hypercore Requirements

Full Hypercore deployment requires:
- **KVM support** (`/dev/kvm` available)
- **dmsetup** for containerd snapshotter
- Static public IP with ports exposed

Without KVM (e.g., on a VPS that's already a VM), use Docker standalone mode.
119 changes: 119 additions & 0 deletions deployments/moltbot-cloud/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#!/bin/bash
# Clawdbot Cloud Entrypoint
# Fast startup by writing config directly instead of running multiple commands

set -e

echo "Clawdbot Cloud: Starting..."

# Generate a token if not provided (hex only for JSON safety)
GATEWAY_TOKEN="${CLAWDBOT_GATEWAY_TOKEN:-$(openssl rand -hex 16)}"
# Validate token is hex-safe (alphanumeric only, no special chars)
if [[ ! "$GATEWAY_TOKEN" =~ ^[a-zA-Z0-9_-]+$ ]]; then
echo "WARNING: Token contains special characters, generating safe token"
GATEWAY_TOKEN=$(openssl rand -hex 16)
fi

# Validate port is numeric
GATEWAY_PORT="${CLAWDBOT_PORT:-18789}"
if [[ ! "$GATEWAY_PORT" =~ ^[0-9]+$ ]]; then
echo "WARNING: Invalid port '$GATEWAY_PORT', using default 18789"
GATEWAY_PORT=18789
fi

# Default model (can be overridden via env)
CLAWDBOT_MODEL="${CLAWDBOT_MODEL:-anthropic/claude-sonnet-4-20250514}"

# Create config directory
mkdir -p ~/.clawdbot

# Write config to clawdbot.json (the config file the gateway reads)
cat > ~/.clawdbot/clawdbot.json << EOF
{
"gateway": {
"port": ${GATEWAY_PORT},
"mode": "local",
"bind": "lan",
"auth": {
"mode": "token",
"token": "${GATEWAY_TOKEN}"
}
},
"agents": {
"defaults": {
"model": {
"primary": "${CLAWDBOT_MODEL}"
},
"workspace": "/home/clawdbot/workspace"
}
}
}
EOF

# Create workspace directory
mkdir -p ~/workspace

echo ""
echo "╔════════════════════════════════════════════════════════════════╗"
echo "║ CLAWDBOT CLOUD READY ║"
echo "╚════════════════════════════════════════════════════════════════╝"
echo ""
echo "📋 Token: $GATEWAY_TOKEN"
echo ""

# Check API keys and create auth-profiles.json
if [ -n "$ANTHROPIC_API_KEY" ]; then
echo "ANTHROPIC_API_KEY is set (${#ANTHROPIC_API_KEY} chars)"

# Create auth-profiles directory
mkdir -p ~/.clawdbot/agents/main/agent

# Write auth-profiles.json with proper JSON escaping
if command -v jq &> /dev/null; then
# Use jq for safe JSON generation
jq -n \
--arg key "$ANTHROPIC_API_KEY" \
'{
version: 1,
profiles: {
"anthropic:default": {
type: "api_key",
provider: "anthropic",
key: $key
}
},
lastGood: {
anthropic: "anthropic:default"
}
}' > ~/.clawdbot/agents/main/agent/auth-profiles.json
else
# Fallback: validate key has no dangerous chars (Anthropic keys are base64-safe)
if [[ "$ANTHROPIC_API_KEY" =~ ^[a-zA-Z0-9_-]+$ ]]; then
cat > ~/.clawdbot/agents/main/agent/auth-profiles.json << AUTHEOF
{
"version": 1,
"profiles": {
"anthropic:default": {
"type": "api_key",
"provider": "anthropic",
"key": "${ANTHROPIC_API_KEY}"
}
},
"lastGood": {
"anthropic": "anthropic:default"
}
}
AUTHEOF
else
echo "ERROR: API key contains invalid characters"
exit 1
fi
fi
echo "Created auth-profiles.json"
else
echo "WARNING: ANTHROPIC_API_KEY is NOT set"
fi

# Start the gateway (clawdbot is the actual binary name)
# --allow-unconfigured is needed since we're writing config directly instead of using `clawdbot setup`
exec clawdbot gateway run --bind lan --port "${GATEWAY_PORT}" --allow-unconfigured --token "${GATEWAY_TOKEN}"
12 changes: 12 additions & 0 deletions pkg/cluster/serf.go
Original file line number Diff line number Diff line change
Expand Up @@ -811,9 +811,21 @@ func (a *Agent) LogsRequest(id string) (*pb.VmLogsResponse, error) {
//nolint:gocognit
func (a *Agent) monitorWorkloads() {
ticker := time.NewTicker(WorkloadBroadcastPeriod)
gcCounter := 0
for range ticker.C {
ctx := a.ctrRepo.GetContext(context.Background())

// Run CNI garbage collection every 10 iterations (~5 minutes with 30s period)
gcCounter++
if gcCounter >= 10 {
gcCounter = 0
if cleaned, err := a.ctrRepo.GarbageCollectCNI(ctx); err != nil {
a.logger.WithError(err).Warn("CNI garbage collection failed")
} else if cleaned > 0 {
a.logger.Infof("CNI garbage collection cleaned %d orphaned IP allocations", cleaned)
}
}

tasks, err := a.ctrRepo.GetTasks(ctx)
if err != nil {
a.logger.WithError(err).Error("failed to get tasks")
Expand Down
Loading