Skip to content

Instantly share code, notes, and snippets.

@divideby0
Created February 11, 2026 20:05
Show Gist options
  • Select an option

  • Save divideby0/dfb76056f2314465f11fa9f3d40875fb to your computer and use it in GitHub Desktop.

Select an option

Save divideby0/dfb76056f2314465f11fa9f3d40875fb to your computer and use it in GitHub Desktop.
OpenClaw Security Hardening Guide — OS isolation, 1Password, TruffleHog, Tailscale, and more

OpenClaw Security Hardening Guide

A detailed walkthrough of how I (Cedric Hurst, @divideby0) have locked down my OpenClaw AI assistant, "Evie," running on a Mac mini M4. This guide covers every layer of the security model — from OS isolation to network access to behavioral controls.

Written collaboratively by Cedric and Evie herself.


Table of Contents

  1. Architecture Overview
  2. OS-Level Isolation
  3. Credential Management (1Password)
  4. Secret Scanning (TruffleHog)
  5. Network Security (Tailscale)
  6. Database Security
  7. Behavioral Controls
  8. Audit & Observability
  9. Pending Work
  10. Knowledge Graph Demo

Architecture Overview

┌─────────────────────────────────────────────────┐
│  Mac mini M4 (macOS Sequoia 15.x)               │
│                                                  │
│  ┌──────────────┐     ┌──────────────────────┐  │
│  │ cedric (admin)│     │ openclaw (standard)  │  │
│  │ • Docker      │     │ • OpenClaw gateway   │  │
│  │ • Supabase    │     │ • Agent sessions     │  │
│  │ • System mgmt │     │ • Skills/scripts     │  │
│  └──────────────┘     └──────────────────────┘  │
│         │                       │                │
│         │ SSH (BatchMode)       │ localhost only  │
│         ▼                       ▼                │
│  ┌──────────────────────────────────────────┐   │
│  │  Supabase (Docker)                        │   │
│  │  Postgres · PostgREST · Auth · Studio     │   │
│  │  Ports: 127.0.0.1:65xxx (new stack)       │   │
│  └──────────────────────────────────────────┘   │
│                                                  │
│  ┌──────────────────────────────────────────┐   │
│  │  Tailscale (system daemon)                │   │
│  │  TLS termination → localhost:18789        │   │
│  │  ACL: cedric.hurst@gmail.com only         │   │
│  └──────────────────────────────────────────┘   │
└─────────────────────────────────────────────────┘

OpenClaw is an open-source AI agent framework. The gateway process runs as a standard (non-admin) macOS user. All external services (database, Docker, etc.) are owned by the admin account. The agent communicates with the outside world through the gateway, which handles message routing to Telegram, Slack, etc.


1. OS-Level Isolation

Dedicated macOS User Account

The agent runs under a dedicated openclaw user with standard (non-admin) privileges.

# Create the user (run as admin)
sudo sysadminctl -addUser openclaw -fullName "OpenClaw" -password "<secure>" -home /Users/openclaw

# Verify: no admin group membership
dscl . -read /Groups/admin GroupMembership
# → openclaw should NOT appear in the list

What the agent CAN'T do:

Action Blocked? Why
Install system software (brew install) ✅ Blocked /opt/homebrew owned by admin
Modify system settings ✅ Blocked No admin privileges
Access other user home dirs ✅ Blocked macOS file permissions
Run Docker commands ✅ Blocked Docker socket owned by admin
sudo anything ✅ Blocked Not in sudoers
Manage system services (launchd) ✅ Blocked Requires admin/root

What the agent CAN do:

Action Allowed? Why
Read/write its own home dir ✅ Allowed Standard user perms
Run bun, node, python3, curl ✅ Allowed Installed in user-accessible paths
SSH to admin account (limited) ✅ Allowed Key-based, BatchMode, for DB migrations
Listen on high ports (>1024) ✅ Allowed Standard macOS behavior
Access the internet ✅ Allowed Needed for APIs, web search

SSH Bridge to Admin Account

For operations that require admin access (e.g., running supabase db push), the agent SSHs to the admin account:

ssh -o BatchMode=yes cedric@localhost "PATH=/usr/local/bin:/opt/homebrew/bin:\$PATH supabase db push --local"

