Назад към всички

clawtime

// Install, configure, start, and troubleshoot ClawTime — a private self-hosted webchat UI for OpenClaw with passkey (Face ID) auth, Piper TTS voice, and 3D avatar. Requires Cloudflare tunnel for HTTPS (passkeys need a real domain). Use when the user wants to install ClawTime on a new machine, configur

$ git log --oneline --stat
stars:1,933
forks:367
updated:March 4, 2026
SKILL.mdreadonly
SKILL.md Frontmatter
nameclawtime
descriptionInstall, configure, start, and troubleshoot ClawTime — a private self-hosted webchat UI for OpenClaw with passkey (Face ID) auth, Piper TTS voice, and 3D avatar. Requires Cloudflare tunnel for HTTPS (passkeys need a real domain). Use when the user wants to install ClawTime on a new machine, configure the tunnel, register a passkey, set up TTS, start/stop the server, or diagnose errors. Triggered by requests like "install clawtime", "set up clawtime", "start clawtime", "clawtime isn't working", "register passkey", "device auth issue".
metadata[object Object]

ClawTime — Local Installation with Cloudflare Tunnel

ClawTime is a private webchat UI connecting to the OpenClaw gateway via WebSocket. Features: passkey (Face ID/Touch ID) auth, Piper TTS voice, 3D avatar.

Why Cloudflare is required: WebAuthn (passkeys) need HTTPS on a real domain. http://localhost only works on the same machine — not from a phone on your network.

Architecture

iPhone/Browser → https://portal.yourdomain.com → Cloudflare Tunnel → localhost:3000 (ClawTime) → ws://127.0.0.1:18789 (OpenClaw Gateway)

Prerequisites

  • Node.js v22+
  • cloudflared CLI: brew install cloudflared
  • A domain with DNS on Cloudflare (free tier works)
  • OpenClaw running: openclaw status
  • (Optional) Piper TTS + ffmpeg for voice

Installation Steps

1. Clone & install

cd ~/Projects
git clone https://github.com/youngkent/clawtime.git
cd clawtime
npm install --legacy-peer-deps

2. Set up Cloudflare Tunnel

# Login to Cloudflare
cloudflared tunnel login

# Create named tunnel
cloudflared tunnel create clawtime

# Configure routing
# Edit ~/.cloudflared/config.yml:

~/.cloudflared/config.yml:

tunnel: clawtime
credentials-file: /Users/YOUR_USER/.cloudflared/<tunnel-id>.json

ingress:
  - hostname: portal.yourdomain.com
    service: http://localhost:3000
  - service: http_status:404

Then in Cloudflare DNS dashboard: add a CNAME record:

  • Name: portal → Target: <tunnel-id>.cfargotunnel.com (Proxied ✅)

3. Configure OpenClaw gateway

The gateway must whitelist ClawTime's origin:

openclaw config patch '{"gateway":{"controlUi":{"allowedOrigins":["https://portal.yourdomain.com"]}}}'
openclaw gateway restart

⚠️ PUBLIC_URL must match this origin exactly — it's used as the WebSocket origin header for device auth.

4. Start ClawTime server

Minimum (no TTS):

cd ~/Projects/clawtime
PUBLIC_URL=https://portal.yourdomain.com \
SETUP_TOKEN=<your-setup-token> \
GATEWAY_TOKEN=<gateway-token> \
node server.js

With Piper TTS:

cd ~/Projects/clawtime
PUBLIC_URL=https://portal.yourdomain.com \
SETUP_TOKEN=<your-setup-token> \
GATEWAY_TOKEN=<gateway-token> \
BOT_NAME="Beware" \
BOT_EMOJI="🌀" \
TTS_COMMAND='python3 -m piper --data-dir ~/Documents/resources/piper-voices -m en_US-kusal-medium -f /tmp/clawtime-tts-tmp.wav -- {{TEXT}} && ffmpeg -y -loglevel error -i /tmp/clawtime-tts-tmp.wav {{OUTPUT}}' \
node server.js

