Onboarding & Setup
A cold-start guide: from nothing to an agent whose secrets are tokenized on the wire. The validated path is a Debian-family Linux host; adjacent releases work unchanged, other distros may need package-name swaps.
The golden rule of client auth
Log your agent in before you enable the proxy. Waterwall's CA is name-constrained
to your API hosts only β it deliberately refuses to intercept OAuth callback hosts like
console.anthropic.com. Logging in with the proxy enabled fails with a
permitted subtree violation. Authenticate first, then turn the proxy on.
1. Prerequisites
- A 64-bit Debian 13 / Ubuntu 24.04+ host (or similar)
- Python 3.12 or newer (
python3 --version) - Network egress on
:443to your upstream API hosts, plus your OAuth/login host and package mirror - Root /
sudofor the install step (the service drops to an unprivilegedwaterwalluser at runtime) - ~500 MB disk for the venv + audit logs
- On your workstation: the agent client installed (e.g. Claude Code CLI)
2. Install
sudo git clone https://github.com/jimstratus/waterwall.git /opt/waterwall
cd /opt/waterwall
sudo python3 -m venv .venv
sudo .venv/bin/pip install -e ".[dev]"
sudo ./deploy/systemd/install.sh
The installer is idempotent β re-running never clobbers an existing CA, signing key, config, or host list. It:
- creates the
waterwallsystem user/group - seeds
/etc/waterwall/permitted_hosts.yamlwith the default host set - generates a Name-Constrained RSA-4096 CA at
/etc/waterwall/{ca.pem,ca.key,mitmproxy-ca.pem} - generates the Ed25519 audit signing keypair at
/etc/waterwall/{signing.key,signing.pub} - writes default
patterns.py+config.yaml, creates the log dirs, installs the systemd unit + weekly restart timer, and enables (does not start) the service
3. Back up the keys (do not skip)
/etc/waterwall/signing.key Ed25519 private key β lose it and ALL past audit logs become
unverifiable forever. Treat it like a CA root.
/etc/waterwall/signing.pub public key β safe to share with anyone who verifies evidence.
/etc/waterwall/ca.{pem,key} the name-constrained CA β needed to re-issue clients after a wipe.
Copy these out-of-band before you start the service. A disk failure without a backup means you cannot reconstruct prior audit evidence.
4. Start and health-check
sudo systemctl start waterwall-proxy.service
sudo systemctl status waterwall-proxy.service # β active (running)
curl -sf http://127.0.0.1:8889/healthz | python3 -m json.tool
A startup check (verify-install, 10 checks) runs before the proxy launches; a failure
blocks start. A healthy probe shows "status": "ok", "chain_intact": true, and a
"patterns_loaded" count.
upstream_reachable starts false
"upstream_reachable": false right after a fresh start is normal β it only flips true
once the proxy relays its first upstream response. It does not gate health. Drive one
request through, then re-probe.
5. Authenticate the agent β then enable the proxy
# a. make sure proxy env vars are NOT set
unset HTTPS_PROXY NODE_EXTRA_CA_CERTS CLAUDE_CODE_CERT_STORE NO_PROXY
# b. log in directly (no proxy)
claude /login
claude --print "ping" | head -3 # should respond, not 401
# c. NOW enable the proxy for session traffic
export HTTPS_PROXY=http://127.0.0.1:8888
export NODE_EXTRA_CA_CERTS=/etc/waterwall/ca.pem
export CLAUDE_CODE_CERT_STORE=bundled,system
export NO_PROXY="127.0.0.1,localhost,downloads.claude.ai,statsig.anthropic.com"
The NO_PROXY exclusions matter: your client touches update and telemetry hosts that the
name-constrained CA refuses to intercept by design β without excluding them you get TLS
handshake failures. Add the exports to your shell profile so every new session inherits them.
For OpenAI / OpenRouter / other clients, point that client's base URL through
http://127.0.0.1:8888 with the same NODE_EXTRA_CA_CERTS, and confirm its host is in
permitted_hosts.yaml.
6. (Recommended) the placeholder-preservation protocol
For agent workflows where a response must contain the actual secret to be executable
(generating a vault/sops command, say), append the protocol block from
docs/claude-md-insert.md to your ~/.claude/CLAUDE.md. It tells the model to reproduce
<pl:TYPE:HEX> placeholders byte-for-byte instead of paraphrasing them as <your_key> β
which is what lets the local detokenizer substitute the real value back. Plain Q&A about
secrets works without it; operational outputs that use the secret need it.
7. (Recommended) the SessionStart health hook
Add to ~/.claude/settings.json so each session warns you when the proxy is down or
kill-switched:
{
"hooks": {
"SessionStart": [
{ "matcher": "*", "hooks": [{ "type": "command",
"command": "/opt/waterwall/.venv/bin/waterwall pre-launch-hook" }] }
]
}
}
SessionStart hooks can warn but cannot hard-block; for a hard refusal-to-launch, use the
deploy/wrappers/waterwall-launch wrapper which gates on the hook's exit code.
You're done
Drive a request through the proxy and re-probe /healthz to watch upstream_reachable
flip true. From here: the Runbook for day-to-day operation, the
Deploy guide for the full production procedure, and the
TUI Dashboard for live visibility.