diff --git a/README.md b/README.md index 0c19138..2e82131 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # MacBook Pro Node - OpenClaw Gateway Integration +## Documentation + +- [OpenClaw Architecture](docs/OPENCLAW_ARCHITECTURE.md) +- [OpenClaw Health Monitoring](docs/OPENCLAW_HEALTH_MONITORING.md) + ## Overview This repository documents the OpenClaw gateway integration for the satware AG MacBook Pro, configured as a remote tool node controlled by the agent-zero Docker container running on the Manjaro PC. diff --git a/docs/OPENCLAW_ARCHITECTURE.md b/docs/OPENCLAW_ARCHITECTURE.md new file mode 100644 index 0000000..054aea3 --- /dev/null +++ b/docs/OPENCLAW_ARCHITECTURE.md @@ -0,0 +1,47 @@ +# OpenClaw Architecture: satware AG MacBook Pro Node + +## Hub-and-Spoke System + +The satware AG MacBook Pro operates as a **Remote Tool Node** in a hub-and-spoke configuration, where the Hub (Gateway) resides inside a Docker container on the "Luca" Manjaro server. + +### 1. Connection Chain (The "Extreme Details") + +The connection from this MacBook to the gateway uses a multi-layered tunnel to ensure secure communications over the Tailscale network while bypassing various Docker isolation barriers. + +#### A. Layer 1: Tailscale Mesh (IP: 100.64.0.x) +The underlying transport is the Tailscale mesh network. This MacBook (`100.64.0.12`) sees the Luca server at `100.64.0.39`. + +#### B. Layer 2: SSH Tunnel (Container Entry) +* **Service**: `ai.openclaw.ssh-tunnel.plist` +* **Command**: `ssh -N -L 18789:172.18.0.2:18789 root@100.64.0.39 -p 2222` +* **How it works**: + 1. The MacBook initiates an SSH connection to port `2222` on Luca's Tailscale IP. Port `2222` is the SSH server running **inside** the `agent-zero` Docker container. + 2. The `-L 18789:172.18.0.2:18789` switch creates a local loopback listener on this MacBook (`127.0.0.1:18789`). + 3. Traffic sent to `127.0.0.1:18789` is forwarded through the encrypted SSH tunnel directly to the container's internal IP and port (`172.18.0.2:18789`). + 4. Since the SSH server is inside the container, `172.18.0.2` is the container's internal interface IP, which the OpenClaw gateway is listening on. + +#### C. Layer 3: WebSocket Node Connection +* **Service**: `ai.openclaw.node.plist` +* **Command**: `openclaw node run --host 127.0.0.1 --port 18789` +* **How it works**: + 1. The node process starts on the MacBook. + 2. It connects to `ws://127.0.0.1:18789`. Because of the SSH tunnel above, this connects it to the remote gateway on Luca. + 3. It authenticates using the `OPENCLAW_GATEWAY_TOKEN` provided in the environment variables. + +### 2. Component Layout + +| Component | Role | Runtime | Host | +|-----------|------|---------|------| +| **Gateway Hub** | Primary agent orchestrator | Node.js (inside Docker) | Luca (Manjaro) | +| **Node Runner** | Secondary tool executor | Node.js (native) | MacBook Pro | +| **Llama Server** | LLM Inference (122B Qwen) | ROCm / HIP | evox2-js (AMD Strix Halo) | + +### 3. Permissions and Sandboxing + +* **Gateway Sandbox**: Disabled (`mode: off`) inside the `agent-zero` container. The container *is* the security boundary. +* **Node Permissions**: The MacBook Node requires **full disk access**, **accessibility**, and **screen recording** permissions granted to the Node.js binary (located at `/usr/local/opt/node/bin/node` or cellar equivalent). +* **Executive Approvals**: Controlled on the Gateway side in `openclaw-data/exec-approvals.json`. The MacBook node is currently granted a set of allowed commands (see status in README). + +### 4. Why 127.0.0.1? + +The MacBook's node configures the gateway URL as `ws://127.0.0.1:18789` because OpenClaw's security policy prohibits sending operator/node tokens over unencrypted (`ws://`) connections to **remote** IP addresses. However, `127.0.0.1` is considered "local" and always trusted. The SSH tunnel transparently wraps the local connection in encryption for its journey over the network. diff --git a/docs/OPENCLAW_HEALTH_MONITORING.md b/docs/OPENCLAW_HEALTH_MONITORING.md new file mode 100644 index 0000000..305d760 --- /dev/null +++ b/docs/OPENCLAW_HEALTH_MONITORING.md @@ -0,0 +1,96 @@ +# OpenClaw Health Monitoring: MacBook Pro Node + +## 1. Quick Service Status (Launchd) + +Check if the node and tunnel services are reported as running by macOS: + +```bash +launchctl list | grep ai.openclaw +``` +* `ai.openclaw.node`: The node runner. +* `ai.openclaw.ssh-tunnel`: The SSH entry tunnel. + +A status of `0` in the middle column usually indicates success. A positive number is the PID. + +## 2. Testing Connectivity (The "Curl" Test) + +Since the tunnel maps the gateway to your local loopback, you can probe the gateway's health endpoint directly from the MacBook terminal: + +```bash +curl -i http://127.0.0.1:18789/health +``` +* **Success**: Returns `HTTP/1.1 200 OK` with a JSON body `{"ok":true,...}`. +* **Failure (Tunnel Down)**: `curl: (7) Failed to connect to 127.0.0.1 port 18789: Connection refused`. +* **Failure (Gateway Down)**: `curl: (52) Empty reply from server` or timeout (if SSH connected but gateway is dead). + +## 3. Log Inspection + +The LaunchAgents are configured to log to `~/.openclaw/logs/`. + +* **Node Logs**: `tail -f ~/.openclaw/logs/node.err.log` + * Look for: `[node] connected to gateway`, `[node] authenticated`. + * Errors like `Device identity required` indicate a pairing/token issue. +* **Tunnel Logs**: `tail -f ~/.openclaw/logs/ssh-tunnel.err.log` + * Look for: `Permission denied`, `Connection refused`, or `channel 0: open failed: connect failed`. + * `Forwarding port 18789 to 172.18.0.2:18789` should be present. + +## 4. Using the Local CLI + +You can use the `openclaw` CLI directly on the MacBook to interact with the gateway through the tunnel. + +### Check Node Status +```bash +openclaw node status +``` + +### Manual Node Run (Foreground) +If the background service is acting up, stop it and run in the foreground to see real-time output: +```bash +launchctl unload ~/Library/LaunchAgents/ai.openclaw.node.plist +openclaw node run --host 127.0.0.1 --port 18789 --display-name "Debug Node" +``` + +## 5. Advanced RPC Diagnostics + +If you need to verify specific gateway states (e.g., if the node is actually visible in the `node.list`), you can use the RPC protocol. Since the `openclaw` CLI sometimes has scope issues, you can use a simple Node.js script (similar to the one on Luca) to send signed requests. + +**Example: Check Gateway Health via RPC** +```bash +OPENCLAW_WS_URL=ws://127.0.0.1:18789 \ +OPENCLAW_TOKEN=$(grep OPENCLAW_GATEWAY_TOKEN ~/Library/LaunchAgents/ai.openclaw.node.plist | sed -E 's/.*(.*)<\/string>.*/\1/') \ +node -e " +const WebSocket = require('ws'); +const ws = new WebSocket(process.env.OPENCLAW_WS_URL); +ws.on('open', () => { + ws.send(JSON.stringify({ + type: 'req', id: '1', method: 'connect', + params: { auth: { token: process.env.OPENCLAW_TOKEN }, role: 'operator', scopes: ['operator.read'] } + })); +}); +ws.on('message', (data) => { + const msg = JSON.parse(data); + if (msg.method === 'connect') { + ws.send(JSON.stringify({ type: 'req', id: '2', method: 'health' })); + } else if (msg.id === '2') { + console.log(JSON.stringify(msg.payload, null, 2)); + process.exit(0); + } +}); +" +``` + +## 6. Restarting Services + +If things get stuck, the "Standard Restart" is: + +```bash +# 1. Restart Tunnel +launchctl unload ~/Library/LaunchAgents/ai.openclaw.ssh-tunnel.plist +launchctl load ~/Library/LaunchAgents/ai.openclaw.ssh-tunnel.plist + +# 2. Restart Node +launchctl unload ~/Library/LaunchAgents/ai.openclaw.node.plist +launchctl load ~/Library/LaunchAgents/ai.openclaw.node.plist +``` + +Wait ~5 seconds between steps for the port to bind.