⚠️ TTS Security Note: The {{TEXT}} placeholder is substituted into a shell command. ClawTime's server must sanitize text before substitution to prevent command injection. The server should strip or escape shell metacharacters (; | & $ \ ( ) { } < >) from user input before passing it to the TTS command. If you're modifying the TTS pipeline, use child_process.execFile()with argument arrays instead ofchild_process.exec()` with string interpolation.

5. Start Cloudflare tunnel

cloudflared tunnel run clawtime

6. Register passkey (first time only)

  1. Open https://portal.yourdomain.com/?setup=<your-setup-token> in Safari
  2. Follow the passkey (Face ID / Touch ID) prompt
  3. ❌ Do NOT use private/incognito mode — Safari blocks passkeys there
  4. ❌ Do NOT use Chrome on iOS — use Safari

After registration, access ClawTime at https://portal.yourdomain.com.


Environment Variables

VariableRequiredDescription
PUBLIC_URLPublic HTTPS URL (must match allowedOrigins in gateway config)
GATEWAY_TOKENOpenClaw gateway auth token
SETUP_TOKENFor registrationPassphrase for ?setup=<token> passkey registration URL
TTS_COMMANDFor voicePiper command with {{TEXT}} and {{OUTPUT}} placeholders
BOT_NAMENoDisplay name (default: "Beware")
BOT_EMOJINoAvatar emoji (default: "🌀")
PORTNoServer port (default: 3000)

Storing Tokens Securely (recommended)

Instead of passing tokens as plaintext env vars or in plist files, store them in macOS Keychain:

# Store tokens in Keychain
security add-generic-password -s "clawtime-gateway-token" -a "$(whoami)" -w "YOUR_GATEWAY_TOKEN"
security add-generic-password -s "clawtime-setup-token" -a "$(whoami)" -w "YOUR_SETUP_TOKEN"

Then retrieve them at launch time:

GATEWAY_TOKEN=$(security find-generic-password -s "clawtime-gateway-token" -a "$(whoami)" -w) \
SETUP_TOKEN=$(security find-generic-password -s "clawtime-setup-token" -a "$(whoami)" -w) \
PUBLIC_URL=https://portal.yourdomain.com \
node server.js

This avoids storing secrets in plaintext on disk.


Device Authentication (Critical)

ClawTime authenticates with the OpenClaw gateway using Ed25519 keypair auth. This is where most installs break — see details in references/device-auth.md.

Quick summary:

  • Keypair auto-generated in ~/.clawtime/device-key.json on first run
  • Device ID = SHA-256 of raw 32-byte Ed25519 pubkey (NOT the full SPKI-encoded key)
  • Signature payload format: v2|deviceId|clientId|clientMode|role|scopes|signedAtMs|token|nonce
  • If device auth fails → delete ~/.clawtime/device-key.json and restart

Auto-Start on Boot (macOS launchd)

See references/launchd.md for plist templates for both the server and tunnel.


Managing Services

# Stop server
pkill -f "node server.js"

# Stop tunnel
pkill -f "cloudflared"

# View logs (if backgrounded)
tail -f /tmp/clawtime.log
tail -f /tmp/cloudflared.log

# Restart after code/config changes
pkill -9 -f "node server.js"; sleep 2; # then re-run start command

Getting the Gateway Token

# From macOS Keychain
security find-generic-password -s "openclaw-gateway-token" -a "$(whoami)" -w

# From config file
cat ~/.openclaw/openclaw.json | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('gateway',{}).get('token',''))"

Passkey Operations

# Reset passkeys (re-register from scratch)
echo '[]' > ~/.clawtime/credentials.json
# Restart server, then visit /?setup=<token>

# Reset device key (new keypair on next restart)
rm ~/.clawtime/device-key.json

Troubleshooting

See references/troubleshooting.md for all common errors and fixes. See references/device-auth.md for deep-dive on gateway auth issues.