https://github.com/tech4242/aquaman
Credential isolation proxy for OpenClaw — secrets stay submerged, agents stay dry. 🔱🦞
https://github.com/tech4242/aquaman
Last synced: 2 months ago
JSON representation
Credential isolation proxy for OpenClaw — secrets stay submerged, agents stay dry. 🔱🦞
- Host: GitHub
- URL: https://github.com/tech4242/aquaman
- Owner: tech4242
- License: mit
- Created: 2026-02-02T21:44:22.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2026-02-23T17:02:22.000Z (4 months ago)
- Last Synced: 2026-02-24T03:32:40.536Z (4 months ago)
- Language: TypeScript
- Homepage:
- Size: 877 KB
- Stars: 16
- Watchers: 1
- Forks: 2
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-openclaw - aquaman - | Credential isolation proxy — API keys never enter the agent process, injected via UDS from Keychain/1Password/Vault/keepassxc | (Security / Security Tools)
- awesome-claw-opus - Aquaman
README
# 🔱🦞 Aquaman
[](https://github.com/tech4242/aquaman/actions/workflows/ci.yml)
[](https://codecov.io/gh/tech4242/aquaman)
[](https://www.npmjs.com/package/aquaman-proxy)
[](https://www.npmjs.com/package/aquaman-proxy)
[](https://github.com/tech4242/aquaman#security-model)
[](https://www.typescriptlang.org/)
[](LICENSE)
API key protection for OpenClaw — credentials stay in your vault, never in the agent's memory. 🔱🦞
You bought a brand new Mac Mini, set up OpenClaw, and now you're staring at your `~/.openclaw/openclaw.json` wondering why your Anthropic API key is sitting there in plaintext. You read the articles. You know what happens when an agent gets prompt-injected. We get it.
Aquaman fixes this with three layers of defense:
1. **Process isolation** — API keys live in a separate proxy process. The agent never sees them. Even RCE in the agent can't reach credentials — they're in a different address space.
2. **Request policies** — Per-service rules control *which endpoints* an agent can call. Block admin APIs, prevent deletions, allow drafts but deny sends. Denied requests never get real credentials.
3. **Tamper-evident audit** — Every credential use is logged with SHA-256 hash chains. You can prove what was accessed and detect tampering after the fact.
No SDK changes required. The proxy is transparent — your agent talks to `aquaman.local`, the proxy injects auth and forwards to the real API.
## Quick Start
```bash
openclaw plugins install aquaman-plugin # 1. install plugin + proxy
openclaw aquaman setup # 2. store your API keys
openclaw # 3. done — proxy starts automatically
```
Troubleshooting: `openclaw aquaman doctor`
> **Using npm?** `npm install -g aquaman-proxy && aquaman setup` does
> the same thing — installs the proxy CLI, stores your keys, and installs
> the plugin. Use this if you prefer managing packages with npm.
### Docker
Single-image deployment — same UDS architecture, containerized.
```bash
git clone https://github.com/tech4242/aquaman.git && cd aquaman
cp docker/.env.example docker/.env
# Edit docker/.env — pick a backend and set credentials
npm run docker:build && npm run docker:run
```
## How It Works
```
Agent / OpenClaw Gateway Aquaman Proxy
┌──────────────────────┐ ┌──────────────────────┐
│ │ │ │
│ ANTHROPIC_BASE_URL │══ Unix ════> │ Keychain / 1Pass / │
│ = aquaman.local │ Domain │ Vault / Encrypted │
│ │<═ Socket ═══ │ │
│ fetch() interceptor │══ (UDS) ══=> │ + Policy enforced │
│ redirects channel │ │ + Auth injected: │
│ API traffic │ │ header / url-path │
│ │ │ basic / oauth │
│ │ │ │
│ No credentials. │ ~/.aquaman/ │ │
│ No open ports. │ proxy.sock │ │
│ Nothing to steal. │ (chmod 600) │ │
└──────────────────────┘ └──┬──────────┬────────┘
│ │
│ ▼
│ ~/.aquaman/audit/
│ (hash-chained log)
▼
api.anthropic.com
api.telegram.org
slack.com/api ...
```
1. **Store** — Credentials live in a vault backend (Keychain, 1Password, Vault, Bitwarden, encrypted file, KeePassXC, systemd-creds)
2. **Policy** — Proxy checks method + path rules *before* touching credentials. Denied requests get a 403, never real auth headers.
3. **Inject** — Proxy looks up the credential and adds the auth header before forwarding. 25 builtin services, 4 auth modes (header, URL-path, HTTP Basic, OAuth).
4. **Audit** — Every credential use is logged with SHA-256 hash chains.
The agent only sees a sentinel hostname (`aquaman.local`). It never sees a key, and no port is open for other processes to probe.
## Security Model
| Layer | What it does | What it stops |
|-------|-------------|---------------|
| **Process isolation** | Credentials in separate process, connected via Unix domain socket (`chmod 600`) | Compromised agent can't read keys — different address space, no TCP port to probe |
| **Service allowlisting** | `proxiedServices` controls which APIs the agent can reach | Agent can't talk to services you didn't authorize |
| **Request policies** | Method + path rules per service, enforced before credential injection | Agent can reach Anthropic but not its admin API; can draft emails but not send them |
| **Audit trail** | SHA-256 hash-chained logs of every credential use | Post-incident forensics, tamper detection, compliance evidence |
## Request Policies
OAuth scopes can't distinguish between "draft an email" and "send an email" — they're both `gmail.send`. Request policies fill that gap: allow the service, then restrict what happens inside it.
```yaml
# ~/.aquaman/config.yaml
policy:
anthropic:
defaultAction: allow
rules:
- method: "*"
path: "/v1/organizations/**"
action: deny # block admin/billing API
openai:
defaultAction: allow
rules:
- method: "*"
path: "/v1/organization/**"
action: deny # block admin API
- method: DELETE
path: "/v1/**"
action: deny # no deletions
slack:
defaultAction: allow
rules:
- method: "*"
path: "/admin.*"
action: deny # block Slack admin methods
gmail:
defaultAction: allow
rules:
- method: POST
path: "/v1/users/*/messages/send"
action: deny # drafts ok, sending blocked
```
- **No policy = allow all** (backward compatible)
- **First match wins** — rules evaluated top-to-bottom, unmatched requests fall through to `defaultAction`
- **Denied before auth** — blocked requests never get real credentials
- **Path globs:** `*` matches within a segment, `**` matches zero or more segments
- **`aquaman setup`** applies safe defaults (blocks admin/billing endpoints for stored services)
- **`aquaman policy list`** shows all configured rules; **`aquaman policy test `** dry-runs a request
- **`aquaman doctor`** validates your policy config and warns about typos
## Credential Backends
| Backend | Best For | Setup |
|---------|----------|-------|
| `keychain` | Local dev on macOS (default) | Works out of the box |
| `encrypted-file` | Linux, WSL2, CI/CD | AES-256-GCM, password-protected |
| `keepassxc` | Existing KeePass users | Set `AQUAMAN_KEEPASS_PASSWORD` or key file |
| `1password` | Team credential sharing | `brew install 1password-cli && op signin` |
| `vault` | Enterprise secrets management | Set `VAULT_ADDR` + `VAULT_TOKEN` |
| `systemd-creds` | Linux with systemd ≥ 256 | TPM2-backed, no root required |
| `bitwarden` | Bitwarden users | `bw login && export BW_SESSION=$(bw unlock --raw)` |
**Important:** `encrypted-file` is a last-resort backend for headless Linux/CI environments without a native keyring. For better security, install `libsecret-1-dev` (for GNOME Keyring), use `systemd-creds` (Linux with TPM2), or use 1Password/Vault.
## Security Audit
`openclaw security audit --deep` reports two expected findings:
- **`dangerous-exec`** on `proxy-manager.ts` — the plugin spawns the proxy as a separate process. This is how aquaman keeps credentials out of the agent.
- **`tools_reachable_permissive_policy`** — OpenClaw warns that plugin tools (like `aquaman_status`) are reachable when no restrictive tool profile is set. This is an environment-level advisory about your agent's tool policy, not a vulnerability in aquaman. If your agents handle untrusted input, set `"tools": { "profile": "coding" }` in `openclaw.json` to restrict which tools agents can call.
`aquaman setup` adds the plugin to `plugins.allow` automatically so OpenClaw knows you trust it.