<a href="https://github.com/dedene/claw-wrap/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/dedene/claw-wrap/actions/workflows/ci.yml/badge.svg" /></a>
<a href="https://go.dev/"><img alt="Go 1.24+" src="https://img.shields.io/badge/go-1.24+-00ADD8.svg" /></a>
<a href="https://goreportcard.com/report/github.com/dedene/claw-wrap"><img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/dedene/claw-wrap" /></a>
<a href="https://opensource.org/licenses/MIT"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-green.svg" /></a>
A secure credential proxy for CLI tools and HTTP APIs. Executes tools with secrets on behalf of sandboxed processes, or injects credentials into HTTP requests via MITM proxy — credentials never enter the sandbox.
<a href="https://github.com/dedene/claw-wrap/raw/v0.5.0/docs/INSTALL.md">Install</a> ·
<a href="https://github.com/dedene/claw-wrap/raw/v0.5.0/docs/CONFIG.md">Config</a> ·
<a href="https://github.com/dedene/claw-wrap/raw/v0.5.0/docs/SANDBOX.md">Sandbox</a> ·
<a href="https://github.com/dedene/claw-wrap/raw/v0.5.0/docs/SPEC.md">Protocol</a>
You're running an AI agent in a sandbox. The agent needs to call gh to interact with GitHub — list
repos, read issues, check PRs. So you give it your GH_TOKEN.
Now the agent has full access to your GitHub account. It can read private repos, push code, delete repositories, create tokens. Any process in the sandbox can grab the token from the environment. One prompt injection and your credentials are exfiltrated.
claw-wrap solves this. The agent calls gh repo list like normal, but:
gh is actually a symlink to claw-wrapgh, and streams the output backYou can also block dangerous commands server-side — the agent can gh repo list but not
gh repo delete.
┌─────────────────────────────────────────────────────────┐
│ SANDBOX │
│ │
│ agent calls "gh repo list" │
│ ↓ │
│ /usr/local/bin/gh → claw-wrap (symlink) │
│ 1. Reads HMAC secret from /run/openclaw/auth │
│ 2. Signs request with timestamp │
│ 3. Sends to daemon via Unix socket │
│ 4. Streams stdout/stderr back to agent │
│ ↓ │
└─────────│───────────────────────────────────────────────┘
│ Unix socket (/run/openclaw/secrets.sock)
↓
┌─────────────────────────────────────────────────────────┐
│ claw-wrap daemon (outside sandbox) │
│ 1. Verifies HMAC signature + timestamp │
│ 2. Checks args against blocked patterns │
│ 3. Fetches GH_TOKEN from credential backend │
│ 4. Spawns real gh binary with token in environment │
│ 5. Streams stdout/stderr back through socket │
│ │
│ ⚠️ Credentials NEVER leave the daemon process │
└─────────────────────────────────────────────────────────┘
claw-wrap supports two approaches for credential injection:
| Mode | Best For | How It Works |
|---|---|---|
| CLI Wrapper | CLI tools (gh, aws, gcloud) | Symlink intercepts command, daemon executes with credentials |
| HTTP Proxy | HTTP APIs, curl, SDKs | MITM proxy injects auth headers into matching requests |
Use CLI Wrapper when:
gh repo delete)Use HTTP Proxy when:
| Backend | Prefix | Example | Notes |
|---|---|---|---|
| pass | pass: |
pass:cli/github/token |
Default when no prefix given |
| Environment | env: |
env:MY_TOKEN |
Reads from daemon environment |
| 1Password | op:// |
op://Vault/Item/field |
Requires op CLI, session auth |
| Bitwarden | bw: |
bw:item-uuid |
Requires bw CLI, session managed |
| macOS Keychain | keychain: |
keychain:service-name |
macOS only |
| age | age: |
age:/path/to/file.age |
File-level encryption |
| HashiCorp Vault | vault: |
vault:secret/myapp/key |
KV-v1 & KV-v2, external auth |
All backends except env: support jq extraction: vault:secret/app/creds | .password
This example sets up gh (GitHub CLI) as a proxied tool.
gh installed somewhere (e.g. via Homebrew: brew install gh)brew install dedene/tap/claw-wrap
Or from source:
git clone https://github.com/dedene/claw-wrap.git
cd claw-wrap
make build
sudo make install
# If you haven't initialized pass yet:
gpg --gen-key
pass init <your-gpg-key-id>
# Store the token
pass insert cli/github/token
Create /etc/openclaw/wrappers.yaml:
credentials:
github-token:
source: pass:cli/github/token
tools:
gh:
binary: /home/linuxbrew/.linuxbrew/bin/gh # path to real gh binary
env:
GH_TOKEN: github-token
See Configuration Reference for proxy tuning, output limits, and connection settings.
Linux (systemd):
sudo cp init/claw-wrap.service /etc/systemd/system/
# Edit User=YOUR_USERNAME to your actual username
sudo editor /etc/systemd/system/claw-wrap.service
sudo systemctl daemon-reload
sudo systemctl enable --now claw-wrap
macOS (launchd):
cp init/com.openclaw.claw-wrap.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.openclaw.claw-wrap.plist
claw-wrap install
This creates symlinks in /usr/local/bin pointing to the auto-detected claw-wrap binary
(auto-elevates with sudo if needed). Override with --install-dir /other/path.
claw-wrap list # Should show gh
claw-wrap check # Should show credentials OK (run from host/admin context)
gh repo list # Should work — using proxied credentials
| Backend | Format | Example |
|---|---|---|
| pass | pass:path |
pass:cli/github/token |
| 1Password CLI | op://vault/item/field |
op://dev/github/token |
| Bitwarden CLI | bw:item-uuid |
bw:a1b2c3d4 |
| macOS Keychain | keychain:service |
keychain:github-token |
| age | age:/path/file.age |
age:/etc/secrets/token.age |
| Environment | env:VAR |
env:GH_TOKEN |
All backends support | .jq_expr suffix for JSON field extraction (e.g. op://vault/item/field | .token).
claw-wrap doesn't just proxy credentials — it enforces what the agent can do with them.
Reject commands that match regex patterns. The agent gets an error, the command never runs:
tools:
gh:
binary: /home/linuxbrew/.linuxbrew/bin/gh
env:
GH_TOKEN: github-token
blocked_args:
- pattern: "repo\\s+delete"
match: command
message: 'Repository deletion is blocked'
- pattern: "repo\\s+create"
match: command
message: 'Repository creation is blocked'
- pattern: "auth\\s+"
match: command
message: 'Auth commands are blocked'
- pattern: 'ssh-key'
message: 'SSH key management is blocked'
By default, blocked patterns run in arg mode (each argument is matched independently). Use
match: command when a regex needs to span multiple args (for example repo\\s+delete).
The env key supports three value types — all entries are admin-controlled and cannot be overridden by the agent:
credentials:
gog-keyring-password:
source: pass:gog/keyring
db-password:
source: op://vault/db/password
tools:
gog:
binary: /home/linuxbrew/.linuxbrew/bin/gog
env:
# Credential reference: value matches a defined credential name
GOG_KEYRING_PASSWORD: gog-keyring-password
# Template interpolation: {{ name }} substituted inline
DATABASE_URL: "postgres://app:{{ db-password }}@localhost/mydb"
# Literal value: no credential refs, used as-is
GOG_ENABLE_COMMANDS: 'gmail,calendar,drive,tasks,contacts,keep,time'
The agent cannot override any env entry — values are stripped from inherited environment and set by the daemon.
Deprecated:
forced_envis deprecated. Useenvinstead — literal values (without credential refs) work the same way.
Sanitize sensitive values from tool output before it is streamed back to the client:
tools:
gh:
binary: /home/linuxbrew/.linuxbrew/bin/gh
env:
GH_TOKEN: github-token
redact_output:
- pattern: 'gh[pousr]_[A-Za-z0-9]{36}'
replace: '[GITHUB_TOKEN]'
- pattern: "(?i)(authorization:\\s*bearer\\s+)[^\\s]+"
replace: '${1}[REDACTED]'
If replace is omitted, claw-wrap uses [REDACTED]. See
Configuration Reference for full details.
For tools that make HTTP API calls, claw-wrap can act as a MITM proxy that injects credentials based on request host/path:
http_proxy:
enabled: true
listen: 127.0.0.1:8080
routes:
- host: api.github.com
inject:
header: Authorization
value: 'Bearer {{github-token}}'
deny:
- DELETE /**
tools:
curl:
binary: /usr/bin/curl
use_proxy: true # Injects HTTP_PROXY + CA trust
The proxy:
use_proxy: true tools)See HTTP Proxy Settings for full configuration.
tool, args, cwd, and request env (protocol v2).deny_unverified_caller_exe: true
for strict mode.claw-wrap works with deny-by-default sandboxes where credentials directories (~/.password-store,
~/.gnupg, ~/.ssh) are not accessible:
See docs/SANDBOX.md for the full guide — firejail profile, nono setup, self-restart mechanism, and verification steps.
pass, systemd, and troubleshooting# Daemon mode (usually via systemd)
claw-wrap daemon
# Admin commands
claw-wrap list # List configured tools
claw-wrap check # Verify credentials
claw-wrap install # Create symlinks (auto-detects directory)
claw-wrap install --install-dir /usr/local/bin # Override directory
claw-wrap version # Show version
claw-wrap help # Show help
# Tool execution (via symlinks)
/usr/local/bin/gh repo list
/usr/local/bin/gh issue list
/usr/local/bin/gh pr view 42
make build # Build to ./build/claw-wrap
make test # Run tests
make install # Install to /usr/local/bin
make fmt # Format code
make lint # Run go vet
make clean # Remove build artifacts
MIT
$ claude mcp add claw-wrap \
-- python -m otcore.mcp_server <graph>