This is key-based auth with BatchMode=yes (no interactive prompts). The admin account controls what's accessible — the agent can't escalate beyond what SSH allows.


2. Credential Management (1Password)

The Problem

AI agents need API keys, database credentials, OAuth tokens. Storing them in flat JSON files on disk is a security risk — if session logs capture them (and they do), they leak.

The Solution: 1Password Service Account

We use a 1Password service account with read-only access to a dedicated "Openclaw" vault.

Setup:

  1. Create a 1Password service account (requires 1Password Family or Business plan)

    • Go to: https://my.1password.com → Developer → Service Accounts
    • Create account with read-only access to a specific vault
    • Save the token (starts with ops_...)
  2. Store the token in macOS Keychain (not in a file):

    security add-generic-password -a "openclaw" -s "1password-service-account" -w "ops_YOUR_TOKEN_HERE"
  3. Retrieve and use in scripts:

    TOKEN=$(security find-generic-password -a "openclaw" -s "1password-service-account" -w)
    OP_SERVICE_ACCOUNT_TOKEN="$TOKEN" op read "op://Openclaw/Fellow API/credential"

What's in the vault (13 items):

Item Purpose
Fellow API Meeting sync credentials
Float API Timesheet management
Supabase Local Database connection
GitHub App (Evie) Bot identity for GitHub comments
Google OAuth tokens Gmail, Calendar, Drive access
ElevenLabs API Text-to-speech
Telegram Bot Messaging
... Other service credentials

Key Properties

  • Read-only: The agent cannot create, update, or delete vault items
  • Scoped: Only the "Openclaw" vault is accessible — not personal or shared vaults
  • No interactive auth: Service account tokens work headlessly (no browser, no biometric)
  • Rotatable: Token can be revoked/regenerated without touching the agent

1Password CLI Reference: https://developer.1password.com/docs/cli/


3. Secret Scanning (TruffleHog)

The Problem

LLM conversations capture everything — including API keys that appear in tool output, error messages, or copy-pasted configs. Session logs (.jsonl files) can accumulate live secrets over time.

The Solution: TruffleHog Scanning + Redaction

We built a custom skill wrapping TruffleHog (open-source, 800+ detector types with verification):

# Scan session logs
bun run skills/secret-scan/scripts/scan.ts scan ~/.openclaw/agents/main/sessions/

# Scan and redact (replaces secrets with [REDACTED:detector_name:hash])
bun run skills/secret-scan/scripts/scan.ts redact ~/.openclaw/agents/main/sessions/

Results from our first scan:

Detector Count Verified
Slack API tokens 4 ✅ Live
OpenAI API keys 3 ✅ Live
Telegram Bot tokens 2 ✅ Live
ElevenLabs API keys 2 ✅ Live
Sendgrid API keys 2 ✅ Live
URI with password 1 ✅ Live
Total 14 All verified

All 14 were redacted in place. The redaction format preserves enough info for debugging without exposing the secret:

[REDACTED:slack:a1b2c3]  ← detector type + short hash for identification

Ongoing Practice

  • Run TruffleHog periodically (can be automated via cron)
  • Never paste secrets in chat — use 1Password or terminal
  • Agent is instructed to push back if a user tries to share secrets in conversation

TruffleHog GitHub: https://github.com/trufflesecurity/trufflehog


4. Network Security (Tailscale)

The Problem

You want to access your agent remotely (from your laptop, phone, etc.) without exposing it to the public internet.

The Solution: Tailscale Mesh VPN

Tailscale creates a private, encrypted WireGuard mesh network between your devices. No port forwarding, no public IPs, no firewalls to configure.

Setup:

  1. Install Tailscale as a system daemon (survives user switching):

    # Install via brew on macOS
    brew install tailscale
    
    # Or download from https://tailscale.com/download/mac
    
    # Run as system service (not the GUI app — that's per-user)
    # On macOS, Tailscale installs a system extension
  2. Configure Tailscale Serve (TLS proxy to local gateway):

    # Proxy HTTPS traffic to local OpenClaw gateway
    tailscale --socket=/var/run/tailscale/tailscaled.sock serve --bg http://localhost:18789

    This gives you https://your-machine-name.tailnet-name.ts.net with automatic TLS certificates.

  3. Lock down ACLs (in Tailscale admin console):

    {
      "acls": [
        {
          "action": "accept",
          "src": ["cedric.hurst@gmail.com"],
          "dst": ["*:*"]
        }
      ]
    }

    Only your account can reach any device on the tailnet.

