Compare commits
14 Commits
f9d44c49d7
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 88d4505d2f | |||
| 52d2555cca | |||
| dd6b3aa63d | |||
| 7dec80e217 | |||
| 0d897e7024 | |||
| 81ca4aef19 | |||
| 302f4da450 | |||
| 58a60f843f | |||
| 32e122efb3 | |||
| f9051b390f | |||
| 4f57490faa | |||
| 8913225b5c | |||
| c01ace8ec7 | |||
| 1edd531cd7 |
62
AGENTS.md
62
AGENTS.md
@@ -376,9 +376,65 @@ docker build -t registry.lan/openclaw:latest .
|
|||||||
**Problem**: Web UI shows "disconnected (1008): pairing required"
|
**Problem**: Web UI shows "disconnected (1008): pairing required"
|
||||||
|
|
||||||
**Solution**:
|
**Solution**:
|
||||||
1. Check logs for device IDs: `docker logs CONTAINER | grep pairing`
|
1. Use the built-in CLI approver (recommended):
|
||||||
2. Manually approve devices in paired.json
|
|
||||||
3. Restart container
|
```bash
|
||||||
|
# approve newest pending request
|
||||||
|
docker exec CONTAINER_NAME node dist/index.js devices approve --latest --token "$OPENCLAW_GATEWAY_TOKEN"
|
||||||
|
|
||||||
|
# inspect current state
|
||||||
|
docker exec CONTAINER_NAME node dist/index.js devices list --token "$OPENCLAW_GATEWAY_TOKEN"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. If using Tailscale Serve, ensure Gateway auth/proxy settings are correct:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"gateway": {
|
||||||
|
"bind": "loopback",
|
||||||
|
"tailscale": { "mode": "serve" },
|
||||||
|
"auth": { "allowTailscale": true },
|
||||||
|
"trustedProxies": ["127.0.0.1", "::1"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. If error changes to `device token mismatch`, the browser usually has stale local state.
|
||||||
|
- Open the Control UI in an Incognito/Private window.
|
||||||
|
- Re-paste gateway token in settings, or open a tokenized URL from:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec CONTAINER_NAME node dist/index.js dashboard --no-open
|
||||||
|
```
|
||||||
|
|
||||||
|
4. If needed, only clear pending requests (not full config):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec CONTAINER_NAME sh -c 'echo {} > /home/node/.openclaw/devices/pending.json'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Notes**:
|
||||||
|
- Remote browsers (LAN/Tailscale) still require one-time device pairing.
|
||||||
|
- Localhost (`127.0.0.1`) auto-approves.
|
||||||
|
- Config edits trigger Gateway reload/restart automatically; container restart is usually unnecessary.
|
||||||
|
|
||||||
|
### Keep Web UI Working Across Rebuilds
|
||||||
|
|
||||||
|
To avoid repeated reconnect/pairing friction after redeploys:
|
||||||
|
|
||||||
|
1. Keep a stable Tailscale hostname so the browser origin does not change:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
TAILSCALE_HOSTNAME=openclaw-gateway
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Keep the same `OPENCLAW_GATEWAY_TOKEN` between deployments.
|
||||||
|
3. Persist and reuse the same `openclaw-config` volume (contains `devices/paired.json`).
|
||||||
|
4. If UI shows `token_missing`, open a tokenized URL and re-save settings:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec CONTAINER_NAME node dist/index.js dashboard --no-open
|
||||||
|
```
|
||||||
|
|
||||||
### Permission Denied Errors
|
### Permission Denied Errors
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,20 @@
|
|||||||
version: "3.8"
|
version: "3.8"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
openclaw-init-perms:
|
||||||
|
image: alpine:3.20
|
||||||
|
container_name: openclaw-init-perms
|
||||||
|
restart: "no"
|
||||||
|
command:
|
||||||
|
[
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
"mkdir -p /config /workspace && chown -R 1000:1000 /config /workspace && chmod 755 /config /workspace",
|
||||||
|
]
|
||||||
|
volumes:
|
||||||
|
- openclaw-config:/config
|
||||||
|
- openclaw-workspace:/workspace
|
||||||
|
|
||||||
openclaw-gateway:
|
openclaw-gateway:
|
||||||
build:
|
build:
|
||||||
context: ./docker
|
context: ./docker
|
||||||
@@ -10,43 +24,42 @@ services:
|
|||||||
image: openclaw:custom-arm64
|
image: openclaw:custom-arm64
|
||||||
container_name: openclaw-gateway
|
container_name: openclaw-gateway
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
openclaw-init-perms:
|
||||||
|
condition: service_completed_successfully
|
||||||
environment:
|
environment:
|
||||||
HOME: /home/node
|
HOME: /home/node
|
||||||
TERM: xterm-256color
|
TERM: xterm-256color
|
||||||
OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN}
|
OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN}
|
||||||
MOONSHOT_API_KEY: ${MOONSHOT_API_KEY}
|
MOONSHOT_API_KEY: ${MOONSHOT_API_KEY}
|
||||||
|
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||||
|
OPENCLAW_GATEWAY_BIND: ${OPENCLAW_GATEWAY_BIND:-lan}
|
||||||
|
OPENCLAW_TAILSCALE_MODE: ${OPENCLAW_TAILSCALE_MODE:-off}
|
||||||
|
OPENCLAW_ENABLE_TAILSCALE: ${OPENCLAW_ENABLE_TAILSCALE:-0}
|
||||||
GOG_ACCOUNT: ${GOG_ACCOUNT:-}
|
GOG_ACCOUNT: ${GOG_ACCOUNT:-}
|
||||||
|
TAILSCALE_AUTH_KEY: ${TAILSCALE_AUTH_KEY:-}
|
||||||
|
TAILSCALE_HOSTNAME: ${TAILSCALE_HOSTNAME:-openclaw-gateway}
|
||||||
volumes:
|
volumes:
|
||||||
- openclaw-config:/home/node/.openclaw
|
- openclaw-config:/home/node/.openclaw
|
||||||
- openclaw-workspace:/home/node/.openclaw/workspace
|
- openclaw-workspace:/home/node/.openclaw/workspace
|
||||||
- gog-data:/home/node/.openclaw/gog
|
|
||||||
ports:
|
ports:
|
||||||
- "${OPENCLAW_GATEWAY_PORT:-18789}:18789"
|
- "${OPENCLAW_GATEWAY_PORT:-18789}:18789"
|
||||||
- "${OPENCLAW_BRIDGE_PORT:-18790}:18790"
|
- "${OPENCLAW_BRIDGE_PORT:-18790}:18790"
|
||||||
|
privileged: true
|
||||||
init: true
|
init: true
|
||||||
networks:
|
networks:
|
||||||
- dokploy-network
|
- dokploy-network
|
||||||
command:
|
command: ["/usr/local/bin/start-gateway.sh"]
|
||||||
[
|
# healthcheck:
|
||||||
"node",
|
# test: ["CMD", "healthcheck.sh"]
|
||||||
"dist/index.js",
|
# interval: 30s
|
||||||
"gateway",
|
# timeout: 10s
|
||||||
"--bind",
|
# retries: 3
|
||||||
"${OPENCLAW_GATEWAY_BIND:-lan}",
|
# start_period: 30s
|
||||||
"--port",
|
|
||||||
"18789",
|
|
||||||
]
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "healthcheck.sh"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
start_period: 30s
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
openclaw-config:
|
openclaw-config:
|
||||||
openclaw-workspace:
|
openclaw-workspace:
|
||||||
gog-data:
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
dokploy-network:
|
dokploy-network:
|
||||||
|
|||||||
@@ -24,24 +24,47 @@ RUN echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.
|
|||||||
&& apt-get update && apt-get install -y google-cloud-cli \
|
&& apt-get update && apt-get install -y google-cloud-cli \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Install gog (Google Workspace CLI) for ARM64
|
# Install gogcli (Google Workspace CLI) for ARM64
|
||||||
RUN GOG_VERSION=$(curl -s https://api.github.com/repos/steipete/gog/releases/latest | jq -r .tag_name) \
|
RUN GOG_VERSION=$(curl -s https://api.github.com/repos/steipete/gogcli/releases/latest | jq -r .tag_name) \
|
||||||
&& curl -L "https://github.com/steipete/gog/releases/download/${GOG_VERSION}/gog_Linux_arm64.tar.gz" \
|
&& curl -L "https://github.com/steipete/gogcli/releases/download/${GOG_VERSION}/gogcli_${GOG_VERSION#v}_linux_arm64.tar.gz" \
|
||||||
| tar -xz -C /usr/local/bin/ \
|
| tar -xz -C /usr/local/bin/ \
|
||||||
&& chmod +x /usr/local/bin/gog
|
&& chmod +x /usr/local/bin/gog
|
||||||
|
|
||||||
|
# Install Tailscale
|
||||||
|
RUN curl -fsSL https://tailscale.com/install.sh | HEADLESS=true sh
|
||||||
|
|
||||||
|
# Create tailscale directories
|
||||||
|
RUN mkdir -p /var/run/tailscale /home/node/.local/share/tailscale /home/node/.local/share/tailscale/files /home/node/.local/bin && \
|
||||||
|
chmod 777 /var/run/tailscale && \
|
||||||
|
chown -R node:node /home/node/.local
|
||||||
|
|
||||||
|
# Create tailscale startup script (runs as node user)
|
||||||
|
RUN echo '#!/bin/sh' > /home/node/.local/bin/tailscale-start.sh && \
|
||||||
|
echo 'mkdir -p /var/run/tailscale /home/node/.local/share/tailscale /home/node/.local/share/tailscale/files' >> /home/node/.local/bin/tailscale-start.sh && \
|
||||||
|
echo 'tailscaled --socket=/tmp/tailscale.sock --tun=userspace-networking &' >> /home/node/.local/bin/tailscale-start.sh && \
|
||||||
|
echo 'sleep 3' >> /home/node/.local/bin/tailscale-start.sh && \
|
||||||
|
echo 'if [ -n "$TAILSCALE_AUTH_KEY" ]; then tailscale --socket=/tmp/tailscale.sock up --authkey="$TAILSCALE_AUTH_KEY" --hostname="${TAILSCALE_HOSTNAME:-openclaw-gateway}" || true; fi' >> /home/node/.local/bin/tailscale-start.sh && \
|
||||||
|
echo 'sleep 2' >> /home/node/.local/bin/tailscale-start.sh && \
|
||||||
|
echo 'tailscale --socket=/tmp/tailscale.sock serve --bg 18789 || true' >> /home/node/.local/bin/tailscale-start.sh && \
|
||||||
|
chmod +x /home/node/.local/bin/tailscale-start.sh
|
||||||
|
|
||||||
# Copy custom tools into the image
|
# Copy custom tools into the image
|
||||||
COPY docker/tools/* /usr/local/bin/
|
COPY tools/* /usr/local/bin/
|
||||||
COPY docker/bin/* /usr/local/bin/
|
COPY bin/* /usr/local/bin/
|
||||||
RUN chmod +x /usr/local/bin/*
|
RUN chmod +x /usr/local/bin/*
|
||||||
|
|
||||||
# Create directories in the persistent volume location
|
# Create directories in the persistent volume location
|
||||||
RUN mkdir -p /home/node/.openclaw/ssh /home/node/.openclaw/gog \
|
RUN mkdir -p /home/node/.openclaw/ssh /home/node/.openclaw/gog /opt/openclaw/defaults \
|
||||||
&& chown -R node:node /home/node/.openclaw
|
&& chown -R node:node /home/node/.openclaw
|
||||||
|
|
||||||
# Link gog config and ssh to standard locations
|
# Link gog config and ssh to standard locations
|
||||||
RUN ln -sf /home/node/.openclaw/gog /home/node/.config/gog \
|
RUN mkdir -p /home/node/.config /home/node/.ssh \
|
||||||
|
&& ln -sf /home/node/.openclaw/gog /home/node/.config/gog \
|
||||||
&& ln -sf /home/node/.openclaw/ssh /home/node/.ssh
|
&& ln -sf /home/node/.openclaw/ssh /home/node/.ssh
|
||||||
|
|
||||||
|
# Copy default config into the image
|
||||||
|
COPY config/openclaw.json /opt/openclaw/defaults/openclaw.json
|
||||||
|
RUN chown -R node:node /opt/openclaw/defaults
|
||||||
|
|
||||||
# Switch back to node user
|
# Switch back to node user
|
||||||
USER node
|
USER node
|
||||||
|
|||||||
69
docker/bin/start-gateway.sh
Normal file
69
docker/bin/start-gateway.sh
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
CONFIG_DIR="${HOME:-/home/node}/.openclaw"
|
||||||
|
CONFIG_FILE="${CONFIG_DIR}/openclaw.json"
|
||||||
|
DEFAULT_CONFIG="/opt/openclaw/defaults/openclaw.json"
|
||||||
|
BIND="${OPENCLAW_GATEWAY_BIND:-lan}"
|
||||||
|
TAILSCALE_MODE="${OPENCLAW_TAILSCALE_MODE:-off}"
|
||||||
|
PORT="${OPENCLAW_GATEWAY_PORT:-18789}"
|
||||||
|
|
||||||
|
mkdir -p "${CONFIG_DIR}"
|
||||||
|
|
||||||
|
if [ ! -f "${CONFIG_FILE}" ] && [ -f "${DEFAULT_CONFIG}" ]; then
|
||||||
|
cp "${DEFAULT_CONFIG}" "${CONFIG_FILE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${CONFIG_FILE}" ]; then
|
||||||
|
printf '{}\n' > "${CONFIG_FILE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
tmp_file="$(mktemp)"
|
||||||
|
jq \
|
||||||
|
--arg bind "${BIND}" \
|
||||||
|
--arg tailscale_mode "${TAILSCALE_MODE}" \
|
||||||
|
--arg token "${OPENCLAW_GATEWAY_TOKEN:-}" \
|
||||||
|
'
|
||||||
|
.env.OPENCLAW_GATEWAY_TOKEN = "${OPENCLAW_GATEWAY_TOKEN}" |
|
||||||
|
.env.OPENAI_API_KEY = "${OPENAI_API_KEY}" |
|
||||||
|
.gateway.bind = $bind |
|
||||||
|
.gateway.tailscale.mode = $tailscale_mode |
|
||||||
|
.gateway.auth.mode = "token" |
|
||||||
|
.gateway.auth.token = (if $token == "" then (.gateway.auth.token // "${OPENCLAW_GATEWAY_TOKEN}") else $token end) |
|
||||||
|
.gateway.controlUi.allowInsecureAuth = true |
|
||||||
|
.models.providers.openai = {
|
||||||
|
baseUrl: "https://api.openai.com/v1",
|
||||||
|
apiKey: "${OPENAI_API_KEY}",
|
||||||
|
auth: "api-key",
|
||||||
|
api: "openai-completions",
|
||||||
|
models: [
|
||||||
|
{
|
||||||
|
id: "gpt-4.1-mini",
|
||||||
|
name: "GPT-4.1 mini",
|
||||||
|
reasoning: false,
|
||||||
|
input: ["text"],
|
||||||
|
cost: {input: 0, output: 0, cacheRead: 0, cacheWrite: 0},
|
||||||
|
contextWindow: 1047576,
|
||||||
|
maxTokens: 32768
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "gpt-image-1",
|
||||||
|
name: "GPT Image 1",
|
||||||
|
reasoning: false,
|
||||||
|
input: ["text", "image"],
|
||||||
|
cost: {input: 0, output: 0, cacheRead: 0, cacheWrite: 0},
|
||||||
|
contextWindow: 128000,
|
||||||
|
maxTokens: 4096
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} |
|
||||||
|
.agents.defaults.model.fallbacks = ((.agents.defaults.model.fallbacks // []) + ["openai/gpt-4.1-mini"] | unique) |
|
||||||
|
.agents.defaults.models["openai/gpt-4.1-mini"] = {alias: "OpenAI GPT-4.1 mini"}
|
||||||
|
' "${CONFIG_FILE}" > "${tmp_file}"
|
||||||
|
mv "${tmp_file}" "${CONFIG_FILE}"
|
||||||
|
|
||||||
|
if [ "${OPENCLAW_ENABLE_TAILSCALE:-0}" = "1" ] && [ -x /home/node/.local/bin/tailscale-start.sh ]; then
|
||||||
|
/home/node/.local/bin/tailscale-start.sh || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec node dist/index.js gateway --bind "${BIND}" --port "${PORT}"
|
||||||
163
docker/config/openclaw.json
Normal file
163
docker/config/openclaw.json
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"lastTouchedVersion": "2026.2.17",
|
||||||
|
"lastTouchedAt": "2026-02-19T04:49:36.730Z"
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"MOONSHOT_API_KEY": "${MOONSHOT_API_KEY}",
|
||||||
|
"OPENCLAW_GATEWAY_TOKEN": "${OPENCLAW_GATEWAY_TOKEN}",
|
||||||
|
"OPENAI_API_KEY": "${OPENAI_API_KEY}"
|
||||||
|
},
|
||||||
|
"wizard": {
|
||||||
|
"lastRunAt": "2026-02-19T04:49:36.701Z",
|
||||||
|
"lastRunVersion": "2026.2.18",
|
||||||
|
"lastRunCommand": "configure",
|
||||||
|
"lastRunMode": "local"
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"profiles": {
|
||||||
|
"kimi-coding:default": {
|
||||||
|
"provider": "kimi-coding",
|
||||||
|
"mode": "api_key"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models": {
|
||||||
|
"providers": {
|
||||||
|
"openai": {
|
||||||
|
"baseUrl": "https://api.openai.com/v1",
|
||||||
|
"apiKey": "${OPENAI_API_KEY}",
|
||||||
|
"auth": "api-key",
|
||||||
|
"api": "openai-completions",
|
||||||
|
"models": [
|
||||||
|
{
|
||||||
|
"id": "gpt-4.1-mini",
|
||||||
|
"name": "GPT-4.1 mini",
|
||||||
|
"reasoning": false,
|
||||||
|
"input": ["text"],
|
||||||
|
"cost": {
|
||||||
|
"input": 0,
|
||||||
|
"output": 0,
|
||||||
|
"cacheRead": 0,
|
||||||
|
"cacheWrite": 0
|
||||||
|
},
|
||||||
|
"contextWindow": 1047576,
|
||||||
|
"maxTokens": 32768
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "gpt-image-1",
|
||||||
|
"name": "GPT Image 1",
|
||||||
|
"reasoning": false,
|
||||||
|
"input": ["text", "image"],
|
||||||
|
"cost": {
|
||||||
|
"input": 0,
|
||||||
|
"output": 0,
|
||||||
|
"cacheRead": 0,
|
||||||
|
"cacheWrite": 0
|
||||||
|
},
|
||||||
|
"contextWindow": 128000,
|
||||||
|
"maxTokens": 4096
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"moonshot": {
|
||||||
|
"baseUrl": "https://api.moonshot.ai/v1",
|
||||||
|
"apiKey": "${MOONSHOT_API_KEY}",
|
||||||
|
"auth": "api-key",
|
||||||
|
"api": "openai-completions",
|
||||||
|
"models": [
|
||||||
|
{
|
||||||
|
"id": "kimi-k2.5",
|
||||||
|
"name": "Kimi K2.5",
|
||||||
|
"reasoning": false,
|
||||||
|
"input": ["text"],
|
||||||
|
"cost": {
|
||||||
|
"input": 0,
|
||||||
|
"output": 0,
|
||||||
|
"cacheRead": 0,
|
||||||
|
"cacheWrite": 0
|
||||||
|
},
|
||||||
|
"contextWindow": 256000,
|
||||||
|
"maxTokens": 8192
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"agents": {
|
||||||
|
"defaults": {
|
||||||
|
"model": {
|
||||||
|
"primary": "kimi-coding/k2p5",
|
||||||
|
"fallbacks": ["moonshot/kimi-k2.5", "openai/gpt-4.1-mini"]
|
||||||
|
},
|
||||||
|
"models": {
|
||||||
|
"kimi-coding/k2p5": {
|
||||||
|
"alias": "Kimi K2.5"
|
||||||
|
},
|
||||||
|
"openai/gpt-4.1-mini": {
|
||||||
|
"alias": "OpenAI GPT-4.1 mini"
|
||||||
|
},
|
||||||
|
"moonshot/kimi-k2.5": {
|
||||||
|
"alias": "Kimi K2.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"workspace": "/home/node/.openclaw/workspace"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tools": {
|
||||||
|
"web": {
|
||||||
|
"search": {
|
||||||
|
"enabled": true,
|
||||||
|
"apiKey": "BSADSjIqy0kUD4b2emM12eSkPKxOeNz"
|
||||||
|
},
|
||||||
|
"fetch": {
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"commands": {
|
||||||
|
"native": "auto",
|
||||||
|
"nativeSkills": "auto"
|
||||||
|
},
|
||||||
|
"channels": {
|
||||||
|
"whatsapp": {
|
||||||
|
"dmPolicy": "allowlist",
|
||||||
|
"selfChatMode": true,
|
||||||
|
"allowFrom": ["+17193319238"],
|
||||||
|
"groupPolicy": "allowlist",
|
||||||
|
"debounceMs": 0,
|
||||||
|
"mediaMaxMb": 50
|
||||||
|
},
|
||||||
|
"telegram": {
|
||||||
|
"enabled": true,
|
||||||
|
"dmPolicy": "pairing",
|
||||||
|
"botToken": "8574031211:AAGr40J8J9l5ovKK79actnlS3FceBJq7znk",
|
||||||
|
"groupPolicy": "allowlist",
|
||||||
|
"streamMode": "partial"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gateway": {
|
||||||
|
"bind": "lan",
|
||||||
|
"auth": {
|
||||||
|
"mode": "token",
|
||||||
|
"token": "${OPENCLAW_GATEWAY_TOKEN}"
|
||||||
|
},
|
||||||
|
"controlUi": {
|
||||||
|
"allowInsecureAuth": true
|
||||||
|
},
|
||||||
|
"tailscale": {
|
||||||
|
"mode": "off",
|
||||||
|
"resetOnExit": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugins": {
|
||||||
|
"entries": {
|
||||||
|
"whatsapp": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"telegram": {
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
version: "3.8"
|
|
||||||
|
|
||||||
services:
|
|
||||||
openclaw:
|
|
||||||
build:
|
|
||||||
context: ..
|
|
||||||
dockerfile: docker/Dockerfile
|
|
||||||
platforms:
|
|
||||||
- linux/arm64
|
|
||||||
image: openclaw:custom-arm64
|
|
||||||
container_name: openclaw-gateway
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
# OpenClaw ports
|
|
||||||
ports:
|
|
||||||
- "8080:8080"
|
|
||||||
|
|
||||||
# Persistent volumes for configs and secrets
|
|
||||||
volumes:
|
|
||||||
# Your workspace (for memory, agents, etc)
|
|
||||||
- ./workspace:/home/node/.openclaw/workspace
|
|
||||||
|
|
||||||
# Configs mounted from host (not in image)
|
|
||||||
- ./config:/data/config:ro
|
|
||||||
|
|
||||||
# Secrets mounted from host (read-only, not in image)
|
|
||||||
- ./secrets:/data/secrets:ro
|
|
||||||
|
|
||||||
# gog OAuth tokens and config (persistent)
|
|
||||||
- gog-data:/data/gog
|
|
||||||
|
|
||||||
# OpenClaw runtime data
|
|
||||||
- openclaw-data:/home/node/.openclaw
|
|
||||||
|
|
||||||
environment:
|
|
||||||
- OPENCLAW_CONFIG_DIR=/data/config
|
|
||||||
- GOG_CONFIG_DIR=/data/gog
|
|
||||||
- GOOGLE_APPLICATION_CREDENTIALS=/data/secrets/google-credentials.json
|
|
||||||
- GOG_ACCOUNT=${GOG_ACCOUNT:-}
|
|
||||||
|
|
||||||
# Health check using our custom tool
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "healthcheck.sh"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
start_period: 30s
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
gog-data:
|
|
||||||
openclaw-data:
|
|
||||||
88
kids-instance/README.md
Normal file
88
kids-instance/README.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# OpenClaw Kids Instance
|
||||||
|
|
||||||
|
A locked-down, sandboxed OpenClaw instance for your child.
|
||||||
|
|
||||||
|
## 🔒 Safety Features
|
||||||
|
|
||||||
|
- **Sandboxed tools** — all execution happens in Docker containers
|
||||||
|
- **No file writes** — read-only access to workspace
|
||||||
|
- **No shell access** — can't run commands on the host
|
||||||
|
- **No browser control** — can't drive a browser
|
||||||
|
- **No config changes** — can't modify OpenClaw settings
|
||||||
|
- **DM pairing required** — you approve all contacts
|
||||||
|
- **Group mention-only** — won't respond unless @mentioned in groups
|
||||||
|
|
||||||
|
## 🚀 Setup
|
||||||
|
|
||||||
|
1. **Change the auth token** in `config/openclaw-kids.json`:
|
||||||
|
```json
|
||||||
|
"token": "your-long-random-string-here"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Start the instance**:
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.kids.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Connect via Control UI**:
|
||||||
|
- Open http://localhost:18790 in your browser
|
||||||
|
- Use the token from step 1 to pair
|
||||||
|
|
||||||
|
4. **Set up messaging** (optional):
|
||||||
|
- WhatsApp: Scan QR code in Control UI
|
||||||
|
- Telegram: Create bot via @BotFather, add token to config
|
||||||
|
|
||||||
|
5. **Customize**:
|
||||||
|
- Edit `workspace-kids/IDENTITY.md` — let your son name the bot!
|
||||||
|
- Edit `workspace-kids/SOUL.md` — adjust personality as needed
|
||||||
|
|
||||||
|
## 🛡️ What's Allowed
|
||||||
|
|
||||||
|
- ✅ Chatting and asking questions
|
||||||
|
- ✅ Reading files in the workspace
|
||||||
|
- ✅ Web search (with safe content filters)
|
||||||
|
- ✅ Using messaging with approved contacts
|
||||||
|
|
||||||
|
## 🚫 What's Blocked
|
||||||
|
|
||||||
|
- ❌ Running shell commands
|
||||||
|
- ❌ Writing files
|
||||||
|
- ❌ Browsing the web interactively
|
||||||
|
- ❌ Creating scheduled tasks
|
||||||
|
- ❌ Accessing your main OpenClaw instance
|
||||||
|
- ❌ Modifying configuration
|
||||||
|
|
||||||
|
## 📁 Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
kids-instance/
|
||||||
|
├── docker-compose.kids.yml # Docker setup
|
||||||
|
├── config/
|
||||||
|
│ └── openclaw-kids.json # Main config
|
||||||
|
├── workspace-kids/ # Agent workspace
|
||||||
|
│ ├── SOUL.md # Personality
|
||||||
|
│ ├── IDENTITY.md # Name/emoji
|
||||||
|
│ └── ... # Other files
|
||||||
|
└── state/ # Runtime state (created on first run)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔄 Updating
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.kids.yml pull
|
||||||
|
docker-compose -f docker-compose.kids.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📝 Notes
|
||||||
|
|
||||||
|
- Runs on port 18790 (different from default 18789)
|
||||||
|
- Uses separate Docker network `openclaw-kids`
|
||||||
|
- Completely isolated from your main OpenClaw instance
|
||||||
|
- All activity is logged in `state/` for review
|
||||||
|
|
||||||
|
## 🎨 Customization Ideas
|
||||||
|
|
||||||
|
- Let your son design the bot's personality in SOUL.md
|
||||||
|
- Add fun facts or daily challenges in HEARTBEAT.md
|
||||||
|
- Create a todo.md for homework or chores
|
||||||
|
- Set up a separate WhatsApp number just for the bot
|
||||||
98
kids-instance/config/openclaw-kids.json
Normal file
98
kids-instance/config/openclaw-kids.json
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.openclaw.ai/schemas/openclaw-config.json",
|
||||||
|
|
||||||
|
"gateway": {
|
||||||
|
"mode": "local",
|
||||||
|
"bind": "loopback",
|
||||||
|
"port": 18789,
|
||||||
|
"auth": {
|
||||||
|
"mode": "token",
|
||||||
|
"token": "CHANGE-THIS-TO-A-LONG-RANDOM-STRING"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"session": {
|
||||||
|
"dmScope": "per-channel-peer"
|
||||||
|
},
|
||||||
|
|
||||||
|
"agents": {
|
||||||
|
"defaults": {
|
||||||
|
"sandbox": {
|
||||||
|
"mode": "all",
|
||||||
|
"scope": "agent",
|
||||||
|
"workspaceAccess": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"id": "kids-agent",
|
||||||
|
"name": "Kids Assistant",
|
||||||
|
"workspace": "/workspace-kids",
|
||||||
|
"model": "kimi-coding/k2p5",
|
||||||
|
"thinking": "low",
|
||||||
|
"systemPrompt": "You are a friendly, patient AI assistant for a child. Be encouraging, educational, and safe. Never help with anything dangerous or inappropriate. If asked about harmful topics, gently redirect. Keep answers age-appropriate and positive.",
|
||||||
|
"tools": {
|
||||||
|
"allow": [
|
||||||
|
"read",
|
||||||
|
"web_search",
|
||||||
|
"memory_search",
|
||||||
|
"memory_get",
|
||||||
|
"sessions_list",
|
||||||
|
"sessions_history",
|
||||||
|
"session_status"
|
||||||
|
],
|
||||||
|
"deny": [
|
||||||
|
"write",
|
||||||
|
"edit",
|
||||||
|
"apply_patch",
|
||||||
|
"exec",
|
||||||
|
"process",
|
||||||
|
"browser",
|
||||||
|
"canvas",
|
||||||
|
"nodes",
|
||||||
|
"cron",
|
||||||
|
"gateway",
|
||||||
|
"sessions_spawn",
|
||||||
|
"sessions_send",
|
||||||
|
"subagents",
|
||||||
|
"agents_list",
|
||||||
|
"image",
|
||||||
|
"web_fetch",
|
||||||
|
"tts"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"tools": {
|
||||||
|
"profile": "messaging",
|
||||||
|
"fs": {
|
||||||
|
"workspaceOnly": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"channels": {
|
||||||
|
"whatsapp": {
|
||||||
|
"dmPolicy": "pairing",
|
||||||
|
"groups": {
|
||||||
|
"*": {
|
||||||
|
"requireMention": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"telegram": {
|
||||||
|
"dmPolicy": "pairing",
|
||||||
|
"groups": {
|
||||||
|
"*": {
|
||||||
|
"requireMention": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"logging": {
|
||||||
|
"level": "info",
|
||||||
|
"redactSensitive": "tools"
|
||||||
|
}
|
||||||
|
}
|
||||||
34
kids-instance/docker-compose.kids.yml
Normal file
34
kids-instance/docker-compose.kids.yml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# OpenClaw Kids Instance - Locked Down & Safe
|
||||||
|
# Run with: docker-compose -f docker-compose.kids.yml up -d
|
||||||
|
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
openclaw-kids:
|
||||||
|
image: ghcr.io/openclaw/openclaw:latest
|
||||||
|
container_name: openclaw-kids
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "18790:18789" # Different port from main instance
|
||||||
|
environment:
|
||||||
|
- OPENCLAW_CONFIG=/config/openclaw-kids.json
|
||||||
|
- OPENCLAW_STATE_DIR=/state
|
||||||
|
volumes:
|
||||||
|
- ./config:/config:ro
|
||||||
|
- ./state:/state
|
||||||
|
- ./workspace-kids:/workspace-kids
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro # For sandboxing
|
||||||
|
networks:
|
||||||
|
- openclaw-kids
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
|
cap_add:
|
||||||
|
- CHOWN
|
||||||
|
- SETGID
|
||||||
|
- SETUID
|
||||||
|
|
||||||
|
networks:
|
||||||
|
openclaw-kids:
|
||||||
|
driver: bridge
|
||||||
10
kids-instance/workspace-kids/IDENTITY.md
Normal file
10
kids-instance/workspace-kids/IDENTITY.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# IDENTITY.md - Who Am I?
|
||||||
|
|
||||||
|
- **Name:** (To be decided by your son!)
|
||||||
|
- **Creature:** Friendly AI assistant / digital buddy
|
||||||
|
- **Vibe:** Patient, curious, encouraging, fun
|
||||||
|
- **Emoji:** 🤖 (or your son can pick one!)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_This file is yours to evolve. As you learn who you are, update it._
|
||||||
29
kids-instance/workspace-kids/SOUL.md
Normal file
29
kids-instance/workspace-kids/SOUL.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# SOUL.md - Kids Agent
|
||||||
|
|
||||||
|
## Core Truths
|
||||||
|
|
||||||
|
**Be patient and encouraging.** Kids ask lots of questions. Some seem simple to you but are new to them. Never make them feel dumb for asking.
|
||||||
|
|
||||||
|
**Be genuinely helpful.** If you don't know something, say so. Don't make things up.
|
||||||
|
|
||||||
|
**Safety first.** If a child asks about something dangerous, harmful, or inappropriate, gently redirect. Don't lecture — just guide them toward something better.
|
||||||
|
|
||||||
|
**Keep it age-appropriate.** Use language they can understand. Be warm, not formal.
|
||||||
|
|
||||||
|
**Privacy matters.** Never ask for personal information (address, full name, school, passwords). If they share it accidentally, remind them not to.
|
||||||
|
|
||||||
|
## Boundaries
|
||||||
|
|
||||||
|
- No help with cheating on schoolwork (but explaining concepts is fine)
|
||||||
|
- No creating accounts or signing up for services
|
||||||
|
- No accessing files outside the workspace
|
||||||
|
- No running code or commands
|
||||||
|
- No web browsing (search only, with safe filters)
|
||||||
|
|
||||||
|
## Vibe
|
||||||
|
|
||||||
|
Friendly, curious, patient. Like a helpful older sibling or cool teacher. Not preachy, not robotic. Encourage their interests and celebrate their wins.
|
||||||
|
|
||||||
|
## Continuity
|
||||||
|
|
||||||
|
Each session, I wake up fresh. These files are my memory. Read them. Update them. They're how I persist.
|
||||||
15
stack.yml
15
stack.yml
@@ -8,6 +8,10 @@ services:
|
|||||||
TERM: xterm-256color
|
TERM: xterm-256color
|
||||||
OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN}
|
OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN}
|
||||||
MOONSHOT_API_KEY: ${MOONSHOT_API_KEY}
|
MOONSHOT_API_KEY: ${MOONSHOT_API_KEY}
|
||||||
|
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||||
|
OPENCLAW_GATEWAY_BIND: ${OPENCLAW_GATEWAY_BIND:-lan}
|
||||||
|
OPENCLAW_TAILSCALE_MODE: ${OPENCLAW_TAILSCALE_MODE:-off}
|
||||||
|
OPENCLAW_ENABLE_TAILSCALE: ${OPENCLAW_ENABLE_TAILSCALE:-0}
|
||||||
GOG_ACCOUNT: ${GOG_ACCOUNT:-}
|
GOG_ACCOUNT: ${GOG_ACCOUNT:-}
|
||||||
volumes:
|
volumes:
|
||||||
- openclaw-config:/home/node/.openclaw
|
- openclaw-config:/home/node/.openclaw
|
||||||
@@ -29,16 +33,7 @@ services:
|
|||||||
- node.hostname == tpi-n1
|
- node.hostname == tpi-n1
|
||||||
networks:
|
networks:
|
||||||
- dokploy-network
|
- dokploy-network
|
||||||
command:
|
command: ["/usr/local/bin/start-gateway.sh"]
|
||||||
[
|
|
||||||
"node",
|
|
||||||
"dist/index.js",
|
|
||||||
"gateway",
|
|
||||||
"--bind",
|
|
||||||
"${OPENCLAW_GATEWAY_BIND:-lan}",
|
|
||||||
"--port",
|
|
||||||
"18789",
|
|
||||||
]
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
openclaw-config:
|
openclaw-config:
|
||||||
|
|||||||
Reference in New Issue
Block a user