OpenClaw Configuration

Archived. Manual hardening steps on this page are superseded by the deploy.sh Ansible automation. See Deployment for the current deployment workflow.

OpenClaw Configuration

Skillful-Alhazen is designed to run as a persistent agent via OpenClaw — an agent platform that provides always-on connectivity to messaging platforms (Telegram, Discord, etc.), scheduled tasks, and background processing.

This guide covers installing OpenClaw to work with skillful-alhazen, hardening the deployment for security, and accessing dashboards remotely via Tailscale.

Security context: OpenClaw grants an AI agent persistent access to your system — including shell execution, file I/O, and network access. The OpenClaw Security Hardening Guide documents the threat landscape in detail. This page applies those recommendations to a skillful-alhazen deployment. Read the full guide before proceeding.


Before You Begin: Account Hygiene

Never connect these accounts to an OpenClaw-managed system:

  • Primary email, banking, or cryptocurrency accounts
  • Work or corporate systems
  • Password managers or credential vaults
  • Social media with irreplaceable history
  • Government or healthcare portals

Acceptable as dedicated/burner accounts only:

  • A Telegram bot token (created for this purpose)
  • A development-only GitHub account
  • An Anthropic API key with usage limits set
  • Low-stakes services you could recreate in under an hour

Skillful-alhazen’s skills (literature search, job hunting, knowledge graph) don’t require access to sensitive personal accounts. Keep it that way.


Prerequisites

  1. OpenClaw installed (npm install -g openclaw)
  2. Tailscale installed and configured (for remote dashboard access)
  3. Node.js 22.12.0+ — required to mitigate CVE-2026-21636 (permission model bypass). Check with node --version.
  4. Skillful-alhazen already set up per Getting Started

Installing OpenClaw

npm install -g openclaw

Verify installation:

openclaw --version
openclaw status

Gateway Configuration

The OpenClaw gateway is the always-on daemon that manages sessions, messaging, cron jobs, and heartbeats.

Start the Gateway

openclaw gateway start

Key Configuration

Use openclaw gateway config to set up:

  • Channel: Connect to Telegram, Discord, Signal, etc.
  • Workspace: Point to your skillful-alhazen workspace directory
  • Model: Set your preferred Claude model
  • Heartbeat: Configure periodic check-in interval
# View current config
openclaw gateway config get

# Example: set workspace
openclaw gateway config set workspace /path/to/skillful-alhazen

Refer to the OpenClaw docs for the full configuration reference.

Agent Setup

OpenClaw agents are configured under ~/.openclaw/agents/. The key files:

File Purpose
AGENTS.md Agent behavior and conventions
SOUL.md Personality and identity
USER.md Information about the user
MEMORY.md Long-term memory (curated)
HEARTBEAT.md Periodic task checklist
TOOLS.md Environment-specific notes (devices, SSH hosts, etc.)

These files live in the OpenClaw workspace (~/.openclaw/workspace/) and are injected into every session.

Cron Jobs

OpenClaw supports scheduled tasks via cron jobs. Skillful-alhazen uses these for automated workflows like the job forager.

Example: Nightly Job Forager

The jobhunt skill’s forager pipeline runs nightly to discover new job postings:

Schedule: 0 0 * * * (midnight PST)
Type: agentTurn (isolated session)
Task: Run the full forager pipeline — search sources, triage candidates, report relevant finds

Cron jobs can be managed through the OpenClaw gateway or via the agent’s cron tool during a session.

Heartbeats vs Cron

Use Case Mechanism
Batch periodic checks (email, calendar) Heartbeat
Exact timing (“9 AM every Monday”) Cron
One-shot reminders Cron (at schedule)
Tasks needing isolation from main session Cron (isolated session)

Security Hardening

The sections below follow the three-tier model from the OpenClaw Security Hardening Guide, adapted for a macOS deployment running skillful-alhazen.

Threat Model

Running OpenClaw under your login account means:

  • The gateway process can read/write everything in your home directory
  • Credentials (API keys, bot tokens, device keys) sit alongside personal files
  • A compromised agent session could exfiltrate SSH keys, browser data, or documents
  • No separation between “what the bot can do” and “what you can do”

