Aquaman: API Key Protection
Protect API keys and secrets for OpenClaw. Credentials stay in your vault, never in the agent's memory
Install
openclaw plugins install clawhub:aquaman-pluginaquaman-plugin: API Key Protection for OpenClaw
The aquaman adapter for the OpenClaw Gateway. Your API keys and tokens stay in your vault. The agent never sees them: they live in a separate process that injects them on the way out, and that process has no endpoint that hands a key back. A compromised agent can't read them. What it can do while it runs is send requests through the proxy, which request policy bounds and the audit log records (see Security model).
This plugin spawns aquaman-proxy (exact-pinned, same author) on Gateway startup, routes model traffic to that proxy over a token-gated loopback listener, and lets you reach the same vault and policy engine from inside OpenClaw.
Agent / OpenClaw Gateway Aquaman Proxy
┌──────────────────────┐ ┌──────────────────────┐
│ │ │ │
│ models.providers.* │══ loopback ═>│ Keychain / 1Pass / │
│ .baseUrl = 127.0.0.1│ (models) │ Vault / Encrypted │
│ │<═ Socket ════│ │
│ fetch() interceptor │══ (UDS) ════>│ + Policy enforced │
│ redirects channel │ │ + Auth injected: │
│ API traffic │ │ header / url-path │
│ │ │ basic / oauth │
│ │ │ │
│ No credentials. │ ~/.aquaman/ │ │
│ Token-gated loopback│ proxy.sock │ │
│ No keys to read. │ (chmod 600) │ │
└──────────────────────┘ └──┬──────────┬────────┘
│ │
│ ▼
│ ~/.aquaman/audit/
│ (hash-chained log)
▼
api.anthropic.com / api.mistral.ai /
api.telegram.org / slack.com/api …
What it does
- Bring your own vault: aquaman has no house vault; secrets stay in the store you already run: Keychain, 1Password, HashiCorp Vault, Bitwarden, KeePassXC, systemd-creds, or encrypted-file.
- Agent gets a proxy URL: requests route through
~/.aquaman/proxy.sock(UDS,chmod 0o600); the proxy injects auth headers on the fly. - Dangerous endpoints blocked: request policies deny admin APIs, prevent deletions, block sends - before credentials are even injected.
- Tamper-evident audit log: every credential use logged with SHA-256 hash chains.
Quick Start
openclaw plugins install aquaman-plugin # 1. install plugin + proxy
openclaw aquaman setup # 2. store your API keys, wire up plugin
openclaw # 3. done - proxy starts automatically
The aquaman-proxy binary is bundled as an exact-pinned npm dependency - no separate download or install needed.
Using npm directly?
npm install -g aquaman-proxy && aquaman openclaw setupdoes the same thing - installs the proxy CLI, stores your keys, installs the plugin into~/.openclaw/extensions/aquaman-plugin/, and wires the credentials (SecretRef refs on OpenClaw ≥ 2026.6.5, the auth-profiles.json placeholder on older versions).
Troubleshooting: openclaw aquaman doctor (or aquaman openclaw doctor from a regular shell).
Security model
Aquaman keeps API credentials out of the agent process by running them in a separate proxy process. The agent never sees the secret - only a sentinel base URL that the proxy intercepts, authenticates, and forwards. See the architecture diagram in the main README.
How model traffic reaches the proxy (v0.15.0+)
aquaman openclaw setupenables aquaman's loopback listener and pointsmodels.providers.<svc>.baseUrlat it (http://127.0.0.1:<port>/anthropic,…/openai/v1). That is OpenClaw's documented local-provider pattern, and it trusts that exact origin for model requests.- The SecretRef resolver hands the gateway the loopback token as the provider api key. The listener is token-gated, the token only grants access to 127.0.0.1, and the proxy strips it before injecting your real key from the vault.
- Why not the Unix socket: OpenClaw's model transport builds its own HTTP client. It never calls
globalThis.fetchand resolves hostnames itself, so the olderaquaman.localsentinel could not work for model traffic on 2026.7.33+.aquaman openclaw doctorfails if a provider is credential-wired but not routed.
Channel credentials on 2026.7.33+: Telegram routed, the rest at rest only
Each channel builds its own HTTP client per request, so this plugin's interceptor no longer sees channel traffic. Model-provider isolation is unaffected, since it no longer depends on the interceptor either.
Telegram is the one bundled channel with an endpoint override. aquaman openclaw setup writes:
{ channels: { telegram: { apiRoot: "http://127.0.0.1:8585/telegram", botToken: "<loopback token>" } } }
The Bot API carries its token in the URL path rather than a header, so the loopback token travels in the /bot<TOKEN> segment. The proxy accepts it there, strips it, and injects your real bot token from the vault. Setup leaves a channel untouched and says why when it uses a self-hosted apiRoot, a tokenFile, multiple accounts, or has no vault credential yet.
Other channels are vault storage and aquaman openclaw migrate only: OpenClaw uses the token directly, so the proxy is not in the path and those calls are not audited. Discord and Slack expose no override; Matrix, Mattermost and Nextcloud Talk already point at your own server. aquaman openclaw doctor reports which of your channels are in which group.
Transports and access control
The proxy listens two ways, and which one a host uses depends on what that host can dial:
- Unix socket
~/.aquaman/proxy.sock(0600): coding agents and anything else that can dial a socket. File permissions mean only processes running as you can connect. - Loopback TCP
127.0.0.1:<port>, token-gated: Hermes (v0.13.0+), OpenClaw model traffic and Telegram (v0.15.0+), because each builds its own HTTP client.
The token is a capability to reach the local proxy, not a credential: generated per install, stored in ~/.aquaman/config.yaml (0600), stripped by the proxy before your real key is injected. Any local process can reach a loopback port, including other users, where the socket's 0600 shuts them out, so the listener stays off until a host needs it. Full table in the root README.
What a compromised agent can and can't do
- Can't read your keys. They are in the proxy's address space. Since v0.15.0 the proxy this plugin spawns (
aquaman openclaw plugin-mode) serves no endpoint that returns a credential value. Through v0.14.x it exposedPOST /broker/resolve, the coding-agent credential broker, to any process that could reach the socket. That is what ClawHub's ClawScan flagged on 0.14.x. Upgrade if you're on 0.12–0.14. - Can send requests through the proxy to the services in your
serviceslist while it runs. The socket'schmod 0o600keeps other users out, not other processes running as you. Request policy (deny rules, enforced before injection) bounds what those requests can do, and every one is in the hash-chained audit log. - If you also run
aquaman daemonfor coding agents or the Hermes secret source, refs you declared there (projects.yaml,aquaman broker allow) can be fetched by any process running as you, since that is what declaring a ref means. Both proxies bind~/.aquaman/proxy.sock; the one started last owns it, and this plugin's proxy never serves the broker.
Proxy process
- The plugin spawns the
aquamanbinary from theaquaman-proxynpm package, declared as an exact-pinned dependency (no semver range) and published by the same author (tech4242). After spawn, the plugin checks the running proxy's reported version against its own and logs a warning if they disagree. - The spawn (
aquaman openclaw plugin-mode) is what triggers thedangerous-execfinding in OpenClaw's static scanner. It's intentional and is the whole point of the plugin.
HTTP interceptor scope
- Only services listed in the plugin's
servicesconfig get their traffic redirected to the local proxy. As of v0.11.4, the interceptor filters its known-host map by yourserviceslist. Channels you didn't opt into keep talking to the upstream directly. - The interceptor uses a Unix Domain Socket (no TCP, no network exposure). UDS file permissions are
chmod 0o600, enforced explicitly at proxy startup (v0.12.0+).
Credential wiring: SecretRef (v0.14.0+, OpenClaw ≥ 2026.6.5)
- On current OpenClaw,
aquaman openclaw setupwires the plugin through OpenClaw's canonical SecretRef credential surface: the manifest declares an exec resolver (secretProviderIntegrations.aquaman→dist/secrets-resolver.mjs) andopenclaw.jsongetsmodels.providers.<svc>.apiKeyrefs pointing at it. The resolver returns the loopback token, or theaquaman-proxy-managedplaceholder when no listener is configured. Either way it's a marker: real keys stay in your vault, and the proxy strips the marker and injects the real credential per request. - No
openclaw doctor --fiximport step, and the wiring survives OpenClaw's plaintext-scrub flows (openclaw secrets configure --apply).aquaman openclaw doctorreports the wiring state and suggests the upgrade on legacy installs.
Auth profiles (legacy path, OpenClaw < 2026.6.5)
- On gateways older than 2026.6.5 the plugin writes
~/.openclaw/agents/<id>/agent/auth-profiles.jsonon load, with placeholder API-key entries foranthropicandopenai, so OpenClaw doesn't reject requests before they reach the proxy. The proxy strips the placeholder and injects the real credential. It's skipped when the SecretRef wiring is present, and it never happens during OpenClaw's discovery loads (plugins inspect|doctor|install) (v0.15.0+). - The plugin never overwrites an existing
auth-profiles.json. To suppress generation entirely, setautoGenerateAuthProfiles: falsein the plugin config (v0.11.4+). - OpenClaw ≥ 2026.6.5: provider auth profiles live in SQLite and the runtime read path for
auth-profiles.jsonwas removed (openclaw/openclaw#89102), so since v0.15.0 the plugin doesn't write the file there. It warns you to runaquaman openclaw setup, which wires SecretRef. - OpenClaw 2.0 (≥ 2026.8.1): a leftover
auth-profiles.jsonis no longer ignored. It locksanthropic/openaiout ("requires legacy credential migration"). aquaman-plugin ≤ 0.14.x recreated it on every load, so the lockout came back even afteropenclaw doctor --fix. After upgrading to 0.15.0, runopenclaw doctor --fixonce to archive it.aquaman openclaw doctordetects the state and prints the exact steps.
Audit log
- Every credential use is recorded in
~/.aquaman/audit/current.jsonlwith a SHA-256 hash chain so tampering is detectable. The log stays local - no telemetry. aquaman openclaw doctorsurfaces audit log issues;aquaman audit tailshows recent entries.- Operators can constrain which upstream endpoints get proxied (and therefore credentialed) via the
policyconfig in~/.aquaman/config.yaml. Denied requests return 403 before any credential is injected.
Host surface the plugin touches
process:spawn:aquaman(the proxy binary; see "Proxy process" above).global:override:globalThis.fetch(the interceptor; scoped to yourserviceslist).env:write:*_BASE_URLandGITHUB_API_URL(sentinel base URLs, skipped for providers routed by config).fs:write:~/.openclaw/agents/*/agent/auth-profiles.json(legacy gateways < 2026.6.5 only; skipped when SecretRef wiring is present, and never during discovery loads).- Agent tool
aquaman_status, declared in the manifest'scontracts.tools(OpenClaw drops undeclared tools).
Scanner findings
openclaw security audit --deep reports two expected findings:
dangerous-execon the proxy-manager module: the plugin spawns the proxy as a separate process. This is how credential isolation works.tools_reachable_permissive_policy: advisory about your tool policy, not an aquaman vulnerability. Set"tools": { "profile": "coding" }inopenclaw.jsonif your agents handle untrusted input.
ClawHub's ClawScan additionally produces a higher-level review of plugin behavior. Its verdict on 0.14.x was suspicious because of the broker endpoint described above; v0.15.0 removes that endpoint from the plugin's proxy. See the publisher note on the package page for context on each item.
aquaman openclaw setup appends the plugin to your plugins.allow list if you have one, and never creates one. That list governs OpenClaw's own plugins too, including the anthropic/openai model providers, so a list holding only aquaman-plugin blocks them, and setup did exactly that through v0.14.x. If you have that leftover list, add "anthropic" and "openai" to it. aquaman openclaw doctor flags it.
Available commands
All commands work via OpenClaw CLI or your terminal:
| OpenClaw CLI | Terminal | Description |
|---|---|---|
openclaw aquaman setup | aquaman openclaw setup | OpenClaw bundle - vault wizard + plugin install + auth-profiles |
openclaw aquaman doctor | aquaman openclaw doctor | Deep diagnostic for the OpenClaw integration |
openclaw aquaman credentials list | aquaman credentials list | List stored credentials |
openclaw aquaman credentials add | aquaman credentials add | Add a credential (interactive) |
openclaw aquaman policy-list | aquaman policy list | Show request policy rules |
openclaw aquaman audit-tail | aquaman audit tail | Recent audit entries |
openclaw aquaman services-list | aquaman services list | List configured services |
openclaw aquaman status | aquaman openclaw status | Plugin lifecycle + sentinel env vars |
Slash commands in chat: /aquaman-status, /aquaman list, /aquaman doctor.
Config options
aquaman openclaw setup writes these to ~/.openclaw/openclaw.json automatically:
| Key | Type | Default | Description |
|---|---|---|---|
backend | "keychain" | "1password" | "vault" | "encrypted-file" | "keepassxc" | "systemd-creds" | "bitwarden" | "keychain" | Credential store |
services | string[] | ["anthropic", "openai"] | Services to proxy (also gates which hostnames the interceptor redirects, v0.11.4+) |
autoGenerateAuthProfiles | boolean | true | Auto-generate auth-profiles.json with placeholder anthropic/openai entries when the file is absent. Set false to manage your own (v0.11.4+) |
Advanced settings (audit, vault, request policies) go in ~/.aquaman/config.yaml. See the request policy docs.
Documentation
- Root README: value prop, three-path Quick Start, security model
aquaman-proxy: core CLI and daemonaquaman-coder: coding-agent adapter (Claude Code, Codex/OpenCode/Cursor planned)
License
MIT