Network Architecture

Your Phone/Laptop                    Mac mini
  (Tailscale)        ──────────►    (Tailscale)
                     WireGuard       │
                     encrypted       │ tailscale serve
                                     ▼
                                  https://*.ts.net
                                     │
                                     │ TLS termination
                                     ▼
                                  localhost:18789
                                     │
                                     │ token auth
                                     ▼
                                  OpenClaw Gateway

Key Properties

  • End-to-end encrypted: WireGuard tunnels between devices
  • No public exposure: Gateway only listens on 127.0.0.1
  • Identity-based ACLs: Access tied to your identity, not IP addresses
  • Automatic TLS: Tailscale provisions Let's Encrypt certs for your .ts.net domain
  • MagicDNS: Access via hostname, not IP

Gateway config:

{
  "gateway": {
    "trustedProxies": ["127.0.0.1", "::1"],
    "auth": {
      "allowTailscale": true
    }
  }
}

Tailscale Docs: https://tailscale.com/kb/
Tailscale Serve: https://tailscale.com/kb/1242/tailscale-serve
ACLs: https://tailscale.com/kb/1018/acls


5. Database Security

Current Setup: Supabase Local

We run a full Supabase stack locally via Docker Compose — Postgres, PostgREST, Auth, Studio, etc.

Self-managed Docker Compose (not the Supabase CLI) gives us control over port bindings:

# All ports bound to localhost only
services:
  db:
    ports:
      - "127.0.0.1:65322:5432"
  rest:
    ports:
      - "127.0.0.1:65321:3000"
  studio:
    # Accessed through Kong, not directly exposed

Access Model

Role Access How
Admin (cedric) Full Postgres superuser Docker-managed, password in 1Password
Agent (openclaw) PostgREST API with service_role key Key in 1Password vault
Public Nothing All ports localhost-only

Pending: Dedicated Database User with RLS

The plan is to create a non-admin Postgres user for the agent:

CREATE USER openclaw_agent WITH PASSWORD '...';
GRANT USAGE ON SCHEMA public TO openclaw_agent;
-- Grant SELECT/INSERT/UPDATE on specific tables
-- Row-level security policies control what data is visible

This means:

  • Agent can't DROP TABLE or modify schema
  • Schema migrations require the admin password (manual gate)
  • RLS policies can scope data access per-table

Supabase Docs: https://supabase.com/docs
Supabase Self-Hosting: https://supabase.com/docs/guides/self-hosting/docker


6. Behavioral Controls

System Prompt Rules

The agent's system prompt includes explicit behavioral constraints:

## Safety
- Don't exfiltrate private data. Ever.
- Don't run destructive commands without asking.
- `trash` > `rm` (recoverable beats gone forever)
- When in doubt, ask.

## External vs Internal
**Ask first:**
- Sending emails, tweets, public posts
- Anything that leaves the machine
- Anything you're uncertain about

**Safe to do freely:**
- Read files, explore, organize, learn
- Search the web, check calendars
- Work within this workspace

Messaging Rules

Channel Policy
WhatsApp & Signal Read-only by default. Only send when explicitly asked.
Telegram Agent's own bot identity. Can send freely.
Slack Read access. Drafts reviewed before sending to clients.
Group chats Participate, don't dominate. Never act as user's proxy.

Stop Means Stop

A hard-learned rule:

When Cedric says STOP, STOP IMMEDIATELY. No extra commands, no "helpful" checks. Acknowledge and wait. "All yours" means hands completely off.

This is in the system prompt AND in the agent's long-term memory.

Credential Hygiene

  • Agent refuses to display API keys or passwords when asked
  • Pushes back if user tries to paste secrets in chat
  • Suggests 1Password or terminal for credential operations

7. Audit & Observability

Session Logs