Unfixable risks (acknowledged, not eliminated):

  • Prompt injection from content the agent processes (papers, web pages, job postings)
  • Zero-day exploits in OpenClaw, Node.js, or Claude’s tool use
  • Compromised model provider APIs — a poisoned response could instruct the agent to act maliciously
  • Supply chain attacks through MCP servers or npm packages

These are inherent to any system that gives an AI agent tool access. The mitigations below reduce the blast radius if any of them occur.

Deployment Choice: Local vs VPS

The hardening guide recommends running OpenClaw on an isolated VPS rather than a primary computer. Skillful-alhazen can run on either:

Factor Local (Mac Mini, etc.) VPS (Hetzner, etc.)
Docker (TypeDB) Native Docker Desktop Docker CE
Latency to local LLMs (Ollama) Low High / N/A
Blast radius if compromised Your local machine Disposable server
Dashboard access Tailscale Serve Tailscale or direct
Cost Hardware you already own ~$4-10/month

If you run locally, the dedicated user isolation described below is essential — it constrains the blast radius to the service user’s home directory rather than your entire account.

If you run on a VPS, user isolation is still recommended, but you gain the additional protection of being able to destroy and rebuild the entire machine if compromised.


Tier 1: Basic Protection

These are foundational steps. Skip none of them.

1.1 Gateway Binding: Loopback Only

The gateway must bind to 127.0.0.1, never 0.0.0.0. In openclaw.json:

{
  "gateway": {
    "bind": "loopback",
    "port": 18789
  }
}

Verify after startup:

sudo lsof -i :18789 -sTCP:LISTEN
# Should show localhost:18789, NOT *:18789

1.2 Firewall: Block the Gateway Port

Even with loopback binding, explicitly block the gateway port from external access:

macOS (pf):

# Add to /etc/pf.conf:
block in on ! lo0 proto tcp to any port 18789

Linux (ufw):

sudo ufw deny in 18789

1.3 File Permissions

Credential files must not be world-readable:

chmod 700 ~/.openclaw/credentials ~/.openclaw/identity
chmod 600 ~/.openclaw/openclaw.json
chmod 600 ~/.openclaw/identity/device.json
chmod 600 ~/.openclaw/credentials/*

1.4 Authentication Enforcement

Ensure device authentication is not disabled. In openclaw.json, verify that dangerouslyDisableDeviceAuth is not present or is set to false. Require pairing for Telegram DM access:

{
  "channels": {
    "telegram": {
      "dmPolicy": "pairing"
    }
  }
}

File permissions prevent other users from reading credentials, but don’t protect against disk theft or backup exposure. For stronger protection, encrypt sensitive files with age before startup:

# Encrypt
age -r <your-public-key> -o openclaw.json.age openclaw.json

# Decrypt into memory at startup (in LaunchDaemon wrapper script)
age -d -i <key-file> openclaw.json.age > /tmp/openclaw-config && ...

This is especially important if your machine’s disk is not encrypted with FileVault (macOS) or LUKS (Linux).

1.6 Run the Built-in Security Audit

openclaw gateway audit

Fix all CRITICAL and WARNING findings before proceeding.


Tier 2: Standard Protection

2.1 Dedicated User Isolation (macOS)

This is the most impactful single hardening step for local deployments. Migrate the gateway from your login account to a dedicated service user.

What this achieves:

  • Process isolation: Gateway runs as a separate OS user with no access to personal files
  • Credential separation: Bot tokens, API keys, and device keys are owned by the service user; your login user cannot read them without sudo
  • Least-privilege skill access: Read-only access to skill code via ACLs; the bot cannot modify code or push to git
  • Auditability: All gateway processes visible under the service user in ps

Architecture:

/Users/<your-user>  (chmod 700)
  ├── <skill-repo>/  ◄── ACL: read-only for <service-user>
  └── .docker/run/docker.sock  ◄── ACL: read+write for <service-user>
         │ (ACL traverse only)
         ▼
/Users/<service-user>  (chmod 700)
  └── .openclaw/
      ├── openclaw.json        600 — config + tokens
      ├── credentials/         700 — telegram pairing
      ├── identity/            700 — device keys
      ├── agents/              700 — session data
      ├── workspace/           skills symlinks → shared repo
      └── logs/                755 — readable for debugging
         │
         ▼
/Library/LaunchDaemons/
  ai.openclaw.gateway.plist    root:wheel 644
  UserName: <service-user>
  KeepAlive: true

Migration Steps

Step 0: Full Backup

mkdir -p ~/openclaw-migration-backup
rsync -a ~/.openclaw/ ~/openclaw-migration-backup/.openclaw/
cp ~/Library/LaunchAgents/ai.openclaw.gateway.plist ~/openclaw-migration-backup/

Step 1: Stop the gateway

launchctl bootout gui/$(id -u)/ai.openclaw.gateway
# Verify: lsof -i :<port> should return nothing

Step 2: Create a shared group

sudo dscl . -create /Groups/<shared-group>
sudo dscl . -create /Groups/<shared-group> PrimaryGroupID <available-gid>
sudo dscl . -create /Groups/<shared-group> RealName "OpenClaw Shared Access"
sudo dscl . -append /Groups/<shared-group> GroupMembership <your-user>
sudo dscl . -append /Groups/<shared-group> GroupMembership <service-user>

Step 3: ACL-based shared access

Grant the service user search-only (traverse) on your home directory and recursive read-only on the shared skill repository:

# Traverse only — cannot list directory contents
sudo chmod +a "<service-user> allow search" /Users/<your-user>

# Recursive read-only on the skill repository
sudo chmod -R +a "<service-user> allow read,readattr,readextattr,readsecurity,search,execute" \
  /Users/<your-user>/<skill-repo>

If Docker Desktop is installed, the Docker socket on macOS is a symlink into your home directory. Add traverse ACLs on the intermediate directories:

sudo chmod +a "<service-user> allow search" /Users/<your-user>/.docker
sudo chmod +a "<service-user> allow search" /Users/<your-user>/.docker/run
sudo chmod +a "<service-user> allow read,write,readattr,readextattr" \
  /Users/<your-user>/.docker/run/docker.sock

Step 4: Create directory structure

Mirror the .openclaw layout under the service user’s home:

/Users/<service-user>/.openclaw/
├── agents/main/{agent,sessions}
├── browser/
├── canvas/
├── credentials/
├── cron/runs/
├── devices/
├── identity/
├── logs/
├── media/
├── subagents/
├── telegram/
├── tools/
└── workspace/{memory,skills}

Step 5: Copy and adapt configuration

Copy all configuration files from the old location. Critical path rewrites:

  • openclaw.json: Change workspace to the new location. Keep skill environment variables (like ALHAZEN_PROJECT_ROOT) pointing to the shared repo — accessed via ACL.
  • sessions.json: Contains hardcoded paths to workspace, skills, and session files. Global find-and-replace the old .openclaw prefix.
  • exec-approvals.json: Contains a socket path that needs updating.
  • Session .jsonl files: May contain embedded paths.
# Fix all .openclaw path references (but NOT shared repo paths)
sed -i '' 's|/Users/<your-user>/.openclaw/|/Users/<service-user>/.openclaw/|g' <files...>

Do not change paths that reference the shared skill repository — those are correct and accessed via ACL.

Set ownership and permissions:

  • All files: chown -R <service-user>:staff
  • Sensitive directories (agents, credentials, identity, media, subagents, telegram): chmod 700
  • Sensitive files (openclaw.json, device keys, session state): chmod 600
  • Logs directory: chmod 755

Step 6: Service user shell profile

Create a minimal .zshrc for the service user:

export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
export PYTHONDONTWRITEBYTECODE=1
export DOCKER_HOST="unix:///var/run/docker.sock"
export HOME="/Users/<service-user>"
export UV_NO_SYNC=1
export UV_CACHE_DIR="/Users/<service-user>/.cache/uv"

Key settings:

  • PYTHONDONTWRITEBYTECODE=1 — prevents __pycache__ write failures in the read-only shared repo
  • UV_NO_SYNC=1 — prevents uv from trying to write to the shared repo’s .venv
  • UV_CACHE_DIR — writable cache location for the service user

Step 7: Docker configuration

mkdir -p /Users/<service-user>/.docker
echo '{"currentContext": "desktop-linux"}' > /Users/<service-user>/.docker/config.json

Step 8: Create LaunchDaemon

Replace the per-user LaunchAgent with a system LaunchDaemon at /Library/LaunchDaemons/ai.openclaw.gateway.plist:

  • UserName and GroupName specify the service user
  • HOME points to the service user’s home
  • Environment includes DOCKER_HOST, PYTHONDONTWRITEBYTECODE, UV_NO_SYNC, UV_CACHE_DIR
  • Owned by root:wheel, mode 644
  • RunAtLoad: true and KeepAlive: true

Step 9: Disable old LaunchAgent

mv ~/Library/LaunchAgents/ai.openclaw.gateway.plist \
   ~/Library/LaunchAgents/ai.openclaw.gateway.plist.disabled

Step 10: Lock down home directories

sudo chmod 700 /Users/<your-user>    # ACLs still allow service user traverse
sudo chmod 700 /Users/<service-user>  # You can't read their credentials without sudo

Step 11: Start and verify

sudo launchctl bootstrap system /Library/LaunchDaemons/ai.openclaw.gateway.plist

Verify:

  • ps aux | grep openclaw-gateway — runs as service user
  • curl -4 http://127.0.0.1:18789/ — HTTP 200
  • sudo lsof -i :<port> -sTCP:LISTEN — bound to loopback
  • Send a Telegram message to the bot

2.2 Tool Allowlisting

Switch from denylists to explicit allowlists. Only permit commands the agent actually needs:

{
  "permissions": {
    "allow": [
      "WebSearch",
      "Bash(uv run:*)",
      "Bash(docker ps:*)",
      "Bash(docker compose:*)"
    ],
    "deny": [
      "Bash(sudo:*)",
      "Bash(chmod:*)",
      "Bash(chown:*)",
      "Bash(rm -rf:*)",
      "Bash(ssh:*)",
      "Bash(scp:*)"
    ]
  }
}

Allowlists are more secure than denylists — a denylist only blocks what you think of, while an allowlist blocks everything you didn’t think of.

2.3 MCP Server Security

If using MCP servers with OpenClaw:

  • Pin versions — set autoUpdate: false to prevent untrusted updates
  • Disable unnecessary servers — especially filesystem, shell, ssh, and docker MCP servers
  • Never enable enableAllProjectMcpServers: true
  • Review source code of any MCP server before installation — search for eval(), exec(), spawn(), and unexpected network calls

Warning: A Cisco Talos study found that 26% of surveyed MCP skills contained vulnerabilities. Treat MCP servers as untrusted code.

2.4 Monitoring Script

Install a monitoring script that verifies security invariants. Run it periodically or after any configuration change:

Check What it verifies
Process isolation Gateway runs as service user, not login user
LaunchDaemon status Loaded with a running PID
Gateway connectivity Responds on loopback
Network binding Bound to localhost, not 0.0.0.0
File permissions Both homes are 700, config is 600, daemon is root:wheel
Cross-user isolation Config files owned by service user
Old config LaunchAgent is disabled
Logs No EACCES or permission errors

2.5 Maintenance Schedule

Frequency Task
Weekly Review gateway logs for anomalies or permission errors
Monthly Update OpenClaw (npm update -g openclaw), review tool allowlists
Monthly Audit active OAuth sessions and API key usage
Quarterly Rotate API keys and gateway tokens
After any uv sync Re-apply ACL on the skill repo .venv (see Operations below)

Tier 3: Advanced Protection (Optional)

These measures provide defense-in-depth but require more setup and maintenance.

3.1 Container Sandbox

The hardening guide recommends running OpenClaw inside a container with no direct internet access, proxying API calls through a credential broker (e.g., LiteLLM):

┌──────────────────────────────────────────────┐
│  openclaw-internal network (no internet)     │
│  ┌──────────────┐    ┌───────────────────┐   │
│  │  OpenClaw    │───▶│  LiteLLM          │   │
│  │  (no egress) │    │  (credential      │──▶ Internet (API only)
│  │              │    │   broker + rate    │   │
│  └──────────────┘    │   limiting)        │   │
│                      └───────────────────┘   │
└──────────────────────────────────────────────┘

Verification if implemented:

  • docker exec openclaw-agent ping 8.8.8.8 should fail
  • docker exec openclaw-agent nc -zv litellm 4000 should succeed
  • Containers run as UID 1000 (not root)
  • Filesystem is read-only: docker exec openclaw-agent touch /test-file should fail

Note: Use rootless Podman over Docker where possible. A Docker daemon escape grants full host access; a Podman escape lands as an unprivileged user.

Skillful-alhazen’s Docker Compose services (TypeDB, dashboards) already run on an isolated network. Containerizing the OpenClaw gateway itself would add an additional isolation layer.

3.2 Network Egress Filtering

A proxy container (e.g., Squid) between OpenClaw and the internet restricts which domains the agent can reach:

Example allowlist (deny-by-default):

# API providers
.anthropic.com
.openai.com

# Skills: literature search
.europepmc.org
.ebi.ac.uk

# Skills: job hunting
.linkedin.com
.greenhouse.io
.lever.co

# Infrastructure
.github.com
.npmjs.org
.telegram.org
.docker.io

Block all financial, email, cloud storage, social media, and unknown domains. Audit actually-accessed domains monthly and remove unused entries.

3.3 Source Code Review

Before installing any skill or MCP server:

  1. Download and review the source code
  2. Search for eval(), exec(), spawn(), fetch(), XMLHttpRequest
  3. Verify publisher identity and download history
  4. Run npm audit on dependencies
  5. Check for unexpected outbound network calls

This applies to third-party skills. Skillful-alhazen’s own skills are open source and auditable in the repository.


Remote Access with Tailscale

Skillful-alhazen runs Docker services (TypeDB, dashboards, hub) bound to 127.0.0.1 for security. To access these remotely (e.g., from your phone or laptop), use Tailscale Serve to proxy local ports over your Tailnet with authentication.

Why Tailscale Serve (Not Port Binding)

Binding ports to 0.0.0.0 exposes services to the entire network without authentication. Tailscale Serve keeps services on localhost and proxies them through your authenticated Tailnet — only your devices can reach them.

Setup

  1. Install Tailscale and join your Tailnet:
# macOS: Install from App Store or download from tailscale.com
# Verify connection
tailscale status
  1. Enable HTTPS (required for Tailscale Serve):
tailscale cert <your-machine-name>.<tailnet-name>.ts.net
  1. Serve the dashboard hub (port 8080):
tailscale serve --https=8080 http://127.0.0.1:8080
  1. Serve the job hunt dashboard (port 3001):
tailscale serve --https=3001 http://127.0.0.1:3001
  1. Verify from any device on your Tailnet:
https://<your-machine-name>.<tailnet-name>.ts.net:8080/
https://<your-machine-name>.<tailnet-name>.ts.net:3001/

Persistence Across Reboots

Tailscale Serve configs persist, but Tailscale itself must be running:

  • macOS: Add Tailscale.app to System Settings > General > Login Items
  • Linux: sudo systemctl enable tailscaled

Docker Compose Port Bindings

The docker-compose.yml intentionally binds to 127.0.0.1:

services:
  hub:
    ports:
      - "127.0.0.1:8080:80"    # Only localhost — Tailscale proxies this
  jobhunt-dashboard:
    ports:
      - "127.0.0.1:3001:3000"  # Only localhost — Tailscale proxies this
  typedb:
    ports:
      - "127.0.0.1:1729:1729"  # Only localhost

Do not change these to 0.0.0.0 — that bypasses Tailscale’s authentication layer.


Ongoing Operations

Task Command Restart needed?
Update skill code git pull in the shared repo (as your user) No
Rebuild skill venv uv sync then re-apply ACL on .venv (see below) No
Update OpenClaw npm update -g openclaw then sudo launchctl kickstart -k system/ai.openclaw.gateway Yes
Edit config sudo -u <service-user> vi /Users/<service-user>/.openclaw/openclaw.json then restart Yes
View logs sudo tail -f /Users/<service-user>/.openclaw/logs/gateway.err.log No
Run monitor openclaw-monitor No

After recreating the skill repo .venv (e.g., after uv sync or dependency changes):

sudo chmod -R +a "<service-user> allow read,readattr,readextattr,readsecurity,search,execute" \
  /Users/<your-user>/<skill-repo>/.venv

The service user runs with UV_NO_SYNC=1 — it uses the venv read-only and never tries to modify it.


Emergency Response

If you suspect a compromise:

  1. Stop immediately: sudo launchctl bootout system/ai.openclaw.gateway (or openclaw stop)
  2. Cut network: sudo pfctl -e && echo "block all" | sudo pfctl -f - (macOS)
  3. Preserve evidence: Copy the .openclaw directory and logs before making changes
  4. Assume all credentials compromised: Revoke API keys, bot tokens, OAuth sessions, and SSH keys immediately
  5. Review logs: Check session transcripts for unexpected tool invocations or data exfiltration attempts

Recovery: If running on a VPS, destroy and rebuild from scratch. If running locally, the dedicated user isolation means the blast radius is limited to the service user’s home directory — but still rotate all credentials the agent had access to.


Rollback (User Isolation)

If the user isolation setup causes issues, full rollback takes under 2 minutes:

  1. sudo launchctl bootout system/ai.openclaw.gateway
  2. sudo rm /Library/LaunchDaemons/ai.openclaw.gateway.plist
  3. sudo chmod 750 /Users/<your-user> + remove ACLs with chmod -a
  4. Rename .disabled back to .plist, re-bootstrap the old LaunchAgent
  5. Everything returns to pre-migration state

Risk Summary

Risk Status Mitigation
Agent reads personal files Mitigated Dedicated user isolation; chmod 700 on both homes
Credentials exposed in plaintext Partially mitigated File permissions (600/700); full mitigation requires encryption at rest
Gateway exposed on network Mitigated Loopback binding; firewall rule
Agent runs destructive commands Mitigated Tool allowlisting; sudo denied
Python writes to read-only repo Mitigated PYTHONDONTWRITEBYTECODE=1
uv writes to read-only venv Mitigated UV_NO_SYNC=1
Docker socket access Mitigated Explicit ACLs on socket path
Prompt injection from content Not mitigated Inherent to AI agent architecture; reduce blast radius via isolation
Supply chain attacks (MCP/npm) Partially mitigated Source code review; version pinning
Compromised model provider Not mitigated Inherent risk; limit connected accounts
Container escape (Docker) Not mitigated locally Would require containerizing OpenClaw itself (Tier 3)
ACLs lost after venv recreation Documented Re-apply after each uv sync

Architecture Overview

┌─────────────────────────────────────────────────────┐
│                   Your Devices                      │
│              (phone, laptop, etc.)                  │
└──────────────────────┬──────────────────────────────┘
                       │ Tailscale (encrypted, authenticated)
                       ▼
┌─────────────────────────────────────────────────────┐
│                  Mac Mini / Server                  │
│                                                     │
│  ┌──────────────┐  ┌───────────┐  ┌──────────────┐  │
│  │ OpenClaw     │  │ Tailscale │  │ Docker       │  │
│  │ Gateway      │  │  Serve    │  │              │  │
│  │ (service     │  │           │  │ ┌──────────┐ │  │
│  │  user)       │  │ :8080 ──▶─│──│─│ Hub      │ │  │
│  │              │  │ :3001 ──▶─│──│─│ Dashboard│ │  │
│  │ Telegram     │  │           │  │ │ TypeDB   │ │  │
│  │ Cron         │  │           │  │ └──────────┘ │  │
│  │ Sessions     │  └───────────┘  └──────────────┘  │
│  └──────────────┘                                   │
│         │ read-only ACL                             │
│         ▼                                           │
│  ┌──────────────┐                                   │
│  │ Skill Repo   │  (owned by your login user)       │
│  │ (read-only)  │                                   │
│  └──────────────┘                                   │
└─────────────────────────────────────────────────────┘

Troubleshooting

Symptom Cause Fix
Dashboard returns 404 remotely Tailscale stopped tailscale up
tailscale serve fails Tailscale not running tailscale up first
Services unreachable after reboot Tailscale not in Login Items Add to Login Items
Gateway not responding LaunchDaemon stopped sudo launchctl kickstart -k system/ai.openclaw.gateway
Cron jobs not firing Gateway down or job disabled openclaw gateway status, check job config
EACCES on agent sessions Hardcoded paths not updated sed replace old .openclaw paths in sessions.json
Skill scripts fail with import errors venv ACL missing after uv sync Re-apply ACL on .venv directory
__pycache__ write errors Missing env var Verify PYTHONDONTWRITEBYTECODE=1 in LaunchDaemon
Docker commands fail from agent Socket ACL missing Check ACLs on .docker/run/docker.sock