Reference

Docker

v0.13 The image built from the repository’s Dockerfile, released as ghcr.io/rohansx/cloakpipe:0.14.0, runs the gate over HTTP by default: agents connect to http://<host>:8900/mcp, and the gate checks each tool call before it reaches your MCP server.

Up to v0.12.0, the image’s default command was the PII proxy; that is now start, below.

Run it

git clone https://github.com/rohansx/cloakpipe && cd cloakpipe
docker build -t cloakpipe-gate .
openssl rand -hex 32 > listen.token
docker run --rm -p 127.0.0.1:8900:8900 \
  -v "$PWD/cloakpipe.manifest.yaml:/app/cloakpipe.manifest.yaml:ro" \
  -v "$PWD/listen.token:/run/secrets/listen.token:ro" \
  -v cloakpipe-data:/data \
  -e CLOAKPIPE_UPSTREAM_URL=https://tools.example.com/mcp \
  -e CLOAKPIPE_LISTEN_TOKEN_FILE=/run/secrets/listen.token \
  cloakpipe-gate

The image’s entrypoint, cloakpipe-entrypoint (docker/gate-entrypoint.sh), turns the variables below into flags and runs cloakpipe mcp-proxy --listen 0.0.0.0:8900 --manifest /app/cloakpipe.manifest.yaml --upstream-url $CLOAKPIPE_UPSTREAM_URL …. Every decision is the same as with the binary; see MCP over HTTP.

Then point the agent at the gate, for example Claude Code:

claude mcp add --transport http support-tools http://localhost:8900/mcp \
  --header "Authorization: Bearer $(cat listen.token)"

Variables

Variable Becomes
CLOAKPIPE_UPSTREAM_URL --upstream-url: your MCP server over Streamable HTTP. This or CLOAKPIPE_UPSTREAM is required.
CLOAKPIPE_UPSTREAM --upstream: a stdio server command run inside the container. The image has no Node.js or Python, so build on top of it to add your server’s runtime.
CLOAKPIPE_UPSTREAM_HEADER one --upstream-header, Name: value; a value env:VAR is read from VAR
CLOAKPIPE_MANIFEST --manifest, default /app/cloakpipe.manifest.yaml. Mount yours there.
CLOAKPIPE_LISTEN --listen, default 0.0.0.0:8900
CLOAKPIPE_LISTEN_TOKEN_FILE --listen-token-file: the bearer token agents must send
CLOAKPIPE_INSECURE_NO_TOKEN set to 1 to listen on a non-loopback address without a token, on a private network only
CLOAKPIPE_TOOLS_LOCK --tools-lock, with CLOAKPIPE_UPSTREAM only (a tools lock does not combine with an HTTP upstream yet)
CLOAKPIPE_WEBHOOK_URL, CLOAKPIPE_WEBHOOK_SECRET_FILE, CLOAKPIPE_WEBHOOK_FORMAT --webhook, --webhook-secret-file, --webhook-format. See Alerts & events.
CLOAKPIPE_OTEL_ENDPOINT, CLOAKPIPE_EVENTS_LOG --otel-endpoint, --events-log

The gate reads a few more itself: CLOAKPIPE_TASK_CONTEXT (the task context file), and CLOAKPIPE_HOME and CLOAKPIPE_OPERATIONS_DB, which the image sets.

Arguments after gate are added to the flags, for example docker run … cloakpipe-gate gate --mode shadow to start in shadow mode.

The entrypoint refuses to start, with exit code 2, when there is no manifest at CLOAKPIPE_MANIFEST (no manifest is mounted), when both or neither of CLOAKPIPE_UPSTREAM_URL and CLOAKPIPE_UPSTREAM are set, and when the gate would listen on a non-loopback address without CLOAKPIPE_LISTEN_TOKEN_FILE (unless CLOAKPIPE_INSECURE_NO_TOKEN=1). The binary on its own only warns about a missing token; the image is stricter because it listens on 0.0.0.0 by default.

State and health

The image sets CLOAKPIPE_HOME=/data and CLOAKPIPE_OPERATIONS_DB=/data/operations.db, and declares /data a volume. The decision ledger, the gate signing key (created on first start) and the operations store all live there, so mount a named volume to keep them across restarts. Any command other than gate runs cloakpipe itself, so this exports the ledger from the volume:

docker run --rm -v cloakpipe-data:/data cloakpipe-gate ledger export --out /data/audit-pack.tar

The health check asks GET /health on the listen port, which needs no token.

Compose example

docker/compose.example.yaml runs the gate in front of the support-agent example’s tools, served over HTTP by examples/support-agent/mock_http_upstream.py:

openssl rand -hex 32 > docker/listen.token
docker compose -f docker/compose.example.yaml up --build

The gate enforces examples/support-agent/cloakpipe.manifest.yaml, so the injected email from the quick start is refused as scope_mismatch over HTTP too. Replace the tools service and the manifest with yours. The file publishes the port on 127.0.0.1 only; put a reverse proxy with TLS in front for agents on other machines.

The PII proxy

Any command other than gate runs cloakpipe itself. start runs the OpenAI- and Anthropic-compatible PII proxy with the bundled config, as the image did by default up to v0.12.0:

docker run -p 8900:8900 -e OPENAI_API_KEY=sk-... cloakpipe-gate start

The repository’s root docker-compose.yml runs it that way.