Every agent interaction is logged to .jsonl files:

~/.openclaw/agents/main/sessions/<session-id>.jsonl

These contain the full conversation including tool calls and their outputs. They're the primary audit trail for what the agent did and why.

Memory Files

The agent maintains its own notes:

~/.openclaw/workspace/memory/YYYY-MM-DD.md   # Daily activity log
~/.openclaw/workspace/MEMORY.md               # Long-term memory

These are human-readable markdown files you can review anytime.

GitHub Audit Trail

When the agent comments on issues or PRs, it posts as a GitHub App bot (evie-assistant[bot]), making it visually distinct from human comments. The App ID, installation, and permissions are all auditable through GitHub's settings.


8. Pending Security Work

Item Status Description
Port binding migration 🔄 In progress Moving from *:64xxx (LAN-exposed) to 127.0.0.1:65xxx (localhost-only)
Dedicated DB user + RLS 📋 Planned Non-admin Postgres user for agent, admin password only known to human
Periodic secret scans 📋 Planned Cron job to run TruffleHog weekly
op CLI fix 🐛 Bug v2.32.0 hanging on all commands — needs reinstall or update
Session log rotation 📋 Planned Archive/compress old session logs

Appendix: Knowledge Graph Demo

To demonstrate how OpenClaw's knowledge base works, here's what the system knows about Maurice Rabb — the person this guide is being shared with. All of this was assembled automatically from meeting sync, attendee resolution, and Apollo.io enrichment.

Person Record

Field Value Source
Full Name Maurice Rabb Fellow API (attendee resolution)
Title Director of Pedagogy Apollo.io enrichment
Organization Spantree Apollo.io enrichment
Location Chicago, Illinois Apollo.io enrichment
LinkedIn linkedin.com/in/mauricerabb Apollo.io enrichment
Emails maurice.rabb@spantree.net, maur@trifork.com Fellow API (calendar invites)
Internal ID af1cea0b-9429-4190-bc41-322baf9742f5 Auto-generated UUID

Meeting Participation: 146 meetings

Maurice appears in 146 meetings across our synced Fellow data. Breakdown by series:

Meeting Series Count Context
Nextpoint internal 30 Primary project — search/query parser work
Global Stand (FTW!) 12 Company-wide standup
L10 12 Leadership/ops cadence
Spantree-Trifork Ops 8 Operational meetings
Cedric / Maurice 1:1 6 Direct manager check-ins
DTY Daily Standup 6 Cross-project standup
Team Time! 6 Team social/sync
Project Leads check-in 4 Cross-project leadership
Tuesday Check-in: Spantree + Nextpoint 4 Client-facing check-in
Friday Sprint Close & Planning 4 Sprint ceremonies
Spangineering Club 3 Engineering community
Kermit Collective 2 Quarterly advisory
Fluent Workshop/Standup 2 Workshop contributions

How This Was Built

  1. Fellow Sync pulled 262 meeting notes and 127 recordings with transcripts into a local Postgres database
  2. Attendee Resolution matched maurice.rabb@spantree.net from calendar invites across all meetings to a single people record (96.7% resolution rate across 2,719 total attendees)
  3. Apollo.io Enrichment looked up Maurice's email and returned his title, LinkedIn, location, and organization — zero API credits spent (basic enrichment is free)
  4. Cross-referencing links Maurice's person ID across meetings, transcript segments (speaker identification), and action items

The result: ask "what has Maurice been working on?" and the system can search across 146 meetings, find his transcript segments, surface his action items, and provide context — all from a local database query.


Summary

The security philosophy is defense in depth:

Layer Control
OS Dedicated non-admin user, no sudo, no Docker
Credentials 1Password service account, read-only, macOS Keychain
Secrets TruffleHog scanning + redaction of session logs
Network Tailscale mesh VPN, localhost-only ports, ACLs
Database Self-managed Docker Compose, localhost binding, RLS (planned)
Behavioral System prompt rules, messaging policies, stop-means-stop
Audit Session logs, memory files, GitHub App bot identity

No single layer is the complete answer. They stack.


Written by Evie (OpenClaw AI Assistant) with direction from Cedric Hurst. February 11, 2026.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment